From cf3994901ea2b295253b83f22ab556de8b7568ad Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Fri, 10 Mar 2023 03:22:45 -0800 Subject: [PATCH] Automatic y sorting with z buffer, 3d! --- assets.mdesk | 4 + assets/level0.json | 17 +++- build_desktop_debug.bat | 2 + codegen.c | 7 +- main.c | 189 ++++++++++++++++++++++++++-------------- quad.glsl | 12 ++- run_codegen.bat | 1 + 7 files changed, 161 insertions(+), 71 deletions(-) diff --git a/assets.mdesk b/assets.mdesk index 35e9aeb..ed00cb9 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -54,6 +54,10 @@ { filepath: "bullet.png", } +@image props_atlas: +{ + filepath: "copyrighted/props.png" +} @image drop_shadow: { filepath: "drop_shadow.png", diff --git a/assets/level0.json b/assets/level0.json index 08d506d..5d47fdd 100644 --- a/assets/level0.json +++ b/assets/level0.json @@ -370,8 +370,8 @@ "rotation":0, "visible":true, "width":32, - "x":1009, - "y":974.833333333333 + "x":1478.33333333333, + "y":736.166666666666 }, { "class":"", @@ -416,6 +416,17 @@ "width":32, "x":1019.66666666667, "y":917.666666666667 + }, + { + "class":"PROP", + "height":32, + "id":11, + "name":"TREE0", + "rotation":0, + "visible":true, + "width":32, + "x":780, + "y":917.333333333333 }], "opacity":1, "type":"objectgroup", @@ -424,7 +435,7 @@ "y":0 }], "nextlayerid":8, - "nextobjectid":11, + "nextobjectid":12, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.9.2", diff --git a/build_desktop_debug.bat b/build_desktop_debug.bat index cd9e5e2..ca2a6d7 100644 --- a/build_desktop_debug.bat +++ b/build_desktop_debug.bat @@ -2,9 +2,11 @@ @REM https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170 +remedybg.exe stop-debugging call run_codegen.bat || goto :error cl /DDEVTOOLS /Igen /Ithirdparty /W3 /Zi /WX main.c || goto :error @REM cl /Igen /Ithirdparty /W3 /Zi /WX main.c || goto :error +remedybg.exe start-debugging goto :EOF :error diff --git a/codegen.c b/codegen.c index aaae2b8..4b86d7d 100644 --- a/codegen.c +++ b/codegen.c @@ -205,7 +205,12 @@ int main(int argc, char **argv) { if(has_decimal(x_string)) x_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(x_string)); if(has_decimal(y_string)) y_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(y_string)); - if(MD_S8Match(name, MD_S8Lit("PLAYER"), 0)) + MD_String8 class = MD_ChildFromString(object, MD_S8Lit("class"), 0)->first_child->string; + if(MD_S8Match(class, MD_S8Lit("PROP"), 0)) + { + fprintf(output, "{ .exists = true, .is_prop = true, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(x_string), MD_S8VArg(y_string)); + } + else if(MD_S8Match(name, MD_S8Lit("PLAYER"), 0)) { fprintf(output, "{ .exists = true, .is_character = true, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(x_string), MD_S8VArg(y_string)); } diff --git a/main.c b/main.c index 0eef895..a64f30d 100644 --- a/main.c +++ b/main.c @@ -36,6 +36,15 @@ #define Log(...) { printf("Log %d | ", __LINE__); printf(__VA_ARGS__); } +double clamp(double d, double min, double max) { + const double t = d < min ? min : d; + return t > max ? max : t; +} +float clampf(float d, float min, float max) { + const float t = d < min ? min : d; + return t > max ? max : t; +} + // so can be grep'd and removed #define dbgprint(...) { printf("Debug | %s:%d | ", __FILE__, __LINE__); printf(__VA_ARGS__); } Vec2 RotateV2(Vec2 v, float theta) @@ -112,15 +121,6 @@ typedef enum CharacterState CHARACTER_TALKING, } CharacterState; -typedef enum EntityKind -{ - ENTITY_INVALID, // zero initialized is invalid entity - - ENTITY_PLAYER, - ENTITY_OLD_MAN, - ENTITY_BULLET, -} EntityKind; - #ifdef DEVTOOLS #define SERVER_URL "http://localhost:8090" #else @@ -174,6 +174,9 @@ typedef struct Entity bool is_bullet; + // props + bool is_prop; + // npcs bool is_npc; double character_say_timer; @@ -574,11 +577,12 @@ SwordToDamage entity_sword_to_do_damage(Entity *from, Overlapping o) return to_return; } +// aabb advice by iRadEntertainment Vec2 entity_aabb_size(Entity *e) { - if(e->is_character == ENTITY_PLAYER) + if(e->is_character) { - return V2(TILE_SIZE, TILE_SIZE); + return V2(TILE_SIZE, TILE_SIZE*0.5f); } else if(e->is_npc) { @@ -608,6 +612,10 @@ Vec2 entity_aabb_size(Entity *e) { return V2(TILE_SIZE*0.25f, TILE_SIZE*0.25f); } + else if(e->is_prop) + { + return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); + } else { assert(false); @@ -900,7 +908,7 @@ void reset_level() void init(void) { Log("Size of entity struct: %zu\n", sizeof(Entity)); - Log("Size of %lld entities: %zu kb\n", ARRLEN(entities), sizeof(entities)/1024); + Log("Size of %d entities: %zu kb\n", (int)ARRLEN(entities), sizeof(entities)/1024); sg_setup(&(sg_desc){ .context = sapp_sgcontext(), }); @@ -976,10 +984,14 @@ void init(void) state.pip = sg_make_pipeline(&(sg_pipeline_desc) { .shader = shd, + .depth = { + .compare = SG_COMPAREFUNC_LESS_EQUAL, + .write_enabled = true + }, .layout = { .attrs = { - [ATTR_quad_vs_position].format = SG_VERTEXFORMAT_FLOAT2, + [ATTR_quad_vs_position].format = SG_VERTEXFORMAT_FLOAT3, [ATTR_quad_vs_texcoord0].format = SG_VERTEXFORMAT_FLOAT2, } }, @@ -1090,6 +1102,14 @@ AABB aabb_at(Vec2 at, Vec2 size) }; } +AABB aabb_at_yplusdown(Vec2 at, Vec2 size) +{ + return (AABB){ + .upper_left = at, + .lower_right = AddV2(at, V2(size.x, size.y)), + }; +} + Quad quad_at(Vec2 at, Vec2 size) { Quad to_return; @@ -1227,6 +1247,7 @@ AABB world_cam_aabb() int num_draw_calls = 0; +#define FLOATS_PER_VERTEX (3 + 2) float cur_batch_data[1024*10] = {0}; int cur_batch_data_index = 0; // @TODO check last tint as well, do this when factor into drawing parameters @@ -1239,10 +1260,10 @@ void flush_quad_batch() state.bind.fs_images[SLOT_quad_tex] = cur_batch_image; sg_apply_bindings(&state.bind); sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_quad_fs_params, &SG_RANGE(cur_batch_params)); - assert(cur_batch_data_index % 4 == 0); - sg_draw(0, cur_batch_data_index/4, 1); + assert(cur_batch_data_index % FLOATS_PER_VERTEX == 0); + sg_draw(0, cur_batch_data_index/FLOATS_PER_VERTEX, 1); num_draw_calls += 1; - memset(cur_batch_data, 0, cur_batch_data_index); + memset(cur_batch_data, 0, cur_batch_data_index*sizeof(*cur_batch_data)); cur_batch_data_index = 0; } @@ -1255,6 +1276,8 @@ typedef struct DrawParams Color tint; AABB clip_to; // if world space is in world space, if screen space is in screen space - Lao Tzu + float y_coord_sorting; + float alpha_clip_threshold; } DrawParams; @@ -1275,6 +1298,7 @@ void draw_quad(DrawParams d) params.tint[1] = d.tint.G; params.tint[2] = d.tint.B; params.tint[3] = d.tint.A; + params.alpha_clip_threshold = d.alpha_clip_threshold; if(aabb_is_valid(d.clip_to) && LenV2(aabb_size(d.clip_to)) > 0.1) { @@ -1334,7 +1358,7 @@ void draw_quad(DrawParams d) continue; // cull out of screen quads } - float new_vertices[ (2 + 2)*4 ] = {0}; + float new_vertices[ FLOATS_PER_VERTEX*4 ] = {0}; Vec2 region_size = SubV2(d.image_region.lower_right, d.image_region.upper_left); assert(region_size.X > 0.0); assert(region_size.Y > 0.0); @@ -1355,13 +1379,28 @@ void draw_quad(DrawParams d) for(int i = 0; i < 4; i++) { Vec2 in_clip_space = into_clip_space(points[i]); - new_vertices[i*4] = in_clip_space.X; - new_vertices[i*4 + 1] = in_clip_space.Y; - new_vertices[i*4 + 2] = tex_coords[i].X; - new_vertices[i*4 + 3] = tex_coords[i].Y; + new_vertices[i*FLOATS_PER_VERTEX + 0] = in_clip_space.X; + new_vertices[i*FLOATS_PER_VERTEX + 1] = in_clip_space.Y; + if(d.y_coord_sorting == 0.0f) + { + new_vertices[i*FLOATS_PER_VERTEX + 2] = 1.0f; + //new_vertices[i*FLOATS_PER_VERTEX + 2] = 0.5f; + } + else + { + //new_vertices[i*FLOATS_PER_VERTEX + 2] = (float)clamp(world_to_screen(V2(0.0f, d.y_coord_sorting)).y/screen_size().y, 0.0f, 1.0f); + new_vertices[i*FLOATS_PER_VERTEX + 2] = clampf(d.y_coord_sorting, 0.0f, 0.98f); // y sorted things always in front of non y sorted things + + //new_vertices[i*FLOATS_PER_VERTEX + 2] = -0.5f; + //new_vertices[i*FLOATS_PER_VERTEX + 2] = 0.1f; + } + //new_vertices[i*FLOATS_PER_VERTEX + 2] = 0.0f; + new_vertices[i*FLOATS_PER_VERTEX + 3] = tex_coords[i].X; + new_vertices[i*FLOATS_PER_VERTEX + 4] = tex_coords[i].Y; } - size_t total_size = ARRLEN(new_vertices)*sizeof(new_vertices); + // two triangles drawn, six vertices + size_t total_size = 6*FLOATS_PER_VERTEX; // batched a little too close to the sun if(cur_batch_data_index + total_size >= ARRLEN(cur_batch_data)) @@ -1371,13 +1410,13 @@ void draw_quad(DrawParams d) cur_batch_params = params; } -#define PUSH_VERTEX(vert) { memcpy(&cur_batch_data[cur_batch_data_index], &vert, 4*sizeof(float)); cur_batch_data_index += 4; } - PUSH_VERTEX(new_vertices[0*4]); - PUSH_VERTEX(new_vertices[1*4]); - PUSH_VERTEX(new_vertices[2*4]); - PUSH_VERTEX(new_vertices[0*4]); - PUSH_VERTEX(new_vertices[2*4]); - PUSH_VERTEX(new_vertices[3*4]); +#define PUSH_VERTEX(vert) { memcpy(&cur_batch_data[cur_batch_data_index], &vert, FLOATS_PER_VERTEX*sizeof(float)); cur_batch_data_index += FLOATS_PER_VERTEX; } + PUSH_VERTEX(new_vertices[0*FLOATS_PER_VERTEX]); + PUSH_VERTEX(new_vertices[1*FLOATS_PER_VERTEX]); + PUSH_VERTEX(new_vertices[2*FLOATS_PER_VERTEX]); + PUSH_VERTEX(new_vertices[0*FLOATS_PER_VERTEX]); + PUSH_VERTEX(new_vertices[2*FLOATS_PER_VERTEX]); + PUSH_VERTEX(new_vertices[3*FLOATS_PER_VERTEX]); #undef PUSH_VERTEX } @@ -1395,37 +1434,6 @@ double anim_sprite_duration(AnimatedSprite *s) return s->num_frames * s->time_per_frame; } -void draw_animated_sprite(AnimatedSprite *s, double elapsed_time, bool flipped, Vec2 pos, Color tint) -{ - pos = AddV2(pos, s->offset); - sg_image spritesheet_img = *s->img; - int index = (int)floor(elapsed_time/s->time_per_frame) % s->num_frames; - if(s->no_wrap) - { - index = (int)floor(elapsed_time/s->time_per_frame); - if(index >= s->num_frames) index = s->num_frames - 1; - } - - Quad q = quad_centered(pos, s->region_size); - - if(flipped) - { - swap(&q.points[0], &q.points[1]); - swap(&q.points[3], &q.points[2]); - } - - AABB region; - region.upper_left = AddV2(s->start, V2(index * s->horizontal_diff_btwn_frames, 0.0f)); - float width = img_size(spritesheet_img).X; - while(region.upper_left.X >= width) - { - region.upper_left.X -= width; - region.upper_left.Y += s->region_size.Y; - } - region.lower_right = AddV2(region.upper_left, s->region_size); - - draw_quad((DrawParams){true, q, spritesheet_img, region, tint}); -} @@ -1620,7 +1628,7 @@ AABB draw_text(TextParams t) { col = t.colors[i]; } - draw_quad((DrawParams){t.world_space, to_draw, image_font, font_atlas_region, col, t.clip_to}); + draw_quad((DrawParams){t.world_space, to_draw, image_font, font_atlas_region, col, t.clip_to, .y_coord_sorting = 1.0f}); } } } @@ -1630,6 +1638,52 @@ AABB draw_text(TextParams t) return bounds; } +float y_coord_sorting_at(Vec2 pos) +{ + float y_coord_sorting = world_to_screen(pos).y / screen_size().y; + + // debug draw the y cord sorting value +#if 0 + char *to_draw = tprint("%f", y_coord_sorting); + draw_text((TextParams){true, false, to_draw, pos, BLACK, 1.0f}); +#endif + + return y_coord_sorting; +} + +void draw_animated_sprite(AnimatedSprite *s, double elapsed_time, bool flipped, Vec2 pos, Color tint) +{ + float y_sort_pos = y_coord_sorting_at(pos); + pos = AddV2(pos, s->offset); + sg_image spritesheet_img = *s->img; + int index = (int)floor(elapsed_time/s->time_per_frame) % s->num_frames; + if(s->no_wrap) + { + index = (int)floor(elapsed_time/s->time_per_frame); + if(index >= s->num_frames) index = s->num_frames - 1; + } + + Quad q = quad_centered(pos, s->region_size); + + if(flipped) + { + swap(&q.points[0], &q.points[1]); + swap(&q.points[3], &q.points[2]); + } + + AABB region; + region.upper_left = AddV2(s->start, V2(index * s->horizontal_diff_btwn_frames, 0.0f)); + float width = img_size(spritesheet_img).X; + while(region.upper_left.X >= width) + { + region.upper_left.X -= width; + region.upper_left.Y += s->region_size.Y; + } + region.lower_right = AddV2(region.upper_left, s->region_size); + + draw_quad((DrawParams){true, q, spritesheet_img, region, tint, .y_coord_sorting = y_sort_pos, .alpha_clip_threshold = 0.2f}); +} + // gets aabbs overlapping the input aabb, including entities and tiles Overlapping get_overlapping(Level *l, AABB aabb) { @@ -1903,11 +1957,13 @@ void frame(void) sg_apply_pipeline(state.pip); //colorquad(false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), RED); - sg_image img = image_wonky_mystery_tile; + sg_image img = image_white_square; AABB region = full_region(img); //region.lower_right.X *= 0.5f; draw_quad((DrawParams){false,quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), img, region, WHITE}); + + flush_quad_batch(); sg_end_pass(); sg_commit(); reset(&scratch); @@ -2291,6 +2347,11 @@ void frame(void) else if(it->is_character) { } + else if(it->is_prop) + { + Vec2 prop_size = V2(126.0f, 180.0f); + draw_quad((DrawParams){true, quad_centered(AddV2(it->pos, V2(0.0f, 70.0)), prop_size), image_props_atlas, aabb_at_yplusdown(V2(3.0f, 295.0f), prop_size), WHITE, .y_coord_sorting = y_coord_sorting_at(AddV2(it->pos, V2(0.0f, 20.0f))), .alpha_clip_threshold = 0.4f}); + } else { assert(false); @@ -2515,9 +2576,9 @@ draw_dialog_panel(talking_to); total_height -= (total_height - (vertical_spacing + HELPER_SIZE)); const float padding = 50.0f; float y = screen_size().y/2.0f + total_height/2.0f; - draw_quad((DrawParams){false, quad_at(V2(padding, y), V2(HELPER_SIZE,HELPER_SIZE)), IMG(image_shift_icon), (Color){1.0f,1.0f,1.0f,fmaxf(0.0f, 1.0f-learned_shift)}}); + draw_quad((DrawParams){false, quad_at(V2(padding, y), V2(HELPER_SIZE,HELPER_SIZE)), IMG(image_shift_icon), (Color){1.0f,1.0f,1.0f,fmaxf(0.0f, 1.0f-learned_shift)}, .y_coord_sorting = 0.0f}); y -= vertical_spacing; - draw_quad((DrawParams){false, quad_at(V2(padding, y), V2(HELPER_SIZE,HELPER_SIZE)), IMG(image_space_icon), (Color){1.0f,1.0f,1.0f,fmaxf(0.0f, 1.0f-learned_space)}}); + draw_quad((DrawParams){false, quad_at(V2(padding, y), V2(HELPER_SIZE,HELPER_SIZE)), IMG(image_space_icon), (Color){1.0f,1.0f,1.0f,fmaxf(0.0f, 1.0f-learned_space)}, .y_coord_sorting = 0.0f}); PROFILE_SCOPE("flush rendering") { diff --git a/quad.glsl b/quad.glsl index bb3c195..8568a80 100644 --- a/quad.glsl +++ b/quad.glsl @@ -1,15 +1,15 @@ @module quad @vs vs -in vec2 position; +in vec3 position; in vec2 texcoord0; out vec2 uv; out vec2 pos; void main() { - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = vec4(position.xyz, 1.0); uv = texcoord0; - pos = position; + pos = position.xy; } @end @@ -21,6 +21,8 @@ uniform fs_params { // both in clip space vec2 clip_ul; vec2 clip_lr; + + float alpha_clip_threshold; }; in vec2 uv; @@ -32,6 +34,10 @@ void main() { // clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard; frag_color = texture(tex, uv) * tint; + if(frag_color.a <= alpha_clip_threshold) + { + discard; + } //frag_color = vec4(pos.x,0.0,0.0,1.0); } @end diff --git a/run_codegen.bat b/run_codegen.bat index 8af2b50..13f3b9c 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -12,6 +12,7 @@ copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ru copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\TiledMap Editor\Ancient Ruins-Animated Terrains-16 frames.tsx" "assets\copyrighted\ruins_animated.tsx" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\TiledMap Editor\Terrain - Ancient Ruins.tsx" "assets\copyrighted\ruins_ancient.tsx" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Terrain.png" "assets\copyrighted\ruins_ancient.png" || goto :error +copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Props\Atlas-Props.png" "assets\copyrighted\props.png" || goto :error copy "Undead - Pixel Art Characters\Undead - Pixel Art Characters\Sprites\Wraith_Red.png" "assets\copyrighted\wraith.png" || goto :error copy "Undead - Pixel Art Characters\Undead - Pixel Art Characters\Sprites\Skeleton_Blue.png" "assets\copyrighted\skeleton.png" || goto :error