NazaraEngine/src/Nazara/Graphics/Camera.cpp

355 lines
7.1 KiB
C++

// Copyright (C) 2015 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/Camera.hpp>
#include <Nazara/Graphics/Scene.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzCamera::NzCamera() :
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_aspectRatio(0.f),
m_fov(70.f),
m_zFar(100.f),
m_zNear(1.f)
{
}
void NzCamera::EnsureFrustumUpdate() const
{
if (!m_frustumUpdated)
UpdateFrustum();
}
void NzCamera::EnsureProjectionMatrixUpdate() const
{
if (!m_projectionMatrixUpdated)
UpdateProjectionMatrix();
}
void NzCamera::EnsureViewMatrixUpdate() const
{
if (!m_viewMatrixUpdated)
UpdateViewMatrix();
}
void NzCamera::EnsureViewportUpdate() const
{
if (!m_viewportUpdated)
UpdateViewport();
}
float NzCamera::GetAspectRatio() const
{
return m_aspectRatio;
}
NzVector3f NzCamera::GetEyePosition() const
{
return GetPosition(nzCoordSys_Global);
}
NzVector3f NzCamera::GetForward() const
{
return NzNode::GetForward();
}
float NzCamera::GetFOV() const
{
return m_fov;
}
const NzFrustumf& NzCamera::GetFrustum() const
{
if (!m_frustumUpdated)
UpdateFrustum();
return m_frustum;
}
NzVector3f NzCamera::GetGlobalForward() const
{
return NzVector3f::Forward();
}
NzVector3f NzCamera::GetGlobalRight() const
{
return NzVector3f::Right();
}
NzVector3f NzCamera::GetGlobalUp() const
{
return NzVector3f::Up();
}
const NzMatrix4f& NzCamera::GetProjectionMatrix() const
{
if (!m_projectionMatrixUpdated)
UpdateProjectionMatrix();
return m_projectionMatrix;
}
const NzRenderTarget* NzCamera::GetTarget() const
{
return m_target;
}
const NzRectf& NzCamera::GetTargetRegion() const
{
return m_targetRegion;
}
const NzMatrix4f& NzCamera::GetViewMatrix() const
{
if (!m_viewMatrixUpdated)
UpdateViewMatrix();
return m_viewMatrix;
}
const NzRecti& NzCamera::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 NzCamera::GetZFar() const
{
return m_zFar;
}
float NzCamera::GetZNear() const
{
return m_zNear;
}
void NzCamera::SetFOV(float fov)
{
#if NAZARA_GRAPHICS_SAFE
if (NzNumberEquals(fov, 0.f))
{
NazaraError("FOV must be different from zero");
return;
}
#endif
m_fov = fov;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
}
void NzCamera::SetTarget(const NzRenderTarget* renderTarget)
{
m_target = renderTarget;
if (m_target)
{
m_targetReleaseSlot.Connect(m_target->OnRenderTargetRelease, this, &NzCamera::OnRenderTargetRelease);
m_targetResizeSlot.Connect(m_target->OnRenderTargetSizeChange, this, &NzCamera::OnRenderTargetSizeChange);
}
else
{
m_targetReleaseSlot.Disconnect();
m_targetResizeSlot.Disconnect();
}
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
m_viewportUpdated = false;
}
void NzCamera::SetTarget(const NzRenderTarget& renderTarget)
{
SetTarget(&renderTarget);
}
void NzCamera::SetTargetRegion(const NzRectf& region)
{
m_targetRegion = region;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
m_viewportUpdated = false;
}
void NzCamera::SetViewport(const NzRecti& 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 NzCamera::SetZFar(float zFar)
{
m_zFar = zFar;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
}
void NzCamera::SetZNear(float zNear)
{
#if NAZARA_GRAPHICS_SAFE
if (zNear < 0.f || NzNumberEquals(zNear, 0.f))
{
NazaraError("ZNear shall be a strictly positive number");
return;
}
#endif
m_zNear = zNear;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
}
void NzCamera::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::SetTarget(m_target);
NzRenderer::SetViewport(m_viewport);
}
void NzCamera::InvalidateNode()
{
NzNode::InvalidateNode();
// Le frustum et la view matrix dépendent des paramètres du node, invalidons-les
m_frustumUpdated = false;
m_viewMatrixUpdated = false;
}
void NzCamera::OnRenderTargetRelease(const NzRenderTarget* renderTarget)
{
if (renderTarget == m_target)
m_target = nullptr;
else
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
}
void NzCamera::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget)
{
if (renderTarget == m_target)
{
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
m_viewportUpdated = false;
}
else
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
}
void NzCamera::UpdateFrustum() const
{
if (!m_projectionMatrixUpdated)
UpdateProjectionMatrix();
if (!m_viewMatrixUpdated)
UpdateViewMatrix();
m_frustum.Extract(m_viewMatrix, m_projectionMatrix);
m_frustumUpdated = true;
}
void NzCamera::UpdateProjectionMatrix() const
{
#if NAZARA_GRAPHICS_SAFE
// Il n'y a pas grand chose à faire d'autre qu'un avertissement à ce stade
if (m_zNear >= m_zFar)
NazaraWarning("ZNear is greater or equal to ZFar (" + NzString::Number(m_zNear) + " >= " + NzString::Number(m_zFar) + ").");
#endif
if (!m_viewportUpdated)
UpdateViewport(); // Peut affecter l'aspect ratio
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
m_projectionMatrixUpdated = true;
}
void NzCamera::UpdateViewMatrix() const
{
if (!m_derivedUpdated)
UpdateDerived();
m_viewMatrix.MakeViewMatrix(m_derivedPosition, m_derivedRotation);
m_viewMatrixUpdated = true;
}
void NzCamera::UpdateViewport() const
{
#if NAZARA_GRAPHICS_SAFE
if (!m_target)
{
NazaraError("Camera has no render target");
return;
}
#endif
unsigned int width = m_target->GetWidth();
unsigned int height = std::max(m_target->GetHeight(), 1U);
float vWidth = width * m_targetRegion.width;
float vHeight = height * m_targetRegion.height;
float aspectRatio = vWidth/vHeight;
if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f))
{
m_aspectRatio = aspectRatio;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
}
m_viewport.x = static_cast<int>(width * m_targetRegion.x);
m_viewport.y = static_cast<int>(height * m_targetRegion.y);
m_viewport.width = static_cast<int>(vWidth);
m_viewport.height = static_cast<int>(vHeight);
m_viewportUpdated = true;
}