Skip to content

Commit

Permalink
fix(gfx/scenegraph): fixed transform not being updated, resolved PR c…
Browse files Browse the repository at this point in the history
…omments
  • Loading branch information
tomezpl committed Oct 13, 2024
1 parent 0b81e40 commit 18a223f
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 160 deletions.
10 changes: 6 additions & 4 deletions src/examples/demo/DemoApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
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 @@ -23,7 +23,7 @@ namespace lepus
template <class TransformableType = Transformable>
Node* AddChild(const TransformableType* transformable)
{
return m_Root->AddChild((const Transformable*)transformable);
return m_Root->AddChild(static_cast<const Transformable*>(transformable));
}

~SceneGraph()
Expand Down
1 change: 1 addition & 0 deletions src/lepus/gfx/SceneGraph/Renderable.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace lepus
{
namespace gfx
{
/// @brief A base class for any renderable node that can appear in a scene.
template <class MeshType = lepus::engine::objects::Mesh>
class Renderable : public Transformable
{
Expand Down
27 changes: 14 additions & 13 deletions src/lepus/gfx/SceneGraph/Transformable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -22,9 +23,9 @@ namespace lepus
m_OwnsTransform = true;
}

[[nodiscard]] lepus::math::Transform& GetTransform() const
[[nodiscard]] lepus::math::Transform* GetTransform() const
{
return const_cast<lepus::math::Transform&>(*reinterpret_cast<const lepus::math::Transform*>(m_Transform));
return m_Transform;
}

/// @brief Constructs a world transform matrix for this Transformable by traversing up the scene hierarchy.
Expand All @@ -33,17 +34,17 @@ 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.
// 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)
while (root->Parent() != nullptr && !root->Parent()->IsRoot())
{
root = const_cast<SceneNode*>(root->Parent());
depth++;
Expand All @@ -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();
Expand Down
267 changes: 133 additions & 134 deletions src/lepus/utility/types/Quaternion.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 18a223f

Please sign in to comment.