Why a custom primitive component?

Mock Q4 · Partial · "Why not UProceduralMeshComponent / UDynamicMeshComponent?" · 7 min

In one breath

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

Gains that need owning the component

  1. 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-shape SetLocalTransform would 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.
  2. One tree walk feeds everything (GenerateRenderData() → proxy, collision, paint raycasts), so they can't drift apart.
  3. Per-node picking: a hit proxy per shape, so a click resolves straight to the node. PMC's are per section.
  4. Stays parametric: the component owns the tree, not vertices.

Costs (say these first)

45-second answer

"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).