From 9e054f572acda9347b7abc80fa9848a4e94da9f8 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Wed, 11 Jun 2025 16:01:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?WayRenderer=20=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96,=20=E5=8E=8B=E5=8A=9B=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Model3DView.vue | 1 - src/core/Constract.ts | 1 + src/core/manager/LineSegmentManager.ts | 10 +- src/example/example1.js | 18 +- src/modules/carton/CartonRenderer.ts | 8 +- src/modules/measure/MeasureRenderer.ts | 52 +++--- src/modules/way/WayRenderer.ts | 292 ++++++++++++++++++--------------- 7 files changed, 211 insertions(+), 171 deletions(-) diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index a93385d..9b75bac 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -870,7 +870,6 @@ function handleFileChange(file) { const arrayBuffer = reader.result //@ts-ignore loader.parseAsync(arrayBuffer, '').then((content) => { - debugger addGroupToScene(content.scene) }) diff --git a/src/core/Constract.ts b/src/core/Constract.ts index de837ab..81d1932 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -33,6 +33,7 @@ const Constract = Object.freeze({ HEIGHT_WAY_LABEL: 0.03, HEIGHT_WAY_LINE: 0.02, + MAX_WAY_INSTANCES: 10000, MAX_MEASURE_INSTANCES: 1000, MAX_GSTORE_INSTANCES: 10000, MAX_PALLET_INSTANCES: 10000, diff --git a/src/core/manager/LineSegmentManager.ts b/src/core/manager/LineSegmentManager.ts index db7eb40..c17e740 100644 --- a/src/core/manager/LineSegmentManager.ts +++ b/src/core/manager/LineSegmentManager.ts @@ -4,13 +4,15 @@ import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeome import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2' import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' import type { Object3DLike, Vector3Like } from '@/types/ModelTypes.ts' +import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry' +import { Line2 } from 'three/examples/jsm/lines/Line2' /** * 线段管理器 */ export default class LineSegmentManager { private readonly viewport: Viewport - private readonly lineGeometry: LineSegmentsGeometry + private readonly lineGeometry: LineGeometry // LineSegmentsGeometry private readonly lineMaterial: LineMaterial private readonly lineSegments: LineSegments2 private readonly segments: Map = new Map() @@ -242,7 +244,7 @@ export default class LineSegmentManager { this.lineGeometry.setColors(this.colorArray) // 设置实例计数为可见线段数量 - this.lineGeometry.instanceCount = Array.from(this.segments.values()).filter(s => s.visible).length + // this.lineGeometry.instanceCount = Array.from(this.segments.values()).filter(s => s.visible).length this.needsUpdate = false } @@ -257,10 +259,10 @@ export default class LineSegmentManager { this.viewport = viewport this.lineMaterial = lineMaterial - this.lineGeometry = new LineSegmentsGeometry() + this.lineGeometry = new LineGeometry() // new LineSegmentsGeometry() // 创建线段的渲染对象 - this.lineSegments = new LineSegments2(this.lineGeometry, this.lineMaterial) + this.lineSegments = new Line2(this.lineGeometry, this.lineMaterial) this.lineSegments.name = name this.lineSegments.frustumCulled = false this.viewport.scene.add(this.lineSegments) diff --git a/src/example/example1.js b/src/example/example1.js index 91774a7..4a919ab 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -330,13 +330,27 @@ export default { t: 'carton', v: true, tf: [[0, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]], - dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } + dt: { in: [], out: [], center: [] } + }, + { + id: 'way1', + t: 'way', + v: true, + tf: [[2, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]], + dt: { in: [], out: [], center: ['way2'] } + }, + { + id: 'way2', + t: 'way', + v: true, + tf: [[5, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]], + dt: { in: [], out: [], center: ['way1'] } } ] }, { catalogCode: '__f1', t: 'floor', - items: buildAgvPerformanceData('measure', 100, 100) + items: buildAgvPerformanceData('way', 100, 100) }, { catalogCode: '__f2', t: 'floor', diff --git a/src/modules/carton/CartonRenderer.ts b/src/modules/carton/CartonRenderer.ts index c8c6dd8..80c53a2 100644 --- a/src/modules/carton/CartonRenderer.ts +++ b/src/modules/carton/CartonRenderer.ts @@ -37,11 +37,15 @@ export default class PalletRenderer extends BaseRenderer { this.cartonGeometry = mesh.geometry this.cartonMaterial = new THREE.MeshPhongMaterial({ color: 0xc29a70 }) // mesh.material as THREE.Material this.cartonGeometry.scale(0.01, 0.01, 0.01) - this.cartonGeometry.rotateX(Math.PI / 2) + // this.cartonGeometry.rotateX(-Math.PI / 2) + this.cartonGeometry.center() + this.cartonGeometry.translate(0, 0.3, 0) + cartonTexture.flipY = true cartonTexture.wrapS = THREE.RepeatWrapping cartonTexture.wrapT = THREE.RepeatWrapping - cartonTexture.repeat.set(1, 1) + cartonTexture.repeat.set(1, -1) + cartonTexture.offset.y = 1 //@ts-ignore this.cartonMaterial.map = cartonTexture //@ts-ignore diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index ebe4285..da57375 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -34,32 +34,6 @@ export default class MeasureRenderer extends BaseRenderer { readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) - 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, - Constract.MAX_MEASURE_INSTANCES) - ) - } - - get lineSegmentManager(): LineSegmentManager { - if (!this.tempViewport) { - throw new Error('tempViewport is not set.') - } - return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () => - // 构建 LineSegment.points 代理对象 - LineSegmentManager.create(this.itemTypeName, - this.tempViewport, - this.lineMaterial) - ) - } - /** * 所有的点,必须使用同一个尺寸, 改属性也无效 */ @@ -109,6 +83,32 @@ export default class MeasureRenderer extends BaseRenderer { this.tempViewport.labelManager.removeLabel(object) } + 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, + Constract.MAX_MEASURE_INSTANCES) + ) + } + + get lineSegmentManager(): LineSegmentManager { + if (!this.tempViewport) { + throw new Error('tempViewport is not set.') + } + return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () => + // 构建 LineSegment.points 代理对象 + LineSegmentManager.create(this.itemTypeName, + this.tempViewport, + this.lineMaterial) + ) + } + dispose() { super.dispose() this.pointMaterial?.dispose() diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts index b01ebeb..57d89b6 100644 --- a/src/modules/way/WayRenderer.ts +++ b/src/modules/way/WayRenderer.ts @@ -4,10 +4,12 @@ import { Text } from 'troika-three-text' import MoveLinePointPng from '@/assets/images/moveline_point.png' import SimSunTTF from '@/assets/fonts/simsunb.ttf' import { getLineId } from '@/core/ModelUtils.ts' -import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' -import { numberToString } from '@/utils/webutils.ts' import Constract from '@/core/Constract.ts' import type { ExtrudeGeometryOptions } from 'three/src/geometries/ExtrudeGeometry' +import InstancePointManager from '@/core/manager/InstancePointManager.ts' +import type { Object3DLike } from '@/types/ModelTypes.ts' +import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' +import LineSegmentManager from '@/core/manager/LineSegmentManager.ts' /** * 辅助测量工具渲染器 @@ -17,166 +19,184 @@ export default class WayRenderer extends BaseRenderer { static POINT_NAME = 'way_point' static LINE_NAME = 'way_line' + pointGeometry: THREE.BufferGeometry pointMaterial: THREE.Material - lineMaterial = new THREE.MeshBasicMaterial({ + lineMaterial: LineMaterial = new LineMaterial({ color: 0xa0cfff, - transparent: true, - opacity: 0.2, - side: THREE.DoubleSide + linewidth: 0.8, + vertexColors: false, + dashed: false, + gapSize: 0, + worldUnits: true }) + // lineMaterial = new THREE.MeshBasicMaterial({ + // color: 0xa0cfff, + // transparent: true, + // opacity: 0.2, + // side: THREE.DoubleSide + // }) /** * 默认点的高度, 防止和地面重合 */ readonly defulePositionY: number = Constract.HEIGHT_WAY - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) - readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) - - constructor(itemTypeName: string) { - super(itemTypeName) - } + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.5, 0.1, 0.5) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) async init() { return Promise.all([ super.init(), - this.loadFont() - ]) - } - - async loadFont() { - return new Promise((resolve, reject) => { - new THREE.TextureLoader().load( - MoveLinePointPng, - (texture) => { - this.pointMaterial = new THREE.SpriteMaterial({ - map: texture, - transparent: true, - side: THREE.DoubleSide - }) - resolve() - }, - undefined, - function(err) { - reject(err) - } - ) - }) - } - - /** - * 所有的点,必须使用同一个尺寸, 改属性也无效 - */ - override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { - super.afterCreateOrUpdatePoint(item, option, object) - - const point = object - point.position.y = this.defulePositionY - point.scale.set(this.defaultScale.x, this.defaultScale.y, this.defaultScale.z) - point.rotation.set( - THREE.MathUtils.degToRad(this.defaultRotation.x), - THREE.MathUtils.degToRad(this.defaultRotation.y), - THREE.MathUtils.degToRad(this.defaultRotation.z) - ) - } - - private _createOrUpdateLine(startPosition: THREE.Vector3, endPosition: THREE.Vector3, type: LinkType) { - const width = 1 - const halfWidth = width / 2 - - const path = new THREE.LineCurve3( - new THREE.Vector3(startPosition.x, startPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE), - new THREE.Vector3(endPosition.x, endPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE) - ) + new THREE.TextureLoader().loadAsync(MoveLinePointPng) - const shape = new THREE.Shape() - shape.moveTo(halfWidth, -halfWidth) - shape.lineTo(halfWidth, halfWidth) + ]).then(([_, texture]) => { + texture.flipY = false - const extrudeSettings: ExtrudeGeometryOptions = { - steps: 2, // 沿路径的分段数 - depth: 1, // 实际由路径长度决定 - bevelEnabled: false, // 禁用倒角 - extrudePath: path // 挤出路径 - } - - const extrudedGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings) - extrudedGeometry.rotateX(Math.PI / 2) + this.pointGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2) + this.pointGeometry.center() - return new THREE.Mesh(extrudedGeometry, this.lineMaterial) + this.pointMaterial = new THREE.MeshBasicMaterial({ + map: texture, + transparent: true, + depthWrite: false, + side: THREE.DoubleSide + }) + this.pointMaterial.needsUpdate = true + }) } - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - const startPosition = new THREE.Vector3(start.tf[0][0], 0, start.tf[0][2]) - const endPosition = new THREE.Vector3(end.tf[0][0], 0, end.tf[0][2]) - - const group = new THREE.Group() - const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) - group.add(lineMesh) - - - const midPoint = new THREE.Vector3() - .addVectors(startPosition, endPosition) - .multiplyScalar(0.5) - - const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) - - const label = new Text() - label.text = distance - label.font = SimSunTTF - label.fontSize = 0.2 - label.color = '#5f5f5f' - label.opacity = 0.8 - label.anchorX = 'center' - label.anchorY = 'middle' - label.depthOffset = 1 - label.material.depthTest = false - label.name = WayRenderer.LABEL_NAME - label.quaternion.copy(this.tempViewport.camera.quaternion) - label.sync() - label.position.set(midPoint.x, Constract.HEIGHT_WAY_LABEL, midPoint.z) - - group.add(label) - - return group + 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) } - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - super.updateLine(start, end, type, option) - - const startPosition = new THREE.Vector3(start.tf[0][0], 0.01, start.tf[0][2]) - const endPosition = new THREE.Vector3(end.tf[0][0], 0.01, end.tf[0][2]) - + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike { const lineId = getLineId(start.id, end.id, type) - const group = this.tempViewport.entityManager.findLineObjectById(lineId) - - // 清空group里的元素 - const label: Text = group.children[1] - group.clear() - - const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) - group.add(lineMesh) - - const midPoint = new THREE.Vector3() - .addVectors(startPosition, endPosition) - .multiplyScalar(0.5) - - const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) - label.text = distance - label.quaternion.copy(this.tempViewport.camera.quaternion) - label.sync() - label.position.set(midPoint.x, midPoint.y, midPoint.z) - group.add(label) + return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color) } - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - const obj = new THREE.Sprite(this.pointMaterial as THREE.SpriteMaterial) - obj.name = WayRenderer.POINT_NAME - return obj - } + // private _createOrUpdateLine(startPosition: THREE.Vector3, endPosition: THREE.Vector3, type: LinkType) { + // const width = 1 + // const halfWidth = width / 2 + // + // const path = new THREE.LineCurve3( + // new THREE.Vector3(startPosition.x, startPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE), + // new THREE.Vector3(endPosition.x, endPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE) + // ) + // + // const shape = new THREE.Shape() + // shape.moveTo(halfWidth, -halfWidth) + // shape.lineTo(halfWidth, halfWidth) + // + // const extrudeSettings: ExtrudeGeometryOptions = { + // steps: 2, // 沿路径的分段数 + // depth: 1, // 实际由路径长度决定 + // bevelEnabled: false, // 禁用倒角 + // extrudePath: path // 挤出路径 + // } + // + // const extrudedGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings) + // extrudedGeometry.rotateX(Math.PI / 2) + // + // return new THREE.Mesh(extrudedGeometry, this.lineMaterial) + // } + + // createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { + // const startPosition = new THREE.Vector3(start.tf[0][0], 0, start.tf[0][2]) + // const endPosition = new THREE.Vector3(end.tf[0][0], 0, end.tf[0][2]) + // + // const group = new THREE.Group() + // const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) + // group.add(lineMesh) + // + // + // const midPoint = new THREE.Vector3() + // .addVectors(startPosition, endPosition) + // .multiplyScalar(0.5) + // + // const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) + // + // const label = new Text() + // label.text = distance + // label.font = SimSunTTF + // label.fontSize = 0.2 + // label.color = '#5f5f5f' + // label.opacity = 0.8 + // label.anchorX = 'center' + // label.anchorY = 'middle' + // label.depthOffset = 1 + // label.material.depthTest = false + // label.name = WayRenderer.LABEL_NAME + // label.quaternion.copy(this.tempViewport.camera.quaternion) + // label.sync() + // label.position.set(midPoint.x, Constract.HEIGHT_WAY_LABEL, midPoint.z) + // + // group.add(label) + // + // return group + // } + // + // updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + // super.updateLine(start, end, type, option) + // + // const startPosition = new THREE.Vector3(start.tf[0][0], 0.01, start.tf[0][2]) + // const endPosition = new THREE.Vector3(end.tf[0][0], 0.01, end.tf[0][2]) + // + // const lineId = getLineId(start.id, end.id, type) + // const group = this.tempViewport.entityManager.findLineObjectById(lineId) + // + // // 清空group里的元素 + // const label: Text = group.children[1] + // group.clear() + // + // const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) + // group.add(lineMesh) + // + // const midPoint = new THREE.Vector3() + // .addVectors(startPosition, endPosition) + // .multiplyScalar(0.5) + // + // const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) + // label.text = distance + // label.quaternion.copy(this.tempViewport.camera.quaternion) + // label.sync() + // label.position.set(midPoint.x, midPoint.y, midPoint.z) + // group.add(label) + // } dispose() { super.dispose() this.pointMaterial?.dispose() this.lineMaterial?.dispose() } + + get lineSegmentManager(): LineSegmentManager { + if (!this.tempViewport) { + throw new Error('tempViewport is not set.') + } + return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () => + // 构建 LineSegment.points 代理对象 + LineSegmentManager.create(this.itemTypeName, + this.tempViewport, + this.lineMaterial) + ) + } + + 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, + Constract.MAX_WAY_INSTANCES) + ) + } } From 0b6f551a686797b2f47575bdd6b13f0c93223c32 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Wed, 11 Jun 2025 17:32:49 +0800 Subject: [PATCH 2/3] =?UTF-8?q?WayRenderer=20PanelGeometry=20=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3=20Line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Model3DView.vue | 3 + src/core/Constract.ts | 3 +- src/core/ModelUtils.ts | 60 ++++------- src/core/base/BaseRenderer.ts | 24 +++-- src/core/manager/EntityManager.ts | 2 +- src/core/manager/InstancePointManager.ts | 25 +++++ src/core/manager/LabelManager.ts | 7 +- src/modules/way/WayRenderer.ts | 176 ++++++++++++------------------- 8 files changed, 140 insertions(+), 160 deletions(-) diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index 653de6d..d57aba6 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -176,6 +176,9 @@ watch(() => restate.mode, (newVal) => { } }) +/** + * 添加输送线 + */ function addConveyor() { const conveyorLength = 10 // 长度 const conveyorWidth = 0.8 // 宽度 diff --git a/src/core/Constract.ts b/src/core/Constract.ts index 81d1932..800e6b0 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -34,8 +34,9 @@ const Constract = Object.freeze({ HEIGHT_WAY_LINE: 0.02, MAX_WAY_INSTANCES: 10000, + MAX_WAY_LINE_INSTANCES: 40000, MAX_MEASURE_INSTANCES: 1000, MAX_GSTORE_INSTANCES: 10000, - MAX_PALLET_INSTANCES: 10000, + MAX_PALLET_INSTANCES: 10000 }) export default Constract diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 32b988e..14dfa6e 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -495,6 +495,27 @@ export async function loadByUrl(url): Promise { }) } +/** + * 通过两点位置生成一个平面矩阵, z轴为平面长度, x轴为平面宽度 + * @param startPosition + * @param endPosition + */ +export function linkPlaneByPoint(startPosition: THREE.Vector3, endPosition: THREE.Vector3, width: number): THREE.Matrix4 { + const dir = new THREE.Vector3().subVectors(endPosition, startPosition) + const length = dir.length() // 平面长度 = 两点距离 + dir.normalize() + + const center = new THREE.Vector3().addVectors(startPosition, endPosition).multiplyScalar(0.5) + // const orientation = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), dir) + + 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.scale.set(width, 1, length) // Z轴缩放为长度 + dummy.updateMatrix() + return dummy.matrix +} + export async function loadGlbModule(url: string): Promise { const response = await axios.get(url, { responseType: 'arraybuffer' @@ -528,42 +549,3 @@ export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) { return null } } - -export async function generateMaterialWithTexture(color: number, quality: number, info: any, textureInfos: any) { - let materialClone - - let url = color?.toString() + ' ' + JSON.stringify(textureInfos) + ' ' + quality + ' ' + (info == undefined ? '' : JSON.stringify(info)) - - if (!this.materialsWithTextureMap[url]) { - let meterialTmp = await this.generateMaterial(color, quality, info) - for (let textureInfo of textureInfos) { - if (textureInfo.path != undefined && textureInfo.repeatX != undefined && textureInfo.repeatY != undefined) { - let textureTmp = await this.generateTexture(textureInfo.path, textureInfo.repeatX, textureInfo.repeatY) - - if (textureInfo.rotation != undefined) { - textureTmp.rotation = textureInfo.rotation - } - if (textureInfo.normalMapType != undefined) { - textureTmp.normalMapType = textureInfo.normalMapType - } - - if (textureInfo.type == Model.TextureTypeEnum.NormalMap) { - if (quality != Model.MaterialQualityEnum.Low) { - meterialTmp.normalMap = textureTmp - } - } else if (textureInfo.type == Model.TextureTypeEnum.AlphaMap) { - meterialTmp.alphaMap = textureTmp - } else if (textureInfo.type == Model.TextureTypeEnum.Map) { - meterialTmp.map = textureTmp - } else { - meterialTmp.map = textureTmp - } - meterialTmp.needsUpdate = true - } - } - this.materialsWithTextureMap[url] = meterialTmp - } - materialClone = this.materialsWithTextureMap[url] - - return materialClone -} diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index f0e6786..301449c 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -1,6 +1,6 @@ import type Viewport from '@/core/engine/Viewport' import * as THREE from 'three' -import { getLineId, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts' +import { getLineId, linkPlaneByPoint, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts' import { Line2 } from 'three/examples/jsm/lines/Line2' import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts' import Constract from '@/core/Constract.ts' @@ -223,11 +223,25 @@ export default abstract class BaseRenderer { this.tempViewport.entityManager.appendLineObject(id, line) if (line instanceof THREE.Object3D) { this.appendToScene(line) + + } else if (line instanceof PointManageWrap) { + line.manager.syncMeshObject3D(line) } this.afterCreateOrUpdateLine(start, end, type, option, line) } + updateLineEntity(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) + } + /** * 更新一根线 * @param start 起点 @@ -235,10 +249,8 @@ export default abstract class BaseRenderer { * @param type 线的类型 * @param option 渲染选项 */ - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - const lineId = getLineId(start.id, end.id, type) - - const line = this.tempViewport.entityManager.findLineObjectById(lineId) + 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) @@ -249,8 +261,6 @@ export default abstract class BaseRenderer { } else if (line instanceof LineManageWrap) { line.manager.updateLine(lineId, startPoint.position, endPoint.position, line.color) } - - this.afterCreateOrUpdateLine(start, end, type, option, line) } /** diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index d4128c7..bb4a831 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -226,7 +226,7 @@ export default class EntityManager { // 只通知起点对应的渲染器 continue } - renderer.updateLine(start, end, lineDiffItem.type) + renderer.updateLineEntity(start, end, lineDiffItem.type) } // "线"删除 diff --git a/src/core/manager/InstancePointManager.ts b/src/core/manager/InstancePointManager.ts index 05effac..bc872b2 100644 --- a/src/core/manager/InstancePointManager.ts +++ b/src/core/manager/InstancePointManager.ts @@ -19,6 +19,19 @@ export default class InstancePointManager { // instanceId -> itemId private __indexIdMap = new Map() + createPointSimple(id: string): PointManageWrap { + //@ts-ignore + const wrap = this.createPoint({ + id: id, + name: id, + v: true, + tf: [[0, 0, 0], [0, 0, 0], [0, 0, 0]], + dt: {} + }) + + return wrap + } + /** * 创建点实例 * @param item 点数据 @@ -233,6 +246,18 @@ export class PointManageWrap { this.meshIndex = -1 } + applyMatrix4(matrix: THREE.Matrix4): void { + const position = new THREE.Vector3() + const quaternion = new THREE.Quaternion() + const scale = new THREE.Vector3() + matrix.decompose(position, quaternion, scale) + + this.position.copy(position) + this.rotation.setFromQuaternion(quaternion) + this.scale.copy(scale) + this.manager.syncMeshObject3D(this) + } + createBox3(): THREE.Box3 { const instancedMesh = this.manager.instancedMesh const matrix = new THREE.Matrix4() diff --git a/src/core/manager/LabelManager.ts b/src/core/manager/LabelManager.ts index cd5aa04..e2713dd 100644 --- a/src/core/manager/LabelManager.ts +++ b/src/core/manager/LabelManager.ts @@ -28,6 +28,8 @@ export interface LabelOption { */ padding?: number | string text?: string + + format?: (distance: number) => string } /** @@ -58,7 +60,10 @@ export default class LabelManager implements IControls { // 计算距离 const distance = startPos.distanceTo(endPos) - const text = distance.toFixed(2) + ' m' + let text = distance.toFixed(2) + ' m' + if (option.format) { + text = option.format(distance) + } this.updateLabel(lineRef, text) return labelObj diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts index 57d89b6..014e640 100644 --- a/src/modules/way/WayRenderer.ts +++ b/src/modules/way/WayRenderer.ts @@ -1,18 +1,15 @@ import * as THREE from 'three' import BaseRenderer from '@/core/base/BaseRenderer.ts' -import { Text } from 'troika-three-text' import MoveLinePointPng from '@/assets/images/moveline_point.png' -import SimSunTTF from '@/assets/fonts/simsunb.ttf' -import { getLineId } from '@/core/ModelUtils.ts' +import { getLineId, linkPlaneByPoint } from '@/core/ModelUtils.ts' import Constract from '@/core/Constract.ts' -import type { ExtrudeGeometryOptions } from 'three/src/geometries/ExtrudeGeometry' -import InstancePointManager from '@/core/manager/InstancePointManager.ts' +import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' import LineSegmentManager from '@/core/manager/LineSegmentManager.ts' /** - * 辅助测量工具渲染器 + * AGV行走路线渲染器 point 是二维码站点 */ export default class WayRenderer extends BaseRenderer { static LABEL_NAME = 'way_label' @@ -21,20 +18,21 @@ export default class WayRenderer extends BaseRenderer { pointGeometry: THREE.BufferGeometry pointMaterial: THREE.Material - lineMaterial: LineMaterial = new LineMaterial({ - color: 0xa0cfff, - linewidth: 0.8, - vertexColors: false, - dashed: false, - gapSize: 0, - worldUnits: true - }) - // lineMaterial = new THREE.MeshBasicMaterial({ + // lineMaterial: LineMaterial = new LineMaterial({ // color: 0xa0cfff, - // transparent: true, - // opacity: 0.2, - // side: THREE.DoubleSide + // 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 + }) /** * 默认点的高度, 防止和地面重合 @@ -42,6 +40,9 @@ export default class WayRenderer extends BaseRenderer { 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([ @@ -76,97 +77,48 @@ export default class WayRenderer extends BaseRenderer { createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike { const lineId = getLineId(start.id, end.id, type) - return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color) + + // 用 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) } - // private _createOrUpdateLine(startPosition: THREE.Vector3, endPosition: THREE.Vector3, type: LinkType) { - // const width = 1 - // const halfWidth = width / 2 - // - // const path = new THREE.LineCurve3( - // new THREE.Vector3(startPosition.x, startPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE), - // new THREE.Vector3(endPosition.x, endPosition.z, halfWidth - Constract.HEIGHT_WAY_LINE) - // ) - // - // const shape = new THREE.Shape() - // shape.moveTo(halfWidth, -halfWidth) - // shape.lineTo(halfWidth, halfWidth) - // - // const extrudeSettings: ExtrudeGeometryOptions = { - // steps: 2, // 沿路径的分段数 - // depth: 1, // 实际由路径长度决定 - // bevelEnabled: false, // 禁用倒角 - // extrudePath: path // 挤出路径 - // } - // - // const extrudedGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings) - // extrudedGeometry.rotateX(Math.PI / 2) - // - // return new THREE.Mesh(extrudedGeometry, this.lineMaterial) - // } - - // createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - // const startPosition = new THREE.Vector3(start.tf[0][0], 0, start.tf[0][2]) - // const endPosition = new THREE.Vector3(end.tf[0][0], 0, end.tf[0][2]) - // - // const group = new THREE.Group() - // const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) - // group.add(lineMesh) - // - // - // const midPoint = new THREE.Vector3() - // .addVectors(startPosition, endPosition) - // .multiplyScalar(0.5) - // - // const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) - // - // const label = new Text() - // label.text = distance - // label.font = SimSunTTF - // label.fontSize = 0.2 - // label.color = '#5f5f5f' - // label.opacity = 0.8 - // label.anchorX = 'center' - // label.anchorY = 'middle' - // label.depthOffset = 1 - // label.material.depthTest = false - // label.name = WayRenderer.LABEL_NAME - // label.quaternion.copy(this.tempViewport.camera.quaternion) - // label.sync() - // label.position.set(midPoint.x, Constract.HEIGHT_WAY_LABEL, midPoint.z) - // - // group.add(label) - // - // return group - // } - // - // updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - // super.updateLine(start, end, type, option) - // - // const startPosition = new THREE.Vector3(start.tf[0][0], 0.01, start.tf[0][2]) - // const endPosition = new THREE.Vector3(end.tf[0][0], 0.01, end.tf[0][2]) - // - // const lineId = getLineId(start.id, end.id, type) - // const group = this.tempViewport.entityManager.findLineObjectById(lineId) - // - // // 清空group里的元素 - // const label: Text = group.children[1] - // group.clear() - // - // const lineMesh = this._createOrUpdateLine(startPosition, endPosition, type) - // group.add(lineMesh) - // - // const midPoint = new THREE.Vector3() - // .addVectors(startPosition, endPosition) - // .multiplyScalar(0.5) - // - // const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) - // label.text = distance - // label.quaternion.copy(this.tempViewport.camera.quaternion) - // label.sync() - // label.position.set(midPoint.x, midPoint.y, midPoint.z) - // group.add(label) - // } + afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { + super.afterCreateOrUpdateLine(start, end, type, option, object) + + const startPoint = this.tempViewport?.entityManager.findObjectById(start.id) + const endPoint = this.tempViewport?.entityManager.findObjectById(end.id) + + this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, { + useHtmlLabel: false, + fontSize: 0.2, + color: '#333333', + format: (distance) => { + return (distance * 1000).toFixed(0) + } + }) + } + + afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { + super.afterDeleteLine(start, end, type, option, object) + this.tempViewport.labelManager.removeLabel(object) + } dispose() { super.dispose() @@ -174,15 +126,17 @@ export default class WayRenderer extends BaseRenderer { this.lineMaterial?.dispose() } - get lineSegmentManager(): LineSegmentManager { + get lineManager(): InstancePointManager { if (!this.tempViewport) { throw new Error('tempViewport is not set.') } - return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () => + return this.tempViewport.getOrCreatePointManager(this.itemTypeName + '_line', () => // 构建 LineSegment.points 代理对象 - LineSegmentManager.create(this.itemTypeName, + InstancePointManager.create(this.itemTypeName + '_line', this.tempViewport, - this.lineMaterial) + this.lineGeometry, + this.lineMaterial, + Constract.MAX_WAY_LINE_INSTANCES) ) } From faf3c770d6ed4ae0e44b1edd3c095e4dcc3d417c Mon Sep 17 00:00:00 2001 From: luoyifan Date: Wed, 11 Jun 2025 17:39:44 +0800 Subject: [PATCH 3/3] =?UTF-8?q?WayRenderer=20PanelGeometry=20=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3=20Line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/example/ExampleUtil.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/example/ExampleUtil.js b/src/example/ExampleUtil.js index 7c61487..39c814d 100644 --- a/src/example/ExampleUtil.js +++ b/src/example/ExampleUtil.js @@ -68,17 +68,18 @@ export function buildAgvPerformanceData(t, rows, cols) { } } - data.get('wp_0_0').dt.center.push('wp_0_' + (cols - 1)) - data.get('wp_0_' + (cols - 1)).dt.center.push('wp_0_0') - - data.get('wp_' + (rows - 1) + '_0').dt.center.push('wp_' + (rows - 1) + '_' + (cols - 1)) - data.get('wp_' + (rows - 1) + '_' + (cols - 1)).dt.center.push('wp_' + (rows - 1) + '_0') - - data.get('wp_' + (rows - 1) + '_0').dt.center.push('wp_0_0') - data.get('wp_0_0').dt.center.push('wp_' + (rows - 1) + '_0') - - data.get('wp_' + (rows - 1) + '_' + (cols - 1)).dt.center.push('wp_0_' + (cols - 1)) - data.get('wp_0_' + (cols - 1)).dt.center.push('wp_' + (rows - 1) + '_' + (cols - 1)) + // 四个对角连线 + // data.get('wp_0_0').dt.center.push('wp_0_' + (cols - 1)) + // data.get('wp_0_' + (cols - 1)).dt.center.push('wp_0_0') + // + // data.get('wp_' + (rows - 1) + '_0').dt.center.push('wp_' + (rows - 1) + '_' + (cols - 1)) + // data.get('wp_' + (rows - 1) + '_' + (cols - 1)).dt.center.push('wp_' + (rows - 1) + '_0') + // + // data.get('wp_' + (rows - 1) + '_0').dt.center.push('wp_0_0') + // data.get('wp_0_0').dt.center.push('wp_' + (rows - 1) + '_0') + // + // data.get('wp_' + (rows - 1) + '_' + (cols - 1)).dt.center.push('wp_0_' + (cols - 1)) + // data.get('wp_0_' + (cols - 1)).dt.center.push('wp_' + (rows - 1) + '_' + (cols - 1)) return Array.from(data.values()) }