/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include "Node.hpp" #include "Math/Math.hpp" namespace openVulkanoCpp::Scene { class Camera : public Node { protected: Math::Matrix4f viewProjection{1}, view{1}, projection{1}; Math::Vector4f camPosition{}; float nearPlane, farPlane, width, height; Camera() : nearPlane(0), farPlane(0), width(0), height(0) {} Camera(float width, float height, float nearPlane, float farPlane) : nearPlane(nearPlane), farPlane(farPlane), width(width), height(height) { } ~Camera() override = default; public: void Init(float width, float height, float nearPlane, float farPlane) { this->width = width; this->height = height; this->nearPlane = nearPlane; this->farPlane = farPlane; Node::Init(); UpdateProjectionMatrix(); } virtual void SetSize(const float width, const float height) { if (this->width == width && this->height == height) return; this->width = width; this->height = height; UpdateProjectionMatrix(); } void SetNearPlane(float nearPlane) { this->nearPlane = nearPlane; } void SetFarPlane(float farPlane) { this->farPlane = farPlane; } [[nodiscard]] float NearPlane() const { return nearPlane; } [[nodiscard]] float FarPlane() const { return farPlane; } virtual void UpdateProjectionMatrix() = 0; void UpdateViewProjectionMatrix() { //TODO this should be done based on the clipspace used by the rendering api // In vulkan the screen space is defined as y=0=top and y=1=bottom and thus the coordinate have to be flipped viewProjection = projection * Math::Matrix4f(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1) * view; } void UpdateWorldMatrix(const Math::Matrix4f& parentWorldMat) override { Node::UpdateWorldMatrix(parentWorldMat); view = Math::Utils::inverse(GetWorldMatrix()); camPosition = GetWorldMatrix()[3]; UpdateViewProjectionMatrix(); } [[nodiscard]] const Math::Matrix4f& GetViewProjectionMatrix() const { return viewProjection; } }; class PerspectiveCamera : public Camera { protected: float fov = 0, aspect = 0; public: PerspectiveCamera() = default; ~PerspectiveCamera() override = default; PerspectiveCamera(float fovDegrees, float nearPlane = 0.1f, float farPlane = 1000.0f, float width = 16, float height = 9) : Camera() { Init(fovDegrees, width, height, nearPlane, farPlane); PerspectiveCamera::UpdateProjectionMatrix(); } void Init(float fovDegrees, float nearPlane, float farPlane) { Init(fovDegrees, 16, 9, nearPlane, farPlane); } void Init(float fovDegrees, float width, float height, float nearPlane, float farPlane) { this->fov = Math::Utils::radians(fovDegrees); aspect = width / height; Camera::Init(width, height, nearPlane, farPlane); } void SetSize(const float width, const float height) override { aspect = width / height; Camera::SetSize(width, height); } void SetAspect(const float aspect) { this->aspect = aspect; Camera::SetSize(aspect, 1); } void SetFovX(const float fov) { SetFov(2 * atan(tan(fov * 0.5f) * aspect)); } void SetFovXRad(const float fov) { SetFovRad(2 * atan(tan(fov * 0.5f) * aspect)); } void SetFov(const float fov) { SetFovRad(Math::Utils::radians(fov)); } void SetFovRad(const float fov) { this->fov = fov; } [[nodiscard]] float GetFov() const { return Math::Utils::degrees(fov); } [[nodiscard]] float GetFovX() const { return 2.0f * atanf(tanf(GetFov() * 0.5f) * aspect); } [[nodiscard]] float GetFovRad() const { return fov; } [[nodiscard]] float GetFovXRad() const { return 2.0f * atanf(tanf(fov * 0.5f) * aspect); } void UpdateProjectionMatrix() final { projection = Math::Utils::perspectiveRH_ZO(fov, aspect, nearPlane, farPlane); UpdateViewProjectionMatrix(); } }; class OrthographicCamera : public Camera { public: void UpdateProjectionMatrix() final { const float widthHalf = width * 0.5f, heightHalf = height * 0.5f; projection = Math::Utils::orthoRH_ZO(-widthHalf, widthHalf, -heightHalf, heightHalf, nearPlane, farPlane); UpdateViewProjectionMatrix(); } }; }