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.
 
 
 

337 lines
10 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 {
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)
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)
let matrix = getMatrixFromTf(item.tf)
if (item.dt.storeAt?.item) {
// 如果是库存物品, 则需要将位置和角度设置为库存位置
const rack = this.tempViewport.stateManager.findItemById(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(this.tempViewport, item)
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)
}
point.setMatrix4(matrix)
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
return point
}
/**
* 删除一个点
* @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)
}
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 newObject = this.updatePoint(item, originObject, option)
newObject.visible = ((typeof item.v !== 'undefined') ? item.v : true)
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)
// }
point.matrix = getMatrixFromTf(item.tf)
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(viewport: Viewport, item: ItemJson)
: { 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
}
}