From 05d25531b56cc393013ce6062f79c1b2073e4f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Thu, 4 Jan 2024 23:32:24 +0000 Subject: [PATCH 01/31] refactor: added a basic scene graph --- src/examples/demo/DemoApp.h | 5 + src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h | 16 ++- .../gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp | 58 ++++++--- src/lepus/gfx/GraphicsEngine/GraphicsApi.h | 3 + src/lepus/gfx/SceneGraph.h | 120 ++++++++++++++++++ src/lepus/gfx/SceneGraph/Renderable.h | 60 +++++++++ src/lepus/utility/types/Transform.h | 29 +++++ 7 files changed, 269 insertions(+), 22 deletions(-) create mode 100644 src/lepus/gfx/SceneGraph.h create mode 100644 src/lepus/gfx/SceneGraph/Renderable.h diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 4bc65ec..e1fdfa0 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -147,6 +147,11 @@ class DemoApp : public system::BaseApp engine.Setup(); m_Camera.Transform().Origin(m_Camera.Transform().Forward() * -2.f); + auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); + auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + api.GetSceneGraph().AddChild(&cube); + api.GetSceneGraph().AddChild(&cube2); m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h index 334cb54..17a9499 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h @@ -10,6 +10,7 @@ #include "ApiGL/Bindings.h" #include "ApiGL/Types/GLMesh.h" +#include namespace lepus { @@ -59,6 +60,8 @@ namespace lepus } }; + typedef lepus::gfx::SceneGraph> GLSceneGraph; + template GraphicsApiGLOptions& GraphicsApi::GetOptions(); class GraphicsApiGL : public GraphicsApi @@ -71,12 +74,6 @@ namespace lepus /// @brief Handle to the vertex array objects. GLuint vao; - /// @brief Handle to the global VBO. - GLuint vbo[_meshCount] = { 0, 0 }; - - /// @brief Handle to the global IBO. - GLuint ibo[_meshCount] = { 0, 0 }; - /// @brief List with all uniforms used by the API. // TODO: Change to array - might get better cache/locality to improve access times. std::forward_list*> uniforms; @@ -88,7 +85,7 @@ namespace lepus GLuint m_Programs[GraphicsApiGLOptions::ProgramCount]; - GLMesh m_Meshes[_meshCount]; + GLSceneGraph m_Scene; private: void SetupVertexArrays(); @@ -126,6 +123,11 @@ namespace lepus void UpdateUniforms() override; + inline GLSceneGraph& GetSceneGraph() + { + return m_Scene; + } + void Draw() override; void ClearFrameBuffer(float r, float g, float b) override; diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp index 0ae519d..5566cbf 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp @@ -5,6 +5,8 @@ using namespace lepus::gfx; void GraphicsApiGL::Init(GraphicsApiOptions* options) { InitInternal((GraphicsApiGLOptions*)options); + + m_Scene = GLSceneGraph(); } void GraphicsApiGL::SetupVertexArrays() @@ -14,20 +16,10 @@ void GraphicsApiGL::SetupVertexArrays() glBindVertexArray(m_Pipeline.vao); } +// TODO: rename this, or move to SetupVertexArrays? void GraphicsApiGL::SetupBuffers() { glBindVertexArray(m_Pipeline.vao); - - // Creating a mesh from built-in primitive geometry. - m_Meshes[0] = GLMesh(lepus::utility::Primitives::Cube()); - m_Pipeline.vbo[0] = m_Meshes[0].GetVBO(); - m_Pipeline.ibo[0] = m_Meshes[0].GetIBO(); - - // Creating a mesh by copying the first mesh. - // Vertex & index data is shared with the first mesh, but uploaded to a separate pair of OpenGL buffers. - m_Meshes[1] = GLMesh(m_Meshes[0]); - m_Pipeline.vbo[1] = m_Meshes[1].GetVBO(); - m_Pipeline.ibo[1] = m_Meshes[1].GetIBO(); } void GraphicsApiGL::SetupShaders() @@ -104,13 +96,49 @@ void GraphicsApiGL::Draw() glUseProgram(m_Programs[0]); glBindVertexArray(m_Pipeline.vao); - for (uint8_t meshIndex = 0; meshIndex < _meshCount; meshIndex++) + + const GLSceneGraph::Node* currentNode = m_Scene.Root(); + + bool branchComplete = false; + + while (currentNode) { - glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo[meshIndex]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo[meshIndex]); + if (!branchComplete && !currentNode->IsRoot()) + { + typedef lepus::gfx::Renderable GLRenderable; + const GLRenderable* renderable = currentNode->GetRenderable(); + if (renderable) + { + glBindBuffer(GL_ARRAY_BUFFER, renderable->GetMesh()->GetVBO()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderable->GetMesh()->GetIBO()); - glDrawElements(GL_TRIANGLES, (GLsizei)m_Meshes[meshIndex].IndexCount(), GL_UNSIGNED_INT, 0); + glDrawElements(GL_TRIANGLES, (GLsizei)renderable->GetMesh()->IndexCount(), GL_UNSIGNED_INT, 0); + } + } + + if (!branchComplete && currentNode->FirstChild()) + { + currentNode = currentNode->FirstChild(); + } + else if (currentNode->NextSibling()) + { + currentNode = currentNode->NextSibling(); + branchComplete = false; + } + else + { + branchComplete = true; + currentNode = currentNode->Parent(); + } } + + // for (uint8_t meshIndex = 0; meshIndex < _meshCount; meshIndex++) + // { + // glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo[meshIndex]); + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo[meshIndex]); + + // glDrawElements(GL_TRIANGLES, (GLsizei)m_Meshes[meshIndex].IndexCount(), GL_UNSIGNED_INT, 0); + // } } void GraphicsApiGL::ClearFrameBuffer(float r, float g, float b) diff --git a/src/lepus/gfx/GraphicsEngine/GraphicsApi.h b/src/lepus/gfx/GraphicsEngine/GraphicsApi.h index 17ece38..410dc99 100644 --- a/src/lepus/gfx/GraphicsEngine/GraphicsApi.h +++ b/src/lepus/gfx/GraphicsEngine/GraphicsApi.h @@ -52,8 +52,10 @@ namespace lepus { private: bool m_ShutdownCalled; + protected: GraphicsApiOptions* m_Options; + protected: /// @brief Performs internal, boilerplate setup for all API wrappers. /// @tparam TGraphicsApiOptions An options type derived from GraphicsApiOptions for a specific graphics API. @@ -71,6 +73,7 @@ namespace lepus /// @param name The name of the uniform to fetch. /// @return API-specific handle for a uniform of a given type. virtual void* GetUniformInternal(char* name) = 0; + public: /// @brief Default constructor. Does nothing, so Init(GraphicsApiOptions*) needs to be called manually. GraphicsApi() diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h new file mode 100644 index 0000000..fa53a1f --- /dev/null +++ b/src/lepus/gfx/SceneGraph.h @@ -0,0 +1,120 @@ +#ifndef LEPUS_GFX_SCENEGRAPH +#define LEPUS_GFX_SCENEGRAPH +#include "SceneGraph/Renderable.h" + +namespace lepus +{ + namespace gfx + { + template> + class SceneGraph + { + public: + + class Node + { + private: + friend class SceneGraph; + + protected: + Node* m_Sibling; + Node* m_Child; + Node* m_Parent; + const RenderableType* m_Renderable; + + public: + Node() + { + m_Sibling = nullptr; + m_Child = nullptr; + m_Parent = nullptr; + m_Renderable = nullptr; + } + + ~Node() + { + + } + + inline const RenderableType* GetRenderable() const { return m_Renderable; } + inline const Node* NextSibling() const { return m_Sibling; } + inline const Node* Parent() const { return m_Parent; } + inline const Node* FirstChild() const { return m_Child; } + + inline bool IsRoot() const { return Parent() == nullptr; } + + inline const Node* AddChild(const RenderableType* renderable) + { + Node* currentNode = m_Child; + Node* lastChild = m_Child; + while (currentNode) + { + lastChild = currentNode; + currentNode = currentNode->m_Sibling; + } + + if (!currentNode) + { + currentNode = new Node(); + currentNode->m_Renderable = renderable; + currentNode->m_Parent = this; + } + + if (!m_Child) + { + m_Child = currentNode; + } + else if (lastChild && !lastChild->m_Sibling) + { + lastChild->m_Sibling = currentNode; + } + + return currentNode; + } + }; + + protected: + Node* m_Root; + + public: + + SceneGraph() + { + m_Root = new Node(); + } + + const Node* AddChild(const RenderableType* renderable) + { + return m_Root->AddChild(renderable); + } + + ~SceneGraph() + { + Node* currentNode = m_Root->m_Child; + Node* parent = currentNode; + + // Delete all children + while (currentNode) + { + if (currentNode->m_Child) + { + parent = currentNode; + currentNode = currentNode->m_Child; + } + else + { + parent->m_Child = currentNode->m_Sibling; + delete currentNode; + currentNode = parent; + } + } + } + + const Node* Root() const + { + return m_Root; + } + }; + } +} +#endif \ No newline at end of file diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h new file mode 100644 index 0000000..802f6a2 --- /dev/null +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -0,0 +1,60 @@ +#ifndef LEPUS_GFX_SCENEGRAPH_RENDERABLE +#define LEPUS_GFX_SCENEGRAPH_RENDERABLE + +#include +#include + +namespace lepus +{ + namespace gfx + { + template + class Renderable + { + private: + const MeshType* m_Mesh; + const lepus::math::Transform* m_Transform; + bool m_OwnsTransform; + + public: + Renderable() = delete; + + Renderable(const MeshType* mesh, const lepus::math::Transform& transform) + { + m_Mesh = mesh; + m_Transform = &transform; + m_OwnsTransform = false; + } + + Renderable(const MeshType* mesh, lepus::math::Transform&& transform) + { + m_Mesh = mesh; + m_Transform = new lepus::math::Transform(transform); + m_OwnsTransform = true; + } + + ~Renderable() + { + m_Mesh = nullptr; + + if (m_OwnsTransform) + { + delete m_Transform; + m_Transform = nullptr; + } + } + + const MeshType* GetMesh() const + { + return m_Mesh; + } + + const lepus::math::Transform& GetTransform() const + { + return *m_Transform; + } + }; + } +} + +#endif \ No newline at end of file diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index 57f99bb..2e00628 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -27,6 +27,35 @@ namespace lepus m_Rotation = lepus::types::Quaternion(); } + Transform(Transform&& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + } + + Transform(const Transform& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + } + + Transform& operator=(const Transform& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + + return *this; + } + inline lepus::types::Vector3 Origin() const { return m_Origin; } inline void Origin(const lepus::types::Vector3& vec) { From f9fdeb47e537b7c15dbe203c914d2da0901d52d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Mon, 12 Feb 2024 12:23:27 +0000 Subject: [PATCH 02/31] chore: wip --- CMakeLists.txt | 2 ++ src/lepus/gfx/SceneGraph.h | 12 ++++++++- tests/L3D/SceneGraph/SceneGraphTests.cpp | 34 ++++++++++++++++++++++++ tests/L3D/SceneGraph/SceneGraphTests.h | 6 +++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/L3D/SceneGraph/SceneGraphTests.cpp create mode 100644 tests/L3D/SceneGraph/SceneGraphTests.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f8c68..d088e9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,8 @@ add_executable(LepusGfx_Tests tests/L3D/GraphicsEngine/GraphicsApiTests.cpp tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.h tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.cpp + tests/L3D/SceneGraph/SceneGraphTests.h + tests/L3D/SceneGraph/SceneGraphTests.cpp ) add_executable(LepusSystem_Tests diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index fa53a1f..69f344d 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -91,18 +91,28 @@ namespace lepus ~SceneGraph() { Node* currentNode = m_Root->m_Child; - Node* parent = currentNode; + Node* parent = m_Root; // Delete all children while (currentNode) { + std::cout << "current node is " << std::hex << currentNode << std::endl; + std::cout << "parent is " << std::hex << parent; + if (parent == currentNode) + { + std::cout << "(we're root)"; + } + std::cout << std::endl; + if (currentNode->m_Child) { + std::cout << "moving to child" << std::endl; parent = currentNode; currentNode = currentNode->m_Child; } else { + std::cout << "deleting current node " << std::hex << currentNode << std::endl; parent->m_Child = currentNode->m_Sibling; delete currentNode; currentNode = parent; diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp new file mode 100644 index 0000000..82c43f9 --- /dev/null +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -0,0 +1,34 @@ +#include +#include "SceneGraphTests.h" + +class SceneGraphTest : public testing::Test +{ + +}; + +TEST(SceneGraphTest, SceneGraphIsInitialisedCorrectly) +{ + lepus::gfx::SceneGraph sceneGraph = lepus::gfx::SceneGraph(); + + ASSERT_TRUE(sceneGraph.Root()->IsRoot()); + ASSERT_EQ(sceneGraph.Root()->FirstChild(), nullptr); + ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); + ASSERT_EQ(sceneGraph.Root()->Parent(), nullptr); + ASSERT_EQ(sceneGraph.Root()->GetRenderable(), nullptr); +} + +TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) +{ + lepus::gfx::SceneGraph sceneGraph = lepus::gfx::SceneGraph(); + lepus::math::Transform someTransform = lepus::math::Transform(); + lepus::engine::objects::Mesh someMesh = lepus::engine::objects::Mesh(); + lepus::gfx::Renderable someRenderable = lepus::gfx::Renderable(&someMesh, someTransform); + + sceneGraph.AddChild(&someRenderable); + ASSERT_EQ(sceneGraph.Root()->FirstChild()->GetRenderable(), &someRenderable); + ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); + ASSERT_EQ(sceneGraph.Root()->FirstChild()->FirstChild(), nullptr); + ASSERT_EQ(sceneGraph.Root()->FirstChild()->NextSibling(), nullptr); + + std::cout << "test" << std::endl; +} \ No newline at end of file diff --git a/tests/L3D/SceneGraph/SceneGraphTests.h b/tests/L3D/SceneGraph/SceneGraphTests.h new file mode 100644 index 0000000..640555e --- /dev/null +++ b/tests/L3D/SceneGraph/SceneGraphTests.h @@ -0,0 +1,6 @@ +#ifndef LTESTS_LGFX_SCENEGRAPH_TESTS +#define LTESTS_LGFX_SCENEGRAPH_TESTS + +#include + +#endif \ No newline at end of file From fef4cc1268eceef916f960ff2e0493e9e1feedab Mon Sep 17 00:00:00 2001 From: Tomasz Zajac Date: Mon, 12 Feb 2024 13:50:35 +0000 Subject: [PATCH 03/31] fix: fixed failing build due to C5262 in MSVC headers --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d088e9d..c56d8cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,7 +223,8 @@ if(MSVC) # 4263: member not overriding base class virtual # 4265-5027: copy ctor, move ctor, assignment, move assignment implicitly deleted (this messes up gtest projects) # 5045: "compiler will insert Spectre mitigation for memory load" - set(MSVCDisabledWarnings 4100 4514 4464 4820 4263 4625 5026 4626 5027 5045) + # 5262: implicit switch fallback + set(MSVCDisabledWarnings 4100 4514 4464 4820 4263 4625 5026 4626 5027 5045 5262) set(MSVCDisabledWarningsFormatted "") foreach(Warning IN LISTS MSVCDisabledWarnings) From 5fe5ebd8d35f95ac56bc86006fa1dbb09d18b891 Mon Sep 17 00:00:00 2001 From: Tomasz Zajac Date: Mon, 12 Feb 2024 14:32:11 +0000 Subject: [PATCH 04/31] fix: fixed scenegraph dtor not terminating after deleting root --- src/lepus/gfx/SceneGraph.h | 13 ++----------- tests/L3D/SceneGraph/SceneGraphTests.cpp | 2 -- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index 69f344d..1237b1d 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -96,26 +96,17 @@ namespace lepus // Delete all children while (currentNode) { - std::cout << "current node is " << std::hex << currentNode << std::endl; - std::cout << "parent is " << std::hex << parent; - if (parent == currentNode) - { - std::cout << "(we're root)"; - } - std::cout << std::endl; - if (currentNode->m_Child) { - std::cout << "moving to child" << std::endl; parent = currentNode; currentNode = currentNode->m_Child; } else { - std::cout << "deleting current node " << std::hex << currentNode << std::endl; + bool wasRoot = currentNode->IsRoot(); parent->m_Child = currentNode->m_Sibling; delete currentNode; - currentNode = parent; + currentNode = wasRoot ? nullptr : parent; } } } diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index 82c43f9..043e444 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -29,6 +29,4 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->FirstChild(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->NextSibling(), nullptr); - - std::cout << "test" << std::endl; } \ No newline at end of file From a9e138c2cbf3dc3bd27ff6e3b0e79e70ae4e1131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Mon, 18 Mar 2024 22:00:11 +0000 Subject: [PATCH 05/31] feat: added basic model translation matrix --- Content/GLSL/Unlit/RGBVertex.vert | 3 ++- src/examples/demo/DemoApp.h | 12 ++++++++--- .../gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp | 10 +++++++++ .../gfx/GraphicsEngine/GraphicsApi/Uniforms.h | 1 + src/lepus/gfx/SceneGraph/Renderable.h | 4 ++-- src/lepus/utility/types/Transform.h | 21 +++++++++++++++++++ 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/Content/GLSL/Unlit/RGBVertex.vert b/Content/GLSL/Unlit/RGBVertex.vert index 48e73b0..641a37b 100644 --- a/Content/GLSL/Unlit/RGBVertex.vert +++ b/Content/GLSL/Unlit/RGBVertex.vert @@ -4,12 +4,13 @@ layout (location = 0) in vec3 position; uniform mat4 PROJ; uniform mat4 VIEW; +uniform mat4 MODEL; out vec3 vertColor; void main() { - gl_Position = PROJ * VIEW * vec4(position, 1.0); + gl_Position = PROJ * VIEW * MODEL * vec4(position, 1.0); float normalisedIndex = mod(float(gl_VertexID), 3.0f); float r = step(normalisedIndex, 0.0f); diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index e1fdfa0..c00df51 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -147,37 +147,41 @@ class DemoApp : public system::BaseApp engine.Setup(); m_Camera.Transform().Origin(m_Camera.Transform().Forward() * -2.f); + // Instantiate two Renderables in the scene graph, each with its own transform, using the same cube mesh data. auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); api.GetSceneGraph().AddChild(&cube); api.GetSceneGraph().AddChild(&cube2); + // Update projection and view matrices with data from the camera object. m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); - m_UniformState.viewMatrix = m_Camera.BuildViewMatrix(); ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX))->Value((float*)m_UniformState.viewMatrix.data()); + // Initialise the FOV variable and set up a callback so we can let the user adjust it with the mouse scroll wheel. m_FOV = m_Camera.FOV(); - glfwSetScrollCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::scroll); float runningTime = glfwGetTime(); GLFWwindow* window = reinterpret_cast(windowing->GetWindowPtr()); + + // Set up mouse input for camera freelook. glfwGetCursorPos(window, &m_MouseState.lastX, &m_MouseState.lastY); glfwSetCursorPosCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::cursorPos); - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, 1); + // Initialise all keys as released. KeyboardState keys = { false, false, false, false }; float deltaTime = 0.f; while (isRunning) { + cube2.GetTransform().SetPosition(1.f * sinf(runningTime), 1.f * cosf(runningTime), -1.f); windowing->Update(); // Update window before drawing UpdateInput(keys, windowing); @@ -222,6 +226,8 @@ class DemoApp : public system::BaseApp m_Camera.FOV(m_FOV); lepus::types::Vector3 forwardDelta, rightDelta; + + // Process camera movement input based on keyboard state. if (keys.s) { forwardDelta = forwardDelta - (m_Camera.Transform().Forward() * deltaTime * _camSpeed); diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp index 5566cbf..ba5dbab 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp @@ -52,6 +52,11 @@ void GraphicsApiGL::SetupUniforms() auto* view = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX)); m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(view)); m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX, reinterpret_cast*>(view)); + + // Model matrix + auto* model = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX)); + m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(model)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX, reinterpret_cast*>(model)); } void GraphicsApiGL::CreatePipeline() @@ -109,6 +114,11 @@ void GraphicsApiGL::Draw() const GLRenderable* renderable = currentNode->GetRenderable(); if (renderable) { + lepus::math::Matrix4x4 modelMat = renderable->GetTransform().BuildMatrix(); + auto modelMatUniformLoation = GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX); + // TODO: integrate this with UpdateUniforms somehow, the model matrix uniform needs to be different for each renderable! + glUniformMatrix4fv(modelMatUniformLoation->Location(), 1, true, modelMat.data()); + glBindBuffer(GL_ARRAY_BUFFER, renderable->GetMesh()->GetVBO()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderable->GetMesh()->GetIBO()); diff --git a/src/lepus/gfx/GraphicsEngine/GraphicsApi/Uniforms.h b/src/lepus/gfx/GraphicsEngine/GraphicsApi/Uniforms.h index 783aa2b..1f1034e 100644 --- a/src/lepus/gfx/GraphicsEngine/GraphicsApi/Uniforms.h +++ b/src/lepus/gfx/GraphicsEngine/GraphicsApi/Uniforms.h @@ -14,6 +14,7 @@ namespace lepus #pragma region Engine global uniform names #define LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX "PROJ" #define LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX "VIEW" +#define LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX "MODEL" #pragma endregion } } diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h index 802f6a2..18252ba 100644 --- a/src/lepus/gfx/SceneGraph/Renderable.h +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -49,9 +49,9 @@ namespace lepus return m_Mesh; } - const lepus::math::Transform& GetTransform() const + lepus::math::Transform& GetTransform() const { - return *m_Transform; + return (lepus::math::Transform&)(*reinterpret_cast(m_Transform)); } }; } diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index 2e00628..7cc67c5 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -115,6 +115,27 @@ namespace lepus return ret; } + + inline void SetPosition(float x, float y, float z) + { + m_Origin.x(x); + m_Origin.y(y); + m_Origin.z(z); + } + + inline lepus::math::Matrix4x4 BuildMatrix() const + { + lepus::math::Matrix4x4 model = lepus::math::Matrix4x4::Identity(); + + // Translation + model.set<0, 3>(m_Origin.x()); + model.set<1, 3>(m_Origin.y()); + model.set<2, 3>(m_Origin.z()); + + // TODO: add scaling and rotation + + return model; + } }; } } From 48cfdcc238f27c9b144200aba1b3e97a58de162f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 6 Oct 2024 14:10:20 +0100 Subject: [PATCH 06/31] chore: got z rotation working --- src/examples/demo/DemoApp.h | 22 ++++++++++++--- src/lepus/utility/types/Quaternion.h | 40 +++++++++++++++++++++++++--- src/lepus/utility/types/Transform.h | 28 +++++++++++++++++++ 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index c00df51..70d1bc0 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -51,7 +51,7 @@ class DemoApp : public system::BaseApp struct KeyboardState { - bool w = false, a = false, s = false, d = false; + bool w = false, a = false, s = false, d = false, e = false; }; struct @@ -90,7 +90,7 @@ class DemoApp : public system::BaseApp { float deltaX = (xpos - m_MouseState.lastX) / 300.0; float deltaY = (ypos - m_MouseState.lastY) / 300.0; - lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, deltaX); + lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, -deltaX); auto combined = rotationYaw; float angle = combined.Angle(); @@ -98,7 +98,7 @@ class DemoApp : public system::BaseApp { m_Camera.Transform().Rotate(rotationYaw); } - lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), deltaY); + lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), -deltaY); angle = rotationPitch.Angle(); if (abs(angle) > 0.001f) { @@ -175,16 +175,26 @@ class DemoApp : public system::BaseApp glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, 1); // Initialise all keys as released. - KeyboardState keys = { false, false, false, false }; + KeyboardState keys = { false, false, false, false, false }; float deltaTime = 0.f; + // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI * 0.25f)); while (isRunning) { + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", cube2.GetTransform().Rotation().Axis().ToString().c_str()); cube2.GetTransform().SetPosition(1.f * sinf(runningTime), 1.f * cosf(runningTime), -1.f); windowing->Update(); // Update window before drawing + bool eKeyPressedLastFrame = keys.e; UpdateInput(keys, windowing); + cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); + if (!eKeyPressedLastFrame && keys.e) + { + cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); + } Tick(deltaTime, keys); UpdateUniforms(&api); @@ -210,6 +220,7 @@ class DemoApp : public system::BaseApp keys.a = glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS; keys.s = glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS; keys.d = glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS; + keys.e = glfwGetKey(window, GLFW_KEY_E) == GLFW_RELEASE; } inline void UpdateUniforms(gfx::GraphicsApi* api) @@ -245,6 +256,9 @@ class DemoApp : public system::BaseApp rightDelta = rightDelta - (m_Camera.Transform().Right() * deltaTime * _camSpeed); } + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", m_Camera.Transform().Rotation().Axis().ToString().c_str()); + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(m_Camera.Transform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); + m_Camera.Transform().Origin(m_Camera.Transform().Origin() + forwardDelta + rightDelta); } diff --git a/src/lepus/utility/types/Quaternion.h b/src/lepus/utility/types/Quaternion.h index 6a802dd..f16823b 100644 --- a/src/lepus/utility/types/Quaternion.h +++ b/src/lepus/utility/types/Quaternion.h @@ -64,31 +64,63 @@ namespace lepus /// @return A scalar describing the angle of rotation calculated from the Quaternion's real part (arccos(w) * 2). inline float Angle() const { - return acosf(fmax(-1.f, fmin(1.f, w()))) * 2.f; + float wrappedW = w(); + // if (wrappedW > 1.f) + // { + // wrappedW = -1.f + (wrappedW - 1.f); + // } + // else if (wrappedW < -1.f) + // { + // wrappedW = 1.f + (wrappedW + 1.f); + // } + return acosf(fmin(1.f, fmax(-1.f, wrappedW))) * 2.f; } inline float x() const { return m_Components[0]; } inline float y() const { return m_Components[1]; } inline float z() const { return m_Components[2]; } - inline float w() const { return m_Components[3]; } + inline float w() const + { + float w = m_Components[3]; + return w; + } inline float x(float newX) { return m_Components[0] = newX; } inline float y(float newY) { return m_Components[1] = newY; } inline float z(float newZ) { return m_Components[2] = newZ; } - inline float w(float newW) { return m_Components[3] = newW; } + inline float w(float newW) + { + // Wrap the angle component as going outside of [-1, 1] would cause the Quaternion to rotate in the opposite direction + // if (newW > 1.f) + // { + // newW = -1.f + (newW - 1.f); + // } + // else if (newW < -1.f) + // { + // newW = 1.f + (newW + 1.f); + // } + + return m_Components[3] = newW; + } inline Quaternion operator*(const Quaternion& b) const { Quaternion result = Quaternion(); + // lepus::engine::ConsoleLogger::Global().LogInfo("Quaternion", "operator*", std::to_string(w()).c_str(), "const Quaternion& b"); + + // result.w(fmin(1.f, fmax(-1.f, w()))); + lepus::types::Vector3 va = lepus::types::Vector3((float*)GetData()); lepus::types::Vector3 vb = lepus::types::Vector3((float*)b.GetData()); result.w((w() * b.w()) - lepus::types::Vector3::Dot(va, vb)); - lepus::types::Vector3 vc = lepus::types::Vector3::Cross(vb, va) + (vb * w()) + (va * b.w()); + lepus::types::Vector3 vc = lepus::types::Vector3::Cross(va, vb) + (vb * w()) + (va * b.w()); result.x(vc.x()); result.y(vc.y()); result.z(vc.z()); + // lepus::engine::ConsoleLogger::Global().LogInfo("Quaternion", "operator*", result.Axis().ToString().c_str(), "const Quaternion& b"); + return result; } diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index 7cc67c5..7c29b8b 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -133,6 +133,34 @@ namespace lepus model.set<2, 3>(m_Origin.z()); // TODO: add scaling and rotation + lepus::types::Vector3 axis = m_Rotation.Axis(); + //axis = axis * (1.f / axis.Magnitude()); + float angle = m_Rotation.Angle(); + + // model.set<0, 0>(sinf(angle) * axis.z()); + + // model.set<0, 1>(cosf(angle) * axis.z()); + // model.set<1, 0>(cosf(angle) * axis.z()); + // model.set<1, 1>(sinf(angle) * axis.z()); + + // float theta = (float)PI * 0.25f; + // float theta = fmod(angle, (float)PI); + // model.set<0, 1>(cosf(theta)); + // model.set<1, 0>(-sinf(theta)); + // model.set<1, 1>(-cosf(angle * 2.f)); + // model.set<0, 1>(2.f * sinf(angle) * axis.z()); + // model.set<1, 0>(2.f * -cosf(angle) * axis.z()); + + // model.set<0, 0>(sinf(angle) * axis.z() + cosf(angle) * axis.y()); + // model.set<1, 1>(cosf(angle) * axis.z() + sinf(angle) * axis.x()); + // model.set<2, 2>(cosf(angle) * axis.x() + sinf(angle) * axis.y()); + + float sign = axis.z(); + angle *= sign; + model.set<0, 0>(cosf(angle)); + model.set<0, 1>(sinf(angle)); + model.set<1, 1>(cosf(angle)); + model.set<1, 0>(-sinf(angle)); return model; } From 4f88206aa3156271810bbaf8f150b35acca7ec94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 6 Oct 2024 19:31:05 +0100 Subject: [PATCH 07/31] feat: added XYZ rotation to model matrix --- src/examples/demo/DemoApp.h | 24 ++++++++++--- src/lepus/utility/types/Quaternion.h | 52 ++++++++++++---------------- src/lepus/utility/types/Transform.h | 43 +++++++++-------------- 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 70d1bc0..622fac9 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -151,8 +151,18 @@ class DemoApp : public system::BaseApp auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cubeX = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cubeY = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cubeZ = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); api.GetSceneGraph().AddChild(&cube); api.GetSceneGraph().AddChild(&cube2); + api.GetSceneGraph().AddChild(&cubeX); + api.GetSceneGraph().AddChild(&cubeY); + api.GetSceneGraph().AddChild(&cubeZ); + + cubeX.GetTransform().SetPosition(-1.5f, 2.f, 0.f); + cubeY.GetTransform().SetPosition(0.f, 2.f, 0.f); + cubeZ.GetTransform().SetPosition(1.5f, 2.f, 0.f); // Update projection and view matrices with data from the camera object. m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); @@ -189,10 +199,16 @@ class DemoApp : public system::BaseApp bool eKeyPressedLastFrame = keys.e; UpdateInput(keys, windowing); - cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); + // cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().Angle()).c_str()); + cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), deltaTime)); + cubeX.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), deltaTime)); + cubeY.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + cubeZ.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); if (!eKeyPressedLastFrame && keys.e) { - cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); + // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); } Tick(deltaTime, keys); @@ -256,8 +272,8 @@ class DemoApp : public system::BaseApp rightDelta = rightDelta - (m_Camera.Transform().Right() * deltaTime * _camSpeed); } - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", m_Camera.Transform().Rotation().Axis().ToString().c_str()); - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(m_Camera.Transform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", m_Camera.Transform().Rotation().Axis().ToString().c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(m_Camera.Transform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); m_Camera.Transform().Origin(m_Camera.Transform().Origin() + forwardDelta + rightDelta); } diff --git a/src/lepus/utility/types/Quaternion.h b/src/lepus/utility/types/Quaternion.h index f16823b..1176a04 100644 --- a/src/lepus/utility/types/Quaternion.h +++ b/src/lepus/utility/types/Quaternion.h @@ -53,6 +53,7 @@ namespace lepus if (sqrtInvW == 0.f) { // just return Vector3.Up if divide by 0 + // sqrtInvW = 1.f; return Vector3(0.f, 1.f, 0.f); } @@ -64,43 +65,18 @@ namespace lepus /// @return A scalar describing the angle of rotation calculated from the Quaternion's real part (arccos(w) * 2). inline float Angle() const { - float wrappedW = w(); - // if (wrappedW > 1.f) - // { - // wrappedW = -1.f + (wrappedW - 1.f); - // } - // else if (wrappedW < -1.f) - // { - // wrappedW = 1.f + (wrappedW + 1.f); - // } - return acosf(fmin(1.f, fmax(-1.f, wrappedW))) * 2.f; + return acosf(w()) * 2.f; } inline float x() const { return m_Components[0]; } inline float y() const { return m_Components[1]; } inline float z() const { return m_Components[2]; } - inline float w() const - { - float w = m_Components[3]; - return w; - } + inline float w() const { return m_Components[3]; } + inline float x(float newX) { return m_Components[0] = newX; } inline float y(float newY) { return m_Components[1] = newY; } inline float z(float newZ) { return m_Components[2] = newZ; } - inline float w(float newW) - { - // Wrap the angle component as going outside of [-1, 1] would cause the Quaternion to rotate in the opposite direction - // if (newW > 1.f) - // { - // newW = -1.f + (newW - 1.f); - // } - // else if (newW < -1.f) - // { - // newW = 1.f + (newW + 1.f); - // } - - return m_Components[3] = newW; - } + inline float w(float newW) { return m_Components[3] = newW; } inline Quaternion operator*(const Quaternion& b) const { @@ -124,6 +100,24 @@ namespace lepus return result; } + /// @brief Computes a unit quaternion from this quaternion. + /// @return A copy of this quaternion divided by its magnitude. + inline Quaternion Normalised() const + { + Quaternion result = Quaternion(); + + float x = this->x(), y = this->y(), z = this->z(), w = this->w(); + + float mag = sqrtf(w * w + x * x + y * y + z * z); + + result.x(x / mag); + result.y(y / mag); + result.z(z / mag); + result.w(w / mag); + + return result; + } + /// @brief Rotates a Vector3 v by the axis and angle described by this Quaternion. /// @param v Vector3 representing a point in 3D space to rotate. /// @return A Vector3 containing the point v rotated around the origin. diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index 7c29b8b..f088165 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -133,34 +133,23 @@ namespace lepus model.set<2, 3>(m_Origin.z()); // TODO: add scaling and rotation - lepus::types::Vector3 axis = m_Rotation.Axis(); + lepus::types::Quaternion normRot = m_Rotation.Normalised(); + lepus::types::Vector3 axis = normRot.Axis(); //axis = axis * (1.f / axis.Magnitude()); - float angle = m_Rotation.Angle(); - - // model.set<0, 0>(sinf(angle) * axis.z()); - - // model.set<0, 1>(cosf(angle) * axis.z()); - // model.set<1, 0>(cosf(angle) * axis.z()); - // model.set<1, 1>(sinf(angle) * axis.z()); - - // float theta = (float)PI * 0.25f; - // float theta = fmod(angle, (float)PI); - // model.set<0, 1>(cosf(theta)); - // model.set<1, 0>(-sinf(theta)); - // model.set<1, 1>(-cosf(angle * 2.f)); - // model.set<0, 1>(2.f * sinf(angle) * axis.z()); - // model.set<1, 0>(2.f * -cosf(angle) * axis.z()); - - // model.set<0, 0>(sinf(angle) * axis.z() + cosf(angle) * axis.y()); - // model.set<1, 1>(cosf(angle) * axis.z() + sinf(angle) * axis.x()); - // model.set<2, 2>(cosf(angle) * axis.x() + sinf(angle) * axis.y()); - - float sign = axis.z(); - angle *= sign; - model.set<0, 0>(cosf(angle)); - model.set<0, 1>(sinf(angle)); - model.set<1, 1>(cosf(angle)); - model.set<1, 0>(-sinf(angle)); + float angle = normRot.Angle(); + + float cosTheta = cosf(angle); + float invCosTheta = 1.f - cosTheta; + float sinTheta = sinf(angle); + model.set<0, 0>(axis.x() * axis.x() * invCosTheta + cosTheta); + model.set<0, 1>(axis.x() * axis.y() * invCosTheta + axis.z() * sinTheta); + model.set<1, 1>(axis.y() * axis.y() * invCosTheta + cosTheta); + model.set<1, 0>(axis.y() * axis.x() * invCosTheta - axis.z() * sinTheta); + model.set<2, 0>(axis.z() * axis.x() * invCosTheta + axis.y() * sinTheta); + model.set<2, 1>(axis.z() * axis.y() * invCosTheta - axis.x() * sinTheta); + model.set<0, 2>(axis.x() * axis.z() * invCosTheta - axis.y() * sinTheta); + model.set<1, 2>(axis.y() * axis.z() * invCosTheta + axis.x() * sinTheta); + model.set<2, 2>(axis.z() * axis.z() * invCosTheta + cosTheta); return model; } From 094890eba494e1425f9e5517d89d35bfea25feec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Mon, 7 Oct 2024 20:19:43 +0100 Subject: [PATCH 08/31] feat: added scaling to the model matrix --- src/examples/demo/DemoApp.h | 387 +++++++++--------- src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h | 223 +++++----- src/lepus/gfx/GraphicsEngine/GraphicsApi.h | 33 +- .../gfx/GraphicsEngine/GraphicsEngine.cpp | 6 +- src/lepus/utility/types/Transform.h | 323 ++++++++------- 5 files changed, 513 insertions(+), 459 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 622fac9..e05323c 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -31,7 +31,7 @@ namespace DemoAppGLFWCallbacks void cursorPos(GLFWwindow* window, double xpos, double ypos); void scroll(GLFWwindow* window, double xoffset, double yoffset); -} +} // namespace DemoAppGLFWCallbacks class DemoApp : public system::BaseApp { @@ -42,254 +42,263 @@ class DemoApp : public system::BaseApp struct { - double lastX, lastY; + double lastX, lastY; } m_MouseState; lepus::gfx::Camera m_Camera; - virtual void Init(void callback()) {}; + virtual void Init(void callback()){}; struct KeyboardState { - bool w = false, a = false, s = false, d = false, e = false; + bool w = false, a = false, s = false, d = false, e = false; }; struct { - lepus::math::Matrix4x4 projMatrix, viewMatrix; + lepus::math::Matrix4x4 projMatrix, viewMatrix; } m_UniformState; public: DemoApp() { - m_FOV = 0.f; + m_FOV = 0.f; - m_MouseState = - { - -1.0, -1.0 - }; + m_MouseState = + { + -1.0, -1.0}; - m_Camera = lepus::gfx::Camera(); + m_Camera = lepus::gfx::Camera(); - Init(); + Init(); } inline void Init() { - assert(!m_Initialised); + assert(!m_Initialised); - // Enable logging - engine::ConsoleLogger::Global().Enabled = true; + // Enable logging + engine::ConsoleLogger::Global().Enabled = true; - m_Initialised = true; + m_Initialised = true; - DemoAppGLFWCallbacks::_demoApp = this; + DemoAppGLFWCallbacks::_demoApp = this; } inline void OnMouseMove(double xpos, double ypos) { - float deltaX = (xpos - m_MouseState.lastX) / 300.0; - float deltaY = (ypos - m_MouseState.lastY) / 300.0; - lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, -deltaX); - - auto combined = rotationYaw; - float angle = combined.Angle(); - if (abs(angle) > 0.001f) - { - m_Camera.Transform().Rotate(rotationYaw); - } - lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), -deltaY); - angle = rotationPitch.Angle(); - if (abs(angle) > 0.001f) - { - m_Camera.Transform().Rotate(rotationPitch); - } - - m_MouseState.lastX = xpos; - m_MouseState.lastY = ypos; + float deltaX = (xpos - m_MouseState.lastX) / 300.0; + float deltaY = (ypos - m_MouseState.lastY) / 300.0; + lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, -deltaX); + + auto combined = rotationYaw; + float angle = combined.Angle(); + if (abs(angle) > 0.001f) + { + m_Camera.Transform().Rotate(rotationYaw); + } + lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), -deltaY); + angle = rotationPitch.Angle(); + if (abs(angle) > 0.001f) + { + m_Camera.Transform().Rotate(rotationPitch); + } + + m_MouseState.lastX = xpos; + m_MouseState.lastY = ypos; } inline void OnScroll(double xoffset, float yoffset) { - m_FOV = fmax(1.f, fmin(179.f, m_FOV + (float)yoffset)); - engine::ConsoleLogger::Global().LogInfo("", "scrollCallback", "yoffset", std::to_string(xoffset).append(", ").append(std::to_string(yoffset)).append("FOV: ").append(std::to_string(m_FOV)).c_str()); + m_FOV = fmax(1.f, fmin(179.f, m_FOV + (float)yoffset)); + engine::ConsoleLogger::Global().LogInfo("", "scrollCallback", "yoffset", std::to_string(xoffset).append(", ").append(std::to_string(yoffset)).append("FOV: ").append(std::to_string(m_FOV)).c_str()); } inline int Run() override { - std::shared_ptr windowing = std::make_shared(800, 600); - - // Create new graphics engine instance - gfx::GraphicsApiGLOptions options = {}; - options.mainViewport = { 800, 600 }; - gfx::GraphicsEngine engine(&options, windowing); - - // Termination condition for main loop - bool isRunning = true; - - // Set the window as the current OpenGL context. - windowing->SetAsCurrentContext(); - - // Output start message to console - engine::ConsoleLogger::Global().LogInfo("", "main", "Demo starting!"); - - // Load & compile shaders. - std::string vertShaderSrc = system::FileSystem::Read("../../Content/GLSL/Unlit/RGBVertex.vert"), fragShaderSrc = system::FileSystem::Read("../../Content/GLSL/Unlit/RGBVertex.frag"); - gfx::ShaderCompiledResult - vertShader = gfx::ShaderCompilerGLSL::Singleton().CompileShader(vertShaderSrc.c_str(), vertShaderSrc.length(), gfx::VertexShader), - fragShader = gfx::ShaderCompilerGLSL::Singleton().CompileShader(fragShaderSrc.c_str(), fragShaderSrc.length(), gfx::FragmentShader); - - // Register shader with the API. - auto& api = engine.GetApi(); - api.GetOptions().RegisterShader(&vertShader, &fragShader); - - // Set up engine for drawing. - engine.Setup(); - m_Camera.Transform().Origin(m_Camera.Transform().Forward() * -2.f); - - // Instantiate two Renderables in the scene graph, each with its own transform, using the same cube mesh data. - auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); - auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cubeX = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cubeY = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cubeZ = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - api.GetSceneGraph().AddChild(&cube); - api.GetSceneGraph().AddChild(&cube2); - api.GetSceneGraph().AddChild(&cubeX); - api.GetSceneGraph().AddChild(&cubeY); - api.GetSceneGraph().AddChild(&cubeZ); - - cubeX.GetTransform().SetPosition(-1.5f, 2.f, 0.f); - cubeY.GetTransform().SetPosition(0.f, 2.f, 0.f); - cubeZ.GetTransform().SetPosition(1.5f, 2.f, 0.f); - - // Update projection and view matrices with data from the camera object. - m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); - ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); - m_UniformState.viewMatrix = m_Camera.BuildViewMatrix(); - ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX))->Value((float*)m_UniformState.viewMatrix.data()); - - // Initialise the FOV variable and set up a callback so we can let the user adjust it with the mouse scroll wheel. - m_FOV = m_Camera.FOV(); - glfwSetScrollCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::scroll); - - float runningTime = glfwGetTime(); - - GLFWwindow* window = reinterpret_cast(windowing->GetWindowPtr()); - - // Set up mouse input for camera freelook. - glfwGetCursorPos(window, &m_MouseState.lastX, &m_MouseState.lastY); - glfwSetCursorPosCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::cursorPos); - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, 1); - - // Initialise all keys as released. - KeyboardState keys = { false, false, false, false, false }; - - float deltaTime = 0.f; - // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI * 0.25f)); - - while (isRunning) - { - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", cube2.GetTransform().Rotation().Axis().ToString().c_str()); - cube2.GetTransform().SetPosition(1.f * sinf(runningTime), 1.f * cosf(runningTime), -1.f); - windowing->Update(); // Update window before drawing - - bool eKeyPressedLastFrame = keys.e; - UpdateInput(keys, windowing); - // cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().Angle()).c_str()); - cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), deltaTime)); - cubeX.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), deltaTime)); - cubeY.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); - cubeZ.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); - if (!eKeyPressedLastFrame && keys.e) - { - // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); - } - Tick(deltaTime, keys); - UpdateUniforms(&api); - - engine.Render(100, 149, 237); - - float newRunningTime = glfwGetTime(); - deltaTime = newRunningTime - runningTime; - runningTime = newRunningTime; - - isRunning = windowing->Update(); - } - - Shutdown(windowing); - - return 0; + std::shared_ptr windowing = std::make_shared(800, 600); + + // Create new graphics engine instance + gfx::GraphicsApiGLOptions options = {}; + options.mainViewport = {800, 600}; + gfx::GraphicsEngine engine(&options, windowing); + + // Termination condition for main loop + bool isRunning = true; + + // Set the window as the current OpenGL context. + windowing->SetAsCurrentContext(); + + // Output start message to console + engine::ConsoleLogger::Global().LogInfo("", "main", "Demo starting!"); + + // Load & compile shaders. + std::string vertShaderSrc = system::FileSystem::Read("../../Content/GLSL/Unlit/RGBVertex.vert"), fragShaderSrc = system::FileSystem::Read("../../Content/GLSL/Unlit/RGBVertex.frag"); + gfx::ShaderCompiledResult + vertShader = gfx::ShaderCompilerGLSL::Singleton().CompileShader(vertShaderSrc.c_str(), vertShaderSrc.length(), gfx::VertexShader), + fragShader = gfx::ShaderCompilerGLSL::Singleton().CompileShader(fragShaderSrc.c_str(), fragShaderSrc.length(), gfx::FragmentShader); + + // Register shader with the API. + auto& api = engine.GetApi(); + api.GetOptions().RegisterShader(&vertShader, &fragShader); + + // Set up engine for drawing. + engine.Setup(); + m_Camera.Transform().Origin(m_Camera.Transform().Forward() * -2.f); + + // Instantiate two Renderables in the scene graph, each with its own transform, using the same cube mesh data. + auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); + auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + + // Also a cube to test each X-axis (pitch), Y-axis (yaw) and Z-axis (roll) rotation. + auto cubeX = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cubeY = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cubeZ = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + // api.GetSceneGraph().AddChild(&cube); + api.GetSceneGraph().AddChild(&cube2); + api.GetSceneGraph().AddChild(&cubeX); + api.GetSceneGraph().AddChild(&cubeY); + api.GetSceneGraph().AddChild(&cubeZ); + + cubeX.GetTransform().SetPosition(-1.5f, 2.f, 0.f); + cubeY.GetTransform().SetPosition(0.f, 2.f, 0.f); + cubeZ.GetTransform().SetPosition(1.5f, 2.f, 0.f); + + // Update projection and view matrices with data from the camera object. + m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); + ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); + m_UniformState.viewMatrix = m_Camera.BuildViewMatrix(); + ((lepus::gfx::GLMatrixUniformBinding*)api.GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX))->Value((float*)m_UniformState.viewMatrix.data()); + + // Initialise the FOV variable and set up a callback so we can let the user adjust it with the mouse scroll wheel. + m_FOV = m_Camera.FOV(); + glfwSetScrollCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::scroll); + + float runningTime = glfwGetTime(); + + GLFWwindow* window = reinterpret_cast(windowing->GetWindowPtr()); + + // Set up mouse input for camera freelook. + glfwGetCursorPos(window, &m_MouseState.lastX, &m_MouseState.lastY); + glfwSetCursorPosCallback(reinterpret_cast(windowing->GetWindowPtr()), DemoAppGLFWCallbacks::cursorPos); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, 1); + + // Initialise all keys as released. + KeyboardState keys = {false, false, false, false, false}; + + float deltaTime = 0.f; + // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI * 0.25f)); + + while (isRunning) + { + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", cube2.GetTransform().Rotation().Axis().ToString().c_str()); + cube2.GetTransform().SetPosition(1.f * sinf(runningTime), 1.f * cosf(runningTime), -1.f); + windowing->Update(); // Update window before drawing + + bool eKeyPressedLastFrame = keys.e; + UpdateInput(keys, windowing); + // cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().Angle()).c_str()); + // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), deltaTime)); + cubeX.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), deltaTime)); + cubeY.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + cubeZ.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); + + cube2.GetTransform().SetScale(fabs(cosf(runningTime))); + // cube.GetTransform().SetScale(fabs(cosf(runningTime))); + + // cubeX.GetTransform().SetScale(fabs(cosf(runningTime))); + // cubeY.GetTransform().SetScale(fabs(cosf(runningTime))); + // cubeZ.GetTransform().SetScale(fabs(cosf(runningTime))); + + if (!eKeyPressedLastFrame && keys.e) + { + // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); + lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); + } + Tick(deltaTime, keys); + UpdateUniforms(&api); + + engine.Render(100, 149, 237); + + float newRunningTime = glfwGetTime(); + deltaTime = newRunningTime - runningTime; + runningTime = newRunningTime; + + isRunning = windowing->Update(); + } + + Shutdown(windowing); + + return 0; } inline void UpdateInput(KeyboardState& keys, std::shared_ptr windowing) { - GLFWwindow* window = (GLFWwindow*)windowing->GetWindowPtr(); + GLFWwindow* window = (GLFWwindow*)windowing->GetWindowPtr(); - keys.w = glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS; - keys.a = glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS; - keys.s = glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS; - keys.d = glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS; - keys.e = glfwGetKey(window, GLFW_KEY_E) == GLFW_RELEASE; + keys.w = glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS; + keys.a = glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS; + keys.s = glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS; + keys.d = glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS; + keys.e = glfwGetKey(window, GLFW_KEY_E) == GLFW_RELEASE; } inline void UpdateUniforms(gfx::GraphicsApi* api) { - m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); - m_UniformState.viewMatrix = m_Camera.BuildViewMatrix(); + m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); + m_UniformState.viewMatrix = m_Camera.BuildViewMatrix(); - ((lepus::gfx::GLMatrixUniformBinding*)api->GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); - ((lepus::gfx::GLMatrixUniformBinding*)api->GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX))->Value((float*)m_UniformState.viewMatrix.data()); + ((lepus::gfx::GLMatrixUniformBinding*)api->GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX))->Value((float*)m_UniformState.projMatrix.data()); + ((lepus::gfx::GLMatrixUniformBinding*)api->GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX))->Value((float*)m_UniformState.viewMatrix.data()); } inline void Tick(float deltaTime, const KeyboardState& keys) { - m_Camera.FOV(m_FOV); - - lepus::types::Vector3 forwardDelta, rightDelta; - - // Process camera movement input based on keyboard state. - if (keys.s) - { - forwardDelta = forwardDelta - (m_Camera.Transform().Forward() * deltaTime * _camSpeed); - } - if (keys.w) - { - forwardDelta = forwardDelta + (m_Camera.Transform().Forward() * deltaTime * _camSpeed); - } - if (keys.d) - { - rightDelta = rightDelta + (m_Camera.Transform().Right() * deltaTime * _camSpeed); - } - if (keys.a) - { - rightDelta = rightDelta - (m_Camera.Transform().Right() * deltaTime * _camSpeed); - } - - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", m_Camera.Transform().Rotation().Axis().ToString().c_str()); - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(m_Camera.Transform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); - - m_Camera.Transform().Origin(m_Camera.Transform().Origin() + forwardDelta + rightDelta); + m_Camera.FOV(m_FOV); + + lepus::types::Vector3 forwardDelta, rightDelta; + + // Process camera movement input based on keyboard state. + if (keys.s) + { + forwardDelta = forwardDelta - (m_Camera.Transform().Forward() * deltaTime * _camSpeed); + } + if (keys.w) + { + forwardDelta = forwardDelta + (m_Camera.Transform().Forward() * deltaTime * _camSpeed); + } + if (keys.d) + { + rightDelta = rightDelta + (m_Camera.Transform().Right() * deltaTime * _camSpeed); + } + if (keys.a) + { + rightDelta = rightDelta - (m_Camera.Transform().Right() * deltaTime * _camSpeed); + } + + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", m_Camera.Transform().Rotation().Axis().ToString().c_str()); + // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(m_Camera.Transform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); + + m_Camera.Transform().Origin(m_Camera.Transform().Origin() + forwardDelta + rightDelta); } inline void Shutdown(std::shared_ptr windowing) { - // Output shutdown message to console - engine::ConsoleLogger::Global().LogInfo("", "main", "Demo shutting down!"); + // Output shutdown message to console + engine::ConsoleLogger::Global().LogInfo("", "main", "Demo shutting down!"); - windowing->Shutdown(); + windowing->Shutdown(); - system::WindowingGLFW::Terminate(); + system::WindowingGLFW::Terminate(); - engine::ConsoleLogger::Shutdown(); + engine::ConsoleLogger::Shutdown(); - m_Initialised = false; + m_Initialised = false; } }; diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h index 17a9499..a7a316e 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h @@ -14,134 +14,143 @@ namespace lepus { - namespace gfx + namespace gfx + { + class GraphicsApiGLOptions : public GraphicsApiOptions { - class GraphicsApiGLOptions : public GraphicsApiOptions + public: + static const size_t ProgramCount = 8; + + private: + GLuint m_FragmentShaders[ProgramCount]; + GLuint m_VertexShaders[ProgramCount]; + size_t m_ShaderCount = 0; + + public: + GraphicsApiType GetType() override { return GraphicsApiOpenGL; } + + GraphicsApiGLOptions() + : GraphicsApiOptions() + { + // Zero the shader arrays. + memset(m_FragmentShaders, 0, ProgramCount * sizeof(GLuint)); + memset(m_VertexShaders, 0, ProgramCount * sizeof(GLuint)); + } + + inline GLuint const GetFragmentShader(size_t index) { return m_FragmentShaders[index]; } + inline GLuint const GetVertexShader(size_t index) { return m_VertexShaders[index]; } + + const size_t RegisterShader(GLShaderCompiledResult const* vertexShader = nullptr, GLShaderCompiledResult const* fragShader = nullptr, GLShaderCompiledResult const* geomShader = nullptr) + { + assert(m_ShaderCount < ProgramCount); + + if (vertexShader) { - public: - static const size_t ProgramCount = 8; - private: - GLuint m_FragmentShaders[ProgramCount]; - GLuint m_VertexShaders[ProgramCount]; - size_t m_ShaderCount = 0; - public: - GraphicsApiType GetType() override { return GraphicsApiOpenGL; } - - GraphicsApiGLOptions() : GraphicsApiOptions() - { - // Zero the shader arrays. - memset(m_FragmentShaders, 0, ProgramCount * sizeof(GLuint)); - memset(m_VertexShaders, 0, ProgramCount * sizeof(GLuint)); - } - - inline GLuint const GetFragmentShader(size_t index) { return m_FragmentShaders[index]; } - inline GLuint const GetVertexShader(size_t index) { return m_VertexShaders[index]; } - - const size_t RegisterShader(GLShaderCompiledResult const* vertexShader = nullptr, GLShaderCompiledResult const* fragShader = nullptr, GLShaderCompiledResult const* geomShader = nullptr) - { - assert(m_ShaderCount < ProgramCount); - - if (vertexShader) - { - m_VertexShaders[m_ShaderCount] = vertexShader->ShaderHandle; - } - - if (fragShader) - { - m_FragmentShaders[m_ShaderCount] = fragShader->ShaderHandle; - } - - if (geomShader) - { - // TODO - } - - return m_ShaderCount++; - } - }; - - typedef lepus::gfx::SceneGraph> GLSceneGraph; - - template GraphicsApiGLOptions& GraphicsApi::GetOptions(); - - class GraphicsApiGL : public GraphicsApi + m_VertexShaders[m_ShaderCount] = vertexShader->ShaderHandle; + } + + if (fragShader) { - friend class GraphicsApiGLOptions; - private: - static const uint8_t _meshCount = 2; - struct - { - /// @brief Handle to the vertex array objects. - GLuint vao; + m_FragmentShaders[m_ShaderCount] = fragShader->ShaderHandle; + } - /// @brief List with all uniforms used by the API. - // TODO: Change to array - might get better cache/locality to improve access times. - std::forward_list*> uniforms; + if (geomShader) + { + // TODO + } - /// @brief Uniform map to update the values. - // TODO: Move to a Material class? - std::unordered_map*> uniformMap; - } m_Pipeline; + return m_ShaderCount++; + } + }; - GLuint m_Programs[GraphicsApiGLOptions::ProgramCount]; + typedef lepus::gfx::SceneGraph> GLSceneGraph; - GLSceneGraph m_Scene; + template GraphicsApiGLOptions& GraphicsApi::GetOptions(); - private: - void SetupVertexArrays(); - void SetupBuffers(); - void SetupShaders(); - void SetupUniforms(); + class GraphicsApiGL : public GraphicsApi + { + friend class GraphicsApiGLOptions; - private: - inline void* GetUniformInternal(char* name) override - { - size_t targetKeyLength = strlen(name); + private: + static const uint8_t _meshCount = 2; + struct + { + /// @brief Handle to the vertex array objects. + GLuint vao; + + /// @brief List with all uniforms used by the API. + // TODO: Change to array - might get better cache/locality to improve access times. + std::forward_list*> uniforms; + + /// @brief Uniform map to update the values. + // TODO: Move to a Material class? + std::unordered_map*> uniformMap; + } m_Pipeline; + + GLuint m_Programs[GraphicsApiGLOptions::ProgramCount]; + + GLSceneGraph m_Scene; + + private: + void SetupVertexArrays(); + void SetupBuffers(); + void SetupShaders(); + void SetupUniforms(); + + private: + inline void* GetUniformInternal(char* name) override + { + size_t targetKeyLength = strlen(name); + + // TODO: unordered_map doesn't really work with string keys... add actual hashing! + for (auto it = m_Pipeline.uniformMap.begin(); it != m_Pipeline.uniformMap.end(); it++) + { + size_t keyLength = strlen(it->first); + if (targetKeyLength == keyLength && !strcmp(name, it->first)) + { + return it->second; + } + } - // TODO: unordered_map doesn't really work with string keys... add actual hashing! - for (auto it = m_Pipeline.uniformMap.begin(); it != m_Pipeline.uniformMap.end(); it++) - { - size_t keyLength = strlen(it->first); - if (targetKeyLength == keyLength && !strcmp(name, it->first)) - { - return it->second; - } - } + return nullptr; + } - return nullptr; - } + public: + GraphicsApiGL(GraphicsApiGLOptions options) + { + GraphicsApiGL::Init(&options); + } - public: - GraphicsApiGL(GraphicsApiGLOptions options) - { - Init(&options); - } + GraphicsApiGL(GraphicsApiGLOptions* options) + { + GraphicsApiGL::Init(options); + } - void Init(GraphicsApiOptions* options) override; + void Init(GraphicsApiOptions* options) override; - void CreatePipeline() override; + void CreatePipeline() override; - void UpdateUniforms() override; + void UpdateUniforms() override; - inline GLSceneGraph& GetSceneGraph() - { - return m_Scene; - } + inline GLSceneGraph& GetSceneGraph() + { + return m_Scene; + } - void Draw() override; + void Draw() override; - void ClearFrameBuffer(float r, float g, float b) override; + void ClearFrameBuffer(float r, float g, float b) override; - /// @brief Dummy method as OpenGL itself doesn't need to do anything for the swap chain to work. - void SwapBuffers() override {} + /// @brief Dummy method as OpenGL itself doesn't need to do anything for the swap chain to work. + void SwapBuffers() override {} - void Shutdown() override; - }; + void Shutdown() override; + }; - template const lepus::gfx::GLUniformBinding* GraphicsApi::GetUniform*>(char* name); + template const lepus::gfx::GLUniformBinding* GraphicsApi::GetUniform*>(char* name); - template GraphicsApiGL& GraphicsEngine::GetApi(); - } -} + template GraphicsApiGL& GraphicsEngine::GetApi(); + } // namespace gfx +} // namespace lepus #endif \ No newline at end of file diff --git a/src/lepus/gfx/GraphicsEngine/GraphicsApi.h b/src/lepus/gfx/GraphicsEngine/GraphicsApi.h index 410dc99..57b317c 100644 --- a/src/lepus/gfx/GraphicsEngine/GraphicsApi.h +++ b/src/lepus/gfx/GraphicsEngine/GraphicsApi.h @@ -38,10 +38,7 @@ namespace lepus /// @brief Main viewport used by the application. lepus::types::Viewport mainViewport = {}; - virtual ~GraphicsApiOptions() - { - - } + virtual ~GraphicsApiOptions() {} }; /// @brief API wrapper to be used by GraphicsEngine. @@ -84,15 +81,25 @@ namespace lepus GraphicsApi(GraphicsApiOptions* options) { - Init(options); - m_ShutdownCalled = false; + m_Options = nullptr; + GraphicsApi::Init(options); } - /// @brief Initialises the API with the provided options. - /// @param options An options object using a GraphicsApiOptions type for the requested API. - /// Make sure you don't pass a pointer to a new object here. Implementations of this class must copy the options, - /// not reference a pointer, so there's a potential risk of a memory leak there. - virtual void Init(GraphicsApiOptions* options) = 0; + /// @brief Initialises the API with the provided + /// options. + /// @param options An options object using a + /// GraphicsApiOptions type for the requested API. Make + /// sure you don't pass a pointer to a new object here. + /// Implementations of this class must copy the options, + /// not reference a pointer, so there's a potential risk + /// of a memory leak there. + virtual inline void Init(GraphicsApiOptions* options) + { + assert(options == nullptr); + + InitInternal(options); + m_ShutdownCalled = false; + } /// @brief Obtains the options object this GraphicsApi was initialised with. /// @@ -149,7 +156,7 @@ namespace lepus Shutdown(); } }; - } -} + } // namespace gfx +} // namespace lepus #endif \ No newline at end of file diff --git a/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp b/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp index 64ebed4..d8f7996 100644 --- a/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp +++ b/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp @@ -22,12 +22,14 @@ void GraphicsEngine::InitApi(GraphicsApiOptions* options) switch (options->GetType()) { case GraphicsApiType::GraphicsApiOpenGL: - m_Api = new GraphicsApiGL(*(GraphicsApiGLOptions*)options); + m_Api = new GraphicsApiGL(*dynamic_cast(options)); break; case GraphicsApiType::GraphicsApiVulkan: // TODO + break; case GraphicsApiType::GraphicsApiTest: // Ignore test/mock APIs. + break; case GraphicsApiType::GraphicsApiUnknown: default: // Assert if the API type is not part of the enum. @@ -41,7 +43,7 @@ void GraphicsEngine::Setup() m_Api->CreatePipeline(); } -void GraphicsEngine::Render(float r, float g, float b) +void GraphicsEngine::Render(const float r, const float g, const float b) { m_Api->ClearFrameBuffer(r, g, b); diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index f088165..91e30ba 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -8,153 +8,180 @@ namespace lepus { namespace math { - class Transform - { - private: - lepus::types::Vector3 m_Origin, m_Forward, m_Right, m_Up; - // axis-angle - lepus::types::Quaternion m_Rotation; - - public: - Transform() - { - m_Origin = lepus::types::Vector3(); - - m_Forward = lepus::types::Vector3(0.f, 0.f, 1.f); - m_Right = lepus::types::Vector3(1.f, 0.f, 0.f); - m_Up = lepus::types::Vector3(0.f, 1.f, 0.f); - - m_Rotation = lepus::types::Quaternion(); - } - - Transform(Transform&& transform) - { - m_Origin = transform.Origin(); - m_Forward = transform.Forward(); - m_Up = transform.Up(); - m_Right = transform.Right(); - m_Rotation = transform.Rotation(); - } - - Transform(const Transform& transform) - { - m_Origin = transform.Origin(); - m_Forward = transform.Forward(); - m_Up = transform.Up(); - m_Right = transform.Right(); - m_Rotation = transform.Rotation(); - } - - Transform& operator=(const Transform& transform) - { - m_Origin = transform.Origin(); - m_Forward = transform.Forward(); - m_Up = transform.Up(); - m_Right = transform.Right(); - m_Rotation = transform.Rotation(); - - return *this; - } - - inline lepus::types::Vector3 Origin() const { return m_Origin; } - inline void Origin(const lepus::types::Vector3& vec) - { - m_Origin.x(vec.x()); - m_Origin.y(vec.y()); - m_Origin.z(vec.z()); - } - - inline lepus::types::Vector3 Forward() const { return lepus::types::Vector3(m_Forward); } - inline lepus::types::Vector3 Right() const { return lepus::types::Vector3(m_Right); } - inline lepus::types::Vector3 Up() const { return lepus::types::Vector3(m_Up); } - - inline lepus::types::Quaternion Rotation() const { return m_Rotation; } - inline void Rotation(const lepus::types::Quaternion& newRotation) - { - m_Rotation.x(newRotation.x()); - m_Rotation.y(newRotation.y()); - m_Rotation.z(newRotation.z()); - m_Rotation.w(newRotation.w()); - } - - inline void Rotate(const lepus::types::Quaternion& quat) - { - m_Rotation = m_Rotation * quat; - - // Rotate the basis vectors - auto newForward = quat.Rotate(m_Forward); - auto newRight = quat.Rotate(m_Right); - auto newUp = quat.Rotate(m_Up); - - // Set basis vectors to rotated - m_Forward.x(newForward.x());m_Forward.y(newForward.y());m_Forward.z(newForward.z()); - m_Right.x(newRight.x());m_Right.y(newRight.y());m_Right.z(newRight.z()); - m_Up.x(newUp.x());m_Up.y(newUp.y());m_Up.z(newUp.z()); - } - - static inline lepus::math::Matrix4x4 AxisAngle(lepus::types::Vector3& axis, float angle) - { - lepus::math::Matrix4x4 ret = lepus::math::Matrix4x4::Identity(); - - lepus::types::Vector3 unitAxis = axis * (1.f / axis.Magnitude()); - - float c = cosf(angle); - float s = sinf(angle); - - ret.set<0, 0>(c + (unitAxis.x() * unitAxis.x() * (1.f - c))); - ret.set<1, 0>((unitAxis.y() * unitAxis.x() * (1.f - c)) + (unitAxis.z() * s)); - ret.set<2, 0>((unitAxis.z() * unitAxis.x() * (1.f - c)) - (unitAxis.y() * s)); - - ret.set<0, 1>((unitAxis.y() * unitAxis.x() * (1.f - c)) - (unitAxis.z() * s)); - ret.set<1, 1>(c + (unitAxis.y() * unitAxis.y() * (1.f - c))); - ret.set<2, 1>((unitAxis.z() * unitAxis.y() * (1.f - c)) + (unitAxis.x() * s)); - - ret.set<0, 2>((unitAxis.z() * unitAxis.x() * (1.f - c)) + (unitAxis.y() * s)); - ret.set<1, 2>((unitAxis.z() * unitAxis.y() * (1.f - c)) - (unitAxis.x() * s)); - ret.set<2, 2>(c + (unitAxis.z() * unitAxis.z() * (1.f - c))); - - return ret; - } - - inline void SetPosition(float x, float y, float z) - { - m_Origin.x(x); - m_Origin.y(y); - m_Origin.z(z); - } - - inline lepus::math::Matrix4x4 BuildMatrix() const - { - lepus::math::Matrix4x4 model = lepus::math::Matrix4x4::Identity(); - - // Translation - model.set<0, 3>(m_Origin.x()); - model.set<1, 3>(m_Origin.y()); - model.set<2, 3>(m_Origin.z()); - - // TODO: add scaling and rotation - lepus::types::Quaternion normRot = m_Rotation.Normalised(); - lepus::types::Vector3 axis = normRot.Axis(); - //axis = axis * (1.f / axis.Magnitude()); - float angle = normRot.Angle(); - - float cosTheta = cosf(angle); - float invCosTheta = 1.f - cosTheta; - float sinTheta = sinf(angle); - model.set<0, 0>(axis.x() * axis.x() * invCosTheta + cosTheta); - model.set<0, 1>(axis.x() * axis.y() * invCosTheta + axis.z() * sinTheta); - model.set<1, 1>(axis.y() * axis.y() * invCosTheta + cosTheta); - model.set<1, 0>(axis.y() * axis.x() * invCosTheta - axis.z() * sinTheta); - model.set<2, 0>(axis.z() * axis.x() * invCosTheta + axis.y() * sinTheta); - model.set<2, 1>(axis.z() * axis.y() * invCosTheta - axis.x() * sinTheta); - model.set<0, 2>(axis.x() * axis.z() * invCosTheta - axis.y() * sinTheta); - model.set<1, 2>(axis.y() * axis.z() * invCosTheta + axis.x() * sinTheta); - model.set<2, 2>(axis.z() * axis.z() * invCosTheta + cosTheta); - - return model; - } - }; - } -} + class Transform + { + private: + lepus::types::Vector3 m_Origin, m_Forward, m_Right, m_Up; + // axis-angle + lepus::types::Quaternion m_Rotation; + + lepus::types::Vector3 m_Scale; + + public: + Transform() + { + m_Origin = lepus::types::Vector3(); + + m_Forward = lepus::types::Vector3(0.f, 0.f, 1.f); + m_Right = lepus::types::Vector3(1.f, 0.f, 0.f); + m_Up = lepus::types::Vector3(0.f, 1.f, 0.f); + + m_Rotation = lepus::types::Quaternion(); + + m_Scale = lepus::types::Vector3(1.f, 1.f, 1.f); + } + + Transform(Transform&& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + m_Scale = transform.Scale(); + } + + Transform(const Transform& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + m_Scale = transform.Scale(); + } + + Transform& operator=(const Transform& transform) + { + m_Origin = transform.Origin(); + m_Forward = transform.Forward(); + m_Up = transform.Up(); + m_Right = transform.Right(); + m_Rotation = transform.Rotation(); + m_Scale = transform.Scale(); + + return *this; + } + + inline lepus::types::Vector3 Origin() const { return m_Origin; } + inline void Origin(const lepus::types::Vector3& vec) + { + m_Origin.x(vec.x()); + m_Origin.y(vec.y()); + m_Origin.z(vec.z()); + } + + inline lepus::types::Vector3 Forward() const { return lepus::types::Vector3(m_Forward); } + inline lepus::types::Vector3 Right() const { return lepus::types::Vector3(m_Right); } + inline lepus::types::Vector3 Up() const { return lepus::types::Vector3(m_Up); } + + inline lepus::types::Quaternion Rotation() const { return m_Rotation; } + inline void Rotation(const lepus::types::Quaternion& newRotation) + { + m_Rotation.x(newRotation.x()); + m_Rotation.y(newRotation.y()); + m_Rotation.z(newRotation.z()); + m_Rotation.w(newRotation.w()); + } + + inline void Rotate(const lepus::types::Quaternion& quat) + { + m_Rotation = m_Rotation * quat; + + // Rotate the basis vectors + auto newForward = quat.Rotate(m_Forward); + auto newRight = quat.Rotate(m_Right); + auto newUp = quat.Rotate(m_Up); + + // Set basis vectors to rotated + m_Forward.x(newForward.x()); + m_Forward.y(newForward.y()); + m_Forward.z(newForward.z()); + m_Right.x(newRight.x()); + m_Right.y(newRight.y()); + m_Right.z(newRight.z()); + m_Up.x(newUp.x()); + m_Up.y(newUp.y()); + m_Up.z(newUp.z()); + } + + static inline lepus::math::Matrix4x4 AxisAngle(lepus::types::Vector3& axis, float angle) + { + lepus::math::Matrix4x4 ret = lepus::math::Matrix4x4::Identity(); + + lepus::types::Vector3 unitAxis = axis * (1.f / axis.Magnitude()); + + float c = cosf(angle); + float s = sinf(angle); + + ret.set<0, 0>(c + (unitAxis.x() * unitAxis.x() * (1.f - c))); + ret.set<1, 0>((unitAxis.y() * unitAxis.x() * (1.f - c)) + (unitAxis.z() * s)); + ret.set<2, 0>((unitAxis.z() * unitAxis.x() * (1.f - c)) - (unitAxis.y() * s)); + + ret.set<0, 1>((unitAxis.y() * unitAxis.x() * (1.f - c)) - (unitAxis.z() * s)); + ret.set<1, 1>(c + (unitAxis.y() * unitAxis.y() * (1.f - c))); + ret.set<2, 1>((unitAxis.z() * unitAxis.y() * (1.f - c)) + (unitAxis.x() * s)); + + ret.set<0, 2>((unitAxis.z() * unitAxis.x() * (1.f - c)) + (unitAxis.y() * s)); + ret.set<1, 2>((unitAxis.z() * unitAxis.y() * (1.f - c)) - (unitAxis.x() * s)); + ret.set<2, 2>(c + (unitAxis.z() * unitAxis.z() * (1.f - c))); + + return ret; + } + + inline lepus::types::Vector3 Scale() const { return m_Scale; } + + inline void SetPosition(float x, float y, float z) + { + m_Origin.x(x); + m_Origin.y(y); + m_Origin.z(z); + } + + inline void SetScale(float scale) + { + this->SetScale(scale, scale, scale); + } + + inline void SetScale(float x, float y, float z) + { + m_Scale.x(x); + m_Scale.y(y); + m_Scale.z(z); + } + + inline lepus::math::Matrix4x4 BuildMatrix() const + { + lepus::math::Matrix4x4 model = lepus::math::Matrix4x4::Identity(); + + // Translation + model.set<0, 3>(m_Origin.x()); + model.set<1, 3>(m_Origin.y()); + model.set<2, 3>(m_Origin.z()); + + // TODO: add scaling and rotation + lepus::types::Quaternion normRot = m_Rotation.Normalised(); + lepus::types::Vector3 axis = normRot.Axis(); + // axis = axis * (1.f / axis.Magnitude()); + float angle = normRot.Angle(); + + float cosTheta = cosf(angle); + float invCosTheta = 1.f - cosTheta; + float sinTheta = sinf(angle); + model.set<0, 0>(m_Scale.x() * ((axis.x() * axis.x()) * invCosTheta + cosTheta)); + model.set<0, 1>(m_Scale.y() * (axis.x() * axis.y() * invCosTheta + axis.z() * sinTheta)); + model.set<1, 1>(m_Scale.y() * ((axis.y() * axis.y()) * invCosTheta + cosTheta)); + model.set<1, 0>(m_Scale.x() * (axis.y() * axis.x() * invCosTheta - axis.z() * sinTheta)); + model.set<2, 0>(m_Scale.x() * (axis.z() * axis.x() * invCosTheta + axis.y() * sinTheta)); + model.set<2, 1>(m_Scale.y() * (axis.z() * axis.y() * invCosTheta - axis.x() * sinTheta)); + model.set<0, 2>(m_Scale.z() * (axis.x() * axis.z() * invCosTheta - axis.y() * sinTheta)); + model.set<1, 2>(m_Scale.z() * (axis.y() * axis.z() * invCosTheta + axis.x() * sinTheta)); + model.set<2, 2>(m_Scale.z() * ((axis.z() * axis.z()) * invCosTheta + cosTheta)); + + return model; + } + }; + } // namespace math +} // namespace lepus #endif From f5efd1e25e3cb1caf0e8023d8329001b071ce23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Tue, 8 Oct 2024 22:13:00 +0100 Subject: [PATCH 09/31] refactor: split SceneGraph::Node into separate SceneNode class --- src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h | 2 +- .../gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp | 212 +++++++++--------- src/lepus/gfx/SceneGraph.h | 162 +++++-------- src/lepus/gfx/SceneGraph/Renderable.h | 106 +++++---- src/lepus/gfx/SceneGraph/SceneNode.h | 74 ++++++ src/lepus/gfx/SceneGraph/Transformable.h | 35 +++ 6 files changed, 324 insertions(+), 267 deletions(-) create mode 100644 src/lepus/gfx/SceneGraph/SceneNode.h create mode 100644 src/lepus/gfx/SceneGraph/Transformable.h diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h index a7a316e..c6df153 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h @@ -63,7 +63,7 @@ namespace lepus } }; - typedef lepus::gfx::SceneGraph> GLSceneGraph; + typedef lepus::gfx::SceneGraph GLSceneGraph; template GraphicsApiGLOptions& GraphicsApi::GetOptions(); diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp index ba5dbab..3031c73 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp @@ -4,160 +4,160 @@ using namespace lepus::gfx; void GraphicsApiGL::Init(GraphicsApiOptions* options) { - InitInternal((GraphicsApiGLOptions*)options); + InitInternal((GraphicsApiGLOptions*)options); - m_Scene = GLSceneGraph(); + m_Scene = GLSceneGraph(); } void GraphicsApiGL::SetupVertexArrays() { - // Create a global VAO and bind it. - glCreateVertexArrays(1, &m_Pipeline.vao); - glBindVertexArray(m_Pipeline.vao); + // Create a global VAO and bind it. + glCreateVertexArrays(1, &m_Pipeline.vao); + glBindVertexArray(m_Pipeline.vao); } // TODO: rename this, or move to SetupVertexArrays? void GraphicsApiGL::SetupBuffers() { - glBindVertexArray(m_Pipeline.vao); + glBindVertexArray(m_Pipeline.vao); } void GraphicsApiGL::SetupShaders() { - // Zero the program array. - memset(m_Programs, 0, GraphicsApiGLOptions::ProgramCount * sizeof(GLuint)); - - for (size_t i = 0; i < GraphicsApiGLOptions::ProgramCount; i++) + // Zero the program array. + memset(m_Programs, 0, GraphicsApiGLOptions::ProgramCount * sizeof(GLuint)); + + for (size_t i = 0; i < GraphicsApiGLOptions::ProgramCount; i++) + { + auto& options = GetOptions(); + GLuint fragShader = options.GetFragmentShader(i), vertShader = options.GetVertexShader(i); + if (fragShader && vertShader) { - auto& options = GetOptions(); - GLuint fragShader = options.GetFragmentShader(i), vertShader = options.GetVertexShader(i); - if (fragShader && vertShader) - { - m_Programs[i] = glCreateProgram(); - glAttachShader(m_Programs[i], fragShader); - glAttachShader(m_Programs[i], vertShader); - glLinkProgram(m_Programs[i]); - } + m_Programs[i] = glCreateProgram(); + glAttachShader(m_Programs[i], fragShader); + glAttachShader(m_Programs[i], vertShader); + glLinkProgram(m_Programs[i]); } + } } void GraphicsApiGL::SetupUniforms() { - // Proj matrix - auto* proj = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX)); - m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(proj)); - m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX, reinterpret_cast*>(proj)); - - // View matrix - auto* view = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX)); - m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(view)); - m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX, reinterpret_cast*>(view)); - - // Model matrix - auto* model = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX)); - m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(model)); - m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX, reinterpret_cast*>(model)); + // Proj matrix + auto* proj = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX)); + m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(proj)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX, reinterpret_cast*>(proj)); + + // View matrix + auto* view = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX)); + m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(view)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX, reinterpret_cast*>(view)); + + // Model matrix + auto* model = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX)); + m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(model)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX, reinterpret_cast*>(model)); } void GraphicsApiGL::CreatePipeline() { - SetupVertexArrays(); - SetupBuffers(); - SetupShaders(); - SetupUniforms(); - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CCW); + SetupVertexArrays(); + SetupBuffers(); + SetupShaders(); + SetupUniforms(); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); } void GraphicsApiGL::UpdateUniforms() { - for (auto uniform = m_Pipeline.uniforms.begin(); uniform != m_Pipeline.uniforms.end(); uniform++) + for (auto uniform = m_Pipeline.uniforms.begin(); uniform != m_Pipeline.uniforms.end(); uniform++) + { + auto uniformVal = *uniform; + if (uniformVal->IsDirty()) { - auto uniformVal = *uniform; - if (uniformVal->IsDirty()) - { - const GLint& location = uniformVal->Location(); - switch (uniformVal->Type()) - { - // TODO: with more shaders, we'll want to wrap the uniform objects in "instances" that specify a program handle and hold a reference to the uniform data - // However, it'll work for now given there is only ever one GL program in use. - case lepus::gfx::UniformType::MATRIX4: - glUniformMatrix4fv(location, 1, true, (reinterpret_cast(uniformVal))->Value()); - break; - case lepus::gfx::UniformType::FLOAT: - glUniform1f(location, (reinterpret_cast(uniformVal))->Value()); - break; - } - } + const GLint& location = uniformVal->Location(); + switch (uniformVal->Type()) + { + // TODO: with more shaders, we'll want to wrap the uniform objects in "instances" that specify a program handle and hold a reference to the uniform data + // However, it'll work for now given there is only ever one GL program in use. + case lepus::gfx::UniformType::MATRIX4: + glUniformMatrix4fv(location, 1, true, (reinterpret_cast(uniformVal))->Value()); + break; + case lepus::gfx::UniformType::FLOAT: + glUniform1f(location, (reinterpret_cast(uniformVal))->Value()); + break; + } } + } } void GraphicsApiGL::Draw() { - glUseProgram(m_Programs[0]); + glUseProgram(m_Programs[0]); + + glBindVertexArray(m_Pipeline.vao); - glBindVertexArray(m_Pipeline.vao); + const GLSceneGraph::Node* currentNode = m_Scene.Root(); - const GLSceneGraph::Node* currentNode = m_Scene.Root(); + bool branchComplete = false; - bool branchComplete = false; + while (currentNode) + { + if (!branchComplete && !currentNode->IsRoot()) + { + typedef lepus::gfx::Renderable GLRenderable; + auto renderable = (const GLRenderable*)(currentNode->GetTransformable()); + if (renderable) + { + lepus::math::Matrix4x4 modelMat = renderable->GetTransform().BuildMatrix(); + auto modelMatUniformLoation = GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX); + // TODO: integrate this with UpdateUniforms somehow, the model matrix uniform needs to be different for each renderable! + glUniformMatrix4fv(modelMatUniformLoation->Location(), 1, true, modelMat.data()); + + glBindBuffer(GL_ARRAY_BUFFER, renderable->GetMesh()->GetVBO()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderable->GetMesh()->GetIBO()); + + glDrawElements(GL_TRIANGLES, (GLsizei)renderable->GetMesh()->IndexCount(), GL_UNSIGNED_INT, 0); + } + } - while (currentNode) + if (!branchComplete && currentNode->FirstChild()) + { + currentNode = currentNode->FirstChild(); + } + else if (currentNode->NextSibling()) + { + currentNode = currentNode->NextSibling(); + branchComplete = false; + } + else { - if (!branchComplete && !currentNode->IsRoot()) - { - typedef lepus::gfx::Renderable GLRenderable; - const GLRenderable* renderable = currentNode->GetRenderable(); - if (renderable) - { - lepus::math::Matrix4x4 modelMat = renderable->GetTransform().BuildMatrix(); - auto modelMatUniformLoation = GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX); - // TODO: integrate this with UpdateUniforms somehow, the model matrix uniform needs to be different for each renderable! - glUniformMatrix4fv(modelMatUniformLoation->Location(), 1, true, modelMat.data()); - - glBindBuffer(GL_ARRAY_BUFFER, renderable->GetMesh()->GetVBO()); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderable->GetMesh()->GetIBO()); - - glDrawElements(GL_TRIANGLES, (GLsizei)renderable->GetMesh()->IndexCount(), GL_UNSIGNED_INT, 0); - } - } - - if (!branchComplete && currentNode->FirstChild()) - { - currentNode = currentNode->FirstChild(); - } - else if (currentNode->NextSibling()) - { - currentNode = currentNode->NextSibling(); - branchComplete = false; - } - else - { - branchComplete = true; - currentNode = currentNode->Parent(); - } + branchComplete = true; + currentNode = currentNode->Parent(); } + } - // for (uint8_t meshIndex = 0; meshIndex < _meshCount; meshIndex++) - // { - // glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo[meshIndex]); - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo[meshIndex]); + // for (uint8_t meshIndex = 0; meshIndex < _meshCount; meshIndex++) + // { + // glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo[meshIndex]); + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo[meshIndex]); - // glDrawElements(GL_TRIANGLES, (GLsizei)m_Meshes[meshIndex].IndexCount(), GL_UNSIGNED_INT, 0); - // } + // glDrawElements(GL_TRIANGLES, (GLsizei)m_Meshes[meshIndex].IndexCount(), GL_UNSIGNED_INT, 0); + // } } void GraphicsApiGL::ClearFrameBuffer(float r, float g, float b) { - glClearColor(r, g, b, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(r, g, b, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void GraphicsApiGL::Shutdown() { - GraphicsApi::Shutdown(); + GraphicsApi::Shutdown(); } \ No newline at end of file diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index 1237b1d..0d7155c 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -6,116 +6,54 @@ namespace lepus { namespace gfx { - template> - class SceneGraph - { - public: - - class Node - { - private: - friend class SceneGraph; - - protected: - Node* m_Sibling; - Node* m_Child; - Node* m_Parent; - const RenderableType* m_Renderable; - - public: - Node() - { - m_Sibling = nullptr; - m_Child = nullptr; - m_Parent = nullptr; - m_Renderable = nullptr; - } - - ~Node() - { - - } - - inline const RenderableType* GetRenderable() const { return m_Renderable; } - inline const Node* NextSibling() const { return m_Sibling; } - inline const Node* Parent() const { return m_Parent; } - inline const Node* FirstChild() const { return m_Child; } - - inline bool IsRoot() const { return Parent() == nullptr; } - - inline const Node* AddChild(const RenderableType* renderable) - { - Node* currentNode = m_Child; - Node* lastChild = m_Child; - while (currentNode) - { - lastChild = currentNode; - currentNode = currentNode->m_Sibling; - } - - if (!currentNode) - { - currentNode = new Node(); - currentNode->m_Renderable = renderable; - currentNode->m_Parent = this; - } - - if (!m_Child) - { - m_Child = currentNode; - } - else if (lastChild && !lastChild->m_Sibling) - { - lastChild->m_Sibling = currentNode; - } - - return currentNode; - } - }; - - protected: - Node* m_Root; - - public: - - SceneGraph() - { - m_Root = new Node(); - } - - const Node* AddChild(const RenderableType* renderable) - { - return m_Root->AddChild(renderable); - } - - ~SceneGraph() - { - Node* currentNode = m_Root->m_Child; - Node* parent = m_Root; - - // Delete all children - while (currentNode) - { - if (currentNode->m_Child) - { - parent = currentNode; - currentNode = currentNode->m_Child; - } - else - { - bool wasRoot = currentNode->IsRoot(); - parent->m_Child = currentNode->m_Sibling; - delete currentNode; - currentNode = wasRoot ? nullptr : parent; - } - } - } - - const Node* Root() const - { - return m_Root; - } - }; - } -} + class SceneGraph + { + public: + typedef SceneNode Node; + + protected: + Node* m_Root; + + public: + SceneGraph() + { + m_Root = new Node(); + } + + template + const Node* AddChild(const TransformableType* transformable) + { + return m_Root->AddChild((const Transformable*)transformable); + } + + ~SceneGraph() + { + Node* currentNode = m_Root->m_Child; + Node* parent = m_Root; + + // Delete all children + while (currentNode) + { + if (currentNode->m_Child) + { + parent = currentNode; + currentNode = currentNode->m_Child; + } + else + { + bool wasRoot = currentNode->IsRoot(); + parent->m_Child = currentNode->m_Sibling; + delete currentNode; + currentNode = wasRoot ? nullptr : parent; + } + } + } + + [[nodiscard]] const Node* Root() const + { + return m_Root; + } + }; + } // namespace gfx +} // namespace lepus #endif \ No newline at end of file diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h index 18252ba..bd86609 100644 --- a/src/lepus/gfx/SceneGraph/Renderable.h +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -3,58 +3,68 @@ #include #include +#include "SceneNode.h" +#include "Transformable.h" namespace lepus { namespace gfx { - template - class Renderable - { - private: - const MeshType* m_Mesh; - const lepus::math::Transform* m_Transform; - bool m_OwnsTransform; - - public: - Renderable() = delete; - - Renderable(const MeshType* mesh, const lepus::math::Transform& transform) - { - m_Mesh = mesh; - m_Transform = &transform; - m_OwnsTransform = false; - } - - Renderable(const MeshType* mesh, lepus::math::Transform&& transform) - { - m_Mesh = mesh; - m_Transform = new lepus::math::Transform(transform); - m_OwnsTransform = true; - } - - ~Renderable() - { - m_Mesh = nullptr; - - if (m_OwnsTransform) - { - delete m_Transform; - m_Transform = nullptr; - } - } - - const MeshType* GetMesh() const - { - return m_Mesh; - } - - lepus::math::Transform& GetTransform() const - { - return (lepus::math::Transform&)(*reinterpret_cast(m_Transform)); - } - }; - } -} + template + class Renderable : protected Transformable + { + private: + const MeshType* m_Mesh; + + public: + Renderable() = delete; + + Renderable(const MeshType* mesh, const lepus::math::Transform& transform) + { + m_Mesh = mesh; + m_Transform = &transform; + m_OwnsTransform = false; + } + + Renderable(const MeshType* mesh, lepus::math::Transform&& transform) + { + m_Mesh = mesh; + m_Transform = new lepus::math::Transform(transform); + m_OwnsTransform = true; + } + + ~Renderable() + { + m_Mesh = nullptr; + + if (m_OwnsTransform) + { + delete m_Transform; + m_Transform = nullptr; + } + } + + const MeshType* GetMesh() const + { + return m_Mesh; + } + + [[nodiscard]] lepus::math::Transform& GetTransform() const + { + return const_cast(*reinterpret_cast(m_Transform)); + } + + /// @brief Constructs a world transform for this Renderable by traversing up the scene hierarchy. + [[nodiscard]] lepus::math::Transform GetWorldTransform(const lepus::gfx::SceneNode* parent) const + { + auto ownTransform = this->GetTransform(); + + lepus::math::Transform worldTransform = lepus::math::Transform(); + + return worldTransform; + } + }; + } // namespace gfx +} // namespace lepus #endif \ No newline at end of file diff --git a/src/lepus/gfx/SceneGraph/SceneNode.h b/src/lepus/gfx/SceneGraph/SceneNode.h new file mode 100644 index 0000000..abc77ea --- /dev/null +++ b/src/lepus/gfx/SceneGraph/SceneNode.h @@ -0,0 +1,74 @@ +#ifndef LEPUS_GFX_SCENEGRAPH_SCENENODE +#define LEPUS_GFX_SCENEGRAPH_SCENENODE + +#include +#include "Transformable.h" + +namespace lepus +{ + namespace gfx + { + class SceneNode + { + private: + friend class SceneGraph; + + protected: + SceneNode* m_Sibling; + SceneNode* m_Child; + SceneNode* m_Parent; + const Transformable* m_Transformable; + + public: + SceneNode() + { + m_Sibling = nullptr; + m_Child = nullptr; + m_Parent = nullptr; + m_Transformable = nullptr; + } + + ~SceneNode() + { + } + + [[nodiscard]] inline const Transformable* GetTransformable() const { return m_Transformable; } + [[nodiscard]] inline const SceneNode* NextSibling() const { return m_Sibling; } + [[nodiscard]] inline const SceneNode* Parent() const { return m_Parent; } + [[nodiscard]] inline const SceneNode* FirstChild() const { return m_Child; } + + [[nodiscard]] inline bool IsRoot() const { return Parent() == nullptr; } + + inline const SceneNode* AddChild(const Transformable* transformable) + { + SceneNode* currentNode = m_Child; + SceneNode* lastChild = m_Child; + while (currentNode) + { + lastChild = currentNode; + currentNode = currentNode->m_Sibling; + } + + if (!currentNode) + { + currentNode = new SceneNode(); + currentNode->m_Transformable = transformable; + currentNode->m_Parent = this; + } + + if (!m_Child) + { + m_Child = currentNode; + } + else if (lastChild && !lastChild->m_Sibling) + { + lastChild->m_Sibling = currentNode; + } + + return currentNode; + } + }; + } // namespace gfx +} // namespace lepus + +#endif diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h new file mode 100644 index 0000000..6d0bb09 --- /dev/null +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -0,0 +1,35 @@ +#ifndef LEPUS_GFX_SCENEGRAPH_TRANSFORMABLE +#define LEPUS_GFX_SCENEGRAPH_TRANSFORMABLE + +#include + +namespace lepus +{ + namespace gfx + { + class Transformable + { + protected: + const lepus::math::Transform* m_Transform; + bool m_OwnsTransform; + + public: + Transformable() + { + m_Transform = new lepus::math::Transform(); + m_OwnsTransform = true; + } + + ~Transformable() + { + if (m_OwnsTransform) + { + delete m_Transform; + m_Transform = nullptr; + } + } + }; + } // namespace gfx +} // namespace lepus + +#endif From 50b738868a82a94f20921c71525e490250e8f47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sat, 12 Oct 2024 01:26:15 +0100 Subject: [PATCH 10/31] feat(gfx/SceneGraph): added world transforms for child nodes --- src/examples/demo/DemoApp.h | 43 ++-------- .../gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp | 2 +- src/lepus/gfx/SceneGraph.h | 2 +- src/lepus/gfx/SceneGraph/Renderable.h | 17 +--- src/lepus/gfx/SceneGraph/SceneNode.h | 5 +- src/lepus/gfx/SceneGraph/Transformable.h | 80 +++++++++++++++++++ 6 files changed, 92 insertions(+), 57 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index e05323c..240635d 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -151,19 +151,7 @@ class DemoApp : public system::BaseApp auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - // Also a cube to test each X-axis (pitch), Y-axis (yaw) and Z-axis (roll) rotation. - auto cubeX = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cubeY = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - auto cubeZ = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); - // api.GetSceneGraph().AddChild(&cube); - api.GetSceneGraph().AddChild(&cube2); - api.GetSceneGraph().AddChild(&cubeX); - api.GetSceneGraph().AddChild(&cubeY); - api.GetSceneGraph().AddChild(&cubeZ); - - cubeX.GetTransform().SetPosition(-1.5f, 2.f, 0.f); - cubeY.GetTransform().SetPosition(0.f, 2.f, 0.f); - cubeZ.GetTransform().SetPosition(1.5f, 2.f, 0.f); + auto cubeNode = api.GetSceneGraph().AddChild(&cube); // Update projection and view matrices with data from the camera object. m_UniformState.projMatrix = m_Camera.BuildPerspectiveMatrix(); @@ -189,37 +177,18 @@ class DemoApp : public system::BaseApp KeyboardState keys = {false, false, false, false, false}; float deltaTime = 0.f; - // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI * 0.25f)); + cube2.GetTransform().Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + + cubeNode->AddChild(&cube2); while (isRunning) { - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", cube2.GetTransform().Rotation().Axis().ToString().c_str()); - cube2.GetTransform().SetPosition(1.f * sinf(runningTime), 1.f * cosf(runningTime), -1.f); windowing->Update(); // Update window before drawing bool eKeyPressedLastFrame = keys.e; UpdateInput(keys, windowing); - // cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().w()).c_str()); - // lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Tick", std::to_string(cube2.GetTransform().Rotation().Angle()).c_str()); - // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), deltaTime)); - cubeX.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), deltaTime)); - cubeY.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); - cubeZ.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), deltaTime)); - - cube2.GetTransform().SetScale(fabs(cosf(runningTime))); - // cube.GetTransform().SetScale(fabs(cosf(runningTime))); - - // cubeX.GetTransform().SetScale(fabs(cosf(runningTime))); - // cubeY.GetTransform().SetScale(fabs(cosf(runningTime))); - // cubeZ.GetTransform().SetScale(fabs(cosf(runningTime))); - - if (!eKeyPressedLastFrame && keys.e) - { - // cube2.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 0.f, 1.f), PI / 4.f)); - lepus::engine::ConsoleLogger::Global().LogInfo("DemoApp", "Run", std::to_string(cube2.GetTransform().Rotation().Angle() * (1.f / PI) * 180.f).c_str()); - } + cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + Tick(deltaTime, keys); UpdateUniforms(&api); diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp index 3031c73..960b57f 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp @@ -114,7 +114,7 @@ void GraphicsApiGL::Draw() auto renderable = (const GLRenderable*)(currentNode->GetTransformable()); if (renderable) { - lepus::math::Matrix4x4 modelMat = renderable->GetTransform().BuildMatrix(); + lepus::math::Matrix4x4 modelMat = renderable->GetWorldMatrix(currentNode); auto modelMatUniformLoation = GetUniform(LEPUS_GFX_UNIFORMS_GLOBAL_MODEL_MATRIX); // TODO: integrate this with UpdateUniforms somehow, the model matrix uniform needs to be different for each renderable! glUniformMatrix4fv(modelMatUniformLoation->Location(), 1, true, modelMat.data()); diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index 0d7155c..4cc1f25 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -21,7 +21,7 @@ namespace lepus } template - const Node* AddChild(const TransformableType* transformable) + Node* AddChild(const TransformableType* transformable) { return m_Root->AddChild((const Transformable*)transformable); } diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h index bd86609..88a5cbf 100644 --- a/src/lepus/gfx/SceneGraph/Renderable.h +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -11,7 +11,7 @@ namespace lepus namespace gfx { template - class Renderable : protected Transformable + class Renderable : public Transformable { private: const MeshType* m_Mesh; @@ -48,21 +48,6 @@ namespace lepus { return m_Mesh; } - - [[nodiscard]] lepus::math::Transform& GetTransform() const - { - return const_cast(*reinterpret_cast(m_Transform)); - } - - /// @brief Constructs a world transform for this Renderable by traversing up the scene hierarchy. - [[nodiscard]] lepus::math::Transform GetWorldTransform(const lepus::gfx::SceneNode* parent) const - { - auto ownTransform = this->GetTransform(); - - lepus::math::Transform worldTransform = lepus::math::Transform(); - - return worldTransform; - } }; } // namespace gfx } // namespace lepus diff --git a/src/lepus/gfx/SceneGraph/SceneNode.h b/src/lepus/gfx/SceneGraph/SceneNode.h index abc77ea..559eff6 100644 --- a/src/lepus/gfx/SceneGraph/SceneNode.h +++ b/src/lepus/gfx/SceneGraph/SceneNode.h @@ -2,12 +2,13 @@ #define LEPUS_GFX_SCENEGRAPH_SCENENODE #include -#include "Transformable.h" namespace lepus { namespace gfx { + class Transformable; + class SceneNode { private: @@ -39,7 +40,7 @@ namespace lepus [[nodiscard]] inline bool IsRoot() const { return Parent() == nullptr; } - inline const SceneNode* AddChild(const Transformable* transformable) + inline SceneNode* AddChild(const Transformable* transformable) { SceneNode* currentNode = m_Child; SceneNode* lastChild = m_Child; diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index 6d0bb09..df1dcda 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -7,6 +7,8 @@ namespace lepus { namespace gfx { + class SceneNode; + class Transformable { protected: @@ -20,6 +22,84 @@ namespace lepus m_OwnsTransform = true; } + [[nodiscard]] lepus::math::Transform& GetTransform() const + { + return const_cast(*reinterpret_cast(m_Transform)); + } + + /// @brief Constructs a world transform matrix for this Transformable by traversing up the scene hierarchy. + [[nodiscard]] lepus::math::Matrix4x4 GetWorldMatrix(const lepus::gfx::SceneNode* node) const + { + auto ownTransform = this->GetTransform(); + + // If this is already the root of the scene subtree then this is our world matrix. + if (!node->Parent() || !node->Parent()->Parent()) + { + return ownTransform.BuildMatrix(); + } + + // Double work but this should be lighter than using a vector for this. + // We're traversing up the tree from direct parent to root, and counting how deep in the hierarchy we are. + auto* root = const_cast(node); + const SceneNode** leaves = nullptr; + uint8_t depth = 1; + while (root->Parent() != nullptr && root->Parent()->Parent() != nullptr) + { + root = const_cast(root->Parent()); + depth++; + } + + leaves = new const SceneNode*[depth]; + leaves[depth - 1] = node; + auto currentLeaf = const_cast(node); + for (int i = (int)depth - 2; depth > 1 && i >= 0; i--) + { + currentLeaf = const_cast(node->Parent()); + leaves[i] = currentLeaf; + + if (i == 0) + { + break; + } + } + + // Accumulate position and rotation + lepus::types::Vector3 accPos(0.f, 0.f, 0.f); + lepus::types::Quaternion accRot = lepus::types::Quaternion(); + lepus::types::Vector3 accScale(1.f, 1.f, 1.f); + + for (uint8_t i = 1; i < depth; i++) + { + lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform().Origin()); + rotated.w(1.f); + + const lepus::math::Transform& parentTransform = leaves[i - 1]->GetTransformable()->GetTransform(); + lepus::math::Matrix4x4 mat = parentTransform.BuildMatrix(); + + accRot = accRot * (parentTransform.Rotation()); + rotated = mat.Multiply(rotated); + + accPos.x(accPos.x() + rotated.x()); + accPos.y(accPos.y() + rotated.y()); + accPos.z(accPos.z() + rotated.z()); + + accScale.Multiply(parentTransform.Scale()); + } + + lepus::math::Transform worldTransform = lepus::math::Transform(); + + worldTransform.Origin(accPos); + worldTransform.Rotate(accRot); + worldTransform.SetScale(accScale.x(), accScale.y(), accScale.z()); + + if (leaves) + { + delete[] leaves; + } + + return worldTransform.BuildMatrix(); + } + ~Transformable() { if (m_OwnsTransform) From 54c874eeadb4954e35e74e3b7e702bce606eb83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sat, 12 Oct 2024 14:36:17 +0100 Subject: [PATCH 11/31] test(gfx/SceneGraph): fixed test compile error --- tests/L3D/SceneGraph/SceneGraphTests.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index 043e444..a2a3644 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -3,7 +3,6 @@ class SceneGraphTest : public testing::Test { - }; TEST(SceneGraphTest, SceneGraphIsInitialisedCorrectly) @@ -14,7 +13,7 @@ TEST(SceneGraphTest, SceneGraphIsInitialisedCorrectly) ASSERT_EQ(sceneGraph.Root()->FirstChild(), nullptr); ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); ASSERT_EQ(sceneGraph.Root()->Parent(), nullptr); - ASSERT_EQ(sceneGraph.Root()->GetRenderable(), nullptr); + ASSERT_EQ(sceneGraph.Root()->GetTransformable(), nullptr); } TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) @@ -25,7 +24,7 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) lepus::gfx::Renderable someRenderable = lepus::gfx::Renderable(&someMesh, someTransform); sceneGraph.AddChild(&someRenderable); - ASSERT_EQ(sceneGraph.Root()->FirstChild()->GetRenderable(), &someRenderable); + ASSERT_EQ(sceneGraph.Root()->FirstChild()->GetTransformable(), &someRenderable); ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->FirstChild(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->NextSibling(), nullptr); From 85bd8bb2c3d9780a35c48b1c8f7a37c13b50943e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sat, 12 Oct 2024 15:03:03 +0100 Subject: [PATCH 12/31] tweak(gfx/SceneGraph): updated demo app --- src/examples/demo/DemoApp.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 240635d..8264f34 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -179,6 +179,7 @@ class DemoApp : public system::BaseApp float deltaTime = 0.f; cube2.GetTransform().Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + // Parent the second cube to the first cube. cubeNode->AddChild(&cube2); while (isRunning) @@ -187,8 +188,13 @@ class DemoApp : public system::BaseApp bool eKeyPressedLastFrame = keys.e; UpdateInput(keys, windowing); + + // Rotate the parent cube cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + // Move the child cube back and forth along the parent's Z-axis + cube2.GetTransform().Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); + Tick(deltaTime, keys); UpdateUniforms(&api); From b69f552bfa58d902dc73871282d603eb9a2af960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sat, 12 Oct 2024 16:05:23 +0100 Subject: [PATCH 13/31] chore: added clang-format --- .clang-format | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9ad2856 --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +AllowShortFunctionsOnASingleLine: InlineOnly +BreakBeforeBraces: Allman +Language: Cpp +SpaceAroundPointerQualifiers: Default +PointerAlignment: Left +NamespaceIndentation: All +IndentWidth: 4 +AccessModifierOffset: 0 +ColumnLimit: 0 +UseTab: ForIndentation +AllowShortBlocksOnASingleLine: Always +BreakInheritanceList: AfterComma +BreakBeforeInheritanceComma: false +BreakConstructorInitializersBeforeComma: false +SortIncludes: Never \ No newline at end of file From 0b81e40eef40eabd8fe211c7f62591e7c76006fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sat, 12 Oct 2024 19:47:08 +0100 Subject: [PATCH 14/31] docs: updated README --- README.md | 88 ++++++++++--------------------------------------------- 1 file changed, 16 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 107d304..bca6fb5 100644 --- a/README.md +++ b/README.md @@ -3,88 +3,32 @@ ### About LepusEngine is a game engine developed in C++ and modern OpenGL (versions >= 3.3). -The renderer uses a programmable pipeline and as a result comes with a bundled set of GLSL shaders. - ### Building -The engine now uses CMake (tested version 3.15.2). Download and install it if you don't have it already. - -General use of CMake comes down to running `cmake configure` and `cmake generate` (please look up KitWare's documentation to learn more). - -Currently the engine only builds on Windows during to its reliance on PhysX. On Windows, you can also use CMake-gui. - -#### Dependencies -Download binaries for these libraries and place them in `/build/deps`: - -* GLFW (*build/deps/glfw-x.x.x.bin.WIN64*) - * The root directory of a 64-bit pre-compiled binary GLFW package for MSVC++ (folder name usually *glfw-x.x.x.bin.WIN64*). - * For example, *build/deps/glfw-x.x.x.bin.WIN64/lib-vc2015/* should contain your 64-bit *glfw3.lib* files -* GLFW32 (*build/deps/glfw-x.x.x.bin.WIN32*) - * The root directory of a 32-bit pre-compiled binary GLFW package for MSVC++ (folder name usually *glfw-x.x.x.bin.WIN32*). - * For example, *build/deps/glfw-x.x.x.bin.WIN32/lib-vc2015/* should contain your 32-bit *glfw3.lib* files -* GLEW (*build/deps/glew-x.x.x-win32*) - * The root directory of a GLEW package for Win32 (folder name usually *glew-x.x.x-win32/glew-x.x.x*). - * For example, *build/deps/glew-x.x.x-win32/glew-x.x.x/lib/* should contain the *glew.lib* files -* PHYSX (*build/deps/PhysX-x.x/physx/install/vcxxwin64*) - * The root directory of the *compiled binaries* for PhysX for 64-bit Visual C++ (folder name usually *PhysX-x.x/physx/install/vcxxwin64*). - * For example, *build/deps/PhysX-x.x/physx/install/vcxxwin64/PhysX/include/* should contain the *PxPhysXConfig.h* header file. - * You can find info regarding building PhysX SDK from source on [NVIDIA's official PhysX repository](https://github.com/NVIDIAGameWorks/PhysX). -* PHYSX32 (*build/deps/PhysX-x.x/physx/install/vcxxwin32*) - * The root directory of the *compiled binaries* for PhysX for 32-bit Visual C++ (folder name usually *PhysX-x.x/physx/install/vcxxwin32*). - * For example, *build/deps/PhysX-x.x/physx/install/vcxxwin32/PhysX/include/* should contain the *PxPhysXConfig.h* header file. - * You can find info regarding building PhysX SDK from source on [NVIDIA's official PhysX repository](https://github.com/NVIDIAGameWorks/PhysX). - -### Usage -The __RenderEngine__ class (from the _LepusEngine_::_Lepus3D_ namespace) is responsible for drawing pretty much anything to your screen. - -__RenderEngine__ also creates and updates a GL context and window. - -An example app using the engine would look like this: - -```c++ -#include +The engine uses CMake (>= 3.26) to generate build files. [Download and install it](https://cmake.org/download/) if you don't have it already. -using namespace LepusEngine; -using namespace Lepus3D; +Dependencies for the project are resolved and installed using [vcpkg](https://vcpkg.io/). See [Dependencies](#dependencies) for more info. -int main() -{ - Lepus3D::RenderEngine engine("LepusDemo", 800, 600); +General use of CMake comes down to running `cmake configure` and `cmake generate` (please look up [KitWare's documentation](https://cmake.org/documentation/) to learn more). - Lepus3D::Material testMat("Test material", "PerVertexUnlit"); - Lepus3D::BoxMesh testMesh; +Some IDEs such as Visual Studio, Visual Studio Code, and CLion come with CMake support or offer "CMake Tools" extensions that allow you to generate the build files from your IDE. +On Windows, you can also use CMake-gui. - // Be sure to get your own images, these are not provided with the Git repository - Lepus3D::Texture2D firstTx("container.jpg"); // Loads from Solution/Content/ - testMat.SetAttributeTex("_Texture1", firstTx); +The project is primarily built with Visual C++ 2022 during development and [GCC 11.4.0 on GitHub Actions](https://github.com/tomezpl/LepusEngine/actions), +and as such those are the "officially supported" compilers at this time. - Lepus3D::Transform transform; - - sf::Clock timer; +#### Dependencies +This project uses [vcpkg](https://vcpkg.io/) to locate and install dependencies, such as code libraries, while keeping the need for platform-specific configuration down to a minimum (or none). - bool running = true; - while (running) - { - float timeSeconds = timer.getElapsedTime().asSeconds(); - testMat.SetAttributeF("_Time", timeSeconds); - transform.SetRotation(Vector3(timeSeconds * 25.f, timeSeconds * 50.f, 0.f)); - transform.SetScale(sin(timeSeconds)); - engine.Update(); // Update window events etc. - engine.StartScene(); // Start drawing (clear buffers etc.) - engine.DrawMesh(testMesh, testMat, transform); - engine.EndScene(); // Finish drawing (display in window) - running = engine.Update(); - } - engine.Shutdown(); - return 0; -} -``` +vcpkg integrates with CMake via a CMake toolchain file. Provided you have a `VCPKG_ROOT` environment variable pointing at your **bootstrapped vcpkg** path, +the CMakeLists script for LepusEngine should automatically pick up the toolchain file and install the dependencies as needed. +See [Microsoft's vcpkg documentation](https://learn.microsoft.com/en-gb/vcpkg/get_started/get-started?pivots=shell-powershell) for more details. -Check the header files to see how things work. [RenderEngine.h](https://github.com/tomezpl/LepusEngine/blob/master/Lepus3D/Source/RenderEngine.h) is usually a good place to start. +Additionally, some open-source cross-platform dependencies, such as gl3w and imgui are included in the `3rdparty` directory. +For imgui, you'll need to run `git submodule update` in order to fetch code from the imgui repository before building LepusEngine. -### Contributing -I myself am focused on creating the renderer. However, I plan on making this a full game engine, and any help would be appreciated. +### Usage +See the ["demo" example program](https://github.com/tomezpl/LepusEngine/tree/master/src/examples/demo) for an example of how to use the engine's API. -If you have any questions you can contact me at **tomaszzlc@gmail.com**. If there are any pull requests I'll try to merge them into **master** (assuming they work). ### Licensing LepusEngine is licensed under the MIT license. This means you are free to use LepusEngine in any way you want, but you must include the copyright notice and the permission notice. From 18a223f3d2647c373a98345878a709b954f5dc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 14:38:55 +0100 Subject: [PATCH 15/31] fix(gfx/scenegraph): fixed transform not being updated, resolved PR comments --- src/examples/demo/DemoApp.h | 10 +- src/lepus/gfx/SceneGraph.h | 2 +- src/lepus/gfx/SceneGraph/Renderable.h | 1 + src/lepus/gfx/SceneGraph/Transformable.h | 27 +-- src/lepus/utility/types/Quaternion.h | 267 +++++++++++------------ src/lepus/utility/types/Transform.h | 19 +- 6 files changed, 166 insertions(+), 160 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 8264f34..48c0aa4 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -177,9 +177,11 @@ class DemoApp : public system::BaseApp KeyboardState keys = {false, false, false, false, false}; float deltaTime = 0.f; - cube2.GetTransform().Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + auto transform = cube2.GetTransform(); + transform->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + transform->SetScale(0.5f, 0.25f, 1.f); - // Parent the second cube to the first cube. + // Parent the second cube to the first cube. cubeNode->AddChild(&cube2); while (isRunning) @@ -190,10 +192,10 @@ class DemoApp : public system::BaseApp UpdateInput(keys, windowing); // Rotate the parent cube - cube.GetTransform().Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); // Move the child cube back and forth along the parent's Z-axis - cube2.GetTransform().Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); + cube2.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); Tick(deltaTime, keys); UpdateUniforms(&api); diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index 4cc1f25..cf44bee 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -23,7 +23,7 @@ namespace lepus template Node* AddChild(const TransformableType* transformable) { - return m_Root->AddChild((const Transformable*)transformable); + return m_Root->AddChild(static_cast(transformable)); } ~SceneGraph() diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h index 88a5cbf..6b7c9e9 100644 --- a/src/lepus/gfx/SceneGraph/Renderable.h +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -10,6 +10,7 @@ namespace lepus { namespace gfx { + /// @brief A base class for any renderable node that can appear in a scene. template class Renderable : public Transformable { diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index df1dcda..3030a8a 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -9,10 +9,11 @@ namespace lepus { class SceneNode; + /// @brief A base class for any node that has a transform in a scene. class Transformable { protected: - const lepus::math::Transform* m_Transform; + lepus::math::Transform* m_Transform; bool m_OwnsTransform; public: @@ -22,9 +23,9 @@ namespace lepus m_OwnsTransform = true; } - [[nodiscard]] lepus::math::Transform& GetTransform() const + [[nodiscard]] lepus::math::Transform* GetTransform() const { - return const_cast(*reinterpret_cast(m_Transform)); + return m_Transform; } /// @brief Constructs a world transform matrix for this Transformable by traversing up the scene hierarchy. @@ -33,9 +34,9 @@ namespace lepus auto ownTransform = this->GetTransform(); // If this is already the root of the scene subtree then this is our world matrix. - if (!node->Parent() || !node->Parent()->Parent()) + if (!node->Parent() || node->Parent()->IsRoot()) { - return ownTransform.BuildMatrix(); + return ownTransform->BuildMatrix(); } // Double work but this should be lighter than using a vector for this. @@ -43,7 +44,7 @@ namespace lepus auto* root = const_cast(node); const SceneNode** leaves = nullptr; uint8_t depth = 1; - while (root->Parent() != nullptr && root->Parent()->Parent() != nullptr) + while (root->Parent() != nullptr && !root->Parent()->IsRoot()) { root = const_cast(root->Parent()); depth++; @@ -65,25 +66,25 @@ namespace lepus // Accumulate position and rotation lepus::types::Vector3 accPos(0.f, 0.f, 0.f); - lepus::types::Quaternion accRot = lepus::types::Quaternion(); - lepus::types::Vector3 accScale(1.f, 1.f, 1.f); + lepus::types::Quaternion accRot = this->m_Transform->Rotation(); + lepus::types::Vector3 accScale = this->m_Transform->Scale(); for (uint8_t i = 1; i < depth; i++) { - lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform().Origin()); + lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin()); rotated.w(1.f); - const lepus::math::Transform& parentTransform = leaves[i - 1]->GetTransformable()->GetTransform(); - lepus::math::Matrix4x4 mat = parentTransform.BuildMatrix(); + auto parentTransform = leaves[i - 1]->GetTransformable()->GetTransform(); + lepus::math::Matrix4x4 mat = parentTransform->BuildMatrix(); - accRot = accRot * (parentTransform.Rotation()); + accRot = accRot * (parentTransform->Rotation()); rotated = mat.Multiply(rotated); accPos.x(accPos.x() + rotated.x()); accPos.y(accPos.y() + rotated.y()); accPos.z(accPos.z() + rotated.z()); - accScale.Multiply(parentTransform.Scale()); + accScale.Multiply(parentTransform->Scale()); } lepus::math::Transform worldTransform = lepus::math::Transform(); diff --git a/src/lepus/utility/types/Quaternion.h b/src/lepus/utility/types/Quaternion.h index 1176a04..314a77a 100644 --- a/src/lepus/utility/types/Quaternion.h +++ b/src/lepus/utility/types/Quaternion.h @@ -10,139 +10,138 @@ namespace lepus { namespace types { - class Quaternion : public Vector<4> - { - public: - Quaternion() - { - const float identity[] = { 0.f, 0.f, 0.f, 1.f }; - init((float*)identity); - } - - Quaternion(const Quaternion& quat) - { - init(quat); - } - - Quaternion(float* quatData) - { - init(quatData); - } - - /// @brief Constructs a Quaternion from Axis-Angle representation. - /// @remarks This is not for initialising XYZW directly - use the array initialiser instead. - /// @param axisX X component of the axis - /// @param axisY Y component of the axis - /// @param axisZ Z component of the axis - /// @param angle Rotation angle around the axis - Quaternion(float axisX, float axisY, float axisZ, float angle) - { - // Negating the angle here so that the Quaternion represents a clockwise rotation along an axis as observed looking towards the origin/object. - angle *= -1.f; - float const q[] = { axisX * sinf(angle / 2.f), axisY * sinf(angle / 2.f), axisZ * sinf(angle / 2.f), cos(angle / 2.f) }; - init((float*)q); - } - - Quaternion(const Vector3& axis, float angle) : Quaternion(axis.x(), axis.y(), axis.z(), angle) {} - - /// @brief Get the axis of rotation for this Quaternion. - /// @return A Vector3 representing the axis of rotation calculated from the Quaternion's imaginary part. - inline Vector3 Axis() const - { - float sqrtInvW = sqrt(fmax(0.f, 1.f - w() * w())); - if (sqrtInvW == 0.f) - { - // just return Vector3.Up if divide by 0 - // sqrtInvW = 1.f; - return Vector3(0.f, 1.f, 0.f); - } - - Vector3 vec = Vector3(x() / sqrtInvW, y() / sqrtInvW, z() / sqrtInvW); - return vec * (1.f / vec.Magnitude()); - } - - /// @brief Get the angle of rotation for this Quaternion. - /// @return A scalar describing the angle of rotation calculated from the Quaternion's real part (arccos(w) * 2). - inline float Angle() const - { - return acosf(w()) * 2.f; - } - - inline float x() const { return m_Components[0]; } - inline float y() const { return m_Components[1]; } - inline float z() const { return m_Components[2]; } - inline float w() const { return m_Components[3]; } - - inline float x(float newX) { return m_Components[0] = newX; } - inline float y(float newY) { return m_Components[1] = newY; } - inline float z(float newZ) { return m_Components[2] = newZ; } - inline float w(float newW) { return m_Components[3] = newW; } - - inline Quaternion operator*(const Quaternion& b) const - { - Quaternion result = Quaternion(); - - // lepus::engine::ConsoleLogger::Global().LogInfo("Quaternion", "operator*", std::to_string(w()).c_str(), "const Quaternion& b"); - - // result.w(fmin(1.f, fmax(-1.f, w()))); - - lepus::types::Vector3 va = lepus::types::Vector3((float*)GetData()); - lepus::types::Vector3 vb = lepus::types::Vector3((float*)b.GetData()); - result.w((w() * b.w()) - lepus::types::Vector3::Dot(va, vb)); - - lepus::types::Vector3 vc = lepus::types::Vector3::Cross(va, vb) + (vb * w()) + (va * b.w()); - result.x(vc.x()); - result.y(vc.y()); - result.z(vc.z()); - - // lepus::engine::ConsoleLogger::Global().LogInfo("Quaternion", "operator*", result.Axis().ToString().c_str(), "const Quaternion& b"); - - return result; - } - - /// @brief Computes a unit quaternion from this quaternion. - /// @return A copy of this quaternion divided by its magnitude. - inline Quaternion Normalised() const - { - Quaternion result = Quaternion(); - - float x = this->x(), y = this->y(), z = this->z(), w = this->w(); - - float mag = sqrtf(w * w + x * x + y * y + z * z); - - result.x(x / mag); - result.y(y / mag); - result.z(z / mag); - result.w(w / mag); - - return result; - } - - /// @brief Rotates a Vector3 v by the axis and angle described by this Quaternion. - /// @param v Vector3 representing a point in 3D space to rotate. - /// @return A Vector3 containing the point v rotated around the origin. - inline lepus::types::Vector3 Rotate(const lepus::types::Vector3& v) const - { - Quaternion p = Quaternion(); - p.w(0.f); - p.x(v.x()); - p.y(v.y()); - p.z(v.z()); - Quaternion conjugate = Quaternion(*this); - conjugate.x(-conjugate.x()); - conjugate.y(-conjugate.y()); - conjugate.z(-conjugate.z()); - return lepus::types::Vector3((float*)(*this * p * conjugate).GetData()); - } - - /// @brief Create a text representation of the Quaternion. Useful for debugging. - /// @return A string representing the Quaternion formatted as: "X = x, Y = y, Z = z, W = w" - std::string ToString() const - { - return std::string("X = ").append(std::to_string(x())).append(", Y = ").append(std::to_string(y())).append(", Z = ").append(std::to_string(z())).append(", W = ").append(std::to_string(w())); - } - }; - } -} + class Quaternion : public Vector<4> + { + public: + Quaternion() + { + const float identity[] = {0.f, 0.f, 0.f, 1.f}; + init((float*)identity); + } + + Quaternion(const Quaternion& quat) + { + init(quat); + } + + Quaternion(float* quatData) + { + init(quatData); + } + + /// @brief Constructs a Quaternion from Axis-Angle representation. + /// @remarks This is not for initialising XYZW directly - use the array initialiser instead. + /// @param axisX X component of the axis + /// @param axisY Y component of the axis + /// @param axisZ Z component of the axis + /// @param angle Rotation angle around the axis + Quaternion(float axisX, float axisY, float axisZ, float angle) + { + // Negating the angle here so that the Quaternion represents a clockwise rotation along an axis as observed looking towards the origin/object. + angle *= -1.f; + float const q[] = {axisX * sinf(angle / 2.f), axisY * sinf(angle / 2.f), axisZ * sinf(angle / 2.f), cos(angle / 2.f)}; + init((float*)q); + } + + Quaternion(const Vector3& axis, float angle) + : Quaternion(axis.x(), axis.y(), axis.z(), angle) {} + + /// @brief Get the axis of rotation for this Quaternion. + /// @return A Vector3 representing the axis of rotation calculated from the Quaternion's imaginary part. + inline Vector3 Axis() const + { + float sqrtInvW = sqrt(fmax(0.f, 1.f - w() * w())); + if (sqrtInvW == 0.f) + { + // just return Vector3.Up if divide by 0 + // sqrtInvW = 1.f; + return Vector3(0.f, 1.f, 0.f); + } + + Vector3 vec = Vector3(x() / sqrtInvW, y() / sqrtInvW, z() / sqrtInvW); + return vec * (1.f / vec.Magnitude()); + } + + /// @brief Get the angle of rotation for this Quaternion. + /// @return A scalar describing the angle of rotation calculated from the Quaternion's real part (arccos(w) * 2). + inline float Angle() const + { + return acosf(w()) * 2.f; + } + + inline float x() const { return m_Components[0]; } + inline float y() const { return m_Components[1]; } + inline float z() const { return m_Components[2]; } + inline float w() const { return m_Components[3]; } + + inline float x(float newX) { return m_Components[0] = newX; } + inline float y(float newY) { return m_Components[1] = newY; } + inline float z(float newZ) { return m_Components[2] = newZ; } + inline float w(float newW) { return m_Components[3] = newW; } + + inline Quaternion operator*(const Quaternion& b) const + { + Quaternion result = Quaternion(); + + // lepus::engine::ConsoleLogger::Global().LogInfo("Quaternion", "operator*", std::to_string(w()).c_str(), "const Quaternion& b"); + + // result.w(fmin(1.f, fmax(-1.f, w()))); + + lepus::types::Vector3 va = lepus::types::Vector3((float*)GetData()); + lepus::types::Vector3 vb = lepus::types::Vector3((float*)b.GetData()); + result.w((w() * b.w()) - lepus::types::Vector3::Dot(va, vb)); + + lepus::types::Vector3 vc = lepus::types::Vector3::Cross(va, vb) + (vb * w()) + (va * b.w()); + result.x(vc.x()); + result.y(vc.y()); + result.z(vc.z()); + + return result; + } + + /// @brief Computes a unit quaternion from this quaternion. + /// @return A copy of this quaternion divided by its magnitude. + inline Quaternion Normalised() const + { + Quaternion result = Quaternion(); + + float x = this->x(), y = this->y(), z = this->z(), w = this->w(); + + float mag = sqrtf(w * w + x * x + y * y + z * z); + + result.x(x / mag); + result.y(y / mag); + result.z(z / mag); + result.w(w / mag); + + return result; + } + + /// @brief Rotates a Vector3 v by the axis and angle described by this Quaternion. + /// @param v Vector3 representing a point in 3D space to rotate. + /// @return A Vector3 containing the point v rotated around the origin. + inline lepus::types::Vector3 Rotate(const lepus::types::Vector3& v) const + { + Quaternion p = Quaternion(); + p.w(0.f); + p.x(v.x()); + p.y(v.y()); + p.z(v.z()); + Quaternion conjugate = Quaternion(*this); + conjugate.x(-conjugate.x()); + conjugate.y(-conjugate.y()); + conjugate.z(-conjugate.z()); + return lepus::types::Vector3((float*)(*this * p * conjugate).GetData()); + } + + /// @brief Create a text representation of the Quaternion. Useful for debugging. + /// @return A string representing the Quaternion formatted as: "X = x, Y = y, Z = z, W = w" + std::string ToString() const + { + return std::string("X = ").append(std::to_string(x())).append(", Y = ").append(std::to_string(y())).append(", Z = ").append(std::to_string(z())).append(", W = ").append(std::to_string(w())); + } + }; + } // namespace types +} // namespace lepus #endif \ No newline at end of file diff --git a/src/lepus/utility/types/Transform.h b/src/lepus/utility/types/Transform.h index 91e30ba..8f0479d 100644 --- a/src/lepus/utility/types/Transform.h +++ b/src/lepus/utility/types/Transform.h @@ -12,7 +12,7 @@ namespace lepus { private: lepus::types::Vector3 m_Origin, m_Forward, m_Right, m_Up; - // axis-angle + lepus::types::Quaternion m_Rotation; lepus::types::Vector3 m_Scale; @@ -159,24 +159,27 @@ namespace lepus model.set<1, 3>(m_Origin.y()); model.set<2, 3>(m_Origin.z()); - // TODO: add scaling and rotation + // Normalised rotation quaternion for the rotation matrix lepus::types::Quaternion normRot = m_Rotation.Normalised(); lepus::types::Vector3 axis = normRot.Axis(); - // axis = axis * (1.f / axis.Magnitude()); float angle = normRot.Angle(); float cosTheta = cosf(angle); float invCosTheta = 1.f - cosTheta; float sinTheta = sinf(angle); - model.set<0, 0>(m_Scale.x() * ((axis.x() * axis.x()) * invCosTheta + cosTheta)); + + // Build the rotation & scaling components of the matrix. + model.set<0, 0>(m_Scale.x() * (axis.x() * axis.x() * invCosTheta + cosTheta)); model.set<0, 1>(m_Scale.y() * (axis.x() * axis.y() * invCosTheta + axis.z() * sinTheta)); - model.set<1, 1>(m_Scale.y() * ((axis.y() * axis.y()) * invCosTheta + cosTheta)); + model.set<0, 2>(m_Scale.z() * (axis.x() * axis.z() * invCosTheta - axis.y() * sinTheta)); + model.set<1, 0>(m_Scale.x() * (axis.y() * axis.x() * invCosTheta - axis.z() * sinTheta)); + model.set<1, 1>(m_Scale.y() * (axis.y() * axis.y() * invCosTheta + cosTheta)); + model.set<1, 2>(m_Scale.z() * (axis.y() * axis.z() * invCosTheta + axis.x() * sinTheta)); + model.set<2, 0>(m_Scale.x() * (axis.z() * axis.x() * invCosTheta + axis.y() * sinTheta)); model.set<2, 1>(m_Scale.y() * (axis.z() * axis.y() * invCosTheta - axis.x() * sinTheta)); - model.set<0, 2>(m_Scale.z() * (axis.x() * axis.z() * invCosTheta - axis.y() * sinTheta)); - model.set<1, 2>(m_Scale.z() * (axis.y() * axis.z() * invCosTheta + axis.x() * sinTheta)); - model.set<2, 2>(m_Scale.z() * ((axis.z() * axis.z()) * invCosTheta + cosTheta)); + model.set<2, 2>(m_Scale.z() * (axis.z() * axis.z() * invCosTheta + cosTheta)); return model; } From d04e0135a9fdb621711c8f5959427c8a9e71227c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 15:44:14 +0100 Subject: [PATCH 16/31] fix(gfx/scenegraph): fixed segfault in SceneGraph destructor --- src/lepus/gfx/SceneGraph.h | 7 +++++-- src/lepus/gfx/SceneGraph/Transformable.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lepus/gfx/SceneGraph.h b/src/lepus/gfx/SceneGraph.h index cf44bee..d4d8b2b 100644 --- a/src/lepus/gfx/SceneGraph.h +++ b/src/lepus/gfx/SceneGraph.h @@ -43,8 +43,11 @@ namespace lepus { bool wasRoot = currentNode->IsRoot(); parent->m_Child = currentNode->m_Sibling; - delete currentNode; - currentNode = wasRoot ? nullptr : parent; + if (currentNode) + { + delete currentNode; + } + currentNode = wasRoot ? nullptr : (parent->m_Child ? parent : parent->m_Parent); } } } diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index 3030a8a..e432540 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -103,7 +103,7 @@ namespace lepus ~Transformable() { - if (m_OwnsTransform) + if (m_OwnsTransform && m_Transform) { delete m_Transform; m_Transform = nullptr; From 5aa37ca775a8fc3f42f824007e4e202796eba3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 19:31:14 +0100 Subject: [PATCH 17/31] fix(gfx/scenegraph): fixed more complex scene trees not applying relative transforms correctly --- src/lepus/gfx/SceneGraph/Renderable.h | 2 +- src/lepus/gfx/SceneGraph/Transformable.h | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lepus/gfx/SceneGraph/Renderable.h b/src/lepus/gfx/SceneGraph/Renderable.h index 6b7c9e9..2f37212 100644 --- a/src/lepus/gfx/SceneGraph/Renderable.h +++ b/src/lepus/gfx/SceneGraph/Renderable.h @@ -20,7 +20,7 @@ namespace lepus public: Renderable() = delete; - Renderable(const MeshType* mesh, const lepus::math::Transform& transform) + Renderable(const MeshType* mesh, lepus::math::Transform& transform) { m_Mesh = mesh; m_Transform = &transform; diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index e432540..00b117d 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -55,7 +55,7 @@ namespace lepus auto currentLeaf = const_cast(node); for (int i = (int)depth - 2; depth > 1 && i >= 0; i--) { - currentLeaf = const_cast(node->Parent()); + currentLeaf = const_cast(currentLeaf->Parent()); leaves[i] = currentLeaf; if (i == 0) @@ -65,7 +65,7 @@ namespace lepus } // Accumulate position and rotation - lepus::types::Vector3 accPos(0.f, 0.f, 0.f); + lepus::types::Vector3 accPos(leaves[0]->GetTransformable()->GetTransform()->Origin()); lepus::types::Quaternion accRot = this->m_Transform->Rotation(); lepus::types::Vector3 accScale = this->m_Transform->Scale(); @@ -74,17 +74,19 @@ namespace lepus lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin()); rotated.w(1.f); - auto parentTransform = leaves[i - 1]->GetTransformable()->GetTransform(); - lepus::math::Matrix4x4 mat = parentTransform->BuildMatrix(); + auto parentTransform = *(leaves[i - 1]->GetTransformable()->GetTransform()); + parentTransform.Origin(lepus::types::Vector3()); - accRot = accRot * (parentTransform->Rotation()); + accRot = accRot * (parentTransform.Rotation()); + parentTransform.Rotation(accRot); + lepus::math::Matrix4x4 mat = parentTransform.BuildMatrix(); rotated = mat.Multiply(rotated); accPos.x(accPos.x() + rotated.x()); accPos.y(accPos.y() + rotated.y()); accPos.z(accPos.z() + rotated.z()); - accScale.Multiply(parentTransform->Scale()); + accScale.Multiply(parentTransform.Scale()); } lepus::math::Transform worldTransform = lepus::math::Transform(); From e807ac467a3bfab29c98085585c49c4162969fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 19:31:41 +0100 Subject: [PATCH 18/31] test(gfx/scenegraph): added test for converting relative transforms in a scene tree to a world matrix --- tests/L3D/SceneGraph/SceneGraphTests.cpp | 46 ++++++++++++++++++++++++ tests/L3D/SceneGraph/SceneGraphTests.h | 1 + 2 files changed, 47 insertions(+) diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index a2a3644..cb82b11 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -28,4 +28,50 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) ASSERT_EQ(sceneGraph.Root()->NextSibling(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->FirstChild(), nullptr); ASSERT_EQ(sceneGraph.Root()->FirstChild()->NextSibling(), nullptr); +} + +/// @brief Test world transforms in a scene graph. +/// +/// This scene sets up transform A at (0, 0, -2), B at (2, 0, 0) relative to A, C at (-3, 0, -3) relative to B. +/// B has a scale of (1.0/1.5). A is rotated counter-clockwise around the Y axis by 90 degrees. +/// A's anchor point is its center of mass so it remains in the same spot after the rotation. +/// B ends up at (0, 0, 0) in world space. +/// C's local coords are scaled by (1.0/1.5), and are therefore (-2, 0, -2) away from B in world units, and (0, 0, -2) away fom A in world units. +/// Therefore, C should end up at (2, 0, -2) in world space, effectively taking B's original world-space position. +TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) +{ + lepus::gfx::SceneGraph sceneGraph = lepus::gfx::SceneGraph(); + lepus::gfx::Transformable rootTransformable = lepus::gfx::Transformable(); + lepus::gfx::Transformable childTransformable1 = lepus::gfx::Transformable(); + lepus::gfx::Transformable childTransformable2 = lepus::gfx::Transformable(); + + rootTransformable.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + childTransformable1.GetTransform()->Origin(lepus::types::Vector3(2.f, 0.f, 0.f)); + childTransformable2.GetTransform()->Origin(lepus::types::Vector3(-3.f, 0.f, -3.f)); + + auto rootNode = sceneGraph.AddChild(&rootTransformable); + auto childNode1 = rootNode->AddChild(&childTransformable1); + auto childNode2 = childNode1->AddChild(&childTransformable2); + + rootTransformable.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); + childTransformable1.GetTransform()->SetScale(1.f / 1.5f); + + auto childNode2World = childTransformable2.GetWorldMatrix(childNode2).Multiply(lepus::types::Vector4(0.f, 0.f, 0.f, 1.f)); + + auto childNode1World = childTransformable2.GetWorldMatrix(childNode1).Multiply(lepus::types::Vector4(0.f, 0.f, 0.f, 1.f)); + + auto finalRootPos = rootTransformable.GetTransform()->Origin(); + ASSERT_EQ(finalRootPos.x(), 0.f); + ASSERT_EQ(finalRootPos.y(), 0.f); + ASSERT_EQ(finalRootPos.z(), -2.f); + + float cn1X = childNode1World.x(), cn1Y = childNode1World.y(), cn1Z = childNode1World.z(); + ASSERT_NEAR(cn1X, 0.f, 0.0001f); + ASSERT_NEAR(cn1Y, 0.f, 0.0001f); + ASSERT_NEAR(cn1Z, 0.f, 0.0001f); + + float cn2X = childNode2World.x(), cn2Y = childNode2World.y(), cn2Z = childNode2World.z(); + ASSERT_NEAR(cn2X, 2.f, 0.0001f); + ASSERT_NEAR(cn2Y, 0.f, 0.0001f); + ASSERT_NEAR(cn2Z, -2.f, 0.0001f); } \ No newline at end of file diff --git a/tests/L3D/SceneGraph/SceneGraphTests.h b/tests/L3D/SceneGraph/SceneGraphTests.h index 640555e..6f718a3 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.h +++ b/tests/L3D/SceneGraph/SceneGraphTests.h @@ -2,5 +2,6 @@ #define LTESTS_LGFX_SCENEGRAPH_TESTS #include +#include #endif \ No newline at end of file From 6d2b04a82f515e6c76b95cbe3cfacdad8223e461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 19:35:21 +0100 Subject: [PATCH 19/31] refactor(gfx/GraphicsEngine): changed dynamic_cast to static_cast in InitApi --- .../gfx/GraphicsEngine/GraphicsEngine.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp b/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp index d8f7996..86da9a5 100644 --- a/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp +++ b/src/lepus/gfx/GraphicsEngine/GraphicsEngine.cpp @@ -8,48 +8,48 @@ using namespace lepus::gfx; void GraphicsEngine::InitWindowing(std::shared_ptr windowing) { - m_Windowing = windowing; + m_Windowing = windowing; } void GraphicsEngine::InitApi(GraphicsApiOptions* options) { - // Check that an API wasn't initialised before. For now, only one API can be initialised at runtime. - assert(m_Api == nullptr); - - // Check that windowing has been initialised. In most cases we'll need windowing to make use of the API. - assert(m_Windowing != nullptr); - - switch (options->GetType()) - { - case GraphicsApiType::GraphicsApiOpenGL: - m_Api = new GraphicsApiGL(*dynamic_cast(options)); - break; - case GraphicsApiType::GraphicsApiVulkan: - // TODO - break; - case GraphicsApiType::GraphicsApiTest: - // Ignore test/mock APIs. - break; - case GraphicsApiType::GraphicsApiUnknown: - default: - // Assert if the API type is not part of the enum. - assert(false); - break; - } + // Check that an API wasn't initialised before. For now, only one API can be initialised at runtime. + assert(m_Api == nullptr); + + // Check that windowing has been initialised. In most cases we'll need windowing to make use of the API. + assert(m_Windowing != nullptr); + + switch (options->GetType()) + { + case GraphicsApiType::GraphicsApiOpenGL: + m_Api = new GraphicsApiGL(*static_cast(options)); + break; + case GraphicsApiType::GraphicsApiVulkan: + // TODO + break; + case GraphicsApiType::GraphicsApiTest: + // Ignore test/mock APIs. + break; + case GraphicsApiType::GraphicsApiUnknown: + default: + // Assert if the API type is not part of the enum. + assert(false); + break; + } } void GraphicsEngine::Setup() { - m_Api->CreatePipeline(); + m_Api->CreatePipeline(); } void GraphicsEngine::Render(const float r, const float g, const float b) { - m_Api->ClearFrameBuffer(r, g, b); + m_Api->ClearFrameBuffer(r, g, b); - m_Api->UpdateUniforms(); - m_Api->Draw(); + m_Api->UpdateUniforms(); + m_Api->Draw(); - m_Api->SwapBuffers(); - m_Windowing->SwapBuffers(); + m_Api->SwapBuffers(); + m_Windowing->SwapBuffers(); } \ No newline at end of file From 1ae00dce7cc7ff60eb8beb721be08245a2af79a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 20:39:19 +0100 Subject: [PATCH 20/31] ci: try fix linux glew build error --- CMakeLists.txt | 119 ++++++++++++++++++++++++------------------------- vcpkg.json | 1 - 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c56d8cb..3a0ca02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,32 +8,32 @@ ### Be sure to define your system's triplet to ensure correct target architecture (32/64-bit). https://vcpkg.readthedocs.io/en/latest/users/triplets/#additional-remarks # min. required CMake version -cmake_minimum_required (VERSION 3.26) +cmake_minimum_required(VERSION 3.26) include(FindOpenGL) include(CMakePrintHelpers) set(VCPKG_FEATURE_FLAGS "manifests,registries,versions") # VCPKG directory -if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) - set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - CACHE STRING "") -endif() +if (DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + CACHE STRING "") +endif () # VCPKG triplet to use (architecture, e.g. x86-windows) -if(DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) - set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") -endif() +if (DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) + set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") +endif () # C++ standard set(CMAKE_CXX_STANDARD 17) # Ensure correct library bitness if running Windows -if(${CMAKE_HOST_WIN32}) - if(ENV{VCPKG_DEFAULT_TRIPLET} STREQUAL "x64-windows") - set(CMAKE_SIZEOF_VOID_P 8) - endif() -endif() +if (${CMAKE_HOST_WIN32}) + if (ENV{VCPKG_DEFAULT_TRIPLET} STREQUAL "x64-windows") + set(CMAKE_SIZEOF_VOID_P 8) + endif () +endif () project(Lepus) @@ -109,15 +109,12 @@ add_dependencies(LepusDemo LepusGfx) # Dependency libraries ## LepusGfx: GL3W (core profile loading) add_library(GL3W - 3rdparty/gl3w/src/gl3w.c + 3rdparty/gl3w/src/gl3w.c ) include_directories(3rdparty/gl3w/include) ## LepusGfx: GLFW libraries find_package(glfw3 CONFIG REQUIRED) -## LepusGfx: GLEW includes -include_directories(LepusGfx ${GLEW_INCLUDES}) - ## LepusGfx: GLFW includes include_directories(LepusGfx ${GLFW_INCLUDES}) @@ -155,11 +152,11 @@ file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/Debug) # Set working directory for debugger set_property(TARGET LepusDemo PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${EXECUTABLE_OUTPUT_PATH}/Debug") -if(OpenGL::GL) - set(GL_LIBRARY OpenGL::GL) -elseif(OpenGL::OpenGL) - set(GL_LIBRARY OpenGL::OpenGL) -endif(OpenGL::GL) +if (OpenGL::GL) + set(GL_LIBRARY OpenGL::GL) +elseif (OpenGL::OpenGL) + set(GL_LIBRARY OpenGL::OpenGL) +endif (OpenGL::GL) target_link_libraries(LepusGfx PRIVATE GL3W glfw ${GL_LIBRARY} DearImgui LepusEngine LepusUtility LepusSystem) target_link_libraries(LepusDemo PRIVATE DearImgui LepusGfx LepusUtility LepusEngine) @@ -170,9 +167,9 @@ add_custom_command(TARGET LepusDemo POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_ # Unit testing framework include(FetchContent) FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.12.1 + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) @@ -182,23 +179,23 @@ enable_testing() # Unit test projects for each library add_executable(LepusGfx_Tests - tests/L3D/GraphicsEngine/GraphicsApiTests.h - tests/L3D/GraphicsEngine/GraphicsApiTests.cpp - tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.h - tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.cpp - tests/L3D/SceneGraph/SceneGraphTests.h - tests/L3D/SceneGraph/SceneGraphTests.cpp + tests/L3D/GraphicsEngine/GraphicsApiTests.h + tests/L3D/GraphicsEngine/GraphicsApiTests.cpp + tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.h + tests/L3D/GraphicsEngine/GraphicsApiOptionsTests.cpp + tests/L3D/SceneGraph/SceneGraphTests.h + tests/L3D/SceneGraph/SceneGraphTests.cpp ) add_executable(LepusSystem_Tests - tests/LSystem/IO/FileSystemTests.h - tests/LSystem/IO/FileSystemTests.cpp + tests/LSystem/IO/FileSystemTests.h + tests/LSystem/IO/FileSystemTests.cpp ) add_executable(LepusUtility_Tests - tests/LUtility/MathTests/MatrixTests.cpp - tests/LUtility/MathTests/VectorTests.cpp - tests/LUtility/MathTests/TransformTests.cpp + tests/LUtility/MathTests/MatrixTests.cpp + tests/LUtility/MathTests/VectorTests.cpp + tests/LUtility/MathTests/TransformTests.cpp ) add_custom_command(TARGET LepusSystem_Tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/tests/Content" ${CMAKE_BINARY_DIR}/tests/Content) @@ -214,28 +211,28 @@ gtest_discover_tests(LepusUtility_Tests) # Warnings set(LepusTargets LepusEngine LepusSystem LepusGfx LepusSystem_Tests LepusGfx_Tests LepusUtility_Tests) -if(MSVC) - # Ignore warnings: - # 4100: unreferenced param, - # 4514: unreferenced inline function removed, - # 4464: parent relative include, - # 4820: struct padding, - # 4263: member not overriding base class virtual - # 4265-5027: copy ctor, move ctor, assignment, move assignment implicitly deleted (this messes up gtest projects) - # 5045: "compiler will insert Spectre mitigation for memory load" - # 5262: implicit switch fallback - set(MSVCDisabledWarnings 4100 4514 4464 4820 4263 4625 5026 4626 5027 5045 5262) - - set(MSVCDisabledWarningsFormatted "") - foreach(Warning IN LISTS MSVCDisabledWarnings) - set(MSVCDisabledWarningsFormatted ${MSVCDisabledWarningsFormatted} /wd${Warning}) - endforeach() - - foreach(Target IN LISTS LepusTargets) - target_compile_options(${Target} PRIVATE /Wall ${MSVCDisabledWarningsFormatted} /WX /external:W3) - endforeach() -else() - foreach(Target IN LISTS LepusTargets) - target_compile_options(${Target} PRIVATE -Wall -Wextra -Wpedantic) - endforeach() -endif() +if (MSVC) + # Ignore warnings: + # 4100: unreferenced param, + # 4514: unreferenced inline function removed, + # 4464: parent relative include, + # 4820: struct padding, + # 4263: member not overriding base class virtual + # 4265-5027: copy ctor, move ctor, assignment, move assignment implicitly deleted (this messes up gtest projects) + # 5045: "compiler will insert Spectre mitigation for memory load" + # 5262: implicit switch fallback + set(MSVCDisabledWarnings 4100 4514 4464 4820 4263 4625 5026 4626 5027 5045 5262) + + set(MSVCDisabledWarningsFormatted "") + foreach (Warning IN LISTS MSVCDisabledWarnings) + set(MSVCDisabledWarningsFormatted ${MSVCDisabledWarningsFormatted} /wd${Warning}) + endforeach () + + foreach (Target IN LISTS LepusTargets) + target_compile_options(${Target} PRIVATE /Wall ${MSVCDisabledWarningsFormatted} /WX /external:W3) + endforeach () +else () + foreach (Target IN LISTS LepusTargets) + target_compile_options(${Target} PRIVATE -Wall -Wextra -Wpedantic) + endforeach () +endif () diff --git a/vcpkg.json b/vcpkg.json index 17bf9ea..49b8772 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -3,7 +3,6 @@ "version": "1.0.0", "dependencies": [ "glfw3", - "glew", "bullet3" ], "builtin-baseline": "8dbd66f5a7821ced1ed57696b50375a977006813" From 63b820daa775a5ff3971979cd5be1d405e2669f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 20:54:41 +0100 Subject: [PATCH 21/31] ci: try fix linux glew build error --- scripts/install-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-ubuntu.sh b/scripts/install-ubuntu.sh index 25e91db..a634494 100644 --- a/scripts/install-ubuntu.sh +++ b/scripts/install-ubuntu.sh @@ -14,7 +14,7 @@ sudo apt install git # OpenGL echo "Installing OpenGL and Xorg dependencies" -sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libgl1-mesa-glx libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev +sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libgl1-mesa-glx libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev # VCPKG echo "Installing vcpkg" From f0c63a021cb3803d8ba75a11961c4dad5fcceb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 21:17:55 +0100 Subject: [PATCH 22/31] ci: try fix linux glew build error --- CMakeLists.txt | 27 --------------------------- vcpkg-configuration.json | 9 +++++++++ vcpkg.json | 4 ++-- 3 files changed, 11 insertions(+), 29 deletions(-) create mode 100644 vcpkg-configuration.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a0ca02..765339e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,6 @@ set(LEPUS_SRC_DIR ${CMAKE_SOURCE_DIR}/src/lepus) # Engine-wide include directories include_directories(${CMAKE_SOURCE_DIR}/include src ${CMAKE_SOURCE_DIR}/3rdparty) include_directories(${VCPKG_INCLUDE_PATH}) -include_directories(${VCPKG_INCLUDE_PATH}/bullet) link_directories(${VCPKG_LIB_PATH}) @@ -119,32 +118,6 @@ find_package(glfw3 CONFIG REQUIRED) include_directories(LepusGfx ${GLFW_INCLUDES}) # LepusEngine sources -## LepusEngine: PhysX includes -set(PHYSX_INCLUDES ${VCPKG_DEPS_PATH}/include/physx) -include_directories(${PHYSX_INCLUDES}) - -## LepusEngine: Bullet3 libraries - -#find_library(BULLET_COMMON_LIB Bullet3Common ${VCPKG_LIBS_REL_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -set(BULLET_COMMON_LIB ${Bullet3Common}) -#find_library(BULLET_COLLISION_LIB BulletCollision ${VCPKG_LIBS_REL_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -set(BULLET_COLLISION_LIB ${BulletCollision}) -#find_library(BULLET_DYNAMICS_LIB BulletDynamics ${VCPKG_LIBS_REL_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -set(BULLET_DYNAMICS_LIB ${BulletDynamics}) -#find_library(BULLET_LINMATH_LIB LinearMath ${VCPKG_LIBS_REL_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -set(BULLET_LINMATH_LIB ${LinearMath}) - -#find_library(BULLET_COMMON_LIB_DBG Bullet3Common_Debug ${VCPKG_LIBS_DBG_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -#find_library(BULLET_COLLISION_LIB_DBG BulletCollision_Debug ${VCPKG_LIBS_DBG_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -#find_library(BULLET_DYNAMICS_LIB_DBG BulletDynamics_Debug ${VCPKG_LIBS_DBG_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) -#find_library(BULLET_LINMATH_LIB_DBG LinearMath_Debug ${VCPKG_LIBS_DBG_PATH} NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) - -# Link Bullet for debug and release targets -target_link_libraries(LepusEngine PRIVATE LinearMath Bullet3Common BulletDynamics BulletCollision) - -#target_link_libraries(LepusEngine debug Bullet3Common_Debug optimized Bullet3Common) -#target_link_libraries(LepusEngine debug BulletDynamics_Debug optimized BulletDynamics) -#target_link_libraries(LepusEngine debug BulletCollision_Debug optimized BulletCollision) file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/Release) file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/Debug) diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000..3253075 --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,9 @@ + +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg-configuration.schema.json", + "default-registry": { + "kind": "git", + "repository": "https://github.com/tomezpl/vcpkg", + "baseline": "5ead4fccc5d32bc0063983e597eb32aafab3fef4" + } +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 49b8772..5155064 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,9 +1,9 @@ { + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "lepus-engine", "version": "1.0.0", "dependencies": [ "glfw3", "bullet3" - ], - "builtin-baseline": "8dbd66f5a7821ced1ed57696b50375a977006813" + ] } \ No newline at end of file From 52f3a8f3bdfa63611c7e560a4f9c8ec316d4bbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 21:23:52 +0100 Subject: [PATCH 23/31] ci: try fix linux glew build error --- vcpkg.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vcpkg.json b/vcpkg.json index 5155064..60125e4 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -3,7 +3,6 @@ "name": "lepus-engine", "version": "1.0.0", "dependencies": [ - "glfw3", - "bullet3" + "glfw3" ] } \ No newline at end of file From 1a7e06c31861a89052fe8e928ef45ddb9aee558d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 21:25:59 +0100 Subject: [PATCH 24/31] ci: try fix linux glew build error --- scripts/install-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-ubuntu.sh b/scripts/install-ubuntu.sh index a634494..0e29985 100644 --- a/scripts/install-ubuntu.sh +++ b/scripts/install-ubuntu.sh @@ -14,7 +14,7 @@ sudo apt install git # OpenGL echo "Installing OpenGL and Xorg dependencies" -sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libgl1-mesa-glx libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev +sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libgl1-mesa-glx libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev pkg-config # VCPKG echo "Installing vcpkg" From 98257ba2e61834265f8e006522c06e20dc6bbd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 13 Oct 2024 21:28:01 +0100 Subject: [PATCH 25/31] ci: try fix linux glew build error --- scripts/install-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-ubuntu.sh b/scripts/install-ubuntu.sh index 0e29985..5423b41 100644 --- a/scripts/install-ubuntu.sh +++ b/scripts/install-ubuntu.sh @@ -14,7 +14,7 @@ sudo apt install git # OpenGL echo "Installing OpenGL and Xorg dependencies" -sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libgl1-mesa-glx libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev pkg-config +sudo apt install libopengl-dev libgl-dev libgl1-mesa-dev libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev pkg-config # VCPKG echo "Installing vcpkg" From d458b6d82a78a5b9a117feb319fa659666f712ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Tue, 15 Oct 2024 00:19:23 +0100 Subject: [PATCH 26/31] fix(gfx/scenegraph): fixed scaling bug --- src/lepus/gfx/SceneGraph/Transformable.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index 00b117d..d278d01 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -66,15 +66,17 @@ namespace lepus // Accumulate position and rotation lepus::types::Vector3 accPos(leaves[0]->GetTransformable()->GetTransform()->Origin()); - lepus::types::Quaternion accRot = this->m_Transform->Rotation(); - lepus::types::Vector3 accScale = this->m_Transform->Scale(); + lepus::types::Quaternion accRot = lepus::types::Quaternion(); + lepus::types::Vector3 accScale = lepus::types::Vector3(1.f, 1.f, 1.f); for (uint8_t i = 1; i < depth; i++) { - lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin()); + auto parentTransform = *(leaves[i - 1]->GetTransformable()->GetTransform()); + + lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin() * accScale); rotated.w(1.f); + accScale.Multiply(parentTransform.Scale()); - auto parentTransform = *(leaves[i - 1]->GetTransformable()->GetTransform()); parentTransform.Origin(lepus::types::Vector3()); accRot = accRot * (parentTransform.Rotation()); @@ -85,14 +87,13 @@ namespace lepus accPos.x(accPos.x() + rotated.x()); accPos.y(accPos.y() + rotated.y()); accPos.z(accPos.z() + rotated.z()); - - accScale.Multiply(parentTransform.Scale()); } lepus::math::Transform worldTransform = lepus::math::Transform(); worldTransform.Origin(accPos); - worldTransform.Rotate(accRot); + worldTransform.Rotate(accRot * this->m_Transform->Rotation()); + accScale = accScale * this->m_Transform->Scale(); worldTransform.SetScale(accScale.x(), accScale.y(), accScale.z()); if (leaves) From 5cc821e629e6a70d5e0b15f194acd3f58c36de80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Tue, 15 Oct 2024 00:19:56 +0100 Subject: [PATCH 27/31] test(gfx/scenegraph): corrected expected values in world coords test and updated the demo app --- src/examples/demo/DemoApp.h | 26 +++++++++++++++++++----- tests/L3D/SceneGraph/SceneGraphTests.cpp | 24 +++++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 48c0aa4..cc2527c 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -150,6 +150,22 @@ class DemoApp : public system::BaseApp auto cubeMesh = lepus::gfx::GLMesh(lepus::utility::Primitives::Cube()); auto cube = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); auto cube2 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cube3 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + auto cube4 = lepus::gfx::Renderable(&cubeMesh, lepus::math::Transform()); + + cube.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + cube2.GetTransform()->Origin(lepus::types::Vector3(2.f, 0.f, 0.f)); + cube3.GetTransform()->Origin(lepus::types::Vector3(-3.f, 0.f, -3.f)); + cube4.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, 6.f)); + + auto rootNode = api.GetSceneGraph().AddChild(&cube); + auto childNode1 = rootNode->AddChild(&cube2); + auto childNode2 = childNode1->AddChild(&cube3); + auto childNode3 = childNode2->AddChild(&cube4); + + cube.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); + cube2.GetTransform()->SetScale(1.f / 1.5f); + cube3.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (-50.f / 180.f))); auto cubeNode = api.GetSceneGraph().AddChild(&cube); @@ -178,11 +194,11 @@ class DemoApp : public system::BaseApp float deltaTime = 0.f; auto transform = cube2.GetTransform(); - transform->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); - transform->SetScale(0.5f, 0.25f, 1.f); + // transform->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); + // transform->SetScale(0.5f, 0.25f, 1.f); // Parent the second cube to the first cube. - cubeNode->AddChild(&cube2); + // cubeNode->AddChild(&cube2); while (isRunning) { @@ -192,10 +208,10 @@ class DemoApp : public system::BaseApp UpdateInput(keys, windowing); // Rotate the parent cube - cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime)); + cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime * 0.5f)); // Move the child cube back and forth along the parent's Z-axis - cube2.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); + // cube2.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); Tick(deltaTime, keys); UpdateUniforms(&api); diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index cb82b11..9bb2546 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -32,33 +32,46 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) /// @brief Test world transforms in a scene graph. /// -/// This scene sets up transform A at (0, 0, -2), B at (2, 0, 0) relative to A, C at (-3, 0, -3) relative to B. +/// This scene sets up transform A at (0, 0, -2), B at (2, 0, 0) relative to A, C at (-3, 0, -3) relative to B, D at (0, 0, 6) relative to C. /// B has a scale of (1.0/1.5). A is rotated counter-clockwise around the Y axis by 90 degrees. /// A's anchor point is its center of mass so it remains in the same spot after the rotation. /// B ends up at (0, 0, 0) in world space. /// C's local coords are scaled by (1.0/1.5), and are therefore (-2, 0, -2) away from B in world units, and (0, 0, -2) away fom A in world units. /// Therefore, C should end up at (2, 0, -2) in world space, effectively taking B's original world-space position. +/// C is also rotated counter-clockwise around the Y axis by 50 degrees. Before applying A's rotation, D ends up at approx. (-3.1, 0, -0.55) away from A in world units. +/// Once A's 90deg rotation is applied then, D should end up at approx (-0.55, 0, -5.06) in world space. +/// +/// (it may be helpful to draw this on a piece of paper) TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) { lepus::gfx::SceneGraph sceneGraph = lepus::gfx::SceneGraph(); lepus::gfx::Transformable rootTransformable = lepus::gfx::Transformable(); lepus::gfx::Transformable childTransformable1 = lepus::gfx::Transformable(); lepus::gfx::Transformable childTransformable2 = lepus::gfx::Transformable(); + lepus::gfx::Transformable childTransformable3 = lepus::gfx::Transformable(); rootTransformable.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); childTransformable1.GetTransform()->Origin(lepus::types::Vector3(2.f, 0.f, 0.f)); childTransformable2.GetTransform()->Origin(lepus::types::Vector3(-3.f, 0.f, -3.f)); + childTransformable3.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, 6.f)); auto rootNode = sceneGraph.AddChild(&rootTransformable); auto childNode1 = rootNode->AddChild(&childTransformable1); auto childNode2 = childNode1->AddChild(&childTransformable2); + auto childNode3 = childNode2->AddChild(&childTransformable3); rootTransformable.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); childTransformable1.GetTransform()->SetScale(1.f / 1.5f); + childTransformable2.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (-50.f / 180.f))); + + auto childNode3WorldMat = childTransformable3.GetWorldMatrix(childNode3); + lepus::types::Vector3 childNode3World(childNode3WorldMat.get(0, 3), childNode3WorldMat.get(1, 3), childNode3WorldMat.get(2, 3)); - auto childNode2World = childTransformable2.GetWorldMatrix(childNode2).Multiply(lepus::types::Vector4(0.f, 0.f, 0.f, 1.f)); + auto childNode2WorldMat = childTransformable2.GetWorldMatrix(childNode2); + lepus::types::Vector3 childNode2World(childNode2WorldMat.get(0, 3), childNode2WorldMat.get(1, 3), childNode2WorldMat.get(2, 3)); - auto childNode1World = childTransformable2.GetWorldMatrix(childNode1).Multiply(lepus::types::Vector4(0.f, 0.f, 0.f, 1.f)); + auto childNode1WorldMat = childTransformable1.GetWorldMatrix(childNode1); + lepus::types::Vector3 childNode1World(childNode1WorldMat.get(0, 3), childNode1WorldMat.get(1, 3), childNode1WorldMat.get(2, 3)); auto finalRootPos = rootTransformable.GetTransform()->Origin(); ASSERT_EQ(finalRootPos.x(), 0.f); @@ -74,4 +87,9 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) ASSERT_NEAR(cn2X, 2.f, 0.0001f); ASSERT_NEAR(cn2Y, 0.f, 0.0001f); ASSERT_NEAR(cn2Z, -2.f, 0.0001f); + + float cn3X = childNode3World.x(), cn3Y = childNode3World.y(), cn3Z = childNode3World.z(); + ASSERT_NEAR(cn3X, -0.55f, 0.03f); + ASSERT_NEAR(cn3Y, 0.f, 0.0001f); + ASSERT_NEAR(cn3Z, -5.06f, 0.01f); } \ No newline at end of file From 18b37cd2cb3a5f3cebfd8d2e009c7346567f7921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Tue, 15 Oct 2024 20:30:12 +0100 Subject: [PATCH 28/31] test(gfx/scenegraph): replaced hard-coded values in world coords test --- tests/L3D/SceneGraph/SceneGraphTests.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index 9bb2546..59a27d9 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -60,9 +60,11 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) auto childNode2 = childNode1->AddChild(&childTransformable2); auto childNode3 = childNode2->AddChild(&childTransformable3); + const float nodeCTheta = -50.f; + rootTransformable.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); childTransformable1.GetTransform()->SetScale(1.f / 1.5f); - childTransformable2.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (-50.f / 180.f))); + childTransformable2.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (nodeCTheta / 180.f))); auto childNode3WorldMat = childTransformable3.GetWorldMatrix(childNode3); lepus::types::Vector3 childNode3World(childNode3WorldMat.get(0, 3), childNode3WorldMat.get(1, 3), childNode3WorldMat.get(2, 3)); @@ -89,7 +91,9 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) ASSERT_NEAR(cn2Z, -2.f, 0.0001f); float cn3X = childNode3World.x(), cn3Y = childNode3World.y(), cn3Z = childNode3World.z(); - ASSERT_NEAR(cn3X, -0.55f, 0.03f); + float cn3PreRotX = 0.f + 4.f * sinf((float)PI * (nodeCTheta / 180.f)); + float cn3PreRotZ = -4.f + 4.f * cosf((float)PI * (nodeCTheta / 180.f)); + ASSERT_NEAR(cn3X, 0.f - (cn3PreRotZ - -2.f), 0.03f); ASSERT_NEAR(cn3Y, 0.f, 0.0001f); - ASSERT_NEAR(cn3Z, -5.06f, 0.01f); + ASSERT_NEAR(cn3Z, -2.f + (cn3PreRotX - 0.f), 0.01f); } \ No newline at end of file From 830b63ac6f7114c43fb6252f464762a8608d432d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Tue, 15 Oct 2024 22:55:25 +0100 Subject: [PATCH 29/31] test(gfx/scenegraph): commented the test and made float error uniform --- tests/L3D/SceneGraph/SceneGraphTests.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index 59a27d9..e8bb6c8 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -91,9 +91,13 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) ASSERT_NEAR(cn2Z, -2.f, 0.0001f); float cn3X = childNode3World.x(), cn3Y = childNode3World.y(), cn3Z = childNode3World.z(); + // To explain: child node 2 is at (0, 0, -4) before the root node's rotation is applied. + // child node 3 is at (0, 0, 4) in child node 2's local space. Before child node 2's rotation is applied, that's (0,0,0) + // Thus we rotate child node 3 by node 2's theta around node 2, and we get child node 3's world-space position before the root node's rotation is applied. + // Since the root node is rotated by 90 degrees, to make things easier we just swap the Z and X coords float cn3PreRotX = 0.f + 4.f * sinf((float)PI * (nodeCTheta / 180.f)); float cn3PreRotZ = -4.f + 4.f * cosf((float)PI * (nodeCTheta / 180.f)); - ASSERT_NEAR(cn3X, 0.f - (cn3PreRotZ - -2.f), 0.03f); + ASSERT_NEAR(cn3X, 0.f - (cn3PreRotZ - -2.f), 0.0001f); ASSERT_NEAR(cn3Y, 0.f, 0.0001f); - ASSERT_NEAR(cn3Z, -2.f + (cn3PreRotX - 0.f), 0.01f); + ASSERT_NEAR(cn3Z, -2.f + (cn3PreRotX - 0.f), 0.0001f); } \ No newline at end of file From 9e0382e5abf99f029cb0b3d28f83e66f5c6b4341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Wed, 16 Oct 2024 23:23:58 +0100 Subject: [PATCH 30/31] fix(types/Quaternion): removed the weird angle flip that was causing issues with world transforms --- src/examples/demo/DemoApp.h | 22 +++++++---- src/lepus/gfx/SceneGraph/Transformable.h | 49 ++++++++++++++++++++---- src/lepus/utility/types/Quaternion.h | 4 +- tests/L3D/SceneGraph/SceneGraphTests.cpp | 8 ++-- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index cc2527c..9f3eff9 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -89,7 +89,7 @@ class DemoApp : public system::BaseApp { float deltaX = (xpos - m_MouseState.lastX) / 300.0; float deltaY = (ypos - m_MouseState.lastY) / 300.0; - lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, -deltaX); + lepus::types::Quaternion rotationYaw = lepus::types::Quaternion(0.f, 1.f, 0.f, deltaX); auto combined = rotationYaw; float angle = combined.Angle(); @@ -97,7 +97,7 @@ class DemoApp : public system::BaseApp { m_Camera.Transform().Rotate(rotationYaw); } - lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), -deltaY); + lepus::types::Quaternion rotationPitch = lepus::types::Quaternion(m_Camera.Transform().Right(), deltaY); angle = rotationPitch.Angle(); if (abs(angle) > 0.001f) { @@ -155,17 +155,17 @@ class DemoApp : public system::BaseApp cube.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); cube2.GetTransform()->Origin(lepus::types::Vector3(2.f, 0.f, 0.f)); - cube3.GetTransform()->Origin(lepus::types::Vector3(-3.f, 0.f, -3.f)); - cube4.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, 6.f)); + cube3.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, 2.f)); + cube4.GetTransform()->Origin(lepus::types::Vector3(0.f, -2.f, 0.f)); auto rootNode = api.GetSceneGraph().AddChild(&cube); auto childNode1 = rootNode->AddChild(&cube2); auto childNode2 = childNode1->AddChild(&cube3); - auto childNode3 = childNode2->AddChild(&cube4); + // auto childNode3 = childNode2->AddChild(&cube4); cube.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); - cube2.GetTransform()->SetScale(1.f / 1.5f); - cube3.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (-50.f / 180.f))); + // cube2.GetTransform()->SetScale(1.f / 1.5f); + cube2.GetTransform()->Rotate(lepus::types::Quaternion(1.f, 0.f, 0.f, (float)PI * (-90.f / 180.f))); auto cubeNode = api.GetSceneGraph().AddChild(&cube); @@ -208,7 +208,13 @@ class DemoApp : public system::BaseApp UpdateInput(keys, windowing); // Rotate the parent cube - cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), deltaTime * 0.5f)); + if (!keys.e && eKeyPressedLastFrame) + { + cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), PI * -0.25f)); + } + cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), -deltaTime)); + cube2.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), -deltaTime)); + cube3.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), -deltaTime)); // Move the child cube back and forth along the parent's Z-axis // cube2.GetTransform()->Origin(lepus::types::Vector3(0.f, 0.f, -1.f + ((sinf(runningTime) + 1.f) * 0.5f) * -2.f)); diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index d278d01..d2919f9 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -76,23 +76,58 @@ namespace lepus lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin() * accScale); rotated.w(1.f); accScale.Multiply(parentTransform.Scale()); - + // parentTransform.Origin(lepus::types::Vector3()); + // + lepus::types::Quaternion normParentRot = parentTransform.Rotation().Normalised(); + lepus::types::Vector3 axis = normParentRot.Axis(); + float angle = normParentRot.Angle(); - accRot = accRot * (parentTransform.Rotation()); parentTransform.Rotation(accRot); lepus::math::Matrix4x4 mat = parentTransform.BuildMatrix(); - rotated = mat.Multiply(rotated); + lepus::types::Vector4 rotatedAxis = mat.Multiply(lepus::types::Vector4(axis)); - accPos.x(accPos.x() + rotated.x()); - accPos.y(accPos.y() + rotated.y()); - accPos.z(accPos.z() + rotated.z()); + parentTransform.Rotation(lepus::types::Quaternion(rotatedAxis.x(), rotatedAxis.y(), rotatedAxis.z(), angle)); + mat = parentTransform.BuildMatrix(); + // rotated = mat.Multiply(rotated); + // rotated = accRot.Rotate(rotated); + + accRot = accRot * (parentTransform.Rotation()); + parentTransform.Rotation(accRot); + mat = parentTransform.BuildMatrix(); + auto fwd = mat.Multiply(lepus::types::Vector4(0, 0, 1, 1)), + rgt = mat.Multiply(lepus::types::Vector4(1, 0, 0, 1)), + up = mat.Multiply(lepus::types::Vector4(0, 1, 0, 1)); + + auto forward = lepus::types::Vector3(fwd.x(), fwd.y(), fwd.z()); + auto right = lepus::types::Vector3(rgt.x(), rgt.y(), rgt.z()); + auto newUp = lepus::types::Vector3(up.x(), up.y(), up.z()); + + // if (i == 1) + // { + // accPos.x(accPos.x() + rotated.x()); + // accPos.y(accPos.y() + rotated.y()); + // accPos.z(accPos.z() + rotated.z()); + // } + // else + { + accPos = accPos + forward * rotated.z() + right * rotated.x() + newUp * rotated.y(); + } } lepus::math::Transform worldTransform = lepus::math::Transform(); + lepus::types::Quaternion normRot = this->m_Transform->Rotation().Normalised(); + lepus::types::Vector3 axis = normRot.Axis(); + float angle = normRot.Angle(); + worldTransform.Rotation(accRot); + lepus::types::Vector4 rotatedAxis = worldTransform.BuildMatrix().Multiply(lepus::types::Vector4(axis)); + axis.x(rotatedAxis.x()); + axis.y(rotatedAxis.y()); + axis.z(rotatedAxis.z()); + worldTransform.Origin(accPos); - worldTransform.Rotate(accRot * this->m_Transform->Rotation()); + worldTransform.Rotate(lepus::types::Quaternion(axis, angle)); accScale = accScale * this->m_Transform->Scale(); worldTransform.SetScale(accScale.x(), accScale.y(), accScale.z()); diff --git a/src/lepus/utility/types/Quaternion.h b/src/lepus/utility/types/Quaternion.h index 314a77a..fec4897 100644 --- a/src/lepus/utility/types/Quaternion.h +++ b/src/lepus/utility/types/Quaternion.h @@ -37,8 +37,6 @@ namespace lepus /// @param angle Rotation angle around the axis Quaternion(float axisX, float axisY, float axisZ, float angle) { - // Negating the angle here so that the Quaternion represents a clockwise rotation along an axis as observed looking towards the origin/object. - angle *= -1.f; float const q[] = {axisX * sinf(angle / 2.f), axisY * sinf(angle / 2.f), axisZ * sinf(angle / 2.f), cos(angle / 2.f)}; init((float*)q); } @@ -123,7 +121,7 @@ namespace lepus inline lepus::types::Vector3 Rotate(const lepus::types::Vector3& v) const { Quaternion p = Quaternion(); - p.w(0.f); + p.w(1.f); p.x(v.x()); p.y(v.y()); p.z(v.z()); diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index e8bb6c8..25446fb 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -39,7 +39,7 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) /// C's local coords are scaled by (1.0/1.5), and are therefore (-2, 0, -2) away from B in world units, and (0, 0, -2) away fom A in world units. /// Therefore, C should end up at (2, 0, -2) in world space, effectively taking B's original world-space position. /// C is also rotated counter-clockwise around the Y axis by 50 degrees. Before applying A's rotation, D ends up at approx. (-3.1, 0, -0.55) away from A in world units. -/// Once A's 90deg rotation is applied then, D should end up at approx (-0.55, 0, -5.06) in world space. +/// Once A's 90deg rotation is applied then, D should end up at approx (-0.57, 0, -5.06) in world space. /// /// (it may be helpful to draw this on a piece of paper) TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) @@ -60,9 +60,9 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) auto childNode2 = childNode1->AddChild(&childTransformable2); auto childNode3 = childNode2->AddChild(&childTransformable3); - const float nodeCTheta = -50.f; + const float nodeCTheta = 50.f; - rootTransformable.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * -0.5f)); + rootTransformable.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * 0.5f)); childTransformable1.GetTransform()->SetScale(1.f / 1.5f); childTransformable2.GetTransform()->Rotate(lepus::types::Quaternion(0.f, 1.f, 0.f, (float)PI * (nodeCTheta / 180.f))); @@ -99,5 +99,5 @@ TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) float cn3PreRotZ = -4.f + 4.f * cosf((float)PI * (nodeCTheta / 180.f)); ASSERT_NEAR(cn3X, 0.f - (cn3PreRotZ - -2.f), 0.0001f); ASSERT_NEAR(cn3Y, 0.f, 0.0001f); - ASSERT_NEAR(cn3Z, -2.f + (cn3PreRotX - 0.f), 0.0001f); + ASSERT_NEAR(cn3Z, -2.f - (cn3PreRotX - 0.f), 0.0001f); } \ No newline at end of file From 2da67c96e02f05d78d08ae58a0c08e4cf2524346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Wed, 16 Oct 2024 23:47:36 +0100 Subject: [PATCH 31/31] refactor(gfx/Transformable): cleaned up GetWorldMatrix() --- src/examples/demo/DemoApp.h | 9 +++---- src/lepus/gfx/SceneGraph/Transformable.h | 32 +++++++----------------- tests/L3D/SceneGraph/SceneGraphTests.cpp | 2 +- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/examples/demo/DemoApp.h b/src/examples/demo/DemoApp.h index 9f3eff9..3597657 100644 --- a/src/examples/demo/DemoApp.h +++ b/src/examples/demo/DemoApp.h @@ -193,12 +193,9 @@ class DemoApp : public system::BaseApp KeyboardState keys = {false, false, false, false, false}; float deltaTime = 0.f; - auto transform = cube2.GetTransform(); - // transform->Origin(lepus::types::Vector3(0.f, 0.f, -2.f)); - // transform->SetScale(0.5f, 0.25f, 1.f); - // Parent the second cube to the first cube. - // cubeNode->AddChild(&cube2); + cube2.GetTransform()->SetScale(1.f, 0.5f, 1.f); + cube3.GetTransform()->SetScale(1.f, 2.f, 1.f); while (isRunning) { @@ -213,7 +210,7 @@ class DemoApp : public system::BaseApp cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), PI * -0.25f)); } cube.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(0.f, 1.f, 0.f), -deltaTime)); - cube2.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), -deltaTime)); + // cube2.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 0.f, 0.f), -deltaTime)); cube3.GetTransform()->Rotate(lepus::types::Quaternion(lepus::types::Vector3(1.f, 1.f, 1.f), -deltaTime)); // Move the child cube back and forth along the parent's Z-axis diff --git a/src/lepus/gfx/SceneGraph/Transformable.h b/src/lepus/gfx/SceneGraph/Transformable.h index d2919f9..d2f692b 100644 --- a/src/lepus/gfx/SceneGraph/Transformable.h +++ b/src/lepus/gfx/SceneGraph/Transformable.h @@ -73,12 +73,12 @@ namespace lepus { auto parentTransform = *(leaves[i - 1]->GetTransformable()->GetTransform()); - lepus::types::Vector4 rotated(leaves[i]->GetTransformable()->GetTransform()->Origin() * accScale); - rotated.w(1.f); + lepus::types::Vector3 scaled = leaves[i]->GetTransformable()->GetTransform()->Origin() * accScale; + accScale.Multiply(parentTransform.Scale()); - // + parentTransform.Origin(lepus::types::Vector3()); - // + lepus::types::Quaternion normParentRot = parentTransform.Rotation().Normalised(); lepus::types::Vector3 axis = normParentRot.Axis(); float angle = normParentRot.Angle(); @@ -88,31 +88,17 @@ namespace lepus lepus::types::Vector4 rotatedAxis = mat.Multiply(lepus::types::Vector4(axis)); parentTransform.Rotation(lepus::types::Quaternion(rotatedAxis.x(), rotatedAxis.y(), rotatedAxis.z(), angle)); - mat = parentTransform.BuildMatrix(); - // rotated = mat.Multiply(rotated); - // rotated = accRot.Rotate(rotated); accRot = accRot * (parentTransform.Rotation()); parentTransform.Rotation(accRot); mat = parentTransform.BuildMatrix(); - auto fwd = mat.Multiply(lepus::types::Vector4(0, 0, 1, 1)), - rgt = mat.Multiply(lepus::types::Vector4(1, 0, 0, 1)), + auto forward = mat.Multiply(lepus::types::Vector4(0, 0, 1, 1)), + right = mat.Multiply(lepus::types::Vector4(1, 0, 0, 1)), up = mat.Multiply(lepus::types::Vector4(0, 1, 0, 1)); - auto forward = lepus::types::Vector3(fwd.x(), fwd.y(), fwd.z()); - auto right = lepus::types::Vector3(rgt.x(), rgt.y(), rgt.z()); - auto newUp = lepus::types::Vector3(up.x(), up.y(), up.z()); - - // if (i == 1) - // { - // accPos.x(accPos.x() + rotated.x()); - // accPos.y(accPos.y() + rotated.y()); - // accPos.z(accPos.z() + rotated.z()); - // } - // else - { - accPos = accPos + forward * rotated.z() + right * rotated.x() + newUp * rotated.y(); - } + accPos.x(accPos.x() + forward.x() * scaled.z() + right.x() * scaled.x() + up.x() * scaled.y()); + accPos.y(accPos.y() + forward.y() * scaled.z() + right.y() * scaled.x() + up.y() * scaled.y()); + accPos.z(accPos.z() + forward.z() * scaled.z() + right.z() * scaled.x() + up.z() * scaled.y()); } lepus::math::Transform worldTransform = lepus::math::Transform(); diff --git a/tests/L3D/SceneGraph/SceneGraphTests.cpp b/tests/L3D/SceneGraph/SceneGraphTests.cpp index 25446fb..4d3bd88 100644 --- a/tests/L3D/SceneGraph/SceneGraphTests.cpp +++ b/tests/L3D/SceneGraph/SceneGraphTests.cpp @@ -41,7 +41,7 @@ TEST(SceneGraphTest, SceneGraphChildIsAddedCorrectly) /// C is also rotated counter-clockwise around the Y axis by 50 degrees. Before applying A's rotation, D ends up at approx. (-3.1, 0, -0.55) away from A in world units. /// Once A's 90deg rotation is applied then, D should end up at approx (-0.57, 0, -5.06) in world space. /// -/// (it may be helpful to draw this on a piece of paper) +/// (it may be helpful to draw this on a piece of paper - which means this test will also not cover everything as it's limited to the XZ plane) TEST(SceneGraphTest, SceneGraphChildTransformsCreateCorrectWorldCoords) { lepus::gfx::SceneGraph sceneGraph = lepus::gfx::SceneGraph();