Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
scene_component_tests.cpp
Go to the documentation of this file.
1// This file is part of the Rendering Engine project.
2// Author: Alexander Obzherin <alexanderobzherin@gmail.com>
3// Copyright (c) 2026 Alexander Obzherin
4// Distributed under the terms of the zlib License. See LICENSE.md for details.
5
6#include "scene_component.hpp"
8
9using namespace rendering_engine;
11
12TEST(SceneComponent, DefaultsToIdentityAndEngineAxes)
13{
14 SceneComponent component;
15 ExpectNear(component.GetPosition(), glm::vec3(0));
16 ExpectNear(component.GetScale(), glm::vec3(1));
17 ExpectNear(component.GetRotation(), glm::vec3(0));
18 ExpectNear(glm::mat4_cast(component.GetRotationQuat()), glm::mat4(1));
19 ExpectNear(component.GetWorldMatrix(), glm::mat4(1));
20 ExpectNear(component.GetForward(), glm::vec3(1, 0, 0));
21 ExpectNear(component.GetRight(), glm::vec3(0, 1, 0));
22 ExpectNear(component.GetUp(), glm::vec3(0, 0, 1));
23}
24
25TEST(SceneComponent, PositionAndScaleUpdateAnEvaluatedRoot)
26{
27 SceneComponent component;
28 component.GetWorldMatrix();
29 component.SetPosition(glm::vec3(3, -4, 5));
30 component.SetScale(glm::vec3(2, 3, 4));
31 ExpectNear(component.GetPosition(), glm::vec3(3, -4, 5));
32 ExpectNear(component.GetWorldPosition(), glm::vec3(3, -4, 5));
33 ExpectNear(component.GetScale(), glm::vec3(2, 3, 4));
34 ExpectNear(component.GetWorldScale(), glm::vec3(2, 3, 4));
35 glm::mat4 expected(1);
36 expected[0][0] = 2;
37 expected[1][1] = 3;
38 expected[2][2] = 4;
39 expected[3] = glm::vec4(3, -4, 5, 1);
40 ExpectNear(component.GetWorldMatrix(), expected);
41}
42
43TEST(SceneComponent, IndependentEulerRotationsUseDegreesAndEngineSigns)
44{
45 struct Case
46 {
47 glm::vec3 angles;
48 glm::vec3 forward;
49 glm::vec3 right;
50 glm::vec3 up;
51 };
52 const Case cases[] = {
53 {{90, 0, 0}, {0, 0, 1}, {0, 1, 0}, {-1, 0, 0}},
54 {{0, 90, 0}, {0, -1, 0}, {1, 0, 0}, {0, 0, 1}},
55 {{0, 0, 90}, {1, 0, 0}, {0, 0, -1}, {0, 1, 0}}
56 };
57 SceneComponent component;
58 for (const auto& item : cases)
59 {
60 SCOPED_TRACE(::testing::Message() << "Euler " << item.angles.x << ", "
61 << item.angles.y << ", " << item.angles.z);
62 component.SetRotation(item.angles);
63 ExpectNear(component.GetRotation(), item.angles);
64 ExpectNear(component.GetForward(), item.forward);
65 ExpectNear(component.GetRight(), item.right);
66 ExpectNear(component.GetUp(), item.up);
67 }
68}
69
70TEST(SceneComponent, QuaternionSetterControlsOrientation)
71{
72 SceneComponent component;
73 const auto rotation = glm::angleAxis(glm::radians(90.0f), glm::vec3(0, 0, 1));
74 component.SetRotation(rotation);
75 // Compare orientations, allowing the equivalent quaternion signs q and -q.
76 EXPECT_NEAR(glm::abs(glm::dot(component.GetRotationQuat(), rotation)), 1.0f, 1e-5f);
77 ExpectNear(component.GetForward(), glm::vec3(0, 1, 0));
78 ExpectNear(component.GetRight(), glm::vec3(-1, 0, 0));
79 ExpectNear(component.GetUp(), glm::vec3(0, 0, 1));
80 ExpectNear(glm::mat4_cast(component.GetWorldRotation()), glm::mat4_cast(rotation));
81 // Quaternion-to-Euler conversion is deferred pending its public convention.
82}
83
84TEST(SceneComponent, ScalesThenRotatesThenTranslates)
85{
86 SceneComponent component;
87 component.SetPosition(glm::vec3(10, 20, 30));
88 component.SetRotation(glm::vec3(0, 90, 0));
89 component.SetScale(glm::vec3(2, 3, 4));
90 // (1,2,3) -> (2,6,12) -> (6,-2,12) -> (16,18,42).
91 ExpectNear(component.GetWorldMatrix() * glm::vec4(1, 2, 3, 1), glm::vec4(16, 18, 42, 1));
92 ExpectNear(component.GetWorldMatrix() * glm::vec4(1, 2, 3, 0), glm::vec4(6, -2, 12, 0));
93}
94
95TEST(SceneComponent, ParentTranslationRotationAndScaleAffectChild)
96{
97 SceneComponent parent;
98 SceneComponent child;
99 child.SetPosition(glm::vec3(1, 2, 3));
100 child.AttachTo(&parent);
101 parent.SetPosition(glm::vec3(10, 20, 30));
102 parent.UpdateWorldMatrix();
103 child.UpdateWorldMatrix();
104 ExpectNear(child.GetWorldPosition(), glm::vec3(11, 22, 33));
105
106 parent.SetPosition(glm::vec3(0));
107 parent.SetRotation(glm::vec3(0, 90, 0));
108 parent.UpdateWorldMatrix();
109 child.UpdateWorldMatrix();
110 ExpectNear(child.GetWorldPosition(), glm::vec3(2, -1, 3));
111 ExpectNear(glm::mat4_cast(child.GetWorldRotation()), glm::mat4_cast(parent.GetRotationQuat()));
112
113 parent.SetRotation(glm::vec3(0));
114 parent.SetScale(glm::vec3(2, 3, 4));
115 child.SetScale(glm::vec3(3, 2, 5));
116 parent.UpdateWorldMatrix();
117 child.UpdateWorldMatrix();
118 ExpectNear(child.GetWorldPosition(), glm::vec3(2, 6, 12));
119 ExpectNear(child.GetWorldScale(), glm::vec3(6, 6, 20));
120}
121
122TEST(SceneComponent, NestedTransformsRefreshWithExplicitUpdates)
123{
124 SceneComponent root;
125 SceneComponent child;
126 SceneComponent leaf;
127 root.SetPosition(glm::vec3(10, 20, 30));
128 root.SetRotation(glm::vec3(0, 90, 0));
129 root.SetScale(glm::vec3(2));
130 child.SetPosition(glm::vec3(1, 0, 0));
131 child.SetRotation(glm::vec3(0, 90, 0));
132 child.SetScale(glm::vec3(3));
133 child.AttachTo(&root);
134 leaf.SetPosition(glm::vec3(1, 0, 0));
135 leaf.AttachTo(&child);
136 root.UpdateWorldMatrix();
137 child.UpdateWorldMatrix();
138 leaf.UpdateWorldMatrix();
139 glm::mat4 expected(1);
140 expected[0][0] = -6;
141 expected[1][1] = -6;
142 expected[2][2] = 6;
143 expected[3] = glm::vec4(4, 18, 30, 1);
144 ExpectNear(leaf.GetWorldMatrix(), expected);
145 ExpectNear(leaf.GetWorldPosition(), glm::vec3(4, 18, 30));
146 ExpectNear(leaf.GetWorldScale(), glm::vec3(6));
147
148 // Actor/Drawable update flow explicitly refreshes each world transform.
149 root.SetPosition(glm::vec3(20, 30, 40));
150 root.UpdateWorldMatrix();
151 child.UpdateWorldMatrix();
152 leaf.UpdateWorldMatrix();
153 expected[3] = glm::vec4(14, 28, 40, 1);
154 ExpectNear(leaf.GetWorldMatrix(), expected);
155}
156
157TEST(SceneComponent, WorldRotationCombinesUnscaledParentAndChild)
158{
159 SceneComponent parent;
160 SceneComponent child;
161 parent.SetRotation(glm::vec3(0, 90, 0));
162 child.SetRotation(glm::vec3(0, 0, 90));
163 child.AttachTo(&parent);
164 parent.UpdateWorldMatrix();
165 child.UpdateWorldMatrix();
166 // Roll maps local Y to -Z and Z to Y; parent's yaw then maps Y to X.
167 glm::mat4 expected(1);
168 expected[0] = glm::vec4(0, -1, 0, 0);
169 expected[1] = glm::vec4(0, 0, -1, 0);
170 expected[2] = glm::vec4(1, 0, 0, 0);
171 ExpectNear(glm::mat4_cast(child.GetWorldRotation()), expected);
172 ExpectNear(child.GetWorldMatrix(), expected);
173}
174
175TEST(SceneComponent, DetachRetainsLocalTransform)
176{
177 SceneComponent parent;
178 SceneComponent child;
179 child.SetPosition(glm::vec3(1, 2, 3));
180 child.SetRotation(glm::vec3(10, 20, 30));
181 child.SetScale(glm::vec3(2, 3, 4));
182 const glm::mat4 local = child.GetWorldMatrix();
183 parent.SetPosition(glm::vec3(10, 0, 0));
184 child.AttachTo(&parent);
185 child.UpdateWorldMatrix();
186 ExpectNear(child.GetWorldPosition(), glm::vec3(11, 2, 3));
187 child.AttachTo(nullptr);
188 ExpectNear(child.GetWorldMatrix(), local);
189 ExpectNear(child.GetPosition(), glm::vec3(1, 2, 3));
190 ExpectNear(child.GetRotation(), glm::vec3(10, 20, 30));
191 ExpectNear(child.GetScale(), glm::vec3(2, 3, 4));
192}
Represents a 3D transformable scene component with position, rotation, and scale.
glm::vec3 GetRight() const
Returns the right (local Y+) direction vector in world space.
const glm::vec3 & GetPosition() const
Gets the current position.
void AttachTo(SceneComponent *parent)
Attaches this scene component to a parent scene component.
const glm::vec3 & GetRotation() const
Gets the current rotation as Euler angles (in degrees).
const glm::mat4 & GetWorldMatrix()
Returns the world transformation matrix (model matrix).
void SetRotation(const glm::quat &rotation)
Sets the rotation using a quaternion.
glm::vec3 GetWorldPosition() const
Returns the world-space position of this component.
void SetPosition(const glm::vec3 &position)
Sets the position of the component in world space.
const glm::vec3 & GetScale() const
Gets the current scale.
glm::vec3 GetForward() const
Returns the forward (local X+) direction vector in world space.
glm::quat GetWorldRotation() const
Returns the world-space rotation of this component.
void SetScale(const glm::vec3 &scale)
Sets the scale for each dimension.
glm::vec3 GetWorldScale() const
Returns the world-space scale of this component.
const glm::quat & GetRotationQuat() const
Gets the current rotation as a quaternion.
glm::vec3 GetUp() const
Returns the up (local Z+) direction vector in world space.
void ExpectNear(const glm::vec< N, T, Q > &actual, const glm::vec< N, T, Q > &expected, float tolerance=1e-5f)
TEST(SceneComponent, DefaultsToIdentityAndEngineAxes)