feat: implemented camera controller

This commit is contained in:
Sara 2024-09-25 11:58:37 +02:00
parent 0f612b0bd6
commit 4704f9ca7b
10 changed files with 153 additions and 30 deletions

53
src/camera_controller.c Normal file
View file

@ -0,0 +1,53 @@
#include "camera_controller.h"
#include "utils/mirror.h"
START_REFLECT(CameraController);
REFLECT_TYPECLASS(CameraController, SceneNodeEntity);
END_REFLECT(CameraController);
impl_Drop_for(CameraController,
DestroyCameraController
)
impl_SceneNodeEntity_for(CameraController,
CameraControllerEnterTree,
CameraControllerExitTree,
CameraControllerTick
)
SceneNode *CreateCameraController(Transformable target) {
CameraController *self = new(CameraController);
*self = (CameraController){
.transform = tc_null(Transformable),
.rotation_speed = 10.f,
.max_speed_time = 4.f,
.target = target,
.time_disjointed = 0.f
};
SceneNode *node = CreateSceneNode(CameraController_as_SceneNodeEntity(self));
return node;
}
void DestroyCameraController(CameraController *self) {
free(self);
}
void CameraControllerEnterTree(CameraController *self) {
self->transform = TC_CAST(self->node->parent->entity, Transformable);
self->global = self->transform.tc->get_global_transform(self->transform.data);
}
void CameraControllerExitTree(CameraController *self) {}
void CameraControllerTick(CameraController *self, double delta) {
Transform current = self->transform.tc->get_global_transform(self->transform.data);
Transform target = self->target.tc->get_global_transform(self->target.data);
float const time_mul = fminf(1.f, powf(self->time_disjointed / self->max_speed_time, 0.8f));
float const angle = QuaternionAngleDifference(current.rotation, target.rotation);
float const step = self->rotation_speed * time_mul * delta;
self->time_disjointed += delta;
if(step < angle)
target.rotation = QuaternionSlerp(current.rotation, target.rotation, step / angle);
else
self->time_disjointed = 0.f;
self->transform.tc->set_global_transform(self->transform.data, target);
}

30
src/camera_controller.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef CAMERA_CONTROLLER_H
#define CAMERA_CONTROLLER_H
#include "core/transformable.h"
#include "core/scene_node_entity.h"
#include "utils/mirror.h"
#include "utils/drop.h"
typedef struct CameraController {
SceneNode *node;
Transformable transform;
Transform global;
float rotation_speed;
float max_speed_time;
Transformable target;
float time_disjointed;
} CameraController;
extern SceneNode *CreateCameraController(Transformable target);
extern void DestroyCameraController(CameraController *self);
extern void CameraControllerEnterTree(CameraController *self);
extern void CameraControllerExitTree(CameraController *self);
extern void CameraControllerTick(CameraController *self, double delta);
DECL_REFLECT(CameraController);
decl_typeclass_impl(SceneNodeEntity, CameraController);
decl_typeclass_impl(Drop, CameraController);
#endif // !CAMERA_CONTROLLER_H

View file

@ -51,7 +51,7 @@ Camera3D CameraNodeGetCamera(CameraNode *self) {
Vector3 forward = MATRIX_FORWARD(mat); Vector3 forward = MATRIX_FORWARD(mat);
// construct a new camera at the global transform location and facing the forward vector // construct a new camera at the global transform location and facing the forward vector
return (Camera3D){ return (Camera3D){
.fovy = 70, .fovy = 90,
.position = global_transform.translation, .position = global_transform.translation,
.projection = CAMERA_PERSPECTIVE, .projection = CAMERA_PERSPECTIVE,
.target = Vector3Add(global_transform.translation, forward), .target = Vector3Add(global_transform.translation, forward),

View file

@ -15,9 +15,10 @@ void InitializeRaylibContext() {
InitWindow(1280, 800, "FOGD Engine"); InitWindow(1280, 800, "FOGD Engine");
if(!IsWindowFullscreen()) if(!IsWindowFullscreen())
ToggleFullscreen(); ToggleFullscreen();
SetTargetFPS(120); SetTargetFPS(60);
} }
void RunGame(Scene *initial_scene) { void RunGame(Scene *initial_scene) {
// set the main scene // set the main scene
SwitchScene(initial_scene); SwitchScene(initial_scene);

View file

@ -5,18 +5,6 @@
#include "utils/drop.h" #include "utils/drop.h"
#include "utils/mirror.h" #include "utils/mirror.h"
//! Tick a node and all children recursively
static
void Internal_SceneNodeTick(SceneNode *self, double delta) {
// tick self first
self->entity.tc->tick(self->entity.data, delta);
// fix the number of elements to update now,
// so that if/when new children are added, they don't get ticked until next frame
size_t const len = self->children.len;
for(size_t i = 0; i < len; ++i)
Internal_SceneNodeTick(*list_at_as(SceneNode*, &self->children, i), delta);
}
//! Update global transform of a node //! Update global transform of a node
static static
void Internal_SceneNodeUpdateTransform(SceneNode *self) { void Internal_SceneNodeUpdateTransform(SceneNode *self) {
@ -45,6 +33,19 @@ void Internal_SceneNodeUpdateTransformRecursive(SceneNode *self, bool is_dirty)
Internal_SceneNodeUpdateTransformRecursive(*child, is_dirty); Internal_SceneNodeUpdateTransformRecursive(*child, is_dirty);
} }
//! Tick a node and all children recursively
static
void Internal_SceneNodeTick(SceneNode *self, double delta) {
// tick self first
self->entity.tc->tick(self->entity.data, delta);
// fix the number of elements to update now,
// so that if/when new children are added, they don't get ticked until next frame
size_t const len = self->children.len;
for(size_t i = 0; i < len; ++i) {
Internal_SceneNodeTick(*list_at_as(SceneNode*, &self->children, i), delta);
}
}
//! Remove a single node (does NOT recurse to children children) from this scene. //! Remove a single node (does NOT recurse to children children) from this scene.
static static
void Internal_SceneRemoveNode(Scene *self, SceneNode *node) { void Internal_SceneRemoveNode(Scene *self, SceneNode *node) {
@ -181,6 +182,6 @@ 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_SceneNodeUpdateTransformRecursive(self->root, false); Internal_SceneNodeUpdateTransformRecursive(self->root, false);
Internal_SceneNodeTick(self->root, delta_time);
} }

View file

@ -21,7 +21,9 @@ Vector3 TransformPosition(Transform self, Vector3 local_pos) {
Vector3 TransformDirection(Transform self, Vector3 local_direction) { Vector3 TransformDirection(Transform self, Vector3 local_direction) {
Matrix const m = TransformGetMatrix(self); 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)); 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 self, Vector3 local_scale) { Vector3 TransformScale(Transform self, Vector3 local_scale) {
@ -41,15 +43,15 @@ Transform TransformTransform(Transform self, Transform other) {
} }
Vector3 InverseTransformPosition(Transform self, Vector3 global_pos) { Vector3 InverseTransformPosition(Transform self, Vector3 global_pos) {
return Vector3Subtract(InverseTransformDirection(self, global_pos), self.translation); return InverseTransformDirection(self, Vector3Subtract(global_pos, self.translation));
} }
Vector3 InverseTransformDirection(Transform self, Vector3 global_direction) { Vector3 InverseTransformDirection(Transform self, Vector3 global_direction) {
Matrix const mat = TransformGetMatrix(self); Matrix const mat = TransformGetMatrix(self);
return (Vector3){ return (Vector3){
Vector3DotProduct(MATRIX_RIGHT(mat), Vector3Scale(VECTOR3_RIGHT, global_direction.x)), Vector3DotProduct((Vector3){global_direction.x, 0.f, 0.f}, MATRIX_RIGHT(mat)),
Vector3DotProduct(MATRIX_UP(mat), Vector3Scale(VECTOR3_UP, global_direction.y)), Vector3DotProduct((Vector3){0.f, global_direction.y, 0.f}, MATRIX_UP(mat)),
Vector3DotProduct(MATRIX_FORWARD(mat), Vector3Scale(VECTOR3_FORWARD, global_direction.z)) Vector3DotProduct((Vector3){0.f, 0.f, global_direction.z}, MATRIX_FORWARD(mat))
}; };
} }
@ -68,3 +70,9 @@ Transform InverseTransformTransform(Transform self, Transform other) {
.rotation = InverseTransformRotation(self, other.rotation) .rotation = InverseTransformRotation(self, other.rotation)
}; };
} }
float QuaternionAngleDifference(Quaternion a, Quaternion b) {
Quaternion c = QuaternionMultiply(a, QuaternionInvert(b));
return 2.f*asinf(Vector3Length((Vector3){c.x, c.y, c.z}));
}

View file

@ -43,6 +43,8 @@ extern Vector3 InverseTransformScale(Transform self, Vector3 global_scale);
extern Quaternion InverseTransformRotation(Transform self, Quaternion quat); extern Quaternion InverseTransformRotation(Transform self, Quaternion quat);
extern Transform InverseTransformTransform(Transform self, Transform other); extern Transform InverseTransformTransform(Transform self, Transform other);
extern float QuaternionAngleDifference(Quaternion a, Quaternion b);
#define MATRIX_RIGHT(self_) ((Vector3){self_.m0, self_.m1, self_.m2}) #define MATRIX_RIGHT(self_) ((Vector3){self_.m0, self_.m1, self_.m2})
#define MATRIX_UP(self_) ((Vector3){self_.m4, self_.m5, self_.m6}) #define MATRIX_UP(self_) ((Vector3){self_.m4, self_.m5, self_.m6})
#define MATRIX_FORWARD(self_) ((Vector3){self_.m8, self_.m9, self_.m10}) #define MATRIX_FORWARD(self_) ((Vector3){self_.m8, self_.m9, self_.m10})
@ -60,7 +62,13 @@ extern Transform InverseTransformTransform(Transform self, Transform other);
#define impl_Transformable_for(T)\ #define impl_Transformable_for(T)\
static Transform T##_get_transform_GEN_(T* self) { return self->transform; }\ 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 = 1; } /*!< sets dirty bit */\ static void T##_set_transform_GEN_(T* self, Transform value) { self->transform = value; self->dirty_bit = 1; } /*!< sets dirty bit */\
static Transform T##_get_global_transform_GEN_(T* self) { return self->global_transform; }\ static void T##_force_update_GEN_(T* self);\
static Transform T##_get_global_transform_GEN_(T* self) {\
if(self->dirty_bit) {\
T##_force_update_GEN_(self);\
}\
return self->global_transform;\
}\
static void T##_set_global_transform_GEN_(T* self, Transform value) {\ static void T##_set_global_transform_GEN_(T* self, Transform value) {\
if(tc_is_null(self->parent_transformable)) {\ if(tc_is_null(self->parent_transformable)) {\
self->transform = value;\ self->transform = value;\

View file

@ -1,5 +1,6 @@
#include "raylib.h" #include "raylib.h"
#include "test_object.h" #include "test_object.h"
#include "camera_controller.h"
#include "core/input.h" #include "core/input.h"
#include "core/camera_node.h" #include "core/camera_node.h"
#include "core/engine_global.h" #include "core/engine_global.h"
@ -8,15 +9,18 @@
#include "core/transform_node.h" #include "core/transform_node.h"
#include "core/mesh_render_entity.h" #include "core/mesh_render_entity.h"
#include "utils/debug.h" #include "utils/debug.h"
#include "utils/mirror.h"
//! Create camera node with a transformable parent. //! Create camera node with a transformable parent.
SceneNode *CreateCameraScene() { SceneNode *CreateCameraScene() {
SceneNode *camera_parent = CreateTransformNode(); SceneNode *camera_parent = CreateTransformNode();
SceneNodeAddChild(camera_parent, CreateCameraNode()); SceneNode *camera_transform = CreateTransformNode();
SceneNodeAddChild(camera_parent, camera_transform);
SceneNodeAddChild(camera_transform, CreateCameraNode());
// set camera parent offset // set camera parent offset
Transformable transformable = TC_CAST(camera_parent->entity, Transformable); Transformable transformable = TC_CAST(camera_transform->entity, Transformable);
Transform transform = transformable.tc->get_transform(transformable.data); Transform transform = transformable.tc->get_transform(transformable.data);
transform.translation = (Vector3){0.f, 4.f, -10.f}; transform.translation = (Vector3){-0.2f, 0.5f, -1.5f};
transform.rotation = QuaternionFromEuler(7.5f * DEG2RAD, 0.f, 0.f); transform.rotation = QuaternionFromEuler(7.5f * DEG2RAD, 0.f, 0.f);
transformable.tc->set_transform(transformable.data, transform); transformable.tc->set_transform(transformable.data, transform);
return camera_parent; return camera_parent;
@ -26,11 +30,13 @@ SceneNode *CreateCameraScene() {
SceneNode *CreateModelScene() { SceneNode *CreateModelScene() {
SceneNode *model_parent = CreateTransformNode(); SceneNode *model_parent = CreateTransformNode();
// create the rest of the scene with a mesh and a functionality node // create the rest of the scene with a mesh and a functionality node
SceneNodeAddChild(model_parent, CreateMeshRenderEntity("spacefighter")); SceneNodeAddChild(model_parent, CreateMeshRenderEntity("rider"));
SceneNodeAddChild(model_parent, CreateTestObject()); SceneNodeAddChild(model_parent, CreateTestObject());
// move the renderer's parent transform // move the renderer's parent transform
Transformable transformable = TC_CAST(model_parent->entity, Transformable); Transformable transformable = TC_CAST(model_parent->entity, Transformable);
Transform transform = transformable.tc->get_global_transform(transformable.data); Transform transform = transformable.tc->get_global_transform(transformable.data);
transform.translation.x = 10.f;
transform.translation.y = 10.f;
transformable.tc->set_global_transform(transformable.data, transform); transformable.tc->set_global_transform(transformable.data, transform);
return model_parent; return model_parent;
} }
@ -38,8 +44,10 @@ SceneNode *CreateModelScene() {
Scene *CreateInitialScene() { Scene *CreateInitialScene() {
SceneNode *root = CreateTransformNode(); SceneNode *root = CreateTransformNode();
SceneNode *model = CreateModelScene(); SceneNode *model = CreateModelScene();
SceneNode *camera_scene = CreateCameraScene();
SceneNodeAddChild(root, model); SceneNodeAddChild(root, model);
SceneNodeAddChild(model, CreateCameraScene()); SceneNodeAddChild(root, camera_scene);
SceneNodeAddChild(camera_scene, CreateCameraController(TC_CAST(model->entity, Transformable)));
return CreateScene(root); return CreateScene(root);
} }
@ -48,6 +56,7 @@ void ConfigureInput() {
AddAction("pitch_down", INPUT_LISTENER_KEY, 0, KEY_W); AddAction("pitch_down", INPUT_LISTENER_KEY, 0, KEY_W);
AddAction("roll_left", INPUT_LISTENER_KEY, 0, KEY_A); AddAction("roll_left", INPUT_LISTENER_KEY, 0, KEY_A);
AddAction("roll_right", INPUT_LISTENER_KEY, 0, KEY_D); AddAction("roll_right", INPUT_LISTENER_KEY, 0, KEY_D);
AddAction("stop", INPUT_LISTENER_KEY, 0, KEY_SPACE);
} }
int main() { int main() {

View file

@ -26,6 +26,8 @@ impl_SceneNodeEntity_for(TestObject,
SceneNode *CreateTestObject() { SceneNode *CreateTestObject() {
TestObject *self = new(TestObject); TestObject *self = new(TestObject);
self->rotation = self->fly_input = Vector2Zero();
self->stopped = false;
return CreateSceneNode(TestObject_as_SceneNodeEntity(self)); return CreateSceneNode(TestObject_as_SceneNodeEntity(self));
} }
@ -40,6 +42,7 @@ void TestObjectEnterTree(TestObject *self) {
AddListener("pitch_down", ButtonInputListener(self, TestObjectDownInput)); AddListener("pitch_down", ButtonInputListener(self, TestObjectDownInput));
AddListener("roll_left", ButtonInputListener(self, TestObjectLeftInput)); AddListener("roll_left", ButtonInputListener(self, TestObjectLeftInput));
AddListener("roll_right", ButtonInputListener(self, TestObjectRightInput)); AddListener("roll_right", ButtonInputListener(self, TestObjectRightInput));
AddListener("stop", ButtonInputListener(self, TestObjectStopInput));
} }
void TestObjectExitTree(TestObject *self) { void TestObjectExitTree(TestObject *self) {
@ -50,11 +53,16 @@ void TestObjectDraw(TestObject *self) {
} }
void TestObjectTick(TestObject *self, double delta) { void TestObjectTick(TestObject *self, double delta) {
if(self->stopped) return;
Vector2 diff = Vector2Subtract(self->fly_input, self->rotation);
float const length = Vector2Length(diff);
if(length != 0.f)
self->rotation = Vector2Add(self->rotation, Vector2Scale(diff, 1.0f/length * fminf(4.f * delta, length)));
Transform global_transform = self->transform.tc->get_global_transform(self->transform.data); Transform global_transform = self->transform.tc->get_global_transform(self->transform.data);
Matrix global_matrix = TransformGetMatrix(global_transform); Matrix global_matrix = TransformGetMatrix(global_transform);
global_transform.translation = Vector3Add(global_transform.translation, Vector3Scale(MATRIX_FORWARD(global_matrix), 20.f * delta)); global_transform.translation = Vector3Add(global_transform.translation, Vector3Scale(MATRIX_FORWARD(global_matrix), 20.f * delta));
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->fly_input.x * 1.f * delta), global_transform.rotation); global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->rotation.x * 4.f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->fly_input.y * 1.f * delta), global_transform.rotation); global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->rotation.y * 4.f * delta), global_transform.rotation);
self->transform.tc->set_global_transform(self->transform.data, global_transform); self->transform.tc->set_global_transform(self->transform.data, global_transform);
} }
@ -65,14 +73,16 @@ void TestObjectLeftInput(TestObject *self, bool value) {
void TestObjectRightInput(TestObject *self, bool value) { void TestObjectRightInput(TestObject *self, bool value) {
self->fly_input.x += value ? +1 : -1; self->fly_input.x += value ? +1 : -1;
LOG_INFO("Input Right");
} }
void TestObjectUpInput(TestObject *self, bool value) { void TestObjectUpInput(TestObject *self, bool value) {
self->fly_input.y += value ? -1 : +1; self->fly_input.y += value ? -1 : +1;
LOG_INFO("Input Up");
} }
void TestObjectDownInput(TestObject *self, bool value) { void TestObjectDownInput(TestObject *self, bool value) {
self->fly_input.y += value ? +1 : -1; self->fly_input.y += value ? +1 : -1;
} }
void TestObjectStopInput(TestObject *self, bool value) {
self->stopped = value;
}

View file

@ -13,6 +13,8 @@ typedef struct TestObject {
SceneNode *node; SceneNode *node;
Transformable transform; Transformable transform;
Vector2 fly_input; Vector2 fly_input;
Vector2 rotation;
bool stopped;
} TestObject; } TestObject;
SceneNode *CreateTestObject(); SceneNode *CreateTestObject();
@ -27,6 +29,7 @@ extern void TestObjectLeftInput(TestObject *self, bool value);
extern void TestObjectRightInput(TestObject *self, bool value); extern void TestObjectRightInput(TestObject *self, bool value);
extern void TestObjectUpInput(TestObject *self, bool value); extern void TestObjectUpInput(TestObject *self, bool value);
extern void TestObjectDownInput(TestObject *self, bool value); extern void TestObjectDownInput(TestObject *self, bool value);
extern void TestObjectStopInput(TestObject *self, bool value);
DECL_REFLECT(TestObject); DECL_REFLECT(TestObject);
decl_typeclass_impl(SceneNodeEntity, TestObject); decl_typeclass_impl(SceneNodeEntity, TestObject);