import * as THREE from 'three' import BaseRenderer from '@/core/base/BaseRenderer.ts' import MoveLinePointPng from '@/assets/images/moveline_point.png' import { getLineId, linkPlaneByPoint } from '@/core/ModelUtils.ts' import Constract from '@/core/Constract.ts' import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' import TriangleUrl from '@/assets/images/conveyor/shapes/triangle.png' import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts' /** * AGV行走路线渲染器 point 是二维码站点 */ export default class WayRenderer extends BaseRenderer { static LABEL_NAME = 'way_label' static POINT_NAME = 'way_point' static LINE_NAME = 'way_line' pointGeometry: THREE.BufferGeometry pointMaterial: THREE.Material // lineMaterial: LineMaterial = new LineMaterial({ // color: 0xa0cfff, // linewidth: 0.8, // vertexColors: false, // dashed: false, // gapSize: 0, // worldUnits: true // }) lineGeometry: THREE.BufferGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2) lineMaterial = new THREE.MeshBasicMaterial({ color: 0xa0cfff, transparent: true, opacity: 0.2, side: THREE.DoubleSide }) dirGeometry: THREE.PlaneGeometry dirMaterial: THREE.Material /** * 默认点的高度, 防止和地面重合 */ readonly defulePositionY: number = Constract.HEIGHT_WAY readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.5, 0.1, 0.5) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) readonly rendererOption = { lineWidth: 0.8 } async init() { return Promise.all([ super.init(), new THREE.TextureLoader().loadAsync(MoveLinePointPng), new THREE.TextureLoader().loadAsync(TriangleUrl) ]).then(([_, texture, dirTexture]) => { texture.flipY = false this.pointGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2) this.pointGeometry.center() this.pointMaterial = new THREE.MeshBasicMaterial({ map: texture, transparent: true, depthWrite: false, side: THREE.DoubleSide }) this.pointMaterial.needsUpdate = true this.dirGeometry = new THREE.PlaneGeometry(1, 1) this.dirGeometry.rotateX(-Math.PI / 2) .rotateY(Math.PI / 2) this.dirGeometry.center() this.dirMaterial = new THREE.MeshBasicMaterial({ map: dirTexture, transparent: true, opacity: 1, depthWrite: false, side: THREE.DoubleSide }) }) } createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike { // 不允许改变高度/角度/大小 item.tf = [ [item.tf[0][0], this.defulePositionY, item.tf[0][2]], [this.defaultRotation.x, this.defaultRotation.y, this.defaultRotation.z], [this.defaultScale.x, this.defaultScale.y, this.defaultScale.z] ] return this.pointManager.createPoint(item) } createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike { const lineId = getLineId(start.id, end.id, type) // 用 PlaneGeometry 替代线段 const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2]) const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2]) const wrap = this.lineManager.createPointSimple(lineId) const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth) wrap.applyMatrix4(matrix) return wrap } updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { const lineId = getLineId(start.id, end.id, type) const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2]) const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2]) const wrap = this.tempViewport.entityManager.findLineObjectById(lineId) as PointManageWrap const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth) wrap.applyMatrix4(matrix) } afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2]) const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2]) // =============== 下面这一段不要删除,用来显示数字标签 ============================= // this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, { // useHtmlLabel: false, // fontSize: 0.2, // color: '#333333', // format: (distance) => { // return (distance * 1000).toFixed(0) // } // }) // ========================================================================== const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth) if (object.userData.dirWraps) { // 清空之前的箭头 object.userData.dirWraps.forEach((uuid: string) => { this.dirPointManager.delete(uuid) }) } const length = startPosition.distanceTo(endPosition) if (length < 0.1) { // 如果两点距离小于 0.1m,则不添加方向指示器 return } else if (length < 3) { // 如果两点距离小于 3m,则在中间添加一个方向指示器 const dirWrap = this.dirPointManager.create(object.uuid + '_dir') const dummy = new THREE.Object3D() dummy.position.setFromMatrixPosition(matrix) dummy.rotation.setFromRotationMatrix(matrix) dummy.scale.set(0.4, 0.01, 0.2) dummy.updateMatrix() dirWrap.setMatrix4(dummy.matrix) object.userData.dirWraps = [dirWrap.uuid] } else { // 否则每隔 3m 添加一个方向指示器 for (let i = 0; i < length; i += 3) { const dirWrap = this.dirPointManager.create(object.uuid + '_dir_' + i) const position = startPosition.clone().lerp(endPosition, i / length) const dummy = new THREE.Object3D() dummy.position.copy(position) dummy.lookAt(endPosition) dummy.scale.set(0.4, 0.01, 0.2) dummy.updateMatrix() dirWrap.setMatrix4(dummy.matrix) if (!object.userData.dirWraps) { object.userData.dirWraps = [] } object.userData.dirWraps.push(dirWrap.uuid) } } } afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { // =============== 下面这一段不要删除,用来显示数字标签 ============================= // super.afterDeleteLine(start, end, type, option, object) // this.tempViewport.labelManager.removeLabel(object) // ========================================================================== if (object.userData.dirWraps) { // 清空之前的箭头 object.userData.dirWraps.forEach((uuid: string) => { this.dirPointManager.delete(uuid) }) } } dispose() { super.dispose() this.pointMaterial?.dispose() this.lineMaterial?.dispose() } get lineManager(): InstancePointManager { if (!this.tempViewport) { throw new Error('tempViewport is not set.') } return this.tempViewport.getOrCreatePointManager(this.itemTypeName + '_line', () => // 构建 LineSegment.points 代理对象 InstancePointManager.create(this.itemTypeName + '_line', this.tempViewport, this.lineGeometry, this.lineMaterial, false, false) ) } get pointManager(): InstancePointManager { if (!this.tempViewport) { throw new Error('tempViewport is not set.') } return this.tempViewport.getOrCreatePointManager(this.itemTypeName, () => // 构建 InstanceMesh 代理对象 InstancePointManager.create(this.itemTypeName, this.tempViewport, this.pointGeometry, this.pointMaterial, true, true) ) } get dirPointManager(): InstanceMeshManager { if (!this.tempViewport) { throw new Error('tempViewport is not set.') } return this.tempViewport.getOrCreateMeshManager(this.itemTypeName + '_dir', () => // 构建 InstanceMesh 代理对象 new InstanceMeshManager(this.itemTypeName + '_dir', this.tempViewport, this.dirGeometry, this.dirMaterial, false, false) ) } }