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.
- package/LICENSE.txt +0 -0
- package/README.md +0 -0
- package/dist/Renderer.cjs +18200 -0
- package/dist/Renderer.cjs.map +1 -0
- package/dist/Renderer.js +18183 -0
- package/dist/Renderer.js.map +1 -0
- package/dist/client.cjs +94 -260
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +71 -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} +173 -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 +572 -0
- package/src/renderer/Geometry.js +1049 -0
- package/src/renderer/Material.js +61 -0
- package/src/renderer/Mesh.js +211 -0
- package/src/renderer/Node.js +112 -0
- package/src/renderer/Pipeline.js +643 -0
- package/src/renderer/Renderer.js +1324 -0
- package/src/renderer/Skin.js +792 -0
- package/src/renderer/Texture.js +584 -0
- package/src/renderer/core/AssetManager.js +359 -0
- package/src/renderer/core/CullingSystem.js +307 -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 +546 -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 +2064 -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 +417 -0
- package/src/renderer/rendering/passes/FogPass.js +419 -0
- package/src/renderer/rendering/passes/GBufferPass.js +706 -0
- package/src/renderer/rendering/passes/HiZPass.js +714 -0
- package/src/renderer/rendering/passes/LightingPass.js +739 -0
- package/src/renderer/rendering/passes/ParticlePass.js +835 -0
- package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
- package/src/renderer/rendering/passes/PostProcessPass.js +282 -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 +265 -0
- package/src/renderer/rendering/passes/SSGITilePass.js +296 -0
- package/src/renderer/rendering/passes/ShadowPass.js +1822 -0
- package/src/renderer/rendering/passes/TransparentPass.js +831 -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/depth_copy.wgsl +17 -0
- package/src/renderer/rendering/shaders/geometry.wgsl +550 -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 +525 -0
- package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
- package/src/renderer/rendering/shaders/postproc.wgsl +272 -0
- package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
- package/src/renderer/rendering/shaders/shadow.wgsl +76 -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/utils/BoundingSphere.js +439 -0
- package/src/renderer/utils/Frustum.js +281 -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,308 @@
|
|
|
1
|
+
import { BasePass } from "./BasePass.js"
|
|
2
|
+
import { Texture } from "../../Texture.js"
|
|
3
|
+
|
|
4
|
+
import aoWGSL from "../shaders/ao.wgsl"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* AOPass - Screen Space Ambient Occlusion
|
|
8
|
+
*
|
|
9
|
+
* Pass 5 in the 7-pass pipeline.
|
|
10
|
+
* Calculates ambient occlusion from depth and normal buffers.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Cavity/corner darkening (traditional SSAO)
|
|
14
|
+
* - Normal-based darkening (plasticity for objects in shadow)
|
|
15
|
+
* - Blue noise jittered sampling
|
|
16
|
+
* - Reflectivity-based fade (reflective surfaces have no AO)
|
|
17
|
+
* - Distance fade (fades to 0 at configurable distance)
|
|
18
|
+
*
|
|
19
|
+
* Inputs: GBuffer (depth, normal, ARM)
|
|
20
|
+
* Output: AO texture (r8unorm - single channel)
|
|
21
|
+
*/
|
|
22
|
+
class AOPass extends BasePass {
|
|
23
|
+
constructor(engine = null) {
|
|
24
|
+
super('AO', engine)
|
|
25
|
+
|
|
26
|
+
this.renderPipeline = null
|
|
27
|
+
this.outputTexture = null
|
|
28
|
+
this.gbuffer = null
|
|
29
|
+
this.noiseTexture = null
|
|
30
|
+
this.noiseSize = 128
|
|
31
|
+
this.noiseAnimated = true
|
|
32
|
+
|
|
33
|
+
// Render dimensions (may differ from canvas when effect scaling is applied)
|
|
34
|
+
this.width = 0
|
|
35
|
+
this.height = 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Convenience getters for AO settings (with defaults for backward compatibility)
|
|
39
|
+
get aoIntensity() { return this.settings?.ao?.intensity ?? 1.0 }
|
|
40
|
+
get aoRadius() {
|
|
41
|
+
// Scale radius by renderScale and height relative to 1080p
|
|
42
|
+
// Settings are authored for 1080p, so scale proportionally
|
|
43
|
+
const baseRadius = this.settings?.ao?.radius ?? 64.0
|
|
44
|
+
const renderScale = this.settings?.rendering?.renderScale ?? 1.0
|
|
45
|
+
const heightScale = this.height > 0 ? this.height / 1080 : 1.0
|
|
46
|
+
return baseRadius * renderScale * heightScale
|
|
47
|
+
}
|
|
48
|
+
get aoFadeDistance() { return this.settings?.ao?.fadeDistance ?? 40.0 }
|
|
49
|
+
get aoBias() { return this.settings?.ao?.bias ?? 0.005 }
|
|
50
|
+
get sampleCount() { return this.settings?.ao?.sampleCount ?? 16 }
|
|
51
|
+
get aoLevel() { return this.settings?.ao?.level ?? 0.5 }
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set the GBuffer from GBufferPass
|
|
55
|
+
* @param {GBuffer} gbuffer - GBuffer textures
|
|
56
|
+
*/
|
|
57
|
+
async setGBuffer(gbuffer) {
|
|
58
|
+
this.gbuffer = gbuffer
|
|
59
|
+
this._needsRebuild = true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Set the noise texture for jittering
|
|
64
|
+
* @param {Texture} noise - Noise texture (blue noise or bayer dither)
|
|
65
|
+
* @param {number} size - Texture size
|
|
66
|
+
* @param {boolean} animated - Whether to animate noise offset each frame
|
|
67
|
+
*/
|
|
68
|
+
setNoise(noise, size = 64, animated = true) {
|
|
69
|
+
this.noiseTexture = noise
|
|
70
|
+
this.noiseSize = size
|
|
71
|
+
this.noiseAnimated = animated
|
|
72
|
+
this._needsRebuild = true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async _init() {
|
|
76
|
+
// Create output texture (single channel AO)
|
|
77
|
+
this.outputTexture = await Texture.renderTarget(this.engine, 'r8unorm')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async _buildPipeline() {
|
|
81
|
+
if (!this.gbuffer) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { device } = this.engine
|
|
86
|
+
|
|
87
|
+
// Create bind group layout
|
|
88
|
+
const bglEntries = [
|
|
89
|
+
// Uniforms
|
|
90
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
|
|
91
|
+
// Depth texture
|
|
92
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'depth' } },
|
|
93
|
+
// Normal texture
|
|
94
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'unfilterable-float' } },
|
|
95
|
+
// ARM texture (for reflectivity)
|
|
96
|
+
{ binding: 3, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'unfilterable-float' } },
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
// Add blue noise if available
|
|
100
|
+
if (this.noiseTexture) {
|
|
101
|
+
bglEntries.push(
|
|
102
|
+
{ binding: 4, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
107
|
+
label: 'AO Bind Group Layout',
|
|
108
|
+
entries: bglEntries,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Create uniform buffer (256 bytes minimum required by WebGPU)
|
|
112
|
+
// inverseProjection(64) + projection(64) + view(64) + canvasSize(8) + aoParams(16) + noiseParams(16) + cameraParams(8) + padding(16) = 256 bytes
|
|
113
|
+
this.uniformBuffer = device.createBuffer({
|
|
114
|
+
label: 'AO Uniforms',
|
|
115
|
+
size: 256,
|
|
116
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Create shader module
|
|
120
|
+
const shaderModule = device.createShaderModule({
|
|
121
|
+
label: 'AO Shader',
|
|
122
|
+
code: aoWGSL,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Check for compilation errors
|
|
126
|
+
const compilationInfo = await shaderModule.getCompilationInfo()
|
|
127
|
+
for (const message of compilationInfo.messages) {
|
|
128
|
+
if (message.type === 'error') {
|
|
129
|
+
console.error('AO Shader Error:', message.message)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Create pipeline
|
|
135
|
+
const pipelineLayout = device.createPipelineLayout({
|
|
136
|
+
label: 'AO Pipeline Layout',
|
|
137
|
+
bindGroupLayouts: [bindGroupLayout],
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// Use async pipeline creation for non-blocking initialization
|
|
141
|
+
this.renderPipeline = await device.createRenderPipelineAsync({
|
|
142
|
+
label: 'AO Pipeline',
|
|
143
|
+
layout: pipelineLayout,
|
|
144
|
+
vertex: {
|
|
145
|
+
module: shaderModule,
|
|
146
|
+
entryPoint: 'vertexMain',
|
|
147
|
+
},
|
|
148
|
+
fragment: {
|
|
149
|
+
module: shaderModule,
|
|
150
|
+
entryPoint: 'fragmentMain',
|
|
151
|
+
targets: [{ format: 'r8unorm' }],
|
|
152
|
+
},
|
|
153
|
+
primitive: {
|
|
154
|
+
topology: 'triangle-list',
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Create bind group
|
|
159
|
+
const entries = [
|
|
160
|
+
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
161
|
+
{ binding: 1, resource: this.gbuffer.depth.view },
|
|
162
|
+
{ binding: 2, resource: this.gbuffer.normal.view },
|
|
163
|
+
{ binding: 3, resource: this.gbuffer.arm.view },
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
if (this.noiseTexture) {
|
|
167
|
+
entries.push({ binding: 4, resource: this.noiseTexture.view })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.bindGroup = device.createBindGroup({
|
|
171
|
+
label: 'AO Bind Group',
|
|
172
|
+
layout: bindGroupLayout,
|
|
173
|
+
entries: entries,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
this.bindGroupLayout = bindGroupLayout
|
|
177
|
+
this._needsRebuild = false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async _execute(context) {
|
|
181
|
+
const { device, canvas } = this.engine
|
|
182
|
+
const { camera } = context
|
|
183
|
+
|
|
184
|
+
// Check if AO is enabled - if not, clear to white (no occlusion)
|
|
185
|
+
if (!this.settings?.ao?.enabled) {
|
|
186
|
+
if (this.outputTexture) {
|
|
187
|
+
const commandEncoder = device.createCommandEncoder({ label: 'AOClear' })
|
|
188
|
+
const passEncoder = commandEncoder.beginRenderPass({
|
|
189
|
+
colorAttachments: [{
|
|
190
|
+
view: this.outputTexture.view,
|
|
191
|
+
clearValue: { r: 1, g: 1, b: 1, a: 1 }, // White = no occlusion
|
|
192
|
+
loadOp: 'clear',
|
|
193
|
+
storeOp: 'store',
|
|
194
|
+
}],
|
|
195
|
+
})
|
|
196
|
+
passEncoder.end()
|
|
197
|
+
device.queue.submit([commandEncoder.finish()])
|
|
198
|
+
}
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Rebuild pipeline if needed
|
|
203
|
+
if (this._needsRebuild) {
|
|
204
|
+
await this._buildPipeline()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// If rebuild was attempted but failed, don't use stale pipeline with old bind groups
|
|
208
|
+
if (!this.renderPipeline || !this.gbuffer || this._needsRebuild) {
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Update uniforms
|
|
213
|
+
const uniformData = new Float32Array(64) // 256 bytes / 4
|
|
214
|
+
|
|
215
|
+
// Inverse projection matrix (for reconstructing view-space position)
|
|
216
|
+
if (camera.iProj) {
|
|
217
|
+
uniformData.set(camera.iProj, 0)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Projection matrix
|
|
221
|
+
uniformData.set(camera.proj, 16)
|
|
222
|
+
|
|
223
|
+
// View matrix
|
|
224
|
+
uniformData.set(camera.view, 32)
|
|
225
|
+
|
|
226
|
+
// AO size (vec2f at offset 192 = float index 48)
|
|
227
|
+
// Use stored dimensions (may differ from canvas when effect scaling is applied)
|
|
228
|
+
uniformData[48] = this.width || canvas.width
|
|
229
|
+
uniformData[49] = this.height || canvas.height
|
|
230
|
+
|
|
231
|
+
// GBuffer size (vec2f at offset 200 = float index 50)
|
|
232
|
+
// Always full resolution canvas size for GBuffer sampling
|
|
233
|
+
uniformData[50] = canvas.width
|
|
234
|
+
uniformData[51] = canvas.height
|
|
235
|
+
|
|
236
|
+
// AO parameters: intensity, radius, fadeDistance, bias (vec4f at offset 208 = float index 52)
|
|
237
|
+
uniformData[52] = this.aoIntensity
|
|
238
|
+
uniformData[53] = this.aoRadius
|
|
239
|
+
uniformData[54] = this.aoFadeDistance
|
|
240
|
+
uniformData[55] = this.aoBias
|
|
241
|
+
|
|
242
|
+
// Noise parameters: size, offsetX, offsetY, frame (vec4f at offset 224 = float index 56)
|
|
243
|
+
uniformData[56] = this.noiseSize
|
|
244
|
+
uniformData[57] = this.noiseAnimated ? (Math.random() * 0.1) : 0 // Animated offset X
|
|
245
|
+
uniformData[58] = this.noiseAnimated ? (Math.random() * 0.1) : 0 // Animated offset Y
|
|
246
|
+
uniformData[59] = performance.now() / 1000 // Time for animation
|
|
247
|
+
|
|
248
|
+
// Camera near/far (vec2f at offset 240 = float index 60)
|
|
249
|
+
uniformData[60] = camera.near || 0.1
|
|
250
|
+
uniformData[61] = camera.far || 1000
|
|
251
|
+
|
|
252
|
+
device.queue.writeBuffer(this.uniformBuffer, 0, uniformData)
|
|
253
|
+
|
|
254
|
+
// Render AO pass
|
|
255
|
+
const commandEncoder = device.createCommandEncoder({ label: 'AO Pass' })
|
|
256
|
+
|
|
257
|
+
const renderPass = commandEncoder.beginRenderPass({
|
|
258
|
+
label: 'AO Render Pass',
|
|
259
|
+
colorAttachments: [{
|
|
260
|
+
view: this.outputTexture.view,
|
|
261
|
+
clearValue: { r: 1, g: 1, b: 1, a: 1 }, // White = no occlusion
|
|
262
|
+
loadOp: 'clear',
|
|
263
|
+
storeOp: 'store',
|
|
264
|
+
}],
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
renderPass.setPipeline(this.renderPipeline)
|
|
268
|
+
renderPass.setBindGroup(0, this.bindGroup)
|
|
269
|
+
renderPass.draw(3) // Full-screen triangle
|
|
270
|
+
renderPass.end()
|
|
271
|
+
|
|
272
|
+
device.queue.submit([commandEncoder.finish()])
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async _resize(width, height) {
|
|
276
|
+
// Store dimensions for height-based scaling
|
|
277
|
+
this.width = width
|
|
278
|
+
this.height = height
|
|
279
|
+
|
|
280
|
+
// Recreate output texture at new size
|
|
281
|
+
this.outputTexture = await Texture.renderTarget(this.engine, 'r8unorm')
|
|
282
|
+
this._needsRebuild = true
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_destroy() {
|
|
286
|
+
this.renderPipeline = null
|
|
287
|
+
this.outputTexture = null
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get the output AO texture
|
|
292
|
+
*/
|
|
293
|
+
getOutputTexture() {
|
|
294
|
+
return this.outputTexture
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Configure AO parameters
|
|
299
|
+
*/
|
|
300
|
+
configure(options) {
|
|
301
|
+
if (options.intensity !== undefined) this.aoIntensity = options.intensity
|
|
302
|
+
if (options.radius !== undefined) this.aoRadius = options.radius
|
|
303
|
+
if (options.fadeDistance !== undefined) this.aoFadeDistance = options.fadeDistance
|
|
304
|
+
if (options.bias !== undefined) this.aoBias = options.bias
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export { AOPass }
|