summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/ProceduralMaterial.h
blob: e0085666c1f10d30c24f43a1c49d07d8df35c76d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#pragma once

#include "Configuration/UnityConfigure.h"
#include "Runtime/Threads/Mutex.h"
#include "Runtime/Shaders/Material.h"
#include "Runtime/Utilities/Hash128.h"
#include "ProceduralTexture.h"
#include "SubstanceInput.h"
#include "External/Allegorithmic/builds/Engines/include/substance/handle.h"
#include "External/Allegorithmic/builds/Engines/include/substance/linker/linker.h"

class SubstanceArchive;
class Texture2D;
class Image;

#if UNITY_EDITOR
bool IsSubstanceSupportedOnPlatform(BuildTargetPlatform platform);
#endif

bool IsSubstanceSupported();															// return true if Substance is supported on current platform
TextureFormat GetSubstanceTextureFormat(SubstanceOutputFormat outputFormat, bool requireCompressed=false);	// return the required Substance texture format
SubstanceEngineIDEnum GetSubstanceEngineID();																// return the required Substance engine ID

// Global Substance processor usage
enum ProceduralProcessorUsage
{
	ProceduralProcessorUsage_Unsupported = 0,	// Substance isn't supported
	ProceduralProcessorUsage_One,				// uses only one CPU
	ProceduralProcessorUsage_Half,				// uses half of the CPU count (and not the first one if applicable)
	ProceduralProcessorUsage_All				// uses all the CPU
};

// Memory budget
enum ProceduralCacheSize
{
	ProceduralCacheSize_Tiny = 0,	// 128 Mb
	ProceduralCacheSize_Medium,		// 256 Mb
	ProceduralCacheSize_Heavy,		// 512 Mb
	ProceduralCacheSize_NoLimit,	// unlimited
	ProceduralCacheSize_None		// 1 byte
};

size_t GetProceduralMemoryBudget(ProceduralCacheSize budget);

// Loading behavior
enum ProceduralLoadingBehavior
{
	ProceduralLoadingBehavior_None = 0,         // do nothing
	ProceduralLoadingBehavior_Generate,         // generate textures
	ProceduralLoadingBehavior_BakeAndKeep,      // use baked textures, the substance may be generated later on
	ProceduralLoadingBehavior_BakeAndDiscard,   // use baked textures, the substance data is removed from runtime
	ProceduralLoadingBehavior_Cache             // generate textures and cache it, then use it at runtime to speed-up the loading
};

/* A ProceduralMaterial is a dynamic material that use the Substance engine.
 */

class ProceduralMaterial : public Material
{
public:		// NESTED TYPES
	
	typedef std::vector<PPtr<ProceduralTexture> > Textures;	
	typedef std::vector<ProceduralTexture*> PingedTextures;	

public:		// METHODS

	REGISTER_DERIVED_CLASS( ProceduralMaterial, Material )
	DECLARE_OBJECT_SERIALIZE( ProceduralMaterial )
	
	ProceduralMaterial( MemLabelId label, ObjectCreationMode mode );

	void Clean();

	ProceduralMaterial* Clone();
private:
	void RebuildClone();

public:
#if UNITY_EDITOR
	// Creates the material (one time call only from the importer)
	void Init( SubstanceArchive& substancePackage, const UnityStr& prototypeName, const SubstanceInputs& inputs, const Textures& textures );
	SubstanceArchive* GetSubstancePackage() { return m_PingedPackage; }
	const char* GetSubstancePackageName();
#endif
	const Textures&	GetTextures() const { return m_Textures; }
	Textures& GetTextures() { return m_Textures; }
	PingedTextures& GetPingedTextures() { return m_PingedTextures; }
	SubstanceInputs& GetSubstanceInputs () { return m_Inputs; }
	const SubstanceInputs& GetSubstanceInputs () const { return m_Inputs; }
	const SubstanceArchive* GetSubstancePackage() const { return m_PingedPackage; }
	SubstanceHandle* GetSubstanceHandle();
    
	// Generation sizes accessors
	void SetSize(int width, int height);
	int GetWidth() const  { return m_Width; }
	int GetHeight() const  { return m_Height; }

	// Threaded loading, launch the generation if all data is available
	void AwakeFromLoadThreaded();
	
	// Used in the editor to generate at reimport time
	void AwakeFromLoad( AwakeFromLoadMode awakeMode );

	// Call the rebuild of all the textures and updates the SBS texture assets
	//	generationType, the type of requested texture generation
	//	forceTextureGeneration, a boolean to force textures generation even though they may be up to date from the engine's point of view
	void RebuildTextures();	

	// Rebuilds all texture immediately in a synchronous manner
	// When that function returns, all textures should have been generated
	void RebuildTexturesImmediately();

	// Call the rebuild of all the textures in all of the Procedural Materials
	static void ReloadAll (bool unload = true, bool load = true);
	
    // Awake dependent objects
	void AwakeDependencies(bool awakeThreaded);

	// Process rebuild of textures
#if ENABLE_SUBSTANCE
    static ProceduralMaterial* m_PackedSubstance;
	static void PackSubstances(std::vector<ProceduralMaterial*>& materials);
	bool ProcessTexturesThreaded(const std::map<ProceduralTexture*, SubstanceTexture>& textures);
#if !UNITY_EDITOR
	bool PreProcess(std::set<unsigned int>& cachedTextureIDs);
	void PostProcess(const std::map<ProceduralTexture*, SubstanceTexture>& textures, const std::set<unsigned int>& cachedTextureIDs);
#endif
#endif

	// Scriptable input accessors
	std::vector<std::string> GetSubstanceProperties() const;
	bool HasSubstanceProperty( const std::string& inputName ) const;
	bool GetSubstanceBoolean( const std::string& inputName ) const;
	void SetSubstanceBoolean( const std::string& inputName, bool value );
	float GetSubstanceFloat( const std::string& inputName ) const;
	void SetSubstanceFloat( const std::string& inputName, float value );
	Vector4f GetSubstanceVector( const std::string& inputName ) const;
	void SetSubstanceVector( const std::string& inputName, const Vector4f& value );
	ColorRGBAf GetSubstanceColor( const std::string& inputName ) const;
	void SetSubstanceColor( const std::string& inputName, const ColorRGBAf& value );
	int GetSubstanceEnum( const string& inputName );
	void SetSubstanceEnum( const string& inputName, int value );
	Texture2D* GetSubstanceTexture( const string& inputName ) const;
	void SetSubstanceTexture( const string& inputName, Texture2D* value );

#if ENABLE_SUBSTANCE
	// Called by the SubstanceSystem when he process a "SetInput" command
	void Callback_SetSubstanceInput( const string& inputName, SubstanceValue& inputValue );
#endif

	const SubstanceInput* FindSubstanceInput( const string& inputName ) const;
	SubstanceInput* FindSubstanceInput( const string& inputName );

	// Property caching
	bool IsSubstancePropertyCached( const string& inputName ) const;
	void CacheSubstanceProperty( const string& inputName, bool value );
	void ClearCache();

	// Memory budget
	void SetProceduralMemoryBudget(ProceduralCacheSize budget);
	ProceduralCacheSize GetProceduralMemoryBudget() const;
	void SetProceduralMemoryWorkBudget(ProceduralCacheSize budget);
	ProceduralCacheSize GetProceduralMemoryWorkBudget() const;
	void SetProceduralMemorySleepBudget(ProceduralCacheSize budget);
	ProceduralCacheSize GetProceduralMemorySleepBudget() const;

protected:
	
#if ENABLE_SUBSTANCE
	void ApplyInputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID);
	void ApplyOutputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID, const std::set<unsigned int>& cachedTextureIDs);
#endif

private:
	
	// Shared substance data
	struct SubstanceData
	{
	    UInt8* substanceData;                       // Platform dependent linked binary content
	    SubstanceHandle* substanceHandle;           // Substance engine handle, shared by instances
	    ProceduralCacheSize memoryWorkBudget;       // 'Work' cache size, shared by instances 
	    ProceduralCacheSize memorySleepBudget;      // 'Sleep' cache size, shared by instances 
	    int instanceCount;                          // Count of substances using the same handle
	};

	PPtr<SubstanceArchive> m_SubstancePackage;      // The parent SBS package from which we get generated
	SubstanceArchive* m_PingedPackage;              // The pinged package
	SubstanceData* m_SubstanceData;                 // Shared substance data
	UnityStr m_PrototypeName;                       // The name of the original graph in the package
	int m_Width;                                    // Width
	int m_Height;                                   // Height
	Textures m_Textures;                            // The list of persistent output textures for that material
	PingedTextures m_PingedTextures;                // The list of pinged output textures
	SubstanceInputs m_Inputs;                       // Substance inputs
	static Mutex m_InputMutex;                      // Input accessors mutex
	Hash128 m_Hash;                                 // Hash used for cache status checking
	
public:
	// Texture inputs
	struct TextureInput
	{
		Texture2D* texture;
		Image* image;
		SubstanceTextureInput* inputParameters;
		void* buffer;
	};
	std::vector<TextureInput> m_TextureInputs;
#if UNITY_EDITOR
	std::vector<TextureInput>& GetTextureInputs() { return m_TextureInputs; }
#endif

	void ApplyTextureInput (int substanceInputIndex, const SubstanceTextureInput& requiredTextureInput);
	
	// Animated substances
private:
	int m_AnimationUpdateRate;
	float m_AnimationTime;
public:
	void SetAnimationUpdateRate(int rate) { m_AnimationUpdateRate = rate; }
	int GetAnimationUpdateRate() const { return m_AnimationUpdateRate; }
	void UpdateAnimation(float time);
	
	// Flags
public:
	enum Flag
	{
		Flag_DeprecatedGenerateAtLoad = 1<<0,  // deprecated
		Flag_Animated                 = 1<<2,  // the material has animated textures
		Flag_AwakeClone               = 1<<3,  // the material is a clone which require to be awaken
		Flag_GenerateAll              = 1<<4,  // we force the generation of all outputs
		Flag_ConstSize                = 1<<5,  // the size and seed don't change at runtime
		Flag_ForceGenerate            = 1<<6,  // force the generation
		Flag_Clone                    = 1<<7,  // the material is a clone
		Flag_Import                   = 1<<8,  // the material is being imported
		Flag_Awake                    = 1<<9,  // the material is awakening
		Flag_Uncompressed             = 1<<10, // the import is forced uncompressed
		Flag_Broken                   = 1<<11, // some dependencies are lacking, the substance can't be generated
		Flag_Readable                 = 1<<12  // generated textures are readable, provided it's in RAW format
	};
	void EnableFlag(const Flag& flag, bool enabled=true) { if (enabled) m_Flags |= (unsigned int)flag; else m_Flags &= ~(unsigned int)flag; }
	bool IsFlagEnabled(const Flag& flag) const { return m_Flags & (unsigned int)flag; }
private:
	unsigned int m_Flags;
	
	// Loading mode
public:
	void SetLoadingBehavior(ProceduralLoadingBehavior behavior) { m_LoadingBehavior = behavior; }
	ProceduralLoadingBehavior GetLoadingBehavior() const { return m_LoadingBehavior; }
private:
	ProceduralLoadingBehavior m_LoadingBehavior;

// Substance processing
public:
	bool IsProcessing() const;
	static void SetProceduralProcessorUsage(ProceduralProcessorUsage processorUsage);
	static ProceduralProcessorUsage GetProceduralProcessorUsage();
	static void StopProcessing();
    SubstanceData* GetSubstanceData() { return m_SubstanceData; }

// Presets handling
	bool SetPreset(const std::string& presetContent);
	std::string GetPreset() const;

// Textures accessors
	ProceduralTexture* GetGeneratedTexture(const std::string& textureName);
	
// Integration
	unsigned int integrationTimeStamp;

#if UNITY_EDITOR
	UInt8* GetHashPtr() { return m_Hash.hashData.bytes; }
	void InvalidateIfCachedOrInvalidTextures();
	
	friend struct TemporarilyStripSubstanceData;
#endif

// Enter/Leave PlayMode
#if UNITY_EDITOR
	bool m_isAlreadyLoadedInCurrentScene;
#endif

// Caching
#if ENABLE_SUBSTANCE && !UNITY_EDITOR
	std::string GetCacheFolder() const;
	std::string GetCacheFilename(const ProceduralTexture& texture) const;
	bool ReadCachedTexture(string& fileName, std::map<ProceduralTexture*, SubstanceTexture>& cachedTextures, const std::string& folder, const ProceduralTexture& texture);
	bool WriteCachedTexture(string& fileName, const std::string& folder, const ProceduralTexture& texture, const SubstanceTexture& data);
#endif
};