@needle-tools/usd 0.0.1-1d6bd1e

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 (47) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +100 -0
  3. package/examples/index.html +58 -0
  4. package/examples/package-lock.json +1548 -0
  5. package/examples/package.json +24 -0
  6. package/examples/public/HttpReferences copy.usda +46 -0
  7. package/examples/public/HttpReferences.usda +44 -0
  8. package/examples/public/gingerbread/GingerbreadHouse.usda +35 -0
  9. package/examples/public/gingerbread/house/GingerBreadHouse.usdc +0 -0
  10. package/examples/public/gingerbread/house/textures/color.jpg +0 -0
  11. package/examples/public/gingerbread/house/textures/metallic_roughness.jpg +0 -0
  12. package/examples/public/gingerbread/house/textures/normal.jpg +0 -0
  13. package/examples/public/gingerbread/snowman/Snowman.usdc +0 -0
  14. package/examples/public/gingerbread/snowman/textures/color.jpg +0 -0
  15. package/examples/public/gingerbread/snowman/textures/metallic_roughness.jpg +0 -0
  16. package/examples/public/gingerbread/snowman/textures/normal.jpg +0 -0
  17. package/examples/public/test.usdz +0 -0
  18. package/examples/public/vite.svg +1 -0
  19. package/examples/src/fileHandling.ts +256 -0
  20. package/examples/src/main.ts +167 -0
  21. package/examples/src/three.ts +140 -0
  22. package/examples/src/vite-env.d.ts +1 -0
  23. package/examples/tsconfig.json +23 -0
  24. package/examples/vite.config.js +21 -0
  25. package/package.json +50 -0
  26. package/src/bindings/.gitattributes +2 -0
  27. package/src/bindings/emHdBindings.data +19331 -0
  28. package/src/bindings/emHdBindings.js +12227 -0
  29. package/src/bindings/emHdBindings.wasm +0 -0
  30. package/src/bindings/emHdBindings.worker.js +124 -0
  31. package/src/bindings/index.js +124 -0
  32. package/src/create.three.js +252 -0
  33. package/src/hydra/ThreeJsRenderDelegate.js +872 -0
  34. package/src/hydra/consoleRenderDelegate.js +47 -0
  35. package/src/hydra/index.js +5 -0
  36. package/src/index.d.ts +1 -0
  37. package/src/index.js +5 -0
  38. package/src/plugins/index.js +2 -0
  39. package/src/plugins/needle.js +158 -0
  40. package/src/types/bindings.d.ts +82 -0
  41. package/src/types/create.three.d.ts +65 -0
  42. package/src/types/hydra.d.ts +32 -0
  43. package/src/types/index.d.ts +5 -0
  44. package/src/types/plugins.d.ts +9 -0
  45. package/src/types/vite.d.ts +19 -0
  46. package/src/utils.js +24 -0
  47. package/src/vite/index.js +22 -0
@@ -0,0 +1,47 @@
1
+ // This is a complete Hydra render delegate.
2
+ // It doesn't do anything useful, but it logs out all the calls and arguments it receives.
3
+ export const delegate = {
4
+ createSPrim: (...args) => {
5
+ console.log("createSPrim", args);
6
+ return {
7
+ updateNode: (...args) => {
8
+ console.log("updateNode", args);
9
+ },
10
+ updateFinished: (...args) => {
11
+ console.log("updateFinished", args);
12
+ },
13
+ }
14
+ },
15
+ createRPrim: (...args) => {
16
+ console.log("createRPrim", args);
17
+ return {
18
+ setMaterial(...args) {
19
+ console.log("setMaterial", args);
20
+ },
21
+ updatePoints(...args) {
22
+ console.log("updatePoints", args);
23
+ },
24
+ updateIndices(...args) {
25
+ console.log("updateIndices", args);
26
+ },
27
+ updateNormals(...args) {
28
+ console.log("updateNormals", args);
29
+ },
30
+ setTransform(...args) {
31
+ console.log("setTransform", args);
32
+ },
33
+ updatePrimvar(...args) {
34
+ console.log("updatePrimvar", args);
35
+ },
36
+ skelDetected(...args) {
37
+ console.log("skelDetected", args);
38
+ },
39
+ setGeomSubsetMaterial(...args) {
40
+ console.log("setGeomSubsetMaterial", args);
41
+ },
42
+ }
43
+ },
44
+ CommitResources(...args) {
45
+ console.log("CommitResources", args);
46
+ },
47
+ }
@@ -0,0 +1,5 @@
1
+ import { ThreeRenderDelegateInterface } from "./ThreeJsRenderDelegate.js";
2
+ import { delegate as ConsoleDelegate } from "./consoleRenderDelegate.js";
3
+
4
+ export { ThreeRenderDelegateInterface as threeJsRenderDelegate };
5
+ export { ConsoleDelegate as consoleRenderDelegate }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./types";
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./bindings/index.js";
2
+ export * from "./hydra/index.js";
3
+ export * from "./create.three.js";
4
+ export * from "./vite/index.js";
5
+ export { tryDetermineFileFormat } from "./utils.js"
@@ -0,0 +1,2 @@
1
+ export { addPluginForNeedleEngine } from "./needle.js";
2
+
@@ -0,0 +1,158 @@
1
+ import { Loader, Object3D } from "three";
2
+ import { createThreeHydra, getUsdModule } from "../index.js";
3
+
4
+
5
+ /** @type {import("../types").addPluginForNeedleEngine} */
6
+ export async function addPluginForNeedleEngine(options) {
7
+ if (options.debug) console.debug("🐉 Adding USDZ plugin to Needle Engine");
8
+ return import("@needle-tools/engine").then(module => {
9
+ return onAddNeedlePlugin(module, options);
10
+ })
11
+ }
12
+
13
+
14
+ /**
15
+ * @param {typeof import("@needle-tools/engine")} NEEDLE
16
+ * @param {import("../types").PluginContext} opts
17
+ */
18
+ function onAddNeedlePlugin(NEEDLE, opts) {
19
+
20
+ class NeedleUSDZHHandler extends NEEDLE.Behaviour {
21
+ constructor() {
22
+ super();
23
+ /**
24
+ * @type {import("..").NeedleThreeHydraHandle | null}
25
+ */
26
+ this.handle = null;
27
+ this.root = new Object3D();
28
+ }
29
+
30
+ update() {
31
+ if (this.handle) {
32
+ this.handle.update(this.context.time.deltaTime);
33
+ }
34
+ }
35
+ onDestroy() {
36
+ if (this.handle) {
37
+ this.handle.dispose();
38
+ this.handle = null;
39
+ }
40
+ }
41
+ }
42
+
43
+
44
+ /**
45
+ * Needle USDZ Loader
46
+ * @extends {Loader<Object3D>}
47
+ */
48
+ class NeedleUSDZLoader extends Loader {
49
+
50
+ /**
51
+ * the usd module promise - initialized when the first loader is created
52
+ * @type {Promise<import("../types").USD> | null}
53
+ * @private
54
+ * @static
55
+ */
56
+ static usdModule = null;
57
+
58
+ // LOADER IMPL:
59
+
60
+ /**
61
+ * @param {NeedleUSDZHHandler} comp
62
+ */
63
+ constructor(comp) {
64
+ super();
65
+ this.comp = comp;
66
+ }
67
+
68
+ /**
69
+ * @type {Loader<Object3D>["loadAsync"]}
70
+ */
71
+ async loadAsync(url, onProgress) {
72
+
73
+ const debug = opts.debug || false;;
74
+
75
+ onProgress?.(new ProgressEvent("load", { lengthComputable: false, loaded: 0, total: 0 }));
76
+
77
+ // Get the files immediately (don't wait for the USD module to be loaded)
78
+ const files = opts.getFiles();
79
+
80
+ NeedleUSDZLoader.usdModule ??= getUsdModule({
81
+ debug
82
+ // setURLModifier: USDLoadingManager.urlModifier,
83
+ }).then(res => {
84
+ if (debug) console.debug("🐉 USD HYDRA IS READY");
85
+ return res;
86
+ })
87
+ const usd = await NeedleUSDZLoader.usdModule;
88
+
89
+ onProgress?.(new ProgressEvent("load", { lengthComputable: true, loaded: .5, total: 1 }));
90
+
91
+ // Load hydra
92
+ const handle = await createThreeHydra({
93
+ debug,
94
+ USD: usd,
95
+ scene: this.comp.root,
96
+ url: url,
97
+ files: files,
98
+ });
99
+ this.comp.handle = handle;
100
+
101
+ if (debug) console.debug("Loaded", this.comp);
102
+
103
+ onProgress?.(new ProgressEvent("load", { lengthComputable: true, loaded: 1, total: 1 }));
104
+
105
+ return this.comp.root;
106
+ }
107
+
108
+ parse() {
109
+ console.error("🐉 USDZ LOADER PARSE CALLED: Not implemented");
110
+ return this.comp.root;
111
+ }
112
+ }
113
+
114
+
115
+
116
+
117
+
118
+ const removeFiletype = NEEDLE.NeedleEngineModelLoader.onDetermineModelMimetype(cb => {
119
+
120
+ if (cb.contentType === "application/vnd.usd") {
121
+ return "model/vnd.usd";
122
+ }
123
+ const ext = cb.url.split('.').pop()?.toLowerCase();
124
+ switch (ext) {
125
+ case "usdz":
126
+ return "model/vnd.usdz+zip"
127
+ case "usda":
128
+ return "model/vnd.usda"
129
+ case "usdc":
130
+ return "model/vnd.usdc"
131
+ case "usd":
132
+ return "model/vnd.usd"
133
+ }
134
+ return null;
135
+ });
136
+
137
+ const handlers = new Array();
138
+ const removeCustomLoader = NEEDLE.NeedleEngineModelLoader.onCreateCustomModelLoader(cb => {
139
+ if (cb.mimetype.startsWith("model/vnd.usd")) {
140
+ const handler = new NeedleUSDZHHandler();
141
+ handlers.push(handler);
142
+ NEEDLE.addComponent(cb.context.scene, handler);
143
+ return new NeedleUSDZLoader(handler);
144
+ }
145
+ return null;
146
+ });
147
+
148
+
149
+ return () => {
150
+ removeFiletype();
151
+ removeCustomLoader();
152
+ for (const handler of handlers) {
153
+ handler.destroy();
154
+ }
155
+ handlers.length = 0;
156
+ }
157
+
158
+ }
@@ -0,0 +1,82 @@
1
+
2
+ declare type FSNode = {
3
+ contents: ArrayLike,
4
+ id: number,
5
+ mode: number,
6
+ name: string,
7
+ timestamp: number,
8
+ isFolder: boolean,
9
+ isDevice: boolean,
10
+ read: boolean,
11
+ write: boolean,
12
+ }
13
+
14
+ declare type USD = {
15
+ FS_createDataFile: (parent: string, filepath: string, data: Uint8Array, canRead: boolean, canWrite: boolean, canOwn: boolean) => FSNode,
16
+ FS_createPath: (parent: string, path: string, canRead: boolean, canWrite: boolean) => FSNode,
17
+ FS_unlink: (path: string) => void,
18
+ FS_readdir: (path: string) => string[],
19
+ FS_rmdir: (path: string) => void,
20
+ FS_analyzePath: (path: string) => FSNode,
21
+ HdWebSyncDriver: new (delegate: hydraDelegate, filepath: string) => HdWebSyncDriver,
22
+ flushPendingDeletes: () => void,
23
+ ready: Promise<any>,
24
+ debug: boolean;
25
+ calledRun: boolean;
26
+ stderr: any;
27
+ stdin: any;
28
+ stdout: any;
29
+ };
30
+
31
+ declare type USDStage = {
32
+ GetStartTimeCode(): number,
33
+ GetEndTimeCode(): number,
34
+ GetTimeCodesPerSecond(): number,
35
+ GetUpAxis(): number,
36
+ }
37
+
38
+ declare type HdWebSyncDriver = {
39
+ getFile: (path: string, cb: (loadedFile: ArrayBufferLike) => void) => void,
40
+ GetStage: () => USDStage,
41
+ SetTime(timecode: number): void,
42
+ GetTime(): number,
43
+ Draw(): void,
44
+
45
+ /** ??? */
46
+ clone(): HdWebSyncDriver,
47
+ /** ??? */
48
+ delete(): void,
49
+ /** ??? */
50
+ deleteLater(): void,
51
+ isDeleted(): boolean,
52
+ /** ??? */
53
+ isAliasOf(): boolean,
54
+ }
55
+
56
+ export type GetUsdModuleOptions = {
57
+ debug?: boolean,
58
+ mainScriptUrlOrBlob?: string,
59
+ wasmBinary?: ArrayBufferLike,
60
+ locateFile?: (path: string) => string,
61
+ getPreloadedPackage?: (file: string, size: number) => ArrayBuffer | null,
62
+ setStatus?: (status: string) => void,
63
+ onDownloadProgress?: (downloaded: number, total: number) => void,
64
+ /** Returns a transferable object that can be resolved to an ArrayBuffer,
65
+ * or an URL that can be fetched to get an ArrayBuffer.
66
+ */
67
+ urlModifier?: (url: string) =>
68
+ Promise<
69
+ ArrayBuffer | File | FileSystemFileHandle | FileSystemFileEntry | string
70
+ > | ArrayBuffer | File | FileSystemFileHandle | FileSystemFileEntry | string,
71
+ }
72
+
73
+ /**
74
+ * Loads the USD Module.
75
+ * @example
76
+ * ```javascript
77
+ * getUsdModule({ mainScriptUrlOrBlob: "/emHdBindings.js" }).then(USD => { ... })
78
+ * ```
79
+ */
80
+ export function getUsdModule(opts?: GetUsdModuleOptions): Promise<USD>;
81
+
82
+ export type USDRoot = {}
@@ -0,0 +1,65 @@
1
+ import { Scene } from "three"
2
+ import { HdWebSyncDriver, USD } from ".."
3
+
4
+ export declare type HydraFile = File & { path: string };
5
+
6
+ export declare type createThreeHydraConfig = {
7
+
8
+ debug?: boolean,
9
+
10
+ /**
11
+ * USD Module
12
+ * @example
13
+ * ```javascript
14
+ * getUsdModule({
15
+ * mainScriptUrlOrBlob: "/emHdBindings.js"
16
+ * }).then(USD => { ... })
17
+ * ```
18
+ */
19
+ USD: USD,
20
+
21
+ /**
22
+ * Optional buffer of the usdz file
23
+ */
24
+ buffer?: ArrayBuffer,
25
+
26
+ url?: string,
27
+
28
+ /**
29
+ * The scene to be loaded as the root of the USD stage.
30
+ */
31
+ scene: Object3D,
32
+
33
+ /**
34
+ * Files to be loaded into the virtual file system.
35
+ * The first file will be loaded as the root file, others will be loaded as dependencies.
36
+ */
37
+ files: Array<HydraFile>,
38
+ }
39
+
40
+ /**
41
+ * Use the hydra handle to update the usd scene periodically.
42
+ */
43
+ export declare type NeedleThreeHydraHandle = {
44
+ /** The hydra driver
45
+ */
46
+ driver: HdWebSyncDriver,
47
+ /** Call update periodically to update the usd scene.
48
+ * @param dt The delta time since the last update.
49
+ */
50
+ update: (dt: number) => void,
51
+ /** Call this to update the usd scene immediately.
52
+ * @param dt The delta time since the last update.
53
+ */
54
+ dispose: () => void,
55
+ }
56
+
57
+ export declare class USDLoadingManager {
58
+ static setURLModifier(callback: (url: string) => string): void;
59
+ static urlModifier: (url: string) => string;
60
+ }
61
+
62
+ /**
63
+ * Creates a new three.js hydra handle.
64
+ */
65
+ export function createThreeHydra(config: createThreeHydraConfig): Promise<NeedleThreeHydraHandle>
@@ -0,0 +1,32 @@
1
+ import { Object3D, Texture } from 'three';
2
+ import { HdWebSyncDriver } from './bindings';
3
+
4
+ export class hydraDelegate { }
5
+
6
+
7
+
8
+
9
+
10
+
11
+ export const consoleRenderDelegate: hydraDelegate = {}
12
+
13
+
14
+
15
+
16
+
17
+
18
+ export type threeJsRenderDelegateConfig = {
19
+ driver: () => HdWebSyncDriver,
20
+ usdRoot: Object3D,
21
+ /** Paths for resolving textures */
22
+ paths?: string[],
23
+ /** @deprecated */
24
+ envMap?: Texture,
25
+ }
26
+ export class threeJsRenderDelegate extends hydraDelegate {
27
+ constructor(path: string, config: threeJsRenderDelegateConfig)
28
+ }
29
+
30
+
31
+
32
+
@@ -0,0 +1,5 @@
1
+ export * from "./bindings"
2
+ export * from "./hydra"
3
+ export * from "./create.three"
4
+ export * from "./vite";
5
+ export * from "./plugins"
@@ -0,0 +1,9 @@
1
+
2
+
3
+ export type PluginContext = {
4
+ debug?: boolean,
5
+ getFiles: () => Array<import("../types").HydraFile>
6
+ }
7
+
8
+
9
+ export declare function addPluginForNeedleEngine(options: PluginContext): Promise<(() => void)>;
@@ -0,0 +1,19 @@
1
+ import { Plugin } from "vite";
2
+
3
+
4
+ /**
5
+ * Enable USD WASM support for vite based applications.
6
+ * @example sveltekit
7
+ * ```ts
8
+ * import { needleUSD } from '@needle-tools/usd';
9
+ * export default defineConfig(async ({ command }) => {
10
+ * return {
11
+ * plugins: [
12
+ * ...needleUSD(),
13
+ * sveltekit(),
14
+ * ],
15
+ * }
16
+ * });
17
+ * ```
18
+ */
19
+ export declare function needleUSD(): Array<Plugin>;
package/src/utils.js ADDED
@@ -0,0 +1,24 @@
1
+
2
+
3
+ /**
4
+ * @param {ArrayBuffer} buffer
5
+ * @returns {"usdz" | "usd" | "usda" | "unknown"}
6
+ */
7
+ export function tryDetermineFileFormat(buffer) {
8
+
9
+ const bytes = new Uint8Array(buffer, 0, 16);
10
+
11
+ // USDZ
12
+ if (bytes[0] == 80 && bytes[1] == 75 && bytes[2] == 3 && bytes[3] == 4) {
13
+ return "usdz";
14
+ }
15
+ // USD
16
+ if (bytes[0] == 80 && bytes[1] == 88 && bytes[2] == 82 && bytes[3] == 45 && bytes[4] == 85 && bytes[5] == 83 && bytes[6] == 68 && bytes[7] == 67) {
17
+ return "usd";
18
+ }
19
+ if (bytes[0] === 35 && bytes[1] === 117 && bytes[2] === 115 && bytes[3] === 100) {
20
+ return "usda";
21
+ }
22
+
23
+ return "unknown";
24
+ }
@@ -0,0 +1,22 @@
1
+
2
+ /** @type {import('vite').Plugin} */
3
+ const crossOriginIsolatedPlugin = {
4
+ name: 'needle:usd-crossoriginisolated',
5
+ configureServer: (server) => {
6
+ server.middlewares.use((_req, res, next) => {
7
+ res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
8
+ res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
9
+ next();
10
+ });
11
+ }
12
+ };
13
+
14
+
15
+ /**
16
+ * Needle USD plugin for vite.
17
+ * This plugin sets the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
18
+ * @returns {Array<import('vite').Plugin>} plugins
19
+ */
20
+ export function needleUSD() {
21
+ return [crossOriginIsolatedPlugin]
22
+ }