billboards!
This commit is contained in:
@ -29,8 +29,12 @@ void render_queue_clear(void) {
|
||||
/* and start overwriting the existing data */
|
||||
arrsetlen(ctx.render_queue_2d, 0);
|
||||
|
||||
/* TODO: free memory if it isn't used for a while */
|
||||
for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i)
|
||||
arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0);
|
||||
|
||||
for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i)
|
||||
arrsetlen(ctx.billboard_batches[i].value.primitives, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -332,18 +336,23 @@ static void render_2d(void) {
|
||||
static void render_space(void) {
|
||||
/* 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)
|
||||
return;
|
||||
if (hmlenu(ctx.uncolored_mesh_batches) != 0 || hmlenu(ctx.billboard_batches) != 0) {
|
||||
use_space_pipeline();
|
||||
apply_fog();
|
||||
|
||||
use_space_pipeline();
|
||||
apply_fog();
|
||||
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.uncolored_mesh_batches); ++i) {
|
||||
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) {
|
||||
finally_draw_billboard_batch(&ctx.billboard_batches[i].value, ctx.billboard_batches[i].key);
|
||||
}
|
||||
|
||||
pop_fog();
|
||||
}
|
||||
|
||||
pop_fog();
|
||||
render_skybox(); /* after everything else, as to use depth buffer for early z rejection */
|
||||
}
|
||||
|
||||
|
||||
@ -357,7 +366,6 @@ void render(void) {
|
||||
|
||||
start_render_frame(); {
|
||||
render_space();
|
||||
render_skybox(); /* after space, as to use depth buffer for early z rejection */
|
||||
render_2d();
|
||||
} end_render_frame();
|
||||
}
|
||||
@ -370,6 +378,7 @@ void draw_camera(Vec3 position, float fov, Vec3 up, Vec3 direction) {
|
||||
.target = direction,
|
||||
.up = up,
|
||||
};
|
||||
|
||||
camera_projection_matrix = camera_perspective(&camera);
|
||||
camera_look_at_matrix = camera_look_at(&camera);
|
||||
}
|
||||
@ -378,9 +387,11 @@ void draw_camera(Vec3 position, float fov, Vec3 up, Vec3 direction) {
|
||||
/* TODO: https://stackoverflow.com/questions/62493770/how-to-add-roll-in-camera-class */
|
||||
DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position, float fov, float roll, float pitch, float yaw) {
|
||||
(void)roll;
|
||||
|
||||
float yawc, yaws, pitchc, pitchs;
|
||||
sincosf(yaw, &yaws, &yawc);
|
||||
sincosf(pitch, &pitchs, &pitchc);
|
||||
|
||||
Camera const camera = {
|
||||
.fov = fov,
|
||||
.pos = position,
|
||||
@ -391,6 +402,7 @@ DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position,
|
||||
})),
|
||||
.up = (Vec3){0, 1, 0},
|
||||
};
|
||||
|
||||
camera_projection_matrix = camera_perspective(&camera);
|
||||
camera_look_at_matrix = camera_look_at(&camera);
|
||||
|
||||
@ -520,364 +532,3 @@ void issue_deferred_draw_commands(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void render_circle(const CirclePrimitive *circle) {
|
||||
static Vec2 vertices[CIRCLE_VERTICES_MAX];
|
||||
static int prev_num_vertices = 0;
|
||||
static Vec2 prev_position = {0};
|
||||
|
||||
int const num_vertices = MIN((int)circle->radius, CIRCLE_VERTICES_MAX);
|
||||
|
||||
if (prev_num_vertices != num_vertices) {
|
||||
create_circle_geometry(circle->position,
|
||||
circle->radius,
|
||||
num_vertices,
|
||||
vertices);
|
||||
prev_num_vertices = num_vertices;
|
||||
prev_position = circle->position;
|
||||
} else {
|
||||
/* reuse the data, but offset it by difference with previously generated position */
|
||||
/* no evil cos sin ops this way, if radius is shared in sequential calls */
|
||||
Vec2 const d = { prev_position.x - circle->position.x, prev_position.y - circle->position.y };
|
||||
for (int i = 0; i < num_vertices; ++i)
|
||||
vertices[i] = (Vec2){ vertices[i].x - d.x, vertices[i].y - d.y };
|
||||
prev_position = circle->position;
|
||||
}
|
||||
|
||||
VertexBuffer buffer = get_scratch_vertex_array();
|
||||
specify_vertex_buffer(buffer, vertices, sizeof (Vec2) * num_vertices);
|
||||
|
||||
DeferredCommandDraw command = {0};
|
||||
|
||||
command.vertices = (AttributeArrayPointer) {
|
||||
.arity = 2,
|
||||
.type = GL_FLOAT,
|
||||
.stride = sizeof (Vec2),
|
||||
.offset = 0,
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
command.constant_colored = true;
|
||||
command.color = circle->color;
|
||||
|
||||
command.element_buffer = get_circle_element_buffer();
|
||||
command.element_count = (num_vertices - 2) * 3;
|
||||
command.range_end = (num_vertices - 2) * 3;
|
||||
|
||||
use_texture_mode(circle->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY);
|
||||
|
||||
DeferredCommand final_command = {
|
||||
.type = DEFERRED_COMMAND_TYPE_DRAW,
|
||||
.draw = command
|
||||
};
|
||||
|
||||
arrpush(deferred_commands, final_command);
|
||||
}
|
||||
|
||||
|
||||
void finally_render_quads(const Primitive2D primitives[],
|
||||
const struct QuadBatch batch,
|
||||
const VertexBuffer buffer)
|
||||
{
|
||||
DeferredCommandDraw command = {0};
|
||||
|
||||
GLsizei 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 = GL_FLOAT,
|
||||
.stride = off,
|
||||
.offset = voff,
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
if (batch.textured)
|
||||
command.texcoords = (AttributeArrayPointer) {
|
||||
.arity = 2,
|
||||
.type = GL_FLOAT,
|
||||
.stride = off,
|
||||
.offset = uvoff,
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
if (!batch.constant_colored) {
|
||||
command.colors = (AttributeArrayPointer) {
|
||||
.arity = 4,
|
||||
.type = GL_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 * (GLsizei)batch.size;
|
||||
command.range_end = 6 * (GLsizei)batch.size;
|
||||
|
||||
use_texture_mode(batch.mode);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch,
|
||||
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 buffer_element = {
|
||||
.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,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
|
||||
} else if (batch.constant_colored && batch.textured) {
|
||||
ElementIndexedQuadWithoutColor const buffer_element = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
.uv0 = uv0,
|
||||
.uv1 = uv1,
|
||||
.uv2 = uv2,
|
||||
.uv3 = uv3,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
|
||||
} else if (!batch.constant_colored && !batch.textured) {
|
||||
ElementIndexedQuadWithoutTexture const buffer_element = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
|
||||
/* equal for all (flat shaded) */
|
||||
.c0 = color,
|
||||
// .c1 = color,
|
||||
.c2 = color,
|
||||
// .c3 = color,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
|
||||
} else if (batch.constant_colored && !batch.textured) {
|
||||
ElementIndexedQuadWithoutColorWithoutTexture const buffer_element = {
|
||||
.v0 = v0,
|
||||
.v1 = v1,
|
||||
.v2 = v2,
|
||||
.v3 = v3,
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
}
|
||||
|
||||
SDL_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch,
|
||||
const TextureKey texture_key,
|
||||
const VertexBuffer buffer)
|
||||
{
|
||||
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) {
|
||||
UncoloredSpaceTriangle *payload =
|
||||
&((UncoloredSpaceTriangle *)(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;
|
||||
}
|
||||
|
||||
specify_vertex_buffer(buffer, batch->primitives, primitives_len * sizeof (UncoloredSpaceTriangle));
|
||||
|
||||
DeferredCommandDraw command = {0};
|
||||
|
||||
command.vertices = (AttributeArrayPointer) {
|
||||
.arity = 3,
|
||||
.type = GL_FLOAT,
|
||||
.stride = offsetof(UncoloredSpaceTriangle, v1),
|
||||
.offset = offsetof(UncoloredSpaceTriangle, v0),
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
command.texcoords = (AttributeArrayPointer) {
|
||||
.arity = 2,
|
||||
.type = GL_FLOAT,
|
||||
.stride = offsetof(UncoloredSpaceTriangle, v1),
|
||||
.offset = offsetof(UncoloredSpaceTriangle, uv0),
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
command.textured = true;
|
||||
command.texture_key = texture_key;
|
||||
|
||||
command.primitive_count = (GLsizei)(3 * primitives_len);
|
||||
|
||||
DeferredCommand final_command = {
|
||||
.type = DEFERRED_COMMAND_TYPE_DRAW,
|
||||
.draw = command
|
||||
};
|
||||
|
||||
arrpush(deferred_commands, final_command);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool push_text_payload_to_vertex_buffer_builder(FontData const *font_data,
|
||||
VertexBufferBuilder *builder,
|
||||
stbtt_aligned_quad quad)
|
||||
{
|
||||
(void)font_data;
|
||||
|
||||
ElementIndexedQuadWithoutColor buffer_element = {
|
||||
.v0 = (Vec2){ quad.x0, quad.y0 },
|
||||
.v1 = (Vec2){ quad.x1, quad.y0 },
|
||||
.v2 = (Vec2){ quad.x1, quad.y1 },
|
||||
.v3 = (Vec2){ quad.x0, quad.y1 },
|
||||
|
||||
.uv0 = (Vec2){ quad.s0, quad.t0 },
|
||||
.uv1 = (Vec2){ quad.s1, quad.t0 },
|
||||
.uv2 = (Vec2){ quad.s1, quad.t1 },
|
||||
.uv3 = (Vec2){ quad.s0, quad.t1 },
|
||||
};
|
||||
|
||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_text(FontData const *font_data,
|
||||
size_t len,
|
||||
Color color,
|
||||
VertexBuffer buffer)
|
||||
{
|
||||
DeferredCommandDraw command = {0};
|
||||
|
||||
command.vertices = (AttributeArrayPointer) {
|
||||
.arity = 2,
|
||||
.type = GL_FLOAT,
|
||||
.stride = offsetof(ElementIndexedQuadWithoutColor, v1),
|
||||
.offset = offsetof(ElementIndexedQuadWithoutColor, v0),
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
command.texcoords = (AttributeArrayPointer) {
|
||||
.arity = 2,
|
||||
.type = GL_FLOAT,
|
||||
.stride = offsetof(ElementIndexedQuadWithoutColor, v1),
|
||||
.offset = offsetof(ElementIndexedQuadWithoutColor, uv0),
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
command.constant_colored = true;
|
||||
command.color = color;
|
||||
|
||||
command.gpu_texture = font_data->texture;
|
||||
command.uses_gpu_key = true;
|
||||
command.textured = true;
|
||||
|
||||
command.element_buffer = get_quad_element_buffer();
|
||||
command.element_count = 6 * (GLsizei)len;
|
||||
command.range_end = 6 * (GLsizei)len;
|
||||
|
||||
use_texture_mode(TEXTURE_MODE_GHOSTLY);
|
||||
|
||||
DeferredCommand final_command = {
|
||||
.type = DEFERRED_COMMAND_TYPE_DRAW,
|
||||
.draw = command
|
||||
};
|
||||
|
||||
arrpush(deferred_commands, final_command);
|
||||
|
||||
/* TODO: why doesn't it get restored if not placed here? */
|
||||
// glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
|
||||
size_t get_text_payload_size(void) {
|
||||
return sizeof (ElementIndexedQuadWithoutColor);
|
||||
}
|
||||
|
Reference in New Issue
Block a user