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.
- package/LICENSE.txt +0 -0
- package/README.md +0 -0
- package/dist/Renderer.cjs +20844 -0
- package/dist/Renderer.cjs.map +1 -0
- package/dist/Renderer.js +20827 -0
- package/dist/Renderer.js.map +1 -0
- package/dist/client.cjs +91 -260
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +68 -215
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +165 -432
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +117 -370
- package/dist/server.js.map +1 -1
- package/dist/terminal.cjs +113 -200
- package/dist/terminal.cjs.map +1 -1
- package/dist/terminal.js +50 -51
- package/dist/terminal.js.map +1 -1
- package/dist/utils-CRhi1BDa.cjs +259 -0
- package/dist/utils-CRhi1BDa.cjs.map +1 -0
- package/dist/utils-D7tXt6-2.js +260 -0
- package/dist/utils-D7tXt6-2.js.map +1 -0
- package/package.json +19 -15
- package/src/{client.ts → network/client.js} +170 -403
- package/src/{compress-browser.ts → network/compress-browser.js} +2 -4
- package/src/{compress-node.ts → network/compress-node.js} +8 -14
- package/src/{server.ts → network/server.js} +229 -317
- package/src/{terminal.js → network/terminal.js} +0 -0
- package/src/{topazcube.ts → network/topazcube.js} +2 -2
- package/src/network/utils.js +375 -0
- package/src/renderer/Camera.js +191 -0
- package/src/renderer/DebugUI.js +703 -0
- package/src/renderer/Geometry.js +1049 -0
- package/src/renderer/Material.js +64 -0
- package/src/renderer/Mesh.js +211 -0
- package/src/renderer/Node.js +112 -0
- package/src/renderer/Pipeline.js +645 -0
- package/src/renderer/Renderer.js +1496 -0
- package/src/renderer/Skin.js +792 -0
- package/src/renderer/Texture.js +584 -0
- package/src/renderer/core/AssetManager.js +394 -0
- package/src/renderer/core/CullingSystem.js +308 -0
- package/src/renderer/core/EntityManager.js +541 -0
- package/src/renderer/core/InstanceManager.js +343 -0
- package/src/renderer/core/ParticleEmitter.js +358 -0
- package/src/renderer/core/ParticleSystem.js +564 -0
- package/src/renderer/core/SpriteSystem.js +349 -0
- package/src/renderer/gltf.js +563 -0
- package/src/renderer/math.js +161 -0
- package/src/renderer/rendering/HistoryBufferManager.js +333 -0
- package/src/renderer/rendering/ProbeCapture.js +1495 -0
- package/src/renderer/rendering/ReflectionProbeManager.js +352 -0
- package/src/renderer/rendering/RenderGraph.js +2258 -0
- package/src/renderer/rendering/passes/AOPass.js +308 -0
- package/src/renderer/rendering/passes/AmbientCapturePass.js +593 -0
- package/src/renderer/rendering/passes/BasePass.js +101 -0
- package/src/renderer/rendering/passes/BloomPass.js +420 -0
- package/src/renderer/rendering/passes/CRTPass.js +724 -0
- package/src/renderer/rendering/passes/FogPass.js +445 -0
- package/src/renderer/rendering/passes/GBufferPass.js +730 -0
- package/src/renderer/rendering/passes/HiZPass.js +744 -0
- package/src/renderer/rendering/passes/LightingPass.js +753 -0
- package/src/renderer/rendering/passes/ParticlePass.js +841 -0
- package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
- package/src/renderer/rendering/passes/PostProcessPass.js +405 -0
- package/src/renderer/rendering/passes/ReflectionPass.js +157 -0
- package/src/renderer/rendering/passes/RenderPostPass.js +364 -0
- package/src/renderer/rendering/passes/SSGIPass.js +266 -0
- package/src/renderer/rendering/passes/SSGITilePass.js +305 -0
- package/src/renderer/rendering/passes/ShadowPass.js +2072 -0
- package/src/renderer/rendering/passes/TransparentPass.js +831 -0
- package/src/renderer/rendering/passes/VolumetricFogPass.js +715 -0
- package/src/renderer/rendering/shaders/ao.wgsl +182 -0
- package/src/renderer/rendering/shaders/bloom.wgsl +97 -0
- package/src/renderer/rendering/shaders/bloom_blur.wgsl +80 -0
- package/src/renderer/rendering/shaders/crt.wgsl +455 -0
- package/src/renderer/rendering/shaders/depth_copy.wgsl +17 -0
- package/src/renderer/rendering/shaders/geometry.wgsl +580 -0
- package/src/renderer/rendering/shaders/hiz_reduce.wgsl +114 -0
- package/src/renderer/rendering/shaders/light_culling.wgsl +204 -0
- package/src/renderer/rendering/shaders/lighting.wgsl +932 -0
- package/src/renderer/rendering/shaders/lighting_common.wgsl +143 -0
- package/src/renderer/rendering/shaders/particle_render.wgsl +672 -0
- package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
- package/src/renderer/rendering/shaders/postproc.wgsl +293 -0
- package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
- package/src/renderer/rendering/shaders/shadow.wgsl +117 -0
- package/src/renderer/rendering/shaders/ssgi.wgsl +266 -0
- package/src/renderer/rendering/shaders/ssgi_accumulate.wgsl +114 -0
- package/src/renderer/rendering/shaders/ssgi_propagate.wgsl +132 -0
- package/src/renderer/rendering/shaders/volumetric_blur.wgsl +80 -0
- package/src/renderer/rendering/shaders/volumetric_composite.wgsl +80 -0
- package/src/renderer/rendering/shaders/volumetric_raymarch.wgsl +634 -0
- package/src/renderer/utils/BoundingSphere.js +439 -0
- package/src/renderer/utils/Frustum.js +281 -0
- package/src/renderer/utils/Raycaster.js +761 -0
- package/dist/client.d.cts +0 -211
- package/dist/client.d.ts +0 -211
- package/dist/server.d.cts +0 -120
- package/dist/server.d.ts +0 -120
- package/dist/terminal.d.cts +0 -64
- package/dist/terminal.d.ts +0 -64
- 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 }
|