portfolio prep
commit
6c1a1fbb53
|
@ -0,0 +1,14 @@
|
||||||
|
# build dirs
|
||||||
|
xcode/
|
||||||
|
kdev/
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
[Bb]uild/
|
||||||
|
[Bb]in/
|
||||||
|
|
||||||
|
# build files
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
cmake_minimum_required(VERSION 3.17)
|
||||||
|
|
||||||
|
project(engine)
|
||||||
|
project(sim)
|
||||||
|
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules")
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
|
||||||
|
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
find_package(SDL2_image REQUIRED)
|
||||||
|
find_package(SDL2_ttf REQUIRED)
|
||||||
|
|
||||||
|
include_directories(SDL2 PRIVATE "${CMAKE_SOURCE_DIR}/src/engine" "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
include_directories("/opt/homebrew/include/")
|
||||||
|
link_directories("/opt/homebrew/lib/" "./lib/")
|
||||||
|
else()
|
||||||
|
link_directories("./lib/")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(
|
||||||
|
GLOB_RECURSE
|
||||||
|
SIM_SRC
|
||||||
|
"${CMAKE_SOURCE_DIR}/src/sim/*.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(sim STATIC ${SIM_SRC})
|
||||||
|
set_target_properties(sim PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
|
target_link_libraries(sim SDL2 SDL2_image SDL2_ttf ecs c m)
|
||||||
|
|
||||||
|
file(
|
||||||
|
GLOB_RECURSE
|
||||||
|
ENGINE_SRC
|
||||||
|
"${CMAKE_SOURCE_DIR}/src/engine/*.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(engine ${ENGINE_SRC})
|
||||||
|
target_link_libraries(engine sim SDL2 SDL2_image SDL2_ttf ecs c m)
|
Binary file not shown.
|
@ -0,0 +1,124 @@
|
||||||
|
#ifndef ecs_h
|
||||||
|
#define ecs_h
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned long long ecsEntityId;
|
||||||
|
typedef unsigned long long ecsComponentMask;
|
||||||
|
|
||||||
|
typedef void (*ecsSystemFn)(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
|
||||||
|
#define noentity ((ecsEntityId)0x0)
|
||||||
|
#define nocomponent ((ecsComponentMask)0x0)
|
||||||
|
#define anycomponent ((ecsComponentMask)~0x0)
|
||||||
|
|
||||||
|
typedef enum ECSqueryComparison {
|
||||||
|
ECS_NOQUERY = 0x0,
|
||||||
|
ECS_QUERY_ANY,
|
||||||
|
ECS_QUERY_ALL,
|
||||||
|
} ecsQueryComparison;
|
||||||
|
|
||||||
|
typedef struct ecsComponentQuery {
|
||||||
|
ecsQueryComparison comparison;
|
||||||
|
ecsComponentMask mask;
|
||||||
|
} ecsComponentQuery;
|
||||||
|
|
||||||
|
void ecsInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocates a component list for a component type of stride bytes.
|
||||||
|
* \param stride The number of bytes to allocate for each component.
|
||||||
|
*/
|
||||||
|
ecsComponentMask ecsMakeComponentType(size_t stride);
|
||||||
|
#define ecsRegisterComponent(__type) ecsMakeComponentType(sizeof(__type))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get a pointer to a component attached to entity.
|
||||||
|
* \param entity The entity to find a component of.
|
||||||
|
* \param component The component type to find.
|
||||||
|
* \returns A pointer to a component if found.
|
||||||
|
* \returns NULL if entity does not contain the given component.
|
||||||
|
*/
|
||||||
|
void* ecsGetComponentPtr(ecsEntityId entity, ecsComponentMask component);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Assigns a new entity id.
|
||||||
|
* \param components A component query referencing the components to add to the new object.
|
||||||
|
* \returns The id used to reference the newly created entity.
|
||||||
|
* \returns NULL if allocation failed
|
||||||
|
*/
|
||||||
|
ecsEntityId ecsCreateEntity(ecsComponentMask components);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets the component mask for an entity.
|
||||||
|
* \param entity the entity to get the mask for.
|
||||||
|
* \returns the ecsComponentMask for entity.
|
||||||
|
*/
|
||||||
|
ecsEntityId ecsGetComponentMask(ecsEntityId entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys an entity and all associated components
|
||||||
|
* \param entity The id of the entity to destroy.
|
||||||
|
*/
|
||||||
|
void ecsDestroyEntity(ecsEntityId entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Attaches one or more components.
|
||||||
|
* \param entity The entity to attach the new components to.
|
||||||
|
* \param components Bitmask of the componentId's to attach.
|
||||||
|
*/
|
||||||
|
void ecsAttachComponents(ecsEntityId entity, ecsComponentMask components);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Detaches one or more components.
|
||||||
|
* \param entity The entity to detach components from.
|
||||||
|
* \param components Bitmask of the components to detach.
|
||||||
|
*/
|
||||||
|
void ecsDetachComponents(ecsEntityId entity, ecsComponentMask components);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enables a function to act as a system for entities matching the given query.
|
||||||
|
* \param func The function to call when query is met.
|
||||||
|
* \param components The required components to run this system.
|
||||||
|
* \param comparison The type of requirement components represent. one of { ECS_QUERY_ANY ; ECS_QUERY_ALL }.
|
||||||
|
* \note
|
||||||
|
* When comparison=ECS_QUERY_ALL the system will run only when all of the masked components are present on an entity.
|
||||||
|
* \note
|
||||||
|
* When comparison=ECS_QUERY_ANY the system will run for all entities where any of the masked components are present.
|
||||||
|
*/
|
||||||
|
void ecsEnableSystem(ecsSystemFn func, ecsComponentMask components, ecsQueryComparison comparison, int maxThreads, int executionOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Disables a function acting as a system.
|
||||||
|
* \param func Pointer to the function to disable.
|
||||||
|
*/
|
||||||
|
void ecsDisableSystem(ecsSystemFn func);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Run currently enabled systems.
|
||||||
|
* \note Implicitly calls ecsRunTasks after completion.
|
||||||
|
*/
|
||||||
|
void ecsRunSystems(float deltaTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Run queued tasks.
|
||||||
|
*/
|
||||||
|
void ecsRunTasks(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Terminate the ECS and clean up allocated resources.
|
||||||
|
*/
|
||||||
|
void ecsTerminate(void);
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ecs_h */
|
|
@ -0,0 +1,313 @@
|
||||||
|
#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_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 init_asset_database(size_t max_asset_bytes)
|
||||||
|
{
|
||||||
|
assert(adb_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_init = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int close_asset_database()
|
||||||
|
{
|
||||||
|
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 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(loadfn != 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* get_asset(asset_handle_t handle)
|
||||||
|
{
|
||||||
|
asset_t* asset = get_asset_t(handle);
|
||||||
|
if(asset == NULL)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return asset->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int try_get_asset(asset_handle_t handle, void** o_ptr)
|
||||||
|
{
|
||||||
|
void* asset = get_asset(handle);
|
||||||
|
if(o_ptr != NULL)
|
||||||
|
memcpy(o_ptr, &asset, sizeof(void*));
|
||||||
|
return asset != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_handle_t load_asset(const char* file)
|
||||||
|
{
|
||||||
|
char* filename = realpath(file, NULL);
|
||||||
|
if(filename == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
asset_handle_t handle = str_hash(filename);
|
||||||
|
|
||||||
|
if(try_get_asset(handle, NULL))
|
||||||
|
return handle;
|
||||||
|
|
||||||
|
size_t ext_offset = file_extention_offset(filename);
|
||||||
|
file_handler_t* handler = get_file_handler(filename + ext_offset);
|
||||||
|
|
||||||
|
adb_assets_first[adb_assets_last] = (asset_t){
|
||||||
|
.filename = filename,
|
||||||
|
.extention_offset = ext_offset,
|
||||||
|
.instance = handler->load(filename),
|
||||||
|
.handle = handle
|
||||||
|
};
|
||||||
|
|
||||||
|
++adb_assets_last;
|
||||||
|
|
||||||
|
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 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 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,28 @@
|
||||||
|
#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 init_asset_database(size_t max_asset);
|
||||||
|
extern asset_handle_t get_file_handle(const char* filename);
|
||||||
|
extern int asset_is_loaded(asset_handle_t handle);
|
||||||
|
extern void* get_asset(asset_handle_t handle);
|
||||||
|
#define get_asset_as(__TYPE, handle) ((__TYPE*)get_asset(handle))
|
||||||
|
extern int try_get_asset(asset_handle_t handle, void** o_ptr);
|
||||||
|
extern asset_handle_t load_asset(const char* file);
|
||||||
|
extern void free_asset(asset_handle_t handle);
|
||||||
|
extern size_t set_asset_memory(size_t max_bytes);
|
||||||
|
extern int close_asset_database();
|
||||||
|
extern int register_file_handler(const char* file_extention, asset_load_fn loadfn, asset_free_fn freefn);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* adb_h */
|
|
@ -0,0 +1,206 @@
|
||||||
|
#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 "adb.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
SDL_Window* window;
|
||||||
|
SDL_Renderer* renderer;
|
||||||
|
int engine_wants_to_quit;
|
||||||
|
float frame_start_time;
|
||||||
|
float last_frame_time;
|
||||||
|
short is_render_frame;
|
||||||
|
float target_frame_time;
|
||||||
|
float render_frame_time;
|
||||||
|
|
||||||
|
void system_window_clear(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time);
|
||||||
|
void system_window_display(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time);
|
||||||
|
|
||||||
|
void engine_init();
|
||||||
|
void engine_run();
|
||||||
|
void engine_handle_event(SDL_Event* event);
|
||||||
|
void engine_clean();
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
// set the frame start time here to ensure there will be time passed when engine_run is called
|
||||||
|
frame_start_time = (float)clock() / CLOCKS_PER_SEC;
|
||||||
|
engine_wants_to_quit = 0;
|
||||||
|
// init asset database for 100 assets
|
||||||
|
init_asset_database(100);
|
||||||
|
|
||||||
|
// register default image file handlers
|
||||||
|
register_file_handler(".png", &asset_load_sdl_image, &asset_free_sdl_image);
|
||||||
|
register_file_handler(".jpg", &asset_load_sdl_image, &asset_free_sdl_image);
|
||||||
|
// default font file handlers
|
||||||
|
register_file_handler(".ttf", &asset_load_ttf_font, &asset_free_ttf_font);
|
||||||
|
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);
|
||||||
|
// allow sim to adjust init settings as needed
|
||||||
|
sim_config(&init_settings);
|
||||||
|
target_frame_time = 1.f/(float)init_settings.target_framerate;
|
||||||
|
|
||||||
|
// 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("BOIDS!!",
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize runtime object database
|
||||||
|
ecsInit();
|
||||||
|
|
||||||
|
// initialize imgui renderer
|
||||||
|
uiInit(renderer);
|
||||||
|
|
||||||
|
// enable screen refresh system
|
||||||
|
ecsEnableSystem(&system_window_clear, nocomponent, ECS_NOQUERY, 0, -100);
|
||||||
|
|
||||||
|
// initialize sim
|
||||||
|
sim_init();
|
||||||
|
|
||||||
|
ecsEnableSystem(&system_window_display, nocomponent, ECS_NOQUERY, 0, 10000);
|
||||||
|
// run created tasks
|
||||||
|
ecsRunTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_run()
|
||||||
|
{
|
||||||
|
SDL_Event evt;
|
||||||
|
float actual_time;
|
||||||
|
|
||||||
|
while(!engine_wants_to_quit)
|
||||||
|
{
|
||||||
|
last_frame_time = frame_start_time;
|
||||||
|
frame_start_time = (float)clock() / CLOCKS_PER_SEC;
|
||||||
|
|
||||||
|
ecsRunSystems(frame_start_time - last_frame_time);
|
||||||
|
|
||||||
|
while(SDL_PollEvent(&evt))
|
||||||
|
{
|
||||||
|
engine_handle_event(&evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_handle_event(SDL_Event* event)
|
||||||
|
{
|
||||||
|
switch(event->type)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case SDL_QUIT:
|
||||||
|
engine_wants_to_quit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engine_clean()
|
||||||
|
{
|
||||||
|
// quit sim, ui asset database, ecs
|
||||||
|
sim_quit();
|
||||||
|
uiTerminate();
|
||||||
|
close_asset_database();
|
||||||
|
ecsTerminate();
|
||||||
|
// delete renderer and window, quit sdl
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_window_clear(ecsEntityId* entities, ecsComponentMask* components, size_t size, float delta_time)
|
||||||
|
{
|
||||||
|
render_frame_time += delta_time;
|
||||||
|
is_render_frame = render_frame_time >= target_frame_time;
|
||||||
|
|
||||||
|
if(is_render_frame)
|
||||||
|
{
|
||||||
|
render_frame_time = 0;
|
||||||
|
// 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(is_render_frame)
|
||||||
|
{
|
||||||
|
// swap buffer
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
.target_framerate = 60
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _engine_h
|
||||||
|
#define _engine_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
int target_framerate;
|
||||||
|
} engine_init_t;
|
||||||
|
|
||||||
|
extern short is_render_frame;
|
||||||
|
|
||||||
|
extern void default_engine_init_settings(engine_init_t*);
|
||||||
|
|
||||||
|
extern void sim_config(engine_init_t*);
|
||||||
|
extern void sim_init();
|
||||||
|
extern void sim_quit();
|
||||||
|
|
||||||
|
extern struct SDL_Renderer* renderer;
|
||||||
|
|
||||||
|
#if defined(DEBUG)
|
||||||
|
#define E_LOG(...)\
|
||||||
|
fprintf(stdout, "%s:%d: ", __FILE__, __LINE__);\
|
||||||
|
fprintf(stdout, __VA_ARGS__);\
|
||||||
|
fprintf(stdout, "\n")
|
||||||
|
#elif defined(NDEBUG)
|
||||||
|
#define E_LOG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !_engine_h */
|
|
@ -0,0 +1,380 @@
|
||||||
|
#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, int* isActive)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int last = *isActive;
|
||||||
|
*isActive = !(*isActive);
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
uiNextLine();
|
||||||
|
|
||||||
|
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,31 @@
|
||||||
|
#ifndef ui_h
|
||||||
|
#define ui_h
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
|
||||||
|
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, int* isActive);
|
||||||
|
|
||||||
|
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();
|
||||||
|
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,149 @@
|
||||||
|
#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 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);
|
||||||
|
|
||||||
|
float diffm = vmag(&diff);
|
||||||
|
|
||||||
|
if(diffm == 0 || isnan(diffm))
|
||||||
|
{
|
||||||
|
memmove(r, from, sizeof(fvec));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmulf(&dir, &diff, 1.f/diffm * max_delta);
|
||||||
|
|
||||||
|
float dirm = vmag(&dir);
|
||||||
|
|
||||||
|
if(dirm >= diffm)
|
||||||
|
{
|
||||||
|
memmove(r, from, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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,300 @@
|
||||||
|
#include "boid_c.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <engine.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
ecsComponentMask boid_component;
|
||||||
|
|
||||||
|
float boid_acceleration = 75.f;
|
||||||
|
float boid_max_velocity = 50.f;
|
||||||
|
|
||||||
|
SDL_Texture* boid_texture;
|
||||||
|
|
||||||
|
behaviour_t alignment = {
|
||||||
|
.range = 10.f,
|
||||||
|
.force = .8f
|
||||||
|
};
|
||||||
|
behaviour_t separation = {
|
||||||
|
.range = 4.f,
|
||||||
|
.force = 50.0f
|
||||||
|
};
|
||||||
|
behaviour_t cohesion = {
|
||||||
|
.range = 100.f,
|
||||||
|
.force = 0.5f
|
||||||
|
};
|
||||||
|
behaviour_t wall_avoid = {
|
||||||
|
.range = 50.f,
|
||||||
|
.force = 100.f
|
||||||
|
};
|
||||||
|
behaviour_t mouse_interact = {
|
||||||
|
.range = 70.f,
|
||||||
|
.force = -1000.f
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_Rect boid_available_area = {0,0,800, 800};
|
||||||
|
|
||||||
|
void system_boid_update_position(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid;
|
||||||
|
fvec force, velocity;
|
||||||
|
float acceleration = boid_acceleration * delta_time;
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
|
||||||
|
force = boid->force;
|
||||||
|
vmulf(&force, &force, boid_max_velocity);
|
||||||
|
vmax(&force, &force, boid_max_velocity);
|
||||||
|
vmovetowards(&boid->velocity, &boid->velocity, &force, acceleration);
|
||||||
|
|
||||||
|
assert(!isnan(boid->position.x) && !isnan(boid->position.y));
|
||||||
|
assert(!isnan(boid->velocity.x) && !isnan(boid->velocity.y));
|
||||||
|
|
||||||
|
boid->force = (fvec){ 0.f, 0.f };
|
||||||
|
|
||||||
|
vmulf(&velocity, &boid->velocity, delta_time);
|
||||||
|
vadd(&boid->position, &boid->position, &velocity);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boid_update_near(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
float dist;
|
||||||
|
boid_c* boid, *other;
|
||||||
|
|
||||||
|
float max_range = alignment.range;
|
||||||
|
max_range = separation.range > max_range ? separation.range : max_range;
|
||||||
|
max_range = cohesion.range > max_range ? cohesion.range : max_range;
|
||||||
|
|
||||||
|
size_t hits = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
hits = 0;
|
||||||
|
|
||||||
|
memset(boid->near, noentity, sizeof(boid->near));
|
||||||
|
for(size_t j = 0; j < count && hits < BOID_NEAR_COUNT; ++j)
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(entities[j], boid_component);
|
||||||
|
dist = vdist(&boid->position, &other->position);
|
||||||
|
|
||||||
|
assert(!isnan(dist));
|
||||||
|
if(dist < max_range)
|
||||||
|
{
|
||||||
|
boid->near[hits++] = entities[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_draw_boids(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
if(!is_render_frame) return;
|
||||||
|
|
||||||
|
boid_c* boid;
|
||||||
|
|
||||||
|
int tw, th;
|
||||||
|
SDL_QueryTexture(boid_texture, NULL, NULL, &tw, &th);
|
||||||
|
SDL_Rect srcrect = {
|
||||||
|
.x = 0, .y = 0,
|
||||||
|
.w = tw, .h = th
|
||||||
|
};
|
||||||
|
SDL_FRect dstrect = {
|
||||||
|
.w = 5, .h = 5
|
||||||
|
};
|
||||||
|
int hw = dstrect.w / 2;
|
||||||
|
int hh = dstrect.h / 2;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
|
||||||
|
dstrect.x = boid->position.x - hw;
|
||||||
|
dstrect.y = boid->position.y - hh;
|
||||||
|
SDL_RenderCopyExF(renderer, boid_texture,
|
||||||
|
&srcrect, &dstrect,
|
||||||
|
(double)vang(&boid->velocity, &VDOWN) * 57.2957795,
|
||||||
|
&(SDL_FPoint){hw,hh}, SDL_FLIP_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boids_cohesion(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid, *other;
|
||||||
|
fvec avrg, force, diff;
|
||||||
|
float dist;
|
||||||
|
size_t hit_count;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
avrg = (fvec){ 0.f, 0.f };
|
||||||
|
hit_count = 0;
|
||||||
|
|
||||||
|
for(size_t j = 0; j < BOID_NEAR_COUNT && boid->near[j] != noentity; ++j)
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(boid->near[j], boid_component);
|
||||||
|
dist = vdist(&other->position, &boid->position);
|
||||||
|
if(dist < cohesion.range)
|
||||||
|
{
|
||||||
|
++hit_count;
|
||||||
|
vsub(&diff, &other->position, &avrg);
|
||||||
|
vmulf(&diff, &diff, 1.f/(hit_count));
|
||||||
|
vadd(&avrg, &avrg, &diff);
|
||||||
|
}
|
||||||
|
assert(!isnan(avrg.x) && !isnan(avrg.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
vsub(&force, &avrg, &boid->position);
|
||||||
|
vmulf(&force, &force, cohesion.force);
|
||||||
|
vadd(&boid->force, &boid->force, &force);
|
||||||
|
|
||||||
|
assert(!isnan(boid->force.x) && !isnan(boid->force.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boids_separation(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid, *other;
|
||||||
|
fvec avrg, force, diff;
|
||||||
|
float dist;
|
||||||
|
size_t hit_count;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
avrg = (fvec){ 0.f, 0.f };
|
||||||
|
hit_count = 0;
|
||||||
|
|
||||||
|
for(size_t j = 0; j < BOID_NEAR_COUNT && boid->near[j] != noentity; ++j)
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(boid->near[j], boid_component);
|
||||||
|
dist = vdist(&other->position, &boid->position);
|
||||||
|
if(dist < separation.range)
|
||||||
|
{
|
||||||
|
++hit_count;
|
||||||
|
vsub(&diff, &other->position, &avrg);
|
||||||
|
vmulf(&diff, &diff, 1.f/(hit_count));
|
||||||
|
vadd(&avrg, &avrg, &diff);
|
||||||
|
}
|
||||||
|
assert(!isnan(avrg.x) && !isnan(avrg.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
vsub(&force, &avrg, &boid->position);
|
||||||
|
vmulf(&force, &force, separation.force);
|
||||||
|
vsub(&boid->force, &boid->force, &force);
|
||||||
|
assert(!isnan(boid->force.x) && !isnan(boid->force.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boids_alignment(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid, *other;
|
||||||
|
fvec avrg, diff;
|
||||||
|
float dist;
|
||||||
|
size_t hit_count;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
avrg = (fvec){ 0.f, 0.f };
|
||||||
|
hit_count = 0;
|
||||||
|
|
||||||
|
for(size_t j = 0; j < BOID_NEAR_COUNT && boid->near[j] != noentity; ++j)
|
||||||
|
{
|
||||||
|
other = ecsGetComponentPtr(boid->near[j], boid_component);
|
||||||
|
dist = vdist(&other->position, &boid->position);
|
||||||
|
|
||||||
|
if(dist < alignment.range)
|
||||||
|
{
|
||||||
|
++hit_count;
|
||||||
|
vsub(&diff, &other->velocity, &avrg);
|
||||||
|
vmulf(&diff, &diff, 1.f/(hit_count));
|
||||||
|
vadd(&avrg, &avrg, &diff);
|
||||||
|
}
|
||||||
|
assert(!isnan(avrg.x) && !isnan(avrg.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
vmulf(&avrg, &avrg, alignment.force);
|
||||||
|
vadd(&boid->force, &boid->force, &avrg);
|
||||||
|
assert(!isnan(boid->force.x) && !isnan(boid->force.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boid_mouse(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid;
|
||||||
|
fvec mouse, diff;
|
||||||
|
float m;
|
||||||
|
int imx, imy;
|
||||||
|
uint32_t mstate = SDL_GetMouseState(&imx, &imy);
|
||||||
|
mouse = (fvec){ (float)imx, (float)imy };
|
||||||
|
if((mstate & SDL_BUTTON_LEFT) == 0) return;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
vsub(&diff, &mouse, &boid->position);
|
||||||
|
m = vmag(&diff);
|
||||||
|
if(m < mouse_interact.range)
|
||||||
|
{
|
||||||
|
vmulf(&diff, &diff, (1.f/m)*mouse_interact.force);
|
||||||
|
vadd(&boid->force, &boid->force, &diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boids_wrap(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
boid_c* boid;
|
||||||
|
int iw, ih;
|
||||||
|
float w, h;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &iw, &ih);
|
||||||
|
w = (float)iw; h = (float)ih;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
if(boid->position.x >= w) boid->position.x -= w;
|
||||||
|
if(boid->position.x < 0) boid->position.x += w;
|
||||||
|
if(boid->position.y >= h) boid->position.y -= h;
|
||||||
|
if(boid->position.y < 0) boid->position.y += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_boids_wall_avoid(ecsEntityId* entities, ecsComponentMask* components, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
if(wall_avoid.force == 0.f) return;
|
||||||
|
|
||||||
|
boid_c* boid;
|
||||||
|
fvec force;
|
||||||
|
|
||||||
|
SDL_Rect limits = boid_available_area;
|
||||||
|
limits.x += wall_avoid.range;
|
||||||
|
limits.w -= wall_avoid.range*2;
|
||||||
|
limits.y += wall_avoid.range;
|
||||||
|
limits.h -= wall_avoid.range*2;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entities[i], boid_component);
|
||||||
|
force = (fvec){ 0.f, 0.f };
|
||||||
|
|
||||||
|
if(boid->position.x >= limits.x + limits.w)
|
||||||
|
force.x = -1.f;
|
||||||
|
if(boid->position.x <= limits.x)
|
||||||
|
force.x = 1.f;
|
||||||
|
if(boid->position.y >= limits.y + limits.h)
|
||||||
|
force.y = -1.f;
|
||||||
|
if(boid->position.y <= limits.y)
|
||||||
|
force.y = 1.f;
|
||||||
|
|
||||||
|
vnor(&force, &force);
|
||||||
|
vmulf(&force, &force, wall_avoid.force);
|
||||||
|
vadd(&boid->force, &boid->force, &force);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef boid_c_h
|
||||||
|
#define boid_c_h
|
||||||
|
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#ifndef BOID_NEAR_COUNT
|
||||||
|
#define BOID_NEAR_COUNT (100)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern ecsComponentMask boid_component;
|
||||||
|
typedef struct boid_c {
|
||||||
|
fvec position;
|
||||||
|
fvec velocity;
|
||||||
|
fvec force;
|
||||||
|
ecsEntityId near[BOID_NEAR_COUNT];
|
||||||
|
} boid_c;
|
||||||
|
|
||||||
|
typedef struct behaviour_t {
|
||||||
|
float force;
|
||||||
|
float range;
|
||||||
|
} behaviour_t;
|
||||||
|
|
||||||
|
extern struct SDL_Texture* boid_texture;
|
||||||
|
extern float boid_acceleration;
|
||||||
|
extern float boid_max_velocity;
|
||||||
|
|
||||||
|
extern behaviour_t alignment;
|
||||||
|
extern behaviour_t separation;
|
||||||
|
extern behaviour_t cohesion;
|
||||||
|
extern behaviour_t wall_avoid;
|
||||||
|
extern behaviour_t mouse_interact;
|
||||||
|
extern struct SDL_Rect boid_available_area;
|
||||||
|
|
||||||
|
extern void system_boid_update_position(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boids_cohesion(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boids_separation(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boids_alignment(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boids_wall_avoid(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boids_wrap(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_draw_boids(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boid_mouse(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
extern void system_boid_update_near(ecsEntityId*, ecsComponentMask*, size_t, float);
|
||||||
|
|
||||||
|
#endif /* boid_c_h */
|
|
@ -0,0 +1,154 @@
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <ecs.h>
|
||||||
|
#include <engine.h>
|
||||||
|
#include <adb.h>
|
||||||
|
#include <ui.h>
|
||||||
|
|
||||||
|
#define BOID_NEAR_COUNT (200)
|
||||||
|
#include "boid_c.h"
|
||||||
|
|
||||||
|
int boid_spawn_num;
|
||||||
|
|
||||||
|
void system_draw_gui(ecsEntityId* entities, ecsComponentMask* mask, size_t count, float delta_time)
|
||||||
|
{
|
||||||
|
static int show_sliders = 1;
|
||||||
|
|
||||||
|
int ww, wh;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &ww, &wh);
|
||||||
|
|
||||||
|
boid_available_area.w = ww;
|
||||||
|
boid_available_area.h = wh;
|
||||||
|
boid_available_area.x = 0;
|
||||||
|
|
||||||
|
SDL_Rect rect = {
|
||||||
|
0, 0, 500, wh
|
||||||
|
};
|
||||||
|
|
||||||
|
uiBeginFrame();
|
||||||
|
|
||||||
|
if(uiBeginWindow(&rect, &show_sliders))
|
||||||
|
{
|
||||||
|
// set the area boids will stay in to exclude the area of the ui
|
||||||
|
boid_available_area.x = rect.w;
|
||||||
|
boid_available_area.w = ww - rect.w;
|
||||||
|
|
||||||
|
// render velocity and acceleration sliders
|
||||||
|
uiLabelNext("speed", 0.25f);
|
||||||
|
uiSlider(&boid_max_velocity, 10.f, 100.f, 5.f);
|
||||||
|
uiLabelNext("acceleration", 0.25f);
|
||||||
|
uiSlider(&boid_acceleration, 10.f, 100.f, 5.f);
|
||||||
|
|
||||||
|
// alignment parameters
|
||||||
|
uiHeader("alignment");
|
||||||
|
|
||||||
|
uiLabelNext("range", 0.25f);
|
||||||
|
uiSlider(&(alignment.range), 0.1f, 100.f, 1.f);
|
||||||
|
uiLabelNext("force", 0.25f);
|
||||||
|
uiSlider(&(alignment.force), 0.0f, 2.f, 0.01f);
|
||||||
|
|
||||||
|
// cohesion parameters
|
||||||
|
uiHeader("cohesion");
|
||||||
|
|
||||||
|
uiLabelNext("range", 0.25f);
|
||||||
|
uiSlider(&(cohesion.range), 0.1f, 100.f, 1.f);
|
||||||
|
uiLabelNext("force", 0.25f);
|
||||||
|
uiSlider(&(cohesion.force), 0.0f, 2.f, .01f);
|
||||||
|
|
||||||
|
// mouse interaction parameters
|
||||||
|
uiHeader("mouse");
|
||||||
|
|
||||||
|
uiLabelNext("range", 0.25f);
|
||||||
|
uiSlider(&(mouse_interact.range), 0.1f, 100.f, 1.f);
|
||||||
|
uiLabelNext("force", 0.25f);
|
||||||
|
uiSlider(&(mouse_interact.force), -200.f, 200.f, 10.f);
|
||||||
|
|
||||||
|
|
||||||
|
// separation range
|
||||||
|
uiHeader("separation");
|
||||||
|
|
||||||
|
uiSlider(&(separation.range), 0.1f, 100.f, 1.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sim_config(engine_init_t* config)
|
||||||
|
{
|
||||||
|
config->window_width = 1700;
|
||||||
|
config->window_height = 1000;
|
||||||
|
config->window_init_flags |= SDL_WINDOW_RESIZABLE;
|
||||||
|
boid_spawn_num = 500;
|
||||||
|
config->target_framerate = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_boids()
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &w, &h);
|
||||||
|
|
||||||
|
fvec position = {9, 0};
|
||||||
|
ecsEntityId entity;
|
||||||
|
boid_c* boid;
|
||||||
|
|
||||||
|
for(int i = 0; i < boid_spawn_num; i++)
|
||||||
|
{
|
||||||
|
position = (fvec){rand() % w, rand() % h};
|
||||||
|
if((entity = ecsCreateEntity(boid_component)) != noentity)
|
||||||
|
{
|
||||||
|
boid = ecsGetComponentPtr(entity, boid_component);
|
||||||
|
(*boid) = (boid_c){
|
||||||
|
.position = position,
|
||||||
|
.velocity = {0,0},
|
||||||
|
.force = {0,0}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sim_init()
|
||||||
|
{
|
||||||
|
// register boid_c as a component type
|
||||||
|
boid_component = ecsRegisterComponent(boid_c);
|
||||||
|
|
||||||
|
// enable the functions that make boids boid
|
||||||
|
ecsEnableSystem(&system_boid_update_position, boid_component, ECS_QUERY_ALL, 8, 50);
|
||||||
|
ecsEnableSystem(&system_boid_update_near, boid_component, ECS_QUERY_ALL, 0, 100);
|
||||||
|
ecsEnableSystem(&system_draw_boids, boid_component, ECS_QUERY_ALL, 0, 200);
|
||||||
|
ecsEnableSystem(&system_boids_wall_avoid, boid_component, ECS_QUERY_ALL, 8, 300);
|
||||||
|
ecsEnableSystem(&system_boids_cohesion, boid_component, ECS_QUERY_ALL, 8, 400);
|
||||||
|
ecsEnableSystem(&system_boids_alignment, boid_component, ECS_QUERY_ALL, 8, 410);
|
||||||
|
ecsEnableSystem(&system_boids_separation, boid_component, ECS_QUERY_ALL, 8, 420);
|
||||||
|
ecsEnableSystem(&system_boid_mouse, boid_component, ECS_QUERY_ALL, 8, 430);
|
||||||
|
|
||||||
|
// enable the gui system
|
||||||
|
ecsEnableSystem(&system_draw_gui, nocomponent, ECS_NOQUERY, 0, 500);
|
||||||
|
|
||||||
|
int w, h;
|
||||||
|
SDL_GetRendererOutputSize(renderer, &w, &h);
|
||||||
|
|
||||||
|
// load boid image
|
||||||
|
//asset_handle_t blur_asset = load_asset("blur.png");
|
||||||
|
asset_handle_t arrow_asset = load_asset("boid.png");
|
||||||
|
// set boid texture
|
||||||
|
boid_texture = get_asset(arrow_asset);
|
||||||
|
|
||||||
|
// load and set font
|
||||||
|
asset_handle_t font_asset = load_asset("Inter-Regular.otf");
|
||||||
|
uiSetFont(get_asset(font_asset));
|
||||||
|
|
||||||
|
// set the initially available area
|
||||||
|
boid_available_area = (SDL_Rect){
|
||||||
|
.x = 0, .y = 0,
|
||||||
|
.w = w, .h = h
|
||||||
|
};
|
||||||
|
|
||||||
|
// spawn a bunch of boids
|
||||||
|
spawn_boids();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sim_quit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue