diff --git a/models/building-1.blend b/models/building-1.blend index 86c0e55..88f86cc 100644 Binary files a/models/building-1.blend and b/models/building-1.blend differ diff --git a/models/building-1.blend1 b/models/building-1.blend1 index 4dc55fa..1952caf 100644 Binary files a/models/building-1.blend1 and b/models/building-1.blend1 differ diff --git a/resources/building-1.glb b/resources/building-1.glb index 1e16ff4..e38919e 100644 Binary files a/resources/building-1.glb and b/resources/building-1.glb differ diff --git a/resources/building_a.glb b/resources/building_a.glb new file mode 100644 index 0000000..c6b7373 Binary files /dev/null and b/resources/building_a.glb differ diff --git a/resources/building_b.glb b/resources/building_b.glb new file mode 100644 index 0000000..a149f69 Binary files /dev/null and b/resources/building_b.glb differ diff --git a/resources/building_c.glb b/resources/building_c.glb new file mode 100644 index 0000000..b7af293 Binary files /dev/null and b/resources/building_c.glb differ diff --git a/resources/building_d.glb b/resources/building_d.glb new file mode 100644 index 0000000..a850650 Binary files /dev/null and b/resources/building_d.glb differ diff --git a/src/camera_controller.c b/src/camera_controller.c index 9aa1733..23dac65 100644 --- a/src/camera_controller.c +++ b/src/camera_controller.c @@ -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); } diff --git a/src/camera_controller.h b/src/camera_controller.h index 29b2279..ca0d689 100644 --- a/src/camera_controller.h +++ b/src/camera_controller.h @@ -14,6 +14,7 @@ typedef struct CameraController { float max_speed_time; Transformable target; float time_rotated; + CameraNode *camera; } CameraController; extern SceneNode *CreateCameraController(Transformable target); diff --git a/src/city_generator.c b/src/city_generator.c new file mode 100644 index 0000000..e82cb69 --- /dev/null +++ b/src/city_generator.c @@ -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; +} diff --git a/src/city_generator.h b/src/city_generator.h new file mode 100644 index 0000000..dffbfaa --- /dev/null +++ b/src/city_generator.h @@ -0,0 +1,8 @@ +#ifndef CITY_GENERATOR_H +#define CITY_GENERATOR_H + +#include "core/scene.h" + +extern SceneNode *GenerateCity(); + +#endif // !CITY_GENERATOR_H diff --git a/src/core/camera_node.c b/src/core/camera_node.c index 6d21434..cef88ec 100644 --- a/src/core/camera_node.c +++ b/src/core/camera_node.c @@ -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), diff --git a/src/core/camera_node.h b/src/core/camera_node.h index 8837bfd..79fd2b0 100644 --- a/src/core/camera_node.h +++ b/src/core/camera_node.h @@ -10,6 +10,7 @@ typedef struct CameraNode { SceneNode *node; Transformable transform; + float fov; } CameraNode; //! Instantiate new camera node diff --git a/src/core/render.c b/src/core/render.c index 6929335..1b6bd3b 100644 --- a/src/core/render.c +++ b/src/core/render.c @@ -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); diff --git a/src/core/resources.c b/src/core/resources.c index 57831f6..7ed929e 100644 --- a/src/core/resources.c +++ b/src/core/resources.c @@ -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; } diff --git a/src/core/scene.c b/src/core/scene.c index f017c22..f252d14 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -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; } diff --git a/src/core/scene.h b/src/core/scene.h index 5ce6340..4e4826e 100644 --- a/src/core/scene.h +++ b/src/core/scene.h @@ -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. diff --git a/src/main.c b/src/main.c index dc03fe6..23c4de1 100644 --- a/src/main.c +++ b/src/main.c @@ -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() { diff --git a/src/player_controller.c b/src/player_controller.c index 4910ba6..918f62d 100644 --- a/src/player_controller.c +++ b/src/player_controller.c @@ -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; } diff --git a/src/player_controller.h b/src/player_controller.h index e20a724..c6fc5ce 100644 --- a/src/player_controller.h +++ b/src/player_controller.h @@ -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); diff --git a/src/utils b/src/utils index faf0463..bb201d5 160000 --- a/src/utils +++ b/src/utils @@ -1 +1 @@ -Subproject commit faf0463e3791ee97dfc370282ecad4cd3b4ca475 +Subproject commit bb201d5085cfb2e54ebad9a0bf22347a5251f7a2