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) ) }