progress on physics

This commit is contained in:
Sara 2023-10-16 21:31:48 +02:00
parent 6cc16cf1f8
commit c7e6b2aa29
24 changed files with 635 additions and 173 deletions

View file

@ -1187,8 +1187,8 @@
"padding": 0, "padding": 0,
"tags": ["World"], "tags": ["World"],
"tagsSourceEnumUid": 477, "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] } ], "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": [], "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": [], "savedSelections": [],
"cachedPixelData": { "cachedPixelData": {
"opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
@ -1236,11 +1236,7 @@
"savedSelections": [], "savedSelections": [],
"cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" } "cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" }
} }
], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ ], "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": [] },
{ "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": [] },
"levels": [ "levels": [
{ {
"identifier": "level_0", "identifier": "level_0",

View file

@ -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": [ "arguments": [
"/usr/lib64/ccache/cc", "/usr/lib64/ccache/cc",
@ -50,23 +33,6 @@
"file": "/home/sara/Documents/c-projects/fencer/src/render.c", "file": "/home/sara/Documents/c-projects/fencer/src/render.c",
"output": "/home/sara/Documents/c-projects/fencer/bin/fencer" "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": [ "arguments": [
"/usr/lib64/ccache/cc", "/usr/lib64/ccache/cc",
@ -134,5 +100,243 @@
"directory": "/home/sara/Documents/c-projects/fencer", "directory": "/home/sara/Documents/c-projects/fencer",
"file": "/home/sara/Documents/c-projects/fencer/src/assets.c", "file": "/home/sara/Documents/c-projects/fencer/src/assets.c",
"output": "/home/sara/Documents/c-projects/fencer/bin/fencer" "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"
} }
] ]

View file

@ -1187,8 +1187,8 @@
"padding": 0, "padding": 0,
"tags": ["World"], "tags": ["World"],
"tagsSourceEnumUid": 477, "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] } ], "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": [], "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": [], "savedSelections": [],
"cachedPixelData": { "cachedPixelData": {
"opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "opaqueTiles": "0000000100010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
@ -1236,11 +1236,7 @@
"savedSelections": [], "savedSelections": [],
"cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" } "cachedPixelData": { "opaqueTiles": "000", "averageColors": "187928790bbd" }
} }
], "enums": [{ "identifier": "tilecollisiontype", "uid": 477, "values": [ ], "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": [] },
{ "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": [] },
"levels": [ "levels": [
{ {
"identifier": "level_0", "identifier": "level_0",

Binary file not shown.

View file

@ -1,25 +1,32 @@
#include "collision.h" #include "collision.h"
#include "debug.h"
#include "player.h" #include "player.h"
// ===================================================== // =====================================================
// Shape overlap test using the separating axis theorem // 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 static
Range _internal_collision_get_range_on_axis(PhysicsEntity self, Vector axis) { 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); 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) { for(size_t point_index = 1; point_index < shape_get_points_count(shape); ++point_index) {
point = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self.transformable->get_transform(self.data)); point = shape_get_point_transformed(shape, point_index, *transform);
dot = vdotf(axis, point); dot = vdotf(axis, point);
if(dot < range.min) if(dot < range.min) {
range.min = fminf(dot, range.min); range.min = dot;
if(dot > range.max) range.minpoint = point;
range.max = fmaxf(dot, range.max); }
if(dot > range.max) {
range.max = dot;
range.maxpoint = point;
}
} }
return range; 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 a_range = _internal_collision_get_range_on_axis(self, axis);
Range b_range = _internal_collision_get_range_on_axis(other, 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) if(a_range.min <= b_range.max && a_range.min <= b_range.max) {
return vmulff(axis, fminf(b_range.max - b_range.min, b_range.min - a_range.max)); return vmulff(axis, fminf(a_range.max - b_range.min, b_range.min - a_range.max));
else } else {
return ZeroVector; return ZeroVector;
}
} }
static 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 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); Vector b = shape_get_point_transformed(self.tc->get_shape(self.data), next_index, *self_transform);
// the direction of the line // the direction of the line
Vector diff = vsubf(b, a); Vector normal = vperpendicularf(vsubf(b, a));
// the smallest escape vector on this axis // 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); float sqr_mag = vsqrmagnitudef(escape);
if(sqr_mag < shortest_sqrmag) { if(sqr_mag < shortest_sqrmag) {
shortest_sqrmag = sqr_mag; shortest_sqrmag = sqr_mag;
shortest_escape = escape; shortest_escape = escape;
shortest_escape_edge = point_index; shortest_escape_edge = point_index;
} }
if(sqr_mag == 0) {
return 0;
}
} }
RigidBody* rba = self.tc->get_rigidbody(self.data); 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) { *out = (Collision) {
.other = other, .other = other,
@ -79,7 +93,7 @@ int _internal_collision_get_overlap(PhysicsEntity self, PhysicsEntity other, Col
.point = InfinityVector, .point = InfinityVector,
.normal = vnormalizedf(shortest_escape), .normal = vnormalizedf(shortest_escape),
.velocity = vsubf(rigidbody_get_velocity(rba), rigidbody_get_velocity(rbb)), .velocity = velocity,
.separation_force = shortest_escape, .separation_force = shortest_escape,
.edge_left = shape_get_point_transformed(self_shape, shortest_escape_edge, *self_transform), .edge_left = shape_get_point_transformed(self_shape, shortest_escape_edge, *self_transform),

View file

@ -11,59 +11,31 @@
#include "player.h" #include "player.h"
#include <assert.h> #include <assert.h>
static Spritesheet* spr_player_standing = NULL;
static Player* player = NULL; static Player* player = NULL;
static Level* level = 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 static
void play() { void play() {
spr_player_standing = spritesheet_load("assets/sprites/player.png", (IVector){128, 128});
g_camera.fov = 40; g_camera.fov = 40;
player = malloc(sizeof(Player)); player = malloc(sizeof(Player));
player->transform.scale = (Vector){4.f, 4.f}; player_spawn(player, ZeroVector);
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);
level = level_load("level_0"); 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 static
void tick() { void tick() {
g_camera.transform.position = vaddf(g_camera.transform.position, cam_speed); Player_as_BehaviourEntity(player).tc->update(player, delta_time());
player->transform.position = g_camera.transform.position; g_camera.transform.position = player->transform.position;
} }
static static
void draw() { void draw() {
level_draw(level); level_draw(level);
sprite_entity_draw(Player_as_SpriteEntity(player)); sprite_entity_draw(Player_as_SpriteEntity(player));
shape_draw(player->shape, player->transform);
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {

View file

@ -1,6 +1,7 @@
#include "level.h" #include "level.h"
#include "assets.h" #include "assets.h"
#include "debug.h" #include "debug.h"
#include "physics_world.h"
#include "tilemap.h" #include "tilemap.h"
#include <cjson/cJSON.h> #include <cjson/cJSON.h>
#include <stdio.h> #include <stdio.h>
@ -23,6 +24,11 @@ static
void _deallocate_level(void* self_void) { void _deallocate_level(void* self_void) {
Level* self = 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); free(self);
} }
@ -84,6 +90,7 @@ Level* level_from_json(cJSON* json) {
// load a tilemap as an autolayer // load a tilemap as an autolayer
LOG_INFO("loading autolayer"); LOG_INFO("loading autolayer");
*next_tilemap = tilemap_from_autolayer(layer); *next_tilemap = tilemap_from_autolayer(layer);
physics_world_add_map(*next_tilemap);
++next_tilemap; ++next_tilemap;
} else if(strcmp(type->valuestring, "Entities")) { } else if(strcmp(type->valuestring, "Entities")) {
// load entities // load entities
@ -123,3 +130,12 @@ void level_draw(Level* self) {
tilemap_draw(*map, self->transform); 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;
}

View file

@ -5,6 +5,8 @@
#include <SDL2/SDL_render.h> #include <SDL2/SDL_render.h>
#include <cjson/cJSON.h> #include <cjson/cJSON.h>
typedef struct Tilemap Tilemap;
typedef struct Level Level; typedef struct Level Level;
extern void world_init(); extern void world_init();
@ -16,4 +18,7 @@ extern void level_destroy(Level* self);
extern void level_draw(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 #endif // !_fencer_level_h

View file

@ -125,10 +125,12 @@ void _internal_physics_narrow_collision() {
for(PhysicsEntity* left = _world_bodies; left < end; ++left) { for(PhysicsEntity* left = _world_bodies; left < end; ++left) {
for(PhysicsEntity* right = _world_bodies; right < half_end; ++right) { 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); if(collision_check(*left, *right, &collision_left, &collision_right)) {
right->tc->on_collision(right->data, 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_a;
Collision collision_b; Collision collision_b;
PhysicsEntity tileentity; PhysicsEntity tileentity;
RigidBody* entity_body = entity.tc->get_rigidbody(entity.data);
size_t len = tilemap_get_tile_count(map); size_t len = tilemap_get_tile_count(map);
for(size_t i = 0; i < len; ++i) { 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)) { if(collision_check(entity, tileentity, &collision_a, &collision_b)) {
entity.tc->on_collision(entity.data, collision_a); 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 static
void _internal_physics_move() { void _internal_physics_apply() {
PhysicsEntity* end = _world_bodies + _world_bodies_len; PhysicsEntity* end = _world_bodies + _world_bodies_len;
RigidBody* body = NULL; RigidBody* body = NULL;
for(PhysicsEntity* entity = _world_bodies; entity < end; ++entity) { for(PhysicsEntity* entity = _world_bodies; entity < end; ++entity) {
body = entity->tc->get_rigidbody(entity->data); body = entity->tc->get_rigidbody(entity->data);
rigidbody_solve_contacts(body);
rigidbody_apply_physics(body); rigidbody_apply_physics(body);
} }
} }
@ -175,5 +180,5 @@ void physics_world_tick() {
// _internal_physics_broad_collision(); // _internal_physics_broad_collision();
_internal_physics_narrow_collision(); _internal_physics_narrow_collision();
_internal_physics_tilemap_collision(); _internal_physics_tilemap_collision();
_internal_physics_move(); _internal_physics_apply();
} }

View file

@ -1,37 +1,60 @@
#include "player.h" #include "player.h"
#include "assets.h"
#include "debug.h" #include "debug.h"
#include "program.h" #include "program.h"
#include "rigidbody.h" #include "rigidbody.h"
#include "input.h" #include "input.h"
#include "physics_world.h"
static Vector directional = ZeroVector; static Vector directional = ZeroVector;
static Spritesheet* spr_player_standing = NULL;
static static
void player_input_h(int val) { void player_input_h(int val) {
directional.x = val * 0.1f; directional.x = val * 10.f;
} }
static static
void player_input_v(int val) { void player_input_v(int val) {
directional.y = -val * 0.1f; directional.y = -val * 10.f;
} }
void player_spawn(Player* self, Vector at) { void player_spawn(Player* self, Vector at) {
player_start(self);
self->transform.position = at; 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) { 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 = 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) { void player_update(Player* self, float dt) {
Vector velocity = rigidbody_get_velocity(self->rigidbody); Vector velocity = rigidbody_get_velocity(self->rigidbody);
ASSERT_RETURN(!visnanf(velocity),, "Velocity is NaN (2)"); Vector velocity_target = {directional.x, directional.y};
velocity = vmovetowardsf(velocity, vmulff(directional, 100.f), 10000.f * dt); rigidbody_accelerate(self->rigidbody, vmulff(vsubf(velocity_target, velocity), 200.f));
ASSERT_RETURN(!visnanf(velocity),, "Velocity is NaN (3)");
rigidbody_set_velocity(self->rigidbody, velocity);
} }
void player_collision(Player* self, Collision hit) { void player_collision(Player* self, Collision hit) {

View file

@ -11,6 +11,7 @@
#include <SDL2/SDL_image.h> #include <SDL2/SDL_image.h>
SDL_Window* g_window; SDL_Window* g_window;
static double _target_delta_time = 0.0;
static double _delta_time; static double _delta_time;
static double _frame_start; static double _frame_start;
static double _game_start_time; static double _game_start_time;
@ -35,9 +36,10 @@ double get_time_s() {
int program_run(const struct ProgramSettings* settings) { int program_run(const struct ProgramSettings* settings) {
LOG_INFO("Starting program..."); LOG_INFO("Starting program...");
float target_dt = 1.0f/settings->target_fps;
if(settings->target_fps <= 0) { 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(); _game_start_time = _frame_start = get_time_s();
SDL_Init(INITFLAGS); SDL_Init(INITFLAGS);
@ -62,19 +64,20 @@ int program_run(const struct ProgramSettings* settings) {
LOG_INFO("Starting program loop"); LOG_INFO("Starting program loop");
for(;;) { for(;;) {
render_present(); 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(); settings->on_draw();
physics_world_tick(); SDL_Delay(1);
} }
LOG_ERROR("Failed to exit"); LOG_ERROR("Failed to exit");
@ -122,5 +125,5 @@ void program_handle_windowevent(SDL_WindowEvent* event) {
inline inline
float delta_time() { float delta_time() {
return _delta_time; return _target_delta_time == 0 ? _delta_time : _target_delta_time;
} }

View file

@ -1,37 +1,156 @@
#include "rigidbody.h" #include "rigidbody.h"
#include "camera.h"
#include "debug.h"
#include "program.h" #include "program.h"
#include "collision.h"
typedef struct {
Collision hit;
float warming;
int expiry;
} Contact;
struct RigidBody { struct RigidBody {
Transformable transformable; Transformable transformable;
float mass; float mass;
Vector linear_velocity;
Vector linear_force; Vector linear_force;
Vector linear_velocity;
int is_static; int is_static;
List contacts;
}; };
RigidBody* rigidbody_make(Transformable transform) { RigidBody* rigidbody_make(Transformable transform) {
RigidBody* self = malloc(sizeof(RigidBody)); RigidBody* self = malloc(sizeof(RigidBody));
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for rigidbody");
*self = (RigidBody){ *self = (RigidBody){
.transformable = transform, .transformable = transform,
.mass = 0.0f, .mass = 1.0f,
.linear_velocity = ZeroVector, .linear_velocity = ZeroVector,
.linear_force = ZeroVector, .is_static = 0,
.is_static = 0 .contacts = list_from_type(Contact),
}; };
return self; return self;
} }
void rigidbody_destroy(RigidBody* self) { void rigidbody_destroy(RigidBody* self) {
list_empty(&self->contacts);
free(self); 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) { void rigidbody_apply_physics(RigidBody* self) {
Vector position = transformable_get_position(self->transformable);
Vector velocity = vmulff(self->linear_velocity, delta_time()); 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) { 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) { void rigidbody_add_impulse(RigidBody* self, Vector force) {
self->linear_force = vaddf(self->linear_force, force); self->linear_force = vaddf(self->linear_force, force);
self->linear_velocity = vaddf(self->linear_velocity, force);
} }
void rigidbody_accelerate(RigidBody* self, Vector force) { void rigidbody_accelerate(RigidBody* self, Vector force) {
force = vmulff(force, delta_time()); rigidbody_add_impulse(self, vmulff(force, delta_time()));
self->linear_force = vaddf(self->linear_force, force);
} }
int rigidbody_is_static(const RigidBody* self) { 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) { void rigidbody_set_velocity(RigidBody* self, Vector velocity) {
self->linear_force = vaddf(self->linear_force, vsubf(velocity, self->linear_force));
self->linear_velocity = velocity; self->linear_velocity = velocity;
} }

View file

@ -3,6 +3,9 @@
#include "shape.h" #include "shape.h"
#include "transformable.h" #include "transformable.h"
#include "list.h"
struct Collision;
typedef struct RigidBody RigidBody; typedef struct RigidBody RigidBody;
@ -10,6 +13,9 @@ typedef struct RigidBody RigidBody;
RigidBody* rigidbody_make(Transformable transform); RigidBody* rigidbody_make(Transformable transform);
void rigidbody_destroy(RigidBody* self); 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); void rigidbody_apply_physics(RigidBody* self);
float rigidbody_get_mass(const RigidBody* self); float rigidbody_get_mass(const RigidBody* self);

View file

@ -1,5 +1,8 @@
#include "shape.h" #include "shape.h"
#include "camera.h"
#include "debug.h" #include "debug.h"
#include "render.h"
#include <SDL2/SDL_render.h>
#include <stdlib.h> #include <stdlib.h>
struct Shape { struct Shape {
@ -86,9 +89,9 @@ Shape* shape_new(const Vector* points, size_t points_len) {
Shape* shape_new_square(Vector size) { Shape* shape_new_square(Vector size) {
return shape_new((Vector[4]){ return shape_new((Vector[4]){
ZeroVector, ZeroVector,
(Vector){0.f, size.y}, (Vector){size.x, 0.f},
size, size,
(Vector){size.x, 0.f} (Vector){0.f, size.y},
}, 4); }, 4);
} }
@ -182,3 +185,23 @@ Vector shape_get_median_point(Shape* self) {
int shape_is_convex(Shape* self) { int shape_is_convex(Shape* self) {
return self->is_convex; 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);
}
}

View file

@ -25,5 +25,6 @@ extern Vector shape_remove_point(Shape* self, size_t at);
extern Vector shape_get_median_point(Shape* self); extern Vector shape_get_median_point(Shape* self);
extern int shape_is_convex(Shape* self); extern int shape_is_convex(Shape* self);
extern void shape_draw(Shape* self, Transform transform);
#endif // !_fencer_shape_h #endif // !_fencer_shape_h

View file

@ -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."); ASSERT_RETURN(self != NULL, NULL, "Failed to allocate memory for new sprite.");
self->spritesheet = sheet; self->spritesheet = sheet;
self->origin = (Vector){0.5f, 1.0f}; self->origin = (Vector){0.5f, 0.5f};
self->current_frame = initial_frame; self->current_frame = initial_frame;
// TODO: replace with a getter for the current game time. // TODO: replace with a getter for the current game time.
self->current_frame_time = 0; self->current_frame_time = 0;

View file

@ -1,5 +1,5 @@
#include "sprite_entity.h" #include "sprite_entity.h"
#include "sprite.h" #include "sprite.h"
void sprite_entity_draw(SpriteEntity self) { void sprite_entity_draw(SpriteEntity self) {
Sprite* sprite = self.tc->get_sprite(self.data); Sprite* sprite = self.tc->get_sprite(self.data);

View file

@ -1,5 +1,6 @@
#include "tilemap.h" #include "tilemap.h"
#include "debug.h" #include "debug.h"
#include "rigidbody.h"
#include "tileset.h" #include "tileset.h"
#include "assets.h" #include "assets.h"
#include "camera.h" #include "camera.h"
@ -63,6 +64,7 @@ Tilemap* tilemap_from_autolayer(cJSON* json) {
free(self); free(self);
RETURN_ERROR(NULL, "Failed to allocate map memory"); 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; const double px_to_ws = 1.0 / grid->valuedouble;
@ -80,17 +82,30 @@ Tilemap* tilemap_from_autolayer(cJSON* json) {
return self; return self;
} }
void tilemap_destroy(Tilemap* self) {
free(self->map);
rigidbody_destroy(self->rigidbody);
free(self);
}
void tilemap_draw(Tilemap* self, Transform transform) { void tilemap_draw(Tilemap* self, Transform transform) {
Transform tiletrans = transform_apply(transform, self->transform); Transform tiletrans = transform_apply(transform, self->transform);
TileInstance* tile; 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) { for(int i = 0; i < self->map_num; ++i) {
tile = self->map + i; tile = self->map + i;
tiletrans = transform_apply(self->transform, tile->transform); tiletrans = transform_apply(self->transform, tile->transform);
Sprite* sprite = tiledef_get_sprite(tile->tiledef); Sprite* sprite = tiledef_get_sprite(tile->tiledef);
if(sprite != NULL) if(sprite != NULL) {
sprite_draw(sprite, tiletrans); sprite_draw(sprite, tiletrans);
}
} }
} }
@ -102,6 +117,10 @@ TileInstance* tilemap_get_tile(Tilemap* self, size_t at) {
return &self->map[at]; return &self->map[at];
} }
Transform* tilemap_get_transform(Tilemap* self ) {
return &self->transform;
}
RigidBody* tile_instance_get_rigidbody(TileInstance* self) { RigidBody* tile_instance_get_rigidbody(TileInstance* self) {
return self->parent_map->rigidbody; return self->parent_map->rigidbody;
} }

View file

@ -22,20 +22,20 @@ extern void tilemap_draw(Tilemap* self, Transform transform);
extern size_t tilemap_get_tile_count(Tilemap* self); extern size_t tilemap_get_tile_count(Tilemap* self);
extern TileInstance* tilemap_get_tile(Tilemap* self, size_t at); 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 RigidBody* tile_instance_get_rigidbody(TileInstance* self);
extern Transform* tile_instance_get_transform(TileInstance* self); extern Transform* tile_instance_get_transform(TileInstance* self);
extern Shape* tile_instance_get_shape(TileInstance* self); extern Shape* tile_instance_get_shape(TileInstance* self);
extern void tile_instance_on_collision(TileInstance* self, Collision collision); 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); impl_Transformable_for(Tilemap,
extern float* tile_instance_get_rotation(TileInstance* self); tilemap_get_transform
)
impl_Transformable_for(TileInstance, impl_Transformable_for(TileInstance,
tile_instance_get_transform, tile_instance_get_transform
tile_instance_get_position,
tile_instance_get_scale,
tile_instance_get_rotation
) )
impl_PhysicsEntity_for(TileInstance, impl_PhysicsEntity_for(TileInstance,
tile_instance_get_rigidbody, tile_instance_get_rigidbody,

View file

@ -122,6 +122,7 @@ Tileset* tileset_from_json(cJSON* json) {
.sprite = sprite_from_spritesheet(self->atlas, tid), .sprite = sprite_from_spritesheet(self->atlas, tid),
.collision = shape_new_square(OneVector) .collision = shape_new_square(OneVector)
}; };
sprite_set_origin(self->tiledefs[tid].sprite, ZeroVector);
// TODO: generate/read collision information // TODO: generate/read collision information
} }

View file

@ -51,26 +51,8 @@ Transform* transform_get_transform(Transform* self) {
return 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, impl_Transformable_for(Transform,
transform_get_transform, transform_get_transform
transform_get_position,
transform_get_scale,
transform_get_rotation
); );
#endif // !_fencer_transform_h #endif // !_fencer_transform_h

41
src/transformable.c Normal file
View file

@ -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;
}

View file

@ -6,9 +6,6 @@
typedef struct { typedef struct {
struct Transform* (*const get_transform)(void* self); 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; } ITransformable;
typedef struct { typedef struct {
@ -16,16 +13,23 @@ typedef struct {
ITransformable const* tc; ITransformable const* tc;
} Transformable; } 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) {\ static inline Transformable T##_as_Transformable(T* x) {\
TC_FN_TYPECHECK(Transform*, get_transform_f, T*);\ 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 = {\ static ITransformable const tc = {\
.get_transform = (Transform*(*const)(void*)) get_transform_f,\ .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\
};\ };\
return (Transformable){.tc = &tc, .data = x};\ return (Transformable){.tc = &tc, .data = x};\
} }

View file

@ -46,6 +46,15 @@ typedef struct IVector {
// Floating point vector maths functions. // Floating point vector maths functions.
/// ///
static inline 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) { Vector vaddf(Vector a, Vector b) {
return (Vector){a.x + b.x, a.y + b.y}; return (Vector){a.x + b.x, a.y + b.y};
} }
@ -71,14 +80,20 @@ Vector vperpendicularf(Vector a) {
} }
static inline static inline
float vmagnitudef(Vector a) { 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); return sqrtf(a.x*a.x + a.y*a.y);
} }
static inline static inline
float vsqrmagnitudef(Vector a) { 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 static inline
Vector vnormalizedf(Vector a) { Vector vnormalizedf(Vector a) {
if(veqf(a, ZeroVector)) return ZeroVector;
return vmulff(a, 1.0/vmagnitudef(a)); return vmulff(a, 1.0/vmagnitudef(a));
} }
static inline static inline
@ -104,6 +119,22 @@ Vector vrotatef(Vector a, float t) {
sinf(t) * a.x + cosf(t) * a.y 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 static inline
IVector vaddi(IVector a, IVector b) { IVector vaddi(IVector a, IVector b) {