From c0b26d5e4d629d9ccae30e3452cd1e38e60caccd Mon Sep 17 00:00:00 2001 From: RandyGaul Date: Sat, 29 Jun 2024 19:03:46 -0700 Subject: [PATCH] Optional vertex modulation in draw API --- docs/api_reference.md | 3 + docs/draw/cf_draw_set_vertex_callback.md | 34 +++++++++ docs/draw/cf_vertex.md | 38 ++++++++++ docs/draw/cf_vertexfn.md | 33 +++++++++ include/cute_draw.h | 91 ++++++++++++++++++++++++ src/cute_draw.cpp | 61 +++++++++------- src/internal/cute_draw_internal.h | 21 +----- 7 files changed, 236 insertions(+), 45 deletions(-) create mode 100644 docs/draw/cf_draw_set_vertex_callback.md create mode 100644 docs/draw/cf_vertex.md create mode 100644 docs/draw/cf_vertexfn.md diff --git a/docs/api_reference.md b/docs/api_reference.md index eed491b8a..1f554d463 100644 --- a/docs/api_reference.md +++ b/docs/api_reference.md @@ -348,6 +348,7 @@ This is a list of all functions in Cute Framework organized by categories. This - [cf_draw_quad2](/draw/cf_draw_quad2.md) - [cf_draw_quad_fill](/draw/cf_draw_quad_fill.md) - [cf_draw_quad_fill2](/draw/cf_draw_quad_fill2.md) +- [cf_draw_set_vertex_callback](/draw/cf_draw_set_vertex_callback.md) - [cf_draw_sprite](/draw/cf_draw_sprite.md) - [cf_draw_tri](/draw/cf_draw_tri.md) - [cf_draw_tri_fill](/draw/cf_draw_tri_fill.md) @@ -373,10 +374,12 @@ This is a list of all functions in Cute Framework organized by categories. This - [cf_render_settings_push_viewport](/draw/cf_render_settings_push_viewport.md) - [cf_render_settings_set_atlas_dimensions](/draw/cf_render_settings_set_atlas_dimensions.md) - [cf_render_to](/draw/cf_render_to.md) +- [CF_VertexFn](/draw/cf_vertexfn.md) ### structs - [CF_TemporaryImage](/draw/cf_temporaryimage.md) +- [CF_Vertex](/draw/cf_vertex.md) ## graphics diff --git a/docs/draw/cf_draw_set_vertex_callback.md b/docs/draw/cf_draw_set_vertex_callback.md new file mode 100644 index 000000000..2a365675d --- /dev/null +++ b/docs/draw/cf_draw_set_vertex_callback.md @@ -0,0 +1,34 @@ +[](../header.md ':include') + +# cf_draw_set_vertex_callback + +Category: [draw](/api_reference?id=draw) +GitHub: [cute_draw.h](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_draw.h) +--- + +An optional callback for modifying vertices before they are sent to the GPU. + +```cpp +void cf_draw_set_vertex_callback(CF_VertexFn* vertex_fn); +``` + +## Remarks + +Setup this callback to apply per-vertex modulations for implementing advanced graphical effects. +`Count` is always a multiple of three, as this function always processes large batched arrays of +triangles. Since all shapes are rendered with signed-distance functions, most shapes merely generate +a single quad, so you may find triangle counts lower than originally anticipated. + +Call [cf_draw_set_vertex_callback](/draw/cf_draw_set_vertex_callback.md) to setup your callback. + +There is no adjecancy info provided. If you need to know which triangles connect to others you +should probably redesign your feature to not require adjecancy information, or use your own custom +rendering solution. With a custom solution you may use low-level graphics in cute_graphics.h, where +any adjacency info can be controlled 100% by you a-priori. + +## Related Pages + +[CF_Vertex](/draw/cf_vertex.md) +[CF_VertexFn](/draw/cf_vertexfn.md) +cf_draw_push_vertex_callback +cf_draw_pop_vertex_callback diff --git a/docs/draw/cf_vertex.md b/docs/draw/cf_vertex.md new file mode 100644 index 000000000..f329d2f08 --- /dev/null +++ b/docs/draw/cf_vertex.md @@ -0,0 +1,38 @@ +[](../header.md ':include') + +# CF_Vertex + +Category: [draw](/api_reference?id=draw) +GitHub: [cute_draw.h](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_draw.h) +--- + +The full vertex layout CF uses just before sending verts to the GPU. + +Struct Members | Description +--- | --- +`CF_V2 p` | World space position. +`CF_V2 posH` | "Homogenous" position transformed by the camera. +`CF_V2 a, b, c` | For internal use -- used in signed-distance functions for rendering shapes. +`CF_V2 uv` | For internal use -- used for sprite rendering. +`CF_Pixel color` | Color for rendering shapes (ignored for sprites). +`float radius` | For internal use -- Used for applying "chubbiness" factor for shapes, or radii on circle/capsule. +`float stroke` | For internal use -- Used for shape rendering for border style stroke rendering (no fill). +`float aa` | For internal use -- Factor for the size of antialiasing. +`uint8_t type` | For internal use -- The type of shape to be rendered, used by the signed-distance functions within CF's internal fragment shader. +`uint8_t alpha` | Used for the alpha-component (transparency). +`uint8_t fill` | For internal use -- Whether or not to render shapes as filled or strokedx. +`uint8_t not_used` | For internal use -- Reserved for a future purpose, simply fulfills byte alignment for now. +`CF_Color attributes` | Four general purpose floats passed into custom user shaders. + +## Remarks + +You may fill in vertices via callback by `cf_draw_push_vertex_callback`. See [CF_VertexFn](/draw/cf_vertexfn.md). +This is useful when you need to fill in unique `attributes` per-vertex, or modify any other +bits of the vertex before rendering. This could be used to implement features like dynamically +generated UV's for shape slicing, or complex lighting systems. + +## Related Pages + +cf_draw_pop_vertex_callback +[CF_VertexFn](/draw/cf_vertexfn.md) +cf_draw_push_vertex_callback diff --git a/docs/draw/cf_vertexfn.md b/docs/draw/cf_vertexfn.md new file mode 100644 index 000000000..8d078f297 --- /dev/null +++ b/docs/draw/cf_vertexfn.md @@ -0,0 +1,33 @@ +[](../header.md ':include') + +# CF_VertexFn + +Category: [draw](/api_reference?id=draw) +GitHub: [cute_draw.h](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_draw.h) +--- + +An optional callback for modifying vertices before they are sent to the GPU. + +```cpp +typedef void (CF_VertexFn)(CF_Vertex* verts, int count); +``` + +## Remarks + +Setup this callback to apply per-vertex modulations for implementing advanced graphical effects. +`Count` is always a multiple of three, as this function always processes large batched arrays of +triangles. Since all shapes are rendered with signed-distance functions, most shapes merely generate +a single quad, so you may find triangle counts lower than originally anticipated. + +Call [cf_draw_set_vertex_callback](/draw/cf_draw_set_vertex_callback.md) to setup your callback. + +There is no adjecancy info provided. If you need to know which triangles connect to others you +should probably redesign your feature to not require adjecancy information, or use your own custom +rendering solution. With a custom solution you may use low-level graphics in cute_graphics.h, where +any adjacency info can be controlled 100% by you a-priori. + +## Related Pages + +[CF_Vertex](/draw/cf_vertex.md) +cf_draw_pop_vertex_callback +cf_draw_push_vertex_callback diff --git a/include/cute_draw.h b/include/cute_draw.h index 0c5cc26e8..a3236110d 100644 --- a/include/cute_draw.h +++ b/include/cute_draw.h @@ -503,6 +503,97 @@ CF_API CF_Color CF_CALL cf_draw_pop_vertex_attributes(); */ CF_API CF_Color CF_CALL cf_draw_peek_vertex_attributes(); +/** + * @struct CF_Vertex + * @category draw + * @brief The full vertex layout CF uses just before sending verts to the GPU. + * @remarks You may fill in vertices via callback by `cf_draw_push_vertex_callback`. See `CF_VertexFn`. + * This is useful when you need to fill in unique `attributes` per-vertex, or modify any other + * bits of the vertex before rendering. This could be used to implement features like dynamically + * generated UV's for shape slicing, or complex lighting systems. + * @related CF_Vertex CF_VertexFn cf_draw_push_vertex_callback cf_draw_pop_vertex_callback + */ +typedef struct CF_Vertex +{ + /* @member World space position. */ + CF_V2 p; + + /* @member "Homogenous" position transformed by the camera. */ + CF_V2 posH; + + /* @member For internal use -- used in signed-distance functions for rendering shapes. */ + CF_V2 a, b, c; + + /* @member For internal use -- used for sprite rendering. */ + CF_V2 uv; + + /* @member Color for rendering shapes (ignored for sprites). */ + CF_Pixel color; + + /* @member For internal use -- Used for applying "chubbiness" factor for shapes, or radii on circle/capsule. */ + float radius; + + /* @member For internal use -- Used for shape rendering for border style stroke rendering (no fill). */ + float stroke; + + /* @member For internal use -- Factor for the size of antialiasing. */ + float aa; + + /* @member For internal use -- The type of shape to be rendered, used by the signed-distance functions within CF's internal fragment shader. */ + uint8_t type; + + /* @member Used for the alpha-component (transparency). */ + uint8_t alpha; + + /* @member For internal use -- Whether or not to render shapes as filled or strokedx. */ + uint8_t fill; + + /* @member For internal use -- Reserved for a future purpose, simply fulfills byte alignment for now. */ + uint8_t not_used; + + /* @member Four general purpose floats passed into custom user shaders. */ + CF_Color attributes; +} CF_Vertex; +// @end + +/** + * @function CF_VertexFn + * @category draw + * @brief An optional callback for modifying vertices before they are sent to the GPU. + * @remarks Setup this callback to apply per-vertex modulations for implementing advanced graphical effects. + * `Count` is always a multiple of three, as this function always processes large batched arrays of + * triangles. Since all shapes are rendered with signed-distance functions, most shapes merely generate + * a single quad, so you may find triangle counts lower than originally anticipated. + * + * Call `cf_draw_set_vertex_callback` to setup your callback. + * + * There is no adjecancy info provided. If you need to know which triangles connect to others you + * should probably redesign your feature to not require adjecancy information, or use your own custom + * rendering solution. With a custom solution you may use low-level graphics in cute_graphics.h, where + * any adjacency info can be controlled 100% by you a-priori. + * @related CF_Vertex CF_VertexFn cf_draw_push_vertex_callback cf_draw_pop_vertex_callback + */ +typedef void (CF_VertexFn)(CF_Vertex* verts, int count); + +/** + * @function cf_draw_set_vertex_callback + * @category draw + * @brief An optional callback for modifying vertices before they are sent to the GPU. + * @remarks Setup this callback to apply per-vertex modulations for implementing advanced graphical effects. + * `Count` is always a multiple of three, as this function always processes large batched arrays of + * triangles. Since all shapes are rendered with signed-distance functions, most shapes merely generate + * a single quad, so you may find triangle counts lower than originally anticipated. + * + * Call `cf_draw_set_vertex_callback` to setup your callback. + * + * There is no adjecancy info provided. If you need to know which triangles connect to others you + * should probably redesign your feature to not require adjecancy information, or use your own custom + * rendering solution. With a custom solution you may use low-level graphics in cute_graphics.h, where + * any adjacency info can be controlled 100% by you a-priori. + * @related CF_Vertex CF_VertexFn cf_draw_push_vertex_callback cf_draw_pop_vertex_callback + */ +CF_API void CF_CALL cf_draw_set_vertex_callback(CF_VertexFn* vertex_fn); + /** * @function cf_make_font * @category text diff --git a/src/cute_draw.cpp b/src/cute_draw.cpp index a9eb08984..e138ed274 100644 --- a/src/cute_draw.cpp +++ b/src/cute_draw.cpp @@ -111,13 +111,13 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ CF_UNUSED(udata); int vert_count = 0; draw->verts.ensure_count(count * 6); - DrawVertex* verts = draw->verts.data(); - CF_MEMSET(verts, 0, sizeof(DrawVertex) * count * 6); + CF_Vertex* verts = draw->verts.data(); + CF_MEMSET(verts, 0, sizeof(CF_Vertex) * count * 6); for (int i = 0; i < count; ++i) { spritebatch_sprite_t* s = sprites + i; BatchGeometry geom = s->geom; - DrawVertex* out = verts + vert_count; + CF_Vertex* out = verts + vert_count; v2 quad[6] = { geom.box[0], @@ -137,7 +137,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ geom.boxH[2], }; - switch (s->geom.type) { + switch (geom.type) { case BATCH_GEOMETRY_TYPE_TRI: { for (int i = 0; i < 3; ++i) { @@ -148,7 +148,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); out[i].fill = 255; out[i].aa = 0; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } out[0].posH = geom.a; @@ -173,7 +173,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].type = VA_TYPE_TRIANGLE_SDF; out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); out[i].fill = s->geom.fill ? 255 : 0; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } vert_count += 6; @@ -192,7 +192,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].type = VA_TYPE_BOX; out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); out[i].fill = s->geom.fill ? 255 : 0; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } out[0].p = quad[0]; @@ -268,7 +268,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ CF_ASSERT(false); } out[i].color = s->geom.color; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } out[0].posH = geom.a; @@ -314,7 +314,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].type = VA_TYPE_SEGMENT; out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); out[i].fill = s->geom.fill ? 255 : 0; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } vert_count += 6; @@ -333,7 +333,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].type = VA_TYPE_SEGMENT; out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); out[i].fill = s->geom.fill ? 255 : 0; - out[i].user_params = geom.user_params; + out[i].attributes = geom.user_params; } out[0].p = geom.box[0]; @@ -349,6 +349,11 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ } } + // Allow users to optionally modulate vertices. + if (draw->vertex_fn) { + draw->vertex_fn(verts, vert_count); + } + // Apply viewport. Rect viewport = draw->viewports.last(); if (viewport.w >= 0 && viewport.h >= 0) { @@ -420,41 +425,41 @@ void cf_make_draw() CF_VertexAttribute attrs[12] = { }; attrs[0].name = "in_pos"; attrs[0].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[0].offset = CF_OFFSET_OF(DrawVertex, p); + attrs[0].offset = CF_OFFSET_OF(CF_Vertex, p); attrs[1].name = "in_posH"; attrs[1].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[1].offset = CF_OFFSET_OF(DrawVertex, posH); + attrs[1].offset = CF_OFFSET_OF(CF_Vertex, posH); attrs[2].name = "in_a"; attrs[2].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[2].offset = CF_OFFSET_OF(DrawVertex, a); + attrs[2].offset = CF_OFFSET_OF(CF_Vertex, a); attrs[3].name = "in_b"; attrs[3].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[3].offset = CF_OFFSET_OF(DrawVertex, b); + attrs[3].offset = CF_OFFSET_OF(CF_Vertex, b); attrs[4].name = "in_c"; attrs[4].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[4].offset = CF_OFFSET_OF(DrawVertex, c); + attrs[4].offset = CF_OFFSET_OF(CF_Vertex, c); attrs[5].name = "in_uv"; attrs[5].format = CF_VERTEX_FORMAT_FLOAT2; - attrs[5].offset = CF_OFFSET_OF(DrawVertex, uv); + attrs[5].offset = CF_OFFSET_OF(CF_Vertex, uv); attrs[6].name = "in_col"; attrs[6].format = CF_VERTEX_FORMAT_UBYTE4N; - attrs[6].offset = CF_OFFSET_OF(DrawVertex, color); + attrs[6].offset = CF_OFFSET_OF(CF_Vertex, color); attrs[7].name = "in_radius"; attrs[7].format = CF_VERTEX_FORMAT_FLOAT; - attrs[7].offset = CF_OFFSET_OF(DrawVertex, radius); + attrs[7].offset = CF_OFFSET_OF(CF_Vertex, radius); attrs[8].name = "in_stroke"; attrs[8].format = CF_VERTEX_FORMAT_FLOAT; - attrs[8].offset = CF_OFFSET_OF(DrawVertex, stroke); + attrs[8].offset = CF_OFFSET_OF(CF_Vertex, stroke); attrs[9].name = "in_aa"; attrs[9].format = CF_VERTEX_FORMAT_FLOAT; - attrs[9].offset = CF_OFFSET_OF(DrawVertex, aa); + attrs[9].offset = CF_OFFSET_OF(CF_Vertex, aa); attrs[10].name = "in_params"; attrs[10].format = CF_VERTEX_FORMAT_UBYTE4N; - attrs[10].offset = CF_OFFSET_OF(DrawVertex, type); + attrs[10].offset = CF_OFFSET_OF(CF_Vertex, type); attrs[11].name = "in_user_params"; attrs[11].format = CF_VERTEX_FORMAT_FLOAT4; - attrs[11].offset = CF_OFFSET_OF(DrawVertex, user_params); - cf_mesh_set_attributes(draw->mesh, attrs, CF_ARRAY_SIZE(attrs), sizeof(DrawVertex), 0); + attrs[11].offset = CF_OFFSET_OF(CF_Vertex, attributes); + cf_mesh_set_attributes(draw->mesh, attrs, CF_ARRAY_SIZE(attrs), sizeof(CF_Vertex), 0); // Shaders. draw->shaders.add(CF_MAKE_SOKOL_SHADER(sprite_shader)); @@ -693,10 +698,6 @@ static void s_draw_capsule(v2 a, v2 b, float stroke, float radius, bool fill) s.geom.type = BATCH_GEOMETRY_TYPE_CAPSULE; s_bounding_box_of_capsule(a, b, radius, stroke, s.geom.box); - s.geom.box[0] = s.geom.box[0]; - s.geom.box[1] = s.geom.box[1]; - s.geom.box[2] = s.geom.box[2]; - s.geom.box[3] = s.geom.box[3]; s.geom.boxH[0] = mul(m, s.geom.box[0]); s.geom.boxH[1] = mul(m, s.geom.box[1]); s.geom.boxH[2] = mul(m, s.geom.box[2]); @@ -713,6 +714,7 @@ static void s_draw_capsule(v2 a, v2 b, float stroke, float radius, bool fill) s.geom.aa = draw->aaf * draw->antialias.last(); s.geom.user_params = draw->user_params.last(); s.sort_bits = draw->layers.last(); + spritebatch_push(&draw->sb, s); } @@ -2157,6 +2159,11 @@ CF_Color cf_draw_peek_vertex_attributes() return draw->user_params.last(); } +void cf_draw_set_vertex_callback(CF_VertexFn* vertex_fn) +{ + draw->vertex_fn = vertex_fn; +} + void cf_render_settings_filter(CF_Filter filter) { draw->filter = filter; diff --git a/src/internal/cute_draw_internal.h b/src/internal/cute_draw_internal.h index 0c0aaf701..77250b13f 100644 --- a/src/internal/cute_draw_internal.h +++ b/src/internal/cute_draw_internal.h @@ -10,6 +10,7 @@ #include #include +#include #include @@ -49,23 +50,6 @@ struct BatchGeometry #include -struct DrawVertex -{ - CF_V2 p; - CF_V2 posH; - CF_V2 a, b, c; - CF_V2 uv; - CF_Pixel color; - float radius; - float stroke; - float aa; - uint8_t type; // r - uint8_t alpha; // g - uint8_t fill; // b - uint8_t not_used; // a - CF_Color user_params; -}; - struct CF_Strike { CF_V2 p0, p1; @@ -106,7 +90,7 @@ struct CF_Draw float cam_rotation = 0; float aaf = 0; Cute::Array temp; - Cute::Array verts; + Cute::Array verts; Cute::Array font_sizes = { 18 }; Cute::Array fonts = { NULL }; Cute::Array blurs = { 0 }; @@ -114,6 +98,7 @@ struct CF_Draw Cute::Array text_clip_boxes = { cf_make_aabb(cf_v2(-FLT_MAX, -FLT_MAX), cf_v2(FLT_MAX, FLT_MAX)) }; Cute::Array vertical = { false }; Cute::Array strikes; + CF_VertexFn* vertex_fn = NULL; }; void cf_make_draw();