entity trees, xform storing & lerping

This commit is contained in:
jacob 2024-03-04 18:39:10 -06:00
parent e9dc6b2854
commit bb9b374471
8 changed files with 453 additions and 103 deletions

View File

@ -174,8 +174,9 @@ if (RTC)
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)") message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)")
endif() endif()
# NOTE: Adress sanitizer is disable for now because for some reason it's hiding local variables in debug mode. # NOTE: Adress sanitizer is disable for now because for some reason it's hiding local variables in debug mode.
set(COMPILER_FLAGS "${COMPILER_FLAGS} -DRTC=1")
# set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=undefined -fno-sanitize=alignment -DRTC=1") # set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=undefined -fno-sanitize=alignment -DRTC=1")
set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=undefined -fsanitize-trap=undefined -fno-sanitize=alignment -DRTC=1") # set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=undefined -fsanitize-trap=undefined -fno-sanitize=alignment -DRTC=1")
# set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -DRTC=1") # set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -DRTC=1")
endif() endif()

View File

@ -216,6 +216,7 @@ extern "C" {
#define RGB_F(r, g, b) RGBA_F(r, g, b, 1.f) #define RGB_F(r, g, b) RGBA_F(r, g, b, 1.f)
#define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF) #define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF)
#define COLOR_BLACK RGB(0, 0, 0 )
#define COLOR_RED RGB(0xFF, 0, 0 ) #define COLOR_RED RGB(0xFF, 0, 0 )
#define COLOR_GREEN RGB(0, 0xFF, 0 ) #define COLOR_GREEN RGB(0, 0xFF, 0 )
#define COLOR_BLUE RGB(0, 0, 0xFF) #define COLOR_BLUE RGB(0, 0, 0xFF)
@ -406,17 +407,11 @@ struct v4_array {
}; };
struct mat3x3 { struct mat3x3 {
union { f32 e[3][3];
f32 e[3][3];
struct v3 rows[3];
};
}; };
struct mat4x4 { struct mat4x4 {
union { f32 e[4][4];
f32 e[4][4];
struct v4 rows[4];
};
}; };
#define RECT(x, y, width, height) (struct rect) { (x), (y), (width), (height) } #define RECT(x, y, width, height) (struct rect) { (x), (y), (width), (height) }

View File

@ -31,25 +31,33 @@ enum entity_prop {
struct entity { struct entity {
b32 active; b32 active;
u64 gen; struct entity_handle handle;
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 *next_free;
/* TODO: Entity tree */ /* Tree */
struct entity_handle parent;
struct entity_handle next;
struct entity_handle prev;
struct entity_handle first;
struct entity_handle last;
/* ====================================================================== */ /* ====================================================================== */
/* Translation, rotation, scale in relation to parent entity */ /* Translation, rotation, scale in relation to parent entity */
struct trs rel_trs; struct trs rel_trs;
struct mat3x3 world_xform; /* Calculated post-physics */
/* ====================================================================== */ /* ====================================================================== */
/* Sprite */ /* Sprite */
struct string sprite_name; struct string sprite_name;
struct trs sprite_trs; struct trs sprite_trs;
struct v2 sprite_pivot; /* Normalized. <0, 0> is center, <1, 1> is bottom right corner, etc. */ struct v2 sprite_pivot_norm; /* Pivot x & y are each normalized about sprite dimensions. <0, 0> is center, <1, 1> is bottom right corner, etc. */
u32 sprite_tint;
/* ====================================================================== */ /* ====================================================================== */
/* Animation */ /* Animation */
@ -82,6 +90,15 @@ struct entity {
}; };
/* ========================== *
* Handle
* ========================== */
INLINE b32 entity_is_valid(struct entity_handle eh)
{
return eh.gen != 0;
}
/* ========================== * /* ========================== *
* Prop * Prop
* ========================== */ * ========================== */

View File

@ -17,8 +17,6 @@ GLOBAL struct {
struct sys_thread game_thread; struct sys_thread game_thread;
f64 timescale; f64 timescale;
f64 dt;
f64 time;
struct entity *free_entity_head; struct entity *free_entity_head;
struct tick tick; struct tick tick;
@ -43,16 +41,17 @@ INTERNAL struct entity *entity_alloc(void)
entity = L.free_entity_head; entity = L.free_entity_head;
L.free_entity_head = entity->next_free; L.free_entity_head = entity->next_free;
*entity = (struct entity) { *entity = (struct entity) {
.gen = entity->gen .handle = entity->handle
}; };
} else { } else {
/* Make new */ /* Make new */
if (L.tick.entities_count >= MAX_ENTITIES) { if (L.tick.entities_count >= MAX_ENTITIES) {
sys_panic(STR("MAX_ENTITIES reached")); sys_panic(STR("MAX_ENTITIES reached"));
} }
entity = &L.tick.entities[L.tick.entities_count++]; u64 idx = L.tick.entities_count++;
entity = &L.tick.entities[idx];
*entity = (struct entity) { *entity = (struct entity) {
.gen = 1 .handle = { .gen = 1, .idx = idx }
}; };
} }
return entity; return entity;
@ -69,6 +68,31 @@ INTERNAL void entity_release(struct entity *entity)
} }
#endif #endif
INTERNAL struct entity *entity_from_handle(struct entity_handle eh)
{
if (eh.idx < ARRAY_COUNT(L.tick.entities)) {
struct entity *entity = &L.tick.entities[eh.idx];
if (entity->handle.gen == eh.gen) {
return entity;
}
}
return NULL;
}
INTERNAL void entity_tree_attach(struct entity *parent, struct entity *child)
{
if (entity_is_valid(parent->last)) {
struct entity *last_child = entity_from_handle(parent->last);
last_child->next = child->handle;
child->prev = last_child->handle;
}
parent->last = child->handle;
if (!entity_is_valid(parent->first)) {
parent->first = child->handle;
}
child->parent = parent->handle;
}
/* ========================== * /* ========================== *
* Game cmd * Game cmd
* ========================== */ * ========================== */
@ -130,8 +154,8 @@ INTERNAL void game_update(void)
#endif #endif
++L.tick.id; ++L.tick.id;
L.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale); L.tick.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale);
L.time += L.dt; L.tick.time += L.tick.dt;
/* TODO: remove this (testing) */ /* TODO: remove this (testing) */
/* Initialize entities */ /* Initialize entities */
@ -140,28 +164,78 @@ INTERNAL void game_update(void)
run = 1; run = 1;
/* Player ent */ /* Player ent */
struct entity *player_ent;
{ {
struct string sprite_name = STR("res/graphics/timmy.ase"); struct string sprite_name = STR("res/graphics/timmy.ase");
struct sheet *sheet = sheet_load(sprite_name); struct sheet *sheet = sheet_load(sprite_name);
f32 meters_width = sheet->image_size.x / PIXELS_PER_UNIT; f32 meters_width = sheet->image_size.x / PIXELS_PER_UNIT;
f32 meters_height = sheet->image_size.y / PIXELS_PER_UNIT; f32 meters_height = sheet->image_size.y / PIXELS_PER_UNIT;
struct v2 sprite_size = V2(meters_width, meters_height);
struct v2 pos = V2(0, 0); struct v2 pos = V2(0, 0);
//f32 rot = PI / 2;
f32 rot = 0;
struct v2 size = V2(meters_width, meters_height);
struct entity *e = entity_alloc(); struct entity *e = entity_alloc();
e->active = true; e->active = true;
e->rel_trs = TRS(.t = pos, .r = rot, .s = V2(2, 2)); e->rel_trs = TRS(.t = pos, .r = PI / 4, .s = V2(2, 1));
e->sprite_name = sprite_name; e->sprite_name = sprite_name;
e->sprite_trs = TRS(.t = V2(1, 0), .r = 0, .s = size); e->sprite_trs = TRS(.s = sprite_size);
e->sprite_pivot = V2(1, 0); e->sprite_pivot_norm = V2(0, 0.65);
e->sprite_tint = COLOR_WHITE;
//entity_enable_prop(e, ENTITY_PROP_TEST);
entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE);
player_ent = e;
}
/* Child ent */
struct entity *child_ent;
{
struct string sprite_name = STR("res/graphics/timmy.ase");
struct v2 sprite_size = V2(1, 1);
struct v2 pos = V2(2, 2);
struct entity *e = entity_alloc();
e->active = true;
e->rel_trs = TRS(.t = pos, .r = 0, .s = V2(1, 1));
e->sprite_name = sprite_name;
e->sprite_trs = TRS(.s = sprite_size);
//e->sprite_pivot_norm = V2(0, 0.65);
e->sprite_tint = RGBA_F(1, 0, 0, 0.5);
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);
child_ent = e;
}
/* Child ent 2 */
{
struct string sprite_name = STR("res/graphics/timmy.ase");
struct v2 sprite_size = V2(1, 1);
struct v2 pos = V2(0, -1);
struct entity *e = entity_alloc();
e->active = true;
e->rel_trs = TRS(.t = pos, .r = PI / 2, .s = V2(1, 1));
e->sprite_name = sprite_name;
e->sprite_trs = TRS(.s = sprite_size);
//e->sprite_pivot_norm = V2(0, 0.65);
e->sprite_tint = RGBA_F(0, 1, 0, 0.2);
//entity_enable_prop(e, ENTITY_PROP_TEST);
//entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE);
entity_tree_attach(child_ent, e);
} }
} }
@ -186,7 +260,7 @@ INTERNAL void game_update(void)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* ========================== * /* ========================== *
* Update entities * 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 < ARRAY_COUNT(L.tick.entities); ++entity_index) {
@ -213,7 +287,7 @@ INTERNAL void game_update(void)
struct sheet_tag tag = sheet_get_tag(sheet, ent->animation_name); struct sheet_tag tag = sheet_get_tag(sheet, ent->animation_name);
if (ent->animation_start_gen == ent->animation_gen) { if (ent->animation_start_gen == ent->animation_gen) {
ent->animation_time_in_frame += L.dt; ent->animation_time_in_frame += L.tick.dt;
} else { } else {
ent->animation_frame = sheet_get_frame(sheet, tag.start); ent->animation_frame = sheet_get_frame(sheet, tag.start);
ent->animation_start_gen = ent->animation_gen; ent->animation_start_gen = ent->animation_gen;
@ -236,9 +310,37 @@ INTERNAL void game_update(void)
} }
} }
break_animation: break_animation:
(UNUSED)0; /* Disable label at end of compound statement warning */
/* ENTITY_PROP_TEST */
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
f32 t = ((f32)L.tick.time);
f32 r = t * 0.5f;
f32 s = 1 + (math_abs_f32(math_sin(t * 5)) * 3);
(UNUSED)r;
(UNUSED)s;
ent->rel_trs.r = ent->test_start_rel_trs.r + (r * -5.f);
//ent->sprite_trs.r = ent->test_start_sprite_trs.r + (-r * 3.0f);
//ent->rel_trs.s = v2_mul(ent->test_start_rel_trs.s, s);
ent->rel_trs.s.x = ent->test_start_rel_trs.s.x * s;
//ent->sprite_trs.s = v2_mul(ent->test_start_sprite_trs.s, s);
}
}
/* ========================== *
* Update entity physics
* ========================== */
for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) {
struct entity *ent = &L.tick.entities[entity_index];
if (!ent->active) continue;
if (entity_is_valid(ent->parent)) continue; /* Only update parent entities */
/* ========================== * /* ========================== *
* Update position * Update position from mouse
* ========================== */ * ========================== */
/* ENTITY_PROP_TEST_FOLLOW_MOUSE */ /* ENTITY_PROP_TEST_FOLLOW_MOUSE */
@ -247,28 +349,52 @@ break_animation:
ent->test_start_rel_trs.t = L.tick.player_focus; ent->test_start_rel_trs.t = L.tick.player_focus;
} }
/* ENTITY_PROP_TEST */ /* ========================== *
if (entity_has_prop(ent, ENTITY_PROP_TEST)) { * Calculate tree trs
f32 t = ((f32)L.time); * ========================== */
f32 r = t * 0.5f; ent->world_xform = mat3x3_from_trs(ent->rel_trs);
ent->rel_trs.r = ent->test_start_rel_trs.r + (r * 0.5f); struct entity *child = entity_from_handle(ent->first);
ent->sprite_trs.r = ent->test_start_sprite_trs.r + (r * 3.0f); struct mat3x3 parent_xform = ent->world_xform;
while (child) {
child->world_xform = mat3x3_trs(parent_xform, child->rel_trs);
/* Depth first iteration */
f32 s = 1 + (math_abs_f32(math_sin(t * 5)) * 0.25f); if (child->first.gen) {
ent->rel_trs.s = v2_mul(ent->test_start_rel_trs.s, s); /* Next child */
ent->sprite_trs.s = v2_mul(ent->test_start_sprite_trs.s, s * 1.5f); parent_xform = child->world_xform;
child = entity_from_handle(child->first);
} else if (child->next.gen) {
/* Next sibling */
child = entity_from_handle(child->next);
} else if (child->parent.gen && entity_from_handle(child->parent)->next.gen) {
/* Next parent sibling */
struct entity *parent = entity_from_handle(child->parent);
struct entity *grandparent = entity_from_handle(parent->parent);
parent_xform = grandparent->world_xform;
child = entity_from_handle(parent->next);
} else {
child = NULL;
}
} }
}
/* ========================== *
* Update entities post-physics
* ========================== */
for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) {
struct entity *ent = &L.tick.entities[entity_index];
if (!ent->active) continue;
/* ========================== * /* ========================== *
* Update sound emitter * Update sound emitter
* ========================== */ * ========================== */
if (entity_has_prop(ent, ENTITY_PROP_TEST_SOUND_EMITTER)) { if (entity_has_prop(ent, ENTITY_PROP_TEST_SOUND_EMITTER)) {
struct mixer_desc desc = ent->sound_desc; struct mixer_desc desc = ent->sound_desc;
desc.speed = L.timescale; desc.speed = L.timescale;
desc.pos = ent->rel_trs.t; desc.pos = mat3x3_get_pos(ent->world_xform);
struct sound *sound = sound_load_async(ent->sound_name, 0); struct sound *sound = sound_load_async(ent->sound_name, 0);
b32 played = ent->sound_handle.gen != 0; b32 played = ent->sound_handle.gen != 0;
if (sound) { if (sound) {

View File

@ -6,6 +6,9 @@
#define PI ((f32)3.14159265358979323846) #define PI ((f32)3.14159265358979323846)
#define TAU ((f32)6.28318530717958647693) #define TAU ((f32)6.28318530717958647693)
INLINE struct trs trs_from_mat3x3(struct mat3x3 m);
INLINE struct trs trs_lerp(struct trs a, struct trs b, f32 t);
/* ========================== * /* ========================== *
* Rounding * Rounding
* ========================== */ * ========================== */
@ -62,13 +65,15 @@ INLINE f64 math_abs_f64(f64 f)
INLINE i32 math_sign_f32(f32 f) INLINE i32 math_sign_f32(f32 f)
{ {
u32 bits = *(u32 *)&f; u32 bits = *(u32 *)&f;
return bits & ((u32)1 << 31); i32 sign_bit = bits & ((u32)1 << 31);
return 1 + (sign_bit * -2);
} }
INLINE i32 math_sign_f64(f64 f) INLINE i32 math_sign_f64(f64 f)
{ {
u64 bits = *(u64 *)&f; u64 bits = *(u64 *)&f;
return bits & ((u64)1 << 31); i32 sign_bit = bits & ((u64)1 << 31);
return 1 + (sign_bit * -2);
} }
/* ========================== * /* ========================== *
@ -186,6 +191,17 @@ INLINE f32 math_lerp_f32(f32 val0, f32 val1, f32 t)
return val0 + ((val1 - val0) * t); return val0 + ((val1 - val0) * t);
} }
INLINE f64 math_lerp_f64(f64 val0, f64 val1, f64 t)
{
return val0 + ((val1 - val0) * t);
}
INLINE f32 math_lerp_angle(f32 a, f32 b, f32 t) {
f32 diff = math_mod_f32(b - a, TAU);
diff = math_mod_f32(2.0f * diff, TAU) - diff;
return a + diff * t;
}
/* ========================== * /* ========================== *
* Trig * Trig
* ========================== */ * ========================== */
@ -202,26 +218,55 @@ INLINE f32 math_lerp_f32(f32 val0, f32 val1, f32 t)
INLINE f32 math_sin(f32 x) INLINE f32 math_sin(f32 x)
{ {
const f32 c = 0.225; const f32 c = 0.225;
x -= (TAU * (f32)math_floor_f32(x / TAU)); /* [0, TAU] */
/* Wrap to range [0, TAU] */ x += (TAU * (x < -PI)) - (TAU * (x > PI)); /* [-PI, PI] */
x -= (TAU * (f32)math_floor_f32(x / TAU));
/* Wrap to range [-PI, PI] */
x += (TAU * (x < -PI)) - (TAU * (x > PI));
/* Parabola */
f32 y = (4.0f/PI) * x + (-4.0f/(PI*PI)) * x * math_abs_f32(x); f32 y = (4.0f/PI) * x + (-4.0f/(PI*PI)) * x * math_abs_f32(x);
/* Precision adjustment */
y = c * (y * math_abs_f32(y) - y) + y; y = c * (y * math_abs_f32(y) - y) + y;
return y; return y;
} }
INLINE f32 math_cos(f32 x) INLINE f32 math_cos(f32 x)
{ {
return math_sin(x + (TAU / 4.0f)); return math_sin(x + (PI / 2.0f));
}
/* https://mazzo.li/posts/vectorized-atan2.html */
INLINE f32 math_atan2(f32 x, f32 y) {
const f32 a1 = 0.99997726f;
const f32 a3 = -0.33262347f;
const f32 a5 = 0.19354346f;
const f32 a7 = -0.11643287f;
const f32 a9 = 0.05265332f;
const f32 a11 = -0.01172120f;
/* Ensure input is in [-1, +1] */
b32 swap = math_abs_f32(x) < math_abs_f32(y);
f32 s = (swap ? x : y) / (swap ? y : x);
/* Approximate atan */
f32 s_sq = s*s;
f32 res = s * (a1 + s_sq * (a3 + s_sq * (a5 + s_sq * (a7 + s_sq * (a9 + s_sq * a11)))));
res = swap ? (s >= 0.0f ? (PI / 2.f) : -(PI / 2.f)) - res : res;
/* Adjust quadrants */
if (x >= 0.0f && y >= 0.0f) {} /* 1st quadrant */
else if (x < 0.0f && y >= 0.0f) { res = PI + res; } /* 2nd quadrant */
else if (x < 0.0f && y < 0.0f) { res = -PI + res; } /* 3rd quadrant */
else if (x >= 0.0f && y < 0.0f) {} /* 4th quadrant */
return res;
}
INLINE f32 math_asin(f32 x)
{
/* TODO: Dedicated arcsin approximation */
return (PI / 2.0f) - math_atan2(x, math_sqrt(1.0f - (x*x)));
}
INLINE f32 math_acos(f32 x)
{
/* TODO: Dedicated arccos approximation */
return math_atan2(x, math_sqrt(1.0f - (x*x)));
} }
/* ========================== * /* ========================== *
@ -238,6 +283,17 @@ INLINE struct v2 v2_mul_v2(struct v2 a, struct v2 b)
return V2(a.x * b.x, a.y * b.y); return V2(a.x * b.x, a.y * b.y);
} }
INLINE struct v2 v2_div(struct v2 a, f32 s)
{
f32 d = 1 / s;
return V2(a.x * d, a.y * d);
}
INLINE struct v2 v2_div_v2(struct v2 a, struct v2 b)
{
return V2(a.x * (1 / b.x), a.y * (1 / b.y));
}
INLINE struct v2 v2_neg(struct v2 a) INLINE struct v2 v2_neg(struct v2 a)
{ {
return V2(-a.x, -a.y); return V2(-a.x, -a.y);
@ -282,7 +338,7 @@ INLINE f32 v2_dot(struct v2 a, struct v2 b)
return a.x * b.x + a.y * b.y; return a.x * b.x + a.y * b.y;
} }
INLINE f32 v2_det(struct v2 a, struct v2 b) INLINE f32 v2_wedge(struct v2 a, struct v2 b)
{ {
return a.x * b.y - a.y * b.x; return a.x * b.y - a.y * b.x;
} }
@ -502,6 +558,49 @@ INLINE struct v2 mat3x3_get_pos(struct mat3x3 m)
return V2(m.e[2][0], m.e[2][1]); return V2(m.e[2][0], m.e[2][1]);
} }
INLINE f32 mat3x3_get_determinant(struct mat3x3 m)
{
return m.e[0][0] * m.e[1][1] - m.e[0][1] * m.e[1][0];
}
INLINE f32 mat3x3_get_rot(struct mat3x3 m)
{
return math_atan2(m.e[0][0], m.e[0][1]);
}
INLINE struct v2 mat3x3_get_scale(struct mat3x3 m)
{
f32 det_sign = math_sign_f32(mat3x3_get_determinant(m));
struct v2 bx = V2(m.e[0][0], m.e[0][1]);
struct v2 by = V2(m.e[1][0], m.e[1][1]);
return V2(v2_len(bx), det_sign * v2_len(by));
}
INLINE f32 mat3x3_get_skew(struct mat3x3 m)
{
f32 det = mat3x3_get_determinant(m);
i32 det_sign = math_sign_f32(det);
struct v2 bx_norm = v2_norm(V2(m.e[0][0], m.e[0][1]));
struct v2 by_norm = v2_norm(V2(m.e[1][0], m.e[1][1]));
by_norm = v2_mul(by_norm, det_sign);
f32 dot = v2_dot(bx_norm, by_norm);
return math_acos(dot) - (PI * 0.5f);
}
INLINE struct mat3x3 mat3x3_lerp(struct mat3x3 a, struct mat3x3 b, f32 t)
{
struct trs trs_a = trs_from_mat3x3(a);
struct trs trs_b = trs_from_mat3x3(b);
struct trs trs = trs_lerp(trs_a, trs_b, t);
return mat3x3_from_trs(trs);
}
/* ========================== * /* ========================== *
* Mat4x4 * Mat4x4
* ========================== */ * ========================== */
@ -568,11 +667,30 @@ INLINE struct trs trs_lerp(struct trs a, struct trs b, f32 t)
{ {
struct trs res; struct trs res;
res.t = v2_lerp(a.t, b.t, t); res.t = v2_lerp(a.t, b.t, t);
res.r = math_lerp_f32(a.r, b.r, t); res.r = math_lerp_angle(a.r, b.r, t);
res.s = v2_lerp(a.s, b.s, t); res.s = v2_lerp(a.s, b.s, t);
return res; return res;
} }
INLINE struct trs trs_from_mat3x3(struct mat3x3 m)
{
struct trs trs = { 0 };
trs.t = mat3x3_get_pos(m);
trs.r = mat3x3_get_rot(m);
trs.s = mat3x3_get_scale(m);
trs.t = V2(m.e[2][0], m.e[2][1]);
struct v2 bx = V2(m.e[0][0], m.e[0][1]);
struct v2 by = V2(m.e[1][0], m.e[1][1]);
trs.s = V2(v2_len(bx), v2_len(by));
trs.r = math_atan2(bx.x, bx.y);
return trs;
}
/* ========================== * /* ========================== *
* Quad * Quad
* ========================== */ * ========================== */
@ -623,15 +741,11 @@ INLINE struct quad quad_scale(struct quad q, f32 s)
INLINE struct quad quad_mul_mat3x3(struct quad quad, struct mat3x3 m) INLINE struct quad quad_mul_mat3x3(struct quad quad, struct mat3x3 m)
{ {
struct v3 p1_v3 = mat3x3_mul_v3(m, V3(quad.p1.x, quad.p1.y, 1));
struct v3 p2_v3 = mat3x3_mul_v3(m, V3(quad.p2.x, quad.p2.y, 1));
struct v3 p3_v3 = mat3x3_mul_v3(m, V3(quad.p3.x, quad.p3.y, 1));
struct v3 p4_v3 = mat3x3_mul_v3(m, V3(quad.p4.x, quad.p4.y, 1));
return (struct quad) { return (struct quad) {
V2(p1_v3.x, p1_v3.y), mat3x3_mul_v2(m, quad.p1),
V2(p2_v3.x, p2_v3.y), mat3x3_mul_v2(m, quad.p2),
V2(p3_v3.x, p3_v3.y), mat3x3_mul_v2(m, quad.p3),
V2(p4_v3.x, p4_v3.y) mat3x3_mul_v2(m, quad.p4)
}; };
} }

View File

@ -434,7 +434,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count)
/* Calculate pan */ /* Calculate pan */
f32 pan_start = effect_data->spatial_pan; f32 pan_start = effect_data->spatial_pan;
f32 pan_end = v2_det(listener_dir, sound_rel_dir) * pan_scale; f32 pan_end = v2_wedge(listener_dir, sound_rel_dir) * pan_scale;
effect_data->spatial_pan = pan_end; effect_data->spatial_pan = pan_end;
/* Spatialize samples */ /* Spatialize samples */

View File

@ -7,6 +7,9 @@ struct tick {
u64 id; /* Starts at 1 */ u64 id; /* Starts at 1 */
sys_timestamp_t published_ts; sys_timestamp_t published_ts;
f64 dt;
f64 time;
struct v2 player_focus; /* Mouse cursor pos in world coordinates */ struct v2 player_focus; /* Mouse cursor pos in world coordinates */
u64 entities_count; /* Includes 'released' & non-active entities */ u64 entities_count; /* Includes 'released' & non-active entities */

View File

@ -198,8 +198,7 @@ INTERNAL struct mat3x3 view_get_xform(struct view view)
INTERNAL struct v2 view_inverse_point(struct view view, struct v2 p) INTERNAL struct v2 view_inverse_point(struct view view, struct v2 p)
{ {
struct mat3x3 mtx_inverse = mat3x3_inverse(view_get_xform(view)); struct mat3x3 mtx_inverse = mat3x3_inverse(view_get_xform(view));
struct v3 p_inverse = mat3x3_mul_v3(mtx_inverse, V3(p.x, p.y, 1)); return mat3x3_mul_v2(mtx_inverse, p);
return V2(p_inverse.x, p_inverse.y);
} }
INTERNAL struct v2 view_mouse_pos(struct view view) INTERNAL struct v2 view_mouse_pos(struct view view)
@ -207,6 +206,10 @@ INTERNAL struct v2 view_mouse_pos(struct view view)
return view_inverse_point(view, L.screen_mouse); return view_inverse_point(view, L.screen_mouse);
} }
/* ========================== *
* Update
* ========================== */
/* TODO: remove this (testing) */ /* TODO: remove this (testing) */
INTERNAL void debug_draw_xform(struct mat3x3 mtx) INTERNAL void debug_draw_xform(struct mat3x3 mtx)
{ {
@ -217,18 +220,16 @@ INTERNAL void debug_draw_xform(struct mat3x3 mtx)
struct v2 pos = mat3x3_get_pos(mtx); struct v2 pos = mat3x3_get_pos(mtx);
struct v2 x_ray = mat3x3_get_right(mtx); struct v2 x_ray = mat3x3_get_right(mtx);
struct v2 y_ray = mat3x3_get_down(mtx); struct v2 y_ray = mat3x3_get_up(mtx);
struct quad quad = quad_mul_mat3x3(quad_scale(QUAD_UNIT_SQUARE, 0.075), mtx);
struct quad quad = quad_from_rect(RECT(0, 0, 1, -1));
quad = quad_mul_mat3x3(quad_scale(quad, 0.075), mtx);
draw_solid_ray(L.world_canvas, pos, x_ray, thickness, color_x); draw_solid_ray(L.world_canvas, pos, x_ray, thickness, color_x);
draw_solid_ray(L.world_canvas, pos, y_ray, thickness, color_y); draw_solid_ray(L.world_canvas, pos, y_ray, thickness, color_y);
draw_solid_quad(L.world_canvas, quad, color); draw_solid_quad(L.world_canvas, quad, color);
} }
/* ========================== *
* Update
* ========================== */
INTERNAL void user_update(void) INTERNAL void user_update(void)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
@ -360,17 +361,32 @@ INTERNAL void user_update(void)
tick_cpy(tick, t1); tick_cpy(tick, t1);
/* Blend entities */
#if 1 #if 1
/* Blend time */
tick->time = math_lerp_f64(t0->time, t1->time, (f64)blend);
/* 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 *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];
if (e0->gen == e1->gen && e0->continuity_gen == e1->continuity_gen) { if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) {
e->rel_trs = trs_lerp(e0->rel_trs, e1->rel_trs, blend); e->rel_trs = trs_lerp(e0->rel_trs, e1->rel_trs, blend);
e->sprite_trs = trs_lerp(e0->sprite_trs, e1->sprite_trs, blend); e->sprite_trs = trs_lerp(e0->sprite_trs, e1->sprite_trs, blend);
e->sprite_pivot = v2_lerp(e0->sprite_pivot, e1->sprite_pivot, blend); e->sprite_pivot_norm = v2_lerp(e0->sprite_pivot_norm, e1->sprite_pivot_norm, blend);
if (e->handle.idx == 0) {
struct mat3x3 world_xform_test = mat3x3_lerp(e0->world_xform, e1->world_xform, blend);
(UNUSED)world_xform_test;
//if (world_xform_test.e[1][0] != e0->world_xform.e[1][0] || world_xform_test.e[1][1] != e0->world_xform.e[1][1]) {
// DEBUGBREAK;
//}
//DEBUGBREAK;
}
e->world_xform = mat3x3_lerp(e0->world_xform, e1->world_xform, blend);
} }
} }
#else #else
@ -388,10 +404,10 @@ INTERNAL void user_update(void)
u32 x_color = RGBA(0x3f, 0, 0, 0xFF); u32 x_color = RGBA(0x3f, 0, 0, 0xFF);
u32 y_color = RGBA(0, 0x3f, 0, 0xFF); u32 y_color = RGBA(0, 0x3f, 0, 0xFF);
i64 startx = -5; i64 startx = -10;
i64 starty = -5; i64 starty = -10;
i64 rows = 10; i64 rows = 20;
i64 cols = 10; i64 cols = 20;
/* Draw column lines */ /* Draw column lines */
@ -417,6 +433,71 @@ INTERNAL void user_update(void)
} }
} }
#if 1
/* Draw math functions */
{
u32 detail = 1000;
f32 thickness = 2.f / PIXELS_PER_UNIT / L.world_view.zoom;
/* sin */
{
u32 color = COLOR_RED;
f32 x_start = -10;
f32 x_end = 10;
struct v2_array v = { .points = arena_push_array(scratch.arena, struct v2, detail), .count = detail };
for (u32 i = 0; i < detail; ++i) {
f32 x = x_start + ((x_end - x_start) * (i / ((f32)detail - 1)));
f32 y = math_sin(x);
v.points[i] = V2(x, -y);
}
draw_solid_poly_line(L.world_canvas, v, false, thickness, color);
}
/* arcsin */
{
u32 color = COLOR_BLUE;
f32 x_start = -1;
f32 x_end = 1;
struct v2_array v = { .points = arena_push_array(scratch.arena, struct v2, detail), .count = detail };
for (u32 i = 0; i < detail; ++i) {
f32 x = x_start + ((x_end - x_start) * (i / ((f32)detail - 1)));
f32 y = math_asin(x);
v.points[i] = V2(x, -y);
}
draw_solid_poly_line(L.world_canvas, v, false, thickness, color);
}
/* arccos */
{
u32 color = COLOR_GREEN;
f32 x_start = -1;
f32 x_end = 1;
struct v2_array v = { .points = arena_push_array(scratch.arena, struct v2, detail), .count = detail };
for (u32 i = 0; i < detail; ++i) {
f32 x = x_start + ((x_end - x_start) * (i / ((f32)detail - 1)));
f32 y = math_acos(x);
v.points[i] = V2(x, -y);
}
draw_solid_poly_line(L.world_canvas, v, false, thickness, color);
}
/* arctan */
{
u32 color = COLOR_BLACK;
f32 x_start = -10;
f32 x_end = 10;
struct v2_array v = { .points = arena_push_array(scratch.arena, struct v2, detail), .count = detail };
for (u32 i = 0; i < detail; ++i) {
f32 x = x_start + ((x_end - x_start) * (i / ((f32)detail - 1)));
f32 y = math_atan2(1, x);
v.points[i] = V2(x, -y);
}
draw_solid_poly_line(L.world_canvas, v, false, thickness, color);
}
}
#endif
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@ -429,12 +510,6 @@ INTERNAL void user_update(void)
struct entity *ent = &tick->entities[entity_index]; struct entity *ent = &tick->entities[entity_index];
if (!ent->active) continue; if (!ent->active) continue;
/* Debug draw transform */
{
struct mat3x3 mtx = mat3x3_from_trs(ent->rel_trs);
debug_draw_xform(mtx);
}
/* Draw sprite */ /* Draw sprite */
if (ent->sprite_name.len > 0) { if (ent->sprite_name.len > 0) {
struct string tex_name = ent->sprite_name; struct string tex_name = ent->sprite_name;
@ -443,57 +518,70 @@ INTERNAL void user_update(void)
struct quad quad = QUAD_UNIT_SQUARE_CENTERED; struct quad quad = QUAD_UNIT_SQUARE_CENTERED;
/* TODO: Do this calculation in game loop */ /* TODO: Do this calculation in game loop */
#if 0
struct mat3x3 mtx; struct mat3x3 mtx;
{ {
/* Entity trs */ /* Entity trs */
mtx = mat3x3_from_trs(ent->rel_trs); mtx = mat3x3_from_trs(ent->world_trs);
/* Sprite trs & pivot */ /* Sprite trs & pivot */
struct v2 pivot = v2_mul_v2(ent->sprite_pivot, v2_mul(ent->sprite_trs.s, 0.5f)); struct v2 scale_div_2 = v2_mul(ent->sprite_trs.s, 0.5f);
struct v2 pivot = v2_mul_v2(ent->sprite_pivot_norm, scale_div_2);
mtx = mat3x3_trs_pivot_r(mtx, ent->sprite_trs, pivot); mtx = mat3x3_trs_pivot_r(mtx, ent->sprite_trs, pivot);
quad = quad_mul_mat3x3(quad, mtx); quad = quad_mul_mat3x3(quad, mtx);
} }
#else
struct mat3x3 mtx;
{
struct v2 scale_div_2 = v2_mul(ent->sprite_trs.s, 0.5f);
struct v2 pivot = v2_mul_v2(ent->sprite_pivot_norm, scale_div_2);
mtx = mat3x3_trs_pivot_r(ent->world_xform, ent->sprite_trs, pivot);
quad = quad_mul_mat3x3(quad, mtx);
}
#endif
struct texture *texture = texture_load_async(tex_name); struct texture *texture = texture_load_async(tex_name);
if (texture) { if (texture) {
u32 tint = ent->sprite_tint;
struct draw_texture_params params; struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint);
if (entity_has_prop(ent, ENTITY_PROP_SPRITE_ANIMATED)) { if (entity_has_prop(ent, ENTITY_PROP_SPRITE_ANIMATED)) {
struct sheet_frame frame = ent->animation_frame; struct sheet_frame frame = ent->animation_frame;
params.clip = frame.clip;
params = DRAW_TEXTURE_PARAMS(.texture = texture, .clip = frame.clip); params = DRAW_TEXTURE_PARAMS(.texture = texture, .clip = frame.clip);
} else {
params = DRAW_TEXTURE_PARAMS(.texture = texture);
} }
draw_texture_quad(L.world_canvas, params, quad); draw_texture_quad(L.world_canvas, params, quad);
} }
#if 0
/* Debug draw sprite quad */ /* Debug draw sprite quad */
{ {
f32 thickness = 2.f; f32 thickness = 2.f;
u32 color = RGBA_F(1, 1, 0, 0.25); u32 color = RGBA_F(1, 1, 0, 0.25);
draw_solid_quad_line(L.world_canvas, quad, (thickness / PIXELS_PER_UNIT / L.world_view.zoom), color); draw_solid_quad_line(L.world_canvas, quad, (thickness / PIXELS_PER_UNIT / L.world_view.zoom), color);
} }
#endif
#if 0
/* Debug draw sprite transform */ /* Debug draw sprite transform */
{ {
debug_draw_xform(mtx); debug_draw_xform(mtx);
} }
#endif
/* Debug draw sprite pivot */ /* Debug draw sprite pivot */
{ {
u32 color = RGBA_F(1, 0, 0, 1); u32 color = RGBA_F(1, 0, 0, 1);
struct mat3x3 mtx_pre_pivot = mat3x3_from_trs(ent->rel_trs); struct mat3x3 mtx_pre_pivot = ent->world_xform;
mtx_pre_pivot = mat3x3_trs(mtx_pre_pivot, ent->sprite_trs); mtx_pre_pivot = mat3x3_trs(mtx_pre_pivot, ent->sprite_trs);
struct v2 p = mat3x3_get_pos(mtx_pre_pivot); draw_solid_circle(L.world_canvas, mat3x3_get_pos(mtx_pre_pivot), 0.02, color, 20);
draw_solid_circle(L.world_canvas, p, 0.05, color, 20);
draw_solid_circle(L.world_canvas, mat3x3_get_pos(mtx_pre_pivot), 0.05, color, 20);
} }
/* Debug draw sprite name */ #if 0
/* Debug draw sprite info */
if (entity_has_prop(ent, ENTITY_PROP_TEST)) { if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
struct font *disp_font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f); struct font *disp_font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f);
@ -515,7 +603,7 @@ INTERNAL void user_update(void)
); );
struct string text = string_format(scratch.arena, fmt, struct string text = string_format(scratch.arena, fmt,
FMT_STR(disp_name), FMT_STR(disp_name),
FMT_FLOAT((f64)ent->rel_trs.r), FMT_FLOAT((f64)ent->world_trs.r),
FMT_FLOAT((f64)ent->sprite_trs.r) FMT_FLOAT((f64)ent->sprite_trs.r)
); );
@ -523,6 +611,12 @@ INTERNAL void user_update(void)
draw_text_ex(L.world_canvas, disp_font, pos, 1.0f / PIXELS_PER_UNIT, text); draw_text_ex(L.world_canvas, disp_font, pos, 1.0f / PIXELS_PER_UNIT, text);
} }
} }
#endif
}
/* Debug draw transform */
{
debug_draw_xform(ent->world_xform);
} }
} }
@ -540,7 +634,7 @@ INTERNAL void user_update(void)
.pos = world_mouse .pos = world_mouse
}); });
/* Draw debug info */ /* Debug draw info */
{ {
f32 spacing = 20; f32 spacing = 20;
struct v2 pos = V2(10, 8); struct v2 pos = V2(10, 8);