Browse Source

SelectInspect OBB 黄选

master
修宁 6 months ago
parent
commit
f2bdc448db
  1. 16
      doc/OBB矩形包围盒API.md
  2. 88
      src/core/ItemObbManager.ts
  3. 119
      src/core/ModelUtils.ts
  4. 43
      src/core/controls/SelectInspect.ts
  5. 69
      src/core/manager/InstancePointManager.ts

16
doc/OBB矩形包围盒API.md

@ -59,27 +59,27 @@ deleteItem(id: string)
---
### getItemsByPosition(x:number, z:number): ItemJson[]
### getItemsByPosition(x:number, z:number): string[]
根据位置,获取命中的物品集合
根据位置,获取命中的物品ID集合
getItemsByPosition({x:number, z:number}): ItemJson[]
getItemsByPosition({x:number, z:number}): string[]
---
### getItemsByPositionDistance(x, z, distance): {item, distance}[]
### getItemsByPositionDistance(x, z, distance): {id:string, distance:number}[]
getItemsByPositionDistance(x:number, z:number, distance:number): {item:ItemJson, distance:number}[]
getItemsByPositionDistance(x:number, z:number, distance:number): {id:string, distance:number}[]
根据位置,获取周边单位距离内的所有物品集合,及距离
根据位置,获取周边单位距离内的所有物品ID集合,及他们的距离
---
### getItemsByRect(x1,y1,x2,y2): ItemJson[]
### getItemsByRect(x1,y1,x2,y2): string[]
根据矩形,获取与矩形有碰撞的所有物品集合
给出一个选择框(矩形)2个顶点,根据矩形,获取与矩形有碰撞的所有物品集合
getItemsByPosition({x:number, z:number}): ItemJson[]

88
src/core/ItemObbManager.ts

@ -0,0 +1,88 @@
// 开发一个物品 OBB 包围盒管理器
// 借助 three-mesh-bvh 库来实现
// 里面所有的物品不用管渲染(不用去到 Scene / 不用管纹理和材质),但期望他最大程度的使用 BufferGeometry 用显卡来运算
import * as THREE from 'three'
/**
* OBB
* 100,000 , OBB
* /
*/
export default class ItemObbManager {
/**
* (ID)
*/
addOrUpdate(...items: ItemMetrix[]): void {
}
/**
* ID
*/
remove(...ids: string[]): void {
}
/**
* OBB包围盒的所有物品ID集合
* @param x X坐标
* @param z Z坐标
*/
getItemsByPosition(x: number, z: number): string[] {
return []
}
/**
* OBB包围盒碰到的所有物品ID集合
* @param x X坐标
* @param z Z坐标
* @param distance
*/
getItemsByDistance(x: number, z: number, distance: number): string[] {
// Implementation for getting items by distance
return []
}
/**
* (x1,z1)->(x2,z2)OBB包围盒与矩形有交集的所有物品ID集合
*/
getItemsByRect(x1: number, z1: number, x2: number, z2: number): string[] {
return []
}
/**
* (x1,z1)->(x2,z2)OBB包围盒完全在矩形内的物品ID集合
*/
getItemsByRectInclude(): string[] {
return []
}
}
export interface ItemMetrix {
/**
* ID,
*/
id: string
/**
* , 3x3矩阵, X轴正增长向右, Y轴正增长向屏幕外, Z轴正增长向下
*/
tf: [
/**
* position,
* [0]=x轴向右, [1]=y轴高度向屏幕外, [2]=z轴向下
*/
[number, number, number],
/**
* rotation,
* [0]=X轴逆向旋转角度, [1]=Y轴逆向旋转角度, [2]=Z轴逆向旋转角度
* three.js "角度""弧度"
*/
[number, number, number],
/**
* scale, [0]=X宽度, [1]=Y高度, [2]=Z长度
*/
[number, number, number],
]
}

119
src/core/ModelUtils.ts

@ -599,14 +599,115 @@ export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) {
}
export function getMatrixFromTf(tf: number[][]): THREE.Matrix4 {
const dummy = new THREE.Object3D()
dummy.position.set(tf[0][0], tf[0][1], tf[0][2])
dummy.rotation.set(
THREE.MathUtils.degToRad(tf[1][0]),
THREE.MathUtils.degToRad(tf[1][1]),
THREE.MathUtils.degToRad(tf[1][2])
const [pos, rot, scale] = tf
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])
)
dummy.scale.set(tf[2][0], tf[2][1], tf[2][2])
dummy.updateMatrix()
return dummy.matrix
return matrix
}
/**
* AABBox
* @param matrix ItemJson
*/
export function getAABBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] {
if (matrix instanceof THREE.Matrix4) {
} else {
// item.tf = [[0, 0.1, 0], [0, 0, 0], [1, 1, 1]]
matrix = getMatrixFromTf(matrix.tf)
}
// 获取 AABB 包围盒
const box = new THREE.Box3()
.setFromCenterAndSize(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 1, 1))
.applyMatrix4(matrix)
const min = box.min
const max = box.max
const corners = [
new THREE.Vector3(min.x, min.y, min.z),
new THREE.Vector3(max.x, min.y, min.z),
new THREE.Vector3(max.x, max.y, min.z),
new THREE.Vector3(min.x, max.y, min.z),
new THREE.Vector3(min.x, min.y, max.z),
new THREE.Vector3(max.x, min.y, max.z),
new THREE.Vector3(max.x, max.y, max.z),
new THREE.Vector3(min.x, max.y, max.z)
]
// 构建边索引(共 12 条边)
const edgePositions: THREE.Vector3[] = []
const edges = [
[0, 1], [1, 2], [2, 3], [3, 0], // 侧面
[4, 5], [5, 6], [6, 7], [7, 4], // 右侧面
// 上面的点 2,3,6,7
[3, 2], [3, 7], [7, 6], [6, 2],
// 下面的点: 0,1,4,5
[0, 1], [1, 5], [5, 4], [4, 0]
]
edges.forEach(([a, b]) => {
const va = corners[a]
const vb = corners[b]
edgePositions.push(va, vb)
})
return edgePositions
}
/**
* OBB
* @param matrix ItemJson
*/
export function getOBBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] {
if (matrix instanceof THREE.Matrix4) {
} else {
// item.tf = [[0, 0.1, 0], [0, 0, 0], [1, 1, 1]]
matrix = getMatrixFromTf(matrix.tf)
}
// 获取 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 corners = vertices.map(v =>
v.clone().applyMatrix4(matrix)
)
// 构建边索引(共 12 条边)
const edgePositions: THREE.Vector3[] = []
const edges = [
[0, 1], [1, 2], [2, 3], [3, 0], // 侧面
[4, 5], [5, 6], [6, 7], [7, 4], // 右侧面
// 上面的点 2,3,6,7
[3, 2], [3, 7], [7, 6], [6, 2],
// 下面的点: 0,1,4,5
[0, 1], [1, 5], [5, 4], [4, 0]
]
edges.forEach(([a, b]) => {
const va = corners[a]
const vb = corners[b]
edgePositions.push(va, vb)
})
return edgePositions
}

43
src/core/controls/SelectInspect.ts

@ -10,6 +10,7 @@ import { getSetter } from '@/core/manager/ModuleManager.ts'
import Constract from '@/core/Constract.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import { PointManageWrap } from '@/core/manager/InstancePointManager.ts'
import { getAABBox, getOBBox } from '@/core/ModelUtils.ts'
/**
*
@ -248,51 +249,27 @@ export default class SelectInspect implements IControls {
// 更新选中对象的包围盒线框
private updateSelectionBox = () => {
const selectedObject = this.viewport.state.selectedObject
this.clearSelectionBox()
if (!selectedObject) {
const item = this.viewport.state.selectedItem
if (!item) {
return
}
this.selectionId = selectedObject.userData?.entityId
// 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)
selectedObject.updateWorldMatrix(false, true)
let box: THREE.Box3
if (selectedObject instanceof THREE.Object3D) {
box = new THREE.Box3().setFromObject(selectedObject)
} else if (selectedObject instanceof PointManageWrap) {
box = selectedObject.createBox3()
}
const min = box.min
const max = box.max
const corners = [
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)
]
// 构建矩形边框(4 条边)
// 构造 positions 数组
const edgePositions = getOBBox(item) // getOBBox(item) // getAABBox(item)
const positions = []
for (let i = 0; i < 4; i++) {
const p1 = corners[i]
const p2 = corners[(i + 1) % 4]
for (let i = 0; i < edgePositions.length; i += 2) {
const p1 = edgePositions[i]
const p2 = edgePositions[i + 1]
positions.push(p1.x, p1.y, p1.z)
positions.push(p2.x, p2.y, p2.z)
}
// 创建几何体
const lineGeom = new LineGeometry()
const vertices = new Float32Array(positions)
lineGeom.setPositions(vertices)
const lineGeom = new LineGeometry().setPositions(new Float32Array(positions))
const selectionBox = new Line2(lineGeom, this.yellowMaterial)
selectionBox.computeLineDistances()
selectionBox.scale.set(1, 1, 1)
this.selectionBox = selectionBox
console.log('selectedItem', this.viewport.state.selectedItem)

69
src/core/manager/InstancePointManager.ts

@ -304,15 +304,66 @@ export class PointManageWrap {
return this
}
createBox3(): THREE.Box3 {
const instancedMesh = this.manager.blocks[this.blockIndex]?.instancedMesh
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)
// =================================
// 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()
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)
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…
Cancel
Save