@needle-tools/materialx 1.0.0-alpha
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/.github/workflows/release.yml +39 -0
- package/README.md +1 -0
- package/bin/JsMaterialXCore.js +21 -0
- package/bin/JsMaterialXCore.wasm +0 -0
- package/bin/JsMaterialXGenShader.data +27551 -0
- package/bin/JsMaterialXGenShader.js +23 -0
- package/bin/JsMaterialXGenShader.wasm +0 -0
- package/bin/README.md +5 -0
- package/codegen/register_types.ts +6 -0
- package/index.ts +5 -0
- package/package.json +20 -0
- package/package.needle.json +3 -0
- package/src/helper.js +457 -0
- package/src/index.ts +5 -0
- package/src/loader/loader.needle.ts +106 -0
- package/src/loader/loader.three.ts +555 -0
- package/src/materialx.ts +220 -0
- package/src/textureHelper.ts +170 -0
- package/src/utils.ts +58 -0
- package/tsconfig.json +20 -0
- package/workspace.code-workspace +18 -0
package/src/materialx.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Context, delay, isDevEnvironment, ObjectUtils } from "@needle-tools/engine";
|
|
2
|
+
import MaterialX from "../bin/JsMaterialXGenShader.js";
|
|
3
|
+
import { debug } from "./utils.js";
|
|
4
|
+
import { renderPMREMToEquirect } from "./textureHelper.js";
|
|
5
|
+
import { Light, MeshBasicMaterial, Object3D, PMREMGenerator } from "three";
|
|
6
|
+
import { registerLights } from "./helper.js";
|
|
7
|
+
|
|
8
|
+
// Configure MaterialX with the correct path for its data files
|
|
9
|
+
const materialXConfig = {
|
|
10
|
+
locateFile: (path: string, scriptDirectory: string) => {
|
|
11
|
+
if (debug) console.debug("MaterialX locateFile called:", { path, scriptDirectory });
|
|
12
|
+
|
|
13
|
+
// Return the correct path for MaterialX data files
|
|
14
|
+
if (path.endsWith('.data') || path.endsWith('.wasm')) {
|
|
15
|
+
// For Vite dev server, we need to use the correct module path
|
|
16
|
+
const correctPath = new URL(`../bin/${path}`, import.meta.url).href;
|
|
17
|
+
if (debug) console.log("Resolved path:", correctPath);
|
|
18
|
+
return correctPath;
|
|
19
|
+
}
|
|
20
|
+
return scriptDirectory + path;
|
|
21
|
+
},
|
|
22
|
+
// Add buffer allocation to handle the data file properly
|
|
23
|
+
wasmBinary: null,
|
|
24
|
+
wasmMemory: null
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Global MaterialX module instance - initialized lazily
|
|
28
|
+
export const state = new class {
|
|
29
|
+
materialXModule: any = null;
|
|
30
|
+
materialXGenerator: any = null;
|
|
31
|
+
materialXGenContext: any = null;
|
|
32
|
+
materialXStdLib: any = null;
|
|
33
|
+
materialXInitPromise: Promise<void> | null = null;
|
|
34
|
+
|
|
35
|
+
// Global MaterialX environment instance
|
|
36
|
+
private _materialXEnvironment: MaterialXEnvironment | null = null;
|
|
37
|
+
get materialXEnvironment() {
|
|
38
|
+
this._materialXEnvironment ??= new MaterialXEnvironment();
|
|
39
|
+
return this._materialXEnvironment;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
// Initialize MaterialX WASM module lazily
|
|
45
|
+
export async function initializeMaterialX(): Promise<void> {
|
|
46
|
+
if (state.materialXInitPromise) {
|
|
47
|
+
return state.materialXInitPromise;
|
|
48
|
+
}
|
|
49
|
+
return state.materialXInitPromise = (async () => {
|
|
50
|
+
if (state.materialXModule) return; // Already initialized
|
|
51
|
+
if (debug) console.log("Initializing MaterialX WASM module...");
|
|
52
|
+
try {
|
|
53
|
+
const module = await MaterialX(materialXConfig);
|
|
54
|
+
if (debug) console.log("MaterialXLoader module loaded", module);
|
|
55
|
+
state.materialXModule = module;
|
|
56
|
+
|
|
57
|
+
// Initialize shader generator and context
|
|
58
|
+
state.materialXGenerator = module.EsslShaderGenerator.create();
|
|
59
|
+
state.materialXGenContext = new module.GenContext(state.materialXGenerator);
|
|
60
|
+
|
|
61
|
+
// Load standard libraries
|
|
62
|
+
const tempDoc = module.createDocument();
|
|
63
|
+
state.materialXStdLib = module.loadStandardLibraries(state.materialXGenContext);
|
|
64
|
+
tempDoc.setDataLibrary(state.materialXStdLib);
|
|
65
|
+
|
|
66
|
+
// Initialize basic lighting with default light rig
|
|
67
|
+
const defaultLightRigXml = `<?xml version="1.0"?>
|
|
68
|
+
<materialx version="1.39">
|
|
69
|
+
<!-- Default directional light -->
|
|
70
|
+
<directional_light name="default_light" type="lightshader">
|
|
71
|
+
<input name="direction" type="vector3" value="0.0, -1.0, -0.5" />
|
|
72
|
+
<input name="color" type="color3" value="1.0, 1.0, 1.0" />
|
|
73
|
+
<input name="intensity" type="float" value="1.0" />
|
|
74
|
+
</directional_light>
|
|
75
|
+
</materialx>`;
|
|
76
|
+
|
|
77
|
+
// This prewarms the shader generation context to have all light types
|
|
78
|
+
await registerLights(state.materialXModule, [], state.materialXGenContext);
|
|
79
|
+
|
|
80
|
+
if (debug) console.log("MaterialX generator initialized successfully");
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error("Failed to load MaterialX module:", error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// MaterialX Environment Manager - handles lighting and environment setup
|
|
89
|
+
export class MaterialXEnvironment {
|
|
90
|
+
private lights: any[] = [];
|
|
91
|
+
private lightData: any = null;
|
|
92
|
+
private radianceTexture: any = null;
|
|
93
|
+
private irradianceTexture: any = null;
|
|
94
|
+
private context: Context | null = null;
|
|
95
|
+
private initialized: boolean = false;
|
|
96
|
+
|
|
97
|
+
constructor() {
|
|
98
|
+
if (debug) console.log("MaterialX Environment created");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setContext(context: Context) {
|
|
102
|
+
this.context = context;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
// Initialize MaterialX lighting system based on the reference implementation
|
|
107
|
+
async initializeLighting(lightRigXml: string, renderer?: any, radianceTexture?: any, irradianceTexture?: any): Promise<void> {
|
|
108
|
+
if (!materialXModule || !materialXGenContext) {
|
|
109
|
+
console.warn("MaterialX module not initialized, skipping lighting setup");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
registerLights(materialXModule, this.lights, materialXGenContext);
|
|
114
|
+
}
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
// Initialize with Needle Engine context
|
|
118
|
+
async initializeFromContext(): Promise<void> {
|
|
119
|
+
if (!this.context) {
|
|
120
|
+
console.warn("No Needle context available for MaterialX environment initialization");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Prevent multiple initializations
|
|
125
|
+
if (this.initialized) {
|
|
126
|
+
if (debug) console.log("MaterialX environment already initialized, skipping");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Clean up previous textures if they exist
|
|
131
|
+
if (this.radianceTexture) {
|
|
132
|
+
if (debug) console.log("Disposing previous radiance texture");
|
|
133
|
+
this.radianceTexture.dispose();
|
|
134
|
+
this.radianceTexture = null;
|
|
135
|
+
}
|
|
136
|
+
if (this.irradianceTexture) {
|
|
137
|
+
if (debug) console.log("Disposing previous irradiance texture");
|
|
138
|
+
this.irradianceTexture.dispose();
|
|
139
|
+
this.irradianceTexture = null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get renderer from context
|
|
143
|
+
const renderer = this.context.renderer;
|
|
144
|
+
|
|
145
|
+
// TODO remove this delay; we should wait for the scene lighting to be ready
|
|
146
|
+
// and then update the uniforms
|
|
147
|
+
let envMap = this.context.scene.environment;
|
|
148
|
+
while (!envMap) {
|
|
149
|
+
await delay(200);
|
|
150
|
+
envMap = this.context.scene.environment;
|
|
151
|
+
}
|
|
152
|
+
var pmrem = new PMREMGenerator(renderer);
|
|
153
|
+
const target = pmrem.fromEquirectangular(envMap);
|
|
154
|
+
|
|
155
|
+
const radianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 0.0, 1024, 512, target.height);
|
|
156
|
+
const irradianceRenderTarget = renderPMREMToEquirect(renderer, target.texture, 1.0, 32, 16, target.height);
|
|
157
|
+
|
|
158
|
+
this.radianceTexture = radianceRenderTarget.texture;
|
|
159
|
+
this.irradianceTexture = irradianceRenderTarget.texture;
|
|
160
|
+
|
|
161
|
+
// Clean up PMREM generator and its render target
|
|
162
|
+
target.dispose();
|
|
163
|
+
pmrem.dispose();
|
|
164
|
+
|
|
165
|
+
if (debug) {
|
|
166
|
+
console.log({ radiance: this.radianceTexture, irradiance: this.irradianceTexture });
|
|
167
|
+
// Show both of them on cubes in the scene
|
|
168
|
+
const unlitMat = new MeshBasicMaterial();
|
|
169
|
+
const radianceMat = unlitMat.clone();
|
|
170
|
+
radianceMat.map = this.radianceTexture;
|
|
171
|
+
const radianceCube = ObjectUtils.createPrimitive("Cube", { material: radianceMat });
|
|
172
|
+
const irradianceMat = unlitMat.clone();
|
|
173
|
+
irradianceMat.map = this.irradianceTexture;
|
|
174
|
+
const irradianceCube = ObjectUtils.createPrimitive("Cube", { material: irradianceMat });
|
|
175
|
+
this.context.scene.add(radianceCube);
|
|
176
|
+
this.context.scene.add(irradianceCube);
|
|
177
|
+
radianceCube.position.set(2, 0, 0);
|
|
178
|
+
radianceCube.scale.y = 0.00001;
|
|
179
|
+
irradianceCube.position.set(-2, 0, 0);
|
|
180
|
+
irradianceCube.scale.y = 0.00001;
|
|
181
|
+
// await this.initializeLighting(defaultLightRigXml, renderer);
|
|
182
|
+
console.log("MaterialX environment initialized from Needle context", this, this.context.scene);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Find lights in scene
|
|
186
|
+
let lights = new Array<Light>();
|
|
187
|
+
this.context.scene.traverse((object: Object3D) => {
|
|
188
|
+
if ((object as Light).isLight) lights.push(object as Light);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
this.lightData = await registerLights(state.materialXModule, lights, state.materialXGenContext);
|
|
192
|
+
|
|
193
|
+
// Mark as initialized
|
|
194
|
+
this.initialized = true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// getLights() { return this.lights; }
|
|
198
|
+
getLightData() { return this.lightData; }
|
|
199
|
+
getRadianceTexture() { return this.radianceTexture; }
|
|
200
|
+
getIrradianceTexture() { return this.irradianceTexture; }
|
|
201
|
+
|
|
202
|
+
setRadianceTexture(texture: any) { this.radianceTexture = texture; }
|
|
203
|
+
setIrradianceTexture(texture: any) { this.irradianceTexture = texture; }
|
|
204
|
+
|
|
205
|
+
// Reset the environment to allow re-initialization
|
|
206
|
+
reset() {
|
|
207
|
+
if (debug) console.log("Resetting MaterialX environment");
|
|
208
|
+
if (this.radianceTexture) {
|
|
209
|
+
this.radianceTexture.dispose();
|
|
210
|
+
this.radianceTexture = null;
|
|
211
|
+
}
|
|
212
|
+
if (this.irradianceTexture) {
|
|
213
|
+
this.irradianceTexture.dispose();
|
|
214
|
+
this.irradianceTexture = null;
|
|
215
|
+
}
|
|
216
|
+
this.initialized = false;
|
|
217
|
+
// this.lights = [];
|
|
218
|
+
this.lightData = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +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;
|
|
170
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +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;
|
|
58
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +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
|
+
}
|