Stock Components
Every world created by safi_ecs_create (or safi_app_init) has these registered. Attach them to entities with ecs_set / ecs_add.
SafiTransform
Use safi_transform_identity() for a sensible default (zero position, unit quaternion, unit scale). safi_transform_to_mat4() composes a TRS model matrix.
SafiGlobalTransform
World-space model matrix. Written automatically each frame by the engine's transform propagation system. Add it alongside SafiTransform on any entity that needs a world-space pose (renderable meshes, physics bodies, etc.).
SafiCamera
SafiCamera exists as a component but projection is computed in the demo, not by the engine. Orthographic projection is not supported. There is no camera priority, viewport, or multi-camera system. See Camera System.
SafiActiveCamera
Tag component — marks which camera the engine renders from. Attach to exactly one entity that also has SafiCamera. The engine's render system finds the active camera via (SafiCamera, SafiActiveCamera) query.
Singleton by convention: the engine centralises the "at most one holder" policy via safi_ecs_make_tag_unique(world, ecs_id(SafiActiveCamera), holder) (declared in safi/ecs/singleton_tag.h). deser_active_camera calls it automatically, so snapshot restore and scene load can never end up with two active cameras at the same time.
SafiEngineOwned
Tag for engine infrastructure entities that must survive safi_scene_clear, stay out of safi_scene_save / snapshot, and be ignored by the editor's scene hierarchy panel. The editor fly-cam carries this tag; future additions (culling proxies, shadow-RT holders, debug billboards) should follow the same convention.
SafiStableId
128-bit random GUID that uniquely identifies an entity across snapshots, scene save/load, undo steps, and future prefab instantiation. Every named entity gets one automatically (via an EcsOnAdd observer on SafiName), so user code rarely touches the component directly.
Serialised as 32 lowercase hex characters alongside name in scene JSON; safi_scene_restore_snapshot matches on the id first and falls back to name for entities that predate the format. See safi/ecs/stable_id.h for generation helpers (safi_stable_id_new, safi_stable_id_to_string, safi_stable_id_from_string) and the safi_scene_find_entity_by_stable_id lookup.
SafiMeshRenderer
The engine's render system queries (SafiGlobalTransform, SafiMeshRenderer) and resolves the handle via safi_assets_resolve_model() each frame, then issues one safi_model_draw_lit call per visible entity. Set visible = false to skip rendering without removing the component.
Models are loaded through the Asset System with path-based deduplication and refcounting. The inspector shows the resolved path (or <code> for procedurally-registered models).
SafiPrimitive
A procedural mesh with editable shape, dimensions, color, and texture. The engine's primitive_system builds the GPU resources on EcsPreStore and auto-attaches a SafiMeshRenderer. Flecs .copy / .move / .dtor hooks on the component keep the refcounts of _model_handle and texture balanced — including on scene-restore overwrites — so no code path leaks a registry entry.
texture is a SafiTextureHandle, which means primitive textures dedup across entities, participate in hot-reload, and can be assigned by dropping a file onto the Inspector (the inspector edits a relative path string and resolves it through safi_assets_load_texture). Changing any field — shape, dims, color, texture — triggers a rebuild on the next frame. See the full reference on Primitive Shapes.
SafiName
Optional debug-display name; the MicroUI inspector shows it.
SafiSpin
Shipped with the engine so the first demo can spin a cube with three lines of code.