Skill v1.0.0
currentLLM-judged scan90/100version: "1.0.0" name: ada-procedural-models description: Generate low-poly GLB 3D models from an AdaScript program in Ada Studio (primitives, revolved profiles, triangles, part hierarchy, PBR materials), then place them with Model3DSource. allowed-tools: editor.project.context, editor.model.generate, editor.model.inspect, editor.model.validate, editor.components.describe, editor.scene.get, editor.scene.apply, editor.docs.read, editor.build.start, editor.play.start, editor.task.status
Ada Studio Procedural Models
Use this for props, furniture, vehicles, buildings, trees, robots and other stylized or low-poly objects built from simple shapes. Blender is not available; describe the model in AdaScript and Studio writes a .glb. For blocky voxel art use ada-voxel-models instead. Organic meshes, skinned characters and animation clips cannot be generated this way.
- Read
editor.project.contextfor the Assets root. Choose a new destination such asAssets/Models/lamp.glb;editor.model.generatenever overwrites, so use a new name for each revision. - Call
editor.model.generatewithsource(the script) anddestination. The script runs in an isolated VM: no files, network, scene or engine access, a 64 KiB source limit and a small time/step budget. Errors come back as text; fix the script and call again. - Read the result:
parts(names and parents),materials,vertexCount,triangleCountandassetReference(@res://...). Runeditor.model.inspecton the path to read the real bounds and confirm the size is what you intended (1 unit is 1 meter). - Read
editor.components.describewith queryModel3DSource, add that component and a Transform to an entity, and setsourceto theassetReference. Preserve unrelated payload fields and pass the current scene revision. - Build, run visible Play and inspect a completed frame before claiming the model looks right. Generation proves the file is valid, not that it reads well.
Script API
Define func build(model). Axes follow the engine: X right, Y up, Z forward. Every shape is centered on its own origin, so build it, then move it with model.place. Shape calls return the part id (a whole number); keep it in a variable. A failed call records the error and the generation is rejected with that message.
| Call | Meaning | |
|---|---|---|
model.material(i, r, g, b) | Base color of material i (0...63), channels 0...1 in linear space. | |
model.surface(i, metallic, roughness) | PBR factors 0...1. Defaults: metallic 0, roughness 0.6. | |
model.emissive(i, r, g, b) | Glow color 0...1. | |
model.box(w, h, d) | Box with flat sides. | |
model.plane(w, d) | Horizontal plane facing up (a floor; one-sided). | |
model.sphere(radius, segments) | UV sphere. segments is 3...64 around the axis; rings are half of it. | |
model.cylinder(rTop, rBottom, height, segments) | Capped cylinder. Different radii make a frustum; a zero radius makes a cone. | |
model.capsule(radius, height, segments) | Total height including both caps, at least 2 * radius. | |
model.torus(major, minor, segments) | Ring; minor < major. | |
model.profile(radius, y) ... model.lathe(segments) | Revolves the collected (radius, y) points around Y. | |
model.vertex(x, y, z) ... model.triangle(a, b, c) ... model.mesh() | Raw flat-shaded triangles from vertex indices; mesh() returns the part. | |
model.group() | Empty part, used as a pivot or parent. | |
model.place(id, x, y, z) | Position relative to the parent. | |
model.rotate(id, x, y, z) | Degrees; roll (Z) is applied first, then pitch (X), then yaw (Y). | |
model.scale(id, x, y, z) | Positive factors only; mirroring is not supported. The scale of a part without children is baked into its geometry, so squashed spheres (clouds, bushes) shade correctly; a scaled group scales its children and can distort their shading when the factors differ. | |
model.paint(id, i) | Material for the part. Material 0 is neutral gray unless you define it. | |
model.parent(id, parentId) | Attach to a part created earlier; transforms are inherited. | |
model.name(id, "text") | Name shown in the scene hierarchy and editor.model.inspect. |
Winding: triangles must be counter-clockwise when seen from outside, so cross(b - a, c - a) points out. A lathe profile runs from the top, outward and down the side, back to the axis at the bottom (a solid), as in a cup: profile(0, 1), profile(0.5, 1), profile(0.3, 0), profile(0, 0). Repeat the first point to close a ring profile. Corners sharper than 60 degrees get hard edges; gentler turns are smooth.
Limits: 512 parts, 200,000 vertices in total, sizes up to 1000, 256 profile points, 20,000 custom vertices.
func build(model) {model.material(1, 0.55, 0.55, 0.6);model.surface(1, 0.8, 0.35);model.material(2, 1.0, 0.85, 0.4);model.emissive(2, 1.0, 0.8, 0.3);var lamp = model.group();model.name(lamp, "Desk lamp");var base = model.cylinder(0.12, 0.15, 0.04, 24);model.paint(base, 1);model.place(base, 0.0, 0.02, 0.0);model.parent(base, lamp);var arm = model.cylinder(0.015, 0.015, 0.45, 12);model.paint(arm, 1);model.place(arm, 0.0, 0.26, 0.0);model.parent(arm, lamp);var shade = model.cylinder(0.04, 0.12, 0.14, 24);model.paint(shade, 1);model.place(shade, 0.0, 0.55, 0.0);model.parent(shade, lamp);var bulb = model.sphere(0.045, 12);model.paint(bulb, 2);model.place(bulb, 0.0, 0.52, 0.0);model.parent(bulb, lamp);}
Craft
- Model at real-world size in meters; an object about 1 m across is easy to frame.
- Put the origin where the object sits or pivots: a prop's feet at
y = 0, a door's hinge at its own group. - Group the parts (
model.group()plusmodel.parent) so the scene gets a named hierarchy that code can animate by part later. - Use 3-6 materials and distinct roughness values; shape reads better than detail. Segments of 12-24 are enough for small parts.
- Use
planefor floors and walls only when seen from the front; thinboxparts have both sides. - Prefer shapes and loops over raw triangles. Loops over many parts are fine in a script, but each call costs script budget, so build repeated detail with a few
latheorboxparts. - Only the calls above exist; do not use
print, files, imports orSystem. A script that fails validation is rejected before running.