diff --git a/SDK/include/NDK/Components/CameraComponent.hpp b/SDK/include/NDK/Components/CameraComponent.hpp new file mode 100644 index 000000000..36610587e --- /dev/null +++ b/SDK/include/NDK/Components/CameraComponent.hpp @@ -0,0 +1,93 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_COMPONENTS_CAMERACOMPONENT_HPP +#define NDK_COMPONENTS_CAMERACOMPONENT_HPP + +#include +#include +#include +#include +#include + +namespace Ndk +{ + class Entity; + + class NDK_API CameraComponent : public Component, NzRenderTarget::Listener + { + public: + CameraComponent(); + ~CameraComponent(); + + void EnsureFrustumUpdate() const; + void EnsureProjectionMatrixUpdate() const; + void EnsureViewMatrixUpdate() const; + void EnsureViewportUpdate() const; + + float GetAspectRatio() const; + NzVector3f GetEyePosition() const; + NzVector3f GetForward() const; + float GetFOV() const; + const NzFrustumf& GetFrustum() const; + unsigned int GetLayer() const; + const NzMatrix4f& GetProjectionMatrix() const; + const NzRenderTarget* GetTarget() const; + const NzRectf& GetTargetRegion() const; + const NzMatrix4f& GetViewMatrix() const; + const NzRecti& GetViewport() const; + float GetZFar() const; + float GetZNear() const; + + void SetFOV(float fov); + void SetLayer(unsigned int layer); + void SetTarget(const NzRenderTarget* renderTarget); + void SetTargetRegion(const NzRectf& region); + void SetViewport(const NzRecti& viewport); + void SetZFar(float zFar); + void SetZNear(float zNear); + + static ComponentIndex componentIndex; + + private: + void InvalidateFrustum() const; + void InvalidateProjectionMatrix() const; + void InvalidateViewMatrix() const; + void InvalidateViewport() const; + + void OnAttached() override; + void OnComponentAttached(BaseComponent& component) override; + void OnComponentDetached(BaseComponent& component) override; + void OnDetached() override; + void OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) override; + bool OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) override; + + void UpdateFrustum() const; + void UpdateProjectionMatrix() const; + void UpdateViewMatrix() const; + void UpdateViewport() const; + + mutable NzFrustumf m_frustum; + mutable NzMatrix4f m_projectionMatrix; + mutable NzMatrix4f m_viewMatrix; + NzRectf m_targetRegion; + mutable NzRecti m_viewport; + const NzRenderTarget* m_target; + mutable bool m_frustumUpdated; + mutable bool m_projectionMatrixUpdated; + mutable bool m_viewMatrixUpdated; + mutable bool m_viewportUpdated; + mutable float m_aspectRatio; + float m_fov; + float m_zFar; + float m_zNear; + unsigned int m_layer; + }; +} + +#include + +#endif // NDK_COMPONENTS_CAMERACOMPONENT_HPP diff --git a/SDK/include/NDK/Components/CameraComponent.inl b/SDK/include/NDK/Components/CameraComponent.inl new file mode 100644 index 000000000..210a75389 --- /dev/null +++ b/SDK/include/NDK/Components/CameraComponent.inl @@ -0,0 +1,186 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + inline CameraComponent::CameraComponent() : + 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), + m_layer(0) + { + } + + inline CameraComponent::~CameraComponent() + { + if (m_target) + m_target->RemoveListener(this); + } + + inline void CameraComponent::EnsureFrustumUpdate() const + { + if (!m_frustumUpdated) + UpdateFrustum(); + } + + inline void CameraComponent::EnsureProjectionMatrixUpdate() const + { + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); + } + + inline void CameraComponent::EnsureViewMatrixUpdate() const + { + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); + } + + inline void CameraComponent::EnsureViewportUpdate() const + { + if (!m_viewportUpdated) + UpdateViewport(); + } + + inline float CameraComponent::GetAspectRatio() const + { + EnsureViewportUpdate(); + + return m_aspectRatio; + } + + inline float CameraComponent::GetFOV() const + { + return m_fov; + } + + inline const NzFrustumf& CameraComponent::GetFrustum() const + { + EnsureFrustumUpdate(); + + return m_frustum; + } + + inline const NzMatrix4f& CameraComponent::GetProjectionMatrix() const + { + EnsureProjectionMatrixUpdate(); + + return m_projectionMatrix; + } + + inline const NzRenderTarget* CameraComponent::GetTarget() const + { + return m_target; + } + + inline const NzRectf& CameraComponent::GetTargetRegion() const + { + return m_targetRegion; + } + + inline const NzMatrix4f& CameraComponent::GetViewMatrix() const + { + EnsureViewMatrixUpdate(); + + return m_viewMatrix; + } + + inline const NzRecti& CameraComponent::GetViewport() const + { + EnsureViewportUpdate(); + + return m_viewport; + } + + inline float CameraComponent::GetZFar() const + { + return m_zFar; + } + + inline float CameraComponent::GetZNear() const + { + return m_zNear; + } + + inline void CameraComponent::SetFOV(float fov) + { + NazaraAssert(fov != 0.f, "FOV must be different from zero"); + + m_fov = fov; + InvalidateProjectionMatrix(); + } + + inline void CameraComponent::SetTarget(const NzRenderTarget* renderTarget) + { + if (m_target) + m_target->RemoveListener(this); + + m_target = renderTarget; + if (m_target) + m_target->AddListener(this); + } + + inline void CameraComponent::SetTargetRegion(const NzRectf& region) + { + m_targetRegion = region; + InvalidateViewport(); + } + + inline void CameraComponent::SetViewport(const NzRecti& viewport) + { + NazaraAssert(m_target, "Component has no render target"); + + // 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)); + } + + inline void CameraComponent::SetZFar(float zFar) + { + m_zFar = zFar; + InvalidateProjectionMatrix(); + } + + inline void CameraComponent::SetZNear(float zNear) + { + NazaraAssert(NzNumberEquals(zNear, 0.f), "zNear cannot be zero"); + + m_zNear = zNear; + InvalidateProjectionMatrix(); + } + + inline void CameraComponent::InvalidateFrustum() const + { + m_frustumUpdated = false; + } + + inline void CameraComponent::InvalidateProjectionMatrix() const + { + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; + } + + inline void CameraComponent::InvalidateViewMatrix() const + { + m_frustumUpdated = false; + m_viewMatrixUpdated = false; + } + + inline void CameraComponent::InvalidateViewport() const + { + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; + m_viewportUpdated = false; + } +} diff --git a/SDK/src/NDK/Components/CameraComponent.cpp b/SDK/src/NDK/Components/CameraComponent.cpp new file mode 100644 index 000000000..23053b217 --- /dev/null +++ b/SDK/src/NDK/Components/CameraComponent.cpp @@ -0,0 +1,124 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include +#include + +namespace Ndk +{ + /*void CameraComponent::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 CameraComponent::OnAttached() + { + InvalidateViewMatrix(); + } + + void CameraComponent::OnComponentAttached(BaseComponent& component) + { + if (IsComponent(component)) + InvalidateViewMatrix(); + } + + void CameraComponent::OnComponentDetached(BaseComponent& component) + { + if (IsComponent(component)) + InvalidateViewMatrix(); + } + + void CameraComponent::OnDetached() + { + InvalidateViewMatrix(); + } + + void CameraComponent::OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) + { + NazaraUnused(userdata); + + if (renderTarget == m_target) + m_target = nullptr; + else + NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); + } + + bool CameraComponent::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) + { + NazaraUnused(userdata); + + if (renderTarget == m_target) + { + InvalidateViewport(); + + return true; + } + else + { + NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); + return false; + } + } + + void CameraComponent::UpdateFrustum() const + { + EnsureProjectionMatrixUpdate(); + EnsureViewMatrixUpdate(); + + m_frustum.Extract(m_viewMatrix, m_projectionMatrix); + m_frustumUpdated = true; + } + + void CameraComponent::UpdateProjectionMatrix() const + { + EnsureViewportUpdate(); // Peut affecter l'aspect ratio + + m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar); + m_projectionMatrixUpdated = true; + } + + void CameraComponent::UpdateViewMatrix() const + { + NazaraAssert(m_entity && m_entity->HasComponent(), "CameraComponent requires NodeComponent"); + + NodeComponent& nodeComponent = m_entity->GetComponent(); + + m_viewMatrix.MakeViewMatrix(nodeComponent.GetPosition(nzCoordSys_Global), nodeComponent.GetRotation(nzCoordSys_Global)); + m_viewMatrixUpdated = true; + } + + void CameraComponent::UpdateViewport() const + { + NazaraAssert(m_target, "CameraComponent has no target"); + + unsigned int targetWidth = m_target->GetWidth(); + unsigned int targetHeight = std::max(m_target->GetHeight(), 1U); // Pour éviter une division par zéro + + // La zone visée est définie en "pourcentage" (0...1), on calcule les valeurs en pixel + NzRectf fViewport(m_targetRegion); + fViewport.x *= targetWidth; + fViewport.y *= targetHeight; + fViewport.width *= targetWidth; + fViewport.height *= targetHeight; + + // On calcule le rapport largeur/hauteur, et s'il est différent on invalide + float aspectRatio = fViewport.width/fViewport.height; + if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f)) + { + m_aspectRatio = aspectRatio; + + InvalidateProjectionMatrix(); + } + + m_viewport.Set(fViewport); + m_viewportUpdated = true; + } + + ComponentIndex CameraComponent::componentIndex; +} diff --git a/SDK/src/NDK/Sdk.cpp b/SDK/src/NDK/Sdk.cpp index 456332a88..407a88d38 100644 --- a/SDK/src/NDK/Sdk.cpp +++ b/SDK/src/NDK/Sdk.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ namespace Ndk BaseSystem::Initialize(); // Composants + InitializeComponent("NdkCam"); InitializeComponent("NdkColli"); InitializeComponent("NdkList"); InitializeComponent("NdkNode");