techkaczynski.dev / code

summaryrefslogtreecommitdiff
path: root/editor.c
diff options
context:
space:
mode:
authortech kaczynski <tk@techkaczynski.dev>2026-11-04 05:56:35 +1000
committertech kaczynski <tk@techkaczynski.dev>2026-11-04 05:56:35 +1000
commitea84213ece95e3ef8ff7f0095cee73ee6bdd0f9a (patch)
tree32a502e72438ba0c6b6fa00dbd9dbdf3603fe7e3 /editor.c
parente5560f69e11051a85c1d54780a59a4fbd83d8ed9 (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.c207
1 files changed, 197 insertions, 10 deletions
diff --git a/editor.c b/editor.c
index 2591cc1..18cd918 100644
--- a/editor.c
+++ b/editor.c
@@ -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));