// 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 #include #include #include #include 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(width * m_targetRegion.x); m_viewport.y = static_cast(height * m_targetRegion.y); m_viewport.width = static_cast(vWidth); m_viewport.height = static_cast(vHeight); m_viewportUpdated = true; }