topazcube 0.1.30 → 0.1.33

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 (96) hide show
  1. package/LICENSE.txt +0 -0
  2. package/README.md +0 -0
  3. package/dist/Renderer.cjs +18200 -0
  4. package/dist/Renderer.cjs.map +1 -0
  5. package/dist/Renderer.js +18183 -0
  6. package/dist/Renderer.js.map +1 -0
  7. package/dist/client.cjs +94 -260
  8. package/dist/client.cjs.map +1 -1
  9. package/dist/client.js +71 -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} +173 -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 +572 -0
  33. package/src/renderer/Geometry.js +1049 -0
  34. package/src/renderer/Material.js +61 -0
  35. package/src/renderer/Mesh.js +211 -0
  36. package/src/renderer/Node.js +112 -0
  37. package/src/renderer/Pipeline.js +643 -0
  38. package/src/renderer/Renderer.js +1324 -0
  39. package/src/renderer/Skin.js +792 -0
  40. package/src/renderer/Texture.js +584 -0
  41. package/src/renderer/core/AssetManager.js +359 -0
  42. package/src/renderer/core/CullingSystem.js +307 -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 +546 -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 +2064 -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 +417 -0
  58. package/src/renderer/rendering/passes/FogPass.js +419 -0
  59. package/src/renderer/rendering/passes/GBufferPass.js +706 -0
  60. package/src/renderer/rendering/passes/HiZPass.js +714 -0
  61. package/src/renderer/rendering/passes/LightingPass.js +739 -0
  62. package/src/renderer/rendering/passes/ParticlePass.js +835 -0
  63. package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
  64. package/src/renderer/rendering/passes/PostProcessPass.js +282 -0
  65. package/src/renderer/rendering/passes/ReflectionPass.js +157 -0
  66. package/src/renderer/rendering/passes/RenderPostPass.js +364 -0
  67. package/src/renderer/rendering/passes/SSGIPass.js +265 -0
  68. package/src/renderer/rendering/passes/SSGITilePass.js +296 -0
  69. package/src/renderer/rendering/passes/ShadowPass.js +1822 -0
  70. package/src/renderer/rendering/passes/TransparentPass.js +831 -0
  71. package/src/renderer/rendering/shaders/ao.wgsl +182 -0
  72. package/src/renderer/rendering/shaders/bloom.wgsl +97 -0
  73. package/src/renderer/rendering/shaders/bloom_blur.wgsl +80 -0
  74. package/src/renderer/rendering/shaders/depth_copy.wgsl +17 -0
  75. package/src/renderer/rendering/shaders/geometry.wgsl +550 -0
  76. package/src/renderer/rendering/shaders/hiz_reduce.wgsl +114 -0
  77. package/src/renderer/rendering/shaders/light_culling.wgsl +204 -0
  78. package/src/renderer/rendering/shaders/lighting.wgsl +932 -0
  79. package/src/renderer/rendering/shaders/lighting_common.wgsl +143 -0
  80. package/src/renderer/rendering/shaders/particle_render.wgsl +525 -0
  81. package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
  82. package/src/renderer/rendering/shaders/postproc.wgsl +272 -0
  83. package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
  84. package/src/renderer/rendering/shaders/shadow.wgsl +76 -0
  85. package/src/renderer/rendering/shaders/ssgi.wgsl +266 -0
  86. package/src/renderer/rendering/shaders/ssgi_accumulate.wgsl +114 -0
  87. package/src/renderer/rendering/shaders/ssgi_propagate.wgsl +132 -0
  88. package/src/renderer/utils/BoundingSphere.js +439 -0
  89. package/src/renderer/utils/Frustum.js +281 -0
  90. package/dist/client.d.cts +0 -211
  91. package/dist/client.d.ts +0 -211
  92. package/dist/server.d.cts +0 -120
  93. package/dist/server.d.ts +0 -120
  94. package/dist/terminal.d.cts +0 -64
  95. package/dist/terminal.d.ts +0 -64
  96. package/src/utils.ts +0 -403
@@ -0,0 +1,417 @@
1
+ import { BasePass } from "./BasePass.js"
2
+
3
+ import bloomExtractWGSL from "../shaders/bloom.wgsl"
4
+ import bloomBlurWGSL from "../shaders/bloom_blur.wgsl"
5
+
6
+ /**
7
+ * BloomPass - HDR Bloom/Glare effect
8
+ *
9
+ * Extracts bright pixels with exponential falloff and applies
10
+ * two-pass diagonal Gaussian blur for X-shaped glare effect.
11
+ *
12
+ * Input: HDR lighting output
13
+ * Output: Blurred bloom texture with X-shaped glare
14
+ */
15
+ class BloomPass extends BasePass {
16
+ constructor(engine = null) {
17
+ super('Bloom', engine)
18
+
19
+ // Pipelines
20
+ this.extractPipeline = null
21
+ this.blurPipeline = null
22
+
23
+ // Textures (ping-pong for blur)
24
+ this.brightTexture = null // Extracted bright pixels
25
+ this.blurTextureA = null // After horizontal blur
26
+ this.blurTextureB = null // After vertical blur (final output)
27
+
28
+ // Resources
29
+ this.inputTexture = null
30
+ this.extractUniformBuffer = null
31
+ this.blurUniformBufferH = null // Horizontal blur uniforms
32
+ this.blurUniformBufferV = null // Vertical blur uniforms
33
+ this.extractBindGroup = null
34
+ this.blurBindGroupH = null // Horizontal blur
35
+ this.blurBindGroupV = null // Vertical blur
36
+ this.sampler = null
37
+
38
+ // Textures pending destruction (wait for GPU to finish using them)
39
+ // Use a ring buffer of 3 frames to ensure GPU is definitely done
40
+ this._pendingDestroyRing = [[], [], []]
41
+ this._pendingDestroyIndex = 0
42
+
43
+ // Render dimensions (may differ from canvas when effect scaling is applied)
44
+ this.width = 0
45
+ this.height = 0
46
+ // Bloom internal resolution (scaled down for performance)
47
+ this.bloomWidth = 0
48
+ this.bloomHeight = 0
49
+ }
50
+
51
+ // Convenience getters for bloom settings
52
+ get bloomEnabled() { return this.settings?.bloom?.enabled ?? true }
53
+ get intensity() { return this.settings?.bloom?.intensity ?? 1.0 }
54
+ get threshold() { return this.settings?.bloom?.threshold ?? 0.8 }
55
+ get softThreshold() { return this.settings?.bloom?.softThreshold ?? 0.5 }
56
+ get radius() { return this.settings?.bloom?.radius ?? 32 }
57
+ get emissiveBoost() { return this.settings?.bloom?.emissiveBoost ?? 2.0 }
58
+ get maxBrightness() { return this.settings?.bloom?.maxBrightness ?? 4.0 }
59
+ get renderScale() { return this.settings?.rendering?.renderScale ?? 1.0 }
60
+ // Bloom resolution scale - 0.5 = half res (faster), 1.0 = full res (quality)
61
+ get bloomScale() { return this.settings?.bloom?.scale ?? 0.5 }
62
+
63
+ /**
64
+ * Set the input texture (HDR lighting output)
65
+ * @param {Object} texture - Input texture with view property
66
+ */
67
+ setInputTexture(texture) {
68
+ // Only rebuild if the texture actually changed
69
+ if (this.inputTexture !== texture) {
70
+ this.inputTexture = texture
71
+ this._needsRebuild = true
72
+ }
73
+ }
74
+
75
+ async _init() {
76
+ const { device } = this.engine
77
+
78
+ // Create sampler for all bloom textures
79
+ this.sampler = device.createSampler({
80
+ label: 'Bloom Sampler',
81
+ minFilter: 'linear',
82
+ magFilter: 'linear',
83
+ addressModeU: 'clamp-to-edge',
84
+ addressModeV: 'clamp-to-edge',
85
+ })
86
+ }
87
+
88
+ /**
89
+ * Create or recreate bloom textures at scaled resolution
90
+ */
91
+ _createTextures(width, height) {
92
+ const { device } = this.engine
93
+
94
+ // Queue old textures for deferred destruction (GPU may still be using them)
95
+ // Add to current slot in ring buffer - will be destroyed 3 frames later
96
+ const slot = this._pendingDestroyRing[this._pendingDestroyIndex]
97
+ if (this.brightTexture?.texture) slot.push(this.brightTexture.texture)
98
+ if (this.blurTextureA?.texture) slot.push(this.blurTextureA.texture)
99
+ if (this.blurTextureB?.texture) slot.push(this.blurTextureB.texture)
100
+
101
+ // Calculate scaled resolution for bloom (0.5 = half res for performance)
102
+ // Bloom is blurry anyway, so half-res is usually sufficient
103
+ const scale = this.bloomScale
104
+ const bloomWidth = Math.max(1, Math.floor(width * scale))
105
+ const bloomHeight = Math.max(1, Math.floor(height * scale))
106
+
107
+ this.bloomWidth = bloomWidth
108
+ this.bloomHeight = bloomHeight
109
+
110
+ console.log(`Bloom: ${width}x${height} -> ${bloomWidth}x${bloomHeight} (scale: ${scale})`)
111
+
112
+ // Create textures at scaled resolution
113
+ const createBloomTexture = (label) => {
114
+ const texture = device.createTexture({
115
+ label,
116
+ size: { width: bloomWidth, height: bloomHeight, depthOrArrayLayers: 1 },
117
+ format: 'rgba16float',
118
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
119
+ })
120
+ return {
121
+ texture,
122
+ view: texture.createView({ label: `${label} View` }),
123
+ sampler: this.sampler,
124
+ width: bloomWidth,
125
+ height: bloomHeight,
126
+ }
127
+ }
128
+
129
+ this.brightTexture = createBloomTexture('Bloom Bright')
130
+ this.blurTextureA = createBloomTexture('Bloom Blur A')
131
+ this.blurTextureB = createBloomTexture('Bloom Blur B')
132
+ }
133
+
134
+ async _buildPipeline() {
135
+ if (!this.inputTexture) {
136
+ return
137
+ }
138
+
139
+ const { device, canvas } = this.engine
140
+ // Store initial dimensions (may be updated by resize)
141
+ this.width = canvas.width
142
+ this.height = canvas.height
143
+
144
+ // Create bloom textures
145
+ this._createTextures(this.width, this.height)
146
+
147
+ // Create uniform buffers
148
+ this.extractUniformBuffer = device.createBuffer({
149
+ label: 'Bloom Extract Uniforms',
150
+ size: 32, // 5 floats + padding for 16-byte alignment
151
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
152
+ })
153
+
154
+ this.blurUniformBufferH = device.createBuffer({
155
+ label: 'Bloom Blur H Uniforms',
156
+ size: 32, // 8 floats (2 vec2 + 2 float + padding)
157
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
158
+ })
159
+
160
+ this.blurUniformBufferV = device.createBuffer({
161
+ label: 'Bloom Blur V Uniforms',
162
+ size: 32,
163
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
164
+ })
165
+
166
+ // ===== EXTRACT PIPELINE =====
167
+ const extractBGL = device.createBindGroupLayout({
168
+ label: 'Bloom Extract BGL',
169
+ entries: [
170
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
171
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
172
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
173
+ ],
174
+ })
175
+
176
+ const extractModule = device.createShaderModule({
177
+ label: 'Bloom Extract Shader',
178
+ code: bloomExtractWGSL,
179
+ })
180
+
181
+ // ===== BLUR PIPELINE =====
182
+ const blurBGL = device.createBindGroupLayout({
183
+ label: 'Bloom Blur BGL',
184
+ entries: [
185
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
186
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
187
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
188
+ ],
189
+ })
190
+
191
+ const blurModule = device.createShaderModule({
192
+ label: 'Bloom Blur Shader',
193
+ code: bloomBlurWGSL,
194
+ })
195
+
196
+ // Create both pipelines in parallel for faster initialization
197
+ const [extractPipeline, blurPipeline] = await Promise.all([
198
+ device.createRenderPipelineAsync({
199
+ label: 'Bloom Extract Pipeline',
200
+ layout: device.createPipelineLayout({ bindGroupLayouts: [extractBGL] }),
201
+ vertex: { module: extractModule, entryPoint: 'vertexMain' },
202
+ fragment: {
203
+ module: extractModule,
204
+ entryPoint: 'fragmentMain',
205
+ targets: [{ format: 'rgba16float' }],
206
+ },
207
+ primitive: { topology: 'triangle-list' },
208
+ }),
209
+ device.createRenderPipelineAsync({
210
+ label: 'Bloom Blur Pipeline',
211
+ layout: device.createPipelineLayout({ bindGroupLayouts: [blurBGL] }),
212
+ vertex: { module: blurModule, entryPoint: 'vertexMain' },
213
+ fragment: {
214
+ module: blurModule,
215
+ entryPoint: 'fragmentMain',
216
+ targets: [{ format: 'rgba16float' }],
217
+ },
218
+ primitive: { topology: 'triangle-list' },
219
+ })
220
+ ])
221
+
222
+ this.extractPipeline = extractPipeline
223
+ this.blurPipeline = blurPipeline
224
+
225
+ this.extractBindGroup = device.createBindGroup({
226
+ label: 'Bloom Extract Bind Group',
227
+ layout: extractBGL,
228
+ entries: [
229
+ { binding: 0, resource: { buffer: this.extractUniformBuffer } },
230
+ { binding: 1, resource: this.inputTexture.view },
231
+ { binding: 2, resource: this.sampler },
232
+ ],
233
+ })
234
+
235
+ // Horizontal blur: brightTexture -> blurTextureA
236
+ this.blurBindGroupH = device.createBindGroup({
237
+ label: 'Bloom Blur H Bind Group',
238
+ layout: blurBGL,
239
+ entries: [
240
+ { binding: 0, resource: { buffer: this.blurUniformBufferH } },
241
+ { binding: 1, resource: this.brightTexture.view },
242
+ { binding: 2, resource: this.sampler },
243
+ ],
244
+ })
245
+
246
+ // Vertical blur: blurTextureA -> blurTextureB
247
+ this.blurBindGroupV = device.createBindGroup({
248
+ label: 'Bloom Blur V Bind Group',
249
+ layout: blurBGL,
250
+ entries: [
251
+ { binding: 0, resource: { buffer: this.blurUniformBufferV } },
252
+ { binding: 1, resource: this.blurTextureA.view },
253
+ { binding: 2, resource: this.sampler },
254
+ ],
255
+ })
256
+
257
+ this._needsRebuild = false
258
+ }
259
+
260
+ async _execute(context) {
261
+ // Skip if bloom is disabled in settings
262
+ if (!this.bloomEnabled) {
263
+ return
264
+ }
265
+
266
+ const { device, canvas } = this.engine
267
+
268
+ // Rotate ring buffer and destroy textures from 3 frames ago
269
+ this._pendingDestroyIndex = (this._pendingDestroyIndex + 1) % 3
270
+ const toDestroy = this._pendingDestroyRing[this._pendingDestroyIndex]
271
+ for (const tex of toDestroy) {
272
+ tex.destroy()
273
+ }
274
+ this._pendingDestroyRing[this._pendingDestroyIndex] = []
275
+
276
+ // Rebuild pipeline if needed
277
+ if (this._needsRebuild) {
278
+ await this._buildPipeline()
279
+ }
280
+
281
+ // If rebuild was attempted but failed, don't use stale pipeline with old bind groups
282
+ if (!this.extractPipeline || !this.blurPipeline || !this.inputTexture || this._needsRebuild) {
283
+ return
284
+ }
285
+
286
+ // Use bloom-specific dimensions (may be scaled down for performance)
287
+ const bloomWidth = this.bloomWidth
288
+ const bloomHeight = this.bloomHeight
289
+
290
+ // Scale radius based on bloom height relative to 1080p (settings are authored for 1080p)
291
+ // Also scale by bloomScale since we're working at lower resolution
292
+ const heightScale = bloomHeight / 1080
293
+ const blurRadius = this.radius * this.renderScale * heightScale
294
+
295
+ // Update all uniforms BEFORE creating command encoder
296
+ // (writeBuffer is immediate, commands are batched)
297
+ device.queue.writeBuffer(this.extractUniformBuffer, 0, new Float32Array([
298
+ this.threshold,
299
+ this.softThreshold,
300
+ this.intensity,
301
+ this.emissiveBoost,
302
+ this.maxBrightness,
303
+ 0.0, 0.0, 0.0, // padding
304
+ ]))
305
+
306
+ // Diagonal blur uniforms (X-shaped glare)
307
+ const diag = 0.7071067811865476 // 1/sqrt(2)
308
+ device.queue.writeBuffer(this.blurUniformBufferH, 0, new Float32Array([
309
+ diag, diag, // direction (diagonal: top-left to bottom-right)
310
+ 1.0 / bloomWidth, 1.0 / bloomHeight, // texelSize (at bloom resolution)
311
+ blurRadius, 0.0, // blurRadius, padding
312
+ 0.0, 0.0, // more padding for alignment
313
+ ]))
314
+
315
+ // Second diagonal blur uniforms
316
+ device.queue.writeBuffer(this.blurUniformBufferV, 0, new Float32Array([
317
+ diag, -diag, // direction (diagonal: bottom-left to top-right)
318
+ 1.0 / bloomWidth, 1.0 / bloomHeight, // texelSize (at bloom resolution)
319
+ blurRadius, 0.0, // blurRadius, padding
320
+ 0.0, 0.0, // more padding for alignment
321
+ ]))
322
+
323
+ const commandEncoder = device.createCommandEncoder({ label: 'Bloom Pass' })
324
+
325
+ // ===== PASS 1: Extract bright pixels =====
326
+ {
327
+ const pass = commandEncoder.beginRenderPass({
328
+ label: 'Bloom Extract',
329
+ colorAttachments: [{
330
+ view: this.brightTexture.view,
331
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
332
+ loadOp: 'clear',
333
+ storeOp: 'store',
334
+ }],
335
+ })
336
+ pass.setPipeline(this.extractPipeline)
337
+ pass.setBindGroup(0, this.extractBindGroup)
338
+ pass.draw(3)
339
+ pass.end()
340
+ }
341
+
342
+ // ===== PASS 2: Diagonal blur (top-left to bottom-right) =====
343
+ {
344
+ const pass = commandEncoder.beginRenderPass({
345
+ label: 'Bloom Blur Diag1',
346
+ colorAttachments: [{
347
+ view: this.blurTextureA.view,
348
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
349
+ loadOp: 'clear',
350
+ storeOp: 'store',
351
+ }],
352
+ })
353
+ pass.setPipeline(this.blurPipeline)
354
+ pass.setBindGroup(0, this.blurBindGroupH)
355
+ pass.draw(3)
356
+ pass.end()
357
+ }
358
+
359
+ // ===== PASS 3: Diagonal blur (bottom-left to top-right) =====
360
+ {
361
+ const pass = commandEncoder.beginRenderPass({
362
+ label: 'Bloom Blur Diag2',
363
+ colorAttachments: [{
364
+ view: this.blurTextureB.view,
365
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
366
+ loadOp: 'clear',
367
+ storeOp: 'store',
368
+ }],
369
+ })
370
+ pass.setPipeline(this.blurPipeline)
371
+ pass.setBindGroup(0, this.blurBindGroupV)
372
+ pass.draw(3)
373
+ pass.end()
374
+ }
375
+
376
+ device.queue.submit([commandEncoder.finish()])
377
+ }
378
+
379
+ async _resize(width, height) {
380
+ this.width = width
381
+ this.height = height
382
+ this._needsRebuild = true
383
+ }
384
+
385
+ _destroy() {
386
+ if (this.brightTexture?.texture) this.brightTexture.texture.destroy()
387
+ if (this.blurTextureA?.texture) this.blurTextureA.texture.destroy()
388
+ if (this.blurTextureB?.texture) this.blurTextureB.texture.destroy()
389
+ // Clean up any pending textures in all ring buffer slots
390
+ for (const slot of this._pendingDestroyRing) {
391
+ for (const tex of slot) {
392
+ tex.destroy()
393
+ }
394
+ }
395
+ this._pendingDestroyRing = [[], [], []]
396
+ this.extractPipeline = null
397
+ this.blurPipeline = null
398
+ }
399
+
400
+ /**
401
+ * Get the final bloom texture (after blur)
402
+ */
403
+ getOutputTexture() {
404
+ return this.blurTextureB
405
+ }
406
+
407
+ /**
408
+ * Get the bright extraction texture (before blur)
409
+ * Used by SSGITilePass for directional light accumulation
410
+ */
411
+ getBrightTexture() {
412
+ return this.brightTexture
413
+ }
414
+ }
415
+
416
+
417
+ export { BloomPass }