feat: implemented camera controller
This commit is contained in:
parent
0f612b0bd6
commit
4704f9ca7b
53
src/camera_controller.c
Normal file
53
src/camera_controller.c
Normal 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
30
src/camera_controller.h
Normal 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
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;\
|
||||
|
|
19
src/main.c
19
src/main.c
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue