17 changed files with 108 additions and 480 deletions
@ -1,408 +0,0 @@ |
|||
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' |
|||
import { getMatrixFromTf } from '@/core/ModelUtils.ts' |
|||
|
|||
/** |
|||
* 点实例管理器 |
|||
* 内部使用 InstanceMesh 用于管理大量点实例 |
|||
*/ |
|||
export default class InstancePointManager { |
|||
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() |
|||
// itemId -> data
|
|||
private __uuidMap = new Map<string, PointManageWrap>() |
|||
|
|||
createPointSimple(id: string): PointManageWrap { |
|||
//@ts-ignore
|
|||
const wrap = this.createPoint({ |
|||
id: id, |
|||
name: id, |
|||
v: true, |
|||
tf: [[0, 0, 0], [0, 0, 0], [0, 0, 0]], |
|||
dt: {} |
|||
}) |
|||
|
|||
return wrap |
|||
} |
|||
|
|||
/** |
|||
* 创建点实例 |
|||
* @param item 点数据 |
|||
* @returns 分配的实例ID (如果失败返回-1) |
|||
*/ |
|||
createPoint(item: ItemJson): PointManageWrap { |
|||
// 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 |
|||
} |
|||
|
|||
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 |
|||
} |
|||
}) |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 更新点实例 |
|||
* @param item 点数据 |
|||
* @param option 更新选项 |
|||
*/ |
|||
updatePoint(item: ItemJson, option: { position?: Vector3, rotation?: Vector3, scale?: Vector3 } = {}): void { |
|||
const wrap = this.__uuidMap.get(item.id) |
|||
if (wrap === undefined) return |
|||
|
|||
wrap.visible = item.v !== false |
|||
wrap.userData.t = item.t |
|||
wrap.userData.entityId = item.id |
|||
|
|||
wrap.setMatrix4(getMatrixFromTf(item.tf)) |
|||
|
|||
// let [position, rotation, scale] = item.tf
|
|||
// if (option.position) {
|
|||
// position = option.position.toArray()
|
|||
// }
|
|||
// if (option.rotation) {
|
|||
// rotation = option.rotation.toArray()
|
|||
// }
|
|||
// if (option.scale) {
|
|||
// scale = option.scale.toArray()
|
|||
// }
|
|||
//
|
|||
// wrap.position.set(position[0], position[1], position[2])
|
|||
// wrap.rotation.set(
|
|||
// THREE.MathUtils.degToRad(rotation[0]),
|
|||
// THREE.MathUtils.degToRad(rotation[1]),
|
|||
// THREE.MathUtils.degToRad(rotation[2])
|
|||
// )
|
|||
// wrap.scale.set(scale[0], scale[1], scale[2])
|
|||
// this.syncMeshObject3D(wrap)
|
|||
} |
|||
|
|||
/** |
|||
* 获取点实例数据 |
|||
*/ |
|||
getObject3DLike(id: string): PointManageWrap { |
|||
return this.__uuidMap.get(id) |
|||
} |
|||
|
|||
/** |
|||
* 获取点实例数据 |
|||
*/ |
|||
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) |
|||
} |
|||
|
|||
setBlockMatrixAt(wrap: PointManageWrap, matrix: THREE.Matrix4) { |
|||
const block = this.blocks[wrap.blockIndex] |
|||
if (!block) { |
|||
console.warn(`InstanceMeshManager: Block ${wrap.blockIndex} not found!`) |
|||
return |
|||
} |
|||
|
|||
if (!block.__indexIdMap.has(wrap.meshIndex)) { |
|||
wrap.parent = block.instancedMesh |
|||
this.__uuidMap.set(wrap.uuid, wrap) |
|||
block.__indexIdMap.set(wrap.meshIndex, wrap.uuid) |
|||
} |
|||
block.instancedMesh.setMatrixAt(wrap.meshIndex, matrix) |
|||
block.instancedMesh.instanceMatrix.needsUpdate = true |
|||
} |
|||
|
|||
// syncMeshObject3D(wrap: PointManageWrap) {
|
|||
// if (wrap.meshIndex < 0) {
|
|||
// console.error('InstancePointManager: Invalid meshIndex for wrap', wrap)
|
|||
// return
|
|||
// }
|
|||
// if (!wrap.visible) {
|
|||
// this.dummy.scale.set(0, 0, 0)
|
|||
// } else {
|
|||
// this.dummy.position.copy(wrap.position)
|
|||
// this.dummy.rotation.copy(wrap.rotation)
|
|||
// this.dummy.scale.copy(wrap.scale)
|
|||
// }
|
|||
// this.dummy.updateMatrix()
|
|||
//
|
|||
// const block = this.blocks[wrap.blockIndex]
|
|||
// if (!wrap.parent) {
|
|||
// wrap.parent = block.instancedMesh
|
|||
// this.__uuidMap.set(wrap.uuid, wrap)
|
|||
// block.__indexIdMap.set(wrap.meshIndex, wrap.uuid)
|
|||
// }
|
|||
// block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix)
|
|||
// block.instancedMesh.instanceMatrix.needsUpdate = true
|
|||
// }
|
|||
|
|||
/** |
|||
* 删除点实例 |
|||
* @param id 点ID |
|||
*/ |
|||
deletePoint(id: string): void { |
|||
const wrap = this.__uuidMap.get(id) |
|||
if (!wrap) { |
|||
console.warn(`InstanceMeshManager: Wrap with id ${id} not found`) |
|||
return |
|||
} |
|||
|
|||
const block = this.blocks[wrap.blockIndex] |
|||
if (!block) { |
|||
console.warn(`InstanceMeshManager: Block ${wrap.blockIndex} not found for wrap ${id}`) |
|||
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(id) |
|||
block.__indexIdMap.delete(wrap.meshIndex) |
|||
|
|||
wrap.dispose() |
|||
} |
|||
|
|||
public static create( |
|||
name: string, |
|||
viewport: Viewport, |
|||
geometry: THREE.BufferGeometry, |
|||
material: THREE.Material, |
|||
allowSelect: boolean, allowDrag: boolean |
|||
): InstancePointManager { |
|||
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, 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 |
|||
|
|||
// 创建第一个 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() { |
|||
for (const block of this.blocks) { |
|||
block.dispose() |
|||
} |
|||
this.__uuidMap.clear() |
|||
this.blocks.length = 0 |
|||
} |
|||
} |
|||
|
|||
export class PointManageWrap { |
|||
readonly manager: InstancePointManager |
|||
readonly blockIndex: number = -1 |
|||
readonly meshIndex: number = -1 |
|||
parent: THREE.Object3D | null = null |
|||
|
|||
uuid: string |
|||
name: string |
|||
visible: boolean |
|||
|
|||
//@ts-ignore
|
|||
userData: UserData = {} |
|||
|
|||
updateWorldMatrix() { |
|||
|
|||
} |
|||
|
|||
get type() { |
|||
return 'PointManageWrap' |
|||
} |
|||
|
|||
get isObject3D() { |
|||
return false |
|||
} |
|||
|
|||
constructor(pointManager: InstancePointManager, blockIndex: number, meshIndex: number, data: any) { |
|||
this.manager = pointManager |
|||
this.blockIndex = blockIndex |
|||
this.meshIndex = meshIndex |
|||
_.extend(this, data) |
|||
} |
|||
|
|||
dispose() { |
|||
this.manager.deletePoint(this.uuid) |
|||
this.parent = null |
|||
} |
|||
|
|||
setMatrix4(matrix: THREE.Matrix4): PointManageWrap { |
|||
this.manager.setBlockMatrixAt(this, matrix) |
|||
return this |
|||
} |
|||
|
|||
set matrix(matrix: THREE.Matrix4) { |
|||
this.setMatrix4(matrix) |
|||
} |
|||
|
|||
get matrix(): THREE.Matrix4 { |
|||
const block = this.manager.blocks[this.blockIndex] |
|||
const matrix = new THREE.Matrix4() |
|||
block.instancedMesh.getMatrixAt(this.meshIndex, matrix) |
|||
return matrix |
|||
} |
|||
|
|||
applyMatrix4(targetMatrix: THREE.Matrix4): PointManageWrap { |
|||
const matrix = this.matrix |
|||
const newMatrix = matrix.multiplyMatrices(targetMatrix, matrix) |
|||
this.setMatrix4(newMatrix) |
|||
return this |
|||
} |
|||
|
|||
createBox3() { |
|||
const instancedMesh = this.manager.blocks[this.blockIndex]?.instancedMesh |
|||
const matrix = new THREE.Matrix4() |
|||
instancedMesh.getMatrixAt(this.meshIndex, matrix) |
|||
// 创建包围盒并应用矩阵
|
|||
const geometry = instancedMesh.geometry |
|||
//@ts-ignore
|
|||
const localBox = new THREE.Box3().setFromBufferAttribute(geometry.attributes.position) |
|||
return localBox.clone().applyMatrix4(matrix) |
|||
} |
|||
|
|||
createBox3Points() { |
|||
// 原始代码 =================================
|
|||
// const instancedMesh = this.manager.blocks[this.blockIndex]?.instancedMesh
|
|||
//
|
|||
// const matrix = new THREE.Matrix4()
|
|||
// instancedMesh.getMatrixAt(this.meshIndex, matrix)
|
|||
// // 创建包围盒并应用矩阵
|
|||
// const geometry = instancedMesh.geometry
|
|||
// //@ts-ignore
|
|||
// const localBox = new THREE.Box3().setFromBufferAttribute(geometry.attributes.position)
|
|||
// return localBox.clone().applyMatrix4(matrix)
|
|||
// =================================
|
|||
|
|||
// TF 代码 =================================
|
|||
const item = this.manager.viewport.entityManager.findItemById(this.uuid) |
|||
|
|||
const [pos, rot, scale] = item.tf |
|||
// // item.tf = [[8.5, 0.1, 2], [0, 0, 0], [1.5, 0.1, 2]]
|
|||
const matrix = new THREE.Matrix4() |
|||
matrix.compose( |
|||
new THREE.Vector3(pos[0], pos[1], pos[2]), |
|||
new THREE.Quaternion().setFromEuler(new THREE.Euler( |
|||
THREE.MathUtils.degToRad(rot[0]), |
|||
THREE.MathUtils.degToRad(rot[1]), |
|||
THREE.MathUtils.degToRad(rot[2]), |
|||
'XYZ' |
|||
)), |
|||
new THREE.Vector3(scale[0], scale[1], scale[2]) |
|||
) |
|||
// 获取 AABB 包围盒 ==========================
|
|||
// const aabb = new THREE.Box3()
|
|||
// .setFromCenterAndSize(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 1, 1))
|
|||
// .applyMatrix4(matrix)
|
|||
// const min = aabb.min
|
|||
// const max = aabb.max
|
|||
// return [
|
|||
// new THREE.Vector3(min.x - Constract.YELLOW_EXPAND_AMOUNT, max.y + Constract.YELLOW_EXPAND_AMOUNT, min.z - Constract.YELLOW_EXPAND_AMOUNT),
|
|||
// new THREE.Vector3(max.x + Constract.YELLOW_EXPAND_AMOUNT, max.y + Constract.YELLOW_EXPAND_AMOUNT, min.z - Constract.YELLOW_EXPAND_AMOUNT),
|
|||
// new THREE.Vector3(max.x + Constract.YELLOW_EXPAND_AMOUNT, max.y + Constract.YELLOW_EXPAND_AMOUNT, max.z + Constract.YELLOW_EXPAND_AMOUNT),
|
|||
// new THREE.Vector3(min.x - Constract.YELLOW_EXPAND_AMOUNT, max.y + Constract.YELLOW_EXPAND_AMOUNT, max.z + Constract.YELLOW_EXPAND_AMOUNT)
|
|||
// ]
|
|||
|
|||
// 获取 OOBB 包围盒
|
|||
const halfSize = new THREE.Vector3().setFromMatrixScale(matrix).multiplyScalar(0.5) |
|||
const vertices = [ |
|||
new THREE.Vector3(-halfSize.x, -halfSize.y, -halfSize.z), |
|||
new THREE.Vector3(halfSize.x, -halfSize.y, -halfSize.z), |
|||
new THREE.Vector3(halfSize.x, halfSize.y, -halfSize.z), |
|||
new THREE.Vector3(-halfSize.x, halfSize.y, -halfSize.z), |
|||
new THREE.Vector3(-halfSize.x, -halfSize.y, halfSize.z), |
|||
new THREE.Vector3(halfSize.x, -halfSize.y, halfSize.z), |
|||
new THREE.Vector3(halfSize.x, halfSize.y, halfSize.z), |
|||
new THREE.Vector3(-halfSize.x, halfSize.y, halfSize.z) |
|||
] |
|||
|
|||
// 应用旋转 + 平移到每个顶点
|
|||
const verticesWorld = vertices.map(v => |
|||
v.clone().applyMatrix4(matrix) |
|||
) |
|||
|
|||
return verticesWorld |
|||
} |
|||
} |
|||
Loading…
Reference in new issue