Skip to content

Commit

Permalink
feat(gfx/SceneGraph): added world transforms for child nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
tomezpl committed Oct 12, 2024
1 parent f5efd1e commit 50b7388
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 57 deletions.
43 changes: 6 additions & 37 deletions src/examples/demo/DemoApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,7 @@ class DemoApp : public system::BaseApp
auto cube = lepus::gfx::Renderable<lepus::gfx::GLMesh>(&cubeMesh, lepus::math::Transform());
auto cube2 = lepus::gfx::Renderable<lepus::gfx::GLMesh>(&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<lepus::gfx::GLMesh>(&cubeMesh, lepus::math::Transform());
auto cubeY = lepus::gfx::Renderable<lepus::gfx::GLMesh>(&cubeMesh, lepus::math::Transform());
auto cubeZ = lepus::gfx::Renderable<lepus::gfx::GLMesh>(&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();
Expand All @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<GLuint, GLMatrixUniformBinding>(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());
Expand Down
2 changes: 1 addition & 1 deletion src/lepus/gfx/SceneGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace lepus
}

template <class TransformableType = Transformable>
const Node* AddChild(const TransformableType* transformable)
Node* AddChild(const TransformableType* transformable)
{
return m_Root->AddChild((const Transformable*)transformable);
}
Expand Down
17 changes: 1 addition & 16 deletions src/lepus/gfx/SceneGraph/Renderable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace lepus
namespace gfx
{
template <class MeshType = lepus::engine::objects::Mesh>
class Renderable : protected Transformable
class Renderable : public Transformable
{
private:
const MeshType* m_Mesh;
Expand Down Expand Up @@ -48,21 +48,6 @@ namespace lepus
{
return m_Mesh;
}

[[nodiscard]] lepus::math::Transform& GetTransform() const
{
return const_cast<lepus::math::Transform&>(*reinterpret_cast<const lepus::math::Transform*>(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
Expand Down
5 changes: 3 additions & 2 deletions src/lepus/gfx/SceneGraph/SceneNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
#define LEPUS_GFX_SCENEGRAPH_SCENENODE

#include <lepus/gfx/SceneGraph.h>
#include "Transformable.h"

namespace lepus
{
namespace gfx
{
class Transformable;

class SceneNode
{
private:
Expand Down Expand Up @@ -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;
Expand Down
80 changes: 80 additions & 0 deletions src/lepus/gfx/SceneGraph/Transformable.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace lepus
{
namespace gfx
{
class SceneNode;

class Transformable
{
protected:
Expand All @@ -20,6 +22,84 @@ namespace lepus
m_OwnsTransform = true;
}

[[nodiscard]] lepus::math::Transform& GetTransform() const
{
return const_cast<lepus::math::Transform&>(*reinterpret_cast<const lepus::math::Transform*>(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<SceneNode*>(node);
const SceneNode** leaves = nullptr;
uint8_t depth = 1;
while (root->Parent() != nullptr && root->Parent()->Parent() != nullptr)
{
root = const_cast<SceneNode*>(root->Parent());
depth++;
}

leaves = new const SceneNode*[depth];
leaves[depth - 1] = node;
auto currentLeaf = const_cast<SceneNode*>(node);
for (int i = (int)depth - 2; depth > 1 && i >= 0; i--)
{
currentLeaf = const_cast<SceneNode*>(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)
Expand Down

0 comments on commit 50b7388

Please sign in to comment.