diff --git a/Resources/Editor/Models/Arrow.fbx b/Resources/Editor/Models/Arrow.fbx deleted file mode 100644 index d7941931..00000000 Binary files a/Resources/Editor/Models/Arrow.fbx and /dev/null differ diff --git a/Resources/Editor/Models/Arrow_Rotate.fbx b/Resources/Editor/Models/Arrow_Rotate.fbx new file mode 100644 index 00000000..0aaadb2f Binary files /dev/null and b/Resources/Editor/Models/Arrow_Rotate.fbx differ diff --git a/Resources/Editor/Models/Arrow_Scale.fbx b/Resources/Editor/Models/Arrow_Scale.fbx new file mode 100644 index 00000000..eff044eb Binary files /dev/null and b/Resources/Editor/Models/Arrow_Scale.fbx differ diff --git a/Resources/Editor/Models/Arrow_Translate.fbx b/Resources/Editor/Models/Arrow_Translate.fbx new file mode 100644 index 00000000..7a52a0ec Binary files /dev/null and b/Resources/Editor/Models/Arrow_Translate.fbx differ diff --git a/Sources/Overload/OvAnalytics/OvAnalytics.vcxproj b/Sources/Overload/OvAnalytics/OvAnalytics.vcxproj index 2c0a6880..72a680c1 100644 --- a/Sources/Overload/OvAnalytics/OvAnalytics.vcxproj +++ b/Sources/Overload/OvAnalytics/OvAnalytics.vcxproj @@ -14,19 +14,19 @@ 15.0 {45BFDD00-877F-42ED-A0E4-92E56F7D3C0E} OvAnalytics - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvAudio/OvAudio.vcxproj b/Sources/Overload/OvAudio/OvAudio.vcxproj index 54da97b1..5d5acb78 100644 --- a/Sources/Overload/OvAudio/OvAudio.vcxproj +++ b/Sources/Overload/OvAudio/OvAudio.vcxproj @@ -14,19 +14,19 @@ 15.0 {E76098C7-D1C1-430E-A7B2-527CF45BD853} OvAudio - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvCore/OvCore.vcxproj b/Sources/Overload/OvCore/OvCore.vcxproj index eb8527ce..8e0321e1 100644 --- a/Sources/Overload/OvCore/OvCore.vcxproj +++ b/Sources/Overload/OvCore/OvCore.vcxproj @@ -14,19 +14,19 @@ 15.0 {C1D2EBB7-EE2A-4D42-858B-F14F5A47B867} OvCore - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte @@ -123,7 +123,7 @@ xcopy "$(SolutionDir)..\..\Build\OvWindowing\bin\$(Configuration)\*.dll" "$(Solu xcopy "$(SolutionDir)..\..\Build\OvWindowing\lib\$(Configuration)\*.lib" "$(SolutionDir)..\..\Build\$(ProjectName)\lib\$(Configuration)" /e /y /i /r - liblua53.a;OvMaths.lib;OvPhysics.lib;OvRendering.lib;OvTools.lib;OvUI.lib;OvAudio.lib;OvDebug.lib;OvWindowing.lib;%(AdditionalDependencies) + liblua53.a;OvMaths.lib;OvPhysics.lib;OvRendering.lib;OvTools.lib;OvUI.lib;OvAudio.lib;OvDebug.lib;OvWindowing.lib;OvAnalytics.lib;%(AdditionalDependencies) @@ -140,7 +140,7 @@ xcopy "$(SolutionDir)..\..\Build\OvWindowing\lib\$(Configuration)\*.lib" "$(Solu true true - liblua53.a;OvMaths.lib;OvPhysics.lib;OvRendering.lib;OvTools.lib;OvUI.lib;OvAudio.lib;OvDebug.lib;OvWindowing.lib;%(AdditionalDependencies) + liblua53.a;OvMaths.lib;OvPhysics.lib;OvRendering.lib;OvTools.lib;OvUI.lib;OvAudio.lib;OvDebug.lib;OvWindowing.lib;OvAnalytics.lib;%(AdditionalDependencies) RD /S /Q "$(SolutionDir)..\..\Build\$(ProjectName)\include" diff --git a/Sources/Overload/OvCore/include/OvCore/ECS/Components/Behaviour.inl b/Sources/Overload/OvCore/include/OvCore/ECS/Components/Behaviour.inl index 5ea50945..76605033 100644 --- a/Sources/Overload/OvCore/include/OvCore/ECS/Components/Behaviour.inl +++ b/Sources/Overload/OvCore/include/OvCore/ECS/Components/Behaviour.inl @@ -6,6 +6,9 @@ #pragma once + +#include + #include "OvCore/ECS/Components/Behaviour.h" namespace OvCore::ECS::Components diff --git a/Sources/Overload/OvCore/include/OvCore/ECS/Components/CCamera.h b/Sources/Overload/OvCore/include/OvCore/ECS/Components/CCamera.h index 4e3c2476..0e435a40 100644 --- a/Sources/Overload/OvCore/include/OvCore/ECS/Components/CCamera.h +++ b/Sources/Overload/OvCore/include/OvCore/ECS/Components/CCamera.h @@ -35,18 +35,6 @@ namespace OvCore::ECS::Components */ std::string GetName() override; - /** - * Returns the projection matrix - * @param p_windowWidth - * @param p_windowHeight - */ - OvMaths::FMatrix4 GetProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight); - - /** - * Returns the view matrix - */ - OvMaths::FMatrix4 GetViewMatrix(); - /** * Sets the fov of the camera to the given value * @param p_value @@ -71,6 +59,18 @@ namespace OvCore::ECS::Components */ void SetClearColor(const OvMaths::FVector3& p_clearColor); + /** + * Defines if the camera should apply frustum geometry culling in rendering + * @param p_enable + */ + void SetFrustumGeometryCulling(bool p_enable); + + /** + * Defines if the camera should apply frustum light culling in rendering + * @param p_enable + */ + void SetFrustumLightCulling(bool p_enable); + /** * Returns the fov of the camera */ @@ -91,6 +91,16 @@ namespace OvCore::ECS::Components */ const OvMaths::FVector3& GetClearColor() const; + /** + * Returns true if the frustum geometry culling is enabled + */ + bool HasFrustumGeometryCulling() const; + + /** + * Returns true if the frustum light culling is enabled + */ + bool HasFrustumLightCulling() const; + /** * Returns the OvRendering camera instance attached to this component */ diff --git a/Sources/Overload/OvCore/include/OvCore/ECS/Components/CModelRenderer.h b/Sources/Overload/OvCore/include/OvCore/ECS/Components/CModelRenderer.h index 6af09db8..e62a7086 100644 --- a/Sources/Overload/OvCore/include/OvCore/ECS/Components/CModelRenderer.h +++ b/Sources/Overload/OvCore/include/OvCore/ECS/Components/CModelRenderer.h @@ -21,6 +21,17 @@ namespace OvCore::ECS::Components class API_OVCORE CModelRenderer : public AComponent { public: + /** + * Defines how the model renderer bounding sphere should be interpreted + */ + enum class EFrustumBehaviour + { + DISABLED = 0, + CULL_MODEL = 1, + CULL_MESHES = 2, + CULL_CUSTOM = 3 + }; + /** * Constructor * @param p_owner @@ -43,6 +54,28 @@ namespace OvCore::ECS::Components */ OvRendering::Resources::Model* GetModel() const; + /** + * Sets a bounding mode + * @param p_boundingMode + */ + void SetFrustumBehaviour(EFrustumBehaviour p_boundingMode); + + /** + * Returns the current bounding mode + */ + EFrustumBehaviour GetFrustumBehaviour() const; + + /** + * Returns the custom bounding sphere + */ + const OvRendering::Geometry::BoundingSphere& GetCustomBoundingSphere() const; + + /** + * Sets the custom bounding sphere + * @param p_boundingSphere + */ + void SetCustomBoundingSphere(const OvRendering::Geometry::BoundingSphere& p_boundingSphere); + /** * Serialize the component * @param p_doc @@ -66,5 +99,7 @@ namespace OvCore::ECS::Components private: OvRendering::Resources::Model* m_model = nullptr; OvTools::Eventing::Event<> m_modelChangedEvent; + OvRendering::Geometry::BoundingSphere m_customBoundingSphere = { {}, 1.0f }; + EFrustumBehaviour m_frustumBehaviour = EFrustumBehaviour::CULL_MODEL; }; } \ No newline at end of file diff --git a/Sources/Overload/OvCore/include/OvCore/ECS/Renderer.h b/Sources/Overload/OvCore/include/OvCore/ECS/Renderer.h index 4fc74fe5..95eb262b 100644 --- a/Sources/Overload/OvCore/include/OvCore/ECS/Renderer.h +++ b/Sources/Overload/OvCore/include/OvCore/ECS/Renderer.h @@ -10,6 +10,7 @@ #include #include +#include #include "OvCore/API/Export.h" #include "OvCore/Resources/Material.h" @@ -51,29 +52,60 @@ namespace OvCore::ECS /** * Fill the given FMatrix4 vector with lights information * @param p_scene - * @param p_out */ - void FindLightMatrices(const OvCore::SceneSystem::Scene& p_scene, std::vector& p_out); + std::vector FindLightMatrices(const OvCore::SceneSystem::Scene& p_scene); + + /** + * Fill the given FMatrix4 vector with lights information that are inside the frustum + * @param p_scene + * @param p_frustum + */ + std::vector FindLightMatricesInFrustum(const OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum); /** * Draw the given scene using the given default material (optional) if no material found on an actor * @param p_scene * @param p_cameraPosition + * @param p_camera + * @param p_customFrustum + * @param p_defaultMaterial + */ + void RenderScene + ( + OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + const OvRendering::LowRenderer::Camera& p_camera, + const OvRendering::Data::Frustum* p_customFrustum = nullptr, + OvCore::Resources::Material* p_defaultMaterial = nullptr + ); + + /** + * Returns opaque and transparents drawables from the scene with frustum culling + * @param p_scene + * @param p_cameraPosition + * @param p_frustum * @param p_defaultMaterial */ - void RenderScene(OvCore::SceneSystem::Scene& p_scene, const OvMaths::FVector3& p_cameraPosition, Resources::Material* p_defaultMaterial = nullptr); + std::pair FindAndSortFrustumCulledDrawables + ( + const OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + const OvRendering::Data::Frustum& p_frustum, + OvCore::Resources::Material* p_defaultMaterial + ); /** - * Find every drawables objects in the scene. Sorting order: - * - Opaques (Front to back) - * - Transparents (Back to front) - * @param p_opaques - * @param p_transparents + * Returns opaque and transparents drawables from the scene * @param p_scene * @param p_cameraPosition * @param p_defaultMaterial */ - void FindAndSortDrawables(OpaqueDrawables& p_opaques, TransparentDrawables& p_transparents, const OvCore::SceneSystem::Scene& p_scene, const OvMaths::FVector3& p_cameraPosition, Resources::Material* p_defaultMaterial = nullptr); + std::pair FindAndSortDrawables + ( + const OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + OvCore::Resources::Material* p_defaultMaterial + ); /** * Draw a Drawable instance diff --git a/Sources/Overload/OvCore/src/OvCore/ECS/Components/CCamera.cpp b/Sources/Overload/OvCore/src/OvCore/ECS/Components/CCamera.cpp index 9a2b5a27..b833f199 100644 --- a/Sources/Overload/OvCore/src/OvCore/ECS/Components/CCamera.cpp +++ b/Sources/Overload/OvCore/src/OvCore/ECS/Components/CCamera.cpp @@ -35,17 +35,6 @@ std::string OvCore::ECS::Components::CCamera::GetName() return "Camera"; } - -OvMaths::FMatrix4 OvCore::ECS::Components::CCamera::GetProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) -{ - return m_camera.GetProjectionMatrix(p_windowWidth, p_windowHeight); -} - -OvMaths::FMatrix4 OvCore::ECS::Components::CCamera::GetViewMatrix() -{ - return m_camera.GetViewMatrix(owner.transform.GetWorldPosition()); -} - void OvCore::ECS::Components::CCamera::SetFov(float p_value) { m_camera.SetFov(p_value); @@ -61,6 +50,16 @@ void OvCore::ECS::Components::CCamera::SetFar(float p_value) m_camera.SetFar(p_value); } +void OvCore::ECS::Components::CCamera::SetFrustumGeometryCulling(bool p_enable) +{ + m_camera.SetFrustumGeometryCulling(p_enable); +} + +void OvCore::ECS::Components::CCamera::SetFrustumLightCulling(bool p_enable) +{ + m_camera.SetFrustumLightCulling(p_enable); +} + float OvCore::ECS::Components::CCamera::GetFov() const { return m_camera.GetFov(); @@ -81,11 +80,21 @@ const OvMaths::FVector3 & OvCore::ECS::Components::CCamera::GetClearColor() cons return m_camera.GetClearColor(); } +bool OvCore::ECS::Components::CCamera::HasFrustumGeometryCulling() const +{ + return m_camera.HasFrustumGeometryCulling(); +} + void OvCore::ECS::Components::CCamera::SetClearColor(const OvMaths::FVector3 & p_clearColor) { m_camera.SetClearColor(p_clearColor); } +bool OvCore::ECS::Components::CCamera::HasFrustumLightCulling() const +{ + return m_camera.HasFrustumLightCulling(); +} + OvRendering::LowRenderer::Camera & OvCore::ECS::Components::CCamera::GetCamera() { return m_camera; @@ -97,6 +106,8 @@ void OvCore::ECS::Components::CCamera::OnSerialize(tinyxml2::XMLDocument & p_doc OvCore::Helpers::Serializer::SerializeFloat(p_doc, p_node, "near", m_camera.GetNear()); OvCore::Helpers::Serializer::SerializeFloat(p_doc, p_node, "far", m_camera.GetFar()); OvCore::Helpers::Serializer::SerializeVec3(p_doc, p_node, "clear_color", m_camera.GetClearColor()); + OvCore::Helpers::Serializer::SerializeBoolean(p_doc, p_node, "frustum_geometry_culling", m_camera.HasFrustumGeometryCulling()); + OvCore::Helpers::Serializer::SerializeBoolean(p_doc, p_node, "frustum_light_culling", m_camera.HasFrustumLightCulling()); } void OvCore::ECS::Components::CCamera::OnDeserialize(tinyxml2::XMLDocument & p_doc, tinyxml2::XMLNode * p_node) @@ -104,7 +115,9 @@ void OvCore::ECS::Components::CCamera::OnDeserialize(tinyxml2::XMLDocument & p_d m_camera.SetFov(OvCore::Helpers::Serializer::DeserializeFloat(p_doc, p_node, "fov")); m_camera.SetNear(OvCore::Helpers::Serializer::DeserializeFloat(p_doc, p_node, "near")); m_camera.SetFar(OvCore::Helpers::Serializer::DeserializeFloat(p_doc, p_node, "far")); - SetClearColor(OvCore::Helpers::Serializer::DeserializeVec3(p_doc, p_node, "clear_color")); + m_camera.SetClearColor(OvCore::Helpers::Serializer::DeserializeVec3(p_doc, p_node, "clear_color")); + m_camera.SetFrustumGeometryCulling(OvCore::Helpers::Serializer::DeserializeBoolean(p_doc, p_node, "frustum_geometry_culling")); + m_camera.SetFrustumLightCulling(OvCore::Helpers::Serializer::DeserializeBoolean(p_doc, p_node, "frustum_light_culling")); } void OvCore::ECS::Components::CCamera::OnInspector(OvUI::Internal::WidgetContainer& p_root) @@ -113,4 +126,6 @@ void OvCore::ECS::Components::CCamera::OnInspector(OvUI::Internal::WidgetContain OvCore::Helpers::GUIDrawer::DrawScalar(p_root, "Near", std::bind(&CCamera::GetNear, this), std::bind(&CCamera::SetNear, this, std::placeholders::_1)); OvCore::Helpers::GUIDrawer::DrawScalar(p_root, "Far", std::bind(&CCamera::GetFar, this), std::bind(&CCamera::SetFar, this, std::placeholders::_1)); OvCore::Helpers::GUIDrawer::DrawColor(p_root, "Clear color", [this]() {return reinterpret_cast(GetClearColor()); }, [this](OvUI::Types::Color p_color) { SetClearColor({ p_color.r, p_color.g, p_color.b }); }, false); + OvCore::Helpers::GUIDrawer::DrawBoolean(p_root, "Frustum Geometry Culling", std::bind(&CCamera::HasFrustumGeometryCulling, this), std::bind(&CCamera::SetFrustumGeometryCulling, this, std::placeholders::_1)); + OvCore::Helpers::GUIDrawer::DrawBoolean(p_root, "Frustum Light Culling", std::bind(&CCamera::HasFrustumLightCulling, this), std::bind(&CCamera::SetFrustumLightCulling, this, std::placeholders::_1)); } diff --git a/Sources/Overload/OvCore/src/OvCore/ECS/Components/CModelRenderer.cpp b/Sources/Overload/OvCore/src/OvCore/ECS/Components/CModelRenderer.cpp index a7b22460..7451a978 100644 --- a/Sources/Overload/OvCore/src/OvCore/ECS/Components/CModelRenderer.cpp +++ b/Sources/Overload/OvCore/src/OvCore/ECS/Components/CModelRenderer.cpp @@ -5,8 +5,12 @@ */ #include +#include #include #include +#include +#include +#include #include "OvCore/Global/ServiceLocator.h" #include "OvCore/ResourceManagement/TextureManager.h" @@ -41,17 +45,71 @@ OvRendering::Resources::Model * OvCore::ECS::Components::CModelRenderer::GetMode return m_model; } +void OvCore::ECS::Components::CModelRenderer::SetFrustumBehaviour(EFrustumBehaviour p_boundingMode) +{ + m_frustumBehaviour = p_boundingMode; +} + +OvCore::ECS::Components::CModelRenderer::EFrustumBehaviour OvCore::ECS::Components::CModelRenderer::GetFrustumBehaviour() const +{ + return m_frustumBehaviour; +} + +const OvRendering::Geometry::BoundingSphere& OvCore::ECS::Components::CModelRenderer::GetCustomBoundingSphere() const +{ + return m_customBoundingSphere; +} + +void OvCore::ECS::Components::CModelRenderer::SetCustomBoundingSphere(const OvRendering::Geometry::BoundingSphere& p_boundingSphere) +{ + m_customBoundingSphere = p_boundingSphere; +} + void OvCore::ECS::Components::CModelRenderer::OnSerialize(tinyxml2::XMLDocument & p_doc, tinyxml2::XMLNode * p_node) { OvCore::Helpers::Serializer::SerializeModel(p_doc, p_node, "model", m_model); + OvCore::Helpers::Serializer::SerializeInt(p_doc, p_node, "frustum_behaviour", reinterpret_cast(m_frustumBehaviour)); + OvCore::Helpers::Serializer::SerializeVec3(p_doc, p_node, "custom_bounding_sphere_position", m_customBoundingSphere.position); + OvCore::Helpers::Serializer::SerializeFloat(p_doc, p_node, "custom_bounding_sphere_radius", m_customBoundingSphere.radius); } void OvCore::ECS::Components::CModelRenderer::OnDeserialize(tinyxml2::XMLDocument & p_doc, tinyxml2::XMLNode* p_node) { OvCore::Helpers::Serializer::DeserializeModel(p_doc, p_node, "model", m_model); + OvCore::Helpers::Serializer::DeserializeInt(p_doc, p_node, "frustum_behaviour", reinterpret_cast(m_frustumBehaviour)); + OvCore::Helpers::Serializer::DeserializeVec3(p_doc, p_node, "custom_bounding_sphere_position", m_customBoundingSphere.position); + OvCore::Helpers::Serializer::DeserializeFloat(p_doc, p_node, "custom_bounding_sphere_radius", m_customBoundingSphere.radius); } void OvCore::ECS::Components::CModelRenderer::OnInspector(OvUI::Internal::WidgetContainer& p_root) { - OvCore::Helpers::GUIDrawer::DrawMesh(p_root, "Model", m_model, &m_modelChangedEvent); + using namespace OvCore::Helpers; + + GUIDrawer::DrawMesh(p_root, "Model", m_model, &m_modelChangedEvent); + + GUIDrawer::CreateTitle(p_root, "Frustum Culling Behaviour"); + auto& boundingMode = p_root.CreateWidget(0); + boundingMode.choices.emplace(0, "Disabled"); + boundingMode.choices.emplace(1, "Cull model"); + boundingMode.choices.emplace(2, "Cull model & sub-meshes"); + boundingMode.choices.emplace(3, "Cull custom bounding sphere"); + auto& boundingModeDispatcher = boundingMode.AddPlugin>(); + boundingModeDispatcher.RegisterReference(reinterpret_cast(m_frustumBehaviour)); + + auto& centerLabel = p_root.CreateWidget("Bounding Sphere Center", GUIDrawer::TitleColor); + auto& centerWidget = p_root.CreateWidget>(GUIDrawer::GetDataType(), GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT, 0.f, 0.05f, "", GUIDrawer::GetFormat()); + auto& centerDispatcher = centerWidget.AddPlugin>>(); + centerDispatcher.RegisterReference(reinterpret_cast&>(m_customBoundingSphere.position)); + + auto& radiusLabel = p_root.CreateWidget("Bounding Sphere Radius", GUIDrawer::TitleColor); + auto& radiusWidget = p_root.CreateWidget(0.0f, GUIDrawer::_MAX_FLOAT, 0.f, 0.1f); + auto& radiusDispatcher = radiusWidget.AddPlugin>(); + radiusDispatcher.RegisterReference(m_customBoundingSphere.radius); + + boundingMode.ValueChangedEvent += [&](int p_choice) + { + centerLabel.enabled = centerWidget.enabled = radiusLabel.enabled = radiusWidget.enabled = p_choice == 3; + }; + + centerLabel.enabled = centerWidget.enabled = radiusLabel.enabled = radiusWidget.enabled = m_frustumBehaviour == EFrustumBehaviour::CULL_CUSTOM; } diff --git a/Sources/Overload/OvCore/src/OvCore/ECS/Renderer.cpp b/Sources/Overload/OvCore/src/OvCore/ECS/Renderer.cpp index 869f7faf..d52da5f5 100644 --- a/Sources/Overload/OvCore/src/OvCore/ECS/Renderer.cpp +++ b/Sources/Overload/OvCore/src/OvCore/ECS/Renderer.cpp @@ -4,7 +4,10 @@ * @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. */ +#include + #include +#include #include "OvCore/ECS/Renderer.h" #include "OvCore/ECS/Components/CModelRenderer.h" @@ -36,30 +39,87 @@ OvCore::ECS::Components::CCamera* OvCore::ECS::Renderer::FindMainCamera(const Ov return nullptr; } -void OvCore::ECS::Renderer::FindLightMatrices(const OvCore::SceneSystem::Scene& p_scene, std::vector& p_out) +std::vector OvCore::ECS::Renderer::FindLightMatrices(const OvCore::SceneSystem::Scene& p_scene) { + std::vector result; + const auto& facs = p_scene.GetFastAccessComponents(); for (auto light : facs.lights) + { if (light->owner.IsActive()) - p_out.push_back(light->GetData().GenerateMatrix()); + { + result.push_back(light->GetData().GenerateMatrix()); + } + } + + return result; } -void OvCore::ECS::Renderer::RenderScene(OvCore::SceneSystem::Scene& p_scene, const OvMaths::FVector3& p_cameraPosition, Resources::Material* p_defaultMaterial) + + +std::vector OvCore::ECS::Renderer::FindLightMatricesInFrustum(const OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum) { - OpaqueDrawables opaqueMeshes; - TransparentDrawables transparentMeshes; + std::vector result; + + const auto& facs = p_scene.GetFastAccessComponents(); - FindAndSortDrawables(opaqueMeshes, transparentMeshes, p_scene, p_cameraPosition, p_defaultMaterial); + for (auto light : facs.lights) + { + if (light->owner.IsActive()) + { + const auto& lightData = light->GetData(); + const auto& position = lightData.GetTransform().GetWorldPosition(); + auto effectRange = lightData.GetEffectRange(); + + // We always consider lights that have an +inf range (Not necessary to test if they are in frustum) + if (std::isinf(effectRange) || p_frustum.SphereInFrustum(position.x, position.y, position.z, lightData.GetEffectRange())) + { + result.push_back(lightData.GenerateMatrix()); + } + } + } + + return result; +} + +void OvCore::ECS::Renderer::RenderScene +( + OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + const OvRendering::LowRenderer::Camera& p_camera, + const OvRendering::Data::Frustum* p_customFrustum, + OvCore::Resources::Material* p_defaultMaterial +) +{ + OpaqueDrawables opaqueMeshes; + TransparentDrawables transparentMeshes; + + if (p_camera.HasFrustumGeometryCulling()) + { + const auto& frustum = p_customFrustum ? *p_customFrustum : p_camera.GetFrustum(); + std::tie(opaqueMeshes, transparentMeshes) = FindAndSortFrustumCulledDrawables(p_scene, p_cameraPosition, frustum, p_defaultMaterial); + } + else + { + std::tie(opaqueMeshes, transparentMeshes) = FindAndSortDrawables(p_scene, p_cameraPosition, p_defaultMaterial); + } - for (const auto&[distance, drawable] : opaqueMeshes) + for (const auto& [distance, drawable] : opaqueMeshes) DrawDrawable(drawable); for (const auto& [distance, drawable] : transparentMeshes) DrawDrawable(drawable); } -void OvCore::ECS::Renderer::FindAndSortDrawables(OpaqueDrawables& p_opaques, TransparentDrawables& p_transparents, const OvCore::SceneSystem::Scene& p_scene, const OvMaths::FVector3& p_cameraPosition, Resources::Material* p_defaultMaterial) +void FindAndSortDrawables +( + OvCore::ECS::Renderer::OpaqueDrawables& p_opaques, + OvCore::ECS::Renderer::TransparentDrawables& p_transparents, + const OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + OvCore::Resources::Material* p_defaultMaterial +) { for (OvCore::ECS::Components::CModelRenderer* modelRenderer : p_scene.GetFastAccessComponents().modelRenderers) { @@ -71,6 +131,8 @@ void OvCore::ECS::Renderer::FindAndSortDrawables(OpaqueDrawables& p_opaques, Tra if (auto materialRenderer = modelRenderer->owner.GetComponent()) { + const auto& transform = modelRenderer->owner.transform.GetFTransform(); + const OvCore::ECS::Components::CMaterialRenderer::MaterialList& materials = materialRenderer->GetMaterials(); for (auto mesh : model->GetMeshes()) @@ -86,7 +148,7 @@ void OvCore::ECS::Renderer::FindAndSortDrawables(OpaqueDrawables& p_opaques, Tra if (material) { - Drawable element = { modelRenderer->owner.transform.GetWorldMatrix(), mesh, material, materialRenderer->GetUserMatrix() }; + OvCore::ECS::Renderer::Drawable element = { transform.GetWorldMatrix(), mesh, material, materialRenderer->GetUserMatrix() }; if (material->IsBlendable()) p_transparents.emplace(distanceToActor, element); @@ -100,6 +162,140 @@ void OvCore::ECS::Renderer::FindAndSortDrawables(OpaqueDrawables& p_opaques, Tra } } +std::pair OvCore::ECS::Renderer::FindAndSortFrustumCulledDrawables +( + const OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + const OvRendering::Data::Frustum& p_frustum, + OvCore::Resources::Material* p_defaultMaterial +) +{ + using namespace OvCore::ECS::Components; + + OvCore::ECS::Renderer::OpaqueDrawables opaqueDrawables; + OvCore::ECS::Renderer::TransparentDrawables transparentDrawables; + + for (CModelRenderer* modelRenderer : p_scene.GetFastAccessComponents().modelRenderers) + { + auto& owner = modelRenderer->owner; + + if (owner.IsActive()) + { + if (auto model = modelRenderer->GetModel()) + { + if (auto materialRenderer = modelRenderer->owner.GetComponent()) + { + auto& transform = owner.transform.GetFTransform(); + + OvRendering::Settings::ECullingOptions cullingOptions = OvRendering::Settings::ECullingOptions::NONE; + + if (modelRenderer->GetFrustumBehaviour() != CModelRenderer::EFrustumBehaviour::DISABLED) + { + cullingOptions |= OvRendering::Settings::ECullingOptions::FRUSTUM_PER_MODEL; + } + + if (modelRenderer->GetFrustumBehaviour() == CModelRenderer::EFrustumBehaviour::CULL_MESHES) + { + cullingOptions |= OvRendering::Settings::ECullingOptions::FRUSTUM_PER_MESH; + } + + const auto& modelBoundingSphere = modelRenderer->GetFrustumBehaviour() == CModelRenderer::EFrustumBehaviour::CULL_CUSTOM ? modelRenderer->GetCustomBoundingSphere() : model->GetBoundingSphere(); + + std::vector> meshes; + + { + PROFILER_SPY("Frustum Culling"); + meshes = GetMeshesInFrustum(*model, modelBoundingSphere, transform, p_frustum, cullingOptions); + } + + if (!meshes.empty()) + { + float distanceToActor = OvMaths::FVector3::Distance(transform.GetWorldPosition(), p_cameraPosition); + const OvCore::ECS::Components::CMaterialRenderer::MaterialList& materials = materialRenderer->GetMaterials(); + + for (const auto& mesh : meshes) + { + OvCore::Resources::Material* material = nullptr; + + if (mesh.get().GetMaterialIndex() < MAX_MATERIAL_COUNT) + { + material = materials.at(mesh.get().GetMaterialIndex()); + if (!material || !material->GetShader()) + material = p_defaultMaterial; + } + + if (material) + { + OvCore::ECS::Renderer::Drawable element = { transform.GetWorldMatrix(), &mesh.get(), material, materialRenderer->GetUserMatrix() }; + + if (material->IsBlendable()) + transparentDrawables.emplace(distanceToActor, element); + else + opaqueDrawables.emplace(distanceToActor, element); + } + } + } + } + } + } + } + + return { opaqueDrawables, transparentDrawables }; +} + +std::pair OvCore::ECS::Renderer::FindAndSortDrawables +( + const OvCore::SceneSystem::Scene& p_scene, + const OvMaths::FVector3& p_cameraPosition, + OvCore::Resources::Material* p_defaultMaterial +) +{ + OvCore::ECS::Renderer::OpaqueDrawables opaqueDrawables; + OvCore::ECS::Renderer::TransparentDrawables transparentDrawables; + + for (OvCore::ECS::Components::CModelRenderer* modelRenderer : p_scene.GetFastAccessComponents().modelRenderers) + { + if (modelRenderer->owner.IsActive()) + { + if (auto model = modelRenderer->GetModel()) + { + float distanceToActor = OvMaths::FVector3::Distance(modelRenderer->owner.transform.GetWorldPosition(), p_cameraPosition); + + if (auto materialRenderer = modelRenderer->owner.GetComponent()) + { + const auto& transform = modelRenderer->owner.transform.GetFTransform(); + + const OvCore::ECS::Components::CMaterialRenderer::MaterialList& materials = materialRenderer->GetMaterials(); + + for (auto mesh : model->GetMeshes()) + { + OvCore::Resources::Material* material = nullptr; + + if (mesh->GetMaterialIndex() < MAX_MATERIAL_COUNT) + { + material = materials.at(mesh->GetMaterialIndex()); + if (!material || !material->GetShader()) + material = p_defaultMaterial; + } + + if (material) + { + OvCore::ECS::Renderer::Drawable element = { transform.GetWorldMatrix(), mesh, material, materialRenderer->GetUserMatrix() }; + + if (material->IsBlendable()) + transparentDrawables.emplace(distanceToActor, element); + else + opaqueDrawables.emplace(distanceToActor, element); + } + } + } + } + } + } + + return { opaqueDrawables, transparentDrawables }; +} + void OvCore::ECS::Renderer::DrawDrawable(const Drawable& p_toDraw) { m_userMatrixSender(std::get<3>(p_toDraw)); diff --git a/Sources/Overload/OvDebug/OvDebug.vcxproj b/Sources/Overload/OvDebug/OvDebug.vcxproj index 7cc086e3..67bd14e6 100644 --- a/Sources/Overload/OvDebug/OvDebug.vcxproj +++ b/Sources/Overload/OvDebug/OvDebug.vcxproj @@ -14,19 +14,19 @@ 15.0 {03A0CA9A-2BB6-4284-8D8E-F6F0F9E6DD32} OvDebug - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvEditor/OvEditor.vcxproj b/Sources/Overload/OvEditor/OvEditor.vcxproj index 8b7b7481..ce444ef2 100644 --- a/Sources/Overload/OvEditor/OvEditor.vcxproj +++ b/Sources/Overload/OvEditor/OvEditor.vcxproj @@ -14,19 +14,19 @@ 15.0 {7EBB8C83-AB76-40FA-9F8F-8C2398AA3F7F} OvEditor - 10.0.17763.0 + 10.0 Application true - v141 + v142 MultiByte Application false - v141 + v142 true MultiByte @@ -48,6 +48,7 @@ $(ProjectDir)include\;$(SolutionDir)..\..\Build\OvCore\include\;$(IncludePath) $(SolutionDir)..\..\Build\OvCore\lib\$(Configuration)\;$(LibraryPath) $(ProjectDir)src\;$(SourcePath) + Overload $(SolutionDir)..\..\Bin\$(ProjectName)\$(Configuration)\ @@ -55,6 +56,7 @@ $(ProjectDir)include\;$(SolutionDir)..\..\Build\OvCore\include\;$(IncludePath) $(SolutionDir)..\..\Build\OvCore\lib\$(Configuration)\;$(LibraryPath) $(ProjectDir)src\;$(SourcePath) + Overload @@ -140,6 +142,7 @@ xcopy "$(ProjectDir)Layout.ini" "$(SolutionDir)..\..\Build\$(ProjectName)\$(Conf + @@ -160,6 +163,7 @@ xcopy "$(ProjectDir)Layout.ini" "$(SolutionDir)..\..\Build\$(ProjectName)\$(Conf + @@ -169,6 +173,7 @@ xcopy "$(ProjectDir)Layout.ini" "$(SolutionDir)..\..\Build\$(ProjectName)\$(Conf + @@ -190,6 +195,7 @@ xcopy "$(ProjectDir)Layout.ini" "$(SolutionDir)..\..\Build\$(ProjectName)\$(Conf + diff --git a/Sources/Overload/OvEditor/OvEditor.vcxproj.filters b/Sources/Overload/OvEditor/OvEditor.vcxproj.filters index d05fa459..4f82f6ff 100644 --- a/Sources/Overload/OvEditor/OvEditor.vcxproj.filters +++ b/Sources/Overload/OvEditor/OvEditor.vcxproj.filters @@ -96,6 +96,12 @@ Source Files + + Source Files + + + Source Files + @@ -182,6 +188,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/CameraController.h b/Sources/Overload/OvEditor/include/OvEditor/Core/CameraController.h index 97e480e8..90232798 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Core/CameraController.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/CameraController.h @@ -78,6 +78,11 @@ namespace OvEditor::Core */ OvMaths::FVector3 GetPosition() const; + /** + * Returns true if the right mouse click is being pressed + */ + bool IsRightMousePressed() const; + private: void HandleCameraXYMovement(float p_deltaTime); void HandleCameraZMovement(float p_deltaTime); diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorRenderer.h b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorRenderer.h index aa351412..525282f7 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorRenderer.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorRenderer.h @@ -17,6 +17,7 @@ #include "OvEditor/Core/Context.h" +namespace OvEditor::Core { enum class EGizmoOperation; } namespace OvEditor::Panels { class AView; } namespace OvEditor::Core @@ -38,11 +39,29 @@ namespace OvEditor::Core */ void InitMaterials(); + /** + * Prepare the picking material by send it the color corresponding to the given actor + * @param p_actor + */ + void PreparePickingMaterial(OvCore::ECS::Actor& p_actor); + + /** + * Calculate the model matrix for a camera attached to the given actor + * @param p_actor + */ + OvMaths::FMatrix4 CalculateCameraModelMatrix(OvCore::ECS::Actor& p_actor); + /** * Render the scene * @param p_cameraPosition + * @param p_camera + */ + void RenderScene(const OvMaths::FVector3& p_cameraPosition, const OvRendering::LowRenderer::Camera& p_camera, const OvRendering::Data::Frustum* p_customFrustum = nullptr); + + /** + * Render the scene for actor picking (Unlit version of the scene with colors indicating actor IDs) */ - void RenderScene(const OvMaths::FVector3& p_cameraPosition); + void RenderSceneForActorPicking(); /** * Render the User Interface @@ -55,11 +74,13 @@ namespace OvEditor::Core void RenderCameras(); /** - * Render a guizmo at position + * Render a gizmo at position * @param p_position * @param p_rotation + * @param p_operation + * @param p_pickable (Determine the shader to use to render the gizmo) */ - void RenderGuizmo(const OvMaths::FVector3& p_position, const OvMaths::FQuaternion& p_rotation); + void RenderGizmo(const OvMaths::FVector3& p_position, const OvMaths::FQuaternion& p_rotation, OvEditor::Core::EGizmoOperation p_operation, bool p_pickable = false); /** * Render a model to the stencil buffer @@ -78,11 +99,21 @@ namespace OvEditor::Core */ void RenderActorAsSelected(OvCore::ECS::Actor& p_actor, bool p_toStencil); + /** + * Render the camera frustum + */ + void RenderCameraFrustum(OvCore::ECS::Components::CCamera& p_camera); + /** * Render an actor collider */ void RenderActorCollider(OvCore::ECS::Actor& p_actor); + /** + * Render light bounds + */ + void RenderLightBounds(OvCore::ECS::Components::CLight& p_light); + /** * Render ambient box volume */ @@ -93,6 +124,11 @@ namespace OvEditor::Core */ void RenderAmbientSphereVolume(OvCore::ECS::Components::CAmbientSphereLight& p_ambientSphereLight); + /** + * Render the the bounding spheres of the given model renderer + */ + void RenderBoundingSpheres(OvCore::ECS::Components::CModelRenderer& p_modelRenderer); + /** * Render model */ @@ -119,6 +155,12 @@ namespace OvEditor::Core */ void UpdateLights(OvCore::SceneSystem::Scene& p_scene); + /** + * Update the light SSBO with the current scene (Lights outside of the given frustum are culled) + * @param p_scene + */ + void UpdateLightsInFrustum(OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum); + private: Context& m_context; @@ -129,7 +171,9 @@ namespace OvEditor::Core OvCore::Resources::Material m_emptyMaterial; OvCore::Resources::Material m_defaultMaterial; OvCore::Resources::Material m_cameraMaterial; - OvCore::Resources::Material m_guizmoArrowMaterial; - OvCore::Resources::Material m_guizmoBallMaterial; + OvCore::Resources::Material m_gizmoArrowMaterial; + OvCore::Resources::Material m_gizmoBallMaterial; + OvCore::Resources::Material m_gizmoPickingMaterial; + OvCore::Resources::Material m_actorPickingMaterial; }; } \ No newline at end of file diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/GizmoBehaviour.h b/Sources/Overload/OvEditor/include/OvEditor/Core/GizmoBehaviour.h new file mode 100644 index 00000000..2f38613c --- /dev/null +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/GizmoBehaviour.h @@ -0,0 +1,119 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +namespace OvEditor::Core +{ + enum class EGizmoOperation + { + TRANSLATE, + ROTATE, + SCALE + }; + + /* Handle gizmo behaviours */ + class GizmoBehaviour + { + public: + enum class EDirection + { + X, + Y, + Z + }; + + /** + * Starts the gizmo picking behaviour for the given target in the given direction + * @param p_actor + * @param p_cameraPosition + * @param p_operation + * @param p_direction + */ + void StartPicking(OvCore::ECS::Actor& p_target, const OvMaths::FVector3& p_cameraPosition, EGizmoOperation p_operation, EDirection p_direction); + + /** + * Stops the gizmo picking behaviour + */ + void StopPicking(); + + /** + * Handle the current behaviour + * @param p_viewMatrix + * @param p_projectionMatrix + * @param p_viewSize + */ + void ApplyOperation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize); + + /** + * Set the given mouse position as the current mouse position and update the previous mouse position + * @param p_mousePosition + */ + void SetCurrentMouse(const OvMaths::FVector2& p_mousePosition); + + /** + * Returns true if the gizmo is currently picked + */ + bool IsPicking() const; + + private: + /** + * Returns the global direction matching with the current m_direction + */ + OvMaths::FVector3 GetFakeDirection() const; + + /** + * Returns the actual direction of the target matching with the current m_direction + * @param p_relative (If true, the direction depends on hierarchy) + */ + OvMaths::FVector3 GetRealDirection(bool p_relative = false) const; + + /** + * Returns the 3D vector of the arrow projected to the screen + * @param p_viewMatrix + * @param p_projectionMatrix + * @param p_viewSize + */ + OvMaths::FVector2 GetScreenDirection(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const; + + /** + * Handle the translation behaviour + * @param p_viewMatrix + * @param p_projectionMatrix + * @param p_viewSize + */ + void ApplyTranslation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const; + + /** + * Handle the rotation behaviour + * @param p_viewMatrix + * @param p_projectionMatrix + * @param p_viewSize + */ + void ApplyRotation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const; + + /** + * Handle the scale behaviour + * @param p_viewMatrix + * @param p_projectionMatrix + * @param p_viewSize + */ + void ApplyScale(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const; + + private: + bool m_firstMouse = true; + float m_distanceToActor = 0.0f; + OvCore::ECS::Actor* m_target; + EGizmoOperation m_currentOperation; + EDirection m_direction; + OvMaths::FTransform m_originalTransform; + OvMaths::FVector2 m_originMouse; + OvMaths::FVector2 m_currentMouse; + OvMaths::FVector2 m_screenDirection; + }; +} diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/ProjectHub.h b/Sources/Overload/OvEditor/include/OvEditor/Core/ProjectHub.h index f68d534d..741f73dd 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Core/ProjectHub.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/ProjectHub.h @@ -36,6 +36,12 @@ namespace OvEditor::Core */ void SetupContext(); + /** + * Register the project (identified from the given path) into the project hub + * @param p_path + */ + void RegisterProject(const std::string& p_path); + private: std::unique_ptr m_device; std::unique_ptr m_window; diff --git a/Sources/Overload/OvEditor/include/OvEditor/Panels/AView.h b/Sources/Overload/OvEditor/include/OvEditor/Panels/AView.h index 7afcc44e..6f13782b 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Panels/AView.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Panels/AView.h @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace OvEditor::Core { class EditorRenderer; } @@ -35,27 +36,12 @@ namespace OvEditor::Panels const OvUI::Settings::PanelWindowSettings& p_windowSettings ); - /** - * Destructor - */ - ~AView(); - /** * Update the view * @param p_deltaTime */ virtual void Update(float p_deltaTime); - /** - * Bind the FBO attached to the view - */ - void Bind(); - - /** - * Unbind the FBO attached to the view - */ - void Unbind(); - /** * Custom implementation of the draw method */ @@ -108,6 +94,12 @@ namespace OvEditor::Panels */ void FillEngineUBO(); + protected: + /** + * Update camera matrices + */ + void PrepareCamera(); + protected: OvEditor::Core::EditorRenderer& m_editorRenderer; OvRendering::LowRenderer::Camera m_camera; @@ -116,9 +108,6 @@ namespace OvEditor::Panels OvMaths::FVector3 m_gridColor = OvMaths::FVector3::One; - private: - uint32_t m_fbo; - uint32_t m_renderTexture; - uint32_t m_depthStencilBuffer; + OvRendering::Buffers::Framebuffer m_fbo; }; } \ No newline at end of file diff --git a/Sources/Overload/OvEditor/include/OvEditor/Panels/AViewControllable.h b/Sources/Overload/OvEditor/include/OvEditor/Panels/AViewControllable.h index 343685ec..effc4cfa 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Panels/AViewControllable.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Panels/AViewControllable.h @@ -40,7 +40,7 @@ namespace OvEditor::Panels */ OvEditor::Core::CameraController& GetCameraController(); - private: + protected: OvEditor::Core::CameraController m_cameraController; }; } \ No newline at end of file diff --git a/Sources/Overload/OvEditor/include/OvEditor/Panels/GameView.h b/Sources/Overload/OvEditor/include/OvEditor/Panels/GameView.h index c7fa56e8..bc79b8b2 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Panels/GameView.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Panels/GameView.h @@ -41,6 +41,16 @@ namespace OvEditor::Panels */ virtual void _Render_Impl() override; + /** + * Returns true if the game view has a camera + */ + bool HasCamera() const; + + /** + * Returns the game view camera frustum or nothing if the game isn't playing + */ + std::optional GetActiveFrustum() const; + private: OvCore::SceneSystem::SceneManager& m_sceneManager; bool m_hasCamera = false; diff --git a/Sources/Overload/OvEditor/include/OvEditor/Panels/SceneView.h b/Sources/Overload/OvEditor/include/OvEditor/Panels/SceneView.h index 0378f33b..0e1248ad 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Panels/SceneView.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Panels/SceneView.h @@ -7,6 +7,7 @@ #pragma once #include "OvEditor/Panels/AViewControllable.h" +#include "OvEditor/Core/GizmoBehaviour.h" namespace OvEditor::Panels { @@ -27,8 +28,7 @@ namespace OvEditor::Panels ); /** - * Update the scene view (Inputs logic) - * @param p_deltaTime + * Update the scene view */ virtual void Update(float p_deltaTime) override; @@ -37,7 +37,26 @@ namespace OvEditor::Panels */ virtual void _Render_Impl() override; + /** + * Render the actual scene + * @param p_defaultRenderState + */ + void RenderScene(uint8_t p_defaultRenderState); + + /** + * Render the scene for actor picking (Using unlit colors) + */ + void RenderSceneForActorPicking(); + + /** + * Render the scene for actor picking and handle the logic behind it + */ + void HandleActorPicking(); + private: OvCore::SceneSystem::SceneManager& m_sceneManager; + OvRendering::Buffers::Framebuffer m_actorPickingFramebuffer; + OvEditor::Core::GizmoBehaviour m_gizmoOperations; + OvEditor::Core::EGizmoOperation m_currentOperation = OvEditor::Core::EGizmoOperation::TRANSLATE; }; } \ No newline at end of file diff --git a/Sources/Overload/OvEditor/include/OvEditor/Resources/RawShaders.h b/Sources/Overload/OvEditor/include/OvEditor/Resources/RawShaders.h index 6c544899..fbc3ffcd 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Resources/RawShaders.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Resources/RawShaders.h @@ -22,8 +22,8 @@ namespace OvEditor::Resources static std::pair GetGrid(); /** - * Retursn the guizmo shader + * Returns the gizmo shader */ - static std::pair GetGuizmo(); + static std::pair GetGizmo(); }; } \ No newline at end of file diff --git a/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h new file mode 100644 index 00000000..53768e02 --- /dev/null +++ b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h @@ -0,0 +1,81 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +namespace OvEditor::Settings +{ + /** + * Accessible from anywhere editor settings + */ + class EditorSettings + { + public: + template + class Property + { + public: + /** + * Creates the property with a default value + * @param p_value + */ + Property(T p_value) : m_value(p_value) {} + + /** + * Event called when the property value changes + */ + OvTools::Eventing::Event OnValueChanged; + + /** + * Assign a new value to the property + * @param p_value + */ + inline T& operator=(T p_value) + { + Set(p_value); + return m_value; + } + + /** + * Assign a new valeu to the property + * @param p_value + */ + inline void Set(T p_value) + { + m_value = p_value; + OnValueChanged.Invoke(m_value); + } + + inline operator T() + { + return m_value; + } + + /** + * Returns the value of the property + */ + inline T Get() const + { + return m_value; + } + + private: + T m_value; + }; + + /** + * No construction possible + */ + EditorSettings() = delete; + + inline static Property ShowGeometryBounds = { false }; + inline static Property ShowLightBounds = { false }; + inline static Property ShowGeometryFrustumCullingInSceneView = { false }; + inline static Property ShowLightFrustumCullingInSceneView = { false }; + }; +} diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/CameraController.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/CameraController.cpp index 843f2c7e..460ef643 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/CameraController.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/CameraController.cpp @@ -232,6 +232,11 @@ OvMaths::FVector3 OvEditor::Core::CameraController::GetPosition() const return m_cameraPosition; } +bool OvEditor::Core::CameraController::IsRightMousePressed() const +{ + return m_rightMousePressed; +} + void OvEditor::Core::CameraController::HandleCameraXYMovement(float p_deltaTime) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp index 97a5321d..8986ffac 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp @@ -40,7 +40,7 @@ OvEditor::Core::Context::Context(const std::string& p_projectPath, const std::st /* Settings */ OvWindowing::Settings::DeviceSettings deviceSettings; deviceSettings.contextMajorVersion = 4; - deviceSettings.contextMajorVersion = 3; + deviceSettings.contextMinorVersion = 3; windowSettings.title = "Overload Editor"; windowSettings.width = 1280; windowSettings.height = 720; diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp index 58a74a20..950ffce7 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp @@ -82,8 +82,8 @@ void OvEditor::Core::Editor::PreUpdate() void OvEditor::Core::Editor::Update(float p_deltaTime) { UpdateCurrentEditorMode(p_deltaTime); - UpdateEditorPanels(p_deltaTime); PrepareRendering(p_deltaTime); + UpdateEditorPanels(p_deltaTime); RenderViews(p_deltaTime); RenderEditorUI(p_deltaTime); m_editorActions.ExecuteDelayedActions(); @@ -176,15 +176,8 @@ void OvEditor::Core::Editor::UpdateEditorPanels(float p_deltaTime) void OvEditor::Core::Editor::PrepareRendering(float p_deltaTime) { - { - PROFILER_SPY("Light SSBO Update"); - m_editorRenderer.UpdateLights(*m_context.sceneManager.GetCurrentScene()); - } - - { - PROFILER_SPY("Engine UBO Update"); - m_context.engineUBO->SetSubData(m_context.device->GetElapsedTime(), 3 * sizeof(OvMaths::FMatrix4) + sizeof(OvMaths::FVector3)); - } + PROFILER_SPY("Engine UBO Update"); + m_context.engineUBO->SetSubData(m_context.device->GetElapsedTime(), 3 * sizeof(OvMaths::FMatrix4) + sizeof(OvMaths::FVector3)); } void OvEditor::Core::Editor::RenderViews(float p_deltaTime) @@ -197,8 +190,8 @@ void OvEditor::Core::Editor::RenderViews(float p_deltaTime) PROFILER_SPY("Editor Views Update"); assetView.Update(p_deltaTime); - sceneView.Update(p_deltaTime); gameView.Update(p_deltaTime); + sceneView.Update(p_deltaTime); } if (assetView.IsOpened()) @@ -212,13 +205,6 @@ void OvEditor::Core::Editor::RenderViews(float p_deltaTime) m_context.lightSSBO->Bind(0); - if (sceneView.IsOpened()) - { - PROFILER_SPY("Scene View Rendering"); - - sceneView.Render(); - } - if (gameView.IsOpened()) { PROFILER_SPY("Game View Rendering"); @@ -226,6 +212,13 @@ void OvEditor::Core::Editor::RenderViews(float p_deltaTime) gameView.Render(); } + if (sceneView.IsOpened()) + { + PROFILER_SPY("Scene View Rendering"); + + sceneView.Render(); + } + m_context.lightSSBO->Unbind(); } diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorRenderer.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorRenderer.cpp index ec3332b6..6ed8c242 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorRenderer.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorRenderer.cpp @@ -13,9 +13,16 @@ #include #include +#include + +#include + #include "OvEditor/Core/EditorRenderer.h" #include "OvEditor/Core/EditorResources.h" #include "OvEditor/Panels/AView.h" +#include "OvEditor/Panels/GameView.h" +#include "OvEditor/Core/GizmoBehaviour.h" +#include "OvEditor/Settings/EditorSettings.h" #include "OvEditor/Core/EditorActions.h" @@ -23,6 +30,11 @@ using namespace OvMaths; using namespace OvRendering::Resources; using namespace OvCore::Resources; +const OvMaths::FVector3 DEBUG_BOUNDS_COLOR = { 1.0f, 0.0f, 0.0f }; +const OvMaths::FVector3 LIGHT_VOLUME_COLOR = { 1.0f, 1.0f, 0.0f }; +const OvMaths::FVector3 COLLIDER_COLOR = { 0.0f, 1.0f, 0.0f }; +const OvMaths::FVector3 FRUSTUM_COLOR = { 1.0f, 1.0f, 1.0f }; + OvEditor::Core::EditorRenderer::EditorRenderer(Context& p_context) : m_context(p_context) { m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::STENCIL_TEST, true); @@ -96,25 +108,121 @@ void OvEditor::Core::EditorRenderer::InitMaterials() m_outlineMaterial.Set("u_DiffuseMap", nullptr); m_outlineMaterial.SetDepthTest(false); - /* Guizmo Arrow Material */ - m_guizmoArrowMaterial.SetShader(m_context.editorResources->GetShader("Guizmo")); - m_guizmoArrowMaterial.SetGPUInstances(3); - m_guizmoArrowMaterial.Set("u_IsBall", false); + /* Gizmo Arrow Material */ + m_gizmoArrowMaterial.SetShader(m_context.editorResources->GetShader("Gizmo")); + m_gizmoArrowMaterial.SetGPUInstances(3); + m_gizmoArrowMaterial.Set("u_IsBall", false); + m_gizmoArrowMaterial.Set("u_IsPickable", false); + + /* Gizmo Ball Material */ + m_gizmoBallMaterial.SetShader(m_context.editorResources->GetShader("Gizmo")); + m_gizmoBallMaterial.Set("u_IsBall", true); + m_gizmoBallMaterial.Set("u_IsPickable", false); + + /* Gizmo Pickable Material */ + m_gizmoPickingMaterial.SetShader(m_context.editorResources->GetShader("Gizmo")); + m_gizmoPickingMaterial.SetGPUInstances(3); + m_gizmoPickingMaterial.Set("u_IsBall", false); + m_gizmoPickingMaterial.Set("u_IsPickable", true); + + /* Picking Material */ + m_actorPickingMaterial.SetShader(m_context.shaderManager[":Shaders\\Unlit.glsl"]); + m_actorPickingMaterial.Set("u_Diffuse", FVector4(1.f, 1.f, 1.f, 1.0f)); + m_actorPickingMaterial.Set("u_DiffuseMap", nullptr); + m_actorPickingMaterial.SetFrontfaceCulling(false); + m_actorPickingMaterial.SetBackfaceCulling(false); +} + +void OvEditor::Core::EditorRenderer::PreparePickingMaterial(OvCore::ECS::Actor& p_actor) +{ + uint32_t actorID = static_cast(p_actor.GetID()); + + auto bytes = reinterpret_cast(&actorID); + auto color = FVector4{ bytes[0] / 255.0f, bytes[1] / 255.0f, bytes[2] / 255.0f, 1.0f }; - /* Guizmo Ball Material */ - m_guizmoBallMaterial.SetShader(m_context.editorResources->GetShader("Guizmo")); - m_guizmoBallMaterial.SetShader(m_context.editorResources->GetShader("Guizmo")); - m_guizmoBallMaterial.Set("u_IsBall", true); + m_actorPickingMaterial.Set("u_Diffuse", color); } -void OvEditor::Core::EditorRenderer::RenderScene(const OvMaths::FVector3& p_cameraPosition) +OvMaths::FMatrix4 OvEditor::Core::EditorRenderer::CalculateCameraModelMatrix(OvCore::ECS::Actor& p_actor) +{ + auto translation = FMatrix4::Translation(p_actor.transform.GetWorldPosition()); + auto rotation = FQuaternion::ToMatrix4(p_actor.transform.GetWorldRotation()); + auto scale = FMatrix4::Scaling({ 0.4f, 0.4f, 0.4f }); + + return translation * rotation * scale; +} + +void OvEditor::Core::EditorRenderer::RenderScene(const OvMaths::FVector3& p_cameraPosition, const OvRendering::LowRenderer::Camera& p_camera, const OvRendering::Data::Frustum* p_customFrustum) { /* Render the actors */ m_context.lightSSBO->Bind(0); - m_context.renderer->RenderScene(*m_context.sceneManager.GetCurrentScene(), p_cameraPosition, &m_emptyMaterial); + m_context.renderer->RenderScene(*m_context.sceneManager.GetCurrentScene(), p_cameraPosition, p_camera, p_customFrustum, &m_emptyMaterial); m_context.lightSSBO->Unbind(); } +void OvEditor::Core::EditorRenderer::RenderSceneForActorPicking() +{ + auto& scene = *m_context.sceneManager.GetCurrentScene(); + + /* Render models */ + for (auto modelRenderer : scene.GetFastAccessComponents().modelRenderers) + { + auto& actor = modelRenderer->owner; + + if (actor.IsActive()) + { + if (auto model = modelRenderer->GetModel()) + { + if (auto materialRenderer = modelRenderer->owner.GetComponent()) + { + const OvCore::ECS::Components::CMaterialRenderer::MaterialList& materials = materialRenderer->GetMaterials(); + const auto& modelMatrix = actor.transform.GetWorldMatrix(); + + PreparePickingMaterial(actor); + + for (auto mesh : model->GetMeshes()) + { + OvCore::Resources::Material* material = nullptr; + + if (mesh->GetMaterialIndex() < MAX_MATERIAL_COUNT) + { + material = materials.at(mesh->GetMaterialIndex()); + if (!material || !material->GetShader()) + material = &m_emptyMaterial; + } + + if (material) + { + m_actorPickingMaterial.SetBackfaceCulling(material->HasBackfaceCulling()); + m_actorPickingMaterial.SetFrontfaceCulling(material->HasFrontfaceCulling()); + m_actorPickingMaterial.SetColorWriting(material->HasColorWriting()); + m_actorPickingMaterial.SetDepthTest(material->HasDepthTest()); + m_actorPickingMaterial.SetDepthWriting(material->HasDepthWriting()); + + m_context.renderer->DrawMesh(*mesh, m_actorPickingMaterial, &modelMatrix); + } + } + } + } + } + } + + /* Render cameras */ + for (auto camera : m_context.sceneManager.GetCurrentScene()->GetFastAccessComponents().cameras) + { + auto& actor = camera->owner; + + if (actor.IsActive()) + { + PreparePickingMaterial(actor); + auto& model = *m_context.editorResources->GetModel("Camera"); + auto modelMatrix = CalculateCameraModelMatrix(actor); + + m_context.renderer->DrawModelWithSingleMaterial(model, m_actorPickingMaterial, &modelMatrix); + } + } +} + void OvEditor::Core::EditorRenderer::RenderUI() { m_context.uiManager->Render(); @@ -122,24 +230,53 @@ void OvEditor::Core::EditorRenderer::RenderUI() void OvEditor::Core::EditorRenderer::RenderCameras() { + using namespace OvMaths; + for (auto camera : m_context.sceneManager.GetCurrentScene()->GetFastAccessComponents().cameras) { - if (camera->owner.IsActive()) + auto& actor = camera->owner; + + if (actor.IsActive()) { - auto modelMatrix = FMatrix4::Translation(camera->owner.transform.GetWorldPosition()) * FQuaternion::ToMatrix4(camera->owner.transform.GetWorldRotation()) * FMatrix4::Scaling({ 0.4f, 0.4f, 0.4f }); - m_context.renderer->DrawModelWithSingleMaterial(*m_context.editorResources->GetModel("Camera"), m_cameraMaterial, &modelMatrix); + auto& model = *m_context.editorResources->GetModel("Camera"); + auto modelMatrix = CalculateCameraModelMatrix(actor); + + m_context.renderer->DrawModelWithSingleMaterial(model, m_cameraMaterial, &modelMatrix); } } } -void OvEditor::Core::EditorRenderer::RenderGuizmo(const OvMaths::FVector3& p_position, const OvMaths::FQuaternion& p_rotation) +void OvEditor::Core::EditorRenderer::RenderGizmo(const OvMaths::FVector3& p_position, const OvMaths::FQuaternion& p_rotation, OvEditor::Core::EGizmoOperation p_operation, bool p_pickable) { using namespace OvMaths; FMatrix4 model = FMatrix4::Translation(p_position) * FQuaternion::ToMatrix4(FQuaternion::Normalize(p_rotation)); - FMatrix4 sphereModel = model * OvMaths::FMatrix4::Scaling({ 0.1f, 0.1f, 0.1f }); - m_context.renderer->DrawModelWithSingleMaterial(*m_context.editorResources->GetModel("Sphere"), m_guizmoBallMaterial, &sphereModel); - m_context.renderer->DrawModelWithSingleMaterial(*m_context.editorResources->GetModel("Arrow"), m_guizmoArrowMaterial, &model); + + if (!p_pickable) + { + FMatrix4 sphereModel = model * OvMaths::FMatrix4::Scaling({ 0.1f, 0.1f, 0.1f }); + m_context.renderer->DrawModelWithSingleMaterial(*m_context.editorResources->GetModel("Sphere"), m_gizmoBallMaterial, &sphereModel); + } + + OvRendering::Resources::Model* arrowModel = nullptr; + + switch (p_operation) + { + case OvEditor::Core::EGizmoOperation::TRANSLATE: + arrowModel = m_context.editorResources->GetModel("Arrow_Translate"); + break; + case OvEditor::Core::EGizmoOperation::ROTATE: + arrowModel = m_context.editorResources->GetModel("Arrow_Rotate"); + break; + case OvEditor::Core::EGizmoOperation::SCALE: + arrowModel = m_context.editorResources->GetModel("Arrow_Scale"); + break; + } + + if (arrowModel) + { + m_context.renderer->DrawModelWithSingleMaterial(*arrowModel, p_pickable ? m_gizmoPickingMaterial : m_gizmoArrowMaterial, &model); + } } void OvEditor::Core::EditorRenderer::RenderModelToStencil(const OvMaths::FMatrix4& p_worldMatrix, OvRendering::Resources::Model& p_model) @@ -164,13 +301,18 @@ void OvEditor::Core::EditorRenderer::RenderActorAsSelected(OvCore::ECS::Actor& p { if (p_actor.IsActive()) { - /* Render static mesh outline */ + /* Render static mesh outline and bounding spheres */ if (auto modelRenderer = p_actor.GetComponent(); modelRenderer && modelRenderer->GetModel()) { if (p_toStencil) RenderModelToStencil(p_actor.transform.GetWorldMatrix(), *modelRenderer->GetModel()); else RenderModelOutline(p_actor.transform.GetWorldMatrix(), *modelRenderer->GetModel()); + + if (Settings::EditorSettings::ShowGeometryBounds) + { + RenderBoundingSpheres(*modelRenderer); + } } /* Render camera component outline */ @@ -182,6 +324,8 @@ void OvEditor::Core::EditorRenderer::RenderActorAsSelected(OvCore::ECS::Actor& p RenderModelToStencil(model, *m_context.editorResources->GetModel("Camera")); else RenderModelOutline(model, *m_context.editorResources->GetModel("Camera")); + + RenderCameraFrustum(*cameraComponent); } /* Render the actor collider */ @@ -201,11 +345,88 @@ void OvEditor::Core::EditorRenderer::RenderActorAsSelected(OvCore::ECS::Actor& p RenderAmbientSphereVolume(*ambientSphereComp); } + if (OvEditor::Settings::EditorSettings::ShowLightBounds) + { + if (auto light = p_actor.GetComponent(); light && !p_toStencil) + { + RenderLightBounds(*light); + } + } + for (auto& child : p_actor.GetChildren()) RenderActorAsSelected(*child, p_toStencil); } } +void OvEditor::Core::EditorRenderer::RenderCameraFrustum(OvCore::ECS::Components::CCamera& p_camera) +{ + auto& gameView = EDITOR_PANEL(Panels::GameView, "Game View"); + auto gameViewSize = gameView.GetSafeSize(); + + if (gameViewSize.first == 0 || gameViewSize.second == 0) + { + gameViewSize = { 16, 9 }; + } + + auto& owner = p_camera.owner; + auto& camera = p_camera.GetCamera(); + + const auto& cameraPos = owner.transform.GetWorldPosition(); + const auto& cameraForward = owner.transform.GetWorldForward(); + const auto& cameraRotation = owner.transform.GetWorldRotation(); + + auto drawFrustumLine = [&](const FVector3& p_start, const FVector3& p_end, float planeDistance) + { + auto offset = cameraPos + cameraForward * planeDistance; + auto start = offset + p_start; + auto end = offset + p_end; + m_context.shapeDrawer->DrawLine(start, end, FRUSTUM_COLOR); + }; + + camera.CacheMatrices(gameViewSize.first, gameViewSize.second, cameraPos); + const auto proj = FMatrix4::Transpose(camera.GetProjectionMatrix()); + const auto near = camera.GetNear(); + const auto far = camera.GetFar(); + + const auto nLeft = near * (proj.data[2] - 1.0f) / proj.data[0]; + const auto nRight = near * (1.0f + proj.data[2]) / proj.data[0]; + const auto nTop = near * (1.0f + proj.data[6]) / proj.data[5]; + const auto nBottom = near * (proj.data[6] - 1.0f) / proj.data[5]; + + // Get the sides of the far plane. + const auto fLeft = far * (proj.data[2] - 1.0f) / proj.data[0]; + const auto fRight = far * (1.0f + proj.data[2]) / proj.data[0]; + const auto fTop = far * (1.0f + proj.data[6]) / proj.data[5]; + const auto fBottom = far * (proj.data[6] - 1.0f) / proj.data[5]; + + auto a = cameraRotation * FVector3{ nLeft, nTop, 0 }; + auto b = cameraRotation * FVector3{ nRight, nTop, 0 }; + auto c = cameraRotation * FVector3{ nLeft, nBottom, 0 }; + auto d = cameraRotation * FVector3{ nRight, nBottom, 0 }; + auto e = cameraRotation * FVector3{ fLeft, fTop, 0 }; + auto f = cameraRotation * FVector3{ fRight, fTop, 0 }; + auto g = cameraRotation * FVector3{ fLeft, fBottom, 0 }; + auto h = cameraRotation * FVector3{ fRight, fBottom, 0 }; + + // Draw near plane + drawFrustumLine(a, b, near); + drawFrustumLine(b, d, near); + drawFrustumLine(d, c, near); + drawFrustumLine(c, a, near); + + // Draw far plane + drawFrustumLine(e, f, far); + drawFrustumLine(f, h, far); + drawFrustumLine(h, g, far); + drawFrustumLine(g, e, far); + + // Draw lines between near and far planes + drawFrustumLine(a + cameraForward * near, e + cameraForward * far, 0); + drawFrustumLine(b + cameraForward * near, f + cameraForward * far, 0); + drawFrustumLine(c + cameraForward * near, g + cameraForward * far, 0); + drawFrustumLine(d + cameraForward * near, h + cameraForward * far, 0); +} + void OvEditor::Core::EditorRenderer::RenderActorCollider(OvCore::ECS::Actor & p_actor) { using namespace OvCore::ECS::Components; @@ -293,6 +514,30 @@ void OvEditor::Core::EditorRenderer::RenderActorCollider(OvCore::ECS::Actor & p_ m_context.renderer->SetRasterizationLinesWidth(1.0f); } +void OvEditor::Core::EditorRenderer::RenderLightBounds(OvCore::ECS::Components::CLight& p_light) +{ + bool depthTestBackup = m_context.renderer->GetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST); + m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, false); + + auto& data = p_light.GetData(); + + OvMaths::FQuaternion rotation = data.GetTransform().GetWorldRotation(); + OvMaths::FVector3 center = data.GetTransform().GetWorldPosition(); + float radius = data.GetEffectRange(); + + if (!std::isinf(radius)) + { + for (float i = 0; i <= 360.0f; i += 10.0f) + { + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), sin(i * (3.14f / 180.0f)), 0.f } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), sin((i + 10.0f) * (3.14f / 180.0f)), 0.f } *radius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ 0.f, sin(i * (3.14f / 180.0f)), cos(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ 0.f, sin((i + 10.0f) * (3.14f / 180.0f)), cos((i + 10.0f) * (3.14f / 180.0f)) } *radius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), 0.f, sin(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), 0.f, sin((i + 10.0f) * (3.14f / 180.0f)) } *radius), DEBUG_BOUNDS_COLOR, 1.f); + } + } + + m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, depthTestBackup); +} + void OvEditor::Core::EditorRenderer::RenderAmbientBoxVolume(OvCore::ECS::Components::CAmbientBoxLight & p_ambientBoxLight) { bool depthTestBackup = m_context.renderer->GetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST); @@ -309,18 +554,18 @@ void OvEditor::Core::EditorRenderer::RenderAmbientBoxVolume(OvCore::ECS::Compone OvMaths::FVector3 actorScale = p_ambientBoxLight.owner.transform.GetWorldScale(); OvMaths::FVector3 halfSize = size / 2.f; - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, -halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, -halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, -halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, -halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, -halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, -halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, -halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, -halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, -halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, -halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + OvMaths::FVector3{ -halfSize.x, +halfSize.y, +halfSize.z }, center + OvMaths::FVector3{ +halfSize.x, +halfSize.y, +halfSize.z }, LIGHT_VOLUME_COLOR, 1.f); m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, depthTestBackup); } @@ -338,12 +583,76 @@ void OvEditor::Core::EditorRenderer::RenderAmbientSphereVolume(OvCore::ECS::Comp for (float i = 0; i <= 360.0f; i += 10.0f) { - m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), sin(i * (3.14f / 180.0f)), 0.f } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), sin((i + 10.0f) * (3.14f / 180.0f)), 0.f } *radius), OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ 0.f, sin(i * (3.14f / 180.0f)), cos(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ 0.f, sin((i + 10.0f) * (3.14f / 180.0f)), cos((i + 10.0f) * (3.14f / 180.0f)) } *radius), OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); - m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), 0.f, sin(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), 0.f, sin((i + 10.0f) * (3.14f / 180.0f)) } *radius), OvMaths::FVector3{ 1.f, 1.f, 0.f }, 1.f); + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), sin(i * (3.14f / 180.0f)), 0.f } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), sin((i + 10.0f) * (3.14f / 180.0f)), 0.f } *radius), LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ 0.f, sin(i * (3.14f / 180.0f)), cos(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ 0.f, sin((i + 10.0f) * (3.14f / 180.0f)), cos((i + 10.0f) * (3.14f / 180.0f)) } *radius), LIGHT_VOLUME_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(center + rotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), 0.f, sin(i * (3.14f / 180.0f)) } *radius), center + rotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), 0.f, sin((i + 10.0f) * (3.14f / 180.0f)) } *radius), LIGHT_VOLUME_COLOR, 1.f); + } + + m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, depthTestBackup); +} + +void OvEditor::Core::EditorRenderer::RenderBoundingSpheres(OvCore::ECS::Components::CModelRenderer& p_modelRenderer) +{ + using namespace OvCore::ECS::Components; + using namespace OvPhysics::Entities; + + bool depthTestBackup = m_context.renderer->GetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST); + m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, false); + + /* Draw the sphere collider if any */ + if (auto model = p_modelRenderer.GetModel()) + { + auto& actor = p_modelRenderer.owner; + + OvMaths::FVector3 actorScale = actor.transform.GetWorldScale(); + OvMaths::FQuaternion actorRotation = actor.transform.GetWorldRotation(); + OvMaths::FVector3 actorPosition = actor.transform.GetWorldPosition(); + + const auto& modelBoundingsphere = + p_modelRenderer.GetFrustumBehaviour() == OvCore::ECS::Components::CModelRenderer::EFrustumBehaviour::CULL_CUSTOM ? + p_modelRenderer.GetCustomBoundingSphere() : + model->GetBoundingSphere(); + + float radiusScale = std::max(std::max(std::max(actorScale.x, actorScale.y), actorScale.z), 0.0f); + float scaledRadius = modelBoundingsphere.radius * radiusScale; + auto sphereOffset = OvMaths::FQuaternion::RotatePoint(modelBoundingsphere.position, actorRotation) * radiusScale; + + OvMaths::FVector3 boundingSphereCenter = actorPosition + sphereOffset; + + for (float i = 0; i <= 360.0f; i += 10.0f) + { + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), sin(i * (3.14f / 180.0f)), 0.f } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), sin((i + 10.0f) * (3.14f / 180.0f)), 0.f } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ 0.f, sin(i * (3.14f / 180.0f)), cos(i * (3.14f / 180.0f)) } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ 0.f, sin((i + 10.0f) * (3.14f / 180.0f)), cos((i + 10.0f) * (3.14f / 180.0f)) } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), 0.f, sin(i * (3.14f / 180.0f)) } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), 0.f, sin((i + 10.0f) * (3.14f / 180.0f)) } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + } + + if (p_modelRenderer.GetFrustumBehaviour() == OvCore::ECS::Components::CModelRenderer::EFrustumBehaviour::CULL_MESHES) + { + const auto& meshes = model->GetMeshes(); + + if (meshes.size() > 1) // One mesh would result into the same bounding sphere for mesh and model + { + for (auto mesh : meshes) + { + auto& meshBoundingSphere = mesh->GetBoundingSphere(); + float scaledRadius = meshBoundingSphere.radius * radiusScale; + auto sphereOffset = OvMaths::FQuaternion::RotatePoint(meshBoundingSphere.position, actorRotation) * radiusScale; + + OvMaths::FVector3 boundingSphereCenter = actorPosition + sphereOffset; + + for (float i = 0; i <= 360.0f; i += 10.0f) + { + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), sin(i * (3.14f / 180.0f)), 0.f } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), sin((i + 10.0f) * (3.14f / 180.0f)), 0.f } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ 0.f, sin(i * (3.14f / 180.0f)), cos(i * (3.14f / 180.0f)) } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ 0.f, sin((i + 10.0f) * (3.14f / 180.0f)), cos((i + 10.0f) * (3.14f / 180.0f)) } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + m_context.shapeDrawer->DrawLine(boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos(i * (3.14f / 180.0f)), 0.f, sin(i * (3.14f / 180.0f)) } *scaledRadius), boundingSphereCenter + actorRotation * (OvMaths::FVector3{ cos((i + 10.0f) * (3.14f / 180.0f)), 0.f, sin((i + 10.0f) * (3.14f / 180.0f)) } *scaledRadius), DEBUG_BOUNDS_COLOR, 1.f); + } + } + } + } } m_context.renderer->SetCapability(OvRendering::Settings::ERenderingCapability::DEPTH_TEST, depthTestBackup); + m_context.renderer->SetRasterizationLinesWidth(1.0f); } void OvEditor::Core::EditorRenderer::RenderModelAsset(OvRendering::Resources::Model& p_model) @@ -371,13 +680,18 @@ void OvEditor::Core::EditorRenderer::RenderGrid(const OvMaths::FVector3& p_viewP FMatrix4 model = FMatrix4::Scaling({ 1000.f, 1.f, 1000.f }); m_gridMaterial.Set("u_Color", p_color); m_context.renderer->DrawModelWithSingleMaterial(*m_context.editorResources->GetModel("Plane"), m_gridMaterial, &model); - - // m_context.shapeDrawer->DrawGrid(p_viewPos, p_color, 1000, 0.1f, 0.5f, 20.0f, 1.0f); } void OvEditor::Core::EditorRenderer::UpdateLights(OvCore::SceneSystem::Scene& p_scene) { - std::vector lightMatrices; - m_context.renderer->FindLightMatrices(p_scene, lightMatrices); + PROFILER_SPY("Light SSBO Update"); + auto lightMatrices = m_context.renderer->FindLightMatrices(p_scene); m_context.lightSSBO->SendBlocks(lightMatrices.data(), lightMatrices.size() * sizeof(FMatrix4)); -} \ No newline at end of file +} + +void OvEditor::Core::EditorRenderer::UpdateLightsInFrustum(OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum) +{ + PROFILER_SPY("Light SSBO Update (Frustum culled)"); + auto lightMatrices = m_context.renderer->FindLightMatricesInFrustum(p_scene, p_frustum); + m_context.lightSSBO->SendBlocks(lightMatrices.data(), lightMatrices.size() * sizeof(FMatrix4)); +} diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorResources.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorResources.cpp index ddd4633d..7ac1d200 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorResources.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorResources.cpp @@ -120,19 +120,21 @@ OvEditor::Core::EditorResources::EditorResources(const std::string& p_editorAsse } /* Models */ - m_models["Cube"] = ModelLoader::Create(modelsFolder + "Cube.fbx", modelParserFlags); - m_models["Cylinder"] = ModelLoader::Create(modelsFolder + "Cylinder.fbx", modelParserFlags); - m_models["Plane"] = ModelLoader::Create(modelsFolder + "Plane.fbx", modelParserFlags); - m_models["Roll"] = ModelLoader::Create(modelsFolder + "Roll.fbx", modelParserFlags); - m_models["Sphere"] = ModelLoader::Create(modelsFolder + "Sphere.fbx", modelParserFlags); - m_models["Arrow"] = ModelLoader::Create(modelsFolder + "Arrow.fbx", modelParserFlags); - m_models["Camera"] = ModelLoader::Create(modelsFolder + "Camera.fbx", modelParserFlags); + m_models["Cube"] = ModelLoader::Create(modelsFolder + "Cube.fbx", modelParserFlags); + m_models["Cylinder"] = ModelLoader::Create(modelsFolder + "Cylinder.fbx", modelParserFlags); + m_models["Plane"] = ModelLoader::Create(modelsFolder + "Plane.fbx", modelParserFlags); + m_models["Roll"] = ModelLoader::Create(modelsFolder + "Roll.fbx", modelParserFlags); + m_models["Sphere"] = ModelLoader::Create(modelsFolder + "Sphere.fbx", modelParserFlags); + m_models["Arrow_Translate"] = ModelLoader::Create(modelsFolder + "Arrow_Translate.fbx", modelParserFlags); + m_models["Arrow_Rotate"] = ModelLoader::Create(modelsFolder + "Arrow_Rotate.fbx", modelParserFlags); + m_models["Arrow_Scale"] = ModelLoader::Create(modelsFolder + "Arrow_Scale.fbx", modelParserFlags); + m_models["Camera"] = ModelLoader::Create(modelsFolder + "Camera.fbx", modelParserFlags); /* Shaders */ auto gridSource = OvEditor::Resources::RawShaders::GetGrid(); - auto guizmoSource = OvEditor::Resources::RawShaders::GetGuizmo(); + auto gizmoSource = OvEditor::Resources::RawShaders::GetGizmo(); m_shaders["Grid"] = ShaderLoader::CreateFromSource(gridSource.first, gridSource.second); - m_shaders["Guizmo"] = ShaderLoader::CreateFromSource(guizmoSource.first, guizmoSource.second); + m_shaders["Gizmo"] = ShaderLoader::CreateFromSource(gizmoSource.first, gizmoSource.second); /* From memory */ { diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/GizmoBehaviour.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/GizmoBehaviour.cpp new file mode 100644 index 00000000..bd5458ae --- /dev/null +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/GizmoBehaviour.cpp @@ -0,0 +1,178 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#include "OvEditor/Core/GizmoBehaviour.h" + +void OvEditor::Core::GizmoBehaviour::StartPicking(OvCore::ECS::Actor& p_target, const OvMaths::FVector3& p_cameraPosition, EGizmoOperation p_operation, EDirection p_direction) +{ + m_target = &p_target; + m_firstMouse = true; + m_originalTransform = p_target.transform.GetFTransform(); + m_distanceToActor = OvMaths::FVector3::Distance(p_cameraPosition, m_target->transform.GetWorldPosition()); + m_currentOperation = p_operation; + m_direction = p_direction; +} + +void OvEditor::Core::GizmoBehaviour::StopPicking() +{ + m_target = nullptr; +} + +OvMaths::FVector3 OvEditor::Core::GizmoBehaviour::GetFakeDirection() const +{ + auto result = OvMaths::FVector3(); + + switch (m_direction) + { + case OvEditor::Core::GizmoBehaviour::EDirection::X: + result = OvMaths::FVector3::Right; + break; + case OvEditor::Core::GizmoBehaviour::EDirection::Y: + result = OvMaths::FVector3::Up; + break; + case OvEditor::Core::GizmoBehaviour::EDirection::Z: + result = OvMaths::FVector3::Forward; + break; + } + + return result; +} + +OvMaths::FVector3 OvEditor::Core::GizmoBehaviour::GetRealDirection(bool p_relative) const +{ + auto result = OvMaths::FVector3(); + + switch (m_direction) + { + case OvEditor::Core::GizmoBehaviour::EDirection::X: + result = p_relative ? m_originalTransform.GetWorldRight() : m_originalTransform.GetLocalRight(); + break; + case OvEditor::Core::GizmoBehaviour::EDirection::Y: + result = p_relative ? m_originalTransform.GetWorldUp() : m_originalTransform.GetLocalUp(); + break; + case OvEditor::Core::GizmoBehaviour::EDirection::Z: + result = p_relative ? m_originalTransform.GetWorldForward() : m_originalTransform.GetLocalForward(); + break; + } + + return result; +} + +OvMaths::FVector2 OvEditor::Core::GizmoBehaviour::GetScreenDirection(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const +{ + auto start = m_originalTransform.GetWorldPosition(); + auto end = m_originalTransform.GetWorldPosition() + GetRealDirection(true) * 0.01f; + + auto start2D = OvMaths::FVector2(); + { + auto clipSpacePos = p_projectionMatrix * (p_viewMatrix * OvMaths::FVector4{ start.x, start.y, start.z, 1.0f }); + auto ndcSpacePos = OvMaths::FVector3{ clipSpacePos.x, clipSpacePos.y, clipSpacePos.z } / clipSpacePos.w; + auto windowSpacePos = ((OvMaths::FVector2{ ndcSpacePos.x, ndcSpacePos.y } + 1.0f) / 2.0f); + windowSpacePos.x *= p_viewSize.x; + windowSpacePos.y *= p_viewSize.y; + start2D = windowSpacePos; + } + + auto end2D = OvMaths::FVector2(); + { + auto clipSpacePos = p_projectionMatrix * (p_viewMatrix * OvMaths::FVector4{ end.x, end.y, end.z, 1.0f }); + auto ndcSpacePos = OvMaths::FVector3{ clipSpacePos.x, clipSpacePos.y, clipSpacePos.z } / clipSpacePos.w; + auto windowSpacePos = ((OvMaths::FVector2{ ndcSpacePos.x, ndcSpacePos.y } + 1.0f) / 2.0f); + windowSpacePos.x *= p_viewSize.x; + windowSpacePos.y *= p_viewSize.y; + end2D = windowSpacePos; + } + + auto result = end2D - start2D; + + result.y *= -1; // Screen coordinates are reversed, so we inverse the Y + + return OvMaths::FVector2::Normalize(result); +} + +void OvEditor::Core::GizmoBehaviour::ApplyTranslation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const +{ + auto unitsPerPixel = 0.001f * m_distanceToActor; + auto originPosition = m_originalTransform.GetLocalPosition(); + + auto screenDirection = GetScreenDirection(p_viewMatrix, p_projectionMatrix, p_viewSize); + + auto totalDisplacement = m_currentMouse - m_originMouse; + auto translationCoefficient = OvMaths::FVector2::Dot(totalDisplacement, screenDirection); + + m_target->transform.SetLocalPosition(originPosition + GetRealDirection() * translationCoefficient * unitsPerPixel); +} + +void OvEditor::Core::GizmoBehaviour::ApplyRotation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const +{ + auto unitsPerPixel = 0.2f; + auto originRotation = m_originalTransform.GetLocalRotation(); + + auto screenDirection = GetScreenDirection(p_viewMatrix, p_projectionMatrix, p_viewSize); + screenDirection = OvMaths::FVector2(-screenDirection.y, screenDirection.x); + + auto totalDisplacement = m_currentMouse - m_originMouse; + auto rotationCoefficient = OvMaths::FVector2::Dot(totalDisplacement, screenDirection); + + auto rotationToApply = OvMaths::FQuaternion(OvMaths::FVector3(GetFakeDirection() * rotationCoefficient * unitsPerPixel)); + m_target->transform.SetLocalRotation(originRotation * rotationToApply); +} + +void OvEditor::Core::GizmoBehaviour::ApplyScale(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) const +{ + auto unitsPerPixel = 0.01f; + auto originScale = m_originalTransform.GetLocalScale(); + + auto screenDirection = GetScreenDirection(p_viewMatrix, p_projectionMatrix, p_viewSize); + + auto totalDisplacement = m_currentMouse - m_originMouse; + auto scaleCoefficient = OvMaths::FVector2::Dot(totalDisplacement, screenDirection); + + auto newScale = originScale + GetFakeDirection() * scaleCoefficient * unitsPerPixel; + + /* Prevent scale from being negative*/ + newScale.x = std::max(newScale.x, 0.0001f); + newScale.y = std::max(newScale.y, 0.0001f); + newScale.z = std::max(newScale.z, 0.0001f); + + m_target->transform.SetLocalScale(newScale); +} + +void OvEditor::Core::GizmoBehaviour::ApplyOperation(const OvMaths::FMatrix4& p_viewMatrix, const OvMaths::FMatrix4& p_projectionMatrix, const OvMaths::FVector2& p_viewSize) +{ + switch (m_currentOperation) + { + case EGizmoOperation::TRANSLATE: + ApplyTranslation(p_viewMatrix, p_projectionMatrix, p_viewSize); + break; + + case EGizmoOperation::ROTATE: + ApplyRotation(p_viewMatrix, p_projectionMatrix, p_viewSize); + break; + + case EGizmoOperation::SCALE: + ApplyScale(p_viewMatrix, p_projectionMatrix, p_viewSize); + break; + } +} + +void OvEditor::Core::GizmoBehaviour::SetCurrentMouse(const OvMaths::FVector2& p_mousePosition) +{ + if (m_firstMouse) + { + m_currentMouse = m_originMouse = p_mousePosition; + m_firstMouse = false; + } + else + { + m_currentMouse = p_mousePosition; + } +} + +bool OvEditor::Core::GizmoBehaviour::IsPicking() const +{ + return m_target; +} \ No newline at end of file diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/ProjectHub.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/ProjectHub.cpp index 2e6b4e77..16d6df04 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/ProjectHub.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/ProjectHub.cpp @@ -307,3 +307,8 @@ void OvEditor::Core::ProjectHub::SetupContext() m_uiManager->EnableEditorLayoutSave(false); m_uiManager->EnableDocking(false); } + +void OvEditor::Core::ProjectHub::RegisterProject(const std::string& p_path) +{ + static_cast(m_mainPanel.get())->RegisterProject(p_path); +} diff --git a/Sources/Overload/OvEditor/src/OvEditor/Main.cpp b/Sources/Overload/OvEditor/src/OvEditor/Main.cpp index 242e20a4..af0be930 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Main.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Main.cpp @@ -4,20 +4,70 @@ * @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. */ +#include + +#include + #include "OvEditor/Core/ProjectHub.h" #include "OvEditor/Core/Application.h" -#ifdef _DEBUG - int main() -#else - #undef APIENTRY - #include "Windows.h" - INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) +#undef APIENTRY +#include "Windows.h" + +/** +* When Overload is launched from a project file, we should consider the executable path as +* the current working directory +* @param p_executablePath +*/ +void UpdateWorkingDirectory(const std::string& p_executablePath) +{ + if (!IsDebuggerPresent()) + { + std::filesystem::current_path(OvTools::Utils::PathParser::GetContainingFolder(p_executablePath)); + } +} + +int main(int argc, char** argv); + +#ifndef _DEBUG +INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) +{ + main(__argc, __argv); +} #endif + +int main(int argc, char** argv) { - OvEditor::Core::ProjectHub* hub = new OvEditor::Core::ProjectHub(); - auto[ready, projectPath, projectName] = hub->Run(); - delete hub; + UpdateWorkingDirectory(argv[0]); + + bool ready = false; + std::string projectPath; + std::string projectName; + + { + OvEditor::Core::ProjectHub hub; + + if (argc < 2) + { + // No project file given as argument ==> Open the ProjectHub + std::tie(ready, projectPath, projectName) = hub.Run(); + } + else + { + // Project file given as argument ==> Open the project + std::string projectFile = argv[1]; + + if (OvTools::Utils::PathParser::GetExtension(projectFile) == "ovproject") + { + ready = true; + projectPath = OvTools::Utils::PathParser::GetContainingFolder(projectFile); + projectName = OvTools::Utils::PathParser::GetElementName(projectFile); + OvTools::Utils::String::Replace(projectName, ".ovproject", ""); + } + + hub.RegisterProject(projectPath); + } + } if (ready) { diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AView.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AView.cpp index dc67c138..6b61a86d 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AView.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AView.cpp @@ -16,62 +16,20 @@ OvEditor::Panels::AView::AView const OvUI::Settings::PanelWindowSettings& p_windowSettings ) : PanelWindow(p_title, p_opened, p_windowSettings), m_editorRenderer(EDITOR_RENDERER()) { - glGenFramebuffers(1, &m_fbo); - glGenTextures(1, &m_renderTexture); - glGenRenderbuffers(1, &m_depthStencilBuffer); - m_cameraPosition = { -10.0f, 4.0f, 10.0f }; m_camera.SetPitch(-10.0f); m_camera.SetYaw(-45.f); - m_image = &CreateWidget(m_renderTexture, OvMaths::FVector2{ 0.f, 0.f }); -} - -OvEditor::Panels::AView::~AView() -{ - glDeleteBuffers(1, &m_fbo); - glDeleteTextures(1, &m_renderTexture); - glGenRenderbuffers(1, &m_depthStencilBuffer); + m_image = &CreateWidget(m_fbo.GetTextureID(), OvMaths::FVector2{ 0.f, 0.f }); } void OvEditor::Panels::AView::Update(float p_deltaTime) { - Bind(); - auto[winWidth, winHeight] = GetSafeSize(); m_image->size = OvMaths::FVector2(static_cast(winWidth), static_cast(winHeight)); - /* Setup texture */ - glBindTexture(GL_TEXTURE_2D, m_renderTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, winWidth, winHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - /* Setup depth-stencil buffer (24 + 8 bits) */ - glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, winWidth, winHeight); - - /* Setup frame buffer */ - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_renderTexture, 0); - - Unbind(); -} - -void OvEditor::Panels::AView::Bind() -{ - auto[winWidth, winHeight] = GetSafeSize(); - - glViewport(0, 0, winWidth, winHeight); - - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); -} - -void OvEditor::Panels::AView::Unbind() -{ - glBindFramebuffer(GL_FRAMEBUFFER, 0); + m_fbo.Resize(winWidth, winHeight); } void OvEditor::Panels::AView::_Draw_Impl() @@ -88,13 +46,12 @@ void OvEditor::Panels::AView::Render() FillEngineUBO(); auto [winWidth, winHeight] = GetSafeSize(); - auto projection = m_camera.GetProjectionMatrix(winWidth, winHeight); - auto view = m_camera.GetViewMatrix(m_cameraPosition); - EDITOR_CONTEXT(shapeDrawer)->SetViewProjection(projection * view); - Bind(); + EDITOR_CONTEXT(shapeDrawer)->SetViewProjection(m_camera.GetProjectionMatrix() * m_camera.GetViewMatrix()); + + glViewport(0, 0, winWidth, winHeight); // TODO: Move this OpenGL call to OvRendering + _Render_Impl(); - Unbind(); } void OvEditor::Panels::AView::SetCameraPosition(const OvMaths::FVector3 & p_position) @@ -135,8 +92,14 @@ void OvEditor::Panels::AView::FillEngineUBO() auto[winWidth, winHeight] = GetSafeSize(); size_t offset = sizeof(OvMaths::FMatrix4); // We skip the model matrix (Which is a special case, modified every draw calls) - engineUBO.SetSubData(OvMaths::FMatrix4::Transpose(m_camera.GetViewMatrix(m_cameraPosition)), std::ref(offset)); - engineUBO.SetSubData(OvMaths::FMatrix4::Transpose(m_camera.GetProjectionMatrix(winWidth, winHeight)), std::ref(offset)); + engineUBO.SetSubData(OvMaths::FMatrix4::Transpose(m_camera.GetViewMatrix()), std::ref(offset)); + engineUBO.SetSubData(OvMaths::FMatrix4::Transpose(m_camera.GetProjectionMatrix()), std::ref(offset)); engineUBO.SetSubData(m_cameraPosition, std::ref(offset)); } +void OvEditor::Panels::AView::PrepareCamera() +{ + auto [winWidth, winHeight] = GetSafeSize(); + m_camera.CacheMatrices(winWidth, winHeight, m_cameraPosition); +} + diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index afff5396..15d9d12b 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -873,6 +873,32 @@ OvEditor::Panels::AssetBrowser::AssetBrowser m_projectAssetFolder(p_projectAssetFolder), m_projectScriptFolder(p_projectScriptFolder) { + if (!std::filesystem::exists(m_projectAssetFolder)) + { + std::filesystem::create_directories(m_projectAssetFolder); + + OvWindowing::Dialogs::MessageBox message + ( + "Assets folder not found", + "The \"Assets/\" folders hasn't been found in your project directory.\nIt has been automatically generated", + OvWindowing::Dialogs::MessageBox::EMessageType::WARNING, + OvWindowing::Dialogs::MessageBox::EButtonLayout::OK + ); + } + + if (!std::filesystem::exists(m_projectScriptFolder)) + { + std::filesystem::create_directories(m_projectScriptFolder); + + OvWindowing::Dialogs::MessageBox message + ( + "Scripts folder not found", + "The \"Scripts/\" folders hasn't been found in your project directory.\nIt has been automatically generated", + OvWindowing::Dialogs::MessageBox::EMessageType::WARNING, + OvWindowing::Dialogs::MessageBox::EButtonLayout::OK + ); + } + auto& refreshButton = CreateWidget("Rescan assets"); refreshButton.ClickedEvent += std::bind(&AssetBrowser::Refresh, this); refreshButton.lineBreak = false; diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetView.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetView.cpp index 50a114b7..1e6b995f 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetView.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetView.cpp @@ -20,6 +20,7 @@ OvEditor::Panels::AssetView::AssetView ) : AViewControllable(p_title, p_opened, p_windowSettings) { m_camera.SetClearColor({ 0.278f, 0.278f, 0.278f }); + m_camera.SetFar(1000.0f); m_resource = static_cast(nullptr); m_image->AddPlugin>>("File").DataReceivedEvent += [this](auto p_data) @@ -47,12 +48,18 @@ OvEditor::Panels::AssetView::AssetView void OvEditor::Panels::AssetView::_Render_Impl() { - EDITOR_CONTEXT(renderer)->SetStencilMask(0xFF); - EDITOR_CONTEXT(renderer)->Clear(m_camera); - EDITOR_CONTEXT(renderer)->SetStencilMask(0x00); + PrepareCamera(); - uint8_t glState = EDITOR_CONTEXT(renderer)->FetchGLState(); - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + auto& baseRenderer = *EDITOR_CONTEXT(renderer).get(); + + m_fbo.Bind(); + + baseRenderer.SetStencilMask(0xFF); + baseRenderer.Clear(m_camera); + baseRenderer.SetStencilMask(0x00); + + uint8_t glState = baseRenderer.FetchGLState(); + baseRenderer.ApplyStateMask(glState); m_editorRenderer.RenderGrid(m_cameraPosition, m_gridColor); @@ -65,7 +72,9 @@ void OvEditor::Panels::AssetView::_Render_Impl() if (auto pval = std::get_if(&m_resource); pval && *pval) m_editorRenderer.RenderMaterialAsset(**pval); - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + baseRenderer.ApplyStateMask(glState); + + m_fbo.Unbind(); } void OvEditor::Panels::AssetView::SetResource(ViewableResource p_resource) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/GameView.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/GameView.cpp index b612f8d2..fe06ba57 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/GameView.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/GameView.cpp @@ -9,6 +9,7 @@ #include "OvEditor/Core/EditorRenderer.h" #include "OvEditor/Panels/GameView.h" #include "OvEditor/Core/EditorActions.h" +#include "OvEditor/Settings/EditorSettings.h" OvEditor::Panels::GameView::GameView ( @@ -33,6 +34,7 @@ void OvEditor::Panels::GameView::Update(float p_deltaTime) m_camera = cameraComponent->GetCamera(); m_cameraPosition = cameraComponent->owner.transform.GetWorldPosition(); m_hasCamera = true; + PrepareCamera(); } else { @@ -44,13 +46,41 @@ void OvEditor::Panels::GameView::Update(float p_deltaTime) void OvEditor::Panels::GameView::_Render_Impl() { - EDITOR_CONTEXT(renderer)->Clear(m_camera); + auto& baseRenderer = *EDITOR_CONTEXT(renderer).get(); + auto& currentScene = *m_sceneManager.GetCurrentScene(); - uint8_t glState = EDITOR_CONTEXT(renderer)->FetchGLState(); - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + m_fbo.Bind(); + + baseRenderer.Clear(m_camera); + + uint8_t glState = baseRenderer.FetchGLState(); + baseRenderer.ApplyStateMask(glState); if (m_hasCamera) - m_editorRenderer.RenderScene(m_cameraPosition); + { + if (m_camera.HasFrustumLightCulling()) + { + m_editorRenderer.UpdateLightsInFrustum(currentScene, m_camera.GetFrustum()); + } + else + { + m_editorRenderer.UpdateLights(currentScene); + } - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + m_editorRenderer.RenderScene(m_cameraPosition, m_camera); + } + + baseRenderer.ApplyStateMask(glState); + + m_fbo.Unbind(); +} + +bool OvEditor::Panels::GameView::HasCamera() const +{ + return m_hasCamera; +} + +std::optional OvEditor::Panels::GameView::GetActiveFrustum() const +{ + return m_hasCamera ? m_camera.GetFrustum() : std::optional{}; } diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/HelpWindow.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/HelpWindow.cpp index 33fcef3a..8d81195a 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/HelpWindow.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/HelpWindow.cpp @@ -11,25 +11,6 @@ #include #include -class TestWidget : public OvUI::Widgets::AWidget -{ -public: - TestWidget(std::string pid) { id = pid; } - - virtual void _Draw_Impl() override - { - ImGui::Columns(2, id.c_str(), false); - ImGui::SetColumnWidth(0, 75); - ImGui::Text("Coucou je suis a gauche"); - ImGui::NextColumn(); - ImGui::Text("Coucou je suis a droite"); - - ImGui::Columns(1); - } - - std::string id; -}; - OvEditor::Panels::HelpWindow::HelpWindow ( const std::string& p_title, diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/Hierarchy.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/Hierarchy.cpp index 44ac7a9a..3a501b05 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/Hierarchy.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/Hierarchy.cpp @@ -223,11 +223,26 @@ void OvEditor::Panels::Hierarchy::SelectActorByInstance(OvCore::ECS::Actor& p_ac SelectActorByWidget(*result->second); } +void ExpandTreeNode(OvUI::Widgets::Layout::TreeNode& p_toExpand, const OvUI::Widgets::Layout::TreeNode* p_root) +{ + p_toExpand.Open(); + + if (&p_toExpand != p_root && p_toExpand.HasParent()) + { + ExpandTreeNode(*static_cast(p_toExpand.GetParent()), p_root); + } +} + void OvEditor::Panels::Hierarchy::SelectActorByWidget(OvUI::Widgets::Layout::TreeNode & p_widget) { UnselectActorsWidgets(); p_widget.selected = true; + + if (p_widget.HasParent()) + { + ExpandTreeNode(*static_cast(p_widget.GetParent()), m_sceneRoot); + } } void OvEditor::Panels::Hierarchy::AttachActorToParent(OvCore::ECS::Actor & p_actor) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp index 2b8d530f..9449b045 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp @@ -26,6 +26,7 @@ #include "OvEditor/Panels/SceneView.h" #include "OvEditor/Panels/AssetView.h" #include "OvEditor/Core/EditorActions.h" +#include "OvEditor/Settings/EditorSettings.h" using namespace OvUI::Panels; using namespace OvUI::Widgets; @@ -150,7 +151,6 @@ void OvEditor::Panels::MenuBar::CreateSettingsMenu() cameraPositionMenu.CreateWidget("Scene View").ClickedEvent += EDITOR_BIND(ResetSceneViewCameraPosition); cameraPositionMenu.CreateWidget("Asset View").ClickedEvent += EDITOR_BIND(ResetAssetViewCameraPosition); - auto& viewColors = settingsMenu.CreateWidget("View Colors"); auto& sceneViewBackground = viewColors.CreateWidget("Scene View Background"); auto& sceneViewBackgroundPicker = sceneViewBackground.CreateWidget(false, OvUI::Types::Color{ 0.278f, 0.278f, 0.278f }); @@ -199,9 +199,16 @@ void OvEditor::Panels::MenuBar::CreateSettingsMenu() EDITOR_PANEL(Panels::AssetView, "Asset View").SetGridColor(OvMaths::FVector3::One); assetViewGridPicker.color = OvUI::Types::Color::White; }; + + auto& debuggingMenu = settingsMenu.CreateWidget("Debugging"); + debuggingMenu.CreateWidget("Show geometry bounds", "", true, Settings::EditorSettings::ShowGeometryBounds).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowGeometryBounds = p_value; }; + debuggingMenu.CreateWidget("Show lights bounds", "", true, Settings::EditorSettings::ShowLightBounds).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowLightBounds = p_value; }; + auto& subMenu = debuggingMenu.CreateWidget("Frustum culling visualizer..."); + subMenu.CreateWidget("For geometry", "", true, Settings::EditorSettings::ShowGeometryFrustumCullingInSceneView).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowGeometryFrustumCullingInSceneView = p_value; }; + subMenu.CreateWidget("For lights", "", true, Settings::EditorSettings::ShowLightFrustumCullingInSceneView).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowLightFrustumCullingInSceneView = p_value; }; } -void OvEditor::Panels::MenuBar::CreateLayoutMenu() +void OvEditor::Panels::MenuBar::CreateLayoutMenu() { auto& layoutMenu = CreateWidget("Layout"); layoutMenu.CreateWidget("Reset").ClickedEvent += EDITOR_BIND(ResetLayout); @@ -210,6 +217,7 @@ void OvEditor::Panels::MenuBar::CreateLayoutMenu() void OvEditor::Panels::MenuBar::CreateHelpMenu() { auto& helpMenu = CreateWidget("Help"); + helpMenu.CreateWidget("Overload Documentation").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("http://overloadengine.org/documentation/annotated.html"); }; helpMenu.CreateWidget("Scripting Documentation").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("http://overloadengine.org/api"); }; helpMenu.CreateWidget("Overload Website").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("http://overloadengine.org"); }; diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/SceneView.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/SceneView.cpp index 02d44fac..d5c2ed5d 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/SceneView.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/SceneView.cpp @@ -9,6 +9,8 @@ #include "OvEditor/Core/EditorRenderer.h" #include "OvEditor/Core/EditorActions.h" #include "OvEditor/Panels/SceneView.h" +#include "OvEditor/Panels/GameView.h" +#include "OvEditor/Settings/EditorSettings.h" OvEditor::Panels::SceneView::SceneView ( @@ -19,6 +21,7 @@ OvEditor::Panels::SceneView::SceneView m_sceneManager(EDITOR_CONTEXT(sceneManager)) { m_camera.SetClearColor({ 0.278f, 0.278f, 0.278f }); + m_camera.SetFar(1000.0f); m_image->AddPlugin>>("File").DataReceivedEvent += [this](auto p_data) { @@ -36,31 +39,78 @@ void OvEditor::Panels::SceneView::Update(float p_deltaTime) { AViewControllable::Update(p_deltaTime); - if (IsHovered() && EDITOR_CONTEXT(inputManager)->IsMouseButtonPressed(OvWindowing::Inputs::EMouseButton::MOUSE_BUTTON_LEFT)) + using namespace OvWindowing::Inputs; + + if (IsFocused() && !m_cameraController.IsRightMousePressed()) { - /* Prevent losing focus on actor while resizing a window */ - if (auto cursor = ImGui::GetMouseCursor(); - cursor != ImGuiMouseCursor_ResizeEW && - cursor != ImGuiMouseCursor_ResizeNS && - cursor != ImGuiMouseCursor_ResizeNWSE && - cursor != ImGuiMouseCursor_ResizeNESW && - cursor != ImGuiMouseCursor_ResizeAll) - EDITOR_EXEC(UnselectActor()); + if (EDITOR_CONTEXT(inputManager)->IsKeyPressed(EKey::KEY_W)) + { + m_currentOperation = OvEditor::Core::EGizmoOperation::TRANSLATE; + } + + if (EDITOR_CONTEXT(inputManager)->IsKeyPressed(EKey::KEY_E)) + { + m_currentOperation = OvEditor::Core::EGizmoOperation::ROTATE; + } + + if (EDITOR_CONTEXT(inputManager)->IsKeyPressed(EKey::KEY_R)) + { + m_currentOperation = OvEditor::Core::EGizmoOperation::SCALE; + } } } void OvEditor::Panels::SceneView::_Render_Impl() { - EDITOR_CONTEXT(renderer)->SetStencilMask(0xFF); - EDITOR_CONTEXT(renderer)->Clear(m_camera); - EDITOR_CONTEXT(renderer)->SetStencilMask(0x00); + PrepareCamera(); - uint8_t glState = EDITOR_CONTEXT(renderer)->FetchGLState(); - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + auto& baseRenderer = *EDITOR_CONTEXT(renderer).get(); + + uint8_t glState = baseRenderer.FetchGLState(); + baseRenderer.ApplyStateMask(glState); + + RenderScene(glState); + baseRenderer.ApplyStateMask(glState); + HandleActorPicking(); + baseRenderer.ApplyStateMask(glState); +} + +void OvEditor::Panels::SceneView::RenderScene(uint8_t p_defaultRenderState) +{ + auto& baseRenderer = *EDITOR_CONTEXT(renderer).get(); + auto& currentScene = *m_sceneManager.GetCurrentScene(); + auto& gameView = EDITOR_PANEL(OvEditor::Panels::GameView, "Game View"); + + // If the game is playing, and ShowLightFrustumCullingInSceneView is true, apply the game view frustum culling to the scene view (For debugging purposes) + if (auto gameViewFrustum = gameView.GetActiveFrustum(); gameViewFrustum.has_value() && gameView.GetCamera().HasFrustumLightCulling() && Settings::EditorSettings::ShowLightFrustumCullingInSceneView) + { + m_editorRenderer.UpdateLightsInFrustum(currentScene, gameViewFrustum.value()); + } + else + { + m_editorRenderer.UpdateLights(currentScene); + } + + m_fbo.Bind(); + + baseRenderer.SetStencilMask(0xFF); + baseRenderer.Clear(m_camera); + baseRenderer.SetStencilMask(0x00); m_editorRenderer.RenderGrid(m_cameraPosition, m_gridColor); m_editorRenderer.RenderCameras(); - m_editorRenderer.RenderScene(m_cameraPosition); + + // If the game is playing, and ShowGeometryFrustumCullingInSceneView is true, apply the game view frustum culling to the scene view (For debugging purposes) + if (auto gameViewFrustum = gameView.GetActiveFrustum(); gameViewFrustum.has_value() && gameView.GetCamera().HasFrustumLightCulling() && Settings::EditorSettings::ShowGeometryFrustumCullingInSceneView) + { + m_camera.SetFrustumGeometryCulling(gameView.HasCamera() ? gameView.GetCamera().HasFrustumGeometryCulling() : false); + m_editorRenderer.RenderScene(m_cameraPosition, m_camera, &gameViewFrustum.value()); + m_camera.SetFrustumGeometryCulling(false); + } + else + { + m_editorRenderer.RenderScene(m_cameraPosition, m_camera); + } if (EDITOR_EXEC(IsAnyActorSelected())) { @@ -69,14 +119,107 @@ void OvEditor::Panels::SceneView::_Render_Impl() if (selectedActor.IsActive()) { m_editorRenderer.RenderActorAsSelected(selectedActor, true); - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); + baseRenderer.ApplyStateMask(p_defaultRenderState); m_editorRenderer.RenderActorAsSelected(selectedActor, false); } - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); - EDITOR_CONTEXT(renderer)->Clear(false, true, false); - m_editorRenderer.RenderGuizmo(selectedActor.transform.GetWorldPosition(), selectedActor.transform.GetWorldRotation()); + baseRenderer.ApplyStateMask(p_defaultRenderState); + baseRenderer.Clear(false, true, false); + m_editorRenderer.RenderGizmo(selectedActor.transform.GetWorldPosition(), selectedActor.transform.GetWorldRotation(), m_currentOperation); } - EDITOR_CONTEXT(renderer)->ApplyStateMask(glState); -} \ No newline at end of file + m_fbo.Unbind(); +} + +void OvEditor::Panels::SceneView::RenderSceneForActorPicking() +{ + auto& baseRenderer = *EDITOR_CONTEXT(renderer).get(); + + auto [winWidth, winHeight] = GetSafeSize(); + + m_actorPickingFramebuffer.Resize(winWidth, winHeight); + m_actorPickingFramebuffer.Bind(); + baseRenderer.SetClearColor(1.0f, 1.0f, 1.0f); + baseRenderer.Clear(); + m_editorRenderer.RenderSceneForActorPicking(); + + if (EDITOR_EXEC(IsAnyActorSelected())) + { + auto& selectedActor = EDITOR_EXEC(GetSelectedActor()); + baseRenderer.Clear(false, true, false); + m_editorRenderer.RenderGizmo(selectedActor.transform.GetWorldPosition(), selectedActor.transform.GetWorldRotation(), m_currentOperation, true); + } + + m_actorPickingFramebuffer.Unbind(); +} + +bool IsResizing() +{ + auto cursor = ImGui::GetMouseCursor(); + + return + cursor == ImGuiMouseCursor_ResizeEW || + cursor == ImGuiMouseCursor_ResizeNS || + cursor == ImGuiMouseCursor_ResizeNWSE || + cursor == ImGuiMouseCursor_ResizeNESW || + cursor == ImGuiMouseCursor_ResizeAll;; +} + +void OvEditor::Panels::SceneView::HandleActorPicking() +{ + using namespace OvWindowing::Inputs; + + auto& inputManager = *EDITOR_CONTEXT(inputManager); + + if (inputManager.IsMouseButtonReleased(EMouseButton::MOUSE_BUTTON_LEFT)) + { + m_gizmoOperations.StopPicking(); + } + + if (IsHovered() && !IsResizing() && inputManager.IsMouseButtonPressed(EMouseButton::MOUSE_BUTTON_LEFT) && !m_cameraController.IsRightMousePressed()) + { + RenderSceneForActorPicking(); + + // Look actor under mouse + auto [mouseX, mouseY] = inputManager.GetMousePosition(); + mouseX -= m_position.x; + mouseY -= m_position.y; + mouseY = GetSafeSize().second - mouseY + 25; + + m_actorPickingFramebuffer.Bind(); + uint8_t pixel[3]; + glReadPixels(static_cast(mouseX), static_cast(mouseY), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel); + m_actorPickingFramebuffer.Unbind(); + + /* Gizmo picking */ + if (EDITOR_EXEC(IsAnyActorSelected()) && pixel[0] == 255 && pixel[1] == 255 && pixel[2] >= 252 && pixel[2] <= 254) + { + auto direction = static_cast(pixel[2] - 252); + m_gizmoOperations.StartPicking(EDITOR_EXEC(GetSelectedActor()), m_cameraPosition, m_currentOperation, direction); + } + /* Actor picking */ + else + { + uint32_t actorID = (0 << 24) | (pixel[2] << 16) | (pixel[1] << 8) | (pixel[0] << 0); + + if (auto actor = EDITOR_CONTEXT(sceneManager).GetCurrentScene()->FindActorByID(actorID)) + { + EDITOR_EXEC(SelectActor(*actor)); + } + else + { + EDITOR_EXEC(UnselectActor()); + } + } + } + + if (m_gizmoOperations.IsPicking()) + { + auto mousePosition = EDITOR_CONTEXT(inputManager)->GetMousePosition(); + + auto [winWidth, winHeight] = GetSafeSize(); + + m_gizmoOperations.SetCurrentMouse({ static_cast(mousePosition.first), static_cast(mousePosition.second) }); + m_gizmoOperations.ApplyOperation(m_camera.GetViewMatrix(), m_camera.GetProjectionMatrix(), { static_cast(winWidth), static_cast(winHeight) }); + } +} diff --git a/Sources/Overload/OvEditor/src/OvEditor/Resources/RawShaders.cpp b/Sources/Overload/OvEditor/src/OvEditor/Resources/RawShaders.cpp index 832cc761..80a68ab0 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Resources/RawShaders.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Resources/RawShaders.cpp @@ -115,7 +115,7 @@ void main() return source; } -std::pair OvEditor::Resources::RawShaders::GetGuizmo() +std::pair OvEditor::Resources::RawShaders::GetGizmo() { std::pair source; @@ -137,12 +137,11 @@ layout (std140) uniform EngineUBO out VS_OUT { - vec3 FragPos; - vec3 Normal; flat vec3 Color; } vs_out; uniform bool u_IsBall; +uniform bool u_IsPickable; mat4 rotationMatrix(vec3 axis, float angle) { @@ -171,11 +170,42 @@ void main() float distanceToCamera = distance(ubo_ViewPos, instanceModel[3].xyz); - vs_out.FragPos = vec3(instanceModel * vec4(geo_Pos * distanceToCamera * 0.1f, 1.0)); - vs_out.Normal = normalize(mat3(transpose(inverse(instanceModel))) * geo_Normal); - vs_out.Color = vec3(float(gl_InstanceID == 1 || u_IsBall), float(gl_InstanceID == 2 || u_IsBall), float(gl_InstanceID == 0 || u_IsBall)); + vec3 pos = geo_Pos; - gl_Position = ubo_Projection * ubo_View * vec4(vs_out.FragPos, 1.0); + vec3 fragPos = vec3(instanceModel * vec4(pos * distanceToCamera * 0.1f, 1.0)); + + if (u_IsPickable) + { + int blueComponent = 0; + + if (gl_InstanceID == 1) + blueComponent = 252; + + if (gl_InstanceID == 2) + blueComponent = 253; + + if (gl_InstanceID == 0) + blueComponent = 254; + + vs_out.Color = vec3(1.0f, 1.0f, blueComponent / 255.0f); + } + else + { + if (u_IsBall) + { + vs_out.Color = vec3(1.0f); + } + else + { + float red = float(gl_InstanceID == 1); // X + float green = float(gl_InstanceID == 2); // Y + float blue = float(gl_InstanceID == 0); // Z + + vs_out.Color = vec3(red, green, blue); + } + } + + gl_Position = ubo_Projection * ubo_View * vec4(fragPos, 1.0); } )"; @@ -186,25 +216,15 @@ out vec4 FRAGMENT_COLOR; in VS_OUT { - vec3 FragPos; - vec3 Normal; vec3 Color; } fs_in; -const vec3 c_lightPosition = vec3(-9000.0, 10000.0, 11000.0); -const vec3 c_lightDiffuse = vec3(1.0, 1.0, 1.0); -const vec3 c_lightAmbient = vec3(0.3, 0.3, 0.3); - -vec3 Lambert(vec3 p_fragPos, vec3 p_normal) -{ - const float diffuse = max(dot(p_normal, normalize(c_lightPosition - p_fragPos)), 0.0); - return clamp(c_lightDiffuse * diffuse + c_lightAmbient, 0.0, 1.0); -} +uniform bool u_IsPickable; void main() { - FRAGMENT_COLOR = vec4(Lambert(fs_in.FragPos, fs_in.Normal) * fs_in.Color, 0.5f); + FRAGMENT_COLOR = vec4(fs_in.Color, 1.0f); })"; return source; -} +} \ No newline at end of file diff --git a/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp new file mode 100644 index 00000000..3d6e6e0d --- /dev/null +++ b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp @@ -0,0 +1,7 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#include "OvEditor/Settings/EditorSettings.h" \ No newline at end of file diff --git a/Sources/Overload/OvGame/OvGame.vcxproj b/Sources/Overload/OvGame/OvGame.vcxproj index 2575e68b..48dfc539 100644 --- a/Sources/Overload/OvGame/OvGame.vcxproj +++ b/Sources/Overload/OvGame/OvGame.vcxproj @@ -14,19 +14,19 @@ 15.0 {C9CFF0F8-3CA5-424E-A41D-110158096532} OvGame - 10.0.17763.0 + 10.0 Application true - v141 + v142 MultiByte Application false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvGame/include/OvGame/Core/GameRenderer.h b/Sources/Overload/OvGame/include/OvGame/Core/GameRenderer.h index 9f5061b7..019f75ee 100644 --- a/Sources/Overload/OvGame/include/OvGame/Core/GameRenderer.h +++ b/Sources/Overload/OvGame/include/OvGame/Core/GameRenderer.h @@ -43,6 +43,12 @@ namespace OvGame::Core */ void UpdateLights(OvCore::SceneSystem::Scene& p_scene); + /** + * Update the light SSBO with the current scene (Lights outside of the given frustum are culled) + * @param p_scene + */ + void UpdateLightsInFrustum(OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum); + private: Context& m_context; OvCore::Resources::Material m_emptyMaterial; diff --git a/Sources/Overload/OvGame/src/OvGame/Core/GameRenderer.cpp b/Sources/Overload/OvGame/src/OvGame/Core/GameRenderer.cpp index 604f6eac..7d85b04f 100644 --- a/Sources/Overload/OvGame/src/OvGame/Core/GameRenderer.cpp +++ b/Sources/Overload/OvGame/src/OvGame/Core/GameRenderer.cpp @@ -6,6 +6,8 @@ #include "OvGame/Core/GameRenderer.h" +#include + #include #include #include @@ -51,17 +53,30 @@ void OvGame::Core::GameRenderer::RenderScene() { if (auto currentScene = m_context.sceneManager.GetCurrentScene()) { - UpdateLights(*currentScene); - - if (OvCore::ECS::Components::CCamera* mainCamera = m_context.renderer->FindMainCamera(*currentScene)) + if (OvCore::ECS::Components::CCamera* mainCameraComponent = m_context.renderer->FindMainCamera(*currentScene)) { - UpdateEngineUBO(*mainCamera); + if (mainCameraComponent->HasFrustumLightCulling()) + { + UpdateLightsInFrustum(*currentScene, mainCameraComponent->GetCamera().GetFrustum()); + } + else + { + UpdateLights(*currentScene); + } + + auto [winWidth, winHeight] = m_context.window->GetSize(); + const auto& cameraPosition = mainCameraComponent->owner.transform.GetWorldPosition(); + auto& camera = mainCameraComponent->GetCamera(); - m_context.renderer->Clear(mainCamera->GetCamera(), true, true, false); + camera.CacheMatrices(winWidth, winHeight, cameraPosition); + + UpdateEngineUBO(*mainCameraComponent); + + m_context.renderer->Clear(camera, true, true, false); uint8_t glState = m_context.renderer->FetchGLState(); m_context.renderer->ApplyStateMask(glState); - m_context.renderer->RenderScene(*currentScene, mainCamera->owner.transform.GetWorldPosition(), &m_emptyMaterial); + m_context.renderer->RenderScene(*currentScene, cameraPosition, camera, nullptr, &m_emptyMaterial); m_context.renderer->ApplyStateMask(glState); } else @@ -74,18 +89,24 @@ void OvGame::Core::GameRenderer::RenderScene() void OvGame::Core::GameRenderer::UpdateEngineUBO(OvCore::ECS::Components::CCamera& p_mainCamera) { - auto [winWidth, winHeight] = m_context.window->GetSize(); - size_t offset = sizeof(OvMaths::FMatrix4); // We skip the model matrix (Which is a special case, modified every draw calls) + auto& camera = p_mainCamera.GetCamera(); - m_context.engineUBO->SetSubData(OvMaths::FMatrix4::Transpose(p_mainCamera.GetViewMatrix()), std::ref(offset)); - m_context.engineUBO->SetSubData(OvMaths::FMatrix4::Transpose(p_mainCamera.GetProjectionMatrix(winWidth, winHeight)), std::ref(offset)); + m_context.engineUBO->SetSubData(OvMaths::FMatrix4::Transpose(camera.GetViewMatrix()), std::ref(offset)); + m_context.engineUBO->SetSubData(OvMaths::FMatrix4::Transpose(camera.GetProjectionMatrix()), std::ref(offset)); m_context.engineUBO->SetSubData(p_mainCamera.owner.transform.GetWorldPosition(), std::ref(offset)); } void OvGame::Core::GameRenderer::UpdateLights(OvCore::SceneSystem::Scene& p_scene) { - std::vector lightMatrices; - m_context.renderer->FindLightMatrices(p_scene, lightMatrices); + PROFILER_SPY("Light SSBO Update"); + auto lightMatrices = m_context.renderer->FindLightMatrices(p_scene); m_context.lightSSBO->SendBlocks(lightMatrices.data(), lightMatrices.size() * sizeof(FMatrix4)); -} \ No newline at end of file +} + +void OvGame::Core::GameRenderer::UpdateLightsInFrustum(OvCore::SceneSystem::Scene& p_scene, const OvRendering::Data::Frustum& p_frustum) +{ + PROFILER_SPY("Light SSBO Update (Frustum culled)"); + auto lightMatrices = m_context.renderer->FindLightMatricesInFrustum(p_scene, p_frustum); + m_context.lightSSBO->SendBlocks(lightMatrices.data(), lightMatrices.size() * sizeof(FMatrix4)); +} diff --git a/Sources/Overload/OvMaths/OvMaths.vcxproj b/Sources/Overload/OvMaths/OvMaths.vcxproj index 61dc2862..432c006c 100644 --- a/Sources/Overload/OvMaths/OvMaths.vcxproj +++ b/Sources/Overload/OvMaths/OvMaths.vcxproj @@ -14,19 +14,19 @@ 15.0 {4186CB5D-5E99-43FB-8562-42E8F24AC436} OvMaths - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvMaths/src/OvMaths/FMatrix3.cpp b/Sources/Overload/OvMaths/src/OvMaths/FMatrix3.cpp index 8bd0bc2c..00939e9f 100644 --- a/Sources/Overload/OvMaths/src/OvMaths/FMatrix3.cpp +++ b/Sources/Overload/OvMaths/src/OvMaths/FMatrix3.cpp @@ -5,6 +5,8 @@ */ #include +#include +#include #include "OvMaths/FMatrix3.h" diff --git a/Sources/Overload/OvMaths/src/OvMaths/FMatrix4.cpp b/Sources/Overload/OvMaths/src/OvMaths/FMatrix4.cpp index 0b50fcd8..1eaff630 100644 --- a/Sources/Overload/OvMaths/src/OvMaths/FMatrix4.cpp +++ b/Sources/Overload/OvMaths/src/OvMaths/FMatrix4.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "OvMaths/FMatrix4.h" #include "OvMaths/FVector3.h" diff --git a/Sources/Overload/OvMaths/src/OvMaths/FQuaternion.cpp b/Sources/Overload/OvMaths/src/OvMaths/FQuaternion.cpp index 8db74324..8a610907 100644 --- a/Sources/Overload/OvMaths/src/OvMaths/FQuaternion.cpp +++ b/Sources/Overload/OvMaths/src/OvMaths/FQuaternion.cpp @@ -5,6 +5,8 @@ */ #include +#include +#include #include "OvMaths/FQuaternion.h" diff --git a/Sources/Overload/OvMaths/src/OvMaths/FVector3.cpp b/Sources/Overload/OvMaths/src/OvMaths/FVector3.cpp index 049d5174..f10698df 100644 --- a/Sources/Overload/OvMaths/src/OvMaths/FVector3.cpp +++ b/Sources/Overload/OvMaths/src/OvMaths/FVector3.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "OvMaths/FVector3.h" @@ -140,7 +141,7 @@ OvMaths::FVector3 OvMaths::FVector3::Divide(const FVector3& p_left, float p_scal float OvMaths::FVector3::Length(const FVector3& p_target) { - return sqrtf(p_target.x * p_target.x + p_target.y * p_target.y + p_target.z * p_target.z); + return std::sqrt(p_target.x * p_target.x + p_target.y * p_target.y + p_target.z * p_target.z); } float OvMaths::FVector3::Dot(const FVector3& p_left, const FVector3& p_right) @@ -150,7 +151,7 @@ float OvMaths::FVector3::Dot(const FVector3& p_left, const FVector3& p_right) float OvMaths::FVector3::Distance(const FVector3 & p_left, const FVector3 & p_right) { - return sqrt + return std::sqrt ( (p_left.x - p_right.x) * (p_left.x - p_right.x) + (p_left.y - p_right.y) * (p_left.y - p_right.y) + diff --git a/Sources/Overload/OvPhysics/OvPhysics.vcxproj b/Sources/Overload/OvPhysics/OvPhysics.vcxproj index b94d6237..2928f4e7 100644 --- a/Sources/Overload/OvPhysics/OvPhysics.vcxproj +++ b/Sources/Overload/OvPhysics/OvPhysics.vcxproj @@ -14,19 +14,19 @@ 15.0 {9DE77393-CCFB-4085-986C-5D37E2B5B54B} OvPhysics - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvRendering/OvRendering.vcxproj b/Sources/Overload/OvRendering/OvRendering.vcxproj index 74bccfb6..ad54855d 100644 --- a/Sources/Overload/OvRendering/OvRendering.vcxproj +++ b/Sources/Overload/OvRendering/OvRendering.vcxproj @@ -14,19 +14,19 @@ 15.0 {677CDBA0-43A9-4A7C-9A7C-3C38C73D1D50} OvRendering - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte @@ -129,6 +129,7 @@ xcopy "$(SolutionDir)..\..\Dependencies\assimp\bin\*.dll" "$(SolutionDir)..\..\B + @@ -136,7 +137,9 @@ xcopy "$(SolutionDir)..\..\Dependencies\assimp\bin\*.dll" "$(SolutionDir)..\..\B + + @@ -155,6 +158,7 @@ xcopy "$(SolutionDir)..\..\Dependencies\assimp\bin\*.dll" "$(SolutionDir)..\..\B + @@ -163,12 +167,14 @@ xcopy "$(SolutionDir)..\..\Dependencies\assimp\bin\*.dll" "$(SolutionDir)..\..\B + + diff --git a/Sources/Overload/OvRendering/OvRendering.vcxproj.filters b/Sources/Overload/OvRendering/OvRendering.vcxproj.filters index 1837d7ce..72bdb6b0 100644 --- a/Sources/Overload/OvRendering/OvRendering.vcxproj.filters +++ b/Sources/Overload/OvRendering/OvRendering.vcxproj.filters @@ -117,6 +117,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -170,6 +182,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/Sources/Overload/OvRendering/include/OvRendering/Buffers/Framebuffer.h b/Sources/Overload/OvRendering/include/OvRendering/Buffers/Framebuffer.h new file mode 100644 index 00000000..c74e1f84 --- /dev/null +++ b/Sources/Overload/OvRendering/include/OvRendering/Buffers/Framebuffer.h @@ -0,0 +1,70 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +#include "OvRendering/Context/Driver.h" + +namespace OvRendering::Buffers +{ + /** + * Wraps OpenGL EBO + */ + class API_OVRENDERING Framebuffer + { + public: + /** + * Create the framebuffer + * @param p_width + * @param p_height + */ + Framebuffer(uint16_t p_width = 0, uint16_t p_height = 0); + + /** + * Destructor + */ + ~Framebuffer(); + + /** + * Bind the framebuffer + */ + void Bind(); + + /** + * Unbind the framebuffer + */ + void Unbind(); + + /** + * Defines a new size for the framebuffer + * @param p_width + * @param p_height + */ + void Resize(uint16_t p_width, uint16_t p_height); + + /** + * Returns the ID of the OpenGL framebuffer + */ + uint32_t GetID(); + + /** + * Returns the ID of the OpenGL render texture + */ + uint32_t GetTextureID(); + + /** + * Returns the ID of the OpenGL render buffer + */ + uint32_t GetRenderBufferID(); + + private: + uint32_t m_bufferID = 0; + uint32_t m_renderTexture = 0; + uint32_t m_depthStencilBuffer = 0; + }; +} \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/Buffers/UniformBuffer.h b/Sources/Overload/OvRendering/include/OvRendering/Buffers/UniformBuffer.h index 5391cf7a..8d0b2cf9 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Buffers/UniformBuffer.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Buffers/UniformBuffer.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "OvRendering/Context/Driver.h" #include "OvRendering/Buffers/EAccessSpecifier.h" diff --git a/Sources/Overload/OvRendering/include/OvRendering/Core/Renderer.h b/Sources/Overload/OvRendering/include/OvRendering/Core/Renderer.h index 880e2d05..85ccac68 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Core/Renderer.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Core/Renderer.h @@ -10,14 +10,15 @@ #include "OvRendering/Context/Driver.h" #include "OvRendering/LowRenderer/Camera.h" -#include "OvRendering/Resources/IMesh.h" #include "OvRendering/Resources/Shader.h" +#include "OvRendering/Resources/Model.h" #include "OvRendering/Settings/ERenderingCapability.h" #include "OvRendering/Settings/EPrimitiveMode.h" #include "OvRendering/Settings/ERasterizationMode.h" #include "OvRendering/Settings/EComparaisonAlgorithm.h" #include "OvRendering/Settings/EOperation.h" #include "OvRendering/Settings/ECullFace.h" +#include "OvRendering/Settings/ECullingOptions.h" namespace OvRendering::Core { @@ -247,6 +248,23 @@ namespace OvRendering::Core */ void Draw(Resources::IMesh& p_mesh, Settings::EPrimitiveMode p_primitiveMode = Settings::EPrimitiveMode::TRIANGLES, uint32_t p_instances = 1); + /** + * Returns the list of meshes from a model that should be rendered + * @param p_model + * @param p_modelBoundingSphere + * @param p_modelTransform + * @param p_frustum + * @param p_cullingOptions + */ + std::vector> GetMeshesInFrustum + ( + const OvRendering::Resources::Model& p_model, + const OvRendering::Geometry::BoundingSphere& p_modelBoundingSphere, + const OvMaths::FTransform& p_modelTransform, + const OvRendering::Data::Frustum& p_frustum, + OvRendering::Settings::ECullingOptions p_cullingOptions + ); + /** * Fetch and returns the actual OpenGL state */ diff --git a/Sources/Overload/OvRendering/include/OvRendering/Data/Frustum.h b/Sources/Overload/OvRendering/include/OvRendering/Data/Frustum.h new file mode 100644 index 00000000..5ae3d70b --- /dev/null +++ b/Sources/Overload/OvRendering/include/OvRendering/Data/Frustum.h @@ -0,0 +1,77 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +#include +#include + +#include "OvRendering/API/Export.h" +#include "OvRendering/Geometry/BoundingSphere.h" + +namespace OvRendering::Data +{ + /** + * Mathematic representation of a 3D frustum + */ + class API_OVRENDERING Frustum + { + public: + /** + * Update frustum values + * @param p_viewProjection + */ + void CalculateFrustum(const OvMaths::FMatrix4& p_viewProjection); + + /** + * Returns true if the given point is in frustum + * @param p_x + * @param p_y + * @param p_z + */ + bool PointInFrustum(float p_x, float p_y, float p_z) const; + + /** + * Returns true if the given sphere is in frustum + * @param p_x + * @param p_y + * @param p_z + * @param p_radius + */ + bool SphereInFrustum(float p_x, float p_y, float p_z, float p_radius) const; + + /** + * Returns true if the given cube is in frustum + * @param p_x + * @param p_y + * @param p_z + * @param p_size + */ + bool CubeInFrustum(float p_x, float p_y, float p_z, float p_size) const; + + /** + * Returns true if the given bouding sphere is in frustum + * @param p_boundingSphere + * @param p_transform + */ + bool BoundingSphereInFrustum(const OvRendering::Geometry::BoundingSphere& p_boundingSphere, const OvMaths::FTransform& p_transform) const; + + /** + * Returns the near plane + */ + std::array GetNearPlane() const; + + /** + * Returns the far plane + */ + std::array GetFarPlane() const; + + private: + float m_frustum[6][4]; + }; +} \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/Entities/Light.h b/Sources/Overload/OvRendering/include/OvRendering/Entities/Light.h index 998b19f7..9c40e00f 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Entities/Light.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Entities/Light.h @@ -36,6 +36,16 @@ namespace OvRendering::Entities */ OvMaths::FMatrix4 GenerateMatrix() const; + /** + * Calculate the light effect range from the quadratic falloff equation + */ + float GetEffectRange() const; + + /** + * Returns the light transform + */ + const OvMaths::FTransform& GetTransform() const; + OvMaths::FVector3 color = { 1.f, 1.f, 1.f }; float intensity = 1.f; float constant = 0.0f; diff --git a/Sources/Overload/OvRendering/include/OvRendering/Geometry/BoundingSphere.h b/Sources/Overload/OvRendering/include/OvRendering/Geometry/BoundingSphere.h new file mode 100644 index 00000000..a23af145 --- /dev/null +++ b/Sources/Overload/OvRendering/include/OvRendering/Geometry/BoundingSphere.h @@ -0,0 +1,21 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +namespace OvRendering::Geometry +{ + /** + * Data structure that defines a bounding sphere (Position + radius) + */ + struct BoundingSphere + { + OvMaths::FVector3 position; + float radius; + }; +} \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/LowRenderer/Camera.h b/Sources/Overload/OvRendering/include/OvRendering/LowRenderer/Camera.h index 3a5c620d..231e7da1 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/LowRenderer/Camera.h +++ b/Sources/Overload/OvRendering/include/OvRendering/LowRenderer/Camera.h @@ -11,6 +11,7 @@ #include #include "OvRendering/API/Export.h" +#include "OvRendering/Data/Frustum.h" namespace OvRendering::LowRenderer { @@ -26,17 +27,33 @@ namespace OvRendering::LowRenderer Camera(); /** - * Returns the projection matrix + * Cache the projection, view and frustum matrices + * @param p_windowWidth + * @param p_windowHeight + * @param p_position + */ + void CacheMatrices(uint16_t p_windowWidth, uint16_t p_windowHeight, const OvMaths::FVector3& p_position); + + /** + * Calculate and cache the result projection matrix * @param p_windowWidth * @param p_windowHeight */ - OvMaths::FMatrix4 GetProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) const; + void CacheProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight); /** - * Returns the view matrix + * Calculate and cache the result view matrix * @param p_position */ - OvMaths::FMatrix4 GetViewMatrix(const OvMaths::FVector3& p_position) const; + void CacheViewMatrix(const OvMaths::FVector3& p_position); + + /** + * Calculate and cache the result frustum. + * This method should be called after projection and view matrices are cached. + * @param p_view + * @param p_projection + */ + void CacheFrustum(const OvMaths::FMatrix4& p_view, const OvMaths::FMatrix4& p_projection); /** * Returns the forward vector of the camera @@ -88,6 +105,31 @@ namespace OvRendering::LowRenderer */ const OvMaths::FVector3& GetClearColor() const; + /** + * Returns the cached projection matrix + */ + const OvMaths::FMatrix4& GetProjectionMatrix() const; + + /** + * Returns the cached view matrix + */ + const OvMaths::FMatrix4& GetViewMatrix() const; + + /** + * Retursn the cached frustum + */ + const OvRendering::Data::Frustum& GetFrustum() const; + + /** + * Returns true if the frustum culling for geometry is enabled + */ + bool HasFrustumGeometryCulling() const; + + /** + * Returns true if the frustum culling for lights is enabled + */ + bool HasFrustumLightCulling() const; + /** * Sets the yaw of the camera to the given value * @param p_value @@ -130,6 +172,18 @@ namespace OvRendering::LowRenderer */ void SetClearColor(const OvMaths::FVector3& p_clearColor); + /** + * Defines if the camera should apply frustum culling to geometry while rendering + * @param p_enable + */ + void SetFrustumGeometryCulling(bool p_enable); + + /** + * Defines if the camera should apply frustum culling to lights while rendering + * @param p_enable + */ + void SetFrustumLightCulling(bool p_enable); + /** * Sets the rotation to the camera with a quaternion * @param p_rotation @@ -137,9 +191,15 @@ namespace OvRendering::LowRenderer void SetRotation(const OvMaths::FQuaternion& p_rotation); private: + OvMaths::FMatrix4 CalculateProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) const; + OvMaths::FMatrix4 CalculateViewMatrix(const OvMaths::FVector3& p_position) const; void UpdateCameraVectors(); private: + OvRendering::Data::Frustum m_frustum; + OvMaths::FMatrix4 m_viewMatrix; + OvMaths::FMatrix4 m_projectionMatrix; + OvMaths::FVector3 m_forward; OvMaths::FVector3 m_up; OvMaths::FVector3 m_right; @@ -153,5 +213,8 @@ namespace OvRendering::LowRenderer float m_far; OvMaths::FVector3 m_clearColor; + + bool m_frustumGeometryCulling; + bool m_frustumLightCulling; }; } \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Mesh.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Mesh.h index cd0cb94c..df677dbc 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Mesh.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Mesh.h @@ -13,6 +13,7 @@ #include "OvRendering/Buffers/IndexBuffer.h" #include "OvRendering/Resources/IMesh.h" #include "OvRendering/Geometry/Vertex.h" +#include "OvRendering/Geometry/BoundingSphere.h" namespace OvRendering::Resources { @@ -55,8 +56,14 @@ namespace OvRendering::Resources */ uint32_t GetMaterialIndex() const; + /** + * Returns the bounding sphere of the mesh + */ + const OvRendering::Geometry::BoundingSphere& GetBoundingSphere() const; + private: void CreateBuffers(const std::vector& p_vertices, const std::vector& p_indices); + void ComputeBoundingSphere(const std::vector& p_vertices); private: const uint32_t m_vertexCount; @@ -66,5 +73,7 @@ namespace OvRendering::Resources Buffers::VertexArray m_vertexArray; std::unique_ptr> m_vertexBuffer; std::unique_ptr m_indexBuffer; + + Geometry::BoundingSphere m_boundingSphere; }; } \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Model.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Model.h index 6bd288b6..717bb2d5 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Model.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Model.h @@ -32,15 +32,24 @@ namespace OvRendering::Resources */ const std::vector& GetMaterialNames() const; + /** + * Returns the bounding sphere of the model + */ + const OvRendering::Geometry::BoundingSphere& GetBoundingSphere() const; + private: Model(const std::string& p_path); ~Model(); + void ComputeBoundingSphere(); + public: const std::string path; private: std::vector m_meshes; std::vector m_materialNames; + + Geometry::BoundingSphere m_boundingSphere; }; } \ No newline at end of file diff --git a/Sources/Overload/OvRendering/include/OvRendering/Settings/ECullingOptions.h b/Sources/Overload/OvRendering/include/OvRendering/Settings/ECullingOptions.h new file mode 100644 index 00000000..3f7dda92 --- /dev/null +++ b/Sources/Overload/OvRendering/include/OvRendering/Settings/ECullingOptions.h @@ -0,0 +1,31 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include "OvRendering/API/Export.h" + +namespace OvRendering::Settings +{ + /** + * Represents some culling options + */ + enum class ECullingOptions + { + NONE = 0x0, + FRUSTUM_PER_MODEL = 0x1, + FRUSTUM_PER_MESH = 0x2 + }; + + inline ECullingOptions operator~ (ECullingOptions a) { return (ECullingOptions)~(int)a; } + inline ECullingOptions operator| (ECullingOptions a, ECullingOptions b) { return (ECullingOptions)((int)a | (int)b); } + inline ECullingOptions operator& (ECullingOptions a, ECullingOptions b) { return (ECullingOptions)((int)a & (int)b); } + inline ECullingOptions operator^ (ECullingOptions a, ECullingOptions b) { return (ECullingOptions)((int)a ^ (int)b); } + inline ECullingOptions& operator|= (ECullingOptions& a, ECullingOptions b) { return (ECullingOptions&)((int&)a |= (int)b); } + inline ECullingOptions& operator&= (ECullingOptions& a, ECullingOptions b) { return (ECullingOptions&)((int&)a &= (int)b); } + inline ECullingOptions& operator^= (ECullingOptions& a, ECullingOptions b) { return (ECullingOptions&)((int&)a ^= (int)b); } + inline bool IsFlagSet(ECullingOptions p_flag, ECullingOptions p_mask) { return (int)p_flag & (int)p_mask; } +} \ No newline at end of file diff --git a/Sources/Overload/OvRendering/src/OvRendering/Buffers/Framebuffer.cpp b/Sources/Overload/OvRendering/src/OvRendering/Buffers/Framebuffer.cpp new file mode 100644 index 00000000..d0b3eac6 --- /dev/null +++ b/Sources/Overload/OvRendering/src/OvRendering/Buffers/Framebuffer.cpp @@ -0,0 +1,82 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#include + +#include "OvRendering/Buffers/Framebuffer.h" + +OvRendering::Buffers::Framebuffer::Framebuffer(uint16_t p_width, uint16_t p_height) +{ + /* Generate OpenGL objects */ + glGenFramebuffers(1, &m_bufferID); + glGenTextures(1, &m_renderTexture); + glGenRenderbuffers(1, &m_depthStencilBuffer); + + /* Setup texture */ + glBindTexture(GL_TEXTURE_2D, m_renderTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Setup framebuffer */ + Bind(); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_renderTexture, 0); + Unbind(); + + Resize(p_width, p_height); +} + +OvRendering::Buffers::Framebuffer::~Framebuffer() +{ + /* Destroy OpenGL objects */ + glDeleteBuffers(1, &m_bufferID); + glDeleteTextures(1, &m_renderTexture); + glGenRenderbuffers(1, &m_depthStencilBuffer); +} + +void OvRendering::Buffers::Framebuffer::Bind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, m_bufferID); +} + +void OvRendering::Buffers::Framebuffer::Unbind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void OvRendering::Buffers::Framebuffer::Resize(uint16_t p_width, uint16_t p_height) +{ + /* Resize texture */ + glBindTexture(GL_TEXTURE_2D, m_renderTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, p_width, p_height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Setup depth-stencil buffer (24 + 8 bits) */ + glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, p_width, p_height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + /* Attach depth and stencil buffer to the framebuffer */ + Bind(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + Unbind(); +} + +uint32_t OvRendering::Buffers::Framebuffer::GetID() +{ + return m_bufferID; +} + +uint32_t OvRendering::Buffers::Framebuffer::GetTextureID() +{ + return m_renderTexture; +} + +uint32_t OvRendering::Buffers::Framebuffer::GetRenderBufferID() +{ + return m_depthStencilBuffer; +} diff --git a/Sources/Overload/OvRendering/src/OvRendering/Core/Renderer.cpp b/Sources/Overload/OvRendering/src/OvRendering/Core/Renderer.cpp index 7f80dd54..a12ee632 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Core/Renderer.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Core/Renderer.cpp @@ -222,6 +222,40 @@ void OvRendering::Core::Renderer::Draw(Resources::IMesh& p_mesh, Settings::EPrim } } +std::vector> OvRendering::Core::Renderer::GetMeshesInFrustum +( + const OvRendering::Resources::Model& p_model, + const OvRendering::Geometry::BoundingSphere& p_modelBoundingSphere, + const OvMaths::FTransform& p_modelTransform, + const OvRendering::Data::Frustum& p_frustum, + OvRendering::Settings::ECullingOptions p_cullingOptions +) +{ + const bool frustumPerModel = OvRendering::Settings::IsFlagSet(Settings::ECullingOptions::FRUSTUM_PER_MODEL, p_cullingOptions); + + if (!frustumPerModel || p_frustum.BoundingSphereInFrustum(p_modelBoundingSphere, p_modelTransform)) + { + std::vector> result; + + const bool frustumPerMesh = OvRendering::Settings::IsFlagSet(Settings::ECullingOptions::FRUSTUM_PER_MESH, p_cullingOptions); + + const auto& meshes = p_model.GetMeshes(); + + for (auto mesh : meshes) + { + // Do not check if the mesh is in frustum if the model has only one mesh, because model and mesh bounding sphere are equals + if (meshes.size() == 1 || !frustumPerMesh || p_frustum.BoundingSphereInFrustum(mesh->GetBoundingSphere(), p_modelTransform)) + { + result.emplace_back(*mesh); + } + } + + return result; + } + + return {}; +} + uint8_t OvRendering::Core::Renderer::FetchGLState() { using namespace OvRendering::Settings; diff --git a/Sources/Overload/OvRendering/src/OvRendering/Data/Frustum.cpp b/Sources/Overload/OvRendering/src/OvRendering/Data/Frustum.cpp new file mode 100644 index 00000000..3830c3e1 --- /dev/null +++ b/Sources/Overload/OvRendering/src/OvRendering/Data/Frustum.cpp @@ -0,0 +1,277 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#include +#include + +#include "OvRendering/Data/Frustum.h" + +// We create an enum of the sides so we don't have to call each side 0 or 1. +// This way it makes it more understandable and readable when dealing with frustum sides. +enum FrustumSide +{ + RIGHT = 0, // The RIGHT side of the frustum + LEFT = 1, // The LEFT side of the frustum + BOTTOM = 2, // The BOTTOM side of the frustum + TOP = 3, // The TOP side of the frustum + BACK = 4, // The BACK side of the frustum + FRONT = 5 // The FRONT side of the frustum +}; + +// Like above, instead of saying a number for the ABC and D of the plane, we +// want to be more descriptive. +enum PlaneData +{ + A = 0, // The X value of the plane's normal + B = 1, // The Y value of the plane's normal + C = 2, // The Z value of the plane's normal + D = 3 // The distance the plane is from the origin +}; + +///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +///// +///// This normalizes a plane (A side) from a given frustum. +///// +///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +void NormalizePlane(float frustum[6][4], int side) +{ + // Here we calculate the magnitude of the normal to the plane (point A B C) + // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). + // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) + float magnitude = (float)sqrt(frustum[side][A] * frustum[side][A] + + frustum[side][B] * frustum[side][B] + + frustum[side][C] * frustum[side][C]); + + // Then we divide the plane's values by it's magnitude. + // This makes it easier to work with. + frustum[side][A] /= magnitude; + frustum[side][B] /= magnitude; + frustum[side][C] /= magnitude; + frustum[side][D] /= magnitude; +} + + +///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +///// +///// This extracts our frustum from the projection and modelview matrix. +///// +///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +void OvRendering::Data::Frustum::CalculateFrustum(const OvMaths::FMatrix4& p_viewProjection) +{ + auto columnMajorViewProjection = OvMaths::FMatrix4::Transpose(p_viewProjection); + float const* clip = columnMajorViewProjection.data; + + // Now we actually want to get the sides of the frustum. To do this we take + // the clipping planes we received above and extract the sides from them. + + // This will extract the RIGHT side of the frustum + m_frustum[RIGHT][A] = clip[3] - clip[0]; + m_frustum[RIGHT][B] = clip[7] - clip[4]; + m_frustum[RIGHT][C] = clip[11] - clip[8]; + m_frustum[RIGHT][D] = clip[15] - clip[12]; + + // Now that we have a normal (A,B,C) and a distance (D) to the plane, + // we want to normalize that normal and distance. + + // Normalize the RIGHT side + NormalizePlane(m_frustum, RIGHT); + + // This will extract the LEFT side of the frustum + m_frustum[LEFT][A] = clip[3] + clip[0]; + m_frustum[LEFT][B] = clip[7] + clip[4]; + m_frustum[LEFT][C] = clip[11] + clip[8]; + m_frustum[LEFT][D] = clip[15] + clip[12]; + + // Normalize the LEFT side + NormalizePlane(m_frustum, LEFT); + + // This will extract the BOTTOM side of the frustum + m_frustum[BOTTOM][A] = clip[3] + clip[1]; + m_frustum[BOTTOM][B] = clip[7] + clip[5]; + m_frustum[BOTTOM][C] = clip[11] + clip[9]; + m_frustum[BOTTOM][D] = clip[15] + clip[13]; + + // Normalize the BOTTOM side + NormalizePlane(m_frustum, BOTTOM); + + // This will extract the TOP side of the frustum + m_frustum[TOP][A] = clip[3] - clip[1]; + m_frustum[TOP][B] = clip[7] - clip[5]; + m_frustum[TOP][C] = clip[11] - clip[9]; + m_frustum[TOP][D] = clip[15] - clip[13]; + + // Normalize the TOP side + NormalizePlane(m_frustum, TOP); + + // This will extract the BACK side of the frustum + m_frustum[BACK][A] = clip[3] - clip[2]; + m_frustum[BACK][B] = clip[7] - clip[6]; + m_frustum[BACK][C] = clip[11] - clip[10]; + m_frustum[BACK][D] = clip[15] - clip[14]; + + // Normalize the BACK side + NormalizePlane(m_frustum, BACK); + + // This will extract the FRONT side of the frustum + m_frustum[FRONT][A] = clip[3] + clip[2]; + m_frustum[FRONT][B] = clip[7] + clip[6]; + m_frustum[FRONT][C] = clip[11] + clip[10]; + m_frustum[FRONT][D] = clip[15] + clip[14]; + + // Normalize the FRONT side + NormalizePlane(m_frustum, FRONT); +} + +// The code below will allow us to make checks within the frustum. For example, +// if we want to see if a point, a sphere, or a cube lies inside of the frustum. +// Because all of our planes point INWARDS (The normals are all pointing inside the frustum) +// we then can assume that if a point is in FRONT of all of the planes, it's inside. + +///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +///// +///// This determines if a point is inside of the frustum +///// +///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +bool OvRendering::Data::Frustum::PointInFrustum(float x, float y, float z) const +{ + // If you remember the plane equation (A*x + B*y + C*z + D = 0), then the rest + // of this code should be quite obvious and easy to figure out yourself. + // In case don't know the plane equation, it might be a good idea to look + // at our Plane Collision tutorial at www.GameTutorials.com in OpenGL Tutorials. + // I will briefly go over it here. (A,B,C) is the (X,Y,Z) of the normal to the plane. + // They are the same thing... but just called ABC because you don't want to say: + // (x*x + y*y + z*z + d = 0). That would be wrong, so they substitute them. + // the (x, y, z) in the equation is the point that you are testing. The D is + // The distance the plane is from the origin. The equation ends with "= 0" because + // that is true when the point (x, y, z) is ON the plane. When the point is NOT on + // the plane, it is either a negative number (the point is behind the plane) or a + // positive number (the point is in front of the plane). We want to check if the point + // is in front of the plane, so all we have to do is go through each point and make + // sure the plane equation goes out to a positive number on each side of the frustum. + // The result (be it positive or negative) is the distance the point is front the plane. + + // Go through all the sides of the frustum + for (int i = 0; i < 6; i++) + { + // Calculate the plane equation and check if the point is behind a side of the frustum + if (m_frustum[i][A] * x + m_frustum[i][B] * y + m_frustum[i][C] * z + m_frustum[i][D] <= 0) + { + // The point was behind a side, so it ISN'T in the frustum + return false; + } + } + + // The point was inside of the frustum (In front of ALL the sides of the frustum) + return true; +} + + +///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +///// +///// This determines if a sphere is inside of our frustum by it's center and radius. +///// +///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +bool OvRendering::Data::Frustum::SphereInFrustum(float x, float y, float z, float radius) const +{ + // Now this function is almost identical to the PointInFrustum(), except we + // now have to deal with a radius around the point. The point is the center of + // the radius. So, the point might be outside of the frustum, but it doesn't + // mean that the rest of the sphere is. It could be half and half. So instead of + // checking if it's less than 0, we need to add on the radius to that. Say the + // equation produced -2, which means the center of the sphere is the distance of + // 2 behind the plane. Well, what if the radius was 5? The sphere is still inside, + // so we would say, if(-2 < -5) then we are outside. In that case it's false, + // so we are inside of the frustum, but a distance of 3. This is reflected below. + + // Go through all the sides of the frustum + for (int i = 0; i < 6; i++) + { + // If the center of the sphere is farther away from the plane than the radius + if (m_frustum[i][A] * x + m_frustum[i][B] * y + m_frustum[i][C] * z + m_frustum[i][D] <= -radius) + { + // The distance was greater than the radius so the sphere is outside of the frustum + return false; + } + } + + // The sphere was inside of the frustum! + return true; +} + + +///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +///// +///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length +///// +///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +bool OvRendering::Data::Frustum::CubeInFrustum(float x, float y, float z, float size) const +{ + // This test is a bit more work, but not too much more complicated. + // Basically, what is going on is, that we are given the center of the cube, + // and half the length. Think of it like a radius. Then we checking each point + // in the cube and seeing if it is inside the frustum. If a point is found in front + // of a side, then we skip to the next side. If we get to a plane that does NOT have + // a point in front of it, then it will return false. + + // *Note* - This will sometimes say that a cube is inside the frustum when it isn't. + // This happens when all the corners of the bounding box are not behind any one plane. + // This is rare and shouldn't effect the overall rendering speed. + + for (int i = 0; i < 6; i++) + { + if (m_frustum[i][A] * (x - size) + m_frustum[i][B] * (y - size) + m_frustum[i][C] * (z - size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x + size) + m_frustum[i][B] * (y - size) + m_frustum[i][C] * (z - size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x - size) + m_frustum[i][B] * (y + size) + m_frustum[i][C] * (z - size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x + size) + m_frustum[i][B] * (y + size) + m_frustum[i][C] * (z - size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x - size) + m_frustum[i][B] * (y - size) + m_frustum[i][C] * (z + size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x + size) + m_frustum[i][B] * (y - size) + m_frustum[i][C] * (z + size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x - size) + m_frustum[i][B] * (y + size) + m_frustum[i][C] * (z + size) + m_frustum[i][D] > 0) + continue; + if (m_frustum[i][A] * (x + size) + m_frustum[i][B] * (y + size) + m_frustum[i][C] * (z + size) + m_frustum[i][D] > 0) + continue; + + // If we get here, it isn't in the frustum + return false; + } + + return true; +} + +bool OvRendering::Data::Frustum::BoundingSphereInFrustum(const OvRendering::Geometry::BoundingSphere& p_boundingSphere, const OvMaths::FTransform& p_transform) const +{ + const auto& position = p_transform.GetWorldPosition(); + const auto& rotation = p_transform.GetWorldRotation(); + const auto& scale = p_transform.GetWorldScale(); + + float maxScale = std::max(std::max(std::max(scale.x, scale.y), scale.z), 0.0f); + float scaledRadius = p_boundingSphere.radius * maxScale; + auto sphereOffset = OvMaths::FQuaternion::RotatePoint(p_boundingSphere.position, rotation) * maxScale; + + OvMaths::FVector3 worldCenter = position + sphereOffset; + + return SphereInFrustum(worldCenter.x, worldCenter.y, worldCenter.z, scaledRadius); +} + +std::array OvRendering::Data::Frustum::GetNearPlane() const +{ + return { m_frustum[FRONT][0], m_frustum[FRONT][1], m_frustum[FRONT][2], m_frustum[FRONT][3] }; +} + +std::array OvRendering::Data::Frustum::GetFarPlane() const +{ + return { m_frustum[BACK][0], m_frustum[BACK][1], m_frustum[BACK][2], m_frustum[BACK][3] }; +} diff --git a/Sources/Overload/OvRendering/src/OvRendering/Entities/Light.cpp b/Sources/Overload/OvRendering/src/OvRendering/Entities/Light.cpp index 48acc2c2..5e6943e6 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Entities/Light.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Entities/Light.cpp @@ -48,3 +48,76 @@ OvMaths::FMatrix4 OvRendering::Entities::Light::GenerateMatrix() const return result; } + +float CalculateLuminosity(float p_constant, float p_linear, float p_quadratic, float p_intensity, float p_distance) +{ + auto attenuation = (p_constant + p_linear * p_distance + p_quadratic * (p_distance * p_distance)); + return (1.0f / attenuation) * p_intensity; +} + +float CalculatePointLightRadius(float p_constant, float p_linear, float p_quadratic, float p_intensity) +{ + constexpr float threshold = 1 / 255.0f; + constexpr float step = 1.0f; + + float distance = 0.0f; + + #define TRY_GREATER(value)\ + else if (CalculateLuminosity(p_constant, p_linear, p_quadratic, p_intensity, value) > threshold)\ + {\ + distance = value;\ + } + + #define TRY_LESS(value, newValue)\ + else if (CalculateLuminosity(p_constant, p_linear, p_quadratic, p_intensity, value) < threshold)\ + {\ + distance = newValue;\ + } + + // Prevents infinite while true. If a light has a bigger radius than 10000 we ignore it and make it infinite + if (CalculateLuminosity(p_constant, p_linear, p_quadratic, p_intensity, 1000.0f) > threshold) + { + return std::numeric_limits::infinity(); + } + TRY_LESS(20.0f, 0.0f) + TRY_GREATER(750.0f) + TRY_LESS(50.0f, 20.0f + step) + TRY_LESS(100.0f, 50.0f + step) + TRY_GREATER(500.0f) + TRY_GREATER(250.0f) + + while (true) + { + if (CalculateLuminosity(p_constant, p_linear, p_quadratic, p_intensity, distance) < threshold) // If the light has a very low luminosity for the given distance, we consider the current distance as the light radius + { + return distance; + } + else + { + distance += step; + } + } +} + +float CalculateAmbientBoxLightRadius(const OvMaths::FVector3& p_position, const OvMaths::FVector3& p_size) +{ + return OvMaths::FVector3::Distance(p_position, p_position + p_size); +} + +float OvRendering::Entities::Light::GetEffectRange() const +{ + switch (static_cast(static_cast(type))) + { + case Type::POINT: + case Type::SPOT: return CalculatePointLightRadius(constant, linear, quadratic, intensity); + case Type::AMBIENT_BOX: return CalculateAmbientBoxLightRadius(m_transform.GetWorldPosition(), { constant, linear, quadratic }); + case Type::AMBIENT_SPHERE: return constant; + } + + return std::numeric_limits::infinity(); +} + +const OvMaths::FTransform& OvRendering::Entities::Light::GetTransform() const +{ + return m_transform; +} diff --git a/Sources/Overload/OvRendering/src/OvRendering/LowRenderer/Camera.cpp b/Sources/Overload/OvRendering/src/OvRendering/LowRenderer/Camera.cpp index 57239b1d..66c05a52 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/LowRenderer/Camera.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/LowRenderer/Camera.cpp @@ -15,25 +15,34 @@ OvRendering::LowRenderer::Camera::Camera() : m_roll(0.f), m_fov(45.f), m_near(0.1f), - m_far(1000.f), - m_clearColor(0.f, 0.f, 0.f) + m_far(100.f), + m_clearColor(0.f, 0.f, 0.f), + m_frustumGeometryCulling(false), + m_frustumLightCulling(false) { UpdateCameraVectors(); } -OvMaths::FMatrix4 OvRendering::LowRenderer::Camera::GetProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) const +void OvRendering::LowRenderer::Camera::CacheMatrices(uint16_t p_windowWidth, uint16_t p_windowHeight, const OvMaths::FVector3& p_position) { - return OvMaths::FMatrix4::CreatePerspective(m_fov, static_cast(p_windowWidth) / static_cast(p_windowHeight), m_near, m_far); + CacheProjectionMatrix(p_windowWidth, p_windowHeight); + CacheViewMatrix(p_position); + CacheFrustum(m_viewMatrix, m_projectionMatrix); } -OvMaths::FMatrix4 OvRendering::LowRenderer::Camera::GetViewMatrix(const OvMaths::FVector3& p_position) const +void OvRendering::LowRenderer::Camera::CacheProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) { - return OvMaths::FMatrix4::CreateView - ( - p_position.x, p_position.y, p_position.z, // Position - p_position.x + m_forward.x, p_position.y + m_forward.y, p_position.z + m_forward.z, // LookAt (Position + Forward) - m_up.x, m_up.y, m_up.z // Up Vector - ); + m_projectionMatrix = CalculateProjectionMatrix(p_windowWidth, p_windowHeight); +} + +void OvRendering::LowRenderer::Camera::CacheViewMatrix(const OvMaths::FVector3& p_position) +{ + m_viewMatrix = CalculateViewMatrix(p_position); +} + +void OvRendering::LowRenderer::Camera::CacheFrustum(const OvMaths::FMatrix4& p_view, const OvMaths::FMatrix4& p_projection) +{ + m_frustum.CalculateFrustum(p_projection * p_view); } const OvMaths::FVector3 & OvRendering::LowRenderer::Camera::GetForward() const @@ -86,6 +95,31 @@ const OvMaths::FVector3 & OvRendering::LowRenderer::Camera::GetClearColor() cons return m_clearColor; } +const OvMaths::FMatrix4& OvRendering::LowRenderer::Camera::GetProjectionMatrix() const +{ + return m_projectionMatrix; +} + +const OvMaths::FMatrix4& OvRendering::LowRenderer::Camera::GetViewMatrix() const +{ + return m_viewMatrix; +} + +const OvRendering::Data::Frustum& OvRendering::LowRenderer::Camera::GetFrustum() const +{ + return m_frustum; +} + +bool OvRendering::LowRenderer::Camera::HasFrustumGeometryCulling() const +{ + return m_frustumGeometryCulling; +} + +bool OvRendering::LowRenderer::Camera::HasFrustumLightCulling() const +{ + return m_frustumLightCulling; +} + void OvRendering::LowRenderer::Camera::SetYaw(float p_value) { m_yaw = p_value; @@ -127,6 +161,16 @@ void OvRendering::LowRenderer::Camera::SetClearColor(const OvMaths::FVector3 & p m_clearColor = p_clearColor; } +void OvRendering::LowRenderer::Camera::SetFrustumGeometryCulling(bool p_enable) +{ + m_frustumGeometryCulling = p_enable; +} + +void OvRendering::LowRenderer::Camera::SetFrustumLightCulling(bool p_enable) +{ + m_frustumLightCulling = p_enable; +} + void OvRendering::LowRenderer::Camera::SetRotation(const OvMaths::FQuaternion & p_rotation) { m_forward = p_rotation * OvMaths::FVector3(0.f, 0.f, 1.f); @@ -134,6 +178,21 @@ void OvRendering::LowRenderer::Camera::SetRotation(const OvMaths::FQuaternion & m_up = p_rotation * OvMaths::FVector3(0.f, 1.f, 0.f); } +OvMaths::FMatrix4 OvRendering::LowRenderer::Camera::CalculateProjectionMatrix(uint16_t p_windowWidth, uint16_t p_windowHeight) const +{ + return OvMaths::FMatrix4::CreatePerspective(m_fov, static_cast(p_windowWidth) / static_cast(p_windowHeight), m_near, m_far); +} + +OvMaths::FMatrix4 OvRendering::LowRenderer::Camera::CalculateViewMatrix(const OvMaths::FVector3& p_position) const +{ + return OvMaths::FMatrix4::CreateView + ( + p_position.x, p_position.y, p_position.z, // Position + p_position.x + m_forward.x, p_position.y + m_forward.y, p_position.z + m_forward.z, // LookAt (Position + Forward) + m_up.x, m_up.y, m_up.z // Up Vector + ); +} + void OvRendering::LowRenderer::Camera::UpdateCameraVectors() { m_forward.x = std::cos(m_yaw * 0.0174f) * std::cos(m_pitch * 0.0174f); @@ -143,4 +202,4 @@ void OvRendering::LowRenderer::Camera::UpdateCameraVectors() m_forward = OvMaths::FVector3::Normalize(m_forward); m_right = OvMaths::FVector3::Normalize(OvMaths::FVector3::Cross(m_forward, OvMaths::FVector3(0.0f, 1.0f, 0.0f))); m_up = OvMaths::FVector3::Normalize(OvMaths::FVector3::Cross(m_right, m_forward)); -} +} \ No newline at end of file diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ModelLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ModelLoader.cpp index db6dc942..f1f13ccb 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ModelLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ModelLoader.cpp @@ -13,7 +13,10 @@ OvRendering::Resources::Model* OvRendering::Resources::Loaders::ModelLoader::Cre Model* result = new Model(p_filepath); if (__ASSIMP.LoadModel(p_filepath, result->m_meshes, result->m_materialNames, p_parserFlags)) + { + result->ComputeBoundingSphere(); return result; + } delete result; diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Mesh.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Mesh.cpp index 6bf60eaa..b39cbe99 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Mesh.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Mesh.cpp @@ -4,6 +4,8 @@ * @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. */ +#include + #include "OvRendering/Resources/Mesh.h" OvRendering::Resources::Mesh::Mesh(const std::vector& p_vertices, const std::vector& p_indices, uint32_t p_materialIndex) : @@ -12,6 +14,7 @@ OvRendering::Resources::Mesh::Mesh(const std::vector& p_vertic m_materialIndex(p_materialIndex) { CreateBuffers(p_vertices, p_indices); + ComputeBoundingSphere(p_vertices); } void OvRendering::Resources::Mesh::Bind() @@ -39,6 +42,11 @@ uint32_t OvRendering::Resources::Mesh::GetMaterialIndex() const return m_materialIndex; } +const OvRendering::Geometry::BoundingSphere& OvRendering::Resources::Mesh::GetBoundingSphere() const +{ + return m_boundingSphere; +} + void OvRendering::Resources::Mesh::CreateBuffers(const std::vector& p_vertices, const std::vector& p_indices) { std::vector vertexData; @@ -78,3 +86,39 @@ void OvRendering::Resources::Mesh::CreateBuffers(const std::vector& p_vertices) +{ + m_boundingSphere.position = OvMaths::FVector3::Zero; + m_boundingSphere.radius = 0.0f; + + if (!p_vertices.empty()) + { + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float minZ = std::numeric_limits::max(); + + float maxX = std::numeric_limits::min(); + float maxY = std::numeric_limits::min(); + float maxZ = std::numeric_limits::min(); + + for (const auto& vertex : p_vertices) + { + minX = std::min(minX, vertex.position[0]); + minY = std::min(minY, vertex.position[1]); + minZ = std::min(minZ, vertex.position[2]); + + maxX = std::max(maxX, vertex.position[0]); + maxY = std::max(maxY, vertex.position[1]); + maxZ = std::max(maxZ, vertex.position[2]); + } + + m_boundingSphere.position = OvMaths::FVector3{ minX + maxX, minY + maxY, minZ + maxZ } / 2.0f; + + for (const auto& vertex : p_vertices) + { + const auto& position = reinterpret_cast(vertex.position); + m_boundingSphere.radius = std::max(m_boundingSphere.radius, OvMaths::FVector3::Distance(m_boundingSphere.position, position)); + } + } +} diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Model.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Model.cpp index 5fc410e4..9896a6c1 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Model.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Model.cpp @@ -4,8 +4,15 @@ * @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. */ +#include + #include "OvRendering/Resources/Model.h" +const OvRendering::Geometry::BoundingSphere& OvRendering::Resources::Model::GetBoundingSphere() const +{ + return m_boundingSphere; +} + OvRendering::Resources::Model::Model(const std::string & p_path) : path(p_path) { } @@ -16,6 +23,45 @@ OvRendering::Resources::Model::~Model() delete mesh; } +void OvRendering::Resources::Model::ComputeBoundingSphere() +{ + if (m_meshes.size() == 1) + { + m_boundingSphere = m_meshes[0]->GetBoundingSphere(); + } + else + { + m_boundingSphere.position = OvMaths::FVector3::Zero; + m_boundingSphere.radius = 0.0f; + + if (!m_meshes.empty()) + { + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float minZ = std::numeric_limits::max(); + + float maxX = std::numeric_limits::min(); + float maxY = std::numeric_limits::min(); + float maxZ = std::numeric_limits::min(); + + for (const auto& mesh : m_meshes) + { + const auto& boundingSphere = mesh->GetBoundingSphere(); + minX = std::min(minX, boundingSphere.position.x - boundingSphere.radius); + minY = std::min(minY, boundingSphere.position.y - boundingSphere.radius); + minZ = std::min(minZ, boundingSphere.position.z - boundingSphere.radius); + + maxX = std::max(maxX, boundingSphere.position.x + boundingSphere.radius); + maxY = std::max(maxY, boundingSphere.position.y + boundingSphere.radius); + maxZ = std::max(maxZ, boundingSphere.position.z + boundingSphere.radius); + } + + m_boundingSphere.position = OvMaths::FVector3{ minX + maxX, minY + maxY, minZ + maxZ } / 2.0f; + m_boundingSphere.radius = OvMaths::FVector3::Distance(m_boundingSphere.position, { minX, minY, minZ }); + } + } +} + const std::vector& OvRendering::Resources::Model::GetMeshes() const { return m_meshes; diff --git a/Sources/Overload/OvTools/OvTools.vcxproj b/Sources/Overload/OvTools/OvTools.vcxproj index ea484c53..c0989c6d 100644 --- a/Sources/Overload/OvTools/OvTools.vcxproj +++ b/Sources/Overload/OvTools/OvTools.vcxproj @@ -14,19 +14,19 @@ 15.0 {8A618DBA-F8C9-42BF-B94F-29249E677EC9} OvTools - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte @@ -104,6 +104,7 @@ xcopy "$(OutDir)*.dll" "$(SolutionDir)..\..\Build\$(ProjectName)\bin\$(Configura + diff --git a/Sources/Overload/OvTools/OvTools.vcxproj.filters b/Sources/Overload/OvTools/OvTools.vcxproj.filters index 64d8fc74..04aa7869 100644 --- a/Sources/Overload/OvTools/OvTools.vcxproj.filters +++ b/Sources/Overload/OvTools/OvTools.vcxproj.filters @@ -45,6 +45,9 @@ Header Files + + Header Files + diff --git a/Sources/Overload/OvTools/include/OvTools/Utils/ReferenceOrValue.h b/Sources/Overload/OvTools/include/OvTools/Utils/ReferenceOrValue.h new file mode 100644 index 00000000..5b8880c6 --- /dev/null +++ b/Sources/Overload/OvTools/include/OvTools/Utils/ReferenceOrValue.h @@ -0,0 +1,101 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @restrictions: This software may not be resold, redistributed or otherwise conveyed to a third party. +*/ + +#pragma once + +#include + +namespace OvTools::Utils +{ + /** + * A simple class that can represent a reference or a value of the given type. + * The usage of the data is the same for both usages. + * The goal is to be able to instanciate objects that can be a reference or a value without + * taking care of what it is. + */ + template + class ReferenceOrValue + { + public: + /** + * Construct the ReferenceOrValue instance with a reference + * @param p_reference + */ + ReferenceOrValue(std::reference_wrapper p_reference) : m_data{ &p_reference.get() } + { + } + + /** + * Construct the ReferenceOrValue instance with a value + * @param p_value + */ + ReferenceOrValue(T p_value = T()) : m_data{ p_value } + { + } + + /** + * Make the ReferenceOrValue a reference + * @param p_reference + */ + void MakeReference(T& p_reference) + { + m_data = &p_reference; + } + + /** + * Make the ReferenceOrValue a value + * @param p_value + */ + void MakeValue(T p_value = T()) + { + m_data = p_value; + } + + /** + * Implicit conversion of a ReferenceOrValue to a T + */ + operator T() + { + return Get(); + } + + /** + * Assignment operator thats call the setter of the ReferenceOrValue instance + * @param p_value + */ + ReferenceOrValue& operator=(T p_value) + { + Set(p_value); + return *this; + } + + /** + * Returns the value (From reference or directly from the value) + */ + T Get() const + { + if (auto pval = std::get_if(&m_data)) + return *pval; + else + return *std::get(m_data); + } + + /** + * Sets the value (To the reference or directly to the value) + * @param p_value + */ + void Set(T p_value) + { + if (auto pval = std::get_if(&m_data)) + * pval = p_value; + else + *std::get(m_data) = p_value; + } + + private: + std::variant m_data; + }; +} \ No newline at end of file diff --git a/Sources/Overload/OvUI/OvUI.vcxproj b/Sources/Overload/OvUI/OvUI.vcxproj index e48904ba..268b85d5 100644 --- a/Sources/Overload/OvUI/OvUI.vcxproj +++ b/Sources/Overload/OvUI/OvUI.vcxproj @@ -14,19 +14,19 @@ 15.0 {A5ADA180-BA29-4EC8-ACFD-7DCBFD065967} OvUI - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte diff --git a/Sources/Overload/OvWindowing/OvWindowing.vcxproj b/Sources/Overload/OvWindowing/OvWindowing.vcxproj index db53834a..a9732b6d 100644 --- a/Sources/Overload/OvWindowing/OvWindowing.vcxproj +++ b/Sources/Overload/OvWindowing/OvWindowing.vcxproj @@ -42,19 +42,19 @@ 15.0 {54A2B9EB-6984-49C8-A080-481504343A19} OvWindowing - 10.0.17763.0 + 10.0 DynamicLibrary true - v141 + v142 MultiByte DynamicLibrary false - v141 + v142 true MultiByte