@needle-tools/materialx 1.0.1-next.c1bbe8d → 1.0.1-next.c315a2f
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 +2 -0
- package/package.json +1 -1
- package/src/loader/loader.needle.ts +25 -29
- package/src/loader/loader.three.ts +94 -345
- package/src/{helper.js → materialx.helper.ts} +180 -177
- package/src/materialx.material.ts +216 -0
- package/src/materialx.ts +109 -81
- package/src/materialx.types.d.ts +50 -0
- package/src/textureHelper.ts +6 -6
- package/src/utils.ts +39 -4
|
@@ -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
|
+
import { debug, debugUpdate } from './utils';
|
|
9
|
+
import { MaterialX } from './materialx.types';
|
|
10
10
|
|
|
11
11
|
const IMAGE_PROPERTY_SEPARATOR = "_";
|
|
12
12
|
const UADDRESS_MODE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "uaddressmode";
|
|
@@ -20,8 +20,7 @@ const IMAGE_PATH_SEPARATOR = "/";
|
|
|
20
20
|
* @param {Object} capabilities
|
|
21
21
|
* @returns {THREE.Texture}
|
|
22
22
|
*/
|
|
23
|
-
export function prepareEnvTexture(texture, capabilities)
|
|
24
|
-
{
|
|
23
|
+
export function prepareEnvTexture(texture, capabilities) {
|
|
25
24
|
let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, texture.format, texture.type);
|
|
26
25
|
newTexture.wrapS = THREE.RepeatWrapping;
|
|
27
26
|
newTexture.anisotropy = capabilities.getMaxAnisotropy();
|
|
@@ -39,15 +38,12 @@ export function prepareEnvTexture(texture, capabilities)
|
|
|
39
38
|
* @param {any} dimension
|
|
40
39
|
* @returns {THREE.Uniform}
|
|
41
40
|
*/
|
|
42
|
-
function fromVector(value, dimension)
|
|
43
|
-
{
|
|
41
|
+
function fromVector(value, dimension) {
|
|
44
42
|
let outValue;
|
|
45
|
-
if (value)
|
|
46
|
-
{
|
|
43
|
+
if (value) {
|
|
47
44
|
outValue = [...value.data()];
|
|
48
45
|
}
|
|
49
|
-
else
|
|
50
|
-
{
|
|
46
|
+
else {
|
|
51
47
|
outValue = [];
|
|
52
48
|
for (let i = 0; i < dimension; ++i)
|
|
53
49
|
outValue.push(0.0);
|
|
@@ -58,23 +54,16 @@ function fromVector(value, dimension)
|
|
|
58
54
|
|
|
59
55
|
/**
|
|
60
56
|
* Get Three uniform from MaterialX matrix
|
|
61
|
-
* @param {mx.matrix} matrix
|
|
62
|
-
* @param {mx.matrix.size} dimension
|
|
63
57
|
*/
|
|
64
|
-
function fromMatrix(matrix, dimension)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < matrix.numRows(); ++i)
|
|
70
|
-
{
|
|
71
|
-
for (let k = 0; k < matrix.numColumns(); ++k)
|
|
72
|
-
{
|
|
58
|
+
function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"]) {
|
|
59
|
+
const vec = new Array(dimension);
|
|
60
|
+
if (matrix) {
|
|
61
|
+
for (let i = 0; i < matrix.numRows(); ++i) {
|
|
62
|
+
for (let k = 0; k < matrix.numColumns(); ++k) {
|
|
73
63
|
vec.push(matrix.getItem(i, k));
|
|
74
64
|
}
|
|
75
65
|
}
|
|
76
|
-
} else
|
|
77
|
-
{
|
|
66
|
+
} else {
|
|
78
67
|
for (let i = 0; i < dimension; ++i)
|
|
79
68
|
vec.push(0.0);
|
|
80
69
|
}
|
|
@@ -82,94 +71,140 @@ function fromMatrix(matrix, dimension)
|
|
|
82
71
|
return vec;
|
|
83
72
|
}
|
|
84
73
|
|
|
74
|
+
|
|
75
|
+
export type Loaders = {
|
|
76
|
+
/**
|
|
77
|
+
* Cache key for the loaders, used to identify and reuse textures
|
|
78
|
+
*/
|
|
79
|
+
readonly cacheKey: string;
|
|
80
|
+
/**
|
|
81
|
+
* Get a texture by path
|
|
82
|
+
* @param {string} path - The path to the texture
|
|
83
|
+
* @return {Promise<THREE.Texture>} - A promise that resolves to the texture
|
|
84
|
+
*/
|
|
85
|
+
readonly getTexture: (path: string) => Promise<THREE.Texture>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const defaultTexture = new THREE.Texture();
|
|
89
|
+
defaultTexture.needsUpdate = true;
|
|
90
|
+
defaultTexture.image = new Image();
|
|
91
|
+
defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFr6+vGqg52AAAAAxJREFUeJxjZGBEgQAAWAAJLpjsTQAAAABJRU5ErkJggg=="
|
|
92
|
+
// defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAB5QTFRFAAAABAQEw8PD////v7+/vb29Xl5eQEBA+/v7PDw8GPBYkgAAAB1JREFUeJxjZGBgYFQSABIUMlxgDGMGBtaIAnIZAKwQCSDYUEZEAAAAAElFTkSuQmCC";
|
|
93
|
+
// defaultTexture.wrapS = THREE.RepeatWrapping;
|
|
94
|
+
// defaultTexture.wrapT = THREE.RepeatWrapping;
|
|
95
|
+
// defaultTexture.minFilter = THREE.NearestFilter;
|
|
96
|
+
// defaultTexture.magFilter = THREE.NearestFilter;
|
|
97
|
+
// defaultTexture.repeat = new THREE.Vector2(100, 100);
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const defaultNormalTexture = new THREE.Texture();
|
|
101
|
+
defaultNormalTexture.needsUpdate = true;
|
|
102
|
+
defaultNormalTexture.image = new Image();
|
|
103
|
+
defaultNormalTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAABJQTFRFgYH4gIH4gYH3gIH3gIH5gID4m94ORAAAADFJREFUeJxjZBBkfMdo9P/BB0aBj/8FGB0ufghgFGT4r8wo+P8rD2Pgo3sMjIz8jAwAMLoN0ZjS5hgAAAAASUVORK5CYII=";
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
function tryGetFromCache(key: string): any {
|
|
107
|
+
const wasEnabled = THREE.Cache.enabled;
|
|
108
|
+
THREE.Cache.enabled = true;
|
|
109
|
+
const value = THREE.Cache.get(key);
|
|
110
|
+
THREE.Cache.enabled = wasEnabled;
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function addToCache(key: string, value: any): void {
|
|
114
|
+
const wasEnabled = THREE.Cache.enabled;
|
|
115
|
+
THREE.Cache.enabled = true;
|
|
116
|
+
THREE.Cache.add(key, value);
|
|
117
|
+
THREE.Cache.enabled = wasEnabled;
|
|
118
|
+
if (debug) console.log('[MaterialX] Added to cache:', key, value);
|
|
119
|
+
}
|
|
120
|
+
|
|
85
121
|
/**
|
|
86
122
|
* Get Three uniform from MaterialX value
|
|
87
|
-
* @param {mx.Uniform.type} type
|
|
88
|
-
* @param {mx.Uniform.value} value
|
|
89
|
-
* @param {mx.Uniform.name} name
|
|
90
|
-
* @param {mx.Uniforms} uniforms
|
|
91
|
-
* @param {THREE.TextureLoader} textureLoader
|
|
92
|
-
* @param {string} searchPath
|
|
93
|
-
* @param {boolean} flipY
|
|
94
123
|
*/
|
|
95
|
-
function toThreeUniform(type, value, name
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
{
|
|
124
|
+
function toThreeUniform(uniforms: any, type: string, value: any, name: string, loaders: Loaders, searchPath: string): THREE.Uniform {
|
|
125
|
+
|
|
126
|
+
const uniform = new THREE.Uniform<any>(null);
|
|
127
|
+
|
|
128
|
+
switch (type) {
|
|
100
129
|
case 'float':
|
|
101
130
|
case 'integer':
|
|
102
131
|
case 'boolean':
|
|
103
|
-
|
|
132
|
+
uniform.value = value;
|
|
104
133
|
break;
|
|
105
134
|
case 'vector2':
|
|
106
|
-
|
|
135
|
+
uniform.value = fromVector(value, 2);
|
|
107
136
|
break;
|
|
108
137
|
case 'vector3':
|
|
109
138
|
case 'color3':
|
|
110
|
-
|
|
139
|
+
uniform.value = fromVector(value, 3);
|
|
111
140
|
break;
|
|
112
141
|
case 'vector4':
|
|
113
142
|
case 'color4':
|
|
114
|
-
|
|
143
|
+
uniform.value = fromVector(value, 4);
|
|
115
144
|
break;
|
|
116
145
|
case 'matrix33':
|
|
117
|
-
|
|
146
|
+
uniform.value = fromMatrix(value, 9);
|
|
118
147
|
break;
|
|
119
148
|
case 'matrix44':
|
|
120
|
-
|
|
149
|
+
uniform.value = fromMatrix(value, 16);
|
|
121
150
|
break;
|
|
122
151
|
case 'filename':
|
|
123
|
-
if (value)
|
|
124
|
-
{
|
|
152
|
+
if (value) {
|
|
125
153
|
// Cache / reuse texture to avoid reload overhead.
|
|
126
154
|
// Note: that data blobs and embedded data textures are not cached as they are transient data.
|
|
127
|
-
let checkCache =
|
|
155
|
+
let checkCache = true;
|
|
128
156
|
let texturePath = searchPath + IMAGE_PATH_SEPARATOR + value;
|
|
129
|
-
if (value.startsWith('blob:'))
|
|
130
|
-
{
|
|
157
|
+
if (value.startsWith('blob:')) {
|
|
131
158
|
texturePath = value;
|
|
132
|
-
if (debug) console.log('Load blob URL:', texturePath);
|
|
133
159
|
checkCache = false;
|
|
134
160
|
}
|
|
135
|
-
else if (value.startsWith('
|
|
136
|
-
{
|
|
161
|
+
else if (value.startsWith('data:')) {
|
|
137
162
|
texturePath = value;
|
|
138
|
-
|
|
163
|
+
checkCache = false;
|
|
139
164
|
}
|
|
140
|
-
else if (value.startsWith('
|
|
141
|
-
{
|
|
165
|
+
else if (value.startsWith('http')) {
|
|
142
166
|
texturePath = value;
|
|
143
|
-
checkCache =
|
|
144
|
-
if (debug) console.log('Load data URL:', texturePath);
|
|
167
|
+
checkCache = true;
|
|
145
168
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (
|
|
169
|
+
|
|
170
|
+
const cacheKey = `${loaders.cacheKey}-${texturePath}`;
|
|
171
|
+
const cacheValue = checkCache ? tryGetFromCache(cacheKey) : null;
|
|
172
|
+
if (cacheValue) {
|
|
173
|
+
if (debug) console.log('[MaterialX] Use cached texture: ', cacheKey, cacheValue);
|
|
174
|
+
if (cacheValue instanceof Promise) {
|
|
175
|
+
cacheValue.then(res => {
|
|
176
|
+
if (res) uniform.value = res;
|
|
177
|
+
else console.warn(`[MaterialX] Failed to load texture ${name} '${texturePath}'`);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
uniform.value = cacheValue;
|
|
182
|
+
}
|
|
152
183
|
}
|
|
153
|
-
else
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
184
|
+
else {
|
|
185
|
+
if (name.toLowerCase().includes("normal")) {
|
|
186
|
+
uniform.value = defaultNormalTexture;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
uniform.value = defaultTexture;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (debug) console.log('[MaterialX] Load texture:', texturePath);
|
|
193
|
+
// Save the loading promise in the cache
|
|
194
|
+
const promise = loaders.getTexture(texturePath).then(res => {
|
|
195
|
+
if (res) {
|
|
196
|
+
res.colorSpace = THREE.LinearSRGBColorSpace;
|
|
197
|
+
setTextureParameters(res, name, uniforms);
|
|
198
|
+
}
|
|
199
|
+
return res;
|
|
200
|
+
});
|
|
201
|
+
if (checkCache) {
|
|
202
|
+
addToCache(cacheKey, promise);
|
|
203
|
+
}
|
|
204
|
+
promise.then(res => {
|
|
205
|
+
if (res) uniform.value = res;
|
|
206
|
+
else console.warn(`[MaterialX] Failed to load texture ${name} '${texturePath}'`);
|
|
207
|
+
});
|
|
173
208
|
}
|
|
174
209
|
}
|
|
175
210
|
break;
|
|
@@ -178,29 +213,24 @@ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath,
|
|
|
178
213
|
break;
|
|
179
214
|
default:
|
|
180
215
|
const key = type + ':' + name;
|
|
181
|
-
if (!valueTypeWarningMap.has(key))
|
|
182
|
-
{
|
|
216
|
+
if (!valueTypeWarningMap.has(key)) {
|
|
183
217
|
valueTypeWarningMap.set(key, true);
|
|
184
218
|
console.warn('MaterialX: Unsupported uniform type: ' + type + ' for uniform: ' + name, value);
|
|
185
219
|
}
|
|
186
|
-
|
|
220
|
+
break;
|
|
187
221
|
}
|
|
188
222
|
|
|
189
|
-
return
|
|
223
|
+
return uniform;
|
|
190
224
|
}
|
|
191
225
|
|
|
192
|
-
const valueTypeWarningMap = new Map();
|
|
226
|
+
const valueTypeWarningMap = new Map<string, boolean>();
|
|
193
227
|
|
|
194
228
|
/**
|
|
195
229
|
* Get Three wrapping mode
|
|
196
|
-
* @param {mx.TextureFilter.wrap} mode
|
|
197
|
-
* @returns {THREE.Wrapping}
|
|
198
230
|
*/
|
|
199
|
-
function getWrapping(mode)
|
|
200
|
-
{
|
|
231
|
+
function getWrapping(mode: number): THREE.Wrapping {
|
|
201
232
|
let wrap;
|
|
202
|
-
switch (mode)
|
|
203
|
-
{
|
|
233
|
+
switch (mode) {
|
|
204
234
|
case 1:
|
|
205
235
|
wrap = THREE.ClampToEdgeWrapping;
|
|
206
236
|
break;
|
|
@@ -217,61 +247,36 @@ function getWrapping(mode)
|
|
|
217
247
|
return wrap;
|
|
218
248
|
}
|
|
219
249
|
|
|
220
|
-
/**
|
|
221
|
-
* Get Three minification filter
|
|
222
|
-
* @param {mx.TextureFilter.minFilter} type
|
|
223
|
-
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
224
|
-
*/
|
|
225
|
-
function getMinFilter(type, generateMipmaps)
|
|
226
|
-
{
|
|
227
|
-
/** @type {THREE.TextureFilter} */
|
|
228
|
-
let filterType = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
|
|
229
|
-
if (type === 0)
|
|
230
|
-
{
|
|
231
|
-
filterType = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
232
|
-
}
|
|
233
|
-
return filterType;
|
|
234
|
-
}
|
|
235
250
|
|
|
236
251
|
/**
|
|
237
252
|
* Set Three texture parameters
|
|
238
|
-
* @param {THREE.Texture} texture
|
|
239
|
-
* @param {mx.Uniform.name} name
|
|
240
|
-
* @param {mx.Uniforms} uniforms
|
|
241
|
-
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
242
253
|
*/
|
|
243
|
-
function setTextureParameters(texture, name, uniforms
|
|
244
|
-
{
|
|
254
|
+
function setTextureParameters(texture: THREE.Texture, name: string, uniforms: any, generateMipmaps = true) {
|
|
245
255
|
const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
|
|
246
256
|
const base = name.substring(0, idx) || name;
|
|
247
257
|
|
|
248
|
-
|
|
249
|
-
texture.wrapS = THREE.RepeatWrapping;
|
|
250
|
-
texture.wrapT = THREE.RepeatWrapping;
|
|
251
|
-
texture.magFilter = THREE.LinearFilter;
|
|
252
|
-
texture.flipY = flipY;
|
|
253
|
-
|
|
254
|
-
if (uniforms.find(base + UADDRESS_MODE_SUFFIX))
|
|
255
|
-
{
|
|
258
|
+
if (uniforms.find(base + UADDRESS_MODE_SUFFIX)) {
|
|
256
259
|
const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData();
|
|
257
260
|
texture.wrapS = getWrapping(uaddressmode);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
if (uniforms.find(base + VADDRESS_MODE_SUFFIX))
|
|
261
|
-
{
|
|
263
|
+
if (uniforms.find(base + VADDRESS_MODE_SUFFIX)) {
|
|
262
264
|
const vaddressmode = uniforms.find(base + VADDRESS_MODE_SUFFIX).getValue().getData();
|
|
263
265
|
texture.wrapT = getWrapping(vaddressmode);
|
|
264
266
|
}
|
|
265
267
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
+
const mxFilterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1;
|
|
269
|
+
let minFilter: THREE.TextureFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
|
|
270
|
+
if (mxFilterType === 0) {
|
|
271
|
+
minFilter = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
272
|
+
}
|
|
273
|
+
texture.minFilter = minFilter;
|
|
268
274
|
}
|
|
269
275
|
|
|
270
276
|
/**
|
|
271
277
|
* Return the global light rotation matrix
|
|
272
278
|
*/
|
|
273
|
-
export function getLightRotation()
|
|
274
|
-
{
|
|
279
|
+
export function getLightRotation() {
|
|
275
280
|
return new THREE.Matrix4().makeRotationY(Math.PI / 2);
|
|
276
281
|
}
|
|
277
282
|
|
|
@@ -280,11 +285,9 @@ export function getLightRotation()
|
|
|
280
285
|
* @param {mx.Document} doc
|
|
281
286
|
* @returns {Array.<mx.Node>}
|
|
282
287
|
*/
|
|
283
|
-
export function findLights(doc)
|
|
284
|
-
|
|
285
|
-
let
|
|
286
|
-
for (let node of doc.getNodes())
|
|
287
|
-
{
|
|
288
|
+
export function findLights(doc: MaterialX.Document) {
|
|
289
|
+
let lights = new Array<any>;
|
|
290
|
+
for (let node of doc.getNodes()) {
|
|
288
291
|
if (node.getType() === "lightshader")
|
|
289
292
|
lights.push(node);
|
|
290
293
|
}
|
|
@@ -295,19 +298,14 @@ let lightTypesBound = {};
|
|
|
295
298
|
|
|
296
299
|
/**
|
|
297
300
|
* Register lights in shader generation context
|
|
298
|
-
* @param {
|
|
301
|
+
* @param {MaterialX.MODULE} mx MaterialX Module
|
|
299
302
|
* @param {mx.GenContext} genContext Shader generation context
|
|
300
|
-
* @returns {Array.<mx.Node>}
|
|
301
303
|
*/
|
|
302
|
-
export async function registerLights(mx, genContext)
|
|
303
|
-
{
|
|
304
|
+
export async function registerLights(mx: MaterialX.MODULE, genContext: any): Promise<void> {
|
|
304
305
|
lightTypesBound = {};
|
|
305
306
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
306
|
-
|
|
307
307
|
mx.HwShaderGenerator.unbindLightShaders(genContext);
|
|
308
|
-
|
|
309
308
|
let lightId = 1;
|
|
310
|
-
|
|
311
309
|
// All light types so that we have NodeDefs for them
|
|
312
310
|
const defaultLightRigXml = `<?xml version="1.0"?>
|
|
313
311
|
<materialx version="1.39">
|
|
@@ -325,30 +323,27 @@ export async function registerLights(mx, genContext)
|
|
|
325
323
|
|
|
326
324
|
// Load default light rig XML to ensure we have all light types available
|
|
327
325
|
const lightRigDoc = mx.createDocument();
|
|
328
|
-
await mx.readFromXmlString(lightRigDoc, defaultLightRigXml);
|
|
326
|
+
await mx.readFromXmlString(lightRigDoc, defaultLightRigXml, "");
|
|
329
327
|
const document = mx.createDocument();
|
|
330
328
|
const stdlib = mx.loadStandardLibraries(genContext);
|
|
331
329
|
document.setDataLibrary(stdlib);
|
|
332
330
|
document.importLibrary(lightRigDoc);
|
|
333
331
|
const defaultLights = findLights(document);
|
|
334
|
-
|
|
335
|
-
console.log("Default lights in MaterialX document", defaultLights);
|
|
332
|
+
if (debug) console.log("Default lights in MaterialX document", defaultLights);
|
|
336
333
|
|
|
337
334
|
// Loading a document seems to reset this option for some reason, so we set it again
|
|
338
335
|
genContext.getOptions().hwMaxActiveLightSources = maxLightCount;
|
|
339
336
|
|
|
340
337
|
// Register types only – we get these from the default light rig XML above
|
|
341
338
|
// This is needed to ensure that the light shaders are bound for each light type
|
|
342
|
-
for (let light of defaultLights)
|
|
343
|
-
{
|
|
339
|
+
for (let light of defaultLights) {
|
|
344
340
|
const lightDef = light.getNodeDef();
|
|
345
341
|
if (debug) console.log("Default light node definition", lightDef);
|
|
346
342
|
if (!lightDef) continue;
|
|
347
343
|
|
|
348
344
|
const lightName = lightDef.getName();
|
|
349
345
|
if (debug) console.log("Registering default light", { lightName, lightDef });
|
|
350
|
-
if (!lightTypesBound[lightName])
|
|
351
|
-
{
|
|
346
|
+
if (!lightTypesBound[lightName]) {
|
|
352
347
|
// TODO check if we need to bind light shader for each three.js light instead of once per type
|
|
353
348
|
if (debug) console.log("Bind light shader for node", { lightName, lightId, lightDef });
|
|
354
349
|
lightTypesBound[lightName] = lightId;
|
|
@@ -374,16 +369,22 @@ function threeLightTypeToMaterialXNodeName(threeLightType) {
|
|
|
374
369
|
}
|
|
375
370
|
};
|
|
376
371
|
|
|
372
|
+
type LightData = {
|
|
373
|
+
type: number, // Light type ID
|
|
374
|
+
position: THREE.Vector3, // Position in world space
|
|
375
|
+
direction: THREE.Vector3, // Direction in world space
|
|
376
|
+
color: THREE.Color, // Color of the light
|
|
377
|
+
intensity: number, // Intensity of the light
|
|
378
|
+
decay_rate: number, // Decay rate for point and spot lights
|
|
379
|
+
inner_angle: number, // Inner angle for spot lights
|
|
380
|
+
outer_angle: number, // Outer angle for spot lights
|
|
381
|
+
}
|
|
382
|
+
|
|
377
383
|
/**
|
|
378
384
|
* 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 }}
|
|
383
385
|
*/
|
|
384
|
-
export function getLightData(
|
|
385
|
-
|
|
386
|
-
const lightData = [];
|
|
386
|
+
export function getLightData(lights: any, genContext: any): { lightData: LightData[], lightCount: number } {
|
|
387
|
+
const lightData = new Array();
|
|
387
388
|
const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
|
|
388
389
|
|
|
389
390
|
// Three.js lights
|
|
@@ -399,9 +400,18 @@ export function getLightData(mx, lights, genContext)
|
|
|
399
400
|
console.error("MaterialX: Light type not registered in context. Make sure to register light types before using them.", lightDefinitionName);
|
|
400
401
|
|
|
401
402
|
const wp = light.getWorldPosition(new THREE.Vector3());
|
|
402
|
-
const wd = getWorldDirection(light, new THREE.Vector3(0,0
|
|
403
|
+
const wd = getWorldDirection(light, new THREE.Vector3(0, 0, -1));
|
|
403
404
|
|
|
404
|
-
//
|
|
405
|
+
// Shader math from the generated MaterialX shader:
|
|
406
|
+
// float low = min(light.inner_angle, light.outer_angle);
|
|
407
|
+
// float high = light.inner_angle;
|
|
408
|
+
// float cosDir = dot(result.direction, -light.direction);
|
|
409
|
+
// float spotAttenuation = smoothstep(low, high, cosDir);
|
|
410
|
+
|
|
411
|
+
const outerAngleRad = light.angle;
|
|
412
|
+
const innerAngleRad = outerAngleRad * (1 - light.penumbra);
|
|
413
|
+
const inner_angle = Math.cos(innerAngleRad);
|
|
414
|
+
const outer_angle = Math.cos(outerAngleRad);
|
|
405
415
|
|
|
406
416
|
lightData.push({
|
|
407
417
|
type: lightTypesBound[lightDefinitionName],
|
|
@@ -410,11 +420,11 @@ export function getLightData(mx, lights, genContext)
|
|
|
410
420
|
color: new THREE.Color().fromArray(light.color.toArray()),
|
|
411
421
|
// Luminous efficacy for converting radiant power in watts (W) to luminous flux in lumens (lm) at a wavelength of 555 nm.
|
|
412
422
|
// 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.
|
|
413
|
-
intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415: 1.0),
|
|
423
|
+
intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415 : 1.0),
|
|
414
424
|
decay_rate: 2.0,
|
|
415
425
|
// Approximations for testing – the relevant light has 61.57986...129.4445 as inner/outer spot angle
|
|
416
|
-
inner_angle:
|
|
417
|
-
outer_angle:
|
|
426
|
+
inner_angle: inner_angle,
|
|
427
|
+
outer_angle: outer_angle,
|
|
418
428
|
});
|
|
419
429
|
}
|
|
420
430
|
|
|
@@ -422,8 +432,7 @@ export function getLightData(mx, lights, genContext)
|
|
|
422
432
|
const lightCount = lightData.length;
|
|
423
433
|
|
|
424
434
|
// If we don't have enough entries in lightData, fill with empty lights
|
|
425
|
-
while (lightData.length < maxLightCount)
|
|
426
|
-
{
|
|
435
|
+
while (lightData.length < maxLightCount) {
|
|
427
436
|
const emptyLight = {
|
|
428
437
|
type: 0, // Default light type
|
|
429
438
|
position: new THREE.Vector3(0, 0, 0),
|
|
@@ -437,38 +446,32 @@ export function getLightData(mx, lights, genContext)
|
|
|
437
446
|
lightData.push(emptyLight);
|
|
438
447
|
}
|
|
439
448
|
|
|
440
|
-
if (
|
|
441
|
-
console.log("Registered lights in MaterialX context", lightTypesBound, lightData);
|
|
449
|
+
if (debugUpdate) console.log("Registered lights in MaterialX context", lightTypesBound, lightData);
|
|
442
450
|
|
|
443
451
|
return { lightData, lightCount };
|
|
444
452
|
}
|
|
445
453
|
|
|
446
454
|
/**
|
|
447
455
|
* Get uniform values for a shader
|
|
448
|
-
* @param {mx.shaderStage} shaderStage
|
|
449
|
-
* @param {THREE.TextureLoader} textureLoader
|
|
450
456
|
*/
|
|
451
|
-
export function getUniformValues(shaderStage,
|
|
452
|
-
{
|
|
457
|
+
export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Loaders, searchPath: string) {
|
|
453
458
|
const threeUniforms = {};
|
|
454
459
|
|
|
455
|
-
const uniformBlocks =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
{
|
|
461
|
-
for (let i = 0; i < uniforms.size(); ++i)
|
|
462
|
-
{
|
|
460
|
+
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
461
|
+
for (const [blockName, uniforms] of Object.entries(uniformBlocks)) {
|
|
462
|
+
// Seems struct uniforms (like in LightData) end up here as well, we should filter those out.
|
|
463
|
+
if (blockName === "LightData") continue;
|
|
464
|
+
|
|
465
|
+
if (!uniforms.empty()) {
|
|
466
|
+
for (let i = 0; i < uniforms.size(); ++i) {
|
|
463
467
|
const variable = uniforms.get(i);
|
|
464
468
|
const value = variable.getValue()?.getData();
|
|
465
469
|
const name = variable.getVariable();
|
|
466
|
-
if (debug) console.log("Adding uniform", { name, value, type: variable.getType().getName() });
|
|
467
|
-
threeUniforms[name] =
|
|
468
|
-
textureLoader, searchPath, flipY));
|
|
470
|
+
if (debug) console.log("Adding uniform", { path: variable.getPath(), name: name, value: value, type: variable.getType().getName() });
|
|
471
|
+
threeUniforms[name] = toThreeUniform(uniforms, variable.getType().getName(), value, name, loaders, searchPath);;
|
|
469
472
|
}
|
|
470
473
|
}
|
|
471
|
-
}
|
|
474
|
+
}
|
|
472
475
|
|
|
473
476
|
return threeUniforms;
|
|
474
477
|
}
|