Why a custom primitive component?
Cheap convex collision is why the plugin exists, but PMC can do convex collision too, so it's not why the component is custom. It's custom because the shape tree is the source of truth: one tree walk feeds rendering, collision and editor picking, and owning the body setup lets a moved node keep its cooked collision. Name the costs yourself before they do.
What went wrong
- "Cheaper-than-mesh collision" didn't survive "so why not PMC?". Keep the two questions apart: why the plugin vs why the component.
- "An entire environment as one primitive" invites culling/streaming pushback. Say "one component per assembly".
- The "costs" given were limitations of the shape model, not the costs of writing your own component and proxy.
Gains that need owning the component
- Live edits without re-cooking. A pure move doesn't change hull vertices, so the cooked hulls are kept and only
transforms change, all installed in one batched
SetGeometry. Per-shapeSetLocalTransformwould be O(N²) because it rebuilds the union each call. It falls back to a full rebuild if count, mapping or vertices change. Strongest single answer. - One tree walk feeds everything (
GenerateRenderData()→ proxy, collision, paint raycasts), so they can't drift apart. - Per-node picking: a hit proxy per shape, so a click resolves straight to the node. PMC's are per section.
- Stays parametric: the component owns the tree, not vertices.
Costs (say these first)
- Engine-version maintenance:
FPrimitiveSceneProxyand Chaos internals aren't stable API. - Dynamic draw path (
GetDynamicMeshElementsevery frame) vs static meshes' cached draw commands. - No free Nanite, LODs, distance fields, HLOD/World Partition. The bake to static mesh is the mitigation.
- Granularity: one bounds box, so it's culled all-or-nothing and not streamable in pieces.
- Render-thread correctness is on you.
"Cheap convex collision is why the plugin exists, but PMC can do convex collision too, so that isn't why the component is custom. It's custom because the shape tree is the source of truth. One tree walk feeds rendering, collision and editor picking, and owning the body setup lets me move a node's collision without re-cooking: the hulls stay, and I install the new transforms in one batched geometry update. The costs are real: I maintain a scene proxy and some Chaos internals across engine versions, I use the dynamic draw path, and I don't get Nanite, LODs or streaming. That's why shipping content bakes to a static mesh."
Quiz yourself
Why is "cheap collision" not a good answer to "why not PMC?"
PMC supports simple convex collision too (AddCollisionConvexMesh). Cheap collision justifies the plugin, not the custom component.
What's the fast path, and why is per-shape SetLocalTransform O(N²)?
On a pure move, keep the cooked hulls and swap transforms in one batched SetGeometry. SetLocalTransform rebuilds the whole union each call, so N calls × N shapes.
Static vs dynamic draw path: which does your proxy use, and what's the cost?
Dynamic (GetDynamicMeshElements): it gathers mesh batches every frame instead of using cached mesh draw commands. Fine for editing, costly at scale, which is why baking exists.
Name three costs of a custom component.
Engine-version maintenance, dynamic draw path, no Nanite/LOD/streaming, all-or-nothing culling, render-thread correctness (any three).