feat: defined various core engine concepts
- Scene Tree - Engine loop - Draw list - Renderables - Resources
This commit is contained in:
parent
ddd9323c60
commit
ba614edd29
|
@ -1,95 +0,0 @@
|
||||||
/**********************************************************************************************
|
|
||||||
*
|
|
||||||
* raylibExtras * Utilities and Shared Components for Raylib
|
|
||||||
*
|
|
||||||
* Resource Dir * function to help find resource dir in common locations
|
|
||||||
*
|
|
||||||
* LICENSE: MIT
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022 Jeffery Myers
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
**********************************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "raylib.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" { // Prevents name mangling of functions
|
|
||||||
#endif
|
|
||||||
/// <summary>
|
|
||||||
/// Looks for the specified resource dir in several common locations
|
|
||||||
/// The working dir
|
|
||||||
/// The app dir
|
|
||||||
/// Up to 3 levels above the app dir
|
|
||||||
/// When found the dir will be set as the working dir so that assets can be loaded relative to that.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folderName">The name of the resources dir to look for</param>
|
|
||||||
/// <returns>True if a dir with the name was found, false if no change was made to the working dir</returns>
|
|
||||||
inline static bool SearchAndSetResourceDir(const char* folderName)
|
|
||||||
{
|
|
||||||
// check the working dir
|
|
||||||
if (DirectoryExists(folderName))
|
|
||||||
{
|
|
||||||
ChangeDirectory(TextFormat("%s/%s", GetWorkingDirectory(), folderName));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* appDir = GetApplicationDirectory();
|
|
||||||
|
|
||||||
// check the applicationDir
|
|
||||||
const char* dir = TextFormat("%s%s", appDir, folderName);
|
|
||||||
if (DirectoryExists(dir))
|
|
||||||
{
|
|
||||||
ChangeDirectory(dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check one up from the app dir
|
|
||||||
dir = TextFormat("%s../%s", appDir, folderName);
|
|
||||||
if (DirectoryExists(dir))
|
|
||||||
{
|
|
||||||
ChangeDirectory(dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check two up from the app dir
|
|
||||||
dir = TextFormat("%s../../%s", appDir, folderName);
|
|
||||||
if (DirectoryExists(dir))
|
|
||||||
{
|
|
||||||
ChangeDirectory(dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check three up from the app dir
|
|
||||||
dir = TextFormat("%s../../../%s", appDir, folderName);
|
|
||||||
if (DirectoryExists(dir))
|
|
||||||
{
|
|
||||||
ChangeDirectory(dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
}
|
|
||||||
#endif
|
|
18
src/core/camera_node.h
Normal file
18
src/core/camera_node.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene_node_entity.h"
|
||||||
|
#include "utils/drop.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
typedef struct CameraNode {
|
||||||
|
SceneNode *node;
|
||||||
|
} CameraNode;
|
||||||
|
|
||||||
|
void CameraNodeEnterTree(CameraNode *self);
|
||||||
|
void CameraNodeExitTree(CameraNode *self);
|
||||||
|
void CameraNodeTick(CameraNode *self, double delta);
|
||||||
|
Camera3D CameraNodeGetCamera(CameraNode *self);
|
||||||
|
|
||||||
|
impl_default_Drop_for(CameraNode);
|
||||||
|
impl_SceneNodeEntity_for(CameraNode, CameraNodeEnterTree, CameraNodeExitTree, CameraNodeTick);
|
||||||
|
|
35
src/core/engine_loop.c
Normal file
35
src/core/engine_loop.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include "engine_loop.h"
|
||||||
|
#include "resources.h"
|
||||||
|
#include "render.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
static
|
||||||
|
void InitializeRaylibContext() {
|
||||||
|
// initialize fullscreen game window
|
||||||
|
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_HIGHDPI);
|
||||||
|
InitWindow(1280, 800, "Hello Raylib");
|
||||||
|
if(!IsWindowFullscreen())
|
||||||
|
ToggleFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunGame() {
|
||||||
|
while (!WindowShouldClose()) {
|
||||||
|
RenderNextFrame();
|
||||||
|
}
|
||||||
|
ShutDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeEngine() {
|
||||||
|
// initialize context
|
||||||
|
InitializeRaylibContext();
|
||||||
|
// initialize engine subsystems
|
||||||
|
InitializeResourceSubsystem();
|
||||||
|
InitializeRenderingSubsystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutDown() {
|
||||||
|
CleanResourceSubsystem();
|
||||||
|
CloseWindow();
|
||||||
|
exit(0);
|
||||||
|
}
|
14
src/core/engine_loop.h
Normal file
14
src/core/engine_loop.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct GameContext {
|
||||||
|
} GameContext;
|
||||||
|
|
||||||
|
// IMPORTANT: should be implemented by game code
|
||||||
|
extern void InitializeGame(GameContext *settings);
|
||||||
|
|
||||||
|
//! initialize window and application context
|
||||||
|
extern void InitializeEngine();
|
||||||
|
//! run the actual game, requires InitializeEngine to be called first
|
||||||
|
extern void RunGame();
|
||||||
|
// shut down game entirely
|
||||||
|
extern void ShutDown();
|
35
src/core/render.c
Normal file
35
src/core/render.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include "render.h"
|
||||||
|
#include "utils/debug.h"
|
||||||
|
#include "utils/list.h"
|
||||||
|
|
||||||
|
static List g_render_objects = {}; //!< List of all registered rendering objects
|
||||||
|
CameraNode *camera = NULL; //!< Reference to current main camera
|
||||||
|
|
||||||
|
void InitializeRenderingSubsystem() {
|
||||||
|
g_render_objects = list_from_type(Renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanupRenderingSubsystem() {
|
||||||
|
list_empty(&g_render_objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRenderable(Renderable renderable) {
|
||||||
|
ASSERT_RETURN(list_find(&g_render_objects, &renderable) == g_render_objects.len,, "AddRenderable: Argument is already registered, cannot register twice");
|
||||||
|
list_add(&g_render_objects, &renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveRenderable(Renderable renderable) {
|
||||||
|
size_t idx = list_find(&g_render_objects, &renderable);
|
||||||
|
ASSERT_RETURN(idx != g_render_objects.len,, "RemoveRenderable: Argument is not registered, cannot unregister.");
|
||||||
|
list_erase(&g_render_objects, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderNextFrame() {
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
BeginMode3D(CameraNodeGetCamera(camera));
|
||||||
|
list_foreach(Renderable *,object, &g_render_objects)
|
||||||
|
object->tc->draw(object->data);
|
||||||
|
EndMode3D();
|
||||||
|
EndDrawing();
|
||||||
|
}
|
24
src/core/render.h
Normal file
24
src/core/render.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "camera_node.h"
|
||||||
|
#include "renderable.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
//! Initialize the rendering subsystem.
|
||||||
|
extern void InitializeRenderingSubsystem();
|
||||||
|
//! Cleanup after rendering system,
|
||||||
|
//! assumes you won't want to re-initialize after.
|
||||||
|
extern void CleanupRenderingSubsystem();
|
||||||
|
|
||||||
|
//! Register a renderable object through a typeclass.
|
||||||
|
extern void AddRenderable(Renderable renderable);
|
||||||
|
//! Remove a renderable object from draw list
|
||||||
|
extern void RemoveRenderable(Renderable renderable);
|
||||||
|
|
||||||
|
//! Set a camera as the main rendering camera.
|
||||||
|
extern void SetMainCamera(CameraNode *camera);
|
||||||
|
//! Get the current main rendering camera.
|
||||||
|
extern void GetMainCamera(CameraNode *camera);
|
||||||
|
|
||||||
|
//! Draw a frame to the screen based on the current state of the game.
|
||||||
|
extern void RenderNextFrame();
|
23
src/core/renderable.h
Normal file
23
src/core/renderable.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene_node_entity.h"
|
||||||
|
#include "utils/typeclass_helpers.h"
|
||||||
|
|
||||||
|
typedef struct IRenderable {
|
||||||
|
void (*const draw)(void *self);
|
||||||
|
} IRenderable;
|
||||||
|
|
||||||
|
typedef struct Renderable {
|
||||||
|
IRenderable const *tc;
|
||||||
|
void *data;
|
||||||
|
ISceneNodeEntity const *scene_node_entity;
|
||||||
|
} Renderable;
|
||||||
|
|
||||||
|
#define impl_Renderable_for(T, draw_f)\
|
||||||
|
Renderable T##_as_Renderable(T *x) {\
|
||||||
|
TC_FN_TYPECHECK(void, draw_f, T*);\
|
||||||
|
static IRenderable const tc = {\
|
||||||
|
.draw = (void(*const)(void*)) draw_f\
|
||||||
|
};\
|
||||||
|
return (Renderable){ .tc = &tc, .data = x, .scene_node_entity = T##_as_SceneNodeEntity(x).tc };\
|
||||||
|
}
|
64
src/core/resources.c
Normal file
64
src/core/resources.c
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include "resources.h"
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "utils/dictionary.h"
|
||||||
|
#include "utils/debug.h"
|
||||||
|
|
||||||
|
char const *RESOURCE_DIRECTORY = "resources";
|
||||||
|
|
||||||
|
typedef enum ResourceType {
|
||||||
|
MODEL_RESOURCE = 0, TEXTURE_RESOURCE
|
||||||
|
} ResourceType;
|
||||||
|
|
||||||
|
typedef struct ResourceContainer {
|
||||||
|
uint32_t rid;
|
||||||
|
char const *resource_name;
|
||||||
|
ResourceType type;
|
||||||
|
union {
|
||||||
|
Model model;
|
||||||
|
Texture texture;
|
||||||
|
};
|
||||||
|
} ResourceContainer;
|
||||||
|
|
||||||
|
static Dictionary g_resources = {};
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void Internal_CleanResourceContainer(ResourceContainer *container) {
|
||||||
|
switch(container->type) {
|
||||||
|
case MODEL_RESOURCE:
|
||||||
|
UnloadModel(container->model);
|
||||||
|
return;
|
||||||
|
case TEXTURE_RESOURCE:
|
||||||
|
UnloadTexture(container->texture);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UNREACHABLE("CleanResourceContainer: Cleanup function not defined for %u", container->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void 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.");
|
||||||
|
}
|
||||||
|
// check application installation directory for a resource directory
|
||||||
|
// this is the default running environment
|
||||||
|
char const *installation_resource_dir = TextFormat(GetApplicationDirectory(), RESOURCE_DIRECTORY);
|
||||||
|
if(DirectoryExists(installation_resource_dir)) {
|
||||||
|
ChangeDirectory(installation_resource_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UNREACHABLE("Failed to find resource directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeResourceSubsystem() {
|
||||||
|
InitializeResourceDirectory();
|
||||||
|
g_resources = dictionary_from_type(ResourceContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanResourceSubsystem() {
|
||||||
|
list_foreach(ResourceContainer *,container, &g_resources.list)
|
||||||
|
Internal_CleanResourceContainer(container);
|
||||||
|
dictionary_empty(&g_resources);
|
||||||
|
}
|
||||||
|
|
11
src/core/resources.h
Normal file
11
src/core/resources.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
extern void InitializeResourceSubsystem();
|
||||||
|
extern void CleanResourceSubsystem();
|
||||||
|
|
||||||
|
//! Load a model resource from disk, path is relative to the resource folder
|
||||||
|
extern Model *ResourcesLoadModel(char const *res_path);
|
||||||
|
//! Load a texture resource from disk, path is relative to the resource folder
|
||||||
|
extern Texture *ResourcesLoadTexture(char const *res_path);
|
98
src/core/scene.c
Normal file
98
src/core/scene.c
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#include "scene.h"
|
||||||
|
#include "scene_node_entity.h"
|
||||||
|
#include "utils/debug.h"
|
||||||
|
#include "utils/list.h"
|
||||||
|
#include "utils/drop.h"
|
||||||
|
#include "utils/mirror.h"
|
||||||
|
|
||||||
|
struct SceneNode {
|
||||||
|
SceneNode *parent;
|
||||||
|
Scene *scene;
|
||||||
|
List children; //!< list of child SceneNodes
|
||||||
|
SceneNodeEntity entity; //!< scene node entity that adds functionality to the node
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Scene {
|
||||||
|
SceneNode *root;
|
||||||
|
List nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void Internal_SceneRemoveNode(Scene *self, SceneNode *node) {
|
||||||
|
size_t idx = list_find(&self->nodes, node);
|
||||||
|
list_erase(&self->nodes, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void Internal_SceneAddNode(Scene *self, SceneNode node) {
|
||||||
|
ASSERT_RETURN(list_find(&self->nodes, &node) == self->nodes.len,, "Attempting to add node that is already in this scene");
|
||||||
|
ASSERT_RETURN(node.scene != NULL,, "Attempting to add node that is already in a scene, remove it from that scene first");
|
||||||
|
list_add(&self->nodes, &node);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneNode *CreateSceneNode() {
|
||||||
|
SceneNode *node = new(SceneNode);
|
||||||
|
ASSERT_RETURN(node != NULL, NULL, "CreateSceneNode: Failed to instantiate new SceneNode");
|
||||||
|
node->children = list_from_type(SceneNode*);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroySceneNode(SceneNode *self) {
|
||||||
|
// remove all children from scene as well
|
||||||
|
list_foreach(SceneNode **, child, &self->children)
|
||||||
|
DestroySceneNode(*child);
|
||||||
|
// inform entity of exit tree event
|
||||||
|
self->entity.tc->exit_tree(self->entity.data);
|
||||||
|
// remove node from scene if it is part of one
|
||||||
|
if(self->scene != NULL)
|
||||||
|
Internal_SceneRemoveNode(self->scene, self);
|
||||||
|
if(self->entity.data != NULL) {
|
||||||
|
self->entity.drop->drop(self->entity.data);
|
||||||
|
}
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneNode *SceneNodeGetChild(SceneNode *self, size_t idx) {
|
||||||
|
return *list_at_as(SceneNode*, &self->children, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneNodeAddChild(SceneNode *self, SceneNode *child) {
|
||||||
|
// catch logic error of trying to attach a child that already has a parent
|
||||||
|
ASSERT_RETURN(child->parent != NULL,, "SceneNodeAddChild: New child node already has a parent");
|
||||||
|
// register parent-child relationship
|
||||||
|
list_add(&self->children, &child);
|
||||||
|
child->parent = self;
|
||||||
|
// make sure scene matches
|
||||||
|
child->scene = self->scene;
|
||||||
|
// notify child of scene entrance
|
||||||
|
child->entity.tc->enter_tree(child->entity.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneNodeRemoveChild(SceneNode *self, SceneNode *child) {
|
||||||
|
// catch error of attempting to remove non-child node
|
||||||
|
ASSERT_RETURN(child->parent == self,, "SceneNodeRemoveChild: Cannot remove node that is not a child of this node.");
|
||||||
|
// get the index and use it to ensure that the child is actually registered with the parent
|
||||||
|
size_t const idx = list_find(&self->children, child);
|
||||||
|
ASSERT_RETURN(idx == self->children.len,, "IMPORTANT: SceneNodeRemoveChild: child is not registered with parent.");
|
||||||
|
// we now know that the child is actually a child of self.
|
||||||
|
// notify child that it is being removed from the tree.
|
||||||
|
child->entity.tc->exit_tree(child->entity.data);
|
||||||
|
// use the index to erase the object from the list
|
||||||
|
list_erase(&self->children, idx);
|
||||||
|
Internal_SceneRemoveNode(child->scene, child);
|
||||||
|
child->scene = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Internal_SceneNodeAttachEntity(SceneNodeEntity object) {
|
||||||
|
object.tc->get_node(object.data)->entity = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scene *CreateScene(SceneNode const *root) {
|
||||||
|
Scene *scene = new(Scene);
|
||||||
|
scene->nodes = list_from_type(SceneNode);
|
||||||
|
Internal_SceneAddNode(scene, *root);
|
||||||
|
root->entity.tc->enter_tree(root->entity.data);
|
||||||
|
return scene;
|
||||||
|
}
|
27
src/core/scene.h
Normal file
27
src/core/scene.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stddef.h"
|
||||||
|
#include "utils/typeclass_helpers.h"
|
||||||
|
typedef struct SceneNodeEntity SceneNodeEntity;
|
||||||
|
|
||||||
|
typedef struct SceneNode SceneNode;
|
||||||
|
typedef struct Scene Scene;
|
||||||
|
|
||||||
|
//! Instantiate a new scene node.
|
||||||
|
extern SceneNode *CreateSceneNode();
|
||||||
|
//! Free a scene node.
|
||||||
|
extern void DestroySceneNode(SceneNode *self);
|
||||||
|
//! Get a child from a scene node.
|
||||||
|
extern SceneNode *SceneNodeGetChild(SceneNode *self, size_t idx);
|
||||||
|
//! Attach a child to a node
|
||||||
|
extern void SceneNodeAddChild(SceneNode *self, SceneNode *child);
|
||||||
|
//! Detach a child from a node, removing it from the scene
|
||||||
|
extern void SceneNodeRemoveChild(SceneNode *self, SceneNode *child);
|
||||||
|
|
||||||
|
//! INTERNAL FUNCTION, used to attach a scene node entity typeclass to an object.
|
||||||
|
extern void Internal_SceneNodeAttachEntity(SceneNodeEntity object);
|
||||||
|
|
||||||
|
//! Instantiate a new scene with a root node.
|
||||||
|
extern Scene *CreateScene(SceneNode const *root);
|
||||||
|
//! Destroy a node and it's scene tree.
|
||||||
|
extern void DestroyScene(Scene *scene);
|
47
src/core/scene_node_entity.h
Normal file
47
src/core/scene_node_entity.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene.h"
|
||||||
|
#include "utils/mirror.h"
|
||||||
|
#include "utils/drop.h"
|
||||||
|
|
||||||
|
struct SceneNode;
|
||||||
|
|
||||||
|
typedef struct ISceneNodeEntity {
|
||||||
|
void(*const enter_tree)(void *self);
|
||||||
|
void(*const exit_tree)(void *self);
|
||||||
|
void(*const tick)(void *self, double delta);
|
||||||
|
struct SceneNode *(*const get_node)(void *self);
|
||||||
|
void (*const set_node)(void *self, struct SceneNode *node);
|
||||||
|
} ISceneNodeEntity;
|
||||||
|
|
||||||
|
typedef struct SceneNodeEntity {
|
||||||
|
ISceneNodeEntity const *tc;
|
||||||
|
void *data;
|
||||||
|
IMirror const *mirror;
|
||||||
|
IDrop const *drop;
|
||||||
|
} SceneNodeEntity;
|
||||||
|
|
||||||
|
//! Implement SceneNodeEntity for a struct.
|
||||||
|
//! IMPORTANT: requires Mirror and Drop to be implemented,
|
||||||
|
//! as well as a `SceneNode *node` member on T.
|
||||||
|
//! Generates functions called T##_get_node_GEN_ and T##_set_node_GEN_ as getter and setter for that member.
|
||||||
|
#define impl_SceneNodeEntity_for(T, enter_tree_f, exit_tree_f, tick_f)\
|
||||||
|
static struct SceneNode *T##_get_node_GEN_(T *self) { return self->node; }\
|
||||||
|
static void T##_set_node_GEN_(T *self, struct SceneNode *node) { self->node = node; }\
|
||||||
|
SceneNodeEntity T##AttachToSceneNode(T* x, SceneNode *node) {\
|
||||||
|
TC_FN_TYPECHECK(void, enter_tree_f, T*);\
|
||||||
|
TC_FN_TYPECHECK(void, exit_tree_f, T*);\
|
||||||
|
TC_FN_TYPECHECK(void, tick_f, T*, double);\
|
||||||
|
static ISceneNodeEntity const tc = {\
|
||||||
|
.enter_tree = (void(*const)(void*)) enter_tree_f,\
|
||||||
|
.exit_tree = (void(*const)(void*)) exit_tree_f,\
|
||||||
|
.tick = (void(*const)(void*,double)) tick_f,\
|
||||||
|
.get_node = (struct SceneNode *(*const)(void*)) T##_get_node_GEN_,\
|
||||||
|
.set_node = (void (*const)(void*, struct SceneNode*)) T##_set_node_GEN_\
|
||||||
|
};\
|
||||||
|
Internal_AttachToSceneNode(object);\
|
||||||
|
x->node = node;\
|
||||||
|
SceneNodeEntity e = { .tc = &tc, .data = x, .mirror = T##_as_Mirror(x).tc, .drop = T##_as_Drop(x).tc };\
|
||||||
|
Internal_SceneNodeAttachEntity(e);\
|
||||||
|
return e;\
|
||||||
|
}
|
19
src/core/transformable.c
Normal file
19
src/core/transformable.c
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "transformable.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateTransformable(Transformable *self, Transformable *parent) {
|
||||||
|
Transform *parent_global = self->tc->get_global_transform(self->data);
|
||||||
|
Transform *global = self->tc->get_global_transform(self->data);
|
||||||
|
Transform *local = self->tc->get_transform(self->data);
|
||||||
|
global->translation = Vector3Add(parent_global->translation, local->translation);
|
||||||
|
global->rotation = QuaternionMultiply(parent_global->rotation, local->rotation);
|
||||||
|
global->scale = Vector3Multiply(parent_global->scale, local->scale);
|
||||||
|
}
|
31
src/core/transformable.h
Normal file
31
src/core/transformable.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils/mirror.h"
|
||||||
|
#include "utils/typeclass_helpers.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "raymath.h"
|
||||||
|
|
||||||
|
typedef struct ITransformable {
|
||||||
|
Transform *(*const get_transform)(void*);
|
||||||
|
Transform *(*const get_global_transform)(void*);
|
||||||
|
} ITransformable;
|
||||||
|
|
||||||
|
typedef struct Transformable {
|
||||||
|
ITransformable const *tc;
|
||||||
|
void *data;
|
||||||
|
IMirror const *mirror;
|
||||||
|
} Transformable;
|
||||||
|
|
||||||
|
extern Matrix TransformGetMatrix(Transform const *self);
|
||||||
|
extern void UpdateTransformable(Transformable *self, Transformable *parent);
|
||||||
|
|
||||||
|
#define impl_Transformable_for(T, get_transform_f)\
|
||||||
|
Transformable T##_as_Transformable(T *x) {\
|
||||||
|
TC_FN_TYPECHECK(Transform, get_transform_f, T*);\
|
||||||
|
static ITransformable const tc = {\
|
||||||
|
.get_transform = (Transform *(*const)(void*)) get_transform_f,\
|
||||||
|
.get_global_transform = (Transform *(*const)(void*)) get_global_transform_f\
|
||||||
|
};\
|
||||||
|
return { .tc = &tc, data = x, .mirror = T##_as_Mirror(x).tc };\
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue