@needle-tools/materialx 1.1.1-next.623fc20 → 1.1.1-next.8e8afe1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/.gitattributes +4 -0
- package/{index.ts → index.d.ts} +1 -1
- package/index.js +2 -0
- package/{needle.ts → needle.d.ts} +1 -1
- package/needle.js +2 -0
- package/package.json +11 -8
- package/src/index.d.ts +11 -0
- package/src/index.js +11 -0
- package/src/loader/loader.needle.d.ts +15 -0
- package/src/loader/loader.needle.js +62 -0
- package/src/loader/loader.three.d.ts +61 -0
- package/src/loader/loader.three.js +351 -0
- package/src/materialx.d.ts +60 -0
- package/src/materialx.helper.d.ts +31 -0
- package/src/{materialx.helper.ts → materialx.helper.js} +137 -96
- package/src/{materialx.ts → materialx.js} +115 -64
- package/src/materialx.material.d.ts +37 -0
- package/src/{materialx.material.ts → materialx.material.js} +109 -44
- package/src/utils.d.ts +17 -0
- package/src/{utils.ts → utils.js} +21 -4
- package/src/utils.texture.d.ts +13 -0
- package/src/{textureHelper.ts → utils.texture.js} +18 -17
- package/src/index.ts +0 -3
- package/src/loader/loader.needle.ts +0 -43
- package/src/loader/loader.three.ts +0 -337
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
import type { MaterialX as MX } from "./materialx.types.js";
|
|
2
1
|
import MaterialX from "../bin/JsMaterialXGenShader.js";
|
|
3
2
|
import { debug } from "./utils.js";
|
|
4
|
-
import { renderPMREMToEquirect } from "./
|
|
3
|
+
import { renderPMREMToEquirect } from "./utils.texture.js";
|
|
5
4
|
import { Light, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry, PMREMGenerator, Scene, Texture, WebGLRenderer } from "three";
|
|
6
|
-
import { registerLights, getLightData
|
|
7
|
-
import type { MaterialXMaterial } from "./materialx.material.js";
|
|
8
|
-
|
|
9
|
-
export type MaterialXContext = {
|
|
10
|
-
getTime?(): number,
|
|
11
|
-
getFrame?(): number,
|
|
12
|
-
}
|
|
13
|
-
|
|
5
|
+
import { registerLights, getLightData } from "./materialx.helper.js";
|
|
14
6
|
|
|
15
7
|
export const state = new class {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
/** @type {import("./materialx.types.js").MaterialX.MODULE | null} */
|
|
9
|
+
materialXModule = null;
|
|
10
|
+
/** @type {any} */
|
|
11
|
+
materialXGenerator = null;
|
|
12
|
+
/** @type {any} */
|
|
13
|
+
materialXGenContext = null;
|
|
14
|
+
/** @type {any} */
|
|
15
|
+
materialXStdLib = null;
|
|
16
|
+
/** @type {Promise<void> | null} */
|
|
17
|
+
materialXInitPromise = null;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
|
|
24
20
|
/**
|
|
25
21
|
* Wait for the MaterialX WASM module to be ready.
|
|
22
|
+
* @returns {Promise<void>}
|
|
26
23
|
*/
|
|
27
|
-
export async function ready()
|
|
24
|
+
export async function ready() {
|
|
28
25
|
if (state.materialXInitPromise) {
|
|
29
26
|
return state.materialXInitPromise;
|
|
30
27
|
}
|
|
@@ -33,18 +30,19 @@ export async function ready(): Promise<void> {
|
|
|
33
30
|
if (debug) console.log("[MaterialX] Initializing WASM module...");
|
|
34
31
|
try {
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
/** @type {Array<string>} */
|
|
34
|
+
const urls = await Promise.all([
|
|
37
35
|
/** @ts-ignore */
|
|
38
|
-
import(`../bin/JsMaterialXCore.wasm?url`).then(m => m.default || m),
|
|
36
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXCore.wasm?url`).then(m => m.default || m),
|
|
39
37
|
/** @ts-ignore */
|
|
40
|
-
import(`../bin/JsMaterialXGenShader.wasm?url`).then(m => m.default || m),
|
|
38
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXGenShader.wasm?url`).then(m => m.default || m),
|
|
41
39
|
/** @ts-ignore */
|
|
42
|
-
import(`../bin/JsMaterialXGenShader.data.txt?url`).then(m => m.default || m),
|
|
40
|
+
import( /* @vite-ignore */ `../bin/JsMaterialXGenShader.data.txt?url`).then(m => m.default || m),
|
|
43
41
|
]);
|
|
44
42
|
const [JsMaterialXCore, JsMaterialXGenShader, JsMaterialXGenShader_data] = urls;
|
|
45
43
|
|
|
46
44
|
const module = await MaterialX({
|
|
47
|
-
locateFile: (
|
|
45
|
+
locateFile: (/** @type {string} */ path, /** @type {string} */ scriptDirectory) => {
|
|
48
46
|
if (debug) console.debug("[MaterialX] locateFile called:", { path, scriptDirectory });
|
|
49
47
|
|
|
50
48
|
if (path.includes("JsMaterialXCore.wasm")) {
|
|
@@ -61,7 +59,7 @@ export async function ready(): Promise<void> {
|
|
|
61
59
|
},
|
|
62
60
|
});
|
|
63
61
|
if (debug) console.log("[MaterialX] module loaded", module);
|
|
64
|
-
state.materialXModule =
|
|
62
|
+
state.materialXModule = /** @type {import("./materialx.types.js").MaterialX.MODULE} */ (module);
|
|
65
63
|
|
|
66
64
|
// Initialize shader generator and context
|
|
67
65
|
state.materialXGenerator = module.EsslShaderGenerator.create();
|
|
@@ -95,6 +93,8 @@ export async function ready(): Promise<void> {
|
|
|
95
93
|
// Set a reasonable default for max active lights
|
|
96
94
|
state.materialXGenContext.getOptions().hwMaxActiveLightSources = 4;
|
|
97
95
|
|
|
96
|
+
// state.materialXGenContext.getOptions().hwShadowMap = true;
|
|
97
|
+
|
|
98
98
|
// This prewarms the shader generation context to have all light types
|
|
99
99
|
await registerLights(state.materialXModule, state.materialXGenContext);
|
|
100
100
|
|
|
@@ -106,45 +106,69 @@ export async function ready(): Promise<void> {
|
|
|
106
106
|
})();
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
109
|
+
/**
|
|
110
|
+
* @typedef {Object} EnvironmentTextureSet
|
|
111
|
+
* @property {Texture | null} radianceTexture
|
|
112
|
+
* @property {Texture | null} irradianceTexture
|
|
113
|
+
*/
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* MaterialXEnvironment manages the environment settings for MaterialX materials.
|
|
117
117
|
*/
|
|
118
118
|
export class MaterialXEnvironment {
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
/**
|
|
121
|
+
* @param {Scene} scene
|
|
122
|
+
* @returns {MaterialXEnvironment | null}
|
|
123
|
+
*/
|
|
124
|
+
static get(scene) {
|
|
121
125
|
return this.getEnvironment(scene);
|
|
122
126
|
}
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
/** @type {WeakMap<Scene, MaterialXEnvironment>} */
|
|
129
|
+
static _environments = new Map();
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {Scene} scene
|
|
133
|
+
* @returns {MaterialXEnvironment}
|
|
134
|
+
*/
|
|
135
|
+
static getEnvironment(scene) {
|
|
125
136
|
if (this._environments.has(scene)) {
|
|
126
|
-
return this._environments.get(scene)
|
|
137
|
+
return /** @type {MaterialXEnvironment} */ (this._environments.get(scene));
|
|
127
138
|
}
|
|
128
139
|
const env = new MaterialXEnvironment(scene);
|
|
129
140
|
this._environments.set(scene, env);
|
|
130
141
|
return env;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
/** @type {Array<Light>} */
|
|
145
|
+
_lights = [];
|
|
146
|
+
/** @type {import("./materialx.helper.js").LightData[] | null} */
|
|
147
|
+
_lightData = null;
|
|
148
|
+
/** @type {number} */
|
|
149
|
+
_lightCount = 0;
|
|
150
|
+
|
|
151
|
+
/** @type {Promise<boolean> | null} */
|
|
152
|
+
_initializePromise = null;
|
|
153
|
+
/** @type {boolean} */
|
|
154
|
+
_isInitialized = false;
|
|
155
|
+
/** @type {number} */
|
|
156
|
+
_lastUpdateFrame = -1;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {Scene} _scene
|
|
160
|
+
*/
|
|
161
|
+
constructor(_scene) {
|
|
162
|
+
this._scene = _scene;
|
|
143
163
|
if (debug) console.log("[MaterialX] Environment created");
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Initialize with Needle Engine context
|
|
168
|
+
* @param {WebGLRenderer} renderer
|
|
169
|
+
* @returns {Promise<boolean>}
|
|
170
|
+
*/
|
|
171
|
+
async initialize(renderer) {
|
|
148
172
|
if (this._initializePromise) {
|
|
149
173
|
return this._initializePromise;
|
|
150
174
|
}
|
|
@@ -152,7 +176,12 @@ export class MaterialXEnvironment {
|
|
|
152
176
|
return this._initializePromise;
|
|
153
177
|
}
|
|
154
178
|
|
|
155
|
-
|
|
179
|
+
/**
|
|
180
|
+
* @param {number} frame
|
|
181
|
+
* @param {Scene} scene
|
|
182
|
+
* @param {WebGLRenderer} renderer
|
|
183
|
+
*/
|
|
184
|
+
update(frame, scene, renderer) {
|
|
156
185
|
if (!this._initializePromise) {
|
|
157
186
|
this.initialize(renderer);
|
|
158
187
|
return;
|
|
@@ -179,13 +208,15 @@ export class MaterialXEnvironment {
|
|
|
179
208
|
const radianceCube = new Mesh(planeGeometry, radianceMat);
|
|
180
209
|
const irradianceMat = unlitMat.clone();
|
|
181
210
|
irradianceMat.map = textures.irradianceTexture;
|
|
182
|
-
const irradianceCube = new Mesh(planeGeometry,
|
|
211
|
+
const irradianceCube = new Mesh(planeGeometry, irradianceMat);
|
|
183
212
|
scene.add(radianceCube);
|
|
184
213
|
scene.add(irradianceCube);
|
|
185
214
|
radianceCube.name = "MaterialXRadianceCube";
|
|
186
|
-
radianceCube.position.set(.
|
|
215
|
+
radianceCube.position.set(.8, 1, .01);
|
|
216
|
+
radianceCube.scale.set(1.5, 1, 1);
|
|
187
217
|
irradianceCube.name = "MaterialXIrradianceCube";
|
|
188
|
-
irradianceCube.position.set(-.
|
|
218
|
+
irradianceCube.position.set(-.8, 1, -.01);
|
|
219
|
+
irradianceCube.scale.set(1.5, 0.98, 1);
|
|
189
220
|
console.log("[MaterialX] environment initialized from Needle context", { textures, radianceCube, irradianceCube });
|
|
190
221
|
}
|
|
191
222
|
}
|
|
@@ -209,11 +240,18 @@ export class MaterialXEnvironment {
|
|
|
209
240
|
this._texturesCache.clear();
|
|
210
241
|
}
|
|
211
242
|
|
|
243
|
+
get lights() {
|
|
244
|
+
return this._lights;
|
|
245
|
+
}
|
|
212
246
|
get lightData() {
|
|
213
247
|
return this._lightData;
|
|
214
248
|
}
|
|
215
249
|
get lightCount() { return this._lightCount || 0; }
|
|
216
|
-
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {import("./materialx.material.js").MaterialXMaterial} material
|
|
253
|
+
*/
|
|
254
|
+
getTextures(material) {
|
|
217
255
|
if (material.envMap) {
|
|
218
256
|
// If the material has its own envMap, we don't use the irradiance texture
|
|
219
257
|
return this._getTextures(material.envMap);
|
|
@@ -221,11 +259,18 @@ export class MaterialXEnvironment {
|
|
|
221
259
|
return this._getTextures(this._scene.environment);
|
|
222
260
|
}
|
|
223
261
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
262
|
+
/** @type {PMREMGenerator | null} */
|
|
263
|
+
_pmremGenerator = null;
|
|
264
|
+
/** @type {WebGLRenderer | null} */
|
|
265
|
+
_renderer = null;
|
|
266
|
+
/** @type {Map<Texture | null, EnvironmentTextureSet>} */
|
|
267
|
+
_texturesCache = new Map();
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {WebGLRenderer} renderer
|
|
271
|
+
* @returns {Promise<boolean>}
|
|
272
|
+
*/
|
|
273
|
+
async _initialize(renderer) {
|
|
229
274
|
this._isInitialized = false;
|
|
230
275
|
this._pmremGenerator = new PMREMGenerator(renderer);
|
|
231
276
|
this._renderer = renderer;
|
|
@@ -234,11 +279,13 @@ export class MaterialXEnvironment {
|
|
|
234
279
|
return true;
|
|
235
280
|
}
|
|
236
281
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
282
|
+
/**
|
|
283
|
+
* @param {Texture | null | undefined} texture
|
|
284
|
+
* @returns {{radianceTexture: Texture | null, irradianceTexture: Texture | null}}
|
|
285
|
+
*/
|
|
286
|
+
_getTextures(texture) {
|
|
287
|
+
/** @type {EnvironmentTextureSet | undefined} */
|
|
288
|
+
let res = this._texturesCache.get(texture || null);
|
|
242
289
|
if (res) {
|
|
243
290
|
return res;
|
|
244
291
|
}
|
|
@@ -264,14 +311,18 @@ export class MaterialXEnvironment {
|
|
|
264
311
|
return res;
|
|
265
312
|
}
|
|
266
313
|
|
|
267
|
-
|
|
314
|
+
/**
|
|
315
|
+
* @param {boolean} collectLights
|
|
316
|
+
*/
|
|
317
|
+
updateLighting = (collectLights = false) => {
|
|
268
318
|
if (!this._scene) return;
|
|
269
319
|
// Find lights in scene
|
|
270
320
|
if (collectLights) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
321
|
+
/** @type {Array<Light>} */
|
|
322
|
+
const lights = new Array();
|
|
323
|
+
this._scene.traverse((/** @type {Object3D} */ object) => {
|
|
324
|
+
if ((/** @type {Light} */ (object)).isLight && object.visible)
|
|
325
|
+
lights.push(/** @type {Light} */ (object));
|
|
275
326
|
});
|
|
276
327
|
this._lights = lights;
|
|
277
328
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BufferGeometry, Camera, Group, IUniform, MaterialParameters, Object3D, Scene, ShaderMaterial, Texture, WebGLRenderer } from "three";
|
|
2
|
+
import { MaterialXContext, MaterialXEnvironment } from "./materialx.js";
|
|
3
|
+
import { Callbacks } from "./materialx.helper.js";
|
|
4
|
+
|
|
5
|
+
declare type MaterialXMaterialInitParameters = {
|
|
6
|
+
name: string;
|
|
7
|
+
shaderName?: string | null;
|
|
8
|
+
shader: any;
|
|
9
|
+
loaders: Callbacks;
|
|
10
|
+
context: MaterialXContext;
|
|
11
|
+
parameters?: MaterialParameters;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Uniforms = Record<string, IUniform & { needsUpdate?: boolean }>;
|
|
16
|
+
type Precision = "highp" | "mediump" | "lowp";
|
|
17
|
+
|
|
18
|
+
export declare class MaterialXMaterial extends ShaderMaterial {
|
|
19
|
+
readonly shaderName: string | null;
|
|
20
|
+
|
|
21
|
+
copy(source: MaterialXMaterial): this;
|
|
22
|
+
|
|
23
|
+
private _context: MaterialXContext | null;
|
|
24
|
+
private _shader: any;
|
|
25
|
+
private _needsTangents: boolean;
|
|
26
|
+
|
|
27
|
+
constructor(init?: MaterialXMaterialInitParameters);
|
|
28
|
+
|
|
29
|
+
private _missingTangentsWarned: boolean;
|
|
30
|
+
onBeforeRender(renderer: WebGLRenderer, _scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D, _group: Group): void;
|
|
31
|
+
|
|
32
|
+
envMapIntensity: number;
|
|
33
|
+
envMap: Texture | null;
|
|
34
|
+
updateUniforms(environment: MaterialXEnvironment, _renderer: WebGLRenderer, object: Object3D, camera: Camera, time?: number, frame?: number): void;
|
|
35
|
+
|
|
36
|
+
private updateEnvironmentUniforms(environment: MaterialXEnvironment): void;
|
|
37
|
+
}
|
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
import { BufferGeometry, Camera, FrontSide, GLSL3, Group,
|
|
1
|
+
import { BufferGeometry, Camera, FrontSide, GLSL3, Group, Matrix3, Matrix4, Object3D, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
|
|
2
2
|
import { debug, getFrame, getTime } from "./utils.js";
|
|
3
|
-
import {
|
|
4
|
-
import { generateMaterialPropertiesForUniforms, getUniformValues
|
|
3
|
+
import { MaterialXEnvironment } from "./materialx.js";
|
|
4
|
+
import { generateMaterialPropertiesForUniforms, getUniformValues } from "./materialx.helper.js";
|
|
5
5
|
import { cloneUniforms, cloneUniformsGroups } from "three/src/renderers/shaders/UniformsUtils.js";
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
// Add helper matrices for uniform updates (similar to MaterialX example)
|
|
9
8
|
const normalMat = new Matrix3();
|
|
10
9
|
const worldViewPos = new Vector3();
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} MaterialXMaterialInitParameters
|
|
13
|
+
* @property {string} name
|
|
14
|
+
* @property {string | null} [shaderName] - Optional name of the shader
|
|
15
|
+
* @property {any} shader
|
|
16
|
+
* @property {import('./materialx.helper.js').Callbacks} loaders
|
|
17
|
+
* @property {import('./materialx.js').MaterialXContext} context
|
|
18
|
+
* @property {import('three').MaterialParameters} [parameters] - Optional parameters
|
|
19
|
+
* @property {boolean} [debug] - Debug flag
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {"highp" | "mediump" | "lowp"} Precision
|
|
24
|
+
*/
|
|
24
25
|
|
|
25
26
|
export class MaterialXMaterial extends ShaderMaterial {
|
|
26
27
|
|
|
27
|
-
/** The original name of the shader
|
|
28
|
-
|
|
28
|
+
/** The original name of the shader
|
|
29
|
+
* @type {string | null} */
|
|
30
|
+
shaderName = null;
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @param {MaterialXMaterial} source
|
|
34
|
+
* @returns {this}
|
|
35
|
+
*/
|
|
36
|
+
copy(source) {
|
|
31
37
|
super.copy(source);
|
|
32
38
|
this._context = source._context;
|
|
33
39
|
this._shader = source._shader;
|
|
34
|
-
this.uniforms = cloneUniforms(source.uniforms)
|
|
40
|
+
this.uniforms = cloneUniforms(source.uniforms);
|
|
35
41
|
this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups);
|
|
36
42
|
this.envMapIntensity = source.envMapIntensity;
|
|
37
43
|
this.envMap = source.envMap;
|
|
@@ -41,10 +47,17 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
41
47
|
return this;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
/** @type {import('./materialx.js').MaterialXContext | null} */
|
|
51
|
+
_context = null;
|
|
52
|
+
/** @type {any} */
|
|
53
|
+
_shader = null;
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
_needsTangents = false;
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
/**
|
|
58
|
+
* @param {MaterialXMaterialInitParameters} [init]
|
|
59
|
+
*/
|
|
60
|
+
constructor(init) {
|
|
48
61
|
|
|
49
62
|
// TODO: we need to properly copy the uniforms and other properties from the source material
|
|
50
63
|
if (!init) {
|
|
@@ -75,7 +88,8 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
75
88
|
vertexShader = vertexShader.replace(/\bi_color_0\b/g, 'color');
|
|
76
89
|
|
|
77
90
|
// Patch fragmentShader
|
|
78
|
-
const precision = init.parameters?.precision || "highp"
|
|
91
|
+
const precision = init.parameters?.precision || "highp";
|
|
92
|
+
vertexShader = vertexShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
79
93
|
fragmentShader = fragmentShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
80
94
|
fragmentShader = fragmentShader.replace(/#define M_FLOAT_EPS 1e-8/g, precision === "highp" ? `#define M_FLOAT_EPS 1e-8` : `#define M_FLOAT_EPS 1e-3`);
|
|
81
95
|
|
|
@@ -88,9 +102,16 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
88
102
|
fragmentShader = fragmentShader.replace(/\bi_tangent\b/g, 'tangent');
|
|
89
103
|
fragmentShader = fragmentShader.replace(/\bi_color_0\b/g, 'color');
|
|
90
104
|
|
|
105
|
+
// Capture some vertex shader properties
|
|
106
|
+
const uv_is_vec2 = vertexShader.includes('in vec2 uv;'); // check if uv is vec2; e.g. https://matlib.gpuopen.com/main/materials/all?material=da6ec531-f5c1-4790-ac14-8a5c51d0314e
|
|
107
|
+
const uv1_is_vec2 = vertexShader.includes('in vec2 uv1;');
|
|
108
|
+
const uv2_is_vec2 = vertexShader.includes('in vec2 uv2;');
|
|
109
|
+
const uv3_is_vec2 = vertexShader.includes('in vec2 uv3;');
|
|
110
|
+
|
|
91
111
|
// Remove `in vec3 position;` and so on since they're already declared by ShaderMaterial
|
|
92
112
|
vertexShader = vertexShader.replace(/in\s+vec3\s+position;/g, '');
|
|
93
113
|
vertexShader = vertexShader.replace(/in\s+vec3\s+normal;/g, '');
|
|
114
|
+
vertexShader = vertexShader.replace(/in\s+vec2\s+uv;/g, '');
|
|
94
115
|
vertexShader = vertexShader.replace(/in\s+vec3\s+uv;/g, '');
|
|
95
116
|
var hasUv1 = vertexShader.includes('in vec3 uv1;');
|
|
96
117
|
vertexShader = vertexShader.replace(/in\s+vec3\s+uv1;/g, '');
|
|
@@ -105,10 +126,10 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
105
126
|
|
|
106
127
|
// Patch uv 2-component to 3-component (`texcoord_0 = uv;` needs to be replaced with `texcoord_0 = vec3(uv, 0.0);`)
|
|
107
128
|
// TODO what if we actually have a 3-component UV? Not sure what three.js does then
|
|
108
|
-
vertexShader = vertexShader.replace(/texcoord_0 = uv;/g, 'texcoord_0 = vec3(uv, 0.0);');
|
|
109
|
-
vertexShader = vertexShader.replace(/texcoord_1 = uv1;/g, 'texcoord_1 = vec3(uv1, 0.0);');
|
|
110
|
-
vertexShader = vertexShader.replace(/texcoord_2 = uv2;/g, 'texcoord_2 = vec3(uv2, 0.0);');
|
|
111
|
-
vertexShader = vertexShader.replace(/texcoord_3 = uv3;/g, 'texcoord_3 = vec3(uv3, 0.0);');
|
|
129
|
+
if (!uv_is_vec2) vertexShader = vertexShader.replace(/texcoord_0 = uv;/g, 'texcoord_0 = vec3(uv, 0.0);');
|
|
130
|
+
if (!uv1_is_vec2) vertexShader = vertexShader.replace(/texcoord_1 = uv1;/g, 'texcoord_1 = vec3(uv1, 0.0);');
|
|
131
|
+
if (!uv2_is_vec2) vertexShader = vertexShader.replace(/texcoord_2 = uv2;/g, 'texcoord_2 = vec3(uv2, 0.0);');
|
|
132
|
+
if (!uv3_is_vec2) vertexShader = vertexShader.replace(/texcoord_3 = uv3;/g, 'texcoord_3 = vec3(uv3, 0.0);');
|
|
112
133
|
|
|
113
134
|
// Patch units – seems MaterialX uses different units and we end up with wrong light values?
|
|
114
135
|
// result.direction = light.position - position;
|
|
@@ -128,7 +149,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
128
149
|
#include <tonemapping_fragment>
|
|
129
150
|
#include <colorspace_fragment>`);
|
|
130
151
|
|
|
131
|
-
const defines
|
|
152
|
+
const defines = {};
|
|
132
153
|
if (hasUv1) defines['USE_UV1'] = '';
|
|
133
154
|
if (hasUv2) defines['USE_UV2'] = '';
|
|
134
155
|
if (hasUv3) defines['USE_UV3'] = '';
|
|
@@ -148,9 +169,10 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
148
169
|
defines: defines,
|
|
149
170
|
...init.parameters, // Spread any additional parameters passed to the material
|
|
150
171
|
});
|
|
172
|
+
this.shaderName = init.shaderName || null;
|
|
151
173
|
this._context = init.context;
|
|
152
174
|
this._shader = init.shader;
|
|
153
|
-
this.
|
|
175
|
+
this._needsTangents = vertexShader.includes('in vec4 tangent;') || vertexShader.includes('in vec3 tangent;');
|
|
154
176
|
|
|
155
177
|
Object.assign(this.uniforms, {
|
|
156
178
|
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath),
|
|
@@ -161,6 +183,9 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
161
183
|
u_viewPosition: { value: new Vector3() },
|
|
162
184
|
u_worldInverseTransposeMatrix: { value: new Matrix4() },
|
|
163
185
|
|
|
186
|
+
// u_shadowMatrix: { value: new Matrix4() },
|
|
187
|
+
// u_shadowMap: { value: null, type: 't' }, // Shadow map
|
|
188
|
+
|
|
164
189
|
u_envMatrix: { value: new Matrix4() },
|
|
165
190
|
u_envRadiance: { value: null, type: 't' },
|
|
166
191
|
u_envRadianceMips: { value: 8, type: 'i' },
|
|
@@ -176,7 +201,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
176
201
|
generateMaterialPropertiesForUniforms(this, init.shader.getStage('vertex'));
|
|
177
202
|
|
|
178
203
|
|
|
179
|
-
if (debug) {
|
|
204
|
+
if (debug || init.debug) {
|
|
180
205
|
// Get lighting and environment data from MaterialX environment
|
|
181
206
|
console.group("[MaterialX]: ", this.name);
|
|
182
207
|
console.log(`Vertex shader length: ${vertexShader.length}\n`, vertexShader);
|
|
@@ -185,22 +210,50 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
185
210
|
}
|
|
186
211
|
}
|
|
187
212
|
|
|
188
|
-
|
|
213
|
+
/** @type {boolean} */
|
|
214
|
+
_missingTangentsWarned = false;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {WebGLRenderer} renderer
|
|
218
|
+
* @param {Scene} _scene
|
|
219
|
+
* @param {Camera} camera
|
|
220
|
+
* @param {BufferGeometry} geometry
|
|
221
|
+
* @param {Object3D} object
|
|
222
|
+
* @param {Group} _group
|
|
223
|
+
*/
|
|
224
|
+
onBeforeRender(renderer, _scene, camera, geometry, object, _group) {
|
|
225
|
+
if (this._needsTangents && !geometry.attributes.tangent) {
|
|
226
|
+
if (!this._missingTangentsWarned) {
|
|
227
|
+
this._missingTangentsWarned = true;
|
|
228
|
+
console.warn(`[MaterialX] Tangents are required for this material (${this.name}) but not present in the geometry.`);
|
|
229
|
+
// TODO: can we compute tangents here?
|
|
230
|
+
}
|
|
231
|
+
}
|
|
189
232
|
const time = this._context?.getTime?.() || getTime();
|
|
190
233
|
const frame = this._context?.getFrame?.() || getFrame();
|
|
191
234
|
const env = MaterialXEnvironment.get(_scene);
|
|
192
235
|
if (env) {
|
|
193
|
-
env.update(frame, _scene,
|
|
194
|
-
this.updateUniforms(env, object, camera, time, frame);
|
|
236
|
+
env.update(frame, _scene, renderer);
|
|
237
|
+
this.updateUniforms(env, renderer, object, camera, time, frame);
|
|
195
238
|
}
|
|
196
239
|
}
|
|
197
240
|
|
|
241
|
+
/** @type {number} */
|
|
242
|
+
envMapIntensity = 1.0; // Default intensity for environment map
|
|
243
|
+
/** @type {Texture | null} */
|
|
244
|
+
envMap = null; // Environment map texture, can be set externally
|
|
198
245
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
246
|
+
/**
|
|
247
|
+
* @param {MaterialXEnvironment} environment
|
|
248
|
+
* @param {WebGLRenderer} _renderer
|
|
249
|
+
* @param {Object3D} object
|
|
250
|
+
* @param {Camera} camera
|
|
251
|
+
* @param {number} [time]
|
|
252
|
+
* @param {number} [frame]
|
|
253
|
+
*/
|
|
254
|
+
updateUniforms = (environment, _renderer, object, camera, time, frame) => {
|
|
202
255
|
|
|
203
|
-
const uniforms = this.uniforms
|
|
256
|
+
const uniforms = this.uniforms;
|
|
204
257
|
|
|
205
258
|
// Update standard transformation matrices
|
|
206
259
|
if (uniforms.u_worldMatrix) {
|
|
@@ -213,15 +266,23 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
213
266
|
uniforms.u_viewProjectionMatrix.needsUpdate = true;
|
|
214
267
|
}
|
|
215
268
|
|
|
269
|
+
if (uniforms.u_worldInverseTransposeMatrix) {
|
|
270
|
+
uniforms.u_worldInverseTransposeMatrix.value.setFromMatrix3(normalMat.getNormalMatrix(object.matrixWorld));
|
|
271
|
+
uniforms.u_worldInverseTransposeMatrix.needsUpdate = true;
|
|
272
|
+
}
|
|
273
|
+
|
|
216
274
|
if (uniforms.u_viewPosition) {
|
|
217
275
|
uniforms.u_viewPosition.value.copy(camera.getWorldPosition(worldViewPos));
|
|
218
276
|
uniforms.u_viewPosition.needsUpdate = true;
|
|
219
277
|
}
|
|
220
278
|
|
|
221
|
-
if (uniforms.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
279
|
+
// if (uniforms.u_shadowMap) {
|
|
280
|
+
// const light = environment.lights?.[2] || null;
|
|
281
|
+
// uniforms.u_shadowMatrix.value = light?.shadow?.matrix.clone().premultiply(object.matrixWorld.clone()).invert();
|
|
282
|
+
// uniforms.u_shadowMap.value = light.shadow?.map || null;
|
|
283
|
+
// uniforms.u_shadowMap.needsUpdate = true;
|
|
284
|
+
// console.log("[MaterialX] Renderer shadow map updated", light);
|
|
285
|
+
// }
|
|
225
286
|
|
|
226
287
|
// Update time uniforms
|
|
227
288
|
if (uniforms.u_time) {
|
|
@@ -239,9 +300,13 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
239
300
|
this.uniformsNeedUpdate = true;
|
|
240
301
|
}
|
|
241
302
|
|
|
242
|
-
|
|
303
|
+
/**
|
|
304
|
+
* @private
|
|
305
|
+
* @param {MaterialXEnvironment} environment
|
|
306
|
+
*/
|
|
307
|
+
updateEnvironmentUniforms = (environment) => {
|
|
243
308
|
|
|
244
|
-
const uniforms = this.uniforms
|
|
309
|
+
const uniforms = this.uniforms;
|
|
245
310
|
|
|
246
311
|
// Get lighting data from environment
|
|
247
312
|
const lightData = environment.lightData || null;
|
package/src/utils.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get URL parameter value
|
|
3
|
+
*/
|
|
4
|
+
export function getParam(name: string): boolean | string;
|
|
5
|
+
|
|
6
|
+
export const debug: boolean | string;
|
|
7
|
+
export const debugUpdate: boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get current time in seconds
|
|
11
|
+
*/
|
|
12
|
+
export function getTime(): number;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get current frame number
|
|
16
|
+
*/
|
|
17
|
+
export function getFrame(): number;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
// import { BufferGeometry } from 'three';
|
|
2
|
+
// import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get URL parameter value
|
|
6
|
+
* @param {string} name - Parameter name
|
|
7
|
+
* @returns {boolean|string} Parameter value or false if not found
|
|
8
|
+
*/
|
|
9
|
+
export function getParam(name) {
|
|
2
10
|
const urlParams = new URLSearchParams(window.location.search);
|
|
3
11
|
const param = urlParams.get(name);
|
|
4
12
|
if (param == null || param === "0" || param === "false") return false;
|
|
@@ -9,21 +17,30 @@ export function getParam(name: string) {
|
|
|
9
17
|
export const debug = getParam("debugmaterialx");
|
|
10
18
|
export const debugUpdate = debug === "update";
|
|
11
19
|
|
|
12
|
-
|
|
13
20
|
let time = 0;
|
|
21
|
+
/**
|
|
22
|
+
* Get current time in seconds
|
|
23
|
+
* @returns {number} Current time
|
|
24
|
+
*/
|
|
14
25
|
export function getTime() {
|
|
15
26
|
return time;
|
|
16
27
|
}
|
|
28
|
+
|
|
17
29
|
let frame = 0;
|
|
30
|
+
/**
|
|
31
|
+
* Get current frame number
|
|
32
|
+
* @returns {number} Current frame
|
|
33
|
+
*/
|
|
18
34
|
export function getFrame() {
|
|
19
35
|
return frame;
|
|
20
36
|
}
|
|
21
37
|
|
|
22
|
-
const performance = window.performance ||
|
|
38
|
+
const performance = window.performance || /** @type {any} */ (window).webkitPerformance || /** @type {any} */ (window).mozPerformance;
|
|
39
|
+
|
|
23
40
|
function updateTime() {
|
|
24
41
|
time = performance.now() / 1000; // Convert to seconds
|
|
25
42
|
frame++;
|
|
26
43
|
window.requestAnimationFrame(updateTime);
|
|
27
44
|
}
|
|
28
|
-
window.requestAnimationFrame(updateTime);
|
|
29
45
|
|
|
46
|
+
window.requestAnimationFrame(updateTime);
|