// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include namespace Nz { bool MaterialParams::IsValid() const { return true; } Material::Material(MaterialSettings settings, const std::string& referenceModuleName) : Material(std::move(settings), Graphics::Instance()->GetShaderModuleResolver()->Resolve(referenceModuleName)) { } Material::Material(MaterialSettings settings, const nzsl::Ast::ModulePtr& referenceModule) : m_settings(std::move(settings)) { NazaraAssert(referenceModule, "invalid module"); Graphics* graphics = Graphics::Instance(); const std::shared_ptr& renderDevice = graphics->GetRenderDevice(); nzsl::Ast::SanitizeVisitor::Options options; options.forceAutoBindingResolve = true; options.partialSanitization = true; options.moduleResolver = graphics->GetShaderModuleResolver(); options.optionValues[CRC32("MaxLightCount")] = SafeCast(PredefinedLightData::MaxLightCount); options.optionValues[CRC32("MaxLightCascadeCount")] = SafeCast(PredefinedDirectionalLightData::MaxLightCascadeCount); options.optionValues[CRC32("MaxJointCount")] = SafeCast(PredefinedSkeletalData::MaxMatricesCount); nzsl::Ast::ModulePtr sanitizedModule = nzsl::Ast::Sanitize(*referenceModule, options); m_reflection.Reflect(*sanitizedModule); m_renderPipelineLayout = renderDevice->InstantiateRenderPipelineLayout(m_reflection.GetPipelineLayoutInfo()); if (const ShaderReflection::ExternalBlockData* block = m_reflection.GetExternalBlockByTag("Material")) { for (const auto& [tag, shaderSampler] : block->samplers) { std::size_t textureIndex = m_textures.size(); auto& texture = m_textures.emplace_back(); texture.bindingIndex = shaderSampler.bindingIndex; texture.bindingSet = shaderSampler.bindingSet; texture.imageType = ToImageType(shaderSampler.imageType); m_textureByTag.emplace(tag, textureIndex); } assert(block->storageBlocks.empty()); //< TODO for (const auto& [tag, shaderBlock] : block->uniformBlocks) { std::size_t blockIndex = m_uniformBlocks.size(); const ShaderReflection::StructData* structData = m_reflection.GetStructByIndex(shaderBlock.structIndex); assert(structData); std::size_t size = structData->fieldOffsets.GetAlignedSize(); auto& uniformBlock = m_uniformBlocks.emplace_back(); uniformBlock.bindingIndex = shaderBlock.bindingIndex; uniformBlock.bindingSet = shaderBlock.bindingSet; uniformBlock.bufferPool = std::make_unique(renderDevice, BufferType::Uniform, size); uniformBlock.structIndex = shaderBlock.structIndex; m_uniformBlockByTag.emplace(tag, blockIndex); } } m_engineShaderBindings.fill(InvalidBindingIndex); if (const ShaderReflection::ExternalBlockData* block = m_reflection.GetExternalBlockByTag("Engine")) { // TODO: Ensure structs layout is what's expected if (auto it = block->uniformBlocks.find("InstanceData"); it != block->uniformBlocks.end()) m_engineShaderBindings[EngineShaderBinding::InstanceDataUbo] = it->second.bindingIndex; if (auto it = block->uniformBlocks.find("LightData"); it != block->uniformBlocks.end()) m_engineShaderBindings[EngineShaderBinding::LightDataUbo] = it->second.bindingIndex; if (auto it = block->uniformBlocks.find("ViewerData"); it != block->uniformBlocks.end()) m_engineShaderBindings[EngineShaderBinding::ViewerDataUbo] = it->second.bindingIndex; if (auto it = block->samplers.find("ShadowMapsDirectional"); it != block->samplers.end()) m_engineShaderBindings[EngineShaderBinding::ShadowmapDirectional] = it->second.bindingIndex; if (auto it = block->samplers.find("ShadowMapsPoint"); it != block->samplers.end()) m_engineShaderBindings[EngineShaderBinding::ShadowmapPoint] = it->second.bindingIndex; if (auto it = block->samplers.find("ShadowMapsSpot"); it != block->samplers.end()) m_engineShaderBindings[EngineShaderBinding::ShadowmapSpot] = it->second.bindingIndex; if (auto it = block->uniformBlocks.find("SkeletalData"); it != block->uniformBlocks.end()) m_engineShaderBindings[EngineShaderBinding::SkeletalDataUbo] = it->second.bindingIndex; if (auto it = block->samplers.find("TextureOverlay"); it != block->samplers.end()) m_engineShaderBindings[EngineShaderBinding::OverlayTexture] = it->second.bindingIndex; } for (const auto& handlerPtr : m_settings.GetPropertyHandlers()) handlerPtr->Setup(*this, m_reflection); for (const auto& passOpt : m_settings.GetPasses()) { if (!passOpt) continue; for (const auto& uberShader : passOpt->shaders) { uberShader->UpdateConfigCallback([=](UberShader::Config& config, const std::vector& vertexBuffers) { if (vertexBuffers.empty()) return; const VertexDeclaration& vertexDeclaration = *vertexBuffers.front().declaration; const auto& components = vertexDeclaration.GetComponents(); Int32 locationIndex = 0; for (const auto& component : components) { switch (component.component) { case VertexComponent::Color: config.optionValues[CRC32("VertexColorLoc")] = locationIndex; break; case VertexComponent::Normal: config.optionValues[CRC32("VertexNormalLoc")] = locationIndex; break; case VertexComponent::Position: config.optionValues[CRC32("VertexPositionLoc")] = locationIndex; break; case VertexComponent::Tangent: config.optionValues[CRC32("VertexTangentLoc")] = locationIndex; break; case VertexComponent::TexCoord: config.optionValues[CRC32("VertexUvLoc")] = locationIndex; break; case VertexComponent::JointIndices: config.optionValues[CRC32("VertexJointIndicesLoc")] = locationIndex; break; case VertexComponent::JointWeights: config.optionValues[CRC32("VertexJointWeightsLoc")] = locationIndex; break; case VertexComponent::Unused: case VertexComponent::Userdata: break; } ++locationIndex; } }); } } } std::shared_ptr Material::GetDefaultInstance() const { std::shared_ptr instance = m_defaultInstance.lock(); if (!instance) { instance = Instantiate(); m_defaultInstance = std::weak_ptr(instance); } return instance; } std::shared_ptr Material::Instantiate() const { return std::make_shared(shared_from_this()); } std::shared_ptr Material::Build(const ParameterList& materialData) { return std::shared_ptr(); } std::shared_ptr Material::Get(MaterialType lightingType) { Graphics* graphics = Graphics::Instance(); NazaraAssert(graphics, "Graphics module has not been initialized"); return graphics->GetDefaultMaterials().materials[lightingType].material; } std::shared_ptr Material::LoadFromFile(const std::filesystem::path& filePath, const MaterialParams& params) { Graphics* graphics = Graphics::Instance(); NazaraAssert(graphics, "Graphics module has not been initialized"); return graphics->GetMaterialLoader().LoadFromFile(filePath, params); } std::shared_ptr Material::LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params) { Graphics* graphics = Graphics::Instance(); NazaraAssert(graphics, "Graphics module has not been initialized"); return graphics->GetMaterialLoader().LoadFromMemory(data, size, params); } std::shared_ptr Material::LoadFromStream(Stream& stream, const MaterialParams& params) { Graphics* graphics = Graphics::Instance(); NazaraAssert(graphics, "Graphics module has not been initialized"); return graphics->GetMaterialLoader().LoadFromStream(stream, params); } }