@needle-tools/materialx 1.1.0-next.0aa8b46 → 1.1.0-next.2d4dc5e
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 +1 -1
- package/src/loader/loader.needle.ts +3 -3
- package/src/loader/loader.three.ts +2 -2
- package/src/materialx.helper.ts +48 -3
- package/src/materialx.material.ts +6 -3
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
|
@@ -2,7 +2,7 @@ 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
7
|
import { MaterialParameters } from "three";
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
14
14
|
|
|
15
15
|
onImport = (loader: GLTFLoader, url: string, context: Context) => {
|
|
16
16
|
if (debug) console.log("MaterialXLoaderPlugin: Registering MaterialX extension for", url);
|
|
17
|
-
|
|
17
|
+
_useNeedleMaterialX(loader, {
|
|
18
18
|
cacheKey: url,
|
|
19
19
|
parameters: {
|
|
20
20
|
precision: context.renderer.capabilities.getMaxPrecision("highp") as MaterialParameters["precision"],
|
|
@@ -38,6 +38,6 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
|
|
|
38
38
|
/**
|
|
39
39
|
* Add the MaterialXLoaderPlugin to the Needle Engine.
|
|
40
40
|
*/
|
|
41
|
-
export async function
|
|
41
|
+
export async function useNeedleMaterialX() {
|
|
42
42
|
addCustomExtensionPlugin(new MaterialXLoaderPlugin());
|
|
43
43
|
}
|
|
@@ -309,9 +309,9 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
|
|
|
309
309
|
|
|
310
310
|
|
|
311
311
|
/**
|
|
312
|
-
* Add the
|
|
312
|
+
* Add the MaterialXLoader to the GLTFLoader instance.
|
|
313
313
|
*/
|
|
314
|
-
export function
|
|
314
|
+
export function useNeedleMaterialX(loader: GLTFLoader, options?: MaterialXLoaderOptions, context?: MaterialXContext) {
|
|
315
315
|
loader.register(p => {
|
|
316
316
|
const loader = new MaterialXLoader(p, options || {}, context || {});
|
|
317
317
|
return loader;
|
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: () => {
|
|
506
|
+
return material.uniforms?.[uniformName].value
|
|
507
|
+
},
|
|
508
|
+
set: (v) => {
|
|
509
|
+
const uniforms = material.uniforms;
|
|
510
|
+
if (!uniforms || !uniforms[uniformName]) {
|
|
511
|
+
console.warn(`[MaterialX] Uniform ${uniformName} not found in ${material.name} uniforms`);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
material.uniforms[uniformName].value = v;
|
|
515
|
+
material.uniformsNeedUpdate = true;
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BufferGeometry, Camera, FrontSide, GLSL3, Group, IUniform, MaterialParameters, Matrix3, Matrix4, Object3D, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
|
|
2
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
5
|
|
|
6
6
|
|
|
7
7
|
// Add helper matrices for uniform updates (similar to MaterialX example)
|
|
@@ -137,6 +137,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
137
137
|
depthWrite: !isTransparent,
|
|
138
138
|
defines: defines,
|
|
139
139
|
});
|
|
140
|
+
this._context = init.context;
|
|
140
141
|
|
|
141
142
|
Object.assign(this.uniforms, {
|
|
142
143
|
...getUniformValues(init.shader.getStage('vertex'), init.loaders, searchPath),
|
|
@@ -158,7 +159,9 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
158
159
|
u_lightData: { value: [], needsUpdate: false }, // Array of light data. We need to set needsUpdate to false until we actually update it
|
|
159
160
|
});
|
|
160
161
|
|
|
161
|
-
this
|
|
162
|
+
generateMaterialPropertiesForUniforms(this, init.shader.getStage('pixel'));
|
|
163
|
+
generateMaterialPropertiesForUniforms(this, init.shader.getStage('vertex'));
|
|
164
|
+
|
|
162
165
|
|
|
163
166
|
if (debug) {
|
|
164
167
|
// Get lighting and environment data from MaterialX environment
|
|
@@ -216,7 +219,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
216
219
|
if (frame === undefined) frame = getFrame();
|
|
217
220
|
uniforms.u_frame.value = frame;
|
|
218
221
|
}
|
|
219
|
-
|
|
222
|
+
|
|
220
223
|
// Update light uniforms
|
|
221
224
|
this.updateEnvironmentUniforms(environment);
|
|
222
225
|
|