@needle-tools/materialx 1.1.0-next.bc1b608 → 1.1.1-next.5900297
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 +3 -0
- package/README.md +17 -3
- package/index.ts +1 -1
- package/needle.ts +1 -1
- package/package.json +2 -2
- package/src/loader/loader.needle.ts +9 -7
- package/src/loader/loader.three.ts +24 -8
- package/src/materialx.helper.ts +48 -3
- package/src/materialx.material.ts +36 -25
- package/src/materialx.ts +26 -27
- package/src/utils.ts +15 -65
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,9 @@ All notable changes to this package will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2025-07-15
|
|
8
|
+
- Add: `useNeedleMaterialX` hooks for vanilla three.js and Needle Engine
|
|
9
|
+
|
|
7
10
|
## [1.0.6] - 2025-07-15
|
|
8
11
|
- Fix: texture/environment sampling on some Android devices
|
|
9
12
|
|
package/README.md
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
# Needle MaterialX
|
|
2
2
|
|
|
3
|
-
Web runtime support to load and display MaterialX materials in Needle Engine
|
|
3
|
+
Web runtime support to load and display MaterialX materials in Needle Engine and three.js
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
`npm i @needle-tools/materialx`
|
|
7
7
|
|
|
8
|
+
## Examples
|
|
9
|
+
- [three.js Example on Stackblitz](https://stackblitz.com/edit/needle-materialx-example?file=main.js,package.json,index.html)
|
|
10
|
+
|
|
8
11
|
## How to use
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
### Use with Needle Engine
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { useNeedleMaterialX } from "@needle-tools/materialx/needle";
|
|
17
|
+
// Simply call this function in global scope as soon as possible
|
|
18
|
+
useNeedleMaterialX();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Use with three.js
|
|
11
22
|
|
|
12
23
|
```ts
|
|
13
|
-
import "@needle-tools/materialx"
|
|
24
|
+
import { useNeedleMaterialX } from "@needle-tools/materialx";
|
|
25
|
+
// Call the function with your GLTFLoader instance
|
|
26
|
+
useNeedleMaterialX(<yourGltfLoaderInstance>);
|
|
14
27
|
```
|
|
15
28
|
|
|
29
|
+
|
|
16
30
|
<br />
|
|
17
31
|
|
|
18
32
|
# Contact ✒️
|
package/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./src/index.js";
|
|
2
|
-
export {
|
|
2
|
+
export { useNeedleMaterialX } from "./src/loader/loader.three.js";
|
package/needle.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./src/index.js";
|
|
2
|
-
export {
|
|
2
|
+
export { useNeedleMaterialX } from "./src/loader/loader.needle.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/materialx",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1-next.5900297",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@needle-tools/engine": "4.x || ^4.6.0-0",
|
|
23
|
-
"three": "
|
|
23
|
+
"three": ">=0.169.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@needle-tools/engine": "4.x",
|
|
@@ -2,8 +2,9 @@ import { addCustomExtensionPlugin } from "@needle-tools/engine";
|
|
|
2
2
|
import { Context, GLTF, INeedleGLTFExtensionPlugin } from "@needle-tools/engine";
|
|
3
3
|
import type { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
4
|
import type { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useNeedleMaterialX as _useNeedleMaterialX, MaterialXLoader } from "./loader.three.js";
|
|
6
6
|
import { debug } from "../utils.js";
|
|
7
|
+
import { MaterialParameters } from "three";
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
@@ -13,13 +14,14 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
13
14
|
|
|
14
15
|
onImport = (loader: GLTFLoader, url: string, context: Context) => {
|
|
15
16
|
if (debug) console.log("MaterialXLoaderPlugin: Registering MaterialX extension for", url);
|
|
16
|
-
|
|
17
|
+
_useNeedleMaterialX(loader, {
|
|
18
|
+
cacheKey: url,
|
|
19
|
+
parameters: {
|
|
20
|
+
precision: context.renderer.capabilities.getMaxPrecision("highp") as MaterialParameters["precision"],
|
|
21
|
+
}
|
|
22
|
+
}, {
|
|
17
23
|
getTime: () => context.time.time,
|
|
18
24
|
getFrame: () => context.time.frame,
|
|
19
|
-
getScene: () => context.scene,
|
|
20
|
-
getRenderer: () => context.renderer,
|
|
21
|
-
}, {
|
|
22
|
-
cacheKey: url
|
|
23
25
|
});
|
|
24
26
|
};
|
|
25
27
|
|
|
@@ -36,6 +38,6 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
36
38
|
/**
|
|
37
39
|
* Add the MaterialXLoaderPlugin to the Needle Engine.
|
|
38
40
|
*/
|
|
39
|
-
export async function
|
|
41
|
+
export async function useNeedleMaterialX() {
|
|
40
42
|
addCustomExtensionPlugin(new MaterialXLoaderPlugin());
|
|
41
43
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Material, MeshStandardMaterial, DoubleSide, FrontSide } from "three";
|
|
1
|
+
import { Material, MeshStandardMaterial, DoubleSide, FrontSide, MaterialParameters } from "three";
|
|
2
2
|
import { GLTFLoader, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
3
|
import { ready, state, MaterialXContext } from "../materialx.js";
|
|
4
4
|
import { debug } from "../utils.js";
|
|
@@ -28,6 +28,14 @@ type MaterialDefinition = {
|
|
|
28
28
|
},
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// init.context.getRenderer().capabilities.getMaxPrecision("highp")
|
|
32
|
+
export type MaterialXLoaderOptions = {
|
|
33
|
+
/** The URL of the GLTF file being loaded */
|
|
34
|
+
cacheKey?: string;
|
|
35
|
+
/** Parameters for the MaterialX loader */
|
|
36
|
+
parameters?: Pick<MaterialParameters, "precision">;
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
// MaterialX loader extension for js GLTFLoader
|
|
32
40
|
export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
33
41
|
readonly name = "NEEDLE_materials_mtlx";
|
|
@@ -51,7 +59,11 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
51
59
|
* @param cacheKey The URL of the GLTF file
|
|
52
60
|
* @param context The context for the GLTF loading process
|
|
53
61
|
*/
|
|
54
|
-
constructor(
|
|
62
|
+
constructor(
|
|
63
|
+
private parser: GLTFParser,
|
|
64
|
+
private options: MaterialXLoaderOptions,
|
|
65
|
+
private context: MaterialXContext
|
|
66
|
+
) {
|
|
55
67
|
if (debug) console.log("MaterialXLoader created for parser");
|
|
56
68
|
// Start loading of MaterialX environment if the root extension exists
|
|
57
69
|
if (this.materialX_root_data) {
|
|
@@ -227,14 +239,18 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
227
239
|
const elementName = (renderableElement as any).getNamePath ? (renderableElement as any).getNamePath() : (renderableElement as any).getName();
|
|
228
240
|
|
|
229
241
|
const shader = state.materialXGenerator.generate(elementName, renderableElement, state.materialXGenContext);
|
|
242
|
+
|
|
230
243
|
const shaderMaterial = new MaterialXMaterial({
|
|
231
244
|
name: material_extension.name,
|
|
232
245
|
shader,
|
|
233
|
-
transparent: isTransparent,
|
|
234
|
-
side: material_def.doubleSided ? DoubleSide : FrontSide,
|
|
235
246
|
context: this.context,
|
|
247
|
+
parameters: {
|
|
248
|
+
transparent: isTransparent,
|
|
249
|
+
side: material_def.doubleSided ? DoubleSide : FrontSide,
|
|
250
|
+
...this.options.parameters,
|
|
251
|
+
},
|
|
236
252
|
loaders: {
|
|
237
|
-
cacheKey: this.cacheKey,
|
|
253
|
+
cacheKey: this.options.cacheKey || "",
|
|
238
254
|
getTexture: async url => {
|
|
239
255
|
// Find the index of the texture in the parser
|
|
240
256
|
const filenameWithoutExt = url.split('/').pop()?.split('.').shift() || '';
|
|
@@ -296,11 +312,11 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
296
312
|
|
|
297
313
|
|
|
298
314
|
/**
|
|
299
|
-
* Add the
|
|
315
|
+
* Add the MaterialXLoader to the GLTFLoader instance.
|
|
300
316
|
*/
|
|
301
|
-
export function
|
|
317
|
+
export function useNeedleMaterialX(loader: GLTFLoader, options?: MaterialXLoaderOptions, context?: MaterialXContext) {
|
|
302
318
|
loader.register(p => {
|
|
303
|
-
const loader = new MaterialXLoader(p, options
|
|
319
|
+
const loader = new MaterialXLoader(p, options || {}, context || {});
|
|
304
320
|
return loader;
|
|
305
321
|
});
|
|
306
322
|
}
|
package/src/materialx.helper.ts
CHANGED
|
@@ -466,12 +466,57 @@ export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Lo
|
|
|
466
466
|
for (let i = 0; i < uniforms.size(); ++i) {
|
|
467
467
|
const variable = uniforms.get(i);
|
|
468
468
|
const value = variable.getValue()?.getData();
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
const uniformName = variable.getVariable();
|
|
470
|
+
const type = variable.getType().getName();
|
|
471
|
+
if (debug) console.log("Adding uniform", { path: variable.getPath(), name: uniformName, value: value, type: type });
|
|
472
|
+
threeUniforms[uniformName] = toThreeUniform(uniforms, type, value, uniformName, loaders, searchPath);
|
|
472
473
|
}
|
|
473
474
|
}
|
|
474
475
|
}
|
|
475
476
|
|
|
476
477
|
return threeUniforms;
|
|
477
478
|
}
|
|
479
|
+
|
|
480
|
+
export function generateMaterialPropertiesForUniforms(material: THREE.ShaderMaterial, shaderStage: MaterialX.ShaderStage) {
|
|
481
|
+
|
|
482
|
+
const uniformBlocks = shaderStage.getUniformBlocks()
|
|
483
|
+
for (const [blockName, uniforms] of Object.entries(uniformBlocks)) {
|
|
484
|
+
// Seems struct uniforms (like in LightData) end up here as well, we should filter those out.
|
|
485
|
+
if (blockName === "LightData") continue;
|
|
486
|
+
|
|
487
|
+
if (!uniforms.empty()) {
|
|
488
|
+
for (let i = 0; i < uniforms.size(); ++i) {
|
|
489
|
+
const variable = uniforms.get(i);
|
|
490
|
+
const uniformName = variable.getVariable();
|
|
491
|
+
let key = variable.getPath().split('/').pop();
|
|
492
|
+
switch (key) {
|
|
493
|
+
case "_Color":
|
|
494
|
+
key = "color";
|
|
495
|
+
break;
|
|
496
|
+
case "_Roughness":
|
|
497
|
+
key = "roughness";
|
|
498
|
+
break;
|
|
499
|
+
case "_Metallic":
|
|
500
|
+
key = "metalness";
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
if (key) {
|
|
504
|
+
Object.defineProperty(material, key, {
|
|
505
|
+
get: function () {
|
|
506
|
+
return this.uniforms?.[uniformName].value
|
|
507
|
+
},
|
|
508
|
+
set: function (v) {
|
|
509
|
+
const uniforms = this.uniforms;
|
|
510
|
+
if (!uniforms || !uniforms[uniformName]) {
|
|
511
|
+
console.warn(`[MaterialX] Uniform ${uniformName} not found in ${this.name} uniforms`);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
this.uniforms[uniformName].value = v;
|
|
515
|
+
this.uniformsNeedUpdate = true;
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { BufferGeometry, Camera, FrontSide, GLSL3, Group, IUniform, MaterialParameters, Matrix3, Matrix4, Object3D, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
|
|
2
|
-
import { debug } from "./utils.js";
|
|
2
|
+
import { debug, getFrame, getTime } from "./utils.js";
|
|
3
3
|
import { MaterialXContext, MaterialXEnvironment } from "./materialx.js";
|
|
4
|
-
import { getUniformValues, Loaders } from "./materialx.helper.js";
|
|
4
|
+
import { generateMaterialPropertiesForUniforms, getUniformValues, Loaders } from "./materialx.helper.js";
|
|
5
|
+
import { cloneUniforms, cloneUniformsGroups } from "three/src/renderers/shaders/UniformsUtils.js";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
// Add helper matrices for uniform updates (similar to MaterialX example)
|
|
8
|
-
const identityMatrix = new Matrix4();
|
|
9
9
|
const normalMat = new Matrix3();
|
|
10
|
-
const viewProjMat = new Matrix4();
|
|
11
10
|
const worldViewPos = new Vector3();
|
|
12
11
|
|
|
13
12
|
declare type MaterialXMaterialInitParameters = {
|
|
14
13
|
name: string,
|
|
15
14
|
shader: any,
|
|
16
15
|
loaders: Loaders,
|
|
17
|
-
transparent?: boolean,
|
|
18
|
-
side?: MaterialParameters['side'],
|
|
19
16
|
context: MaterialXContext,
|
|
17
|
+
// Optional parameters
|
|
18
|
+
parameters?: MaterialParameters,
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
type Uniforms = Record<string, IUniform & { needsUpdate?: boolean }>;
|
|
@@ -27,10 +26,19 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
27
26
|
copy(source: MaterialXMaterial): this {
|
|
28
27
|
super.copy(source);
|
|
29
28
|
this._context = source._context;
|
|
29
|
+
this._shader = source._shader;
|
|
30
|
+
this.uniforms = cloneUniforms(source.uniforms) as Uniforms;
|
|
31
|
+
this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups);
|
|
32
|
+
this.envMapIntensity = source.envMapIntensity;
|
|
33
|
+
this.envMap = source.envMap;
|
|
34
|
+
generateMaterialPropertiesForUniforms(this, this._shader.getStage('pixel'));
|
|
35
|
+
generateMaterialPropertiesForUniforms(this, this._shader.getStage('vertex'));
|
|
36
|
+
this.needsUpdate = true;
|
|
30
37
|
return this;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
private _context: MaterialXContext | null = null;
|
|
41
|
+
private _shader: any;
|
|
34
42
|
|
|
35
43
|
constructor(init?: MaterialXMaterialInitParameters) {
|
|
36
44
|
|
|
@@ -63,7 +71,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
63
71
|
vertexShader = vertexShader.replace(/\bi_color_0\b/g, 'color');
|
|
64
72
|
|
|
65
73
|
// Patch fragmentShader
|
|
66
|
-
const precision = init.
|
|
74
|
+
const precision = init.parameters?.precision || "highp" as Precision;
|
|
67
75
|
fragmentShader = fragmentShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
68
76
|
fragmentShader = fragmentShader.replace(/#define M_FLOAT_EPS 1e-8/g, precision === "highp" ? `#define M_FLOAT_EPS 1e-8` : `#define M_FLOAT_EPS 1e-3`);
|
|
69
77
|
|
|
@@ -124,19 +132,20 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
124
132
|
if (hasColor) defines['USE_COLOR'] = '';
|
|
125
133
|
|
|
126
134
|
const searchPath = ""; // Could be derived from the asset path if needed
|
|
127
|
-
const isTransparent = init.transparent ?? false;
|
|
135
|
+
const isTransparent = init.parameters?.transparent ?? false;
|
|
128
136
|
super({
|
|
129
137
|
name: init.name,
|
|
130
138
|
uniforms: {},
|
|
131
139
|
vertexShader: vertexShader,
|
|
132
140
|
fragmentShader: fragmentShader,
|
|
133
141
|
glslVersion: GLSL3,
|
|
134
|
-
transparent: isTransparent,
|
|
135
|
-
side: init.side ? init.side : FrontSide,
|
|
136
142
|
depthTest: true,
|
|
137
143
|
depthWrite: !isTransparent,
|
|
138
144
|
defines: defines,
|
|
145
|
+
...init.parameters, // Spread any additional parameters passed to the material
|
|
139
146
|
});
|
|
147
|
+
this._context = init.context;
|
|
148
|
+
this._shader = init.shader;
|
|
140
149
|
|
|
141
150
|
Object.assign(this.uniforms, {
|
|
142
151
|
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath),
|
|
@@ -158,7 +167,9 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
158
167
|
u_lightData: { value: [], needsUpdate: false }, // Array of light data. We need to set needsUpdate to false until we actually update it
|
|
159
168
|
});
|
|
160
169
|
|
|
161
|
-
this
|
|
170
|
+
generateMaterialPropertiesForUniforms(this, init.shader.getStage('pixel'));
|
|
171
|
+
generateMaterialPropertiesForUniforms(this, init.shader.getStage('vertex'));
|
|
172
|
+
|
|
162
173
|
|
|
163
174
|
if (debug) {
|
|
164
175
|
// Get lighting and environment data from MaterialX environment
|
|
@@ -170,19 +181,19 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
170
181
|
}
|
|
171
182
|
|
|
172
183
|
onBeforeRender(_renderer: WebGLRenderer, _scene: Scene, camera: Camera, _geometry: BufferGeometry, object: Object3D, _group: Group): void {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
const time = this._context?.getTime?.() || getTime();
|
|
185
|
+
const frame = this._context?.getFrame?.() || getFrame();
|
|
186
|
+
const env = MaterialXEnvironment.get(_scene);
|
|
187
|
+
if (env) {
|
|
188
|
+
env.update(frame, _scene, _renderer);
|
|
189
|
+
this.updateUniforms(env, object, camera, time, frame);
|
|
179
190
|
}
|
|
180
191
|
}
|
|
181
192
|
|
|
182
193
|
|
|
183
194
|
envMapIntensity: number = 1.0; // Default intensity for environment map
|
|
184
195
|
envMap: Texture | null = null; // Environment map texture, can be set externally
|
|
185
|
-
updateUniforms = (environment: MaterialXEnvironment, object: Object3D, camera: Camera) => {
|
|
196
|
+
updateUniforms = (environment: MaterialXEnvironment, object: Object3D, camera: Camera, time?: number, frame?: number) => {
|
|
186
197
|
|
|
187
198
|
const uniforms = this.uniforms as Uniforms;
|
|
188
199
|
|
|
@@ -208,13 +219,13 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
208
219
|
}
|
|
209
220
|
|
|
210
221
|
// Update time uniforms
|
|
211
|
-
if (
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
if (uniforms.u_time) {
|
|
223
|
+
if (time === undefined) time = getTime();
|
|
224
|
+
uniforms.u_time.value = time;
|
|
225
|
+
}
|
|
226
|
+
if (uniforms.u_frame) {
|
|
227
|
+
if (frame === undefined) frame = getFrame();
|
|
228
|
+
uniforms.u_frame.value = frame;
|
|
218
229
|
}
|
|
219
230
|
|
|
220
231
|
// Update light uniforms
|
package/src/materialx.ts
CHANGED
|
@@ -7,10 +7,8 @@ import { registerLights, getLightData, LightData } from "./materialx.helper.js";
|
|
|
7
7
|
import type { MaterialXMaterial } from "./materialx.material.js";
|
|
8
8
|
|
|
9
9
|
export type MaterialXContext = {
|
|
10
|
-
getTime(): number,
|
|
11
|
-
getFrame(): number,
|
|
12
|
-
getScene(): Scene,
|
|
13
|
-
getRenderer(): WebGLRenderer,
|
|
10
|
+
getTime?(): number,
|
|
11
|
+
getFrame?(): number,
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
|
|
@@ -114,23 +112,21 @@ type EnvironmentTextureSet = {
|
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
type EnvironmentContext = Pick<MaterialXContext, "getRenderer" | "getScene">;
|
|
119
115
|
/**
|
|
120
116
|
* MaterialXEnvironment manages the environment settings for MaterialX materials.
|
|
121
117
|
*/
|
|
122
118
|
export class MaterialXEnvironment {
|
|
123
119
|
|
|
124
|
-
static get(
|
|
125
|
-
return this.getEnvironment(
|
|
120
|
+
static get(scene: Scene): MaterialXEnvironment | null {
|
|
121
|
+
return this.getEnvironment(scene);
|
|
126
122
|
}
|
|
127
|
-
private static _environments: WeakMap<
|
|
128
|
-
private static getEnvironment(
|
|
129
|
-
if (this._environments.has(
|
|
130
|
-
return this._environments.get(
|
|
123
|
+
private static _environments: WeakMap<Scene, MaterialXEnvironment> = new Map();
|
|
124
|
+
private static getEnvironment(scene: Scene): MaterialXEnvironment {
|
|
125
|
+
if (this._environments.has(scene)) {
|
|
126
|
+
return this._environments.get(scene)!;
|
|
131
127
|
}
|
|
132
|
-
const env = new MaterialXEnvironment(
|
|
133
|
-
this._environments.set(
|
|
128
|
+
const env = new MaterialXEnvironment(scene);
|
|
129
|
+
this._environments.set(scene, env);
|
|
134
130
|
return env;
|
|
135
131
|
}
|
|
136
132
|
|
|
@@ -143,22 +139,22 @@ export class MaterialXEnvironment {
|
|
|
143
139
|
private _isInitialized: boolean = false;
|
|
144
140
|
private _lastUpdateFrame: number = -1;
|
|
145
141
|
|
|
146
|
-
constructor(private
|
|
142
|
+
constructor(private _scene: Scene) {
|
|
147
143
|
if (debug) console.log("[MaterialX] Environment created");
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
// Initialize with Needle Engine context
|
|
151
|
-
async initialize(): Promise<boolean> {
|
|
147
|
+
async initialize(renderer: WebGLRenderer): Promise<boolean> {
|
|
152
148
|
if (this._initializePromise) {
|
|
153
149
|
return this._initializePromise;
|
|
154
150
|
}
|
|
155
|
-
this._initializePromise = this._initialize();
|
|
151
|
+
this._initializePromise = this._initialize(renderer);
|
|
156
152
|
return this._initializePromise;
|
|
157
153
|
}
|
|
158
154
|
|
|
159
|
-
update(frame: number, scene: Scene) {
|
|
155
|
+
update(frame: number, scene: Scene, renderer: WebGLRenderer): void {
|
|
160
156
|
if (!this._initializePromise) {
|
|
161
|
-
this.initialize();
|
|
157
|
+
this.initialize(renderer);
|
|
162
158
|
return;
|
|
163
159
|
}
|
|
164
160
|
if (!this._isInitialized) {
|
|
@@ -205,6 +201,7 @@ export class MaterialXEnvironment {
|
|
|
205
201
|
this._lightCount = 0;
|
|
206
202
|
this._pmremGenerator?.dispose();
|
|
207
203
|
this._pmremGenerator = null;
|
|
204
|
+
this._renderer = null;
|
|
208
205
|
for (const textureSet of this._texturesCache.values()) {
|
|
209
206
|
textureSet.radianceTexture?.dispose();
|
|
210
207
|
textureSet.irradianceTexture?.dispose();
|
|
@@ -221,15 +218,17 @@ export class MaterialXEnvironment {
|
|
|
221
218
|
// If the material has its own envMap, we don't use the irradiance texture
|
|
222
219
|
return this._getTextures(material.envMap);
|
|
223
220
|
}
|
|
224
|
-
return this._getTextures(this.
|
|
221
|
+
return this._getTextures(this._scene.environment);
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
private _pmremGenerator: PMREMGenerator | null = null;
|
|
225
|
+
private _renderer: WebGLRenderer | null = null;
|
|
228
226
|
private readonly _texturesCache: Map<Texture | null, EnvironmentTextureSet> = new Map();
|
|
229
227
|
|
|
230
|
-
private async _initialize(): Promise<boolean> {
|
|
228
|
+
private async _initialize(renderer: WebGLRenderer): Promise<boolean> {
|
|
231
229
|
this._isInitialized = false;
|
|
232
|
-
this._pmremGenerator = new PMREMGenerator(
|
|
230
|
+
this._pmremGenerator = new PMREMGenerator(renderer);
|
|
231
|
+
this._renderer = renderer;
|
|
233
232
|
this.updateLighting(true);
|
|
234
233
|
this._isInitialized = true;
|
|
235
234
|
return true;
|
|
@@ -244,11 +243,11 @@ export class MaterialXEnvironment {
|
|
|
244
243
|
return res;
|
|
245
244
|
}
|
|
246
245
|
|
|
247
|
-
if (this.
|
|
246
|
+
if (this._scene && this._pmremGenerator && this._renderer && texture) {
|
|
248
247
|
if (debug) console.log("[MaterialX] Generating environment textures", texture.name);
|
|
249
248
|
const target = this._pmremGenerator.fromEquirectangular(texture);
|
|
250
|
-
const radianceRenderTarget = renderPMREMToEquirect(this.
|
|
251
|
-
const irradianceRenderTarget = renderPMREMToEquirect(this.
|
|
249
|
+
const radianceRenderTarget = renderPMREMToEquirect(this._renderer, target.texture, 0.0, 1024, 512, target.height);
|
|
250
|
+
const irradianceRenderTarget = renderPMREMToEquirect(this._renderer, target.texture, 1.0, 32, 16, target.height);
|
|
252
251
|
target.dispose();
|
|
253
252
|
res = {
|
|
254
253
|
radianceTexture: radianceRenderTarget.texture,
|
|
@@ -266,11 +265,11 @@ export class MaterialXEnvironment {
|
|
|
266
265
|
}
|
|
267
266
|
|
|
268
267
|
private updateLighting = (collectLights: boolean = false) => {
|
|
269
|
-
if (!this.
|
|
268
|
+
if (!this._scene) return;
|
|
270
269
|
// Find lights in scene
|
|
271
270
|
if (collectLights) {
|
|
272
271
|
const lights = new Array<Light>();
|
|
273
|
-
this.
|
|
272
|
+
this._scene.traverse((object: Object3D) => {
|
|
274
273
|
if ((object as Light).isLight && object.visible)
|
|
275
274
|
lights.push(object as Light);
|
|
276
275
|
});
|
package/src/utils.ts
CHANGED
|
@@ -10,70 +10,20 @@ export const debug = getParam("debugmaterialx");
|
|
|
10
10
|
export const debugUpdate = debug === "update";
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// const getUniformLocation = WebGL2RenderingContext.prototype.getUniformLocation;
|
|
22
|
-
// const programAndNameToUniformLocation = new WeakMap<WebGLUniformLocation, { program: WebGLProgram, name: string }>();
|
|
23
|
-
// WebGL2RenderingContext.prototype.getUniformLocation = function (program: WebGLProgram, name: string) {
|
|
24
|
-
// const location = getUniformLocation.call(this, program, name);
|
|
25
|
-
// if (location) {
|
|
26
|
-
// programAndNameToUniformLocation.set(location, { program, name });
|
|
27
|
-
// }
|
|
28
|
-
// return location;
|
|
29
|
-
// };
|
|
30
|
-
|
|
31
|
-
// const uniform4fv = WebGL2RenderingContext.prototype.uniform4fv;
|
|
32
|
-
// WebGL2RenderingContext.prototype.uniform4fv = function (location: WebGLUniformLocation | null, v: Float32Array | number[]) {
|
|
33
|
-
// if (location) {
|
|
34
|
-
// const uniformName = programAndNameToUniformLocation.get(location);
|
|
35
|
-
// if (true) console.log("Calling uniform4fv", { location, v, name: uniformName?.name });
|
|
36
|
-
// }
|
|
37
|
-
// return uniform4fv.call(this, location, v);
|
|
38
|
-
// };
|
|
39
|
-
|
|
40
|
-
// const uniform3fv = WebGL2RenderingContext.prototype.uniform3fv;
|
|
41
|
-
// WebGL2RenderingContext.prototype.uniform3fv = function (location: WebGLUniformLocation | null, v: Float32Array | number[]) {
|
|
42
|
-
// if (location) {
|
|
43
|
-
// const uniformName = programAndNameToUniformLocation.get(location);
|
|
44
|
-
// if (true) console.log("Calling uniform3fv", { location, v, name: uniformName?.name });
|
|
45
|
-
// }
|
|
46
|
-
// return uniform3fv.call(this, location, v);
|
|
47
|
-
// };
|
|
48
|
-
|
|
49
|
-
// const uniform3iv = WebGL2RenderingContext.prototype.uniform3iv;
|
|
50
|
-
// WebGL2RenderingContext.prototype.uniform3iv = function (location: WebGLUniformLocation | null, v: Int32Array | number[]) {
|
|
51
|
-
// if (location) {
|
|
52
|
-
// const uniformName = programAndNameToUniformLocation.get(location);
|
|
53
|
-
// if (true) console.log("Calling uniform3iv", { location, v, name: uniformName?.name });
|
|
54
|
-
// }
|
|
55
|
-
// return uniform3iv.call(this, location, v);
|
|
56
|
-
// };
|
|
57
|
-
|
|
58
|
-
// const uniform3uiv = WebGL2RenderingContext.prototype.uniform3uiv;
|
|
59
|
-
// WebGL2RenderingContext.prototype.uniform3uiv = function (location: WebGLUniformLocation | null, v: Uint32Array | number[]) {
|
|
60
|
-
// if (location) {
|
|
61
|
-
// const uniformName = programAndNameToUniformLocation.get(location);
|
|
62
|
-
// if (true) console.log("Calling uniform3uiv", { location, v, name: uniformName?.name });
|
|
63
|
-
// }
|
|
64
|
-
// return uniform3uiv.call(this, location, v);
|
|
65
|
-
// };
|
|
13
|
+
let time = 0;
|
|
14
|
+
export function getTime() {
|
|
15
|
+
return time;
|
|
16
|
+
}
|
|
17
|
+
let frame = 0;
|
|
18
|
+
export function getFrame() {
|
|
19
|
+
return frame;
|
|
20
|
+
}
|
|
66
21
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// }
|
|
75
|
-
// return uniform3f.call(this, location, x, y, z);
|
|
76
|
-
// };
|
|
77
|
-
// };
|
|
78
|
-
// // patchWebGL2();
|
|
22
|
+
const performance = window.performance || (window as any).webkitPerformance || (window as any).mozPerformance;
|
|
23
|
+
function updateTime() {
|
|
24
|
+
time = performance.now() / 1000; // Convert to seconds
|
|
25
|
+
frame++;
|
|
26
|
+
window.requestAnimationFrame(updateTime);
|
|
27
|
+
}
|
|
28
|
+
window.requestAnimationFrame(updateTime);
|
|
79
29
|
|