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,703 @@
|
|
|
1
|
+
import GUI from 'lil-gui'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DebugUI - Stats display and settings GUI for the renderer
|
|
5
|
+
*
|
|
6
|
+
* Created lazily on first debug mode activation.
|
|
7
|
+
* Shows stats panel and collapsible settings folders.
|
|
8
|
+
*/
|
|
9
|
+
class DebugUI {
|
|
10
|
+
constructor(engine) {
|
|
11
|
+
this.engine = engine
|
|
12
|
+
this.gui = null
|
|
13
|
+
this.statsFolder = null
|
|
14
|
+
this.initialized = false
|
|
15
|
+
|
|
16
|
+
// Stats display elements
|
|
17
|
+
this._statsController = null
|
|
18
|
+
this._statsText = ''
|
|
19
|
+
|
|
20
|
+
// Stats update throttling (5 updates per second)
|
|
21
|
+
this._lastStatsUpdate = 0
|
|
22
|
+
this._statsUpdateInterval = 200 // ms
|
|
23
|
+
|
|
24
|
+
// Folder references for updating
|
|
25
|
+
this.folders = {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the debug UI (called lazily on first debug mode)
|
|
30
|
+
*/
|
|
31
|
+
init() {
|
|
32
|
+
if (this.initialized) return
|
|
33
|
+
|
|
34
|
+
this.gui = new GUI({ title: 'Debug Panel' })
|
|
35
|
+
this.gui.close() // Start collapsed
|
|
36
|
+
|
|
37
|
+
// Add CSS for stats styling
|
|
38
|
+
this._addStyles()
|
|
39
|
+
|
|
40
|
+
// Create stats folder at the top
|
|
41
|
+
this._createStatsFolder()
|
|
42
|
+
|
|
43
|
+
// Create settings folders
|
|
44
|
+
this._createRenderingFolder()
|
|
45
|
+
this._createCameraFolder()
|
|
46
|
+
this._createEnvironmentFolder()
|
|
47
|
+
this._createLightingFolder()
|
|
48
|
+
this._createMainLightFolder()
|
|
49
|
+
this._createShadowFolder()
|
|
50
|
+
this._createPlanarReflectionFolder()
|
|
51
|
+
this._createAOFolder()
|
|
52
|
+
this._createAmbientCaptureFolder()
|
|
53
|
+
this._createSSGIFolder()
|
|
54
|
+
this._createVolumetricFogFolder()
|
|
55
|
+
this._createBloomFolder()
|
|
56
|
+
this._createTonemapFolder()
|
|
57
|
+
this._createDitheringFolder()
|
|
58
|
+
this._createCRTFolder()
|
|
59
|
+
this._createDebugFolder()
|
|
60
|
+
|
|
61
|
+
// Close all folders by default
|
|
62
|
+
for (const folder of Object.values(this.folders)) {
|
|
63
|
+
folder.close()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.initialized = true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Add custom CSS for the debug UI
|
|
71
|
+
*/
|
|
72
|
+
_addStyles() {
|
|
73
|
+
if (document.getElementById('debug-ui-styles')) return
|
|
74
|
+
|
|
75
|
+
const style = document.createElement('style')
|
|
76
|
+
style.id = 'debug-ui-styles'
|
|
77
|
+
style.textContent = `
|
|
78
|
+
.lil-gui .stats-display {
|
|
79
|
+
font-family: monospace;
|
|
80
|
+
font-size: 11px;
|
|
81
|
+
line-height: 1.4;
|
|
82
|
+
white-space: pre;
|
|
83
|
+
padding: 4px 8px;
|
|
84
|
+
background: rgba(0, 0, 0, 0.3);
|
|
85
|
+
border-radius: 3px;
|
|
86
|
+
color: #8f8;
|
|
87
|
+
}
|
|
88
|
+
.lil-gui .stats-title {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
gap: 8px;
|
|
92
|
+
}
|
|
93
|
+
.lil-gui .stats-title::before {
|
|
94
|
+
content: '📊';
|
|
95
|
+
}
|
|
96
|
+
`
|
|
97
|
+
document.head.appendChild(style)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create stats folder with live updating display
|
|
102
|
+
*/
|
|
103
|
+
_createStatsFolder() {
|
|
104
|
+
this.statsFolder = this.gui.addFolder('Stats')
|
|
105
|
+
this.statsFolder.$title.classList.add('stats-title')
|
|
106
|
+
|
|
107
|
+
// Create a custom stats display element
|
|
108
|
+
const statsDiv = document.createElement('div')
|
|
109
|
+
statsDiv.className = 'stats-display'
|
|
110
|
+
statsDiv.textContent = 'Loading...'
|
|
111
|
+
this._statsElement = statsDiv
|
|
112
|
+
|
|
113
|
+
// Add to folder
|
|
114
|
+
this.statsFolder.$children.appendChild(statsDiv)
|
|
115
|
+
|
|
116
|
+
// Keep stats folder open by default
|
|
117
|
+
this.statsFolder.open()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Update stats display (throttled to 5 updates/sec, only when panel is open)
|
|
122
|
+
*/
|
|
123
|
+
updateStats() {
|
|
124
|
+
if (!this._statsElement || !this.engine.stats) return
|
|
125
|
+
|
|
126
|
+
// Only update if GUI panel is open
|
|
127
|
+
if (this.gui?._closed) return
|
|
128
|
+
|
|
129
|
+
// Throttle updates to 5 per second
|
|
130
|
+
const now = performance.now()
|
|
131
|
+
if (now - this._lastStatsUpdate < this._statsUpdateInterval) return
|
|
132
|
+
this._lastStatsUpdate = now
|
|
133
|
+
|
|
134
|
+
const s = this.engine.stats
|
|
135
|
+
const fmt = this._fmt.bind(this)
|
|
136
|
+
|
|
137
|
+
// Basic timing stats
|
|
138
|
+
const fps = s.avg_fps?.toFixed(0) || '0'
|
|
139
|
+
const ms = s.avg_dt?.toFixed(1) || '0'
|
|
140
|
+
const renderMs = s.avg_dt_render?.toFixed(2) || '0'
|
|
141
|
+
|
|
142
|
+
// Draw calls breakdown
|
|
143
|
+
const dc = s.totalDC || s.drawCalls || 0
|
|
144
|
+
const dcParts = []
|
|
145
|
+
if (s.shadowDC) dcParts.push(`sh:${fmt(s.shadowDC)}`)
|
|
146
|
+
if (s.planarDC) dcParts.push(`pl:${fmt(s.planarDC)}`)
|
|
147
|
+
if (s.transparentDC) dcParts.push(`tr:${fmt(s.transparentDC)}`)
|
|
148
|
+
const dcBreakdown = dcParts.length > 0 ? ` (${dcParts.join(' ')})` : ''
|
|
149
|
+
|
|
150
|
+
// Triangles breakdown
|
|
151
|
+
const tri = s.totalTri || s.triangles || 0
|
|
152
|
+
const triStr = this._formatNumber(tri)
|
|
153
|
+
const triParts = []
|
|
154
|
+
if (s.shadowTri) triParts.push(`sh:${this._formatNumber(s.shadowTri)}`)
|
|
155
|
+
if (s.planarTri) triParts.push(`pl:${this._formatNumber(s.planarTri)}`)
|
|
156
|
+
if (s.transparentTri) triParts.push(`tr:${this._formatNumber(s.transparentTri)}`)
|
|
157
|
+
const triBreakdown = triParts.length > 0 ? ` (${triParts.join(' ')})` : ''
|
|
158
|
+
|
|
159
|
+
// Get renderer stats
|
|
160
|
+
const renderer = this.engine.renderer
|
|
161
|
+
const lightingStats = renderer?.getPass?.('lighting')?.stats || {}
|
|
162
|
+
const gbufferStats = renderer?.getPass?.('gbuffer')?.legacyCullingStats || {}
|
|
163
|
+
const occStats = renderer?.cullingSystem?.getOcclusionStats?.() || {}
|
|
164
|
+
const rendererStats = renderer?.stats || {}
|
|
165
|
+
|
|
166
|
+
// Entity culling stats
|
|
167
|
+
const visibleEntities = rendererStats.visibleEntities || 0
|
|
168
|
+
const occCulled = occStats.culled || 0
|
|
169
|
+
|
|
170
|
+
// Mesh culling stats
|
|
171
|
+
const meshRendered = gbufferStats.rendered || 0
|
|
172
|
+
const meshTotal = gbufferStats.total || 0
|
|
173
|
+
const meshOccCulled = gbufferStats.culledByOcclusion || 0
|
|
174
|
+
const meshSkipped = gbufferStats.skippedNoBsphere || 0
|
|
175
|
+
|
|
176
|
+
// Shadow entity culling
|
|
177
|
+
const shEntCull = []
|
|
178
|
+
if (s.shadowFrustumCulled) shEntCull.push(`fr:${fmt(s.shadowFrustumCulled)}`)
|
|
179
|
+
if (s.shadowDistanceCulled) shEntCull.push(`dist:${fmt(s.shadowDistanceCulled)}`)
|
|
180
|
+
if (s.shadowHiZCulled) shEntCull.push(`hiz:${fmt(s.shadowHiZCulled)}`)
|
|
181
|
+
if (s.shadowPixelCulled) shEntCull.push(`px:${fmt(s.shadowPixelCulled)}`)
|
|
182
|
+
const shEntStr = shEntCull.length > 0 ? ` ${shEntCull.join(' ')}` : ''
|
|
183
|
+
|
|
184
|
+
// Shadow mesh culling
|
|
185
|
+
const shMeshCull = []
|
|
186
|
+
if (s.shadowMeshFrustumCulled) shMeshCull.push(`fr:${fmt(s.shadowMeshFrustumCulled)}`)
|
|
187
|
+
if (s.shadowMeshDistanceCulled) shMeshCull.push(`dist:${fmt(s.shadowMeshDistanceCulled)}`)
|
|
188
|
+
if (s.shadowMeshOcclusionCulled) shMeshCull.push(`occ:${fmt(s.shadowMeshOcclusionCulled)}`)
|
|
189
|
+
const shMeshStr = shMeshCull.length > 0 ? ` ${shMeshCull.join(' ')}` : ''
|
|
190
|
+
|
|
191
|
+
// Lights stats
|
|
192
|
+
const visibleLights = lightingStats.visibleLights || 0
|
|
193
|
+
const lightOccCulled = lightingStats.culledByOcclusion || 0
|
|
194
|
+
|
|
195
|
+
this._statsElement.textContent =
|
|
196
|
+
`FPS: ${fps} (${ms}ms) render: ${renderMs}ms\n` +
|
|
197
|
+
`DC: ${fmt(dc)}${dcBreakdown}\n` +
|
|
198
|
+
`Tri: ${triStr}${triBreakdown}\n` +
|
|
199
|
+
`Entities: ${fmt(visibleEntities)} (occ:${fmt(occCulled)})\n` +
|
|
200
|
+
`Meshes: ${fmt(meshRendered)}/${fmt(meshTotal)} (occ:${fmt(meshOccCulled)} skip:${fmt(meshSkipped)})\n` +
|
|
201
|
+
`Shadow ent:${shEntStr || ' -'} mesh:${shMeshStr || ' -'}\n` +
|
|
202
|
+
`Lights: ${fmt(visibleLights)} (occ:${fmt(lightOccCulled)})`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Format number with thin space as thousand separator
|
|
207
|
+
*/
|
|
208
|
+
_fmt(n) {
|
|
209
|
+
if (n === undefined || n === null) return '0'
|
|
210
|
+
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '\u2009')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Format large numbers with K/M suffix
|
|
215
|
+
*/
|
|
216
|
+
_formatNumber(n) {
|
|
217
|
+
if (n > 1000000) return (n / 1000000).toFixed(1) + 'M'
|
|
218
|
+
if (n > 1000) return (n / 1000).toFixed(0) + 'K'
|
|
219
|
+
return n.toString()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
_createRenderingFolder() {
|
|
223
|
+
const s = this.engine.settings
|
|
224
|
+
const folder = this.gui.addFolder('Rendering')
|
|
225
|
+
this.folders.rendering = folder
|
|
226
|
+
|
|
227
|
+
folder.add(s.rendering, 'renderScale', 0.25, 2.0, 0.25).name('Render Scale')
|
|
228
|
+
.onChange(() => this.engine.needsResize = true)
|
|
229
|
+
folder.add(s.rendering.autoScale, 'enabled').name('Auto-Scale (4K)')
|
|
230
|
+
.onChange(() => this.engine.needsResize = true)
|
|
231
|
+
folder.add(s.rendering.autoScale, 'maxHeight', 720, 2160, 1).name('Max Height')
|
|
232
|
+
folder.add(s.rendering.autoScale, 'scaleFactor', 0.25, 1.0, 0.05).name('Scale Factor')
|
|
233
|
+
folder.add(s.rendering, 'fxaa').name('FXAA')
|
|
234
|
+
folder.add(s.rendering, 'jitter').name('TAA Jitter')
|
|
235
|
+
if (s.rendering.jitterAmount !== undefined) {
|
|
236
|
+
folder.add(s.rendering, 'jitterAmount', 0, 1, 0.01).name('Jitter Amount')
|
|
237
|
+
}
|
|
238
|
+
if (s.rendering.jitterFadeDistance !== undefined) {
|
|
239
|
+
folder.add(s.rendering, 'jitterFadeDistance', 5, 100, 1).name('Jitter Fade Dist')
|
|
240
|
+
}
|
|
241
|
+
folder.add(s.rendering, 'debug').name('Debug Mode')
|
|
242
|
+
.onChange((value) => {
|
|
243
|
+
// Immediately hide panel when debug mode is turned off
|
|
244
|
+
if (!value && this.gui) {
|
|
245
|
+
this.gui.domElement.style.display = 'none'
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Culling subfolder
|
|
250
|
+
if (s.culling) {
|
|
251
|
+
const cullFolder = folder.addFolder('Culling')
|
|
252
|
+
cullFolder.add(s.culling, 'frustumEnabled').name('Frustum Culling')
|
|
253
|
+
|
|
254
|
+
if (s.occlusionCulling) {
|
|
255
|
+
cullFolder.add(s.occlusionCulling, 'enabled').name('Occlusion Culling')
|
|
256
|
+
cullFolder.add(s.occlusionCulling, 'threshold', 0.1, 2.0, 0.1).name('Occlusion Threshold')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Planar reflection culling sub-folder
|
|
260
|
+
if (s.culling.planarReflection) {
|
|
261
|
+
const prFolder = cullFolder.addFolder('Planar Reflection')
|
|
262
|
+
prFolder.add(s.culling.planarReflection, 'frustum').name('Frustum Culling')
|
|
263
|
+
prFolder.add(s.culling.planarReflection, 'maxDistance', 10, 200, 10).name('Max Distance')
|
|
264
|
+
prFolder.add(s.culling.planarReflection, 'maxSkinned', 0, 100, 1).name('Max Skinned')
|
|
265
|
+
prFolder.add(s.culling.planarReflection, 'minPixelSize', 0, 16, 1).name('Min Pixel Size')
|
|
266
|
+
prFolder.close()
|
|
267
|
+
}
|
|
268
|
+
cullFolder.close()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Noise subfolder
|
|
272
|
+
if (s.noise) {
|
|
273
|
+
const noiseFolder = folder.addFolder('Noise')
|
|
274
|
+
noiseFolder.add(s.noise, 'type', ['bluenoise', 'bayer8']).name('Type')
|
|
275
|
+
.onChange(() => {
|
|
276
|
+
// Reload noise texture when type changes
|
|
277
|
+
if (this.engine.renderer?.renderGraph) {
|
|
278
|
+
this.engine.renderer.renderGraph.reloadNoiseTexture()
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
noiseFolder.add(s.noise, 'animated').name('Animated')
|
|
282
|
+
noiseFolder.close()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
_createAOFolder() {
|
|
287
|
+
const s = this.engine.settings
|
|
288
|
+
if (!s.ao) return
|
|
289
|
+
|
|
290
|
+
const folder = this.gui.addFolder('Ambient Occlusion')
|
|
291
|
+
this.folders.ao = folder
|
|
292
|
+
|
|
293
|
+
folder.add(s.ao, 'enabled').name('Enabled')
|
|
294
|
+
folder.add(s.ao, 'intensity', 0, 2, 0.1).name('Intensity')
|
|
295
|
+
folder.add(s.ao, 'radius', 8, 128, 1).name('Radius')
|
|
296
|
+
folder.add(s.ao, 'fadeDistance', 10, 100, 1).name('Fade Distance')
|
|
297
|
+
folder.add(s.ao, 'bias', 0, 0.05, 0.001).name('Bias')
|
|
298
|
+
folder.add(s.ao, 'level', 0, 1, 0.05).name('Level')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_createShadowFolder() {
|
|
302
|
+
const s = this.engine.settings
|
|
303
|
+
if (!s.shadow) return
|
|
304
|
+
|
|
305
|
+
const folder = this.gui.addFolder('Shadows')
|
|
306
|
+
this.folders.shadow = folder
|
|
307
|
+
|
|
308
|
+
folder.add(s.shadow, 'strength', 0, 1, 0.05).name('Strength')
|
|
309
|
+
if (s.shadow.spotMaxDistance !== undefined) {
|
|
310
|
+
folder.add(s.shadow, 'spotMaxDistance', 10, 200, 5).name('Spot Max Distance')
|
|
311
|
+
}
|
|
312
|
+
if (s.shadow.spotFadeStart !== undefined) {
|
|
313
|
+
folder.add(s.shadow, 'spotFadeStart', 10, 150, 5).name('Spot Fade Start')
|
|
314
|
+
}
|
|
315
|
+
folder.add(s.shadow, 'bias', 0, 0.01, 0.0001).name('Bias')
|
|
316
|
+
folder.add(s.shadow, 'normalBias', 0, 0.05, 0.001).name('Normal Bias')
|
|
317
|
+
folder.add(s.shadow, 'surfaceBias', 0, 0.1, 0.001).name('Surface Bias')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
_createMainLightFolder() {
|
|
321
|
+
const s = this.engine.settings
|
|
322
|
+
if (!s.mainLight) return
|
|
323
|
+
|
|
324
|
+
const folder = this.gui.addFolder('Main Light')
|
|
325
|
+
this.folders.mainLight = folder
|
|
326
|
+
|
|
327
|
+
folder.add(s.mainLight, 'enabled').name('Enabled')
|
|
328
|
+
folder.add(s.mainLight, 'intensity', 0, 2, 0.05).name('Intensity')
|
|
329
|
+
|
|
330
|
+
// Color picker
|
|
331
|
+
folder.addColor({ color: this._rgbToHex(s.mainLight.color) }, 'color')
|
|
332
|
+
.name('Color')
|
|
333
|
+
.onChange((hex) => {
|
|
334
|
+
const rgb = this._hexToRgb(hex)
|
|
335
|
+
s.mainLight.color = [rgb.r, rgb.g, rgb.b]
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// Direction controls
|
|
339
|
+
const dirProxy = {
|
|
340
|
+
x: s.mainLight.direction[0],
|
|
341
|
+
y: s.mainLight.direction[1],
|
|
342
|
+
z: s.mainLight.direction[2]
|
|
343
|
+
}
|
|
344
|
+
const updateDir = () => {
|
|
345
|
+
s.mainLight.direction = [dirProxy.x, dirProxy.y, dirProxy.z]
|
|
346
|
+
}
|
|
347
|
+
folder.add(dirProxy, 'x', -1, 1, 0.05).name('Direction X').onChange(updateDir)
|
|
348
|
+
folder.add(dirProxy, 'y', -1, 1, 0.05).name('Direction Y').onChange(updateDir)
|
|
349
|
+
folder.add(dirProxy, 'z', -1, 1, 0.05).name('Direction Z').onChange(updateDir)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_createEnvironmentFolder() {
|
|
353
|
+
const s = this.engine.settings
|
|
354
|
+
if (!s.environment) return
|
|
355
|
+
|
|
356
|
+
const folder = this.gui.addFolder('Environment')
|
|
357
|
+
this.folders.environment = folder
|
|
358
|
+
|
|
359
|
+
folder.add(s.environment, 'exposure', 0.1, 4, 0.1).name('Exposure')
|
|
360
|
+
folder.add(s.environment, 'diffuse', 0, 10, 0.1).name('Diffuse IBL')
|
|
361
|
+
folder.add(s.environment, 'specular', 0, 10, 0.1).name('Specular IBL')
|
|
362
|
+
|
|
363
|
+
// Fog subfolder
|
|
364
|
+
if (s.environment.fog) {
|
|
365
|
+
const fogFolder = folder.addFolder('Fog')
|
|
366
|
+
fogFolder.add(s.environment.fog, 'enabled').name('Enabled')
|
|
367
|
+
fogFolder.addColor({ color: this._rgbToHex(s.environment.fog.color) }, 'color')
|
|
368
|
+
.name('Color')
|
|
369
|
+
.onChange((hex) => {
|
|
370
|
+
const rgb = this._hexToRgb(hex)
|
|
371
|
+
s.environment.fog.color[0] = rgb.r
|
|
372
|
+
s.environment.fog.color[1] = rgb.g
|
|
373
|
+
s.environment.fog.color[2] = rgb.b
|
|
374
|
+
})
|
|
375
|
+
fogFolder.add(s.environment.fog.distances, '0', 0, 50, 1).name('Near Distance')
|
|
376
|
+
fogFolder.add(s.environment.fog.distances, '1', 0, 200, 5).name('Mid Distance')
|
|
377
|
+
fogFolder.add(s.environment.fog.distances, '2', 50, 500, 10).name('Far Distance')
|
|
378
|
+
fogFolder.add(s.environment.fog.alpha, '0', 0, 1, 0.01).name('Near Alpha')
|
|
379
|
+
fogFolder.add(s.environment.fog.alpha, '1', 0, 1, 0.01).name('Mid Alpha')
|
|
380
|
+
fogFolder.add(s.environment.fog.alpha, '2', 0, 1, 0.01).name('Far Alpha')
|
|
381
|
+
fogFolder.add(s.environment.fog.heightFade, '0', -100, 100, 1).name('Bottom Y')
|
|
382
|
+
fogFolder.add(s.environment.fog.heightFade, '1', -50, 200, 5).name('Top Y')
|
|
383
|
+
fogFolder.add(s.environment.fog, 'brightResist', 0, 1, 0.05).name('Bright Resist')
|
|
384
|
+
// Initialize debug if not present
|
|
385
|
+
if (s.environment.fog.debug === undefined) s.environment.fog.debug = 0
|
|
386
|
+
fogFolder.add(s.environment.fog, 'debug', 0, 10, 1).name('Debug Mode')
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
_createLightingFolder() {
|
|
391
|
+
const s = this.engine.settings
|
|
392
|
+
if (!s.lighting) return
|
|
393
|
+
|
|
394
|
+
const folder = this.gui.addFolder('Lighting System')
|
|
395
|
+
this.folders.lighting = folder
|
|
396
|
+
|
|
397
|
+
folder.add(s.lighting, 'cullingEnabled').name('Light Culling')
|
|
398
|
+
folder.add(s.lighting, 'maxDistance', 50, 500, 10).name('Max Distance')
|
|
399
|
+
if (s.lighting.specularBoost !== undefined) {
|
|
400
|
+
folder.add(s.lighting, 'specularBoost', 0, 2, 0.05).name('Specular Boost')
|
|
401
|
+
}
|
|
402
|
+
if (s.lighting.specularBoostRoughnessCutoff !== undefined) {
|
|
403
|
+
folder.add(s.lighting, 'specularBoostRoughnessCutoff', 0.1, 1.0, 0.05).name('Boost Roughness Cutoff')
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_createSSGIFolder() {
|
|
408
|
+
const s = this.engine.settings
|
|
409
|
+
if (!s.ssgi) return
|
|
410
|
+
|
|
411
|
+
const folder = this.gui.addFolder('SSGI (Indirect Light)')
|
|
412
|
+
this.folders.ssgi = folder
|
|
413
|
+
|
|
414
|
+
folder.add(s.ssgi, 'enabled').name('Enabled')
|
|
415
|
+
folder.add(s.ssgi, 'intensity', 0.0, 5.0, 0.1).name('Intensity')
|
|
416
|
+
folder.add(s.ssgi, 'emissiveBoost', 0.0, 50.0, 1.0).name('Emissive Boost')
|
|
417
|
+
folder.add(s.ssgi, 'maxBrightness', 1.0, 50.0, 1.0).name('Max Brightness')
|
|
418
|
+
folder.add(s.ssgi, 'sampleRadius', 0.5, 4.0, 0.5).name('Sample Radius')
|
|
419
|
+
folder.add(s.ssgi, 'saturateLevel', 0.1, 2.0, 0.1).name('Saturate Level')
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
_createVolumetricFogFolder() {
|
|
423
|
+
const s = this.engine.settings
|
|
424
|
+
if (!s.volumetricFog) return
|
|
425
|
+
|
|
426
|
+
// Initialize defaults for missing properties
|
|
427
|
+
const vf = s.volumetricFog
|
|
428
|
+
if (vf.density === undefined && vf.densityMultiplier === undefined) vf.density = 0.5
|
|
429
|
+
if (vf.scatterStrength === undefined) vf.scatterStrength = 1.0
|
|
430
|
+
if (!vf.heightRange) vf.heightRange = [-5, 20]
|
|
431
|
+
if (vf.resolution === undefined) vf.resolution = 0.25
|
|
432
|
+
if (vf.maxSamples === undefined) vf.maxSamples = 32
|
|
433
|
+
if (vf.blurRadius === undefined) vf.blurRadius = 4
|
|
434
|
+
if (vf.noiseStrength === undefined) vf.noiseStrength = 1.0
|
|
435
|
+
if (vf.noiseScale === undefined) vf.noiseScale = 0.25
|
|
436
|
+
if (vf.noiseAnimated === undefined) vf.noiseAnimated = true
|
|
437
|
+
if (vf.shadowsEnabled === undefined) vf.shadowsEnabled = true
|
|
438
|
+
if (vf.mainLightScatter === undefined) vf.mainLightScatter = 1.0
|
|
439
|
+
if (vf.mainLightScatterDark === undefined) vf.mainLightScatterDark = 3.0
|
|
440
|
+
if (vf.mainLightSaturation === undefined) vf.mainLightSaturation = 1.0
|
|
441
|
+
if (vf.brightnessThreshold === undefined) vf.brightnessThreshold = 1.0
|
|
442
|
+
if (vf.minVisibility === undefined) vf.minVisibility = 0.15
|
|
443
|
+
if (vf.skyBrightness === undefined) vf.skyBrightness = 5.0
|
|
444
|
+
if (vf.debug === undefined) vf.debug = 0
|
|
445
|
+
|
|
446
|
+
const folder = this.gui.addFolder('Volumetric Fog')
|
|
447
|
+
this.folders.volumetricFog = folder
|
|
448
|
+
|
|
449
|
+
folder.add(vf, 'enabled').name('Enabled')
|
|
450
|
+
|
|
451
|
+
// Density - use whichever property exists
|
|
452
|
+
if (vf.density !== undefined) {
|
|
453
|
+
folder.add(vf, 'density', 0.0, 2.0, 0.05).name('Density')
|
|
454
|
+
} else if (vf.densityMultiplier !== undefined) {
|
|
455
|
+
folder.add(vf, 'densityMultiplier', 0.0, 2.0, 0.05).name('Density')
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
folder.add(vf, 'scatterStrength', 0.0, 10.0, 0.1).name('Scatter (Lights)')
|
|
459
|
+
folder.add(vf, 'mainLightScatter', 0.0, 5.0, 0.1).name('Sun Scatter (Light)')
|
|
460
|
+
folder.add(vf, 'mainLightScatterDark', 0.0, 10.0, 0.1).name('Sun Scatter (Dark)')
|
|
461
|
+
folder.add(vf, 'mainLightSaturation', 0.0, 1.0, 0.01).name('Sun Saturation')
|
|
462
|
+
folder.add(vf, 'brightnessThreshold', 0.1, 5.0, 0.1).name('Bright Threshold')
|
|
463
|
+
folder.add(vf, 'minVisibility', 0.0, 1.0, 0.05).name('Min Visibility')
|
|
464
|
+
folder.add(vf, 'skyBrightness', 0.0, 10.0, 0.5).name('Sky Brightness')
|
|
465
|
+
folder.add(vf.heightRange, '0', -50, 50, 1).name('Height Bottom')
|
|
466
|
+
folder.add(vf.heightRange, '1', -10, 100, 1).name('Height Top')
|
|
467
|
+
|
|
468
|
+
// Quality sub-folder
|
|
469
|
+
const qualityFolder = folder.addFolder('Quality')
|
|
470
|
+
qualityFolder.add(vf, 'resolution', 0.125, 0.5, 0.125).name('Resolution')
|
|
471
|
+
qualityFolder.add(vf, 'maxSamples', 16, 128, 8).name('Max Samples')
|
|
472
|
+
qualityFolder.add(vf, 'blurRadius', 0, 8, 1).name('Blur Radius')
|
|
473
|
+
qualityFolder.close()
|
|
474
|
+
|
|
475
|
+
// Noise sub-folder
|
|
476
|
+
const noiseFolder = folder.addFolder('Noise')
|
|
477
|
+
noiseFolder.add(vf, 'noiseStrength', 0.0, 1.0, 0.1).name('Strength')
|
|
478
|
+
noiseFolder.add(vf, 'noiseScale', 0.05, 1.0, 0.05).name('Scale (Detail)')
|
|
479
|
+
noiseFolder.add(vf, 'noiseAnimated').name('Animated')
|
|
480
|
+
noiseFolder.close()
|
|
481
|
+
|
|
482
|
+
// Shadows & Debug
|
|
483
|
+
folder.add(vf, 'shadowsEnabled').name('Shadows')
|
|
484
|
+
folder.add(vf, 'debug', 0, 12, 1).name('Debug Mode')
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
_createBloomFolder() {
|
|
488
|
+
const s = this.engine.settings
|
|
489
|
+
if (!s.bloom) return
|
|
490
|
+
|
|
491
|
+
const folder = this.gui.addFolder('Bloom')
|
|
492
|
+
this.folders.bloom = folder
|
|
493
|
+
|
|
494
|
+
folder.add(s.bloom, 'enabled').name('Enabled')
|
|
495
|
+
folder.add(s.bloom, 'intensity', 0.0, 1.0, 0.01).name('Intensity')
|
|
496
|
+
folder.add(s.bloom, 'threshold', 0.0, 2.0, 0.01).name('Threshold')
|
|
497
|
+
folder.add(s.bloom, 'softThreshold', 0.0, 1.0, 0.1).name('Soft Threshold')
|
|
498
|
+
folder.add(s.bloom, 'radius', 8, 128, 4).name('Blur Radius')
|
|
499
|
+
folder.add(s.bloom, 'emissiveBoost', 0.0, 20.0, 0.1).name('Emissive Boost')
|
|
500
|
+
folder.add(s.bloom, 'maxBrightness', 1.0, 10.0, 0.5).name('Max Brightness')
|
|
501
|
+
if (s.bloom.scale !== undefined) {
|
|
502
|
+
folder.add(s.bloom, 'scale', 0.25, 1.0, 0.25).name('Resolution Scale')
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
_createTonemapFolder() {
|
|
507
|
+
const s = this.engine.settings
|
|
508
|
+
if (!s.rendering) return
|
|
509
|
+
|
|
510
|
+
// Ensure tonemapMode exists
|
|
511
|
+
if (s.rendering.tonemapMode === undefined) s.rendering.tonemapMode = 0
|
|
512
|
+
|
|
513
|
+
const folder = this.gui.addFolder('Tone Mapping')
|
|
514
|
+
this.folders.tonemap = folder
|
|
515
|
+
|
|
516
|
+
folder.add(s.rendering, 'tonemapMode', { 'ACES': 0, 'Reinhard': 1, 'None (Linear)': 2 }).name('Mode')
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
_createPlanarReflectionFolder() {
|
|
520
|
+
const s = this.engine.settings
|
|
521
|
+
if (!s.planarReflection) return
|
|
522
|
+
|
|
523
|
+
const folder = this.gui.addFolder('Planar Reflection')
|
|
524
|
+
this.folders.planarReflection = folder
|
|
525
|
+
|
|
526
|
+
folder.add(s.planarReflection, 'enabled').name('Enabled')
|
|
527
|
+
folder.add(s.planarReflection, 'groundLevel', -10, 10, 0.1).name('Ground Level')
|
|
528
|
+
folder.add(s.planarReflection, 'resolution', 0.25, 1.0, 0.25).name('Resolution')
|
|
529
|
+
folder.add(s.planarReflection, 'roughnessCutoff', 0.0, 1.0, 0.05).name('Roughness Cutoff')
|
|
530
|
+
folder.add(s.planarReflection, 'normalPerturbation', 0.0, 2.0, 0.1).name('Normal Perturbation')
|
|
531
|
+
folder.add(s.planarReflection, 'distanceFade', 0.1, 10.0, 0.1).name('Distance Fade')
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
_createAmbientCaptureFolder() {
|
|
535
|
+
const s = this.engine.settings
|
|
536
|
+
if (!s.ambientCapture) return
|
|
537
|
+
|
|
538
|
+
const folder = this.gui.addFolder('Probe GI (Ambient Capture)')
|
|
539
|
+
this.folders.ambientCapture = folder
|
|
540
|
+
|
|
541
|
+
folder.add(s.ambientCapture, 'enabled').name('Enabled')
|
|
542
|
+
folder.add(s.ambientCapture, 'intensity', 0.0, 2.0, 0.05).name('Intensity')
|
|
543
|
+
folder.add(s.ambientCapture, 'maxDistance', 5, 100, 5).name('Max Distance')
|
|
544
|
+
folder.add(s.ambientCapture, 'emissiveBoost', 0.0, 10.0, 0.1).name('Emissive Boost')
|
|
545
|
+
folder.add(s.ambientCapture, 'saturateLevel', 0.0, 2.0, 0.05).name('Saturate Level')
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
_createDitheringFolder() {
|
|
549
|
+
const s = this.engine.settings
|
|
550
|
+
if (!s.dithering) return
|
|
551
|
+
|
|
552
|
+
const folder = this.gui.addFolder('Dithering')
|
|
553
|
+
this.folders.dithering = folder
|
|
554
|
+
|
|
555
|
+
folder.add(s.dithering, 'enabled').name('Enabled')
|
|
556
|
+
folder.add(s.dithering, 'colorLevels', 4, 256, 1).name('Color Levels')
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
_createCRTFolder() {
|
|
560
|
+
const s = this.engine.settings
|
|
561
|
+
if (!s.crt) return
|
|
562
|
+
|
|
563
|
+
const folder = this.gui.addFolder('CRT Effect')
|
|
564
|
+
this.folders.crt = folder
|
|
565
|
+
|
|
566
|
+
folder.add(s.crt, 'enabled').name('CRT Enabled')
|
|
567
|
+
folder.add(s.crt, 'upscaleEnabled').name('Upscale Only')
|
|
568
|
+
folder.add(s.crt, 'upscaleTarget', 1, 8, 1).name('Upscale Target')
|
|
569
|
+
|
|
570
|
+
// Geometry sub-folder
|
|
571
|
+
const geomFolder = folder.addFolder('Geometry')
|
|
572
|
+
geomFolder.add(s.crt, 'curvature', 0, 0.25, 0.005).name('Curvature')
|
|
573
|
+
geomFolder.add(s.crt, 'cornerRadius', 0, 0.2, 0.005).name('Corner Radius')
|
|
574
|
+
geomFolder.add(s.crt, 'zoom', 1.0, 1.25, 0.005).name('Zoom')
|
|
575
|
+
geomFolder.close()
|
|
576
|
+
|
|
577
|
+
// Scanlines sub-folder
|
|
578
|
+
const scanFolder = folder.addFolder('Scanlines')
|
|
579
|
+
scanFolder.add(s.crt, 'scanlineIntensity', 0, 1, 0.05).name('Intensity')
|
|
580
|
+
scanFolder.add(s.crt, 'scanlineWidth', 0, 1, 0.05).name('Width')
|
|
581
|
+
scanFolder.add(s.crt, 'scanlineBrightBoost', 0, 2, 0.05).name('Bright Boost')
|
|
582
|
+
scanFolder.add(s.crt, 'scanlineHeight', 1, 10, 1).name('Height (px)')
|
|
583
|
+
scanFolder.close()
|
|
584
|
+
|
|
585
|
+
// Convergence sub-folder
|
|
586
|
+
const convFolder = folder.addFolder('RGB Convergence')
|
|
587
|
+
convFolder.add(s.crt.convergence, '0', -3, 3, 0.1).name('Red X Offset')
|
|
588
|
+
convFolder.add(s.crt.convergence, '1', -3, 3, 0.1).name('Green X Offset')
|
|
589
|
+
convFolder.add(s.crt.convergence, '2', -3, 3, 0.1).name('Blue X Offset')
|
|
590
|
+
convFolder.close()
|
|
591
|
+
|
|
592
|
+
// Phosphor mask sub-folder
|
|
593
|
+
const maskFolder = folder.addFolder('Phosphor Mask')
|
|
594
|
+
maskFolder.add(s.crt, 'maskType', ['none', 'aperture', 'slot', 'shadow']).name('Type')
|
|
595
|
+
maskFolder.add(s.crt, 'maskIntensity', 0, 1, 0.05).name('Intensity')
|
|
596
|
+
maskFolder.close()
|
|
597
|
+
|
|
598
|
+
// Vignette sub-folder
|
|
599
|
+
const vigFolder = folder.addFolder('Vignette')
|
|
600
|
+
vigFolder.add(s.crt, 'vignetteIntensity', 0, 1, 0.05).name('Intensity')
|
|
601
|
+
vigFolder.add(s.crt, 'vignetteSize', 0.1, 1, 0.05).name('Size')
|
|
602
|
+
vigFolder.close()
|
|
603
|
+
|
|
604
|
+
// Blur (top level, not in subfolder)
|
|
605
|
+
folder.add(s.crt, 'blurSize', 0, 8, 0.1).name('H-Blur (px)')
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
_createCameraFolder() {
|
|
609
|
+
const s = this.engine.settings
|
|
610
|
+
if (!s.camera) return
|
|
611
|
+
|
|
612
|
+
const folder = this.gui.addFolder('Camera')
|
|
613
|
+
this.folders.camera = folder
|
|
614
|
+
|
|
615
|
+
folder.add(s.camera, 'fov', 30, 120, 1).name('FOV').onChange(() => {
|
|
616
|
+
if (this.engine.camera) this.engine.camera.fov = s.camera.fov
|
|
617
|
+
})
|
|
618
|
+
folder.add(s.camera, 'near', 0.01, 1, 0.01).name('Near Plane').onChange(() => {
|
|
619
|
+
if (this.engine.camera) this.engine.camera.near = s.camera.near
|
|
620
|
+
})
|
|
621
|
+
folder.add(s.camera, 'far', 100, 10000, 100).name('Far Plane').onChange(() => {
|
|
622
|
+
if (this.engine.camera) this.engine.camera.far = s.camera.far
|
|
623
|
+
})
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
_createDebugFolder() {
|
|
627
|
+
const folder = this.gui.addFolder('Debug Options')
|
|
628
|
+
this.folders.debug = folder
|
|
629
|
+
|
|
630
|
+
// Add engine-level debug options
|
|
631
|
+
if (!this.engine._debugSettings) {
|
|
632
|
+
this.engine._debugSettings = {
|
|
633
|
+
showLights: false,
|
|
634
|
+
lightCrossSize: 10
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
folder.add(this.engine._debugSettings, 'showLights').name('Show Lights')
|
|
639
|
+
folder.add(this.engine._debugSettings, 'lightCrossSize', 5, 30, 1).name('Cross Size')
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Show or hide the debug UI
|
|
644
|
+
*/
|
|
645
|
+
setVisible(visible) {
|
|
646
|
+
if (visible && !this.initialized) {
|
|
647
|
+
this.init()
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (this.gui) {
|
|
651
|
+
this.gui.domElement.style.display = visible ? '' : 'none'
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Check if debug mode is on and update visibility
|
|
657
|
+
*/
|
|
658
|
+
update() {
|
|
659
|
+
const debugMode = this.engine.settings?.rendering?.debug ?? false
|
|
660
|
+
|
|
661
|
+
if (debugMode && !this.initialized) {
|
|
662
|
+
this.init()
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (this.gui) {
|
|
666
|
+
this.gui.domElement.style.display = debugMode ? '' : 'none'
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (debugMode && this.initialized) {
|
|
670
|
+
this.updateStats()
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Destroy the debug UI
|
|
676
|
+
*/
|
|
677
|
+
destroy() {
|
|
678
|
+
if (this.gui) {
|
|
679
|
+
this.gui.destroy()
|
|
680
|
+
this.gui = null
|
|
681
|
+
}
|
|
682
|
+
this.initialized = false
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Utility functions
|
|
686
|
+
_rgbToHex(rgb) {
|
|
687
|
+
const r = Math.round((rgb[0] || 0) * 255)
|
|
688
|
+
const g = Math.round((rgb[1] || 0) * 255)
|
|
689
|
+
const b = Math.round((rgb[2] || 0) * 255)
|
|
690
|
+
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
_hexToRgb(hex) {
|
|
694
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
|
695
|
+
return result ? {
|
|
696
|
+
r: parseInt(result[1], 16) / 255,
|
|
697
|
+
g: parseInt(result[2], 16) / 255,
|
|
698
|
+
b: parseInt(result[3], 16) / 255
|
|
699
|
+
} : { r: 1, g: 1, b: 1 }
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export { DebugUI }
|