@needle-tools/materialx 1.0.1-next.7bd39cb → 1.0.1-next.b9467c8
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/package.json +1 -1
- package/src/loader/loader.needle.ts +1 -1
- package/src/loader/loader.three.ts +70 -56
- package/src/materialx.helper.ts +65 -113
- package/src/materialx.material.ts +18 -8
package/package.json
CHANGED
|
@@ -53,7 +53,7 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
53
53
|
// Register the MaterialX loader extension
|
|
54
54
|
// Environment initialization is now handled in the MaterialXLoader constructor
|
|
55
55
|
loader.register(p => {
|
|
56
|
-
this.loader = new MaterialXLoader(p,
|
|
56
|
+
this.loader = new MaterialXLoader(p, context);
|
|
57
57
|
return this.loader;
|
|
58
58
|
});
|
|
59
59
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context } from "@needle-tools/engine";
|
|
2
|
-
import { Material, MeshStandardMaterial, Texture, NearestFilter, CompressedTexture
|
|
2
|
+
import { Material, MeshStandardMaterial, Texture, NearestFilter, CompressedTexture } from "three";
|
|
3
3
|
import { GLTF, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
4
|
import { ready, MaterialXEnvironment, state } from "../materialx.js";
|
|
5
5
|
import { debug } from "../utils.js";
|
|
@@ -21,14 +21,6 @@ export type MaterialX_material_extension = {
|
|
|
21
21
|
name: string; // Material name reference
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
type MaterialDefinition = {
|
|
25
|
-
name?: string; // Optional name for the material
|
|
26
|
-
doubleSided?: boolean; // Whether the material is double-sided
|
|
27
|
-
extensions?: {
|
|
28
|
-
[key: string]: any; // Extensions for the material, including MaterialX
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
// MaterialX loader extension for js GLTFLoader
|
|
33
25
|
export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
34
26
|
readonly name = "NEEDLE_materials_mtlx";
|
|
@@ -46,13 +38,7 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
46
38
|
return this._generatedMaterials;
|
|
47
39
|
}
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
* MaterialXLoader constructor
|
|
51
|
-
* @param parser The GLTFParser instance
|
|
52
|
-
* @param url The URL of the GLTF file
|
|
53
|
-
* @param context The context for the GLTF loading process
|
|
54
|
-
*/
|
|
55
|
-
constructor(private parser: GLTFParser, private url: string, private context: Context) {
|
|
41
|
+
constructor(private parser: GLTFParser, private context: Context) {
|
|
56
42
|
if (debug) console.log("MaterialXLoader created for parser");
|
|
57
43
|
// Start loading of MaterialX environment if the root extension exists
|
|
58
44
|
if (this.materialX_root_data) {
|
|
@@ -108,14 +94,15 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
108
94
|
|
|
109
95
|
private async _loadMaterialAsync(materialIndex: number): Promise<Material> {
|
|
110
96
|
|
|
111
|
-
const materialDef = this.parser.json.materials?.[materialIndex]
|
|
112
|
-
if (debug) console.log("[MaterialX] extension found in material:", materialDef.extensions
|
|
97
|
+
const materialDef = this.parser.json.materials?.[materialIndex];
|
|
98
|
+
if (debug) console.log("[MaterialX] extension found in material:", materialDef.extensions[this.name]);
|
|
113
99
|
|
|
114
100
|
// Handle different types of MaterialX data
|
|
115
|
-
const
|
|
101
|
+
const dataIndex = materialDef.extensions[this.name] as MaterialX_material_extension;
|
|
116
102
|
|
|
117
|
-
if (
|
|
118
|
-
|
|
103
|
+
if (dataIndex) {
|
|
104
|
+
// Create a new material and process MaterialX - AWAIT THIS!
|
|
105
|
+
return await this._createMaterialXMaterial(dataIndex);
|
|
119
106
|
}
|
|
120
107
|
|
|
121
108
|
// Return fallback material instead of null
|
|
@@ -124,7 +111,7 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
124
111
|
return fallbackMaterial;
|
|
125
112
|
}
|
|
126
113
|
|
|
127
|
-
private async _createMaterialXMaterial(
|
|
114
|
+
private async _createMaterialXMaterial(material_extension: MaterialX_material_extension): Promise<Material> {
|
|
128
115
|
try {
|
|
129
116
|
if (debug) console.log(`Creating MaterialX material: ${material_extension.name}`);
|
|
130
117
|
|
|
@@ -236,45 +223,72 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
236
223
|
name: material_extension.name,
|
|
237
224
|
shader,
|
|
238
225
|
transparent: isTransparent,
|
|
239
|
-
side: material_def.doubleSided ? DoubleSide : FrontSide,
|
|
240
226
|
loaders: {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
227
|
+
getTexture: url => {
|
|
228
|
+
// Return a checkerboard texture for now
|
|
229
|
+
const defaultTexture = new Texture();
|
|
230
|
+
defaultTexture.image = new Image();
|
|
231
|
+
defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAG0lEQVR4nGJqWH9q9e8XjA/VrL8UfQIEAAD//zn2CCX5UcsdAAAAAElFTkSuQmCC";
|
|
232
|
+
defaultTexture.needsUpdate = true;
|
|
233
|
+
// Pixelated filtering
|
|
234
|
+
defaultTexture.magFilter = NearestFilter;
|
|
235
|
+
defaultTexture.minFilter = NearestFilter;
|
|
236
|
+
new Promise(() => {
|
|
237
|
+
// Find the index of the texture in the parser
|
|
238
|
+
const filenameWithoutExt = url.split('/').pop()?.split('.').shift() || '';
|
|
239
|
+
|
|
240
|
+
// Resolve the texture from the MaterialX root extension
|
|
241
|
+
const ext = this.materialX_root_data;
|
|
242
|
+
if (ext) {
|
|
243
|
+
const textures = ext.textures || [];
|
|
244
|
+
let index = -1;
|
|
245
|
+
for (const texture of textures) {
|
|
246
|
+
// Find the texture by name and use the pointer string to get the index
|
|
247
|
+
if (texture.name === filenameWithoutExt) {
|
|
248
|
+
const ptr = texture.pointer;
|
|
249
|
+
const indexStr = ptr.substring("/textures/".length);
|
|
250
|
+
index = parseInt(indexStr);
|
|
251
|
+
|
|
252
|
+
if (isNaN(index) || index < 0) {
|
|
253
|
+
console.error("[MaterialX] Invalid texture index in pointer:", ptr);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
if (debug) console.log("[MaterialX] Texture index found:", index, "for", filenameWithoutExt);
|
|
258
|
+
}
|
|
264
259
|
}
|
|
265
260
|
}
|
|
266
|
-
}
|
|
267
261
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
262
|
+
if (index < 0) {
|
|
263
|
+
console.error("[MaterialX] Texture not found in parser:", filenameWithoutExt, this.parser.json);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
this.parser.getDependency("texture", index).then(tex => {
|
|
267
|
+
if (debug) console.log("[MaterialX] Texture loaded:" + tex.name, tex);
|
|
268
|
+
|
|
269
|
+
// update the default texture with the loaded texture
|
|
270
|
+
defaultTexture.image = tex.image;
|
|
271
|
+
defaultTexture.needsUpdate = true;
|
|
272
|
+
defaultTexture.userData = tex.userData || {}; // needed for LODs
|
|
273
|
+
defaultTexture.format = tex.format;
|
|
274
|
+
defaultTexture.source = tex.source || defaultTexture.source;
|
|
275
|
+
defaultTexture.type = tex.type;
|
|
276
|
+
defaultTexture.format = tex.format;
|
|
277
|
+
defaultTexture.magFilter = tex.magFilter;
|
|
278
|
+
defaultTexture.minFilter = tex.minFilter;
|
|
279
|
+
defaultTexture.wrapS = tex.wrapS;
|
|
280
|
+
defaultTexture.wrapT = tex.wrapT;
|
|
281
|
+
defaultTexture.generateMipmaps = tex.generateMipmaps;
|
|
282
|
+
defaultTexture.mipmaps = tex.mipmaps || defaultTexture.mipmaps;
|
|
283
|
+
defaultTexture.unpackAlignment = tex.unpackAlignment;
|
|
284
|
+
|
|
285
|
+
if (!(defaultTexture instanceof CompressedTexture) && tex instanceof CompressedTexture) {
|
|
286
|
+
(defaultTexture as any).isCompressedTexture = true;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
271
289
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return tex;
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
290
|
+
});
|
|
291
|
+
return defaultTexture;
|
|
278
292
|
}
|
|
279
293
|
}
|
|
280
294
|
});
|
package/src/materialx.helper.ts
CHANGED
|
@@ -73,162 +73,95 @@ function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"
|
|
|
73
73
|
|
|
74
74
|
|
|
75
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);
|
|
76
|
+
getTexture: (path: string) => THREE.Texture;
|
|
119
77
|
}
|
|
120
78
|
|
|
121
79
|
/**
|
|
122
80
|
* Get Three uniform from MaterialX value
|
|
81
|
+
* @param {mx.Uniform.type} type
|
|
82
|
+
* @param {mx.Uniform.value} value
|
|
83
|
+
* @param {mx.Uniform.name} name
|
|
84
|
+
* @param {mx.Uniforms} uniforms
|
|
85
|
+
* @param {Loaders} loaders
|
|
86
|
+
* @param {string} searchPath
|
|
87
|
+
* @param {boolean} flipY
|
|
123
88
|
*/
|
|
124
|
-
function toThreeUniform(
|
|
125
|
-
|
|
126
|
-
const uniform = new THREE.Uniform<any>(null);
|
|
89
|
+
function toThreeUniform(type: string, value: any, name: string, uniforms: any, loaders: Loaders, searchPath, flipY: boolean) {
|
|
127
90
|
|
|
128
91
|
switch (type) {
|
|
129
92
|
case 'float':
|
|
130
93
|
case 'integer':
|
|
131
94
|
case 'boolean':
|
|
132
|
-
|
|
95
|
+
return value;
|
|
133
96
|
break;
|
|
134
97
|
case 'vector2':
|
|
135
|
-
|
|
98
|
+
return fromVector(value, 2);
|
|
136
99
|
break;
|
|
137
100
|
case 'vector3':
|
|
138
101
|
case 'color3':
|
|
139
|
-
|
|
140
|
-
break;
|
|
102
|
+
return fromVector(value, 3);
|
|
141
103
|
case 'vector4':
|
|
142
104
|
case 'color4':
|
|
143
|
-
|
|
144
|
-
break;
|
|
105
|
+
return fromVector(value, 4);
|
|
145
106
|
case 'matrix33':
|
|
146
|
-
|
|
147
|
-
break;
|
|
107
|
+
return fromMatrix(value, 9);
|
|
148
108
|
case 'matrix44':
|
|
149
|
-
|
|
150
|
-
break;
|
|
109
|
+
return fromMatrix(value, 16);
|
|
151
110
|
case 'filename':
|
|
152
111
|
if (value) {
|
|
153
112
|
// Cache / reuse texture to avoid reload overhead.
|
|
154
113
|
// Note: that data blobs and embedded data textures are not cached as they are transient data.
|
|
155
|
-
let checkCache =
|
|
114
|
+
let checkCache = false;
|
|
156
115
|
let texturePath = searchPath + IMAGE_PATH_SEPARATOR + value;
|
|
157
116
|
if (value.startsWith('blob:')) {
|
|
158
117
|
texturePath = value;
|
|
118
|
+
if (debug) console.log('Load blob URL:', texturePath);
|
|
159
119
|
checkCache = false;
|
|
160
120
|
}
|
|
161
|
-
else if (value.startsWith('
|
|
121
|
+
else if (value.startsWith('http')) {
|
|
162
122
|
texturePath = value;
|
|
163
|
-
|
|
123
|
+
if (debug) console.log('Load HTTP URL:', texturePath);
|
|
164
124
|
}
|
|
165
|
-
else if (value.startsWith('
|
|
125
|
+
else if (value.startsWith('data:')) {
|
|
166
126
|
texturePath = value;
|
|
167
|
-
checkCache =
|
|
127
|
+
checkCache = false;
|
|
128
|
+
if (debug) console.log('Load data URL:', texturePath);
|
|
168
129
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
}
|
|
130
|
+
const cachedTexture = checkCache && THREE.Cache.get(texturePath);
|
|
131
|
+
if (cachedTexture) {
|
|
132
|
+
if (debug) console.log('Use cached texture: ', texturePath, cachedTexture);
|
|
133
|
+
return cachedTexture;
|
|
183
134
|
}
|
|
184
135
|
else {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
});
|
|
136
|
+
const texture = loaders.getTexture(texturePath);
|
|
137
|
+
if (checkCache) THREE.Cache.add(texturePath, texture);
|
|
138
|
+
// Set address & filtering mode
|
|
139
|
+
if (texture) setTextureParameters(texture, name, uniforms, flipY);
|
|
140
|
+
return texture;
|
|
208
141
|
}
|
|
209
142
|
}
|
|
210
143
|
break;
|
|
211
144
|
case 'samplerCube':
|
|
212
145
|
case 'string':
|
|
213
|
-
|
|
146
|
+
return null;
|
|
214
147
|
default:
|
|
215
148
|
const key = type + ':' + name;
|
|
216
149
|
if (!valueTypeWarningMap.has(key)) {
|
|
217
150
|
valueTypeWarningMap.set(key, true);
|
|
218
151
|
console.warn('MaterialX: Unsupported uniform type: ' + type + ' for uniform: ' + name, value);
|
|
219
152
|
}
|
|
220
|
-
|
|
153
|
+
return null;
|
|
221
154
|
}
|
|
222
|
-
|
|
223
|
-
return uniform;
|
|
224
155
|
}
|
|
225
156
|
|
|
226
157
|
const valueTypeWarningMap = new Map<string, boolean>();
|
|
227
158
|
|
|
228
159
|
/**
|
|
229
160
|
* Get Three wrapping mode
|
|
161
|
+
* @param {mx.TextureFilter.wrap} mode
|
|
162
|
+
* @returns {THREE.Wrapping}
|
|
230
163
|
*/
|
|
231
|
-
function getWrapping(mode
|
|
164
|
+
function getWrapping(mode) {
|
|
232
165
|
let wrap;
|
|
233
166
|
switch (mode) {
|
|
234
167
|
case 1:
|
|
@@ -247,14 +180,36 @@ function getWrapping(mode: number): THREE.Wrapping {
|
|
|
247
180
|
return wrap;
|
|
248
181
|
}
|
|
249
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Get Three minification filter
|
|
185
|
+
* @param {mx.TextureFilter.minFilter} type
|
|
186
|
+
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
187
|
+
*/
|
|
188
|
+
function getMinFilter(type, generateMipmaps) {
|
|
189
|
+
let filterType: THREE.TextureFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
|
|
190
|
+
if (type === 0) {
|
|
191
|
+
filterType = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
192
|
+
}
|
|
193
|
+
return filterType;
|
|
194
|
+
}
|
|
250
195
|
|
|
251
196
|
/**
|
|
252
197
|
* Set Three texture parameters
|
|
198
|
+
* @param {THREE.Texture} texture
|
|
199
|
+
* @param {mx.Uniform.name} name
|
|
200
|
+
* @param {mx.Uniforms} uniforms
|
|
201
|
+
* @param {mx.TextureFilter.generateMipmaps} generateMipmaps
|
|
253
202
|
*/
|
|
254
|
-
function setTextureParameters(texture
|
|
203
|
+
function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true) {
|
|
255
204
|
const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
|
|
256
205
|
const base = name.substring(0, idx) || name;
|
|
257
206
|
|
|
207
|
+
// texture.generateMipmaps = generateMipmaps;
|
|
208
|
+
// texture.wrapS = THREE.RepeatWrapping;
|
|
209
|
+
// texture.wrapT = THREE.RepeatWrapping;
|
|
210
|
+
// texture.magFilter = THREE.LinearFilter;
|
|
211
|
+
// texture.flipY = flipY;
|
|
212
|
+
|
|
258
213
|
if (uniforms.find(base + UADDRESS_MODE_SUFFIX)) {
|
|
259
214
|
const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData();
|
|
260
215
|
texture.wrapS = getWrapping(uaddressmode);
|
|
@@ -265,12 +220,8 @@ function setTextureParameters(texture: THREE.Texture, name: string, uniforms: an
|
|
|
265
220
|
texture.wrapT = getWrapping(vaddressmode);
|
|
266
221
|
}
|
|
267
222
|
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
if (mxFilterType === 0) {
|
|
271
|
-
minFilter = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
|
|
272
|
-
}
|
|
273
|
-
texture.minFilter = minFilter;
|
|
223
|
+
const filterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1;
|
|
224
|
+
texture.minFilter = getMinFilter(filterType, generateMipmaps);
|
|
274
225
|
}
|
|
275
226
|
|
|
276
227
|
/**
|
|
@@ -454,7 +405,7 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
|
|
|
454
405
|
/**
|
|
455
406
|
* Get uniform values for a shader
|
|
456
407
|
*/
|
|
457
|
-
export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Loaders, searchPath: string) {
|
|
408
|
+
export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Loaders, searchPath: string, flipY: boolean) {
|
|
458
409
|
const threeUniforms = {};
|
|
459
410
|
|
|
460
411
|
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
@@ -467,8 +418,9 @@ export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Lo
|
|
|
467
418
|
const variable = uniforms.get(i);
|
|
468
419
|
const value = variable.getValue()?.getData();
|
|
469
420
|
const name = variable.getVariable();
|
|
470
|
-
if (debug) console.log("Adding uniform", { path: variable.getPath(), name
|
|
471
|
-
threeUniforms[name] = toThreeUniform(
|
|
421
|
+
if (debug) console.log("Adding uniform", { path: variable.getPath(), name, value, type: variable.getType().getName() });
|
|
422
|
+
threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms,
|
|
423
|
+
loaders, searchPath, flipY));
|
|
472
424
|
}
|
|
473
425
|
}
|
|
474
426
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Camera, DoubleSide, FrontSide, GLSL3,
|
|
1
|
+
import { Camera, DoubleSide, FrontSide, GLSL3, Matrix3, Matrix4, Mesh, Object3D, ShaderMaterial, Texture, Vector3 } from "three";
|
|
2
2
|
import { debug } from "./utils.js";
|
|
3
|
-
import { MaterialXEnvironment } from "./materialx.js";
|
|
3
|
+
import { MaterialXEnvironment, state } from "./materialx.js";
|
|
4
4
|
import { getUniformValues, Loaders } from "./materialx.helper.js";
|
|
5
5
|
import { Context } from "@needle-tools/engine";
|
|
6
6
|
|
|
@@ -16,7 +16,6 @@ declare type MaterialXMaterialInitParameters = {
|
|
|
16
16
|
shader: any,
|
|
17
17
|
loaders: Loaders,
|
|
18
18
|
transparent?: boolean,
|
|
19
|
-
side?: MaterialParameters['side'],
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export class MaterialXMaterial extends ShaderMaterial {
|
|
@@ -98,22 +97,26 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
98
97
|
#include <colorspace_fragment>`);
|
|
99
98
|
|
|
100
99
|
const searchPath = ""; // Could be derived from the asset path if needed
|
|
100
|
+
const flipV = false; // Set based on your geometry requirements
|
|
101
101
|
const isTransparent = init.transparent ?? false;
|
|
102
102
|
super({
|
|
103
103
|
name: init.name,
|
|
104
|
-
uniforms: {
|
|
104
|
+
uniforms: {
|
|
105
|
+
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath, flipV),
|
|
106
|
+
...getUniformValues(init.shader.getStage('pixel'), init.loaders, searchPath, flipV),
|
|
107
|
+
},
|
|
105
108
|
vertexShader: vertexShader,
|
|
106
109
|
fragmentShader: fragmentShader,
|
|
107
110
|
glslVersion: GLSL3,
|
|
108
111
|
transparent: isTransparent,
|
|
109
|
-
side:
|
|
112
|
+
side: FrontSide,
|
|
110
113
|
depthTest: true,
|
|
111
114
|
depthWrite: !isTransparent,
|
|
112
115
|
});
|
|
113
116
|
|
|
117
|
+
|
|
118
|
+
|
|
114
119
|
Object.assign(this.uniforms, {
|
|
115
|
-
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath),
|
|
116
|
-
...getUniformValues(init.shader.getStage('pixel'), init.loaders, searchPath),
|
|
117
120
|
u_envMatrix: { value: new Matrix4() },
|
|
118
121
|
u_envRadiance: { value: null, type: 't' },
|
|
119
122
|
u_envRadianceMips: { value: 8, type: 'i' },
|
|
@@ -142,6 +145,8 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
142
145
|
|
|
143
146
|
const uniforms = this.uniforms;
|
|
144
147
|
|
|
148
|
+
if (!uniforms) return;
|
|
149
|
+
|
|
145
150
|
// TODO remove. Not sure why this is needed, but without it
|
|
146
151
|
// we currently get some "swimming" where matrices are not up to date.
|
|
147
152
|
camera.updateMatrixWorld(true);
|
|
@@ -188,15 +193,20 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
188
193
|
const lightCount = environment.lightCount || 0;
|
|
189
194
|
const textures = environment.getTextures(this) || null;
|
|
190
195
|
|
|
196
|
+
// Update each generated material's lighting uniforms
|
|
197
|
+
if (!this.uniforms) return;
|
|
198
|
+
|
|
191
199
|
// Update light count
|
|
192
200
|
if (this.uniforms.u_numActiveLightSources && lightCount >= 0) {
|
|
193
201
|
this.uniforms.u_numActiveLightSources.value = lightCount;
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
// Update light data
|
|
204
|
+
// Update light data if we have lights
|
|
197
205
|
if (lightData) {
|
|
198
206
|
this.uniforms.u_lightData.value = lightData;
|
|
207
|
+
if (debug) console.log("[MaterialX] Updated light data for material", this.name, lightData, this.uniforms,);
|
|
199
208
|
}
|
|
209
|
+
else if (debug) console.warn("[MaterialX] No light data available to update uniforms for material", this.name);
|
|
200
210
|
|
|
201
211
|
// Update environment uniforms
|
|
202
212
|
if (this.uniforms.u_envMatrix) {
|