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