@needle-tools/materialx 1.0.0-alpha → 1.0.0-next.12f185d

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/src/materialx.ts CHANGED
@@ -1,220 +1,238 @@
1
- import { Context, delay, isDevEnvironment, ObjectUtils } from "@needle-tools/engine";
2
- import MaterialX from "../bin/JsMaterialXGenShader.js";
3
- import { debug } from "./utils.js";
4
- import { renderPMREMToEquirect } from "./textureHelper.js";
5
- import { Light, MeshBasicMaterial, Object3D, PMREMGenerator } from "three";
6
- import { registerLights } from "./helper.js";
7
-
8
- // Configure MaterialX with the correct path for its data files
9
- const materialXConfig = {
10
- locateFile: (path: string, scriptDirectory: string) => {
11
- if (debug) console.debug("MaterialX locateFile called:", { path, scriptDirectory });
12
-
13
- // Return the correct path for MaterialX data files
14
- if (path.endsWith('.data') || path.endsWith('.wasm')) {
15
- // For Vite dev server, we need to use the correct module path
16
- const correctPath = new URL(`../bin/${path}`, import.meta.url).href;
17
- if (debug) console.log("Resolved path:", correctPath);
18
- return correctPath;
19
- }
20
- return scriptDirectory + path;
21
- },
22
- // Add buffer allocation to handle the data file properly
23
- wasmBinary: null,
24
- wasmMemory: null
25
- };
26
-
27
- // Global MaterialX module instance - initialized lazily
28
- export const state = new class {
29
- materialXModule: any = null;
30
- materialXGenerator: any = null;
31
- materialXGenContext: any = null;
32
- materialXStdLib: any = null;
33
- materialXInitPromise: Promise<void> | null = null;
34
-
35
- // Global MaterialX environment instance
36
- private _materialXEnvironment: MaterialXEnvironment | null = null;
37
- get materialXEnvironment() {
38
- this._materialXEnvironment ??= new MaterialXEnvironment();
39
- return this._materialXEnvironment;
40
- }
41
- }
42
-
43
-
44
- // Initialize MaterialX WASM module lazily
45
- export async function initializeMaterialX(): Promise<void> {
46
- if (state.materialXInitPromise) {
47
- return state.materialXInitPromise;
48
- }
49
- return state.materialXInitPromise = (async () => {
50
- if (state.materialXModule) return; // Already initialized
51
- if (debug) console.log("Initializing MaterialX WASM module...");
52
- try {
53
- const module = await MaterialX(materialXConfig);
54
- if (debug) console.log("MaterialXLoader module loaded", module);
55
- state.materialXModule = module;
56
-
57
- // Initialize shader generator and context
58
- state.materialXGenerator = module.EsslShaderGenerator.create();
59
- state.materialXGenContext = new module.GenContext(state.materialXGenerator);
60
-
61
- // Load standard libraries
62
- const tempDoc = module.createDocument();
63
- state.materialXStdLib = module.loadStandardLibraries(state.materialXGenContext);
64
- tempDoc.setDataLibrary(state.materialXStdLib);
65
-
66
- // Initialize basic lighting with default light rig
67
- const defaultLightRigXml = `<?xml version="1.0"?>
68
- <materialx version="1.39">
69
- <!-- Default directional light -->
70
- <directional_light name="default_light" type="lightshader">
71
- <input name="direction" type="vector3" value="0.0, -1.0, -0.5" />
72
- <input name="color" type="color3" value="1.0, 1.0, 1.0" />
73
- <input name="intensity" type="float" value="1.0" />
74
- </directional_light>
75
- </materialx>`;
76
-
77
- // This prewarms the shader generation context to have all light types
78
- await registerLights(state.materialXModule, [], state.materialXGenContext);
79
-
80
- if (debug) console.log("MaterialX generator initialized successfully");
81
- } catch (error) {
82
- console.error("Failed to load MaterialX module:", error);
83
- throw error;
84
- }
85
- })();
86
- }
87
-
88
- // MaterialX Environment Manager - handles lighting and environment setup
89
- export class MaterialXEnvironment {
90
- private lights: any[] = [];
91
- private lightData: any = null;
92
- private radianceTexture: any = null;
93
- private irradianceTexture: any = null;
94
- private context: Context | null = null;
95
- private initialized: boolean = false;
96
-
97
- constructor() {
98
- if (debug) console.log("MaterialX Environment created");
99
- }
100
-
101
- setContext(context: Context) {
102
- this.context = context;
103
- }
104
-
105
- /*
106
- // Initialize MaterialX lighting system based on the reference implementation
107
- async initializeLighting(lightRigXml: string, renderer?: any, radianceTexture?: any, irradianceTexture?: any): Promise<void> {
108
- if (!materialXModule || !materialXGenContext) {
109
- console.warn("MaterialX module not initialized, skipping lighting setup");
110
- return;
111
- }
112
-
113
- registerLights(materialXModule, this.lights, materialXGenContext);
114
- }
115
- */
116
-
117
- // Initialize with Needle Engine context
118
- async initializeFromContext(): Promise<void> {
119
- if (!this.context) {
120
- console.warn("No Needle context available for MaterialX environment initialization");
121
- return;
122
- }
123
-
124
- // Prevent multiple initializations
125
- if (this.initialized) {
126
- if (debug) console.log("MaterialX environment already initialized, skipping");
127
- return;
128
- }
129
-
130
- // Clean up previous textures if they exist
131
- if (this.radianceTexture) {
132
- if (debug) console.log("Disposing previous radiance texture");
133
- this.radianceTexture.dispose();
134
- this.radianceTexture = null;
135
- }
136
- if (this.irradianceTexture) {
137
- if (debug) console.log("Disposing previous irradiance texture");
138
- this.irradianceTexture.dispose();
139
- this.irradianceTexture = null;
140
- }
141
-
142
- // Get renderer from context
143
- const renderer = this.context.renderer;
144
-
145
- // TODO remove this delay; we should wait for the scene lighting to be ready
146
- // and then update the uniforms
147
- let envMap = this.context.scene.environment;
148
- while (!envMap) {
149
- await delay(200);
150
- envMap = this.context.scene.environment;
151
- }
152
- var pmrem = new PMREMGenerator(renderer);
153
- const target = pmrem.fromEquirectangular(envMap);
154
-
155
- const radianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 0.0, 1024, 512, target.height);
156
- const irradianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 1.0, 32, 16, target.height);
157
-
158
- this.radianceTexture = radianceRenderTarget.texture;
159
- this.irradianceTexture = irradianceRenderTarget.texture;
160
-
161
- // Clean up PMREM generator and its render target
162
- target.dispose();
163
- pmrem.dispose();
164
-
165
- if (debug) {
166
- console.log({ radiance: this.radianceTexture, irradiance: this.irradianceTexture });
167
- // Show both of them on cubes in the scene
168
- const unlitMat = new MeshBasicMaterial();
169
- const radianceMat = unlitMat.clone();
170
- radianceMat.map = this.radianceTexture;
171
- const radianceCube = ObjectUtils.createPrimitive("Cube", { material: radianceMat });
172
- const irradianceMat = unlitMat.clone();
173
- irradianceMat.map = this.irradianceTexture;
174
- const irradianceCube = ObjectUtils.createPrimitive("Cube", { material: irradianceMat });
175
- this.context.scene.add(radianceCube);
176
- this.context.scene.add(irradianceCube);
177
- radianceCube.position.set(2, 0, 0);
178
- radianceCube.scale.y = 0.00001;
179
- irradianceCube.position.set(-2, 0, 0);
180
- irradianceCube.scale.y = 0.00001;
181
- // await this.initializeLighting(defaultLightRigXml, renderer);
182
- console.log("MaterialX environment initialized from Needle context", this, this.context.scene);
183
- }
184
-
185
- // Find lights in scene
186
- let lights = new Array<Light>();
187
- this.context.scene.traverse((object: Object3D) => {
188
- if ((object as Light).isLight) lights.push(object as Light);
189
- });
190
-
191
- this.lightData = await registerLights(state.materialXModule, lights, state.materialXGenContext);
192
-
193
- // Mark as initialized
194
- this.initialized = true;
195
- }
196
-
197
- // getLights() { return this.lights; }
198
- getLightData() { return this.lightData; }
199
- getRadianceTexture() { return this.radianceTexture; }
200
- getIrradianceTexture() { return this.irradianceTexture; }
201
-
202
- setRadianceTexture(texture: any) { this.radianceTexture = texture; }
203
- setIrradianceTexture(texture: any) { this.irradianceTexture = texture; }
204
-
205
- // Reset the environment to allow re-initialization
206
- reset() {
207
- if (debug) console.log("Resetting MaterialX environment");
208
- if (this.radianceTexture) {
209
- this.radianceTexture.dispose();
210
- this.radianceTexture = null;
211
- }
212
- if (this.irradianceTexture) {
213
- this.irradianceTexture.dispose();
214
- this.irradianceTexture = null;
215
- }
216
- this.initialized = false;
217
- // this.lights = [];
218
- this.lightData = null;
219
- }
220
- }
1
+ import { Context, delay, isDevEnvironment, ObjectUtils } from "@needle-tools/engine";
2
+ import MaterialX from "../bin/JsMaterialXGenShader.js";
3
+ import { debug } from "./utils.js";
4
+ import { renderPMREMToEquirect } from "./textureHelper.js";
5
+ import { Light, MeshBasicMaterial, Object3D, PMREMGenerator } from "three";
6
+ import { registerLights } from "./helper.js";
7
+
8
+ // @ts-ignore
9
+ import coreWasmUrl from "../bin/JsMaterialXCore.wasm?url";
10
+ // @ts-ignore
11
+ import shaderWasmUrl from "../bin/JsMaterialXGenShader.wasm?url";
12
+ // @ts-ignore
13
+ import shaderDataUrl from "../bin/JsMaterialXGenShader.data?url&inline";
14
+
15
+
16
+ // Configure MaterialX with the correct path for its data files
17
+ const materialXConfig = {
18
+ locateFile: (path: string, scriptDirectory: string) => {
19
+ if (debug) console.debug("MaterialX locateFile called:", { path, scriptDirectory });
20
+
21
+ // Return the correct path for MaterialX data files
22
+ if (path.endsWith('.data') || path.endsWith('.wasm')) {
23
+ if(path.includes("JsMaterialXCore.wasm")) {
24
+ return coreWasmUrl; // Use the URL for the core WASM file
25
+ }
26
+ else if (path.includes("JsMaterialXGenShader.wasm")) {
27
+ return shaderWasmUrl; // Use the URL for the shader WASM file
28
+ }
29
+ else if (path.includes("JsMaterialXGenShader.data")) {
30
+ return shaderDataUrl; // Use the URL for the shader data file
31
+ }
32
+ // For Vite dev server, we need to use the correct module path
33
+ // const url = new URL(`../bin/${path}?url`, import.meta.url);
34
+ // const correctPath = url.href;
35
+ // if (debug) console.debug("MaterialX locateFile resolved:", correctPath, { url, meta_url: import.meta.url });
36
+ // return correctPath;
37
+ }
38
+ return scriptDirectory + path;
39
+ },
40
+ // Add buffer allocation to handle the data file properly
41
+ wasmBinary: null,
42
+ wasmMemory: null
43
+ };
44
+
45
+ // Global MaterialX module instance - initialized lazily
46
+ export const state = new class {
47
+ materialXModule: any = null;
48
+ materialXGenerator: any = null;
49
+ materialXGenContext: any = null;
50
+ materialXStdLib: any = null;
51
+ materialXInitPromise: Promise<void> | null = null;
52
+
53
+ // Global MaterialX environment instance
54
+ private _materialXEnvironment: MaterialXEnvironment | null = null;
55
+ get materialXEnvironment() {
56
+ this._materialXEnvironment ??= new MaterialXEnvironment();
57
+ return this._materialXEnvironment;
58
+ }
59
+ }
60
+
61
+
62
+ // Initialize MaterialX WASM module lazily
63
+ export async function initializeMaterialX(): Promise<void> {
64
+ if (state.materialXInitPromise) {
65
+ return state.materialXInitPromise;
66
+ }
67
+ return state.materialXInitPromise = (async () => {
68
+ if (state.materialXModule) return; // Already initialized
69
+ if (debug) console.log("Initializing MaterialX WASM module...");
70
+ try {
71
+ const module = await MaterialX(materialXConfig);
72
+ if (debug) console.log("MaterialXLoader module loaded", module);
73
+ state.materialXModule = module;
74
+
75
+ // Initialize shader generator and context
76
+ state.materialXGenerator = module.EsslShaderGenerator.create();
77
+ state.materialXGenContext = new module.GenContext(state.materialXGenerator);
78
+
79
+ // Load standard libraries
80
+ const tempDoc = module.createDocument();
81
+ state.materialXStdLib = module.loadStandardLibraries(state.materialXGenContext);
82
+ tempDoc.setDataLibrary(state.materialXStdLib);
83
+
84
+ // Initialize basic lighting with default light rig
85
+ const defaultLightRigXml = `<?xml version="1.0"?>
86
+ <materialx version="1.39">
87
+ <!-- Default directional light -->
88
+ <directional_light name="default_light" type="lightshader">
89
+ <input name="direction" type="vector3" value="0.0, -1.0, -0.5" />
90
+ <input name="color" type="color3" value="1.0, 1.0, 1.0" />
91
+ <input name="intensity" type="float" value="1.0" />
92
+ </directional_light>
93
+ </materialx>`;
94
+
95
+ // This prewarms the shader generation context to have all light types
96
+ await registerLights(state.materialXModule, [], state.materialXGenContext);
97
+
98
+ if (debug) console.log("MaterialX generator initialized successfully");
99
+ } catch (error) {
100
+ console.error("Failed to load MaterialX module:", error);
101
+ throw error;
102
+ }
103
+ })();
104
+ }
105
+
106
+ // MaterialX Environment Manager - handles lighting and environment setup
107
+ export class MaterialXEnvironment {
108
+ private lights: any[] = [];
109
+ private lightData: any = null;
110
+ private radianceTexture: any = null;
111
+ private irradianceTexture: any = null;
112
+ private context: Context | null = null;
113
+ private initialized: boolean = false;
114
+
115
+ constructor() {
116
+ if (debug) console.log("MaterialX Environment created");
117
+ }
118
+
119
+ setContext(context: Context) {
120
+ this.context = context;
121
+ }
122
+
123
+ /*
124
+ // Initialize MaterialX lighting system based on the reference implementation
125
+ async initializeLighting(lightRigXml: string, renderer?: any, radianceTexture?: any, irradianceTexture?: any): Promise<void> {
126
+ if (!materialXModule || !materialXGenContext) {
127
+ console.warn("MaterialX module not initialized, skipping lighting setup");
128
+ return;
129
+ }
130
+
131
+ registerLights(materialXModule, this.lights, materialXGenContext);
132
+ }
133
+ */
134
+
135
+ // Initialize with Needle Engine context
136
+ async initializeFromContext(): Promise<void> {
137
+ if (!this.context) {
138
+ console.warn("No Needle context available for MaterialX environment initialization");
139
+ return;
140
+ }
141
+
142
+ // Prevent multiple initializations
143
+ if (this.initialized) {
144
+ if (debug) console.log("MaterialX environment already initialized, skipping");
145
+ return;
146
+ }
147
+
148
+ // Clean up previous textures if they exist
149
+ if (this.radianceTexture) {
150
+ if (debug) console.log("Disposing previous radiance texture");
151
+ this.radianceTexture.dispose();
152
+ this.radianceTexture = null;
153
+ }
154
+ if (this.irradianceTexture) {
155
+ if (debug) console.log("Disposing previous irradiance texture");
156
+ this.irradianceTexture.dispose();
157
+ this.irradianceTexture = null;
158
+ }
159
+
160
+ // Get renderer from context
161
+ const renderer = this.context.renderer;
162
+
163
+ // TODO remove this delay; we should wait for the scene lighting to be ready
164
+ // and then update the uniforms
165
+ let envMap = this.context.scene.environment;
166
+ while (!envMap) {
167
+ await delay(200);
168
+ envMap = this.context.scene.environment;
169
+ }
170
+ var pmrem = new PMREMGenerator(renderer);
171
+ const target = pmrem.fromEquirectangular(envMap);
172
+
173
+ const radianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 0.0, 1024, 512, target.height);
174
+ const irradianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 1.0, 32, 16, target.height);
175
+
176
+ this.radianceTexture = radianceRenderTarget.texture;
177
+ this.irradianceTexture = irradianceRenderTarget.texture;
178
+
179
+ // Clean up PMREM generator and its render target
180
+ target.dispose();
181
+ pmrem.dispose();
182
+
183
+ if (debug) {
184
+ console.log({ radiance: this.radianceTexture, irradiance: this.irradianceTexture });
185
+ // Show both of them on cubes in the scene
186
+ const unlitMat = new MeshBasicMaterial();
187
+ const radianceMat = unlitMat.clone();
188
+ radianceMat.map = this.radianceTexture;
189
+ const radianceCube = ObjectUtils.createPrimitive("Cube", { material: radianceMat });
190
+ const irradianceMat = unlitMat.clone();
191
+ irradianceMat.map = this.irradianceTexture;
192
+ const irradianceCube = ObjectUtils.createPrimitive("Cube", { material: irradianceMat });
193
+ this.context.scene.add(radianceCube);
194
+ this.context.scene.add(irradianceCube);
195
+ radianceCube.position.set(2, 0, 0);
196
+ radianceCube.scale.y = 0.00001;
197
+ irradianceCube.position.set(-2, 0, 0);
198
+ irradianceCube.scale.y = 0.00001;
199
+ // await this.initializeLighting(defaultLightRigXml, renderer);
200
+ console.log("MaterialX environment initialized from Needle context", this, this.context.scene);
201
+ }
202
+
203
+ // Find lights in scene
204
+ let lights = new Array<Light>();
205
+ this.context.scene.traverse((object: Object3D) => {
206
+ if ((object as Light).isLight) lights.push(object as Light);
207
+ });
208
+
209
+ this.lightData = await registerLights(state.materialXModule, lights, state.materialXGenContext);
210
+
211
+ // Mark as initialized
212
+ this.initialized = true;
213
+ }
214
+
215
+ // getLights() { return this.lights; }
216
+ getLightData() { return this.lightData; }
217
+ getRadianceTexture() { return this.radianceTexture; }
218
+ getIrradianceTexture() { return this.irradianceTexture; }
219
+
220
+ setRadianceTexture(texture: any) { this.radianceTexture = texture; }
221
+ setIrradianceTexture(texture: any) { this.irradianceTexture = texture; }
222
+
223
+ // Reset the environment to allow re-initialization
224
+ reset() {
225
+ if (debug) console.log("Resetting MaterialX environment");
226
+ if (this.radianceTexture) {
227
+ this.radianceTexture.dispose();
228
+ this.radianceTexture = null;
229
+ }
230
+ if (this.irradianceTexture) {
231
+ this.irradianceTexture.dispose();
232
+ this.irradianceTexture = null;
233
+ }
234
+ this.initialized = false;
235
+ // this.lights = [];
236
+ this.lightData = null;
237
+ }
238
+ }