From f32793273800c2f143a809c7d8f73d205f388a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Sat, 2 Jan 2021 21:15:59 +0100 Subject: [PATCH] Commit current work Reworked conditions, added uber-shaders, comparison nodes, fixed Discard --- build/scripts/modules/graphics.lua | 1 + examples/GraphicsTest/main.cpp | 75 ++- include/Nazara/Graphics/BasicMaterial.hpp | 30 +- include/Nazara/Graphics/BasicMaterial.inl | 59 ++- include/Nazara/Graphics/Material.hpp | 10 +- include/Nazara/Graphics/Material.inl | 47 +- include/Nazara/Graphics/MaterialPipeline.hpp | 10 +- include/Nazara/Graphics/MaterialPipeline.inl | 10 +- include/Nazara/Graphics/MaterialSettings.hpp | 38 +- include/Nazara/Graphics/MaterialSettings.inl | 76 +-- include/Nazara/Graphics/UberShader.hpp | 39 ++ include/Nazara/Graphics/UberShader.inl | 12 + .../Nazara/OpenGLRenderer/OpenGLDevice.hpp | 1 + .../OpenGLRenderPipelineLayout.hpp | 6 +- .../OpenGLRenderer/OpenGLShaderStage.hpp | 7 + .../OpenGLRenderer/Wrapper/WGL/WGLContext.inl | 3 +- include/Nazara/Renderer/RenderDevice.hpp | 3 + include/Nazara/Shader/ShaderAst.hpp | 4 + include/Nazara/Shader/ShaderAst.inl | 11 + include/Nazara/Shader/ShaderAstSerializer.hpp | 1 + include/Nazara/Shader/ShaderAstSerializer.inl | 6 + include/Nazara/Shader/ShaderBuilder.hpp | 7 +- include/Nazara/Shader/ShaderEnums.hpp | 13 +- include/Nazara/Shader/ShaderNodes.inl | 2 +- include/Nazara/Shader/ShaderWriter.hpp | 2 +- include/Nazara/Shader/SpirvWriter.inl | 6 +- .../Nazara/VulkanRenderer/VulkanDevice.hpp | 1 + .../VulkanRenderer/VulkanShaderStage.hpp | 2 + .../basicmaterial.frag.shaderflow | 459 +++++++++++++++--- src/Nazara/Core/Error.cpp | 5 +- src/Nazara/Graphics/BasicMaterial.cpp | 108 +++-- src/Nazara/Graphics/Material.cpp | 6 +- src/Nazara/Graphics/MaterialPipeline.cpp | 7 +- src/Nazara/Graphics/PhongLightingMaterial.cpp | 54 +-- .../Shaders/basicmaterial.frag.shader | Bin 1126 -> 1745 bytes .../Shaders/basicmaterial.frag.shader.h | 2 +- .../Shaders/basicmaterial.vert.shader | Bin 1046 -> 1050 bytes .../Shaders/basicmaterial.vert.shader.h | 2 +- src/Nazara/Graphics/UberShader.cpp | 54 +++ .../OpenGLRenderer/OpenGLCommandBuffer.cpp | 1 + src/Nazara/OpenGLRenderer/OpenGLDevice.cpp | 5 + .../OpenGLRenderer/OpenGLShaderBinding.cpp | 6 + .../OpenGLRenderer/OpenGLShaderStage.cpp | 65 ++- .../OpenGLRenderer/Wrapper/WGL/WGLContext.cpp | 18 +- src/Nazara/Physics2D/Arbiter2D.cpp | 2 +- src/Nazara/Shader/GlslWriter.cpp | 51 +- src/Nazara/Shader/ShaderAstSerializer.cpp | 3 + src/Nazara/Shader/ShaderAstValidator.cpp | 33 +- src/Nazara/Shader/ShaderNodes.cpp | 7 +- src/Nazara/Shader/SpirvAstVisitor.cpp | 162 ++++++- src/Nazara/Shader/SpirvWriter.cpp | 16 +- src/Nazara/VulkanRenderer/VulkanDevice.cpp | 9 + .../VulkanRenderer/VulkanShaderStage.cpp | 36 +- .../VulkanRenderer/Wrapper/Instance.cpp | 6 +- src/ShaderNode/DataModels/BinOp.hpp | 163 +++++++ src/ShaderNode/DataModels/BinOp.inl | 240 +++++++++ src/ShaderNode/DataModels/BoolValue.cpp | 126 +++++ src/ShaderNode/DataModels/BoolValue.hpp | 47 ++ src/ShaderNode/DataModels/BoolValue.inl | 1 + src/ShaderNode/DataModels/BufferField.cpp | 3 +- src/ShaderNode/DataModels/BufferField.hpp | 3 +- src/ShaderNode/DataModels/Cast.hpp | 3 +- src/ShaderNode/DataModels/Cast.inl | 75 +-- src/ShaderNode/DataModels/CompOp.hpp | 220 +++++++++ src/ShaderNode/DataModels/CompOp.inl | 293 +++++++++++ .../DataModels/ConditionalExpression.cpp | 59 ++- .../DataModels/ConditionalExpression.hpp | 3 +- src/ShaderNode/DataModels/FloatValue.cpp | 23 +- src/ShaderNode/DataModels/FloatValue.hpp | 8 +- src/ShaderNode/DataModels/InputValue.cpp | 32 +- src/ShaderNode/DataModels/InputValue.hpp | 5 +- src/ShaderNode/DataModels/Mat4BinOp.hpp | 4 +- src/ShaderNode/DataModels/Mat4BinOp.inl | 48 +- src/ShaderNode/DataModels/Mat4VecMul.cpp | 4 +- src/ShaderNode/DataModels/Mat4VecMul.hpp | 2 +- src/ShaderNode/DataModels/OutputValue.cpp | 40 +- src/ShaderNode/DataModels/OutputValue.hpp | 5 +- .../DataModels/PositionOutputValue.cpp | 3 +- .../DataModels/PositionOutputValue.hpp | 2 +- src/ShaderNode/DataModels/SampleTexture.cpp | 3 +- src/ShaderNode/DataModels/SampleTexture.hpp | 2 +- src/ShaderNode/DataModels/ShaderNode.cpp | 5 + src/ShaderNode/DataModels/ShaderNode.hpp | 3 +- src/ShaderNode/DataModels/TextureValue.cpp | 29 +- src/ShaderNode/DataModels/TextureValue.hpp | 5 +- src/ShaderNode/DataModels/VecBinOp.cpp | 75 --- src/ShaderNode/DataModels/VecBinOp.hpp | 86 ---- src/ShaderNode/DataModels/VecBinOp.inl | 131 ----- src/ShaderNode/DataModels/VecComposition.hpp | 49 ++ src/ShaderNode/DataModels/VecComposition.inl | 192 ++++++++ .../DataModels/VecDecomposition.cpp | 176 +++++++ .../DataModels/VecDecomposition.hpp | 48 ++ .../{Cast.cpp => VecDecomposition.inl} | 0 src/ShaderNode/DataModels/VecDot.cpp | 4 +- src/ShaderNode/DataModels/VecDot.hpp | 2 +- src/ShaderNode/DataModels/VecFloatMul.cpp | 4 +- src/ShaderNode/DataModels/VecFloatMul.hpp | 2 +- src/ShaderNode/DataModels/VecValue.cpp | 1 - src/ShaderNode/DataModels/VecValue.hpp | 8 +- src/ShaderNode/DataModels/VecValue.inl | 30 +- src/ShaderNode/ShaderGraph.cpp | 162 +++++-- src/ShaderNode/Widgets/ConditionEditor.cpp | 4 +- src/ShaderNode/Widgets/MainWindow.cpp | 6 +- 103 files changed, 3248 insertions(+), 790 deletions(-) create mode 100644 include/Nazara/Graphics/UberShader.hpp create mode 100644 include/Nazara/Graphics/UberShader.inl create mode 100644 src/Nazara/Graphics/UberShader.cpp create mode 100644 src/ShaderNode/DataModels/BinOp.hpp create mode 100644 src/ShaderNode/DataModels/BinOp.inl create mode 100644 src/ShaderNode/DataModels/BoolValue.cpp create mode 100644 src/ShaderNode/DataModels/BoolValue.hpp create mode 100644 src/ShaderNode/DataModels/BoolValue.inl create mode 100644 src/ShaderNode/DataModels/CompOp.hpp create mode 100644 src/ShaderNode/DataModels/CompOp.inl delete mode 100644 src/ShaderNode/DataModels/VecBinOp.cpp delete mode 100644 src/ShaderNode/DataModels/VecBinOp.hpp delete mode 100644 src/ShaderNode/DataModels/VecBinOp.inl create mode 100644 src/ShaderNode/DataModels/VecComposition.hpp create mode 100644 src/ShaderNode/DataModels/VecComposition.inl create mode 100644 src/ShaderNode/DataModels/VecDecomposition.cpp create mode 100644 src/ShaderNode/DataModels/VecDecomposition.hpp rename src/ShaderNode/DataModels/{Cast.cpp => VecDecomposition.inl} (100%) delete mode 100644 src/ShaderNode/DataModels/VecValue.cpp diff --git a/build/scripts/modules/graphics.lua b/build/scripts/modules/graphics.lua index 71b2738ce..92a087236 100644 --- a/build/scripts/modules/graphics.lua +++ b/build/scripts/modules/graphics.lua @@ -5,5 +5,6 @@ MODULE.ClientOnly = true MODULE.Libraries = { "NazaraCore", "NazaraRenderer", + "NazaraShader", "NazaraUtility" } diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index 946d6bc28..5e09b0b9f 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -35,20 +35,6 @@ int main() std::shared_ptr device = window.GetRenderDevice(); - /*auto fragmentShader = device->InstantiateShaderStage(Nz::ShaderStageType::Fragment, Nz::ShaderLanguage::NazaraBinary, "frag.shader"); - if (!fragmentShader) - { - std::cout << "Failed to instantiate fragment shader" << std::endl; - return __LINE__; - } - - auto vertexShader = device->InstantiateShaderStage(Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraBinary, "vert.shader"); - if (!vertexShader) - { - std::cout << "Failed to instantiate fragment shader" << std::endl; - return __LINE__; - }*/ - Nz::MeshRef drfreak = Nz::Mesh::LoadFromFile("resources/Spaceship/spaceship.obj", meshParams); if (!drfreak) { @@ -89,20 +75,49 @@ int main() return __LINE__; } + // Texture (alpha-map) + Nz::ImageRef alphaImage = Nz::Image::LoadFromFile("alphatest.png"); + if (!alphaImage || !alphaImage->Convert(Nz::PixelFormat_RGBA8)) + { + NazaraError("Failed to load image"); + return __LINE__; + } + + Nz::TextureInfo alphaTexParams; + alphaTexParams.pixelFormat = alphaImage->GetFormat(); + alphaTexParams.type = alphaImage->GetType(); + alphaTexParams.width = alphaImage->GetWidth(); + alphaTexParams.height = alphaImage->GetHeight(); + alphaTexParams.depth = alphaImage->GetDepth(); + + std::shared_ptr alphaTexture = device->InstantiateTexture(alphaTexParams); + if (!alphaTexture->Update(alphaImage->GetConstPixels())) + { + NazaraError("Failed to update texture"); + return __LINE__; + } + + + std::shared_ptr textureSampler = device->InstantiateTextureSampler({}); Nz::Material material(Nz::BasicMaterial::GetSettings()); - //material.SetShader(Nz::ShaderStageType::Fragment, fragmentShader); - //material.SetShader(Nz::ShaderStageType::Vertex, vertexShader); material.EnableDepthBuffer(true); - material.SetTexture(0, texture); - material.SetTextureSampler(0, textureSampler); + + Nz::BasicMaterial basicMat(material); + basicMat.EnableAlphaTest(true); + basicMat.SetAlphaMap(alphaTexture); + basicMat.SetAlphaSampler(textureSampler); + basicMat.SetDiffuseMap(alphaTexture); + basicMat.SetDiffuseSampler(textureSampler); Nz::PredefinedInstanceData instanceUboOffsets = Nz::PredefinedInstanceData::GetOffsets(); Nz::PredefinedViewerData viewerUboOffsets = Nz::PredefinedViewerData::GetOffsets(); + const Nz::BasicMaterial::UniformOffsets& materialSettingOffsets = Nz::BasicMaterial::GetOffsets(); std::vector instanceDataBuffer(instanceUboOffsets.totalSize); std::vector viewerDataBuffer(viewerUboOffsets.totalSize); + std::vector materialSettings(materialSettingOffsets.totalSize); Nz::Vector2ui windowSize = window.GetSize(); @@ -110,6 +125,9 @@ int main() Nz::AccessByOffset(viewerDataBuffer.data(), viewerUboOffsets.viewMatrixOffset) = Nz::Matrix4f::Translate(Nz::Vector3f::Backward() * 1); Nz::AccessByOffset(viewerDataBuffer.data(), viewerUboOffsets.projMatrixOffset) = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f); + Nz::AccessByOffset(materialSettings.data(), materialSettingOffsets.alphaThreshold) = 0.5f; + Nz::AccessByOffset(materialSettings.data(), materialSettingOffsets.diffuseColor) = Nz::Vector4f(1.f, 1.f, 1.f, 1.f); + std::shared_ptr renderPipelineLayout = material.GetSettings()->GetRenderPipelineLayout(); std::shared_ptr instanceDataUbo = device->InstantiateBuffer(Nz::BufferType_Uniform); @@ -128,6 +146,15 @@ int main() return __LINE__; } + std::shared_ptr matSettingUBO = device->InstantiateBuffer(Nz::BufferType_Uniform); + if (!matSettingUBO->Initialize(materialSettings.size(), Nz::BufferUsage_DeviceLocal | Nz::BufferUsage_Dynamic)) + { + NazaraError("Failed to create mat setting UBO"); + return __LINE__; + } + + matSettingUBO->Fill(materialSettings.data(), 0, materialSettings.size()); + Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding(); shaderBinding->Update({ { @@ -142,6 +169,18 @@ int main() instanceDataUbo.get(), 0, instanceDataBuffer.size() } }, + { + 3, + Nz::ShaderBinding::UniformBufferBinding { + matSettingUBO.get(), 0, materialSettings.size() + } + }, + { + 0, + Nz::ShaderBinding::TextureBinding { + alphaTexture.get(), textureSampler.get() + } + }, { 1, Nz::ShaderBinding::TextureBinding { diff --git a/include/Nazara/Graphics/BasicMaterial.hpp b/include/Nazara/Graphics/BasicMaterial.hpp index ef7488a5e..ff4815732 100644 --- a/include/Nazara/Graphics/BasicMaterial.hpp +++ b/include/Nazara/Graphics/BasicMaterial.hpp @@ -17,30 +17,48 @@ namespace Nz friend class MaterialPipeline; public: + struct UniformOffsets; + BasicMaterial(Material& material); + inline void EnableAlphaTest(bool alphaTest); + inline const std::shared_ptr& GetAlphaMap() const; - float GetAlphaThreshold() const; + inline const std::shared_ptr& GetAlphaSampler() const; + float GetAlphaTestThreshold() const; Color GetDiffuseColor() const; inline const std::shared_ptr& GetDiffuseMap() const; + inline const std::shared_ptr& GetDiffuseSampler() const; inline bool HasAlphaMap() const; - inline bool HasAlphaThreshold() const; + inline bool HasAlphaTest() const; + inline bool HasAlphaTestThreshold() const; inline bool HasDiffuseColor() const; inline bool HasDiffuseMap() const; inline void SetAlphaMap(std::shared_ptr alphaMap); - void SetAlphaThreshold(float alphaThreshold); + inline void SetAlphaSampler(std::shared_ptr alphaSampler); + void SetAlphaTestThreshold(float alphaThreshold); void SetDiffuseColor(const Color& diffuse); inline void SetDiffuseMap(std::shared_ptr diffuseMap); + inline void SetDiffuseSampler(std::shared_ptr diffuseSampler); - static const std::shared_ptr& GetSettings(); + static inline const UniformOffsets& GetOffsets(); + static inline const std::shared_ptr& GetSettings(); - private: struct UniformOffsets { std::size_t alphaThreshold; std::size_t diffuseColor; + std::size_t totalSize; + }; + + private: + struct ConditionIndexes + { + std::size_t alphaTest; + std::size_t hasAlphaMap; + std::size_t hasDiffuseMap; }; struct TextureIndexes @@ -54,11 +72,13 @@ namespace Nz Material& m_material; std::size_t m_uniformBlockIndex; + ConditionIndexes m_conditionIndexes; TextureIndexes m_textureIndexes; UniformOffsets m_uniformOffsets; static std::shared_ptr s_materialSettings; static std::size_t s_uniformBlockIndex; + static ConditionIndexes s_conditionIndexes; static TextureIndexes s_textureIndexes; static UniformOffsets s_uniformOffsets; }; diff --git a/include/Nazara/Graphics/BasicMaterial.inl b/include/Nazara/Graphics/BasicMaterial.inl index 7a13539c8..875dc4b8e 100644 --- a/include/Nazara/Graphics/BasicMaterial.inl +++ b/include/Nazara/Graphics/BasicMaterial.inl @@ -9,24 +9,47 @@ namespace Nz { + inline void BasicMaterial::EnableAlphaTest(bool alphaTest) + { + NazaraAssert(HasAlphaTest(), "Material has no alpha test condition"); + m_material.EnableCondition(m_conditionIndexes.alphaTest, alphaTest); + } + inline const std::shared_ptr& BasicMaterial::GetAlphaMap() const { - NazaraAssert(HasAlphaMap(), "Material has no alpha map slot"); + NazaraAssert(HasAlphaMap(), "Material has no alpha texture slot"); return m_material.GetTexture(m_textureIndexes.alpha); } + inline const std::shared_ptr& BasicMaterial::GetAlphaSampler() const + { + NazaraAssert(HasAlphaMap(), "Material has no alpha texture slot"); + return m_material.GetTextureSampler(m_textureIndexes.alpha); + } + inline const std::shared_ptr& BasicMaterial::GetDiffuseMap() const { - NazaraAssert(HasDiffuseMap(), "Material has no alpha map slot"); + NazaraAssert(HasDiffuseMap(), "Material has no alpha texture slot"); return m_material.GetTexture(m_textureIndexes.diffuse); } + inline const std::shared_ptr& BasicMaterial::GetDiffuseSampler() const + { + NazaraAssert(HasDiffuseMap(), "Material has no alpha texture slot"); + return m_material.GetTextureSampler(m_textureIndexes.diffuse); + } + inline bool BasicMaterial::HasAlphaMap() const { return m_textureIndexes.alpha != MaterialSettings::InvalidIndex; } - inline bool BasicMaterial::HasAlphaThreshold() const + inline bool BasicMaterial::HasAlphaTest() const + { + return m_conditionIndexes.alphaTest != MaterialSettings::InvalidIndex; + } + + inline bool BasicMaterial::HasAlphaTestThreshold() const { return m_uniformOffsets.alphaThreshold != MaterialSettings::InvalidIndex; } @@ -44,13 +67,43 @@ namespace Nz inline void BasicMaterial::SetAlphaMap(std::shared_ptr alphaMap) { NazaraAssert(HasAlphaMap(), "Material has no alpha map slot"); + bool hasAlphaMap = (alphaMap != nullptr); m_material.SetTexture(m_textureIndexes.alpha, std::move(alphaMap)); + + if (m_conditionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex) + m_material.EnableCondition(m_conditionIndexes.hasAlphaMap, hasAlphaMap); + } + + inline void BasicMaterial::SetAlphaSampler(std::shared_ptr alphaSampler) + { + NazaraAssert(HasAlphaMap(), "Material has no alpha map slot"); + m_material.SetTextureSampler(m_textureIndexes.alpha, std::move(alphaSampler)); } inline void BasicMaterial::SetDiffuseMap(std::shared_ptr diffuseMap) { NazaraAssert(HasDiffuseMap(), "Material has no diffuse map slot"); + bool hasDiffuseMap = (diffuseMap != nullptr); m_material.SetTexture(m_textureIndexes.diffuse, std::move(diffuseMap)); + + if (m_conditionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex) + m_material.EnableCondition(m_conditionIndexes.hasDiffuseMap, hasDiffuseMap); + } + + inline void BasicMaterial::SetDiffuseSampler(std::shared_ptr diffuseSampler) + { + NazaraAssert(HasDiffuseMap(), "Material has no diffuse map slot"); + m_material.SetTextureSampler(m_textureIndexes.diffuse, std::move(diffuseSampler)); + } + + inline const std::shared_ptr& BasicMaterial::GetSettings() + { + return s_materialSettings; + } + + inline auto BasicMaterial::GetOffsets() -> const UniformOffsets& + { + return s_uniformOffsets; } } diff --git a/include/Nazara/Graphics/Material.hpp b/include/Nazara/Graphics/Material.hpp index 1459e21d0..f3e58e271 100644 --- a/include/Nazara/Graphics/Material.hpp +++ b/include/Nazara/Graphics/Material.hpp @@ -41,6 +41,7 @@ namespace Nz inline void EnableAlphaTest(bool alphaTest); inline void EnableBlending(bool blending); inline void EnableColorWrite(bool colorWrite); + inline void EnableCondition(std::size_t conditionIndex, bool enable); inline void EnableDepthBuffer(bool depthBuffer); inline void EnableDepthSorting(bool depthSorting); inline void EnableDepthWrite(bool depthWrite); @@ -53,7 +54,7 @@ namespace Nz inline void EnableVertexColor(bool vertexColor); inline void EnsurePipelineUpdate() const; - + inline RendererComparison GetDepthCompareFunc() const; inline BlendFunc GetDstBlend() const; inline FaceSide GetFaceCulling() const; @@ -63,7 +64,7 @@ namespace Nz inline const MaterialPipelineInfo& GetPipelineInfo() const; inline float GetPointSize() const; inline const std::shared_ptr& GetSettings() const; - inline const std::shared_ptr& GetShader(ShaderStageType shaderStage) const; + inline const std::shared_ptr& GetShader(ShaderStageType shaderStage) const; inline BlendFunc GetSrcBlend() const; inline const std::shared_ptr& GetTexture(std::size_t textureIndex) const; inline const std::shared_ptr& GetTextureSampler(std::size_t textureIndex) const; @@ -76,6 +77,7 @@ namespace Nz inline bool IsAlphaTestEnabled() const; inline bool IsBlendingEnabled() const; inline bool IsColorWriteEnabled() const; + inline bool IsConditionEnabled(std::size_t conditionIndex) const; inline bool IsDepthBufferEnabled() const; inline bool IsDepthSortingEnabled() const; inline bool IsDepthWriteEnabled() const; @@ -92,7 +94,6 @@ namespace Nz inline void SetFaceFilling(FaceFilling filling); inline void SetLineWidth(float lineWidth); inline void SetPointSize(float pointSize); - inline void SetShader(ShaderStageType shaderStage, std::shared_ptr shader); inline void SetUniformBuffer(std::size_t bufferIndex, UniformBufferRef uniformBuffer); inline void SetSrcBlend(BlendFunc func); inline void SetTexture(std::size_t textureIndex, std::shared_ptr texture); @@ -115,7 +116,8 @@ namespace Nz std::vector m_textures; std::vector m_uniformBuffers; mutable std::shared_ptr m_pipeline; - MaterialPipelineInfo m_pipelineInfo; + UInt32 m_enabledConditions; + mutable MaterialPipelineInfo m_pipelineInfo; mutable bool m_pipelineUpdated; bool m_shadowCastingEnabled; }; diff --git a/include/Nazara/Graphics/Material.inl b/include/Nazara/Graphics/Material.inl index 3729bf2e2..8a2e56b15 100644 --- a/include/Nazara/Graphics/Material.inl +++ b/include/Nazara/Graphics/Material.inl @@ -111,6 +111,15 @@ namespace Nz InvalidatePipeline(); } + inline void Material::EnableCondition(std::size_t conditionIndex, bool enable) + { + if (TestBit(m_enabledConditions, conditionIndex) != enable) + { + m_enabledConditions = SetBit(m_enabledConditions, conditionIndex); + InvalidatePipeline(); + } + } + /*! * \brief Enable/Disable depth buffer for this material * @@ -428,9 +437,9 @@ namespace Nz * \brief Gets the über-shader used by this material * \return Constant pointer to the über-shader used */ - inline const std::shared_ptr& Material::GetShader(ShaderStageType shaderStage) const + inline const std::shared_ptr& Material::GetShader(ShaderStageType shaderStage) const { - return m_pipelineInfo.shaders[UnderlyingCast(shaderStage)]; + return m_pipelineInfo.shaders[UnderlyingCast(shaderStage)].uberShader; } /*! @@ -507,6 +516,11 @@ namespace Nz return m_pipelineInfo.colorWrite; } + inline bool Material::IsConditionEnabled(std::size_t conditionIndex) const + { + return TestBit(m_enabledConditions, conditionIndex); + } + /*! * \brief Checks whether this material has depth buffer enabled * \return true If it is the case @@ -682,22 +696,6 @@ namespace Nz InvalidatePipeline(); } - /*! - * \brief Sets the shader with a constant reference to a ubershader - * - * \param uberShader Uber shader to apply - * - * \remark Invalidates the pipeline - * - * \see GetShader - */ - inline void Material::SetShader(ShaderStageType shaderStage, std::shared_ptr shader) - { - m_pipelineInfo.shaders[UnderlyingCast(shaderStage)] = std::move(shader); - - InvalidatePipeline(); - } - inline void Material::SetUniformBuffer(std::size_t bufferIndex, UniformBufferRef uniformBuffer) { NazaraAssert(bufferIndex < m_uniformBuffers.size(), "Invalid shared uniform buffer index"); @@ -739,6 +737,19 @@ namespace Nz inline void Material::UpdatePipeline() const { + for (auto& shader : m_pipelineInfo.shaders) + shader.enabledConditions = 0; + + const auto& conditions = m_settings->GetConditions(); + for (std::size_t conditionIndex = 0; conditionIndex < conditions.size(); ++conditionIndex) + { + if (TestBit(m_enabledConditions, conditionIndex)) + { + for (std::size_t shaderStage = 0; shaderStage < ShaderStageTypeCount; ++shaderStage) + m_pipelineInfo.shaders[shaderStage].enabledConditions |= conditions[conditionIndex].enabledConditions[shaderStage]; + } + } + m_pipeline = MaterialPipeline::Get(m_pipelineInfo); m_pipelineUpdated = true; } diff --git a/include/Nazara/Graphics/MaterialPipeline.hpp b/include/Nazara/Graphics/MaterialPipeline.hpp index 188536b23..9e02828b1 100644 --- a/include/Nazara/Graphics/MaterialPipeline.hpp +++ b/include/Nazara/Graphics/MaterialPipeline.hpp @@ -17,18 +17,24 @@ namespace Nz { - class ShaderStage; + class UberShader; struct MaterialPipelineInfo : RenderStates { + struct ShaderStage + { + std::shared_ptr uberShader; + Nz::UInt64 enabledConditions = 0; + }; + bool alphaTest = false; bool depthSorting = false; bool hasVertexColor = false; bool reflectionMapping = false; bool shadowReceive = true; + std::array shaders; std::shared_ptr settings; - std::array, ShaderStageTypeCount> shaders; }; inline bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs); diff --git a/include/Nazara/Graphics/MaterialPipeline.inl b/include/Nazara/Graphics/MaterialPipeline.inl index f4d80e9c4..b83e6fc6b 100644 --- a/include/Nazara/Graphics/MaterialPipeline.inl +++ b/include/Nazara/Graphics/MaterialPipeline.inl @@ -40,7 +40,10 @@ namespace Nz for (std::size_t i = 0; i < lhs.shaders.size(); ++i) { - if (lhs.shaders[i] != rhs.shaders[i]) + if (lhs.shaders[i].enabledConditions != rhs.shaders[i].enabledConditions) + return false; + + if (lhs.shaders[i].uberShader != rhs.shaders[i].uberShader) return false; } @@ -82,7 +85,10 @@ namespace std NazaraPipelineMember(settings.get()); //< Hash pointer for (const auto& shader : pipelineInfo.shaders) - Nz::HashCombine(seed, shader.get()); + { + Nz::HashCombine(seed, shader.enabledConditions); + Nz::HashCombine(seed, shader.uberShader.get()); + } #undef NazaraPipelineMember #undef NazaraPipelineBoolMember diff --git a/include/Nazara/Graphics/MaterialSettings.hpp b/include/Nazara/Graphics/MaterialSettings.hpp index e2aec4257..272e478df 100644 --- a/include/Nazara/Graphics/MaterialSettings.hpp +++ b/include/Nazara/Graphics/MaterialSettings.hpp @@ -19,29 +19,35 @@ namespace Nz { + class UberShader; + class MaterialSettings { public: - using DefaultShaders = std::array, ShaderStageTypeCount>; using PredefinedBinding = std::array; + using Shaders = std::array, ShaderStageTypeCount>; + struct Builder; + struct Condition; struct SharedUniformBlock; struct Texture; struct UniformBlock; inline MaterialSettings(); - inline MaterialSettings(std::vector textures, std::vector uniformBlocks, std::vector sharedUniformBlocks, const PredefinedBinding& predefinedBinding, DefaultShaders defaultShaders); + inline MaterialSettings(Builder builder); MaterialSettings(const MaterialSettings&) = default; MaterialSettings(MaterialSettings&&) = delete; ~MaterialSettings() = default; - inline const std::shared_ptr& GetDefaultShader(ShaderStageType stage) const; - inline const DefaultShaders& GetDefaultShaders() const; + inline const std::vector& GetConditions() const; + inline std::size_t GetConditionIndex(const std::string_view& name) const; inline std::size_t GetPredefinedBindingIndex(PredefinedShaderBinding binding) const; inline const std::shared_ptr& GetRenderPipelineLayout() const; + inline const std::shared_ptr& GetShader(ShaderStageType stage) const; + inline const Shaders& GetShaders() const; inline const std::vector& GetSharedUniformBlocks() const; - inline std::size_t GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const; inline std::size_t GetSharedUniformBlockIndex(const std::string_view& name) const; + inline std::size_t GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const; inline const std::vector& GetTextures() const; inline std::size_t GetTextureIndex(const std::string_view& name) const; inline const std::vector& GetUniformBlocks() const; @@ -53,6 +59,22 @@ namespace Nz static constexpr std::size_t InvalidIndex = std::numeric_limits::max(); + struct Builder + { + PredefinedBinding predefinedBinding; + Shaders shaders; + std::vector conditions; + std::vector textures; + std::vector uniformBlocks; + std::vector sharedUniformBlocks; + }; + + struct Condition + { + std::string name; + std::array enabledConditions; + }; + struct UniformVariable { std::string name; @@ -84,11 +106,7 @@ namespace Nz private: std::shared_ptr m_pipelineLayout; - std::vector m_sharedUniformBlocks; - std::vector m_textures; - std::vector m_uniformBlocks; - DefaultShaders m_defaultShaders; - PredefinedBinding m_predefinedBinding; + Builder m_data; }; } diff --git a/include/Nazara/Graphics/MaterialSettings.inl b/include/Nazara/Graphics/MaterialSettings.inl index fd0356e04..e88d6845e 100644 --- a/include/Nazara/Graphics/MaterialSettings.inl +++ b/include/Nazara/Graphics/MaterialSettings.inl @@ -10,22 +10,18 @@ namespace Nz { inline MaterialSettings::MaterialSettings() : - MaterialSettings({}, {}, {}, { InvalidIndex }, {}) + MaterialSettings(Builder{}) { } - inline MaterialSettings::MaterialSettings(std::vector textures, std::vector uniformBlocks, std::vector sharedUniformBlocks, const PredefinedBinding& predefinedBindings, DefaultShaders defaultShaders) : - m_sharedUniformBlocks(std::move(sharedUniformBlocks)), - m_textures(std::move(textures)), - m_uniformBlocks(std::move(uniformBlocks)), - m_defaultShaders(std::move(defaultShaders)), - m_predefinedBinding(predefinedBindings) + inline MaterialSettings::MaterialSettings(Builder data) : + m_data(std::move(data)) { RenderPipelineLayoutInfo info; unsigned int bindingIndex = 0; - for (const Texture& textureInfo : m_textures) + for (const Texture& textureInfo : m_data.textures) { info.bindings.push_back({ //textureInfo.bindingPoint, @@ -35,7 +31,7 @@ namespace Nz }); } - for (const UniformBlock& ubo : m_uniformBlocks) + for (const UniformBlock& ubo : m_data.uniformBlocks) { info.bindings.push_back({ //ubo.bindingPoint, @@ -45,7 +41,7 @@ namespace Nz }); } - for (const SharedUniformBlock& ubo : m_sharedUniformBlocks) + for (const SharedUniformBlock& ubo : m_data.sharedUniformBlocks) { info.bindings.push_back({ //ubo.bindingPoint, @@ -58,19 +54,25 @@ namespace Nz m_pipelineLayout = Graphics::Instance()->GetRenderDevice().InstantiateRenderPipelineLayout(std::move(info)); } - inline const std::shared_ptr& MaterialSettings::GetDefaultShader(ShaderStageType stage) const + inline auto MaterialSettings::GetConditions() const -> const std::vector& { - return m_defaultShaders[UnderlyingCast(stage)]; + return m_data.conditions; } - inline auto MaterialSettings::GetDefaultShaders() const -> const DefaultShaders& + inline std::size_t MaterialSettings::GetConditionIndex(const std::string_view& name) const { - return m_defaultShaders; + for (std::size_t i = 0; i < m_data.conditions.size(); ++i) + { + if (m_data.conditions[i].name == name) + return i; + } + + return InvalidIndex; } inline std::size_t MaterialSettings::GetPredefinedBindingIndex(PredefinedShaderBinding binding) const { - return m_predefinedBinding[UnderlyingCast(binding)]; + return m_data.predefinedBinding[UnderlyingCast(binding)]; } inline const std::shared_ptr& MaterialSettings::GetRenderPipelineLayout() const @@ -78,32 +80,37 @@ namespace Nz return m_pipelineLayout; } + inline const std::shared_ptr& MaterialSettings::GetShader(ShaderStageType stage) const + { + return m_data.shaders[UnderlyingCast(stage)]; + } + + inline auto MaterialSettings::GetShaders() const -> const Shaders& + { + return m_data.shaders; + } + inline auto MaterialSettings::GetSharedUniformBlocks() const -> const std::vector& { - return m_sharedUniformBlocks; + return m_data.sharedUniformBlocks; } inline std::size_t MaterialSettings::GetSharedUniformBlockIndex(const std::string_view& name) const { - for (std::size_t i = 0; i < m_sharedUniformBlocks.size(); ++i) + for (std::size_t i = 0; i < m_data.sharedUniformBlocks.size(); ++i) { - if (m_sharedUniformBlocks[i].name == name) + if (m_data.sharedUniformBlocks[i].name == name) return i; } return InvalidIndex; } - inline auto MaterialSettings::GetTextures() const -> const std::vector& - { - return m_textures; - } - inline std::size_t MaterialSettings::GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const { - assert(uniformBlockIndex < m_sharedUniformBlocks.size()); + assert(uniformBlockIndex < m_data.sharedUniformBlocks.size()); - const std::vector& variables = m_sharedUniformBlocks[uniformBlockIndex].uniforms; + const std::vector& variables = m_data.sharedUniformBlocks[uniformBlockIndex].uniforms; for (std::size_t i = 0; i < variables.size(); ++i) { if (variables[i].name == name) @@ -113,11 +120,16 @@ namespace Nz return InvalidIndex; } + inline auto MaterialSettings::GetTextures() const -> const std::vector& + { + return m_data.textures; + } + inline std::size_t MaterialSettings::GetTextureIndex(const std::string_view& name) const { - for (std::size_t i = 0; i < m_textures.size(); ++i) + for (std::size_t i = 0; i < m_data.textures.size(); ++i) { - if (m_textures[i].name == name) + if (m_data.textures[i].name == name) return i; } @@ -126,14 +138,14 @@ namespace Nz inline auto MaterialSettings::GetUniformBlocks() const -> const std::vector& { - return m_uniformBlocks; + return m_data.uniformBlocks; } inline std::size_t MaterialSettings::GetUniformBlockIndex(const std::string_view& name) const { - for (std::size_t i = 0; i < m_uniformBlocks.size(); ++i) + for (std::size_t i = 0; i < m_data.uniformBlocks.size(); ++i) { - if (m_uniformBlocks[i].name == name) + if (m_data.uniformBlocks[i].name == name) return i; } @@ -142,9 +154,9 @@ namespace Nz inline std::size_t MaterialSettings::GetUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const { - assert(uniformBlockIndex < m_uniformBlocks.size()); + assert(uniformBlockIndex < m_data.uniformBlocks.size()); - const std::vector& variables = m_uniformBlocks[uniformBlockIndex].uniforms; + const std::vector& variables = m_data.uniformBlocks[uniformBlockIndex].uniforms; for (std::size_t i = 0; i < variables.size(); ++i) { if (variables[i].name == name) diff --git a/include/Nazara/Graphics/UberShader.hpp b/include/Nazara/Graphics/UberShader.hpp new file mode 100644 index 000000000..ae8221199 --- /dev/null +++ b/include/Nazara/Graphics/UberShader.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_UBER_SHADER_HPP +#define NAZARA_UBER_SHADER_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class ShaderStage; + + class NAZARA_GRAPHICS_API UberShader + { + public: + inline UberShader(ShaderAst shaderAst); + ~UberShader() = default; + + UInt64 GetConditionFlagByName(const std::string_view& condition) const; + + const std::shared_ptr& Get(UInt64 combination); + + private: + std::unordered_map> m_combinations; + ShaderAst m_shaderAst; + UInt64 m_combinationMask; + }; +} + +#include + +#endif // NAZARA_UBER_SHADER_HPP diff --git a/include/Nazara/Graphics/UberShader.inl b/include/Nazara/Graphics/UberShader.inl new file mode 100644 index 000000000..376dc23cb --- /dev/null +++ b/include/Nazara/Graphics/UberShader.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index 8398c8e61..610438e10 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -36,6 +36,7 @@ namespace Nz std::shared_ptr InstantiateCommandPool(QueueType queueType) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; + std::shared_ptr InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states) override; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp index 30ceb101c..0340f43ae 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp @@ -52,9 +52,11 @@ namespace Nz void Release(ShaderBinding& binding); inline void TryToShrink(); + static constexpr UInt32 InvalidIndex = 0xFFFFFFFF; + struct TextureDescriptor { - UInt32 bindingIndex; + UInt32 bindingIndex = InvalidIndex; GLuint texture; GLuint sampler; GL::TextureTarget textureTarget; @@ -62,7 +64,7 @@ namespace Nz struct UniformBufferDescriptor { - UInt32 bindingIndex; + UInt32 bindingIndex = InvalidIndex; GLuint buffer; GLintptr offset; GLsizeiptr size; diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderStage.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderStage.hpp index 0463f5b49..d917f6f14 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderStage.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderStage.hpp @@ -12,13 +12,17 @@ #include #include #include +#include #include namespace Nz { + class ShaderAst; + class NAZARA_OPENGLRENDERER_API OpenGLShaderStage : public ShaderStage { public: + OpenGLShaderStage(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states); OpenGLShaderStage(OpenGLDevice& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize); OpenGLShaderStage(const OpenGLShaderStage&) = delete; OpenGLShaderStage(OpenGLShaderStage&&) noexcept = default; @@ -30,6 +34,9 @@ namespace Nz OpenGLShaderStage& operator=(OpenGLShaderStage&&) noexcept = default; private: + void CheckCompilationStatus(); + void Create(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states); + GL::Shader m_shader; }; } diff --git a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl index d6ca768d4..f21ceb1e2 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl @@ -9,7 +9,8 @@ namespace Nz::GL { inline GL::WGLContext::WGLContext(const OpenGLDevice* device, const WGLLoader& loader) : Context(device), - m_loader(loader) + m_loader(loader), + m_handle(nullptr) { } diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index fb26dc58f..5f658ef69 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ namespace Nz { class CommandPool; + class ShaderAst; class ShaderStage; class NAZARA_RENDERER_API RenderDevice @@ -33,6 +35,7 @@ namespace Nz virtual std::shared_ptr InstantiateCommandPool(QueueType queueType) = 0; virtual std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) = 0; virtual std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) = 0; + virtual std::shared_ptr InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states) = 0; virtual std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) = 0; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const std::filesystem::path& sourcePath); virtual std::shared_ptr InstantiateTexture(const TextureInfo& params) = 0; diff --git a/include/Nazara/Shader/ShaderAst.hpp b/include/Nazara/Shader/ShaderAst.hpp index 5b80a1380..54199a179 100644 --- a/include/Nazara/Shader/ShaderAst.hpp +++ b/include/Nazara/Shader/ShaderAst.hpp @@ -41,6 +41,8 @@ namespace Nz void AddStruct(std::string name, std::vector members); void AddUniform(std::string name, ShaderExpressionType type, std::optional bindingIndex = {}, std::optional memoryLayout = {}); + inline std::size_t FindConditionByName(const std::string_view& conditionName) const; + inline const Condition& GetCondition(std::size_t i) const; inline std::size_t GetConditionCount() const; inline const std::vector& GetConditions() const; @@ -110,6 +112,8 @@ namespace Nz ShaderExpressionType type; }; + static constexpr std::size_t InvalidCondition = std::numeric_limits::max(); + private: std::vector m_conditions; std::vector m_functions; diff --git a/include/Nazara/Shader/ShaderAst.inl b/include/Nazara/Shader/ShaderAst.inl index f00517db5..64bce4583 100644 --- a/include/Nazara/Shader/ShaderAst.inl +++ b/include/Nazara/Shader/ShaderAst.inl @@ -12,6 +12,17 @@ namespace Nz { } + inline std::size_t ShaderAst::FindConditionByName(const std::string_view& conditionName) const + { + for (std::size_t i = 0; i < m_conditions.size(); ++i) + { + if (m_conditions[i].name == conditionName) + return i; + } + + return InvalidCondition; + } + inline auto Nz::ShaderAst::GetCondition(std::size_t i) const -> const Condition& { assert(i < m_functions.size()); diff --git a/include/Nazara/Shader/ShaderAstSerializer.hpp b/include/Nazara/Shader/ShaderAstSerializer.hpp index 5155a702e..e6e50487a 100644 --- a/include/Nazara/Shader/ShaderAstSerializer.hpp +++ b/include/Nazara/Shader/ShaderAstSerializer.hpp @@ -141,6 +141,7 @@ namespace Nz }; NAZARA_SHADER_API ByteArray SerializeShader(const ShaderAst& shader); + inline ShaderAst UnserializeShader(const void* data, std::size_t size); NAZARA_SHADER_API ShaderAst UnserializeShader(ByteStream& stream); } diff --git a/include/Nazara/Shader/ShaderAstSerializer.inl b/include/Nazara/Shader/ShaderAstSerializer.inl index 31882eeb7..6b61c1197 100644 --- a/include/Nazara/Shader/ShaderAstSerializer.inl +++ b/include/Nazara/Shader/ShaderAstSerializer.inl @@ -128,6 +128,12 @@ namespace Nz m_stream(stream) { } + + inline ShaderAst UnserializeShader(const void* data, std::size_t size) + { + ByteStream byteStream(data, size); + return UnserializeShader(byteStream); + } } #include diff --git a/include/Nazara/Shader/ShaderBuilder.hpp b/include/Nazara/Shader/ShaderBuilder.hpp index d7c982eee..03ac51ac2 100644 --- a/include/Nazara/Shader/ShaderBuilder.hpp +++ b/include/Nazara/Shader/ShaderBuilder.hpp @@ -56,7 +56,12 @@ namespace Nz::ShaderBuilder constexpr GenBuilder DeclareVariable; constexpr GenBuilder Discard; constexpr BinOpBuilder Divide; - constexpr BinOpBuilder Equal; + constexpr BinOpBuilder Equal; + constexpr BinOpBuilder GreaterThan; + constexpr BinOpBuilder GreaterThanOrEqual; + constexpr BinOpBuilder LessThan; + constexpr BinOpBuilder LessThanOrEqual; + constexpr BinOpBuilder NotEqual; constexpr GenBuilder ExprStatement; constexpr GenBuilder Identifier; constexpr GenBuilder IntrinsicCall; diff --git a/include/Nazara/Shader/ShaderEnums.hpp b/include/Nazara/Shader/ShaderEnums.hpp index 596292d9d..ccb2f78d5 100644 --- a/include/Nazara/Shader/ShaderEnums.hpp +++ b/include/Nazara/Shader/ShaderEnums.hpp @@ -33,7 +33,7 @@ namespace Nz::ShaderNodes UInt1, //< uint UInt2, //< uvec2 UInt3, //< uvec3 - UInt4 //< uvec4 + UInt4 //< uvec4 }; enum class BinaryType @@ -43,7 +43,12 @@ namespace Nz::ShaderNodes Multiply, //< * Divide, //< / - Equality //< == + CompEq, //< == + CompGe, //< >= + CompGt, //< > + CompLe, //< <= + CompLt, //< < + CompNe //< <= }; enum class BuiltinEntry @@ -87,7 +92,9 @@ namespace Nz::ShaderNodes IntrinsicCall, Sample2D, SwizzleOp, - StatementBlock + StatementBlock, + + Max = StatementBlock }; enum class SsaInstruction diff --git a/include/Nazara/Shader/ShaderNodes.inl b/include/Nazara/Shader/ShaderNodes.inl index fe0872a9d..f3fbecaff 100644 --- a/include/Nazara/Shader/ShaderNodes.inl +++ b/include/Nazara/Shader/ShaderNodes.inl @@ -149,7 +149,7 @@ namespace Nz::ShaderNodes inline Discard::Discard() : - Statement(NodeType::DeclareVariable) + Statement(NodeType::Discard) { } diff --git a/include/Nazara/Shader/ShaderWriter.hpp b/include/Nazara/Shader/ShaderWriter.hpp index 3fd25d7ff..337c3c3cf 100644 --- a/include/Nazara/Shader/ShaderWriter.hpp +++ b/include/Nazara/Shader/ShaderWriter.hpp @@ -28,7 +28,7 @@ namespace Nz struct States { - std::unordered_set enabledConditions; + Nz::UInt64 enabledConditions = 0; }; }; } diff --git a/include/Nazara/Shader/SpirvWriter.inl b/include/Nazara/Shader/SpirvWriter.inl index 44babe815..ed518e5f4 100644 --- a/include/Nazara/Shader/SpirvWriter.inl +++ b/include/Nazara/Shader/SpirvWriter.inl @@ -3,13 +3,17 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include namespace Nz { inline bool SpirvWriter::IsConditionEnabled(const std::string& condition) const { - return m_context.states->enabledConditions.find(condition) != m_context.states->enabledConditions.end(); + std::size_t conditionIndex = m_context.shader->FindConditionByName(condition); + assert(conditionIndex != ShaderAst::InvalidCondition); + + return TestBit(m_context.states->enabledConditions, conditionIndex); } } diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index 873c7dc9c..4d47de3bf 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -27,6 +27,7 @@ namespace Nz std::shared_ptr InstantiateCommandPool(QueueType queueType) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; + std::shared_ptr InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states) override; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; diff --git a/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp b/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp index 059183853..a82ed6894 100644 --- a/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp +++ b/include/Nazara/VulkanRenderer/VulkanShaderStage.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ namespace Nz VulkanShaderStage(VulkanShaderStage&&) = delete; ~VulkanShaderStage() = default; + bool Create(Vk::Device& device, const ShaderAst& shader, const ShaderWriter::States& states); bool Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize); inline const Vk::ShaderModule& GetHandle() const; diff --git a/shaders/BasicMaterial/basicmaterial.frag.shaderflow b/shaders/BasicMaterial/basicmaterial.frag.shaderflow index 4ffa6960d..ead0fa7f7 100644 --- a/shaders/BasicMaterial/basicmaterial.frag.shaderflow +++ b/shaders/BasicMaterial/basicmaterial.frag.shaderflow @@ -19,35 +19,52 @@ "type": "UniformBufferObject" } ], + "conditions": [ + { + "name": "HAS_DIFFUSE_TEXTURE" + }, + { + "name": "HAS_ALPHA_TEXTURE" + }, + { + "name": "ALPHA_TEST" + } + ], "connections": [ { - "in_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", + "in_id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", + "in_index": 3, + "out_id": "{93fdbb4c-bc81-4100-89a9-b465793099b9}", + "out_index": 0 + }, + { + "in_id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", "in_index": 0, - "out_id": "{ac98a68f-0160-4189-af31-b8278e7c119c}", + "out_id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", "out_index": 0 }, { - "in_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", - "in_index": 1, - "out_id": "{db10f064-504d-4072-a49e-51a061b2efbe}", - "out_index": 0 - }, - { - "in_id": "{cf0ae20a-88cd-4788-9ed7-eaf014d8f971}", + "in_id": "{92d95fe0-84f6-4d27-91ea-992d5f73c04e}", "in_index": 0, - "out_id": "{c41cd67b-2f34-4ec4-acc6-2f7285e7c6e3}", + "out_id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", "out_index": 0 }, { - "in_id": "{cf0ae20a-88cd-4788-9ed7-eaf014d8f971}", + "in_id": "{bed466d8-5ed0-4e8a-bba7-1c809cb4c3f7}", "in_index": 1, - "out_id": "{74d3ca95-ae1d-496d-88c1-ce6c7327012a}", + "out_id": "{07a43c79-67e2-46b1-87d4-e00d2da22820}", "out_index": 0 }, { - "in_id": "{f5a6874b-0559-4fd1-9836-27567f9696a4}", - "in_index": 1, - "out_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", + "in_id": "{93fdbb4c-bc81-4100-89a9-b465793099b9}", + "in_index": 0, + "out_id": "{6fcfbcd0-c2df-41dd-bb50-74b455b9021f}", + "out_index": 0 + }, + { + "in_id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", + "in_index": 0, + "out_id": "{f5a6874b-0559-4fd1-9836-27567f9696a4}", "out_index": 0 }, { @@ -57,22 +74,136 @@ "out_index": 0 }, { - "in_id": "{bed466d8-5ed0-4e8a-bba7-1c809cb4c3f7}", + "in_id": "{92d95fe0-84f6-4d27-91ea-992d5f73c04e}", "in_index": 1, - "out_id": "{43df5c43-d6f4-440f-a049-4fde6e738883}", - "out_index": 0 - }, - { - "in_id": "{43df5c43-d6f4-440f-a049-4fde6e738883}", - "in_index": 0, - "out_id": "{07a43c79-67e2-46b1-87d4-e00d2da22820}", + "out_id": "{f5a6874b-0559-4fd1-9836-27567f9696a4}", "out_index": 0 }, { "in_id": "{be3547ff-0bf3-4701-9c27-c21e9d1322c3}", "in_index": 0, - "out_id": "{f5a6874b-0559-4fd1-9836-27567f9696a4}", + "out_id": "{92d95fe0-84f6-4d27-91ea-992d5f73c04e}", "out_index": 0 + }, + { + "in_id": "{e1f86d56-eb21-4267-9075-e6b0cc875a6d}", + "in_index": 0, + "out_id": "{bed466d8-5ed0-4e8a-bba7-1c809cb4c3f7}", + "out_index": 0 + }, + { + "in_id": "{93fdbb4c-bc81-4100-89a9-b465793099b9}", + "in_index": 1, + "out_id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", + "out_index": 3 + }, + { + "in_id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", + "in_index": 2, + "out_id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", + "out_index": 2 + }, + { + "in_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", + "in_index": 0, + "out_id": "{ac98a68f-0160-4189-af31-b8278e7c119c}", + "out_index": 0 + }, + { + "in_id": "{7750a050-b116-4e1b-bd89-b194c366d256}", + "in_index": 1, + "out_id": "{ca2c2ac5-39e0-4814-9432-fbf3e20d3cad}", + "out_index": 0 + }, + { + "in_id": "{3cdb5bb1-f572-4055-a1af-460b152b0c13}", + "in_index": 0, + "out_id": "{d7acd173-9188-43b5-bfa1-31f17dff44ad}", + "out_index": 0 + }, + { + "in_id": "{cf0ae20a-88cd-4788-9ed7-eaf014d8f971}", + "in_index": 0, + "out_id": "{c41cd67b-2f34-4ec4-acc6-2f7285e7c6e3}", + "out_index": 0 + }, + { + "in_id": "{e1f86d56-eb21-4267-9075-e6b0cc875a6d}", + "in_index": 1, + "out_id": "{07a43c79-67e2-46b1-87d4-e00d2da22820}", + "out_index": 0 + }, + { + "in_id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", + "in_index": 1, + "out_id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", + "out_index": 1 + }, + { + "in_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", + "in_index": 1, + "out_id": "{db10f064-504d-4072-a49e-51a061b2efbe}", + "out_index": 0 + }, + { + "in_id": "{d7acd173-9188-43b5-bfa1-31f17dff44ad}", + "in_index": 1, + "out_id": "{1f9d52d7-4f44-4d96-8edb-fbc1239a93bb}", + "out_index": 0 + }, + { + "in_id": "{bed466d8-5ed0-4e8a-bba7-1c809cb4c3f7}", + "in_index": 0, + "out_id": "{fbaddbbe-f9cd-4e8d-b7a8-40c10c96f580}", + "out_index": 0 + }, + { + "in_id": "{f5a6874b-0559-4fd1-9836-27567f9696a4}", + "in_index": 1, + "out_id": "{e1f86d56-eb21-4267-9075-e6b0cc875a6d}", + "out_index": 0 + }, + { + "in_id": "{cf0ae20a-88cd-4788-9ed7-eaf014d8f971}", + "in_index": 1, + "out_id": "{74d3ca95-ae1d-496d-88c1-ce6c7327012a}", + "out_index": 0 + }, + { + "in_id": "{bb071807-e65e-4c31-acf0-d296efa665fa}", + "in_index": 0, + "out_id": "{92d95fe0-84f6-4d27-91ea-992d5f73c04e}", + "out_index": 0 + }, + { + "in_id": "{7750a050-b116-4e1b-bd89-b194c366d256}", + "in_index": 0, + "out_id": "{f9ba0cce-3b85-4f95-a79e-a2f64d955d89}", + "out_index": 0 + }, + { + "in_id": "{6fcfbcd0-c2df-41dd-bb50-74b455b9021f}", + "in_index": 0, + "out_id": "{7750a050-b116-4e1b-bd89-b194c366d256}", + "out_index": 0 + }, + { + "in_id": "{d7acd173-9188-43b5-bfa1-31f17dff44ad}", + "in_index": 0, + "out_id": "{fc7542b2-5752-4891-98c1-35b498da257b}", + "out_index": 0 + }, + { + "in_id": "{fc7542b2-5752-4891-98c1-35b498da257b}", + "in_index": 1, + "out_id": "{743930bd-1d81-4d4c-b7ec-175a34838d69}", + "out_index": 0 + }, + { + "in_id": "{fc7542b2-5752-4891-98c1-35b498da257b}", + "in_index": 0, + "out_id": "{bb071807-e65e-4c31-acf0-d296efa665fa}", + "out_index": 3 } ], "inputs": [ @@ -103,8 +234,22 @@ "variable_name": "" }, "position": { - "x": 103, - "y": 173 + "x": 330.0833333333333, + "y": 236.19444444444446 + } + }, + { + "id": "{becdd0d4-2b28-44f5-86c2-2ed6b846326c}", + "model": { + "name": "vec_decompose", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 1022.5200000000003, + "y": -53.35999999999996 } }, { @@ -117,8 +262,8 @@ "variable_name": "lightFactor" }, "position": { - "x": 278, - "y": 212 + "x": 486.33333333333326, + "y": 285.61111111111114 } }, { @@ -136,8 +281,8 @@ "variable_name": "lightDir" }, "position": { - "x": 115, - "y": 267 + "x": 294.1666666666667, + "y": 358.6666666666667 } }, { @@ -150,8 +295,8 @@ "variable_name": "" }, "position": { - "x": 299, - "y": 488 + "x": 167.75, + "y": 473.97222222222194 } }, { @@ -165,8 +310,8 @@ "variable_name": "" }, "position": { - "x": 103, - "y": 470 + "x": -10.194444444444457, + "y": 445 } }, { @@ -180,8 +325,8 @@ "variable_name": "" }, "position": { - "x": 113, - "y": 579 + "x": -0.19444444444445708, + "y": 554.0000000000001 } }, { @@ -191,11 +336,25 @@ "preview_enabled": false, "preview_height": 64, "preview_width": 64, + "variable_name": "textureColor" + }, + "position": { + "x": 1055.9166666666665, + "y": 458.6388888888888 + } + }, + { + "id": "{93fdbb4c-bc81-4100-89a9-b465793099b9}", + "model": { + "name": "float_mul", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, "variable_name": "" }, "position": { - "x": 488, - "y": 376 + "x": 1051.286666666667, + "y": 195.20666666666665 } }, { @@ -209,8 +368,8 @@ "variable_name": "" }, "position": { - "x": 912, - "y": 489 + "x": 2598.5, + "y": 293.33333333333326 } }, { @@ -223,28 +382,8 @@ "variable_name": "" }, "position": { - "x": 707, - "y": 492 - } - }, - { - "id": "{43df5c43-d6f4-440f-a049-4fde6e738883}", - "model": { - "name": "cast_vec4", - "preview_enabled": false, - "preview_height": 64, - "preview_width": 64, - "value": [ - 1, - 0, - 0, - 0 - ], - "variable_name": "matDiffuse" - }, - "position": { - "x": 521, - "y": 616 + "x": 377.1388888888888, + "y": 507.83333333333337 } }, { @@ -259,8 +398,198 @@ "variable_name": "" }, "position": { - "x": 309, - "y": 637 + "x": 135.11111111111126, + "y": 643.5277777777775 + } + }, + { + "id": "{e1f86d56-eb21-4267-9075-e6b0cc875a6d}", + "model": { + "condition_name": "HAS_DIFFUSE_TEXTURE", + "name": "ConditionalExpression", + "preview_enabled": true, + "preview_height": 128, + "preview_width": 128, + "variable_name": "" + }, + "position": { + "x": 602.5, + "y": 566.5 + } + }, + { + "id": "{ca2c2ac5-39e0-4814-9432-fbf3e20d3cad}", + "model": { + "input": "vertUV", + "name": "Input", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 344.36666666666684, + "y": 53.300000000000026 + } + }, + { + "id": "{f9ba0cce-3b85-4f95-a79e-a2f64d955d89}", + "model": { + "name": "Texture", + "preview_enabled": true, + "preview_height": 64, + "preview_width": 64, + "texture": "MaterialAlphaMap", + "variable_name": "" + }, + "position": { + "x": 344.59999999999985, + "y": -35.10000000000005 + } + }, + { + "id": "{7750a050-b116-4e1b-bd89-b194c366d256}", + "model": { + "name": "SampleTexture", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 491.0333333333334, + "y": 18.366666666666674 + } + }, + { + "id": "{6fcfbcd0-c2df-41dd-bb50-74b455b9021f}", + "model": { + "name": "vec_decompose", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 682.0999999999998, + "y": 19.88000000000003 + } + }, + { + "id": "{359a78e1-df0d-467f-907e-7bff04a55db5}", + "model": { + "name": "vec_compose4", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 1240.62, + "y": -41.900000000000034 + } + }, + { + "id": "{bb071807-e65e-4c31-acf0-d296efa665fa}", + "model": { + "name": "vec_decompose", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 2270, + "y": 67 + } + }, + { + "id": "{92d95fe0-84f6-4d27-91ea-992d5f73c04e}", + "model": { + "condition_name": "HAS_ALPHA_TEXTURE", + "name": "ConditionalExpression", + "preview_enabled": true, + "preview_height": 128, + "preview_width": 128, + "variable_name": "" + }, + "position": { + "x": 1415.8933333333334, + "y": 135.44000000000005 + } + }, + { + "id": "{3cdb5bb1-f572-4055-a1af-460b152b0c13}", + "model": { + "name": "Discard", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 2694, + "y": 4 + } + }, + { + "id": "{d7acd173-9188-43b5-bfa1-31f17dff44ad}", + "model": { + "condition_name": "ALPHA_TEST", + "name": "ConditionalExpression", + "preview_enabled": true, + "preview_height": 128, + "preview_width": 128, + "variable_name": "" + }, + "position": { + "x": 2240, + "y": -174 + } + }, + { + "id": "{1f9d52d7-4f44-4d96-8edb-fbc1239a93bb}", + "model": { + "name": "bool_constant", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "value": false, + "variable_name": "" + }, + "position": { + "x": 2005, + "y": -99 + } + }, + { + "id": "{fc7542b2-5752-4891-98c1-35b498da257b}", + "model": { + "name": "float_lt", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 2000, + "y": -241 + } + }, + { + "id": "{743930bd-1d81-4d4c-b7ec-175a34838d69}", + "model": { + "buffer": "settings", + "field": "AlphaThreshold", + "name": "BufferField", + "preview_enabled": false, + "preview_height": 64, + "preview_width": 64, + "variable_name": "" + }, + "position": { + "x": 1675, + "y": -254 } } ], @@ -280,7 +609,7 @@ }, { "name": "DiffuseColor", - "type": "Float3" + "type": "Float4" } ], "name": "BasicSettings" diff --git a/src/Nazara/Core/Error.cpp b/src/Nazara/Core/Error.cpp index 348950bec..7f7b12232 100644 --- a/src/Nazara/Core/Error.cpp +++ b/src/Nazara/Core/Error.cpp @@ -88,7 +88,7 @@ namespace Nz #if defined(NAZARA_PLATFORM_WINDOWS) wchar_t* buffer = nullptr; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, + DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, 0, @@ -96,6 +96,9 @@ namespace Nz 0, nullptr); + if (length == 0) + return ""; + CallOnExit freeOnExit([buffer] { LocalFree(buffer); }); return FromWideString(buffer); #elif defined(NAZARA_PLATFORM_POSIX) diff --git a/src/Nazara/Graphics/BasicMaterial.cpp b/src/Nazara/Graphics/BasicMaterial.cpp index 59798a086..f92e61f7f 100644 --- a/src/Nazara/Graphics/BasicMaterial.cpp +++ b/src/Nazara/Graphics/BasicMaterial.cpp @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -33,12 +35,17 @@ namespace Nz const std::shared_ptr& materialSettings = material.GetSettings(); if (materialSettings == s_materialSettings) { + m_conditionIndexes = s_conditionIndexes; m_textureIndexes = s_textureIndexes; m_uniformBlockIndex = s_uniformBlockIndex; m_uniformOffsets = s_uniformOffsets; } else { + m_conditionIndexes.alphaTest = materialSettings->GetConditionIndex("AlphaTest"); + m_conditionIndexes.hasAlphaMap = materialSettings->GetConditionIndex("HasAlphaMap"); + m_conditionIndexes.hasDiffuseMap = materialSettings->GetConditionIndex("HasDiffuseMap"); + m_textureIndexes.alpha = materialSettings->GetTextureIndex("Alpha"); m_textureIndexes.diffuse = materialSettings->GetTextureIndex("Diffuse"); @@ -49,9 +56,9 @@ namespace Nz } } - float BasicMaterial::GetAlphaThreshold() const + float BasicMaterial::GetAlphaTestThreshold() const { - NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform"); + NazaraAssert(HasAlphaTestThreshold(), "Material has no alpha threshold uniform"); BufferMapper mapper(m_material.GetUniformBuffer(m_uniformBlockIndex), BufferAccess_ReadOnly); return AccessByOffset(mapper.GetPointer(), m_uniformOffsets.alphaThreshold); @@ -67,9 +74,9 @@ namespace Nz return Color(colorPtr[0] * 255, colorPtr[1] * 255, colorPtr[2] * 255, colorPtr[3] * 255); //< TODO: Make color able to use float } - void BasicMaterial::SetAlphaThreshold(float alphaThreshold) + void BasicMaterial::SetAlphaTestThreshold(float alphaThreshold) { - NazaraAssert(HasAlphaThreshold(), "Material has no alpha threshold uniform"); + NazaraAssert(HasAlphaTestThreshold(), "Material has no alpha threshold uniform"); BufferMapper mapper(m_material.GetUniformBuffer(m_uniformBlockIndex), BufferAccess_WriteOnly); AccessByOffset(mapper.GetPointer(), m_uniformOffsets.alphaThreshold) = alphaThreshold; @@ -87,20 +94,16 @@ namespace Nz colorPtr[3] = diffuse.a / 255.f; } - const std::shared_ptr& BasicMaterial::GetSettings() - { - return s_materialSettings; - } - bool BasicMaterial::Initialize() { FieldOffsets fieldOffsets(StructLayout_Std140); - s_uniformOffsets.diffuseColor = fieldOffsets.AddField(StructFieldType_Float4); s_uniformOffsets.alphaThreshold = fieldOffsets.AddField(StructFieldType_Float1); + s_uniformOffsets.diffuseColor = fieldOffsets.AddField(StructFieldType_Float4); + s_uniformOffsets.totalSize = fieldOffsets.GetSize(); - MaterialSettings::PredefinedBinding predefinedBinding; - predefinedBinding.fill(MaterialSettings::InvalidIndex); + MaterialSettings::Builder settings; + settings.predefinedBinding.fill(MaterialSettings::InvalidIndex); std::vector variables; variables.assign({ @@ -120,31 +123,29 @@ namespace Nz AccessByOffset(defaultValues.data(), s_uniformOffsets.diffuseColor) = Vector4f(1.f, 1.f, 1.f, 1.f); AccessByOffset(defaultValues.data(), s_uniformOffsets.alphaThreshold) = 0.2f; - std::vector textures; - s_textureIndexes.alpha = textures.size(); - textures.push_back({ + s_textureIndexes.alpha = settings.textures.size(); + settings.textures.push_back({ "MaterialAlphaMap", "Alpha", ImageType_2D }); - - s_textureIndexes.diffuse = textures.size(); - textures.push_back({ + + s_textureIndexes.diffuse = settings.textures.size(); + settings.textures.push_back({ "MaterialDiffuseMap", "Diffuse", ImageType_2D }); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::TexOverlay)] = textures.size(); - textures.push_back({ + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::TexOverlay)] = settings.textures.size(); + settings.textures.push_back({ "TextureOverlay", "Overlay", ImageType_2D }); - std::vector uniformBlocks; - s_uniformBlockIndex = uniformBlocks.size(); - uniformBlocks.assign({ + s_uniformBlockIndex = settings.uniformBlocks.size(); + settings.uniformBlocks.assign({ { fieldOffsets.GetSize(), "BasicSettings", @@ -154,19 +155,61 @@ namespace Nz } }); - std::vector sharedUniformBlock; + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboInstanceData)] = settings.textures.size() + settings.uniformBlocks.size() + settings.sharedUniformBlocks.size(); + settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock()); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboInstanceData)] = textures.size() + uniformBlocks.size() + sharedUniformBlock.size(); - sharedUniformBlock.push_back(PredefinedInstanceData::GetUniformBlock()); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboViewerData)] = textures.size() + uniformBlocks.size() + sharedUniformBlock.size(); - sharedUniformBlock.push_back(PredefinedViewerData::GetUniformBlock()); + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboViewerData)] = settings.textures.size() + settings.uniformBlocks.size() + settings.sharedUniformBlocks.size(); + settings.sharedUniformBlocks.push_back(PredefinedViewerData::GetUniformBlock()); // Shaders - MaterialSettings::DefaultShaders defaultShaders; - defaultShaders[UnderlyingCast(ShaderStageType::Fragment)] = Graphics::Instance()->GetRenderDevice().InstantiateShaderStage(Nz::ShaderStageType::Fragment, Nz::ShaderLanguage::NazaraBinary, r_fragmentShader, sizeof(r_fragmentShader)); - defaultShaders[UnderlyingCast(ShaderStageType::Vertex)] = Graphics::Instance()->GetRenderDevice().InstantiateShaderStage(Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraBinary, r_vertexShader, sizeof(r_vertexShader)); + auto& fragmentShader = settings.shaders[UnderlyingCast(ShaderStageType::Fragment)]; + auto& vertexShader = settings.shaders[UnderlyingCast(ShaderStageType::Vertex)]; - s_materialSettings = std::make_shared(std::move(textures), std::move(uniformBlocks), std::move(sharedUniformBlock), predefinedBinding, std::move(defaultShaders)); + fragmentShader = std::make_shared(UnserializeShader(r_fragmentShader, sizeof(r_fragmentShader))); + vertexShader = std::make_shared(UnserializeShader(r_vertexShader, sizeof(r_vertexShader))); + + // Conditions + + // HasDiffuseMap + { + std::array shaderConditions; + shaderConditions.fill(0); + shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetConditionFlagByName("HAS_DIFFUSE_TEXTURE"); + + s_conditionIndexes.hasDiffuseMap = settings.conditions.size(); + settings.conditions.push_back({ + "HasDiffuseMap", + shaderConditions + }); + } + + // HasAlphaMap + { + std::array shaderConditions; + shaderConditions.fill(0); + shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetConditionFlagByName("HAS_ALPHA_TEXTURE"); + + s_conditionIndexes.hasAlphaMap = settings.conditions.size(); + settings.conditions.push_back({ + "HasAlphaMap", + shaderConditions + }); + } + + // AlphaTest + { + std::array shaderConditions; + shaderConditions.fill(0); + shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetConditionFlagByName("ALPHA_TEST"); + + s_conditionIndexes.alphaTest = settings.conditions.size(); + settings.conditions.push_back({ + "AlphaTest", + shaderConditions + }); + } + + s_materialSettings = std::make_shared(std::move(settings)); return true; } @@ -178,6 +221,7 @@ namespace Nz std::shared_ptr BasicMaterial::s_materialSettings; std::size_t BasicMaterial::s_uniformBlockIndex; + BasicMaterial::ConditionIndexes BasicMaterial::s_conditionIndexes; BasicMaterial::TextureIndexes BasicMaterial::s_textureIndexes; BasicMaterial::UniformOffsets BasicMaterial::s_uniformOffsets; } diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index 952402cb5..7a51c9ba2 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -24,11 +24,15 @@ namespace Nz */ Material::Material(std::shared_ptr settings) : m_settings(std::move(settings)), + m_enabledConditions(0), m_pipelineUpdated(false), m_shadowCastingEnabled(true) { m_pipelineInfo.settings = m_settings; - m_pipelineInfo.shaders = m_settings->GetDefaultShaders(); + + const auto& shaders = m_settings->GetShaders(); + for (std::size_t i = 0; i < ShaderStageTypeCount; ++i) + m_pipelineInfo.shaders[i].uberShader = shaders[i]; m_textures.resize(m_settings->GetTextures().size()); diff --git a/src/Nazara/Graphics/MaterialPipeline.cpp b/src/Nazara/Graphics/MaterialPipeline.cpp index 5996f64b0..938563bee 100644 --- a/src/Nazara/Graphics/MaterialPipeline.cpp +++ b/src/Nazara/Graphics/MaterialPipeline.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Nz @@ -47,10 +48,10 @@ namespace Nz renderPipelineInfo.pipelineLayout = m_pipelineInfo.settings->GetRenderPipelineLayout(); - for (const auto& shaderStage : m_pipelineInfo.shaders) + for (const auto& shaderEntry : m_pipelineInfo.shaders) { - if (shaderStage) - renderPipelineInfo.shaderStages.push_back(shaderStage); + if (shaderEntry.uberShader) + renderPipelineInfo.shaderStages.push_back(shaderEntry.uberShader->Get(shaderEntry.enabledConditions)); } renderPipelineInfo.vertexBuffers = vertexBuffers; diff --git a/src/Nazara/Graphics/PhongLightingMaterial.cpp b/src/Nazara/Graphics/PhongLightingMaterial.cpp index 903c34d97..d8258c55a 100644 --- a/src/Nazara/Graphics/PhongLightingMaterial.cpp +++ b/src/Nazara/Graphics/PhongLightingMaterial.cpp @@ -151,8 +151,8 @@ namespace Nz s_phongUniformOffsets.diffuseColor = phongUniformStruct.AddField(StructFieldType_Float4); s_phongUniformOffsets.specularColor = phongUniformStruct.AddField(StructFieldType_Float4); - MaterialSettings::PredefinedBinding predefinedBinding; - predefinedBinding.fill(MaterialSettings::InvalidIndex); + MaterialSettings::Builder settings; + settings.predefinedBinding.fill(MaterialSettings::InvalidIndex); std::vector phongVariables; phongVariables.assign({ @@ -187,10 +187,8 @@ namespace Nz AccessByOffset(defaultValues.data(), s_phongUniformOffsets.alphaThreshold) = 0.2f; AccessByOffset(defaultValues.data(), s_phongUniformOffsets.shininess) = 50.f; - std::vector uniformBlocks; - - s_phongUniformBlockIndex = uniformBlocks.size(); - uniformBlocks.push_back({ + s_phongUniformBlockIndex = settings.uniformBlocks.size(); + settings.uniformBlocks.push_back({ phongUniformStruct.GetSize(), "PhongSettings", "MaterialPhongSettings", @@ -198,65 +196,63 @@ namespace Nz std::move(defaultValues) }); - std::vector sharedUniformBlock; - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboInstanceData)] = sharedUniformBlock.size(); - sharedUniformBlock.push_back(PredefinedInstanceData::GetUniformBlock()); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboLighData)] = sharedUniformBlock.size(); - sharedUniformBlock.push_back(PredefinedLightData::GetUniformBlock()); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboViewerData)] = sharedUniformBlock.size(); - sharedUniformBlock.push_back(PredefinedViewerData::GetUniformBlock()); + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboInstanceData)] = settings.sharedUniformBlocks.size(); + settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock()); + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboLighData)] = settings.sharedUniformBlocks.size(); + settings.sharedUniformBlocks.push_back(PredefinedLightData::GetUniformBlock()); + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::UboViewerData)] = settings.sharedUniformBlocks.size(); + settings.sharedUniformBlocks.push_back(PredefinedViewerData::GetUniformBlock()); - std::vector textures; - s_textureIndexes.alpha = textures.size(); - textures.push_back({ + s_textureIndexes.alpha = settings.textures.size(); + settings.textures.push_back({ "MaterialAlphaMap", "Alpha", ImageType_2D }); - s_textureIndexes.diffuse = textures.size(); - textures.push_back({ + s_textureIndexes.diffuse = settings.textures.size(); + settings.textures.push_back({ "MaterialDiffuseMap", "Diffuse", ImageType_2D }); - s_textureIndexes.emissive = textures.size(); - textures.push_back({ + s_textureIndexes.emissive = settings.textures.size(); + settings.textures.push_back({ "MaterialEmissiveMap", "Emissive", ImageType_2D }); - s_textureIndexes.height = textures.size(); - textures.push_back({ + s_textureIndexes.height = settings.textures.size(); + settings.textures.push_back({ "MaterialHeightMap", "Height", ImageType_2D }); - s_textureIndexes.normal = textures.size(); - textures.push_back({ + s_textureIndexes.normal = settings.textures.size(); + settings.textures.push_back({ "MaterialNormalMap", "Normal", ImageType_2D }); - s_textureIndexes.specular = textures.size(); - textures.push_back({ + s_textureIndexes.specular = settings.textures.size(); + settings.textures.push_back({ "MaterialSpecularMap", "Specular", ImageType_2D }); - predefinedBinding[UnderlyingCast(PredefinedShaderBinding::TexOverlay)] = textures.size(); - textures.push_back({ + settings.predefinedBinding[UnderlyingCast(PredefinedShaderBinding::TexOverlay)] = settings.textures.size(); + settings.textures.push_back({ "TextureOverlay", "Overlay", ImageType_2D, }); - s_materialSettings = std::make_shared(std::move(textures), std::move(uniformBlocks), std::move(sharedUniformBlock), predefinedBinding, MaterialSettings::DefaultShaders{}); + s_materialSettings = std::make_shared(std::move(settings)); return true; } diff --git a/src/Nazara/Graphics/Resources/Shaders/basicmaterial.frag.shader b/src/Nazara/Graphics/Resources/Shaders/basicmaterial.frag.shader index de1e590c453324d531d93dd1a59942a62bb8cccd..70cdc4d2b1d52a3468c1d90ddbec8d2df18d90ec 100644 GIT binary patch literal 1745 zcmbVMU2obj6wL~)Xb2e{e`F7Puh;-lX>Ab+-QFq-T=0}8$Z_e%pX;CMYt`OkY-X>a zL%q@=`1ss=eXnnFmJG*15PTxQYJ|?Pn_Tuz27^h`zfAkT)5*9`^bzRp&(W~!kf9}# z)OfePvr%}$w;;*OTVAt4s|>epL3yb%1BOXCb-dnt=Qe5yo|`w==6VYFp0Tu~Tb z-Xb}I`)mv$qWJWY5x?DOOPm52w9f>0!mGv2b-^x3eVX6#v4u2)z`0?K-F@FF7u0Q-QO z?MHpZx zBo?KomKXp97+8Q9q#b4ph~@?vHF*y+KPxwo$2s{kvl1icv4FEYDO_P`e V%!ORJi9q+IrIi+^0*z&00RW*5Cj6mo$S`sEkpCgy+?gUkb&0kV!WGY=wB4CG+iG+B~ahmm`74YMvMNFD@0 YJX2PXe%8s-Op24QGfMzfd|*}s03Zn&1poj5 delta 181 zcmbQmF^z-AFW4i9fq{V$geMBPFfwlpHDMCv1#+2zm;)*Wq!>6S&tnqP;Dm~BW#)yX zRygP97o~tCnSdBo@f{{9b(mt#%sjvRqTIxsVvt%)gJqd@7&#|5FzbRefB}eS$_nDL SPL^j +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + UberShader::UberShader(ShaderAst shaderAst) : + m_shaderAst(std::move(shaderAst)) + { + std::size_t conditionCount = m_shaderAst.GetConditionCount(); + + if (conditionCount >= 64) + throw std::runtime_error("Too many conditions"); + + m_combinationMask = std::numeric_limits::max(); + m_combinationMask <<= conditionCount; + m_combinationMask = ~m_combinationMask; + } + + UInt64 UberShader::GetConditionFlagByName(const std::string_view& condition) const + { + std::size_t conditionIndex = m_shaderAst.FindConditionByName(condition); + if (conditionIndex != ShaderAst::InvalidCondition) + return SetBit(0, conditionIndex); + else + return 0; + } + + const std::shared_ptr& UberShader::Get(UInt64 combination) + { + combination &= m_combinationMask; + + auto it = m_combinations.find(combination); + if (it == m_combinations.end()) + { + ShaderWriter::States states; + states.enabledConditions = combination; + + std::shared_ptr stage = Graphics::Instance()->GetRenderDevice().InstantiateShaderStage(m_shaderAst, std::move(states)); + + it = m_combinations.emplace(combination, std::move(stage)).first; + } + + return it->second; + } +} diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index f23eba582..b5ccbd0ed 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -93,6 +93,7 @@ namespace Nz command.framebuffer->Activate(); context = GL::Context::GetCurrentContext(); + context->glClearColor(0.5, 0.5, 0.5, 1.0); context->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index 3085a7955..5d2ffa399 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -68,6 +68,11 @@ namespace Nz return std::make_shared(std::move(pipelineLayoutInfo)); } + std::shared_ptr OpenGLDevice::InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states) + { + return std::make_shared(*this, shaderAst, states); + } + std::shared_ptr OpenGLDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) { return std::make_shared(*this, type, lang, source, sourceSize); diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp index a236d81c7..adca247e9 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp @@ -20,6 +20,8 @@ namespace Nz const auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, i); UInt32 textureIndex = textureDescriptor.bindingIndex; + if (textureIndex == OpenGLRenderPipelineLayout::InvalidIndex) + continue; context.BindSampler(textureIndex, textureDescriptor.sampler); context.BindTexture(textureIndex, textureDescriptor.textureTarget, textureDescriptor.texture); @@ -29,6 +31,10 @@ namespace Nz { const auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, i); + UInt32 uboIndex = uboDescriptor.bindingIndex; + if (uboIndex == OpenGLRenderPipelineLayout::InvalidIndex) + continue; + context.BindUniformBuffer(uboDescriptor.bindingIndex, uboDescriptor.buffer, uboDescriptor.offset, uboDescriptor.size); } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderStage.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderStage.cpp index 068ef9634..dd7a69e5b 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderStage.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderStage.cpp @@ -14,6 +14,15 @@ namespace Nz { + OpenGLShaderStage::OpenGLShaderStage(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states) + { + if (!m_shader.Create(device, ToOpenGL(shaderAst.GetStage()))) + throw std::runtime_error("failed to create shader"); //< TODO: Handle error message + + Create(device, shaderAst, states); + CheckCompilationStatus(); + } + OpenGLShaderStage::OpenGLShaderStage(OpenGLDevice& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) { if (!m_shader.Create(device, ToOpenGL(type))) @@ -28,32 +37,11 @@ namespace Nz case ShaderLanguage::NazaraBinary: { - ByteStream byteStream(source, sourceSize); - auto shader = Nz::UnserializeShader(byteStream); - + auto shader = UnserializeShader(source, sourceSize); if (shader.GetStage() != type) throw std::runtime_error("incompatible shader stage"); - const auto& context = device.GetReferenceContext(); - const auto& contextParams = context.GetParams(); - - GlslWriter::Environment env; - env.glES = (contextParams.type == GL::ContextType::OpenGL_ES); - env.glMajorVersion = contextParams.glMajorVersion; - env.glMinorVersion = contextParams.glMinorVersion; - env.extCallback = [&](const std::string_view& ext) - { - return context.IsExtensionSupported(std::string(ext)); - }; - env.flipYPosition = true; - - GlslWriter writer; - writer.SetEnv(env); - - std::string code = writer.Generate(shader); - - m_shader.SetSource(code.data(), code.size()); - m_shader.Compile(); + Create(device, shader, {}); break; } @@ -71,8 +59,39 @@ namespace Nz throw std::runtime_error("Unsupported shader language"); } + CheckCompilationStatus(); + } + + void OpenGLShaderStage::CheckCompilationStatus() + { std::string errorLog; if (!m_shader.GetCompilationStatus(&errorLog)) throw std::runtime_error("Failed to compile shader: " + errorLog); } + + void OpenGLShaderStage::Create(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states) + { + const auto& context = device.GetReferenceContext(); + const auto& contextParams = context.GetParams(); + + GlslWriter::Environment env; + env.glES = (contextParams.type == GL::ContextType::OpenGL_ES); + env.glMajorVersion = contextParams.glMajorVersion; + env.glMinorVersion = contextParams.glMinorVersion; + env.extCallback = [&](const std::string_view& ext) + { + return context.IsExtensionSupported(std::string(ext)); + }; + env.flipYPosition = true; + + GlslWriter writer; + writer.SetEnv(env); + + std::string code = writer.Generate(shaderAst, states); + + NazaraError(code); + + m_shader.SetSource(code.data(), code.size()); + m_shader.Compile(); + } } diff --git a/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp b/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp index ffa9339e3..052c0f63a 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp @@ -174,7 +174,7 @@ namespace Nz::GL WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB }; - m_handle = baseContext->wglCreateContextAttribsARB(m_deviceContext, nullptr, attributes.data()); + m_handle = baseContext->wglCreateContextAttribsARB(m_deviceContext, (shareContext) ? shareContext->m_handle : nullptr, attributes.data()); if (m_handle) { m_params.type = ContextType::OpenGL; @@ -198,16 +198,16 @@ namespace Nz::GL return false; } - m_params.type = ContextType::OpenGL; - } - - if (shareContext) - { - if (!m_loader.wglShareLists(shareContext->m_handle, m_handle)) + if (shareContext) { - NazaraError("failed to share context objects: " + Error::GetLastSystemError()); - return false; + if (!m_loader.wglShareLists(shareContext->m_handle, m_handle)) + { + NazaraError("failed to share context objects: " + Error::GetLastSystemError()); + return false; + } } + + m_params.type = ContextType::OpenGL; } LoadWGLExt(); diff --git a/src/Nazara/Physics2D/Arbiter2D.cpp b/src/Nazara/Physics2D/Arbiter2D.cpp index 9da160216..2cb35ff4f 100644 --- a/src/Nazara/Physics2D/Arbiter2D.cpp +++ b/src/Nazara/Physics2D/Arbiter2D.cpp @@ -39,7 +39,7 @@ namespace Nz float Arbiter2D::GetContactDepth(std::size_t i) const { - return cpArbiterGetDepth(m_arbiter, int(i)); + return float(cpArbiterGetDepth(m_arbiter, int(i))); } Nz::Vector2f Arbiter2D::GetContactPointA(std::size_t i) const diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index 649f25072..0dfe2476c 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -418,21 +419,17 @@ namespace Nz switch (node.op) { - case ShaderNodes::BinaryType::Add: - Append(" + "); - break; - case ShaderNodes::BinaryType::Substract: - Append(" - "); - break; - case ShaderNodes::BinaryType::Multiply: - Append(" * "); - break; - case ShaderNodes::BinaryType::Divide: - Append(" / "); - break; - case ShaderNodes::BinaryType::Equality: - Append(" == "); - break; + case ShaderNodes::BinaryType::Add: Append(" + "); break; + case ShaderNodes::BinaryType::Substract: Append(" - "); break; + case ShaderNodes::BinaryType::Multiply: Append(" * "); break; + case ShaderNodes::BinaryType::Divide: Append(" / "); break; + + case ShaderNodes::BinaryType::CompEq: Append(" == "); break; + case ShaderNodes::BinaryType::CompGe: Append(" >= "); break; + case ShaderNodes::BinaryType::CompGt: Append(" > "); break; + case ShaderNodes::BinaryType::CompLe: Append(" <= "); break; + case ShaderNodes::BinaryType::CompLt: Append(" < "); break; + case ShaderNodes::BinaryType::CompNe: Append(" != "); break; } Visit(node.right, true); @@ -448,15 +445,17 @@ namespace Nz Append(node.exprType); Append("("); - for (std::size_t i = 0; node.expressions[i]; ++i) + bool first = true; + for (const auto& exprPtr : node.expressions) { - if (i != 0) + if (!exprPtr) + break; + + if (!first) m_currentState->stream << ", "; - const auto& exprPtr = node.expressions[i]; - NazaraAssert(exprPtr, "Invalid expression"); - Visit(exprPtr); + first = false; } Append(")"); @@ -465,7 +464,10 @@ namespace Nz void GlslWriter::Visit(ShaderNodes::ConditionalExpression& node) { - if (m_context.states->enabledConditions.count(node.conditionName) != 0) + std::size_t conditionIndex = m_context.shader->FindConditionByName(node.conditionName); + assert(conditionIndex != ShaderAst::InvalidCondition); + + if (TestBit(m_context.states->enabledConditions, conditionIndex)) Visit(node.truePath); else Visit(node.falsePath); @@ -473,7 +475,10 @@ namespace Nz void GlslWriter::Visit(ShaderNodes::ConditionalStatement& node) { - if (m_context.states->enabledConditions.count(node.conditionName) != 0) + std::size_t conditionIndex = m_context.shader->FindConditionByName(node.conditionName); + assert(conditionIndex != ShaderAst::InvalidCondition); + + if (TestBit(m_context.states->enabledConditions, conditionIndex)) Visit(node.statement); } @@ -604,7 +609,7 @@ namespace Nz void GlslWriter::Visit(ShaderNodes::SwizzleOp& node) { - Visit(node.expression); + Visit(node.expression, true); Append("."); for (std::size_t i = 0; i < node.componentCount; ++i) diff --git a/src/Nazara/Shader/ShaderAstSerializer.cpp b/src/Nazara/Shader/ShaderAstSerializer.cpp index a5e870713..7f12ad3b5 100644 --- a/src/Nazara/Shader/ShaderAstSerializer.cpp +++ b/src/Nazara/Shader/ShaderAstSerializer.cpp @@ -659,6 +659,9 @@ namespace Nz Int32 nodeTypeInt; m_stream >> nodeTypeInt; + if (nodeTypeInt < static_cast(ShaderNodes::NodeType::None) || nodeTypeInt > static_cast(ShaderNodes::NodeType::Max)) + throw std::runtime_error("invalid node type"); + ShaderNodes::NodeType nodeType = static_cast(nodeTypeInt); #define HandleType(Type) case ShaderNodes::NodeType:: Type : node = std::make_shared(); break diff --git a/src/Nazara/Shader/ShaderAstValidator.cpp b/src/Nazara/Shader/ShaderAstValidator.cpp index 643ff2d85..e139fa25e 100644 --- a/src/Nazara/Shader/ShaderAstValidator.cpp +++ b/src/Nazara/Shader/ShaderAstValidator.cpp @@ -150,8 +150,17 @@ namespace Nz switch (node.op) { + case ShaderNodes::BinaryType::CompGe: + case ShaderNodes::BinaryType::CompGt: + case ShaderNodes::BinaryType::CompLe: + case ShaderNodes::BinaryType::CompLt: + if (leftType == ShaderNodes::BasicType::Boolean) + throw AstError{ "this operation is not supported for booleans" }; + + [[fallthrough]]; case ShaderNodes::BinaryType::Add: - case ShaderNodes::BinaryType::Equality: + case ShaderNodes::BinaryType::CompEq: + case ShaderNodes::BinaryType::CompNe: case ShaderNodes::BinaryType::Substract: TypeMustMatch(node.left, node.right); break; @@ -236,7 +245,7 @@ namespace Nz } if (componentCount != requiredComponents) - throw AstError{ "Component count doesn't match required component count" }; + throw AstError{ "component count doesn't match required component count" }; ShaderAstRecursiveVisitor::Visit(node); } @@ -246,28 +255,16 @@ namespace Nz MandatoryNode(node.truePath); MandatoryNode(node.falsePath); - for (std::size_t i = 0; i < m_shader.GetConditionCount(); ++i) - { - const auto& condition = m_shader.GetCondition(i); - if (condition.name == node.conditionName) - return; - } - - throw AstError{ "Condition not found" }; + if (m_shader.FindConditionByName(node.conditionName) == ShaderAst::InvalidCondition) + throw AstError{ "condition not found" }; } void ShaderAstValidator::Visit(ShaderNodes::ConditionalStatement& node) { MandatoryNode(node.statement); - for (std::size_t i = 0; i < m_shader.GetConditionCount(); ++i) - { - const auto& condition = m_shader.GetCondition(i); - if (condition.name == node.conditionName) - return; - } - - throw AstError{ "Condition not found" }; + if (m_shader.FindConditionByName(node.conditionName) == ShaderAst::InvalidCondition) + throw AstError{ "condition not found" }; } void ShaderAstValidator::Visit(ShaderNodes::Constant& /*node*/) diff --git a/src/Nazara/Shader/ShaderNodes.cpp b/src/Nazara/Shader/ShaderNodes.cpp index c6aa7874a..2aa00ec69 100644 --- a/src/Nazara/Shader/ShaderNodes.cpp +++ b/src/Nazara/Shader/ShaderNodes.cpp @@ -140,7 +140,12 @@ namespace Nz::ShaderNodes break; } - case BinaryType::Equality: + case BinaryType::CompEq: + case BinaryType::CompGe: + case BinaryType::CompGt: + case BinaryType::CompLe: + case BinaryType::CompLt: + case BinaryType::CompNe: exprType = BasicType::Boolean; break; } diff --git a/src/Nazara/Shader/SpirvAstVisitor.cpp b/src/Nazara/Shader/SpirvAstVisitor.cpp index 871f41b41..e012d6de3 100644 --- a/src/Nazara/Shader/SpirvAstVisitor.cpp +++ b/src/Nazara/Shader/SpirvAstVisitor.cpp @@ -154,7 +154,7 @@ namespace Nz break; } - case ShaderNodes::BinaryType::Equality: + case ShaderNodes::BinaryType::CompEq: { switch (leftType) { @@ -185,6 +185,166 @@ namespace Nz break; } + + case ShaderNodes::BinaryType::CompGe: + { + switch (leftType) + { + case ShaderNodes::BasicType::Float1: + case ShaderNodes::BasicType::Float2: + case ShaderNodes::BasicType::Float3: + case ShaderNodes::BasicType::Float4: + case ShaderNodes::BasicType::Mat4x4: + return SpirvOp::OpFOrdGreaterThan; + + case ShaderNodes::BasicType::Int1: + case ShaderNodes::BasicType::Int2: + case ShaderNodes::BasicType::Int3: + case ShaderNodes::BasicType::Int4: + return SpirvOp::OpSGreaterThan; + + case ShaderNodes::BasicType::UInt1: + case ShaderNodes::BasicType::UInt2: + case ShaderNodes::BasicType::UInt3: + case ShaderNodes::BasicType::UInt4: + return SpirvOp::OpUGreaterThan; + + case ShaderNodes::BasicType::Boolean: + case ShaderNodes::BasicType::Sampler2D: + case ShaderNodes::BasicType::Void: + break; + } + + break; + } + + case ShaderNodes::BinaryType::CompGt: + { + switch (leftType) + { + case ShaderNodes::BasicType::Float1: + case ShaderNodes::BasicType::Float2: + case ShaderNodes::BasicType::Float3: + case ShaderNodes::BasicType::Float4: + case ShaderNodes::BasicType::Mat4x4: + return SpirvOp::OpFOrdGreaterThanEqual; + + case ShaderNodes::BasicType::Int1: + case ShaderNodes::BasicType::Int2: + case ShaderNodes::BasicType::Int3: + case ShaderNodes::BasicType::Int4: + return SpirvOp::OpSGreaterThanEqual; + + case ShaderNodes::BasicType::UInt1: + case ShaderNodes::BasicType::UInt2: + case ShaderNodes::BasicType::UInt3: + case ShaderNodes::BasicType::UInt4: + return SpirvOp::OpUGreaterThanEqual; + + case ShaderNodes::BasicType::Boolean: + case ShaderNodes::BasicType::Sampler2D: + case ShaderNodes::BasicType::Void: + break; + } + + break; + } + + case ShaderNodes::BinaryType::CompLe: + { + switch (leftType) + { + case ShaderNodes::BasicType::Float1: + case ShaderNodes::BasicType::Float2: + case ShaderNodes::BasicType::Float3: + case ShaderNodes::BasicType::Float4: + case ShaderNodes::BasicType::Mat4x4: + return SpirvOp::OpFOrdLessThanEqual; + + case ShaderNodes::BasicType::Int1: + case ShaderNodes::BasicType::Int2: + case ShaderNodes::BasicType::Int3: + case ShaderNodes::BasicType::Int4: + return SpirvOp::OpSLessThanEqual; + + case ShaderNodes::BasicType::UInt1: + case ShaderNodes::BasicType::UInt2: + case ShaderNodes::BasicType::UInt3: + case ShaderNodes::BasicType::UInt4: + return SpirvOp::OpULessThanEqual; + + case ShaderNodes::BasicType::Boolean: + case ShaderNodes::BasicType::Sampler2D: + case ShaderNodes::BasicType::Void: + break; + } + + break; + } + + case ShaderNodes::BinaryType::CompLt: + { + switch (leftType) + { + case ShaderNodes::BasicType::Float1: + case ShaderNodes::BasicType::Float2: + case ShaderNodes::BasicType::Float3: + case ShaderNodes::BasicType::Float4: + case ShaderNodes::BasicType::Mat4x4: + return SpirvOp::OpFOrdLessThan; + + case ShaderNodes::BasicType::Int1: + case ShaderNodes::BasicType::Int2: + case ShaderNodes::BasicType::Int3: + case ShaderNodes::BasicType::Int4: + return SpirvOp::OpSLessThan; + + case ShaderNodes::BasicType::UInt1: + case ShaderNodes::BasicType::UInt2: + case ShaderNodes::BasicType::UInt3: + case ShaderNodes::BasicType::UInt4: + return SpirvOp::OpULessThan; + + case ShaderNodes::BasicType::Boolean: + case ShaderNodes::BasicType::Sampler2D: + case ShaderNodes::BasicType::Void: + break; + } + + break; + } + + case ShaderNodes::BinaryType::CompNe: + { + switch (leftType) + { + case ShaderNodes::BasicType::Boolean: + return SpirvOp::OpLogicalNotEqual; + + case ShaderNodes::BasicType::Float1: + case ShaderNodes::BasicType::Float2: + case ShaderNodes::BasicType::Float3: + case ShaderNodes::BasicType::Float4: + case ShaderNodes::BasicType::Mat4x4: + return SpirvOp::OpFOrdNotEqual; + + case ShaderNodes::BasicType::Int1: + case ShaderNodes::BasicType::Int2: + case ShaderNodes::BasicType::Int3: + case ShaderNodes::BasicType::Int4: + case ShaderNodes::BasicType::UInt1: + case ShaderNodes::BasicType::UInt2: + case ShaderNodes::BasicType::UInt3: + case ShaderNodes::BasicType::UInt4: + return SpirvOp::OpINotEqual; + + case ShaderNodes::BasicType::Sampler2D: + case ShaderNodes::BasicType::Void: + break; + } + + break; + } case ShaderNodes::BinaryType::Multiply: { diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index c8ad4d4b4..535d53297 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -33,7 +33,8 @@ namespace Nz using LocalContainer = std::unordered_set>; using ParameterContainer = std::unordered_set< std::shared_ptr>; - PreVisitor(const SpirvWriter::States& conditions, SpirvConstantCache& constantCache) : + PreVisitor(const ShaderAst& shader, const SpirvWriter::States& conditions, SpirvConstantCache& constantCache) : + m_shader(shader), m_conditions(conditions), m_constantCache(constantCache) { @@ -52,7 +53,10 @@ namespace Nz void Visit(ShaderNodes::ConditionalExpression& node) override { - if (m_conditions.enabledConditions.count(node.conditionName) != 0) + std::size_t conditionIndex = m_shader.FindConditionByName(node.conditionName); + assert(conditionIndex != ShaderAst::InvalidCondition); + + if (TestBit(m_conditions.enabledConditions, conditionIndex)) Visit(node.truePath); else Visit(node.falsePath); @@ -60,7 +64,10 @@ namespace Nz void Visit(ShaderNodes::ConditionalStatement& node) override { - if (m_conditions.enabledConditions.count(node.conditionName) != 0) + std::size_t conditionIndex = m_shader.FindConditionByName(node.conditionName); + assert(conditionIndex != ShaderAst::InvalidCondition); + + if (TestBit(m_conditions.enabledConditions, conditionIndex)) Visit(node.statement); } @@ -141,6 +148,7 @@ namespace Nz ParameterContainer paramVars; private: + const ShaderAst& m_shader; const SpirvWriter::States& m_conditions; SpirvConstantCache& m_constantCache; }; @@ -229,7 +237,7 @@ namespace Nz ShaderAstCloner cloner; - PreVisitor preVisitor(conditions, state.constantTypeCache); + PreVisitor preVisitor(shader, conditions, state.constantTypeCache); for (const auto& func : shader.GetFunctions()) { functionStatements.emplace_back(cloner.Clone(func.statement)); diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 2d67003e7..5f6ed9e3d 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -39,6 +39,15 @@ namespace Nz return pipelineLayout; } + std::shared_ptr VulkanDevice::InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states) + { + auto stage = std::make_shared(); + if (!stage->Create(*this, shaderAst, states)) + return {}; + + return stage; + } + std::shared_ptr VulkanDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) { auto stage = std::make_shared(); diff --git a/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp b/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp index 9459ccfe3..2162a1c64 100644 --- a/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp +++ b/src/Nazara/VulkanRenderer/VulkanShaderStage.cpp @@ -10,6 +10,26 @@ namespace Nz { + bool VulkanShaderStage::Create(Vk::Device& device, const ShaderAst& shader, const ShaderWriter::States& states) + { + m_stage = shader.GetStage(); + + SpirvWriter::Environment env; + + SpirvWriter writer; + writer.SetEnv(env); + + std::vector code = writer.Generate(shader, states); + + if (!m_shaderModule.Create(device, code.data(), code.size() * sizeof(UInt32))) + { + NazaraError("Failed to create shader module"); + return false; + } + + return true; + } + bool VulkanShaderStage::Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) { m_stage = type; @@ -18,24 +38,12 @@ namespace Nz { case ShaderLanguage::NazaraBinary: { - ByteStream byteStream(source, sourceSize); - auto shader = Nz::UnserializeShader(byteStream); - + auto shader = UnserializeShader(source, sourceSize); if (shader.GetStage() != type) throw std::runtime_error("incompatible shader stage"); - SpirvWriter::Environment env; - - SpirvWriter writer; - writer.SetEnv(env); - - std::vector code = writer.Generate(shader); - - if (!m_shaderModule.Create(device, code.data(), code.size() * sizeof(UInt32))) - { - NazaraError("Failed to create shader module"); + if (!Create(device, shader, {})) return false; - } break; } diff --git a/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp b/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp index e15812528..aa5f6f513 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp @@ -49,7 +49,11 @@ namespace Nz ss << "[Validation]"; - ss << "[" << pCallbackData->messageIdNumber << ":" << pCallbackData->pMessageIdName << "]: " << pCallbackData->pMessage; + ss << "[" << pCallbackData->messageIdNumber; + if (pCallbackData->pMessageIdName) + ss << ":" << pCallbackData->pMessageIdName; + + ss << "]: " << pCallbackData->pMessage; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) NazaraError(ss.str()); diff --git a/src/ShaderNode/DataModels/BinOp.hpp b/src/ShaderNode/DataModels/BinOp.hpp new file mode 100644 index 000000000..8efb9fc6e --- /dev/null +++ b/src/ShaderNode/DataModels/BinOp.hpp @@ -0,0 +1,163 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BINOP_HPP +#define NAZARA_SHADERNODES_BINOP_HPP + +#include +#include +#include + +template +class BinOp : public ShaderNode +{ + public: + BinOp(ShaderGraph& graph); + ~BinOp() = default; + + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + + virtual QString GetOperationString() const = 0; + + unsigned int nPorts(QtNodes::PortType portType) const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + void setInData(std::shared_ptr value, int index) override; + + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + + private: + virtual void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) = 0; + + bool ComputePreview(QPixmap& pixmap) override; + void UpdateOutput(); + + std::shared_ptr m_lhs; + std::shared_ptr m_rhs; + std::shared_ptr m_output; +}; + + +template +class BinAdd : public BinOp +{ + public: + using BinOp::BinOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class BinMul : public BinOp +{ + public: + using BinOp::BinOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class BinSub : public BinOp +{ + public: + using BinOp::BinOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class BinDiv : public BinOp +{ + public: + using BinOp::BinOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + + +class FloatAdd : public BinAdd +{ + public: + using BinAdd::BinAdd; + + QString caption() const override { return "Float addition"; } + QString name() const override { return "float_add"; } +}; + +class FloatMul : public BinMul +{ + public: + using BinMul::BinMul; + + QString caption() const override { return "Float multiplication"; } + QString name() const override { return "float_mul"; } +}; + +class FloatSub : public BinMul +{ + public: + using BinMul::BinMul; + + QString caption() const override { return "Float subtraction"; } + QString name() const override { return "float_sub"; } +}; + +class FloatDiv : public BinDiv +{ + public: + using BinDiv::BinDiv; + + QString caption() const override { return "Float division"; } + QString name() const override { return "float_div"; } +}; + + +class VecAdd : public BinAdd +{ + public: + using BinAdd::BinAdd; + + QString caption() const override { return "Vector addition"; } + QString name() const override { return "vec_add"; } +}; + +class VecMul : public BinMul +{ + public: + using BinMul::BinMul; + + QString caption() const override { return "Vector multiplication"; } + QString name() const override { return "vec_mul"; } +}; + +class VecSub : public BinMul +{ + public: + using BinMul::BinMul; + + QString caption() const override { return "Vector subtraction"; } + QString name() const override { return "vec_sub"; } +}; + +class VecDiv : public BinDiv +{ + public: + using BinDiv::BinDiv; + + QString caption() const override { return "Vector division"; } + QString name() const override { return "vec_div"; } +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/BinOp.inl b/src/ShaderNode/DataModels/BinOp.inl new file mode 100644 index 000000000..10031254c --- /dev/null +++ b/src/ShaderNode/DataModels/BinOp.inl @@ -0,0 +1,240 @@ +#include +#include + +template +BinOp::BinOp(ShaderGraph& graph) : +ShaderNode(graph) +{ + UpdateOutput(); +} + +template +Nz::ShaderNodes::NodePtr BinOp::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const +{ + assert(count == 2); + assert(outputIndex == 0); + + using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; + constexpr BuilderType builder; + return builder(expressions[0], expressions[1]); +} + +template +QtNodes::NodeDataType BinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0 || portIndex == 1); + + return DataType::Type(); +} + +template +unsigned int BinOp::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 2; + case QtNodes::PortType::Out: return 1; + default: break; + } + + assert(false); + throw std::runtime_error("invalid port type"); +} + +template +std::shared_ptr BinOp::outData(QtNodes::PortIndex port) +{ + assert(port == 0); + return m_output; +} + +template +QString BinOp::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: + { + switch (portIndex) + { + case 0: + return "A"; + + case 1: + return "B"; + + default: + break; + } + } + + case QtNodes::PortType::Out: + { + assert(portIndex == 0); + return "A " + GetOperationString() + " B"; + } + + default: + break; + } + + return QString{}; +} + +template +bool BinOp::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0 || portIndex == 1); + return portType == QtNodes::PortType::In || portType == QtNodes::PortType::Out; +} + +template +void BinOp::setInData(std::shared_ptr value, int index) +{ + assert(index == 0 || index == 1); + + std::shared_ptr castedValue; + if (value && value->type().id == DataType::Type().id) + castedValue = std::static_pointer_cast(value); + + if (index == 0) + m_lhs = std::move(castedValue); + else + m_rhs = std::move(castedValue); + + UpdateOutput(); +} + +template +QtNodes::NodeValidationState BinOp::validationState() const +{ + if (!m_lhs || !m_rhs) + return QtNodes::NodeValidationState::Error; + + if constexpr (std::is_same_v) + { + if (m_lhs->componentCount != m_rhs->componentCount) + return QtNodes::NodeValidationState::Error; + } + + return QtNodes::NodeValidationState::Valid; +} + +template +QString BinOp::validationMessage() const +{ + if (!m_lhs || !m_rhs) + return "Missing operands"; + + if constexpr (std::is_same_v) + { + if (m_lhs->componentCount != m_rhs->componentCount) + return "Incompatible components count (left has " + QString::number(m_lhs->componentCount) + ", right has " + QString::number(m_rhs->componentCount) + ")"; + } + + return QString(); +} + +template +bool BinOp::ComputePreview(QPixmap& pixmap) +{ + if (!m_lhs || !m_rhs) + return false; + + pixmap = QPixmap::fromImage(m_output->preview.GenerateImage()); + return true; +} + +template +void BinOp::UpdateOutput() +{ + if (validationState() != QtNodes::NodeValidationState::Valid) + { + if constexpr (std::is_same_v) + m_output = std::make_shared(4); + else + m_output = std::make_shared(); + + m_output->preview = PreviewValues(1, 1); + m_output->preview.Fill(Nz::Vector4f::Zero()); + return; + } + + if constexpr (std::is_same_v) + m_output = std::make_shared(m_lhs->componentCount); + else + m_output = std::make_shared(); + + const PreviewValues& leftPreview = m_lhs->preview; + const PreviewValues& rightPreview = m_rhs->preview; + std::size_t maxWidth = std::max(leftPreview.GetWidth(), rightPreview.GetWidth()); + std::size_t maxHeight = std::max(leftPreview.GetHeight(), rightPreview.GetHeight()); + + // FIXME: Prevent useless copy + PreviewValues leftResized = leftPreview; + if (leftResized.GetWidth() != maxWidth || leftResized.GetHeight() != maxHeight) + leftResized = leftResized.Resized(maxWidth, maxHeight); + + PreviewValues rightResized = rightPreview; + if (rightResized.GetWidth() != maxWidth || rightResized.GetHeight() != maxHeight) + rightResized = rightResized.Resized(maxWidth, maxHeight); + + m_output->preview = PreviewValues(maxWidth, maxHeight); + ApplyOp(leftResized.GetData(), rightResized.GetData(), m_output->preview.GetData(), maxWidth * maxHeight); + + Q_EMIT dataUpdated(0); + + UpdatePreview(); +} + +template +void BinAdd::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + output[i] = left[i] + right[i]; +} + +template +QString BinAdd::GetOperationString() const +{ + return "+"; +} + +template +void BinMul::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + output[i] = left[i] * right[i]; +} + +template +QString BinMul::GetOperationString() const +{ + return "*"; +} + +template +void BinSub::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + output[i] = left[i] - right[i]; +} + +template +QString BinSub::GetOperationString() const +{ + return "-"; +} + +template +void BinDiv::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + output[i] = left[i] / right[i]; +} + +template +QString BinDiv::GetOperationString() const +{ + return "/"; +} diff --git a/src/ShaderNode/DataModels/BoolValue.cpp b/src/ShaderNode/DataModels/BoolValue.cpp new file mode 100644 index 000000000..940d8f0fb --- /dev/null +++ b/src/ShaderNode/DataModels/BoolValue.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include + +BoolValue::BoolValue(ShaderGraph& graph) : +ShaderNode(graph), +m_value(true) +{ + UpdatePreview(); +} + +QString BoolValue::caption() const +{ + static QString caption = "Boolean constant"; + return caption; +} + +QString BoolValue::name() const +{ + static QString name = "bool_constant"; + return name; +} + +QtNodes::NodeDataType BoolValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::Out); + assert(portIndex == 0); + + return BoolData::Type(); +} + +unsigned int BoolValue::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 0; + case QtNodes::PortType::Out: return 1; + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +std::shared_ptr BoolValue::outData(QtNodes::PortIndex port) +{ + assert(port == 0); + + float c = (m_value) ? 1.f : 0.f; + + auto out = std::make_shared(); + out->preview(0, 0) = Nz::Vector4f(c, c, c, 1.f); + + return out; +} + +QString BoolValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + assert(portType == QtNodes::PortType::Out); + + return (m_value) ? "true" : "false"; +} + +bool BoolValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + return portType == QtNodes::PortType::Out; +} + +void BoolValue::BuildNodeEdition(QFormLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QCheckBox* checkbox = new QCheckBox; + checkbox->setCheckState((m_value) ? Qt::Checked : Qt::Unchecked); + connect(checkbox, &QCheckBox::stateChanged, [=](int newState) + { + m_value = (newState == Qt::Checked); + Q_EMIT dataUpdated(0); + + UpdatePreview(); + }); + + layout->addRow(tr("Value"), checkbox); +} + +Nz::ShaderNodes::NodePtr BoolValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count, std::size_t outputIndex) const +{ + assert(count == 0); + assert(outputIndex == 0); + + return Nz::ShaderBuilder::Constant(m_value); +} + +bool BoolValue::ComputePreview(QPixmap& pixmap) +{ + pixmap.fill(ToColor()); + return true; +} + +QColor BoolValue::ToColor() const +{ + float value = (m_value) ? 1.f : 0.f; + + return QColor::fromRgbF(value, value, value, value); +} + +void BoolValue::restore(const QJsonObject& data) +{ + m_value = float(data["value"].toBool(m_value)); + + ShaderNode::restore(data); +} + +QJsonObject BoolValue::save() const +{ + QJsonObject data = ShaderNode::save(); + data["value"] = m_value; + + return data; +} diff --git a/src/ShaderNode/DataModels/BoolValue.hpp b/src/ShaderNode/DataModels/BoolValue.hpp new file mode 100644 index 000000000..d3e664d9d --- /dev/null +++ b/src/ShaderNode/DataModels/BoolValue.hpp @@ -0,0 +1,47 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BOOLVALUE_HPP +#define NAZARA_SHADERNODES_BOOLVALUE_HPP + +#include +#include +#include +#include +#include +#include +#include + +class BoolValue : public ShaderNode +{ + public: + BoolValue(ShaderGraph& graph); + ~BoolValue() = default; + + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + void BuildNodeEdition(QFormLayout* layout) override; + + QString caption() const override; + QString name() const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + unsigned int nPorts(QtNodes::PortType portType) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + private: + bool ComputePreview(QPixmap& pixmap) override; + QColor ToColor() const; + + void restore(const QJsonObject& data) override; + QJsonObject save() const override; + + bool m_value; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/BoolValue.inl b/src/ShaderNode/DataModels/BoolValue.inl new file mode 100644 index 000000000..428c66ae7 --- /dev/null +++ b/src/ShaderNode/DataModels/BoolValue.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataModels/BufferField.cpp b/src/ShaderNode/DataModels/BufferField.cpp index 3f3986fb5..11707f717 100644 --- a/src/ShaderNode/DataModels/BufferField.cpp +++ b/src/ShaderNode/DataModels/BufferField.cpp @@ -49,9 +49,10 @@ ShaderNode(graph) UpdatePreview(); } -Nz::ShaderNodes::ExpressionPtr BufferField::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +Nz::ShaderNodes::NodePtr BufferField::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 0); + assert(outputIndex == 0); if (!m_currentBufferIndex) throw std::runtime_error("no buffer"); diff --git a/src/ShaderNode/DataModels/BufferField.hpp b/src/ShaderNode/DataModels/BufferField.hpp index 8bd232a7a..7e5e46be4 100644 --- a/src/ShaderNode/DataModels/BufferField.hpp +++ b/src/ShaderNode/DataModels/BufferField.hpp @@ -15,10 +15,9 @@ class BufferField : public ShaderNode BufferField(ShaderGraph& graph); ~BufferField() = default; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; - QString caption() const override { return "BufferField"; } QString name() const override { return "BufferField"; } diff --git a/src/ShaderNode/DataModels/Cast.hpp b/src/ShaderNode/DataModels/Cast.hpp index c30f3eadc..ea3303dd3 100644 --- a/src/ShaderNode/DataModels/Cast.hpp +++ b/src/ShaderNode/DataModels/Cast.hpp @@ -17,10 +17,9 @@ class CastVec : public ShaderNode CastVec(ShaderGraph& graph); ~CastVec() = default; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const; void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; - QString caption() const override; QString name() const override; diff --git a/src/ShaderNode/DataModels/Cast.inl b/src/ShaderNode/DataModels/Cast.inl index 0bad72055..5a215c72a 100644 --- a/src/ShaderNode/DataModels/Cast.inl +++ b/src/ShaderNode/DataModels/Cast.inl @@ -16,6 +16,44 @@ ShaderNode(graph) m_output = std::make_shared(ToComponentCount); } +template +Nz::ShaderNodes::NodePtr CastVec::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const +{ + assert(m_input); + assert(count == 1); + assert(outputIndex == 0); + + std::size_t fromComponentCount = m_input->componentCount; + + if (ToComponentCount > fromComponentCount) + { + std::size_t overflowComponentCount = ToComponentCount - fromComponentCount; + + std::array expr; + expr[0] = expressions[0]; + for (std::size_t i = 0; i < overflowComponentCount; ++i) + expr[i + 1] = Nz::ShaderBuilder::Constant(m_overflowComponents[i]); + + constexpr auto ExpressionType = VecExpressionType; + + return Nz::ShaderBuilder::Cast(expr.data(), 1 + overflowComponentCount); + } + else if (ToComponentCount < fromComponentCount) + { + std::array swizzleComponents; + for (std::size_t i = 0; i < ToComponentCount; ++i) + swizzleComponents[i] = static_cast(static_cast(Nz::ShaderNodes::SwizzleComponent::First) + i); + + return std::apply([&](auto... components) + { + std::initializer_list componentList{ components... }; + return Nz::ShaderBuilder::Swizzle(expressions[0], componentList); + }, swizzleComponents); + } + else + return expressions[0]; //< no-op +} + template void CastVec::BuildNodeEdition(QFormLayout* layout) { @@ -48,43 +86,6 @@ void CastVec::BuildNodeEdition(QFormLayout* layout) } } -template -Nz::ShaderNodes::ExpressionPtr CastVec::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const -{ - assert(m_input); - assert(count == 1); - - std::size_t fromComponentCount = m_input->componentCount; - - if (ToComponentCount > fromComponentCount) - { - std::size_t overflowComponentCount = ToComponentCount - fromComponentCount; - - std::array expr; - expr[0] = expressions[0]; - for (std::size_t i = 0; i < overflowComponentCount; ++i) - expr[i + 1] = Nz::ShaderBuilder::Constant(m_overflowComponents[i]); - - constexpr auto ExpressionType = VecExpressionType; - - return Nz::ShaderBuilder::Cast(expr.data(), 1 + overflowComponentCount); - } - else if (ToComponentCount < fromComponentCount) - { - std::array swizzleComponents; - for (std::size_t i = 0; i < ToComponentCount; ++i) - swizzleComponents[i] = static_cast(static_cast(Nz::ShaderNodes::SwizzleComponent::First) + i); - - return std::apply([&](auto... components) - { - std::initializer_list componentList{ components... }; - return Nz::ShaderBuilder::Swizzle(expressions[0], componentList); - }, swizzleComponents); - } - else - return expressions[0]; //< no-op -} - template QString CastVec::caption() const { diff --git a/src/ShaderNode/DataModels/CompOp.hpp b/src/ShaderNode/DataModels/CompOp.hpp new file mode 100644 index 000000000..cb7da53e4 --- /dev/null +++ b/src/ShaderNode/DataModels/CompOp.hpp @@ -0,0 +1,220 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_COMPOP_HPP +#define NAZARA_SHADERNODES_COMPOP_HPP + +#include +#include +#include +#include + +template +class CompOp : public ShaderNode +{ + public: + CompOp(ShaderGraph& graph); + ~CompOp() = default; + + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + + virtual QString GetOperationString() const = 0; + + unsigned int nPorts(QtNodes::PortType portType) const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + void setInData(std::shared_ptr value, int index) override; + + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + + private: + virtual void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) = 0; + + bool ComputePreview(QPixmap& pixmap) override; + void UpdateOutput(); + + std::shared_ptr m_output; + std::shared_ptr m_lhs; + std::shared_ptr m_rhs; +}; + + +template +class CompEq : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class CompGe : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class CompGt : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class CompLe : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class CompLt : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + +template +class CompNe : public CompOp +{ + public: + using CompOp::CompOp; + + void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; + QString GetOperationString() const final; +}; + + +class FloatEq : public CompEq +{ + public: + using CompEq::CompEq; + + QString caption() const override { return "Float equality"; } + QString name() const override { return "float_eq"; } +}; + +class FloatGe : public CompGe +{ + public: + using CompGe::CompGe; + + QString caption() const override { return "Float greater than or equal"; } + QString name() const override { return "float_ge"; } +}; + +class FloatGt : public CompGt +{ + public: + using CompGt::CompGt; + + QString caption() const override { return "Float greater than"; } + QString name() const override { return "float_gt"; } +}; + +class FloatLe : public CompLe +{ + public: + using CompLe::CompLe; + + QString caption() const override { return "Float less than or equal"; } + QString name() const override { return "float_le"; } +}; + +class FloatLt : public CompLt +{ + public: + using CompLt::CompLt; + + QString caption() const override { return "Float less than"; } + QString name() const override { return "float_lt"; } +}; + +class FloatNe : public CompNe +{ + public: + using CompNe::CompNe; + + QString caption() const override { return "Float inequality"; } + QString name() const override { return "float_ne"; } +}; + + +class VecEq : public CompEq +{ + public: + using CompEq::CompEq; + + QString caption() const override { return "Vector equality"; } + QString name() const override { return "vec_eq"; } +}; + +class VecGe : public CompGe +{ + public: + using CompGe::CompGe; + + QString caption() const override { return "Vector greater than or equal"; } + QString name() const override { return "vec_ge"; } +}; + +class VecGt : public CompGt +{ + public: + using CompGt::CompGt; + + QString caption() const override { return "Vector greater than"; } + QString name() const override { return "vec_gt"; } +}; + +class VecLe : public CompLe +{ + public: + using CompLe::CompLe; + + QString caption() const override { return "Vector less than or equal"; } + QString name() const override { return "vec_le"; } +}; + +class VecLt : public CompLt +{ + public: + using CompLt::CompLt; + + QString caption() const override { return "Vector less than"; } + QString name() const override { return "vec_lt"; } +}; + +class VecNe : public CompNe +{ + public: + using CompNe::CompNe; + + QString caption() const override { return "Vector inequality"; } + QString name() const override { return "vec_ne"; } +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/CompOp.inl b/src/ShaderNode/DataModels/CompOp.inl new file mode 100644 index 000000000..4ca0a122e --- /dev/null +++ b/src/ShaderNode/DataModels/CompOp.inl @@ -0,0 +1,293 @@ +#include +#include + +template +CompOp::CompOp(ShaderGraph& graph) : +ShaderNode(graph) +{ + UpdateOutput(); +} + +template +Nz::ShaderNodes::NodePtr CompOp::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const +{ + assert(count == 2); + assert(outputIndex == 0); + + using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; + constexpr BuilderType builder; + return builder(expressions[0], expressions[1]); +} + +template +QtNodes::NodeDataType CompOp::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: + { + assert(portIndex == 0 || portIndex == 1); + return DataType::Type(); + } + + case QtNodes::PortType::Out: + { + assert(portIndex == 0); + return BoolData::Type(); + } + + default: break; + } + + assert(false); + throw std::runtime_error("invalid port type"); +} + +template +unsigned int CompOp::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 2; + case QtNodes::PortType::Out: return 1; + default: break; + } + + assert(false); + throw std::runtime_error("invalid port type"); +} + +template +std::shared_ptr CompOp::outData(QtNodes::PortIndex port) +{ + assert(port == 0); + return m_output; +} + +template +QString CompOp::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: + { + switch (portIndex) + { + case 0: + return "A"; + + case 1: + return "B"; + + default: + break; + } + } + + case QtNodes::PortType::Out: + { + assert(portIndex == 0); + return "A " + GetOperationString() + " B"; + } + + default: + break; + } + + return QString{}; +} + +template +bool CompOp::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0 || portIndex == 1); + return portType == QtNodes::PortType::In || portType == QtNodes::PortType::Out; +} + +template +void CompOp::setInData(std::shared_ptr value, int index) +{ + assert(index == 0 || index == 1); + + std::shared_ptr castedValue; + if (value && value->type().id == DataType::Type().id) + castedValue = std::static_pointer_cast(value); + + if (index == 0) + m_lhs = std::move(castedValue); + else + m_rhs = std::move(castedValue); + + UpdateOutput(); +} + +template +QtNodes::NodeValidationState CompOp::validationState() const +{ + if (!m_lhs || !m_rhs) + return QtNodes::NodeValidationState::Error; + + if constexpr (std::is_same_v) + { + if (m_lhs->componentCount != m_rhs->componentCount) + return QtNodes::NodeValidationState::Error; + } + + return QtNodes::NodeValidationState::Valid; +} + +template +QString CompOp::validationMessage() const +{ + if (!m_lhs || !m_rhs) + return "Missing operands"; + + if constexpr (std::is_same_v) + { + if (m_lhs->componentCount != m_rhs->componentCount) + return "Incompatible components count (left has " + QString::number(m_lhs->componentCount) + ", right has " + QString::number(m_rhs->componentCount) + ")"; + } + + return QString(); +} + +template +bool CompOp::ComputePreview(QPixmap& pixmap) +{ + if (!m_lhs || !m_rhs) + return false; + + pixmap = QPixmap::fromImage(m_output->preview.GenerateImage()); + return true; +} + +template +void CompOp::UpdateOutput() +{ + if (validationState() != QtNodes::NodeValidationState::Valid) + { + m_output = std::make_shared(); + m_output->preview = PreviewValues(1, 1); + m_output->preview.Fill(Nz::Vector4f::Zero()); + return; + } + + m_output = std::make_shared(); + + const PreviewValues& leftPreview = m_lhs->preview; + const PreviewValues& rightPreview = m_rhs->preview; + std::size_t maxWidth = std::max(leftPreview.GetWidth(), rightPreview.GetWidth()); + std::size_t maxHeight = std::max(leftPreview.GetHeight(), rightPreview.GetHeight()); + + // FIXME: Prevent useless copy + PreviewValues leftResized = leftPreview; + if (leftResized.GetWidth() != maxWidth || leftResized.GetHeight() != maxHeight) + leftResized = leftResized.Resized(maxWidth, maxHeight); + + PreviewValues rightResized = rightPreview; + if (rightResized.GetWidth() != maxWidth || rightResized.GetHeight() != maxHeight) + rightResized = rightResized.Resized(maxWidth, maxHeight); + + m_output->preview = PreviewValues(maxWidth, maxHeight); + ApplyOp(leftResized.GetData(), rightResized.GetData(), m_output->preview.GetData(), maxWidth * maxHeight); + + Q_EMIT dataUpdated(0); + + UpdatePreview(); +} + +template +void CompEq::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] == right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompEq::GetOperationString() const +{ + return "=="; +} + +template +void CompGe::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] >= right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompGe::GetOperationString() const +{ + return ">="; +} + +template +void CompGt::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] > right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompGt::GetOperationString() const +{ + return ">"; +} + +template +void CompLe::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] >= right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompLe::GetOperationString() const +{ + return "<="; +} + +template +void CompLt::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] > right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompLt::GetOperationString() const +{ + return "<"; +} + +template +void CompNe::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + float r = (left[i] != right[i]) ? 1.f : 0.f; + output[i] = Nz::Vector4f(r, r, r, r); + } +} + +template +QString CompNe::GetOperationString() const +{ + return "!="; +} diff --git a/src/ShaderNode/DataModels/ConditionalExpression.cpp b/src/ShaderNode/DataModels/ConditionalExpression.cpp index d034ada49..27224e8d6 100644 --- a/src/ShaderNode/DataModels/ConditionalExpression.cpp +++ b/src/ShaderNode/DataModels/ConditionalExpression.cpp @@ -35,9 +35,10 @@ ShaderNode(graph) UpdatePreview(); } -Nz::ShaderNodes::ExpressionPtr ConditionalExpression::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr ConditionalExpression::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); + assert(outputIndex == 0); if (!m_currentConditionIndex) throw std::runtime_error("no condition"); @@ -100,15 +101,48 @@ void ConditionalExpression::BuildNodeEdition(QFormLayout* layout) auto ConditionalExpression::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType { + switch (portType) + { + case QtNodes::PortType::In: + { + switch (portIndex) + { + case 0: + { + if (!m_truePath && !m_falsePath) + return VecData::Type(); + + return (m_truePath) ? m_truePath->type() : m_falsePath->type(); + } + + case 1: + { + if (!m_truePath && !m_falsePath) + return VecData::Type(); + + return (m_falsePath) ? m_falsePath->type() : m_truePath->type(); + } + + default: + break; + } + } + + case QtNodes::PortType::Out: + { + assert(portIndex == 0); + + if (!m_truePath && !m_falsePath) + return VecData::Type(); + + return (m_truePath) ? m_truePath->type() : m_falsePath->type(); + } + + default: + break; + } + return VecData::Type(); - - assert(portType == QtNodes::PortType::Out); - assert(portIndex == 0); - - if (!m_truePath && !m_falsePath) - return VecData::Type(); - - return (m_truePath) ? m_truePath->type() : m_falsePath->type(); } QString ConditionalExpression::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const @@ -120,10 +154,10 @@ QString ConditionalExpression::portCaption(QtNodes::PortType portType, QtNodes:: switch (portIndex) { case 0: - return "True path"; + return "True expression"; case 1: - return "False path"; + return "False expression"; default: break; @@ -189,6 +223,9 @@ QtNodes::NodeValidationState ConditionalExpression::validationState() const QString ConditionalExpression::validationMessage() const { + if (!m_currentConditionIndex) + return "Invalid condition"; + if (!m_truePath || !m_falsePath) return "Missing input"; diff --git a/src/ShaderNode/DataModels/ConditionalExpression.hpp b/src/ShaderNode/DataModels/ConditionalExpression.hpp index a89d6c40c..1c732243d 100644 --- a/src/ShaderNode/DataModels/ConditionalExpression.hpp +++ b/src/ShaderNode/DataModels/ConditionalExpression.hpp @@ -15,10 +15,9 @@ class ConditionalExpression : public ShaderNode ConditionalExpression(ShaderGraph& graph); ~ConditionalExpression() = default; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; - QString caption() const override; QString name() const override; diff --git a/src/ShaderNode/DataModels/FloatValue.cpp b/src/ShaderNode/DataModels/FloatValue.cpp index 23a4c766f..4efc10585 100644 --- a/src/ShaderNode/DataModels/FloatValue.cpp +++ b/src/ShaderNode/DataModels/FloatValue.cpp @@ -36,9 +36,13 @@ unsigned int FloatValue::nPorts(QtNodes::PortType portType) const { case QtNodes::PortType::In: return 0; case QtNodes::PortType::Out: return 1; + + default: + break; } - return 0; + assert(false); + throw std::runtime_error("Invalid port type"); } std::shared_ptr FloatValue::outData(QtNodes::PortIndex port) @@ -51,6 +55,20 @@ std::shared_ptr FloatValue::outData(QtNodes::PortIndex port) return out; } +QString FloatValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + assert(portType == QtNodes::PortType::Out); + + return QString::number(m_value); +} + +bool FloatValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + return portType == QtNodes::PortType::Out; +} + void FloatValue::BuildNodeEdition(QFormLayout* layout) { ShaderNode::BuildNodeEdition(layout); @@ -71,9 +89,10 @@ void FloatValue::BuildNodeEdition(QFormLayout* layout) layout->addRow(tr("Value"), spinbox); } -Nz::ShaderNodes::ExpressionPtr FloatValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +Nz::ShaderNodes::NodePtr FloatValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 0); + assert(outputIndex == 0); return Nz::ShaderBuilder::Constant(m_value); } diff --git a/src/ShaderNode/DataModels/FloatValue.hpp b/src/ShaderNode/DataModels/FloatValue.hpp index fe77e5464..6dfb110f8 100644 --- a/src/ShaderNode/DataModels/FloatValue.hpp +++ b/src/ShaderNode/DataModels/FloatValue.hpp @@ -17,6 +17,9 @@ class FloatValue : public ShaderNode FloatValue(ShaderGraph& graph); ~FloatValue() = default; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + void BuildNodeEdition(QFormLayout* layout) override; + QString caption() const override; QString name() const override; @@ -26,9 +29,8 @@ class FloatValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; - void BuildNodeEdition(QFormLayout* layout) override; - - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; private: bool ComputePreview(QPixmap& pixmap) override; diff --git a/src/ShaderNode/DataModels/InputValue.cpp b/src/ShaderNode/DataModels/InputValue.cpp index e4710e6f5..f6926278b 100644 --- a/src/ShaderNode/DataModels/InputValue.cpp +++ b/src/ShaderNode/DataModels/InputValue.cpp @@ -109,9 +109,10 @@ void InputValue::BuildNodeEdition(QFormLayout* layout) layout->addRow(tr("Input"), inputSelection); } -Nz::ShaderNodes::ExpressionPtr InputValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +Nz::ShaderNodes::NodePtr InputValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count, std::size_t outputIndex) const { assert(count == 0); + assert(outputIndex == 0); if (!m_currentInputIndex) throw std::runtime_error("no input"); @@ -184,6 +185,35 @@ std::shared_ptr InputValue::outData(QtNodes::PortIndex port) throw std::runtime_error("Unhandled input type"); } +QString InputValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + assert(portType == QtNodes::PortType::Out); + + if (!m_currentInputIndex) + return QString(); + + const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex); + return QString::fromStdString(inputEntry.name); +} + +bool InputValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + + switch (portType) + { + case QtNodes::PortType::In: return false; + case QtNodes::PortType::Out: return m_currentInputIndex.has_value(); + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + QtNodes::NodeValidationState InputValue::validationState() const { if (!m_currentInputIndex) diff --git a/src/ShaderNode/DataModels/InputValue.hpp b/src/ShaderNode/DataModels/InputValue.hpp index d0e1593b9..9863d7b98 100644 --- a/src/ShaderNode/DataModels/InputValue.hpp +++ b/src/ShaderNode/DataModels/InputValue.hpp @@ -19,7 +19,7 @@ class InputValue : public ShaderNode void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override { return "Input"; } QString name() const override { return "Input"; } @@ -30,6 +30,9 @@ class InputValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + QtNodes::NodeValidationState validationState() const override; QString validationMessage() const override; diff --git a/src/ShaderNode/DataModels/Mat4BinOp.hpp b/src/ShaderNode/DataModels/Mat4BinOp.hpp index c90549bdd..1cd7e06dd 100644 --- a/src/ShaderNode/DataModels/Mat4BinOp.hpp +++ b/src/ShaderNode/DataModels/Mat4BinOp.hpp @@ -6,14 +6,14 @@ #include #include -template +template class Mat4BinOp : public ShaderNode { public: Mat4BinOp(ShaderGraph& graph); ~Mat4BinOp() = default; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const; unsigned int nPorts(QtNodes::PortType portType) const override; diff --git a/src/ShaderNode/DataModels/Mat4BinOp.inl b/src/ShaderNode/DataModels/Mat4BinOp.inl index eb57e74c2..50c4102e4 100644 --- a/src/ShaderNode/DataModels/Mat4BinOp.inl +++ b/src/ShaderNode/DataModels/Mat4BinOp.inl @@ -1,51 +1,55 @@ #include #include -template -Mat4BinOp::Mat4BinOp(ShaderGraph& graph) : +template +Mat4BinOp::Mat4BinOp(ShaderGraph& graph) : ShaderNode(graph) { UpdateOutput(); } -template -Nz::ShaderNodes::ExpressionPtr Mat4BinOp::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +template +Nz::ShaderNodes::NodePtr Mat4BinOp::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); - using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; + assert(outputIndex == 0); + + using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; constexpr BuilderType builder; return builder(expressions[0], expressions[1]); } -template -QtNodes::NodeDataType Mat4BinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const +template +QtNodes::NodeDataType Mat4BinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const { assert(portIndex == 0 || portIndex == 1); return Matrix4Data::Type(); } -template -unsigned int Mat4BinOp::nPorts(QtNodes::PortType portType) const +template +unsigned int Mat4BinOp::nPorts(QtNodes::PortType portType) const { switch (portType) { case QtNodes::PortType::In: return 2; case QtNodes::PortType::Out: return 1; + default: break; } - return 0; + assert(false); + throw std::runtime_error("invalid port type"); } -template -std::shared_ptr Mat4BinOp::outData(QtNodes::PortIndex port) +template +std::shared_ptr Mat4BinOp::outData(QtNodes::PortIndex port) { assert(port == 0); return m_output; } -template -void Mat4BinOp::setInData(std::shared_ptr value, int index) +template +void Mat4BinOp::setInData(std::shared_ptr value, int index) { assert(index == 0 || index == 1); @@ -64,8 +68,8 @@ void Mat4BinOp::setInData(std::shared_ptr value, int i UpdateOutput(); } -template -QtNodes::NodeValidationState Mat4BinOp::validationState() const +template +QtNodes::NodeValidationState Mat4BinOp::validationState() const { if (!m_lhs || !m_rhs) return QtNodes::NodeValidationState::Error; @@ -73,8 +77,8 @@ QtNodes::NodeValidationState Mat4BinOp::validationState() const return QtNodes::NodeValidationState::Valid; } -template -QString Mat4BinOp::validationMessage() const +template +QString Mat4BinOp::validationMessage() const { if (!m_lhs || !m_rhs) return "Missing operands"; @@ -82,8 +86,8 @@ QString Mat4BinOp::validationMessage() const return QString(); } -template -bool Mat4BinOp::ComputePreview(QPixmap& pixmap) +template +bool Mat4BinOp::ComputePreview(QPixmap& pixmap) { if (!m_lhs || !m_rhs) return false; @@ -94,8 +98,8 @@ bool Mat4BinOp::ComputePreview(QPixmap& pixmap) //return true; } -template -void Mat4BinOp::UpdateOutput() +template +void Mat4BinOp::UpdateOutput() { if (validationState() != QtNodes::NodeValidationState::Valid) { diff --git a/src/ShaderNode/DataModels/Mat4VecMul.cpp b/src/ShaderNode/DataModels/Mat4VecMul.cpp index af43e4f1d..426c0bde1 100644 --- a/src/ShaderNode/DataModels/Mat4VecMul.cpp +++ b/src/ShaderNode/DataModels/Mat4VecMul.cpp @@ -7,9 +7,11 @@ ShaderNode(graph) UpdateOutput(); } -Nz::ShaderNodes::ExpressionPtr Mat4VecMul::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr Mat4VecMul::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); + assert(outputIndex == 0); + using namespace Nz::ShaderNodes; return BinaryOp::Build(BinaryType::Multiply, expressions[0], expressions[1]); } diff --git a/src/ShaderNode/DataModels/Mat4VecMul.hpp b/src/ShaderNode/DataModels/Mat4VecMul.hpp index 608439fcb..d3b25b3b1 100644 --- a/src/ShaderNode/DataModels/Mat4VecMul.hpp +++ b/src/ShaderNode/DataModels/Mat4VecMul.hpp @@ -13,7 +13,7 @@ class Mat4VecMul : public ShaderNode Mat4VecMul(ShaderGraph& graph); ~Mat4VecMul() = default; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override; QString name() const override; diff --git a/src/ShaderNode/DataModels/OutputValue.cpp b/src/ShaderNode/DataModels/OutputValue.cpp index 7ffb4df91..22d4bfb72 100644 --- a/src/ShaderNode/DataModels/OutputValue.cpp +++ b/src/ShaderNode/DataModels/OutputValue.cpp @@ -54,12 +54,13 @@ void OutputValue::BuildNodeEdition(QFormLayout* layout) layout->addRow(tr("Output"), outputSelection); } -Nz::ShaderNodes::ExpressionPtr OutputValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr OutputValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { using namespace Nz::ShaderBuilder; using namespace Nz::ShaderNodes; assert(count == 1); + assert(outputIndex == 0); if (!m_currentOutputIndex) throw std::runtime_error("no output"); @@ -70,6 +71,11 @@ Nz::ShaderNodes::ExpressionPtr OutputValue::GetExpression(Nz::ShaderNodes::Expre return Nz::ShaderBuilder::Assign(std::move(output), *expressions); } +std::shared_ptr OutputValue::outData(QtNodes::PortIndex /*port*/) +{ + return {}; +} + QtNodes::NodeDataType OutputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { assert(portType == QtNodes::PortType::In); @@ -88,14 +94,40 @@ unsigned int OutputValue::nPorts(QtNodes::PortType portType) const { case QtNodes::PortType::In: return 1; case QtNodes::PortType::Out: return 0; + default: break; } - return 0; + assert(false); + throw std::runtime_error("invalid port type"); } -std::shared_ptr OutputValue::outData(QtNodes::PortIndex /*port*/) +QString OutputValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { - return {}; + assert(portType == QtNodes::PortType::In); + assert(portIndex == 0); + + if (!m_currentOutputIndex) + return QString(); + + const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); + return QString::fromStdString(outputEntry.name); +} + +bool OutputValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + + switch (portType) + { + case QtNodes::PortType::In: return m_currentOutputIndex.has_value(); + case QtNodes::PortType::Out: return false; + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); } void OutputValue::setInData(std::shared_ptr value, int index) diff --git a/src/ShaderNode/DataModels/OutputValue.hpp b/src/ShaderNode/DataModels/OutputValue.hpp index 12c812c5c..e798b7e82 100644 --- a/src/ShaderNode/DataModels/OutputValue.hpp +++ b/src/ShaderNode/DataModels/OutputValue.hpp @@ -16,7 +16,7 @@ class OutputValue : public ShaderNode void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override { return "Output"; } QString name() const override { return "Output"; } @@ -27,6 +27,9 @@ class OutputValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + void setInData(std::shared_ptr value, int index) override; QtNodes::NodeValidationState validationState() const override; diff --git a/src/ShaderNode/DataModels/PositionOutputValue.cpp b/src/ShaderNode/DataModels/PositionOutputValue.cpp index 6f2ab9d3a..2f9f59716 100644 --- a/src/ShaderNode/DataModels/PositionOutputValue.cpp +++ b/src/ShaderNode/DataModels/PositionOutputValue.cpp @@ -14,12 +14,13 @@ ShaderNode(graph) DisableCustomVariableName(); } -Nz::ShaderNodes::ExpressionPtr PositionOutputValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr PositionOutputValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { using namespace Nz::ShaderBuilder; using namespace Nz::ShaderNodes; assert(count == 1); + assert(outputIndex == 0); auto output = Nz::ShaderBuilder::Identifier(Nz::ShaderBuilder::Builtin(BuiltinEntry::VertexPosition)); return Nz::ShaderBuilder::Assign(std::move(output), *expressions); diff --git a/src/ShaderNode/DataModels/PositionOutputValue.hpp b/src/ShaderNode/DataModels/PositionOutputValue.hpp index 2c2b6eb54..6626226e4 100644 --- a/src/ShaderNode/DataModels/PositionOutputValue.hpp +++ b/src/ShaderNode/DataModels/PositionOutputValue.hpp @@ -14,7 +14,7 @@ class PositionOutputValue : public ShaderNode public: PositionOutputValue(ShaderGraph& graph); - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override { return "PositionOutputValue"; } QString name() const override { return "PositionOutputValue"; } diff --git a/src/ShaderNode/DataModels/SampleTexture.cpp b/src/ShaderNode/DataModels/SampleTexture.cpp index f0eccc770..4d7fcddee 100644 --- a/src/ShaderNode/DataModels/SampleTexture.cpp +++ b/src/ShaderNode/DataModels/SampleTexture.cpp @@ -71,11 +71,12 @@ bool SampleTexture::ComputePreview(QPixmap& pixmap) return true; } -Nz::ShaderNodes::ExpressionPtr SampleTexture::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr SampleTexture::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(m_texture); assert(m_uv); assert(count == 2); + assert(outputIndex == 0); return Nz::ShaderBuilder::Sample2D(expressions[0], expressions[1]); } diff --git a/src/ShaderNode/DataModels/SampleTexture.hpp b/src/ShaderNode/DataModels/SampleTexture.hpp index 5b1dd98fd..d819a0df0 100644 --- a/src/ShaderNode/DataModels/SampleTexture.hpp +++ b/src/ShaderNode/DataModels/SampleTexture.hpp @@ -17,7 +17,7 @@ class SampleTexture : public ShaderNode SampleTexture(ShaderGraph& graph); ~SampleTexture() = default; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override { return "Sample texture"; } QString name() const override { return "SampleTexture"; } diff --git a/src/ShaderNode/DataModels/ShaderNode.cpp b/src/ShaderNode/DataModels/ShaderNode.cpp index e596a6a6d..abda9cd6b 100644 --- a/src/ShaderNode/DataModels/ShaderNode.cpp +++ b/src/ShaderNode/DataModels/ShaderNode.cpp @@ -85,6 +85,11 @@ void ShaderNode::EnablePreview(bool enable) } } +int ShaderNode::GetOutputOrder() const +{ + return 0; +} + QWidget* ShaderNode::embeddedWidget() { if (!m_embeddedWidget) diff --git a/src/ShaderNode/DataModels/ShaderNode.hpp b/src/ShaderNode/DataModels/ShaderNode.hpp index 874fce54f..d3b200768 100644 --- a/src/ShaderNode/DataModels/ShaderNode.hpp +++ b/src/ShaderNode/DataModels/ShaderNode.hpp @@ -18,14 +18,15 @@ class ShaderNode : public QtNodes::NodeDataModel public: ShaderNode(ShaderGraph& graph); + virtual Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const = 0; virtual void BuildNodeEdition(QFormLayout* layout); inline void DisablePreview(); void EnablePreview(bool enable = true); - virtual Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const = 0; inline ShaderGraph& GetGraph(); inline const ShaderGraph& GetGraph() const; + virtual int GetOutputOrder() const; inline const std::string& GetVariableName() const; inline void SetPreviewSize(const Nz::Vector2i& size); diff --git a/src/ShaderNode/DataModels/TextureValue.cpp b/src/ShaderNode/DataModels/TextureValue.cpp index c400d0469..9d0d25665 100644 --- a/src/ShaderNode/DataModels/TextureValue.cpp +++ b/src/ShaderNode/DataModels/TextureValue.cpp @@ -110,12 +110,13 @@ void TextureValue::BuildNodeEdition(QFormLayout* layout) layout->addRow(tr("Texture"), textureSelection); } -Nz::ShaderNodes::ExpressionPtr TextureValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +Nz::ShaderNodes::NodePtr TextureValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { if (!m_currentTextureIndex) throw std::runtime_error("invalid texture input"); assert(count == 0); + assert(outputIndex == 0); const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex); @@ -178,6 +179,32 @@ std::shared_ptr TextureValue::outData(QtNodes::PortIndex port return textureData; } +QString TextureValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::Out); + + if (!m_currentTextureIndex) + return QString(); + + const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex); + return QString::fromStdString(textureEntry.name); +} + +bool TextureValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: return false; + case QtNodes::PortType::Out: return m_currentTextureIndex.has_value(); + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + QtNodes::NodeValidationState TextureValue::validationState() const { if (!m_currentTextureIndex) diff --git a/src/ShaderNode/DataModels/TextureValue.hpp b/src/ShaderNode/DataModels/TextureValue.hpp index d328e2f05..6d6e6ff27 100644 --- a/src/ShaderNode/DataModels/TextureValue.hpp +++ b/src/ShaderNode/DataModels/TextureValue.hpp @@ -18,7 +18,7 @@ class TextureValue : public ShaderNode void BuildNodeEdition(QFormLayout* layout) override; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override { return "Texture"; } QString name() const override { return "Texture"; } @@ -29,6 +29,9 @@ class TextureValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + QtNodes::NodeValidationState validationState() const override; QString validationMessage() const override; diff --git a/src/ShaderNode/DataModels/VecBinOp.cpp b/src/ShaderNode/DataModels/VecBinOp.cpp deleted file mode 100644 index f99591420..000000000 --- a/src/ShaderNode/DataModels/VecBinOp.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include - -QString VecAdd::caption() const -{ - static QString caption = "Vector addition"; - return caption; -} - -QString VecAdd::name() const -{ - static QString name = "vec_add"; - return name; -} - -void VecAdd::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - output[i] = left[i] + right[i]; -} - -QString VecMul::caption() const -{ - static QString caption = "Vector multiplication"; - return caption; -} - -QString VecMul::name() const -{ - static QString name = "vec_mul"; - return name; -} - -void VecMul::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - output[i] = left[i] * right[i]; -} - -QString VecSub::caption() const -{ - static QString caption = "Vector subtraction"; - return caption; -} - - -QString VecSub::name() const -{ - static QString name = "vec_sub"; - return name; -} - -void VecSub::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - output[i] = left[i] - right[i]; -} - -QString VecDiv::caption() const -{ - static QString caption = "Vector divide"; - return caption; -} - - -QString VecDiv::name() const -{ - static QString name = "vec_div"; - return name; -} - -void VecDiv::ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - output[i] = left[i] / right[i]; -} diff --git a/src/ShaderNode/DataModels/VecBinOp.hpp b/src/ShaderNode/DataModels/VecBinOp.hpp deleted file mode 100644 index 3c0a5aebd..000000000 --- a/src/ShaderNode/DataModels/VecBinOp.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#ifndef NAZARA_SHADERNODES_VECBINOP_HPP -#define NAZARA_SHADERNODES_VECBINOP_HPP - -#include -#include - -template -class VecBinOp : public ShaderNode -{ - public: - VecBinOp(ShaderGraph& graph); - ~VecBinOp() = default; - - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; - - unsigned int nPorts(QtNodes::PortType portType) const override; - - QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; - - std::shared_ptr outData(QtNodes::PortIndex port) override; - - void setInData(std::shared_ptr value, int index) override; - - QtNodes::NodeValidationState validationState() const override; - QString validationMessage() const override; - - private: - virtual void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) = 0; - - bool ComputePreview(QPixmap& pixmap) override; - void UpdateOutput(); - - std::shared_ptr m_lhs; - std::shared_ptr m_rhs; - std::shared_ptr m_output; -}; - -class VecAdd : public VecBinOp -{ - public: - using VecBinOp::VecBinOp; - - QString caption() const override; - QString name() const override; - - void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; -}; - -class VecMul : public VecBinOp -{ - public: - using VecBinOp::VecBinOp; - - QString caption() const override; - QString name() const override; - - void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; -}; - -class VecSub : public VecBinOp -{ - public: - using VecBinOp::VecBinOp; - - QString caption() const override; - QString name() const override; - - void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; -}; - -class VecDiv : public VecBinOp -{ - public: - using VecBinOp::VecBinOp; - - QString caption() const override; - QString name() const override; - - void ApplyOp(const Nz::Vector4f* left, const Nz::Vector4f* right, Nz::Vector4f* output, std::size_t pixelCount) override; -}; - -#include - -#endif diff --git a/src/ShaderNode/DataModels/VecBinOp.inl b/src/ShaderNode/DataModels/VecBinOp.inl deleted file mode 100644 index a8b9d6d2d..000000000 --- a/src/ShaderNode/DataModels/VecBinOp.inl +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include - -template -VecBinOp::VecBinOp(ShaderGraph& graph) : -ShaderNode(graph) -{ - UpdateOutput(); -} - -template -Nz::ShaderNodes::ExpressionPtr VecBinOp::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const -{ - assert(count == 2); - using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; - constexpr BuilderType builder; - return builder(expressions[0], expressions[1]); -} - -template -QtNodes::NodeDataType VecBinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const -{ - assert(portIndex == 0 || portIndex == 1); - - return VecData::Type(); -} - -template -unsigned int VecBinOp::nPorts(QtNodes::PortType portType) const -{ - switch (portType) - { - case QtNodes::PortType::In: return 2; - case QtNodes::PortType::Out: return 1; - } - - return 0; -} - -template -std::shared_ptr VecBinOp::outData(QtNodes::PortIndex port) -{ - assert(port == 0); - return m_output; -} - -template -void VecBinOp::setInData(std::shared_ptr value, int index) -{ - assert(index == 0 || index == 1); - - std::shared_ptr castedValue; - if (value && value->type().id == VecData::Type().id) - castedValue = std::static_pointer_cast(value); - - if (index == 0) - m_lhs = std::move(castedValue); - else - m_rhs = std::move(castedValue); - - UpdateOutput(); -} - -template -QtNodes::NodeValidationState VecBinOp::validationState() const -{ - if (!m_lhs || !m_rhs) - return QtNodes::NodeValidationState::Error; - - if (m_lhs->componentCount != m_rhs->componentCount) - return QtNodes::NodeValidationState::Error; - - return QtNodes::NodeValidationState::Valid; -} - -template -QString VecBinOp::validationMessage() const -{ - if (!m_lhs || !m_rhs) - return "Missing operands"; - - if (m_lhs->componentCount != m_rhs->componentCount) - return "Incompatible components count (left has " + QString::number(m_lhs->componentCount) + ", right has " + QString::number(m_rhs->componentCount) + ")"; - - return QString(); -} - -template -bool VecBinOp::ComputePreview(QPixmap& pixmap) -{ - if (!m_lhs || !m_rhs) - return false; - - pixmap = QPixmap::fromImage(m_output->preview.GenerateImage()); - return true; -} - -template -void VecBinOp::UpdateOutput() -{ - if (validationState() != QtNodes::NodeValidationState::Valid) - { - m_output = std::make_shared(4); - m_output->preview = PreviewValues(1, 1); - m_output->preview.Fill(Nz::Vector4f::Zero()); - return; - } - - m_output = std::make_shared(m_lhs->componentCount); - - const PreviewValues& leftPreview = m_lhs->preview; - const PreviewValues& rightPreview = m_rhs->preview; - std::size_t maxWidth = std::max(leftPreview.GetWidth(), rightPreview.GetWidth()); - std::size_t maxHeight = std::max(leftPreview.GetHeight(), rightPreview.GetHeight()); - - // FIXME: Prevent useless copy - PreviewValues leftResized = leftPreview; - if (leftResized.GetWidth() != maxWidth || leftResized.GetHeight() != maxHeight) - leftResized = leftResized.Resized(maxWidth, maxHeight); - - PreviewValues rightResized = rightPreview; - if (rightResized.GetWidth() != maxWidth || rightResized.GetHeight() != maxHeight) - rightResized = rightResized.Resized(maxWidth, maxHeight); - - m_output->preview = PreviewValues(maxWidth, maxHeight); - ApplyOp(leftResized.GetData(), rightResized.GetData(), m_output->preview.GetData(), maxWidth * maxHeight); - - Q_EMIT dataUpdated(0); - - UpdatePreview(); -} diff --git a/src/ShaderNode/DataModels/VecComposition.hpp b/src/ShaderNode/DataModels/VecComposition.hpp new file mode 100644 index 000000000..6a41b1e20 --- /dev/null +++ b/src/ShaderNode/DataModels/VecComposition.hpp @@ -0,0 +1,49 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_VECTOR_COMPOSITION_HPP +#define NAZARA_SHADERNODES_VECTOR_COMPOSITION_HPP + +#include +#include +#include +#include +#include +#include + +template +class VecComposition : public ShaderNode +{ + public: + VecComposition(ShaderGraph& graph); + ~VecComposition() = default; + + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const; + + QString caption() const override; + QString name() const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + unsigned int nPorts(QtNodes::PortType portType) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + void setInData(std::shared_ptr value, int index) override; + + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + + private: + bool ComputePreview(QPixmap& pixmap) override; + void UpdateOutput(); + + std::array, ComponentCount> m_inputs; + std::shared_ptr m_output; +}; + +using Vec2Composition = VecComposition<2>; +using Vec3Composition = VecComposition<3>; +using Vec4Composition = VecComposition<4>; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/VecComposition.inl b/src/ShaderNode/DataModels/VecComposition.inl new file mode 100644 index 000000000..9f17da698 --- /dev/null +++ b/src/ShaderNode/DataModels/VecComposition.inl @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +template +VecComposition::VecComposition(ShaderGraph& graph) : +ShaderNode(graph) +{ + static_assert(ComponentCount <= s_vectorComponents.size()); + + m_output = std::make_shared(ComponentCount); +} + +template +Nz::ShaderNodes::NodePtr VecComposition::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const +{ + assert(count == ComponentCount); + assert(outputIndex == 0); + + std::array expr; + for (std::size_t i = 0; i < count; ++i) + expr[i] = expressions[i]; + + constexpr auto ExpressionType = VecExpressionType; + return Nz::ShaderBuilder::Cast(expr.data(), expr.size()); +} + +template +QString VecComposition::caption() const +{ + static QString caption = "Compose Vector" + QString::number(ComponentCount); + return caption; +} + +template +QString VecComposition::name() const +{ + static QString name = "vec_compose" + QString::number(ComponentCount); + return name; +} + +template +QtNodes::NodeDataType VecComposition::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: + { + assert(portIndex >= 0); + assert(portIndex < ComponentCount); + return FloatData::Type(); + } + + case QtNodes::PortType::Out: + { + assert(portIndex == 0); + return VecData::Type(); + } + + default: break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +template +unsigned int VecComposition::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return ComponentCount; + case QtNodes::PortType::Out: return 1; + + default: break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +template +std::shared_ptr VecComposition::outData(QtNodes::PortIndex port) +{ + assert(port == 0); + + if (validationState() != QtNodes::NodeValidationState::Valid) + return nullptr; + + return m_output; +} + +template +void VecComposition::setInData(std::shared_ptr value, int index) +{ + assert(index >= 0 && index < ComponentCount); + + if (value && value->type().id == FloatData::Type().id) + { + assert(dynamic_cast(value.get()) != nullptr); + m_inputs[index] = std::static_pointer_cast(value); + } + else + m_inputs[index].reset(); + + UpdateOutput(); +} + +template +QtNodes::NodeValidationState VecComposition::validationState() const +{ + for (std::size_t i = 0; i < ComponentCount; ++i) + { + if (!m_inputs[i]) + return QtNodes::NodeValidationState::Error; + } + + return QtNodes::NodeValidationState::Valid; +} + +template +QString VecComposition::validationMessage() const +{ + for (std::size_t i = 0; i < ComponentCount; ++i) + { + if (!m_inputs[i]) + return "Missing input #" + QString::number(i + 1); + } + + return QString(); +} + +template +bool VecComposition::ComputePreview(QPixmap& pixmap) +{ + if (validationState() != QtNodes::NodeValidationState::Valid) + return false; + + pixmap = QPixmap::fromImage(m_output->preview.GenerateImage()); + return true; +} + +template +void VecComposition::UpdateOutput() +{ + if (validationState() != QtNodes::NodeValidationState::Valid) + { + m_output->preview = PreviewValues(1, 1); + m_output->preview(0, 0) = Nz::Vector4f::Zero(); + return; + } + + std::array previewResized; + std::size_t maxInputWidth = 0; + std::size_t maxInputHeight = 0; + + for (std::size_t i = 0; i < ComponentCount; ++i) + { + // FIXME: Prevent useless copy + previewResized[i] = m_inputs[i]->preview; + + maxInputWidth = std::max(maxInputWidth, previewResized[i].GetWidth()); + maxInputHeight = std::max(maxInputHeight, previewResized[i].GetHeight()); + } + + PreviewValues& output = m_output->preview; + output = PreviewValues(maxInputWidth, maxInputHeight); + + for (std::size_t i = 0; i < ComponentCount; ++i) + { + if (previewResized[i].GetWidth() != maxInputWidth || previewResized[i].GetHeight() != maxInputHeight) + previewResized[i] = previewResized[i].Resized(maxInputWidth, maxInputHeight); + } + + for (std::size_t y = 0; y < maxInputHeight; ++y) + { + for (std::size_t x = 0; x < maxInputWidth; ++x) + { + Nz::Vector4f color(0.f, 0.f, 0.f, 1.f); + for (std::size_t i = 0; i < ComponentCount; ++i) + color[i] = previewResized[i](x, y)[0]; + + output(x, y) = color; + } + } + + Q_EMIT dataUpdated(0); + + UpdatePreview(); +} diff --git a/src/ShaderNode/DataModels/VecDecomposition.cpp b/src/ShaderNode/DataModels/VecDecomposition.cpp new file mode 100644 index 000000000..df69df8e6 --- /dev/null +++ b/src/ShaderNode/DataModels/VecDecomposition.cpp @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +VecDecomposition::VecDecomposition(ShaderGraph& graph) : +ShaderNode(graph) +{ + DisablePreview(); + DisableCustomVariableName(); +} + +Nz::ShaderNodes::NodePtr VecDecomposition::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const +{ + assert(count == 1); + assert(outputIndex < m_outputs.size()); + + using namespace Nz::ShaderBuilder; + using namespace Nz::ShaderNodes; + + return Nz::ShaderBuilder::Swizzle(expressions[0], static_cast(Nz::UnderlyingCast(SwizzleComponent::First) + outputIndex)); +} + +QString VecDecomposition::caption() const +{ + return "Vector decomposition"; +} + +QString VecDecomposition::name() const +{ + return "vec_decompose"; +} + +QtNodes::NodeDataType VecDecomposition::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: + { + assert(portIndex == 0); + return VecData::Type(); + } + + case QtNodes::PortType::Out: + { + assert(portIndex >= 0 && portIndex < m_outputs.size()); + return FloatData::Type(); + } + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +unsigned int VecDecomposition::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 1; + case QtNodes::PortType::Out: return m_outputs.size(); + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +QString VecDecomposition::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::Out); + assert(portIndex >= 0 && portIndex < s_vectorComponents.size()); + + return QString(QChar(s_vectorComponents[portIndex])); +} + +bool VecDecomposition::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + switch (portType) + { + case QtNodes::PortType::In: return false; + case QtNodes::PortType::Out: return true; + + default: + break; + } + + assert(false); + throw std::runtime_error("Invalid port type"); +} + +std::shared_ptr VecDecomposition::outData(QtNodes::PortIndex port) +{ + if (!m_input) + return {}; + + return m_outputs[port]; +} + +void VecDecomposition::setInData(std::shared_ptr value, int index) +{ + assert(index == 0); + + std::shared_ptr castedValue; + if (value && value->type().id == VecData::Type().id) + castedValue = std::static_pointer_cast(value); + + m_input = std::move(castedValue); + + UpdateOutputs(); +} + +QtNodes::NodeValidationState VecDecomposition::validationState() const +{ + if (!m_input) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString VecDecomposition::validationMessage() const +{ + if (!m_input) + return "Missing input"; + + return QString(); +} + +void VecDecomposition::UpdateOutputs() +{ + if (validationState() != QtNodes::NodeValidationState::Valid) + { + auto dummy = std::make_shared(); + dummy->preview = PreviewValues(1, 1); + dummy->preview.Fill(Nz::Vector4f::Zero()); + + m_outputs.fill(dummy); + return; + } + + std::size_t previewWidth = m_input->preview.GetWidth(); + std::size_t previewHeight = m_input->preview.GetHeight(); + std::size_t pixelCount = previewWidth * previewHeight; + + for (std::size_t i = 0; i < m_input->componentCount; ++i) + { + m_outputs[i] = std::make_shared(); + m_outputs[i]->preview = PreviewValues(previewWidth, previewHeight); + + const Nz::Vector4f* inputData = m_input->preview.GetData(); + Nz::Vector4f* outputData = m_outputs[i]->preview.GetData(); + for (std::size_t j = 0; j < pixelCount; ++j) + { + const Nz::Vector4f& input = *inputData++; + + *outputData++ = Nz::Vector4f(input[i], input[i], input[i], input[i]); + } + + Q_EMIT dataUpdated(i); + } + + for (std::size_t i = m_input->componentCount; i < m_outputs.size(); ++i) + m_outputs[i] = nullptr; + + UpdatePreview(); +} diff --git a/src/ShaderNode/DataModels/VecDecomposition.hpp b/src/ShaderNode/DataModels/VecDecomposition.hpp new file mode 100644 index 000000000..c50c95f40 --- /dev/null +++ b/src/ShaderNode/DataModels/VecDecomposition.hpp @@ -0,0 +1,48 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_VECTOR_DECOMPOSITION_HPP +#define NAZARA_SHADERNODES_VECTOR_DECOMPOSITION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +class VecDecomposition : public ShaderNode +{ + public: + VecDecomposition(ShaderGraph& graph); + ~VecDecomposition() = default; + + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + + QString caption() const override; + QString name() const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + unsigned int nPorts(QtNodes::PortType portType) const override; + + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + void setInData(std::shared_ptr value, int index) override; + + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + + private: + void UpdateOutputs(); + + std::shared_ptr m_input; + std::array, 4> m_outputs; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/Cast.cpp b/src/ShaderNode/DataModels/VecDecomposition.inl similarity index 100% rename from src/ShaderNode/DataModels/Cast.cpp rename to src/ShaderNode/DataModels/VecDecomposition.inl diff --git a/src/ShaderNode/DataModels/VecDot.cpp b/src/ShaderNode/DataModels/VecDot.cpp index 516e1b19b..e327cf06b 100644 --- a/src/ShaderNode/DataModels/VecDot.cpp +++ b/src/ShaderNode/DataModels/VecDot.cpp @@ -8,9 +8,11 @@ ShaderNode(graph) UpdateOutput(); } -Nz::ShaderNodes::ExpressionPtr VecDot::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr VecDot::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); + assert(outputIndex == 0); + using namespace Nz::ShaderNodes; return IntrinsicCall::Build(IntrinsicType::DotProduct, { expressions[0], expressions[1] }); } diff --git a/src/ShaderNode/DataModels/VecDot.hpp b/src/ShaderNode/DataModels/VecDot.hpp index 74e6a7266..251694ae8 100644 --- a/src/ShaderNode/DataModels/VecDot.hpp +++ b/src/ShaderNode/DataModels/VecDot.hpp @@ -13,7 +13,7 @@ class VecDot : public ShaderNode VecDot(ShaderGraph& graph); ~VecDot() = default; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override; QString name() const override; diff --git a/src/ShaderNode/DataModels/VecFloatMul.cpp b/src/ShaderNode/DataModels/VecFloatMul.cpp index 06ee951c6..33237dc25 100644 --- a/src/ShaderNode/DataModels/VecFloatMul.cpp +++ b/src/ShaderNode/DataModels/VecFloatMul.cpp @@ -7,9 +7,11 @@ ShaderNode(graph) UpdateOutput(); } -Nz::ShaderNodes::ExpressionPtr VecFloatMul::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const +Nz::ShaderNodes::NodePtr VecFloatMul::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); + assert(outputIndex == 0); + using namespace Nz::ShaderNodes; return BinaryOp::Build(BinaryType::Multiply, expressions[0], expressions[1]); } diff --git a/src/ShaderNode/DataModels/VecFloatMul.hpp b/src/ShaderNode/DataModels/VecFloatMul.hpp index dd1fbd1af..b7759d4c7 100644 --- a/src/ShaderNode/DataModels/VecFloatMul.hpp +++ b/src/ShaderNode/DataModels/VecFloatMul.hpp @@ -13,7 +13,7 @@ class VecFloatMul : public ShaderNode VecFloatMul(ShaderGraph& graph); ~VecFloatMul() = default; - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; QString caption() const override; QString name() const override; diff --git a/src/ShaderNode/DataModels/VecValue.cpp b/src/ShaderNode/DataModels/VecValue.cpp deleted file mode 100644 index ede5bdfac..000000000 --- a/src/ShaderNode/DataModels/VecValue.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/src/ShaderNode/DataModels/VecValue.hpp b/src/ShaderNode/DataModels/VecValue.hpp index 86cd93692..3fcebb638 100644 --- a/src/ShaderNode/DataModels/VecValue.hpp +++ b/src/ShaderNode/DataModels/VecValue.hpp @@ -18,6 +18,9 @@ class VecValue : public ShaderNode VecValue(ShaderGraph& graph); ~VecValue() = default; + Nz::ShaderNodes::NodePtr BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const override; + void BuildNodeEdition(QFormLayout* layout) override; + QString caption() const override; QString name() const override; @@ -27,9 +30,8 @@ class VecValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; - void BuildNodeEdition(QFormLayout* layout) override; - - Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const override; + QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; private: bool ComputePreview(QPixmap& pixmap) override; diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl index b07c46a9b..ef5ad0622 100644 --- a/src/ShaderNode/DataModels/VecValue.inl +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -75,6 +75,33 @@ std::shared_ptr VecValue::outData(QtNodes::Po return out; } +template +QString VecValue::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + assert(portType == QtNodes::PortType::Out); + + QString caption = "vec" + QString::number(ComponentCount) + "("; + for (std::size_t i = 0; i < ComponentCount; ++i) + { + if (i > 0) + caption += ", "; + + caption += QString::number(m_value[i]); + } + + caption += ")"; + + return caption; +} + +template +bool VecValue::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portIndex == 0); + return portType == QtNodes::PortType::Out; +} + template void VecValue::BuildNodeEdition(QFormLayout* layout) { @@ -100,9 +127,10 @@ void VecValue::BuildNodeEdition(QFormLayout* layout) } template -Nz::ShaderNodes::ExpressionPtr VecValue::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +Nz::ShaderNodes::NodePtr VecValue::BuildNode(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count, std::size_t outputIndex) const { assert(count == 0); + assert(outputIndex == 0); return Nz::ShaderBuilder::Constant(m_value); } diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index c0c575493..d385b098d 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -1,19 +1,23 @@ #include #include +#include +#include #include #include +#include #include #include #include #include +#include +#include #include +#include #include #include #include -#include -#include -#include -#include +#include +#include #include #include #include @@ -61,27 +65,6 @@ m_type(ShaderType::NotSet) AddInput("UV", PrimitiveType::Float2, InputRole::TexCoord, 0, 0); AddOutput("RenderTarget0", PrimitiveType::Float4, 0); AddTexture("Potato", TextureType::Sampler2D, 1); - AddStruct("TestStruct", { - { - { "position", PrimitiveType::Float3 }, - { "normal", PrimitiveType::Float3 }, - { "uv", PrimitiveType::Float2 }, - { "inner", 2 } - } - }); - AddStruct("InnerStruct", { - { - { "a", PrimitiveType::Float3 }, - } - }); - AddStruct("OuterStruct", { - { - { "a", 1 }, - { "b", PrimitiveType::Float1 } - } - }); - - AddBuffer("testUBO", BufferType::UniformBufferObject, 0, 0); UpdateTexturePreview(0, QImage(R"(C:\Users\Lynix\Pictures\potatavril.png)")); @@ -460,23 +443,30 @@ QJsonObject ShaderGraph::Save() Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() { std::vector statements; - QHash usageCount; - std::function DetectVariables; - DetectVariables = [&](QtNodes::Node* node) + using Key = QPair; + auto BuildKey = [](QUuid uuid, std::size_t index) { - auto it = usageCount.find(node->id()); + return Key(uuid, index); + }; + + QHash usageCount; + + std::function DetectVariables; + DetectVariables = [&](QtNodes::Node* node, std::size_t outputIndex) + { + auto it = usageCount.find(BuildKey(node->id(), outputIndex)); if (it == usageCount.end()) { for (const auto& connectionSet : node->nodeState().getEntries(QtNodes::PortType::In)) { for (const auto& [uuid, conn] : connectionSet) { - DetectVariables(conn->getNode(QtNodes::PortType::Out)); + DetectVariables(conn->getNode(QtNodes::PortType::Out), conn->getPortIndex(QtNodes::PortType::Out)); } } - it = usageCount.insert(node->id(), 0); + it = usageCount.insert(BuildKey(node->id(), outputIndex), 0); } (*it)++; @@ -488,28 +478,28 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() { if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) { - DetectVariables(node); + DetectVariables(node, 0); outputNodes.push_back(node); } }); - QHash variableExpressions; + QHash variableExpressions; unsigned int varCount = 0; std::unordered_set usedVariableNames; - std::function HandleNode; - HandleNode = [&](QtNodes::Node* node) -> Nz::ShaderNodes::ExpressionPtr + std::function HandleNode; + HandleNode = [&](QtNodes::Node* node, std::size_t portIndex) -> Nz::ShaderNodes::NodePtr { ShaderNode* shaderNode = static_cast(node->nodeDataModel()); if (shaderNode->validationState() != QtNodes::NodeValidationState::Valid) throw std::runtime_error(shaderNode->validationMessage().toStdString()); qDebug() << shaderNode->name() << node->id(); - if (auto it = variableExpressions.find(node->id()); it != variableExpressions.end()) + if (auto it = variableExpressions.find(BuildKey(node->id(), portIndex)); it != variableExpressions.end()) return *it; - auto it = usageCount.find(node->id()); + auto it = usageCount.find(BuildKey(node->id(), portIndex)); assert(it != usageCount.end()); std::size_t inputCount = shaderNode->nPorts(QtNodes::PortType::In); @@ -521,16 +511,25 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() for (const auto& [uuid, conn] : connectionSet) { assert(i < expressions.size()); - expressions[i] = HandleNode(conn->getNode(QtNodes::PortType::Out)); + Nz::ShaderNodes::NodePtr inputNode = HandleNode(conn->getNode(QtNodes::PortType::Out), conn->getPortIndex(QtNodes::PortType::Out)); + if (inputNode->IsStatement()) + throw std::runtime_error("unexpected statement"); + + expressions[i] = std::static_pointer_cast(inputNode); i++; } } - auto expression = shaderNode->GetExpression(expressions.data(), expressions.size()); + auto astNode = shaderNode->BuildNode(expressions.data(), expressions.size(), portIndex); const std::string& variableName = shaderNode->GetVariableName(); if (*it > 1 || !variableName.empty()) { + if (astNode->IsStatement()) + throw std::runtime_error("unexpected statement"); + + auto expression = std::static_pointer_cast(astNode); + Nz::ShaderNodes::ExpressionPtr varExpression; if (expression->GetExpressionCategory() == Nz::ShaderNodes::ExpressionCategory::RValue) { @@ -546,23 +545,37 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst() usedVariableNames.insert(name); auto variable = Nz::ShaderBuilder::Local(std::move(name), expression->GetExpressionType()); - statements.emplace_back(Nz::ShaderBuilder::DeclareVariable(variable, expression)); + statements.emplace_back(Nz::ShaderBuilder::DeclareVariable(variable, std::move(expression))); varExpression = Nz::ShaderBuilder::Identifier(variable); } else - varExpression = expression; + varExpression = std::move(expression); - variableExpressions.insert(node->id(), varExpression); + variableExpressions.insert(BuildKey(node->id(), portIndex), varExpression); return varExpression; } else - return expression; + return astNode; }; + std::sort(outputNodes.begin(), outputNodes.end(), [](QtNodes::Node* lhs, QtNodes::Node* rhs) + { + ShaderNode* leftNode = static_cast(lhs->nodeDataModel()); + ShaderNode* rightNode = static_cast(rhs->nodeDataModel()); + + return leftNode->GetOutputOrder() < rightNode->GetOutputOrder(); + }); + for (QtNodes::Node* node : outputNodes) - statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); + { + auto astNode = HandleNode(node, 0); + if (!astNode->IsStatement()) + statements.emplace_back(Nz::ShaderBuilder::ExprStatement(std::static_pointer_cast(astNode))); + else + statements.emplace_back(std::static_pointer_cast(astNode)); + } return Nz::ShaderNodes::StatementBlock::Build(std::move(statements)); } @@ -738,31 +751,74 @@ Nz::ShaderStageType ShaderGraph::ToShaderStageType(ShaderType type) std::shared_ptr ShaderGraph::BuildRegistry() { auto registry = std::make_shared(); - RegisterShaderNode(*this, registry, "Inputs"); + + // Casts RegisterShaderNode(*this, registry, "Casts"); RegisterShaderNode(*this, registry, "Casts"); RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Shader"); - RegisterShaderNode(*this, registry, "Outputs"); + + // Constants + RegisterShaderNode(*this, registry, "Constants"); RegisterShaderNode(*this, registry, "Constants"); + RegisterShaderNode(*this, registry, "Constants"); + RegisterShaderNode(*this, registry, "Constants"); + RegisterShaderNode(*this, registry, "Constants"); + + // Inputs + RegisterShaderNode(*this, registry, "Inputs"); RegisterShaderNode(*this, registry, "Inputs"); - RegisterShaderNode(*this, registry, "Outputs"); + + // Outputs + RegisterShaderNode(*this, registry, "Outputs"); RegisterShaderNode(*this, registry, "Outputs"); - RegisterShaderNode(*this, registry, "Texture"); - RegisterShaderNode(*this, registry, "Texture"); + RegisterShaderNode(*this, registry, "Outputs"); + + // Float comparison + RegisterShaderNode(*this, registry, "Float comparisons"); + RegisterShaderNode(*this, registry, "Float comparisons"); + RegisterShaderNode(*this, registry, "Float comparisons"); + RegisterShaderNode(*this, registry, "Float comparisons"); + RegisterShaderNode(*this, registry, "Float comparisons"); + RegisterShaderNode(*this, registry, "Float comparisons"); + + // Float operations + RegisterShaderNode(*this, registry, "Float operations"); + RegisterShaderNode(*this, registry, "Float operations"); + RegisterShaderNode(*this, registry, "Float operations"); + RegisterShaderNode(*this, registry, "Float operations"); + + // Matrix operations RegisterShaderNode(*this, registry, "Matrix operations"); RegisterShaderNode(*this, registry, "Matrix operations"); RegisterShaderNode(*this, registry, "Matrix operations"); RegisterShaderNode(*this, registry, "Matrix operations"); + + // Shader + RegisterShaderNode(*this, registry, "Shader"); + + // Texture + RegisterShaderNode(*this, registry, "Texture"); + RegisterShaderNode(*this, registry, "Texture"); + + // Vector comparison + RegisterShaderNode(*this, registry, "Vector comparisons"); + RegisterShaderNode(*this, registry, "Vector comparisons"); + RegisterShaderNode(*this, registry, "Vector comparisons"); + RegisterShaderNode(*this, registry, "Vector comparisons"); + RegisterShaderNode(*this, registry, "Vector comparisons"); + RegisterShaderNode(*this, registry, "Vector comparisons"); + + // Vector operations RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Vector operations"); - RegisterShaderNode(*this, registry, "Constants"); - RegisterShaderNode(*this, registry, "Constants"); - RegisterShaderNode(*this, registry, "Constants"); return registry; } diff --git a/src/ShaderNode/Widgets/ConditionEditor.cpp b/src/ShaderNode/Widgets/ConditionEditor.cpp index 2b8c40ab8..e16527254 100644 --- a/src/ShaderNode/Widgets/ConditionEditor.cpp +++ b/src/ShaderNode/Widgets/ConditionEditor.cpp @@ -112,6 +112,8 @@ void ConditionEditor::RefreshConditions() checkbox->setCheckable(true); checkbox->setCheckState((conditionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); - m_model->setItem(rowIndex, 1, checkbox); + m_model->setItem(rowIndex, 1, checkbox); + + rowIndex++; } } diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index a9bfcc59f..aa01c3c97 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -184,10 +184,10 @@ void MainWindow::OnGenerateGLSL() Nz::GlslWriter writer; Nz::GlslWriter::States states; - for (const auto& condition : m_shaderGraph.GetConditions()) + for (std::size_t i = 0; i < m_shaderGraph.GetConditionCount(); ++i) { - if (condition.enabled) - states.enabledConditions.insert(condition.name); + if (m_shaderGraph.IsConditionEnabled(i)) + states.enabledConditions = Nz::SetBit(states.enabledConditions, i); } std::string glsl = writer.Generate(ToShader(), states);