From c7e6b2aa29ecf0238bed602bb1db4b79661e8c1c Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 16 Oct 2023 21:31:48 +0200 Subject: [PATCH] progress on physics --- assets/levels/levels.ldtk | 10 +- compile_commands.json | 272 +++++++++++++++++++++++++++++++++----- levels.ldtk | 10 +- sprites/player.ase | Bin 1559 -> 2215 bytes src/collision.c | 46 ++++--- src/fencer.c | 36 +---- src/level.c | 16 +++ src/level.h | 5 + src/physics_world.c | 15 ++- src/player.c | 41 ++++-- src/program.c | 29 ++-- src/rigidbody.c | 136 +++++++++++++++++-- src/rigidbody.h | 6 + src/shape.c | 27 +++- src/shape.h | 1 + src/sprite.c | 2 +- src/sprite_entity.c | 2 +- src/tilemap.c | 21 ++- src/tilemap.h | 14 +- src/tileset.c | 1 + src/transform.h | 20 +-- src/transformable.c | 41 ++++++ src/transformable.h | 24 ++-- src/vmath.h | 33 ++++- 24 files changed, 635 insertions(+), 173 deletions(-) create mode 100644 src/transformable.c diff --git a/assets/levels/levels.ldtk b/assets/levels/levels.ldtk index efcb32c..46ff888 100644 --- a/assets/levels/levels.ldtk +++ b/assets/levels/levels.ldtk @@ -1187,8 +1187,8 @@ "padding": 0, "tags": ["World"], "tagsSourceEnumUid": 477, - "enumTags": [ { "enumValueId": "none", "tileIds": [] }, { "enumValueId": "fullrect", "tileIds": [4,6,7,10,11,12,13,14,15,16,17,21,24,25,26,27,31,34,35,36,37,44,45] }, { "enumValueId": "pointshape", "tileIds": [0,2,3,20,22,23,30,32] } ], - "customData": [], + "enumTags": [ { "enumValueId": "fullrect", "tileIds": [1,4,6,7,10,11,12,13,14,15,16,17,21,24,25,26,27,31,34,35,36,37,44,45] }, { "enumValueId": "pointshape", "tileIds": [0,2,3,20,22,23,30,32] } ], + "customData": [ { "tileId": 0, "data": "\"collisionShape\": [\n [0.0, 0.0], [1.0, 1.0],\n [1.0, 0.0]\n]" }, { "tileId": 2, "data": "\"collisionsPoints\": [\n [0.0, 0.0], [0.0, 1.0], [1.0, 0.0]\n]" } ], "savedSelections": [], "cachedPixelData": { "opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -1236,11 +1236,7 @@ "savedSelections": [], "cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" } } - ], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ - { "id": "none", "tileRect": { "tilesetUid": 3, "x": 384, "y": 64, "w": 16, "h": 16 }, "color": 16777215 }, - { "id": "fullrect", "tileRect": { "tilesetUid": 3, "x": 272, "y": 272, "w": 16, "h": 16 }, "color": 16723968 }, - { "id": "pointshape", "tileRect": { "tilesetUid": 3, "x": 288, "y": 48, "w": 16, "h": 16 }, "color": 8698879 } - ], "iconTilesetUid": 3, "externalRelPath": null, "externalFileChecksum": null, "tags": ["Tiles"] }], "externalEnums": [], "levelFields": [] }, + ], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ { "id": "fullrect", "tileRect": { "tilesetUid": 3, "x": 272, "y": 272, "w": 16, "h": 16 }, "color": 16723968 }, { "id": "pointshape", "tileRect": { "tilesetUid": 3, "x": 288, "y": 48, "w": 16, "h": 16 }, "color": 8698879 } ], "iconTilesetUid": 3, "externalRelPath": null, "externalFileChecksum": null, "tags": ["Tiles"] }], "externalEnums": [], "levelFields": [] }, "levels": [ { "identifier": "level_0", diff --git a/compile_commands.json b/compile_commands.json index bf63341..3ac525a 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -1,21 +1,4 @@ [ - { - "arguments": [ - "/usr/lib64/ccache/cc", - "-c", - "-Wall", - "-g3", - "-DVMATH_SDL=1", - "-Iinclude", - "-Isrc", - "-o", - "bin/fencer", - "src/fencer.c" - ], - "directory": "/home/sara/Documents/c-projects/fencer", - "file": "/home/sara/Documents/c-projects/fencer/src/fencer.c", - "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" - }, { "arguments": [ "/usr/lib64/ccache/cc", @@ -50,23 +33,6 @@ "file": "/home/sara/Documents/c-projects/fencer/src/render.c", "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" }, - { - "arguments": [ - "/usr/lib64/ccache/cc", - "-c", - "-Wall", - "-g3", - "-DVMATH_SDL=1", - "-Iinclude", - "-Isrc", - "-o", - "bin/fencer", - "src/tilemap.c" - ], - "directory": "/home/sara/Documents/c-projects/fencer", - "file": "/home/sara/Documents/c-projects/fencer/src/tilemap.c", - "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" - }, { "arguments": [ "/usr/lib64/ccache/cc", @@ -134,5 +100,243 @@ "directory": "/home/sara/Documents/c-projects/fencer", "file": "/home/sara/Documents/c-projects/fencer/src/assets.c", "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/level.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/level.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/tileset.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/tileset.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/debug.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/debug.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/input.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/input.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/shape.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/shape.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/fencer.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/fencer.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/rigidbody.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/rigidbody.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/collision.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/collision.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/player.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/player.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/physics_world.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/physics_world.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/tilemap.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/tilemap.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/sprite_entity.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/sprite_entity.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/transformable.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/transformable.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" + }, + { + "arguments": [ + "/usr/lib64/ccache/cc", + "-c", + "-Wall", + "-g3", + "-DVMATH_SDL=1", + "-Iinclude", + "-Isrc", + "-o", + "bin/fencer", + "src/list.c" + ], + "directory": "/home/sara/Documents/c-projects/fencer", + "file": "/home/sara/Documents/c-projects/fencer/src/list.c", + "output": "/home/sara/Documents/c-projects/fencer/bin/fencer" } ] diff --git a/levels.ldtk b/levels.ldtk index efcb32c..46ff888 100644 --- a/levels.ldtk +++ b/levels.ldtk @@ -1187,8 +1187,8 @@ "padding": 0, "tags": ["World"], "tagsSourceEnumUid": 477, - "enumTags": [ { "enumValueId": "none", "tileIds": [] }, { "enumValueId": "fullrect", "tileIds": [4,6,7,10,11,12,13,14,15,16,17,21,24,25,26,27,31,34,35,36,37,44,45] }, { "enumValueId": "pointshape", "tileIds": [0,2,3,20,22,23,30,32] } ], - "customData": [], + "enumTags": [ { "enumValueId": "fullrect", "tileIds": [1,4,6,7,10,11,12,13,14,15,16,17,21,24,25,26,27,31,34,35,36,37,44,45] }, { "enumValueId": "pointshape", "tileIds": [0,2,3,20,22,23,30,32] } ], + "customData": [ { "tileId": 0, "data": "\"collisionShape\": [\n [0.0, 0.0], [1.0, 1.0],\n [1.0, 0.0]\n]" }, { "tileId": 2, "data": "\"collisionsPoints\": [\n [0.0, 0.0], [0.0, 1.0], [1.0, 0.0]\n]" } ], "savedSelections": [], "cachedPixelData": { "opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -1236,11 +1236,7 @@ "savedSelections": [], "cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" } } - ], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ - { "id": "none", "tileRect": { "tilesetUid": 3, "x": 384, "y": 64, "w": 16, "h": 16 }, "color": 16777215 }, - { "id": "fullrect", "tileRect": { "tilesetUid": 3, "x": 272, "y": 272, "w": 16, "h": 16 }, "color": 16723968 }, - { "id": "pointshape", "tileRect": { "tilesetUid": 3, "x": 288, "y": 48, "w": 16, "h": 16 }, "color": 8698879 } - ], "iconTilesetUid": 3, "externalRelPath": null, "externalFileChecksum": null, "tags": ["Tiles"] }], "externalEnums": [], "levelFields": [] }, + ], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ { "id": "fullrect", "tileRect": { "tilesetUid": 3, "x": 272, "y": 272, "w": 16, "h": 16 }, "color": 16723968 }, { "id": "pointshape", "tileRect": { "tilesetUid": 3, "x": 288, "y": 48, "w": 16, "h": 16 }, "color": 8698879 } ], "iconTilesetUid": 3, "externalRelPath": null, "externalFileChecksum": null, "tags": ["Tiles"] }], "externalEnums": [], "levelFields": [] }, "levels": [ { "identifier": "level_0", diff --git a/sprites/player.ase b/sprites/player.ase index b6927e09db9b5c29c2fd8d857b994bed9c130326..d1e0b298dad20b73d988f20b6be108884c57ee37 100644 GIT binary patch delta 1724 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 diff --git a/src/collision.c b/src/collision.c index e302e9c..e1ee83c 100644 --- a/src/collision.c +++ b/src/collision.c @@ -1,25 +1,32 @@ #include "collision.h" +#include "debug.h" #include "player.h" // ===================================================== // Shape overlap test using the separating axis theorem // ===================================================== -typedef struct Range {float min; float max; } Range; +typedef struct Range {float min; Vector minpoint; float max; Vector maxpoint; } Range; static Range _internal_collision_get_range_on_axis(PhysicsEntity self, Vector axis) { - Vector point = shape_get_point_transformed(self.tc->get_shape(self.data), 0, *self.transformable->get_transform(self.data)); + Transform* transform = self.transformable->get_transform(self.data); + Shape* shape = self.tc->get_shape(self.data); + Vector point = shape_get_point_transformed(shape, 0, *transform); float dot = vdotf(axis, point); - Range range = {dot, dot}; + Range range = {dot, point, dot, point}; - for(size_t point_index = 1; point_index < shape_get_points_count(self.tc->get_shape(self.data)); ++point_index) { - point = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self.transformable->get_transform(self.data)); + for(size_t point_index = 1; point_index < shape_get_points_count(shape); ++point_index) { + point = shape_get_point_transformed(shape, point_index, *transform); dot = vdotf(axis, point); - if(dot < range.min) - range.min = fminf(dot, range.min); - if(dot > range.max) - range.max = fmaxf(dot, range.max); + if(dot < range.min) { + range.min = dot; + range.minpoint = point; + } + if(dot > range.max) { + range.max = dot; + range.maxpoint = point; + } } return range; @@ -30,10 +37,11 @@ Vector _internal_collision_overlap_on_axis(PhysicsEntity self, PhysicsEntity oth Range a_range = _internal_collision_get_range_on_axis(self, axis); Range b_range = _internal_collision_get_range_on_axis(other, axis); - if(a_range.min <= b_range.max && b_range.min <= b_range.max) - return vmulff(axis, fminf(b_range.max - b_range.min, b_range.min - a_range.max)); - else + if(a_range.min <= b_range.max && a_range.min <= b_range.max) { + return vmulff(axis, fminf(a_range.max - b_range.min, b_range.min - a_range.max)); + } else { return ZeroVector; + } } static @@ -58,20 +66,26 @@ int _internal_collision_get_overlap(PhysicsEntity self, PhysicsEntity other, Col Vector a = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self_transform); Vector b = shape_get_point_transformed(self.tc->get_shape(self.data), next_index, *self_transform); // the direction of the line - Vector diff = vsubf(b, a); + Vector normal = vperpendicularf(vsubf(b, a)); // the smallest escape vector on this axis - Vector escape = _internal_collision_overlap_on_axis(self, other, vnormalizedf(diff)); + Vector escape = _internal_collision_overlap_on_axis(self, other, vnormalizedf(normal)); float sqr_mag = vsqrmagnitudef(escape); if(sqr_mag < shortest_sqrmag) { shortest_sqrmag = sqr_mag; shortest_escape = escape; shortest_escape_edge = point_index; } + + if(sqr_mag == 0) { + return 0; + } } RigidBody* rba = self.tc->get_rigidbody(self.data); - RigidBody* rbb = self.tc->get_rigidbody(self.data); + RigidBody* rbb = other.tc->get_rigidbody(other.data); + Vector velocity = rigidbody_get_velocity(rba); + velocity = vsubf(velocity, rigidbody_get_velocity(rbb)); *out = (Collision) { .other = other, @@ -79,7 +93,7 @@ int _internal_collision_get_overlap(PhysicsEntity self, PhysicsEntity other, Col .point = InfinityVector, .normal = vnormalizedf(shortest_escape), - .velocity = vsubf(rigidbody_get_velocity(rba), rigidbody_get_velocity(rbb)), + .velocity = velocity, .separation_force = shortest_escape, .edge_left = shape_get_point_transformed(self_shape, shortest_escape_edge, *self_transform), diff --git a/src/fencer.c b/src/fencer.c index ff97a81..c720e1d 100644 --- a/src/fencer.c +++ b/src/fencer.c @@ -11,59 +11,31 @@ #include "player.h" #include -static Spritesheet* spr_player_standing = NULL; static Player* player = NULL; - static Level* level = NULL; -static Vector cam_speed = ZeroVector; - -static -void cam_move_h(int val) { - cam_speed.x = val * 0.1f; -} -static -void cam_move_v(int val) { - cam_speed.y = -val * 0.1f; -} - static void play() { - spr_player_standing = spritesheet_load("assets/sprites/player.png", (IVector){128, 128}); g_camera.fov = 40; player = malloc(sizeof(Player)); - player->transform.scale = (Vector){4.f, 4.f}; - store_asset(player, free); - player->sprite = sprite_from_spritesheet(spr_player_standing, 0); - player->rigidbody = rigidbody_make(Player_as_Transformable(player)); - sprite_set_origin(player->sprite, (Vector){0.25f, 1.f}); - player->shape = shape_new((Vector[]){ - {-.1f, -.75f}, - {0.1f, -.75f}, - {0.1f, 0.00f}, - {-.1f, 0.00f} - }, 4); + player_spawn(player, ZeroVector); level = level_load("level_0"); - - input_add_axis_action(SDL_SCANCODE_A, SDL_SCANCODE_D, &cam_move_h); - input_add_axis_action(SDL_SCANCODE_S, SDL_SCANCODE_W, &cam_move_v); - - physics_world_add_entity(Player_as_PhysicsEntity(player)); } static void tick() { - g_camera.transform.position = vaddf(g_camera.transform.position, cam_speed); - player->transform.position = g_camera.transform.position; + Player_as_BehaviourEntity(player).tc->update(player, delta_time()); + g_camera.transform.position = player->transform.position; } static void draw() { level_draw(level); sprite_entity_draw(Player_as_SpriteEntity(player)); + shape_draw(player->shape, player->transform); } int main(int argc, char* argv[]) { diff --git a/src/level.c b/src/level.c index f94e373..db8d5ec 100644 --- a/src/level.c +++ b/src/level.c @@ -1,6 +1,7 @@ #include "level.h" #include "assets.h" #include "debug.h" +#include "physics_world.h" #include "tilemap.h" #include #include @@ -23,6 +24,11 @@ static void _deallocate_level(void* self_void) { Level* self = self_void; + for(size_t map_index = 0; map_index < self->tilemaps_len; ++map_index) { + physics_world_remove_map(self->tilemaps[map_index]); + tilemap_destroy(self->tilemaps[map_index]); + } + free(self); } @@ -84,6 +90,7 @@ Level* level_from_json(cJSON* json) { // load a tilemap as an autolayer LOG_INFO("loading autolayer"); *next_tilemap = tilemap_from_autolayer(layer); + physics_world_add_map(*next_tilemap); ++next_tilemap; } else if(strcmp(type->valuestring, "Entities")) { // load entities @@ -123,3 +130,12 @@ void level_draw(Level* self) { tilemap_draw(*map, self->transform); } } + +Tilemap* level_get_tilemap_layer(Level* self, size_t layer) { + ASSERT_RETURN(layer < self->tilemaps_len, NULL, "Layer index %zu out of range", layer); + return self->tilemaps[layer]; +} + +size_t level_get_tilemap_count(Level* self) { + return self->tilemaps_len; +} diff --git a/src/level.h b/src/level.h index c5ccab5..6832517 100644 --- a/src/level.h +++ b/src/level.h @@ -5,6 +5,8 @@ #include #include +typedef struct Tilemap Tilemap; + typedef struct Level Level; extern void world_init(); @@ -16,4 +18,7 @@ extern void level_destroy(Level* self); extern void level_draw(Level* self); +extern Tilemap* level_get_tilemap_layer(Level* self, size_t layer); +extern size_t level_get_tilemap_count(Level* self); + #endif // !_fencer_level_h diff --git a/src/physics_world.c b/src/physics_world.c index 66a6d4f..13ff1ef 100644 --- a/src/physics_world.c +++ b/src/physics_world.c @@ -125,10 +125,12 @@ void _internal_physics_narrow_collision() { for(PhysicsEntity* left = _world_bodies; left < end; ++left) { for(PhysicsEntity* right = _world_bodies; right < half_end; ++right) { - collision_check(*left, *right, &collision_left, &collision_right); + if(left == right) continue; - left->tc->on_collision(left->data, collision_left); - right->tc->on_collision(right->data, collision_right); + 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); + } } } } @@ -138,6 +140,7 @@ 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) { @@ -145,6 +148,7 @@ void _internal_tilemap_entity_collision_check(Tilemap* map, PhysicsEntity entity if(collision_check(entity, tileentity, &collision_a, &collision_b)) { entity.tc->on_collision(entity.data, collision_a); + rigidbody_add_contact(entity_body, collision_a); } } } @@ -161,12 +165,13 @@ void _internal_physics_tilemap_collision() { } static -void _internal_physics_move() { +void _internal_physics_apply() { PhysicsEntity* end = _world_bodies + _world_bodies_len; RigidBody* body = NULL; for(PhysicsEntity* entity = _world_bodies; entity < end; ++entity) { body = entity->tc->get_rigidbody(entity->data); + rigidbody_solve_contacts(body); rigidbody_apply_physics(body); } } @@ -175,5 +180,5 @@ void physics_world_tick() { // _internal_physics_broad_collision(); _internal_physics_narrow_collision(); _internal_physics_tilemap_collision(); - _internal_physics_move(); + _internal_physics_apply(); } diff --git a/src/player.c b/src/player.c index a1d6874..8181670 100644 --- a/src/player.c +++ b/src/player.c @@ -1,37 +1,60 @@ #include "player.h" +#include "assets.h" #include "debug.h" #include "program.h" #include "rigidbody.h" #include "input.h" +#include "physics_world.h" static Vector directional = ZeroVector; +static Spritesheet* spr_player_standing = NULL; static void player_input_h(int val) { - directional.x = val * 0.1f; + directional.x = val * 10.f; } static void player_input_v(int val) { - directional.y = -val * 0.1f; + directional.y = -val * 10.f; } void player_spawn(Player* self, Vector at) { + player_start(self); self->transform.position = at; - input_add_axis_action(SDL_SCANCODE_A, SDL_SCANCODE_D, &player_input_h); - input_add_axis_action(SDL_SCANCODE_S, SDL_SCANCODE_W, &player_input_v); - } void player_start(Player* self) { + input_add_axis_action(SDL_SCANCODE_A, SDL_SCANCODE_D, &player_input_h); + input_add_axis_action(SDL_SCANCODE_S, SDL_SCANCODE_W, &player_input_v); + self->transform = IdentityTransform; + self->transform.scale = (Vector){4.f, 4.f}; + + spr_player_standing = spritesheet_load("assets/sprites/player.png", (IVector){128, 128}); + store_asset(self, free); + + self->sprite = sprite_from_spritesheet(spr_player_standing, 0); + sprite_set_origin(self->sprite, (Vector){0.25f, 1.f}); + + self->rigidbody = rigidbody_make(Player_as_Transformable(self)); + + float ex_w = 0.1f; + float h = .75f; + float r = 0.05f; + self->shape = shape_new((Vector[]){ + {r-ex_w, 0.f}, {-ex_w, -r}, + {-ex_w, r-h}, {r-ex_w, -h}, + {ex_w-r, -h}, {ex_w, r-h}, + {ex_w, -r}, {ex_w-r, 0.f}, + }, 8); + + physics_world_add_entity(Player_as_PhysicsEntity(self)); } void player_update(Player* self, float dt) { Vector velocity = rigidbody_get_velocity(self->rigidbody); - ASSERT_RETURN(!visnanf(velocity),, "Velocity is NaN (2)"); - velocity = vmovetowardsf(velocity, vmulff(directional, 100.f), 10000.f * dt); - ASSERT_RETURN(!visnanf(velocity),, "Velocity is NaN (3)"); - rigidbody_set_velocity(self->rigidbody, velocity); + Vector velocity_target = {directional.x, directional.y}; + rigidbody_accelerate(self->rigidbody, vmulff(vsubf(velocity_target, velocity), 200.f)); } void player_collision(Player* self, Collision hit) { diff --git a/src/program.c b/src/program.c index c6dd788..7cdc187 100644 --- a/src/program.c +++ b/src/program.c @@ -11,6 +11,7 @@ #include SDL_Window* g_window; +static double _target_delta_time = 0.0; static double _delta_time; static double _frame_start; static double _game_start_time; @@ -35,9 +36,10 @@ double get_time_s() { int program_run(const struct ProgramSettings* settings) { LOG_INFO("Starting program..."); - float target_dt = 1.0f/settings->target_fps; if(settings->target_fps <= 0) { - target_dt = 0; + _target_delta_time = 0; + } else { + _target_delta_time = 1.0f/settings->target_fps; } _game_start_time = _frame_start = get_time_s(); SDL_Init(INITFLAGS); @@ -62,19 +64,20 @@ int program_run(const struct ProgramSettings* settings) { LOG_INFO("Starting program loop"); for(;;) { render_present(); - double current; - do { - current = get_time_s(); - _delta_time = current - _frame_start; - SDL_Delay(1); - program_handle_events(); - } while(_delta_time <= target_dt); - _frame_start = current; + double current_time = get_time_s(); + _delta_time += current_time - _frame_start; + _frame_start = current_time; - settings->on_tick(); + while(_delta_time > _target_delta_time) { + _delta_time -= _target_delta_time; + settings->on_tick(); + physics_world_tick(); + } + + program_handle_events(); settings->on_draw(); - physics_world_tick(); + SDL_Delay(1); } LOG_ERROR("Failed to exit"); @@ -122,5 +125,5 @@ void program_handle_windowevent(SDL_WindowEvent* event) { inline float delta_time() { - return _delta_time; + return _target_delta_time == 0 ? _delta_time : _target_delta_time; } diff --git a/src/rigidbody.c b/src/rigidbody.c index 4f2faad..daa8fed 100644 --- a/src/rigidbody.c +++ b/src/rigidbody.c @@ -1,37 +1,156 @@ #include "rigidbody.h" +#include "camera.h" +#include "debug.h" #include "program.h" +#include "collision.h" + +typedef struct { + Collision hit; + float warming; + int expiry; +} Contact; struct RigidBody { Transformable transformable; float mass; - Vector linear_velocity; Vector linear_force; + Vector linear_velocity; int is_static; + + List contacts; }; RigidBody* rigidbody_make(Transformable transform) { RigidBody* self = malloc(sizeof(RigidBody)); + ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for rigidbody"); *self = (RigidBody){ .transformable = transform, - .mass = 0.0f, + .mass = 1.0f, .linear_velocity = ZeroVector, - .linear_force = ZeroVector, - .is_static = 0 + .is_static = 0, + .contacts = list_from_type(Contact), }; return self; } void rigidbody_destroy(RigidBody* self) { + list_empty(&self->contacts); free(self); } +void rigidbody_add_contact(RigidBody* self, Collision hit) { + LOG_INFO("contact between rigidbody %p and %p detected", self->transformable.data, hit.other.data); + // update an existing contact + list_foreach(Contact, contact, &self->contacts) { + if(contact->hit.other.data == hit.other.data) { + ++contact->expiry; + contact->hit = hit; + contact->warming += delta_time(); + return; + } + } + + // create a new contact + list_add(&self->contacts, + &(Contact) { + .expiry = 1, + .hit = hit, + .warming = delta_time() + }); + + Contact* c = list_at(&self->contacts, self->contacts.len - 1); + LOG_INFO("added contact %p with exp %d", c->hit.other.data, c->expiry); +} + +static inline +void _internal_rigidbody_collect_contacts(RigidBody* self) { + for(size_t i = 0; i < self->contacts.len; ++i) { + Contact* contact = list_at(&self->contacts, i); + --(contact->expiry); + if(contact->expiry <= 0) { + list_erase(&self->contacts, i); + i--; + } + } +} + +static inline +void _internal_debug_draw_collision_edge(Vector left, Vector right, Vector normal) { +#if !NDEBUG + Vector a = camera_world_to_pixel_point(&g_camera, left); + Vector b = camera_world_to_pixel_point(&g_camera, right); + Vector n = transform_direction(&g_camera.transform, normal); + SDL_SetRenderDrawColor(g_renderer, 255, 255, 255, 255); + SDL_RenderDrawLine(g_renderer, a.x, a.y, b.x, b.y); + a = vlerpf(a, b, 0.5f); + b = vaddf(a, vmulff(n, 100.f)); + SDL_SetRenderDrawColor(g_renderer, 255, 0, 0, 255); + SDL_RenderDrawLine(g_renderer, a.x, a.y, b.x, b.y); +#endif +} + +static inline +Vector _internal_calculate_contact_force(Contact* contact) { + Collision hit = contact->hit; + const float warming = fminf(1.f, contact->warming); + const float d = 1.f; + const float k = 50.0 * warming; + const float b = 1.f; + + const Vector damping = vmulff(hit.normal, k * d); + const Vector bounce = vprojectf(vmulff(hit.normal, -b), hit.velocity); + + return vsubf(damping, bounce); +} + +static inline +void _internal_rigidbody_solve_contact(RigidBody* self, Contact* contact, Vector* solve_forces) { + Collision hit = contact->hit; + if (vsqrmagnitudef(contact->hit.separation_force) < 0.001f*0.001f) + return; + Vector force = _internal_calculate_contact_force(contact); + float dot = vdotf(vnormalizedf(*solve_forces), force); + if (veqf(*solve_forces, ZeroVector) || dot * dot > vsqrmagnitudef(*solve_forces) || dot <= 0.0f) { + #if !NDEBUG + LOG_INFO("warming: %f", contact->warming); + LOG_INFO("force: %f %f", force.x, force.y); + LOG_INFO("dot: %f", dot); + LOG_INFO("mag: %f", vmagnitudef(self->linear_force)); + #endif + *solve_forces = vaddf(*solve_forces, force); + rigidbody_add_impulse(self, force); + } + + _internal_debug_draw_collision_edge(hit.edge_left, hit.edge_right, hit.normal); + + ASSERT_RETURN(!visnanf(force), , "Force contains NaN (1)"); +} + +void rigidbody_solve_contacts(RigidBody* self) { + ASSERT_RETURN(!visnanf(self->linear_velocity),, "Velocity is NaN (0)"); + + Vector solve_forces = ZeroVector; + list_foreach(Contact, contact, &self->contacts) { + _internal_rigidbody_solve_contact(self, contact, &solve_forces); + } + + _internal_rigidbody_collect_contacts(self); + + ASSERT_RETURN(!visnanf(self->linear_velocity),, "Velocity is NaN (1)"); +} + void rigidbody_apply_physics(RigidBody* self) { + Vector position = transformable_get_position(self->transformable); Vector velocity = vmulff(self->linear_velocity, delta_time()); - Vector* position = (self->transformable.tc->get_position(self->transformable.data)); - *position = vaddf(*position, velocity); + + if(vsqrmagnitudef(velocity) > powf(0.00001f, 2)) { + transformable_set_position(self->transformable, vaddf(position, velocity)); + } + + self->linear_force = ZeroVector; } float rigidbody_get_mass(const RigidBody* self) { @@ -44,11 +163,11 @@ void rigidbody_set_mass(RigidBody* self, float mass) { void rigidbody_add_impulse(RigidBody* self, Vector force) { self->linear_force = vaddf(self->linear_force, force); + self->linear_velocity = vaddf(self->linear_velocity, force); } void rigidbody_accelerate(RigidBody* self, Vector force) { - force = vmulff(force, delta_time()); - self->linear_force = vaddf(self->linear_force, force); + rigidbody_add_impulse(self, vmulff(force, delta_time())); } int rigidbody_is_static(const RigidBody* self) { @@ -64,5 +183,6 @@ Vector rigidbody_get_velocity(const RigidBody* self) { } void rigidbody_set_velocity(RigidBody* self, Vector velocity) { + self->linear_force = vaddf(self->linear_force, vsubf(velocity, self->linear_force)); self->linear_velocity = velocity; } diff --git a/src/rigidbody.h b/src/rigidbody.h index 749d55e..a4f29e2 100644 --- a/src/rigidbody.h +++ b/src/rigidbody.h @@ -3,6 +3,9 @@ #include "shape.h" #include "transformable.h" +#include "list.h" + +struct Collision; typedef struct RigidBody RigidBody; @@ -10,6 +13,9 @@ typedef struct RigidBody RigidBody; RigidBody* rigidbody_make(Transformable transform); void rigidbody_destroy(RigidBody* self); +void rigidbody_add_contact(RigidBody* self, struct Collision hit); +void rigidbody_solve_contacts(RigidBody* self); + void rigidbody_apply_physics(RigidBody* self); float rigidbody_get_mass(const RigidBody* self); diff --git a/src/shape.c b/src/shape.c index 9dd1c4b..3b94c60 100644 --- a/src/shape.c +++ b/src/shape.c @@ -1,5 +1,8 @@ #include "shape.h" +#include "camera.h" #include "debug.h" +#include "render.h" +#include #include struct Shape { @@ -86,9 +89,9 @@ Shape* shape_new(const Vector* points, size_t points_len) { Shape* shape_new_square(Vector size) { return shape_new((Vector[4]){ ZeroVector, - (Vector){0.f, size.y}, + (Vector){size.x, 0.f}, size, - (Vector){size.x, 0.f} + (Vector){0.f, size.y}, }, 4); } @@ -182,3 +185,23 @@ Vector shape_get_median_point(Shape* self) { int shape_is_convex(Shape* self) { return self->is_convex; } + +void shape_draw(Shape* self, Transform transform) { + Vector lhs, rhs, normal; + for(size_t i = 0; i < self->points_len; ++i) { + lhs = shape_get_point_transformed(self, i, transform); + rhs = shape_get_point_transformed(self, (i + 1) % self->points_len, transform); + normal = vnormalizedf(vperpendicularf(vsubf(rhs, lhs))); + lhs = camera_world_to_pixel_point(&g_camera, lhs); + rhs = camera_world_to_pixel_point(&g_camera, rhs); + normal = transform_direction(&g_camera.transform, normal); + + SDL_SetRenderDrawColor(g_renderer, 255, 255, 255, 255); + SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y); + + lhs = vlerpf(lhs, rhs, 0.5f); + rhs = vaddf(lhs, vmulff(normal, 10.f)); + SDL_SetRenderDrawColor(g_renderer, 0, 0, 255, 255); + SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y); + } +} diff --git a/src/shape.h b/src/shape.h index 62be26e..bf91607 100644 --- a/src/shape.h +++ b/src/shape.h @@ -25,5 +25,6 @@ extern Vector shape_remove_point(Shape* self, size_t at); extern Vector shape_get_median_point(Shape* self); extern int shape_is_convex(Shape* self); +extern void shape_draw(Shape* self, Transform transform); #endif // !_fencer_shape_h diff --git a/src/sprite.c b/src/sprite.c index 7d7420a..21d1d26 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -25,7 +25,7 @@ Sprite* sprite_from_spritesheet(Spritesheet* sheet, size_t initial_frame) { ASSERT_RETURN(self != NULL, NULL, "Failed to allocate memory for new sprite."); self->spritesheet = sheet; - self->origin = (Vector){0.5f, 1.0f}; + self->origin = (Vector){0.5f, 0.5f}; self->current_frame = initial_frame; // TODO: replace with a getter for the current game time. self->current_frame_time = 0; diff --git a/src/sprite_entity.c b/src/sprite_entity.c index 4b3a0a9..c7bc120 100644 --- a/src/sprite_entity.c +++ b/src/sprite_entity.c @@ -1,5 +1,5 @@ #include "sprite_entity.h" -#include "sprite.h" +#include "sprite.h" void sprite_entity_draw(SpriteEntity self) { Sprite* sprite = self.tc->get_sprite(self.data); diff --git a/src/tilemap.c b/src/tilemap.c index 03b29d8..61cd60c 100644 --- a/src/tilemap.c +++ b/src/tilemap.c @@ -1,5 +1,6 @@ #include "tilemap.h" #include "debug.h" +#include "rigidbody.h" #include "tileset.h" #include "assets.h" #include "camera.h" @@ -63,6 +64,7 @@ Tilemap* tilemap_from_autolayer(cJSON* json) { free(self); RETURN_ERROR(NULL, "Failed to allocate map memory"); } + self->rigidbody = rigidbody_make(Tilemap_as_Transformable(self)); const double px_to_ws = 1.0 / grid->valuedouble; @@ -80,17 +82,30 @@ Tilemap* tilemap_from_autolayer(cJSON* json) { return self; } +void tilemap_destroy(Tilemap* self) { + free(self->map); + rigidbody_destroy(self->rigidbody); + free(self); +} + void tilemap_draw(Tilemap* self, Transform transform) { Transform tiletrans = transform_apply(transform, self->transform); TileInstance* tile; + for(int i = 0; i < self->map_num; ++i) { + tile = self->map + i; + tiletrans = transform_apply(self->transform, tile->transform); + shape_draw(tiledef_get_shape(tile->tiledef), tiletrans); + } + for(int i = 0; i < self->map_num; ++i) { tile = self->map + i; tiletrans = transform_apply(self->transform, tile->transform); Sprite* sprite = tiledef_get_sprite(tile->tiledef); - if(sprite != NULL) + if(sprite != NULL) { sprite_draw(sprite, tiletrans); + } } } @@ -102,6 +117,10 @@ TileInstance* tilemap_get_tile(Tilemap* self, size_t at) { return &self->map[at]; } +Transform* tilemap_get_transform(Tilemap* self ) { + return &self->transform; +} + RigidBody* tile_instance_get_rigidbody(TileInstance* self) { return self->parent_map->rigidbody; } diff --git a/src/tilemap.h b/src/tilemap.h index 6cb8fa1..78bb952 100644 --- a/src/tilemap.h +++ b/src/tilemap.h @@ -22,20 +22,20 @@ extern void tilemap_draw(Tilemap* self, Transform transform); extern size_t tilemap_get_tile_count(Tilemap* self); extern TileInstance* tilemap_get_tile(Tilemap* self, size_t at); +extern Transform* tilemap_get_transform(Tilemap* self); + extern RigidBody* tile_instance_get_rigidbody(TileInstance* self); extern Transform* tile_instance_get_transform(TileInstance* self); extern Shape* tile_instance_get_shape(TileInstance* self); extern void tile_instance_on_collision(TileInstance* self, Collision collision); -extern Vector* tile_instance_get_position(TileInstance* self); -extern Vector* tile_instance_get_scale(TileInstance* self); -extern float* tile_instance_get_rotation(TileInstance* self); + +impl_Transformable_for(Tilemap, + tilemap_get_transform +) impl_Transformable_for(TileInstance, - tile_instance_get_transform, - tile_instance_get_position, - tile_instance_get_scale, - tile_instance_get_rotation + tile_instance_get_transform ) impl_PhysicsEntity_for(TileInstance, tile_instance_get_rigidbody, diff --git a/src/tileset.c b/src/tileset.c index c8357c0..445d73f 100644 --- a/src/tileset.c +++ b/src/tileset.c @@ -122,6 +122,7 @@ Tileset* tileset_from_json(cJSON* json) { .sprite = sprite_from_spritesheet(self->atlas, tid), .collision = shape_new_square(OneVector) }; + sprite_set_origin(self->tiledefs[tid].sprite, ZeroVector); // TODO: generate/read collision information } diff --git a/src/transform.h b/src/transform.h index ae0cc5d..fa88cf5 100644 --- a/src/transform.h +++ b/src/transform.h @@ -51,26 +51,8 @@ Transform* transform_get_transform(Transform* self) { return self; } -static -Vector* transform_get_position(Transform* self) { - return &self->position; -} - -static -Vector* transform_get_scale(Transform* self) { - return &self->scale; -} - -static -float* transform_get_rotation(Transform* self) { - return &self->rotation; -} - impl_Transformable_for(Transform, - transform_get_transform, - transform_get_position, - transform_get_scale, - transform_get_rotation + transform_get_transform ); #endif // !_fencer_transform_h diff --git a/src/transformable.c b/src/transformable.c new file mode 100644 index 0000000..11a2559 --- /dev/null +++ b/src/transformable.c @@ -0,0 +1,41 @@ +#include "transformable.h" +#include "transform.h" + +Vector transformable_get_position(Transformable self) { + return self.tc->get_transform(self.data)->position; +} + +void transformable_set_position(Transformable self, Vector position) { + self.tc->get_transform(self.data)->position = position; +} + +void transformable_move(Transformable self, Vector delta) { + Vector* position = &self.tc->get_transform(self.data)->position; + *position = vaddf(*position, delta); +} + +Vector transformable_get_scale(Transformable self) { + return self.tc->get_transform(self.data)->scale; +} + +void transformable_set_scale(Transformable self, Vector scale) { + self.tc->get_transform(self.data)->scale = scale; +} + +void transformable_scale(Transformable self, Vector factor) { + Vector* scale = &self.tc->get_transform(self.data)->scale; + *scale = vmulf(*scale, factor); +} + +float transformable_get_rotation(Transformable self) { + return self.tc->get_transform(self.data)->rotation; +} + +void transformable_set_rotation(Transformable self, float rotation) { + self.tc->get_transform(self.data)->rotation = rotation; +} + +void transformable_rotate(Transformable self, float delta) { + float* rotation = &self.tc->get_transform(self.data)->rotation; + *rotation = *rotation + delta; +} diff --git a/src/transformable.h b/src/transformable.h index 5e9c79c..0ac7531 100644 --- a/src/transformable.h +++ b/src/transformable.h @@ -6,9 +6,6 @@ typedef struct { struct Transform* (*const get_transform)(void* self); - Vector* (*const get_position)(void* self); - Vector* (*const get_scale)(void* self); - float* (*const get_rotation)(void* self); } ITransformable; typedef struct { @@ -16,16 +13,23 @@ typedef struct { ITransformable const* tc; } Transformable; -#define impl_Transformable_for(T, get_transform_f, get_position_f, get_scale_f, get_rotation_f)\ +extern Vector transformable_get_position(Transformable self); +extern void transformable_set_position(Transformable self, Vector position); +extern void transformable_move(Transformable self, Vector delta); + +extern Vector transformable_get_scale(Transformable self); +extern void transformable_set_scale(Transformable self, Vector scale); +extern void transformable_scale(Transformable self, Vector factor); + +extern float transformable_get_rotation(Transformable self); +extern void transformable_set_rotation(Transformable self, float rotation); +extern void transformable_rotate(Transformable self, float delta); + +#define impl_Transformable_for(T, get_transform_f)\ static inline Transformable T##_as_Transformable(T* x) {\ TC_FN_TYPECHECK(Transform*, get_transform_f, T*);\ - TC_FN_TYPECHECK(Vector*, get_position_f, T*);\ - TC_FN_TYPECHECK(float*, get_rotation_f, T*);\ static ITransformable const tc = {\ - .get_transform = (Transform*(*const)(void*)) get_transform_f,\ - .get_position = (Vector*(*const)(void*)) get_position_f,\ - .get_scale = (Vector*(*const)(void*)) get_scale_f,\ - .get_rotation = (float*(*const)(void*)) get_rotation_f\ + .get_transform = (Transform*(*const)(void*)) get_transform_f\ };\ return (Transformable){.tc = &tc, .data = x};\ } diff --git a/src/vmath.h b/src/vmath.h index 87749c3..3697372 100644 --- a/src/vmath.h +++ b/src/vmath.h @@ -46,6 +46,15 @@ typedef struct IVector { // Floating point vector maths functions. /// static inline +int veqf(Vector a, Vector b) { + const float e = 0.0001f; + return fabsf(a.x - b.x) + fabsf(a.y - b.y) < e; +} +static inline +int visnanf(Vector a) { + return isnanf(a.x) || isnanf(a.y); +} +static inline Vector vaddf(Vector a, Vector b) { return (Vector){a.x + b.x, a.y + b.y}; } @@ -71,14 +80,20 @@ Vector vperpendicularf(Vector a) { } static inline float vmagnitudef(Vector a) { + if(veqf(a, ZeroVector)) return 0.f; + a.x = fabsf(a.x); + a.y = fabsf(a.y); return sqrtf(a.x*a.x + a.y*a.y); } static inline float vsqrmagnitudef(Vector a) { - return fabsf(a.x*a.x) + fabsf(a.y*a.y); + a.x = fabsf(a.x); + a.y = fabsf(a.y); + return a.x*a.x + a.y*a.y; } static inline Vector vnormalizedf(Vector a) { + if(veqf(a, ZeroVector)) return ZeroVector; return vmulff(a, 1.0/vmagnitudef(a)); } static inline @@ -104,6 +119,22 @@ Vector vrotatef(Vector a, float t) { sinf(t) * a.x + cosf(t) * a.y }; } +static inline +Vector vprojectf(Vector onto, Vector from) { + float dot = vdotf(onto, from); + return vmulff(onto, dot); +} +static inline +Vector vlerpf(Vector start, Vector end, float t) { + if(veqf(start, end)) + return end; + t = fminf(fmaxf(0.0f, t), 1.0f); + return vaddf(start, vmulff(vsubf(end, start), t)); +} +static inline +Vector vmovetowardsf(Vector start, Vector end, float delta) { + return vlerpf(start, end, delta / vdistancef(end, start)); +} static inline IVector vaddi(IVector a, IVector b) {