From a262d77869e0f2ad6bfef04aa843901d5ef11073 Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Wed, 11 Jun 2025 18:17:36 +0800 Subject: [PATCH] rack --- src/components/Model3DView.vue | 16 +- src/modules/rack/RackRenderer.ts | 423 +++++++++++++++++++++++++++++++++------ 2 files changed, 372 insertions(+), 67 deletions(-) diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index 5d3f158..2eeeaa1 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -572,9 +572,9 @@ function createGroundStore() { alphaMap: texture1, // 应用纹理 normalMap: texture2, // 应用纹理 // side: THREE.DoubleSide // 如果你的几何体有双面,确保这一面也被贴图 - metalness: 0.6, - roughness: 0.8, - specular: 0x6d6d6d, + // metalness: 0.6, + // roughness: 0.8, + // specular: 0x6d6d6d, transparent: true, needsUpdate: true, }); @@ -584,7 +584,7 @@ function createGroundStore() { // const material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // const mesh1 = new THREE.Mesh(geometry1, material1); - let mesh = new THREE.InstancedMesh(geometry1, material1, 160000); + let mesh = new THREE.InstancedMesh(geometry1, material1, 1600); mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); mesh.castShadow = true; mesh.receiveShadow = true; @@ -592,11 +592,11 @@ function createGroundStore() { let dummy = new THREE.Object3D(); - for (let i = 0; i < 400; i++) { - for (let j = 0; j < 400; j++) { - dummy.position.set(i * 0.1, 0, j * 0.2); + for (let i = 0; i < 40; i++) { + for (let j = 0; j < 40; j++) { + dummy.position.set(i * 0.5, 0, j * 0.5); dummy.updateMatrix(); - mesh.setMatrixAt(i*400 + j, dummy.matrix); + mesh.setMatrixAt(i*40 + j, dummy.matrix); } } diff --git a/src/modules/rack/RackRenderer.ts b/src/modules/rack/RackRenderer.ts index 5c67106..db47d38 100644 --- a/src/modules/rack/RackRenderer.ts +++ b/src/modules/rack/RackRenderer.ts @@ -7,6 +7,9 @@ import { decimalSumBy } from '@/core/ModelUtils' import Constract from '@/core/Constract.ts' import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg' import {BufferGeometry} from "three"; +import storageBar_PNG from "@/assets/Models/storageBar.png"; +import {Material} from "three/src/materials/Material"; +import {InstancedMesh} from "three/src/objects/InstancedMesh"; /** * 货架货位渲染器 @@ -65,69 +68,75 @@ export default class RackRenderer extends BaseRenderer { const group = new THREE.Group() group.name = RackRenderer.POINT_NAME - const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) - const heights = [] - for (let i = 0; i < item.dt.bays.length; i++) { - const bay = item.dt.bays[i] - const bayHeight = decimalSumBy(bay.levelHeight) - heights.push(bayHeight) - } - const rackHeight = _.max(heights) - // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(rackWidth, item.dt.rackDepth) + // const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) + // const heights = [] + // for (let i = 0; i < item.dt.bays.length; i++) { + // const bay = item.dt.bays[i] + // const bayHeight = decimalSumBy(bay.levelHeight) + // heights.push(bayHeight) + // } + // const rackHeight = _.max(heights) + // // 绘制背景矩形框 + // const planeGeometry = new THREE.PlaneGeometry(rackWidth, item.dt.rackDepth) + // + // planeGeometry.rotateX(Math.PI / 2) + // + // const planeMaterial = new THREE.MeshBasicMaterial({ + // color: '#9a9090', + // transparent: true, // 启用透明 + // opacity: 0.5, // 50%透明度 + // depthWrite: false, // 防止深度冲突 + // side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} + // }) + // const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) + // group.add(planeMesh) + // + // + // // 绘制边框 + // const lineXLen = rackWidth - this.defaultLineWidth + // const lineYLen = item.dt.rackDepth - this.defaultLineWidth + // + // const lineGeometry = new LineGeometry().setPositions([ + // -(lineXLen / 2), 0, -(lineYLen / 2), + // lineXLen / 2, 0, -(lineYLen / 2), + // lineXLen / 2, 0, lineYLen / 2, + // -(lineXLen / 2), 0, lineYLen / 2, + // -(lineXLen / 2), 0, -(lineYLen / 2) + // ]) + // const lineMaterial = new LineMaterial({ + // color: '#0d89a5', + // linewidth: this.defaultLineWidth, + // worldUnits: true, + // resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), + // side: THREE.DoubleSide + // }) + // const line = new Line2(lineGeometry, lineMaterial) + // group.add(line as THREE.Object3D) + // + // let lineDistanceX = 0 + // + // for (let i = 0; item.dt.bays.length > 1 && i < item.dt.bays.length - 1; i++) { + // const bay = item.dt.bays[i] + // lineDistanceX += bay.bayWidth + // const lineGeometryT = new LineGeometry().setPositions([ + // -(lineDistanceX) + (lineXLen / 2), 0, lineYLen / 2, + // -(lineDistanceX) + (lineXLen / 2), 0, -(lineYLen / 2) + // ]) + // const lineT = new Line2(lineGeometryT, lineMaterial) + // group.add(lineT as THREE.Object3D) + // } - planeGeometry.rotateX(Math.PI / 2) + const meshes = this.createRack(item, option); - const planeMaterial = new THREE.MeshBasicMaterial({ - color: '#9a9090', - transparent: true, // 启用透明 - opacity: 0.5, // 50%透明度 - depthWrite: false, // 防止深度冲突 - side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} - }) - const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) - group.add(planeMesh) - - - // 绘制边框 - const lineXLen = rackWidth - this.defaultLineWidth - const lineYLen = item.dt.rackDepth - this.defaultLineWidth - - const lineGeometry = new LineGeometry().setPositions([ - -(lineXLen / 2), 0, -(lineYLen / 2), - lineXLen / 2, 0, -(lineYLen / 2), - lineXLen / 2, 0, lineYLen / 2, - -(lineXLen / 2), 0, lineYLen / 2, - -(lineXLen / 2), 0, -(lineYLen / 2) - ]) - const lineMaterial = new LineMaterial({ - color: '#0d89a5', - linewidth: this.defaultLineWidth, - worldUnits: true, - resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), - side: THREE.DoubleSide + meshes.forEach(mesh => { + group.add(mesh) }) - const line = new Line2(lineGeometry, lineMaterial) - group.add(line as THREE.Object3D) - - let lineDistanceX = 0 - - for (let i = 0; item.dt.bays.length > 1 && i < item.dt.bays.length - 1; i++) { - const bay = item.dt.bays[i] - lineDistanceX += bay.bayWidth - const lineGeometryT = new LineGeometry().setPositions([ - -(lineDistanceX) + (lineXLen / 2), 0, lineYLen / 2, - -(lineDistanceX) + (lineXLen / 2), 0, -(lineYLen / 2) - ]) - const lineT = new Line2(lineGeometryT, lineMaterial) - group.add(lineT as THREE.Object3D) - } // 设置位置 - group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - - item.dt.rackWidth = rackWidth - item.dt.rackHeight = rackHeight + // group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + // + // item.dt.rackWidth = rackWidth + // item.dt.rackHeight = rackHeight return group } @@ -144,9 +153,305 @@ export default class RackRenderer extends BaseRenderer { rackVerticalBarDepth = 0.08 rackVerticalBarColor = 0xFF35499C rackVerticalBarGeometry: BufferGeometry = null + rackVerticalBarMaterial: Material = null + + rackHorizontalBarWidth = 0.1 + rackHorizontalBarDepth = 0.08 + rackHorizontalBarColor = 0xFFF97F27 + rackHorizontalBarGeometry: BufferGeometry = null + rackHorizontalBarMaterial: Material = null + + sectionPoints = [ + {x: -0.05, y: -0.05}, + {x: -0.025, y: -0.05}, + {x: 0.04-0.05, y: 0.005-0.05}, + {x: 0.06-0.05, y: 0.005-0.05}, + {x: 0.075-0.05, y: 0-0.05}, + {x: 0.1-0.05, y: 0-0.05}, + {x: 0.1-0.05, y: 0.092-0.05}, + {x: 0.092-0.05, y: 0.1-0.05}, + {x: 0.075-0.05, y: 0.1-0.05}, + {x: 0.075-0.05, y: 0.092-0.05}, + {x: 0.092-0.05, y: 0.092-0.05}, + {x: 0.092-0.05, y: 0.008-0.05}, + {x: 0.008-0.05, y: 0.008-0.05}, + {x: 0.008-0.05, y: 0.092-0.05}, + {x: 0.025-0.05, y: 0.092-0.05}, + {x: 0.025-0.05, y: 0.1-0.05}, + {x: 0.008-0.05, y: 0.1-0.05}, + {x: 0-0.05, y: 0.092-0.05}, + {x: 0-0.05, y: 0-0.05} + ] + + createVerticalBar(x, y, z, length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape(); + shape.moveTo(this.sectionPoints[0].x, this.sectionPoints[0].y); + for (let i = 1; i < this.sectionPoints.length; i++) { + shape.lineTo(this.sectionPoints[i].x , this.sectionPoints[i].y); + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, length, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ); + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve, // 设置挤出轨迹 + }; + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options); + // 调整uv方便正确贴图 + this.resetUVs(geometry); + return geometry + } + + createVerticalBarMaterial(): THREE.Material { + + let textureLoader = new THREE.TextureLoader() + + // 加载纹理 + const textureHole = textureLoader.load(storageBar_PNG); // 孔洞 + const textureMaterial = textureLoader.load(Plastic_Rough_JPG); // 表面材质 + + textureHole.repeat.set(10, 18); // X轴重复,Y轴重复 + textureMaterial.repeat.set(2, 2); // X轴重复,Y轴重复 + // textureHole.offset.set(0.5, 0) + // textureHole.center.set(0.5, 0) + + // 必须设置包裹模式为重复 + textureHole.wrapS = THREE.RepeatWrapping; + textureHole.wrapT = THREE.RepeatWrapping; + textureMaterial.wrapS = THREE.RepeatWrapping; + textureMaterial.wrapT = THREE.RepeatWrapping; + + const material = new THREE.MeshPhongMaterial(); + material.alphaMap = textureHole; + material.normalMap = textureMaterial; + material.color.setHex(this.rackVerticalBarColor, "srgb"); + material.specular.setHex(0xff6d6d6d, 'srgb'); + material.transparent = true; + material.needsUpdate = true; + + return material; + } + + createHorizontalBar(x, y, z, length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape(); + shape.moveTo(this.sectionPoints[0].x, this.sectionPoints[0].y); + for (let i = 1; i < this.sectionPoints.length; i++) { + shape.lineTo(this.sectionPoints[i].x , this.sectionPoints[i].y); + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(length, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ); + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve, // 设置挤出轨迹 + }; + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options); + // 调整uv方便正确贴图 + this.resetUVs(geometry); + return geometry + } + + createHorizontalBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial(); + material.color.setHex(this.rackHorizontalBarColor, "srgb"); + material.specular.setHex(0xff6d6d6d, 'srgb'); + material.transparent = true; + material.needsUpdate = true; - createVerticalBar(x, y, z, length) { + return material; + } + + createRack(item: ItemJson, option?: RendererCudOption): InstancedMesh[] { + if (!item.dt.bays || !item.dt.rackDepth) { + system.showErrorDialog('RackRenderer field bays / rackDepth is null!') + return null + } + + const rackPoint = { + x: item.tf[0][0], + y: item.tf[0][1], + z: item.tf[0][2] + } + const heights = [] + for (let i = 0; i < item.dt.bays.length; i++) { + const bay = item.dt.bays[i] + const bayHeight = decimalSumBy(bay.levelHeight) + heights.push(bayHeight) + } + const rackHeight = _.max(heights) + + // 计算立住坐标点和长度 + const vBarMatrix: {x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number}[] = []; + + let distanceX = 0, distanceY = 0; + + for (let i = -1; i < item.dt.bays.length; i++) { + if (i >= 0) { + const bay = item.dt.bays[i] + distanceX += bay.bayWidth + } + vBarMatrix.push({ + x: rackPoint.x + distanceX, + y: rackPoint.y, + z: rackPoint.z, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI/2, + rz: 0, + l: rackHeight + }) + vBarMatrix.push({ + x: rackPoint.x + distanceX, + y: rackPoint.y, + z: rackPoint.z + item.dt.rackDepth, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI/2, + rz: 0, + l: rackHeight + }) + } + + // 计算横梁数量 + const hBarMatrix: {x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number}[] = []; + distanceX = 0; + for (let i = 0; i < item.dt.bays.length; i++) { + distanceY = 0; + const bay = item.dt.bays[i] + for (let j = 0; j < bay.levelHeight.length; j++) { + const levelHeight = bay.levelHeight[j] + hBarMatrix.push({ + x: rackPoint.x + distanceX, + y: rackPoint.y + distanceY, + z: rackPoint.z, + sx: 1, + sy: 0.8, + sz: 1, + rx: Math.PI/2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + hBarMatrix.push({ + x: rackPoint.x + distanceX, + y: rackPoint.y + distanceY, + z: rackPoint.z + item.dt.rackDepth, + sx: 1, + sy: 0.8, + sz: 1, + rx: -Math.PI/2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + distanceY += levelHeight + } + distanceX += bay.bayWidth + } + + const meshes: InstancedMesh[] = []; + + if (vBarMatrix.length > 0) { + if (!this.rackVerticalBarGeometry) { + this.rackVerticalBarGeometry = this.createVerticalBar(vBarMatrix[0].x, vBarMatrix[0].y, vBarMatrix[0].z, vBarMatrix[0].l) + } + if (!this.rackVerticalBarMaterial) { + this.rackVerticalBarMaterial = this.createVerticalBarMaterial() + } + const dummy = new THREE.Object3D(); + const vBarMesh = new THREE.InstancedMesh(this.rackVerticalBarGeometry, this.rackVerticalBarMaterial, vBarMatrix.length); + for (let i = 0; i < vBarMatrix.length; i++) { + const vp = vBarMatrix[i] + dummy.position.set(vp.x, vp.y, vp.z); + dummy.rotation.set(vp.rx, vp.ry, vp.rz); + dummy.scale.set(vp.sx, vp.sy, vp.sz); + dummy.updateMatrix(); + vBarMesh.setMatrixAt(i, dummy.matrix); + } + meshes.push(vBarMesh) + } + + if (hBarMatrix.length > 0) { + if (!this.rackHorizontalBarGeometry) { + this.rackHorizontalBarGeometry = this.createHorizontalBar(hBarMatrix[0].x, hBarMatrix[0].y, hBarMatrix[0].z, hBarMatrix[0].l) + } + if (!this.rackHorizontalBarMaterial) { + this.rackHorizontalBarMaterial = this.createHorizontalBarMaterial() + } + const dummy = new THREE.Object3D(); + const hBarMesh = new THREE.InstancedMesh(this.rackHorizontalBarGeometry, this.rackHorizontalBarMaterial, hBarMatrix.length); + for (let i = 0; i < hBarMatrix.length; i++) { + const hp = hBarMatrix[i] + dummy.position.set(hp.x, hp.y, hp.z); + dummy.rotation.set(hp.rx, hp.ry, hp.rz); + dummy.scale.set(hp.sx, hp.sy, hp.sz); + dummy.updateMatrix(); + hBarMesh.setMatrixAt(i, dummy.matrix); + } + meshes.push(hBarMesh) + } + return meshes; + } + + + resetUVs(geometry) { + if (geometry == undefined) return; + const pos = geometry.getAttribute("position"), + nor = geometry.getAttribute("normal"), + uvs = geometry.getAttribute("uv"); + + for (let i = 0; i < pos.count; i++) { + let x = 0, y = 0; + + const nx = Math.abs(nor.getX(i)), ny = Math.abs(nor.getY(i)), nz = Math.abs(nor.getZ(i)); + + // if facing X + if (nx >= ny && nx >= nz) { + x = pos.getZ(i); + y = pos.getY(i); + } + + // if facing Y + if (ny >= nx && ny >= nz) { + x = pos.getX(i); + y = pos.getZ(i); + } + + // if facing Z + if (nz >= nx && nz >= ny) { + x = pos.getX(i); + y = pos.getY(i); + } + uvs.setXY(i, x, y); + } } }