rework tick -> world, move related logic from game into world api
This commit is contained in:
parent
8a2c40c817
commit
adce14bbf3
13
src/arena.c
13
src/arena.c
@ -90,6 +90,19 @@ void *_arena_push_bytes(struct arena *arena, u64 size, u64 align)
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copies the memory from the source arena into the destination arena,
|
||||||
|
* replacing old contents. Destination arena will be expanded if necessary contents. */
|
||||||
|
void arena_copy_replace(struct arena *dest, struct arena *src)
|
||||||
|
{
|
||||||
|
arena_reset(dest);
|
||||||
|
if (dest->committed < src->committed) {
|
||||||
|
u64 diff = src->committed - dest->committed;
|
||||||
|
_arena_push_bytes(dest, diff, 1);
|
||||||
|
}
|
||||||
|
MEMCPY(dest->base, src->base, src->committed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void arena_decommit_unused_blocks(struct arena *arena)
|
void arena_decommit_unused_blocks(struct arena *arena)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
|
|||||||
@ -29,6 +29,7 @@ struct temp_arena {
|
|||||||
struct arena arena_alloc(u64 reserve);
|
struct arena arena_alloc(u64 reserve);
|
||||||
void arena_release(struct arena *arena);
|
void arena_release(struct arena *arena);
|
||||||
void *_arena_push_bytes(struct arena *arena, u64 size, u64 align);
|
void *_arena_push_bytes(struct arena *arena, u64 size, u64 align);
|
||||||
|
void arena_copy_replace(struct arena *dest, struct arena *src);
|
||||||
void arena_decommit_unused_blocks(struct arena *arena);
|
void arena_decommit_unused_blocks(struct arena *arena);
|
||||||
|
|
||||||
INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align)
|
INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align)
|
||||||
|
|||||||
@ -71,11 +71,11 @@ struct huffman {
|
|||||||
struct huffman_entry *entries;
|
struct huffman_entry *entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL const u32 g_hclen_order[] = {
|
GLOBAL READONLY const u32 g_hclen_order[] = {
|
||||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL const struct huffman_entry g_length_table[] = {
|
GLOBAL READONLY const struct huffman_entry g_length_table[] = {
|
||||||
{3, 0}, /* 257 */
|
{3, 0}, /* 257 */
|
||||||
{4, 0}, /* 258 */
|
{4, 0}, /* 258 */
|
||||||
{5, 0}, /* 259 */
|
{5, 0}, /* 259 */
|
||||||
@ -107,7 +107,7 @@ GLOBAL const struct huffman_entry g_length_table[] = {
|
|||||||
{258, 0}, /* 285 */
|
{258, 0}, /* 285 */
|
||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL const struct huffman_entry g_dist_table[] = {
|
GLOBAL READONLY const struct huffman_entry g_dist_table[] = {
|
||||||
{1, 0}, /* 0 */
|
{1, 0}, /* 0 */
|
||||||
{2, 0}, /* 1 */
|
{2, 0}, /* 1 */
|
||||||
{3, 0}, /* 2 */
|
{3, 0}, /* 2 */
|
||||||
@ -140,7 +140,7 @@ GLOBAL const struct huffman_entry g_dist_table[] = {
|
|||||||
{24577, 13}, /* 29 */
|
{24577, 13}, /* 29 */
|
||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL const u32 g_fixed_bl_counts[][2] = {
|
GLOBAL READONLY const u32 g_fixed_bl_counts[][2] = {
|
||||||
{143, 8},
|
{143, 8},
|
||||||
{255, 9},
|
{255, 9},
|
||||||
{279, 7},
|
{279, 7},
|
||||||
|
|||||||
15
src/common.h
15
src/common.h
@ -163,7 +163,7 @@ extern "C" {
|
|||||||
|
|
||||||
/* Storage specifiers */
|
/* Storage specifiers */
|
||||||
#pragma section(".roglob", read)
|
#pragma section(".roglob", read)
|
||||||
#define READ_ONLY __declspec(allocate(".roglob"))
|
#define READONLY __declspec(allocate(".roglob"))
|
||||||
|
|
||||||
/* Markup */
|
/* Markup */
|
||||||
#define UNUSED void
|
#define UNUSED void
|
||||||
@ -231,19 +231,6 @@ extern "C" {
|
|||||||
#define CAT1(a, b) a ## b
|
#define CAT1(a, b) a ## b
|
||||||
#define CAT(a, b) CAT1(a, b)
|
#define CAT(a, b) CAT1(a, b)
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Nil structs
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
/* Declare */
|
|
||||||
#define DECLARE_NIL_STRUCT(type, function_name) \
|
|
||||||
extern READ_ONLY type CAT(__nil_struct__, function_name); \
|
|
||||||
INLINE READ_ONLY type *function_name(void) { return &CAT(__nil_struct__, function_name); }
|
|
||||||
|
|
||||||
/* Expects struct body after invocation. E.g. DEFINE_NIL_STRUCT(struct a, a_nil) { 0 } */
|
|
||||||
#define DEFINE_NIL_STRUCT(type, function_name) \
|
|
||||||
type READ_ONLY CAT(__nil_struct__, function_name) =
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Bit utils
|
* Bit utils
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
#define RESOURCES_EMBEDDED !(DEVELOPER)
|
#define RESOURCES_EMBEDDED !(DEVELOPER)
|
||||||
|
|
||||||
#define PIXELS_PER_UNIT 256
|
#define PIXELS_PER_UNIT 256
|
||||||
#define MAX_ENTITIES 4096
|
|
||||||
|
|
||||||
#define AUDIO_ENABLED 0
|
#define AUDIO_ENABLED 0
|
||||||
#define VSYNC_ENABLED 0
|
#define VSYNC_ENABLED 0
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
|
|
||||||
DEFINE_NIL_STRUCT(struct entity, entity_nil) { 0 };
|
READONLY struct entity _g_entity_nil = { 0 };
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Prop
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void entity_enable_prop(struct entity *entity, enum entity_prop prop)
|
void entity_enable_prop(struct entity *entity, enum entity_prop prop)
|
||||||
{
|
{
|
||||||
|
|||||||
11
src/entity.h
11
src/entity.h
@ -4,10 +4,6 @@
|
|||||||
#include "sheet.h"
|
#include "sheet.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Entity
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct entity_handle {
|
struct entity_handle {
|
||||||
u64 idx;
|
u64 idx;
|
||||||
u64 gen;
|
u64 gen;
|
||||||
@ -35,7 +31,7 @@ struct entity {
|
|||||||
u64 continuity_gen;
|
u64 continuity_gen;
|
||||||
u64 props[(ENTITY_PROP_COUNT + 63) / 64];
|
u64 props[(ENTITY_PROP_COUNT + 63) / 64];
|
||||||
|
|
||||||
struct entity *next_free;
|
struct entity_handle next_free;
|
||||||
|
|
||||||
/* Tree */
|
/* Tree */
|
||||||
struct entity_handle parent;
|
struct entity_handle parent;
|
||||||
@ -96,9 +92,8 @@ struct entity {
|
|||||||
f32 camera_zoom;
|
f32 camera_zoom;
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_NIL_STRUCT(struct entity, entity_nil);
|
extern READONLY struct entity _g_entity_nil;
|
||||||
|
INLINE READONLY struct entity *entity_nil(void) { return &_g_entity_nil; }
|
||||||
/* Prop */
|
|
||||||
|
|
||||||
void entity_enable_prop(struct entity *entity, enum entity_prop prop);
|
void entity_enable_prop(struct entity *entity, enum entity_prop prop);
|
||||||
void entity_disable_prop(struct entity *entity, enum entity_prop prop);
|
void entity_disable_prop(struct entity *entity, enum entity_prop prop);
|
||||||
|
|||||||
166
src/game.c
166
src/game.c
@ -2,9 +2,8 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "entity.h"
|
#include "world.h"
|
||||||
#include "sheet.h"
|
#include "sheet.h"
|
||||||
#include "tick.h"
|
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
@ -17,8 +16,7 @@ GLOBAL struct {
|
|||||||
struct sys_thread game_thread;
|
struct sys_thread game_thread;
|
||||||
|
|
||||||
f64 timescale;
|
f64 timescale;
|
||||||
struct entity *free_entity_head;
|
struct world world;
|
||||||
struct tick tick;
|
|
||||||
|
|
||||||
/* Game thread input */
|
/* Game thread input */
|
||||||
struct sys_mutex game_cmds_mutex;
|
struct sys_mutex game_cmds_mutex;
|
||||||
@ -26,78 +24,9 @@ GLOBAL struct {
|
|||||||
|
|
||||||
/* Game thread output */
|
/* Game thread output */
|
||||||
struct sys_mutex published_tick_mutex;
|
struct sys_mutex published_tick_mutex;
|
||||||
struct tick published_tick;
|
struct world published_tick;
|
||||||
} L = { 0 }, DEBUG_LVAR(L_game);
|
} L = { 0 }, DEBUG_LVAR(L_game);
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Entity allocation
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
INTERNAL struct entity *entity_alloc(void)
|
|
||||||
{
|
|
||||||
struct entity *entity = NULL;
|
|
||||||
if (L.free_entity_head) {
|
|
||||||
/* Reuse from free list */
|
|
||||||
entity = L.free_entity_head;
|
|
||||||
L.free_entity_head = entity->next_free;
|
|
||||||
*entity = (struct entity) {
|
|
||||||
.handle = entity->handle
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
/* Make new */
|
|
||||||
if (L.tick.entities_count >= MAX_ENTITIES) {
|
|
||||||
sys_panic(STR("MAX_ENTITIES reached"));
|
|
||||||
}
|
|
||||||
u64 idx = L.tick.entities_count++;
|
|
||||||
entity = &L.tick.entities[idx];
|
|
||||||
*entity = (struct entity) {
|
|
||||||
.handle = { .gen = 1, .idx = idx }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
INTERNAL void entity_release(struct entity *entity)
|
|
||||||
{
|
|
||||||
entity->next_free = L.free_entity_head;
|
|
||||||
L.free_entity_head = entity;
|
|
||||||
*entity = (struct entity) {
|
|
||||||
.gen = entity->gen + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */
|
|
||||||
INTERNAL struct entity *entity_from_handle(struct entity_handle eh)
|
|
||||||
{
|
|
||||||
if (eh.idx < L.tick.entities_count) {
|
|
||||||
struct entity *entity = &L.tick.entities[eh.idx];
|
|
||||||
if (entity->handle.gen == eh.gen) {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entity_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL void entity_tree_attach(struct entity *parent, struct entity *child)
|
|
||||||
{
|
|
||||||
struct entity *first_child = entity_from_handle(parent->first);
|
|
||||||
struct entity *last_child = entity_from_handle(parent->last);
|
|
||||||
|
|
||||||
child->prev = last_child->handle;
|
|
||||||
child->parent = parent->handle;
|
|
||||||
|
|
||||||
if (last_child->valid) {
|
|
||||||
last_child->next = child->handle;
|
|
||||||
}
|
|
||||||
parent->last = child->handle;
|
|
||||||
|
|
||||||
if (!first_child->valid) {
|
|
||||||
parent->first = child->handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Game cmd
|
* Game cmd
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -140,8 +69,8 @@ INTERNAL void publish_game_tick(void)
|
|||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
sys_mutex_lock(&L.published_tick_mutex);
|
sys_mutex_lock(&L.published_tick_mutex);
|
||||||
L.tick.published_ts = sys_timestamp();
|
L.world.tick_published_ts = sys_timestamp();
|
||||||
tick_copy(&L.published_tick, &L.tick);
|
world_copy_replace(&L.published_tick, &L.world);
|
||||||
sys_mutex_unlock(&L.published_tick_mutex);
|
sys_mutex_unlock(&L.published_tick_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,16 +80,16 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
|
||||||
++L.tick.id;
|
++L.world.tick_id;
|
||||||
L.tick.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale);
|
L.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale);
|
||||||
L.tick.time += L.tick.dt;
|
L.world.time += L.world.dt;
|
||||||
|
|
||||||
/* TODO: remove this (testing) */
|
/* TODO: remove this (testing) */
|
||||||
/* Initialize entities */
|
/* Initialize entities */
|
||||||
static b32 run = 0;
|
static b32 run = 0;
|
||||||
if (!run) {
|
if (!run) {
|
||||||
run = 1;
|
run = 1;
|
||||||
(UNUSED)entity_tree_attach;
|
(UNUSED)world_link_entities;
|
||||||
|
|
||||||
/* Player ent */
|
/* Player ent */
|
||||||
struct entity *player_ent;
|
struct entity *player_ent;
|
||||||
@ -169,7 +98,7 @@ INTERNAL void game_update(void)
|
|||||||
struct v2 size = V2(1, 1);
|
struct v2 size = V2(1, 1);
|
||||||
f32 r = 0;
|
f32 r = 0;
|
||||||
|
|
||||||
struct entity *e = entity_alloc();
|
struct entity *e = world_alloc_entity(&L.world);
|
||||||
e->valid = true;
|
e->valid = true;
|
||||||
e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size);
|
e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size);
|
||||||
|
|
||||||
@ -219,7 +148,7 @@ INTERNAL void game_update(void)
|
|||||||
struct v2 size = V2(1, 1);
|
struct v2 size = V2(1, 1);
|
||||||
f32 r = 0;
|
f32 r = 0;
|
||||||
|
|
||||||
struct entity *e = entity_alloc();
|
struct entity *e = world_alloc_entity(&L.world);
|
||||||
e->valid = true;
|
e->valid = true;
|
||||||
e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size);
|
e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size);
|
||||||
|
|
||||||
@ -260,12 +189,12 @@ INTERNAL void game_update(void)
|
|||||||
//entity_enable_prop(e, ENTITY_PROP_TEST);
|
//entity_enable_prop(e, ENTITY_PROP_TEST);
|
||||||
//entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE);
|
//entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE);
|
||||||
|
|
||||||
entity_tree_attach(player_ent, e);
|
world_link_entities(&L.world, player_ent, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Camera ent */
|
/* Camera ent */
|
||||||
{
|
{
|
||||||
struct entity *e = entity_alloc();
|
struct entity *e = world_alloc_entity(&L.world);
|
||||||
e->valid = true;
|
e->valid = true;
|
||||||
e->rel_xform = XFORM_IDENT;
|
e->rel_xform = XFORM_IDENT;
|
||||||
|
|
||||||
@ -281,7 +210,7 @@ INTERNAL void game_update(void)
|
|||||||
* Process game cmds
|
* Process game cmds
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
L.tick.player_move_dir = V2(0, 0);
|
L.world.player_move_dir = V2(0, 0);
|
||||||
|
|
||||||
struct game_cmd_array game_cmds = pop_cmds(scratch.arena);
|
struct game_cmd_array game_cmds = pop_cmds(scratch.arena);
|
||||||
for (u64 i = 0; i < game_cmds.count; ++i) {
|
for (u64 i = 0; i < game_cmds.count; ++i) {
|
||||||
@ -292,21 +221,21 @@ INTERNAL void game_update(void)
|
|||||||
case GAME_CMD_KIND_PLAYER_MOVE: {
|
case GAME_CMD_KIND_PLAYER_MOVE: {
|
||||||
struct v2 dir = cmd.dir;
|
struct v2 dir = cmd.dir;
|
||||||
|
|
||||||
L.tick.player_move_dir = v2_add(L.tick.player_move_dir, dir);
|
L.world.player_move_dir = v2_add(L.world.player_move_dir, dir);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
/* Focus */
|
/* Focus */
|
||||||
case GAME_CMD_KIND_PLAYER_FOCUS: {
|
case GAME_CMD_KIND_PLAYER_FOCUS: {
|
||||||
L.tick.player_focus = cmd.pos;
|
L.world.player_focus = cmd.pos;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_len(L.tick.player_move_dir) > 1.f) {
|
if (v2_len(L.world.player_move_dir) > 1.f) {
|
||||||
/* Clamp movement magnitude */
|
/* Clamp movement magnitude */
|
||||||
L.tick.player_move_dir = v2_norm(L.tick.player_move_dir);
|
L.world.player_move_dir = v2_norm(L.world.player_move_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
@ -316,8 +245,8 @@ INTERNAL void game_update(void)
|
|||||||
* Update entities pre-physics
|
* Update entities pre-physics
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) {
|
for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) {
|
||||||
struct entity *ent = &L.tick.entities[entity_index];
|
struct entity *ent = &L.world.entities[entity_index];
|
||||||
if (!ent->valid) continue;
|
if (!ent->valid) continue;
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -333,7 +262,7 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
/* ENTITY_PROP_ANIMATING */
|
/* ENTITY_PROP_ANIMATING */
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING) && ent->animation_start_time == 0) {
|
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING) && ent->animation_start_time == 0) {
|
||||||
ent->animation_start_time = L.tick.time;
|
ent->animation_start_time = L.world.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -343,7 +272,7 @@ INTERNAL void game_update(void)
|
|||||||
if (ent->animation_start_time > 0) {
|
if (ent->animation_start_time > 0) {
|
||||||
/* Stop animation if past duration and not looping */
|
/* Stop animation if past duration and not looping */
|
||||||
if (!ent->animation_looping) {
|
if (!ent->animation_looping) {
|
||||||
f64 time_in_anim = L.tick.time - ent->animation_start_time;
|
f64 time_in_anim = L.world.time - ent->animation_start_time;
|
||||||
|
|
||||||
struct sheet *sheet = sheet_load(ent->sprite_name);
|
struct sheet *sheet = sheet_load(ent->sprite_name);
|
||||||
if (sheet) {
|
if (sheet) {
|
||||||
@ -374,7 +303,7 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
/* ENTITY_PROP_TEST */
|
/* ENTITY_PROP_TEST */
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
|
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
|
||||||
f32 t = ((f32)L.tick.time);
|
f32 t = ((f32)L.world.time);
|
||||||
f32 r = t * 2.f;
|
f32 r = t * 2.f;
|
||||||
f32 s = 1 + (math_fabs(math_sin(t * 5)) * 3);
|
f32 s = 1 + (math_fabs(math_sin(t * 5)) * 3);
|
||||||
(UNUSED)r;
|
(UNUSED)r;
|
||||||
@ -390,8 +319,8 @@ INTERNAL void game_update(void)
|
|||||||
* Update entity physics
|
* Update entity physics
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) {
|
for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) {
|
||||||
struct entity *ent = &L.tick.entities[entity_index];
|
struct entity *ent = &L.world.entities[entity_index];
|
||||||
if (!ent->valid) continue;
|
if (!ent->valid) continue;
|
||||||
if (ent->parent.gen) continue; /* Only update parent entities */
|
if (ent->parent.gen) continue; /* Only update parent entities */
|
||||||
|
|
||||||
@ -403,7 +332,7 @@ INTERNAL void game_update(void)
|
|||||||
f32 max_speed = ent->player_max_speed;
|
f32 max_speed = ent->player_max_speed;
|
||||||
f32 acceleration_rate = ent->player_acceleration;
|
f32 acceleration_rate = ent->player_acceleration;
|
||||||
acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */
|
acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */
|
||||||
struct v2 target_velocity = v2_mul(L.tick.player_move_dir, max_speed);
|
struct v2 target_velocity = v2_mul(L.world.player_move_dir, max_speed);
|
||||||
struct v2 target_acceleration = v2_sub(target_velocity, ent->velocity);
|
struct v2 target_acceleration = v2_sub(target_velocity, ent->velocity);
|
||||||
ent->acceleration = v2_mul(target_acceleration, acceleration_rate);
|
ent->acceleration = v2_mul(target_acceleration, acceleration_rate);
|
||||||
}
|
}
|
||||||
@ -413,7 +342,7 @@ INTERNAL void game_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
{
|
{
|
||||||
f32 dt = (f32)L.tick.dt;
|
f32 dt = (f32)L.world.dt;
|
||||||
|
|
||||||
/* Apply acceleration to velocity */
|
/* Apply acceleration to velocity */
|
||||||
struct v2 a = v2_mul(ent->acceleration, dt);
|
struct v2 a = v2_mul(ent->acceleration, dt);
|
||||||
@ -429,7 +358,7 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
|
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
|
||||||
struct v2 ent_pos = ent->rel_xform.og;
|
struct v2 ent_pos = ent->rel_xform.og;
|
||||||
struct v2 look_pos = L.tick.player_focus;
|
struct v2 look_pos = L.world.player_focus;
|
||||||
f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2;
|
f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2;
|
||||||
ent->rel_xform = xform_with_rotation(ent->rel_xform, r);
|
ent->rel_xform = xform_with_rotation(ent->rel_xform, r);
|
||||||
}
|
}
|
||||||
@ -440,8 +369,8 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
/* ENTITY_PROP_TEST_FOLLOW_MOUSE */
|
/* ENTITY_PROP_TEST_FOLLOW_MOUSE */
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_TEST_FOLLOW_MOUSE)) {
|
if (entity_has_prop(ent, ENTITY_PROP_TEST_FOLLOW_MOUSE)) {
|
||||||
ent->rel_xform.og = L.tick.player_focus;
|
ent->rel_xform.og = L.world.player_focus;
|
||||||
ent->test_start_rel_xform.og = L.tick.player_focus;
|
ent->test_start_rel_xform.og = L.world.player_focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -450,25 +379,27 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
ent->world_xform = ent->rel_xform;
|
ent->world_xform = ent->rel_xform;
|
||||||
|
|
||||||
struct entity *child = entity_from_handle(ent->first);
|
struct entity *child = world_entity_from_handle(&L.world, ent->first);
|
||||||
struct xform parent_xform = ent->world_xform;
|
struct xform parent_xform = ent->world_xform;
|
||||||
while (child->valid) {
|
while (child->valid) {
|
||||||
|
|
||||||
|
/* Calculate child world xform */
|
||||||
child->world_xform = xform_mul(parent_xform, child->rel_xform);
|
child->world_xform = xform_mul(parent_xform, child->rel_xform);
|
||||||
|
|
||||||
/* Depth first iteration */
|
/* Depth first iteration */
|
||||||
if (child->first.gen) {
|
if (child->first.gen) {
|
||||||
/* Next child */
|
/* Next child */
|
||||||
parent_xform = child->world_xform;
|
parent_xform = child->world_xform;
|
||||||
child = entity_from_handle(child->first);
|
child = world_entity_from_handle(&L.world, child->first);
|
||||||
} else if (child->next.gen) {
|
} else if (child->next.gen) {
|
||||||
/* Next sibling */
|
/* Next sibling */
|
||||||
child = entity_from_handle(child->next);
|
child = world_entity_from_handle(&L.world, child->next);
|
||||||
} else if (child->parent.gen && entity_from_handle(child->parent)->next.gen) {
|
} else if (child->parent.gen && world_entity_from_handle(&L.world, child->parent)->next.gen) {
|
||||||
/* Next parent sibling */
|
/* Next parent sibling */
|
||||||
struct entity *parent = entity_from_handle(child->parent);
|
struct entity *parent = world_entity_from_handle(&L.world, child->parent);
|
||||||
struct entity *grandparent = entity_from_handle(parent->parent);
|
struct entity *grandparent = world_entity_from_handle(&L.world, parent->parent);
|
||||||
parent_xform = grandparent->world_xform;
|
parent_xform = grandparent->world_xform;
|
||||||
child = entity_from_handle(parent->next);
|
child = world_entity_from_handle(&L.world, parent->next);
|
||||||
} else {
|
} else {
|
||||||
child = entity_nil();
|
child = entity_nil();
|
||||||
}
|
}
|
||||||
@ -479,8 +410,8 @@ INTERNAL void game_update(void)
|
|||||||
* Update entities post-physics
|
* Update entities post-physics
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) {
|
for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) {
|
||||||
struct entity *ent = &L.tick.entities[entity_index];
|
struct entity *ent = &L.world.entities[entity_index];
|
||||||
if (!ent->valid) continue;
|
if (!ent->valid) continue;
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -488,7 +419,7 @@ INTERNAL void game_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) {
|
if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) {
|
||||||
struct entity *follow = entity_from_handle(ent->camera_follow);
|
struct entity *follow = world_entity_from_handle(&L.world, ent->camera_follow);
|
||||||
|
|
||||||
ent->world_xform = follow->world_xform;
|
ent->world_xform = follow->world_xform;
|
||||||
ent->world_xform = xform_with_rotation(ent->world_xform, 0);
|
ent->world_xform = xform_with_rotation(ent->world_xform, 0);
|
||||||
@ -548,9 +479,14 @@ void game_startup(void)
|
|||||||
L.game_cmds_mutex = sys_mutex_alloc();
|
L.game_cmds_mutex = sys_mutex_alloc();
|
||||||
L.game_cmds_arena = arena_alloc(GIGABYTE(64));
|
L.game_cmds_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
/* Initialize tick storage */
|
/* Initialize world */
|
||||||
|
world_alloc(&L.world);
|
||||||
|
|
||||||
|
/* Initialize tick transmission */
|
||||||
|
world_alloc(&L.published_tick);
|
||||||
L.published_tick_mutex = sys_mutex_alloc();
|
L.published_tick_mutex = sys_mutex_alloc();
|
||||||
|
|
||||||
|
|
||||||
L.timescale = 1.0;
|
L.timescale = 1.0;
|
||||||
L.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread"));
|
L.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread"));
|
||||||
}
|
}
|
||||||
@ -565,16 +501,16 @@ void game_shutdown(void)
|
|||||||
* Interface
|
* Interface
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void game_get_latest_tick(struct tick *dest)
|
void game_get_latest_tick(struct world *dest)
|
||||||
{
|
{
|
||||||
sys_mutex_lock(&L.published_tick_mutex);
|
sys_mutex_lock(&L.published_tick_mutex);
|
||||||
tick_copy(dest, &L.published_tick);
|
world_copy_replace(dest, &L.published_tick);
|
||||||
sys_mutex_unlock(&L.published_tick_mutex);
|
sys_mutex_unlock(&L.published_tick_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 game_get_latest_tick_id(void)
|
u64 game_get_latest_tick_id(void)
|
||||||
{
|
{
|
||||||
return L.published_tick.id;
|
return L.published_tick.tick_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_push_cmds(struct game_cmd_array cmd_array)
|
void game_push_cmds(struct game_cmd_array cmd_array)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef GAME_H
|
#ifndef GAME_H
|
||||||
#define GAME_H
|
#define GAME_H
|
||||||
|
|
||||||
struct tick;
|
struct world;
|
||||||
|
|
||||||
enum game_cmd_kind {
|
enum game_cmd_kind {
|
||||||
GAME_CMD_KIND_NONE,
|
GAME_CMD_KIND_NONE,
|
||||||
@ -31,7 +31,7 @@ struct game_cmd_array {
|
|||||||
void game_startup(void);
|
void game_startup(void);
|
||||||
void game_shutdown(void);
|
void game_shutdown(void);
|
||||||
|
|
||||||
void game_get_latest_tick(struct tick *dest);
|
void game_get_latest_tick(struct world *dest);
|
||||||
u64 game_get_latest_tick_id(void);
|
u64 game_get_latest_tick_id(void);
|
||||||
|
|
||||||
void game_push_cmds(struct game_cmd_array cmd_array);
|
void game_push_cmds(struct game_cmd_array cmd_array);
|
||||||
|
|||||||
@ -21,7 +21,7 @@ GLOBAL struct {
|
|||||||
b32 file_valid;
|
b32 file_valid;
|
||||||
} L = { 0 }, DEBUG_LVAR(L_log);
|
} L = { 0 }, DEBUG_LVAR(L_log);
|
||||||
|
|
||||||
GLOBAL const struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = {
|
GLOBAL READONLY const struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = {
|
||||||
[LOG_LEVEL_CRITICAL] = {
|
[LOG_LEVEL_CRITICAL] = {
|
||||||
STR_NOCAST("CRITICAL"),
|
STR_NOCAST("CRITICAL"),
|
||||||
0xFFFF00FF
|
0xFFFF00FF
|
||||||
|
|||||||
@ -267,7 +267,7 @@ struct sheet *sheet_load(struct string path)
|
|||||||
* Sheet data
|
* Sheet data
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
GLOBAL struct sheet_tag g_default_tag = { 0 };
|
GLOBAL READONLY struct sheet_tag g_default_tag = { 0 };
|
||||||
|
|
||||||
struct sheet_tag sheet_get_tag(struct sheet *sheet, struct string name)
|
struct sheet_tag sheet_get_tag(struct sheet *sheet, struct string name)
|
||||||
{
|
{
|
||||||
|
|||||||
10
src/tick.c
10
src/tick.c
@ -1,10 +0,0 @@
|
|||||||
#include "tick.h"
|
|
||||||
|
|
||||||
void tick_copy(struct tick *dst, struct tick *src)
|
|
||||||
{
|
|
||||||
__prof;
|
|
||||||
/* Copy non-entity fields */
|
|
||||||
MEMCPY(dst, src, FIELD_OFFSETOF(struct tick, entities));
|
|
||||||
/* Copy entities */
|
|
||||||
MEMCPY(&dst->entities, &src->entities, sizeof(struct entity) * src->entities_count);
|
|
||||||
}
|
|
||||||
22
src/tick.h
22
src/tick.h
@ -1,22 +0,0 @@
|
|||||||
#ifndef TICK_H
|
|
||||||
#define TICK_H
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
|
|
||||||
struct tick {
|
|
||||||
u64 id; /* Starts at 1 */
|
|
||||||
sys_timestamp_t published_ts;
|
|
||||||
|
|
||||||
f64 dt;
|
|
||||||
f64 time;
|
|
||||||
|
|
||||||
struct v2 player_move_dir; /* Player movement direction */
|
|
||||||
struct v2 player_focus; /* Mouse cursor pos in world coordinates */
|
|
||||||
|
|
||||||
u64 entities_count; /* Includes 'released' & non-active entities */
|
|
||||||
struct entity entities[MAX_ENTITIES];
|
|
||||||
};
|
|
||||||
|
|
||||||
void tick_copy(struct tick *dst, struct tick *src);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
88
src/user.c
88
src/user.c
@ -12,7 +12,8 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "tick.h"
|
#include "world.h"
|
||||||
|
#include "entity.h"
|
||||||
|
|
||||||
/* FIXME: remove this (testing) */
|
/* FIXME: remove this (testing) */
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
@ -29,6 +30,11 @@ struct view {
|
|||||||
f32 rot;
|
f32 rot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct bind_state {
|
||||||
|
b32 pressed; /* Is this bind held down this frame */
|
||||||
|
u32 num_presses; /* How many times was this bind pressed since last frame */
|
||||||
|
};
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
b32 shutdown;
|
b32 shutdown;
|
||||||
struct sys_thread user_thread;
|
struct sys_thread user_thread;
|
||||||
@ -37,7 +43,11 @@ GLOBAL struct {
|
|||||||
struct renderer_canvas *world_canvas;
|
struct renderer_canvas *world_canvas;
|
||||||
struct renderer_canvas *ui_canvas;
|
struct renderer_canvas *ui_canvas;
|
||||||
struct view world_view;
|
struct view world_view;
|
||||||
struct tick blend_ticks[2];
|
|
||||||
|
struct world blend_ticks[2];
|
||||||
|
struct world world;
|
||||||
|
|
||||||
|
struct bind_state bind_states[USER_BIND_KIND_COUNT];
|
||||||
|
|
||||||
b32 debug_camera;
|
b32 debug_camera;
|
||||||
b32 debug_camera_panning;
|
b32 debug_camera_panning;
|
||||||
@ -62,16 +72,9 @@ GLOBAL struct {
|
|||||||
* Bind state
|
* Bind state
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct bind_state {
|
|
||||||
b32 pressed; /* Is this bind held down this frame */
|
|
||||||
u32 num_presses; /* How many times was this bind pressed since last frame */
|
|
||||||
};
|
|
||||||
|
|
||||||
GLOBAL struct bind_state g_bind_states[USER_BIND_KIND_COUNT] = { 0 };
|
|
||||||
|
|
||||||
/* TODO: Remove this */
|
/* TODO: Remove this */
|
||||||
|
|
||||||
GLOBAL enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
|
GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
|
||||||
[SYS_BTN_W] = USER_BIND_KIND_MOVE_UP,
|
[SYS_BTN_W] = USER_BIND_KIND_MOVE_UP,
|
||||||
[SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN,
|
[SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN,
|
||||||
[SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT,
|
[SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT,
|
||||||
@ -122,8 +125,8 @@ INTERNAL struct sys_event_array pull_sys_events(struct arena *arena)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct interp_ticks {
|
struct interp_ticks {
|
||||||
struct tick *from_tick;
|
struct world *from_tick;
|
||||||
struct tick *to_tick;
|
struct world *to_tick;
|
||||||
};
|
};
|
||||||
|
|
||||||
INTERNAL struct interp_ticks pull_interp_ticks(void)
|
INTERNAL struct interp_ticks pull_interp_ticks(void)
|
||||||
@ -132,9 +135,9 @@ INTERNAL struct interp_ticks pull_interp_ticks(void)
|
|||||||
ASSERT(ARRAY_COUNT(L.blend_ticks) == 2);
|
ASSERT(ARRAY_COUNT(L.blend_ticks) == 2);
|
||||||
|
|
||||||
/* Determine from & to tick from existing ticks */
|
/* Determine from & to tick from existing ticks */
|
||||||
struct tick *from_tick = NULL;
|
struct world *from_tick = NULL;
|
||||||
struct tick *to_tick = NULL;
|
struct world *to_tick = NULL;
|
||||||
if (L.blend_ticks[1].id > L.blend_ticks[0].id) {
|
if (L.blend_ticks[1].tick_id > L.blend_ticks[0].tick_id) {
|
||||||
from_tick = &L.blend_ticks[0];
|
from_tick = &L.blend_ticks[0];
|
||||||
to_tick = &L.blend_ticks[1];
|
to_tick = &L.blend_ticks[1];
|
||||||
} else {
|
} else {
|
||||||
@ -144,9 +147,9 @@ INTERNAL struct interp_ticks pull_interp_ticks(void)
|
|||||||
|
|
||||||
/* Check for new tick from game thread */
|
/* Check for new tick from game thread */
|
||||||
u64 latest_tick_id = game_get_latest_tick_id();
|
u64 latest_tick_id = game_get_latest_tick_id();
|
||||||
if (latest_tick_id > to_tick->id) {
|
if (latest_tick_id > to_tick->tick_id) {
|
||||||
/* Swap pointers */
|
/* Swap pointers */
|
||||||
struct tick *temp = from_tick;
|
struct world *temp = from_tick;
|
||||||
from_tick = to_tick;
|
from_tick = to_tick;
|
||||||
to_tick = temp;
|
to_tick = temp;
|
||||||
/* Pull game tick */
|
/* Pull game tick */
|
||||||
@ -314,9 +317,9 @@ INTERNAL void user_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Reset bind states "was_pressed" */
|
/* Reset bind states "was_pressed" */
|
||||||
for (u32 i = 0; i < ARRAY_COUNT(g_bind_states); ++i) {
|
for (u32 i = 0; i < ARRAY_COUNT(L.bind_states); ++i) {
|
||||||
g_bind_states[i] = (struct bind_state) {
|
L.bind_states[i] = (struct bind_state) {
|
||||||
.pressed = g_bind_states[i].pressed
|
.pressed = L.bind_states[i].pressed
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,9 +374,9 @@ INTERNAL void user_update(void)
|
|||||||
enum user_bind_kind bind = g_binds[button];
|
enum user_bind_kind bind = g_binds[button];
|
||||||
if (bind) {
|
if (bind) {
|
||||||
b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN;
|
b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN;
|
||||||
g_bind_states[bind].pressed = pressed;
|
L.bind_states[bind].pressed = pressed;
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
++g_bind_states[bind].num_presses;
|
++L.bind_states[bind].num_presses;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,8 +389,8 @@ INTERNAL void user_update(void)
|
|||||||
/* Movement */
|
/* Movement */
|
||||||
{
|
{
|
||||||
struct v2 input_move_dir = { 0 };
|
struct v2 input_move_dir = { 0 };
|
||||||
for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(g_bind_states); ++bind) {
|
for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(L.bind_states); ++bind) {
|
||||||
struct bind_state state = g_bind_states[bind];
|
struct bind_state state = L.bind_states[bind];
|
||||||
|
|
||||||
if (!state.pressed && state.num_presses <= 0) {
|
if (!state.pressed && state.num_presses <= 0) {
|
||||||
continue;
|
continue;
|
||||||
@ -424,11 +427,11 @@ INTERNAL void user_update(void)
|
|||||||
* Update view from debug camera
|
* Update view from debug camera
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
if (g_bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) {
|
if (L.bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) {
|
||||||
L.debug_draw = !L.debug_draw;
|
L.debug_draw = !L.debug_draw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) {
|
if (L.bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) {
|
||||||
L.debug_camera = !L.debug_camera;
|
L.debug_camera = !L.debug_camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,7 +447,7 @@ INTERNAL void user_update(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Zoom view */
|
/* Zoom view */
|
||||||
i32 input_zooms = g_bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - g_bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses;
|
i32 input_zooms = L.bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - L.bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses;
|
||||||
if (input_zooms != 0) {
|
if (input_zooms != 0) {
|
||||||
i32 dir = input_zooms >= 0 ? 1 : -1;
|
i32 dir = input_zooms >= 0 ? 1 : -1;
|
||||||
u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms;
|
u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms;
|
||||||
@ -479,29 +482,28 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
/* Pull ticks */
|
/* Pull ticks */
|
||||||
struct interp_ticks interp_ticks = pull_interp_ticks();
|
struct interp_ticks interp_ticks = pull_interp_ticks();
|
||||||
struct tick *t0 = interp_ticks.from_tick;
|
struct world *t0 = interp_ticks.from_tick;
|
||||||
struct tick *t1 = interp_ticks.to_tick;
|
struct world *t1 = interp_ticks.to_tick;
|
||||||
|
|
||||||
/* Produce interpolated tick */
|
/* Produce interpolated tick */
|
||||||
struct tick *tick = arena_push_zero(scratch.arena, struct tick);
|
|
||||||
{
|
{
|
||||||
__profscope(produce_interpolated_tick);
|
__profscope(produce_interpolated_tick);
|
||||||
|
|
||||||
sys_timestamp_t tick_publish_delta = t1->published_ts - t0->published_ts;
|
sys_timestamp_t tick_publish_delta = t1->tick_published_ts - t0->tick_published_ts;
|
||||||
f32 tick_blend = (f32)((sys_timestamp() - t1->published_ts) / (f64)tick_publish_delta);
|
f32 tick_blend = (f32)((sys_timestamp() - t1->tick_published_ts) / (f64)tick_publish_delta);
|
||||||
|
|
||||||
tick_copy(tick, t1);
|
world_copy_replace(&L.world, t1);
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
/* Blend time */
|
/* Blend time */
|
||||||
tick->time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend);
|
L.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend);
|
||||||
|
|
||||||
/* Blend entities */
|
/* Blend entities */
|
||||||
u64 num_entities = min_u64(t0->entities_count, t1->entities_count);
|
u64 num_entities = min_u64(t0->entities_count, t1->entities_count);
|
||||||
for (u64 i = 0; i < num_entities; ++i) {
|
for (u64 i = 0; i < num_entities; ++i) {
|
||||||
struct entity *e = &tick->entities[i];
|
|
||||||
struct entity *e0 = &t0->entities[i];
|
struct entity *e0 = &t0->entities[i];
|
||||||
struct entity *e1 = &t1->entities[i];
|
struct entity *e1 = &t1->entities[i];
|
||||||
|
struct entity *e = &L.world.entities[i];
|
||||||
if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) {
|
if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) {
|
||||||
e->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend);
|
e->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend);
|
||||||
e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend);
|
e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend);
|
||||||
@ -525,8 +527,8 @@ INTERNAL void user_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Find camera */
|
/* Find camera */
|
||||||
for (u64 entity_index = 0; entity_index < tick->entities_count; ++entity_index) {
|
for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) {
|
||||||
struct entity *ent = &tick->entities[entity_index];
|
struct entity *ent = &L.world.entities[entity_index];
|
||||||
if (!ent->valid) continue;
|
if (!ent->valid) continue;
|
||||||
|
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_CAMERA) && ent->camera_active && !L.debug_camera) {
|
if (entity_has_prop(ent, ENTITY_PROP_CAMERA) && ent->camera_active && !L.debug_camera) {
|
||||||
@ -586,8 +588,8 @@ INTERNAL void user_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Iterate entities */
|
/* Iterate entities */
|
||||||
for (u64 entity_index = 0; entity_index < tick->entities_count; ++entity_index) {
|
for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) {
|
||||||
struct entity *ent = &tick->entities[entity_index];
|
struct entity *ent = &L.world.entities[entity_index];
|
||||||
if (!ent->valid) continue;
|
if (!ent->valid) continue;
|
||||||
|
|
||||||
b32 is_camera = entity_has_prop(ent, ENTITY_PROP_CAMERA);
|
b32 is_camera = entity_has_prop(ent, ENTITY_PROP_CAMERA);
|
||||||
@ -613,7 +615,7 @@ INTERNAL void user_update(void)
|
|||||||
struct sheet_frame frame = sheet_get_frame(sheet, tag.start);
|
struct sheet_frame frame = sheet_get_frame(sheet, tag.start);
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
|
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
|
||||||
b32 looping = ent->animation_looping;
|
b32 looping = ent->animation_looping;
|
||||||
f64 time_in_anim = tick->time - ent->animation_start_time;
|
f64 time_in_anim = L.world.time - ent->animation_start_time;
|
||||||
|
|
||||||
u64 frame_index = tag.start;
|
u64 frame_index = tag.start;
|
||||||
while (time_in_anim > 0) {
|
while (time_in_anim > 0) {
|
||||||
@ -769,7 +771,7 @@ INTERNAL void user_update(void)
|
|||||||
u64 canvases_count = 0;
|
u64 canvases_count = 0;
|
||||||
{
|
{
|
||||||
/* Only render world if not on first frame */
|
/* Only render world if not on first frame */
|
||||||
if (t0->id > 0 && t1->id > 0) {
|
if (t0->tick_id > 0 && t1->tick_id > 0) {
|
||||||
*arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas;
|
*arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas;
|
||||||
++canvases_count;
|
++canvases_count;
|
||||||
}
|
}
|
||||||
@ -810,6 +812,10 @@ void user_startup(struct sys_window *window)
|
|||||||
L.sys_events_mutex = sys_mutex_alloc();
|
L.sys_events_mutex = sys_mutex_alloc();
|
||||||
L.sys_events_arena = arena_alloc(GIGABYTE(64));
|
L.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
|
world_alloc(&L.blend_ticks[0]);
|
||||||
|
world_alloc(&L.blend_ticks[1]);
|
||||||
|
world_alloc(&L.world);
|
||||||
|
|
||||||
L.world_canvas = renderer_canvas_alloc();
|
L.world_canvas = renderer_canvas_alloc();
|
||||||
L.world_view = (struct view) {
|
L.world_view = (struct view) {
|
||||||
.px_per_unit = PIXELS_PER_UNIT,
|
.px_per_unit = PIXELS_PER_UNIT,
|
||||||
|
|||||||
98
src/world.c
Normal file
98
src/world.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "world.h"
|
||||||
|
#include "entity.h"
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* World allocation
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void world_alloc(struct world *world)
|
||||||
|
{
|
||||||
|
MEMZERO_STRUCT(world);
|
||||||
|
world->entities_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
world->entities = (struct entity *)world->entities_arena.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
void world_copy_replace(struct world *dest, struct world *src)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
|
||||||
|
/* Copy non-arena fields */
|
||||||
|
MEMCPY(dest, src, FIELD_OFFSETOF(struct world, _copy_barrier));
|
||||||
|
|
||||||
|
/* Copy arena contents */
|
||||||
|
arena_copy_replace(&dest->entities_arena, &src->entities_arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Entity allocation
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct entity *world_alloc_entity(struct world *world)
|
||||||
|
{
|
||||||
|
struct entity *entity = NULL;
|
||||||
|
if (world->first_free_entity.gen) {
|
||||||
|
/* Reuse from free list */
|
||||||
|
entity = world_entity_from_handle(world, world->first_free_entity);
|
||||||
|
world->first_free_entity = entity->next_free;
|
||||||
|
*entity = (struct entity) {
|
||||||
|
.handle = entity->handle
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
/* Make new */
|
||||||
|
u64 idx = world->entities_count++;
|
||||||
|
entity = &world->entities[idx];
|
||||||
|
*entity = (struct entity) {
|
||||||
|
.handle = { .gen = 1, .idx = idx }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void world_release_entity(struct world *world, struct entity *entity)
|
||||||
|
{
|
||||||
|
struct entity_handle next_free = world->first_free_entity;
|
||||||
|
world->first_free_entity = entity->handle;
|
||||||
|
*entity = (struct entity) {
|
||||||
|
.handle.gen = entity->handle.gen + 1,
|
||||||
|
.next_free = next_free
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Queries
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */
|
||||||
|
struct entity *world_entity_from_handle(struct world *world, struct entity_handle handle)
|
||||||
|
{
|
||||||
|
if (handle.idx < world->entities_count) {
|
||||||
|
struct entity *entities = world->entities;
|
||||||
|
struct entity *entity = &entities[handle.idx];
|
||||||
|
if (entity->handle.gen == handle.gen) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entity_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Tree
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void world_link_entities(struct world *world, struct entity *parent, struct entity *child)
|
||||||
|
{
|
||||||
|
struct entity *first_child = world_entity_from_handle(world, parent->first);
|
||||||
|
struct entity *last_child = world_entity_from_handle(world, parent->last);
|
||||||
|
|
||||||
|
child->prev = last_child->handle;
|
||||||
|
child->parent = parent->handle;
|
||||||
|
|
||||||
|
if (last_child->valid) {
|
||||||
|
last_child->next = child->handle;
|
||||||
|
}
|
||||||
|
parent->last = child->handle;
|
||||||
|
|
||||||
|
if (!first_child->valid) {
|
||||||
|
parent->first = child->handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/world.h
Normal file
39
src/world.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef WORLD_H
|
||||||
|
#define WORLD_H
|
||||||
|
|
||||||
|
#include "entity.h"
|
||||||
|
|
||||||
|
struct world {
|
||||||
|
u64 tick_id; /* Starts at 1 */
|
||||||
|
sys_timestamp_t tick_published_ts;
|
||||||
|
|
||||||
|
f64 dt;
|
||||||
|
f64 time;
|
||||||
|
|
||||||
|
struct v2 player_move_dir; /* Player movement direction */
|
||||||
|
struct v2 player_focus; /* Mouse cursor pos in world coordinates */
|
||||||
|
|
||||||
|
u64 entities_count; /* Includes 'released' & inactive entities */
|
||||||
|
struct entity_handle first_free_entity;
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* Everything after this field is not automatically coppied
|
||||||
|
* in world_copy_replace */
|
||||||
|
u8 _copy_barrier;
|
||||||
|
|
||||||
|
/* Arenas */
|
||||||
|
struct arena entities_arena;
|
||||||
|
struct entity *entities;
|
||||||
|
};
|
||||||
|
|
||||||
|
void world_alloc(struct world *world);
|
||||||
|
void world_copy_replace(struct world *dest, struct world *src);
|
||||||
|
|
||||||
|
struct entity *world_alloc_entity(struct world *world);
|
||||||
|
void world_release_entity(struct world *world, struct entity *entity);
|
||||||
|
struct entity *world_entity_from_handle(struct world *world, struct entity_handle handle);
|
||||||
|
|
||||||
|
void world_link_entities(struct world *world, struct entity *parent, struct entity *child);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user