new build system config
This commit is contained in:
parent
96867d6e1b
commit
5ab93d62ad
50 changed files with 81 additions and 46 deletions
32
core/Build-Core.lua
Normal file
32
core/Build-Core.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
project "Fencer-Core"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
targetdir "bin/%{cfg.buildcfg}"
|
||||
staticruntime "off"
|
||||
|
||||
files { "src/**.c" }
|
||||
includedirs { "src/" }
|
||||
|
||||
targetdir ( "../bin" .. OutputDir .. "/%{prj.name}" )
|
||||
objdir ( "../intermediate" .. OutputDir .. "/%{prj.name}" )
|
||||
|
||||
filter "system:windows"
|
||||
systemversion "latest"
|
||||
defines {}
|
||||
|
||||
filter "configurations:Debug"
|
||||
defines { "DEBUG" }
|
||||
runtime "Debug"
|
||||
symbols "On"
|
||||
|
||||
filter "configurations:Release"
|
||||
defines { "RELEASE" }
|
||||
runtime "Release"
|
||||
optimize "On"
|
||||
symbols "On"
|
||||
|
||||
filter "configurations:Dist"
|
||||
defines { "DIST" }
|
||||
runtime "Release"
|
||||
optimize "On"
|
||||
symbols "Off"
|
||||
34
core/src/asset.h
Normal file
34
core/src/asset.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef _fencer_asset_h
|
||||
#define _fencer_asset_h
|
||||
|
||||
#include "drop.h"
|
||||
#include "typeclass_helpers.h"
|
||||
#include <SDL2/SDL_render.h>
|
||||
|
||||
typedef uintptr_t asset_id;
|
||||
|
||||
typedef struct {
|
||||
asset_id (*const get_id)(void*);
|
||||
void (*const set_id)(void*, asset_id);
|
||||
} IAsset;
|
||||
|
||||
typedef struct Asset {
|
||||
void* data;
|
||||
IAsset const* tc;
|
||||
IDrop const* drop;
|
||||
} Asset;
|
||||
|
||||
#define impl_Asset_for(T, get_id_f, set_id_f)\
|
||||
static inline Asset T##_as_Asset(T* x) {\
|
||||
TC_FN_TYPECHECK(asset_id, get_id_f, T*);\
|
||||
TC_FN_TYPECHECK(void, set_id_f, T*, asset_id);\
|
||||
TC_FN_TYPECHECK(Drop, T##_as_Drop, T*);\
|
||||
static IAsset tc = (IAsset){\
|
||||
.get_id = (asset_id(*const)(void*)) get_id_f,\
|
||||
.set_id = (void(*const)(void*,asset_id)) set_id_f,\
|
||||
};\
|
||||
IDrop const* drop = T##_as_Drop(x).tc;\
|
||||
return (Asset){.data=x, .tc=&tc, .drop=drop};\
|
||||
}
|
||||
|
||||
#endif // !_fencer_asset_h
|
||||
132
core/src/assets.c
Normal file
132
core/src/assets.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "assets.h"
|
||||
#include "debug.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static List _assets;
|
||||
static asset_id _next_id = 0;
|
||||
|
||||
static
|
||||
size_t file_length(FILE* fp) {
|
||||
size_t start = ftell(fp);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t r = ftell(fp);
|
||||
fseek(fp, start, SEEK_SET);
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
void read_file(FILE* fp, char* out_buffer, size_t out_size) {
|
||||
size_t start = ftell(fp);
|
||||
fread(out_buffer, 1, out_size, fp);
|
||||
fseek(fp, start, SEEK_SET);
|
||||
}
|
||||
|
||||
void assets_init() {
|
||||
LOG_INFO("assets_init");
|
||||
_assets = list_from_type(Asset);
|
||||
_next_id = 1;
|
||||
}
|
||||
|
||||
// clear all assets without shrinking the assets array.
|
||||
static
|
||||
void _empty_assets() {
|
||||
list_foreach(Asset*, asset, &_assets) {
|
||||
asset->drop->drop(asset->data);
|
||||
}
|
||||
list_empty(&_assets);
|
||||
}
|
||||
|
||||
void assets_clean() {
|
||||
_empty_assets();
|
||||
}
|
||||
|
||||
void assets_reset() {
|
||||
_empty_assets();
|
||||
}
|
||||
|
||||
asset_id store_asset(Asset asset) {
|
||||
list_reserve(&_assets, _assets.len + 1);
|
||||
size_t index = list_add(&_assets, &asset);
|
||||
Asset* new_asset = list_at_as(Asset, &_assets, index);
|
||||
asset_id id = _next_id++;
|
||||
new_asset->tc->set_id(new_asset->data, id);
|
||||
asset_id actual_id = new_asset->tc->get_id(new_asset->data);
|
||||
ASSERT_RETURN(actual_id == id,actual_id, "ID stored in asset not the same as the expected id");
|
||||
return actual_id;
|
||||
}
|
||||
|
||||
static
|
||||
Asset* _internal_get_by_id(asset_id id) {
|
||||
ASSERT_RETURN_WARN(id != 0, NULL, "Cannot get element with id 0");
|
||||
|
||||
list_foreach(Asset*, asset, &_assets) {
|
||||
if(asset->tc->get_id(asset->data) == id) {
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_WARNING(NULL, "Failed to find requested asset with id %zu", id);
|
||||
}
|
||||
|
||||
void* get_asset(asset_id id) {
|
||||
Asset* found = _internal_get_by_id(id);
|
||||
ASSERT_RETURN(found != NULL, NULL, "Failed to find asset with id %zu.", id);
|
||||
return found->data;
|
||||
}
|
||||
|
||||
asset_id get_asset_id(void* asset) {
|
||||
list_foreach(Asset*, asset, &_assets) {
|
||||
if(asset->data == asset) {
|
||||
return asset->tc->get_id(asset->data);
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_WARNING(0, "Failed to find asset referencing %p", asset);
|
||||
}
|
||||
|
||||
void free_asset(asset_id id) {
|
||||
Asset* found;
|
||||
size_t found_index;
|
||||
for(size_t i = 0; i < _assets.len; ++i) {
|
||||
found = list_at_as(Asset, &_assets, i);
|
||||
if(found->tc->get_id(found->data) == id) {
|
||||
found_index = i;
|
||||
break;
|
||||
} else {
|
||||
found = NULL;
|
||||
}
|
||||
}
|
||||
ASSERT_RETURN(found != NULL,, "Attempt to free nonexistent asset.");
|
||||
found->drop->drop(found->data);
|
||||
list_erase(&_assets, found_index);
|
||||
}
|
||||
|
||||
cJSON* load_json_from_file(const char* filename) {
|
||||
FILE* fp = fopen(filename, "r");
|
||||
ASSERT_RETURN(fp != NULL, NULL, "Failed to open file %s.", filename);
|
||||
|
||||
size_t len = file_length(fp);
|
||||
char* buffer = malloc(len + 1);
|
||||
ASSERT_RETURN(buffer != NULL, NULL, "Failed to allocate buffer for json string.");
|
||||
|
||||
read_file(fp, buffer, len);
|
||||
cJSON* out = cJSON_Parse(buffer);
|
||||
free(buffer);
|
||||
|
||||
ASSERT_RETURN(out != NULL, NULL, "Failed to parse json from file %s.", filename);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t json_array_len(cJSON* json) {
|
||||
ASSERT_RETURN(cJSON_IsArray(json), 0, "Tried to get the length of a non-array json element.");
|
||||
size_t len = 0;
|
||||
cJSON* itr;
|
||||
cJSON_ArrayForEach(itr, json) {
|
||||
++len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
46
core/src/assets.h
Normal file
46
core/src/assets.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef _fencer_assets_h
|
||||
#define _fencer_assets_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "asset.h"
|
||||
#include <cjson/cJSON.h>
|
||||
|
||||
typedef void(*AssetDestructor)(void*);
|
||||
|
||||
// Prepare asset management.
|
||||
// Other asset management functions are invalid until assets_init is called.
|
||||
void assets_init();
|
||||
// Clean up and shut down assets management.
|
||||
void assets_clean();
|
||||
// Clear all loaded assets
|
||||
void assets_reset();
|
||||
// Submit an object to be managed as an asset. If the destructor is NULL, free will be used.
|
||||
// Returns zero if asset could not be stored.
|
||||
asset_id store_asset(Asset drop);
|
||||
// Get an asset already submitted to the asset manager.
|
||||
// Returns 0 if asset does not exist.
|
||||
void* get_asset(asset_id id);
|
||||
// Get an asset id, returns 0 if no such asset exists.
|
||||
asset_id get_asset_id(void* asset);
|
||||
// Free an asset managed by the asset manager using it's destructor, or free(..) if NULL.
|
||||
void free_asset(asset_id id);
|
||||
// load a file's contents into json
|
||||
cJSON* load_json_from_file(const char* filepath);
|
||||
// Get the length of a cJSON array
|
||||
size_t json_array_len(cJSON* array);
|
||||
static inline
|
||||
Vector json_array_to_vector(cJSON* array) {
|
||||
return (Vector) {
|
||||
cJSON_GetArrayItem(array, 0)->valuedouble,
|
||||
cJSON_GetArrayItem(array, 1)->valuedouble,
|
||||
};
|
||||
}
|
||||
static inline
|
||||
IVector json_array_to_ivector(cJSON* array) {
|
||||
return (IVector) {
|
||||
cJSON_GetArrayItem(array, 0)->valueint,
|
||||
cJSON_GetArrayItem(array, 1)->valueint
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !_fencer_assets_h
|
||||
37
core/src/behaviour_entity.h
Normal file
37
core/src/behaviour_entity.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef _update_entity_h
|
||||
#define _update_entity_h
|
||||
|
||||
#include "drop.h"
|
||||
#include "typeclass_helpers.h"
|
||||
#include "vmath.h"
|
||||
|
||||
typedef struct {
|
||||
void (*const spawn)(void* self, Vector at);
|
||||
void (*const update)(void* self, float dt);
|
||||
void (*const start)(void* self);
|
||||
void (*const draw)(void* self);
|
||||
} IEntityBehaviour;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
IEntityBehaviour const* tc;
|
||||
IDrop const* drop;
|
||||
} BehaviourEntity;
|
||||
|
||||
#define impl_BehaviourEntity_for(T, start_f, update_f, spawn_f, draw_f)\
|
||||
static inline BehaviourEntity T##_as_BehaviourEntity(T* x) {\
|
||||
TC_FN_TYPECHECK(void, start_f, T*);\
|
||||
TC_FN_TYPECHECK(void, update_f, T*, float);\
|
||||
TC_FN_TYPECHECK(void, draw_f, T*);\
|
||||
static IEntityBehaviour const tc = {\
|
||||
.spawn = (void(*const)(void*, Vector)) spawn_f,\
|
||||
.update = (void(*const)(void*, float)) update_f,\
|
||||
.start = (void(*const)(void*)) start_f,\
|
||||
.draw = (void(*const)(void*)) draw_f,\
|
||||
};\
|
||||
TC_FN_TYPECHECK(Drop, T##_as_Drop, T*);\
|
||||
IDrop const* drop = T##_as_Drop(x).tc;\
|
||||
return (BehaviourEntity){.tc = &tc, .drop = drop, .data = x};\
|
||||
}\
|
||||
|
||||
#endif // !_update_entity_h
|
||||
46
core/src/camera.c
Normal file
46
core/src/camera.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "camera.h"
|
||||
#include "debug.h"
|
||||
#include "render.h"
|
||||
|
||||
Camera g_camera;
|
||||
|
||||
void camera_init() {
|
||||
LOG_INFO("camera_init");
|
||||
g_camera.transform = IdentityTransform;
|
||||
g_camera.fov = 10;
|
||||
}
|
||||
|
||||
static inline
|
||||
float _camera_height(Camera* self) {
|
||||
return self->fov * ((float)g_render_resolution.y / (float)g_render_resolution.x);
|
||||
}
|
||||
|
||||
SDL_FRect camera_world_to_pixel_rect(Camera* self, SDL_FRect* world_space) {
|
||||
Transform t = self->transform;
|
||||
t.rotation = 0;
|
||||
t.scale = OneVector;
|
||||
t = transform_invert(t);
|
||||
|
||||
Vector tl = {world_space->x + (self->fov / 2.0), world_space->y + (_camera_height(self) / 2.0)};
|
||||
Vector size = {world_space->w, world_space->h};
|
||||
|
||||
tl = vmulff(transform_point(&t, tl), g_render_resolution.x / self->fov);
|
||||
size = vmulff(vmulf(t.scale, size), g_render_resolution.x / self->fov);
|
||||
|
||||
|
||||
return (SDL_FRect){
|
||||
tl.x, tl.y,
|
||||
size.x, size.y
|
||||
};
|
||||
}
|
||||
|
||||
Vector camera_world_to_pixel_point(Camera* self, Vector point) {
|
||||
Transform t = self->transform;
|
||||
t.rotation = 0;
|
||||
t.scale = OneVector;
|
||||
t = transform_invert(t);
|
||||
|
||||
point = (Vector){point.x + (self->fov / 2.0), point.y + (_camera_height(self) / 2.0)};
|
||||
|
||||
return vmulff(transform_point(&t, point), g_render_resolution.x / self->fov);
|
||||
}
|
||||
23
core/src/camera.h
Normal file
23
core/src/camera.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _fencer_camera_h
|
||||
#define _fencer_camera_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "transform.h"
|
||||
#include <SDL2/SDL_rect.h>
|
||||
|
||||
struct Camera {
|
||||
float fov;
|
||||
Transform transform;
|
||||
};
|
||||
|
||||
typedef struct Camera Camera;
|
||||
|
||||
// current world location of camera
|
||||
extern Camera g_camera;
|
||||
|
||||
extern void camera_init();
|
||||
extern SDL_FRect camera_screen_to_world_space(Camera* self, SDL_FRect* camera_space);
|
||||
extern SDL_FRect camera_world_to_pixel_rect(Camera* self, SDL_FRect* world_space);
|
||||
extern Vector camera_world_to_pixel_point(Camera* self, Vector point);
|
||||
|
||||
#endif // !_fencer_camera_h
|
||||
141
core/src/collision.c
Normal file
141
core/src/collision.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#include "collision.h"
|
||||
#include "vmath.h"
|
||||
#include "rigidbody.h"
|
||||
|
||||
// =====================================================
|
||||
// Shape overlap test using the separating axis theorem
|
||||
// =====================================================
|
||||
|
||||
typedef struct Range {float min; Vector minpoint; float max; Vector maxpoint; } Range;
|
||||
|
||||
static
|
||||
Range _internal_collision_get_range_on_axis(PhysicsEntity self, Vector axis) {
|
||||
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, point, dot, point};
|
||||
|
||||
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 = dot;
|
||||
range.minpoint = point;
|
||||
}
|
||||
if(dot > range.max) {
|
||||
range.max = dot;
|
||||
range.maxpoint = point;
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static
|
||||
Vector _internal_collision_overlap_on_axis(PhysicsEntity self, PhysicsEntity other, Vector axis, Vector* out_point) {
|
||||
Range a_range = _internal_collision_get_range_on_axis(self, axis);
|
||||
Range b_range = _internal_collision_get_range_on_axis(other, axis);
|
||||
|
||||
const float overlap_left = a_range.max - b_range.min;
|
||||
const float overlap_right = b_range.min - a_range.max;
|
||||
*out_point = fabsf(overlap_right) <= fabsf(overlap_left) ? a_range.maxpoint : a_range.minpoint;
|
||||
if(a_range.min <= b_range.max && b_range.min <= a_range.max) {
|
||||
const float shortest = fminf(overlap_left, overlap_right);
|
||||
return vmulff(axis, shortest);
|
||||
} else {
|
||||
return ZeroVector;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int _internal_collision_get_overlap(PhysicsEntity self, PhysicsEntity other, Collision* out) {
|
||||
// get components used
|
||||
Shape* self_shape = self.tc->get_shape(self.data);
|
||||
Transform* self_transform = self.transformable->get_transform(self.data);
|
||||
|
||||
// the shortest distance to solve collision found so far
|
||||
Vector shortest_escape = InfinityVector;
|
||||
// the squared length of the shortest escape vector found so far
|
||||
float shortest_dot = INFINITY;
|
||||
// the first index of the points on the edge
|
||||
size_t shortest_escape_edge = 0;
|
||||
// the number of points in the shape of self
|
||||
size_t self_point_count = shape_get_points_count(self_shape);
|
||||
|
||||
for(size_t point_index = 0; point_index < self_point_count; ++point_index) {
|
||||
// the next point on the line
|
||||
size_t next_index = (point_index + 1) % self_point_count;
|
||||
// get the two points defining the collision edge
|
||||
Vector edge_lhs = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self_transform);
|
||||
Vector edge_rhs = shape_get_point_transformed(self.tc->get_shape(self.data), next_index, *self_transform);
|
||||
// the direction of the line
|
||||
Vector normal = vnormalizedf(vperpendicularf(vsubf(edge_rhs, edge_lhs)));
|
||||
|
||||
Vector overlap_point;
|
||||
// the smallest escape vector on this axis
|
||||
Vector escape = _internal_collision_overlap_on_axis(self, other, normal, &overlap_point);
|
||||
float dot = vdotf(vinvf(normal), escape);
|
||||
if(dot <= 0.0) {
|
||||
return 0;
|
||||
}
|
||||
if(dot <= shortest_dot) {
|
||||
shortest_dot = dot;
|
||||
shortest_escape = escape;
|
||||
shortest_escape_edge = point_index;
|
||||
}
|
||||
}
|
||||
|
||||
RigidBody* rba = self.tc->get_rigidbody(self.data);
|
||||
RigidBody* rbb = other.tc->get_rigidbody(other.data);
|
||||
const Vector velocity = vsubf(rigidbody_get_velocity(rba), rigidbody_get_velocity(rbb));
|
||||
const Vector normal = vnormalizedf(shortest_escape);
|
||||
Vector world_point = _internal_collision_get_range_on_axis(self, normal).minpoint;
|
||||
|
||||
*out = (Collision) {
|
||||
.other = other,
|
||||
|
||||
.point = inverse_transform_point(rigidbody_get_transform(rba), world_point),
|
||||
.normal = normal,
|
||||
|
||||
.velocity = velocity,
|
||||
.penetration_vector = shortest_escape,
|
||||
|
||||
.edge_left = shape_get_point_transformed(self_shape, shortest_escape_edge, *self_transform),
|
||||
.edge_right = shape_get_point_transformed(self_shape, (1 + shortest_escape_edge) % self_point_count, *self_transform),
|
||||
};
|
||||
|
||||
return !veqf(shortest_escape, ZeroVector);
|
||||
}
|
||||
|
||||
Collision collision_invert(Collision collision_a, PhysicsEntity a) {
|
||||
Vector world_point = _internal_collision_get_range_on_axis(collision_a.other, collision_a.normal).maxpoint;
|
||||
RigidBody* body = collision_a.other.tc->get_rigidbody(collision_a.other.data);
|
||||
return (Collision){
|
||||
.other = a,
|
||||
.point = inverse_transform_point(rigidbody_get_transform(body), world_point),
|
||||
.normal = vinvf(collision_a.normal),
|
||||
.velocity = vinvf(collision_a.velocity),
|
||||
.penetration_vector = vinvf(collision_a.penetration_vector),
|
||||
.edge_left = collision_a.edge_left,
|
||||
.edge_right = collision_a.edge_right,
|
||||
};
|
||||
}
|
||||
|
||||
int collision_check(PhysicsEntity a, PhysicsEntity b, Collision* out_a, Collision* out_b) {
|
||||
Collision collision_a, collision_b;
|
||||
int collision_a_overlaps = _internal_collision_get_overlap(a, b, &collision_a);
|
||||
int collision_b_overlaps = _internal_collision_get_overlap(b, a, &collision_b);
|
||||
|
||||
if(!collision_a_overlaps || !collision_b_overlaps)
|
||||
return 0;
|
||||
|
||||
if(vsqrmagnitudef(collision_a.penetration_vector) <= vsqrmagnitudef(collision_b.penetration_vector))
|
||||
collision_b = collision_invert(collision_a, a);
|
||||
else
|
||||
collision_a = collision_invert(collision_b, b);
|
||||
|
||||
*out_a = collision_a;
|
||||
*out_b = collision_b;
|
||||
return (collision_b_overlaps << 1) | collision_a_overlaps;
|
||||
}
|
||||
24
core/src/collision.h
Normal file
24
core/src/collision.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _fencer_collision_h
|
||||
#define _fencer_collision_h
|
||||
|
||||
#include "shape.h"
|
||||
#include "physics_entity.h"
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct Collision {
|
||||
PhysicsEntity other;
|
||||
|
||||
Vector point;
|
||||
Vector normal;
|
||||
|
||||
Vector velocity;
|
||||
Vector penetration_vector;
|
||||
|
||||
Vector edge_left;
|
||||
Vector edge_right;
|
||||
} Collision;
|
||||
|
||||
extern Collision collision_invert(Collision src, PhysicsEntity new_other);
|
||||
extern int collision_check(PhysicsEntity a, PhysicsEntity b, Collision* out_a, Collision* out_b);
|
||||
|
||||
#endif // !_fencer_collision_h
|
||||
9
core/src/debug.c
Normal file
9
core/src/debug.c
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#include "debug.h"
|
||||
|
||||
#if NDEBUG
|
||||
int g_debug_error_abort = 0;
|
||||
int g_debug_log_lvl = 0;
|
||||
#else
|
||||
int g_debug_error_abort = 1;
|
||||
int g_debug_log_lvl = 3;
|
||||
#endif
|
||||
69
core/src/debug.h
Normal file
69
core/src/debug.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef _fencer_debug_h
|
||||
#define _fencer_debug_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
extern int g_debug_error_abort;
|
||||
extern int g_debug_log_lvl;
|
||||
|
||||
#define LOG_INFO(...) do {\
|
||||
if(g_debug_log_lvl < 3) break;\
|
||||
printf("[%s:%d] INFO | ", __FILE__, __LINE__);\
|
||||
printf(__VA_ARGS__);\
|
||||
printf("\n");\
|
||||
} while(0)
|
||||
|
||||
#define LOG_ERROR(...) do {\
|
||||
if(g_debug_log_lvl >= 1) {\
|
||||
printf("[%s:%d] ERROR | ", __FILE__, __LINE__);\
|
||||
printf(__VA_ARGS__);\
|
||||
printf("\n");\
|
||||
fflush(stdout);\
|
||||
}\
|
||||
if(g_debug_error_abort != 0) abort();\
|
||||
} while(0)
|
||||
|
||||
#define LOG_WARNING(...) do {\
|
||||
if(g_debug_log_lvl < 2) break;\
|
||||
printf("[%s:%d] WARNING | ", __FILE__, __LINE__);\
|
||||
printf(__VA_ARGS__);\
|
||||
printf("\n");\
|
||||
} while(0)
|
||||
|
||||
|
||||
#define RETURN_ERROR(__VALUE, ...) do {\
|
||||
LOG_ERROR(__VA_ARGS__);\
|
||||
return __VALUE;\
|
||||
} while(0)
|
||||
|
||||
#define RETURN_WARNING(__VALUE, ...) do {\
|
||||
LOG_WARNING(__VA_ARGS__);\
|
||||
return __VALUE;\
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_RETURN(__ASSERT, __RETURN, ...) do {\
|
||||
if(!(__ASSERT)) {\
|
||||
LOG_ERROR(__VA_ARGS__);\
|
||||
return __RETURN;\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#define CHECK(__ASSERT, ...) do {\
|
||||
if(!(__ASSERT)) {\
|
||||
LOG_ERROR(__VA_ARGS__);\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_RETURN_WARN(__ASSERT, __RETURN, ...) do {\
|
||||
if(!(__ASSERT)) {\
|
||||
LOG_WARNING(__VA_ARGS__);\
|
||||
return __RETURN;\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#endif // !_fencer_debug_h
|
||||
26
core/src/drop.h
Normal file
26
core/src/drop.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef _fencer_drop_h
|
||||
#define _fencer_drop_h
|
||||
|
||||
typedef struct {
|
||||
void (*const drop)(void* self);
|
||||
} IDrop;
|
||||
|
||||
typedef struct {
|
||||
void const* data;
|
||||
IDrop const* tc;
|
||||
} Drop;
|
||||
|
||||
#define impl_Drop_for(T, drop_f)\
|
||||
static inline Drop T##_as_Drop(T* x) {\
|
||||
TC_FN_TYPECHECK(void, drop_f, T*);\
|
||||
static IDrop const tc = {\
|
||||
.drop = (void(*const)(void*)) drop_f,\
|
||||
};\
|
||||
return (Drop){.tc = &tc, .data = x};\
|
||||
}
|
||||
|
||||
#define impl_default_Drop_for(T)\
|
||||
static void default_drop_##T(T* v) { free(v); }\
|
||||
impl_Drop_for(T, default_drop_##T)
|
||||
|
||||
#endif // !_fencer_drop_h
|
||||
80
core/src/game_world.c
Normal file
80
core/src/game_world.c
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include "game_world.h"
|
||||
|
||||
#include "behaviour_entity.h"
|
||||
#include "list.h"
|
||||
#include "program.h"
|
||||
|
||||
static List _add_queue;
|
||||
static List _remove_queue;
|
||||
static List _game_entities;
|
||||
|
||||
static inline
|
||||
size_t _internal_find_index_for_entity(void* data, const List* list) {
|
||||
for(size_t i = 0; i < _game_entities.len; ++i) {
|
||||
BehaviourEntity* entity = list_at_as(BehaviourEntity, &_game_entities, i);
|
||||
if(entity->data == entity) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return list->len;
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_clear_removed() {
|
||||
list_foreach(size_t*, index, &_remove_queue) {
|
||||
BehaviourEntity* entity = list_at_as(BehaviourEntity, &_game_entities, *index);
|
||||
entity->drop->drop(entity->data);
|
||||
list_erase(&_game_entities, *index);
|
||||
}
|
||||
list_empty(&_remove_queue);
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_process_new() {
|
||||
list_foreach(BehaviourEntity*, entity, &_add_queue) {
|
||||
list_add(&_game_entities, entity);
|
||||
entity->tc->start(entity->data);
|
||||
}
|
||||
list_empty(&_add_queue);
|
||||
}
|
||||
|
||||
void game_world_init() {
|
||||
_game_entities = list_from_type(BehaviourEntity);
|
||||
_add_queue = list_from_type(BehaviourEntity);
|
||||
_remove_queue = list_from_type(size_t);
|
||||
}
|
||||
|
||||
void game_world_close() {
|
||||
_internal_clear_removed();
|
||||
_internal_process_new();
|
||||
list_foreach(BehaviourEntity*, entity, &_game_entities) {
|
||||
entity->drop->drop(entity->data);
|
||||
}
|
||||
list_empty(&_game_entities);
|
||||
}
|
||||
|
||||
void game_world_add_entity(BehaviourEntity entity) {
|
||||
list_add(&_add_queue, &entity);
|
||||
}
|
||||
|
||||
void game_world_remove_entity(void* entity) {
|
||||
size_t index = _internal_find_index_for_entity(entity, &_game_entities);
|
||||
if(index != _game_entities.len) {
|
||||
list_add(&_remove_queue, &index);
|
||||
}
|
||||
}
|
||||
|
||||
void game_world_update() {
|
||||
_internal_process_new();
|
||||
list_foreach(BehaviourEntity*, entity, &_game_entities) {
|
||||
entity->tc->update(entity->data, delta_time());
|
||||
}
|
||||
_internal_clear_removed();
|
||||
}
|
||||
|
||||
void game_word_draw() {
|
||||
list_foreach(BehaviourEntity*, entity, &_game_entities) {
|
||||
entity->tc->draw(entity->data);
|
||||
}
|
||||
}
|
||||
15
core/src/game_world.h
Normal file
15
core/src/game_world.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef _fencer_game_world_h
|
||||
#define _fencer_game_world_h
|
||||
|
||||
#include "behaviour_entity.h"
|
||||
|
||||
extern void game_world_init();
|
||||
extern void game_world_close();
|
||||
|
||||
extern void game_world_add_entity(BehaviourEntity entity);
|
||||
extern void game_world_remove_entity(void* entity);
|
||||
|
||||
extern void game_world_update();
|
||||
extern void game_world_draw();
|
||||
|
||||
#endif // !_fencer_game_world_h
|
||||
103
core/src/input.c
Normal file
103
core/src/input.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include "input.h"
|
||||
#include "input_axis.h"
|
||||
#include "debug.h"
|
||||
|
||||
static List _devices;
|
||||
|
||||
static inline
|
||||
void _internal_open_keyboard() {
|
||||
InputDevice* keyboard = malloc(sizeof(InputDevice));
|
||||
*keyboard = (InputDevice){
|
||||
.listeners = NULL,
|
||||
.type = InputDevice_KBM,
|
||||
.id = -1,
|
||||
.keyboard = {
|
||||
.state = SDL_GetKeyboardState(NULL)
|
||||
}
|
||||
};
|
||||
list_add(&_devices, &keyboard);
|
||||
LOG_INFO("registered keyboard %d", keyboard->id);
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_open_controller(size_t id) {
|
||||
InputDevice* device = malloc(sizeof(InputDevice));
|
||||
*device = (InputDevice) {
|
||||
.listeners = NULL,
|
||||
.type = InputDevice_Gamepad,
|
||||
.id = id,
|
||||
.gamepad = {
|
||||
.id = id,
|
||||
.controller = SDL_GameControllerOpen(id)
|
||||
}
|
||||
};
|
||||
|
||||
list_add(&_devices, &device);
|
||||
LOG_INFO("registered game controller %d", device->id);
|
||||
}
|
||||
|
||||
void input_init() {
|
||||
_devices = list_from_type(InputDevice*);
|
||||
// open the keyboard by default
|
||||
_internal_open_keyboard();
|
||||
|
||||
// open any controllers already available
|
||||
const size_t joystick_count = SDL_NumJoysticks();
|
||||
for(size_t i = 0; i < joystick_count; ++i) {
|
||||
_internal_open_controller(i);
|
||||
}
|
||||
}
|
||||
|
||||
void input_clean() {
|
||||
list_foreach(InputDevice**, _device, &_devices) {
|
||||
InputDevice* device = *_device;
|
||||
if(device->type == InputDevice_Gamepad)
|
||||
SDL_GameControllerClose(device->gamepad.controller);
|
||||
free(device);
|
||||
}
|
||||
list_empty(&_devices);
|
||||
}
|
||||
|
||||
void input_handle_event(SDL_Event event) {
|
||||
list_foreach(InputDevice**, _device, &_devices) {
|
||||
InputDevice* device = *_device;
|
||||
|
||||
if(device->listeners != NULL) {
|
||||
list_foreach(InputListener*, listener, device->listeners) {
|
||||
InputAxis axis = listener->axis;
|
||||
|
||||
if(axis.tc->is_changed_by(axis.data, event)) {
|
||||
InputEvent value = axis.tc->evaluate(axis.data, event);
|
||||
listener->fn(listener->self, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_device_set_listeners(InputDevice* self, List* listeners) {
|
||||
if(self->listeners != NULL) {
|
||||
list_foreach(InputListener*, listener, self->listeners) {
|
||||
listener->axis.tc->set_device(listener->axis.data, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
self->listeners = listeners;
|
||||
|
||||
if(listeners == NULL)
|
||||
return;
|
||||
|
||||
list_foreach(InputListener*, listener, listeners)
|
||||
listener->axis.tc->set_device(listener->axis.data, self);
|
||||
LOG_INFO("Set listeners for device %d to %p", self->id, listeners);
|
||||
}
|
||||
|
||||
InputDevice* input_get_device_by_id(int id) {
|
||||
list_foreach(InputDevice**, _device, &_devices) {
|
||||
InputDevice* device = *_device;
|
||||
if(device->id == id) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
49
core/src/input.h
Normal file
49
core/src/input.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef _fencer_input_h
|
||||
#define _fencer_input_h
|
||||
|
||||
#include "list.h"
|
||||
#include "input_axis.h"
|
||||
#include <SDL2/SDL_events.h>
|
||||
#include <SDL2/SDL_scancode.h>
|
||||
#include <SDL2/SDL_keyboard.h>
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
|
||||
struct PlayerInput;
|
||||
|
||||
typedef void (*InputDelegateFn)(void* self, InputEvent event);
|
||||
|
||||
typedef struct InputListener {
|
||||
void* self;
|
||||
InputDelegateFn fn;
|
||||
InputAxis axis;
|
||||
} InputListener;
|
||||
|
||||
typedef enum InputDeviceType {
|
||||
InputDevice_Gamepad,
|
||||
InputDevice_KBM
|
||||
} InputDeviceType;
|
||||
|
||||
typedef struct InputDevice {
|
||||
List* listeners;
|
||||
InputDeviceType type;
|
||||
int id;
|
||||
union {
|
||||
struct {
|
||||
const uint8_t* state;
|
||||
} keyboard;
|
||||
struct {
|
||||
SDL_JoystickID id;
|
||||
SDL_GameController* controller;
|
||||
} gamepad;
|
||||
};
|
||||
} InputDevice;
|
||||
|
||||
extern void input_init();
|
||||
extern void input_clean();
|
||||
|
||||
// handle an input event
|
||||
extern void input_handle_event(SDL_Event event);
|
||||
extern void input_device_set_listeners(InputDevice* self, List* listeners);
|
||||
extern InputDevice* input_get_device_by_id(int id);
|
||||
|
||||
#endif // !_fencer_input_h
|
||||
187
core/src/input_axis.c
Normal file
187
core/src/input_axis.c
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#include "input_axis.h"
|
||||
#include "debug.h"
|
||||
#include "input.h"
|
||||
|
||||
KeyBind* keybind_new(SDL_Scancode key) {
|
||||
KeyBind* self = malloc(sizeof(KeyBind));
|
||||
*self = (KeyBind) {
|
||||
.device = NULL,
|
||||
.scancode = key,
|
||||
.state = 0
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
int keybind_is_changed_by(KeyBind* self, SDL_Event event) {
|
||||
return self->device->type == InputDevice_KBM
|
||||
&& (event.type == SDL_KEYUP || event.type == SDL_KEYDOWN)
|
||||
&& event.key.keysym.scancode == self->scancode;
|
||||
}
|
||||
|
||||
InputEvent keybind_evaluate(KeyBind* self, SDL_Event event) {
|
||||
return (InputEvent) {
|
||||
.type = InputEvent_Bool,
|
||||
.as_bool = self->device->keyboard.state[self->scancode]
|
||||
};
|
||||
}
|
||||
|
||||
void keybind_set_device(KeyBind* self, InputDevice* device) {
|
||||
self->device = device;
|
||||
}
|
||||
|
||||
ControllerAxis* controlleraxis_new(int axis) {
|
||||
ControllerAxis* self = malloc(sizeof(ControllerAxis));
|
||||
*self = (ControllerAxis){
|
||||
.axis = axis,
|
||||
.device = NULL
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
int controlleraxis_is_changed_by(ControllerAxis* self, SDL_Event event) {
|
||||
int r = self->device->type == InputDevice_Gamepad
|
||||
&& event.type == SDL_CONTROLLERAXISMOTION
|
||||
&& event.caxis.which == self->device->gamepad.id
|
||||
&& event.caxis.axis == self->axis;
|
||||
return r;
|
||||
}
|
||||
|
||||
InputEvent controlleraxis_evaluate(ControllerAxis* self, SDL_Event event) {
|
||||
float result = (float)event.caxis.value / 32767.0;
|
||||
LOG_INFO("axis %f", result);
|
||||
return (InputEvent) {
|
||||
.type = InputEvent_Float,
|
||||
.as_float = result
|
||||
};
|
||||
}
|
||||
|
||||
void controlleraxis_set_device(ControllerAxis* self, InputDevice* device) {
|
||||
self->device = device;
|
||||
}
|
||||
|
||||
ControllerButton* controllerbutton_new(int button) {
|
||||
ControllerButton* self = malloc(sizeof(ControllerButton));
|
||||
*self = (ControllerButton) {
|
||||
.button = button,
|
||||
.device = NULL
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
int controllerbutton_is_changed_by(ControllerButton* self, SDL_Event event) {
|
||||
return self->device->type == InputDevice_Gamepad
|
||||
&& event.cbutton.which == self->device->gamepad.id
|
||||
&& (event.type == SDL_CONTROLLERBUTTONUP || event.type == SDL_CONTROLLERBUTTONDOWN)
|
||||
&& event.cbutton.button == self->button;
|
||||
}
|
||||
|
||||
InputEvent controllerbutton_evaluate(ControllerButton* self, SDL_Event event) {
|
||||
return (InputEvent) {
|
||||
.type = InputEvent_Bool,
|
||||
.as_bool = event.cbutton.state
|
||||
};
|
||||
}
|
||||
|
||||
void controllerbutton_set_device(ControllerButton* self, InputDevice* device) {
|
||||
self->device = device;
|
||||
}
|
||||
|
||||
CompositeAxis1D* compositeaxis1d_new(InputAxis left, InputAxis right, InputEventType type) {
|
||||
CompositeAxis1D* self = malloc(sizeof(CompositeAxis1D));
|
||||
*self = (CompositeAxis1D) {
|
||||
.left = left,
|
||||
.right = right,
|
||||
.type = type,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
int compositeaxis1d_is_changed_by(CompositeAxis1D* self, SDL_Event event) {
|
||||
return self->left.tc->is_changed_by(self->left.data, event)
|
||||
|| self->right.tc->is_changed_by(self->right.data, event);
|
||||
}
|
||||
|
||||
static inline
|
||||
InputEvent _internal_event_to_type(InputEventType type, InputEvent event) {
|
||||
if(type == InputEvent_Vector && event.type == InputEvent_Vector)
|
||||
return event;
|
||||
|
||||
float as_float = 0;
|
||||
switch(event.type) {
|
||||
case InputEvent_Vector:
|
||||
LOG_ERROR("No (1)");
|
||||
break;
|
||||
case InputEvent_Bool:
|
||||
as_float = event.as_bool;
|
||||
break;
|
||||
case InputEvent_Float:
|
||||
as_float = event.as_float;
|
||||
break;
|
||||
case InputEvent_Int:
|
||||
as_float = event.as_int;
|
||||
break;
|
||||
}
|
||||
event.type = type;
|
||||
switch(type) {
|
||||
case InputEvent_Int:
|
||||
event.as_int = round(as_float);
|
||||
return event;
|
||||
case InputEvent_Float:
|
||||
event.as_float = as_float;
|
||||
return event;
|
||||
case InputEvent_Bool:
|
||||
event.as_bool = as_float != 0;
|
||||
return event;
|
||||
case InputEvent_Vector:
|
||||
LOG_ERROR("No (2)");
|
||||
return event;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
InputEvent compositeaxis1d_evaluate(CompositeAxis1D* self, SDL_Event event) {
|
||||
InputEvent left_result = self->left.tc->evaluate(self->left.data, event);
|
||||
InputEvent right_result = self->right.tc->evaluate(self->right.data, event);
|
||||
|
||||
ASSERT_RETURN(self->type != InputEvent_Vector
|
||||
|| (left_result.type == InputEvent_Vector && right_result.type == InputEvent_Vector),
|
||||
((InputEvent){.type = 0, .as_bool = 0}), "Composite axis can only output vector if both composite elements output vector.");
|
||||
|
||||
InputEvent final = {
|
||||
.type = self->type
|
||||
};
|
||||
// if both outputs are booleans, they will be combined to an int
|
||||
if(final.type == InputEvent_Bool)
|
||||
final.type = InputEvent_Int;
|
||||
|
||||
left_result = _internal_event_to_type(final.type, left_result);
|
||||
right_result = _internal_event_to_type(final.type, right_result);
|
||||
|
||||
switch(final.type) {
|
||||
default:
|
||||
LOG_ERROR("Invalid composite input result");
|
||||
final.type = InputEvent_Bool;
|
||||
final.as_bool = 0;
|
||||
return final;
|
||||
case InputEvent_Int:
|
||||
final.as_int = right_result.as_int - left_result.as_int;
|
||||
return final;
|
||||
case InputEvent_Float:
|
||||
final.as_float = right_result.as_float - left_result.as_float;
|
||||
return final;
|
||||
case InputEvent_Vector:
|
||||
final.as_vector = vsubf(right_result.as_vector, left_result.as_vector);
|
||||
return final;
|
||||
}
|
||||
}
|
||||
|
||||
void compositeaxis1d_set_device(CompositeAxis1D* self, InputDevice* device) {
|
||||
self->left.tc->set_device(self->left.data, device);
|
||||
self->right.tc->set_device(self->right.data, device);
|
||||
}
|
||||
|
||||
void compositeaxis1d_drop(CompositeAxis1D* self) {
|
||||
self->left.drop->drop(self->left.data);
|
||||
self->right.drop->drop(self->right.data);
|
||||
free(self);
|
||||
}
|
||||
129
core/src/input_axis.h
Normal file
129
core/src/input_axis.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef _fencer_input_axis_h
|
||||
#define _fencer_input_axis_h
|
||||
|
||||
#include "typeclass_helpers.h"
|
||||
#include "vmath.h"
|
||||
#include "drop.h"
|
||||
#include <SDL2/SDL_events.h>
|
||||
#include <SDL2/SDL_keyboard.h>
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
|
||||
struct InputDevice;
|
||||
|
||||
typedef enum InputEventType {
|
||||
InputEvent_Vector = 0,
|
||||
InputEvent_Float = 1,
|
||||
InputEvent_Int = 2,
|
||||
InputEvent_Bool = 3,
|
||||
} InputEventType;
|
||||
|
||||
typedef struct InputEvent {
|
||||
InputEventType type;
|
||||
union {
|
||||
Vector as_vector;
|
||||
float as_float;
|
||||
int as_int;
|
||||
int as_bool;
|
||||
};
|
||||
} InputEvent;
|
||||
|
||||
typedef struct {
|
||||
int (*const is_changed_by)(void*, SDL_Event);
|
||||
struct InputEvent (*const evaluate)(void*, SDL_Event);
|
||||
void (*const set_device)(void*, struct InputDevice*);
|
||||
} IInputAxis;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
IInputAxis const* tc;
|
||||
IDrop const* drop;
|
||||
} InputAxis;
|
||||
|
||||
#define impl_InputAxis_for(T, is_changed_by_f, evaluate_f, set_device_f)\
|
||||
static inline InputAxis T##_as_InputAxis(T* x) {\
|
||||
TC_FN_TYPECHECK(int, is_changed_by_f, T*, SDL_Event);\
|
||||
TC_FN_TYPECHECK(struct InputEvent, evaluate_f, T*, SDL_Event);\
|
||||
TC_FN_TYPECHECK(void, set_device_f, T*, struct InputDevice*);\
|
||||
static IInputAxis const tc = {\
|
||||
.is_changed_by = (int(*const)(void*,SDL_Event)) is_changed_by_f,\
|
||||
.evaluate = (struct InputEvent(*const)(void*,SDL_Event)) evaluate_f,\
|
||||
.set_device = (void(*const)(void*,struct InputDevice*)) set_device_f\
|
||||
};\
|
||||
IDrop const* drop = T##_as_Drop(x).tc;\
|
||||
return (InputAxis){.data=x, .tc=&tc, .drop = drop};\
|
||||
}
|
||||
|
||||
typedef struct KeyBind {
|
||||
int state;
|
||||
SDL_Scancode scancode;
|
||||
struct InputDevice* device;
|
||||
} KeyBind;
|
||||
|
||||
extern KeyBind* keybind_new(SDL_Scancode bind);
|
||||
extern int keybind_is_changed_by(KeyBind* self, SDL_Event event);
|
||||
extern struct InputEvent keybind_evaluate(KeyBind* self, SDL_Event);
|
||||
extern void keybind_set_device(KeyBind* self, struct InputDevice* device);
|
||||
|
||||
impl_default_Drop_for(KeyBind)
|
||||
impl_InputAxis_for(KeyBind,
|
||||
keybind_is_changed_by,
|
||||
keybind_evaluate,
|
||||
keybind_set_device
|
||||
)
|
||||
|
||||
typedef struct ControllerAxis {
|
||||
struct InputDevice* device;
|
||||
int axis;
|
||||
} ControllerAxis;
|
||||
|
||||
extern ControllerAxis* controlleraxis_new(int axis);
|
||||
extern int controlleraxis_is_changed_by(ControllerAxis* self, SDL_Event event);
|
||||
extern struct InputEvent controlleraxis_evaluate(ControllerAxis* self, SDL_Event event);
|
||||
extern void controlleraxis_set_device(ControllerAxis* self, struct InputDevice* device);
|
||||
|
||||
impl_default_Drop_for(ControllerAxis)
|
||||
impl_InputAxis_for(ControllerAxis,
|
||||
controlleraxis_is_changed_by,
|
||||
controlleraxis_evaluate,
|
||||
controlleraxis_set_device
|
||||
)
|
||||
|
||||
typedef struct ControllerButton {
|
||||
struct InputDevice* device;
|
||||
int button;
|
||||
} ControllerButton;
|
||||
|
||||
extern ControllerButton* controllerbutton_new(int button);
|
||||
extern int controllerbutton_is_changed_by(ControllerButton* self, SDL_Event event);
|
||||
extern struct InputEvent controllerbutton_evaluate(ControllerButton* self, SDL_Event event);
|
||||
extern void controllerbutton_set_device(ControllerButton* self, struct InputDevice* device);
|
||||
|
||||
impl_default_Drop_for(ControllerButton)
|
||||
impl_InputAxis_for(ControllerButton,
|
||||
controllerbutton_is_changed_by,
|
||||
controllerbutton_evaluate,
|
||||
controllerbutton_set_device
|
||||
)
|
||||
|
||||
typedef struct CompositeAxis1D {
|
||||
InputAxis left;
|
||||
InputAxis right;
|
||||
InputEventType type;
|
||||
} CompositeAxis1D;
|
||||
|
||||
extern CompositeAxis1D* compositeaxis1d_new(InputAxis left, InputAxis right, InputEventType type);
|
||||
extern int compositeaxis1d_is_changed_by(CompositeAxis1D* self, SDL_Event event);
|
||||
extern struct InputEvent compositeaxis1d_evaluate(CompositeAxis1D* self, SDL_Event event);
|
||||
extern void compositeaxis1d_set_device(CompositeAxis1D* self, struct InputDevice* device);
|
||||
extern void compositeaxis1d_drop(CompositeAxis1D* self);
|
||||
|
||||
impl_Drop_for(CompositeAxis1D,
|
||||
compositeaxis1d_drop
|
||||
)
|
||||
impl_InputAxis_for(CompositeAxis1D,
|
||||
compositeaxis1d_is_changed_by,
|
||||
compositeaxis1d_evaluate,
|
||||
compositeaxis1d_set_device
|
||||
)
|
||||
|
||||
#endif // !_fencer_input_axis_h
|
||||
161
core/src/list.c
Normal file
161
core/src/list.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include "list.h"
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef LIST_DEFAULT_RESERVE
|
||||
#define LIST_DEFAULT_RESERVE 4
|
||||
#endif
|
||||
|
||||
List list_init(size_t element_size) {
|
||||
List self = {
|
||||
.element_size = element_size,
|
||||
.cap = LIST_DEFAULT_RESERVE,
|
||||
.len = 0,
|
||||
.data = malloc(element_size * LIST_DEFAULT_RESERVE),
|
||||
};
|
||||
|
||||
if(self.data == NULL) {
|
||||
LOG_ERROR("Failed to allocate list with starting capacity of %d", LIST_DEFAULT_RESERVE);
|
||||
self.cap = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
List list_copy(const List* source) {
|
||||
List self = list_init(source->element_size);
|
||||
list_reserve(&self, source->cap);
|
||||
if(self.cap > 0) {
|
||||
memcpy(self.data, source->data, source->element_size * source->len);
|
||||
self.len = source->len;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
void list_empty(List* self) {
|
||||
if(self->data == NULL || self->cap == 0)
|
||||
return;
|
||||
self->data = NULL;
|
||||
self->cap = 0;
|
||||
self->len = 0;
|
||||
}
|
||||
|
||||
void list_reserve(List* self, size_t at_least) {
|
||||
if(at_least < self->cap)
|
||||
return;
|
||||
|
||||
size_t new_cap = self->cap > 0 ? self->cap : LIST_DEFAULT_RESERVE;
|
||||
while(at_least >= new_cap) {
|
||||
new_cap *= 2;
|
||||
}
|
||||
|
||||
void* new;
|
||||
if(self->data == NULL)
|
||||
new = malloc(new_cap * self->element_size);
|
||||
else
|
||||
new = realloc(self->data, new_cap * self->element_size);
|
||||
ASSERT_RETURN(new != NULL,, "Failed to reserve space for %zu extra elements in list", new_cap);
|
||||
|
||||
self->data = new;
|
||||
self->cap = new_cap;
|
||||
}
|
||||
|
||||
static inline
|
||||
void* list_at_unchecked(List* self, size_t at) {
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
return data.as_byte + self->element_size * at;
|
||||
}
|
||||
|
||||
void* list_at(List* self, size_t at) {
|
||||
ASSERT_RETURN(at < self->len, NULL, "Index %zu out of bounds", at);
|
||||
return list_at_unchecked(self, at);
|
||||
}
|
||||
|
||||
size_t list_add(List* self, void* item) {
|
||||
list_reserve(self, self->len + 1);
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
uint8_t* into = data.as_byte + self->element_size * self->len;
|
||||
|
||||
memcpy(into, item, self->element_size);
|
||||
++self->len;
|
||||
|
||||
return self->len - 1;
|
||||
}
|
||||
|
||||
void list_insert(List* self, void* item, size_t at) {
|
||||
list_reserve(self, self->len + 1);
|
||||
|
||||
if(at == self->len - 1) {
|
||||
list_add(self, item);
|
||||
return;
|
||||
}
|
||||
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
uint8_t* from = data.as_byte + self->element_size * at;
|
||||
uint8_t* into = data.as_byte + self->element_size * (at + 1);
|
||||
uint8_t* end = data.as_byte + self->element_size * self->len;
|
||||
memmove(into, from, end - from);
|
||||
memcpy(from, item, self->element_size);
|
||||
++self->len;
|
||||
}
|
||||
|
||||
void list_erase(List* self, size_t at) {
|
||||
ASSERT_RETURN(at < self->len,, "Index %zu out of bounds", at);
|
||||
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
uint8_t* into = data.as_byte + at * self->element_size;
|
||||
uint8_t* from = data.as_byte + (at + 1) * self->element_size;
|
||||
|
||||
if(at < self->len - 1)
|
||||
memmove(into, from, (self->len - at) * self->element_size);
|
||||
--self->len;
|
||||
|
||||
size_t new_cap = self->cap;
|
||||
while(new_cap > self->len) {
|
||||
new_cap /= 2;
|
||||
}
|
||||
new_cap *= 2;
|
||||
|
||||
|
||||
if(new_cap == self->cap)
|
||||
return;
|
||||
|
||||
void* shrunk = realloc(self->data, new_cap * self->element_size);
|
||||
ASSERT_RETURN(shrunk != NULL || new_cap == 0,, "Failed to shrink List to %zu", new_cap);
|
||||
|
||||
self->data = shrunk;
|
||||
self->cap = new_cap;
|
||||
}
|
||||
|
||||
void* list_iterator_begin(List* self) {
|
||||
return list_at_unchecked(self, 0);
|
||||
}
|
||||
|
||||
void* list_iterator_end(List* self) {
|
||||
return list_at_unchecked(self, self->len);
|
||||
}
|
||||
35
core/src/list.h
Normal file
35
core/src/list.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef _fencer_list_h
|
||||
#define _fencer_list_h
|
||||
|
||||
#include "stddef.h"
|
||||
|
||||
typedef struct List List;
|
||||
struct List {
|
||||
void* data;
|
||||
size_t cap;
|
||||
size_t len;
|
||||
|
||||
size_t element_size;
|
||||
};
|
||||
|
||||
extern List list_init(size_t element_size);
|
||||
extern List list_copy(const List* source);
|
||||
extern void list_empty(List* list);
|
||||
|
||||
extern void list_reserve(List* self, size_t at_least);
|
||||
extern void* list_at(List* list, size_t at);
|
||||
|
||||
extern size_t list_add(List* self, void* item);
|
||||
extern void list_insert(List* self, void* item, size_t at);
|
||||
extern void list_erase(List* self, size_t at);
|
||||
|
||||
extern void* list_iterator_begin(List* self);
|
||||
extern void* list_iterator_end(List* self);
|
||||
|
||||
#define list_from_type(T) list_init(sizeof(T))
|
||||
#define list_foreach(T, iter, list) for(T iter = list_iterator_begin(list); iter != (T)list_iterator_end(list); ++iter)
|
||||
#define list_at_as(T, __list, __i) ((T*)(list_at(__list, __i)))
|
||||
|
||||
#define list_iterator_begin_as(T, __list) ((T*)(list_iterator_begin(__list)))
|
||||
|
||||
#endif // !_fencer_list_h
|
||||
91
core/src/physics_entity.c
Normal file
91
core/src/physics_entity.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "physics_entity.h"
|
||||
#include "camera.h"
|
||||
#include "rigidbody.h"
|
||||
#include "shape.h"
|
||||
#include "render.h"
|
||||
#include "debug.h"
|
||||
|
||||
void physics_entity_debug_draw(PhysicsEntity self) {
|
||||
RigidBody* body = self.tc->get_rigidbody(self.data);
|
||||
Shape* shape = self.tc->get_shape(self.data);
|
||||
Transform* transform = self.transformable->get_transform(self.data);
|
||||
|
||||
shape_draw(shape, *transform);
|
||||
rigidbody_debug_draw_contacts(body);
|
||||
|
||||
Vector lhs = transform->position;
|
||||
Vector rhs = vaddf(lhs, rigidbody_get_velocity(body));
|
||||
lhs = camera_world_to_pixel_point(&g_camera, lhs);
|
||||
rhs = camera_world_to_pixel_point(&g_camera, rhs);
|
||||
SDL_SetRenderDrawColor(g_renderer, 0, 255, 0, 255);
|
||||
SDL_RenderDrawLine(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
|
||||
|
||||
rhs = camera_world_to_pixel_point(&g_camera, vaddf(transform->position, rigidbody_get_force(body)));
|
||||
SDL_SetRenderDrawColor(g_renderer, 0, 255, 255, 255);
|
||||
SDL_RenderDrawLine(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
|
||||
}
|
||||
static inline
|
||||
Vector _internal_calculate_contact_force(RigidBody* self, Contact* contact) {
|
||||
const Vector velocity = contact->hit.velocity;
|
||||
const Vector normal = contact->hit.normal;
|
||||
const float push = -vdotf(normal, velocity);
|
||||
const Vector current_velocity = rigidbody_get_velocity(self);
|
||||
return vaddf(current_velocity, vmulff(normal, push * rigidbody_get_bounce(self)));
|
||||
}
|
||||
|
||||
static inline
|
||||
int _internal_default_contact_solver(RigidBody* body, Contact* contact, Transform pre_solve) {
|
||||
Collision hit = contact->hit;
|
||||
Transform* trans = rigidbody_get_transform(body);
|
||||
const Vector world_collision_point = vaddf(transform_point(&pre_solve, hit.point), hit.penetration_vector);
|
||||
const float current_dot = vdotf(hit.normal, vsubf(transform_point(trans, hit.point), world_collision_point));
|
||||
if(current_dot >= -0.0001)
|
||||
return 1;
|
||||
// the desired position is anywhere the overlapping vertex is further along the normal than the contact point
|
||||
const Vector target = vaddf(trans->position, vmulff(hit.normal, -current_dot));
|
||||
trans->position = vmovetowardsf(trans->position, target, 1.f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void physics_entity_solve_contacts(PhysicsEntity self, List* contacts) {
|
||||
RigidBody* body = self.tc->get_rigidbody(self.data);
|
||||
const Transform pre_solve = *rigidbody_get_transform(body);
|
||||
|
||||
// attempt to solve constraints
|
||||
int done;
|
||||
for(size_t iteration = 50; iteration != 0; --iteration) {
|
||||
done = 1;
|
||||
list_foreach(Contact*, contact, contacts) {
|
||||
if(!_internal_default_contact_solver(body, contact, pre_solve))
|
||||
done = 0;
|
||||
}
|
||||
if(done)
|
||||
break;
|
||||
if(iteration == 1)
|
||||
LOG_WARNING("gave up on solving %zu contacts", contacts->len);
|
||||
}
|
||||
Vector dir = vnormalizedf(vsubf(rigidbody_get_transform(body)->position, pre_solve.position));
|
||||
Vector vel = rigidbody_get_velocity(body);
|
||||
float dot = vdotf(dir, vel);
|
||||
|
||||
if(dot < 0)
|
||||
vel = vsubf(vel, vmulff(dir, dot * (1.0 + rigidbody_get_bounce(body))));
|
||||
|
||||
rigidbody_set_velocity(body, vel);
|
||||
}
|
||||
|
||||
void physics_entity_update(PhysicsEntity self) {
|
||||
RigidBody* body = self.tc->get_rigidbody(self.data);
|
||||
|
||||
ASSERT_RETURN(!visnanf(rigidbody_get_velocity(body)),, "Velocity is NaN (0)");
|
||||
|
||||
List* contacts = rigidbody_get_contacts(body);
|
||||
if(contacts->len > 0) {
|
||||
self.tc->collision_solver(self.data, contacts);
|
||||
list_foreach(Contact*, contact, contacts)
|
||||
self.tc->on_collision(self.data, contact->hit);
|
||||
}
|
||||
rigidbody_collect_contacts(body);
|
||||
|
||||
ASSERT_RETURN(!visnanf(rigidbody_get_velocity(body)),, "Velocity is NaN (1)");
|
||||
}
|
||||
47
core/src/physics_entity.h
Normal file
47
core/src/physics_entity.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef _fencer_collidable_h
|
||||
#define _fencer_collidable_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "typeclass_helpers.h"
|
||||
#include "list.h"
|
||||
#include "shape.h"
|
||||
|
||||
typedef struct Collision Collision;
|
||||
typedef struct RigidBody RigidBody;
|
||||
|
||||
typedef struct {
|
||||
RigidBody* (*const get_rigidbody)(void* self);
|
||||
Shape* (*const get_shape)(void* self);
|
||||
void(*const on_collision)(void* self, Collision collision);
|
||||
void(*const collision_solver)(void* self, List* collisions);
|
||||
} IPhysicsEntity;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
IPhysicsEntity const* tc;
|
||||
ITransformable const* transformable;
|
||||
} PhysicsEntity;
|
||||
|
||||
extern void physics_entity_debug_draw(PhysicsEntity self);
|
||||
extern void physics_entity_solve_contacts(PhysicsEntity self, List* contacts);
|
||||
extern void physics_entity_update(PhysicsEntity self);
|
||||
|
||||
|
||||
#define impl_PhysicsEntity_for(T, get_rigidbody_f, get_shape_f, on_collision_f, collision_solver_f)\
|
||||
static inline PhysicsEntity T##_as_PhysicsEntity(T* x) {\
|
||||
TC_FN_TYPECHECK(Transformable, T##_as_Transformable, T*);\
|
||||
TC_FN_TYPECHECK(RigidBody*, get_rigidbody_f, T*);\
|
||||
TC_FN_TYPECHECK(Shape*, get_shape_f, T*);\
|
||||
TC_FN_TYPECHECK(void, on_collision_f, T*, Collision);\
|
||||
TC_FN_TYPECHECK(void, collision_solver_f, T*, List*);\
|
||||
static IPhysicsEntity const tc = {\
|
||||
.get_rigidbody = (RigidBody*(*const)(void*)) get_rigidbody_f,\
|
||||
.get_shape = (Shape*(*const)(void*)) get_shape_f,\
|
||||
.on_collision = (void(*const)(void*,Collision)) on_collision_f,\
|
||||
.collision_solver = (void(*const)(void*,List*)) collision_solver_f,\
|
||||
};\
|
||||
Transformable transformable = T##_as_Transformable(x);\
|
||||
return (PhysicsEntity){.data = x, .tc = &tc, .transformable = transformable.tc};\
|
||||
}
|
||||
|
||||
#endif // !_fencer_collidable_h
|
||||
68
core/src/physics_world.c
Normal file
68
core/src/physics_world.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include "physics_world.h"
|
||||
#include "debug.h"
|
||||
#include "collision.h"
|
||||
#include "rigidbody.h"
|
||||
|
||||
static List _world_bodies;
|
||||
|
||||
void physics_world_init() {
|
||||
_world_bodies = list_from_type(PhysicsEntity);
|
||||
}
|
||||
|
||||
void physics_world_clean() {
|
||||
list_empty(&_world_bodies);
|
||||
}
|
||||
|
||||
void physics_world_add_entity(PhysicsEntity entity) {
|
||||
list_add(&_world_bodies, &entity);
|
||||
}
|
||||
|
||||
void physics_world_remove_entity(PhysicsEntity entity) {
|
||||
for(size_t i = 0; i < _world_bodies.len; ++i) {
|
||||
PhysicsEntity* found = list_at_as(PhysicsEntity, &_world_bodies, i);
|
||||
if(found->data == entity.data) {
|
||||
list_erase(&_world_bodies, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_RETURN(0,, "Physics entity with data at %p is not registered in physics world", entity.data);
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_physics_narrow_collision() {
|
||||
size_t half_end = _world_bodies.len/2;
|
||||
Collision collision_left, collision_right;
|
||||
|
||||
PhysicsEntity* right = NULL;
|
||||
list_foreach(PhysicsEntity*, left, &_world_bodies) {
|
||||
for(size_t right_index = 0; right_index < half_end; ++right_index) {
|
||||
right = list_at_as(PhysicsEntity, &_world_bodies, right_index);
|
||||
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_physics_apply() {
|
||||
list_foreach(PhysicsEntity*, entity, &_world_bodies) {
|
||||
physics_entity_update(*entity);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_physics_integrate_forces() {
|
||||
list_foreach(PhysicsEntity*, entity, &_world_bodies)
|
||||
rigidbody_integrate_forces(entity->tc->get_rigidbody(entity->data));
|
||||
}
|
||||
|
||||
void physics_world_tick() {
|
||||
_internal_physics_integrate_forces();
|
||||
_internal_physics_narrow_collision();
|
||||
_internal_physics_apply();
|
||||
}
|
||||
14
core/src/physics_world.h
Normal file
14
core/src/physics_world.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _fencer_physics_world_h
|
||||
#define _fencer_physics_world_h
|
||||
|
||||
#include "physics_entity.h"
|
||||
|
||||
extern void physics_world_init();
|
||||
extern void physics_world_clean();
|
||||
|
||||
extern void physics_world_add_entity(PhysicsEntity entity);
|
||||
extern void physics_world_remove_entity(PhysicsEntity entity);
|
||||
|
||||
extern void physics_world_tick();
|
||||
|
||||
#endif // !_fencer_physics_world_h
|
||||
37
core/src/player_input.c
Normal file
37
core/src/player_input.c
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "player_input.h"
|
||||
|
||||
PlayerInput* playerinput_new(void* target, int device) {
|
||||
PlayerInput* self = malloc(sizeof(PlayerInput));
|
||||
self->listeners = list_from_type(InputListener);
|
||||
self->device = input_get_device_by_id(device);
|
||||
self->target = target;
|
||||
input_device_set_listeners(self->device, &self->listeners);
|
||||
return self;
|
||||
}
|
||||
|
||||
void playerinput_add(PlayerInput* self, InputAxis axis, InputDelegateFn delegate) {
|
||||
InputListener listener = {
|
||||
.axis = axis,
|
||||
.fn = delegate,
|
||||
.self = self->target,
|
||||
};
|
||||
listener.axis.tc->set_device(listener.axis.data, self->device);
|
||||
list_add(&self->listeners, &listener);
|
||||
}
|
||||
|
||||
void playerinput_set_device(PlayerInput* self, int device) {
|
||||
if(self->device != NULL)
|
||||
input_device_set_listeners(self->device, NULL);
|
||||
self->device = input_get_device_by_id(device);
|
||||
input_device_set_listeners(self->device, &self->listeners);
|
||||
}
|
||||
|
||||
void playerinput_drop(PlayerInput* self) {
|
||||
input_device_set_listeners(self->device, NULL);
|
||||
|
||||
list_foreach(InputListener*, listener, &self->listeners) {
|
||||
listener->axis.drop->drop(listener->axis.data);
|
||||
}
|
||||
list_empty(&self->listeners);
|
||||
free(self);
|
||||
}
|
||||
23
core/src/player_input.h
Normal file
23
core/src/player_input.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _fencer_player_input_h
|
||||
#define _fencer_player_input_h
|
||||
|
||||
#include "list.h"
|
||||
#include "input.h"
|
||||
#include "input_axis.h"
|
||||
|
||||
typedef struct PlayerInput {
|
||||
InputDevice* device;
|
||||
List listeners;
|
||||
void* target;
|
||||
} PlayerInput;
|
||||
|
||||
extern PlayerInput* playerinput_new(void* target, int device);
|
||||
extern void playerinput_add(PlayerInput* self, InputAxis axis, InputDelegateFn delegate);
|
||||
extern void playerinput_set_device(PlayerInput* self, int device);
|
||||
extern void playerinput_drop(PlayerInput* self);
|
||||
|
||||
impl_Drop_for(PlayerInput,
|
||||
playerinput_drop
|
||||
)
|
||||
|
||||
#endif // !_fencer_player_input_h
|
||||
141
core/src/program.c
Normal file
141
core/src/program.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#include "program.h"
|
||||
#include "camera.h"
|
||||
#include "game_world.h"
|
||||
#include "physics_world.h"
|
||||
#include "time.h"
|
||||
#include "assets.h"
|
||||
#include "debug.h"
|
||||
#include "input.h"
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
|
||||
SDL_Window* g_window;
|
||||
static double _target_delta_time = 0.0;
|
||||
static double _delta_time;
|
||||
static double _frame_start;
|
||||
static double _game_start_time;
|
||||
|
||||
|
||||
#define INITFLAGS SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK
|
||||
|
||||
static inline
|
||||
double tstos(struct timespec ts) {
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec * 1E-09;
|
||||
}
|
||||
|
||||
struct timespec get_time() {
|
||||
struct timespec ts;
|
||||
timespec_get(&ts, TIME_UTC);
|
||||
return ts;
|
||||
}
|
||||
|
||||
double get_time_s() {
|
||||
return tstos(get_time());
|
||||
}
|
||||
|
||||
void program_run(const struct ProgramSettings* settings) {
|
||||
LOG_INFO("Starting program...");
|
||||
if(settings->target_fps <= 0) {
|
||||
_target_delta_time = 0;
|
||||
} else {
|
||||
_target_delta_time = 1.0f/settings->target_fps;
|
||||
}
|
||||
_game_start_time = get_time_s();
|
||||
_frame_start = _game_start_time;
|
||||
if(SDL_Init(INITFLAGS) != 0) {
|
||||
LOG_ERROR("SDL init error: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
g_window = SDL_CreateWindow(
|
||||
settings->title,
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
|
||||
settings->view_resolution.x,
|
||||
settings->view_resolution.y,
|
||||
SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_RESIZABLE);
|
||||
|
||||
physics_world_init();
|
||||
render_init(g_window, settings);
|
||||
camera_init();
|
||||
assets_init();
|
||||
input_init();
|
||||
game_world_init();
|
||||
|
||||
LOG_INFO("settings->on_play");
|
||||
settings->on_play();
|
||||
|
||||
LOG_INFO("Starting program loop");
|
||||
for(;;) {
|
||||
render_present();
|
||||
|
||||
double current_time = get_time_s();
|
||||
_delta_time += current_time - _frame_start;
|
||||
_frame_start = current_time;
|
||||
|
||||
program_handle_events();
|
||||
while(_delta_time > _target_delta_time) {
|
||||
_delta_time -= _target_delta_time;
|
||||
settings->on_tick();
|
||||
game_world_update();
|
||||
physics_world_tick();
|
||||
}
|
||||
settings->on_draw();
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
||||
ASSERT_RETURN(0,, "Program escaped containment, nuking from orbit...");
|
||||
abort();
|
||||
}
|
||||
|
||||
void program_quit() {
|
||||
game_world_close();
|
||||
input_clean();
|
||||
assets_clean();
|
||||
render_clean();
|
||||
physics_world_clean();
|
||||
SDL_DestroyWindow(g_window);
|
||||
SDL_Quit();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void program_handle_events() {
|
||||
SDL_Event event;
|
||||
while(SDL_PollEvent(&event)) {
|
||||
switch(event.type) {
|
||||
default: break;
|
||||
case SDL_KEYUP:
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
input_handle_event(event);
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if(event.window.windowID == SDL_GetWindowID(g_window)) {
|
||||
program_handle_windowevent(&event.window);
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
program_quit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void program_handle_windowevent(SDL_WindowEvent* event) {
|
||||
switch(event->type) {
|
||||
default:
|
||||
render_handle_resize();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
float delta_time() {
|
||||
return _target_delta_time == 0 ? _delta_time : _target_delta_time;
|
||||
}
|
||||
|
||||
inline
|
||||
float game_time() {
|
||||
return get_time_s() - _game_start_time;
|
||||
}
|
||||
33
core/src/program.h
Normal file
33
core/src/program.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef _fencer_program_h
|
||||
#define _fencer_program_h
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include "vmath.h"
|
||||
#include "render.h"
|
||||
|
||||
typedef void(*TickCallback)();
|
||||
typedef void(*PlayCallback)();
|
||||
typedef void(*DrawCallback)();
|
||||
|
||||
struct ProgramSettings {
|
||||
const char* title;
|
||||
IVector view_resolution;
|
||||
int target_fps;
|
||||
|
||||
TickCallback on_tick;
|
||||
PlayCallback on_play;
|
||||
DrawCallback on_draw;
|
||||
};
|
||||
|
||||
extern SDL_Window* g_window;
|
||||
|
||||
extern void program_run(const struct ProgramSettings* settings);
|
||||
extern void program_quit();
|
||||
extern void program_handle_events();
|
||||
extern void program_handle_windowevent(SDL_WindowEvent* event);
|
||||
|
||||
extern float delta_time();
|
||||
extern float game_time();
|
||||
|
||||
#endif // !_fencer_program_h
|
||||
88
core/src/render.c
Normal file
88
core/src/render.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include "render.h"
|
||||
#include "program.h"
|
||||
#include "debug.h"
|
||||
#include <SDL2/SDL_pixels.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
|
||||
SDL_Renderer* g_renderer;
|
||||
SDL_Texture* g_render_target;
|
||||
SDL_Rect g_render_area;
|
||||
IVector g_render_resolution;
|
||||
|
||||
void render_init(SDL_Window* window, const struct ProgramSettings* settings) {
|
||||
LOG_INFO("render_init");
|
||||
// create renderer, needs to be able to target textures, preferably hardware accelerated
|
||||
g_renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
|
||||
// create render target texture
|
||||
g_render_target = SDL_CreateTexture(
|
||||
g_renderer,
|
||||
SDL_PIXELFORMAT_ARGB32,
|
||||
SDL_TEXTUREACCESS_TARGET,
|
||||
settings->view_resolution.x, settings->view_resolution.y);
|
||||
|
||||
// store the desired view resolution
|
||||
g_render_resolution = settings->view_resolution;
|
||||
|
||||
// get the destination area for rendering the target onto the window
|
||||
render_calculate_render_area();
|
||||
|
||||
// clear target black
|
||||
SDL_SetRenderTarget(g_renderer, g_render_target);
|
||||
SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(g_renderer);
|
||||
}
|
||||
|
||||
void render_clean() {
|
||||
// clear up allocated SDL objects
|
||||
SDL_DestroyTexture(g_render_target);
|
||||
SDL_DestroyRenderer(g_renderer);
|
||||
}
|
||||
|
||||
void render_present() {
|
||||
// clear window white
|
||||
SDL_SetRenderTarget(g_renderer, NULL);
|
||||
SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(g_renderer);
|
||||
// copy render target
|
||||
SDL_Rect source_rect = {0, 0, g_render_resolution.x, g_render_resolution.y};
|
||||
SDL_RenderCopy(g_renderer, g_render_target, &source_rect, &g_render_area);
|
||||
SDL_RenderPresent(g_renderer);
|
||||
// clear render target
|
||||
SDL_SetRenderTarget(g_renderer, g_render_target);
|
||||
SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(g_renderer);
|
||||
}
|
||||
|
||||
void render_calculate_render_area() {
|
||||
// get aspect ratios of both the window and the rendertexture
|
||||
IVector window_resolution = render_get_window_size();
|
||||
float window_aspect = (float)window_resolution.x / (float)window_resolution.y;
|
||||
float target_aspect = (float)g_render_resolution.x / (float)g_render_resolution.y;
|
||||
|
||||
// calculate the largest area that will fit the entire rendertexture into the window space
|
||||
g_render_area = (SDL_Rect) {0, 0, window_resolution.x, window_resolution.y};
|
||||
if(window_aspect <= target_aspect) {
|
||||
g_render_area.h = window_resolution.x / target_aspect;
|
||||
g_render_area.y = (window_resolution.y - g_render_area.h) / 2;
|
||||
} else {
|
||||
g_render_area.w = window_resolution.y * target_aspect;
|
||||
g_render_area.x += (window_resolution.x - g_render_area.w) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
IVector render_get_window_size() {
|
||||
// select window as target (store target to reset)
|
||||
SDL_Texture* target = SDL_GetRenderTarget(g_renderer);
|
||||
SDL_SetRenderTarget(g_renderer, NULL);
|
||||
// fetch output size (= window size) of renderer
|
||||
int window_width, window_height;
|
||||
SDL_GetRendererOutputSize(g_renderer, &window_width, &window_height);
|
||||
// reset render target
|
||||
SDL_SetRenderTarget(g_renderer, target);
|
||||
// construct IVector from fetched output size
|
||||
return (IVector){window_width, window_height};
|
||||
}
|
||||
|
||||
void render_handle_resize() {
|
||||
render_calculate_render_area();
|
||||
}
|
||||
29
core/src/render.h
Normal file
29
core/src/render.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef _fencer_render_h
|
||||
#define _fencer_render_h
|
||||
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_rect.h>
|
||||
#include "vmath.h"
|
||||
|
||||
struct ProgramSettings;
|
||||
|
||||
// renderer created from window in Program passed to render_init
|
||||
extern SDL_Renderer* g_renderer;
|
||||
// render target
|
||||
extern SDL_Texture* g_render_target;
|
||||
// area of window to render render_target to
|
||||
extern SDL_Rect g_render_area;
|
||||
// size of render_target
|
||||
extern IVector g_render_resolution;
|
||||
|
||||
extern void render_init(SDL_Window* window, const struct ProgramSettings* settings);
|
||||
extern void render_clean();
|
||||
extern void render_present();
|
||||
|
||||
extern SDL_FRect render_calculate_unit_rect();
|
||||
extern void render_calculate_render_area();
|
||||
extern IVector render_get_window_size();
|
||||
extern void render_handle_resize();
|
||||
|
||||
|
||||
#endif // !_fencer_render_h
|
||||
161
core/src/rigidbody.c
Normal file
161
core/src/rigidbody.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include "rigidbody.h"
|
||||
#include "camera.h"
|
||||
#include "debug.h"
|
||||
#include "program.h"
|
||||
#include "collision.h"
|
||||
#include "transformable.h"
|
||||
|
||||
struct RigidBody {
|
||||
Transformable transformable;
|
||||
|
||||
float mass;
|
||||
|
||||
float bounce;
|
||||
|
||||
Vector last_linear_force;
|
||||
Vector next_linear_force;
|
||||
Vector linear_velocity;
|
||||
Transform internal_transform;
|
||||
|
||||
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 = 1.0f,
|
||||
.bounce = 0.0f,
|
||||
|
||||
.linear_velocity = ZeroVector,
|
||||
.next_linear_force = ZeroVector,
|
||||
.last_linear_force = ZeroVector,
|
||||
|
||||
.internal_transform = *transform.tc->get_transform(transform.data),
|
||||
|
||||
.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) {
|
||||
list_add(&self->contacts,
|
||||
&(Contact) {
|
||||
.hit = hit,
|
||||
.duration = delta_time()
|
||||
});
|
||||
}
|
||||
|
||||
void rigidbody_collect_contacts(RigidBody* self) {
|
||||
list_empty(&self->contacts);
|
||||
}
|
||||
|
||||
List* rigidbody_get_contacts(RigidBody* self) {
|
||||
return &self->contacts;
|
||||
}
|
||||
|
||||
static inline
|
||||
void _internal_debug_draw_collision_edge(RigidBody* self, Contact* contact) {
|
||||
#if !NDEBUG
|
||||
Vector left = contact->hit.edge_left;
|
||||
Vector right = contact->hit.edge_right;
|
||||
Vector point = transform_point(&self->internal_transform, contact->hit.point);
|
||||
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, contact->hit.normal);
|
||||
SDL_SetRenderDrawColor(g_renderer, 255, 2, 255, 255);
|
||||
SDL_RenderDrawLine(g_renderer, a.x, a.y, b.x, b.y);
|
||||
a = camera_world_to_pixel_point(&g_camera, point);
|
||||
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
|
||||
}
|
||||
|
||||
void rigidbody_integrate_forces(RigidBody* self) {
|
||||
const float dt = delta_time();
|
||||
|
||||
Vector position = self->internal_transform.position;
|
||||
Vector velocity = self->linear_velocity;
|
||||
Vector acceleration = vmulff(self->last_linear_force, 0.5f * dt * dt);
|
||||
|
||||
position = vaddf(position, vmulff(velocity, dt));
|
||||
position = vaddf(position, acceleration);
|
||||
|
||||
acceleration = vmulff(vaddf(self->next_linear_force, acceleration), dt * 0.5f);
|
||||
velocity = vaddf(velocity, acceleration);
|
||||
|
||||
self->linear_velocity = velocity;
|
||||
self->internal_transform.position = position;
|
||||
transformable_set_position(self->transformable, position);
|
||||
|
||||
self->last_linear_force = self->next_linear_force;
|
||||
self->next_linear_force = ZeroVector;
|
||||
}
|
||||
|
||||
float rigidbody_get_mass(const RigidBody* self) {
|
||||
return self->mass;
|
||||
}
|
||||
|
||||
void rigidbody_set_mass(RigidBody* self, float mass) {
|
||||
self->mass = mass;
|
||||
}
|
||||
|
||||
float rigidbody_get_bounce(const RigidBody* self) {
|
||||
return self->bounce;
|
||||
}
|
||||
|
||||
void rigidbody_set_bounce(RigidBody* self, float bounce) {
|
||||
self->bounce = bounce;
|
||||
}
|
||||
|
||||
void rigidbody_add_impulse(RigidBody* self, Vector force, int use_mass) {
|
||||
rigidbody_accelerate(self, vmulff(force, 1.0f/delta_time()), use_mass);
|
||||
}
|
||||
|
||||
void rigidbody_accelerate(RigidBody* self, Vector force, int use_mass) {
|
||||
if(use_mass)
|
||||
force = vmulff(force, 1.0f / self->mass);
|
||||
if(vsqrmagnitudef(force) > powf(0.01f, 2))
|
||||
self->next_linear_force = vaddf(self->next_linear_force, force);
|
||||
}
|
||||
|
||||
int rigidbody_is_static(const RigidBody* self) {
|
||||
return self->is_static;
|
||||
}
|
||||
|
||||
void rigidbody_set_static(RigidBody* self, int is_static) {
|
||||
self->is_static = is_static;
|
||||
}
|
||||
|
||||
Vector rigidbody_get_velocity(const RigidBody* self) {
|
||||
return self->linear_velocity;
|
||||
}
|
||||
|
||||
void rigidbody_set_velocity(RigidBody* self, Vector velocity) {
|
||||
self->next_linear_force = vaddf(self->next_linear_force, vsubf(velocity, self->next_linear_force));
|
||||
self->linear_velocity = velocity;
|
||||
}
|
||||
|
||||
Vector rigidbody_get_force(RigidBody* self) {
|
||||
return self->next_linear_force;
|
||||
}
|
||||
|
||||
void rigidbody_debug_draw_contacts(RigidBody* self) {
|
||||
list_foreach(Contact*, contact, &self->contacts) {
|
||||
_internal_debug_draw_collision_edge(self, contact);
|
||||
}
|
||||
}
|
||||
|
||||
Transform* rigidbody_get_transform(RigidBody* self) {
|
||||
return &self->internal_transform;
|
||||
}
|
||||
51
core/src/rigidbody.h
Normal file
51
core/src/rigidbody.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef _fencer_rigidbody_h
|
||||
#define _fencer_rigidbody_h
|
||||
|
||||
#include "shape.h"
|
||||
#include "transformable.h"
|
||||
#include "list.h"
|
||||
#include "collision.h"
|
||||
|
||||
struct Collision;
|
||||
typedef struct {
|
||||
struct Collision hit;
|
||||
float duration;
|
||||
} Contact;
|
||||
typedef struct RigidBody RigidBody;
|
||||
typedef void (*CollisionHandlerFn)(void* obj, List* collisions);
|
||||
|
||||
// Referenced transform is stored but not owned by the rigidbody.
|
||||
extern RigidBody* rigidbody_make(Transformable transform);
|
||||
extern void rigidbody_destroy(RigidBody* self);
|
||||
|
||||
extern void rigidbody_add_contact(RigidBody* self, struct Collision hit);
|
||||
extern void rigidbody_collect_contacts(RigidBody* self);
|
||||
extern List* rigidbody_get_contacts(RigidBody* self);
|
||||
|
||||
extern void rigidbody_integrate_forces(RigidBody* self);
|
||||
|
||||
extern float rigidbody_get_mass(const RigidBody* self);
|
||||
extern void rigidbody_set_mass(RigidBody* self, float mass);
|
||||
|
||||
extern float rigidbody_get_bounce(const RigidBody* self);
|
||||
extern void rigidbody_set_bounce(RigidBody* self, float bounce);
|
||||
|
||||
extern void rigidbody_add_impulse(RigidBody* self, Vector force, int use_mass);
|
||||
extern void rigidbody_accelerate(RigidBody* self, Vector force, int use_mass);
|
||||
|
||||
extern int rigidbody_is_static(const RigidBody* self);
|
||||
extern void rigidbody_set_static(RigidBody* self, int is_static);
|
||||
|
||||
extern Vector rigidbody_get_velocity(const RigidBody* self);
|
||||
extern void rigidbody_set_velocity(RigidBody* self, Vector velocity);
|
||||
|
||||
extern Vector rigidbody_get_force(RigidBody* self);
|
||||
|
||||
extern void rigidbody_debug_draw_contacts(RigidBody* self);
|
||||
extern Transform* rigidbody_get_transform(RigidBody* self);
|
||||
|
||||
impl_Transformable_for(RigidBody,
|
||||
rigidbody_get_transform
|
||||
)
|
||||
|
||||
#endif // !_fencer_rigidbody_h
|
||||
185
core/src/shape.c
Normal file
185
core/src/shape.c
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "shape.h"
|
||||
#include "camera.h"
|
||||
#include "debug.h"
|
||||
#include "render.h"
|
||||
#include "list.h"
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct Shape {
|
||||
List points;
|
||||
|
||||
Vector mean;
|
||||
int is_convex;
|
||||
};
|
||||
|
||||
static
|
||||
Vector* _shape_get_furthest_in_direction(Shape* self, Vector direction) {
|
||||
// ensure direction is normalized
|
||||
direction = vnormalizedf(direction);
|
||||
float furthest_dot = vdotf(direction, vsubf(*list_at_as(Vector, &self->points, 0), self->mean));
|
||||
Vector* furthest = list_at(&self->points, 0);
|
||||
|
||||
float dot;
|
||||
list_foreach(Vector*, point, &self->points) {
|
||||
dot = vdotf(direction, vsubf(*point, self->mean));
|
||||
if(dot > furthest_dot) {
|
||||
furthest = point;
|
||||
}
|
||||
}
|
||||
|
||||
return furthest;
|
||||
}
|
||||
|
||||
// go through each point,
|
||||
// in order to be convex, none of the points in the shape can be "inset".
|
||||
// This means that if one of the points is not the furthest in it's own direction
|
||||
// measured from the median.
|
||||
static
|
||||
int _shape_calculate_is_convex(Shape* self) {
|
||||
// point relative to mean
|
||||
Vector relative;
|
||||
list_foreach(Vector*, point, &self->points) {
|
||||
relative = vsubf(*point, self->mean);
|
||||
if(point != _shape_get_furthest_in_direction(self, relative)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
Vector _shape_calculate_mean(Shape* self) {
|
||||
Vector avg = ZeroVector;
|
||||
size_t count = 0;
|
||||
list_foreach(Vector*, point, &self->points) {
|
||||
++count;
|
||||
avg = vaddf(avg, vmulff(*point, 1.0/count));
|
||||
}
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
Shape* shape_new(const Vector* points, size_t points_len) {
|
||||
// allocate required space for shape and points array
|
||||
Shape* self = malloc(sizeof(Shape));
|
||||
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate enough space for a shape object.");
|
||||
|
||||
self->points = list_from_type(Vector);
|
||||
list_reserve(&self->points, points_len);
|
||||
|
||||
if(points != NULL) {
|
||||
memcpy(list_iterator_begin_as(Vector, &self->points), points, points_len * sizeof(Vector));
|
||||
self->points.len = points_len;
|
||||
|
||||
// derive metadata
|
||||
self->mean = _shape_calculate_mean(self);
|
||||
self->is_convex = _shape_calculate_is_convex(self);
|
||||
} else {
|
||||
self->is_convex = 1;
|
||||
self->mean = ZeroVector;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
Shape* shape_new_square(Vector size) {
|
||||
return shape_new((Vector[4]){
|
||||
ZeroVector,
|
||||
(Vector){size.x, 0.f},
|
||||
size,
|
||||
(Vector){0.f, size.y},
|
||||
}, 4);
|
||||
}
|
||||
|
||||
Shape* shape_clone(const Shape* source) {
|
||||
Shape* self = malloc(sizeof(Shape));
|
||||
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for shape object.");
|
||||
|
||||
self->points = list_copy(&source->points);
|
||||
|
||||
// copy data from source
|
||||
self->mean = source->mean;
|
||||
self->is_convex = source->is_convex;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void shape_destroy(Shape* self) {
|
||||
list_empty(&self->points);
|
||||
free(self);
|
||||
}
|
||||
|
||||
Vector* shape_get_start(Shape* self) {
|
||||
return list_iterator_begin(&self->points);
|
||||
}
|
||||
|
||||
Vector* shape_get_end(Shape* self) {
|
||||
return list_iterator_end(&self->points);
|
||||
}
|
||||
|
||||
size_t shape_get_points_count(const Shape* self) {
|
||||
return self->points.len;
|
||||
}
|
||||
|
||||
Vector shape_get_point(Shape* self, size_t at) {
|
||||
ASSERT_RETURN(at < self->points.len, self->mean, "Point index %zu out of bounds for shape", at);
|
||||
return *list_at_as(Vector, &self->points, at);
|
||||
}
|
||||
|
||||
Vector shape_get_point_transformed(Shape* self, size_t at, Transform transform) {
|
||||
return transform_point(&transform, shape_get_point(self, at));
|
||||
}
|
||||
|
||||
void shape_set_point(Shape* self, size_t at, Vector point) {
|
||||
ASSERT_RETURN(at < self->points.len,, "Point index %zu out of bounds for shape", at);
|
||||
*list_at_as(Vector, &self->points, at) = point;
|
||||
}
|
||||
|
||||
void shape_add_point(Shape* self, Vector point) {
|
||||
list_add(&self->points, &point);
|
||||
}
|
||||
|
||||
void shape_insert_point(Shape* self, size_t at, Vector point) {
|
||||
ASSERT_RETURN(at < self->points.len + 1,, "Point index %zu out of bounds for shape", at);
|
||||
list_insert(&self->points, &point, at);
|
||||
}
|
||||
|
||||
List* shape_get_points(Shape* self) {
|
||||
return &self->points;
|
||||
}
|
||||
|
||||
Vector shape_remove_point(Shape* self, size_t at) {
|
||||
Vector point = *list_at_as(Vector, &self->points, at);
|
||||
list_erase(&self->points, at);
|
||||
return point;
|
||||
}
|
||||
|
||||
Vector shape_get_median_point(Shape* self) {
|
||||
return self->mean;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
32
core/src/shape.h
Normal file
32
core/src/shape.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef _fencer_shape_h
|
||||
#define _fencer_shape_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "transform.h"
|
||||
#include "list.h"
|
||||
|
||||
typedef struct Shape Shape;
|
||||
|
||||
extern Shape* shape_new(const Vector* points, size_t points_len);
|
||||
extern Shape* shape_new_square(Vector size);
|
||||
extern Shape* shape_clone(const Shape* source);
|
||||
extern void shape_destroy(Shape* self);
|
||||
|
||||
extern Vector* shape_get_start(Shape* self);
|
||||
extern Vector* shape_get_end(Shape* self);
|
||||
|
||||
extern size_t shape_get_points_count(const Shape* self);
|
||||
extern Vector shape_get_point(Shape* self, size_t at);
|
||||
extern Vector shape_get_point_transformed(Shape* self, size_t at, Transform transform);
|
||||
|
||||
extern void shape_set_point(Shape* self, size_t at, Vector point);
|
||||
extern void shape_add_point(Shape* self, Vector point);
|
||||
extern void shape_insert_point(Shape* self, size_t at, Vector point);
|
||||
extern List* shape_get_points(Shape* self);
|
||||
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
|
||||
104
core/src/sprite.c
Normal file
104
core/src/sprite.c
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include "sprite.h"
|
||||
#include "camera.h"
|
||||
#include "debug.h"
|
||||
#include "render.h"
|
||||
#include "spritesheet.h"
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
|
||||
struct Sprite {
|
||||
// The animation sheet to sample sprites from.
|
||||
Spritesheet* spritesheet;
|
||||
|
||||
// The current frame of animation.
|
||||
size_t tile_index;
|
||||
|
||||
// The local transformation of this sprite.
|
||||
Vector origin;
|
||||
|
||||
SDL_RendererFlip flip_state;
|
||||
};
|
||||
|
||||
Sprite* sprite_from_spritesheet(Spritesheet* sheet, size_t initial_frame) {
|
||||
Sprite* self = malloc(sizeof(Sprite));
|
||||
|
||||
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate memory for new sprite.");
|
||||
|
||||
self->spritesheet = sheet;
|
||||
self->origin = (Vector){0.5f, 0.5f};
|
||||
self->tile_index = initial_frame;
|
||||
self->flip_state = SDL_FLIP_NONE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void sprite_destroy(Sprite* self) {
|
||||
free(self);
|
||||
}
|
||||
|
||||
void sprite_draw(Sprite* self, Transform transform) {
|
||||
SDL_Texture* texture = spritesheet_get_texture(self->spritesheet);
|
||||
SDL_Rect source = spritesheet_get_tile_rect(self->spritesheet, self->tile_index);
|
||||
|
||||
Vector origin = self->origin;
|
||||
if(self->flip_state && SDL_FLIP_HORIZONTAL) {
|
||||
origin.x = 1.0-origin.x;
|
||||
}
|
||||
if((self->flip_state & SDL_FLIP_VERTICAL) != 0) {
|
||||
origin.y = 1.0-origin.y;
|
||||
}
|
||||
|
||||
Vector left_top = transform_point(&transform, vinvf(origin));
|
||||
SDL_FRect destination = (SDL_FRect) {
|
||||
left_top.x, left_top.y,
|
||||
transform.scale.x, transform.scale.y
|
||||
};
|
||||
destination = camera_world_to_pixel_rect(&g_camera, &destination);
|
||||
|
||||
origin = vmulf(origin, transform.scale);
|
||||
|
||||
SDL_RenderCopyExF(g_renderer, texture, &source, &destination,
|
||||
transform.rotation * 57.2958, &origin, self->flip_state);
|
||||
SDL_SetRenderDrawColor(g_renderer, 255, 255, 255, 255);
|
||||
}
|
||||
|
||||
Vector sprite_get_origin(Sprite* self) {
|
||||
return self->origin;
|
||||
}
|
||||
|
||||
void sprite_set_origin(Sprite* self, Vector origin) {
|
||||
self->origin = origin;
|
||||
}
|
||||
|
||||
size_t sprite_get_tile(const Sprite* self) {
|
||||
return self->tile_index;
|
||||
}
|
||||
|
||||
void sprite_set_tile(Sprite* self, size_t frame) {
|
||||
frame = frame % spritesheet_get_tile_count(self->spritesheet);
|
||||
self->tile_index = frame;
|
||||
}
|
||||
|
||||
Spritesheet* sprite_get_spritesheet(const Sprite* self) {
|
||||
return self->spritesheet;
|
||||
}
|
||||
|
||||
void sprite_set_spritesheet(Sprite* self, Spritesheet* spritesheet) {
|
||||
self->spritesheet = spritesheet;
|
||||
}
|
||||
|
||||
void sprite_flip_horizontal(Sprite* self, int horizontal) {
|
||||
if(horizontal) {
|
||||
self->flip_state |= SDL_FLIP_HORIZONTAL;
|
||||
} else {
|
||||
self->flip_state &= ~SDL_FLIP_HORIZONTAL;
|
||||
}
|
||||
}
|
||||
|
||||
void sprite_flip_vertical(Sprite* self, int vertical) {
|
||||
if(vertical) {
|
||||
self->flip_state |= SDL_FLIP_VERTICAL;
|
||||
} else {
|
||||
self->flip_state &= ~SDL_FLIP_VERTICAL;
|
||||
}
|
||||
}
|
||||
30
core/src/sprite.h
Normal file
30
core/src/sprite.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef _fencer_sprite_h
|
||||
#define _fencer_sprite_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "transform.h"
|
||||
#include "spritesheet.h"
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_rect.h>
|
||||
|
||||
// Forward declaration of the private sprite struct
|
||||
typedef struct Sprite Sprite;
|
||||
|
||||
extern Sprite* sprite_from_spritesheet(Spritesheet* sheet, size_t initial_frame);
|
||||
extern void sprite_destroy(Sprite* sprite);
|
||||
|
||||
extern void sprite_draw(Sprite* self, Transform transform);
|
||||
|
||||
extern Vector sprite_get_origin(Sprite* self);
|
||||
extern void sprite_set_origin(Sprite* self, Vector origin);
|
||||
|
||||
extern size_t sprite_get_tile(const Sprite* self);
|
||||
extern void sprite_set_tile(Sprite* self, size_t tile);
|
||||
|
||||
extern Spritesheet* sprite_get_spritesheet(const Sprite* self);
|
||||
extern void sprite_set_spritesheet(Sprite* self, Spritesheet* spritesheet);
|
||||
|
||||
extern void sprite_flip_horizontal(Sprite* self, int horizontal);
|
||||
extern void sprite_flip_vertical(Sprite* self, int vertical);
|
||||
|
||||
#endif // !_fencer_sprite_h
|
||||
8
core/src/sprite_entity.c
Normal file
8
core/src/sprite_entity.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#include "sprite_entity.h"
|
||||
#include "sprite.h"
|
||||
|
||||
void sprite_entity_draw(SpriteEntity self) {
|
||||
Sprite* sprite = self.tc->get_sprite(self.data);
|
||||
Transform* transform = self.transformable->get_transform(self.data);
|
||||
sprite_draw(sprite, *transform);
|
||||
}
|
||||
29
core/src/sprite_entity.h
Normal file
29
core/src/sprite_entity.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef _fencer_sprite_entity_h
|
||||
#define _fencer_sprite_entity_h
|
||||
|
||||
#include "typeclass_helpers.h"
|
||||
#include "sprite.h"
|
||||
|
||||
typedef struct {
|
||||
Sprite* (*const get_sprite)(void*);
|
||||
} ISpriteEntity;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
ISpriteEntity const* tc;
|
||||
ITransformable const* transformable;
|
||||
} SpriteEntity;
|
||||
|
||||
extern void sprite_entity_draw(SpriteEntity self);
|
||||
|
||||
#define impl_SpriteEntity_for(T, get_sprite_f)\
|
||||
static inline SpriteEntity T##_as_SpriteEntity(T* x) {\
|
||||
TC_FN_TYPECHECK(Sprite*, get_sprite_f, T*);\
|
||||
static ISpriteEntity const tc = {\
|
||||
.get_sprite = (Sprite*(*const)(void*)) get_sprite_f,\
|
||||
};\
|
||||
Transformable t = T##_as_Transformable(x);\
|
||||
return (SpriteEntity){.tc = &tc, .transformable = t.tc, .data = x};\
|
||||
}
|
||||
|
||||
#endif // !_fencer_sprite_entity_h
|
||||
83
core/src/spritesheet.c
Normal file
83
core/src/spritesheet.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include "spritesheet.h"
|
||||
#include "assets.h"
|
||||
#include "render.h"
|
||||
#include "debug.h"
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
|
||||
struct Spritesheet {
|
||||
asset_id asset_id;
|
||||
|
||||
// The texture to sample.
|
||||
SDL_Texture* texture;
|
||||
// The resolution of the texture.
|
||||
IVector resolution;
|
||||
// The number of tiles in the sheet.
|
||||
size_t tile_count;
|
||||
// The number of tiles contained in the width of the texture.
|
||||
size_t tile_shear;
|
||||
// The resolution of a single tile of the sheet.
|
||||
IVector tile_size;
|
||||
};
|
||||
|
||||
void _internal_spritesheet_destroy(Spritesheet* self) {
|
||||
SDL_DestroyTexture(self->texture);
|
||||
free(self);
|
||||
}
|
||||
|
||||
Spritesheet* spritesheet_load(const char* texture_name, IVector tile_size) {
|
||||
SDL_Texture* texture = IMG_LoadTexture(g_renderer, texture_name);
|
||||
ASSERT_RETURN(texture != NULL, NULL, "Failed to load texture from file %s.", texture_name);
|
||||
return spritesheet_from_texture(texture, tile_size);
|
||||
}
|
||||
|
||||
Spritesheet* spritesheet_from_texture(SDL_Texture* texture, IVector tile_size) {
|
||||
Spritesheet* self = malloc(sizeof(Spritesheet));
|
||||
|
||||
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate spritesheet.");
|
||||
|
||||
self->asset_id = store_asset(Spritesheet_as_Asset(self));
|
||||
|
||||
// Load the texture image and query it's size
|
||||
self->texture = texture;
|
||||
SDL_QueryTexture(self->texture, NULL, NULL, &self->resolution.x, &self->resolution.y);
|
||||
|
||||
self->tile_size = tile_size;
|
||||
self->tile_shear = self->resolution.x / self->tile_size.x;
|
||||
self->tile_count = self->resolution.x / self->tile_size.x * self->resolution.y / self->tile_size.y;
|
||||
return self;
|
||||
}
|
||||
|
||||
void spritesheet_destroy(Spritesheet* self) {
|
||||
free_asset(self->asset_id);
|
||||
}
|
||||
|
||||
SDL_Texture* spritesheet_get_texture(const Spritesheet* self) {
|
||||
return self->texture;
|
||||
}
|
||||
|
||||
SDL_Rect spritesheet_get_tile_rect(const Spritesheet* self, size_t index) {
|
||||
IVector tile_coord = {index % self->tile_shear, index / self->tile_shear};
|
||||
tile_coord = vmuli(tile_coord, self->tile_size);
|
||||
|
||||
return (SDL_Rect) {
|
||||
tile_coord.x, tile_coord.y,
|
||||
self->tile_size.x, self->tile_size.y
|
||||
};
|
||||
}
|
||||
|
||||
IVector spritesheet_get_resolution(const Spritesheet* self) {
|
||||
return self->resolution;
|
||||
}
|
||||
|
||||
size_t spritesheet_get_tile_count(const Spritesheet* self) {
|
||||
return self->tile_count;
|
||||
}
|
||||
|
||||
asset_id spritesheet_get_asset_id(Spritesheet* self) {
|
||||
return self->asset_id;
|
||||
}
|
||||
|
||||
void spritesheet_set_asset_id(Spritesheet* self, asset_id id) {
|
||||
self->asset_id = id;
|
||||
}
|
||||
33
core/src/spritesheet.h
Normal file
33
core/src/spritesheet.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef _fencer_spritesheet_h
|
||||
#define _fencer_spritesheet_h
|
||||
|
||||
#include "asset.h"
|
||||
#include "vmath.h"
|
||||
#include <SDL2/SDL_render.h>
|
||||
|
||||
typedef struct Spritesheet Spritesheet;
|
||||
|
||||
extern Spritesheet* spritesheet_load(const char* texture_name, IVector tile_size);
|
||||
extern Spritesheet* spritesheet_from_texture(SDL_Texture* texture, IVector tile_size);
|
||||
extern void spritesheet_destroy(Spritesheet* self);
|
||||
|
||||
extern SDL_Texture* spritesheet_get_texture(const Spritesheet* self);
|
||||
extern SDL_Rect spritesheet_get_tile_rect(const Spritesheet* self, size_t index);
|
||||
extern IVector spritesheet_get_resolution(const Spritesheet* self);
|
||||
extern size_t spritesheet_get_tile_count(const Spritesheet* self);
|
||||
|
||||
extern asset_id spritesheet_get_asset_id(Spritesheet* self);
|
||||
extern void spritesheet_set_asset_id(Spritesheet* self, asset_id id);
|
||||
|
||||
extern void _internal_spritesheet_destroy(Spritesheet* self_void);
|
||||
|
||||
impl_Drop_for(Spritesheet,
|
||||
_internal_spritesheet_destroy
|
||||
)
|
||||
|
||||
impl_Asset_for(Spritesheet,
|
||||
spritesheet_get_asset_id,
|
||||
spritesheet_set_asset_id
|
||||
)
|
||||
|
||||
#endif // !_fencer_spritesheet_h
|
||||
63
core/src/transform.h
Normal file
63
core/src/transform.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef _fencer_transform_h
|
||||
#define _fencer_transform_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "transformable.h"
|
||||
|
||||
typedef struct Transform Transform;
|
||||
struct Transform {
|
||||
Vector position;
|
||||
Vector scale;
|
||||
float rotation;
|
||||
};
|
||||
|
||||
#define IdentityTransform (Transform){ZeroVector, OneVector, 0.0f}
|
||||
|
||||
static inline Transform transform_apply(Transform parent, Transform child);
|
||||
static inline Transform transform_invert(Transform a);
|
||||
static inline Vector transform_direction(Transform* self, Vector direction);
|
||||
static inline Vector transform_point(Transform* self, Vector point);
|
||||
|
||||
static inline
|
||||
Transform transform_apply(Transform parent, Transform child) {
|
||||
return (Transform) {
|
||||
.position = transform_point(&parent, child.position),
|
||||
.scale = vmulf(parent.scale, child.scale),
|
||||
.rotation = parent.rotation + child.rotation
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
Transform transform_invert(Transform a) {
|
||||
return (Transform) {
|
||||
.position = vinvf(a.position),
|
||||
.scale = vreciprocalf(a.scale),
|
||||
.rotation = -a.rotation
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
Vector transform_direction(Transform* self, Vector direction) {
|
||||
return vrotatef(direction, self->rotation);
|
||||
}
|
||||
|
||||
static inline
|
||||
Vector transform_point(Transform* self, Vector position) {
|
||||
return vaddf(vmulf(vrotatef(position, self->rotation), self->scale), self->position);
|
||||
}
|
||||
|
||||
static inline
|
||||
Vector inverse_transform_point(Transform* self, Vector position) {
|
||||
return vrotatef(vmulf(vsubf(position, self->position), vreciprocalf(self->scale)), -self->rotation);
|
||||
}
|
||||
|
||||
static
|
||||
Transform* transform_get_transform(Transform* self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
impl_Transformable_for(Transform,
|
||||
transform_get_transform
|
||||
);
|
||||
|
||||
#endif // !_fencer_transform_h
|
||||
41
core/src/transformable.c
Normal file
41
core/src/transformable.c
Normal 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;
|
||||
}
|
||||
37
core/src/transformable.h
Normal file
37
core/src/transformable.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef _fencer_transformable_h
|
||||
#define _fencer_transformable_h
|
||||
|
||||
#include "vmath.h"
|
||||
#include "typeclass_helpers.h"
|
||||
|
||||
typedef struct {
|
||||
struct Transform* (*const get_transform)(void* self);
|
||||
} ITransformable;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
ITransformable const* tc;
|
||||
} Transformable;
|
||||
|
||||
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*);\
|
||||
static ITransformable const tc = {\
|
||||
.get_transform = (Transform*(*const)(void*)) get_transform_f\
|
||||
};\
|
||||
return (Transformable){.tc = &tc, .data = x};\
|
||||
}
|
||||
|
||||
#endif // !_fencer_transformable_h
|
||||
7
core/src/typeclass_helpers.h
Normal file
7
core/src/typeclass_helpers.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _fencer_typeclass_helpers_h
|
||||
#define _fencer_typeclass_helpers_h
|
||||
|
||||
#define TC_FN_TYPECHECK(__Return, __Name, ...)\
|
||||
__Return (*const __Name##_)(__VA_ARGS__) = __Name; (void)__Name##_
|
||||
|
||||
#endif // !_fencer_typeclass_helpers_h
|
||||
170
core/src/vmath.h
Normal file
170
core/src/vmath.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#ifndef _fencer_vmath_h
|
||||
#define _fencer_vmath_h
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#if VMATH_SDL == 1
|
||||
|
||||
#include <SDL2/SDL_rect.h>
|
||||
typedef SDL_FPoint Vector;
|
||||
typedef SDL_Point IVector;
|
||||
|
||||
#else
|
||||
|
||||
typedef struct Vector {
|
||||
float x;
|
||||
float y;
|
||||
} Vector;
|
||||
|
||||
typedef struct IVector {
|
||||
int x;
|
||||
int y;
|
||||
} IVector;
|
||||
|
||||
#endif
|
||||
|
||||
// Vector Constant Macros
|
||||
#define ZeroVector (Vector){0.0f, 0.0f}
|
||||
#define InfinityVector (Vector){INFINITY, INFINITY}
|
||||
#define OneVector (Vector){1.0f,1.0f}
|
||||
|
||||
#define UpVector (Vector){0.0f,-1.0f}
|
||||
#define RightVector (Vector){1.0f,0.0f}
|
||||
#define LeftVector (Vector){-1.0f,0.0f}
|
||||
#define DownVector (Vector){0.0f,1.0f}
|
||||
|
||||
#define MakeVector(__X, __Y) (Vector){__X, __Y}
|
||||
#define VectorFrom(__A) (Vector){__A, __A}
|
||||
|
||||
// Integer Vector Constant Macros
|
||||
#define ZeroIVector (IVector){0,0}
|
||||
#define OneIVector (IVector){1,1}
|
||||
|
||||
#define UpIVector (IVector){-1,0}
|
||||
#define DownIVector (IVector){1,0}
|
||||
#define RightIVector (IVector){1,0}
|
||||
#define LeftIVector (IVector){-1,0}
|
||||
|
||||
#define MakeIVector(__X, __Y) (IVector){__X, __Y}
|
||||
#define IVectorFrom(__A) (IVector){__A, __A}
|
||||
|
||||
///
|
||||
// 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};
|
||||
}
|
||||
static inline
|
||||
Vector vsubf(Vector a, Vector b) {
|
||||
return (Vector){a.x - b.x, a.y - b.y};
|
||||
}
|
||||
static inline
|
||||
Vector vmulff(Vector a, float b) {
|
||||
return (Vector){a.x * b, a.y * b};
|
||||
}
|
||||
static inline
|
||||
Vector vmulf(Vector a, Vector b) {
|
||||
return (Vector) {a.x * b.x, a.y * b.y};
|
||||
}
|
||||
static inline
|
||||
Vector vinvf(Vector a) {
|
||||
return (Vector){-a.x, -a.y};
|
||||
}
|
||||
static inline
|
||||
Vector vperpendicularf(Vector a) {
|
||||
return (Vector){a.y, -a.x};
|
||||
}
|
||||
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) {
|
||||
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
|
||||
float vdotf(Vector a, Vector b) {
|
||||
return (a.x*b.x) + (a.y*b.y);
|
||||
}
|
||||
static inline
|
||||
float vdistancef(Vector a, Vector b) {
|
||||
return vmagnitudef(vsubf(a, b));
|
||||
}
|
||||
static inline
|
||||
float vsqrdistf(Vector a, Vector b) {
|
||||
return vsqrmagnitudef(vsubf(a, b));
|
||||
}
|
||||
static inline
|
||||
Vector vreciprocalf(Vector a) {
|
||||
return (Vector){1.0/a.x, 1.0/a.y};
|
||||
}
|
||||
static inline
|
||||
Vector vrotatef(Vector a, float t) {
|
||||
return (Vector){
|
||||
cosf(t) * a.x - sinf(t) * a.y,
|
||||
sinf(t) * a.x + cosf(t) * a.y
|
||||
};
|
||||
}
|
||||
static inline
|
||||
float vanglebetweenf(Vector a, Vector b) {
|
||||
return vdotf(a, b) / (vmagnitudef(a) * vmagnitudef(b));
|
||||
}
|
||||
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
|
||||
Vector vaveragef(Vector* array, size_t count) {
|
||||
Vector acc = ZeroVector;
|
||||
for(size_t i = 0; i < count; ++i) {
|
||||
acc = vaddf(acc, array[i]);
|
||||
}
|
||||
return vmulff(acc, 1.0/(float)count);
|
||||
}
|
||||
|
||||
static inline
|
||||
IVector vaddi(IVector a, IVector b) {
|
||||
return (IVector){a.x + b.x, a.y + b.y};
|
||||
}
|
||||
static inline
|
||||
IVector vsubi(IVector a, IVector b) {
|
||||
return (IVector){a.x - b.x, a.y - b.y};
|
||||
}
|
||||
static inline
|
||||
IVector vmuli(IVector a, IVector b) {
|
||||
return (IVector){a.x * b.x, a.y * b.y};
|
||||
}
|
||||
|
||||
#endif // !_fencer_vmath_h
|
||||
Loading…
Add table
Add a link
Reference in a new issue