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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,7 @@
|
||||
#include <Nazara/Graphics/AbstractViewer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzAbstractViewer::~NzAbstractViewer() = default;
|
||||
namespace Nz
|
||||
{
|
||||
AbstractViewer::~AbstractViewer() = default;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -5,4 +5,7 @@
|
||||
#include <Nazara/Graphics/Drawable.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzDrawable::~NzDrawable() = default;
|
||||
namespace Nz
|
||||
{
|
||||
Drawable::~Drawable() = default;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user