Big Graphics update
Separated LightManager Added Sprite class Added View class Camera is no longer a SceneNode Fixed Material not invalidating programs Renamed CameraPosition uniform to EyePosition Renamed VisibilityTest to FrustumCull Former-commit-id: ff7fbe4d9b31a3c269baab0b48c6faa347a12161
This commit is contained in:
8
src/Nazara/Graphics/AbstractViewer.cpp
Normal file
8
src/Nazara/Graphics/AbstractViewer.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/AbstractViewer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzAbstractViewer::~NzAbstractViewer() = default;
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzCamera::NzCamera() :
|
||||
m_viewport(0.f, 0.f, 1.f, 1.f),
|
||||
m_targetRegion(0.f, 0.f, 1.f, 1.f),
|
||||
m_target(nullptr),
|
||||
m_frustumUpdated(false),
|
||||
m_projectionMatrixUpdated(false),
|
||||
@@ -28,26 +28,6 @@ NzCamera::~NzCamera()
|
||||
m_target->RemoveListener(this);
|
||||
}
|
||||
|
||||
void NzCamera::Activate()
|
||||
{
|
||||
#ifdef NAZARA_GRAPHICS_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("Camera has no render target");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
|
||||
NzRenderer::SetTarget(m_target);
|
||||
NzRenderer::SetViewport(m_viewport);
|
||||
|
||||
if (m_scene)
|
||||
m_scene->SetActiveCamera(this);
|
||||
}
|
||||
|
||||
void NzCamera::EnsureFrustumUpdate() const
|
||||
{
|
||||
if (!m_frustumUpdated)
|
||||
@@ -66,16 +46,20 @@ void NzCamera::EnsureViewMatrixUpdate() const
|
||||
UpdateViewMatrix();
|
||||
}
|
||||
|
||||
void NzCamera::EnsureViewportUpdate() const
|
||||
{
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
}
|
||||
|
||||
float NzCamera::GetAspectRatio() const
|
||||
{
|
||||
return m_aspectRatio;
|
||||
}
|
||||
|
||||
const NzBoundingVolumef& NzCamera::GetBoundingVolume() const
|
||||
NzVector3f NzCamera::GetEyePosition() const
|
||||
{
|
||||
///TODO: Remplacer par le bounding volume du Frustum ?
|
||||
static NzBoundingVolumef dummy(nzExtend_Null);
|
||||
return dummy;
|
||||
return GetPosition(nzCoordSys_Global);
|
||||
}
|
||||
|
||||
float NzCamera::GetFOV() const
|
||||
@@ -99,11 +83,6 @@ const NzMatrix4f& NzCamera::GetProjectionMatrix() const
|
||||
return m_projectionMatrix;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzCamera::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Camera;
|
||||
}
|
||||
|
||||
const NzRenderTarget* NzCamera::GetTarget() const
|
||||
{
|
||||
return m_target;
|
||||
@@ -174,6 +153,9 @@ void NzCamera::SetTarget(const NzRenderTarget& renderTarget)
|
||||
void NzCamera::SetTargetRegion(const NzRectf& region)
|
||||
{
|
||||
m_targetRegion = region;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
m_viewportUpdated = false;
|
||||
}
|
||||
|
||||
@@ -210,16 +192,33 @@ void NzCamera::SetZNear(float zNear)
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzCamera::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const
|
||||
void NzCamera::ApplyView() const
|
||||
{
|
||||
NazaraUnused(renderQueue);
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("Camera has no render target");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NazaraInternalError("SceneNode::AddToRenderQueue() called on Camera");
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix);
|
||||
NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix);
|
||||
NzRenderer::SetViewport(m_viewport);
|
||||
}
|
||||
|
||||
void NzCamera::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
NzNode::Invalidate();
|
||||
|
||||
// Le frustum et la view matrix dépendent des paramètres du node, invalidons-les
|
||||
m_frustumUpdated = false;
|
||||
@@ -241,21 +240,17 @@ bool NzCamera::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void
|
||||
NazaraUnused(userdata);
|
||||
|
||||
if (renderTarget == m_target)
|
||||
{
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
m_viewportUpdated = false;
|
||||
}
|
||||
else
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzCamera::Register()
|
||||
{
|
||||
}
|
||||
|
||||
void NzCamera::Unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void NzCamera::UpdateFrustum() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
@@ -270,6 +265,9 @@ void NzCamera::UpdateFrustum() const
|
||||
|
||||
void NzCamera::UpdateProjectionMatrix() const
|
||||
{
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport(); // Peut affecter l'aspect ratio
|
||||
|
||||
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
|
||||
m_projectionMatrixUpdated = true;
|
||||
}
|
||||
@@ -288,8 +286,8 @@ void NzCamera::UpdateViewport() const
|
||||
unsigned int width = m_target->GetWidth();
|
||||
unsigned int height = std::max(m_target->GetHeight(), 1U);
|
||||
|
||||
float vWidth = width * m_viewport.width;
|
||||
float vHeight = height * m_viewport.height;
|
||||
float vWidth = width * m_targetRegion.width;
|
||||
float vHeight = height * m_targetRegion.height;
|
||||
float aspectRatio = vWidth/vHeight;
|
||||
|
||||
if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f))
|
||||
@@ -305,10 +303,3 @@ void NzCamera::UpdateViewport() const
|
||||
m_viewport.height = vHeight;
|
||||
m_viewportUpdated = true;
|
||||
}
|
||||
|
||||
bool NzCamera::VisibilityTest(const NzCamera* camera)
|
||||
{
|
||||
NazaraUnused(camera);
|
||||
//NazaraInternalError("SceneNode::IsVisible() called on Camera");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Graphics/Sprite.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
@@ -131,13 +132,18 @@ void NzForwardRenderQueue::AddModel(const NzModel* model)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pair = opaqueModels.insert(std::make_pair(material, MeshContainer::mapped_type()));
|
||||
auto pair = opaqueModels.insert(std::make_pair(material, BatchedModelContainer::mapped_type()));
|
||||
if (pair.second)
|
||||
material->AddResourceListener(this, ResourceType_Material);
|
||||
|
||||
auto& meshMap = std::get<2>(pair.first->second);
|
||||
bool& used = std::get<0>(pair.first->second);
|
||||
bool& enableInstancing = std::get<1>(pair.first->second);
|
||||
|
||||
auto pair2 = meshMap.insert(std::make_pair(staticMesh, StaticMeshContainer::mapped_type()));
|
||||
used = true;
|
||||
|
||||
auto& meshMap = std::get<3>(pair.first->second);
|
||||
|
||||
auto pair2 = meshMap.insert(std::make_pair(staticMesh, BatchedStaticMeshContainer::mapped_type()));
|
||||
if (pair2.second)
|
||||
{
|
||||
staticMesh->AddResourceListener(this, ResourceType_StaticMesh);
|
||||
@@ -153,7 +159,7 @@ void NzForwardRenderQueue::AddModel(const NzModel* model)
|
||||
|
||||
// As-t-on suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ?
|
||||
if (instanceCount >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT)
|
||||
std::get<0>(pair.first->second) = true; // Apparemment oui, activons l'instancing avec ce matériau
|
||||
enableInstancing = true; // Apparemment oui, activons l'instancing avec ce matériau
|
||||
|
||||
staticDataContainer.resize(instanceCount);
|
||||
StaticData& data = staticDataContainer.back();
|
||||
@@ -166,6 +172,19 @@ void NzForwardRenderQueue::AddModel(const NzModel* model)
|
||||
}
|
||||
}
|
||||
|
||||
void NzForwardRenderQueue::AddSprite(const NzSprite* sprite)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!sprite)
|
||||
{
|
||||
NazaraError("Invalid sprite");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
sprites[sprite->GetMaterial()].push_back(sprite);
|
||||
}
|
||||
|
||||
void NzForwardRenderQueue::Clear(bool fully)
|
||||
{
|
||||
directionnalLights.clear();
|
||||
@@ -176,7 +195,10 @@ void NzForwardRenderQueue::Clear(bool fully)
|
||||
transparentStaticModels.clear();
|
||||
|
||||
if (fully)
|
||||
{
|
||||
opaqueModels.clear();
|
||||
sprites.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NzForwardRenderQueue::Sort(const NzCamera& camera)
|
||||
@@ -219,7 +241,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind
|
||||
case ResourceType_SkeletalMesh:
|
||||
{
|
||||
for (auto& pair : opaqueModels)
|
||||
std::get<1>(pair.second).erase(static_cast<const NzSkeletalMesh*>(resource));
|
||||
std::get<2>(pair.second).erase(static_cast<const NzSkeletalMesh*>(resource));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -227,7 +249,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind
|
||||
case ResourceType_StaticMesh:
|
||||
{
|
||||
for (auto& pair : opaqueModels)
|
||||
std::get<2>(pair.second).erase(static_cast<const NzStaticMesh*>(resource));
|
||||
std::get<3>(pair.second).erase(static_cast<const NzStaticMesh*>(resource));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -236,7 +258,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind
|
||||
return false; // Nous ne voulons plus recevoir d'évènement de cette ressource
|
||||
}
|
||||
|
||||
bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
|
||||
bool NzForwardRenderQueue::BatchedModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
|
||||
{
|
||||
for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i)
|
||||
{
|
||||
@@ -255,7 +277,26 @@ bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial*
|
||||
return mat1 < mat2;
|
||||
}
|
||||
|
||||
bool NzForwardRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2)
|
||||
bool NzForwardRenderQueue::BatchedSpriteMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
|
||||
{
|
||||
for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i)
|
||||
{
|
||||
const NzShaderProgram* program1 = mat1->GetShaderProgram(nzShaderTarget_Sprite, i);
|
||||
const NzShaderProgram* program2 = mat2->GetShaderProgram(nzShaderTarget_Sprite, i);
|
||||
|
||||
if (program1 != program2)
|
||||
return program1 < program2;
|
||||
}
|
||||
|
||||
const NzTexture* diffuseMap1 = mat1->GetDiffuseMap();
|
||||
const NzTexture* diffuseMap2 = mat2->GetDiffuseMap();
|
||||
if (diffuseMap1 != diffuseMap2)
|
||||
return diffuseMap1 < diffuseMap2;
|
||||
|
||||
return mat1 < mat2;
|
||||
}
|
||||
|
||||
bool NzForwardRenderQueue::BatchedSkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2)
|
||||
{
|
||||
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||
const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr;
|
||||
@@ -269,7 +310,7 @@ bool NzForwardRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMe
|
||||
return buffer2 < buffer2;
|
||||
}
|
||||
|
||||
bool NzForwardRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2)
|
||||
bool NzForwardRenderQueue::BatchedStaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2)
|
||||
{
|
||||
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||
const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Drawable.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Sprite.hpp>
|
||||
#include <Nazara/Renderer/Config.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
@@ -19,106 +20,47 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
class LightManager
|
||||
static NzIndexBuffer* s_indexBuffer = nullptr;
|
||||
unsigned int maxLightCount = 3; ///TODO: Constante sur le nombre maximum de lumières
|
||||
unsigned int s_maxSprites = 8192;
|
||||
|
||||
NzIndexBuffer* BuildIndexBuffer()
|
||||
{
|
||||
public:
|
||||
LightManager() = default;
|
||||
~LightManager() = default;
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(false, s_maxSprites*6, nzBufferStorage_Hardware, nzBufferUsage_Static));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
unsigned int FindClosestLights(const NzLight** lights, unsigned int lightCount, const NzVector3f& position, float squaredRadius)
|
||||
{
|
||||
for (Light& light : m_lights)
|
||||
{
|
||||
light.light = nullptr;
|
||||
light.score = std::numeric_limits<unsigned int>::max(); // Nous jouons au Golf
|
||||
}
|
||||
NzBufferMapper<NzIndexBuffer> mapper(indexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
nzUInt16* indices = static_cast<nzUInt16*>(mapper.GetPointer());
|
||||
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
{
|
||||
const NzLight* light = *lights;
|
||||
for (unsigned int i = 0; i < s_maxSprites; ++i)
|
||||
{
|
||||
*indices++ = i*4 + 0;
|
||||
*indices++ = i*4 + 2;
|
||||
*indices++ = i*4 + 1;
|
||||
|
||||
unsigned int score = std::numeric_limits<unsigned int>::max();
|
||||
switch (light->GetLightType())
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
score = 0; // Lumière choisie d'office
|
||||
break;
|
||||
*indices++ = i*4 + 2;
|
||||
*indices++ = i*4 + 3;
|
||||
*indices++ = i*4 + 1;
|
||||
}
|
||||
|
||||
case nzLightType_Point:
|
||||
{
|
||||
float lightRadius = light->GetRadius();
|
||||
|
||||
float squaredDistance = position.SquaredDistance(light->GetPosition());
|
||||
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
|
||||
score = static_cast<unsigned int>(squaredDistance*1000.f);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nzLightType_Spot:
|
||||
{
|
||||
float lightRadius = light->GetRadius();
|
||||
|
||||
///TODO: Attribuer bonus/malus selon l'angle du spot ?
|
||||
float squaredDistance = position.SquaredDistance(light->GetPosition());
|
||||
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
|
||||
score = static_cast<unsigned int>(squaredDistance*1000.f);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (score < m_lights[0].score)
|
||||
{
|
||||
unsigned int j;
|
||||
for (j = 1; j < 3; ++j) ///TODO: Constante
|
||||
{
|
||||
if (score > m_lights[j].score)
|
||||
break;
|
||||
}
|
||||
|
||||
j--; // Position de la nouvelle lumière
|
||||
|
||||
// Décalage
|
||||
std::memcpy(&m_lights[0], &m_lights[1], j*sizeof(Light));
|
||||
|
||||
m_lights[j].light = light;
|
||||
m_lights[j].score = score;
|
||||
}
|
||||
|
||||
lights++;
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < 3; ++i) ///TODO: Constante
|
||||
{
|
||||
if (m_lights[i].light)
|
||||
break;
|
||||
}
|
||||
|
||||
return 3-i; ///TODO: Constante
|
||||
}
|
||||
|
||||
const NzLight* GetLight(unsigned int i) const
|
||||
{
|
||||
///TODO: Constante
|
||||
return m_lights[3-i-1].light; // Les lumières sont stockées dans l'ordre inverse (De la plus éloignée à la plus proche)
|
||||
}
|
||||
|
||||
private:
|
||||
struct Light
|
||||
{
|
||||
const NzLight* light;
|
||||
unsigned int score;
|
||||
};
|
||||
|
||||
Light m_lights[3]; ///TODO: Constante
|
||||
};
|
||||
return indexBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
NzForwardRenderTechnique::NzForwardRenderTechnique() :
|
||||
m_maxLightsPerObject(3) // Valeur totalement arbitraire
|
||||
m_spriteBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_UV), s_maxSprites*4, nzBufferStorage_Hardware, nzBufferUsage_Dynamic),
|
||||
m_maxLightsPerObject(maxLightCount)
|
||||
{
|
||||
if (!s_indexBuffer)
|
||||
s_indexBuffer = BuildIndexBuffer();
|
||||
|
||||
m_indexBuffer = s_indexBuffer;
|
||||
}
|
||||
|
||||
NzForwardRenderTechnique::~NzForwardRenderTechnique()
|
||||
{
|
||||
if (m_indexBuffer.Reset())
|
||||
s_indexBuffer = nullptr;
|
||||
}
|
||||
|
||||
void NzForwardRenderTechnique::Clear(const NzScene* scene)
|
||||
@@ -134,247 +76,22 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene)
|
||||
|
||||
void NzForwardRenderTechnique::Draw(const NzScene* scene)
|
||||
{
|
||||
///TODO: Regrouper les activations par méthode
|
||||
LightManager lightManager;
|
||||
// Rendu en projection perspective (3D)
|
||||
m_directionnalLights.SetLights(&m_renderQueue.directionnalLights[0], m_renderQueue.directionnalLights.size());
|
||||
m_lights.SetLights(&m_renderQueue.lights[0], m_renderQueue.lights.size());
|
||||
|
||||
const NzCamera* camera = scene->GetActiveCamera();
|
||||
const NzShaderProgram* lastProgram = nullptr;
|
||||
if (!m_renderQueue.opaqueModels.empty())
|
||||
DrawOpaqueModels(scene, m_renderQueue.opaqueModels);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix());
|
||||
NzRenderer::SetMatrix(nzMatrixType_View, camera->GetViewMatrix());
|
||||
if (!m_renderQueue.sprites.empty())
|
||||
DrawSprites(scene, m_renderQueue.sprites);
|
||||
|
||||
// Rendu des modèles opaques
|
||||
for (auto& matIt : m_renderQueue.opaqueModels)
|
||||
{
|
||||
NzForwardRenderQueue::SkeletalMeshContainer& skeletalContainer = std::get<1>(matIt.second);
|
||||
NzForwardRenderQueue::StaticMeshContainer& staticContainer = std::get<2>(matIt.second);
|
||||
if (!m_renderQueue.transparentsModels.empty())
|
||||
DrawTransparentModels(scene, m_renderQueue.transparentsModels);
|
||||
|
||||
if (!skeletalContainer.empty() || !staticContainer.empty())
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
|
||||
// La RenderQueue active ou non l'instancing selon le nombre d'instances
|
||||
bool renderQueueInstancing = std::get<0>(matIt.second);
|
||||
|
||||
// Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active
|
||||
// Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches
|
||||
// (Le deferred shading n'a pas ce problème)
|
||||
bool instancing = m_instancingEnabled && m_renderQueue.lights.empty() && renderQueueInstancing;
|
||||
|
||||
// On commence par récupérer le programme du matériau
|
||||
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0);
|
||||
|
||||
unsigned int lightCount = 0;
|
||||
|
||||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (program != lastProgram)
|
||||
{
|
||||
NzRenderer::SetShaderProgram(program);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
program->SendVector(program->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition());
|
||||
|
||||
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
|
||||
lightCount = m_renderQueue.directionnalLights.size();
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
m_renderQueue.directionnalLights[i]->Enable(program, i);
|
||||
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
material->Apply(program);
|
||||
|
||||
// Meshs squelettiques
|
||||
/*if (!skeletalContainer.empty())
|
||||
{
|
||||
NzRenderer::SetVertexBuffer(m_skinningBuffer); // Vertex buffer commun
|
||||
for (auto& subMeshIt : container)
|
||||
{
|
||||
///TODO
|
||||
}
|
||||
}*/
|
||||
|
||||
// Meshs statiques
|
||||
for (auto& subMeshIt : staticContainer)
|
||||
{
|
||||
const NzSpheref& boundingSphere = subMeshIt.second.first;
|
||||
const NzStaticMesh* mesh = subMeshIt.first;
|
||||
std::vector<NzForwardRenderQueue::StaticData>& staticData = subMeshIt.second.second;
|
||||
|
||||
if (!staticData.empty())
|
||||
{
|
||||
const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer();
|
||||
const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer();
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> drawFunc;
|
||||
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> instancedDrawFunc;
|
||||
unsigned int indexCount;
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
drawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawFunc = NzRenderer::DrawPrimitives;
|
||||
indexCount = vertexBuffer->GetVertexCount();
|
||||
instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced;
|
||||
}
|
||||
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode();
|
||||
if (instancing)
|
||||
{
|
||||
NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
|
||||
|
||||
instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4));
|
||||
|
||||
unsigned int stride = instanceBuffer->GetStride();
|
||||
|
||||
const NzForwardRenderQueue::StaticData* data = &staticData[0];
|
||||
unsigned int instanceCount = staticData.size();
|
||||
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount();
|
||||
|
||||
while (instanceCount > 0)
|
||||
{
|
||||
unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
|
||||
instanceCount -= renderedInstanceCount;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount);
|
||||
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(mapper.GetPointer());
|
||||
|
||||
for (unsigned int i = 0; i < renderedInstanceCount; ++i)
|
||||
{
|
||||
std::memcpy(ptr, data->transformMatrix, sizeof(float)*16);
|
||||
|
||||
data++;
|
||||
ptr += stride;
|
||||
}
|
||||
|
||||
mapper.Unmap();
|
||||
|
||||
instancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const NzForwardRenderQueue::StaticData& data : staticData)
|
||||
{
|
||||
// Calcul des lumières les plus proches
|
||||
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
|
||||
{
|
||||
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.transformMatrix.GetTranslation() + boundingSphere.GetPosition(), boundingSphere.radius);
|
||||
count -= lightCount;
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
lightManager.GetLight(i)->Enable(program, lightCount++);
|
||||
}
|
||||
|
||||
for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières
|
||||
NzLight::Disable(program, i);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
|
||||
drawFunc(primitiveMode, 0, indexCount);
|
||||
}
|
||||
}
|
||||
staticData.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Et on remet à zéro l'instancing
|
||||
std::get<0>(matIt.second) = false;
|
||||
}
|
||||
|
||||
for (const std::pair<unsigned int, bool>& pair : m_renderQueue.transparentsModels)
|
||||
{
|
||||
// Matériau
|
||||
NzMaterial* material = (pair.second) ?
|
||||
m_renderQueue.transparentStaticModels[pair.first].material :
|
||||
m_renderQueue.transparentSkeletalModels[pair.first].material;
|
||||
|
||||
// On commence par récupérer le shader du matériau
|
||||
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0);
|
||||
|
||||
unsigned int lightCount = 0;
|
||||
|
||||
// Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même
|
||||
if (program != lastProgram)
|
||||
{
|
||||
NzRenderer::SetShaderProgram(program);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
program->SendVector(program->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition());
|
||||
|
||||
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
|
||||
lightCount = m_renderQueue.directionnalLights.size();
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
m_renderQueue.directionnalLights[i]->Enable(program, i);
|
||||
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
material->Apply(program);
|
||||
|
||||
// Mesh
|
||||
if (pair.second)
|
||||
{
|
||||
NzForwardRenderQueue::TransparentStaticModel& staticModel = m_renderQueue.transparentStaticModels[pair.first];
|
||||
|
||||
const NzMatrix4f& matrix = staticModel.transformMatrix;
|
||||
NzStaticMesh* mesh = staticModel.mesh;
|
||||
|
||||
const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer();
|
||||
const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer();
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> drawFunc;
|
||||
unsigned int indexCount;
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
drawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
drawFunc = NzRenderer::DrawPrimitives;
|
||||
indexCount = vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
// Calcul des lumières les plus proches
|
||||
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
|
||||
{
|
||||
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), matrix.GetTranslation() + staticModel.boundingSphere.GetPosition(), staticModel.boundingSphere.radius);
|
||||
count -= lightCount;
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
lightManager.GetLight(i)->Enable(program, lightCount++);
|
||||
}
|
||||
|
||||
for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières
|
||||
NzLight::Disable(program, i);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||
drawFunc(mesh->GetPrimitiveMode(), 0, indexCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
///TODO
|
||||
}
|
||||
}
|
||||
// Les autres drawables (Exemple: Terrain)
|
||||
for (const NzDrawable* drawable : m_renderQueue.otherDrawables)
|
||||
drawable->Draw();
|
||||
|
||||
// Les billboards
|
||||
/*if (!m_renderQueue.billboards.empty())
|
||||
@@ -420,10 +137,6 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
|
||||
billboards.clear();
|
||||
}
|
||||
}*/
|
||||
|
||||
// Les autres drawables (Exemple: Terrain)
|
||||
for (const NzDrawable* drawable : m_renderQueue.otherDrawables)
|
||||
drawable->Draw();
|
||||
}
|
||||
|
||||
unsigned int NzForwardRenderTechnique::GetMaxLightsPerObject() const
|
||||
@@ -438,5 +151,340 @@ NzAbstractRenderQueue* NzForwardRenderTechnique::GetRenderQueue()
|
||||
|
||||
void NzForwardRenderTechnique::SetMaxLightsPerObject(unsigned int lightCount)
|
||||
{
|
||||
m_maxLightsPerObject = lightCount; ///TODO: Vérifier par rapport à la constante
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (lightCount > maxLightCount)
|
||||
{
|
||||
NazaraError("Light count is over maximum light count (" + NzString::Number(lightCount) + " > " + NzString::Number(lightCount) + ')');
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_maxLightsPerObject = lightCount;
|
||||
}
|
||||
|
||||
void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene, NzForwardRenderQueue::BatchedModelContainer& opaqueModels)
|
||||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const NzShaderProgram* lastProgram = nullptr;
|
||||
|
||||
for (auto& matIt : opaqueModels)
|
||||
{
|
||||
bool& used = std::get<0>(matIt.second);
|
||||
if (used)
|
||||
{
|
||||
bool& renderQueueInstancing = std::get<1>(matIt.second);
|
||||
NzForwardRenderQueue::BatchedSkeletalMeshContainer& skeletalContainer = std::get<2>(matIt.second);
|
||||
NzForwardRenderQueue::BatchedStaticMeshContainer& staticContainer = std::get<3>(matIt.second);
|
||||
|
||||
if (!skeletalContainer.empty() || !staticContainer.empty())
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
|
||||
// Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active
|
||||
// Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches
|
||||
// (Le deferred shading n'a pas ce problème)
|
||||
bool instancing = m_instancingEnabled && m_lights.IsEmpty() && renderQueueInstancing;
|
||||
|
||||
// On commence par récupérer le programme du matériau
|
||||
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0);
|
||||
|
||||
unsigned int lightCount = 0;
|
||||
|
||||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (program != lastProgram)
|
||||
{
|
||||
NzRenderer::SetShaderProgram(program);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
|
||||
lightCount = std::min(m_directionnalLights.GetLightCount(), 3U);
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
m_directionnalLights.GetLight(i)->Enable(program, i);
|
||||
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
material->Apply(program);
|
||||
|
||||
// Meshs squelettiques
|
||||
/*if (!skeletalContainer.empty())
|
||||
{
|
||||
NzRenderer::SetVertexBuffer(m_skinningBuffer); // Vertex buffer commun
|
||||
for (auto& subMeshIt : container)
|
||||
{
|
||||
///TODO
|
||||
}
|
||||
}*/
|
||||
|
||||
// Meshs statiques
|
||||
for (auto& subMeshIt : staticContainer)
|
||||
{
|
||||
const NzSpheref& boundingSphere = subMeshIt.second.first;
|
||||
const NzStaticMesh* mesh = subMeshIt.first;
|
||||
std::vector<NzForwardRenderQueue::StaticData>& staticData = subMeshIt.second.second;
|
||||
|
||||
if (!staticData.empty())
|
||||
{
|
||||
const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer();
|
||||
const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer();
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> DrawFunc;
|
||||
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> InstancedDrawFunc;
|
||||
unsigned int indexCount;
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
DrawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||
InstancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawFunc = NzRenderer::DrawPrimitives;
|
||||
InstancedDrawFunc = NzRenderer::DrawPrimitivesInstanced;
|
||||
indexCount = vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode();
|
||||
if (instancing)
|
||||
{
|
||||
NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
|
||||
|
||||
instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4));
|
||||
|
||||
unsigned int stride = instanceBuffer->GetStride();
|
||||
|
||||
const NzForwardRenderQueue::StaticData* data = &staticData[0];
|
||||
unsigned int instanceCount = staticData.size();
|
||||
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount();
|
||||
|
||||
while (instanceCount > 0)
|
||||
{
|
||||
unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
|
||||
instanceCount -= renderedInstanceCount;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount);
|
||||
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(mapper.GetPointer());
|
||||
|
||||
for (unsigned int i = 0; i < renderedInstanceCount; ++i)
|
||||
{
|
||||
std::memcpy(ptr, data->transformMatrix, sizeof(float)*16);
|
||||
|
||||
data++;
|
||||
ptr += stride;
|
||||
}
|
||||
|
||||
mapper.Unmap();
|
||||
|
||||
InstancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const NzForwardRenderQueue::StaticData& data : staticData)
|
||||
{
|
||||
// Calcul des lumières les plus proches
|
||||
if (lightCount < m_maxLightsPerObject && !m_lights.IsEmpty())
|
||||
{
|
||||
unsigned int count = m_lights.ComputeClosestLights(data.transformMatrix.GetTranslation() + boundingSphere.GetPosition(), boundingSphere.radius, maxLightCount);
|
||||
count -= lightCount;
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
m_lights.GetResult(i)->Enable(program, lightCount++);
|
||||
}
|
||||
|
||||
for (unsigned int i = lightCount; i < maxLightCount; ++i)
|
||||
NzLight::Disable(program, i);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
|
||||
DrawFunc(primitiveMode, 0, indexCount);
|
||||
}
|
||||
}
|
||||
staticData.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Et on remet à zéro les données
|
||||
renderQueueInstancing = false;
|
||||
used = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzForwardRenderTechnique::DrawSprites(const NzScene* scene, NzForwardRenderQueue::BatchedSpriteContainer& sprites)
|
||||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const NzShaderProgram* lastProgram = nullptr;
|
||||
|
||||
NzRenderer::SetIndexBuffer(m_indexBuffer);
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity());
|
||||
NzRenderer::SetVertexBuffer(&m_spriteBuffer);
|
||||
|
||||
for (auto& matIt : sprites)
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
auto& spriteVector = matIt.second;
|
||||
|
||||
unsigned int spriteCount = spriteVector.size();
|
||||
if (spriteCount > 0)
|
||||
{
|
||||
// On commence par récupérer le programme du matériau
|
||||
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Sprite, 0);
|
||||
|
||||
// Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même
|
||||
if (program != lastProgram)
|
||||
{
|
||||
NzRenderer::SetShaderProgram(program);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
material->Apply(program);
|
||||
|
||||
const NzSprite** spritePtr = &spriteVector[0];
|
||||
do
|
||||
{
|
||||
unsigned int renderedSpriteCount = std::min(spriteCount, 64U);
|
||||
spriteCount -= renderedSpriteCount;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedSpriteCount*4);
|
||||
NzVertexStruct_XYZ_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_UV*>(vertexMapper.GetPointer());
|
||||
|
||||
for (unsigned int i = 0; i < renderedSpriteCount; ++i)
|
||||
{
|
||||
const NzSprite* sprite = *spritePtr++;
|
||||
const NzRectf& textureCoords = sprite->GetTextureCoords();
|
||||
const NzVector2f& halfSize = sprite->GetSize()*0.5f;
|
||||
NzVector3f center = sprite->GetPosition();
|
||||
NzQuaternionf rotation = sprite->GetRotation();
|
||||
|
||||
vertices->position = center + rotation * NzVector3f(-halfSize.x, -halfSize.y, 0.f);
|
||||
vertices->uv.Set(textureCoords.x, textureCoords.y + textureCoords.height);
|
||||
vertices++;
|
||||
|
||||
vertices->position = center + rotation * NzVector3f(halfSize.x, -halfSize.y, 0.f);
|
||||
vertices->uv.Set(textureCoords.width, textureCoords.y + textureCoords.height);
|
||||
vertices++;
|
||||
|
||||
vertices->position = center + rotation * NzVector3f(-halfSize.x, halfSize.y, 0.f);
|
||||
vertices->uv.Set(textureCoords.x, textureCoords.y);
|
||||
vertices++;
|
||||
|
||||
vertices->position = center + rotation * NzVector3f(halfSize.x, halfSize.y, 0.f);
|
||||
vertices->uv.Set(textureCoords.width, textureCoords.y);
|
||||
vertices++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, renderedSpriteCount*6);
|
||||
}
|
||||
while (spriteCount > 0);
|
||||
|
||||
spriteVector.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene, NzForwardRenderQueue::TransparentModelContainer& transparentModels)
|
||||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const NzShaderProgram* lastProgram = nullptr;
|
||||
|
||||
for (const std::pair<unsigned int, bool>& pair : transparentModels)
|
||||
{
|
||||
// Matériau
|
||||
NzMaterial* material = (pair.second) ?
|
||||
m_renderQueue.transparentStaticModels[pair.first].material :
|
||||
m_renderQueue.transparentSkeletalModels[pair.first].material;
|
||||
|
||||
// On commence par récupérer le shader du matériau
|
||||
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0);
|
||||
|
||||
unsigned int lightCount = 0;
|
||||
|
||||
// Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même
|
||||
if (program != lastProgram)
|
||||
{
|
||||
NzRenderer::SetShaderProgram(program);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
|
||||
lightCount = std::min(m_directionnalLights.GetLightCount(), 3U);
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
m_directionnalLights.GetLight(i)->Enable(program, i);
|
||||
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
material->Apply(program);
|
||||
|
||||
// Mesh
|
||||
if (pair.second)
|
||||
{
|
||||
NzForwardRenderQueue::TransparentStaticModel& staticModel = m_renderQueue.transparentStaticModels[pair.first];
|
||||
|
||||
const NzMatrix4f& matrix = staticModel.transformMatrix;
|
||||
NzStaticMesh* mesh = staticModel.mesh;
|
||||
|
||||
const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer();
|
||||
const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer();
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> DrawFunc;
|
||||
unsigned int indexCount;
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
DrawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawFunc = NzRenderer::DrawPrimitives;
|
||||
indexCount = vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
// Calcul des lumières les plus proches
|
||||
if (lightCount < m_maxLightsPerObject && !m_lights.IsEmpty())
|
||||
{
|
||||
unsigned int count = m_lights.ComputeClosestLights(matrix.GetTranslation() + staticModel.boundingSphere.GetPosition(), staticModel.boundingSphere.radius, maxLightCount);
|
||||
count -= lightCount;
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
m_lights.GetResult(i)->Enable(program, lightCount++);
|
||||
}
|
||||
|
||||
for (unsigned int i = lightCount; i < maxLightCount; ++i)
|
||||
NzLight::Disable(program, i);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||
DrawFunc(mesh->GetPrimitiveMode(), 0, indexCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
///TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,31 @@ void NzLight::Disable(const NzShaderProgram* program, unsigned int lightUnit)
|
||||
program->SendInteger(program->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1);
|
||||
}
|
||||
|
||||
bool NzLight::FrustumCull(const NzFrustumf& frustum)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
return true; // Toujours visible
|
||||
|
||||
case nzLightType_Point:
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
// Un test sphérique est bien plus rapide et précis que celui de la bounding box
|
||||
return frustum.Contains(NzSpheref(m_derivedPosition, m_radius));
|
||||
|
||||
case nzLightType_Spot:
|
||||
if (!m_boundingVolumeUpdated)
|
||||
UpdateBoundingVolume();
|
||||
|
||||
return frustum.Contains(m_boundingVolume);
|
||||
}
|
||||
|
||||
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
void NzLight::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
@@ -307,28 +332,3 @@ void NzLight::UpdateBoundingVolume() const
|
||||
|
||||
m_boundingVolumeUpdated = true;
|
||||
}
|
||||
|
||||
bool NzLight::VisibilityTest(const NzCamera* camera)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
return true; // Toujours visible
|
||||
|
||||
case nzLightType_Point:
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
// Un test sphérique est bien plus rapide et précis que celui de la bounding box
|
||||
return camera->GetFrustum().Contains(NzSpheref(m_derivedPosition, m_radius));
|
||||
|
||||
case nzLightType_Spot:
|
||||
if (!m_boundingVolumeUpdated)
|
||||
UpdateBoundingVolume();
|
||||
|
||||
return camera->GetFrustum().Contains(m_boundingVolume);
|
||||
}
|
||||
|
||||
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
160
src/Nazara/Graphics/LightManager.cpp
Normal file
160
src/Nazara/Graphics/LightManager.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/LightManager.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzLightManager::NzLightManager() :
|
||||
m_lightCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
NzLightManager::NzLightManager(const NzLight** lights, unsigned int lightCount)
|
||||
{
|
||||
SetLights(lights, lightCount);
|
||||
}
|
||||
|
||||
void NzLightManager::AddLights(const NzLight** lights, unsigned int lightCount)
|
||||
{
|
||||
m_lights.push_back(std::make_pair(lights, lightCount));
|
||||
m_lightCount += lightCount;
|
||||
}
|
||||
|
||||
void NzLightManager::Clear()
|
||||
{
|
||||
m_lights.clear();
|
||||
m_lightCount = 0;
|
||||
}
|
||||
|
||||
unsigned int NzLightManager::ComputeClosestLights(const NzVector3f& position, float squaredRadius, unsigned int maxResults)
|
||||
{
|
||||
m_results.resize(maxResults);
|
||||
for (Light& light : m_results)
|
||||
{
|
||||
light.light = nullptr;
|
||||
light.score = std::numeric_limits<unsigned int>::max(); // Nous jouons au Golf
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_lights.size(); ++i)
|
||||
{
|
||||
const NzLight** lights = m_lights[i].first;
|
||||
unsigned int lightCount = m_lights[i].second;
|
||||
|
||||
for (unsigned int j = 0; j < lightCount; ++j)
|
||||
{
|
||||
const NzLight* light = *lights++;
|
||||
|
||||
unsigned int score = std::numeric_limits<unsigned int>::max();
|
||||
switch (light->GetLightType())
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
score = 0; // Lumière choisie d'office
|
||||
break;
|
||||
|
||||
case nzLightType_Point:
|
||||
{
|
||||
float lightRadius = light->GetRadius();
|
||||
|
||||
float squaredDistance = position.SquaredDistance(light->GetPosition());
|
||||
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
|
||||
score = static_cast<unsigned int>(squaredDistance*1000.f);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nzLightType_Spot:
|
||||
{
|
||||
float lightRadius = light->GetRadius();
|
||||
|
||||
///TODO: Attribuer bonus/malus selon l'angle du spot ?
|
||||
float squaredDistance = position.SquaredDistance(light->GetPosition());
|
||||
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
|
||||
score = static_cast<unsigned int>(squaredDistance*1000.f);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (score < m_results[0].score)
|
||||
{
|
||||
unsigned int k;
|
||||
for (k = 1; k < maxResults; ++k)
|
||||
{
|
||||
if (score > m_results[k].score)
|
||||
break;
|
||||
}
|
||||
|
||||
k--; // Position de la nouvelle lumière
|
||||
|
||||
// Décalage
|
||||
std::memcpy(&m_results[0], &m_results[1], k*sizeof(Light));
|
||||
|
||||
m_results[k].light = light;
|
||||
m_results[k].score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < maxResults; ++i)
|
||||
{
|
||||
if (m_results[i].light)
|
||||
break;
|
||||
}
|
||||
|
||||
return maxResults-i;
|
||||
}
|
||||
|
||||
const NzLight* NzLightManager::GetLight(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (index >= m_lightCount)
|
||||
{
|
||||
NazaraError("Light index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_lightCount) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned int i = 0; i < m_lights.size(); ++i)
|
||||
{
|
||||
unsigned int lightCount = m_lights[i].second;
|
||||
if (index > lightCount)
|
||||
index -= lightCount;
|
||||
else
|
||||
{
|
||||
const NzLight** lights = m_lights[i].first;
|
||||
return lights[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
NazaraInternalError("Light not found");
|
||||
#else
|
||||
NazaraError("Light not found");
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned int NzLightManager::GetLightCount() const
|
||||
{
|
||||
return m_lightCount;
|
||||
}
|
||||
|
||||
const NzLight* NzLightManager::GetResult(unsigned int i) const
|
||||
{
|
||||
return m_results[i].light;
|
||||
}
|
||||
|
||||
bool NzLightManager::IsEmpty() const
|
||||
{
|
||||
return m_lightCount == 0;
|
||||
}
|
||||
|
||||
void NzLightManager::SetLights(const NzLight** lights, unsigned int lightCount)
|
||||
{
|
||||
Clear();
|
||||
AddLights(lights, lightCount);
|
||||
}
|
||||
@@ -630,6 +630,25 @@ NzModel& NzModel::operator=(NzModel&& node)
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool NzModel::FrustumCull(const NzFrustumf& frustum)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!IsDrawable())
|
||||
{
|
||||
NazaraError("Model is not drawable");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_drawEnabled)
|
||||
return false;
|
||||
|
||||
if (!m_boundingVolumeUpdated)
|
||||
UpdateBoundingVolume();
|
||||
|
||||
return frustum.Contains(m_boundingVolume);
|
||||
}
|
||||
|
||||
void NzModel::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
@@ -671,23 +690,4 @@ void NzModel::UpdateBoundingVolume() const
|
||||
m_boundingVolumeUpdated = true;
|
||||
}
|
||||
|
||||
bool NzModel::VisibilityTest(const NzCamera* camera)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!IsDrawable())
|
||||
{
|
||||
NazaraError("Model is not drawable");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_drawEnabled)
|
||||
return false;
|
||||
|
||||
if (!m_boundingVolumeUpdated)
|
||||
UpdateBoundingVolume();
|
||||
|
||||
return camera->GetFrustum().Contains(m_boundingVolume);
|
||||
}
|
||||
|
||||
NzModelLoader::LoaderList NzModel::s_loaders;
|
||||
|
||||
@@ -30,7 +30,7 @@ struct NzSceneImpl
|
||||
NzClock updateClock;
|
||||
NzColor ambientColor = NzColor(25,25,25);
|
||||
NzSceneRoot root;
|
||||
NzCamera* activeCamera;
|
||||
NzAbstractViewer* viewer;
|
||||
bool update;
|
||||
float frameTime;
|
||||
float updateTime;
|
||||
@@ -64,9 +64,9 @@ void NzScene::AddToVisibilityList(NzUpdatable* object)
|
||||
void NzScene::Cull()
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_impl->activeCamera)
|
||||
if (!m_impl->viewer)
|
||||
{
|
||||
NazaraError("No active camera");
|
||||
NazaraError("No viewer");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -76,8 +76,8 @@ void NzScene::Cull()
|
||||
|
||||
m_impl->visibleUpdateList.clear();
|
||||
|
||||
// Frustum culling/Viewport culling
|
||||
RecursiveCameraCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->activeCamera, &m_impl->root);
|
||||
// Frustum culling
|
||||
RecursiveFrustumCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->viewer->GetFrustum(), &m_impl->root);
|
||||
|
||||
///TODO: Occlusion culling
|
||||
|
||||
@@ -87,22 +87,18 @@ void NzScene::Cull()
|
||||
void NzScene::Draw()
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_impl->activeCamera)
|
||||
if (!m_impl->viewer)
|
||||
{
|
||||
NazaraError("No active camera");
|
||||
NazaraError("No viewer");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->renderTechnique->Clear(this);
|
||||
m_impl->viewer->ApplyView();
|
||||
m_impl->renderTechnique->Draw(this);
|
||||
}
|
||||
|
||||
NzCamera* NzScene::GetActiveCamera() const
|
||||
{
|
||||
return m_impl->activeCamera;
|
||||
}
|
||||
|
||||
NzColor NzScene::GetAmbientColor() const
|
||||
{
|
||||
return m_impl->ambientColor;
|
||||
@@ -123,6 +119,11 @@ NzSceneNode& NzScene::GetRoot() const
|
||||
return m_impl->root;
|
||||
}
|
||||
|
||||
NzAbstractViewer* NzScene::GetViewer() const
|
||||
{
|
||||
return m_impl->viewer;
|
||||
}
|
||||
|
||||
float NzScene::GetUpdateTime() const
|
||||
{
|
||||
return m_impl->updateTime;
|
||||
@@ -161,6 +162,11 @@ void NzScene::SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique)
|
||||
m_impl->renderTechnique.reset(renderTechnique);
|
||||
}
|
||||
|
||||
void NzScene::SetViewer(NzAbstractViewer* viewer)
|
||||
{
|
||||
m_impl->viewer = viewer;
|
||||
}
|
||||
|
||||
void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond)
|
||||
{
|
||||
m_impl->updatePerSecond = updatePerSecond;
|
||||
@@ -209,7 +215,7 @@ NzScene::operator const NzSceneNode&() const
|
||||
return m_impl->root;
|
||||
}
|
||||
|
||||
void NzScene::RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCamera* camera, NzNode* node)
|
||||
void NzScene::RecursiveFrustumCull(NzAbstractRenderQueue* renderQueue, const NzFrustumf& frustum, NzNode* node)
|
||||
{
|
||||
for (NzNode* child : node->GetChilds())
|
||||
{
|
||||
@@ -218,17 +224,12 @@ void NzScene::RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCa
|
||||
NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child);
|
||||
|
||||
///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag
|
||||
sceneNode->UpdateVisibility(camera);
|
||||
sceneNode->UpdateVisibility(frustum);
|
||||
if (sceneNode->IsVisible())
|
||||
sceneNode->AddToRenderQueue(renderQueue);
|
||||
}
|
||||
|
||||
if (child->HasChilds())
|
||||
RecursiveCameraCull(renderQueue, camera, child);
|
||||
RecursiveFrustumCull(renderQueue, frustum, child);
|
||||
}
|
||||
}
|
||||
|
||||
void NzScene::SetActiveCamera(NzCamera* camera)
|
||||
{
|
||||
m_impl->activeCamera = camera;
|
||||
}
|
||||
|
||||
@@ -97,11 +97,11 @@ void NzSceneNode::Update()
|
||||
{
|
||||
}
|
||||
|
||||
void NzSceneNode::UpdateVisibility(const NzCamera* camera)
|
||||
void NzSceneNode::UpdateVisibility(const NzFrustumf& frustum)
|
||||
{
|
||||
bool wasVisible = m_visible;
|
||||
|
||||
m_visible = VisibilityTest(camera);
|
||||
m_visible = FrustumCull(frustum);
|
||||
|
||||
if (m_visible != wasVisible)
|
||||
OnVisibilityChange(m_visible);
|
||||
|
||||
@@ -31,6 +31,13 @@ nzSceneNodeType NzSceneRoot::GetSceneNodeType() const
|
||||
return nzSceneNodeType_Root;
|
||||
}
|
||||
|
||||
bool NzSceneRoot::FrustumCull(const NzFrustumf& frustum)
|
||||
{
|
||||
NazaraUnused(frustum);
|
||||
|
||||
return true; // Toujours visible
|
||||
}
|
||||
|
||||
void NzSceneRoot::Register()
|
||||
{
|
||||
NazaraInternalError("SceneNode::Register() called on SceneRoot");
|
||||
@@ -40,10 +47,3 @@ void NzSceneRoot::Unregister()
|
||||
{
|
||||
NazaraInternalError("SceneNode::Unregister() called on SceneRoot");
|
||||
}
|
||||
|
||||
bool NzSceneRoot::VisibilityTest(const NzCamera* camera)
|
||||
{
|
||||
NazaraUnused(camera);
|
||||
|
||||
return true; // Toujours visible
|
||||
}
|
||||
|
||||
@@ -198,15 +198,15 @@ void NzSkyboxBackground::Draw(const NzScene* scene) const
|
||||
|
||||
s_program->SendInteger(s_skyboxLocation, 0);
|
||||
|
||||
NzCamera* camera = scene->GetActiveCamera();
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
|
||||
NzMatrix4f skyboxMatrix(camera->GetViewMatrix());
|
||||
NzMatrix4f skyboxMatrix(viewer->GetViewMatrix());
|
||||
skyboxMatrix.SetTranslation(NzVector3f::Zero());
|
||||
|
||||
NzRenderer::SetIndexBuffer(m_indexBuffer);
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix());
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, viewer->GetProjectionMatrix());
|
||||
NzRenderer::SetMatrix(nzMatrixType_View, skyboxMatrix);
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Scale(NzVector3f(camera->GetZNear())));
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Scale(NzVector3f(viewer->GetZNear())));
|
||||
NzRenderer::SetRenderStates(states);
|
||||
NzRenderer::SetShaderProgram(s_program);
|
||||
NzRenderer::SetTexture(0, m_texture);
|
||||
|
||||
114
src/Nazara/Graphics/Sprite.cpp
Normal file
114
src/Nazara/Graphics/Sprite.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Sprite.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzSprite::NzSprite() :
|
||||
m_textureCoords(0.f, 0.f, 1.f, 1.f),
|
||||
m_size(64.f, 64.f)
|
||||
{
|
||||
}
|
||||
|
||||
NzSprite::NzSprite(const NzSprite& sprite) :
|
||||
NzSceneNode(sprite),
|
||||
m_textureCoords(sprite.m_textureCoords),
|
||||
m_size(sprite.m_size),
|
||||
m_material(sprite.m_material)
|
||||
{
|
||||
}
|
||||
|
||||
NzSprite::NzSprite(NzSprite&& sprite) :
|
||||
NzSceneNode(sprite),
|
||||
m_textureCoords(sprite.m_textureCoords),
|
||||
m_size(sprite.m_size),
|
||||
m_material(std::move(sprite.m_material))
|
||||
{
|
||||
}
|
||||
|
||||
NzSprite::~NzSprite() = default;
|
||||
|
||||
void NzSprite::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const
|
||||
{
|
||||
renderQueue->AddSprite(this);
|
||||
}
|
||||
|
||||
const NzBoundingVolumef& NzSprite::GetBoundingVolume() const
|
||||
{
|
||||
static NzBoundingVolumef infinity(NzBoundingVolumef::Infinite());
|
||||
return infinity;
|
||||
}
|
||||
|
||||
NzMaterial* NzSprite::GetMaterial() const
|
||||
{
|
||||
return m_material;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzSprite::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Sprite;
|
||||
}
|
||||
|
||||
const NzVector2f& NzSprite::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
const NzRectf& NzSprite::GetTextureCoords() const
|
||||
{
|
||||
return m_textureCoords;
|
||||
}
|
||||
|
||||
void NzSprite::SetMaterial(NzMaterial* material)
|
||||
{
|
||||
m_material = material;
|
||||
}
|
||||
|
||||
void NzSprite::SetSize(const NzVector2f& size)
|
||||
{
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
void NzSprite::SetTextureCoords(const NzRectf& coords)
|
||||
{
|
||||
m_textureCoords = coords;
|
||||
}
|
||||
|
||||
void NzSprite::SetTextureRect(const NzRectui& rect)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_material)
|
||||
{
|
||||
NazaraError("Sprite has no material");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_material->HasDiffuseMap())
|
||||
{
|
||||
NazaraError("Sprite material has no diffuse map");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzTexture* diffuseMap = m_material->GetDiffuseMap();
|
||||
|
||||
float invWidth = 1.f/diffuseMap->GetWidth();
|
||||
float invHeight = 1.f/diffuseMap->GetHeight();
|
||||
|
||||
SetTextureCoords(NzRectf(invWidth*rect.x, invHeight*rect.y, invWidth*rect.width, invHeight*rect.height));
|
||||
}
|
||||
|
||||
bool NzSprite::FrustumCull(const NzFrustumf& frustum)
|
||||
{
|
||||
///TODO: Effectuer un vrai test
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSprite::Register()
|
||||
{
|
||||
}
|
||||
|
||||
void NzSprite::Unregister()
|
||||
{
|
||||
}
|
||||
279
src/Nazara/Graphics/View.cpp
Normal file
279
src/Nazara/Graphics/View.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/View.hpp>
|
||||
#include <Nazara/Graphics/Scene.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/RenderTarget.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzView::NzView() :
|
||||
m_targetRegion(0.f, 0.f, 1.f, 1.f),
|
||||
m_target(nullptr),
|
||||
m_frustumUpdated(false),
|
||||
m_projectionMatrixUpdated(false),
|
||||
m_viewMatrixUpdated(false),
|
||||
m_viewportUpdated(false),
|
||||
m_zFar(1.f),
|
||||
m_zNear(-1.f)
|
||||
{
|
||||
}
|
||||
|
||||
NzView::~NzView()
|
||||
{
|
||||
if (m_target)
|
||||
m_target->RemoveListener(this);
|
||||
}
|
||||
|
||||
void NzView::EnsureFrustumUpdate() const
|
||||
{
|
||||
if (!m_frustumUpdated)
|
||||
UpdateFrustum();
|
||||
}
|
||||
|
||||
void NzView::EnsureProjectionMatrixUpdate() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
}
|
||||
|
||||
void NzView::EnsureViewMatrixUpdate() const
|
||||
{
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
}
|
||||
|
||||
void NzView::EnsureViewportUpdate() const
|
||||
{
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
}
|
||||
|
||||
float NzView::GetAspectRatio() const
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
NzVector3f NzView::GetEyePosition() const
|
||||
{
|
||||
return GetPosition(nzCoordSys_Global);
|
||||
}
|
||||
|
||||
const NzFrustumf& NzView::GetFrustum() const
|
||||
{
|
||||
if (!m_frustumUpdated)
|
||||
UpdateFrustum();
|
||||
|
||||
return m_frustum;
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzView::GetProjectionMatrix() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
return m_projectionMatrix;
|
||||
}
|
||||
|
||||
const NzRenderTarget* NzView::GetTarget() const
|
||||
{
|
||||
return m_target;
|
||||
}
|
||||
|
||||
const NzRectf& NzView::GetTargetRegion() const
|
||||
{
|
||||
return m_targetRegion;
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzView::GetViewMatrix() const
|
||||
{
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
return m_viewMatrix;
|
||||
}
|
||||
|
||||
const NzRectui& NzView::GetViewport() const
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("Camera has no render target");
|
||||
return m_viewport;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
|
||||
return m_viewport;
|
||||
}
|
||||
|
||||
float NzView::GetZFar() const
|
||||
{
|
||||
return m_zFar;
|
||||
}
|
||||
|
||||
float NzView::GetZNear() const
|
||||
{
|
||||
return m_zNear;
|
||||
}
|
||||
|
||||
void NzView::SetTarget(const NzRenderTarget* renderTarget)
|
||||
{
|
||||
if (m_target)
|
||||
m_target->RemoveListener(this);
|
||||
|
||||
m_target = renderTarget;
|
||||
if (m_target)
|
||||
m_target->AddListener(this);
|
||||
}
|
||||
|
||||
void NzView::SetTarget(const NzRenderTarget& renderTarget)
|
||||
{
|
||||
SetTarget(&renderTarget);
|
||||
}
|
||||
|
||||
void NzView::SetTargetRegion(const NzRectf& region)
|
||||
{
|
||||
m_targetRegion = region;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
m_viewportUpdated = false;
|
||||
}
|
||||
|
||||
void NzView::SetViewport(const NzRectui& viewport)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("Camera has no render target");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// On calcule la région nécessaire pour produire ce viewport avec la taille actuelle de la cible
|
||||
float invWidth = 1.f/m_target->GetWidth();
|
||||
float invHeight = 1.f/m_target->GetHeight();
|
||||
|
||||
SetTargetRegion(NzRectf(invWidth * viewport.x, invHeight * viewport.y, invWidth * viewport.width, invHeight * viewport.height));
|
||||
}
|
||||
|
||||
void NzView::SetZFar(float zFar)
|
||||
{
|
||||
m_zFar = zFar;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzView::SetZNear(float zNear)
|
||||
{
|
||||
m_zNear = zNear;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzView::ApplyView() const
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("Camera has no render target");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix);
|
||||
NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix);
|
||||
NzRenderer::SetViewport(m_viewport);
|
||||
}
|
||||
|
||||
void NzView::Invalidate()
|
||||
{
|
||||
NzNode::Invalidate();
|
||||
|
||||
// Le frustum et la view matrix dépendent des paramètres du node, invalidons-les
|
||||
m_frustumUpdated = false;
|
||||
m_viewMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzView::OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata)
|
||||
{
|
||||
NazaraUnused(userdata);
|
||||
|
||||
if (renderTarget == m_target)
|
||||
m_target = nullptr;
|
||||
else
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
|
||||
}
|
||||
|
||||
bool NzView::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata)
|
||||
{
|
||||
NazaraUnused(userdata);
|
||||
|
||||
if (renderTarget == m_target)
|
||||
{
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
m_viewportUpdated = false;
|
||||
}
|
||||
else
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzView::UpdateFrustum() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
m_frustum.Extract(m_viewMatrix, m_projectionMatrix);
|
||||
m_frustumUpdated = true;
|
||||
}
|
||||
|
||||
void NzView::UpdateProjectionMatrix() const
|
||||
{
|
||||
if (!m_viewportUpdated)
|
||||
UpdateViewport();
|
||||
|
||||
m_projectionMatrix.MakeOrtho(m_viewport.x, m_viewport.x + m_viewport.width, m_viewport.y, m_viewport.y + m_viewport.height, m_zNear, m_zFar);
|
||||
m_projectionMatrixUpdated = true;
|
||||
}
|
||||
|
||||
void NzView::UpdateViewMatrix() const
|
||||
{
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
m_viewMatrix.MakeViewMatrix(m_derivedPosition, m_derivedRotation);
|
||||
m_viewMatrixUpdated = true;
|
||||
}
|
||||
|
||||
void NzView::UpdateViewport() const
|
||||
{
|
||||
unsigned int width = m_target->GetWidth();
|
||||
unsigned int height = std::max(m_target->GetHeight(), 1U);
|
||||
|
||||
m_viewport.x = width * m_targetRegion.x;
|
||||
m_viewport.y = height * m_targetRegion.y;
|
||||
m_viewport.width = width * m_targetRegion.width;
|
||||
m_viewport.height = height * m_targetRegion.height;
|
||||
m_viewportUpdated = true;
|
||||
}
|
||||
Reference in New Issue
Block a user