@aura3d/cli 5.0.3 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/aura.exports.json +20 -0
- package/data/root-export-dispositions.json +20 -0
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +1 -0
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/verify/index.d.ts +2 -0
- package/dist/commands/verify/index.d.ts.map +1 -0
- package/dist/commands/verify/index.js +11 -0
- package/dist/commands/verify/index.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/look-hdri-check.d.ts +7 -0
- package/dist/look-hdri-check.d.ts.map +1 -0
- package/dist/look-hdri-check.js +88 -0
- package/dist/look-hdri-check.js.map +1 -0
- package/dist/physics-backend-check.d.ts +8 -0
- package/dist/physics-backend-check.d.ts.map +1 -0
- package/dist/physics-backend-check.js +44 -0
- package/dist/physics-backend-check.js.map +1 -0
- package/dist/verify/cdp.d.ts +69 -0
- package/dist/verify/cdp.d.ts.map +1 -0
- package/dist/verify/cdp.js +223 -0
- package/dist/verify/cdp.js.map +1 -0
- package/dist/verify/codes.d.ts +17 -0
- package/dist/verify/codes.d.ts.map +1 -0
- package/dist/verify/codes.js +64 -0
- package/dist/verify/codes.js.map +1 -0
- package/dist/verify/command.d.ts +12 -0
- package/dist/verify/command.d.ts.map +1 -0
- package/dist/verify/command.js +101 -0
- package/dist/verify/command.js.map +1 -0
- package/dist/verify/frame.d.ts +46 -0
- package/dist/verify/frame.d.ts.map +1 -0
- package/dist/verify/frame.js +190 -0
- package/dist/verify/frame.js.map +1 -0
- package/dist/verify/index.d.ts +7 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +7 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/lint.d.ts +43 -0
- package/dist/verify/lint.d.ts.map +1 -0
- package/dist/verify/lint.js +548 -0
- package/dist/verify/lint.js.map +1 -0
- package/dist/verify/run.d.ts +90 -0
- package/dist/verify/run.d.ts.map +1 -0
- package/dist/verify/run.js +317 -0
- package/dist/verify/run.js.map +1 -0
- package/package.json +6 -6
- package/skills/agent-docs/api-signatures.md +89 -23
- package/skills/agent-docs/recipes.md +94 -1
- package/skills/agent-docs/threejs-to-aura3d.md +7 -5
- package/skills/agent-files/AGENTS.md +1 -1
- package/skills/aura3d-materials-environments/SKILL.md +5 -5
- package/skills/aura3d-materials-environments/references/metal-glass.md +32 -0
- package/skills/aura3d-materials-environments/references/visual-helpers.md +20 -0
- package/skills/aura3d-performance/SKILL.md +5 -0
- package/skills/llms.txt +159 -30
- package/skills/manifest.json +4 -0
|
@@ -24,7 +24,7 @@ const app = createAuraApp("#app", { scene: scene({ background: "#101418" }) });
|
|
|
24
24
|
|
|
25
25
|
| Call | Returns | Options |
|
|
26
26
|
|---|---|---|
|
|
27
|
-
| `createAuraApp(target: AuraAppTarget, options: AuraCreateAppOptions)` | `AuraApp` | options: frameMode?: "sync" \| "async" \| undefi…; scene: AuraSceneBuilder \| AuraSc…; worker?: { readonly createWorker: …; physics?: { readonly layers?: AuraC…; diagnostics?: boolean \| AuraDiagnostics…; renderer?: AuraCreateAppRendererOpti…; performanceQuality?: AuraPerformanceQuality \| …; quality?: AuraQualityMode \| undefin…; preserveDrawingBuffer?: boolean \| undefined; autoStart?: boolean \| undefined; resize?: boolean \| undefined; pixelRatio?: number \| { readonly max?:…; qualityRebuild?: {
|
|
27
|
+
| `createAuraApp(target: AuraAppTarget, options: AuraCreateAppOptions)` | `AuraApp` | options: frameMode?: "sync" \| "async" \| undefi…; scene: AuraSceneBuilder \| AuraSc…; worker?: { readonly createWorker: …; physics?: { readonly layers?: AuraC…; diagnostics?: boolean \| AuraDiagnostics…; renderer?: AuraCreateAppRendererOpti…; performanceQuality?: AuraPerformanceQuality \| …; quality?: AuraQualityMode \| undefin…; preserveDrawingBuffer?: boolean \| undefined; autoStart?: boolean \| undefined; resize?: boolean \| undefined; routeState?: boolean \| undefined; pixelRatio?: number \| { readonly max?:…; qualityRebuild?: {… |
|
|
28
28
|
| `app.onFrame(callback: AuraFrameCallback)` | `() => void` | |
|
|
29
29
|
| `app.ready()` | `Promise<void>` | |
|
|
30
30
|
| `app.nodes` | `AuraRuntimeNodeRegistry` | |
|
|
@@ -58,9 +58,27 @@ const stage = scene({ background: "#101418" }).add(primitives.box({ name: "crate
|
|
|
58
58
|
| `scene().physics(next: AuraPhysicsWorldController)` | `AuraSceneBuilder` | |
|
|
59
59
|
| `scene().diagnostics(enabled?: boolean)` | `AuraSceneBuilder` | |
|
|
60
60
|
|
|
61
|
+
## `camera`
|
|
62
|
+
|
|
63
|
+
`camera.frame(subject | subjects | bounds, { fill, angle, azimuth, elevation, padding, fov, aspect })` returns an orbit camera spec placed so the subject fills `fill` (default 0.6) of the limiting frame axis; `camera.frameScene(scene)` frames a whole scene (ground planes ignored); `camera.framePose(...)` returns the pose for `app.camera.setPose` on resize; `camera.worldBounds(subject)` returns the `{ min, max }` box it frames. Solved for `aspect` (default 16/9).
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
void camera.frame(primitives.box({ name: "subject" }), { fill: 0.6 });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Call | Returns | Options |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `camera.perspective(options?: Omit<AuraCameraSpec, "mode">)` | `AuraCameraSpec` | options: position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; up?: AuraVec3 \| undefined; roll?: number \| undefined; offset?: AuraVec3 \| undefined; targetOffset?: AuraVec3 \| undefined; offsetMode?: "scene" \| "target-yaw" \| …; fov?: number \| undefined; focalLength?: number \| undefined; filmGauge?: number \| undefined; filmOffset?: number \| undefined; viewOffset?: { readonly fullWidth: num…; depth?: { readonly mode?: "standa…; near?: number \| undefined; far?: number \| undefined; layers?: number \| undefined; distance… |
|
|
72
|
+
| `camera.orbit(options?: Omit<AuraCameraSpec, "mode">)` | `AuraCameraSpec` | same as `camera.perspective` |
|
|
73
|
+
| `camera.frame(subject: AuraFrameSubject \| AuraFrameBounds \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraCameraSpec` | options: fill?: number \| undefined; angle?: AuraFrameAngle \| undefined; azimuth?: number \| undefined; elevation?: number \| undefined; padding?: number \| undefined; fov?: number \| undefined; aspect?: number \| undefined; minDistance?: number \| undefined; exclude?: ((node: AuraSceneNode) =>… |
|
|
74
|
+
| `camera.frameScene(subject: AuraFrameSubject \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraCameraSpec` | same as `camera.frame` |
|
|
75
|
+
| `camera.worldBounds(subject: AuraFrameSubject \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraFrameBounds` | same as `camera.frame` |
|
|
76
|
+
| `camera.framePose(subject: AuraFrameSubject \| AuraFrameBounds \| readonly AuraFrameSubject[], options?: AuraCameraFrameOptions)` | `AuraFramePose` | same as `camera.frame` |
|
|
77
|
+
| `camera.frameAsset(asset: AuraAssetRef<"model">, options?: AuraCameraFrameAssetOptions)` | `AuraCameraSpec` | asset: type: "model"; format: string; formatMetadata?: FormatMetadata \| undefined; url: string; hash?: string \| undefined; sha256?: string \| undefined; integrity?: AssetIntegrity \| undefined; request?: AssetRequestOptions \| und…; bounds?: AuraVec3 \| undefined; sizeBytes?: number \| undefined; optional?: boolean \| undefined; metadata?: AuraAssetMetadata \| undef…; variants?: AuraAssetVariants \| undef…; requiredDecoders?: readonly AuraAssetRequire…; lods?: readonly AuraAssetLodLeve…; colliderUrl?: string \| undefined; budget?… |
|
|
78
|
+
|
|
61
79
|
## `looks`
|
|
62
80
|
|
|
63
|
-
Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options.
|
|
81
|
+
Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options. HDRI-backed presets load `/hdri/<file>.hdr` from `public/hdri/`; a missing file degrades to a procedural probe, logs one `LOOK_HDRI_MISSING` console error and lists it in `app.diagnostics().warnings`. `looks.heroStage({ target, radius, background })` is studio IBL + key/fill/rim + a floor that fades into `background`; `looks.heroRig` is the lights alone; `looks.fogForExtent({ extent })` sizes fog by distance. A scene with NO look, light, environment or effect gets the hero stage automatically at mount (any authored look/light/environment/effect, or a scene kit, turns it off; `?a3d-cold-start-look=0` forces it off).
|
|
64
82
|
|
|
65
83
|
```ts
|
|
66
84
|
void looks.preset("product-studio");
|
|
@@ -68,7 +86,11 @@ void looks.preset("product-studio");
|
|
|
68
86
|
|
|
69
87
|
| Call | Returns | Options |
|
|
70
88
|
|---|---|---|
|
|
71
|
-
| `looks.
|
|
89
|
+
| `looks.heroRig(options?: AuraHeroRigOptions)` | `AuraNodeBuilder<AuraGroupNode>` | options: target?: AuraVec3 \| undefined; radius?: number \| undefined; intensity?: number \| undefined; keyColor?: string \| undefined; fillColor?: string \| undefined; rimColor?: string \| undefined; shadow?: boolean \| undefined; name?: string \| undefined |
|
|
90
|
+
| `looks.heroStage(options?: AuraHeroStageOptions)` | `AuraNodeBuilder<AuraGroupNode>` | options: environment?: "none" \| "studio" \| "neut…; environmentIntensity?: number \| undefined; background?: string \| undefined; floor?: false \| { readonly color?…; target?: AuraVec3 \| undefined; radius?: number \| undefined; intensity?: number \| undefined; keyColor?: string \| undefined; fillColor?: string \| undefined; rimColor?: string \| undefined; shadow?: boolean \| undefined; name?: string \| undefined |
|
|
91
|
+
| `looks.fogForExtent(options: AuraFogForExtentOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: extent: number; color?: string \| undefined; haze?: number \| undefined; mode?: "exp" \| "exp2" \| undefined; name?: string \| undefined |
|
|
92
|
+
| `looks.fogDensityForExtent(extent: number, haze?: number, mode?: "exp" \| "exp2")` | `number` | |
|
|
93
|
+
| `looks.preset(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` (+1 overloads) | `AuraNodeBuilder<AuraGroupNode>` | overrides: sun?: { readonly azimuthDeg?: n…; exposureEv?: number \| undefined; fogDensityScale?: number \| undefined; accent?: string \| undefined; background?: string \| undefined; hdri?: AuraAssetRef<"texture"> \|…; environmentIntensity?: number \| undefined; environmentRotationDeg?: number \| undefined; key?: { readonly color?: AuraCo…; rim?: false \| { readonly color?…; fog?: { readonly color?: AuraCo…; grade?: { readonly contrast?: num…; post?: AuraPostPresetId \| { read… |
|
|
72
94
|
| `looks.nodes(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` | `readonly AuraNodeBuilder<AuraSceneNode>[]` | same as `looks.preset` |
|
|
73
95
|
| `looks.appOptions(id: AuraLookId, options: AuraLookBuildOptions \| undefined)` | `Pick<AuraCreateAppOptions, "pixelRatio" \| "renderer">` | options: expansion?: "auto" \| "v0" \| "v1" \| un…; flags?: QrFlags \| undefined |
|
|
74
96
|
| `looks.list()` | `readonly AuraLookId[]` | |
|
|
@@ -77,7 +99,7 @@ void looks.preset("product-studio");
|
|
|
77
99
|
|
|
78
100
|
## `material`
|
|
79
101
|
|
|
80
|
-
Colours are CSS strings. Metals need an environment.
|
|
102
|
+
Colours are CSS strings (`#rgb`, `#rrggbbaa`, `rgb()`, `hsl()`, `oklch()`, names); an invalid value throws `COLOR_INVALID` naming the option. Metals need an environment.
|
|
81
103
|
|
|
82
104
|
```ts
|
|
83
105
|
void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
@@ -85,18 +107,21 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
85
107
|
|
|
86
108
|
| Call | Returns | Options |
|
|
87
109
|
|---|---|---|
|
|
88
|
-
| `material.custom(spec: AuraCustomMaterialSpec)` | `AuraMaterialSpec` | spec: glsl: { readonly vertex?: strin…; wgsl?: { readonly vertex?: strin…; uniforms?: Readonly<Record<string, A…; uniformAliases?: Readonly<Record<string, s…; defines?: Readonly<Record<string, s…; transparent?: boolean \| undefined; depthWrite?: boolean \| undefined; depthTest?: boolean \| undefined; side?: "front" \| "back" \| "doubl…; blending?: "
|
|
110
|
+
| `material.custom(spec: AuraCustomMaterialSpec)` | `AuraMaterialSpec` | spec: glsl: { readonly vertex?: strin…; wgsl?: { readonly vertex?: strin…; uniforms?: Readonly<Record<string, A…; uniformAliases?: Readonly<Record<string, s…; defines?: Readonly<Record<string, s…; transparent?: boolean \| undefined; depthWrite?: boolean \| undefined; depthTest?: boolean \| undefined; side?: "front" \| "back" \| "doubl…; blending?: "additive" \| "multiply" \|…; fog?: boolean \| undefined; patch?: Readonly<Record<string, s…; receiveShadow?: boolean \| undefined; castShadow?: boolean \| undefined |
|
|
89
111
|
| `material.toon(options?: AuraToonMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; steps?: number \| undefined; ramp?: AuraAssetRef<"texture", s…; shadowColor?: string \| undefined; rim?: AuraMaterialRimSpec \| und… |
|
|
112
|
+
| `material.strata(options?: AuraStrataMaterialOptions)` | `AuraMaterialSpec` | options: bands?: number \| undefined; colors?: readonly string[] \| undef…; heightRange?: readonly [number, number]…; noise?: number \| undefined; noiseScale?: number \| undefined; seam?: number \| undefined; variation?: number \| undefined; slope?: AuraStrataSlopeOptions \| …; roughness?: number \| undefined; seed?: number \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
113
|
+
| `material.caustics(options?: AuraCausticsMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; causticColor?: string \| undefined; strength?: number \| undefined; scale?: number \| undefined; speed?: number \| undefined; surfaceY?: number \| undefined; depthFade?: number \| undefined; roughness?: number \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
114
|
+
| `material.lightBeam(options?: AuraLightBeamMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; intensity?: number \| undefined; length?: number \| undefined; falloff?: number \| undefined; edgeSoftness?: number \| undefined; noise?: number \| undefined; noiseScale?: number \| undefined; noiseSpeed?: number \| undefined |
|
|
90
115
|
| `material.matcap(options: AuraMatcapMaterialOptions)` | `AuraMaterialSpec` | options: map: AuraAssetRef<"texture"> \|…; color?: string \| undefined; opacity?: number \| undefined; transparent?: boolean \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
91
116
|
| `material.depth(options?: AuraDepthPreviewMaterialOptions)` | `AuraMaterialSpec` | options: near?: number \| undefined; far?: number \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
92
117
|
| `material.normal(options?: AuraNormalMaterialOptions)` | `AuraMaterialSpec` | options: side?: "front" \| "back" \| "doubl… |
|
|
93
118
|
| `material.distance(options?: AuraDistanceMaterialOptions)` | `AuraMaterialSpec` | options: referencePosition?: AuraVec3 \| undefined; near?: number \| undefined; far?: number \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
94
119
|
| `material.shadow(options?: AuraShadowMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; opacity?: number \| undefined; side?: "front" \| "back" \| "doubl…; light?: "directional" \| "point" \|… |
|
|
95
120
|
| `material.node(options: TslMaterialNodeOptions \| ShaderGraphMaterialOptions)` | `AuraMaterialSpec` | |
|
|
96
|
-
| `material.pbr(
|
|
121
|
+
| `material.pbr(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
97
122
|
| `material.physical(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
98
123
|
| `material.emissive(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
99
|
-
| `material.metal(
|
|
124
|
+
| `material.metal(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
100
125
|
| `material.rubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
101
126
|
| `material.glass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
102
127
|
| `material.clearcoat(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
@@ -109,6 +134,8 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
109
134
|
| `material.brushedMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
110
135
|
| `material.frostedGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
111
136
|
| `material.clearGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
137
|
+
| `material.studioMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
138
|
+
| `material.studioGlass(options?: AuraMaterialSpec & { readonly tint?: AuraColor; })` | `AuraMaterialSpec` | |
|
|
112
139
|
| `material.blackRubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
113
140
|
| `material.matteClay(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
114
141
|
| `material.ceramic(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
@@ -121,7 +148,7 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
121
148
|
| `material.carPaint(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
122
149
|
| `material.planet(preset?: AuraSolarPlanetMaterialPreset, options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
123
150
|
| `material.atmosphere(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
124
|
-
| `material.proceduralTexture(texture: AuraProceduralTextureKind, options?: Partial<Omit<AuraProceduralTextureSpec, "texture" \| "kind">>)` | `AuraProceduralTextureSpec` | options:
|
|
151
|
+
| `material.proceduralTexture(texture: AuraProceduralTextureKind, options?: Partial<Omit<AuraProceduralTextureSpec, "texture" \| "kind">>)` | `AuraProceduralTextureSpec` | options: strength?: number \| undefined; contrast?: number \| undefined; scale?: number \| undefined; direction?: AuraVec3 \| undefined; colorA?: string \| undefined; colorB?: string \| undefined |
|
|
125
152
|
| `material.proceduralTextures` | `value` | |
|
|
126
153
|
| `material.parameters(name: string, spec: AuraMaterialSpec)` | `AuraEditableMaterialParameters` | |
|
|
127
154
|
| `material.fromParameters(parameters: AuraEditableMaterialParameters)` | `AuraMaterialSpec` | parameters: kind: "aura-material-parameters"; name: string; material: AuraMaterialSpec; roughness: number; metallic: number; metalness: number; transmission: number; clearcoat: number; clearcoatRoughness: number; thickness: number; ior: number; sheen: number; iridescence: number; anisotropy: number; envMapIntensity: number; emissiveIntensity: number |
|
|
@@ -143,11 +170,11 @@ void lights.directional({ position: [4, 6, 3], target: [0, 0, 0], intensity: 3,
|
|
|
143
170
|
|---|---|---|
|
|
144
171
|
| `lights.lensFlare(options?: AuraLensFlareOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; light?: string \| undefined; elements?: readonly AuraLensFlareEle…; occlusion?: AuraLensFlareOcclusion \| …; edgeFade?: number \| undefined; intensity?: number \| undefined; runtime?: AuraRuntimeNodeSpec \| und… |
|
|
145
172
|
| `lights.ambient(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined |
|
|
146
|
-
| `lights.hemisphere(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined |
|
|
173
|
+
| `lights.hemisphere(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined; color?: string \| undefined |
|
|
147
174
|
| `lights.lightProbe(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; sh?: readonly number[] \| undef…; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined |
|
|
148
|
-
| `lights.directional(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraDirectional
|
|
149
|
-
| `lights.point(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; power?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; photometricProfile?: AuraPhotometricProfile \| … |
|
|
150
|
-
| `lights.spot(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; angle?: number \| undefined; penumbra?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; intensity?: number \| undefined; power?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; photometricProfile?: AuraPhotometricProfile \| …; projectedImage?: AuraProjectedLightImage \|… |
|
|
175
|
+
| `lights.directional(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraDirectional…; castShadow?: boolean \| undefined |
|
|
176
|
+
| `lights.point(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; power?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; castShadow?: boolean \| undefined; photometricProfile?: AuraPhotometricProfile \| … |
|
|
177
|
+
| `lights.spot(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; angle?: number \| undefined; penumbra?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; intensity?: number \| undefined; power?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; castShadow?: boolean \| undefined; photometricProfile?: AuraPhotometricProfile \| …; projectedImage?: AuraProjectedLightImage \|… |
|
|
151
178
|
| `lights.studio(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: intensity?: number \| undefined |
|
|
152
179
|
| `lights.rect(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; width?: number \| undefined; height?: number \| undefined; twoSided?: boolean \| undefined |
|
|
153
180
|
| `lights.rectArea(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | same as `lights.rect` |
|
|
@@ -165,23 +192,23 @@ void environments.neutral({ intensity: 0.8, background: false });
|
|
|
165
192
|
|
|
166
193
|
| Call | Returns | Options |
|
|
167
194
|
|---|---|---|
|
|
168
|
-
| `environments.outdoor(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…;
|
|
169
|
-
| `environments.room(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…;
|
|
170
|
-
| `environments.space(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s
|
|
195
|
+
| `environments.outdoor(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; biome?: OutdoorBiome \| undefined |
|
|
196
|
+
| `environments.room(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; colorTemperatureK?: number \| undefined |
|
|
197
|
+
| `environments.space(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s… |
|
|
171
198
|
| `environments.underwater(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | same as `environments.space` |
|
|
172
199
|
| `environments.preset(name: AuraEnvironmentPresetName, options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined |
|
|
173
200
|
| `environments.neutral(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
174
201
|
| `environments.none(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined |
|
|
175
|
-
| `environments.capture(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; position?: AuraVec3 \| undefined; resolution?: 64 \| 128 \| 256 \| undefined; update?: "
|
|
202
|
+
| `environments.capture(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; position?: AuraVec3 \| undefined; resolution?: 64 \| 128 \| 256 \| undefined; update?: "on-demand" \| "once" \| { …; include?: "all" \| "sky-only" \| unde… |
|
|
176
203
|
| `environments.hdri(options: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; texture: AuraAssetRef<"texture">; reflectionTexture?: AuraAssetRef<"texture"> \|… |
|
|
177
|
-
| `environments.studio(options?:
|
|
204
|
+
| `environments.studio(options?: AuraStudioEnvironmentOptions)` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: preset?: "studio" \| "softbox" \| un…; name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined |
|
|
178
205
|
| `environments.materialLab(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
179
206
|
| `environments.productHero(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
180
207
|
| `environments.nightCinematic(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
181
208
|
| `environments.metalStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
182
209
|
| `environments.glassStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
183
210
|
| `environments.presets()` | `readonly AuraEnvironmentMapPreset[]` | |
|
|
184
|
-
| `environments.forMaterial(materialClass: "
|
|
211
|
+
| `environments.forMaterial(materialClass: "product" \| "studio" \| "metal" \| "glass")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
|
|
185
212
|
|
|
186
213
|
## `sky`
|
|
187
214
|
|
|
@@ -193,8 +220,8 @@ void sky.preetham({ sun: { elevationDeg: 35, azimuthDeg: 120 } });
|
|
|
193
220
|
|
|
194
221
|
| Call | Returns | Options |
|
|
195
222
|
|---|---|---|
|
|
196
|
-
| `sky.preetham(options: AuraSkyPreethamOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: moon?: unknown; groundColor?: string \| undefined; sun: AuraSkySunSpec; turbidity?: number \| undefined; rayleigh?: number \| undefined; mieCoefficient?: number \| undefined; mieDirectionalG?: number \| undefined;
|
|
197
|
-
| `sky.gradient(options: AuraSkyGradientOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options:
|
|
223
|
+
| `sky.preetham(options: AuraSkyPreethamOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: exposure?: number \| undefined; moon?: unknown; groundColor?: string \| undefined; sun: AuraSkySunSpec; turbidity?: number \| undefined; rayleigh?: number \| undefined; mieCoefficient?: number \| undefined; mieDirectionalG?: number \| undefined; stars?: AuraSkyStarsOptions \| und…; clouds?: AuraSkyCloudsOptions \| un…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
|
|
224
|
+
| `sky.gradient(options: AuraSkyGradientOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: intensity?: number \| undefined; moon?: unknown; clouds?: AuraSkyCloudsOptions \| un…; sun?: AuraSkySunSpec \| undefined; zenith: string; horizon: string; ground?: string \| undefined; exponent?: number \| undefined; horizonGlow?: number \| undefined; bands?: readonly AuraSkyBand[] \| …; stars?: AuraSkyStarsOptions \| und…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
|
|
198
225
|
| `sky.hdri(options: {…})` | `AuraNodeBuilder<AuraSceneNode>` | options: texture: AuraAssetRef<"texture">; intensity?: number \| undefined; rotation?: number \| undefined; blurriness?: number \| undefined; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
|
|
199
226
|
| `sky.dayNight(options?: {…})` | `{ readonly nodes: readonly AuraSceneNode[]; readonly background: stri…` | options: hour?: number \| undefined; seed?: number \| undefined; starCount?: number \| undefined; cloudCellCount?: number \| undefined; starLimit?: number \| undefined; cloudLimit?: number \| undefined; model?: "preetham" \| "gradient" \|… |
|
|
200
227
|
|
|
@@ -210,7 +237,7 @@ void primitives.cylinder({ name: "plinth", size: [2, 0.9, 2], material: material
|
|
|
210
237
|
|---|---|---|
|
|
211
238
|
| `primitives.box(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: name?: string \| undefined; material?: AuraMaterialSpec \| undefi…; size?: number \| AuraVec3 \| undef…; castShadow?: boolean \| undefined; receiveShadow?: boolean \| undefined; physics?: AuraNodePhysicsSpec \| und…; instances?: readonly AuraTransformSpe…; instanceColors?: readonly string[] \| undef…; geometry?: AuraCustomGeometrySpec \| …; text3D?: Omit<AuraText3DGeometry, …; lod?: AuraRootLodSpec \| undefin…; params?: AuraPrimitiveParams \| und…; position?: AuraVec3 \| undefined; pivot?: AuraVec3 \| undefined; rotation?: Aur… |
|
|
212
239
|
| `primitives.sphere(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
213
|
-
| `primitives.plane(options:
|
|
240
|
+
| `primitives.plane(options: AuraPlaneOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: position?: AuraVec3 \| undefined; layers?: number \| undefined; physics?: AuraNodePhysicsSpec \| und…; name?: string \| undefined; pivot?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 \| undef…; lookAt?: AuraVec3 \| undefined; rotationOrder?: AuraEulerOrder \| undefined; quaternion?: AuraQuat \| undefined; renderOrder?: number \| undefined; frustumCulled?: boolean \| undefined; userData?: Record<string, unknown> \|…; castShadow?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; … |
|
|
214
241
|
| `primitives.cylinder(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
215
242
|
| `primitives.capsule(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
216
243
|
| `primitives.torus(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
@@ -235,6 +262,8 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
|
|
|
235
262
|
|
|
236
263
|
| Call | Returns | Options |
|
|
237
264
|
|---|---|---|
|
|
265
|
+
| `effects.godRays(options?: AuraGodRaysOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: name?: string \| undefined; source?: AuraVec3 \| "sun" \| AuraGo…; intensity?: number \| undefined; decay?: number \| undefined; samples?: 64 \| 32 \| 48 \| undefined; color?: string \| undefined; density?: number \| undefined; medium?: number \| undefined |
|
|
266
|
+
| `effects.volumetricBeam(options?: AuraVolumetricBeamOptions)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; direction?: AuraVec3 \| undefined; length?: number \| undefined; endRadius?: number \| undefined; color?: string \| undefined; intensity?: number \| undefined; falloff?: number \| undefined; edgeSoftness?: number \| undefined; noise?: number \| undefined; noiseScale?: number \| undefined; noiseSpeed?: number \| undefined; radialSegments?: number \| undefined |
|
|
238
267
|
| `effects.contactShadows(options?: AuraContactShadowOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: length?: number \| undefined; thickness?: number \| undefined; steps?: 8 \| 12 \| 16 \| undefined; intensity?: number \| undefined; lights?: "sun" \| "shadowed" \| unde… |
|
|
239
268
|
| `effects.toon(options?: AuraToonOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; bands?: number \| undefined; bandMix?: number \| undefined; outline?: number \| undefined; ink?: readonly [number, number,…; edgeStart?: number \| undefined; edgeEnd?: number \| undefined; size?: readonly [number, number]…; order?: AuraPostPassOrder \| undef…; inkSource?: AuraToonInkSource \| undef…; fadeDistance?: number \| undefined; inkColor?: string \| undefined |
|
|
240
269
|
| `effects.pixelation(options?: AuraPixelationOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: size?: number \| undefined; name?: string \| undefined |
|
|
@@ -255,7 +284,7 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
|
|
|
255
284
|
| `effects.vignette(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
256
285
|
| `effects.filmGrain(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
257
286
|
| `effects.chromaticAberration(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
258
|
-
| `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("
|
|
287
|
+
| `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("depth" \| "colo…; insertAt?: PostInsertAt \| undefined; order?: AuraPostPassOrder \| undef…; enabled?: boolean \| undefined |
|
|
259
288
|
| `effects.fog(options?: FogNodeOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
260
289
|
| `effects.cinematicBloom(options?: Omit<AuraEffectNode, "kind" \| "effect">)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
261
290
|
| `effects.volumetricFog(options?: AuraVolumetricFogOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
@@ -305,6 +334,31 @@ void instances.cone({ name: "pines", transforms: [{ position: [0, 1, 0], scale:
|
|
|
305
334
|
| `instances.custom(spec: AuraCustomGeometrySpec, options: Omit<AuraInstancedPrimitiveOptions, "geometry">)` | `AuraNodeBuilder<AuraPrimitiveNode>` | spec: kind: "aura-custom-geometry"; positions: AuraVec3Attribute; normals?: AuraVec3Attribute \| undef…; uvs?: AuraVec2Attribute \| undef…; uv1s?: AuraVec2Attribute \| undef…; colors?: readonly string[] \| reado…; indices: AuraIndexAttribute; attributes?: Readonly<Record<string, A…; bounds?: { readonly min: AuraRootV…; groups?: readonly AuraGeometryGrou…; drawRange?: AuraGeometryDrawRangeSpec…; morphTargets?: readonly AuraGeometryMorp…; topology?: "triangles" \| "lines" \| "…; dynamic?: boolean \| undefined |
|
|
306
335
|
| `instances.model(asset: TAsset, options: {…})` (+1 overloads) | `AuraNodeBuilder<AuraGroupNode>` | options: name?: string \| undefined; material?: AuraMaterialSpec \| undefi…; castShadow?: boolean \| undefined; receiveShadow?: boolean \| undefined; visible?: boolean \| undefined; role?: AuraModelRole \| undefined; scaleMode?: AuraModelScaleMode \| unde…; targetHeight?: number \| undefined; targetMaxDimension?: number \| undefined; targetLength?: number \| undefined; physics?: AuraNodePhysicsSpec \| und…; hiddenNodeNames?: readonly string[] \| undef…; materialOverrides?: readonly AuraModelMateria…; variant?: string \| undefined; lod?… |
|
|
307
336
|
|
|
337
|
+
## `points`
|
|
338
|
+
|
|
339
|
+
Static cloud. `Float32Array` positions + packed `Uint8Array`/`Uint32Array`/`Float32Array` colors stay typed (no per-point objects, no color parsing). Live clouds: `points.stream` + `pointStreamOf(handle)` (docs/agents/streaming-buffers.md).
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
void points({ positions: new Float32Array([0, 0, 0, 1, 1, 1]), colors: new Uint8Array(8).fill(255), size: 2 });
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
| Call | Returns | Options |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| `points(options: AuraPointsOptions)` | `AuraNodeBuilder<AuraPointsNode>` | options: points?: AuraLinePositionsInput \| …; positions?: AuraLinePositionsInput \| …; colors?: readonly string[] \| Float…; size?: number \| undefined; sizeAttenuation?: boolean \| undefined; map?: AuraMaterialTextureInput …; alphaTest?: number \| undefined; transparent?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; name?: string \| undefined; visible?: boolean \| undefined; runtime?: AuraRuntimeNodeSpec \| und…; position?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 \| undef… |
|
|
348
|
+
| `points.stream(spec: AuraPointStreamSpec)` | `AuraNodeBuilder<AuraPointsNode>` | spec: capacity: number; colors?: AuraPointStreamColorForma…; colorSpace?: "linear" \| "srgb" \| undef…; size?: number \| undefined; sizeAttenuation?: boolean \| undefined; map?: AuraMaterialTextureInput …; alphaTest?: number \| undefined; transparent?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; bounds?: { readonly min: AuraVec3;…; name?: string \| undefined; visible?: boolean \| undefined; runtime: AuraRuntimeNodeSpec; position?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 … |
|
|
349
|
+
|
|
350
|
+
## `pointStreamOf`
|
|
351
|
+
|
|
352
|
+
Narrows `app.nodes.require(id)` of a `points.stream({ capacity, colors?, runtime: { id } })` node to `{ write, lease, setDrawCount, drawCount, capacity, stats }`; typed in-place updates, one dirty-range upload.
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
void pointStreamOf;
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
| Call | Returns | Options |
|
|
359
|
+
|---|---|---|
|
|
360
|
+
| `pointStreamOf(handle: AuraRuntimeNodeHandle \| undefined)` | `AuraPointStreamHandle` | |
|
|
361
|
+
|
|
308
362
|
## `geometry`
|
|
309
363
|
|
|
310
364
|
`define` builds the spec; mount it with `geometry.custom(spec, { material })`.
|
|
@@ -351,7 +405,7 @@ void interactions.orbit({ target: "crate" });
|
|
|
351
405
|
|
|
352
406
|
## `physics`
|
|
353
407
|
|
|
354
|
-
Rapier-backed worlds, bodies and colliders; see `docs/api/` physics pages before use.
|
|
408
|
+
Rapier-backed worlds, bodies and colliders; see `docs/api/` physics pages before use. Needs `npm install @dimforge/rapier3d-compat@0.20.0` (optional peer; once installed, every app that imports the engine loads its 1.08 MB gzip chunk at startup unless it is built with `aura3d()` from `@aura3d/assets/vite`, which leaves Rapier out of apps that never mention physics, or `aura3d({ physics: "lazy" })`, which downloads it on first use (then `await app.physics.ready?.()` before imperative calls); see `docs/concepts/physics.md`): without it you get the typed `PHYSICS_BACKEND_MISSING` error (`error.code`, `error.installCommand`) in `diagnostics().errors`, on the canvas, from `app.physics`, from `preloadPhysics()`, and from `aura3d doctor`. Working example: recipe 16 (`recipes-src/physics-basics`).
|
|
355
409
|
|
|
356
410
|
```ts
|
|
357
411
|
void physics.box;
|
|
@@ -397,6 +451,18 @@ void AnimationController;
|
|
|
397
451
|
| `AnimationController#listClips()` | `readonly AuraRegisteredAnimationClip<TClipId, TEvent>[]` | |
|
|
398
452
|
| `AnimationController#dispose()` | `void` | |
|
|
399
453
|
|
|
454
|
+
## `createSpatialPicker`
|
|
455
|
+
|
|
456
|
+
CPU picking for `points` (nearest to the ray within a pixel or world tolerance) and instanced nodes (per-instance box or sphere). `picker.pick({ camera, ndc }, { viewport, pixelRadius })`; `picker.invalidate(name)` after writing a position or matrix buffer in place. `bindSpatialPicking` adds hover and click.
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
const picker = createSpatialPicker({ nodes: [] });
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
| Call | Returns | Options |
|
|
463
|
+
|---|---|---|
|
|
464
|
+
| `createSpatialPicker(sourcesOf: SpatialPickerSources \| (() => SpatialPickerSources))` | `SpatialPicker` | |
|
|
465
|
+
|
|
400
466
|
## `createFixedStepLoop`
|
|
401
467
|
|
|
402
468
|
Deterministic simulation loop for games.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Recipes: copy a working scene, then adapt it
|
|
2
2
|
|
|
3
|
-
Coming from three.js? Read [`threejs-to-aura3d.md`](threejs-to-aura3d.md) first; signatures are in [`api-signatures.md`](api-signatures.md). Recipes 8 to 15 build complete scenes with no admitted models. Start here when the task looks like one of these. Each recipe is a complete `src/main.ts` body that compiled against the engine types (`tsc`, strict) and uses only non-deprecated root exports. Copy it, change names and numbers, run `npm run build`. Do not read `node_modules/@aura3d/engine/dist` to discover the API first: that is the slowest path (in the cold-start trial, 8 of 15 runs spent all 40 turns reading `.d.ts` and `dist` files without writing a scene, while the game-loop runs, which had a complete sample to copy, finished in 11 to 17 turns).
|
|
3
|
+
Coming from three.js? Read [`threejs-to-aura3d.md`](threejs-to-aura3d.md) first; signatures are in [`api-signatures.md`](api-signatures.md). Recipes 8 to 15 build complete scenes with no admitted models; recipe 16 covers physics. Start here when the task looks like one of these. Each recipe is a complete `src/main.ts` body that compiled against the engine types (`tsc`, strict) and uses only non-deprecated root exports. Copy it, change names and numbers, run `npm run build`. Do not read `node_modules/@aura3d/engine/dist` to discover the API first: that is the slowest path (in the cold-start trial, 8 of 15 runs spent all 40 turns reading `.d.ts` and `dist` files without writing a scene, while the game-loop runs, which had a complete sample to copy, finished in 11 to 17 turns).
|
|
4
4
|
|
|
5
5
|
Page needs a `<div id="app">` (or a `<canvas id="app">`); `createAuraApp("#app", { scene })` mounts into it. Background, camera, lights and fog are ordinary nodes or scene options. Animate inside `app.onFrame((frame) => ...)` using `frame.dt`. Await `app.ready()` when you need the first frame. With a HUD or other elements beside the canvas, give the canvas a wrapper of definite size (`<div id="stage" style="width:640px;height:360px"><canvas id="app"></canvas></div>`), or pass `resize: false` and a `width`/`height` on the canvas; the app sizes itself to its parent, and an auto-height parent that also holds a HUD can make a canvas grow until it draws nothing on 4.1.3 and earlier (see troubleshooting, black frame checklist).
|
|
6
6
|
|
|
7
7
|
Rules that every recipe follows:
|
|
8
8
|
|
|
9
9
|
- Orbit control: `interactions.orbit({ target: "<node name>" })`. It takes a node name, nothing else.
|
|
10
|
+
- Frame the subject with `camera.frame(subject, { fill: 0.6 })` (a node or array of nodes, not a hand-placed `camera.perspective`; pass `aspect` if the canvas is not 16:9).
|
|
10
11
|
- Metals need something to reflect: add `environments.neutral(...)` (or a look preset) or a metallic material renders dark.
|
|
11
12
|
- Instancing: `instances.cone/cylinder/sphere/box({ transforms, colors })` draws one draw call per shape; shapes are unit-sized, so `scale` sets the size.
|
|
12
13
|
- A deformed surface needs vertices: build a grid with `geometry.define` and mount it with `geometry.custom(...)`.
|
|
@@ -719,6 +720,98 @@ const app = createAuraApp("#app", { scene: stage });
|
|
|
719
720
|
await app.ready();
|
|
720
721
|
```
|
|
721
722
|
|
|
723
|
+
## 16. Physics: bodies, a stacked wall, a swinging rope, collision events, fixed step
|
|
724
|
+
|
|
725
|
+
- Goal: `.physics({...})` bodies on nodes (static ground, a 5 x 4 crate wall), a ball on a `ball-socket` rope from a gantry pivot that swings into the wall, `app.physics.onCollision`, and a fixed 1/60 step so the result depends on the step count only. `?steps=140` freezes the simulation after 140 steps for captures.
|
|
726
|
+
- Install first: `npm install @dimforge/rapier3d-compat@0.20.0`. Rapier is an optional peer, so it is only in your build if you install it; once installed, every app that imports `@aura3d/engine` downloads and initialises it at startup, even one that never calls physics (a 2.85 MB chunk, 1.08 MB gzip, measured; the `aura3d-no-tla` condition does not avoid it). Build with `aura3d()` from `@aura3d/assets/vite` and Vite leaves Rapier out of any app whose code never mentions physics; `aura3d({ physics: "lazy" })` keeps it but downloads it on first use, in which case add `await app.physics.ready?.()` before the imperative `app.physics.createBody` / `createJoint` calls below (`docs/concepts/physics.md`). Without it the app does not crash silently: `diagnostics().errors` and the canvas show `PHYSICS_BACKEND_MISSING` with this install command, `app.physics` throws the same typed error, and `aura3d doctor` flags the project.
|
|
727
|
+
- Final frame: the ball has hit the wall at the base of a dark gantry and the crates are tumbling into a heap on green ground.
|
|
728
|
+
- Replaces in three.js: a Rapier/cannon setup plus a manual body-to-mesh sync loop (here `physics: { bindNodes: true }` plus `.runtime({ id })` on each node does the sync).
|
|
729
|
+
- Values: gravity `[0, -9.81, 0]`, step 1/60 with at most 5 catch-up steps, ball mass 6 radius 0.55, crates mass 1, friction 0.7.
|
|
730
|
+
- Capture: `recipes-src/physics-basics/frame.png`; source: `recipes-src/physics-basics/main.ts`.
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
import { camera, createAuraApp, lights, material, primitives, scene, type AuraNodeBuilder, type AuraPrimitiveNode } from "@aura3d/engine";
|
|
734
|
+
|
|
735
|
+
// Rigid bodies on scene nodes: a static ground, a stacked wall of crates, a ball on a ball-socket rope that swings
|
|
736
|
+
// into the wall (the wrecking ball), collision events, and a deterministic fixed step. Needs the Rapier backend
|
|
737
|
+
// (`npm install @dimforge/rapier3d-compat@0.20.0`); without it the app reports PHYSICS_BACKEND_MISSING in
|
|
738
|
+
// diagnostics().errors and on the canvas. ?steps=N freezes the simulation after N fixed steps for a capture.
|
|
739
|
+
const STEP = 1 / 60; // fixed step: the result depends on the step count, never on the display rate
|
|
740
|
+
const MAX_CATCH_UP = 5;
|
|
741
|
+
const freezeAt = new URLSearchParams(location.search).get("steps");
|
|
742
|
+
|
|
743
|
+
const CRATE = 0.5;
|
|
744
|
+
const COLORS = ["#c98b4b", "#b8763a", "#d9a066", "#a86a32"];
|
|
745
|
+
const crates: AuraNodeBuilder<AuraPrimitiveNode>[] = [];
|
|
746
|
+
for (let row = 0; row < 5; row += 1) {
|
|
747
|
+
for (let col = 0; col < 4; col += 1) {
|
|
748
|
+
crates.push(
|
|
749
|
+
primitives.box({ name: `crate-${row}-${col}`, size: CRATE, material: material.pbr({ color: COLORS[(row + col) % COLORS.length]!, roughness: 0.8 }) })
|
|
750
|
+
.position(0.2 + (row % 2) * 0.02, CRATE / 2 + row * CRATE, (col - 1.5) * CRATE)
|
|
751
|
+
.runtime({ id: `crate-${row}-${col}` })
|
|
752
|
+
.physics({ type: "dynamic", shape: "box", halfExtents: [CRATE / 2, CRATE / 2, CRATE / 2], mass: 1, friction: 0.7, restitution: 0.05 })
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const PIVOT: [number, number, number] = [-1.2, 6.2, 0]; // rope anchor; the ball starts 5 m away on the arc and falls into the wall
|
|
758
|
+
const BALL_START: [number, number, number] = [-4.2, 2.2, 0];
|
|
759
|
+
|
|
760
|
+
const app = createAuraApp("#app", {
|
|
761
|
+
physics: { gravity: [0, -9.81, 0], bindNodes: true, seed: 7 },
|
|
762
|
+
scene: scene({ background: "#8fb4d8" })
|
|
763
|
+
.add(lights.hemisphere({ skyColor: "#cfe2f6", groundColor: "#4a5a3a", intensity: 1.2 }))
|
|
764
|
+
.add(lights.directional({ name: "sun", position: [6, 9, 5], target: [0, 0.5, 0], intensity: 4, color: "#fff0d8", shadow: true }))
|
|
765
|
+
.add(
|
|
766
|
+
primitives.box({ name: "ground", size: [30, 0.5, 14], material: material.pbr({ color: "#6f8a50", roughness: 1 }) })
|
|
767
|
+
.position(0, -0.25, 0)
|
|
768
|
+
.runtime({ id: "ground" })
|
|
769
|
+
.physics({ type: "static", shape: "box", halfExtents: [15, 0.25, 7], friction: 0.9 })
|
|
770
|
+
)
|
|
771
|
+
.add(primitives.box({ name: "gantry", size: [8, 0.25, 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0], PIVOT[1] + 0.125, 0))
|
|
772
|
+
.add(primitives.box({ name: "post-left", size: [0.3, PIVOT[1], 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0] - 3.85, PIVOT[1] / 2, 0))
|
|
773
|
+
.add(primitives.box({ name: "post-right", size: [0.3, PIVOT[1], 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0] + 3.85, PIVOT[1] / 2, 0))
|
|
774
|
+
.addMany(crates)
|
|
775
|
+
.add(
|
|
776
|
+
primitives.sphere({ name: "ball", size: 1.1, material: material.pbr({ color: "#d94b3d", metallic: 0.4, roughness: 0.35 }) })
|
|
777
|
+
.position(...BALL_START)
|
|
778
|
+
.runtime({ id: "ball" })
|
|
779
|
+
.physics({ type: "dynamic", shape: "sphere", radius: 0.55, mass: 6, restitution: 0.1, friction: 0.5 })
|
|
780
|
+
)
|
|
781
|
+
.camera(camera.perspective({ position: [4, 3.6, 13], target: [-0.8, 2.7, 0], fov: 46, near: 0.1, far: 200 }))
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// The anchor is a body-less pivot: a static body with no node, and the rope is a ball-socket joint through the
|
|
785
|
+
// world-space anchor point above the ball (anchors are world space, bodies keep their local frames).
|
|
786
|
+
app.physics.createBody({ name: "pivot", type: "static", shape: "sphere", radius: 0.05, position: PIVOT, sensor: true });
|
|
787
|
+
app.physics.createJoint({ kind: "ball-socket", bodyA: "pivot", bodyB: "ball", anchor: PIVOT });
|
|
788
|
+
|
|
789
|
+
// Collision events: count solid hits and remember the hardest one. The page object is what the render gate reads.
|
|
790
|
+
const stats = { steps: 0, solidHits: 0, hardestHit: 0, firstCrateHitStep: -1 };
|
|
791
|
+
app.physics.onCollision((event) => {
|
|
792
|
+
if (event.phase !== "begin" || event.sensor) return;
|
|
793
|
+
stats.solidHits += 1;
|
|
794
|
+
stats.hardestHit = Math.max(stats.hardestHit, event.relativeSpeed);
|
|
795
|
+
const names = [event.nodeA, event.nodeB];
|
|
796
|
+
if (stats.firstCrateHitStep < 0 && names.includes("ball") && names.some((name) => name?.startsWith("crate-"))) stats.firstCrateHitStep = stats.steps;
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
const frozen = freezeAt === null ? undefined : Math.max(0, Math.floor(Number(freezeAt)));
|
|
800
|
+
let accumulator = 0;
|
|
801
|
+
const stepOnce = (): void => { app.physics.step(STEP); stats.steps += 1; };
|
|
802
|
+
if (frozen !== undefined) for (let i = 0; i < frozen; i += 1) stepOnce();
|
|
803
|
+
app.onFrame((frame) => {
|
|
804
|
+
if (frozen !== undefined) return;
|
|
805
|
+
accumulator = Math.min(accumulator + frame.dt, STEP * MAX_CATCH_UP);
|
|
806
|
+
while (accumulator >= STEP) { stepOnce(); accumulator -= STEP; }
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const positions = (): Record<string, readonly number[]> =>
|
|
810
|
+
Object.fromEntries(["ball", "crate-0-0", "crate-4-2"].map((name) => [name, app.physics.bodies.require(name).position().map((value) => Math.round(value * 1e4) / 1e4)]));
|
|
811
|
+
(window as unknown as { __PHYSICS_RECIPE__: unknown }).__PHYSICS_RECIPE__ = { stats, positions, diagnostics: () => app.diagnostics().errors };
|
|
812
|
+
await app.ready();
|
|
813
|
+
```
|
|
814
|
+
|
|
722
815
|
## Type traps (strict `tsc` errors agents hit on a first draft)
|
|
723
816
|
|
|
724
817
|
| Error | Cause | Fix |
|
|
@@ -5,13 +5,13 @@ Read this first. Aura3D is a standalone engine with its own renderer: you descri
|
|
|
5
5
|
| If you would write (three.js) | Write (Aura3D) | Difference that bites |
|
|
6
6
|
| --- | --- | --- |
|
|
7
7
|
| `Scene` + `PerspectiveCamera` + `WebGLRenderer` + `setAnimationLoop` | `scene({ background }).camera(camera.perspective({ position, target, fov }))` then `createAuraApp("#app", { scene })`; per frame `app.onFrame((f) => ...)` with `f.dt`; `await app.ready()` | No renderer, resize or loop code. Mount target is a `<canvas>` or a sized wrapper. |
|
|
8
|
-
| `new Mesh(geometry, material)` | `primitives.box({ name, size: [w, h, d], material: material.pbr({ color }) })` (also `sphere`, `plane`, `cylinder`, `capsule`, `cone`, `torus`, `torusKnot`, `lathe`, `extrude`, `tube`) | Primitives are unit shapes scaled by `size`. `sphere` size is the **diameter**; `plane` is `[w, 1, d]` and lies in XZ. Unknown option keys are reported, not ignored. |
|
|
9
|
-
| `mesh.position.set(...)`, `rotation`, `scale` | Build time: `.position(x, y, z).rotate(x, y, z).scale(2)`. Runtime: `.runtime({ id, mutable: true })`, then `app.nodes.require(id).setPosition(x, y, z)`, `.setRotation`, `.setScale` | A node you move later needs `runtime({ id, mutable: true })`. Handles have `set*`, not `.position.x`. Vectors are tuples, not `Vector3`. |
|
|
8
|
+
| `new Mesh(geometry, material)` | `primitives.box({ name, size: [w, h, d], material: material.pbr({ color }) })` (also `sphere`, `plane`, `cylinder`, `capsule`, `cone`, `torus`, `torusKnot`, `lathe`, `extrude`, `tube`) | Primitives are unit shapes scaled by `size`. `sphere` size is the **diameter**; `plane` is `[w, 1, d]` and lies in XZ (`size: [w, d]` is accepted and means the same). Unknown option keys are reported, not ignored. |
|
|
9
|
+
| `mesh.position.set(...)`, `rotation`, `scale` | Build time: `.position(x, y, z).rotate(x, y, z).scale(2)`. Runtime: `.runtime({ id, mutable: true })`, then `app.nodes.require(id).setPosition(x, y, z)`, `.setRotation`, `.setScale` | A node you move later needs `runtime({ id, mutable: true })`. Handles have `set*`, not `.position.x`. Vectors are tuples, not `Vector3`. Builder `.rotation(x, y, z)` is accepted as `.rotate(x, y, z)`. |
|
|
10
10
|
| `Group` / `Object3D` hierarchy, bones as groups | `group("arm", [children], { position }).runtime({ id, mutable: true })` | Children use local transforms; a group's origin is its pivot. |
|
|
11
11
|
| `BufferGeometry` + `setAttribute` + `setIndex` | `geometry.custom(geometry.define({ positions, normals, uvs, colors, indices, bounds }), { name, material })` | Typed arrays in; `bounds` required; `colors` are per-vertex RGBA. `geometry.dynamic` for geometry you rewrite. |
|
|
12
|
-
| `MeshStandardMaterial` | `material.pbr({ color, roughness, metallic, emissive, opacity })` | Also `physical` (clearcoat), `emissive`, `toon`, `matcap`. Colour is a CSS string. |
|
|
12
|
+
| `MeshStandardMaterial` | `material.pbr({ color, roughness, metallic, emissive, opacity })` | Also `physical` (clearcoat), `emissive`, `toon`, `matcap`. Colour is a CSS string. `side: "double"` means `doubleSided: true`; `transparent`, `wireframe`, `flatShading` have no equivalent and are reported (see [`guessable-api.md`](guessable-api.md)). |
|
|
13
13
|
| `ShaderMaterial` | `material.custom({ glsl: { vertex, fragment }, uniforms: { t: { type: "f32", value: 0 } } })`; `handle.setUniform("t", v)` | GLSL 3.00 (`in`/`out`, write `outColor`). Prelude names: `position`, `normal`, `uv`, `modelViewProjectionMatrix`, `modelMatrix`, `cameraPosition`, `uTime` (wall-clock seconds; advances only while the app redraws every frame: give the node `.runtime({ id, mutable: true })` or register `app.onFrame(...)`, else a static scene draws once and `uTime` looks frozen). Node/TSL: `material.node`. |
|
|
14
|
-
| `DirectionalLight` + `castShadow` | `lights.directional({ position, target, intensity, shadow: true })` | `shadow: true` on the light; meshes take `castShadow` / `receiveShadow`. Also `point`, `spot`, `hemisphere`, `ambient`, `rect`. |
|
|
14
|
+
| `DirectionalLight` + `castShadow` | `lights.directional({ position, target, intensity, shadow: true })` | `shadow: true` on the light (`castShadow: true` is accepted as the same); meshes take `castShadow` / `receiveShadow`. Also `point`, `spot`, `hemisphere`, `ambient`, `rect`. |
|
|
15
15
|
| `scene.environment = pmrem` | `environments.neutral({ intensity, background: false })` (also `studio`, `outdoor`, `hdri`, `preset`) | Metals render dark without an environment. `looks.preset(id)` bundles environment, key light, fog and grade. |
|
|
16
16
|
| `scene.background = sky shader`, `Sky` | `sky.preetham({ sun: { elevationDeg, azimuthDeg } })`, `sky.gradient`, `sky.dayNight` | Move the sun live with `app.atmosphere.setSky({ sun })`. |
|
|
17
17
|
| `scene.fog = new FogExp2` | `effects.fog({ color, density })` | A scene node, not a property. |
|
|
@@ -25,6 +25,8 @@ Read this first. Aura3D is a standalone engine with its own renderer: you descri
|
|
|
25
25
|
| `geometry.dispose()`, `renderer.dispose()` | `app.dispose()` | One call disposes everything the app made. |
|
|
26
26
|
| `Vector3`, `Clock` | `[x, y, z]` tuples at option sites; `Clock` from `@aura3d/engine`; `Vector3` from `@aura3d/math/three` | Mutable three.js-shaped math lives only in `@aura3d/math/three`. |
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Guessed names: every three.js spelling that models actually tried, with the decision for each (alias, reported with a suggestion, or documented), is in [`guessable-api.md`](guessable-api.md). Unknown builder options are reported once with a "did you mean" suggestion (`OPTION_IGNORED`; strict mode throws).
|
|
29
|
+
|
|
30
|
+
Not here: `GLTFLoader` (use a typed asset, `model(assets.x)`; see [`asset-workflow.md`](asset-workflow.md)), `renderer.toneMapping` (use the look preset's `output`, or `output: { toneMapping: "aces" }`; `"aces-filmic"` is not a name), custom render targets and `EffectComposer` chains (no equivalent; check `docs/migration/from-threejs.md`).
|
|
29
31
|
|
|
30
32
|
Porting an existing file: `npx @aura3d/cli@latest migrate three "src/**/*.ts"` lists what maps, what is approximate and what has no equivalent; it is a worklist, not a drop-in conversion. Full mapping with verdicts: [`docs/migration/from-threejs.md`](../migration/from-threejs.md).
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Aura3D agent instructions
|
|
2
2
|
|
|
3
|
-
Read ./llms.txt first. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
|
|
3
|
+
Read ./llms.txt first: its first screen holds typechecked, GPU-rendered patterns (scene skeleton, instancing, custom shader, procedural geometry, rigged models, game loop, physics, particles) to copy and adapt; do not open `node_modules` or `dist` for APIs. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
|
|
@@ -53,16 +53,16 @@ Step 1, presets (always try first):
|
|
|
53
53
|
`environments.productHero`, `environments.nightCinematic`,
|
|
54
54
|
`environments.metalStudio`, `environments.glassStudio`, or
|
|
55
55
|
`environments.forMaterial("metal" | "glass" | "product" | "studio")`.
|
|
56
|
+
Metal and glass that must read: [metal-glass](references/metal-glass.md) (`material.studioMetal`, `material.studioGlass`, softbox).
|
|
56
57
|
3. Outdoor: `sky.dayNight({ hour })` returns `nodes`, `background`, and
|
|
57
58
|
`dayFactor`; `weather.precipitation({ type: "rain" | "snow" })` and
|
|
58
59
|
`weather.wetGround(...)`; `water.surface({ preset: "calm" | "moderate" |
|
|
59
60
|
"rough" | "storm" })`. Add their `nodes` with `scene().addMany(...)` and use
|
|
60
|
-
the returned `background`.
|
|
61
|
+
the returned `background`. Strata, caustics, god rays, beams: [visual helpers](references/visual-helpers.md).
|
|
61
62
|
4. Choosing values: start from `material.presets()` and the named builders
|
|
62
|
-
above (car paint is `material.clearcoatPaint`, thin glass
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`packages/engine/src/devtools/materials/MaterialValidation.ts`) is a source
|
|
63
|
+
above (car paint is `material.clearcoatPaint`, thin glass `material.glass`,
|
|
64
|
+
brushed metal `material.brushedMetal`); `GAME_READY_MATERIAL_PRESETS` in
|
|
65
|
+
`packages/engine/src/devtools/materials/MaterialValidation.ts` is a source
|
|
66
66
|
reference for values, not an import.
|
|
67
67
|
|
|
68
68
|
Step 2, a licensed local file (Poly Haven or another CC0 / CC-BY source you
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Metal and glass that must read
|
|
2
|
+
|
|
3
|
+
Use `material.studioMetal({ color, roughness })` and `material.studioGlass({ tint, ior, thickness })` for product shots,
|
|
4
|
+
trophies, spheres and anything where chrome, steel, gold or clear glass is the subject. They carry sensible reflection
|
|
5
|
+
settings (full metalness, no clearcoat, `envMapIntensity` 1 for metal and 1.25 for glass, absorption tint and
|
|
6
|
+
`thickness` for glass), wrap `material.metal` and `material.glass`, and every option still overrides.
|
|
7
|
+
|
|
8
|
+
Why they exist: chrome and glass only show what they reflect or refract. A near-uniform probe (`environments.neutral()`,
|
|
9
|
+
plain `environments.studio()`) plus a pale backdrop leaves both invisible, and raising `envMapIntensity` only brightens
|
|
10
|
+
the same flat reflection. Clear glass additionally needs physical transmission, which is off unless
|
|
11
|
+
`A3D_QR_MATERIALS_TRANSMISSION` is on; without it the glass draws as an opaque white ball and the console prints
|
|
12
|
+
`TRANSMISSION_DEGRADED`.
|
|
13
|
+
|
|
14
|
+
What the helpers do automatically:
|
|
15
|
+
|
|
16
|
+
- A scene that uses either helper and authors no `environments.*` node or look binds the `softbox` environment itself.
|
|
17
|
+
An authored environment or look always wins.
|
|
18
|
+
- `createAuraApp` turns physical transmission on for a scene that uses `material.studioGlass`. A source that sets the
|
|
19
|
+
flag explicitly (`?a3d-qr=-materials.transmission`, `qualityRebuild.flags`) wins. It is decided from the scene given
|
|
20
|
+
to `createAuraApp`; a later `setScene` does not re-decide it.
|
|
21
|
+
|
|
22
|
+
To choose it explicitly: `environments.studio({ preset: "softbox" })`, or `environments.preset("softbox")` (background on).
|
|
23
|
+
`environments.studio()` and `environments.neutral()` without the option render exactly as before.
|
|
24
|
+
|
|
25
|
+
The `softbox` probe is a structured studio: an overhead softbox, a tall key softbox, two strip lights, a windowed fill
|
|
26
|
+
with mullions and two dark flags over a gradient room, at the neutral probe's mean brightness so diffuse lighting
|
|
27
|
+
matches. It is embedded in the package (about 230 KB of base64 in its own lazy chunk), so no `/aura-environments` fetch
|
|
28
|
+
and no copied file; `public/aura-environments/softbox.*` (about 0.5 MiB) ships the same bytes. Regenerate with
|
|
29
|
+
`node_modules/.bin/tsx tools/environments/bake-softbox.ts`.
|
|
30
|
+
|
|
31
|
+
Glass shows the opaque scene behind it, so put it in front of a mid-tone or graded backdrop, not plain white. The helpers
|
|
32
|
+
cannot add a backdrop for you.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Visual helpers: strata, caustics, god rays, light beams
|
|
2
|
+
|
|
3
|
+
Judge each in a real-GPU capture, not from the declaration.
|
|
4
|
+
|
|
5
|
+
- `material.strata({ bands, colors, heightRange, noise, noiseScale, seam, variation, slope, roughness, seed })`: height-banded
|
|
6
|
+
rock lit by scene lights and the environment. `slope: { angle, blend, flatColor }` gives flat ground its own colour and bands only
|
|
7
|
+
steep faces, so a canyon wall separates from its floor. Apply it to a `geometry.custom` heightfield (recipe 13) or any mesh. Static: it
|
|
8
|
+
never forces redraws.
|
|
9
|
+
- `material.caustics({ color, causticColor, strength, scale, speed, surfaceY, depthFade, roughness })`: a lit floor with moving
|
|
10
|
+
caustic light projected straight down in world X/Z. It reads `uTime`, so it animates only while the app draws frames: a
|
|
11
|
+
scene with a `uTime` custom shader is a declared time consumer on builds that carry `timeConsumers`; elsewhere add
|
|
12
|
+
`app.onFrame(() => {})` or a looping timeline.
|
|
13
|
+
- `effects.godRays({ source, intensity, decay, samples, color, density, medium })`: screen-space shafts (post stage S4, WebGL2
|
|
14
|
+
v2 chain). `source` is `"sun"` (the strongest directional light), a world point or a light-like `{ position }` / `{ direction }`.
|
|
15
|
+
Shafts need emitters: visible sky or a bright object near the light, and the light in front of the camera. `samples` is capped by the
|
|
16
|
+
quality tier, and the stage is off on the Low tier. Where the backend cannot sample depth the depth-aware chain throws
|
|
17
|
+
`DEPTH_POSTPROCESS_UNSUPPORTED`, as for every depth-aware post effect.
|
|
18
|
+
- `effects.volumetricBeam({ position, direction, length, endRadius, color, intensity, falloff, edgeSoftness, noise })`: an open
|
|
19
|
+
additive cone mesh (a primitive node) with a soft silhouette and drifting dust noise; two beams make a lighthouse.
|
|
20
|
+
`material.lightBeam(options)` is the same shader for your own cone.
|
|
@@ -69,6 +69,11 @@ manual quality override invalidates every measurement that follows.
|
|
|
69
69
|
`batched.mesh` apply texture maps since 4.1; older builds drew them white.
|
|
70
70
|
An empty instance array is a no-op with an `INSTANCES_EMPTY` diagnostic, not a
|
|
71
71
|
crash, so plan for zero placements instead of special-casing them.
|
|
72
|
+
Live data at 10-20 Hz (sensor point clouds, per-frame poses): do not rebuild the scene or tuple
|
|
73
|
+
arrays. Declare `points.stream({ capacity, colors: "u8x4n", runtime: { id } })`, then
|
|
74
|
+
`pointStreamOf(app.nodes.require(id)).write(Float32Array, { colors })`; dense instance writes go
|
|
75
|
+
through `handle.setInstanceMatrices` (primitives and, since 5.1, `instances.model`). See
|
|
76
|
+
`docs/agents/streaming-buffers.md`; its numbers are measurements, not claims.
|
|
72
77
|
4. Scattered props (trees, rocks, lamps, houses): plan placements with
|
|
73
78
|
`scatterAlongPath` or `scatterInArea` (seeded, exclusion zones, `minDistance`,
|
|
74
79
|
slope and height filters), split them by distance with `splitScatterByDistance`
|