diff --git a/include/Nazara/Renderer/GlslWriter.hpp b/include/Nazara/Renderer/GlslWriter.hpp new file mode 100644 index 000000000..121f50ab7 --- /dev/null +++ b/include/Nazara/Renderer/GlslWriter.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GLSLWRITER_HPP +#define NAZARA_GLSLWRITER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_RENDERER_API GlslWriter : public ShaderWriter + { + public: + GlslWriter(); + GlslWriter(const GlslWriter&) = delete; + GlslWriter(GlslWriter&&) = delete; + ~GlslWriter() = default; + + Nz::String Generate(const ShaderAst::StatementPtr& node) override; + + void RegisterFunction(const String& name, ShaderAst::StatementPtr statement, std::initializer_list parameters, ShaderAst::ExpressionType ret) override; + void RegisterVariable(const String& name, ShaderAst::ExpressionType type) override; + + void Write(const ShaderAst::AssignOp& node) override; + void Write(const ShaderAst::Branch& node) override; + void Write(const ShaderAst::BinaryOp& node) override; + void Write(const ShaderAst::Constant& node) override; + void Write(const ShaderAst::ExpressionStatement& node) override; + void Write(const ShaderAst::NodePtr& node) override; + void Write(const ShaderAst::StatementBlock& node) override; + void Write(const ShaderAst::Variable& node) override; + + private: + struct Function; + + void Append(ShaderAst::ExpressionType type); + void Append(const String& txt); + void AppendFunction(const Function& func); + void AppendLine(const Nz::String& txt = Nz::String()); + + void EnterScope(); + void LeaveScope(); + + struct Function + { + std::vector parameters; + ShaderAst::ExpressionType retType; + ShaderAst::StatementPtr node; + String name; + }; + + struct State + { + std::set> m_variables; + StringStream stream; + unsigned int indentLevel = 0; + }; + + std::unordered_map m_functions; + State* m_currentState; + }; +} + +#endif // NAZARA_GLSLWRITER_HPP diff --git a/include/Nazara/Renderer/ShaderAst.hpp b/include/Nazara/Renderer/ShaderAst.hpp new file mode 100644 index 000000000..be6ac2c29 --- /dev/null +++ b/include/Nazara/Renderer/ShaderAst.hpp @@ -0,0 +1,197 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADER_AST_HPP +#define NAZARA_SHADER_AST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class ShaderWriter; + + namespace ShaderAst + { + enum class AssignType + { + Simple //< = + }; + + enum class BinaryType + { + Add, //< + + Substract, //< - + Multiply, //< * + Divide, //< / + Equality //< == + }; + + enum class ExpressionType + { + Float1, // float + Float2, // vec2 + Float3, // vec3 + Float4, // vec4 + + None // void + }; + + enum class VariableType + { + Parameter, + Variable, + Uniform + }; + + ////////////////////////////////////////////////////////////////////////// + + class Node; + + using NodePtr = std::shared_ptr; + + class NAZARA_RENDERER_API Node + { + public: + virtual ~Node() = default; + + virtual void Register(ShaderWriter& visitor) = 0; + virtual void Visit(ShaderWriter& visitor) = 0; + }; + + class Statement; + + using StatementPtr = std::shared_ptr; + + class NAZARA_RENDERER_API Statement : public Node + { + }; + + class Expression; + + using ExpressionPtr = std::shared_ptr; + + class NAZARA_RENDERER_API Expression : public Node + { + }; + + class NAZARA_RENDERER_API ExpressionStatement : public Statement + { + public: + inline explicit ExpressionStatement(ExpressionPtr expr); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + ExpressionPtr expression; + }; + + ////////////////////////////////////////////////////////////////////////// + + class NAZARA_RENDERER_API StatementBlock : public Statement + { + public: + template explicit StatementBlock(Args&&... args); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + std::vector statements; + }; + + class Variable; + + using VariablePtr = std::shared_ptr; + + class NAZARA_RENDERER_API Variable : public Expression + { + public: + inline Variable(VariableType varKind, const Nz::String& varName, ExpressionType varType); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + ExpressionType type; + VariableType kind; + Nz::String name; + }; + + ////////////////////////////////////////////////////////////////////////// + + class NAZARA_RENDERER_API AssignOp : public Expression + { + public: + inline AssignOp(AssignType Op, VariablePtr Var, ExpressionPtr Right); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + AssignType op; + VariablePtr variable; + ExpressionPtr right; + }; + + class NAZARA_RENDERER_API BinaryOp : public Expression + { + public: + inline BinaryOp(BinaryType Op, ExpressionPtr Left, ExpressionPtr Right); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + BinaryType op; + ExpressionPtr left; + ExpressionPtr right; + }; + + class NAZARA_RENDERER_API Branch : public Statement + { + public: + inline Branch(ExpressionPtr condition, StatementPtr trueStatement, StatementPtr falseStatement = nullptr); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + struct ConditionalStatement + { + ExpressionPtr condition; + StatementPtr statement; + }; + + std::vector condStatements; + StatementPtr elseStatement; + }; + + class NAZARA_RENDERER_API Constant : public Expression + { + public: + inline explicit Constant(float value); + + void Register(ShaderWriter& visitor) override; + void Visit(ShaderWriter& visitor) override; + + ExpressionType exprType; + + union + { + float vec1; + Nz::Vector2f vec2; + Nz::Vector3f vec3; + Nz::Vector4f vec4; + } values; + }; + } +} + +#include + +#endif // NAZARA_SHADER_AST_HPP diff --git a/include/Nazara/Renderer/ShaderAst.inl b/include/Nazara/Renderer/ShaderAst.inl new file mode 100644 index 000000000..cb98bbdac --- /dev/null +++ b/include/Nazara/Renderer/ShaderAst.inl @@ -0,0 +1,58 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace ShaderAst + { + inline ExpressionStatement::ExpressionStatement(ExpressionPtr expr) : + expression(std::move(expr)) + { + } + + template + StatementBlock::StatementBlock(Args&& ...args) : + statements({std::forward(args)...}) + { + } + + inline Variable::Variable(VariableType varKind, const Nz::String& varName, ExpressionType varType) : + kind(varKind), + name(varName), + type(varType) + { + } + + inline AssignOp::AssignOp(AssignType Op, VariablePtr Var, ExpressionPtr Right) : + op(Op), + variable(std::move(Var)), + right(std::move(Right)) + { + } + + inline BinaryOp::BinaryOp(BinaryType Op, ExpressionPtr Left, ExpressionPtr Right) : + op(Op), + left(std::move(Left)), + right(std::move(Right)) + { + } + + inline Branch::Branch(ExpressionPtr condition, StatementPtr trueStatement, StatementPtr falseStatement) + { + condStatements.emplace_back(ConditionalStatement{ std::move(condition), std::move(trueStatement) }); + elseStatement = std::move(falseStatement); + } + + inline Constant::Constant(float value) : + exprType(ExpressionType::Float1) + { + values.vec1 = value; + } + } +} + +#include diff --git a/include/Nazara/Renderer/ShaderBuilder.hpp b/include/Nazara/Renderer/ShaderBuilder.hpp new file mode 100644 index 000000000..75d43ae8b --- /dev/null +++ b/include/Nazara/Renderer/ShaderBuilder.hpp @@ -0,0 +1,70 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADER_BUILDER_HPP +#define NAZARA_SHADER_BUILDER_HPP + +#include +#include +#include + +namespace Nz { namespace ShaderBuilder +{ + template + class GenBuilder + { + public: + template + std::shared_ptr operator()(Args&&... args) const + { + return std::make_shared(std::forward(args)...); + } + }; + + template + class AssignOpBuilder + { + public: + std::shared_ptr operator()(const Nz::ShaderAst::VariablePtr& left, const Nz::ShaderAst::ExpressionPtr& right) const + { + return std::make_shared(op, left, right); + } + }; + + template + class BinOpBuilder + { + public: + std::shared_ptr operator()(const Nz::ShaderAst::ExpressionPtr& left, const Nz::ShaderAst::ExpressionPtr& right) const + { + return std::make_shared(op, left, right); + } + }; + + template + class VarBuilder + { + public: + template + std::shared_ptr operator()(Args&&... args) const + { + return std::make_shared(type, std::forward(args)...); + } + }; + + constexpr BinOpBuilder Add; + constexpr AssignOpBuilder Assign; + constexpr BinOpBuilder Equal; + constexpr GenBuilder Block; + constexpr GenBuilder Branch; + constexpr GenBuilder Constant; + constexpr GenBuilder ExprStatement; + constexpr VarBuilder Variable; +} } + +#include + +#endif // NAZARA_SHADER_BUILDER_HPP diff --git a/include/Nazara/Renderer/ShaderBuilder.inl b/include/Nazara/Renderer/ShaderBuilder.inl new file mode 100644 index 000000000..94ab292ec --- /dev/null +++ b/include/Nazara/Renderer/ShaderBuilder.inl @@ -0,0 +1,15 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace ShaderAst + { + } +} + +#include diff --git a/include/Nazara/Renderer/ShaderWriter.hpp b/include/Nazara/Renderer/ShaderWriter.hpp new file mode 100644 index 000000000..4e006ffb4 --- /dev/null +++ b/include/Nazara/Renderer/ShaderWriter.hpp @@ -0,0 +1,40 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADERWRITER_HPP +#define NAZARA_SHADERWRITER_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_RENDERER_API ShaderWriter + { + public: + ShaderWriter() = default; + ShaderWriter(const ShaderWriter&) = delete; + ShaderWriter(ShaderWriter&&) = delete; + virtual ~ShaderWriter(); + + virtual Nz::String Generate(const ShaderAst::StatementPtr& node) = 0; + + virtual void RegisterFunction(const String& name, ShaderAst::StatementPtr node, std::initializer_list parameters, ShaderAst::ExpressionType ret) = 0; + virtual void RegisterVariable(const String& name, ShaderAst::ExpressionType type) = 0; + + virtual void Write(const ShaderAst::AssignOp& node) = 0; + virtual void Write(const ShaderAst::Branch& node) = 0; + virtual void Write(const ShaderAst::BinaryOp& node) = 0; + virtual void Write(const ShaderAst::Constant& node) = 0; + virtual void Write(const ShaderAst::ExpressionStatement& node) = 0; + virtual void Write(const ShaderAst::NodePtr& node) = 0; + virtual void Write(const ShaderAst::StatementBlock& node) = 0; + virtual void Write(const ShaderAst::Variable& node) = 0; + }; +} + +#endif // NAZARA_SHADERWRITER_HPP diff --git a/src/Nazara/Renderer/GlslWriter.cpp b/src/Nazara/Renderer/GlslWriter.cpp new file mode 100644 index 000000000..bbb542e21 --- /dev/null +++ b/src/Nazara/Renderer/GlslWriter.cpp @@ -0,0 +1,253 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + GlslWriter::GlslWriter() : + m_currentState(nullptr) + { + } + + String GlslWriter::Generate(const ShaderAst::StatementPtr& node) + { + State state; + m_currentState = &state; + CallOnExit onExit([this]() + { + m_currentState = nullptr; + }); + + node->Register(*this); + + for (const auto& pair : state.m_variables) + { + Append(pair.first); + Append(" "); + Append(pair.second); + AppendLine(";"); + } + + AppendLine(); + + Function entryPoint; + entryPoint.name = "main"; //< GLSL has only one entry point name possible + entryPoint.node = node; + entryPoint.retType = ShaderAst::ExpressionType::None; + + AppendFunction(entryPoint); + + return state.stream; + } + + void GlslWriter::RegisterFunction(const String& name, ShaderAst::StatementPtr statement, std::initializer_list parameters, ShaderAst::ExpressionType retType) + { + Function func; + func.retType = retType; + func.name = name; + func.node = std::move(statement); + func.parameters.assign(parameters); + + m_functions[name] = std::move(func); + } + + void GlslWriter::RegisterVariable(const String& name, ShaderAst::ExpressionType type) + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + m_currentState->m_variables.insert(std::make_pair(type, name)); + } + + void GlslWriter::Write(const ShaderAst::NodePtr& node) + { + node->Visit(*this); + } + + void GlslWriter::Write(const ShaderAst::AssignOp& node) + { + Write(node.variable); + + switch (node.op) + { + case ShaderAst::AssignType::Simple: + Append(" = "); + break; + } + + Write(node.right); + } + + void GlslWriter::Write(const ShaderAst::Branch& node) + { + bool first = true; + for (const auto& statement : node.condStatements) + { + if (!first) + Append("else "); + + Append("if ("); + Write(statement.condition); + AppendLine(")"); + + EnterScope(); + Write(statement.statement); + LeaveScope(); + + first = false; + } + + if (node.elseStatement) + { + AppendLine("else"); + + EnterScope(); + Write(node.elseStatement); + LeaveScope(); + } + } + + void GlslWriter::Write(const ShaderAst::BinaryOp& node) + { + Write(node.left); + + switch (node.op) + { + case ShaderAst::BinaryType::Add: + Append(" + "); + break; + case ShaderAst::BinaryType::Substract: + Append(" - "); + break; + case ShaderAst::BinaryType::Multiply: + Append(" * "); + break; + case ShaderAst::BinaryType::Divide: + Append(" / "); + break; + case ShaderAst::BinaryType::Equality: + Append(" == "); + break; + } + + Write(node.right); + } + + void GlslWriter::Write(const ShaderAst::Constant& node) + { + switch (node.exprType) + { + case ShaderAst::ExpressionType::Float1: + Append(String::Format("%F", node.values.vec1)); + break; + } + } + + void GlslWriter::Write(const ShaderAst::ExpressionStatement& node) + { + Write(node.expression); + Append(";"); + } + + void GlslWriter::Write(const ShaderAst::StatementBlock& node) + { + bool first = true; + for (const ShaderAst::StatementPtr& statement : node.statements) + { + if (!first) + AppendLine(); + + Write(statement); + + first = false; + } + } + + void GlslWriter::Write(const ShaderAst::Variable& node) + { + Append(node.name); + } + + void GlslWriter::Append(ShaderAst::ExpressionType type) + { + switch (type) + { + case ShaderAst::ExpressionType::Float1: + Append("float"); + break; + case ShaderAst::ExpressionType::Float2: + Append("vec2"); + break; + case ShaderAst::ExpressionType::Float3: + Append("vec3"); + break; + case ShaderAst::ExpressionType::Float4: + Append("vec4"); + break; + case ShaderAst::ExpressionType::None: + Append("void"); + break; + } + } + + void GlslWriter::Append(const String& txt) + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + m_currentState->stream << txt; + } + + void GlslWriter::AppendFunction(const Function& func) + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + Append(func.retType); + + m_currentState->stream << ' '; + Append(func.name); + + m_currentState->stream << '('; + for (std::size_t i = 0; i < func.parameters.size(); ++i) + { + if (i != 0) + m_currentState->stream << ", "; + + Append(func.parameters[i]->type); + m_currentState->stream << ' '; + Append(func.parameters[i]->name); + } + m_currentState->stream << ")\n"; + + EnterScope(); + Write(func.node); + LeaveScope(); + } + + void GlslWriter::AppendLine(const String& txt) + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + m_currentState->stream << txt << '\n' << String(m_currentState->indentLevel, '\t'); + } + + void GlslWriter::EnterScope() + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + m_currentState->indentLevel++; + AppendLine("{"); + } + + void GlslWriter::LeaveScope() + { + NazaraAssert(m_currentState, "This function should only be called while processing an AST"); + + m_currentState->indentLevel--; + AppendLine(); + AppendLine("}"); + } + +} diff --git a/src/Nazara/Renderer/ShaderAst.cpp b/src/Nazara/Renderer/ShaderAst.cpp new file mode 100644 index 000000000..3402eb987 --- /dev/null +++ b/src/Nazara/Renderer/ShaderAst.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz { namespace ShaderAst +{ + void ExpressionStatement::Register(ShaderWriter& visitor) + { + expression->Register(visitor); + } + + void ExpressionStatement::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void StatementBlock::Register(ShaderWriter& visitor) + { + for (auto& statementPtr : statements) + statementPtr->Register(visitor); + } + + void StatementBlock::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void Variable::Register(ShaderWriter& visitor) + { + visitor.RegisterVariable(name, type); + } + + void Variable::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void AssignOp::Register(ShaderWriter& visitor) + { + variable->Register(visitor); + right->Register(visitor); + } + + void AssignOp::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void BinaryOp::Register(ShaderWriter& visitor) + { + left->Register(visitor); + right->Register(visitor); + } + + void BinaryOp::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void Branch::Register(ShaderWriter& visitor) + { + for (ConditionalStatement& statement : condStatements) + { + statement.condition->Register(visitor); + statement.statement->Register(visitor); + } + + if (elseStatement) + elseStatement->Register(visitor); + } + + void Branch::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } + + + void Constant::Register(ShaderWriter&) + { + } + + void Constant::Visit(ShaderWriter& visitor) + { + visitor.Write(*this); + } +} +} diff --git a/src/Nazara/Renderer/ShaderWriter.cpp b/src/Nazara/Renderer/ShaderWriter.cpp new file mode 100644 index 000000000..8ca48da3e --- /dev/null +++ b/src/Nazara/Renderer/ShaderWriter.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + ShaderWriter::~ShaderWriter() = default; +}