Graphics: Separate pipeline state from Material into a new class, MaterialPipeline

This allows much more efficient batching, along with pipeline reusage and preparation for the Vulkan API


Former-commit-id: 4ed2f66567f7da6b6b6ee073e4d855b9a935000d [formerly b540f468fc700a16d5136d4dbb8632e23882fd3d] [formerly 37fff624ec65cc387130875410b6ea35c1a5bcfb [formerly ab9a88f514f46f6596499e285981fa6da588bb03]]
Former-commit-id: a2e8859196c0f72b7d7ffd8764a887e6c8173743 [formerly c886cdade14769db243ff993a1741f6052a2eb2a]
Former-commit-id: e1d02662fb1ac165c7e888380afee7601350060f
This commit is contained in:
Lynix 2016-08-05 22:09:39 +02:00
parent 8fbe279a50
commit 87b5047b14
31 changed files with 2249 additions and 1422 deletions

View File

@ -28,8 +28,8 @@ namespace Ndk
m_characterSize(24)
{
Nz::MaterialRef backgroundMaterial = Nz::Material::New();
backgroundMaterial->Enable(Nz::RendererParameter_Blend, true);
backgroundMaterial->Enable(Nz::RendererParameter_DepthBuffer, false);
backgroundMaterial->EnableBlending(true);
backgroundMaterial->EnableDepthBuffer(false);
backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha);
backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha);

View File

@ -1,4 +1,4 @@
// This file was automatically generated on 12 Jul 2016 at 17:44:43
// This file was automatically generated on 20 Jul 2016 at 13:49:17
/*
Nazara Engine - Graphics module
@ -68,6 +68,7 @@
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/ParticleStruct.hpp>
#include <Nazara/Graphics/Renderable.hpp>
#include <Nazara/Graphics/MaterialPipeline.hpp>
#include <Nazara/Graphics/RenderTechniques.hpp>
#include <Nazara/Graphics/SceneData.hpp>
#include <Nazara/Graphics/SkeletalModel.hpp>

View File

@ -121,8 +121,7 @@ namespace Nz
inline void Billboard::SetDefaultMaterial()
{
MaterialRef material = Material::New();
material->Enable(RendererParameter_FaceCulling, true);
material->EnableLighting(false);
material->EnableFaceCulling(true);
SetMaterial(std::move(material));
}

View File

@ -9,7 +9,7 @@
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/ForwardRenderQueue.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Math/Box.hpp>
#include <Nazara/Math/Matrix4.hpp>
@ -21,8 +21,6 @@
namespace Nz
{
class ForwardRenderQueue;
class NAZARA_GRAPHICS_API DeferredRenderQueue : public AbstractRenderQueue
{
public:
@ -43,11 +41,6 @@ namespace Nz
void Clear(bool fully = false) override;
struct MeshDataComparator
{
bool operator()(const MeshData& data1, const MeshData& data2) const;
};
struct MeshInstanceEntry
{
NazaraSlot(IndexBuffer, OnIndexBufferRelease, indexBufferReleaseSlot);
@ -56,12 +49,7 @@ namespace Nz
std::vector<Matrix4f> instances;
};
typedef std::map<MeshData, MeshInstanceEntry, MeshDataComparator> MeshInstanceContainer;
struct BatchedModelMaterialComparator
{
bool operator()(const Material* mat1, const Material* mat2) const;
};
typedef std::map<MeshData, MeshInstanceEntry, ForwardRenderQueue::MeshDataComparator> MeshInstanceContainer;
struct BatchedModelEntry
{
@ -69,14 +57,21 @@ namespace Nz
MeshInstanceContainer meshMap;
bool enabled = false;
bool instancingEnabled = false;
};
typedef std::map<const Material*, BatchedModelEntry, BatchedModelMaterialComparator> ModelBatches;
typedef std::map<const Material*, BatchedModelEntry, ForwardRenderQueue::MaterialComparator> MeshMaterialBatches;
struct BatchedMaterialEntry
{
std::size_t maxInstanceCount = 0;
MeshMaterialBatches materialMap;
};
typedef std::map<const MaterialPipeline*, BatchedMaterialEntry, ForwardRenderQueue::MaterialPipelineComparator> MeshPipelineBatches;
struct Layer
{
ModelBatches opaqueModels;
MeshPipelineBatches opaqueModels;
unsigned int clearCount = 0;
};

View File

@ -18,7 +18,7 @@ namespace Nz
{
NazaraAssert(material, "Invalid material");
return material->HasDepthMaterial() || (material->IsEnabled(RendererParameter_DepthBuffer) && material->IsEnabled(RendererParameter_DepthWrite) && material->IsShadowCastingEnabled());
return material->HasDepthMaterial() || (material->IsDepthBufferEnabled() && material->IsDepthWriteEnabled() && material->IsShadowCastingEnabled());
}
}

View File

@ -63,6 +63,7 @@ namespace Nz
mutable std::unordered_map<const Shader*, ShaderUniforms> m_shaderUniforms;
Buffer m_vertexBuffer;
mutable DepthRenderQueue m_renderQueue;
Texture m_whiteTexture;
VertexBuffer m_billboardPointBuffer;
VertexBuffer m_spriteBuffer;

View File

@ -47,6 +47,16 @@ namespace Nz
void Sort(const AbstractViewer* viewer);
struct MaterialComparator
{
bool operator()(const Material* mat1, const Material* mat2) const;
};
struct MaterialPipelineComparator
{
bool operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const;
};
/// Billboards
struct BillboardData
{
@ -56,11 +66,6 @@ namespace Nz
Vector2f sinCos;
};
struct BatchedBillboardComparator
{
bool operator()(const Material* mat1, const Material* mat2) const;
};
struct BatchedBillboardEntry
{
NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot);
@ -68,7 +73,15 @@ namespace Nz
std::vector<BillboardData> billboards;
};
typedef std::map<const Material*, BatchedBillboardEntry, BatchedBillboardComparator> BatchedBillboardContainer;
typedef std::map<const Material*, BatchedBillboardEntry, MaterialComparator> BatchedBillboardContainer;
struct BatchedBillboardPipelineEntry
{
BatchedBillboardContainer materialMap;
bool enabled = false;
};
typedef std::map<const MaterialPipeline*, BatchedBillboardPipelineEntry, MaterialPipelineComparator> BillboardPipelineBatches;
/// Sprites
struct SpriteChain_XYZ_Color_UV
@ -84,22 +97,25 @@ namespace Nz
std::vector<SpriteChain_XYZ_Color_UV> spriteChains;
};
struct BatchedSpriteMaterialComparator
{
bool operator()(const Material* mat1, const Material* mat2);
};
typedef std::map<const Texture*, BatchedSpriteEntry> BasicSpriteOverlayContainer;
typedef std::map<const Texture*, BatchedSpriteEntry> SpriteOverlayBatches;
struct BatchedBasicSpriteEntry
{
NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot);
BasicSpriteOverlayContainer overlayMap;
SpriteOverlayBatches overlayMap;
bool enabled = false;
};
typedef std::map<const Material*, BatchedBasicSpriteEntry> BasicSpriteBatches;
typedef std::map<const Material*, BatchedBasicSpriteEntry, MaterialComparator> SpriteMaterialBatches;
struct BatchedSpritePipelineEntry
{
SpriteMaterialBatches materialMap;
bool enabled = false;
};
typedef std::map<const MaterialPipeline*, BatchedSpritePipelineEntry, MaterialPipelineComparator> SpritePipelineBatches;
/// Meshes
struct MeshDataComparator
@ -118,21 +134,23 @@ namespace Nz
typedef std::map<MeshData, MeshInstanceEntry, MeshDataComparator> MeshInstanceContainer;
struct BatchedModelMaterialComparator
{
bool operator()(const Material* mat1, const Material* mat2) const;
};
struct BatchedModelEntry
{
NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot);
MeshInstanceContainer meshMap;
bool enabled = false;
bool instancingEnabled = false;
};
typedef std::map<const Material*, BatchedModelEntry, BatchedModelMaterialComparator> ModelBatches;
typedef std::map<const Material*, BatchedModelEntry, MaterialComparator> MeshMaterialBatches;
struct BatchedMaterialEntry
{
std::size_t maxInstanceCount = 0;
MeshMaterialBatches materialMap;
};
typedef std::map<const MaterialPipeline*, BatchedMaterialEntry, MaterialPipelineComparator> MeshPipelineBatches;
struct TransparentModelData
{
@ -146,9 +164,9 @@ namespace Nz
struct Layer
{
BatchedBillboardContainer billboards;
BasicSpriteBatches basicSprites;
ModelBatches opaqueModels;
BillboardPipelineBatches billboards;
SpritePipelineBatches basicSprites;
MeshPipelineBatches opaqueModels;
TransparentModelContainer transparentModels;
std::vector<TransparentModelData> transparentModelData;
std::vector<const Drawable*> otherDrawables;

View File

@ -84,6 +84,7 @@ namespace Nz
mutable std::vector<LightIndex> m_lights;
Buffer m_vertexBuffer;
mutable ForwardRenderQueue m_renderQueue;
Texture m_whiteTexture;
VertexBuffer m_billboardPointBuffer;
VertexBuffer m_spriteBuffer;
unsigned int m_maxLightPassPerObject;

View File

@ -20,7 +20,7 @@
#include <Nazara/Core/String.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Graphics/MaterialPipeline.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Renderer/TextureSampler.hpp>
#include <Nazara/Renderer/UberShader.hpp>
@ -58,20 +58,33 @@ namespace Nz
public:
inline Material();
inline Material(const MaterialPipeline* pipeline);
inline Material(const MaterialPipelineInfo& pipelineInfo);
inline Material(const String& pipelineName);
inline Material(const Material& material);
inline ~Material();
const Shader* Apply(UInt32 shaderFlags = 0, UInt8 textureUnit = 0, UInt8* lastUsedUnit = nullptr) const;
void Apply(const MaterialPipeline::Instance& instance, UInt8 textureUnit = 0, UInt8* lastUsedUnit = nullptr) const;
void BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams = MaterialParams());
inline void Enable(RendererParameter renderParameter, bool enable);
inline void Configure(const MaterialPipeline* pipeline);
inline void Configure(const MaterialPipelineInfo& pipelineInfo);
inline bool Configure(const String& pipelineName);
inline void EnableAlphaTest(bool alphaTest);
inline void EnableBlending(bool blending);
inline void EnableColorWrite(bool colorWrite);
inline void EnableDepthBuffer(bool depthBuffer);
inline void EnableDepthSorting(bool depthSorting);
inline void EnableLighting(bool lighting);
inline void EnableDepthWrite(bool depthWrite);
inline void EnableFaceCulling(bool faceCulling);
inline void EnableScissorTest(bool scissorTest);
inline void EnableShadowCasting(bool castShadows);
inline void EnableShadowReceive(bool receiveShadows);
inline void EnableTransform(bool transform);
inline void EnableStencilTest(bool stencilTest);
inline void EnsurePipelineUpdate() const;
inline const TextureRef& GetAlphaMap() const;
inline float GetAlphaThreshold() const;
@ -87,10 +100,12 @@ namespace Nz
inline FaceSide GetFaceCulling() const;
inline FaceFilling GetFaceFilling() const;
inline const TextureRef& GetHeightMap() const;
inline float GetLineWidth() const;
inline const TextureRef& GetNormalMap() const;
inline const RenderStates& GetRenderStates() const;
inline const MaterialPipeline* GetPipeline() const;
inline const MaterialPipelineInfo& GetPipelineInfo() const;
inline float GetPointSize() const;
inline const UberShader* GetShader() const;
inline const UberShaderInstance* GetShaderInstance(UInt32 flags = ShaderFlags_None) const;
inline float GetShininess() const;
inline Color GetSpecularColor() const;
inline const TextureRef& GetSpecularMap() const;
@ -107,12 +122,16 @@ namespace Nz
inline bool HasSpecularMap() const;
inline bool IsAlphaTestEnabled() const;
inline bool IsBlendingEnabled() const;
inline bool IsColorWriteEnabled() const;
inline bool IsDepthBufferEnabled() const;
inline bool IsDepthSortingEnabled() const;
inline bool IsEnabled(RendererParameter renderParameter) const;
inline bool IsLightingEnabled() const;
inline bool IsDepthWriteEnabled() const;
inline bool IsFaceCullingEnabled() const;
inline bool IsScissorTestEnabled() const;
inline bool IsStencilTestEnabled() const;
inline bool IsShadowCastingEnabled() const;
inline bool IsShadowReceiveEnabled() const;
inline bool IsTransformEnabled() const;
inline bool LoadFromFile(const String& filePath, const MaterialParams& params = MaterialParams());
inline bool LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params = MaterialParams());
@ -139,9 +158,10 @@ namespace Nz
inline void SetFaceFilling(FaceFilling filling);
inline bool SetHeightMap(const String& textureName);
inline void SetHeightMap(TextureRef textureName);
inline void SetLineWidth(float lineWidth);
inline bool SetNormalMap(const String& textureName);
inline void SetNormalMap(TextureRef textureName);
inline void SetRenderStates(const RenderStates& states);
inline void SetPointSize(float pointSize);
inline void SetShader(UberShaderConstRef uberShader);
inline bool SetShader(const String& uberShaderName);
inline void SetShininess(float shininess);
@ -161,16 +181,9 @@ namespace Nz
NazaraSignal(OnMaterialReset, const Material* /*material*/);
private:
struct ShaderInstance
{
const Shader* shader;
UberShaderInstance* uberInstance = nullptr;
int uniforms[MaterialUniform_Max + 1];
};
void Copy(const Material& material);
void GenerateShader(UInt32 flags) const;
inline void InvalidateShaders();
inline void InvalidatePipeline();
inline void UpdatePipeline() const;
static bool Initialize();
static void Uninitialize();
@ -179,7 +192,8 @@ namespace Nz
Color m_diffuseColor;
Color m_specularColor;
MaterialRef m_depthMaterial; //< Materialception
RenderStates m_states;
mutable const MaterialPipeline* m_pipeline;
MaterialPipelineInfo m_pipelineInfo;
TextureSampler m_diffuseSampler;
TextureSampler m_specularSampler;
TextureRef m_alphaMap;
@ -188,14 +202,8 @@ namespace Nz
TextureRef m_heightMap;
TextureRef m_normalMap;
TextureRef m_specularMap;
UberShaderConstRef m_uberShader;
mutable ShaderInstance m_shaders[ShaderFlags_Max + 1];
bool m_alphaTestEnabled;
bool m_depthSortingEnabled;
bool m_lightingEnabled;
mutable bool m_pipelineUpdated;
bool m_shadowCastingEnabled;
bool m_shadowReceiveEnabled;
bool m_transformEnabled;
float m_alphaThreshold;
float m_shininess;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
// Copyright (C) 2016 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_MATERIALPIPELINE_HPP
#define NAZARA_MATERIALPIPELINE_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Renderer/RenderPipeline.hpp>
#include <Nazara/Renderer/UberShader.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <array>
namespace Nz
{
struct MaterialPipelineInfo : RenderStates
{
bool alphaTest = false;
bool depthSorting = false;
bool hasAlphaMap = false;
bool hasDiffuseMap = false;
bool hasEmissiveMap = false;
bool hasHeightMap = false;
bool hasNormalMap = false;
bool hasSpecularMap = false;
bool shadowReceive = true;
UberShaderConstRef uberShader;
};
inline bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs);
inline bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs);
class MaterialPipeline;
using MaterialPipelineConstRef = ObjectRef<const MaterialPipeline>;
using MaterialPipelineLibrary = ObjectLibrary<MaterialPipeline>;
using MaterialPipelineRef = ObjectRef<MaterialPipeline>;
class NAZARA_GRAPHICS_API MaterialPipeline : public RefCounted
{
friend class Graphics;
friend MaterialPipelineLibrary;
public:
struct Instance;
MaterialPipeline(const MaterialPipeline&) = delete;
MaterialPipeline(MaterialPipeline&&) = delete;
~MaterialPipeline() = default;
inline const Instance& Apply(UInt32 flags = ShaderFlags_None) const;
MaterialPipeline& operator=(const MaterialPipeline&) = delete;
MaterialPipeline& operator=(MaterialPipeline&&) = delete;
inline const MaterialPipelineInfo& GetInfo() const;
inline const Instance& GetInstance(UInt32 flags = ShaderFlags_None) const;
static MaterialPipelineRef GetPipeline(const MaterialPipelineInfo& pipelineInfo);
struct Instance
{
RenderPipeline renderPipeline;
UberShaderInstance* uberInstance = nullptr;
std::array<int, MaterialUniform_Max + 1> uniforms;
};
private:
inline MaterialPipeline(const MaterialPipelineInfo& pipelineInfo);
void GenerateRenderPipeline(UInt32 flags) const;
static bool Initialize();
template<typename... Args> static MaterialPipelineRef New(Args&&... args);
static void Uninitialize();
MaterialPipelineInfo m_pipelineInfo;
mutable std::array<Instance, ShaderFlags_Max + 1> m_instances;
using PipelineCache = std::unordered_map<MaterialPipelineInfo, MaterialPipelineRef>;
static PipelineCache s_pipelineCache;
static MaterialPipelineLibrary::LibraryMap s_library;
};
}
#include <Nazara/Graphics/MaterialPipeline.inl>
#endif // NAZARA_MATERIALPIPELINE_HPP

View File

@ -0,0 +1,143 @@
// Copyright (C) 2016 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/MaterialPipeline.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <functional>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline MaterialPipeline::MaterialPipeline(const MaterialPipelineInfo& pipelineInfo) :
m_pipelineInfo(pipelineInfo)
{
}
/*!
* \brief Enable pipeline states for rendering
*
* \param flags Shader flags
*/
inline const MaterialPipeline::Instance& MaterialPipeline::Apply(UInt32 flags) const
{
const Instance& instance = GetInstance(flags);
instance.uberInstance->Activate();
Renderer::SetRenderStates(m_pipelineInfo);
return instance;
}
/*!
* \brief Retrieve a MaterialPipelineInfo object describing this pipeline
*
* \return Pipeline informations
*/
const MaterialPipelineInfo& MaterialPipeline::GetInfo() const
{
return m_pipelineInfo;
}
/*!
* \brief Retrieve (and generate if required) a pipeline instance using shader flags without applying it
*
* \param flags Shader flags
*
* \return Pipeline instance
*/
inline const MaterialPipeline::Instance& MaterialPipeline::GetInstance(UInt32 flags) const
{
const Instance& instance = m_instances[flags];
if (!instance.uberInstance)
GenerateRenderPipeline(flags);
return instance;
}
bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs)
{
if (!operator==(static_cast<const RenderStates&>(lhs), static_cast<const RenderStates&>(rhs)))
return false;
#define NazaraPipelineMember(field) if (lhs.##field != rhs.##field) return false
#define NazaraPipelineBoolMember NazaraPipelineMember
NazaraPipelineBoolMember(alphaTest);
NazaraPipelineBoolMember(depthSorting);
NazaraPipelineBoolMember(hasAlphaMap);
NazaraPipelineBoolMember(hasDiffuseMap);
NazaraPipelineBoolMember(hasEmissiveMap);
NazaraPipelineBoolMember(hasHeightMap);
NazaraPipelineBoolMember(hasNormalMap);
NazaraPipelineBoolMember(hasSpecularMap);
NazaraPipelineBoolMember(shadowReceive);
NazaraPipelineMember(uberShader);
#undef NazaraPipelineMember
#undef NazaraPipelineBoolMember
return true;
}
bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs)
{
return !operator==(lhs, rhs);
}
/*!
* \brief Creates a new MaterialPipeline from the arguments
* \return A reference to the newly created material pipeline
*
* \param args Arguments for the material pipeline
*/
template<typename... Args>
MaterialPipelineRef MaterialPipeline::New(Args&&... args)
{
std::unique_ptr<MaterialPipeline> object(new MaterialPipeline(std::forward<Args>(args)...));
return object.release();
}
}
namespace std
{
template<>
struct hash<Nz::MaterialPipelineInfo>
{
size_t operator()(const Nz::MaterialPipelineInfo& pipelineInfo) const
{
hash<Nz::RenderStates> parentHash;
std::size_t seed = parentHash(pipelineInfo);
Nz::UInt16 parameterHash = 0;
Nz::UInt16 parameterIndex = 0;
#define NazaraPipelineMember(member) Nz::HashCombine(seed, pipelineInfo.##member)
#define NazaraPipelineBoolMember(member) parameterHash |= ((pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++)
NazaraPipelineBoolMember(alphaTest);
NazaraPipelineBoolMember(depthSorting);
NazaraPipelineBoolMember(hasAlphaMap);
NazaraPipelineBoolMember(hasDiffuseMap);
NazaraPipelineBoolMember(hasEmissiveMap);
NazaraPipelineBoolMember(hasHeightMap);
NazaraPipelineBoolMember(hasNormalMap);
NazaraPipelineBoolMember(hasSpecularMap);
NazaraPipelineBoolMember(shadowReceive);
NazaraPipelineMember(uberShader);
#undef NazaraPipelineMember
#undef NazaraPipelineBoolMember
Nz::HashCombine(seed, parameterHash);
return seed;
}
};
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -123,8 +123,7 @@ namespace Nz
inline void Sprite::SetDefaultMaterial()
{
MaterialRef material = Material::New();
material->Enable(RendererParameter_FaceCulling, false);
material->EnableLighting(false);
material->EnableFaceCulling(false);
SetMaterial(std::move(material));
}

View File

@ -119,10 +119,9 @@ namespace Nz
inline void TextSprite::SetDefaultMaterial()
{
MaterialRef material = Material::New();
material->Enable(RendererParameter_Blend, true);
material->Enable(RendererParameter_DepthWrite, false);
material->Enable(RendererParameter_FaceCulling, false);
material->EnableLighting(false);
material->EnableBlending(true);
material->EnableDepthWrite(false);
material->EnableFaceCulling(false);
material->SetDstBlend(BlendFunc_InvSrcAlpha);
material->SetSrcBlend(BlendFunc_SrcAlpha);

View File

@ -0,0 +1,42 @@
// 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_RENDERPIPELINE_HPP
#define NAZARA_RENDERPIPELINE_HPP
#include <Nazara/Utility/Enums.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Renderer/Shader.hpp>
namespace Nz
{
struct RenderPipelineInfo : RenderStates
{
ShaderConstRef shader;
};
class RenderPipeline
{
public:
inline RenderPipeline();
inline ~RenderPipeline();
inline bool Create(const RenderPipelineInfo& pipelineInfo);
inline void Destroy();
inline const RenderPipelineInfo& GetInfo() const;
inline bool IsValid() const;
private:
RenderPipelineInfo m_pipelineInfo;
bool m_valid;
};
}
#include <Nazara/Renderer/RenderPipeline.inl>
#endif // NAZARA_RENDERPIPELINE_HPP

View File

@ -0,0 +1,48 @@
// 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 <Nazara/Renderer/RenderPipeline.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
inline RenderPipeline::RenderPipeline() :
m_valid(false)
{
}
inline RenderPipeline::~RenderPipeline()
{
}
inline bool RenderPipeline::Create(const RenderPipelineInfo& pipelineInfo)
{
NazaraAssert(pipelineInfo.shader, "Invalid shader");
m_pipelineInfo = pipelineInfo;
m_valid = true;
return true;
}
inline void RenderPipeline::Destroy()
{
m_valid = false;
}
inline const RenderPipelineInfo& RenderPipeline::GetInfo() const
{
NazaraAssert(m_valid, "Invalid pipeline info");
return m_pipelineInfo;
}
inline bool RenderPipeline::IsValid() const
{
return m_valid;
}
}
#include <Nazara/Renderer/DebugOff.hpp>

View File

@ -7,8 +7,8 @@
#ifndef NAZARA_RENDERSTATES_HPP
#define NAZARA_RENDERSTATES_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <Nazara/Renderer/Shader.hpp>
namespace Nz
{
@ -74,10 +74,7 @@ namespace Nz
float pointSize = 1.f;
};
struct RenderPipeline : RenderStates
{
ShaderConstRef shader;
};
inline bool operator==(const RenderStates& lhs, const RenderStates& rhs);
}
#include <Nazara/Renderer/RenderStates.inl>

View File

@ -3,11 +3,143 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Renderer/RenderStates.hpp>
#include <cstring>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <functional>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
bool operator==(const RenderStates& lhs, const RenderStates& rhs)
{
#define NazaraRenderStateMember(field) if (lhs.##field != rhs.##field) return false
#define NazaraRenderStateBoolMember NazaraRenderStateMember
#define NazaraRenderStateFloatMember(field, maxDiff) if (!NumberEquals(lhs.##field, rhs.##field, maxDiff)) return false
NazaraRenderStateBoolMember(blending);
NazaraRenderStateBoolMember(colorWrite);
NazaraRenderStateBoolMember(depthBuffer);
NazaraRenderStateBoolMember(faceCulling);
NazaraRenderStateBoolMember(scissorTest);
NazaraRenderStateBoolMember(stencilTest);
if (lhs.depthBuffer)
NazaraRenderStateBoolMember(depthWrite);
NazaraRenderStateMember(faceFilling);
if (lhs.blending) //< Remember, at this time we know lhs.blending == rhs.blending
{
NazaraRenderStateMember(dstBlend);
NazaraRenderStateMember(srcBlend);
}
if (lhs.depthBuffer)
NazaraRenderStateMember(depthFunc);
if (lhs.faceCulling)
NazaraRenderStateMember(cullingSide);
if (lhs.stencilTest)
{
NazaraRenderStateMember(stencilCompare.back);
NazaraRenderStateMember(stencilCompare.front);
NazaraRenderStateMember(stencilCompareMask.back);
NazaraRenderStateMember(stencilCompareMask.front);
NazaraRenderStateMember(stencilDepthFail.back);
NazaraRenderStateMember(stencilDepthFail.front);
NazaraRenderStateMember(stencilFail.back);
NazaraRenderStateMember(stencilFail.front);
NazaraRenderStateMember(stencilPass.back);
NazaraRenderStateMember(stencilPass.front);
NazaraRenderStateMember(stencilReference.back);
NazaraRenderStateMember(stencilReference.front);
NazaraRenderStateMember(stencilWriteMask.back);
NazaraRenderStateMember(stencilWriteMask.front);
}
NazaraRenderStateFloatMember(lineWidth, 0.001f);
NazaraRenderStateFloatMember(pointSize, 0.001f);
#undef NazaraRenderStateMember
#undef NazaraRenderStateBoolMember
#undef NazaraRenderStateFloatMember
return true;
}
}
namespace std
{
template<>
struct hash<Nz::RenderStates>
{
size_t operator()(const Nz::RenderStates& pipelineInfo) const
{
std::size_t seed = 0;
Nz::UInt8 parameterHash = 0;
Nz::UInt8 parameterIndex = 0;
#define NazaraRenderStateMember(member) Nz::HashCombine(seed, pipelineInfo.##member)
#define NazaraRenderStateBoolMember(member) parameterHash |= ((pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++)
#define NazaraRenderStateBoolMemberDep(dependency, member) parameterHash |= ((pipelineInfo.##dependency && pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++)
#define NazaraRenderStateFloatMember(member, maxDiff) Nz::HashCombine(seed, std::floor(pipelineInfo.##member / maxDiff) * maxDiff)
NazaraRenderStateBoolMember(blending);
NazaraRenderStateBoolMember(colorWrite);
NazaraRenderStateBoolMember(depthBuffer);
NazaraRenderStateBoolMember(faceCulling);
NazaraRenderStateBoolMember(scissorTest);
NazaraRenderStateBoolMember(stencilTest);
NazaraRenderStateBoolMemberDep(depthBuffer, depthWrite);
NazaraRenderStateMember(faceFilling);
if (pipelineInfo.blending) //< Remember, at this time we know lhs.blending == rhs.blending
{
NazaraRenderStateMember(dstBlend);
NazaraRenderStateMember(srcBlend);
}
if (pipelineInfo.depthBuffer)
NazaraRenderStateMember(depthFunc);
if (pipelineInfo.faceCulling)
NazaraRenderStateMember(cullingSide);
if (pipelineInfo.stencilTest)
{
NazaraRenderStateMember(stencilCompare.back);
NazaraRenderStateMember(stencilCompare.front);
NazaraRenderStateMember(stencilCompareMask.back);
NazaraRenderStateMember(stencilCompareMask.front);
NazaraRenderStateMember(stencilDepthFail.back);
NazaraRenderStateMember(stencilDepthFail.front);
NazaraRenderStateMember(stencilFail.back);
NazaraRenderStateMember(stencilFail.front);
NazaraRenderStateMember(stencilPass.back);
NazaraRenderStateMember(stencilPass.front);
NazaraRenderStateMember(stencilReference.back);
NazaraRenderStateMember(stencilReference.front);
NazaraRenderStateMember(stencilWriteMask.back);
NazaraRenderStateMember(stencilWriteMask.front);
}
NazaraRenderStateFloatMember(lineWidth, 0.001f);
NazaraRenderStateFloatMember(pointSize, 0.001f);
#undef NazaraRenderStateMember
#undef NazaraRenderStateBoolMember
#undef NazaraRenderStateBoolMemberDep
#undef NazaraRenderStateFloatMember
Nz::HashCombine(seed, parameterHash);
return seed;
}
};
}
#include <Nazara/Renderer/DebugOff.hpp>

View File

@ -73,13 +73,43 @@ namespace Nz
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
for (auto& pair : m_renderQueue->layers)
for (auto& layerPair : m_renderQueue->layers)
{
DeferredRenderQueue::Layer& layer = pair.second;
for (auto& pipelinePair : layerPair.second.opaqueModels)
{
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
for (auto& matIt : layer.opaqueModels)
if (pipelineEntry.maxInstanceCount > 0)
{
auto& matEntry = matIt.second;
bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT);
UInt32 flags = ShaderFlags_Deferred;
if (instancing)
flags |= ShaderFlags_Instancing;
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(flags);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
for (auto& materialPair : pipelineEntry.materialMap)
{
const Material* material = materialPair.first;
auto& matEntry = materialPair.second;
if (matEntry.enabled)
{
@ -87,30 +117,7 @@ namespace Nz
if (!meshInstances.empty())
{
const Material* material = matIt.first;
bool useInstancing = instancingEnabled && matEntry.instancingEnabled;
// We begin by getting the program for materials
UInt32 flags = ShaderFlags_Deferred;
if (useInstancing)
flags |= ShaderFlags_Instancing;
const Shader* shader = material->Apply(flags);
// The uniforms are conserved in our program, there's no point to send them back if they don't change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambient color for the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
material->Apply(pipelineInstance);
// Meshes
for (auto& meshIt : meshInstances)
@ -145,7 +152,7 @@ namespace Nz
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(vertexBuffer);
if (useInstancing)
if (instancing)
{
// We get the buffer for instance of Renderer and we configure it to work with matrices
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
@ -186,9 +193,12 @@ namespace Nz
}
}
// Abd we set it back data to zero
// And we set it back data to zero
matEntry.enabled = false;
matEntry.instancingEnabled = false;
}
}
pipelineEntry.maxInstanceCount = 0;
}
}
}

View File

@ -191,27 +191,39 @@ namespace Nz
void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix)
{
if (material->IsEnabled(RendererParameter_Blend))
if (material->IsBlendingEnabled())
// One transparent material ? I don't like it, go see if I'm in the forward queue
m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix);
else
{
Layer& currentLayer = GetLayer(renderOrder);
auto& opaqueModels = currentLayer.opaqueModels;
MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels;
auto it = opaqueModels.find(material);
if (it == opaqueModels.end())
const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = opaqueModels.find(materialPipeline);
if (pipelineIt == opaqueModels.end())
{
BatchedMaterialEntry materialEntry;
pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first;
}
BatchedMaterialEntry& materialEntry = pipelineIt->second;
MeshMaterialBatches& materialMap = materialEntry.materialMap;
auto materialIt = materialMap.find(material);
if (materialIt == materialMap.end())
{
BatchedModelEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &DeferredRenderQueue::OnMaterialInvalidation);
it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first;
materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first;
}
BatchedModelEntry& entry = it->second;
BatchedModelEntry& entry = materialIt->second;
entry.enabled = true;
auto& meshMap = entry.meshMap;
MeshInstanceContainer& meshMap = entry.meshMap;
auto it2 = meshMap.find(meshData);
if (it2 == meshMap.end())
@ -225,13 +237,8 @@ namespace Nz
it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first;
}
// We add matrices to the list of instances of this object
std::vector<Matrix4f>& instances = it2->second.instances;
instances.push_back(transformMatrix);
// Do we have enough instances to perform instancing ?
if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT)
entry.instancingEnabled = true; // Thus we can activate it
}
}
@ -306,9 +313,11 @@ namespace Nz
{
Layer& layer = pair.second;
for (auto& modelPair : layer.opaqueModels)
for (auto& pipelineEntry : layer.opaqueModels)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto& materialEntry : pipelineEntry.second.materialMap)
{
MeshInstanceContainer& meshes = materialEntry.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
@ -320,6 +329,7 @@ namespace Nz
}
}
}
}
/*!
* \brief Handle the invalidation of a material
@ -333,7 +343,8 @@ namespace Nz
{
Layer& layer = pair.second;
layer.opaqueModels.erase(material);
for (auto& pipelineEntry : layer.opaqueModels)
pipelineEntry.second.materialMap.erase(material);
}
}
@ -348,10 +359,11 @@ namespace Nz
for (auto& pair : layers)
{
Layer& layer = pair.second;
for (auto& modelPair : layer.opaqueModels)
for (auto& pipelineEntry : layer.opaqueModels)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto& materialEntry : pipelineEntry.second.materialMap)
{
MeshInstanceContainer& meshes = materialEntry.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
@ -363,58 +375,5 @@ namespace Nz
}
}
}
/*!
* \brief Functor to compare two batched model with material
* \return true If first material is "smaller" than the second one
*
* \param mat1 First material to compare
* \param mat2 Second material to compare
*/
bool DeferredRenderQueue::BatchedModelMaterialComparator::operator()(const Material* mat1, const Material* mat2) const
{
const UberShader* uberShader1 = mat1->GetShader();
const UberShader* uberShader2 = mat2->GetShader();
if (uberShader1 != uberShader2)
return uberShader1 < uberShader2;
const Shader* shader1 = mat1->GetShaderInstance(ShaderFlags_Deferred)->GetShader();
const Shader* shader2 = mat2->GetShaderInstance(ShaderFlags_Deferred)->GetShader();
if (shader1 != shader2)
return shader1 < shader2;
const Texture* diffuseMap1 = mat1->GetDiffuseMap();
const Texture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
return diffuseMap1 < diffuseMap2;
return mat1 < mat2;
}
/*!
* \brief Functor to compare two mesh data
* \return true If first mesh is "smaller" than the second one
*
* \param data1 First mesh to compare
* \param data2 Second mesh to compare
*/
bool DeferredRenderQueue::MeshDataComparator::operator()(const MeshData& data1, const MeshData& data2) const
{
const Buffer* buffer1;
const Buffer* buffer2;
buffer1 = (data1.indexBuffer) ? data1.indexBuffer->GetBuffer() : nullptr;
buffer2 = (data2.indexBuffer) ? data2.indexBuffer->GetBuffer() : nullptr;
if (buffer1 != buffer2)
return buffer1 < buffer2;
buffer1 = data1.vertexBuffer->GetBuffer();
buffer2 = data2.vertexBuffer->GetBuffer();
if (buffer1 != buffer2)
return buffer1 < buffer2;
return data1.primitiveMode < data2.primitiveMode;
}
}

View File

@ -23,8 +23,8 @@ namespace Nz
{
// Material
m_baseMaterial = Material::New();
m_baseMaterial->Enable(RendererParameter_ColorWrite, false);
m_baseMaterial->Enable(RendererParameter_FaceCulling, false);
m_baseMaterial->EnableColorWrite(false);
m_baseMaterial->EnableFaceCulling(false);
//m_baseMaterial->SetFaceCulling(FaceSide_Front);
}

View File

@ -52,6 +52,10 @@ namespace Nz
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
std::array<UInt8, 4> whitePixel = {255, 255, 255, 255};
m_whiteTexture.Create(ImageType_2D, PixelFormatType_RGBA8, 1, 1);
m_whiteTexture.Update(whitePixel.data());
m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic);
m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer);
@ -208,6 +212,8 @@ namespace Nz
void DepthRenderTechnique::DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
@ -215,13 +221,46 @@ namespace Nz
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
for (auto& matIt : layer.basicSprites)
for (auto& pipelinePair : layer.basicSprites)
{
const Material* material = matIt.first;
auto& matEntry = matIt.second;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
if (pipelineEntry.enabled)
{
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
for (auto& materialPair : pipelineEntry.materialMap)
{
const Material* material = materialPair.first;
auto& matEntry = materialPair.second;
if (matEntry.enabled)
{
UInt8 overlayUnit;
material->Apply(pipelineInstance, 0, &overlayUnit);
overlayUnit++;
shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
auto& overlayMap = matEntry.overlayMap;
for (auto& overlayIt : overlayMap)
{
@ -231,34 +270,7 @@ namespace Nz
unsigned int spriteChainCount = spriteChainVector.size();
if (spriteChainCount > 0)
{
// We begin to apply the material (and get the shader activated doing so)
UInt32 flags = 0;
if (overlay)
flags |= ShaderFlags_TextureOverlay;
UInt8 overlayUnit;
const Shader* shader = material->Apply(flags, 0, &overlayUnit);
if (overlay)
{
overlayUnit++;
Renderer::SetTexture(overlayUnit, overlay);
Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
}
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Overlay
shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, Renderer::GetMatrix(MatrixType_ViewProj).GetTranslation());
lastShader = shader;
}
Renderer::SetTexture(overlayUnit, (overlay) ? overlay : &m_whiteTexture);
unsigned int spriteChain = 0; // Which chain of sprites are we treating
unsigned int spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain
@ -267,18 +279,18 @@ namespace Nz
{
// We open the buffer in writing mode
BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
unsigned int spriteCount = 0;
unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount()/4);
unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
do
{
ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain];
unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset);
std::memcpy(vertices, currentChain.vertices + spriteChainOffset*4, 4*count*sizeof(VertexStruct_XYZ_Color_UV));
vertices += count*4;
std::memcpy(vertices, currentChain.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV));
vertices += count * 4;
spriteCount += count;
spriteChainOffset += count;
@ -289,23 +301,24 @@ namespace Nz
spriteChain++;
spriteChainOffset = 0;
}
}
while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
} while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
vertexMapper.Unmap();
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount*6);
}
while (spriteChain < spriteChainCount);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6);
} while (spriteChain < spriteChainCount);
spriteChainVector.clear();
}
}
// On remet à zéro
// We set it back to zero
matEntry.enabled = false;
}
}
pipelineEntry.enabled = false;
}
}
}
/*!
@ -317,6 +330,8 @@ namespace Nz
void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
@ -327,7 +342,32 @@ namespace Nz
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
for (auto& matIt : layer.billboards)
for (auto& pipelinePair : layer.billboards)
{
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
if (pipelineEntry.enabled)
{
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
for (auto& matIt : pipelinePair.second.materialMap)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
@ -337,19 +377,7 @@ namespace Nz
if (billboardCount > 0)
{
// We begin to apply the material (and get the shader activated doing so)
const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, Renderer::GetMatrix(MatrixType_ViewProj).GetTranslation());
lastShader = shader;
}
material->Apply(pipelineInstance);
const ForwardRenderQueue::BillboardData* data = &billboardVector[0];
unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount();
@ -369,22 +397,23 @@ namespace Nz
}
}
}
}
}
else
{
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetVertexBuffer(&m_billboardPointBuffer);
for (auto& matIt : layer.billboards)
for (auto& pipelinePair : layer.billboards)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
unsigned int billboardCount = billboardVector.size();
if (billboardCount > 0)
if (pipelineEntry.enabled)
{
// We begin to apply the material (and get the shader activated doing so)
const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor);
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
@ -392,22 +421,31 @@ namespace Nz
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, Renderer::GetMatrix(MatrixType_ViewProj).GetTranslation());
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
const ForwardRenderQueue::BillboardData* data = &billboardVector[0];
unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4);
for (auto& matIt : pipelinePair.second.materialMap)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
const ForwardRenderQueue::BillboardData* data = &billboardVector[0];
unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4);
unsigned int billboardCount = billboardVector.size();
do
{
unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw);
billboardCount -= renderedBillboardCount;
BufferMapper<VertexBuffer> vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4);
BillboardPoint* vertices = reinterpret_cast<BillboardPoint*>(vertexMapper.GetPointer());
BufferMapper<VertexBuffer> vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount * 4);
BillboardPoint* vertices = static_cast<BillboardPoint*>(vertexMapper.GetPointer());
for (unsigned int i = 0; i < renderedBillboardCount; ++i)
{
@ -444,7 +482,7 @@ namespace Nz
vertexMapper.Unmap();
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount*6);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6);
}
while (billboardCount > 0);
@ -453,6 +491,7 @@ namespace Nz
}
}
}
}
/*!
* \brief Draws opaques models
@ -463,35 +502,49 @@ namespace Nz
void DepthRenderTechnique::DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
for (auto& matIt : layer.opaqueModels)
for (auto& pipelinePair : layer.opaqueModels)
{
auto& matEntry = matIt.second;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
if (matEntry.enabled)
if (pipelineEntry.maxInstanceCount > 0)
{
ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT);
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0);
if (!meshInstances.empty())
{
const Material* material = matIt.first;
bool instancing = m_instancingEnabled && matEntry.instancingEnabled;
// We begin to apply the material (and get the shader activated doing so)
UInt8 freeTextureUnit;
const Shader* shader = material->Apply((instancing) ? ShaderFlags_Instancing : 0, 0, &freeTextureUnit);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
for (auto& materialPair : pipelineEntry.materialMap)
{
const Material* material = materialPair.first;
auto& matEntry = materialPair.second;
if (matEntry.enabled)
{
UInt8 freeTextureUnit;
material->Apply(pipelineInstance, 0, &freeTextureUnit);
ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
// Meshes
for (auto& meshIt : meshInstances)
{
@ -535,7 +588,7 @@ namespace Nz
const Matrix4f* instanceMatrices = &instances[0];
unsigned int instanceCount = instances.size();
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // The maximum number of instances in one batch
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Maximum number of instance in one batch
while (instanceCount > 0)
{
@ -565,11 +618,12 @@ namespace Nz
instances.clear();
}
}
matEntry.enabled = false;
}
}
// And we set the data back to zero
matEntry.enabled = false;
matEntry.instancingEnabled = false;
pipelineEntry.maxInstanceCount = 0;
}
}
}

View File

@ -372,12 +372,11 @@ namespace Nz
*
* \remark Produces a NazaraAssert if material is invalid
*/
void ForwardRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix)
{
NazaraAssert(material, "Invalid material");
if (material->IsEnabled(RendererParameter_Blend))
if (material->IsBlendingEnabled())
{
Layer& currentLayer = GetLayer(renderOrder);
auto& transparentModels = currentLayer.transparentModels;
@ -398,21 +397,33 @@ namespace Nz
else
{
Layer& currentLayer = GetLayer(renderOrder);
auto& opaqueModels = currentLayer.opaqueModels;
MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels;
auto it = opaqueModels.find(material);
if (it == opaqueModels.end())
const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = opaqueModels.find(materialPipeline);
if (pipelineIt == opaqueModels.end())
{
BatchedMaterialEntry materialEntry;
pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first;
}
BatchedMaterialEntry& materialEntry = pipelineIt->second;
MeshMaterialBatches& materialMap = materialEntry.materialMap;
auto materialIt = materialMap.find(material);
if (materialIt == materialMap.end())
{
BatchedModelEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first;
materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first;
}
BatchedModelEntry& entry = it->second;
BatchedModelEntry& entry = materialIt->second;
entry.enabled = true;
auto& meshMap = entry.meshMap;
MeshInstanceContainer& meshMap = entry.meshMap;
auto it2 = meshMap.find(meshData);
if (it2 == meshMap.end())
@ -431,9 +442,7 @@ namespace Nz
std::vector<Matrix4f>& instances = it2->second.instances;
instances.push_back(transformMatrix);
// Do we have enough instances to perform instancing ?
if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT)
entry.instancingEnabled = true; // Thus we can activate it
materialEntry.maxInstanceCount = std::max(materialEntry.maxInstanceCount, instances.size());
}
}
@ -448,21 +457,34 @@ namespace Nz
*
* \remark Produces a NazaraAssert if material is invalid
*/
void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay)
{
NazaraAssert(material, "Invalid material");
Layer& currentLayer = GetLayer(renderOrder);
auto& basicSprites = currentLayer.basicSprites;
SpritePipelineBatches& basicSprites = currentLayer.basicSprites;
auto matIt = basicSprites.find(material);
if (matIt == basicSprites.end())
const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = basicSprites.find(materialPipeline);
if (pipelineIt == basicSprites.end())
{
BatchedSpritePipelineEntry materialEntry;
pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first;
}
BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second;
pipelineEntry.enabled = true;
SpriteMaterialBatches& materialMap = pipelineEntry.materialMap;
auto matIt = materialMap.find(material);
if (matIt == materialMap.end())
{
BatchedBasicSpriteEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
matIt = basicSprites.insert(std::make_pair(material, std::move(entry))).first;
matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first;
}
BatchedBasicSpriteEntry& entry = matIt->second;
@ -541,13 +563,15 @@ namespace Nz
return nearPlane.Distance(position1) > nearPlane.Distance(position2);
});
for (auto& pair : layer.billboards)
for (auto& pipelinePair : layer.billboards)
{
const Material* mat = pair.first;
for (auto& matPair : pipelinePair.second.materialMap)
{
const Material* mat = matPair.first;
if (mat->IsDepthSortingEnabled())
{
BatchedBillboardEntry& entry = pair.second;
BatchedBillboardEntry& entry = matPair.second;
auto& billboardVector = entry.billboards;
std::sort(billboardVector.begin(), billboardVector.end(), [&viewerPos] (const BillboardData& data1, const BillboardData& data2)
@ -558,6 +582,7 @@ namespace Nz
}
}
}
}
/*!
* \brief Gets the billboard data
@ -571,13 +596,26 @@ namespace Nz
{
auto& billboards = GetLayer(renderOrder).billboards;
auto it = billboards.find(material);
if (it == billboards.end())
const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = billboards.find(materialPipeline);
if (pipelineIt == billboards.end())
{
BatchedBillboardPipelineEntry pipelineEntry;
pipelineIt = billboards.insert(BillboardPipelineBatches::value_type(materialPipeline, std::move(pipelineEntry))).first;
}
BatchedBillboardPipelineEntry& pipelineEntry = pipelineIt->second;
pipelineEntry.enabled = true;
BatchedBillboardContainer& materialMap = pipelineEntry.materialMap;
auto it = materialMap.find(material);
if (it == materialMap.end())
{
BatchedBillboardEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
it = billboards.insert(std::make_pair(material, std::move(entry))).first;
it = materialMap.insert(BatchedBillboardContainer::value_type(material, std::move(entry))).first;
}
BatchedBillboardEntry& entry = it->second;
@ -620,9 +658,11 @@ namespace Nz
{
Layer& layer = pair.second;
for (auto& modelPair : layer.opaqueModels)
for (auto& pipelineEntry : layer.opaqueModels)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto& materialEntry : pipelineEntry.second.materialMap)
{
MeshInstanceContainer& meshes = materialEntry.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
@ -634,6 +674,7 @@ namespace Nz
}
}
}
}
/*!
* \brief Handle the invalidation of a material
@ -647,9 +688,14 @@ namespace Nz
{
Layer& layer = pair.second;
layer.basicSprites.erase(material);
layer.billboards.erase(material);
layer.opaqueModels.erase(material);
for (auto& pipelineEntry : layer.basicSprites)
pipelineEntry.second.materialMap.erase(material);
for (auto& pipelineEntry : layer.billboards)
pipelineEntry.second.materialMap.erase(material);
for (auto& pipelineEntry : layer.opaqueModels)
pipelineEntry.second.materialMap.erase(material);
}
}
@ -664,10 +710,10 @@ namespace Nz
for (auto& pair : layers)
{
Layer& layer = pair.second;
for (auto matIt = layer.basicSprites.begin(); matIt != layer.basicSprites.end(); ++matIt)
for (auto& pipelineEntry : layer.basicSprites)
{
auto& overlayMap = matIt->second.overlayMap;
overlayMap.erase(texture);
for (auto& materialEntry : pipelineEntry.second.materialMap)
materialEntry.second.overlayMap.erase(texture);
}
}
}
@ -683,9 +729,11 @@ namespace Nz
for (auto& pair : layers)
{
Layer& layer = pair.second;
for (auto& modelPair : layer.opaqueModels)
for (auto& pipelineEntry : layer.opaqueModels)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto& materialEntry : pipelineEntry.second.materialMap)
{
MeshInstanceContainer& meshes = materialEntry.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
@ -697,27 +745,10 @@ namespace Nz
}
}
}
}
/*!
* \brief Functor to compare two batched billboard with material
* \return true If first material is "smaller" than the second one
*
* \param mat1 First material to compare
* \param mat2 Second material to compare
*/
bool ForwardRenderQueue::BatchedBillboardComparator::operator()(const Material* mat1, const Material* mat2) const
bool ForwardRenderQueue::MaterialComparator::operator()(const Material* mat1, const Material* mat2) const
{
const UberShader* uberShader1 = mat1->GetShader();
const UberShader* uberShader2 = mat2->GetShader();
if (uberShader1 != uberShader2)
return uberShader1 < uberShader2;
const Shader* shader1 = mat1->GetShaderInstance(ShaderFlags_Billboard | ShaderFlags_VertexColor)->GetShader();
const Shader* shader2 = mat2->GetShaderInstance(ShaderFlags_Billboard | ShaderFlags_VertexColor)->GetShader();
if (shader1 != shader2)
return shader1 < shader2;
const Texture* diffuseMap1 = mat1->GetDiffuseMap();
const Texture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
@ -726,60 +757,14 @@ namespace Nz
return mat1 < mat2;
}
/*!
* \brief Functor to compare two batched model with material
* \return true If first material is "smaller" than the second one
*
* \param mat1 First material to compare
* \param mat2 Second material to compare
*/
bool ForwardRenderQueue::BatchedModelMaterialComparator::operator()(const Material* mat1, const Material* mat2) const
bool ForwardRenderQueue::MaterialPipelineComparator::operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const
{
const UberShader* uberShader1 = mat1->GetShader();
const UberShader* uberShader2 = mat2->GetShader();
if (uberShader1 != uberShader2)
return uberShader1 < uberShader2;
const Shader* shader1 = mat1->GetShaderInstance()->GetShader();
const Shader* shader2 = mat2->GetShaderInstance()->GetShader();
const Shader* shader1 = pipeline1->GetInstance().renderPipeline.GetInfo().shader;
const Shader* shader2 = pipeline2->GetInstance().renderPipeline.GetInfo().shader;
if (shader1 != shader2)
return shader1 < shader2;
const Texture* diffuseMap1 = mat1->GetDiffuseMap();
const Texture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
return diffuseMap1 < diffuseMap2;
return mat1 < mat2;
}
/*!
* \brief Functor to compare two batched sprites with material
* \return true If first material is "smaller" than the second one
*
* \param mat1 First material to compare
* \param mat2 Second material to compare
*/
bool ForwardRenderQueue::BatchedSpriteMaterialComparator::operator()(const Material* mat1, const Material* mat2)
{
const UberShader* uberShader1 = mat1->GetShader();
const UberShader* uberShader2 = mat2->GetShader();
if (uberShader1 != uberShader2)
return uberShader1 < uberShader2;
const Shader* shader1 = mat1->GetShaderInstance()->GetShader();
const Shader* shader2 = mat2->GetShaderInstance()->GetShader();
if (shader1 != shader2)
return shader1 < shader2;
const Texture* diffuseMap1 = mat1->GetDiffuseMap();
const Texture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
return diffuseMap1 < diffuseMap2;
return mat1 < mat2;
return pipeline1 < pipeline2;
}
/*!

View File

@ -53,6 +53,10 @@ namespace Nz
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
std::array<UInt8, 4> whitePixel = {255, 255, 255, 255};
m_whiteTexture.Create(ImageType_2D, PixelFormatType_RGBA8, 1, 1);
m_whiteTexture.Update(whitePixel.data());
m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic);
m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer);
@ -297,36 +301,16 @@ namespace Nz
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
for (auto& matIt : layer.basicSprites)
for (auto& pipelinePair : layer.basicSprites)
{
const Material* material = matIt.first;
auto& matEntry = matIt.second;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
if (matEntry.enabled)
if (pipelineEntry.enabled)
{
auto& overlayMap = matEntry.overlayMap;
for (auto& overlayIt : overlayMap)
{
const Texture* overlay = overlayIt.first;
auto& spriteChainVector = overlayIt.second.spriteChains;
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
unsigned int spriteChainCount = spriteChainVector.size();
if (spriteChainCount > 0)
{
// We begin to apply the material (and get the shader activated doing so)
UInt32 flags = ShaderFlags_VertexColor;
if (overlay)
flags |= ShaderFlags_TextureOverlay;
UInt8 overlayUnit;
const Shader* shader = material->Apply(flags, 0, &overlayUnit);
if (overlay)
{
overlayUnit++;
Renderer::SetTexture(overlayUnit, overlay);
Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
}
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
@ -336,14 +320,38 @@ namespace Nz
// Ambiant color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Overlay
shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
lastShader = shader;
}
for (auto& materialPair : pipelineEntry.materialMap)
{
const Material* material = materialPair.first;
auto& matEntry = materialPair.second;
if (matEntry.enabled)
{
UInt8 overlayUnit;
material->Apply(pipelineInstance, 0, &overlayUnit);
overlayUnit++;
shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
auto& overlayMap = matEntry.overlayMap;
for (auto& overlayIt : overlayMap)
{
const Texture* overlay = overlayIt.first;
auto& spriteChainVector = overlayIt.second.spriteChains;
unsigned int spriteChainCount = spriteChainVector.size();
if (spriteChainCount > 0)
{
Renderer::SetTexture(overlayUnit, (overlay) ? overlay : &m_whiteTexture);
unsigned int spriteChain = 0; // Which chain of sprites are we treating
unsigned int spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain
@ -373,14 +381,12 @@ namespace Nz
spriteChain++;
spriteChainOffset = 0;
}
}
while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
} while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
vertexMapper.Unmap();
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6);
}
while (spriteChain < spriteChainCount);
} while (spriteChain < spriteChainCount);
spriteChainVector.clear();
}
@ -390,6 +396,9 @@ namespace Nz
matEntry.enabled = false;
}
}
pipelineEntry.enabled = false;
}
}
}
/*!
@ -415,17 +424,16 @@ namespace Nz
Renderer::SetVertexBuffer(&s_quadVertexBuffer);
for (auto& matIt : layer.billboards)
for (auto& pipelinePair : layer.billboards)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
unsigned int billboardCount = billboardVector.size();
if (billboardCount > 0)
if (pipelineEntry.enabled)
{
// We begin to apply the material (and get the shader activated doing so)
const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
@ -441,6 +449,18 @@ namespace Nz
lastShader = shader;
}
for (auto& matIt : pipelinePair.second.materialMap)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
unsigned int billboardCount = billboardVector.size();
if (billboardCount > 0)
{
// We begin to apply the material (and get the shader activated doing so)
material->Apply(pipelineInstance);
const ForwardRenderQueue::BillboardData* data = &billboardVector[0];
unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount();
do
@ -459,22 +479,23 @@ namespace Nz
}
}
}
}
}
else
{
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetVertexBuffer(&m_billboardPointBuffer);
for (auto& matIt : layer.billboards)
for (auto& pipelinePair : layer.billboards)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
unsigned int billboardCount = billboardVector.size();
if (billboardCount > 0)
if (pipelineEntry.enabled)
{
// We begin to apply the material (and get the shader activated doing so)
const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor);
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
@ -490,9 +511,16 @@ namespace Nz
lastShader = shader;
}
for (auto& matIt : pipelinePair.second.materialMap)
{
const Material* material = matIt.first;
auto& entry = matIt.second;
auto& billboardVector = entry.billboards;
const ForwardRenderQueue::BillboardData* data = &billboardVector[0];
unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4);
unsigned int billboardCount = billboardVector.size();
do
{
unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw);
@ -545,6 +573,7 @@ namespace Nz
}
}
}
}
/*!
* \brief Draws opaques models
@ -562,27 +591,17 @@ namespace Nz
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
for (auto& matIt : layer.opaqueModels)
for (auto& pipelinePair : layer.opaqueModels)
{
auto& matEntry = matIt.second;
const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second;
if (matEntry.enabled)
if (pipelineEntry.maxInstanceCount > 0)
{
ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT);
const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0);
if (!meshInstances.empty())
{
const Material* material = matIt.first;
// We only use instancing when no light (other than directional) is active
// This is because instancing is not compatible with the search of nearest lights
// Deferred shading does not have this problem
bool noPointSpotLight = m_renderQueue.pointLights.empty() && m_renderQueue.spotLights.empty();
bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || noPointSpotLight) && matEntry.instancingEnabled;
// We begin to apply the material (and get the shader activated doing so)
UInt8 freeTextureUnit;
const Shader* shader = material->Apply((instancing) ? ShaderFlags_Instancing : 0, 0, &freeTextureUnit);
const Shader* shader = pipelineInstance.uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
@ -598,6 +617,18 @@ namespace Nz
lastShader = shader;
}
for (auto& materialPair : pipelineEntry.materialMap)
{
const Material* material = materialPair.first;
auto& matEntry = materialPair.second;
if (matEntry.enabled)
{
UInt8 freeTextureUnit;
material->Apply(pipelineInstance, 0, &freeTextureUnit);
ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
// Meshes
for (auto& meshIt : meshInstances)
{
@ -667,9 +698,6 @@ namespace Nz
// Sends the uniforms
for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset * i, freeTextureUnit + i);
// And we give them to draw
drawFunc(meshData.primitiveMode, 0, indexCount);
}
const Matrix4f* instanceMatrices = &instances[0];
@ -691,7 +719,7 @@ namespace Nz
}
}
// We don't forget to disable the blending to avoid to interfeer with the rest of the rendering
// We don't forget to disable the blending to avoid to interferering with the rest of the rendering
Renderer::Enable(RendererParameter_Blend, false);
Renderer::SetDepthFunc(oldDepthFunc);
}
@ -753,11 +781,12 @@ namespace Nz
instances.clear();
}
}
matEntry.enabled = false;
}
}
// And we set the data back to zero
matEntry.enabled = false;
matEntry.instancingEnabled = false;
pipelineEntry.maxInstanceCount = 0;
}
}
}
@ -775,6 +804,8 @@ namespace Nz
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
const MaterialPipeline* lastPipeline = nullptr;
const MaterialPipeline::Instance* pipelineInstance = nullptr;
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
unsigned int lightCount = 0;
@ -786,9 +817,16 @@ namespace Nz
// Material
const Material* material = modelData.material;
const MaterialPipeline* pipeline = material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &pipeline->Apply();
lastPipeline = pipeline;
}
// We begin to apply the material (and get the shader activated doing so)
UInt8 freeTextureUnit;
const Shader* shader = material->Apply(0, 0, &freeTextureUnit);
const Shader* shader = material->Apply(*pipelineInstance, 0, &freeTextureUnit);
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)

View File

@ -63,12 +63,20 @@ namespace Nz
// Initialisation of the module
CallOnExit onExit(Graphics::Uninitialize);
// Materials
if (!MaterialPipeline::Initialize())
{
NazaraError("Failed to initialize material pipelines");
return false;
}
if (!Material::Initialize())
{
NazaraError("Failed to initialize materials");
return false;
}
// Renderables
if (!ParticleController::Initialize())
{
NazaraError("Failed to initialize particle controllers");
@ -121,7 +129,7 @@ namespace Nz
Loaders::RegisterMesh();
Loaders::RegisterTexture();
// RenderTechniques
// Render techniques
if (!DepthRenderTechnique::Initialize())
{
NazaraError("Failed to initialize Depth Rendering");
@ -213,19 +221,25 @@ namespace Nz
Loaders::UnregisterMesh();
Loaders::UnregisterTexture();
DeferredRenderTechnique::Uninitialize();
DepthRenderTechnique::Uninitialize();
ForwardRenderTechnique::Uninitialize();
SkinningManager::Uninitialize();
// Renderables
ParticleRenderer::Uninitialize();
ParticleGenerator::Uninitialize();
ParticleDeclaration::Uninitialize();
ParticleController::Uninitialize();
Material::Uninitialize();
SkyboxBackground::Uninitialize();
Sprite::Uninitialize();
TileMap::Uninitialize();
// Render techniques
DeferredRenderTechnique::Uninitialize();
DepthRenderTechnique::Uninitialize();
ForwardRenderTechnique::Uninitialize();
SkinningManager::Uninitialize();
// Materials
Material::Uninitialize();
MaterialPipeline::Uninitialize();
NazaraNotice("Uninitialized: Graphics module");
// Free of dependances

View File

@ -2,40 +2,15 @@
// 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 // Mandatory to include the OpenGL headers
#endif
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/UberShaderPreprocessor.hpp>
#include <cstring>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
const UInt8 r_basicFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.frag.h>
};
const UInt8 r_basicVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.vert.h>
};
const UInt8 r_phongLightingFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h>
};
const UInt8 r_phongLightingVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h>
};
}
/*!
* \ingroup graphics
* \class Nz::Material
@ -46,7 +21,6 @@ namespace Nz
* \brief Checks whether the parameters for the material are correct
* \return true If parameters are valid
*/
bool MaterialParams::IsValid() const
{
if (!UberShaderLibrary::Has(shaderName))
@ -57,41 +31,35 @@ namespace Nz
/*!
* \brief Applies shader to the material
* \return Constant pointer to the shader
*
* \param shaderFlags Flags for the shader
* \param instance Pipeline instance to update
* \param textureUnit Unit for the texture GL_TEXTURE"i"
* \param lastUsedUnit Optional argument to get the last texture unit
*/
const Shader* Material::Apply(UInt32 shaderFlags, UInt8 textureUnit, UInt8* lastUsedUnit) const
void Material::Apply(const MaterialPipeline::Instance& instance, UInt8 textureUnit, UInt8* lastUsedUnit) const
{
const ShaderInstance& instance = m_shaders[shaderFlags];
if (!instance.uberInstance)
GenerateShader(shaderFlags);
instance.uberInstance->Activate();
const Shader* shader = instance.renderPipeline.GetInfo().shader;
if (instance.uniforms[MaterialUniform_AlphaThreshold] != -1)
instance.shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold);
shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold);
if (instance.uniforms[MaterialUniform_Ambient] != -1)
instance.shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor);
shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor);
if (instance.uniforms[MaterialUniform_Diffuse] != -1)
instance.shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor);
shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor);
if (instance.uniforms[MaterialUniform_Shininess] != -1)
instance.shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess);
shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess);
if (instance.uniforms[MaterialUniform_Specular] != -1)
instance.shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor);
shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor);
if (m_alphaMap && instance.uniforms[MaterialUniform_AlphaMap] != -1)
{
Renderer::SetTexture(textureUnit, m_alphaMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_AlphaMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_AlphaMap], textureUnit);
textureUnit++;
}
@ -99,7 +67,7 @@ namespace Nz
{
Renderer::SetTexture(textureUnit, m_diffuseMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_DiffuseMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_DiffuseMap], textureUnit);
textureUnit++;
}
@ -107,7 +75,7 @@ namespace Nz
{
Renderer::SetTexture(textureUnit, m_emissiveMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_EmissiveMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_EmissiveMap], textureUnit);
textureUnit++;
}
@ -115,7 +83,7 @@ namespace Nz
{
Renderer::SetTexture(textureUnit, m_heightMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_HeightMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_HeightMap], textureUnit);
textureUnit++;
}
@ -123,7 +91,7 @@ namespace Nz
{
Renderer::SetTexture(textureUnit, m_normalMap);
Renderer::SetTextureSampler(textureUnit, m_diffuseSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_NormalMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_NormalMap], textureUnit);
textureUnit++;
}
@ -131,25 +99,20 @@ namespace Nz
{
Renderer::SetTexture(textureUnit, m_specularMap);
Renderer::SetTextureSampler(textureUnit, m_specularSampler);
instance.shader->SendInteger(instance.uniforms[MaterialUniform_SpecularMap], textureUnit);
shader->SendInteger(instance.uniforms[MaterialUniform_SpecularMap], textureUnit);
textureUnit++;
}
Renderer::SetRenderStates(m_states);
if (lastUsedUnit)
*lastUsedUnit = textureUnit;
return instance.shader;
}
/*!
* \brief Builds the material from parameters
* \brief Builds the material from a parameter list
*
* \param matData Data information for the material
* \param matParams Parameters for the material
* \param matParams Additional parameters for the material
*/
void Material::BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams)
{
Color color;
@ -160,7 +123,6 @@ namespace Nz
ErrorFlags errFlags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled, true);
if (matData.GetFloatParameter(MaterialData::AlphaThreshold, &fValue))
SetAlphaThreshold(fValue);
@ -188,14 +150,11 @@ namespace Nz
if (matData.GetIntegerParameter(MaterialData::FaceFilling, &iValue))
SetFaceFilling(static_cast<FaceFilling>(iValue));
if (matData.GetBooleanParameter(MaterialData::Lighting, &isEnabled))
EnableLighting(isEnabled);
if (matData.GetFloatParameter(MaterialData::LineWidth, &fValue))
m_states.lineWidth = fValue;
SetLineWidth(fValue);
if (matData.GetFloatParameter(MaterialData::PointSize, &fValue))
m_states.pointSize = fValue;
SetPointSize(fValue);
if (matData.GetColorParameter(MaterialData::SpecularColor, &color))
SetSpecularColor(color);
@ -206,30 +165,27 @@ namespace Nz
if (matData.GetIntegerParameter(MaterialData::SrcBlend, &iValue))
SetSrcBlend(static_cast<BlendFunc>(iValue));
if (matData.GetBooleanParameter(MaterialData::Transform, &isEnabled))
EnableTransform(isEnabled);
// RendererParameter
if (matData.GetBooleanParameter(MaterialData::Blending, &isEnabled))
Enable(RendererParameter_Blend, isEnabled);
EnableBlending(isEnabled);
if (matData.GetBooleanParameter(MaterialData::ColorWrite, &isEnabled))
Enable(RendererParameter_ColorWrite, isEnabled);
EnableColorWrite(isEnabled);
if (matData.GetBooleanParameter(MaterialData::DepthBuffer, &isEnabled))
Enable(RendererParameter_DepthBuffer, isEnabled);
EnableDepthBuffer(isEnabled);
if (matData.GetBooleanParameter(MaterialData::DepthWrite, &isEnabled))
Enable(RendererParameter_DepthWrite, isEnabled);
EnableDepthWrite(isEnabled);
if (matData.GetBooleanParameter(MaterialData::FaceCulling, &isEnabled))
Enable(RendererParameter_FaceCulling, isEnabled);
EnableFaceCulling(isEnabled);
if (matData.GetBooleanParameter(MaterialData::ScissorTest, &isEnabled))
Enable(RendererParameter_ScissorTest, isEnabled);
EnableScissorTest(isEnabled);
if (matData.GetBooleanParameter(MaterialData::StencilTest, &isEnabled))
Enable(RendererParameter_StencilTest, isEnabled);
EnableStencilTest(isEnabled);
// Samplers
if (matData.GetIntegerParameter(MaterialData::DiffuseAnisotropyLevel, &iValue))
@ -252,41 +208,43 @@ namespace Nz
// Stencil
if (matData.GetIntegerParameter(MaterialData::StencilCompare, &iValue))
m_states.stencilCompare.front = static_cast<RendererComparison>(iValue);
m_pipelineInfo.stencilCompare.front = static_cast<RendererComparison>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilFail, &iValue))
m_states.stencilFail.front = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilFail.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilPass, &iValue))
m_states.stencilPass.front = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilPass.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilZFail, &iValue))
m_states.stencilDepthFail.front = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilDepthFail.front = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilMask, &iValue))
m_states.stencilWriteMask.front = static_cast<UInt32>(iValue);
m_pipelineInfo.stencilWriteMask.front = static_cast<UInt32>(iValue);
if (matData.GetIntegerParameter(MaterialData::StencilReference, &iValue))
m_states.stencilReference.front = static_cast<unsigned int>(iValue);
m_pipelineInfo.stencilReference.front = static_cast<unsigned int>(iValue);
// Stencil (back)
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilCompare, &iValue))
m_states.stencilCompare.back = static_cast<RendererComparison>(iValue);
m_pipelineInfo.stencilCompare.back = static_cast<RendererComparison>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilFail, &iValue))
m_states.stencilFail.back = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilFail.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilPass, &iValue))
m_states.stencilPass.back = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilPass.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilZFail, &iValue))
m_states.stencilDepthFail.back = static_cast<StencilOperation>(iValue);
m_pipelineInfo.stencilDepthFail.back = static_cast<StencilOperation>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilMask, &iValue))
m_states.stencilWriteMask.back = static_cast<UInt32>(iValue);
m_pipelineInfo.stencilWriteMask.back = static_cast<UInt32>(iValue);
if (matData.GetIntegerParameter(MaterialData::BackFaceStencilReference, &iValue))
m_states.stencilReference.back = static_cast<unsigned int>(iValue);
m_pipelineInfo.stencilReference.back = static_cast<unsigned int>(iValue);
InvalidatePipeline();
// Textures
if (matParams.loadAlphaMap && matData.GetStringParameter(MaterialData::AlphaTexturePath, &path))
@ -310,6 +268,11 @@ namespace Nz
SetShader(matParams.shaderName);
}
/*!
* \brief Builds a ParameterList with material data
*
* \param matData Destination parameter list which will receive material data
*/
void Material::SaveToParameters(ParameterList* matData)
{
NazaraAssert(matData, "Invalid ParameterList");
@ -323,22 +286,20 @@ namespace Nz
matData->SetParameter(MaterialData::DiffuseColor, GetDiffuseColor());
matData->SetParameter(MaterialData::DstBlend, int(GetDstBlend()));
matData->SetParameter(MaterialData::FaceFilling, int(GetFaceFilling()));
matData->SetParameter(MaterialData::Lighting, IsLightingEnabled());
matData->SetParameter(MaterialData::LineWidth, GetRenderStates().lineWidth);
matData->SetParameter(MaterialData::PointSize, GetRenderStates().pointSize);
matData->SetParameter(MaterialData::LineWidth, GetLineWidth());
matData->SetParameter(MaterialData::PointSize, GetPointSize());
matData->SetParameter(MaterialData::Shininess, GetShininess());
matData->SetParameter(MaterialData::SpecularColor, GetSpecularColor());
matData->SetParameter(MaterialData::SrcBlend, int(GetSrcBlend()));
matData->SetParameter(MaterialData::Transform, IsTransformEnabled());
// RendererParameter
matData->SetParameter(MaterialData::Blending, GetRenderStates().blending);
matData->SetParameter(MaterialData::ColorWrite, GetRenderStates().colorWrite);
matData->SetParameter(MaterialData::DepthBuffer, GetRenderStates().depthBuffer);
matData->SetParameter(MaterialData::DepthWrite, GetRenderStates().depthWrite);
matData->SetParameter(MaterialData::FaceCulling, GetRenderStates().faceCulling);
matData->SetParameter(MaterialData::ScissorTest, GetRenderStates().scissorTest);
matData->SetParameter(MaterialData::StencilTest, GetRenderStates().stencilTest);
matData->SetParameter(MaterialData::Blending, IsBlendingEnabled());
matData->SetParameter(MaterialData::ColorWrite, IsColorWriteEnabled());
matData->SetParameter(MaterialData::DepthBuffer, IsDepthBufferEnabled());
matData->SetParameter(MaterialData::DepthWrite, IsDepthWriteEnabled());
matData->SetParameter(MaterialData::FaceCulling, IsFaceCullingEnabled());
matData->SetParameter(MaterialData::ScissorTest, IsScissorTestEnabled());
matData->SetParameter(MaterialData::StencilTest, IsStencilTestEnabled());
// Samplers
matData->SetParameter(MaterialData::DiffuseAnisotropyLevel, int(GetDiffuseSampler().GetAnisotropicLevel()));
@ -350,20 +311,20 @@ namespace Nz
matData->SetParameter(MaterialData::SpecularWrap, int(GetSpecularSampler().GetWrapMode()));
// Stencil
matData->SetParameter(MaterialData::StencilCompare, int(GetRenderStates().stencilCompare.front));
matData->SetParameter(MaterialData::StencilFail, int(GetRenderStates().stencilFail.front));
matData->SetParameter(MaterialData::StencilPass, int(GetRenderStates().stencilPass.front));
matData->SetParameter(MaterialData::StencilZFail, int(GetRenderStates().stencilDepthFail.front));
matData->SetParameter(MaterialData::StencilMask, int(GetRenderStates().stencilWriteMask.front));
matData->SetParameter(MaterialData::StencilReference, int(GetRenderStates().stencilReference.front));
matData->SetParameter(MaterialData::StencilCompare, int(GetPipelineInfo().stencilCompare.front));
matData->SetParameter(MaterialData::StencilFail, int(GetPipelineInfo().stencilFail.front));
matData->SetParameter(MaterialData::StencilPass, int(GetPipelineInfo().stencilPass.front));
matData->SetParameter(MaterialData::StencilZFail, int(GetPipelineInfo().stencilDepthFail.front));
matData->SetParameter(MaterialData::StencilMask, int(GetPipelineInfo().stencilWriteMask.front));
matData->SetParameter(MaterialData::StencilReference, int(GetPipelineInfo().stencilReference.front));
// Stencil (back)
matData->SetParameter(MaterialData::BackFaceStencilCompare, int(GetRenderStates().stencilCompare.back));
matData->SetParameter(MaterialData::BackFaceStencilFail, int(GetRenderStates().stencilFail.back));
matData->SetParameter(MaterialData::BackFaceStencilPass, int(GetRenderStates().stencilPass.back));
matData->SetParameter(MaterialData::BackFaceStencilZFail, int(GetRenderStates().stencilDepthFail.back));
matData->SetParameter(MaterialData::BackFaceStencilMask, int(GetRenderStates().stencilWriteMask.back));
matData->SetParameter(MaterialData::BackFaceStencilReference, int(GetRenderStates().stencilReference.back));
matData->SetParameter(MaterialData::BackFaceStencilCompare, int(GetPipelineInfo().stencilCompare.back));
matData->SetParameter(MaterialData::BackFaceStencilFail, int(GetPipelineInfo().stencilFail.back));
matData->SetParameter(MaterialData::BackFaceStencilPass, int(GetPipelineInfo().stencilPass.back));
matData->SetParameter(MaterialData::BackFaceStencilZFail, int(GetPipelineInfo().stencilDepthFail.back));
matData->SetParameter(MaterialData::BackFaceStencilMask, int(GetPipelineInfo().stencilWriteMask.back));
matData->SetParameter(MaterialData::BackFaceStencilReference, int(GetPipelineInfo().stencilReference.back));
// Textures
if (HasAlphaMap())
@ -411,6 +372,8 @@ namespace Nz
/*!
* \brief Resets the material, cleans everything
*
* \remark Invalidates the pipeline
*/
void Material::Reset()
{
@ -423,29 +386,22 @@ namespace Nz
m_heightMap.Reset();
m_normalMap.Reset();
m_specularMap.Reset();
m_uberShader.Reset();
for (ShaderInstance& instance : m_shaders)
instance.uberInstance = nullptr;
m_alphaThreshold = 0.2f;
m_alphaTestEnabled = false;
m_ambientColor = Color(128, 128, 128);
m_depthSortingEnabled = false;
m_diffuseColor = Color::White;
m_diffuseSampler = TextureSampler();
m_lightingEnabled = true;
m_shadowCastingEnabled = true;
m_shadowReceiveEnabled = true;
m_shininess = 50.f;
m_specularColor = Color::White;
m_specularSampler = TextureSampler();
m_states = RenderStates();
m_states.depthBuffer = true;
m_states.faceCulling = true;
m_transformEnabled = true;
m_pipelineInfo = MaterialPipelineInfo();
m_pipelineInfo.depthBuffer = true;
m_pipelineInfo.faceCulling = true;
SetShader("Basic");
InvalidatePipeline();
}
/*!
@ -453,24 +409,18 @@ namespace Nz
*
* \param material Material to copy into this
*/
void Material::Copy(const Material& material)
{
// Copy of base states
m_alphaTestEnabled = material.m_alphaTestEnabled;
m_alphaThreshold = material.m_alphaThreshold;
m_ambientColor = material.m_ambientColor;
m_depthSortingEnabled = material.m_depthSortingEnabled;
m_diffuseColor = material.m_diffuseColor;
m_diffuseSampler = material.m_diffuseSampler;
m_lightingEnabled = material.m_lightingEnabled;
m_pipelineInfo = material.m_pipelineInfo;
m_shininess = material.m_shininess;
m_shadowCastingEnabled = material.m_shadowCastingEnabled;
m_shadowReceiveEnabled = material.m_shadowReceiveEnabled;
m_specularColor = material.m_specularColor;
m_specularSampler = material.m_specularSampler;
m_states = material.m_states;
m_transformEnabled = material.m_transformEnabled;
// Copy of reference to the textures
m_alphaMap = material.m_alphaMap;
@ -480,61 +430,8 @@ namespace Nz
m_heightMap = material.m_heightMap;
m_normalMap = material.m_normalMap;
m_specularMap = material.m_specularMap;
m_uberShader = material.m_uberShader;
// We copy the instances of the shader too
std::memcpy(&m_shaders[0], &material.m_shaders[0], (ShaderFlags_Max + 1) * sizeof(ShaderInstance));
}
/*!
* \brief Generates the shader based on flag
*
* \param flags Flag for the shaer
*/
void Material::GenerateShader(UInt32 flags) const
{
ParameterList list;
list.SetParameter("ALPHA_MAPPING", m_alphaMap.IsValid());
list.SetParameter("ALPHA_TEST", m_alphaTestEnabled);
list.SetParameter("COMPUTE_TBNMATRIX", m_normalMap.IsValid() || m_heightMap.IsValid());
list.SetParameter("DIFFUSE_MAPPING", m_diffuseMap.IsValid());
list.SetParameter("EMISSIVE_MAPPING", m_emissiveMap.IsValid());
list.SetParameter("LIGHTING", m_lightingEnabled);
list.SetParameter("NORMAL_MAPPING", m_normalMap.IsValid());
list.SetParameter("PARALLAX_MAPPING", m_heightMap.IsValid());
list.SetParameter("SHADOW_MAPPING", m_shadowReceiveEnabled);
list.SetParameter("SPECULAR_MAPPING", m_specularMap.IsValid());
list.SetParameter("TEXTURE_MAPPING", m_alphaMap.IsValid() || m_diffuseMap.IsValid() || m_emissiveMap.IsValid() ||
m_normalMap.IsValid() || m_heightMap.IsValid() || m_specularMap.IsValid() ||
flags & ShaderFlags_TextureOverlay);
list.SetParameter("TRANSFORM", m_transformEnabled);
list.SetParameter("FLAG_BILLBOARD", static_cast<bool>((flags & ShaderFlags_Billboard) != 0));
list.SetParameter("FLAG_DEFERRED", static_cast<bool>((flags & ShaderFlags_Deferred) != 0));
list.SetParameter("FLAG_INSTANCING", static_cast<bool>((flags & ShaderFlags_Instancing) != 0));
list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & ShaderFlags_TextureOverlay) != 0));
list.SetParameter("FLAG_VERTEXCOLOR", static_cast<bool>((flags & ShaderFlags_VertexColor) != 0));
ShaderInstance& instance = m_shaders[flags];
instance.uberInstance = m_uberShader->Get(list);
instance.shader = instance.uberInstance->GetShader();
#define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = instance.shader->GetUniformLocation("Material" #name)
CacheUniform(AlphaMap);
CacheUniform(AlphaThreshold);
CacheUniform(Ambient);
CacheUniform(Diffuse);
CacheUniform(DiffuseMap);
CacheUniform(EmissiveMap);
CacheUniform(HeightMap);
CacheUniform(NormalMap);
CacheUniform(Shininess);
CacheUniform(Specular);
CacheUniform(SpecularMap);
#undef CacheUniform
InvalidatePipeline();
}
/*!
@ -543,7 +440,6 @@ namespace Nz
*
* \remark Produces a NazaraError if the material library failed to be initialized
*/
bool Material::Initialize()
{
if (!MaterialLibrary::Initialize())
@ -558,67 +454,21 @@ namespace Nz
return false;
}
// Basic shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_basicFragmentShader), sizeof(r_basicFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_basicVertexShader), sizeof(r_basicVertexShader));
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("Basic", uberShader);
}
// PhongLighting shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader));
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING LIGHTING NORMAL_MAPPING PARALLAX_MAPPING SHADOW_MAPPING SPECULAR_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX LIGHTING PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("PhongLighting", uberShader);
}
// Once the base shaders are registered, we can now set some default materials
s_defaultMaterial = New();
s_defaultMaterial->Enable(RendererParameter_FaceCulling, false);
s_defaultMaterial->EnableFaceCulling(false);
s_defaultMaterial->SetFaceFilling(FaceFilling_Line);
MaterialLibrary::Register("Default", s_defaultMaterial);
MaterialRef mat;
mat = New();
mat->Enable(RendererParameter_DepthWrite, false);
mat->Enable(RendererParameter_FaceCulling, false);
mat->EnableLighting(false);
MaterialLibrary::Register("Basic2D", std::move(mat));
mat = New();
mat->Enable(RendererParameter_Blend, true);
mat->Enable(RendererParameter_DepthWrite, false);
mat->Enable(RendererParameter_FaceCulling, false);
mat->EnableLighting(false);
mat->SetDstBlend(BlendFunc_InvSrcAlpha);
mat->SetSrcBlend(BlendFunc_SrcAlpha);
MaterialLibrary::Register("Translucent2D", std::move(mat));
return true;
}
/*!
* \brief Uninitializes the material librairies
*/
void Material::Uninitialize()
{
s_defaultMaterial.Reset();
UberShaderLibrary::Unregister("PhongLighting");
UberShaderLibrary::Unregister("Basic");
MaterialManager::Uninitialize();
MaterialLibrary::Uninitialize();
}

View File

@ -0,0 +1,168 @@
// Copyright (C) 2016 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/MaterialPipeline.hpp>
#ifndef NAZARA_RENDERER_OPENGL
#define NAZARA_RENDERER_OPENGL // Mandatory to include the OpenGL headers
#endif
#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/UberShaderPreprocessor.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
namespace
{
const UInt8 r_basicFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.frag.h>
};
const UInt8 r_basicVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/Basic/core.vert.h>
};
const UInt8 r_phongLightingFragmentShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h>
};
const UInt8 r_phongLightingVertexShader[] = {
#include <Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h>
};
}
/*!
* \ingroup graphics
* \class Nz::MaterialPipeline
*
* \brief Graphics class used to contains all rendering states that are not allowed to change individually on rendering devices
*/
/*!
* \brief Returns a reference to a MaterialPipeline built with MaterialPipelineInfo
*
* This function is using a cache, calling it multiples times with the same MaterialPipelineInfo will returns references to a single MaterialPipeline
*
* \param pipelineInfo Pipeline informations used to build/retrieve a MaterialPipeline object
*/
MaterialPipelineRef MaterialPipeline::GetPipeline(const MaterialPipelineInfo& pipelineInfo)
{
auto it = s_pipelineCache.lower_bound(pipelineInfo);
if (it == s_pipelineCache.end() || it->first != pipelineInfo)
it = s_pipelineCache.insert(it, PipelineCache::value_type(pipelineInfo, New(pipelineInfo)));
return it->second;
}
void MaterialPipeline::GenerateRenderPipeline(UInt32 flags) const
{
ParameterList list;
list.SetParameter("ALPHA_MAPPING", m_pipelineInfo.hasAlphaMap);
list.SetParameter("ALPHA_TEST", m_pipelineInfo.alphaTest);
list.SetParameter("COMPUTE_TBNMATRIX", m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap);
list.SetParameter("DIFFUSE_MAPPING", m_pipelineInfo.hasDiffuseMap);
list.SetParameter("EMISSIVE_MAPPING", m_pipelineInfo.hasEmissiveMap);
list.SetParameter("NORMAL_MAPPING", m_pipelineInfo.hasNormalMap);
list.SetParameter("PARALLAX_MAPPING", m_pipelineInfo.hasHeightMap);
list.SetParameter("SHADOW_MAPPING", m_pipelineInfo.shadowReceive);
list.SetParameter("SPECULAR_MAPPING", m_pipelineInfo.hasSpecularMap);
list.SetParameter("TEXTURE_MAPPING", m_pipelineInfo.hasAlphaMap || m_pipelineInfo.hasDiffuseMap || m_pipelineInfo.hasEmissiveMap ||
m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap || m_pipelineInfo.hasSpecularMap ||
flags & ShaderFlags_TextureOverlay);
list.SetParameter("TRANSFORM", true);
list.SetParameter("FLAG_BILLBOARD", static_cast<bool>((flags & ShaderFlags_Billboard) != 0));
list.SetParameter("FLAG_DEFERRED", static_cast<bool>((flags & ShaderFlags_Deferred) != 0));
list.SetParameter("FLAG_INSTANCING", static_cast<bool>((flags & ShaderFlags_Instancing) != 0));
list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & ShaderFlags_TextureOverlay) != 0));
list.SetParameter("FLAG_VERTEXCOLOR", static_cast<bool>((flags & ShaderFlags_VertexColor) != 0));
Instance& instance = m_instances[flags];
instance.uberInstance = m_pipelineInfo.uberShader->Get(list);
RenderPipelineInfo renderPipelineInfo;
static_cast<RenderStates&>(renderPipelineInfo).operator=(m_pipelineInfo); // Not my proudest line
renderPipelineInfo.shader = instance.uberInstance->GetShader();
instance.renderPipeline.Create(renderPipelineInfo);
#define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = renderPipelineInfo.shader->GetUniformLocation("Material" #name)
CacheUniform(AlphaMap);
CacheUniform(AlphaThreshold);
CacheUniform(Ambient);
CacheUniform(Diffuse);
CacheUniform(DiffuseMap);
CacheUniform(EmissiveMap);
CacheUniform(HeightMap);
CacheUniform(NormalMap);
CacheUniform(Shininess);
CacheUniform(Specular);
CacheUniform(SpecularMap);
#undef CacheUniform
}
bool MaterialPipeline::Initialize()
{
// Basic shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_basicFragmentShader), sizeof(r_basicFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_basicVertexShader), sizeof(r_basicVertexShader));
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("Basic", uberShader);
}
// PhongLighting shader
{
UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New();
String fragmentShader(reinterpret_cast<const char*>(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader));
String vertexShader(reinterpret_cast<const char*>(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader));
uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING NORMAL_MAPPING PARALLAX_MAPPING SHADOW_MAPPING SPECULAR_MAPPING");
uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
UberShaderLibrary::Register("PhongLighting", uberShader);
}
// Once the base shaders are registered, we can now set some default materials
MaterialPipelineInfo pipelineInfo;
// Basic 2D - No depth write/face culling
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo));
// Translucent 2D - Alpha blending with no depth write/face culling
pipelineInfo.blending = false;
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha;
MaterialPipelineLibrary::Register("Translucent2D", GetPipeline(pipelineInfo));
return true;
}
void MaterialPipeline::Uninitialize()
{
s_pipelineCache.clear();
UberShaderLibrary::Unregister("PhongLighting");
UberShaderLibrary::Unregister("Basic");
MaterialPipelineLibrary::Uninitialize();
}
MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library;
MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache;
}

View File

@ -132,7 +132,7 @@ void main()
vec2 texCoord = vTexCoord;
#endif
#if LIGHTING && PARALLAX_MAPPING
#if PARALLAX_MAPPING
float height = texture(MaterialHeightMap, texCoord).r;
float v = height*ParallaxScale + ParallaxBias;
@ -159,7 +159,6 @@ void main()
discard;
#endif // ALPHA_TEST
#if LIGHTING
#if NORMAL_MAPPING
vec3 normal = normalize(vLightToWorld * (2.0 * vec3(texture(MaterialNormalMap, texCoord)) - 1.0));
#else
@ -179,9 +178,6 @@ void main()
RenderTarget0 = vec4(diffuseColor.rgb, dot(specularColor, vec3(0.3, 0.59, 0.11)));
RenderTarget1 = vec4(EncodeNormal(normal));
RenderTarget2 = vec4(FloatToColor(gl_FragCoord.z), (MaterialShininess == 0.0) ? 0.0 : max(log2(MaterialShininess), 0.1)/10.5); // http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf
#else // LIGHTING
RenderTarget0 = vec4(diffuseColor.rgb, 0.0);
#endif
#else // FLAG_DEFERRED
#if ALPHA_MAPPING
diffuseColor.a *= texture(MaterialAlphaMap, texCoord).r;
@ -192,7 +188,6 @@ void main()
discard;
#endif
#if LIGHTING
vec3 lightAmbient = vec3(0.0);
vec3 lightDiffuse = vec3(0.0);
vec3 lightSpecular = vec3(0.0);
@ -474,9 +469,6 @@ void main()
#else
RenderTarget0 = fragmentColor;
#endif // EMISSIVE_MAPPING
#else
RenderTarget0 = diffuseColor;
#endif // LIGHTING
#endif // FLAG_DEFERRED
}

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,7 @@ in vec3 VertexPosition;
in vec3 VertexNormal;
in vec3 VertexTangent;
in vec2 VertexTexCoord;
in vec4 VertexUserdata0;
/********************Sortant********************/
out vec4 vColor;
@ -27,6 +28,7 @@ uniform vec3 EyePosition;
uniform mat4 InvViewMatrix;
uniform mat4 LightViewProjMatrix[3];
uniform float VertexDepth;
uniform mat4 ViewMatrix;
uniform mat4 ViewProjMatrix;
uniform mat4 WorldMatrix;
uniform mat4 WorldViewProjMatrix;
@ -107,21 +109,19 @@ void main()
vColor = color;
#if LIGHTING
#if FLAG_INSTANCING
#if FLAG_INSTANCING
mat3 rotationMatrix = mat3(InstanceData0);
#else
#else
mat3 rotationMatrix = mat3(WorldMatrix);
#endif
#endif
#if COMPUTE_TBNMATRIX
#if COMPUTE_TBNMATRIX
vec3 binormal = cross(VertexNormal, VertexTangent);
vLightToWorld[0] = normalize(rotationMatrix * VertexTangent);
vLightToWorld[1] = normalize(rotationMatrix * binormal);
vLightToWorld[2] = normalize(rotationMatrix * VertexNormal);
#else
#else
vNormal = normalize(rotationMatrix * VertexNormal);
#endif
#endif
#if SHADOW_MAPPING
@ -133,12 +133,12 @@ void main()
vTexCoord = VertexTexCoord;
#endif
#if LIGHTING && PARALLAX_MAPPING
#if PARALLAX_MAPPING
vViewDir = EyePosition - VertexPosition;
vViewDir *= vLightToWorld;
#endif
#if LIGHTING && !FLAG_DEFERRED
#if !FLAG_DEFERRED
#if FLAG_INSTANCING
vWorldPos = vec3(InstanceData0 * vec4(VertexPosition, 1.0));
#else

File diff suppressed because one or more lines are too long