diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue
index 2f38a72..e87bbcd 100644
--- a/src/components/Model3DView.vue
+++ b/src/components/Model3DView.vue
@@ -16,7 +16,7 @@
打开材质
添加输送线
- 添加货架
+ 添加货架2
添加地堆
材质颜色
@@ -86,10 +86,9 @@ import { TransformControls } from 'three/examples/jsm/controls/TransformControls
import Split from '@/components/split/split.vue'
import SplitArea from '@/components/split/split-area.vue'
import { renderIcon } from '@/utils/webutils.js'
-import textureUrl from '@/assets/images/conveyor/shapes/RibSideSkirtThumbnail.jpg'
-import moveUrl from '@/assets/images/conveyor/shapes/move.svg'
-import arrowRightUrl from '@/assets/images/conveyor/shapes/arrow-right.svg'
-import rackUrl from '@/assets/images/conveyor/shapes/Rack.png'
+import rackPlatUrl from "@/assets/images/conveyor/shapes/RackPlatform.png";
+import rackBlue from "@/assets/images/conveyor/shapes/Rack-blue.png";
+import triangleUrl from "@/assets/images/conveyor/shapes/triangle.png";
// DOM refs
const canvasContainer = ref(null)
@@ -169,77 +168,119 @@ watch(() => restate.mode, (newVal) => {
tcontrols.setMode(newVal)
}
})
-
-function addConveyor(){//输送线
- // 1. 创建几何体:长 10 米,宽 0.8 米
- const length = 20; // 长度 (X轴)
- const width = 0.8; // 宽度 (Y轴)
- const height = 0.2; // 厚度/高度 (Z轴)
-
- const geometry = new THREE.BoxGeometry(length, width, height);
-
- // 2. 加载纹理
- const textureLoader = new THREE.TextureLoader();
- textureLoader.load(textureUrl, (texture)=>{
- // 3. 设置纹理参数
- texture.wrapS = THREE.RepeatWrapping; // X方向重复(实际会拉伸)
- texture.wrapT = THREE.RepeatWrapping; // Y方向重复
- texture.center.set(0.5,0.5)
- texture.rotation = Math.PI / 2
-
- texture.repeat.set(1, 1); // 初始不缩放,后面根据需要调整
-
- // 4. 根据输送线长度自动调整Y方向的重复次数
- // 假设你的纹理高度是 256px,想要在 0.8m 宽度上显示完整的一行
- // 所以我们计算 repeat.y = 0.8 / (texture.height / texture.width * length)
- // 或者你可以手动设置 repeat.y 来控制重复频率
- texture.repeat.set(1, 20); // 调整这个值来控制Y方向的重复频率
-
- // 5. 创建材质并应用纹理
- const material = new THREE.MeshBasicMaterial(
- { map: texture,color: 0x9f9f9f});
-
- // 6. 创建网格对象
- const conveyorBelt = new THREE.Mesh(geometry, material);
-
- // 7. 可选:旋转为X轴方向(默认PlaneGeometry是X/Y平面)
- conveyorBelt.rotation.x = -Math.PI / 2; // 让其平放在地面上(X/Z 平面)
-
- // 放置到合适的位置(如果需要)
- conveyorBelt.position.y = height / 2; // 让底部贴地
- // // 示例:在输送线不同位置添加多个标记
- addMarkerAt(-9.5, height,0,moveUrl); // 起点
- addMarkerAt(0, height,0,arrowRightUrl); // 中间
- addMarkerAt(9.5, height,0,moveUrl); // 终点
- scene.add(conveyorBelt)
- // 动画函数
- let offsetSpeed = 0.01; // 控制滚动速度
- function animate() {
- requestAnimationFrame(animate);
-
- // 沿Y轴滚动
- if (texture) {
- let currentOffset = texture.offset.y;
- currentOffset += offsetSpeed;
-
- // 当偏移量超过1时重置,以实现无缝循环
- if (currentOffset >= 1) {
- currentOffset -= 1;
- }
- texture.offset.set(texture.offset.x,currentOffset);
- }
-
- renderer.render(scene, camera);
- }
- animate();
+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 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 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 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 = [];
+
+ legPositions.forEach(pos => {
+ const leg = new THREE.Mesh(legGeometry, legMaterial);
+ leg.position.set(
+ pos[0],
+ -legHeight / 2,
+ pos[1]
+ );
+ legs.push(leg);
});
-}
-function addMarkerAt(x, y,z,textUrl, scale = 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 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.closePath(); // 封闭路径
+
+ // 拉伸成三维几何体
+ const extrudeSettings = {
+ depth: beamWidth,
+ steps: 16,
+ bevelEnabled: false
+ };
+
+ 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;
+ 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);
+ // 将所有元素组合成一个组
+ 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);
+}
+function addMarkerAt(x, y,z,textUrl, scale = 1,lip) {
const loader = new THREE.TextureLoader();
loader.load(textUrl, (texture) => {
// 创建一个平面作为标记(大小可以调整)
- const markerGeometry = new THREE.PlaneGeometry(.6 * scale, 0.5 * scale); // 宽高比例保持一致
+ let markerGeometry
+ if(lip){
+ markerGeometry=new THREE.PlaneGeometry(.25 * scale, .25 * scale);
+ }else{
+ markerGeometry = new THREE.PlaneGeometry(.45 * scale, .3 * scale); // 宽高比例保持一致
+ }
const markerMaterial = new THREE.MeshBasicMaterial({
map: texture,
@@ -257,126 +298,172 @@ function addMarkerAt(x, y,z,textUrl, scale = 1) {
// 旋转为 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;
+ }
scene.add(marker);
});
}
function createShelf(){//创建货架
- const textureLoader = new THREE.TextureLoader();
- const metalTexture = textureLoader.load('path/to/your/metal/texture.jpg'); // 替换为实际路径
+ 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 material = new THREE.MeshStandardMaterial({
- map: metalTexture,
- metalness: 1.0,
- roughness: 0.5
+ const unitLength = (shelfLength - (columns - 1) * gapBetweenPlanks) / columns;
+
+ const textureLoader = new THREE.TextureLoader();
+ const shelfTexture = textureLoader.load(rackPlatUrl, () => {
+ renderer.render(scene, camera);
});
+ const redPoleTexture = textureLoader.load(rackBlue, () => {
+ renderer.render(scene, camera); // 确保贴图加载后重新渲染
+ });
+
+ shelfTexture.wrapS = THREE.RepeatWrapping;
+ shelfTexture.wrapT = THREE.RepeatWrapping;
+ shelfTexture.repeat.set(1, 20);
+ shelfTexture.rotation = Math.PI / 2;
- // 货架参数
- const shelfLength = 10; // 总长
- const shelfWidth = 0.8; // 宽度(深度)
- const shelfThickness = 0.1; // 层板厚度
- const rows = 3; // 层数
- const columns = 4; // 列数
- const spacingY = 1; // 层间距
+ const shelfMaterial = new THREE.MeshPhongMaterial({
+ map: shelfTexture,
+ color: 0x999999,
+ shininess: 60
+ });
- const unitLength = shelfLength / columns; // 每个格子长度:2.5m
+ // 垂直柱子材质
+ const poleMaterial = new THREE.MeshPhongMaterial({
+ map: shelfTexture,
+ color: 0x333333,
+ shininess: 60
+ });
- // ✅ 整体上浮高度:让模型位于水平面之上
- const baseHeight = 2; // 单位:米
+ // 柱子尺寸
+ const poleSize = gapBetweenPlanks; // 柱子的宽度和深度都等于缝隙宽度
+ const totalHeight = rows * spacingY; // 柱子总高度
- // ---------------------
- // 创建层板(3层 × 4列)
- // ---------------------
for (let row = 0; row < rows; row++) {
+ 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, material);
+ const mesh = new THREE.Mesh(geometry, shelfMaterial);
- const x = -shelfLength / 2 + unitLength * col + unitLength / 2;
- const y = -(shelfThickness + spacingY) * row - shelfThickness / 2 + baseHeight; // 加上偏移
+ const x = currentXOffset + unitLength / 2;
+ // 计算层板的 Y 坐标,使其基于地面高度
+ const y = baseHeight + row * spacingY + shelfThickness / 2;
const z = 0;
mesh.position.set(x, y, z);
scene.add(mesh);
- }
- }
-
- // ---------------------
- // 计算第三层层板底部位置(用于支撑柱对齐)
- // ---------------------
- const thirdShelfCenterY = -(shelfThickness + spacingY) * 2 - shelfThickness / 2 + baseHeight;
- const thirdShelfBottomY = thirdShelfCenterY - shelfThickness / 2;
-
- // ---------------------
-// 创建支撑柱 + 添加贴纸
-// ---------------------
-
- const postWidth = 0.1;
- const postDepth = 0.1;
- const postHeight = 3.3;
-
+ if (row === 0) { // 只在第一层添加垂直柱子
+ // 最左侧柱子
+ if (col === 0) {
+ 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);
+
+ // // 红色柱子
+ 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,
+ })
+ // new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 })
+ );
+ redLeftPole.position.set(leftPoleX, leftPoleY, redLeftPoleZ);
+ scene.add(redLeftPole);
+ }
+ // 层板间隙中的柱子
+ if (col < columns - 1) {
+ const gapStartX = currentXOffset + unitLength;
+
+ // 原有灰色柱子(居中)
+ 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);
+
+ // 新增红色柱子,在原有柱子的前面或后面并排
+ 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);
+ }
- const postPositionsX = [0, 2.5, 5, 7.5, 10];
- const postPositionsZ = [-shelfWidth / 2, shelfWidth / 2];
+ // 最右侧柱子
+ if (col === columns - 1) {
+ 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);
+
+ // 红色柱子
+ 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);
+ }
+ }
-// 加载贴纸纹理(可以是透明PNG)
- const loader = new THREE.TextureLoader();
- loader.load(rackUrl, (texture) => {
- // 创建贴纸材质(支持透明)
- const stickerMaterial = new THREE.MeshBasicMaterial({
- map: texture,
- transparent: true,
- side: THREE.DoubleSide // 双面可见
- });
+ currentXOffset += unitLength + gapBetweenPlanks;
+ }
+ }
- postPositionsX.forEach(x => {
- postPositionsZ.forEach(z => {
- // 创建柱子
- const postGeometry = new THREE.BoxGeometry(postWidth, postHeight, postDepth);
- const post = new THREE.Mesh(postGeometry, material);
-
- // 设置柱子中心 Y 坐标,使其底部对齐第三层层板底部
- const postCenterY = thirdShelfBottomY + postHeight / 2;
-
- post.position.set(
- x - shelfLength / 2,
- postCenterY,
- z
- );
-
- scene.add(post);
-
- // ---------------------
- // 创建贴纸(附加在外侧面上)
- // ---------------------
- const stickerWidth = 0.08; // 贴纸宽度
- const stickerHeight = 0.2; // 贴纸高度
-
- const stickerGeometry = new THREE.PlaneGeometry(stickerWidth, stickerHeight);
- const sticker = new THREE.Mesh(stickerGeometry, stickerMaterial);
-
- // 将贴纸放在柱子外侧表面中间位置
- sticker.position.set(
- post.position.x + postWidth / 2, // 柱子外侧
- post.position.y, // Y 中心
- post.position.z // Z 对齐
- );
-
- // 让贴纸始终面向相机(可选)
- // 使用 dcm.orientation.lookAt(camera.position) 或手动设置朝向
- // 这里简单设置固定朝向正面(可根据需求扩展为自动朝向相机)
- sticker.lookAt(new THREE.Vector3(1, 0, 0)); // 面向 X 正方向
-
- // 微调:让贴纸略微浮出柱子表面,避免深度冲突
- sticker.position.x += 0.01;
-
- scene.add(sticker);
- });
- });
- })
+ // 光源
+ 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() {
@@ -888,6 +975,7 @@ function cleanupThree() {
padding: 10px;
background: #f5f5f5;
border-bottom: 1px solid #ccc;
+ flex-wrap: wrap;
}
.dialog-container {