@aura3d/cli 5.1.0 → 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/root-export-dispositions.json +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- 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/package.json +6 -6
- package/skills/agent-docs/api-signatures.md +65 -22
- package/skills/agent-docs/recipes.md +93 -1
- package/skills/agent-docs/threejs-to-aura3d.md +7 -5
- package/skills/aura3d-materials-environments/SKILL.md +4 -4
- package/skills/aura3d-materials-environments/references/metal-glass.md +32 -0
- package/skills/aura3d-performance/SKILL.md +5 -0
- package/skills/llms.txt +2 -2
- package/skills/manifest.json +4 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const PHYSICS_BACKEND_INSTALL_COMMAND = "npm install @dimforge/rapier3d-compat@0.20.0";
|
|
2
|
+
/**
|
|
3
|
+
* Lint-time signal for the typed PHYSICS_BACKEND_MISSING runtime error: a project whose sources call `.physics(...)`,
|
|
4
|
+
* `app.physics` or `PhysicsWorld` must be able to resolve the Rapier backend. Returns one failure line (with the exact
|
|
5
|
+
* install command) or an empty list.
|
|
6
|
+
*/
|
|
7
|
+
export declare function physicsBackendFailures(projectDir: string): string[];
|
|
8
|
+
//# sourceMappingURL=physics-backend-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"physics-backend-check.d.ts","sourceRoot":"","sources":["../src/physics-backend-check.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,+BAA+B,iDAAkC,CAAC;AAe/E;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAcnE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
const BACKEND = "@dimforge/rapier3d-compat";
|
|
5
|
+
export const PHYSICS_BACKEND_INSTALL_COMMAND = `npm install ${BACKEND}@0.20.0`;
|
|
6
|
+
const PHYSICS_USE = /\.physics\s*\(|\bapp\.physics\b|\bpreloadPhysics\b|\bPhysicsWorld\b|\bphysics\s*:\s*\{/u;
|
|
7
|
+
function sourceFiles(directory, depth = 0, found = []) {
|
|
8
|
+
if (depth > 6 || found.length > 400 || !existsSync(directory))
|
|
9
|
+
return found;
|
|
10
|
+
for (const name of readdirSync(directory)) {
|
|
11
|
+
if (name === "node_modules" || name === "dist" || name.startsWith("."))
|
|
12
|
+
continue;
|
|
13
|
+
const path = join(directory, name);
|
|
14
|
+
const stats = statSync(path);
|
|
15
|
+
if (stats.isDirectory())
|
|
16
|
+
sourceFiles(path, depth + 1, found);
|
|
17
|
+
else if (/\.(?:ts|tsx|mts|js|jsx|mjs)$/u.test(name) && !name.endsWith(".d.ts") && stats.size < 512 * 1024)
|
|
18
|
+
found.push(path);
|
|
19
|
+
}
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Lint-time signal for the typed PHYSICS_BACKEND_MISSING runtime error: a project whose sources call `.physics(...)`,
|
|
24
|
+
* `app.physics` or `PhysicsWorld` must be able to resolve the Rapier backend. Returns one failure line (with the exact
|
|
25
|
+
* install command) or an empty list.
|
|
26
|
+
*/
|
|
27
|
+
export function physicsBackendFailures(projectDir) {
|
|
28
|
+
const root = resolve(projectDir);
|
|
29
|
+
const used = sourceFiles(join(root, "src")).filter((file) => PHYSICS_USE.test(readFileSync(file, "utf8")));
|
|
30
|
+
if (used.length === 0)
|
|
31
|
+
return [];
|
|
32
|
+
let resolvable = false;
|
|
33
|
+
try {
|
|
34
|
+
createRequire(join(root, "package.json")).resolve(`${BACKEND}/package.json`);
|
|
35
|
+
resolvable = true;
|
|
36
|
+
}
|
|
37
|
+
catch { /* not installed */ }
|
|
38
|
+
if (!resolvable) {
|
|
39
|
+
// Some package exports maps hide package.json; fall back to the installed directory.
|
|
40
|
+
resolvable = existsSync(join(root, "node_modules", BACKEND, "package.json"));
|
|
41
|
+
}
|
|
42
|
+
return resolvable ? [] : [`PHYSICS_BACKEND_MISSING: ${used.length} source file(s) use Aura3D physics (first: ${used[0].slice(root.length + 1)}) but ${BACKEND} is not installed. Run: ${PHYSICS_BACKEND_INSTALL_COMMAND}`];
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=physics-backend-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"physics-backend-check.js","sourceRoot":"","sources":["../src/physics-backend-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAC5C,MAAM,CAAC,MAAM,+BAA+B,GAAG,eAAe,OAAO,SAAS,CAAC;AAC/E,MAAM,WAAW,GAAG,yFAAyF,CAAC;AAE9G,SAAS,WAAW,CAAC,SAAiB,EAAE,KAAK,GAAG,CAAC,EAAE,QAAkB,EAAE;IACrE,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5E,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACjF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;aACxD,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9H,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3G,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,CAAC;QACH,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,OAAO,eAAe,CAAC,CAAC;QAC7E,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,qFAAqF;QACrF,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,4BAA4B,IAAI,CAAC,MAAM,8CAA8C,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,2BAA2B,+BAA+B,EAAE,CAAC,CAAC;AAC9N,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aura3d/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aura3d": "dist/cli.js",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"main": "./dist/index.js",
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@aura3d/asset-index": "5.
|
|
31
|
-
"@aura3d/assets": "5.
|
|
32
|
-
"@aura3d/audio": "5.
|
|
33
|
-
"@aura3d/rendering": "5.
|
|
34
|
-
"create-aura3d": "5.
|
|
30
|
+
"@aura3d/asset-index": "5.2.0",
|
|
31
|
+
"@aura3d/assets": "5.2.0",
|
|
32
|
+
"@aura3d/audio": "5.2.0",
|
|
33
|
+
"@aura3d/rendering": "5.2.0",
|
|
34
|
+
"create-aura3d": "5.2.0",
|
|
35
35
|
"typescript": "^5.8.3"
|
|
36
36
|
},
|
|
37
37
|
"description": "Bring real GLB/glTF assets into Aura3D browser apps with typed references, provenance, catalog search, validation, screenshots, and deploy checks.",
|
|
@@ -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` | |
|
|
@@ -78,7 +78,7 @@ void camera.frame(primitives.box({ name: "subject" }), { fill: 0.6 });
|
|
|
78
78
|
|
|
79
79
|
## `looks`
|
|
80
80
|
|
|
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`.
|
|
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).
|
|
82
82
|
|
|
83
83
|
```ts
|
|
84
84
|
void looks.preset("product-studio");
|
|
@@ -86,7 +86,11 @@ void looks.preset("product-studio");
|
|
|
86
86
|
|
|
87
87
|
| Call | Returns | Options |
|
|
88
88
|
|---|---|---|
|
|
89
|
-
| `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… |
|
|
90
94
|
| `looks.nodes(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` | `readonly AuraNodeBuilder<AuraSceneNode>[]` | same as `looks.preset` |
|
|
91
95
|
| `looks.appOptions(id: AuraLookId, options: AuraLookBuildOptions \| undefined)` | `Pick<AuraCreateAppOptions, "pixelRatio" \| "renderer">` | options: expansion?: "auto" \| "v0" \| "v1" \| un…; flags?: QrFlags \| undefined |
|
|
92
96
|
| `looks.list()` | `readonly AuraLookId[]` | |
|
|
@@ -103,7 +107,7 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
103
107
|
|
|
104
108
|
| Call | Returns | Options |
|
|
105
109
|
|---|---|---|
|
|
106
|
-
| `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 |
|
|
107
111
|
| `material.toon(options?: AuraToonMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; steps?: number \| undefined; ramp?: AuraAssetRef<"texture", s…; shadowColor?: string \| undefined; rim?: AuraMaterialRimSpec \| und… |
|
|
108
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… |
|
|
109
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,10 +118,10 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
114
118
|
| `material.distance(options?: AuraDistanceMaterialOptions)` | `AuraMaterialSpec` | options: referencePosition?: AuraVec3 \| undefined; near?: number \| undefined; far?: number \| undefined; side?: "front" \| "back" \| "doubl… |
|
|
115
119
|
| `material.shadow(options?: AuraShadowMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; opacity?: number \| undefined; side?: "front" \| "back" \| "doubl…; light?: "directional" \| "point" \|… |
|
|
116
120
|
| `material.node(options: TslMaterialNodeOptions \| ShaderGraphMaterialOptions)` | `AuraMaterialSpec` | |
|
|
117
|
-
| `material.pbr(
|
|
121
|
+
| `material.pbr(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
118
122
|
| `material.physical(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
119
123
|
| `material.emissive(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
120
|
-
| `material.metal(
|
|
124
|
+
| `material.metal(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
121
125
|
| `material.rubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
122
126
|
| `material.glass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
123
127
|
| `material.clearcoat(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
@@ -130,6 +134,8 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
130
134
|
| `material.brushedMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
131
135
|
| `material.frostedGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
132
136
|
| `material.clearGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
137
|
+
| `material.studioMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
138
|
+
| `material.studioGlass(options?: AuraMaterialSpec & { readonly tint?: AuraColor; })` | `AuraMaterialSpec` | |
|
|
133
139
|
| `material.blackRubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
134
140
|
| `material.matteClay(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
135
141
|
| `material.ceramic(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
@@ -142,7 +148,7 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
|
|
|
142
148
|
| `material.carPaint(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
143
149
|
| `material.planet(preset?: AuraSolarPlanetMaterialPreset, options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
144
150
|
| `material.atmosphere(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
|
|
145
|
-
| `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 |
|
|
146
152
|
| `material.proceduralTextures` | `value` | |
|
|
147
153
|
| `material.parameters(name: string, spec: AuraMaterialSpec)` | `AuraEditableMaterialParameters` | |
|
|
148
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 |
|
|
@@ -164,11 +170,11 @@ void lights.directional({ position: [4, 6, 3], target: [0, 0, 0], intensity: 3,
|
|
|
164
170
|
|---|---|---|
|
|
165
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… |
|
|
166
172
|
| `lights.ambient(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined |
|
|
167
|
-
| `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 |
|
|
168
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 |
|
|
169
|
-
| `lights.directional(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraDirectional
|
|
170
|
-
| `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 \| … |
|
|
171
|
-
| `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 \|… |
|
|
172
178
|
| `lights.studio(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: intensity?: number \| undefined |
|
|
173
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 |
|
|
174
180
|
| `lights.rectArea(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | same as `lights.rect` |
|
|
@@ -186,23 +192,23 @@ void environments.neutral({ intensity: 0.8, background: false });
|
|
|
186
192
|
|
|
187
193
|
| Call | Returns | Options |
|
|
188
194
|
|---|---|---|
|
|
189
|
-
| `environments.outdoor(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…;
|
|
190
|
-
| `environments.room(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…;
|
|
191
|
-
| `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… |
|
|
192
198
|
| `environments.underwater(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | same as `environments.space` |
|
|
193
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 |
|
|
194
200
|
| `environments.neutral(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
195
201
|
| `environments.none(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined |
|
|
196
|
-
| `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… |
|
|
197
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"> \|… |
|
|
198
|
-
| `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 |
|
|
199
205
|
| `environments.materialLab(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
200
206
|
| `environments.productHero(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
201
207
|
| `environments.nightCinematic(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
202
208
|
| `environments.metalStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
203
209
|
| `environments.glassStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
|
|
204
210
|
| `environments.presets()` | `readonly AuraEnvironmentMapPreset[]` | |
|
|
205
|
-
| `environments.forMaterial(materialClass: "product" \| "
|
|
211
|
+
| `environments.forMaterial(materialClass: "product" \| "studio" \| "metal" \| "glass")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
|
|
206
212
|
|
|
207
213
|
## `sky`
|
|
208
214
|
|
|
@@ -214,8 +220,8 @@ void sky.preetham({ sun: { elevationDeg: 35, azimuthDeg: 120 } });
|
|
|
214
220
|
|
|
215
221
|
| Call | Returns | Options |
|
|
216
222
|
|---|---|---|
|
|
217
|
-
| `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;
|
|
218
|
-
| `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 |
|
|
219
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 |
|
|
220
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" \|… |
|
|
221
227
|
|
|
@@ -231,7 +237,7 @@ void primitives.cylinder({ name: "plinth", size: [2, 0.9, 2], material: material
|
|
|
231
237
|
|---|---|---|
|
|
232
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… |
|
|
233
239
|
| `primitives.sphere(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
234
|
-
| `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…; … |
|
|
235
241
|
| `primitives.cylinder(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
236
242
|
| `primitives.capsule(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
237
243
|
| `primitives.torus(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
|
|
@@ -278,7 +284,7 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
|
|
|
278
284
|
| `effects.vignette(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
279
285
|
| `effects.filmGrain(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
280
286
|
| `effects.chromaticAberration(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
281
|
-
| `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" \| "
|
|
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 |
|
|
282
288
|
| `effects.fog(options?: FogNodeOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
283
289
|
| `effects.cinematicBloom(options?: Omit<AuraEffectNode, "kind" \| "effect">)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
284
290
|
| `effects.volumetricFog(options?: AuraVolumetricFogOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
|
|
@@ -328,6 +334,31 @@ void instances.cone({ name: "pines", transforms: [{ position: [0, 1, 0], scale:
|
|
|
328
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 |
|
|
329
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?… |
|
|
330
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
|
+
|
|
331
362
|
## `geometry`
|
|
332
363
|
|
|
333
364
|
`define` builds the spec; mount it with `geometry.custom(spec, { material })`.
|
|
@@ -374,7 +405,7 @@ void interactions.orbit({ target: "crate" });
|
|
|
374
405
|
|
|
375
406
|
## `physics`
|
|
376
407
|
|
|
377
|
-
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`).
|
|
378
409
|
|
|
379
410
|
```ts
|
|
380
411
|
void physics.box;
|
|
@@ -420,6 +451,18 @@ void AnimationController;
|
|
|
420
451
|
| `AnimationController#listClips()` | `readonly AuraRegisteredAnimationClip<TClipId, TEvent>[]` | |
|
|
421
452
|
| `AnimationController#dispose()` | `void` | |
|
|
422
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
|
+
|
|
423
466
|
## `createFixedStepLoop`
|
|
424
467
|
|
|
425
468
|
Deterministic simulation loop for games.
|
|
@@ -1,6 +1,6 @@
|
|
|
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
|
|
|
@@ -720,6 +720,98 @@ const app = createAuraApp("#app", { scene: stage });
|
|
|
720
720
|
await app.ready();
|
|
721
721
|
```
|
|
722
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
|
+
|
|
723
815
|
## Type traps (strict `tsc` errors agents hit on a first draft)
|
|
724
816
|
|
|
725
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).
|
|
@@ -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
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.
|
|
@@ -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`
|
package/skills/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Aura3D
|
|
2
2
|
|
|
3
|
-
> Aura3D 5.
|
|
3
|
+
> Aura3D 5.2.0: a TypeScript SDK for browser 3D apps and games. You write a typed, declarative scene with `@aura3d/engine`; the `@aura3d/cli` toolchain handles typed GLB/glTF assets, checks and deploy gates. Built to be driven by AI coding agents. Site: https://aura3d.auraone.ai
|
|
4
4
|
|
|
5
5
|
## First screen: Aura3D patterns (copy the shape, change the subject)
|
|
6
6
|
|
|
@@ -280,4 +280,4 @@ Release integrity rules:
|
|
|
280
280
|
- Docs: https://aura3d.auraone.ai/docs/api.html, https://aura3d.auraone.ai/docs/agent-quickstart.html, https://aura3d.auraone.ai/docs/claims.html
|
|
281
281
|
- Showcase (25 playable games): https://aura3d.auraone.ai/apps/showcase-index/
|
|
282
282
|
- Sitemap: https://aura3d.auraone.ai/sitemap.xml; crawl policy: https://aura3d.auraone.ai/robots.txt
|
|
283
|
-
- Last updated: 2026-11-
|
|
283
|
+
- Last updated: 2026-11-07
|