Merge branch 'NDK-Refactor' into NDK

Conflicts:
	examples/HardwareInfo/main.cpp
	include/Nazara/Renderer/Enums.hpp
	include/Nazara/Renderer/GpuQuery.hpp
	include/Nazara/Renderer/OpenGL.hpp
	include/Nazara/Renderer/RenderBuffer.hpp
	include/Nazara/Renderer/RenderTexture.hpp
	include/Nazara/Renderer/Texture.hpp
	src/Nazara/Graphics/AbstractRenderTechnique.cpp
	src/Nazara/Graphics/DeferredRenderTechnique.cpp
	src/Nazara/Graphics/Material.cpp
	src/Nazara/Graphics/SkyboxBackground.cpp
	src/Nazara/Renderer/GpuQuery.cpp
	src/Nazara/Renderer/OpenGL.cpp
	src/Nazara/Renderer/RenderBuffer.cpp
	src/Nazara/Renderer/RenderTexture.cpp
	src/Nazara/Renderer/Renderer.cpp
	src/Nazara/Renderer/Shader.cpp
	src/Nazara/Renderer/ShaderStage.cpp
	src/Nazara/Renderer/Texture.cpp

Former-commit-id: 2f1c7e9f9766f59ab83d9405856a1898ac4ab48f
This commit is contained in:
Lynix
2015-09-25 23:16:58 +02:00
613 changed files with 68051 additions and 66125 deletions

View File

@@ -5,6 +5,9 @@
#include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzAbstractBackground::~NzAbstractBackground() = default;
namespace Nz
{
AbstractBackground::~AbstractBackground() = default;
NzBackgroundLibrary::LibraryMap NzAbstractBackground::s_library;
BackgroundLibrary::LibraryMap AbstractBackground::s_library;
}

View File

@@ -5,28 +5,31 @@
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzAbstractRenderQueue::~NzAbstractRenderQueue() = default;
void NzAbstractRenderQueue::AddDirectionalLight(const DirectionalLight& light)
namespace Nz
{
directionalLights.push_back(light);
}
AbstractRenderQueue::~AbstractRenderQueue() = default;
void NzAbstractRenderQueue::AddPointLight(const PointLight& light)
{
pointLights.push_back(light);
}
void AbstractRenderQueue::AddDirectionalLight(const DirectionalLight& light)
{
directionalLights.push_back(light);
}
void NzAbstractRenderQueue::AddSpotLight(const SpotLight& light)
{
spotLights.push_back(light);
}
void AbstractRenderQueue::AddPointLight(const PointLight& light)
{
pointLights.push_back(light);
}
void NzAbstractRenderQueue::Clear(bool fully)
{
NazaraUnused(fully);
void AbstractRenderQueue::AddSpotLight(const SpotLight& light)
{
spotLights.push_back(light);
}
directionalLights.clear();
pointLights.clear();
spotLights.clear();
void AbstractRenderQueue::Clear(bool fully)
{
NazaraUnused(fully);
directionalLights.clear();
pointLights.clear();
spotLights.clear();
}
}

View File

@@ -8,24 +8,27 @@
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzAbstractRenderTechnique::NzAbstractRenderTechnique() :
m_instancingEnabled(true)
namespace Nz
{
}
AbstractRenderTechnique::AbstractRenderTechnique() :
m_instancingEnabled(true)
{
}
NzAbstractRenderTechnique::~NzAbstractRenderTechnique() = default;
AbstractRenderTechnique::~AbstractRenderTechnique() = default;
void NzAbstractRenderTechnique::EnableInstancing(bool instancing)
{
m_instancingEnabled = instancing;
}
void AbstractRenderTechnique::EnableInstancing(bool instancing)
{
m_instancingEnabled = instancing;
}
NzString NzAbstractRenderTechnique::GetName() const
{
return NzRenderTechniques::ToString(GetType());
}
String AbstractRenderTechnique::GetName() const
{
return RenderTechniques::ToString(GetType());
}
bool NzAbstractRenderTechnique::IsInstancingEnabled() const
{
return m_instancingEnabled;
bool AbstractRenderTechnique::IsInstancingEnabled() const
{
return m_instancingEnabled;
}
}

View File

@@ -5,4 +5,7 @@
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzAbstractViewer::~NzAbstractViewer() = default;
namespace Nz
{
AbstractViewer::~AbstractViewer() = default;
}

View File

@@ -10,19 +10,22 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
void NzBillboard::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
namespace Nz
{
if (!m_material)
return;
void Billboard::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
if (!m_material)
return;
renderQueue->AddBillboard(m_material, instanceData.transformMatrix.GetTranslation(), m_size, m_sinCos, m_color);
renderQueue->AddBillboard(m_material, instanceData.transformMatrix.GetTranslation(), m_size, m_sinCos, m_color);
}
void Billboard::MakeBoundingVolume() const
{
constexpr float sqrt2 = float(M_SQRT2);
m_boundingVolume.Set(Vector3f(0.f), sqrt2*m_size.x*Vector3f::Right() + sqrt2*m_size.y*Vector3f::Down());
}
BillboardLibrary::LibraryMap Billboard::s_library;
}
void NzBillboard::MakeBoundingVolume() const
{
constexpr float sqrt2 = float(M_SQRT2);
m_boundingVolume.Set(NzVector3f(0.f), sqrt2*m_size.x*NzVector3f::Right() + sqrt2*m_size.y*NzVector3f::Down());
}
NzBillboardLibrary::LibraryMap NzBillboard::s_library;

View File

@@ -7,64 +7,66 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
NzRenderStates BuildRenderStates()
namespace
{
NzRenderStates states;
states.depthFunc = nzRendererComparison_Equal;
states.faceCulling = nzFaceSide_Back;
states.parameters[nzRendererParameter_DepthBuffer] = true;
states.parameters[nzRendererParameter_DepthWrite] = false;
states.parameters[nzRendererParameter_FaceCulling] = true;
RenderStates BuildRenderStates()
{
RenderStates states;
states.depthFunc = RendererComparison_Equal;
states.faceCulling = FaceSide_Back;
states.parameters[RendererParameter_DepthBuffer] = true;
states.parameters[RendererParameter_DepthWrite] = false;
states.parameters[RendererParameter_FaceCulling] = true;
return states;
return states;
}
}
ColorBackground::ColorBackground(const Color& color) :
m_color(color)
{
m_uberShader = UberShaderLibrary::Get("Basic");
ParameterList list;
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
}
void ColorBackground::Draw(const AbstractViewer* viewer) const
{
NazaraUnused(viewer);
static RenderStates states(BuildRenderStates());
Renderer::SetRenderStates(states);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, m_color);
shader->SendFloat(m_vertexDepthUniform, 1.f);
Renderer::DrawFullscreenQuad();
}
BackgroundType ColorBackground::GetBackgroundType() const
{
return BackgroundType_Color;
}
Color ColorBackground::GetColor() const
{
return m_color;
}
void ColorBackground::SetColor(const Color& color)
{
m_color = color;
}
}
NzColorBackground::NzColorBackground(const NzColor& color) :
m_color(color)
{
m_uberShader = NzUberShaderLibrary::Get("Basic");
NzParameterList list;
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const NzShader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
}
void NzColorBackground::Draw(const NzAbstractViewer* viewer) const
{
NazaraUnused(viewer);
static NzRenderStates states(BuildRenderStates());
NzRenderer::SetRenderStates(states);
m_uberShaderInstance->Activate();
const NzShader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, m_color);
shader->SendFloat(m_vertexDepthUniform, 1.f);
NzRenderer::DrawFullscreenQuad();
}
nzBackgroundType NzColorBackground::GetBackgroundType() const
{
return nzBackgroundType_Color;
}
NzColor NzColorBackground::GetColor() const
{
return m_color;
}
void NzColorBackground::SetColor(const NzColor& color)
{
m_color = color;
}

View File

@@ -7,161 +7,164 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredBloomPass::NzDeferredBloomPass() :
m_uniformUpdated(false),
m_brightLuminance(0.8f),
m_brightMiddleGrey(0.5f),
m_brightThreshold(0.8f),
m_blurPassCount(5)
namespace Nz
{
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(nzSamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_bloomBrightShader = NzShaderLibrary::Get("DeferredBloomBright");
m_bloomFinalShader = NzShaderLibrary::Get("DeferredBloomFinal");
m_bloomStates.parameters[nzRendererParameter_DepthBuffer] = false;
m_gaussianBlurShader = NzShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
for (unsigned int i = 0; i < 2; ++i)
m_bloomTextures[i] = NzTexture::New();
}
NzDeferredBloomPass::~NzDeferredBloomPass() = default;
unsigned int NzDeferredBloomPass::GetBlurPassCount() const
{
return m_blurPassCount;
}
float NzDeferredBloomPass::GetBrightLuminance() const
{
return m_brightLuminance;
}
float NzDeferredBloomPass::GetBrightMiddleGrey() const
{
return m_brightMiddleGrey;
}
float NzDeferredBloomPass::GetBrightThreshold() const
{
return m_brightThreshold;
}
NzTexture* NzDeferredBloomPass::GetTexture(unsigned int i) const
{
#if NAZARA_GRAPHICS_SAFE
if (i >= 2)
DeferredBloomPass::DeferredBloomPass() :
m_uniformUpdated(false),
m_brightLuminance(0.8f),
m_brightMiddleGrey(0.5f),
m_brightThreshold(0.8f),
m_blurPassCount(5)
{
NazaraError("Texture index out of range (" + NzString::Number(i) + " >= 2)");
return nullptr;
}
#endif
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(SamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(SamplerWrap_Clamp);
return m_bloomTextures[i];
}
m_bloomBrightShader = ShaderLibrary::Get("DeferredBloomBright");
m_bloomFinalShader = ShaderLibrary::Get("DeferredBloomFinal");
m_bloomStates.parameters[RendererParameter_DepthBuffer] = false;
m_gaussianBlurShader = ShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
bool NzDeferredBloomPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
NzRenderer::SetRenderStates(m_bloomStates);
NzRenderer::SetTextureSampler(0, m_bilinearSampler);
NzRenderer::SetTextureSampler(1, m_bilinearSampler);
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetShader(m_bloomBrightShader);
if (!m_uniformUpdated)
{
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightLuminance"), m_brightLuminance);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightMiddleGrey"), m_brightMiddleGrey);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightThreshold"), m_brightThreshold);
m_uniformUpdated = true;
for (unsigned int i = 0; i < 2; ++i)
m_bloomTextures[i] = Texture::New();
}
NzRenderer::SetTexture(0, m_workTextures[secondWorkTexture]);
NzRenderer::DrawFullscreenQuad();
DeferredBloomPass::~DeferredBloomPass() = default;
NzRenderer::SetTarget(&m_bloomRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x/8, m_dimensions.y/8));
NzRenderer::SetShader(m_gaussianBlurShader);
for (unsigned int i = 0; i < m_blurPassCount; ++i)
unsigned int DeferredBloomPass::GetBlurPassCount() const
{
m_bloomRTT.SetColorTarget(0); // bloomTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, NzVector2f(1.f, 0.f));
NzRenderer::SetTexture(0, (i == 0) ? m_workTextures[firstWorkTexture] : static_cast<const NzTexture*>(m_bloomTextures[1]));
NzRenderer::DrawFullscreenQuad();
m_bloomRTT.SetColorTarget(1); // bloomTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, NzVector2f(0.f, 1.f));
NzRenderer::SetTexture(0, m_bloomTextures[0]);
NzRenderer::DrawFullscreenQuad();
return m_blurPassCount;
}
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetShader(m_bloomFinalShader);
NzRenderer::SetTexture(0, m_bloomTextures[1]);
NzRenderer::SetTexture(1, m_workTextures[secondWorkTexture]);
NzRenderer::DrawFullscreenQuad();
return true;
}
bool NzDeferredBloomPass::Resize(const NzVector2ui& dimensions)
{
NzDeferredRenderPass::Resize(dimensions);
m_bloomRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
float DeferredBloomPass::GetBrightLuminance() const
{
m_bloomTextures[i]->Create(nzImageType_2D, nzPixelFormat_RGBA8, dimensions.x/8, dimensions.y/8);
m_bloomRTT.AttachTexture(nzAttachmentPoint_Color, i, m_bloomTextures[i]);
}
m_bloomRTT.Unlock();
if (!m_bloomRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
return m_brightLuminance;
}
return true;
}
float DeferredBloomPass::GetBrightMiddleGrey() const
{
return m_brightMiddleGrey;
}
void NzDeferredBloomPass::SetBlurPassCount(unsigned int passCount)
{
m_blurPassCount = passCount; // N'est pas une uniforme
}
float DeferredBloomPass::GetBrightThreshold() const
{
return m_brightThreshold;
}
void NzDeferredBloomPass::SetBrightLuminance(float luminance)
{
m_brightLuminance = luminance;
m_uniformUpdated = false;
}
Texture* DeferredBloomPass::GetTexture(unsigned int i) const
{
#if NAZARA_GRAPHICS_SAFE
if (i >= 2)
{
NazaraError("Texture index out of range (" + String::Number(i) + " >= 2)");
return nullptr;
}
#endif
void NzDeferredBloomPass::SetBrightMiddleGrey(float middleGrey)
{
m_brightMiddleGrey = middleGrey;
m_uniformUpdated = false;
}
return m_bloomTextures[i];
}
void NzDeferredBloomPass::SetBrightThreshold(float threshold)
{
m_brightThreshold = threshold;
m_uniformUpdated = false;
bool DeferredBloomPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
Renderer::SetRenderStates(m_bloomStates);
Renderer::SetTextureSampler(0, m_bilinearSampler);
Renderer::SetTextureSampler(1, m_bilinearSampler);
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_bloomBrightShader);
if (!m_uniformUpdated)
{
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightLuminance"), m_brightLuminance);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightMiddleGrey"), m_brightMiddleGrey);
m_bloomBrightShader->SendFloat(m_bloomBrightShader->GetUniformLocation("BrightThreshold"), m_brightThreshold);
m_uniformUpdated = true;
}
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::DrawFullscreenQuad();
Renderer::SetTarget(&m_bloomRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x/8, m_dimensions.y/8));
Renderer::SetShader(m_gaussianBlurShader);
for (unsigned int i = 0; i < m_blurPassCount; ++i)
{
m_bloomRTT.SetColorTarget(0); // bloomTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(1.f, 0.f));
Renderer::SetTexture(0, (i == 0) ? m_workTextures[firstWorkTexture] : static_cast<const Texture*>(m_bloomTextures[1]));
Renderer::DrawFullscreenQuad();
m_bloomRTT.SetColorTarget(1); // bloomTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(0.f, 1.f));
Renderer::SetTexture(0, m_bloomTextures[0]);
Renderer::DrawFullscreenQuad();
}
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_bloomFinalShader);
Renderer::SetTexture(0, m_bloomTextures[1]);
Renderer::SetTexture(1, m_workTextures[secondWorkTexture]);
Renderer::DrawFullscreenQuad();
return true;
}
bool DeferredBloomPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
m_bloomRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_bloomTextures[i]->Create(ImageType_2D, PixelFormatType_RGBA8, dimensions.x/8, dimensions.y/8);
m_bloomRTT.AttachTexture(AttachmentPoint_Color, i, m_bloomTextures[i]);
}
m_bloomRTT.Unlock();
if (!m_bloomRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
void DeferredBloomPass::SetBlurPassCount(unsigned int passCount)
{
m_blurPassCount = passCount; // N'est pas une uniforme
}
void DeferredBloomPass::SetBrightLuminance(float luminance)
{
m_brightLuminance = luminance;
m_uniformUpdated = false;
}
void DeferredBloomPass::SetBrightMiddleGrey(float middleGrey)
{
m_brightMiddleGrey = middleGrey;
m_uniformUpdated = false;
}
void DeferredBloomPass::SetBrightThreshold(float threshold)
{
m_brightThreshold = threshold;
m_uniformUpdated = false;
}
}

View File

@@ -9,174 +9,177 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
// http://digitalerr0r.wordpress.com/2009/05/16/xna-shader-programming-tutorial-20-depth-of-field/
NzShaderRef BuildDepthOfFieldShader()
namespace
{
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D BlurTexture;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer1;\n"
"uniform vec2 InvTargetSize;" "\n"
"float Distance = 30.0;\n"
"float Range = 10.0;\n"
"float Near = 0.1;\n"
"float Far = (1000.0) / (1000.0 - 0.1);\n"
//"float Far = 50.0;\n"
"void main()\n"
"{\n"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"// Get our original pixel from ColorMap\n"
"vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb;\n"
"// Get our bloom pixel from bloom texture\n"
"vec3 blur = textureLod(BlurTexture, texCoord, 0.0).rgb;\n"
"float depth = textureLod(GBuffer1, texCoord, 0.0).w;\n"
"depth = (2.0 * 0.1) / (1000.0 + 0.1 - depth * (1000.0 - 0.1));"
"depth = 1.0 - depth;\n"
"float fSceneZ = ( -Near * Far ) / ( depth - Far);\n"
"float blurFactor = clamp(abs(fSceneZ - Distance)/Range, 0.0, 1.0);\n"
"RenderTarget0 = vec4(mix(color, blur, blurFactor), 1.0);\n"
"}\n";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
NzShaderRef shader = NzShader::New();
if (!shader->Create())
// http://digitalerr0r.wordpress.com/2009/05/16/xna-shader-programming-tutorial-20-depth-of-field/
ShaderRef BuildDepthOfFieldShader()
{
NazaraError("Failed to load create shader");
return nullptr;
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D BlurTexture;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer1;\n"
"uniform vec2 InvTargetSize;" "\n"
"float Distance = 30.0;\n"
"float Range = 10.0;\n"
"float Near = 0.1;\n"
"float Far = (1000.0) / (1000.0 - 0.1);\n"
//"float Far = 50.0;\n"
"void main()\n"
"{\n"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"// Get our original pixel from ColorMap\n"
"vec3 color = textureLod(ColorTexture, texCoord, 0.0).rgb;\n"
"// Get our bloom pixel from bloom texture\n"
"vec3 blur = textureLod(BlurTexture, texCoord, 0.0).rgb;\n"
"float depth = textureLod(GBuffer1, texCoord, 0.0).w;\n"
"depth = (2.0 * 0.1) / (1000.0 + 0.1 - depth * (1000.0 - 0.1));"
"depth = 1.0 - depth;\n"
"float fSceneZ = ( -Near * Far ) / ( depth - Far);\n"
"float blurFactor = clamp(abs(fSceneZ - Distance)/Range, 0.0, 1.0);\n"
"RenderTarget0 = vec4(mix(color, blur, blurFactor), 1.0);\n"
"}\n";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
ShaderRef shader = Shader::New();
if (!shader->Create())
{
NazaraError("Failed to load create shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentSource))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
return shader;
}
if (!shader->AttachStageFromSource(nzShaderStage_Fragment, fragmentSource))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(nzShaderStage_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
return shader;
}
}
NzDeferredDOFPass::NzDeferredDOFPass()
{
m_dofShader = BuildDepthOfFieldShader();
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("ColorTexture"), 0);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("BlurTexture"), 1);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("GBuffer1"), 2);
m_gaussianBlurShader = NzShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
for (unsigned int i = 0; i < 2; ++i)
m_dofTextures[i] = NzTexture::New();
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(nzSamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest);
m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_states.parameters[nzRendererParameter_DepthBuffer] = false;
}
NzDeferredDOFPass::~NzDeferredDOFPass() = default;
bool NzDeferredDOFPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
NzRenderer::SetTextureSampler(0, m_pointSampler);
NzRenderer::SetTextureSampler(1, m_bilinearSampler);
NzRenderer::SetTextureSampler(2, m_pointSampler);
NzRenderer::SetTarget(&m_dofRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x/4, m_dimensions.y/4));
NzRenderer::SetShader(m_gaussianBlurShader);
const unsigned int dofBlurPass = 2;
for (unsigned int i = 0; i < dofBlurPass; ++i)
{
m_dofRTT.SetColorTarget(0); // dofTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, NzVector2f(1.f, 0.f));
NzRenderer::SetTexture(0, (i == 0) ? m_workTextures[secondWorkTexture] : static_cast<const NzTexture*>(m_dofTextures[1]));
NzRenderer::DrawFullscreenQuad();
m_dofRTT.SetColorTarget(1); // dofTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, NzVector2f(0.f, 1.f));
NzRenderer::SetTexture(0, m_dofTextures[0]);
NzRenderer::DrawFullscreenQuad();
}
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetShader(m_dofShader);
NzRenderer::SetTexture(0, m_workTextures[secondWorkTexture]);
NzRenderer::SetTexture(1, m_dofTextures[1]);
NzRenderer::SetTexture(2, m_GBuffer[1]);
NzRenderer::DrawFullscreenQuad();
return true;
}
bool NzDeferredDOFPass::Resize(const NzVector2ui& dimensions)
{
NzDeferredRenderPass::Resize(dimensions);
m_dofRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
DeferredDOFPass::DeferredDOFPass()
{
m_dofTextures[i]->Create(nzImageType_2D, nzPixelFormat_RGBA8, dimensions.x/4, dimensions.y/4);
m_dofRTT.AttachTexture(nzAttachmentPoint_Color, i, m_dofTextures[i]);
}
m_dofRTT.Unlock();
m_dofShader = BuildDepthOfFieldShader();
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("ColorTexture"), 0);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("BlurTexture"), 1);
m_dofShader->SendInteger(m_dofShader->GetUniformLocation("GBuffer1"), 2);
if (!m_dofRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
m_gaussianBlurShader = ShaderLibrary::Get("DeferredGaussianBlur");
m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter");
for (unsigned int i = 0; i < 2; ++i)
m_dofTextures[i] = Texture::New();
m_bilinearSampler.SetAnisotropyLevel(1);
m_bilinearSampler.SetFilterMode(SamplerFilter_Bilinear);
m_bilinearSampler.SetWrapMode(SamplerWrap_Clamp);
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.parameters[RendererParameter_DepthBuffer] = false;
}
return true;
DeferredDOFPass::~DeferredDOFPass() = default;
bool DeferredDOFPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTextureSampler(1, m_bilinearSampler);
Renderer::SetTextureSampler(2, m_pointSampler);
Renderer::SetTarget(&m_dofRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x/4, m_dimensions.y/4));
Renderer::SetShader(m_gaussianBlurShader);
const unsigned int dofBlurPass = 2;
for (unsigned int i = 0; i < dofBlurPass; ++i)
{
m_dofRTT.SetColorTarget(0); // dofTextureA
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(1.f, 0.f));
Renderer::SetTexture(0, (i == 0) ? m_workTextures[secondWorkTexture] : static_cast<const Texture*>(m_dofTextures[1]));
Renderer::DrawFullscreenQuad();
m_dofRTT.SetColorTarget(1); // dofTextureB
m_gaussianBlurShader->SendVector(m_gaussianBlurShaderFilterLocation, Vector2f(0.f, 1.f));
Renderer::SetTexture(0, m_dofTextures[0]);
Renderer::DrawFullscreenQuad();
}
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_dofShader);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTexture(1, m_dofTextures[1]);
Renderer::SetTexture(2, m_GBuffer[1]);
Renderer::DrawFullscreenQuad();
return true;
}
bool DeferredDOFPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
m_dofRTT.Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_dofTextures[i]->Create(ImageType_2D, PixelFormatType_RGBA8, dimensions.x/4, dimensions.y/4);
m_dofRTT.AttachTexture(AttachmentPoint_Color, i, m_dofTextures[i]);
}
m_dofRTT.Unlock();
if (!m_dofRTT.IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
}

View File

@@ -8,32 +8,35 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredFXAAPass::NzDeferredFXAAPass()
namespace Nz
{
m_fxaaShader = NzShaderLibrary::Get("DeferredFXAA");
DeferredFXAAPass::DeferredFXAAPass()
{
m_fxaaShader = ShaderLibrary::Get("DeferredFXAA");
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest);
m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.parameters[nzRendererParameter_DepthBuffer] = false;
}
NzDeferredFXAAPass::~NzDeferredFXAAPass() = default;
bool NzDeferredFXAAPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetRenderStates(m_states);
NzRenderer::SetShader(m_fxaaShader);
NzRenderer::SetTexture(0, m_workTextures[secondWorkTexture]);
NzRenderer::SetTextureSampler(0, m_pointSampler);
NzRenderer::DrawFullscreenQuad();
return true;
m_states.parameters[RendererParameter_DepthBuffer] = false;
}
DeferredFXAAPass::~DeferredFXAAPass() = default;
bool DeferredFXAAPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraUnused(sceneData);
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetRenderStates(m_states);
Renderer::SetShader(m_fxaaShader);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::DrawFullscreenQuad();
return true;
}
}

View File

@@ -8,49 +8,52 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredFinalPass::NzDeferredFinalPass()
namespace Nz
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest);
m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp);
DeferredFinalPass::DeferredFinalPass()
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_states.parameters[nzRendererParameter_DepthBuffer] = false;
m_states.parameters[RendererParameter_DepthBuffer] = false;
m_uberShader = NzUberShaderLibrary::Get("Basic");
m_uberShader = UberShaderLibrary::Get("Basic");
NzParameterList list;
list.SetParameter("AUTO_TEXCOORDS", true);
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", false);
ParameterList list;
list.SetParameter("AUTO_TEXCOORDS", true);
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", false);
m_uberShaderInstance = m_uberShader->Get(list);
m_uberShaderInstance = m_uberShader->Get(list);
const NzShader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
}
NzDeferredFinalPass::~NzDeferredFinalPass() = default;
bool NzDeferredFinalPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
sceneData.viewer->ApplyView();
NzRenderer::SetRenderStates(m_states);
NzRenderer::SetTexture(0, m_workTextures[secondWorkTexture]);
NzRenderer::SetTextureSampler(0, m_pointSampler);
m_uberShaderInstance->Activate();
const NzShader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, NzColor::White);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
NzRenderer::DrawFullscreenQuad();
return false;
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
}
DeferredFinalPass::~DeferredFinalPass() = default;
bool DeferredFinalPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
sceneData.viewer->ApplyView();
Renderer::SetRenderStates(m_states);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTextureSampler(0, m_pointSampler);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, Color::White);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
Renderer::DrawFullscreenQuad();
return false;
}
}

View File

@@ -9,143 +9,146 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
NzShaderRef BuildFogShader()
namespace
{
/*const nzUInt8 fragmentSource[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/FXAA.frag.h>
};*/
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer2;\n"
"uniform mat4 InvViewProjMatrix;\n"
"uniform vec2 InvTargetSize;\n"
"uniform vec3 EyePosition;\n"
"float n = 0.1;"
"float f = 1000.0;"
"float color_to_float(vec3 color)\n"
"{\n"
"const vec3 byte_to_float = vec3(1.0, 1.0/256, 1.0/(256*256));\n"
"return dot(color, byte_to_float);\n"
"}\n"
"void main()\n"
"{"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"\t" "vec3 color = texture(ColorTexture, texCoord).xyz;\n"
"vec4 gVec2 = textureLod(GBuffer2, texCoord, 0.0);\n"
"float depth = color_to_float(gVec2.xyz)*2.0 - 1.0;\n"
"float linearDepth = (2 * n) / (f + n - depth * (f - n));"
"vec3 viewSpace = vec3(texCoord*2.0 - 1.0, depth);\n"
"vec4 worldPos = InvViewProjMatrix * vec4(viewSpace, 1.0);\n"
"worldPos.xyz /= worldPos.w;\n"
/*"float lumThreshold = 0.1;"
"float lumMultipler = 2.0;"
//"float lumFactor = max(dot(color, vec3(0.299, 0.587, 0.114)) - lumThreshold, 0.0) / (1.0-lumThreshold);"
"float fogFactor = (1.0 - clamp(worldPos.y-2.0, 0.0, 1.0)) - lumFactor*lumMultipler;"
"fogFactor += (1.0 - clamp(EyePosition.y-2.5, 0.0, 1.0));"
"fogFactor = clamp(fogFactor, 0.0, 1.0);"*/
"float lumThreshold = 0.8;"
"float lumMultipler = 2.0;"
"float luminosity = dot(color, vec3(0.299, 0.587, 0.114));"
"float lumFactor = max(luminosity - lumThreshold, 0.0) / (1.0-lumThreshold);"
"vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
"vec2 fogrange = vec2(0, 50);\n"
"float fogeffect = clamp( 1.0 - (fogrange.y - linearDepth*0.5*f) / (fogrange.y - fogrange.x) , 0.0, 1.0 ) * fogColor.w;\n"
"fogeffect = max(fogeffect-lumFactor, 0.0);"
//fogeffect*=(1.0 - int(depth));
"\t" "vec3 fragmentColor = color*(1.0-fogeffect) + fogColor.rgb * fogeffect;\n"
"\t" "RenderTarget0 = vec4(fragmentColor, 1.0);\n"
"}";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
NzShaderRef shader = NzShader::New();
if (!shader->Create())
ShaderRef BuildFogShader()
{
NazaraError("Failed to load create shader");
return nullptr;
/*const UInt8 fragmentSource[] = {
#include <Nazara/Graphics/Resources/DeferredShading/Shaders/FXAA.frag.h>
};*/
const char* fragmentSource =
"#version 140\n"
"out vec4 RenderTarget0;\n"
"uniform sampler2D ColorTexture;\n"
"uniform sampler2D GBuffer2;\n"
"uniform mat4 InvViewProjMatrix;\n"
"uniform vec2 InvTargetSize;\n"
"uniform vec3 EyePosition;\n"
"float n = 0.1;"
"float f = 1000.0;"
"float color_to_float(vec3 color)\n"
"{\n"
"const vec3 byte_to_float = vec3(1.0, 1.0/256, 1.0/(256*256));\n"
"return dot(color, byte_to_float);\n"
"}\n"
"void main()\n"
"{"
"vec2 texCoord = gl_FragCoord.xy * InvTargetSize;\n"
"\t" "vec3 color = texture(ColorTexture, texCoord).xyz;\n"
"vec4 gVec2 = textureLod(GBuffer2, texCoord, 0.0);\n"
"float depth = color_to_float(gVec2.xyz)*2.0 - 1.0;\n"
"float linearDepth = (2 * n) / (f + n - depth * (f - n));"
"vec3 viewSpace = vec3(texCoord*2.0 - 1.0, depth);\n"
"vec4 worldPos = InvViewProjMatrix * vec4(viewSpace, 1.0);\n"
"worldPos.xyz /= worldPos.w;\n"
/*"float lumThreshold = 0.1;"
"float lumMultipler = 2.0;"
//"float lumFactor = max(dot(color, vec3(0.299, 0.587, 0.114)) - lumThreshold, 0.0) / (1.0-lumThreshold);"
"float fogFactor = (1.0 - clamp(worldPos.y-2.0, 0.0, 1.0)) - lumFactor*lumMultipler;"
"fogFactor += (1.0 - clamp(EyePosition.y-2.5, 0.0, 1.0));"
"fogFactor = clamp(fogFactor, 0.0, 1.0);"*/
"float lumThreshold = 0.8;"
"float lumMultipler = 2.0;"
"float luminosity = dot(color, vec3(0.299, 0.587, 0.114));"
"float lumFactor = max(luminosity - lumThreshold, 0.0) / (1.0-lumThreshold);"
"vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
"vec2 fogrange = vec2(0, 50);\n"
"float fogeffect = clamp( 1.0 - (fogrange.y - linearDepth*0.5*f) / (fogrange.y - fogrange.x) , 0.0, 1.0 ) * fogColor.w;\n"
"fogeffect = max(fogeffect-lumFactor, 0.0);"
//fogeffect*=(1.0 - int(depth));
"\t" "vec3 fragmentColor = color*(1.0-fogeffect) + fogColor.rgb * fogeffect;\n"
"\t" "RenderTarget0 = vec4(fragmentColor, 1.0);\n"
"}";
const char* vertexSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"void main()\n"
"{\n"
"\t" "gl_Position = vec4(VertexPosition, 1.0);" "\n"
"}\n";
///TODO: Remplacer ça par des ShaderNode
ShaderRef shader = Shader::New();
if (!shader->Create())
{
NazaraError("Failed to load create shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentSource/*String(reinterpret_cast<const char*>(fragmentSource), sizeof(fragmentSource))*/))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(ShaderStageType_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 1);
return shader;
}
if (!shader->AttachStageFromSource(nzShaderStage_Fragment, fragmentSource/*NzString(reinterpret_cast<const char*>(fragmentSource), sizeof(fragmentSource))*/))
{
NazaraError("Failed to load fragment shader");
return nullptr;
}
if (!shader->AttachStageFromSource(nzShaderStage_Vertex, vertexSource))
{
NazaraError("Failed to load vertex shader");
return nullptr;
}
if (!shader->Link())
{
NazaraError("Failed to link shader");
return nullptr;
}
shader->SendInteger(shader->GetUniformLocation("ColorTexture"), 0);
shader->SendInteger(shader->GetUniformLocation("GBuffer2"), 1);
return shader;
}
DeferredFogPass::DeferredFogPass()
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_shader = BuildFogShader();
m_shaderEyePositionLocation = m_shader->GetUniformLocation("EyePosition");
m_states.parameters[RendererParameter_DepthBuffer] = false;
}
DeferredFogPass::~DeferredFogPass() = default;
bool DeferredFogPass::Process( const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetShader(m_shader);
m_shader->SendVector(m_shaderEyePositionLocation, sceneData.viewer->GetEyePosition());
Renderer::SetRenderStates(m_states);
Renderer::SetTexture(0, m_workTextures[secondWorkTexture]);
Renderer::SetTexture(1, m_GBuffer[2]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTextureSampler(1, m_pointSampler);
Renderer::DrawFullscreenQuad();
return true;
}
NzDeferredFogPass::NzDeferredFogPass()
{
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest);
m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_shader = BuildFogShader();
m_shaderEyePositionLocation = m_shader->GetUniformLocation("EyePosition");
m_states.parameters[nzRendererParameter_DepthBuffer] = false;
}
NzDeferredFogPass::~NzDeferredFogPass() = default;
bool NzDeferredFogPass::Process( const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetShader(m_shader);
m_shader->SendVector(m_shaderEyePositionLocation, sceneData.viewer->GetEyePosition());
NzRenderer::SetRenderStates(m_states);
NzRenderer::SetTexture(0, m_workTextures[secondWorkTexture]);
NzRenderer::SetTexture(1, m_GBuffer[2]);
NzRenderer::SetTextureSampler(0, m_pointSampler);
NzRenderer::SetTextureSampler(1, m_pointSampler);
NzRenderer::DrawFullscreenQuad();
return true;
}

View File

@@ -11,32 +11,35 @@
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredForwardPass::NzDeferredForwardPass() = default;
NzDeferredForwardPass::~NzDeferredForwardPass() = default;
void NzDeferredForwardPass::Initialize(NzDeferredRenderTechnique* technique)
namespace Nz
{
NzDeferredRenderPass::Initialize(technique);
DeferredForwardPass::DeferredForwardPass() = default;
DeferredForwardPass::~DeferredForwardPass() = default;
m_forwardTechnique = technique->GetForwardTechnique();
}
bool NzDeferredForwardPass::Process(const NzSceneData& sceneData, unsigned int workTexture, unsigned sceneTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(workTexture);
m_workRTT->SetColorTarget(sceneTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
if (sceneData.background)
sceneData.background->Draw(sceneData.viewer);
NzRenderer::SetMatrix(nzMatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
NzRenderer::SetMatrix(nzMatrixType_View, sceneData.viewer->GetViewMatrix());
m_forwardTechnique->Draw(sceneData);
return false;
void DeferredForwardPass::Initialize(DeferredRenderTechnique* technique)
{
DeferredRenderPass::Initialize(technique);
m_forwardTechnique = technique->GetForwardTechnique();
}
bool DeferredForwardPass::Process(const SceneData& sceneData, unsigned int workTexture, unsigned sceneTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(workTexture);
m_workRTT->SetColorTarget(sceneTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
if (sceneData.background)
sceneData.background->Draw(sceneData.viewer);
Renderer::SetMatrix(MatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, sceneData.viewer->GetViewMatrix());
m_forwardTechnique->Draw(sceneData);
return false;
}
}

View File

@@ -16,244 +16,247 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredGeometryPass::NzDeferredGeometryPass()
namespace Nz
{
m_clearShader = NzShaderLibrary::Get("DeferredGBufferClear");
m_clearStates.parameters[nzRendererParameter_DepthBuffer] = true;
m_clearStates.parameters[nzRendererParameter_FaceCulling] = true;
m_clearStates.parameters[nzRendererParameter_StencilTest] = true;
m_clearStates.depthFunc = nzRendererComparison_Always;
m_clearStates.frontFace.stencilCompare = nzRendererComparison_Always;
m_clearStates.frontFace.stencilPass = nzStencilOperation_Zero;
}
NzDeferredGeometryPass::~NzDeferredGeometryPass() = default;
bool NzDeferredGeometryPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
NazaraUnused(secondWorkTexture);
bool instancingEnabled = m_deferredTechnique->IsInstancingEnabled();
m_GBufferRTT->SetColorTargets({0, 1, 2}); // G-Buffer
NzRenderer::SetTarget(m_GBufferRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetRenderStates(m_clearStates);
NzRenderer::SetShader(m_clearShader);
NzRenderer::DrawFullscreenQuad();
NzRenderer::SetMatrix(nzMatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
NzRenderer::SetMatrix(nzMatrixType_View, sceneData.viewer->GetViewMatrix());
const NzShader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
for (auto& matIt : m_renderQueue->opaqueModels)
DeferredGeometryPass::DeferredGeometryPass()
{
auto& matEntry = matIt.second;
m_clearShader = ShaderLibrary::Get("DeferredGBufferClear");
m_clearStates.parameters[RendererParameter_DepthBuffer] = true;
m_clearStates.parameters[RendererParameter_FaceCulling] = true;
m_clearStates.parameters[RendererParameter_StencilTest] = true;
m_clearStates.depthFunc = RendererComparison_Always;
m_clearStates.frontFace.stencilCompare = RendererComparison_Always;
m_clearStates.frontFace.stencilPass = StencilOperation_Zero;
}
if (matEntry.enabled)
DeferredGeometryPass::~DeferredGeometryPass() = default;
bool DeferredGeometryPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(firstWorkTexture);
NazaraUnused(secondWorkTexture);
bool instancingEnabled = m_deferredTechnique->IsInstancingEnabled();
m_GBufferRTT->SetColorTargets({0, 1, 2}); // G-Buffer
Renderer::SetTarget(m_GBufferRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetRenderStates(m_clearStates);
Renderer::SetShader(m_clearShader);
Renderer::DrawFullscreenQuad();
Renderer::SetMatrix(MatrixType_Projection, sceneData.viewer->GetProjectionMatrix());
Renderer::SetMatrix(MatrixType_View, sceneData.viewer->GetViewMatrix());
const Shader* lastShader = nullptr;
const ShaderUniforms* shaderUniforms = nullptr;
for (auto& matIt : m_renderQueue->opaqueModels)
{
NzDeferredRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
auto& matEntry = matIt.second;
if (!meshInstances.empty())
if (matEntry.enabled)
{
const NzMaterial* material = matIt.first;
DeferredRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;
bool useInstancing = instancingEnabled && matEntry.instancingEnabled;
// On commence par récupérer le programme du matériau
nzUInt32 flags = nzShaderFlags_Deferred;
if (useInstancing)
flags |= nzShaderFlags_Instancing;
const NzShader* shader = material->Apply(flags);
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
if (shader != lastShader)
if (!meshInstances.empty())
{
// Index des uniformes dans le shader
shaderUniforms = GetShaderUniforms(shader);
const Material* material = matIt.first;
// Couleur ambiante de la scène
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position de la caméra
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
bool useInstancing = instancingEnabled && matEntry.instancingEnabled;
lastShader = shader;
}
// On commence par récupérer le programme du matériau
UInt32 flags = ShaderFlags_Deferred;
if (useInstancing)
flags |= ShaderFlags_Instancing;
// Meshes
for (auto& meshIt : meshInstances)
{
const NzMeshData& meshData = meshIt.first;
auto& meshEntry = meshIt.second;
const Shader* shader = material->Apply(flags);
std::vector<NzMatrix4f>& instances = meshEntry.instances;
if (!instances.empty())
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
if (shader != lastShader)
{
const NzIndexBuffer* indexBuffer = meshData.indexBuffer;
const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer;
// Index des uniformes dans le shader
shaderUniforms = GetShaderUniforms(shader);
// Gestion du draw call avant la boucle de rendu
NzRenderer::DrawCall drawFunc;
NzRenderer::DrawCallInstanced instancedDrawFunc;
unsigned int indexCount;
// Couleur ambiante de la scène
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position de la caméra
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
if (indexBuffer)
lastShader = shader;
}
// Meshes
for (auto& meshIt : meshInstances)
{
const MeshData& meshData = meshIt.first;
auto& meshEntry = meshIt.second;
std::vector<Matrix4f>& instances = meshEntry.instances;
if (!instances.empty())
{
drawFunc = NzRenderer::DrawIndexedPrimitives;
instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced;
indexCount = indexBuffer->GetIndexCount();
}
else
{
drawFunc = NzRenderer::DrawPrimitives;
instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced;
indexCount = vertexBuffer->GetVertexCount();
}
const IndexBuffer* indexBuffer = meshData.indexBuffer;
const VertexBuffer* vertexBuffer = meshData.vertexBuffer;
NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(vertexBuffer);
// Gestion du draw call avant la boucle de rendu
Renderer::DrawCall drawFunc;
Renderer::DrawCallInstanced instancedDrawFunc;
unsigned int indexCount;
if (useInstancing)
{
// On récupère le buffer d'instancing du Renderer et on le configure pour fonctionner avec des matrices
NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4));
const NzMatrix4f* instanceMatrices = &instances[0];
unsigned int instanceCount = instances.size();
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre de matrices que peut contenir le buffer
while (instanceCount > 0)
if (indexBuffer)
{
// On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing)
unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
instanceCount -= renderedInstanceCount;
// On remplit l'instancing buffer avec nos matrices world
instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true);
instanceMatrices += renderedInstanceCount;
// Et on affiche
instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount);
drawFunc = Renderer::DrawIndexedPrimitives;
instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced;
indexCount = indexBuffer->GetIndexCount();
}
}
else
{
// Sans instancing, on doit effectuer un draw call pour chaque instance
// Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances
// À cause du temps de modification du buffer d'instancing
for (const NzMatrix4f& matrix : instances)
else
{
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
drawFunc(meshData.primitiveMode, 0, indexCount);
drawFunc = Renderer::DrawPrimitives;
instancedDrawFunc = Renderer::DrawPrimitivesInstanced;
indexCount = vertexBuffer->GetVertexCount();
}
}
instances.clear();
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(vertexBuffer);
if (useInstancing)
{
// On récupère le buffer d'instancing du Renderer et on le configure pour fonctionner avec des matrices
VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer();
instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4));
const Matrix4f* instanceMatrices = &instances[0];
unsigned int instanceCount = instances.size();
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre de matrices que peut contenir le buffer
while (instanceCount > 0)
{
// On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing)
unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
instanceCount -= renderedInstanceCount;
// On remplit l'instancing buffer avec nos matrices world
instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true);
instanceMatrices += renderedInstanceCount;
// Et on affiche
instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount);
}
}
else
{
// Sans instancing, on doit effectuer un draw call pour chaque instance
// Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances
// À cause du temps de modification du buffer d'instancing
for (const Matrix4f& matrix : instances)
{
Renderer::SetMatrix(MatrixType_World, matrix);
drawFunc(meshData.primitiveMode, 0, indexCount);
}
}
instances.clear();
}
}
}
// Et on remet à zéro les données
matEntry.enabled = false;
matEntry.instancingEnabled = false;
}
}
return false; // On ne fait que remplir le G-Buffer, les work texture ne sont pas affectées
}
bool DeferredGeometryPass::Resize(const Vector2ui& dimensions)
{
DeferredRenderPass::Resize(dimensions);
/*
G-Buffer:
Texture0: Diffuse Color + Flags
Texture1: Encoded normal
Texture2: Specular value + Shininess
Texture3: N/A
*/
try
{
ErrorFlags errFlags(ErrorFlag_ThrowException);
unsigned int width = dimensions.x;
unsigned int height = dimensions.y;
m_depthStencilBuffer->Create(PixelFormatType_Depth24Stencil8, width, height);
m_GBuffer[0]->Create(ImageType_2D, PixelFormatType_RGBA8, width, height); // Texture 0 : Diffuse Color + Specular
m_GBuffer[1]->Create(ImageType_2D, PixelFormatType_RG16F, width, height); // Texture 1 : Encoded normal
m_GBuffer[2]->Create(ImageType_2D, PixelFormatType_RGBA8, width, height); // Texture 2 : Depth (24bits) + Shininess
m_GBufferRTT->Create(true);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 0, m_GBuffer[0]);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 1, m_GBuffer[1]);
m_GBufferRTT->AttachTexture(AttachmentPoint_Color, 2, m_GBuffer[2]);
// Texture 3 : Emission map ?
m_GBufferRTT->AttachBuffer(AttachmentPoint_DepthStencil, 0, m_depthStencilBuffer);
m_GBufferRTT->Unlock();
m_workRTT->Create(true);
for (unsigned int i = 0; i < 2; ++i)
{
m_workTextures[i]->Create(ImageType_2D, PixelFormatType_RGBA8, width, height);
m_workRTT->AttachTexture(AttachmentPoint_Color, i, m_workTextures[i]);
}
// Et on remet à zéro les données
matEntry.enabled = false;
matEntry.instancingEnabled = false;
m_workRTT->AttachBuffer(AttachmentPoint_DepthStencil, 0, m_depthStencilBuffer);
m_workRTT->Unlock();
if (!m_workRTT->IsComplete() || !m_GBufferRTT->IsComplete())
{
NazaraError("Incomplete RTT");
return false;
}
return true;
}
}
return false; // On ne fait que remplir le G-Buffer, les work texture ne sont pas affectées
}
bool NzDeferredGeometryPass::Resize(const NzVector2ui& dimensions)
{
NzDeferredRenderPass::Resize(dimensions);
/*
G-Buffer:
Texture0: Diffuse Color + Flags
Texture1: Encoded normal
Texture2: Specular value + Shininess
Texture3: N/A
*/
try
{
NzErrorFlags errFlags(nzErrorFlag_ThrowException);
unsigned int width = dimensions.x;
unsigned int height = dimensions.y;
m_depthStencilBuffer->Create(nzPixelFormat_Depth24Stencil8, width, height);
m_GBuffer[0]->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); // Texture 0 : Diffuse Color + Specular
m_GBuffer[1]->Create(nzImageType_2D, nzPixelFormat_RG16F, width, height); // Texture 1 : Encoded normal
m_GBuffer[2]->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height); // Texture 2 : Depth (24bits) + Shininess
m_GBufferRTT->Create(true);
m_GBufferRTT->AttachTexture(nzAttachmentPoint_Color, 0, m_GBuffer[0]);
m_GBufferRTT->AttachTexture(nzAttachmentPoint_Color, 1, m_GBuffer[1]);
m_GBufferRTT->AttachTexture(nzAttachmentPoint_Color, 2, m_GBuffer[2]);
// Texture 3 : Emission map ?
m_GBufferRTT->AttachBuffer(nzAttachmentPoint_DepthStencil, 0, m_depthStencilBuffer);
m_GBufferRTT->Unlock();
m_workRTT->Create(true);
for (unsigned int i = 0; i < 2; ++i)
catch (const std::exception& e)
{
m_workTextures[i]->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height);
m_workRTT->AttachTexture(nzAttachmentPoint_Color, i, m_workTextures[i]);
}
m_workRTT->AttachBuffer(nzAttachmentPoint_DepthStencil, 0, m_depthStencilBuffer);
m_workRTT->Unlock();
if (!m_workRTT->IsComplete() || !m_GBufferRTT->IsComplete())
{
NazaraError("Incomplete RTT");
NazaraError("Failed to create G-Buffer RTT: " + String(e.what()));
return false;
}
return true;
}
catch (const std::exception& e)
const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const
{
NazaraError("Failed to create G-Buffer RTT: " + NzString(e.what()));
return false;
}
}
auto it = m_shaderUniforms.find(shader);
if (it == m_shaderUniforms.end())
{
ShaderUniforms uniforms;
uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &DeferredGeometryPass::OnShaderInvalidated);
uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &DeferredGeometryPass::OnShaderInvalidated);
const NzDeferredGeometryPass::ShaderUniforms* NzDeferredGeometryPass::GetShaderUniforms(const NzShader* shader) const
{
auto it = m_shaderUniforms.find(shader);
if (it == m_shaderUniforms.end())
uniforms.eyePosition = shader->GetUniformLocation("EyePosition");
uniforms.sceneAmbient = shader->GetUniformLocation("SceneAmbient");
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first;
}
return &it->second;
}
void DeferredGeometryPass::OnShaderInvalidated(const Shader* shader) const
{
ShaderUniforms uniforms;
uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &NzDeferredGeometryPass::OnShaderInvalidated);
uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &NzDeferredGeometryPass::OnShaderInvalidated);
uniforms.eyePosition = shader->GetUniformLocation("EyePosition");
uniforms.sceneAmbient = shader->GetUniformLocation("SceneAmbient");
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first;
m_shaderUniforms.erase(shader);
}
return &it->second;
}
void NzDeferredGeometryPass::OnShaderInvalidated(const NzShader* shader) const
{
m_shaderUniforms.erase(shader);
}

View File

@@ -11,280 +11,283 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredPhongLightingPass::NzDeferredPhongLightingPass() :
m_lightMeshesDrawing(false)
namespace Nz
{
m_directionalLightShader = NzShaderLibrary::Get("DeferredDirectionnalLight");
m_directionalLightShaderEyePositionLocation = m_directionalLightShader->GetUniformLocation("EyePosition");
m_directionalLightShaderSceneAmbientLocation = m_directionalLightShader->GetUniformLocation("SceneAmbient");
m_directionalLightUniforms.ubo = false;
m_directionalLightUniforms.locations.type = -1; // Type déjà connu
m_directionalLightUniforms.locations.color = m_directionalLightShader->GetUniformLocation("LightColor");
m_directionalLightUniforms.locations.factors = m_directionalLightShader->GetUniformLocation("LightFactors");
m_directionalLightUniforms.locations.parameters1 = m_directionalLightShader->GetUniformLocation("LightDirection");
m_directionalLightUniforms.locations.parameters2 = -1;
m_directionalLightUniforms.locations.parameters3 = -1;
m_pointSpotLightShader = NzShaderLibrary::Get("DeferredPointSpotLight");
m_pointSpotLightShaderDiscardLocation = m_pointSpotLightShader->GetUniformLocation("Discard");
m_pointSpotLightShaderEyePositionLocation = m_pointSpotLightShader->GetUniformLocation("EyePosition");
m_pointSpotLightShaderSceneAmbientLocation = m_pointSpotLightShader->GetUniformLocation("SceneAmbient");
m_pointSpotLightUniforms.ubo = false;
m_pointSpotLightUniforms.locations.type = m_pointSpotLightShader->GetUniformLocation("LightType");
m_pointSpotLightUniforms.locations.color = m_pointSpotLightShader->GetUniformLocation("LightColor");
m_pointSpotLightUniforms.locations.factors = m_pointSpotLightShader->GetUniformLocation("LightFactors");
m_pointSpotLightUniforms.locations.parameters1 = m_pointSpotLightShader->GetUniformLocation("LightParameters1");
m_pointSpotLightUniforms.locations.parameters2 = m_pointSpotLightShader->GetUniformLocation("LightParameters2");
m_pointSpotLightUniforms.locations.parameters3 = m_pointSpotLightShader->GetUniformLocation("LightParameters3");
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(nzSamplerFilter_Nearest);
m_pointSampler.SetWrapMode(nzSamplerWrap_Clamp);
m_cone = NzMesh::New();
m_cone->CreateStatic();
m_coneMesh = static_cast<NzStaticMesh*>(m_cone->BuildSubMesh(NzPrimitive::Cone(1.f, 1.f, 16, NzMatrix4f::Rotate(NzEulerAnglesf(90.f, 0.f, 0.f)))));
m_sphere = NzMesh::New();
m_sphere->CreateStatic();
m_sphereMesh = static_cast<NzStaticMesh*>(m_sphere->BuildSubMesh(NzPrimitive::IcoSphere(1.f, 1)));
}
NzDeferredPhongLightingPass::~NzDeferredPhongLightingPass() = default;
void NzDeferredPhongLightingPass::EnableLightMeshesDrawing(bool enable)
{
m_lightMeshesDrawing = enable;
}
bool NzDeferredPhongLightingPass::IsLightMeshesDrawingEnabled() const
{
return m_lightMeshesDrawing;
}
bool NzDeferredPhongLightingPass::Process(const NzSceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(secondWorkTexture);
m_workRTT->SetColorTarget(firstWorkTexture);
NzRenderer::SetTarget(m_workRTT);
NzRenderer::SetViewport(NzRecti(0, 0, m_dimensions.x, m_dimensions.y));
NzRenderer::SetTexture(0, m_GBuffer[0]);
NzRenderer::SetTextureSampler(0, m_pointSampler);
NzRenderer::SetTexture(1, m_GBuffer[1]);
NzRenderer::SetTextureSampler(1, m_pointSampler);
NzRenderer::SetTexture(2, m_GBuffer[2]);
NzRenderer::SetTextureSampler(2, m_pointSampler);
NzRenderer::SetClearColor(NzColor::Black);
NzRenderer::Clear(nzRendererBuffer_Color);
NzRenderStates lightStates;
lightStates.dstBlend = nzBlendFunc_One;
lightStates.srcBlend = nzBlendFunc_One;
lightStates.parameters[nzRendererParameter_Blend] = true;
lightStates.parameters[nzRendererParameter_DepthBuffer] = false;
lightStates.parameters[nzRendererParameter_DepthWrite] = false;
// Directional lights
if (!m_renderQueue->directionalLights.empty())
DeferredPhongLightingPass::DeferredPhongLightingPass() :
m_lightMeshesDrawing(false)
{
NzRenderer::SetRenderStates(lightStates);
NzRenderer::SetShader(m_directionalLightShader);
m_directionalLightShader->SendColor(m_directionalLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_directionalLightShader->SendVector(m_directionalLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
m_directionalLightShader = ShaderLibrary::Get("DeferredDirectionnalLight");
m_directionalLightShaderEyePositionLocation = m_directionalLightShader->GetUniformLocation("EyePosition");
m_directionalLightShaderSceneAmbientLocation = m_directionalLightShader->GetUniformLocation("SceneAmbient");
for (auto& light : m_renderQueue->directionalLights)
{
m_directionalLightShader->SendColor(m_directionalLightUniforms.locations.color, light.color);
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.factors, NzVector2f(light.ambientFactor, light.diffuseFactor));
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.parameters1, NzVector4f(light.direction));
m_directionalLightUniforms.ubo = false;
m_directionalLightUniforms.locations.type = -1; // Type déjà connu
m_directionalLightUniforms.locations.color = m_directionalLightShader->GetUniformLocation("LightColor");
m_directionalLightUniforms.locations.factors = m_directionalLightShader->GetUniformLocation("LightFactors");
m_directionalLightUniforms.locations.parameters1 = m_directionalLightShader->GetUniformLocation("LightDirection");
m_directionalLightUniforms.locations.parameters2 = -1;
m_directionalLightUniforms.locations.parameters3 = -1;
NzRenderer::DrawFullscreenQuad();
}
m_pointSpotLightShader = ShaderLibrary::Get("DeferredPointSpotLight");
m_pointSpotLightShaderDiscardLocation = m_pointSpotLightShader->GetUniformLocation("Discard");
m_pointSpotLightShaderEyePositionLocation = m_pointSpotLightShader->GetUniformLocation("EyePosition");
m_pointSpotLightShaderSceneAmbientLocation = m_pointSpotLightShader->GetUniformLocation("SceneAmbient");
m_pointSpotLightUniforms.ubo = false;
m_pointSpotLightUniforms.locations.type = m_pointSpotLightShader->GetUniformLocation("LightType");
m_pointSpotLightUniforms.locations.color = m_pointSpotLightShader->GetUniformLocation("LightColor");
m_pointSpotLightUniforms.locations.factors = m_pointSpotLightShader->GetUniformLocation("LightFactors");
m_pointSpotLightUniforms.locations.parameters1 = m_pointSpotLightShader->GetUniformLocation("LightParameters1");
m_pointSpotLightUniforms.locations.parameters2 = m_pointSpotLightShader->GetUniformLocation("LightParameters2");
m_pointSpotLightUniforms.locations.parameters3 = m_pointSpotLightShader->GetUniformLocation("LightParameters3");
m_pointSampler.SetAnisotropyLevel(1);
m_pointSampler.SetFilterMode(SamplerFilter_Nearest);
m_pointSampler.SetWrapMode(SamplerWrap_Clamp);
m_cone = Mesh::New();
m_cone->CreateStatic();
m_coneMesh = static_cast<StaticMesh*>(m_cone->BuildSubMesh(Primitive::Cone(1.f, 1.f, 16, Matrix4f::Rotate(EulerAnglesf(90.f, 0.f, 0.f)))));
m_sphere = Mesh::New();
m_sphere->CreateStatic();
m_sphereMesh = static_cast<StaticMesh*>(m_sphere->BuildSubMesh(Primitive::IcoSphere(1.f, 1)));
}
// Point lights/Spot lights
if (!m_renderQueue->pointLights.empty() || !m_renderQueue->spotLights.empty())
DeferredPhongLightingPass::~DeferredPhongLightingPass() = default;
void DeferredPhongLightingPass::EnableLightMeshesDrawing(bool enable)
{
// http://www.altdevblogaday.com/2011/08/08/stencil-buffer-optimisation-for-deferred-lights/
lightStates.parameters[nzRendererParameter_StencilTest] = true;
lightStates.faceCulling = nzFaceSide_Front;
lightStates.backFace.stencilMask = 0xFF;
lightStates.backFace.stencilReference = 0;
lightStates.backFace.stencilFail = nzStencilOperation_Keep;
lightStates.backFace.stencilPass = nzStencilOperation_Keep;
lightStates.backFace.stencilZFail = nzStencilOperation_Invert;
lightStates.frontFace.stencilMask = 0xFF;
lightStates.frontFace.stencilReference = 0;
lightStates.frontFace.stencilFail = nzStencilOperation_Keep;
lightStates.frontFace.stencilPass = nzStencilOperation_Keep;
lightStates.frontFace.stencilZFail = nzStencilOperation_Invert;
m_lightMeshesDrawing = enable;
}
NzRenderer::SetRenderStates(lightStates);
bool DeferredPhongLightingPass::IsLightMeshesDrawingEnabled() const
{
return m_lightMeshesDrawing;
}
NzRenderer::SetShader(m_pointSpotLightShader);
m_pointSpotLightShader->SendColor(m_pointSpotLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_pointSpotLightShader->SendVector(m_pointSpotLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
bool DeferredPhongLightingPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
NazaraUnused(secondWorkTexture);
NzMatrix4f lightMatrix;
lightMatrix.MakeIdentity();
if (!m_renderQueue->pointLights.empty())
m_workRTT->SetColorTarget(firstWorkTexture);
Renderer::SetTarget(m_workRTT);
Renderer::SetViewport(Recti(0, 0, m_dimensions.x, m_dimensions.y));
Renderer::SetTexture(0, m_GBuffer[0]);
Renderer::SetTextureSampler(0, m_pointSampler);
Renderer::SetTexture(1, m_GBuffer[1]);
Renderer::SetTextureSampler(1, m_pointSampler);
Renderer::SetTexture(2, m_GBuffer[2]);
Renderer::SetTextureSampler(2, m_pointSampler);
Renderer::SetClearColor(Color::Black);
Renderer::Clear(RendererBuffer_Color);
RenderStates lightStates;
lightStates.dstBlend = BlendFunc_One;
lightStates.srcBlend = BlendFunc_One;
lightStates.parameters[RendererParameter_Blend] = true;
lightStates.parameters[RendererParameter_DepthBuffer] = false;
lightStates.parameters[RendererParameter_DepthWrite] = false;
// Directional lights
if (!m_renderQueue->directionalLights.empty())
{
const NzIndexBuffer* indexBuffer = m_sphereMesh->GetIndexBuffer();
NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(m_sphereMesh->GetVertexBuffer());
Renderer::SetRenderStates(lightStates);
Renderer::SetShader(m_directionalLightShader);
m_directionalLightShader->SendColor(m_directionalLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_directionalLightShader->SendVector(m_directionalLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, nzLightType_Point);
for (const auto& light : m_renderQueue->pointLights)
for (auto& light : m_renderQueue->directionalLights)
{
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, NzVector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, NzVector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, NzVector4f(0.f, 0.f, 0.f, light.invRadius));
m_directionalLightShader->SendColor(m_directionalLightUniforms.locations.color, light.color);
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_directionalLightShader->SendVector(m_directionalLightUniforms.locations.parameters1, Vector4f(light.direction));
lightMatrix.SetScale(NzVector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère
lightMatrix.SetTranslation(light.position);
NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix);
// Rendu de la sphère dans le stencil buffer
NzRenderer::Enable(nzRendererParameter_ColorWrite, false);
NzRenderer::Enable(nzRendererParameter_DepthBuffer, true);
NzRenderer::Enable(nzRendererParameter_FaceCulling, false);
NzRenderer::SetStencilCompareFunction(nzRendererComparison_Always);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Rendu de la sphère comme zone d'effet
NzRenderer::Enable(nzRendererParameter_ColorWrite, true);
NzRenderer::Enable(nzRendererParameter_DepthBuffer, false);
NzRenderer::Enable(nzRendererParameter_FaceCulling, true);
NzRenderer::SetStencilCompareFunction(nzRendererComparison_NotEqual, nzFaceSide_Back);
NzRenderer::SetStencilPassOperation(nzStencilOperation_Zero, nzFaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
Renderer::DrawFullscreenQuad();
}
}
if (m_lightMeshesDrawing)
// Point lights/Spot lights
if (!m_renderQueue->pointLights.empty() || !m_renderQueue->spotLights.empty())
{
// http://www.altdevblogaday.com/2011/08/08/stencil-buffer-optimisation-for-deferred-lights/
lightStates.parameters[RendererParameter_StencilTest] = true;
lightStates.faceCulling = FaceSide_Front;
lightStates.backFace.stencilMask = 0xFF;
lightStates.backFace.stencilReference = 0;
lightStates.backFace.stencilFail = StencilOperation_Keep;
lightStates.backFace.stencilPass = StencilOperation_Keep;
lightStates.backFace.stencilZFail = StencilOperation_Invert;
lightStates.frontFace.stencilMask = 0xFF;
lightStates.frontFace.stencilReference = 0;
lightStates.frontFace.stencilFail = StencilOperation_Keep;
lightStates.frontFace.stencilPass = StencilOperation_Keep;
lightStates.frontFace.stencilZFail = StencilOperation_Invert;
Renderer::SetRenderStates(lightStates);
Renderer::SetShader(m_pointSpotLightShader);
m_pointSpotLightShader->SendColor(m_pointSpotLightShaderSceneAmbientLocation, sceneData.ambientColor);
m_pointSpotLightShader->SendVector(m_pointSpotLightShaderEyePositionLocation, sceneData.viewer->GetEyePosition());
Matrix4f lightMatrix;
lightMatrix.MakeIdentity();
if (!m_renderQueue->pointLights.empty())
{
NzRenderer::Enable(nzRendererParameter_DepthBuffer, true);
NzRenderer::Enable(nzRendererParameter_DepthWrite, true);
NzRenderer::Enable(nzRendererParameter_FaceCulling, false);
NzRenderer::Enable(nzRendererParameter_StencilTest, false);
NzRenderer::SetFaceFilling(nzFaceFilling_Line);
const IndexBuffer* indexBuffer = m_sphereMesh->GetIndexBuffer();
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(m_sphereMesh->GetVertexBuffer());
const NzShader* shader = NzShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
NzRenderer::SetShader(shader);
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, LightType_Point);
for (const auto& light : m_renderQueue->pointLights)
{
lightMatrix.SetScale(NzVector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, Vector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, Vector4f(0.f, 0.f, 0.f, light.invRadius));
lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère
lightMatrix.SetTranslation(light.position);
NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix);
Renderer::SetMatrix(MatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
// Rendu de la sphère dans le stencil buffer
Renderer::Enable(RendererParameter_ColorWrite, false);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::SetStencilCompareFunction(RendererComparison_Always);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Rendu de la sphère comme zone d'effet
Renderer::Enable(RendererParameter_ColorWrite, true);
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::SetStencilCompareFunction(RendererComparison_NotEqual, FaceSide_Back);
Renderer::SetStencilPassOperation(StencilOperation_Zero, FaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
NzRenderer::Enable(nzRendererParameter_DepthBuffer, false);
NzRenderer::Enable(nzRendererParameter_DepthWrite, false);
NzRenderer::Enable(nzRendererParameter_FaceCulling, true);
NzRenderer::Enable(nzRendererParameter_StencilTest, true);
NzRenderer::SetFaceFilling(nzFaceFilling_Fill);
}
}
if (m_lightMeshesDrawing)
{
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::Enable(RendererParameter_StencilTest, false);
Renderer::SetFaceFilling(FaceFilling_Line);
if (!m_renderQueue->spotLights.empty())
{
const NzIndexBuffer* indexBuffer = m_coneMesh->GetIndexBuffer();
NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(m_coneMesh->GetVertexBuffer());
const Shader* shader = ShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, nzLightType_Spot);
for (const auto& light : m_renderQueue->spotLights)
{
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, NzVector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, NzVector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, NzVector4f(light.direction, light.invRadius));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters3, NzVector2f(light.innerAngleCosine, light.outerAngleCosine));
Renderer::SetShader(shader);
for (const auto& light : m_renderQueue->pointLights)
{
lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère
lightMatrix.SetTranslation(light.position);
float baseRadius = light.radius * light.outerAngleTangent * 1.1f;
lightMatrix.MakeTransform(light.position, NzQuaternionf::RotationBetween(NzVector3f::Forward(), light.direction), NzVector3f(baseRadius, baseRadius, light.radius));
Renderer::SetMatrix(MatrixType_World, lightMatrix);
NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
// Rendu de la sphère dans le stencil buffer
NzRenderer::Enable(nzRendererParameter_ColorWrite, false);
NzRenderer::Enable(nzRendererParameter_DepthBuffer, true);
NzRenderer::Enable(nzRendererParameter_FaceCulling, false);
NzRenderer::SetStencilCompareFunction(nzRendererComparison_Always);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Rendu de la sphère comme zone d'effet
NzRenderer::Enable(nzRendererParameter_ColorWrite, true);
NzRenderer::Enable(nzRendererParameter_DepthBuffer, false);
NzRenderer::Enable(nzRendererParameter_FaceCulling, true);
NzRenderer::SetFaceCulling(nzFaceSide_Front);
NzRenderer::SetStencilCompareFunction(nzRendererComparison_NotEqual, nzFaceSide_Back);
NzRenderer::SetStencilPassOperation(nzStencilOperation_Zero, nzFaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_DepthWrite, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::Enable(RendererParameter_StencilTest, true);
Renderer::SetFaceFilling(FaceFilling_Fill);
}
}
if (m_lightMeshesDrawing)
if (!m_renderQueue->spotLights.empty())
{
NzRenderer::Enable(nzRendererParameter_DepthBuffer, true);
NzRenderer::Enable(nzRendererParameter_DepthWrite, true);
NzRenderer::Enable(nzRendererParameter_FaceCulling, false);
NzRenderer::Enable(nzRendererParameter_StencilTest, false);
NzRenderer::SetFaceFilling(nzFaceFilling_Line);
const IndexBuffer* indexBuffer = m_coneMesh->GetIndexBuffer();
Renderer::SetIndexBuffer(indexBuffer);
Renderer::SetVertexBuffer(m_coneMesh->GetVertexBuffer());
const NzShader* shader = NzShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
NzRenderer::SetShader(shader);
m_pointSpotLightShader->SendInteger(m_pointSpotLightUniforms.locations.type, LightType_Spot);
for (const auto& light : m_renderQueue->spotLights)
{
m_pointSpotLightShader->SendColor(m_pointSpotLightUniforms.locations.color, light.color);
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.factors, Vector2f(light.ambientFactor, light.diffuseFactor));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, Vector4f(light.position, light.attenuation));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, Vector4f(light.direction, light.invRadius));
m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters3, Vector2f(light.innerAngleCosine, light.outerAngleCosine));
float baseRadius = light.radius * light.outerAngleTangent * 1.1f;
lightMatrix.MakeTransform(light.position, NzQuaternionf::RotationBetween(NzVector3f::Forward(), light.direction), NzVector3f(baseRadius, baseRadius, light.radius));
lightMatrix.MakeTransform(light.position, Quaternionf::RotationBetween(Vector3f::Forward(), light.direction), Vector3f(baseRadius, baseRadius, light.radius));
NzRenderer::SetMatrix(nzMatrixType_World, lightMatrix);
Renderer::SetMatrix(MatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
// Rendu de la sphère dans le stencil buffer
Renderer::Enable(RendererParameter_ColorWrite, false);
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::SetStencilCompareFunction(RendererComparison_Always);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, true);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
// Rendu de la sphère comme zone d'effet
Renderer::Enable(RendererParameter_ColorWrite, true);
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::SetFaceCulling(FaceSide_Front);
Renderer::SetStencilCompareFunction(RendererComparison_NotEqual, FaceSide_Back);
Renderer::SetStencilPassOperation(StencilOperation_Zero, FaceSide_Back);
m_pointSpotLightShader->SendBoolean(m_pointSpotLightShaderDiscardLocation, false);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
NzRenderer::Enable(nzRendererParameter_DepthBuffer, false);
NzRenderer::Enable(nzRendererParameter_DepthWrite, false);
NzRenderer::Enable(nzRendererParameter_FaceCulling, true);
NzRenderer::Enable(nzRendererParameter_StencilTest, true);
NzRenderer::SetFaceFilling(nzFaceFilling_Fill);
if (m_lightMeshesDrawing)
{
Renderer::Enable(RendererParameter_DepthBuffer, true);
Renderer::Enable(RendererParameter_DepthWrite, true);
Renderer::Enable(RendererParameter_FaceCulling, false);
Renderer::Enable(RendererParameter_StencilTest, false);
Renderer::SetFaceFilling(FaceFilling_Line);
const Shader* shader = ShaderLibrary::Get("DebugSimple");
static int colorLocation = shader->GetUniformLocation("Color");
Renderer::SetShader(shader);
for (const auto& light : m_renderQueue->spotLights)
{
float baseRadius = light.radius * light.outerAngleTangent * 1.1f;
lightMatrix.MakeTransform(light.position, Quaternionf::RotationBetween(Vector3f::Forward(), light.direction), Vector3f(baseRadius, baseRadius, light.radius));
Renderer::SetMatrix(MatrixType_World, lightMatrix);
shader->SendColor(colorLocation, light.color);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount());
}
Renderer::Enable(RendererParameter_DepthBuffer, false);
Renderer::Enable(RendererParameter_DepthWrite, false);
Renderer::Enable(RendererParameter_FaceCulling, true);
Renderer::Enable(RendererParameter_StencilTest, true);
Renderer::SetFaceFilling(FaceFilling_Fill);
}
}
Renderer::Enable(RendererParameter_StencilTest, false);
}
NzRenderer::Enable(nzRendererParameter_StencilTest, false);
return true;
}
return true;
}

View File

@@ -7,42 +7,45 @@
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzDeferredRenderPass::NzDeferredRenderPass() :
m_enabled(true)
namespace Nz
{
}
NzDeferredRenderPass::~NzDeferredRenderPass() = default;
void NzDeferredRenderPass::Enable(bool enable)
{
m_enabled = enable;
}
void NzDeferredRenderPass::Initialize(NzDeferredRenderTechnique* technique)
{
m_deferredTechnique = technique;
m_renderQueue = static_cast<NzDeferredRenderQueue*>(technique->GetRenderQueue());
m_depthStencilBuffer = technique->GetDepthStencilBuffer();
m_GBufferRTT = technique->GetGBufferRTT();
for (unsigned int i = 0; i < 3; ++i)
m_GBuffer[i] = technique->GetGBuffer(i);
m_workRTT = technique->GetWorkRTT();
for (unsigned int i = 0; i < 2; ++i)
m_workTextures[i] = technique->GetWorkTexture(i);
}
bool NzDeferredRenderPass::IsEnabled() const
{
return m_enabled;
}
bool NzDeferredRenderPass::Resize(const NzVector2ui& dimensions)
{
m_dimensions = dimensions;
return true;
DeferredRenderPass::DeferredRenderPass() :
m_enabled(true)
{
}
DeferredRenderPass::~DeferredRenderPass() = default;
void DeferredRenderPass::Enable(bool enable)
{
m_enabled = enable;
}
void DeferredRenderPass::Initialize(DeferredRenderTechnique* technique)
{
m_deferredTechnique = technique;
m_renderQueue = static_cast<DeferredRenderQueue*>(technique->GetRenderQueue());
m_depthStencilBuffer = technique->GetDepthStencilBuffer();
m_GBufferRTT = technique->GetGBufferRTT();
for (unsigned int i = 0; i < 3; ++i)
m_GBuffer[i] = technique->GetGBuffer(i);
m_workRTT = technique->GetWorkRTT();
for (unsigned int i = 0; i < 2; ++i)
m_workTextures[i] = technique->GetWorkTexture(i);
}
bool DeferredRenderPass::IsEnabled() const
{
return m_enabled;
}
bool DeferredRenderPass::Resize(const Vector2ui& dimensions)
{
m_dimensions = dimensions;
return true;
}
}

View File

@@ -10,190 +10,193 @@
///TODO: Rendre les billboards via Deferred Shading si possible
NzDeferredRenderQueue::NzDeferredRenderQueue(NzForwardRenderQueue* forwardQueue) :
m_forwardQueue(forwardQueue)
namespace Nz
{
}
void NzDeferredRenderQueue::AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos, const NzColor& color)
{
m_forwardQueue->AddBillboard(material, position, size, sinCos, color);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const NzVector2f> sizePtr, NzSparsePtr<const NzVector2f> sinCosPtr, NzSparsePtr<const NzColor> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const NzVector2f> sizePtr, NzSparsePtr<const NzVector2f> sinCosPtr, NzSparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const NzVector2f> sizePtr, NzSparsePtr<const float> anglePtr, NzSparsePtr<const NzColor> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const NzVector2f> sizePtr, NzSparsePtr<const float> anglePtr, NzSparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const float> sizePtr, NzSparsePtr<const NzVector2f> sinCosPtr, NzSparsePtr<const NzColor> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const float> sizePtr, NzSparsePtr<const NzVector2f> sinCosPtr, NzSparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const float> sizePtr, NzSparsePtr<const float> anglePtr, NzSparsePtr<const NzColor> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr);
}
void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr<const NzVector3f> positionPtr, NzSparsePtr<const float> sizePtr, NzSparsePtr<const float> anglePtr, NzSparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr);
}
void NzDeferredRenderQueue::AddDrawable(const NzDrawable* drawable)
{
m_forwardQueue->AddDrawable(drawable);
}
void NzDeferredRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix)
{
if (material->IsEnabled(nzRendererParameter_Blend))
// Un matériau transparent ? J'aime pas, va voir dans la forward queue si j'y suis
m_forwardQueue->AddMesh(material, meshData, meshAABB, transformMatrix);
else
DeferredRenderQueue::DeferredRenderQueue(ForwardRenderQueue* forwardQueue) :
m_forwardQueue(forwardQueue)
{
auto it = opaqueModels.find(material);
if (it == opaqueModels.end())
{
BatchedModelEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &NzDeferredRenderQueue::OnMaterialInvalidation);
it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first;
}
BatchedModelEntry& entry = it->second;
entry.enabled = true;
auto& meshMap = entry.meshMap;
auto it2 = meshMap.find(meshData);
if (it2 == meshMap.end())
{
MeshInstanceEntry instanceEntry;
if (meshData.indexBuffer)
instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &NzDeferredRenderQueue::OnIndexBufferInvalidation);
instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &NzDeferredRenderQueue::OnVertexBufferInvalidation);
it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first;
}
// On ajoute la matrice à la liste des instances de cet objet
std::vector<NzMatrix4f>& instances = it2->second.instances;
instances.push_back(transformMatrix);
// Avons-nous suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ?
if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT)
entry.instancingEnabled = true; // Apparemment oui, activons l'instancing avec ce matériau
}
}
void NzDeferredRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay)
{
m_forwardQueue->AddSprites(material, vertices, spriteCount, overlay);
}
void NzDeferredRenderQueue::Clear(bool fully)
{
NzAbstractRenderQueue::Clear(fully);
if (fully)
opaqueModels.clear();
m_forwardQueue->Clear(fully);
}
void NzDeferredRenderQueue::OnIndexBufferInvalidation(const NzIndexBuffer* indexBuffer)
{
for (auto& modelPair : opaqueModels)
void DeferredRenderQueue::AddBillboard(const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos, const Color& color)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
m_forwardQueue->AddBillboard(material, position, size, sinCos, color);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const Color> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const Color> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const Vector2f> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const Color> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const Vector2f> sinCosPtr, SparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const Color> colorPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr);
}
void DeferredRenderQueue::AddBillboards(const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr)
{
m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr);
}
void DeferredRenderQueue::AddDrawable(const Drawable* drawable)
{
m_forwardQueue->AddDrawable(drawable);
}
void DeferredRenderQueue::AddMesh(const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix)
{
if (material->IsEnabled(RendererParameter_Blend))
// Un matériau transparent ? J'aime pas, va voir dans la forward queue si j'y suis
m_forwardQueue->AddMesh(material, meshData, meshAABB, transformMatrix);
else
{
const NzMeshData& renderData = it->first;
if (renderData.indexBuffer == indexBuffer)
it = meshes.erase(it);
else
++it;
auto it = opaqueModels.find(material);
if (it == opaqueModels.end())
{
BatchedModelEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &DeferredRenderQueue::OnMaterialInvalidation);
it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first;
}
BatchedModelEntry& entry = it->second;
entry.enabled = true;
auto& meshMap = entry.meshMap;
auto it2 = meshMap.find(meshData);
if (it2 == meshMap.end())
{
MeshInstanceEntry instanceEntry;
if (meshData.indexBuffer)
instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &DeferredRenderQueue::OnIndexBufferInvalidation);
instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &DeferredRenderQueue::OnVertexBufferInvalidation);
it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first;
}
// On ajoute la matrice à la liste des instances de cet objet
std::vector<Matrix4f>& instances = it2->second.instances;
instances.push_back(transformMatrix);
// Avons-nous suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ?
if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT)
entry.instancingEnabled = true; // Apparemment oui, activons l'instancing avec ce matériau
}
}
}
void NzDeferredRenderQueue::OnMaterialInvalidation(const NzMaterial* material)
{
opaqueModels.erase(material);
}
void NzDeferredRenderQueue::OnVertexBufferInvalidation(const NzVertexBuffer* vertexBuffer)
{
for (auto& modelPair : opaqueModels)
void DeferredRenderQueue::AddSprites(const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
m_forwardQueue->AddSprites(material, vertices, spriteCount, overlay);
}
void DeferredRenderQueue::Clear(bool fully)
{
AbstractRenderQueue::Clear(fully);
if (fully)
opaqueModels.clear();
m_forwardQueue->Clear(fully);
}
void DeferredRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer)
{
for (auto& modelPair : opaqueModels)
{
const NzMeshData& renderData = it->first;
if (renderData.vertexBuffer == vertexBuffer)
it = meshes.erase(it);
else
++it;
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
if (renderData.indexBuffer == indexBuffer)
it = meshes.erase(it);
else
++it;
}
}
}
}
bool NzDeferredRenderQueue::BatchedModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) const
{
const NzUberShader* uberShader1 = mat1->GetShader();
const NzUberShader* uberShader2 = mat2->GetShader();
if (uberShader1 != uberShader2)
return uberShader1 < uberShader2;
const NzShader* shader1 = mat1->GetShaderInstance(nzShaderFlags_Deferred)->GetShader();
const NzShader* shader2 = mat2->GetShaderInstance(nzShaderFlags_Deferred)->GetShader();
if (shader1 != shader2)
return shader1 < shader2;
const NzTexture* diffuseMap1 = mat1->GetDiffuseMap();
const NzTexture* diffuseMap2 = mat2->GetDiffuseMap();
if (diffuseMap1 != diffuseMap2)
return diffuseMap1 < diffuseMap2;
return mat1 < mat2;
}
bool NzDeferredRenderQueue::MeshDataComparator::operator()(const NzMeshData& data1, const NzMeshData& data2) const
{
const NzBuffer* buffer1;
const NzBuffer* 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;
void DeferredRenderQueue::OnMaterialInvalidation(const Material* material)
{
opaqueModels.erase(material);
}
void DeferredRenderQueue::OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer)
{
for (auto& modelPair : opaqueModels)
{
MeshInstanceContainer& meshes = modelPair.second.meshMap;
for (auto it = meshes.begin(); it != meshes.end();)
{
const MeshData& renderData = it->first;
if (renderData.vertexBuffer == vertexBuffer)
it = meshes.erase(it);
else
++it;
}
}
}
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;
}
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,4 +5,7 @@
#include <Nazara/Graphics/Drawable.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzDrawable::~NzDrawable() = default;
namespace Nz
{
Drawable::~Drawable() = default;
}

View File

@@ -10,115 +10,122 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
nzTernary CheckStatic(NzInputStream& stream, const NzModelParameters& parameters)
namespace
{
NazaraUnused(stream);
NazaraUnused(parameters);
return nzTernary_Unknown;
}
bool LoadStatic(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
{
NazaraUnused(parameters);
NzMeshRef mesh = NzMesh::New();
if (!mesh->LoadFromStream(stream, parameters.mesh))
Ternary CheckStatic(InputStream& stream, const ModelParameters& parameters)
{
NazaraError("Failed to load model mesh");
return false;
NazaraUnused(stream);
NazaraUnused(parameters);
return Ternary_Unknown;
}
if (mesh->IsAnimable())
bool LoadStatic(Model* model, InputStream& stream, const ModelParameters& parameters)
{
NazaraError("Can't load animated mesh into static model");
return false;
}
NazaraUnused(parameters);
model->Reset();
model->SetMesh(mesh);
if (parameters.loadMaterials)
{
unsigned int matCount = model->GetMaterialCount();
for (unsigned int i = 0; i < matCount; ++i)
MeshRef mesh = Mesh::New();
if (!mesh->LoadFromStream(stream, parameters.mesh))
{
NzString mat = mesh->GetMaterial(i);
if (!mat.IsEmpty())
NazaraError("Failed to load model mesh");
return false;
}
if (mesh->IsAnimable())
{
NazaraError("Can't load animated mesh into static model");
return false;
}
model->Reset();
model->SetMesh(mesh);
if (parameters.loadMaterials)
{
unsigned int matCount = model->GetMaterialCount();
for (unsigned int i = 0; i < matCount; ++i)
{
NzMaterialRef material = NzMaterial::New();
if (material->LoadFromFile(mat, parameters.material))
model->SetMaterial(i, material);
else
NazaraWarning("Failed to load material #" + NzString::Number(i));
String mat = mesh->GetMaterial(i);
if (!mat.IsEmpty())
{
MaterialRef material = Material::New();
if (material->LoadFromFile(mat, parameters.material))
model->SetMaterial(i, material);
else
NazaraWarning("Failed to load material #" + String::Number(i));
}
}
}
return true;
}
return true;
}
nzTernary CheckAnimated(NzInputStream& stream, const NzSkeletalModelParameters& parameters)
{
NazaraUnused(stream);
NazaraUnused(parameters);
return nzTernary_Unknown;
}
bool LoadAnimated(NzSkeletalModel* model, NzInputStream& stream, const NzSkeletalModelParameters& parameters)
{
NazaraUnused(parameters);
NzMeshRef mesh = NzMesh::New();
if (!mesh->LoadFromStream(stream, parameters.mesh))
Ternary CheckAnimated(InputStream& stream, const SkeletalModelParameters& parameters)
{
NazaraError("Failed to load model mesh");
return false;
NazaraUnused(stream);
NazaraUnused(parameters);
return Ternary_Unknown;
}
if (!mesh->IsAnimable())
bool LoadAnimated(SkeletalModel* model, InputStream& stream, const SkeletalModelParameters& parameters)
{
NazaraError("Can't load static mesh into animated model");
return false;
}
NazaraUnused(parameters);
model->Reset();
model->SetMesh(mesh);
if (parameters.loadMaterials)
{
unsigned int matCount = model->GetMaterialCount();
for (unsigned int i = 0; i < matCount; ++i)
MeshRef mesh = Mesh::New();
if (!mesh->LoadFromStream(stream, parameters.mesh))
{
NzString mat = mesh->GetMaterial(i);
if (!mat.IsEmpty())
NazaraError("Failed to load model mesh");
return false;
}
if (!mesh->IsAnimable())
{
NazaraError("Can't load static mesh into animated model");
return false;
}
model->Reset();
model->SetMesh(mesh);
if (parameters.loadMaterials)
{
unsigned int matCount = model->GetMaterialCount();
for (unsigned int i = 0; i < matCount; ++i)
{
NzMaterialRef material = NzMaterial::New();
if (material->LoadFromFile(mat, parameters.material))
model->SetMaterial(i, material);
else
NazaraWarning("Failed to load material #" + NzString::Number(i));
String mat = mesh->GetMaterial(i);
if (!mat.IsEmpty())
{
MaterialRef material = Material::New();
if (material->LoadFromFile(mat, parameters.material))
model->SetMaterial(i, material);
else
NazaraWarning("Failed to load material #" + String::Number(i));
}
}
}
return true;
}
}
namespace Loaders
{
void RegisterMesh()
{
ModelLoader::RegisterLoader(MeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
SkeletalModelLoader::RegisterLoader(MeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
}
return true;
void UnregisterMesh()
{
ModelLoader::UnregisterLoader(MeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
SkeletalModelLoader::UnregisterLoader(MeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
}
}
}
void NzLoaders_Mesh_Register()
{
NzModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
NzSkeletalModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
}
void NzLoaders_Mesh_Unregister()
{
NzModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
NzSkeletalModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
}

View File

@@ -9,7 +9,13 @@
#include <Nazara/Prerequesites.hpp>
void NzLoaders_Mesh_Register();
void NzLoaders_Mesh_Unregister();
namespace Nz
{
namespace Loaders
{
void RegisterMesh();
void UnregisterMesh();
}
}
#endif // NAZARA_LOADERS_MESH_HPP

View File

@@ -20,298 +20,304 @@
///TODO: N'avoir qu'un seul VertexBuffer communs à tous les meshes
namespace
namespace Nz
{
bool IsSupported(const NzString& extension)
namespace
{
return (extension == "obj");
}
nzTernary Check(NzInputStream& stream, const NzModelParameters& parameters)
{
NazaraUnused(stream);
NazaraUnused(parameters);
return nzTernary_Unknown;
}
bool LoadMaterials(NzModel* model, const NzString& filePath, const NzMaterialParams& parameters, const NzString* materials, const NzOBJParser::Mesh* meshes, unsigned int meshCount)
{
NzFile file(filePath);
if (!file.Open(nzOpenMode_ReadOnly | nzOpenMode_Text))
bool IsSupported(const String& extension)
{
NazaraError("Failed to open MTL file (" + file.GetPath() + ')');
return false;
return (extension == "obj");
}
NzMTLParser materialParser(file);
if (!materialParser.Parse())
Ternary Check(InputStream& stream, const ModelParameters& parameters)
{
NazaraError("MTL parser failed");
return false;
NazaraUnused(stream);
NazaraUnused(parameters);
return Ternary_Unknown;
}
std::unordered_map<NzString, NzMaterialRef> materialCache;
NzString baseDir = file.GetDirectory();
for (unsigned int i = 0; i < meshCount; ++i)
bool LoadMaterials(Model* model, const String& filePath, const MaterialParams& parameters, const String* materials, const OBJParser::Mesh* meshes, unsigned int meshCount)
{
const NzString& matName = materials[meshes[i].material];
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
if (!mtlMat)
File file(filePath);
if (!file.Open(OpenMode_ReadOnly | OpenMode_Text))
{
NazaraWarning("MTL has no material \"" + matName + '"');
continue;
NazaraError("Failed to open MTL file (" + file.GetPath() + ')');
return false;
}
auto it = materialCache.find(matName);
if (it == materialCache.end())
MTLParser materialParser(file);
if (!materialParser.Parse())
{
NzMaterialRef material = NzMaterial::New();
material->SetShader(parameters.shaderName);
NazaraError("MTL parser failed");
return false;
}
nzUInt8 alphaValue = static_cast<nzUInt8>(mtlMat->alpha*255.f);
NzColor ambientColor(mtlMat->ambient);
NzColor diffuseColor(mtlMat->diffuse);
NzColor specularColor(mtlMat->specular);
ambientColor.a = alphaValue;
diffuseColor.a = alphaValue;
specularColor.a = alphaValue;
material->SetAmbientColor(ambientColor);
material->SetDiffuseColor(diffuseColor);
material->SetSpecularColor(specularColor);
material->SetShininess(mtlMat->shininess);
bool isTranslucent = (alphaValue != 255);
if (parameters.loadAlphaMap && !mtlMat->alphaMap.IsEmpty())
std::unordered_map<String, MaterialRef> materialCache;
String baseDir = file.GetDirectory();
for (unsigned int i = 0; i < meshCount; ++i)
{
const String& matName = materials[meshes[i].material];
const MTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
if (!mtlMat)
{
if (material->SetAlphaMap(baseDir + mtlMat->alphaMap))
isTranslucent = true; // Une alpha map indique de la transparence
NazaraWarning("MTL has no material \"" + matName + '"');
continue;
}
auto it = materialCache.find(matName);
if (it == materialCache.end())
{
MaterialRef material = Material::New();
material->SetShader(parameters.shaderName);
UInt8 alphaValue = static_cast<UInt8>(mtlMat->alpha*255.f);
Color ambientColor(mtlMat->ambient);
Color diffuseColor(mtlMat->diffuse);
Color specularColor(mtlMat->specular);
ambientColor.a = alphaValue;
diffuseColor.a = alphaValue;
specularColor.a = alphaValue;
material->SetAmbientColor(ambientColor);
material->SetDiffuseColor(diffuseColor);
material->SetSpecularColor(specularColor);
material->SetShininess(mtlMat->shininess);
bool isTranslucent = (alphaValue != 255);
if (parameters.loadAlphaMap && !mtlMat->alphaMap.IsEmpty())
{
if (material->SetAlphaMap(baseDir + mtlMat->alphaMap))
isTranslucent = true; // Une alpha map indique de la transparence
else
NazaraWarning("Failed to load alpha map (" + mtlMat->alphaMap + ')');
}
if (parameters.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
{
if (!material->SetDiffuseMap(baseDir + mtlMat->diffuseMap))
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
}
if (parameters.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
{
if (!material->SetSpecularMap(baseDir + mtlMat->specularMap))
NazaraWarning("Failed to load specular map (" + mtlMat->specularMap + ')');
}
// Si nous avons une alpha map ou des couleurs transparentes,
// nous devons configurer le matériau pour accepter la transparence au mieux
if (isTranslucent)
{
// On paramètre le matériau pour accepter la transparence au mieux
material->Enable(RendererParameter_Blend, true);
material->Enable(RendererParameter_DepthWrite, false);
material->SetDstBlend(BlendFunc_InvSrcAlpha);
material->SetSrcBlend(BlendFunc_SrcAlpha);
}
it = materialCache.emplace(matName, std::move(material)).first;
}
model->SetMaterial(meshes[i].material, it->second);
}
return true;
}
bool Load(Model* model, InputStream& stream, const ModelParameters& parameters)
{
OBJParser parser(stream);
if (!parser.Parse())
{
NazaraError("OBJ parser failed");
return false;
}
MeshRef mesh = Mesh::New();
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
{
NazaraInternalError("Failed to create mesh");
return false;
}
const String* materials = parser.GetMaterials();
const Vector4f* positions = parser.GetPositions();
const Vector3f* normals = parser.GetNormals();
const Vector3f* texCoords = parser.GetTexCoords();
const OBJParser::Mesh* meshes = parser.GetMeshes();
unsigned int meshCount = parser.GetMeshCount();
NazaraAssert(materials != nullptr && positions != nullptr && normals != nullptr &&
texCoords != nullptr && meshes != nullptr && meshCount > 0,
"Invalid OBJParser output");
// Un conteneur temporaire pour contenir les indices de face avant triangulation
std::vector<unsigned int> faceIndices(3); // Comme il y aura au moins trois sommets
for (unsigned int i = 0; i < meshCount; ++i)
{
unsigned int faceCount = meshes[i].faces.size();
if (faceCount == 0)
continue;
std::vector<unsigned int> indices;
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
// Afin d'utiliser OBJParser::FaceVertex comme clé dans un unordered_map,
// nous devons fournir un foncteur de hash ainsi qu'un foncteur de comparaison
// Hash
struct FaceVertexHasher
{
std::size_t operator()(const OBJParser::FaceVertex& o) const
{
std::size_t seed = 0;
HashCombine(seed, o.normal);
HashCombine(seed, o.position);
HashCombine(seed, o.texCoord);
return seed;
}
};
// Comparaison
struct FaceVertexComparator
{
bool operator()(const OBJParser::FaceVertex& lhs, const OBJParser::FaceVertex& rhs) const
{
return lhs.normal == rhs.normal &&
lhs.position == rhs.position &&
lhs.texCoord == rhs.texCoord;
}
};
std::unordered_map<OBJParser::FaceVertex, unsigned int, FaceVertexHasher, FaceVertexComparator> vertices;
unsigned int vertexCount = 0;
for (unsigned int j = 0; j < faceCount; ++j)
{
unsigned int faceVertexCount = meshes[i].faces[j].vertices.size();
faceIndices.resize(faceVertexCount);
for (unsigned int k = 0; k < faceVertexCount; ++k)
{
const OBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
auto it = vertices.find(vertex);
if (it == vertices.end())
it = vertices.emplace(vertex, vertexCount++).first;
faceIndices[k] = it->second;
}
for (unsigned int k = 1; k < faceVertexCount-1; ++k)
{
indices.push_back(faceIndices[0]);
indices.push_back(faceIndices[k]);
indices.push_back(faceIndices[k+1]);
}
}
// Création des buffers
IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indices.size(), parameters.mesh.storage, BufferUsage_Static);
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.mesh.storage, BufferUsage_Static);
// Remplissage des indices
IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
for (unsigned int j = 0; j < indices.size(); ++j)
indexMapper.Set(j, indices[j]);
indexMapper.Unmap(); // Pour laisser les autres tâches affecter l'index buffer
// Remplissage des vertices
bool hasNormals = true;
bool hasTexCoords = true;
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
MeshVertex* meshVertices = static_cast<MeshVertex*>(vertexMapper.GetPointer());
for (auto& vertexPair : vertices)
{
const OBJParser::FaceVertex& vertexIndices = vertexPair.first;
unsigned int index = vertexPair.second;
MeshVertex& vertex = meshVertices[index];
const Vector4f& vec = positions[vertexIndices.position];
vertex.position.Set(vec.x, vec.y, vec.z);
vertex.position *= parameters.mesh.scale/vec.w;
if (vertexIndices.normal >= 0)
vertex.normal = normals[vertexIndices.normal];
else
NazaraWarning("Failed to load alpha map (" + mtlMat->alphaMap + ')');
hasNormals = false;
if (vertexIndices.texCoord >= 0)
{
const Vector3f& uvw = texCoords[vertexIndices.texCoord];
vertex.uv.Set(uvw.x, (parameters.mesh.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
}
else
hasTexCoords = false;
}
if (parameters.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
vertexMapper.Unmap();
StaticMeshRef subMesh = StaticMesh::New(mesh);
if (!subMesh->Create(vertexBuffer))
{
if (!material->SetDiffuseMap(baseDir + mtlMat->diffuseMap))
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
NazaraError("Failed to create StaticMesh");
continue;
}
if (parameters.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
{
if (!material->SetSpecularMap(baseDir + mtlMat->specularMap))
NazaraWarning("Failed to load specular map (" + mtlMat->specularMap + ')');
}
if (parameters.mesh.optimizeIndexBuffers)
indexBuffer->Optimize();
// Si nous avons une alpha map ou des couleurs transparentes,
// nous devons configurer le matériau pour accepter la transparence au mieux
if (isTranslucent)
{
// On paramètre le matériau pour accepter la transparence au mieux
material->Enable(nzRendererParameter_Blend, true);
material->Enable(nzRendererParameter_DepthWrite, false);
material->SetDstBlend(nzBlendFunc_InvSrcAlpha);
material->SetSrcBlend(nzBlendFunc_SrcAlpha);
}
subMesh->GenerateAABB();
subMesh->SetIndexBuffer(indexBuffer);
subMesh->SetMaterialIndex(meshes[i].material);
subMesh->SetPrimitiveMode(PrimitiveMode_TriangleList);
it = materialCache.emplace(matName, std::move(material)).first;
// Ce que nous pouvons générer dépend des données à disposition (par exemple les tangentes nécessitent des coordonnées de texture)
if (hasNormals && hasTexCoords)
subMesh->GenerateTangents();
else if (hasTexCoords)
subMesh->GenerateNormalsAndTangents();
else
subMesh->GenerateNormals();
mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh);
}
mesh->SetMaterialCount(parser.GetMaterialCount());
if (parameters.mesh.center)
mesh->Recenter();
model->SetMesh(mesh);
// On charge les matériaux si demandé
String mtlLib = parser.GetMtlLib();
if (parameters.loadMaterials && !mtlLib.IsEmpty())
{
ErrorFlags flags(ErrorFlag_ThrowExceptionDisabled);
LoadMaterials(model, stream.GetDirectory() + mtlLib, parameters.material, materials, meshes, meshCount);
}
model->SetMaterial(meshes[i].material, it->second);
return true;
}
return true;
}
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
namespace Loaders
{
NzOBJParser parser(stream);
if (!parser.Parse())
void RegisterOBJ()
{
NazaraError("OBJ parser failed");
return false;
ModelLoader::RegisterLoader(IsSupported, Check, Load);
}
NzMeshRef mesh = NzMesh::New();
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
void UnregisterOBJ()
{
NazaraInternalError("Failed to create mesh");
return false;
ModelLoader::UnregisterLoader(IsSupported, Check, Load);
}
const NzString* materials = parser.GetMaterials();
const NzVector4f* positions = parser.GetPositions();
const NzVector3f* normals = parser.GetNormals();
const NzVector3f* texCoords = parser.GetTexCoords();
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
unsigned int meshCount = parser.GetMeshCount();
NazaraAssert(materials != nullptr && positions != nullptr && normals != nullptr &&
texCoords != nullptr && meshes != nullptr && meshCount > 0,
"Invalid OBJParser output");
// Un conteneur temporaire pour contenir les indices de face avant triangulation
std::vector<unsigned int> faceIndices(3); // Comme il y aura au moins trois sommets
for (unsigned int i = 0; i < meshCount; ++i)
{
unsigned int faceCount = meshes[i].faces.size();
if (faceCount == 0)
continue;
std::vector<unsigned int> indices;
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
// Afin d'utiliser OBJParser::FaceVertex comme clé dans un unordered_map,
// nous devons fournir un foncteur de hash ainsi qu'un foncteur de comparaison
// Hash
struct FaceVertexHasher
{
std::size_t operator()(const NzOBJParser::FaceVertex& o) const
{
std::size_t seed = 0;
NzHashCombine(seed, o.normal);
NzHashCombine(seed, o.position);
NzHashCombine(seed, o.texCoord);
return seed;
}
};
// Comparaison
struct FaceVertexComparator
{
bool operator()(const NzOBJParser::FaceVertex& lhs, const NzOBJParser::FaceVertex& rhs) const
{
return lhs.normal == rhs.normal &&
lhs.position == rhs.position &&
lhs.texCoord == rhs.texCoord;
}
};
std::unordered_map<NzOBJParser::FaceVertex, unsigned int, FaceVertexHasher, FaceVertexComparator> vertices;
unsigned int vertexCount = 0;
for (unsigned int j = 0; j < faceCount; ++j)
{
unsigned int faceVertexCount = meshes[i].faces[j].vertices.size();
faceIndices.resize(faceVertexCount);
for (unsigned int k = 0; k < faceVertexCount; ++k)
{
const NzOBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
auto it = vertices.find(vertex);
if (it == vertices.end())
it = vertices.emplace(vertex, vertexCount++).first;
faceIndices[k] = it->second;
}
for (unsigned int k = 1; k < faceVertexCount-1; ++k)
{
indices.push_back(faceIndices[0]);
indices.push_back(faceIndices[k]);
indices.push_back(faceIndices[k+1]);
}
}
// Création des buffers
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(vertexCount > std::numeric_limits<nzUInt16>::max(), indices.size(), parameters.mesh.storage, nzBufferUsage_Static);
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.mesh.storage, nzBufferUsage_Static);
// Remplissage des indices
NzIndexMapper indexMapper(indexBuffer, nzBufferAccess_WriteOnly);
for (unsigned int j = 0; j < indices.size(); ++j)
indexMapper.Set(j, indices[j]);
indexMapper.Unmap(); // Pour laisser les autres tâches affecter l'index buffer
// Remplissage des vertices
bool hasNormals = true;
bool hasTexCoords = true;
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
for (auto& vertexPair : vertices)
{
const NzOBJParser::FaceVertex& vertexIndices = vertexPair.first;
unsigned int index = vertexPair.second;
NzMeshVertex& vertex = meshVertices[index];
const NzVector4f& vec = positions[vertexIndices.position];
vertex.position.Set(vec.x, vec.y, vec.z);
vertex.position *= parameters.mesh.scale/vec.w;
if (vertexIndices.normal >= 0)
vertex.normal = normals[vertexIndices.normal];
else
hasNormals = false;
if (vertexIndices.texCoord >= 0)
{
const NzVector3f& uvw = texCoords[vertexIndices.texCoord];
vertex.uv.Set(uvw.x, (parameters.mesh.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
}
else
hasTexCoords = false;
}
vertexMapper.Unmap();
NzStaticMeshRef subMesh = NzStaticMesh::New(mesh);
if (!subMesh->Create(vertexBuffer))
{
NazaraError("Failed to create StaticMesh");
continue;
}
if (parameters.mesh.optimizeIndexBuffers)
indexBuffer->Optimize();
subMesh->GenerateAABB();
subMesh->SetIndexBuffer(indexBuffer);
subMesh->SetMaterialIndex(meshes[i].material);
subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList);
// Ce que nous pouvons générer dépend des données à disposition (par exemple les tangentes nécessitent des coordonnées de texture)
if (hasNormals && hasTexCoords)
subMesh->GenerateTangents();
else if (hasTexCoords)
subMesh->GenerateNormalsAndTangents();
else
subMesh->GenerateNormals();
mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh);
}
mesh->SetMaterialCount(parser.GetMaterialCount());
if (parameters.mesh.center)
mesh->Recenter();
model->SetMesh(mesh);
// On charge les matériaux si demandé
NzString mtlLib = parser.GetMtlLib();
if (parameters.loadMaterials && !mtlLib.IsEmpty())
{
NzErrorFlags flags(nzErrorFlag_ThrowExceptionDisabled);
LoadMaterials(model, stream.GetDirectory() + mtlLib, parameters.material, materials, meshes, meshCount);
}
return true;
}
}
void NzLoaders_OBJ_Register()
{
NzModelLoader::RegisterLoader(IsSupported, Check, Load);
}
void NzLoaders_OBJ_Unregister()
{
NzModelLoader::UnregisterLoader(IsSupported, Check, Load);
}

View File

@@ -9,7 +9,13 @@
#include <Nazara/Prerequesites.hpp>
void NzLoaders_OBJ_Register();
void NzLoaders_OBJ_Unregister();
namespace Nz
{
namespace Loaders
{
void RegisterOBJ();
void UnregisterOBJ();
}
}
#endif // NAZARA_LOADERS_OBJ_HPP

View File

@@ -8,41 +8,47 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
nzTernary Check(NzInputStream& stream, const NzMaterialParams& parameters)
namespace
{
NazaraUnused(stream);
NazaraUnused(parameters);
return nzTernary_Unknown;
}
bool Load(NzMaterial* material, NzInputStream& stream, const NzMaterialParams& parameters)
{
NazaraUnused(parameters);
NzTextureRef texture = NzTexture::New();
if (!texture->LoadFromStream(stream))
Ternary Check(InputStream& stream, const MaterialParams& parameters)
{
NazaraError("Failed to load diffuse map");
return false;
NazaraUnused(stream);
NazaraUnused(parameters);
return Ternary_Unknown;
}
material->Reset();
material->SetDiffuseMap(texture);
material->SetShader(parameters.shaderName);
bool Load(Material* material, InputStream& stream, const MaterialParams& parameters)
{
NazaraUnused(parameters);
return true;
TextureRef texture = Texture::New();
if (!texture->LoadFromStream(stream))
{
NazaraError("Failed to load diffuse map");
return false;
}
material->Reset();
material->SetDiffuseMap(texture);
material->SetShader(parameters.shaderName);
return true;
}
}
namespace Loaders
{
void RegisterTexture()
{
MaterialLoader::RegisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
}
void UnregisterTexture()
{
MaterialLoader::UnregisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
}
}
}
void NzLoaders_Texture_Register()
{
NzMaterialLoader::RegisterLoader(NzImageLoader::IsExtensionSupported, Check, Load);
}
void NzLoaders_Texture_Unregister()
{
NzMaterialLoader::UnregisterLoader(NzImageLoader::IsExtensionSupported, Check, Load);
}

View File

@@ -9,7 +9,13 @@
#include <Nazara/Prerequesites.hpp>
void NzLoaders_Texture_Register();
void NzLoaders_Texture_Unregister();
namespace Nz
{
namespace Loaders
{
void RegisterTexture();
void UnregisterTexture();
}
}
#endif // NAZARA_LOADERS_TEXTURE_HPP

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -25,165 +25,168 @@
#include <Nazara/Utility/Font.hpp>
#include <Nazara/Graphics/Debug.hpp>
bool NzGraphics::Initialize()
namespace Nz
{
if (s_moduleReferenceCounter > 0)
bool Graphics::Initialize()
{
s_moduleReferenceCounter++;
return true; // Déjà initialisé
}
// Initialisation des dépendances
if (!NzRenderer::Initialize())
{
NazaraError("Failed to initialize Renderer module");
return false;
}
s_moduleReferenceCounter++;
// Initialisation du module
NzCallOnExit onExit(NzGraphics::Uninitialize);
if (!NzMaterial::Initialize())
{
NazaraError("Failed to initialize materials");
return false;
}
if (!NzParticleController::Initialize())
{
NazaraError("Failed to initialize particle controllers");
return false;
}
if (!NzParticleDeclaration::Initialize())
{
NazaraError("Failed to initialize particle declarations");
return false;
}
if (!NzParticleGenerator::Initialize())
{
NazaraError("Failed to initialize particle generators");
return false;
}
if (!NzParticleRenderer::Initialize())
{
NazaraError("Failed to initialize particle renderers");
return false;
}
if (!NzSkinningManager::Initialize())
{
NazaraError("Failed to initialize skinning manager");
return false;
}
if (!NzSkyboxBackground::Initialize())
{
NazaraError("Failed to initialize skybox backgrounds");
return false;
}
// Loaders
NzLoaders_OBJ_Register();
// Loaders génériques
NzLoaders_Mesh_Register();
NzLoaders_Texture_Register();
// RenderTechniques
if (!NzForwardRenderTechnique::Initialize())
{
NazaraError("Failed to initialize Forward Rendering");
return false;
}
NzRenderTechniques::Register(NzRenderTechniques::ToString(nzRenderTechniqueType_BasicForward), 0, []() -> NzAbstractRenderTechnique* { return new NzForwardRenderTechnique; });
if (NzDeferredRenderTechnique::IsSupported())
{
if (NzDeferredRenderTechnique::Initialize())
NzRenderTechniques::Register(NzRenderTechniques::ToString(nzRenderTechniqueType_DeferredShading), 20, []() -> NzAbstractRenderTechnique* { return new NzDeferredRenderTechnique; });
else
if (s_moduleReferenceCounter > 0)
{
NazaraWarning("Failed to initialize Deferred Rendering");
s_moduleReferenceCounter++;
return true; // Déjà initialisé
}
}
NzFont::SetDefaultAtlas(std::make_shared<NzGuillotineTextureAtlas>());
onExit.Reset();
NazaraNotice("Initialized: Graphics module");
return true;
}
bool NzGraphics::IsInitialized()
{
return s_moduleReferenceCounter != 0;
}
void NzGraphics::Uninitialize()
{
if (s_moduleReferenceCounter != 1)
{
// Le module est soit encore utilisé, soit pas initialisé
if (s_moduleReferenceCounter > 1)
s_moduleReferenceCounter--;
return;
}
// Libération du module
s_moduleReferenceCounter = 0;
// Libération de l'atlas s'il vient de nous
std::shared_ptr<NzAbstractAtlas> defaultAtlas = NzFont::GetDefaultAtlas();
if (defaultAtlas && defaultAtlas->GetStorage() & nzDataStorage_Hardware)
{
NzFont::SetDefaultAtlas(nullptr);
// La police par défaut peut faire vivre un atlas hardware après la libération du module (ce qui va être problématique)
// du coup, si la police par défaut utilise un atlas hardware, on lui enlève.
// Je n'aime pas cette solution mais je n'en ai pas de meilleure sous la main pour l'instant
if (!defaultAtlas.unique())
// Initialisation des dépendances
if (!Renderer::Initialize())
{
// Encore au moins une police utilise l'atlas
NzFont* defaultFont = NzFont::GetDefault();
defaultFont->SetAtlas(nullptr);
NazaraError("Failed to initialize Renderer module");
return false;
}
if (!defaultAtlas.unique())
s_moduleReferenceCounter++;
// Initialisation du module
CallOnExit onExit(Graphics::Uninitialize);
if (!Material::Initialize())
{
NazaraError("Failed to initialize materials");
return false;
}
if (!ParticleController::Initialize())
{
NazaraError("Failed to initialize particle controllers");
return false;
}
if (!ParticleDeclaration::Initialize())
{
NazaraError("Failed to initialize particle declarations");
return false;
}
if (!ParticleGenerator::Initialize())
{
NazaraError("Failed to initialize particle generators");
return false;
}
if (!ParticleRenderer::Initialize())
{
NazaraError("Failed to initialize particle renderers");
return false;
}
if (!SkinningManager::Initialize())
{
NazaraError("Failed to initialize skinning manager");
return false;
}
if (!SkyboxBackground::Initialize())
{
NazaraError("Failed to initialize skybox backgrounds");
return false;
}
// Loaders
Loaders::RegisterOBJ();
// Loaders génériques
Loaders::RegisterMesh();
Loaders::RegisterTexture();
// RenderTechniques
if (!ForwardRenderTechnique::Initialize())
{
NazaraError("Failed to initialize Forward Rendering");
return false;
}
RenderTechniques::Register(RenderTechniques::ToString(RenderTechniqueType_BasicForward), 0, []() -> AbstractRenderTechnique* { return new ForwardRenderTechnique; });
if (DeferredRenderTechnique::IsSupported())
{
if (DeferredRenderTechnique::Initialize())
RenderTechniques::Register(RenderTechniques::ToString(RenderTechniqueType_DeferredShading), 20, []() -> AbstractRenderTechnique* { return new DeferredRenderTechnique; });
else
{
// Toujours pas seuls propriétaires ? Ah ben zut.
NazaraWarning("Default font atlas uses hardware storage and is still used");
NazaraWarning("Failed to initialize Deferred Rendering");
}
}
Font::SetDefaultAtlas(std::make_shared<GuillotineTextureAtlas>());
onExit.Reset();
NazaraNotice("Initialized: Graphics module");
return true;
}
defaultAtlas.reset();
bool Graphics::IsInitialized()
{
return s_moduleReferenceCounter != 0;
}
// Loaders
NzLoaders_Mesh_Unregister();
NzLoaders_OBJ_Unregister();
NzLoaders_Texture_Unregister();
void Graphics::Uninitialize()
{
if (s_moduleReferenceCounter != 1)
{
// Le module est soit encore utilisé, soit pas initialisé
if (s_moduleReferenceCounter > 1)
s_moduleReferenceCounter--;
NzDeferredRenderTechnique::Uninitialize();
NzForwardRenderTechnique::Uninitialize();
NzSkinningManager::Uninitialize();
NzParticleRenderer::Uninitialize();
NzParticleGenerator::Uninitialize();
NzParticleDeclaration::Uninitialize();
NzParticleController::Uninitialize();
NzMaterial::Uninitialize();
NzSkyboxBackground::Uninitialize();
return;
}
NazaraNotice("Uninitialized: Graphics module");
// Libération du module
s_moduleReferenceCounter = 0;
// Libération des dépendances
NzRenderer::Uninitialize();
// Libération de l'atlas s'il vient de nous
std::shared_ptr<AbstractAtlas> defaultAtlas = Font::GetDefaultAtlas();
if (defaultAtlas && defaultAtlas->GetStorage() & DataStorage_Hardware)
{
Font::SetDefaultAtlas(nullptr);
// La police par défaut peut faire vivre un atlas hardware après la libération du module (ce qui va être problématique)
// du coup, si la police par défaut utilise un atlas hardware, on lui enlève.
// Je n'aime pas cette solution mais je n'en ai pas de meilleure sous la main pour l'instant
if (!defaultAtlas.unique())
{
// Encore au moins une police utilise l'atlas
Font* defaultFont = Font::GetDefault();
defaultFont->SetAtlas(nullptr);
if (!defaultAtlas.unique())
{
// Toujours pas seuls propriétaires ? Ah ben zut.
NazaraWarning("Default font atlas uses hardware storage and is still used");
}
}
}
defaultAtlas.reset();
// Loaders
Loaders::UnregisterMesh();
Loaders::UnregisterOBJ();
Loaders::UnregisterTexture();
DeferredRenderTechnique::Uninitialize();
ForwardRenderTechnique::Uninitialize();
SkinningManager::Uninitialize();
ParticleRenderer::Uninitialize();
ParticleGenerator::Uninitialize();
ParticleDeclaration::Uninitialize();
ParticleController::Uninitialize();
Material::Uninitialize();
SkyboxBackground::Uninitialize();
NazaraNotice("Uninitialized: Graphics module");
// Libération des dépendances
Renderer::Uninitialize();
}
unsigned int Graphics::s_moduleReferenceCounter = 0;
}
unsigned int NzGraphics::s_moduleReferenceCounter = 0;

View File

@@ -7,42 +7,45 @@
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Graphics/Debug.hpp>
nzUInt32 NzGuillotineTextureAtlas::GetStorage() const
namespace Nz
{
return nzDataStorage_Hardware;
}
NzAbstractImage* NzGuillotineTextureAtlas::ResizeImage(NzAbstractImage* oldImage, const NzVector2ui& size) const
{
std::unique_ptr<NzTexture> newTexture(new NzTexture);
if (newTexture->Create(nzImageType_2D, nzPixelFormat_A8, size.x, size.y, 1))
UInt32 GuillotineTextureAtlas::GetStorage() const
{
if (oldImage)
return DataStorage_Hardware;
}
AbstractImage* GuillotineTextureAtlas::ResizeImage(AbstractImage* oldImage, const Vector2ui& size) const
{
std::unique_ptr<Texture> newTexture(new Texture);
if (newTexture->Create(ImageType_2D, PixelFormatType_A8, size.x, size.y, 1))
{
NzTexture* oldTexture = static_cast<NzTexture*>(oldImage);
// Copie des anciennes données
///TODO: Copie de texture à texture
NzImage image;
if (!oldTexture->Download(&image))
if (oldImage)
{
NazaraError("Failed to download old texture");
return nullptr;
Texture* oldTexture = static_cast<Texture*>(oldImage);
// Copie des anciennes données
///TODO: Copie de texture à texture
Image image;
if (!oldTexture->Download(&image))
{
NazaraError("Failed to download old texture");
return nullptr;
}
if (!newTexture->Update(image, Rectui(0, 0, image.GetWidth(), image.GetHeight())))
{
NazaraError("Failed to update texture");
return nullptr;
}
}
if (!newTexture->Update(image, NzRectui(0, 0, image.GetWidth(), image.GetHeight())))
{
NazaraError("Failed to update texture");
return nullptr;
}
return newTexture.release();
}
else
{
// Si on arrive ici c'est que la taille demandée est trop grande pour la carte graphique
// ou que nous manquons de mémoire
return nullptr;
}
return newTexture.release();
}
else
{
// Si on arrive ici c'est que la taille demandée est trop grande pour la carte graphique
// ou que nous manquons de mémoire
return nullptr;
}
}

View File

@@ -5,39 +5,42 @@
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzInstancedRenderable::~NzInstancedRenderable()
namespace Nz
{
OnInstancedRenderableRelease(this);
InstancedRenderable::~InstancedRenderable()
{
OnInstancedRenderableRelease(this);
}
bool InstancedRenderable::Cull(const Frustumf& frustum, const InstanceData& instanceData) const
{
return frustum.Contains(instanceData.volume);
}
const BoundingVolumef& InstancedRenderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
void InstancedRenderable::InvalidateData(InstanceData* instanceData, UInt32 flags) const
{
instanceData->flags |= flags;
}
void InstancedRenderable::UpdateBoundingVolume(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
NazaraUnused(instanceData);
instanceData->volume.Update(instanceData->transformMatrix);
}
void InstancedRenderable::UpdateData(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
}
InstancedRenderableLibrary::LibraryMap InstancedRenderable::s_library;
}
bool NzInstancedRenderable::Cull(const NzFrustumf& frustum, const InstanceData& instanceData) const
{
return frustum.Contains(instanceData.volume);
}
const NzBoundingVolumef& NzInstancedRenderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
void NzInstancedRenderable::InvalidateData(InstanceData* instanceData, nzUInt32 flags) const
{
instanceData->flags |= flags;
}
void NzInstancedRenderable::UpdateBoundingVolume(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
NazaraUnused(instanceData);
instanceData->volume.Update(instanceData->transformMatrix);
}
void NzInstancedRenderable::UpdateData(InstanceData* instanceData) const
{
NazaraAssert(instanceData, "Invalid instance data");
}
NzInstancedRenderableLibrary::LibraryMap NzInstancedRenderable::s_library;

View File

@@ -15,164 +15,167 @@
///TODO: Utilisation des UBOs
///TODO: Scale ?
NzLight::NzLight(nzLightType type) :
m_type(type)
namespace Nz
{
SetAmbientFactor((type == nzLightType_Directional) ? 0.2f : 0.f);
SetAttenuation(0.9f);
SetColor(NzColor::White);
SetDiffuseFactor(1.f);
SetInnerAngle(15.f);
SetOuterAngle(45.f);
SetRadius(5.f);
}
void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
switch (m_type)
Light::Light(LightType type) :
m_type(type)
{
case nzLightType_Directional:
{
NzAbstractRenderQueue::DirectionalLight light;
light.ambientFactor = m_ambientFactor;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(NzVector3f::Forward(), 0.f);
renderQueue->AddDirectionalLight(light);
break;
}
case nzLightType_Point:
{
NzAbstractRenderQueue::PointLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.invRadius = m_invRadius;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddPointLight(light);
break;
}
case nzLightType_Spot:
{
NzAbstractRenderQueue::SpotLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(NzVector3f::Forward(), 0.f);
light.innerAngleCosine = m_innerAngleCosine;
light.invRadius = m_invRadius;
light.outerAngleCosine = m_outerAngleCosine;
light.outerAngleTangent = m_outerAngleTangent;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddSpotLight(light);
break;
}
default:
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
break;
}
}
NzLight* NzLight::Clone() const
{
return new NzLight(*this);
}
NzLight* NzLight::Create() const
{
return new NzLight;
}
bool NzLight::Cull(const NzFrustumf& frustum, const NzMatrix4f& transformMatrix) const
{
switch (m_type)
{
case nzLightType_Directional:
return true; // Always visible
case nzLightType_Point:
return frustum.Contains(NzSpheref(transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise)
case nzLightType_Spot:
return frustum.Contains(m_boundingVolume);
SetAmbientFactor((type == LightType_Directional) ? 0.2f : 0.f);
SetAttenuation(0.9f);
SetColor(Color::White);
SetDiffuseFactor(1.f);
SetInnerAngle(15.f);
SetOuterAngle(45.f);
SetRadius(5.f);
}
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
return false;
}
void NzLight::UpdateBoundingVolume(const NzMatrix4f& transformMatrix)
{
switch (m_type)
void Light::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const
{
case nzLightType_Directional:
break; // Nothing to do (bounding volume should be infinite)
case nzLightType_Point:
m_boundingVolume.Update(transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted
break;
case nzLightType_Spot:
m_boundingVolume.Update(transformMatrix);
break;
default:
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
break;
}
}
void NzLight::MakeBoundingVolume() const
{
switch (m_type)
{
case nzLightType_Directional:
m_boundingVolume.MakeInfinite();
break;
case nzLightType_Point:
switch (m_type)
{
NzVector3f radius(m_radius * float(M_SQRT3));
m_boundingVolume.Set(-radius, radius);
break;
case LightType_Directional:
{
AbstractRenderQueue::DirectionalLight light;
light.ambientFactor = m_ambientFactor;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
renderQueue->AddDirectionalLight(light);
break;
}
case LightType_Point:
{
AbstractRenderQueue::PointLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.invRadius = m_invRadius;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddPointLight(light);
break;
}
case LightType_Spot:
{
AbstractRenderQueue::SpotLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
light.innerAngleCosine = m_innerAngleCosine;
light.invRadius = m_invRadius;
light.outerAngleCosine = m_outerAngleCosine;
light.outerAngleTangent = m_outerAngleTangent;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddSpotLight(light);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
Light* Light::Clone() const
{
return new Light(*this);
}
Light* Light::Create() const
{
return new Light;
}
bool Light::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const
{
switch (m_type)
{
case LightType_Directional:
return true; // Always visible
case LightType_Point:
return frustum.Contains(Spheref(transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise)
case LightType_Spot:
return frustum.Contains(m_boundingVolume);
}
case nzLightType_Spot:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
return false;
}
void Light::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
switch (m_type)
{
// On forme une boite sur l'origine
NzBoxf box(NzVector3f::Zero());
case LightType_Directional:
break; // Nothing to do (bounding volume should be infinite)
// On calcule le reste des points
NzVector3f base(NzVector3f::Forward()*m_radius);
case LightType_Point:
m_boundingVolume.Update(transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted
break;
// Il nous faut maintenant le rayon du cercle projeté à cette distance
// Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente
float radius = m_radius * m_outerAngleTangent;
NzVector3f lExtend = NzVector3f::Left()*radius;
NzVector3f uExtend = NzVector3f::Up()*radius;
case LightType_Spot:
m_boundingVolume.Update(transformMatrix);
break;
// Et on ajoute ensuite les quatres extrémités de la pyramide
box.ExtendTo(base + lExtend + uExtend);
box.ExtendTo(base + lExtend - uExtend);
box.ExtendTo(base - lExtend + uExtend);
box.ExtendTo(base - lExtend - uExtend);
m_boundingVolume.Set(box);
break;
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
default:
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
break;
void Light::MakeBoundingVolume() const
{
switch (m_type)
{
case LightType_Directional:
m_boundingVolume.MakeInfinite();
break;
case LightType_Point:
{
Vector3f radius(m_radius * float(M_SQRT3));
m_boundingVolume.Set(-radius, radius);
break;
}
case LightType_Spot:
{
// On forme une boite sur l'origine
Boxf box(Vector3f::Zero());
// On calcule le reste des points
Vector3f base(Vector3f::Forward()*m_radius);
// Il nous faut maintenant le rayon du cercle projeté à cette distance
// Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente
float radius = m_radius * m_outerAngleTangent;
Vector3f lExtend = Vector3f::Left()*radius;
Vector3f uExtend = Vector3f::Up()*radius;
// Et on ajoute ensuite les quatres extrémités de la pyramide
box.ExtendTo(base + lExtend + uExtend);
box.ExtendTo(base + lExtend - uExtend);
box.ExtendTo(base - lExtend + uExtend);
box.ExtendTo(base - lExtend - uExtend);
m_boundingVolume.Set(box);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,350 +10,353 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzModelParameters::NzModelParameters()
namespace Nz
{
material.shaderName = "PhongLighting";
}
bool NzModelParameters::IsValid() const
{
if (loadMaterials && !material.IsValid())
return false;
return mesh.IsValid();
}
NzModel::NzModel() :
m_matCount(0),
m_skin(0),
m_skinCount(1)
{
}
NzModel::~NzModel()
{
Reset();
}
void NzModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
ModelParameters::ModelParameters()
{
const NzStaticMesh* mesh = static_cast<const NzStaticMesh*>(m_mesh->GetSubMesh(i));
NzMaterial* material = m_materials[mesh->GetMaterialIndex()];
NzMeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = mesh->GetVertexBuffer();
renderQueue->AddMesh(material, meshData, mesh->GetAABB(), instanceData.transformMatrix);
}
}
NzMaterial* NzModel::GetMaterial(const NzString& subMeshName) const
{
#if NAZARA_GRAPHICS_SAFE
if (!m_mesh)
{
NazaraError("Model has no mesh");
return nullptr;
}
#endif
NzSubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return nullptr;
material.shaderName = "PhongLighting";
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
bool ModelParameters::IsValid() const
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount) + ')');
return nullptr;
if (loadMaterials && !material.IsValid())
return false;
return mesh.IsValid();
}
return m_materials[m_skin*m_matCount + matIndex];
}
NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const
{
#if NAZARA_GRAPHICS_SAFE
if (matIndex >= m_matCount)
Model::Model() :
m_matCount(0),
m_skin(0),
m_skinCount(1)
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount) + ')');
return nullptr;
}
#endif
return m_materials[m_skin*m_matCount + matIndex];
}
NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, const NzString& subMeshName) const
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount) + ')');
return nullptr;
}
#endif
NzSubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return nullptr;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
Model::~Model()
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount) + ')');
return nullptr;
Reset();
}
return m_materials[skinIndex*m_matCount + matIndex];
}
NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount) + ')');
return nullptr;
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
{
const StaticMesh* mesh = static_cast<const StaticMesh*>(m_mesh->GetSubMesh(i));
Material* material = m_materials[mesh->GetMaterialIndex()];
MeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = mesh->GetVertexBuffer();
renderQueue->AddMesh(material, meshData, mesh->GetAABB(), instanceData.transformMatrix);
}
}
if (matIndex >= m_matCount)
Material* Model::GetMaterial(const String& subMeshName) const
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount) + ')');
return nullptr;
#if NAZARA_GRAPHICS_SAFE
if (!m_mesh)
{
NazaraError("Model has no mesh");
return nullptr;
}
#endif
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return nullptr;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount) + ')');
return nullptr;
}
return m_materials[m_skin*m_matCount + matIndex];
}
#endif
return m_materials[skinIndex*m_matCount + matIndex];
}
unsigned int NzModel::GetMaterialCount() const
{
return m_matCount;
}
NzMesh* NzModel::GetMesh() const
{
return m_mesh;
}
unsigned int NzModel::GetSkin() const
{
return m_skin;
}
unsigned int NzModel::GetSkinCount() const
{
return m_skinCount;
}
bool NzModel::IsAnimated() const
{
return false;
}
bool NzModel::LoadFromFile(const NzString& filePath, const NzModelParameters& params)
{
return NzModelLoader::LoadFromFile(this, filePath, params);
}
bool NzModel::LoadFromMemory(const void* data, std::size_t size, const NzModelParameters& params)
{
return NzModelLoader::LoadFromMemory(this, data, size, params);
}
bool NzModel::LoadFromStream(NzInputStream& stream, const NzModelParameters& params)
{
return NzModelLoader::LoadFromStream(this, stream, params);
}
void NzModel::Reset()
{
m_matCount = 0;
m_skinCount = 0;
if (m_mesh)
Material* Model::GetMaterial(unsigned int matIndex) const
{
m_mesh.Reset();
m_materials.clear();
#if NAZARA_GRAPHICS_SAFE
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount) + ')');
return nullptr;
}
#endif
return m_materials[m_skin*m_matCount + matIndex];
}
}
bool NzModel::SetMaterial(const NzString& subMeshName, NzMaterial* material)
{
NzSubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
Material* Model::GetMaterial(unsigned int skinIndex, const String& subMeshName) const
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + String::Number(skinIndex) + " >= " + String::Number(m_skinCount) + ')');
return nullptr;
}
#endif
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return nullptr;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount) + ')');
return nullptr;
}
return m_materials[skinIndex*m_matCount + matIndex];
}
Material* Model::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + String::Number(skinIndex) + " >= " + String::Number(m_skinCount) + ')');
return nullptr;
}
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount) + ')');
return nullptr;
}
#endif
return m_materials[skinIndex*m_matCount + matIndex];
}
unsigned int Model::GetMaterialCount() const
{
return m_matCount;
}
Mesh* Model::GetMesh() const
{
return m_mesh;
}
unsigned int Model::GetSkin() const
{
return m_skin;
}
unsigned int Model::GetSkinCount() const
{
return m_skinCount;
}
bool Model::IsAnimated() const
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
bool Model::LoadFromFile(const String& filePath, const ModelParameters& params)
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount) + ')');
return false;
return ModelLoader::LoadFromFile(this, filePath, params);
}
unsigned int index = m_skin*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = NzMaterial::GetDefault();
return true;
}
void NzModel::SetMaterial(unsigned int matIndex, NzMaterial* material)
{
#if NAZARA_GRAPHICS_SAFE
if (matIndex >= m_matCount)
bool Model::LoadFromMemory(const void* data, std::size_t size, const ModelParameters& params)
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
return;
}
#endif
unsigned int index = m_skin*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = NzMaterial::GetDefault();
}
bool NzModel::SetMaterial(unsigned int skinIndex, const NzString& subMeshName, NzMaterial* material)
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount));
return false;
}
#endif
NzSubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
return ModelLoader::LoadFromMemory(this, data, size, params);
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
bool Model::LoadFromStream(InputStream& stream, const ModelParameters& params)
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
return false;
return ModelLoader::LoadFromStream(this, stream, params);
}
unsigned int index = skinIndex*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = NzMaterial::GetDefault();
return true;
}
void NzModel::SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material)
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount));
return;
}
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
return;
}
#endif
unsigned int index = skinIndex*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = NzMaterial::GetDefault();
}
void NzModel::SetMesh(NzMesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && !mesh->IsValid())
{
NazaraError("Invalid mesh");
return;
}
#endif
m_mesh = mesh;
if (m_mesh)
{
m_matCount = mesh->GetMaterialCount();
m_materials.clear();
m_materials.resize(m_matCount, NzMaterial::GetDefault());
m_skinCount = 1;
}
else
void Model::Reset()
{
m_matCount = 0;
m_materials.clear();
m_skinCount = 0;
if (m_mesh)
{
m_mesh.Reset();
m_materials.clear();
}
}
InvalidateBoundingVolume();
}
void NzModel::SetSkin(unsigned int skin)
{
#if NAZARA_GRAPHICS_SAFE
if (skin >= m_skinCount)
bool Model::SetMaterial(const String& subMeshName, Material* material)
{
NazaraError("Skin index out of range (" + NzString::Number(skin) + " >= " + NzString::Number(m_skinCount) + ')');
return;
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount) + ')');
return false;
}
unsigned int index = m_skin*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = Material::GetDefault();
return true;
}
#endif
m_skin = skin;
}
void NzModel::SetSkinCount(unsigned int skinCount)
{
#if NAZARA_GRAPHICS_SAFE
if (skinCount == 0)
void Model::SetMaterial(unsigned int matIndex, Material* material)
{
NazaraError("Skin count must be over zero");
return;
#if NAZARA_GRAPHICS_SAFE
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount));
return;
}
#endif
unsigned int index = m_skin*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = Material::GetDefault();
}
#endif
m_materials.resize(m_matCount*skinCount, NzMaterial::GetDefault());
m_skinCount = skinCount;
bool Model::SetMaterial(unsigned int skinIndex, const String& subMeshName, Material* material)
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + String::Number(skinIndex) + " >= " + String::Number(m_skinCount));
return false;
}
#endif
SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName);
if (!subMesh)
{
NazaraError("Mesh has no submesh \"" + subMeshName + '"');
return false;
}
unsigned int matIndex = subMesh->GetMaterialIndex();
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount));
return false;
}
unsigned int index = skinIndex*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = Material::GetDefault();
return true;
}
void Model::SetMaterial(unsigned int skinIndex, unsigned int matIndex, Material* material)
{
#if NAZARA_GRAPHICS_SAFE
if (skinIndex >= m_skinCount)
{
NazaraError("Skin index out of range (" + String::Number(skinIndex) + " >= " + String::Number(m_skinCount));
return;
}
if (matIndex >= m_matCount)
{
NazaraError("Material index out of range (" + String::Number(matIndex) + " >= " + String::Number(m_matCount));
return;
}
#endif
unsigned int index = skinIndex*m_matCount + matIndex;
if (material)
m_materials[index] = material;
else
m_materials[index] = Material::GetDefault();
}
void Model::SetMesh(Mesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && !mesh->IsValid())
{
NazaraError("Invalid mesh");
return;
}
#endif
m_mesh = mesh;
if (m_mesh)
{
m_matCount = mesh->GetMaterialCount();
m_materials.clear();
m_materials.resize(m_matCount, Material::GetDefault());
m_skinCount = 1;
}
else
{
m_matCount = 0;
m_materials.clear();
m_skinCount = 0;
}
InvalidateBoundingVolume();
}
void Model::SetSkin(unsigned int skin)
{
#if NAZARA_GRAPHICS_SAFE
if (skin >= m_skinCount)
{
NazaraError("Skin index out of range (" + String::Number(skin) + " >= " + String::Number(m_skinCount) + ')');
return;
}
#endif
m_skin = skin;
}
void Model::SetSkinCount(unsigned int skinCount)
{
#if NAZARA_GRAPHICS_SAFE
if (skinCount == 0)
{
NazaraError("Skin count must be over zero");
return;
}
#endif
m_materials.resize(m_matCount*skinCount, Material::GetDefault());
m_skinCount = skinCount;
}
void Model::MakeBoundingVolume() const
{
if (m_mesh)
m_boundingVolume.Set(m_mesh->GetAABB());
else
m_boundingVolume.MakeNull();
}
ModelLoader::LoaderList Model::s_loaders;
}
void NzModel::MakeBoundingVolume() const
{
if (m_mesh)
m_boundingVolume.Set(m_mesh->GetAABB());
else
m_boundingVolume.MakeNull();
}
NzModelLoader::LoaderList NzModel::s_loaders;

View File

@@ -5,31 +5,34 @@
#include <Nazara/Graphics/ParticleController.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleController::NzParticleController(const NzParticleController& controller) :
NzRefCounted()
namespace Nz
{
NazaraUnused(controller);
}
NzParticleController::~NzParticleController()
{
OnParticleControllerRelease(this);
}
bool NzParticleController::Initialize()
{
if (!NzParticleControllerLibrary::Initialize())
ParticleController::ParticleController(const ParticleController& controller) :
RefCounted()
{
NazaraError("Failed to initialise library");
return false;
NazaraUnused(controller);
}
return true;
}
ParticleController::~ParticleController()
{
OnParticleControllerRelease(this);
}
void NzParticleController::Uninitialize()
{
NzParticleControllerLibrary::Uninitialize();
}
bool ParticleController::Initialize()
{
if (!ParticleControllerLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
NzParticleControllerLibrary::LibraryMap NzParticleController::s_library;
return true;
}
void ParticleController::Uninitialize()
{
ParticleControllerLibrary::Uninitialize();
}
ParticleControllerLibrary::LibraryMap ParticleController::s_library;
}

View File

@@ -13,226 +13,229 @@
#include <cstring>
#include <Nazara/Graphics/Debug.hpp>
NzParticleDeclaration::NzParticleDeclaration() :
m_stride(0)
namespace Nz
{
}
NzParticleDeclaration::NzParticleDeclaration(const NzParticleDeclaration& declaration) :
NzRefCounted(),
m_stride(declaration.m_stride)
{
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzParticleComponent_Max+1));
}
NzParticleDeclaration::~NzParticleDeclaration()
{
OnParticleDeclarationRelease(this);
}
void NzParticleDeclaration::DisableComponent(nzParticleComponent component)
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
ParticleDeclaration::ParticleDeclaration() :
m_stride(0)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == nzParticleComponent_Unused)
{
NazaraError("Cannot disable \"unused\" component");
return;
}
#endif
Component& vertexComponent = m_components[component];
if (vertexComponent.enabled)
{
vertexComponent.enabled = false;
m_stride -= NzUtility::ComponentStride[vertexComponent.type];
}
}
void NzParticleDeclaration::EnableComponent(nzParticleComponent component, nzComponentType type, unsigned int offset)
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (!IsTypeSupported(type))
{
NazaraError("Component type 0x" + NzString::Number(type, 16) + " is not supported by particle declarations");
return;
}
#endif
if (component != nzParticleComponent_Unused)
{
Component& particleComponent = m_components[component];
if (particleComponent.enabled)
m_stride -= NzUtility::ComponentStride[particleComponent.type];
else
particleComponent.enabled = true;
particleComponent.offset = offset;
particleComponent.type = type;
}
m_stride += NzUtility::ComponentStride[type];
}
void NzParticleDeclaration::GetComponent(nzParticleComponent component, bool* enabled, nzComponentType* type, unsigned int* offset) const
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
ParticleDeclaration::ParticleDeclaration(const ParticleDeclaration& declaration) :
RefCounted(),
m_stride(declaration.m_stride)
{
NazaraError("Particle component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == nzParticleComponent_Unused)
{
NazaraError("Cannot get \"unused\" component");
return;
}
#endif
const Component& particleComponent = m_components[component];
if (enabled)
*enabled = particleComponent.enabled;
if (type)
*type = particleComponent.type;
if (offset)
*offset = particleComponent.offset;
}
unsigned int NzParticleDeclaration::GetStride() const
{
return m_stride;
}
void NzParticleDeclaration::SetStride(unsigned int stride)
{
m_stride = stride;
}
NzParticleDeclaration& NzParticleDeclaration::operator=(const NzParticleDeclaration& declaration)
{
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzParticleComponent_Max+1));
m_stride = declaration.m_stride;
return *this;
}
NzParticleDeclaration* NzParticleDeclaration::Get(nzParticleLayout layout)
{
#ifdef NAZARA_DEBUG
if (layout > nzParticleLayout_Max)
{
NazaraError("Particle layout out of enum");
return nullptr;
}
#endif
return &s_declarations[layout];
}
bool NzParticleDeclaration::IsTypeSupported(nzComponentType type)
{
switch (type)
{
case nzComponentType_Color:
case nzComponentType_Double1:
case nzComponentType_Double2:
case nzComponentType_Double3:
case nzComponentType_Double4:
case nzComponentType_Float1:
case nzComponentType_Float2:
case nzComponentType_Float3:
case nzComponentType_Float4:
case nzComponentType_Int1:
case nzComponentType_Int2:
case nzComponentType_Int3:
case nzComponentType_Int4:
case nzComponentType_Quaternion:
return true;
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(ParticleComponent_Max+1));
}
NazaraError("Component type not handled (0x" + NzString::Number(type, 16) + ')');
return false;
}
bool NzParticleDeclaration::Initialize()
{
if (!NzParticleDeclarationLibrary::Initialize())
ParticleDeclaration::~ParticleDeclaration()
{
NazaraError("Failed to initialise library");
OnParticleDeclarationRelease(this);
}
void ParticleDeclaration::DisableComponent(ParticleComponent component)
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == ParticleComponent_Unused)
{
NazaraError("Cannot disable \"unused\" component");
return;
}
#endif
Component& vertexComponent = m_components[component];
if (vertexComponent.enabled)
{
vertexComponent.enabled = false;
m_stride -= Utility::ComponentStride[vertexComponent.type];
}
}
void ParticleDeclaration::EnableComponent(ParticleComponent component, ComponentType type, unsigned int offset)
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (!IsTypeSupported(type))
{
NazaraError("Component type 0x" + String::Number(type, 16) + " is not supported by particle declarations");
return;
}
#endif
if (component != ParticleComponent_Unused)
{
Component& particleComponent = m_components[component];
if (particleComponent.enabled)
m_stride -= Utility::ComponentStride[particleComponent.type];
else
particleComponent.enabled = true;
particleComponent.offset = offset;
particleComponent.type = type;
}
m_stride += Utility::ComponentStride[type];
}
void ParticleDeclaration::GetComponent(ParticleComponent component, bool* enabled, ComponentType* type, unsigned int* offset) const
{
#ifdef NAZARA_DEBUG
if (component > ParticleComponent_Max)
{
NazaraError("Particle component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == ParticleComponent_Unused)
{
NazaraError("Cannot get \"unused\" component");
return;
}
#endif
const Component& particleComponent = m_components[component];
if (enabled)
*enabled = particleComponent.enabled;
if (type)
*type = particleComponent.type;
if (offset)
*offset = particleComponent.offset;
}
unsigned int ParticleDeclaration::GetStride() const
{
return m_stride;
}
void ParticleDeclaration::SetStride(unsigned int stride)
{
m_stride = stride;
}
ParticleDeclaration& ParticleDeclaration::operator=(const ParticleDeclaration& declaration)
{
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(ParticleComponent_Max+1));
m_stride = declaration.m_stride;
return *this;
}
ParticleDeclaration* ParticleDeclaration::Get(ParticleLayout layout)
{
#ifdef NAZARA_DEBUG
if (layout > ParticleLayout_Max)
{
NazaraError("Particle layout out of enum");
return nullptr;
}
#endif
return &s_declarations[layout];
}
bool ParticleDeclaration::IsTypeSupported(ComponentType type)
{
switch (type)
{
case ComponentType_Color:
case ComponentType_Double1:
case ComponentType_Double2:
case ComponentType_Double3:
case ComponentType_Double4:
case ComponentType_Float1:
case ComponentType_Float2:
case ComponentType_Float3:
case ComponentType_Float4:
case ComponentType_Int1:
case ComponentType_Int2:
case ComponentType_Int3:
case ComponentType_Int4:
case ComponentType_Quaternion:
return true;
}
NazaraError("Component type not handled (0x" + String::Number(type, 16) + ')');
return false;
}
try
bool ParticleDeclaration::Initialize()
{
NzErrorFlags flags(nzErrorFlag_Silent | nzErrorFlag_ThrowException);
if (!ParticleDeclarationLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
// Layout : Type
NzParticleDeclaration* declaration;
try
{
ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowException);
// nzParticleLayout_Billboard : NzParticleStruct_Billboard
declaration = &s_declarations[nzParticleLayout_Billboard];
declaration->EnableComponent(nzParticleComponent_Color, nzComponentType_Color, NzOffsetOf(NzParticleStruct_Billboard, color));
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Billboard, life));
declaration->EnableComponent(nzParticleComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, normal));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Float1, NzOffsetOf(NzParticleStruct_Billboard, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, velocity));
// Layout : Type
ParticleDeclaration* declaration;
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Billboard), "Invalid stride for declaration nzParticleLayout_Billboard");
// ParticleLayout_Billboard : ParticleStruct_Billboard
declaration = &s_declarations[ParticleLayout_Billboard];
declaration->EnableComponent(ParticleComponent_Color, ComponentType_Color, NazaraOffsetOf(ParticleStruct_Billboard, color));
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Int1, NazaraOffsetOf(ParticleStruct_Billboard, life));
declaration->EnableComponent(ParticleComponent_Normal, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, normal));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Billboard, rotation));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Billboard, velocity));
// nzParticleLayout_Model : NzParticleStruct_Model
declaration = &s_declarations[nzParticleLayout_Model];
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Model, life));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Model, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Quaternion, NzOffsetOf(NzParticleStruct_Model, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Model, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Billboard), "Invalid stride for declaration ParticleLayout_Billboard");
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Model), "Invalid stride for declaration nzParticleLayout_Model");
// ParticleLayout_Model : ParticleStruct_Model
declaration = &s_declarations[ParticleLayout_Model];
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Int1, NazaraOffsetOf(ParticleStruct_Model, life));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Model, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Quaternion, NazaraOffsetOf(ParticleStruct_Model, rotation));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Model, velocity));
// nzParticleLayout_Sprite : NzParticleStruct_Sprite
declaration = &s_declarations[nzParticleLayout_Sprite];
declaration->EnableComponent(nzParticleComponent_Color, nzComponentType_Color, NzOffsetOf(NzParticleStruct_Sprite, color));
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Sprite, life));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float2, NzOffsetOf(NzParticleStruct_Sprite, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Float1, NzOffsetOf(NzParticleStruct_Sprite, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float2, NzOffsetOf(NzParticleStruct_Sprite, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Model), "Invalid stride for declaration ParticleLayout_Model");
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Sprite), "Invalid stride for declaration nzParticleLayout_Sprite");
}
catch (const std::exception& e)
{
NazaraError("Failed to initialize particle declarations: " + NzString(e.what()));
return false;
// ParticleLayout_Sprite : ParticleStruct_Sprite
declaration = &s_declarations[ParticleLayout_Sprite];
declaration->EnableComponent(ParticleComponent_Color, ComponentType_Color, NazaraOffsetOf(ParticleStruct_Sprite, color));
declaration->EnableComponent(ParticleComponent_Life, ComponentType_Int1, NazaraOffsetOf(ParticleStruct_Sprite, life));
declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float2, NazaraOffsetOf(ParticleStruct_Sprite, position));
declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Sprite, rotation));
declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float2, NazaraOffsetOf(ParticleStruct_Sprite, velocity));
NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Sprite), "Invalid stride for declaration ParticleLayout_Sprite");
}
catch (const std::exception& e)
{
NazaraError("Failed to initialize particle declarations: " + String(e.what()));
return false;
}
return true;
}
return true;
}
void ParticleDeclaration::Uninitialize()
{
ParticleDeclarationLibrary::Uninitialize();
}
void NzParticleDeclaration::Uninitialize()
{
NzParticleDeclarationLibrary::Uninitialize();
ParticleDeclaration ParticleDeclaration::s_declarations[ParticleLayout_Max+1];
ParticleDeclarationLibrary::LibraryMap ParticleDeclaration::s_library;
}
NzParticleDeclaration NzParticleDeclaration::s_declarations[nzParticleLayout_Max+1];
NzParticleDeclarationLibrary::LibraryMap NzParticleDeclaration::s_library;

View File

@@ -12,80 +12,83 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzParticleEmitter::NzParticleEmitter() :
m_lagCompensationEnabled(false),
m_emissionAccumulator(0.f),
m_emissionRate(0.f),
m_emissionCount(1)
namespace Nz
{
}
NzParticleEmitter::~NzParticleEmitter() = default;
void NzParticleEmitter::Emit(NzParticleSystem& system, float elapsedTime) const
{
if (m_emissionRate > 0.f)
ParticleEmitter::ParticleEmitter() :
m_lagCompensationEnabled(false),
m_emissionAccumulator(0.f),
m_emissionRate(0.f),
m_emissionCount(1)
{
// On accumule la partie réelle (pour éviter qu'un taux d'update élevé empêche des particules de se former)
m_emissionAccumulator += elapsedTime*m_emissionRate;
}
float emissionCount = std::floor(m_emissionAccumulator); // Le nombre d'émissions de cette mise à jour
m_emissionAccumulator -= emissionCount; // On enlève la partie entière
ParticleEmitter::~ParticleEmitter() = default;
if (emissionCount >= 1.f)
void ParticleEmitter::Emit(ParticleSystem& system, float elapsedTime) const
{
if (m_emissionRate > 0.f)
{
// On calcule le nombre maximum de particules pouvant être émises cette fois-ci
unsigned int emissionCountInt = static_cast<unsigned int>(emissionCount);
unsigned int maxParticleCount = emissionCountInt*m_emissionCount;
// On accumule la partie réelle (pour éviter qu'un taux d'update élevé empêche des particules de se former)
m_emissionAccumulator += elapsedTime*m_emissionRate;
// On récupère le nombre de particules qu'il est possible de créer selon l'espace libre
unsigned int particleCount = std::min(maxParticleCount, system.GetMaxParticleCount() - system.GetParticleCount());
if (particleCount == 0)
return;
float emissionCount = std::floor(m_emissionAccumulator); // Le nombre d'émissions de cette mise à jour
m_emissionAccumulator -= emissionCount; // On enlève la partie entière
// Et on émet nos particules
void* particles = system.GenerateParticles(particleCount);
NzParticleMapper mapper(particles, system.GetDeclaration());
SetupParticles(mapper, particleCount);
if (m_lagCompensationEnabled)
if (emissionCount >= 1.f)
{
// On va maintenant appliquer les contrôleurs
float invEmissionRate = 1.f/m_emissionRate;
for (unsigned int i = 1; i <= emissionCountInt; ++i)
system.ApplyControllers(mapper, std::min(m_emissionCount*i, particleCount), invEmissionRate);
// On calcule le nombre maximum de particules pouvant être émises cette fois-ci
unsigned int emissionCountInt = static_cast<unsigned int>(emissionCount);
unsigned int maxParticleCount = emissionCountInt*m_emissionCount;
// On récupère le nombre de particules qu'il est possible de créer selon l'espace libre
unsigned int particleCount = std::min(maxParticleCount, system.GetMaxParticleCount() - system.GetParticleCount());
if (particleCount == 0)
return;
// Et on émet nos particules
void* particles = system.GenerateParticles(particleCount);
ParticleMapper mapper(particles, system.GetDeclaration());
SetupParticles(mapper, particleCount);
if (m_lagCompensationEnabled)
{
// On va maintenant appliquer les contrôleurs
float invEmissionRate = 1.f/m_emissionRate;
for (unsigned int i = 1; i <= emissionCountInt; ++i)
system.ApplyControllers(mapper, std::min(m_emissionCount*i, particleCount), invEmissionRate);
}
}
}
}
}
void NzParticleEmitter::EnableLagCompensation(bool enable)
{
m_lagCompensationEnabled = enable;
}
void ParticleEmitter::EnableLagCompensation(bool enable)
{
m_lagCompensationEnabled = enable;
}
unsigned int NzParticleEmitter::GetEmissionCount() const
{
return m_emissionCount;
}
unsigned int ParticleEmitter::GetEmissionCount() const
{
return m_emissionCount;
}
float NzParticleEmitter::GetEmissionRate() const
{
return m_emissionRate;
}
float ParticleEmitter::GetEmissionRate() const
{
return m_emissionRate;
}
bool NzParticleEmitter::IsLagCompensationEnabled() const
{
return m_lagCompensationEnabled;
}
bool ParticleEmitter::IsLagCompensationEnabled() const
{
return m_lagCompensationEnabled;
}
void NzParticleEmitter::SetEmissionCount(unsigned int count)
{
m_emissionCount = count;
}
void ParticleEmitter::SetEmissionCount(unsigned int count)
{
m_emissionCount = count;
}
void NzParticleEmitter::SetEmissionRate(float rate)
{
m_emissionRate = rate;
void ParticleEmitter::SetEmissionRate(float rate)
{
m_emissionRate = rate;
}
}

View File

@@ -5,31 +5,34 @@
#include <Nazara/Graphics/ParticleGenerator.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleGenerator::NzParticleGenerator(const NzParticleGenerator& generator) :
NzRefCounted()
namespace Nz
{
NazaraUnused(generator);
}
NzParticleGenerator::~NzParticleGenerator()
{
OnParticleGeneratorRelease(this);
}
bool NzParticleGenerator::Initialize()
{
if (!NzParticleGeneratorLibrary::Initialize())
ParticleGenerator::ParticleGenerator(const ParticleGenerator& generator) :
RefCounted()
{
NazaraError("Failed to initialise library");
return false;
NazaraUnused(generator);
}
return true;
}
ParticleGenerator::~ParticleGenerator()
{
OnParticleGeneratorRelease(this);
}
void NzParticleGenerator::Uninitialize()
{
NzParticleGeneratorLibrary::Uninitialize();
}
bool ParticleGenerator::Initialize()
{
if (!ParticleGeneratorLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
NzParticleGeneratorLibrary::LibraryMap NzParticleGenerator::s_library;
return true;
}
void ParticleGenerator::Uninitialize()
{
ParticleGeneratorLibrary::Uninitialize();
}
ParticleGeneratorLibrary::LibraryMap ParticleGenerator::s_library;
}

View File

@@ -6,10 +6,14 @@
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleMapper::NzParticleMapper(void* buffer, const NzParticleDeclaration* declaration) :
m_declaration(declaration),
m_ptr(static_cast<nzUInt8*>(buffer))
namespace Nz
{
}
ParticleMapper::ParticleMapper(void* buffer, const ParticleDeclaration* declaration) :
m_declaration(declaration),
m_ptr(static_cast<UInt8*>(buffer))
{
}
NzParticleMapper::~NzParticleMapper() = default;
ParticleMapper::~ParticleMapper() = default;
}

View File

@@ -5,31 +5,34 @@
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleRenderer::NzParticleRenderer(const NzParticleRenderer& renderer) :
NzRefCounted()
namespace Nz
{
NazaraUnused(renderer);
}
NzParticleRenderer::~NzParticleRenderer()
{
OnParticleRendererRelease(this);
}
bool NzParticleRenderer::Initialize()
{
if (!NzParticleRendererLibrary::Initialize())
ParticleRenderer::ParticleRenderer(const ParticleRenderer& renderer) :
RefCounted()
{
NazaraError("Failed to initialise library");
return false;
NazaraUnused(renderer);
}
return true;
}
ParticleRenderer::~ParticleRenderer()
{
OnParticleRendererRelease(this);
}
void NzParticleRenderer::Uninitialize()
{
NzParticleRendererLibrary::Uninitialize();
}
bool ParticleRenderer::Initialize()
{
if (!ParticleRendererLibrary::Initialize())
{
NazaraError("Failed to initialise library");
return false;
}
NzParticleRendererLibrary::LibraryMap NzParticleRenderer::s_library;
return true;
}
void ParticleRenderer::Uninitialize()
{
ParticleRendererLibrary::Uninitialize();
}
ParticleRendererLibrary::LibraryMap ParticleRenderer::s_library;
}

View File

@@ -11,294 +11,296 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, nzParticleLayout layout) :
NzParticleSystem(maxParticleCount, NzParticleDeclaration::Get(layout))
namespace Nz
{
}
NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, NzParticleDeclarationConstRef declaration) :
m_declaration(std::move(declaration)),
m_processing(false),
m_maxParticleCount(maxParticleCount),
m_particleCount(0)
{
// En cas d'erreur, un constructeur ne peut que lancer une exception
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
m_particleSize = m_declaration->GetStride(); // La taille de chaque particule
ResizeBuffer();
}
NzParticleSystem::NzParticleSystem(const NzParticleSystem& system) :
NzRenderable(system),
m_controllers(system.m_controllers),
m_generators(system.m_generators),
m_declaration(system.m_declaration),
m_renderer(system.m_renderer),
m_processing(false),
m_maxParticleCount(system.m_maxParticleCount),
m_particleCount(system.m_particleCount),
m_particleSize(system.m_particleSize)
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
ResizeBuffer();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
}
NzParticleSystem::~NzParticleSystem() = default;
void NzParticleSystem::AddController(NzParticleControllerRef controller)
{
NazaraAssert(controller, "Invalid particle controller");
m_controllers.emplace_back(std::move(controller));
}
void NzParticleSystem::AddEmitter(NzParticleEmitter* emitter)
{
NazaraAssert(emitter, "Invalid particle emitter");
m_emitters.emplace_back(emitter);
}
void NzParticleSystem::AddGenerator(NzParticleGeneratorRef generator)
{
NazaraAssert(generator, "Invalid particle generator");
m_generators.emplace_back(std::move(generator));
}
void NzParticleSystem::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
NazaraAssert(m_renderer, "Invalid particle renderer");
NazaraAssert(renderQueue, "Invalid renderqueue");
NazaraUnused(transformMatrix);
if (m_particleCount > 0)
ParticleSystem::ParticleSystem(unsigned int maxParticleCount, ParticleLayout layout) :
ParticleSystem(maxParticleCount, ParticleDeclaration::Get(layout))
{
NzParticleMapper mapper(m_buffer.data(), m_declaration);
m_renderer->Render(*this, mapper, 0, m_particleCount-1, renderQueue);
}
}
void NzParticleSystem::ApplyControllers(NzParticleMapper& mapper, unsigned int particleCount, float elapsedTime)
{
m_processing = true;
// Pour éviter un verrouillage en cas d'exception
NzCallOnExit onExit([this]()
ParticleSystem::ParticleSystem(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration) :
m_declaration(std::move(declaration)),
m_processing(false),
m_maxParticleCount(maxParticleCount),
m_particleCount(0)
{
// En cas d'erreur, un constructeur ne peut que lancer une exception
ErrorFlags flags(ErrorFlag_ThrowException, true);
m_particleSize = m_declaration->GetStride(); // La taille de chaque particule
ResizeBuffer();
}
ParticleSystem::ParticleSystem(const ParticleSystem& system) :
Renderable(system),
m_controllers(system.m_controllers),
m_generators(system.m_generators),
m_declaration(system.m_declaration),
m_renderer(system.m_renderer),
m_processing(false),
m_maxParticleCount(system.m_maxParticleCount),
m_particleCount(system.m_particleCount),
m_particleSize(system.m_particleSize)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
ResizeBuffer();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
}
ParticleSystem::~ParticleSystem() = default;
void ParticleSystem::AddController(ParticleControllerRef controller)
{
NazaraAssert(controller, "Invalid particle controller");
m_controllers.emplace_back(std::move(controller));
}
void ParticleSystem::AddEmitter(ParticleEmitter* emitter)
{
NazaraAssert(emitter, "Invalid particle emitter");
m_emitters.emplace_back(emitter);
}
void ParticleSystem::AddGenerator(ParticleGeneratorRef generator)
{
NazaraAssert(generator, "Invalid particle generator");
m_generators.emplace_back(std::move(generator));
}
void ParticleSystem::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const
{
NazaraAssert(m_renderer, "Invalid particle renderer");
NazaraAssert(renderQueue, "Invalid renderqueue");
NazaraUnused(transformMatrix);
if (m_particleCount > 0)
{
ParticleMapper mapper(m_buffer.data(), m_declaration);
m_renderer->Render(*this, mapper, 0, m_particleCount-1, renderQueue);
}
}
void ParticleSystem::ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime)
{
m_processing = true;
// Pour éviter un verrouillage en cas d'exception
CallOnExit onExit([this]()
{
m_processing = false;
});
for (ParticleController* controller : m_controllers)
controller->Apply(*this, mapper, 0, particleCount-1, elapsedTime);
onExit.CallAndReset();
// On tue maintenant les particules mortes durant la mise à jour
if (m_dyingParticles.size() < m_particleCount)
{
// On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater
// La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer,
// sans cette solution certaines particules pourraient échapper à la mort
for (unsigned int index : m_dyingParticles)
KillParticle(index);
}
else
KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide
m_dyingParticles.clear();
}
void* ParticleSystem::CreateParticle()
{
return CreateParticles(1);
}
void* ParticleSystem::CreateParticles(unsigned int count)
{
if (count == 0)
return nullptr;
if (m_particleCount + count > m_maxParticleCount)
return nullptr;
unsigned int particlesIndex = m_particleCount;
m_particleCount += count;
return &m_buffer[particlesIndex*m_particleSize];
}
void* ParticleSystem::GenerateParticle()
{
return GenerateParticles(1);
}
void* ParticleSystem::GenerateParticles(unsigned int count)
{
void* ptr = CreateParticles(count);
if (!ptr)
return nullptr;
ParticleMapper mapper(ptr, m_declaration);
for (ParticleGenerator* generator : m_generators)
generator->Generate(*this, mapper, 0, count-1);
return ptr;
}
const ParticleDeclarationConstRef& ParticleSystem::GetDeclaration() const
{
return m_declaration;
}
float ParticleSystem::GetFixedStepSize() const
{
return m_stepSize;
}
unsigned int ParticleSystem::GetMaxParticleCount() const
{
return m_maxParticleCount;
}
unsigned int ParticleSystem::GetParticleCount() const
{
return m_particleCount;
}
unsigned int ParticleSystem::GetParticleSize() const
{
return m_particleSize;
}
void ParticleSystem::KillParticle(unsigned int index)
{
///FIXME: Vérifier index
if (m_processing)
{
// Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste d'attente
m_dyingParticles.insert(index);
return;
}
// On déplace la dernière particule vivante à la place de celle-ci
if (--m_particleCount > 0)
std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize);
}
void ParticleSystem::KillParticles()
{
m_particleCount = 0;
}
void ParticleSystem::RemoveController(ParticleController* controller)
{
auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
if (it != m_controllers.end())
m_controllers.erase(it);
}
void ParticleSystem::RemoveEmitter(ParticleEmitter* emitter)
{
auto it = std::find(m_emitters.begin(), m_emitters.end(), emitter);
if (it != m_emitters.end())
m_emitters.erase(it);
}
void ParticleSystem::RemoveGenerator(ParticleGenerator* generator)
{
auto it = std::find(m_generators.begin(), m_generators.end(), generator);
if (it != m_generators.end())
m_generators.erase(it);
}
void ParticleSystem::SetFixedStepSize(float stepSize)
{
m_stepSize = stepSize;
}
void ParticleSystem::SetRenderer(ParticleRenderer* renderer)
{
m_renderer = renderer;
}
void ParticleSystem::Update(float elapsedTime)
{
// Émission
for (ParticleEmitter* emitter : m_emitters)
emitter->Emit(*this, elapsedTime);
// Mise à jour
if (m_particleCount > 0)
{
///TODO: Mettre à jour en utilisant des threads
ParticleMapper mapper(m_buffer.data(), m_declaration);
ApplyControllers(mapper, m_particleCount, elapsedTime);
}
}
void ParticleSystem::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
NazaraUnused(transformMatrix);
// Nothing to do here (our bounding volume is global)
}
ParticleSystem& ParticleSystem::operator=(const ParticleSystem& system)
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
Renderable::operator=(system);
m_controllers = system.m_controllers;
m_declaration = system.m_declaration;
m_generators = system.m_generators;
m_maxParticleCount = system.m_maxParticleCount;
m_particleCount = system.m_particleCount;
m_particleSize = system.m_particleSize;
m_renderer = system.m_renderer;
m_stepSize = system.m_stepSize;
// La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier
m_dyingParticles.clear();
m_processing = false;
});
m_stepAccumulator = 0.f;
for (NzParticleController* controller : m_controllers)
controller->Apply(*this, mapper, 0, particleCount-1, elapsedTime);
m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose
ResizeBuffer();
onExit.CallAndReset();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
// On tue maintenant les particules mortes durant la mise à jour
if (m_dyingParticles.size() < m_particleCount)
{
// On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater
// La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer,
// sans cette solution certaines particules pourraient échapper à la mort
for (unsigned int index : m_dyingParticles)
KillParticle(index);
}
else
KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide
m_dyingParticles.clear();
}
void* NzParticleSystem::CreateParticle()
{
return CreateParticles(1);
}
void* NzParticleSystem::CreateParticles(unsigned int count)
{
if (count == 0)
return nullptr;
if (m_particleCount + count > m_maxParticleCount)
return nullptr;
unsigned int particlesIndex = m_particleCount;
m_particleCount += count;
return &m_buffer[particlesIndex*m_particleSize];
}
void* NzParticleSystem::GenerateParticle()
{
return GenerateParticles(1);
}
void* NzParticleSystem::GenerateParticles(unsigned int count)
{
void* ptr = CreateParticles(count);
if (!ptr)
return nullptr;
NzParticleMapper mapper(ptr, m_declaration);
for (NzParticleGenerator* generator : m_generators)
generator->Generate(*this, mapper, 0, count-1);
return ptr;
}
const NzParticleDeclarationConstRef& NzParticleSystem::GetDeclaration() const
{
return m_declaration;
}
float NzParticleSystem::GetFixedStepSize() const
{
return m_stepSize;
}
unsigned int NzParticleSystem::GetMaxParticleCount() const
{
return m_maxParticleCount;
}
unsigned int NzParticleSystem::GetParticleCount() const
{
return m_particleCount;
}
unsigned int NzParticleSystem::GetParticleSize() const
{
return m_particleSize;
}
void NzParticleSystem::KillParticle(unsigned int index)
{
///FIXME: Vérifier index
if (m_processing)
{
// Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste d'attente
m_dyingParticles.insert(index);
return;
return *this;
}
// On déplace la dernière particule vivante à la place de celle-ci
if (--m_particleCount > 0)
std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize);
}
void NzParticleSystem::KillParticles()
{
m_particleCount = 0;
}
void NzParticleSystem::RemoveController(NzParticleController* controller)
{
auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
if (it != m_controllers.end())
m_controllers.erase(it);
}
void NzParticleSystem::RemoveEmitter(NzParticleEmitter* emitter)
{
auto it = std::find(m_emitters.begin(), m_emitters.end(), emitter);
if (it != m_emitters.end())
m_emitters.erase(it);
}
void NzParticleSystem::RemoveGenerator(NzParticleGenerator* generator)
{
auto it = std::find(m_generators.begin(), m_generators.end(), generator);
if (it != m_generators.end())
m_generators.erase(it);
}
void NzParticleSystem::SetFixedStepSize(float stepSize)
{
m_stepSize = stepSize;
}
void NzParticleSystem::SetRenderer(NzParticleRenderer* renderer)
{
m_renderer = renderer;
}
void NzParticleSystem::Update(float elapsedTime)
{
// Émission
for (NzParticleEmitter* emitter : m_emitters)
emitter->Emit(*this, elapsedTime);
// Mise à jour
if (m_particleCount > 0)
void ParticleSystem::MakeBoundingVolume() const
{
///TODO: Mettre à jour en utilisant des threads
NzParticleMapper mapper(m_buffer.data(), m_declaration);
ApplyControllers(mapper, m_particleCount, elapsedTime);
///TODO: Calculer l'AABB (prendre la taille des particules en compte s'il y a)
m_boundingVolume.MakeInfinite();
}
void ParticleSystem::ResizeBuffer()
{
// Histoire de décrire un peu mieux l'erreur en cas d'échec
try
{
m_buffer.resize(m_maxParticleCount*m_particleSize);
}
catch (const std::exception& e)
{
StringStream stream;
stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;
NazaraError(stream.ToString());
}
}
}
void NzParticleSystem::UpdateBoundingVolume(const NzMatrix4f& transformMatrix)
{
NazaraUnused(transformMatrix);
// Nothing to do here (our bounding volume is global)
}
NzParticleSystem& NzParticleSystem::operator=(const NzParticleSystem& system)
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
NzRenderable::operator=(system);
m_controllers = system.m_controllers;
m_declaration = system.m_declaration;
m_generators = system.m_generators;
m_maxParticleCount = system.m_maxParticleCount;
m_particleCount = system.m_particleCount;
m_particleSize = system.m_particleSize;
m_renderer = system.m_renderer;
m_stepSize = system.m_stepSize;
// La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier
m_dyingParticles.clear();
m_processing = false;
m_stepAccumulator = 0.f;
m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose
ResizeBuffer();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
return *this;
}
void NzParticleSystem::MakeBoundingVolume() const
{
///TODO: Calculer l'AABB (prendre la taille des particules en compte s'il y a)
m_boundingVolume.MakeInfinite();
}
void NzParticleSystem::ResizeBuffer()
{
// Histoire de décrire un peu mieux l'erreur en cas d'échec
try
{
m_buffer.resize(m_maxParticleCount*m_particleSize);
}
catch (const std::exception& e)
{
NzStringStream stream;
stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;
NazaraError(stream.ToString());
}
}

View File

@@ -9,157 +9,160 @@
#include <unordered_map>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
const char* techniquesName[] =
namespace
{
"Advanced Forward",
"Basic Forward",
"Deferred Shading",
"Light Pre-Pass",
"User"
};
static_assert(sizeof(techniquesName)/sizeof(const char*) == nzRenderTechniqueType_Max+1, "Render technique type name array is incomplete");
struct RenderTechnique
{
NzRenderTechniques::RenderTechniqueFactory factory;
int ranking;
};
std::unordered_map<NzString, RenderTechnique> s_renderTechniques;
}
NzAbstractRenderTechnique* NzRenderTechniques::GetByEnum(nzRenderTechniqueType renderTechnique, int* techniqueRanking)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > nzRenderTechniqueType_Max)
{
NazaraError("Render technique type out of enum");
return nullptr;
}
#endif
return GetByName(techniquesName[renderTechnique], techniqueRanking);
}
NzAbstractRenderTechnique* NzRenderTechniques::GetByIndex(unsigned int index, int* techniqueRanking)
{
#if NAZARA_GRAPHICS_SAFE
if (index >= s_renderTechniques.size())
{
NazaraError("Technique index out of range (" + NzString::Number(index) + " >= " + NzString::Number(s_renderTechniques.size()) + ')');
return nullptr;
}
#endif
auto it = s_renderTechniques.begin();
std::advance(it, index);
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
NzAbstractRenderTechnique* NzRenderTechniques::GetByName(const NzString& name, int* techniqueRanking)
{
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
{
NazaraError("Technique name cannot be empty");
return nullptr;
}
#endif
auto it = s_renderTechniques.find(name);
if (it == s_renderTechniques.end())
{
NazaraError("Technique not found");
return nullptr;
}
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
NzAbstractRenderTechnique* NzRenderTechniques::GetByRanking(int maxRanking, int* techniqueRanking)
{
if (maxRanking < 0)
maxRanking = std::numeric_limits<int>::max();
int currentRanking = -1;
RenderTechnique* technique = nullptr;
for (auto it = s_renderTechniques.begin(); it != s_renderTechniques.end(); ++it)
{
int ranking = it->second.ranking;
if (ranking > currentRanking && ranking <= maxRanking)
const char* techniquesName[] =
{
currentRanking = ranking;
technique = &(it->second);
"Advanced Forward",
"Basic Forward",
"Deferred Shading",
"Light Pre-Pass",
"User"
};
static_assert(sizeof(techniquesName)/sizeof(const char*) == RenderTechniqueType_Max+1, "Render technique type name array is incomplete");
struct RenderTechnique
{
RenderTechniques::RenderTechniqueFactory factory;
int ranking;
};
std::unordered_map<String, RenderTechnique> s_renderTechniques;
}
AbstractRenderTechnique* RenderTechniques::GetByEnum(RenderTechniqueType renderTechnique, int* techniqueRanking)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > RenderTechniqueType_Max)
{
NazaraError("Render technique type out of enum");
return nullptr;
}
#endif
return GetByName(techniquesName[renderTechnique], techniqueRanking);
}
if (!technique)
AbstractRenderTechnique* RenderTechniques::GetByIndex(unsigned int index, int* techniqueRanking)
{
NazaraError("No technique found");
return nullptr;
#if NAZARA_GRAPHICS_SAFE
if (index >= s_renderTechniques.size())
{
NazaraError("Technique index out of range (" + String::Number(index) + " >= " + String::Number(s_renderTechniques.size()) + ')');
return nullptr;
}
#endif
auto it = s_renderTechniques.begin();
std::advance(it, index);
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
if (techniqueRanking)
*techniqueRanking = currentRanking;
return technique->factory();
}
unsigned int NzRenderTechniques::GetCount()
{
return s_renderTechniques.size();
}
void NzRenderTechniques::Register(const NzString& name, int ranking, RenderTechniqueFactory factory)
{
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
AbstractRenderTechnique* RenderTechniques::GetByName(const String& name, int* techniqueRanking)
{
NazaraError("Technique name cannot be empty");
return;
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
{
NazaraError("Technique name cannot be empty");
return nullptr;
}
#endif
auto it = s_renderTechniques.find(name);
if (it == s_renderTechniques.end())
{
NazaraError("Technique not found");
return nullptr;
}
if (techniqueRanking)
*techniqueRanking = it->second.ranking;
return it->second.factory();
}
if (ranking < 0)
AbstractRenderTechnique* RenderTechniques::GetByRanking(int maxRanking, int* techniqueRanking)
{
NazaraError("Technique ranking cannot be negative");
return;
if (maxRanking < 0)
maxRanking = std::numeric_limits<int>::max();
int currentRanking = -1;
RenderTechnique* technique = nullptr;
for (auto it = s_renderTechniques.begin(); it != s_renderTechniques.end(); ++it)
{
int ranking = it->second.ranking;
if (ranking > currentRanking && ranking <= maxRanking)
{
currentRanking = ranking;
technique = &(it->second);
}
}
if (!technique)
{
NazaraError("No technique found");
return nullptr;
}
if (techniqueRanking)
*techniqueRanking = currentRanking;
return technique->factory();
}
if (!factory)
unsigned int RenderTechniques::GetCount()
{
NazaraError("Technique function must be valid");
return;
return s_renderTechniques.size();
}
#endif
s_renderTechniques[name] = {factory, ranking};
}
NzString NzRenderTechniques::ToString(nzRenderTechniqueType renderTechnique)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > nzRenderTechniqueType_Max)
void RenderTechniques::Register(const String& name, int ranking, RenderTechniqueFactory factory)
{
NazaraError("Render technique type out of enum");
return NzString("Error");
#if NAZARA_GRAPHICS_SAFE
if (name.IsEmpty())
{
NazaraError("Technique name cannot be empty");
return;
}
if (ranking < 0)
{
NazaraError("Technique ranking cannot be negative");
return;
}
if (!factory)
{
NazaraError("Technique function must be valid");
return;
}
#endif
s_renderTechniques[name] = {factory, ranking};
}
#endif
return techniquesName[renderTechnique];
}
String RenderTechniques::ToString(RenderTechniqueType renderTechnique)
{
#ifdef NAZARA_DEBUG
if (renderTechnique > RenderTechniqueType_Max)
{
NazaraError("Render technique type out of enum");
return String("Error");
}
#endif
void NzRenderTechniques::Unregister(const NzString& name)
{
s_renderTechniques.erase(name);
return techniquesName[renderTechnique];
}
void RenderTechniques::Unregister(const String& name)
{
s_renderTechniques.erase(name);
}
}

View File

@@ -5,23 +5,26 @@
#include <Nazara/Graphics/Renderable.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzRenderable::~NzRenderable() = default;
bool NzRenderable::Cull(const NzFrustumf& frustum, const NzMatrix4f& transformMatrix) const
namespace Nz
{
NazaraUnused(transformMatrix);
Renderable::~Renderable() = default;
return frustum.Contains(m_boundingVolume);
}
const NzBoundingVolumef& NzRenderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
void NzRenderable::UpdateBoundingVolume(const NzMatrix4f& transformMatrix)
{
m_boundingVolume.Update(transformMatrix);
bool Renderable::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const
{
NazaraUnused(transformMatrix);
return frustum.Contains(m_boundingVolume);
}
const BoundingVolumef& Renderable::GetBoundingVolume() const
{
EnsureBoundingVolumeUpdated();
return m_boundingVolume;
}
void Renderable::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
m_boundingVolume.Update(transformMatrix);
}
}

View File

@@ -12,276 +12,279 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
bool NzSkeletalModelParameters::IsValid() const
namespace Nz
{
if (!NzModelParameters::IsValid())
return false;
if (loadAnimation && !animation.IsValid())
return false;
return true;
}
NzSkeletalModel::NzSkeletalModel() :
m_currentSequence(nullptr),
m_animationEnabled(true)
{
}
void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
if (!m_mesh)
return;
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
bool SkeletalModelParameters::IsValid() const
{
const NzSkeletalMesh* mesh = static_cast<const NzSkeletalMesh*>(m_mesh->GetSubMesh(i));
const NzMaterial* material = m_materials[mesh->GetMaterialIndex()];
if (!ModelParameters::IsValid())
return false;
NzMeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = NzSkinningManager::GetBuffer(mesh, &m_skeleton);
if (loadAnimation && !animation.IsValid())
return false;
renderQueue->AddMesh(material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix);
return true;
}
}
void NzSkeletalModel::AdvanceAnimation(float elapsedTime)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
SkeletalModel::SkeletalModel() :
m_currentSequence(nullptr),
m_animationEnabled(true)
{
NazaraError("Model has no animation");
return;
}
#endif
m_interpolation += m_currentSequence->frameRate * elapsedTime;
while (m_interpolation > 1.f)
void SkeletalModel::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
m_interpolation -= 1.f;
if (!m_mesh)
return;
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
if (m_nextFrame+1 > lastFrame)
unsigned int submeshCount = m_mesh->GetSubMeshCount();
for (unsigned int i = 0; i < submeshCount; ++i)
{
if (m_animation->IsLoopPointInterpolationEnabled())
const SkeletalMesh* mesh = static_cast<const SkeletalMesh*>(m_mesh->GetSubMesh(i));
const Material* material = m_materials[mesh->GetMaterialIndex()];
MeshData meshData;
meshData.indexBuffer = mesh->GetIndexBuffer();
meshData.primitiveMode = mesh->GetPrimitiveMode();
meshData.vertexBuffer = SkinningManager::GetBuffer(mesh, &m_skeleton);
renderQueue->AddMesh(material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix);
}
}
void SkeletalModel::AdvanceAnimation(float elapsedTime)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return;
}
#endif
m_interpolation += m_currentSequence->frameRate * elapsedTime;
while (m_interpolation > 1.f)
{
m_interpolation -= 1.f;
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
if (m_nextFrame+1 > lastFrame)
{
m_currentFrame = m_nextFrame;
m_nextFrame = m_currentSequence->firstFrame;
if (m_animation->IsLoopPointInterpolationEnabled())
{
m_currentFrame = m_nextFrame;
m_nextFrame = m_currentSequence->firstFrame;
}
else
{
m_currentFrame = m_currentSequence->firstFrame;
m_nextFrame = m_currentFrame+1;
}
}
else
{
m_currentFrame = m_currentSequence->firstFrame;
m_nextFrame = m_currentFrame+1;
m_currentFrame = m_nextFrame;
m_nextFrame++;
}
}
else
{
m_currentFrame = m_nextFrame;
m_nextFrame++;
}
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
InvalidateBoundingVolume();
}
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
InvalidateBoundingVolume();
}
NzSkeletalModel* NzSkeletalModel::Clone() const
{
return new NzSkeletalModel(*this);
}
NzSkeletalModel* NzSkeletalModel::Create() const
{
return new NzSkeletalModel;
}
void NzSkeletalModel::EnableAnimation(bool animation)
{
m_animationEnabled = animation;
}
NzAnimation* NzSkeletalModel::GetAnimation() const
{
return m_animation;
}
NzSkeleton* NzSkeletalModel::GetSkeleton()
{
InvalidateBoundingVolume();
return &m_skeleton;
}
const NzSkeleton* NzSkeletalModel::GetSkeleton() const
{
return &m_skeleton;
}
bool NzSkeletalModel::HasAnimation() const
{
return m_animation != nullptr;
}
bool NzSkeletalModel::IsAnimated() const
{
return true;
}
bool NzSkeletalModel::IsAnimationEnabled() const
{
return m_animationEnabled;
}
bool NzSkeletalModel::LoadFromFile(const NzString& filePath, const NzSkeletalModelParameters& params)
{
return NzSkeletalModelLoader::LoadFromFile(this, filePath, params);
}
bool NzSkeletalModel::LoadFromMemory(const void* data, std::size_t size, const NzSkeletalModelParameters& params)
{
return NzSkeletalModelLoader::LoadFromMemory(this, data, size, params);
}
bool NzSkeletalModel::LoadFromStream(NzInputStream& stream, const NzSkeletalModelParameters& params)
{
return NzSkeletalModelLoader::LoadFromStream(this, stream, params);
}
void NzSkeletalModel::Reset()
{
NzModel::Reset();
m_skeleton.Destroy();
}
bool NzSkeletalModel::SetAnimation(NzAnimation* animation)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_mesh)
SkeletalModel* SkeletalModel::Clone() const
{
NazaraError("Model has no mesh");
return false;
return new SkeletalModel(*this);
}
if (animation)
SkeletalModel* SkeletalModel::Create() const
{
if (!animation->IsValid())
return new SkeletalModel;
}
void SkeletalModel::EnableAnimation(bool animation)
{
m_animationEnabled = animation;
}
Animation* SkeletalModel::GetAnimation() const
{
return m_animation;
}
Skeleton* SkeletalModel::GetSkeleton()
{
InvalidateBoundingVolume();
return &m_skeleton;
}
const Skeleton* SkeletalModel::GetSkeleton() const
{
return &m_skeleton;
}
bool SkeletalModel::HasAnimation() const
{
return m_animation != nullptr;
}
bool SkeletalModel::IsAnimated() const
{
return true;
}
bool SkeletalModel::IsAnimationEnabled() const
{
return m_animationEnabled;
}
bool SkeletalModel::LoadFromFile(const String& filePath, const SkeletalModelParameters& params)
{
return SkeletalModelLoader::LoadFromFile(this, filePath, params);
}
bool SkeletalModel::LoadFromMemory(const void* data, std::size_t size, const SkeletalModelParameters& params)
{
return SkeletalModelLoader::LoadFromMemory(this, data, size, params);
}
bool SkeletalModel::LoadFromStream(InputStream& stream, const SkeletalModelParameters& params)
{
return SkeletalModelLoader::LoadFromStream(this, stream, params);
}
void SkeletalModel::Reset()
{
Model::Reset();
m_skeleton.Destroy();
}
bool SkeletalModel::SetAnimation(Animation* animation)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_mesh)
{
NazaraError("Invalid animation");
NazaraError("Model has no mesh");
return false;
}
if (animation->GetType() != m_mesh->GetAnimationType())
if (animation)
{
NazaraError("Animation type must match mesh animation type");
if (!animation->IsValid())
{
NazaraError("Invalid animation");
return false;
}
if (animation->GetType() != m_mesh->GetAnimationType())
{
NazaraError("Animation type must match mesh animation type");
return false;
}
if (animation->GetJointCount() != m_mesh->GetJointCount())
{
NazaraError("Animation joint count must match mesh joint count");
return false;
}
}
#endif
m_animation = animation;
if (m_animation)
{
m_currentFrame = 0;
m_interpolation = 0.f;
SetSequence(0);
}
return true;
}
void SkeletalModel::SetMesh(Mesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && mesh->GetAnimationType() != AnimationType_Skeletal)
{
NazaraError("Mesh animation type must be skeletal");
return;
}
#endif
Model::SetMesh(mesh);
if (m_mesh)
{
if (m_animation && m_animation->GetJointCount() != m_mesh->GetJointCount())
{
NazaraWarning("Animation joint count is not matching new mesh joint count, disabling animation...");
SetAnimation(nullptr);
}
m_skeleton = *m_mesh->GetSkeleton(); // Copie du squelette template
}
}
bool SkeletalModel::SetSequence(const String& sequenceName)
{
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return false;
}
#endif
const Sequence* currentSequence = m_animation->GetSequence(sequenceName);
if (!currentSequence)
{
NazaraError("Sequence not found");
return false;
}
if (animation->GetJointCount() != m_mesh->GetJointCount())
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
return true;
}
void SkeletalModel::SetSequence(unsigned int sequenceIndex)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Animation joint count must match mesh joint count");
return false;
NazaraError("Model has no animation");
return;
}
}
#endif
#endif
m_animation = animation;
if (m_animation)
{
m_currentFrame = 0;
m_interpolation = 0.f;
SetSequence(0);
}
return true;
}
void NzSkeletalModel::SetMesh(NzMesh* mesh)
{
#if NAZARA_GRAPHICS_SAFE
if (mesh && mesh->GetAnimationType() != nzAnimationType_Skeletal)
{
NazaraError("Mesh animation type must be skeletal");
return;
}
#endif
NzModel::SetMesh(mesh);
if (m_mesh)
{
if (m_animation && m_animation->GetJointCount() != m_mesh->GetJointCount())
const Sequence* currentSequence = m_animation->GetSequence(sequenceIndex);
#if NAZARA_GRAPHICS_SAFE
if (!currentSequence)
{
NazaraWarning("Animation joint count is not matching new mesh joint count, disabling animation...");
SetAnimation(nullptr);
NazaraError("Sequence not found");
return;
}
#endif
m_skeleton = *m_mesh->GetSkeleton(); // Copie du squelette template
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
}
}
bool NzSkeletalModel::SetSequence(const NzString& sequenceName)
{
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
void SkeletalModel::MakeBoundingVolume() const
{
NazaraError("Model has no animation");
return false;
m_boundingVolume.Set(m_skeleton.GetAABB());
}
#endif
const NzSequence* currentSequence = m_animation->GetSequence(sequenceName);
if (!currentSequence)
void SkeletalModel::Update()
{
NazaraError("Sequence not found");
return false;
/*if (m_animationEnabled && m_animation)
AdvanceAnimation(m_scene->GetUpdateTime());*/
}
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
return true;
SkeletalModelLoader::LoaderList SkeletalModel::s_loaders;
}
void NzSkeletalModel::SetSequence(unsigned int sequenceIndex)
{
#if NAZARA_GRAPHICS_SAFE
if (!m_animation)
{
NazaraError("Model has no animation");
return;
}
#endif
const NzSequence* currentSequence = m_animation->GetSequence(sequenceIndex);
#if NAZARA_GRAPHICS_SAFE
if (!currentSequence)
{
NazaraError("Sequence not found");
return;
}
#endif
m_currentSequence = currentSequence;
m_nextFrame = m_currentSequence->firstFrame;
}
void NzSkeletalModel::MakeBoundingVolume() const
{
m_boundingVolume.Set(m_skeleton.GetAABB());
}
void NzSkeletalModel::Update()
{
/*if (m_animationEnabled && m_animation)
AdvanceAnimation(m_scene->GetUpdateTime());*/
}
NzSkeletalModelLoader::LoaderList NzSkeletalModel::s_loaders;

View File

@@ -13,183 +13,186 @@
#include <unordered_map>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
struct BufferData
{
NazaraSlot(NzSkeletalMesh, OnSkeletalMeshDestroy, skeletalMeshDestroySlot);
NzVertexBufferRef buffer;
bool updated;
};
using MeshMap = std::unordered_map<const NzSkeletalMesh*, BufferData>;
struct MeshData
namespace
{
NazaraSlot(NzSkeleton, OnSkeletonDestroy, skeletonDestroySlot);
NazaraSlot(NzSkeleton, OnSkeletonJointsInvalidated, skeletonJointsInvalidatedSlot);
MeshMap meshMap;
};
struct SkinningData
{
const NzSkeletalMesh* mesh;
const NzSkeleton* skeleton;
NzVertexBuffer* buffer;
};
using SkeletonMap = std::unordered_map<const NzSkeleton*, MeshData>;
SkeletonMap s_cache;
std::vector<SkinningData> s_skinningQueue;
void Skin_MonoCPU(const NzSkeletalMesh* mesh, const NzSkeleton* skeleton, NzVertexBuffer* buffer)
{
NzBufferMapper<NzVertexBuffer> inputMapper(mesh->GetVertexBuffer(), nzBufferAccess_ReadOnly);
NzBufferMapper<NzVertexBuffer> outputMapper(buffer, nzBufferAccess_DiscardAndWrite);
NzSkinningData skinningData;
skinningData.inputVertex = static_cast<NzSkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<NzMeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
NzSkinPositionNormalTangent(skinningData, 0, mesh->GetVertexCount());
}
void Skin_MultiCPU(const NzSkeletalMesh* mesh, const NzSkeleton* skeleton, NzVertexBuffer* buffer)
{
NzBufferMapper<NzVertexBuffer> inputMapper(mesh->GetVertexBuffer(), nzBufferAccess_ReadOnly);
NzBufferMapper<NzVertexBuffer> outputMapper(buffer, nzBufferAccess_DiscardAndWrite);
NzSkinningData skinningData;
skinningData.inputVertex = static_cast<NzSkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<NzMeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
// Afin d'empêcher les différents threads de vouloir mettre à jour la même matrice en même temps,
// on se charge de la mettre à jour avant de les lancer
unsigned int jointCount = skeleton->GetJointCount();
for (unsigned int i = 0; i < jointCount; ++i)
skinningData.joints[i].EnsureSkinningMatrixUpdate();
unsigned int workerCount = NzTaskScheduler::GetWorkerCount();
std::ldiv_t div = std::ldiv(mesh->GetVertexCount(), workerCount);
for (unsigned int i = 0; i < workerCount; ++i)
NzTaskScheduler::AddTask(NzSkinPositionNormalTangent, skinningData, i*div.quot, (i == workerCount-1) ? div.quot + div.rem : div.quot);
NzTaskScheduler::Run();
NzTaskScheduler::WaitForTasks();
}
}
NzVertexBuffer* NzSkinningManager::GetBuffer(const NzSkeletalMesh* mesh, const NzSkeleton* skeleton)
{
#if NAZARA_GRAPHICS_SAFE
if (!mesh)
{
NazaraError("Invalid mesh");
return nullptr;
}
if (!skeleton)
{
NazaraError("Invalid skeleton");
return nullptr;
}
#endif
NzErrorFlags flags(nzErrorFlag_ThrowException);
SkeletonMap::iterator it = s_cache.find(skeleton);
if (it == s_cache.end())
{
MeshData meshData;
meshData.skeletonDestroySlot.Connect(skeleton->OnSkeletonDestroy, OnSkeletonRelease);
meshData.skeletonJointsInvalidatedSlot.Connect(skeleton->OnSkeletonJointsInvalidated, OnSkeletonInvalidated);
it = s_cache.insert(std::make_pair(skeleton, std::move(meshData))).first;
}
NzVertexBuffer* buffer;
MeshMap& meshMap = it->second.meshMap;
MeshMap::iterator it2 = meshMap.find(mesh);
if (it2 == meshMap.end())
{
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), mesh->GetVertexCount(), nzDataStorage_Hardware, nzBufferUsage_Dynamic);
BufferData data;
data.skeletalMeshDestroySlot.Connect(mesh->OnSkeletalMeshDestroy, OnSkeletalMeshDestroy);
data.buffer = vertexBuffer;
data.updated = true;
meshMap.insert(std::make_pair(mesh, std::move(data)));
s_skinningQueue.push_back(SkinningData{mesh, skeleton, vertexBuffer});
buffer = vertexBuffer;
}
else
{
BufferData& data = it2->second;
if (!data.updated)
struct BufferData
{
s_skinningQueue.push_back(SkinningData{mesh, skeleton, data.buffer});
data.updated = true;
NazaraSlot(SkeletalMesh, OnSkeletalMeshDestroy, skeletalMeshDestroySlot);
VertexBufferRef buffer;
bool updated;
};
using MeshMap = std::unordered_map<const SkeletalMesh*, BufferData>;
struct MeshData
{
NazaraSlot(Skeleton, OnSkeletonDestroy, skeletonDestroySlot);
NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, skeletonJointsInvalidatedSlot);
MeshMap meshMap;
};
struct QueueData
{
const SkeletalMesh* mesh;
const Skeleton* skeleton;
VertexBuffer* buffer;
};
using SkeletonMap = std::unordered_map<const Skeleton*, MeshData>;
SkeletonMap s_cache;
std::vector<QueueData> s_skinningQueue;
void Skin_MonoCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer)
{
BufferMapper<VertexBuffer> inputMapper(mesh->GetVertexBuffer(), BufferAccess_ReadOnly);
BufferMapper<VertexBuffer> outputMapper(buffer, BufferAccess_DiscardAndWrite);
SkinningData skinningData;
skinningData.inputVertex = static_cast<SkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<MeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
SkinPositionNormalTangent(skinningData, 0, mesh->GetVertexCount());
}
buffer = data.buffer;
void Skin_MultiCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer)
{
BufferMapper<VertexBuffer> inputMapper(mesh->GetVertexBuffer(), BufferAccess_ReadOnly);
BufferMapper<VertexBuffer> outputMapper(buffer, BufferAccess_DiscardAndWrite);
SkinningData skinningData;
skinningData.inputVertex = static_cast<SkeletalMeshVertex*>(inputMapper.GetPointer());
skinningData.outputVertex = static_cast<MeshVertex*>(outputMapper.GetPointer());
skinningData.joints = skeleton->GetJoints();
// Afin d'empêcher les différents threads de vouloir mettre à jour la même matrice en même temps,
// on se charge de la mettre à jour avant de les lancer
unsigned int jointCount = skeleton->GetJointCount();
for (unsigned int i = 0; i < jointCount; ++i)
skinningData.joints[i].EnsureSkinningMatrixUpdate();
unsigned int workerCount = TaskScheduler::GetWorkerCount();
std::ldiv_t div = std::ldiv(mesh->GetVertexCount(), workerCount);
for (unsigned int i = 0; i < workerCount; ++i)
TaskScheduler::AddTask(SkinPositionNormalTangent, skinningData, i*div.quot, (i == workerCount-1) ? div.quot + div.rem : div.quot);
TaskScheduler::Run();
TaskScheduler::WaitForTasks();
}
}
return buffer;
}
void NzSkinningManager::Skin()
{
for (SkinningData& data : s_skinningQueue)
s_skinFunc(data.mesh, data.skeleton, data.buffer);
s_skinningQueue.clear();
}
bool NzSkinningManager::Initialize()
{
///TODO: GPU Skinning
if (NzTaskScheduler::Initialize())
s_skinFunc = Skin_MultiCPU;
else
s_skinFunc = Skin_MonoCPU;
return true; // Rien de particulier à faire
}
void NzSkinningManager::OnSkeletalMeshDestroy(const NzSkeletalMesh* mesh)
{
for (auto& pair : s_cache)
VertexBuffer* SkinningManager::GetBuffer(const SkeletalMesh* mesh, const Skeleton* skeleton)
{
MeshMap& meshMap = pair.second.meshMap;
meshMap.erase(mesh);
#if NAZARA_GRAPHICS_SAFE
if (!mesh)
{
NazaraError("Invalid mesh");
return nullptr;
}
if (!skeleton)
{
NazaraError("Invalid skeleton");
return nullptr;
}
#endif
ErrorFlags flags(ErrorFlag_ThrowException);
SkeletonMap::iterator it = s_cache.find(skeleton);
if (it == s_cache.end())
{
MeshData meshData;
meshData.skeletonDestroySlot.Connect(skeleton->OnSkeletonDestroy, OnSkeletonRelease);
meshData.skeletonJointsInvalidatedSlot.Connect(skeleton->OnSkeletonJointsInvalidated, OnSkeletonInvalidated);
it = s_cache.insert(std::make_pair(skeleton, std::move(meshData))).first;
}
VertexBuffer* buffer;
MeshMap& meshMap = it->second.meshMap;
MeshMap::iterator it2 = meshMap.find(mesh);
if (it2 == meshMap.end())
{
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), mesh->GetVertexCount(), DataStorage_Hardware, BufferUsage_Dynamic);
BufferData data;
data.skeletalMeshDestroySlot.Connect(mesh->OnSkeletalMeshDestroy, OnSkeletalMeshDestroy);
data.buffer = vertexBuffer;
data.updated = true;
meshMap.insert(std::make_pair(mesh, std::move(data)));
s_skinningQueue.push_back(QueueData{mesh, skeleton, vertexBuffer});
buffer = vertexBuffer;
}
else
{
BufferData& data = it2->second;
if (!data.updated)
{
s_skinningQueue.push_back(QueueData{mesh, skeleton, data.buffer});
data.updated = true;
}
buffer = data.buffer;
}
return buffer;
}
}
void NzSkinningManager::OnSkeletonInvalidated(const NzSkeleton* skeleton)
{
for (auto& pair : s_cache.at(skeleton).meshMap)
pair.second.updated = false;
}
void SkinningManager::Skin()
{
for (QueueData& data : s_skinningQueue)
s_skinFunc(data.mesh, data.skeleton, data.buffer);
void NzSkinningManager::OnSkeletonRelease(const NzSkeleton* skeleton)
{
s_cache.erase(skeleton);
}
s_skinningQueue.clear();
}
void NzSkinningManager::Uninitialize()
{
s_cache.clear();
s_skinningQueue.clear();
}
bool SkinningManager::Initialize()
{
///TODO: GPU Skinning
if (TaskScheduler::Initialize())
s_skinFunc = Skin_MultiCPU;
else
s_skinFunc = Skin_MonoCPU;
NzSkinningManager::SkinFunction NzSkinningManager::s_skinFunc = nullptr;
return true; // Rien de particulier à faire
}
void SkinningManager::OnSkeletalMeshDestroy(const SkeletalMesh* mesh)
{
for (auto& pair : s_cache)
{
MeshMap& meshMap = pair.second.meshMap;
meshMap.erase(mesh);
}
}
void SkinningManager::OnSkeletonInvalidated(const Skeleton* skeleton)
{
for (auto& pair : s_cache.at(skeleton).meshMap)
pair.second.updated = false;
}
void SkinningManager::OnSkeletonRelease(const Skeleton* skeleton)
{
s_cache.erase(skeleton);
}
void SkinningManager::Uninitialize()
{
s_cache.clear();
s_skinningQueue.clear();
}
SkinningManager::SkinFunction SkinningManager::s_skinFunc = nullptr;
}

View File

@@ -12,148 +12,151 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
static NzIndexBufferRef s_indexBuffer;
static NzRenderStates s_renderStates;
static NzShaderRef s_shader;
static NzVertexBufferRef s_vertexBuffer;
}
NzSkyboxBackground::NzSkyboxBackground(NzTextureRef cubemapTexture)
{
m_sampler.SetWrapMode(nzSamplerWrap_Clamp); // Nécessaire pour ne pas voir les côtés
SetTexture(std::move(cubemapTexture));
}
void NzSkyboxBackground::Draw(const NzAbstractViewer* viewer) const
{
NzMatrix4f skyboxMatrix(viewer->GetViewMatrix());
skyboxMatrix.SetTranslation(NzVector3f::Zero());
NzRenderer::SetIndexBuffer(s_indexBuffer);
NzRenderer::SetMatrix(nzMatrixType_View, skyboxMatrix);
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Scale(NzVector3f(viewer->GetZNear())));
NzRenderer::SetRenderStates(s_renderStates);
NzRenderer::SetShader(s_shader);
NzRenderer::SetTexture(0, m_texture);
NzRenderer::SetTextureSampler(0, m_sampler);
NzRenderer::SetVertexBuffer(s_vertexBuffer);
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, 36);
NzRenderer::SetMatrix(nzMatrixType_View, viewer->GetViewMatrix());
}
nzBackgroundType NzSkyboxBackground::GetBackgroundType() const
{
return nzBackgroundType_Skybox;
}
bool NzSkyboxBackground::Initialize()
{
const nzUInt16 indices[6*6] =
namespace
{
0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
7, 6, 5, 7, 5, 4,
4, 5, 1, 4, 1, 0,
0, 3, 7, 0, 7, 4,
1, 6, 2, 1, 5, 6
};
const float vertices[8 * 3 * sizeof(float)] =
{
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
};
///TODO: Replace by ShaderNode (probably after Vulkan)
const char* fragmentShaderSource =
"#version 140\n"
"in vec3 vTexCoord;\n"
"out vec4 RenderTarget0;\n"
"uniform samplerCube Skybox;\n"
"uniform float VertexDepth;\n"
"void main()\n"
"{\n"
" RenderTarget0 = texture(Skybox, vTexCoord);\n"
" gl_FragDepth = VertexDepth;\n"
"}\n";
const char* vertexShaderSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"out vec3 vTexCoord;\n"
"uniform mat4 WorldViewProjMatrix;\n"
"void main()\n"
"{\n"
" vec4 WVPVertex = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"
" gl_Position = WVPVertex.xyww;\n"
" vTexCoord = vec3(VertexPosition.x, VertexPosition.y, -VertexPosition.z);\n"
"}\n";
try
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
// Index buffer
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(false, 36, nzDataStorage_Hardware, nzBufferUsage_Static);
indexBuffer->Fill(indices, 0, 36);
// Vertex buffer
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ), 8, nzDataStorage_Hardware, nzBufferUsage_Static);
vertexBuffer->Fill(vertices, 0, 8);
// Shader
NzShaderRef shader = NzShader::New();
shader->Create();
shader->AttachStageFromSource(nzShaderStage_Fragment, fragmentShaderSource);
shader->AttachStageFromSource(nzShaderStage_Vertex, vertexShaderSource);
shader->Link();
shader->SendInteger(shader->GetUniformLocation("Skybox"), 0);
shader->SendFloat(shader->GetUniformLocation("VertexDepth"), 1.f);
// Renderstates
s_renderStates.depthFunc = nzRendererComparison_Equal;
s_renderStates.faceCulling = nzFaceSide_Front;
s_renderStates.parameters[nzRendererParameter_DepthBuffer] = true;
s_renderStates.parameters[nzRendererParameter_DepthWrite] = false;
s_renderStates.parameters[nzRendererParameter_FaceCulling] = true;
// Exception-free zone
s_indexBuffer = std::move(indexBuffer);
s_shader = std::move(shader);
s_vertexBuffer = std::move(vertexBuffer);
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + NzString(e.what()));
return false;
static IndexBufferRef s_indexBuffer;
static RenderStates s_renderStates;
static ShaderRef s_shader;
static VertexBufferRef s_vertexBuffer;
}
return true;
}
SkyboxBackground::SkyboxBackground(TextureRef cubemapTexture)
{
m_sampler.SetWrapMode(SamplerWrap_Clamp); // Nécessaire pour ne pas voir les côtés
void NzSkyboxBackground::Uninitialize()
{
s_indexBuffer.Reset();
s_shader.Reset();
s_vertexBuffer.Reset();
}
SetTexture(std::move(cubemapTexture));
}
void SkyboxBackground::Draw(const AbstractViewer* viewer) const
{
Matrix4f skyboxMatrix(viewer->GetViewMatrix());
skyboxMatrix.SetTranslation(Vector3f::Zero());
Renderer::SetIndexBuffer(s_indexBuffer);
Renderer::SetMatrix(MatrixType_View, skyboxMatrix);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Scale(Vector3f(viewer->GetZNear())));
Renderer::SetRenderStates(s_renderStates);
Renderer::SetShader(s_shader);
Renderer::SetTexture(0, m_texture);
Renderer::SetTextureSampler(0, m_sampler);
Renderer::SetVertexBuffer(s_vertexBuffer);
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, 36);
Renderer::SetMatrix(MatrixType_View, viewer->GetViewMatrix());
}
BackgroundType SkyboxBackground::GetBackgroundType() const
{
return BackgroundType_Skybox;
}
bool SkyboxBackground::Initialize()
{
const UInt16 indices[6*6] =
{
0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
7, 6, 5, 7, 5, 4,
4, 5, 1, 4, 1, 0,
0, 3, 7, 0, 7, 4,
1, 6, 2, 1, 5, 6
};
const float vertices[8 * 3 * sizeof(float)] =
{
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
};
///TODO: Replace by ShaderNode (probably after Vulkan)
const char* fragmentShaderSource =
"#version 140\n"
"in vec3 vTexCoord;\n"
"out vec4 RenderTarget0;\n"
"uniform samplerCube Skybox;\n"
"uniform float VertexDepth;\n"
"void main()\n"
"{\n"
" RenderTarget0 = texture(Skybox, vTexCoord);\n"
" gl_FragDepth = VertexDepth;\n"
"}\n";
const char* vertexShaderSource =
"#version 140\n"
"in vec3 VertexPosition;\n"
"out vec3 vTexCoord;\n"
"uniform mat4 WorldViewProjMatrix;\n"
"void main()\n"
"{\n"
" vec4 WVPVertex = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"
" gl_Position = WVPVertex.xyww;\n"
" vTexCoord = vec3(VertexPosition.x, VertexPosition.y, -VertexPosition.z);\n"
"}\n";
try
{
ErrorFlags flags(ErrorFlag_ThrowException, true);
// Index buffer
IndexBufferRef indexBuffer = IndexBuffer::New(false, 36, DataStorage_Hardware, BufferUsage_Static);
indexBuffer->Fill(indices, 0, 36);
// Vertex buffer
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ), 8, DataStorage_Hardware, BufferUsage_Static);
vertexBuffer->Fill(vertices, 0, 8);
// Shader
ShaderRef shader = Shader::New();
shader->Create();
shader->AttachStageFromSource(ShaderStageType_Fragment, fragmentShaderSource);
shader->AttachStageFromSource(ShaderStageType_Vertex, vertexShaderSource);
shader->Link();
shader->SendInteger(shader->GetUniformLocation("Skybox"), 0);
shader->SendFloat(shader->GetUniformLocation("VertexDepth"), 1.f);
// Renderstates
s_renderStates.depthFunc = RendererComparison_Equal;
s_renderStates.faceCulling = FaceSide_Front;
s_renderStates.parameters[RendererParameter_DepthBuffer] = true;
s_renderStates.parameters[RendererParameter_DepthWrite] = false;
s_renderStates.parameters[RendererParameter_FaceCulling] = true;
// Exception-free zone
s_indexBuffer = std::move(indexBuffer);
s_shader = std::move(shader);
s_vertexBuffer = std::move(vertexBuffer);
}
catch (const std::exception& e)
{
NazaraError("Failed to initialise: " + String(e.what()));
return false;
}
return true;
}
void SkyboxBackground::Uninitialize()
{
s_indexBuffer.Reset();
s_shader.Reset();
s_vertexBuffer.Reset();
}
}

View File

@@ -9,44 +9,47 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
void NzSprite::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
namespace Nz
{
if (!m_material)
return;
void Sprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
if (!m_material)
return;
const NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const NzVertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(m_material, vertices, 1);
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(m_material, vertices, 1);
}
void Sprite::MakeBoundingVolume() const
{
m_boundingVolume.Set(Vector3f(0.f), m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down());
}
void Sprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(4 * sizeof(VertexStruct_XYZ_Color_UV));
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(instanceData->data.data());
SparsePtr<Color> colorPtr(&vertices[0].color, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector3f> posPtr(&vertices[0].position, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector2f> texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV));
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(Vector3f(0.f));
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftTop);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right());
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightTop);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.y*Vector3f::Down());
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftBottom);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down());
*texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightBottom);
}
SpriteLibrary::LibraryMap Sprite::s_library;
}
void NzSprite::MakeBoundingVolume() const
{
m_boundingVolume.Set(NzVector3f(0.f), m_size.x*NzVector3f::Right() + m_size.y*NzVector3f::Down());
}
void NzSprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(4 * sizeof(NzVertexStruct_XYZ_Color_UV));
NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_Color_UV*>(instanceData->data.data());
NzSparsePtr<NzColor> colorPtr(&vertices[0].color, sizeof(NzVertexStruct_XYZ_Color_UV));
NzSparsePtr<NzVector3f> posPtr(&vertices[0].position, sizeof(NzVertexStruct_XYZ_Color_UV));
NzSparsePtr<NzVector2f> texCoordPtr(&vertices[0].uv, sizeof(NzVertexStruct_XYZ_Color_UV));
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(NzVector3f(0.f));
*texCoordPtr++ = m_textureCoords.GetCorner(nzRectCorner_LeftTop);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*NzVector3f::Right());
*texCoordPtr++ = m_textureCoords.GetCorner(nzRectCorner_RightTop);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.y*NzVector3f::Down());
*texCoordPtr++ = m_textureCoords.GetCorner(nzRectCorner_LeftBottom);
*colorPtr++ = m_color;
*posPtr++ = instanceData->transformMatrix.Transform(m_size.x*NzVector3f::Right() + m_size.y*NzVector3f::Down());
*texCoordPtr++ = m_textureCoords.GetCorner(nzRectCorner_RightBottom);
}
NzSpriteLibrary::LibraryMap NzSprite::s_library;

View File

@@ -11,230 +11,233 @@
#include <Nazara/Utility/Font.hpp>
#include <Nazara/Graphics/Debug.hpp>
void NzTextSprite::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
namespace Nz
{
if (!m_material)
return;
for (auto& pair : m_renderInfos)
void TextSprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const
{
NzTexture* overlay = pair.first;
RenderIndices& indices = pair.second;
if (!m_material)
return;
if (indices.count > 0)
for (auto& pair : m_renderInfos)
{
const NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const NzVertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(m_material, &vertices[indices.first*4], indices.count, overlay);
}
}
}
Texture* overlay = pair.first;
RenderIndices& indices = pair.second;
void NzTextSprite::Update(const NzAbstractTextDrawer& drawer)
{
m_atlases.clear();
NzCallOnExit clearOnFail([this]()
{
Clear();
});
unsigned int fontCount = drawer.GetFontCount();
for (unsigned int i = 0; i < fontCount; ++i)
{
NzFont* font = drawer.GetFont(i);
const NzAbstractAtlas* atlas = font->GetAtlas().get();
NazaraAssert(atlas->GetStorage() & nzDataStorage_Hardware, "Font uses a non-hardware atlas which cannot be used by text sprites");
if (m_atlases.find(atlas) == m_atlases.end())
{
AtlasSlots& slots = m_atlases[atlas];
slots.clearSlot.Connect(atlas->OnAtlasCleared, this, &NzTextSprite::OnAtlasInvalidated);
slots.layerChangeSlot.Connect(atlas->OnAtlasLayerChange, this, &NzTextSprite::OnAtlasLayerChange);
slots.releaseSlot.Connect(atlas->OnAtlasRelease, this, &NzTextSprite::OnAtlasInvalidated);
}
}
unsigned int glyphCount = drawer.GetGlyphCount();
m_localVertices.resize(glyphCount * 4);
NzTexture* lastTexture = nullptr;
unsigned int* count = nullptr;
for (unsigned int i = 0; i < glyphCount; ++i)
{
const NzAbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
NzTexture* texture = static_cast<NzTexture*>(glyph.atlas);
if (lastTexture != texture)
{
auto pair = m_renderInfos.insert(std::make_pair(texture, RenderIndices{0U, 0U}));
count = &pair.first->second.count;
lastTexture = texture;
}
(*count)++;
}
// Attribution des indices
unsigned int index = 0;
for (auto& pair : m_renderInfos)
{
RenderIndices& indices = pair.second;
indices.first = index;
index += indices.count;
indices.count = 0; // On réinitialise count à zéro (on va s'en servir comme compteur dans la boucle suivante)
}
lastTexture = nullptr;
RenderIndices* indices = nullptr;
for (unsigned int i = 0; i < glyphCount; ++i)
{
const NzAbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
NzTexture* texture = static_cast<NzTexture*>(glyph.atlas);
if (lastTexture != texture)
{
indices = &m_renderInfos[texture]; // On a changé de texture, on ajuste le pointeur
lastTexture = texture;
}
// On commence par transformer les coordonnées entières en flottantes:
NzVector2ui size(texture->GetSize());
float invWidth = 1.f/size.x;
float invHeight = 1.f/size.y;
NzRectf uvRect(glyph.atlasRect);
uvRect.x *= invWidth;
uvRect.y *= invHeight;
uvRect.width *= invWidth;
uvRect.height *= invHeight;
static nzRectCorner normalCorners[4] = {nzRectCorner_LeftTop, nzRectCorner_RightTop, nzRectCorner_LeftBottom, nzRectCorner_RightBottom};
static nzRectCorner flippedCorners[4] = {nzRectCorner_LeftBottom, nzRectCorner_LeftTop, nzRectCorner_RightBottom, nzRectCorner_RightTop};
// Affectation des positions, couleurs, coordonnées de textures
for (unsigned int j = 0; j < 4; ++j)
{
// Remember that indices->count is a counter here, not a count value
m_localVertices[indices->count*4 + j].color = glyph.color;
m_localVertices[indices->count*4 + j].position.Set(glyph.corners[j]);
m_localVertices[indices->count*4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j]));
}
// Et on passe au prochain sommet
indices->count++;
}
m_localBounds = drawer.GetBounds();
InvalidateBoundingVolume();
InvalidateInstanceData(0);
clearOnFail.Reset();
}
void NzTextSprite::MakeBoundingVolume() const
{
NzRectf bounds(m_localBounds);
NzVector2f max = bounds.GetMaximum();
NzVector2f min = bounds.GetMinimum();
m_boundingVolume.Set(min.x*NzVector3f::Right() + min.y*NzVector3f::Down(), max.x*NzVector3f::Right() + max.y*NzVector3f::Down());
}
void NzTextSprite::OnAtlasInvalidated(const NzAbstractAtlas* atlas)
{
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + NzString::Pointer(atlas));
return;
}
#endif
NazaraWarning("TextSprite " + NzString::Pointer(this) + " has been cleared because atlas " + NzString::Pointer(atlas) + " has been invalidated (cleared or released)");
Clear();
}
void NzTextSprite::OnAtlasLayerChange(const NzAbstractAtlas* atlas, NzAbstractImage* oldLayer, NzAbstractImage* newLayer)
{
NazaraUnused(atlas);
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + NzString::Pointer(atlas));
return;
}
#endif
// La texture d'un atlas vient d'être recréée (changement de taille)
// nous devons ajuster les coordonnées de textures et la texture du rendu
NzTexture* oldTexture = static_cast<NzTexture*>(oldLayer);
NzTexture* newTexture = static_cast<NzTexture*>(newLayer);
// Il est possible que nous n'utilisions pas la texture en question (l'atlas nous prévenant pour chacun de ses layers)
auto it = m_renderInfos.find(oldTexture);
if (it != m_renderInfos.end())
{
// Nous utilisons bien cette texture, nous devons mettre à jour les coordonnées de texture
RenderIndices indices = std::move(it->second);
NzVector2ui oldSize(oldTexture->GetSize());
NzVector2ui newSize(newTexture->GetSize());
NzVector2f scale = NzVector2f(oldSize)/NzVector2f(newSize); // ratio ancienne et nouvelle taille
// On va maintenant parcourir toutes les coordonnées de texture concernées pour les multiplier par ce ratio
NzSparsePtr<NzVector2f> texCoordPtr(&m_localVertices[indices.first].uv, sizeof(NzVertexStruct_XYZ_Color_UV));
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
m_localVertices[i*4 + j].uv *= scale;
}
// Nous enlevons l'ancienne texture et rajoutons la nouvelle à sa place (pour les mêmes indices)
m_renderInfos.erase(it);
m_renderInfos.insert(std::make_pair(newTexture, std::move(indices)));
}
}
void NzTextSprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(m_localVertices.size() * sizeof(NzVertexStruct_XYZ_Color_UV));
NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_Color_UV*>(instanceData->data.data());
NzSparsePtr<NzColor> colorPtr(&vertices[0].color, sizeof(NzVertexStruct_XYZ_Color_UV));
NzSparsePtr<NzVector3f> posPtr(&vertices[0].position, sizeof(NzVertexStruct_XYZ_Color_UV));
NzSparsePtr<NzVector2f> texCoordPtr(&vertices[0].uv, sizeof(NzVertexStruct_XYZ_Color_UV));
// Nous allons maintenant initialiser les sommets finaux (ceux envoyés à la RenderQueue)
// à l'aide du repère, de la matrice et de notre attribut de couleur
for (auto& pair : m_renderInfos)
{
RenderIndices& indices = pair.second;
NzSparsePtr<NzColor> color = colorPtr + indices.first*4;
NzSparsePtr<NzVector3f> pos = posPtr + indices.first*4;
NzSparsePtr<NzVector2f> uv = texCoordPtr + indices.first*4;
NzVertexStruct_XY_Color_UV* localVertex = &m_localVertices[indices.first*4];
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
if (indices.count > 0)
{
NzVector3f localPos = localVertex->position.x*NzVector3f::Right() + localVertex->position.y*NzVector3f::Down();
localPos *= m_scale;
*pos++ = instanceData->transformMatrix.Transform(localPos);
*color++ = m_color * localVertex->color;
*uv++ = localVertex->uv;
localVertex++;
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(instanceData.data.data());
renderQueue->AddSprites(m_material, &vertices[indices.first*4], indices.count, overlay);
}
}
}
}
NzTextSpriteLibrary::LibraryMap NzTextSprite::s_library;
void TextSprite::Update(const AbstractTextDrawer& drawer)
{
m_atlases.clear();
CallOnExit clearOnFail([this]()
{
Clear();
});
unsigned int fontCount = drawer.GetFontCount();
for (unsigned int i = 0; i < fontCount; ++i)
{
Font* font = drawer.GetFont(i);
const AbstractAtlas* atlas = font->GetAtlas().get();
NazaraAssert(atlas->GetStorage() & DataStorage_Hardware, "Font uses a non-hardware atlas which cannot be used by text sprites");
if (m_atlases.find(atlas) == m_atlases.end())
{
AtlasSlots& slots = m_atlases[atlas];
slots.clearSlot.Connect(atlas->OnAtlasCleared, this, &TextSprite::OnAtlasInvalidated);
slots.layerChangeSlot.Connect(atlas->OnAtlasLayerChange, this, &TextSprite::OnAtlasLayerChange);
slots.releaseSlot.Connect(atlas->OnAtlasRelease, this, &TextSprite::OnAtlasInvalidated);
}
}
unsigned int glyphCount = drawer.GetGlyphCount();
m_localVertices.resize(glyphCount * 4);
Texture* lastTexture = nullptr;
unsigned int* count = nullptr;
for (unsigned int i = 0; i < glyphCount; ++i)
{
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
Texture* texture = static_cast<Texture*>(glyph.atlas);
if (lastTexture != texture)
{
auto pair = m_renderInfos.insert(std::make_pair(texture, RenderIndices{0U, 0U}));
count = &pair.first->second.count;
lastTexture = texture;
}
(*count)++;
}
// Attribution des indices
unsigned int index = 0;
for (auto& pair : m_renderInfos)
{
RenderIndices& indices = pair.second;
indices.first = index;
index += indices.count;
indices.count = 0; // On réinitialise count à zéro (on va s'en servir comme compteur dans la boucle suivante)
}
lastTexture = nullptr;
RenderIndices* indices = nullptr;
for (unsigned int i = 0; i < glyphCount; ++i)
{
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
Texture* texture = static_cast<Texture*>(glyph.atlas);
if (lastTexture != texture)
{
indices = &m_renderInfos[texture]; // On a changé de texture, on ajuste le pointeur
lastTexture = texture;
}
// On commence par transformer les coordonnées entières en flottantes:
Vector2ui size(texture->GetSize());
float invWidth = 1.f/size.x;
float invHeight = 1.f/size.y;
Rectf uvRect(glyph.atlasRect);
uvRect.x *= invWidth;
uvRect.y *= invHeight;
uvRect.width *= invWidth;
uvRect.height *= invHeight;
static RectCorner normalCorners[4] = {RectCorner_LeftTop, RectCorner_RightTop, RectCorner_LeftBottom, RectCorner_RightBottom};
static RectCorner flippedCorners[4] = {RectCorner_LeftBottom, RectCorner_LeftTop, RectCorner_RightBottom, RectCorner_RightTop};
// Affectation des positions, couleurs, coordonnées de textures
for (unsigned int j = 0; j < 4; ++j)
{
// Remember that indices->count is a counter here, not a count value
m_localVertices[indices->count*4 + j].color = glyph.color;
m_localVertices[indices->count*4 + j].position.Set(glyph.corners[j]);
m_localVertices[indices->count*4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j]));
}
// Et on passe au prochain sommet
indices->count++;
}
m_localBounds = drawer.GetBounds();
InvalidateBoundingVolume();
InvalidateInstanceData(0);
clearOnFail.Reset();
}
void TextSprite::MakeBoundingVolume() const
{
Rectf bounds(m_localBounds);
Vector2f max = bounds.GetMaximum();
Vector2f min = bounds.GetMinimum();
m_boundingVolume.Set(min.x*Vector3f::Right() + min.y*Vector3f::Down(), max.x*Vector3f::Right() + max.y*Vector3f::Down());
}
void TextSprite::OnAtlasInvalidated(const AbstractAtlas* atlas)
{
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + String::Pointer(atlas));
return;
}
#endif
NazaraWarning("TextSprite " + String::Pointer(this) + " has been cleared because atlas " + String::Pointer(atlas) + " has been invalidated (cleared or released)");
Clear();
}
void TextSprite::OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer)
{
NazaraUnused(atlas);
#ifdef NAZARA_DEBUG
if (m_atlases.find(atlas) == m_atlases.end())
{
NazaraInternalError("Not listening to " + String::Pointer(atlas));
return;
}
#endif
// La texture d'un atlas vient d'être recréée (changement de taille)
// nous devons ajuster les coordonnées de textures et la texture du rendu
Texture* oldTexture = static_cast<Texture*>(oldLayer);
Texture* newTexture = static_cast<Texture*>(newLayer);
// Il est possible que nous n'utilisions pas la texture en question (l'atlas nous prévenant pour chacun de ses layers)
auto it = m_renderInfos.find(oldTexture);
if (it != m_renderInfos.end())
{
// Nous utilisons bien cette texture, nous devons mettre à jour les coordonnées de texture
RenderIndices indices = std::move(it->second);
Vector2ui oldSize(oldTexture->GetSize());
Vector2ui newSize(newTexture->GetSize());
Vector2f scale = Vector2f(oldSize)/Vector2f(newSize); // ratio ancienne et nouvelle taille
// On va maintenant parcourir toutes les coordonnées de texture concernées pour les multiplier par ce ratio
SparsePtr<Vector2f> texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XYZ_Color_UV));
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
m_localVertices[i*4 + j].uv *= scale;
}
// Nous enlevons l'ancienne texture et rajoutons la nouvelle à sa place (pour les mêmes indices)
m_renderInfos.erase(it);
m_renderInfos.insert(std::make_pair(newTexture, std::move(indices)));
}
}
void TextSprite::UpdateData(InstanceData* instanceData) const
{
instanceData->data.resize(m_localVertices.size() * sizeof(VertexStruct_XYZ_Color_UV));
VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<VertexStruct_XYZ_Color_UV*>(instanceData->data.data());
SparsePtr<Color> colorPtr(&vertices[0].color, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector3f> posPtr(&vertices[0].position, sizeof(VertexStruct_XYZ_Color_UV));
SparsePtr<Vector2f> texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV));
// Nous allons maintenant initialiser les sommets finaux (ceux envoyés à la RenderQueue)
// à l'aide du repère, de la matrice et de notre attribut de couleur
for (auto& pair : m_renderInfos)
{
RenderIndices& indices = pair.second;
SparsePtr<Color> color = colorPtr + indices.first*4;
SparsePtr<Vector3f> pos = posPtr + indices.first*4;
SparsePtr<Vector2f> uv = texCoordPtr + indices.first*4;
VertexStruct_XY_Color_UV* localVertex = &m_localVertices[indices.first*4];
for (unsigned int i = 0; i < indices.count; ++i)
{
for (unsigned int j = 0; j < 4; ++j)
{
Vector3f localPos = localVertex->position.x*Vector3f::Right() + localVertex->position.y*Vector3f::Down();
localPos *= m_scale;
*pos++ = instanceData->transformMatrix.Transform(localPos);
*color++ = m_color * localVertex->color;
*uv++ = localVertex->uv;
localVertex++;
}
}
}
}
TextSpriteLibrary::LibraryMap TextSprite::s_library;
}

View File

@@ -7,60 +7,63 @@
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
namespace
namespace Nz
{
NzRenderStates BuildRenderStates()
namespace
{
NzRenderStates states;
states.depthFunc = nzRendererComparison_Equal;
states.faceCulling = nzFaceSide_Back;
states.parameters[nzRendererParameter_DepthBuffer] = true;
states.parameters[nzRendererParameter_DepthWrite] = false;
states.parameters[nzRendererParameter_FaceCulling] = true;
RenderStates BuildRenderStates()
{
RenderStates states;
states.depthFunc = RendererComparison_Equal;
states.faceCulling = FaceSide_Back;
states.parameters[RendererParameter_DepthBuffer] = true;
states.parameters[RendererParameter_DepthWrite] = false;
states.parameters[RendererParameter_FaceCulling] = true;
return states;
return states;
}
}
TextureBackground::TextureBackground(TextureRef texture)
{
m_uberShader = UberShaderLibrary::Get("Basic");
ParameterList list;
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", true);
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const Shader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
SetTexture(std::move(texture));
}
void TextureBackground::Draw(const AbstractViewer* viewer) const
{
NazaraUnused(viewer);
static RenderStates states(BuildRenderStates());
Renderer::SetRenderStates(states);
Renderer::SetTexture(0, m_texture);
m_uberShaderInstance->Activate();
const Shader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, Color::White);
shader->SendFloat(m_vertexDepthUniform, 1.f);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
Renderer::DrawFullscreenQuad();
}
BackgroundType TextureBackground::GetBackgroundType() const
{
return BackgroundType_Texture;
}
}
NzTextureBackground::NzTextureBackground(NzTextureRef texture)
{
m_uberShader = NzUberShaderLibrary::Get("Basic");
NzParameterList list;
list.SetParameter("DIFFUSE_MAPPING", true);
list.SetParameter("TEXTURE_MAPPING", true);
list.SetParameter("UNIFORM_VERTEX_DEPTH", true);
m_uberShaderInstance = m_uberShader->Get(list);
const NzShader* shader = m_uberShaderInstance->GetShader();
m_materialDiffuseUniform = shader->GetUniformLocation("MaterialDiffuse");
m_materialDiffuseMapUniform = shader->GetUniformLocation("MaterialDiffuseMap");
m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth");
SetTexture(std::move(texture));
}
void NzTextureBackground::Draw(const NzAbstractViewer* viewer) const
{
NazaraUnused(viewer);
static NzRenderStates states(BuildRenderStates());
NzRenderer::SetRenderStates(states);
NzRenderer::SetTexture(0, m_texture);
m_uberShaderInstance->Activate();
const NzShader* shader = m_uberShaderInstance->GetShader();
shader->SendColor(m_materialDiffuseUniform, NzColor::White);
shader->SendFloat(m_vertexDepthUniform, 1.f);
shader->SendInteger(m_materialDiffuseMapUniform, 0);
NzRenderer::DrawFullscreenQuad();
}
nzBackgroundType NzTextureBackground::GetBackgroundType() const
{
return nzBackgroundType_Texture;
}