From bd635ae4bd4c61a803aa2999365cd494aa958197 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Wed, 11 Jun 2025 18:46:13 +0800 Subject: [PATCH] =?UTF-8?q?WayRenderer=20=E7=AE=AD=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/ModelUtils.ts | 16 ++++- src/core/manager/InstancePointManager.ts | 3 +- src/modules/way/WayRenderer.ts | 114 +++++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 14dfa6e..9d083a5 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -510,7 +510,8 @@ export function linkPlaneByPoint(startPosition: THREE.Vector3, endPosition: THRE const dummy = new THREE.Object3D() dummy.position.copy(center) - dummy.quaternion.setFromRotationMatrix(new THREE.Matrix4().lookAt(startPosition, endPosition, new THREE.Vector3(0, 1, 0))) + dummy.lookAt(endPosition); + // dummy.quaternion.setFromRotationMatrix(new THREE.Matrix4().lookAt(startPosition, endPosition, new THREE.Vector3(0, 1, 0))) dummy.scale.set(width, 1, length) // Z轴缩放为长度 dummy.updateMatrix() return dummy.matrix @@ -549,3 +550,16 @@ export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) { return null } } + +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]) + ) + dummy.scale.set(tf[2][0], tf[2][1], tf[2][2]) + dummy.updateMatrix() + return dummy.matrix +} diff --git a/src/core/manager/InstancePointManager.ts b/src/core/manager/InstancePointManager.ts index bc872b2..720719b 100644 --- a/src/core/manager/InstancePointManager.ts +++ b/src/core/manager/InstancePointManager.ts @@ -246,7 +246,7 @@ export class PointManageWrap { this.meshIndex = -1 } - applyMatrix4(matrix: THREE.Matrix4): void { + applyMatrix4(matrix: THREE.Matrix4): PointManageWrap { const position = new THREE.Vector3() const quaternion = new THREE.Quaternion() const scale = new THREE.Vector3() @@ -256,6 +256,7 @@ export class PointManageWrap { this.rotation.setFromQuaternion(quaternion) this.scale.copy(scale) this.manager.syncMeshObject3D(this) + return this } createBox3(): THREE.Box3 { diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts index 014e640..35d6ce3 100644 --- a/src/modules/way/WayRenderer.ts +++ b/src/modules/way/WayRenderer.ts @@ -1,10 +1,11 @@ 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 { getLineId, getMatrixFromTf, 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 { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' import LineSegmentManager from '@/core/manager/LineSegmentManager.ts' @@ -33,6 +34,8 @@ export default class WayRenderer extends BaseRenderer { opacity: 0.2, side: THREE.DoubleSide }) + dirGeometry: THREE.PlaneGeometry + dirMaterial: THREE.Material /** * 默认点的高度, 防止和地面重合 @@ -47,9 +50,10 @@ export default class WayRenderer extends BaseRenderer { async init() { return Promise.all([ super.init(), - new THREE.TextureLoader().loadAsync(MoveLinePointPng) + new THREE.TextureLoader().loadAsync(MoveLinePointPng), + new THREE.TextureLoader().loadAsync(TriangleUrl) - ]).then(([_, texture]) => { + ]).then(([_, texture, dirTexture]) => { texture.flipY = false this.pointGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2) @@ -62,6 +66,19 @@ export default class WayRenderer extends BaseRenderer { 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 + }) }) } @@ -100,24 +117,77 @@ export default class WayRenderer extends BaseRenderer { } afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { - super.afterCreateOrUpdateLine(start, end, type, option, object) + 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) - const startPoint = this.tempViewport?.entityManager.findObjectById(start.id) - const endPoint = this.tempViewport?.entityManager.findObjectById(end.id) + if (object.userData.dirWraps) { + // 清空之前的箭头 + object.userData.dirWraps.forEach((uuid: string) => { + this.dirPointManager.deletePoint(uuid) + }) + } - this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, { - useHtmlLabel: false, - fontSize: 0.2, - color: '#333333', - format: (distance) => { - return (distance * 1000).toFixed(0) + const length = startPosition.distanceTo(endPosition) + if (length < 0.1) { + // 如果两点距离小于 0.1m,则不添加方向指示器 + return + + } else if (length < 3) { + // 如果两点距离小于 3m,则在中间添加一个方向指示器 + const dirWrap = this.dirPointManager.createPointSimple(object.uuid + '_dir') + dirWrap.position.setFromMatrixPosition(matrix) + dirWrap.rotation.setFromRotationMatrix(matrix) + dirWrap.scale.set(0.4, 0.01, 0.2) + dirWrap.manager.syncMeshObject3D(dirWrap) + + object.userData.dirWraps = [dirWrap.uuid] + + } else { + // 否则每隔 3m 添加一个方向指示器 + for (let i = 0; i < length; i += 3) { + const dirWrap = this.dirPointManager.createPointSimple(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.applyMatrix4(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) + // =============== 下面这一段不要删除,用来显示数字标签 ============================= + // super.afterDeleteLine(start, end, type, option, object) + // this.tempViewport.labelManager.removeLabel(object) + // ========================================================================== + + if (object.userData.dirWraps) { + // 清空之前的箭头 + object.userData.dirWraps.forEach((uuid: string) => { + this.dirPointManager.deletePoint(uuid) + }) + } } dispose() { @@ -153,4 +223,18 @@ export default class WayRenderer extends BaseRenderer { Constract.MAX_WAY_INSTANCES) ) } + + get dirPointManager(): InstancePointManager { + if (!this.tempViewport) { + throw new Error('tempViewport is not set.') + } + return this.tempViewport.getOrCreatePointManager(this.itemTypeName + '_dir', () => + // 构建 InstanceMesh 代理对象 + InstancePointManager.create(this.itemTypeName + '_dir', + this.tempViewport, + this.dirGeometry, + this.dirMaterial, + Constract.MAX_WAY_LINE_INSTANCES) + ) + } }