@needle-tools/materialx 1.0.1-next.c315a2f → 1.0.1-next.df0e959
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/codegen/register_types.ts +0 -2
- package/package.json +1 -1
- package/src/helper.js +490 -0
- package/src/loader/loader.needle.ts +33 -22
- package/src/loader/loader.three.ts +386 -106
- package/src/materialx.ts +91 -124
- package/src/textureHelper.ts +6 -6
- package/src/utils.ts +4 -39
- package/src/materialx.helper.ts +0 -477
- package/src/materialx.material.ts +0 -216
- package/src/materialx.types.d.ts +0 -50
package/src/materialx.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { Context, delay, isDevEnvironment, ObjectUtils, GameObject
|
|
2
|
-
import type { MaterialX as MX } from "./materialx.types.js";
|
|
1
|
+
import { Context, delay, isDevEnvironment, ObjectUtils, GameObject } from "@needle-tools/engine";
|
|
3
2
|
import MaterialX from "../bin/JsMaterialXGenShader.js";
|
|
4
3
|
import { debug } from "./utils.js";
|
|
5
4
|
import { renderPMREMToEquirect } from "./textureHelper.js";
|
|
6
|
-
import { Light,
|
|
7
|
-
import { registerLights
|
|
8
|
-
import type { MaterialXMaterial } from "./materialx.material.js";
|
|
5
|
+
import { Light, MeshBasicMaterial, Object3D, PMREMGenerator, Texture } from "three";
|
|
6
|
+
import { registerLights } from "./helper.js";
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
export const state = new class {
|
|
12
|
-
materialXModule:
|
|
10
|
+
materialXModule: typeof MaterialX | null = null;
|
|
13
11
|
materialXGenerator: any = null;
|
|
14
12
|
materialXGenContext: any = null;
|
|
15
13
|
materialXStdLib: any = null;
|
|
@@ -61,7 +59,7 @@ export async function ready(): Promise<void> {
|
|
|
61
59
|
},
|
|
62
60
|
});
|
|
63
61
|
if (debug) console.log("[MaterialX] module loaded", module);
|
|
64
|
-
state.materialXModule = module
|
|
62
|
+
state.materialXModule = module;
|
|
65
63
|
|
|
66
64
|
// Initialize shader generator and context
|
|
67
65
|
state.materialXGenerator = module.EsslShaderGenerator.create();
|
|
@@ -73,6 +71,7 @@ export async function ready(): Promise<void> {
|
|
|
73
71
|
tempDoc.setDataLibrary(state.materialXStdLib);
|
|
74
72
|
|
|
75
73
|
// TODO ShaderInterfaceType.SHADER_INTERFACE_REDUCED would be better, but doesn't actually seem to be supported in the MaterialX javascript bindings
|
|
74
|
+
const options = state.materialXGenContext.getOptions();
|
|
76
75
|
state.materialXGenContext.getOptions().shaderInterfaceType = state.materialXModule.ShaderInterfaceType.SHADER_INTERFACE_COMPLETE;
|
|
77
76
|
|
|
78
77
|
// SPECULAR_ENVIRONMENT_NONE: Do not use specular environment maps.
|
|
@@ -96,7 +95,7 @@ export async function ready(): Promise<void> {
|
|
|
96
95
|
state.materialXGenContext.getOptions().hwMaxActiveLightSources = 4;
|
|
97
96
|
|
|
98
97
|
// This prewarms the shader generation context to have all light types
|
|
99
|
-
await registerLights(state.materialXModule, state.materialXGenContext);
|
|
98
|
+
await registerLights(state.materialXModule, [], state.materialXGenContext);
|
|
100
99
|
|
|
101
100
|
if (debug) console.log("[MaterialX] generator initialized successfully");
|
|
102
101
|
} catch (error) {
|
|
@@ -106,156 +105,124 @@ export async function ready(): Promise<void> {
|
|
|
106
105
|
})();
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
type EnvironmentTextureSet = {
|
|
110
|
-
radianceTexture: Texture | null;
|
|
111
|
-
irradianceTexture: Texture | null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
108
|
// MaterialX Environment Manager - handles lighting and environment setup
|
|
115
109
|
export class MaterialXEnvironment {
|
|
116
110
|
private _context: Context | null = null;
|
|
117
|
-
private _lights: Array<Light> = [];
|
|
118
111
|
private _lightData: any = null;
|
|
119
112
|
private _lightCount: number = 0;
|
|
113
|
+
private _radianceTexture: Texture | null = null;
|
|
114
|
+
private _irradianceTexture: Texture | null = null;
|
|
120
115
|
private _initializePromise: Promise<boolean> | null = null;
|
|
121
116
|
|
|
122
|
-
private _unsubscribehook: (() => void) | null = null;
|
|
123
|
-
|
|
124
117
|
constructor() {
|
|
125
118
|
if (debug) console.log("[MaterialX] Environment created");
|
|
126
119
|
}
|
|
127
120
|
|
|
128
121
|
// Initialize with Needle Engine context
|
|
129
|
-
async
|
|
122
|
+
async initializeFromContext(context: Context): Promise<boolean> {
|
|
123
|
+
|
|
124
|
+
// Prevent multiple initializations
|
|
130
125
|
if (this._initializePromise) {
|
|
126
|
+
if (debug) console.log("[MaterialX] environment already initialized, skipping");
|
|
131
127
|
return this._initializePromise;
|
|
132
128
|
}
|
|
133
|
-
return this._initializePromise = this._initialize(context);
|
|
134
|
-
}
|
|
135
129
|
|
|
136
|
-
|
|
137
|
-
get lightCount() { return this._lightCount || 0; }
|
|
138
|
-
getTextures(material: MaterialXMaterial) {
|
|
139
|
-
if (material.envMap) {
|
|
140
|
-
// If the material has its own envMap, we don't use the irradiance texture
|
|
141
|
-
return this._getTextures(material.envMap);
|
|
142
|
-
}
|
|
143
|
-
return this._getTextures(this._context?.scene.environment);
|
|
130
|
+
return this._initializePromise = this._initialize(context);
|
|
144
131
|
}
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
getLightData() { return this._lightData; }
|
|
134
|
+
getLightCount() { return this._lightCount; }
|
|
135
|
+
|
|
136
|
+
setRadianceTexture(texture: Texture) { this._radianceTexture = texture; }
|
|
137
|
+
getRadianceTexture() { return this._radianceTexture; }
|
|
148
138
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
this._context = context;
|
|
152
|
-
this._pmremGenerator = new PMREMGenerator(context.renderer);
|
|
153
|
-
|
|
154
|
-
this._unsubscribehook?.();
|
|
155
|
-
this._unsubscribehook = onBeforeRender(() => {
|
|
156
|
-
this.updateLighting(false);
|
|
157
|
-
this._getTextures(context.scene.environment);
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
// TODO remove this delay; we should wait for the scene lighting to be ready
|
|
161
|
-
// and then update the uniforms
|
|
162
|
-
while (!context.scene.environment) {
|
|
163
|
-
await delay(5);
|
|
164
|
-
}
|
|
165
|
-
this._getTextures(context.scene.environment);
|
|
166
|
-
|
|
167
|
-
// if (debug) {
|
|
168
|
-
// console.log({ radiance: this._radianceTexture, irradiance: this._irradianceTexture });
|
|
169
|
-
// // Show both of them on cubes in the scene
|
|
170
|
-
// const unlitMat = new MeshBasicMaterial();
|
|
171
|
-
// unlitMat.side = 2;
|
|
172
|
-
// const radianceMat = unlitMat.clone();
|
|
173
|
-
// radianceMat.map = this._radianceTexture;
|
|
174
|
-
// const radianceCube = ObjectUtils.createPrimitive("Quad", { material: radianceMat });
|
|
175
|
-
// const irradianceMat = unlitMat.clone();
|
|
176
|
-
// irradianceMat.map = this._irradianceTexture;
|
|
177
|
-
// const irradianceCube = ObjectUtils.createPrimitive("Quad", { material: irradianceMat });
|
|
178
|
-
// context.scene.add(radianceCube);
|
|
179
|
-
// context.scene.add(irradianceCube);
|
|
180
|
-
// radianceCube.position.set(2, 0, 0);
|
|
181
|
-
// irradianceCube.position.set(-2, 0, 0);
|
|
182
|
-
// console.log("[MaterialX] environment initialized from Needle context", this, this._context.scene);
|
|
183
|
-
// }
|
|
184
|
-
|
|
185
|
-
this.updateLighting(true);
|
|
186
|
-
|
|
187
|
-
// Mark as initialized
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
139
|
+
getIrradianceTexture() { return this._irradianceTexture; }
|
|
140
|
+
setIrradianceTexture(texture: Texture) { this._irradianceTexture = texture; }
|
|
190
141
|
|
|
191
142
|
// Reset the environment to allow re-initialization
|
|
192
143
|
reset() {
|
|
193
144
|
if (debug) console.log("[MaterialX] Resetting environment");
|
|
145
|
+
if (this._radianceTexture) {
|
|
146
|
+
this._radianceTexture.dispose();
|
|
147
|
+
this._radianceTexture = null;
|
|
148
|
+
}
|
|
149
|
+
if (this._irradianceTexture) {
|
|
150
|
+
this._irradianceTexture.dispose();
|
|
151
|
+
this._irradianceTexture = null;
|
|
152
|
+
}
|
|
194
153
|
this._initializePromise = null;
|
|
195
|
-
this.
|
|
154
|
+
// this.lights = [];
|
|
196
155
|
this._lightData = null;
|
|
197
|
-
this._lightCount = 0;
|
|
198
|
-
this._pmremGenerator?.dispose();
|
|
199
|
-
this._pmremGenerator = null;
|
|
200
|
-
for(const textureSet of this._texturesCache.values()) {
|
|
201
|
-
textureSet.radianceTexture?.dispose();
|
|
202
|
-
textureSet.irradianceTexture?.dispose();
|
|
203
|
-
}
|
|
204
|
-
this._texturesCache.clear();
|
|
205
|
-
|
|
206
|
-
this._unsubscribehook?.();
|
|
207
|
-
this._unsubscribehook = null;
|
|
208
156
|
}
|
|
209
157
|
|
|
210
|
-
private
|
|
211
|
-
radianceTexture: Texture | null,
|
|
212
|
-
irradianceTexture: Texture | null
|
|
213
|
-
} {
|
|
158
|
+
private _initialize: (context: Context) => Promise<boolean> = async (context: Context) => {
|
|
214
159
|
|
|
215
|
-
|
|
216
|
-
if (res) {
|
|
217
|
-
return res;
|
|
218
|
-
}
|
|
160
|
+
this._context = context;
|
|
219
161
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const irradianceRenderTarget = renderPMREMToEquirect(this._context.renderer, target.texture, 1.0, 32, 16, target.height);
|
|
226
|
-
target.dispose();
|
|
227
|
-
res = {
|
|
228
|
-
radianceTexture: radianceRenderTarget.texture,
|
|
229
|
-
irradianceTexture: irradianceRenderTarget.texture
|
|
230
|
-
}
|
|
162
|
+
// Clean up previous textures if they exist
|
|
163
|
+
if (this._radianceTexture) {
|
|
164
|
+
if (debug) console.log("[MaterialX] Disposing previous radiance texture");
|
|
165
|
+
this._radianceTexture.dispose();
|
|
166
|
+
this._radianceTexture = null;
|
|
231
167
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
168
|
+
if (this._irradianceTexture) {
|
|
169
|
+
if (debug) console.log("[MaterialX] Disposing previous irradiance texture");
|
|
170
|
+
this._irradianceTexture.dispose();
|
|
171
|
+
this._irradianceTexture = null;
|
|
237
172
|
}
|
|
238
|
-
this._texturesCache.set(texture || null, res);
|
|
239
|
-
return res;
|
|
240
|
-
}
|
|
241
173
|
|
|
242
|
-
|
|
243
|
-
|
|
174
|
+
// Get renderer from context
|
|
175
|
+
const renderer = this._context.renderer;
|
|
244
176
|
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
});
|
|
252
|
-
this._lights = lights;
|
|
177
|
+
// TODO remove this delay; we should wait for the scene lighting to be ready
|
|
178
|
+
// and then update the uniforms
|
|
179
|
+
let envMap = this._context.scene.environment;
|
|
180
|
+
while (!envMap) {
|
|
181
|
+
await delay(200);
|
|
182
|
+
envMap = this._context.scene.environment;
|
|
253
183
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
184
|
+
var pmrem = new PMREMGenerator(renderer);
|
|
185
|
+
const target = pmrem.fromEquirectangular(envMap);
|
|
186
|
+
|
|
187
|
+
const radianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 0.0, 1024, 512, target.height);
|
|
188
|
+
const irradianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 1.0, 32, 16, target.height);
|
|
189
|
+
|
|
190
|
+
this._radianceTexture = radianceRenderTarget.texture;
|
|
191
|
+
this._irradianceTexture = irradianceRenderTarget.texture;
|
|
192
|
+
|
|
193
|
+
// Clean up PMREM generator and its render target
|
|
194
|
+
target.dispose();
|
|
195
|
+
pmrem.dispose();
|
|
196
|
+
|
|
197
|
+
if (debug) {
|
|
198
|
+
console.log({ radiance: this._radianceTexture, irradiance: this._irradianceTexture });
|
|
199
|
+
// Show both of them on cubes in the scene
|
|
200
|
+
const unlitMat = new MeshBasicMaterial();
|
|
201
|
+
unlitMat.side = 2;
|
|
202
|
+
const radianceMat = unlitMat.clone();
|
|
203
|
+
radianceMat.map = this._radianceTexture;
|
|
204
|
+
const radianceCube = ObjectUtils.createPrimitive("Quad", { material: radianceMat });
|
|
205
|
+
const irradianceMat = unlitMat.clone();
|
|
206
|
+
irradianceMat.map = this._irradianceTexture;
|
|
207
|
+
const irradianceCube = ObjectUtils.createPrimitive("Quad", { material: irradianceMat });
|
|
208
|
+
this._context.scene.add(radianceCube);
|
|
209
|
+
this._context.scene.add(irradianceCube);
|
|
210
|
+
radianceCube.position.set(2, 0, 0);
|
|
211
|
+
irradianceCube.position.set(-2, 0, 0);
|
|
212
|
+
// await this.initializeLighting(defaultLightRigXml, renderer);
|
|
213
|
+
console.log("[MaterialX] environment initialized from Needle context", this, this._context.scene);
|
|
259
214
|
}
|
|
215
|
+
|
|
216
|
+
// Find lights in scene
|
|
217
|
+
let lights = new Array<Light>();
|
|
218
|
+
this._context.scene.traverse((object: Object3D) => {
|
|
219
|
+
if ((object as Light).isLight && GameObject.isActiveInHierarchy(object))
|
|
220
|
+
lights.push(object as Light);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const { lightData, lightCount } = await registerLights(state.materialXModule, lights, state.materialXGenContext);
|
|
224
|
+
this._lightData = lightData;
|
|
225
|
+
this._lightCount = lightCount;
|
|
226
|
+
return true;
|
|
260
227
|
}
|
|
261
228
|
}
|
package/src/textureHelper.ts
CHANGED
|
@@ -40,7 +40,7 @@ export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Tex
|
|
|
40
40
|
} else {
|
|
41
41
|
imageHeight = 256; // Final fallback
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
const maxMip = Math.log2(imageHeight) - 2;
|
|
45
45
|
const cubeUVHeight = imageHeight;
|
|
46
46
|
const cubeUVWidth = 3 * Math.max(Math.pow(2, maxMip), 7 * 16);
|
|
@@ -129,14 +129,14 @@ export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Tex
|
|
|
129
129
|
const currentAutoClear = renderer.autoClear;
|
|
130
130
|
const currentXrEnabled = renderer.xr.enabled;
|
|
131
131
|
const currentShadowMapEnabled = renderer.shadowMap.enabled;
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
renderTarget.texture.generateMipmaps = true;
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
try {
|
|
136
136
|
// Disable XR and shadow mapping during our render to avoid interference
|
|
137
137
|
renderer.xr.enabled = false;
|
|
138
138
|
renderer.shadowMap.enabled = false;
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
// Render to our target
|
|
141
141
|
renderer.autoClear = true;
|
|
142
142
|
renderer.setRenderTarget(renderTarget);
|
|
@@ -148,7 +148,7 @@ export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Tex
|
|
|
148
148
|
renderer.autoClear = currentAutoClear;
|
|
149
149
|
renderer.xr.enabled = currentXrEnabled;
|
|
150
150
|
renderer.shadowMap.enabled = currentShadowMapEnabled;
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
// Clean up temporary objects
|
|
153
153
|
geometry.dispose();
|
|
154
154
|
material.dispose();
|
|
@@ -159,7 +159,7 @@ export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Tex
|
|
|
159
159
|
renderTarget.texture.mapping = EquirectangularReflectionMapping;
|
|
160
160
|
|
|
161
161
|
// Log mipmap infos
|
|
162
|
-
if (debug) console.log('
|
|
162
|
+
if (debug) console.log('PMREM to Equirect Render Target:', {
|
|
163
163
|
width: renderTarget.width,
|
|
164
164
|
height: renderTarget.height,
|
|
165
165
|
mipmaps: renderTarget.texture.mipmaps?.length,
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { Context, getParam } from "@needle-tools/engine";
|
|
|
2
2
|
import { Mesh } from "three";
|
|
3
3
|
|
|
4
4
|
export const debug = getParam("debugmaterialx");
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* =====================================
|
|
@@ -30,44 +33,6 @@ const patchWebGL2 = () => {
|
|
|
30
33
|
}
|
|
31
34
|
return uniform4fv.call(this, location, v);
|
|
32
35
|
};
|
|
33
|
-
|
|
34
|
-
const uniform3fv = WebGL2RenderingContext.prototype.uniform3fv;
|
|
35
|
-
WebGL2RenderingContext.prototype.uniform3fv = function (location: WebGLUniformLocation | null, v: Float32Array | number[]) {
|
|
36
|
-
if (location) {
|
|
37
|
-
const uniformName = programAndNameToUniformLocation.get(location);
|
|
38
|
-
if (true) console.log("Calling uniform3fv", { location, v, name: uniformName?.name });
|
|
39
|
-
}
|
|
40
|
-
return uniform3fv.call(this, location, v);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const uniform3iv = WebGL2RenderingContext.prototype.uniform3iv;
|
|
44
|
-
WebGL2RenderingContext.prototype.uniform3iv = function (location: WebGLUniformLocation | null, v: Int32Array | number[]) {
|
|
45
|
-
if (location) {
|
|
46
|
-
const uniformName = programAndNameToUniformLocation.get(location);
|
|
47
|
-
if (true) console.log("Calling uniform3iv", { location, v, name: uniformName?.name });
|
|
48
|
-
}
|
|
49
|
-
return uniform3iv.call(this, location, v);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const uniform3uiv = WebGL2RenderingContext.prototype.uniform3uiv;
|
|
53
|
-
WebGL2RenderingContext.prototype.uniform3uiv = function (location: WebGLUniformLocation | null, v: Uint32Array | number[]) {
|
|
54
|
-
if (location) {
|
|
55
|
-
const uniformName = programAndNameToUniformLocation.get(location);
|
|
56
|
-
if (true) console.log("Calling uniform3uiv", { location, v, name: uniformName?.name });
|
|
57
|
-
}
|
|
58
|
-
return uniform3uiv.call(this, location, v);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const uniform3f = WebGL2RenderingContext.prototype.uniform3f;
|
|
62
|
-
WebGL2RenderingContext.prototype.uniform3f = function (location: WebGLUniformLocation
|
|
63
|
-
| null, x: number, y: number, z: number) {
|
|
64
|
-
if (location) {
|
|
65
|
-
const uniformName = programAndNameToUniformLocation.get(location);
|
|
66
|
-
if (uniformName?.name !== "diffuse")
|
|
67
|
-
if (true) console.log("Calling uniform3f", { location, x, y, z, name: uniformName?.name });
|
|
68
|
-
}
|
|
69
|
-
return uniform3f.call(this, location, x, y, z);
|
|
70
|
-
};
|
|
71
36
|
};
|
|
72
37
|
// patchWebGL2();
|
|
73
38
|
|