working geometry pool manager!
This commit is contained in:
parent
0240a752ad
commit
9ee28ef62f
4 changed files with 389 additions and 70 deletions
|
|
@ -20,7 +20,7 @@ export type WorldDataEmitterEvents = {
|
|||
entityMoved: (data: any) => void
|
||||
playerEntity: (data: any) => void
|
||||
time: (data: number) => void
|
||||
renderDistance: (viewDistance: number) => void
|
||||
renderDistance: (viewDistance: number, keepChunksDistance: number) => void
|
||||
blockEntities: (data: Record<string, any> | { blockEntities: Record<string, any> }) => void
|
||||
markAsLoaded: (data: { x: number, z: number }) => void
|
||||
unloadChunk: (data: { x: number, z: number }) => void
|
||||
|
|
@ -79,7 +79,7 @@ export class WorldDataEmitter extends (EventEmitter as new () => TypedEmitter<Wo
|
|||
|
||||
updateViewDistance (viewDistance: number) {
|
||||
this.viewDistance = viewDistance
|
||||
this.emitter.emit('renderDistance', viewDistance)
|
||||
this.emitter.emit('renderDistance', viewDistance, this.keepChunksDistance)
|
||||
}
|
||||
|
||||
listenToBot (bot: typeof __type_bot) {
|
||||
|
|
|
|||
|
|
@ -487,7 +487,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
this.allChunksFinished = true
|
||||
this.allLoadedIn ??= Date.now() - this.initialChunkLoadWasStartedIn!
|
||||
}
|
||||
this.updateChunksStats()
|
||||
}
|
||||
|
||||
changeHandSwingingState (isAnimationPlaying: boolean, isLeftHand: boolean): void { }
|
||||
|
|
|
|||
298
renderer/viewer/three/chunkMeshManager.ts
Normal file
298
renderer/viewer/three/chunkMeshManager.ts
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
import * as THREE from 'three'
|
||||
import { MesherGeometryOutput } from '../lib/mesher/shared'
|
||||
|
||||
export interface ChunkMeshPool {
|
||||
mesh: THREE.Mesh
|
||||
inUse: boolean
|
||||
lastUsedTime: number
|
||||
sectionKey?: string
|
||||
}
|
||||
|
||||
export class ChunkMeshManager {
|
||||
private readonly meshPool: ChunkMeshPool[] = []
|
||||
private readonly activeSections = new Map<string, ChunkMeshPool>()
|
||||
private poolSize: number
|
||||
private maxPoolSize: number
|
||||
private minPoolSize: number
|
||||
|
||||
// Performance tracking
|
||||
private hits = 0
|
||||
private misses = 0
|
||||
|
||||
// Debug flag to bypass pooling
|
||||
public bypassPooling = false
|
||||
|
||||
constructor (
|
||||
public material: THREE.Material,
|
||||
public worldHeight: number,
|
||||
viewDistance = 3,
|
||||
) {
|
||||
this.updateViewDistance(viewDistance)
|
||||
|
||||
console.log(`ChunkMeshManager: Initializing with pool size ${this.poolSize} (min: ${this.minPoolSize}, max: ${this.maxPoolSize})`)
|
||||
|
||||
this.initializePool()
|
||||
}
|
||||
|
||||
private initializePool () {
|
||||
// Create initial pool
|
||||
for (let i = 0; i < this.poolSize; i++) {
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
const mesh = new THREE.Mesh(geometry, this.material)
|
||||
mesh.visible = false
|
||||
mesh.matrixAutoUpdate = false
|
||||
mesh.name = 'pooled-section-mesh'
|
||||
|
||||
const poolEntry: ChunkMeshPool = {
|
||||
mesh,
|
||||
inUse: false,
|
||||
lastUsedTime: 0
|
||||
}
|
||||
|
||||
this.meshPool.push(poolEntry)
|
||||
// Don't add to scene here - meshes will be added to containers
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or create a section with new geometry data
|
||||
*/
|
||||
updateSection (sectionKey: string, geometryData: MesherGeometryOutput): THREE.Mesh | null {
|
||||
// Normal pooling mode
|
||||
// Check if section already exists
|
||||
let poolEntry = this.activeSections.get(sectionKey)
|
||||
|
||||
if (!poolEntry) {
|
||||
// Get mesh from pool
|
||||
poolEntry = this.acquireMesh()
|
||||
if (!poolEntry) {
|
||||
console.warn(`ChunkMeshManager: No available mesh in pool for section ${sectionKey}`)
|
||||
return null
|
||||
}
|
||||
|
||||
this.activeSections.set(sectionKey, poolEntry)
|
||||
poolEntry.sectionKey = sectionKey
|
||||
}
|
||||
|
||||
const { mesh } = poolEntry
|
||||
const { geometry } = mesh
|
||||
|
||||
// Update geometry attributes efficiently
|
||||
this.updateGeometryAttribute(geometry, 'position', geometryData.positions, 3)
|
||||
this.updateGeometryAttribute(geometry, 'normal', geometryData.normals, 3)
|
||||
this.updateGeometryAttribute(geometry, 'color', geometryData.colors, 3)
|
||||
this.updateGeometryAttribute(geometry, 'uv', geometryData.uvs, 2)
|
||||
|
||||
// Use direct index assignment for better performance (like before)
|
||||
geometry.index = new THREE.BufferAttribute(geometryData.indices as Uint32Array | Uint16Array, 1)
|
||||
|
||||
// Set bounding box and sphere for the 16x16x16 section
|
||||
geometry.boundingBox = new THREE.Box3(
|
||||
new THREE.Vector3(-8, -8, -8),
|
||||
new THREE.Vector3(8, 8, 8)
|
||||
)
|
||||
geometry.boundingSphere = new THREE.Sphere(
|
||||
new THREE.Vector3(0, 0, 0),
|
||||
Math.sqrt(3 * 8 ** 2)
|
||||
)
|
||||
|
||||
// Position the mesh
|
||||
mesh.position.set(geometryData.sx, geometryData.sy, geometryData.sz)
|
||||
mesh.updateMatrix()
|
||||
mesh.visible = true
|
||||
|
||||
// Store metadata
|
||||
;(mesh as any).tilesCount = geometryData.positions.length / 3 / 4
|
||||
;(mesh as any).blocksCount = geometryData.blocksCount
|
||||
|
||||
poolEntry.lastUsedTime = performance.now()
|
||||
|
||||
return mesh
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a section and return its mesh to the pool
|
||||
*/
|
||||
releaseSection (sectionKey: string): boolean {
|
||||
const poolEntry = this.activeSections.get(sectionKey)
|
||||
if (!poolEntry) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Hide mesh and mark as available
|
||||
poolEntry.mesh.visible = false
|
||||
poolEntry.inUse = false
|
||||
poolEntry.sectionKey = undefined
|
||||
poolEntry.lastUsedTime = 0
|
||||
|
||||
// Clear geometry to free memory
|
||||
this.clearGeometry(poolEntry.mesh.geometry)
|
||||
|
||||
this.activeSections.delete(sectionKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mesh for section if it exists
|
||||
*/
|
||||
getSectionMesh (sectionKey: string): THREE.Mesh | undefined {
|
||||
return this.activeSections.get(sectionKey)?.mesh
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if section is managed by this pool
|
||||
*/
|
||||
hasSection (sectionKey: string): boolean {
|
||||
return this.activeSections.has(sectionKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update pool size based on new view distance
|
||||
*/
|
||||
updateViewDistance (maxViewDistance: number) {
|
||||
// Calculate dynamic pool size based on view distance
|
||||
const chunksInView = (maxViewDistance * 2 + 1) ** 2
|
||||
const maxSectionsPerChunk = this.worldHeight / 16
|
||||
const avgSectionsPerChunk = 5
|
||||
this.minPoolSize = Math.floor(chunksInView * avgSectionsPerChunk)
|
||||
this.maxPoolSize = Math.floor(chunksInView * maxSectionsPerChunk) + 1
|
||||
this.poolSize ??= this.minPoolSize
|
||||
|
||||
// Expand pool if needed to reach optimal size
|
||||
if (this.minPoolSize > this.poolSize) {
|
||||
const targetSize = Math.min(this.minPoolSize, this.maxPoolSize)
|
||||
this.expandPool(targetSize)
|
||||
}
|
||||
|
||||
console.log(`ChunkMeshManager: Updated view max distance to ${maxViewDistance}, pool: ${this.poolSize}/${this.maxPoolSize}, optimal: ${this.minPoolSize}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pool statistics
|
||||
*/
|
||||
getStats () {
|
||||
const freeCount = this.meshPool.filter(entry => !entry.inUse).length
|
||||
const hitRate = this.hits + this.misses > 0 ? (this.hits / (this.hits + this.misses) * 100).toFixed(1) : '0'
|
||||
|
||||
return {
|
||||
poolSize: this.poolSize,
|
||||
activeCount: this.activeSections.size,
|
||||
freeCount,
|
||||
hitRate: `${hitRate}%`,
|
||||
hits: this.hits,
|
||||
misses: this.misses
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup and dispose resources
|
||||
*/
|
||||
dispose () {
|
||||
// Release all active sections
|
||||
for (const [sectionKey] of this.activeSections) {
|
||||
this.releaseSection(sectionKey)
|
||||
}
|
||||
|
||||
// Dispose all meshes and geometries
|
||||
for (const poolEntry of this.meshPool) {
|
||||
// Meshes will be removed from scene when their parent containers are removed
|
||||
poolEntry.mesh.geometry.dispose()
|
||||
}
|
||||
|
||||
this.meshPool.length = 0
|
||||
this.activeSections.clear()
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
private acquireMesh (): ChunkMeshPool | undefined {
|
||||
if (this.bypassPooling) {
|
||||
return {
|
||||
mesh: new THREE.Mesh(new THREE.BufferGeometry(), this.material),
|
||||
inUse: true,
|
||||
lastUsedTime: performance.now()
|
||||
}
|
||||
}
|
||||
|
||||
// Find first available mesh
|
||||
const availableMesh = this.meshPool.find(entry => !entry.inUse)
|
||||
|
||||
if (availableMesh) {
|
||||
availableMesh.inUse = true
|
||||
this.hits++
|
||||
return availableMesh
|
||||
}
|
||||
|
||||
// No available mesh, expand pool to accommodate new sections
|
||||
let newPoolSize = Math.min(this.poolSize + 16, this.maxPoolSize)
|
||||
if (newPoolSize === this.poolSize) {
|
||||
newPoolSize = this.poolSize + 8
|
||||
this.maxPoolSize = newPoolSize
|
||||
console.warn(`ChunkMeshManager: Pool exhausted (${this.poolSize}/${this.maxPoolSize}). Emergency expansion to ${newPoolSize}`)
|
||||
}
|
||||
|
||||
this.misses++
|
||||
this.expandPool(newPoolSize)
|
||||
return this.acquireMesh()
|
||||
}
|
||||
|
||||
private expandPool (newSize: number) {
|
||||
const oldSize = this.poolSize
|
||||
this.poolSize = newSize
|
||||
|
||||
// console.log(`ChunkMeshManager: Expanding pool from ${oldSize} to ${newSize}`)
|
||||
|
||||
// Add new meshes to pool
|
||||
for (let i = oldSize; i < newSize; i++) {
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
const mesh = new THREE.Mesh(geometry, this.material)
|
||||
mesh.visible = false
|
||||
mesh.matrixAutoUpdate = false
|
||||
mesh.name = 'pooled-section-mesh'
|
||||
|
||||
const poolEntry: ChunkMeshPool = {
|
||||
mesh,
|
||||
inUse: false,
|
||||
lastUsedTime: 0
|
||||
}
|
||||
|
||||
this.meshPool.push(poolEntry)
|
||||
// Don't add to scene here - meshes will be added to containers
|
||||
}
|
||||
}
|
||||
|
||||
private updateGeometryAttribute (
|
||||
geometry: THREE.BufferGeometry,
|
||||
name: string,
|
||||
array: Float32Array,
|
||||
itemSize: number
|
||||
) {
|
||||
const attribute = geometry.getAttribute(name)
|
||||
|
||||
if (attribute && attribute.count === array.length / itemSize) {
|
||||
// Reuse existing attribute
|
||||
;(attribute.array as Float32Array).set(array)
|
||||
attribute.needsUpdate = true
|
||||
} else {
|
||||
// Create new attribute (this will dispose the old one automatically)
|
||||
geometry.setAttribute(name, new THREE.BufferAttribute(array, itemSize))
|
||||
}
|
||||
}
|
||||
|
||||
private clearGeometry (geometry: THREE.BufferGeometry) {
|
||||
// Clear attributes but keep the attribute objects for reuse
|
||||
const attributes = ['position', 'normal', 'color', 'uv']
|
||||
for (const name of attributes) {
|
||||
const attr = geometry.getAttribute(name)
|
||||
if (attr) {
|
||||
// Just mark as needing update but don't dispose to avoid recreation costs
|
||||
attr.needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry.index) {
|
||||
geometry.index.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import { renderSign } from '../sign-renderer'
|
|||
import { DisplayWorldOptions, GraphicsInitOptions } from '../../../src/appViewer'
|
||||
import { chunkPos, sectionPos } from '../lib/simpleUtils'
|
||||
import { WorldRendererCommon } from '../lib/worldrendererCommon'
|
||||
import { WorldDataEmitterWorker } from '../lib/worldDataEmitter'
|
||||
import { addNewStat } from '../lib/ui/newStats'
|
||||
import { MesherGeometryOutput, InstancingMode } from '../lib/mesher/shared'
|
||||
import { ItemSpecificContextProperties } from '../lib/basePlayerState'
|
||||
|
|
@ -25,6 +26,7 @@ import { CameraShake } from './cameraShake'
|
|||
import { ThreeJsMedia } from './threeJsMedia'
|
||||
import { Fountain } from './threeJsParticles'
|
||||
import { InstancedRenderer } from './instancedRenderer'
|
||||
import { ChunkMeshManager } from './chunkMeshManager'
|
||||
|
||||
type SectionKey = string
|
||||
|
||||
|
|
@ -53,6 +55,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
cameraContainer: THREE.Object3D
|
||||
media: ThreeJsMedia
|
||||
instancedRenderer: InstancedRenderer | undefined
|
||||
chunkMeshManager: ChunkMeshManager
|
||||
waitingChunksToDisplay = {} as { [chunkKey: string]: SectionKey[] }
|
||||
camera: THREE.PerspectiveCamera
|
||||
renderTimeAvg = 0
|
||||
|
|
@ -106,6 +109,14 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
this.cameraShake = new CameraShake(this, this.onRender)
|
||||
this.media = new ThreeJsMedia(this)
|
||||
this.instancedRenderer = new InstancedRenderer(this)
|
||||
this.chunkMeshManager = new ChunkMeshManager(this.material, this.worldSizeParams.worldHeight, this.viewDistance)
|
||||
|
||||
// Enable bypass pooling for debugging if URL param is present
|
||||
if (new URLSearchParams(location.search).get('bypassMeshPooling') === 'true') {
|
||||
this.chunkMeshManager.bypassPooling = true
|
||||
console.log('ChunkMeshManager: Bypassing pooling for debugging')
|
||||
}
|
||||
|
||||
// this.fountain = new Fountain(this.scene, this.scene, {
|
||||
// position: new THREE.Vector3(0, 10, 0),
|
||||
// })
|
||||
|
|
@ -137,6 +148,15 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
})
|
||||
}
|
||||
|
||||
override connect (worldView: WorldDataEmitterWorker) {
|
||||
super.connect(worldView)
|
||||
|
||||
// Add additional renderDistance handling for mesh pool updates
|
||||
worldView.on('renderDistance', (viewDistance) => {
|
||||
this.chunkMeshManager.updateViewDistance(viewDistance)
|
||||
})
|
||||
}
|
||||
|
||||
updateEntity (e, isPosUpdate = false) {
|
||||
const overrides = {
|
||||
rotation: {
|
||||
|
|
@ -346,8 +366,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
text += `B: ${formatBigNumber(this.blocksRendered)} `
|
||||
if (instancedStats) {
|
||||
text += `I: ${formatBigNumber(instancedStats.totalInstances)}/${instancedStats.activeBlockTypes}t `
|
||||
text += `DC: ${formatBigNumber(instancedStats.drawCalls)}`
|
||||
text += `DC: ${formatBigNumber(instancedStats.drawCalls)} `
|
||||
}
|
||||
const poolStats = this.chunkMeshManager.getStats()
|
||||
const poolMode = this.chunkMeshManager.bypassPooling ? 'BYPASS' : poolStats.hitRate
|
||||
text += `MP: ${poolStats.activeCount}/${poolStats.poolSize} ${poolMode}`
|
||||
pane.updateText(text)
|
||||
this.backendInfoReport = text
|
||||
}
|
||||
|
|
@ -361,7 +384,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
const [x, y, z] = key.split(',').map(x => Math.floor(+x / 16))
|
||||
// sum of distances: x + y + z
|
||||
const chunkDistance = Math.abs(x - this.cameraSectionPos.x) + Math.abs(y - this.cameraSectionPos.y) + Math.abs(z - this.cameraSectionPos.z)
|
||||
const section = this.sectionObjects[key].children.find(child => child.name === 'mesh')!
|
||||
const section = this.sectionObjects[key].mesh!
|
||||
section.renderOrder = 500 - chunkDistance
|
||||
}
|
||||
|
||||
|
|
@ -402,7 +425,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
delete this.waitingChunksToDisplay[chunkKey]
|
||||
}
|
||||
|
||||
// debugRecomputedDeletedObjects = 0
|
||||
handleWorkerMessage (data: { geometry: MesherGeometryOutput, key, type }): void {
|
||||
if (data.type !== 'geometry') return
|
||||
|
||||
|
|
@ -413,55 +435,74 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
|
||||
this.instancedRenderer?.removeSectionInstances(data.key)
|
||||
|
||||
// if (data.key === '48,64,32') {
|
||||
// console.log('handleWorkerMessage', data.key, this.sectionObjects[data.key], this.sectionInstancingMode[data.key], Object.keys(data.geometry.instancedBlocks).length, data.geometry.positions.length)
|
||||
// }
|
||||
|
||||
// Handle instanced blocks data from worker
|
||||
if (hasInstancedBlocks) {
|
||||
this.instancedRenderer?.handleInstancedBlocksFromWorker(data.geometry.instancedBlocks, data.key, this.getInstancingMode(new Vec3(chunkCoords[0], chunkCoords[1], chunkCoords[2])))
|
||||
}
|
||||
|
||||
// Check if chunk should be loaded and has geometry
|
||||
if (!this.loadedChunks[chunkKey] || !data.geometry.positions.length || !this.active) {
|
||||
// Release any existing section from the pool
|
||||
this.chunkMeshManager.releaseSection(data.key)
|
||||
return
|
||||
}
|
||||
|
||||
// remvoe object from scene
|
||||
let object = this.sectionObjects[data.key]
|
||||
if (object) {
|
||||
this.scene.remove(object)
|
||||
disposeObject(object)
|
||||
// disposeObject(object)
|
||||
delete this.sectionObjects[data.key]
|
||||
}
|
||||
|
||||
object = this.sectionObjects[data.key]
|
||||
// Use ChunkMeshManager for optimized mesh handling
|
||||
const mesh = this.chunkMeshManager.updateSection(data.key, data.geometry)
|
||||
|
||||
if (!this.loadedChunks[chunkKey] || !data.geometry.positions.length || !this.active) return
|
||||
if (!mesh) {
|
||||
console.warn(`Failed to get mesh for section ${data.key}`)
|
||||
return
|
||||
}
|
||||
|
||||
// if (object) {
|
||||
// this.debugRecomputedDeletedObjects++
|
||||
// Create or update the section object container
|
||||
object = new THREE.Group()
|
||||
this.sectionObjects[data.key] = object
|
||||
this.scene.add(object)
|
||||
|
||||
// Add the pooled mesh to the container
|
||||
object.add(mesh)
|
||||
this.sectionObjects[data.key].mesh = mesh as THREE.Mesh<THREE.BufferGeometry, THREE.MeshLambertMaterial>
|
||||
|
||||
// Handle signs and heads (these are added to the container, not the pooled mesh)
|
||||
this.addSignsAndHeads(object as THREE.Group, data.geometry)
|
||||
|
||||
this.updateBoxHelper(data.key)
|
||||
|
||||
// Handle chunk-based rendering
|
||||
if (this.displayOptions.inWorldRenderingConfig._renderByChunks) {
|
||||
object.visible = false
|
||||
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
||||
this.waitingChunksToDisplay[chunkKey] ??= []
|
||||
this.waitingChunksToDisplay[chunkKey].push(data.key)
|
||||
if (this.finishedChunks[chunkKey]) {
|
||||
this.finishChunk(chunkKey)
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePosDataChunk(data.key)
|
||||
object.matrixAutoUpdate = false
|
||||
}
|
||||
|
||||
private addSignsAndHeads (object: THREE.Group, geometry: MesherGeometryOutput) {
|
||||
// Clear existing signs and heads
|
||||
// const childrenToRemove = object.children.filter(child => child.name !== 'mesh' && child.name !== 'helper')
|
||||
// for (const child of childrenToRemove) {
|
||||
// object.remove(child)
|
||||
// disposeObject(child)
|
||||
// }
|
||||
|
||||
const geometry = object?.mesh?.geometry ?? new THREE.BufferGeometry()
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(data.geometry.positions, 3))
|
||||
geometry.setAttribute('normal', new THREE.BufferAttribute(data.geometry.normals, 3))
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(data.geometry.colors, 3))
|
||||
geometry.setAttribute('uv', new THREE.BufferAttribute(data.geometry.uvs, 2))
|
||||
geometry.index = new THREE.BufferAttribute(data.geometry.indices as Uint32Array | Uint16Array, 1)
|
||||
const { sx, sy, sz } = data.geometry
|
||||
|
||||
// Set bounding box for the 16x16x16 section
|
||||
geometry.boundingBox = new THREE.Box3(new THREE.Vector3(-8, -8, -8), new THREE.Vector3(8, 8, 8))
|
||||
geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), Math.sqrt(3 * 8 ** 2))
|
||||
|
||||
const mesh = object?.mesh ?? new THREE.Mesh(geometry, this.material)
|
||||
mesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
|
||||
mesh.name = 'mesh'
|
||||
if (!object) {
|
||||
object = new THREE.Group()
|
||||
object.add(mesh)
|
||||
}
|
||||
(object as any).tilesCount = data.geometry.positions.length / 3 / 4;
|
||||
(object as any).blocksCount = data.geometry.blocksCount
|
||||
|
||||
// should not compute it once
|
||||
if (Object.keys(data.geometry.signs).length) {
|
||||
for (const [posKey, { isWall, isHanging, rotation }] of Object.entries(data.geometry.signs)) {
|
||||
// Add signs
|
||||
if (Object.keys(geometry.signs).length) {
|
||||
for (const [posKey, { isWall, isHanging, rotation }] of Object.entries(geometry.signs)) {
|
||||
const signBlockEntity = this.blockEntities[posKey]
|
||||
if (!signBlockEntity) continue
|
||||
const [x, y, z] = posKey.split(',')
|
||||
|
|
@ -470,8 +511,10 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
object.add(sign)
|
||||
}
|
||||
}
|
||||
if (Object.keys(data.geometry.heads).length) {
|
||||
for (const [posKey, { isWall, rotation }] of Object.entries(data.geometry.heads)) {
|
||||
|
||||
// Add heads
|
||||
if (Object.keys(geometry.heads).length) {
|
||||
for (const [posKey, { isWall, rotation }] of Object.entries(geometry.heads)) {
|
||||
const headBlockEntity = this.blockEntities[posKey]
|
||||
if (!headBlockEntity) continue
|
||||
const [x, y, z] = posKey.split(',')
|
||||
|
|
@ -480,31 +523,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
object.add(head)
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.sectionObjects[data.key]) {
|
||||
this.sectionObjects[data.key] = object
|
||||
this.sectionObjects[data.key].mesh = mesh
|
||||
this.scene.add(object)
|
||||
}
|
||||
|
||||
this.updateBoxHelper(data.key)
|
||||
|
||||
if (this.displayOptions.inWorldRenderingConfig._renderByChunks) {
|
||||
object.visible = false
|
||||
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
||||
this.waitingChunksToDisplay[chunkKey] ??= []
|
||||
this.waitingChunksToDisplay[chunkKey].push(data.key)
|
||||
if (this.finishedChunks[chunkKey]) {
|
||||
// todo it might happen even when it was not an update
|
||||
this.finishChunk(chunkKey)
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePosDataChunk(data.key)
|
||||
object.matrixAutoUpdate = false
|
||||
mesh.onAfterRender = (renderer, scene, camera, geometry, material, group) => {
|
||||
// mesh.matrixAutoUpdate = false
|
||||
}
|
||||
}
|
||||
|
||||
getSignTexture (position: Vec3, blockEntity, backSide = false) {
|
||||
|
|
@ -1031,10 +1049,13 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
// Remove instanced blocks for this section
|
||||
this.instancedRenderer?.removeSectionInstances(key)
|
||||
|
||||
const mesh = this.sectionObjects[key]
|
||||
if (mesh) {
|
||||
this.scene.remove(mesh)
|
||||
disposeObject(mesh)
|
||||
// Release section from mesh pool
|
||||
this.chunkMeshManager.releaseSection(key)
|
||||
|
||||
const object = this.sectionObjects[key]
|
||||
if (object) {
|
||||
this.scene.remove(object)
|
||||
disposeObject(object)
|
||||
}
|
||||
delete this.sectionObjects[key]
|
||||
}
|
||||
|
|
@ -1089,6 +1110,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
|
||||
destroy (): void {
|
||||
this.instancedRenderer?.destroy()
|
||||
this.chunkMeshManager.dispose()
|
||||
super.destroy()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue