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.
363 lines
12 KiB
363 lines
12 KiB
import type Viewport from '@/core/engine/Viewport'
|
|
import * as THREE from 'three'
|
|
import { getLineId, getMatrixFromTf, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts'
|
|
import { Line2 } from 'three/examples/jsm/lines/Line2'
|
|
import { LineWrap } from '@/core/manager/LineSegmentManager.ts'
|
|
import type { LineLike, Object3DLike } from '@/types/ModelTypes.ts'
|
|
import { MeshWrap } from '@/core/manager/InstanceMeshManager.ts'
|
|
import { getRenderer } from '@/core/manager/ModuleManager.ts'
|
|
|
|
/**
|
|
* 基本渲染器基类
|
|
* 定义了点 / 线如何渲染到 Three.js 场景中.
|
|
* 后期考虑调用 InstancePool 渲染
|
|
*/
|
|
export default abstract class BaseRenderer {
|
|
|
|
/**
|
|
* 每次 beginUpdate 时记录临时 viewport, endUpdate 之后要马上删除, 因为 BaseRenderer 全局只有一个实例, 而 viewport 有多个
|
|
*/
|
|
tempViewport?: Viewport = undefined
|
|
|
|
isUpdating: boolean = false
|
|
|
|
readonly itemTypeName: string
|
|
|
|
constructor(itemTypeName: string) {
|
|
this.itemTypeName = itemTypeName
|
|
}
|
|
|
|
async init(): Promise<any> {
|
|
return Promise.resolve()
|
|
}
|
|
|
|
/**
|
|
* 开始更新
|
|
* @param viewport 当前视口
|
|
*/
|
|
beginRendererUpdate(viewport: Viewport): void {
|
|
this.tempViewport = viewport
|
|
this.isUpdating = true
|
|
}
|
|
|
|
/**
|
|
* 结束更新
|
|
*/
|
|
endRendererUpdate(): void {
|
|
this.tempViewport = undefined
|
|
this.isUpdating = false
|
|
}
|
|
|
|
/**
|
|
* 创建一个最基本的点对象, 不用管 item 的 name / id / 位置 / 转换 / 大小 和 userData, 除非有明确定义
|
|
*/
|
|
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike {
|
|
throw new Error('createPointBasic method must be implemented in derived class.')
|
|
}
|
|
|
|
/**
|
|
* 创建测量线
|
|
*/
|
|
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): LineLike | undefined | void {
|
|
throw new Error('createLineBasic method must be implemented in derived class.')
|
|
}
|
|
|
|
abstract get defaultScale(): THREE.Vector3
|
|
|
|
abstract get defaultRotation(): THREE.Vector3
|
|
|
|
abstract get defulePositionY(): number
|
|
|
|
/**
|
|
* 创建或更新线之后的回调
|
|
*/
|
|
afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: LineLike) {
|
|
}
|
|
|
|
/**
|
|
* 创建或更新点之后的回调
|
|
*/
|
|
afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: Object3DLike) {
|
|
}
|
|
|
|
/**
|
|
* 删除线之后的回调
|
|
*/
|
|
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption) {
|
|
}
|
|
|
|
/**
|
|
* 将对象添加到当前视口的场景中
|
|
*/
|
|
appendToScene(...objects: Object3DLike[]) {
|
|
if (!this.tempViewport || !this.tempViewport.scene) {
|
|
console.warn('No active viewport to append objects to.')
|
|
return
|
|
}
|
|
|
|
this.tempViewport.scene.add(...objects)
|
|
}
|
|
|
|
removeFromScene(...objects: THREE.Object3D[]) {
|
|
if (!this.tempViewport || !this.tempViewport.scene) {
|
|
console.warn('No active viewport to remove objects from.')
|
|
return
|
|
}
|
|
this.tempViewport.scene.remove(...objects)
|
|
//this.tempViewport.dragManager.setDragObjects(objects, 'remove')
|
|
}
|
|
|
|
createPointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike {
|
|
const point = this.createPoint(item, option)
|
|
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
|
|
|
|
if (item.dt.storeAt?.item) {
|
|
this.tempViewport.runtimeManager.addStoreAt(item.dt.storeAt?.item, item.id)
|
|
}
|
|
|
|
setUserDataForItem(item, point)
|
|
this.afterCreateOrUpdatePoint(item, option, point)
|
|
this.tempViewport.entityManager.appendObject(item.id, point)
|
|
this.appendToScene(point)
|
|
return point
|
|
}
|
|
|
|
createOrUpdatePointForRuntime(item: ItemJson): Object3DLike {
|
|
const option = { isRuntime: true }
|
|
return this.createPointForEntity(item, option)
|
|
}
|
|
|
|
/**
|
|
* 创建一个点
|
|
* @param item 点的定义
|
|
* @param option 渲染选项
|
|
*/
|
|
createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike {
|
|
// 由基础类创造一个属于自己的点演示
|
|
const point = this.createPointBasic(item, option)
|
|
const matrix = this.calcMeshMatrix(item, option)
|
|
point.setMatrix4(matrix)
|
|
|
|
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
|
|
return point
|
|
}
|
|
|
|
calcMeshMatrix(item: ItemJson, option?: RendererCudOption) {
|
|
let matrix = getMatrixFromTf(item.tf)
|
|
|
|
if (item.dt.storeAt?.item) {
|
|
// 如果是库存物品, 则需要将位置和角度设置为库存位置
|
|
const rack = Model.find(item.dt.storeAt?.item)
|
|
if (!rack) {
|
|
console.error(`Cannot find rack for item ${item.id} at ${item.dt.storeAt?.item}`)
|
|
}
|
|
const rackRenderer = getRenderer(rack.t)
|
|
if (!rackRenderer) {
|
|
console.error(`Cannot find renderer for rack type ${rack.t}`)
|
|
}
|
|
const { position, rotation } = rackRenderer.getStorePlacement(rack, item.dt.storeAt.bay, item.dt.storeAt.level, item.dt.storeAt.cell)
|
|
if (!position || !rotation) {
|
|
console.error(`无法获取物品 ${item.id} 的存储位置`)
|
|
}
|
|
|
|
option.position = position
|
|
option.rotation = rotation
|
|
}
|
|
|
|
if (_.isArray(option?.position) && _.isArray(option?.rotation)) {
|
|
matrix = new THREE.Matrix4()
|
|
matrix.compose(
|
|
new THREE.Vector3(option.position[0], option.position[1], option.position[2]),
|
|
new THREE.Quaternion().setFromEuler(new THREE.Euler(
|
|
THREE.MathUtils.degToRad(option?.rotation[0]),
|
|
THREE.MathUtils.degToRad(option?.rotation[1]),
|
|
THREE.MathUtils.degToRad(option?.rotation[2]),
|
|
'XYZ'
|
|
)),
|
|
new THREE.Vector3(item.tf[2][0], item.tf[2][1], item.tf[2][2])
|
|
)
|
|
// 回写 item 数据
|
|
item.tf[0] = _.cloneDeep(option.position)
|
|
item.tf[1] = _.cloneDeep(option.rotation)
|
|
}
|
|
|
|
return matrix
|
|
}
|
|
|
|
/**
|
|
* 删除一个点
|
|
* @param id 点的唯一标识
|
|
* @param option 渲染选项
|
|
*/
|
|
deletePoint(id: string, option?: RendererCudOption) {
|
|
const object = this.tempViewport.entityManager.findObjectById(id)
|
|
if (object instanceof THREE.Object3D) {
|
|
this.removeFromScene(object)
|
|
|
|
} else if (object instanceof MeshWrap) {
|
|
// 如果是 PointManageWrap, 则需要调用管理器的删除方法
|
|
object.manager.delete(id)
|
|
|
|
} else {
|
|
throw new Error('unkown Point type', object)
|
|
}
|
|
|
|
const item = this.tempViewport.entityManager.findItemById(id)
|
|
if (item?.dt?.storeAt?.item) {
|
|
this.tempViewport.runtimeManager.removeStoreAt(item.dt.storeAt?.item, id)
|
|
}
|
|
|
|
this.tempViewport.entityManager.deleteEntityOnly(id)
|
|
this.tempViewport.entityManager.deleteObjectsOnly(id)
|
|
}
|
|
|
|
updatePointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike {
|
|
const originObject = this.tempViewport.entityManager.findObjectById(item.id)
|
|
const originItem = this.tempViewport.entityManager.findItemById(item.id)
|
|
const newObject = this.updatePoint(item, originObject, option)
|
|
newObject.visible = ((typeof item.v !== 'undefined') ? item.v : true)
|
|
|
|
// 库存位置发生改变
|
|
if (originItem.dt.storeAt?.item !== item.dt.storeAt?.item) {
|
|
if (originItem.dt.storeAt?.item) {
|
|
this.tempViewport.runtimeManager.removeStoreAt(originItem.dt.storeAt?.item, item.id)
|
|
}
|
|
if (item.dt.storeAt?.item) {
|
|
this.tempViewport.runtimeManager.addStoreAt(item.dt.storeAt?.item, item.id)
|
|
}
|
|
}
|
|
|
|
if (originObject !== newObject) {
|
|
// 如果更新后的对象和原来的对象不同, 则替换掉
|
|
setUserDataForItem(item, newObject)
|
|
|
|
this.removeFromScene(originObject as THREE.Object3D)
|
|
this.appendToScene(newObject as THREE.Object3D)
|
|
this.tempViewport.entityManager.replaceObject(item.id, newObject)
|
|
}
|
|
|
|
this.afterCreateOrUpdatePoint(item, option, newObject)
|
|
return newObject
|
|
}
|
|
|
|
/**
|
|
* 更新一个点
|
|
* @param item 点的定义
|
|
* @param option 渲染选项
|
|
*/
|
|
updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike {
|
|
const point = object
|
|
point.name = item.name || point.name
|
|
|
|
// point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
|
|
//
|
|
// point.rotation.set(
|
|
// THREE.MathUtils.degToRad(item.tf[1][0]),
|
|
// THREE.MathUtils.degToRad(item.tf[1][1]),
|
|
// THREE.MathUtils.degToRad(item.tf[1][2])
|
|
// )
|
|
//
|
|
// point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2])
|
|
// if (point instanceof PointManageWrap) {
|
|
// point.manager.syncMeshObject3D(point)
|
|
// }
|
|
const matrix = this.calcMeshMatrix(item, option)
|
|
point.setMatrix4(matrix)
|
|
|
|
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
|
|
return point
|
|
}
|
|
|
|
/**
|
|
* 创建一根线
|
|
* @param start 起点
|
|
* @param end 终点
|
|
* @param type 线的类型
|
|
* @param option 渲染选项
|
|
*/
|
|
createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
|
|
const line = this.createLineBasic(start, end, type)
|
|
const id = getLineId(start.id, end.id, type)
|
|
|
|
const startPoint = this.tempViewport.entityManager.findObjectById(start.id)
|
|
const endPoint = this.tempViewport.entityManager.findObjectById(end.id)
|
|
|
|
this.tempViewport.entityManager.findObjectById(id)
|
|
if (line instanceof Line2) {
|
|
const geom = line.geometry
|
|
geom.setFromPoints([startPoint.position, endPoint.position])
|
|
}
|
|
setUserDataForLine(start, end, type, line)
|
|
this.tempViewport.entityManager.appendLineObject(id, line)
|
|
if (line instanceof THREE.Object3D) {
|
|
this.appendToScene(line)
|
|
}
|
|
|
|
this.afterCreateOrUpdateLine(start, end, type, option, line)
|
|
}
|
|
|
|
updateLineForEntity(start: ItemJson, end: ItemJson, type: LinkType, option: any = {}) {
|
|
const lineId = getLineId(start.id, end.id, type)
|
|
const line = this.tempViewport.entityManager.findLineObjectById(lineId)
|
|
|
|
option.lineId = lineId
|
|
option.line = line
|
|
|
|
this.updateLine(start, end, type, option)
|
|
this.afterCreateOrUpdateLine(start, end, type, option, line)
|
|
}
|
|
|
|
/**
|
|
* 获取物品存放位
|
|
* 获取物品存储到地堆货位中,返回可存放的位置和角度一个 Position 和 Rotation
|
|
*/
|
|
getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0)
|
|
: { position: [number, number, number], rotation: [number, number, number] } {
|
|
throw new Error(' 不支持库存物品的添加. t=' + this.itemTypeName)
|
|
}
|
|
|
|
/**
|
|
* 更新一根线
|
|
* @param start 起点
|
|
* @param end 终点
|
|
* @param type 线的类型
|
|
* @param option 渲染选项
|
|
*/
|
|
updateLine(start: ItemJson, end: ItemJson, type: LinkType, option: any) {
|
|
const { line, lineId } = option
|
|
const startPoint = this.tempViewport.entityManager.findObjectById(start.id)
|
|
const endPoint = this.tempViewport.entityManager.findObjectById(end.id)
|
|
|
|
if (line instanceof Line2) {
|
|
const geom = line.geometry
|
|
geom.setFromPoints([startPoint.position, endPoint.position])
|
|
|
|
} else if (line instanceof LineWrap) {
|
|
line.manager.updateLine(lineId, startPoint.position, endPoint.position, line.color)
|
|
}
|
|
}
|
|
|
|
deleteLineForEntity(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
|
|
this.deleteLine(start, end, type, option)
|
|
this.afterDeleteLine(start, end, type, option)
|
|
}
|
|
|
|
/**
|
|
* 删除一根线
|
|
* @param start 起点
|
|
* @param end 终点
|
|
* @param option 渲染选项
|
|
*/
|
|
deleteLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
|
|
const lineId = getLineId(start.id, end.id, type)
|
|
const line = this.tempViewport.entityManager.findLineObjectById(lineId)
|
|
|
|
this.tempViewport.entityManager.deleteLineObjectOnly(lineId)
|
|
}
|
|
|
|
dispose() {
|
|
// 清理资源
|
|
this.isUpdating = false
|
|
}
|
|
}
|
|
|
|
|