You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

224 lines
7.3 KiB

import * as THREE from 'three'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts'
import type Viewport from '@/designer/Viewport.ts'
import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'
import { Vector2 } from 'three/src/math/Vector2'
export function quickCopyByMouse() {
// 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport
const currentMouseInfo = window['CurrentMouseInfo']
if (!currentMouseInfo?.viewport || !currentMouseInfo.x || !currentMouseInfo.z) {
system.msg('无法获取鼠标位置')
return
}
const viewport: Viewport = currentMouseInfo.viewport
const point: THREE.Vector2 = currentMouseInfo.mouse
const ray = new THREE.Raycaster()
ray.setFromCamera(point, viewport.camera)
const intersections = ray.intersectObjects(viewport.dragControl._dragObjects, true)
if (intersections.length === 0) {
system.msg('没有找到可复制的对象')
return
}
console.log('intersections:', intersections)
/*
// 如果不在线上,查找0.2米内的有效点 Object3D, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连
findObject3DByCondition(viewport.scene, object => {
// 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象
if (object instanceof THREE.Object3D && object.visible &&
object.userData.type && viewport.toolbox[object.userData.type]) {
const toolbox: Toolbox = viewport.toolbox[object.userData.type]
// 检查是否在 0.2 米内
const distance = object.position.distanceTo(new THREE.Vector3(x, 0, z))
if (distance < 0.2) {
// 找到一个有效点,执行复制操作
toolbox.start(object)
system.msg('复制成功')
return true
}
}
return false
})
*/
}
/**
* 查找射线周围指定半径内的对象
*/
export function findObjectsInRadius(viewport: Viewport,
point: THREE.Vector2,
radius: number,
lines: { object: THREE.Object3D, distance: number }[],
points: { object: THREE.Object3D, distance: number }[]
): void {
const ray = new THREE.Raycaster()
ray.setFromCamera(point, viewport.camera)
viewport.dragControl._dragObjects.forEach(obj => {
if (obj instanceof THREE.Points) {
// 处理点云:遍历每个点
const distance = distanceToRay(ray, point)
if (distance <= radius) {
points.push({ object: obj, distance })
}
} else if (obj instanceof THREE.Line) {
// 处理线段:计算线段到射线的最近距离
const distance = getLineDistanceToRay(ray, obj)
if (distance <= radius) {
lines.push({ object: obj, distance })
}
}
})
}
/**
* 计算点到射线的最短距离
*/
function distanceToRay(ray: THREE.Raycaster, point: THREE.Vector2) {
const closestPoint = new THREE.Vector3()
ray.closestPointToPoint(point, closestPoint)
return point.distanceTo(closestPoint)
}
/**
* 计算线段到射线的最短距离
*/
function getLineDistanceToRay(ray: THREE.Raycaster, line: THREE.Line) {
const lineStart = new THREE.Vector3()
const lineEnd = new THREE.Vector3()
line.geometry.attributes.position.getXYZ(0, lineStart)
line.geometry.attributes.position.getXYZ(1, lineEnd)
line.localToWorld(lineStart)
line.localToWorld(lineEnd)
const lineSegment = new THREE.Line3(lineStart, lineEnd)
const closestOnRay = new THREE.Vector3()
const closestOnLine = new THREE.Vector3()
THREE.Line3.prototype.closestPointsRayLine ??= function(ray, line, closestOnRay, closestOnLine) {
// 实现射线与线段最近点计算(需自定义或使用数学库)
}
lineSegment.closestPointsRayLine(ray, true, closestOnRay, closestOnLine)
return closestOnRay.distanceTo(closestOnLine)
}
/**
* 考虑吸附的情况下计算鼠标事件位置
*/
export function calcPositionUseSnap(e: MouseEvent, point: THREE.Vector3) {
// 按下 ctrl 键,不启用吸附,其他情况启用吸附
const gridOption = worldModel.gridOption
if (!e.ctrlKey && !e.metaKey) {
if (gridOption.snapEnabled && gridOption.snapDistance > 0) {
// 启用吸附, 针对 point 的 x 和 z 坐标进行吸附, 吸附距离为 gridOption.snapDistance
const snapDistance = gridOption.snapDistance
const newPoint = new THREE.Vector3(point.x, point.y, point.z)
newPoint.x = Math.round(newPoint.x / snapDistance) * snapDistance
newPoint.z = Math.round(newPoint.z / snapDistance) * snapDistance
return newPoint
}
}
return point
}
/**
* 在给定的场景中查找具有指定 uuid 的 Object3D 对象
*/
export function findObject3DById(scene: THREE.Object3D, uuid: string): THREE.Object3D | undefined {
const rets = findObject3DByCondition(scene, object => object.uuid === uuid)
if (rets.length > 0) {
return rets[0]
}
return undefined
}
/**
* 在给定场景中查找满足特定条件的 Object3D 对象集合
*/
export function findObject3DByCondition(scene: THREE.Object3D, condition: (object: THREE.Object3D) => boolean): THREE.Object3D[] {
const foundObjects: THREE.Object3D[] = []
// 定义一个内部递归函数来遍历每个节点及其子节点
function traverse(obj: THREE.Object3D) {
if (condition(obj)) {
foundObjects.push(obj)
}
// 遍历当前对象的所有子对象
for (let i = 0; i < obj.children.length; i++) {
traverse(obj.children[i])
}
}
// 开始从场景根节点进行遍历
traverse(scene)
return foundObjects
}
export function loadSceneFromJson(scene: THREE.Scene, items: ItemJson[]) {
console.time('loadSceneFromJson')
const object3ds: THREE.Object3D[] = []
// beforeLoad 通知所有加载的对象, 模型加载开始
getAllItemTypes().forEach((itemType: ItemTypeDefineOption) => {
const ret = itemType.clazz.beforeLoad()
Array.isArray(ret) && object3ds.push(...ret)
})
const loads = loadObject3DFromJson(items)
Array.isArray(loads) && object3ds.push(...loads)
// afterLoadComplete 通知所有加载的对象, 模型加载完成
getAllItemTypes().forEach((itemType: ItemTypeDefineOption) => {
const ret = itemType.clazz.afterLoadComplete(object3ds)
Array.isArray(ret) && object3ds.push(...ret)
})
scene.add(...object3ds)
// afterAddScene 通知所有加载的对象, 模型加载完成
getAllItemTypes().forEach(itemType => {
itemType.clazz.afterAddScene(scene, object3ds)
})
console.log('loadSceneFromJson:', items.length, 'items,', object3ds.length, 'objects')
console.timeEnd('loadSceneFromJson')
}
function loadObject3DFromJson(items: ItemJson[]): THREE.Object3D[] {
const result: THREE.Object3D[] = []
for (const item of items) {
if (!item || !item.t) {
console.error('unkown item:', item)
continue
}
const object3D: THREE.Object3D | undefined = getItemTypeByName(item.t)?.clazz.loadFromJson(item)
if (object3D === undefined) {
continue
}
if (_.isArray(item.items)) {
// 如果有子元素,递归处理
const children = loadObject3DFromJson(item.items)
children.forEach(child => object3D.add(child))
}
result.push(object3D)
}
return result
}