rendering: use sprite batching techniques for rect primitives, unite their render path
This commit is contained in:
parent
82bad550e5
commit
b295c5920c
@ -104,6 +104,7 @@ set(TWN_NONOPT_SOURCE_FILES
|
|||||||
|
|
||||||
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
||||||
src/rendering/twn_sprites.c
|
src/rendering/twn_sprites.c
|
||||||
|
src/rendering/twn_rects.c
|
||||||
src/rendering/twn_text.c
|
src/rendering/twn_text.c
|
||||||
src/rendering/twn_triangles.c
|
src/rendering/twn_triangles.c
|
||||||
src/rendering/twn_circles.c
|
src/rendering/twn_circles.c
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#ifndef TWN_TEXTURES_MODES_H
|
#ifndef TWN_TEXTURES_MODES_H
|
||||||
#define TWN_TEXTURES_MODES_H
|
#define TWN_TEXTURES_MODES_H
|
||||||
|
|
||||||
|
/* TODO: rename, as it doesn't have to be about textures only, but blending */
|
||||||
|
/* TODO: move from public /include/ tree */
|
||||||
|
|
||||||
/* alpha channel information */
|
/* alpha channel information */
|
||||||
typedef enum TextureMode {
|
typedef enum TextureMode {
|
||||||
TEXTURE_MODE_OPAQUE, /* all pixels are solid */
|
TEXTURE_MODE_OPAQUE, /* all pixels are solid */
|
||||||
|
@ -35,22 +35,6 @@ void render_queue_clear(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* rectangle */
|
|
||||||
void draw_rectangle(Rect rect, Color color) {
|
|
||||||
RectPrimitive rectangle = {
|
|
||||||
.rect = rect,
|
|
||||||
.color = color,
|
|
||||||
};
|
|
||||||
|
|
||||||
Primitive2D primitive = {
|
|
||||||
.type = PRIMITIVE_2D_RECT,
|
|
||||||
.rect = rectangle,
|
|
||||||
};
|
|
||||||
|
|
||||||
arrput(ctx.render_queue_2d, primitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void draw_9slice(const char *texture_path, int texture_w, int texture_h, int border_thickness, Rect rect, Color color) {
|
void draw_9slice(const char *texture_path, int texture_w, int texture_h, int border_thickness, Rect rect, Color color) {
|
||||||
const float bt = (float)border_thickness; /* i know! */
|
const float bt = (float)border_thickness; /* i know! */
|
||||||
const float bt2 = bt * 2; /* combined size of the two borders in an axis */
|
const float bt2 = bt * 2; /* combined size of the two borders in an axis */
|
||||||
@ -204,19 +188,27 @@ static void render_2d(void) {
|
|||||||
|
|
||||||
switch (current->type) {
|
switch (current->type) {
|
||||||
case PRIMITIVE_2D_SPRITE: {
|
case PRIMITIVE_2D_SPRITE: {
|
||||||
const struct SpriteBatch batch =
|
const struct QuadBatch batch =
|
||||||
collect_sprite_batch(current, render_queue_len - i);
|
collect_sprite_batch(current, render_queue_len - i);
|
||||||
|
|
||||||
/* TODO: what's even the point? just use OR_EQUAL comparison */
|
/* TODO: what's even the point? just use OR_EQUAL comparison */
|
||||||
set_depth_range((double)batch_count / UINT16_MAX, 1.0);
|
set_depth_range((double)batch_count / UINT16_MAX, 1.0);
|
||||||
render_sprites(current, batch);
|
render_sprite_batch(current, batch);
|
||||||
|
|
||||||
i += batch.size - 1; ++batch_count;
|
i += batch.size - 1; ++batch_count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PRIMITIVE_2D_RECT:
|
case PRIMITIVE_2D_RECT: {
|
||||||
render_rectangle(¤t->rect);
|
const struct QuadBatch batch =
|
||||||
|
collect_rect_batch(current, render_queue_len - i);
|
||||||
|
|
||||||
|
/* TODO: what's even the point? just use OR_EQUAL comparison */
|
||||||
|
set_depth_range((double)batch_count / UINT16_MAX, 1.0);
|
||||||
|
render_rect_batch(current, batch);
|
||||||
|
|
||||||
|
i += batch.size - 1; ++batch_count;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PRIMITIVE_2D_CIRCLE:
|
case PRIMITIVE_2D_CIRCLE:
|
||||||
render_circle(¤t->circle);
|
render_circle(¤t->circle);
|
||||||
break;
|
break;
|
||||||
|
@ -133,15 +133,18 @@ void create_circle_geometry(Vec2 position,
|
|||||||
SDL_Vertex **vertices_out,
|
SDL_Vertex **vertices_out,
|
||||||
int **indices_out);
|
int **indices_out);
|
||||||
|
|
||||||
struct SpriteBatch {
|
struct QuadBatch {
|
||||||
size_t size; /* how many primitives are in current batch */
|
size_t size; /* how many primitives are in current batch */
|
||||||
TextureMode mode;
|
TextureMode mode; /* how color should be applied */
|
||||||
bool constant_colored; /* whether colored batch is uniformly colored */
|
bool constant_colored; /* whether colored batch is uniformly colored */
|
||||||
bool repeat; /* whether repeat is needed */
|
bool repeat; /* whether repeat is needed */
|
||||||
} collect_sprite_batch(const Primitive2D primitives[], size_t len);
|
bool textured;
|
||||||
|
} collect_quad_batch(const Primitive2D primitives[], size_t len);
|
||||||
void render_sprites(const Primitive2D primitives[],
|
void render_quad_batch(const Primitive2D primitives[], struct QuadBatch batch);
|
||||||
const struct SpriteBatch batch);
|
struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len);
|
||||||
|
struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len);
|
||||||
|
void render_sprite_batch(const Primitive2D primitives[], struct QuadBatch batch);
|
||||||
|
void render_rect_batch(const Primitive2D primitives[], struct QuadBatch batch);
|
||||||
|
|
||||||
void draw_uncolored_space_traingle_batch(MeshBatch *batch,
|
void draw_uncolored_space_traingle_batch(MeshBatch *batch,
|
||||||
TextureKey texture_key);
|
TextureKey texture_key);
|
||||||
@ -162,14 +165,14 @@ VertexBuffer create_vertex_buffer(void);
|
|||||||
|
|
||||||
void delete_vertex_buffer(VertexBuffer buffer);
|
void delete_vertex_buffer(VertexBuffer buffer);
|
||||||
|
|
||||||
void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes);
|
void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes);
|
||||||
|
|
||||||
/* uses present in 1.5 buffer mapping feature */
|
/* uses present in 1.5 buffer mapping feature */
|
||||||
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes);
|
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes);
|
||||||
|
|
||||||
/* collects bytes for sending to the gpu until all is pushed, which is when false is returned */
|
/* collects bytes for sending to the gpu until all is pushed, which is when false is returned */
|
||||||
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
|
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
|
||||||
void *bytes,
|
void const *bytes,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
/* state */
|
/* state */
|
||||||
@ -194,15 +197,13 @@ void use_2d_pipeline(void);
|
|||||||
|
|
||||||
void use_texture_mode(TextureMode mode);
|
void use_texture_mode(TextureMode mode);
|
||||||
|
|
||||||
void upload_quad_vertices(Rect rect);
|
void finally_render_quads(Primitive2D const primitives[],
|
||||||
|
struct QuadBatch batch,
|
||||||
void finally_render_sprites(Primitive2D const primitives[],
|
|
||||||
struct SpriteBatch batch,
|
|
||||||
VertexBuffer buffer);
|
VertexBuffer buffer);
|
||||||
|
|
||||||
size_t get_sprite_payload_size(struct SpriteBatch batch);
|
size_t get_quad_payload_size(struct QuadBatch batch);
|
||||||
|
|
||||||
bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
|
bool push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch,
|
||||||
VertexBufferBuilder *builder,
|
VertexBufferBuilder *builder,
|
||||||
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
|
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
|
||||||
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
|
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "twn_util_c.h"
|
#include "twn_util_c.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_text_c.h"
|
#include "twn_text_c.h"
|
||||||
|
#include "twn_types.h"
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
@ -48,6 +49,34 @@ typedef struct ElementIndexedQuadWithoutColor {
|
|||||||
} ElementIndexedQuadWithoutColor;
|
} ElementIndexedQuadWithoutColor;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct ElementIndexedQuadWithoutTexture {
|
||||||
|
/* upper-left */
|
||||||
|
Vec2 v0;
|
||||||
|
Color c0;
|
||||||
|
/* bottom-left */
|
||||||
|
Vec2 v1;
|
||||||
|
Color c1;
|
||||||
|
/* bottom-right */
|
||||||
|
Vec2 v2;
|
||||||
|
Color c2;
|
||||||
|
/* upper-right */
|
||||||
|
Vec2 v3;
|
||||||
|
Color c3;
|
||||||
|
} ElementIndexedQuadWithoutTexture;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct ElementIndexedQuadWithoutColorWithoutTexture {
|
||||||
|
/* upper-left */
|
||||||
|
Vec2 v0;
|
||||||
|
/* bottom-left */
|
||||||
|
Vec2 v1;
|
||||||
|
/* bottom-right */
|
||||||
|
Vec2 v2;
|
||||||
|
/* upper-right */
|
||||||
|
Vec2 v3;
|
||||||
|
} ElementIndexedQuadWithoutColorWithoutTexture;
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PIPELINE_NO,
|
PIPELINE_NO,
|
||||||
PIPELINE_SPACE,
|
PIPELINE_SPACE,
|
||||||
@ -136,33 +165,6 @@ void use_2d_pipeline(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void upload_quad_vertices(Rect rect) {
|
|
||||||
/* client memory needs to be reachable on glDraw*, so */
|
|
||||||
static float vertices[6 * 2];
|
|
||||||
|
|
||||||
vertices[0] = rect.x; vertices[1] = rect.y;
|
|
||||||
vertices[2] = rect.x; vertices[3] = rect.y + rect.h;
|
|
||||||
vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h;
|
|
||||||
vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h;
|
|
||||||
vertices[8] = rect.x + rect.w; vertices[9] = rect.y;
|
|
||||||
vertices[10] = rect.x; vertices[11] = rect.y;
|
|
||||||
|
|
||||||
glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void render_rectangle(const RectPrimitive *rectangle) {
|
|
||||||
glColor4ub(rectangle->color.r, rectangle->color.g,
|
|
||||||
rectangle->color.b, rectangle->color.a);
|
|
||||||
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
upload_quad_vertices(rectangle->rect);
|
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void render_circle(const CirclePrimitive *circle) {
|
void render_circle(const CirclePrimitive *circle) {
|
||||||
SDL_Vertex *vertices = NULL;
|
SDL_Vertex *vertices = NULL;
|
||||||
int *indices = NULL;
|
int *indices = NULL;
|
||||||
@ -258,7 +260,7 @@ VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
|
|||||||
|
|
||||||
|
|
||||||
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
|
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
|
||||||
void *bytes, size_t size) {
|
void const *bytes, size_t size) {
|
||||||
if (builder->bytes_left == 0)
|
if (builder->bytes_left == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -277,24 +279,30 @@ bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void finally_render_sprites(const Primitive2D primitives[],
|
void finally_render_quads(const Primitive2D primitives[],
|
||||||
const struct SpriteBatch batch,
|
const struct QuadBatch batch,
|
||||||
const VertexBuffer buffer)
|
const VertexBuffer buffer)
|
||||||
{
|
{
|
||||||
(void)buffer;
|
(void)buffer;
|
||||||
|
|
||||||
GLsizei off;
|
GLsizei off = 0, voff = 0, uvoff = 0, coff = 0;
|
||||||
GLsizei voff;
|
|
||||||
GLsizei uvoff;
|
|
||||||
|
|
||||||
if (!batch.constant_colored) {
|
if (!batch.constant_colored && batch.textured) {
|
||||||
off = offsetof(ElementIndexedQuad, v1);
|
off = offsetof(ElementIndexedQuad, v1);
|
||||||
voff = offsetof(ElementIndexedQuad, v0);
|
voff = offsetof(ElementIndexedQuad, v0);
|
||||||
uvoff = offsetof(ElementIndexedQuad, uv0);
|
uvoff = offsetof(ElementIndexedQuad, uv0);
|
||||||
} else {
|
coff = offsetof(ElementIndexedQuad, c0);
|
||||||
|
} else if (batch.constant_colored && batch.textured) {
|
||||||
off = offsetof(ElementIndexedQuadWithoutColor, v1);
|
off = offsetof(ElementIndexedQuadWithoutColor, v1);
|
||||||
voff = offsetof(ElementIndexedQuadWithoutColor, v0);
|
voff = offsetof(ElementIndexedQuadWithoutColor, v0);
|
||||||
uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vertex specification */
|
/* vertex specification */
|
||||||
@ -304,65 +312,82 @@ void finally_render_sprites(const Primitive2D primitives[],
|
|||||||
off,
|
off,
|
||||||
(void *)(size_t)voff);
|
(void *)(size_t)voff);
|
||||||
|
|
||||||
|
if (batch.textured) {
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
glClientActiveTexture(GL_TEXTURE0);
|
glClientActiveTexture(GL_TEXTURE0);
|
||||||
glTexCoordPointer(2,
|
glTexCoordPointer(2,
|
||||||
GL_FLOAT,
|
GL_FLOAT,
|
||||||
off,
|
off,
|
||||||
(void *)(size_t)uvoff);
|
(void *)(size_t)uvoff);
|
||||||
|
}
|
||||||
|
|
||||||
if (!batch.constant_colored) {
|
if (!batch.constant_colored) {
|
||||||
glEnableClientState(GL_COLOR_ARRAY);
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
glColorPointer(4,
|
glColorPointer(4,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
off,
|
off,
|
||||||
(void *)offsetof(ElementIndexedQuad, c0));
|
(void *)(size_t)coff);
|
||||||
} else
|
} else
|
||||||
glColor4ub(primitives[0].sprite.color.r,
|
glColor4ub(primitives[0].sprite.color.r,
|
||||||
primitives[0].sprite.color.g,
|
primitives[0].sprite.color.g,
|
||||||
primitives[0].sprite.color.b,
|
primitives[0].sprite.color.b,
|
||||||
primitives[0].sprite.color.a);
|
primitives[0].sprite.color.a);
|
||||||
|
|
||||||
|
if (batch.textured) {
|
||||||
if (!batch.repeat)
|
if (!batch.repeat)
|
||||||
textures_bind(&ctx.texture_cache, primitives->sprite.texture_key);
|
textures_bind(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||||
else
|
else
|
||||||
textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key);
|
textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||||
|
}
|
||||||
|
|
||||||
bind_quad_element_buffer();
|
bind_quad_element_buffer();
|
||||||
|
|
||||||
glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL);
|
glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL);
|
||||||
|
|
||||||
/* clear the state */
|
/* clear the state */
|
||||||
|
{
|
||||||
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
if (batch.textured)
|
||||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
if (!batch.constant_colored)
|
if (!batch.constant_colored)
|
||||||
glDisableClientState(GL_COLOR_ARRAY);
|
glDisableClientState(GL_COLOR_ARRAY);
|
||||||
|
|
||||||
|
if (batch.textured)
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t get_sprite_payload_size(struct SpriteBatch batch) {
|
|
||||||
if (batch.constant_colored)
|
|
||||||
return sizeof (ElementIndexedQuadWithoutColor);
|
|
||||||
else
|
|
||||||
return sizeof (ElementIndexedQuad);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
|
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 (ElementIndexedQuadWithoutTexture);
|
||||||
|
else if (!batch.constant_colored && !batch.textured)
|
||||||
|
return sizeof (ElementIndexedQuadWithoutColorWithoutTexture);
|
||||||
|
|
||||||
|
SDL_assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch,
|
||||||
VertexBufferBuilder *builder,
|
VertexBufferBuilder *builder,
|
||||||
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
|
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
|
||||||
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
|
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
|
||||||
Color color)
|
Color color)
|
||||||
{
|
{
|
||||||
if (!batch.constant_colored) {
|
if (!batch.constant_colored && batch.textured) {
|
||||||
ElementIndexedQuad buffer_element = {
|
ElementIndexedQuad const buffer_element = {
|
||||||
.v0 = v0,
|
.v0 = v0,
|
||||||
.v1 = v1,
|
.v1 = v1,
|
||||||
.v2 = v2,
|
.v2 = v2,
|
||||||
@ -382,8 +407,8 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
|
|||||||
|
|
||||||
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||||
|
|
||||||
} else {
|
} else if (batch.constant_colored && batch.textured) {
|
||||||
ElementIndexedQuadWithoutColor buffer_element = {
|
ElementIndexedQuadWithoutColor const buffer_element = {
|
||||||
.v0 = v0,
|
.v0 = v0,
|
||||||
.v1 = v1,
|
.v1 = v1,
|
||||||
.v2 = v2,
|
.v2 = v2,
|
||||||
@ -395,8 +420,37 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
|
|||||||
.uv3 = uv3,
|
.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);
|
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_assert(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ void delete_vertex_buffer(VertexBuffer buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes) {
|
void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) {
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||||
glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW);
|
||||||
}
|
}
|
||||||
|
30
src/rendering/twn_quads.c
Normal file
30
src/rendering/twn_quads.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "twn_draw_c.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct QuadBatch collect_quad_batch(const Primitive2D primitives[], size_t len) {
|
||||||
|
if (primitives[0].type == PRIMITIVE_2D_SPRITE)
|
||||||
|
return collect_sprite_batch(primitives, len);
|
||||||
|
else if (primitives[0].type == PRIMITIVE_2D_RECT)
|
||||||
|
return collect_rect_batch(primitives, len);
|
||||||
|
else
|
||||||
|
SDL_assert(false);
|
||||||
|
|
||||||
|
return (struct QuadBatch){0};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* assumes that orthogonal matrix setup is done already */
|
||||||
|
void render_quad_batch(const Primitive2D primitives[],
|
||||||
|
const struct QuadBatch batch)
|
||||||
|
{
|
||||||
|
if (primitives[0].type == PRIMITIVE_2D_SPRITE)
|
||||||
|
render_sprite_batch(primitives, batch);
|
||||||
|
else if (primitives[0].type == PRIMITIVE_2D_RECT)
|
||||||
|
render_rect_batch(primitives, batch);
|
||||||
|
else
|
||||||
|
SDL_assert(false);
|
||||||
|
|
||||||
|
return (struct QuadBatch){0};
|
||||||
|
}
|
104
src/rendering/twn_rects.c
Normal file
104
src/rendering/twn_rects.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#include "twn_draw.h"
|
||||||
|
#include "twn_draw_c.h"
|
||||||
|
#include "twn_engine_context_c.h"
|
||||||
|
#include "twn_util.h"
|
||||||
|
#include "twn_util_c.h"
|
||||||
|
#include "twn_textures_c.h"
|
||||||
|
#include "twn_option.h"
|
||||||
|
|
||||||
|
#include <stb_ds.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
|
void draw_rectangle(Rect rect, Color color) {
|
||||||
|
RectPrimitive rectangle = {
|
||||||
|
.rect = rect,
|
||||||
|
.color = color,
|
||||||
|
};
|
||||||
|
|
||||||
|
Primitive2D primitive = {
|
||||||
|
.type = PRIMITIVE_2D_RECT,
|
||||||
|
.rect = rectangle,
|
||||||
|
};
|
||||||
|
|
||||||
|
arrput(ctx.render_queue_2d, primitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len) {
|
||||||
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
|
||||||
|
SDL_assert(primitives && len != 0);
|
||||||
|
|
||||||
|
struct QuadBatch batch = {
|
||||||
|
.mode = primitives[0].rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY,
|
||||||
|
.constant_colored = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].rect.color;
|
||||||
|
|
||||||
|
/* batch size is clamped so that reallocated short indices could be used */
|
||||||
|
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
|
||||||
|
len = QUAD_ELEMENT_BUFFER_LENGTH;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
const Primitive2D *const current = &primitives[i];
|
||||||
|
|
||||||
|
/* don't touch things other than rectangles */
|
||||||
|
if (current->type != PRIMITIVE_2D_RECT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* only collect the same blend modes */
|
||||||
|
if ((current->rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY) != batch.mode)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* if all are modulated the same we can skip sending the color data */
|
||||||
|
if (*(const uint32_t *)¤t->rect.color != uniform_color)
|
||||||
|
batch.constant_colored = false;
|
||||||
|
|
||||||
|
++batch.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* assumes that orthogonal matrix setup is done already */
|
||||||
|
void render_rect_batch(const Primitive2D primitives[],
|
||||||
|
const struct QuadBatch batch)
|
||||||
|
{
|
||||||
|
SDL_assert(primitives && batch.size != 0);
|
||||||
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
|
||||||
|
|
||||||
|
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
|
||||||
|
static VertexBuffer vertex_array = 0;
|
||||||
|
if (vertex_array == 0)
|
||||||
|
vertex_array = create_vertex_buffer();
|
||||||
|
|
||||||
|
use_texture_mode(batch.mode);
|
||||||
|
|
||||||
|
/* vertex population over a vertex buffer builder interface */
|
||||||
|
{
|
||||||
|
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < batch.size; ++i) {
|
||||||
|
/* render opaques front to back, to gain benefit of an early z rejection */
|
||||||
|
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
|
||||||
|
const RectPrimitive rect = primitives[cur].rect;
|
||||||
|
|
||||||
|
Vec2 v0 = { rect.rect.x, rect.rect.y };
|
||||||
|
Vec2 v1 = { rect.rect.x, rect.rect.y + rect.rect.h };
|
||||||
|
Vec2 v2 = { rect.rect.x + rect.rect.w, rect.rect.y + rect.rect.h };
|
||||||
|
Vec2 v3 = { rect.rect.x + rect.rect.w, rect.rect.y };
|
||||||
|
|
||||||
|
push_quad_payload_to_vertex_buffer_builder(
|
||||||
|
batch, &payload,
|
||||||
|
v0, v1, v2, v3,
|
||||||
|
(Vec2){0}, (Vec2){0}, (Vec2){0}, (Vec2){0},
|
||||||
|
rect.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finally_render_quads(primitives, batch, vertex_array);
|
||||||
|
}
|
@ -60,15 +60,18 @@ void draw_sprite_args(const DrawSpriteArgs args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct SpriteBatch collect_sprite_batch(const Primitive2D primitives[], size_t len) {
|
struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len) {
|
||||||
/* assumes that first primitive is already a sprite */
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
|
||||||
|
SDL_assert(primitives && len != 0);
|
||||||
|
|
||||||
const uint16_t texture_key_id = primitives[0].sprite.texture_key.id;
|
const uint16_t texture_key_id = primitives[0].sprite.texture_key.id;
|
||||||
const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key);
|
const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key);
|
||||||
|
|
||||||
struct SpriteBatch batch = {
|
struct QuadBatch batch = {
|
||||||
.mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key),
|
.mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key),
|
||||||
.constant_colored = true,
|
.constant_colored = true,
|
||||||
.repeat = primitives[0].sprite.repeat,
|
.repeat = primitives[0].sprite.repeat,
|
||||||
|
.textured = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].sprite.color;
|
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].sprite.color;
|
||||||
@ -116,9 +119,12 @@ struct SpriteBatch collect_sprite_batch(const Primitive2D primitives[], size_t l
|
|||||||
|
|
||||||
|
|
||||||
/* assumes that orthogonal matrix setup is done already */
|
/* assumes that orthogonal matrix setup is done already */
|
||||||
void render_sprites(const Primitive2D primitives[],
|
void render_sprite_batch(const Primitive2D primitives[],
|
||||||
const struct SpriteBatch batch)
|
const struct QuadBatch batch)
|
||||||
{
|
{
|
||||||
|
SDL_assert(primitives && batch.size != 0);
|
||||||
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
|
||||||
|
|
||||||
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
|
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
|
||||||
static VertexBuffer vertex_array = 0;
|
static VertexBuffer vertex_array = 0;
|
||||||
if (vertex_array == 0)
|
if (vertex_array == 0)
|
||||||
@ -131,10 +137,10 @@ void render_sprites(const Primitive2D primitives[],
|
|||||||
|
|
||||||
/* vertex population over a vertex buffer builder interface */
|
/* vertex population over a vertex buffer builder interface */
|
||||||
{
|
{
|
||||||
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_sprite_payload_size(batch) * batch.size);
|
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
|
||||||
|
|
||||||
for (size_t i = 0; i < batch.size; ++i) {
|
for (size_t i = 0; i < batch.size; ++i) {
|
||||||
/* render opaques front to back */
|
/* render opaques front to back, to gain benefit of an early z rejection */
|
||||||
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
|
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
|
||||||
const SpritePrimitive sprite = primitives[cur].sprite;
|
const SpritePrimitive sprite = primitives[cur].sprite;
|
||||||
|
|
||||||
@ -233,9 +239,9 @@ void render_sprites(const Primitive2D primitives[],
|
|||||||
v3 = (Vec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y };
|
v3 = (Vec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y };
|
||||||
}
|
}
|
||||||
|
|
||||||
push_sprite_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color);
|
push_quad_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finally_render_sprites(primitives, batch, vertex_array);
|
finally_render_quads(primitives, batch, vertex_array);
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,6 @@
|
|||||||
#include "rendering/twn_fog.c"
|
#include "rendering/twn_fog.c"
|
||||||
#include "rendering/twn_skybox.c"
|
#include "rendering/twn_skybox.c"
|
||||||
#include "rendering/twn_sprites.c"
|
#include "rendering/twn_sprites.c"
|
||||||
|
#include "rendering/twn_rects.c"
|
||||||
#include "rendering/twn_text.c"
|
#include "rendering/twn_text.c"
|
||||||
#include "rendering/twn_triangles.c"
|
#include "rendering/twn_triangles.c"
|
||||||
|
Loading…
Reference in New Issue
Block a user