entity trees, xform storing & lerping
This commit is contained in:
parent
e9dc6b2854
commit
bb9b374471
@ -174,8 +174,9 @@ if (RTC)
|
||||
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)")
|
||||
endif()
|
||||
# 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 -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")
|
||||
endif()
|
||||
|
||||
|
||||
@ -216,6 +216,7 @@ extern "C" {
|
||||
#define RGB_F(r, g, b) RGBA_F(r, g, b, 1.f)
|
||||
|
||||
#define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF)
|
||||
#define COLOR_BLACK RGB(0, 0, 0 )
|
||||
#define COLOR_RED RGB(0xFF, 0, 0 )
|
||||
#define COLOR_GREEN RGB(0, 0xFF, 0 )
|
||||
#define COLOR_BLUE RGB(0, 0, 0xFF)
|
||||
@ -406,17 +407,11 @@ struct v4_array {
|
||||
};
|
||||
|
||||
struct mat3x3 {
|
||||
union {
|
||||
f32 e[3][3];
|
||||
struct v3 rows[3];
|
||||
};
|
||||
};
|
||||
|
||||
struct mat4x4 {
|
||||
union {
|
||||
f32 e[4][4];
|
||||
struct v4 rows[4];
|
||||
};
|
||||
};
|
||||
|
||||
#define RECT(x, y, width, height) (struct rect) { (x), (y), (width), (height) }
|
||||
|
||||
25
src/entity.h
25
src/entity.h
@ -31,25 +31,33 @@ enum entity_prop {
|
||||
|
||||
struct entity {
|
||||
b32 active;
|
||||
u64 gen;
|
||||
struct entity_handle handle;
|
||||
u64 continuity_gen;
|
||||
|
||||
u64 props[(ENTITY_PROP_COUNT + 63) / 64];
|
||||
|
||||
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 */
|
||||
|
||||
struct trs rel_trs;
|
||||
|
||||
struct mat3x3 world_xform; /* Calculated post-physics */
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Sprite */
|
||||
|
||||
struct string sprite_name;
|
||||
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 */
|
||||
@ -82,6 +90,15 @@ struct entity {
|
||||
|
||||
};
|
||||
|
||||
/* ========================== *
|
||||
* Handle
|
||||
* ========================== */
|
||||
|
||||
INLINE b32 entity_is_valid(struct entity_handle eh)
|
||||
{
|
||||
return eh.gen != 0;
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Prop
|
||||
* ========================== */
|
||||
|
||||
182
src/game.c
182
src/game.c
@ -17,8 +17,6 @@ GLOBAL struct {
|
||||
struct sys_thread game_thread;
|
||||
|
||||
f64 timescale;
|
||||
f64 dt;
|
||||
f64 time;
|
||||
struct entity *free_entity_head;
|
||||
struct tick tick;
|
||||
|
||||
@ -43,16 +41,17 @@ INTERNAL struct entity *entity_alloc(void)
|
||||
entity = L.free_entity_head;
|
||||
L.free_entity_head = entity->next_free;
|
||||
*entity = (struct entity) {
|
||||
.gen = entity->gen
|
||||
.handle = entity->handle
|
||||
};
|
||||
} else {
|
||||
/* Make new */
|
||||
if (L.tick.entities_count >= MAX_ENTITIES) {
|
||||
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) {
|
||||
.gen = 1
|
||||
.handle = { .gen = 1, .idx = idx }
|
||||
};
|
||||
}
|
||||
return entity;
|
||||
@ -69,6 +68,31 @@ INTERNAL void entity_release(struct entity *entity)
|
||||
}
|
||||
#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
|
||||
* ========================== */
|
||||
@ -130,8 +154,8 @@ INTERNAL void game_update(void)
|
||||
#endif
|
||||
|
||||
++L.tick.id;
|
||||
L.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale);
|
||||
L.time += L.dt;
|
||||
L.tick.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale);
|
||||
L.tick.time += L.tick.dt;
|
||||
|
||||
/* TODO: remove this (testing) */
|
||||
/* Initialize entities */
|
||||
@ -140,28 +164,78 @@ INTERNAL void game_update(void)
|
||||
run = 1;
|
||||
|
||||
/* Player ent */
|
||||
struct entity *player_ent;
|
||||
{
|
||||
struct string sprite_name = STR("res/graphics/timmy.ase");
|
||||
|
||||
struct sheet *sheet = sheet_load(sprite_name);
|
||||
f32 meters_width = sheet->image_size.x / 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);
|
||||
//f32 rot = PI / 2;
|
||||
f32 rot = 0;
|
||||
struct v2 size = V2(meters_width, meters_height);
|
||||
|
||||
struct entity *e = entity_alloc();
|
||||
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_trs = TRS(.t = V2(1, 0), .r = 0, .s = size);
|
||||
e->sprite_pivot = V2(1, 0);
|
||||
e->sprite_trs = TRS(.s = sprite_size);
|
||||
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_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) {
|
||||
@ -213,7 +287,7 @@ INTERNAL void game_update(void)
|
||||
struct sheet_tag tag = sheet_get_tag(sheet, ent->animation_name);
|
||||
|
||||
if (ent->animation_start_gen == ent->animation_gen) {
|
||||
ent->animation_time_in_frame += L.dt;
|
||||
ent->animation_time_in_frame += L.tick.dt;
|
||||
} else {
|
||||
ent->animation_frame = sheet_get_frame(sheet, tag.start);
|
||||
ent->animation_start_gen = ent->animation_gen;
|
||||
@ -236,9 +310,37 @@ INTERNAL void game_update(void)
|
||||
}
|
||||
}
|
||||
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 position
|
||||
* 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 from mouse
|
||||
* ========================== */
|
||||
|
||||
/* ENTITY_PROP_TEST_FOLLOW_MOUSE */
|
||||
@ -247,19 +349,43 @@ break_animation:
|
||||
ent->test_start_rel_trs.t = L.tick.player_focus;
|
||||
}
|
||||
|
||||
/* ENTITY_PROP_TEST */
|
||||
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
|
||||
f32 t = ((f32)L.time);
|
||||
f32 r = t * 0.5f;
|
||||
/* ========================== *
|
||||
* Calculate tree trs
|
||||
* ========================== */
|
||||
ent->world_xform = mat3x3_from_trs(ent->rel_trs);
|
||||
|
||||
ent->rel_trs.r = ent->test_start_rel_trs.r + (r * 0.5f);
|
||||
ent->sprite_trs.r = ent->test_start_sprite_trs.r + (r * 3.0f);
|
||||
struct entity *child = entity_from_handle(ent->first);
|
||||
struct mat3x3 parent_xform = ent->world_xform;
|
||||
while (child) {
|
||||
child->world_xform = mat3x3_trs(parent_xform, child->rel_trs);
|
||||
|
||||
|
||||
f32 s = 1 + (math_abs_f32(math_sin(t * 5)) * 0.25f);
|
||||
ent->rel_trs.s = v2_mul(ent->test_start_rel_trs.s, s);
|
||||
ent->sprite_trs.s = v2_mul(ent->test_start_sprite_trs.s, s * 1.5f);
|
||||
/* Depth first iteration */
|
||||
if (child->first.gen) {
|
||||
/* Next child */
|
||||
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
|
||||
@ -268,7 +394,7 @@ break_animation:
|
||||
if (entity_has_prop(ent, ENTITY_PROP_TEST_SOUND_EMITTER)) {
|
||||
struct mixer_desc desc = ent->sound_desc;
|
||||
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);
|
||||
b32 played = ent->sound_handle.gen != 0;
|
||||
if (sound) {
|
||||
|
||||
164
src/math.h
164
src/math.h
@ -6,6 +6,9 @@
|
||||
#define PI ((f32)3.14159265358979323846)
|
||||
#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
|
||||
* ========================== */
|
||||
@ -62,13 +65,15 @@ INLINE f64 math_abs_f64(f64 f)
|
||||
INLINE i32 math_sign_f32(f32 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
* ========================== */
|
||||
@ -202,26 +218,55 @@ INLINE f32 math_lerp_f32(f32 val0, f32 val1, f32 t)
|
||||
INLINE f32 math_sin(f32 x)
|
||||
{
|
||||
const f32 c = 0.225;
|
||||
|
||||
/* Wrap to range [0, TAU] */
|
||||
x -= (TAU * (f32)math_floor_f32(x / TAU));
|
||||
|
||||
/* Wrap to range [-PI, PI] */
|
||||
x += (TAU * (x < -PI)) - (TAU * (x > PI));
|
||||
|
||||
/* Parabola */
|
||||
x -= (TAU * (f32)math_floor_f32(x / TAU)); /* [0, TAU] */
|
||||
x += (TAU * (x < -PI)) - (TAU * (x > PI)); /* [-PI, PI] */
|
||||
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;
|
||||
|
||||
return y;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -502,6 +558,49 @@ INLINE struct v2 mat3x3_get_pos(struct mat3x3 m)
|
||||
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
|
||||
* ========================== */
|
||||
@ -568,11 +667,30 @@ INLINE struct trs trs_lerp(struct trs a, struct trs b, f32 t)
|
||||
{
|
||||
struct trs res;
|
||||
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);
|
||||
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
|
||||
* ========================== */
|
||||
@ -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)
|
||||
{
|
||||
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) {
|
||||
V2(p1_v3.x, p1_v3.y),
|
||||
V2(p2_v3.x, p2_v3.y),
|
||||
V2(p3_v3.x, p3_v3.y),
|
||||
V2(p4_v3.x, p4_v3.y)
|
||||
mat3x3_mul_v2(m, quad.p1),
|
||||
mat3x3_mul_v2(m, quad.p2),
|
||||
mat3x3_mul_v2(m, quad.p3),
|
||||
mat3x3_mul_v2(m, quad.p4)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -434,7 +434,7 @@ struct mixed_pcm_f32 mixer_update(struct arena *arena, u64 frame_count)
|
||||
|
||||
/* Calculate 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;
|
||||
|
||||
/* Spatialize samples */
|
||||
|
||||
@ -7,6 +7,9 @@ struct tick {
|
||||
u64 id; /* Starts at 1 */
|
||||
sys_timestamp_t published_ts;
|
||||
|
||||
f64 dt;
|
||||
f64 time;
|
||||
|
||||
struct v2 player_focus; /* Mouse cursor pos in world coordinates */
|
||||
|
||||
u64 entities_count; /* Includes 'released' & non-active entities */
|
||||
|
||||
164
src/user.c
164
src/user.c
@ -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)
|
||||
{
|
||||
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 V2(p_inverse.x, p_inverse.y);
|
||||
return mat3x3_mul_v2(mtx_inverse, p);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Update
|
||||
* ========================== */
|
||||
|
||||
/* TODO: remove this (testing) */
|
||||
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 x_ray = mat3x3_get_right(mtx);
|
||||
struct v2 y_ray = mat3x3_get_down(mtx);
|
||||
struct quad quad = quad_mul_mat3x3(quad_scale(QUAD_UNIT_SQUARE, 0.075), mtx);
|
||||
struct v2 y_ray = mat3x3_get_up(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, y_ray, thickness, color_y);
|
||||
draw_solid_quad(L.world_canvas, quad, color);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Update
|
||||
* ========================== */
|
||||
|
||||
INTERNAL void user_update(void)
|
||||
{
|
||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||
@ -360,17 +361,32 @@ INTERNAL void user_update(void)
|
||||
|
||||
tick_cpy(tick, t1);
|
||||
|
||||
/* Blend entities */
|
||||
#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);
|
||||
for (u64 i = 0; i < num_entities; ++i) {
|
||||
struct entity *e = &tick->entities[i];
|
||||
struct entity *e0 = &t0->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->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
|
||||
@ -388,10 +404,10 @@ INTERNAL void user_update(void)
|
||||
u32 x_color = RGBA(0x3f, 0, 0, 0xFF);
|
||||
u32 y_color = RGBA(0, 0x3f, 0, 0xFF);
|
||||
|
||||
i64 startx = -5;
|
||||
i64 starty = -5;
|
||||
i64 rows = 10;
|
||||
i64 cols = 10;
|
||||
i64 startx = -10;
|
||||
i64 starty = -10;
|
||||
i64 rows = 20;
|
||||
i64 cols = 20;
|
||||
|
||||
|
||||
/* 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];
|
||||
if (!ent->active) continue;
|
||||
|
||||
/* Debug draw transform */
|
||||
{
|
||||
struct mat3x3 mtx = mat3x3_from_trs(ent->rel_trs);
|
||||
debug_draw_xform(mtx);
|
||||
}
|
||||
|
||||
/* Draw sprite */
|
||||
if (ent->sprite_name.len > 0) {
|
||||
struct string tex_name = ent->sprite_name;
|
||||
@ -443,57 +518,70 @@ INTERNAL void user_update(void)
|
||||
struct quad quad = QUAD_UNIT_SQUARE_CENTERED;
|
||||
|
||||
/* TODO: Do this calculation in game loop */
|
||||
#if 0
|
||||
struct mat3x3 mtx;
|
||||
{
|
||||
/* Entity trs */
|
||||
mtx = mat3x3_from_trs(ent->rel_trs);
|
||||
mtx = mat3x3_from_trs(ent->world_trs);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
if (texture) {
|
||||
|
||||
struct draw_texture_params params;
|
||||
u32 tint = ent->sprite_tint;
|
||||
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint);
|
||||
if (entity_has_prop(ent, ENTITY_PROP_SPRITE_ANIMATED)) {
|
||||
struct sheet_frame frame = ent->animation_frame;
|
||||
params.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);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Debug draw sprite quad */
|
||||
{
|
||||
f32 thickness = 2.f;
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Debug draw sprite transform */
|
||||
{
|
||||
debug_draw_xform(mtx);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Debug draw sprite pivot */
|
||||
{
|
||||
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);
|
||||
|
||||
struct v2 p = mat3x3_get_pos(mtx_pre_pivot);
|
||||
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);
|
||||
draw_solid_circle(L.world_canvas, mat3x3_get_pos(mtx_pre_pivot), 0.02, color, 20);
|
||||
}
|
||||
|
||||
/* Debug draw sprite name */
|
||||
#if 0
|
||||
/* Debug draw sprite info */
|
||||
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
|
||||
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,
|
||||
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)
|
||||
);
|
||||
|
||||
@ -523,6 +611,12 @@ INTERNAL void user_update(void)
|
||||
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
|
||||
});
|
||||
|
||||
/* Draw debug info */
|
||||
/* Debug draw info */
|
||||
{
|
||||
f32 spacing = 20;
|
||||
struct v2 pos = V2(10, 8);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user