draw: increase far Z, separate path for space quads, fix billboard batching
This commit is contained in:
		| @@ -12,7 +12,7 @@ | ||||
|  | ||||
|  | ||||
| #define TERRAIN_FREQUENCY 0.15f | ||||
| #define TERRAIN_DISTANCE 64 | ||||
| #define TERRAIN_DISTANCE 128 | ||||
| #define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2) | ||||
| #define PLAYER_HEIGHT 0.6f | ||||
|  | ||||
| @@ -161,6 +161,7 @@ static void draw_terrain(SceneIngame *scn) { | ||||
|                             (Rect){ .w = 128, .h = 128 }, | ||||
|                             (Color){255, 255, 255, 255}); | ||||
|  | ||||
|             /* TODO: only draw upclose */ | ||||
|             draw_billboard("/assets/grasses/10.png", | ||||
|                           (Vec3){ (float)x, d0 + 0.15f, (float)y }, | ||||
|                           (Vec2){0.3f, 0.3f}, | ||||
|   | ||||
| @@ -95,14 +95,15 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, | ||||
|     const Vec2 uv3c = { xr + wr,      yr      }; | ||||
|  | ||||
|     for (size_t batch_n = 0; batch_n <= (primitives_len - 1) / QUAD_ELEMENT_BUFFER_LENGTH; batch_n++) { | ||||
|         size_t const processing = MIN(primitives_len - batch_n * QUAD_ELEMENT_BUFFER_LENGTH, QUAD_ELEMENT_BUFFER_LENGTH); | ||||
|  | ||||
|         /* emit vertex data */ | ||||
|         VertexBuffer const buffer = get_scratch_vertex_array(); | ||||
|         VertexBufferBuilder builder = build_vertex_buffer( | ||||
|             buffer, | ||||
|             sizeof (ElementIndexedBillboard) * MIN(primitives_len - batch_n * QUAD_ELEMENT_BUFFER_LENGTH, QUAD_ELEMENT_BUFFER_LENGTH)); | ||||
|             sizeof (ElementIndexedBillboard) * processing); | ||||
|  | ||||
|         for (size_t i = 0; i < primitives_len; ++i) { | ||||
|         for (size_t i = 0; i < processing; ++i) { | ||||
|             struct SpaceBillboard const billboard = ((SpaceBillboard *)(void *)batch->primitives)[batch_n * QUAD_ELEMENT_BUFFER_LENGTH + i]; | ||||
|  | ||||
|             /* a = (right + up) * size, b = (right - up) * size*/ | ||||
| @@ -179,20 +180,20 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, | ||||
|             .buffer = buffer | ||||
|         }; | ||||
|  | ||||
|         command.textured = true; | ||||
|         command.texture_key = texture_key; | ||||
|         command.textured = true; | ||||
|  | ||||
|         command.element_buffer = get_quad_element_buffer(); | ||||
|         command.element_count = 6 * (uint32_t)primitives_len; | ||||
|         command.range_end = 6 * (uint32_t)primitives_len; | ||||
|         command.element_count = 6 * (uint32_t)processing; | ||||
|         command.range_end = 6 * (uint32_t)processing; | ||||
|  | ||||
|         /* TODO: support alpha blended case, with distance sort */ | ||||
|         TextureMode mode = textures_get_mode(&ctx.texture_cache, texture_key); | ||||
|         if (mode == TEXTURE_MODE_GHOSTLY) | ||||
|             mode = TEXTURE_MODE_SEETHROUGH; | ||||
|  | ||||
|         command.texture_mode = mode; | ||||
|         command.pipeline = PIPELINE_SPACE; | ||||
|         command.texture_mode = mode; | ||||
|  | ||||
|         command.depth_range_high = depth_range_high; | ||||
|         command.depth_range_low = depth_range_low; | ||||
|   | ||||
| @@ -52,6 +52,9 @@ void render_clear(void) { | ||||
|  | ||||
|     for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i) | ||||
|         arrsetlen(ctx.billboard_batches[i].value.primitives, 0); | ||||
|  | ||||
|     for (size_t i = 0; i < hmlenu(ctx.quad_batches); ++i) | ||||
|         arrsetlen(ctx.quad_batches[i].value.primitives, 0); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -196,31 +199,6 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_ | ||||
| } | ||||
|  | ||||
|  | ||||
| TWN_API void draw_quad(char const *texture, | ||||
|                        Vec3        v0,          /* upper-left */ | ||||
|                        Vec3        v1,          /* bottom-left */ | ||||
|                        Vec3        v2,          /* bottom-right */ | ||||
|                        Vec3        v3,          /* upper-right */ | ||||
|                        Rect        texture_region, | ||||
|                        Color       color) | ||||
| { | ||||
|     Vec2 const uv0 = { texture_region.x,                    texture_region.y }; | ||||
|     Vec2 const uv1 = { texture_region.x,                    texture_region.y + texture_region.h }; | ||||
|     Vec2 const uv2 = { texture_region.x + texture_region.w, texture_region.y + texture_region.h }; | ||||
|     Vec2 const uv3 = { texture_region.x + texture_region.w, texture_region.y }; | ||||
|  | ||||
|     draw_triangle(texture, | ||||
|                   v0, v1, v3, | ||||
|                   uv0, uv1, uv3, | ||||
|                   color, color, color); | ||||
|  | ||||
|     draw_triangle(texture, | ||||
|                   v3, v1, v2, | ||||
|                   uv3, uv1, uv2, | ||||
|                   color, color, color); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void render_2d(void) { | ||||
|     const size_t render_queue_len = arrlenu(ctx.render_queue_2d); | ||||
|  | ||||
| @@ -397,18 +375,16 @@ static void render_2d(void) { | ||||
| static void render_space(void) { | ||||
|     finally_draw_models(); | ||||
|  | ||||
|     /* nothing to do, abort */ | ||||
|     /* as space pipeline isn't used we can have fewer changes and initialization costs */ | ||||
|     if (hmlenu(ctx.uncolored_mesh_batches) != 0 || hmlenu(ctx.billboard_batches) != 0) { | ||||
|         for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) { | ||||
|     for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) | ||||
|         finally_draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value, | ||||
|                                                      ctx.uncolored_mesh_batches[i].key); | ||||
|         } | ||||
|  | ||||
|         for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i) { | ||||
|     for (size_t i = 0; i < hmlenu(ctx.quad_batches); ++i) | ||||
|         finally_draw_space_quads_batch(&ctx.quad_batches[i].value, | ||||
|                                         ctx.quad_batches[i].key); | ||||
|  | ||||
|     for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i) | ||||
|         finally_draw_billboard_batch(&ctx.billboard_batches[i].value, ctx.billboard_batches[i].key); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     render_skybox(); /* after everything else, as to use depth buffer for early z rejection */ | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ extern float camera_2d_zoom; | ||||
|  | ||||
| extern double depth_range_low, depth_range_high; | ||||
|  | ||||
| #define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) | ||||
| #define QUAD_ELEMENT_BUFFER_LENGTH ((65536 * 1) / 6) | ||||
| #define CIRCLE_VERTICES_MAX 2048 | ||||
|  | ||||
| /* TODO: limit to only most necessary */ | ||||
| @@ -217,6 +217,7 @@ typedef struct ElementIndexedQuadWithoutColorWithoutTexture { | ||||
|     Vec2 v3; | ||||
| } ElementIndexedQuadWithoutColorWithoutTexture; | ||||
|  | ||||
| /* TODO: rename to space quad */ | ||||
| /* TODO: no color variant */ | ||||
| typedef struct ElementIndexedBillboard { | ||||
|     /* upper-left */ | ||||
| @@ -334,6 +335,9 @@ void push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch, | ||||
|                                                 Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, | ||||
|                                                 Color color); | ||||
|  | ||||
| void finally_draw_space_quads_batch(const MeshBatch *batch, | ||||
|                                     const TextureKey texture_key); | ||||
|  | ||||
| void finally_draw_text(FontData const *font_data, | ||||
|                        size_t len, | ||||
|                        Color color, | ||||
|   | ||||
| @@ -584,8 +584,8 @@ void finally_draw_command(DeferredCommandDraw command) { | ||||
|  | ||||
|     finally_use_texture_mode(command.texture_mode); | ||||
|  | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, command.vertices.buffer); | ||||
|     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, command.element_buffer); | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, command.vertices.buffer); | ||||
|  | ||||
|     glEnableClientState(GL_VERTEX_ARRAY); | ||||
|     glVertexPointer(command.vertices.arity, | ||||
| @@ -637,7 +637,7 @@ void finally_draw_command(DeferredCommandDraw command) { | ||||
|                                 command.range_start, | ||||
|                                 command.range_end, | ||||
|                                 command.element_count, | ||||
|                                 GL_UNSIGNED_SHORT, | ||||
|                                 GL_UNSIGNED_INT, | ||||
|                                 NULL); | ||||
|     } else { | ||||
|         SDL_assert(command.primitive_count != 0); | ||||
| @@ -661,6 +661,7 @@ void finally_draw_command(DeferredCommandDraw command) { | ||||
|     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | ||||
| } | ||||
|  | ||||
|  | ||||
| void render_line(const LinePrimitive *line) { | ||||
|     finally_use_2d_pipeline(); | ||||
|     glBegin(GL_LINES); | ||||
|   | ||||
| @@ -62,15 +62,15 @@ IndexBuffer get_quad_element_buffer(void) { | ||||
|     /* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */ | ||||
|     if (buffer == 0) { | ||||
|         buffer = create_index_buffer(); | ||||
|         IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); | ||||
|         IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLuint) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); | ||||
|  | ||||
|         for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { | ||||
|             ((GLshort *)builder.base)[i * 6 + 0] = (GLshort)(i * 4 + 0); | ||||
|             ((GLshort *)builder.base)[i * 6 + 1] = (GLshort)(i * 4 + 1); | ||||
|             ((GLshort *)builder.base)[i * 6 + 2] = (GLshort)(i * 4 + 2); | ||||
|             ((GLshort *)builder.base)[i * 6 + 3] = (GLshort)(i * 4 + 2); | ||||
|             ((GLshort *)builder.base)[i * 6 + 4] = (GLshort)(i * 4 + 3); | ||||
|             ((GLshort *)builder.base)[i * 6 + 5] = (GLshort)(i * 4 + 0); | ||||
|             ((GLuint *)builder.base)[i * 6 + 0] = (GLuint)(i * 4 + 0); | ||||
|             ((GLuint *)builder.base)[i * 6 + 1] = (GLuint)(i * 4 + 1); | ||||
|             ((GLuint *)builder.base)[i * 6 + 2] = (GLuint)(i * 4 + 2); | ||||
|             ((GLuint *)builder.base)[i * 6 + 3] = (GLuint)(i * 4 + 2); | ||||
|             ((GLuint *)builder.base)[i * 6 + 4] = (GLuint)(i * 4 + 3); | ||||
|             ((GLuint *)builder.base)[i * 6 + 5] = (GLuint)(i * 4 + 0); | ||||
|         } | ||||
|  | ||||
|         finish_index_builder(&builder); | ||||
|   | ||||
| @@ -1,4 +1,8 @@ | ||||
| #include "twn_draw_c.h" | ||||
| #include "twn_draw.h" | ||||
| #include "twn_engine_context_c.h" | ||||
| #include "twn_util.h" | ||||
| #include "twn_util_c.h" | ||||
|  | ||||
| #include <stb_ds.h> | ||||
|  | ||||
| @@ -171,3 +175,146 @@ void push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch, | ||||
|         ((ElementIndexedQuadWithoutColorWithoutTexture *)builder->base)[index] = payload; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void draw_quad(char const *texture, | ||||
|                Vec3        v0,              /* upper-left */ | ||||
|                Vec3        v1,              /* bottom-left */ | ||||
|                Vec3        v2,              /* bottom-right */ | ||||
|                Vec3        v3,              /* upper-right */ | ||||
|                Rect        texture_region, | ||||
|                Color       color) | ||||
| { | ||||
|     Vec2 const uv0 = { texture_region.x,                    texture_region.y }; | ||||
|     Vec2 const uv1 = { texture_region.x,                    texture_region.y + texture_region.h }; | ||||
|     Vec2 const uv2 = { texture_region.x + texture_region.w, texture_region.y + texture_region.h }; | ||||
|     Vec2 const uv3 = { texture_region.x + texture_region.w, texture_region.y }; | ||||
|  | ||||
|     // TODO: order drawing by atlas id as well, so that texture rebinding is not as common | ||||
|     const TextureKey texture_key = textures_get_key(&ctx.texture_cache, texture); | ||||
|  | ||||
|     struct MeshBatchItem *batch_p = hmgetp_null(ctx.quad_batches, texture_key); | ||||
|     if (!batch_p) { | ||||
|         struct MeshBatch item = {0}; | ||||
|         hmput(ctx.quad_batches, texture_key, item); | ||||
|         batch_p = &ctx.quad_batches[hmlenu(ctx.quad_batches) - 1]; /* TODO: can last index be used? */ | ||||
|     } | ||||
|  | ||||
|     /* TODO: rename from billboards */ | ||||
|     struct ElementIndexedBillboard billboard = { | ||||
|         .v0 = v0, | ||||
|         .v1 = v1, | ||||
|         .v2 = v2, | ||||
|         .v3 = v3, | ||||
|  | ||||
|         /* will be adjusted later when atlas is baked */ | ||||
|         .uv0 = uv0, | ||||
|         .uv1 = uv1, | ||||
|         .uv2 = uv2, | ||||
|         .uv3 = uv3, | ||||
|  | ||||
|         /* flat shading is assumed, so we can skip setting the duplicates */ | ||||
|         .c0 = color, | ||||
|         // .c1 = color, | ||||
|         .c2 = color, | ||||
|         // .c3 = color, | ||||
|     }; | ||||
|  | ||||
|     struct ElementIndexedBillboard *billboards = (struct ElementIndexedBillboard *)(void *)batch_p->value.primitives; | ||||
|  | ||||
|     arrpush(billboards, billboard); | ||||
|     batch_p->value.primitives = (uint8_t *)billboards; | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_draw_space_quads_batch(const MeshBatch *batch, | ||||
|                                     const TextureKey texture_key) | ||||
| { | ||||
|     const size_t primitives_len = arrlenu(batch->primitives); | ||||
|  | ||||
|     /* nothing to do */ | ||||
|     if (primitives_len == 0) | ||||
|         return; | ||||
|  | ||||
|     const Rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); | ||||
|     const Rect dims    = textures_get_dims(&ctx.texture_cache, texture_key); | ||||
|  | ||||
|     const float wr = srcrect.w / dims.w; | ||||
|     const float hr = srcrect.h / dims.h; | ||||
|     const float xr = srcrect.x / dims.w; | ||||
|     const float yr = srcrect.y / dims.h; | ||||
|  | ||||
|     /* update pixel-based uvs to correspond with texture atlases */ | ||||
|     for (size_t i = 0; i < primitives_len; ++i) { | ||||
|         ElementIndexedBillboard *payload = | ||||
|             &((ElementIndexedBillboard *)(void *)batch->primitives)[i]; | ||||
|  | ||||
|         payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr; | ||||
|         payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr; | ||||
|         payload->uv1.x = xr + ((float)payload->uv1.x / srcrect.w) * wr; | ||||
|         payload->uv1.y = yr + ((float)payload->uv1.y / srcrect.h) * hr; | ||||
|         payload->uv2.x = xr + ((float)payload->uv2.x / srcrect.w) * wr; | ||||
|         payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr; | ||||
|         payload->uv3.x = xr + ((float)payload->uv3.x / srcrect.w) * wr; | ||||
|         payload->uv3.y = yr + ((float)payload->uv3.y / srcrect.h) * hr; | ||||
|     } | ||||
|  | ||||
|     for (size_t batch_n = 0; batch_n <= (primitives_len - 1) / QUAD_ELEMENT_BUFFER_LENGTH; batch_n++) { | ||||
|         size_t const processing = MIN(primitives_len - batch_n * QUAD_ELEMENT_BUFFER_LENGTH, QUAD_ELEMENT_BUFFER_LENGTH); | ||||
|  | ||||
|         VertexBuffer const buffer = get_scratch_vertex_array(); | ||||
|  | ||||
|         specify_vertex_buffer(buffer, &batch->primitives[batch_n * QUAD_ELEMENT_BUFFER_LENGTH], sizeof (ElementIndexedBillboard) * processing); | ||||
|  | ||||
|         DeferredCommandDraw command = {0}; | ||||
|  | ||||
|         command.vertices = (AttributeArrayPointer) { | ||||
|             .arity = 3, | ||||
|             .type = TWN_FLOAT, | ||||
|             .stride = offsetof(ElementIndexedBillboard, v1), | ||||
|             .offset = offsetof(ElementIndexedBillboard, v0), | ||||
|             .buffer = buffer | ||||
|         }; | ||||
|  | ||||
|         command.texcoords = (AttributeArrayPointer) { | ||||
|             .arity = 2, | ||||
|             .type = TWN_FLOAT, | ||||
|             .stride = offsetof(ElementIndexedBillboard, v1), | ||||
|             .offset = offsetof(ElementIndexedBillboard, uv0), | ||||
|             .buffer = buffer | ||||
|         }; | ||||
|  | ||||
|         command.colors = (AttributeArrayPointer) { | ||||
|             .arity = 4, | ||||
|             .type = TWN_UNSIGNED_BYTE, | ||||
|             .stride = offsetof(ElementIndexedBillboard, v1), | ||||
|             .offset = offsetof(ElementIndexedBillboard, c0), | ||||
|             .buffer = buffer | ||||
|         }; | ||||
|  | ||||
|         command.texture_key = texture_key; | ||||
|         command.textured = true; | ||||
|  | ||||
|         command.element_buffer = get_quad_element_buffer(); | ||||
|         command.element_count = 6 * (uint32_t)processing; | ||||
|         command.range_end = 6 * (uint32_t)processing; | ||||
|  | ||||
|         /* TODO: support alpha blended case, with distance sort */ | ||||
|         TextureMode mode = textures_get_mode(&ctx.texture_cache, texture_key); | ||||
|         if (mode == TEXTURE_MODE_GHOSTLY) | ||||
|             mode = TEXTURE_MODE_SEETHROUGH; | ||||
|  | ||||
|         command.pipeline = PIPELINE_SPACE; | ||||
|         command.texture_mode = mode; | ||||
|  | ||||
|         command.depth_range_high = depth_range_high; | ||||
|         command.depth_range_low = depth_range_low; | ||||
|  | ||||
|         DeferredCommand final_command = { | ||||
|             .type = DEFERRED_COMMAND_TYPE_DRAW, | ||||
|             .draw = command | ||||
|         }; | ||||
|  | ||||
|         arrpush(deferred_commands, final_command); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  | ||||
|  | ||||
| #define CAMERA_NEAR_Z 0.1f | ||||
| #define CAMERA_FAR_Z 100.0f | ||||
| #define CAMERA_FAR_Z 1000.0f | ||||
|  | ||||
|  | ||||
| Matrix4 camera_look_at(const Camera *const camera) { | ||||
|   | ||||
| @@ -49,6 +49,7 @@ typedef struct EngineContext { | ||||
|     Primitive2D *render_queue_2d; | ||||
|     MeshBatchItem *uncolored_mesh_batches; | ||||
|     MeshBatchItem *billboard_batches; | ||||
|     MeshBatchItem *quad_batches; | ||||
|     TextCache text_cache; | ||||
|     TextureCache texture_cache; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user