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);
// construct a new camera at the global transform location and facing the forward vector
return (Camera3D){
.fovy = 70,
.fovy = 90,
.position = global_transform.translation,
.projection = CAMERA_PERSPECTIVE,
.target = Vector3Add(global_transform.translation, forward),

View file

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

View file

@ -5,18 +5,6 @@
#include "utils/drop.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
static
void Internal_SceneNodeUpdateTransform(SceneNode *self) {
@ -45,6 +33,19 @@ void Internal_SceneNodeUpdateTransformRecursive(SceneNode *self, bool 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.
static
void Internal_SceneRemoveNode(Scene *self, SceneNode *node) {
@ -181,6 +182,6 @@ void DestroyScene(Scene *self) {
}
void SceneTick(Scene* self, double delta_time) {
Internal_SceneNodeTick(self->root, delta_time);
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) {
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) {
@ -41,15 +43,15 @@ Transform TransformTransform(Transform self, Transform other) {
}
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) {
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))
Vector3DotProduct((Vector3){global_direction.x, 0.f, 0.f}, MATRIX_RIGHT(mat)),
Vector3DotProduct((Vector3){0.f, global_direction.y, 0.f}, MATRIX_UP(mat)),
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)
};
}
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 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_UP(self_) ((Vector3){self_.m4, self_.m5, self_.m6})
#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)\
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 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) {\
if(tc_is_null(self->parent_transformable)) {\
self->transform = value;\

View file

@ -1,5 +1,6 @@
#include "raylib.h"
#include "test_object.h"
#include "camera_controller.h"
#include "core/input.h"
#include "core/camera_node.h"
#include "core/engine_global.h"
@ -8,15 +9,18 @@
#include "core/transform_node.h"
#include "core/mesh_render_entity.h"
#include "utils/debug.h"
#include "utils/mirror.h"
//! Create camera node with a transformable parent.
SceneNode *CreateCameraScene() {
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
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.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);
transformable.tc->set_transform(transformable.data, transform);
return camera_parent;
@ -26,11 +30,13 @@ SceneNode *CreateCameraScene() {
SceneNode *CreateModelScene() {
SceneNode *model_parent = CreateTransformNode();
// 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());
// move the renderer's parent transform
Transformable transformable = TC_CAST(model_parent->entity, Transformable);
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);
return model_parent;
}
@ -38,8 +44,10 @@ SceneNode *CreateModelScene() {
Scene *CreateInitialScene() {
SceneNode *root = CreateTransformNode();
SceneNode *model = CreateModelScene();
SceneNode *camera_scene = CreateCameraScene();
SceneNodeAddChild(root, model);
SceneNodeAddChild(model, CreateCameraScene());
SceneNodeAddChild(root, camera_scene);
SceneNodeAddChild(camera_scene, CreateCameraController(TC_CAST(model->entity, Transformable)));
return CreateScene(root);
}
@ -48,6 +56,7 @@ void ConfigureInput() {
AddAction("pitch_down", INPUT_LISTENER_KEY, 0, KEY_W);
AddAction("roll_left", INPUT_LISTENER_KEY, 0, KEY_A);
AddAction("roll_right", INPUT_LISTENER_KEY, 0, KEY_D);
AddAction("stop", INPUT_LISTENER_KEY, 0, KEY_SPACE);
}
int main() {

View file

@ -26,6 +26,8 @@ impl_SceneNodeEntity_for(TestObject,
SceneNode *CreateTestObject() {
TestObject *self = new(TestObject);
self->rotation = self->fly_input = Vector2Zero();
self->stopped = false;
return CreateSceneNode(TestObject_as_SceneNodeEntity(self));
}
@ -40,6 +42,7 @@ void TestObjectEnterTree(TestObject *self) {
AddListener("pitch_down", ButtonInputListener(self, TestObjectDownInput));
AddListener("roll_left", ButtonInputListener(self, TestObjectLeftInput));
AddListener("roll_right", ButtonInputListener(self, TestObjectRightInput));
AddListener("stop", ButtonInputListener(self, TestObjectStopInput));
}
void TestObjectExitTree(TestObject *self) {
@ -50,11 +53,16 @@ void TestObjectDraw(TestObject *self) {
}
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);
Matrix global_matrix = TransformGetMatrix(global_transform);
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_RIGHT(global_matrix), self->fly_input.y * 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->rotation.y * 4.f * delta), global_transform.rotation);
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) {
self->fly_input.x += value ? +1 : -1;
LOG_INFO("Input Right");
}
void TestObjectUpInput(TestObject *self, bool value) {
self->fly_input.y += value ? -1 : +1;
LOG_INFO("Input Up");
}
void TestObjectDownInput(TestObject *self, bool value) {
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;
Transformable transform;
Vector2 fly_input;
Vector2 rotation;
bool stopped;
} TestObject;
SceneNode *CreateTestObject();
@ -27,6 +29,7 @@ extern void TestObjectLeftInput(TestObject *self, bool value);
extern void TestObjectRightInput(TestObject *self, bool value);
extern void TestObjectUpInput(TestObject *self, bool value);
extern void TestObjectDownInput(TestObject *self, bool value);
extern void TestObjectStopInput(TestObject *self, bool value);
DECL_REFLECT(TestObject);
decl_typeclass_impl(SceneNodeEntity, TestObject);