diff --git a/TODO.md b/TODO.md index 6e60fb6..81cd1a2 100644 --- a/TODO.md +++ b/TODO.md @@ -152,3 +152,10 @@ components have aspect ratio lock sensibly (button) - give default admin commands - go deep with it, let us modify the engine runtime state + +## TAR PHYSICS + +- collision detection +- solver +- LUA API's for colliders and rigidbodies +- triple check accounting for rotation in all the other systems... should button, audiosource, be effected? should relative spin around the center of the transform?? diff --git a/engine/dist/include/yoyoengine/ecs/collider.h b/engine/dist/include/yoyoengine/ecs/collider.h index 8cbb453..29b39f0 100644 --- a/engine/dist/include/yoyoengine/ecs/collider.h +++ b/engine/dist/include/yoyoengine/ecs/collider.h @@ -25,6 +25,19 @@ #include #include +enum ye_collider_type { + YE_COLLIDER_RECT = 0, + YE_COLLIDER_CIRCLE = 1 +}; + +// v2, meh +// enum ye_collider_flags { +// YE_COLLIDER_TRIGGER = 1 << 0, +// YE_COLLIDER_STATIC = 1 << 1, +// YE_COLLIDER_RECT = 1 << 2, +// YE_COLLIDER_CIRCLE = 1 << 3 +// }; + /** * @brief A structure representing a collider component. * @@ -35,32 +48,30 @@ struct ye_component_collider { bool active; /**< Controls whether system will act upon this component. */ bool relative; /**< Specifies whether this component is relative to a parent transform. */ - - struct ye_rectf rect; /**< The collider rectangle. */ bool is_trigger; /**< Specifies whether this collider is a trigger. If false, it is a static collider. */ - /* - Meta tracking trigger states - */ - // bool _trigger_entered; + enum ye_collider_type type; /**< The type of collider. */ + float x, y; + // TODO: not sure how i feel about anonymous structs in the union... + union { + // YE_COLLIDER_RECT + struct { float width, height; }; + + // YE_COLLIDER_CIRCLE + struct { float radius; }; + }; }; -/** - * @brief Adds a static collider to an entity. - * - * @param entity The entity to which the collider is to be added. - * @param rect The rectangle defining the collider. - */ -YE_API void ye_add_static_collider_component(struct ye_entity *entity, struct ye_rectf rect); +/////////////////////////////////////////////////// -/** - * @brief Adds a trigger collider to an entity. - * - * @param entity The entity to which the collider is to be added. - * @param rect The rectangle defining the collider. - */ -YE_API void ye_add_trigger_collider_component(struct ye_entity *entity, struct ye_rectf rect); +YE_API void ye_add_static_rect_collider_component(struct ye_entity *entity, float x, float y, float w, float h); +YE_API void ye_add_trigger_rect_collider_component(struct ye_entity *entity, float x, float y, float w, float h); + +YE_API void ye_add_static_circle_collider_component(struct ye_entity *entity, float x, float y, float radius); +YE_API void ye_add_trigger_circle_collider_component(struct ye_entity *entity, float x, float y, float radius); + +/////////////////////////////////////////////////// /** * @brief Removes an entity's collider component. diff --git a/engine/dist/include/yoyoengine/ecs/ecs.h b/engine/dist/include/yoyoengine/ecs/ecs.h index 776c5b7..150d595 100644 --- a/engine/dist/include/yoyoengine/ecs/ecs.h +++ b/engine/dist/include/yoyoengine/ecs/ecs.h @@ -17,6 +17,8 @@ #include +#include + /* ============================================================= ENTITY LISTS @@ -28,12 +30,12 @@ YE_API extern struct ye_entity_node *entity_list_head; YE_API extern struct ye_entity_node *transform_list_head; YE_API extern struct ye_entity_node *renderer_list_head; YE_API extern struct ye_entity_node *camera_list_head; -YE_API extern struct ye_entity_node *physics_list_head; YE_API extern struct ye_entity_node *tag_list_head; YE_API extern struct ye_entity_node *collider_list_head; YE_API extern struct ye_entity_node *lua_script_list_head; YE_API extern struct ye_entity_node *audiosource_list_head; YE_API extern struct ye_entity_node *button_list_head; +YE_API extern struct ye_entity_node *rigidbody_list_head; /** * @brief Linked list structure for storing entities @@ -95,18 +97,10 @@ struct ye_entity { struct ye_component_lua_script *lua_script; // lua script component struct ye_component_button *button; // button component struct ye_component_camera *camera; // camera component - struct ye_component_physics *physics; // physics component struct ye_component_collider *collider; // collider component struct ye_component_tag *tag; // tag component struct ye_component_audiosource *audiosource; // audiosource component -}; - -/** - * @brief 2D vector structure - */ -struct ye_vec2f { - float x; - float y; + struct ye_component_rigidbody *rigidbody; // rigidbody component }; /* diff --git a/engine/dist/include/yoyoengine/ecs/physics.h b/engine/dist/include/yoyoengine/ecs/physics.h deleted file mode 100644 index 1863cd7..0000000 --- a/engine/dist/include/yoyoengine/ecs/physics.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is a part of yoyoengine. (https://github.com/yoyoengine/yoyoengine) - Copyright (C) 2023-2024 Ryan Zmuda - - Licensed under the MIT license. See LICENSE file in the project root for details. -*/ - -/** - * @file physics.h - * @brief ECS Physics component - */ - -#ifndef YE_PHYSICS_H -#define YE_PHYSICS_H - -#include - -/* - Set a default of 10 substeps if the developer does not override it -*/ -#ifndef YE_PHYSICS_SUBSTEPS - #define YE_PHYSICS_SUBSTEPS 10 -#endif - -#include -#include - -/** - * @brief Physics component structure - * - * This structure holds the physics properties of an entity. - */ -struct ye_component_physics { - bool active; /**< Controls whether system will act upon this component */ - - struct ye_vec2f velocity; /**< Velocity of entity */ - float rotational_velocity; /**< Rotational velocity of entity */ -}; - -/** - * @brief Adds a physics component to an entity - * - * @param entity The entity to add the component to - * @param velocity_x The x component of the velocity - * @param velocity_y The y component of the velocity - */ -YE_API void ye_add_physics_component(struct ye_entity *entity, float velocity_x, float velocity_y); - -/** - * @brief Removes the physics component from an entity - * - * @param entity The entity to remove the component from - */ -YE_API void ye_remove_physics_component(struct ye_entity *entity); - -/** - * @brief Physics system function - * - * This function is responsible for updating the physics components of all entities. - */ -YE_API void ye_system_physics(); - -#endif \ No newline at end of file diff --git a/engine/dist/include/yoyoengine/ecs/renderer.h b/engine/dist/include/yoyoengine/ecs/renderer.h index b359862..11916e6 100644 --- a/engine/dist/include/yoyoengine/ecs/renderer.h +++ b/engine/dist/include/yoyoengine/ecs/renderer.h @@ -22,6 +22,8 @@ #include +#include + /** * @enum ye_component_renderer_type * @brief Enum to store different unique types of renderers. diff --git a/engine/dist/include/yoyoengine/ecs/transform.h b/engine/dist/include/yoyoengine/ecs/transform.h index 09eb135..55c8482 100644 --- a/engine/dist/include/yoyoengine/ecs/transform.h +++ b/engine/dist/include/yoyoengine/ecs/transform.h @@ -27,6 +27,9 @@ struct ye_component_transform { float x; // the transform x position float y; // the transform y position + + // physics2 + float rotation; // clockwise rotation in degrees }; /** diff --git a/engine/dist/include/yoyoengine/lua_api.h b/engine/dist/include/yoyoengine/lua_api.h index 2649cbe..af2dc04 100644 --- a/engine/dist/include/yoyoengine/lua_api.h +++ b/engine/dist/include/yoyoengine/lua_api.h @@ -50,8 +50,8 @@ YE_API void ye_lua_renderer_register(lua_State *L); // Button YE_API void ye_lua_button_register(lua_State *L); -// Physics -YE_API void ye_lua_physics_register(lua_State *L); +// Rigidbody +YE_API void ye_lua_rigidbody_register(lua_State *L); // Collider YE_API void ye_lua_collider_register(lua_State *L); diff --git a/engine/dist/include/yoyoengine/tar_physics/rigidbody.h b/engine/dist/include/yoyoengine/tar_physics/rigidbody.h new file mode 100644 index 0000000..001a5da --- /dev/null +++ b/engine/dist/include/yoyoengine/tar_physics/rigidbody.h @@ -0,0 +1,40 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#ifndef YE_RIGIDBODY_H +#define YE_RIGIDBODY_H + +#include + +#include + +#include + +// TODO: gravity as a vec2 for flexibility (in physics main, not here) +// note: will be applying impulses directly to velocity + +struct ye_component_rigidbody { + bool active; /**< Controls whether system will act upon this component. */ + + // TODO: inverse mass for performance + float mass; /**< The mass of the rigidbody. */ + + float restitution; /**< The "bounciness" of the rigidbody. */ + + float kinematic_friction; /**< The friction of the rigidbody. */ + float rotational_kinematic_friction; /**< The rotational friction of the rigidbody. */ + + struct ye_vec2f velocity; /**< Velocity of entity */ + float rotational_velocity; /**< Rotational velocity of entity */ +}; + +// TODO: freeze axes? + +YE_API void ye_add_rigidbody_component(struct ye_entity *ent, float mass, float restitution, float kinematic_friction, float rotational_kinematic_friction); +YE_API void ye_remove_rigidbody_component(struct ye_entity *ent); + +#endif // YE_RIGIDBODY_H \ No newline at end of file diff --git a/engine/dist/include/yoyoengine/tar_physics/solver.h b/engine/dist/include/yoyoengine/tar_physics/solver.h new file mode 100644 index 0000000..4b7bc4f --- /dev/null +++ b/engine/dist/include/yoyoengine/tar_physics/solver.h @@ -0,0 +1,24 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#ifndef YE_SOLVER_H +#define YE_SOLVER_H + +#include + +#include + +#include + + +YE_API bool ye_detect_circle_circle_collision(struct ye_vec2f pos1, float radius1, struct ye_vec2f pos2, float radius2); + +YE_API bool ye_detect_circle_rect_collision(struct ye_vec2f circle_pos, float circle_radius, struct ye_rectf rect, float rotation); + +YE_API bool ye_detect_rect_rect_collision(struct ye_rectf rect1, float rotation1, struct ye_rectf rect2, float rotation2); + +#endif // YE_SOLVER_H \ No newline at end of file diff --git a/engine/dist/include/yoyoengine/tar_physics/tar.h b/engine/dist/include/yoyoengine/tar_physics/tar.h new file mode 100644 index 0000000..6a7e14e --- /dev/null +++ b/engine/dist/include/yoyoengine/tar_physics/tar.h @@ -0,0 +1,24 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +/* + PHYSICS SYSTEM V2: + A simple velocity (immediate impulse application) physics system. + + FUTURE WISHLIST: + - physics thread + - advanced paramters (drag, static friction, etc) +*/ + +#ifndef YE_TAR_H +#define YE_TAR_H + +#include + +YE_API void ye_physics_tick(float dt); + +#endif // YE_TAR_H \ No newline at end of file diff --git a/engine/dist/include/yoyoengine/types.h b/engine/dist/include/yoyoengine/types.h new file mode 100644 index 0000000..047d527 --- /dev/null +++ b/engine/dist/include/yoyoengine/types.h @@ -0,0 +1,25 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#ifndef YE_TYPES_H +#define YE_TYPES_H + +/** + * @brief A rectangle made up of floats. + */ +struct ye_rectf { + float x, y, w, h; +}; + +/** + * @brief A 2D vector made up of floats. + */ +struct ye_vec2f { + float x, y; +}; + +#endif // YE_TYPES_H \ No newline at end of file diff --git a/engine/dist/include/yoyoengine/utils.h b/engine/dist/include/yoyoengine/utils.h index 7b00b58..8ee5e40 100644 --- a/engine/dist/include/yoyoengine/utils.h +++ b/engine/dist/include/yoyoengine/utils.h @@ -1,5 +1,5 @@ /* - This file is a part of yoyoengine. (https://github.com/yoyoengine/yoyoengine) + This file is a part of yoyoengine. (https://github.com/yoyoengine) Copyright (C) 2023-2024 Ryan Zmuda Licensed under the MIT license. See LICENSE file in the project root for details. @@ -62,13 +62,6 @@ YE_API void ye_auto_fit_bounds(struct ye_rectf* bounds_f, struct ye_rectf* obj_f */ YE_API SDL_Rect ye_get_real_texture_size_rect(SDL_Texture *pTexture); -/** - * @brief A rectangle made up of floats. - */ -struct ye_rectf { - float x, y, w, h; -}; - /** * @brief Convert a floating rectangle to an integer rectangle. * @@ -91,7 +84,7 @@ YE_API struct ye_rectf ye_convert_rect_rectf(SDL_Rect rect); enum ye_component_type { YE_COMPONENT_TRANSFORM, YE_COMPONENT_RENDERER, - YE_COMPONENT_PHYSICS, + YE_COMPONENT_RIGIDBODY, YE_COMPONENT_COLLIDER, YE_COMPONENT_LUA_SCRIPT, YE_COMPONENT_AUDIOSOURCE, diff --git a/engine/dist/include/yoyoengine/yoyoengine.h b/engine/dist/include/yoyoengine/yoyoengine.h index 3d1884d..d28e9e8 100644 --- a/engine/dist/include/yoyoengine/yoyoengine.h +++ b/engine/dist/include/yoyoengine/yoyoengine.h @@ -39,17 +39,23 @@ #include "uthash/uthash.h" #include "cache.h" #include "ui.h" + +// ecs // #include "ecs/ecs.h" #include "ecs/button.h" #include "ecs/audiosource.h" #include "ecs/camera.h" -#include "ecs/ecs.h" -#include "ecs/physics.h" #include "ecs/renderer.h" #include "ecs/transform.h" #include "ecs/collider.h" #include "ecs/tag.h" #include "ecs/lua_script.h" + +// physics // +#include "tar_physics/tar.h" +#include "tar_physics/solver.h" +#include "tar_physics/rigidbody.h" + #include "utils.h" #include "timer.h" #include "audio.h" diff --git a/engine/src/console/commands/cmd_entlist.c b/engine/src/console/commands/cmd_entlist.c index 78e7479..e8aade4 100644 --- a/engine/src/console/commands/cmd_entlist.c +++ b/engine/src/console/commands/cmd_entlist.c @@ -31,7 +31,7 @@ void ye_cmd_entlist(int argc, const char **argv) { bool include_camera = false; bool include_lua_script = false; bool include_button = false; - bool include_physics = false; + bool include_rigidbody = false; bool include_collider = false; bool include_tag = false; bool include_audiosource = false; @@ -55,8 +55,8 @@ void ye_cmd_entlist(int argc, const char **argv) { include_lua_script = true; else if(strcmp(argv[i], "button") == 0) include_button = true; - else if(strcmp(argv[i], "physics") == 0) - include_physics = true; + else if(strcmp(argv[i], "rigidbody") == 0) + include_rigidbody = true; else if(strcmp(argv[i], "collider") == 0) include_collider = true; else if(strcmp(argv[i], "tag") == 0) @@ -65,7 +65,7 @@ void ye_cmd_entlist(int argc, const char **argv) { include_audiosource = true; else{ ye_logf(_YE_RESERVED_LL_SYSTEM, "Invalid -c specifier: %s\n", argv[i]); - ye_logf(_YE_RESERVED_LL_SYSTEM, "Valid specifiers: transform, renderer, camera, lua_script, button, physics, collider, tag, audiosource\n"); + ye_logf(_YE_RESERVED_LL_SYSTEM, "Valid specifiers: transform, renderer, camera, lua_script, button, rigidbody, collider, tag, audiosource\n"); return; } } else if(strcmp(argv[i], "-t") == 0 && i + 1 < argc) { @@ -98,7 +98,7 @@ void ye_cmd_entlist(int argc, const char **argv) { matches = false; if(include_button && !current->entity->button) matches = false; - if(include_physics && !current->entity->physics) + if(include_rigidbody && !current->entity->rigidbody) matches = false; if(include_collider && !current->entity->collider) matches = false; @@ -127,8 +127,8 @@ void ye_cmd_entlist(int argc, const char **argv) { ye_logf(_YE_RESERVED_LL_SYSTEM, " [Lua Script]\n"); if(current->entity->button) ye_logf(_YE_RESERVED_LL_SYSTEM, " [Button]\n"); - if(current->entity->physics) - ye_logf(_YE_RESERVED_LL_SYSTEM, " [Physics]\n"); + if(current->entity->rigidbody) + ye_logf(_YE_RESERVED_LL_SYSTEM, " [Rigidbody]\n"); if(current->entity->collider) ye_logf(_YE_RESERVED_LL_SYSTEM, " [Collider]\n"); if(current->entity->tag) diff --git a/engine/src/ecs/collider.c b/engine/src/ecs/collider.c index c069f79..e293cb1 100644 --- a/engine/src/ecs/collider.c +++ b/engine/src/ecs/collider.c @@ -9,20 +9,51 @@ #include #include -void ye_add_static_collider_component(struct ye_entity *entity, struct ye_rectf rect){ +///////////////////////// +// v2 // +///////////////////////// + +void ye_add_static_rect_collider_component(struct ye_entity *entity, float x, float y, float w, float h){ + struct ye_component_collider *collider = malloc(sizeof(struct ye_component_collider)); + collider->active = true; + + collider->is_trigger = false; + collider->type = YE_COLLIDER_RECT; + collider->x = x; + collider->y = y; + collider->width = w; + collider->height = h; + + entity->collider = collider; + ye_entity_list_add(&collider_list_head, entity); +} + +void ye_add_trigger_rect_collider_component(struct ye_entity *entity, float x, float y, float w, float h){ + ye_add_static_rect_collider_component(entity, x, y, w, h); + entity->collider->is_trigger = true; +} + +void ye_add_static_circle_collider_component(struct ye_entity *entity, float x, float y, float radius){ struct ye_component_collider *collider = malloc(sizeof(struct ye_component_collider)); collider->active = true; - collider->rect = rect; + collider->is_trigger = false; + collider->type = YE_COLLIDER_CIRCLE; + collider->x = x; + collider->y = y; + collider->radius = radius; + entity->collider = collider; ye_entity_list_add(&collider_list_head, entity); } -void ye_add_trigger_collider_component(struct ye_entity *entity, struct ye_rectf rect){ - ye_add_static_collider_component(entity, rect); +void ye_add_trigger_circle_collider_component(struct ye_entity *entity, float x, float y, float radius){ + ye_add_static_circle_collider_component(entity, x, y, radius); entity->collider->is_trigger = true; } +///////////////////////// + void ye_remove_collider_component(struct ye_entity *entity){ free(entity->collider); entity->collider = NULL; diff --git a/engine/src/ecs/ecs.c b/engine/src/ecs/ecs.c index d1c1c2f..c9c2b9d 100644 --- a/engine/src/ecs/ecs.c +++ b/engine/src/ecs/ecs.c @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -18,13 +20,14 @@ #include #include #include -#include #include #include #include #include #include +#include + // entity id counter (used to assign unique ids to entities) int eid = 0; @@ -114,12 +117,12 @@ struct ye_entity_node *entity_list_head; struct ye_entity_node *transform_list_head; struct ye_entity_node *renderer_list_head; struct ye_entity_node *camera_list_head; -struct ye_entity_node *physics_list_head; struct ye_entity_node *tag_list_head; struct ye_entity_node *collider_list_head; struct ye_entity_node *lua_script_list_head; struct ye_entity_node *audiosource_list_head; struct ye_entity_node *button_list_head; +struct ye_entity_node *rigidbody_list_head; struct ye_entity_node * ye_get_entity_list_head(){ return entity_list_head; @@ -141,10 +144,10 @@ struct ye_entity * ye_create_entity(){ entity->camera = NULL; entity->lua_script = NULL; entity->button = NULL; - entity->physics = NULL; entity->collider = NULL; entity->tag = NULL; entity->audiosource = NULL; + entity->rigidbody = NULL; // add the entity to the entity list ye_entity_list_add(&entity_list_head, entity); @@ -170,7 +173,7 @@ struct ye_entity * ye_create_entity_named(const char *name){ entity->camera = NULL; entity->lua_script = NULL; entity->button = NULL; - entity->physics = NULL; + entity->rigidbody = NULL; entity->collider = NULL; entity->tag = NULL; entity->audiosource = NULL; @@ -245,20 +248,27 @@ struct ye_entity * ye_duplicate_entity(struct ye_entity *entity){ new_entity->button->active = entity->button->active; new_entity->button->relative = entity->button->relative; } - if(entity->physics != NULL){ - ye_add_physics_component(new_entity, entity->physics->velocity.x, entity->physics->velocity.y); - new_entity->physics->active = entity->physics->active; - new_entity->physics->rotational_velocity = entity->physics->rotational_velocity; + if(entity->rigidbody != NULL){ + ye_add_rigidbody_component(new_entity, entity->rigidbody->mass, entity->rigidbody->restitution, entity->rigidbody->kinematic_friction, entity->rigidbody->rotational_kinematic_friction); + new_entity->rigidbody->active = entity->rigidbody->active; + new_entity->rigidbody->velocity = entity->rigidbody->velocity; + new_entity->rigidbody->rotational_velocity = entity->rigidbody->rotational_velocity; } if(entity->collider != NULL){ - if(entity->collider->is_trigger){ - ye_add_trigger_collider_component(new_entity, entity->collider->rect); + if(entity->collider->type == YE_COLLIDER_RECT){ + if(entity->collider->is_trigger) + ye_add_trigger_rect_collider_component(new_entity, entity->collider->x, entity->collider->y, entity->collider->width, entity->collider->height); + else + ye_add_static_rect_collider_component(new_entity, entity->collider->x, entity->collider->y, entity->collider->width, entity->collider->height); } - else{ - ye_add_static_collider_component(new_entity, entity->collider->rect); + else if(entity->collider->type == YE_COLLIDER_CIRCLE){ + if(entity->collider->is_trigger) + ye_add_trigger_circle_collider_component(new_entity, entity->collider->x, entity->collider->y, entity->collider->radius); + else + ye_add_static_circle_collider_component(new_entity, entity->collider->x, entity->collider->y, entity->collider->radius); } new_entity->collider->active = entity->collider->active; - new_entity->collider->is_trigger = entity->collider->is_trigger; + new_entity->collider->is_trigger = entity->collider->is_trigger; // redundant } if(entity->tag != NULL){ ye_add_tag_component(new_entity); @@ -303,7 +313,7 @@ void ye_destroy_entity(struct ye_entity * entity){ if(entity->transform != NULL) ye_remove_transform_component(entity); if(entity->renderer != NULL) ye_remove_renderer_component(entity); if(entity->camera != NULL) ye_remove_camera_component(entity); - if(entity->physics != NULL) ye_remove_physics_component(entity); + if(entity->rigidbody != NULL) ye_remove_rigidbody_component(entity); if(entity->tag != NULL) ye_remove_tag_component(entity); if(entity->lua_script != NULL) ye_remove_lua_script_component(entity); if(entity->button != NULL) ye_remove_button_component(entity); @@ -381,12 +391,12 @@ void ye_init_ecs(){ transform_list_head = ye_entity_list_create(); renderer_list_head = ye_entity_list_create(); camera_list_head = ye_entity_list_create(); - physics_list_head = ye_entity_list_create(); tag_list_head = ye_entity_list_create(); collider_list_head = ye_entity_list_create(); audiosource_list_head = ye_entity_list_create(); lua_script_list_head = ye_entity_list_create(); button_list_head = ye_entity_list_create(); + rigidbody_list_head = ye_entity_list_create(); ye_logf(info, "Initialized ECS\n"); } @@ -424,12 +434,12 @@ void ye_shutdown_ecs(){ ye_entity_list_destroy(&transform_list_head); ye_entity_list_destroy(&renderer_list_head); ye_entity_list_destroy(&camera_list_head); - ye_entity_list_destroy(&physics_list_head); ye_entity_list_destroy(&tag_list_head); ye_entity_list_destroy(&collider_list_head); ye_entity_list_destroy(&lua_script_list_head); ye_entity_list_destroy(&audiosource_list_head); ye_entity_list_destroy(&button_list_head); + ye_entity_list_destroy(&rigidbody_list_head); // take care of cleaning up any entity pointers that exist in global state YE_STATE.engine.target_camera = NULL; @@ -445,14 +455,14 @@ void ye_print_entities(){ int i = 0; while(current != NULL){ char b[100]; - snprintf(b, sizeof(b), "\"%s\" -> ID:%d Trn:%d Rdr:%d Cam:%d Btn:%d Scr:%d Phy:%d Col:%d Tag:%d Aud:%d\n", + snprintf(b, sizeof(b), "\"%s\" -> ID:%d Trn:%d Rdr:%d Cam:%d Btn:%d Scr:%d RB:%d Col:%d Tag:%d Aud:%d\n", current->entity->name, current->entity->id, current->entity->transform != NULL, current->entity->renderer != NULL, current->entity->camera != NULL, current->entity->button != NULL, current->entity->lua_script != NULL, - current->entity->physics != NULL, + current->entity->rigidbody != NULL, current->entity->collider != NULL, current->entity->tag != NULL, current->entity->audiosource != NULL diff --git a/engine/src/ecs/physics.c b/engine/src/ecs/physics.c deleted file mode 100644 index a8225b7..0000000 --- a/engine/src/ecs/physics.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - This file is a part of yoyoengine. (https://github.com/zoogies/yoyoengine) - Copyright (C) 2023 Ryan Zmuda - - Licensed under the MIT license. See LICENSE file in the project root for details. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - Physics component - - Holds some information about an entity's physics - - Velocity is in pixels per second -*/ -void ye_add_physics_component(struct ye_entity *entity, float velocity_x, float velocity_y){ - entity->physics = malloc(sizeof(struct ye_component_physics)); - entity->physics->active = true; - // entity->physics->mass = mass; - // entity->physics->drag = drag; - entity->physics->velocity.x = velocity_x; - entity->physics->velocity.y = velocity_y; - entity->physics->rotational_velocity = 0; // directly modified by pointer because not often used - // entity->physics->acceleration.x = acceleration_x; - // entity->physics->acceleration.y = acceleration_y; - - // add this entity to the physics component list - ye_entity_list_add(&physics_list_head, entity); - - // log that we added a physics and to what ID - // ye_logf(debug, "Added physics component to entity %d\n", entity->id); -} - -// this is such a hack, but if we remove a component mid physics calculations, we dont want to break the memory access -bool reset_physics_traversal = false; -struct ye_entity_node *current = NULL; - -void ye_remove_physics_component(struct ye_entity *entity){ - free(entity->physics); - entity->physics = NULL; - - // remove the entity from the physics component list - ye_entity_list_remove(&physics_list_head, entity); - - reset_physics_traversal = true; - current = physics_list_head; - - // log that we removed a physics and to what ID - // ye_logf(debug, "Removed physics component from entity %d\n", entity->id); -} - -bool ye_rectf_collision(struct ye_rectf rect1, struct ye_rectf rect2){ - // ye_logf(debug, "rect1: %f, %f, %f, %f\n", rect1.x, rect1.y, rect1.w, rect1.h); - if (rect1.x < rect2.x + rect2.w && - rect1.x + rect1.w > rect2.x && - rect1.y < rect2.y + rect2.h && - rect1.y + rect1.h > rect2.y) { - return true; - } - return false; -} - -/* - Physics system - - Acts upon the list of tracked entities with physics components and updates their - position based on their velocity and acceleration. Multiply by delta time to get - the actual change in position. (engine_runtime_state.frame_time). - - Currently, we will always do CCD (continuous collision detection) for all entities with physics. - In the future, a great optimization would be to only perform CCD for entities meeting certain velocity - thresholds, or if we are below a certain framerate. We could also expose a bool for CCD on the physics - component to allow more fine grained control, as well as an integer for specifying the number of steps. - - TODO/Considerations: - - maybe we want to check for hitting multiple overlapping triggers? - - trigger colliders are needed - - Physics entities need a transform component to work, we apply these forces to the transform not the component position. -*/ -void ye_system_physics(){ - // current time - left for debugging - // unsigned long start = SDL_GetTicks64(); - - float delta = ye_delta_time(); - // iterate over all entities with physics - current = physics_list_head; - while (current != NULL) { - // generic validity checks (if the entity is destroyed after a collision, we need to check) - if(current->entity == NULL || current->entity->physics == NULL){ - current = current->next; - continue; - } - - // if the current entity is inactive, dont run physics on it - if(!current->entity->active){ - current = current->next; - continue; - } - - // if we have a physics component and a transform component - if (current->entity->physics->active && current->entity->transform != NULL) { - // if we have velocity proceed with checks - if(current->entity->physics->velocity.x != 0 || current->entity->physics->velocity.y != 0){ - - // printf("started looking at entity with vx:%f vy:%f\n",current->entity->physics->velocity.x, current->entity->physics->velocity.y); - - /* - CASE THAT ENTITY HAS NO COLLIDER (or inactive one) - We just apply its velocity to its transform (also account for rotational) - */ - if(current->entity->collider == NULL || !current->entity->collider->active){ - current->entity->transform->x += current->entity->physics->velocity.x * delta; - current->entity->transform->y += current->entity->physics->velocity.y * delta; - - // do the rotation as well - if(current->entity->physics->rotational_velocity != 0 && current->entity->renderer != NULL){ - // update the entity's rotation based on its rotational velocity - current->entity->renderer->rotation += current->entity->physics->rotational_velocity * delta; - if(current->entity->renderer->rotation > 360) current->entity->renderer->rotation -= 360; - if(current->entity->renderer->rotation < 0) current->entity->renderer->rotation += 360; - } - - current = current->next; - continue; - } - - /* - CASE THAT ENTITY HAS A COLLIDER - We need to check if we are colliding with any other colliders (CCD) - */ - // get the current collider position - struct ye_rectf old_position = ye_get_position(current->entity,YE_COMPONENT_COLLIDER); - - // // if relative, mitigate the offset bias - // if(current->entity->collider->relative){ - // old_position.x += current->entity->collider->rect.x; - // old_position.y += current->entity->collider->rect.y; - // } - - struct ye_rectf new_position = old_position; - - // printf("original pos: %f,%f\n",old_position.x,old_position.y); - - // calculate the change in position based on the velocity - float dx = current->entity->physics->velocity.x * delta; - float dy = current->entity->physics->velocity.y * delta; - - // printf("calculated dx:%f dy:%f\n",dx,dy); - - // if this entity has a static collider, we need to check if we are colliding with any other static colliders - if(current->entity->collider && !current->entity->collider->is_trigger && current->entity->collider->active){ - for(int i = 0; i < YE_PHYSICS_SUBSTEPS; i++){ - float substep = (i + 1) / (float)YE_PHYSICS_SUBSTEPS; // Calculate sub-step factor - - // Calculate the interpolated position based on the sub-step - new_position.x = old_position.x + substep * dx; - new_position.y = old_position.y + substep * dy; - - // printf("tested substep at x:%f y:%f\n",new_position.x,new_position.y); - - // check for collisions by comparing this interpolated position with all other colliders - struct ye_entity_node *current_collider = collider_list_head; - while (current_collider != NULL) { - // if a collider is on a different entity and is active - if (current_collider->entity->id != current->entity->id && current_collider->entity->collider != NULL && current_collider->entity->collider->active) { - // check if we collide with it - if(ye_rectf_collision(new_position, ye_get_position(current_collider->entity,YE_COMPONENT_COLLIDER))){ - - /* - TODO: FIXME: PATCH - - This should actually reset to the smallest fitting substep we took - */ - if(!current_collider->entity->collider->is_trigger){ - new_position.x = old_position.x; - new_position.y = old_position.y; - } - - break; - } - } - current_collider = current_collider->next; - } - - // if we actually hit a collider this step, current_collider will not be NULL - if(current_collider != NULL && current_collider->entity->active && current_collider->entity->collider->active){ - // ye_logf(debug, "Hit collider on entity %d\n", current_collider->entity->id); - // if collider we touched is static - if(!current_collider->entity->collider->is_trigger){ - current->entity->physics->velocity.x = 0; - current->entity->physics->velocity.y = 0; - - ye_fire_event(YE_EVENT_COLLISION, (union ye_event_args){.collision = {current->entity, current_collider->entity}}); - - ye_lua_signal_collisions(current->entity,current_collider->entity); - - // check reset flag - if(reset_physics_traversal){ - current = physics_list_head; - break; - } - - // make sure entity hasn't been destroyed - if(current->entity == NULL){ - break; - } - - /* - Saving for later as it may be relevant to the future: - - // calculate the distance between the entity and the collider in both directions - int dx = current_collider->entity->collider->rect.x - (current->entity->transform->rect.w + current->entity->collider->rect.w) - current->entity->transform->rect.x; - int dy = current_collider->entity->collider->rect.y - (current->entity->transform->rect.h + current->entity->collider->rect.h) - current->entity->transform->rect.y; - - // move the entity in the direction with the smaller distance, unless already overlapping - if (abs(dx) < abs(dy) && dx != 0) { - current->entity->transform->rect.x += dx + current->entity->collider->rect.w; - } else if (abs(dy) < abs(dx) && dy != 0) { - current->entity->transform->rect.y += dy; - } - */ - - break; // break out of the substep loop - } - else{ - /* - If we hit a trigger collider, broadcast the two collision entities into the - event callback - */ - ye_fire_event(YE_EVENT_TRIGGER_ENTER, (union ye_event_args){.collision = {current->entity, current_collider->entity}}); - - ye_lua_signal_trigger_enter(current->entity,current_collider->entity); - - // check reset flag - if(reset_physics_traversal){ - current = physics_list_head; - break; - } - - // make sure entity hasn't been destroyed - if(current->entity == NULL){ - break; - } - } - } // TODO: do we want to cancel rotational velocity here too? - } - } - - if(reset_physics_traversal){ - current = physics_list_head; - reset_physics_traversal = false; - continue; - } - - /* - even if we havent changed our new position at all from the old, this line is still true. - We are changing whatever position this entity needs to be based on whatever substep max it hit or change it needs to be. - */ - current->entity->transform->x = new_position.x; // bug? relativity makes it so we need to do something diff because this is offset with relative from root transform? - current->entity->transform->y = new_position.y; - } - - // make sure entity is still real and valid - if(current->entity == NULL){ - current = current->next; - continue; - } - - // if we have rotational velocity apply it (if we have a renderer) - if(current->entity->physics->rotational_velocity != 0 && current->entity->renderer != NULL){ - // update the entity's rotation based on its rotational velocity - current->entity->renderer->rotation += current->entity->physics->rotational_velocity * delta; - if(current->entity->renderer->rotation > 360) current->entity->renderer->rotation -= 360; - if(current->entity->renderer->rotation < 0) current->entity->renderer->rotation += 360; - } - } - current = current->next; - } - // printf("Physics system took %lu ms\n", SDL_GetTicks64() - start); -} \ No newline at end of file diff --git a/engine/src/ecs/renderer.c b/engine/src/ecs/renderer.c index 5cdc782..6c3b751 100644 --- a/engine/src/ecs/renderer.c +++ b/engine/src/ecs/renderer.c @@ -18,10 +18,13 @@ #include #include #include +#include #include #include #include +#include + void ye_update_renderer_component(struct ye_entity *entity){ /*The purpose of this function is to be invoked when we know we have changed some internal variables of the renderer, and need to recompute the outputted texture*/ switch(entity->renderer->type){ @@ -578,6 +581,12 @@ void ye_system_renderer(SDL_Renderer *renderer) { // entity rect is now a reflection of the actual calculated rect + // get rotation factoring in the transform + float actual_rotation = current->entity->renderer->rotation; + if(current->entity->transform) + actual_rotation = current->entity->transform->rotation + current->entity->renderer->rotation; + + /* TODO: HACK FOR ACEROLA JAM ZERO, THERE IS A BETTER WAY TO FORMULAICALLY DETERMINE THIS OFFSET TO CUT DOWN ON RENDERING COSTS @@ -587,7 +596,7 @@ void ye_system_renderer(SDL_Renderer *renderer) { */ int occlusion_offset_x = 0; int occlusion_offset_y = 0; - if(current->entity->renderer->rotation != 0){ + if(actual_rotation != 0){ occlusion_offset_x = YE_STATE.engine.target_camera->camera->view_field.w; occlusion_offset_y = YE_STATE.engine.target_camera->camera->view_field.h; // occlusion_offset_x = 1920; @@ -663,10 +672,10 @@ void ye_system_renderer(SDL_Renderer *renderer) { else if(current->entity->renderer->flipped_y){ flip = SDL_FLIP_VERTICAL; } - SDL_RenderCopyEx(renderer, current->entity->renderer->texture, ent_src_rect, &entity_rect, (int)current->entity->renderer->rotation, NULL, flip); + SDL_RenderCopyEx(renderer, current->entity->renderer->texture, ent_src_rect, &entity_rect, actual_rotation, NULL, flip); } - else if(current->entity->renderer->rotation != 0.0){ - SDL_RenderCopyEx(renderer, current->entity->renderer->texture, ent_src_rect, &entity_rect, (int)current->entity->renderer->rotation, ¤t->entity->renderer->center, SDL_FLIP_NONE); + else if(actual_rotation != 0.0){ + SDL_RenderCopyEx(renderer, current->entity->renderer->texture, ent_src_rect, &entity_rect, actual_rotation, ¤t->entity->renderer->center, SDL_FLIP_NONE); } else{ SDL_RenderCopy(renderer, current->entity->renderer->texture, ent_src_rect, &entity_rect); diff --git a/engine/src/engine.c b/engine/src/engine.c index 2169905..2e882d8 100644 --- a/engine/src/engine.c +++ b/engine/src/engine.c @@ -36,13 +36,14 @@ #include #include #include -#include #include #include #include #include #include +#include + // buffer to hold filepath strings // will be modified by getPath() char path_buffer[1024]; @@ -140,7 +141,7 @@ void ye_process_frame(){ int physics_time = SDL_GetTicks64(); if(!YE_STATE.editor.editor_mode){ // update physics - ye_system_physics(); // TODO: decouple from framerate + ye_physics_tick(YE_STATE.runtime.delta_time); // TODO: decouple from framerate } YE_STATE.runtime.physics_time = SDL_GetTicks64() - physics_time; diff --git a/engine/src/scene.c b/engine/src/scene.c index 9dab73f..4b5c718 100644 --- a/engine/src/scene.c +++ b/engine/src/scene.c @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -23,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +32,8 @@ #include #include +#include + void ye_init_scene_manager(){ YE_STATE.runtime.scene_name = NULL; @@ -74,15 +77,26 @@ struct ye_rectf ye_retrieve_position(json_t *parent){ } void ye_construct_transform(struct ye_entity* e, json_t* transform, const char* entity_name) { - int x, y; - if(!ye_json_int(transform,"x",&x) || !ye_json_int(transform,"y",&y)) { - ye_logf(warning,"Entity %s has a transform component with invalid position field\n", entity_name); + float x, y, rotation; + if(!ye_json_float(transform,"x",&x)) { + ye_logf(warning,"Entity %s has a transform component, but it is missing the x field\n", entity_name); x = 0; + } + if(!ye_json_float(transform,"y",&y)) { + ye_logf(warning,"Entity %s has a transform component, but it is missing the y field\n", entity_name); y = 0; } + if(!ye_json_float(transform,"rotation",&rotation)) { + ye_logf(warning,"Entity %s has a transform component, but it is missing the rotation field\n", entity_name); + rotation = 0; + } // construct transform component ye_add_transform_component(e,x,y); + if(e->transform == NULL) return; + + // update rotation + e->transform->rotation = rotation; } void ye_construct_camera(struct ye_entity* e, json_t* camera, const char* entity_name){ @@ -361,34 +375,48 @@ void ye_construct_renderer(struct ye_entity* e, json_t* renderer, const char* en } } -void ye_construct_physics(struct ye_entity* e, json_t* physics, const char* entity_name){ - // get velocity - if(ye_json_has_key(physics,"velocity")){ - json_t *velocity = NULL; - if(!ye_json_object(physics,"velocity",&velocity)) { - ye_logf(warning,"Entity \"%s\" has a physics component, but it is missing the velocity field\n", entity_name); - } else { - float x,y; - if(!ye_json_float(velocity,"x",&x) || !ye_json_float(velocity,"y",&y)) { - ye_logf(warning,"Entity %s has a physics component with invalid velocity field\n", entity_name); - } else { - ye_add_physics_component(e,x,y); - } - } +void ye_construct_rigidbody(struct ye_entity* e, json_t* rigidbody, const char* entity_name){ + float mass; + if(!ye_json_float(rigidbody,"mass",&mass)) { + ye_logf(warning,"Entity %s has a rigidbody component, but it is missing the mass field\n", entity_name); + return; } - if(e->physics == NULL) return; + float restitution; + if(!ye_json_float(rigidbody,"restitution",&restitution)) { + ye_logf(warning,"Entity %s has a rigidbody component, but it is missing the restitution field\n", entity_name); + return; + } - // get rotational velocity - if(ye_json_has_key(physics,"rotational velocity")){ - float rotational_velocity = 0; ye_json_float(physics,"rotational velocity",&rotational_velocity); - e->physics->rotational_velocity = rotational_velocity; + float kinematic_friction; + if(!ye_json_float(rigidbody,"kinematic_friction",&kinematic_friction)) { + ye_logf(warning,"Entity %s has a rigidbody component, but it is missing the kinematic friction field\n", entity_name); + return; } - // update active state - if(ye_json_has_key(physics,"active")){ - bool active = true; ye_json_bool(physics,"active",&active); - e->physics->active = active; + float rotational_kinematic_friction; + if(!ye_json_float(rigidbody,"rotational_kinematic_friction",&rotational_kinematic_friction)) { + ye_logf(warning,"Entity %s has a rigidbody component, but it is missing the rotational kinematic friction field\n", entity_name); + return; + } + + // add the rigidbody component + ye_add_rigidbody_component(e,mass,restitution,kinematic_friction,rotational_kinematic_friction); + + float vx = 0; + float vy = 0; + float vr = 0; + if(!ye_json_float(rigidbody,"vx",&vx) || !ye_json_float(rigidbody,"vy",&vy) || !ye_json_float(rigidbody,"vr",&vr)) { + ye_logf(warning,"Entity %s has a rigidbody component with invalid velocity field\n", entity_name); + } else { + e->rigidbody->velocity = (struct ye_vec2f){vx,vy}; + e->rigidbody->rotational_velocity = vr; + } + + // update the active state + if(ye_json_has_key(rigidbody,"active")){ + bool active = true; ye_json_bool(rigidbody,"active",&active); + e->rigidbody->active = active; } } @@ -416,8 +444,19 @@ void ye_construct_tag(struct ye_entity* e, json_t* tag){ } void ye_construct_collider(struct ye_entity* e, json_t* collider, const char* entity_name){ - // validate bounds field - struct ye_rectf b = ye_retrieve_position(collider); + // common + float x,y; + if(!ye_json_float(collider,"x",&x) || !ye_json_float(collider,"y",&y)) { + ye_logf(warning,"Entity %s has a collider component, but it is missing the x or y field\n", entity_name); + return; + } + + // get the type + enum ye_collider_type type; + if(!ye_json_int(collider,"type",(int*)&type)) { + ye_logf(warning,"Entity %s has a collider component, but it is missing the type field\n", entity_name); + return; + } // validate is_trigger field bool is_trigger; @@ -426,11 +465,45 @@ void ye_construct_collider(struct ye_entity* e, json_t* collider, const char* en is_trigger = false; } - // add the collider component - if(!is_trigger) - ye_add_static_collider_component(e,b); - else - ye_add_trigger_collider_component(e,b); + json_t *impl = NULL; + if(!ye_json_object(collider,"impl",&impl)) { + ye_logf(warning,"Entity %s has a collider component, but it is missing the impl field\n", entity_name); + return; + } + + switch(type) { + case YE_COLLIDER_RECT: + // validate the w,h fields + float w,h; + if(!ye_json_float(impl,"w",&w) || !ye_json_float(impl,"h",&h)) { + ye_logf(warning,"Entity %s has a collider component, but it is missing the w or h field\n", entity_name); + return; + } + + if(is_trigger) + ye_add_trigger_rect_collider_component(e,x,y,w,h); + else + ye_add_static_rect_collider_component(e,x,y,w,h); + + break; + case YE_COLLIDER_CIRCLE: + // validate the radius field + float radius; + if(!ye_json_float(impl,"radius",&radius)) { + ye_logf(warning,"Entity %s has a collider component, but it is missing the radius field\n", entity_name); + return; + } + + if(is_trigger) + ye_add_trigger_circle_collider_component(e,x,y,radius); + else + ye_add_static_circle_collider_component(e,x,y,radius); + + break; + default: + ye_logf(warning,"Entity %s has a collider component, but it has an invalid type field\n", entity_name); + return; + } if(e->collider == NULL) return; @@ -667,14 +740,14 @@ void ye_construct_scene(json_t *entities){ ye_construct_renderer(e,renderer,entity_name); } - // if we have a physics component on our entity - if(ye_json_has_key(components,"physics")){ - json_t *physics = NULL; ye_json_object(components,"physics",&physics); - if(physics == NULL){ - ye_logf(warning,"Entity %s has a physics field, but it's invalid.\n", entity_name); + // if we have a rigidbody component on our entity + if(ye_json_has_key(components,"rigidbody")){ + json_t *rigidbody = NULL; ye_json_object(components,"rigidbody",&rigidbody); + if(rigidbody == NULL){ + ye_logf(warning,"Entity %s has a rigidbody field, but it's invalid.\n", entity_name); continue; } - ye_construct_physics(e,physics,entity_name); + ye_construct_rigidbody(e,rigidbody,entity_name); } // if we have a tag component on our entity diff --git a/engine/src/scripting/lua_api.c b/engine/src/scripting/lua_api.c index 472308f..a432239 100644 --- a/engine/src/scripting/lua_api.c +++ b/engine/src/scripting/lua_api.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -69,7 +69,7 @@ int ye_lua_check_component_exists(lua_State* L){ entity->camera, entity->lua_script, entity->button, - entity->physics, + entity->rigidbody, entity->collider, entity->tag, entity->audiosource @@ -141,7 +141,7 @@ int ye_lua_remove_component(lua_State* L){ ye_remove_button_component(entity); break; case 5: - ye_remove_physics_component(entity); + ye_remove_rigidbody_component(entity); break; case 6: ye_remove_collider_component(entity); @@ -206,9 +206,9 @@ void ye_register_lua_scripting_api(lua_State *state){ ye_lua_collider_register(state); /* - Physics + Rigidbody */ - ye_lua_physics_register(state); + ye_lua_rigidbody_register(state); /* LuaScript diff --git a/engine/src/scripting/lua_collider.c b/engine/src/scripting/lua_collider.c index 2e2861e..82ff0bd 100644 --- a/engine/src/scripting/lua_collider.c +++ b/engine/src/scripting/lua_collider.c @@ -21,14 +21,15 @@ int ye_lua_create_static_collider(lua_State *L) { return 0; } - struct ye_rectf rect = { - .x = luaL_checknumber(L, 2), - .y = luaL_checknumber(L, 3), - .w = luaL_checknumber(L, 4), - .h = luaL_checknumber(L, 5) - }; + // struct ye_rectf rect = { + // .x = luaL_checknumber(L, 2), + // .y = luaL_checknumber(L, 3), + // .w = luaL_checknumber(L, 4), + // .h = luaL_checknumber(L, 5) + // }; - ye_add_static_collider_component(ent, rect); + // ye_add_static_collider_component(ent, rect); + // TODO: COLLIDER REWORK return 0; } @@ -41,14 +42,15 @@ int ye_lua_create_trigger_collider(lua_State *L) { return 0; } - struct ye_rectf rect = { - .x = luaL_checknumber(L, 2), - .y = luaL_checknumber(L, 3), - .w = luaL_checknumber(L, 4), - .h = luaL_checknumber(L, 5) - }; + // struct ye_rectf rect = { + // .x = luaL_checknumber(L, 2), + // .y = luaL_checknumber(L, 3), + // .w = luaL_checknumber(L, 4), + // .h = luaL_checknumber(L, 5) + // }; - ye_add_trigger_collider_component(ent, rect); + // ye_add_trigger_collider_component(ent, rect); + // TODO: COLLIDER REWORK return 0; } @@ -63,10 +65,11 @@ int ye_lua_collider_query(lua_State *L){ lua_pushboolean(L, ent->collider->active); lua_pushboolean(L, ent->collider->relative); - lua_pushnumber(L, ent->collider->rect.x); - lua_pushnumber(L, ent->collider->rect.y); - lua_pushnumber(L, ent->collider->rect.w); - lua_pushnumber(L, ent->collider->rect.h); + // TODO: COLLIDER REWORK + // lua_pushnumber(L, ent->collider->rect.x); + // lua_pushnumber(L, ent->collider->rect.y); + // lua_pushnumber(L, ent->collider->rect.w); + // lua_pushnumber(L, ent->collider->rect.h); lua_pushboolean(L, ent->collider->is_trigger); return 7; @@ -88,21 +91,23 @@ int ye_lua_collider_modify(lua_State *L){ ent->collider->relative = lua_toboolean(L, 3); } - if(lua_isnumber(L, 4)){ - ent->collider->rect.x = lua_tonumber(L, 4); - } + // if(lua_isnumber(L, 4)){ + // ent->collider->rect.x = lua_tonumber(L, 4); + // } - if(lua_isnumber(L, 5)){ - ent->collider->rect.y = lua_tonumber(L, 5); - } + // if(lua_isnumber(L, 5)){ + // ent->collider->rect.y = lua_tonumber(L, 5); + // } - if(lua_isnumber(L, 6)){ - ent->collider->rect.w = lua_tonumber(L, 6); - } + // if(lua_isnumber(L, 6)){ + // ent->collider->rect.w = lua_tonumber(L, 6); + // } - if(lua_isnumber(L, 7)){ - ent->collider->rect.h = lua_tonumber(L, 7); - } + // if(lua_isnumber(L, 7)){ + // ent->collider->rect.h = lua_tonumber(L, 7); + // } + + // TODO: COLLIDER REWORK if(lua_isboolean(L, 8)){ ent->collider->is_trigger = lua_toboolean(L, 8); diff --git a/engine/src/scripting/lua_physics.c b/engine/src/scripting/lua_physics.c deleted file mode 100644 index 6a1a177..0000000 --- a/engine/src/scripting/lua_physics.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - This file is a part of yoyoengine. (https://github.com/zoogies/yoyoengine) - Copyright (C) 2024 Ryan Zmuda - - Licensed under the MIT license. See LICENSE file in the project root for details. -*/ - -#include - -#include -#include -#include - - - -int ye_lua_create_physics_component(lua_State *L) { - struct ye_entity * ent = lua_touserdata(L, 1); - - if(ent == NULL) { - ye_logf(error, "could not create physics component: entity is null\n"); - return 0; - } - - float x = luaL_checknumber(L, 2); - float y = luaL_checknumber(L, 3); - - ye_add_physics_component(ent, x, y); - - return 0; -} - -int ye_lua_physics_query(lua_State *L){ - struct ye_entity * ent = lua_touserdata(L, 1); - - if(ent == NULL) { - ye_logf(error, "could not query physics component: entity is null\n"); - return 0; - } - - lua_pushboolean(L, ent->physics->active); - lua_pushnumber(L, ent->physics->velocity.x); - lua_pushnumber(L, ent->physics->velocity.y); - lua_pushnumber(L, ent->physics->rotational_velocity); - - return 4; -} - -int ye_lua_physics_modify(lua_State *L){ - struct ye_entity * ent = lua_touserdata(L, 1); - - if(ent == NULL) { - ye_logf(error, "could not modify physics component: entity is null\n"); - return 0; - } - - if(lua_isboolean(L, 2)){ - ent->physics->active = lua_toboolean(L, 2); - } - - if(lua_isnumber(L, 3)){ - ent->physics->velocity.x = luaL_checknumber(L, 3); - } - - if(lua_isnumber(L, 4)){ - ent->physics->velocity.y = luaL_checknumber(L, 4); - } - - if(lua_isnumber(L, 5)){ - ent->physics->rotational_velocity = luaL_checknumber(L, 5); - } - - return 0; -} - - - -void ye_lua_physics_register(lua_State *L) { - // init - lua_register(L, "ye_lua_create_physics_component", ye_lua_create_physics_component); - - // query - lua_register(L, "ye_lua_physics_query", ye_lua_physics_query); - - // modify - lua_register(L, "ye_lua_physics_modify", ye_lua_physics_modify); -} \ No newline at end of file diff --git a/engine/src/scripting/lua_rigidbody.c b/engine/src/scripting/lua_rigidbody.c new file mode 100644 index 0000000..c93054a --- /dev/null +++ b/engine/src/scripting/lua_rigidbody.c @@ -0,0 +1,74 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#include + +#include +#include +#include + + + +int ye_lua_create_rigidbody_component(lua_State *L) { + struct ye_entity * ent = lua_touserdata(L, 1); + + if(ent == NULL) { + ye_logf(error, "could not create rigidbody component: entity is null\n"); + return 0; + } + + // float x = luaL_checknumber(L, 2); + // float y = luaL_checknumber(L, 3); + + // ye_add_rigidbody_component(ent, x, y); + + // TODO: RIGIDBODY + + return 0; +} + +int ye_lua_rigidbody_query(lua_State *L){ + struct ye_entity * ent = lua_touserdata(L, 1); + + if(ent == NULL) { + ye_logf(error, "could not query rigidbody component: entity is null\n"); + return 0; + } + + lua_pushboolean(L, ent->rigidbody->active); + lua_pushnumber(L, ent->rigidbody->velocity.x); + lua_pushnumber(L, ent->rigidbody->velocity.y); + lua_pushnumber(L, ent->rigidbody->rotational_velocity); + + return 4; +} + +int ye_lua_rigidbody_modify(lua_State *L){ + struct ye_entity * ent = lua_touserdata(L, 1); + + if(ent == NULL) { + ye_logf(error, "could not modify rigidbody component: entity is null\n"); + return 0; + } + + // TODO RIGIDBODY + + return 0; +} + + + +void ye_lua_rigidbody_register(lua_State *L) { + // init + lua_register(L, "ye_lua_create_rigidbody_component", ye_lua_create_rigidbody_component); + + // query + lua_register(L, "ye_lua_rigidbody_query", ye_lua_rigidbody_query); + + // modify + lua_register(L, "ye_lua_rigidbody_modify", ye_lua_rigidbody_modify); +} \ No newline at end of file diff --git a/engine/src/tar_physics/rigidbody.c b/engine/src/tar_physics/rigidbody.c new file mode 100644 index 0000000..e852217 --- /dev/null +++ b/engine/src/tar_physics/rigidbody.c @@ -0,0 +1,62 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#include + +#include +#include +#include + +//////////////////////////////////////////////// + +#include // NULL + +/* + ECS Stuff +*/ +void ye_add_rigidbody_component(struct ye_entity *ent, float mass, float restitution, float kinematic_friction, float rotational_kinematic_friction) { + if(!ent) { + ye_logf(YE_LL_ERROR, "ye_add_rigidbody_component: entity is NULL"); + return; + } + + if(ent->rigidbody) { + ye_logf(YE_LL_WARNING, "ye_add_rigidbody_component: entity already has a rigidbody component"); + return; + } + + struct ye_component_rigidbody *rb = malloc(sizeof(struct ye_component_rigidbody)); + rb->active = true; + rb->mass = mass; + rb->restitution = restitution; + rb->kinematic_friction = kinematic_friction; + rb->rotational_kinematic_friction = rotational_kinematic_friction; + rb->velocity.x = 0.0f; + rb->velocity.y = 0.0f; + rb->rotational_velocity = 0.0f; + + ent->rigidbody = rb; + ye_entity_list_add(&rigidbody_list_head, ent); +} + +void ye_remove_rigidbody_component(struct ye_entity *ent) { + if(!ent) { + ye_logf(YE_LL_ERROR, "ye_remove_rigidbody_component: entity is NULL"); + return; + } + + if(!ent->rigidbody) { + ye_logf(YE_LL_WARNING, "ye_remove_rigidbody_component: entity does not have a rigidbody component"); + return; + } + + free(ent->rigidbody); + ent->rigidbody = NULL; + ye_entity_list_remove(&rigidbody_list_head, ent); +} + +//////////////////////////////////////////////// \ No newline at end of file diff --git a/engine/src/tar_physics/solver.c b/engine/src/tar_physics/solver.c new file mode 100644 index 0000000..4a52256 --- /dev/null +++ b/engine/src/tar_physics/solver.c @@ -0,0 +1,129 @@ +// /* +// This file is a part of yoyoengine. (https://github.com/yoyoengine) +// Copyright (C) 2023-2024 Ryan Zmuda + +// Licensed under the MIT license. See LICENSE file in the project root for details. +// */ + +// ///////////////////////////////////// + +// #include +// #include + +// #include +// #include + +// // detection + +// bool ye_detect_circle_circle_collision(struct ye_vec2f pos1, float radius1, struct ye_vec2f pos2, float radius2){ +// float dx = pos1.x - pos2.x; +// float dy = pos1.y - pos2.y; +// float distance = sqrt(dx * dx + dy * dy); +// return distance < radius1 + radius2; +// } + +// bool ye_detect_circle_rect_collision(struct ye_vec2f circle_pos, float circle_radius, struct ye_rectf rect, float rotation) { +// // 1. Translate circle to rectangle's local space +// float rect_center_x = rect.x + rect.w / 2; +// float rect_center_y = rect.y + rect.h / 2; + +// float sin_rot = sinf(-rotation); // Negate to reverse rotation +// float cos_rot = cosf(-rotation); + +// // Circle's position relative to rectangle center +// float local_circle_x = cos_rot * (circle_pos.x - rect_center_x) - sin_rot * (circle_pos.y - rect_center_y); +// float local_circle_y = sin_rot * (circle_pos.x - rect_center_x) + cos_rot * (circle_pos.y - rect_center_y); + +// // 2. Treat rectangle as axis-aligned in local space +// float dx = fabs(local_circle_x); +// float dy = fabs(local_circle_y); + +// if (dx > rect.w / 2 + circle_radius) return false; // Circle too far in X +// if (dy > rect.h / 2 + circle_radius) return false; // Circle too far in Y + +// if (dx <= rect.w / 2) return true; // Circle overlaps rectangle horizontally +// if (dy <= rect.h / 2) return true; // Circle overlaps rectangle vertically + +// // 3. Check corner distance +// float corner_distance = (dx - rect.w / 2) * (dx - rect.w / 2) + +// (dy - rect.h / 2) * (dy - rect.h / 2); + +// return corner_distance <= circle_radius * circle_radius; +// } + +// // helpers +// void ye_get_rect_corners(struct ye_rectf rect, float cos_rot, float sin_rot, struct ye_vec2f* corners) { +// float hw = rect.w / 2; // Half width +// float hh = rect.h / 2; // Half height + +// // Local corner offsets +// struct ye_vec2f offsets[4] = { +// { -hw, -hh }, { hw, -hh }, { hw, hh }, { -hw, hh } +// }; + +// // Rotate and translate corners +// for (int i = 0; i < 4; i++) { +// float rotated_x = offsets[i].x * cos_rot - offsets[i].y * sin_rot; +// float rotated_y = offsets[i].x * sin_rot + offsets[i].y * cos_rot; +// corners[i].x = rect.x + rotated_x; +// corners[i].y = rect.y + rotated_y; +// } +// } + +// struct ye_vec2f ye_get_normal(struct ye_vec2f point1, struct ye_vec2f point2) { +// struct ye_vec2f edge = { point2.x - point1.x, point2.y - point1.y }; +// return (struct ye_vec2f){ -edge.y, edge.x }; // Perpendicular vector +// } + +// void ye_project_onto_axis(struct ye_vec2f* corners, int count, struct ye_vec2f axis, float* min, float* max) { +// *min = *max = (corners[0].x * axis.x + corners[0].y * axis.y); // Dot product + +// for (int i = 1; i < count; i++) { +// float projection = (corners[i].x * axis.x + corners[i].y * axis.y); +// if (projection < *min) *min = projection; +// if (projection > *max) *max = projection; +// } +// } + +// bool ye_overlap_on_axis(struct ye_vec2f* corners1, struct ye_vec2f* corners2, struct ye_vec2f axis) { +// // Project corners of both rectangles onto the axis +// float min1, max1, min2, max2; +// ye_project_onto_axis(corners1, 4, axis, &min1, &max1); +// ye_project_onto_axis(corners2, 4, axis, &min2, &max2); + +// // Check for overlap +// return !(max1 < min2 || max2 < min1); // Return false if no overlap +// } + +// bool ye_detect_rect_rect_collision(struct ye_rectf rect1, float rotation1, struct ye_rectf rect2, float rotation2) { +// // Step 1: Precompute cosine and sine for both rectangles +// float cos1 = cosf(rotation1), sin1 = sinf(rotation1); +// float cos2 = cosf(rotation2), sin2 = sinf(rotation2); + +// // Step 2: Get the four corners of both rectangles +// struct ye_vec2f corners1[4], corners2[4]; +// ye_get_rect_corners(rect1, cos1, sin1, corners1); +// ye_get_rect_corners(rect2, cos2, sin2, corners2); + +// // Step 3: Define the axes to test +// struct ye_vec2f axes[4] = { +// ye_get_normal(corners1[1], corners1[0]), // Axis 1 from rect1 +// ye_get_normal(corners1[3], corners1[0]), // Axis 2 from rect1 +// ye_get_normal(corners2[1], corners2[0]), // Axis 1 from rect2 +// ye_get_normal(corners2[3], corners2[0]) // Axis 2 from rect2 +// }; + +// // Step 4: Check for overlaps on all axes +// for (int i = 0; i < 4; i++) { +// if (!ye_overlap_on_axis(corners1, corners2, axes[i])) { +// return false; // Separation found, no collision +// } +// } + +// // Step 5: If no separation axis, collision detected +// return true; +// } + +// ///////////////////////////////////// + +// NOTE: DO IT YOURSELF NO CHATGPT ORACLE SHIT! \ No newline at end of file diff --git a/engine/src/tar_physics/tar.c b/engine/src/tar_physics/tar.c new file mode 100644 index 0000000..b94cdce --- /dev/null +++ b/engine/src/tar_physics/tar.c @@ -0,0 +1,36 @@ +/* + This file is a part of yoyoengine. (https://github.com/yoyoengine) + Copyright (C) 2023-2024 Ryan Zmuda + + Licensed under the MIT license. See LICENSE file in the project root for details. +*/ + +#include + +#include + +#include +#include + +#include + +void ye_physics_tick(float dt) { + struct ye_entity_node *node = rigidbody_list_head; + while(node) { + struct ye_entity *entity = node->entity; + if(entity->rigidbody) { + if(entity->rigidbody->active) { + // TODO: apply gravity + + entity->transform->x += entity->rigidbody->velocity.x * dt; + entity->transform->y += entity->rigidbody->velocity.y * dt; + entity->transform->rotation += entity->rigidbody->rotational_velocity * dt; + + // TODO: collision detection + + // TODO: collision solver + } + } + node = node->next; + } +} \ No newline at end of file diff --git a/engine/src/utils.c b/engine/src/utils.c index 1a93f22..49cc93d 100644 --- a/engine/src/utils.c +++ b/engine/src/utils.c @@ -21,6 +21,8 @@ #include #include +#include + void ye_auto_fit_bounds(struct ye_rectf* bounds_f, struct ye_rectf* obj_f, enum ye_alignment alignment, SDL_Point* center, bool should_grow_to_fit){ SDL_Rect _bounds = ye_convert_rectf_rect(*bounds_f); SDL_Rect _obj = ye_convert_rectf_rect(*obj_f); @@ -190,9 +192,18 @@ struct ye_rectf ye_get_position(struct ye_entity *entity, enum ye_component_type case YE_COMPONENT_COLLIDER: if(entity->collider != NULL){ // set x,y,w,h - pos = entity->collider->rect; - pos.w = entity->collider->rect.w; - pos.h = entity->collider->rect.h; + pos.x = entity->collider->x; + pos.y = entity->collider->y; + + if(entity->collider->type == YE_COLLIDER_RECT){ + pos.w = entity->collider->width; + pos.h = entity->collider->height; + } else if(entity->collider->type == YE_COLLIDER_CIRCLE){ + pos.x -= entity->collider->radius; + pos.y -= entity->collider->radius; + pos.w = entity->collider->radius * 2; + pos.h = entity->collider->radius * 2; + } // printf("UTIL: found pos at x:%f y:%f w:%f h:%f\n",pos.x,pos.y,pos.w,pos.h); @@ -351,8 +362,8 @@ bool ye_component_exists(struct ye_entity *entity, enum ye_component_type type){ return entity->renderer != NULL; case YE_COMPONENT_CAMERA: return entity->camera != NULL; - case YE_COMPONENT_PHYSICS: - return entity->physics != NULL; + case YE_COMPONENT_RIGIDBODY: + return entity->rigidbody != NULL; case YE_COMPONENT_TAG: return entity->tag != NULL; case YE_COMPONENT_COLLIDER: