diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index b89ba96..10c28b8 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -904,3 +904,39 @@ export function getOBBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] { return edgePositions } + +/** + * 重置UV坐标 + * @param geometry + */ +export function resetUVs(geometry: THREE.ExtrudeGeometry) { + 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) + } +} diff --git a/src/example/example1.js b/src/example/example1.js index 56d45c4..6bdf0e7 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -163,6 +163,9 @@ export default { rackDepth: 1, // rackWidth: 5.1, // rackHeight: 4.2, + bottomBarHeight: 0.2, + bottomLinkHeight: 0.2, + topLinkDistance: 0.2, levelCount: 3, bayCount: 4, hideFloor: false, diff --git a/src/modules/rack/Rack3dObject.ts b/src/modules/rack/Rack3dObject.ts new file mode 100644 index 0000000..db86e8f --- /dev/null +++ b/src/modules/rack/Rack3dObject.ts @@ -0,0 +1,484 @@ +import * as THREE from 'three' +import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' +import {BufferGeometry} from "three"; +import {Material} from "three/src/materials/Material"; +import storageBar_PNG from "@/assets/Models/storageBar.png"; +import Plastic_Rough_JPG from "@/assets/Models/Plastic_Rough.jpg"; +import {InstancedMesh} from "three/src/objects/InstancedMesh"; +import {decimalSumBy, resetUVs} from "@/core/ModelUtils"; + +export default class Rack3dObject extends THREE.Object3D { + + private rackVerticalBarWidth = 0.1 + private rackVerticalBarDepth = 0.08 + private static rackVerticalBarColor = 0xFF35499C + private static rackVerticalBarGeometryMap: Map = new Map() + private static rackVerticalBarMaterial: Material = null + + private static rackLinkBarColor = 0xFF35499C + private static rackLinkBarGeometryMap: Map = new Map() + private static rackLinkBarMaterial: Material = null + + private rackHorizontalBarWidth = 0.1 + private rackHorizontalBarDepth = 0.08 + private static rackHorizontalBarColor = 0xFFF97F27 + private static rackHorizontalBarGeometryMap: Map = new Map() + private static rackHorizontalBarMaterial: Material = null + + bottomBarHeight = 0.2 + bottomLinkHeight = 0.2 + topLinkDistance = 0.2 + + private static barSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.025, y: -0.05 }, + { x: -0.01, y: -0.045 }, + { x: 0.01, y: -0.045 }, + { x: 0.025, y: -0.05 }, + { x: 0.05, y: -0.05 }, + { x: 0.05, y: 0.042 }, + { x: 0.042, y: 0.05 }, + { x: 0.025, y: 0.05 }, + { x: 0.025, y: 0.042 }, + { x: 0.042, y: 0.042 }, + { x: 0.042, y: -0.042 }, + { x: -0.042, y: -0.042 }, + { x: -0.042, y: 0.042 }, + { x: -0.025, y: 0.042 }, + { x: -0.025, y: 0.05 }, + { x: -0.042, y: 0.05 }, + { x: -0.05, y: 0.042 }, + { x: -0.05, y: -0.05 } + ] + + private static linkSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.05, y: 0.05 }, + { x: 0, y: 0.05 }, + { x: 0, y: 0.06 }, + { x: -0.06, y: 0.06 }, + { x: -0.06, y: -0.05 }, + { x: -0.05, y: -0.05 } + ] + + private static linkBarSectionPoints = [ + { x: -0.025, y: -0.025 }, + { x: 0.025, y: -0.025 }, + { x: 0.025, y: 0.025 }, + { x: -0.025, y: 0.025 }, + { x: -0.025, y: -0.025 } + ] + + private createVerticalBar(length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(Rack3dObject.barSectionPoints[0].x, Rack3dObject.barSectionPoints[0].y) + for (let i = 1; i < Rack3dObject.barSectionPoints.length; i++) { + shape.lineTo(Rack3dObject.barSectionPoints[i].x, Rack3dObject.barSectionPoints[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方便正确贴图 + resetUVs(geometry) + return geometry + } + + private 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(Rack3dObject.rackVerticalBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + private createLinkBar(height: number, depth: number): THREE.BufferGeometry { + + const bgs: BufferGeometry[] = [] + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(Rack3dObject.linkBarSectionPoints[0].x, Rack3dObject.linkBarSectionPoints[0].y) + for (let i = 1; i < Rack3dObject.linkBarSectionPoints.length; i++) { + shape.lineTo(Rack3dObject.linkBarSectionPoints[i].x, Rack3dObject.linkBarSectionPoints[i].y) + } + + // 拉伸轨迹线 横向 底部 + const curveHBottom = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHBottom = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHBottom // 设置挤出轨迹 + } + + // 拉伸轨迹线 横向 底部 + const curveHTop = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, height, 0), new THREE.Vector3(0, height, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHTop = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHTop // 设置挤出轨迹 + } + + // 创建挤出几何体 + const geometryHBottom = new THREE.ExtrudeGeometry(shape, optionsHBottom) + const geometryHTop = new THREE.ExtrudeGeometry(shape, optionsHTop) + bgs.push(geometryHBottom, geometryHTop) + + // 需要创建斜杆 + + for (let i = 0; i < Math.floor(height / depth); i++) { + // 拉伸轨迹线 斜向 + const curveD = new THREE.CatmullRomCurve3( + (i % 2 == 0) ? [new THREE.Vector3(0, depth * i, 0), new THREE.Vector3(0, depth * (i + 1), depth)] + : [new THREE.Vector3(0, depth * (i + 1), 0), new THREE.Vector3(0, depth * (i), depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + const optionsD = { + steps: 1, + bevelEnabled: false, + extrudePath: curveD // 设置挤出轨迹 + } + + const geometryD = new THREE.ExtrudeGeometry(shape, optionsD) + bgs.push(geometryD) + } + + if (height > depth) { + + + } + + // 调整uv方便正确贴图 + // resetUVs(geometry); + return mergeGeometries(bgs) + } + + private createLinkBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(Rack3dObject.rackLinkBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + private createHorizontalBar(length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(Rack3dObject.barSectionPoints[0].x, Rack3dObject.barSectionPoints[0].y) + for (let i = 1; i < Rack3dObject.barSectionPoints.length; i++) { + shape.lineTo(Rack3dObject.barSectionPoints[i].x, Rack3dObject.barSectionPoints[i].y) + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.05, 0, 0), new THREE.Vector3(length - 0.05, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + + const linkShapeL = new THREE.Shape() + const linkShapeR = new THREE.Shape() + linkShapeL.moveTo(Rack3dObject.linkSectionPoints[0].x, Rack3dObject.linkSectionPoints[0].y) + linkShapeR.moveTo(Rack3dObject.linkSectionPoints[0].x + (length), Rack3dObject.linkSectionPoints[0].y) + for (let i = 1; i < Rack3dObject.linkSectionPoints.length; i++) { + linkShapeL.lineTo(Rack3dObject.linkSectionPoints[i].x, Rack3dObject.linkSectionPoints[i].y) + linkShapeR.lineTo(Rack3dObject.linkSectionPoints[i].x + (length), Rack3dObject.linkSectionPoints[i].y) + } + + // 拉伸轨迹线 + const linkCurve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 0.08)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const linkOptions = { + steps: 1, + bevelEnabled: false, + extrudePath: linkCurve // 设置挤出轨迹 + } + // 创建挤出几何体 + const linkGeometryL = new THREE.ExtrudeGeometry(linkShapeL, linkOptions) + linkGeometryL.rotateZ(-Math.PI / 2) + const linkGeometryR = new THREE.ExtrudeGeometry(linkShapeR, linkOptions) + linkGeometryR.rotateX(-Math.PI) + linkGeometryR.rotateZ(-Math.PI / 2) + + // 调整uv方便正确贴图 + // resetUVs(geometry); + return mergeGeometries([geometry, linkGeometryL, linkGeometryR]) + } + + private createHorizontalBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(Rack3dObject.rackHorizontalBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + constructor(item: ItemJson, option?: RendererCudOption) { + super(); + + const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) + const rackDepth = item.dt.rackDepth + 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 }[] = [] + + // 计算 + const linkBarMatrix: { 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: distanceX - rackWidth / 2, + y: 0, + z: -rackDepth / 2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + vBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: 0, + z: item.dt.rackDepth - rackDepth / 2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: this.bottomLinkHeight, + z: i % 2 == 0 ? (item.dt.rackDepth - rackDepth / 2) : -rackDepth / 2, + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + 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 = this.bottomBarHeight + const bay = item.dt.bays[i] + for (let j = 0; j < bay.levelHeight.length; j++) { + const levelHeight = bay.levelHeight[j] + if (distanceY <= 0) { + continue + } + hBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: distanceY, + z: -rackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + hBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: distanceY, + z: item.dt.rackDepth - rackDepth / 2, + 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 (!Rack3dObject.rackVerticalBarGeometryMap.get(vBarMatrix[0].l)) { + Rack3dObject.rackVerticalBarGeometryMap.set(vBarMatrix[0].l, this.createVerticalBar(vBarMatrix[0].l)) + } + if (!Rack3dObject.rackVerticalBarMaterial) { + Rack3dObject.rackVerticalBarMaterial = this.createVerticalBarMaterial() + } + const rackVerticalBarGeometry = Rack3dObject.rackVerticalBarGeometryMap.get(vBarMatrix[0].l) + const rackVerticalBarMaterial = Rack3dObject.rackVerticalBarMaterial + const dummy = new THREE.Object3D() + const vBarMesh = new THREE.InstancedMesh(rackVerticalBarGeometry, 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 (linkBarMatrix.length > 0) { + const linkBarKey: LinkBarKey = new LinkBarKey(rackHeight - this.bottomLinkHeight - this.topLinkDistance, item.dt.rackDepth) + if (!Rack3dObject.rackLinkBarGeometryMap.get(linkBarKey)) { + Rack3dObject.rackLinkBarGeometryMap.set(linkBarKey, this.createLinkBar(linkBarKey.height, linkBarKey.depth)) + } + const rackLinkBarGeometry = Rack3dObject.rackLinkBarGeometryMap.get(linkBarKey) + if (!Rack3dObject.rackLinkBarMaterial) { + Rack3dObject.rackLinkBarMaterial = this.createLinkBarMaterial() + } + const rackLinkBarMaterial = Rack3dObject.rackLinkBarMaterial + const dummy = new THREE.Object3D() + const linkBarMesh = new THREE.InstancedMesh(rackLinkBarGeometry, rackLinkBarMaterial, linkBarMatrix.length) + for (let i = 0; i < linkBarMatrix.length; i++) { + const lp = linkBarMatrix[i] + dummy.position.set(lp.x, lp.y, lp.z) + dummy.rotation.set(lp.rx, lp.ry, lp.rz) + dummy.scale.set(lp.sx, lp.sy, lp.sz) + dummy.updateMatrix() + linkBarMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(linkBarMesh) + } + + if (hBarMatrix.length > 0) { + if (!Rack3dObject.rackHorizontalBarGeometryMap.get(hBarMatrix[0].l)) { + Rack3dObject.rackHorizontalBarGeometryMap.set(hBarMatrix[0].l, this.createHorizontalBar(hBarMatrix[0].l)) + } + const rackHorizontalBarGeometry = Rack3dObject.rackHorizontalBarGeometryMap.get(hBarMatrix[0].l) + if (!Rack3dObject.rackHorizontalBarMaterial) { + Rack3dObject.rackHorizontalBarMaterial = this.createHorizontalBarMaterial() + } + const rackHorizontalBarMaterial = Rack3dObject.rackHorizontalBarMaterial + const dummy = new THREE.Object3D() + const hBarMesh = new THREE.InstancedMesh(rackHorizontalBarGeometry, 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) + } + + meshes.forEach(mesh => { + this.add(mesh) + mesh.instanceMatrix.needsUpdate = true + }) + } + + +} + +class LinkBarKey { + height: number + depth: number + constructor(height: number, depth: number) { + this.height = height + this.depth = depth + } + equals(other: LinkBarKey): boolean { + return this.height == other.height && this.depth == other.depth + } +} diff --git a/src/modules/rack/RackRenderer.ts b/src/modules/rack/RackRenderer.ts index 660cb59..a32bcb0 100644 --- a/src/modules/rack/RackRenderer.ts +++ b/src/modules/rack/RackRenderer.ts @@ -1,16 +1,9 @@ import * as THREE from 'three' -import { BufferGeometry } from 'three' import BaseRenderer from '@/core/base/BaseRenderer.ts' import { decimalSumBy } from '@/core/ModelUtils' import Constract from '@/core/Constract.ts' -import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg?inline' -import storageBar_PNG from '@/assets/Models/storageBar.png?inline' -import { Material } from 'three/src/materials/Material' -import { InstancedMesh } from 'three/src/objects/InstancedMesh' -//@ts-ignore -import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' import type Viewport from '@/core/engine/Viewport.ts' -import type { Object3DLike } from '@/types/ModelTypes.ts' +import Rack3dObject from "@/modules/rack/Rack3dObject"; /** * 货架货位渲染器 @@ -111,7 +104,7 @@ export default class RackRenderer extends BaseRenderer { return { position: [ worldPosition.x, - worldPosition.y + 0.15, // 加上横梁高度 + worldPosition.y + rack.dt.bottomBarHeight + 0.05, // 加上横梁高度 worldPosition.z ], rotation: [ @@ -174,7 +167,7 @@ export default class RackRenderer extends BaseRenderer { return null } - const group = new THREE.Group() + const group = new Rack3dObject(item, option) group.name = RackRenderer.POINT_NAME const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) @@ -185,62 +178,6 @@ export default class RackRenderer extends BaseRenderer { 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) - // } - - const meshes = this.createRack(item, option) - - meshes.forEach(mesh => { - group.add(mesh) - mesh.instanceMatrix.needsUpdate = true - }) // 设置位置 group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) @@ -264,497 +201,4 @@ export default class RackRenderer extends BaseRenderer { super.dispose() this.pointMaterial?.dispose() } - - rackVerticalBarWidth = 0.1 - rackVerticalBarDepth = 0.08 - rackVerticalBarColor = 0xFF35499C - rackVerticalBarGeometry: BufferGeometry = null - rackVerticalBarMaterial: Material = null - - rackLinkBarColor = 0xFF35499C - rackLinkBarGeometry: BufferGeometry = null - rackLinkBarMaterial: Material = null - - rackHorizontalBarWidth = 0.1 - rackHorizontalBarDepth = 0.08 - rackHorizontalBarColor = 0xFFF97F27 - rackHorizontalBarGeometry: BufferGeometry = null - rackHorizontalBarMaterial: Material = null - - bottomBarHeight = 0.2 - bottomLinkHeight = 0.2 - - barSectionPoints = [ - { x: -0.05, y: -0.05 }, - { x: -0.025, y: -0.05 }, - { x: -0.01, y: -0.045 }, - { x: 0.01, y: -0.045 }, - { x: 0.025, y: -0.05 }, - { x: 0.05, y: -0.05 }, - { x: 0.05, y: 0.042 }, - { x: 0.042, y: 0.05 }, - { x: 0.025, y: 0.05 }, - { x: 0.025, y: 0.042 }, - { x: 0.042, y: 0.042 }, - { x: 0.042, y: -0.042 }, - { x: -0.042, y: -0.042 }, - { x: -0.042, y: 0.042 }, - { x: -0.025, y: 0.042 }, - { x: -0.025, y: 0.05 }, - { x: -0.042, y: 0.05 }, - { x: -0.05, y: 0.042 }, - { x: -0.05, y: -0.05 } - ] - - linkSectionPoints = [ - { x: -0.05, y: -0.05 }, - { x: -0.05, y: 0.05 }, - { x: 0, y: 0.05 }, - { x: 0, y: 0.06 }, - { x: -0.06, y: 0.06 }, - { x: -0.06, y: -0.05 }, - { x: -0.05, y: -0.05 } - ] - - linkBarSectionPoints = [ - { x: -0.025, y: -0.025 }, - { x: 0.025, y: -0.025 }, - { x: 0.025, y: 0.025 }, - { x: -0.025, y: 0.025 }, - { x: -0.025, y: -0.025 } - ] - - createVerticalBar(x, y, z, length): THREE.BufferGeometry { - - // 创建一个形状 柱子的截面形状 - const shape = new THREE.Shape() - shape.moveTo(this.barSectionPoints[0].x, this.barSectionPoints[0].y) - for (let i = 1; i < this.barSectionPoints.length; i++) { - shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[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 - } - - createLinkBar(x, y, z, vBarLength, depth, bottomDistance, topDistance): THREE.BufferGeometry { - - const bgs: BufferGeometry[] = [] - const top = vBarLength - topDistance - // 创建一个形状 柱子的截面形状 - const shape = new THREE.Shape() - shape.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y) - for (let i = 1; i < this.linkBarSectionPoints.length; i++) { - shape.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y) - } - - // 拉伸轨迹线 横向 底部 - const curveHBottom = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, bottomDistance, 0), new THREE.Vector3(0, bottomDistance, depth)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsHBottom = { - steps: 1, - bevelEnabled: false, - extrudePath: curveHBottom // 设置挤出轨迹 - } - - // 拉伸轨迹线 横向 底部 - const curveHTop = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, top, 0), new THREE.Vector3(0, top, depth)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsHTop = { - steps: 1, - bevelEnabled: false, - extrudePath: curveHTop // 设置挤出轨迹 - } - - // 创建挤出几何体 - const geometryHBottom = new THREE.ExtrudeGeometry(shape, optionsHBottom) - const geometryHTop = new THREE.ExtrudeGeometry(shape, optionsHTop) - bgs.push(geometryHBottom, geometryHTop) - - let remainingHeight = vBarLength - bottomDistance - topDistance - - // 需要创建斜杆 - - for (let i = 0; i < Math.floor(remainingHeight / depth); i++) { - // 拉伸轨迹线 斜向 - const curveD = new THREE.CatmullRomCurve3( - (i % 2 == 0) ? [new THREE.Vector3(0, bottomDistance + depth * i, 0), new THREE.Vector3(0, bottomDistance + depth * (i + 1), depth)] - : [new THREE.Vector3(0, bottomDistance + depth * (i + 1), 0), new THREE.Vector3(0, bottomDistance + depth * (i), depth)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - const optionsD = { - steps: 1, - bevelEnabled: false, - extrudePath: curveD // 设置挤出轨迹 - } - - const geometryD = new THREE.ExtrudeGeometry(shape, optionsD) - bgs.push(geometryD) - } - - if (vBarLength - bottomDistance - topDistance > depth) { - - - } - - // 调整uv方便正确贴图 - // this.resetUVs(geometry); - return mergeGeometries(bgs) - } - - createLinkBarMaterial(): THREE.Material { - - const material = new THREE.MeshPhongMaterial() - material.color.setHex(this.rackLinkBarColor, '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.barSectionPoints[0].x, this.barSectionPoints[0].y) - for (let i = 1; i < this.barSectionPoints.length; i++) { - shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[i].y) - } - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0.05, 0, 0), new THREE.Vector3(length - 0.05, 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelEnabled: false, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - - const linkShapeL = new THREE.Shape() - const linkShapeR = new THREE.Shape() - linkShapeL.moveTo(this.linkSectionPoints[0].x, this.linkSectionPoints[0].y) - linkShapeR.moveTo(this.linkSectionPoints[0].x + (length), this.linkSectionPoints[0].y) - for (let i = 1; i < this.linkSectionPoints.length; i++) { - linkShapeL.lineTo(this.linkSectionPoints[i].x, this.linkSectionPoints[i].y) - linkShapeR.lineTo(this.linkSectionPoints[i].x + (length), this.linkSectionPoints[i].y) - } - - // 拉伸轨迹线 - const linkCurve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 0.08)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const linkOptions = { - steps: 1, - bevelEnabled: false, - extrudePath: linkCurve // 设置挤出轨迹 - } - // 创建挤出几何体 - const linkGeometryL = new THREE.ExtrudeGeometry(linkShapeL, linkOptions) - linkGeometryL.rotateZ(-Math.PI / 2) - const linkGeometryR = new THREE.ExtrudeGeometry(linkShapeR, linkOptions) - linkGeometryR.rotateX(-Math.PI) - linkGeometryR.rotateZ(-Math.PI / 2) - - // 调整uv方便正确贴图 - // this.resetUVs(geometry); - return mergeGeometries([geometry, linkGeometryL, linkGeometryR]) - } - - 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 - - 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 rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) - const rackDepth = item.dt.rackDepth - 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 }[] = [] - - // 计算 - const linkBarMatrix: { 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: distanceX - rackWidth / 2, - y: 0, - z: -rackDepth / 2, - sx: 0.8, - sy: 1, - sz: 1, - rx: 0, - ry: Math.PI / 2, - rz: 0, - l: rackHeight - }) - vBarMatrix.push({ - x: distanceX - rackWidth / 2, - y: 0, - z: item.dt.rackDepth - rackDepth / 2, - sx: 0.8, - sy: 1, - sz: 1, - rx: 0, - ry: -Math.PI / 2, - rz: 0, - l: rackHeight - }) - linkBarMatrix.push({ - x: distanceX - rackWidth / 2, - y: 0, - z: i % 2 == 0 ? (item.dt.rackDepth - rackDepth / 2) : -rackDepth / 2, - sx: 1, - sy: 1, - sz: 1, - rx: 0, - ry: i % 2 == 0 ? Math.PI : 0, - 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 = this.bottomBarHeight - const bay = item.dt.bays[i] - for (let j = 0; j < bay.levelHeight.length; j++) { - const levelHeight = bay.levelHeight[j] - if (distanceY <= 0) { - continue - } - hBarMatrix.push({ - x: distanceX - rackWidth / 2, - y: distanceY, - z: -rackDepth / 2, - sx: 1, - sy: 0.8, - sz: 1, - rx: Math.PI / 2, - ry: 0, - rz: 0, - l: bay.bayWidth - }) - hBarMatrix.push({ - x: distanceX - rackWidth / 2, - y: distanceY, - z: item.dt.rackDepth - rackDepth / 2, - 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 (linkBarMatrix.length > 0) { - if (!this.rackLinkBarGeometry) { - this.rackLinkBarGeometry = this.createLinkBar(linkBarMatrix[0].x, linkBarMatrix[0].y, linkBarMatrix[0].z, rackHeight, item.dt.rackDepth, this.bottomLinkHeight, 0.2) - } - if (!this.rackLinkBarMaterial) { - this.rackLinkBarMaterial = this.createLinkBarMaterial() - } - const dummy = new THREE.Object3D() - const linkBarMesh = new THREE.InstancedMesh(this.rackLinkBarGeometry, this.rackLinkBarMaterial, linkBarMatrix.length) - for (let i = 0; i < linkBarMatrix.length; i++) { - const lp = linkBarMatrix[i] - dummy.position.set(lp.x, lp.y, lp.z) - dummy.rotation.set(lp.rx, lp.ry, lp.rz) - dummy.scale.set(lp.sx, lp.sy, lp.sz) - dummy.updateMatrix() - linkBarMesh.setMatrixAt(i, dummy.matrix) - } - meshes.push(linkBarMesh) - } - - 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: THREE.ExtrudeGeometry) { - 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) - } - } - } diff --git a/src/modules/rack/RackRenderer.ts_back b/src/modules/rack/RackRenderer.ts_back new file mode 100644 index 0000000..d66ca23 --- /dev/null +++ b/src/modules/rack/RackRenderer.ts_back @@ -0,0 +1,760 @@ +import * as THREE from 'three' +import { BufferGeometry } from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import { decimalSumBy } from '@/core/ModelUtils' +import Constract from '@/core/Constract.ts' +import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg?inline' +import storageBar_PNG from '@/assets/Models/storageBar.png?inline' +import { Material } from 'three/src/materials/Material' +import { InstancedMesh } from 'three/src/objects/InstancedMesh' +//@ts-ignore +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' +import type Viewport from '@/core/engine/Viewport.ts' +import type { Object3DLike } from '@/types/ModelTypes.ts' + +/** + * 货架货位渲染器 + */ +export default class RackRenderer extends BaseRenderer { + static POINT_NAME = 'rack' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.05 + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + 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(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) + point.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { + throw new Error('not allow store line.') + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + throw new Error('not allow store line.') + } + + /** + * 获取物品存放在货架后的位置和旋转角度 + * item.dt.storeAt 必须存在, 比如 { item:'货架ID', bay:0, level:1, cell:0 } + */ + getStorePlacement(viewport: Viewport, item: ItemJson): { position: [number, number, number], rotation: [number, number, number] } { + if (!item.dt?.storeAt?.item) { + // 没有定义存储位置,返回空对象 + //@ts-ignore + return {} + } + + const bay = item.dt?.storeAt?.bay || 0 + const level = item.dt?.storeAt?.level || 0 + // 暂时不用算格子 const cell = item.dt?.storeAt?.cell || 0 + + // 目标货架 + const rack = viewport.stateManager.findItemById(item.dt.storeAt.item) + const rackWidth = decimalSumBy(rack.dt.bays, (b: any) => b.bayWidth) + const bays = rack.dt.bays + const levelHeights = rack.dt.bays[bay]?.levelHeight + + // 局部坐标系下的偏移量 + let localX = 0 + for (let i = 0; i < bay; i++) { + localX += bays[i]?.bayWidth || 0 + } + localX += bays[bay].bayWidth / 2 // 居中 + + let localY = 0 + for (let i = 0; i < level; i++) { + localY += levelHeights[i] + } + + // 构建局部偏移向量 + const rackPos = new THREE.Vector3(...rack.tf[0]) + const offset = new THREE.Vector3( + localX - rackWidth / 2, // 相对坐标从最左边开始, + localY, 0) + + // 应用货架的旋转 + const q = new THREE.Quaternion() + q.setFromEuler(new THREE.Euler( + THREE.MathUtils.degToRad(rack.tf[1][0]), + THREE.MathUtils.degToRad(rack.tf[1][1]), + THREE.MathUtils.degToRad(rack.tf[1][2]) + )) + + offset.applyQuaternion(q) + const worldPosition = rackPos.clone().add(offset) + + return { + position: [ + worldPosition.x, + worldPosition.y + this.bottomBarHeight + 0.1, // 加上横梁高度 + worldPosition.z + ], + rotation: [ + // 托盘旋转90度才能放进货架 + rack.tf[1][0], + rack.tf[1][1] + 90, + rack.tf[1][2] + ] + } + } + + updatePoint(item: ItemJson, group: THREE.Group, option?: RendererCudOption): THREE.Group { + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + group.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + // group.scale 不允许修改!! + + // 禁止缩放, + item.tf[2][0] = item.dt.rackWidth + item.tf[2][1] = item.dt.rackHeight + + + // 更新放在内部的所有箱子 + const subItems = this.tempViewport.runtimeManager.getItemsByRack(item.id) + const viewport = this.tempViewport + + if (subItems) { + _.defer(() => { + viewport.stateManager.update(({ getEntity, putEntity, deleteEntity, addEntity }) => { + for (const subItemId of subItems) { + const subItem = getEntity(subItemId) + if (subItem) { + const { position, rotation } = this.getStorePlacement(viewport, subItem) + if (position) { + subItem.tf[0][0] = position[0] + subItem.tf[0][1] = position[1] + subItem.tf[0][2] = position[2] + subItem.tf[1][0] = rotation[0] + subItem.tf[1][1] = rotation[1] + subItem.tf[1][2] = rotation[2] + putEntity(subItem) + } + } + } + }) + + }) + } + + return group + } + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + // 创建平面几何体 + if (!item.dt.bays || !item.dt.rackDepth) { + system.showErrorDialog('RackRenderer field bays / rackDepth is null!') + return null + } + + 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) + // + // 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) + // } + + const meshes = this.createRack(item, option) + + meshes.forEach(mesh => { + group.add(mesh) + mesh.instanceMatrix.needsUpdate = true + }) + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + + group.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + + item.dt.rackWidth = rackWidth + item.dt.rackHeight = rackHeight + + item.tf[2][0] = item.dt.rackWidth + item.tf[2][1] = item.dt.rackHeight + + return group + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + rackVerticalBarWidth = 0.1 + rackVerticalBarDepth = 0.08 + rackVerticalBarColor = 0xFF35499C + rackVerticalBarGeometry: BufferGeometry = null + rackVerticalBarMaterial: Material = null + + rackLinkBarColor = 0xFF35499C + rackLinkBarGeometry: BufferGeometry = null + rackLinkBarMaterial: Material = null + + rackHorizontalBarWidth = 0.1 + rackHorizontalBarDepth = 0.08 + rackHorizontalBarColor = 0xFFF97F27 + rackHorizontalBarGeometry: BufferGeometry = null + rackHorizontalBarMaterial: Material = null + + bottomBarHeight = 0.2 + bottomLinkHeight = 0.2 + + barSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.025, y: -0.05 }, + { x: -0.01, y: -0.045 }, + { x: 0.01, y: -0.045 }, + { x: 0.025, y: -0.05 }, + { x: 0.05, y: -0.05 }, + { x: 0.05, y: 0.042 }, + { x: 0.042, y: 0.05 }, + { x: 0.025, y: 0.05 }, + { x: 0.025, y: 0.042 }, + { x: 0.042, y: 0.042 }, + { x: 0.042, y: -0.042 }, + { x: -0.042, y: -0.042 }, + { x: -0.042, y: 0.042 }, + { x: -0.025, y: 0.042 }, + { x: -0.025, y: 0.05 }, + { x: -0.042, y: 0.05 }, + { x: -0.05, y: 0.042 }, + { x: -0.05, y: -0.05 } + ] + + linkSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.05, y: 0.05 }, + { x: 0, y: 0.05 }, + { x: 0, y: 0.06 }, + { x: -0.06, y: 0.06 }, + { x: -0.06, y: -0.05 }, + { x: -0.05, y: -0.05 } + ] + + linkBarSectionPoints = [ + { x: -0.025, y: -0.025 }, + { x: 0.025, y: -0.025 }, + { x: 0.025, y: 0.025 }, + { x: -0.025, y: 0.025 }, + { x: -0.025, y: -0.025 } + ] + + createVerticalBar(x, y, z, length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(this.barSectionPoints[0].x, this.barSectionPoints[0].y) + for (let i = 1; i < this.barSectionPoints.length; i++) { + shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[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 + } + + createLinkBar(x, y, z, vBarLength, depth, bottomDistance, topDistance): THREE.BufferGeometry { + + const bgs: BufferGeometry[] = [] + const top = vBarLength - topDistance + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y) + for (let i = 1; i < this.linkBarSectionPoints.length; i++) { + shape.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y) + } + + // 拉伸轨迹线 横向 底部 + const curveHBottom = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, bottomDistance, 0), new THREE.Vector3(0, bottomDistance, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHBottom = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHBottom // 设置挤出轨迹 + } + + // 拉伸轨迹线 横向 底部 + const curveHTop = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, top, 0), new THREE.Vector3(0, top, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHTop = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHTop // 设置挤出轨迹 + } + + // 创建挤出几何体 + const geometryHBottom = new THREE.ExtrudeGeometry(shape, optionsHBottom) + const geometryHTop = new THREE.ExtrudeGeometry(shape, optionsHTop) + bgs.push(geometryHBottom, geometryHTop) + + let remainingHeight = vBarLength - bottomDistance - topDistance + + // 需要创建斜杆 + + for (let i = 0; i < Math.floor(remainingHeight / depth); i++) { + // 拉伸轨迹线 斜向 + const curveD = new THREE.CatmullRomCurve3( + (i % 2 == 0) ? [new THREE.Vector3(0, bottomDistance + depth * i, 0), new THREE.Vector3(0, bottomDistance + depth * (i + 1), depth)] + : [new THREE.Vector3(0, bottomDistance + depth * (i + 1), 0), new THREE.Vector3(0, bottomDistance + depth * (i), depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + const optionsD = { + steps: 1, + bevelEnabled: false, + extrudePath: curveD // 设置挤出轨迹 + } + + const geometryD = new THREE.ExtrudeGeometry(shape, optionsD) + bgs.push(geometryD) + } + + if (vBarLength - bottomDistance - topDistance > depth) { + + + } + + // 调整uv方便正确贴图 + // this.resetUVs(geometry); + return mergeGeometries(bgs) + } + + createLinkBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(this.rackLinkBarColor, '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.barSectionPoints[0].x, this.barSectionPoints[0].y) + for (let i = 1; i < this.barSectionPoints.length; i++) { + shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[i].y) + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.05, 0, 0), new THREE.Vector3(length - 0.05, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + + const linkShapeL = new THREE.Shape() + const linkShapeR = new THREE.Shape() + linkShapeL.moveTo(this.linkSectionPoints[0].x, this.linkSectionPoints[0].y) + linkShapeR.moveTo(this.linkSectionPoints[0].x + (length), this.linkSectionPoints[0].y) + for (let i = 1; i < this.linkSectionPoints.length; i++) { + linkShapeL.lineTo(this.linkSectionPoints[i].x, this.linkSectionPoints[i].y) + linkShapeR.lineTo(this.linkSectionPoints[i].x + (length), this.linkSectionPoints[i].y) + } + + // 拉伸轨迹线 + const linkCurve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 0.08)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const linkOptions = { + steps: 1, + bevelEnabled: false, + extrudePath: linkCurve // 设置挤出轨迹 + } + // 创建挤出几何体 + const linkGeometryL = new THREE.ExtrudeGeometry(linkShapeL, linkOptions) + linkGeometryL.rotateZ(-Math.PI / 2) + const linkGeometryR = new THREE.ExtrudeGeometry(linkShapeR, linkOptions) + linkGeometryR.rotateX(-Math.PI) + linkGeometryR.rotateZ(-Math.PI / 2) + + // 调整uv方便正确贴图 + // this.resetUVs(geometry); + return mergeGeometries([geometry, linkGeometryL, linkGeometryR]) + } + + 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 + + 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 rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) + const rackDepth = item.dt.rackDepth + 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 }[] = [] + + // 计算 + const linkBarMatrix: { 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: distanceX - rackWidth / 2, + y: 0, + z: -rackDepth / 2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + vBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: 0, + z: item.dt.rackDepth - rackDepth / 2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: 0, + z: i % 2 == 0 ? (item.dt.rackDepth - rackDepth / 2) : -rackDepth / 2, + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + 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 = this.bottomBarHeight + const bay = item.dt.bays[i] + for (let j = 0; j < bay.levelHeight.length; j++) { + const levelHeight = bay.levelHeight[j] + if (distanceY <= 0) { + continue + } + hBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: distanceY, + z: -rackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + hBarMatrix.push({ + x: distanceX - rackWidth / 2, + y: distanceY, + z: item.dt.rackDepth - rackDepth / 2, + 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 (linkBarMatrix.length > 0) { + if (!this.rackLinkBarGeometry) { + this.rackLinkBarGeometry = this.createLinkBar(linkBarMatrix[0].x, linkBarMatrix[0].y, linkBarMatrix[0].z, rackHeight, item.dt.rackDepth, this.bottomLinkHeight, 0.2) + } + if (!this.rackLinkBarMaterial) { + this.rackLinkBarMaterial = this.createLinkBarMaterial() + } + const dummy = new THREE.Object3D() + const linkBarMesh = new THREE.InstancedMesh(this.rackLinkBarGeometry, this.rackLinkBarMaterial, linkBarMatrix.length) + for (let i = 0; i < linkBarMatrix.length; i++) { + const lp = linkBarMatrix[i] + dummy.position.set(lp.x, lp.y, lp.z) + dummy.rotation.set(lp.rx, lp.ry, lp.rz) + dummy.scale.set(lp.sx, lp.sy, lp.sz) + dummy.updateMatrix() + linkBarMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(linkBarMesh) + } + + 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: THREE.ExtrudeGeometry) { + 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) + } + } + +}