From 4352083c4b1fde7bc4c51435021d1d1f5a25e09e Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 15 Jul 2013 00:23:04 +0200 Subject: [PATCH] Added ShaderManager (Experimental) Former-commit-id: 327e373f2b932e31184e88c5f29bd5bd8fa3ba46 --- include/Nazara/Core/Enums.hpp | 4 +- include/Nazara/Graphics/ColorBackground.hpp | 3 +- .../Nazara/Graphics/ForwardRenderQueue.hpp | 12 +- include/Nazara/Graphics/Light.hpp | 4 +- include/Nazara/Graphics/TextureBackground.hpp | 3 +- include/Nazara/Renderer.hpp | 2 +- include/Nazara/Renderer/Enums.hpp | 34 +- include/Nazara/Renderer/Material.hpp | 25 +- include/Nazara/Renderer/ShaderBuilder.hpp | 28 - include/Nazara/Renderer/ShaderManager.hpp | 33 + .../Nazara/Renderer/ShaderManagerParams.hpp | 43 + include/Nazara/Utility/Enums.hpp | 3 +- src/Nazara/Graphics/ColorBackground.cpp | 96 +- src/Nazara/Graphics/ForwardRenderQueue.cpp | 42 +- .../Graphics/ForwardRenderTechnique.cpp | 255 ++++-- src/Nazara/Graphics/Light.cpp | 9 +- src/Nazara/Graphics/TextureBackground.cpp | 102 +-- src/Nazara/Renderer/DebugDrawer.cpp | 16 +- src/Nazara/Renderer/GLSLShader.cpp | 1 - src/Nazara/Renderer/Material.cpp | 212 +++-- src/Nazara/Renderer/Renderer.cpp | 18 +- src/Nazara/Renderer/ShaderBuilder.cpp | 536 ----------- src/Nazara/Renderer/ShaderManager.cpp | 841 ++++++++++++++++++ 23 files changed, 1367 insertions(+), 955 deletions(-) delete mode 100644 include/Nazara/Renderer/ShaderBuilder.hpp create mode 100644 include/Nazara/Renderer/ShaderManager.hpp create mode 100644 include/Nazara/Renderer/ShaderManagerParams.hpp delete mode 100644 src/Nazara/Renderer/ShaderBuilder.cpp create mode 100644 src/Nazara/Renderer/ShaderManager.cpp diff --git a/include/Nazara/Core/Enums.hpp b/include/Nazara/Core/Enums.hpp index 6a4190503..c7e3e05ee 100644 --- a/include/Nazara/Core/Enums.hpp +++ b/include/Nazara/Core/Enums.hpp @@ -115,7 +115,9 @@ enum nzStreamOptionFlags { nzStreamOption_None = 0x0, - nzStreamOption_Text = 0x1 + nzStreamOption_Text = 0x1, + + nzStreamOption_Max = nzStreamOption_Text*2-1 }; enum nzTernary diff --git a/include/Nazara/Graphics/ColorBackground.hpp b/include/Nazara/Graphics/ColorBackground.hpp index 7d19e8318..71e8891f8 100644 --- a/include/Nazara/Graphics/ColorBackground.hpp +++ b/include/Nazara/Graphics/ColorBackground.hpp @@ -16,7 +16,6 @@ class NAZARA_API NzColorBackground : public NzAbstractBackground { public: NzColorBackground(const NzColor& color = NzColor::Black); - ~NzColorBackground(); void Draw(const NzScene* scene) const; @@ -27,7 +26,7 @@ class NAZARA_API NzColorBackground : public NzAbstractBackground private: NzColor m_color; - NzShaderRef m_shader; + NzShaderConstRef m_shader; }; #endif // NAZARA_COLORBACKGROUND_HPP diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 65bc8a13f..90a4857f3 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -39,7 +39,7 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource private: void OnResourceDestroy(const NzResource* resource, int index); - struct MaterialComparator + struct ModelMaterialComparator { bool operator()(const NzMaterial* mat1, const NzMaterial* mat2); }; @@ -55,6 +55,12 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2); }; + struct StaticData + { + NzBoxf aabb; + NzMatrix4f transformMatrix; + }; + struct StaticMeshComparator { bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2); @@ -78,8 +84,8 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource }; typedef std::map, SkeletalMeshComparator> SkeletalMeshContainer; - typedef std::map, StaticMeshComparator> StaticMeshContainer; - typedef std::map, MaterialComparator> MeshContainer; + typedef std::map, StaticMeshComparator> StaticMeshContainer; + typedef std::map, ModelMaterialComparator> MeshContainer; MeshContainer opaqueModels; std::vector> transparentsModels; diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index 2d9d5c0ae..5da646872 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -23,7 +23,7 @@ class NAZARA_API NzLight : public NzSceneNode void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const override; - void Apply(const NzShader* shader, unsigned int lightUnit) const; + void Enable(const NzShader* shader, unsigned int lightUnit) const; NzColor GetAmbientColor() const; float GetAttenuation() const; @@ -46,6 +46,8 @@ class NAZARA_API NzLight : public NzSceneNode NzLight& operator=(const NzLight& light); + static void Disable(const NzShader* shader, unsigned int lightUnit); + private: void Invalidate(); void Register(); diff --git a/include/Nazara/Graphics/TextureBackground.hpp b/include/Nazara/Graphics/TextureBackground.hpp index 9ed003f36..17d5f86c3 100644 --- a/include/Nazara/Graphics/TextureBackground.hpp +++ b/include/Nazara/Graphics/TextureBackground.hpp @@ -17,7 +17,6 @@ class NAZARA_API NzTextureBackground : public NzAbstractBackground public: NzTextureBackground(); NzTextureBackground(NzTexture* texture); - ~NzTextureBackground(); void Draw(const NzScene* scene) const; @@ -27,7 +26,7 @@ class NAZARA_API NzTextureBackground : public NzAbstractBackground void SetTexture(NzTexture* texture); private: - NzShaderRef m_shader; + NzShaderConstRef m_shader; NzTextureRef m_texture; }; diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index 29e6faa26..f227e2600 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index d0323df40..95df7b547 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -94,9 +94,11 @@ enum nzRendererCap enum nzRendererClearFlags { - nzRendererClear_Color = 0x01, - nzRendererClear_Depth = 0x02, - nzRendererClear_Stencil = 0x04 + nzRendererClear_Color = 0x1, + nzRendererClear_Depth = 0x2, + nzRendererClear_Stencil = 0x4, + + nzRendererClear_Max = nzRendererClear_Stencil*2-1 }; enum nzRendererComparison @@ -155,17 +157,11 @@ enum nzShaderFlags { nzShaderFlags_None = 0, - nzShaderFlags_AlphaMapping = 0x001, - nzShaderFlags_AlphaTest = 0x002, - nzShaderFlags_Deferred = 0x004, - nzShaderFlags_DiffuseMapping = 0x008, - nzShaderFlags_EmissiveMapping = 0x010, - nzShaderFlags_FlipUVs = 0x020, - nzShaderFlags_Instancing = 0x040, - nzShaderFlags_Lighting = 0x080, - nzShaderFlags_NormalMapping = 0x100, - nzShaderFlags_ParallaxMapping = 0x200, - nzShaderFlags_SpecularMapping = 0x400 + //nzShaderFlags_Deferred = 0x1, + nzShaderFlags_FlipUVs = 0x1, + nzShaderFlags_Instancing = 0x2, + + nzShaderFlags_Max = nzShaderFlags_Instancing*2-1 }; enum nzShaderLanguage @@ -178,11 +174,19 @@ enum nzShaderLanguage nzShaderLanguage_Max = nzShaderLanguage_GLSL }; +enum nzShaderTarget +{ + nzShaderTarget_FullscreenQuad, + nzShaderTarget_Model, + nzShaderTarget_None, + + nzShaderTarget_Max = nzShaderTarget_None +}; + enum nzShaderUniform { nzShaderUniform_CameraPosition, nzShaderUniform_InvTargetSize, - nzShaderUniform_LightCount, nzShaderUniform_MaterialAlphaMap, nzShaderUniform_MaterialAlphaThreshold, nzShaderUniform_MaterialAmbient, diff --git a/include/Nazara/Renderer/Material.hpp b/include/Nazara/Renderer/Material.hpp index d77cff43f..fda3ec1bc 100644 --- a/include/Nazara/Renderer/Material.hpp +++ b/include/Nazara/Renderer/Material.hpp @@ -57,7 +57,6 @@ class NAZARA_API NzMaterial : public NzResource NzTexture* GetAlphaMap() const; float GetAlphaThreshold() const; NzColor GetAmbientColor() const; - const NzShader* GetCustomShader() const; nzRendererComparison GetDepthFunc() const; NzColor GetDiffuseColor() const; NzTexture* GetDiffuseMap() const; @@ -70,7 +69,7 @@ class NAZARA_API NzMaterial : public NzResource NzTexture* GetHeightMap() const; NzTexture* GetNormalMap() const; const NzRenderStates& GetRenderStates() const; - nzUInt32 GetShaderFlags() const; + const NzShader* GetShader(nzShaderTarget target, nzUInt32 flags) const; float GetShininess() const; NzColor GetSpecularColor() const; NzTexture* GetSpecularMap() const; @@ -78,10 +77,15 @@ class NAZARA_API NzMaterial : public NzResource const NzTextureSampler& GetSpecularSampler() const; nzBlendFunc GetSrcBlend() const; - bool HasCustomShader() const; + bool HasAlphaMap() const; + bool HasDiffuseMap() const; + bool HasEmissiveMap() const; + bool HasHeightMap() const; + bool HasNormalMap() const; + bool HasSpecularMap() const; - bool IsEnabled(nzRendererParameter renderParameter) const; bool IsAlphaTestEnabled() const; + bool IsEnabled(nzRendererParameter renderParameter) const; bool IsLightingEnabled() const; bool LoadFromFile(const NzString& filePath, const NzMaterialParams& params = NzMaterialParams()); @@ -94,7 +98,6 @@ class NAZARA_API NzMaterial : public NzResource void SetAlphaMap(NzTexture* map); void SetAlphaThreshold(float alphaThreshold); void SetAmbientColor(const NzColor& ambient); - void SetCustomShader(const NzShader* shader); void SetDepthFunc(nzRendererComparison depthFunc); void SetDiffuseColor(const NzColor& diffuse); bool SetDiffuseMap(const NzString& texturePath); @@ -110,6 +113,7 @@ class NAZARA_API NzMaterial : public NzResource bool SetNormalMap(const NzString& texturePath); void SetNormalMap(NzTexture* map); void SetRenderStates(const NzRenderStates& states); + void SetShader(nzShaderTarget target, nzUInt32 flags, const NzShader* shader); void SetShininess(float shininess); void SetSpecularColor(const NzColor& specular); bool SetSpecularMap(const NzString& texturePath); @@ -123,19 +127,26 @@ class NAZARA_API NzMaterial : public NzResource static NzMaterial* GetDefault(); private: + struct ShaderUnit + { + NzShaderConstRef shader; + bool custom = false; + }; + void Copy(const NzMaterial& material); + void GenerateShader(nzShaderTarget target, nzUInt32 flags) const; + void InvalidateShaders(nzShaderTarget target); static bool Initialize(); static void Uninitialize(); - nzUInt32 m_shaderFlags; NzColor m_ambientColor; NzColor m_diffuseColor; NzColor m_specularColor; NzRenderStates m_states; + mutable ShaderUnit m_shaders[nzShaderTarget_Max+1][nzShaderFlags_Max+1]; NzTextureSampler m_diffuseSampler; NzTextureSampler m_specularSampler; - mutable NzShaderConstRef m_customShader; NzTextureRef m_alphaMap; NzTextureRef m_diffuseMap; NzTextureRef m_emissiveMap; diff --git a/include/Nazara/Renderer/ShaderBuilder.hpp b/include/Nazara/Renderer/ShaderBuilder.hpp deleted file mode 100644 index f0529943b..000000000 --- a/include/Nazara/Renderer/ShaderBuilder.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2013 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_SHADERBUILDER_HPP -#define NAZARA_SHADERBUILDER_HPP - -#include -#include - -class NAZARA_API NzShaderBuilder -{ - friend class NzRenderer; - - public: - NzShaderBuilder() = delete; - ~NzShaderBuilder() = delete; - - static const NzShader* Get(nzUInt32 flags); - - private: - static bool Initialize(); - static void Uninitialize(); -}; - -#endif // NAZARA_SHADERBUILDER_HPP diff --git a/include/Nazara/Renderer/ShaderManager.hpp b/include/Nazara/Renderer/ShaderManager.hpp new file mode 100644 index 000000000..e4adae798 --- /dev/null +++ b/include/Nazara/Renderer/ShaderManager.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2013 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_SHADERMANAGER_HPP +#define NAZARA_SHADERMANAGER_HPP + +#include +#include +#include + +class NAZARA_API NzShaderManager +{ + friend class NzRenderer; + + public: + NzShaderManager() = delete; + ~NzShaderManager() = delete; + + static const NzShader* Get(const NzShaderManagerParams& params); + + private: + static NzString BuildFragmentCode(const NzShaderManagerParams& params); + static NzString BuildVertexCode(const NzShaderManagerParams& params); + static NzShader* GenerateShader(const NzShaderManagerParams& params); + + static bool Initialize(); + static void Uninitialize(); +}; + +#endif // NAZARA_SHADERMANAGER_HPP diff --git a/include/Nazara/Renderer/ShaderManagerParams.hpp b/include/Nazara/Renderer/ShaderManagerParams.hpp new file mode 100644 index 000000000..e31a1e750 --- /dev/null +++ b/include/Nazara/Renderer/ShaderManagerParams.hpp @@ -0,0 +1,43 @@ +// Copyright (C) 2013 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_SHADERMANAGERPARAMS_HPP +#define NAZARA_SHADERMANAGERPARAMS_HPP + +#include + +struct NzShaderManagerParams +{ + struct FullscreenQuad + { + bool alphaMapping; + bool alphaTest; + bool diffuseMapping; + }; + + struct Model + { + bool alphaMapping; + bool alphaTest; + bool diffuseMapping; + bool emissiveMapping; + bool lighting; + bool normalMapping; + bool parallaxMapping; + bool specularMapping; + }; + + nzShaderTarget target; + nzUInt32 flags; + + union + { + FullscreenQuad fullscreenQuad; + Model model; + }; +}; + +#endif // NAZARA_SHADERMANAGERPARAMS_HPP diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index f51f15803..af0a6dda5 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -268,7 +268,8 @@ enum nzWindowStyleFlags nzWindowStyle_Resizable = 0x4, nzWindowStyle_Titlebar = 0x8, - nzWindowStyle_Default = nzWindowStyle_Closable | nzWindowStyle_Resizable | nzWindowStyle_Titlebar + nzWindowStyle_Default = nzWindowStyle_Closable | nzWindowStyle_Resizable | nzWindowStyle_Titlebar, + nzWindowStyle_Max = nzWindowStyle_Titlebar*2-1 }; #endif // NAZARA_ENUMS_UTILITY_HPP diff --git a/src/Nazara/Graphics/ColorBackground.cpp b/src/Nazara/Graphics/ColorBackground.cpp index 3dadd4759..9997d9c99 100644 --- a/src/Nazara/Graphics/ColorBackground.cpp +++ b/src/Nazara/Graphics/ColorBackground.cpp @@ -2,101 +2,23 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#ifndef NAZARA_RENDERER_OPENGL -#define NAZARA_RENDERER_OPENGL // Nécessaire pour inclure les headers OpenGL -#endif - -#include #include #include -#include +#include #include #include -namespace -{ - NzShader* BuildShader() - { - const char* fragmentSource110 = - "#version 110\n" - "uniform vec4 Color;\n" - "void main()\n" - "{\n" - " gl_FragColor = Color;\n" - "}\n"; - - const char* fragmentSource140 = - "#version 140\n" - "out vec4 RenderTarget0;\n" - "uniform vec4 Color;\n" - "void main()\n" - "{\n" - " RenderTarget0 = Color;\n" - "}\n"; - - const char* vertexSource110 = - "#version 110\n" - "attribute vec2 VertexPosition;\n" - "void main()\n" - "{\n" - " gl_Position = vec4(VertexPosition, 0.0, 1.0);\n" - "}\n"; - - const char* vertexSource140 = - "#version 140\n" - "in vec2 VertexPosition;\n" - "void main()\n" - "{\n" - " gl_Position = vec4(VertexPosition, 0.0, 1.0);\n" - "}\n"; - - ///TODO: Remplacer ça par des ShaderNode - std::unique_ptr shader(new NzShader(nzShaderLanguage_GLSL)); - shader->SetPersistent(false); - - bool useGLSL140 = (NzOpenGL::GetVersion() >= 310); - - if (!shader->Load(nzShaderType_Fragment, (useGLSL140) ? fragmentSource140 : fragmentSource110)) - { - NazaraError("Failed to load fragment shader"); - return nullptr; - } - - if (!shader->Load(nzShaderType_Vertex, (useGLSL140) ? vertexSource140 : vertexSource110)) - { - NazaraError("Failed to load vertex shader"); - return nullptr; - } - - if (!shader->Compile()) - { - NazaraError("Failed to compile shader"); - return nullptr; - } - - return shader.release(); - } - - static NzShader* s_shader = nullptr; - static unsigned int s_colorLocation; -} - NzColorBackground::NzColorBackground(const NzColor& color) : m_color(color) { - if (!s_shader) - { - s_shader = BuildShader(); - s_colorLocation = s_shader->GetUniformLocation("Color"); - } + NzShaderManagerParams params; + params.target = nzShaderTarget_FullscreenQuad; + params.flags = 0; + params.fullscreenQuad.alphaMapping = false; + params.fullscreenQuad.alphaTest = false; + params.fullscreenQuad.diffuseMapping = false; - m_shader = s_shader; -} - -NzColorBackground::~NzColorBackground() -{ - if (m_shader.Reset()) - s_shader = nullptr; + m_shader = NzShaderManager::Get(params); } void NzColorBackground::Draw(const NzScene* scene) const @@ -105,7 +27,7 @@ void NzColorBackground::Draw(const NzScene* scene) const static NzRenderStates states; - m_shader->SendColor(s_colorLocation, m_color); + m_shader->SendColor(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuse), m_color); NzRenderer::SetRenderStates(states); NzRenderer::SetShader(m_shader); diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index 57cf714d3..df6f9c3bc 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -142,7 +142,14 @@ void NzForwardRenderQueue::AddModel(const NzModel* model) if (pair2.second) staticMesh->AddResourceListener(this, ResourceType_StaticMesh); - pair2.first->second.push_back(transformMatrix); + std::vector& staticDataContainer = pair2.first->second; + + staticDataContainer.resize(staticDataContainer.size()+1); + StaticData& data = staticDataContainer.back(); + + data.aabb = staticMesh->GetAABB(); + data.aabb.Transform(transformMatrix); + data.transformMatrix = transformMatrix; } break; @@ -257,31 +264,22 @@ bool NzForwardRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh* return buffer1 < buffer2; } -bool NzForwardRenderQueue::MaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) +bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) { - const NzShader* shader1 = mat1->GetCustomShader(); - const NzShader* shader2 = mat2->GetCustomShader(); - - if (shader1) + ///TODO: Comparaison des shaders + for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i) { - if (shader2) - { - if (shader1 != shader2) - return shader1 < shader2; - } - else - return true; - } - else if (shader2) - return false; - else - { - nzUInt32 shaderFlags1 = mat1->GetShaderFlags(); - nzUInt32 shaderFlags2 = mat2->GetShaderFlags(); + const NzShader* shader1 = mat1->GetShader(nzShaderTarget_Model, i); + const NzShader* shader2 = mat2->GetShader(nzShaderTarget_Model, i); - if (shaderFlags1 != shaderFlags2) - return shaderFlags1 < shaderFlags2; + if (shader1 != shader2) + return shader1 < shader2; } + const NzTexture* diffuseMap1 = mat1->GetDiffuseMap(); + const NzTexture* diffuseMap2 = mat2->GetDiffuseMap(); + if (diffuseMap1 != diffuseMap2) + return diffuseMap1 < diffuseMap2; + return mat1 < mat2; } diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 2bf277f62..300e2eff9 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -10,20 +10,107 @@ #include #include #include -#include +#include #include +#include +#include +#include #include namespace { - struct LightComparator + class LightManager { - bool operator()(const NzLight* light1, const NzLight* light2) - { - return light1->GetPosition().SquaredDistance(pos) < light2->GetPosition().SquaredDistance(pos); - } + public: + LightManager() = default; + ~LightManager() = default; - NzVector3f pos; + unsigned int FindClosestLights(const NzLight** lights, unsigned int lightCount, const NzSpheref& object) + { + for (Light& light : m_lights) + { + light.light = nullptr; + light.score = std::numeric_limits::max(); // Nous jouons au Golf + } + + for (unsigned int i = 0; i < lightCount; ++i) + { + const NzLight* light = *lights; + + unsigned int score = std::numeric_limits::max(); + switch (light->GetLightType()) + { + case nzLightType_Directional: + score = 0; // Lumière choisie d'office + break; + + case nzLightType_Point: + { + NzSpheref lightSphere(light->GetPosition(), light->GetRadius()); + + if (lightSphere.Intersect(object)) + score = static_cast(light->GetPosition().SquaredDistance(object.GetPosition())*1000.f); + + break; + } + + case nzLightType_Spot: + { + NzSpheref lightSphere(light->GetPosition(), light->GetRadius()); + + ///TODO: Attribuer bonus/malus selon l'angle du spot ? + if (lightSphere.Intersect(object)) + score = static_cast(light->GetPosition().SquaredDistance(object.GetPosition())*1000.f); + + break; + } + } + + if (score < m_lights[0].score) + { + unsigned int j; + for (j = 1; j < 3; ++j) ///TODO: Constante + { + if (score > m_lights[j].score) + break; + } + + j--; // Position de la nouvelle lumière + + // Décalage + std::memcpy(&m_lights[0], &m_lights[1], j*sizeof(Light)); + + m_lights[j].light = light; + m_lights[j].score = score; + } + + lights++; + } + + unsigned int i; + for (i = 0; i < 3; ++i) ///TODO: Constante + { + if (m_lights[i].light) + break; + } + + return 3-i; ///TODO: Constante + } + + const NzLight* GetLight(unsigned int i) const + { + ///TODO: Constante + return m_lights[3-i-1].light; // Les lumières sont stockées dans l'ordre inverse (De la plus éloignée à la plus proche) + } + + private: + struct Light + { + const NzLight* light; + unsigned int score; + }; + + Light m_lights[3]; ///TODO: Constante }; } @@ -46,15 +133,11 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene) void NzForwardRenderTechnique::Draw(const NzScene* scene) { ///TODO: Regrouper les activations par méthode - LightComparator lightComparator; + LightManager lightManager; const NzCamera* camera = scene->GetActiveCamera(); const NzShader* lastShader = nullptr; - // Externes à la boucle pour conserver leur valeurs si le shader ne change pas - unsigned int lightCount = 0; - int lightCountLocation = -1; - // Rendu des modèles opaques for (auto& matIt : m_renderQueue.opaqueModels) { @@ -66,18 +149,13 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) const NzMaterial* material = matIt.first; // On commence par récupérer le shader du matériau - const NzShader* shader; - if (material->HasCustomShader()) - shader = material->GetCustomShader(); - else - shader = NzShaderBuilder::Get(material->GetShaderFlags()); + const NzShader* shader = material->GetShader(nzShaderTarget_Model, 0); + + unsigned int lightCount = 0; // Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même if (shader != lastShader) { - // On récupère l'information sur l'éclairage en même temps que la position de l'uniforme "LightCount" - lightCountLocation = shader->GetUniformLocation(nzShaderUniform_LightCount); - NzRenderer::SetShader(shader); // Couleur ambiante de la scène @@ -85,18 +163,11 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) // Position de la caméra shader->SendVector(shader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition()); - lightCount = 0; - // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) - if (lightCountLocation != -1) - { - for (const NzLight* light : m_renderQueue.directionnalLights) - { - light->Apply(shader, lightCount++); - if (lightCount > NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT) - break; // Prévenons les bêtises des utilisateurs - } - } + lightCount = m_renderQueue.directionnalLights.size(); + for (unsigned int i = 0; i < lightCount; ++i) + m_renderQueue.directionnalLights[i]->Enable(shader, i); + lastShader = shader; } @@ -116,8 +187,9 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) for (auto& subMeshIt : staticContainer) { const NzStaticMesh* mesh = subMeshIt.first; - std::vector& matrices = subMeshIt.second; - if (!matrices.empty()) + std::vector& staticData = subMeshIt.second; + + if (!staticData.empty()) { const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); @@ -140,28 +212,25 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer); - for (const NzMatrix4f& matrix : matrices) + for (const NzForwardRenderQueue::StaticData& data : staticData) { // Calcul des lumières les plus proches - ///TODO: LightManager ? - if (lightCountLocation != -1) + if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty()) { - std::vector& lights = m_renderQueue.lights; + unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.aabb.GetBoundingSphere()); + count -= lightCount; - lightComparator.pos = matrix.GetTranslation(); - std::sort(lights.begin(), lights.end(), lightComparator); - - unsigned int max = std::min(std::min(NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT - lightCount, m_maxLightsPerObject), static_cast(lights.size())); - for (unsigned int i = 0; i < max; ++i) - lights[i]->Apply(shader, lightCount++); - - shader->SendInteger(lightCountLocation, lightCount); + for (unsigned int i = 0; i < count; ++i) + lightManager.GetLight(i)->Enable(shader, lightCount++); } - NzRenderer::SetMatrix(nzMatrixType_World, matrix); + for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières + NzLight::Disable(shader, i); + + NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); drawFunc(mesh->GetPrimitiveMode(), 0, indexCount); } - matrices.clear(); + staticData.clear(); } } } @@ -175,18 +244,13 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) m_renderQueue.transparentSkeletalModels[pair.first].material; // On commence par récupérer le shader du matériau - const NzShader* shader; - if (material->HasCustomShader()) - shader = material->GetCustomShader(); - else - shader = NzShaderBuilder::Get(material->GetShaderFlags()); + const NzShader* shader = material->GetShader(nzShaderTarget_Model, 0); + + unsigned int lightCount = 0; // Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même if (shader != lastShader) { - // On récupère l'information sur l'éclairage en même temps que la position de l'uniforme "LightCount" - lightCountLocation = shader->GetUniformLocation(nzShaderUniform_LightCount); - NzRenderer::SetShader(shader); // Couleur ambiante de la scène @@ -194,18 +258,11 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) // Position de la caméra shader->SendVector(shader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition()); - lightCount = 0; - // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) - if (lightCountLocation != -1) - { - for (const NzLight* light : m_renderQueue.directionnalLights) - { - light->Apply(shader, lightCount++); - if (lightCount > NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT) - break; // Prévenons les bêtises des utilisateurs - } - } + lightCount = m_renderQueue.directionnalLights.size(); + for (unsigned int i = 0; i < lightCount; ++i) + m_renderQueue.directionnalLights[i]->Enable(shader, i); + lastShader = shader; } @@ -241,21 +298,18 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) NzRenderer::SetVertexBuffer(vertexBuffer); // Calcul des lumières les plus proches - ///TODO: LightManager ? - if (lightCountLocation != -1) + if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty()) { - std::vector& lights = m_renderQueue.lights; + unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), staticModel.aabb.GetBoundingSphere()); + count -= lightCount; - lightComparator.pos = matrix.GetTranslation(); - std::sort(lights.begin(), lights.end(), lightComparator); - - unsigned int max = std::min(std::min(NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT - lightCount, m_maxLightsPerObject), static_cast(lights.size())); - for (unsigned int i = 0; i < max; ++i) - lights[i]->Apply(shader, lightCount++); - - shader->SendInteger(lightCountLocation, lightCount); + for (unsigned int i = 0; i < count; ++i) + lightManager.GetLight(i)->Enable(shader, lightCount++); } + for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières + NzLight::Disable(shader, i); + NzRenderer::SetMatrix(nzMatrixType_World, matrix); drawFunc(mesh->GetPrimitiveMode(), 0, indexCount); } @@ -265,6 +319,51 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) } } + // Les billboards + /*if (!m_renderQueue.billboards.empty()) + { + //NzRenderer::SetIndexBuffer(m_billboardIndexBuffer); + NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity()); + NzRenderer::SetShader(m_billboardShader); + NzRenderer::SetVertexBuffer(m_billboardVertexBuffer); + + m_billboardShader->SendVector(s_cameraForwardLocation, camera->GetForward()); + m_billboardShader->SendVector(s_cameraUpLocation, camera->GetUp()); + m_billboardShader->SendVector(s_worldUpLocation, NzVector3f::Up()); + + // Couleur ambiante de la scène + m_billboardShader->SendColor(m_billboardShader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + m_billboardShader->SendVector(m_billboardShader->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition()); + + lightCount = 0; + + // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) + m_renderQueue.lights[0]->Apply(m_billboardShader, 0); + + for (auto& matIt : m_renderQueue.billboards) + { + const NzMaterial* material = matIt.first; + auto& billboards = matIt.second; + + material->Apply(m_billboardShader); + + unsigned int billboardCount = billboards.size(); + const NzForwardRenderQueue::BillboardData* data = &billboards[0]; + while (billboardCount > 0) + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboards); + billboardCount -= renderedBillboardCount; + + m_billboardVertexBuffer->FillVertices(data, 0, renderedBillboardCount, true); + data += renderedBillboardCount; + + NzRenderer::DrawPrimitives(nzPrimitiveMode_PointList, 0, renderedBillboardCount); + } + billboards.clear(); + } + }*/ + // Les autres drawables (Exemple: Terrain) for (const NzDrawable* drawable : m_renderQueue.otherDrawables) drawable->Draw(); @@ -282,5 +381,5 @@ NzAbstractRenderQueue* NzForwardRenderTechnique::GetRenderQueue() void NzForwardRenderTechnique::SetMaxLightsPerObject(unsigned int lightCount) { - m_maxLightsPerObject = lightCount; + m_maxLightsPerObject = lightCount; ///TODO: Vérifier par rapport à la constante } diff --git a/src/Nazara/Graphics/Light.cpp b/src/Nazara/Graphics/Light.cpp index 928f9bc3a..327ce1fc5 100644 --- a/src/Nazara/Graphics/Light.cpp +++ b/src/Nazara/Graphics/Light.cpp @@ -38,7 +38,7 @@ void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const renderQueue->AddLight(this); } -void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const +void NzLight::Enable(const NzShader* shader, unsigned int lightUnit) const { /* struct Light @@ -66,6 +66,7 @@ void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const -P3: float cosInnerAngle + float cosOuterAngle */ + ///TODO: Optimiser int typeLocation = shader->GetUniformLocation("Lights[0].type"); int ambientLocation = shader->GetUniformLocation("Lights[0].ambient"); int diffuseLocation = shader->GetUniformLocation("Lights[0].diffuse"); @@ -217,6 +218,12 @@ NzLight& NzLight::operator=(const NzLight& light) return *this; } +void NzLight::Disable(const NzShader* shader, unsigned int lightUnit) +{ + ///TODO: Optimiser + shader->SendInteger(shader->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1); +} + void NzLight::Invalidate() { NzSceneNode::Invalidate(); diff --git a/src/Nazara/Graphics/TextureBackground.cpp b/src/Nazara/Graphics/TextureBackground.cpp index 987b55f59..f6caecee5 100644 --- a/src/Nazara/Graphics/TextureBackground.cpp +++ b/src/Nazara/Graphics/TextureBackground.cpp @@ -2,99 +2,22 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#ifndef NAZARA_RENDERER_OPENGL -#define NAZARA_RENDERER_OPENGL // Nécessaire pour inclure les headers OpenGL -#endif - -#include #include #include -#include +#include #include #include -namespace -{ - static NzShader* s_shader = nullptr; - static int s_textureLocation; - - NzShader* BuildShader() - { - const char* fragmentSource110 = - "#version 110\n" - "varying vec2 vTexCoord;\n" - "uniform sampler2D Texture;\n" - "void main()\n" - "{\n" - " gl_FragColor = texture(Texture, vTexCoord);\n" - "}\n"; - - const char* fragmentSource140 = - "#version 140\n" - "in vec2 vTexCoord;\n" - "out vec4 RenderTarget0;\n" - "uniform sampler2D Texture;\n" - "void main()\n" - "{\n" - " RenderTarget0 = texture(Texture, vTexCoord);\n" - "}\n"; - - const char* vertexSource110 = - "#version 110\n" - "attribute vec2 VertexPosition;\n" - "varying vec2 vTexCoord;\n" - "void main()\n" - "{\n" - " gl_Position = vec4(VertexPosition, 0.0, 1.0);\n" - " vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, (VertexPosition.y + 1.0)*0.5);\n" - "}\n"; - - const char* vertexSource140 = - "#version 140\n" - "in vec2 VertexPosition;\n" - "out vec2 vTexCoord;\n" - "void main()\n" - "{\n" - " gl_Position = vec4(VertexPosition, 0.0, 1.0);\n" - " vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, (VertexPosition.y + 1.0)*0.5);\n" - "}\n"; - - ///TODO: Remplacer ça par des ShaderNode - std::unique_ptr shader(new NzShader(nzShaderLanguage_GLSL)); - shader->SetPersistent(false); - - bool useGLSL140 = (NzOpenGL::GetVersion() >= 310); - - if (!shader->Load(nzShaderType_Fragment, (useGLSL140) ? fragmentSource140 : fragmentSource110)) - { - NazaraError("Failed to load fragment shader"); - return nullptr; - } - - if (!shader->Load(nzShaderType_Vertex, (useGLSL140) ? vertexSource140 : vertexSource110)) - { - NazaraError("Failed to load vertex shader"); - return nullptr; - } - - if (!shader->Compile()) - { - NazaraError("Failed to compile shader"); - return nullptr; - } - - s_textureLocation = shader->GetUniformLocation("Texture"); - - return shader.release(); - } -} - NzTextureBackground::NzTextureBackground() { - if (!s_shader) - s_shader = BuildShader(); + NzShaderManagerParams params; + params.target = nzShaderTarget_FullscreenQuad; + params.flags = 0; + params.fullscreenQuad.alphaMapping = false; + params.fullscreenQuad.alphaTest = false; + params.fullscreenQuad.diffuseMapping = true; - m_shader = s_shader; + m_shader = NzShaderManager::Get(params); } NzTextureBackground::NzTextureBackground(NzTexture* texture) : @@ -103,19 +26,14 @@ NzTextureBackground() m_texture = texture; } -NzTextureBackground::~NzTextureBackground() -{ - if (m_shader.Reset()) - s_shader = nullptr; -} - void NzTextureBackground::Draw(const NzScene* scene) const { NazaraUnused(scene); static NzRenderStates states; - m_shader->SendInteger(s_textureLocation, 0); + m_shader->SendColor(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuse), NzColor::White); + m_shader->SendInteger(m_shader->GetUniformLocation(nzShaderUniform_MaterialDiffuseMap), 0); NzRenderer::SetRenderStates(states); NzRenderer::SetShader(m_shader); diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 28d8a1772..ea62c8a2c 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -529,7 +529,19 @@ bool NzDebugDrawer::Initialize() { // Shader { - shader = NzShaderBuilder::Get(nzShaderFlags_None); + NzShaderManagerParams params; + params.target = nzShaderTarget_Model; + params.flags = 0; + params.model.alphaMapping = false; + params.model.alphaTest = false; + params.model.diffuseMapping = false; + params.model.emissiveMapping = false; + params.model.lighting = false; + params.model.normalMapping = false; + params.model.parallaxMapping = false; + params.model.specularMapping = false; + + shader = NzShaderManager::Get(params); if (!shader) { NazaraError("Failed to build debug shader"); diff --git a/src/Nazara/Renderer/GLSLShader.cpp b/src/Nazara/Renderer/GLSLShader.cpp index 9a329382b..595a891d5 100644 --- a/src/Nazara/Renderer/GLSLShader.cpp +++ b/src/Nazara/Renderer/GLSLShader.cpp @@ -66,7 +66,6 @@ bool NzGLSLShader::Compile() CacheUniform(CameraPosition); CacheUniform(InvTargetSize); - CacheUniform(LightCount); CacheUniform(MaterialAlphaMap); CacheUniform(MaterialAlphaThreshold); CacheUniform(MaterialAmbient); diff --git a/src/Nazara/Renderer/Material.cpp b/src/Nazara/Renderer/Material.cpp index 4287eefa4..65deeead3 100644 --- a/src/Nazara/Renderer/Material.cpp +++ b/src/Nazara/Renderer/Material.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,13 +31,16 @@ NzMaterial::NzMaterial(NzMaterial&& material) Copy(material); // Nous "volons" la référence du matériau - material.m_alphaMap = nullptr; - material.m_customShader = nullptr; - material.m_diffuseMap = nullptr; - material.m_emissiveMap = nullptr; - material.m_heightMap = nullptr; - material.m_normalMap = nullptr; - material.m_specularMap = nullptr; + material.m_alphaMap.Reset(); + material.m_diffuseMap.Reset(); + material.m_emissiveMap.Reset(); + material.m_heightMap.Reset(); + material.m_normalMap.Reset(); + material.m_specularMap.Reset(); + + for (unsigned int i = 0; i <= nzShaderTarget_Max; ++i) + for (ShaderUnit& unit : material.m_shaders[i]) + unit.shader.Reset(); } void NzMaterial::Apply(const NzShader* shader) const @@ -160,19 +163,11 @@ void NzMaterial::Enable(nzRendererParameter renderParameter, bool enable) void NzMaterial::EnableAlphaTest(bool alphaTest) { m_alphaTestEnabled = alphaTest; - if (m_alphaTestEnabled) - m_shaderFlags |= nzShaderFlags_AlphaTest; - else - m_shaderFlags &= ~nzShaderFlags_AlphaTest; } void NzMaterial::EnableLighting(bool lighting) { m_lightingEnabled = lighting; - if (m_lightingEnabled) - m_shaderFlags |= nzShaderFlags_Lighting; - else - m_shaderFlags &= ~nzShaderFlags_Lighting; } NzTexture* NzMaterial::GetAlphaMap() const @@ -190,11 +185,6 @@ NzColor NzMaterial::GetAmbientColor() const return m_ambientColor; } -const NzShader* NzMaterial::GetCustomShader() const -{ - return m_customShader; -} - nzRendererComparison NzMaterial::GetDepthFunc() const { return m_states.depthFunc; @@ -255,9 +245,13 @@ const NzRenderStates& NzMaterial::GetRenderStates() const return m_states; } -nzUInt32 NzMaterial::GetShaderFlags() const +const NzShader* NzMaterial::GetShader(nzShaderTarget target, nzUInt32 flags) const { - return m_shaderFlags; + const ShaderUnit& unit = m_shaders[target][flags]; + if (!unit.shader.IsValid()) + GenerateShader(target, flags); + + return unit.shader; } float NzMaterial::GetShininess() const @@ -290,9 +284,39 @@ nzBlendFunc NzMaterial::GetSrcBlend() const return m_states.srcBlend; } -bool NzMaterial::HasCustomShader() const +bool NzMaterial::HasAlphaMap() const { - return m_customShader != nullptr; + return m_alphaMap.IsValid(); +} + +bool NzMaterial::HasDiffuseMap() const +{ + return m_diffuseMap.IsValid(); +} + +bool NzMaterial::HasEmissiveMap() const +{ + return m_emissiveMap.IsValid(); +} + +bool NzMaterial::HasHeightMap() const +{ + return m_heightMap.IsValid(); +} + +bool NzMaterial::HasNormalMap() const +{ + return m_normalMap.IsValid(); +} + +bool NzMaterial::HasSpecularMap() const +{ + return m_specularMap.IsValid(); +} + +bool NzMaterial::IsAlphaTestEnabled() const +{ + return m_alphaTestEnabled; } bool NzMaterial::IsEnabled(nzRendererParameter parameter) const @@ -308,11 +332,6 @@ bool NzMaterial::IsEnabled(nzRendererParameter parameter) const return m_states.parameters[parameter]; } -bool NzMaterial::IsAlphaTestEnabled() const -{ - return m_alphaTestEnabled; -} - bool NzMaterial::IsLightingEnabled() const { return m_lightingEnabled; @@ -336,20 +355,27 @@ bool NzMaterial::LoadFromStream(NzInputStream& stream, const NzMaterialParams& p void NzMaterial::Reset() { m_alphaMap.Reset(); - m_customShader.Reset(); m_diffuseMap.Reset(); m_emissiveMap.Reset(); m_heightMap.Reset(); m_normalMap.Reset(); m_specularMap.Reset(); + for (unsigned int i = 0; i <= nzShaderTarget_Max; ++i) + { + for (ShaderUnit& unit : m_shaders[i]) + { + unit.custom = false; + unit.shader.Reset(); + } + } + m_alphaThreshold = 0.2f; m_alphaTestEnabled = false; m_ambientColor = NzColor(128, 128, 128); m_diffuseColor = NzColor::White; m_diffuseSampler = NzTextureSampler(); m_lightingEnabled = true; - m_shaderFlags = nzShaderFlags_Lighting; m_shininess = 50.f; m_specularColor = NzColor::White; m_specularSampler = NzTextureSampler(); @@ -378,10 +404,8 @@ bool NzMaterial::SetAlphaMap(const NzString& texturePath) void NzMaterial::SetAlphaMap(NzTexture* map) { m_alphaMap = map; - if (m_alphaMap) - m_shaderFlags |= nzShaderFlags_AlphaMapping; - else - m_shaderFlags &= ~nzShaderFlags_AlphaMapping; + + InvalidateShaders(nzShaderTarget_Model); } void NzMaterial::SetAlphaThreshold(float alphaThreshold) @@ -394,11 +418,6 @@ void NzMaterial::SetAmbientColor(const NzColor& ambient) m_ambientColor = ambient; } -void NzMaterial::SetCustomShader(const NzShader* shader) -{ - m_customShader = shader; -} - void NzMaterial::SetDepthFunc(nzRendererComparison depthFunc) { m_states.depthFunc = depthFunc; @@ -429,10 +448,8 @@ bool NzMaterial::SetDiffuseMap(const NzString& texturePath) void NzMaterial::SetDiffuseMap(NzTexture* map) { m_diffuseMap = map; - if (m_diffuseMap) - m_shaderFlags |= nzShaderFlags_DiffuseMapping; - else - m_shaderFlags &= ~nzShaderFlags_DiffuseMapping; + + InvalidateShaders(nzShaderTarget_Model); } void NzMaterial::SetDiffuseSampler(const NzTextureSampler& sampler) @@ -465,10 +482,8 @@ bool NzMaterial::SetEmissiveMap(const NzString& texturePath) void NzMaterial::SetEmissiveMap(NzTexture* map) { m_emissiveMap = map; - if (m_emissiveMap) - m_shaderFlags |= nzShaderFlags_EmissiveMapping; - else - m_shaderFlags &= ~nzShaderFlags_EmissiveMapping; + + InvalidateShaders(nzShaderTarget_Model); } void NzMaterial::SetFaceCulling(nzFaceCulling culling) @@ -501,6 +516,8 @@ bool NzMaterial::SetHeightMap(const NzString& texturePath) void NzMaterial::SetHeightMap(NzTexture* map) { m_heightMap = map; + + InvalidateShaders(nzShaderTarget_Model); } bool NzMaterial::SetNormalMap(const NzString& texturePath) @@ -523,10 +540,8 @@ bool NzMaterial::SetNormalMap(const NzString& texturePath) void NzMaterial::SetNormalMap(NzTexture* map) { m_normalMap = map; - if (m_normalMap) - m_shaderFlags |= nzShaderFlags_NormalMapping; - else - m_shaderFlags &= ~nzShaderFlags_NormalMapping; + + InvalidateShaders(nzShaderTarget_Model); } void NzMaterial::SetRenderStates(const NzRenderStates& states) @@ -534,6 +549,14 @@ void NzMaterial::SetRenderStates(const NzRenderStates& states) m_states = states; } +void NzMaterial::SetShader(nzShaderTarget target, nzUInt32 flags, const NzShader* shader) +{ + ShaderUnit& unit = m_shaders[target][flags]; + + unit.custom = (shader != nullptr); + unit.shader = shader; +} + void NzMaterial::SetShininess(float shininess) { m_shininess = shininess; @@ -564,10 +587,8 @@ bool NzMaterial::SetSpecularMap(const NzString& texturePath) void NzMaterial::SetSpecularMap(NzTexture* map) { m_specularMap = map; - if (m_specularMap) - m_shaderFlags |= nzShaderFlags_SpecularMapping; - else - m_shaderFlags &= ~nzShaderFlags_SpecularMapping; + + InvalidateShaders(nzShaderTarget_Model); } void NzMaterial::SetSpecularSampler(const NzTextureSampler& sampler) @@ -592,13 +613,16 @@ NzMaterial& NzMaterial::operator=(NzMaterial&& material) Copy(material); // Comme ça nous volons la référence du matériau - material.m_alphaMap = nullptr; - material.m_customShader = nullptr; - material.m_diffuseMap = nullptr; - material.m_emissiveMap = nullptr; - material.m_heightMap = nullptr; - material.m_normalMap = nullptr; - material.m_specularMap = nullptr; + material.m_alphaMap.Reset(); + material.m_diffuseMap.Reset(); + material.m_emissiveMap.Reset(); + material.m_heightMap.Reset(); + material.m_normalMap.Reset(); + material.m_specularMap.Reset(); + + for (unsigned int i = 0; i <= nzShaderTarget_Max; ++i) + for (ShaderUnit& unit : material.m_shaders[i]) + unit.shader.Reset(); return *this; } @@ -611,18 +635,20 @@ NzMaterial* NzMaterial::GetDefault() void NzMaterial::Copy(const NzMaterial& material) { m_alphaMap.Reset(); - m_customShader.Reset(); m_diffuseMap.Reset(); m_emissiveMap.Reset(); m_heightMap.Reset(); m_normalMap.Reset(); m_specularMap.Reset(); + for (unsigned int i = 0; i <= nzShaderTarget_Max; ++i) + for (ShaderUnit& unit : m_shaders[i]) + unit.shader.Reset(); + std::memcpy(this, &material, sizeof(NzMaterial)); // Autorisé dans notre cas, et bien plus rapide // Ensuite une petite astuce pour récupérer correctement les références m_alphaMap.Release(); - m_customShader.Release(); m_diffuseMap.Release(); m_emissiveMap.Release(); m_heightMap.Release(); @@ -630,11 +656,63 @@ void NzMaterial::Copy(const NzMaterial& material) m_specularMap.Release(); m_alphaMap = material.m_alphaMap; - m_customShader = material.m_customShader; m_diffuseMap = material.m_diffuseMap; + m_emissiveMap = material.m_emissiveMap; m_heightMap = material.m_heightMap; m_normalMap = material.m_normalMap; m_specularMap = material.m_specularMap; + + for (unsigned int i = 0; i <= nzShaderTarget_Max; ++i) + { + for (unsigned int j = 0; j <= nzShaderFlags_Max; ++j) + { + NzShaderConstRef& shader = m_shaders[i][j].shader; + + shader.Release(); + shader = material.m_shaders[i][j].shader; + } + } +} + +void NzMaterial::GenerateShader(nzShaderTarget target, nzUInt32 flags) const +{ + NzShaderManagerParams params; + params.target = target; + params.flags = flags; + + switch (target) + { + case nzShaderTarget_FullscreenQuad: + params.fullscreenQuad.alphaMapping = m_alphaMap.IsValid(); + params.fullscreenQuad.alphaTest = m_alphaTestEnabled; + params.fullscreenQuad.diffuseMapping = m_diffuseMap.IsValid(); + break; + + case nzShaderTarget_Model: + params.model.alphaMapping = m_alphaMap.IsValid(); + params.model.alphaTest = m_alphaTestEnabled; + params.model.diffuseMapping = m_diffuseMap.IsValid(); + params.model.emissiveMapping = m_emissiveMap.IsValid(); + params.model.lighting = m_lightingEnabled; + params.model.normalMapping = m_normalMap.IsValid(); + params.model.parallaxMapping = m_heightMap.IsValid(); + params.model.specularMapping = m_specularMap.IsValid(); + break; + + case nzShaderTarget_None: + break; + } + + m_shaders[target][flags].shader = NzShaderManager::Get(params); +} + +void NzMaterial::InvalidateShaders(nzShaderTarget target) +{ + for (ShaderUnit& unit : m_shaders[target]) + { + if (!unit.custom) + unit.shader.Reset(); + } } bool NzMaterial::Initialize() diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index bd67a2ac2..fefa8b33f 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,10 +37,10 @@ namespace { Update_None = 0, - Update_Matrices = 0x01, - Update_Shader = 0x02, - Update_Textures = 0x04, - Update_VAO = 0x08, + Update_Matrices = 0x1, + Update_Shader = 0x2, + Update_Textures = 0x4, + Update_VAO = 0x8 }; struct MatrixUnit @@ -500,6 +500,8 @@ bool NzRenderer::Initialize() if (!NzOpenGL::Initialize()) { NazaraError("Failed to initialize OpenGL"); + Uninitialize(); + return false; } @@ -616,9 +618,9 @@ bool NzRenderer::Initialize() return false; } - if (!NzShaderBuilder::Initialize()) + if (!NzShaderManager::Initialize()) { - NazaraError("Failed to initialize shader builder"); + NazaraError("Failed to initialize shader manager"); Uninitialize(); return false; @@ -1180,7 +1182,7 @@ void NzRenderer::Uninitialize() NzLoaders_Texture_Unregister(); NzTextureSampler::Uninitialize(); - NzShaderBuilder::Uninitialize(); + NzShaderManager::Uninitialize(); NzMaterial::Uninitialize(); NzDebugDrawer::Uninitialize(); diff --git a/src/Nazara/Renderer/ShaderBuilder.cpp b/src/Nazara/Renderer/ShaderBuilder.cpp deleted file mode 100644 index f1652e300..000000000 --- a/src/Nazara/Renderer/ShaderBuilder.cpp +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright (C) 2013 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 -#include -#include -#include -#include -#include - -///TODO: Remplacer par les ShaderNode - -namespace -{ - std::unordered_map> s_shaders; - - NzString BuildFragmentShaderSource(nzUInt32 flags) - { - bool glsl140 = (NzOpenGL::GetVersion() >= 310); - //bool useMRT = (glsl140 && NzRenderer::HasCapability(nzRendererCap_MultipleRenderTargets)); - bool uvMapping = (flags & nzShaderFlags_DiffuseMapping || flags & nzShaderFlags_NormalMapping || flags & nzShaderFlags_SpecularMapping); - - NzString inKW = (glsl140) ? "in" : "varying"; - NzString fragmentColorKW = (glsl140) ? "RenderTarget0" : "gl_FragColor"; - NzString textureLookupKW = (glsl140) ? "texture" : "texture2D"; - - NzString sourceCode; - sourceCode.Reserve(1024); // Le shader peut faire plus, mais cela évite déjà beaucoup de petites allocations - - /********************Préprocesseur********************/ - sourceCode = "#version "; - if (glsl140) - sourceCode += "140\n"; - else - sourceCode += "110\n"; - - sourceCode += '\n'; - - if (flags & nzShaderFlags_Lighting) - { - sourceCode += "#define LIGHT_DIRECTIONAL 0\n" - "#define LIGHT_POINT 1\n" - "#define LIGHT_SPOT 2\n" - "\n"; - } - - sourceCode += '\n'; - - /********************Uniformes********************/ - if (flags & nzShaderFlags_Lighting) - { - sourceCode += "struct Light\n" - "{\n" - "int type;\n" - "vec4 ambient;\n" - "vec4 diffuse;\n" - "vec4 specular;\n" - "\n" - "vec4 parameters1;\n" - "vec4 parameters2;\n" - "vec2 parameters3;\n" - "};\n" - "\n"; - } - - if (flags & nzShaderFlags_Lighting) - { - sourceCode += "uniform vec3 CameraPosition;\n" - "uniform int LightCount;\n" - "uniform Light Lights[" NazaraStringifyMacro(NAZARA_RENDERER_SHADER_MAX_LIGHTCOUNT) "];\n" - "uniform vec4 MaterialAmbient;\n"; - } - - if (flags & nzShaderFlags_AlphaMapping) - sourceCode += "uniform sampler2D MaterialAlphaMap;\n"; - - if (flags & nzShaderFlags_AlphaTest) - sourceCode += "uniform float MaterialAlphaThreshold;\n"; - - sourceCode += "uniform vec4 MaterialDiffuse;\n"; - - if (flags & nzShaderFlags_DiffuseMapping) - sourceCode += "uniform sampler2D MaterialDiffuseMap;\n"; - - if (flags & nzShaderFlags_EmissiveMapping) - sourceCode += "uniform sampler2D MaterialEmissiveMap;\n"; - - if (flags & nzShaderFlags_Lighting) - { - if (flags & nzShaderFlags_NormalMapping) - sourceCode += "uniform sampler2D MaterialNormalMap;\n"; - - sourceCode += "uniform float MaterialShininess;\n" - "uniform vec4 MaterialSpecular;\n"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += "uniform sampler2D MaterialSpecularMap;\n"; - - sourceCode += "uniform vec4 SceneAmbient;\n"; - } - - sourceCode += '\n'; - - /********************Entrant********************/ - if (flags & nzShaderFlags_Lighting) - { - if (flags & nzShaderFlags_NormalMapping) - sourceCode += inKW + " mat3 vLightToWorld;\n"; - else - sourceCode += inKW + " vec3 vNormal;\n"; - } - - if (uvMapping) - sourceCode += inKW + " vec2 vTexCoord;\n"; - - if (flags & nzShaderFlags_Lighting) - sourceCode += inKW + " vec3 vWorldPos;\n"; - - sourceCode += '\n'; - - /********************Sortant********************/ - if (glsl140) - sourceCode += "out vec4 RenderTarget0;\n"; - - sourceCode += '\n'; - - /********************Fonctions********************/ - sourceCode += "void main()\n" - "{\n"; - - sourceCode += "float alpha = MaterialDiffuse.a"; - if (flags & nzShaderFlags_AlphaMapping) - sourceCode += '*' + textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; - - sourceCode += ";\n"; - - if (flags & nzShaderFlags_AlphaTest) - { - sourceCode += "if (alpha < MaterialAlphaThreshold)\n" - "discard;\n"; - } - - if (flags & nzShaderFlags_Lighting) - { - sourceCode += "vec3 light = vec3(0.0, 0.0, 0.0);\n"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += "vec3 si = vec3(0.0, 0.0, 0.0);\n"; - - if (flags & nzShaderFlags_NormalMapping) - sourceCode += "vec3 normal = normalize(vLightToWorld * (2.0 * vec3(" + textureLookupKW + "(MaterialNormalMap, vTexCoord)) - 1.0));\n"; - else - sourceCode += "vec3 normal = normalize(vNormal);\n"; - - sourceCode += "\n" - "for (int i = 0; i < LightCount; ++i)\n" - "{\n"; - - if (glsl140) - { - sourceCode += "switch (Lights[i].type)\n" - "{\n" - "case LIGHT_DIRECTIONAL:\n"; - } - else // Le GLSL 110 n'a pas d'instruction switch - sourceCode += "if (Lights[i].type == LIGHT_DIRECTIONAL)\n"; - - // Directional Light - sourceCode += "{\n" - "vec3 lightDir = normalize(-Lights[i].parameters1.xyz);\n" - "light += Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);\n" - "\n" - "float lambert = max(dot(normal, lightDir), 0.0);\n" - "light += lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;\n" - "\n" - "if (MaterialShininess > 0.0)\n" - "{\n" - "vec3 eyeVec = normalize(CameraPosition - vWorldPos);\n" - "vec3 reflection = reflect(-lightDir, normal);\n" - "\n" - "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);\n"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += "si"; - else - sourceCode += "light"; - - sourceCode += " += specular * Lights[i].specular.rgb * MaterialSpecular.rgb;\n" - "}\n"; - - if (glsl140) - { - sourceCode += "break;\n" - "}\n" - "\n" - "case LIGHT_POINT:\n"; - } - else - sourceCode += "}\n" - "else if (Lights[i].type == LIGHT_POINT)\n"; - - // Point Light - sourceCode += "{\n" - "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;\n" - "\n" - "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.x*length(lightDir), 0.0);\n" - "light += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);\n" - "\n" - "lightDir = normalize(lightDir);\n" - "float lambert = max(dot(normal, lightDir), 0.0);\n" - "light += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;\n" - "\n" - "if (MaterialShininess > 0.0)\n" - "{\n" - "vec3 eyeVec = normalize(CameraPosition - vWorldPos);\n" - "vec3 reflection = reflect(-lightDir, normal);\n" - "\n" - "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);\n"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += "si"; - else - sourceCode += "light"; - - sourceCode += " += att * specular * Lights[i].specular.rgb * MaterialSpecular.rgb;\n" - "}\n"; - - if (glsl140) - { - sourceCode += "break;\n" - "}\n" - "\n" - "case LIGHT_SPOT:\n"; - } - else - { - sourceCode += "}\n" - "else if (Lights[i].type == LIGHT_SPOT)\n"; - } - - // Spot Light - sourceCode += "{\n" - "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;\n" - "\n" - "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*length(lightDir), 0.0);\n" - "light += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);\n" - "\n" - "lightDir = normalize(lightDir);\n" - "\n" - "float curAngle = dot(Lights[i].parameters2.xyz, -lightDir);\n" - "float outerAngle = Lights[i].parameters3.y;\n" - "float innerMinusOuterAngle = Lights[i].parameters3.x - outerAngle;\n" - "float lambert = max(dot(normal, lightDir), 0.0);\n" - "att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0);\n" - "light += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;\n" - "\n" - "if (MaterialShininess > 0.0)\n" - "{\n" - "vec3 eyeVec = normalize(CameraPosition - vWorldPos);\n" - "vec3 reflection = reflect(-lightDir, normal);\n" - "\n" - "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);\n"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += "si"; - else - sourceCode += "light"; - - sourceCode += " += att * specular * Lights[i].specular.rgb * MaterialSpecular.rgb;\n" - "}\n"; - - if (glsl140) - { - sourceCode += "break;\n" - "}\n" - "default:\n" - "break;\n" - "}\n"; - } - else - sourceCode += "}\n"; - - sourceCode += "}\n" - "\n"; - - sourceCode += "vec3 lighting = light"; - - if (flags & nzShaderFlags_DiffuseMapping) - sourceCode += "*vec3(" + textureLookupKW + "(MaterialDiffuseMap, vTexCoord))"; - - if (flags & nzShaderFlags_SpecularMapping) - sourceCode += " + si*vec3(" + textureLookupKW + "(MaterialSpecularMap, vTexCoord))"; // Utiliser l'alpha de MaterialSpecular n'aurait aucun sens - - sourceCode += ";\n"; - - if (flags & nzShaderFlags_EmissiveMapping) - { - sourceCode += "float intensity = light.r*0.3 + light.g*0.59 + light.b*0.11;\n" - "vec3 emission = vec3(" + textureLookupKW + "(MaterialEmissiveMap, vTexCoord));\n" - + fragmentColorKW + " = vec4(mix(lighting, emission, clamp(1.0 - 3.0*intensity, 0.0, 1.0)), alpha);\n"; - } - else - sourceCode += fragmentColorKW + " = vec4(lighting, alpha);\n"; - } - else - { - sourceCode += fragmentColorKW + " = MaterialDiffuse"; - - if (flags & nzShaderFlags_DiffuseMapping) - sourceCode += "*vec4(" + textureLookupKW + "(MaterialDiffuseMap, vTexCoord).rgb, alpha);\n"; - - sourceCode += ";\n"; - } - - sourceCode += "}\n"; - - return sourceCode; - } - - NzString BuildVertexShaderSource(nzUInt32 flags) - { - bool glsl140 = (NzOpenGL::GetVersion() >= 310); - bool uvMapping = (flags & nzShaderFlags_DiffuseMapping || flags & nzShaderFlags_NormalMapping || flags & nzShaderFlags_SpecularMapping); - - NzString inKW = (glsl140) ? "in" : "attribute"; - NzString outKW = (glsl140) ? "out" : "varying"; - - NzString sourceCode; - sourceCode.Reserve(512); // Le shader peut faire plus, mais cela évite déjà beaucoup de petites allocations - - /********************Version de GLSL********************/ - sourceCode = "#version "; - if (glsl140) - sourceCode += "140\n"; - else - sourceCode += "110\n"; - - sourceCode += '\n'; - - /********************Uniformes********************/ - if (flags & nzShaderFlags_Instancing) - sourceCode += "uniform mat4 ViewProjMatrix;\n"; - else - { - if (flags & nzShaderFlags_Lighting) - sourceCode += "uniform mat4 WorldMatrix;\n"; - - sourceCode += "uniform mat4 WorldViewProjMatrix;\n"; - } - - sourceCode += '\n'; - - /********************Entrant********************/ - if (flags & nzShaderFlags_Instancing) - sourceCode += inKW + " mat4 InstanceMatrix;\n"; - - sourceCode += inKW + " vec3 VertexPosition;\n"; - - if (flags & nzShaderFlags_Lighting) - { - sourceCode += inKW + " vec3 VertexNormal;\n"; - sourceCode += inKW + " vec3 VertexTangent;\n"; - } - - if (uvMapping) - sourceCode += inKW + " vec2 VertexTexCoord;\n"; - - sourceCode += '\n'; - - /********************Sortant********************/ - if (flags & nzShaderFlags_Lighting) - { - if (flags & nzShaderFlags_NormalMapping) - sourceCode += outKW + " mat3 vLightToWorld;\n"; - else - sourceCode += outKW + " vec3 vNormal;\n"; - } - - if (uvMapping) - sourceCode += outKW + " vec2 vTexCoord;\n"; - - if (flags & nzShaderFlags_Lighting) - sourceCode += outKW + " vec3 vWorldPos;\n"; - - sourceCode += '\n'; - - /********************Code********************/ - sourceCode += "void main()\n" - "{\n"; - - if (flags & nzShaderFlags_Instancing) - sourceCode += "gl_Position = ViewProjMatrix * InstanceMatrix * vec4(VertexPosition, 1.0);\n"; - else - sourceCode += "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"; - - if (flags & nzShaderFlags_Lighting) - { - if (flags & nzShaderFlags_Instancing) - { - if (glsl140) - sourceCode += "mat3 rotationMatrix = mat3(InstanceMatrix);\n"; - else - sourceCode += "mat3 rotationMatrix = mat3(InstanceMatrix[0].xyz, InstanceMatrix[1].xyz, InstanceMatrix[2].xyz);\n"; - } - else - { - if (glsl140) - sourceCode += "mat3 rotationMatrix = mat3(WorldMatrix);\n"; - else - sourceCode += "mat3 rotationMatrix = mat3(WorldMatrix[0].xyz, WorldMatrix[1].xyz, WorldMatrix[2].xyz);\n"; - } - - if (flags & nzShaderFlags_NormalMapping) - { - sourceCode += "\n" - "vec3 binormal = cross(VertexNormal, VertexTangent);\n" - "vLightToWorld[0] = normalize(rotationMatrix * VertexTangent);\n" - "vLightToWorld[1] = normalize(rotationMatrix * binormal);\n" - "vLightToWorld[2] = normalize(rotationMatrix * VertexNormal);\n" - "\n"; - } - else - sourceCode += "vNormal = normalize(rotationMatrix * VertexNormal);\n"; - } - - if (uvMapping) - { - if (flags & nzShaderFlags_FlipUVs) - sourceCode += "vTexCoord = vec2(VertexTexCoord.x, 1.0 - VertexTexCoord.y);\n"; - else - sourceCode += "vTexCoord = VertexTexCoord;\n"; - } - - if (flags & nzShaderFlags_Lighting) - { - if (flags & nzShaderFlags_Instancing) - sourceCode += "vWorldPos = vec3(InstanceMatrix * vec4(VertexPosition, 1.0));\n"; - else - sourceCode += "vWorldPos = vec3(WorldMatrix * vec4(VertexPosition, 1.0));\n"; - } - - sourceCode += "}\n"; - - return sourceCode; - } - - NzShader* BuildShader(nzUInt32 flags) - { - std::unique_ptr shader(new NzShader); - if (!shader->Create(nzShaderLanguage_GLSL)) - { - NazaraError("Failed to create shader"); - return nullptr; - } - - NzString fragmentSource = BuildFragmentShaderSource(flags); - if (!shader->Load(nzShaderType_Fragment, fragmentSource)) - { - NazaraError("Failed to load fragment shader: " + shader->GetLog()); - NazaraNotice("Fragment shader source: "); - NazaraNotice(fragmentSource); - return nullptr; - } - - NzString vertexSource = BuildVertexShaderSource(flags); - if (!shader->Load(nzShaderType_Vertex, vertexSource)) - { - NazaraError("Failed to load vertex shader: " + shader->GetLog()); - NazaraNotice("Vertex shader source: "); - NazaraNotice(vertexSource); - return nullptr; - } - - #ifdef NAZARA_DEBUG - NazaraNotice("Fragment shader source: "); - NazaraNotice(fragmentSource); - NazaraNotice("Vertex shader source: "); - NazaraNotice(vertexSource); - #endif - - if (!shader->Compile()) - { - NazaraError("Failed to compile shader: " + shader->GetLog()); - return nullptr; - } - - shader->SetFlags(flags); - shader->SetPersistent(false); - - return shader.release(); - } -} - -const NzShader* NzShaderBuilder::Get(nzUInt32 flags) -{ - auto it = s_shaders.find(flags); - if (it == s_shaders.end()) - { - // Alors nous créons le shader - NzShader* shader = BuildShader(flags); - if (!shader) - { - NazaraWarning("Failed to build shader (flags: 0x" + NzString::Number(flags, 16) + "), using default one..."); - - shader = s_shaders[0]; // Shader par défaut - } - - s_shaders[flags] = shader; - - return shader; - } - else - return it->second; -} - -bool NzShaderBuilder::Initialize() -{ - NzShader* shader = BuildShader(0); - if (!shader) - { - NazaraInternalError("Failed to build default shader"); - return false; - } - - s_shaders[0] = shader; - - return true; -} - -void NzShaderBuilder::Uninitialize() -{ - s_shaders.clear(); -} diff --git a/src/Nazara/Renderer/ShaderManager.cpp b/src/Nazara/Renderer/ShaderManager.cpp new file mode 100644 index 000000000..c35948ad3 --- /dev/null +++ b/src/Nazara/Renderer/ShaderManager.cpp @@ -0,0 +1,841 @@ +// Copyright (C) 2013 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 +#include +#include +#include +#include + +namespace +{ + struct ParamsHash + { + std::size_t operator()(const NzShaderManagerParams& params) const + { + static_assert(nzShaderTarget_Max < 0x4, "Maximum shader target takes more than 2 bits"); + + std::size_t h = (params.target << 0) | // 2 bits + (params.flags << 2); // 8 bits + + switch (params.target) + { + case nzShaderTarget_FullscreenQuad: + h |= (params.fullscreenQuad.alphaMapping << 10) | // 1 bit + (params.fullscreenQuad.alphaTest << 11) | // 1 bit + (params.fullscreenQuad.diffuseMapping << 12); // 1 bit + break; + + case nzShaderTarget_Model: + h |= (params.model.alphaMapping << 10) | // 1 bit + (params.model.alphaTest << 11) | // 1 bit + (params.model.diffuseMapping << 12) | // 1 bit + (params.model.emissiveMapping << 13) | // 1 bit + (params.model.lighting << 14) | // 1 bit + (params.model.normalMapping << 15) | // 1 bit + (params.model.parallaxMapping << 16) | // 1 bit + (params.model.specularMapping << 17); // 1 bit + break; + + case nzShaderTarget_None: + break; + } + + return h; + } + }; + + struct ParamsEquality + { + bool operator()(const NzShaderManagerParams& first, const NzShaderManagerParams& second) const + { + if (first.target != second.target || first.flags != second.flags) + return false; + + switch (first.target) + { + case nzShaderTarget_FullscreenQuad: + return std::memcmp(&first.fullscreenQuad, &second.fullscreenQuad, sizeof(NzShaderManagerParams::FullscreenQuad)) == 0; + + case nzShaderTarget_Model: + return std::memcmp(&first.model, &second.model, sizeof(NzShaderManagerParams::Model)) == 0; + + case nzShaderTarget_None: + return true; + } + + return false; + } + }; + + std::unordered_map s_shaders; + NzString s_inKW; + NzString s_outKW; + NzString s_fragmentColorKW; + NzString s_textureLookupKW; + bool s_earlyFragmentTest; + bool s_glsl140; + unsigned int s_glslVersion; +} + +const NzShader* NzShaderManager::Get(const NzShaderManagerParams& params) +{ + auto it = s_shaders.find(params); + if (it == s_shaders.end()) + { + // Alors nous gébérons le shader + NzShader* shader = GenerateShader(params); + if (!shader) + { + NazaraWarning("Failed to build shader, using default one..."); + + NzShaderManagerParams defaultParams; + defaultParams.flags = params.flags; + defaultParams.target = nzShaderTarget_None; + + shader = s_shaders[defaultParams]; // Shader par défaut + } + + s_shaders[params] = shader; + + return shader; + } + else + return it->second; +} + +NzString NzShaderManager::BuildFragmentCode(const NzShaderManagerParams& params) +{ + #ifdef NAZARA_DEBUG + if (params.target > nzShaderTarget_Max) + { + NazaraError("Shader target out of enum"); + return NzString(); + } + #endif + + NzString source; + source.Reserve(2048); // Le shader peut faire plus, mais cela évite déjà beaucoup de petites allocations + + /********************Header********************/ + source += "#version "; + source += NzString::Number(s_glslVersion); + source += "\n\n"; + + if (s_earlyFragmentTest) + source += "layout(early_fragment_tests) in;" "\n\n"; + + switch (params.target) + { + case nzShaderTarget_FullscreenQuad: + { + /********************Entrant********************/ + if (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping) + source += s_inKW + " vec2 vTexCoord;" "\n\n"; + + /********************Sortant********************/ + if (s_glsl140) + source += "out vec4 RenderTarget0;" "\n\n"; + + /********************Uniformes********************/ + if (params.fullscreenQuad.alphaMapping) + source += "uniform sampler2D MaterialAlphaMap;" "\n"; + + if (params.fullscreenQuad.alphaTest) + source += "uniform float MaterialAlphaThreshold;" "\n"; + + source += "uniform vec4 MaterialDiffuse;" "\n"; + + if (params.fullscreenQuad.diffuseMapping) + source += "uniform sampler2D MaterialDiffuseMap;" "\n"; + + source += '\n'; + + /********************Fonctions********************/ + source += "void main()" "\n" + "{" "\n"; + + source += "\t" "vec4 fragmentColor = MaterialDiffuse"; + if (params.fullscreenQuad.diffuseMapping) + source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)"; + + source += ";" "\n" + "\t" "float fragmentAlpha = "; + + if (params.fullscreenQuad.diffuseMapping) + source += "fragmentColor.a"; + else + source += "MaterialDiffuse.a"; + + if (params.fullscreenQuad.alphaMapping) + source += '*' + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; + + source += ";" "\n"; + + if (params.fullscreenQuad.alphaMapping) + { + source += "if (fragmentAlpha < MaterialAlphaThreshold)" "\n" + "\t" "discard;" "\n"; + } + + source += "\t" "RenderTarget0 = fragmentColor;" "\n" + "}" "\n"; + break; + } + + case nzShaderTarget_Model: + { + //bool parallaxMapping = (params.model.lighting && params.model.parallaxMapping); + bool uvMapping = (params.model.alphaMapping || params.model.diffuseMapping || params.model.normalMapping || params.model.specularMapping); + + if (params.model.lighting) + { + source += "#define LIGHT_DIRECTIONAL 0" "\n" + "#define LIGHT_POINT 1" "\n" + "#define LIGHT_SPOT 2" "\n\n"; + } + + /********************Entrant********************/ + if (params.model.lighting) + { + if (params.model.normalMapping) + source += s_inKW + " mat3 vLightToWorld;" "\n"; + else + source += s_inKW + " vec3 vNormal;" "\n"; + } + + if (uvMapping) + source += s_inKW + " vec2 vTexCoord;" "\n"; + + if (params.model.lighting) + source += s_inKW + " vec3 vWorldPos;" "\n"; + + if (params.model.lighting || uvMapping) + source += '\n'; + + /********************Sortant********************/ + if (s_glsl140) + source += "out vec4 RenderTarget0;" "\n\n"; + + /********************Uniformes********************/ + if (params.model.lighting) + { + source += "struct Light" "\n" + "{" "\n" + "\t" "int type;" "\n" + "\t" "vec4 ambient;" "\n" + "\t" "vec4 diffuse;" "\n" + "\t" "vec4 specular;" "\n\n" + + "\t" "vec4 parameters1;" "\n" + "\t" "vec4 parameters2;" "\n" + "\t" "vec2 parameters3;" "\n" + "};" "\n\n" + + "uniform vec3 CameraPosition;" "\n" + "uniform Light Lights[3];" "\n" + "uniform vec4 MaterialAmbient;" "\n"; + } + + if (params.model.alphaMapping) + source += "uniform sampler2D MaterialAlphaMap;" "\n"; + + if (params.model.alphaTest) + source += "uniform float MaterialAlphaThreshold;" "\n"; + + source += "uniform vec4 MaterialDiffuse;" "\n"; + + if (params.model.diffuseMapping) + source += "uniform sampler2D MaterialDiffuseMap;" "\n"; + + if (params.model.emissiveMapping) + source += "uniform sampler2D MaterialEmissiveMap;" "\n"; + + if (params.model.lighting) + { + if (params.model.normalMapping) + source += "uniform sampler2D MaterialNormalMap;" "\n"; + + source += "uniform float MaterialShininess;" "\n" + "uniform vec4 MaterialSpecular;" "\n"; + + if (params.model.specularMapping) + source += "uniform sampler2D MaterialSpecularMap;" "\n"; + + source += "uniform vec4 SceneAmbient;" "\n"; + } + + source += '\n'; + + /********************Fonctions********************/ + source += "void main()" "\n" + "{" "\n"; + + /*if (!parallaxMapping) + {*/ + source += "\t" "vec4 fragmentColor = MaterialDiffuse"; + if (params.model.diffuseMapping) + source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)"; + + source += ";" "\n"; + //} + + if (params.model.alphaMapping) + { + source += "\t" "fragmentColor.a *= "; + + if (params.model.alphaMapping) + source += s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; + + /*if (parallaxMapping && params.model.diffuseMapping) + source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord).a";*/ + + source += ";" "\n"; + } + + if (params.model.alphaTest) + { + source += "if (diffuseColor.a < MaterialAlphaThreshold)" "\n" + "\t" "discard;" "\n"; + } + + if (params.model.lighting) + { + source += "\t" "vec3 lightColor = vec3(0.0);" "\n"; + + if (params.model.specularMapping) + source += "\t" "vec3 specularColor = vec3(0.0);" "\n"; + + if (params.model.normalMapping) + source += "\t" "vec3 normal = normalize(vLightToWorld * (2.0 * vec3(" + s_textureLookupKW + "(MaterialNormalMap, vTexCoord)) - 1.0));" "\n"; + else + source += "\t" "vec3 normal = normalize(vNormal);" "\n"; + + source += '\n'; + + source += "\t" "if (MaterialShininess > 0.0)" "\n" + "\t" "{" "\n" + "\t\t" "vec3 eyeVec = normalize(CameraPosition - vWorldPos);" "\n\n" + + "\t\t" "for (int i = 0; i < 3; ++i)" "\n" + "\t\t" "{" "\n"; + + if (s_glsl140) + { + source += "\t\t\t" "switch (Lights[i].type)" "\n" + "\t\t\t" "{" "\n" + "\t\t\t\t" "case LIGHT_DIRECTIONAL:" "\n"; + } + else // Le GLSL 110 n'a pas d'instruction switch + source += "\t\t\t" "if (Lights[i].type == LIGHT_DIRECTIONAL)" "\n"; + + // Directional Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = normalize(-Lights[i].parameters1.xyz);" "\n" + "\t\t\t\t\t" "lightColor += Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n\n" + + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n" + + "\t\t\t\t\t" "vec3 reflection = reflect(-lightDir, normal);" "\n" + "\t\t\t\t\t" "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);" "\n"; + + if (params.model.specularMapping) + source += "\t\t\t\t\t" "specularColor"; + else + source += "\t\t\t\t\t" "lightColor"; + + source += " += specular * Lights[i].specular.rgb * MaterialSpecular.rgb;" "\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + + "\t\t\t\t" "case LIGHT_POINT:" "\n"; + } + else + source += "\t\t\t" "}" "\n" + "\t\t\t" "else if (Lights[i].type == LIGHT_POINT)" "\n"; + + // Point Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;" "\n\n" + + "\t\t\t\t\t" "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.x*length(lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n" + + "\t\t\t\t\t" "lightDir = normalize(lightDir);" "\n" + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n" + + "\t\t\t\t\t" "vec3 reflection = reflect(-lightDir, normal);" "\n" + "\t\t\t\t\t" "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);" "\n"; + + if (params.model.specularMapping) + source += "\t\t\t\t\t" "specularColor"; + else + source += "\t\t\t\t\t" "lightColor"; + + source += " += att * specular * Lights[i].specular.rgb * MaterialSpecular.rgb;" "\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + + "\t\t\t\t" "case LIGHT_SPOT:" "\n"; + } + else + { + source += "\t\t\t" "}" "\n" + "\t\t\t" "else if (Lights[i].type == LIGHT_SPOT)" "\n"; + } + + // Spot Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;" "\n\n" + + "\t\t\t\t\t" "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*length(lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n\n" + + "\t\t\t\t\t" "lightDir = normalize(lightDir);" "\n\n" + + "\t\t\t\t\t" "float curAngle = dot(Lights[i].parameters2.xyz, -lightDir);" "\n" + "\t\t\t\t\t" "float outerAngle = Lights[i].parameters3.y;" "\n" + "\t\t\t\t\t" "float innerMinusOuterAngle = Lights[i].parameters3.x - outerAngle;" "\n" + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n" + + "\t\t\t\t\t" "vec3 reflection = reflect(-lightDir, normal);" "\n" + "\t\t\t\t\t" "float specular = pow(max(dot(reflection, eyeVec), 0.0), MaterialShininess);" "\n"; + + if (params.model.specularMapping) + source += "\t\t\t\t\t" "specularColor"; + else + source += "\t\t\t\t\t" "lightColor"; + + source += " += att * specular * Lights[i].specular.rgb * MaterialSpecular.rgb;\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + "\t\t\t\t" "default:" "\n" + "\t\t\t\t\t" "break;" "\n" + "\t\t\t" "}" "\n"; + } + else + source += "\t\t\t" "}" "\n"; + + source += "\t\t" "}" "\n" + "\t" "}" "\n" + "\t" "else" "\n" + "\t" "{" "\n"; + + source += "\t\t" "for (int i = 0; i < 3; ++i)" "\n" + "\t\t" "{" "\n"; + + if (s_glsl140) + { + source += "\t\t\t" "switch (Lights[i].type)" "\n" + "\t\t\t" "{" "\n" + "\t\t\t\t" "case LIGHT_DIRECTIONAL:" "\n"; + } + else // Le GLSL 110 n'a pas d'instruction switch + source += "\t\t\t" "if (Lights[i].type == LIGHT_DIRECTIONAL)" "\n"; + + // Directional Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = normalize(-Lights[i].parameters1.xyz);" "\n" + "\t\t\t\t\t" "lightColor += Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n\n" + + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + + "\t\t\t\t" "case LIGHT_POINT:" "\n"; + } + else + source += "\t\t\t" "}" "\n" + "\t\t\t" "else if (Lights[i].type == LIGHT_POINT)" "\n"; + + // Point Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;" "\n\n" + + "\t\t\t\t\t" "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.x*length(lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n" + + "\t\t\t\t\t" "lightDir = normalize(lightDir);" "\n" + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + + "\t\t\t\t" "case LIGHT_SPOT:" "\n"; + } + else + { + source += "\t\t\t" "}" "\n" + "\t\t\t" "else if (Lights[i].type == LIGHT_SPOT)" "\n"; + } + + // Spot Light + source += "\t\t\t\t" "{" "\n" + "\t\t\t\t\t" "vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos;" "\n\n" + + "\t\t\t\t\t" "float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*length(lightDir), 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * Lights[i].ambient.rgb * (MaterialAmbient.rgb + SceneAmbient.rgb);" "\n\n" + + "\t\t\t\t\t" "lightDir = normalize(lightDir);" "\n\n" + + "\t\t\t\t\t" "float curAngle = dot(Lights[i].parameters2.xyz, -lightDir);" "\n" + "\t\t\t\t\t" "float outerAngle = Lights[i].parameters3.y;" "\n" + "\t\t\t\t\t" "float innerMinusOuterAngle = Lights[i].parameters3.x - outerAngle;" "\n" + "\t\t\t\t\t" "float lambert = max(dot(normal, lightDir), 0.0);" "\n" + "\t\t\t\t\t" "att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0);" "\n" + "\t\t\t\t\t" "lightColor += att * lambert * Lights[i].diffuse.rgb * MaterialDiffuse.rgb;" "\n\n"; + + if (s_glsl140) + { + source += "\t\t\t\t\t" "break;" "\n" + "\t\t\t\t" "}" "\n\n" + "\t\t\t\t" "default:" "\n" + "\t\t\t\t\t" "break;" "\n" + "\t\t\t" "}" "\n"; + } + else + source += "\t\t\t" "}" "\n"; + + source += "\t\t" "}" "\n" + "\t" "}" "\n\n" + + "\t" "fragmentColor *= vec4(lightColor"; + + if (params.model.specularMapping) + source += "+ specularColor*" + s_textureLookupKW + "(MaterialSpecularMap, vTexCoord).rgb"; // Utiliser l'alpha de MaterialSpecular n'aurait aucun sens + + source += ", 1.0);" "\n"; + + if (params.model.emissiveMapping) + { + if (params.model.specularMapping) + source += "\t" "float lightIntensity = dot(lightColor + specularColor, vec3(0.3, 0.59, 0.11));" "\n"; + else + source += "\t" "float lightIntensity = dot(lightColor, vec3(0.3, 0.59, 0.11));" "\n"; + + source += "\t" "vec3 emissionColor = MaterialDiffuse.rgb*" + s_textureLookupKW + "(MaterialEmissiveMap, vTexCoord).rgb;" "\n" + "\t" + s_fragmentColorKW + " = vec4(mix(fragmentColor.rgb, emissionColor, clamp(1.0 - 3.0*lightIntensity, 0.0, 1.0)), fragmentColor.a);" "\n"; + } + else + source += '\t' + s_fragmentColorKW + " = fragmentColor;" "\n"; + } + else + source += '\t' + s_fragmentColorKW + " = fragmentColor;" "\n"; + + source += "}" "\n"; + break; + } + + case nzShaderTarget_None: + { + /********************Sortant********************/ + if (s_glsl140) + source += "out vec4 RenderTarget0;" "\n\n"; + + /********************Uniformes********************/ + source += "uniform vec4 MaterialDiffuse;" "\n\n"; + + /********************Fonctions********************/ + source += "void main()" "\n" + "{" "\n"; + source += '\t' + s_fragmentColorKW + " = MaterialDiffuse;" "\n"; + source += "}" "\n"; + break; + } + } + + return source; +} + +NzString NzShaderManager::BuildVertexCode(const NzShaderManagerParams& params) +{ + #ifdef NAZARA_DEBUG + if (params.target > nzShaderTarget_Max) + { + NazaraError("Shader target out of enum"); + return NzString(); + } + #endif + + NzString source; + source.Reserve(2048); // Le shader peut faire plus, mais cela évite déjà beaucoup de petites allocations + + /********************Header********************/ + source += "#version "; + source += NzString::Number(s_glslVersion); + source += "\n\n"; + + switch (params.target) + { + case nzShaderTarget_FullscreenQuad: + { + bool uvMapping = (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping); + + /********************Entrant********************/ + source += s_inKW + " vec2 VertexPosition;" "\n\n"; + + /********************Sortant********************/ + if (uvMapping) + source += s_outKW + " vec2 vTexCoord;" "\n\n"; + + /********************Code********************/ + source += "void main()" "\n" + "{" "\n" + "\t" "gl_Position = vec4(VertexPosition, 0.0, 1.0);" "\n"; + + if (uvMapping) + { + if (params.flags & nzShaderFlags_FlipUVs) + source += "\t" "vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, 0.5 - VertexPosition.y*0.5;" "\n"; + else + source += "\t" "vTexCoord = vec2((VertexPosition.x + 1.0)*0.5, (VertexPosition.y + 1.0)*0.5);" "\n"; + } + + source += "}" "\n"; + break; + } + + case nzShaderTarget_Model: + { + bool uvMapping = (params.model.diffuseMapping || params.model.normalMapping || params.model.specularMapping); + + /********************Entrant********************/ + if (params.flags & nzShaderFlags_Instancing) + source += s_inKW + " mat4 InstanceData0;" "\n"; + + source += s_inKW + " vec3 VertexPosition;" "\n"; + + if (params.model.lighting) + { + source += s_inKW + " vec3 VertexNormal;" "\n"; + source += s_inKW + " vec3 VertexTangent;" "\n"; + } + + if (uvMapping) + source += s_inKW + " vec2 VertexTexCoord;" "\n"; + + source += '\n'; + + /********************Sortant********************/ + if (params.model.lighting) + { + if (params.model.normalMapping) + source += s_outKW + " mat3 vLightToWorld;" "\n"; + else + source += s_outKW + " vec3 vNormal;" "\n"; + } + + if (uvMapping) + source += s_outKW + " vec2 vTexCoord;" "\n"; + + if (params.model.lighting) + source += s_outKW + " vec3 vWorldPos;" "\n"; + + if (params.model.lighting || uvMapping) + source += '\n'; + + /********************Uniformes********************/ + if (params.flags & nzShaderFlags_Instancing) + source += "uniform mat4 ViewProjMatrix;" "\n"; + else + { + if (params.model.lighting) + source += "uniform mat4 WorldMatrix;" "\n"; + + source += "uniform mat4 WorldViewProjMatrix;" "\n"; + } + + source += '\n'; + + /********************Code********************/ + source += "void main()" "\n" + "{" "\n"; + + if (params.flags & nzShaderFlags_Instancing) + source += "\t" "gl_Position = ViewProjMatrix * InstanceData0 * vec4(VertexPosition, 1.0);" "\n"; + else + source += "\t" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);" "\n"; + + if (params.model.lighting) + { + if (params.flags & nzShaderFlags_Instancing) + { + if (s_glsl140) + source += "\t" "mat3 rotationMatrix = mat3(InstanceData0);" "\n"; + else + source += "\t" "mat3 rotationMatrix = mat3(InstanceData0[0].xyz, InstanceData0[1].xyz, InstanceData0[2].xyz);" "\n"; + } + else + { + if (s_glsl140) + source += "\t" "mat3 rotationMatrix = mat3(WorldMatrix);" "\n"; + else + source += "\t" "mat3 rotationMatrix = mat3(WorldMatrix[0].xyz, WorldMatrix[1].xyz, WorldMatrix[2].xyz);" "\n"; + } + + if (params.model.normalMapping) + { + source += "\n" + "\t" "vec3 binormal = cross(VertexNormal, VertexTangent);" "\n" + "\t" "vLightToWorld[0] = normalize(rotationMatrix * VertexTangent);" "\n" + "\t" "vLightToWorld[1] = normalize(rotationMatrix * binormal);" "\n" + "\t" "vLightToWorld[2] = normalize(rotationMatrix * VertexNormal);" "\n\n"; + } + else + source += "\t" "vNormal = normalize(rotationMatrix * VertexNormal);" "\n"; + } + + if (uvMapping) + { + if (params.flags & nzShaderFlags_FlipUVs) + source += "\t" "vTexCoord = vec2(VertexTexCoord.x, 1.0 - VertexTexCoord.y);" "\n"; + else + source += "\t" "vTexCoord = VertexTexCoord;" "\n"; + } + + if (params.model.lighting) + { + if (params.flags & nzShaderFlags_Instancing) + source += "\t" "vWorldPos = vec3(InstanceData0 * vec4(VertexPosition, 1.0));" "\n"; + else + source += "\t" "vWorldPos = vec3(WorldMatrix * vec4(VertexPosition, 1.0));" "\n"; + } + + source += "}" "\n"; + break; + } + + case nzShaderTarget_None: + { + /********************Entrant********************/ + if (params.flags & nzShaderFlags_Instancing) + source += s_inKW + " mat4 InstanceData0;" "\n"; + + source += s_inKW + " vec3 VertexPosition;" "\n\n"; + + /********************Uniformes********************/ + if (params.flags & nzShaderFlags_Instancing) + source += "uniform mat4 ViewProjMatrix;" "\n\n"; + else + source += "uniform mat4 WorldViewProjMatrix;" "\n\n"; + + /********************Code********************/ + source += "void main()" "\n" + "{" "\n"; + + if (params.flags & nzShaderFlags_Instancing) + source += "\t" "gl_Position = ViewProjMatrix * InstanceData0 * vec4(VertexPosition, 1.0);" "\n"; + else + source += "\t" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);" "\n"; + + source += "}" "\n"; + break; + } + } + + return source; +} + +NzShader* NzShaderManager::GenerateShader(const NzShaderManagerParams& params) +{ + std::unique_ptr shader(new NzShader); + shader->SetPersistent(false); + + if (!shader->Create(nzShaderLanguage_GLSL)) + { + NazaraError("Failed to create shader"); + return nullptr; + } + + NzString fragmentSource = BuildFragmentCode(params); + if (!shader->Load(nzShaderType_Fragment, fragmentSource)) + { + NazaraError("Failed to load fragment shader: " + shader->GetLog()); + NazaraNotice("Source:\n" + fragmentSource); + return nullptr; + } + + NzString vertexSource = BuildVertexCode(params); + if (!shader->Load(nzShaderType_Vertex, vertexSource)) + { + NazaraError("Failed to load vertex shader: " + shader->GetLog()); + NazaraNotice("Source:\n" + vertexSource); + return nullptr; + } + + #ifdef NAZARA_DEBUG + NazaraNotice("Fragment shader source:\n" + fragmentSource); + NazaraNotice("Vertex shader source:\n" + vertexSource); + #endif + + if (!shader->Compile()) + { + NazaraError("Failed to compile shader: " + shader->GetLog()); + return nullptr; + } + + return shader.release(); +} + +bool NzShaderManager::Initialize() +{ + s_glslVersion = NzOpenGL::GetGLSLVersion(); + s_earlyFragmentTest = (s_glslVersion >= 420 || NzOpenGL::IsSupported(nzOpenGLExtension_Shader_ImageLoadStore)); + s_glsl140 = (s_glslVersion >= 140); + + s_fragmentColorKW = (s_glsl140) ? "RenderTarget0" : "gl_FragColor"; + s_inKW = (s_glsl140) ? "in" : "varying"; + s_outKW = (s_glsl140) ? "out" : "varying"; + s_textureLookupKW = (s_glsl140) ? "texture" : "texture2D"; + + NzShaderManagerParams params; + params.target = nzShaderTarget_None; + for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i) + { + params.flags = i; + + NzShader* shader = GenerateShader(params); + if (!shader) + { + NazaraError("Failed to generate default shader (flags: 0x" + NzString::Number(i, 16) + ')'); + Uninitialize(); + return false; + } + + s_shaders[params] = shader; + } + + return true; +} + +void NzShaderManager::Uninitialize() +{ + s_shaders.clear(); + s_fragmentColorKW.Clear(false); + s_inKW.Clear(false); + s_outKW.Clear(false); + s_textureLookupKW.Clear(false); +}