@needle-tools/materialx 1.1.1-next.714bc32 → 1.1.1-next.8e8afe1
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/bin/.gitattributes +4 -0
- package/{index.ts → index.d.ts} +1 -1
- package/index.js +2 -0
- package/{needle.ts → needle.d.ts} +1 -1
- package/needle.js +2 -0
- package/package.json +11 -8
- package/src/index.d.ts +11 -0
- package/src/{index.ts → index.js} +2 -4
- package/src/loader/loader.needle.d.ts +15 -0
- package/src/loader/loader.needle.js +62 -0
- package/src/loader/loader.three.d.ts +61 -0
- package/src/loader/{loader.three.ts → loader.three.js} +92 -82
- package/src/materialx.d.ts +60 -0
- package/src/materialx.helper.d.ts +31 -0
- package/src/{materialx.helper.ts → materialx.helper.js} +97 -63
- package/src/{materialx.ts → materialx.js} +109 -63
- package/src/materialx.material.d.ts +37 -0
- package/src/{materialx.material.ts → materialx.material.js} +70 -35
- package/src/utils.d.ts +17 -0
- package/src/{utils.ts → utils.js} +18 -5
- package/src/utils.texture.d.ts +13 -0
- package/src/{utils.texture.ts → utils.texture.js} +18 -17
- package/src/loader/loader.needle.ts +0 -43
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
//
|
|
5
5
|
import * as THREE from 'three';
|
|
6
6
|
import { debug, debugUpdate } from './utils.js';
|
|
7
|
-
import { MaterialX } from './materialx.types.js';
|
|
8
7
|
|
|
9
8
|
const IMAGE_PROPERTY_SEPARATOR = "_";
|
|
10
9
|
const UADDRESS_MODE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "uaddressmode";
|
|
@@ -13,13 +12,13 @@ const FILTER_TYPE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "filtertype";
|
|
|
13
12
|
const IMAGE_PATH_SEPARATOR = "/";
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
15
|
+
* Initializes the environment texture as MaterialX expects it
|
|
17
16
|
* @param {THREE.Texture} texture
|
|
18
17
|
* @param {Object} capabilities
|
|
19
18
|
* @returns {THREE.Texture}
|
|
20
19
|
*/
|
|
21
20
|
export function prepareEnvTexture(texture, capabilities) {
|
|
22
|
-
let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, texture.format, texture.type);
|
|
21
|
+
let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, /** @type {any} */ (texture.format), texture.type);
|
|
23
22
|
newTexture.wrapS = THREE.RepeatWrapping;
|
|
24
23
|
newTexture.anisotropy = capabilities.getMaxAnisotropy();
|
|
25
24
|
newTexture.minFilter = THREE.LinearMipmapLinearFilter;
|
|
@@ -34,7 +33,7 @@ export function prepareEnvTexture(texture, capabilities) {
|
|
|
34
33
|
* Get Three uniform from MaterialX vector
|
|
35
34
|
* @param {any} value
|
|
36
35
|
* @param {any} dimension
|
|
37
|
-
* @returns {
|
|
36
|
+
* @returns {Array<number>}
|
|
38
37
|
*/
|
|
39
38
|
function fromVector(value, dimension) {
|
|
40
39
|
let outValue;
|
|
@@ -52,8 +51,11 @@ function fromVector(value, dimension) {
|
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
53
|
* Get Three uniform from MaterialX matrix
|
|
54
|
+
* @param {any} matrix
|
|
55
|
+
* @param {number} dimension
|
|
56
|
+
* @returns {Array<number>}
|
|
55
57
|
*/
|
|
56
|
-
function fromMatrix(matrix
|
|
58
|
+
function fromMatrix(matrix, dimension) {
|
|
57
59
|
const vec = new Array(dimension);
|
|
58
60
|
if (matrix) {
|
|
59
61
|
for (let i = 0; i < matrix.numRows(); ++i) {
|
|
@@ -69,19 +71,11 @@ function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"
|
|
|
69
71
|
return vec;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
readonly cacheKey?: string;
|
|
78
|
-
/**
|
|
79
|
-
* Get a texture by path
|
|
80
|
-
* @param {string} path - The path to the texture
|
|
81
|
-
* @return {Promise<THREE.Texture>} - A promise that resolves to the texture
|
|
82
|
-
*/
|
|
83
|
-
readonly getTexture: (path: string) => Promise<THREE.Texture | null | void>;
|
|
84
|
-
}
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {Object} Callbacks
|
|
76
|
+
* @property {string} [cacheKey] - Cache key for the loaders, used to identify and reuse textures
|
|
77
|
+
* @property {(path: string) => Promise<THREE.Texture | null | void>} getTexture - Get a texture by path
|
|
78
|
+
*/
|
|
85
79
|
|
|
86
80
|
const defaultTexture = new THREE.Texture();
|
|
87
81
|
defaultTexture.needsUpdate = true;
|
|
@@ -94,21 +88,28 @@ defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAA
|
|
|
94
88
|
// defaultTexture.magFilter = THREE.NearestFilter;
|
|
95
89
|
// defaultTexture.repeat = new THREE.Vector2(100, 100);
|
|
96
90
|
|
|
97
|
-
|
|
98
91
|
const defaultNormalTexture = new THREE.Texture();
|
|
99
92
|
defaultNormalTexture.needsUpdate = true;
|
|
100
93
|
defaultNormalTexture.image = new Image();
|
|
101
94
|
defaultNormalTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAABJQTFRFgYH4gIH4gYH3gIH3gIH5gID4m94ORAAAADFJREFUeJxjZBBkfMdo9P/BB0aBj/8FGB0ufghgFGT4r8wo+P8rD2Pgo3sMjIz8jAwAMLoN0ZjS5hgAAAAASUVORK5CYII=";
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} key
|
|
98
|
+
* @returns {any}
|
|
99
|
+
*/
|
|
100
|
+
function tryGetFromCache(key) {
|
|
105
101
|
const wasEnabled = THREE.Cache.enabled;
|
|
106
102
|
THREE.Cache.enabled = true;
|
|
107
103
|
const value = THREE.Cache.get(key);
|
|
108
104
|
THREE.Cache.enabled = wasEnabled;
|
|
109
105
|
return value;
|
|
110
106
|
}
|
|
111
|
-
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {string} key
|
|
110
|
+
* @param {any} value
|
|
111
|
+
*/
|
|
112
|
+
function addToCache(key, value) {
|
|
112
113
|
const wasEnabled = THREE.Cache.enabled;
|
|
113
114
|
THREE.Cache.enabled = true;
|
|
114
115
|
THREE.Cache.add(key, value);
|
|
@@ -118,10 +119,17 @@ function addToCache(key: string, value: any): void {
|
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
121
|
* Get Three uniform from MaterialX value
|
|
122
|
+
* @param {any} uniforms
|
|
123
|
+
* @param {string} type
|
|
124
|
+
* @param {any} value
|
|
125
|
+
* @param {string} name
|
|
126
|
+
* @param {Callbacks} loaders
|
|
127
|
+
* @param {string} searchPath
|
|
128
|
+
* @returns {THREE.Uniform}
|
|
121
129
|
*/
|
|
122
|
-
function toThreeUniform(uniforms
|
|
130
|
+
function toThreeUniform(uniforms, type, value, name, loaders, searchPath) {
|
|
123
131
|
|
|
124
|
-
const uniform = new THREE.Uniform
|
|
132
|
+
const uniform = new THREE.Uniform(/** @type {any} */ (null));
|
|
125
133
|
|
|
126
134
|
switch (type) {
|
|
127
135
|
case 'float':
|
|
@@ -130,21 +138,21 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
|
|
|
130
138
|
uniform.value = value;
|
|
131
139
|
break;
|
|
132
140
|
case 'vector2':
|
|
133
|
-
uniform.value = fromVector(value, 2);
|
|
141
|
+
uniform.value = /** @type {any} */ (fromVector(value, 2));
|
|
134
142
|
break;
|
|
135
143
|
case 'vector3':
|
|
136
144
|
case 'color3':
|
|
137
|
-
uniform.value = fromVector(value, 3);
|
|
145
|
+
uniform.value = /** @type {any} */ (fromVector(value, 3));
|
|
138
146
|
break;
|
|
139
147
|
case 'vector4':
|
|
140
148
|
case 'color4':
|
|
141
|
-
uniform.value = fromVector(value, 4);
|
|
149
|
+
uniform.value = /** @type {any} */ (fromVector(value, 4));
|
|
142
150
|
break;
|
|
143
151
|
case 'matrix33':
|
|
144
|
-
uniform.value = fromMatrix(value, 9);
|
|
152
|
+
uniform.value = /** @type {any} */ (fromMatrix(value, 9));
|
|
145
153
|
break;
|
|
146
154
|
case 'matrix44':
|
|
147
|
-
uniform.value = fromMatrix(value, 16);
|
|
155
|
+
uniform.value = /** @type {any} */ (fromMatrix(value, 16));
|
|
148
156
|
break;
|
|
149
157
|
case 'filename':
|
|
150
158
|
if (value) {
|
|
@@ -182,8 +190,8 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
|
|
|
182
190
|
else {
|
|
183
191
|
if (debug) console.log('[MaterialX] Load texture:', texturePath);
|
|
184
192
|
|
|
185
|
-
if (name.toLowerCase().includes("normal")) uniform.value = defaultNormalTexture;
|
|
186
|
-
else uniform.value = defaultTexture;
|
|
193
|
+
if (name.toLowerCase().includes("normal")) uniform.value = /** @type {any} */ (defaultNormalTexture);
|
|
194
|
+
else uniform.value = /** @type {any} */ (defaultTexture);
|
|
187
195
|
const defaultValue = uniform.value;
|
|
188
196
|
// Save the loading promise in the cache
|
|
189
197
|
const promise = loaders.getTexture(texturePath)
|
|
@@ -203,7 +211,7 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
|
|
|
203
211
|
if (checkCache) addToCache(cacheKey, promise);
|
|
204
212
|
|
|
205
213
|
promise?.then(res => {
|
|
206
|
-
if (res) uniform.value = res;
|
|
214
|
+
if (res) uniform.value = /** @type {any} */ (res);
|
|
207
215
|
else console.warn(`[MaterialX] Failed to load texture ${name} '${texturePath}'`);
|
|
208
216
|
});
|
|
209
217
|
}
|
|
@@ -224,12 +232,15 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
|
|
|
224
232
|
return uniform;
|
|
225
233
|
}
|
|
226
234
|
|
|
227
|
-
|
|
235
|
+
/** @type {Map<string, boolean>} */
|
|
236
|
+
const valueTypeWarningMap = new Map();
|
|
228
237
|
|
|
229
238
|
/**
|
|
230
239
|
* Get Three wrapping mode
|
|
240
|
+
* @param {number} mode
|
|
241
|
+
* @returns {THREE.Wrapping}
|
|
231
242
|
*/
|
|
232
|
-
function getWrapping(mode
|
|
243
|
+
function getWrapping(mode) {
|
|
233
244
|
let wrap;
|
|
234
245
|
switch (mode) {
|
|
235
246
|
case 1:
|
|
@@ -248,11 +259,14 @@ function getWrapping(mode: number): THREE.Wrapping {
|
|
|
248
259
|
return wrap;
|
|
249
260
|
}
|
|
250
261
|
|
|
251
|
-
|
|
252
262
|
/**
|
|
253
263
|
* Set Three texture parameters
|
|
264
|
+
* @param {THREE.Texture} texture
|
|
265
|
+
* @param {string} name
|
|
266
|
+
* @param {any} uniforms
|
|
267
|
+
* @param {boolean} [generateMipmaps=true]
|
|
254
268
|
*/
|
|
255
|
-
function setTextureParameters(texture
|
|
269
|
+
function setTextureParameters(texture, name, uniforms, generateMipmaps = true) {
|
|
256
270
|
const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
|
|
257
271
|
const base = name.substring(0, idx) || name;
|
|
258
272
|
|
|
@@ -267,15 +281,16 @@ function setTextureParameters(texture: THREE.Texture, name: string, uniforms: an
|
|
|
267
281
|
}
|
|
268
282
|
|
|
269
283
|
const mxFilterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1;
|
|
270
|
-
let minFilter
|
|
284
|
+
let minFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
|
|
271
285
|
if (mxFilterType === 0) {
|
|
272
|
-
minFilter = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
286
|
+
minFilter = /** @type {any} */ (generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter);
|
|
273
287
|
}
|
|
274
288
|
texture.minFilter = minFilter;
|
|
275
289
|
}
|
|
276
290
|
|
|
277
291
|
/**
|
|
278
292
|
* Return the global light rotation matrix
|
|
293
|
+
* @returns {THREE.Matrix4}
|
|
279
294
|
*/
|
|
280
295
|
export function getLightRotation() {
|
|
281
296
|
return new THREE.Matrix4().makeRotationY(Math.PI / 2);
|
|
@@ -283,11 +298,11 @@ export function getLightRotation() {
|
|
|
283
298
|
|
|
284
299
|
/**
|
|
285
300
|
* Returns all lights nodes in a MaterialX document
|
|
286
|
-
* @param {
|
|
287
|
-
* @returns {Array
|
|
301
|
+
* @param {any} doc
|
|
302
|
+
* @returns {Array<any>}
|
|
288
303
|
*/
|
|
289
|
-
export function findLights(doc
|
|
290
|
-
let lights = new Array
|
|
304
|
+
export function findLights(doc) {
|
|
305
|
+
let lights = new Array();
|
|
291
306
|
for (let node of doc.getNodes()) {
|
|
292
307
|
if (node.getType() === "lightshader")
|
|
293
308
|
lights.push(node);
|
|
@@ -295,14 +310,16 @@ export function findLights(doc: MaterialX.Document) {
|
|
|
295
310
|
return lights;
|
|
296
311
|
}
|
|
297
312
|
|
|
313
|
+
/** @type {Object<string, number>} */
|
|
298
314
|
let lightTypesBound = {};
|
|
299
315
|
|
|
300
316
|
/**
|
|
301
317
|
* Register lights in shader generation context
|
|
302
|
-
* @param {
|
|
303
|
-
* @param {
|
|
318
|
+
* @param {any} mx - MaterialX Module
|
|
319
|
+
* @param {any} genContext - Shader generation context
|
|
320
|
+
* @returns {Promise<void>}
|
|
304
321
|
*/
|
|
305
|
-
export async function registerLights(mx
|
|
322
|
+
export async function registerLights(mx, genContext) {
|
|
306
323
|
lightTypesBound = {};
|
|
307
324
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
308
325
|
mx.HwShaderGenerator.unbindLightShaders(genContext);
|
|
@@ -355,7 +372,11 @@ export async function registerLights(mx: MaterialX.MODULE, genContext: any): Pro
|
|
|
355
372
|
if (debug) console.log("Light types bound in MaterialX context", lightTypesBound);
|
|
356
373
|
}
|
|
357
374
|
|
|
358
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Converts Three.js light type to MaterialX node name
|
|
377
|
+
* @param {string} threeLightType
|
|
378
|
+
* @returns {string}
|
|
379
|
+
*/
|
|
359
380
|
function threeLightTypeToMaterialXNodeName(threeLightType) {
|
|
360
381
|
switch (threeLightType) {
|
|
361
382
|
case 'PointLight':
|
|
@@ -368,23 +389,27 @@ function threeLightTypeToMaterialXNodeName(threeLightType) {
|
|
|
368
389
|
console.warn('MaterialX: Unsupported light type: ' + threeLightType);
|
|
369
390
|
return 'ND_point_light'; // Default to point light
|
|
370
391
|
}
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
export type LightData = {
|
|
374
|
-
type: number, // Light type ID
|
|
375
|
-
position: THREE.Vector3, // Position in world space
|
|
376
|
-
direction: THREE.Vector3, // Direction in world space
|
|
377
|
-
color: THREE.Color, // Color of the light
|
|
378
|
-
intensity: number, // Intensity of the light
|
|
379
|
-
decay_rate: number, // Decay rate for point and spot lights
|
|
380
|
-
inner_angle: number, // Inner angle for spot lights
|
|
381
|
-
outer_angle: number, // Outer angle for spot lights
|
|
382
392
|
}
|
|
383
393
|
|
|
394
|
+
/**
|
|
395
|
+
* @typedef {Object} LightData
|
|
396
|
+
* @property {number} type - Light type ID
|
|
397
|
+
* @property {THREE.Vector3} position - Position in world space
|
|
398
|
+
* @property {THREE.Vector3} direction - Direction in world space
|
|
399
|
+
* @property {THREE.Color} color - Color of the light
|
|
400
|
+
* @property {number} intensity - Intensity of the light
|
|
401
|
+
* @property {number} decay_rate - Decay rate for point and spot lights
|
|
402
|
+
* @property {number} inner_angle - Inner angle for spot lights
|
|
403
|
+
* @property {number} outer_angle - Outer angle for spot lights
|
|
404
|
+
*/
|
|
405
|
+
|
|
384
406
|
/**
|
|
385
407
|
* Update light data for shader uniforms
|
|
408
|
+
* @param {Array<THREE.Light>} lights
|
|
409
|
+
* @param {any} genContext
|
|
410
|
+
* @returns {{ lightData: LightData[], lightCount: number }}
|
|
386
411
|
*/
|
|
387
|
-
export function getLightData(lights
|
|
412
|
+
export function getLightData(lights, genContext) {
|
|
388
413
|
const lightData = new Array();
|
|
389
414
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
390
415
|
|
|
@@ -410,8 +435,8 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
|
|
|
410
435
|
// float cosDir = dot(result.direction, -light.direction);
|
|
411
436
|
// float spotAttenuation = smoothstep(low, high, cosDir);
|
|
412
437
|
|
|
413
|
-
const outerAngleRad =
|
|
414
|
-
const innerAngleRad = outerAngleRad * (1 -
|
|
438
|
+
const outerAngleRad = /** @type {THREE.SpotLight} */ (light).angle;
|
|
439
|
+
const innerAngleRad = outerAngleRad * (1 - /** @type {THREE.SpotLight} */ (light).penumbra);
|
|
415
440
|
const inner_angle = Math.cos(innerAngleRad);
|
|
416
441
|
const outer_angle = Math.cos(outerAngleRad);
|
|
417
442
|
|
|
@@ -422,7 +447,7 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
|
|
|
422
447
|
color: new THREE.Color().fromArray(light.color.toArray()),
|
|
423
448
|
// Luminous efficacy for converting radiant power in watts (W) to luminous flux in lumens (lm) at a wavelength of 555 nm.
|
|
424
449
|
// Also, three.js lights don't have PI scale baked in, but MaterialX does, so we need to divide by PI for point and spot lights.
|
|
425
|
-
intensity: light.intensity * (
|
|
450
|
+
intensity: light.intensity * (/** @type {THREE.PointLight} */ (light).isPointLight ? 683.0 / 3.1415 : /** @type {THREE.SpotLight} */ (light).isSpotLight ? 683.0 / 3.1415 : 1.0),
|
|
426
451
|
decay_rate: 2.0,
|
|
427
452
|
// Approximations for testing – the relevant light has 61.57986...129.4445 as inner/outer spot angle
|
|
428
453
|
inner_angle: inner_angle,
|
|
@@ -455,8 +480,13 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
|
|
|
455
480
|
|
|
456
481
|
/**
|
|
457
482
|
* Get uniform values for a shader
|
|
483
|
+
* @param {any} shaderStage
|
|
484
|
+
* @param {Callbacks} loaders
|
|
485
|
+
* @param {string} searchPath
|
|
486
|
+
* @returns {Object<string, THREE.Uniform>}
|
|
458
487
|
*/
|
|
459
|
-
export function getUniformValues(shaderStage
|
|
488
|
+
export function getUniformValues(shaderStage, loaders, searchPath) {
|
|
489
|
+
/** @type {Object<string, THREE.Uniform>} */
|
|
460
490
|
const threeUniforms = {};
|
|
461
491
|
|
|
462
492
|
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
@@ -479,7 +509,11 @@ export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Ca
|
|
|
479
509
|
return threeUniforms;
|
|
480
510
|
}
|
|
481
511
|
|
|
482
|
-
|
|
512
|
+
/**
|
|
513
|
+
* @param {THREE.ShaderMaterial} material
|
|
514
|
+
* @param {any} shaderStage
|
|
515
|
+
*/
|
|
516
|
+
export function generateMaterialPropertiesForUniforms(material, shaderStage) {
|
|
483
517
|
|
|
484
518
|
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
485
519
|
for (const [blockName, uniforms] of Object.entries(uniformBlocks)) {
|
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
import type { MaterialX as MX } from "./materialx.types.js";
|
|
2
1
|
import MaterialX from "../bin/JsMaterialXGenShader.js";
|
|
3
2
|
import { debug } from "./utils.js";
|
|
4
3
|
import { renderPMREMToEquirect } from "./utils.texture.js";
|
|
5
4
|
import { Light, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry, PMREMGenerator, Scene, Texture, WebGLRenderer } from "three";
|
|
6
|
-
import { registerLights, getLightData
|
|
7
|
-
import type { MaterialXMaterial } from "./materialx.material.js";
|
|
8
|
-
|
|
9
|
-
export type MaterialXContext = {
|
|
10
|
-
getTime?(): number,
|
|
11
|
-
getFrame?(): number,
|
|
12
|
-
}
|
|
13
|
-
|
|
5
|
+
import { registerLights, getLightData } from "./materialx.helper.js";
|
|
14
6
|
|
|
15
7
|
export const state = new class {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
/** @type {import("./materialx.types.js").MaterialX.MODULE | null} */
|
|
9
|
+
materialXModule = null;
|
|
10
|
+
/** @type {any} */
|
|
11
|
+
materialXGenerator = null;
|
|
12
|
+
/** @type {any} */
|
|
13
|
+
materialXGenContext = null;
|
|
14
|
+
/** @type {any} */
|
|
15
|
+
materialXStdLib = null;
|
|
16
|
+
/** @type {Promise<void> | null} */
|
|
17
|
+
materialXInitPromise = null;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
|
|
24
20
|
/**
|
|
25
21
|
* Wait for the MaterialX WASM module to be ready.
|
|
22
|
+
* @returns {Promise<void>}
|
|
26
23
|
*/
|
|
27
|
-
export async function ready()
|
|
24
|
+
export async function ready() {
|
|
28
25
|
if (state.materialXInitPromise) {
|
|
29
26
|
return state.materialXInitPromise;
|
|
30
27
|
}
|
|
@@ -33,18 +30,19 @@ export async function ready(): Promise<void> {
|
|
|
33
30
|
if (debug) console.log("[MaterialX] Initializing WASM module...");
|
|
34
31
|
try {
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
/** @type {Array<string>} */
|
|
34
|
+
const urls = await Promise.all([
|
|
37
35
|
/** @ts-ignore */
|
|
38
|
-
import(`../bin/JsMaterialXCore.wasm?url`).then(m => m.default || m),
|
|
36
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXCore.wasm?url`).then(m => m.default || m),
|
|
39
37
|
/** @ts-ignore */
|
|
40
|
-
import(`../bin/JsMaterialXGenShader.wasm?url`).then(m => m.default || m),
|
|
38
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXGenShader.wasm?url`).then(m => m.default || m),
|
|
41
39
|
/** @ts-ignore */
|
|
42
|
-
import(`../bin/JsMaterialXGenShader.data.txt?url`).then(m => m.default || m),
|
|
40
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXGenShader.data.txt?url`).then(m => m.default || m),
|
|
43
41
|
]);
|
|
44
42
|
const [JsMaterialXCore, JsMaterialXGenShader, JsMaterialXGenShader_data] = urls;
|
|
45
43
|
|
|
46
44
|
const module = await MaterialX({
|
|
47
|
-
locateFile: (
|
|
45
|
+
locateFile: (/** @type {string} */ path, /** @type {string} */ scriptDirectory) => {
|
|
48
46
|
if (debug) console.debug("[MaterialX] locateFile called:", { path, scriptDirectory });
|
|
49
47
|
|
|
50
48
|
if (path.includes("JsMaterialXCore.wasm")) {
|
|
@@ -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 =
|
|
62
|
+
state.materialXModule = /** @type {import("./materialx.types.js").MaterialX.MODULE} */ (module);
|
|
65
63
|
|
|
66
64
|
// Initialize shader generator and context
|
|
67
65
|
state.materialXGenerator = module.EsslShaderGenerator.create();
|
|
@@ -108,45 +106,69 @@ export async function ready(): Promise<void> {
|
|
|
108
106
|
})();
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
109
|
+
/**
|
|
110
|
+
* @typedef {Object} EnvironmentTextureSet
|
|
111
|
+
* @property {Texture | null} radianceTexture
|
|
112
|
+
* @property {Texture | null} irradianceTexture
|
|
113
|
+
*/
|
|
116
114
|
|
|
117
115
|
/**
|
|
118
116
|
* MaterialXEnvironment manages the environment settings for MaterialX materials.
|
|
119
117
|
*/
|
|
120
118
|
export class MaterialXEnvironment {
|
|
121
119
|
|
|
122
|
-
|
|
120
|
+
/**
|
|
121
|
+
* @param {Scene} scene
|
|
122
|
+
* @returns {MaterialXEnvironment | null}
|
|
123
|
+
*/
|
|
124
|
+
static get(scene) {
|
|
123
125
|
return this.getEnvironment(scene);
|
|
124
126
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
|
|
128
|
+
/** @type {WeakMap<Scene, MaterialXEnvironment>} */
|
|
129
|
+
static _environments = new Map();
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {Scene} scene
|
|
133
|
+
* @returns {MaterialXEnvironment}
|
|
134
|
+
*/
|
|
135
|
+
static getEnvironment(scene) {
|
|
127
136
|
if (this._environments.has(scene)) {
|
|
128
|
-
return this._environments.get(scene)
|
|
137
|
+
return /** @type {MaterialXEnvironment} */ (this._environments.get(scene));
|
|
129
138
|
}
|
|
130
139
|
const env = new MaterialXEnvironment(scene);
|
|
131
140
|
this._environments.set(scene, env);
|
|
132
141
|
return env;
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
/** @type {Array<Light>} */
|
|
145
|
+
_lights = [];
|
|
146
|
+
/** @type {import("./materialx.helper.js").LightData[] | null} */
|
|
147
|
+
_lightData = null;
|
|
148
|
+
/** @type {number} */
|
|
149
|
+
_lightCount = 0;
|
|
150
|
+
|
|
151
|
+
/** @type {Promise<boolean> | null} */
|
|
152
|
+
_initializePromise = null;
|
|
153
|
+
/** @type {boolean} */
|
|
154
|
+
_isInitialized = false;
|
|
155
|
+
/** @type {number} */
|
|
156
|
+
_lastUpdateFrame = -1;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {Scene} _scene
|
|
160
|
+
*/
|
|
161
|
+
constructor(_scene) {
|
|
162
|
+
this._scene = _scene;
|
|
145
163
|
if (debug) console.log("[MaterialX] Environment created");
|
|
146
164
|
}
|
|
147
165
|
|
|
148
|
-
|
|
149
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Initialize with Needle Engine context
|
|
168
|
+
* @param {WebGLRenderer} renderer
|
|
169
|
+
* @returns {Promise<boolean>}
|
|
170
|
+
*/
|
|
171
|
+
async initialize(renderer) {
|
|
150
172
|
if (this._initializePromise) {
|
|
151
173
|
return this._initializePromise;
|
|
152
174
|
}
|
|
@@ -154,7 +176,12 @@ export class MaterialXEnvironment {
|
|
|
154
176
|
return this._initializePromise;
|
|
155
177
|
}
|
|
156
178
|
|
|
157
|
-
|
|
179
|
+
/**
|
|
180
|
+
* @param {number} frame
|
|
181
|
+
* @param {Scene} scene
|
|
182
|
+
* @param {WebGLRenderer} renderer
|
|
183
|
+
*/
|
|
184
|
+
update(frame, scene, renderer) {
|
|
158
185
|
if (!this._initializePromise) {
|
|
159
186
|
this.initialize(renderer);
|
|
160
187
|
return;
|
|
@@ -181,13 +208,15 @@ export class MaterialXEnvironment {
|
|
|
181
208
|
const radianceCube = new Mesh(planeGeometry, radianceMat);
|
|
182
209
|
const irradianceMat = unlitMat.clone();
|
|
183
210
|
irradianceMat.map = textures.irradianceTexture;
|
|
184
|
-
const irradianceCube = new Mesh(planeGeometry,
|
|
211
|
+
const irradianceCube = new Mesh(planeGeometry, irradianceMat);
|
|
185
212
|
scene.add(radianceCube);
|
|
186
213
|
scene.add(irradianceCube);
|
|
187
214
|
radianceCube.name = "MaterialXRadianceCube";
|
|
188
|
-
radianceCube.position.set(.
|
|
215
|
+
radianceCube.position.set(.8, 1, .01);
|
|
216
|
+
radianceCube.scale.set(1.5, 1, 1);
|
|
189
217
|
irradianceCube.name = "MaterialXIrradianceCube";
|
|
190
|
-
irradianceCube.position.set(-.
|
|
218
|
+
irradianceCube.position.set(-.8, 1, -.01);
|
|
219
|
+
irradianceCube.scale.set(1.5, 0.98, 1);
|
|
191
220
|
console.log("[MaterialX] environment initialized from Needle context", { textures, radianceCube, irradianceCube });
|
|
192
221
|
}
|
|
193
222
|
}
|
|
@@ -218,7 +247,11 @@ export class MaterialXEnvironment {
|
|
|
218
247
|
return this._lightData;
|
|
219
248
|
}
|
|
220
249
|
get lightCount() { return this._lightCount || 0; }
|
|
221
|
-
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {import("./materialx.material.js").MaterialXMaterial} material
|
|
253
|
+
*/
|
|
254
|
+
getTextures(material) {
|
|
222
255
|
if (material.envMap) {
|
|
223
256
|
// If the material has its own envMap, we don't use the irradiance texture
|
|
224
257
|
return this._getTextures(material.envMap);
|
|
@@ -226,11 +259,18 @@ export class MaterialXEnvironment {
|
|
|
226
259
|
return this._getTextures(this._scene.environment);
|
|
227
260
|
}
|
|
228
261
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
262
|
+
/** @type {PMREMGenerator | null} */
|
|
263
|
+
_pmremGenerator = null;
|
|
264
|
+
/** @type {WebGLRenderer | null} */
|
|
265
|
+
_renderer = null;
|
|
266
|
+
/** @type {Map<Texture | null, EnvironmentTextureSet>} */
|
|
267
|
+
_texturesCache = new Map();
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {WebGLRenderer} renderer
|
|
271
|
+
* @returns {Promise<boolean>}
|
|
272
|
+
*/
|
|
273
|
+
async _initialize(renderer) {
|
|
234
274
|
this._isInitialized = false;
|
|
235
275
|
this._pmremGenerator = new PMREMGenerator(renderer);
|
|
236
276
|
this._renderer = renderer;
|
|
@@ -239,11 +279,13 @@ export class MaterialXEnvironment {
|
|
|
239
279
|
return true;
|
|
240
280
|
}
|
|
241
281
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
282
|
+
/**
|
|
283
|
+
* @param {Texture | null | undefined} texture
|
|
284
|
+
* @returns {{radianceTexture: Texture | null, irradianceTexture: Texture | null}}
|
|
285
|
+
*/
|
|
286
|
+
_getTextures(texture) {
|
|
287
|
+
/** @type {EnvironmentTextureSet | undefined} */
|
|
288
|
+
let res = this._texturesCache.get(texture || null);
|
|
247
289
|
if (res) {
|
|
248
290
|
return res;
|
|
249
291
|
}
|
|
@@ -269,14 +311,18 @@ export class MaterialXEnvironment {
|
|
|
269
311
|
return res;
|
|
270
312
|
}
|
|
271
313
|
|
|
272
|
-
|
|
314
|
+
/**
|
|
315
|
+
* @param {boolean} collectLights
|
|
316
|
+
*/
|
|
317
|
+
updateLighting = (collectLights = false) => {
|
|
273
318
|
if (!this._scene) return;
|
|
274
319
|
// Find lights in scene
|
|
275
320
|
if (collectLights) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
321
|
+
/** @type {Array<Light>} */
|
|
322
|
+
const lights = new Array();
|
|
323
|
+
this._scene.traverse((/** @type {Object3D} */ object) => {
|
|
324
|
+
if ((/** @type {Light} */ (object)).isLight && object.visible)
|
|
325
|
+
lights.push(/** @type {Light} */ (object));
|
|
280
326
|
});
|
|
281
327
|
this._lights = lights;
|
|
282
328
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BufferGeometry, Camera, Group, IUniform, MaterialParameters, Object3D, Scene, ShaderMaterial, Texture, WebGLRenderer } from "three";
|
|
2
|
+
import { MaterialXContext, MaterialXEnvironment } from "./materialx.js";
|
|
3
|
+
import { Callbacks } from "./materialx.helper.js";
|
|
4
|
+
|
|
5
|
+
declare type MaterialXMaterialInitParameters = {
|
|
6
|
+
name: string;
|
|
7
|
+
shaderName?: string | null;
|
|
8
|
+
shader: any;
|
|
9
|
+
loaders: Callbacks;
|
|
10
|
+
context: MaterialXContext;
|
|
11
|
+
parameters?: MaterialParameters;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Uniforms = Record<string, IUniform & { needsUpdate?: boolean }>;
|
|
16
|
+
type Precision = "highp" | "mediump" | "lowp";
|
|
17
|
+
|
|
18
|
+
export declare class MaterialXMaterial extends ShaderMaterial {
|
|
19
|
+
readonly shaderName: string | null;
|
|
20
|
+
|
|
21
|
+
copy(source: MaterialXMaterial): this;
|
|
22
|
+
|
|
23
|
+
private _context: MaterialXContext | null;
|
|
24
|
+
private _shader: any;
|
|
25
|
+
private _needsTangents: boolean;
|
|
26
|
+
|
|
27
|
+
constructor(init?: MaterialXMaterialInitParameters);
|
|
28
|
+
|
|
29
|
+
private _missingTangentsWarned: boolean;
|
|
30
|
+
onBeforeRender(renderer: WebGLRenderer, _scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D, _group: Group): void;
|
|
31
|
+
|
|
32
|
+
envMapIntensity: number;
|
|
33
|
+
envMap: Texture | null;
|
|
34
|
+
updateUniforms(environment: MaterialXEnvironment, _renderer: WebGLRenderer, object: Object3D, camera: Camera, time?: number, frame?: number): void;
|
|
35
|
+
|
|
36
|
+
private updateEnvironmentUniforms(environment: MaterialXEnvironment): void;
|
|
37
|
+
}
|