rayzee 5.10.2 → 6.0.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/README.md +82 -22
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js +2 -0
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js.map +1 -0
- package/dist/rayzee.es.js +1299 -1843
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +50 -74
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -4
- package/src/AssetConfig.js +56 -0
- package/src/EngineDefaults.js +5 -3
- package/src/EngineEvents.js +1 -0
- package/src/Passes/AIUpscaler.js +44 -22
- package/src/Passes/OIDNDenoiser.js +4 -104
- package/src/PathTracerApp.js +59 -63
- package/src/Processor/AssetLoader.js +5 -2
- package/src/Processor/TextureCreator.js +42 -15
- package/src/Processor/Workers/AIUpscalerWorker.js +21 -6
- package/src/Stages/ASVGF.js +6 -27
- package/src/Stages/AdaptiveSampling.js +10 -26
- package/src/Stages/PathTracer.js +4 -5
- package/src/TSL/BVHTraversal.js +2 -18
- package/src/TSL/Clearcoat.js +1 -2
- package/src/TSL/Common.js +0 -13
- package/src/TSL/EmissiveSampling.js +0 -16
- package/src/TSL/Environment.js +0 -7
- package/src/TSL/LightsDirect.js +3 -379
- package/src/TSL/LightsSampling.js +0 -171
- package/src/TSL/MaterialEvaluation.js +3 -103
- package/src/TSL/MaterialProperties.js +1 -56
- package/src/TSL/MaterialSampling.js +2 -284
- package/src/TSL/MaterialTransmission.js +0 -93
- package/src/TSL/Random.js +0 -23
- package/src/TSL/Struct.js +0 -21
- package/src/TSL/TextureSampling.js +0 -69
- package/src/index.js +5 -2
- package/src/managers/DenoisingManager.js +13 -5
- package/src/managers/OverlayManager.js +14 -2
- package/src/managers/VideoRenderManager.js +4 -4
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js +0 -2
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +0 -1
- package/src/Processor/createRenderTargetHelper.js +0 -521
- package/src/TSL/RayIntersection.js +0 -162
- package/src/managers/helpers/StatsHelper.js +0 -45
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createRenderTargetHelper - A component for displaying Three.js render targets in a resizable window
|
|
3
|
-
*
|
|
4
|
-
* Uses async readback for pixel data.
|
|
5
|
-
*
|
|
6
|
-
* @param {THREE.WebGPURenderer} renderer - The renderer instance
|
|
7
|
-
* @param {THREE.RenderTarget} renderTargetOrTexture - The render target to display
|
|
8
|
-
* @param {Object} options - Optional configuration
|
|
9
|
-
* @param {number} options.width - Initial width of the view (default: 200)
|
|
10
|
-
* @param {number} options.height - Initial height of the view (default: 200)
|
|
11
|
-
* @param {string} options.position - Position on screen ('bottom-right', 'bottom-left', 'top-right', 'top-left') (default: 'bottom-right')
|
|
12
|
-
* @param {boolean} options.flipX - Flip the image horizontally (default: true)
|
|
13
|
-
* @param {boolean} options.flipY - Flip the image vertically (default: true)
|
|
14
|
-
* @param {boolean} options.autoUpdate - Whether to automatically update on animation frames (default: false)
|
|
15
|
-
* @param {string} options.title - Title to display in the header (default: 'Render Target')
|
|
16
|
-
* @param {number} options.textureIndex - For MRT render targets, which texture attachment to read (default: 0)
|
|
17
|
-
* @param {string} options.transform - Optional shader transform: 'normal-remap' remaps [0,1] to visible range
|
|
18
|
-
* @returns {HTMLElement} The container element with attached methods
|
|
19
|
-
*/
|
|
20
|
-
export function createRenderTargetHelper( renderer, renderTargetOrTexture, options = {} ) {
|
|
21
|
-
|
|
22
|
-
// MRT texture index for readback
|
|
23
|
-
const textureIndex = options.textureIndex || 0;
|
|
24
|
-
|
|
25
|
-
// Detect if input is a Texture or RenderTarget
|
|
26
|
-
const isTexture = renderTargetOrTexture.isTexture === true;
|
|
27
|
-
|
|
28
|
-
let renderTarget;
|
|
29
|
-
|
|
30
|
-
if ( isTexture ) {
|
|
31
|
-
|
|
32
|
-
console.warn( 'RenderTargetHelper: Direct Texture input is not supported. Pass a RenderTarget instead.' );
|
|
33
|
-
renderTarget = null;
|
|
34
|
-
|
|
35
|
-
} else {
|
|
36
|
-
|
|
37
|
-
renderTarget = renderTargetOrTexture;
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Default options
|
|
42
|
-
const config = {
|
|
43
|
-
width: options.width || 200,
|
|
44
|
-
height: options.height || 200,
|
|
45
|
-
position: options.position || 'bottom-right',
|
|
46
|
-
flipX: options.flipX !== undefined ? options.flipX : false,
|
|
47
|
-
flipY: options.flipY !== undefined ? options.flipY : false,
|
|
48
|
-
autoUpdate: options.autoUpdate || false,
|
|
49
|
-
theme: options.theme || 'dark', // 'light' or 'dark' - changed default to dark to match the app's theme
|
|
50
|
-
title: options.title || renderTarget?.name || 'Render Target'
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// Create container
|
|
54
|
-
const container = document.createElement( 'div' );
|
|
55
|
-
container.className = 'render-target-helper';
|
|
56
|
-
|
|
57
|
-
// Apply styles based on position
|
|
58
|
-
const positionStyles = {
|
|
59
|
-
'bottom-right': { bottom: '48px', right: '10px' }, // Adjusted for stats position
|
|
60
|
-
'bottom-left': { bottom: '10px', left: '10px' },
|
|
61
|
-
'top-right': { top: '10px', right: '10px' },
|
|
62
|
-
'top-left': { top: '10px', left: '10px' }
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Theme styles
|
|
66
|
-
const themeStyles = {
|
|
67
|
-
'light': {
|
|
68
|
-
backgroundColor: 'white',
|
|
69
|
-
border: '1px solid #ddd',
|
|
70
|
-
color: '#333'
|
|
71
|
-
},
|
|
72
|
-
'dark': {
|
|
73
|
-
backgroundColor: '#1e293b', // Match app's slate color
|
|
74
|
-
border: '1px solid #334155',
|
|
75
|
-
color: '#f8fafc'
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
Object.assign( container.style, {
|
|
80
|
-
display: 'flex',
|
|
81
|
-
flexDirection: 'column',
|
|
82
|
-
position: 'fixed',
|
|
83
|
-
resize: 'both',
|
|
84
|
-
overflow: 'hidden',
|
|
85
|
-
padding: '8px',
|
|
86
|
-
borderRadius: '4px',
|
|
87
|
-
boxShadow: '0 5px 15px rgba(0,0,0,0.3)',
|
|
88
|
-
transition: 'opacity 0.2s ease',
|
|
89
|
-
zIndex: '1000',
|
|
90
|
-
minWidth: '100px',
|
|
91
|
-
minHeight: '100px',
|
|
92
|
-
maxWidth: '500px',
|
|
93
|
-
maxHeight: '500px',
|
|
94
|
-
width: `${config.width}px`,
|
|
95
|
-
height: `${config.height}px`,
|
|
96
|
-
...positionStyles[ config.position ],
|
|
97
|
-
...themeStyles[ config.theme ]
|
|
98
|
-
} );
|
|
99
|
-
|
|
100
|
-
// Create title bar with controls
|
|
101
|
-
const titleBar = document.createElement( 'div' );
|
|
102
|
-
titleBar.style.display = 'flex';
|
|
103
|
-
titleBar.style.justifyContent = 'space-between';
|
|
104
|
-
titleBar.style.alignItems = 'center';
|
|
105
|
-
titleBar.style.marginBottom = '4px';
|
|
106
|
-
titleBar.style.cursor = 'move';
|
|
107
|
-
titleBar.style.userSelect = 'none';
|
|
108
|
-
|
|
109
|
-
// Title
|
|
110
|
-
const title = document.createElement( 'span' );
|
|
111
|
-
title.textContent = config.title;
|
|
112
|
-
title.style.fontSize = '12px';
|
|
113
|
-
title.style.fontFamily = 'monospace';
|
|
114
|
-
title.style.color = themeStyles[ config.theme ].color;
|
|
115
|
-
|
|
116
|
-
// Controls
|
|
117
|
-
const controls = document.createElement( 'div' );
|
|
118
|
-
|
|
119
|
-
// Close button
|
|
120
|
-
const closeBtn = document.createElement( 'button' );
|
|
121
|
-
closeBtn.innerHTML = '×';
|
|
122
|
-
closeBtn.style.background = 'none';
|
|
123
|
-
closeBtn.style.border = 'none';
|
|
124
|
-
closeBtn.style.cursor = 'pointer';
|
|
125
|
-
closeBtn.style.fontSize = '16px';
|
|
126
|
-
closeBtn.style.color = themeStyles[ config.theme ].color;
|
|
127
|
-
closeBtn.style.padding = '0 4px';
|
|
128
|
-
closeBtn.title = 'Close';
|
|
129
|
-
|
|
130
|
-
closeBtn.onclick = () => {
|
|
131
|
-
|
|
132
|
-
container.style.display = 'none';
|
|
133
|
-
if ( config.autoUpdate ) {
|
|
134
|
-
|
|
135
|
-
cancelAnimationFrame( animFrameId );
|
|
136
|
-
animFrameId = null;
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// Add refresh button
|
|
143
|
-
const refreshBtn = document.createElement( 'button' );
|
|
144
|
-
refreshBtn.innerHTML = '⟳';
|
|
145
|
-
refreshBtn.style.background = 'none';
|
|
146
|
-
refreshBtn.style.border = 'none';
|
|
147
|
-
refreshBtn.style.cursor = 'pointer';
|
|
148
|
-
refreshBtn.style.fontSize = '14px';
|
|
149
|
-
refreshBtn.style.color = themeStyles[ config.theme ].color;
|
|
150
|
-
refreshBtn.style.padding = '0 4px';
|
|
151
|
-
refreshBtn.title = 'Refresh';
|
|
152
|
-
|
|
153
|
-
refreshBtn.onclick = () => {
|
|
154
|
-
|
|
155
|
-
container.update();
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
controls.appendChild( refreshBtn );
|
|
160
|
-
controls.appendChild( closeBtn );
|
|
161
|
-
titleBar.appendChild( title );
|
|
162
|
-
titleBar.appendChild( controls );
|
|
163
|
-
container.appendChild( titleBar );
|
|
164
|
-
|
|
165
|
-
// Canvas for displaying the render target
|
|
166
|
-
const domCanvas = document.createElement( 'canvas' );
|
|
167
|
-
domCanvas.style.width = '100%';
|
|
168
|
-
domCanvas.style.height = 'calc(100% - 20px)';
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// Apply flipping if needed
|
|
172
|
-
let transform = '';
|
|
173
|
-
if ( config.flipX ) transform += 'scaleX(-1) ';
|
|
174
|
-
if ( config.flipY ) transform += 'scaleY(-1) ';
|
|
175
|
-
domCanvas.style.transform = transform.trim();
|
|
176
|
-
|
|
177
|
-
container.appendChild( domCanvas );
|
|
178
|
-
|
|
179
|
-
// Get render target dimensions
|
|
180
|
-
let width = renderTarget ? renderTarget.width : 1;
|
|
181
|
-
let height = renderTarget ? renderTarget.height : 1;
|
|
182
|
-
|
|
183
|
-
// Initialize canvas
|
|
184
|
-
domCanvas.width = width;
|
|
185
|
-
domCanvas.height = height;
|
|
186
|
-
|
|
187
|
-
const context = domCanvas.getContext( '2d' );
|
|
188
|
-
|
|
189
|
-
// Pixel data buffer
|
|
190
|
-
let clampedPixels = new Uint8ClampedArray( 4 * width * height );
|
|
191
|
-
|
|
192
|
-
// Guard against concurrent async reads (WebGPU)
|
|
193
|
-
let pendingRead = false;
|
|
194
|
-
|
|
195
|
-
// Make container draggable
|
|
196
|
-
let isDragging = false;
|
|
197
|
-
let dragOffsetX = 0;
|
|
198
|
-
let dragOffsetY = 0;
|
|
199
|
-
|
|
200
|
-
titleBar.addEventListener( 'pointerdown', ( e ) => {
|
|
201
|
-
|
|
202
|
-
isDragging = true;
|
|
203
|
-
dragOffsetX = e.clientX - container.offsetLeft;
|
|
204
|
-
dragOffsetY = e.clientY - container.offsetTop;
|
|
205
|
-
document.body.style.userSelect = 'none'; // Prevent text selection during drag
|
|
206
|
-
|
|
207
|
-
} );
|
|
208
|
-
|
|
209
|
-
function onPointerMove( e ) {
|
|
210
|
-
|
|
211
|
-
if ( ! isDragging ) return;
|
|
212
|
-
|
|
213
|
-
const newLeft = e.clientX - dragOffsetX;
|
|
214
|
-
const newTop = e.clientY - dragOffsetY;
|
|
215
|
-
|
|
216
|
-
const maxX = window.innerWidth - container.offsetWidth;
|
|
217
|
-
const maxY = window.innerHeight - container.offsetHeight;
|
|
218
|
-
|
|
219
|
-
container.style.left = `${Math.max( 0, Math.min( newLeft, maxX ) )}px`;
|
|
220
|
-
container.style.top = `${Math.max( 0, Math.min( newTop, maxY ) )}px`;
|
|
221
|
-
|
|
222
|
-
container.style.bottom = 'auto';
|
|
223
|
-
container.style.right = 'auto';
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function onPointerUp() {
|
|
228
|
-
|
|
229
|
-
isDragging = false;
|
|
230
|
-
document.body.style.userSelect = '';
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
window.addEventListener( 'pointermove', onPointerMove );
|
|
235
|
-
window.addEventListener( 'pointerup', onPointerUp );
|
|
236
|
-
|
|
237
|
-
// Optimize resize handling
|
|
238
|
-
function handleResize() {
|
|
239
|
-
|
|
240
|
-
if ( ! renderTarget ) return;
|
|
241
|
-
|
|
242
|
-
// Get current dimensions from source
|
|
243
|
-
const currentWidth = renderTarget.width;
|
|
244
|
-
const currentHeight = renderTarget.height;
|
|
245
|
-
|
|
246
|
-
// Check if dimensions have changed
|
|
247
|
-
if ( width !== currentWidth || height !== currentHeight ) {
|
|
248
|
-
|
|
249
|
-
width = currentWidth;
|
|
250
|
-
height = currentHeight;
|
|
251
|
-
|
|
252
|
-
// Resize canvas to match dimensions
|
|
253
|
-
domCanvas.width = width;
|
|
254
|
-
domCanvas.height = height;
|
|
255
|
-
|
|
256
|
-
// Recreate pixel buffer
|
|
257
|
-
clampedPixels = new Uint8ClampedArray( 4 * width * height );
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Update dimensions display
|
|
262
|
-
title.textContent = `${config.title} (${width}×${height})`;
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Decode a 16-bit half-float to a 32-bit float.
|
|
268
|
-
*/
|
|
269
|
-
function halfToFloat( h ) {
|
|
270
|
-
|
|
271
|
-
const s = ( h & 0x8000 ) >> 15;
|
|
272
|
-
const e = ( h & 0x7C00 ) >> 10;
|
|
273
|
-
const f = h & 0x03FF;
|
|
274
|
-
|
|
275
|
-
if ( e === 0 ) return ( s ? - 1 : 1 ) * Math.pow( 2, - 14 ) * ( f / 1024 );
|
|
276
|
-
if ( e === 31 ) return f ? NaN : ( s ? - Infinity : Infinity );
|
|
277
|
-
return ( s ? - 1 : 1 ) * Math.pow( 2, e - 15 ) * ( 1 + f / 1024 );
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Draw a pixel buffer to the 2D canvas.
|
|
283
|
-
* Handles Float32Array (float 0-1), Uint16Array (half-float), and Uint8Array (0-255).
|
|
284
|
-
*/
|
|
285
|
-
function drawPixelBuffer( buffer ) {
|
|
286
|
-
|
|
287
|
-
const len = Math.min( buffer.length, clampedPixels.length );
|
|
288
|
-
|
|
289
|
-
if ( buffer instanceof Uint8Array || buffer instanceof Uint8ClampedArray ) {
|
|
290
|
-
|
|
291
|
-
// Values already in 0-255 range
|
|
292
|
-
clampedPixels.set( buffer.subarray( 0, len ) );
|
|
293
|
-
|
|
294
|
-
} else if ( buffer instanceof Uint16Array ) {
|
|
295
|
-
|
|
296
|
-
// Half-float values — decode then scale to 0-255
|
|
297
|
-
for ( let i = 0; i < len; i ++ ) {
|
|
298
|
-
|
|
299
|
-
const val = halfToFloat( buffer[ i ] );
|
|
300
|
-
clampedPixels[ i ] = Math.min( 255, Math.max( 0, ( val || 0 ) * 255 ) );
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
} else {
|
|
305
|
-
|
|
306
|
-
// Float32 values — scale to 0-255
|
|
307
|
-
for ( let i = 0; i < len; i ++ ) {
|
|
308
|
-
|
|
309
|
-
clampedPixels[ i ] = Math.min( 255, Math.max( 0, buffer[ i ] * 255 ) );
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if ( width === 0 || height === 0 ) return;
|
|
316
|
-
|
|
317
|
-
const imageData = new ImageData( clampedPixels, width, height );
|
|
318
|
-
context.putImageData( imageData, 0, 0 );
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Update the display with the current render target contents
|
|
323
|
-
container.update = function update() {
|
|
324
|
-
|
|
325
|
-
if ( ! renderTarget ) return;
|
|
326
|
-
|
|
327
|
-
handleResize();
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
|
|
331
|
-
// Asynchronous pixel readback
|
|
332
|
-
if ( pendingRead ) return; // skip if previous read is still in flight
|
|
333
|
-
pendingRead = true;
|
|
334
|
-
|
|
335
|
-
renderer.readRenderTargetPixelsAsync( renderTarget, 0, 0, width, height, textureIndex )
|
|
336
|
-
.then( ( buffer ) => {
|
|
337
|
-
|
|
338
|
-
pendingRead = false;
|
|
339
|
-
drawPixelBuffer( buffer );
|
|
340
|
-
|
|
341
|
-
} )
|
|
342
|
-
.catch( ( err ) => {
|
|
343
|
-
|
|
344
|
-
pendingRead = false;
|
|
345
|
-
console.error( 'RenderTargetHelper: readback error:', err );
|
|
346
|
-
|
|
347
|
-
} );
|
|
348
|
-
|
|
349
|
-
} catch ( error ) {
|
|
350
|
-
|
|
351
|
-
console.error( 'Error updating render target helper:', error );
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
function onContainerMouseDown() {
|
|
358
|
-
|
|
359
|
-
window.addEventListener( 'mousemove', handleResize );
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function onMouseUp() {
|
|
364
|
-
|
|
365
|
-
window.removeEventListener( 'mousemove', handleResize );
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
container.addEventListener( 'mousedown', onContainerMouseDown );
|
|
370
|
-
window.addEventListener( 'mouseup', onMouseUp );
|
|
371
|
-
window.addEventListener( 'resize', handleResize );
|
|
372
|
-
|
|
373
|
-
// Auto-update animation frame
|
|
374
|
-
let animFrameId = null;
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Show the helper if hidden
|
|
378
|
-
*/
|
|
379
|
-
container.show = function show() {
|
|
380
|
-
|
|
381
|
-
container.style.display = 'flex';
|
|
382
|
-
if ( config.autoUpdate && ! animFrameId ) {
|
|
383
|
-
|
|
384
|
-
container.startAutoUpdate();
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Hide the helper
|
|
392
|
-
*/
|
|
393
|
-
container.hide = function hide() {
|
|
394
|
-
|
|
395
|
-
container.style.display = 'none';
|
|
396
|
-
if ( config.autoUpdate && animFrameId ) {
|
|
397
|
-
|
|
398
|
-
cancelAnimationFrame( animFrameId );
|
|
399
|
-
animFrameId = null;
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Toggle visibility
|
|
407
|
-
*/
|
|
408
|
-
container.toggle = function toggle() {
|
|
409
|
-
|
|
410
|
-
if ( container.style.display === 'none' ) {
|
|
411
|
-
|
|
412
|
-
container.show();
|
|
413
|
-
|
|
414
|
-
} else {
|
|
415
|
-
|
|
416
|
-
container.hide();
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return container.style.display !== 'none';
|
|
421
|
-
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Start auto-updating
|
|
426
|
-
*/
|
|
427
|
-
container.startAutoUpdate = function startAutoUpdate() {
|
|
428
|
-
|
|
429
|
-
if ( animFrameId ) return;
|
|
430
|
-
|
|
431
|
-
const updateLoop = () => {
|
|
432
|
-
|
|
433
|
-
container.update();
|
|
434
|
-
animFrameId = requestAnimationFrame( updateLoop );
|
|
435
|
-
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
animFrameId = requestAnimationFrame( updateLoop );
|
|
439
|
-
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Stop auto-updating
|
|
444
|
-
*/
|
|
445
|
-
container.stopAutoUpdate = function stopAutoUpdate() {
|
|
446
|
-
|
|
447
|
-
if ( animFrameId ) {
|
|
448
|
-
|
|
449
|
-
cancelAnimationFrame( animFrameId );
|
|
450
|
-
animFrameId = null;
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Dispose and clean up resources
|
|
458
|
-
*/
|
|
459
|
-
container.dispose = function dispose() {
|
|
460
|
-
|
|
461
|
-
if ( animFrameId ) {
|
|
462
|
-
|
|
463
|
-
cancelAnimationFrame( animFrameId );
|
|
464
|
-
animFrameId = null;
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Remove window listeners — these close over renderer/renderTarget/container
|
|
469
|
-
// and pin the entire helper graph alive until the page unloads if not cleaned up.
|
|
470
|
-
window.removeEventListener( 'pointermove', onPointerMove );
|
|
471
|
-
window.removeEventListener( 'pointerup', onPointerUp );
|
|
472
|
-
window.removeEventListener( 'mouseup', onMouseUp );
|
|
473
|
-
window.removeEventListener( 'mousemove', handleResize );
|
|
474
|
-
window.removeEventListener( 'resize', handleResize );
|
|
475
|
-
|
|
476
|
-
if ( container.parentNode ) {
|
|
477
|
-
|
|
478
|
-
container.parentNode.removeChild( container );
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Drop closures attached to the container. These close over `renderer`,
|
|
483
|
-
// `renderTargetOrTexture`, and the canvas — keeping them around after the
|
|
484
|
-
// owning stage has been disposed retains the entire Three.js WebGPU graph.
|
|
485
|
-
container.startAutoUpdate = null;
|
|
486
|
-
container.stopAutoUpdate = null;
|
|
487
|
-
container.show = null;
|
|
488
|
-
container.hide = null;
|
|
489
|
-
container.toggle = null;
|
|
490
|
-
container.update = null;
|
|
491
|
-
container.dispose = null;
|
|
492
|
-
|
|
493
|
-
if ( domCanvas ) {
|
|
494
|
-
|
|
495
|
-
domCanvas.width = 0;
|
|
496
|
-
domCanvas.height = 0;
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
clampedPixels = null;
|
|
501
|
-
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
// Start auto-update if configured
|
|
505
|
-
if ( config.autoUpdate ) {
|
|
506
|
-
|
|
507
|
-
container.startAutoUpdate();
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Maintain backward compatibility with original implementation
|
|
512
|
-
if ( container.style.display === 'none' ) {
|
|
513
|
-
|
|
514
|
-
container.style.display = 'flex';
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return container;
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { Fn, float, vec2, vec3, int, If, dot, cross, abs, normalize, sqrt, min, max, select } from 'three/tsl';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
HitInfo,
|
|
5
|
-
} from './Struct.js';
|
|
6
|
-
|
|
7
|
-
// Optimized Intersection with Geometry only (no attributes)
|
|
8
|
-
// Returns { hit, t, u, v }
|
|
9
|
-
export const RayTriangleGeometry = Fn( ( [ ray, posA, posB, posC ] ) => {
|
|
10
|
-
|
|
11
|
-
const edge1 = posB.sub( posA );
|
|
12
|
-
const edge2 = posC.sub( posA );
|
|
13
|
-
const h = cross( ray.direction, edge2 );
|
|
14
|
-
const a = dot( edge1, h ).toVar();
|
|
15
|
-
|
|
16
|
-
const t = float( 0.0 ).toVar();
|
|
17
|
-
const u = float( 0.0 ).toVar();
|
|
18
|
-
const v = float( 0.0 ).toVar();
|
|
19
|
-
const hit = int( 0 ).toVar();
|
|
20
|
-
|
|
21
|
-
If( abs( a ).greaterThan( 1e-8 ), () => {
|
|
22
|
-
|
|
23
|
-
const f = float( 1.0 ).div( a );
|
|
24
|
-
const s = ray.origin.sub( posA );
|
|
25
|
-
u.assign( f.mul( dot( s, h ) ) );
|
|
26
|
-
|
|
27
|
-
If( u.greaterThanEqual( 0.0 ).and( u.lessThanEqual( 1.0 ) ), () => {
|
|
28
|
-
|
|
29
|
-
const q = cross( s, edge1 );
|
|
30
|
-
v.assign( f.mul( dot( ray.direction, q ) ) );
|
|
31
|
-
|
|
32
|
-
If( v.greaterThanEqual( 0.0 ).and( u.add( v ).lessThanEqual( 1.0 ) ), () => {
|
|
33
|
-
|
|
34
|
-
t.assign( f.mul( dot( edge2, q ) ) );
|
|
35
|
-
|
|
36
|
-
If( t.greaterThan( 1e-8 ), () => {
|
|
37
|
-
|
|
38
|
-
hit.assign( 1 );
|
|
39
|
-
|
|
40
|
-
} );
|
|
41
|
-
|
|
42
|
-
} );
|
|
43
|
-
|
|
44
|
-
} );
|
|
45
|
-
|
|
46
|
-
} );
|
|
47
|
-
|
|
48
|
-
return hit;
|
|
49
|
-
|
|
50
|
-
} );
|
|
51
|
-
|
|
52
|
-
// Calculate the intersection of a ray with a triangle using Möller-Trumbore algorithm
|
|
53
|
-
export const RayTriangle = Fn( ( [ ray, tri ] ) => {
|
|
54
|
-
|
|
55
|
-
const didHit = int( 0 ).toVar();
|
|
56
|
-
const dst = float( 1.0e20 ).toVar();
|
|
57
|
-
const hitPoint = vec3( 0.0 ).toVar();
|
|
58
|
-
const normal = vec3( 0.0, 0.0, 1.0 ).toVar();
|
|
59
|
-
const uv = vec2( 0.0 ).toVar();
|
|
60
|
-
const material = int( - 1 ).toVar();
|
|
61
|
-
|
|
62
|
-
const edge1 = tri.posB.sub( tri.posA );
|
|
63
|
-
const edge2 = tri.posC.sub( tri.posA );
|
|
64
|
-
const h = cross( ray.direction, edge2 );
|
|
65
|
-
const a = dot( edge1, h ).toVar();
|
|
66
|
-
|
|
67
|
-
If( abs( a ).greaterThan( 1e-8 ), () => {
|
|
68
|
-
|
|
69
|
-
const f = float( 1.0 ).div( a );
|
|
70
|
-
const s = ray.origin.sub( tri.posA );
|
|
71
|
-
const u = f.mul( dot( s, h ) ).toVar();
|
|
72
|
-
|
|
73
|
-
If( u.greaterThanEqual( 0.0 ).and( u.lessThanEqual( 1.0 ) ), () => {
|
|
74
|
-
|
|
75
|
-
const q = cross( s, edge1 );
|
|
76
|
-
const v = f.mul( dot( ray.direction, q ) ).toVar();
|
|
77
|
-
|
|
78
|
-
If( v.greaterThanEqual( 0.0 ).and( u.add( v ).lessThanEqual( 1.0 ) ), () => {
|
|
79
|
-
|
|
80
|
-
const t = f.mul( dot( edge2, q ) ).toVar();
|
|
81
|
-
|
|
82
|
-
If( t.greaterThan( 1e-8 ).and( t.lessThan( dst ) ), () => {
|
|
83
|
-
|
|
84
|
-
didHit.assign( 1 );
|
|
85
|
-
dst.assign( t );
|
|
86
|
-
hitPoint.assign( ray.origin.add( ray.direction.mul( t ) ) );
|
|
87
|
-
|
|
88
|
-
// Interpolate normal using barycentric coordinates
|
|
89
|
-
const w = float( 1.0 ).sub( u ).sub( v );
|
|
90
|
-
normal.assign( normalize(
|
|
91
|
-
tri.normalA.mul( w ).add( tri.normalB.mul( u ) ).add( tri.normalC.mul( v ) )
|
|
92
|
-
) );
|
|
93
|
-
|
|
94
|
-
// Interpolate UV coordinates
|
|
95
|
-
uv.assign( tri.uvA.mul( w ).add( tri.uvB.mul( u ) ).add( tri.uvC.mul( v ) ) );
|
|
96
|
-
|
|
97
|
-
// Set material index
|
|
98
|
-
material.assign( tri.material );
|
|
99
|
-
|
|
100
|
-
} );
|
|
101
|
-
|
|
102
|
-
} );
|
|
103
|
-
|
|
104
|
-
} );
|
|
105
|
-
|
|
106
|
-
} );
|
|
107
|
-
|
|
108
|
-
return HitInfo( { didHit, dst, hitPoint, normal, uv, materialIndex: material, meshIndex: int( - 1 ), boxTests: int( 0 ), triTests: int( 0 ) } );
|
|
109
|
-
|
|
110
|
-
} );
|
|
111
|
-
|
|
112
|
-
// Ray-sphere intersection
|
|
113
|
-
export const RaySphere = Fn( ( [ ray, sphere ] ) => {
|
|
114
|
-
|
|
115
|
-
const didHit = int( 0 ).toVar();
|
|
116
|
-
const dst = float( 1.0e20 ).toVar();
|
|
117
|
-
const hitPoint = vec3( 0.0 ).toVar();
|
|
118
|
-
const normal = vec3( 0.0, 0.0, 1.0 ).toVar();
|
|
119
|
-
const material = int( - 1 ).toVar();
|
|
120
|
-
|
|
121
|
-
const oc = ray.origin.sub( sphere.position );
|
|
122
|
-
const a = dot( ray.direction, ray.direction );
|
|
123
|
-
const b = float( 2.0 ).mul( dot( oc, ray.direction ) );
|
|
124
|
-
const c = dot( oc, oc ).sub( sphere.radius.mul( sphere.radius ) );
|
|
125
|
-
const discriminant = b.mul( b ).sub( float( 4.0 ).mul( a ).mul( c ) ).toVar();
|
|
126
|
-
|
|
127
|
-
If( discriminant.greaterThan( 0.0 ), () => {
|
|
128
|
-
|
|
129
|
-
const t = b.negate().sub( sqrt( discriminant ) ).div( float( 2.0 ).mul( a ) ).toVar();
|
|
130
|
-
|
|
131
|
-
If( t.greaterThan( 0.0 ), () => {
|
|
132
|
-
|
|
133
|
-
didHit.assign( 1 );
|
|
134
|
-
dst.assign( t );
|
|
135
|
-
hitPoint.assign( ray.origin.add( ray.direction.mul( t ) ) );
|
|
136
|
-
normal.assign( normalize( hitPoint.sub( sphere.position ) ) );
|
|
137
|
-
material.assign( sphere.material );
|
|
138
|
-
|
|
139
|
-
} );
|
|
140
|
-
|
|
141
|
-
} );
|
|
142
|
-
|
|
143
|
-
return HitInfo( { didHit, dst, hitPoint, normal, uv: vec2( 0.0 ), materialIndex: material, meshIndex: int( - 1 ), boxTests: int( 0 ), triTests: int( 0 ) } );
|
|
144
|
-
|
|
145
|
-
} );
|
|
146
|
-
|
|
147
|
-
// Fast ray-AABB distance calculation with early exit optimization
|
|
148
|
-
export const fastRayAABBDst = Fn( ( [ ray, invDir, boxMin, boxMax ] ) => {
|
|
149
|
-
|
|
150
|
-
const t1 = boxMin.sub( ray.origin ).mul( invDir );
|
|
151
|
-
const t2 = boxMax.sub( ray.origin ).mul( invDir );
|
|
152
|
-
|
|
153
|
-
const tMin = min( t1, t2 );
|
|
154
|
-
const tMax = max( t1, t2 );
|
|
155
|
-
|
|
156
|
-
const dstNear = max( max( tMin.x, tMin.y ), tMin.z );
|
|
157
|
-
const dstFar = min( min( tMax.x, tMax.y ), tMax.z ).mul( 1.00000024 ); // Robust traversal: 2 ULP padding (Ize 2013)
|
|
158
|
-
|
|
159
|
-
// Optimized early rejection
|
|
160
|
-
return select( dstFar.greaterThanEqual( max( dstNear, 0.0 ) ), max( dstNear, 0.0 ), float( 1e20 ) );
|
|
161
|
-
|
|
162
|
-
} );
|