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,352 @@
|
|
|
1
|
+
import { Texture } from "../Texture.js"
|
|
2
|
+
import { vec3 } from "../math.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ReflectionProbe - A single reflection probe with position and texture
|
|
6
|
+
*/
|
|
7
|
+
class ReflectionProbe {
|
|
8
|
+
constructor(id, position, worldId = 'default') {
|
|
9
|
+
this.id = id
|
|
10
|
+
this.position = [...position]
|
|
11
|
+
this.worldId = worldId
|
|
12
|
+
this.texture = null
|
|
13
|
+
this.loaded = false
|
|
14
|
+
this.url = null
|
|
15
|
+
this.mipLevels = 6
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* ReflectionProbeManager - Manages loading, caching, and interpolating reflection probes
|
|
21
|
+
*
|
|
22
|
+
* Handles:
|
|
23
|
+
* - Loading probes from server by worldId/position
|
|
24
|
+
* - Finding closest probes to camera
|
|
25
|
+
* - Interpolating between probes
|
|
26
|
+
* - Uploading new captures to server
|
|
27
|
+
*/
|
|
28
|
+
class ReflectionProbeManager {
|
|
29
|
+
constructor(engine) {
|
|
30
|
+
this.engine = engine
|
|
31
|
+
|
|
32
|
+
// All loaded probes: Map<probeId, ReflectionProbe>
|
|
33
|
+
this.probes = new Map()
|
|
34
|
+
|
|
35
|
+
// Probes indexed by world: Map<worldId, ReflectionProbe[]>
|
|
36
|
+
this.probesByWorld = new Map()
|
|
37
|
+
|
|
38
|
+
// Currently active probes for rendering (closest 2)
|
|
39
|
+
this.activeProbes = [null, null]
|
|
40
|
+
this.activeWeights = [1.0, 0.0]
|
|
41
|
+
|
|
42
|
+
// Base/fallback environment map (used when no probes available)
|
|
43
|
+
this.fallbackEnvironment = null
|
|
44
|
+
|
|
45
|
+
// Server configuration
|
|
46
|
+
this.serverBaseUrl = '/api/probes'
|
|
47
|
+
|
|
48
|
+
// Cache settings
|
|
49
|
+
this.maxCachedProbes = 10
|
|
50
|
+
this.loadingProbes = new Set() // URLs currently being loaded
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set fallback environment map
|
|
55
|
+
*/
|
|
56
|
+
setFallbackEnvironment(envMap) {
|
|
57
|
+
this.fallbackEnvironment = envMap
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Register a probe (from capture or loaded from server)
|
|
62
|
+
*/
|
|
63
|
+
registerProbe(probe) {
|
|
64
|
+
this.probes.set(probe.id, probe)
|
|
65
|
+
|
|
66
|
+
// Index by world
|
|
67
|
+
if (!this.probesByWorld.has(probe.worldId)) {
|
|
68
|
+
this.probesByWorld.set(probe.worldId, [])
|
|
69
|
+
}
|
|
70
|
+
this.probesByWorld.get(probe.worldId).push(probe)
|
|
71
|
+
|
|
72
|
+
console.log(`ReflectionProbeManager: Registered probe ${probe.id} at [${probe.position.join(', ')}]`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Load probe from URL
|
|
77
|
+
* @param {string} url - URL to HDR probe image
|
|
78
|
+
* @param {vec3} position - World position of probe
|
|
79
|
+
* @param {string} worldId - World identifier
|
|
80
|
+
* @returns {Promise<ReflectionProbe>}
|
|
81
|
+
*/
|
|
82
|
+
async loadProbe(url, position, worldId = 'default') {
|
|
83
|
+
// Check if already loaded
|
|
84
|
+
const existingId = `${worldId}_${position.join('_')}`
|
|
85
|
+
if (this.probes.has(existingId)) {
|
|
86
|
+
return this.probes.get(existingId)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check if already loading
|
|
90
|
+
if (this.loadingProbes.has(url)) {
|
|
91
|
+
// Wait for existing load
|
|
92
|
+
await new Promise(resolve => {
|
|
93
|
+
const check = () => {
|
|
94
|
+
if (!this.loadingProbes.has(url)) {
|
|
95
|
+
resolve()
|
|
96
|
+
} else {
|
|
97
|
+
setTimeout(check, 100)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
check()
|
|
101
|
+
})
|
|
102
|
+
return this.probes.get(existingId)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.loadingProbes.add(url)
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Load texture (supports HDR)
|
|
109
|
+
const texture = await Texture.fromImage(this.engine, url, {
|
|
110
|
+
flipY: false,
|
|
111
|
+
srgb: false,
|
|
112
|
+
generateMips: true,
|
|
113
|
+
addressMode: 'clamp-to-edge'
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const probe = new ReflectionProbe(existingId, position, worldId)
|
|
117
|
+
probe.texture = texture
|
|
118
|
+
probe.url = url
|
|
119
|
+
probe.loaded = true
|
|
120
|
+
|
|
121
|
+
this.registerProbe(probe)
|
|
122
|
+
|
|
123
|
+
// Enforce cache limit
|
|
124
|
+
this._enforceeCacheLimit()
|
|
125
|
+
|
|
126
|
+
return probe
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`ReflectionProbeManager: Failed to load probe from ${url}:`, error)
|
|
129
|
+
return null
|
|
130
|
+
} finally {
|
|
131
|
+
this.loadingProbes.delete(url)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Load probes for a world from server
|
|
137
|
+
* @param {string} worldId - World identifier
|
|
138
|
+
* @returns {Promise<ReflectionProbe[]>}
|
|
139
|
+
*/
|
|
140
|
+
async loadWorldProbes(worldId) {
|
|
141
|
+
try {
|
|
142
|
+
// Fetch probe manifest from server
|
|
143
|
+
const response = await fetch(`${this.serverBaseUrl}/${worldId}/manifest.json`)
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
console.warn(`ReflectionProbeManager: No probes found for world ${worldId}`)
|
|
146
|
+
return []
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const manifest = await response.json()
|
|
150
|
+
const loadedProbes = []
|
|
151
|
+
|
|
152
|
+
for (const probeInfo of manifest.probes) {
|
|
153
|
+
const probe = await this.loadProbe(
|
|
154
|
+
`${this.serverBaseUrl}/${worldId}/${probeInfo.file}`,
|
|
155
|
+
probeInfo.position,
|
|
156
|
+
worldId
|
|
157
|
+
)
|
|
158
|
+
if (probe) {
|
|
159
|
+
loadedProbes.push(probe)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return loadedProbes
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.warn(`ReflectionProbeManager: Failed to load world probes for ${worldId}:`, error)
|
|
166
|
+
return []
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Find closest probes to a position
|
|
172
|
+
* @param {vec3} position - World position
|
|
173
|
+
* @param {string} worldId - World identifier
|
|
174
|
+
* @param {number} count - Number of probes to find (default 2)
|
|
175
|
+
* @returns {{ probes: ReflectionProbe[], weights: number[] }}
|
|
176
|
+
*/
|
|
177
|
+
findClosestProbes(position, worldId = 'default', count = 2) {
|
|
178
|
+
const worldProbes = this.probesByWorld.get(worldId) || []
|
|
179
|
+
|
|
180
|
+
if (worldProbes.length === 0) {
|
|
181
|
+
return { probes: [], weights: [] }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Calculate distances
|
|
185
|
+
const probesWithDistance = worldProbes
|
|
186
|
+
.filter(p => p.loaded && p.texture)
|
|
187
|
+
.map(probe => {
|
|
188
|
+
const dx = probe.position[0] - position[0]
|
|
189
|
+
const dy = probe.position[1] - position[1]
|
|
190
|
+
const dz = probe.position[2] - position[2]
|
|
191
|
+
const distSq = dx * dx + dy * dy + dz * dz
|
|
192
|
+
return { probe, distance: Math.sqrt(distSq) }
|
|
193
|
+
})
|
|
194
|
+
.sort((a, b) => a.distance - b.distance)
|
|
195
|
+
|
|
196
|
+
// Get closest N probes
|
|
197
|
+
const closest = probesWithDistance.slice(0, count)
|
|
198
|
+
|
|
199
|
+
if (closest.length === 0) {
|
|
200
|
+
return { probes: [], weights: [] }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (closest.length === 1) {
|
|
204
|
+
return {
|
|
205
|
+
probes: [closest[0].probe],
|
|
206
|
+
weights: [1.0]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Calculate interpolation weights based on inverse distance
|
|
211
|
+
const totalInvDist = closest.reduce((sum, p) => sum + 1.0 / Math.max(p.distance, 0.001), 0)
|
|
212
|
+
const weights = closest.map(p => (1.0 / Math.max(p.distance, 0.001)) / totalInvDist)
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
probes: closest.map(p => p.probe),
|
|
216
|
+
weights
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Update active probes based on camera position
|
|
222
|
+
* @param {vec3} cameraPosition - Camera world position
|
|
223
|
+
* @param {string} worldId - Current world
|
|
224
|
+
*/
|
|
225
|
+
updateActiveProbes(cameraPosition, worldId = 'default') {
|
|
226
|
+
const { probes, weights } = this.findClosestProbes(cameraPosition, worldId, 2)
|
|
227
|
+
|
|
228
|
+
this.activeProbes = [probes[0] || null, probes[1] || null]
|
|
229
|
+
this.activeWeights = [weights[0] || 1.0, weights[1] || 0.0]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get active probe textures and weights for shader
|
|
234
|
+
* @returns {{ textures: [Texture, Texture], weights: [number, number] }}
|
|
235
|
+
*/
|
|
236
|
+
getActiveProbeData() {
|
|
237
|
+
return {
|
|
238
|
+
textures: [
|
|
239
|
+
this.activeProbes[0]?.texture || this.fallbackEnvironment,
|
|
240
|
+
this.activeProbes[1]?.texture || this.fallbackEnvironment
|
|
241
|
+
],
|
|
242
|
+
weights: this.activeWeights
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Upload a captured probe to server
|
|
248
|
+
* @param {Blob} probeData - HDR PNG blob
|
|
249
|
+
* @param {vec3} position - Capture position
|
|
250
|
+
* @param {string} worldId - World identifier
|
|
251
|
+
* @returns {Promise<boolean>} Success
|
|
252
|
+
*/
|
|
253
|
+
async uploadProbe(probeData, position, worldId = 'default') {
|
|
254
|
+
try {
|
|
255
|
+
const formData = new FormData()
|
|
256
|
+
formData.append('probe', probeData, 'probe.png')
|
|
257
|
+
formData.append('position', JSON.stringify(position))
|
|
258
|
+
formData.append('worldId', worldId)
|
|
259
|
+
|
|
260
|
+
const response = await fetch(`${this.serverBaseUrl}/upload`, {
|
|
261
|
+
method: 'POST',
|
|
262
|
+
body: formData
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
throw new Error(`Upload failed: ${response.status}`)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const result = await response.json()
|
|
270
|
+
console.log(`ReflectionProbeManager: Uploaded probe to ${result.url}`)
|
|
271
|
+
|
|
272
|
+
// Load the uploaded probe
|
|
273
|
+
await this.loadProbe(result.url, position, worldId)
|
|
274
|
+
|
|
275
|
+
return true
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('ReflectionProbeManager: Failed to upload probe:', error)
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Remove a probe
|
|
284
|
+
*/
|
|
285
|
+
removeProbe(probeId) {
|
|
286
|
+
const probe = this.probes.get(probeId)
|
|
287
|
+
if (!probe) return
|
|
288
|
+
|
|
289
|
+
// Remove from world index
|
|
290
|
+
const worldProbes = this.probesByWorld.get(probe.worldId)
|
|
291
|
+
if (worldProbes) {
|
|
292
|
+
const idx = worldProbes.indexOf(probe)
|
|
293
|
+
if (idx >= 0) worldProbes.splice(idx, 1)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Destroy texture
|
|
297
|
+
if (probe.texture?.texture) {
|
|
298
|
+
probe.texture.texture.destroy()
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.probes.delete(probeId)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Enforce cache limit by removing least recently used probes
|
|
306
|
+
*/
|
|
307
|
+
_enforceeCacheLimit() {
|
|
308
|
+
if (this.probes.size <= this.maxCachedProbes) return
|
|
309
|
+
|
|
310
|
+
// Simple LRU: remove oldest probes not currently active
|
|
311
|
+
const toRemove = []
|
|
312
|
+
for (const [id, probe] of this.probes) {
|
|
313
|
+
if (probe !== this.activeProbes[0] && probe !== this.activeProbes[1]) {
|
|
314
|
+
toRemove.push(id)
|
|
315
|
+
if (this.probes.size - toRemove.length <= this.maxCachedProbes) {
|
|
316
|
+
break
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (const id of toRemove) {
|
|
322
|
+
this.removeProbe(id)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get statistics
|
|
328
|
+
*/
|
|
329
|
+
getStats() {
|
|
330
|
+
return {
|
|
331
|
+
totalProbes: this.probes.size,
|
|
332
|
+
loadedProbes: [...this.probes.values()].filter(p => p.loaded).length,
|
|
333
|
+
worldCount: this.probesByWorld.size,
|
|
334
|
+
activeProbe0: this.activeProbes[0]?.id || 'none',
|
|
335
|
+
activeProbe1: this.activeProbes[1]?.id || 'none',
|
|
336
|
+
weights: this.activeWeights
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Destroy all resources
|
|
342
|
+
*/
|
|
343
|
+
destroy() {
|
|
344
|
+
for (const [id] of this.probes) {
|
|
345
|
+
this.removeProbe(id)
|
|
346
|
+
}
|
|
347
|
+
this.probes.clear()
|
|
348
|
+
this.probesByWorld.clear()
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export { ReflectionProbeManager, ReflectionProbe }
|