minecraft-renderer 0.1.33 → 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.
@@ -149,43 +149,49 @@ const addNametag = (entity, options: { fontFamily: string }, mesh, version: stri
149
149
  c.removeFromParent()
150
150
  }
151
151
  }
152
- if (entity.username !== undefined && !entity.username.startsWith('EMPTY')) {
153
- const canvas = getUsernameTexture(entity, options, version)
154
- if (!canvas) return
155
- const tex = new THREE.Texture(canvas)
156
- tex.needsUpdate = true
157
- let nameTag: THREE.Object3D
158
- if (entity.nameTagFixed) {
159
- const geometry = new THREE.PlaneGeometry()
160
- const material = new THREE.MeshBasicMaterial({ map: tex })
161
- material.transparent = true
162
- nameTag = new THREE.Mesh(geometry, material)
163
- nameTag.rotation.set(entity.pitch, THREE.MathUtils.degToRad(entity.yaw + 180), 0)
164
- nameTag.position.y += entity.height + 0.3
165
- } else {
166
- const spriteMat = new THREE.SpriteMaterial({ map: tex })
167
- nameTag = new THREE.Sprite(spriteMat)
168
- nameTag.position.y += entity.height + 0.6
169
- }
170
- nameTag.renderOrder = 1000
171
- nameTag.scale.set(canvas.width * 0.005, canvas.height * 0.005, 1)
172
- if (entity.nameTagRotationRight) {
173
- nameTag.applyQuaternion(entity.nameTagRotationRight)
174
- }
175
- if (entity.nameTagScale) {
176
- nameTag.scale.multiply(entity.nameTagScale)
177
- }
178
- if (entity.nameTagRotationLeft) {
179
- nameTag.applyQuaternion(entity.nameTagRotationLeft)
180
- }
181
- if (entity.nameTagTranslation) {
182
- nameTag.position.add(entity.nameTagTranslation)
183
- }
184
- nameTag.name = 'nametag'
185
-
186
- mesh.add(nameTag)
187
- return nameTag
152
+ if (entity.username === undefined || entity.username === null) return
153
+
154
+ const plainUsername =
155
+ typeof entity.username === 'string'
156
+ ? entity.username
157
+ : new (PrismarineChatLoader(version))(entity.username).toString()
158
+ if (plainUsername.startsWith('EMPTY')) return
159
+
160
+ const canvas = getUsernameTexture(entity, options, version)
161
+ if (!canvas) return
162
+ const tex = new THREE.Texture(canvas)
163
+ tex.needsUpdate = true
164
+ let nameTag: THREE.Object3D
165
+ if (entity.nameTagFixed) {
166
+ const geometry = new THREE.PlaneGeometry()
167
+ const material = new THREE.MeshBasicMaterial({ map: tex })
168
+ material.transparent = true
169
+ nameTag = new THREE.Mesh(geometry, material)
170
+ nameTag.rotation.set(entity.pitch, THREE.MathUtils.degToRad(entity.yaw + 180), 0)
171
+ nameTag.position.y += entity.height + 0.3
172
+ } else {
173
+ const spriteMat = new THREE.SpriteMaterial({ map: tex })
174
+ nameTag = new THREE.Sprite(spriteMat)
175
+ nameTag.position.y += entity.height + 0.6
176
+ }
177
+ nameTag.renderOrder = 1000
178
+ nameTag.scale.set(canvas.width * 0.005, canvas.height * 0.005, 1)
179
+ if (entity.nameTagRotationRight) {
180
+ nameTag.applyQuaternion(entity.nameTagRotationRight)
181
+ }
182
+ if (entity.nameTagScale) {
183
+ nameTag.scale.multiply(entity.nameTagScale)
188
184
  }
185
+ if (entity.nameTagRotationLeft) {
186
+ nameTag.applyQuaternion(entity.nameTagRotationLeft)
187
+ }
188
+ if (entity.nameTagTranslation) {
189
+ nameTag.position.add(entity.nameTagTranslation)
190
+ }
191
+ nameTag.name = 'nametag'
192
+
193
+ mesh.add(nameTag)
194
+ return nameTag
189
195
  }
190
196
 
191
197
  // todo cleanup
@@ -68,6 +68,14 @@ export const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
68
68
  setSkyboxImage: worldRenderer.skyboxRenderer.setSkyboxImage.bind(worldRenderer.skyboxRenderer),
69
69
  // Rain methods
70
70
  setRain: (newState: boolean) => worldRenderer.toggleModule('rain', newState),
71
+ spawnBlockBreakParticles(x: number, y: number, z: number, blockName: string, floorMap: number[], biomeName?: string) {
72
+ const module = worldRenderer.getModule<import('./modules/blockBreakParticles').BlockBreakParticlesModule>('blockBreakParticles')
73
+ module?.spawnBlockBreakParticles(x, y, z, blockName, floorMap, biomeName)
74
+ },
75
+ spawnBlockCrackParticle(x: number, y: number, z: number, face: number, blockName: string, floorMap: number[], biomeName?: string) {
76
+ const module = worldRenderer.getModule<import('./modules/blockBreakParticles').BlockBreakParticlesModule>('blockBreakParticles')
77
+ module?.spawnCrackParticle(x, y, z, face, blockName, floorMap, biomeName)
78
+ },
71
79
  async loadGeometryExport(exportData: any) {
72
80
  // Import dynamically to avoid circular dependencies
73
81
  const { applyWorldGeometryExport } = await import('./worldGeometryExport')
@@ -217,7 +225,7 @@ export const createGraphicsBackendBase = () => {
217
225
  },
218
226
  get left() {
219
227
  return {
220
- 'Geo Memory': worldRenderer?.worldBlockGeometry.getEstimatedMemoryUsage().readable ?? '-'
228
+ 'Geo Memory': worldRenderer?.chunkMeshManager.getEstimatedMemoryUsage().total ?? '-'
221
229
  }
222
230
  },
223
231
  }),
@@ -0,0 +1,438 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import type { WorldRendererThree } from '../worldRendererThree'
4
+ import type { RendererModuleController, RendererModuleManifest } from '../rendererModuleSystem'
5
+
6
+ // --- Tint data (lazy-loaded from globalThis.loadedData.tints at runtime) ---
7
+ const tints: Record<string, Record<string, [number, number, number]>> = {}
8
+ let tintsInitialized = false
9
+
10
+ function ensureTintsLoaded(): void {
11
+ if (tintsInitialized) return
12
+ const tintsData = (globalThis as any).loadedData?.tints
13
+ if (!tintsData) return
14
+ for (const key of Object.keys(tintsData)) {
15
+ tints[key] = prepareTints(tintsData[key])
16
+ }
17
+ tintsInitialized = true
18
+ }
19
+
20
+ function prepareTints(data: any): Record<string, [number, number, number]> {
21
+ const result: Record<string, [number, number, number]> = {}
22
+ const defaultColor = tintToGl(data.default ?? 0xFFFFFF)
23
+ if (data.data) {
24
+ for (const entry of data.data) {
25
+ const color = tintToGl(entry.color)
26
+ for (const key of entry.keys) {
27
+ result[key] = color
28
+ }
29
+ }
30
+ }
31
+ return new Proxy(result, {
32
+ get(target, prop: string) {
33
+ return target[prop] ?? defaultColor
34
+ }
35
+ })
36
+ }
37
+
38
+ function tintToGl(tint: number): [number, number, number] {
39
+ return [
40
+ ((tint >> 16) & 0xFF) / 255,
41
+ ((tint >> 8) & 0xFF) / 255,
42
+ (tint & 0xFF) / 255,
43
+ ]
44
+ }
45
+
46
+ function resolveTintColor(blockName: string, biomeName: string): [number, number, number] {
47
+ ensureTintsLoaded()
48
+ if (blockName === 'grass_block') return [1, 1, 1]
49
+ if (blockName === 'redstone_wire') return [1, 1, 1]
50
+ if (blockName === 'birch_leaves' || blockName === 'spruce_leaves' || blockName === 'lily_pad') {
51
+ return tints.constant?.[blockName] ?? [1, 1, 1]
52
+ }
53
+ if (blockName.includes('leaves') || blockName === 'vine') {
54
+ return tints.foliage?.[biomeName] ?? [1, 1, 1]
55
+ }
56
+ const grassTintedBlocks = ['short_grass', 'tall_grass', 'fern', 'large_fern', 'sugar_cane', 'grass']
57
+ if (grassTintedBlocks.includes(blockName)) {
58
+ return tints.grass?.[biomeName] ?? [1, 1, 1]
59
+ }
60
+ return [1, 1, 1]
61
+ }
62
+
63
+ interface BreakParticle {
64
+ mesh: THREE.Mesh
65
+ active: boolean
66
+ x: number; y: number; z: number
67
+ prevX: number; prevY: number; prevZ: number
68
+ xd: number; yd: number; zd: number
69
+ age: number
70
+ maxAge: number
71
+ onGround: boolean
72
+ floorMap: number[]
73
+ blockX: number
74
+ blockZ: number
75
+ }
76
+
77
+ const MAX_PARTICLES = 512
78
+ const TICK_RATE = 1 / 20
79
+
80
+ export class BlockBreakParticlesModule implements RendererModuleController {
81
+ private particles: BreakParticle[] = []
82
+ private sharedMaterial?: THREE.MeshBasicMaterial
83
+ private enabled = false
84
+ private tickAccumulator = 0
85
+ private nextParticleIndex = 0
86
+
87
+ constructor(private readonly worldRenderer: WorldRendererThree) {}
88
+
89
+ enable(): void {
90
+ if (this.enabled) return
91
+ this.enabled = true
92
+ this.ensureMaterial()
93
+ }
94
+
95
+ disable(): void {
96
+ if (!this.enabled) return
97
+ this.enabled = false
98
+ }
99
+
100
+ dispose(): void {
101
+ for (const p of this.particles) {
102
+ if (p.active) {
103
+ this.worldRenderer.sceneOrigin.removeAndUntrack(p.mesh)
104
+ }
105
+ p.mesh.geometry.dispose()
106
+ }
107
+ this.particles = []
108
+ this.sharedMaterial?.dispose()
109
+ this.sharedMaterial = undefined
110
+ this.nextParticleIndex = 0
111
+ }
112
+
113
+ render = (deltaTime: number): void => {
114
+ if (!this.enabled) return
115
+
116
+ this.tickAccumulator += deltaTime
117
+ while (this.tickAccumulator >= TICK_RATE) {
118
+ this.tickAccumulator -= TICK_RATE
119
+ this.tickPhysics()
120
+ }
121
+
122
+ const alpha = this.tickAccumulator / TICK_RATE
123
+ this.updateVisuals(alpha)
124
+ }
125
+
126
+ spawnBlockBreakParticles(worldX: number, worldY: number, worldZ: number, blockName: string, floorMap: number[], biomeName = 'plains'): void {
127
+ if (!this.enabled) return
128
+
129
+ const texInfo = this.resolveBlockTexture(blockName)
130
+ if (!texInfo) return
131
+
132
+ const tintColor = resolveTintColor(blockName, biomeName)
133
+
134
+ for (let ox = 0; ox < 4; ox++) {
135
+ for (let oy = 0; oy < 4; oy++) {
136
+ for (let oz = 0; oz < 4; oz++) {
137
+ const px = worldX + (ox + 0.5) / 4
138
+ const py = worldY + (oy + 0.5) / 4
139
+ const pz = worldZ + (oz + 0.5) / 4
140
+
141
+ let motionX = px - worldX - 0.5
142
+ let motionY = py - worldY - 0.5
143
+ let motionZ = pz - worldZ - 0.5
144
+
145
+ motionX += (Math.random() * 2 - 1) * 0.4
146
+ motionY += (Math.random() * 2 - 1) * 0.4
147
+ motionZ += (Math.random() * 2 - 1) * 0.4
148
+
149
+ const strength = (Math.random() + Math.random() + 1) * 0.15
150
+ const len = Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ)
151
+ const xd = (motionX / len) * strength * 0.4
152
+ const yd = (motionY / len) * strength * 0.4 + 0.1
153
+ const zd = (motionZ / len) * strength * 0.4
154
+
155
+ const maxAge = Math.floor(4 / (Math.random() * 0.9 + 0.1))
156
+
157
+ this.createParticle(px, py, pz, xd, yd, zd, maxAge, texInfo, floorMap, worldX, worldZ, 1.0, tintColor)
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ private tickPhysics(): void {
164
+ for (const p of this.particles) {
165
+ if (!p.active) continue
166
+
167
+ p.prevX = p.x
168
+ p.prevY = p.y
169
+ p.prevZ = p.z
170
+
171
+ p.age++
172
+ if (p.age >= p.maxAge) {
173
+ this.deactivateParticle(p)
174
+ continue
175
+ }
176
+
177
+ p.yd -= 0.04
178
+ p.x += p.xd
179
+ p.y += p.yd
180
+ p.z += p.zd
181
+
182
+ // Recalculate onGround each tick (particle may move to a different column)
183
+ const floorY = this.getFloorY(p)
184
+ if (p.y <= floorY) {
185
+ p.y = floorY
186
+ p.yd = 0
187
+ p.onGround = true
188
+ } else {
189
+ p.onGround = false
190
+ }
191
+
192
+ p.xd *= 0.98
193
+ p.yd *= 0.98
194
+ p.zd *= 0.98
195
+
196
+ if (p.onGround) {
197
+ p.xd *= 0.7
198
+ p.zd *= 0.7
199
+ }
200
+ }
201
+ }
202
+
203
+ private updateVisuals(alpha: number): void {
204
+ // Camera is at ~(0,0,0) in scene space (sceneOrigin tracks camera)
205
+ const cameraPosScene = this.worldRenderer.camera.position
206
+
207
+ for (const p of this.particles) {
208
+ if (!p.active) continue
209
+
210
+ const displayX = p.prevX + (p.x - p.prevX) * alpha
211
+ const displayY = p.prevY + (p.y - p.prevY) * alpha
212
+ const displayZ = p.prevZ + (p.z - p.prevZ) * alpha
213
+
214
+ p.mesh.position.set(displayX, displayY, displayZ)
215
+ // lookAt operates in parent (scene) coords — use scene-local camera pos
216
+ p.mesh.lookAt(cameraPosScene.x, cameraPosScene.y, cameraPosScene.z)
217
+ }
218
+ }
219
+
220
+ spawnCrackParticle(worldX: number, worldY: number, worldZ: number, face: number, blockName: string, floorMap: number[], biomeName = 'plains'): void {
221
+ if (!this.enabled) return
222
+
223
+ const texInfo = this.resolveBlockTexture(blockName)
224
+ if (!texInfo) return
225
+
226
+ const tintColor = resolveTintColor(blockName, biomeName)
227
+
228
+ // Random position within block, inset 0.1 on each axis
229
+ let px = worldX + Math.random() * 0.8 + 0.1
230
+ let py = worldY + Math.random() * 0.8 + 0.1
231
+ let pz = worldZ + Math.random() * 0.8 + 0.1
232
+
233
+ // Override position on the hit face axis to be at face + 0.1 offset outward
234
+ switch (face) {
235
+ case 0: py = worldY - 0.1; break
236
+ case 1: py = worldY + 1.0 + 0.1; break
237
+ case 2: pz = worldZ - 0.1; break
238
+ case 3: pz = worldZ + 1.0 + 0.1; break
239
+ case 4: px = worldX - 0.1; break
240
+ case 5: px = worldX + 1.0 + 0.1; break
241
+ }
242
+
243
+ // Small random velocity, heavily damped
244
+ const xd = (Math.random() * 2 - 1) * 0.4 * 0.2
245
+ const yd = (Math.random() * 2 - 1) * 0.4 * 0.2 + 0.1 * 0.2
246
+ const zd = (Math.random() * 2 - 1) * 0.4 * 0.2
247
+
248
+ const maxAge = Math.floor(4 / (Math.random() * 0.9 + 0.1))
249
+
250
+ this.createParticle(px, py, pz, xd, yd, zd, maxAge, texInfo, floorMap, worldX, worldZ, 0.6, tintColor)
251
+ }
252
+
253
+ private createParticle(
254
+ px: number, py: number, pz: number,
255
+ xd: number, yd: number, zd: number,
256
+ maxAge: number,
257
+ texInfo: { u: number; v: number; su: number; sv: number },
258
+ floorMap: number[],
259
+ blockX: number, blockZ: number,
260
+ scaleFactor = 1.0,
261
+ tintColor: [number, number, number] = [1, 1, 1]
262
+ ): void {
263
+ this.ensureMaterial()
264
+
265
+ let particle = this.findInactiveParticle()
266
+
267
+ if (!particle) {
268
+ if (this.particles.length < MAX_PARTICLES) {
269
+ particle = this.allocateParticle()
270
+ } else {
271
+ particle = this.recycleOldest()
272
+ }
273
+ }
274
+
275
+ const randomU = Math.floor(Math.random() * 4)
276
+ const randomV = Math.floor(Math.random() * 4)
277
+ const particleU = texInfo.u + (randomU / 4) * texInfo.su
278
+ const particleV = texInfo.v + (randomV / 4) * texInfo.sv
279
+ const particleSU = texInfo.su / 4
280
+ const particleSV = texInfo.sv / 4
281
+
282
+ this.setGeometryUVs(particle.mesh.geometry as THREE.PlaneGeometry, particleU, particleV, particleSU, particleSV)
283
+
284
+ particle.active = true
285
+ particle.x = px; particle.y = py; particle.z = pz
286
+ particle.prevX = px; particle.prevY = py; particle.prevZ = pz
287
+ particle.xd = xd; particle.yd = yd; particle.zd = zd
288
+ particle.age = 0
289
+ particle.maxAge = maxAge
290
+ particle.onGround = false
291
+ particle.floorMap = floorMap
292
+ particle.blockX = Math.floor(blockX)
293
+ particle.blockZ = Math.floor(blockZ)
294
+
295
+ const scale = 0.1 * (0.5 + Math.random() * 0.5) * 2 * scaleFactor
296
+ particle.mesh.scale.set(scale, scale, scale)
297
+ particle.mesh.position.set(px, py, pz)
298
+ particle.mesh.visible = true
299
+
300
+ // Apply tint: base darkening 0.6 × tint color
301
+ const r = 0.6 * tintColor[0]
302
+ const g = 0.6 * tintColor[1]
303
+ const b = 0.6 * tintColor[2]
304
+ const colorArray = new Float32Array([r, g, b, r, g, b, r, g, b, r, g, b])
305
+ const colorAttr = particle.mesh.geometry.getAttribute('color') as THREE.BufferAttribute
306
+ if (colorAttr) {
307
+ colorAttr.set(colorArray)
308
+ colorAttr.needsUpdate = true
309
+ } else {
310
+ particle.mesh.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3))
311
+ }
312
+
313
+ this.worldRenderer.sceneOrigin.addAndTrack(particle.mesh)
314
+ }
315
+
316
+ private allocateParticle(): BreakParticle {
317
+ const geometry = new THREE.PlaneGeometry(1, 1)
318
+ const mesh = new THREE.Mesh(geometry, this.sharedMaterial!)
319
+ mesh.visible = false
320
+
321
+ const particle: BreakParticle = {
322
+ mesh,
323
+ active: false,
324
+ x: 0, y: 0, z: 0,
325
+ prevX: 0, prevY: 0, prevZ: 0,
326
+ xd: 0, yd: 0, zd: 0,
327
+ age: 0,
328
+ maxAge: 0,
329
+ onGround: false,
330
+ floorMap: [],
331
+ blockX: 0,
332
+ blockZ: 0,
333
+ }
334
+
335
+ this.particles.push(particle)
336
+ return particle
337
+ }
338
+
339
+ private findInactiveParticle(): BreakParticle | undefined {
340
+ for (let i = 0; i < this.particles.length; i++) {
341
+ const idx = (this.nextParticleIndex + i) % this.particles.length
342
+ if (!this.particles[idx].active) {
343
+ this.nextParticleIndex = (idx + 1) % this.particles.length
344
+ return this.particles[idx]
345
+ }
346
+ }
347
+ return undefined
348
+ }
349
+
350
+ private recycleOldest(): BreakParticle {
351
+ let oldest: BreakParticle = this.particles[0]
352
+ for (const p of this.particles) {
353
+ if (p.age > oldest.age) {
354
+ oldest = p
355
+ }
356
+ }
357
+ this.deactivateParticle(oldest)
358
+ return oldest
359
+ }
360
+
361
+ private deactivateParticle(p: BreakParticle): void {
362
+ if (!p.active) return
363
+ p.active = false
364
+ p.mesh.visible = false
365
+ this.worldRenderer.sceneOrigin.removeAndUntrack(p.mesh)
366
+ }
367
+
368
+ private getFloorY(particle: BreakParticle): number {
369
+ let dx = Math.floor(particle.x) - particle.blockX
370
+ let dz = Math.floor(particle.z) - particle.blockZ
371
+ dx = Math.max(-2, Math.min(2, dx))
372
+ dz = Math.max(-2, Math.min(2, dz))
373
+ return particle.floorMap[(dz + 2) * 5 + (dx + 2)]
374
+ }
375
+
376
+ private resolveBlockTexture(blockName: string): { u: number; v: number; su: number; sv: number } | null {
377
+ const resources = this.worldRenderer.resourcesManager.currentResources
378
+ if (!resources) return null
379
+
380
+ const atlasJson = resources.blocksAtlasJson
381
+ const textures = atlasJson.textures
382
+
383
+ if (textures[blockName]) return this.extractUV(textures[blockName], atlasJson)
384
+
385
+ for (const suffix of ['_side', '_top', '_front', '_0', '']) {
386
+ const key = blockName + suffix
387
+ if (textures[key]) return this.extractUV(textures[key], atlasJson)
388
+ }
389
+
390
+ for (const key of Object.keys(textures)) {
391
+ if (key.startsWith(blockName)) return this.extractUV(textures[key], atlasJson)
392
+ }
393
+
394
+ return null
395
+ }
396
+
397
+ private extractUV(
398
+ texInfo: { u: number; v: number; su?: number; sv?: number },
399
+ atlasJson: { suSv: number }
400
+ ): { u: number; v: number; su: number; sv: number } {
401
+ return {
402
+ u: texInfo.u,
403
+ v: texInfo.v,
404
+ su: texInfo.su ?? atlasJson.suSv,
405
+ sv: texInfo.sv ?? atlasJson.suSv,
406
+ }
407
+ }
408
+
409
+ private setGeometryUVs(geometry: THREE.PlaneGeometry, u: number, v: number, su: number, sv: number): void {
410
+ const uvAttr = geometry.getAttribute('uv') as THREE.BufferAttribute
411
+ // PlaneGeometry UV layout: (0,1) (1,1) (0,0) (1,0)
412
+ uvAttr.setXY(0, u, v)
413
+ uvAttr.setXY(1, u + su, v)
414
+ uvAttr.setXY(2, u, v + sv)
415
+ uvAttr.setXY(3, u + su, v + sv)
416
+ uvAttr.needsUpdate = true
417
+ }
418
+
419
+ private ensureMaterial(): void {
420
+ if (this.sharedMaterial) return
421
+ const atlasTexture = this.worldRenderer.material.map
422
+ if (!atlasTexture) return
423
+ this.sharedMaterial = new THREE.MeshBasicMaterial({
424
+ map: atlasTexture,
425
+ vertexColors: true,
426
+ transparent: true,
427
+ alphaTest: 0.1,
428
+ })
429
+ }
430
+ }
431
+
432
+ export const blockBreakParticlesManifest: RendererModuleManifest = {
433
+ id: 'blockBreakParticles',
434
+ controller: BlockBreakParticlesModule,
435
+ enabledDefault: true,
436
+ cannotBeDisabled: true,
437
+ requiresHeightmap: false,
438
+ }
@@ -1,4 +1,5 @@
1
1
  //@ts-nocheck
2
+ import { blockBreakParticlesManifest } from './blockBreakParticles'
2
3
  import { cameraBobbingManifest } from './cameraBobbing'
3
4
  import { rainManifest } from './rain'
4
5
  import { sciFiWorldRevealManifest } from './sciFiWorldReveal'
@@ -9,4 +10,5 @@ export const BUILTIN_MODULES = {
9
10
  futuristicReveal: sciFiWorldRevealManifest,
10
11
  rain: rainManifest,
11
12
  cameraBobbing: cameraBobbingManifest,
13
+ blockBreakParticles: blockBreakParticlesManifest,
12
14
  }
@@ -58,11 +58,9 @@ export class SciFiWorldRevealModule implements RendererModuleController {
58
58
  // Store original methods for patching
59
59
  private originalFinishChunk: ((chunkKey: string) => void) | null = null
60
60
  private originalDestroy: (() => void) | null = null
61
- private originalSceneAdd: ((...object: THREE.Object3D[]) => THREE.Scene) | null = null
61
+ private originalSceneAdd: ((...object: THREE.Object3D[]) => THREE.Group) | null = null
62
62
  private originalHandleWorkerMessage: ((data: { geometry: MesherGeometryOutput; key: string; type: string }) => void) | null = null
63
63
 
64
- private originalWbgHandle: ((data: any) => void) | null = null
65
-
66
64
  private configEnabled = true
67
65
 
68
66
  constructor(private readonly worldRenderer: WorldRendererThree) {
@@ -146,12 +144,10 @@ export class SciFiWorldRevealModule implements RendererModuleController {
146
144
  this.originalDestroy!()
147
145
  }
148
146
 
149
- // Patch handleWorkerMessage
150
- const wbg = wr.worldBlockGeometry
151
- this.originalWbgHandle = wbg.handleWorkerGeometryMessage.bind(wbg)
152
-
153
- wbg.handleWorkerGeometryMessage = (data: any) => {
154
- const result = this.originalWbgHandle!(data)
147
+ // Patch handleWorkerMessage to intercept geometry
148
+ this.originalHandleWorkerMessage = wr.handleWorkerMessage.bind(wr)
149
+ wr.handleWorkerMessage = (data: any) => {
150
+ this.originalHandleWorkerMessage!(data)
155
151
 
156
152
  if (this.enabled && data?.type === 'geometry') {
157
153
  Promise.resolve().then(() => {
@@ -162,14 +158,12 @@ export class SciFiWorldRevealModule implements RendererModuleController {
162
158
  }
163
159
  })
164
160
  }
165
-
166
- return result
167
161
  }
168
162
 
169
163
 
170
164
  // Patch scene.add to intercept mesh additions
171
165
  this.originalSceneAdd = wr.scene.add.bind(wr.scene)
172
- wr.scene.add = (...objects: THREE.Object3D[]): THREE.Scene => {
166
+ wr.scene.add = (...objects: THREE.Object3D[]): THREE.Group => {
173
167
  // Call original add first
174
168
  const result = this.originalSceneAdd!(...objects)
175
169
 
@@ -208,11 +202,6 @@ export class SciFiWorldRevealModule implements RendererModuleController {
208
202
  this.originalSceneAdd = null
209
203
  }
210
204
 
211
- if (this.originalWbgHandle) {
212
- wr.worldBlockGeometry.handleWorkerGeometryMessage = this.originalWbgHandle as any
213
- this.originalWbgHandle = null
214
- }
215
-
216
205
  if (this.onWorldSwitchedCb) {
217
206
  const i = wr.onWorldSwitched.indexOf(this.onWorldSwitchedCb)
218
207
  if (i !== -1) wr.onWorldSwitched.splice(i, 1)
@@ -249,7 +238,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
249
238
  let current: THREE.Object3D | null = mesh
250
239
  while (current) {
251
240
  const { sectionKey } = (current as any)
252
- if (sectionKey && this.worldRenderer.worldBlockGeometry.sectionObjects[sectionKey] === current) {
241
+ if (sectionKey && this.worldRenderer.chunkMeshManager.sectionObjects[sectionKey] === current) {
253
242
  return sectionKey
254
243
  }
255
244
  current = current.parent
@@ -270,7 +259,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
270
259
  const derivedKey = `${sectionX},${sectionY},${sectionZ}`
271
260
 
272
261
  // Verify this key exists in sectionObjects
273
- if (this.worldRenderer.worldBlockGeometry.sectionObjects[derivedKey]) {
262
+ if (this.worldRenderer.chunkMeshManager.sectionObjects[derivedKey]) {
274
263
  return derivedKey
275
264
  }
276
265
 
@@ -281,7 +270,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
281
270
  * Get the scene from world renderer
282
271
  */
283
272
  private get scene(): THREE.Scene {
284
- return this.worldRenderer.scene
273
+ return this.worldRenderer.realScene
285
274
  }
286
275
 
287
276
  /**
@@ -295,7 +284,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
295
284
  * Get original mesh for a section key
296
285
  */
297
286
  private getOriginalMesh(key: string): THREE.Mesh | null {
298
- const sectionObject = this.worldRenderer.worldBlockGeometry.sectionObjects[key]
287
+ const sectionObject = this.worldRenderer.chunkMeshManager.sectionObjects[key]
299
288
  if (!sectionObject) return null
300
289
  return sectionObject.children.find(child => child.name === 'mesh') as THREE.Mesh | null
301
290
  }
@@ -214,7 +214,7 @@ export class PanoramaRenderer {
214
214
  }
215
215
  )
216
216
  if (this.worldRenderer instanceof WorldRendererThree) {
217
- this.scene = this.worldRenderer.scene
217
+ this.scene = this.worldRenderer.realScene
218
218
  }
219
219
  void worldView.init(initPos)
220
220