You've already forked flecs_tests
Reorganized the code structure, fix a lot of issues.
This commit is contained in:
250
src/shape.h
250
src/shape.h
@@ -14,11 +14,6 @@ typedef struct shape_uniform_t {
|
||||
uint8_t _pad[12];
|
||||
} shape_uniform_t;
|
||||
|
||||
typedef enum shape_kind_t {
|
||||
SHAPE_CIRCLE,
|
||||
SHAPE_STAR,
|
||||
} shape_kind_t;
|
||||
|
||||
typedef struct shape_t {
|
||||
shape_vertex_t *verts;
|
||||
uint16_t *indices;
|
||||
@@ -30,98 +25,13 @@ typedef struct shape_t {
|
||||
bool hovered;
|
||||
bool selected;
|
||||
|
||||
shape_kind_t kind;
|
||||
float cx, cy;
|
||||
float sx, sy;
|
||||
float rotation;
|
||||
int star_points;
|
||||
float star_inner_ratio;
|
||||
int last_update_frame;
|
||||
} shape_t;
|
||||
|
||||
#define SHAPE_HOVER_PX 6.0f
|
||||
|
||||
static sg_pipeline shape_pipeline;
|
||||
static sg_pipeline overlay_pipeline;
|
||||
static sg_shader shape_shader;
|
||||
static int g_shape_frame_id;
|
||||
|
||||
static void shape_begin_frame(void)
|
||||
{
|
||||
g_shape_frame_id++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the shape shader, shape pipeline (line strip), and overlay pipeline
|
||||
* (triangles). Call once during app init before drawing any shapes.
|
||||
*/
|
||||
static void shape_init_pipeline(void)
|
||||
{
|
||||
shape_shader = sg_make_shader(&(sg_shader_desc) {
|
||||
.vertex_func = {
|
||||
.source = (const char*) src_shaders_shape_wgsl,
|
||||
.entry = "vs_main",
|
||||
},
|
||||
.fragment_func = {
|
||||
.source = (const char*) src_shaders_shape_wgsl,
|
||||
.entry = "fs_main",
|
||||
},
|
||||
.uniform_blocks = {
|
||||
[0] = {
|
||||
.size = sizeof(mat4),
|
||||
.stage = SG_SHADERSTAGE_VERTEX,
|
||||
.wgsl_group0_binding_n = 0,
|
||||
},
|
||||
[1] = {
|
||||
.size = sizeof(shape_uniform_t),
|
||||
.stage = SG_SHADERSTAGE_VERTEX,
|
||||
.wgsl_group0_binding_n = 1,
|
||||
},
|
||||
},
|
||||
.attrs = {
|
||||
[0] = { .base_type = SG_SHADERATTRBASETYPE_FLOAT },
|
||||
},
|
||||
.label = "Shape shader",
|
||||
});
|
||||
|
||||
shape_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
|
||||
.shader = shape_shader,
|
||||
.index_type = SG_INDEXTYPE_UINT16,
|
||||
.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP,
|
||||
.layout.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
},
|
||||
.label = "Shape pipeline",
|
||||
});
|
||||
|
||||
overlay_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
|
||||
.shader = shape_shader,
|
||||
.index_type = SG_INDEXTYPE_UINT16,
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLES,
|
||||
.layout.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
},
|
||||
.label = "Overlay pipeline",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the shape shader and both pipelines. Call during app shutdown.
|
||||
*/
|
||||
static void shape_shutdown_pipeline(void)
|
||||
{
|
||||
sg_destroy_pipeline(shape_pipeline);
|
||||
sg_destroy_pipeline(overlay_pipeline);
|
||||
sg_destroy_shader(shape_shader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of line segments for a circle of the given radius.
|
||||
* Clamped to [8, 128]; scales roughly with circumference.
|
||||
*
|
||||
* @param r circle radius in world units
|
||||
* @return segment count
|
||||
*/
|
||||
static int shape_calc_segments(float r)
|
||||
{
|
||||
int n = (int)(fabsf(r) * 0.5f) + 16;
|
||||
@@ -130,13 +40,6 @@ static int shape_calc_segments(float r)
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default state for a newly created shape: identity transform, base color,
|
||||
* not hovered, not selected.
|
||||
*
|
||||
* @param s shape to initialize
|
||||
* @param color RGBA base color (copied)
|
||||
*/
|
||||
static void shape_init_common(shape_t *s, const float color[4])
|
||||
{
|
||||
s->hovered = false;
|
||||
@@ -146,34 +49,25 @@ static void shape_init_common(shape_t *s, const float color[4])
|
||||
memset(s->uniform._pad, 0, sizeof(s->uniform._pad));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the per-shape transform matrix from cx, cy, rotation.
|
||||
* Uses R(-angle) so the shader's row-vector convention matches the existing
|
||||
* world-space vertex computation.
|
||||
*/
|
||||
static void shape_build_transform(shape_t *s)
|
||||
{
|
||||
mat4 T, R, S, RS;
|
||||
glm_translate_make(T, (vec3){s->cx, s->cy, 0.0f});
|
||||
glm_rotate_make(R, -s->rotation, (vec3){0.0f, 0.0f, 1.0f});
|
||||
glm_rotate_make(R, s->rotation, (vec3){0.0f, 0.0f, 1.0f});
|
||||
glm_scale_make(S, (vec3){s->sx, s->sy, 1.0f});
|
||||
glm_mat4_mul(R, S, RS);
|
||||
glm_mat4_mul(T, RS, s->uniform.transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create GPU vertex and index buffers from the shape's current verts/indices.
|
||||
*
|
||||
* @param s shape (must have verts and indices allocated)
|
||||
*/
|
||||
static void shape_make_buffers(shape_t *s)
|
||||
{
|
||||
uint32_t vcount = s->num_verts + 1;
|
||||
s->vbuf = sg_make_buffer(&(sg_buffer_desc) {
|
||||
.size = s->num_indices * sizeof(shape_vertex_t),
|
||||
.size = (size_t)vcount * sizeof(shape_vertex_t),
|
||||
.usage = { .stream_update = true },
|
||||
.label = "Shape vertices",
|
||||
});
|
||||
sg_update_buffer(s->vbuf, &(sg_range){s->verts, s->num_indices * sizeof(shape_vertex_t)});
|
||||
sg_update_buffer(s->vbuf, &(sg_range){s->verts, (size_t)vcount * sizeof(shape_vertex_t)});
|
||||
s->ibuf = sg_make_buffer(&(sg_buffer_desc) {
|
||||
.usage = { .index_buffer = true },
|
||||
.data = { s->indices, s->num_indices * sizeof(uint16_t) },
|
||||
@@ -181,11 +75,6 @@ static void shape_make_buffers(shape_t *s)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy GPU buffers and free vertex/index arrays for a single shape.
|
||||
*
|
||||
* @param s shape to tear down
|
||||
*/
|
||||
static void shape_shutdown(shape_t *s)
|
||||
{
|
||||
sg_destroy_buffer(s->vbuf);
|
||||
@@ -194,74 +83,11 @@ static void shape_shutdown(shape_t *s)
|
||||
FREE(s->indices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild vertex and index data from the shape's current parameters (position,
|
||||
* scale, rotation, kind), then recreate GPU buffers. Call after any parameter
|
||||
* change.
|
||||
*
|
||||
* @param s shape to regenerate
|
||||
*/
|
||||
static void shape_regenerate(shape_t *s)
|
||||
{
|
||||
int n, count;
|
||||
if (s->kind == SHAPE_CIRCLE) {
|
||||
int segs = shape_calc_segments(s->sx);
|
||||
n = segs;
|
||||
count = segs + 1;
|
||||
} else {
|
||||
n = s->star_points * 2;
|
||||
count = n + 1;
|
||||
}
|
||||
|
||||
bool resized = ((uint32_t)count != s->num_indices);
|
||||
if (resized) {
|
||||
sg_destroy_buffer(s->vbuf);
|
||||
sg_destroy_buffer(s->ibuf);
|
||||
FREE(s->verts);
|
||||
FREE(s->indices);
|
||||
s->verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||
s->indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
if (s->kind == SHAPE_CIRCLE) {
|
||||
int segs = n;
|
||||
for (int i = 0; i < segs; i++) {
|
||||
float a = (float)i / (float)segs * 2.0f * GLM_PIf - GLM_PI_2f;
|
||||
s->verts[i] = (shape_vertex_t) { cosf(a), sinf(a) };
|
||||
}
|
||||
s->verts[segs] = s->verts[0];
|
||||
} else {
|
||||
for (int i = 0; i < n; i++) {
|
||||
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f;
|
||||
float r = (i & 1) ? s->star_inner_ratio : 1.0f;
|
||||
s->verts[i] = (shape_vertex_t) { cosf(a) * r, sinf(a) * r };
|
||||
}
|
||||
s->verts[n] = s->verts[0];
|
||||
}
|
||||
|
||||
s->num_indices = (uint32_t)count;
|
||||
s->num_verts = (uint32_t)n;
|
||||
for (int i = 0; i <= n; i++) s->indices[i] = (uint16_t)i;
|
||||
|
||||
shape_build_transform(s);
|
||||
|
||||
if (resized) {
|
||||
shape_make_buffers(s);
|
||||
s->last_update_frame = g_shape_frame_id;
|
||||
} else if (s->last_update_frame != g_shape_frame_id) {
|
||||
sg_update_buffer(s->vbuf, &(sg_range){s->verts, (size_t)count * sizeof(shape_vertex_t)});
|
||||
s->last_update_frame = g_shape_frame_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hovered/selected flags and the shader uniform state.
|
||||
* State is 0=normal, 1=hovered (brightened), 2=selected (green).
|
||||
*
|
||||
* @param s shape to update
|
||||
* @param hovered true if cursor is over the shape
|
||||
* @param selected true if shape is in the selection set
|
||||
*/
|
||||
static void shape_set_state(shape_t *s, bool hovered, bool selected)
|
||||
{
|
||||
s->hovered = hovered;
|
||||
@@ -269,16 +95,6 @@ static void shape_set_state(shape_t *s, bool hovered, bool selected)
|
||||
s->uniform.state = selected ? 2u : (hovered ? 1u : 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ray-casting point-in-polygon test. Handles arbitrary non-self-intersecting
|
||||
* polygons.
|
||||
*
|
||||
* @param px point X in world space
|
||||
* @param py point Y in world space
|
||||
* @param verts polygon vertices
|
||||
* @param n vertex count
|
||||
* @return true if the point is inside the polygon
|
||||
*/
|
||||
static bool point_in_polygon(float px, float py, shape_vertex_t *verts, uint32_t n)
|
||||
{
|
||||
bool inside = false;
|
||||
@@ -291,17 +107,6 @@ static bool point_in_polygon(float px, float py, shape_vertex_t *verts, uint32_t
|
||||
return inside;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a world-space point hits this shape. Transforms the query
|
||||
* to local space (verts are now stored relative to origin), then tests
|
||||
* polygon containment and edge proximity.
|
||||
*
|
||||
* @param s shape to test
|
||||
* @param wx point X in world space
|
||||
* @param wy point Y in world space
|
||||
* @param world_tol hit tolerance in world units
|
||||
* @return true if the point hits the shape
|
||||
*/
|
||||
static bool shape_hit_test(shape_t *s, float wx, float wy, float world_tol)
|
||||
{
|
||||
float sc = cosf(s->rotation), ss = sinf(s->rotation);
|
||||
@@ -330,37 +135,9 @@ static bool shape_hit_test(shape_t *s, float wx, float wy, float world_tol)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a draw call for this shape using the shape line-strip pipeline.
|
||||
*
|
||||
* @param s shape to draw
|
||||
* @param mvp model-view-projection matrix (from compute_mvp)
|
||||
*/
|
||||
static void shape_draw(shape_t *s, const mat4 *mvp)
|
||||
{
|
||||
sg_apply_uniforms(0, &SG_RANGE(*mvp));
|
||||
sg_apply_uniforms(1, &SG_RANGE(s->uniform));
|
||||
sg_apply_bindings(&(sg_bindings) {
|
||||
.vertex_buffers[0] = s->vbuf,
|
||||
.index_buffer = s->ibuf,
|
||||
});
|
||||
sg_draw(0, s->num_indices, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a circle shape (returned by value). Allocates verts/indices and GPU
|
||||
* buffers. The number of line segments adapts to radius.
|
||||
*
|
||||
* @param x center X in world space
|
||||
* @param y center Y in world space
|
||||
* @param r radius in world units
|
||||
* @param color RGBA base color
|
||||
* @return fully initialized shape_t
|
||||
*/
|
||||
static shape_t shape_circle(float x, float y, float r, const float color[4])
|
||||
{
|
||||
shape_t s;
|
||||
s.kind = SHAPE_CIRCLE;
|
||||
s.cx = x; s.cy = y;
|
||||
s.sx = r; s.sy = r;
|
||||
s.rotation = 0.0f;
|
||||
@@ -385,38 +162,23 @@ static shape_t shape_circle(float x, float y, float r, const float color[4])
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a star shape (returned by value). Alternates between outer_r and
|
||||
* inner_r at each vertex, producing a star with the given number of points.
|
||||
* Allocates verts/indices and GPU buffers.
|
||||
*
|
||||
* @param x center X in world space
|
||||
* @param y center Y in world space
|
||||
* @param outer_r outer radius in world units
|
||||
* @param inner_r inner radius in world units
|
||||
* @param points number of star points
|
||||
* @param color RGBA base color
|
||||
* @return fully initialized shape_t
|
||||
*/
|
||||
static shape_t shape_star(float x, float y, float outer_r, float inner_r,
|
||||
int points, const float color[4])
|
||||
{
|
||||
shape_t s;
|
||||
s.kind = SHAPE_STAR;
|
||||
s.cx = x; s.cy = y;
|
||||
s.sx = outer_r; s.sy = outer_r;
|
||||
s.rotation = 0.0f;
|
||||
s.star_points = points;
|
||||
s.star_inner_ratio = inner_r / outer_r;
|
||||
|
||||
int n = points * 2;
|
||||
int count = n + 1;
|
||||
s.verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||
s.indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||
|
||||
float inner_ratio = inner_r / outer_r;
|
||||
for (int i = 0; i < n; i++) {
|
||||
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f;
|
||||
float r = (i & 1) ? s.star_inner_ratio : 1.0f;
|
||||
float r = (i & 1) ? inner_ratio : 1.0f;
|
||||
s.verts[i] = (shape_vertex_t) { cosf(a) * r, sinf(a) * r };
|
||||
}
|
||||
s.verts[n] = s.verts[0];
|
||||
|
||||
Reference in New Issue
Block a user