@needle-tools/materialx 1.0.1-next.b9467c8 → 1.0.1-next.c1bbe8d
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/{materialx.helper.ts → helper.js} +151 -106
- package/src/loader/loader.needle.ts +28 -24
- package/src/loader/loader.three.ts +345 -116
- package/src/materialx.ts +81 -109
- package/src/textureHelper.ts +6 -6
- package/src/utils.ts +4 -39
- package/src/materialx.material.ts +0 -227
- package/src/materialx.types.d.ts +0 -50
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
import { TypeStore } from "@needle-tools/engine"
|
|
3
3
|
|
|
4
4
|
// Import types
|
|
5
|
-
import { MaterialXMaterial } from "../src/materialx.material.js";
|
|
6
5
|
import { MaterialXUniformUpdate } from "../src/loader/loader.needle.js";
|
|
7
6
|
|
|
8
7
|
// Register types
|
|
9
|
-
TypeStore.add("MaterialXMaterial", MaterialXMaterial);
|
|
10
8
|
TypeStore.add("MaterialXUniformUpdate", MaterialXUniformUpdate);
|
package/package.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { getParam, getWorldDirection } from '@needle-tools/engine';
|
|
7
7
|
import * as THREE from 'three';
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
const debug = getParam("debugmaterialx");
|
|
10
10
|
|
|
11
11
|
const IMAGE_PROPERTY_SEPARATOR = "_";
|
|
12
12
|
const UADDRESS_MODE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "uaddressmode";
|
|
@@ -20,7 +20,8 @@ const IMAGE_PATH_SEPARATOR = "/";
|
|
|
20
20
|
* @param {Object} capabilities
|
|
21
21
|
* @returns {THREE.Texture}
|
|
22
22
|
*/
|
|
23
|
-
export function prepareEnvTexture(texture, capabilities)
|
|
23
|
+
export function prepareEnvTexture(texture, capabilities)
|
|
24
|
+
{
|
|
24
25
|
let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, texture.format, texture.type);
|
|
25
26
|
newTexture.wrapS = THREE.RepeatWrapping;
|
|
26
27
|
newTexture.anisotropy = capabilities.getMaxAnisotropy();
|
|
@@ -38,12 +39,15 @@ export function prepareEnvTexture(texture, capabilities) {
|
|
|
38
39
|
* @param {any} dimension
|
|
39
40
|
* @returns {THREE.Uniform}
|
|
40
41
|
*/
|
|
41
|
-
function fromVector(value, dimension)
|
|
42
|
+
function fromVector(value, dimension)
|
|
43
|
+
{
|
|
42
44
|
let outValue;
|
|
43
|
-
if (value)
|
|
45
|
+
if (value)
|
|
46
|
+
{
|
|
44
47
|
outValue = [...value.data()];
|
|
45
48
|
}
|
|
46
|
-
else
|
|
49
|
+
else
|
|
50
|
+
{
|
|
47
51
|
outValue = [];
|
|
48
52
|
for (let i = 0; i < dimension; ++i)
|
|
49
53
|
outValue.push(0.0);
|
|
@@ -54,16 +58,23 @@ function fromVector(value, dimension) {
|
|
|
54
58
|
|
|
55
59
|
/**
|
|
56
60
|
* Get Three uniform from MaterialX matrix
|
|
61
|
+
* @param {mx.matrix} matrix
|
|
62
|
+
* @param {mx.matrix.size} dimension
|
|
57
63
|
*/
|
|
58
|
-
function fromMatrix(matrix
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
function fromMatrix(matrix, dimension)
|
|
65
|
+
{
|
|
66
|
+
let vec = [];
|
|
67
|
+
if (matrix)
|
|
68
|
+
{
|
|
69
|
+
for (let i = 0; i < matrix.numRows(); ++i)
|
|
70
|
+
{
|
|
71
|
+
for (let k = 0; k < matrix.numColumns(); ++k)
|
|
72
|
+
{
|
|
63
73
|
vec.push(matrix.getItem(i, k));
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
|
-
} else
|
|
76
|
+
} else
|
|
77
|
+
{
|
|
67
78
|
for (let i = 0; i < dimension; ++i)
|
|
68
79
|
vec.push(0.0);
|
|
69
80
|
}
|
|
@@ -71,99 +82,125 @@ function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"
|
|
|
71
82
|
return vec;
|
|
72
83
|
}
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
export type Loaders = {
|
|
76
|
-
getTexture: (path: string) => THREE.Texture;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
85
|
/**
|
|
80
86
|
* Get Three uniform from MaterialX value
|
|
81
87
|
* @param {mx.Uniform.type} type
|
|
82
88
|
* @param {mx.Uniform.value} value
|
|
83
89
|
* @param {mx.Uniform.name} name
|
|
84
90
|
* @param {mx.Uniforms} uniforms
|
|
85
|
-
* @param {
|
|
91
|
+
* @param {THREE.TextureLoader} textureLoader
|
|
86
92
|
* @param {string} searchPath
|
|
87
93
|
* @param {boolean} flipY
|
|
88
94
|
*/
|
|
89
|
-
function toThreeUniform(type
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, flipY)
|
|
96
|
+
{
|
|
97
|
+
let outValue = null;
|
|
98
|
+
switch (type)
|
|
99
|
+
{
|
|
92
100
|
case 'float':
|
|
93
101
|
case 'integer':
|
|
94
102
|
case 'boolean':
|
|
95
|
-
|
|
103
|
+
outValue = value;
|
|
96
104
|
break;
|
|
97
105
|
case 'vector2':
|
|
98
|
-
|
|
106
|
+
outValue = fromVector(value, 2);
|
|
99
107
|
break;
|
|
100
108
|
case 'vector3':
|
|
101
109
|
case 'color3':
|
|
102
|
-
|
|
110
|
+
outValue = fromVector(value, 3);
|
|
111
|
+
break;
|
|
103
112
|
case 'vector4':
|
|
104
113
|
case 'color4':
|
|
105
|
-
|
|
114
|
+
outValue = fromVector(value, 4);
|
|
115
|
+
break;
|
|
106
116
|
case 'matrix33':
|
|
107
|
-
|
|
117
|
+
outValue = fromMatrix(value, 9);
|
|
118
|
+
break;
|
|
108
119
|
case 'matrix44':
|
|
109
|
-
|
|
120
|
+
outValue = fromMatrix(value, 16);
|
|
121
|
+
break;
|
|
110
122
|
case 'filename':
|
|
111
|
-
if (value)
|
|
123
|
+
if (value)
|
|
124
|
+
{
|
|
112
125
|
// Cache / reuse texture to avoid reload overhead.
|
|
113
126
|
// Note: that data blobs and embedded data textures are not cached as they are transient data.
|
|
114
127
|
let checkCache = false;
|
|
115
128
|
let texturePath = searchPath + IMAGE_PATH_SEPARATOR + value;
|
|
116
|
-
if (value.startsWith('blob:'))
|
|
129
|
+
if (value.startsWith('blob:'))
|
|
130
|
+
{
|
|
117
131
|
texturePath = value;
|
|
118
132
|
if (debug) console.log('Load blob URL:', texturePath);
|
|
119
133
|
checkCache = false;
|
|
120
134
|
}
|
|
121
|
-
else if (value.startsWith('http'))
|
|
135
|
+
else if (value.startsWith('http'))
|
|
136
|
+
{
|
|
122
137
|
texturePath = value;
|
|
123
138
|
if (debug) console.log('Load HTTP URL:', texturePath);
|
|
124
139
|
}
|
|
125
|
-
else if (value.startsWith('data:'))
|
|
140
|
+
else if (value.startsWith('data:'))
|
|
141
|
+
{
|
|
126
142
|
texturePath = value;
|
|
127
143
|
checkCache = false;
|
|
128
144
|
if (debug) console.log('Load data URL:', texturePath);
|
|
129
145
|
}
|
|
130
146
|
const cachedTexture = checkCache && THREE.Cache.get(texturePath);
|
|
131
|
-
if (cachedTexture)
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
if (cachedTexture)
|
|
148
|
+
{
|
|
149
|
+
// Get texture from cache
|
|
150
|
+
outValue = cachedTexture;
|
|
151
|
+
if (debug) console.log('Use cached texture: ', texturePath, outValue);
|
|
134
152
|
}
|
|
135
|
-
else
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
else
|
|
154
|
+
{
|
|
155
|
+
outValue = textureLoader.load(
|
|
156
|
+
texturePath,
|
|
157
|
+
function (texture) {
|
|
158
|
+
if (debug) console.log('Load new texture: ' + texturePath, texture);
|
|
159
|
+
outValue = texture;
|
|
160
|
+
|
|
161
|
+
// Add texture to ThreeJS cache
|
|
162
|
+
if (checkCache)
|
|
163
|
+
THREE.Cache.add(texturePath, texture);
|
|
164
|
+
},
|
|
165
|
+
undefined,
|
|
166
|
+
function (error) {
|
|
167
|
+
console.error('Error loading texture: ', error);
|
|
168
|
+
});
|
|
169
|
+
|
|
138
170
|
// Set address & filtering mode
|
|
139
|
-
if (
|
|
140
|
-
|
|
171
|
+
if (outValue)
|
|
172
|
+
setTextureParameters(outValue, name, uniforms, flipY);
|
|
141
173
|
}
|
|
142
174
|
}
|
|
143
175
|
break;
|
|
144
176
|
case 'samplerCube':
|
|
145
177
|
case 'string':
|
|
146
|
-
|
|
178
|
+
break;
|
|
147
179
|
default:
|
|
148
180
|
const key = type + ':' + name;
|
|
149
|
-
if (!valueTypeWarningMap.has(key))
|
|
181
|
+
if (!valueTypeWarningMap.has(key))
|
|
182
|
+
{
|
|
150
183
|
valueTypeWarningMap.set(key, true);
|
|
151
184
|
console.warn('MaterialX: Unsupported uniform type: ' + type + ' for uniform: ' + name, value);
|
|
152
185
|
}
|
|
153
|
-
|
|
186
|
+
outValue = null;
|
|
154
187
|
}
|
|
188
|
+
|
|
189
|
+
return outValue;
|
|
155
190
|
}
|
|
156
191
|
|
|
157
|
-
const valueTypeWarningMap = new Map
|
|
192
|
+
const valueTypeWarningMap = new Map();
|
|
158
193
|
|
|
159
194
|
/**
|
|
160
195
|
* Get Three wrapping mode
|
|
161
196
|
* @param {mx.TextureFilter.wrap} mode
|
|
162
197
|
* @returns {THREE.Wrapping}
|
|
163
198
|
*/
|
|
164
|
-
function getWrapping(mode)
|
|
199
|
+
function getWrapping(mode)
|
|
200
|
+
{
|
|
165
201
|
let wrap;
|
|
166
|
-
switch (mode)
|
|
202
|
+
switch (mode)
|
|
203
|
+
{
|
|
167
204
|
case 1:
|
|
168
205
|
wrap = THREE.ClampToEdgeWrapping;
|
|
169
206
|
break;
|
|
@@ -185,9 +222,12 @@ function getWrapping(mode) {
|
|
|
185
222
|
* @param {mx.TextureFilter.minFilter} type
|
|
186
223
|
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
187
224
|
*/
|
|
188
|
-
function getMinFilter(type, generateMipmaps)
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
function getMinFilter(type, generateMipmaps)
|
|
226
|
+
{
|
|
227
|
+
/** @type {THREE.TextureFilter} */
|
|
228
|
+
let filterType = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
|
|
229
|
+
if (type === 0)
|
|
230
|
+
{
|
|
191
231
|
filterType = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
192
232
|
}
|
|
193
233
|
return filterType;
|
|
@@ -200,22 +240,25 @@ function getMinFilter(type, generateMipmaps) {
|
|
|
200
240
|
* @param {mx.Uniforms} uniforms
|
|
201
241
|
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
202
242
|
*/
|
|
203
|
-
function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true)
|
|
243
|
+
function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true)
|
|
244
|
+
{
|
|
204
245
|
const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
|
|
205
246
|
const base = name.substring(0, idx) || name;
|
|
206
247
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
248
|
+
texture.generateMipmaps = generateMipmaps;
|
|
249
|
+
texture.wrapS = THREE.RepeatWrapping;
|
|
250
|
+
texture.wrapT = THREE.RepeatWrapping;
|
|
251
|
+
texture.magFilter = THREE.LinearFilter;
|
|
252
|
+
texture.flipY = flipY;
|
|
212
253
|
|
|
213
|
-
if (uniforms.find(base + UADDRESS_MODE_SUFFIX))
|
|
254
|
+
if (uniforms.find(base + UADDRESS_MODE_SUFFIX))
|
|
255
|
+
{
|
|
214
256
|
const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData();
|
|
215
257
|
texture.wrapS = getWrapping(uaddressmode);
|
|
216
258
|
}
|
|
217
259
|
|
|
218
|
-
if (uniforms.find(base + VADDRESS_MODE_SUFFIX))
|
|
260
|
+
if (uniforms.find(base + VADDRESS_MODE_SUFFIX))
|
|
261
|
+
{
|
|
219
262
|
const vaddressmode = uniforms.find(base + VADDRESS_MODE_SUFFIX).getValue().getData();
|
|
220
263
|
texture.wrapT = getWrapping(vaddressmode);
|
|
221
264
|
}
|
|
@@ -227,7 +270,8 @@ function setTextureParameters(texture, name, uniforms, flipY = true, generateMip
|
|
|
227
270
|
/**
|
|
228
271
|
* Return the global light rotation matrix
|
|
229
272
|
*/
|
|
230
|
-
export function getLightRotation()
|
|
273
|
+
export function getLightRotation()
|
|
274
|
+
{
|
|
231
275
|
return new THREE.Matrix4().makeRotationY(Math.PI / 2);
|
|
232
276
|
}
|
|
233
277
|
|
|
@@ -236,9 +280,11 @@ export function getLightRotation() {
|
|
|
236
280
|
* @param {mx.Document} doc
|
|
237
281
|
* @returns {Array.<mx.Node>}
|
|
238
282
|
*/
|
|
239
|
-
export function findLights(doc
|
|
240
|
-
|
|
241
|
-
|
|
283
|
+
export function findLights(doc)
|
|
284
|
+
{
|
|
285
|
+
let lights = [];
|
|
286
|
+
for (let node of doc.getNodes())
|
|
287
|
+
{
|
|
242
288
|
if (node.getType() === "lightshader")
|
|
243
289
|
lights.push(node);
|
|
244
290
|
}
|
|
@@ -249,14 +295,19 @@ let lightTypesBound = {};
|
|
|
249
295
|
|
|
250
296
|
/**
|
|
251
297
|
* Register lights in shader generation context
|
|
252
|
-
* @param {
|
|
298
|
+
* @param {Object} mx MaterialX Module
|
|
253
299
|
* @param {mx.GenContext} genContext Shader generation context
|
|
300
|
+
* @returns {Array.<mx.Node>}
|
|
254
301
|
*/
|
|
255
|
-
export async function registerLights(mx
|
|
302
|
+
export async function registerLights(mx, genContext)
|
|
303
|
+
{
|
|
256
304
|
lightTypesBound = {};
|
|
257
305
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
306
|
+
|
|
258
307
|
mx.HwShaderGenerator.unbindLightShaders(genContext);
|
|
308
|
+
|
|
259
309
|
let lightId = 1;
|
|
310
|
+
|
|
260
311
|
// All light types so that we have NodeDefs for them
|
|
261
312
|
const defaultLightRigXml = `<?xml version="1.0"?>
|
|
262
313
|
<materialx version="1.39">
|
|
@@ -274,27 +325,30 @@ export async function registerLights(mx: MaterialX.MODULE, genContext: any): Pro
|
|
|
274
325
|
|
|
275
326
|
// Load default light rig XML to ensure we have all light types available
|
|
276
327
|
const lightRigDoc = mx.createDocument();
|
|
277
|
-
await mx.readFromXmlString(lightRigDoc, defaultLightRigXml
|
|
328
|
+
await mx.readFromXmlString(lightRigDoc, defaultLightRigXml);
|
|
278
329
|
const document = mx.createDocument();
|
|
279
330
|
const stdlib = mx.loadStandardLibraries(genContext);
|
|
280
331
|
document.setDataLibrary(stdlib);
|
|
281
332
|
document.importLibrary(lightRigDoc);
|
|
282
333
|
const defaultLights = findLights(document);
|
|
283
|
-
if (debug)
|
|
334
|
+
// if (debug)
|
|
335
|
+
console.log("Default lights in MaterialX document", defaultLights);
|
|
284
336
|
|
|
285
337
|
// Loading a document seems to reset this option for some reason, so we set it again
|
|
286
338
|
genContext.getOptions().hwMaxActiveLightSources = maxLightCount;
|
|
287
339
|
|
|
288
340
|
// Register types only – we get these from the default light rig XML above
|
|
289
341
|
// This is needed to ensure that the light shaders are bound for each light type
|
|
290
|
-
for (let light of defaultLights)
|
|
342
|
+
for (let light of defaultLights)
|
|
343
|
+
{
|
|
291
344
|
const lightDef = light.getNodeDef();
|
|
292
345
|
if (debug) console.log("Default light node definition", lightDef);
|
|
293
346
|
if (!lightDef) continue;
|
|
294
347
|
|
|
295
348
|
const lightName = lightDef.getName();
|
|
296
349
|
if (debug) console.log("Registering default light", { lightName, lightDef });
|
|
297
|
-
if (!lightTypesBound[lightName])
|
|
350
|
+
if (!lightTypesBound[lightName])
|
|
351
|
+
{
|
|
298
352
|
// TODO check if we need to bind light shader for each three.js light instead of once per type
|
|
299
353
|
if (debug) console.log("Bind light shader for node", { lightName, lightId, lightDef });
|
|
300
354
|
lightTypesBound[lightName] = lightId;
|
|
@@ -320,22 +374,16 @@ function threeLightTypeToMaterialXNodeName(threeLightType) {
|
|
|
320
374
|
}
|
|
321
375
|
};
|
|
322
376
|
|
|
323
|
-
type LightData = {
|
|
324
|
-
type: number, // Light type ID
|
|
325
|
-
position: THREE.Vector3, // Position in world space
|
|
326
|
-
direction: THREE.Vector3, // Direction in world space
|
|
327
|
-
color: THREE.Color, // Color of the light
|
|
328
|
-
intensity: number, // Intensity of the light
|
|
329
|
-
decay_rate: number, // Decay rate for point and spot lights
|
|
330
|
-
inner_angle: number, // Inner angle for spot lights
|
|
331
|
-
outer_angle: number, // Outer angle for spot lights
|
|
332
|
-
}
|
|
333
|
-
|
|
334
377
|
/**
|
|
335
378
|
* Update light data for shader uniforms
|
|
379
|
+
* @param {Object} mx MaterialX Module
|
|
380
|
+
* @param {Array.<mx.Node>} lights Light nodes
|
|
381
|
+
* @param {mx.GenContext} genContext Shader generation context
|
|
382
|
+
* @returns {{ lightData: Array<any>, lightCount: number }}
|
|
336
383
|
*/
|
|
337
|
-
export function getLightData(lights
|
|
338
|
-
|
|
384
|
+
export function getLightData(mx, lights, genContext)
|
|
385
|
+
{
|
|
386
|
+
const lightData = [];
|
|
339
387
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
340
388
|
|
|
341
389
|
// Three.js lights
|
|
@@ -351,18 +399,9 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
|
|
|
351
399
|
console.error("MaterialX: Light type not registered in context. Make sure to register light types before using them.", lightDefinitionName);
|
|
352
400
|
|
|
353
401
|
const wp = light.getWorldPosition(new THREE.Vector3());
|
|
354
|
-
const wd = getWorldDirection(light, new THREE.Vector3(0,
|
|
355
|
-
|
|
356
|
-
// Shader math from the generated MaterialX shader:
|
|
357
|
-
// float low = min(light.inner_angle, light.outer_angle);
|
|
358
|
-
// float high = light.inner_angle;
|
|
359
|
-
// float cosDir = dot(result.direction, -light.direction);
|
|
360
|
-
// float spotAttenuation = smoothstep(low, high, cosDir);
|
|
402
|
+
const wd = getWorldDirection(light, new THREE.Vector3(0,0,-1));
|
|
361
403
|
|
|
362
|
-
|
|
363
|
-
const innerAngleRad = outerAngleRad * (1 - light.penumbra);
|
|
364
|
-
const inner_angle = Math.cos(innerAngleRad);
|
|
365
|
-
const outer_angle = Math.cos(outerAngleRad);
|
|
404
|
+
// console.log("Registering light", light.penumbra);
|
|
366
405
|
|
|
367
406
|
lightData.push({
|
|
368
407
|
type: lightTypesBound[lightDefinitionName],
|
|
@@ -371,11 +410,11 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
|
|
|
371
410
|
color: new THREE.Color().fromArray(light.color.toArray()),
|
|
372
411
|
// Luminous efficacy for converting radiant power in watts (W) to luminous flux in lumens (lm) at a wavelength of 555 nm.
|
|
373
412
|
// 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.
|
|
374
|
-
intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415
|
|
413
|
+
intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415: 1.0),
|
|
375
414
|
decay_rate: 2.0,
|
|
376
415
|
// Approximations for testing – the relevant light has 61.57986...129.4445 as inner/outer spot angle
|
|
377
|
-
inner_angle:
|
|
378
|
-
outer_angle:
|
|
416
|
+
inner_angle: 0.9,
|
|
417
|
+
outer_angle: 0.4,
|
|
379
418
|
});
|
|
380
419
|
}
|
|
381
420
|
|
|
@@ -383,7 +422,8 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
|
|
|
383
422
|
const lightCount = lightData.length;
|
|
384
423
|
|
|
385
424
|
// If we don't have enough entries in lightData, fill with empty lights
|
|
386
|
-
while (lightData.length < maxLightCount)
|
|
425
|
+
while (lightData.length < maxLightCount)
|
|
426
|
+
{
|
|
387
427
|
const emptyLight = {
|
|
388
428
|
type: 0, // Default light type
|
|
389
429
|
position: new THREE.Vector3(0, 0, 0),
|
|
@@ -397,33 +437,38 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
|
|
|
397
437
|
lightData.push(emptyLight);
|
|
398
438
|
}
|
|
399
439
|
|
|
400
|
-
if (
|
|
440
|
+
if (debug)
|
|
441
|
+
console.log("Registered lights in MaterialX context", lightTypesBound, lightData);
|
|
401
442
|
|
|
402
443
|
return { lightData, lightCount };
|
|
403
444
|
}
|
|
404
445
|
|
|
405
446
|
/**
|
|
406
447
|
* Get uniform values for a shader
|
|
448
|
+
* @param {mx.shaderStage} shaderStage
|
|
449
|
+
* @param {THREE.TextureLoader} textureLoader
|
|
407
450
|
*/
|
|
408
|
-
export function getUniformValues(shaderStage
|
|
451
|
+
export function getUniformValues(shaderStage, textureLoader, searchPath, flipY)
|
|
452
|
+
{
|
|
409
453
|
const threeUniforms = {};
|
|
410
454
|
|
|
411
|
-
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
for (let i = 0; i < uniforms.size(); ++i)
|
|
455
|
+
const uniformBlocks = Object.values(shaderStage.getUniformBlocks());
|
|
456
|
+
uniformBlocks.forEach(uniforms =>
|
|
457
|
+
{
|
|
458
|
+
// TODO Seems struct uniforms (like in LightData) end up here as well, we should filter those out.
|
|
459
|
+
if (!uniforms.empty())
|
|
460
|
+
{
|
|
461
|
+
for (let i = 0; i < uniforms.size(); ++i)
|
|
462
|
+
{
|
|
418
463
|
const variable = uniforms.get(i);
|
|
419
464
|
const value = variable.getValue()?.getData();
|
|
420
465
|
const name = variable.getVariable();
|
|
421
|
-
if (debug) console.log("Adding uniform", {
|
|
466
|
+
if (debug) console.log("Adding uniform", { name, value, type: variable.getType().getName() });
|
|
422
467
|
threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms,
|
|
423
|
-
|
|
468
|
+
textureLoader, searchPath, flipY));
|
|
424
469
|
}
|
|
425
470
|
}
|
|
426
|
-
}
|
|
471
|
+
});
|
|
427
472
|
|
|
428
473
|
return threeUniforms;
|
|
429
474
|
}
|
|
@@ -6,39 +6,40 @@ import type { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
|
6
6
|
import type { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js";
|
|
7
7
|
import { MaterialXLoader } from "./loader.three.js";
|
|
8
8
|
import { debug } from "../utils.js";
|
|
9
|
-
import {
|
|
10
|
-
import { MaterialXMaterial } from "../materialx.material.js";
|
|
9
|
+
import { state } from "../materialx.js";
|
|
11
10
|
|
|
12
11
|
//@dont-generate-component
|
|
13
12
|
export class MaterialXUniformUpdate extends Component {
|
|
14
13
|
|
|
14
|
+
static updateMaterial(mat: Material | Material[], object: Object3D, camera: Camera) {
|
|
15
|
+
if (Array.isArray(mat)) {
|
|
16
|
+
mat.forEach(m => {
|
|
17
|
+
if (m.userData?.updateUniforms) {
|
|
18
|
+
m.userData.updateUniforms(object, camera);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
} else if (mat.userData?.updateUniforms) {
|
|
22
|
+
mat.userData.updateUniforms(object, camera);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
onEnable(): void {
|
|
16
|
-
this.context.addBeforeRenderListener(this.gameObject, this.
|
|
27
|
+
this.context.addBeforeRenderListener(this.gameObject, this._onBeforeRender);
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
onDisable(): void {
|
|
20
|
-
this.context.removeBeforeRenderListener(this.gameObject, this.
|
|
31
|
+
this.context.removeBeforeRenderListener(this.gameObject, this._onBeforeRender);
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
_onBeforeRender = () => {
|
|
24
35
|
// Update uniforms or perform any pre-render logic here
|
|
25
36
|
const gameObject = this.gameObject as any as Mesh;
|
|
26
37
|
const material = gameObject?.material;
|
|
27
38
|
|
|
28
39
|
const camera = this.context.mainCamera;
|
|
29
40
|
if (!camera) return;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (Array.isArray(material)) {
|
|
34
|
-
for (const entry of material) {
|
|
35
|
-
if (entry && entry instanceof MaterialXMaterial) {
|
|
36
|
-
entry.updateUniforms(this.context, env, this.gameObject, camera);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
} else if (material instanceof MaterialXMaterial) {
|
|
40
|
-
material.updateUniforms(this.context, env, this.gameObject, camera);
|
|
41
|
-
}
|
|
41
|
+
|
|
42
|
+
MaterialXUniformUpdate.updateMaterial(material, gameObject, camera);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -58,13 +59,9 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
58
59
|
});
|
|
59
60
|
};
|
|
60
61
|
|
|
61
|
-
onLoaded = (url: string, gltf: GLTF,
|
|
62
|
-
if (debug) console.log("[MaterialX] MaterialXLoaderPlugin: glTF loaded",
|
|
62
|
+
onLoaded = (url: string, gltf: GLTF, context: Context) => {
|
|
63
|
+
if (debug) console.log("[MaterialX] MaterialXLoaderPlugin: glTF loaded", url, gltf.scene);
|
|
63
64
|
|
|
64
|
-
// If we don't have MaterialX data in the loaded glTF we don't need to do anything else here
|
|
65
|
-
if (!this.loader?.materialX_root_data) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
65
|
// Set up onBeforeRender callbacks for objects with MaterialX materials
|
|
69
66
|
// This ensures uniforms are updated properly during rendering
|
|
70
67
|
gltf.scene.traverse((child) => {
|
|
@@ -72,7 +69,7 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
72
69
|
const mesh = child as Mesh;
|
|
73
70
|
const material = mesh.material as Material;
|
|
74
71
|
|
|
75
|
-
if (material
|
|
72
|
+
if (material?.userData?.updateUniforms) {
|
|
76
73
|
if (debug) console.log("[MaterialX] Adding MaterialX uniform update component to:", child.name);
|
|
77
74
|
child.addComponent(MaterialXUniformUpdate);
|
|
78
75
|
}
|
|
@@ -80,6 +77,13 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
80
77
|
});
|
|
81
78
|
|
|
82
79
|
if (debug) console.log("[MaterialX] Loaded: ", this.loader);
|
|
80
|
+
|
|
81
|
+
// Initialize MaterialX lighting system with scene data
|
|
82
|
+
const environment = state.materialXEnvironment;
|
|
83
|
+
environment.initializeFromContext(context).then(() => {
|
|
84
|
+
console.warn("[MaterialX] Environment initialized...");
|
|
85
|
+
this.loader?.updateLightingFromEnvironment(environment);
|
|
86
|
+
});
|
|
83
87
|
};
|
|
84
88
|
|
|
85
89
|
onExport = (_exporter: GLTFExporter, _context: Context) => {
|