Compare commits

...

10 commits

Author SHA1 Message Date
Sara 1b7b915548 feat: removed grid 2024-10-04 11:13:53 +02:00
Sara 315ae2d3b4 feat: implemented city generator 2024-10-04 11:12:13 +02:00
Sara 0ca2972218 feat: started work on city generation 2024-10-01 10:25:43 +02:00
Sara b60cf6cb6c feat: added boost 2024-10-01 10:25:35 +02:00
Sara 4d28ff8829 feat: lowered yaw speed 2024-09-27 16:12:27 +02:00
Sara f79fda16e9 feat: added yaw keys 2024-09-27 16:08:21 +02:00
Sara a9bc57a175 feat: improved slerp amount argument for camera controller 2024-09-27 16:07:58 +02:00
Sara 30d8182f4d feat: added get child by typeid function with option to recurse 2024-09-27 10:34:43 +02:00
Sara b3f9d7222e chore: updated utils 2024-09-27 10:34:43 +02:00
Sara 2fbc198d7d feat: CameraNode fov is now configurable 2024-09-27 10:34:43 +02:00
21 changed files with 186 additions and 53 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
resources/building_a.glb Normal file

Binary file not shown.

BIN
resources/building_b.glb Normal file

Binary file not shown.

BIN
resources/building_c.glb Normal file

Binary file not shown.

BIN
resources/building_d.glb Normal file

Binary file not shown.

View file

@ -1,4 +1,5 @@
#include "camera_controller.h"
#include "core/camera_node.h"
#include "utils/mirror.h"
START_REFLECT(CameraController);
@ -18,10 +19,11 @@ SceneNode *CreateCameraController(Transformable target) {
CameraController *self = new(CameraController);
*self = (CameraController){
.transform = tc_null(Transformable),
.rotation_speed = 5.f,
.rotation_speed = 2.f,
.max_speed_time = 4.f,
.target = target,
.time_rotated = 0.f
.time_rotated = 0.f,
.camera = NULL,
};
SceneNode *node = CreateSceneNode(CameraController_as_SceneNodeEntity(self));
return node;
@ -34,6 +36,9 @@ void DestroyCameraController(CameraController *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);
SceneNode *camera_node = SceneNodeGetChildByTypeid(self->node->parent, GET_TYPEID(CameraNode), true);
self->camera = camera_node->entity.data;
self->camera->fov = 100;
}
void CameraControllerExitTree(CameraController *self) {}
@ -49,11 +54,10 @@ void CameraControllerTick(CameraController *self, double delta) {
// ... and maximum frame step size
float const step = self->rotation_speed * time_mul * delta;
if(angle > 0.f)
target.rotation = QuaternionSlerp(current.rotation, target.rotation, fminf(fminf(step, angle) / angle, 0.99f));
target.rotation = QuaternionSlerp(current.rotation, target.rotation, step);
if(step > angle && self->time_rotated > delta)
self->time_rotated -= 3.f * delta;
else if(step > angle)
self->time_rotated = 0.f;
self->time_rotated += delta;
self->time_rotated -= delta;
else
self->time_rotated += delta;
self->transform.tc->set_global_transform(self->transform.data, target);
}

View file

@ -14,6 +14,7 @@ typedef struct CameraController {
float max_speed_time;
Transformable target;
float time_rotated;
CameraNode *camera;
} CameraController;
extern SceneNode *CreateCameraController(Transformable target);

45
src/city_generator.c Normal file
View file

@ -0,0 +1,45 @@
#include "city_generator.h"
#include "core/mesh_render_entity.h"
#include "core/transform_node.h"
static struct {
size_t x, y;
} const CITY_SIZE = {20, 20};
#define MODELS_LENGTH 4
static ModelResource models[MODELS_LENGTH] = {
ResourceEmpty(Model),
ResourceEmpty(Model),
ResourceEmpty(Model),
ResourceEmpty(Model)
};
static void Internal_LoadModelsIfRequired() {
for(size_t i = 0; i < MODELS_LENGTH; ++i)
if(models[i].handle == NULL && GetModelResource(TextFormat("building_%c", 'a' + i), &models[i]))
LoadResource(models[i].handle);
}
static SceneNode *Internal_CreateBuilding(size_t x, size_t y) {
size_t building_index = (3 * (x + y + GetRandomValue(0, 50))) % MODELS_LENGTH; // semi-random number based on the x,y coordinate
SceneNode *mesh_render_node = CreateMeshRenderEntity(TextFormat("building_%c", 'a' + building_index));
SceneNode *transform_node = CreateTransformNode();
SceneNodeAddChild(transform_node, mesh_render_node);
Transformable transform = TC_CAST(transform_node->entity, Transformable);
Transform local = transform.tc->get_transform(transform.data);
local.translation.x = (float)(x * 40);
local.translation.z = (float)(y * 40);
transform.tc->set_transform(transform.data, local);
return transform_node;
}
SceneNode *GenerateCity() {
Internal_LoadModelsIfRequired();
SceneNode *root = CreateTransformNode();
for(size_t x = 0; x < CITY_SIZE.x; ++x) {
for(size_t y = 0; y < CITY_SIZE.y; ++y) {
SceneNodeAddChild(root, Internal_CreateBuilding(x, y));
}
}
return root;
}

8
src/city_generator.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef CITY_GENERATOR_H
#define CITY_GENERATOR_H
#include "core/scene.h"
extern SceneNode *GenerateCity();
#endif // !CITY_GENERATOR_H

View file

@ -20,6 +20,11 @@ impl_SceneNodeEntity_for(CameraNode,
SceneNode *CreateCameraNode() {
CameraNode *self = new(CameraNode);
*self = (CameraNode){
.fov = 60,
.node = NULL,
.transform = tc_null(Transformable)
};
return CreateSceneNode(CameraNode_as_SceneNodeEntity(self));
}
@ -51,7 +56,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 = 90,
.fovy = self->fov,
.position = global_transform.translation,
.projection = CAMERA_PERSPECTIVE,
.target = Vector3Add(global_transform.translation, forward),

View file

@ -10,6 +10,7 @@
typedef struct CameraNode {
SceneNode *node;
Transformable transform;
float fov;
} CameraNode;
//! Instantiate new camera node

View file

@ -54,7 +54,6 @@ void RenderNextFrame() {
BeginMode3D(CameraNodeGetCamera(camera));
list_foreach(Renderable *,object, &g_render_objects)
object->tc->draw(object->data);
DrawGrid(500, 1.f); // TODO Remove this (or make it a scene node entity)
EndMode3D();
}
DrawFPS(20, 20);

View file

@ -132,9 +132,9 @@ bool GetModelResource(char const *path, ModelResource *out) {
ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path);
*out = ResourceEmpty(Model);
// assert some assumptions about the found resource
ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path);
ASSERT_RETURN(container->type == RESOURCE_MODEL, false, "GetTextureResource: Resource %s is not a Texture.", path);
++container->use_counter;
ASSERT_RETURN(container != NULL, false, "GetModelResource: Resource %s not in index.", path);
ASSERT_RETURN(container->type == RESOURCE_MODEL, false, "GetModelResource: Resource %s is not a Texture.", path);
ASSERT_RETURN(strcmp(container->name, path) == 0, false, "GetModelResource: Resource %s was loaded for path %s", container->name, path);
*out = (ModelResource) {
.handle = container,
.resource = &container->model
@ -174,16 +174,20 @@ bool IsResourceLoaded(ResourceHandle handle) {
void LoadResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "LoadResource: Resource handle invalid");
g_load_functions[handle->type](handle);
handle->is_loaded = true;
if(!handle->is_loaded) {
g_load_functions[handle->type](handle);
handle->is_loaded = true;
}
++handle->use_counter;
}
void ReleaseResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "ReleaseResource: Resource handle invalid");
ASSERT_RETURN_WARN(handle->is_loaded,, "ReleaseResource: Resource %s is not loaded.", handle->path);
g_unload_functions[handle->type](handle);
handle->is_loaded = false;
if(handle->use_counter == 1) {
g_unload_functions[handle->type](handle);
handle->is_loaded = false;
}
--handle->use_counter;
}

View file

@ -170,8 +170,23 @@ SceneNode *SceneNodeGetChildByTypeclass(SceneNode *self, char const *typeclass,
SceneNodeEntity entity = (*child)->entity;
if(mirror_get_function(entity.data, entity.mirror, typeclass))
return *child;
if(recurse)
SceneNodeGetChildByTypeclass(*child, typeclass, recurse);
if(recurse) {
SceneNode *recursed = SceneNodeGetChildByTypeclass(*child, typeclass, recurse);
if(recursed != NULL) return recursed;
}
}
return NULL;
}
SceneNode *SceneNodeGetChildByTypeid(SceneNode *self, typeid id, bool recurse) {
list_foreach(SceneNode **,child, &self->children) {
SceneNodeEntity entity = (*child)->entity;
if(entity.mirror->get_typeid() == id)
return *child;
if(recurse) {
SceneNode *recursed = SceneNodeGetChildByTypeid(*child, id, recurse);
if(recursed != NULL) return recursed;
}
}
return NULL;
}

View file

@ -41,10 +41,12 @@ extern void SceneNodeRemoveChild(SceneNode *self, SceneNode *child);
extern void SceneNodeAttachEntity(SceneNode *self, SceneNodeEntity entity);
//! Detach an entity from a scene node
extern SceneNodeEntity SceneNodeDetachEntity(SceneNode *self);
//! Returns the first child node that implements a specific typeclass.
//! Returns the first child node with an entity that implements a specific typeclass.
//! Optionally recurses through the entire branch
extern SceneNode *SceneNodeGetChildByTypeclass(SceneNode *self, char const *typeclass, bool recurse);
//! Returns the first child node with an entity that matches the given typeid.
//! Optionally recurses through the entire branch.
extern SceneNode *SceneNodeGetChildByTypeid(SceneNode *self, typeid id, bool recurse);
//! Instantiate a new scene with a root node.
extern Scene *CreateScene(SceneNode *root);
//! Destroy a node and it's scene tree.

View file

@ -1,6 +1,7 @@
#include "raylib.h"
#include "player_controller.h"
#include "camera_controller.h"
#include "city_generator.h"
#include "core/input.h"
#include "core/camera_node.h"
#include "core/engine_global.h"
@ -48,6 +49,7 @@ Scene *CreateInitialScene() {
SceneNodeAddChild(root, model);
SceneNodeAddChild(root, camera_scene);
SceneNodeAddChild(camera_scene, CreateCameraController(TC_CAST(model->entity, Transformable)));
SceneNodeAddChild(root, GenerateCity());
return CreateScene(root);
}
@ -56,7 +58,10 @@ 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);
AddAction("yaw_right", INPUT_LISTENER_KEY, 0, KEY_E);
AddAction("yaw_left", INPUT_LISTENER_KEY, 0, KEY_Q);
AddAction("brake", INPUT_LISTENER_KEY, 0, KEY_SPACE);
AddAction("boost", INPUT_LISTENER_KEY, 0, KEY_LEFT_SHIFT);
}
int main() {

View file

@ -1,7 +1,8 @@
#include "player_controller.h"
#include "core/render.h"
#include "utils/debug.h"
#include "core/input.h"
#include "utils/typeclass_helpers.h"
#include "utils/debug.h"
#include "raylib.h"
START_REFLECT(PlayerController);
@ -19,10 +20,25 @@ impl_SceneNodeEntity_for(PlayerController,
PlayerControllerTick
)
static float const PLAYER_BASE_SPEED = 10.f;
static float const PLAYER_BASE_ACCELERATION = 10.f;
static float const PLAYER_BOOST_SPEED = 30.f;
static float const PLAYER_BOOST_ACCELERATION = 60.f;
static float const PLAYER_BRAKE_SPEED = 0.5f;
static float const PLAYER_BRAKE_DECELERATION = 10.f;
static float const PLAYER_FALL_SPEED = 10.f;
static float const PLAYER_FALL_ACCELERATION = 10.f;
SceneNode *CreatePlayerController() {
PlayerController *self = new(PlayerController);
self->rotation = self->fly_input = Vector2Zero();
self->stopped = false;
*self = (PlayerController) {
.node = NULL,
.transform = tc_null(Transformable),
.rotation = Vector3Zero(),
.fly_input = Vector3Zero(),
.speed = 10.f,
.brake = false
};
return CreateSceneNode(PlayerController_as_SceneNodeEntity(self));
}
@ -33,32 +49,42 @@ void DestroyPlayerController(PlayerController *self) {
void PlayerControllerEnterTree(PlayerController *self) {
self->transform = TC_CAST(self->node->parent->entity, Transformable);
DisableCursor();
AddListener("pitch_up", ButtonInputListener(self, PlayerControllerUpInput));
AddListener("pitch_down", ButtonInputListener(self, PlayerControllerDownInput));
AddListener("roll_left", ButtonInputListener(self, PlayerControllerLeftInput));
AddListener("roll_right", ButtonInputListener(self, PlayerControllerRightInput));
AddListener("stop", ButtonInputListener(self, PlayerControllerStopInput));
AddListener("pitch_up", ButtonInputListener(self, PlayerControllerInputPitchUp));
AddListener("pitch_down", ButtonInputListener(self, PlayerControllerInputPitchDown));
AddListener("roll_left", ButtonInputListener(self, PlayerControllerInputRollLeft));
AddListener("roll_right", ButtonInputListener(self, PlayerControllerInputRollRight));
AddListener("yaw_left", ButtonInputListener(self, PlayerControllerInputYawLeft));
AddListener("yaw_right", ButtonInputListener(self, PlayerControllerInputYawRight));
AddListener("brake", ButtonInputListener(self, PlayerControllerInputBrake));
AddListener("boost", ButtonInputListener(self, PlayerControllerInputBoost));
}
void PlayerControllerExitTree(PlayerController *self) {
RemoveAllListeners(self);
}
static
float MoveTowards(float from, float to, float delta) {
if(from == to) return to;
float const diff = to - from;
float const sign = signbit(diff) ? -1.f : 1.f;
return from + sign * fminf(delta, fabsf(diff));
}
//! angular acceleration limited to local X and Z axes
static
void PlayerControllerTickAngularAcceleration(PlayerController *self, double delta) {
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)));
self->rotation.x = MoveTowards(self->rotation.x, self->fly_input.x, delta * 4.f);
self->rotation.y = MoveTowards(self->rotation.y, self->fly_input.y, delta * 4.f);
self->rotation.z = MoveTowards(self->rotation.z, self->fly_input.z, delta * 4.f);
}
//! linear acceleration limited to the local Z axis
static
void PlayerControllerTickLinearAcceleration(PlayerController *self, double delta) {
float const target = self->stopped ? 10.f : (self->rotation.y == 0.f ? 30.f : 20.f);
float const speed_diff = target - self->speed;
self->speed = self->speed + copysignf(fminf(fabsf(speed_diff), 10.f * delta), speed_diff);
float const target = self->brake ? PLAYER_BRAKE_SPEED : (self->boost ? PLAYER_BOOST_SPEED : PLAYER_BASE_SPEED);
float const acceleration = target < self->speed ? PLAYER_BRAKE_DECELERATION : (self->boost ? PLAYER_BOOST_ACCELERATION : PLAYER_BASE_ACCELERATION);
self->speed = MoveTowards(self->speed, target, delta * acceleration);
}
//! Update linear transform based on velocities
@ -66,9 +92,11 @@ static
void PlayerControllerTickTransform(PlayerController *self, double delta) {
Transform global_transform = self->transform.tc->get_global_transform(self->transform.data);
Matrix global_matrix = TransformGetMatrix(global_transform);
float const rotate_speed = 1.f - 0.9f * self->speed / PLAYER_BOOST_SPEED;
global_transform.translation = Vector3Add(global_transform.translation, Vector3Scale(MATRIX_FORWARD(global_matrix), self->speed * delta));
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->rotation.x * 2.f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->rotation.y * 2.5f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->rotation.x * 1.25f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->rotation.y * rotate_speed * 3.75f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_UP(global_matrix), self->rotation.z * rotate_speed * 1.f * delta), global_transform.rotation);
self->transform.tc->set_global_transform(self->transform.data, global_transform);
}
@ -78,22 +106,34 @@ void PlayerControllerTick(PlayerController *self, double delta) {
PlayerControllerTickTransform(self, delta);
}
void PlayerControllerLeftInput(PlayerController *self, bool value) {
void PlayerControllerInputRollLeft(PlayerController *self, bool value) {
self->fly_input.x += value ? -1 : +1;
}
void PlayerControllerRightInput(PlayerController *self, bool value) {
void PlayerControllerInputRollRight(PlayerController *self, bool value) {
self->fly_input.x += value ? +1 : -1;
}
void PlayerControllerUpInput(PlayerController *self, bool value) {
void PlayerControllerInputPitchUp(PlayerController *self, bool value) {
self->fly_input.y += value ? -1 : +1;
}
void PlayerControllerDownInput(PlayerController *self, bool value) {
void PlayerControllerInputPitchDown(PlayerController *self, bool value) {
self->fly_input.y += value ? +1 : -1;
}
void PlayerControllerStopInput(PlayerController *self, bool value) {
self->stopped = value;
void PlayerControllerInputYawRight(PlayerController *self, bool value) {
self->fly_input.z += value ? -1 : +1;
}
void PlayerControllerInputYawLeft(PlayerController *self, bool value) {
self->fly_input.z += value ? +1 : -1;
}
void PlayerControllerInputBrake(PlayerController *self, bool value) {
self->brake = value;
}
void PlayerControllerInputBoost(PlayerController *self, bool value) {
self->boost = value;
}

View file

@ -12,10 +12,11 @@
typedef struct PlayerController {
SceneNode *node;
Transformable transform;
Vector2 fly_input;
Vector2 rotation;
Vector3 fly_input;
Vector3 rotation;
float speed;
bool stopped;
bool brake;
bool boost;
} PlayerController;
SceneNode *CreatePlayerController();
@ -25,11 +26,14 @@ extern void PlayerControllerEnterTree(PlayerController *self);
extern void PlayerControllerExitTree(PlayerController *self);
extern void PlayerControllerTick(PlayerController *self, double delta);
extern void PlayerControllerLeftInput(PlayerController *self, bool value);
extern void PlayerControllerRightInput(PlayerController *self, bool value);
extern void PlayerControllerUpInput(PlayerController *self, bool value);
extern void PlayerControllerDownInput(PlayerController *self, bool value);
extern void PlayerControllerStopInput(PlayerController *self, bool value);
extern void PlayerControllerInputRollLeft(PlayerController *self, bool value);
extern void PlayerControllerInputRollRight(PlayerController *self, bool value);
extern void PlayerControllerInputPitchUp(PlayerController *self, bool value);
extern void PlayerControllerInputPitchDown(PlayerController *self, bool value);
extern void PlayerControllerInputYawRight(PlayerController *self, bool value);
extern void PlayerControllerInputYawLeft(PlayerController *self, bool value);
extern void PlayerControllerInputBrake(PlayerController *self, bool value);
extern void PlayerControllerInputBoost(PlayerController *self, bool value);
DECL_REFLECT(PlayerController);
decl_typeclass_impl(SceneNodeEntity, PlayerController);

@ -1 +1 @@
Subproject commit faf0463e3791ee97dfc370282ecad4cd3b4ca475
Subproject commit bb201d5085cfb2e54ebad9a0bf22347a5251f7a2