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
20namespace 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] = &copyParam<float>;
824 copyParamLookup[GPDT_FLOAT2] = &copyParam<Vector2>;
825 copyParamLookup[GPDT_FLOAT3] = &copyParam<Vector3>;
826 copyParamLookup[GPDT_FLOAT4] = &copyParam<Vector4>;
827
828 copyParamLookup[GPDT_INT1] = &copyParam<int>;
829 copyParamLookup[GPDT_INT2] = &copyParam<Vector2I>;
830 copyParamLookup[GPDT_INT3] = &copyParam<Vector3I>;
831 copyParamLookup[GPDT_INT4] = &copyParam<Vector4I>;
832
833 copyParamLookup[GPDT_MATRIX_2X2] = &copyParam<Matrix2>;
834 copyParamLookup[GPDT_MATRIX_2X3] = &copyParam<Matrix2x3>;
835 copyParamLookup[GPDT_MATRIX_2X4] = &copyParam<Matrix2x4>;
836
837 copyParamLookup[GPDT_MATRIX_3X3] = &copyParam<Matrix3>;
838 copyParamLookup[GPDT_MATRIX_3X2] = &copyParam<Matrix3x2>;
839 copyParamLookup[GPDT_MATRIX_3X4] = &copyParam<Matrix3x4>;
840
841 copyParamLookup[GPDT_MATRIX_4X4] = &copyParam<Matrix4>;
842 copyParamLookup[GPDT_MATRIX_4X2] = &copyParam<Matrix4x2>;
843 copyParamLookup[GPDT_MATRIX_4X3] = &copyParam<Matrix4x3>;
844
845 copyParamLookup[GPDT_BOOL] = &copyParam<int>;
846 copyParamLookup[GPDT_COLOR] = &copyParam<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, &paramData);
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, &paramData);
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, &paramData);
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, &paramData);
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