From 67bd8017c591dc0e659d571358c899eb466b3ccc Mon Sep 17 00:00:00 2001 From: Sara Date: Fri, 13 Sep 2024 00:01:21 +0200 Subject: [PATCH] feat: implemented transform hierarchy --- src/core/camera_node.c | 29 +++++---------- src/core/camera_node.h | 7 +--- src/core/engine_loop.c | 2 + src/core/scene.c | 15 ++++++++ src/core/scene.h | 1 + src/core/transform_node.c | 24 ++++++++---- src/core/transform_node.h | 7 ++-- src/core/transformable.c | 59 ++++++++++++++++++++++++++---- src/core/transformable.h | 77 ++++++++++++++++++++++++++++++++++----- src/main.c | 25 ++++++++++--- 10 files changed, 188 insertions(+), 58 deletions(-) diff --git a/src/core/camera_node.c b/src/core/camera_node.c index af64e39..40ce8af 100644 --- a/src/core/camera_node.c +++ b/src/core/camera_node.c @@ -1,19 +1,15 @@ #include "camera_node.h" +#include "utils/typeclass_helpers.h" #include "utils/debug.h" START_REFLECT(CameraNode); REFLECT_TYPECLASS(CameraNode, Drop); -REFLECT_TYPECLASS(CameraNode, Transformable); REFLECT_TYPECLASS(CameraNode, SceneNodeEntity); END_REFLECT(CameraNode); impl_Drop_for(CameraNode, DestroyCameraNode ) -impl_Transformable_for(CameraNode, - CameraNodeGetTransform, - CameraNodeGetGlobalTransform -) impl_SceneNodeEntity_defaults(CameraNode); impl_SceneNodeEntity_for(CameraNode, @@ -23,11 +19,8 @@ impl_SceneNodeEntity_for(CameraNode, ) SceneNode *CreateCameraNode() { - CameraNode *camera_node = new(CameraNode); - camera_node->transform = TransformIdentity(); - camera_node->global_transform = TransformIdentity(); - camera_node->node = NULL; - return CreateSceneNode(CameraNode_as_SceneNodeEntity(camera_node)); + CameraNode *self = new(CameraNode); + return CreateSceneNode(CameraNode_as_SceneNodeEntity(self)); } void DestroyCameraNode(CameraNode *self) { @@ -36,24 +29,20 @@ void DestroyCameraNode(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; - LOG_INFO("CameraNodeEnterTree"); - } + self->transform = TC_CAST(self->node->parent->entity, Transformable); } 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 }; - LOG_INFO("Forward: %f, %f, %f", forward.x, forward.y, forward.z); return (Camera3D){ .fovy = 90, - .position = self->global_transform.translation, + .position = global_transform.translation, .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} }; } - -Transform *CameraNodeGetTransform(CameraNode *self) { return &self->transform; } -Transform *CameraNodeGetGlobalTransform(CameraNode *self) { return &self->global_transform; } diff --git a/src/core/camera_node.h b/src/core/camera_node.h index 69cb6ba..b31bd25 100644 --- a/src/core/camera_node.h +++ b/src/core/camera_node.h @@ -9,8 +9,7 @@ typedef struct CameraNode { SceneNode *node; - Transform transform; - Transform global_transform; + Transformable transform; } CameraNode; extern SceneNode *CreateCameraNode(); @@ -20,12 +19,8 @@ extern void CameraNodeEnterTree(CameraNode *self); extern Camera3D CameraNodeGetCamera(CameraNode *self); -extern Transform *CameraNodeGetTransform(CameraNode *self); -extern Transform *CameraNodeGetGlobalTransform(CameraNode *self); - DECL_REFLECT(CameraNode) decl_typeclass_impl(Drop, CameraNode) -decl_typeclass_impl(Transformable, CameraNode) decl_typeclass_impl(SceneNodeEntity, CameraNode) #endif // !CAMERA_NODE_H diff --git a/src/core/engine_loop.c b/src/core/engine_loop.c index e875f48..8947cac 100644 --- a/src/core/engine_loop.c +++ b/src/core/engine_loop.c @@ -35,6 +35,8 @@ void InitializeEngine() { } void ShutDown() { + if(GetMainScene() != NULL) + DestroyScene(GetMainScene()); CleanResourceSubsystem(); CloseWindow(); exit(0); diff --git a/src/core/scene.c b/src/core/scene.c index 8c2a050..d53d542 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -1,5 +1,6 @@ #include "scene.h" #include "scene_node_entity.h" +#include "transformable.h" #include "utils/debug.h" #include "utils/drop.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); } +//! 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) { // initialize memory SceneNode *self = new(SceneNode); @@ -152,4 +166,5 @@ void DestroyScene(Scene *self) { void SceneTick(Scene* self, double delta_time) { Internal_SceneNodeTick(self->root, delta_time); + Internal_SceneNodeUpdateTransform(self->root); } diff --git a/src/core/scene.h b/src/core/scene.h index 60d7b25..d026d02 100644 --- a/src/core/scene.h +++ b/src/core/scene.h @@ -6,6 +6,7 @@ #include "utils/list.h" #include "utils/typeclass_helpers.h" typedef struct SceneNodeEntity SceneNodeEntity; +typedef struct Transformable Transformable; typedef struct CameraNode CameraNode; typedef struct Scene Scene; diff --git a/src/core/transform_node.c b/src/core/transform_node.c index 24cce1f..f060287 100644 --- a/src/core/transform_node.c +++ b/src/core/transform_node.c @@ -10,21 +10,22 @@ END_REFLECT(TransformNode); impl_Drop_for(TransformNode, DestroyTransformNode ) -impl_Transformable_for(TransformNode, - TransformNodeGetTransform, - TransformNodeGetGlobalTransform -) -impl_SceneNodeEntity_defaults(TransformNode); +impl_Transformable_for(TransformNode) +impl_SceneNodeEntity_defaults(TransformNode) impl_SceneNodeEntity_for(TransformNode, - TransformNode_default_enter_tree, - TransformNode_default_exit_tree, + TransformNodeEnterTree, + TransformNodeExitTree, TransformNode_default_tick ) SceneNode *CreateTransformNode() { TransformNode *self = new(TransformNode); + self->dirty_bit = 0x0; + self->node = NULL; + self->parent_transformable = tc_null(Transformable); self->global_transform = TransformIdentity(); self->transform = TransformIdentity(); + return CreateSceneNode(TransformNode_as_SceneNodeEntity(self)); } @@ -32,6 +33,15 @@ void DestroyTransformNode(TransformNode *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) { return &self->transform; } diff --git a/src/core/transform_node.h b/src/core/transform_node.h index 80382c0..d178ce3 100644 --- a/src/core/transform_node.h +++ b/src/core/transform_node.h @@ -7,15 +7,16 @@ #include "scene_node_entity.h" #include "utils/drop.h" -typedef struct TransformNode { +typedef struct TransformNode { ExtendsTransformable; SceneNode *node; - Transform transform; - Transform global_transform; } TransformNode; extern SceneNode *CreateTransformNode(); extern void DestroyTransformNode(TransformNode *self); +extern void TransformNodeEnterTree(TransformNode *self); +extern void TransformNodeExitTree(TransformNode *self); + extern Transform *TransformNodeGetTransform(TransformNode *self); extern Transform *TransformNodeGetGlobalTransform(TransformNode *self); diff --git a/src/core/transformable.c b/src/core/transformable.c index a004da7..2e9af7c 100644 --- a/src/core/transformable.c +++ b/src/core/transformable.c @@ -18,11 +18,56 @@ Matrix TransformGetMatrix(Transform const *self) { return mat; } -void UpdateTransformable(Transformable *self, Transformable *parent) { - Transform *parent_global = self->tc->get_global_transform(self->data); - 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); - global->rotation = QuaternionMultiply(parent_global->rotation, local->rotation); - global->scale = Vector3Multiply(parent_global->scale, local->scale); +Vector3 TransformPosition(Transform const *self, Vector3 local_pos) { + return Vector3Add(TransformDirection(self, local_pos), self->translation); +} + +Vector3 TransformDirection(Transform const *self, Vector3 local_direction) { + Matrix const m = TransformGetMatrix(self); + 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) + }; } diff --git a/src/core/transformable.h b/src/core/transformable.h index dfea8ad..294d98d 100644 --- a/src/core/transformable.h +++ b/src/core/transformable.h @@ -7,31 +7,90 @@ #include "raylib.h" #include "raymath.h" +struct Transformable; + typedef struct ITransformable { SceneNode *node; - Transform *(*const get_transform)(void*); - Transform *(*const get_global_transform)(void*); + Transform (*const get_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; typedef struct Transformable { ITransformable const *tc; void *data; IMirror const *mirror; + ISceneNodeEntity const *scene_node_entity; } Transformable; extern Transform TransformIdentity(); 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) {\ - TC_FN_TYPECHECK(Transform *,get_transform_f, T*);\ - TC_FN_TYPECHECK(Transform *,get_global_transform_f, T*);\ static ITransformable const tc = {\ - .get_transform = (Transform *(*const)(void*)) get_transform_f,\ - .get_global_transform = (Transform *(*const)(void*)) get_global_transform_f\ + .get_transform = (Transform (*const)(void*)) T##_get_transform_GEN_,\ + .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 diff --git a/src/main.c b/src/main.c index d1566cf..d3d0293 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include "raylib.h" +#include "test_object.h" #include "core/camera_node.h" #include "core/engine_global.h" #include "core/engine_loop.h" @@ -8,12 +9,24 @@ Scene *CreateInitialScene() { SceneNode *root = CreateTransformNode(); - SceneNode *camera_node = CreateCameraNode(); - Transformable camera = TC_CAST(camera_node->entity, Transformable); - Transform *camera_global = camera.tc->get_global_transform(camera.data); - camera_global->translation = (Vector3){0.f, 10.f, -10.f}; - camera_global->rotation = QuaternionFromEuler(45.f * DEG2RAD, 0.f, 0.f); - SceneNodeAddChild(root, camera_node); + SceneNode *camera_parent_parent = CreateTransformNode(); + Transformable transformable = TC_CAST(camera_parent_parent->entity, Transformable); + Transform transform = transformable.tc->get_transform(transformable.data); + transform.translation = (Vector3){10.f, 0.f, 0.f}; + transform.rotation = QuaternionFromEuler(0.f, -10.f * DEG2RAD, 0.f); + 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); }