321 lines
11 KiB
C
321 lines
11 KiB
C
#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>
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
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);
|
|
}
|
|
}
|