|
|
@ -16,7 +16,7 @@ |
|
|
<el-button>打开材质</el-button> |
|
|
<el-button>打开材质</el-button> |
|
|
</el-upload> |
|
|
</el-upload> |
|
|
<el-button @click="addConveyor">添加输送线</el-button> |
|
|
<el-button @click="addConveyor">添加输送线</el-button> |
|
|
<el-button @click="createShelf">添加货架</el-button> |
|
|
<el-button @click="createShelf">添加货架2</el-button> |
|
|
<el-button @click="createGroundStore">添加地堆</el-button> |
|
|
<el-button @click="createGroundStore">添加地堆</el-button> |
|
|
<div class="demo-color-block"> |
|
|
<div class="demo-color-block"> |
|
|
<span class="demonstration">材质颜色</span> |
|
|
<span class="demonstration">材质颜色</span> |
|
|
@ -86,10 +86,9 @@ import { TransformControls } from 'three/examples/jsm/controls/TransformControls |
|
|
import Split from '@/components/split/split.vue' |
|
|
import Split from '@/components/split/split.vue' |
|
|
import SplitArea from '@/components/split/split-area.vue' |
|
|
import SplitArea from '@/components/split/split-area.vue' |
|
|
import { renderIcon } from '@/utils/webutils.js' |
|
|
import { renderIcon } from '@/utils/webutils.js' |
|
|
import textureUrl from '@/assets/images/conveyor/shapes/RibSideSkirtThumbnail.jpg' |
|
|
import rackPlatUrl from "@/assets/images/conveyor/shapes/RackPlatform.png"; |
|
|
import moveUrl from '@/assets/images/conveyor/shapes/move.svg' |
|
|
import rackBlue from "@/assets/images/conveyor/shapes/Rack-blue.png"; |
|
|
import arrowRightUrl from '@/assets/images/conveyor/shapes/arrow-right.svg' |
|
|
import triangleUrl from "@/assets/images/conveyor/shapes/triangle.png"; |
|
|
import rackUrl from '@/assets/images/conveyor/shapes/Rack.png' |
|
|
|
|
|
|
|
|
|
|
|
// DOM refs |
|
|
// DOM refs |
|
|
const canvasContainer = ref(null) |
|
|
const canvasContainer = ref(null) |
|
|
@ -169,77 +168,119 @@ watch(() => restate.mode, (newVal) => { |
|
|
tcontrols.setMode(newVal) |
|
|
tcontrols.setMode(newVal) |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
function addConveyor(){ |
|
|
function addConveyor(){//输送线 |
|
|
const conveyorLength = 10; // 长度 |
|
|
// 1. 创建几何体:长 10 米,宽 0.8 米 |
|
|
const conveyorWidth = 0.8; // 宽度 |
|
|
const length = 20; // 长度 (X轴) |
|
|
const conveyorHeight = 0.08; // 厚度 |
|
|
const width = 0.8; // 宽度 (Y轴) |
|
|
const wallHeight = conveyorHeight + 0.15; // 挡板高出输送线 0.2 米 |
|
|
const height = 0.2; // 厚度/高度 (Z轴) |
|
|
|
|
|
|
|
|
const legWidth = 0.1; // 柱子宽度(X) |
|
|
const geometry = new THREE.BoxGeometry(length, width, height); |
|
|
const legDepth = 0.1; // 柱子深度(Z) |
|
|
|
|
|
const legHeight = 0.8; // 柱子高度(Y) |
|
|
// 2. 加载纹理 |
|
|
|
|
|
const textureLoader = new THREE.TextureLoader(); |
|
|
// 创建输送线底部板子(灰色) |
|
|
textureLoader.load(textureUrl, (texture)=>{ |
|
|
const conveyorGeometry = new THREE.BoxGeometry(conveyorWidth, conveyorHeight, conveyorLength); |
|
|
// 3. 设置纹理参数 |
|
|
const conveyorMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); |
|
|
texture.wrapS = THREE.RepeatWrapping; // X方向重复(实际会拉伸) |
|
|
const conveyor = new THREE.Mesh(conveyorGeometry, conveyorMaterial); |
|
|
texture.wrapT = THREE.RepeatWrapping; // Y方向重复 |
|
|
conveyor.position.y = conveyorHeight / 2; |
|
|
texture.center.set(0.5,0.5) |
|
|
|
|
|
texture.rotation = Math.PI / 2 |
|
|
// 添加两侧挡板(灰色) |
|
|
|
|
|
const wallWidth = 0.05; |
|
|
texture.repeat.set(1, 1); // 初始不缩放,后面根据需要调整 |
|
|
const wallGeometry = new THREE.BoxGeometry(wallWidth, wallHeight, conveyorLength); |
|
|
|
|
|
const wallMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); |
|
|
// 4. 根据输送线长度自动调整Y方向的重复次数 |
|
|
|
|
|
// 假设你的纹理高度是 256px,想要在 0.8m 宽度上显示完整的一行 |
|
|
const leftWall = new THREE.Mesh(wallGeometry, wallMaterial); |
|
|
// 所以我们计算 repeat.y = 0.8 / (texture.height / texture.width * length) |
|
|
leftWall.position.set(-(conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0); |
|
|
// 或者你可以手动设置 repeat.y 来控制重复频率 |
|
|
|
|
|
texture.repeat.set(1, 20); // 调整这个值来控制Y方向的重复频率 |
|
|
const rightWall = new THREE.Mesh(wallGeometry, wallMaterial); |
|
|
|
|
|
rightWall.position.set((conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0); |
|
|
// 5. 创建材质并应用纹理 |
|
|
|
|
|
const material = new THREE.MeshBasicMaterial( |
|
|
// 创建柱子(灰色,类似桌腿) |
|
|
{ map: texture,color: 0x9f9f9f}); |
|
|
const legGeometry = new THREE.BoxGeometry(legWidth, legHeight, legDepth); |
|
|
|
|
|
const legMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }); // 灰色 |
|
|
// 6. 创建网格对象 |
|
|
|
|
|
const conveyorBelt = new THREE.Mesh(geometry, material); |
|
|
const legPositions = [ |
|
|
|
|
|
[conveyorWidth / 2 - legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 右前 |
|
|
// 7. 可选:旋转为X轴方向(默认PlaneGeometry是X/Y平面) |
|
|
[-conveyorWidth / 2 + legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 左前 |
|
|
conveyorBelt.rotation.x = -Math.PI / 2; // 让其平放在地面上(X/Z 平面) |
|
|
[conveyorWidth / 2 - legWidth / 2, +(conveyorLength / 2 - legDepth / 2)], // 右后 |
|
|
|
|
|
[-conveyorWidth / 2 + legWidth / 2, +(conveyorLength / 2 - legDepth / 2)] // 左后 |
|
|
// 放置到合适的位置(如果需要) |
|
|
]; |
|
|
conveyorBelt.position.y = height / 2; // 让底部贴地 |
|
|
|
|
|
// // 示例:在输送线不同位置添加多个标记 |
|
|
const legs = []; |
|
|
addMarkerAt(-9.5, height,0,moveUrl); // 起点 |
|
|
|
|
|
addMarkerAt(0, height,0,arrowRightUrl); // 中间 |
|
|
legPositions.forEach(pos => { |
|
|
addMarkerAt(9.5, height,0,moveUrl); // 终点 |
|
|
const leg = new THREE.Mesh(legGeometry, legMaterial); |
|
|
scene.add(conveyorBelt) |
|
|
leg.position.set( |
|
|
// 动画函数 |
|
|
pos[0], |
|
|
let offsetSpeed = 0.01; // 控制滚动速度 |
|
|
-legHeight / 2, |
|
|
function animate() { |
|
|
pos[1] |
|
|
requestAnimationFrame(animate); |
|
|
); |
|
|
|
|
|
legs.push(leg); |
|
|
// 沿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 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(); |
|
|
const loader = new THREE.TextureLoader(); |
|
|
loader.load(textUrl, (texture) => { |
|
|
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({ |
|
|
const markerMaterial = new THREE.MeshBasicMaterial({ |
|
|
map: texture, |
|
|
map: texture, |
|
|
@ -257,126 +298,172 @@ function addMarkerAt(x, y,z,textUrl, scale = 1) { |
|
|
// 旋转为 X/Z 平面方向(与输送线一致) |
|
|
// 旋转为 X/Z 平面方向(与输送线一致) |
|
|
marker.rotation.x = -Math.PI / 2; // 和 conveyorBelt 一样旋转角度 |
|
|
marker.rotation.x = -Math.PI / 2; // 和 conveyorBelt 一样旋转角度 |
|
|
marker.rotation.z = Math.PI / 2; // 在平面上旋转 90 度 |
|
|
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(){//创建货架 |
|
|
function createShelf(){//创建货架 |
|
|
const textureLoader = new THREE.TextureLoader(); |
|
|
const shelfLength = 5; |
|
|
const metalTexture = textureLoader.load('path/to/your/metal/texture.jpg'); // 替换为实际路径 |
|
|
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 textureLoader = new THREE.TextureLoader(); |
|
|
const material = new THREE.MeshStandardMaterial({ |
|
|
const shelfTexture = textureLoader.load(rackPlatUrl, () => { |
|
|
map: metalTexture, |
|
|
renderer.render(scene, camera); |
|
|
metalness: 1.0, |
|
|
}); |
|
|
roughness: 0.5 |
|
|
const redPoleTexture = textureLoader.load(rackBlue, () => { |
|
|
|
|
|
renderer.render(scene, camera); // 确保贴图加载后重新渲染 |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 货架参数 |
|
|
shelfTexture.wrapS = THREE.RepeatWrapping; |
|
|
const shelfLength = 10; // 总长 |
|
|
shelfTexture.wrapT = THREE.RepeatWrapping; |
|
|
const shelfWidth = 0.8; // 宽度(深度) |
|
|
shelfTexture.repeat.set(1, 20); |
|
|
const shelfThickness = 0.1; // 层板厚度 |
|
|
shelfTexture.rotation = Math.PI / 2; |
|
|
const rows = 3; // 层数 |
|
|
|
|
|
const columns = 4; // 列数 |
|
|
const shelfMaterial = new THREE.MeshPhongMaterial({ |
|
|
const spacingY = 1; // 层间距 |
|
|
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++) { |
|
|
for (let row = 0; row < rows; row++) { |
|
|
|
|
|
let currentXOffset = -shelfLength / 2; |
|
|
|
|
|
|
|
|
for (let col = 0; col < columns; col++) { |
|
|
for (let col = 0; col < columns; col++) { |
|
|
const geometry = new THREE.BoxGeometry(unitLength, shelfThickness, shelfWidth); |
|
|
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 x = currentXOffset + unitLength / 2; |
|
|
const y = -(shelfThickness + spacingY) * row - shelfThickness / 2 + baseHeight; // 加上偏移 |
|
|
// 计算层板的 Y 坐标,使其基于地面高度 |
|
|
|
|
|
const y = baseHeight + row * spacingY + shelfThickness / 2; |
|
|
const z = 0; |
|
|
const z = 0; |
|
|
|
|
|
|
|
|
mesh.position.set(x, y, z); |
|
|
mesh.position.set(x, y, z); |
|
|
scene.add(mesh); |
|
|
scene.add(mesh); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --------------------- |
|
|
|
|
|
// 计算第三层层板底部位置(用于支撑柱对齐) |
|
|
|
|
|
// --------------------- |
|
|
|
|
|
const thirdShelfCenterY = -(shelfThickness + spacingY) * 2 - shelfThickness / 2 + baseHeight; |
|
|
|
|
|
const thirdShelfBottomY = thirdShelfCenterY - shelfThickness / 2; |
|
|
|
|
|
|
|
|
|
|
|
// --------------------- |
|
|
|
|
|
// 创建支撑柱 + 添加贴纸 |
|
|
|
|
|
// --------------------- |
|
|
|
|
|
|
|
|
|
|
|
const postWidth = 0.1; |
|
|
if (row === 0) { // 只在第一层添加垂直柱子 |
|
|
const postDepth = 0.1; |
|
|
// 最左侧柱子 |
|
|
const postHeight = 3.3; |
|
|
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 postPositionsX = [0, 2.5, 5, 7.5, 10]; |
|
|
// 新增红色柱子,在原有柱子的前面或后面并排 |
|
|
const postPositionsZ = [-shelfWidth / 2, shelfWidth / 2]; |
|
|
const redPoleX = poleX; // 和灰柱 X 相同 |
|
|
|
|
|
const redPoleY = poleY; // 和灰柱 Y 相同 |
|
|
|
|
|
const redPoleZ = z + shelfWidth / 2-poleSize/2; // 在前方 |
|
|
|
|
|
|
|
|
// 加载贴纸纹理(可以是透明PNG) |
|
|
const redPoleMaterial = new THREE.MeshPhongMaterial({ |
|
|
const loader = new THREE.TextureLoader(); |
|
|
color: 0xff0000, |
|
|
loader.load(rackUrl, (texture) => { |
|
|
shininess: 60 |
|
|
// 创建贴纸材质(支持透明) |
|
|
|
|
|
const stickerMaterial = new THREE.MeshBasicMaterial({ |
|
|
|
|
|
map: texture, |
|
|
|
|
|
transparent: true, |
|
|
|
|
|
side: THREE.DoubleSide // 双面可见 |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
postPositionsX.forEach(x => { |
|
|
const redPole = new THREE.Mesh( |
|
|
postPositionsZ.forEach(z => { |
|
|
new THREE.BoxGeometry(poleSize, totalHeight, poleSize), |
|
|
// 创建柱子 |
|
|
redPoleMaterial |
|
|
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 |
|
|
|
|
|
); |
|
|
); |
|
|
|
|
|
redPole.position.set(redPoleX, redPoleY, redPoleZ); |
|
|
|
|
|
scene.add(redPole); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
scene.add(post); |
|
|
// 最右侧柱子 |
|
|
|
|
|
if (col === columns - 1) { |
|
|
// --------------------- |
|
|
const rightPoleX = currentXOffset + unitLength + gapBetweenPlanks - poleSize / 2; |
|
|
// 创建贴纸(附加在外侧面上) |
|
|
const rightPoleY = baseHeight + totalHeight / 2; |
|
|
// --------------------- |
|
|
const rightPoleZ = z - shelfWidth / 2 + poleSize / 2; |
|
|
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( |
|
|
const rightPole = new THREE.Mesh( |
|
|
post.position.x + postWidth / 2, // 柱子外侧 |
|
|
new THREE.BoxGeometry(poleSize, totalHeight, poleSize), |
|
|
post.position.y, // Y 中心 |
|
|
poleMaterial |
|
|
post.position.z // Z 对齐 |
|
|
|
|
|
); |
|
|
); |
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 让贴纸始终面向相机(可选) |
|
|
currentXOffset += unitLength + gapBetweenPlanks; |
|
|
// 使用 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() { |
|
|
function createGroundStore() { |
|
|
@ -888,6 +975,7 @@ function cleanupThree() { |
|
|
padding: 10px; |
|
|
padding: 10px; |
|
|
background: #f5f5f5; |
|
|
background: #f5f5f5; |
|
|
border-bottom: 1px solid #ccc; |
|
|
border-bottom: 1px solid #ccc; |
|
|
|
|
|
flex-wrap: wrap; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.dialog-container { |
|
|
.dialog-container { |
|
|
|