import * as THREE from 'three' import rbush from 'rbush' import { OBB } from 'three/examples/jsm/math/OBB' // import { Octree } from 'three/examples/jsm/math/Octree.js' // import { QuadTreeNode } from '@/core/QuadTree.ts' // import { convexHull } from '@/core/ModelUtils.ts' interface ItemEntry extends rbush.BBox { id: string obb: OBB } // 主管理器类 export default class ItemFindManager { private spatialIndex = new rbush() private items = new Map() dispose() { this.spatialIndex.clear() this.items.clear() } constructor() { } // 添加或更新物品 addOrUpdate(...items: ItemMetrix[]): void { for (const item of items) { const aabb = itemToAABB(item) const obb = itemToOBB(item) if (this.items.has(item.id)) { this.remove(item.id) } const entry: ItemEntry = { id: item.id, obb, ...aabb } this.items.set(item.id, entry) this.spatialIndex.insert(entry) } } // 移除物品 remove(...ids: string[]): void { for (const id of ids) { const entry = this.items.get(id) if (entry) { this.spatialIndex.remove(entry) this.items.delete(id) } } } // 位置查询 getItemsByPosition(x: number, z: number): string[] { const candidates = this.spatialIndex.search({ minX: x, minY: z, maxX: x, maxY: z }) const point = new THREE.Vector3(x, 0, z) return candidates.filter((item) => pointIntersectsOBB(point, item.obb)).map((item) => item.id) } // 距离查询 getItemsByDistance(x: number, z: number, distance: number): string[] { const sphere = new THREE.Sphere(new THREE.Vector3(x, 0, z), distance) const candidates = this.spatialIndex.search({ minX: x - distance, minY: z - distance, maxX: x + distance, maxY: z + distance }) return candidates.filter((item) => sphereIntersectsOBB(sphere, item.obb)).map((item) => item.id) } // 矩形区域查询(有交集) getItemsByRect(x1: number, z1: number, x2: number, z2: number): string[] { const rect = { minX: Math.min(x1, x2), maxX: Math.max(x1, x2), minY: Math.min(z1, z2), maxY: Math.max(z1, z2) } const candidates = this.spatialIndex.search(rect) return candidates.filter((item) => rectIntersectsOBB(rect, item.obb)).map((item) => item.id) } } function pointIntersectsOBB(point: THREE.Vector3, obb: OBB): boolean { const box = new THREE.Box3() setBox3FromOBB(box, obb) return box.containsPoint(point) } function sphereIntersectsOBB(sphere: THREE.Sphere, obb: OBB): boolean { const box = new THREE.Box3() setBox3FromOBB(box, obb) return box.intersectsSphere(sphere) } function rectIntersectsOBB(rect: { minX: number; minY: number; maxX: number; maxY: number }, obb: OBB): boolean { // 简化判断:用 AABB 投影到 XZ 平面做矩形交叉检测 const aabb = new THREE.Box3() setBox3FromOBB(aabb, obb) const aabb2D = { minX: aabb.min.x, minY: aabb.min.z, maxX: aabb.max.x, maxY: aabb.max.z } return !( rect.maxX < aabb2D.minX || rect.minX > aabb2D.maxX || rect.maxY < aabb2D.minY || rect.minY > aabb2D.maxY ) } export function itemToAABB(item: ItemMetrix): { minX: number; minY: number; maxX: number; maxY: number } { // 假设所有物品都是立方体,尺寸为 scale.x × scale.z const x = item.tf[0][0] const z = item.tf[0][2] const halfWidth = item.tf[2][0] / 2 const halfDepth = item.tf[2][2] / 2 return { minX: x - halfWidth, maxX: x + halfWidth, minY: z - halfDepth, maxY: z + halfDepth } } export function itemToOBB(item: ItemMetrix): OBB { const position = new THREE.Vector3(...item.tf[0]) const rotation = new THREE.Euler( THREE.MathUtils.degToRad(item.tf[1][0]), THREE.MathUtils.degToRad(item.tf[1][1]), THREE.MathUtils.degToRad(item.tf[1][2]), 'XYZ' ) const scale = new THREE.Vector3(...item.tf[2]) const matrix = new THREE.Matrix4() .makeRotationFromEuler(rotation) .premultiply(new THREE.Matrix4().makeTranslation(position.x, position.y, position.z)) .premultiply(new THREE.Matrix4().makeScale(scale.x, scale.y, scale.z)) const obb = new OBB( new THREE.Vector3(), new THREE.Vector3(0.5, 0.5, 0.5), new THREE.Matrix3().setFromMatrix4(matrix) ) return obb } function setBox3FromOBB(box: THREE.Box3, obb: OBB): THREE.Box3 { const center = obb.center const halfSize = new THREE.Vector3().copy(obb.halfSize) const rotation = obb.rotation // 8 个局部顶点 const vertices = [ new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, 1, 1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, 1, 1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, -1, 1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, -1, 1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, 1, -1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, 1, -1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, -1, -1)), new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, -1, -1)) ] // 应用旋转和平移到每个顶点 const worldVertices = vertices.map((v) => { return v.applyMatrix3(rotation).add(center) }) // 构造包围盒 box.min.set(Infinity, Infinity, Infinity) box.max.set(-Infinity, -Infinity, -Infinity) for (const v of worldVertices) { box.expandByPoint(v) } return box }