Primitive Shapes
SafiEngine ships a small procedural geometry library and an ECS component that wires it into the standard render path. Use it to get visible objects into a scene without any glTF on disk — ideal for prototyping, debug gizmos, the upcoming editor, and any place you just want "a red cube".
What you get
Each generator fills freshly-malloc'd vertex and index arrays in the engine's standard SafiVertex layout. The caller hands them to safi_mesh_create, then free()s both.
The SafiPrimitive component
The easier path is the ECS component — the engine's primitive_system owns the GPU resources and rebuilds them automatically whenever you edit any field.
Drop this on any entity that also has SafiTransform + SafiGlobalTransform. Next frame, the system will:
- Generate the mesh geometry for the selected shape.
- If
textureis a valid asset handle, borrow the resolved GPU texture from the registry (no re-upload, no refcount churn). Otherwise upload a 1×1 RGBA8 base-color texture fromcolor. - Build a lit pipeline + sampler.
- Auto-attach a
SafiMeshRendererpointing at the internal model so the standard render pass picks it up.
When the component is removed (or the entity is destroyed), the .dtor / .copy / .move hooks on SafiPrimitive release both the internal _model_handle and the user-facing texture handle — no manual cleanup.
Example — spawn a red box
Example — textured capsule
Clear the handle (.texture = {0}) to fall back to the solid color again. The inspector's "Texture" field accepts a project-root-relative path, commits on Enter (so typing a partial path doesn't trigger a load per keystroke), and then loads through the same asset registry. Texture hot-reloads propagate back to primitives automatically: primitive_system subscribes to safi_assets_on_reload and rebuilds affected entities next frame so the new GPU texture replaces the cached pointer.
Inspector
Selecting an entity with SafiPrimitive in the MicroUI debug panel exposes:
- Shape — dropdown (Plane / Box / Sphere / Capsule).
- Dims — shape-specific fields (size, half-extents, radius, segments, rings, height).
- Color — four inline number cells (R, G, B, A).
- Texture — single-line text input for the image path.
Any edit takes effect on the next frame. Changing the shape swaps the mesh; tweaking dimensions rebuilds geometry; editing the texture path reloads the image (or falls back to color if the file is missing).
Low-level builders
If you want to bake a procedural mesh once and keep it out of the ECS, call the builders directly:
All builders produce CCW winding when viewed from the outside of the shape, matching the lit pipeline's CULL_BACK / FRONTFACE_CCW state.
See also
SafiMesh— GPU buffer pair the builders targetSafiMaterial— pipeline + base-color textureSafiMeshRenderer— what the system attaches automatically