1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "Material/BsMaterial.h" |
4 | #include "Material/BsShader.h" |
5 | #include "Material/BsTechnique.h" |
6 | #include "Material/BsPass.h" |
7 | #include "RenderAPI/BsRenderAPI.h" |
8 | #include "Private/RTTI/BsMaterialRTTI.h" |
9 | #include "Resources/BsResources.h" |
10 | #include "Math/BsMatrixNxM.h" |
11 | #include "Math/BsVector3I.h" |
12 | #include "Math/BsVector4I.h" |
13 | #include "Serialization/BsMemorySerializer.h" |
14 | #include "Material/BsMaterialParams.h" |
15 | #include "Material/BsGpuParamsSet.h" |
16 | #include "Animation/BsAnimationCurve.h" |
17 | #include "CoreThread/BsCoreObjectSync.h" |
18 | #include "Private/RTTI/BsShaderVariationRTTI.h" |
19 | |
20 | namespace bs |
21 | { |
22 | enum MaterialLoadFlags |
23 | { |
24 | Load_None = 0, |
25 | Load_Shader = 1, |
26 | Load_All = 2 |
27 | }; |
28 | |
29 | template<class T> |
30 | bool isShaderValid(const T& shader) { return false; } |
31 | |
32 | template<> |
33 | bool isShaderValid(const HShader& shader) { return shader.isLoaded(); } |
34 | |
35 | template<> |
36 | bool isShaderValid(const SPtr<ct::Shader>& shader) { return shader != nullptr; } |
37 | |
38 | template<bool Core> |
39 | SPtr<CoreVariantType<Material, Core>> getMaterialPtr(const TMaterial<Core>* material) |
40 | { |
41 | return std::static_pointer_cast<CoreVariantType<Material, Core>>( |
42 | static_cast<const CoreVariantType<Material, Core>*>(material)->getThisPtr()); |
43 | } |
44 | |
45 | template<bool Core> |
46 | SPtr<typename TMaterial<Core>::GpuParamsSetType> TMaterial<Core>::createParamsSet(UINT32 techniqueIdx) |
47 | { |
48 | if (techniqueIdx >= (UINT32)mTechniques.size()) |
49 | return nullptr; |
50 | |
51 | SPtr<TechniqueType> technique = mTechniques[techniqueIdx]; |
52 | return bs_shared_ptr_new<GpuParamsSetType>(technique, mShader, mParams); |
53 | } |
54 | |
55 | template<bool Core> |
56 | void TMaterial<Core>::updateParamsSet(const SPtr<GpuParamsSetType>& paramsSet, float t, bool updateAll) |
57 | { |
58 | paramsSet->update(mParams, t, updateAll); |
59 | } |
60 | |
61 | template<bool Core> |
62 | UINT32 TMaterial<Core>::findTechnique(const FIND_TECHNIQUE_DESC& desc) const |
63 | { |
64 | UINT32 bestTechniqueIdx = (UINT32)-1; |
65 | UINT32 bestTechniqueScore = std::numeric_limits<UINT32>::max(); |
66 | |
67 | for(UINT32 i = 0; i < (UINT32)mTechniques.size(); i++) |
68 | { |
69 | // Make sure tags match |
70 | bool foundMatch = true; |
71 | for(UINT32 j = 0; j < desc.numTags; j++) |
72 | { |
73 | if (!mTechniques[i]->hasTag(desc.tags[j])) |
74 | { |
75 | foundMatch = false; |
76 | break; |
77 | } |
78 | } |
79 | |
80 | if(!foundMatch) |
81 | continue; |
82 | |
83 | const ShaderVariation& curVariation = mTechniques[i]->getVariation(); |
84 | const auto& curVarParams = curVariation.getParams(); |
85 | const auto& internalVarParams = mVariation.getParams(); |
86 | |
87 | UINT32 numMatchedSearchParams = 0; |
88 | UINT32 numMatchedInternalParams = 0; |
89 | UINT32 currentScore = 0; |
90 | for(auto& param : curVarParams) |
91 | { |
92 | enum SearchResult |
93 | { |
94 | NoParam, |
95 | NotMatching, |
96 | Matching |
97 | }; |
98 | |
99 | SearchResult matchesSearch = NoParam; |
100 | if(desc.variation) |
101 | { |
102 | const auto& searchVarParams = desc.variation->getParams(); |
103 | const auto findSearch = searchVarParams.find(param.first); |
104 | if(findSearch != searchVarParams.end()) |
105 | matchesSearch = findSearch->second.i == param.second.i ? Matching : NotMatching; |
106 | } |
107 | |
108 | SearchResult matchesInternal = NoParam; |
109 | const auto findInternal = internalVarParams.find(param.first); |
110 | if (findInternal != internalVarParams.end()) |
111 | matchesInternal = findInternal->second.i == param.second.i ? Matching : NotMatching; |
112 | |
113 | switch(matchesSearch) |
114 | { |
115 | default: |
116 | case NoParam: |
117 | switch(matchesInternal) |
118 | { |
119 | default: |
120 | case NoParam: |
121 | // When it comes to parameters not part of the search, prefer those with 0 default value |
122 | currentScore += param.second.ui; |
123 | break; |
124 | case NotMatching: |
125 | foundMatch = false; |
126 | break; |
127 | case Matching: |
128 | numMatchedInternalParams++; |
129 | break; |
130 | } |
131 | break; |
132 | case NotMatching: |
133 | if(desc.override) |
134 | { |
135 | foundMatch = false; |
136 | break; |
137 | } |
138 | |
139 | switch(matchesInternal) |
140 | { |
141 | default: |
142 | case NoParam: |
143 | foundMatch = false; |
144 | break; |
145 | case NotMatching: |
146 | foundMatch = false; |
147 | break; |
148 | case Matching: |
149 | numMatchedSearchParams++; |
150 | numMatchedInternalParams++; |
151 | break; |
152 | } |
153 | break; |
154 | case Matching: |
155 | switch(matchesInternal) |
156 | { |
157 | default: |
158 | case NoParam: |
159 | numMatchedSearchParams++; |
160 | break; |
161 | case NotMatching: |
162 | if(desc.override) |
163 | { |
164 | numMatchedSearchParams++; |
165 | numMatchedInternalParams++; |
166 | } |
167 | else |
168 | foundMatch = false; |
169 | break; |
170 | case Matching: |
171 | numMatchedSearchParams++; |
172 | numMatchedInternalParams++; |
173 | break; |
174 | } |
175 | break; |
176 | } |
177 | |
178 | if(!foundMatch) |
179 | break; |
180 | } |
181 | |
182 | if (!foundMatch) |
183 | continue; |
184 | |
185 | if(desc.variation) |
186 | { |
187 | const auto& searchVarParams = desc.variation->getParams(); |
188 | if(numMatchedSearchParams != (UINT32)searchVarParams.size()) |
189 | continue; |
190 | } |
191 | |
192 | if(numMatchedInternalParams != (UINT32)internalVarParams.size()) |
193 | continue; |
194 | |
195 | if (currentScore < bestTechniqueScore) |
196 | { |
197 | bestTechniqueIdx = i; |
198 | bestTechniqueScore = currentScore; |
199 | } |
200 | } |
201 | |
202 | return bestTechniqueIdx; |
203 | |
204 | } |
205 | |
206 | template<bool Core> |
207 | UINT32 TMaterial<Core>::getDefaultTechnique() const |
208 | { |
209 | UINT32 bestTechniqueIdx = 0; |
210 | UINT32 bestTechniqueScore = std::numeric_limits<UINT32>::max(); |
211 | |
212 | for (UINT32 i = 0; i < (UINT32)mTechniques.size(); i++) |
213 | { |
214 | if (mTechniques[i]->hasTags()) |
215 | continue; |
216 | |
217 | const ShaderVariation& curVariation = mTechniques[i]->getVariation(); |
218 | const auto& curVarParams = curVariation.getParams(); |
219 | const auto& internalVarParams = mVariation.getParams(); |
220 | |
221 | bool foundMatch = true; |
222 | UINT32 numMatchedParams = 0; |
223 | UINT32 currentScore = 0; |
224 | for(auto& param : curVarParams) |
225 | { |
226 | enum SearchResult |
227 | { |
228 | NoParam, |
229 | NotMatching, |
230 | Matching |
231 | }; |
232 | |
233 | SearchResult matches = NoParam; |
234 | const auto findInternal = internalVarParams.find(param.first); |
235 | if (findInternal != internalVarParams.end()) |
236 | matches = findInternal->second.i == param.second.i ? Matching : NotMatching; |
237 | |
238 | switch(matches) |
239 | { |
240 | default: |
241 | case NoParam: |
242 | // When it comes to parameters not part of the search, prefer those with 0 default value |
243 | currentScore += param.second.ui; |
244 | break; |
245 | case NotMatching: |
246 | foundMatch = false; |
247 | break; |
248 | case Matching: |
249 | numMatchedParams++; |
250 | break; |
251 | } |
252 | |
253 | if(!foundMatch) |
254 | break; |
255 | } |
256 | |
257 | if (!foundMatch) |
258 | continue; |
259 | |
260 | if(numMatchedParams != (UINT32)internalVarParams.size()) |
261 | continue; |
262 | |
263 | if (currentScore < bestTechniqueScore) |
264 | { |
265 | bestTechniqueIdx = i; |
266 | bestTechniqueScore = currentScore; |
267 | } |
268 | } |
269 | |
270 | return bestTechniqueIdx; |
271 | } |
272 | |
273 | template<bool Core> |
274 | UINT32 TMaterial<Core>::getNumPasses(UINT32 techniqueIdx) const |
275 | { |
276 | if (mShader == nullptr) |
277 | return 0; |
278 | |
279 | if (techniqueIdx >= (UINT32)mTechniques.size()) |
280 | return 0; |
281 | |
282 | return mTechniques[techniqueIdx]->getNumPasses(); |
283 | } |
284 | |
285 | template<bool Core> |
286 | SPtr<typename TMaterial<Core>::PassType> TMaterial<Core>::getPass(UINT32 passIdx, UINT32 techniqueIdx) const |
287 | { |
288 | if (mShader == nullptr) |
289 | return nullptr; |
290 | |
291 | if (techniqueIdx >= (UINT32)mTechniques.size()) |
292 | return nullptr; |
293 | |
294 | if (passIdx < 0 || passIdx >= mTechniques[techniqueIdx]->getNumPasses()) |
295 | return nullptr; |
296 | |
297 | return mTechniques[techniqueIdx]->getPass(passIdx); |
298 | } |
299 | |
300 | template<bool Core> |
301 | TMaterialParamStruct<Core> TMaterial<Core>::getParamStruct(const String& name) const |
302 | { |
303 | throwIfNotInitialized(); |
304 | |
305 | return TMaterialParamStruct<Core>(name, getMaterialPtr(this)); |
306 | } |
307 | |
308 | template<bool Core> |
309 | TMaterialColorGradientParam<Core> TMaterial<Core>::getParamColorGradient(const String& name) const |
310 | { |
311 | throwIfNotInitialized(); |
312 | |
313 | return TMaterialColorGradientParam<Core>(name, getMaterialPtr(this)); |
314 | } |
315 | |
316 | template <bool Core> |
317 | TMaterialCurveParam<float, Core> TMaterial<Core>::getParamFloatCurve(const String& name) const |
318 | { |
319 | throwIfNotInitialized(); |
320 | |
321 | return TMaterialCurveParam<float, Core>(name, getMaterialPtr(this)); |
322 | } |
323 | |
324 | template<bool Core> |
325 | TMaterialParamTexture<Core> TMaterial<Core>::getParamTexture(const String& name) const |
326 | { |
327 | throwIfNotInitialized(); |
328 | |
329 | return TMaterialParamTexture<Core>(name, getMaterialPtr(this)); |
330 | } |
331 | |
332 | template<bool Core> |
333 | TMaterialParamSpriteTexture<Core> TMaterial<Core>::getParamSpriteTexture(const String& name) const |
334 | { |
335 | throwIfNotInitialized(); |
336 | |
337 | return TMaterialParamSpriteTexture<Core>(name, getMaterialPtr(this)); |
338 | } |
339 | |
340 | template<bool Core> |
341 | TMaterialParamLoadStoreTexture<Core> TMaterial<Core>::getParamLoadStoreTexture(const String& name) const |
342 | { |
343 | throwIfNotInitialized(); |
344 | |
345 | return TMaterialParamLoadStoreTexture<Core>(name, getMaterialPtr(this)); |
346 | } |
347 | |
348 | template<bool Core> |
349 | TMaterialParamBuffer<Core> TMaterial<Core>::getParamBuffer(const String& name) const |
350 | { |
351 | throwIfNotInitialized(); |
352 | |
353 | return TMaterialParamBuffer<Core>(name, getMaterialPtr(this)); |
354 | } |
355 | |
356 | template<bool Core> |
357 | TMaterialParamSampState<Core> TMaterial<Core>::getParamSamplerState(const String& name) const |
358 | { |
359 | throwIfNotInitialized(); |
360 | |
361 | return TMaterialParamSampState<Core>(name, getMaterialPtr(this)); |
362 | } |
363 | |
364 | template <bool Core> |
365 | bool TMaterial<Core>::isAnimated(const String& name, UINT32 arrayIdx) |
366 | { |
367 | return mParams->isAnimated(name, arrayIdx); |
368 | } |
369 | |
370 | template<bool Core> |
371 | void TMaterial<Core>::initializeTechniques() |
372 | { |
373 | mTechniques.clear(); |
374 | |
375 | if (isShaderValid(mShader)) |
376 | { |
377 | mParams = bs_shared_ptr_new<MaterialParamsType>(mShader); |
378 | mTechniques = mShader->getCompatibleTechniques(); |
379 | |
380 | if (mTechniques.empty()) |
381 | return; |
382 | |
383 | initDefaultParameters(); |
384 | } |
385 | else |
386 | mParams = nullptr; |
387 | |
388 | _markDependenciesDirty(); |
389 | } |
390 | |
391 | template <bool Core> |
392 | template <typename T> |
393 | void TMaterial<Core>::setParamValue(const String& name, UINT8* buffer, UINT32 numElements) |
394 | { |
395 | TMaterialDataParam<T, Core> param; |
396 | getParam(name, param); |
397 | |
398 | T* ptr = (T*)buffer; |
399 | for (UINT32 i = 0; i < numElements; i++) |
400 | param.set(ptr[i], i); |
401 | } |
402 | |
403 | template <bool Core> |
404 | void TMaterial<Core>::initDefaultParameters() |
405 | { |
406 | const Map<String, SHADER_DATA_PARAM_DESC>& dataParams = mShader->getDataParams(); |
407 | for (auto& paramData : dataParams) |
408 | { |
409 | if (paramData.second.defaultValueIdx == (UINT32)-1) |
410 | continue; |
411 | |
412 | UINT8* buffer = (UINT8*)mShader->getDefaultValue(paramData.second.defaultValueIdx); |
413 | if (buffer == nullptr) |
414 | continue; |
415 | |
416 | switch (paramData.second.type) |
417 | { |
418 | case GPDT_FLOAT1: |
419 | setParamValue<float>(paramData.first, buffer, paramData.second.arraySize); |
420 | break; |
421 | case GPDT_FLOAT2: |
422 | setParamValue<Vector2>(paramData.first, buffer, paramData.second.arraySize); |
423 | break; |
424 | case GPDT_FLOAT3: |
425 | setParamValue<Vector3>(paramData.first, buffer, paramData.second.arraySize); |
426 | break; |
427 | case GPDT_FLOAT4: |
428 | setParamValue<Vector4>(paramData.first, buffer, paramData.second.arraySize); |
429 | break; |
430 | case GPDT_MATRIX_2X2: |
431 | setParamValue<Matrix2>(paramData.first, buffer, paramData.second.arraySize); |
432 | break; |
433 | case GPDT_MATRIX_2X3: |
434 | setParamValue<Matrix2x3>(paramData.first, buffer, paramData.second.arraySize); |
435 | break; |
436 | case GPDT_MATRIX_2X4: |
437 | setParamValue<Matrix2x4>(paramData.first, buffer, paramData.second.arraySize); |
438 | break; |
439 | case GPDT_MATRIX_3X2: |
440 | setParamValue<Matrix3x2>(paramData.first, buffer, paramData.second.arraySize); |
441 | break; |
442 | case GPDT_MATRIX_3X3: |
443 | setParamValue<Matrix3>(paramData.first, buffer, paramData.second.arraySize); |
444 | break; |
445 | case GPDT_MATRIX_3X4: |
446 | setParamValue<Matrix3x4>(paramData.first, buffer, paramData.second.arraySize); |
447 | break; |
448 | case GPDT_MATRIX_4X2: |
449 | setParamValue<Matrix4x2>(paramData.first, buffer, paramData.second.arraySize); |
450 | break; |
451 | case GPDT_MATRIX_4X3: |
452 | setParamValue<Matrix4x3>(paramData.first, buffer, paramData.second.arraySize); |
453 | break; |
454 | case GPDT_MATRIX_4X4: |
455 | setParamValue<Matrix4>(paramData.first, buffer, paramData.second.arraySize); |
456 | break; |
457 | case GPDT_INT1: |
458 | setParamValue<int>(paramData.first, buffer, paramData.second.arraySize); |
459 | break; |
460 | case GPDT_INT2: |
461 | setParamValue<Vector2I>(paramData.first, buffer, paramData.second.arraySize); |
462 | break; |
463 | case GPDT_INT3: |
464 | setParamValue<Vector3I>(paramData.first, buffer, paramData.second.arraySize); |
465 | break; |
466 | case GPDT_INT4: |
467 | setParamValue<Vector4I>(paramData.first, buffer, paramData.second.arraySize); |
468 | break; |
469 | case GPDT_BOOL: |
470 | setParamValue<int>(paramData.first, buffer, paramData.second.arraySize); |
471 | break; |
472 | case GPDT_COLOR: |
473 | setParamValue<Color>(paramData.first, buffer, paramData.second.arraySize); |
474 | break; |
475 | case GPDT_STRUCT: |
476 | { |
477 | TMaterialParamStruct<Core> param = getParamStruct(paramData.first); |
478 | |
479 | UINT32 elementSizeBytes = paramData.second.elementSize * sizeof(UINT32); |
480 | UINT8* ptr = buffer; |
481 | for (UINT32 i = 0; i < paramData.second.arraySize; i++) |
482 | { |
483 | param.set(ptr, elementSizeBytes, i); |
484 | ptr += elementSizeBytes; |
485 | } |
486 | } |
487 | break; |
488 | default: |
489 | break; |
490 | } |
491 | } |
492 | |
493 | const Map<String, SHADER_OBJECT_PARAM_DESC>& textureParams = mShader->getTextureParams(); |
494 | for (auto& param : textureParams) |
495 | { |
496 | if (param.second.defaultValueIdx == (UINT32)-1) |
497 | continue; |
498 | |
499 | TextureType defaultTex = mShader->getDefaultTexture(param.second.defaultValueIdx); |
500 | getParamTexture(param.first).set(defaultTex); |
501 | } |
502 | |
503 | const Map<String, SHADER_OBJECT_PARAM_DESC>& samplerParams = mShader->getSamplerParams(); |
504 | for (auto& param : samplerParams) |
505 | { |
506 | if (param.second.defaultValueIdx == (UINT32)-1) |
507 | continue; |
508 | |
509 | SamplerStateType defaultSampler = mShader->getDefaultSampler(param.second.defaultValueIdx); |
510 | getParamSamplerState(param.first).set(defaultSampler); |
511 | } |
512 | } |
513 | |
514 | template <bool Core> |
515 | template <typename T> |
516 | void TMaterial<Core>::getParam(const String& name, TMaterialDataParam<T, Core>& output) const |
517 | { |
518 | throwIfNotInitialized(); |
519 | |
520 | output = TMaterialDataParam<T, Core>(name, getMaterialPtr(this)); |
521 | } |
522 | |
523 | template<bool Core> |
524 | void TMaterial<Core>::throwIfNotInitialized() const |
525 | { |
526 | if (mShader == nullptr) |
527 | BS_EXCEPT(InternalErrorException, "Material does not have shader set." ); |
528 | |
529 | if (mTechniques.empty()) |
530 | BS_EXCEPT(InternalErrorException, "Shader does not contain a supported technique." ); |
531 | } |
532 | |
533 | template class TMaterial < false > ; |
534 | template class TMaterial < true > ; |
535 | |
536 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<float, false>&) const; |
537 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<int, false>&) const; |
538 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Color, false>&) const; |
539 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector2, false>&) const; |
540 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector3, false>&) const; |
541 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector4, false>&) const; |
542 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector2I, false>&) const; |
543 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector3I, false>&) const; |
544 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Vector4I, false>&) const; |
545 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix2, false>&) const; |
546 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix2x3, false>&) const; |
547 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix2x4, false>&) const; |
548 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix3, false>&) const; |
549 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix3x2, false>&) const; |
550 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix3x4, false>&) const; |
551 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix4, false>&) const; |
552 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix4x2, false>&) const; |
553 | template BS_CORE_EXPORT void TMaterial<false>::getParam(const String&, TMaterialDataParam<Matrix4x3, false>&) const; |
554 | |
555 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<float, true>&) const; |
556 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<int, true>&) const; |
557 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Color, true>&) const; |
558 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector2, true>&) const; |
559 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector3, true>&) const; |
560 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector4, true>&) const; |
561 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector2I, true>&) const; |
562 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector3I, true>&) const; |
563 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Vector4I, true>&) const; |
564 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix2, true>&) const; |
565 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix2x3, true>&) const; |
566 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix2x4, true>&) const; |
567 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix3, true>&) const; |
568 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix3x2, true>&) const; |
569 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix3x4, true>&) const; |
570 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix4, true>&) const; |
571 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix4x2, true>&) const; |
572 | template BS_CORE_EXPORT void TMaterial<true>::getParam(const String&, TMaterialDataParam<Matrix4x3, true>&) const; |
573 | |
574 | Material::Material() |
575 | :mLoadFlags(Load_None) |
576 | { } |
577 | |
578 | Material::Material(const HShader& shader, const ShaderVariation& variation) |
579 | :mLoadFlags(Load_None) |
580 | { |
581 | mShader = shader; |
582 | mVariation = variation; |
583 | } |
584 | |
585 | void Material::initialize() |
586 | { |
587 | addResourceDependency(mShader); |
588 | _markResourcesDirty(); |
589 | initializeIfLoaded(); |
590 | |
591 | Resource::initialize(); |
592 | } |
593 | |
594 | void Material::setShader(const HShader& shader) |
595 | { |
596 | if (mShader == shader) |
597 | return; |
598 | |
599 | removeResourceDependency(mShader); |
600 | mShader = shader; |
601 | addResourceDependency(mShader); |
602 | |
603 | mTechniques.clear(); |
604 | mLoadFlags = Load_None; |
605 | |
606 | // Make sure to clear params, because the default behaviour is to re-apply them (which won't work due to changed |
607 | // shader) |
608 | mParams = nullptr; |
609 | |
610 | _markResourcesDirty(); |
611 | |
612 | initializeIfLoaded(); |
613 | } |
614 | |
615 | void Material::setVariation(const ShaderVariation& variation) |
616 | { |
617 | mVariation = variation; |
618 | markCoreDirty(); |
619 | } |
620 | |
621 | void Material::_markCoreDirty(MaterialDirtyFlags flags) |
622 | { |
623 | markCoreDirty((UINT32)flags); |
624 | } |
625 | |
626 | void Material::_markDependenciesDirty() |
627 | { |
628 | markDependenciesDirty(); |
629 | } |
630 | |
631 | void Material::_markResourcesDirty() |
632 | { |
633 | markListenerResourcesDirty(); |
634 | } |
635 | |
636 | SPtr<ct::Material> Material::getCore() const |
637 | { |
638 | return std::static_pointer_cast<ct::Material>(mCoreSpecific); |
639 | } |
640 | |
641 | SPtr<ct::CoreObject> Material::createCore() const |
642 | { |
643 | ct::Material* material = nullptr; |
644 | |
645 | SPtr<ct::Shader> shader; |
646 | if (mShader.isLoaded()) |
647 | { |
648 | shader = mShader->getCore(); |
649 | |
650 | Vector<SPtr<ct::Technique>> techniques(mTechniques.size()); |
651 | for (UINT32 i = 0; i < (UINT32)mTechniques.size(); i++) |
652 | techniques[i] = mTechniques[i]->getCore(); |
653 | |
654 | SPtr<ct::MaterialParams> materialParams = bs_shared_ptr_new<ct::MaterialParams>(shader, mParams); |
655 | |
656 | material = new (bs_alloc<ct::Material>()) ct::Material(shader, techniques, materialParams, mVariation); |
657 | } |
658 | |
659 | if (material == nullptr) |
660 | material = new (bs_alloc<ct::Material>()) ct::Material(shader, mVariation); |
661 | |
662 | SPtr<ct::Material> materialPtr = bs_shared_ptr<ct::Material>(material); |
663 | materialPtr->_setThisPtr(materialPtr); |
664 | |
665 | return materialPtr; |
666 | } |
667 | |
668 | CoreSyncData Material::syncToCore(FrameAlloc* allocator) |
669 | { |
670 | const UINT32 dirtyParam = (UINT32)MaterialDirtyFlags::Param; |
671 | const bool syncAllParams = (getCoreDirtyFlags() & ~dirtyParam) != 0; |
672 | |
673 | UINT32 paramsSize = 0; |
674 | if (mParams != nullptr) |
675 | mParams->getSyncData(nullptr, paramsSize, syncAllParams); |
676 | |
677 | UINT32 numTechniques = (UINT32)mTechniques.size(); |
678 | UINT32 size = sizeof(bool) + sizeof(UINT32) * 2 + sizeof(SPtr<ct::Shader>) + |
679 | sizeof(SPtr<ct::Technique>) * numTechniques + paramsSize; |
680 | |
681 | size += coreSyncGetElemSize(mVariation); |
682 | |
683 | UINT8* buffer = allocator->alloc(size); |
684 | char* dataPtr = (char*)buffer; |
685 | |
686 | dataPtr = rttiWriteElem(syncAllParams, dataPtr); |
687 | |
688 | SPtr<ct::Shader>* shader = new (dataPtr)SPtr<ct::Shader>(); |
689 | if (mShader.isLoaded(false)) |
690 | *shader = mShader->getCore(); |
691 | else |
692 | *shader = nullptr; |
693 | |
694 | dataPtr += sizeof(SPtr<ct::Shader>); |
695 | dataPtr = rttiWriteElem(numTechniques, dataPtr); |
696 | |
697 | for(UINT32 i = 0; i < numTechniques; i++) |
698 | { |
699 | SPtr<ct::Technique>* technique = new (dataPtr) SPtr<ct::Technique>(); |
700 | *technique = mTechniques[i]->getCore(); |
701 | |
702 | dataPtr += sizeof(SPtr<ct::Technique>); |
703 | } |
704 | |
705 | dataPtr = rttiWriteElem(paramsSize, dataPtr); |
706 | if (mParams != nullptr) |
707 | mParams->getSyncData((UINT8*)dataPtr, paramsSize, syncAllParams); |
708 | |
709 | dataPtr += paramsSize; |
710 | dataPtr = coreSyncWriteElem(mVariation, dataPtr); |
711 | |
712 | return CoreSyncData(buffer, size); |
713 | } |
714 | |
715 | void Material::getCoreDependencies(Vector<CoreObject*>& dependencies) |
716 | { |
717 | if (mShader.isLoaded()) |
718 | dependencies.push_back(mShader.get()); |
719 | |
720 | if(mParams != nullptr) |
721 | mParams->getCoreObjectDependencies(dependencies); |
722 | } |
723 | |
724 | void Material::getListenerResources(Vector<HResource>& resources) |
725 | { |
726 | if (mShader != nullptr) |
727 | resources.push_back(mShader); |
728 | |
729 | if (mParams != nullptr) |
730 | mParams->getResourceDependencies(resources); |
731 | } |
732 | |
733 | void Material::initializeIfLoaded() |
734 | { |
735 | if (areDependenciesLoaded()) |
736 | { |
737 | if (mLoadFlags != Load_All) |
738 | { |
739 | mLoadFlags = Load_All; |
740 | |
741 | // Shader about to change, so save parameters, rebuild material and restore parameters |
742 | SPtr<MaterialParams> oldParams = mParams; |
743 | |
744 | initializeTechniques(); |
745 | markCoreDirty(); |
746 | |
747 | if (mTechniques.empty()) // Wasn't initialized |
748 | return; |
749 | |
750 | if(oldParams) |
751 | setParams(oldParams); |
752 | } |
753 | } |
754 | else |
755 | { |
756 | if (mShader.isLoaded() && mLoadFlags == Load_None) |
757 | { |
758 | mLoadFlags = Load_Shader; |
759 | markListenerResourcesDirty(); // Need to register resources dependent on shader now |
760 | } |
761 | } |
762 | } |
763 | |
764 | void Material::notifyResourceLoaded(const HResource& resource) |
765 | { |
766 | // Ready to initialize as soon as shader loads |
767 | if (resource->getRTTI()->getRTTIId() == TID_Shader) |
768 | initializeIfLoaded(); |
769 | } |
770 | |
771 | void Material::notifyResourceChanged(const HResource& resource) |
772 | { |
773 | // Need full rebuild if shader changed |
774 | if (resource->getRTTI()->getRTTIId() == TID_Shader) |
775 | { |
776 | mLoadFlags = Load_None; |
777 | initializeIfLoaded(); |
778 | } |
779 | else |
780 | { |
781 | // Otherwise just sync changes (most likely just a texture got reimported) |
782 | _markCoreDirty(MaterialDirtyFlags::ParamResource); |
783 | } |
784 | } |
785 | |
786 | HMaterial Material::clone() |
787 | { |
788 | UINT32 bufferSize = 0; |
789 | |
790 | MemorySerializer serializer; |
791 | UINT8* buffer = serializer.encode(this, bufferSize, (void*(*)(size_t))&bs_alloc); |
792 | |
793 | SPtr<Material> cloneObj = std::static_pointer_cast<Material>(serializer.decode(buffer, bufferSize)); |
794 | bs_free(buffer); |
795 | |
796 | return static_resource_cast<Material>(gResources()._createResourceHandle(cloneObj)); |
797 | } |
798 | |
799 | template<class T> |
800 | void copyParam(const SPtr<MaterialParams>& from, Material* to, const String& name, |
801 | const MaterialParams::ParamData& paramRef, UINT32 arraySize) |
802 | { |
803 | TMaterialDataParam<T, false> param; |
804 | to->getParam(name, param); |
805 | |
806 | T paramData; |
807 | for (UINT32 i = 0; i < arraySize; i++) |
808 | { |
809 | from->getDataParam(paramRef, i, paramData); |
810 | param.set(paramData, i); |
811 | } |
812 | } |
813 | |
814 | void Material::setParams(const SPtr<MaterialParams>& params) |
815 | { |
816 | if (params == nullptr) |
817 | return; |
818 | |
819 | std::function< |
820 | void(const SPtr<MaterialParams>&, Material*, const String&, const MaterialParams::ParamData&, UINT32)> |
821 | copyParamLookup[GPDT_COUNT]; |
822 | |
823 | copyParamLookup[GPDT_FLOAT1] = ©Param<float>; |
824 | copyParamLookup[GPDT_FLOAT2] = ©Param<Vector2>; |
825 | copyParamLookup[GPDT_FLOAT3] = ©Param<Vector3>; |
826 | copyParamLookup[GPDT_FLOAT4] = ©Param<Vector4>; |
827 | |
828 | copyParamLookup[GPDT_INT1] = ©Param<int>; |
829 | copyParamLookup[GPDT_INT2] = ©Param<Vector2I>; |
830 | copyParamLookup[GPDT_INT3] = ©Param<Vector3I>; |
831 | copyParamLookup[GPDT_INT4] = ©Param<Vector4I>; |
832 | |
833 | copyParamLookup[GPDT_MATRIX_2X2] = ©Param<Matrix2>; |
834 | copyParamLookup[GPDT_MATRIX_2X3] = ©Param<Matrix2x3>; |
835 | copyParamLookup[GPDT_MATRIX_2X4] = ©Param<Matrix2x4>; |
836 | |
837 | copyParamLookup[GPDT_MATRIX_3X3] = ©Param<Matrix3>; |
838 | copyParamLookup[GPDT_MATRIX_3X2] = ©Param<Matrix3x2>; |
839 | copyParamLookup[GPDT_MATRIX_3X4] = ©Param<Matrix3x4>; |
840 | |
841 | copyParamLookup[GPDT_MATRIX_4X4] = ©Param<Matrix4>; |
842 | copyParamLookup[GPDT_MATRIX_4X2] = ©Param<Matrix4x2>; |
843 | copyParamLookup[GPDT_MATRIX_4X3] = ©Param<Matrix4x3>; |
844 | |
845 | copyParamLookup[GPDT_BOOL] = ©Param<int>; |
846 | copyParamLookup[GPDT_COLOR] = ©Param<Color>; |
847 | |
848 | auto& dataParams = mShader->getDataParams(); |
849 | for (auto& param : dataParams) |
850 | { |
851 | UINT32 arraySize = param.second.arraySize > 1 ? param.second.arraySize : 1; |
852 | |
853 | const MaterialParams::ParamData* paramData = nullptr; |
854 | auto result = params->getParamData(param.first, MaterialParams::ParamType::Data, param.second.type, 0, ¶mData); |
855 | |
856 | if (result != MaterialParams::GetParamResult::Success) |
857 | continue; |
858 | |
859 | UINT32 elemsToCopy = std::min(arraySize, paramData->arraySize); |
860 | |
861 | auto& copyFunction = copyParamLookup[param.second.type]; |
862 | if (copyFunction != nullptr) |
863 | copyFunction(params, this, param.first, *paramData, elemsToCopy); |
864 | else |
865 | { |
866 | if(param.second.type == GPDT_STRUCT) |
867 | { |
868 | TMaterialParamStruct<false> curParam = getParamStruct(param.first); |
869 | |
870 | UINT32 structSize = params->getStructSize(*paramData); |
871 | if (param.second.elementSize != structSize) |
872 | continue; |
873 | |
874 | UINT8* structData = (UINT8*)bs_stack_alloc(structSize); |
875 | for (UINT32 i = 0; i < elemsToCopy; i++) |
876 | { |
877 | params->getStructData(*paramData, structData, structSize, i); |
878 | curParam.set(structData, structSize, i); |
879 | } |
880 | |
881 | bs_stack_free(structData); |
882 | } |
883 | } |
884 | |
885 | for(UINT32 i = 0; i < arraySize; i++) |
886 | { |
887 | const bool isAnimated = params->isAnimated(*paramData, i); |
888 | if(!isAnimated) |
889 | continue; |
890 | |
891 | if(param.second.type == GPDT_FLOAT1) |
892 | { |
893 | TMaterialCurveParam<float, false> curParam = getParamFloatCurve(param.first); |
894 | curParam.set(params->getCurveParam<float>(*paramData, i), i); |
895 | } |
896 | else if(param.second.type == GPDT_COLOR) |
897 | { |
898 | TMaterialColorGradientParam<false> curParam = getParamColorGradient(param.first); |
899 | curParam.set(params->getColorGradientParam(*paramData, i), i); |
900 | } |
901 | } |
902 | } |
903 | |
904 | auto& textureParams = mShader->getTextureParams(); |
905 | for (auto& param : textureParams) |
906 | { |
907 | const MaterialParams::ParamData* paramData = nullptr; |
908 | auto result = params->getParamData(param.first, MaterialParams::ParamType::Texture, GPDT_UNKNOWN, 0, ¶mData); |
909 | |
910 | if (result != MaterialParams::GetParamResult::Success) |
911 | continue; |
912 | |
913 | MateralParamTextureType texType = params->getTextureType(*paramData); |
914 | switch(texType) |
915 | { |
916 | default: |
917 | case MateralParamTextureType::Normal: |
918 | { |
919 | TMaterialParamTexture<false> curParam = getParamTexture(param.first); |
920 | |
921 | HTexture texture; |
922 | TextureSurface surface; |
923 | params->getTexture(*paramData, texture, surface); |
924 | curParam.set(texture); |
925 | } |
926 | break; |
927 | case MateralParamTextureType::LoadStore: |
928 | { |
929 | TMaterialParamLoadStoreTexture<false> curParam = getParamLoadStoreTexture(param.first); |
930 | |
931 | HTexture texture; |
932 | TextureSurface surface; |
933 | params->getLoadStoreTexture(*paramData, texture, surface); |
934 | curParam.set(texture, surface); |
935 | } |
936 | break; |
937 | case MateralParamTextureType::Sprite: |
938 | { |
939 | TMaterialParamSpriteTexture<false> curParam = getParamSpriteTexture(param.first); |
940 | |
941 | HSpriteTexture texture; |
942 | params->getSpriteTexture(*paramData, texture); |
943 | curParam.set(texture); |
944 | } |
945 | break; |
946 | } |
947 | } |
948 | |
949 | auto& bufferParams = mShader->getBufferParams(); |
950 | for (auto& param : bufferParams) |
951 | { |
952 | const MaterialParams::ParamData* paramData = nullptr; |
953 | auto result = params->getParamData(param.first, MaterialParams::ParamType::Buffer, GPDT_UNKNOWN, 0, ¶mData); |
954 | |
955 | if (result != MaterialParams::GetParamResult::Success) |
956 | continue; |
957 | |
958 | TMaterialParamBuffer<false> curParam = getParamBuffer(param.first); |
959 | |
960 | SPtr<GpuBuffer> buffer; |
961 | params->getBuffer(*paramData, buffer); |
962 | curParam.set(buffer); |
963 | } |
964 | |
965 | auto& samplerParams = mShader->getSamplerParams(); |
966 | for (auto& param : samplerParams) |
967 | { |
968 | const MaterialParams::ParamData* paramData = nullptr; |
969 | auto result = params->getParamData(param.first, MaterialParams::ParamType::Sampler, GPDT_UNKNOWN, 0, ¶mData); |
970 | |
971 | if (result != MaterialParams::GetParamResult::Success) |
972 | continue; |
973 | |
974 | TMaterialParamSampState<false> curParam = getParamSamplerState(param.first); |
975 | |
976 | SPtr<SamplerState> samplerState; |
977 | params->getSamplerState(*paramData, samplerState); |
978 | curParam.set(samplerState); |
979 | } |
980 | } |
981 | |
982 | HMaterial Material::create() |
983 | { |
984 | const SPtr<Material> materialPtr = createEmpty(); |
985 | materialPtr->initialize(); |
986 | |
987 | return static_resource_cast<Material>(gResources()._createResourceHandle(materialPtr)); |
988 | } |
989 | |
990 | HMaterial Material::create(const HShader& shader) |
991 | { |
992 | return create(shader, ShaderVariation::EMPTY); |
993 | } |
994 | |
995 | HMaterial Material::create(const HShader& shader, const ShaderVariation& variation) |
996 | { |
997 | SPtr<Material> materialPtr = bs_core_ptr<Material>(new (bs_alloc<Material>()) Material(shader, variation)); |
998 | materialPtr->_setThisPtr(materialPtr); |
999 | materialPtr->initialize(); |
1000 | |
1001 | return static_resource_cast<Material>(gResources()._createResourceHandle(materialPtr)); |
1002 | } |
1003 | |
1004 | SPtr<Material> Material::createEmpty() |
1005 | { |
1006 | SPtr<Material> newMat = bs_core_ptr<Material>(new (bs_alloc<Material>()) Material()); |
1007 | newMat->_setThisPtr(newMat); |
1008 | |
1009 | return newMat; |
1010 | } |
1011 | |
1012 | RTTITypeBase* Material::getRTTIStatic() |
1013 | { |
1014 | return MaterialRTTI::instance(); |
1015 | } |
1016 | |
1017 | RTTITypeBase* Material::getRTTI() const |
1018 | { |
1019 | return Material::getRTTIStatic(); |
1020 | } |
1021 | |
1022 | namespace ct |
1023 | { |
1024 | Material::Material(const SPtr<Shader>& shader, const ShaderVariation& variation) |
1025 | { |
1026 | mVariation = variation; |
1027 | setShader(shader); |
1028 | } |
1029 | |
1030 | Material::Material(const SPtr<Shader>& shader, const Vector<SPtr<Technique>>& techniques, |
1031 | const SPtr<MaterialParams>& materialParams, const ShaderVariation& variation) |
1032 | { |
1033 | mShader = shader; |
1034 | mParams = materialParams; |
1035 | mTechniques = techniques; |
1036 | mVariation = variation; |
1037 | } |
1038 | |
1039 | void Material::setShader(const SPtr<Shader>& shader) |
1040 | { |
1041 | mShader = shader; |
1042 | |
1043 | initializeTechniques(); |
1044 | } |
1045 | |
1046 | void Material::setVariation(const ShaderVariation& variation) |
1047 | { |
1048 | mVariation = variation; |
1049 | } |
1050 | |
1051 | void Material::syncToCore(const CoreSyncData& data) |
1052 | { |
1053 | char* dataPtr = (char*)data.getBuffer(); |
1054 | |
1055 | bool syncAllParams; |
1056 | dataPtr = rttiReadElem(syncAllParams, dataPtr); |
1057 | |
1058 | UINT64 initialParamVersion = mParams != nullptr ? mParams->getParamVersion() : 1; |
1059 | if(syncAllParams) |
1060 | mParams = nullptr; |
1061 | |
1062 | SPtr<Shader>* shader = (SPtr<Shader>*)dataPtr; |
1063 | |
1064 | mShader = *shader; |
1065 | shader->~SPtr<Shader>(); |
1066 | dataPtr += sizeof(SPtr<Shader>); |
1067 | |
1068 | UINT32 numTechniques; |
1069 | dataPtr = rttiReadElem(numTechniques, dataPtr); |
1070 | |
1071 | mTechniques.resize(numTechniques); |
1072 | for(UINT32 i = 0; i < numTechniques; i++) |
1073 | { |
1074 | SPtr<Technique>* technique = (SPtr<Technique>*)dataPtr; |
1075 | mTechniques[i] = *technique; |
1076 | technique->~SPtr<Technique>(); |
1077 | dataPtr += sizeof(SPtr<Technique>); |
1078 | } |
1079 | |
1080 | UINT32 paramsSize = 0; |
1081 | dataPtr = rttiReadElem(paramsSize, dataPtr); |
1082 | if (mParams == nullptr && mShader != nullptr) |
1083 | mParams = bs_shared_ptr_new<MaterialParams>(mShader, initialParamVersion); |
1084 | |
1085 | if(mParams != nullptr && paramsSize > 0) |
1086 | mParams->setSyncData((UINT8*)dataPtr, paramsSize); |
1087 | |
1088 | dataPtr += paramsSize; |
1089 | |
1090 | mVariation.clearParams(); |
1091 | dataPtr = coreSyncReadElem(mVariation, dataPtr); |
1092 | } |
1093 | |
1094 | SPtr<Material> Material::create(const SPtr<Shader>& shader) |
1095 | { |
1096 | Material* material = new (bs_alloc<Material>()) Material(shader, ShaderVariation::EMPTY); |
1097 | SPtr<Material> materialPtr = bs_shared_ptr<Material>(material); |
1098 | materialPtr->_setThisPtr(materialPtr); |
1099 | materialPtr->initialize(); |
1100 | |
1101 | return materialPtr; |
1102 | } |
1103 | } |
1104 | } |
1105 | |