@openusd-wasm/pxr 0.0.1
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/openusd_pxr.d.ts +190 -0
- package/dist/openusd_pxr.js +441 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 KeJun <https://github.com/kejunmao>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @openusd-wasm/pxr
|
|
2
|
+
|
|
3
|
+
Community ESM facade for OpenUSD `pxr` WebAssembly bindings.
|
|
4
|
+
|
|
5
|
+
This package is the high-level facade. The generated Emscripten module and Wasm
|
|
6
|
+
binary live in `@openusd-wasm/core`, matching the split used by projects such
|
|
7
|
+
as `ffmpeg.wasm`.
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
@openusd-wasm/pxr facade and TypeScript declarations
|
|
11
|
+
@openusd-wasm/core Emscripten core JavaScript and Wasm binary
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use a statically imported core factory with explicit asset URLs:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import createOpenUsdPxrWasm from '@openusd-wasm/core'
|
|
18
|
+
import { wasmURL, workerURL } from '@openusd-wasm/core/urls'
|
|
19
|
+
import { createPxr } from '@openusd-wasm/pxr'
|
|
20
|
+
|
|
21
|
+
const pxr = await createPxr({
|
|
22
|
+
core: createOpenUsdPxrWasm,
|
|
23
|
+
wasmURL,
|
|
24
|
+
workerURL,
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This form is recommended for bundlers such as Next.js/Turbopack because the
|
|
29
|
+
Emscripten core module is visible as a normal static import. For custom hosting
|
|
30
|
+
or CDN usage, you can still pass URLs manually:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const pxr = await createPxr({
|
|
34
|
+
coreURL: 'https://cdn.example.com/openusd_pxr_wasm.js',
|
|
35
|
+
wasmURL: 'https://cdn.example.com/openusd_pxr_wasm.wasm',
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The returned namespace exposes `Gf`, `Sdf`, `Usd`, `UsdGeom`, `UsdPhysics`,
|
|
40
|
+
`UsdUtils`, and `FS` helpers for the virtual filesystem.
|
|
41
|
+
|
|
42
|
+
## Unsupported APIs
|
|
43
|
+
|
|
44
|
+
The facade intentionally exposes a small JavaScript-friendly subset of `pxr`.
|
|
45
|
+
The following areas are not wrapped yet.
|
|
46
|
+
|
|
47
|
+
### Gf
|
|
48
|
+
|
|
49
|
+
- Camera, Rotation, and non-4d matrix variants need value conversion coverage before exposing.
|
|
50
|
+
- Line, plane, frustum, interval, and dual quaternion helpers are deferred until their dependent value types are bound.
|
|
51
|
+
|
|
52
|
+
### Sdf
|
|
53
|
+
|
|
54
|
+
- PrimSpec, AttributeSpec, and RelationshipSpec mutation proxies are deferred because list/proxy lifetimes need JS-safe wrappers.
|
|
55
|
+
- ChangeBlock, LayerOffset, Reference/Payload value classes, VariantSpec, and VariantSetSpec APIs are deferred to the next Sdf expansion.
|
|
56
|
+
|
|
57
|
+
### Usd
|
|
58
|
+
|
|
59
|
+
- EditTarget/EditContext, StageCache, StagePopulationMask, StageLoadRules, VariantSets, Payloads, and PrimRange iterator objects are not wrapped yet.
|
|
60
|
+
- Generated Usd schema class coverage beyond concrete schema registration is deferred to schema-driven generation.
|
|
61
|
+
|
|
62
|
+
### UsdGeom
|
|
63
|
+
|
|
64
|
+
- Generated schema classes beyond Xform, Sphere, Cube, and Mesh are deferred to schema-driven generation.
|
|
65
|
+
- PointInstancer, Camera, Imageable, BBoxCache overrides, and typed-array zero-copy bridges are deferred.
|
|
66
|
+
|
|
67
|
+
### UsdPhysics
|
|
68
|
+
|
|
69
|
+
- Generated schema classes beyond CollisionAPI and RigidBodyAPI are deferred to schema-driven generation.
|
|
70
|
+
- Joints, limits, drives, parsing utilities, and metrics helpers are deferred to the next physics expansion.
|
|
71
|
+
|
|
72
|
+
### UsdUtils
|
|
73
|
+
|
|
74
|
+
- StageCache, SparseValueWriter, Stitch, StitchClips, dependency extraction, FlattenLayerStack, TimeCodeRange, localization, and compliance helpers are deferred.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
//#region src/bindings.d.ts
|
|
2
|
+
interface EmscriptenFs {
|
|
3
|
+
filesystems?: Record<string, unknown>;
|
|
4
|
+
mkdir(path: string): void;
|
|
5
|
+
mount(fsType: unknown, opts: Record<string, unknown>, mountPoint: string): void;
|
|
6
|
+
unmount(mountPoint: string): void;
|
|
7
|
+
writeFile(path: string, data: string | ArrayBuffer | ArrayBufferView): void;
|
|
8
|
+
readFile(path: string, opts?: unknown): unknown;
|
|
9
|
+
readdir(path: string): string[];
|
|
10
|
+
stat(path: string): {
|
|
11
|
+
mode: number;
|
|
12
|
+
size: number;
|
|
13
|
+
};
|
|
14
|
+
isDir(mode: number): boolean;
|
|
15
|
+
isFile(mode: number): boolean;
|
|
16
|
+
unlink(path: string): void;
|
|
17
|
+
rmdir(path: string): void;
|
|
18
|
+
rename(oldPath: string, newPath: string): void;
|
|
19
|
+
syncfs(populate: boolean, callback: (err?: unknown) => void): void;
|
|
20
|
+
}
|
|
21
|
+
interface WasmModule extends Record<string, unknown> {
|
|
22
|
+
FS?: EmscriptenFs;
|
|
23
|
+
mainScriptUrlOrBlob?: unknown;
|
|
24
|
+
PxrJsInitializeOpenUsdRuntime?: () => void;
|
|
25
|
+
}
|
|
26
|
+
interface PxrDisposable {
|
|
27
|
+
delete(): void;
|
|
28
|
+
using<T>(fn: (value: this) => T): T;
|
|
29
|
+
}
|
|
30
|
+
type PxrInstance = PxrDisposable & Record<string, any>;
|
|
31
|
+
type PxrClass = {
|
|
32
|
+
new (...args: any[]): PxrInstance;
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
};
|
|
35
|
+
type PxrFunction = (...args: any[]) => any;
|
|
36
|
+
type PxrBindingObject = Record<string, any>;
|
|
37
|
+
type PxrNamespaceValue = PxrClass | PxrFunction | PxrBindingObject;
|
|
38
|
+
type PxrNamespace = Record<string, PxrNamespaceValue>;
|
|
39
|
+
type PxrModuleName = 'Gf' | 'Sdf' | 'Usd' | 'UsdGeom' | 'UsdPhysics' | 'UsdUtils';
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/modules/gf.d.ts
|
|
42
|
+
interface PxrGfNamespace {
|
|
43
|
+
Vec2f: PxrClass;
|
|
44
|
+
Vec2d: PxrClass;
|
|
45
|
+
Vec2h: PxrClass;
|
|
46
|
+
Vec2i: PxrClass;
|
|
47
|
+
Vec3f: PxrClass;
|
|
48
|
+
Vec3d: PxrClass;
|
|
49
|
+
Vec3h: PxrClass;
|
|
50
|
+
Vec3i: PxrClass;
|
|
51
|
+
Vec4f: PxrClass;
|
|
52
|
+
Vec4d: PxrClass;
|
|
53
|
+
Vec4h: PxrClass;
|
|
54
|
+
Vec4i: PxrClass;
|
|
55
|
+
Matrix3d: PxrClass;
|
|
56
|
+
Matrix4d: PxrClass;
|
|
57
|
+
Quatf: PxrClass;
|
|
58
|
+
Quatd: PxrClass;
|
|
59
|
+
Quath: PxrClass;
|
|
60
|
+
Range1d: PxrClass;
|
|
61
|
+
Range2d: PxrClass;
|
|
62
|
+
Range3d: PxrClass;
|
|
63
|
+
Range3f: PxrClass;
|
|
64
|
+
BBox3d: PxrClass;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/modules/sdf.d.ts
|
|
68
|
+
interface PxrSdfNamespace {
|
|
69
|
+
Path: PxrClass;
|
|
70
|
+
AssetPath: PxrClass;
|
|
71
|
+
Layer: PxrClass;
|
|
72
|
+
PrimSpec: PxrClass;
|
|
73
|
+
ValueTypeName: PxrClass;
|
|
74
|
+
ValueTypeNames: PxrBindingObject;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/modules/usd.d.ts
|
|
78
|
+
interface PxrUsdNamespace {
|
|
79
|
+
Stage: PxrClass;
|
|
80
|
+
Prim: PxrClass;
|
|
81
|
+
Attribute: PxrClass;
|
|
82
|
+
Relationship: PxrClass;
|
|
83
|
+
References: PxrClass;
|
|
84
|
+
TimeCode: PxrClass;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/modules/usdGeom.d.ts
|
|
88
|
+
interface PxrUsdGeomNamespace {
|
|
89
|
+
Xform: PxrClass;
|
|
90
|
+
Sphere: PxrClass;
|
|
91
|
+
Cube: PxrClass;
|
|
92
|
+
Mesh: PxrClass;
|
|
93
|
+
Xformable: PxrClass;
|
|
94
|
+
XformOp: PxrClass;
|
|
95
|
+
Primvar: PxrClass;
|
|
96
|
+
PrimvarsAPI: PxrClass;
|
|
97
|
+
BBoxCache: PxrClass;
|
|
98
|
+
SetStageMetersPerUnit: PxrFunction;
|
|
99
|
+
SetStageUpAxis: PxrFunction;
|
|
100
|
+
GetStageUpAxis: PxrFunction;
|
|
101
|
+
Tokens: PxrBindingObject;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/modules/usdPhysics.d.ts
|
|
105
|
+
interface PxrUsdPhysicsNamespace {
|
|
106
|
+
CollisionAPI: PxrClass;
|
|
107
|
+
RigidBodyAPI: PxrClass;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/modules/usdUtils.d.ts
|
|
111
|
+
interface PxrUsdUtilsNamespace {
|
|
112
|
+
CreateNewUsdzPackage: PxrFunction;
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/modules/index.d.ts
|
|
116
|
+
interface PxrModuleMap {
|
|
117
|
+
Gf: PxrGfNamespace;
|
|
118
|
+
Sdf: PxrSdfNamespace;
|
|
119
|
+
Usd: PxrUsdNamespace;
|
|
120
|
+
UsdGeom: PxrUsdGeomNamespace;
|
|
121
|
+
UsdPhysics: PxrUsdPhysicsNamespace;
|
|
122
|
+
UsdUtils: PxrUsdUtilsNamespace;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/index.d.ts
|
|
126
|
+
type WasmFactory = (options?: Record<string, unknown>) => Promise<WasmModule> | WasmModule;
|
|
127
|
+
type WasmFactoryModule = {
|
|
128
|
+
default?: WasmFactory;
|
|
129
|
+
createOpenUsdPxrWasm?: WasmFactory;
|
|
130
|
+
};
|
|
131
|
+
type PxrResourceURL = string | URL | Blob;
|
|
132
|
+
type PxrWasmFactory = WasmFactory;
|
|
133
|
+
type PxrWasmFactoryModule = WasmFactoryModule;
|
|
134
|
+
type PxrCoreFactory = WasmFactory | WasmFactoryModule;
|
|
135
|
+
interface PxrFsListEntry {
|
|
136
|
+
name: string;
|
|
137
|
+
path: string;
|
|
138
|
+
mode: number;
|
|
139
|
+
size: number;
|
|
140
|
+
isDir: boolean;
|
|
141
|
+
isFile: boolean;
|
|
142
|
+
}
|
|
143
|
+
interface PxrFsHelpers {
|
|
144
|
+
raw: EmscriptenFs;
|
|
145
|
+
mkdirp(path: string): void;
|
|
146
|
+
createDir(path: string): void;
|
|
147
|
+
deleteDir(path: string): void;
|
|
148
|
+
listDir(path: string): PxrFsListEntry[];
|
|
149
|
+
mount(mount: Record<string, unknown>): void;
|
|
150
|
+
mount(fsType: string, options: Record<string, unknown>, mountPoint: string): void;
|
|
151
|
+
unmount(mountPoint: string): void;
|
|
152
|
+
mountNodeFS(mountPoint: string, root?: string): void;
|
|
153
|
+
mountWorkerFS(mountPoint: string, options?: Record<string, unknown>): void;
|
|
154
|
+
mountIDBFS(mountPoint: string, options?: Record<string, unknown>): void;
|
|
155
|
+
writeFile(path: string, data: string | ArrayBuffer | ArrayBufferView): void;
|
|
156
|
+
readFile(path: string, encodingOrOpts?: 'binary' | 'utf8' | 'utf-8' | Record<string, unknown>): unknown;
|
|
157
|
+
exists(path: string): boolean;
|
|
158
|
+
deleteFile(path: string): void;
|
|
159
|
+
unlink(path: string): void;
|
|
160
|
+
rename(oldPath: string, newPath: string): void;
|
|
161
|
+
renameFile(oldPath: string, newPath: string): void;
|
|
162
|
+
syncfs(populate?: boolean): Promise<void>;
|
|
163
|
+
}
|
|
164
|
+
interface Pxr extends PxrModuleMap {
|
|
165
|
+
_module: WasmModule;
|
|
166
|
+
FS: PxrFsHelpers;
|
|
167
|
+
using<T extends PxrDisposable>(value: T): T;
|
|
168
|
+
}
|
|
169
|
+
interface CreatePxrOptions extends Record<string, unknown> {
|
|
170
|
+
core?: PxrCoreFactory;
|
|
171
|
+
coreURL?: PxrResourceURL;
|
|
172
|
+
wasmURL: PxrResourceURL;
|
|
173
|
+
workerURL?: PxrResourceURL;
|
|
174
|
+
mainScriptUrlOrBlob?: string | URL | Blob;
|
|
175
|
+
locateFile?: (path: string, prefix: string) => string;
|
|
176
|
+
mounts?: Array<Record<string, unknown>>;
|
|
177
|
+
files?: Record<string, string | ArrayBuffer | ArrayBufferView>;
|
|
178
|
+
preRun?: Function | Function[];
|
|
179
|
+
}
|
|
180
|
+
interface PxrCoreAssets {
|
|
181
|
+
core?: PxrCoreFactory;
|
|
182
|
+
coreURL?: PxrResourceURL;
|
|
183
|
+
wasmURL: PxrResourceURL;
|
|
184
|
+
workerURL?: PxrResourceURL;
|
|
185
|
+
}
|
|
186
|
+
type CreatePxrFromCoreOptions = Omit<CreatePxrOptions, 'core' | 'coreURL' | 'wasmURL' | 'workerURL'>;
|
|
187
|
+
declare function createPxr(options: CreatePxrOptions): Promise<Pxr>;
|
|
188
|
+
declare function createPxrFromCore(core: PxrCoreAssets, options?: CreatePxrFromCoreOptions): Promise<Pxr>;
|
|
189
|
+
//#endregion
|
|
190
|
+
export { CreatePxrFromCoreOptions, CreatePxrOptions, type EmscriptenFs, Pxr, type PxrBindingObject, type PxrClass, PxrCoreAssets, PxrCoreFactory, type PxrDisposable, PxrFsHelpers, PxrFsListEntry, type PxrFunction, type PxrGfNamespace, type PxrInstance, type PxrModuleMap, type PxrModuleName, type PxrNamespace, type PxrNamespaceValue, PxrResourceURL, type PxrSdfNamespace, type PxrUsdGeomNamespace, type PxrUsdNamespace, type PxrUsdPhysicsNamespace, type PxrUsdUtilsNamespace, PxrWasmFactory, PxrWasmFactoryModule, type WasmModule, createPxr, createPxr as default, createPxrFromCore };
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
//#region src/bindings.ts
|
|
2
|
+
function flatName(moduleName, name) {
|
|
3
|
+
return `PxrJs${moduleName}${name}`;
|
|
4
|
+
}
|
|
5
|
+
function attachUsing(value) {
|
|
6
|
+
if (!value) return value;
|
|
7
|
+
Object.defineProperty(value, "using", {
|
|
8
|
+
enumerable: false,
|
|
9
|
+
value(callback) {
|
|
10
|
+
try {
|
|
11
|
+
return callback(value);
|
|
12
|
+
} finally {
|
|
13
|
+
if (value && value.delete) value.delete();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function installUsing(cls) {
|
|
20
|
+
if (!cls || !cls.prototype || cls.prototype.using) return cls;
|
|
21
|
+
try {
|
|
22
|
+
Object.defineProperty(cls.prototype, "using", {
|
|
23
|
+
enumerable: false,
|
|
24
|
+
value(callback) {
|
|
25
|
+
try {
|
|
26
|
+
return callback(this);
|
|
27
|
+
} finally {
|
|
28
|
+
if (this && this.delete) this.delete();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
} catch {}
|
|
33
|
+
return cls;
|
|
34
|
+
}
|
|
35
|
+
function mapClass(module, embindName) {
|
|
36
|
+
const cls = module[embindName];
|
|
37
|
+
if (typeof cls !== "function") throw new Error(`Missing Embind export ${embindName}`);
|
|
38
|
+
return installUsing(cls);
|
|
39
|
+
}
|
|
40
|
+
function mapFunction(module, embindName) {
|
|
41
|
+
const fn = module[embindName];
|
|
42
|
+
if (typeof fn !== "function") throw new Error(`Missing Embind export ${embindName}`);
|
|
43
|
+
return (...args) => fn(...args);
|
|
44
|
+
}
|
|
45
|
+
function mapObject(module, embindName) {
|
|
46
|
+
const fn = module[embindName];
|
|
47
|
+
if (typeof fn !== "function") throw new Error(`Missing Embind export ${embindName}`);
|
|
48
|
+
return fn();
|
|
49
|
+
}
|
|
50
|
+
function bindClass(module, moduleName, name) {
|
|
51
|
+
return mapClass(module, flatName(moduleName, name));
|
|
52
|
+
}
|
|
53
|
+
function bindFunction(module, moduleName, name) {
|
|
54
|
+
return mapFunction(module, flatName(moduleName, name));
|
|
55
|
+
}
|
|
56
|
+
function bindObject(module, moduleName, name) {
|
|
57
|
+
return mapObject(module, flatName(moduleName, name));
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/modules/gf.ts
|
|
61
|
+
function buildGfNamespace(module) {
|
|
62
|
+
return {
|
|
63
|
+
Vec2f: bindClass(module, "Gf", "Vec2f"),
|
|
64
|
+
Vec2d: bindClass(module, "Gf", "Vec2d"),
|
|
65
|
+
Vec2h: bindClass(module, "Gf", "Vec2h"),
|
|
66
|
+
Vec2i: bindClass(module, "Gf", "Vec2i"),
|
|
67
|
+
Vec3f: bindClass(module, "Gf", "Vec3f"),
|
|
68
|
+
Vec3d: bindClass(module, "Gf", "Vec3d"),
|
|
69
|
+
Vec3h: bindClass(module, "Gf", "Vec3h"),
|
|
70
|
+
Vec3i: bindClass(module, "Gf", "Vec3i"),
|
|
71
|
+
Vec4f: bindClass(module, "Gf", "Vec4f"),
|
|
72
|
+
Vec4d: bindClass(module, "Gf", "Vec4d"),
|
|
73
|
+
Vec4h: bindClass(module, "Gf", "Vec4h"),
|
|
74
|
+
Vec4i: bindClass(module, "Gf", "Vec4i"),
|
|
75
|
+
Matrix3d: bindClass(module, "Gf", "Matrix3d"),
|
|
76
|
+
Matrix4d: bindClass(module, "Gf", "Matrix4d"),
|
|
77
|
+
Quatf: bindClass(module, "Gf", "Quatf"),
|
|
78
|
+
Quatd: bindClass(module, "Gf", "Quatd"),
|
|
79
|
+
Quath: bindClass(module, "Gf", "Quath"),
|
|
80
|
+
Range1d: bindClass(module, "Gf", "Range1d"),
|
|
81
|
+
Range2d: bindClass(module, "Gf", "Range2d"),
|
|
82
|
+
Range3d: bindClass(module, "Gf", "Range3d"),
|
|
83
|
+
Range3f: bindClass(module, "Gf", "Range3f"),
|
|
84
|
+
BBox3d: bindClass(module, "Gf", "BBox3d")
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/modules/sdf.ts
|
|
89
|
+
function buildSdfNamespace(module) {
|
|
90
|
+
return {
|
|
91
|
+
Path: bindClass(module, "Sdf", "Path"),
|
|
92
|
+
AssetPath: bindClass(module, "Sdf", "AssetPath"),
|
|
93
|
+
Layer: bindClass(module, "Sdf", "Layer"),
|
|
94
|
+
PrimSpec: bindClass(module, "Sdf", "PrimSpec"),
|
|
95
|
+
ValueTypeName: bindClass(module, "Sdf", "ValueTypeName"),
|
|
96
|
+
ValueTypeNames: bindObject(module, "Sdf", "ValueTypeNames")
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/modules/usd.ts
|
|
101
|
+
function buildUsdNamespace(module) {
|
|
102
|
+
return {
|
|
103
|
+
Stage: bindClass(module, "Usd", "Stage"),
|
|
104
|
+
Prim: bindClass(module, "Usd", "Prim"),
|
|
105
|
+
Attribute: bindClass(module, "Usd", "Attribute"),
|
|
106
|
+
Relationship: bindClass(module, "Usd", "Relationship"),
|
|
107
|
+
References: bindClass(module, "Usd", "References"),
|
|
108
|
+
TimeCode: bindClass(module, "Usd", "TimeCode")
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/modules/usdGeom.ts
|
|
113
|
+
function buildUsdGeomNamespace(module) {
|
|
114
|
+
return {
|
|
115
|
+
Xform: bindClass(module, "UsdGeom", "Xform"),
|
|
116
|
+
Sphere: bindClass(module, "UsdGeom", "Sphere"),
|
|
117
|
+
Cube: bindClass(module, "UsdGeom", "Cube"),
|
|
118
|
+
Mesh: bindClass(module, "UsdGeom", "Mesh"),
|
|
119
|
+
Xformable: bindClass(module, "UsdGeom", "Xformable"),
|
|
120
|
+
XformOp: bindClass(module, "UsdGeom", "XformOp"),
|
|
121
|
+
Primvar: bindClass(module, "UsdGeom", "Primvar"),
|
|
122
|
+
PrimvarsAPI: bindClass(module, "UsdGeom", "PrimvarsAPI"),
|
|
123
|
+
BBoxCache: bindClass(module, "UsdGeom", "BBoxCache"),
|
|
124
|
+
SetStageMetersPerUnit: bindFunction(module, "UsdGeom", "SetStageMetersPerUnit"),
|
|
125
|
+
SetStageUpAxis: bindFunction(module, "UsdGeom", "SetStageUpAxis"),
|
|
126
|
+
GetStageUpAxis: bindFunction(module, "UsdGeom", "GetStageUpAxis"),
|
|
127
|
+
Tokens: bindObject(module, "UsdGeom", "Tokens")
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/modules/usdPhysics.ts
|
|
132
|
+
function buildUsdPhysicsNamespace(module) {
|
|
133
|
+
return {
|
|
134
|
+
CollisionAPI: bindClass(module, "UsdPhysics", "CollisionAPI"),
|
|
135
|
+
RigidBodyAPI: bindClass(module, "UsdPhysics", "RigidBodyAPI")
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/modules/usdUtils.ts
|
|
140
|
+
function buildUsdUtilsNamespace(module) {
|
|
141
|
+
return { CreateNewUsdzPackage: bindFunction(module, "UsdUtils", "CreateNewUsdzPackage") };
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/modules/index.ts
|
|
145
|
+
function buildPxrModules(module) {
|
|
146
|
+
return {
|
|
147
|
+
Gf: buildGfNamespace(module),
|
|
148
|
+
Sdf: buildSdfNamespace(module),
|
|
149
|
+
Usd: buildUsdNamespace(module),
|
|
150
|
+
UsdGeom: buildUsdGeomNamespace(module),
|
|
151
|
+
UsdPhysics: buildUsdPhysicsNamespace(module),
|
|
152
|
+
UsdUtils: buildUsdUtilsNamespace(module)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/modules/sdfConvenience.ts
|
|
157
|
+
function installGetter(cls, name, getter) {
|
|
158
|
+
if (!cls?.prototype) return;
|
|
159
|
+
if (Object.getOwnPropertyDescriptor(cls.prototype, name)) return;
|
|
160
|
+
try {
|
|
161
|
+
Object.defineProperty(cls.prototype, name, {
|
|
162
|
+
enumerable: false,
|
|
163
|
+
get() {
|
|
164
|
+
return getter(this);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
} catch {}
|
|
168
|
+
}
|
|
169
|
+
function installAccessor(cls, name, getter, setter) {
|
|
170
|
+
if (!cls?.prototype) return;
|
|
171
|
+
if (Object.getOwnPropertyDescriptor(cls.prototype, name)) return;
|
|
172
|
+
try {
|
|
173
|
+
Object.defineProperty(cls.prototype, name, {
|
|
174
|
+
enumerable: false,
|
|
175
|
+
get() {
|
|
176
|
+
return getter(this);
|
|
177
|
+
},
|
|
178
|
+
set(next) {
|
|
179
|
+
setter(this, next);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
} catch {}
|
|
183
|
+
}
|
|
184
|
+
function installSdfConvenienceAccessors(pxr) {
|
|
185
|
+
installGetter(pxr.Sdf.AssetPath, "path", (value) => value.GetAssetPath());
|
|
186
|
+
installGetter(pxr.Sdf.AssetPath, "resolvedPath", (value) => value.GetResolvedPath());
|
|
187
|
+
installAccessor(pxr.Sdf.Layer, "subLayerPaths", (value) => value.GetSubLayerPaths(), (value, next) => value.SetSubLayerPaths(next));
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/index.ts
|
|
191
|
+
function mkdirp(FS, path) {
|
|
192
|
+
const parts = path.split("/").filter(Boolean);
|
|
193
|
+
let current = path.startsWith("/") ? "/" : "";
|
|
194
|
+
for (const part of parts) {
|
|
195
|
+
current = current === "/" ? `/${part}` : `${current}/${part}`;
|
|
196
|
+
try {
|
|
197
|
+
FS.mkdir(current);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
try {
|
|
200
|
+
if (FS.isDir(FS.stat(current).mode)) continue;
|
|
201
|
+
} catch {}
|
|
202
|
+
throw err;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function parentPath(path) {
|
|
207
|
+
const parts = path.split("/");
|
|
208
|
+
parts.pop();
|
|
209
|
+
const parent = parts.join("/");
|
|
210
|
+
if (parent) return parent;
|
|
211
|
+
return path.startsWith("/") ? "/" : ".";
|
|
212
|
+
}
|
|
213
|
+
function joinFsPath(base, name) {
|
|
214
|
+
if (base === "/") return `/${name}`;
|
|
215
|
+
return `${base.replace(/\/+$/, "")}/${name}`;
|
|
216
|
+
}
|
|
217
|
+
function ensureParent(FS, path) {
|
|
218
|
+
const parent = parentPath(path);
|
|
219
|
+
if (parent !== ".") mkdirp(FS, parent);
|
|
220
|
+
}
|
|
221
|
+
function isNodeLike() {
|
|
222
|
+
const processLike = globalThis.process;
|
|
223
|
+
return !!processLike?.versions?.node && processLike.type !== "renderer";
|
|
224
|
+
}
|
|
225
|
+
function isBlobLike(value) {
|
|
226
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
227
|
+
}
|
|
228
|
+
function toUrlString(value, baseUrl = import.meta.url) {
|
|
229
|
+
if (value === void 0 || value === null) return void 0;
|
|
230
|
+
if (isBlobLike(value)) return URL.createObjectURL(value);
|
|
231
|
+
if (value instanceof URL) return value.href;
|
|
232
|
+
if (typeof value !== "string") return value;
|
|
233
|
+
try {
|
|
234
|
+
return new URL(value, baseUrl).href;
|
|
235
|
+
} catch {
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function requireUrlString(value, name) {
|
|
240
|
+
if (value === void 0 || value === null) throw new TypeError(`${name} is required`);
|
|
241
|
+
const url = toUrlString(value);
|
|
242
|
+
if (typeof url !== "string") throw new TypeError(`${name} must resolve to a string URL`);
|
|
243
|
+
return url;
|
|
244
|
+
}
|
|
245
|
+
function fileUrlToPathString(value) {
|
|
246
|
+
const url = value instanceof URL ? value : new URL(value);
|
|
247
|
+
if (url.protocol !== "file:") return value instanceof URL ? url.href : value;
|
|
248
|
+
return decodeURIComponent(url.pathname);
|
|
249
|
+
}
|
|
250
|
+
function toWorkerScript(value) {
|
|
251
|
+
if (value === void 0 || value === null || isBlobLike(value)) return value;
|
|
252
|
+
if (isNodeLike()) {
|
|
253
|
+
if (value instanceof URL) return fileUrlToPathString(value);
|
|
254
|
+
if (typeof value === "string" && value.startsWith("file:")) return fileUrlToPathString(value);
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
return toUrlString(value);
|
|
258
|
+
}
|
|
259
|
+
function resolveWasmFactory(core, source = "core") {
|
|
260
|
+
const factory = typeof core === "function" ? core : core.default ?? core.createOpenUsdPxrWasm;
|
|
261
|
+
if (typeof factory !== "function") throw new Error(`Unable to load OpenUSD Wasm factory from ${source}`);
|
|
262
|
+
return factory;
|
|
263
|
+
}
|
|
264
|
+
function runtimeImport(url) {
|
|
265
|
+
return Function("url", "return import(url)")(url);
|
|
266
|
+
}
|
|
267
|
+
async function loadWasmFactory(core, coreURL) {
|
|
268
|
+
if (core !== void 0) return resolveWasmFactory(core);
|
|
269
|
+
const url = requireUrlString(coreURL, "coreURL");
|
|
270
|
+
return resolveWasmFactory(await runtimeImport(url), url);
|
|
271
|
+
}
|
|
272
|
+
function buildLocateFile({ wasmURL, workerURL, locateFile }) {
|
|
273
|
+
const resolvedWasmURL = requireUrlString(wasmURL, "wasmURL");
|
|
274
|
+
const resolvedWorkerURL = workerURL === void 0 ? void 0 : requireUrlString(workerURL, "workerURL");
|
|
275
|
+
return (path, prefix) => {
|
|
276
|
+
if (path.endsWith(".wasm")) return resolvedWasmURL;
|
|
277
|
+
if (resolvedWorkerURL && (path.endsWith(".worker.js") || path.endsWith(".worker.mjs"))) return String(resolvedWorkerURL);
|
|
278
|
+
if (locateFile) return locateFile(path, prefix);
|
|
279
|
+
return `${prefix || ""}${path}`;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function requireFs(module) {
|
|
283
|
+
if (!module.FS) throw new Error("openusd_pxr_wasm was built without FS export");
|
|
284
|
+
return module.FS;
|
|
285
|
+
}
|
|
286
|
+
function normalizeMountArgs(mountOrType, options = {}, mountPoint) {
|
|
287
|
+
if (typeof mountOrType === "string") return {
|
|
288
|
+
type: mountOrType,
|
|
289
|
+
...options || {},
|
|
290
|
+
mountPoint
|
|
291
|
+
};
|
|
292
|
+
return mountOrType || {};
|
|
293
|
+
}
|
|
294
|
+
function mountFilesystem(module, mountOrType, options, mountPointArg) {
|
|
295
|
+
const FS = requireFs(module);
|
|
296
|
+
const mount = normalizeMountArgs(mountOrType, options, mountPointArg);
|
|
297
|
+
const type = typeof mount.type === "string" ? mount.type : "MEMFS";
|
|
298
|
+
const mountPoint = typeof mount.mountPoint === "string" ? mount.mountPoint : typeof mount.path === "string" ? mount.path : void 0;
|
|
299
|
+
if (!mountPoint) throw new Error("mountPoint is required");
|
|
300
|
+
mkdirp(FS, mountPoint);
|
|
301
|
+
if (type === "MEMFS") return;
|
|
302
|
+
const fsType = FS.filesystems?.[type];
|
|
303
|
+
if (!fsType) throw new Error(`${type} is not available in this Wasm build`);
|
|
304
|
+
const opts = { ...mount };
|
|
305
|
+
delete opts.type;
|
|
306
|
+
delete opts.mountPoint;
|
|
307
|
+
delete opts.path;
|
|
308
|
+
FS.mount(fsType, opts, mountPoint);
|
|
309
|
+
}
|
|
310
|
+
function installFiles(module, files) {
|
|
311
|
+
if (!files) return;
|
|
312
|
+
const FS = requireFs(module);
|
|
313
|
+
for (const [path, data] of Object.entries(files)) {
|
|
314
|
+
ensureParent(FS, path);
|
|
315
|
+
FS.writeFile(path, data);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function normalizeCallbacks(callbacks) {
|
|
319
|
+
if (!callbacks) return [];
|
|
320
|
+
return Array.isArray(callbacks) ? callbacks : [callbacks];
|
|
321
|
+
}
|
|
322
|
+
function readFile(FS, path, encodingOrOpts) {
|
|
323
|
+
if (typeof encodingOrOpts === "string") {
|
|
324
|
+
if (encodingOrOpts === "utf8" || encodingOrOpts === "utf-8") return FS.readFile(path, { encoding: "utf8" });
|
|
325
|
+
return FS.readFile(path);
|
|
326
|
+
}
|
|
327
|
+
return FS.readFile(path, encodingOrOpts);
|
|
328
|
+
}
|
|
329
|
+
function listDir(FS, path) {
|
|
330
|
+
return FS.readdir(path).filter((name) => name !== "." && name !== "..").map((name) => {
|
|
331
|
+
const childPath = joinFsPath(path, name);
|
|
332
|
+
const stat = FS.stat(childPath);
|
|
333
|
+
return {
|
|
334
|
+
name,
|
|
335
|
+
path: childPath,
|
|
336
|
+
mode: stat.mode,
|
|
337
|
+
size: stat.size,
|
|
338
|
+
isDir: FS.isDir(stat.mode),
|
|
339
|
+
isFile: FS.isFile(stat.mode)
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function defaultNodeRoot() {
|
|
344
|
+
if (!globalThis.process?.cwd) throw new Error("A NODEFS root is required outside Node.js");
|
|
345
|
+
return globalThis.process.cwd();
|
|
346
|
+
}
|
|
347
|
+
function buildFsHelpers(module) {
|
|
348
|
+
const FS = requireFs(module);
|
|
349
|
+
const rename = (oldPath, newPath) => {
|
|
350
|
+
ensureParent(FS, newPath);
|
|
351
|
+
FS.rename(oldPath, newPath);
|
|
352
|
+
};
|
|
353
|
+
return {
|
|
354
|
+
raw: FS,
|
|
355
|
+
mkdirp: (path) => mkdirp(FS, path),
|
|
356
|
+
createDir: (path) => mkdirp(FS, path),
|
|
357
|
+
deleteDir: (path) => FS.rmdir(path),
|
|
358
|
+
listDir: (path) => listDir(FS, path),
|
|
359
|
+
mount: (mountOrType, options, mountPoint) => mountFilesystem(module, mountOrType, options, mountPoint),
|
|
360
|
+
unmount: (mountPoint) => FS.unmount(mountPoint),
|
|
361
|
+
mountNodeFS: (mountPoint, root = defaultNodeRoot()) => mountFilesystem(module, {
|
|
362
|
+
type: "NODEFS",
|
|
363
|
+
mountPoint,
|
|
364
|
+
root
|
|
365
|
+
}),
|
|
366
|
+
mountWorkerFS: (mountPoint, options = {}) => mountFilesystem(module, {
|
|
367
|
+
type: "WORKERFS",
|
|
368
|
+
mountPoint,
|
|
369
|
+
...options
|
|
370
|
+
}),
|
|
371
|
+
mountIDBFS: (mountPoint, options = {}) => mountFilesystem(module, {
|
|
372
|
+
type: "IDBFS",
|
|
373
|
+
mountPoint,
|
|
374
|
+
...options
|
|
375
|
+
}),
|
|
376
|
+
writeFile: (path, data) => {
|
|
377
|
+
ensureParent(FS, path);
|
|
378
|
+
FS.writeFile(path, data);
|
|
379
|
+
},
|
|
380
|
+
readFile: (path, encodingOrOpts) => readFile(FS, path, encodingOrOpts),
|
|
381
|
+
exists: (path) => {
|
|
382
|
+
try {
|
|
383
|
+
FS.stat(path);
|
|
384
|
+
return true;
|
|
385
|
+
} catch {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
deleteFile: (path) => FS.unlink(path),
|
|
390
|
+
unlink: (path) => FS.unlink(path),
|
|
391
|
+
rename,
|
|
392
|
+
renameFile: rename,
|
|
393
|
+
syncfs: (populate = false) => new Promise((resolve, reject) => {
|
|
394
|
+
FS.syncfs(populate, (err) => err ? reject(err) : resolve());
|
|
395
|
+
})
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function buildPxr(module) {
|
|
399
|
+
const pxr = {
|
|
400
|
+
_module: module,
|
|
401
|
+
FS: buildFsHelpers(module),
|
|
402
|
+
using: attachUsing,
|
|
403
|
+
...buildPxrModules(module)
|
|
404
|
+
};
|
|
405
|
+
installSdfConvenienceAccessors(pxr);
|
|
406
|
+
return pxr;
|
|
407
|
+
}
|
|
408
|
+
function initializeOpenUsdRuntime(module) {
|
|
409
|
+
if (typeof module.PxrJsInitializeOpenUsdRuntime !== "function") throw new Error("openusd_pxr_wasm was built without OpenUSD runtime initialization support");
|
|
410
|
+
module.PxrJsInitializeOpenUsdRuntime();
|
|
411
|
+
}
|
|
412
|
+
async function createPxr(options) {
|
|
413
|
+
if (!options) throw new TypeError("createPxr requires options with core or coreURL and wasmURL");
|
|
414
|
+
const { core, coreURL, wasmURL, workerURL, mainScriptUrlOrBlob, mounts = [], files, preRun, locateFile, ...wasmOptions } = options;
|
|
415
|
+
wasmOptions.locateFile = buildLocateFile({
|
|
416
|
+
wasmURL,
|
|
417
|
+
workerURL,
|
|
418
|
+
locateFile
|
|
419
|
+
});
|
|
420
|
+
wasmOptions.preRun = [...normalizeCallbacks(preRun), (module) => {
|
|
421
|
+
for (const mount of mounts) mountFilesystem(module, mount);
|
|
422
|
+
installFiles(module, files);
|
|
423
|
+
}];
|
|
424
|
+
if (mainScriptUrlOrBlob !== void 0) wasmOptions.mainScriptUrlOrBlob = toWorkerScript(mainScriptUrlOrBlob);
|
|
425
|
+
else if (workerURL !== void 0) wasmOptions.mainScriptUrlOrBlob = toWorkerScript(workerURL);
|
|
426
|
+
const module = await (await loadWasmFactory(core, coreURL))(wasmOptions);
|
|
427
|
+
initializeOpenUsdRuntime(module);
|
|
428
|
+
return buildPxr(module);
|
|
429
|
+
}
|
|
430
|
+
async function createPxrFromCore(core, options = {}) {
|
|
431
|
+
if (!core) throw new TypeError("createPxrFromCore requires @openusd-wasm/core assets");
|
|
432
|
+
return createPxr({
|
|
433
|
+
...options,
|
|
434
|
+
core: core.core,
|
|
435
|
+
coreURL: core.coreURL,
|
|
436
|
+
wasmURL: core.wasmURL,
|
|
437
|
+
workerURL: core.workerURL ?? core.coreURL
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
441
|
+
export { createPxr, createPxr as default, createPxrFromCore };
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openusd-wasm/pxr",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "Community TypeScript facade for OpenUSD pxr WebAssembly bindings.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/openusd-wasm/openusd-pxr-wasm",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/openusd-wasm/openusd-pxr-wasm.git",
|
|
11
|
+
"directory": "packages/pxr"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/openusd_pxr.js",
|
|
14
|
+
"module": "./dist/openusd_pxr.js",
|
|
15
|
+
"types": "./dist/openusd_pxr.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/openusd_pxr.d.ts",
|
|
19
|
+
"import": "./dist/openusd_pxr.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^25.6.2",
|
|
28
|
+
"@typescript/native-preview": "7.0.0-dev.20260509.2",
|
|
29
|
+
"tsdown": "^0.22.0",
|
|
30
|
+
"typescript": "^6.0.3",
|
|
31
|
+
"vitest": "^4.1.5"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsdown",
|
|
35
|
+
"dev": "tsdown --watch",
|
|
36
|
+
"debug": "tsdown --sourcemap",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
}
|
|
40
|
+
}
|