#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 #include void finally_render_quads(const Primitive2D primitives[], const struct QuadBatch batch, const VertexBuffer buffer) { DeferredCommandDraw command = {0}; uint32_t off = 0, voff = 0, uvoff = 0, coff = 0; if (!batch.constant_colored && batch.textured) { off = offsetof(ElementIndexedQuad, v1); voff = offsetof(ElementIndexedQuad, v0); uvoff = offsetof(ElementIndexedQuad, uv0); coff = offsetof(ElementIndexedQuad, c0); } else if (batch.constant_colored && batch.textured) { off = offsetof(ElementIndexedQuadWithoutColor, v1); voff = offsetof(ElementIndexedQuadWithoutColor, v0); uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0); } else if (!batch.constant_colored && !batch.textured) { off = offsetof(ElementIndexedQuadWithoutTexture, v1); voff = offsetof(ElementIndexedQuadWithoutTexture, v0); coff = offsetof(ElementIndexedQuad, c0); } else if (batch.constant_colored && !batch.textured) { off = offsetof(ElementIndexedQuadWithoutColorWithoutTexture, v1); voff = offsetof(ElementIndexedQuadWithoutColorWithoutTexture, v0); } command.vertices = (AttributeArrayPointer) { .arity = 2, .type = TWN_FLOAT, .stride = off, .offset = voff, .buffer = buffer }; if (batch.textured) command.texcoords = (AttributeArrayPointer) { .arity = 2, .type = TWN_FLOAT, .stride = off, .offset = uvoff, .buffer = buffer }; if (!batch.constant_colored) { command.colors = (AttributeArrayPointer) { .arity = 4, .type = TWN_UNSIGNED_BYTE, .stride = off, .offset = coff, .buffer = buffer }; } else { command.constant_colored = true; command.color = primitives[0].sprite.color; } if (batch.textured) { command.textured = true; command.texture_key = batch.texture_key; command.texture_repeat = batch.repeat; } command.element_buffer = get_quad_element_buffer(); command.element_count = 6 * (uint32_t)batch.size; command.range_end = 6 * (uint32_t)batch.size; command.texture_mode = batch.mode; command.pipeline = PIPELINE_2D; 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); } size_t get_quad_payload_size(struct QuadBatch batch) { if (batch.constant_colored && batch.textured) return sizeof (ElementIndexedQuadWithoutColor); else if (!batch.constant_colored && batch.textured) return sizeof (ElementIndexedQuad); else if (batch.constant_colored && !batch.textured) return sizeof (ElementIndexedQuadWithoutColorWithoutTexture); else if (!batch.constant_colored && !batch.textured) return sizeof (ElementIndexedQuadWithoutTexture); SDL_assert(false); return 0; } void push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch, size_t index, VertexBufferBuilder *builder, Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3, Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, Color color) { if (!batch.constant_colored && batch.textured) { ElementIndexedQuad const payload = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, .uv0 = uv0, .uv1 = uv1, .uv2 = uv2, .uv3 = uv3, /* equal for all (flat shaded) */ .c0 = color, // .c1 = color, .c2 = color, // .c3 = color, }; ((ElementIndexedQuad *)builder->base)[index] = payload; } else if (batch.constant_colored && batch.textured) { ElementIndexedQuadWithoutColor const payload = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, .uv0 = uv0, .uv1 = uv1, .uv2 = uv2, .uv3 = uv3, }; ((ElementIndexedQuadWithoutColor *)builder->base)[index] = payload; } else if (!batch.constant_colored && !batch.textured) { ElementIndexedQuadWithoutTexture const payload = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, /* equal for all (flat shaded) */ .c0 = color, // .c1 = color, .c2 = color, // .c3 = color, }; ((ElementIndexedQuadWithoutTexture *)builder->base)[index] = payload; } else if (batch.constant_colored && !batch.textured) { ElementIndexedQuadWithoutColorWithoutTexture const payload = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, }; ((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); } }