Browse Source

GStore 和 Agv1

master
修宁 6 months ago
parent
commit
94c52628e1
  1. BIN
      src/assets/Models/flash_tp.fbx
  2. 1
      src/components/Model3DView.vue
  3. 88
      src/core/ModelUtils.ts
  4. 8
      src/core/ThreeExtend.ts
  5. 13
      src/core/manager/InstanceMeshManager.ts
  6. 19
      src/core/manager/SelectManager.ts
  7. 2
      src/core/manager/WorldModel.ts
  8. 3
      src/editor/ModelMainInit.ts
  9. 5
      src/example/example1.js
  10. 5
      src/modules/agv1/Agv1Entity.ts
  11. 22
      src/modules/agv1/Agv1Interaction.ts
  12. 20
      src/modules/agv1/Agv1PropertySetter.ts
  13. 48
      src/modules/agv1/Agv1Renderer.ts
  14. 15
      src/modules/agv1/index.ts
  15. 170
      src/modules/gstore/GstoreRenderer.ts
  16. 5
      src/runtime/System.ts
  17. 7
      src/types/ThreeExtend.d.ts

BIN
src/assets/Models/flash_tp.fbx

Binary file not shown.

1
src/components/Model3DView.vue

@ -929,6 +929,7 @@ function handleMtlUpload(file) {
}
function addGroupToScene(group) {
console.log('addGroupToScene', group)
modelGroup = group
if (restate.loadScale) {
//

88
src/core/ModelUtils.ts

@ -9,6 +9,10 @@ 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'
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'
/**
* 2D
@ -609,7 +613,6 @@ export function loadTexture(url: string) {
export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) {
if (ext.endsWith('.fbx')) {
system.showLoading()
const loader = new FBXLoader()
return loader.parse(arrayBuffer, '')
@ -632,9 +635,8 @@ export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) {
/**
* ItemJson , THREE.js
*/
export function getMatrixFromTf(tf: number[][]): THREE.Matrix4 {
export function getMatrixFromTf(tf: number[][], matrix = new THREE.Matrix4()): THREE.Matrix4 {
const [pos, rot, scale] = tf
const matrix = new THREE.Matrix4()
matrix.compose(
new THREE.Vector3(pos[0], pos[1], pos[2]),
new THREE.Quaternion().setFromEuler(new THREE.Euler(
@ -698,6 +700,74 @@ export function getAABBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] {
}
/**
* OBB , Line2
* @param matrix ItemJson
* @param material LineMaterial 线
* @param padding , 0
*/
export function drawOBBox(matrix: THREE.Matrix4 | ItemJson, material: LineMaterial, padding: number = 0): Line2 {
// 定义单位立方体的边对应的顶点索引
const edges = [
[0, 1], [1, 2], [2, 3], [3, 0],
[4, 5], [5, 6], [6, 7], [7, 4],
// [0, 4], [1, 5], [2, 6], [3, 7]
[0, 4], [4, 5],
[5, 1], [1, 2],
[2, 6], [6, 7],
[7, 3]
]
// 定义单位立方体的8个顶点位置
const verticesParam = [
-0.5 - padding, -0.5, 0.5 + padding,
0.5 + padding, -0.5, 0.5 + padding,
0.5 + padding, 0.5, 0.5 + padding,
-0.5 - padding, 0.5, 0.5 + padding,
-0.5 - padding, -0.5, -0.5 - padding,
0.5 + padding, -0.5, -0.5 - padding,
0.5 + padding, 0.5, -0.5 - padding,
-0.5 - padding, 0.5, -0.5 - padding
]
if (!(matrix instanceof THREE.Matrix4)) {
// 从 tf 读取的模型, 需要调整Y轴以使底面位于Y=0
matrix = getMatrixFromTf(matrix.tf)
for (let i = 1; i < verticesParam.length; i += 3) {
verticesParam[i] = verticesParam[i] + 0.5
}
}
const vertices = new Float32Array(verticesParam)
// 将顶点通过变换矩阵转换到世界坐标系中
const transformedVertices = []
for (let i = 0; i < vertices.length; i += 3) {
const vec = new THREE.Vector3(vertices[i], vertices[i + 1], vertices[i + 2])
vec.applyMatrix4(matrix)
transformedVertices.push(vec.x, vec.y, vec.z)
}
// 合并所有边的顶点信息
const edgePositions = []
edges.forEach(edge => {
edgePositions.push(
new THREE.Vector3(
transformedVertices[edge[0] * 3],
transformedVertices[edge[0] * 3 + 1],
transformedVertices[edge[0] * 3 + 2]
),
new THREE.Vector3(
transformedVertices[edge[1] * 3],
transformedVertices[edge[1] * 3 + 1],
transformedVertices[edge[1] * 3 + 2]
)
)
})
const geometry = new LineGeometry().setFromPoints(edgePositions)
return new Line2(geometry, material)
}
/**
* OBB
* @param matrix ItemJson
*/
@ -706,7 +776,10 @@ export function getOBBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] {
} else {
// item.tf = [[0, 0.1, 0], [0, 0, 0], [1, 1, 1]]
const height = matrix.tf[0][2]
matrix = getMatrixFromTf(matrix.tf)
matrix.setPosition(new THREE.Vector3(0, height / 2, 0)) // 确保 OBB 的中心在 (0, height/2, 0)
debugger
}
// 获取 OOBB 包围盒
@ -732,10 +805,11 @@ export function getOBBox(matrix: THREE.Matrix4 | ItemJson): THREE.Vector3[] {
const edges = [
[0, 1], [1, 2], [2, 3], [3, 0], // 侧面
[4, 5], [5, 6], [6, 7], [7, 4], // 右侧面
// 上面的点 2,3,6,7
[3, 2], [3, 7], [7, 6], [6, 2],
// 下面的点: 0,1,4,5
[0, 1], [1, 5], [5, 4], [4, 0]
[0, 4], [1, 5], [2, 6], [3, 7]
// // 上面的点 2,3,6,7
// [3, 2], [3, 7], [7, 6], [6, 2],
// // 下面的点: 0,1,4,5
// [0, 1], [1, 5], [5, 4], [4, 0]
]
edges.forEach(([a, b]) => {
const va = corners[a]

8
src/core/ThreeExtend.ts

@ -0,0 +1,8 @@
import * as THREE from 'three'
export function install() {
THREE.Object3D.prototype.setMatrix4 = function(matrix: THREE.Matrix4) {
debugger
this.matrix.copy(matrix)
}
}

13
src/core/manager/InstanceMeshManager.ts

@ -45,6 +45,17 @@ export default class InstanceMeshManager {
}
create(entityId: string, userData?: any): InstanceMeshWrap {
// 如果 entityId 存在,就替换
if (this.__uuidMap.has(entityId)) {
const existingWrap = this.__uuidMap.get(entityId)
if (existingWrap) {
if (userData) {
existingWrap.userData = userData || {}
}
return existingWrap
}
}
let meshIndex = -1
let blockIndex = -1
for (const block of this.blocks) {
@ -161,7 +172,7 @@ export class InstanceMeshWrap {
}
constructor(entityId: string, manager: InstanceMeshManager, blockIndex: number, meshIndex: number, userData?: any) {
this.uuid = system.createUUID()
this.uuid = entityId || system.createUUID()
this.entityId = entityId
this.manager = manager
this.meshIndex = meshIndex

19
src/core/manager/SelectManager.ts

@ -9,7 +9,7 @@ import { getSetter } from '@/core/manager/ModuleManager.ts'
import Constract from '@/core/Constract.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import { PointManageWrap } from '@/core/manager/InstancePointManager.ts'
import { getAABBox, getOBBox } from '@/core/ModelUtils.ts'
import { drawOBBox, getAABBox, getOBBox } from '@/core/ModelUtils.ts'
/**
*
@ -250,24 +250,9 @@ export default class SelectManager {
return
}
// 构造 positions 数组
const edgePositions = getOBBox(item) // getOBBox(item) // getAABBox(item)
const positions = []
for (let i = 0; i < edgePositions.length; i += 2) {
const p1 = edgePositions[i]
const p2 = edgePositions[i + 1]
positions.push(p1.x, p1.y, p1.z)
positions.push(p2.x, p2.y, p2.z)
}
const selectionBox = drawOBBox(item, this.yellowMaterial)
const lineGeom = new LineGeometry().setPositions(new Float32Array(positions))
const selectionBox = new Line2(lineGeom, this.yellowMaterial)
selectionBox.computeLineDistances()
selectionBox.scale.set(1, 1, 1)
this.selectionBox = selectionBox
console.log('selectedItem', this.viewport.state.selectedItem)
this.viewport.scene.add(selectionBox)
}

2
src/core/manager/WorldModel.ts

@ -8,6 +8,7 @@ import Rack from '@/modules/rack'
import ShuttleRack from '@/modules/shuttle_rack'
import Pallet from "@/modules/pallet"
import Tote from "@/modules/tote"
import Agv1 from "@/modules/agv1"
import Carton from "@/modules/carton"
import Ptr from "@/modules/ptr"
import Clx from "@/modules/clx"
@ -80,6 +81,7 @@ export default class WorldModel {
Carton,
Ptr,
Clx,
Agv1,
Charger
]).then(() => {

3
src/editor/ModelMainInit.ts

@ -13,6 +13,7 @@ import FileMenu from './menus/FileMenu'
import EditMenu from './menus/EditMenu'
import ToolsMenu from './menus/Tools'
import Model3DView from './menus/Model3DView'
import { install as ThreeExtendInstall } from '@/core/ThreeExtend.ts'
import { forEachMenu } from '@/runtime/DefineMenu'
import { normalizeShortKey } from '@/utils/webutils'
import WorldModel from '@/core/manager/WorldModel'
@ -37,6 +38,8 @@ export function ModelMainInit() {
const worldModel = new WorldModel()
window['worldModel'] = worldModel
ThreeExtendInstall()
}
export function ModelMainMounted() {

5
src/example/example1.js

@ -326,10 +326,11 @@ export default {
catalogCode: 'f3', t: 'floor',
items: [
{
id: 'gs1',
id: 'gstore1',
t: 'gstore',
// t: 'carton',
v: true,
tf: [[0, 0, 0], [0, 0, 0], [1, 0.5, 1]],
tf: [[0, 0, 0], [0, 0, 0], [2, 0.05, 1]],
dt: { in: [], out: [], center: [] }
}
]

5
src/modules/agv1/Agv1Entity.ts

@ -0,0 +1,5 @@
import BaseEntity from '@/core/base/BaseItemEntity.ts'
export default class Agv1Entity extends BaseEntity {
}

22
src/modules/agv1/Agv1Interaction.ts

@ -0,0 +1,22 @@
import BaseInteraction from '@/core/base/BaseInteraction.ts'
import * as THREE from 'three'
export default class Agv1Interaction 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
}
}

20
src/modules/agv1/Agv1PropertySetter.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;

48
src/modules/agv1/Agv1Renderer.ts

@ -0,0 +1,48 @@
import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import Constract from '@/core/Constract.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import MODULE_FBX_File from '@/assets/Models/flash_tp.fbx?url'
import { load3DModule, loadByUrl } from '@/core/ModelUtils.ts'
/**
*
*/
export default class Agv1Renderer extends BaseRenderer {
static POINT_NAME = 'agv1_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: 0x4559A0
}
meshPrototype: THREE.Group
init() {
return Promise.all([
super.init(),
loadByUrl(MODULE_FBX_File)
]).then(([_, { data: queue3dsFile }]) => {
this.meshPrototype = load3DModule(queue3dsFile, '.fbx').children[0] as THREE.Group
this.meshPrototype.scale.set(0.01, 0.01, 0.01)
})
}
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike {
return this.meshPrototype.clone()
}
dispose() {
super.dispose()
if (this.meshPrototype) {
this.meshPrototype.removeFromParent()
this.meshPrototype = null
}
}
}

15
src/modules/agv1/index.ts

@ -0,0 +1,15 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import Agv1Renderer from './Agv1Renderer.ts'
import Agv1Entity from './Agv1Entity.ts'
import Agv1Interaction from './Agv1Interaction.ts'
import propertySetter from './Agv1PropertySetter.ts'
export const ITEM_TYPE_NAME = 'agv1'
export default defineModule({
name: ITEM_TYPE_NAME,
renderer: new Agv1Renderer(ITEM_TYPE_NAME),
interaction: new Agv1Interaction(ITEM_TYPE_NAME),
setter: propertySetter,
entity: Agv1Entity
})

170
src/modules/gstore/GstoreRenderer.ts

@ -3,9 +3,10 @@ import BaseRenderer from '@/core/base/BaseRenderer.ts'
import Constract from '@/core/Constract.ts'
import { type Object3DLike } from '@/types/ModelTypes.ts'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { getOBBox } from '@/core/ModelUtils.ts'
import { getMatrixFromTf } from '@/core/ModelUtils.ts'
import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts'
import { translate } from 'element-plus'
/**
*
@ -29,76 +30,28 @@ export default class GstoreRenderer extends BaseRenderer {
strokeWidth: 0.08
}
pointGeometry = new THREE.PlaneGeometry(
1, 1
).rotateX(-Math.PI / 2)
pointGeometry = new THREE.BoxGeometry(
1, 1, 1
).translate(0, 0.5, 0)
pointMaterial: THREE.Material = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.5
})
strokeMaterial = new LineMaterial({
color: this.defaultPointOption.strokeColor,
transparent: false,
linewidth: this.defaultPointOption.strokeWidth,
gapSize: 0,
worldUnits: true
strokeMaterial = new THREE.MeshBasicMaterial({
color: 0x038217,
// transparent: true,
// opacity: 0.2,
side: THREE.DoubleSide
})
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.pointGeometry,
this.pointMaterial,
true, true)
)
}
strokeGeometry = new THREE.BoxGeometry(1, 1, 1)
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike {
return this.pointManager.createPoint(item)
}
afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: Object3DLike) {
super.afterCreateOrUpdatePoint(item, option, object)
const edgePositions = getOBBox(item)
// const positions = []
for (let i = 0; i < edgePositions.length; i += 2) {
const p1 = edgePositions[i]
const p2 = edgePositions[i + 1]
// positions.push(p1.x, p1.y, p1.z)
// positions.push(p2.x, p2.y, p2.z)
this.lineSegmentManager.createLine(item.id + '_l' + i, p1, p2, this.strokeMaterial.color)
}
// // 画边线
// const center = [item.tf[0][0], item.tf[0][2]]
// const h = (item.tf[0][1] || this.defulePositionY) + 0.01
// const widthHalf = item.tf[2][0] / 2
// const depthHalf = item.tf[2][2] / 2
// const lwHalf = (item.dt.strokeWidth || this.defaultPointOption.strokeWidth) / 2
// // 左上角
// const p1 = [center[0] - widthHalf + lwHalf, h, center[1] - depthHalf + lwHalf]
// // 右上角
// const p2 = [center[0] + widthHalf - lwHalf, h, center[1] - depthHalf + lwHalf]
// // 右下角
// const p3 = [center[0] + widthHalf - lwHalf, h, center[1] + depthHalf - lwHalf]
// // 左下角
// const p4 = [center[0] - widthHalf + lwHalf, h, center[1] + depthHalf - lwHalf]
//
// this.lineSegmentManager.createLine(item.id + '_l1', p1, p2, this.strokeMaterial.color)
// this.lineSegmentManager.createLine(item.id + '_l2', p2, p3, this.strokeMaterial.color)
// this.lineSegmentManager.createLine(item.id + '_l3', p3, p4, this.strokeMaterial.color)
// this.lineSegmentManager.createLine(item.id + '_l4', p4, p1, this.strokeMaterial.color)
}
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D {
throw new Error('not allow store line.')
}
@ -107,26 +60,109 @@ export default class GstoreRenderer extends BaseRenderer {
throw new Error('not allow store line.')
}
/**
* ,
* @param item
* @param option
* @param object
*/
afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: Object3DLike) {
super.afterCreateOrUpdatePoint(item, option, object)
// 目标物体 matrix
const matrix = getMatrixFromTf(item.tf)
const depth = item.dt.strokeWidth || 0.2
// 从矩阵分解位置、旋转和缩放
const position = new THREE.Vector3()
const rotation = new THREE.Quaternion()
const scale = new THREE.Vector3()
matrix.decompose(position, rotation, scale)
// 计算半尺寸(考虑建筑物缩放)
const halfWidth = scale.x / 2
const halfHeight = scale.y / 2
const halfLength = scale.z / 2
const halfDepth = depth / 2
position.y = position.y + halfHeight
// 计算向内偏移量(确保墙完全包含在内部)
const insetX = halfWidth - halfDepth
const insetZ = halfLength - halfDepth
// 定义四面墙的参数:位置偏移和缩放(全部向内偏移)
const walls = [
// 前墙 (Z+方向)
{
offset: new THREE.Vector3(0, 0, insetZ),
scale: new THREE.Vector3(scale.x, scale.y, depth)
},
// 后墙 (Z-方向)
{
offset: new THREE.Vector3(0, 0, -insetZ),
scale: new THREE.Vector3(scale.x, scale.y, depth)
},
// 左墙 (X-方向)
{
offset: new THREE.Vector3(-insetX, 0, 0),
scale: new THREE.Vector3(depth, scale.y, scale.z - depth * 2)
},
// 右墙 (X+方向)
{
offset: new THREE.Vector3(insetX, 0, 0),
scale: new THREE.Vector3(depth, scale.y, scale.z - depth * 2)
}
]
// 为每面墙创建变换矩阵
const tempMatrix = new THREE.Matrix4()
const tempPosition = new THREE.Vector3()
const tempScale = new THREE.Vector3()
walls.forEach((wall, i) => {
tempPosition.copy(wall.offset).applyQuaternion(rotation).add(position)
tempScale.copy(wall.scale)
tempMatrix.compose(tempPosition, rotation, tempScale)
const wrap1 = this.wallManager.create(item.id + '_' + i, {})
wrap1.setMatrix4(tempMatrix)
})
}
dispose() {
super.dispose()
this.pointGeometry.dispose()
this.pointMaterial.dispose()
this.strokeMaterial.dispose()
// this.strokeMaterial.dispose()
}
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.pointGeometry,
this.pointMaterial,
true, true)
)
}
get lineSegmentManager(): LineSegmentManager {
get wallManager(): InstanceMeshManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set.')
}
const name = this.itemTypeName + '_bj'
const name = 'GS_WALL'
return this.tempViewport.getOrCreateLineManager(name, () =>
return this.tempViewport.getOrCreateMeshManager(name, () => {
// 构建 LineSegment.points 代理对象
LineSegmentManager.create(name,
this.tempViewport,
this.strokeMaterial)
)
return new InstanceMeshManager(name, this.tempViewport, this.strokeGeometry, this.strokeMaterial,
false, false)
})
}
}

5
src/runtime/System.ts

@ -250,6 +250,8 @@ export default class System {
const _insId = _.uniqueId('_dlg')
console.trace()
console.time('showLoading')
system.rootElementList.push({
cmp: markRaw(LoadingDialog),
props: {
@ -267,6 +269,7 @@ export default class System {
* ...
*/
public clearLoading(): void {
console.timeEnd('showLoading')
if (typeof this.globalLoadingHandle === 'function') {
this.globalLoadingHandle()
}
@ -290,4 +293,4 @@ export interface ShowDialogOption {
showOkButton?: boolean
cancelButtonText?: string
okButtonText?: string
}
}

7
src/types/ThreeExtend.d.ts

@ -0,0 +1,7 @@
import * as THREE from 'three'
declare module 'three' {
interface Object3D {
setMatrix4(matrix: THREE.Matrix4): void;
}
}
Loading…
Cancel
Save