feat: implemented transform hierarchy

This commit is contained in:
Sara 2024-09-13 00:01:21 +02:00
parent 48d66b946b
commit 67bd8017c5
10 changed files with 188 additions and 58 deletions

View file

@ -1,19 +1,15 @@
#include "camera_node.h" #include "camera_node.h"
#include "utils/typeclass_helpers.h"
#include "utils/debug.h" #include "utils/debug.h"
START_REFLECT(CameraNode); START_REFLECT(CameraNode);
REFLECT_TYPECLASS(CameraNode, Drop); REFLECT_TYPECLASS(CameraNode, Drop);
REFLECT_TYPECLASS(CameraNode, Transformable);
REFLECT_TYPECLASS(CameraNode, SceneNodeEntity); REFLECT_TYPECLASS(CameraNode, SceneNodeEntity);
END_REFLECT(CameraNode); END_REFLECT(CameraNode);
impl_Drop_for(CameraNode, impl_Drop_for(CameraNode,
DestroyCameraNode DestroyCameraNode
) )
impl_Transformable_for(CameraNode,
CameraNodeGetTransform,
CameraNodeGetGlobalTransform
)
impl_SceneNodeEntity_defaults(CameraNode); impl_SceneNodeEntity_defaults(CameraNode);
impl_SceneNodeEntity_for(CameraNode, impl_SceneNodeEntity_for(CameraNode,
@ -23,11 +19,8 @@ impl_SceneNodeEntity_for(CameraNode,
) )
SceneNode *CreateCameraNode() { SceneNode *CreateCameraNode() {
CameraNode *camera_node = new(CameraNode); CameraNode *self = new(CameraNode);
camera_node->transform = TransformIdentity(); return CreateSceneNode(CameraNode_as_SceneNodeEntity(self));
camera_node->global_transform = TransformIdentity();
camera_node->node = NULL;
return CreateSceneNode(CameraNode_as_SceneNodeEntity(camera_node));
} }
void DestroyCameraNode(CameraNode *self) { void DestroyCameraNode(CameraNode *self) {
@ -36,24 +29,20 @@ void DestroyCameraNode(CameraNode *self) {
} }
void CameraNodeEnterTree(CameraNode *self) { void CameraNodeEnterTree(CameraNode *self) {
if(self->node->scene->main_camera == NULL) { if(self->node->scene->main_camera == NULL)
self->node->scene->main_camera = self; self->node->scene->main_camera = self;
LOG_INFO("CameraNodeEnterTree"); self->transform = TC_CAST(self->node->parent->entity, Transformable);
}
} }
Camera3D CameraNodeGetCamera(CameraNode *self) { Camera3D CameraNodeGetCamera(CameraNode *self) {
Matrix mat = TransformGetMatrix(&self->global_transform); Transform global_transform = self->transform.tc->get_global_transform(self->transform.data);
Matrix mat = TransformGetMatrix(&global_transform);
Vector3 forward = { mat.m8, mat.m9, mat.m10 }; Vector3 forward = { mat.m8, mat.m9, mat.m10 };
LOG_INFO("Forward: %f, %f, %f", forward.x, forward.y, forward.z);
return (Camera3D){ return (Camera3D){
.fovy = 90, .fovy = 90,
.position = self->global_transform.translation, .position = global_transform.translation,
.projection = CAMERA_PERSPECTIVE, .projection = CAMERA_PERSPECTIVE,
.target = Vector3Add(self->global_transform.translation, Vector3Scale(forward, 1000.f)), .target = Vector3Add(global_transform.translation, Vector3Scale(forward, 1000.f)),
.up = (Vector3){mat.m4, mat.m5, mat.m6} .up = (Vector3){mat.m4, mat.m5, mat.m6}
}; };
} }
Transform *CameraNodeGetTransform(CameraNode *self) { return &self->transform; }
Transform *CameraNodeGetGlobalTransform(CameraNode *self) { return &self->global_transform; }

View file

@ -9,8 +9,7 @@
typedef struct CameraNode { typedef struct CameraNode {
SceneNode *node; SceneNode *node;
Transform transform; Transformable transform;
Transform global_transform;
} CameraNode; } CameraNode;
extern SceneNode *CreateCameraNode(); extern SceneNode *CreateCameraNode();
@ -20,12 +19,8 @@ extern void CameraNodeEnterTree(CameraNode *self);
extern Camera3D CameraNodeGetCamera(CameraNode *self); extern Camera3D CameraNodeGetCamera(CameraNode *self);
extern Transform *CameraNodeGetTransform(CameraNode *self);
extern Transform *CameraNodeGetGlobalTransform(CameraNode *self);
DECL_REFLECT(CameraNode) DECL_REFLECT(CameraNode)
decl_typeclass_impl(Drop, CameraNode) decl_typeclass_impl(Drop, CameraNode)
decl_typeclass_impl(Transformable, CameraNode)
decl_typeclass_impl(SceneNodeEntity, CameraNode) decl_typeclass_impl(SceneNodeEntity, CameraNode)
#endif // !CAMERA_NODE_H #endif // !CAMERA_NODE_H

View file

@ -35,6 +35,8 @@ void InitializeEngine() {
} }
void ShutDown() { void ShutDown() {
if(GetMainScene() != NULL)
DestroyScene(GetMainScene());
CleanResourceSubsystem(); CleanResourceSubsystem();
CloseWindow(); CloseWindow();
exit(0); exit(0);

View file

@ -1,5 +1,6 @@
#include "scene.h" #include "scene.h"
#include "scene_node_entity.h" #include "scene_node_entity.h"
#include "transformable.h"
#include "utils/debug.h" #include "utils/debug.h"
#include "utils/drop.h" #include "utils/drop.h"
#include "utils/mirror.h" #include "utils/mirror.h"
@ -60,6 +61,19 @@ void Internal_SceneNodeTick(SceneNode *self, double delta) {
Internal_SceneNodeTick(*list_at_as(SceneNode*, &self->children, i), delta); Internal_SceneNodeTick(*list_at_as(SceneNode*, &self->children, i), delta);
} }
//! Update global transforms of all children recursively
static
void Internal_SceneNodeUpdateTransform(SceneNode *self) {
if(TC_MIRRORS(self->entity, Transformable)) {
Transformable transformable = TC_CAST(self->entity, Transformable);
if(transformable.tc->get_dirty_bit(transformable.data)) {
transformable.tc->force_update(transformable.data);
}
}
list_foreach(SceneNode **,child, &self->children)
Internal_SceneNodeUpdateTransform(*child);
}
SceneNode *CreateSceneNode(SceneNodeEntity entity) { SceneNode *CreateSceneNode(SceneNodeEntity entity) {
// initialize memory // initialize memory
SceneNode *self = new(SceneNode); SceneNode *self = new(SceneNode);
@ -152,4 +166,5 @@ void DestroyScene(Scene *self) {
void SceneTick(Scene* self, double delta_time) { void SceneTick(Scene* self, double delta_time) {
Internal_SceneNodeTick(self->root, delta_time); Internal_SceneNodeTick(self->root, delta_time);
Internal_SceneNodeUpdateTransform(self->root);
} }

View file

@ -6,6 +6,7 @@
#include "utils/list.h" #include "utils/list.h"
#include "utils/typeclass_helpers.h" #include "utils/typeclass_helpers.h"
typedef struct SceneNodeEntity SceneNodeEntity; typedef struct SceneNodeEntity SceneNodeEntity;
typedef struct Transformable Transformable;
typedef struct CameraNode CameraNode; typedef struct CameraNode CameraNode;
typedef struct Scene Scene; typedef struct Scene Scene;

View file

@ -10,21 +10,22 @@ END_REFLECT(TransformNode);
impl_Drop_for(TransformNode, impl_Drop_for(TransformNode,
DestroyTransformNode DestroyTransformNode
) )
impl_Transformable_for(TransformNode, impl_Transformable_for(TransformNode)
TransformNodeGetTransform, impl_SceneNodeEntity_defaults(TransformNode)
TransformNodeGetGlobalTransform
)
impl_SceneNodeEntity_defaults(TransformNode);
impl_SceneNodeEntity_for(TransformNode, impl_SceneNodeEntity_for(TransformNode,
TransformNode_default_enter_tree, TransformNodeEnterTree,
TransformNode_default_exit_tree, TransformNodeExitTree,
TransformNode_default_tick TransformNode_default_tick
) )
SceneNode *CreateTransformNode() { SceneNode *CreateTransformNode() {
TransformNode *self = new(TransformNode); TransformNode *self = new(TransformNode);
self->dirty_bit = 0x0;
self->node = NULL;
self->parent_transformable = tc_null(Transformable);
self->global_transform = TransformIdentity(); self->global_transform = TransformIdentity();
self->transform = TransformIdentity(); self->transform = TransformIdentity();
return CreateSceneNode(TransformNode_as_SceneNodeEntity(self)); return CreateSceneNode(TransformNode_as_SceneNodeEntity(self));
} }
@ -32,6 +33,15 @@ void DestroyTransformNode(TransformNode *self) {
free(self); free(self);
} }
void TransformNodeEnterTree(TransformNode *self) {
if(self->node->parent != NULL && TC_MIRRORS(self->node->parent->entity, Transformable))
self->parent_transformable = TC_CAST(self->node->parent->entity, Transformable);
}
void TransformNodeExitTree(TransformNode *self) {
self->parent_transformable = tc_null(Transformable);
}
Transform *TransformNodeGetTransform(TransformNode *self) { Transform *TransformNodeGetTransform(TransformNode *self) {
return &self->transform; return &self->transform;
} }

View file

@ -7,15 +7,16 @@
#include "scene_node_entity.h" #include "scene_node_entity.h"
#include "utils/drop.h" #include "utils/drop.h"
typedef struct TransformNode { typedef struct TransformNode { ExtendsTransformable;
SceneNode *node; SceneNode *node;
Transform transform;
Transform global_transform;
} TransformNode; } TransformNode;
extern SceneNode *CreateTransformNode(); extern SceneNode *CreateTransformNode();
extern void DestroyTransformNode(TransformNode *self); extern void DestroyTransformNode(TransformNode *self);
extern void TransformNodeEnterTree(TransformNode *self);
extern void TransformNodeExitTree(TransformNode *self);
extern Transform *TransformNodeGetTransform(TransformNode *self); extern Transform *TransformNodeGetTransform(TransformNode *self);
extern Transform *TransformNodeGetGlobalTransform(TransformNode *self); extern Transform *TransformNodeGetGlobalTransform(TransformNode *self);

View file

@ -18,11 +18,56 @@ Matrix TransformGetMatrix(Transform const *self) {
return mat; return mat;
} }
void UpdateTransformable(Transformable *self, Transformable *parent) { Vector3 TransformPosition(Transform const *self, Vector3 local_pos) {
Transform *parent_global = self->tc->get_global_transform(self->data); return Vector3Add(TransformDirection(self, local_pos), self->translation);
Transform *global = self->tc->get_global_transform(self->data); }
Transform *local = self->tc->get_transform(self->data);
global->translation = Vector3Add(parent_global->translation, local->translation); Vector3 TransformDirection(Transform const *self, Vector3 local_direction) {
global->rotation = QuaternionMultiply(parent_global->rotation, local->rotation); Matrix const m = TransformGetMatrix(self);
global->scale = Vector3Multiply(parent_global->scale, local->scale); return Vector3Add(Vector3Add(Vector3Scale(MATRIX_RIGHT(m), local_direction.x), Vector3Scale(MATRIX_UP(m), local_direction.y)), Vector3Scale(MATRIX_FORWARD(m), local_direction.z));
}
Vector3 TransformScale(Transform const *self, Vector3 local_scale) {
return Vector3Multiply(self->scale, TransformDirection(self, local_scale));
}
Quaternion TransformRotation(Transform const *self, Quaternion local_rotation) {
return QuaternionMultiply(self->rotation, local_rotation);
}
Transform TransformTransform(Transform const *self, Transform const *other) {
return (Transform) {
.translation = TransformPosition(self, other->translation),
.scale = TransformScale(self, other->scale),
.rotation = TransformRotation(self, other->rotation)
};
}
Vector3 InverseTransformPosition(Transform const *self, Vector3 global_pos) {
return Vector3Subtract(InverseTransformDirection(self, global_pos), self->translation);
}
Vector3 InverseTransformDirection(Transform const *self, Vector3 global_direction) {
Matrix const mat = TransformGetMatrix(self);
return (Vector3){
Vector3DotProduct(MATRIX_RIGHT(mat), Vector3Scale(VECTOR3_RIGHT, global_direction.x)),
Vector3DotProduct(MATRIX_UP(mat), Vector3Scale(VECTOR3_UP, global_direction.y)),
Vector3DotProduct(MATRIX_FORWARD(mat), Vector3Scale(VECTOR3_FORWARD, global_direction.z))
};
}
Vector3 InverseTransformScale(Transform const *self, Vector3 global_scale) {
return Vector3Multiply(Vector3Invert(self->scale), InverseTransformDirection(self, global_scale));
}
Quaternion InverseTransformRotation(Transform const *self, Quaternion global_rotation) {
return QuaternionMultiply(QuaternionInvert(self->rotation), global_rotation);
}
Transform InverseTransformTransform(Transform const *self, Transform const *other) {
return (Transform) {
.translation = InverseTransformPosition(self, other->translation),
.scale = InverseTransformScale(self, other->scale),
.rotation = InverseTransformRotation(self, other->rotation)
};
} }

View file

@ -7,31 +7,90 @@
#include "raylib.h" #include "raylib.h"
#include "raymath.h" #include "raymath.h"
struct Transformable;
typedef struct ITransformable { typedef struct ITransformable {
SceneNode *node; SceneNode *node;
Transform *(*const get_transform)(void*); Transform (*const get_transform)(void*);
Transform *(*const get_global_transform)(void*); void (*const set_transform)(void*, Transform);
Transform (*const get_global_transform)(void*);
void (*const set_global_transform)(void*, Transform);
void (*const force_update)(void*);
unsigned char (*const get_dirty_bit)(void*);
struct Transformable (*const get_parent_transformable)(void*);
} ITransformable; } ITransformable;
typedef struct Transformable { typedef struct Transformable {
ITransformable const *tc; ITransformable const *tc;
void *data; void *data;
IMirror const *mirror; IMirror const *mirror;
ISceneNodeEntity const *scene_node_entity;
} Transformable; } Transformable;
extern Transform TransformIdentity(); extern Transform TransformIdentity();
extern Matrix TransformGetMatrix(Transform const *self); extern Matrix TransformGetMatrix(Transform const *self);
extern void UpdateTransformable(Transformable *self, Transformable *parent);
#define impl_Transformable_for(T, get_transform_f, get_global_transform_f)\ extern Vector3 TransformPosition(Transform const *self, Vector3 local_pos);
extern Vector3 TransformDirection(Transform const *self, Vector3 local_direction);
extern Vector3 TransformScale(Transform const *self, Vector3 local_scale);
extern Quaternion TransformRotation(Transform const *self, Quaternion local_rotation);
extern Transform TransformTransform(Transform const *self, Transform const *other);
extern Vector3 InverseTransformPosition(Transform const *self, Vector3 global_pos);
extern Vector3 InverseTransformDirection(Transform const *self, Vector3 global_direction);
extern Vector3 InverseTransformScale(Transform const *self, Vector3 global_scale);
extern Quaternion InverseTransformRotation(Transform const *self, Quaternion quat);
extern Transform InverseTransformTransform(Transform const *self, Transform const *other);
#define MATRIX_UP(self_) ((Vector3){self_.m0, self_.m1, self_.m2})
#define MATRIX_RIGHT(self_) ((Vector3){self_.m4, self_.m5, self_.m6})
#define MATRIX_FORWARD(self_) ((Vector3){self_.m8, self_.m9, self_.m10})
#define VECTOR3_RIGHT ((Vector3){1.f, 0.f, 0.f})
#define VECTOR3_UP ((Vector3){0.f, 1.f, 0.f})
#define VECTOR3_FORWARD ((Vector3){0.f, 0.f, 1.f})
//! Defines the variables required to implement Transformable. Put at the start of a struct
#define ExtendsTransformable Transform transform; Transform global_transform; Transformable parent_transformable; unsigned char dirty_bit
//! Define transformable getter-setters for type T
//! Requires member variables: Transform transform; Transform global_transform; Transformable parent_transformable; unsigned char dirty_bit;
//! Macro implements the interface functions itself to ensure consistent behaviour.
#define impl_Transformable_for(T)\
static Transform T##_get_transform_GEN_(T* self) { return self->transform; }\
static void T##_set_transform_GEN_(T* self, Transform value) { self->transform = value; self->dirty_bit |= 0x1; } /*!< sets local dirty bit */\
static Transform T##_get_global_transform_GEN_(T* self) { return self->global_transform; }\
static void T##_set_global_transform_GEN_(T* self, Transform value) {\
self->global_transform = value;\
if(tc_is_null(self->parent_transformable)) {\
self->transform = value;\
} else {\
self->parent_transformable.tc->force_update(self->parent_transformable.data); /* Force a transform update for the parent, to ensure its global transform is up-to-date */\
Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\
self->transform = InverseTransformTransform(&parent_global, &value); /* Use up-to-date parent transform to get the local transform to the new global transform */\
}\
}\
static void T##_force_update_GEN_(T *self) {\
if(tc_is_null(self->parent_transformable)) {\
self->global_transform = self->transform;\
return;\
}\
Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\
self->global_transform = TransformTransform(&parent_global, &self->transform);\
}\
static unsigned char T##_get_dirty_bit_GEN_(T *self) { return self->dirty_bit; }\
static Transformable T##_get_parent_transformable_GEN_(T *self) { return self->parent_transformable; }\
Transformable T##_as_Transformable(T *x) {\ Transformable T##_as_Transformable(T *x) {\
TC_FN_TYPECHECK(Transform *,get_transform_f, T*);\
TC_FN_TYPECHECK(Transform *,get_global_transform_f, T*);\
static ITransformable const tc = {\ static ITransformable const tc = {\
.get_transform = (Transform *(*const)(void*)) get_transform_f,\ .get_transform = (Transform (*const)(void*)) T##_get_transform_GEN_,\
.get_global_transform = (Transform *(*const)(void*)) get_global_transform_f\ .set_transform = (void (*const)(void*,Transform)) T##_set_transform_GEN_,\
.get_global_transform = (Transform (*const)(void*)) T##_get_global_transform_GEN_,\
.set_global_transform = (void (*const)(void*,Transform)) T##_set_global_transform_GEN_,\
.force_update = (void (*const)(void*)) T##_force_update_GEN_,\
.get_dirty_bit = (unsigned char (*const)(void*)) T##_get_dirty_bit_GEN_,\
.get_parent_transformable = (Transformable (*const)(void*)) T##_get_parent_transformable_GEN_\
};\ };\
return (Transformable){ .tc = &tc, .data = x, .mirror = T##_as_Mirror(x).tc };\ return (Transformable){ .tc = &tc, .data = x, .scene_node_entity = T##_as_SceneNodeEntity(x).tc, .mirror = T##_as_Mirror(x).tc };\
} }
#endif // !TRANSFORMABLE_H #endif // !TRANSFORMABLE_H

View file

@ -1,4 +1,5 @@
#include "raylib.h" #include "raylib.h"
#include "test_object.h"
#include "core/camera_node.h" #include "core/camera_node.h"
#include "core/engine_global.h" #include "core/engine_global.h"
#include "core/engine_loop.h" #include "core/engine_loop.h"
@ -8,12 +9,24 @@
Scene *CreateInitialScene() { Scene *CreateInitialScene() {
SceneNode *root = CreateTransformNode(); SceneNode *root = CreateTransformNode();
SceneNode *camera_node = CreateCameraNode(); SceneNode *camera_parent_parent = CreateTransformNode();
Transformable camera = TC_CAST(camera_node->entity, Transformable); Transformable transformable = TC_CAST(camera_parent_parent->entity, Transformable);
Transform *camera_global = camera.tc->get_global_transform(camera.data); Transform transform = transformable.tc->get_transform(transformable.data);
camera_global->translation = (Vector3){0.f, 10.f, -10.f}; transform.translation = (Vector3){10.f, 0.f, 0.f};
camera_global->rotation = QuaternionFromEuler(45.f * DEG2RAD, 0.f, 0.f); transform.rotation = QuaternionFromEuler(0.f, -10.f * DEG2RAD, 0.f);
SceneNodeAddChild(root, camera_node); transformable.tc->set_transform(transformable.data, transform);
// create camera with a transform parent
SceneNode *camera_parent = CreateTransformNode();
// set camera parent location
transformable = TC_CAST(camera_parent->entity, Transformable);
transform = transformable.tc->get_transform(transformable.data);
transform.translation = (Vector3){0.f, 10.f, -10.f};
transform.rotation = QuaternionFromEuler(45.f * DEG2RAD, 0.f, 0.f);
transformable.tc->set_transform(transformable.data, transform);
SceneNodeAddChild(root, camera_parent_parent);
SceneNodeAddChild(camera_parent_parent, camera_parent);
SceneNodeAddChild(camera_parent, CreateCameraNode());
SceneNodeAddChild(root, CreateTestObject());
return CreateScene(root); return CreateScene(root);
} }