Asset to instances model prefab instance authoring chain
Use this for
Not for
Pairs with: Model authoring parametric primitives and CSG for custom meshes, Asset acquisition strategy, Materials and looks PBR surfaces color metalness and textures, Terrain and heightmaps procedural generation biomes and foliage
Step 0: get an asset that actually renders
An entity renders a mesh through a RenderableComponent with shape: "mesh" and an assetId that points at a model asset. The renderer streams that asset's mesh variant, so the model asset must CARRY a mesh variant. Three ways to get one:
- Generate it.
generation_create_job(generation.job.create) with a model capability andtarget.importOnSuccess: trueruns the provider and, on success, finalizes a project asset that already carries the mesh variant. This is the reliable agent path to a fresh custom object. Model, image, texture, and audio are the wired modalities; image-to-model capabilities turn a generated image into a mesh. - Reuse the catalog. A published catalog model already carries its variant, conditioning, and colliders. Prefer this when a close match exists (see asset-acquisition-strategy).
- Author it parametrically.
model_save(project.asset.model_document.save) writes aModelAuthoringDocumentof primitives plus CSG onto the model asset'srendering.modelslice. IMPORTANT SEAM:model_savewrites only that authored slice and leaves the mesh variant untouched, and there is no agent-callable server action that bakes the parametric document into a mesh variant. So a model whose only content is amodel_savedocument does not render on its own yet; it is a durable authoring artifact awaiting a bake. For a shape that must render now from the agent surface alone, generate or reuse instead, or use a primitiveshape(box,sphere,capsule, ...) directly on theRenderableComponentwith no mesh asset at all (a cylinder or cone is not aRenderableComponentshape; it exists only inside a model).
Step 1: reference the asset from an entity
project_add_component a RenderableComponent: { "type": "RenderableComponent", "shape": "mesh", "assetId": "<model asset id>", "visible": true }. Set a materialRef to skin it (see materials-and-looks). For a plain primitive, use shape: "box" (or another primitive) and omit assetId.
Step 2: compose a reusable prefab
A prefab is a named, reusable entity subtree. Author it directly with project_create_prefab (project.prefab.create): the request carries the full entities array inline (at least one, a rootLocalId, each entity a localId/name/components record). Put the RenderableComponent (and a TransformComponent, scripts, colliders) on the root entity so every instance inherits them. You do not need live scene entities to make a prefab; the inline entities array IS the prefab body. project_update_prefab and project_delete_prefab round it out.
Step 3a: instance the prefab once
project_create_entity (project.entity.create) with prefabId set (a uuid or the prefab key) creates one prefab-instance entity in a world. Its effective components resolve from the prefab, with per-instance componentOverrides layered on top. This is the single-instance path. Note: it places the prefab root as one instance entity; it does not expand a multi-entity prefab subtree into separate graph entities, and there is no one-call bulk prefab fan-out on the authoring surface. For many prefab instances, issue several project_create_entity calls. The instanceSet path below does not draw meshes today (see its note).
Step 3b: instance one primitive many times (the scale path)
RENDER TRUTH TODAY: the aggregate renderer draws a palette kind with geometry box, sphere or plane as that primitive, and draws every cylinder and every mesh kind (whatever its meshAssetId) as a unit box. So an InstanceSet does NOT place copies of a model yet. For repeated models (trees, rocks, props), place model entities within the entity budget instead (the renderer batches identical meshes where eligible); for grass and ground cover on terrain, use a terrain scatter (see terrain-and-heightmaps). Use the InstanceSet aggregate for many copies of a box, sphere or plane (a voxel field, crates, markers) instead of thousands of entities:
- Create the resource:
project_create_asset(project.asset.create) withassetKind: "instanceSet". project_apply_instance_set_patch(instance_set.patch.apply) with aconfigureop:layout: "free"(scattered transforms) orlayout: "grid"(dense voxels), and apaletteof kinds withgeometrybox,sphereorplane(plus optionalmaterialAssetId,tint). The schema also acceptscylinder, andmeshwith ameshAssetId, but both render as boxes today.- Fill it in the same tool with
populate(explicitinstancesfor free, orcellsfor grid),scatterInstances(seeded scatter of N over a region), orfill(a grid box region). One call carries the whole batch; you do not author one entity per instance. - Place the aggregate: put an
InstanceSetComponent{ "type": "InstanceSetComponent", "instanceSetAssetId": "<the instanceSet asset id>", "enabled": true }plus aTransformComponenton an entity. The renderer lowers the whole set to instanced draws per kind and physics to one aggregate collider, so the scene stays inside the per-entity complexity budget that thousands of real entities would blow.
When mesh kinds do render, the meshAssetId in the palette will obey the same rule as Step 0: a variant-bearing asset (generated or catalog), not a bare model_save document.
Pitfalls
- A
model_save-only model has no mesh variant; wiring its id into aRenderableComponentrenders nothing. Generate or reuse for a mesh that must show now. - An instanceSet palette kind with geometry
meshorcylinderrenders as a box today, whatever itsmeshAssetId; do not promise a forest or a rock field from it. RenderableComponent.shapemust bemeshwhen you pass anassetId; leaving a primitive shape ignores the mesh.project_create_prefabneeds at least one entity and arootLocalIdthat names a reallocalIdin theentitiesarray.- Free layout uses
instances(transforms); grid layout usescells(integer coordinates). Mixing them per op is silently one or the other; match the layout you configured. - Bulk placement of a full behaving prefab (subtree plus scripts) is not one call; the instanceSet path multiplies primitive GEOMETRY, not prefab behavior.
Verify
project_get_graph_snapshotto confirm the model asset, the prefab, the instance entity'sprefabId, or the entity'sInstanceSetComponent(instanceSetAssetId,layout).qa_capture_renderer_viewportto confirm the instances actually render (this is where a missing mesh variant shows up as an empty capture).