Agent skill

Author Usd Component

by LunCoSim in LunCoSim/lunco-sim

Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata.

Apache-2.0Auto-check passedGame Development

Install Author Usd Component

skills CLI
$ npx skills add LunCoSim/lunco-sim --skill author-usd-component -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install LunCoSim/lunco-sim author-usd-component --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/author-usd-component .claude/skills/author-usd-component && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
author-usd-component
GitHub stars
107
Token cost
~8.7k tokens
SKILL.md length
4,037 words
Files
1
Skills in repo
40
Repo updated
First seen
Licence
Apache-2.0

At a glance

Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata.

  • Works in 3 steps: Edit… → Run python3 scripts/gen_schema.py —… → A new CLASS additionally needs a…
  • Parametric assets
  • SKILL.md covers Component contract before…, Skeleton, Transforms — the mandatory bit and Geometry, plus 9 more sections
  • Calls python3

What it does

Author Usd Component is an agent skill from LunCoSim/lunco-sim. Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata. Use for new habitats, landers, rover parts, shaders, colliders, or parametric assets. xformOpOrder, standard USD schemas, array display colors, collider approximation, component requirements, and component tests are the key contracts. Use build-usd-scene for assembling existing assets, use-asset-library for placement/discovery, and validate-assets for pre-flight checks.

Its SKILL.md is about 8.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Game Development, covering Shaders. The repository describes itself as: Collaborative Multiphysics Cosimulator For Space Missions 🌎🚀🌚. The licence is Apache-2.0.

When your agent uses it

  • Parametric assets
  • Tasks that involve Shaders

Example prompts

  • “/author-usd-component”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Edit crates/lunco-usd-document/schema/schema.usda — the source, never read at runtime
  2. Run python3 scripts/gen_schema.py — regenerates
  3. A new CLASS additionally needs a crates/lunco-usd-document/schema/plugInfo.json

What it can do on your machine

Read from SKILL.md and the folder at commit d1c6f00. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python3

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Author Usd Component loads about 8.7k tokens when it runs. Until then it costs about 131 tokens; SKILL.md has 4,037 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~131
When it runs · the whole SKILL.md, loaded when a task matches
~8.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from LunCoSim/lunco-sim at commit d1c6f00, republished under its Apache-2.0 licence (© LunCoSim). 4,037 words, ~8,725 tokens.

Download SKILL.mdSave it as .claude/skills/author-usd-component/SKILL.md (or your agent's skills folder).
name
author-usd-component
description
Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata. Use for new habitats, landers, rover parts, shaders, colliders, or parametric assets. `xformOpOrder`, standard USD schemas, array display colors, collider approximation, component requirements, and component tests are the key contracts. Use build-usd-scene for assembling existing assets, use-asset-library for placement/discovery, and validate-assets for pre-flight checks.

Author a USD component

USD is the source of truth, projected to Bevy ECS. For a live Editor task, you build a thing through a Rhai typed-operation plan and the document command owner; the engine then projects the authored stage. Do not hand-edit USDA text, patch a file behind the open document, or mutate ECS state to force a result. The resulting component is still a normal .usda asset, but its persistent file is written by the Editor's save operation. Nothing here is a Rust change unless the typed owner is missing a generic USD capability.

Frame is fixed: Y-up, right-handed, −Z-forward, SI metres (docs/architecture/41-axes-and-units.md). Author in that frame. upAxis = "Z" / metersPerUnit != 1 are converted once at the shared USD boundary (crates/lunco-usd-document/src/units.rs; the runtime reader adapter is in lunco-usd-bevy) — never branch on them.

Background: 21-domain-usd.md, 50-usd-driven-visuals.md. Before adding a schema or property, read clean-architecture-and-usd-standards.md and run its standard-schema gate. Use UsdGeom, UsdPhysics, UsdShade, and UsdLux where they own the concept; add a LunCo field only for semantics USD does not define, then delete any overlapping superseded field and reader in the same cutover. Related skills: use-asset-library (where the file goes, how it is discovered, the lunco:// scheme), build-usd-scene (assemble), edit-usd-assembly (interactive headful assembly authoring with screenshot/user-feedback checkpoints), validate-assets (pre-flight it), test-via-api (verify), compose-multidomain-twin. For creating or hot-registering reusable Rhai builders, lints, and component tests, read author-rhai-tool. For the agreed live feedback loop and the Blender/FreeCAD/Fusion/SOLIDWORKS/ COMSOL/OpenUSD practice mapping, read interactive-component-authoring.

Component contract before geometry

Treat a component as a lightweight CAD deliverable rather than an isolated mesh. Before authoring, state its local frame, mount datum, required topology/types, dimensional envelope, mass/inertia owner, collision policy, parameters/units, public provenance, and any deployment or operating limits. Keep public/reference-backed facts separate from Twin study assumptions. For material-dependent parts, record a typed SysML material reference and the condition-specific properties required by the analysis; the USD material assignment/runtime facts remain standard authored USD and must be an explicit projection of that same selection, not a second manual source. Keep this distinct from UsdShade appearance and UsdPhysicsMaterialAPI contact coefficients. Use the SysML material-property gate for the single-source catalogue rule. Do not author physics:density expecting it to affect runtime mass: this component path consumes explicit physics:mass.

Before authoring any component, use the repository-wide five-phase workflow in interactive-component-authoring: analyse the USD/Rhai/Modelica/Rust seam, split by ownership, record sources and rationale in SysML, build with typed Editor tools, and run component → assembly → whole-system gates. This applies to every mission and model. A missing datum or unsupported generic operation is an explicit failing report; this skill does not permit invented dimensions, duplicated requirement literals, or a direct USDA workaround.

One independently reusable or articulated part gets one explicit component root and its own USD file under the Twin's components/ tree. The component's Rhai tool creates typed USD operations and its requirement/test Rhai reads the composed stage and checks both normal and boundary cases. The parent assembly must separately test reference identity, placement, symmetry/clearance, joint endpoints, variant selection, and cross-component wiring. Do not rely on the assembly test to prove the component's internal contract, or on a component test to prove it is correctly mounted.

For live Editor work, create/open the component document, apply the typed Rhai plan, wait for projection, inspect the exact prims, capture a screenshot, and only then save. Do not hand-edit or flatten USDA text to accelerate a component change. Use standard USD variants for genuine configurations; if the typed editor cannot create the needed variant set/blocks or reference-list opinion, report the Rust capability gap instead of using hidden duplicate geometry as a substitute.

Keep the component's Rhai source reviewable: pure plan functions, read-only requirement reports, and runtime test observers are separate responsibilities. Register Twin-scoped libraries through RegisterToolLibrary and verify a real namespaced call in the same process; ListToolLibraries alone is not invocation proof. See author-rhai-tool.

For recurring parametric parts, use the generic assembly_builder::component_bundle_facts and assembly_builder::component_bundle_plan contract instead of adding a vehicle-specific schema or Rust type. Supply SI-metre dimensions, standard Cube/Cylinder/Cone geometry, explicit visual/collision roles, existing material targets, mass/inertia, named attachment or actuator frames, and any limits. The facts call is the validation boundary; the plan returns typed USD operations for the normal proposal/review/commit flow. It does not invent a body, joint, socket occupancy, or material asset. Add those contracts through their existing explicit builders and test the component's normalized facts and composed result in Rhai.

When editing an existing component rather than creating it, use the generic component_editor::update_context or component_editor::selected_update_context facade. It combines the exact document/edit-target/generation checkpoint with the standard property catalog; component_editor::update_plan delegates to the same bundle planner and returns dry typed ops. Keep the bundle recipe in the owning Twin/model package, preserve existing topology and material ownership, and commit one reviewed proposal. Do not infer the recipe from child names or add a Rust registry.

When the component is ready to become a reusable asset, call model_authoring::publish_component(doc, root, edit_target, output, provenance). It checks the top-level component root, standard kind and defaultPrim, applied schemas, reference identities, and caller-supplied provenance, then returns ordinary metadata ops and an explicit save_as command. Review and apply the ops through assembly_edit; call assembly_edit::save_as_document explicitly after projection/lint. The helper does not invent a provenance schema, write USDA directly, or autosave. See scripting-guide.md for the complete facade sequence.

Skeleton

One file = one spawnable thing. The catalog keys off the file, and lunco:spawnable must sit on the stage's defaultPrim.

For live authoring, the skeleton below describes the authored result; it is not permission to paste or rewrite USDA source. Use assembly_edit::new_document or open the exact component document, call the component's Rhai builder, apply its reviewed typed ops, inspect the projected result, and save through the document lifecycle.

usda
#usda 1.0
(
    defaultPrim = "Widget"
    upAxis = "Y"
    metersPerUnit = 1.0
    doc = """What this is, and where its numbers came from."""
)

def Xform "Widget" (
    kind = "component"
    prepend apiSchemas = ["LunCoCatalogAPI"]
)
{
    uniform bool lunco:spawnable = true

    def Scope "Looks" { def Material "Shell" { ... } }

    def Mesh "Body" (prepend apiSchemas = ["MaterialBindingAPI"]) { ... }
}

kind is authored but read by nothing — standard-USD hygiene for DCC interop, not an engine signal. Use doc = "..." prim metadata for descriptions; doc is the standard USD description metadata; do not add a lunco:description attribute (crates/lunco-scene-catalog/src/spawn_meta.rs).

Transforms — the mandatory bit

usda
double3 xformOp:translate = (0, 1.5, 0)
double3 xformOp:scale = (1, 0.5, 1)
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"]

Without xformOpOrder the prim is at identity and every xformOp:* is ignored, silently. Treat a missing or incomplete transform order as an authoring error. This is the single most common way to author a correct-looking file that does nothing.

Supported: translate, scale, orient (quat, USD (w,x,y,z)), transform (matrix4d), rotateX/Y/Z (degrees), all six Euler orders, and the !invert! prefix. Ops compose in listed order, so the last listed applies first to the geometry. An op that is listed but unreadable is skipped as identity — silently.

Also: a translation of (0,0,0), an identity rotation, or an all-zero scale will not overwrite an existing spawned transform (crates/lunco-usd-bevy/src/lib.rs). Authoring zero is a no-op, not a reset.

Geometry

TypeAttributes (defaults)
Cubesize (2.0) — always uniform; use xformOp:scale for a box
Sphereradius (1.0)
Cylinder / Coneradius (1.0), height (2.0), axis ("Z")
Capsuleradius (0.5), height (1.0) — height is the cylindrical section only
Planewidth (2.0), length (2.0)
Meshpoints, faceVertexCounts, faceVertexIndices — all three required
NurbsPatchsee below
BasisCurves / NurbsCurvespoints, widths required

axis defaults to "Z", not Y — a Cylinder with no axis lies along Z (crates/lunco-usd-bevy/src/lib.rs). Cube.width/height/depth do not exist. extent is never read.

Procedural camera backgrounds

Use the reusable environment/starfield_sky.usda pattern for a procedural camera background: author an Xform with MaterialBindingAPI, bind its UsdShade material, and apply LunCoProceduralSkyAPI. This registered API intent is needed because USD has no standard field for a renderer-specific procedural camera background. The projection stamps the existing render-free ProceduralSkybox intent and creates no mesh. Use UsdLuxDomeLight when the scene needs a textured environment light; it is a different USD concept and must not be represented by a skybox marker.

If a referenced component supplies a generic visual proxy but the enclosing vehicle needs a different authored shape, keep the reference for its ports and domain facets, set the referencing visual prim's standard visibility to "invisible", and add the replacement as a visual-only child of the same rigid body. Do not duplicate the electrical/environment component or give the replacement its own body. This keeps USD topology and runtime ownership intact.

UsdGeomPointInstancer is supported for static direct renderable Gprim prototypes: author the standard prototypes, protoIndices, positions, and optional orientations/orientationsf, scales, ids, and invisibleIds properties. The visual projection shares the prototype mesh/material handles so Bevy can automatically batch equal instances. Time-sampled instance arrays and animated prototypes are rejected visibly until runtime sampling is implemented. Arbitrary prototype subtrees are rejected visibly until the multi-mesh render batch is implemented. Native instanceable = true scenegraph instancing, Points, GeomSubset, and subdivisionScheme remain unsupported (catmullClark renders as its raw control cage).

Mesh rules

For Rhai-authored mesh updates, keep points, faceVertexCounts, and faceVertexIndices as numeric arrays and pass them to assembly_builder::mesh_points_update_plan (points only) or assembly_builder::mesh_geometry_update_plan (points plus topology). Review and apply the returned operations through assembly_edit; do not hand-build USD array literals in model-specific scripts. These helpers serialize only at the standard USD attribute boundary and leave material, purpose, and physics metadata untouched.

  • Output is unindexed triangles; n-gons are fan-triangulated, so author convex faces or triangulate yourself.
  • orientation = "leftHanded" flips winding; default is right-handed/CCW.
  • Any malformed topology → no mesh at all, no fallback primitive.
  • Interpolation is inferred from array length only — interpolation metadata is never read. An array matching points.len() is per-vertex; one matching faceVertexIndices.len() is faceVarying; any other length is silently ignored. So uniform/constant normals or UVs vanish.
  • UVs: primvars:st only, UV_0 only. Bare st and primvars:st0 are not read.
  • Normals: authored normals used, else flat-computed. No smoothing.
NurbsPatch
usda
def NurbsPatch "Wall" {
    int uVertexCount = 9
    int vVertexCount = 2
    int uOrder = 3                      # default is 4 if unauthored
    int vOrder = 2
    double[] uKnots = [0,0,0,1,1,2,2,3,3,4,4,4]
    double[] vKnots = [0,0,1,1]
    point3f[] points = [ ... ]          # v-major: v rows of u points
    double[] pointWeights = [ ... ]     # omit ⇒ all 1 ⇒ POLYNOMIAL, not rational
}
  • Point order is index = iv * uVertexCount + iu — v-major rows of u-points. pointWeights uses the same index.
  • uRange / vRange are NEVER READ. The parametric span comes from the knots: [uKnots[uOrder-1], uKnots[uVertexCount]]. Authoring a range that disagrees with the knots does nothing at all.
  • Omitting pointWeights silently gives you the wrong shape. A circle needs rational weights 1, cos45, 1, cos45, …; without them the "circle" is a quadratic B-spline through a square control polygon and bulges ~6% at the diagonals. Pinned by nurbs::tests::dropping_weights_visibly_breaks_the_circle.
  • Tessellation is fixed, not adaptive: clamp(count * 6, 8, 128) per direction.
  • Normals are analytic; a degenerate row (a dome apex) yields +Y rather than NaN.

Every rejection path warns with a reason (crates/lunco-usd-geometry/src/nurbs.rs). If a patch is missing from the render, read the log first — it will say which guard fired, and untrimmed patches log their vert count.

Curves

widths is required — no widths, no mesh. That gate is what stops a camera rail becoming a pipe. Note basis = "bspline" is approximated as CatmullRom (interpolating, not hull-approximating).

Real holes — trim curves

trimCurve:* is the standard USD way to put a genuine hole in a surface, and it is implemented by the importer.

usda
int[] trimCurve:counts = [1]           # curves per loop
int[] trimCurve:orders = [2]           # 2 = linear = a polyline
int[] trimCurve:vertexCounts = [16]
double[] trimCurve:knots = [0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15]
point3f[] trimCurve:points = [ (u, v, w), ... ]   # HOMOGENEOUS 2D
  • Points are homogeneous: the position is (x/w, y/w). Skipping the divide gives a subtly wrong, plausible-looking loop.
  • Coordinates are in the patch's raw parameter space (from the knots), not normalised, and are deliberately not unit/axis converted.
  • Winding does not matter. Classification is even-odd with the domain rectangle as an implicit outer loop, so USD's unstated keep/discard rule never has to be guessed (crates/lunco-usd-geometry/src/trim.rs).
  • Parameter space is anisotropic and non-linear. On a cylinder, u spans circumference while v spans height, and a rational arc parameterises non-uniformly — at the quarter point of a 90° span the true angle is 21.598°, not 22.5°. A circle authored naively renders as a squashed, mis-sized shape.
  • A trim failure renders UNTRIMMED with a warning — bigger than authored, never smaller. A hole that doesn't appear is a log line, not a silent nothing.
  • Trim gives you no reveal: a trimmed surface has no side walls, so the wall thickness at the opening is open. Closing it needs a ruled surface lofted between the two loops, authored separately.

Materials

Bind a UsdPreviewSurface; the Looks scope is convention only, enforced nowhere.

usda
def Scope "Looks" {
    def Material "Shell" {
        token outputs:surface.connect = </Widget/Looks/Shell/Shader.outputs:surface>
        def Shader "Shader" {
            uniform token info:id = "UsdPreviewSurface"
            color3f inputs:diffuseColor = (0.42, 0.40, 0.38)
            float inputs:roughness = 0.9
            float inputs:metallic = 0.0
        }
    }
}

Read: diffuseColor, emissiveColor, metallic, roughness, normal, occlusion, opacity, opacityThreshold, ior, clearcoat, clearcoatRoughness, useSpecularWorkflow, specularColor.

  • MaterialBindingAPI does NOT need applying. Resolution uses compute_bound_material via ::on, so bindings inherit down namespace and collection-based bindings work (crates/lunco-usd-bevy/src/lib.rs). Applying it is harmless.
  • primvars:displayColor must be an ARRAY. color3f[] primvars:displayColor = [(r,g,b)]. A scalar color3f is silently ignored, and the bare displayColor alias is not read at all. Same for float[] primvars:displayOpacity. Values are linear, not sRGB.
  • displayColor is the ONE place a colour is authored, shader or not. A WGSL shader opts in with //!@engine display_color and the engine fills that uniform from the prim's composed primvars:displayColor — so a shader-bound part is still painted the ordinary USD way. Don't author a parallel colour input on the Shader prim; use inputs:* only for what displayColor cannot express (accents, panel scale, wear). An explicit inputs:display_color overrides the fill.
  • inputs:* authored directly on a gprim is not read — put it on a bound Shader.
  • doubleSided (on the gprim, default false) is required for anything you can see through — a trimmed surface reads as a hole from one side and nothing from the other without it.
  • Alpha: opacity < 1 or a connected inputs:opacity → Blend; opacityThreshold > 0 → Mask. For a symbol that must bypass lighting, normals, and shadows, apply LunCoSurfaceAPI to the gprim and author bool lunco:surface:unlit = true. This maps to PbrLook.unlit; it is not emissive radiance and must not be simulated with an emissive-only surface.
  • Navigational annotations use the authored opacity appropriate to their visual job. The reusable route-point dome is translucent, unlit, and shadowless, with a bright green unvisited colour. The generic route policy applies a gray transient material-colour override when its sensor event reaches the point. Its invisible Trigger remains a separate overlap-only prim.
  • For an annotation that is genuinely meant to emit visible radiance, use inputs:emissiveColor; for one that must not emit light, keep that input black. lunco:surface:additive is reserved for authored additive glow surfaces and is not a substitute for unlit presentation.
  • Textures: UsdUVTexture via inputs:file, wrapS/wrapT, inputs:sourceColorSpace. There is no UV primvar reader — UsdPrimvarReader_float2/inputs:st is inert; UVs come from the mesh's own primvars:st. If distinct metallic and roughness textures are both connected, the metallic one is silently dropped (one Bevy slot).

Custom WGSL is bindable through a standard composed UsdShade.Material and UsdShade.Shader, using uniform asset info:wgsl:sourceAsset = @lunco://shaders/x.wgsl@ and typed inputs:* parameters on the Shader. The same material-binding resolution handles inherited and collection bindings. Binding a library shader with no @fragment entry (e.g. pbr_lit.wgsl) is an authored render error; bind a complete material shader instead.

Show full SKILL.md (1,689 more words)Show less

Physics

usda
def Xform "Body" (prepend apiSchemas = ["PhysicsRigidBodyAPI"]) {
    float physics:mass = 4.5
    def Mesh "Hull" (prepend apiSchemas = ["PhysicsCollisionAPI"]) {
        uniform token physics:approximation = "convexHull"
    }
}

Backend is Avian3D. One prim with PhysicsRigidBodyAPI becomes one rigid body aggregating all descendant colliders into a compound; descendants carry PhysicsCollisionAPI only and get no independent body.

A mounted component must not apply PhysicsRigidBodyAPI as a static child. The loader honors the schema wherever it appears: a nested body is separate from its parent and requires a joint. An internal part is mass + geometry (PhysicsMassAPI, PhysicsCollisionAPI on its gprims). A part that must move relative to its host gets a body and a joint authored together. A reusable moving assembly owns every joint in its mechanism, including its host-facing hinge. luncosim --validate reports an unattached nested body as [usd/nested-body-no-joint]; see author-usd-physics.

  • physics:approximation defaults to trimesh, and a trimesh cannot be a moving rigid body in parry. A dynamic mesh body must author "convexHull" or "convexDecomposition" or it will not behave.
  • There is no physics:friction. Use physics:dynamicFriction / physics:staticFriction / physics:restitution on a material bound through material:binding:physics.
  • physics:density is not read anywhere. Author physics:mass.
  • PhysicsScene gravity attributes are vendored but not consumed.
  • Non-cuboid colliders lose exactness under non-uniform scale (tessellated to a convex hull); cuboids stay exact.
  • Joints: PhysicsFixedJoint, PhysicsRevoluteJoint, PhysicsPrismaticJoint. Generic D6 is unsupported and warns.

Render and collision are allowed to differ, and only a cutaway view can tell. That is a legitimate technique, not a bug — but write down that you did it.

For a fixed component mounted on a rover (battery, solar panel, lamp or instrument), keep one root component with its visual, mass and collision facets; do not apply PhysicsRigidBodyAPI to the component unless a joint in the same assembly attaches that body to its host. The host body is the physical owner of fixed descendants. For a photovoltaic component specifically, expose the electrical pin and environment inputs on the reusable root, while the enclosing vehicle owns area, placement and battery wiring (the fixed panel's semantic normal is +Y unless explicitly overridden). This lets the same component be visibly and electrically real without creating a free body or duplicating the panel geometry.

Behaviour — one binding for every language

There is one program contract, not a per-language schema. LunCoProgramAPI is modelled on UsdShade.Shader: its implementation is selected with the standard info:implementationSource / info:id / info:sourceAsset / info:sourceCode vocabulary. info:implementationSource selects exactly one implementation arm; populating another arm is an authoring error. The LunCo runtime dispatches file-backed programs by their source extension.

usda
def Xform "Balloon" (prepend apiSchemas = ["LunCoProgramAPI"]) {
    uniform token info:implementationSource = "sourceAsset"
    uniform asset info:sourceAsset = @lunco://models/Balloon.mo@
    uniform bool lunco:program:realtimeSafe = true

    float inputs:force_y.connect = </Balloon.outputs:netForce>
    float inputs:height.connect  = </Balloon.outputs:position_y>
}

.mo → Modelica, .py → Python, .rhai → Rhai. Nothing else about the prim changes.

  • Role is derived, never declared. A program with inputs:/outputs: ports is a node in the port graph and is stepped; one without them runs for effects only. Parameters are ports — a gain is float inputs:kv = 1.2.
  • A Modelica class output is not an external USD port by itself. Declare each value consumed outside the program as a typed outputs:<name> property on its LunCoProgramAPI prim. Connections must target that declared endpoint; check the generated network's public interface and the composed connection source before relying on it in a material, light, or another program.
  • For a precise change to an existing authored .usda layer, use the explicit Twin Source Editor workflow (OpenTwinSource, then SaveSourceText). It saves the selected layer's authored text. Keep routine stage edits on the typed USD operation and document-journal path.
  • Apply LunCoProgramAPI directly when the program is intrinsic to the thing (a vessel's flight control); apply it to a child Scope for a separable guidance law or patrol tree, so deleting that prim deletes the behaviour. Both placements have info:sourceAsset:subIdentifier for multi-model .mo files.
  • realtimeSafe defaults to false, and the wiring pass will then refuse it a force/torque port on a client-predicted body. A correctly-wired program can do nothing until this is authored true.
  • sourceAsset must be typed asset, never string — only an asset is visible to the resolver, the reference closure, and packaging.
  • Programs use standard info:id for a registered driver, info:sourceAsset for authored source, or info:sourceCode for live, journalled editing, matching the selected info:implementationSource arm. Production programs normally use the asset form. An unknown or unsupported implementation id is an error with a diagnostic; fix the authored source selector instead of adding a fallback.
  • Wiring is native USD connectionPaths; SimConnection is a derived cache, so hand-authoring it is pointless.

Task programs follow the ordinary LunCoProgramAPI rule — a child Scope (conventionally Mission or Program) whose info:sourceAsset names a .rhai source, exactly as .mo selects Modelica. Compose reusable task constructors and policy in Rhai; do not introduce a second behavior format.

Vehicles are a special case with no fallbacks: a wheel missing any required LunCoWheelAPI, PhysxVehicleTireAPI, or PhysicsMaterialAPI attribute logs an error and refuses to spawn. Compose components/mobility/wheel.usda rather than authoring one.

Tunable parameters → Inspector sliders

usda
double radius = 7.345 (
    customData = {
        double min = 3.0
        double max = 12.0
        string unit = "m"
        string type = "double"
    }
)
  • Keys are exactly min, max, unit, type. There is no doc key — documentation goes in USD's own doc = "..." metadata.
  • Both min and max are required, and max > min, or the parameter is skipped silently.
  • type drives write-back and defaults to "float" — set it for a double.
  • Only scalars readable as f64.

The Inspector keeps these values as a local draft while the user edits. Apply commits all changed properties on the selected prim as one generation-checked ApplyUsdOps journal/undo unit; Cancel does not touch USD. AI/Rhai edits use assembly_builder::parameter_plan plus assembly_edit::batch or proposal review, which reaches the same typed document boundary. Parameter hints describe the control surface only; they do not replace USD or Modelica validation.

For a live model component, author instance-specific constants as standard inputs:* overrides and leave the source asset unchanged. A live edit advances the generic lunco_core::ModelStateRevision; it is intentionally not a Modelica-specific compile command. The backend owning the model interprets the revision and reports its own rebuild/reset/readiness result, so the same authoring pattern remains usable for Rhai, physics, or later tool backends.

USD has no expressions. A measured quantity and the transform encoding it are two authored numbers you must keep consistent by hand. Author both, and write the invariant in a comment — the measurement is the durable record, the transform is an encoding of it, and losing the measurement to a scale factor is how a fitted number quietly becomes a magic constant.

Spawnable, variants, persistence

Catalog is fully derived, nothing hardcoded: lunco:spawnable = true on the defaultPrim, id = file stem, category = the immediate parent folder, Title-cased (vessels/rovers/x.usda → "Rovers"). An unreadable file is not spawnable. RescanSpawnCatalog re-reads.

Variants — a variant should choose a component, not restate one:

usda
prepend variantSets = "tire"
variants = { string tire = "regolith" }
variantSet "tire" = {
    "regolith" (prepend references = @lunco://components/mobility/tires/regolith.usda@</Tire>) { }
}

Switch at runtime with SetVariantSelection. Every variant must author every property the others do — a variant that only sets what it needs leaves the another variant's opinions standing, so it accumulates rather than switches.

Keep a referenced component with internal relative relationships on the stable assembly prim, outside the variant. A variant that disables that realization removes the reference with an authored delete references opinion; the variant that uses it keeps the stable reference. A reference placed only inside a variant can lose composed relative relationship targets, so verify the target through StageView::rel_targets on the composed stage.

Persistence: only doc-backed twin scenes keep runtime edits. A scene opened as a raw file path reloads base bytes and discards every edit on restart. A twin is a folder with twin.toml (name, [usd] default_scene), addressed as twin://<name>/<rel>; runtime state lands in .lunco/runtime/, journal in history/.

Adding a new lunco:* property — source + regenerate

A new property is inert until it reaches the registered layer:

  1. Edit crates/lunco-usd-document/schema/schema.usda — the source, never read at runtime
  2. Run python3 scripts/gen_schema.py — regenerates crates/lunco-usd-document/schema/generatedSchema.usda, the file actually compiled in (never hand-edit it)
  3. A new CLASS additionally needs a crates/lunco-usd-document/schema/plugInfo.json Types entry (every_schema_class_is_registered_in_pluginfo pins this)

Registry tests pin source↔generated class parity and (for the wheel domain) schema UI hints, so a forgotten regenerate fails loudly.

Schema-level sliders. customData = { double min; double max; string unit } on a SCHEMA attribute gives every asset composing that schema a derived Inspector slider with zero per-asset authoring (SchemaRegistry::ui_hint, consumed by produce_usd_param_view). Per-asset authored customData still overrides. Hints are UI metadata only — value defaults stay in the component .usda (no-fallback doctrine), e.g. components/mobility/wheel.usda for wheels.

Verify

Pre-flight first — it costs seconds and needs no app:

bash
"$LUNCOSIM_BIN" --validate assets/<your file>.usda

It parses the layer, composes the whole reference closure (so a dangling @lunco://…@ fails loudly here instead of silently at load), and runs the strict wheel reader on any PhysxVehicleWheelAPI prim. See validate-assets.

Then author → load → look. Per test-via-api: drive the already-running workbench, never pkill, and always use the tagged command envelope {"type":"ExecuteCommand","command":"…","params":{…}}. Arguments outside "params" are rejected at the API boundary rather than silently dropped.

Check the log before concluding anything about geometry. The reader warns on every skip path, and "no warning + no geometry" means the prim was never traversed — a different bug from "patch rejected".

For a placed mechanism, inspect the composed transform as well as the source file: every authored xformOp:* must appear in xformOpOrder, and the layer that owns the final placement must be the one that supplies the effective heading. An asset-local forward axis and a scene-specific route heading are separate facts; keep them in separate layers and verify the composed result.

Anti-patterns

  • ❌ xformOp:* without xformOpOrder — identity, silently.
  • ❌ A NurbsPatch circle without pointWeights — a bulged rounded square.
  • ❌ Trusting uRange/vRange — unread; the knots define the span.
  • ❌ Scalar primvars:displayColor — must be an array.
  • ❌ inputs:roughness on the gprim — must be on a bound Shader.
  • ❌ A dynamic mesh body without physics:approximation — trimesh can't move.
  • ❌ physics:friction — does not exist.
  • ❌ string doc inside customData — not a key; use prim doc metadata.
  • ❌ info:sourceAsset typed as string — must be asset.
  • ❌ Editing schema.usda without running scripts/gen_schema.py — the runtime reads only the generated layer.
  • ❌ Hand-editing generatedSchema.usda — the next regenerate erases it.
  • ❌ Assuming kind does something.
  • ❌ Inferring geometry from a screenshot when a number would settle it. Trim loops, control nets and joints are arithmetic — check the arithmetic. A view chosen on a symmetry axis of the hypotheses you are deciding between cannot discriminate them, and will confidently confirm whichever you already believe.
Revolved profile shading

RevolveProfileMesh accepts normal_crease_angle_deg: zero preserves flat shading; a positive angle smooths shared face corners within that crease. Choose the angle in the owning component recipe/requirements. Geometry, collision and topology remain unchanged, and profile rims above the threshold keep sharp normals. The command expands to ordinary USD mesh normals; do not substitute extra geometry to hide flat shading.

© LunCoSim, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/author-usd-component of LunCoSim/lunco-sim.

Open the folder on GitHubat commit d1c6f00

Compare with similar skills

Author Usd Component next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Author Usd Component compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Author Usd Component this skillLunCoSim/lunco-sim107—~8.7kAutomated safety check: PassApache-2.0
PixijsRSamaium/CanvasEngine4012 repos~1.6kAutomated safety check: PassMIT
New ExperimentTresjs/tres3.8k—~782Automated safety check: PassMIT
Assets Shader List AllIvanMurzak/Unity-MCP4.4k—~435Automated safety check: PassApache-2.0
Handle Sdr Tonemap Lutclshortfuse/renodx4.5k—~7.3kAutomated safety check: PassMIT
Unreal Material and VFX Workflowflopperam/unreal-engine-mcp1.1k—~927Automated safety check: PassNone

Similar skills

  • Pixijs

    RSamaium/CanvasEngine

    Use this skill first for ANY PixiJS v8 task; it routes to the right specialized skill for the job.

    401 GitHub starsUsed in 2 repos~1.6k tokens
    Game DevelopmentAuto-check passed
  • New Experiment

    Tresjs/tres

    Create a new experiment in TresJS Lab with all necessary files

    3.8k GitHub stars~782 tokensUpdated yesterday
    Game DevelopmentAuto-check passed
  • Assets Shader List All

    IvanMurzak/Unity-MCP

    List all shaders available in the project assets and packages, sorted by name.

    4.4k GitHub stars~435 tokensUpdated 5 days ago
    Game DevelopmentAuto-check passed
  • Handle Sdr Tonemap Lut

    clshortfuse/renodx

    RenoDX HLSL/Slang shader workflow for proven shader-side SDR tonemap, hard clip, LUT, color grade, and HDR bridge changes.

    4.5k GitHub stars~7.3k tokensUpdated today
    Game DevelopmentAuto-check passed
  • Unreal Material and VFX Workflow

    flopperam/unreal-engine-mcp

    Walks an Unreal Engine MCP agent through building materials, Niagara particle systems, Chaos destruction, and curve assets with inspect-then-edit steps.

    1.1k GitHub stars~927 tokensUpdated 3 mo ago
    Game DevelopmentAuto-check passed
  • Rdc CLI

    BANANASJIM/rdc-cli

    A skill your agent uses when working with RenderDoc capture files (.rdc), analyzing GPU frames, tracing shaders, inspecting draw calls, or running CI assertions against GPU captures.

    182 GitHub stars~2.9k tokensUpdated today
    Game DevelopmentAuto-check passed

More from LunCoSim/lunco-sim

All 40 skills in this repo
  • Nightly Changelog

    LunCoSim/lunco-sim

    Generate concise LunCoSim nightly GitHub release notes with platform downloads, installation guidance, an AI-agent mission prompt, and a changelog link.

    107 GitHub stars~1.1k tokensUpdated today
    Auto-check passed
  • Build or repair a reusable scene component through a live LunCoSim Editor session.

    107 GitHub stars~4.4k tokensUpdated today
    Auto-check passed
  • Assembly Quality

    LunCoSim/lunco-sim

    Build or review a componentized LunCoSim USD assembly with a realistic, dimensionally checkable presentation.

    107 GitHub stars~1.6k tokensUpdated today
    Auto-check passed
  • Author Rhai Tests

    LunCoSim/lunco-sim

    Author and review LunCoSim behavioral, asset-backed, component, mission, visual, and requirements-verification tests.

    107 GitHub stars~3.5k tokensUpdated today
    Auto-check passed
  • Author Rhai Tool

    LunCoSim/lunco-sim

    Create, extend, register, or debug a reusable LunCoSim Rhai tool library for live USD authoring, component linting, inspection, or test support.

    107 GitHub stars~5.2k tokensUpdated today
    Auto-check passed
  • Author Tutorial

    LunCoSim/lunco-sim

    Author an interactive tutorial, guided lesson, onboarding flow, coach-mark tour, or objectives checklist in LunCoSim.

    107 GitHub stars~1.4k tokensUpdated today
    Auto-check passed

Questions about Author Usd Component

What does Author Usd Component do?

Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata. Author Usd Component is an agent skill from LunCoSim/lunco-sim. Author a reusable LunCoSim USD asset from scratch: geometry, materials, physics, behavior, parameters, or spawn-catalog metadata.

When should I use Author Usd Component?

Author Usd Component fits situations like: parametric assets; tasks that involve Shaders.

How do I install Author Usd Component in Claude Code?

Run `npx skills add LunCoSim/lunco-sim --skill author-usd-component -a claude-code`. Or copy the skill folder (skills/author-usd-component in LunCoSim/lunco-sim) into .claude/skills/author-usd-component in your project. Claude Code loads it when a task matches its description.

How do I install Author Usd Component in Codex?

Run `npx skills add LunCoSim/lunco-sim --skill author-usd-component -a codex`. Or copy the skill folder (skills/author-usd-component in LunCoSim/lunco-sim) into .agents/skills/author-usd-component in your project. Codex loads it when a task matches its description.

Can I use Author Usd Component in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add LunCoSim/lunco-sim --skill author-usd-component -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/author-usd-component, .gemini/skills/author-usd-component, .github/skills/author-usd-component and .opencode/skills/author-usd-component in your project.

What does Author Usd Component need to run?

Going by SKILL.md and its folder, Author Usd Component needs the command-line tools its instructions call (python3).

Does Author Usd Component access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Author Usd Component safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Author Usd Component use?

Author Usd Component is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Author Usd Component use?

About 8.7k tokens (SKILL.md is roughly 35k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Author Usd Component?

Skills that share tags, products or a category with Author Usd Component: Pixijs (RSamaium/CanvasEngine, 401 stars), New Experiment (Tresjs/tres, 3.8k stars), Assets Shader List All (IvanMurzak/Unity-MCP, 4.4k stars) and Handle Sdr Tonemap Lut (clshortfuse/renodx, 4.5k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Author Usd Component?

LunCoSim (a GitHub organization) maintains it in LunCoSim/lunco-sim, which has 107 GitHub stars. The repository holds 40 skills in this directory. The repository was last updated on October 9, 2026.

Source: LunCoSim/lunco-sim on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.