diff --git a/include/Nazara/Renderer/ShaderAst.hpp b/include/Nazara/Renderer/ShaderAst.hpp index 28b808d6c..08e8e20b2 100644 --- a/include/Nazara/Renderer/ShaderAst.hpp +++ b/include/Nazara/Renderer/ShaderAst.hpp @@ -258,6 +258,7 @@ namespace Nz { public: inline Cast(ExpressionType castTo, ExpressionPtr first, ExpressionPtr second = nullptr, ExpressionPtr third = nullptr, ExpressionPtr fourth = nullptr); + inline Cast(ExpressionType castTo, ExpressionPtr* expressions, std::size_t expressionCount); ExpressionType GetExpressionType() const override; void Register(ShaderWriter& visitor) override; @@ -265,6 +266,9 @@ namespace Nz ExpressionType exprType; std::array expressions; + + private: + void Validate() const; }; class NAZARA_RENDERER_API Constant : public Expression diff --git a/include/Nazara/Renderer/ShaderAst.inl b/include/Nazara/Renderer/ShaderAst.inl index ea9e5cddf..e837cbdb4 100644 --- a/include/Nazara/Renderer/ShaderAst.inl +++ b/include/Nazara/Renderer/ShaderAst.inl @@ -169,19 +169,16 @@ namespace Nz exprType(castTo), expressions({ {first, second, third, fourth} }) { - unsigned int componentCount = 0; - unsigned int requiredComponents = GetComponentCount(exprType); - for (const auto& exprPtr : expressions) - { - if (!exprPtr) - break; + Validate(); + } - componentCount += GetComponentCount(exprPtr->GetExpressionType()); - } + inline Cast::Cast(ExpressionType castTo, ExpressionPtr* Expressions, std::size_t expressionCount) : + exprType(castTo) + { + for (std::size_t i = 0; i < expressionCount; ++i) + expressions[i] = Expressions[i]; - //TODO: AstParseError - if (componentCount != requiredComponents) - throw std::runtime_error("Component count doesn't match required component count"); + Validate(); } inline Constant::Constant(bool value) : diff --git a/src/Nazara/Renderer/ShaderAst.cpp b/src/Nazara/Renderer/ShaderAst.cpp index 0899cbdc7..a0a0cc031 100644 --- a/src/Nazara/Renderer/ShaderAst.cpp +++ b/src/Nazara/Renderer/ShaderAst.cpp @@ -200,6 +200,23 @@ namespace Nz::ShaderAst visitor.Write(*this); } + void Cast::Validate() const + { + unsigned int componentCount = 0; + unsigned int requiredComponents = GetComponentCount(exprType); + for (const auto& exprPtr : expressions) + { + if (!exprPtr) + break; + + componentCount += GetComponentCount(exprPtr->GetExpressionType()); + } + + //TODO: AstParseError + if (componentCount != requiredComponents) + throw std::runtime_error("Component count doesn't match required component count"); + } + ExpressionCategory SwizzleOp::GetExpressionCategory() const { diff --git a/src/ShaderNode/DataModels/Cast.hpp b/src/ShaderNode/DataModels/Cast.hpp index c1b63e8e0..90dad931e 100644 --- a/src/ShaderNode/DataModels/Cast.hpp +++ b/src/ShaderNode/DataModels/Cast.hpp @@ -10,7 +10,7 @@ #include #include -template +template class CastVec : public ShaderNode { public: @@ -31,25 +31,21 @@ class CastVec : public ShaderNode void setInData(std::shared_ptr value, int index) override; - private: - static constexpr std::size_t FromComponents = From::ComponentCount; - static constexpr std::size_t ToComponents = To::ComponentCount; - static constexpr std::size_t ComponentDiff = (ToComponents >= FromComponents) ? ToComponents - FromComponents : 0; + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + private: bool ComputePreview(QPixmap& pixmap) override; void UpdateOutput(); - VecType m_overflowComponents; - std::shared_ptr m_input; - std::shared_ptr m_output; + std::shared_ptr m_input; + std::shared_ptr m_output; + VecType m_overflowComponents; }; -using CastVec2ToVec3 = CastVec; -using CastVec2ToVec4 = CastVec; -using CastVec3ToVec2 = CastVec; -using CastVec3ToVec4 = CastVec; -using CastVec4ToVec2 = CastVec; -using CastVec4ToVec3 = CastVec; +using CastToVec2 = CastVec<2>; +using CastToVec3 = CastVec<3>; +using CastToVec4 = CastVec<4>; #include diff --git a/src/ShaderNode/DataModels/Cast.inl b/src/ShaderNode/DataModels/Cast.inl index 56866aa3e..64ed934a2 100644 --- a/src/ShaderNode/DataModels/Cast.inl +++ b/src/ShaderNode/DataModels/Cast.inl @@ -4,21 +4,32 @@ #include #include -template -CastVec::CastVec(ShaderGraph& graph) : +template +CastVec::CastVec(ShaderGraph& graph) : ShaderNode(graph) { - static_assert(ComponentDiff <= s_vectorComponents.size()); + static_assert(ToComponentCount <= s_vectorComponents.size()); + + m_overflowComponents.fill(0.f); + + m_output = std::make_shared(ToComponentCount); } -template -void CastVec::BuildNodeEdition(QFormLayout* layout) +template +void CastVec::BuildNodeEdition(QFormLayout* layout) { ShaderNode::BuildNodeEdition(layout); - if constexpr (ComponentDiff > 0) + if (!m_input) + return; + + std::size_t fromComponentCount = m_input->componentCount; + + if (ToComponentCount > fromComponentCount) { - for (std::size_t i = 0; i < ComponentDiff; ++i) + std::size_t overflowComponentCount = ToComponentCount - fromComponentCount; + + for (std::size_t i = 0; i < overflowComponentCount; ++i) { QDoubleSpinBox* spinbox = new QDoubleSpinBox; spinbox->setDecimals(6); @@ -30,31 +41,36 @@ void CastVec::BuildNodeEdition(QFormLayout* layout) UpdateOutput(); }); - layout->addRow(QString::fromUtf8(&s_vectorComponents[FromComponents + i], 1), spinbox); + layout->addRow(QString::fromUtf8(&s_vectorComponents[fromComponentCount + i], 1), spinbox); } } } -template -Nz::ShaderAst::ExpressionPtr CastVec::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const +template +Nz::ShaderAst::ExpressionPtr CastVec::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const { + assert(m_input); assert(count == 1); - if constexpr (ComponentDiff > 0) - { - std::array constants; - for (std::size_t i = 0; i < ComponentDiff; ++i) - constants[i] = Nz::ShaderBuilder::Constant(m_overflowComponents[i]); + std::size_t fromComponentCount = m_input->componentCount; - return std::apply([&](auto&&... values) - { - return Nz::ShaderBuilder::Cast(expressions[0], values...); //< TODO: Forward - }, constants); - } - else + if (ToComponentCount > fromComponentCount) { - std::array swizzleComponents; - for (std::size_t i = 0; i < ToComponents; ++i) + 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(), overflowComponentCount); + } + else if (ToComponentCount < fromComponentCount) + { + std::array swizzleComponents; + for (std::size_t i = 0; i < ToComponentCount; ++i) swizzleComponents[i] = static_cast(static_cast(Nz::ShaderAst::SwizzleComponent::First) + i); return std::apply([&](auto... components) @@ -63,39 +79,41 @@ Nz::ShaderAst::ExpressionPtr CastVec::GetExpression(Nz::ShaderAst::Exp return Nz::ShaderBuilder::Swizzle(expressions[0], componentList); }, swizzleComponents); } + else + return expressions[0]; //< no-op } -template -QString CastVec::caption() const +template +QString CastVec::caption() const { - static QString caption = From::Type().name + " to " + To::Type().name; + static QString caption = "To Vector" + QString::number(ToComponentCount); return caption; } -template -QString CastVec::name() const +template +QString CastVec::name() const { - static QString name = From::Type().id + "to" + To::Type().id; + static QString name = "cast_vec" + QString::number(ToComponentCount); return name; } -template -QtNodes::NodeDataType CastVec::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +template +QtNodes::NodeDataType CastVec::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { assert(portIndex == 0); switch (portType) { - case QtNodes::PortType::In: return From::Type(); - case QtNodes::PortType::Out: return To::Type(); + case QtNodes::PortType::In: return VecData::Type(); + case QtNodes::PortType::Out: return VecData::Type(); } assert(false); throw std::runtime_error("Invalid port type"); } -template -unsigned int CastVec::nPorts(QtNodes::PortType portType) const +template +unsigned int CastVec::nPorts(QtNodes::PortType portType) const { switch (portType) { @@ -106,8 +124,8 @@ unsigned int CastVec::nPorts(QtNodes::PortType portType) const return 0; } -template -std::shared_ptr CastVec::outData(QtNodes::PortIndex port) +template +std::shared_ptr CastVec::outData(QtNodes::PortIndex port) { assert(port == 0); @@ -117,14 +135,14 @@ std::shared_ptr CastVec::outData(QtNodes::PortIndex return m_output; } -template -void CastVec::setInData(std::shared_ptr value, int index) +template +void CastVec::setInData(std::shared_ptr value, int index) { assert(index == 0); if (value) { - assert(dynamic_cast(value.get()) != nullptr); - m_input = std::static_pointer_cast(value); + assert(dynamic_cast(value.get()) != nullptr); + m_input = std::static_pointer_cast(value); } else m_input.reset(); @@ -132,8 +150,26 @@ void CastVec::setInData(std::shared_ptr value, int UpdateOutput(); } -template -bool CastVec::ComputePreview(QPixmap& pixmap) +template +QtNodes::NodeValidationState CastVec::validationState() const +{ + if (!m_input) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +template +QString CastVec::validationMessage() const +{ + if (!m_input) + return "Missing input"; + + return QString(); +} + +template +bool CastVec::ComputePreview(QPixmap& pixmap) { if (!m_input) return false; @@ -142,8 +178,8 @@ bool CastVec::ComputePreview(QPixmap& pixmap) return true; } -template -void CastVec::UpdateOutput() +template +void CastVec::UpdateOutput() { if (!m_input) { @@ -160,10 +196,15 @@ void CastVec::UpdateOutput() QImage& output = m_output->preview; output = QImage(inputWidth, inputHeight, QImage::Format_RGBA8888); - std::array constants; - if constexpr (ComponentDiff > 0) + std::size_t fromComponentCount = m_input->componentCount; + std::size_t commonComponents = std::min(fromComponentCount, ToComponentCount); + std::size_t overflowComponentCount = (ToComponentCount > fromComponentCount) ? ToComponentCount - fromComponentCount : 0; + std::size_t voidComponents = 4 - overflowComponentCount - commonComponents; + + std::array constants; + if (ToComponentCount > fromComponentCount) { - for (std::size_t i = 0; i < ComponentDiff; ++i) + for (std::size_t i = 0; i < overflowComponentCount; ++i) constants[i] = static_cast(std::clamp(int(m_overflowComponents[i] * 255), 0, 255)); } @@ -173,17 +214,14 @@ void CastVec::UpdateOutput() { for (int x = 0; x < inputWidth; ++x) { - constexpr std::size_t CommonComponents = std::min(FromComponents, ToComponents); - constexpr std::size_t VoidComponents = 4 - ComponentDiff - CommonComponents; - - for (std::size_t i = 0; i < CommonComponents; ++i) + for (std::size_t i = 0; i < commonComponents; ++i) *outputPtr++ = inputPtr[i]; - for (std::size_t i = 0; i < ComponentDiff; ++i) + for (std::size_t i = 0; i < overflowComponentCount; ++i) *outputPtr++ = constants[i]; - for (std::size_t i = 0; i < VoidComponents; ++i) - *outputPtr++ = (i == VoidComponents - 1) ? 255 : 0; + for (std::size_t i = 0; i < voidComponents; ++i) + *outputPtr++ = (i == voidComponents - 1) ? 255 : 0; inputPtr += 4; } diff --git a/src/ShaderNode/DataModels/InputValue.cpp b/src/ShaderNode/DataModels/InputValue.cpp index 9ff7fd207..6defcb30f 100644 --- a/src/ShaderNode/DataModels/InputValue.cpp +++ b/src/ShaderNode/DataModels/InputValue.cpp @@ -121,16 +121,17 @@ auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portInd assert(portIndex == 0); if (!m_currentInputIndex) - return Vec4Data::Type(); + return VecData::Type(); const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex); switch (inputEntry.type) { //case InputType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; //case InputType::Float1: return Nz::ShaderAst::ExpressionType::Float1; - case InOutType::Float2: return Vec2Data::Type(); - case InOutType::Float3: return Vec3Data::Type(); - case InOutType::Float4: return Vec4Data::Type(); + case InOutType::Float2: + case InOutType::Float3: + case InOutType::Float4: + return VecData::Type(); } assert(false); @@ -148,8 +149,24 @@ std::shared_ptr InputValue::outData(QtNodes::PortIndex port) const auto& inputEntry = graph.GetInput(*m_currentInputIndex); const auto& preview = graph.GetPreviewModel(); - auto vecData = std::make_shared(); + auto vecData = std::make_shared(GetComponentCount(inputEntry.type)); vecData->preview = preview.GetImage(inputEntry.role, inputEntry.roleIndex); return vecData; } + +QtNodes::NodeValidationState InputValue::validationState() const +{ + if (!m_currentInputIndex) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString InputValue::validationMessage() const +{ + if (!m_currentInputIndex) + return "No input selected"; + + return QString(); +} diff --git a/src/ShaderNode/DataModels/InputValue.hpp b/src/ShaderNode/DataModels/InputValue.hpp index 0170221f5..4b4c1b31c 100644 --- a/src/ShaderNode/DataModels/InputValue.hpp +++ b/src/ShaderNode/DataModels/InputValue.hpp @@ -30,6 +30,9 @@ class InputValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + private: bool ComputePreview(QPixmap& pixmap) override; void OnInputListUpdate(); diff --git a/src/ShaderNode/DataModels/OutputValue.cpp b/src/ShaderNode/DataModels/OutputValue.cpp index 2d3a8dc14..04eeafd48 100644 --- a/src/ShaderNode/DataModels/OutputValue.cpp +++ b/src/ShaderNode/DataModels/OutputValue.cpp @@ -87,16 +87,17 @@ QtNodes::NodeDataType OutputValue::dataType(QtNodes::PortType portType, QtNodes: assert(portIndex == 0); if (!m_currentOutputIndex) - return Vec4Data::Type(); + return VecData::Type(); const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); switch (outputEntry.type) { //case InOutType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; //case InOutType::Float1: return Nz::ShaderAst::ExpressionType::Float1; - case InOutType::Float2: return Vec2Data::Type(); - case InOutType::Float3: return Vec3Data::Type(); - case InOutType::Float4: return Vec4Data::Type(); + case InOutType::Float2: + case InOutType::Float3: + case InOutType::Float4: + return VecData::Type(); } assert(false); @@ -124,8 +125,8 @@ void OutputValue::setInData(std::shared_ptr value, int index) assert(index == 0); if (value) { - assert(dynamic_cast(value.get()) != nullptr); - m_input = std::static_pointer_cast(value); + assert(dynamic_cast(value.get()) != nullptr); + m_input = std::static_pointer_cast(value); } else m_input.reset(); @@ -133,6 +134,35 @@ void OutputValue::setInData(std::shared_ptr value, int index) UpdatePreview(); } +QtNodes::NodeValidationState OutputValue::validationState() const +{ + if (!m_currentOutputIndex || !m_input) + return QtNodes::NodeValidationState::Error; + + const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); + if (GetComponentCount(outputEntry.type) != m_input->componentCount) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString OutputValue::validationMessage() const +{ + if (!m_currentOutputIndex) + return "No output selected"; + + if (!m_input) + return "Missing input"; + + const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); + std::size_t outputComponentCount = GetComponentCount(outputEntry.type); + + if (m_input->componentCount != outputComponentCount) + return "Incompatible component count (expected " + QString::number(outputComponentCount) + ", got " + QString::number(m_input->componentCount) + ")"; + + return QString(); +} + bool OutputValue::ComputePreview(QPixmap& pixmap) { if (!m_input) diff --git a/src/ShaderNode/DataModels/OutputValue.hpp b/src/ShaderNode/DataModels/OutputValue.hpp index 8db611056..1e7055da5 100644 --- a/src/ShaderNode/DataModels/OutputValue.hpp +++ b/src/ShaderNode/DataModels/OutputValue.hpp @@ -29,6 +29,9 @@ class OutputValue : public ShaderNode 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 OnOutputListUpdate(); diff --git a/src/ShaderNode/DataModels/SampleTexture.cpp b/src/ShaderNode/DataModels/SampleTexture.cpp index e0fa852c3..970e8a965 100644 --- a/src/ShaderNode/DataModels/SampleTexture.cpp +++ b/src/ShaderNode/DataModels/SampleTexture.cpp @@ -5,7 +5,7 @@ SampleTexture::SampleTexture(ShaderGraph& graph) : ShaderNode(graph) { - m_output = std::make_shared(); + m_output = std::make_shared(4); UpdateOutput(); } @@ -82,9 +82,8 @@ bool SampleTexture::ComputePreview(QPixmap& pixmap) Nz::ShaderAst::ExpressionPtr SampleTexture::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const { - if (!m_texture || !m_uv) - throw std::runtime_error("invalid inputs"); - + assert(m_texture); + assert(m_uv); assert(count == 2); return Nz::ShaderBuilder::Sample2D(expressions[0], expressions[1]); @@ -99,7 +98,7 @@ auto SampleTexture::dataType(QtNodes::PortType portType, QtNodes::PortIndex port switch (portIndex) { case 0: return Texture2Data::Type(); - case 1: return Vec2Data::Type(); + case 1: return VecData::Type(); } assert(false); @@ -109,7 +108,7 @@ auto SampleTexture::dataType(QtNodes::PortType portType, QtNodes::PortIndex port case QtNodes::PortType::Out: { assert(portIndex == 0); - return Vec4Data::Type(); + return VecData::Type(); } default: @@ -180,9 +179,9 @@ void SampleTexture::setInData(std::shared_ptr value, int inde { if (value) { - assert(dynamic_cast(value.get()) != nullptr); + assert(dynamic_cast(value.get()) != nullptr); - m_uv = std::static_pointer_cast(value); + m_uv = std::static_pointer_cast(value); } else m_uv.reset(); @@ -197,3 +196,28 @@ void SampleTexture::setInData(std::shared_ptr value, int inde UpdateOutput(); } + +QtNodes::NodeValidationState SampleTexture::validationState() const +{ + if (!m_texture || !m_uv) + return QtNodes::NodeValidationState::Error; + + if (m_uv->componentCount != 2) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString SampleTexture::validationMessage() const +{ + if (!m_texture) + return "Missing texture"; + + if (!m_uv) + return "Missing uv"; + + if (m_uv->componentCount != 2) + return "Incompatible UV (expected 2, got " + QString::number(m_uv->componentCount) + ")"; + + return QString(); +} diff --git a/src/ShaderNode/DataModels/SampleTexture.hpp b/src/ShaderNode/DataModels/SampleTexture.hpp index 7a24409d0..7afb77e0f 100644 --- a/src/ShaderNode/DataModels/SampleTexture.hpp +++ b/src/ShaderNode/DataModels/SampleTexture.hpp @@ -34,13 +34,16 @@ class SampleTexture : public ShaderNode void setInData(std::shared_ptr value, int index) override; + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + protected: bool ComputePreview(QPixmap& pixmap) override; void UpdateOutput(); std::shared_ptr m_texture; - std::shared_ptr m_uv; - std::shared_ptr m_output; + std::shared_ptr m_uv; + std::shared_ptr m_output; }; #include diff --git a/src/ShaderNode/DataModels/TextureValue.cpp b/src/ShaderNode/DataModels/TextureValue.cpp index 976ec7dd7..f78a610a9 100644 --- a/src/ShaderNode/DataModels/TextureValue.cpp +++ b/src/ShaderNode/DataModels/TextureValue.cpp @@ -92,7 +92,7 @@ void TextureValue::BuildNodeEdition(QFormLayout* layout) Nz::ShaderAst::ExpressionPtr TextureValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const { if (!m_currentTextureIndex) - throw std::runtime_error("invalid inputs"); + throw std::runtime_error("invalid texture input"); assert(count == 0); @@ -117,7 +117,7 @@ auto TextureValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portI assert(portType == QtNodes::PortType::Out); assert(portIndex == 0); - return Vec4Data::Type(); + return VecData::Type(); } std::shared_ptr TextureValue::outData(QtNodes::PortIndex port) @@ -145,3 +145,19 @@ std::shared_ptr TextureValue::outData(QtNodes::PortIndex port return textureData; } + +QtNodes::NodeValidationState TextureValue::validationState() const +{ + if (!m_currentTextureIndex) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString TextureValue::validationMessage() const +{ + if (!m_currentTextureIndex) + return "No texture selected"; + + return QString(); +} diff --git a/src/ShaderNode/DataModels/TextureValue.hpp b/src/ShaderNode/DataModels/TextureValue.hpp index 27ae9474a..fdf355828 100644 --- a/src/ShaderNode/DataModels/TextureValue.hpp +++ b/src/ShaderNode/DataModels/TextureValue.hpp @@ -29,6 +29,9 @@ class TextureValue : public ShaderNode std::shared_ptr outData(QtNodes::PortIndex port) override; + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + protected: bool ComputePreview(QPixmap& pixmap) override; void OnTextureListUpdate(); diff --git a/src/ShaderNode/DataModels/VecBinOp.cpp b/src/ShaderNode/DataModels/VecBinOp.cpp index fd2e0d5c1..8f63c2642 100644 --- a/src/ShaderNode/DataModels/VecBinOp.cpp +++ b/src/ShaderNode/DataModels/VecBinOp.cpp @@ -1 +1,73 @@ #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 std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + unsigned int lValue = left[i]; + unsigned int rValue = right[i]; + + output[i] = static_cast(std::min(lValue + rValue, 255U)); + } +} + +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 std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + unsigned int lValue = left[i]; + unsigned int rValue = right[i]; + + output[i] = static_cast(lValue * rValue / 255); + } +} + +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 std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) +{ + for (std::size_t i = 0; i < pixelCount; ++i) + { + unsigned int lValue = left[i]; + unsigned int rValue = right[i]; + + unsigned int sub = (lValue >= rValue) ? lValue - rValue : 0u; + + output[i] = static_cast(sub); + } +} diff --git a/src/ShaderNode/DataModels/VecBinOp.hpp b/src/ShaderNode/DataModels/VecBinOp.hpp index 6fa8fe875..f8bebc667 100644 --- a/src/ShaderNode/DataModels/VecBinOp.hpp +++ b/src/ShaderNode/DataModels/VecBinOp.hpp @@ -6,7 +6,7 @@ #include #include -template +template class VecBinOp : public ShaderNode { public: @@ -29,16 +29,15 @@ class VecBinOp : public ShaderNode bool ComputePreview(QPixmap& pixmap) override; void UpdateOutput(); - std::shared_ptr m_lhs; - std::shared_ptr m_rhs; - std::shared_ptr m_output; + std::shared_ptr m_lhs; + std::shared_ptr m_rhs; + std::shared_ptr m_output; }; -template -class VecAdd : public VecBinOp +class VecAdd : public VecBinOp { public: - using VecBinOp::VecBinOp; + using VecBinOp::VecBinOp; QString caption() const override; QString name() const override; @@ -46,11 +45,10 @@ class VecAdd : public VecBinOp void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) override; }; -template -class VecMul : public VecBinOp +class VecMul : public VecBinOp { public: - using VecBinOp::VecBinOp; + using VecBinOp::VecBinOp; QString caption() const override; QString name() const override; @@ -58,11 +56,10 @@ class VecMul : public VecBinOp void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) override; }; -template -class VecSub : public VecBinOp +class VecSub : public VecBinOp { public: - using VecBinOp::VecBinOp; + using VecBinOp::VecBinOp; QString caption() const override; QString name() const override; @@ -70,18 +67,6 @@ class VecSub : public VecBinOp void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) override; }; -using Vec2Add = VecAdd; -using Vec3Add = VecAdd; -using Vec4Add = VecAdd; - -using Vec2Mul = VecMul; -using Vec3Mul = VecMul; -using Vec4Mul = VecMul; - -using Vec2Sub = VecSub; -using Vec3Sub = VecSub; -using Vec4Sub = VecSub; - #include #endif diff --git a/src/ShaderNode/DataModels/VecBinOp.inl b/src/ShaderNode/DataModels/VecBinOp.inl index 2d33f65c6..7d3cf1e12 100644 --- a/src/ShaderNode/DataModels/VecBinOp.inl +++ b/src/ShaderNode/DataModels/VecBinOp.inl @@ -1,17 +1,15 @@ #include #include -template -VecBinOp::VecBinOp(ShaderGraph& graph) : +template +VecBinOp::VecBinOp(ShaderGraph& graph) : ShaderNode(graph) { - m_output = std::make_shared(); - UpdateOutput(); } -template -Nz::ShaderAst::ExpressionPtr VecBinOp::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const +template +Nz::ShaderAst::ExpressionPtr VecBinOp::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const { assert(count == 2); using BuilderType = typename Nz::ShaderBuilder::template BinOpBuilder; @@ -19,16 +17,16 @@ Nz::ShaderAst::ExpressionPtr VecBinOp::GetExpression(Nz::ShaderAst: return builder(expressions[0], expressions[1]); } -template -QtNodes::NodeDataType VecBinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const +template +QtNodes::NodeDataType VecBinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const { assert(portIndex == 0 || portIndex == 1); - return Data::Type(); + return VecData::Type(); } -template -unsigned int VecBinOp::nPorts(QtNodes::PortType portType) const +template +unsigned int VecBinOp::nPorts(QtNodes::PortType portType) const { switch (portType) { @@ -39,24 +37,24 @@ unsigned int VecBinOp::nPorts(QtNodes::PortType portType) const return 0; } -template -std::shared_ptr VecBinOp::outData(QtNodes::PortIndex port) +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) +template +void VecBinOp::setInData(std::shared_ptr value, int index) { assert(index == 0 || index == 1); - std::shared_ptr castedValue; + std::shared_ptr castedValue; if (value) { - assert(dynamic_cast(value.get()) != nullptr); + assert(dynamic_cast(value.get()) != nullptr); - castedValue = std::static_pointer_cast(value); + castedValue = std::static_pointer_cast(value); } if (index == 0) @@ -67,8 +65,8 @@ void VecBinOp::setInData(std::shared_ptr value, UpdateOutput(); } -template -bool VecBinOp::ComputePreview(QPixmap& pixmap) +template +bool VecBinOp::ComputePreview(QPixmap& pixmap) { if (!m_lhs || !m_rhs) return false; @@ -77,16 +75,19 @@ bool VecBinOp::ComputePreview(QPixmap& pixmap) return true; } -template -void VecBinOp::UpdateOutput() +template +void VecBinOp::UpdateOutput() { - if (!m_lhs || !m_rhs) + if (!m_lhs || !m_rhs || m_lhs->componentCount != m_rhs->componentCount) { + m_output = std::make_shared(4); m_output->preview = QImage(1, 1, QImage::Format_RGBA8888); m_output->preview.fill(QColor::fromRgb(0, 0, 0, 0)); return; } + m_output = std::make_shared(m_lhs->componentCount); + const QImage& leftPreview = m_lhs->preview; const QImage& rightPreview = m_rhs->preview; int maxWidth = std::max(leftPreview.width(), rightPreview.width()); @@ -108,83 +109,3 @@ void VecBinOp::UpdateOutput() UpdatePreview(); } - -template -QString VecAdd::caption() const -{ - static QString caption = Data::Type().name + " addition"; - return caption; -} - -template -QString VecAdd::name() const -{ - static QString name = Data::Type().name + "add"; - return name; -} - -template -void VecAdd::ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - { - unsigned int lValue = left[i]; - unsigned int rValue = right[i]; - - output[i] = static_cast(std::min(lValue + rValue, 255U)); - } -} - -template -QString VecMul::caption() const -{ - static QString caption = Data::Type().name + " multiplication"; - return caption; -} - -template -QString VecMul::name() const -{ - static QString name = Data::Type().name + "mul"; - return name; -} - -template -void VecMul::ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - { - unsigned int lValue = left[i]; - unsigned int rValue = right[i]; - - output[i] = static_cast(lValue * rValue / 255); - } -} - -template -QString VecSub::caption() const -{ - static QString caption = Data::Type().name + " subtraction"; - return caption; -} - -template -QString VecSub::name() const -{ - static QString name = Data::Type().name + "sub"; - return name; -} - -template -void VecSub::ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) -{ - for (std::size_t i = 0; i < pixelCount; ++i) - { - unsigned int lValue = left[i]; - unsigned int rValue = right[i]; - - unsigned int sub = (lValue >= rValue) ? lValue - rValue : 0u; - - output[i] = static_cast(sub); - } -} diff --git a/src/ShaderNode/DataModels/VecValue.hpp b/src/ShaderNode/DataModels/VecValue.hpp index 50ff1f4e1..b115477e3 100644 --- a/src/ShaderNode/DataModels/VecValue.hpp +++ b/src/ShaderNode/DataModels/VecValue.hpp @@ -11,7 +11,7 @@ #include #include -template +template class VecValue : public ShaderNode { public: @@ -34,16 +34,14 @@ class VecValue : public ShaderNode private: bool ComputePreview(QPixmap& pixmap) override; - static constexpr std::size_t ComponentCount = Data::ComponentCount; - QColor ToColor() const; VecType m_value; }; -using Vec2Value = VecValue; -using Vec3Value = VecValue; -using Vec4Value = VecValue; +using Vec2Value = VecValue<2>; +using Vec3Value = VecValue<3>; +using Vec4Value = VecValue<4>; #include diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl index 7f3a9dc5e..fd6d31be1 100644 --- a/src/ShaderNode/DataModels/VecValue.inl +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -5,8 +5,8 @@ #include #include -template -VecValue::VecValue(ShaderGraph& graph) : +template +VecValue::VecValue(ShaderGraph& graph) : ShaderNode(graph) { static_assert(ComponentCount <= s_vectorComponents.size()); @@ -21,55 +21,55 @@ ShaderNode(graph) UpdatePreview(); } -template -QString VecValue::caption() const +template +QString VecValue::caption() const { - static QString caption = Data::Type().name + " constant"; + static QString caption = "Vector" + QString::number(ComponentCount) + " constant"; return caption; } -template -QString VecValue::name() const +template +QString VecValue::name() const { - static QString name = Data::Type().id + "Value"; + static QString name = "vec" + QString::number(ComponentCount) + "_constant"; return name; } -template -QtNodes::NodeDataType VecValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +template +QtNodes::NodeDataType VecValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { assert(portType == QtNodes::PortType::Out); assert(portIndex == 0); - return Data::Type(); + return VecData::Type(); } -template -unsigned int VecValue::nPorts(QtNodes::PortType portType) const +template +unsigned int VecValue::nPorts(QtNodes::PortType portType) const { switch (portType) { - case QtNodes::PortType::In: return 0; + case QtNodes::PortType::In: return 0; case QtNodes::PortType::Out: return 1; } return 0; } -template -std::shared_ptr VecValue::outData(QtNodes::PortIndex port) +template +std::shared_ptr VecValue::outData(QtNodes::PortIndex port) { assert(port == 0); - auto out = std::make_shared(); + auto out = std::make_shared(ComponentCount); out->preview = QImage(1, 1, QImage::Format_RGBA8888); out->preview.fill(ToColor()); return out; } -template -void VecValue::BuildNodeEdition(QFormLayout* layout) +template +void VecValue::BuildNodeEdition(QFormLayout* layout) { ShaderNode::BuildNodeEdition(layout); @@ -91,23 +91,23 @@ void VecValue::BuildNodeEdition(QFormLayout* layout) } } -template -Nz::ShaderAst::ExpressionPtr VecValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const +template +Nz::ShaderAst::ExpressionPtr VecValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const { assert(count == 0); return Nz::ShaderBuilder::Constant(m_value); } -template -bool VecValue::ComputePreview(QPixmap& pixmap) +template +bool VecValue::ComputePreview(QPixmap& pixmap) { pixmap.fill(ToColor()); return true; } -template -QColor VecValue::ToColor() const +template +QColor VecValue::ToColor() const { std::array values = { 0.f, 0.f, 0.f, 1.f }; diff --git a/src/ShaderNode/DataTypes/FloatData.cpp b/src/ShaderNode/DataTypes/FloatData.cpp new file mode 100644 index 000000000..0b1f99d61 --- /dev/null +++ b/src/ShaderNode/DataTypes/FloatData.cpp @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataTypes/FloatData.hpp b/src/ShaderNode/DataTypes/FloatData.hpp new file mode 100644 index 000000000..e0f0451d2 --- /dev/null +++ b/src/ShaderNode/DataTypes/FloatData.hpp @@ -0,0 +1,28 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_FLOATDATA_HPP +#define NAZARA_SHADERNODES_FLOATDATA_HPP + +#include +#include + +struct FloatData : public QtNodes::NodeData +{ + inline FloatData(); + + QtNodes::NodeDataType type() const override + { + return Type(); + } + + static QtNodes::NodeDataType Type() + { + return { "float", "Float" }; + } + + QImage preview; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataTypes/FloatData.inl b/src/ShaderNode/DataTypes/FloatData.inl new file mode 100644 index 000000000..3960c5811 --- /dev/null +++ b/src/ShaderNode/DataTypes/FloatData.inl @@ -0,0 +1,7 @@ +#include + +inline FloatData::FloatData() : +preview(1, 1, QImage::Format_RGBA8888) +{ + preview.fill(QColor::fromRgb(255, 255, 255, 0)); +} diff --git a/src/ShaderNode/DataTypes/VecData.cpp b/src/ShaderNode/DataTypes/VecData.cpp index 0b1f99d61..c1f883079 100644 --- a/src/ShaderNode/DataTypes/VecData.cpp +++ b/src/ShaderNode/DataTypes/VecData.cpp @@ -1 +1,18 @@ #include +#include +#include + +Nz::ShaderAst::ExpressionType VecData::GetExpressionType() const +{ + switch (componentCount) + { + case 2: return Nz::ShaderAst::ExpressionType::Float2; + case 3: return Nz::ShaderAst::ExpressionType::Float3; + case 4: return Nz::ShaderAst::ExpressionType::Float4; + default: + break; + } + + assert(false); + throw std::runtime_error("invalid component count"); +} diff --git a/src/ShaderNode/DataTypes/VecData.hpp b/src/ShaderNode/DataTypes/VecData.hpp index 447d37686..5c19be942 100644 --- a/src/ShaderNode/DataTypes/VecData.hpp +++ b/src/ShaderNode/DataTypes/VecData.hpp @@ -9,59 +9,48 @@ struct VecData : public QtNodes::NodeData { - inline VecData(); + inline VecData(std::size_t componentCount); + inline QtNodes::NodeDataType type() const override; + + Nz::ShaderAst::ExpressionType GetExpressionType() const; + + static inline QtNodes::NodeDataType Type(); + + std::size_t componentCount; QImage preview; }; -struct Vec2Data : public VecData +template +struct VecExpressionTypeHelper; + +template<> +struct VecExpressionTypeHelper<1> +{ + static constexpr Nz::ShaderAst::ExpressionType ExpressionType = Nz::ShaderAst::ExpressionType::Float1; +}; + +template<> +struct VecExpressionTypeHelper<2> { - static constexpr std::size_t ComponentCount = 2; static constexpr Nz::ShaderAst::ExpressionType ExpressionType = Nz::ShaderAst::ExpressionType::Float2; - - QtNodes::NodeDataType type() const override - { - return Type(); - } - - static QtNodes::NodeDataType Type() - { - return { "vec2", "Vec2" }; - } }; -struct Vec3Data : public VecData +template<> +struct VecExpressionTypeHelper<3> { - static constexpr std::size_t ComponentCount = 3; static constexpr Nz::ShaderAst::ExpressionType ExpressionType = Nz::ShaderAst::ExpressionType::Float3; - - QtNodes::NodeDataType type() const override - { - return Type(); - } - - static QtNodes::NodeDataType Type() - { - return { "vec3", "Vec3" }; - } }; -struct Vec4Data : public VecData +template<> +struct VecExpressionTypeHelper<4> { - static constexpr std::size_t ComponentCount = 4; static constexpr Nz::ShaderAst::ExpressionType ExpressionType = Nz::ShaderAst::ExpressionType::Float4; - - QtNodes::NodeDataType type() const override - { - return Type(); - } - - static QtNodes::NodeDataType Type() - { - return { "vec4", "Vec4" }; - } }; +template constexpr Nz::ShaderAst::ExpressionType VecExpressionType = VecExpressionTypeHelper::template ExpressionType; + + struct VecTypeDummy {}; template @@ -76,7 +65,7 @@ struct VecTypeHelper<0> template<> struct VecTypeHelper<1> { - using Type = std::array; + using Type = std::array; //< To allow [0] }; template<> diff --git a/src/ShaderNode/DataTypes/VecData.inl b/src/ShaderNode/DataTypes/VecData.inl index 255fbd9ab..a84b5c344 100644 --- a/src/ShaderNode/DataTypes/VecData.inl +++ b/src/ShaderNode/DataTypes/VecData.inl @@ -1,7 +1,18 @@ #include -inline VecData::VecData() : +inline VecData::VecData(std::size_t ComponentCount) : +componentCount(ComponentCount), preview(64, 64, QImage::Format_RGBA8888) { preview.fill(QColor::fromRgb(255, 255, 255, 0)); } + +inline QtNodes::NodeDataType VecData::type() const +{ + return Type(); +} + +inline QtNodes::NodeDataType VecData::Type() +{ + return { "vector", "Vector" }; +} diff --git a/src/ShaderNode/Enums.cpp b/src/ShaderNode/Enums.cpp index fd99c5562..1f756b8de 100644 --- a/src/ShaderNode/Enums.cpp +++ b/src/ShaderNode/Enums.cpp @@ -1,6 +1,21 @@ #include #include +std::size_t GetComponentCount(InOutType type) +{ + switch (type) + { + case InOutType::Bool: return 1; + case InOutType::Float1: return 1; + case InOutType::Float2: return 2; + case InOutType::Float3: return 3; + case InOutType::Float4: return 4; + } + + assert(false); + return 0; +} + const char* EnumToString(InputRole role) { switch (role) diff --git a/src/ShaderNode/Enums.hpp b/src/ShaderNode/Enums.hpp index ba990fdcf..f67623a3f 100644 --- a/src/ShaderNode/Enums.hpp +++ b/src/ShaderNode/Enums.hpp @@ -39,6 +39,8 @@ enum class TextureType constexpr std::size_t TextureTypeCount = static_cast(TextureType::Max) + 1; + +std::size_t GetComponentCount(InOutType type); const char* EnumToString(InputRole role); const char* EnumToString(InOutType input); const char* EnumToString(TextureType textureType); diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 876b0f1a3..63a29a6a0 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -58,7 +58,7 @@ m_flowScene(BuildRegistry()) auto& node3 = m_flowScene.createNode(std::make_unique(*this)); node3.nodeGraphicsObject().setPos(200, 200); - auto& node4 = m_flowScene.createNode(std::make_unique(*this)); + auto& node4 = m_flowScene.createNode(std::make_unique(*this)); node4.nodeGraphicsObject().setPos(400, 200); auto& node5 = m_flowScene.createNode(std::make_unique(*this)); @@ -154,6 +154,8 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() HandleNode = [&](QtNodes::Node* node) -> Nz::ShaderAst::ExpressionPtr { 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()) @@ -211,21 +213,13 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() return expression; }; - try + m_flowScene.iterateOverNodes([&](QtNodes::Node* node) { - m_flowScene.iterateOverNodes([&](QtNodes::Node* node) + if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) { - if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) - { - statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); - } - }); - } - catch (const std::exception&) - { - - return nullptr; - } + statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); + } + }); return std::make_shared(std::move(statements)); } @@ -265,25 +259,16 @@ void ShaderGraph::UpdateTexturePreview(std::size_t textureIndex, QImage preview) std::shared_ptr ShaderGraph::BuildRegistry() { auto registry = std::make_shared(); - RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Casts"); + RegisterShaderNode(*this, registry, "Casts"); + RegisterShaderNode(*this, registry, "Casts"); + RegisterShaderNode(*this, registry, "Casts"); RegisterShaderNode(*this, registry, "Inputs"); RegisterShaderNode(*this, registry, "Outputs"); RegisterShaderNode(*this, registry, "Texture"); RegisterShaderNode(*this, registry, "Texture"); - 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, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); RegisterShaderNode(*this, registry, "Constants"); RegisterShaderNode(*this, registry, "Constants"); RegisterShaderNode(*this, registry, "Constants"); diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index ca0bd4c0a..505c1f285 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -83,15 +84,22 @@ void MainWindow::BuildMenu() void MainWindow::OnCompileToGLSL() { - Nz::GlslWriter writer; - Nz::String glsl = writer.Generate(m_shaderGraph.ToAst()); + try + { + Nz::GlslWriter writer; + Nz::String glsl = writer.Generate(m_shaderGraph.ToAst()); - std::cout << glsl << std::endl; + std::cout << glsl << std::endl; - QTextEdit* output = new QTextEdit; - output->setReadOnly(true); - output->setText(QString::fromUtf8(glsl.GetConstBuffer(), int(glsl.GetSize()))); - output->setAttribute(Qt::WA_DeleteOnClose, true); - output->setWindowTitle("GLSL Output"); - output->show(); + QTextEdit* output = new QTextEdit; + output->setReadOnly(true); + output->setText(QString::fromUtf8(glsl.GetConstBuffer(), int(glsl.GetSize()))); + output->setAttribute(Qt::WA_DeleteOnClose, true); + output->setWindowTitle("GLSL Output"); + output->show(); + } + catch (const std::exception& e) + { + QMessageBox::critical(this, tr("Compilation failed"), QString("Compilation failed: ") + e.what()); + } }