@needle-tools/materialx 1.0.0-alpha → 1.0.0-next.12f185d

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.
@@ -1,170 +1,170 @@
1
- import { WebGLRenderer, Scene, WebGLRenderTarget, PlaneGeometry, OrthographicCamera, ShaderMaterial, RGBAFormat, FloatType, LinearFilter, Mesh, EquirectangularReflectionMapping, RepeatWrapping, LinearMipMapLinearFilter, Texture, WebGLUtils } from 'three';
2
- import { getParam } from '@needle-tools/engine';
3
-
4
- const debug = getParam("debugmaterialx");
5
-
6
- /**
7
- * Renders a PMREM environment map to an equirectangular texture with specified roughness
8
- * @param {WebGLRenderer} renderer - Three.js WebGL renderer
9
- * @param {Texture} pmremTexture - PMREM texture (2D CubeUV layout) to convert
10
- * @param {number} roughness - Roughness value (0.0 to 1.0)
11
- * @param {number} width - Output texture width (default: 1024)
12
- * @param {number} height - Output texture height (default: 512)
13
- * @param {number} renderTargetHeight - Original render target height (optional, for proper PMREM parameter calculation)
14
- * @returns {WebGLRenderTarget} Render target containing the equirectangular texture
15
- * @example // Creating an equirectangular texture from a PMREM environment map at a certain roughness level:
16
- * const pmremRenderTarget = pmremGenerator.fromEquirectangular(envMap);
17
- * const equirectRenderTarget = await renderPMREMToEquirect(renderer, pmremRenderTarget.texture, 0.5, 2048, 1024, pmremRenderTarget.height);
18
-
19
- // Use the rendered equirectangular texture
20
- const equirectTexture = equirectRenderTarget.texture;
21
-
22
- // Apply to your material or save/export
23
- someMaterial.map = equirectTexture;
24
-
25
- // Don't forget to dispose when done
26
- // equirectRenderTarget.dispose();
27
- */
28
- export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Texture, roughness = 0.0, width = 1024, height = 512, renderTargetHeight?: number) {
29
- // TODO Validate inputs
30
- // console.log(renderer, pmremTexture);
31
-
32
- // Calculate PMREM parameters
33
- // For PMREM CubeUV layout, we need the cube face size to calculate proper parameters
34
- // Use renderTargetHeight if provided, otherwise try to derive from texture
35
- let imageHeight;
36
- if (renderTargetHeight) {
37
- imageHeight = renderTargetHeight;
38
- } else if (pmremTexture.image) {
39
- imageHeight = pmremTexture.image.height / 4; // Fallback: assume CubeUV layout height / 4
40
- } else {
41
- imageHeight = 256; // Final fallback
42
- }
43
-
44
- const maxMip = Math.log2(imageHeight) - 2;
45
- const cubeUVHeight = imageHeight;
46
- const cubeUVWidth = 3 * Math.max(Math.pow(2, maxMip), 7 * 16);
47
-
48
- // Create render target for equirectangular output
49
- const renderTarget = new WebGLRenderTarget(width, height, {
50
- format: RGBAFormat,
51
- type: FloatType,
52
- minFilter: LinearMipMapLinearFilter,
53
- magFilter: LinearFilter,
54
- generateMipmaps: true,
55
- wrapS: RepeatWrapping,
56
- anisotropy: renderer.capabilities.getMaxAnisotropy(),
57
- });
58
-
59
- // Create fullscreen quad geometry and camera
60
- const geometry = new PlaneGeometry(2, 2);
61
- const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
62
-
63
- // Create shader material for PMREM to equirectangular conversion
64
- const material = new ShaderMaterial({
65
- defines: {
66
- USE_ENVMAP: '',
67
- ENVMAP_TYPE_CUBE_UV: '',
68
- CUBEUV_TEXEL_WIDTH: 1.0 / cubeUVWidth,
69
- CUBEUV_TEXEL_HEIGHT: 1.0 / cubeUVHeight,
70
- CUBEUV_MAX_MIP: (maxMip + 0) + '.0',
71
- },
72
- uniforms: {
73
- envMap: { value: pmremTexture },
74
- roughness: { value: roughness }
75
- },
76
- vertexShader: `
77
- varying vec2 vUv;
78
-
79
- void main() {
80
- vUv = uv;
81
- gl_Position = vec4(position.xy, 0.0, 1.0);
82
- }
83
- `,
84
- fragmentShader: `
85
- uniform sampler2D envMap;
86
- uniform float roughness;
87
- varying vec2 vUv;
88
-
89
- #include <common>
90
- #include <cube_uv_reflection_fragment>
91
-
92
- void main() {
93
- // Convert UV coordinates to equirectangular direction
94
- vec2 uv = vUv;
95
-
96
- // Map UV (0,1) to spherical coordinates
97
- // Longitude: -π to π, Latitude: 0 to π
98
- float phi = uv.x * 2.0 * PI - PI; // Longitude (-π to π)
99
- float theta = uv.y * PI; // Latitude (0 to π)
100
- // Rotate 90° around Y
101
- phi -= PI / 2.0; // Adjust to match Three.js convention
102
-
103
- // Convert spherical to cartesian coordinates
104
- vec3 direction = vec3(
105
- sin(theta) * cos(phi), // x
106
- cos(theta), // y
107
- sin(theta) * sin(phi) // z
108
- );
109
-
110
- // Sample the PMREM cube texture using the direction and roughness
111
- #ifdef ENVMAP_TYPE_CUBE_UV
112
- vec4 envColor = textureCubeUV(envMap, direction, roughness);
113
- #else
114
- vec4 envColor = vec4(1.0, 0.0, 1.0, 1.0); // Magenta fallback
115
- #endif
116
-
117
- gl_FragColor = vec4(envColor.rgb, 1.0);
118
- }
119
- `
120
- });
121
-
122
- // Create temporary scene and mesh for rendering
123
- const tempScene = new Scene();
124
- const mesh = new Mesh(geometry, material);
125
- tempScene.add(mesh);
126
-
127
- // Store current renderer state
128
- const currentRenderTarget = renderer.getRenderTarget();
129
- const currentAutoClear = renderer.autoClear;
130
- const currentXrEnabled = renderer.xr.enabled;
131
- const currentShadowMapEnabled = renderer.shadowMap.enabled;
132
-
133
- renderTarget.texture.generateMipmaps = true;
134
-
135
- try {
136
- // Disable XR and shadow mapping during our render to avoid interference
137
- renderer.xr.enabled = false;
138
- renderer.shadowMap.enabled = false;
139
-
140
- // Render to our target
141
- renderer.autoClear = true;
142
- renderer.setRenderTarget(renderTarget);
143
- renderer.clear(); // Explicitly clear the render target
144
- renderer.render(tempScene, camera);
145
- } finally {
146
- // Restore renderer state completely
147
- renderer.setRenderTarget(currentRenderTarget);
148
- renderer.autoClear = currentAutoClear;
149
- renderer.xr.enabled = currentXrEnabled;
150
- renderer.shadowMap.enabled = currentShadowMapEnabled;
151
-
152
- // Clean up temporary objects
153
- geometry.dispose();
154
- material.dispose();
155
- tempScene.remove(mesh);
156
- }
157
-
158
- renderTarget.texture.name = 'PMREM_Equirectangular_Texture_' + roughness.toFixed(2);
159
- renderTarget.texture.mapping = EquirectangularReflectionMapping;
160
-
161
- // Log mipmap infos
162
- if (debug) console.log('PMREM to Equirect Render Target:', {
163
- width: renderTarget.width,
164
- height: renderTarget.height,
165
- mipmaps: renderTarget.texture.mipmaps?.length,
166
- roughness: roughness,
167
- });
168
-
169
- return renderTarget;
1
+ import { WebGLRenderer, Scene, WebGLRenderTarget, PlaneGeometry, OrthographicCamera, ShaderMaterial, RGBAFormat, FloatType, LinearFilter, Mesh, EquirectangularReflectionMapping, RepeatWrapping, LinearMipMapLinearFilter, Texture, WebGLUtils } from 'three';
2
+ import { getParam } from '@needle-tools/engine';
3
+
4
+ const debug = getParam("debugmaterialx");
5
+
6
+ /**
7
+ * Renders a PMREM environment map to an equirectangular texture with specified roughness
8
+ * @param {WebGLRenderer} renderer - Three.js WebGL renderer
9
+ * @param {Texture} pmremTexture - PMREM texture (2D CubeUV layout) to convert
10
+ * @param {number} roughness - Roughness value (0.0 to 1.0)
11
+ * @param {number} width - Output texture width (default: 1024)
12
+ * @param {number} height - Output texture height (default: 512)
13
+ * @param {number} renderTargetHeight - Original render target height (optional, for proper PMREM parameter calculation)
14
+ * @returns {WebGLRenderTarget} Render target containing the equirectangular texture
15
+ * @example // Creating an equirectangular texture from a PMREM environment map at a certain roughness level:
16
+ * const pmremRenderTarget = pmremGenerator.fromEquirectangular(envMap);
17
+ * const equirectRenderTarget = await renderPMREMToEquirect(renderer, pmremRenderTarget.texture, 0.5, 2048, 1024, pmremRenderTarget.height);
18
+
19
+ // Use the rendered equirectangular texture
20
+ const equirectTexture = equirectRenderTarget.texture;
21
+
22
+ // Apply to your material or save/export
23
+ someMaterial.map = equirectTexture;
24
+
25
+ // Don't forget to dispose when done
26
+ // equirectRenderTarget.dispose();
27
+ */
28
+ export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Texture, roughness = 0.0, width = 1024, height = 512, renderTargetHeight?: number) {
29
+ // TODO Validate inputs
30
+ // console.log(renderer, pmremTexture);
31
+
32
+ // Calculate PMREM parameters
33
+ // For PMREM CubeUV layout, we need the cube face size to calculate proper parameters
34
+ // Use renderTargetHeight if provided, otherwise try to derive from texture
35
+ let imageHeight;
36
+ if (renderTargetHeight) {
37
+ imageHeight = renderTargetHeight;
38
+ } else if (pmremTexture.image) {
39
+ imageHeight = pmremTexture.image.height / 4; // Fallback: assume CubeUV layout height / 4
40
+ } else {
41
+ imageHeight = 256; // Final fallback
42
+ }
43
+
44
+ const maxMip = Math.log2(imageHeight) - 2;
45
+ const cubeUVHeight = imageHeight;
46
+ const cubeUVWidth = 3 * Math.max(Math.pow(2, maxMip), 7 * 16);
47
+
48
+ // Create render target for equirectangular output
49
+ const renderTarget = new WebGLRenderTarget(width, height, {
50
+ format: RGBAFormat,
51
+ type: FloatType,
52
+ minFilter: LinearMipMapLinearFilter,
53
+ magFilter: LinearFilter,
54
+ generateMipmaps: true,
55
+ wrapS: RepeatWrapping,
56
+ anisotropy: renderer.capabilities.getMaxAnisotropy(),
57
+ });
58
+
59
+ // Create fullscreen quad geometry and camera
60
+ const geometry = new PlaneGeometry(2, 2);
61
+ const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
62
+
63
+ // Create shader material for PMREM to equirectangular conversion
64
+ const material = new ShaderMaterial({
65
+ defines: {
66
+ USE_ENVMAP: '',
67
+ ENVMAP_TYPE_CUBE_UV: '',
68
+ CUBEUV_TEXEL_WIDTH: 1.0 / cubeUVWidth,
69
+ CUBEUV_TEXEL_HEIGHT: 1.0 / cubeUVHeight,
70
+ CUBEUV_MAX_MIP: (maxMip + 0) + '.0',
71
+ },
72
+ uniforms: {
73
+ envMap: { value: pmremTexture },
74
+ roughness: { value: roughness }
75
+ },
76
+ vertexShader: `
77
+ varying vec2 vUv;
78
+
79
+ void main() {
80
+ vUv = uv;
81
+ gl_Position = vec4(position.xy, 0.0, 1.0);
82
+ }
83
+ `,
84
+ fragmentShader: `
85
+ uniform sampler2D envMap;
86
+ uniform float roughness;
87
+ varying vec2 vUv;
88
+
89
+ #include <common>
90
+ #include <cube_uv_reflection_fragment>
91
+
92
+ void main() {
93
+ // Convert UV coordinates to equirectangular direction
94
+ vec2 uv = vUv;
95
+
96
+ // Map UV (0,1) to spherical coordinates
97
+ // Longitude: -π to π, Latitude: 0 to π
98
+ float phi = uv.x * 2.0 * PI - PI; // Longitude (-π to π)
99
+ float theta = uv.y * PI; // Latitude (0 to π)
100
+ // Rotate 90° around Y
101
+ phi -= PI / 2.0; // Adjust to match Three.js convention
102
+
103
+ // Convert spherical to cartesian coordinates
104
+ vec3 direction = vec3(
105
+ sin(theta) * cos(phi), // x
106
+ cos(theta), // y
107
+ sin(theta) * sin(phi) // z
108
+ );
109
+
110
+ // Sample the PMREM cube texture using the direction and roughness
111
+ #ifdef ENVMAP_TYPE_CUBE_UV
112
+ vec4 envColor = textureCubeUV(envMap, direction, roughness);
113
+ #else
114
+ vec4 envColor = vec4(1.0, 0.0, 1.0, 1.0); // Magenta fallback
115
+ #endif
116
+
117
+ gl_FragColor = vec4(envColor.rgb, 1.0);
118
+ }
119
+ `
120
+ });
121
+
122
+ // Create temporary scene and mesh for rendering
123
+ const tempScene = new Scene();
124
+ const mesh = new Mesh(geometry, material);
125
+ tempScene.add(mesh);
126
+
127
+ // Store current renderer state
128
+ const currentRenderTarget = renderer.getRenderTarget();
129
+ const currentAutoClear = renderer.autoClear;
130
+ const currentXrEnabled = renderer.xr.enabled;
131
+ const currentShadowMapEnabled = renderer.shadowMap.enabled;
132
+
133
+ renderTarget.texture.generateMipmaps = true;
134
+
135
+ try {
136
+ // Disable XR and shadow mapping during our render to avoid interference
137
+ renderer.xr.enabled = false;
138
+ renderer.shadowMap.enabled = false;
139
+
140
+ // Render to our target
141
+ renderer.autoClear = true;
142
+ renderer.setRenderTarget(renderTarget);
143
+ renderer.clear(); // Explicitly clear the render target
144
+ renderer.render(tempScene, camera);
145
+ } finally {
146
+ // Restore renderer state completely
147
+ renderer.setRenderTarget(currentRenderTarget);
148
+ renderer.autoClear = currentAutoClear;
149
+ renderer.xr.enabled = currentXrEnabled;
150
+ renderer.shadowMap.enabled = currentShadowMapEnabled;
151
+
152
+ // Clean up temporary objects
153
+ geometry.dispose();
154
+ material.dispose();
155
+ tempScene.remove(mesh);
156
+ }
157
+
158
+ renderTarget.texture.name = 'PMREM_Equirectangular_Texture_' + roughness.toFixed(2);
159
+ renderTarget.texture.mapping = EquirectangularReflectionMapping;
160
+
161
+ // Log mipmap infos
162
+ if (debug) console.log('PMREM to Equirect Render Target:', {
163
+ width: renderTarget.width,
164
+ height: renderTarget.height,
165
+ mipmaps: renderTarget.texture.mipmaps?.length,
166
+ roughness: roughness,
167
+ });
168
+
169
+ return renderTarget;
170
170
  }
package/src/utils.ts CHANGED
@@ -1,58 +1,58 @@
1
- import { Context, getParam } from "@needle-tools/engine";
2
- import { Mesh } from "three";
3
-
4
- export const debug = getParam("debugmaterialx");
5
-
6
-
7
-
8
-
9
-
10
- /**
11
- * =====================================
12
- * Unused
13
- */
14
-
15
- // Patch WebGL2 methods for debugging purposes
16
-
17
- const patchWebGL2 = () => {
18
- const getUniformLocation = WebGL2RenderingContext.prototype.getUniformLocation;
19
- const programAndNameToUniformLocation = new WeakMap<WebGLUniformLocation, { program: WebGLProgram, name: string }>();
20
- WebGL2RenderingContext.prototype.getUniformLocation = function (program: WebGLProgram, name: string) {
21
- const location = getUniformLocation.call(this, program, name);
22
- if (location) {
23
- programAndNameToUniformLocation.set(location, { program, name });
24
- }
25
- return location;
26
- };
27
-
28
- const uniform4fv = WebGL2RenderingContext.prototype.uniform4fv;
29
- WebGL2RenderingContext.prototype.uniform4fv = function (location: WebGLUniformLocation | null, v: Float32Array | number[]) {
30
- if (location) {
31
- const uniformName = programAndNameToUniformLocation.get(location);
32
- if (true) console.log("Calling uniform4fv", { location, v, name: uniformName?.name });
33
- }
34
- return uniform4fv.call(this, location, v);
35
- };
36
- };
37
- // patchWebGL2();
38
-
39
-
40
- // TODO doesn't actually reset yet...
41
- function resetShaders(ctx: Context) {
42
- const scene = ctx.scene;
43
- const gl = ctx.renderer;
44
- console.log(gl.properties, gl.info)
45
- scene.traverse(object => {
46
- if ((object as Mesh).isMesh) {
47
- const mesh = object as Mesh;
48
- if (Array.isArray(mesh.material)) {
49
- mesh.material.forEach(mat => gl.properties.remove(mat));
50
- }
51
- else {
52
- gl.properties.remove(mesh.material);
53
- }
54
- }
55
- })
56
- if (gl.info?.programs)
57
- gl.info.programs.length = 0;
1
+ import { Context, getParam } from "@needle-tools/engine";
2
+ import { Mesh } from "three";
3
+
4
+ export const debug = getParam("debugmaterialx");
5
+
6
+
7
+
8
+
9
+
10
+ /**
11
+ * =====================================
12
+ * Unused
13
+ */
14
+
15
+ // Patch WebGL2 methods for debugging purposes
16
+
17
+ const patchWebGL2 = () => {
18
+ const getUniformLocation = WebGL2RenderingContext.prototype.getUniformLocation;
19
+ const programAndNameToUniformLocation = new WeakMap<WebGLUniformLocation, { program: WebGLProgram, name: string }>();
20
+ WebGL2RenderingContext.prototype.getUniformLocation = function (program: WebGLProgram, name: string) {
21
+ const location = getUniformLocation.call(this, program, name);
22
+ if (location) {
23
+ programAndNameToUniformLocation.set(location, { program, name });
24
+ }
25
+ return location;
26
+ };
27
+
28
+ const uniform4fv = WebGL2RenderingContext.prototype.uniform4fv;
29
+ WebGL2RenderingContext.prototype.uniform4fv = function (location: WebGLUniformLocation | null, v: Float32Array | number[]) {
30
+ if (location) {
31
+ const uniformName = programAndNameToUniformLocation.get(location);
32
+ if (true) console.log("Calling uniform4fv", { location, v, name: uniformName?.name });
33
+ }
34
+ return uniform4fv.call(this, location, v);
35
+ };
36
+ };
37
+ // patchWebGL2();
38
+
39
+
40
+ // TODO doesn't actually reset yet...
41
+ function resetShaders(ctx: Context) {
42
+ const scene = ctx.scene;
43
+ const gl = ctx.renderer;
44
+ console.log(gl.properties, gl.info)
45
+ scene.traverse(object => {
46
+ if ((object as Mesh).isMesh) {
47
+ const mesh = object as Mesh;
48
+ if (Array.isArray(mesh.material)) {
49
+ mesh.material.forEach(mat => gl.properties.remove(mat));
50
+ }
51
+ else {
52
+ gl.properties.remove(mesh.material);
53
+ }
54
+ }
55
+ })
56
+ if (gl.info?.programs)
57
+ gl.info.programs.length = 0;
58
58
  }
package/tsconfig.json CHANGED
@@ -1,20 +1,20 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "useDefineForClassFields": true,
5
- "module": "ESNext",
6
- "lib": ["ESNext", "DOM"],
7
- "moduleResolution": "Node",
8
- "strict": true,
9
- "sourceMap": true,
10
- "resolveJsonModule": true,
11
- "esModuleInterop": true,
12
- "noEmit": true,
13
- "noUnusedLocals": false,
14
- "noUnusedParameters": true,
15
- "noImplicitReturns": true,
16
- "noImplicitAny": false,
17
- "experimentalDecorators": true
18
- },
19
- "include": ["."]
20
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ESNext", "DOM"],
7
+ "moduleResolution": "Node",
8
+ "strict": true,
9
+ "sourceMap": true,
10
+ "resolveJsonModule": true,
11
+ "esModuleInterop": true,
12
+ "noEmit": true,
13
+ "noUnusedLocals": false,
14
+ "noUnusedParameters": true,
15
+ "noImplicitReturns": true,
16
+ "noImplicitAny": false,
17
+ "experimentalDecorators": true
18
+ },
19
+ "include": ["."]
20
+ }
@@ -1,39 +0,0 @@
1
- name: Release Workflow
2
- on:
3
- push:
4
- branches:
5
- - 'release/stable'
6
- - 'release/next'
7
- - 'release/experimental'
8
-
9
- jobs:
10
- run-release-script:
11
- runs-on: ubuntu-latest
12
- timeout-minutes: 5
13
- defaults:
14
- run:
15
- working-directory: ./
16
- steps:
17
- - name: Checkout code
18
- uses: actions/checkout@v4
19
- with:
20
- submodules: 'recursive' # Fetch all submodules recursively
21
- token: ${{ secrets.GH_RELEASE_TOKEN }}
22
-
23
- - name: Setup Node.js
24
- uses: actions/setup-node@v4
25
- with:
26
- node-version: '22'
27
-
28
- - name: Install dependencies
29
- run: npm ci
30
-
31
- - name: Publish to npm (stable branch)
32
- if: startsWith(github.ref_name, 'release/stable')
33
- run: npx --yes needle-publish-helper@stable publish "./dist" --webhook "${{ secrets.DISCORD_WEBHOOK }}" --access-token "${{ secrets.NPM_TOKEN }}" --tag "stable" --create-tag "release/"
34
-
35
- - name: Publish to npm (next and experimental branches)
36
- if: ${{ !startsWith(github.ref_name, 'release/stable') }}
37
- run: npx --yes needle-publish-helper@stable publish "./dist" --webhook "${{ secrets.DISCORD_WEBHOOK }}" --access-token "${{ secrets.NPM_TOKEN }}" --tag "${{ github.ref_name }}" --version+tag --version+hash --create-tag "release/"
38
-
39
-
package/bin/README.md DELETED
@@ -1,5 +0,0 @@
1
- Source: https://github.com/AcademySoftwareFoundation/MaterialX/tree/gh-pages
2
-
3
- Edits:
4
-
5
- - `JsMaterialXGenShader.js` added `export default MaterialX;` at bottom
@@ -1,18 +0,0 @@
1
- {
2
- "folders": [
3
- {
4
- "path": "."
5
- },
6
- {
7
- "name": "Needle",
8
- "path": "./node_modules/@needle-tools"
9
- }
10
- ],
11
- "settings": {
12
- "files.exclude": {
13
- "**/.git": true,
14
- "**/.DS_Store": true,
15
- "**/*.meta": true
16
- }
17
- }
18
- }