i somehow managed to forget the fucking source code '~'
parent
d0381ce231
commit
449795d4f7
|
@ -0,0 +1,333 @@
|
||||||
|
//
|
||||||
|
// adb.c
|
||||||
|
// engine
|
||||||
|
//
|
||||||
|
// Created by Scott on 14/10/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "adb.h"
|
||||||
|
#include <memory.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct asset_t {
|
||||||
|
char* filename;
|
||||||
|
size_t extention_offset;
|
||||||
|
void* instance;
|
||||||
|
asset_handle_t handle;
|
||||||
|
} asset_t;
|
||||||
|
|
||||||
|
typedef struct file_handler_t {
|
||||||
|
uint64_t ext_hash;
|
||||||
|
char* extention;
|
||||||
|
asset_load_fn load;
|
||||||
|
asset_free_fn free;
|
||||||
|
} file_handler_t;
|
||||||
|
|
||||||
|
int adb_is_init = 0;
|
||||||
|
|
||||||
|
asset_t* adb_assets_first;
|
||||||
|
size_t adb_assets_last;
|
||||||
|
size_t adb_assets_size;
|
||||||
|
|
||||||
|
file_handler_t* adb_handlers_first;
|
||||||
|
size_t adb_handlers_last;
|
||||||
|
size_t adb_handlers_size;
|
||||||
|
|
||||||
|
static inline uint64_t str_hash(const char* str);
|
||||||
|
static inline void unload_asset(asset_t* asset);
|
||||||
|
static inline size_t file_extention_offset(const char* filename);
|
||||||
|
static inline void free_asset_t(asset_t*);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MODULE LIFETIME
|
||||||
|
//
|
||||||
|
|
||||||
|
int adb_init(size_t max_asset_bytes)
|
||||||
|
{
|
||||||
|
assert(adb_is_init == 0);
|
||||||
|
adb_assets_size = max_asset_bytes;
|
||||||
|
adb_handlers_size = 5;
|
||||||
|
|
||||||
|
adb_assets_last = 0;
|
||||||
|
adb_assets_first = malloc(adb_assets_size * sizeof(asset_t));
|
||||||
|
|
||||||
|
adb_handlers_last = 0;
|
||||||
|
adb_handlers_first = malloc(adb_handlers_size * sizeof(file_handler_t));
|
||||||
|
|
||||||
|
adb_is_init = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int adb_close()
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < adb_assets_last; ++i)
|
||||||
|
{
|
||||||
|
free_asset_t(&adb_assets_first[i]);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < adb_handlers_last; ++i)
|
||||||
|
{
|
||||||
|
free(adb_handlers_first[i].extention);
|
||||||
|
}
|
||||||
|
free(adb_assets_first);
|
||||||
|
free(adb_handlers_first);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// FILE HANDLERS
|
||||||
|
//
|
||||||
|
|
||||||
|
void sort_file_handlers()
|
||||||
|
{
|
||||||
|
size_t swaps = 0;
|
||||||
|
file_handler_t tmp;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
swaps = 0;
|
||||||
|
for(size_t i = 1; i < adb_handlers_last; ++i)
|
||||||
|
{
|
||||||
|
if(adb_handlers_first[i-1].ext_hash > adb_handlers_first[i].ext_hash)
|
||||||
|
{
|
||||||
|
memcpy(&tmp, &adb_handlers_first[i-1], sizeof(file_handler_t));
|
||||||
|
memcpy(&adb_handlers_first[i-1], &adb_handlers_first[i], sizeof(file_handler_t));
|
||||||
|
memcpy(&adb_handlers_first[i], &tmp, sizeof(file_handler_t));
|
||||||
|
++swaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(swaps > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_file_handler(file_handler_t* handler)
|
||||||
|
{
|
||||||
|
free(handler->extention);
|
||||||
|
free(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_handler_t* get_file_handler(const char* extention)
|
||||||
|
{
|
||||||
|
if(adb_handlers_last == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
uint64_t hash = str_hash(extention);
|
||||||
|
long int l = 0, r = adb_handlers_last-1, m;
|
||||||
|
file_handler_t* current;
|
||||||
|
while(l <= r)
|
||||||
|
{
|
||||||
|
m = floorf((float)(l+r)/2.f);
|
||||||
|
current = &adb_handlers_first[m];
|
||||||
|
if(current->ext_hash < hash)
|
||||||
|
l = m + 1;
|
||||||
|
else if(current->ext_hash > hash)
|
||||||
|
r = m - 1;
|
||||||
|
else if(current->ext_hash == hash)
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int adb_register_file_handler(const char* file_extention, asset_load_fn loadfn, asset_free_fn freefn)
|
||||||
|
{
|
||||||
|
file_handler_t* existing = get_file_handler(file_extention);
|
||||||
|
if(existing == NULL)
|
||||||
|
{
|
||||||
|
if(adb_handlers_last + 1 >= adb_handlers_size)
|
||||||
|
{
|
||||||
|
adb_handlers_first = realloc(adb_handlers_first, adb_handlers_size * 2);
|
||||||
|
}
|
||||||
|
file_handler_t* new = (adb_handlers_first+adb_handlers_last);
|
||||||
|
new->load = loadfn;
|
||||||
|
new->free = freefn;
|
||||||
|
new->extention = malloc(strlen(file_extention));
|
||||||
|
strcpy(new->extention, file_extention);
|
||||||
|
new->ext_hash = str_hash(file_extention);
|
||||||
|
++adb_handlers_last;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(loadfn != NULL)
|
||||||
|
existing->load = loadfn;
|
||||||
|
if(freefn != NULL)
|
||||||
|
existing->free = freefn;
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_file_handlers();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ASSETS
|
||||||
|
//
|
||||||
|
|
||||||
|
void sort_assets()
|
||||||
|
{
|
||||||
|
size_t swaps = 0;
|
||||||
|
asset_t tmp;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
swaps = 0;
|
||||||
|
for(size_t i = 0; i < adb_assets_last; ++i)
|
||||||
|
{
|
||||||
|
if(adb_assets_first[i-1].handle > adb_assets_first[i].handle)
|
||||||
|
{
|
||||||
|
memcpy(&tmp, &adb_assets_first[i-1], sizeof(asset_t));
|
||||||
|
memcpy(&adb_assets_first[i-1], &adb_assets_first[i], sizeof(asset_t));
|
||||||
|
memcpy(&adb_assets_first[i], &tmp, sizeof(asset_t));
|
||||||
|
++swaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(swaps > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_t* get_asset_t(asset_handle_t handle)
|
||||||
|
{
|
||||||
|
if(adb_assets_last == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
asset_t* current;
|
||||||
|
long int l = 0, r = adb_assets_last-1, m;
|
||||||
|
while(l <= r)
|
||||||
|
{
|
||||||
|
m = floorf((float)(l+r)/2.f);
|
||||||
|
current = &adb_assets_first[m];
|
||||||
|
|
||||||
|
if(current->handle < handle)
|
||||||
|
l = m + 1;
|
||||||
|
else if(current->handle > handle)
|
||||||
|
r = m - 1;
|
||||||
|
else if(current->handle == handle)
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* adb_get_asset(asset_handle_t handle)
|
||||||
|
{
|
||||||
|
asset_t* asset = get_asset_t(handle);
|
||||||
|
if(asset == NULL)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return asset->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int adb_try_get_asset(asset_handle_t handle, void** o_ptr)
|
||||||
|
{
|
||||||
|
void* asset = adb_get_asset(handle);
|
||||||
|
if(o_ptr != NULL)
|
||||||
|
memcpy(o_ptr, &asset, sizeof(void*));
|
||||||
|
return asset != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_handle_t adb_load_asset(const char* file)
|
||||||
|
{
|
||||||
|
// get the full path of the file
|
||||||
|
char* filename = realpath(file, NULL);
|
||||||
|
if(filename == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// create asset handle
|
||||||
|
asset_handle_t handle = str_hash(filename);
|
||||||
|
|
||||||
|
// just return handle if asset is loaded
|
||||||
|
if(adb_try_get_asset(handle, NULL))
|
||||||
|
return handle;
|
||||||
|
|
||||||
|
// get the index of the file extention
|
||||||
|
size_t ext_offset = file_extention_offset(filename);
|
||||||
|
// find the handler which is supposed to handle files of this type
|
||||||
|
file_handler_t* handler = get_file_handler(filename + ext_offset);
|
||||||
|
|
||||||
|
// adb has no idea how to handle this file type
|
||||||
|
if(handler == NULL)
|
||||||
|
return 0x0;
|
||||||
|
|
||||||
|
// add the asset
|
||||||
|
adb_assets_first[adb_assets_last] = (asset_t){
|
||||||
|
.filename = filename,
|
||||||
|
.extention_offset = ext_offset,
|
||||||
|
.instance = handler->load(filename),
|
||||||
|
.handle = handle
|
||||||
|
};
|
||||||
|
|
||||||
|
// increment asset offset
|
||||||
|
++adb_assets_last;
|
||||||
|
|
||||||
|
// sort assets by handle
|
||||||
|
/// TODO: properly insert new assets rather than sorting every time an asset is added
|
||||||
|
sort_assets();
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_asset_t(asset_t* asset)
|
||||||
|
{
|
||||||
|
file_handler_t* handler = get_file_handler(asset->filename + asset->extention_offset);
|
||||||
|
assert(handler != NULL);
|
||||||
|
|
||||||
|
handler->free(asset->instance);
|
||||||
|
free(asset->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adb_free_asset(asset_handle_t handle)
|
||||||
|
{
|
||||||
|
asset_t* asset = get_asset_t(handle);
|
||||||
|
|
||||||
|
if(asset == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free_asset_t(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// FILES
|
||||||
|
//
|
||||||
|
|
||||||
|
asset_handle_t get_file_handle(const char* filename)
|
||||||
|
{
|
||||||
|
char* real_path = realpath(filename, NULL);
|
||||||
|
asset_handle_t handle = str_hash(real_path);
|
||||||
|
free(real_path);
|
||||||
|
return adb_try_get_asset(handle, NULL) ? handle : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file_extention_offset(const char* filename)
|
||||||
|
{
|
||||||
|
const char* cptr = filename + strlen(filename);
|
||||||
|
while(cptr != filename)
|
||||||
|
{
|
||||||
|
if(*cptr == '.')
|
||||||
|
return cptr - filename;
|
||||||
|
--cptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t str_hash(const char* str)
|
||||||
|
{
|
||||||
|
static const int shift = (8 * sizeof(uint64_t) - 4);
|
||||||
|
static const uint64_t upper4mask = (uint64_t)0xF << shift;
|
||||||
|
uint64_t hash = 0;
|
||||||
|
uint64_t upperfour;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
for(const char* c = str; *c != '\0'; ++c)
|
||||||
|
{
|
||||||
|
upperfour = hash & upper4mask;
|
||||||
|
hash = ((hash << 4) ^ (*c));
|
||||||
|
if(upperfour != 0)
|
||||||
|
hash ^= (upperfour >> shift);
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash ^ len;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// adb.h
|
||||||
|
// engine
|
||||||
|
//
|
||||||
|
// Created by Scott on 14/10/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef adb_h
|
||||||
|
#define adb_h
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint64_t asset_handle_t;
|
||||||
|
|
||||||
|
typedef void* (*asset_load_fn)(const char*);
|
||||||
|
typedef void (*asset_free_fn)(void*);
|
||||||
|
|
||||||
|
typedef struct asset_t asset_t;
|
||||||
|
typedef struct file_handler_t file_handler_t;
|
||||||
|
|
||||||
|
extern int adb_init(size_t max_asset);
|
||||||
|
extern asset_handle_t adb_get_file_handle(const char* filename);
|
||||||
|
extern int adb_asset_is_loaded(asset_handle_t handle);
|
||||||
|
extern void* adb_get_asset(asset_handle_t handle);
|
||||||
|
#define adb_get_asset_as(__TYPE, handle) ((__TYPE*)get_asset(handle))
|
||||||
|
extern int adb_try_get_asset(asset_handle_t handle, void** o_ptr);
|
||||||
|
extern asset_handle_t adb_load_asset(const char* file);
|
||||||
|
extern void adb_free_asset(asset_handle_t handle);
|
||||||
|
extern size_t adb_set_asset_memory(size_t max_bytes);
|
||||||
|
extern int adb_close();
|
||||||
|
extern int adb_register_file_handler(const char* file_extention, asset_load_fn loadfn, asset_free_fn freefn);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* adb_h */
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include "border_interaction_c.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <camera.h>
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
ecsComponentMask border_interaction_component = nocomponent;
|
||||||
|
|
||||||
|
void _wrap_position(fvec* position, fvec margin, SDL_Rect border)
|
||||||
|
{
|
||||||
|
// wrap horizontal
|
||||||
|
if(position->x < border.x-margin.x)
|
||||||
|
position->x += border.w + margin.x*2;
|
||||||
|
else if(position->x > border.x + border.w + margin.x)
|
||||||
|
position->x -= border.w + margin.x*2;
|
||||||
|
|
||||||
|
// wrap vertical
|
||||||
|
if(position->y < border.y-margin.y)
|
||||||
|
position->y += border.h + margin.y*2;
|
||||||
|
else if(position->y > border.y + border.h + margin.x)
|
||||||
|
position->y -= border.h + margin.y*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void border_interaction_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
fvec* position;
|
||||||
|
border_interaction_t* interaction;
|
||||||
|
view_t* view = ecsGetComponentPtr(get_main_camera(), view_component);
|
||||||
|
SDL_Rect border = {-(view->width/2), -(view->height/2), view->width, view->height};
|
||||||
|
|
||||||
|
if(query_update_group(&physics_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
position = ecsGetComponentPtr(entities[i], position_component);
|
||||||
|
interaction = ecsGetComponentPtr(entities[i], border_interaction_component);
|
||||||
|
|
||||||
|
switch(interaction->type)
|
||||||
|
{
|
||||||
|
default: assert(0); break;
|
||||||
|
case BORDER_WRAP:
|
||||||
|
_wrap_position(position, interaction->margin, border);
|
||||||
|
break;
|
||||||
|
case BORDER_DESTROY:
|
||||||
|
{
|
||||||
|
if(position->x < border.x || position->x > border.x + border.w || position->y < border.y || position->y > border.y + border.h )
|
||||||
|
{
|
||||||
|
ecsDestroyEntity(entities[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef border_interaction_c_h
|
||||||
|
#define border_interaction_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
typedef enum border_interaction_e {
|
||||||
|
BORDER_WRAP,
|
||||||
|
BORDER_DESTROY
|
||||||
|
} border_interaction_e;
|
||||||
|
|
||||||
|
typedef struct border_interaction_t {
|
||||||
|
border_interaction_e type;
|
||||||
|
fvec margin;
|
||||||
|
} border_interaction_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern ecsComponentMask border_interaction_component;
|
||||||
|
|
||||||
|
extern void border_interaction_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* border_interaction_c_h */
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
ecsEntityId camera_main = noentity;
|
||||||
|
|
||||||
|
void set_main_camera(ecsEntityId camera)
|
||||||
|
{
|
||||||
|
if(get_main_camera() == noentity)
|
||||||
|
{
|
||||||
|
camera_main = camera;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ecsEntityId get_main_camera()
|
||||||
|
{
|
||||||
|
if((ecsGetComponentMask(camera_main) & CAMERA_A) == 0)
|
||||||
|
{
|
||||||
|
return (camera_main = noentity);
|
||||||
|
}
|
||||||
|
else return camera_main;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_camera_rect(ecsEntityId camera, SDL_Rect* o_rect)
|
||||||
|
{
|
||||||
|
view_t* view = ecsGetComponentPtr(camera, view_component);
|
||||||
|
fvec* position = ecsGetComponentPtr(camera, position_component);
|
||||||
|
|
||||||
|
if(view != NULL && position != NULL)
|
||||||
|
{
|
||||||
|
get_view_rect(view, o_rect);
|
||||||
|
return;
|
||||||
|
|
||||||
|
o_rect->x += (int)floorf(position->x);
|
||||||
|
o_rect->y += (int)floorf(position->y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*o_rect = (SDL_Rect){0,0,0,0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ecsEntityId create_default_camera()
|
||||||
|
{
|
||||||
|
ecsEntityId camera_id = ecsCreateEntity(CAMERA_A);
|
||||||
|
|
||||||
|
if(camera_id != noentity)
|
||||||
|
{
|
||||||
|
view_t* view = ecsGetComponentPtr(camera_id, view_component);
|
||||||
|
int width, height;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &width, &height);
|
||||||
|
double asp_ratio = (double)width/height;
|
||||||
|
view->width = 50;
|
||||||
|
view->height = view->width * asp_ratio;
|
||||||
|
|
||||||
|
fvec* position = ecsGetComponentPtr(camera_id, position_component);
|
||||||
|
position->x = 0.f;
|
||||||
|
position->y = 0.f;
|
||||||
|
|
||||||
|
if(get_main_camera() == noentity)
|
||||||
|
set_main_camera(camera_id);
|
||||||
|
}
|
||||||
|
return camera_id;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef camera_h
|
||||||
|
#define camera_h
|
||||||
|
|
||||||
|
#include "view_c.h"
|
||||||
|
#include "transform_c.h"
|
||||||
|
|
||||||
|
extern ecsEntityId get_main_camera();
|
||||||
|
|
||||||
|
extern void set_main_camera(ecsEntityId entity);
|
||||||
|
|
||||||
|
extern ecsEntityId create_default_camera();
|
||||||
|
|
||||||
|
extern void get_camera_rect(ecsEntityId camera, SDL_Rect* o_rect);
|
||||||
|
|
||||||
|
#define CAMERA_A position_component | view_component
|
||||||
|
|
||||||
|
#endif /* camera_h */
|
||||||
|
|
|
@ -0,0 +1,334 @@
|
||||||
|
#include "collision_shape_c.h"
|
||||||
|
|
||||||
|
#include "transform_c.h"
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
ecsComponentMask shape_component = 0x0;
|
||||||
|
|
||||||
|
pthread_mutex_t col_reg_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
size_t col_num_registered = 0;
|
||||||
|
ecsEntityId* col_registered_shapes = NULL;
|
||||||
|
|
||||||
|
typedef struct simplex_t {
|
||||||
|
fvec points[4];
|
||||||
|
int count;
|
||||||
|
} simplex_t;
|
||||||
|
|
||||||
|
void _simplex_push(simplex_t* simpl, fvec point);
|
||||||
|
int _next_simplex(simplex_t* simpl, fvec* direction);
|
||||||
|
fvec _support_point(shape_t* a, shape_t* b, fvec direction);
|
||||||
|
fvec _shape_furthest_point(shape_t* shape, fvec direction);
|
||||||
|
int _entities_overlap(ecsEntityId a, ecsEntityId b);
|
||||||
|
void _remove_from_collision_world(size_t index);
|
||||||
|
|
||||||
|
ecsEntityId shape_overlap_test(shape_t* shape, fvec position)
|
||||||
|
{
|
||||||
|
shape_t* other;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < col_num_registered; ++i)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(col_registered_shapes[i], shape_component);
|
||||||
|
|
||||||
|
if(other == NULL)
|
||||||
|
_remove_from_collision_world(i);
|
||||||
|
|
||||||
|
if(col_num_registered == 0)
|
||||||
|
return noentity;
|
||||||
|
} while(other == NULL);
|
||||||
|
|
||||||
|
if(shapes_overlap(shape, other))
|
||||||
|
{
|
||||||
|
return col_registered_shapes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _transform_shape(ecsEntityId entity, shape_t* shape)
|
||||||
|
{
|
||||||
|
shape_t* a = ecsGetComponentPtr(entity, shape_component);
|
||||||
|
memcpy(shape, a, sizeof(shape_t));
|
||||||
|
|
||||||
|
fvec* position = ecsGetComponentPtr(entity, position_component);
|
||||||
|
fvec* scale = ecsGetComponentPtr(entity, scale_component);
|
||||||
|
float* rotation = ecsGetComponentPtr(entity, rotation_component);
|
||||||
|
|
||||||
|
transform_point(&shape->position_offset, position, scale, rotation);
|
||||||
|
|
||||||
|
shape->scale = *scale;
|
||||||
|
shape->rotation_offset += *rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _entities_overlap(ecsEntityId entity_a, ecsEntityId entity_b)
|
||||||
|
{
|
||||||
|
// make copies of the two shapes so that we can transform them safely
|
||||||
|
shape_t real_a;
|
||||||
|
shape_t real_b;
|
||||||
|
_transform_shape(entity_a, &real_a);
|
||||||
|
_transform_shape(entity_b, &real_b);
|
||||||
|
return shapes_overlap(&real_a, &real_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int shapes_overlap(shape_t* a, shape_t* b)
|
||||||
|
{
|
||||||
|
if(a == NULL || b == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fvec direction = VRIGHT;
|
||||||
|
fvec support = _support_point(a, b, direction);
|
||||||
|
|
||||||
|
simplex_t simplex;
|
||||||
|
_simplex_push(&simplex, support);
|
||||||
|
vmulf(&direction, &direction, -1.f);
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
support = _support_point(a, b, direction);
|
||||||
|
|
||||||
|
if(vdot(&support, &direction) <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_simplex_push(&simplex, support);
|
||||||
|
|
||||||
|
if(_next_simplex(&simplex, &direction))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_to_collision_world(ecsEntityId entity)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&col_reg_mutex);
|
||||||
|
size_t new_registered = col_num_registered + 1;
|
||||||
|
col_registered_shapes = realloc(col_registered_shapes, sizeof(ecsEntityId) * new_registered);
|
||||||
|
|
||||||
|
col_registered_shapes[col_num_registered] = entity;
|
||||||
|
col_num_registered = new_registered;
|
||||||
|
pthread_mutex_unlock(&col_reg_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _remove_from_collision_world(size_t index)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&col_reg_mutex);
|
||||||
|
if(col_num_registered > 0)
|
||||||
|
{
|
||||||
|
size_t length_after = (col_num_registered - index);
|
||||||
|
|
||||||
|
ecsEntityId* dest = col_registered_shapes + index;
|
||||||
|
ecsEntityId* src = dest + 1;
|
||||||
|
|
||||||
|
memmove(dest, src, length_after);
|
||||||
|
|
||||||
|
col_num_registered -= 1;
|
||||||
|
|
||||||
|
if(col_num_registered == 0)
|
||||||
|
{
|
||||||
|
free(col_registered_shapes);
|
||||||
|
col_registered_shapes = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ecsEntityId* nreg = NULL;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nreg = realloc(col_registered_shapes, sizeof(ecsEntityId) * col_num_registered);
|
||||||
|
} while(nreg == NULL);
|
||||||
|
col_registered_shapes = nreg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&col_reg_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ecsEntityId shape_sweep(ecsEntityId entity, fvec move, short trigger_overlap)
|
||||||
|
{
|
||||||
|
ecsEntityId hit = noentity;
|
||||||
|
ecsEntityId newhit;
|
||||||
|
fvec* position = ecsGetComponentPtr(entity, position_component);
|
||||||
|
fvec target_position;
|
||||||
|
vadd(&target_position, position, &move);
|
||||||
|
shape_t* shape = ecsGetComponentPtr(entity, shape_component);
|
||||||
|
shape_t* other = NULL;
|
||||||
|
|
||||||
|
while(!veq(position, &target_position))
|
||||||
|
{
|
||||||
|
vmovetowards(position, position, &target_position, 0.1f);
|
||||||
|
|
||||||
|
newhit = shape_overlap_test(shape, *position);
|
||||||
|
|
||||||
|
if(newhit)
|
||||||
|
{
|
||||||
|
if(trigger_overlap)
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(newhit, shape_component);
|
||||||
|
other->on_overlap(newhit, entity);
|
||||||
|
shape->on_overlap(entity, newhit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hit == noentity)
|
||||||
|
{
|
||||||
|
hit = newhit;
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// support functions
|
||||||
|
fvec _circle_furthest_point(shape_t* shape, fvec direction)
|
||||||
|
{
|
||||||
|
vnor(&direction, &direction);
|
||||||
|
vmulf(&direction, &direction, shape->circle.radius);
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
fvec _array_furthest_point(fvec* array, size_t count, fvec direction)
|
||||||
|
{
|
||||||
|
vnor(&direction, &direction);
|
||||||
|
fvec point = array[0];
|
||||||
|
float dot_to_beat = vdot(array, &direction);
|
||||||
|
for(int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
float dot = vdot(array + i, &direction);
|
||||||
|
if(dot > dot_to_beat)
|
||||||
|
{
|
||||||
|
dot_to_beat = dot;
|
||||||
|
point = array[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
fvec _rect_furthest_point(shape_t* shape, fvec direction)
|
||||||
|
{
|
||||||
|
fvec points[4] = {
|
||||||
|
{shape->rect.x, shape->rect.y},
|
||||||
|
{shape->rect.x + shape->rect.w, shape->rect.y},
|
||||||
|
{shape->rect.x + shape->rect.w, shape->rect.y + shape->rect.h},
|
||||||
|
{shape->rect.x, shape->rect.y + shape->rect.h}
|
||||||
|
};
|
||||||
|
|
||||||
|
fvec point = _array_furthest_point(points, 4, direction);
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
fvec _shape_furthest_point(shape_t* shape, fvec direction)
|
||||||
|
{
|
||||||
|
switch(shape->type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return VZERO;
|
||||||
|
case SHAPE_CIRCLE:
|
||||||
|
return _circle_furthest_point(shape, direction);
|
||||||
|
case SHAPE_RECT:
|
||||||
|
return _circle_furthest_point(shape, direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fvec _support_point(shape_t* a, shape_t* b, fvec direction)
|
||||||
|
{
|
||||||
|
fvec inv_dir;
|
||||||
|
vmulf(&inv_dir, &direction, -1.f);
|
||||||
|
|
||||||
|
fvec fpa = _shape_furthest_point(a, direction);
|
||||||
|
fvec fpb = _shape_furthest_point(b, inv_dir);
|
||||||
|
transform_point(&fpa, &a->position_offset, &a->scale, &a->rotation_offset);
|
||||||
|
transform_point(&fpb, &b->position_offset, &b->scale, &b->rotation_offset);
|
||||||
|
|
||||||
|
vsub(&direction, &fpa, &fpb);
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _simplex_push(simplex_t* simpl, fvec point)
|
||||||
|
{
|
||||||
|
memcpy(&simpl->points[1], simpl->points, sizeof(fvec) * 3);
|
||||||
|
simpl->points[0] = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _same_direction(fvec direction, fvec ao)
|
||||||
|
{
|
||||||
|
return vdot(&direction, &ao) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _simplex_line(simplex_t* simplex, fvec* direction)
|
||||||
|
{
|
||||||
|
fvec a = simplex->points[0];
|
||||||
|
fvec b = simplex->points[1];
|
||||||
|
|
||||||
|
fvec ab, ao;
|
||||||
|
vmulf(&ao, &a, -1.f);
|
||||||
|
vsub(&ab, &b, &a);
|
||||||
|
|
||||||
|
if(_same_direction(ab, ao))
|
||||||
|
{
|
||||||
|
vtriple(direction, &ab, &ao);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
simplex->count = 1;
|
||||||
|
*direction = ao;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _simplex_triangle(simplex_t* simplex, fvec* direction)
|
||||||
|
{
|
||||||
|
fvec a = simplex->points[0];
|
||||||
|
fvec b = simplex->points[1];
|
||||||
|
fvec c = simplex->points[2];
|
||||||
|
|
||||||
|
fvec ab, ac, ao;
|
||||||
|
vsub(&ab, &b, &a);
|
||||||
|
vsub(&ac, &c, &a);
|
||||||
|
vmulf(&ao, &a, -1.f);
|
||||||
|
|
||||||
|
fvec abp, acp;
|
||||||
|
vtriple(&abp, &ac, &ab);
|
||||||
|
vtriple(&acp, &ab, &ac);
|
||||||
|
|
||||||
|
if(vdot(&abp, &ao) > 0)
|
||||||
|
{
|
||||||
|
// remove c
|
||||||
|
*direction = abp;
|
||||||
|
simplex->count = 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(vdot(&acp, &ao) > 0)
|
||||||
|
{
|
||||||
|
// remove b
|
||||||
|
simplex->points[1] = simplex->points[2];
|
||||||
|
simplex->count = 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _next_simplex(simplex_t* simpl, fvec* direction)
|
||||||
|
{
|
||||||
|
switch(simpl->count)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
return _simplex_line(simpl, direction);
|
||||||
|
case 3:
|
||||||
|
return _simplex_triangle(simpl, direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef collision_shape_c_h
|
||||||
|
#define collision_shape_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
typedef void (*on_overlap_fn)(ecsEntityId self, ecsEntityId other);
|
||||||
|
|
||||||
|
extern ecsComponentMask shape_component;
|
||||||
|
|
||||||
|
typedef enum SHAPE_TYPE {
|
||||||
|
SHAPE_CIRCLE,
|
||||||
|
SHAPE_RECT
|
||||||
|
} SHAPE_TYPE;
|
||||||
|
|
||||||
|
typedef struct shape_t {
|
||||||
|
SHAPE_TYPE type;
|
||||||
|
on_overlap_fn on_overlap;
|
||||||
|
|
||||||
|
float rotation_offset;
|
||||||
|
fvec position_offset;
|
||||||
|
fvec scale;
|
||||||
|
|
||||||
|
union {
|
||||||
|
SDL_Rect rect;
|
||||||
|
struct {
|
||||||
|
float radius;
|
||||||
|
} circle;
|
||||||
|
};
|
||||||
|
} shape_t;
|
||||||
|
|
||||||
|
extern ecsEntityId shape_sweep(ecsEntityId entity, fvec move, short trigger_overlap);
|
||||||
|
extern int shapes_overlap(shape_t* a, shape_t* b);
|
||||||
|
extern ecsEntityId shape_overlap_test(shape_t* shape, fvec position);
|
||||||
|
|
||||||
|
extern void add_to_collision_world(ecsEntityId entity);
|
||||||
|
|
||||||
|
#endif /* collision_shape_c_h */
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
ecsComponentMask collision_shape_component = 0x0;
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef components_h
|
||||||
|
#define components_h
|
||||||
|
|
||||||
|
#include "transform_c.h"
|
||||||
|
#include "velocity_c.h"
|
||||||
|
#include "sprite_c.h"
|
||||||
|
#include "collision_shape_c.h"
|
||||||
|
#include "border_interaction_c.h"
|
||||||
|
#include "view_c.h"
|
||||||
|
|
||||||
|
#endif /* components_h */
|
|
@ -0,0 +1,300 @@
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ecs.h>
|
||||||
|
#include "components.h"
|
||||||
|
#include "adb.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
typedef struct update_group_t {
|
||||||
|
double last_time;
|
||||||
|
double time;
|
||||||
|
double delta_time;
|
||||||
|
double target_delta;
|
||||||
|
short is_frame;
|
||||||
|
} update_group_t;
|
||||||
|
|
||||||
|
SDL_Window* window;
|
||||||
|
SDL_Renderer* renderer;
|
||||||
|
int engine_wants_to_quit;
|
||||||
|
|
||||||
|
update_group_t frame_update;
|
||||||
|
update_group_t physics_update;
|
||||||
|
|
||||||
|
void system_window_clear(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
void system_window_display(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
void system_update_groups(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
void system_poll_events(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
void engine_init();
|
||||||
|
void engine_run();
|
||||||
|
void engine_handle_event(SDL_Event* event);
|
||||||
|
void engine_clean();
|
||||||
|
|
||||||
|
void engine_update_group(update_group_t* group);
|
||||||
|
|
||||||
|
void* asset_load_sdl_image(const char* filename)
|
||||||
|
{
|
||||||
|
SDL_Surface* surf = IMG_Load(filename);
|
||||||
|
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
|
||||||
|
SDL_FreeSurface(surf);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void asset_free_sdl_image(void* instance)
|
||||||
|
{
|
||||||
|
SDL_Texture* tex = instance;
|
||||||
|
if(tex != NULL)
|
||||||
|
{
|
||||||
|
SDL_DestroyTexture(tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* asset_load_ttf_font(const char* filename)
|
||||||
|
{
|
||||||
|
TTF_Font* font = TTF_OpenFont(filename, 50);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
void asset_free_ttf_font(void* instance)
|
||||||
|
{
|
||||||
|
TTF_Font* font = instance;
|
||||||
|
if(instance != NULL)
|
||||||
|
{
|
||||||
|
TTF_CloseFont(font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
engine_init();
|
||||||
|
engine_run();
|
||||||
|
engine_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_init()
|
||||||
|
{
|
||||||
|
engine_wants_to_quit = 0;
|
||||||
|
|
||||||
|
// init asset database for 100 assets
|
||||||
|
adb_init(100);
|
||||||
|
|
||||||
|
// register default image file handlers
|
||||||
|
adb_register_file_handler(".png", &asset_load_sdl_image, &asset_free_sdl_image);
|
||||||
|
adb_register_file_handler(".jpg", &asset_load_sdl_image, &asset_free_sdl_image);
|
||||||
|
// default font file handlers
|
||||||
|
adb_register_file_handler(".ttf", &asset_load_ttf_font, &asset_free_ttf_font);
|
||||||
|
adb_register_file_handler(".otf", &asset_load_ttf_font, &asset_free_ttf_font);
|
||||||
|
|
||||||
|
// load default init settings
|
||||||
|
engine_init_t init_settings;
|
||||||
|
default_engine_init_settings(&init_settings);
|
||||||
|
// set default target framerates
|
||||||
|
set_update_group_target_framerate(&frame_update, 60);
|
||||||
|
physics_update.target_delta = 0.f;
|
||||||
|
engine_update_group(&frame_update);
|
||||||
|
engine_update_group(&physics_update);
|
||||||
|
|
||||||
|
// allow game to adjust init settings as needed
|
||||||
|
game_config(&init_settings);
|
||||||
|
|
||||||
|
// init sdl, create window and create renderer from window
|
||||||
|
SDL_Init(init_settings.sdl_init_flags);
|
||||||
|
TTF_Init();
|
||||||
|
IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
|
||||||
|
window = SDL_CreateWindow(init_settings.window_name,
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
init_settings.window_width,
|
||||||
|
init_settings.window_height,
|
||||||
|
init_settings.window_init_flags);
|
||||||
|
|
||||||
|
if(window == NULL)
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer = SDL_CreateRenderer(window, init_settings.renderer_index,
|
||||||
|
init_settings.renderer_init_flags);
|
||||||
|
|
||||||
|
if(renderer == NULL)
|
||||||
|
{
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init input system
|
||||||
|
input_init();
|
||||||
|
|
||||||
|
// initialize runtime object database
|
||||||
|
ecsInit();
|
||||||
|
|
||||||
|
// initialize imgui renderer
|
||||||
|
uiInit(renderer);
|
||||||
|
|
||||||
|
position_component = ecsRegisterComponent(fvec);
|
||||||
|
rotation_component = ecsRegisterComponent(float);
|
||||||
|
scale_component = ecsRegisterComponent(fvec);
|
||||||
|
|
||||||
|
linear_velocity_component = ecsRegisterComponent(fvec);
|
||||||
|
angular_velocity_component = ecsRegisterComponent(float);
|
||||||
|
linear_drag_component = ecsRegisterComponent(float);
|
||||||
|
angular_drag_component = ecsRegisterComponent(float);
|
||||||
|
|
||||||
|
sprite_component = ecsRegisterComponent(sprite_t);
|
||||||
|
border_interaction_component = ecsRegisterComponent(border_interaction_t);
|
||||||
|
view_component = ecsRegisterComponent(view_t);
|
||||||
|
shape_component = ecsRegisterComponent(shape_t);
|
||||||
|
|
||||||
|
// enable core engine systems
|
||||||
|
ecsEnableSystem(&system_poll_events, nocomponent, ECS_NOQUERY, 0, -300);
|
||||||
|
ecsEnableSystem(&system_update_groups, nocomponent, ECS_NOQUERY, 0, -200);
|
||||||
|
ecsEnableSystem(&system_update_views, view_component, ECS_QUERY_ALL, MAX_THREADS, 499);
|
||||||
|
|
||||||
|
// 500-599 is for rendering
|
||||||
|
ecsEnableSystem(&system_window_clear, nocomponent, ECS_NOQUERY, 0, 500);
|
||||||
|
ecsEnableSystem(&render_textures_s, sprite_component | position_component, ECS_QUERY_ALL, 0, 550);
|
||||||
|
ecsEnableSystem(&system_window_display, nocomponent, ECS_NOQUERY, 0, 599);
|
||||||
|
|
||||||
|
|
||||||
|
// 600-999 is for post-render updates
|
||||||
|
ecsEnableSystem(&linear_velocity_update_s, position_component | linear_velocity_component, ECS_QUERY_ALL, MAX_THREADS, 750);
|
||||||
|
ecsEnableSystem(&angular_velocity_update_s, rotation_component | angular_velocity_component, ECS_QUERY_ALL, MAX_THREADS, 755);
|
||||||
|
ecsEnableSystem(&border_interaction_s, border_interaction_component | position_component, ECS_QUERY_ALL, MAX_THREADS, 810);
|
||||||
|
ecsEnableSystem(&wrap_rotation_s, rotation_component, ECS_QUERY_ALL, 8, 815);
|
||||||
|
|
||||||
|
|
||||||
|
// initialize game
|
||||||
|
game_init();
|
||||||
|
|
||||||
|
// run created tasks
|
||||||
|
ecsRunTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_run()
|
||||||
|
{
|
||||||
|
float actual_time;
|
||||||
|
|
||||||
|
while(!engine_wants_to_quit)
|
||||||
|
{
|
||||||
|
float physics_update_delta;
|
||||||
|
query_update_group(&physics_update, &physics_update_delta);
|
||||||
|
ecsRunSystems(physics_update_delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_handle_event(SDL_Event* event)
|
||||||
|
{
|
||||||
|
switch(event->type)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case SDL_QUIT:
|
||||||
|
engine_wants_to_quit = 1;
|
||||||
|
break;
|
||||||
|
case SDL_WINDOWEVENT:
|
||||||
|
{
|
||||||
|
switch(event->window.event)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
ecsEnableSystem(&system_update_views, view_component, ECS_QUERY_ALL, 8, -500);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_clean()
|
||||||
|
{
|
||||||
|
// quit game, ui asset database, ecs
|
||||||
|
game_quit();
|
||||||
|
uiTerminate();
|
||||||
|
adb_close();
|
||||||
|
ecsTerminate();
|
||||||
|
input_terminate();
|
||||||
|
|
||||||
|
// delete renderer and window, quit sdl
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_update_group(update_group_t* group)
|
||||||
|
{
|
||||||
|
group->time = (double)clock() / (double)CLOCKS_PER_SEC;
|
||||||
|
double delta_time = group->time - group->last_time;
|
||||||
|
group->is_frame = delta_time >= group->target_delta;
|
||||||
|
|
||||||
|
if(group->is_frame)
|
||||||
|
{
|
||||||
|
group->last_time = group->time;
|
||||||
|
group->delta_time = delta_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_update_groups(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time)
|
||||||
|
{
|
||||||
|
engine_update_group(&frame_update);
|
||||||
|
engine_update_group(&physics_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_window_clear(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time)
|
||||||
|
{
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
// clear screen all black
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void system_window_display(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time)
|
||||||
|
{
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
// swap buffer
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_poll_events(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
SDL_Event evt;
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
while(SDL_PollEvent(&evt))
|
||||||
|
{
|
||||||
|
engine_handle_event(&evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short query_update_group(update_group_t* update_group, float* out_delta_time)
|
||||||
|
{
|
||||||
|
if(out_delta_time != NULL)
|
||||||
|
(*out_delta_time) = (float)update_group->delta_time;
|
||||||
|
|
||||||
|
return update_group->is_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_update_group_target_framerate(update_group_t* group, int fps)
|
||||||
|
{
|
||||||
|
group->target_delta = 1.f / fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void default_engine_init_settings(engine_init_t* init_settings)
|
||||||
|
{
|
||||||
|
(*init_settings) = (engine_init_t){
|
||||||
|
.window_width = 640, .window_height = 420,
|
||||||
|
.window_init_flags = SDL_WINDOW_SHOWN,
|
||||||
|
.sdl_init_flags = SDL_INIT_VIDEO,
|
||||||
|
.renderer_init_flags = SDL_RENDERER_ACCELERATED,
|
||||||
|
.renderer_index = -1,
|
||||||
|
.window_name = "Game"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef _engine_h
|
||||||
|
#define _engine_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef MAX_THREADS
|
||||||
|
#define MAX_THREADS 8
|
||||||
|
#endif /* MAX_THREADS */
|
||||||
|
|
||||||
|
typedef struct engine_init_t {
|
||||||
|
int window_width, window_height;
|
||||||
|
uint32_t window_init_flags;
|
||||||
|
uint32_t sdl_init_flags;
|
||||||
|
uint32_t renderer_init_flags;
|
||||||
|
int renderer_index;
|
||||||
|
const char* window_name;
|
||||||
|
} engine_init_t;
|
||||||
|
|
||||||
|
typedef struct update_group_t update_group_t;
|
||||||
|
|
||||||
|
extern update_group_t frame_update;
|
||||||
|
extern update_group_t physics_update;
|
||||||
|
|
||||||
|
extern void default_engine_init_settings(engine_init_t*);
|
||||||
|
|
||||||
|
extern void game_config(engine_init_t*);
|
||||||
|
extern void game_init();
|
||||||
|
extern void game_quit();
|
||||||
|
|
||||||
|
extern short query_update_group(update_group_t* group, float* delta_time);
|
||||||
|
extern void set_update_group_target_framerate(update_group_t* group, int fps);
|
||||||
|
|
||||||
|
extern struct SDL_Renderer* renderer;
|
||||||
|
|
||||||
|
#if defined(DEBUG)
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define E_LOG(...)\
|
||||||
|
fprintf(stdout, "%s:%d: ", __FILE__, __LINE__);\
|
||||||
|
fprintf(stdout, __VA_ARGS__);\
|
||||||
|
fprintf(stdout, "\n")
|
||||||
|
#else
|
||||||
|
#define E_LOG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
#endif /* !_engine_h */
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef fmath_h
|
||||||
|
#define fmath_h
|
||||||
|
|
||||||
|
static inline
|
||||||
|
float fmovetowards(float start, float target, float delta)
|
||||||
|
{
|
||||||
|
if(start - delta > target)
|
||||||
|
start -= delta;
|
||||||
|
else if(start + delta < target)
|
||||||
|
start += delta;
|
||||||
|
else
|
||||||
|
start = target;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* fmath_h */
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "input.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
const uint8_t* input_key_array;
|
||||||
|
int input_num_keys;
|
||||||
|
|
||||||
|
void input_init()
|
||||||
|
{
|
||||||
|
input_key_array = SDL_GetKeyboardState(&input_num_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_terminate() {}
|
||||||
|
|
||||||
|
int8_t input_axis(unsigned key_negative, unsigned key_positive)
|
||||||
|
{
|
||||||
|
return input_button(key_positive) - input_button(key_negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t input_button(unsigned key)
|
||||||
|
{
|
||||||
|
if(key < input_num_keys)
|
||||||
|
{
|
||||||
|
return input_key_array[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef input_h
|
||||||
|
#define input_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern void input_init();
|
||||||
|
extern void input_terminate();
|
||||||
|
extern int8_t input_axis(unsigned key_negative, unsigned key_positive);
|
||||||
|
extern int8_t input_button(unsigned key);
|
||||||
|
|
||||||
|
#endif /* input_h */
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "sprite_c.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#include "transform_c.h"
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
ecsComponentMask sprite_component = 0x0;
|
||||||
|
|
||||||
|
void render_textures_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
SDL_Rect camera_rect;
|
||||||
|
ecsEntityId id;
|
||||||
|
|
||||||
|
sprite_t* sprite;
|
||||||
|
fvec* position;
|
||||||
|
float* rotation;
|
||||||
|
fvec* scale;
|
||||||
|
float scale_mul;
|
||||||
|
|
||||||
|
float angle;
|
||||||
|
SDL_FRect dst;
|
||||||
|
int window_width, window_height;
|
||||||
|
|
||||||
|
SDL_GetRendererOutputSize(renderer, &window_width, &window_height);
|
||||||
|
get_camera_rect(get_main_camera(), &camera_rect);
|
||||||
|
scale_mul = (float)window_width / camera_rect.w;
|
||||||
|
|
||||||
|
SDL_RenderDrawRect(renderer, &camera_rect);
|
||||||
|
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
id = entities[i];
|
||||||
|
sprite = ecsGetComponentPtr(id, sprite_component);
|
||||||
|
position = ecsGetComponentPtr(id, position_component);
|
||||||
|
rotation = ecsGetComponentPtr(id, rotation_component);
|
||||||
|
scale = ecsGetComponentPtr(id, scale_component);
|
||||||
|
|
||||||
|
if(sprite->texture != NULL)
|
||||||
|
{
|
||||||
|
angle = rotation ? (*rotation) : 0;
|
||||||
|
dst = (SDL_FRect){
|
||||||
|
.x=(position->x - camera_rect.x) * scale_mul,
|
||||||
|
.y=(position->y - camera_rect.y) * scale_mul,
|
||||||
|
.w=(scale ? scale->x : 1.f) * scale_mul,
|
||||||
|
.h=(scale ? scale->y : 1.f) * scale_mul,
|
||||||
|
};
|
||||||
|
|
||||||
|
dst.x -= dst.w * 0.5f;
|
||||||
|
dst.y -= dst.h * 0.5f;
|
||||||
|
|
||||||
|
SDL_RenderCopyExF(
|
||||||
|
renderer, sprite->texture,
|
||||||
|
&sprite->rect, &dst, (double)angle,
|
||||||
|
&(SDL_FPoint){.x=dst.w*0.5f, .y=dst.h*0.5f},
|
||||||
|
0x0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef sprite_c_h
|
||||||
|
#define sprite_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask sprite_component;
|
||||||
|
|
||||||
|
#define SPRITE_A ((ecsComponentMask)(sprite_component | position_component))
|
||||||
|
|
||||||
|
typedef struct sprite_t {
|
||||||
|
SDL_Texture* texture;
|
||||||
|
SDL_Rect rect;
|
||||||
|
} sprite_t;
|
||||||
|
|
||||||
|
extern void render_textures_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* sprite_c_h */
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "transform_c.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
ecsComponentMask position_component = 0x0;
|
||||||
|
ecsComponentMask rotation_component = 0x0;
|
||||||
|
ecsComponentMask scale_component = 0x0;
|
||||||
|
ecsComponentMask wrap_screen_component = 0x0;
|
||||||
|
|
||||||
|
void wrap_rotation_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
float* rotation;
|
||||||
|
if(query_update_group(&physics_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
rotation = ecsGetComponentPtr(entities[i], rotation_component);
|
||||||
|
|
||||||
|
if(*rotation < -360.f)
|
||||||
|
*rotation += 360.f;
|
||||||
|
else if(*rotation > 360.f)
|
||||||
|
*rotation -= 360.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef transform_c_h
|
||||||
|
#define transform_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask position_component;
|
||||||
|
extern ecsComponentMask rotation_component;
|
||||||
|
extern ecsComponentMask scale_component;
|
||||||
|
|
||||||
|
#define TRANSFORM_A ((ecsComponentMask)(position_component | rotation_component | scale_component))
|
||||||
|
|
||||||
|
extern void wrap_screen_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
extern void wrap_rotation_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void transform_point(fvec* r, fvec* position, fvec* scale, float* rotation)
|
||||||
|
{
|
||||||
|
r->x *= scale->x;
|
||||||
|
r->y *= scale->y;
|
||||||
|
vrot(r, r, *rotation);
|
||||||
|
vadd(r, r, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void inverse_transform_point(fvec* r, fvec* position, fvec* scale, float* rotation)
|
||||||
|
{
|
||||||
|
r->x /= scale->x;
|
||||||
|
r->y /= scale->y;
|
||||||
|
vrot(r, r, -(*rotation));
|
||||||
|
vsub(r, r, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* transform_c_h */
|
|
@ -0,0 +1,386 @@
|
||||||
|
//
|
||||||
|
// ui.c
|
||||||
|
// engine
|
||||||
|
//
|
||||||
|
// Created by Scott on 21/10/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ui.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
SDL_Renderer* uiTarget;
|
||||||
|
typedef struct ui_selection_t {
|
||||||
|
uintptr_t item;
|
||||||
|
short keepSelected;
|
||||||
|
enum {
|
||||||
|
UI_SELECT_DEFAULT,
|
||||||
|
UI_SELECT_TEXT
|
||||||
|
} selectKind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
long start, end;
|
||||||
|
} text_select;
|
||||||
|
};
|
||||||
|
} ui_selection_t;
|
||||||
|
|
||||||
|
ui_selection_t uiSelection;
|
||||||
|
|
||||||
|
uint32_t uiCurrentMouseState;
|
||||||
|
uint32_t uiLastMouseState;
|
||||||
|
int uiMouseX, uiMouseY;
|
||||||
|
|
||||||
|
SDL_Rect uiCurrentWindow;
|
||||||
|
int uiIndentLevel;
|
||||||
|
int uiSingleLineHeight = 30;
|
||||||
|
int uiLineSpacing = 5;
|
||||||
|
int uiWindowPadding = 10;
|
||||||
|
int uiTotalHeight;
|
||||||
|
int uiRenderSameLine;
|
||||||
|
|
||||||
|
SDL_Rect uiNextLineRect;
|
||||||
|
|
||||||
|
struct ui_style_t {
|
||||||
|
TTF_Font* font;
|
||||||
|
int indentPixels;
|
||||||
|
} uiStyle;
|
||||||
|
|
||||||
|
void uiAddPixels(int n);
|
||||||
|
|
||||||
|
void uiUpdateMouseState()
|
||||||
|
{
|
||||||
|
uiLastMouseState = uiCurrentMouseState;
|
||||||
|
uiCurrentMouseState = SDL_GetMouseState(&uiMouseX, &uiMouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int uiCanSelect(void* ptr)
|
||||||
|
{
|
||||||
|
intptr_t item = (intptr_t)ptr;
|
||||||
|
|
||||||
|
return uiSelection.item == item || uiSelection.item == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSelect(void* ptr, short keepActive)
|
||||||
|
{
|
||||||
|
uiSelection.item = (intptr_t)ptr;
|
||||||
|
uiSelection.keepSelected = keepActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
short uiIsSelected(void* ptr)
|
||||||
|
{
|
||||||
|
return uiSelection.item == (intptr_t)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiInit(SDL_Renderer* target)
|
||||||
|
{
|
||||||
|
uiTarget = target;
|
||||||
|
uiSelection.item = 0;
|
||||||
|
memset(&uiStyle, 0, sizeof(struct ui_style_t));
|
||||||
|
uiUpdateMouseState();
|
||||||
|
uiStyle.indentPixels = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiTerminate()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSetFont(TTF_Font* font)
|
||||||
|
{
|
||||||
|
uiStyle.font = font;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a mouse button was pressed on this ui frame
|
||||||
|
static inline
|
||||||
|
short uiMouseButtonPressed(uint32_t buttonMask)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(buttonMask & uiCurrentMouseState) != 0 && // button is down
|
||||||
|
(buttonMask & uiLastMouseState) == 0; // button was down
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a mouse button was released on this ui frame
|
||||||
|
static inline
|
||||||
|
short uiMouseButtonReleased(uint32_t buttonMask)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(buttonMask & uiLastMouseState) != 0 && // button was down
|
||||||
|
(buttonMask & uiCurrentMouseState) == 0; // button is up
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a mouse button is currently pressed
|
||||||
|
static inline
|
||||||
|
short uiMouseButtonDown(uint32_t buttonMask)
|
||||||
|
{
|
||||||
|
return uiCurrentMouseState & buttonMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the SDL render target
|
||||||
|
void uiSetTarget(SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
uiTarget = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin a new frame
|
||||||
|
void uiBeginFrame()
|
||||||
|
{
|
||||||
|
if(uiSelection.keepSelected == 0)
|
||||||
|
uiSelection.item = 0;
|
||||||
|
uiUpdateMouseState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// push any number of lines
|
||||||
|
void uiSkipLines(int n)
|
||||||
|
{
|
||||||
|
while(n-- >= 0)
|
||||||
|
{
|
||||||
|
uiNextLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push a new line
|
||||||
|
void uiNextLine()
|
||||||
|
{
|
||||||
|
if(uiRenderSameLine > 0)
|
||||||
|
{
|
||||||
|
uiRenderSameLine--;
|
||||||
|
uiNextLineRect.x += uiNextLineRect.w;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uiAddPixels(uiSingleLineHeight + uiLineSpacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push the next line down by a number of pixels
|
||||||
|
void uiAddPixels(int px)
|
||||||
|
{
|
||||||
|
uiTotalHeight += px;
|
||||||
|
uiNextLineRect = uiCurrentWindow;
|
||||||
|
uiNextLineRect.h = uiSingleLineHeight;
|
||||||
|
uiNextLineRect.y += uiTotalHeight;
|
||||||
|
uiNextLineRect.w -= uiIndentLevel * uiStyle.indentPixels * 2;
|
||||||
|
uiNextLineRect.x += uiIndentLevel * uiStyle.indentPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the next n elements on the same line
|
||||||
|
void uiSameLine(int n)
|
||||||
|
{
|
||||||
|
if(n > 1)
|
||||||
|
{
|
||||||
|
uiRenderSameLine += n-1;
|
||||||
|
|
||||||
|
uiNextLineRect.w /= n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start a window fitting within the given rectangle
|
||||||
|
int uiBeginWindow(SDL_Rect* rect, const char* label, uiWindowState* state)
|
||||||
|
{
|
||||||
|
short isActive = ((*state) & UI_WINDOWSTATE_MINIMIZED) == 0;
|
||||||
|
uiTotalHeight = 0;
|
||||||
|
uiIndentLevel = 0;
|
||||||
|
memcpy(&uiCurrentWindow, rect, sizeof(SDL_Rect));
|
||||||
|
|
||||||
|
if(isActive)
|
||||||
|
{
|
||||||
|
SDL_SetRenderDrawColor(uiTarget, 20, 20, 20, 255);
|
||||||
|
SDL_RenderFillRect(uiTarget, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
uiNextLineRect = uiCurrentWindow;
|
||||||
|
uiNextLineRect.h = uiSingleLineHeight;
|
||||||
|
int toggled = uiButton(label);
|
||||||
|
|
||||||
|
uiCurrentWindow.x += uiWindowPadding;
|
||||||
|
uiCurrentWindow.y += uiWindowPadding;
|
||||||
|
uiCurrentWindow.w -= uiWindowPadding*2;
|
||||||
|
uiCurrentWindow.h -= uiWindowPadding*2;
|
||||||
|
|
||||||
|
uiNextLineRect.x = uiCurrentWindow.x;
|
||||||
|
uiNextLineRect.w = uiCurrentWindow.w;
|
||||||
|
uiNextLineRect.y = uiCurrentWindow.y + uiTotalHeight;
|
||||||
|
uiNextLineRect.h = uiSingleLineHeight;
|
||||||
|
|
||||||
|
if(toggled)
|
||||||
|
{
|
||||||
|
(*state) ^= UI_WINDOWSTATE_MINIMIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if point (x,y) is in r
|
||||||
|
int uiInArea(SDL_Rect* r, int x, int y)
|
||||||
|
{
|
||||||
|
int x_min = r->x, x_max = r->x + r->w;
|
||||||
|
int y_min = r->y, y_max = r->y + r->h;
|
||||||
|
return (x >= x_min && x < x_max && y >= y_min && y < y_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawText(const char* text, SDL_Rect dstrect, SDL_Color colour)
|
||||||
|
{
|
||||||
|
int height = TTF_FontHeight(uiStyle.font);
|
||||||
|
double fontToLineHeight = (double)height/(double)uiSingleLineHeight;
|
||||||
|
|
||||||
|
SDL_Rect srcrect = { 0, 0, dstrect.w * fontToLineHeight, dstrect.h * fontToLineHeight };
|
||||||
|
SDL_Surface* surface = TTF_RenderText_Solid_Wrapped(uiStyle.font, text, colour, srcrect.w);
|
||||||
|
|
||||||
|
dstrect.w = surface->w / fontToLineHeight;
|
||||||
|
|
||||||
|
if(surface == NULL)
|
||||||
|
{
|
||||||
|
SDL_Log("Failed to render text to surface:\n%s", SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* tex = SDL_CreateTextureFromSurface(uiTarget, surface);
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
|
||||||
|
if(tex == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SDL_RenderCopy(uiTarget, tex, &srcrect, &dstrect);
|
||||||
|
|
||||||
|
SDL_DestroyTexture(tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw an interactive slider with a min, max and step
|
||||||
|
int uiSlider(float* value, float min, float max, float step)
|
||||||
|
{
|
||||||
|
const int SLIDER_WIDTH = 10;
|
||||||
|
const int LINE_THICKNESS = 5;
|
||||||
|
|
||||||
|
float startValue = *value;
|
||||||
|
int valueChanged = 0;
|
||||||
|
float valuePercentage = ((*value)-min) / (max - min);
|
||||||
|
|
||||||
|
SDL_Rect position = uiNextLineRect;
|
||||||
|
position.x += SLIDER_WIDTH/2;
|
||||||
|
position.w -= SLIDER_WIDTH;
|
||||||
|
|
||||||
|
SDL_Rect lineRect = {
|
||||||
|
position.x, position.y + uiSingleLineHeight/2,
|
||||||
|
position.w, LINE_THICKNESS
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_Color sliderColor = {230, 230, 230, 255};
|
||||||
|
|
||||||
|
if(uiMouseButtonPressed(SDL_BUTTON_LEFT))
|
||||||
|
{
|
||||||
|
if(uiCanSelect(value) && uiInArea(&position, uiMouseX, uiMouseY))
|
||||||
|
{
|
||||||
|
uiSelect(value, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(uiIsSelected(value) && !uiMouseButtonDown(SDL_BUTTON_LEFT))
|
||||||
|
{
|
||||||
|
uiSelection.keepSelected = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(uiIsSelected(value))
|
||||||
|
{
|
||||||
|
valuePercentage = (float)(uiMouseX - position.x) / (float)(position.w);
|
||||||
|
|
||||||
|
if(valuePercentage < 0)
|
||||||
|
valuePercentage = 0;
|
||||||
|
else if(valuePercentage > 1)
|
||||||
|
valuePercentage = 1;
|
||||||
|
|
||||||
|
(*value) = valuePercentage * (max - min);
|
||||||
|
(*value) = roundf((*value) * (1.f/step)) * step + min;
|
||||||
|
valuePercentage = ((*value) - min) / (max-min);
|
||||||
|
|
||||||
|
|
||||||
|
if(*value != startValue)
|
||||||
|
{
|
||||||
|
valueChanged = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect sliderRect = {
|
||||||
|
position.x + valuePercentage * position.w - SLIDER_WIDTH/2, position.y,
|
||||||
|
SLIDER_WIDTH, uiSingleLineHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_SetRenderDrawColor(uiTarget, 100, 100, 100, 255);
|
||||||
|
SDL_RenderFillRect(uiTarget, &lineRect);
|
||||||
|
|
||||||
|
if(valuePercentage >= 0 && valuePercentage <= 1)
|
||||||
|
{
|
||||||
|
SDL_SetRenderDrawColor(uiTarget, sliderColor.r, sliderColor.g, sliderColor.b, sliderColor.a);
|
||||||
|
SDL_RenderFillRect(uiTarget, &sliderRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
uiNextLine();
|
||||||
|
|
||||||
|
return valueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw a clickable button
|
||||||
|
int uiButton(const char* label)
|
||||||
|
{
|
||||||
|
SDL_Rect position = uiNextLineRect;
|
||||||
|
short clicked = 0;
|
||||||
|
|
||||||
|
if(uiMouseButtonPressed(SDL_BUTTON_LEFT))
|
||||||
|
{
|
||||||
|
if(uiCanSelect(0) && uiInArea(&position, uiMouseX, uiMouseY))
|
||||||
|
{
|
||||||
|
uiSelect(0, 0);
|
||||||
|
clicked = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetRenderDrawColor(uiTarget, 100, 100, 100, 255);
|
||||||
|
SDL_RenderFillRect(uiTarget, &position);
|
||||||
|
|
||||||
|
uiLabel(label);
|
||||||
|
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiLabel(const char* label)
|
||||||
|
{
|
||||||
|
SDL_Colour white = {255, 255, 255, 255};
|
||||||
|
uiDrawText(label, uiNextLineRect, white);
|
||||||
|
uiNextLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiLabelNext(const char* label, float ratio)
|
||||||
|
{
|
||||||
|
int y = uiNextLineRect.y;
|
||||||
|
uiNextLineRect.w = uiCurrentWindow.w * ratio;
|
||||||
|
uiLabel(label);
|
||||||
|
uiTotalHeight -= uiSingleLineHeight + uiLineSpacing;
|
||||||
|
uiNextLineRect.y = y;
|
||||||
|
uiNextLineRect.x += uiCurrentWindow.w * ratio;
|
||||||
|
uiNextLineRect.w = uiCurrentWindow.w * (1-ratio);
|
||||||
|
uiNextLineRect.h = uiSingleLineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiHeader(const char* label)
|
||||||
|
{
|
||||||
|
uiSubIndent();
|
||||||
|
uiAddPixels(uiSingleLineHeight/2);
|
||||||
|
uiLabel(label);
|
||||||
|
uiAddIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSetIndent(int n)
|
||||||
|
{
|
||||||
|
uiIndentLevel = n;
|
||||||
|
uiIndentLevel = uiIndentLevel >= 0 ? uiIndentLevel : 0;
|
||||||
|
// force update of next line rect
|
||||||
|
uiAddPixels(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiAddIndent()
|
||||||
|
{
|
||||||
|
uiSetIndent(uiIndentLevel + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSubIndent()
|
||||||
|
{
|
||||||
|
uiSetIndent(uiIndentLevel - 1);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// ui.h
|
||||||
|
// engine
|
||||||
|
//
|
||||||
|
// Created by Scott on 21/10/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ui_h
|
||||||
|
#define ui_h
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint32_t uiWindowState;
|
||||||
|
#define UI_WINDOWSTATE_ALLOW_MINIMIZE ((uiWindowState)0x1)
|
||||||
|
#define UI_WINDOWSTATE_MINIMIZED ((uiWindowState)0x2)
|
||||||
|
#define UI_WINDOWSTATE_DEFAULT (UI_WINDOWSTATE_ALLOW_MINIMIZE)
|
||||||
|
|
||||||
|
extern void uiInit(SDL_Renderer* target);
|
||||||
|
extern void uiTerminate();
|
||||||
|
|
||||||
|
extern void uiSetTarget(SDL_Renderer* renderer);
|
||||||
|
|
||||||
|
extern void uiSetFont(TTF_Font* font);
|
||||||
|
|
||||||
|
extern void uiBeginFrame();
|
||||||
|
|
||||||
|
extern int uiBeginWindow(SDL_Rect* rect, const char* label, uiWindowState* state);
|
||||||
|
|
||||||
|
extern void uiSkipLines(int n);
|
||||||
|
extern void uiNextLine();
|
||||||
|
extern void uiSameLine(int n);
|
||||||
|
|
||||||
|
extern int uiSlider(float* value, float min, float max, float step);
|
||||||
|
extern int uiButton(const char* label);
|
||||||
|
extern void uiLabel(const char* label);
|
||||||
|
extern void uiLabelNext(const char* label, float ratio);
|
||||||
|
extern void uiHeader(const char* label);
|
||||||
|
extern void uiAddIndent();
|
||||||
|
extern void uiSubIndent();
|
||||||
|
extern void uiSetIndent(int n);
|
||||||
|
|
||||||
|
#endif /* ui_h */
|
|
@ -0,0 +1,203 @@
|
||||||
|
//
|
||||||
|
// vec.h
|
||||||
|
// sim
|
||||||
|
//
|
||||||
|
// Created by Scott on 11/10/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef vec_h
|
||||||
|
#define vec_h
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct fvec {
|
||||||
|
float x, y;
|
||||||
|
} fvec;
|
||||||
|
|
||||||
|
static inline void vadd(fvec* r, fvec* a, fvec* b)
|
||||||
|
{ r->x = a->x + b->x; r->y = a->y + b->y; }
|
||||||
|
|
||||||
|
static inline void vsub(fvec* r, fvec* a, fvec* b)
|
||||||
|
{ r->x = a->x - b->x; r->y = a->y - b->y; }
|
||||||
|
|
||||||
|
static inline void vmul(fvec* r, fvec* a, fvec* b)
|
||||||
|
{ r->x = a->x * b->x; r->y = a->y * b->y; }
|
||||||
|
|
||||||
|
static inline void vmulf(fvec* r, fvec* a, float b)
|
||||||
|
{ r->x = a->x * b; r->y = a->y * b; }
|
||||||
|
|
||||||
|
static inline void vdiv(fvec* r, fvec* a, fvec* b)
|
||||||
|
{ r->x = a->x / b->x; r->y = a->y / b->y; }
|
||||||
|
|
||||||
|
static inline float vmag(fvec* a)
|
||||||
|
{ return sqrt(fabsf(a->x*a->x) + fabsf(a->y*a->y)); }
|
||||||
|
|
||||||
|
static inline int veq(fvec* a, fvec* b)
|
||||||
|
{ return(a->x == b->x && a->y == b->y); }
|
||||||
|
|
||||||
|
static inline void vnor(fvec* r, fvec* a)
|
||||||
|
{
|
||||||
|
float m = vmag(a);
|
||||||
|
if(m == 0)
|
||||||
|
{
|
||||||
|
*r = (fvec){0.f, 0.f};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r->x = a->x / m;
|
||||||
|
r->y = a->y / m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vabs(fvec* r, fvec* a)
|
||||||
|
{
|
||||||
|
r->x = fabsf(a->x);
|
||||||
|
r->y = fabsf(a->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float vdist(fvec* a, fvec* b)
|
||||||
|
{
|
||||||
|
fvec diff;
|
||||||
|
vsub(&diff, a, b);
|
||||||
|
vabs(&diff, &diff);
|
||||||
|
float m = vmag(&diff);
|
||||||
|
return isnan(m) ? 0 : m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vmovetowards(fvec* r, fvec* from, fvec* to, float max_delta)
|
||||||
|
{
|
||||||
|
fvec diff, dir;
|
||||||
|
|
||||||
|
if(max_delta == 0 || isnan(max_delta))
|
||||||
|
{
|
||||||
|
memmove(r, from, sizeof(fvec));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsub(&diff, to, from);
|
||||||
|
|
||||||
|
// get the distance between from and to
|
||||||
|
float diffm = vmag(&diff);
|
||||||
|
|
||||||
|
// if the distance is zero, set return to target vector
|
||||||
|
if(diffm == 0 || isnan(diffm))
|
||||||
|
{
|
||||||
|
memmove(r, to, sizeof(fvec));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmulf(&dir, &diff, 1.f/diffm * max_delta);
|
||||||
|
|
||||||
|
float dirm = vmag(&dir);
|
||||||
|
|
||||||
|
if(dirm >= diffm)
|
||||||
|
{
|
||||||
|
memmove(r, to, sizeof(fvec));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vadd(r, from, &dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vlerp(fvec* r, fvec* from, fvec* to, float t)
|
||||||
|
{
|
||||||
|
fvec dir;
|
||||||
|
vsub(&dir, to, from);
|
||||||
|
vnor(&dir, &dir);
|
||||||
|
vmulf(&dir, &dir, t);
|
||||||
|
vadd(r, from, &dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vmin(fvec* r, fvec* a, float min)
|
||||||
|
{
|
||||||
|
float m = vmag(a);
|
||||||
|
if(m < min)
|
||||||
|
{
|
||||||
|
vmulf(r, a, (1.f/m)*min);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*r = *a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vmax(fvec* r, fvec* a, float max)
|
||||||
|
{
|
||||||
|
float m = vmag(a);
|
||||||
|
if(m > max)
|
||||||
|
vmulf(r, a, (1.f/m)*max);
|
||||||
|
else
|
||||||
|
*r = *a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vclamp(fvec* r, fvec* a, float min, float max)
|
||||||
|
{
|
||||||
|
float m = vmag(a);
|
||||||
|
if(m > max)
|
||||||
|
vmulf(r, a, (1.f/m)*max);
|
||||||
|
else if(m < min)
|
||||||
|
vmulf(r, a, (1.f/m)*min);
|
||||||
|
else
|
||||||
|
*r = *a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float vdot(fvec* a, fvec* b)
|
||||||
|
{
|
||||||
|
return(a->x * b->x + a->y * b->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float vang(fvec* a, fvec* b)
|
||||||
|
{
|
||||||
|
return atan2f(a->y, a->x) - atan2f(b->y, b->x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vrot(fvec* r, fvec* a, float ang)
|
||||||
|
{
|
||||||
|
fvec tmp = *a;
|
||||||
|
r->x = cosf(ang)*tmp.x - sinf(ang)*tmp.y;
|
||||||
|
r->y = sinf(ang)*tmp.x + cosf(ang)*tmp.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vperpend(fvec* r, fvec* a)
|
||||||
|
{
|
||||||
|
r->y = a->x;
|
||||||
|
r->x = -a->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void v3cross(float* r, float* a, float* b)
|
||||||
|
{
|
||||||
|
float store[3];
|
||||||
|
memcpy(store, r, sizeof(float) * 3);
|
||||||
|
store[0] = a[1]*b[2] - a[2]*b[1];
|
||||||
|
store[1] = a[2]*b[0] - a[0]*b[2];
|
||||||
|
store[2] = a[0]*b[1] - a[3]*b[0];
|
||||||
|
memcpy(r, store, sizeof(float) * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vtriple(fvec* r, fvec* a, fvec* b)
|
||||||
|
{
|
||||||
|
float va[3];
|
||||||
|
float vb[3];
|
||||||
|
float vr[3];
|
||||||
|
memcpy(va, a, sizeof(float) * 2);
|
||||||
|
memcpy(vb, b, sizeof(float) * 2);
|
||||||
|
memset(vr, 0.f, sizeof(float) * 3);
|
||||||
|
|
||||||
|
va[2] = vb[2] = 0;
|
||||||
|
|
||||||
|
v3cross(vr, va, vb);
|
||||||
|
v3cross(vr, vr, va);
|
||||||
|
|
||||||
|
r->x = vr[0];
|
||||||
|
r->y = vr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VZERO ((fvec){0.f,0.f})
|
||||||
|
#define VUP ((fvec){0.f, 1.f})
|
||||||
|
#define VDOWN ((fvec){0.f, -1.f})
|
||||||
|
#define VLEFT ((fvec){-1.f, 0.f})
|
||||||
|
#define VRIGHT ((fvec){1.f, 0.f})
|
||||||
|
|
||||||
|
#endif /* vec_h */
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "velocity_c.h"
|
||||||
|
#include <engine.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
ecsComponentMask linear_velocity_component = 0x0;
|
||||||
|
ecsComponentMask angular_velocity_component = 0x0;
|
||||||
|
|
||||||
|
ecsComponentMask linear_drag_component = 0x0;
|
||||||
|
ecsComponentMask angular_drag_component = 0x0;
|
||||||
|
|
||||||
|
|
||||||
|
void linear_velocity_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
fvec* position;
|
||||||
|
fvec* velocity;
|
||||||
|
float* drag;
|
||||||
|
shape_t* collision_shape;
|
||||||
|
fvec actual_velocity;
|
||||||
|
|
||||||
|
if(query_update_group(&physics_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
velocity = ecsGetComponentPtr(entities[i], linear_velocity_component);
|
||||||
|
drag = ecsGetComponentPtr(entities[i], linear_drag_component);
|
||||||
|
collision_shape = ecsGetComponentPtr(entities[i], shape_component);
|
||||||
|
|
||||||
|
// apply velocity
|
||||||
|
memcpy(&actual_velocity, velocity, sizeof(fvec));
|
||||||
|
vmulf(&actual_velocity, &actual_velocity, delta_time);
|
||||||
|
if(collision_shape == NULL)
|
||||||
|
{
|
||||||
|
position = ecsGetComponentPtr(entities[i], position_component);
|
||||||
|
vadd(position, position, &actual_velocity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shape_sweep(entities[i], actual_velocity, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply drag
|
||||||
|
if(drag)
|
||||||
|
{
|
||||||
|
vmovetowards(velocity, velocity, &VZERO, (*drag) * delta_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void angular_velocity_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
float* rotation;
|
||||||
|
float* velocity;
|
||||||
|
float* drag;
|
||||||
|
float max_delta;
|
||||||
|
|
||||||
|
if(query_update_group(&physics_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
rotation = ecsGetComponentPtr(entities[i], rotation_component);
|
||||||
|
velocity = ecsGetComponentPtr(entities[i], angular_velocity_component);
|
||||||
|
drag = ecsGetComponentPtr(entities[i], angular_drag_component);
|
||||||
|
|
||||||
|
// apply current velocity
|
||||||
|
*rotation += (*velocity) * delta_time;
|
||||||
|
|
||||||
|
// apply drag
|
||||||
|
if(drag)
|
||||||
|
{
|
||||||
|
max_delta = *drag * delta_time;
|
||||||
|
if(*velocity != 0.f)
|
||||||
|
{
|
||||||
|
if(*velocity > max_delta)
|
||||||
|
*velocity -= max_delta;
|
||||||
|
else if(*velocity < -max_delta)
|
||||||
|
*velocity += max_delta;
|
||||||
|
else
|
||||||
|
*velocity = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef velocity_c_h
|
||||||
|
#define velocity_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask linear_velocity_component;
|
||||||
|
extern ecsComponentMask angular_velocity_component;
|
||||||
|
extern ecsComponentMask linear_drag_component;
|
||||||
|
extern ecsComponentMask angular_drag_component;
|
||||||
|
|
||||||
|
#define PHYSICS_A ((ecsComponentMask)(linear_velocity_component | linear_drag_component | angular_velocity_component | angular_drag_component| TRANSFORM_A))
|
||||||
|
|
||||||
|
extern void linear_velocity_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
extern void angular_velocity_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* velocity_c_h */
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "view_c.h"
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
double last_aspect_ratio;
|
||||||
|
|
||||||
|
ecsComponentMask view_component = 0x0;
|
||||||
|
|
||||||
|
void get_view_rect(view_t const* view, SDL_Rect* rect)
|
||||||
|
{
|
||||||
|
rect->x = -(view->width / 2);
|
||||||
|
rect->y = -(view->height / 2);
|
||||||
|
rect->w = view->width;
|
||||||
|
rect->h = view->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_update_views(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
view_t* view;
|
||||||
|
|
||||||
|
int width, height; double aspect_ratio;
|
||||||
|
|
||||||
|
SDL_GetRendererOutputSize(renderer, &width, &height);
|
||||||
|
aspect_ratio = (double)height/width;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
view = ecsGetComponentPtr(entities[i], view_component);
|
||||||
|
view->height = view->width * aspect_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
ecsDisableSystem(&system_update_views);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef view_c_h
|
||||||
|
#define view_c_h
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <ecs.h>
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
extern ecsComponentMask view_component;
|
||||||
|
|
||||||
|
typedef struct view_t {
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
} view_t;
|
||||||
|
|
||||||
|
void get_view_rect(view_t const* view, SDL_Rect* rect);
|
||||||
|
|
||||||
|
extern void system_update_views(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* view_c_h */
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "assets.h"
|
||||||
|
|
||||||
|
asset_handle_t fnt_inter = 0x0;
|
||||||
|
asset_handle_t fnt_ibmplex = 0x0;
|
||||||
|
asset_handle_t spr_player = 0x0;
|
||||||
|
asset_handle_t spr_bullet = 0x0;
|
||||||
|
asset_handle_t spr_ufo = 0x0;
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef assets_h
|
||||||
|
#define assets_h
|
||||||
|
|
||||||
|
#include <adb.h>
|
||||||
|
|
||||||
|
extern asset_handle_t fnt_inter;
|
||||||
|
extern asset_handle_t fnt_ibmplex;
|
||||||
|
extern asset_handle_t spr_player;
|
||||||
|
extern asset_handle_t spr_bullet;
|
||||||
|
extern asset_handle_t spr_ufo;
|
||||||
|
|
||||||
|
#endif /* !assets_h */
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef bullet_h
|
||||||
|
#define bullet_h
|
||||||
|
|
||||||
|
#include "components.h"
|
||||||
|
#include "assets.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "player.h"
|
||||||
|
#include "bullet_c.h"
|
||||||
|
|
||||||
|
#define BULLET_A (PHYSICS_A | SPRITE_A | border_interaction_component | bullet_component | shape_component)
|
||||||
|
|
||||||
|
static inline
|
||||||
|
ecsEntityId spawn_bullet(fvec start_position, fvec start_velocity, ecsEntityId source, int power)
|
||||||
|
{
|
||||||
|
ecsEntityId bullet_id = ecsCreateEntity(BULLET_A);
|
||||||
|
assert(bullet_id != noentity);
|
||||||
|
{
|
||||||
|
fvec* position = ecsGetComponentPtr(bullet_id, position_component);
|
||||||
|
memcpy(position, &start_position, sizeof(fvec));
|
||||||
|
|
||||||
|
fvec* velocity = ecsGetComponentPtr(bullet_id, linear_velocity_component);
|
||||||
|
memcpy(velocity, &start_velocity, sizeof(fvec));
|
||||||
|
|
||||||
|
fvec* scale = ecsGetComponentPtr(bullet_id, scale_component);
|
||||||
|
*scale = (fvec){0.5f,0.5f};
|
||||||
|
|
||||||
|
bullet_t* bullet = ecsGetComponentPtr(bullet_id, bullet_component);
|
||||||
|
bullet->source = source;
|
||||||
|
bullet->power = power;
|
||||||
|
|
||||||
|
float* rotation = ecsGetComponentPtr(bullet_id, rotation_component);
|
||||||
|
*rotation = vang(&VUP, &start_velocity);
|
||||||
|
|
||||||
|
sprite_t* sprite = ecsGetComponentPtr(bullet_id, sprite_component);
|
||||||
|
SDL_Texture* spr_bullet_ptr = adb_get_asset(spr_bullet);
|
||||||
|
sprite->texture = spr_bullet_ptr;
|
||||||
|
SDL_QueryTexture(spr_bullet_ptr, NULL, NULL, &sprite->rect.w, &sprite->rect.h);
|
||||||
|
|
||||||
|
shape_t* shape = ecsGetComponentPtr(bullet_id, shape_component);
|
||||||
|
shape->type = SHAPE_CIRCLE;
|
||||||
|
shape->circle.radius = 10.f;
|
||||||
|
|
||||||
|
border_interaction_t* border_interaction = ecsGetComponentPtr(bullet_id, border_interaction_component);
|
||||||
|
border_interaction->type = BORDER_DESTROY;
|
||||||
|
border_interaction->margin = (fvec){0,0};//(fvec){scale->x * sprite->rect.w, scale->y * sprite->rect.h};
|
||||||
|
|
||||||
|
add_to_collision_world(bullet_id);
|
||||||
|
}
|
||||||
|
return bullet_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* bullet_h */
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "bullet_c.h"
|
||||||
|
|
||||||
|
ecsComponentMask bullet_component = 0x0;
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef bullet_c_h
|
||||||
|
#define bullet_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask bullet_component;
|
||||||
|
|
||||||
|
typedef struct bullet_t {
|
||||||
|
ecsEntityId source;
|
||||||
|
int power;
|
||||||
|
} bullet_t;
|
||||||
|
|
||||||
|
#endif /* bullet_c_h */
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "debug_ui.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <ui.h>
|
||||||
|
#include <input.h>
|
||||||
|
|
||||||
|
void show_framerate_ui_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
static uiWindowState window_state = UI_WINDOWSTATE_MINIMIZED;
|
||||||
|
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
char formatted[100];
|
||||||
|
|
||||||
|
uiBeginFrame();
|
||||||
|
SDL_Rect rect = {0, 0, 300, 300};
|
||||||
|
|
||||||
|
if(uiBeginWindow(&rect, "DEBUG INFO", &window_state))
|
||||||
|
{
|
||||||
|
if(sprintf(formatted, "delta_time: %f", delta_time))
|
||||||
|
uiLabel(formatted);
|
||||||
|
|
||||||
|
if(sprintf(formatted, "fps: %f", 1.f/delta_time))
|
||||||
|
uiLabel(formatted);
|
||||||
|
|
||||||
|
if(sprintf(formatted, "ax_hor: %d", input_axis(SDL_SCANCODE_A, SDL_SCANCODE_D)))
|
||||||
|
uiLabel(formatted);
|
||||||
|
|
||||||
|
if(sprintf(formatted, "ax_ver: %d", input_axis(SDL_SCANCODE_S, SDL_SCANCODE_W)))
|
||||||
|
uiLabel(formatted);
|
||||||
|
|
||||||
|
if(sprintf(formatted, "count: %zu", count))
|
||||||
|
uiLabel(formatted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef debug_ui_h
|
||||||
|
#define debug_ui_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
|
||||||
|
extern void show_framerate_ui_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* debug_ui_h */
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef enemies_h
|
||||||
|
#define enemies_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <components.h>
|
||||||
|
#include "enemy_c.h"
|
||||||
|
#include "player_c.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
#define NMI_A PHYSICS_A | player_component | enemy_component;
|
||||||
|
|
||||||
|
ecsEntityId spawn_enemy(fvec position, float rotation, ecsEntityId target);
|
||||||
|
|
||||||
|
#endif /* enemies_h */
|
|
@ -0,0 +1,4 @@
|
||||||
|
#include "enemy_c.h"
|
||||||
|
#include "ecs.h"
|
||||||
|
|
||||||
|
ecsComponentMask enemy_component = 0x0;
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef enemy_c_h
|
||||||
|
#define enemy_c_h
|
||||||
|
|
||||||
|
#include "ecs.h"
|
||||||
|
|
||||||
|
typedef struct enemy_t {
|
||||||
|
ecsEntityId target;
|
||||||
|
} enemy_t;
|
||||||
|
|
||||||
|
extern ecsComponentMask enemy_component;
|
||||||
|
|
||||||
|
#endif /* enemy_c_h */
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <engine.h>
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <adb.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <ui.h>
|
||||||
|
#include <input.h>
|
||||||
|
#include <components.h>
|
||||||
|
#include <camera.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bullet.h"
|
||||||
|
#include "assets.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "debug_ui.h"
|
||||||
|
|
||||||
|
void game_config(engine_init_t* config)
|
||||||
|
{
|
||||||
|
config->window_name = "Asteroids";
|
||||||
|
config->window_init_flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
|
config->window_width = 1200;
|
||||||
|
config->window_height = 800;
|
||||||
|
set_update_group_target_framerate(&frame_update, 60);
|
||||||
|
set_update_group_target_framerate(&frame_update, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_load_assets()
|
||||||
|
{
|
||||||
|
fnt_inter = adb_load_asset("Inter-Regular.otf");
|
||||||
|
fnt_ibmplex = adb_load_asset("IBMPlexMono-Regular.otf");
|
||||||
|
spr_player = adb_load_asset("ship-player.png");
|
||||||
|
spr_bullet = adb_load_asset("orb-pink-red.png");
|
||||||
|
spr_ufo = adb_load_asset("ufo.png");
|
||||||
|
|
||||||
|
uiSetFont(adb_get_asset(fnt_ibmplex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_load_main_scene()
|
||||||
|
{
|
||||||
|
int w,h;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &w, &h);
|
||||||
|
|
||||||
|
// create player entity
|
||||||
|
ecsEntityId cam = create_default_camera();
|
||||||
|
spawn_player((fvec){0,0});
|
||||||
|
fvec* campos = ecsGetComponentPtr(cam, position_component);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_init()
|
||||||
|
{
|
||||||
|
player_component = ecsRegisterComponent(player_t);
|
||||||
|
input_component = ecsRegisterComponent(input_t);
|
||||||
|
bullet_component = ecsRegisterComponent(bullet_t);
|
||||||
|
|
||||||
|
// the 0-499 block is for pre-render updates
|
||||||
|
ecsEnableSystem(&input_update_s, input_component, ECS_QUERY_ALL, MAX_THREADS, 250);
|
||||||
|
ecsEnableSystem(&player_update_s, PLAYER_A, ECS_QUERY_ALL, MAX_THREADS, 255);
|
||||||
|
|
||||||
|
#if defined(DEBUG)
|
||||||
|
ecsEnableSystem(&show_framerate_ui_s, ~noentity, ECS_QUERY_ANY, 0, 550);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
game_load_assets();
|
||||||
|
game_load_main_scene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_quit()
|
||||||
|
{}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "input_c.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <input.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
ecsComponentMask input_component = 0x0;
|
||||||
|
|
||||||
|
void input_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
input_t* input;
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
input = ecsGetComponentPtr(entities[i], input_component);
|
||||||
|
input->axis_horizontal = input_axis(SDL_SCANCODE_A, SDL_SCANCODE_D);
|
||||||
|
input->axis_vertical = input_axis(SDL_SCANCODE_S, SDL_SCANCODE_W);
|
||||||
|
if(input_button(SDL_SCANCODE_J))
|
||||||
|
input->button_fire += 1;
|
||||||
|
else
|
||||||
|
input->button_fire = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef input_c_h
|
||||||
|
#define input_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask input_component;
|
||||||
|
|
||||||
|
typedef struct input_t {
|
||||||
|
int8_t axis_horizontal;
|
||||||
|
int8_t axis_vertical;
|
||||||
|
uint8_t button_fire;
|
||||||
|
} input_t;
|
||||||
|
|
||||||
|
extern void input_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* input_c_h */
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include "player.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <adb.h>
|
||||||
|
#include <ecs.h>
|
||||||
|
#include "assets.h"
|
||||||
|
|
||||||
|
void player_on_overlap(ecsEntityId self, ecsEntityId other)
|
||||||
|
{
|
||||||
|
E_LOG("%zu, %zu", self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ecsEntityId spawn_player(fvec start_position)
|
||||||
|
{
|
||||||
|
ecsEntityId player_id = ecsCreateEntity(PLAYER_A);
|
||||||
|
|
||||||
|
if(player_id)
|
||||||
|
{
|
||||||
|
fvec* position = ecsGetComponentPtr(player_id, position_component);
|
||||||
|
memcpy(position, &start_position, sizeof(fvec));
|
||||||
|
|
||||||
|
sprite_t* sprite = ecsGetComponentPtr(player_id, sprite_component);
|
||||||
|
SDL_Texture* spr_player_ptr = adb_get_asset(spr_player);
|
||||||
|
sprite->texture = spr_player_ptr;
|
||||||
|
SDL_QueryTexture(spr_player_ptr, NULL, NULL, &sprite->rect.w, &sprite->rect.h);
|
||||||
|
|
||||||
|
fvec* scale = ecsGetComponentPtr(player_id, scale_component);
|
||||||
|
scale->x = 1.f;
|
||||||
|
scale->y = 1.f;
|
||||||
|
|
||||||
|
player_t* player = ecsGetComponentPtr(player_id, player_component);
|
||||||
|
player->acceleration = 10.f;
|
||||||
|
player->speed = 10.f;
|
||||||
|
player->rotation_speed = 100.f;
|
||||||
|
player->rotation_acceleration = 120.f;
|
||||||
|
|
||||||
|
float* linear_drag = ecsGetComponentPtr(player_id, linear_drag_component);
|
||||||
|
*linear_drag = 0.5f;
|
||||||
|
|
||||||
|
float* angular_drag = ecsGetComponentPtr(player_id, angular_drag_component);
|
||||||
|
*angular_drag = 1.f;
|
||||||
|
|
||||||
|
shape_t* collider = ecsGetComponentPtr(player_id, shape_component);
|
||||||
|
collider->type = SHAPE_CIRCLE;
|
||||||
|
collider->circle.radius = 100;
|
||||||
|
collider->on_overlap = &player_on_overlap;
|
||||||
|
|
||||||
|
border_interaction_t* border_interaction = ecsGetComponentPtr(player_id, border_interaction_component);
|
||||||
|
border_interaction->type = BORDER_WRAP;
|
||||||
|
border_interaction->margin = (fvec){scale->x, scale->y};
|
||||||
|
|
||||||
|
add_to_collision_world(player_id);
|
||||||
|
}
|
||||||
|
return player_id;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef player_h
|
||||||
|
#define player_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <engine.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#include "player_c.h"
|
||||||
|
#include "input_c.h"
|
||||||
|
|
||||||
|
#define PLAYER_A (ecsComponentMask)(PHYSICS_A | SPRITE_A | border_interaction_component | player_component | input_component | shape_component)
|
||||||
|
|
||||||
|
extern void player_on_overlap(ecsEntityId self, ecsEntityId other);
|
||||||
|
|
||||||
|
extern ecsEntityId spawn_player(fvec start_position);
|
||||||
|
|
||||||
|
#endif /* player_h */
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "player_c.h"
|
||||||
|
|
||||||
|
#include <engine.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <fmath.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "input_c.h"
|
||||||
|
#include "transform_c.h"
|
||||||
|
#include "velocity_c.h"
|
||||||
|
#include "bullet.h"
|
||||||
|
|
||||||
|
ecsComponentMask player_component = 0x0;
|
||||||
|
|
||||||
|
void player_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
ecsEntityId id;
|
||||||
|
|
||||||
|
player_t* player;
|
||||||
|
input_t* input;
|
||||||
|
fvec* lin_velocity;
|
||||||
|
float* ang_velocity;
|
||||||
|
float* rotation;
|
||||||
|
|
||||||
|
if(query_update_group(&frame_update, &delta_time))
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
id = entities[i];
|
||||||
|
player = ecsGetComponentPtr(id, player_component);
|
||||||
|
input = ecsGetComponentPtr(id, input_component);
|
||||||
|
lin_velocity = ecsGetComponentPtr(id, linear_velocity_component);
|
||||||
|
ang_velocity = ecsGetComponentPtr(id, angular_velocity_component);
|
||||||
|
rotation = ecsGetComponentPtr(id, rotation_component);
|
||||||
|
|
||||||
|
*ang_velocity = fmovetowards(*ang_velocity, player->rotation_speed * input->axis_horizontal, player->rotation_acceleration * delta_time);
|
||||||
|
|
||||||
|
if(input->axis_vertical > 0)
|
||||||
|
{
|
||||||
|
fvec input_dir = {
|
||||||
|
.x = 0,
|
||||||
|
.y = -(float)(input->axis_vertical)
|
||||||
|
};
|
||||||
|
|
||||||
|
vnor(&input_dir, &input_dir);
|
||||||
|
vrot(&input_dir, &input_dir, *rotation * 0.017);
|
||||||
|
vmulf(&input_dir, &input_dir, player->speed);
|
||||||
|
vmovetowards(lin_velocity, lin_velocity, &input_dir, player->acceleration * delta_time);
|
||||||
|
}
|
||||||
|
else if(input->axis_vertical > 0)
|
||||||
|
{
|
||||||
|
vmovetowards(lin_velocity, lin_velocity, &VZERO, player->acceleration * delta_time * abs(input->axis_vertical));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input->button_fire == 1)
|
||||||
|
{
|
||||||
|
fvec* position = ecsGetComponentPtr(id, position_component);
|
||||||
|
|
||||||
|
fvec velocity;
|
||||||
|
vrot(&velocity, &VDOWN, *rotation * 0.017);
|
||||||
|
vmulf(&velocity, &velocity, 50.f);
|
||||||
|
|
||||||
|
spawn_bullet(*position, velocity, id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef player_c_h
|
||||||
|
#define player_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
|
||||||
|
extern ecsComponentMask player_component;
|
||||||
|
|
||||||
|
typedef struct player_t {
|
||||||
|
float speed;
|
||||||
|
float rotation_speed;
|
||||||
|
float fire_timer;
|
||||||
|
float acceleration;
|
||||||
|
float rotation_acceleration;
|
||||||
|
} player_t;
|
||||||
|
|
||||||
|
extern void player_update_s(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time);
|
||||||
|
|
||||||
|
#endif /* player_c_h */
|
Loading…
Reference in New Issue