topazcube 0.1.31 → 0.1.35

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.
Files changed (103) hide show
  1. package/LICENSE.txt +0 -0
  2. package/README.md +0 -0
  3. package/dist/Renderer.cjs +20844 -0
  4. package/dist/Renderer.cjs.map +1 -0
  5. package/dist/Renderer.js +20827 -0
  6. package/dist/Renderer.js.map +1 -0
  7. package/dist/client.cjs +91 -260
  8. package/dist/client.cjs.map +1 -1
  9. package/dist/client.js +68 -215
  10. package/dist/client.js.map +1 -1
  11. package/dist/server.cjs +165 -432
  12. package/dist/server.cjs.map +1 -1
  13. package/dist/server.js +117 -370
  14. package/dist/server.js.map +1 -1
  15. package/dist/terminal.cjs +113 -200
  16. package/dist/terminal.cjs.map +1 -1
  17. package/dist/terminal.js +50 -51
  18. package/dist/terminal.js.map +1 -1
  19. package/dist/utils-CRhi1BDa.cjs +259 -0
  20. package/dist/utils-CRhi1BDa.cjs.map +1 -0
  21. package/dist/utils-D7tXt6-2.js +260 -0
  22. package/dist/utils-D7tXt6-2.js.map +1 -0
  23. package/package.json +19 -15
  24. package/src/{client.ts → network/client.js} +170 -403
  25. package/src/{compress-browser.ts → network/compress-browser.js} +2 -4
  26. package/src/{compress-node.ts → network/compress-node.js} +8 -14
  27. package/src/{server.ts → network/server.js} +229 -317
  28. package/src/{terminal.js → network/terminal.js} +0 -0
  29. package/src/{topazcube.ts → network/topazcube.js} +2 -2
  30. package/src/network/utils.js +375 -0
  31. package/src/renderer/Camera.js +191 -0
  32. package/src/renderer/DebugUI.js +703 -0
  33. package/src/renderer/Geometry.js +1049 -0
  34. package/src/renderer/Material.js +64 -0
  35. package/src/renderer/Mesh.js +211 -0
  36. package/src/renderer/Node.js +112 -0
  37. package/src/renderer/Pipeline.js +645 -0
  38. package/src/renderer/Renderer.js +1496 -0
  39. package/src/renderer/Skin.js +792 -0
  40. package/src/renderer/Texture.js +584 -0
  41. package/src/renderer/core/AssetManager.js +394 -0
  42. package/src/renderer/core/CullingSystem.js +308 -0
  43. package/src/renderer/core/EntityManager.js +541 -0
  44. package/src/renderer/core/InstanceManager.js +343 -0
  45. package/src/renderer/core/ParticleEmitter.js +358 -0
  46. package/src/renderer/core/ParticleSystem.js +564 -0
  47. package/src/renderer/core/SpriteSystem.js +349 -0
  48. package/src/renderer/gltf.js +563 -0
  49. package/src/renderer/math.js +161 -0
  50. package/src/renderer/rendering/HistoryBufferManager.js +333 -0
  51. package/src/renderer/rendering/ProbeCapture.js +1495 -0
  52. package/src/renderer/rendering/ReflectionProbeManager.js +352 -0
  53. package/src/renderer/rendering/RenderGraph.js +2258 -0
  54. package/src/renderer/rendering/passes/AOPass.js +308 -0
  55. package/src/renderer/rendering/passes/AmbientCapturePass.js +593 -0
  56. package/src/renderer/rendering/passes/BasePass.js +101 -0
  57. package/src/renderer/rendering/passes/BloomPass.js +420 -0
  58. package/src/renderer/rendering/passes/CRTPass.js +724 -0
  59. package/src/renderer/rendering/passes/FogPass.js +445 -0
  60. package/src/renderer/rendering/passes/GBufferPass.js +730 -0
  61. package/src/renderer/rendering/passes/HiZPass.js +744 -0
  62. package/src/renderer/rendering/passes/LightingPass.js +753 -0
  63. package/src/renderer/rendering/passes/ParticlePass.js +841 -0
  64. package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
  65. package/src/renderer/rendering/passes/PostProcessPass.js +405 -0
  66. package/src/renderer/rendering/passes/ReflectionPass.js +157 -0
  67. package/src/renderer/rendering/passes/RenderPostPass.js +364 -0
  68. package/src/renderer/rendering/passes/SSGIPass.js +266 -0
  69. package/src/renderer/rendering/passes/SSGITilePass.js +305 -0
  70. package/src/renderer/rendering/passes/ShadowPass.js +2072 -0
  71. package/src/renderer/rendering/passes/TransparentPass.js +831 -0
  72. package/src/renderer/rendering/passes/VolumetricFogPass.js +715 -0
  73. package/src/renderer/rendering/shaders/ao.wgsl +182 -0
  74. package/src/renderer/rendering/shaders/bloom.wgsl +97 -0
  75. package/src/renderer/rendering/shaders/bloom_blur.wgsl +80 -0
  76. package/src/renderer/rendering/shaders/crt.wgsl +455 -0
  77. package/src/renderer/rendering/shaders/depth_copy.wgsl +17 -0
  78. package/src/renderer/rendering/shaders/geometry.wgsl +580 -0
  79. package/src/renderer/rendering/shaders/hiz_reduce.wgsl +114 -0
  80. package/src/renderer/rendering/shaders/light_culling.wgsl +204 -0
  81. package/src/renderer/rendering/shaders/lighting.wgsl +932 -0
  82. package/src/renderer/rendering/shaders/lighting_common.wgsl +143 -0
  83. package/src/renderer/rendering/shaders/particle_render.wgsl +672 -0
  84. package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
  85. package/src/renderer/rendering/shaders/postproc.wgsl +293 -0
  86. package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
  87. package/src/renderer/rendering/shaders/shadow.wgsl +117 -0
  88. package/src/renderer/rendering/shaders/ssgi.wgsl +266 -0
  89. package/src/renderer/rendering/shaders/ssgi_accumulate.wgsl +114 -0
  90. package/src/renderer/rendering/shaders/ssgi_propagate.wgsl +132 -0
  91. package/src/renderer/rendering/shaders/volumetric_blur.wgsl +80 -0
  92. package/src/renderer/rendering/shaders/volumetric_composite.wgsl +80 -0
  93. package/src/renderer/rendering/shaders/volumetric_raymarch.wgsl +634 -0
  94. package/src/renderer/utils/BoundingSphere.js +439 -0
  95. package/src/renderer/utils/Frustum.js +281 -0
  96. package/src/renderer/utils/Raycaster.js +761 -0
  97. package/dist/client.d.cts +0 -211
  98. package/dist/client.d.ts +0 -211
  99. package/dist/server.d.cts +0 -120
  100. package/dist/server.d.ts +0 -120
  101. package/dist/terminal.d.cts +0 -64
  102. package/dist/terminal.d.ts +0 -64
  103. package/src/utils.ts +0 -403
@@ -0,0 +1,405 @@
1
+ import { BasePass } from "./BasePass.js"
2
+ import { Pipeline } from "../../Pipeline.js"
3
+
4
+ import postProcessingWGSL from "../shaders/postproc.wgsl"
5
+
6
+ /**
7
+ * PostProcessPass - Final post-processing and tone mapping
8
+ *
9
+ * Pass 7 in the 7-pass pipeline (final pass).
10
+ * Applies tone mapping and outputs to the canvas.
11
+ *
12
+ * Input: HDR lit image from LightingPass
13
+ * Output: Final SDR image to canvas
14
+ */
15
+ class PostProcessPass extends BasePass {
16
+ constructor(engine = null) {
17
+ super('PostProcess', engine)
18
+
19
+ this.pipeline = null
20
+ this.inputTexture = null
21
+ this.bloomTexture = null
22
+ this.dummyBloomTexture = null // 1x1 black texture when bloom disabled
23
+ this.noiseTexture = null
24
+ this.noiseSize = 64
25
+ this.noiseAnimated = true
26
+ this.guiCanvas = null // 2D canvas for GUI overlay
27
+ this.guiTexture = null // GPU texture for GUI
28
+ this.guiSampler = null
29
+
30
+ // CRT support: intermediate texture when CRT is enabled
31
+ this.intermediateTexture = null
32
+ this._outputWidth = 0
33
+ this._outputHeight = 0
34
+ this._lastOutputToTexture = false // Track output mode changes
35
+
36
+ // Store resize dimensions for runtime texture creation
37
+ this._resizeWidth = 0
38
+ this._resizeHeight = 0
39
+ }
40
+
41
+ // Convenience getter for exposure setting
42
+ get exposure() { return this.settings?.environment?.exposure ?? 1.6 }
43
+
44
+ // Convenience getter for fxaa setting
45
+ get fxaa() { return this.settings?.rendering?.fxaa ?? true }
46
+
47
+ // Convenience getter for dithering settings
48
+ get ditheringEnabled() { return this.settings?.dithering?.enabled ?? true }
49
+ get colorLevels() { return this.settings?.dithering?.colorLevels ?? 32 }
50
+
51
+ // Tonemap mode: 0=ACES, 1=Reinhard, 2=None/Linear
52
+ get tonemapMode() { return this.settings?.rendering?.tonemapMode ?? 0 }
53
+
54
+ // Convenience getters for bloom settings
55
+ get bloomEnabled() { return this.settings?.bloom?.enabled ?? true }
56
+ get bloomIntensity() { return this.settings?.bloom?.intensity ?? 1.0 }
57
+ get bloomRadius() { return this.settings?.bloom?.radius ?? 5 }
58
+
59
+ // CRT settings (determines if we output to intermediate texture)
60
+ get crtEnabled() { return this.settings?.crt?.enabled ?? false }
61
+ get crtUpscaleEnabled() { return this.settings?.crt?.upscaleEnabled ?? false }
62
+ get shouldOutputToTexture() { return this.crtEnabled || this.crtUpscaleEnabled }
63
+
64
+ /**
65
+ * Set the input texture (HDR image from LightingPass)
66
+ * @param {Texture} texture - Input HDR texture
67
+ */
68
+ setInputTexture(texture) {
69
+ if (this.inputTexture !== texture) {
70
+ this.inputTexture = texture
71
+ this._needsRebuild = true
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Set the bloom texture (from BloomPass)
77
+ * @param {Object} bloomTexture - Bloom texture with mip levels
78
+ */
79
+ setBloomTexture(bloomTexture) {
80
+ if (this.bloomTexture !== bloomTexture) {
81
+ this.bloomTexture = bloomTexture
82
+ this._needsRebuild = true
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Set the noise texture for dithering
88
+ * @param {Texture} texture - Noise texture (blue noise or bayer dither)
89
+ * @param {number} size - Texture size
90
+ * @param {boolean} animated - Whether to animate noise offset each frame
91
+ */
92
+ setNoise(texture, size = 64, animated = true) {
93
+ this.noiseTexture = texture
94
+ this.noiseSize = size
95
+ this.noiseAnimated = animated
96
+ this._needsRebuild = true
97
+ }
98
+
99
+ /**
100
+ * Set the GUI canvas for overlay rendering
101
+ * @param {HTMLCanvasElement} canvas - 2D canvas with GUI content
102
+ */
103
+ setGuiCanvas(canvas) {
104
+ this.guiCanvas = canvas
105
+ }
106
+
107
+ /**
108
+ * Get the output texture (for CRT pass to use)
109
+ * Returns null if outputting directly to canvas
110
+ */
111
+ getOutputTexture() {
112
+ return this.shouldOutputToTexture ? this.intermediateTexture : null
113
+ }
114
+
115
+ /**
116
+ * Create or resize the intermediate texture for CRT
117
+ */
118
+ async _createIntermediateTexture(width, height) {
119
+ if (!this.shouldOutputToTexture) {
120
+ // Destroy if no longer needed
121
+ if (this.intermediateTexture?.texture) {
122
+ this.intermediateTexture.texture.destroy()
123
+ this.intermediateTexture = null
124
+ }
125
+ return
126
+ }
127
+
128
+ // Skip if size hasn't changed
129
+ if (this._outputWidth === width && this._outputHeight === height && this.intermediateTexture) {
130
+ return
131
+ }
132
+
133
+ const { device } = this.engine
134
+
135
+ // Destroy old texture
136
+ if (this.intermediateTexture?.texture) {
137
+ this.intermediateTexture.texture.destroy()
138
+ }
139
+
140
+ // Create intermediate texture (SDR format for CRT input)
141
+ const texture = device.createTexture({
142
+ label: 'PostProcess Intermediate',
143
+ size: [width, height, 1],
144
+ format: 'rgba8unorm',
145
+ usage: GPUTextureUsage.TEXTURE_BINDING |
146
+ GPUTextureUsage.RENDER_ATTACHMENT |
147
+ GPUTextureUsage.COPY_SRC,
148
+ })
149
+
150
+ const sampler = device.createSampler({
151
+ label: 'PostProcess Intermediate Sampler',
152
+ minFilter: 'nearest',
153
+ magFilter: 'nearest',
154
+ })
155
+
156
+ this.intermediateTexture = {
157
+ texture,
158
+ view: texture.createView(),
159
+ sampler,
160
+ width,
161
+ height,
162
+ format: 'rgba8unorm', // Required by Pipeline.create()
163
+ }
164
+
165
+ this._outputWidth = width
166
+ this._outputHeight = height
167
+ this._needsRebuild = true
168
+
169
+ console.log(`PostProcessPass: Created intermediate texture ${width}x${height} for CRT`)
170
+ }
171
+
172
+ async _init() {
173
+ // Create dummy 1x1 black bloom texture for when bloom is disabled
174
+ // This ensures shader bindings are always valid
175
+ const { device } = this.engine
176
+
177
+ const dummyTexture = device.createTexture({
178
+ label: 'Dummy Bloom Texture',
179
+ size: [1, 1, 1],
180
+ format: 'rgba16float',
181
+ mipLevelCount: 1,
182
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
183
+ })
184
+
185
+ // Fill with black (no bloom)
186
+ device.queue.writeTexture(
187
+ { texture: dummyTexture },
188
+ new Float32Array([0, 0, 0, 0]).buffer,
189
+ { bytesPerRow: 8 },
190
+ { width: 1, height: 1 }
191
+ )
192
+
193
+ const dummySampler = device.createSampler({
194
+ label: 'Dummy Bloom Sampler',
195
+ minFilter: 'linear',
196
+ magFilter: 'linear',
197
+ })
198
+
199
+ this.dummyBloomTexture = {
200
+ texture: dummyTexture,
201
+ view: dummyTexture.createView(),
202
+ sampler: dummySampler,
203
+ mipCount: 1,
204
+ }
205
+
206
+ // Create sampler for GUI texture
207
+ this.guiSampler = device.createSampler({
208
+ label: 'GUI Sampler',
209
+ minFilter: 'linear',
210
+ magFilter: 'linear',
211
+ })
212
+
213
+ // Create dummy 1x1 transparent GUI texture
214
+ const dummyGuiTexture = device.createTexture({
215
+ label: 'Dummy GUI Texture',
216
+ size: [1, 1, 1],
217
+ format: 'rgba8unorm',
218
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
219
+ })
220
+ device.queue.writeTexture(
221
+ { texture: dummyGuiTexture },
222
+ new Uint8Array([0, 0, 0, 0]),
223
+ { bytesPerRow: 4 },
224
+ { width: 1, height: 1 }
225
+ )
226
+ this.dummyGuiTexture = {
227
+ texture: dummyGuiTexture,
228
+ view: dummyGuiTexture.createView(),
229
+ sampler: this.guiSampler,
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Build or rebuild the pipeline
235
+ */
236
+ async _buildPipeline() {
237
+ if (!this.inputTexture) {
238
+ return
239
+ }
240
+
241
+ const textures = [this.inputTexture]
242
+ if (this.noiseTexture) {
243
+ textures.push(this.noiseTexture)
244
+ }
245
+ // Always include bloom texture (real or dummy) so shader bindings are valid
246
+ const effectiveBloomTexture = this.bloomTexture || this.dummyBloomTexture
247
+ textures.push(effectiveBloomTexture)
248
+
249
+ // Always include GUI texture (real or dummy) so shader bindings are valid
250
+ const effectiveGuiTexture = this.guiTexture || this.dummyGuiTexture
251
+ textures.push(effectiveGuiTexture)
252
+
253
+ const hasBloom = this.bloomTexture && this.bloomEnabled
254
+
255
+ // Determine render target: intermediate texture (for CRT) or canvas
256
+ const renderTarget = this.shouldOutputToTexture && this.intermediateTexture
257
+ ? this.intermediateTexture
258
+ : null
259
+
260
+ this.pipeline = await Pipeline.create(this.engine, {
261
+ label: 'postProcess',
262
+ wgslSource: postProcessingWGSL,
263
+ isPostProcessing: true,
264
+ textures: textures,
265
+ uniforms: () => ({
266
+ noiseParams: [this.noiseSize, this.noiseAnimated ? Math.random() : 0, this.noiseAnimated ? Math.random() : 0, this.fxaa ? 1.0 : 0.0],
267
+ ditherParams: [this.ditheringEnabled ? 1.0 : 0.0, this.colorLevels, this.tonemapMode, 0],
268
+ bloomParams: [hasBloom ? 1.0 : 0.0, this.bloomIntensity, this.bloomRadius, effectiveBloomTexture?.mipCount ?? 1]
269
+ }),
270
+ renderTarget: renderTarget,
271
+ })
272
+
273
+ this._needsRebuild = false
274
+ }
275
+
276
+ async _execute(context) {
277
+ const { device, canvas } = this.engine
278
+
279
+ // Check if CRT state changed (need to switch between canvas/texture output)
280
+ const needsOutputToTexture = this.shouldOutputToTexture
281
+ const hasIntermediateTexture = !!this.intermediateTexture
282
+
283
+ // Detect output mode change
284
+ if (needsOutputToTexture !== this._lastOutputToTexture) {
285
+ this._lastOutputToTexture = needsOutputToTexture
286
+ this._needsRebuild = true
287
+
288
+ if (needsOutputToTexture && !hasIntermediateTexture) {
289
+ // CRT was just enabled - create intermediate texture at stored resize dimensions
290
+ // Use resize dimensions (render-scaled), not canvas dimensions (full resolution)
291
+ const w = this._resizeWidth || canvas.width
292
+ const h = this._resizeHeight || canvas.height
293
+ await this._createIntermediateTexture(w, h)
294
+ } else if (!needsOutputToTexture && hasIntermediateTexture) {
295
+ // CRT was just disabled - destroy intermediate texture
296
+ if (this.intermediateTexture?.texture) {
297
+ this.intermediateTexture.texture.destroy()
298
+ this.intermediateTexture = null
299
+ }
300
+ }
301
+ }
302
+
303
+ // Update GUI texture from canvas if available
304
+ if (this.guiCanvas && this.guiCanvas.width > 0 && this.guiCanvas.height > 0) {
305
+ // Check if we need to recreate the texture (size changed)
306
+ const needsNewTexture = !this.guiTexture ||
307
+ this.guiTexture.width !== this.guiCanvas.width ||
308
+ this.guiTexture.height !== this.guiCanvas.height
309
+
310
+ if (needsNewTexture) {
311
+ // Destroy old texture if it exists
312
+ if (this.guiTexture?.texture) {
313
+ this.guiTexture.texture.destroy()
314
+ }
315
+
316
+ // Create new texture matching canvas size
317
+ const texture = device.createTexture({
318
+ label: 'GUI Texture',
319
+ size: [this.guiCanvas.width, this.guiCanvas.height, 1],
320
+ format: 'rgba8unorm',
321
+ usage: GPUTextureUsage.TEXTURE_BINDING |
322
+ GPUTextureUsage.COPY_DST |
323
+ GPUTextureUsage.RENDER_ATTACHMENT,
324
+ })
325
+
326
+ this.guiTexture = {
327
+ texture: texture,
328
+ view: texture.createView(),
329
+ sampler: this.guiSampler,
330
+ width: this.guiCanvas.width,
331
+ height: this.guiCanvas.height,
332
+ }
333
+
334
+ // Force pipeline rebuild to use new texture
335
+ this._needsRebuild = true
336
+ }
337
+
338
+ // Copy canvas content to GPU texture
339
+ device.queue.copyExternalImageToTexture(
340
+ { source: this.guiCanvas },
341
+ { texture: this.guiTexture.texture },
342
+ [this.guiCanvas.width, this.guiCanvas.height]
343
+ )
344
+ }
345
+
346
+ // Rebuild pipeline if needed
347
+ if (this._needsRebuild) {
348
+ await this._buildPipeline()
349
+ }
350
+
351
+ // If rebuild was attempted but failed, don't use stale pipeline with old bind groups
352
+ if (!this.pipeline || this._needsRebuild) {
353
+ console.warn('PostProcessPass: Pipeline not ready')
354
+ return
355
+ }
356
+
357
+ // Determine if bloom is effectively enabled
358
+ const hasBloom = this.bloomTexture && this.bloomEnabled
359
+ const effectiveBloomTexture = this.bloomTexture || this.dummyBloomTexture
360
+
361
+ // Update uniforms each frame
362
+ this.pipeline.uniformValues.set({
363
+ noiseParams: [this.noiseSize, this.noiseAnimated ? Math.random() : 0, this.noiseAnimated ? Math.random() : 0, this.fxaa ? 1.0 : 0.0],
364
+ ditherParams: [this.ditheringEnabled ? 1.0 : 0.0, this.colorLevels, this.tonemapMode, 0],
365
+ bloomParams: [hasBloom ? 1.0 : 0.0, this.bloomIntensity, this.bloomRadius, effectiveBloomTexture?.mipCount ?? 1]
366
+ })
367
+
368
+ // Render to canvas
369
+ this.pipeline.render()
370
+ }
371
+
372
+ async _resize(width, height) {
373
+ // Store resize dimensions for runtime texture creation
374
+ this._resizeWidth = width
375
+ this._resizeHeight = height
376
+
377
+ // Create intermediate texture for CRT if enabled
378
+ await this._createIntermediateTexture(width, height)
379
+
380
+ // Pipeline needs rebuild since canvas size changed
381
+ this._needsRebuild = true
382
+ }
383
+
384
+ _destroy() {
385
+ this.pipeline = null
386
+ if (this.dummyBloomTexture?.texture) {
387
+ this.dummyBloomTexture.texture.destroy()
388
+ this.dummyBloomTexture = null
389
+ }
390
+ if (this.guiTexture?.texture) {
391
+ this.guiTexture.texture.destroy()
392
+ this.guiTexture = null
393
+ }
394
+ if (this.dummyGuiTexture?.texture) {
395
+ this.dummyGuiTexture.texture.destroy()
396
+ this.dummyGuiTexture = null
397
+ }
398
+ if (this.intermediateTexture?.texture) {
399
+ this.intermediateTexture.texture.destroy()
400
+ this.intermediateTexture = null
401
+ }
402
+ }
403
+ }
404
+
405
+ export { PostProcessPass }
@@ -0,0 +1,157 @@
1
+ import { BasePass } from "./BasePass.js"
2
+ import { Texture } from "../../Texture.js"
3
+ import { ProbeCapture } from "../ProbeCapture.js"
4
+ import { ReflectionProbeManager } from "../ReflectionProbeManager.js"
5
+
6
+ /**
7
+ * ReflectionPass - Manages reflection probes and environment lighting
8
+ *
9
+ * Pass 2 in the 7-pass pipeline.
10
+ * Handles:
11
+ * - Loading/managing reflection probes
12
+ * - Real-time probe capture (when triggered)
13
+ * - Probe interpolation based on camera position
14
+ *
15
+ * Configuration:
16
+ * - 1024x1024 octahedral HDR texture per probe
17
+ * - Interpolates between closest 2 probes
18
+ */
19
+ class ReflectionPass extends BasePass {
20
+ constructor(engine = null) {
21
+ super('Reflection', engine)
22
+
23
+ // Probe capture system
24
+ this.probeCapture = null
25
+
26
+ // Probe manager for loading/interpolating
27
+ this.probeManager = null
28
+
29
+ // Current world ID
30
+ this.currentWorldId = 'default'
31
+
32
+ // Capture request queue
33
+ this.captureRequests = []
34
+
35
+ // Output: combined/interpolated probe texture
36
+ this.outputTexture = null
37
+ }
38
+
39
+ async _init() {
40
+ // Initialize probe manager (lightweight, always works)
41
+ this.probeManager = new ReflectionProbeManager(this.engine)
42
+
43
+ // Initialize probe capture system (can fail on some systems)
44
+ try {
45
+ this.probeCapture = new ProbeCapture(this.engine)
46
+ await this.probeCapture.initialize()
47
+ } catch (e) {
48
+ console.warn('ReflectionPass: Probe capture initialization failed:', e)
49
+ this.probeCapture = null
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Set fallback environment map (used when no probes available)
55
+ * @param {Texture} envMap - Environment map texture
56
+ * @param {number} encoding - 0 = equirectangular, 1 = octahedral
57
+ */
58
+ setFallbackEnvironment(envMap, encoding = 0) {
59
+ if (this.probeManager) {
60
+ this.probeManager.setFallbackEnvironment(envMap)
61
+ }
62
+ if (this.probeCapture) {
63
+ this.probeCapture.setFallbackEnvironment(envMap)
64
+ this.probeCapture.envEncoding = encoding
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Load probes for a world
70
+ */
71
+ async loadWorldProbes(worldId) {
72
+ this.currentWorldId = worldId
73
+ if (this.probeManager) {
74
+ await this.probeManager.loadWorldProbes(worldId)
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Request a probe capture at position
80
+ * Will be processed during next execute()
81
+ */
82
+ requestCapture(position, worldId = null) {
83
+ this.captureRequests.push({
84
+ position: [...position],
85
+ worldId: worldId || this.currentWorldId
86
+ })
87
+ }
88
+
89
+ /**
90
+ * Load a specific probe from URL
91
+ */
92
+ async loadProbe(url, position, worldId = null) {
93
+ if (this.probeManager) {
94
+ return await this.probeManager.loadProbe(url, position, worldId || this.currentWorldId)
95
+ }
96
+ return null
97
+ }
98
+
99
+ async _execute(context) {
100
+ const { camera } = context
101
+
102
+ // Update active probes based on camera position
103
+ if (this.probeManager && camera) {
104
+ this.probeManager.updateActiveProbes(camera.position, this.currentWorldId)
105
+ }
106
+
107
+ // Process capture requests (one per frame max)
108
+ if (this.captureRequests.length > 0 && !this.probeCapture?.isCapturing) {
109
+ const request = this.captureRequests.shift()
110
+ // Capture would happen here - requires renderGraph reference
111
+ // For now, log the request
112
+ console.log(`ReflectionPass: Capture requested at [${request.position.join(', ')}]`)
113
+ }
114
+ }
115
+
116
+ async _resize(width, height) {
117
+ // Reflection maps are fixed size, doesn't resize with screen
118
+ }
119
+
120
+ _destroy() {
121
+ if (this.probeCapture) {
122
+ this.probeCapture.destroy()
123
+ }
124
+ if (this.probeManager) {
125
+ this.probeManager.destroy()
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Get probe manager for external access
131
+ */
132
+ getProbeManager() {
133
+ return this.probeManager
134
+ }
135
+
136
+ /**
137
+ * Get probe capture system for manual triggering
138
+ */
139
+ getProbeCapture() {
140
+ return this.probeCapture
141
+ }
142
+
143
+ /**
144
+ * Get active probe data for lighting pass
145
+ */
146
+ getActiveProbeData() {
147
+ if (this.probeManager) {
148
+ return this.probeManager.getActiveProbeData()
149
+ }
150
+ return {
151
+ textures: [null, null],
152
+ weights: [1.0, 0.0]
153
+ }
154
+ }
155
+ }
156
+
157
+ export { ReflectionPass }