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 "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; }

View file

@ -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

View file

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

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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)
};
}

View file

@ -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

View file

@ -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);
}