three-usd-robot 0.13.0 → 0.14.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.
Files changed (43) hide show
  1. package/README.md +19 -1
  2. package/dist/{MeshBinding-B_8RhuQE.d.ts → MeshBinding-5Dad1EOh.d.ts} +16 -4
  3. package/dist/{ThreeUsdRobot-X1zbpRZo.d.ts → ThreeUsdRobot-CurQqRqu.d.ts} +78 -2
  4. package/dist/{ThreeUsdRobotLoader-u3d1yoLM.d.ts → ThreeUsdRobotLoader-DrYXpMta.d.ts} +28 -4
  5. package/dist/chunk-ALC7RBKC.js +68 -0
  6. package/dist/chunk-ALC7RBKC.js.map +1 -0
  7. package/dist/{chunk-JN2QPDB3.js → chunk-HDZGWL6P.js} +36 -3
  8. package/dist/chunk-HDZGWL6P.js.map +1 -0
  9. package/dist/{chunk-YGJ23CG3.js → chunk-IAOPNN6V.js} +3 -66
  10. package/dist/chunk-IAOPNN6V.js.map +1 -0
  11. package/dist/{chunk-5ZPHZ5ZH.js → chunk-IT5RWB7A.js} +5 -4
  12. package/dist/chunk-IT5RWB7A.js.map +1 -0
  13. package/dist/{chunk-FUVHAORT.js → chunk-KRT4NL3H.js} +190 -126
  14. package/dist/chunk-KRT4NL3H.js.map +1 -0
  15. package/dist/{chunk-GRJGPHLW.js → chunk-WQRP5IDJ.js} +382 -59
  16. package/dist/chunk-WQRP5IDJ.js.map +1 -0
  17. package/dist/chunk-ZQXYVBQI.js +154 -0
  18. package/dist/chunk-ZQXYVBQI.js.map +1 -0
  19. package/dist/core.d.ts +5 -4
  20. package/dist/core.js +5 -3
  21. package/dist/extras.d.ts +2 -2
  22. package/dist/helpers.d.ts +2 -2
  23. package/dist/helpers.js +3 -2
  24. package/dist/helpers.js.map +1 -1
  25. package/dist/index.d.ts +61 -8
  26. package/dist/index.js +10 -8
  27. package/dist/index.js.map +1 -1
  28. package/dist/nodes.d.ts +4 -4
  29. package/dist/{parseMdl-vfzBGoMr.d.ts → parseMdl-M862hSal.d.ts} +1 -29
  30. package/dist/react.d.ts +5 -5
  31. package/dist/react.js +6 -4
  32. package/dist/react.js.map +1 -1
  33. package/dist/rendering.d.ts +114 -0
  34. package/dist/rendering.js +174 -0
  35. package/dist/rendering.js.map +1 -0
  36. package/dist/usdGeomCamera-BEsGuz7m.d.ts +53 -0
  37. package/dist/{buildKinematicTree-Q9JOjAjM.d.ts → usdLux-BqQlSOwG.d.ts} +108 -1
  38. package/package.json +5 -1
  39. package/dist/chunk-5ZPHZ5ZH.js.map +0 -1
  40. package/dist/chunk-FUVHAORT.js.map +0 -1
  41. package/dist/chunk-GRJGPHLW.js.map +0 -1
  42. package/dist/chunk-JN2QPDB3.js.map +0 -1
  43. package/dist/chunk-YGJ23CG3.js.map +0 -1
@@ -0,0 +1,114 @@
1
+ import * as THREE from 'three';
2
+ import { L as LightDescription } from './usdLux-BqQlSOwG.js';
3
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-CurQqRqu.js';
4
+ import { C as CameraDescription } from './usdGeomCamera-BEsGuz7m.js';
5
+
6
+ /**
7
+ * DomeLight → image-based environment lighting (M26).
8
+ *
9
+ * A `DomeLight` is not a discrete light: it is the scene's environment. This
10
+ * module fetches the dome's texture through the robot's captured asset context
11
+ * (so relative CDN paths and `.usdz`-embedded images both work), decodes
12
+ * `.hdr` / `.exr` (via three's addons loaders) or LDR images into an
13
+ * equirectangular texture, and applies it as `scene.environment` — three
14
+ * prefilters equirectangular environments internally, no PMREM setup needed.
15
+ *
16
+ * Orientation: three.js expects the equirect pole along +Y. The dome's pole
17
+ * follows `poleAxis` (`DomeLight_1`) or, for the original `DomeLight` schema,
18
+ * the stage up-axis — composed with the dome prim's own rotation and the
19
+ * loader's up-axis normalization into `scene.environmentRotation` (three
20
+ * r162+; skipped with a warning on older peers when it matters).
21
+ */
22
+
23
+ type ApplyUsdEnvironmentOptions = {
24
+ /** Also show the environment as `scene.background` (default `false`). */
25
+ background?: boolean;
26
+ /**
27
+ * Multiplies the dome's authored emission (`intensity × 2^exposure`) into
28
+ * `scene.environmentIntensity`. Defaults to the `lightIntensityScale` the
29
+ * robot was loaded with, so the environment stays in balance with the bound
30
+ * lights (see docs/lighting.md).
31
+ */
32
+ intensityScale?: number;
33
+ /** The dome to apply when the stage has several (default: the first). */
34
+ dome?: LightDescription;
35
+ /** Receives fallback/compatibility diagnostics. */
36
+ onWarn?: (message: string) => void;
37
+ };
38
+ /** What {@link applyUsdEnvironment} applied to the scene. */
39
+ type UsdEnvironment = {
40
+ dome: LightDescription;
41
+ /** The environment texture — equirectangular HDR/LDR, or 1×1 dome color. */
42
+ texture: THREE.Texture;
43
+ /** Environment orientation applied (identity when unsupported by the peer). */
44
+ rotation: THREE.Euler;
45
+ /** Effective emission written to `scene.environmentIntensity`. */
46
+ intensity: number;
47
+ };
48
+ /**
49
+ * Realize the stage's DomeLight as the scene environment. Resolves the dome's
50
+ * texture (falling back to a uniform texture of the dome color when it has
51
+ * none, or when it cannot be fetched or decoded) and sets
52
+ * `scene.environment(+Rotation/+Intensity)` — and, with
53
+ * `options.background`, the matching background properties. Returns `null`
54
+ * when the robot has no DomeLight; the scene is left untouched.
55
+ */
56
+ declare function applyUsdEnvironment(robot: ThreeUsdRobot, scene: THREE.Scene, options?: ApplyUsdEnvironmentOptions): Promise<UsdEnvironment | null>;
57
+
58
+ /**
59
+ * One-call renderer defaults (M28) — the settings every USD viewer built on
60
+ * this library ends up wanting: filmic tone mapping (USD lights and HDRI
61
+ * domes are HDR values; three's default `NoToneMapping` clips them), soft
62
+ * shadow maps (the loader pre-flags meshes and lights, M25), and optionally
63
+ * an exposure — either a plain multiplier or a `UsdGeomCamera` description,
64
+ * whose USD 24.11 linear exposure scale is applied (M27).
65
+ *
66
+ * Deliberately small: post-processing (AO, SSR, TAA, bloom) stays out of the
67
+ * library — compose three's own passes in your app; the Vite example shows a
68
+ * GTAO setup.
69
+ */
70
+
71
+ /** Filmic curve to install. `"none"` restores three's linear pass-through. */
72
+ type ToneMappingPreset = "ACES" | "AgX" | "neutral" | "none";
73
+ type RenderDefaultsOptions = {
74
+ /** Default `"ACES"`. `"neutral"` (Khronos PBR Neutral) needs three r162+. */
75
+ toneMapping?: ToneMappingPreset;
76
+ /**
77
+ * Sets `toneMappingExposure`: a number as-is, or a
78
+ * {@link CameraDescription} via its linear exposure scale
79
+ * ({@link computeCameraExposureScale}). Left untouched when omitted.
80
+ */
81
+ exposure?: number | CameraDescription;
82
+ /** Enable `PCFSoftShadowMap` shadow rendering (default `true`). */
83
+ shadows?: boolean;
84
+ /** Receives compatibility diagnostics. */
85
+ onWarn?: (message: string) => void;
86
+ };
87
+ /**
88
+ * The renderer surface this helper touches — satisfied by `WebGLRenderer`
89
+ * and `WebGPURenderer` alike, without depending on `three/webgpu` types.
90
+ */
91
+ type RenderDefaultsTarget = {
92
+ toneMapping: THREE.ToneMapping;
93
+ toneMappingExposure: number;
94
+ shadowMap: {
95
+ enabled: boolean;
96
+ type: THREE.ShadowMapType;
97
+ };
98
+ };
99
+ /**
100
+ * Apply viewer defaults to a renderer:
101
+ *
102
+ * ```ts
103
+ * import { applyRenderDefaults } from "three-usd-robot/rendering";
104
+ *
105
+ * applyRenderDefaults(renderer); // ACES + soft shadow maps
106
+ * applyRenderDefaults(renderer, { toneMapping: "AgX", exposure: robot.cameras[0]?.userData.usdCamera });
107
+ * ```
108
+ *
109
+ * Only ever switches features on or assigns requested values — `shadows:
110
+ * false` skips enabling shadow maps rather than force-disabling them.
111
+ */
112
+ declare function applyRenderDefaults(renderer: RenderDefaultsTarget, options?: RenderDefaultsOptions): void;
113
+
114
+ export { type ApplyUsdEnvironmentOptions, type RenderDefaultsOptions, type RenderDefaultsTarget, type ToneMappingPreset, type UsdEnvironment, applyRenderDefaults, applyUsdEnvironment };
@@ -0,0 +1,174 @@
1
+ import { computeWorldTransform, computeCameraExposureScale } from './chunk-ZQXYVBQI.js';
2
+ import './chunk-IAOPNN6V.js';
3
+ import './chunk-JGIVJXBU.js';
4
+ import * as THREE from 'three';
5
+ import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
6
+ import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
7
+
8
+ async function applyUsdEnvironment(robot, scene, options = {}) {
9
+ const dome = options.dome ?? robot.domeLights[0];
10
+ if (!dome) return null;
11
+ const onWarn = options.onWarn;
12
+ let texture = null;
13
+ if (dome.textureFile) {
14
+ if (dome.textureFormat && dome.textureFormat !== "latlong" && dome.textureFormat !== "automatic") {
15
+ onWarn?.(
16
+ `DomeLight texture:format "${dome.textureFormat}" is not supported; sampling it as latlong (equirectangular)`
17
+ );
18
+ }
19
+ try {
20
+ texture = await loadDomeTexture(robot, dome.textureFile);
21
+ } catch (err) {
22
+ onWarn?.(
23
+ `DomeLight texture ${dome.textureFile} could not be loaded (${err.message}); falling back to the dome color`
24
+ );
25
+ }
26
+ }
27
+ texture ??= colorTexture(dome.color);
28
+ const intensity = dome.intensity * (options.intensityScale ?? robot.lightIntensityScale ?? 1);
29
+ const rotation = domeRotation(robot, dome);
30
+ scene.environment = texture;
31
+ setSceneEuler(scene, "environmentRotation", rotation, onWarn);
32
+ setSceneNumber(scene, "environmentIntensity", intensity, onWarn);
33
+ if (options.background) {
34
+ scene.background = texture;
35
+ setSceneEuler(scene, "backgroundRotation", rotation, void 0);
36
+ setSceneNumber(scene, "backgroundIntensity", intensity, void 0);
37
+ }
38
+ return { dome, texture, rotation, intensity };
39
+ }
40
+ async function loadDomeTexture(robot, assetPath) {
41
+ const context = robot.assetContext;
42
+ if (!context) {
43
+ throw new Error("robot carries no asset context (was it built by ThreeUsdRobotLoader?)");
44
+ }
45
+ const url = context.resolver.resolve(assetPath, context.baseUrl);
46
+ const bytes = context.resolver.fetchBytes ? await context.resolver.fetchBytes(url) : new TextEncoder().encode(await context.resolver.fetchText(url));
47
+ if (/\.hdr$/i.test(url)) {
48
+ const texData = new RGBELoader().parse(toArrayBuffer(bytes));
49
+ const texture2 = new THREE.DataTexture(
50
+ texData.data,
51
+ texData.width,
52
+ texData.height,
53
+ THREE.RGBAFormat,
54
+ texData.type
55
+ );
56
+ texture2.flipY = true;
57
+ return finishDataTexture(texture2, THREE.LinearSRGBColorSpace);
58
+ }
59
+ if (/\.exr$/i.test(url)) {
60
+ const texData = new EXRLoader().parse(toArrayBuffer(bytes));
61
+ const texture2 = new THREE.DataTexture(
62
+ texData.data,
63
+ texData.width,
64
+ texData.height,
65
+ texData.format,
66
+ texData.type
67
+ );
68
+ texture2.flipY = false;
69
+ return finishDataTexture(texture2, texData.colorSpace);
70
+ }
71
+ const texture = new THREE.Texture(await decodeImage(bytes, url));
72
+ texture.colorSpace = THREE.SRGBColorSpace;
73
+ texture.mapping = THREE.EquirectangularReflectionMapping;
74
+ texture.needsUpdate = true;
75
+ return texture;
76
+ }
77
+ function finishDataTexture(texture, colorSpace) {
78
+ texture.colorSpace = colorSpace;
79
+ texture.magFilter = THREE.LinearFilter;
80
+ texture.minFilter = THREE.LinearFilter;
81
+ texture.generateMipmaps = false;
82
+ texture.mapping = THREE.EquirectangularReflectionMapping;
83
+ texture.needsUpdate = true;
84
+ return texture;
85
+ }
86
+ function colorTexture(color) {
87
+ const data = new Float32Array([color[0], color[1], color[2], 1]);
88
+ const texture = new THREE.DataTexture(data, 1, 1, THREE.RGBAFormat, THREE.FloatType);
89
+ return finishDataTexture(texture, THREE.LinearSRGBColorSpace);
90
+ }
91
+ function domeRotation(robot, dome) {
92
+ const q = robot.quaternion.clone();
93
+ const prim = robot.stage?.GetPrimAtPath(dome.primPath);
94
+ if (prim) {
95
+ const world = new THREE.Matrix4().fromArray(computeWorldTransform(prim));
96
+ const rotation = new THREE.Matrix4().extractRotation(world);
97
+ q.multiply(new THREE.Quaternion().setFromRotationMatrix(rotation));
98
+ }
99
+ const pole = dome.poleAxis === "Y" || dome.poleAxis === "Z" ? dome.poleAxis : robot.robot.upAxis;
100
+ if (pole === "Z") {
101
+ q.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2));
102
+ }
103
+ return new THREE.Euler().setFromQuaternion(q);
104
+ }
105
+ function setSceneEuler(scene, name, rotation, onWarn) {
106
+ if (name in scene) {
107
+ scene[name].copy(rotation);
108
+ } else if (Math.abs(rotation.x) + Math.abs(rotation.y) + Math.abs(rotation.z) > 1e-6) {
109
+ onWarn?.(`this three.js has no Scene.${name} (added r162); the dome orientation is dropped`);
110
+ }
111
+ }
112
+ function setSceneNumber(scene, name, value, onWarn) {
113
+ if (name in scene) {
114
+ scene[name] = value;
115
+ } else if (Math.abs(value - 1) > 1e-6) {
116
+ onWarn?.(`this three.js has no Scene.${name} (added r163); the dome intensity is dropped`);
117
+ }
118
+ }
119
+ function toArrayBuffer(bytes) {
120
+ if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
121
+ return bytes.buffer;
122
+ }
123
+ return bytes.slice().buffer;
124
+ }
125
+ function decodeImage(bytes, url) {
126
+ if (typeof Image === "undefined") {
127
+ return Promise.reject(new Error("LDR dome textures need a browser image decoder"));
128
+ }
129
+ const mime = /\.jpe?g$/i.test(url) ? "image/jpeg" : /\.webp$/i.test(url) ? "image/webp" : "image/png";
130
+ const blob = new Blob([bytes], { type: mime });
131
+ const objectUrl = URL.createObjectURL(blob);
132
+ return new Promise((resolve, reject) => {
133
+ const img = new Image();
134
+ img.onload = () => {
135
+ URL.revokeObjectURL(objectUrl);
136
+ resolve(img);
137
+ };
138
+ img.onerror = () => {
139
+ URL.revokeObjectURL(objectUrl);
140
+ reject(new Error(`failed to decode dome texture: ${url}`));
141
+ };
142
+ img.src = objectUrl;
143
+ });
144
+ }
145
+ function applyRenderDefaults(renderer, options = {}) {
146
+ renderer.toneMapping = resolveToneMapping(options.toneMapping ?? "ACES", options.onWarn);
147
+ if (options.exposure !== void 0) {
148
+ renderer.toneMappingExposure = typeof options.exposure === "number" ? options.exposure : computeCameraExposureScale(options.exposure);
149
+ }
150
+ if (options.shadows ?? true) {
151
+ renderer.shadowMap.enabled = true;
152
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
153
+ }
154
+ }
155
+ function resolveToneMapping(preset, onWarn) {
156
+ switch (preset) {
157
+ case "none":
158
+ return THREE.NoToneMapping;
159
+ case "AgX":
160
+ return THREE.AgXToneMapping;
161
+ case "neutral": {
162
+ const neutral = THREE.NeutralToneMapping;
163
+ if (typeof neutral === "number") return neutral;
164
+ onWarn?.('this three.js has no NeutralToneMapping (added r162); using "ACES" instead');
165
+ return THREE.ACESFilmicToneMapping;
166
+ }
167
+ case "ACES":
168
+ return THREE.ACESFilmicToneMapping;
169
+ }
170
+ }
171
+
172
+ export { applyRenderDefaults, applyUsdEnvironment };
173
+ //# sourceMappingURL=rendering.js.map
174
+ //# sourceMappingURL=rendering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rendering/environment.ts","../src/rendering/renderDefaults.ts"],"names":["texture","THREE2"],"mappings":"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","file":"rendering.js","sourcesContent":["/**\n * DomeLight → image-based environment lighting (M26).\n *\n * A `DomeLight` is not a discrete light: it is the scene's environment. This\n * module fetches the dome's texture through the robot's captured asset context\n * (so relative CDN paths and `.usdz`-embedded images both work), decodes\n * `.hdr` / `.exr` (via three's addons loaders) or LDR images into an\n * equirectangular texture, and applies it as `scene.environment` — three\n * prefilters equirectangular environments internally, no PMREM setup needed.\n *\n * Orientation: three.js expects the equirect pole along +Y. The dome's pole\n * follows `poleAxis` (`DomeLight_1`) or, for the original `DomeLight` schema,\n * the stage up-axis — composed with the dome prim's own rotation and the\n * loader's up-axis normalization into `scene.environmentRotation` (three\n * r162+; skipped with a warning on older peers when it matters).\n */\n\nimport * as THREE from \"three\";\nimport { EXRLoader } from \"three/addons/loaders/EXRLoader.js\";\nimport { RGBELoader } from \"three/addons/loaders/RGBELoader.js\";\nimport type { LightDescription } from \"../schemas/usdLux.js\";\nimport type { ThreeUsdRobot } from \"../three/ThreeUsdRobot.js\";\nimport { computeWorldTransform } from \"../usd/xformOps.js\";\n\nexport type ApplyUsdEnvironmentOptions = {\n /** Also show the environment as `scene.background` (default `false`). */\n background?: boolean;\n /**\n * Multiplies the dome's authored emission (`intensity × 2^exposure`) into\n * `scene.environmentIntensity`. Defaults to the `lightIntensityScale` the\n * robot was loaded with, so the environment stays in balance with the bound\n * lights (see docs/lighting.md).\n */\n intensityScale?: number;\n /** The dome to apply when the stage has several (default: the first). */\n dome?: LightDescription;\n /** Receives fallback/compatibility diagnostics. */\n onWarn?: (message: string) => void;\n};\n\n/** What {@link applyUsdEnvironment} applied to the scene. */\nexport type UsdEnvironment = {\n dome: LightDescription;\n /** The environment texture — equirectangular HDR/LDR, or 1×1 dome color. */\n texture: THREE.Texture;\n /** Environment orientation applied (identity when unsupported by the peer). */\n rotation: THREE.Euler;\n /** Effective emission written to `scene.environmentIntensity`. */\n intensity: number;\n};\n\n/**\n * Realize the stage's DomeLight as the scene environment. Resolves the dome's\n * texture (falling back to a uniform texture of the dome color when it has\n * none, or when it cannot be fetched or decoded) and sets\n * `scene.environment(+Rotation/+Intensity)` — and, with\n * `options.background`, the matching background properties. Returns `null`\n * when the robot has no DomeLight; the scene is left untouched.\n */\nexport async function applyUsdEnvironment(\n robot: ThreeUsdRobot,\n scene: THREE.Scene,\n options: ApplyUsdEnvironmentOptions = {},\n): Promise<UsdEnvironment | null> {\n const dome = options.dome ?? robot.domeLights[0];\n if (!dome) return null;\n const onWarn = options.onWarn;\n\n let texture: THREE.Texture | null = null;\n if (dome.textureFile) {\n if (\n dome.textureFormat &&\n dome.textureFormat !== \"latlong\" &&\n dome.textureFormat !== \"automatic\"\n ) {\n onWarn?.(\n `DomeLight texture:format \"${dome.textureFormat}\" is not supported; sampling it as latlong (equirectangular)`,\n );\n }\n try {\n texture = await loadDomeTexture(robot, dome.textureFile);\n } catch (err) {\n onWarn?.(\n `DomeLight texture ${dome.textureFile} could not be loaded (${(err as Error).message}); falling back to the dome color`,\n );\n }\n }\n texture ??= colorTexture(dome.color);\n\n const intensity = dome.intensity * (options.intensityScale ?? robot.lightIntensityScale ?? 1);\n const rotation = domeRotation(robot, dome);\n\n scene.environment = texture;\n setSceneEuler(scene, \"environmentRotation\", rotation, onWarn);\n setSceneNumber(scene, \"environmentIntensity\", intensity, onWarn);\n if (options.background) {\n scene.background = texture;\n setSceneEuler(scene, \"backgroundRotation\", rotation, undefined);\n setSceneNumber(scene, \"backgroundIntensity\", intensity, undefined);\n }\n return { dome, texture, rotation, intensity };\n}\n\n/**\n * Fetch and decode the dome image through the robot's asset context: `.hdr`\n * (Radiance RGBE) and `.exr` decode to float data textures; anything else\n * decodes as an LDR image (browser only). The result is equirect-mapped.\n */\nasync function loadDomeTexture(robot: ThreeUsdRobot, assetPath: string): Promise<THREE.Texture> {\n const context = robot.assetContext;\n if (!context) {\n throw new Error(\"robot carries no asset context (was it built by ThreeUsdRobotLoader?)\");\n }\n const url = context.resolver.resolve(assetPath, context.baseUrl);\n const bytes = context.resolver.fetchBytes\n ? await context.resolver.fetchBytes(url)\n : new TextEncoder().encode(await context.resolver.fetchText(url));\n\n if (/\\.hdr$/i.test(url)) {\n const texData = new RGBELoader().parse(toArrayBuffer(bytes));\n const texture = new THREE.DataTexture(\n texData.data as unknown as BufferSource,\n texData.width,\n texData.height,\n THREE.RGBAFormat,\n texData.type,\n );\n texture.flipY = true; // Radiance scanlines run top-down\n return finishDataTexture(texture, THREE.LinearSRGBColorSpace);\n }\n if (/\\.exr$/i.test(url)) {\n const texData = new EXRLoader().parse(toArrayBuffer(bytes));\n const texture = new THREE.DataTexture(\n texData.data as unknown as BufferSource,\n texData.width,\n texData.height,\n texData.format,\n texData.type,\n );\n texture.flipY = false; // EXR rows decode bottom-up already\n return finishDataTexture(texture, texData.colorSpace);\n }\n\n const texture = new THREE.Texture(await decodeImage(bytes, url));\n texture.colorSpace = THREE.SRGBColorSpace;\n texture.mapping = THREE.EquirectangularReflectionMapping;\n texture.needsUpdate = true;\n return texture;\n}\n\nfunction finishDataTexture(texture: THREE.DataTexture, colorSpace: string): THREE.Texture {\n texture.colorSpace = colorSpace;\n texture.magFilter = THREE.LinearFilter;\n texture.minFilter = THREE.LinearFilter;\n texture.generateMipmaps = false;\n texture.mapping = THREE.EquirectangularReflectionMapping;\n texture.needsUpdate = true;\n return texture;\n}\n\n/** 1×1 float environment of the dome's (linear) color — a uniform sky. */\nfunction colorTexture(color: [number, number, number]): THREE.Texture {\n const data = new Float32Array([color[0], color[1], color[2], 1]);\n const texture = new THREE.DataTexture(data, 1, 1, THREE.RGBAFormat, THREE.FloatType);\n return finishDataTexture(texture, THREE.LinearSRGBColorSpace);\n}\n\n/**\n * Environment orientation: (root up-axis normalization) × (dome prim's stage\n * rotation) × (pole correction from three's +Y-pole equirect convention to\n * the dome's pole axis).\n */\nfunction domeRotation(robot: ThreeUsdRobot, dome: LightDescription): THREE.Euler {\n const q = robot.quaternion.clone();\n\n const prim = robot.stage?.GetPrimAtPath(dome.primPath);\n if (prim) {\n const world = new THREE.Matrix4().fromArray(computeWorldTransform(prim));\n const rotation = new THREE.Matrix4().extractRotation(world); // drops the prim chain's scale\n q.multiply(new THREE.Quaternion().setFromRotationMatrix(rotation));\n }\n\n // DomeLight_1 authors poleAxis; the original schema follows the stage up-axis.\n const pole = dome.poleAxis === \"Y\" || dome.poleAxis === \"Z\" ? dome.poleAxis : robot.robot.upAxis;\n if (pole === \"Z\") {\n q.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2));\n }\n return new THREE.Euler().setFromQuaternion(q);\n}\n\n/** Set a Scene Euler property when the installed three has it (r162+). */\nfunction setSceneEuler(\n scene: THREE.Scene,\n name: \"environmentRotation\" | \"backgroundRotation\",\n rotation: THREE.Euler,\n onWarn: ((message: string) => void) | undefined,\n): void {\n if (name in scene) {\n scene[name].copy(rotation);\n } else if (Math.abs(rotation.x) + Math.abs(rotation.y) + Math.abs(rotation.z) > 1e-6) {\n onWarn?.(`this three.js has no Scene.${name} (added r162); the dome orientation is dropped`);\n }\n}\n\n/** Set a Scene number property when the installed three has it (r163+). */\nfunction setSceneNumber(\n scene: THREE.Scene,\n name: \"environmentIntensity\" | \"backgroundIntensity\",\n value: number,\n onWarn: ((message: string) => void) | undefined,\n): void {\n if (name in scene) {\n scene[name] = value;\n } else if (Math.abs(value - 1) > 1e-6) {\n onWarn?.(`this three.js has no Scene.${name} (added r163); the dome intensity is dropped`);\n }\n}\n\n/** `RGBELoader.parse` wants an ArrayBuffer aligned to the data. */\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {\n return bytes.buffer as ArrayBuffer;\n }\n return bytes.slice().buffer as ArrayBuffer;\n}\n\n/** Decode LDR image bytes via a blob URL (browser environments). */\nfunction decodeImage(bytes: Uint8Array, url: string): Promise<HTMLImageElement> {\n if (typeof Image === \"undefined\") {\n return Promise.reject(new Error(\"LDR dome textures need a browser image decoder\"));\n }\n const mime = /\\.jpe?g$/i.test(url)\n ? \"image/jpeg\"\n : /\\.webp$/i.test(url)\n ? \"image/webp\"\n : \"image/png\";\n const blob = new Blob([bytes as unknown as BlobPart], { type: mime });\n const objectUrl = URL.createObjectURL(blob);\n return new Promise<HTMLImageElement>((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n URL.revokeObjectURL(objectUrl);\n resolve(img);\n };\n img.onerror = () => {\n URL.revokeObjectURL(objectUrl);\n reject(new Error(`failed to decode dome texture: ${url}`));\n };\n img.src = objectUrl;\n });\n}\n","/**\n * One-call renderer defaults (M28) — the settings every USD viewer built on\n * this library ends up wanting: filmic tone mapping (USD lights and HDRI\n * domes are HDR values; three's default `NoToneMapping` clips them), soft\n * shadow maps (the loader pre-flags meshes and lights, M25), and optionally\n * an exposure — either a plain multiplier or a `UsdGeomCamera` description,\n * whose USD 24.11 linear exposure scale is applied (M27).\n *\n * Deliberately small: post-processing (AO, SSR, TAA, bloom) stays out of the\n * library — compose three's own passes in your app; the Vite example shows a\n * GTAO setup.\n */\n\nimport * as THREE from \"three\";\nimport { type CameraDescription, computeCameraExposureScale } from \"../schemas/usdGeomCamera.js\";\n\n/** Filmic curve to install. `\"none\"` restores three's linear pass-through. */\nexport type ToneMappingPreset = \"ACES\" | \"AgX\" | \"neutral\" | \"none\";\n\nexport type RenderDefaultsOptions = {\n /** Default `\"ACES\"`. `\"neutral\"` (Khronos PBR Neutral) needs three r162+. */\n toneMapping?: ToneMappingPreset;\n /**\n * Sets `toneMappingExposure`: a number as-is, or a\n * {@link CameraDescription} via its linear exposure scale\n * ({@link computeCameraExposureScale}). Left untouched when omitted.\n */\n exposure?: number | CameraDescription;\n /** Enable `PCFSoftShadowMap` shadow rendering (default `true`). */\n shadows?: boolean;\n /** Receives compatibility diagnostics. */\n onWarn?: (message: string) => void;\n};\n\n/**\n * The renderer surface this helper touches — satisfied by `WebGLRenderer`\n * and `WebGPURenderer` alike, without depending on `three/webgpu` types.\n */\nexport type RenderDefaultsTarget = {\n toneMapping: THREE.ToneMapping;\n toneMappingExposure: number;\n shadowMap: { enabled: boolean; type: THREE.ShadowMapType };\n};\n\n/**\n * Apply viewer defaults to a renderer:\n *\n * ```ts\n * import { applyRenderDefaults } from \"three-usd-robot/rendering\";\n *\n * applyRenderDefaults(renderer); // ACES + soft shadow maps\n * applyRenderDefaults(renderer, { toneMapping: \"AgX\", exposure: robot.cameras[0]?.userData.usdCamera });\n * ```\n *\n * Only ever switches features on or assigns requested values — `shadows:\n * false` skips enabling shadow maps rather than force-disabling them.\n */\nexport function applyRenderDefaults(\n renderer: RenderDefaultsTarget,\n options: RenderDefaultsOptions = {},\n): void {\n renderer.toneMapping = resolveToneMapping(options.toneMapping ?? \"ACES\", options.onWarn);\n if (options.exposure !== undefined) {\n renderer.toneMappingExposure =\n typeof options.exposure === \"number\"\n ? options.exposure\n : computeCameraExposureScale(options.exposure);\n }\n if (options.shadows ?? true) {\n renderer.shadowMap.enabled = true;\n renderer.shadowMap.type = THREE.PCFSoftShadowMap;\n }\n}\n\nfunction resolveToneMapping(\n preset: ToneMappingPreset,\n onWarn: ((message: string) => void) | undefined,\n): THREE.ToneMapping {\n switch (preset) {\n case \"none\":\n return THREE.NoToneMapping;\n case \"AgX\":\n return THREE.AgXToneMapping;\n case \"neutral\": {\n // Runtime lookup: the peer floor (r160) predates NeutralToneMapping (r162).\n const neutral = (THREE as Record<string, unknown>).NeutralToneMapping;\n if (typeof neutral === \"number\") return neutral as THREE.ToneMapping;\n onWarn?.('this three.js has no NeutralToneMapping (added r162); using \"ACES\" instead');\n return THREE.ACESFilmicToneMapping;\n }\n case \"ACES\":\n return THREE.ACESFilmicToneMapping;\n }\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import { P as Prim } from './usdLux-BqQlSOwG.js';
2
+
3
+ /**
4
+ * UsdGeomCamera schema helpers (M27) — type check, description extraction,
5
+ * and the linear exposure-scale math.
6
+ *
7
+ * Focal length and apertures follow the practical convention of every DCC
8
+ * (Isaac Sim, Blender, usdview): they are millimeters on a virtual film back,
9
+ * regardless of stage units. Clipping range and focus distance are stage
10
+ * units. Orthographic apertures are tenths of a stage unit (GfCamera's
11
+ * `APERTURE_UNIT`), so `aperture / 10` is the view-box size.
12
+ */
13
+
14
+ declare function isCamera(prim: Prim): boolean;
15
+ /** A UsdGeomCamera resolved to plain values (schema fallbacks applied). */
16
+ type CameraDescription = {
17
+ primPath: string;
18
+ name: string;
19
+ projection: "perspective" | "orthographic";
20
+ /** Millimeters (see module doc). */
21
+ focalLength: number;
22
+ horizontalAperture: number;
23
+ verticalAperture: number;
24
+ /** Lens shift, millimeters. */
25
+ horizontalApertureOffset: number;
26
+ verticalApertureOffset: number;
27
+ /** Near/far, stage units. */
28
+ clippingRange: [number, number];
29
+ /** `0` disables depth of field. */
30
+ fStop: number;
31
+ /** Stage units; `0` = unset. */
32
+ focusDistance: number;
33
+ /** Exposure compensation, stops (EV). */
34
+ exposure: number;
35
+ /** Exposure system (USD 24.11) — authored values only, see {@link computeCameraExposureScale}. */
36
+ exposureIso?: number;
37
+ exposureTime?: number;
38
+ exposureFStop?: number;
39
+ exposureResponsivity?: number;
40
+ };
41
+ /** Resolve a Camera prim into a {@link CameraDescription}, or `null` if not one. */
42
+ declare function readCameraDescription(prim: Prim): CameraDescription | null;
43
+ /**
44
+ * `UsdGeomCamera::ComputeLinearExposureScale` (USD 24.11):
45
+ * `time × iso/100 × responsivity ÷ fStop² × 2^exposure`. Schema fallbacks
46
+ * make an unauthored camera scale by exactly `2^exposure` (i.e. `1`), so it
47
+ * is safe to feed straight into `renderer.toneMappingExposure` — a stage
48
+ * authored with real photographic values yields a physically small scale that
49
+ * pairs with `lightIntensityScale: 1` raw photometric lighting.
50
+ */
51
+ declare function computeCameraExposureScale(camera: CameraDescription): number;
52
+
53
+ export { type CameraDescription as C, computeCameraExposureScale as c, isCamera as i, readCameraDescription as r };
@@ -498,6 +498,34 @@ declare class Stage {
498
498
  GetTimeCodesPerSecond(): number;
499
499
  }
500
500
 
501
+ /**
502
+ * Resolves USD asset paths (from references / payloads / sublayers) to URLs and
503
+ * fetches their text. Composition (`composition.ts`) is I/O-agnostic and goes
504
+ * through an {@link AssetResolver}, so the same engine works in the browser, in
505
+ * Node, or against an in-memory file map in tests.
506
+ */
507
+ interface AssetResolver {
508
+ /** Resolve an authored asset path against the layer's base URL to an absolute key. */
509
+ resolve(assetPath: string, baseUrl: string): string;
510
+ /** Fetch the text of a resolved asset; rejects if it cannot be read. */
511
+ fetchText(url: string): Promise<string>;
512
+ /** Fetch the raw bytes of a resolved asset (for binary USDC / USDZ). Optional. */
513
+ fetchBytes?(url: string): Promise<Uint8Array>;
514
+ }
515
+ /** URL-based resolver using the global `fetch` (browser / Node 18+). */
516
+ declare class DefaultAssetResolver implements AssetResolver {
517
+ resolve(assetPath: string, baseUrl: string): string;
518
+ fetchText(url: string): Promise<string>;
519
+ fetchBytes(url: string): Promise<Uint8Array>;
520
+ }
521
+ /**
522
+ * Resolver over an in-memory `{ path: contents }` map (text or bytes), with
523
+ * posix-style joins. Useful for tests and bundled assets.
524
+ */
525
+ declare function createMemoryResolver(files: Record<string, string | Uint8Array>): AssetResolver;
526
+ /** Resolve `rel` against `baseUrl` using posix semantics, normalizing `.`/`..`. */
527
+ declare function joinPosix(baseUrl: string, rel: string): string;
528
+
501
529
  /**
502
530
  * Builds a kinematic spanning tree from a {@link RobotDescription}.
503
531
  *
@@ -542,4 +570,83 @@ type BuildTreeOptions = {
542
570
  };
543
571
  declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
544
572
 
545
- export { makeRotationY as $, type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEFAULT_METERS_PER_UNIT as D, type Vec4 as E, buildKinematicTree as F, channelFromSamples as G, decomposeRigid as H, fromUsdMatrix as I, type JointDriveDescription as J, type KinematicTree as K, type LinkInertialDescription as L, type Mat4 as M, getTranslation as N, identity4 as O, Prim as P, Quat as Q, type RobotDescription as R, Stage as S, type TreeEdge as T, type UsdaFile as U, type Variability as V, interpolate as W, invert as X, makeEuler as Y, makeRotationFromQuat as Z, makeRotationX as _, AssetPath as a, makeRotationZ as a0, makeScale as a1, makeTranslation as a2, multiply as a3, multiplyAll as a4, toUsdMatrix as a5, Attribute as b, type AttributeSpec as c, DEG2RAD as d, type JointDescription as e, type JointMimicDescription as f, type JointType as g, type KinematicNode as h, Layer as i, type LinkDescription as j, type ListOp as k, type MetadataMap as l, type PrimSpec as m, type PropertySpec as n, RAD2DEG as o, Relationship as p, type RelationshipSpec as q, type SampleChannel as r, type SdfPath as s, type Specifier as t, type UpAxis as u, type UsdDictionary as v, UsdMatrix as w, type UsdValue as x, type Vec2 as y, type Vec3 as z };
573
+ /**
574
+ * UsdLux schema helpers (M25) — type checks and light description extraction.
575
+ *
576
+ * Light inputs are read `inputs:`-namespaced first with a fallback to the
577
+ * pre-21.02 un-namespaced spelling; Omniverse assets author both (and some,
578
+ * like `texture:file` on stock Isaac DomeLights, only the legacy one).
579
+ * Extraction is Three.js-independent: the resulting {@link LightDescription}
580
+ * is bound to concrete lights in `three/LightBinding`.
581
+ */
582
+
583
+ type UsdLightKind = "distant" | "sphere" | "rect" | "disk" | "cylinder" | "dome";
584
+ /** True for any UsdLux light prim this runtime can bind (or collect, for domes). */
585
+ declare function isLight(prim: Prim): boolean;
586
+ /** True for a light schema the runtime knows about but cannot render (yet). */
587
+ declare function isUnsupportedLight(prim: Prim): boolean;
588
+ /** The light kind of a prim, or `null` for non-light prims. */
589
+ declare function getLightKind(prim: Prim): UsdLightKind | null;
590
+ /**
591
+ * A UsdLux light resolved to plain values. Sizes and distances are in stage
592
+ * units of the light's local frame — multiply by the accumulated world scale
593
+ * (the prim chain often carries an `xformOp:scale`, e.g. stock Isaac rooms).
594
+ */
595
+ type LightDescription = {
596
+ primPath: string;
597
+ name: string;
598
+ kind: UsdLightKind;
599
+ /**
600
+ * Effective linear RGB: `inputs:color`, multiplied by the blackbody tint of
601
+ * `inputs:colorTemperature` when `inputs:enableColorTemperature` is set.
602
+ */
603
+ color: [number, number, number];
604
+ /**
605
+ * Effective scalar emission: `inputs:intensity × 2^inputs:exposure`, in the
606
+ * authoring app's photometric units (see docs/lighting.md for calibration).
607
+ * Defaults to 1, except DistantLight whose schema fallback is 50000.
608
+ */
609
+ intensity: number;
610
+ /** `inputs:normalize` — power is normalized by emitter size (not applied yet). */
611
+ normalize: boolean;
612
+ /** ShadowAPI `shadow:enable`, defaulting to `true` when unauthored. */
613
+ shadowEnabled: boolean;
614
+ /** True when `shadow:enable` is actually authored (gates point-light shadows). */
615
+ shadowAuthored: boolean;
616
+ /** ShapingAPI cone when it actually restricts emission (half-angle < 90°). */
617
+ cone?: {
618
+ angleDeg: number;
619
+ softness: number;
620
+ };
621
+ /** SphereLight / DiskLight / CylinderLight radius (stage units). */
622
+ radius?: number;
623
+ /** RectLight size (stage units). */
624
+ width?: number;
625
+ height?: number;
626
+ /** CylinderLight length (stage units). */
627
+ length?: number;
628
+ /** DistantLight angular size (degrees). */
629
+ angle?: number;
630
+ /** SphereLight `treatAsPoint`. */
631
+ treatAsPoint?: boolean;
632
+ /** DomeLight / RectLight `texture:file` asset path (unresolved). */
633
+ textureFile?: string;
634
+ /** DomeLight `texture:format` (`latlong`, `angular`, …). */
635
+ textureFormat?: string;
636
+ /** DomeLight_1 `poleAxis` (`scene` / `Y` / `Z`). */
637
+ poleAxis?: string;
638
+ };
639
+ /**
640
+ * Resolve a light prim into a {@link LightDescription}, or `null` for
641
+ * non-light prims. Purely declarative — visibility/purpose gating is the
642
+ * binder's concern.
643
+ */
644
+ declare function readLightDescription(prim: Prim): LightDescription | null;
645
+ /**
646
+ * Blackbody temperature → linear-ish RGB tint, normalized so 6500 K is white.
647
+ * Piecewise curve fit (Tanner Helland's approximation); good to a few percent
648
+ * over UsdLux's 1000–10000 K authoring range.
649
+ */
650
+ declare function colorTemperatureToRgb(kelvin: number): [number, number, number];
651
+
652
+ export { getTranslation as $, type AssetResolver as A, type BuildTreeOptions as B, type CompositionArc as C, DEFAULT_METERS_PER_UNIT as D, type UsdLightKind as E, UsdMatrix as F, type UsdValue as G, type Variability as H, type Vec4 as I, type JointDriveDescription as J, type KinematicTree as K, type LightDescription as L, type Mat4 as M, buildKinematicTree as N, channelFromSamples as O, Prim as P, Quat as Q, type RobotDescription as R, Stage as S, type TreeEdge as T, type UsdaFile as U, type Vec2 as V, colorTemperatureToRgb as W, createMemoryResolver as X, decomposeRigid as Y, fromUsdMatrix as Z, getLightKind as _, type Vec3 as a, identity4 as a0, interpolate as a1, invert as a2, isLight as a3, isUnsupportedLight as a4, joinPosix as a5, makeEuler as a6, makeRotationFromQuat as a7, makeRotationX as a8, makeRotationY as a9, makeRotationZ as aa, makeScale as ab, makeTranslation as ac, multiply as ad, multiplyAll as ae, readLightDescription as af, toUsdMatrix as ag, type Axis as b, type LinkInertialDescription as c, AssetPath as d, Attribute as e, type AttributeSpec as f, DEG2RAD as g, DefaultAssetResolver as h, type JointDescription as i, type JointMimicDescription as j, type JointType as k, type KinematicNode as l, Layer as m, type LinkDescription as n, type ListOp as o, type MetadataMap as p, type PrimSpec as q, type PropertySpec as r, RAD2DEG as s, Relationship as t, type RelationshipSpec as u, type SampleChannel as v, type SdfPath as w, type Specifier as x, type UpAxis as y, type UsdDictionary as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-usd-robot",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Kinematic OpenUSD robot loader for Three.js — load Isaac Sim / OpenUSD robot assets, extract joints and links, and control articulations in the browser.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,6 +44,10 @@
44
44
  "types": "./dist/nodes.d.ts",
45
45
  "import": "./dist/nodes.js"
46
46
  },
47
+ "./rendering": {
48
+ "types": "./dist/rendering.d.ts",
49
+ "import": "./dist/rendering.js"
50
+ },
47
51
  "./react": {
48
52
  "types": "./dist/react.d.ts",
49
53
  "import": "./dist/react.js"