@needle-tools/materialx 1.1.1 → 1.2.0
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/CHANGELOG.md +6 -0
- package/bin/.gitattributes +4 -0
- package/bin/README.md +6 -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 +24 -10
- 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 +71 -0
- package/src/loader/loader.three.js +334 -0
- package/src/materialx.d.ts +60 -0
- package/src/materialx.helper.d.ts +31 -0
- package/src/{materialx.helper.ts → materialx.helper.js} +149 -101
- package/src/{materialx.ts → materialx.js} +115 -64
- package/src/materialx.material.d.ts +37 -0
- package/src/{materialx.material.ts → materialx.material.js} +110 -40
- 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 -322
- package/tsconfig.json +0 -20
|
@@ -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,33 +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
|
-
|
|
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
|
+
*/
|
|
23
25
|
|
|
24
26
|
export class MaterialXMaterial extends ShaderMaterial {
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
/** The original name of the shader
|
|
29
|
+
* @type {string | null} */
|
|
30
|
+
shaderName = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {MaterialXMaterial} source
|
|
34
|
+
* @returns {this}
|
|
35
|
+
*/
|
|
36
|
+
copy(source) {
|
|
27
37
|
super.copy(source);
|
|
28
38
|
this._context = source._context;
|
|
29
39
|
this._shader = source._shader;
|
|
30
|
-
this.uniforms = cloneUniforms(source.uniforms)
|
|
40
|
+
this.uniforms = cloneUniforms(source.uniforms);
|
|
31
41
|
this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups);
|
|
32
42
|
this.envMapIntensity = source.envMapIntensity;
|
|
33
43
|
this.envMap = source.envMap;
|
|
@@ -37,10 +47,17 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
37
47
|
return this;
|
|
38
48
|
}
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
/** @type {import('./materialx.js').MaterialXContext | null} */
|
|
51
|
+
_context = null;
|
|
52
|
+
/** @type {any} */
|
|
53
|
+
_shader = null;
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
_needsTangents = false;
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
/**
|
|
58
|
+
* @param {MaterialXMaterialInitParameters} [init]
|
|
59
|
+
*/
|
|
60
|
+
constructor(init) {
|
|
44
61
|
|
|
45
62
|
// TODO: we need to properly copy the uniforms and other properties from the source material
|
|
46
63
|
if (!init) {
|
|
@@ -71,7 +88,8 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
71
88
|
vertexShader = vertexShader.replace(/\bi_color_0\b/g, 'color');
|
|
72
89
|
|
|
73
90
|
// Patch fragmentShader
|
|
74
|
-
const precision = init.parameters?.precision || "highp"
|
|
91
|
+
const precision = init.parameters?.precision || "highp";
|
|
92
|
+
vertexShader = vertexShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
75
93
|
fragmentShader = fragmentShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
76
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`);
|
|
77
95
|
|
|
@@ -84,9 +102,16 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
84
102
|
fragmentShader = fragmentShader.replace(/\bi_tangent\b/g, 'tangent');
|
|
85
103
|
fragmentShader = fragmentShader.replace(/\bi_color_0\b/g, 'color');
|
|
86
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
|
+
|
|
87
111
|
// Remove `in vec3 position;` and so on since they're already declared by ShaderMaterial
|
|
88
112
|
vertexShader = vertexShader.replace(/in\s+vec3\s+position;/g, '');
|
|
89
113
|
vertexShader = vertexShader.replace(/in\s+vec3\s+normal;/g, '');
|
|
114
|
+
vertexShader = vertexShader.replace(/in\s+vec2\s+uv;/g, '');
|
|
90
115
|
vertexShader = vertexShader.replace(/in\s+vec3\s+uv;/g, '');
|
|
91
116
|
var hasUv1 = vertexShader.includes('in vec3 uv1;');
|
|
92
117
|
vertexShader = vertexShader.replace(/in\s+vec3\s+uv1;/g, '');
|
|
@@ -101,10 +126,10 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
101
126
|
|
|
102
127
|
// Patch uv 2-component to 3-component (`texcoord_0 = uv;` needs to be replaced with `texcoord_0 = vec3(uv, 0.0);`)
|
|
103
128
|
// TODO what if we actually have a 3-component UV? Not sure what three.js does then
|
|
104
|
-
vertexShader = vertexShader.replace(/texcoord_0 = uv;/g, 'texcoord_0 = vec3(uv, 0.0);');
|
|
105
|
-
vertexShader = vertexShader.replace(/texcoord_1 = uv1;/g, 'texcoord_1 = vec3(uv1, 0.0);');
|
|
106
|
-
vertexShader = vertexShader.replace(/texcoord_2 = uv2;/g, 'texcoord_2 = vec3(uv2, 0.0);');
|
|
107
|
-
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);');
|
|
108
133
|
|
|
109
134
|
// Patch units – seems MaterialX uses different units and we end up with wrong light values?
|
|
110
135
|
// result.direction = light.position - position;
|
|
@@ -124,7 +149,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
124
149
|
#include <tonemapping_fragment>
|
|
125
150
|
#include <colorspace_fragment>`);
|
|
126
151
|
|
|
127
|
-
const defines
|
|
152
|
+
const defines = {};
|
|
128
153
|
if (hasUv1) defines['USE_UV1'] = '';
|
|
129
154
|
if (hasUv2) defines['USE_UV2'] = '';
|
|
130
155
|
if (hasUv3) defines['USE_UV3'] = '';
|
|
@@ -144,8 +169,10 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
144
169
|
defines: defines,
|
|
145
170
|
...init.parameters, // Spread any additional parameters passed to the material
|
|
146
171
|
});
|
|
172
|
+
this.shaderName = init.shaderName || null;
|
|
147
173
|
this._context = init.context;
|
|
148
174
|
this._shader = init.shader;
|
|
175
|
+
this._needsTangents = vertexShader.includes('in vec4 tangent;') || vertexShader.includes('in vec3 tangent;');
|
|
149
176
|
|
|
150
177
|
Object.assign(this.uniforms, {
|
|
151
178
|
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath),
|
|
@@ -156,6 +183,9 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
156
183
|
u_viewPosition: { value: new Vector3() },
|
|
157
184
|
u_worldInverseTransposeMatrix: { value: new Matrix4() },
|
|
158
185
|
|
|
186
|
+
// u_shadowMatrix: { value: new Matrix4() },
|
|
187
|
+
// u_shadowMap: { value: null, type: 't' }, // Shadow map
|
|
188
|
+
|
|
159
189
|
u_envMatrix: { value: new Matrix4() },
|
|
160
190
|
u_envRadiance: { value: null, type: 't' },
|
|
161
191
|
u_envRadianceMips: { value: 8, type: 'i' },
|
|
@@ -171,7 +201,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
171
201
|
generateMaterialPropertiesForUniforms(this, init.shader.getStage('vertex'));
|
|
172
202
|
|
|
173
203
|
|
|
174
|
-
if (debug) {
|
|
204
|
+
if (debug || init.debug) {
|
|
175
205
|
// Get lighting and environment data from MaterialX environment
|
|
176
206
|
console.group("[MaterialX]: ", this.name);
|
|
177
207
|
console.log(`Vertex shader length: ${vertexShader.length}\n`, vertexShader);
|
|
@@ -180,22 +210,50 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
180
210
|
}
|
|
181
211
|
}
|
|
182
212
|
|
|
183
|
-
|
|
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
|
+
}
|
|
184
232
|
const time = this._context?.getTime?.() || getTime();
|
|
185
233
|
const frame = this._context?.getFrame?.() || getFrame();
|
|
186
234
|
const env = MaterialXEnvironment.get(_scene);
|
|
187
235
|
if (env) {
|
|
188
|
-
env.update(frame, _scene,
|
|
189
|
-
this.updateUniforms(env, object, camera, time, frame);
|
|
236
|
+
env.update(frame, _scene, renderer);
|
|
237
|
+
this.updateUniforms(env, renderer, object, camera, time, frame);
|
|
190
238
|
}
|
|
191
239
|
}
|
|
192
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
|
|
193
245
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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) => {
|
|
197
255
|
|
|
198
|
-
const uniforms = this.uniforms
|
|
256
|
+
const uniforms = this.uniforms;
|
|
199
257
|
|
|
200
258
|
// Update standard transformation matrices
|
|
201
259
|
if (uniforms.u_worldMatrix) {
|
|
@@ -208,15 +266,23 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
208
266
|
uniforms.u_viewProjectionMatrix.needsUpdate = true;
|
|
209
267
|
}
|
|
210
268
|
|
|
269
|
+
if (uniforms.u_worldInverseTransposeMatrix) {
|
|
270
|
+
uniforms.u_worldInverseTransposeMatrix.value.setFromMatrix3(normalMat.getNormalMatrix(object.matrixWorld));
|
|
271
|
+
uniforms.u_worldInverseTransposeMatrix.needsUpdate = true;
|
|
272
|
+
}
|
|
273
|
+
|
|
211
274
|
if (uniforms.u_viewPosition) {
|
|
212
275
|
uniforms.u_viewPosition.value.copy(camera.getWorldPosition(worldViewPos));
|
|
213
276
|
uniforms.u_viewPosition.needsUpdate = true;
|
|
214
277
|
}
|
|
215
278
|
|
|
216
|
-
if (uniforms.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
+
// }
|
|
220
286
|
|
|
221
287
|
// Update time uniforms
|
|
222
288
|
if (uniforms.u_time) {
|
|
@@ -234,9 +300,13 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
234
300
|
this.uniformsNeedUpdate = true;
|
|
235
301
|
}
|
|
236
302
|
|
|
237
|
-
|
|
303
|
+
/**
|
|
304
|
+
* @private
|
|
305
|
+
* @param {MaterialXEnvironment} environment
|
|
306
|
+
*/
|
|
307
|
+
updateEnvironmentUniforms = (environment) => {
|
|
238
308
|
|
|
239
|
-
const uniforms = this.uniforms
|
|
309
|
+
const uniforms = this.uniforms;
|
|
240
310
|
|
|
241
311
|
// Get lighting data from environment
|
|
242
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);
|