|
|
|
@ -1,23 +1,25 @@ |
|
|
|
import * as THREE from 'three' |
|
|
|
import type Viewport from '@/core/engine/Viewport.ts' |
|
|
|
import { Vector3 } from 'three/src/math/Vector3' |
|
|
|
import InstanceMeshBlock from '@/core/manager/InstanceMeshBlock.ts' |
|
|
|
|
|
|
|
/** |
|
|
|
* 点实例管理器 |
|
|
|
* 内部使用 InstanceMesh 用于管理大量点实例 |
|
|
|
*/ |
|
|
|
export default class InstancePointManager { |
|
|
|
public readonly name: string |
|
|
|
public readonly viewport: Viewport |
|
|
|
public readonly instancedMesh: THREE.InstancedMesh |
|
|
|
public readonly allowSelect: boolean |
|
|
|
public readonly allowDrag: boolean |
|
|
|
public readonly blockCapacity: number = 1000 // 每个 block 的容量
|
|
|
|
|
|
|
|
private readonly freeIndices: number[] = [] |
|
|
|
private readonly maxInstanceCount: number |
|
|
|
public readonly blocks: InstanceMeshBlock[] = [] |
|
|
|
private readonly geometry: THREE.BufferGeometry |
|
|
|
private readonly material: THREE.Material |
|
|
|
private readonly dummy: THREE.Object3D = new THREE.Object3D() |
|
|
|
// itemId -> instanceId
|
|
|
|
// itemId -> data
|
|
|
|
private __uuidMap = new Map<string, PointManageWrap>() |
|
|
|
// instanceId -> itemId
|
|
|
|
private __indexIdMap = new Map<number, string>() |
|
|
|
|
|
|
|
createPointSimple(id: string): PointManageWrap { |
|
|
|
//@ts-ignore
|
|
|
|
@ -38,21 +40,39 @@ export default class InstancePointManager { |
|
|
|
* @returns 分配的实例ID (如果失败返回-1) |
|
|
|
*/ |
|
|
|
createPoint(item: ItemJson): PointManageWrap { |
|
|
|
if (this.freeIndices.length === 0) { |
|
|
|
system.showErrorDialog('InstancePointManager is full') |
|
|
|
// Try existing blocks
|
|
|
|
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 |
|
|
|
} |
|
|
|
|
|
|
|
const meshIndex = this.freeIndices.pop()! |
|
|
|
return new PointManageWrap(this, { |
|
|
|
return new PointManageWrap(this, blockIndex, meshIndex, { |
|
|
|
uuid: item.id, |
|
|
|
name: item.name, |
|
|
|
blockIndex: blockIndex, |
|
|
|
meshIndex: meshIndex, |
|
|
|
visible: item.v !== false, |
|
|
|
//@ts-ignore
|
|
|
|
userData: { |
|
|
|
t: item.t, |
|
|
|
createType: 'point', |
|
|
|
blockIndex: blockIndex, |
|
|
|
meshIndex: meshIndex, |
|
|
|
entityId: item.id |
|
|
|
} |
|
|
|
}) |
|
|
|
@ -103,8 +123,12 @@ export default class InstancePointManager { |
|
|
|
/** |
|
|
|
* 获取点实例数据 |
|
|
|
*/ |
|
|
|
findByMeshInstanceId(instanceId: number): PointManageWrap { |
|
|
|
const uuid = this.__indexIdMap.get(instanceId) |
|
|
|
findByMeshInstanceId(blockIndex: number, instanceId: number): PointManageWrap { |
|
|
|
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) |
|
|
|
} |
|
|
|
@ -123,13 +147,14 @@ export default class InstancePointManager { |
|
|
|
} |
|
|
|
this.dummy.updateMatrix() |
|
|
|
|
|
|
|
const block = this.blocks[wrap.blockIndex] |
|
|
|
if (!wrap.parent) { |
|
|
|
wrap.parent = this.instancedMesh |
|
|
|
wrap.parent = block.instancedMesh |
|
|
|
this.__uuidMap.set(wrap.uuid, wrap) |
|
|
|
this.__indexIdMap.set(wrap.meshIndex, wrap.uuid) |
|
|
|
block.__indexIdMap.set(wrap.meshIndex, wrap.uuid) |
|
|
|
} |
|
|
|
this.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) |
|
|
|
this.instancedMesh.instanceMatrix.needsUpdate = true |
|
|
|
block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) |
|
|
|
block.instancedMesh.instanceMatrix.needsUpdate = true |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -140,16 +165,18 @@ export default class InstancePointManager { |
|
|
|
const wrap = this.__uuidMap.get(id) |
|
|
|
if (wrap === undefined) return |
|
|
|
|
|
|
|
const block = this.blocks[wrap.blockIndex] |
|
|
|
|
|
|
|
// 隐藏实例
|
|
|
|
this.dummy.scale.set(0, 0, 0) |
|
|
|
this.dummy.updateMatrix() |
|
|
|
this.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) |
|
|
|
this.instancedMesh.instanceMatrix.needsUpdate = true |
|
|
|
block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) |
|
|
|
block.instancedMesh.instanceMatrix.needsUpdate = true |
|
|
|
|
|
|
|
// 回收索引
|
|
|
|
this.freeIndices.push(wrap.meshIndex) |
|
|
|
block.freeIndices.push(wrap.meshIndex) |
|
|
|
this.__uuidMap.delete(id) |
|
|
|
this.__indexIdMap.delete(wrap.meshIndex) |
|
|
|
block.__indexIdMap.delete(wrap.meshIndex) |
|
|
|
|
|
|
|
wrap.dispose() |
|
|
|
} |
|
|
|
@ -159,59 +186,70 @@ export default class InstancePointManager { |
|
|
|
viewport: Viewport, |
|
|
|
geometry: THREE.BufferGeometry, |
|
|
|
material: THREE.Material, |
|
|
|
maxInstances: number = 20000 |
|
|
|
allowSelect: boolean, allowDrag: boolean |
|
|
|
): InstancePointManager { |
|
|
|
return new InstancePointManager(name, viewport, geometry, material, maxInstances) |
|
|
|
return new InstancePointManager(name, allowSelect, allowDrag, viewport, geometry, material) |
|
|
|
} |
|
|
|
|
|
|
|
private createBlock(): InstanceMeshBlock { |
|
|
|
const block = new InstanceMeshBlock( |
|
|
|
this.name, this.allowSelect, this.allowDrag, this.viewport, |
|
|
|
this.geometry, |
|
|
|
this.material, this.blocks.length, this.blockCapacity |
|
|
|
) |
|
|
|
this.blocks.push(block) |
|
|
|
return block |
|
|
|
} |
|
|
|
|
|
|
|
private constructor(name: string, viewport: Viewport, geometry: THREE.BufferGeometry, material: THREE.Material, maxInstances: number) { |
|
|
|
private constructor(name: string, allowSelect: boolean, allowDrag: boolean, viewport: Viewport, geometry: THREE.BufferGeometry, material: THREE.Material) { |
|
|
|
this.name = name |
|
|
|
this.allowSelect = allowSelect |
|
|
|
this.allowDrag = allowDrag |
|
|
|
this.viewport = viewport |
|
|
|
this.geometry = geometry |
|
|
|
this.material = material |
|
|
|
this.maxInstanceCount = maxInstances |
|
|
|
|
|
|
|
this.instancedMesh = new THREE.InstancedMesh(geometry, material, maxInstances) |
|
|
|
this.instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage) |
|
|
|
this.instancedMesh.name = name |
|
|
|
console.log('InstancePointManager: [' + name + '] created with maxInstances:', maxInstances) |
|
|
|
viewport.scene.add(this.instancedMesh) |
|
|
|
this.viewport.entityManager._selectableObjects.push(this.instancedMesh) |
|
|
|
this.viewport.entityManager._draggableObjects.push(this.instancedMesh) |
|
|
|
this.instancedMesh.userData.t = name |
|
|
|
this.instancedMesh.userData.entityId = 'InstancePointManager' |
|
|
|
|
|
|
|
this.dummy.scale.set(0, 0, 0) |
|
|
|
for (let i = 0; i < maxInstances; i++) { |
|
|
|
this.dummy.updateMatrix() |
|
|
|
this.instancedMesh.setMatrixAt(i, this.dummy.matrix) |
|
|
|
this.freeIndices.push(i) |
|
|
|
} |
|
|
|
|
|
|
|
this.instancedMesh.instanceMatrix.needsUpdate = true |
|
|
|
// 创建第一个 block
|
|
|
|
this.createBlock() |
|
|
|
|
|
|
|
|
|
|
|
// this.instancedMesh = new THREE.InstancedMesh(geometry, material, maxInstances)
|
|
|
|
// this.instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
|
|
|
|
// this.instancedMesh.name = name
|
|
|
|
// console.log('InstancePointManager: [' + name + '] created with maxInstances:', maxInstances)
|
|
|
|
// 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 = name
|
|
|
|
// this.instancedMesh.userData.entityId = 'InstancePointManager'
|
|
|
|
//
|
|
|
|
// this.dummy.scale.set(0, 0, 0)
|
|
|
|
// for (let i = 0; i < maxInstances; i++) {
|
|
|
|
// this.dummy.updateMatrix()
|
|
|
|
// this.instancedMesh.setMatrixAt(i, this.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() |
|
|
|
} |
|
|
|
for (const block of this.blocks) { |
|
|
|
block.dispose() |
|
|
|
} |
|
|
|
|
|
|
|
this.instancedMesh.dispose() |
|
|
|
this.__uuidMap.clear() |
|
|
|
this.__indexIdMap.clear() |
|
|
|
this.freeIndices.length = 0 |
|
|
|
this.blocks.length = 0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
export class PointManageWrap { |
|
|
|
readonly manager: InstancePointManager |
|
|
|
meshIndex: number = -1 |
|
|
|
readonly blockIndex: number = -1 |
|
|
|
readonly meshIndex: number = -1 |
|
|
|
parent: THREE.Object3D | null = null |
|
|
|
|
|
|
|
uuid: string |
|
|
|
@ -234,8 +272,9 @@ export class PointManageWrap { |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
constructor(pointManager: InstancePointManager, data: any, meshIndex: number = -1) { |
|
|
|
constructor(pointManager: InstancePointManager, blockIndex: number, meshIndex: number, data: any) { |
|
|
|
this.manager = pointManager |
|
|
|
this.blockIndex = blockIndex |
|
|
|
this.meshIndex = meshIndex |
|
|
|
_.extend(this, data) |
|
|
|
} |
|
|
|
@ -243,7 +282,6 @@ export class PointManageWrap { |
|
|
|
dispose() { |
|
|
|
this.manager.deletePoint(this.uuid) |
|
|
|
this.parent = null |
|
|
|
this.meshIndex = -1 |
|
|
|
} |
|
|
|
|
|
|
|
applyMatrix4(matrix: THREE.Matrix4): PointManageWrap { |
|
|
|
@ -260,7 +298,8 @@ export class PointManageWrap { |
|
|
|
} |
|
|
|
|
|
|
|
createBox3(): THREE.Box3 { |
|
|
|
const instancedMesh = this.manager.instancedMesh |
|
|
|
const instancedMesh = this.manager.blocks[this.blockIndex]?.instancedMesh |
|
|
|
|
|
|
|
const matrix = new THREE.Matrix4() |
|
|
|
instancedMesh.getMatrixAt(this.meshIndex, matrix) |
|
|
|
// 创建包围盒并应用矩阵
|
|
|
|
|