diff options
| author | tech kaczynski <tk@techkaczynski.dev> | 2026-11-04 05:56:35 +1000 |
|---|---|---|
| committer | tech kaczynski <tk@techkaczynski.dev> | 2026-11-04 05:56:35 +1000 |
| commit | ea84213ece95e3ef8ff7f0095cee73ee6bdd0f9a (patch) | |
| tree | 32a502e72438ba0c6b6fa00dbd9dbdf3603fe7e3 /editor.c | |
| parent | e5560f69e11051a85c1d54780a59a4fbd83d8ed9 (diff) | |
added convex hull and trimesh colliders. Fixed physics edit mode bugs. Added debug drawing for all supported collision types.
Diffstat (limited to 'editor.c')
| -rw-r--r-- | editor.c | 207 |
1 files changed, 197 insertions, 10 deletions
@@ -3184,24 +3184,19 @@ panel_properties(EditorPanelContainer container, bool input) { main_cam.input = false; main_cam.update = false; - u64 id = entity_id_from_pid(ent->pid); - - f32 view_height = 10.0f * - tanf(main_cam.perspective_fov * - DEG2RAD * 0.5f); - - cam_focus(entity_world_pos(id), 5.0f, view_height); - ed.physics_pid = ent->pid; ed.physics_body = -1; ed.physics_shape = -1; ed.physics_scroll = (Vec2){0}; ed.prev_mode = ed.mode; + ed.panel[ED_PNL_LEFT].enabled = true; + ed.panel[ED_PNL_RIGHT].enabled = true; ed.mode = ED_MODE_PHYSICS; ed.panel[ED_PNL_LEFT].type = ED_PNL_TYPE_PHYSICS_ELEMENTS; ed.panel[ED_PNL_RIGHT].type = ED_PNL_TYPE_PHYSICS_PROPERTIES; + editor_focus_selection(); edit.focus = 0; add_open = false; @@ -4251,6 +4246,66 @@ panel_physics_shape(PanelEdit *edit, Rect row, } static void +physics_shape_model_geometry( + PhysicsShape *shape, + const Model *model, + u32 point_cnt, + u32 idx_cnt) { + bool trimesh = shape->tag == PHYS_SHAPE_STATIC_TRIMESH; + + Vec3 *points = malloc(point_cnt * sizeof(*points)); + u32 *indices = NULL; + + if (trimesh) { + indices = malloc(idx_cnt * sizeof(*indices)); + } + + if (!points || (trimesh && !indices)) { + perror("model collision allocation failed"); + exit(EXIT_FAILURE); + } + + u32 point_offset = 0; + u32 index_offset = 0; + + for (int i = 0; i < model->meshCount; ++i) { + const Mesh *mesh = &model->meshes[i]; + + for (int v = 0; v < mesh->vertexCount; ++v) { + points[point_offset + v] = (Vec3){ + mesh->vertices[v * 3], + mesh->vertices[v * 3 + 1], + mesh->vertices[v * 3 + 2], + }; + } + + if (trimesh) { + u32 count = (u32)mesh->triangleCount * 3; + + for (u32 k = 0; k < count; ++k) { + u32 index = mesh->indices + ? (u32)mesh->indices[k] + : k; + + indices[index_offset++] = point_offset + index; + } + } + + point_offset += (u32)mesh->vertexCount; + } + + if (trimesh) { + shape->TriMesh.points = points; + shape->TriMesh.indices = indices; + shape->TriMesh.point_cnt = point_cnt; + shape->TriMesh.idx_cnt = idx_cnt; + } else { + shape->ConvexHull.points = points; + shape->ConvexHull.point_cnt = point_cnt; + } +} + +static void panel_physics_properties(EditorPanelContainer container, bool input) { Rect bounds = ed.panel[container].bounds; @@ -4518,10 +4573,20 @@ panel_physics_properties(EditorPanelContainer container, bool input) { } bool fit_model = GuiButton(info, "Add Model Box"); + + info.y += 30; + bool add_hull = GuiButton(info, "Add Model Hull"); + + info.y += 30; + if (body->mode != PHYS_MODE_STATIC) { + GuiSetState(STATE_DISABLED); + } + + bool add_trimesh = GuiButton(info, "Add Model Trimesh"); GuiSetState(button_state); Model *model = NULL; - if (fit_model) { + if (fit_model || add_hull || add_trimesh) { Asset *asset = asset_get(ent->mdl.key); if (asset && asset->tag == ASSET_TAG_MODEL && asset->mdl.meshCount > 0) { @@ -4529,6 +4594,35 @@ panel_physics_properties(EditorPanelContainer container, bool input) { } } + u32 point_cnt = 0; + u32 idx_cnt = 0; + + if (model && (add_hull || add_trimesh)) { + for (int i = 0; i < model->meshCount; ++i) { + Mesh *mesh = &model->meshes[i]; + + if (!mesh->vertices || + mesh->vertexCount <= 0) { + model = NULL; + break; + } + + point_cnt += (u32)mesh->vertexCount; + idx_cnt += (u32)mesh->triangleCount * 3; + } + + if (point_cnt < (add_trimesh ? 3u : 4u) || + (add_trimesh && idx_cnt < 3)) { + model = NULL; + } + + if (!model) { + TraceLog(LOG_WARNING, + "model has no usable " + "collision geometry"); + } + } + if (add_box || add_sphere || add_capsule || add_cylinder || model) { edit.focus = 0; @@ -4584,7 +4678,7 @@ panel_physics_properties(EditorPanelContainer container, bool input) { shape->Cylinder.height = 1.0f; } - if (model) { + if (model && fit_model) { Model local_model = *model; local_model.transform = MatrixIdentity(); @@ -4608,6 +4702,22 @@ panel_physics_properties(EditorPanelContainer container, bool input) { shape->rot); } + if (model && (add_hull || add_trimesh)) { + PhysicsShape *shape = + &body->shapes[body->shape_cnt]; + + shape->tag = add_trimesh + ? PHYS_SHAPE_STATIC_TRIMESH + : PHYS_SHAPE_CONVEX_HULL; + + shape->rot = QuaternionInvert(body->rot); + shape->pos = Vector3RotateByQuaternion( + Vector3Negate(body->pos), shape->rot); + + physics_shape_model_geometry( + shape, model, point_cnt, idx_cnt); + } + body->shape_cnt++; action_commit(&action); @@ -5435,6 +5545,36 @@ gizmo_scale(void) { } static void +physics_draw_hull(b3ShapeId shape_id, Color colour) { + if (!b3Shape_IsValid(shape_id)) { + return; + } + + const b3HullData *hull = b3Shape_GetHull(shape_id); + const b3Vec3 *points = b3GetHullPoints(hull); + const b3HullHalfEdge *edges = b3GetHullEdges(hull); + b3BodyId body_id = b3Shape_GetBody(shape_id); + + for (int i = 0; i < hull->edgeCount; ++i) { + const b3HullHalfEdge *edge = &edges[i]; + + /* Each edge has two halves; draw it once. */ + if (i > edge->twin) { + continue; + } + + b3Pos a = b3Body_GetWorldPoint( + body_id, points[edge->origin]); + b3Pos b = b3Body_GetWorldPoint( + body_id, points[edges[edge->twin].origin]); + + DrawLine3D( + (Vec3){a.x, a.y, a.z}, + (Vec3){b.x, b.y, b.z}, colour); + } +} + +static void physics_draw_shapes(u64 id) { Entity *ent = entity_get(id); if (!ent) { @@ -5496,6 +5636,53 @@ physics_draw_shapes(u64 id) { QuaternionToMatrix(rotation), MatrixTranslate(center.x, center.y, center.z)); + if (shape->tag == PHYS_SHAPE_CONVEX_HULL) { + PhysicsBodyRuntime *bodies = + ent->phys.runtime.bodies; + + if (bodies && bodies[i].shapes) { + physics_draw_hull(bodies[i].shapes[j], + colour); + } + continue; + } + + if (shape->tag == PHYS_SHAPE_STATIC_TRIMESH) { + const Vec3 *points = shape->TriMesh.points; + const u32 *indices = shape->TriMesh.indices; + u32 point_cnt = shape->TriMesh.point_cnt; + + if (!points || !indices) { + continue; + } + + for (u32 k = 0; + k + 2 < shape->TriMesh.idx_cnt; + k += 3) { + u32 ia = indices[k]; + u32 ib = indices[k + 1]; + u32 ic = indices[k + 2]; + + if (ia >= point_cnt || + ib >= point_cnt || + ic >= point_cnt) { + continue; + } + + Vec3 a = Vector3Transform(points[ia], + world); + Vec3 b = Vector3Transform(points[ib], + world); + Vec3 c = Vector3Transform(points[ic], + world); + + DrawLine3D(a, b, colour); + DrawLine3D(b, c, colour); + DrawLine3D(c, a, colour); + } + continue; + } + rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); |
