18 changed files with 1602 additions and 4 deletions
@ -0,0 +1,400 @@ |
|||
import * as THREE from "three"; |
|||
import {CSG} from "three-csg-ts"; |
|||
//@ts-ignore
|
|||
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' |
|||
|
|||
export default class CC53DGraphics { |
|||
|
|||
// 创建cc5的底座
|
|||
static createCC5Pedestal(): THREE.BufferGeometry { |
|||
|
|||
const width = 1.65 |
|||
const depth = 1.65 |
|||
const dd = 0.05 |
|||
const bd = 0.175 |
|||
|
|||
const shape = new THREE.Shape() |
|||
shape.moveTo(-width / 2, -depth / 2 + bd) |
|||
shape.lineTo(-width / 2 + bd, -depth / 2) |
|||
shape.lineTo(width / 2 - bd, -depth / 2) |
|||
shape.lineTo(width / 2, -depth / 2 + bd) |
|||
|
|||
shape.lineTo(width / 2, -0.285) |
|||
shape.lineTo(-0.475, -0.285) |
|||
shape.lineTo(-0.475, -0.125) |
|||
shape.lineTo(width / 2, -0.125) |
|||
shape.lineTo(width / 2, 0.125) |
|||
shape.lineTo(-0.475, 0.125) |
|||
shape.lineTo(-0.475, 0.285) |
|||
shape.lineTo(width / 2, 0.285) |
|||
|
|||
shape.lineTo(width / 2, depth / 2 - bd) |
|||
shape.lineTo(width / 2 - bd, depth / 2) |
|||
shape.lineTo(-width / 2 + bd, depth / 2) |
|||
shape.lineTo(-width / 2, depth / 2 - bd) |
|||
shape.closePath() // 闭合路径
|
|||
|
|||
// 拉伸轨迹线
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const options = { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
const geometry = new THREE.ExtrudeGeometry(shape, options) |
|||
const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) |
|||
const mesh = new THREE.Mesh(geometry, material) |
|||
|
|||
mesh.updateMatrix() |
|||
|
|||
// 倒角
|
|||
const shapeD = new THREE.Shape() |
|||
shapeD.moveTo(-0.02, -0.02) |
|||
shapeD.lineTo(0.02, 0.02) |
|||
shapeD.lineTo(-0.02, 0.02) |
|||
shapeD.closePath() |
|||
|
|||
const shapeDW = new THREE.Shape() |
|||
shapeDW.moveTo(0.02, -0.02) |
|||
shapeDW.lineTo(0.02, 0.02) |
|||
shapeDW.lineTo(-0.02, 0.02) |
|||
shapeDW.closePath() |
|||
|
|||
const shapeDW1 = new THREE.Shape() |
|||
shapeDW1.moveTo(-0.02, -0.02) |
|||
shapeDW1.lineTo(0.02, -0.02) |
|||
shapeDW1.lineTo(-0.02, 0.02) |
|||
shapeDW1.closePath() |
|||
|
|||
|
|||
const positionDs: THREE.Vector3[] = [ |
|||
new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd), |
|||
new THREE.Vector3(-width / 2 + bd, 0.20, -depth / 2), |
|||
new THREE.Vector3(width / 2 - bd, 0.20, -depth / 2), |
|||
new THREE.Vector3(width / 2, 0.20, -depth / 2 + bd), |
|||
new THREE.Vector3(width / 2, 0.20, depth / 2 - bd), |
|||
new THREE.Vector3(width / 2 - bd, 0.20, depth / 2), |
|||
new THREE.Vector3(-width / 2 + bd, 0.20, depth / 2), |
|||
new THREE.Vector3(-width / 2, 0.20, depth / 2 - bd), |
|||
new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd) |
|||
] |
|||
|
|||
let result: THREE.Mesh = mesh |
|||
|
|||
for (let i = 0; i < positionDs.length - 1; i++) { |
|||
|
|||
const curveD = new THREE.CatmullRomCurve3([positionDs[i], positionDs[i + 1]], |
|||
false, |
|||
'catmullrom', |
|||
0) |
|||
const optionsD = { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveD |
|||
} |
|||
let geometryD: THREE.BufferGeometry = null |
|||
if (i == 1) { |
|||
geometryD = new THREE.ExtrudeGeometry(shapeDW, optionsD) |
|||
} else if (i == 5) { |
|||
geometryD = new THREE.ExtrudeGeometry(shapeDW1, optionsD) |
|||
} else { |
|||
geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) |
|||
} |
|||
const meshD = new THREE.Mesh(geometryD, material) |
|||
meshD.updateMatrix() |
|||
result = CSG.subtract(result, meshD) |
|||
} |
|||
return result.geometry |
|||
} |
|||
|
|||
// 创建cc5的立柱
|
|||
static createCC5Pillar(): THREE.BufferGeometry { |
|||
// 606.5
|
|||
const width = 0.3 |
|||
const depth = 1.188 |
|||
const dd = 0.05 |
|||
const shape = new THREE.Shape() |
|||
shape.moveTo(-0.744, -0.594 + dd) |
|||
shape.lineTo(-0.744 + dd, -0.594) |
|||
shape.lineTo(-0.744 + width - dd, -0.594) |
|||
shape.lineTo(-0.744 + width, -0.594 + dd) |
|||
|
|||
shape.lineTo(-0.744 + width, -0.4) |
|||
shape.lineTo(-0.744 + width - 0.08, -0.4) |
|||
shape.lineTo(-0.744 + width - 0.08, 0.4) |
|||
shape.lineTo(-0.744 + width, 0.4) |
|||
|
|||
shape.lineTo(-0.744 + width, -0.594 + depth - dd) |
|||
shape.lineTo(-0.744 + width - dd, -0.594 + depth) |
|||
shape.lineTo(-0.744 + dd, -0.594 + depth) |
|||
shape.lineTo(-0.744, -0.594 + depth - dd) |
|||
// shape.lineTo(-0.728, -0.594 + dd);
|
|||
// shape.closePath()
|
|||
|
|||
// 拉伸轨迹线
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 3.357, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const options = { |
|||
steps: 1, |
|||
bevelSegments: 0.05, |
|||
bevelEnabled: true, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
// const material = new THREE.MeshBasicMaterial({color: 0xffdddbca});
|
|||
// const mesh = new THREE.Mesh(geometry, material);
|
|||
return new THREE.ExtrudeGeometry(shape, options) |
|||
|
|||
} |
|||
|
|||
// 创建cc5的叉
|
|||
static createCC5Fork(): THREE.BufferGeometry { |
|||
// 606.5
|
|||
const width = 0.3 |
|||
const depth = 1.188 |
|||
const dd = 0.05 |
|||
const fdx = 0.06 |
|||
const fdy = 0.03 |
|||
const shape = new THREE.Shape() |
|||
|
|||
shape.moveTo(-0.744 + width - 0.08, -0.4) |
|||
shape.lineTo(-0.744 + width + 0.02, -0.4) |
|||
shape.lineTo(-0.744 + width + 0.02, -0.275) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.275) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.275 + fdy) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.135 - fdy) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.135) |
|||
shape.lineTo(-0.744 + width + 0.02, -0.135) |
|||
shape.lineTo(-0.744 + width + 0.02, 0.135) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.135) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.135 + fdy) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.275 - fdy) |
|||
shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.275) |
|||
shape.lineTo(-0.744 + width + 0.02, 0.275) |
|||
shape.lineTo(-0.744 + width + 0.02, 0.4) |
|||
shape.lineTo(-0.744 + width - 0.08, 0.4) |
|||
|
|||
shape.closePath() |
|||
|
|||
// 拉伸轨迹线
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const options = { |
|||
steps: 1, |
|||
bevelSegments: 0.05, |
|||
bevelEnabled: true, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
const geometry = new THREE.ExtrudeGeometry(shape, options) |
|||
const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) |
|||
const mesh = new THREE.Mesh(geometry, material) |
|||
mesh.updateMatrix() |
|||
|
|||
const shapeD = new THREE.Shape() |
|||
|
|||
|
|||
shapeD.moveTo(-0.744 + width + 0.02, -0.3) |
|||
shapeD.lineTo(-0.744 + width + 0.02 + 1.3, -0.3) |
|||
shapeD.lineTo(-0.744 + width + 0.02 + 1.3, 0.3) |
|||
shapeD.lineTo(-0.744 + width + 0.02, 0.3) |
|||
shape.closePath() |
|||
|
|||
// 拉伸轨迹线
|
|||
const curveD = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 1.3, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const optionsD = { |
|||
steps: 1, |
|||
bevelSegments: 0.05, |
|||
bevelEnabled: true, |
|||
extrudePath: curveD // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) |
|||
const meshD = new THREE.Mesh(geometryD, material) |
|||
|
|||
meshD.updateMatrix() |
|||
|
|||
// 布尔运算
|
|||
const result = CSG.subtract(mesh, meshD) |
|||
|
|||
return result.geometry |
|||
} |
|||
|
|||
// 创建cc5的铰链
|
|||
static createCC5Gemel(isLeft: boolean = false) { |
|||
const width = 0.08 |
|||
const depth = 0.9 |
|||
const dd = 0.02 |
|||
const shape = new THREE.Shape() |
|||
shape.moveTo(-width / 2, -depth / 2 + dd) |
|||
shape.lineTo(-width / 2 + dd, -depth / 2) |
|||
shape.lineTo(width / 2 - dd, -depth / 2) |
|||
shape.lineTo(width / 2, -depth / 2 + dd) |
|||
shape.lineTo(width / 2, depth / 2 - dd) |
|||
shape.lineTo(width / 2 - dd, depth / 2) |
|||
shape.lineTo(-width / 2 + dd, depth / 2) |
|||
shape.lineTo(-width / 2, depth / 2 - dd) |
|||
shape.closePath() |
|||
|
|||
const shapeBar = new THREE.Shape() |
|||
shapeBar.moveTo(0, 0) // 起点在圆心
|
|||
shapeBar.absarc(0, 0, 0.02, 0, Math.PI * 2, false) // 从0到π绘制半圆
|
|||
shapeBar.closePath() |
|||
|
|||
const geometries: THREE.BufferGeometry[] = [] |
|||
|
|||
const curveL1 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(-0.35 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.30 + (isLeft ? 0.05 : 0), 0, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const curveL2 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(-0.15 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.1 + (isLeft ? 0.05 : 0), 0, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const curveL3 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0.15 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.1 - (isLeft ? 0.05 : 0), 0, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const curveL4 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0.30 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.35 - (isLeft ? 0.05 : 0), 0, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
if (!isLeft) { |
|||
const curveBar1 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(-0.36, -0.4, 0), new THREE.Vector3(0.36, -0.4, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const curveBar2 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(-0.36, 0, 0), new THREE.Vector3(0.36, 0, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const curveBar3 = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(-0.36, 0.4, 0), new THREE.Vector3(0.36, 0.4, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
geometries.push(new THREE.ExtrudeGeometry(shapeBar, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveBar1 |
|||
})) |
|||
geometries.push(new THREE.ExtrudeGeometry(shapeBar, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveBar2 |
|||
})) |
|||
geometries.push(new THREE.ExtrudeGeometry(shapeBar, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveBar3 |
|||
})) |
|||
} |
|||
|
|||
const geometryL1 = new THREE.ExtrudeGeometry(shape, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveL1 |
|||
}) |
|||
|
|||
const geometryL2 = new THREE.ExtrudeGeometry(shape, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveL2 |
|||
}) |
|||
|
|||
const geometryL3 = new THREE.ExtrudeGeometry(shape, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveL3 |
|||
}) |
|||
|
|||
const geometryL4 = new THREE.ExtrudeGeometry(shape, { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curveL4 |
|||
}) |
|||
|
|||
geometries.push(geometryL1, geometryL2, geometryL3, geometryL4) |
|||
|
|||
return mergeGeometries(geometries) |
|||
} |
|||
|
|||
// 创建cc5叉的背板
|
|||
static createCC5ForkBasePlate(): THREE.BufferGeometry { |
|||
// 606.5
|
|||
const width = 0.3 |
|||
const depth = 1.188 |
|||
const dd = 0.05 |
|||
const fdx = 0.06 |
|||
const fdy = 0.03 |
|||
|
|||
const shape = new THREE.Shape(); |
|||
shape.moveTo(-0.744 + 0.1, -0.5); |
|||
shape.lineTo(-0.744 + 0.1, 0.5); |
|||
shape.lineTo(-0.744 + 0.2, 0.5); |
|||
shape.lineTo(-0.744 + 0.2, -0.5); |
|||
shape.closePath() |
|||
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, -1.5/*0.02*/, 0), new THREE.Vector3(0, 1.287, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
const options = { |
|||
steps: 1, |
|||
bevelSegments: 0.05, |
|||
bevelEnabled: true, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
return new THREE.ExtrudeGeometry(shape, options) |
|||
} |
|||
|
|||
static cc5PedestalGeometry: THREE.BufferGeometry = null |
|||
static cc5PillarGeometry: THREE.BufferGeometry = null |
|||
static cc5ForkGeometry: THREE.BufferGeometry = null |
|||
static cc5GemelGeometryL: THREE.BufferGeometry = null |
|||
static cc5GemelGeometryR: THREE.BufferGeometry = null |
|||
static cc5ForkBasePlateGeometry: THREE.BufferGeometry = null |
|||
} |
|||
@ -0,0 +1,305 @@ |
|||
import * as THREE from 'three' |
|||
import {CSG} from 'three-csg-ts' |
|||
import gsap from 'gsap' |
|||
//@ts-ignore
|
|||
import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' |
|||
import CC53DGraphics from "@/modules/amr/cc/cc5/CC53DGraphics"; |
|||
import Viewport from "@/core/engine/Viewport"; |
|||
import PtrObject from "@/modules/amr/ptr/PtrObject"; |
|||
import {worldModel} from "@/core/manager/WorldModel"; |
|||
|
|||
|
|||
export default class CC53dObject extends PtrObject { |
|||
|
|||
override __rotationSpeed = (Math.PI / 10) |
|||
override __showForkSpeed: number = 0.15 |
|||
override __upForkSpeed: number = 0.15 |
|||
|
|||
private get upForkSpeed() { |
|||
return this.__upForkSpeed * (worldModel.state.runState.timeRate ?? 1) |
|||
} |
|||
private get showForkSpeed() { |
|||
return this.__showForkSpeed * (worldModel.state.runState.timeRate ?? 1) |
|||
} |
|||
private get rotationSpeed() { |
|||
return this.__rotationSpeed * (worldModel.state.runState.timeRate ?? 1) |
|||
} |
|||
|
|||
constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { |
|||
super(item, viewport) |
|||
|
|||
if (!CC53DGraphics.cc5PedestalGeometry) { |
|||
CC53DGraphics.cc5PedestalGeometry = CC53DGraphics.createCC5Pedestal() |
|||
} |
|||
const cc5PedestalGeometry = CC53DGraphics.cc5PedestalGeometry |
|||
const cc5PedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) |
|||
const cc5PedestalMesh = new THREE.Mesh(cc5PedestalGeometry, cc5PedestalMaterial) |
|||
|
|||
if (!CC53DGraphics.cc5PillarGeometry) { |
|||
CC53DGraphics.cc5PillarGeometry = CC53DGraphics.createCC5Pillar() |
|||
} |
|||
const cc5PillarGeometry = CC53DGraphics.cc5PillarGeometry |
|||
const cc5PillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) |
|||
const cc5PillarMesh = new THREE.Mesh(cc5PillarGeometry, cc5PillarMaterial) |
|||
|
|||
if (!CC53DGraphics.cc5ForkGeometry) { |
|||
CC53DGraphics.cc5ForkGeometry = CC53DGraphics.createCC5Fork() |
|||
} |
|||
const cc5ForkGeometry = CC53DGraphics.cc5ForkGeometry |
|||
const cc5ForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) |
|||
const cc5ForkMesh = new THREE.Mesh(cc5ForkGeometry, cc5ForkMaterial) |
|||
cc5ForkMesh.name = 'cc5Fork' |
|||
|
|||
if (!CC53DGraphics.cc5GemelGeometryL) { |
|||
CC53DGraphics.cc5GemelGeometryL = CC53DGraphics.createCC5Gemel(true) |
|||
} |
|||
const cc5GemelGeometryL = CC53DGraphics.cc5GemelGeometryL |
|||
|
|||
if (!CC53DGraphics.cc5GemelGeometryR) { |
|||
CC53DGraphics.cc5GemelGeometryR = CC53DGraphics.createCC5Gemel(false) |
|||
} |
|||
const cc5GemelGeometryR = CC53DGraphics.cc5GemelGeometryR |
|||
|
|||
const cc5GemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) |
|||
const cc5GemelMeshL1 = new THREE.Mesh(cc5GemelGeometryL, cc5GemelMaterial) |
|||
cc5GemelMeshL1.name = 'cc5GemelMeshL1' |
|||
const cc5GemelMeshL2 = new THREE.Mesh(cc5GemelGeometryL, cc5GemelMaterial) |
|||
cc5GemelMeshL2.name = 'cc5GemelMeshL2' |
|||
const cc5GemelMeshR1 = new THREE.Mesh(cc5GemelGeometryR, cc5GemelMaterial) |
|||
cc5GemelMeshR1.name = 'cc5GemelMeshR1' |
|||
const cc5GemelMeshR2 = new THREE.Mesh(cc5GemelGeometryR, cc5GemelMaterial) |
|||
cc5GemelMeshR2.name = 'cc5GemelMeshR2' |
|||
|
|||
if (!CC53DGraphics.cc5ForkBasePlateGeometry) { |
|||
CC53DGraphics.cc5ForkBasePlateGeometry = CC53DGraphics.createCC5ForkBasePlate() |
|||
} |
|||
const cc5ForkBasePlateGeometry = CC53DGraphics.cc5ForkBasePlateGeometry |
|||
const cc5ForkBasePlateMesh = new THREE.Mesh(cc5ForkBasePlateGeometry, cc5GemelMaterial) |
|||
cc5ForkBasePlateMesh.name = 'cc5ForkBasePlateMesh' |
|||
|
|||
const d = 0 |
|||
|
|||
this.add(cc5PedestalMesh) |
|||
this.add(cc5PillarMesh) |
|||
this.add(cc5ForkMesh) |
|||
|
|||
|
|||
const ac = Math.asin(d / (2 * 0.8)) |
|||
|
|||
|
|||
cc5GemelMeshL1.position.z = 0.5 - d / 4 |
|||
cc5GemelMeshL1.position.y = 0.72 |
|||
cc5GemelMeshL1.rotation.x = -ac |
|||
|
|||
cc5GemelMeshL2.position.z = 0.5 - d / 4 * 3 |
|||
cc5GemelMeshL2.position.y = 0.72 |
|||
cc5GemelMeshL2.rotation.x = -ac |
|||
|
|||
cc5GemelMeshR1.position.z = 0.5 - d / 4 |
|||
cc5GemelMeshR1.position.y = 0.72 |
|||
cc5GemelMeshR1.rotation.x = ac |
|||
|
|||
cc5GemelMeshR2.position.z = 0.5 - d / 4 * 3 |
|||
cc5GemelMeshR2.position.y = 0.72 |
|||
cc5GemelMeshR2.rotation.x = ac |
|||
|
|||
this.add(cc5GemelMeshL1) |
|||
this.add(cc5GemelMeshL2) |
|||
this.add(cc5GemelMeshR1) |
|||
this.add(cc5GemelMeshR2) |
|||
this.add(cc5ForkBasePlateMesh) |
|||
} |
|||
|
|||
override animationShowFork(z: number): Promise<void> { |
|||
const cc5Fork = this.getObjectByName('cc5Fork') |
|||
const cc5GemelMeshL1 = this.getObjectByName('cc5GemelMeshL1') |
|||
const cc5GemelMeshL2 = this.getObjectByName('cc5GemelMeshL2') |
|||
const cc5GemelMeshR1 = this.getObjectByName('cc5GemelMeshR1') |
|||
const cc5GemelMeshR2 = this.getObjectByName('cc5GemelMeshR2') |
|||
const cc5ForkBasePlateMesh = this.getObjectByName('cc5ForkBasePlateMesh') |
|||
|
|||
const fy = cc5Fork.position.y |
|||
|
|||
const time = Math.abs((cc5Fork.position.z - z) / this.upForkSpeed) |
|||
|
|||
const ac = Math.asin(z / (2 * 0.8)) |
|||
|
|||
// cc5GemelMeshL1.position.z = 0.5 - z/4
|
|||
// cc5GemelMeshL1.position.y = fy + 0.72
|
|||
// cc5GemelMeshL1.rotation.x = -ac
|
|||
gsap.to(cc5GemelMeshL1.position, { |
|||
z: 0.5 - z / 4, |
|||
y: fy + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshL1.rotation, { |
|||
x: -ac, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
// cc5GemelMeshL2.position.z = 0.5 - z/4 * 3
|
|||
// cc5GemelMeshL2.position.y = fy + 0.72
|
|||
// cc5GemelMeshL2.rotation.x = -ac
|
|||
gsap.to(cc5GemelMeshL2.position, { |
|||
z: 0.5 - z / 4 * 3, |
|||
y: fy + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshL2.rotation, { |
|||
x: -ac, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
|
|||
// cc5GemelMeshR1.position.z = 0.5 - z/4
|
|||
// cc5GemelMeshR1.position.y = fy + 0.72
|
|||
// cc5GemelMeshR1.rotation.x = ac
|
|||
gsap.to(cc5GemelMeshR1.position, { |
|||
z: 0.5 - z / 4, |
|||
y: fy + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshR1.rotation, { |
|||
x: ac, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
|
|||
// cc5GemelMeshR2.position.z = 0.5 - z/4 * 3
|
|||
// cc5GemelMeshR2.position.y = fy + 0.72
|
|||
// cc5GemelMeshR2.rotation.x = ac
|
|||
gsap.to(cc5GemelMeshR2.position, { |
|||
z: 0.5 - z / 4 * 3, |
|||
y: fy + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshR2.rotation, { |
|||
x: ac, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
|
|||
gsap.to(cc5ForkBasePlateMesh.position, { |
|||
y: fy, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut' |
|||
}) |
|||
|
|||
return new Promise(resolve => { |
|||
this.actionAnimation = gsap.to(cc5Fork.position, { |
|||
z: -z, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'power1.inOut', |
|||
onComplete: ()=>{ |
|||
setTimeout(() => { |
|||
resolve() |
|||
}, 1000 / (worldModel.state.runState.timeRate ?? 1)) |
|||
}, |
|||
}) |
|||
}) |
|||
|
|||
} |
|||
|
|||
override animationHideFork(): Promise<void> { |
|||
return this.animationShowFork(0) |
|||
} |
|||
|
|||
override animationUpFork(y: number): Promise<void> { |
|||
const cc5Fork = this.getObjectByName('cc5Fork') |
|||
const cc5GemelMeshL1 = this.getObjectByName('cc5GemelMeshL1') |
|||
const cc5GemelMeshL2 = this.getObjectByName('cc5GemelMeshL2') |
|||
const cc5GemelMeshR1 = this.getObjectByName('cc5GemelMeshR1') |
|||
const cc5GemelMeshR2 = this.getObjectByName('cc5GemelMeshR2') |
|||
const cc5ForkBasePlateMesh = this.getObjectByName('cc5ForkBasePlateMesh') |
|||
|
|||
|
|||
const time = Math.abs((cc5Fork.position.y - y) / this.upForkSpeed) |
|||
|
|||
gsap.to(cc5GemelMeshL1.position, { |
|||
y: y + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshL2.position, { |
|||
y: y + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshR1.position, { |
|||
y: y + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut' |
|||
}) |
|||
gsap.to(cc5GemelMeshR2.position, { |
|||
y: y + 0.72, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut' |
|||
}) |
|||
gsap.to(cc5ForkBasePlateMesh.position, { |
|||
y: y, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut' |
|||
}) |
|||
|
|||
return new Promise(resolve => { |
|||
const bh = 0.22 |
|||
const children = cc5Fork.children |
|||
|
|||
this.actionAnimation = gsap.to(cc5Fork.position, { |
|||
y: y, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut', |
|||
onComplete: ()=>{ |
|||
setTimeout(() => { |
|||
resolve() |
|||
}, 1000 / (worldModel.state.runState.timeRate ?? 1)) |
|||
}, |
|||
onUpdate: function() { |
|||
const a = this.targets()[0] |
|||
if (a.y < bh) { |
|||
if (a.z > -1) { |
|||
for (let i = 0; i < children.length; i++) { |
|||
const child = children[i] |
|||
child.position.y = bh - a.y |
|||
} |
|||
} else if (a.y < 0 ) { |
|||
for (let i = 0; i < children.length; i++) { |
|||
const child = children[i] |
|||
child.position.y = 0 - a.y |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
override animationDownFork(): Promise<void> { |
|||
return this.animationUpFork(0) |
|||
} |
|||
|
|||
override getArmObject(): THREE.Object3D | undefined { |
|||
const cc5Fork = this.getObjectByName('cc5Fork') |
|||
return cc5Fork |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
import BaseEntity from '@/core/base/BaseItemEntity.ts' |
|||
import type Viewport from '@/core/engine/Viewport.ts' |
|||
import * as THREE from 'three' |
|||
import gsap from 'gsap' |
|||
import CC53dObject from './CC53dObject.ts' |
|||
|
|||
export default class CC5Entity extends BaseEntity { |
|||
constructor(viewport: Viewport, id: string) { |
|||
super(viewport, id) |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
import BaseInteraction from '@/core/base/BaseInteraction.ts' |
|||
import * as THREE from 'three' |
|||
|
|||
export default class CC5Interaction 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 |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; |
|||
import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; |
|||
|
|||
const propertySetter: PropertySetter = { |
|||
flatten: { |
|||
fields: [ |
|||
...basicFieldsSetter, |
|||
{ |
|||
dataPath: 'dt.clxWidth', label: 'CC5宽度', input: 'InputNumber', |
|||
inputProps: {}, |
|||
}, |
|||
{ |
|||
dataPath: 'dt.clxDepth', label: 'CC5深度', input: 'InputNumber', |
|||
inputProps: {}, |
|||
}, |
|||
{ |
|||
dataPath: 'state', label: 'CC5控制', input: 'PtrController', |
|||
inputProps: {}, |
|||
}, |
|||
], |
|||
}, |
|||
}; |
|||
|
|||
export default propertySetter; |
|||
@ -0,0 +1,119 @@ |
|||
import * as THREE from 'three' |
|||
import BaseRenderer from '@/core/base/BaseRenderer.ts' |
|||
import Constract from '@/core/Constract.ts' |
|||
import CC53dObject from './CC53dObject' |
|||
import type { Object3DLike } from '@/types/ModelTypes.ts' |
|||
import Viewport from "@/core/engine/Viewport"; |
|||
|
|||
/** |
|||
* clx渲染器 |
|||
*/ |
|||
export default class CC5Renderer extends BaseRenderer { |
|||
static POINT_NAME = 'cc5' |
|||
|
|||
pointMaterial: THREE.Material |
|||
|
|||
/** |
|||
* 默认点的高度, 防止和地面重合 |
|||
*/ |
|||
readonly defulePositionY: number = Constract.HEIGHT_WAY |
|||
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.65, 3.393, 1.65) |
|||
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) |
|||
readonly defaultLineWidth: number = 0.15 |
|||
|
|||
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.') |
|||
} |
|||
|
|||
|
|||
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { |
|||
// 创建平面几何体
|
|||
const group = new CC53dObject(item, this.tempViewport, option) |
|||
group.name = CC5Renderer.POINT_NAME |
|||
|
|||
// 设置位置
|
|||
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) |
|||
return group |
|||
} |
|||
|
|||
/** |
|||
* 如果某物品要放到 Cl2 上 返回可存放的位置和角度一个 Position 和 Rotation |
|||
*/ |
|||
getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0) |
|||
: { |
|||
position: [number, number, number], rotation: [number, number, number], |
|||
getParentObject3D?: (viewport: Viewport, parent: THREE.Object3D) => THREE.Object3D |
|||
} { |
|||
|
|||
return { |
|||
position: [0, 0.2, -0.15], |
|||
rotation: [0, 90, 0], |
|||
getParentObject3D: this.getArmObject.bind(this) |
|||
} |
|||
} |
|||
getArmObject(viewport: Viewport, item: ItemJson): THREE.Object3D { |
|||
// 获取机械臂对象
|
|||
const object = viewport.entityManager.findObjectById(item.dt.storeAt?.item) |
|||
if (!object) { |
|||
console.warn('PtrRenderer: getArmObject failed, not found Cl2:', item.dt.storeAt?.item) |
|||
return |
|||
} |
|||
|
|||
const agv = object as THREE.Group |
|||
const clxFork = agv.getObjectByName('clxFork') |
|||
return clxFork |
|||
} |
|||
|
|||
updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike { |
|||
const group: THREE.Group = object as 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]) |
|||
) |
|||
|
|||
// 禁止缩放,
|
|||
item.tf[2][0] = this.defaultScale.x |
|||
item.tf[2][1] = this.defaultScale.y |
|||
item.tf[2][2] = this.defaultScale.z |
|||
|
|||
return group |
|||
} |
|||
|
|||
dispose() { |
|||
super.dispose() |
|||
this.pointMaterial?.dispose() |
|||
} |
|||
|
|||
createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { |
|||
throw new Error('Clx createPointBasic not allow!') |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { defineModule } from '@/core/manager/ModuleManager.ts' |
|||
import CC5Renderer from './CC5Renderer.ts' |
|||
import CC5Interaction from './CC5Interaction.ts' |
|||
import propertySetter from './CC5PropertySetter.ts' |
|||
|
|||
export const ITEM_TYPE_NAME = 'cc5' |
|||
|
|||
export default defineModule(ITEM_TYPE_NAME, () => ({ |
|||
renderer: new CC5Renderer(ITEM_TYPE_NAME), |
|||
interaction: new CC5Interaction(ITEM_TYPE_NAME), |
|||
setter: propertySetter |
|||
})) |
|||
@ -0,0 +1,198 @@ |
|||
import * as THREE from "three"; |
|||
import {CSG} from "three-csg-ts"; |
|||
//@ts-ignore
|
|||
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' |
|||
|
|||
export default class FM6003DGraphics { |
|||
|
|||
// 创建ptr的底座
|
|||
public static createFm600Pedestal(): THREE.BufferGeometry { |
|||
// 参数配置
|
|||
const radius = 0.475 // 圆半径
|
|||
const lineDist = 0.325 // 切割线距离圆心距离
|
|||
const segments = 64 // 圆的分段精度
|
|||
|
|||
// 计算切割线与圆的交点
|
|||
const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) |
|||
const startAngle = Math.asin(intersectY / radius) |
|||
const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius)
|
|||
|
|||
|
|||
const shape = new THREE.Shape() |
|||
shape.moveTo(0, 0) // 起点在圆心
|
|||
shape.absarc(0, 0, radius, startAngle, endAngle, false) // 从0到π绘制半圆
|
|||
// shape.lineTo(-lineDist, intersectY)
|
|||
shape.absarc(0, 0, radius, startAngle + Math.PI, endAngle + Math.PI, false) |
|||
|
|||
|
|||
// shape.lineTo(lineDist, -intersectY)
|
|||
// shape.lineTo(-0.5, -intersectY + 0.17)
|
|||
// shape.lineTo(0.75, -intersectY + 0.17)
|
|||
// shape.lineTo(0.75, intersectY - 0.17)
|
|||
// shape.lineTo(-0.5, intersectY - 0.17)
|
|||
|
|||
shape.closePath() // 闭合路径
|
|||
|
|||
// 拉伸轨迹线
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.025, 0), new THREE.Vector3(0, 0.235, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const options = { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
const geometry = new THREE.ExtrudeGeometry(shape, options) |
|||
const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) |
|||
const mesh = new THREE.Mesh(geometry, material) |
|||
|
|||
mesh.updateMatrix() |
|||
|
|||
// 倒角
|
|||
const shapeD = new THREE.Shape() |
|||
shapeD.moveTo(-0.035, -0.02) |
|||
shapeD.lineTo(0.035, 0.02) |
|||
shapeD.lineTo(-0.035, 0.02) |
|||
shapeD.closePath() |
|||
|
|||
const curveD = new THREE.EllipseCurve( |
|||
0, 0, radius, radius, 0, Math.PI * 2, false, 0 |
|||
) |
|||
// 生成拉伸路径点
|
|||
const pointsD = curveD.getPoints(segments).map(p => |
|||
new THREE.Vector3(p.x, 0.200, p.y) |
|||
) |
|||
// 3. 挤出成型
|
|||
const optionsD = { |
|||
steps: segments, |
|||
bevelEnabled: false, |
|||
// bevelSegments: 0.01,
|
|||
extrudePath: new THREE.CatmullRomCurve3(pointsD) |
|||
} |
|||
|
|||
const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) |
|||
const meshD = new THREE.Mesh(geometryD, material) |
|||
meshD.updateMatrix() |
|||
|
|||
// 布尔运算
|
|||
const result = CSG.subtract(mesh, meshD) |
|||
|
|||
return result.geometry |
|||
} |
|||
|
|||
// 创建fm600的顶盘
|
|||
public static createFm600Plate(): THREE.BufferGeometry { |
|||
// 参数配置
|
|||
const radius = 0.425 // 圆半径
|
|||
const lineDist = 0.3 // 切割线距离圆心距离
|
|||
const segments = 64 // 圆的分段精度
|
|||
|
|||
// 计算切割线与圆的交点
|
|||
const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) |
|||
const startAngle = Math.asin(intersectY / radius) |
|||
const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius)
|
|||
|
|||
|
|||
const shape = new THREE.Shape() |
|||
shape.moveTo(0, 0) // 起点在圆心
|
|||
shape.absarc(0, 0, radius, startAngle, endAngle, false) // 从0到π绘制半圆
|
|||
// shape.lineTo(-lineDist, intersectY)
|
|||
shape.absarc(0, 0, radius, startAngle + Math.PI, endAngle + Math.PI, false) |
|||
shape.closePath() // 闭合路径
|
|||
|
|||
|
|||
// 拉伸轨迹线
|
|||
const curve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.24, 0), new THREE.Vector3(0, 0.255, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
|
|||
// 挤出几何图形 参数
|
|||
const options = { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: curve // 设置挤出轨迹
|
|||
} |
|||
// 创建挤出几何体
|
|||
const geometry = new THREE.ExtrudeGeometry(shape, options) |
|||
|
|||
const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) |
|||
const mesh = new THREE.Mesh(geometry, material) |
|||
|
|||
mesh.updateMatrix() |
|||
|
|||
|
|||
const holeShape = new THREE.Shape() |
|||
holeShape.moveTo(0, 0) |
|||
holeShape.absarc(0, 0, 0.1, 0, Math.PI * 2, false) // 从0到π绘制半圆
|
|||
// 拉伸轨迹线
|
|||
const holeCurve = new THREE.CatmullRomCurve3( |
|||
[new THREE.Vector3(0, 0.24, 0), new THREE.Vector3(0, 0.255, 0)], |
|||
false, // 闭合曲线
|
|||
'catmullrom', |
|||
0 |
|||
) |
|||
// 挤出几何图形 参数
|
|||
const holeOptions = { |
|||
steps: 1, |
|||
bevelEnabled: false, |
|||
extrudePath: holeCurve // 设置挤出轨迹
|
|||
} |
|||
|
|||
// 创建挤出几何体
|
|||
const geometryD = new THREE.ExtrudeGeometry(holeShape, holeOptions) |
|||
const meshD = new THREE.Mesh(geometryD, material) |
|||
meshD.updateMatrix() |
|||
// 布尔运算
|
|||
const result = CSG.subtract(mesh, meshD) |
|||
return result.geometry |
|||
} |
|||
|
|||
public static createFm600Header(): THREE.BufferGeometry { |
|||
|
|||
const radius = 0.475 // 圆半径
|
|||
const lineDist = 0.325 // 切割线距离圆心距离
|
|||
const segments = 64 // 圆的分段精度
|
|||
|
|||
// 计算切割线与圆的交点
|
|||
const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) |
|||
const startAngle = Math.asin(intersectY / radius) |
|||
const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius)
|
|||
|
|||
const shapeH = new THREE.Shape() |
|||
shapeH.moveTo(-0.02, -0.02) |
|||
shapeH.lineTo(0.02, 0.02) |
|||
shapeH.lineTo(-0.02, 0.02) |
|||
shapeH.closePath() |
|||
|
|||
const curveH = new THREE.EllipseCurve( |
|||
0, 0, radius-0.02, radius-0.02, Math.PI * 2 - startAngle + 0.1, startAngle - 0.1,false, 0 |
|||
) |
|||
// 生成拉伸路径点
|
|||
const pointsH = curveH.getPoints(segments).map(p => |
|||
new THREE.Vector3(p.x, 0.210, p.y) |
|||
) |
|||
const optionsH = { |
|||
steps: segments, |
|||
bevelEnabled: false, |
|||
// bevelSegments: 0.01,
|
|||
extrudePath: new THREE.CatmullRomCurve3(pointsH) |
|||
} |
|||
const geometryH = new THREE.ExtrudeGeometry(shapeH, optionsH) |
|||
return geometryH |
|||
} |
|||
|
|||
|
|||
public static fm600PedestalGeometry: THREE.BufferGeometry = null |
|||
public static fm600PlateGeometry: THREE.BufferGeometry = null |
|||
public static fm600HeaderGeometry: THREE.BufferGeometry = null |
|||
|
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
import * as THREE from 'three' |
|||
import {CSG} from 'three-csg-ts' |
|||
import gsap from 'gsap' |
|||
import mqtt from 'mqtt' |
|||
import {Euler} from 'three/src/math/Euler' |
|||
import FM600Entity from './FM600Entity' |
|||
import FM6003DGraphics from "./FM6003DGraphics" |
|||
import PtrObject from "@/modules/amr/ptr/PtrObject"; |
|||
import Viewport from "@/core/engine/Viewport"; |
|||
import {worldModel} from "@/core/manager/WorldModel"; |
|||
|
|||
export default class FM6003dObject extends PtrObject { |
|||
|
|||
private _fm600Entity: FM600Entity = null |
|||
|
|||
override __rotationSpeed = (Math.PI / 7) |
|||
override __showForkSpeed: number = 0.2 |
|||
override __upForkSpeed: number = 0.05 |
|||
|
|||
private get upForkSpeed() { |
|||
return this.__upForkSpeed * (worldModel.state.runState.timeRate ?? 1) |
|||
} |
|||
private get rotationSpeed() { |
|||
return this.__rotationSpeed * (worldModel.state.runState.timeRate ?? 1) |
|||
} |
|||
|
|||
public get fm600Entity(): FM600Entity { |
|||
if (!this._fm600Entity) { |
|||
const fm600: FM600Entity = Model.getFm600(this.item.id) as FM600Entity |
|||
this._fm600Entity = fm600 |
|||
} |
|||
return this._fm600Entity |
|||
} |
|||
|
|||
constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { |
|||
super(item, viewport) |
|||
|
|||
if (!FM6003DGraphics.fm600PedestalGeometry) { |
|||
FM6003DGraphics.fm600PedestalGeometry = FM6003DGraphics.createFm600Pedestal() |
|||
} |
|||
// "#ff3232"
|
|||
const fm600PedestalGeometry = FM6003DGraphics.fm600PedestalGeometry |
|||
const fm600PedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffababab}) |
|||
const fm600PedestalMesh = new THREE.Mesh(fm600PedestalGeometry, fm600PedestalMaterial) |
|||
const groupPedestal = new THREE.Group() |
|||
groupPedestal.name = 'fm600Pedestal' |
|||
groupPedestal.add(fm600PedestalMesh) |
|||
|
|||
if (!FM6003DGraphics.fm600HeaderGeometry) { |
|||
FM6003DGraphics.fm600HeaderGeometry = FM6003DGraphics.createFm600Header() |
|||
} |
|||
const fm600HeaderGeometry = FM6003DGraphics.fm600HeaderGeometry |
|||
const fm600HeaderMaterial = new THREE.MeshPhongMaterial({color: 0xffff3232}) |
|||
const fm600HeaderMesh = new THREE.Mesh(fm600HeaderGeometry, fm600HeaderMaterial) |
|||
groupPedestal.add(fm600HeaderMesh) |
|||
|
|||
if (!FM6003DGraphics.fm600PlateGeometry) { |
|||
FM6003DGraphics.fm600PlateGeometry = FM6003DGraphics.createFm600Plate() |
|||
} |
|||
const fm600PlateGeometry = FM6003DGraphics.fm600PlateGeometry |
|||
// "#1f1d1d"
|
|||
const fm600PlateMaterial = new THREE.MeshPhongMaterial({color: 0xff4d4d4d}) |
|||
const pfm600PlateMesh = new THREE.Mesh(fm600PlateGeometry, fm600PlateMaterial) |
|||
pfm600PlateMesh.name = 'fm600Plate' |
|||
|
|||
|
|||
|
|||
this.add(groupPedestal) |
|||
this.add(pfm600PlateMesh) |
|||
} |
|||
|
|||
/*==========动画处理============*/ |
|||
|
|||
|
|||
// 取货
|
|||
override addLoad(height: number, goodsId: string): void { |
|||
this.actionAnimation = 'wq' |
|||
this.TaskMode = 2 |
|||
this.OperationType = 1 |
|||
this.animationUpPlate(0.1).then( |
|||
() => { |
|||
try { |
|||
// 将物品拾取到机械臂上
|
|||
const mesh = this.pickupItem(goodsId) |
|||
mesh.position.set(0, 0, -0.15) |
|||
mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) |
|||
this.getArmObject().add(mesh) |
|||
} catch (e) { |
|||
console.error(e) |
|||
} |
|||
} |
|||
) |
|||
} |
|||
|
|||
// 卸货
|
|||
override addUnload(height: number, goodsId: string): void { |
|||
this.actionAnimation = 'wq' |
|||
this.TaskMode = 2 |
|||
this.OperationType = 2 |
|||
this.animationDownPlate().then( |
|||
() => { |
|||
try { |
|||
const a = this.agvStatusVo.unloadBasLocationVo |
|||
// 将物品从机械臂上卸下
|
|||
this.dropItem(goodsId, a.rack, a.bay, a.level, a.cell) |
|||
} catch (e) { |
|||
console.error(e) |
|||
} |
|||
} |
|||
) |
|||
} |
|||
|
|||
animationUpPlate(y: number): Promise<void> { |
|||
|
|||
const fm600Plate = this.getObjectByName('fm600Plate') |
|||
const time = Math.abs((fm600Plate.position.y - y) / this.upForkSpeed) |
|||
|
|||
return new Promise(resolve => { |
|||
const bh = 0.32 |
|||
const children = fm600Plate.children |
|||
|
|||
this.actionAnimation = gsap.to(fm600Plate.position, { |
|||
y: y, |
|||
duration: time, |
|||
repeat: 0, |
|||
ease: 'sine.inOut', |
|||
onComplete: ()=>{ |
|||
setTimeout(() => { |
|||
resolve() |
|||
}, 1000 / (worldModel.state.runState.timeRate ?? 1)) |
|||
}, |
|||
onUpdate: function () { |
|||
const a = this.targets()[0] |
|||
if (a.y < bh) { |
|||
if (a.y < 0) { |
|||
for (let i = 0; i < children.length; i++) { |
|||
const child = children[i] |
|||
child.position.y = 0 - a.y |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
|
|||
override animationDownPlate(): Promise<void> { |
|||
return this.animationUpFork(0) |
|||
} |
|||
|
|||
override getArmObject(): THREE.Object3D | undefined { |
|||
return this.getObjectByName('fm600Plate') |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,120 @@ |
|||
import * as THREE from 'three' |
|||
import BaseEntity from '@/core/base/BaseItemEntity.ts' |
|||
import type Viewport from '@/core/engine/Viewport.ts' |
|||
import gsap from 'gsap' |
|||
import { nextTick } from 'vue' |
|||
import FM6003dObject from "./FM6003dObject"; |
|||
/** |
|||
* CL2 机械臂实体类 |
|||
* 0.4m/ss // a max 1.2m/s
|
|||
* 90 = 3.5s cl2 |
|||
* 90 = 5s // cLX
|
|||
*/ |
|||
export default class FM600Entity extends BaseEntity { |
|||
constructor(viewport: Viewport, id: string) { |
|||
super(viewport, id) |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
// 抬
|
|||
addArmRaise(height: number) { |
|||
// super.addArmRaise(height)
|
|||
this.taskQueue.add(this.createTask('ARM_RAISE', |
|||
() => this.cl2Object.animationUpFork(height) |
|||
)) |
|||
} |
|||
|
|||
// 降
|
|||
addArmLower() { |
|||
// super.addArmLower()
|
|||
this.taskQueue.add(this.createTask('ARM_LOWER', |
|||
() => this.cl2Object.animationDownFork() |
|||
)) |
|||
} |
|||
|
|||
// 伸
|
|||
addArmExtender(z: number = 1.2) { |
|||
// super.addArmExtender()
|
|||
this.taskQueue.add(this.createTask('ARM_EXTEND', |
|||
() => this.cl2Object.animationShowFork(z) |
|||
)) |
|||
} |
|||
|
|||
// 缩
|
|||
addArmRetractor() { |
|||
// super.addArmRetractor()
|
|||
this.taskQueue.add(this.createTask('ARM_RETRACT', |
|||
() => this.cl2Object.animationHideFork() |
|||
)) |
|||
} |
|||
// 装
|
|||
addLoad(item: string): void { |
|||
|
|||
const ptrForkMesh = this.getArmObject() |
|||
this.taskQueue.add(this.createTask('LOAD', async () => { |
|||
// 实际业务中应包含装载逻辑
|
|||
this.isCarrying = true |
|||
this.isLoading = true |
|||
// 将物品拾取到机械臂上
|
|||
const mesh = this.pickupItem(item) |
|||
mesh.position.set(0, 0, -0.15) |
|||
mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) |
|||
ptrForkMesh.add(mesh) |
|||
})) |
|||
|
|||
this.taskQueue.add(this.createTask('ARM_RAISE', |
|||
()=>{ |
|||
return new Promise(resolve => { |
|||
gsap.to(ptrForkMesh.position, { |
|||
y: ptrForkMesh.position.y + 0.2, |
|||
duration: 1, |
|||
ease: 'sine.inOut', |
|||
onComplete: () => { |
|||
this.isCarrying = true |
|||
this.isLoading = false |
|||
resolve() |
|||
} |
|||
}) |
|||
}) |
|||
})) |
|||
} |
|||
|
|||
// 卸
|
|||
addUnload(itemId: string, rackId: string, bay?: number, level?: number, cell?: number): void { |
|||
this.taskQueue.add(this.createTask('UNLOAD', async () => { |
|||
const item = this.viewport.entityManager.findItemById(itemId) |
|||
this.isCarrying = false |
|||
this.isUnloading = false |
|||
|
|||
// 将物品从机械臂上卸下
|
|||
this.dropItem(item, rackId, bay, level) |
|||
})) |
|||
} |
|||
|
|||
// 帮助方法:获取机械臂对象
|
|||
getArmObject(): THREE.Object3D | undefined { |
|||
const agv = this.object as THREE.Group |
|||
if (agv.children.length > 1) { |
|||
const pillar = agv.children[1] |
|||
if (pillar.children.length > 1) { |
|||
return pillar.children[1] |
|||
} |
|||
} |
|||
return undefined |
|||
} |
|||
|
|||
// 帮助方法:获取机械臂立柱
|
|||
getArmPillar(): THREE.Object3D | undefined { |
|||
const agv = this.object as THREE.Group |
|||
if (agv.children.length > 1) { |
|||
return agv.children[1] |
|||
} |
|||
return undefined |
|||
} |
|||
|
|||
get cl2Object(): FM6003dObject { |
|||
return this.object as FM6003dObject |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
import BaseInteraction from '@/core/base/BaseInteraction.ts' |
|||
import * as THREE from 'three' |
|||
|
|||
export default class FM600Interaction 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 |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
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: {}, |
|||
}, |
|||
{ |
|||
dataPath: 'state', label: 'ptr控制', input: 'PtrController', |
|||
inputProps: {}, |
|||
}, |
|||
], |
|||
}, |
|||
}; |
|||
|
|||
export default propertySetter; |
|||
@ -0,0 +1,122 @@ |
|||
import * as THREE from 'three' |
|||
import BaseRenderer from '@/core/base/BaseRenderer.ts' |
|||
import Constract from '@/core/Constract.ts' |
|||
import FM6003dObject from './FM6003dObject' |
|||
import type { Object3DLike } from '@/types/ModelTypes.ts' |
|||
import type Viewport from '@/core/engine/Viewport.ts' |
|||
|
|||
/** |
|||
* ptr侧叉渲染器 |
|||
*/ |
|||
export default class FM600Renderer extends BaseRenderer { |
|||
static POINT_NAME = 'fm600' |
|||
|
|||
pointMaterial: THREE.Material |
|||
|
|||
/** |
|||
* 默认点的高度, 防止和地面重合 |
|||
*/ |
|||
readonly defulePositionY: number = Constract.HEIGHT_WAY |
|||
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.98, 1.5) |
|||
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) |
|||
|
|||
constructor(itemTypeName: string) { |
|||
super(itemTypeName) |
|||
} |
|||
|
|||
/** |
|||
* 如果某物品要放到 Cl2 上 返回可存放的位置和角度一个 Position 和 Rotation |
|||
*/ |
|||
getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0) |
|||
: { |
|||
position: [number, number, number], rotation: [number, number, number], |
|||
getParentObject3D?: (viewport: Viewport, parent: THREE.Object3D) => THREE.Object3D |
|||
} { |
|||
|
|||
return { |
|||
position: [0, 0.2, -0.15], |
|||
rotation: [0, 90, 0], |
|||
getParentObject3D: this.getArmObject.bind(this) |
|||
} |
|||
} |
|||
|
|||
getArmObject(viewport: Viewport, item: ItemJson): THREE.Object3D { |
|||
// 获取机械臂对象
|
|||
const object = viewport.entityManager.findObjectById(item.dt.storeAt?.item) |
|||
if (!object) { |
|||
console.warn('PtrRenderer: getArmObject failed, not found Cl2:', item.dt.storeAt?.item) |
|||
return |
|||
} |
|||
|
|||
const agv = object as THREE.Group |
|||
if (agv.children.length > 1) { |
|||
const pillar = agv.children[1] |
|||
if (pillar.children.length > 1) { |
|||
return pillar.children[1] |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 所有的点,必须使用 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.') |
|||
} |
|||
|
|||
|
|||
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { |
|||
// 创建平面几何体
|
|||
const group = new FM6003dObject(item, this.tempViewport, option) |
|||
group.name = FM600Renderer.POINT_NAME |
|||
|
|||
// 设置位置
|
|||
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) |
|||
return group |
|||
} |
|||
|
|||
updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike { |
|||
const group: THREE.Group = object as 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]) |
|||
) |
|||
|
|||
// 禁止缩放,
|
|||
item.tf[2][0] = this.defaultScale.x |
|||
item.tf[2][1] = this.defaultScale.y |
|||
item.tf[2][2] = this.defaultScale.z |
|||
|
|||
return group |
|||
} |
|||
|
|||
dispose() { |
|||
super.dispose() |
|||
this.pointMaterial?.dispose() |
|||
} |
|||
|
|||
createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { |
|||
throw new Error('Ptr createPointBasic not allow!') |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { defineModule } from '@/core/manager/ModuleManager.ts' |
|||
import FM600Renderer from './FM600Renderer.ts' |
|||
import FM600Interaction from './FM600Interaction.ts' |
|||
import propertySetter from './FM600PropertySetter.ts' |
|||
|
|||
export const ITEM_TYPE_NAME = 'fm600' |
|||
|
|||
export default defineModule(ITEM_TYPE_NAME, () => ({ |
|||
renderer: new FM600Renderer(ITEM_TYPE_NAME), |
|||
interaction: new FM600Interaction(ITEM_TYPE_NAME), |
|||
setter: propertySetter |
|||
})) |
|||
Loading…
Reference in new issue