diff --git a/resources/spacefighter.glb b/resources/spacefighter.glb new file mode 100644 index 0000000..db98dc3 Binary files /dev/null and b/resources/spacefighter.glb differ diff --git a/src/core/camera_node.c b/src/core/camera_node.c index 8b8b849..1b6fa9b 100644 --- a/src/core/camera_node.c +++ b/src/core/camera_node.c @@ -46,7 +46,7 @@ void CameraNodeExitTree(CameraNode *self) { Camera3D CameraNodeGetCamera(CameraNode *self) { // Get the global transform matrix of the parent transformable Transform global_transform = self->transform.tc->get_global_transform(self->transform.data); - Matrix mat = TransformGetMatrix(&global_transform); + Matrix mat = TransformGetMatrix(global_transform); // get the forward row from the matrix Vector3 forward = MATRIX_FORWARD(mat); // construct a new camera at the global transform location and facing the forward vector diff --git a/src/core/mesh_render_entity.c b/src/core/mesh_render_entity.c new file mode 100644 index 0000000..f6de252 --- /dev/null +++ b/src/core/mesh_render_entity.c @@ -0,0 +1,50 @@ +#include "mesh_render_entity.h" +#include "render.h" + +START_REFLECT(MeshRenderEntity); +REFLECT_TYPECLASS(MeshRenderEntity, Drop); +REFLECT_TYPECLASS(MeshRenderEntity, SceneNodeEntity); +REFLECT_TYPECLASS(MeshRenderEntity, Renderable); +END_REFLECT(MeshRenderEntity); + +impl_SceneNodeEntity_defaults(MeshRenderEntity) +impl_SceneNodeEntity_for(MeshRenderEntity, + MeshRenderEntityEnterTree, + MeshRenderEntityExitTree, + MeshRenderEntity_default_tick +) + +impl_Drop_for(MeshRenderEntity, + DestroyMeshRenderEntity +) + +impl_Renderable_for(MeshRenderEntity, + MeshRenderEntityDraw +) + +SceneNode *CreateMeshRenderEntity(char const *resource_path) { + MeshRenderEntity *self = new(MeshRenderEntity); + GetModelResource(resource_path, &self->model); + return CreateSceneNode(MeshRenderEntity_as_SceneNodeEntity(self)); +} + +void DestroyMeshRenderEntity(MeshRenderEntity *self) { + free(self); +} + +void MeshRenderEntityEnterTree(MeshRenderEntity *self) { + SceneNodeEntity parent_entity = self->node->parent->entity; + self->transform = TC_CAST(parent_entity, Transformable); + LoadResource(self->model.handle); + AddRenderable(MeshRenderEntity_as_Renderable(self)); +} + +void MeshRenderEntityExitTree(MeshRenderEntity *self) { + ReleaseResource(self->model.handle); + RemoveRenderable(MeshRenderEntity_as_Renderable(self)); +} + +void MeshRenderEntityDraw(MeshRenderEntity *self) { + self->model.resource->transform = TransformGetMatrix(self->transform.tc->get_global_transform(self->transform.data)); + DrawModel(*self->model.resource, (Vector3){0.f, 0.f, 0.f}, 1.f, WHITE); +} diff --git a/src/core/mesh_render_entity.h b/src/core/mesh_render_entity.h new file mode 100644 index 0000000..ca726cb --- /dev/null +++ b/src/core/mesh_render_entity.h @@ -0,0 +1,30 @@ +#ifndef MESH_RENDER_ENTITY_H +#define MESH_RENDER_ENTITY_H + +#include "renderable.h" +#include "resources.h" +#include "scene_node_entity.h" +#include "transformable.h" +#include "utils/drop.h" +#include "utils/mirror.h" + +typedef struct MeshRenderEntity { + SceneNode *node; + Transformable transform; + ModelResource model; +} MeshRenderEntity; + +extern SceneNode *CreateMeshRenderEntity(char const *resource_path); +extern void DestroyMeshRenderEntity(MeshRenderEntity *self); + +extern void MeshRenderEntityEnterTree(MeshRenderEntity *self); +extern void MeshRenderEntityExitTree(MeshRenderEntity *self); + +extern void MeshRenderEntityDraw(MeshRenderEntity *self); + +DECL_REFLECT(MeshRenderEntity); +decl_typeclass_impl(Drop, MeshRenderEntity); +decl_typeclass_impl(SceneNodeEntity, MeshRenderEntity); +decl_typeclass_impl(Renderable, MeshRenderEntity); + +#endif // !MESH_RENDER_ENTITY_H diff --git a/src/core/resources.c b/src/core/resources.c index be78d8e..3160d36 100644 --- a/src/core/resources.c +++ b/src/core/resources.c @@ -1,31 +1,178 @@ #include "resources.h" -#include "stdint.h" +#include "stdbool.h" +#include "utils/hash_map.h" #include "utils/dictionary.h" #include "utils/debug.h" +#include "utils/strutil.h" char const *RESOURCE_DIRECTORY = "resources"; +typedef enum ResourceType { + RESOURCE_MODEL, RESOURCE_TEXTURE, + RESOURCE_MAX +} ResourceType; + +typedef struct ResourceContainer { + char const *path; + char const *name; + unsigned use_counter; + bool is_loaded; + ResourceType type; + union { + Model model; + Texture texture; + }; +} ResourceContainer; + +typedef void (*LoadResourceFn)(ResourceContainer *resource); +typedef void (*UnloadResourceFn)(ResourceContainer *resource); + +static void Internal_LoadModelResource(ResourceContainer *resource); +static void Internal_UnloadModelResource(ResourceContainer *resource); +static void Internal_LoadTextureResource(ResourceContainer *resource); +static void Internal_UnloadTextureResource(ResourceContainer *resource); + +LoadResourceFn g_load_functions[] = {Internal_LoadModelResource, Internal_LoadTextureResource}; +UnloadResourceFn g_unload_functions[] = {Internal_UnloadModelResource, Internal_UnloadTextureResource}; + +static HashMap g_resource_map; +static FilePathList g_resource_files; + +//! hash function for hashmaps with char* as key type. +static uintptr_t HashMapHashString(char const **str_ptr) { + return strhash(*str_ptr); +} + +//! Use file extensions to figure out the +static ResourceType ResourceGetTypeFromPath(char const *path) { + // Texture files + if(IsFileExtension(path, ".png")) return RESOURCE_TEXTURE; + if(IsFileExtension(path, ".jpg")) return RESOURCE_TEXTURE; + if(IsFileExtension(path, ".jpeg")) return RESOURCE_TEXTURE; + // Model files + if(IsFileExtension(path, ".glb")) return RESOURCE_MODEL; + if(IsFileExtension(path, ".gltf")) return RESOURCE_MODEL; + return RESOURCE_MAX; +} + +//! Find which resource directory to use: Relative to working directory, or relative to application directory +//! Working directory resources are only supposed to be used for debugging. static inline -void InitializeResourceDirectory() { +void Internal_InitializeResourceDirectory() { // if there is a resource directory in the working directory, prioritize that for debugging if(DirectoryExists(RESOURCE_DIRECTORY)) { ChangeDirectory(TextFormat("%s/%s", GetWorkingDirectory(), RESOURCE_DIRECTORY)); - LOG_WARNING("Using working dir resources, this is possible for debug purposes."); + LOG_WARNING("Using working dir resources, this is intended for debug purposes only."); + return; } // check application installation directory for a resource directory // this is the default running environment - char const *installation_resource_dir = TextFormat(GetApplicationDirectory(), RESOURCE_DIRECTORY); + char const *installation_resource_dir = TextFormat("%s/%s", GetApplicationDirectory(), RESOURCE_DIRECTORY); if(DirectoryExists(installation_resource_dir)) { ChangeDirectory(installation_resource_dir); return; } - UNREACHABLE("Failed to find resource directory"); + UNREACHABLE("Failed to find a valid resource directory"); +} + +static inline +void Internal_IndexResourceDirectory() { + g_resource_files = LoadDirectoryFilesEx(GetWorkingDirectory(), NULL, true); + g_resource_map = hash_map_from_types(char const *, ResourceContainer, HashMapHashString); + ResourceContainer placeholder; + for(size_t i = 0; i < g_resource_files.count; ++i) { + placeholder = (ResourceContainer){ + .is_loaded = false, + .use_counter = 0, + .path = g_resource_files.paths[i], + .name = GetFileName(g_resource_files.paths[i]), + .type = ResourceGetTypeFromPath(g_resource_files.paths[i]) + }; + // only index resources that the engine knows how to load + if(placeholder.type != RESOURCE_MAX) { + LOG_INFO("Internal_IndexResourceDirectory: Indexing %s as %s", placeholder.path, placeholder.name); + hash_map_insert(&g_resource_map, &placeholder.name, &placeholder); + } + } } void InitializeResourceSubsystem() { - InitializeResourceDirectory(); + Internal_InitializeResourceDirectory(); + Internal_IndexResourceDirectory(); } void CleanResourceSubsystem() { + List resources = hash_map_values(&g_resource_map); + list_foreach(ResourceContainer *,resource, &resources) + g_unload_functions[resource->type](resource); + hash_map_empty(&g_resource_map); + UnloadDirectoryFiles(g_resource_files); } +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; + *out = (ModelResource) { + .handle = container, + .resource = &container->model + }; + return true; +} + +bool GetTextureResource(char const *path, TextureResource *out) { + ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path); + *out = ResourceEmpty(Texture); + // assert some assumptions about the found resource + ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path); + ASSERT_RETURN(container->type != RESOURCE_TEXTURE, false, "GetTextureResource: Resource %s is not a Texture.", path); + *out = (TextureResource) { + .handle = container, + .resource = &container->texture + }; + return true; +} + +bool IsResourceLoaded(ResourceHandle handle) { + return handle->is_loaded; +} + +void LoadResource(ResourceHandle handle) { + ASSERT_RETURN(handle != NULL,, "LoadResource: Resource handle invalid"); + 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; + --handle->use_counter; +} + +static +void Internal_LoadModelResource(ResourceContainer *resource) { + resource->model = LoadModel(resource->path); +} + +static +void Internal_UnloadModelResource(ResourceContainer *resource) { + UnloadModel(resource->model); + resource->model = (Model){0}; +} + +static +void Internal_LoadTextureResource(ResourceContainer *resource) { + resource->texture = LoadTexture(resource->path); +} + +static +void Internal_UnloadTextureResource(ResourceContainer *resource) { + UnloadTexture(resource->texture); + resource->texture = (Texture){0}; +} diff --git a/src/core/resources.h b/src/core/resources.h index 84cacf1..1b1c5ca 100644 --- a/src/core/resources.h +++ b/src/core/resources.h @@ -2,10 +2,30 @@ #define RESOURCES_H #include "raylib.h" +#include "stdint.h" +#include "stdbool.h" + +typedef struct ResourceContainer* ResourceHandle; +#define ResourceType(T) typedef struct T##Resource { ResourceHandle handle; T *resource; } T##Resource +#define ResourceEmpty(T) ((T##Resource){.handle = NULL, .resource = NULL}) + +ResourceType(Model); +ResourceType(Texture); //! Allocate and initialize memory required to operate resource subsystem extern void InitializeResourceSubsystem(); //! Clean and shut down resource subsystem, all resource calls are invalid after this extern void CleanResourceSubsystem(); +//! Get model from resources. +extern bool GetModelResource(char const *path, ModelResource *out); +//! Get indexed texture from resources. +extern bool GetTextureResource(char const *path, TextureResource *out); + +//! Check if a resource is loaded or not. +extern bool IsResourceLoaded(ResourceHandle handle); +//! Load a resource using a handle returned from Get{Type}Resource, increments use count. +extern void LoadResource(ResourceHandle resource); +//! Release a resource using a handle returned from Get{Type}Resource, increments use count. +extern void ReleaseResource(ResourceHandle handle); #endif // !RESOURCES_H diff --git a/src/core/transformable.c b/src/core/transformable.c index 2e9af7c..ef596f9 100644 --- a/src/core/transformable.c +++ b/src/core/transformable.c @@ -9,45 +9,45 @@ Transform TransformIdentity() { } -Matrix TransformGetMatrix(Transform const *self) { - Matrix mat = MatrixScale(self->scale.x, self->scale.y, self->scale.z); - mat = MatrixMultiply(mat, QuaternionToMatrix(self->rotation)); - mat.m12 = self->translation.x; - mat.m13 = self->translation.y; - mat.m14 = self->translation.z; +Matrix TransformGetMatrix(Transform self) { + Matrix mat = MatrixScale(self.scale.x, self.scale.y, self.scale.z); + mat = MatrixMultiply(mat, QuaternionToMatrix(self.rotation)); + mat.m12 = self.translation.x; + mat.m13 = self.translation.y; + mat.m14 = self.translation.z; return mat; } -Vector3 TransformPosition(Transform const *self, Vector3 local_pos) { - return Vector3Add(TransformDirection(self, local_pos), self->translation); +Vector3 TransformPosition(Transform self, Vector3 local_pos) { + return Vector3Add(TransformDirection(self, local_pos), self.translation); } -Vector3 TransformDirection(Transform const *self, Vector3 local_direction) { +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)); } -Vector3 TransformScale(Transform const *self, Vector3 local_scale) { - return Vector3Multiply(self->scale, TransformDirection(self, local_scale)); +Vector3 TransformScale(Transform self, Vector3 local_scale) { + return Vector3Multiply(self.scale, TransformDirection(self, local_scale)); } -Quaternion TransformRotation(Transform const *self, Quaternion local_rotation) { - return QuaternionMultiply(self->rotation, local_rotation); +Quaternion TransformRotation(Transform self, Quaternion local_rotation) { + return QuaternionMultiply(self.rotation, local_rotation); } -Transform TransformTransform(Transform const *self, Transform const *other) { +Transform TransformTransform(Transform self, Transform other) { return (Transform) { - .translation = TransformPosition(self, other->translation), - .scale = TransformScale(self, other->scale), - .rotation = TransformRotation(self, other->rotation) + .translation = TransformPosition(self, other.translation), + .scale = TransformScale(self, other.scale), + .rotation = TransformRotation(self, other.rotation) }; } -Vector3 InverseTransformPosition(Transform const *self, Vector3 global_pos) { - return Vector3Subtract(InverseTransformDirection(self, global_pos), self->translation); +Vector3 InverseTransformPosition(Transform self, Vector3 global_pos) { + return Vector3Subtract(InverseTransformDirection(self, global_pos), self.translation); } -Vector3 InverseTransformDirection(Transform const *self, Vector3 global_direction) { +Vector3 InverseTransformDirection(Transform self, Vector3 global_direction) { Matrix const mat = TransformGetMatrix(self); return (Vector3){ Vector3DotProduct(MATRIX_RIGHT(mat), Vector3Scale(VECTOR3_RIGHT, global_direction.x)), @@ -56,18 +56,18 @@ Vector3 InverseTransformDirection(Transform const *self, Vector3 global_directio }; } -Vector3 InverseTransformScale(Transform const *self, Vector3 global_scale) { - return Vector3Multiply(Vector3Invert(self->scale), InverseTransformDirection(self, global_scale)); +Vector3 InverseTransformScale(Transform self, Vector3 global_scale) { + return Vector3Multiply(Vector3Invert(self.scale), InverseTransformDirection(self, global_scale)); } -Quaternion InverseTransformRotation(Transform const *self, Quaternion global_rotation) { - return QuaternionMultiply(QuaternionInvert(self->rotation), global_rotation); +Quaternion InverseTransformRotation(Transform self, Quaternion global_rotation) { + return QuaternionMultiply(QuaternionInvert(self.rotation), global_rotation); } -Transform InverseTransformTransform(Transform const *self, Transform const *other) { +Transform InverseTransformTransform(Transform self, Transform other) { return (Transform) { - .translation = InverseTransformPosition(self, other->translation), - .scale = InverseTransformScale(self, other->scale), - .rotation = InverseTransformRotation(self, other->rotation) + .translation = InverseTransformPosition(self, other.translation), + .scale = InverseTransformScale(self, other.scale), + .rotation = InverseTransformRotation(self, other.rotation) }; } diff --git a/src/core/transformable.h b/src/core/transformable.h index 097eece..ac9db49 100644 --- a/src/core/transformable.h +++ b/src/core/transformable.h @@ -28,19 +28,19 @@ typedef struct Transformable { } Transformable; extern Transform TransformIdentity(); -extern Matrix TransformGetMatrix(Transform const *self); +extern Matrix TransformGetMatrix(Transform self); -extern Vector3 TransformPosition(Transform const *self, Vector3 local_pos); -extern Vector3 TransformDirection(Transform const *self, Vector3 local_direction); -extern Vector3 TransformScale(Transform const *self, Vector3 local_scale); -extern Quaternion TransformRotation(Transform const *self, Quaternion local_rotation); -extern Transform TransformTransform(Transform const *self, Transform const *other); +extern Vector3 TransformPosition(Transform self, Vector3 local_pos); +extern Vector3 TransformDirection(Transform self, Vector3 local_direction); +extern Vector3 TransformScale(Transform self, Vector3 local_scale); +extern Quaternion TransformRotation(Transform self, Quaternion local_rotation); +extern Transform TransformTransform(Transform self, Transform other); -extern Vector3 InverseTransformPosition(Transform const *self, Vector3 global_pos); -extern Vector3 InverseTransformDirection(Transform const *self, Vector3 global_direction); -extern Vector3 InverseTransformScale(Transform const *self, Vector3 global_scale); -extern Quaternion InverseTransformRotation(Transform const *self, Quaternion quat); -extern Transform InverseTransformTransform(Transform const *self, Transform const *other); +extern Vector3 InverseTransformPosition(Transform self, Vector3 global_pos); +extern Vector3 InverseTransformDirection(Transform self, Vector3 global_direction); +extern Vector3 InverseTransformScale(Transform self, Vector3 global_scale); +extern Quaternion InverseTransformRotation(Transform self, Quaternion quat); +extern Transform InverseTransformTransform(Transform self, Transform other); #define MATRIX_UP(self_) ((Vector3){self_.m0, self_.m1, self_.m2}) #define MATRIX_RIGHT(self_) ((Vector3){self_.m4, self_.m5, self_.m6}) @@ -66,7 +66,7 @@ static void T##_set_global_transform_GEN_(T* self, Transform value) {\ self->transform = value;\ } else {\ Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\ - self->transform = InverseTransformTransform(&parent_global, &value); /* Use parent transform to transform the local transform to the new global transform */\ + self->transform = InverseTransformTransform(parent_global, value); /* Use parent transform to transform the local transform to the new global transform */\ }\ }\ static void T##_force_update_GEN_(T *self) {\ @@ -75,7 +75,7 @@ static void T##_force_update_GEN_(T *self) {\ return;\ }\ Transform parent_global = self->parent_transformable.tc->get_global_transform(self->parent_transformable.data);\ - self->global_transform = TransformTransform(&parent_global, &self->transform);\ + self->global_transform = TransformTransform(parent_global, self->transform);\ }\ static unsigned char T##_get_dirty_bit_GEN_(T *self) { return self->dirty_bit; }\ static Transformable T##_get_parent_transformable_GEN_(T *self) { return self->parent_transformable; }\ diff --git a/src/main.c b/src/main.c index 153bdfa..22425b4 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include "core/engine_loop.h" #include "core/scene.h" #include "core/transform_node.h" +#include "core/mesh_render_entity.h" #include "utils/debug.h" Scene *CreateInitialScene() { @@ -28,6 +29,7 @@ Scene *CreateInitialScene() { SceneNodeAddChild(camera_parent_parent, camera_parent); SceneNodeAddChild(camera_parent, CreateCameraNode()); SceneNodeAddChild(camera_parent, CreateTestObject()); + SceneNodeAddChild(root, CreateMeshRenderEntity("spacefighter.glb")); return CreateScene(root); }