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)