#include "physics_world.h" #include "debug.h" #include "collision.h" #include "rigidbody.h" #include "tilemap.h" static PhysicsEntity* _world_bodies = NULL; static size_t _world_bodies_cap = 0; static size_t _world_bodies_len = 0; static Tilemap** _world_maps = NULL; static size_t _world_maps_cap = 0; static size_t _world_maps_len = 0; void physics_world_init() { _world_bodies = malloc(8 * sizeof(PhysicsEntity)); _world_bodies_cap = 8; _world_bodies_len = 0; _world_maps = malloc(4 * (sizeof(Tilemap*))); _world_maps_cap = 4; _world_maps_len = 0; } void physics_world_clean() { free(_world_bodies); _world_bodies_cap = 0; _world_bodies_len = 0; free(_world_maps); _world_maps_cap = 0; _world_maps_len = 0; } void _internal_physics_world_resize_entities(size_t minimum) { if(minimum < _world_bodies_cap) { return; } size_t amount = _world_bodies_cap; while(amount < minimum) { amount *= 2; } PhysicsEntity* new = realloc(_world_bodies, amount * sizeof(PhysicsEntity)); ASSERT_RETURN(new == NULL, , "Failed to grow world bodies array"); _world_bodies = new; _world_bodies_cap = amount; } void physics_world_add_entity(PhysicsEntity entity) { _internal_physics_world_resize_entities(_world_bodies_len + 1); _world_bodies[_world_bodies_len] = entity; ++_world_bodies_len; } static void _internal_physics_world_remove_entity(PhysicsEntity* at) { PhysicsEntity* from = at + 1; memmove(at, from, (from - _world_bodies + _world_bodies_len) * sizeof(PhysicsEntity)); --_world_bodies_len; } void physics_world_remove_entity(PhysicsEntity entity) { PhysicsEntity* end = _world_bodies + _world_bodies_len; for(PhysicsEntity* itr = _world_bodies; itr < end; ++itr) { if(itr->data == entity.data) { _internal_physics_world_remove_entity(itr); return; } } ASSERT_RETURN(0,, "Physics entity with data at %p is not registered in physics world", entity.data); } static inline void _internal_physics_world_resize_maps(size_t minimum) { if(minimum < _world_maps_cap) { return; } size_t amount = _world_maps_cap; while(amount < minimum) { amount *= 2; } Tilemap** new = realloc(_world_maps, amount * sizeof(Tilemap*)); ASSERT_RETURN(new != NULL,, "Could not grow physics tilemaps array"); _world_maps = new; _world_maps_cap = amount; } void physics_world_add_map(Tilemap* map) { _internal_physics_world_resize_maps(_world_maps_len + 1); _world_maps[_world_maps_len] = map; ++_world_maps_len; } static inline void _internal_physics_world_remove_map(Tilemap** at) { Tilemap** from = at + 1; memmove(at, from, (at - _world_maps + _world_bodies_len) * sizeof(Tilemap*)); --_world_bodies_len; } void physics_world_remove_map(Tilemap* map) { Tilemap** end = _world_maps + _world_maps_len; for(Tilemap** itr = _world_maps; itr < end; ++itr) { if(*itr == map) { _internal_physics_world_remove_map(itr); return; } } ASSERT_RETURN(0,, "Physics tilemap with data at %p is not registered in physics world and cannot be removed.", map); } static inline void _internal_physics_narrow_collision() { PhysicsEntity* end = _world_bodies + _world_bodies_len; PhysicsEntity* half_end = _world_bodies + _world_bodies_len/2; Collision collision_left, collision_right; for(PhysicsEntity* left = _world_bodies; left < end; ++left) { for(PhysicsEntity* right = _world_bodies; right < half_end; ++right) { if(left->data == right->data) continue; if(collision_check(*left, *right, &collision_left, &collision_right)) { left->tc->on_collision(left->data, collision_left); right->tc->on_collision(right->data, collision_right); } } } } static inline void _internal_tilemap_entity_collision_check(Tilemap* map, PhysicsEntity entity) { Collision collision_a; Collision collision_b; PhysicsEntity tileentity; RigidBody* entity_body = entity.tc->get_rigidbody(entity.data); size_t len = tilemap_get_tile_count(map); for(size_t i = 0; i < len; ++i) { tileentity = TileInstance_as_PhysicsEntity(tilemap_get_tile(map, i)); if(tileentity.tc->get_shape(tileentity.data) == NULL) continue; if(collision_check(entity, tileentity, &collision_a, &collision_b)) { rigidbody_add_contact(entity_body, collision_a); } } } static inline void _internal_physics_tilemap_collision() { PhysicsEntity* bodies_end = _world_bodies + _world_bodies_len; Tilemap** maps_end = _world_maps + _world_maps_len; for(PhysicsEntity* entity = _world_bodies; entity < bodies_end; ++entity) { for(Tilemap** map = _world_maps; map < maps_end; ++map) { _internal_tilemap_entity_collision_check(*map, *entity); } } } static inline void _internal_physics_apply() { PhysicsEntity* end = _world_bodies + _world_bodies_len; for(PhysicsEntity* entity = _world_bodies; entity < end; ++entity) { physics_entity_update(*entity); } } static inline void _internal_physics_integrate_forces() { PhysicsEntity* end = _world_bodies + _world_bodies_len; for(PhysicsEntity* entity = _world_bodies; entity < end; ++entity) rigidbody_integrate_forces(entity->tc->get_rigidbody(entity->data)); } void physics_world_tick() { _internal_physics_integrate_forces(); _internal_physics_narrow_collision(); _internal_physics_tilemap_collision(); _internal_physics_apply(); }