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