diff --git a/src/assets/Models/carton.glb b/src/assets/Models/carton.glb new file mode 100644 index 0000000..1f5efa7 Binary files /dev/null and b/src/assets/Models/carton.glb differ diff --git a/src/assets/Models/carton.jpg b/src/assets/Models/carton.jpg new file mode 100644 index 0000000..681f48c Binary files /dev/null and b/src/assets/Models/carton.jpg differ diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index e87bbcd..a93385d 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -2,7 +2,7 @@
+ :show-file-list="false" accept=".fbx,.obj,.mtl,.3ds,.glb,.gltf" action="" :auto-upload="false"> 打开模型 restate.mode, (newVal) => { tcontrols.setMode(newVal) } }) -function addConveyor(){ - const conveyorLength = 10; // 长度 - const conveyorWidth = 0.8; // 宽度 - const conveyorHeight = 0.08; // 厚度 - const wallHeight = conveyorHeight + 0.15; // 挡板高出输送线 0.2 米 - const legWidth = 0.1; // 柱子宽度(X) - const legDepth = 0.1; // 柱子深度(Z) - const legHeight = 0.8; // 柱子高度(Y) +function addConveyor() { + const conveyorLength = 10 // 长度 + const conveyorWidth = 0.8 // 宽度 + const conveyorHeight = 0.08 // 厚度 + const wallHeight = conveyorHeight + 0.15 // 挡板高出输送线 0.2 米 + + const legWidth = 0.1 // 柱子宽度(X) + const legDepth = 0.1 // 柱子深度(Z) + const legHeight = 0.8 // 柱子高度(Y) // 创建输送线底部板子(灰色) - const conveyorGeometry = new THREE.BoxGeometry(conveyorWidth, conveyorHeight, conveyorLength); - const conveyorMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); - const conveyor = new THREE.Mesh(conveyorGeometry, conveyorMaterial); - conveyor.position.y = conveyorHeight / 2; + const conveyorGeometry = new THREE.BoxGeometry(conveyorWidth, conveyorHeight, conveyorLength) + const conveyorMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }) + const conveyor = new THREE.Mesh(conveyorGeometry, conveyorMaterial) + conveyor.position.y = conveyorHeight / 2 // 添加两侧挡板(灰色) - const wallWidth = 0.05; - const wallGeometry = new THREE.BoxGeometry(wallWidth, wallHeight, conveyorLength); - const wallMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); + const wallWidth = 0.05 + const wallGeometry = new THREE.BoxGeometry(wallWidth, wallHeight, conveyorLength) + const wallMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }) - const leftWall = new THREE.Mesh(wallGeometry, wallMaterial); - leftWall.position.set(-(conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0); + const leftWall = new THREE.Mesh(wallGeometry, wallMaterial) + leftWall.position.set(-(conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0) - const rightWall = new THREE.Mesh(wallGeometry, wallMaterial); - rightWall.position.set((conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0); + const rightWall = new THREE.Mesh(wallGeometry, wallMaterial) + rightWall.position.set((conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0) // 创建柱子(灰色,类似桌腿) - const legGeometry = new THREE.BoxGeometry(legWidth, legHeight, legDepth); - const legMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); // 灰色 + const legGeometry = new THREE.BoxGeometry(legWidth, legHeight, legDepth) + const legMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }) // 灰色 const legPositions = [ [conveyorWidth / 2 - legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 右前 [-conveyorWidth / 2 + legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 左前 [conveyorWidth / 2 - legWidth / 2, +(conveyorLength / 2 - legDepth / 2)], // 右后 [-conveyorWidth / 2 + legWidth / 2, +(conveyorLength / 2 - legDepth / 2)] // 左后 - ]; + ] - const legs = []; + const legs = [] legPositions.forEach(pos => { - const leg = new THREE.Mesh(legGeometry, legMaterial); + const leg = new THREE.Mesh(legGeometry, legMaterial) leg.position.set( pos[0], -legHeight / 2, pos[1] - ); - legs.push(leg); - }); + ) + legs.push(leg) + }) // ====== 新增部分:顶部带圆角的长方体(胶囊型)====== - const beamWidth = conveyorWidth; // 和输送线一样宽(0.8米) - const beamLength = conveyorLength-conveyorHeight/2;// 和输送线一样长(10米) - const beamHeight = 0.08; // 高度(0.1米) + const beamWidth = conveyorWidth // 和输送线一样宽(0.8米) + const beamLength = conveyorLength - conveyorHeight / 2// 和输送线一样长(10米) + const beamHeight = 0.08 // 高度(0.1米) // 创建 Shape(二维路径) - const shape = new THREE.Shape(); + const shape = new THREE.Shape() - const radius = beamHeight / 2; // 圆角半径 = 一半宽度 + const radius = beamHeight / 2 // 圆角半径 = 一半宽度 // 左侧半圆(从顶部开始,顺时针) - shape.absarc(-beamLength / 2, 0, radius, Math.PI / 2, -Math.PI / 2, false); + shape.absarc(-beamLength / 2, 0, radius, Math.PI / 2, -Math.PI / 2, false) // 右侧半圆(从底部开始,顺时针) - shape.absarc(beamLength / 2, 0, radius, -Math.PI / 2, Math.PI / 2, false); + shape.absarc(beamLength / 2, 0, radius, -Math.PI / 2, Math.PI / 2, false) - shape.closePath(); // 封闭路径 + shape.closePath() // 封闭路径 // 拉伸成三维几何体 const extrudeSettings = { depth: beamWidth, steps: 16, bevelEnabled: false - }; + } - const beamGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const beamMaterial = new THREE.MeshBasicMaterial({ color: 0x032702 }); // 粉色 + const beamGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings) + const beamMaterial = new THREE.MeshBasicMaterial({ color: 0x032702 }) // 粉色 - const beam = new THREE.Mesh(beamGeometry, beamMaterial); - beam.rotation.x = Math.PI / 2; + const beam = new THREE.Mesh(beamGeometry, beamMaterial) + beam.rotation.x = Math.PI / 2 beam.rotation.z = Math.PI / 2 beam.rotation.y = Math.PI / 2 - beam.position.y = beamHeight+conveyorHeight/2; // 底部贴着输送线顶部 - beam.position.x = -beamWidth/2; - addMarkerAt(0, legHeight+beamHeight+conveyorHeight,0,triangleUrl,1,false); - addMarkerAt(beamWidth/2+wallWidth+0.01, legHeight+wallHeight/2,0,triangleUrl,.5,true); - addMarkerAt(beamWidth/2+wallWidth+0.01, legHeight+wallHeight/2,conveyorLength/4,triangleUrl,.5,true); - addMarkerAt(beamWidth/2+wallWidth+0.01, legHeight+wallHeight/2,-conveyorLength/4,triangleUrl,.5,true); - - addMarkerAt(-(beamWidth/2+wallWidth+0.01), legHeight+wallHeight/2,0,triangleUrl,.5,true); - addMarkerAt(-(beamWidth/2+wallWidth+0.01), legHeight+wallHeight/2,conveyorLength/4,triangleUrl,.5,true); - addMarkerAt(-(beamWidth/2+wallWidth+0.01), legHeight+wallHeight/2,-conveyorLength/4,triangleUrl,.5,true); + beam.position.y = beamHeight + conveyorHeight / 2 // 底部贴着输送线顶部 + beam.position.x = -beamWidth / 2 + addMarkerAt(0, legHeight + beamHeight + conveyorHeight, 0, triangleUrl, 1, false) + addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, 0, triangleUrl, .5, true) + addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, conveyorLength / 4, triangleUrl, .5, true) + addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, -conveyorLength / 4, triangleUrl, .5, true) + + addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, 0, triangleUrl, .5, true) + addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, conveyorLength / 4, triangleUrl, .5, true) + addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, -conveyorLength / 4, triangleUrl, .5, true) // 将所有元素组合成一个组 - const group = new THREE.Group(); - group.add(conveyor); - group.add(leftWall); - group.add(rightWall); - legs.forEach(leg => group.add(leg)); - group.add(beam); // 添加新长方体 + const group = new THREE.Group() + group.add(conveyor) + group.add(leftWall) + group.add(rightWall) + legs.forEach(leg => group.add(leg)) + group.add(beam) // 添加新长方体 // 设置组沿Z轴向上移动的距离,例如 1 米 - group.position.y = legHeight; // 修改这里的值来改变提升的高度 - scene.add(group); + group.position.y = legHeight // 修改这里的值来改变提升的高度 + scene.add(group) } -function addMarkerAt(x, y,z,textUrl, scale = 1,lip) { - const loader = new THREE.TextureLoader(); + +function addMarkerAt(x, y, z, textUrl, scale = 1, lip) { + const loader = new THREE.TextureLoader() loader.load(textUrl, (texture) => { // 创建一个平面作为标记(大小可以调整) let markerGeometry - if(lip){ - markerGeometry=new THREE.PlaneGeometry(.25 * scale, .25 * scale); - }else{ - markerGeometry = new THREE.PlaneGeometry(.45 * scale, .3 * scale); // 宽高比例保持一致 + if (lip) { + markerGeometry = new THREE.PlaneGeometry(.25 * scale, .25 * scale) + } else { + markerGeometry = new THREE.PlaneGeometry(.45 * scale, .3 * scale) // 宽高比例保持一致 } const markerMaterial = new THREE.MeshBasicMaterial({ @@ -288,193 +291,193 @@ function addMarkerAt(x, y,z,textUrl, scale = 1,lip) { opacity: 1, depthWrite: false, side: THREE.DoubleSide - }); + }) - const marker = new THREE.Mesh(markerGeometry, markerMaterial); - let yHeight=y+0.01 + const marker = new THREE.Mesh(markerGeometry, markerMaterial) + let yHeight = y + 0.01 // 设置位置:位于输送线表面(Y轴略高于输送线一点) - marker.position.set(x, yHeight, z); // Y = 输送线高度 + 一点间距 + marker.position.set(x, yHeight, z) // Y = 输送线高度 + 一点间距 // 旋转为 X/Z 平面方向(与输送线一致) - marker.rotation.x = -Math.PI / 2; // 和 conveyorBelt 一样旋转角度 - marker.rotation.z = Math.PI / 2; // 在平面上旋转 90 度 - if(lip){ - marker.rotation.y = Math.PI / 2; // 在平面上旋转 90 度 - }else{ - marker.rotation.y = Math.PI; + marker.rotation.x = -Math.PI / 2 // 和 conveyorBelt 一样旋转角度 + marker.rotation.z = Math.PI / 2 // 在平面上旋转 90 度 + if (lip) { + marker.rotation.y = Math.PI / 2 // 在平面上旋转 90 度 + } else { + marker.rotation.y = Math.PI } - scene.add(marker); - }); + scene.add(marker) + }) } -function createShelf(){//创建货架 - const shelfLength = 5; - const shelfWidth = 0.8; - const shelfThickness = 0.02; - const rows = 3; - const columns = 4; - const spacingY = 1; // 每行之间的间距 - const gapBetweenPlanks = 0.1; // 层板之间的间隙宽度 - const baseHeight = 0; // 地面高度设为0 +function createShelf() {//创建货架 + const shelfLength = 5 + const shelfWidth = 0.8 + const shelfThickness = 0.02 + const rows = 3 + const columns = 4 + const spacingY = 1 // 每行之间的间距 + const gapBetweenPlanks = 0.1 // 层板之间的间隙宽度 + const baseHeight = 0 // 地面高度设为0 - const unitLength = (shelfLength - (columns - 1) * gapBetweenPlanks) / columns; + const unitLength = (shelfLength - (columns - 1) * gapBetweenPlanks) / columns - const textureLoader = new THREE.TextureLoader(); + const textureLoader = new THREE.TextureLoader() const shelfTexture = textureLoader.load(rackPlatUrl, () => { - renderer.render(scene, camera); - }); + renderer.render(scene, camera) + }) const redPoleTexture = textureLoader.load(rackBlue, () => { - renderer.render(scene, camera); // 确保贴图加载后重新渲染 - }); + renderer.render(scene, camera) // 确保贴图加载后重新渲染 + }) - shelfTexture.wrapS = THREE.RepeatWrapping; - shelfTexture.wrapT = THREE.RepeatWrapping; - shelfTexture.repeat.set(1, 20); - shelfTexture.rotation = Math.PI / 2; + shelfTexture.wrapS = THREE.RepeatWrapping + shelfTexture.wrapT = THREE.RepeatWrapping + shelfTexture.repeat.set(1, 20) + shelfTexture.rotation = Math.PI / 2 const shelfMaterial = new THREE.MeshPhongMaterial({ map: shelfTexture, color: 0x999999, shininess: 60 - }); + }) // 垂直柱子材质 const poleMaterial = new THREE.MeshPhongMaterial({ map: shelfTexture, color: 0x333333, shininess: 60 - }); + }) // 柱子尺寸 - const poleSize = gapBetweenPlanks; // 柱子的宽度和深度都等于缝隙宽度 - const totalHeight = rows * spacingY; // 柱子总高度 + const poleSize = gapBetweenPlanks // 柱子的宽度和深度都等于缝隙宽度 + const totalHeight = rows * spacingY // 柱子总高度 for (let row = 0; row < rows; row++) { - let currentXOffset = -shelfLength / 2; + let currentXOffset = -shelfLength / 2 for (let col = 0; col < columns; col++) { - const geometry = new THREE.BoxGeometry(unitLength, shelfThickness, shelfWidth); - const mesh = new THREE.Mesh(geometry, shelfMaterial); + const geometry = new THREE.BoxGeometry(unitLength, shelfThickness, shelfWidth) + const mesh = new THREE.Mesh(geometry, shelfMaterial) - const x = currentXOffset + unitLength / 2; + const x = currentXOffset + unitLength / 2 // 计算层板的 Y 坐标,使其基于地面高度 - const y = baseHeight + row * spacingY + shelfThickness / 2; - const z = 0; + const y = baseHeight + row * spacingY + shelfThickness / 2 + const z = 0 - mesh.position.set(x, y, z); - scene.add(mesh); + mesh.position.set(x, y, z) + scene.add(mesh) if (row === 0) { // 只在第一层添加垂直柱子 // 最左侧柱子 if (col === 0) { - const leftPoleX = currentXOffset - poleSize / 2; - const leftPoleY = baseHeight + totalHeight / 2; - const leftPoleZ = z - shelfWidth / 2 + poleSize / 2; + const leftPoleX = currentXOffset - poleSize / 2 + const leftPoleY = baseHeight + totalHeight / 2 + const leftPoleZ = z - shelfWidth / 2 + poleSize / 2 // 黑色柱子 const leftPole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), poleMaterial - ); - leftPole.position.set(leftPoleX, leftPoleY, leftPoleZ); - scene.add(leftPole); + ) + leftPole.position.set(leftPoleX, leftPoleY, leftPoleZ) + scene.add(leftPole) // // 红色柱子 - const redLeftPoleZ = z + shelfWidth / 2 - poleSize / 2; + const redLeftPoleZ = z + shelfWidth / 2 - poleSize / 2 const redLeftPole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), new THREE.MeshPhongMaterial({ map: redPoleTexture, color: 0xffffff, // 颜色可保留作为叠加色 shininess: 60, - transparent: true, + transparent: true }) // new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 }) - ); - redLeftPole.position.set(leftPoleX, leftPoleY, redLeftPoleZ); - scene.add(redLeftPole); + ) + redLeftPole.position.set(leftPoleX, leftPoleY, redLeftPoleZ) + scene.add(redLeftPole) } // 层板间隙中的柱子 if (col < columns - 1) { - const gapStartX = currentXOffset + unitLength; + const gapStartX = currentXOffset + unitLength // 原有灰色柱子(居中) - const poleX = gapStartX + poleSize / 2; - const poleY = baseHeight + totalHeight / 2; - const poleZ = z- shelfWidth / 2+poleSize/2; + const poleX = gapStartX + poleSize / 2 + const poleY = baseHeight + totalHeight / 2 + const poleZ = z - shelfWidth / 2 + poleSize / 2 const pole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), poleMaterial - ); - pole.position.set(poleX, poleY, poleZ); - scene.add(pole); + ) + pole.position.set(poleX, poleY, poleZ) + scene.add(pole) // 新增红色柱子,在原有柱子的前面或后面并排 - const redPoleX = poleX; // 和灰柱 X 相同 - const redPoleY = poleY; // 和灰柱 Y 相同 - const redPoleZ = z + shelfWidth / 2-poleSize/2; // 在前方 + const redPoleX = poleX // 和灰柱 X 相同 + const redPoleY = poleY // 和灰柱 Y 相同 + const redPoleZ = z + shelfWidth / 2 - poleSize / 2 // 在前方 const redPoleMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 - }); + }) const redPole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), redPoleMaterial - ); - redPole.position.set(redPoleX, redPoleY, redPoleZ); - scene.add(redPole); + ) + redPole.position.set(redPoleX, redPoleY, redPoleZ) + scene.add(redPole) } // 最右侧柱子 if (col === columns - 1) { - const rightPoleX = currentXOffset + unitLength + gapBetweenPlanks - poleSize / 2; - const rightPoleY = baseHeight + totalHeight / 2; - const rightPoleZ = z - shelfWidth / 2 + poleSize / 2; + const rightPoleX = currentXOffset + unitLength + gapBetweenPlanks - poleSize / 2 + const rightPoleY = baseHeight + totalHeight / 2 + const rightPoleZ = z - shelfWidth / 2 + poleSize / 2 // 黑色柱子 const rightPole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), poleMaterial - ); - rightPole.position.set(rightPoleX, rightPoleY, rightPoleZ); - scene.add(rightPole); + ) + rightPole.position.set(rightPoleX, rightPoleY, rightPoleZ) + scene.add(rightPole) // 红色柱子 - const redRightPoleZ = z + shelfWidth / 2 - poleSize / 2; + const redRightPoleZ = z + shelfWidth / 2 - poleSize / 2 const redRightPole = new THREE.Mesh( new THREE.BoxGeometry(poleSize, totalHeight, poleSize), new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 }) - ); - redRightPole.position.set(rightPoleX, rightPoleY, redRightPoleZ); - scene.add(redRightPole); + ) + redRightPole.position.set(rightPoleX, rightPoleY, redRightPoleZ) + scene.add(redRightPole) } } - currentXOffset += unitLength + gapBetweenPlanks; + currentXOffset += unitLength + gapBetweenPlanks } } // 光源 - const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); - scene.add(ambientLight); - const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); - directionalLight.position.set(5, 10, 7); - scene.add(directionalLight); + const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) + scene.add(ambientLight) + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8) + directionalLight.position.set(5, 10, 7) + scene.add(directionalLight) } function createGroundStore() { - const planeGeometry = new THREE.PlaneGeometry(1, 1); + const planeGeometry = new THREE.PlaneGeometry(1, 1) const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} - }); - const planeMesh = new THREE.Mesh(planeGeometry, material); + }) + const planeMesh = new THREE.Mesh(planeGeometry, material) planeMesh.rotateX(Math.PI / 2) - scene.add(planeMesh); + scene.add(planeMesh) } function initThree() { @@ -837,7 +840,6 @@ function handleFileChange(file) { system.clearLoading() } - reader.readAsArrayBuffer(file) } else if (fileName.endsWith('.obj')) { reader.readAsText(file) @@ -861,11 +863,26 @@ function handleFileChange(file) { system.clearLoading() } - reader.readAsArrayBuffer(file) + + } else if (fileName.endsWith('.glb') || fileName.endsWith('.gltf')) { + reader.onload = () => { + const loader = new GLTFLoader() + const arrayBuffer = reader.result + //@ts-ignore + loader.parseAsync(arrayBuffer, '').then((content) => { + debugger + addGroupToScene(content.scene) + }) + + system.clearLoading() + } } else { alert('不支持的文件类型!') + return } + + reader.readAsArrayBuffer(file) } diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index ecd4081..32b988e 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -10,6 +10,7 @@ import axios from 'axios' import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader' import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader' +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' export function setUserDataForItem(item: ItemJson, object: Object3DLike) { if (!object.name && item.name) { @@ -494,6 +495,14 @@ export async function loadByUrl(url): Promise { }) } +export async function loadGlbModule(url: string): Promise { + const response = await axios.get(url, { + responseType: 'arraybuffer' + }) + const rt = await new GLTFLoader().parseAsync(response.data, '') + return rt.scene +} + export function loadTexture(url: string) { return new THREE.TextureLoader().loadAsync(url) } @@ -519,3 +528,42 @@ 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/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 35a8bbb..3378f36 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -7,6 +7,7 @@ import Gstore from '@/modules/gstore' import Rack from '@/modules/rack' import Pallet from "@/modules/pallet" import Tote from "@/modules/tote" +import Carton from "@/modules/carton" import Ptr from "@/modules/ptr" import Clx from "@/modules/clx" import Charger from "@/modules/charger" @@ -74,6 +75,7 @@ export default class WorldModel { Rack, Pallet, Tote, + Carton, Ptr, Clx, Charger diff --git a/src/example/ExampleUtil.js b/src/example/ExampleUtil.js index 1017118..7c61487 100644 --- a/src/example/ExampleUtil.js +++ b/src/example/ExampleUtil.js @@ -18,7 +18,7 @@ export function buildPointPerformanceData(t, rows, cols) { node.tf[0][1] = 0.01 node.tf[0][2] = col * spacingZ node.tf[1] = [0, 0, 0] - node.tf[2] = [1, 0.01, 1] + node.tf[2] = [1, 0.4, 1] data.set(node.id, node) } } diff --git a/src/example/example1.js b/src/example/example1.js index f699de4..91774a7 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -326,10 +326,10 @@ export default { catalogCode: 'f3', t: 'floor', items: [ { - id: 'tote1', - t: 'pallet', + id: 'carton1', + t: 'carton', v: true, - tf: [[0, 0.1, 0], [0, 0, 0], [1.0, 0.1, 1.0]], + tf: [[0, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]], dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } } ] @@ -340,7 +340,7 @@ export default { }, { catalogCode: '__f2', t: 'floor', - items: buildPointPerformanceData('gstore', 100, 100) + items: buildPointPerformanceData('carton', 100, 100) } ], elevator: [], // 电梯 diff --git a/src/modules/carton/CartonEntity.ts b/src/modules/carton/CartonEntity.ts new file mode 100644 index 0000000..9cf671f --- /dev/null +++ b/src/modules/carton/CartonEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class PalletEntity extends BaseEntity { + +} diff --git a/src/modules/carton/CartonInteraction.ts b/src/modules/carton/CartonInteraction.ts new file mode 100644 index 0000000..c01c91b --- /dev/null +++ b/src/modules/carton/CartonInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class PalletInteraction extends BaseInteraction { + + get isSinglePointMode(): boolean { + return true + } + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { + item = super.createPointOfItem(item, point) + + // 创建一个地堆货架 + item.dt.palletWidth = 1 // 宽度 + item.dt.palletDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/carton/CartonPropertySetter.ts b/src/modules/carton/CartonPropertySetter.ts new file mode 100644 index 0000000..4a5cfc9 --- /dev/null +++ b/src/modules/carton/CartonPropertySetter.ts @@ -0,0 +1,20 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.palletWidth', label: '托盘宽度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.palletDepth', label: '托盘深度', input: 'InputNumber', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/carton/CartonRenderer.ts b/src/modules/carton/CartonRenderer.ts new file mode 100644 index 0000000..c8c6dd8 --- /dev/null +++ b/src/modules/carton/CartonRenderer.ts @@ -0,0 +1,83 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import InstancePointManager from '@/core/manager/InstancePointManager.ts' +import type { Object3DLike } from '@/types/ModelTypes.ts' +import MODULE_GLB_File from '@/assets/Models/carton.glb?url' +import MODULE_3DS_TEX from '@/assets/Models/carton.jpg?url' +import { load3DModule, loadByUrl, loadGlbModule, loadTexture } from '@/core/ModelUtils.ts' + +/** + * 货架货位渲染器 + */ +export default class PalletRenderer extends BaseRenderer { + static POINT_NAME = 'carton_point' + + /** + * 默认点的高度, 防止和地面重合 + */ + 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 defaultUserData = { + color: 0xc29a70 + } + + cartonGeometry: THREE.BufferGeometry + cartonMaterial: THREE.Material + + init() { + return Promise.all([ + super.init(), + loadGlbModule(MODULE_GLB_File), + loadTexture(MODULE_3DS_TEX) + + ]).then(([_, glbGroup, cartonTexture]) => { + const mesh = glbGroup.children[0] as THREE.Mesh + 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) + + cartonTexture.wrapS = THREE.RepeatWrapping + cartonTexture.wrapT = THREE.RepeatWrapping + cartonTexture.repeat.set(1, 1) + //@ts-ignore + this.cartonMaterial.map = cartonTexture + //@ts-ignore + this.cartonMaterial.color.set(this.defaultUserData.color) + + this.cartonMaterial.needsUpdate = true + }) + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike { + return this.pointManager.createPoint(item) + } + + 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.cartonGeometry, + this.cartonMaterial, + Constract.MAX_PALLET_INSTANCES) + ) + } + + dispose() { + super.dispose() + if (this.cartonGeometry) { + this.cartonGeometry.dispose() + this.cartonGeometry = undefined + } + if (this.cartonMaterial) { + this.cartonMaterial.dispose() + this.cartonMaterial = undefined + } + } +} diff --git a/src/modules/carton/index.ts b/src/modules/carton/index.ts new file mode 100644 index 0000000..f38fc5d --- /dev/null +++ b/src/modules/carton/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import CartonRenderer from './CartonRenderer.ts' +import CartonEntity from './CartonEntity.ts' +import CartonInteraction from './CartonInteraction.ts' +import propertySetter from "./CartonPropertySetter.ts"; + +export const ITEM_TYPE_NAME = 'carton' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new CartonRenderer(ITEM_TYPE_NAME), + interaction: new CartonInteraction(ITEM_TYPE_NAME), + setter: propertySetter, + entity: CartonEntity, +}) diff --git a/src/types/ModelTypes.ts b/src/types/ModelTypes.ts index 3238fc4..8669692 100644 --- a/src/types/ModelTypes.ts +++ b/src/types/ModelTypes.ts @@ -72,3 +72,12 @@ export type Object3DLike = Object3D | LineManageWrap | PointManageWrap * 坐标的范指型, 可以是 THREE.Vector3 或者三元数组 */ export type Vector3Like = THREE.Vector3 | number[] + +/** + * 材质质量枚举 + */ +export const MaterialQualityEnum = { + High: 0, + Middle: 1, + Low: 2, +};