14 changed files with 505 additions and 245 deletions
@ -0,0 +1,74 @@ |
|||||
|
import * as THREE from 'three' |
||||
|
import type Viewport from '@/core/engine/Viewport.ts' |
||||
|
|
||||
|
export default class InstanceMeshBlock { |
||||
|
public readonly name: string |
||||
|
public readonly blockIndex: number |
||||
|
public readonly viewport: Viewport |
||||
|
public readonly instancedMesh: THREE.InstancedMesh |
||||
|
public readonly freeIndices: number[] = [] |
||||
|
|
||||
|
// instanceId -> itemId
|
||||
|
public readonly __indexIdMap = new Map<number, string>() |
||||
|
|
||||
|
getFreeMeshIndex(): number { |
||||
|
if (this.freeIndices.length === 0) { |
||||
|
return -1 // No free index available
|
||||
|
} |
||||
|
return this.freeIndices.pop() // Return the last free index
|
||||
|
} |
||||
|
|
||||
|
constructor(itemTypeName: string, allowSelect: boolean, allowDrag: boolean, viewport: Viewport, |
||||
|
geometry: THREE.BufferGeometry, material: THREE.Material, |
||||
|
blockIndex: number, capacity: number) { |
||||
|
this.name = itemTypeName |
||||
|
this.blockIndex = blockIndex |
||||
|
this.viewport = viewport |
||||
|
this.instancedMesh = new THREE.InstancedMesh(geometry, material, capacity) |
||||
|
this.instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage) |
||||
|
|
||||
|
console.log('createBlock: ' + itemTypeName + '[' + blockIndex + '] capacity:', capacity) |
||||
|
viewport.scene.add(this.instancedMesh) |
||||
|
if (allowSelect) { |
||||
|
this.viewport.entityManager._selectableObjects.push(this.instancedMesh) |
||||
|
} |
||||
|
if (allowDrag) { |
||||
|
this.viewport.entityManager._draggableObjects.push(this.instancedMesh) |
||||
|
} |
||||
|
|
||||
|
this.instancedMesh.userData.t = itemTypeName |
||||
|
this.instancedMesh.userData.entityId = 'InstanceMeshBlock_' + itemTypeName + '_' + blockIndex |
||||
|
_.extend(this.instancedMesh.userData,{ |
||||
|
t: itemTypeName, |
||||
|
blockIndex: blockIndex |
||||
|
}) |
||||
|
|
||||
|
const dummy = new THREE.Object3D() |
||||
|
dummy.scale.set(0, 0, 0) |
||||
|
dummy.updateMatrix() |
||||
|
|
||||
|
for (let i = 0; i < capacity; i++) { |
||||
|
this.instancedMesh.setMatrixAt(i, dummy.matrix) |
||||
|
this.freeIndices.push(i) |
||||
|
} |
||||
|
|
||||
|
this.instancedMesh.instanceMatrix.needsUpdate = true |
||||
|
} |
||||
|
|
||||
|
dispose() { |
||||
|
this.viewport.scene.remove(this.instancedMesh) |
||||
|
this.instancedMesh.geometry.dispose() |
||||
|
|
||||
|
if (this.instancedMesh.material) { |
||||
|
if (Array.isArray(this.instancedMesh.material)) { |
||||
|
this.instancedMesh.material.forEach(mat => mat.dispose()) |
||||
|
} else { |
||||
|
this.instancedMesh.material.dispose() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.instancedMesh.dispose() |
||||
|
this.__indexIdMap.clear() |
||||
|
this.freeIndices.length = 0 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
import * as THREE from 'three' |
||||
|
import type Viewport from '@/core/engine/Viewport.ts' |
||||
|
import InstanceMeshBlock from '@/core/manager/InstanceMeshBlock.ts' |
||||
|
import { PointManageWrap } from '@/core/manager/InstancePointManager.ts' |
||||
|
|
||||
|
export default class InstanceMeshManager { |
||||
|
private __uuidMap = new Map<string, InstanceMeshWrap>() |
||||
|
|
||||
|
public readonly name: string |
||||
|
public readonly viewport: Viewport |
||||
|
public readonly allowSelect: boolean |
||||
|
public readonly allowDrag: boolean |
||||
|
public readonly blockCapacity: number = 1000 // 每个 block 的容量
|
||||
|
|
||||
|
public readonly blocks: InstanceMeshBlock[] = [] |
||||
|
private readonly geometry: THREE.BufferGeometry |
||||
|
private readonly material: THREE.Material |
||||
|
|
||||
|
private readonly dummy: THREE.Object3D = new THREE.Object3D() |
||||
|
|
||||
|
constructor(name: string, viewport: Viewport, allowSelect: boolean, allowDrag: boolean, |
||||
|
geometry: THREE.BufferGeometry, material: THREE.Material) { |
||||
|
this.name = name |
||||
|
this.viewport = viewport |
||||
|
this.allowSelect = allowSelect |
||||
|
this.allowDrag = allowDrag |
||||
|
this.geometry = geometry |
||||
|
this.material = material |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取点实例数据 |
||||
|
*/ |
||||
|
findByMeshInstanceId(blockIndex: number, instanceId: number): InstanceMeshWrap { |
||||
|
if (!this.blocks[blockIndex]) { |
||||
|
console.error('InstancePointManager: Invalid blockIndex', blockIndex) |
||||
|
return null |
||||
|
} |
||||
|
const uuid = this.blocks[blockIndex].__indexIdMap.get(instanceId) |
||||
|
if (!uuid) return |
||||
|
return this.__uuidMap.get(uuid) |
||||
|
} |
||||
|
|
||||
|
create(entityId: string): InstanceMeshWrap { |
||||
|
let meshIndex = -1 |
||||
|
let blockIndex = -1 |
||||
|
for (const block of this.blocks) { |
||||
|
meshIndex = block.getFreeMeshIndex() |
||||
|
if (meshIndex >= 0) { |
||||
|
blockIndex = block.blockIndex |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
// 所有 block 都没有空闲索引,创建新的 block
|
||||
|
if (meshIndex < 0) { |
||||
|
const block = this.createBlock() |
||||
|
meshIndex = block.getFreeMeshIndex() |
||||
|
blockIndex = block.blockIndex |
||||
|
} |
||||
|
if (meshIndex < 0) { |
||||
|
system.showErrorDialog('InstancePointManager: No free index available after creating new block') |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
return new InstanceMeshWrap(entityId, this, blockIndex, meshIndex) |
||||
|
} |
||||
|
|
||||
|
delete(wrap: InstanceMeshWrap) { |
||||
|
const block = this.blocks[wrap.blockIndex] |
||||
|
if (!block) { |
||||
|
console.warn(`InstanceMeshManager: Block ${wrap.blockIndex} not found for wrap ${wrap.uuid}`) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 隐藏实例
|
||||
|
this.dummy.scale.set(0, 0, 0) |
||||
|
this.dummy.updateMatrix() |
||||
|
block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) |
||||
|
block.instancedMesh.instanceMatrix.needsUpdate = true |
||||
|
|
||||
|
// 回收索引
|
||||
|
block.freeIndices.push(wrap.meshIndex) |
||||
|
this.__uuidMap.delete(wrap.uuid) |
||||
|
block.__indexIdMap.delete(wrap.meshIndex) |
||||
|
|
||||
|
wrap.dispose() |
||||
|
} |
||||
|
|
||||
|
// 创建新的 InstanceMeshBlock
|
||||
|
createBlock(): InstanceMeshBlock { |
||||
|
const blockIndex = this.blocks.length |
||||
|
const block = new InstanceMeshBlock(this.name, this.allowSelect, this.allowDrag, |
||||
|
this.viewport, this.geometry, this.material, |
||||
|
blockIndex, this.blockCapacity) |
||||
|
this.blocks.push(block) |
||||
|
return block |
||||
|
} |
||||
|
|
||||
|
setBlockMatrixAt(wrap: InstanceMeshWrap, matrix: THREE.Matrix4) { |
||||
|
const block = this.blocks[wrap.blockIndex] |
||||
|
if (!block) { |
||||
|
console.warn(`InstanceMeshManager: Block ${wrap.blockIndex} not found!`) |
||||
|
return |
||||
|
} |
||||
|
this.__uuidMap.set(wrap.uuid, wrap) |
||||
|
block.instancedMesh.setMatrixAt(wrap.meshIndex, matrix) |
||||
|
wrap.parent = block.instancedMesh |
||||
|
wrap.visible = true // 默认可见
|
||||
|
block.instancedMesh.instanceMatrix.needsUpdate = true |
||||
|
} |
||||
|
|
||||
|
dispose() { |
||||
|
for (const block of this.blocks) { |
||||
|
block.dispose() |
||||
|
} |
||||
|
this.blocks.length = 0 // 清空 blocks 数组
|
||||
|
this.geometry.dispose() // 释放几何体资源
|
||||
|
this.material.dispose() // 释放材质资源
|
||||
|
console.log(`InstanceMeshManager ${this.name} disposed.`) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class InstanceMeshWrap { |
||||
|
readonly entityId: string |
||||
|
readonly manager: InstanceMeshManager |
||||
|
readonly blockIndex: number |
||||
|
readonly meshIndex: number |
||||
|
|
||||
|
uuid: string |
||||
|
parent: THREE.Object3D | null = null |
||||
|
name: string |
||||
|
visible: boolean |
||||
|
|
||||
|
constructor(entityId: string, manager: InstanceMeshManager, blockIndex: number, meshIndex: number) { |
||||
|
this.uuid = system.createUUID() |
||||
|
this.entityId = entityId |
||||
|
this.manager = manager |
||||
|
this.meshIndex = meshIndex |
||||
|
this.blockIndex = blockIndex |
||||
|
} |
||||
|
|
||||
|
syncMatrix(matrix: THREE.Matrix4): InstanceMeshWrap { |
||||
|
this.manager.setBlockMatrixAt(this, matrix) |
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
dispose() { |
||||
|
this.manager.delete(this) |
||||
|
this.parent = null |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue