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,419 @@
1
+ import { BasePass } from "./BasePass.js"
2
+
3
+ /**
4
+ * FogPass - Distance-based fog with height fade
5
+ *
6
+ * Features:
7
+ * - Distance fog based on camera distance (not Z-depth)
8
+ * - Two-gradient system: distance[0]->distance[1] and distance[1]->distance[2]
9
+ * - Height fade: maximum fog at bottomY, zero fog at topY
10
+ * - Emissive/bright colors show through fog more (HDR resistance)
11
+ * - Applied before bloom so bright objects still bloom through fog
12
+ *
13
+ * Input: HDR lighting output, GBuffer (for depth texture)
14
+ * Output: Fog-applied HDR texture
15
+ */
16
+ class FogPass extends BasePass {
17
+ constructor(engine = null) {
18
+ super('Fog', engine)
19
+
20
+ this.renderPipeline = null
21
+ this.outputTexture = null
22
+ this.inputTexture = null
23
+ this.gbuffer = null
24
+ this.uniformBuffer = null
25
+ this.bindGroup = null
26
+ this.sampler = null
27
+
28
+ // Render dimensions
29
+ this.width = 0
30
+ this.height = 0
31
+ }
32
+
33
+ // Fog settings getters
34
+ get fogEnabled() { return this.settings?.environment?.fog?.enabled ?? false }
35
+ get fogColor() { return this.settings?.environment?.fog?.color ?? [0.8, 0.85, 0.9] }
36
+ get fogDistances() { return this.settings?.environment?.fog?.distances ?? [0, 50, 200] }
37
+ get fogAlpha() { return this.settings?.environment?.fog?.alpha ?? [0.0, 0.3, 0.8] }
38
+ get fogHeightFade() { return this.settings?.environment?.fog?.heightFade ?? [-10, 100] }
39
+ get fogBrightResist() { return this.settings?.environment?.fog?.brightResist ?? 0.8 }
40
+
41
+ /**
42
+ * Set the input texture (HDR lighting output)
43
+ */
44
+ setInputTexture(texture) {
45
+ if (this.inputTexture !== texture) {
46
+ this.inputTexture = texture
47
+ this._needsRebuild = true
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Set the GBuffer for depth/normal reconstruction
53
+ */
54
+ setGBuffer(gbuffer) {
55
+ if (this.gbuffer !== gbuffer) {
56
+ this.gbuffer = gbuffer
57
+ this._needsRebuild = true
58
+ }
59
+ }
60
+
61
+ async _init() {
62
+ const { device } = this.engine
63
+
64
+ // Create sampler
65
+ this.sampler = device.createSampler({
66
+ label: 'Fog Sampler',
67
+ minFilter: 'linear',
68
+ magFilter: 'linear',
69
+ addressModeU: 'clamp-to-edge',
70
+ addressModeV: 'clamp-to-edge',
71
+ })
72
+
73
+ // Create uniform buffer
74
+ // inverseProj (64) + inverseView (64) + cameraPosition (12) + near (4) +
75
+ // far (4) + fogEnabled (4) + fogColor (12) + brightResist (4) +
76
+ // distances (12) + pad (4) + alphas (12) + pad (4) + heightFade (8) + screenSize (8) = 224 bytes
77
+ // Round to 256 for alignment
78
+ this.uniformBuffer = device.createBuffer({
79
+ label: 'Fog Uniforms',
80
+ size: 256,
81
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
82
+ })
83
+ }
84
+
85
+ async _buildPipeline() {
86
+ if (!this.inputTexture || !this.gbuffer) {
87
+ return
88
+ }
89
+
90
+ const { device } = this.engine
91
+
92
+ // Destroy old output texture before creating new one
93
+ if (this.outputTexture) {
94
+ this.outputTexture.destroy()
95
+ this.outputTexture = null
96
+ this.outputTextureView = null
97
+ }
98
+
99
+ // Create output texture (same format as input)
100
+ // Include COPY_SRC for planar reflection pass which copies fog output
101
+ this.outputTexture = device.createTexture({
102
+ label: 'Fog Output',
103
+ size: { width: this.width || 1, height: this.height || 1, depthOrArrayLayers: 1 },
104
+ format: 'rgba16float',
105
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,
106
+ })
107
+ this.outputTextureView = this.outputTexture.createView({ label: 'Fog Output View' })
108
+
109
+ // Create bind group layout
110
+ const bindGroupLayout = device.createBindGroupLayout({
111
+ label: 'Fog BGL',
112
+ entries: [
113
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
114
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
115
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
116
+ { binding: 3, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'depth' } },
117
+ ],
118
+ })
119
+
120
+ const shaderModule = device.createShaderModule({
121
+ label: 'Fog Shader',
122
+ code: `
123
+ // Uniforms packed for proper WGSL alignment (vec3f needs 16-byte alignment)
124
+ struct Uniforms {
125
+ inverseProjection: mat4x4f, // floats 0-15
126
+ inverseView: mat4x4f, // floats 16-31
127
+ cameraPosition: vec3f, // floats 32-34
128
+ near: f32, // float 35
129
+ fogColor: vec3f, // floats 36-38
130
+ far: f32, // float 39
131
+ distances: vec3f, // floats 40-42
132
+ fogEnabled: f32, // float 43
133
+ alphas: vec3f, // floats 44-46
134
+ brightResist: f32, // float 47
135
+ heightFade: vec2f, // floats 48-49
136
+ screenSize: vec2f, // floats 50-51
137
+ }
138
+
139
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
140
+ @group(0) @binding(1) var inputTexture: texture_2d<f32>;
141
+ @group(0) @binding(2) var inputSampler: sampler;
142
+ @group(0) @binding(3) var depthTexture: texture_depth_2d;
143
+
144
+ struct VertexOutput {
145
+ @builtin(position) position: vec4f,
146
+ @location(0) uv: vec2f,
147
+ }
148
+
149
+ @vertex
150
+ fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
151
+ var output: VertexOutput;
152
+ // Full-screen triangle
153
+ let x = f32((vertexIndex << 1u) & 2u);
154
+ let y = f32(vertexIndex & 2u);
155
+ output.position = vec4f(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
156
+ output.uv = vec2f(x, 1.0 - y);
157
+ return output;
158
+ }
159
+
160
+ @fragment
161
+ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
162
+ let color = textureSample(inputTexture, inputSampler, input.uv);
163
+
164
+ // If fog disabled, pass through
165
+ if (uniforms.fogEnabled < 0.5) {
166
+ return color;
167
+ }
168
+
169
+ // Sample depth from GBuffer depth texture (same as lighting shader)
170
+ let pixelCoords = vec2i(input.uv * uniforms.screenSize);
171
+ let depth = textureLoad(depthTexture, pixelCoords, 0);
172
+
173
+ // Check if this is sky (depth = 1 means far plane / cleared depth buffer)
174
+ let isSky = depth >= 0.9999;
175
+
176
+ // Get view ray direction (needed for both sky and geometry)
177
+ var iuv = input.uv;
178
+ iuv.y = 1.0 - iuv.y;
179
+ let ndc = vec4f(iuv * 2.0 - 1.0, 0.0, 1.0);
180
+ let viewRay = uniforms.inverseProjection * ndc;
181
+ let rayDir = normalize(viewRay.xyz / viewRay.w);
182
+
183
+ // For sky, treat as far distance; otherwise reconstruct linear depth
184
+ var cameraDistance: f32;
185
+ var worldPosY: f32;
186
+
187
+ if (isSky) {
188
+ // Sky is at far distance
189
+ cameraDistance = uniforms.far;
190
+
191
+ // Map view ray elevation to effective Y for height fade
192
+ // The fog clear zone in sky depends on topY:
193
+ // - Low topY (near camera): small clear zone, fog only at horizon
194
+ // - Medium topY (25m): clear zone extends to ~60° above horizon
195
+ // - High topY (100m+): almost no clear zone, fog fills sky
196
+ let worldRayDir = normalize((uniforms.inverseView * vec4f(rayDir, 0.0)).xyz);
197
+ let camY = uniforms.cameraPosition.y;
198
+ let topY = uniforms.heightFade.y;
199
+ let bottomY = uniforms.heightFade.x;
200
+
201
+ if (worldRayDir.y > 0.0) {
202
+ // Looking up: map elevation to effective Y
203
+ // Use a reference height to control how much sky can be clear
204
+ // At referenceHeight (50m), zenith reaches topY (fully clear)
205
+ // Above that, zenith stays fogged
206
+ let referenceHeight = 50.0;
207
+ let fogHeight = topY - camY;
208
+ let clearRange = min(fogHeight, referenceHeight);
209
+ worldPosY = camY + worldRayDir.y * clearRange;
210
+ } else {
211
+ // Looking down or at horizon: below camera, full fog
212
+ worldPosY = camY + worldRayDir.y * (camY - bottomY);
213
+ }
214
+ } else {
215
+ // Reconstruct linear depth: depth = (z - near) / (far - near), so z = near + depth * (far - near)
216
+ let linearDepth = uniforms.near + depth * (uniforms.far - uniforms.near);
217
+
218
+ // Use linear depth directly as camera distance
219
+ cameraDistance = linearDepth;
220
+
221
+ // Reconstruct world position for height fade
222
+ let viewPos = rayDir * (linearDepth / -rayDir.z);
223
+ let worldPos4 = uniforms.inverseView * vec4f(viewPos, 1.0);
224
+ worldPosY = worldPos4.y;
225
+ }
226
+
227
+ // Distance fog - two gradients
228
+ var distanceFog: f32;
229
+ let d0 = uniforms.distances.x;
230
+ let d1 = uniforms.distances.y;
231
+ let d2 = uniforms.distances.z;
232
+ let a0 = uniforms.alphas.x;
233
+ let a1 = uniforms.alphas.y;
234
+ let a2 = uniforms.alphas.z;
235
+
236
+ if (cameraDistance <= d0) {
237
+ distanceFog = a0;
238
+ } else if (cameraDistance <= d1) {
239
+ let t = (cameraDistance - d0) / max(d1 - d0, 0.001);
240
+ distanceFog = mix(a0, a1, t);
241
+ } else if (cameraDistance <= d2) {
242
+ let t = (cameraDistance - d1) / max(d2 - d1, 0.001);
243
+ distanceFog = mix(a1, a2, t);
244
+ } else {
245
+ distanceFog = a2;
246
+ }
247
+
248
+ // Height fade - full fog at bottomY, zero fog at topY
249
+ let bottomY = uniforms.heightFade.x;
250
+ let topY = uniforms.heightFade.y;
251
+ var heightFactor = clamp((worldPosY - bottomY) / max(topY - bottomY, 0.001), 0.0, 1.0);
252
+ // Below bottomY = maximum fog
253
+ if (worldPosY < bottomY) {
254
+ heightFactor = 0.0;
255
+ }
256
+ var fogAlpha = distanceFog * (1.0 - heightFactor);
257
+
258
+ // Emissive/bright resistance - HDR values show through fog
259
+ // Calculate luminance
260
+ let luminance = dot(color.rgb, vec3f(0.299, 0.587, 0.114));
261
+ // For HDR values > 1.0, reduce fog effect
262
+ let brightnessResist = clamp((luminance - 1.0) / 2.0, 0.0, 1.0);
263
+ fogAlpha *= (1.0 - brightnessResist * uniforms.brightResist);
264
+
265
+ // Apply fog
266
+ let foggedColor = mix(color.rgb, uniforms.fogColor, fogAlpha);
267
+
268
+ return vec4f(foggedColor, color.a);
269
+ }
270
+ `,
271
+ })
272
+
273
+ this.renderPipeline = await device.createRenderPipelineAsync({
274
+ label: 'Fog Pipeline',
275
+ layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] }),
276
+ vertex: { module: shaderModule, entryPoint: 'vertexMain' },
277
+ fragment: {
278
+ module: shaderModule,
279
+ entryPoint: 'fragmentMain',
280
+ targets: [{ format: 'rgba16float' }],
281
+ },
282
+ primitive: { topology: 'triangle-list' },
283
+ })
284
+
285
+ this.bindGroup = device.createBindGroup({
286
+ label: 'Fog Bind Group',
287
+ layout: bindGroupLayout,
288
+ entries: [
289
+ { binding: 0, resource: { buffer: this.uniformBuffer } },
290
+ { binding: 1, resource: this.inputTexture.view },
291
+ { binding: 2, resource: this.sampler },
292
+ { binding: 3, resource: this.gbuffer.depth.view },
293
+ ],
294
+ })
295
+
296
+ this._needsRebuild = false
297
+ }
298
+
299
+ async _execute(context) {
300
+ const { device } = this.engine
301
+ const { camera } = context
302
+
303
+ // Skip if fog disabled
304
+ if (!this.fogEnabled) {
305
+ // Just copy input to output or return input directly
306
+ this.outputTexture = null
307
+ return
308
+ }
309
+
310
+ // Rebuild pipeline if needed
311
+ if (this._needsRebuild) {
312
+ await this._buildPipeline()
313
+ }
314
+
315
+ if (!this.renderPipeline || !this.inputTexture || !this.gbuffer || this._needsRebuild) {
316
+ return
317
+ }
318
+
319
+ // Update uniforms - 256 bytes = 64 floats (matches shader struct layout)
320
+ const uniformData = new Float32Array(64)
321
+
322
+ // inverseProjection (floats 0-15)
323
+ if (camera.iProj) {
324
+ uniformData.set(camera.iProj, 0)
325
+ }
326
+
327
+ // inverseView (floats 16-31)
328
+ if (camera.iView) {
329
+ uniformData.set(camera.iView, 16)
330
+ }
331
+
332
+ // cameraPosition (floats 32-34) + near (float 35)
333
+ uniformData[32] = camera.position[0]
334
+ uniformData[33] = camera.position[1]
335
+ uniformData[34] = camera.position[2]
336
+ uniformData[35] = camera.near || 0.1
337
+
338
+ // fogColor (floats 36-38) + far (float 39)
339
+ const fogColor = this.fogColor
340
+ uniformData[36] = fogColor[0]
341
+ uniformData[37] = fogColor[1]
342
+ uniformData[38] = fogColor[2]
343
+ uniformData[39] = camera.far || 1000
344
+
345
+ // distances (floats 40-42) + fogEnabled (float 43)
346
+ const distances = this.fogDistances
347
+ uniformData[40] = distances[0]
348
+ uniformData[41] = distances[1]
349
+ uniformData[42] = distances[2]
350
+ uniformData[43] = this.fogEnabled ? 1.0 : 0.0
351
+
352
+ // alphas (floats 44-46) + brightResist (float 47)
353
+ const alphas = this.fogAlpha
354
+ uniformData[44] = alphas[0]
355
+ uniformData[45] = alphas[1]
356
+ uniformData[46] = alphas[2]
357
+ uniformData[47] = this.fogBrightResist
358
+
359
+ // heightFade (floats 48-49) + screenSize (floats 50-51)
360
+ const heightFade = this.fogHeightFade
361
+ uniformData[48] = heightFade[0] // bottomY
362
+ uniformData[49] = heightFade[1] // topY
363
+ uniformData[50] = this.width
364
+ uniformData[51] = this.height
365
+
366
+ device.queue.writeBuffer(this.uniformBuffer, 0, uniformData)
367
+
368
+ // Render fog pass
369
+ const commandEncoder = device.createCommandEncoder({ label: 'Fog Pass' })
370
+
371
+ const renderPass = commandEncoder.beginRenderPass({
372
+ label: 'Fog Render Pass',
373
+ colorAttachments: [{
374
+ view: this.outputTextureView,
375
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
376
+ loadOp: 'clear',
377
+ storeOp: 'store',
378
+ }],
379
+ })
380
+
381
+ renderPass.setPipeline(this.renderPipeline)
382
+ renderPass.setBindGroup(0, this.bindGroup)
383
+ renderPass.draw(3)
384
+ renderPass.end()
385
+
386
+ device.queue.submit([commandEncoder.finish()])
387
+ }
388
+
389
+ async _resize(width, height) {
390
+ this.width = width
391
+ this.height = height
392
+ this._needsRebuild = true
393
+ }
394
+
395
+ _destroy() {
396
+ if (this.outputTexture) {
397
+ this.outputTexture.destroy()
398
+ this.outputTexture = null
399
+ }
400
+ this.renderPipeline = null
401
+ }
402
+
403
+ /**
404
+ * Get the output texture (fog-applied HDR)
405
+ */
406
+ getOutputTexture() {
407
+ // If fog is disabled, return input texture
408
+ if (!this.fogEnabled || !this.outputTexture) {
409
+ return this.inputTexture
410
+ }
411
+ return {
412
+ texture: this.outputTexture,
413
+ view: this.outputTextureView,
414
+ sampler: this.sampler,
415
+ }
416
+ }
417
+ }
418
+
419
+ export { FogPass }