minecraft-renderer 0.1.37 → 0.1.38
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/package.json
CHANGED
|
@@ -28,17 +28,46 @@ export interface SectionObject extends THREE.Group {
|
|
|
28
28
|
headsContainer?: THREE.Group
|
|
29
29
|
bannersContainer?: THREE.Group
|
|
30
30
|
boxHelper?: THREE.BoxHelper
|
|
31
|
+
/**
|
|
32
|
+
* World-space coordinates of the section origin. Cached so that
|
|
33
|
+
* {@link ChunkMeshManager.updateBoxHelper} can position lazily-created
|
|
34
|
+
* border helpers correctly under camera-relative rendering, where
|
|
35
|
+
* `mesh.position` is proxied to (world - sceneOrigin) and cannot be
|
|
36
|
+
* reused directly for objects that are tracked separately.
|
|
37
|
+
*/
|
|
38
|
+
worldX?: number
|
|
39
|
+
worldY?: number
|
|
40
|
+
worldZ?: number
|
|
31
41
|
foutain?: boolean
|
|
42
|
+
/**
|
|
43
|
+
* True while the section is held invisible by the "Batch Chunks Display"
|
|
44
|
+
* (`_renderByChunks`) feature, waiting for the parent chunk to finish meshing
|
|
45
|
+
* before being shown together with the rest of the chunk.
|
|
46
|
+
*/
|
|
47
|
+
_waitingForChunkDisplay?: boolean
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
export class ChunkMeshManager {
|
|
35
51
|
private readonly meshPool: ChunkMeshPool[] = []
|
|
36
52
|
private readonly activeSections = new Map<string, ChunkMeshPool>()
|
|
37
53
|
readonly sectionObjects: Record<string, SectionObject> = {}
|
|
54
|
+
/**
|
|
55
|
+
* Sections kept invisible because the "Batch Chunks Display" option is on
|
|
56
|
+
* and their parent chunk hasn't finished meshing yet. Keyed by chunk key
|
|
57
|
+
* (`x,z`); flushed by `WorldRendererThree.finishChunk(chunkKey)`.
|
|
58
|
+
*/
|
|
59
|
+
readonly waitingChunksToDisplay: Record<string, string[]> = {}
|
|
38
60
|
private poolSize!: number
|
|
39
61
|
private maxPoolSize!: number
|
|
40
62
|
private minPoolSize!: number
|
|
41
63
|
private readonly signHeadsRenderer: SignHeadsRenderer
|
|
64
|
+
/**
|
|
65
|
+
* Shared transparent material used as the basis for the wireframe chunk
|
|
66
|
+
* border `BoxHelper` created lazily in {@link updateBoxHelper}. Kept on the
|
|
67
|
+
* manager so the BoxHelper machinery doesn't allocate a new material per
|
|
68
|
+
* section.
|
|
69
|
+
*/
|
|
70
|
+
private readonly chunkBoxMaterial = new THREE.MeshBasicMaterial({ color: 0x00_00_00, transparent: true, opacity: 0 })
|
|
42
71
|
|
|
43
72
|
// Performance tracking
|
|
44
73
|
private hits = 0
|
|
@@ -156,6 +185,17 @@ export class ChunkMeshManager {
|
|
|
156
185
|
// Store metadata
|
|
157
186
|
sectionObject.tilesCount = geometryData.positions.length / 3 / 4
|
|
158
187
|
sectionObject.blocksCount = geometryData.blocksCount
|
|
188
|
+
sectionObject.worldX = geometryData.sx
|
|
189
|
+
sectionObject.worldY = geometryData.sy
|
|
190
|
+
sectionObject.worldZ = geometryData.sz
|
|
191
|
+
// Stamp the section key so modules (e.g. sciFiWorldReveal) can resolve
|
|
192
|
+
// mesh -> section without falling back to sceneOrigin world-position math.
|
|
193
|
+
;(sectionObject as any).sectionKey = sectionKey
|
|
194
|
+
// Tag the group so `WorldRendererThree.getThirdPersonCamera` raycast can
|
|
195
|
+
// still find chunk meshes — the old `WorldBlockGeometry` set this name
|
|
196
|
+
// unconditionally; the pooling port lost that and only the border-helper
|
|
197
|
+
// path used to restore it.
|
|
198
|
+
sectionObject.name = 'chunk'
|
|
159
199
|
|
|
160
200
|
try {
|
|
161
201
|
// Add signs container
|
|
@@ -218,13 +258,64 @@ export class ChunkMeshManager {
|
|
|
218
258
|
this.scene.add(sectionObject)
|
|
219
259
|
sectionObject.matrixAutoUpdate = false
|
|
220
260
|
|
|
261
|
+
// Create chunk border helper eagerly when the option is on so freshly
|
|
262
|
+
// streamed sections immediately get the F3+G yellow wireframe instead of
|
|
263
|
+
// appearing only on the next toggle.
|
|
264
|
+
if (this.worldRenderer.displayOptions?.inWorldRenderingConfig?.showChunkBorders) {
|
|
265
|
+
this.updateBoxHelper(sectionKey, true)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Honor "Batch Chunks Display" (`_renderByChunks`): keep this section's
|
|
269
|
+
// mesh hidden until the whole chunk has finished meshing, so users see a
|
|
270
|
+
// chunk appear as a single 16xHx16 tile instead of streaming per-section.
|
|
271
|
+
// Updates to chunks that are already finished bypass batching to avoid
|
|
272
|
+
// flickering on block changes / lighting updates.
|
|
273
|
+
const chunkCoords = sectionKey.split(',')
|
|
274
|
+
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
|
275
|
+
const renderByChunks = !!this.worldRenderer.displayOptions
|
|
276
|
+
?.inWorldRenderingConfig?._renderByChunks
|
|
277
|
+
if (renderByChunks && !this.worldRenderer.finishedChunks[chunkKey]) {
|
|
278
|
+
sectionObject.visible = false
|
|
279
|
+
sectionObject._waitingForChunkDisplay = true
|
|
280
|
+
const list = this.waitingChunksToDisplay[chunkKey] ?? (this.waitingChunksToDisplay[chunkKey] = [])
|
|
281
|
+
if (!list.includes(sectionKey)) list.push(sectionKey)
|
|
282
|
+
}
|
|
283
|
+
|
|
221
284
|
return sectionObject
|
|
222
285
|
}
|
|
223
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Reveal all sections of a chunk that were held invisible by the
|
|
289
|
+
* "Batch Chunks Display" option. Called from `WorldRendererThree.finishChunk`.
|
|
290
|
+
*/
|
|
291
|
+
finishChunkDisplay (chunkKey: string): void {
|
|
292
|
+
const sectionKeys = this.waitingChunksToDisplay[chunkKey]
|
|
293
|
+
if (!sectionKeys) return
|
|
294
|
+
for (const sectionKey of sectionKeys) {
|
|
295
|
+
const sectionObject = this.sectionObjects[sectionKey]
|
|
296
|
+
if (!sectionObject) continue
|
|
297
|
+
sectionObject._waitingForChunkDisplay = false
|
|
298
|
+
sectionObject.visible = true
|
|
299
|
+
}
|
|
300
|
+
delete this.waitingChunksToDisplay[chunkKey]
|
|
301
|
+
}
|
|
302
|
+
|
|
224
303
|
cleanupSection (sectionKey: string) {
|
|
225
304
|
// Remove section object from scene
|
|
226
305
|
const sectionObject = this.sectionObjects[sectionKey]
|
|
227
306
|
if (sectionObject) {
|
|
307
|
+
// Drop from any pending "batch display" queue so we don't try to flip
|
|
308
|
+
// visibility on a stale (released) object later.
|
|
309
|
+
if (sectionObject._waitingForChunkDisplay) {
|
|
310
|
+
const chunkCoords = sectionKey.split(',')
|
|
311
|
+
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
|
312
|
+
const list = this.waitingChunksToDisplay[chunkKey]
|
|
313
|
+
if (list) {
|
|
314
|
+
const idx = list.indexOf(sectionKey)
|
|
315
|
+
if (idx !== -1) list.splice(idx, 1)
|
|
316
|
+
if (list.length === 0) delete this.waitingChunksToDisplay[chunkKey]
|
|
317
|
+
}
|
|
318
|
+
}
|
|
228
319
|
// Cleanup banner textures before disposing
|
|
229
320
|
if (sectionObject.bannersContainer) {
|
|
230
321
|
sectionObject.bannersContainer.traverse((child) => {
|
|
@@ -243,6 +334,21 @@ export class ChunkMeshManager {
|
|
|
243
334
|
}
|
|
244
335
|
this.worldRenderer.sceneOrigin.removeAndUntrackAll(sectionObject)
|
|
245
336
|
this.scene.remove(sectionObject)
|
|
337
|
+
// boxHelper lives directly on the scene (so it stays world-anchored
|
|
338
|
+
// under camera-relative rendering), so it must be cleaned up explicitly
|
|
339
|
+
// — `removeAndUntrackAll` above only walks `sectionObject` descendants.
|
|
340
|
+
if (sectionObject.boxHelper) {
|
|
341
|
+
this.worldRenderer.sceneOrigin.removeAndUntrack(sectionObject.boxHelper)
|
|
342
|
+
this.scene.remove(sectionObject.boxHelper)
|
|
343
|
+
sectionObject.boxHelper.geometry.dispose()
|
|
344
|
+
const helperMat = sectionObject.boxHelper.material as THREE.Material | THREE.Material[]
|
|
345
|
+
if (Array.isArray(helperMat)) {
|
|
346
|
+
for (const m of helperMat) m.dispose()
|
|
347
|
+
} else {
|
|
348
|
+
helperMat.dispose()
|
|
349
|
+
}
|
|
350
|
+
sectionObject.boxHelper = undefined
|
|
351
|
+
}
|
|
246
352
|
delete this.sectionObjects[sectionKey]
|
|
247
353
|
}
|
|
248
354
|
}
|
|
@@ -285,19 +391,28 @@ export class ChunkMeshManager {
|
|
|
285
391
|
/**
|
|
286
392
|
* Update box helper for a section
|
|
287
393
|
*/
|
|
288
|
-
updateBoxHelper (sectionKey: string, showChunkBorders: boolean, chunkBoxMaterial: THREE.Material) {
|
|
394
|
+
updateBoxHelper (sectionKey: string, showChunkBorders: boolean, chunkBoxMaterial: THREE.Material = this.chunkBoxMaterial) {
|
|
289
395
|
const sectionObject = this.sectionObjects[sectionKey]
|
|
290
396
|
if (!sectionObject?.mesh) return
|
|
291
397
|
|
|
292
398
|
if (showChunkBorders) {
|
|
293
399
|
if (!sectionObject.boxHelper) {
|
|
294
|
-
// mesh
|
|
400
|
+
// Build a 16x16x16 reference mesh in world coordinates so BoxHelper's
|
|
401
|
+
// `setFromObject` produces the correct geometry. The reference mesh is
|
|
402
|
+
// not added to the scene; only the resulting BoxHelper is.
|
|
295
403
|
const staticChunkMesh = new THREE.Mesh(new THREE.BoxGeometry(16, 16, 16), chunkBoxMaterial)
|
|
296
|
-
staticChunkMesh.position.copy(sectionObject.mesh.position)
|
|
297
404
|
const boxHelper = new THREE.BoxHelper(staticChunkMesh, 0xff_ff_00)
|
|
298
405
|
boxHelper.name = 'helper'
|
|
299
|
-
|
|
300
|
-
|
|
406
|
+
// Add directly to the scene and track it through sceneOrigin so that
|
|
407
|
+
// camera-relative rendering (floating origin) keeps the helper pinned
|
|
408
|
+
// to its world coordinates instead of following the camera.
|
|
409
|
+
const sx = sectionObject.worldX ?? 0
|
|
410
|
+
const sy = sectionObject.worldY ?? 0
|
|
411
|
+
const sz = sectionObject.worldZ ?? 0
|
|
412
|
+
this.worldRenderer.sceneOrigin.track(boxHelper, { updateMatrix: true })
|
|
413
|
+
boxHelper.position.set(sx, sy, sz)
|
|
414
|
+
boxHelper.updateMatrix()
|
|
415
|
+
this.scene.add(boxHelper)
|
|
301
416
|
sectionObject.boxHelper = boxHelper
|
|
302
417
|
}
|
|
303
418
|
sectionObject.boxHelper.visible = true
|
|
@@ -306,6 +421,28 @@ export class ChunkMeshManager {
|
|
|
306
421
|
}
|
|
307
422
|
}
|
|
308
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Create / toggle chunk border helpers for every active section. Used by
|
|
426
|
+
* `WorldRendererThree.updateShowChunksBorder` so the F3+G hotkey works
|
|
427
|
+
* after the move from `WorldBlockGeometry` (which created the helpers
|
|
428
|
+
* eagerly per section) to the pooled `ChunkMeshManager`.
|
|
429
|
+
*/
|
|
430
|
+
updateAllBoxHelpers (showChunkBorders: boolean) {
|
|
431
|
+
for (const sectionKey of Object.keys(this.sectionObjects)) {
|
|
432
|
+
this.updateBoxHelper(sectionKey, showChunkBorders)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Forward to {@link SignHeadsRenderer.cleanChunkTextures} so callers in
|
|
438
|
+
* `WorldRendererThree` (which historically owned the sign-texture cache)
|
|
439
|
+
* can invalidate cached sign textures when a section is marked dirty,
|
|
440
|
+
* without reaching into the manager's private members.
|
|
441
|
+
*/
|
|
442
|
+
cleanSignChunkTextures (x: number, z: number) {
|
|
443
|
+
this.signHeadsRenderer.cleanChunkTextures(x, z)
|
|
444
|
+
}
|
|
445
|
+
|
|
309
446
|
/**
|
|
310
447
|
* Get mesh for section if it exists
|
|
311
448
|
*/
|
|
@@ -459,6 +596,7 @@ export class ChunkMeshManager {
|
|
|
459
596
|
|
|
460
597
|
this.meshPool.length = 0
|
|
461
598
|
this.activeSections.clear()
|
|
599
|
+
this.chunkBoxMaterial.dispose()
|
|
462
600
|
}
|
|
463
601
|
|
|
464
602
|
// Private helper methods
|
|
@@ -667,6 +805,12 @@ export class ChunkMeshManager {
|
|
|
667
805
|
updateSectionsVisibility (): void {
|
|
668
806
|
const cameraPos = this.worldRenderer.cameraSectionPos
|
|
669
807
|
for (const [sectionKey, sectionObject] of Object.entries(this.sectionObjects)) {
|
|
808
|
+
// Don't override "Batch Chunks Display" hiding — those sections must
|
|
809
|
+
// stay invisible until their chunk finishes meshing.
|
|
810
|
+
if (sectionObject._waitingForChunkDisplay) {
|
|
811
|
+
sectionObject.visible = false
|
|
812
|
+
continue
|
|
813
|
+
}
|
|
670
814
|
if (!this.performanceOverrideDistance) {
|
|
671
815
|
sectionObject.visible = true
|
|
672
816
|
continue
|
|
@@ -805,4 +949,21 @@ class SignHeadsRenderer {
|
|
|
805
949
|
textures[texturekey] = tex
|
|
806
950
|
return tex
|
|
807
951
|
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Dispose all cached sign textures for the chunk containing world coords
|
|
955
|
+
* (x, z). Called from `WorldRendererThree.cleanChunkTextures` so that
|
|
956
|
+
* re-meshes triggered by `setSectionDirty` (e.g. a player edits a sign)
|
|
957
|
+
* pick up fresh block-entity NBT instead of returning the stale cached
|
|
958
|
+
* texture from {@link SignHeadsRenderer.getSignTexture}.
|
|
959
|
+
*/
|
|
960
|
+
cleanChunkTextures (x: number, z: number) {
|
|
961
|
+
const key = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
|
|
962
|
+
const textures = this.chunkTextures.get(key)
|
|
963
|
+
if (!textures) return
|
|
964
|
+
for (const k of Object.keys(textures)) {
|
|
965
|
+
textures[k].dispose()
|
|
966
|
+
delete textures[k]
|
|
967
|
+
}
|
|
968
|
+
}
|
|
808
969
|
}
|
|
@@ -67,7 +67,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
67
67
|
cameraContainer!: THREE.Object3D
|
|
68
68
|
media: ThreeJsMedia
|
|
69
69
|
get waitingChunksToDisplay() {
|
|
70
|
-
return
|
|
70
|
+
return this.chunkMeshManager.waitingChunksToDisplay
|
|
71
71
|
}
|
|
72
72
|
waypoints: WaypointsRenderer
|
|
73
73
|
cinimaticScript: CinimaticScriptRunner
|
|
@@ -82,7 +82,14 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
82
82
|
camera!: THREE.PerspectiveCamera
|
|
83
83
|
renderTimeAvg = 0
|
|
84
84
|
private pendingSectionUpdates = new Map<string, { geometry: MesherGeometryOutput, key: string, type: string }>()
|
|
85
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Per-section buffering timestamps for `applyPendingSectionUpdates`.
|
|
87
|
+
* Each section gets its own deadline so a continuous stream of updates
|
|
88
|
+
* (e.g. server-side block changes from explosions, pistons, fluid ticks)
|
|
89
|
+
* does not flush freshly added sections together with stale ones via a
|
|
90
|
+
* single global timer.
|
|
91
|
+
*/
|
|
92
|
+
private pendingSectionBufferStartTimes = new Map<string, number>()
|
|
86
93
|
private static readonly MAX_SECTION_UPDATE_BUFFER_MS = 500
|
|
87
94
|
// Memory usage tracking (in bytes)
|
|
88
95
|
get estimatedMemoryUsage() {
|
|
@@ -747,42 +754,57 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
747
754
|
}
|
|
748
755
|
|
|
749
756
|
finishChunk(chunkKey: string) {
|
|
750
|
-
//
|
|
757
|
+
// Reveal all sections of this chunk that were held invisible by the
|
|
758
|
+
// "Batch Chunks Display" (`_renderByChunks`) option. No-op when the
|
|
759
|
+
// option is off — `waitingChunksToDisplay` is empty in that case.
|
|
760
|
+
this.chunkMeshManager.finishChunkDisplay(chunkKey)
|
|
751
761
|
}
|
|
752
762
|
|
|
753
763
|
private applyPendingSectionUpdates() {
|
|
754
764
|
if (this.pendingSectionUpdates.size === 0) return
|
|
755
765
|
|
|
756
766
|
const now = performance.now()
|
|
757
|
-
const
|
|
767
|
+
const sectionHeight = this.getSectionHeight()
|
|
768
|
+
const ready: string[] = []
|
|
769
|
+
|
|
770
|
+
for (const key of this.pendingSectionUpdates.keys()) {
|
|
771
|
+
const startedAt = this.pendingSectionBufferStartTimes.get(key) ?? now
|
|
772
|
+
const sinceFirst = now - startedAt
|
|
758
773
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
774
|
+
if (sinceFirst < WorldRendererThree.MAX_SECTION_UPDATE_BUFFER_MS) {
|
|
775
|
+
// Still within this section's grace window — wait if any neighbor is
|
|
776
|
+
// currently being re-meshed so we don't briefly expose a hole between
|
|
777
|
+
// the just-updated section and a stale neighbor (sky-flicker bug).
|
|
762
778
|
const [sx, sy, sz] = key.split(',').map(Number)
|
|
763
779
|
const neighborKeys = [
|
|
764
780
|
`${sx - 16},${sy},${sz}`, `${sx + 16},${sy},${sz}`,
|
|
765
781
|
`${sx},${sy - sectionHeight},${sz}`, `${sx},${sy + sectionHeight},${sz}`,
|
|
766
782
|
`${sx},${sy},${sz - 16}`, `${sx},${sy},${sz + 16}`,
|
|
767
783
|
]
|
|
768
|
-
|
|
784
|
+
let neighborBusy = false
|
|
769
785
|
for (const neighborKey of neighborKeys) {
|
|
770
786
|
if (
|
|
771
787
|
this.sectionsWaiting.has(neighborKey) &&
|
|
772
788
|
!this.pendingSectionUpdates.has(neighborKey) &&
|
|
773
789
|
this.sectionObjects[neighborKey]
|
|
774
790
|
) {
|
|
775
|
-
|
|
791
|
+
neighborBusy = true
|
|
792
|
+
break
|
|
776
793
|
}
|
|
777
794
|
}
|
|
795
|
+
if (neighborBusy) continue
|
|
778
796
|
}
|
|
797
|
+
|
|
798
|
+
ready.push(key)
|
|
779
799
|
}
|
|
780
800
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
801
|
+
if (ready.length === 0) return
|
|
802
|
+
|
|
803
|
+
for (const key of ready) {
|
|
804
|
+
const update = this.pendingSectionUpdates.get(key)!
|
|
805
|
+
this.pendingSectionUpdates.delete(key)
|
|
806
|
+
this.pendingSectionBufferStartTimes.delete(key)
|
|
784
807
|
|
|
785
|
-
for (const update of updates) {
|
|
786
808
|
const chunkCoords = update.key.split(',')
|
|
787
809
|
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
|
788
810
|
|
|
@@ -805,12 +827,9 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
805
827
|
for (const key of [...this.pendingSectionUpdates.keys()]) {
|
|
806
828
|
if (key.startsWith(`${x},`) && key.endsWith(`,${z}`)) {
|
|
807
829
|
this.pendingSectionUpdates.delete(key)
|
|
830
|
+
this.pendingSectionBufferStartTimes.delete(key)
|
|
808
831
|
}
|
|
809
832
|
}
|
|
810
|
-
|
|
811
|
-
if (this.pendingSectionUpdates.size === 0) {
|
|
812
|
-
this.pendingSectionBufferStartTime = null
|
|
813
|
-
}
|
|
814
833
|
}
|
|
815
834
|
|
|
816
835
|
handleWorkerMessage(data: { geometry: MesherGeometryOutput, key, type }): void {
|
|
@@ -819,15 +838,17 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
819
838
|
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
|
820
839
|
if (!this.loadedChunks[chunkKey] || !this.active) {
|
|
821
840
|
this.pendingSectionUpdates.delete(data.key)
|
|
822
|
-
|
|
823
|
-
this.pendingSectionBufferStartTime = null
|
|
824
|
-
}
|
|
841
|
+
this.pendingSectionBufferStartTimes.delete(data.key)
|
|
825
842
|
return
|
|
826
843
|
}
|
|
827
844
|
|
|
828
845
|
if (this.sectionObjects[data.key]) {
|
|
829
846
|
this.pendingSectionUpdates.set(data.key, data)
|
|
830
|
-
|
|
847
|
+
// Per-section deadline: only set if we don't already have one, so
|
|
848
|
+
// repeated updates to the same section don't postpone its flush.
|
|
849
|
+
if (!this.pendingSectionBufferStartTimes.has(data.key)) {
|
|
850
|
+
this.pendingSectionBufferStartTimes.set(data.key, performance.now())
|
|
851
|
+
}
|
|
831
852
|
return
|
|
832
853
|
}
|
|
833
854
|
|
|
@@ -1137,6 +1158,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1137
1158
|
chunksRenderDistanceOverride !== undefined
|
|
1138
1159
|
) {
|
|
1139
1160
|
for (const [key, object] of Object.entries(this.sectionObjects)) {
|
|
1161
|
+
if (object._waitingForChunkDisplay) continue
|
|
1140
1162
|
const [x, y, z] = key.split(',').map(Number)
|
|
1141
1163
|
const isVisible =
|
|
1142
1164
|
// eslint-disable-next-line no-constant-binary-expression, sonarjs/no-redundant-boolean
|
|
@@ -1149,9 +1171,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1149
1171
|
object.visible = isVisible
|
|
1150
1172
|
}
|
|
1151
1173
|
} else {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1174
|
+
// No debug visibility override active — defer to the manager so the
|
|
1175
|
+
// performance-based override distance (set by `recordRenderTime` /
|
|
1176
|
+
// `autoLowerRenderDistance`) is honored, instead of force-showing every
|
|
1177
|
+
// section every frame and clobbering it.
|
|
1178
|
+
this.chunkMeshManager.updateSectionsVisibility()
|
|
1155
1179
|
}
|
|
1156
1180
|
}
|
|
1157
1181
|
|
|
@@ -1322,20 +1346,16 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1322
1346
|
}
|
|
1323
1347
|
|
|
1324
1348
|
updateShowChunksBorder(value: boolean) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
child.visible = value
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1349
|
+
// Lazily create helpers on the first toggle (they are not created upfront
|
|
1350
|
+
// for sections streamed in while the option was off).
|
|
1351
|
+
this.chunkMeshManager.updateAllBoxHelpers(value)
|
|
1332
1352
|
}
|
|
1333
1353
|
|
|
1334
1354
|
resetWorld() {
|
|
1335
1355
|
super.resetWorld()
|
|
1336
1356
|
|
|
1337
1357
|
this.pendingSectionUpdates.clear()
|
|
1338
|
-
this.
|
|
1358
|
+
this.pendingSectionBufferStartTimes.clear()
|
|
1339
1359
|
this.chunkMeshManager.dispose()
|
|
1340
1360
|
this.chunkMeshManager = new ChunkMeshManager(this, this.scene, this.material, this.worldSizeParams.worldHeight, this.viewDistance)
|
|
1341
1361
|
|
|
@@ -1366,6 +1386,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1366
1386
|
textures[key].dispose()
|
|
1367
1387
|
delete textures[key]
|
|
1368
1388
|
}
|
|
1389
|
+
// Sign / head textures moved to ChunkMeshManager.signHeadsRenderer in PR
|
|
1390
|
+
// #16; without invalidating that cache here, sign edits (and any other
|
|
1391
|
+
// block-entity NBT change picked up via setSectionDirty) would re-render
|
|
1392
|
+
// with the stale cached canvas until a full world reset.
|
|
1393
|
+
this.chunkMeshManager.cleanSignChunkTextures(x, z)
|
|
1369
1394
|
}
|
|
1370
1395
|
|
|
1371
1396
|
readdChunks() {
|
|
@@ -1422,7 +1447,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1422
1447
|
|
|
1423
1448
|
destroy(): void {
|
|
1424
1449
|
this.pendingSectionUpdates.clear()
|
|
1425
|
-
this.
|
|
1450
|
+
this.pendingSectionBufferStartTimes.clear()
|
|
1426
1451
|
this.chunkMeshManager.dispose()
|
|
1427
1452
|
this.disposeModules()
|
|
1428
1453
|
this.fireworksLegacy.destroy()
|