Add a geometry pool, shape hierarchy, addittion and deletion.

This commit is contained in:
2026-04-30 17:55:46 +02:00
parent 9ce6e4accd
commit e71641c094
14 changed files with 2273 additions and 1341 deletions

View File

@@ -7,9 +7,15 @@ typedef struct shape_vertex_t {
float x, y;
} shape_vertex_t;
typedef enum {
SHAPE_CIRCLE,
SHAPE_RECTANGLE,
SHAPE_STAR,
SHAPE_GENERIC,
} shape_kind_t;
typedef struct shape_uniform_t {
mat4 transform;
float base_color[4];
uint32_t state;
uint8_t _pad[12];
} shape_uniform_t;
@@ -17,10 +23,8 @@ typedef struct shape_uniform_t {
typedef struct shape_t {
shape_vertex_t *verts;
uint16_t *indices;
uint32_t num_indices;
uint32_t num_elements;
uint32_t num_verts;
sg_buffer vbuf;
sg_buffer ibuf;
shape_uniform_t uniform;
bool hovered;
bool selected;
@@ -28,8 +32,119 @@ typedef struct shape_t {
float cx, cy;
float sx, sy;
float rotation;
int kind;
uint32_t vertex_base;
uint32_t index_base;
int group_id;
} shape_t;
// -- group entity (for nested groups) --
typedef struct {
int id;
int parent_id; // 0 = top-level group
} group_t;
static group_t* find_group(vector_t *groups, int id) {
for (int i = 0; i < groups->count; i++) {
group_t *g = (group_t*) vec_get(groups, i);
if (g->id == id) return g;
}
return NULL;
}
static int get_topmost_group(vector_t *groups, int gid) {
while (gid != 0) {
group_t *g = find_group(groups, gid);
if (!g || g->parent_id == 0) return gid;
gid = g->parent_id;
}
return 0;
}
static bool is_shape_in_group_hierarchy(int shape_gid, int target_gid, vector_t *groups) {
int cur = shape_gid;
while (cur != 0) {
if (cur == target_gid) return true;
group_t *g = find_group(groups, cur);
if (!g) return false;
cur = g->parent_id;
}
return false;
}
// -- shared geometry buffers (one vbuf + one ibuf for all shapes) --
static sg_buffer g_shape_vbuf = {0};
static sg_buffer g_shape_ibuf = {0};
static bool g_shape_pool_dirty;
static uint32_t g_shape_vert_count;
static uint32_t g_shape_idx_count;
static void shape_pool_rebuild(vector_t *shapes)
{
// count total vertices / indices (line strips: num_elements == num_verts + 1)
uint32_t total_verts = 0, total_indices = 0;
for (int i = 0; i < shapes->count; i++) {
shape_t *s = (shape_t*) vec_get(shapes, i);
total_verts += s->num_elements;
total_indices += s->num_elements;
}
if (g_shape_vbuf.id) { sg_destroy_buffer(g_shape_vbuf); g_shape_vbuf.id = 0; }
if (g_shape_ibuf.id) { sg_destroy_buffer(g_shape_ibuf); g_shape_ibuf.id = 0; }
g_shape_vert_count = 0;
g_shape_idx_count = 0;
if (total_verts == 0) {
g_shape_pool_dirty = false;
return;
}
shape_vertex_t *all_v = (shape_vertex_t*) ALLOC((size_t)total_verts * sizeof(shape_vertex_t));
uint16_t *all_i = (uint16_t*) ALLOC((size_t)total_indices * sizeof(uint16_t));
uint32_t voff = 0, ioff = 0;
for (int i = 0; i < shapes->count; i++) {
shape_t *s = (shape_t*) vec_get(shapes, i);
uint32_t n = s->num_elements;
memcpy(&all_v[voff], s->verts, (size_t)n * sizeof(shape_vertex_t));
for (uint32_t j = 0; j < n; j++)
all_i[ioff + j] = (uint16_t)(voff + s->indices[j]);
s->vertex_base = voff;
s->index_base = ioff;
voff += n;
ioff += n;
}
g_shape_vbuf = sg_make_buffer(&(sg_buffer_desc){
.data = { all_v, (size_t)total_verts * sizeof(shape_vertex_t) },
.label = "Shape verts (shared)",
});
g_shape_ibuf = sg_make_buffer(&(sg_buffer_desc){
.data = { all_i, (size_t)total_indices * sizeof(uint16_t) },
.usage = { .index_buffer = true },
.label = "Shape indices (shared)",
});
FREE(all_v);
FREE(all_i);
g_shape_vert_count = total_verts;
g_shape_idx_count = total_indices;
g_shape_pool_dirty = false;
}
static void shape_pool_shutdown(void)
{
if (g_shape_vbuf.id) { sg_destroy_buffer(g_shape_vbuf); g_shape_vbuf.id = 0; }
if (g_shape_ibuf.id) { sg_destroy_buffer(g_shape_ibuf); g_shape_ibuf.id = 0; }
g_shape_vert_count = 0;
g_shape_idx_count = 0;
}
#define SHAPE_HOVER_PX 6.0f
static int shape_calc_segments(float r)
@@ -40,13 +155,13 @@ static int shape_calc_segments(float r)
return n;
}
static void shape_init_common(shape_t *s, const float color[4])
static void shape_init_common(shape_t *s)
{
s->hovered = false;
s->selected = false;
memcpy(s->uniform.base_color, color, sizeof(float[4]));
s->uniform.state = 0;
memset(s->uniform._pad, 0, sizeof(s->uniform._pad));
s->group_id = 0;
}
static void shape_build_transform(shape_t *s)
@@ -61,24 +176,13 @@ static void shape_build_transform(shape_t *s)
static void shape_make_buffers(shape_t *s)
{
uint32_t vcount = s->num_verts + 1;
s->vbuf = sg_make_buffer(&(sg_buffer_desc) {
.size = (size_t)vcount * sizeof(shape_vertex_t),
.usage = { .stream_update = true },
.label = "Shape vertices",
});
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) },
.label = "Shape indices",
});
(void)s;
g_shape_pool_dirty = true;
}
static void shape_shutdown(shape_t *s)
{
sg_destroy_buffer(s->vbuf);
sg_destroy_buffer(s->ibuf);
g_shape_pool_dirty = true;
FREE(s->verts);
FREE(s->indices);
}
@@ -135,12 +239,13 @@ static bool shape_hit_test(shape_t *s, float wx, float wy, float world_tol)
return false;
}
static shape_t shape_circle(float x, float y, float r, const float color[4])
static shape_t shape_circle(float x, float y, float r)
{
shape_t s;
s.cx = x; s.cy = y;
s.sx = r; s.sy = r;
s.rotation = 0.0f;
s.kind = SHAPE_CIRCLE;
int segs = shape_calc_segments(r);
int count = segs + 1;
@@ -153,22 +258,23 @@ static shape_t shape_circle(float x, float y, float r, const float color[4])
}
s.verts[segs] = s.verts[0];
for (int i = 0; i <= segs; i++) s.indices[i] = (uint16_t)i;
s.num_indices = (uint32_t)count;
s.num_elements = (uint32_t)count;
s.num_verts = (uint32_t)segs;
shape_init_common(&s, color);
shape_init_common(&s);
shape_build_transform(&s);
shape_make_buffers(&s);
return s;
}
static shape_t shape_star(float x, float y, float outer_r, float inner_r,
int points, const float color[4])
int points)
{
shape_t s;
s.cx = x; s.cy = y;
s.sx = outer_r; s.sy = outer_r;
s.rotation = 0.0f;
s.kind = SHAPE_STAR;
int n = points * 2;
int count = n + 1;
@@ -183,10 +289,36 @@ static shape_t shape_star(float x, float y, float outer_r, float inner_r,
}
s.verts[n] = s.verts[0];
for (int i = 0; i <= n; i++) s.indices[i] = (uint16_t)i;
s.num_indices = (uint32_t)count;
s.num_elements = (uint32_t)count;
s.num_verts = (uint32_t)n;
shape_init_common(&s, color);
shape_init_common(&s);
shape_build_transform(&s);
shape_make_buffers(&s);
return s;
}
static shape_t shape_rectangle(float x, float y, float w, float h)
{
shape_t s;
s.cx = x; s.cy = y;
s.sx = w * 0.5f; s.sy = h * 0.5f;
s.rotation = 0.0f;
s.kind = SHAPE_RECTANGLE;
s.num_verts = 4;
s.num_elements = 5;
s.verts = (shape_vertex_t*) ALLOC(5 * sizeof(shape_vertex_t));
s.indices = (uint16_t*) ALLOC(5 * sizeof(uint16_t));
s.verts[0] = (shape_vertex_t){-1.0f, -1.0f};
s.verts[1] = (shape_vertex_t){ 1.0f, -1.0f};
s.verts[2] = (shape_vertex_t){ 1.0f, 1.0f};
s.verts[3] = (shape_vertex_t){-1.0f, 1.0f};
s.verts[4] = s.verts[0];
for (int i = 0; i < 5; i++) s.indices[i] = (uint16_t)i;
shape_init_common(&s);
shape_build_transform(&s);
shape_make_buffers(&s);
return s;