#include "debugdraw.h" #include "render.h" #include "yugine.h" #include "log.h" #include #include "debug.h" #include "window.h" #include "2dphysics.h" #include "stb_ds.h" #include "sokol/sokol_gfx.h" #include "point.sglsl.h" #include "poly.sglsl.h" #include "circle.sglsl.h" #include "line.sglsl.h" #include "grid.sglsl.h" #define PAR_STREAMLINES_IMPLEMENTATION #include "par/par_streamlines.h" #include "font.h" #define v_amt 5000 struct flush { sg_shader shader; sg_pipeline pipe; sg_bindings bind; void *verts; int c; int v; int sc; int sv; }; typedef struct flush flush; static flush fpoint; static flush circle; void flushview(flush *f, HMM_Mat4 *view) { sg_apply_pipeline(f->pipe); sg_apply_bindings(&f->bind); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*view)); sg_draw(f->sc, f->c, 1); } void flushpass(flush *f) { f->sc = f->c; f->c = 0; f->sv = f->v; f->v = 0; } static sg_shader point_shader; static sg_pipeline point_pipe; static sg_bindings point_bind; struct point_vertex { struct draw_p pos; struct rgba color; float radius; }; static int point_c = 0; static int point_sc = 0; static sg_shader line_shader; static sg_pipeline line_pipe; static sg_bindings line_bind; struct line_vert { struct draw_p pos; float dist; struct rgba color; float seg_len; float seg_speed; }; static int line_c = 0; static int line_v = 0; static int line_sc = 0; static int line_sv = 0; static sg_pipeline grid_pipe; static sg_bindings grid_bind; static sg_shader grid_shader; static int grid_c = 0; static sg_pipeline poly_pipe; static sg_bindings poly_bind; static sg_shader poly_shader; static int poly_c = 0; static int poly_v = 0; static int poly_sc = 0; static int poly_sv = 0; struct poly_vertex { struct draw_p pos; float uv[2]; struct rgba color; }; static sg_pipeline circle_pipe; static sg_bindings circle_bind; static sg_shader csg; static int circle_count = 0; static int circle_sc = 0; struct circle_vertex { struct draw_p pos; float radius; struct rgba color; float segsize; float fill; }; /* Writes debug data to buffers, and draws */ void debug_flush(HMM_Mat4 *view) { if (poly_c != 0) { sg_apply_pipeline(poly_pipe); sg_apply_bindings(&poly_bind); sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view)); sg_draw(poly_sc,poly_c,1); } if (point_c != 0) { sg_apply_pipeline(point_pipe); sg_apply_bindings(&point_bind); sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view)); sg_draw(point_sc,point_c,1); } if (line_c != 0) { sg_apply_pipeline(line_pipe); sg_apply_bindings(&line_bind); sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view)); lfs_params_t lt; lt.time = apptime(); sg_apply_uniforms(SG_SHADERSTAGE_FS,0,SG_RANGE_REF(lt)); sg_draw(line_sc,line_c,1); } if (circle_count != 0) { sg_apply_pipeline(circle_pipe); sg_apply_bindings(&circle_bind); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*view)); sg_draw(circle_sc,4,circle_count); } } void debug_nextpass() { point_sc = point_c; point_c = 0; circle_sc = circle_count; circle_count = 0; line_sv = line_v; line_v = 0; line_sc = line_c; line_c = 0; poly_sc = poly_c; poly_c = 0; poly_sv = poly_v; poly_v = 0; } void debug_newframe() { point_sc = 0; point_c = 0; circle_sc = circle_count = line_sv = line_v = line_sc = line_c = poly_sc = poly_c = 0; poly_sv = poly_v = 0; } static sg_shader_uniform_block_desc projection_ubo = { .size = sizeof(projection), .uniforms = { [0] = { .name = "proj", .type = SG_UNIFORMTYPE_MAT4 }, } }; static sg_shader_uniform_block_desc time_ubo = { .size = sizeof(float), .uniforms = { [0] = { .name = "time", .type = SG_UNIFORMTYPE_FLOAT }, } }; void debugdraw_init() { point_shader = sg_make_shader(point_shader_desc(sg_query_backend())); point_pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = point_shader, .layout = { .attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */ [1].format = SG_VERTEXFORMAT_UBYTE4N, /* color */ [2].format = SG_VERTEXFORMAT_FLOAT /* radius */ } }, .primitive_type = SG_PRIMITIVETYPE_POINTS, .colors[0].blend = blend_trans, .label = "dbg point", }); point_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(struct point_vertex)*v_amt, .usage = SG_USAGE_STREAM }); line_shader = sg_make_shader(line_shader_desc(sg_query_backend())); line_pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = line_shader, .layout = { .attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */ [1].format = SG_VERTEXFORMAT_FLOAT, /* dist */ [2].format = SG_VERTEXFORMAT_UBYTE4N, /* color */ [3].format = SG_VERTEXFORMAT_FLOAT, /* seg length */ [4].format = SG_VERTEXFORMAT_FLOAT /* dashed line speed */ } }, .primitive_type = SG_PRIMITIVETYPE_LINES, .index_type = SG_INDEXTYPE_UINT16, .colors[0].blend = blend_trans, .label = "dbg line", }); line_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(struct line_vert)*v_amt, .usage = SG_USAGE_STREAM }); line_bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(uint16_t)*v_amt, .usage = SG_USAGE_STREAM, .type = SG_BUFFERTYPE_INDEXBUFFER }); csg = sg_make_shader(circle_shader_desc(sg_query_backend())); circle_pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = csg, .layout = { .attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, [0].buffer_index = 1, [1].format = SG_VERTEXFORMAT_FLOAT2, [2].format = SG_VERTEXFORMAT_FLOAT, [3].format = SG_VERTEXFORMAT_UBYTE4N, [4].format = SG_VERTEXFORMAT_FLOAT, [5].format = SG_VERTEXFORMAT_FLOAT }, .buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE, }, .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP, .cull_mode = SG_CULLMODE_BACK, .colors[0].blend = blend_trans, .label = "circle pipeline" }); circle_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(struct circle_vertex)*v_amt, .usage = SG_USAGE_STREAM, }); float circleverts[8] = { -1,-1, -1,1, 1,-1, 1,1 }; circle_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){ .data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8}, .usage = SG_USAGE_IMMUTABLE, }); grid_shader = sg_make_shader(grid_shader_desc(sg_query_backend())); grid_pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = grid_shader, .layout = { .attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */ } }, .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP, .cull_mode = SG_CULLMODE_BACK, .label = "grid pipeline", .colors[0].blend = blend_trans, }); grid_bind.vertex_buffers[0] = circle_bind.vertex_buffers[1]; poly_shader = sg_make_shader(poly_shader_desc(sg_query_backend())); poly_pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = poly_shader, .layout = { .attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */ [1].format = SG_VERTEXFORMAT_FLOAT2, /* uv */ [2].format = SG_VERTEXFORMAT_UBYTE4N /* color rgba */ } }, .index_type = SG_INDEXTYPE_UINT32, .colors[0].blend = blend_trans, // .cull_mode = SG_CULLMODE_FRONT, .label = "dbg poly", }); poly_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(struct poly_vertex)*v_amt, .usage = SG_USAGE_STREAM, .type = SG_BUFFERTYPE_VERTEXBUFFER, }); poly_bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ .size = sizeof(uint32_t)*6*v_amt, .usage = SG_USAGE_STREAM, .type = SG_BUFFERTYPE_INDEXBUFFER }); } void draw_line(HMM_Vec2 *points, int n, struct rgba color, float seg_len, float seg_speed) { if (n < 2) return; seg_speed = 1; struct line_vert v[n]; float dist = 0; for (int i = 0; i < n; i++) { v[i].pos.x = points[i].x; v[i].pos.y = points[i].y; v[i].color = color; v[i].seg_len = seg_len; v[i].seg_speed = seg_speed; } v[0].dist = 0; for (int i = 1; i < n; i++) { dist += HMM_DistV2(points[i-1], points[i]); v[i].dist = dist; } int i_c = (n-1)*2; uint16_t idxs[i_c]; for (int i = 0, d = 0; i < n-1; i++, d+=2) { idxs[d] = i + line_v + line_sv; idxs[d+1] = idxs[d]+1; } sg_range vr = { .ptr = v, .size = sizeof(struct line_vert)*n }; sg_range ir = { .ptr = idxs, .size = sizeof(uint16_t)*i_c }; sg_append_buffer(line_bind.vertex_buffers[0], &vr); sg_append_buffer(line_bind.index_buffer, &ir); line_c += i_c; line_v += n; } HMM_Vec2 center_of_vects(HMM_Vec2 *v, int n) { HMM_Vec2 c; for (int i = 0; i < n; i++) { c.x += v[i].x; c.y += v[i].y; } c.x /= n; c.y /= n; return c; } /* Given a series of points p, computes a new series with them expanded on either side by d */ HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n) { if (d == 0) { HMM_Vec2 *ret = NULL; arraddn(ret,n); for (int i = 0; i < n; i++) ret[i] = p[i]; return ret; } parsl_position par_v[n]; uint16_t spine_lens[] = {n}; for (int i = 0; i < n; i++) { par_v[i].x = p[i].x; par_v[i].y = p[i].y; }; parsl_context *par_ctx = parsl_create_context((parsl_config){ .thickness = d, .flags= PARSL_FLAG_ANNOTATIONS, .u_mode = PAR_U_MODE_DISTANCE }); parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){ .num_vertices = n, .num_spines = 1, .vertices = par_v, .spine_lengths = spine_lens, .closed = 0, }); HMM_Vec2 *ret = NULL; arraddn(ret,mesh->num_vertices); for (int i = 0; i < mesh->num_vertices; i++) { ret[i].x = mesh->positions[i].x; ret[i].y = mesh->positions[i].y; }; return ret; } /* Given a strip of points, draws them as segments. So 5 points is 4 segments, and ultimately 8 vertices */ void draw_edge(HMM_Vec2 *points, int n, struct rgba color, int thickness, int flags, struct rgba line_color, float line_seg) { int closed = 0; if (thickness <= 1) { draw_line(points,n,line_color,0,0); return; } /* todo: should be dashed, and filled. use a texture. */ /* draw polygon outline */ if (HMM_EqV2(points[0], points[n-1])) { closed = true; n--; } parsl_position par_v[n]; for (int i = 0; i < n; i++) { par_v[i].x = points[i].x; par_v[i].y = points[i].y; } uint16_t spine_lens[] = {n}; parsl_context *par_ctx = parsl_create_context((parsl_config){ .thickness = thickness, .flags = PARSL_FLAG_ANNOTATIONS, }); parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){ .num_vertices = n, .num_spines = 1, .vertices = par_v, .spine_lengths = spine_lens, .closed = closed }); for (int i = 0; i < mesh->num_triangles*3; i++) mesh->triangle_indices[i] += (poly_v+poly_sv); struct poly_vertex vertices[mesh->num_vertices]; for (int i = 0; i < mesh->num_vertices; i++) { vertices[i].pos = (struct draw_p){ .x = mesh->positions[i].x, .y = mesh->positions[i].y }; vertices[i].uv[0] = mesh->annotations[i].u_along_curve; vertices[i].uv[1] = mesh->annotations[i].v_across_curve; vertices[i].color = color; } sg_append_buffer(poly_bind.vertex_buffers[0], &(sg_range){.ptr = vertices, .size = sizeof(struct poly_vertex)*mesh->num_vertices}); sg_append_buffer(poly_bind.index_buffer, &(sg_range){.ptr = mesh->triangle_indices, sizeof(uint32_t)*mesh->num_triangles*3}); poly_c += mesh->num_triangles*3; poly_v += mesh->num_vertices; parsl_destroy_context(par_ctx); /* Now drawing the line outlines */ if (thickness == 1) { draw_line(points,n,line_color,line_seg, 0); } else { HMM_Vec2 in_p[n]; HMM_Vec2 out_p[n]; for (int i = 1, v = 0; i < n*2+1; i+=2, v++) { in_p[v].x = vertices[i].pos.x; in_p[v].y = vertices[i].pos.y; } for (int i = 0, v = 0; i < n*2; i+=2,v++) { out_p[v].x = vertices[i].pos.x; out_p[v].y = vertices[i].pos.y; } if (!closed) { HMM_Vec2 p[n*2]; for (int i = 0; i < n; i++) p[i] = in_p[i]; for (int i = n-1, v = n; i >= 0; i--,v++) p[v] = out_p[i]; draw_line(p,n*2,line_color,line_seg,0); return; } draw_line(in_p,n,line_color,line_seg,0); draw_line(out_p,n,line_color,line_seg,0); } } void draw_circle(HMM_Vec2 pos, float radius, float pixels, struct rgba color, float seg) { struct circle_vertex cv; cv.pos.x = pos.x; cv.pos.y = pos.y; cv.radius = radius; cv.color = color; cv.segsize = seg/radius; cv.fill = pixels/radius; sg_append_buffer(circle_bind.vertex_buffers[0], &(sg_range){.ptr = &cv, .size = sizeof(struct circle_vertex)}); circle_count++; } void draw_box(HMM_Vec2 c, HMM_Vec2 wh, struct rgba color) { float hw = wh.x / 2.f; float hh = wh.y / 2.f; HMM_Vec2 verts[4] = { { .x = c.x-hw, .y = c.y-hh }, { .x = c.x+hw, .y = c.y-hh }, { .x = c.x+hw, .y = c.y+hh }, { .x = c.x-hw, .y = c.y+hh } }; draw_poly(verts, 4, color); } void draw_grid(float width, float span, struct rgba color) { HMM_Vec2 offset = (HMM_Vec2)cam_pos(); offset = HMM_MulV2F(offset, 1/cam_zoom()); float ubo[4]; ubo[0] = offset.x; ubo[1] = offset.y; ubo[2] = mainwin.size.x; ubo[3] = mainwin.size.y; sg_apply_pipeline(grid_pipe); sg_apply_bindings(&grid_bind); float col[4]; rgba2floats(col,color); fs_params_t pt; pt.thickness = (float)width; pt.span = span/cam_zoom(); memcpy(&pt.color, col, sizeof(float)*4); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(ubo)); sg_apply_uniforms(SG_SHADERSTAGE_FS, 0, SG_RANGE_REF(pt)); sg_draw(0,4,1); } void draw_cppoint(HMM_Vec2 point, float r, struct rgba color) { struct point_vertex p = { .color = color, .radius = r }; p.pos.x = point.X; p.pos.y = point.Y; sg_append_buffer(point_bind.vertex_buffers[0], &(sg_range){.ptr = &p, .size = sizeof(struct point_vertex)}); point_c++; } void draw_points(HMM_Vec2 *points, int n, float size, struct rgba color) { for (int i = 0; i < n; i++) draw_cppoint(points[i], size, color); } void draw_poly(HMM_Vec2 *points, int n, struct rgba color) { /* Find polygon mesh */ int tric = n - 2; if (tric < 1) return; uint32_t tridxs[tric*3]; for (int i = 2, ti = 0; i < n; i++, ti+=3) { tridxs[ti] = 0; tridxs[ti+1] = i-1; tridxs[ti+2] = i; } for (int i = 0; i < tric*3; i++) tridxs[i] += poly_v+poly_sv; struct poly_vertex polyverts[n]; for (int i = 0; i < n; i++) { polyverts[i].pos = (struct draw_p) { .x = points[i].x, .y = points[i].y}; polyverts[i].uv[0] = 0.0; polyverts[i].uv[1] = 0.0; polyverts[i].color = color; } sg_append_buffer(poly_bind.vertex_buffers[0], &(sg_range){.ptr = polyverts, .size = sizeof(struct poly_vertex)*n}); sg_append_buffer(poly_bind.index_buffer, &(sg_range){.ptr = tridxs, sizeof(uint32_t)*3*tric}); poly_c += tric*3; poly_v += n; }