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