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