Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Material.cpp
Go to the documentation of this file.
1
2
3#include "Headers/Material.h"
5
10
12#include "Core/Headers/Kernel.h"
17
18namespace Divide
19{
20
21 namespace
22 {
23 constexpr size_t g_materialXMLVersion = 1u;
24 };
25
26 namespace TypeUtil
27 {
28 const char* MaterialDebugFlagToString( const MaterialDebugFlag materialFlag ) noexcept
29 {
30 return Names::materialDebugFlag[to_base( materialFlag )];
31 }
32
33 MaterialDebugFlag StringToMaterialDebugFlag( const std::string_view name )
34 {
35 for ( U8 i = 0; i < to_U8( MaterialDebugFlag::COUNT ); ++i )
36 {
37 if ( name == Names::materialDebugFlag[i] )
38 {
39 return static_cast<MaterialDebugFlag>(i);
40 }
41 }
42
44 }
45
46 const char* TextureSlotToString( const TextureSlot texUsage ) noexcept
47 {
48 return Names::textureSlot[to_base( texUsage )];
49 }
50
51 TextureSlot StringToTextureSlot( const std::string_view name )
52 {
53 for ( U8 i = 0; i < to_U8( TextureSlot::COUNT ); ++i )
54 {
55 if ( name == Names::textureSlot[i] )
56 {
57 return static_cast<TextureSlot>(i);
58 }
59 }
60
61 return TextureSlot::COUNT;
62 }
63
64 const char* BumpMethodToString( const BumpMethod bumpMethod ) noexcept
65 {
66 return Names::bumpMethod[to_base( bumpMethod )];
67 }
68
69 BumpMethod StringToBumpMethod( const std::string_view name )
70 {
71 for ( U8 i = 0; i < to_U8( BumpMethod::COUNT ); ++i )
72 {
73 if ( name == Names::bumpMethod[i] )
74 {
75 return static_cast<BumpMethod>(i);
76 }
77 }
78
79 return BumpMethod::COUNT;
80 }
81
82 const char* ShadingModeToString( const ShadingMode shadingMode ) noexcept
83 {
84 return Names::shadingMode[to_base( shadingMode )];
85 }
86
87 ShadingMode StringToShadingMode( const std::string_view name )
88 {
89 for ( U8 i = 0; i < to_U8( ShadingMode::COUNT ); ++i )
90 {
91 if ( name == Names::shadingMode[i] )
92 {
93 return static_cast<ShadingMode>(i);
94 }
95 }
96
97 return ShadingMode::COUNT;
98 }
99
100 const char* TextureOperationToString( const TextureOperation textureOp ) noexcept
101 {
102 return Names::textureOperation[to_base( textureOp )];
103 }
104
105 TextureOperation StringToTextureOperation( const std::string_view operation )
106 {
107 for ( U8 i = 0; i < to_U8( TextureOperation::COUNT ); ++i )
108 {
109 if ( operation == Names::textureOperation[i] )
110 {
111 return static_cast<TextureOperation>(i);
112 }
113 }
114
116 }
117 }; //namespace TypeUtil
118
119 bool Material::s_shadersDirty = false;
120
122 {
123 NOP();
124 }
125
127 {
128 s_shadersDirty = false;
129 }
130
132 {
133 s_shadersDirty = true;
134 }
135
136 void Material::Update( [[maybe_unused]] const U64 deltaTimeUS )
137 {
138 s_shadersDirty = false;
139 }
140
142 : CachedResource( descriptor, "Material")
143 {
144 const ShaderProgramInfo defaultShaderInfo = {};
145 // Could just direct copy the arrays, but this looks cool
146 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
147 {
148 auto& perPassInfo = _shaderInfo[s];
149 auto& perPassStates = _defaultRenderStates[s];
150
151 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
152 {
153 perPassInfo[p].fill( defaultShaderInfo );
154 perPassStates[p].fill( {{}, false});
155 }
156 }
157
158 _computeShaderCBK = []( Material* material, const RenderStagePass renderStagePass )
159 {
160
161 const bool isDepthPass = IsDepthPass( renderStagePass );
162 const bool isZPrePass = isDepthPass && renderStagePass._stage == RenderStage::DISPLAY;
163 const bool isShadowPass = renderStagePass._stage == RenderStage::SHADOW;
164
165 const Str<64> vertSource = isDepthPass ? material->baseShaderData()._depthShaderVertSource : material->baseShaderData()._colourShaderVertSource;
166 const Str<64> fragSource = isDepthPass ? material->baseShaderData()._depthShaderFragSource : material->baseShaderData()._colourShaderFragSource;
167
168 Str<32> vertVariant = isDepthPass ? isShadowPass ? material->baseShaderData()._shadowShaderVertVariant
169 : material->baseShaderData()._depthShaderVertVariant
170 : material->baseShaderData()._colourShaderVertVariant;
171
172 Str<32> fragVariant = isDepthPass ? material->baseShaderData()._depthShaderFragVariant
173 : material->baseShaderData()._colourShaderFragVariant;
174
175 ShaderProgramDescriptor shaderDescriptor{};
176 shaderDescriptor._name = Str<256>(vertSource.c_str()) + "_" + fragSource.c_str();
177
178 if ( isShadowPass )
179 {
180 vertVariant += "Shadow";
181 fragVariant += "Shadow.VSM";
182 }
183 else if ( isDepthPass )
184 {
185 vertVariant += "PrePass";
186 fragVariant += "PrePass";
187 }
188
189 ShaderModuleDescriptor vertModule = {};
190 vertModule._variant = vertVariant.c_str();
191 vertModule._sourceFile = (vertSource + ".glsl").c_str();
192 vertModule._moduleType = ShaderType::VERTEX;
193 shaderDescriptor._modules.push_back( vertModule );
194
195 if ( !isDepthPass || isZPrePass || isShadowPass || material->hasTransparency() )
196 {
197 ShaderModuleDescriptor fragModule = {};
198 fragModule._variant = fragVariant.c_str();
199 fragModule._sourceFile = (fragSource + ".glsl").c_str();
201
202 shaderDescriptor._modules.push_back( fragModule );
203 }
204
205 return shaderDescriptor;
206 };
207
208 _recomputeShadersCBK = []()
209 {
210 NOP();
211 };
212 }
213
215 {
216 _context = &context.gfx();
217 properties().receivesShadows( context.config().rendering.shadowMapping.enabled );
218
219 return CachedResource::load( context );
220 }
221
222 Handle<Material> Material::clone( const std::string_view nameSuffix )
223 {
224 DIVIDE_ASSERT( !nameSuffix.empty(), "Material error: clone called without a valid name suffix!" );
225
226 Handle<Material> cloneMatHandle = CreateResource( ResourceDescriptor<Material>( resourceName() + nameSuffix ) );
227 ResourcePtr<Material> cloneMat = Get(cloneMatHandle);
228
229 cloneMat->_baseMaterial = this;
230 cloneMat->_properties = this->_properties;
231 cloneMat->_extraShaderDefines = this->_extraShaderDefines;
232 cloneMat->_computeShaderCBK = this->_computeShaderCBK;
233 cloneMat->_computeRenderStateCBK = this->_computeRenderStateCBK;
234 cloneMat->_defaultRenderStates = this->_defaultRenderStates;
235 cloneMat->_topology = this->_topology;
236 cloneMat->_shaderAttributes = this->_shaderAttributes;
237 cloneMat->_shaderAttributesHash = this->_shaderAttributesHash;
238
239 cloneMat->ignoreXMLData( this->ignoreXMLData() );
240 cloneMat->updatePriorirty( this->updatePriorirty() );
241
242 for ( U8 i = 0u; i < to_U8( this->_textures.size() ); ++i )
243 {
244 const TextureInfo& texInfo = this->_textures[i];
245 if ( texInfo._ptr != INVALID_HANDLE<Texture> )
246 {
247 const Handle<Texture> cloneTex = GetResourceRef( texInfo._ptr );
248 cloneMat->setTexture(
249 static_cast<TextureSlot>(i),
250 cloneTex,
251 texInfo._sampler,
252 texInfo._operation,
253 texInfo._useInGeometryPasses );
254 }
255 }
256
257 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
258 {
259 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
260 {
261 const auto& variantMapSrc = _shaderInfo[s][p];
262 auto& variantMapDst = cloneMat->_shaderInfo[s][p];
263 for ( U8 v = 0u; v < to_U8( RenderStagePass::VariantType::COUNT ); ++v )
264 {
265 variantMapDst[v] = variantMapSrc[v].clone();
266 }
267 }
268 }
269
271 _instances.emplace_back( cloneMatHandle );
272
273 return cloneMatHandle;
274 }
275
276 U32 Material::update( [[maybe_unused]] const U64 deltaTimeUS )
277 {
279
280 if ( properties()._transparencyUpdated )
281 {
284 properties()._transparencyUpdated = false;
285 }
286 if ( properties()._cullUpdated )
287 {
289 properties()._cullUpdated = false;
290 }
291 if ( properties()._needsNewShader || s_shadersDirty )
292 {
294 properties()._needsNewShader = false;
296 }
297
298 return ret;
299 }
300
302 {
303 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
304 {
305 auto& perPassStates = _defaultRenderStates[s];
306 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
307 {
308 perPassStates[p].fill({{}, false});
309 }
310 }
311 }
312
314 {
315 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
316 {
317 auto& perPassStates = _defaultRenderStates[s];
318 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
319 {
320 for ( auto& state : perPassStates[p] )
321 {
322 if ( state._isSet )
323 {
324 state._block._cullMode = ( properties().doubleSided() ? CullMode::NONE : CullMode::BACK );
325 }
326 }
327 }
328 }
329 }
330
331 void Material::setPipelineLayout( const PrimitiveTopology topology, const AttributeMap& shaderAttributes )
332 {
333 if ( topology != _topology )
334 {
335 _topology = topology;
336 properties()._needsNewShader = true;
337 }
338
339 const size_t newHash = GetHash( shaderAttributes );
340 if ( _shaderAttributesHash == 0u || _shaderAttributesHash != newHash )
341 {
342 _shaderAttributes = shaderAttributes;
343 _shaderAttributesHash = newHash;
344 properties()._needsNewShader = true;
345 }
346 }
347
348 bool Material::setSampler( const TextureSlot textureUsageSlot, const SamplerDescriptor sampler )
349 {
350 _textures[to_U32( textureUsageSlot )]._sampler = sampler;
351
352 return true;
353 }
354
355 bool Material::setTextureLocked( const TextureSlot textureUsageSlot, const ResourceDescriptor<Texture>& texture, const SamplerDescriptor sampler, const TextureOperation op, const bool useInGeometryPasses )
356 {
357 return setTextureLocked(textureUsageSlot, CreateResource(texture), sampler, op, useInGeometryPasses);
358 }
359
360 bool Material::setTextureLocked( const TextureSlot textureUsageSlot, const Handle<Texture> texture, const SamplerDescriptor sampler, const TextureOperation op, const bool useInGeometryPasses )
361 {
362 // Invalidate our descriptor sets
363 _descriptorSetMainPass._bindingCount = {0u};
364 _descriptorSetSecondaryPass._bindingCount = { 0u };
365 _descriptorSetPrePass._bindingCount = { 0u };
366 _descriptorSetShadow._bindingCount = { 0u };
367
368 const U32 slot = to_U32( textureUsageSlot );
369
370 TextureInfo& texInfo = _textures[slot];
371
372 setSampler( textureUsageSlot, sampler );
373
374 setTextureOperation( textureUsageSlot, texture != INVALID_HANDLE<Texture> ? op : TextureOperation::NONE );
375
376 if ( texInfo._ptr != INVALID_HANDLE<Texture> )
377 {
378 // Skip adding same texture
379 if ( texture != INVALID_HANDLE<Texture> && texInfo._ptr == texture )
380 {
381 return true;
382 }
383 }
384
385 texInfo._srgb = texture != INVALID_HANDLE<Texture> ? Get(texture)->descriptor()._packing == GFXImagePacking::NORMALIZED_SRGB : false;
386 texInfo._useInGeometryPasses = texture != INVALID_HANDLE<Texture> ? useInGeometryPasses : false;
387 texInfo._ptr = texture;
388
389 if ( textureUsageSlot == TextureSlot::METALNESS )
390 {
391 properties()._usePackedOMR = (texture != INVALID_HANDLE<Texture> && Get(texture)->numChannels() > 2u);
392 }
393
394 if ( textureUsageSlot == TextureSlot::UNIT0 ||
395 textureUsageSlot == TextureSlot::OPACITY )
396 {
398 }
399
400 properties()._needsNewShader = true;
401
402 return true;
403 }
404
405 bool Material::setTexture( const TextureSlot textureUsageSlot,
406 const ResourceDescriptor<Texture>& texture,
407 const SamplerDescriptor sampler,
408 const TextureOperation op,
409 bool useInGeometryPasses)
410 {
412 return setTextureLocked(textureUsageSlot, texture, sampler, op, useInGeometryPasses);
413 }
414
415 bool Material::setTexture( const TextureSlot textureUsageSlot,
416 const Handle<Texture> texture,
417 const SamplerDescriptor sampler,
418 const TextureOperation op,
419 const bool useInGeometryPasses )
420 {
422 return setTextureLocked( textureUsageSlot, texture, sampler, op, useInGeometryPasses );
423 }
424
425 void Material::setTextureOperation( const TextureSlot textureUsageSlot, const TextureOperation op )
426 {
427
428 TextureOperation& crtOp = _textures[to_base( textureUsageSlot )]._operation;
429
430 if ( crtOp != op )
431 {
432 crtOp = op;
433 properties()._needsNewShader = true;
434 }
435 }
436
438 {
440
441 computeAndAppendShaderDefines( shaderDescriptor, stagePass );
442
443 ShaderProgramInfo& info = shaderInfo( stagePass );
444 // if we already have a different shader assigned ...
445 if ( info._shaderRef != INVALID_HANDLE<ShaderProgram> )
446 {
448
449 // We cannot replace a shader that is still loading in the background
450 WAIT_FOR_CONDITION( ptr->getState() == ResourceState::RES_LOADED );
451 if ( ptr->descriptor() != shaderDescriptor )
452 {
453 Console::printfn( LOCALE_STR( "REPLACE_SHADER" ),
454 ptr->resourceName().c_str(),
455 shaderDescriptor._name.c_str(),
458 to_base(stagePass._variant) );
459 }
460 else
461 {
462 return;
463 }
464 }
465
466 ShaderComputeQueue::ShaderQueueElement shaderElement{ &info._shaderRef, shaderDescriptor };
467 if ( updatePriorirty() == UpdatePriority::High )
468 {
469 _context->shaderComputeQueue().process( shaderElement );
471 DIVIDE_ASSERT( info._shaderRef != INVALID_HANDLE<ShaderProgram> );
472 WaitForReady( Get(info._shaderRef) );
473 }
474 else
475 {
476 if ( updatePriorirty() == UpdatePriority::Medium )
477 {
478 _context->shaderComputeQueue().addToQueueFront( shaderElement );
479 }
480 else
481 {
482 _context->shaderComputeQueue().addToQueueBack( shaderElement );
483 }
485 }
486 }
487
489 {
491
492 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
493 {
494 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
495 {
496 RenderStagePass stagePass{ static_cast<RenderStage>(s), static_cast<RenderPassType>(p) };
497 auto& variantMap = _shaderInfo[s][p];
498
499 for ( U8 v = 0u; v < to_U8( RenderStagePass::VariantType::COUNT ); ++v )
500 {
501 ShaderProgramInfo& shaderInfo = variantMap[v];
503 {
504 continue;
505 }
506
507 stagePass._variant = static_cast<RenderStagePass::VariantType>(v);
509 }
510 }
511 }
512
513 _recomputeShadersCBK();
514 }
515
517 {
518 constexpr U8 maxRetries = 250;
519
520 bool justFinishedLoading = false;
521 for ( U8 i = 0; i < maxRetries; ++i )
522 {
523 if ( !canDraw( renderStagePass, justFinishedLoading ) )
524 {
526 {
527 NOP();
528 }
529 }
530 else
531 {
532 return getProgramHandle( renderStagePass );
533 }
534 }
535
536 return _context->imShaders()->imWorldShaderNoTexture();
537 }
538
540 {
541
542 const ShaderProgramInfo& info = shaderInfo( renderStagePass );
543
544 if ( info._shaderRef != INVALID_HANDLE<ShaderProgram> )
545 {
547 return info._shaderRef;
548 }
550
551 return _context->imShaders()->imWorldShaderNoTexture();
552 }
553
554 bool Material::canDraw( const RenderStagePass renderStagePass, bool& shaderJustFinishedLoading )
555 {
557
558 shaderJustFinishedLoading = false;
559 ShaderProgramInfo& info = shaderInfo( renderStagePass );
561 {
562 setShaderProgramInternal( _computeShaderCBK( this, renderStagePass ),
563 renderStagePass );
564 }
565
566 // If we have a shader queued (with a valid ref) ...
568 {
569 // ... we are now passed the "compute" stage. We just need to wait for it to load
570 if ( info._shaderRef == INVALID_HANDLE<ShaderProgram> )
571 {
572 // Shader is still in the queue
573 return false;
574 }
576 }
577
578 // If the shader is computed ...
580 {
581 assert( info._shaderRef != INVALID_HANDLE<ShaderProgram> );
582 // ... wait for the shader to finish loading
583 WaitForReady( Get(info._shaderRef) );
584 // Once it has finished loading, it is ready for drawing
585 shaderJustFinishedLoading = true;
587 info._shaderKeyCache = Get(info._shaderRef)->getGUID();
588 }
589
590 // If the shader isn't ready it may have not passed through the computational stage yet (e.g. the first time this method is called)
592 {
593 // This is usually the first step in generating a shader: No shader available but we need to render in this stagePass
595 {
596 // So request a new shader
598 }
599
600 return false;
601 }
602
603 // Shader should be in the ready state
604 return true;
605 }
606
607 void Material::computeAndAppendShaderDefines( ShaderProgramDescriptor& shaderDescriptor, const RenderStagePass renderStagePass ) const
608 {
610
611 const bool isDepthPass = IsDepthPass( renderStagePass );
612 const bool isPrePass = renderStagePass._passType == RenderPassType::PRE_PASS;
613 const bool isShadowPass = renderStagePass._stage == RenderStage::SHADOW;
614 const bool isWorldAOPass = isShadowPass && renderStagePass._index == ShadowMap::WORLD_AO_LAYER_INDEX;
615
616 DIVIDE_ASSERT( properties().shadingMode() != ShadingMode::COUNT, "Material computeShader error: Invalid shading mode specified!" );
617 std::array<ModuleDefines, to_base( ShaderType::COUNT )> moduleDefines = {};
618
620 {
621 shaderDescriptor._globalDefines.emplace_back( "MSAA_SCREEN_TARGET", true );
622 }
623
624 if ( isShadowPass )
625 {
626 shaderDescriptor._globalDefines.emplace_back( "SHADOW_PASS", true );
627 shaderDescriptor._globalDefines.emplace_back( "SKIP_REFLECT_REFRACT", true );
628
629 if ( isWorldAOPass || to_base( renderStagePass._variant) == to_base( ShadowType::CSM ) )
630 {
631 shaderDescriptor._globalDefines.emplace_back( "ORTHO_PROJECTION", true );
632 if ( isWorldAOPass )
633 {
634 shaderDescriptor._globalDefines.emplace_back( "WORLD_AO_PASS", true );
635 }
636 }
637 }
638 else if ( isDepthPass )
639 {
640 shaderDescriptor._globalDefines.emplace_back( "PRE_PASS", true );
641 shaderDescriptor._globalDefines.emplace_back( "SKIP_REFLECT_REFRACT", true );
642 }
643 if ( renderStagePass._stage == RenderStage::REFLECTION && to_base( renderStagePass._variant ) != to_base( ReflectorType::CUBE ) )
644 {
645 shaderDescriptor._globalDefines.emplace_back( "REFLECTION_PASS", true );
646 }
647 if ( renderStagePass._stage == RenderStage::DISPLAY )
648 {
649 shaderDescriptor._globalDefines.emplace_back( "MAIN_DISPLAY_PASS", true );
650 if ( !properties().isStatic() )
651 {
652 shaderDescriptor._globalDefines.emplace_back( "HAS_VELOCITY", true );
653 }
654 }
655 if ( renderStagePass._passType == RenderPassType::OIT_PASS )
656 {
657 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "OIT_PASS", true );
658 }
659 else if ( renderStagePass._passType == RenderPassType::TRANSPARENCY_PASS )
660 {
661 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "TRANSPARENCY_PASS", true );
662 }
663 switch ( properties().shadingMode() )
664 {
666 {
667 shaderDescriptor._globalDefines.emplace_back( "SHADING_MODE_FLAT", true );
668 } break;
670 {
671 shaderDescriptor._globalDefines.emplace_back( "SHADING_MODE_TOON", true );
672 } break;
674 {
675 shaderDescriptor._globalDefines.emplace_back( "SHADING_MODE_BLINN_PHONG", true );
676 } break;
678 {
679 shaderDescriptor._globalDefines.emplace_back( "SHADING_MODE_PBR_MR", true );
680 } break;
682 {
683 shaderDescriptor._globalDefines.emplace_back( "SHADING_MODE_PBR_SG", true );
684 } break;
685 default: DIVIDE_UNEXPECTED_CALL(); break;
686 }
687 // Display pre-pass caches normal maps in a GBuffer, so it's the only exception
688 if ( (!isDepthPass || renderStagePass._stage == RenderStage::DISPLAY) &&
689 _textures[to_base( TextureSlot::NORMALMAP )]._ptr != INVALID_HANDLE<Texture> &&
690 properties().bumpMethod() != BumpMethod::NONE )
691 {
692 // Bump mapping?
693 shaderDescriptor._globalDefines.emplace_back( "COMPUTE_TBN", true );
694 }
695
696 switch ( _topology )
697 {
698 case PrimitiveTopology::POINTS: shaderDescriptor._globalDefines.emplace_back( "GEOMETRY_POINTS", true ); break;
699 case PrimitiveTopology::LINES: break;
702 case PrimitiveTopology::LINE_STRIP_ADJACENCY: shaderDescriptor._globalDefines.emplace_back( "GEOMETRY_LINES", true ); break;
707 case PrimitiveTopology::TRIANGLE_STRIP_ADJACENCY: shaderDescriptor._globalDefines.emplace_back( "GEOMETRY_TRIANGLES", true ); break;
708 case PrimitiveTopology::PATCH: shaderDescriptor._globalDefines.emplace_back( "GEOMETRY_PATCH", true ); break;
709 default: DIVIDE_UNEXPECTED_CALL();
710 }
711
712 for ( U8 i = 0u; i < to_U8( AttribLocation::COUNT ); ++i )
713 {
714 const AttributeDescriptor& descriptor = _shaderAttributes._attributes[i];
715 if ( descriptor._dataType != GFXDataFormat::COUNT )
716 {
717 shaderDescriptor._globalDefines.emplace_back( Util::StringFormat( "HAS_{}_ATTRIBUTE", Names::attribLocation[i] ).c_str(), true );
718 }
719 }
720
721 if ( hasTransparency() )
722 {
723 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "HAS_TRANSPARENCY", true );
724
725 if ( properties().overrides().useAlphaDiscard() &&
726 renderStagePass._passType != RenderPassType::OIT_PASS )
727 {
728 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "USE_ALPHA_DISCARD", true );
729 }
730 }
731
732 const Configuration& config = _context->context().config();
733 if ( !config.rendering.shadowMapping.enabled )
734 {
735 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "DISABLE_SHADOW_MAPPING", true );
736 }
737 else
738 {
739 if ( !config.rendering.shadowMapping.csm.enabled )
740 {
741 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "DISABLE_SHADOW_MAPPING_CSM", true );
742 }
743 if ( !config.rendering.shadowMapping.spot.enabled )
744 {
745 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "DISABLE_SHADOW_MAPPING_SPOT", true );
746 }
747 if ( !config.rendering.shadowMapping.point.enabled )
748 {
749 moduleDefines[to_base( ShaderType::FRAGMENT )].emplace_back( "DISABLE_SHADOW_MAPPING_POINT", true );
750 }
751 }
752
753 shaderDescriptor._globalDefines.emplace_back( properties().isStatic() ? "NODE_STATIC" : "NODE_DYNAMIC", true );
754
755 if ( properties().isInstanced() )
756 {
757 shaderDescriptor._globalDefines.emplace_back( "OVERRIDE_DATA_IDX", true );
758 }
759
760 if ( properties().hardwareSkinning() )
761 {
762 moduleDefines[to_base( ShaderType::VERTEX )].emplace_back( "USE_GPU_SKINNING", true );
763 }
764 if ( !properties().texturesInFragmentStageOnly() )
765 {
766 shaderDescriptor._globalDefines.emplace_back( "NEED_TEXTURE_DATA_ALL_STAGES", true );
767 }
768
769 for ( U8 i = 0u; i < to_U8( TextureSlot::COUNT ); ++i )
770 {
771 if ( usesTextureInShader( static_cast<TextureSlot>(i), isPrePass, isShadowPass ) )
772 {
773 shaderDescriptor._globalDefines.emplace_back( Util::StringFormat( "USE_{}_TEXTURE", Names::textureSlot[i] ), true );
774 }
775 }
776
777 for ( ShaderModuleDescriptor& module : shaderDescriptor._modules )
778 {
779 module._defines.insert( eastl::end( module._defines ), eastl::begin( moduleDefines[to_base( module._moduleType )] ), eastl::end( moduleDefines[to_base( module._moduleType )] ) );
780 module._defines.insert( eastl::end( module._defines ), eastl::begin( _extraShaderDefines[to_base( module._moduleType )] ), eastl::end( _extraShaderDefines[to_base( module._moduleType )] ) );
781 module._defines.emplace_back( "DEFINE_PLACEHOLDER", false );
782 }
783 }
784
785 bool Material::usesTextureInShader( const TextureSlot slot, const bool isPrePass, const bool isShadowPass ) const noexcept
786 {
787 if ( _textures[to_base( slot )]._ptr == INVALID_HANDLE<Texture> )
788 {
789 return false;
790 }
791
792 if ( !isPrePass && !isShadowPass )
793 {
794 return true;
795 }
796
797 bool add = _textures[to_base( slot )]._useInGeometryPasses;
798 if ( !add )
799 {
800 if ( hasTransparency() )
801 {
802 if ( slot == TextureSlot::UNIT0 )
803 {
804 add = properties().translucencySource() == TranslucencySource::ALBEDO_TEX;
805 }
806 else if ( slot == TextureSlot::OPACITY )
807 {
808 add = properties().translucencySource() == TranslucencySource::OPACITY_MAP_A ||
809 properties().translucencySource() == TranslucencySource::OPACITY_MAP_R;
810 }
811 }
812
813 if ( isPrePass )
814 {
815 // Some best-fit heuristics that will surely break at one point
816 switch ( slot )
817 {
820 {
821 add = true;
822 } break;
824 {
825 add = properties().shadingMode() != ShadingMode::PBR_MR && properties().shadingMode() != ShadingMode::PBR_SG;
826 } break;
828 {
829 add = properties().usePackedOMR();
830 } break;
832 {
833 add = !properties().usePackedOMR();
834 } break;
835 default: break;
836 };
837 }
838 }
839
840 return add;
841 }
842
844 {
845 for ( TextureInfo& tex : _textures )
846 {
847 DestroyResource(tex._ptr);
848 }
849
850 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
851 {
852 auto& passMapShaders = _shaderInfo[s];
853 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
854 {
855 auto& shaders = passMapShaders[p];
856 for ( ShaderProgramInfo& info : shaders )
857 {
858 DestroyResource(info._shaderRef);
859 info = {};
860 }
861 }
862 }
863
864 if ( _baseMaterial != nullptr )
865 {
867 erase_if( _baseMaterial->_instances,
868 [guid = getGUID()]( Handle<Material> instance ) noexcept
869 {
870 return Get(instance)->getGUID() == guid;
871 } );
872 }
873
875 for ( Handle<Material> instance : _instances )
876 {
877 Get(instance)->_baseMaterial = nullptr;
878 }
879
880 return true;
881 }
882
883 void Material::setRenderStateBlock( const RenderStateBlock& renderStateBlock, const RenderStage stage, const RenderPassType pass, const RenderStagePass::VariantType variant )
884 {
885 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
886 {
887 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
888 {
889 const RenderStage crtStage = static_cast<RenderStage>(s);
890 const RenderPassType crtPass = static_cast<RenderPassType>(p);
891 if ( (stage == RenderStage::COUNT || stage == crtStage) && (pass == RenderPassType::COUNT || pass == crtPass) )
892 {
894 {
895 _defaultRenderStates[s][p].fill( { renderStateBlock, true} );
896 }
897 else
898 {
899 _defaultRenderStates[s][p][to_base( variant )] = { renderStateBlock, true};
900 }
901 }
902 }
903 }
904 }
905
907 {
908 const TranslucencySource oldSource = properties()._translucencySource;
909 properties()._translucencySource = TranslucencySource::COUNT;
910 if ( properties().overrides().transparencyEnabled() )
911 {
912 // In order of importance (less to more)!
913 // diffuse channel alpha
914 if ( properties().baseColour().a < 0.95f && _textures[to_base( TextureSlot::UNIT0 )]._operation != TextureOperation::REPLACE )
915 {
916 properties()._translucencySource = TranslucencySource::ALBEDO_COLOUR;
917 }
918
919 // base texture is translucent
920 const Handle<Texture> albedo = _textures[to_base( TextureSlot::UNIT0 )]._ptr;
921 if ( albedo != INVALID_HANDLE<Texture> && Get(albedo)->hasTransparency() && !properties().overrides().ignoreTexDiffuseAlpha() )
922 {
923 properties()._translucencySource = TranslucencySource::ALBEDO_TEX;
924 }
925
926 // opacity map
927 const Handle<Texture> opacity = _textures[to_base( TextureSlot::OPACITY )]._ptr;
928 if ( opacity != INVALID_HANDLE<Texture>)
929 {
930 const U8 channelCount = NumChannels( Get(opacity)->descriptor()._baseFormat );
931 properties()._translucencySource = (channelCount == 4 && Get(opacity)->hasTransparency())
934 }
935 }
936
937 properties()._needsNewShader = oldSource != properties().translucencySource();
938 }
939
941 {
942 auto&[ret, isSet] = _defaultRenderStates[to_base( renderStagePass._stage )][to_base( renderStagePass._passType )][to_base( renderStagePass._variant )];
943 // If we haven't defined a state for this variant, use the default one
944 if ( !isSet )
945 {
946 // Defaults
947 ret._cullMode = properties().doubleSided() ? CullMode::NONE : CullMode::BACK;
948 ret._colourWrite.b[0] = ret._colourWrite.b[1] = ret._colourWrite.b[2] = ret._colourWrite.b[3] = true;
949
950 if ( IsDepthPass( renderStagePass ) ) // DEPTH, Z-PREPASS, SHADOW
951 {
952 ret._zFunc = ComparisonFunction::LEQUAL;
953 ret._depthWriteEnabled = true;
954
955 if ( IsShadowPass( renderStagePass ) ) //SHADOW
956 {
957 ret._colourWrite.b[2] = ret._colourWrite.b[3] = false;
958 ret._cullMode = CullMode::BACK;
959 //ret.setZBias(1.1f, 4.f);
960 }
961 else if ( IsZPrePass( renderStagePass ) ) //Z-PRE_PASS
962 {
963 NOP();
964 }
965 else //DEPTH
966 {
967 ret._colourWrite.b[0] = ret._colourWrite.b[1] = ret._colourWrite.b[2] = ret._colourWrite.b[3] = false;
968 }
969 }
970 else if( renderStagePass._passType == RenderPassType::MAIN_PASS )
971 {
972 ret._zFunc = ComparisonFunction::EQUAL;
973 ret._depthWriteEnabled = false;
974 }
975 else //OIT/TRANSPARENT
976 {
978 renderStagePass._passType == RenderPassType::TRANSPARENCY_PASS );
979 // We use alpha-discard in the prepass for transparent/translucent objects, so depth values for translucent pixels will be invalid
980 ret._zFunc = ComparisonFunction::LEQUAL;
981 ret._depthWriteEnabled = false;
982 ret._cullMode = CullMode::NONE;
983 }
984 if ( _computeRenderStateCBK )
985 {
986 _computeRenderStateCBK( this, renderStagePass, ret );
987 }
988
989 isSet = true;
990 }
991
992 return ret;
993 }
994
995 void Material::getSortKeys( const RenderStagePass renderStagePass, I64& shaderKey, I64& textureKey, bool& transparencyFlag ) const
996 {
997 shaderKey = shaderInfo( renderStagePass )._shaderKeyCache;
999 if ( _textures[to_base( TextureSlot::UNIT0 )]._ptr != INVALID_HANDLE<Texture> )
1000 {
1001 textureKey = Get(_textures[to_base( TextureSlot::UNIT0 )]._ptr)->getGUID();
1002 }
1003 else
1004 {
1005 textureKey = I64_LOWEST;
1006 }
1007 transparencyFlag = hasTransparency();
1008 }
1009
1010 FColour4 Material::getBaseColour( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1011 {
1012 textureOut = INVALID_HANDLE<Texture>;
1013 hasTextureOverride = _textures[to_base( TextureSlot::UNIT0 )]._ptr != INVALID_HANDLE<Texture>;
1014 if ( hasTextureOverride )
1015 {
1016 textureOut = _textures[to_base( TextureSlot::UNIT0 )]._ptr;
1017 }
1018
1019 return properties().baseColour();
1020 }
1021
1022 FColour3 Material::getEmissive( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1023 {
1024 textureOut = INVALID_HANDLE<Texture>;
1025 hasTextureOverride = _textures[to_base( TextureSlot::EMISSIVE )]._ptr != INVALID_HANDLE<Texture>;
1026 if ( hasTextureOverride )
1027 {
1028 textureOut = _textures[to_base( TextureSlot::EMISSIVE )]._ptr;
1029 }
1030
1031 return properties().emissive();
1032 }
1033
1034 FColour3 Material::getAmbient( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1035 {
1036 textureOut = INVALID_HANDLE<Texture>;
1037 hasTextureOverride = false;
1038
1039 return properties().ambient();
1040 }
1041
1042 FColour3 Material::getSpecular( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1043 {
1044 textureOut = INVALID_HANDLE<Texture>;
1045 hasTextureOverride = _textures[to_base( TextureSlot::SPECULAR )]._ptr != INVALID_HANDLE<Texture>;
1046 if ( hasTextureOverride )
1047 {
1048 textureOut = _textures[to_base( TextureSlot::SPECULAR )]._ptr;
1049 }
1050 return properties().specular();
1051 }
1052
1053 F32 Material::getMetallic( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1054 {
1055 textureOut = INVALID_HANDLE<Texture>;
1056 hasTextureOverride = _textures[to_base( TextureSlot::METALNESS )]._ptr != INVALID_HANDLE<Texture>;
1057 if ( hasTextureOverride )
1058 {
1059 textureOut = _textures[to_base( TextureSlot::METALNESS )]._ptr;
1060 }
1061 return properties().metallic();
1062 }
1063
1064 F32 Material::getRoughness( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1065 {
1066 textureOut = INVALID_HANDLE<Texture>;
1067 hasTextureOverride = _textures[to_base( TextureSlot::ROUGHNESS )]._ptr != INVALID_HANDLE<Texture>;
1068 if ( hasTextureOverride )
1069 {
1070 textureOut = _textures[to_base( TextureSlot::ROUGHNESS )]._ptr;
1071 }
1072 return properties().roughness();
1073 }
1074
1075 F32 Material::getOcclusion( bool& hasTextureOverride, Handle<Texture>& textureOut ) const noexcept
1076 {
1077 textureOut = INVALID_HANDLE<Texture>;
1078 hasTextureOverride = _textures[to_base( TextureSlot::OCCLUSION )]._ptr != INVALID_HANDLE<Texture>;
1079 if ( hasTextureOverride )
1080 {
1081 textureOut = _textures[to_base( TextureSlot::OCCLUSION )]._ptr;
1082 }
1083 return properties().occlusion();
1084 }
1085
1086 void Material::getData( const U32 bestProbeID, NodeMaterialData& dataOut )
1087 {
1088 const FColour3& specColour = properties().specular();
1089 const F32 shininess = CLAMPED( properties().shininess(), 0.f, MAX_SHININESS );
1090
1091 const bool useOpacityAlphaChannel = properties().translucencySource() == TranslucencySource::OPACITY_MAP_A;
1092 const bool useAlbedoTexAlphachannel = properties().translucencySource() == TranslucencySource::ALBEDO_TEX;
1093
1094 //ToDo: Maybe store all of these material properties in an internal, cached, NodeMaterialData structure? -Ionut
1095 dataOut._albedo.set( properties().baseColour() );
1096 dataOut._colourData.set( properties().ambient(), shininess );
1097 dataOut._emissiveAndParallax.set( properties().emissive(), properties().parallaxFactor() );
1098 dataOut._data.x = Util::PACK_UNORM4x8( CLAMPED_01( properties().occlusion() ),
1099 CLAMPED_01( properties().metallic() ),
1100 CLAMPED_01( properties().roughness() ),
1101 (properties().doubleSided() ? 1.f : 0.f) );
1102 dataOut._data.y = Util::PACK_UNORM4x8( specColour.r,
1103 specColour.g,
1104 specColour.b,
1105 properties().usePackedOMR() ? 1.f : 0.f );
1106 dataOut._data.z = Util::PACK_UNORM4x8( to_U8( properties().bumpMethod() ),
1107 to_U8( properties().shadingMode() ),
1108 0u,
1109 0u );
1110 dataOut._data.w = bestProbeID;
1111
1113 to_U8( _textures[to_base( TextureSlot::UNIT1 )]._operation ),
1114 to_U8( _textures[to_base( TextureSlot::SPECULAR )]._operation ),
1115 to_U8( _textures[to_base( TextureSlot::EMISSIVE )]._operation ) );
1119 to_U8( _textures[to_base( TextureSlot::OPACITY )]._operation ) );
1120 dataOut._textureOperations.z = Util::PACK_UNORM4x8( useAlbedoTexAlphachannel ? 1.f : 0.f,
1121 useOpacityAlphaChannel ? 1.f : 0.f,
1122 properties().specGloss().x,
1123 properties().specGloss().y );
1124 dataOut._textureOperations.w = Util::PACK_UNORM4x8( properties().receivesShadows() ? 1.f : 0.f, 0.f, 0.f, 0.f );
1125 }
1126
1128 {
1130
1131 ShaderStageVisibility texVisibility = properties().texturesInFragmentStageOnly() ? ShaderStageVisibility::FRAGMENT : ShaderStageVisibility::ALL_DRAW;
1132 const bool isPrePass = renderStagePass._passType == RenderPassType::PRE_PASS;
1133 const bool isShadowPass = renderStagePass._stage == RenderStage::SHADOW;
1134 auto& descriptor = isShadowPass
1135 ? _descriptorSetShadow
1136 : isPrePass
1137 ? _descriptorSetPrePass
1138 : renderStagePass._stage == RenderStage::DISPLAY
1139 ? _descriptorSetMainPass
1140 : _descriptorSetSecondaryPass;
1141
1142 if ( descriptor._bindingCount == 0u )
1143 {
1145 // Check again
1146 if ( descriptor._bindingCount == 0u )
1147 {
1148 for ( U8 i = 0u; i < to_U8( TextureSlot::COUNT ); ++i )
1149 {
1150 if ( usesTextureInShader( static_cast<TextureSlot>(i), isPrePass, isShadowPass ) )
1151 {
1152 DescriptorSetBinding& binding = AddBinding( descriptor, i, texVisibility );
1153 Set( binding._data, _textures[i]._ptr, _textures[i]._sampler );
1154 }
1155 }
1156 }
1157 }
1158
1159 return descriptor;
1160 }
1161
1163 {
1165
1166 // Alternatively we could just copy the maps directly
1167 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
1168 {
1169 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
1170 {
1171 auto& shaders = _shaderInfo[s][p];
1172 for ( const ShaderProgramInfo& info : shaders )
1173 {
1174 if ( info._shaderRef != INVALID_HANDLE<ShaderProgram> && Get(info._shaderRef)->getState() == ResourceState::RES_LOADED )
1175 {
1176 Get(info._shaderRef)->recompile();
1177 }
1178 }
1179 }
1180 }
1181 }
1182
1183 void Material::saveToXML( const std::string& entryName, boost::property_tree::ptree& pt ) const
1184 {
1185 pt.put( entryName + ".version", g_materialXMLVersion );
1186
1187 properties().saveToXML( entryName, pt );
1188 saveRenderStatesToXML( entryName, pt );
1189 saveTextureDataToXML( entryName, pt );
1190 }
1191
1192 void Material::loadFromXML( const std::string& entryName, const boost::property_tree::ptree& pt )
1193 {
1194 if ( ignoreXMLData() )
1195 {
1196 return;
1197 }
1198
1199 const size_t detectedVersion = pt.get<size_t>( entryName + ".version", 0 );
1200 if ( detectedVersion != g_materialXMLVersion )
1201 {
1202 Console::printfn( LOCALE_STR( "MATERIAL_WRONG_VERSION" ), assetName(), detectedVersion, g_materialXMLVersion );
1203 return;
1204 }
1205
1206 properties().loadFromXML( entryName, pt );
1207 loadRenderStatesFromXML( entryName, pt );
1208 loadTextureDataFromXML( entryName, pt );
1209 }
1210
1211 void Material::saveRenderStatesToXML( const std::string& entryName, boost::property_tree::ptree& pt ) const
1212 {
1213 hashMap<size_t, U32> previousHashValues;
1214
1215 U32 blockIndex = 0u;
1216
1217 const std::string stateNode = Util::StringFormat( "{}.RenderStates", entryName).c_str();
1218 const std::string blockNode = Util::StringFormat( "{}.RenderStateIndex.PerStagePass", entryName).c_str();
1219
1220 for ( U8 s = 0u; s < to_U8( RenderStage::COUNT ); ++s )
1221 {
1222 for ( U8 p = 0u; p < to_U8( RenderPassType::COUNT ); ++p )
1223 {
1224 for ( U8 v = 0u; v < to_U8( RenderStagePass::VariantType::COUNT ); ++v )
1225 {
1226 auto& entry = _defaultRenderStates[s][p][v];
1227 if ( !entry._isSet )
1228 {
1229 continue;
1230 }
1231
1232 auto& block = entry._block;
1233 const size_t stateHash = GetHash(block);
1234 if ( previousHashValues.find( stateHash ) == std::cend( previousHashValues ) )
1235 {
1236 SaveToXML(
1237 block,
1238 Util::StringFormat( "{}.{}", stateNode, blockIndex ).c_str(),
1239 pt );
1240 previousHashValues[stateHash] = blockIndex++;
1241 }
1242
1243 boost::property_tree::ptree stateTree;
1244 stateTree.put( "StagePass.<xmlattr>.index", previousHashValues[stateHash] );
1245 stateTree.put( "StagePass.<xmlattr>.stage", s );
1246 stateTree.put( "StagePass.<xmlattr>.pass", p );
1247 stateTree.put( "StagePass.<xmlattr>.variant", v );
1248
1249 pt.add_child( blockNode, stateTree.get_child( "StagePass" ) );
1250 }
1251 }
1252 }
1253 }
1254
1255 void Material::loadRenderStatesFromXML( const std::string& entryName, const boost::property_tree::ptree& pt )
1256 {
1257 hashMap<U32, RenderStateBlock> previousBlocks;
1258
1259 static boost::property_tree::ptree g_emptyPtree;
1260 const std::string stateNode = Util::StringFormat( "{}.RenderStates", entryName).c_str();
1261 const std::string blockNode = Util::StringFormat( "{}.RenderStateIndex", entryName).c_str();
1262 for ( const auto& [tag, data] : pt.get_child( blockNode, g_emptyPtree ) )
1263 {
1264 assert( tag == "PerStagePass" );
1265
1266 const U32 b = data.get<U32>( "<xmlattr>.index", U32_MAX ); assert( b != U32_MAX );
1267 const U8 s = data.get<U8>( "<xmlattr>.stage", to_U8( RenderStage::COUNT ) ); assert( s != to_U8( RenderStage::COUNT ) );
1268 const U8 p = data.get<U8>( "<xmlattr>.pass", to_U8( RenderPassType::COUNT ) ); assert( p != to_U8( RenderPassType::COUNT ) );
1269 const U8 v = data.get<U8>( "<xmlattr>.variant", to_U8( RenderStagePass::VariantType::COUNT ) ); assert( v != to_U8( RenderStagePass::VariantType::COUNT ) );
1270
1271 const auto& it = previousBlocks.find( b );
1272 if ( it != cend( previousBlocks ) )
1273 {
1274 _defaultRenderStates[s][p][v] = { it->second, true };
1275 }
1276 else
1277 {
1278 RenderStateBlock block{};
1279 LoadFromXML( Util::StringFormat( "{}.{}", stateNode, b ).c_str(), pt, block );
1280
1281 _defaultRenderStates[s][p][v] = { block, true };
1282 previousBlocks[b] = block;
1283 }
1284 }
1285 }
1286
1287 void Material::saveTextureDataToXML( const std::string& entryName, boost::property_tree::ptree& pt ) const
1288 {
1289 hashMap<size_t, U32> previousHashValues;
1290
1291 U32 samplerCount = 0u;
1292 for ( U8 i = 0u; i < to_U8( TextureSlot::COUNT ); ++i )
1293 {
1294 const TextureSlot usage = static_cast<TextureSlot>(i);
1295
1296 Handle<Texture> tex = getTexture( usage );
1297 if ( tex != INVALID_HANDLE<Texture> )
1298 {
1299 const Texture* texture = Get(tex);
1300
1301
1302 const std::string textureNode = entryName + ".texture." + TypeUtil::TextureSlotToString( usage );
1303
1304 pt.put( textureNode + ".name", texture->assetName().c_str() );
1305 pt.put( textureNode + ".path", texture->assetLocation().string() );
1306 pt.put( textureNode + ".usage", TypeUtil::TextureOperationToString( _textures[to_base( usage )]._operation ) );
1307 pt.put( textureNode + ".srgb", _textures[to_base( usage )]._srgb );
1308
1309 const SamplerDescriptor sampler = _textures[to_base( usage )]._sampler;
1310 const size_t samplerHash = GetHash(sampler);
1311
1312 if ( previousHashValues.find( samplerHash ) == std::cend( previousHashValues ) )
1313 {
1314 samplerCount++;
1315 XMLParser::saveToXML( sampler, Util::StringFormat( "{}.SamplerDescriptors.{}", entryName, samplerCount ).c_str(), pt );
1316 previousHashValues[samplerHash] = samplerCount;
1317 }
1318 pt.put( textureNode + ".Sampler.id", previousHashValues[samplerHash] );
1319 pt.put( textureNode + ".UseForGeometry", _textures[to_base( usage )]._useInGeometryPasses );
1320 }
1321 }
1322 }
1323
1324 void Material::loadTextureDataFromXML( const std::string& entryName, const boost::property_tree::ptree& pt )
1325 {
1326 hashMap<U32, SamplerDescriptor> previousSamplers;
1327
1328 for ( U8 i = 0u; i < to_U8( TextureSlot::COUNT ); ++i )
1329 {
1330 const TextureSlot usage = static_cast<TextureSlot>(i);
1331
1332 if ( pt.get_child_optional( entryName + ".texture." + TypeUtil::TextureSlotToString( usage ) + ".name" ) )
1333 {
1334 const std::string textureNode = entryName + ".texture." + TypeUtil::TextureSlotToString( usage );
1335
1336 const std::string texName = pt.get<std::string>( textureNode + ".name", "" );
1337 const ResourcePath texPath = ResourcePath( pt.get<std::string>( textureNode + ".path", "" ).c_str() );
1338 // May be a procedural texture
1339 if ( texPath.empty() )
1340 {
1341 continue;
1342 }
1343
1344 if ( !texName.empty() )
1345 {
1347
1348 const bool useInGeometryPasses = pt.get<bool>( textureNode + ".UseForGeometry", _textures[to_base( usage )]._useInGeometryPasses );
1349 const U32 index = pt.get<U32>( textureNode + ".Sampler.id", 0 );
1350 const auto& it = previousSamplers.find( index );
1351
1352 SamplerDescriptor sampler;
1353 if ( it != cend( previousSamplers ) )
1354 {
1355 sampler = it->second;
1356 }
1357 else
1358 {
1359 sampler = XMLParser::loadFromXML( Util::StringFormat( "{}.SamplerDescriptors.{}", entryName, index ).c_str(), pt );
1360 previousSamplers[index] = sampler;
1361 }
1362
1363 setSampler( usage, sampler );
1364
1365 TextureOperation& op = _textures[to_base( usage )]._operation;
1366 bool& srgb = _textures[to_base(usage)]._srgb;
1367
1368 op = TypeUtil::StringToTextureOperation( pt.get<std::string>( textureNode + ".usage", TypeUtil::TextureOperationToString( op ) ) );
1369 srgb = pt.get<bool>( textureNode + ".srgb", srgb);
1370
1371 Handle<Texture> crtTex = _textures[to_base( usage )]._ptr;
1372 if ( crtTex == INVALID_HANDLE<Texture> )
1373 {
1375 }
1376 else if ( (Get(crtTex)->assetLocation() / Get(crtTex)->assetName()) == (texPath / texName) )
1377 {
1378 continue;
1379 }
1380
1381 _textures[to_base( usage )]._useInGeometryPasses = useInGeometryPasses;
1382
1383 ResourceDescriptor<Texture> texture( texName );
1384 texture.assetName( texName.c_str() );
1385 texture.assetLocation( texPath );
1386 texture.waitForReady( true );
1387
1388 TextureDescriptor& texDesc = texture._propertyDescriptor;
1390 if ( srgb )
1391 {
1393 }
1394
1395 setTextureLocked( usage, texture, sampler, op, useInGeometryPasses );
1396 }
1397 }
1398 }
1399 }
1400
1401} //namespace Divide
#define WAIT_FOR_CONDITION(...)
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define NOP()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
virtual bool load(PlatformContext &context)
Loading and unloading interface.
Definition: Resource.cpp:55
ShaderComputeQueue & shaderComputeQueue() noexcept
Definition: GFXDevice.cpp:3107
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
U32 update(U64 deltaTimeUS)
Returns a bit mask composed of UpdateResult flags.
Definition: Material.cpp:276
static void OnStartup()
Definition: Material.cpp:121
void setRenderStateBlock(const RenderStateBlock &renderStateBlock, RenderStage stage, RenderPassType pass, RenderStagePass::VariantType variant=RenderStagePass::VariantType::COUNT)
Definition: Material.cpp:883
void loadRenderStatesFromXML(const std::string &entryName, const boost::property_tree::ptree &pt)
Definition: Material.cpp:1255
bool setTextureLocked(TextureSlot textureUsageSlot, Handle< Texture > texture, SamplerDescriptor sampler, TextureOperation op, bool useInGeometryPasses)
Definition: Material.cpp:360
FColour4 getBaseColour(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1010
Handle< ShaderProgram > computeAndGetProgramHandle(RenderStagePass renderStagePass)
Definition: Material.cpp:516
bool canDraw(RenderStagePass renderStagePass, bool &shaderJustFinishedLoading)
Definition: Material.cpp:554
Handle< Texture > getTexture(TextureSlot textureUsage) const
Definition: Material.inl:68
SharedMutex _textureLock
Definition: Material.h:374
static constexpr F32 MAX_SHININESS
Definition: Material.h:129
F32 getRoughness(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1064
static void RecomputeShaders()
Definition: Material.cpp:131
static void OnShutdown()
Definition: Material.cpp:126
bool hasTransparency() const noexcept
Definition: Material.inl:74
void getData(U32 bestProbeID, NodeMaterialData &dataOut)
Definition: Material.cpp:1086
void loadFromXML(const std::string &entryName, const boost::property_tree::ptree &pt)
Definition: Material.cpp:1192
F32 getMetallic(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1053
FColour3 getEmissive(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1022
void updateCullState()
Definition: Material.cpp:313
void setShaderProgramInternal(ShaderProgramDescriptor shaderDescriptor, RenderStagePass stagePass)
Definition: Material.cpp:437
FColour3 getAmbient(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1034
Handle< Material > clone(const std::string_view nameSuffix)
Return a new instance of this material with the name composed of the base material's name and the giv...
Definition: Material.cpp:222
Material(const ResourceDescriptor< Material > &descriptor)
Definition: Material.cpp:141
static void Update(U64 deltaTimeUS)
Definition: Material.cpp:136
std::array< TextureInfo, to_base(TextureSlot::COUNT)> _textures
Definition: Material.h:379
GFXDevice * _context
Definition: Material.h:362
bool usesTextureInShader(TextureSlot slot, bool isPrePass, bool isShadowPass) const noexcept
Definition: Material.cpp:785
DescriptorSet & getDescriptorSet(const RenderStagePass &renderStagePass)
Definition: Material.cpp:1127
void setTextureOperation(TextureSlot textureUsageSlot, TextureOperation op)
Definition: Material.cpp:425
void clearRenderStates()
Definition: Material.cpp:301
void saveTextureDataToXML(const std::string &entryName, boost::property_tree::ptree &pt) const
Definition: Material.cpp:1287
vector< Handle< Material > > _instances
Definition: Material.h:377
SharedMutex _instanceLock
Definition: Material.h:376
StatePassesPerStage< RenderStateBlockEntry > _defaultRenderStates
Definition: Material.h:371
std::array< ModuleDefines, to_base(ShaderType::COUNT)> _extraShaderDefines
Definition: Material.h:373
const RenderStateBlock & getOrCreateRenderStateBlock(RenderStagePass renderStagePass)
Definition: Material.cpp:940
static bool s_shadersDirty
Definition: Material.h:383
ShaderProgramInfo & shaderInfo(RenderStagePass renderStagePass)
Definition: Material.inl:92
FColour3 getSpecular(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1042
void updateTransparency()
Definition: Material.cpp:906
void recomputeShaders()
Definition: Material.cpp:488
bool setSampler(TextureSlot textureUsageSlot, SamplerDescriptor sampler)
Definition: Material.cpp:348
StatePassesPerStage< ShaderProgramInfo > _shaderInfo
Definition: Material.h:370
void getSortKeys(RenderStagePass renderStagePass, I64 &shaderKey, I64 &textureKey, bool &transparencyFlag) const
Definition: Material.cpp:995
bool unload() override
Definition: Material.cpp:843
void setPipelineLayout(PrimitiveTopology topology, const AttributeMap &shaderAttributes)
Definition: Material.cpp:331
bool setTexture(TextureSlot textureUsageSlot, Handle< Texture > texture, SamplerDescriptor sampler, TextureOperation op, bool useInGeometryPasses=false)
Definition: Material.cpp:415
bool load(PlatformContext &context) override
Loading and unloading interface.
Definition: Material.cpp:214
F32 getOcclusion(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1075
void computeAndAppendShaderDefines(ShaderProgramDescriptor &shaderDescriptor, RenderStagePass renderStagePass) const
Definition: Material.cpp:607
size_t _shaderAttributesHash
Definition: Material.h:381
void saveRenderStatesToXML(const std::string &entryName, boost::property_tree::ptree &pt) const
Definition: Material.cpp:1211
Handle< ShaderProgram > getProgramHandle(RenderStagePass renderStagePass) const
Definition: Material.cpp:539
void loadTextureDataFromXML(const std::string &entryName, const boost::property_tree::ptree &pt)
Definition: Material.cpp:1324
void saveToXML(const std::string &entryName, boost::property_tree::ptree &pt) const
Definition: Material.cpp:1183
PlatformContext & context() noexcept
GFXDevice & gfx() noexcept
Configuration & config() noexcept
void addToQueueBack(const ShaderQueueElement &element)
void addToQueueFront(const ShaderQueueElement &element)
void process(ShaderQueueElement &element)
static constexpr U8 WORLD_AO_LAYER_INDEX
Definition: ShadowMap.h:91
An API-independent representation of a texture.
Definition: Texture.h:83
void set(const T *v) noexcept
set the 4 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:1241
static constexpr const char * attribLocation[]
static const char * shadingMode[]
static const char * materialDebugFlag[]
Definition: MaterialEnums.h:68
static const char * textureOperation[]
static const char * bumpMethod[]
static constexpr const char * textureSlot[]
Definition: Material.h:90
constexpr Optick::Category::Type Streaming
Definition: Profiler.h:65
constexpr Optick::Category::Type Scene
Definition: Profiler.h:66
const char * RenderPassTypeToString(const RenderPassType pass) noexcept
Definition: GFXDevice.cpp:72
TextureSlot StringToTextureSlot(std::string_view name)
Definition: Material.cpp:51
BumpMethod StringToBumpMethod(std::string_view name)
Definition: Material.cpp:69
const char * BumpMethodToString(BumpMethod bumpMethod) noexcept
Definition: Material.cpp:64
const char * ShadingModeToString(ShadingMode shadingMode) noexcept
Definition: Material.cpp:82
const char * TextureSlotToString(TextureSlot texUsage) noexcept
Definition: Material.cpp:46
const char * MaterialDebugFlagToString(const MaterialDebugFlag unitType) noexcept
Definition: Material.cpp:28
TextureOperation StringToTextureOperation(std::string_view operation)
Definition: Material.cpp:105
const char * TextureOperationToString(TextureOperation textureOp) noexcept
Definition: Material.cpp:100
const char * RenderStageToString(const RenderStage stage) noexcept
Definition: GFXDevice.cpp:54
ShadingMode StringToShadingMode(std::string_view name)
Definition: Material.cpp:87
MaterialDebugFlag StringToMaterialDebugFlag(std::string_view name)
Definition: Material.cpp:33
Str StringFormat(const char *fmt, Args &&...args)
U32 PACK_UNORM4x8(const vec4< F32_NORM > &value)
Definition: MathHelper.cpp:369
SamplerDescriptor loadFromXML(const std::string &entryName, const boost::property_tree::ptree &pt)
void saveToXML(const SamplerDescriptor &sampler, const std::string &entryName, boost::property_tree::ptree &pt)
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
static constexpr bool IsDepthPass(const RenderStagePass stagePass) noexcept
constexpr U32 to_U32(const T value)
FORCE_INLINE void DestroyResource(Handle< T > &handle, const bool immediate=false)
MaterialDebugFlag
Definition: MaterialEnums.h:37
T * ResourcePtr
Definition: Resource.h:112
uint8_t U8
@ RES_LOADED
The resource is available for usage.
static constexpr bool IsZPrePass(const RenderStagePass stagePass) noexcept
vector< ModuleDefine > ModuleDefines
constexpr resolve_uac< A, B >::return_type add(const A &a, const B &b) noexcept
size_t GetHash(const PropertyDescriptor< T > &descriptor) noexcept
Definition: Resource.inl:40
void SaveToXML(const TerrainDescriptor &descriptor, boost::property_tree::ptree &pt)
FORCE_INLINE Handle< T > GetResourceRef(const Handle< T > handle)
TextureOperation
How should each texture be added.
hashAlg::unordered_map< K, V, HashFun, Predicate > hashMap
Definition: HashMap.h:55
std::shared_lock< mutex > SharedLock
Definition: SharedMutex.h:49
@ EQUAL
Passes if the incoming YYY value is equal to the stored YYY value.
void WaitForReady(Resource *res)
Definition: Resource.cpp:27
static constexpr bool IsShadowPass(const RenderStagePass stagePass) noexcept
bool LoadFromXML(TerrainDescriptor &descriptor, const boost::property_tree::ptree &pt, std::string_view name)
void Set(DescriptorSetBindingData &dataInOut, ShaderBuffer *buffer, const BufferRange range) noexcept
DescriptorSetBinding & AddBinding(DescriptorSet &setInOut, U8 slot, U16 stageVisibilityMask)
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
ShaderStageVisibility
constexpr U32 U32_MAX
constexpr I64 I64_LOWEST
std::scoped_lock< mutexes... > ScopedLock
Definition: SharedMutex.h:58
@ BACK
Cull Back facing polygons (aka CW)
U8 NumChannels(GFXImageFormat format) noexcept
int64_t I64
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
Project const SceneEntry & entry
Definition: DefaultScene.h:41
TranslucencySource
TextureSlot
Definition: Material.h:76
uint64_t U64
::value constexpr T CLAMPED_01(T n) noexcept
Definition: MathHelper.inl:134
constexpr auto to_base(const Type value) -> Type
Divide::Configuration::Rendering::ShadowMapping::CSMSettings csm
struct Divide::Configuration::Rendering::ShadowMapping shadowMapping
struct Divide::Configuration::Rendering rendering
static NO_INLINE void printfn(const char *format, T &&... args)
DescriptorSetBindingData _data
bool _useInGeometryPasses
Setting this to false will fallback to auto-usage selection (e.g. opacity tex will be used for alpha ...
Definition: Material.h:230
SamplerDescriptor _sampler
Definition: Material.h:227
Handle< Texture > _ptr
Definition: Material.h:226
TextureOperation _operation
Definition: Material.h:228
vector< ShaderModuleDescriptor > _modules
RenderPassType _passType
PropertyDescriptor< T > _propertyDescriptor
Definition: Resource.h:151
Handle< ShaderProgram > _shaderRef
ShaderBuildStage _shaderCompStage