From e60f0f9185c4e2b4994610f463162de9c2761f0e Mon Sep 17 00:00:00 2001 From: yvan Date: Sat, 14 Jun 2025 22:16:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=B9=E5=99=A8=E7=AC=A6?= =?UTF-8?q?=E5=90=88=E5=B0=BA=E5=AF=B8=E5=AE=B9=E5=99=A8=E8=A6=81=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/ModelUtils.ts | 65 +++- src/core/base/BaseRenderer.ts | 47 +-- src/core/controls/DragControl.ts | 319 ----------------- src/core/controls/MouseMoveInspect.ts | 83 ----- src/core/controls/SelectInspect.ts | 441 ------------------------ src/core/engine/SceneHelp.ts | 10 +- src/core/engine/Viewport.ts | 42 +-- src/core/manager/EntityManager.ts | 4 +- src/core/manager/InstanceMeshManager.ts | 18 + src/core/manager/InstancePointManager.ts | 117 ++++--- src/core/manager/StateManager.ts | 6 +- src/editor/widgets/modeltree/ModeltreeViewJs.js | 2 +- src/example/ExampleUtil.js | 2 +- src/example/example1.js | 78 ++++- src/modules/carton/CartonEntity.ts | 23 +- src/modules/carton/CartonRenderer.ts | 14 +- src/modules/pallet/PalletRenderer.ts | 29 +- src/types/ModelTypes.ts | 2 +- 18 files changed, 318 insertions(+), 984 deletions(-) delete mode 100644 src/core/controls/DragControl.ts delete mode 100644 src/core/controls/MouseMoveInspect.ts delete mode 100644 src/core/controls/SelectInspect.ts diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 00be723..f028ead 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -244,7 +244,10 @@ export function getClosestObject(viewport: Viewport, object: THREE.Object3D, ins if (object.userData && object.userData.t && object.userData.entityId) { // 找到第一个有效的业务 Object3D if (object instanceof THREE.InstancedMesh && instanceId >= 0) { - return viewport.pointManagerMap.get(object.userData.t)?.findByMeshInstanceId(object.userData.blockIndex, instanceId) + const manager = viewport.pointManagerMap.get(object.userData.t) + if (manager) { + return manager.findByMeshInstanceId(object.userData.blockIndex, instanceId) + } } return object } @@ -254,6 +257,51 @@ export function getClosestObject(viewport: Viewport, object: THREE.Object3D, ins } /** + * 处理模型几何体,将其缩放到 1x1x1 的立方体,并平移到原点 + */ +export function processModel(mesh: THREE.Mesh) { + const geometry = mesh.geometry.clone() as THREE.BufferGeometry + + // 获取原始包围盒 + //@ts-ignore + const box = new THREE.Box3().setFromBufferAttribute(geometry.attributes.position) + const size = new THREE.Vector3() + box.getSize(size) + + // 计算缩放因子:让整个包围盒变成 1x1x1 的立方体 + const scaleX = 1 / size.x + const scaleY = 1 / size.y + const scaleZ = 1 / size.z + + // 缩放几何体到 1x1x1 立方体(保持原点不变) + geometry.scale(scaleX, scaleY, scaleZ) + + // 更新包围盒 + //@ts-ignore + const scaledBox = new THREE.Box3().setFromBufferAttribute(geometry.attributes.position) + const center = new THREE.Vector3() + scaledBox.getCenter(center) + + // 平移:让中心在 (0, height/2, 0),底部贴地 + const translate = new THREE.Matrix4().makeTranslation( + -center.x, + -scaledBox.min.y, // 保证底部贴地 + -center.z + ) + geometry.applyMatrix4(translate) + + // 可选:验证最终包围盒 + //@ts-ignore + // const finalBox = new THREE.Box3().setFromBufferAttribute(geometry.attributes.position) + // const finalSize = new THREE.Vector3() + // finalBox.getSize(finalSize) + // console.log('最终包围盒大小:', finalSize) // 应该是 (1, 1, 1) + // console.log('包围盒底部 Y 值:', finalBox.min.y) // 应该是 0 + + return geometry +} + +/** * 解析线条 ID, 返回 线条类型, 起点 ID 和终点 ID */ export function parseLineId(lineId): [LinkType, string, string] { @@ -296,7 +344,7 @@ export function deletePointByKeyboard() { system.msg('删除了 ' + deleteCount + ' 个实体') } - viewport.selectInspect.cancelMultiSelect() + viewport.selectManager.cancelMultiSelect() return } @@ -304,7 +352,7 @@ export function deletePointByKeyboard() { // 删除实体 if (deleteEntity(entityId)) { system.msg('删除实体 [' + entityId + '] 成功') - viewport.selectInspect.cancelSelect() + viewport.selectManager.cancelSelect() } }) } @@ -322,19 +370,19 @@ export function escByKeyboard(e: Event) { viewport.interactionManager.exitInteraction() system.msg('退出新建模式') - } else if (viewport.dragControl.isDragging) { + } else if (viewport.dragManager.isDragging) { // 2.取消拖拽 - viewport.dragControl.cancelDrag() + viewport.dragManager.cancelDrag() system.msg('取消拖拽') } else if (viewport.state.multiSelectedEntityIds?.length > 0) { // 3.取消多选 - viewport.selectInspect.cancelMultiSelect() + viewport.selectManager.cancelMultiSelect() system.msg('取消多选(红选)') } else if (viewport.state.selectedEntityId) { // 4.取消单选 - viewport.selectInspect.cancelSelect() + viewport.selectManager.cancelSelect() system.msg('取消单选(黄选)') } } @@ -581,6 +629,9 @@ export function load3DModule(arrayBuffer: ArrayBuffer | string, ext: string) { } } +/** + * 根据 ItemJson 数据结构, 构建适合 THREE.js 的变换矩阵 + */ export function getMatrixFromTf(tf: number[][]): THREE.Matrix4 { const [pos, rot, scale] = tf const matrix = new THREE.Matrix4() diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 5088df4..4d2516d 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -1,6 +1,6 @@ import type Viewport from '@/core/engine/Viewport' import * as THREE from 'three' -import { getLineId, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts' +import { getLineId, getMatrixFromTf, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts' import { Line2 } from 'three/examples/jsm/lines/Line2' import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts' import Constract from '@/core/Constract.ts' @@ -129,16 +129,18 @@ export default abstract class BaseRenderer { createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike { // 由基础类创造一个属于自己的点演示 const point = this.createPointBasic(item, option) + point.setMatrix4(getMatrixFromTf(item.tf)) + + // point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + // + // point.rotation.set( + // THREE.MathUtils.degToRad(item.tf[1][0]), + // THREE.MathUtils.degToRad(item.tf[1][1]), + // THREE.MathUtils.degToRad(item.tf[1][2]) + // ) + // + // point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) - point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - - point.rotation.set( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]) - ) - - point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) point.visible = ((typeof item.v !== 'undefined') ? item.v : true) return point } @@ -198,18 +200,19 @@ export default abstract class BaseRenderer { const point = object point.name = item.name || point.name - point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - - point.rotation.set( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]) - ) - - point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) - if (point instanceof PointManageWrap) { - point.manager.syncMeshObject3D(point) - } + // point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + // + // point.rotation.set( + // THREE.MathUtils.degToRad(item.tf[1][0]), + // THREE.MathUtils.degToRad(item.tf[1][1]), + // THREE.MathUtils.degToRad(item.tf[1][2]) + // ) + // + // point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) + // if (point instanceof PointManageWrap) { + // point.manager.syncMeshObject3D(point) + // } + point.matrix = getMatrixFromTf(item.tf) return point } diff --git a/src/core/controls/DragControl.ts b/src/core/controls/DragControl.ts deleted file mode 100644 index 6c6a770..0000000 --- a/src/core/controls/DragControl.ts +++ /dev/null @@ -1,319 +0,0 @@ -import * as THREE from 'three' -import type Viewport from '@/core/engine/Viewport.ts' -import { getClosestObject } from '@/core/ModelUtils.ts' -import EventBus from '@/runtime/EventBus.ts' -import type { Object3DLike } from '@/types/ModelTypes.ts' -import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts' -import { LineManageWrap } from '@/core/manager/LineSegmentManager.ts' - -/** - * ThreeJS 拖拽管理器(仅限 X/Z 平面) - */ -export default class DragControl { - private viewport: Viewport - private _is_enabled: boolean = true - private domElement: HTMLElement - private isPointerDown: boolean = false - private dragStartMouse: THREE.Vector2 = new THREE.Vector2() - private checkStateInterval: number | null = null - private dragShadowsGroup: THREE.Group | null = null - private dragDelayTimeout: number | null = null - - - private static readonly SHADOW_MATERIAL = new THREE.MeshBasicMaterial({ - color: 0x222222, - transparent: true, - opacity: 0.5, - depthWrite: false, - side: THREE.DoubleSide - }) - - public isDragging: boolean = false - - init(viewport: Viewport): void { - this.viewport = viewport - const domElement = this.viewport.renderer.domElement - this.domElement = domElement - - domElement.addEventListener('pointerdown', this.onPointerDown) - domElement.addEventListener('pointermove', this.onPointerMove) - domElement.addEventListener('pointerup', this.onPointerUp) - domElement.addEventListener('pointerleave', this.onPointerLeave) - - domElement.style.cursor = 'auto' - } - - - /** - * 卸载资源 - */ - dispose(): void { - if (this.domElement) { - this.domElement.removeEventListener('pointermove', this.onPointerMove) - this.domElement.removeEventListener('pointerdown', this.onPointerDown) - this.domElement.removeEventListener('pointerup', this.onPointerUp) - this.domElement.removeEventListener('pointerleave', this.onPointerLeave) - } - - this.cleanupDrag() - this.viewport = null - } - - /** - * pointerdown 事件处理 - */ - private onPointerDown = (event: PointerEvent): void => { - if (!this.enabled) return - - const mouse = this.getMousePosition(event.clientX, event.clientY) - const intersected = this.getIntersectedDraggableObject(mouse) - - if (!intersected) return - - this.viewport.controls.enabled = false - - // 设置定时器:0.1秒后才抓取拖拽 - this.dragDelayTimeout = window.setTimeout(() => { - this.isPointerDown = true - this.dragStartMouse.set(intersected.position.x, intersected.position.z) - - let selectedObjects = [intersected] - const multiSelected = this.viewport.state.multiSelectedObjects - if (multiSelected.length > 0 && multiSelected.includes(intersected)) { - selectedObjects = multiSelected - } - - this.createShadows(selectedObjects) - this.domElement.style.cursor = 'grabbing' - this.checkStateInterval = setInterval(() => { - if (isNaN(CurrentMouseInfo.x) || isNaN(CurrentMouseInfo.z) || !this.isPointerDown) { - this.cancelDrag() - } - }, 100) // 每100毫秒检查一次状态, 鼠标移出要主动清理 - - }, 100) // 0.1秒延迟抓取 - - } - - /** - * pointermove 事件处理 - */ - private onPointerMove = (event: PointerEvent): void => { - if (!this.enabled || !this.domElement) return - - if (!isNaN(this.dragStartMouse.x) && !isNaN(this.dragStartMouse.y) && this.dragShadowsGroup) { - this.isDragging = true - this.domElement.style.cursor = 'grabbing' - this.updateShadows(new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z)) - - } else { - // 射线方法修改 ========================== - const mouse = this.getMousePosition(event.clientX, event.clientY) - const intersected = this.getIntersectedDraggableObject(mouse) - // ===================================== - // const ids = this.viewport.itemFindManager.getItemsByPosition(CurrentMouseInfo.x, CurrentMouseInfo.z) - this.domElement.style.cursor = intersected ? 'grab' : 'auto' - } - } - - /** - * pointerup 事件处理 - */ - private onPointerUp = (event: PointerEvent): void => { - if (this.isDragging) { - const startPos = this.dragStartMouse.clone() - const targetPos = new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z) - - if (startPos && targetPos && !_.isNaN(startPos.x) && !_.isNaN(startPos.y)) { - this.dragComplete(startPos, targetPos) - } - } - - this.cleanupDrag() - } - - /** - * 清理拖拽状态 - */ - private cleanupDrag(): void { - if (this.domElement) { - this.domElement.style.cursor = 'auto' - } - - this.isDragging = false - this.isPointerDown = false - this.dragStartMouse.set(NaN, NaN) - - this.removeShadows() - - if (this.viewport) { - this.viewport.controls.enabled = true - } - - if (this.dragDelayTimeout !== null) { - clearTimeout(this.dragDelayTimeout) - this.dragDelayTimeout = null - } - - if (this.checkStateInterval) { - clearInterval(this.checkStateInterval) - this.checkStateInterval = null - } - } - - /** - * 获取当前鼠标坐标(归一化设备坐标) - */ - private getMousePosition(clientX: number, clientY: number): THREE.Vector2 { - const rect = this.domElement.getBoundingClientRect() - return new THREE.Vector2( - ((clientX - rect.left) / rect.width) * 2 - 1, - ((clientY - rect.top) / rect.height) * -2 + 1 - ) - } - - /** - * 射线检测,返回第一个可拖拽对象 - */ - private getIntersectedDraggableObject(mouse: THREE.Vector2): Object3DLike | undefined { - const raycaster = new THREE.Raycaster() - raycaster.setFromCamera(mouse, this.viewport.camera) - - const draggableObjects = this.viewport.entityManager._draggableObjects || [] - const intersects = raycaster.intersectObjects(draggableObjects, true) - - if (intersects.length > 0) { - return getClosestObject(this.viewport, intersects[0].object, intersects[0].instanceId) - } - } - - - /** - * 创建拖拽阴影 - */ - private createShadows(objects: Object3DLike[]): void { - this.removeShadows() - - this.dragShadowsGroup = new THREE.Group() - for (const obj of objects) { - if (_.isNil(_.get(obj, 'userData.entityId'))) { - console.error(`Object ${obj.name} missing entityId`) - continue - } - - // 克隆对象的几何体和材质 - // const shadowBox = obj.clone() - let box: THREE.Box3 - if (obj instanceof THREE.Object3D) { - box = new THREE.Box3().setFromObject(obj) - } else if (obj instanceof PointManageWrap) { - box = obj.createBox3() - } - const size = new THREE.Vector3() - box.getSize(size) - const geometry = new THREE.PlaneGeometry(size.x, size.z) - const shadowBox = new THREE.Mesh(geometry, DragControl.SHADOW_MATERIAL) - shadowBox.position.copy(obj.position) - shadowBox.rotation.x = -Math.PI / 2 - - shadowBox.userData = { - isShadow: true, - entityId: obj.userData.entityId, - originPosition: obj.position.clone() // 保存原始位置 - } - this.dragShadowsGroup.add(shadowBox) - } - this.viewport.scene.add(this.dragShadowsGroup) - } - - /** - * 移除阴影 - */ - private removeShadows(): void { - if (this.dragShadowsGroup) { - console.log('removeShadows') - this.viewport.scene.remove(this.dragShadowsGroup) - this.dragShadowsGroup = null - } - } - - /** - * 更新阴影位置(仅 X/Z 平面) - */ - private updateShadows(newPosition: THREE.Vector2): void { - if (!this.dragShadowsGroup) return - - // 计算新位置与拖拽开始位置的偏移量 - const offsetX = newPosition.x - this.dragStartMouse.x - const offsetZ = newPosition.y - this.dragStartMouse.y - - for (let i = 0; i < this.dragShadowsGroup.children.length; i++) { - const shadow = this.dragShadowsGroup.children[i] as THREE.Mesh - if (!shadow.userData.originPosition) { - console.error(`Shadow ${shadow.name} does not have originPosition`) - continue - } - - const newPosX = shadow.userData.originPosition.x + offsetX - const newPosZ = shadow.userData.originPosition.z + offsetZ - shadow.position.set(newPosX, shadow.userData.originPosition.y, newPosZ) // 锁定 Y 轴高度 - // console.log(`Updating shadow ${shadow.name} position with offset:`, offsetX, offsetZ) - } - } - - private dragComplete = (startPos: THREE.Vector2, targetPos: THREE.Vector2): void => { - // console.log(`Drag completed from ${startPos.toArray()} to ${targetPos.toArray()}`) - - this.viewport.stateManager.update(({ getEntity, putEntity, deleteEntity, addEntity }) => { - for (const object of this.dragShadowsGroup.children) { - const entityId = object.userData.entityId - if (entityId) { - const entity = getEntity(entityId) - // 更新实体位置 - entity.tf[0][0] = object.position.x - entity.tf[0][2] = object.position.z - putEntity(entity) - - } else { - system.showErrorDialog('not found entity') - } - } - }) - - // EventBus.dispatch('multiSelectedObjectsChanged', { - // multiSelectedObjects: this.viewport.state.multiSelectedObjects - // }) - // EventBus.dispatch('selectedObjectPropertyChanged', {}) - - this.cleanupDrag() - } - - /** - * pointerleave 事件处理 - */ - private onPointerLeave = (_event: PointerEvent): void => { - this.cancelDrag() - } - - /** - * 取消当前拖拽状态 - */ - cancelDrag(): void { - this.cleanupDrag() - } - - - /** - * 设置启用/禁用 - */ - public set enabled(value: boolean) { - this._is_enabled = value - if (!value) { - this.cleanupDrag() - } - } - - public get enabled(): boolean { - return this._is_enabled - } -} diff --git a/src/core/controls/MouseMoveInspect.ts b/src/core/controls/MouseMoveInspect.ts deleted file mode 100644 index 7d23544..0000000 --- a/src/core/controls/MouseMoveInspect.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type Viewport from '@/core/engine/Viewport' -import * as THREE from 'three' - -let pmFn, otFn, lvFn - -/** - * 鼠标移动时,将鼠标位置的坐标转换为设计图上的坐标,并设置到 designer.mousePos 属性中 - */ -export default class MouseMoveInspect { - viewport: Viewport - canvas: HTMLCanvasElement - - constructor() { - } - - init(viewport: Viewport) { - this.viewport = viewport - this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement - - pmFn = this.mouseMove.bind(this) - otFn = this.mouseLv.bind(this) - lvFn = this.mouseLv.bind(this) - this.canvas.addEventListener('pointermove', pmFn) - this.canvas.addEventListener('pointerout', otFn) - this.canvas.addEventListener('mouseleave', lvFn) - } - - dispose() { - this.canvas.removeEventListener('pointermove', pmFn) - pmFn = undefined - this.canvas.removeEventListener('pointerout', otFn) - otFn = undefined - this.canvas.removeEventListener('mouseleave', lvFn) - lvFn = undefined - } - - mouseLv(event: MouseEvent) { - this.viewport.state.mouse.x = NaN - this.viewport.state.mouse.z = NaN - window['CurrentMouseInfo'] = { - x: NaN, - z: NaN, - isShiftKey: false, - isCtrlKey: false, - isAltKey: false, - isMetaKey: false - } - } - - mouseMove = _.throttle(function(this: MouseMoveInspect, event: MouseEvent) { - - const pointv = new THREE.Vector2() - pointv.x = event.offsetX / this.viewport.renderer.domElement.offsetWidth - pointv.y = event.offsetY / this.viewport.renderer.domElement.offsetHeight - - const mouse = new THREE.Vector2() - mouse.set((pointv.x * 2) - 1, -(pointv.y * 2) + 1) - - // 当前鼠标所在的点 - const point = this.viewport.getClosestIntersection(event) - if (!point) { - return - } - - this.viewport.state.mouse.x = point.x - this.viewport.state.mouse.z = point.z - - window['CurrentMouseInfo'] = { - viewport: this.viewport, - x: point.x, - z: point.z, - isShiftKey: event.shiftKey, - isCtrlKey: event.ctrlKey, - isAltKey: event.altKey, - isMetaKey: event.metaKey, - mouse: mouse - } - - }, 1) - - animate(): void { - } -} diff --git a/src/core/controls/SelectInspect.ts b/src/core/controls/SelectInspect.ts deleted file mode 100644 index e71bb78..0000000 --- a/src/core/controls/SelectInspect.ts +++ /dev/null @@ -1,441 +0,0 @@ -import * as THREE from 'three' -import type Viewport from '@/core/engine/Viewport' -import { Line2 } from 'three/examples/jsm/lines/Line2.js' -import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' -import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js' -import EventBus from '@/runtime/EventBus' -import { markRaw } from 'vue' -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' - -/** - * 选择工具,用于在设计器中显示选中对象的包围盒 - */ -export default class SelectInspect { - viewport: Viewport - /** - * 线框材质,用于显示选中对象的包围盒 - */ - yellowMaterial: LineMaterial = new LineMaterial({ color: 0xffff00, linewidth: 3 }) - - /** - * 线框材质,用于显示选中对象的包围盒 - */ - redMaterial: LineMaterial = new LineMaterial({ color: 0xff0000, linewidth: 3 }) - - /** - * 矩形材质,用于显示鼠标拖拽选择的矩形区域 - */ - rectMaterial: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({ - color: 0x000000, - opacity: 0.3, - transparent: true - }) - - /** - * 当前选中对象的矩形选择框 - */ - rectangle: THREE.Mesh | null = null - - /** - * 当前选中对象的包围盒线框 - */ - selectionBox: Line2 - - /** - * 当前鼠标所在的画布, 对应 viewport.renderer.domElement - */ - canvas: HTMLCanvasElement - - /** - * 鼠标按下时记录的起始位置,用于绘制矩形选择框 - */ - recStartPos: THREE.Vector3 | null - - /** - * 当前黄选的实体 ID - */ - selectionId: string - - clickTime: number | null = null - - constructor() { - } - - init(viewport: Viewport) { - this.viewport = viewport - this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement - - // 监听 shift 按住之后的矩形 - this.canvas.addEventListener('pointerdown', this.onMouseDown) - this.canvas.addEventListener('pointermove', this.onMouseMove) - this.canvas.addEventListener('pointerup', this.onMouseUp) - - EventBus.on('selectedObjectChanged', this.updateSelectionBox) - EventBus.on('multiSelectedObjectsChanged', this.updateMultiSelectionBoxes) - EventBus.on('selectedObjectPropertyChanged', this.updateSelectionBox) - // EventBus.on('multiselectedObjectChanged', this.updateMultiSelectionBoxes) - } - - redSelectionGroup = new THREE.Group() - - updateMultiSelectionBoxes = () => { - const multiSelectedObjects = this.viewport.state.multiSelectedObjects - // 为所有多选对象创建包围盒线框 - this.clearRedSelectionBoxes() - - if (!multiSelectedObjects || multiSelectedObjects.length === 0) { - return - } - - for (const object of multiSelectedObjects) { - if (object.userData.entityId) { - this.createRedSelectionBox(object) - } - } - } - - // 取消红选 - cancelMultiSelect() { - this.viewport.state.multiSelectedObjects = [] - this.viewport.state.multiSelectedItems = [] - this.viewport.state.multiSelectedEntityIds = [] - EventBus.dispatch('multiSelectedObjectsChanged', { - viewport: markRaw(this.viewport), - multiSelectedObjects: [], - multiSelectedItems: [], - multiSelectedEntityIds: [] - }) - } - - // 取消黄选 - cancelSelect() { - this.viewport.state.selectedObject = null - this.viewport.state.selectedItem = null - this.viewport.state.selectedEntityId = null - this.viewport.state.selectedObjectSetter = null - EventBus.dispatch('selectedObjectChanged', { - viewport: markRaw(this.viewport), - selectedObject: null, - selectedItem: null, - selectedEntityId: null, - selectedObjectSetter: null - }) - } - - // 根据 entityId 选择对象 - selectById(entityId: string | null) { - if (!entityId) { - this.viewport.state.selectedObject = null - this.viewport.state.selectedItem = null - this.viewport.state.selectedEntityId = null - this.viewport.state.selectedObjectSetter = null - EventBus.dispatch('selectedObjectChanged', { - viewport: markRaw(this.viewport), - selectedObject: null, - selectedItem: null, - selectedEntityId: null, - selectedObjectSetter: null - }) - return - } - - const item = this.viewport.entityManager.findItemById(entityId) - const object = this.viewport.entityManager.findObjectById(entityId) - const itemTypeName = item.t - if (item.dt.protected !== true) { - this.viewport.state.selectedObject = markRaw(object) - this.viewport.state.selectedItem = markRaw(item) - this.viewport.state.selectedEntityId = entityId - this.viewport.state.selectedObjectSetter = getSetter(itemTypeName) - EventBus.dispatch('selectedObjectChanged', { - viewport: markRaw(this.viewport), - selectedObject: this.viewport.state.selectedObject, - selectedItem: this.viewport.state.selectedItem, - selectedEntityId: this.viewport.state.selectedEntityId, - selectedObjectSetter: this.viewport.state.selectedObjectSetter - }) - } - } - - // 多选对象 - multiSelectByIds(entityIds: string[]) { - // 遍历找到的对象,添加到多选对象中 - const multiSelectedObjects = [] - const multiSelectedItems = [] - const multiSelectedEntityIds = [] - for (const entityId of entityIds) { - const object = this.viewport.entityManager.findObjectById(entityId) - if (object.userData.entityId && object.userData.t) { - const item = this.viewport.entityManager.findItemById(object.userData.entityId) - if (item && item.dt.protected !== true) { - multiSelectedObjects.push(object) - multiSelectedItems.push(item) - multiSelectedEntityIds.push(object.userData.entityId) - } - } - } - - this.viewport.state.multiSelectedObjects = markRaw(multiSelectedObjects) - this.viewport.state.multiSelectedItems = markRaw(multiSelectedItems) - this.viewport.state.multiSelectedEntityIds = multiSelectedEntityIds - EventBus.dispatch('multiSelectedObjectsChanged', { - viewport: markRaw(this.viewport), - multiSelectedObjects: this.viewport.state.multiSelectedObjects, - multiSelectedItems: this.viewport.state.multiSelectedItems, - multiSelectedEntityIds: this.viewport.state.multiSelectedEntityIds - }) - } - - // 清除之前的红色包围盒线框 - private clearRedSelectionBoxes() { - if (this.redSelectionGroup.children.length > 0) { - for (const child of this.redSelectionGroup.children) { - this.redSelectionGroup.remove(child) - } - } - this.viewport.scene.remove(this.redSelectionGroup) - this.redSelectionGroup = new THREE.Group() - this.viewport.scene.add(this.redSelectionGroup) - } - - // 创建红选包围盒 - private createRedSelectionBox(object: Object3DLike) { - // 如果对象没有 entityId,则不创建包围盒线框 - if (!object.userData.entityId) { - return - } - let box: THREE.Box3 - if (object instanceof PointManageWrap) { - box = object.createBox3() - - } else if (object instanceof THREE.Object3D) { - box = new THREE.Box3().setFromObject(object) - } - - const min = box.min - const max = box.max - - const corners = [ - new THREE.Vector3(min.x - Constract.RED_EXPAND_AMOUNT, max.y + Constract.RED_EXPAND_AMOUNT, min.z - Constract.RED_EXPAND_AMOUNT), - new THREE.Vector3(max.x + Constract.RED_EXPAND_AMOUNT, max.y + Constract.RED_EXPAND_AMOUNT, min.z - Constract.RED_EXPAND_AMOUNT), - new THREE.Vector3(max.x + Constract.RED_EXPAND_AMOUNT, max.y + Constract.RED_EXPAND_AMOUNT, max.z + Constract.RED_EXPAND_AMOUNT), - new THREE.Vector3(min.x - Constract.RED_EXPAND_AMOUNT, max.y + Constract.RED_EXPAND_AMOUNT, max.z + Constract.RED_EXPAND_AMOUNT) - ] - - // 构建矩形边框(4 条边) - const positions = [] - for (let i = 0; i < 4; i++) { - const p1 = corners[i] - const p2 = corners[(i + 1) % 4] - positions.push(p1.x, p1.y, p1.z) - positions.push(p2.x, p2.y, p2.z) - } - - // 创建几何体 - const lineGeom = new LineGeometry() - const vertices = new Float32Array(positions) - lineGeom.setPositions(vertices) - - const selectionBox = new Line2(lineGeom, this.redMaterial) - selectionBox.computeLineDistances() - - this.redSelectionGroup.add(selectionBox) - } - - // 更新选中对象的包围盒线框 - private updateSelectionBox = () => { - this.clearSelectionBox() - - const item = this.viewport.state.selectedItem - if (!item) { - 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 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) - } - - dispose() { - this.canvas.removeEventListener('pointerdown', this.onMouseDown) - this.canvas.removeEventListener('pointermove', this.onMouseMove) - this.canvas.removeEventListener('pointerup', this.onMouseUp) - - // 销毁选择工具 - this.clearSelectionBox() - this.disposeRect() - this.clearRedSelectionBoxes() - } - - // 清除当前选中对象的包围盒线框 - private clearSelectionBox = () => { - if (this.selectionBox) { - this.viewport.scene.remove(this.selectionBox) - this.selectionBox.geometry.dispose() - this.selectionBox = null - } - } - - private createRectangle = () => { - if (this.rectangle !== null) { - this.disposeRect() - } - if (this.recStartPos) { - // 创建矩形 - this.rectangle = new THREE.Mesh( - new THREE.PlaneGeometry(0.001, 0.001), - this.rectMaterial - ) - this.rectangle.name = 'selectRectangle' - this.rectangle.rotation.x = -Math.PI / 2 // 关键!让平面正对相机 - this.rectangle.position.set( - this.recStartPos.x, - this.recStartPos.y, - this.recStartPos.z - ) - this.viewport.scene.add(this.rectangle) - } - } - - private updateRectangle = (position: THREE.Vector3) => { - if (!this.rectangle || !this.recStartPos) return - // console.log('updateRectangle', this.recStartPos, position) - - const width = position.x - this.recStartPos.x - const height = position.z - this.recStartPos.z - - const newWidth = Math.abs(width) - const newHeight = Math.abs(height) - - // 清理旧几何体 - this.rectangle.geometry.dispose() - this.rectangle.geometry = new THREE.PlaneGeometry(newWidth, newHeight) - this.rectangle.position.set( - this.recStartPos.x + width / 2, - this.recStartPos.y, - this.recStartPos.z + height / 2 - ) - } - - private onMouseDown = (event: MouseEvent) => { - if (event.shiftKey) { - // 记录鼠标按下位置 - this.recStartPos = this.viewport.getClosestIntersection(event) - this.createRectangle() - this.viewport.controls.enabled = false // 禁用控制器 - - } else { - // 为 click 事件添加处理逻辑 - this.clickTime = Date.now() - } - } - - - private onMouseMove = (event: MouseEvent) => { - if (!this.recStartPos) { - this.disposeRect() - } - // 更新矩形大小或重新生成矩形 - const position = this.viewport.getClosestIntersection(event) - if (!position) return - this.updateRectangle(position) - } - - private onMouseUp = (event: MouseEvent) => { - this.disposeRect() - const clickTime = this.clickTime - this.clickTime = null - if (Date.now() - clickTime < 200) { - // 如果是点击事件,触发选中逻辑 - const objects: Object3DLike[] = this.viewport.entityManager.getObjectByCanvasMouse(event) - if (objects.length > 0 && objects[0]?.userData?.entityId && objects[0]?.userData?.createType !== 'line') { - console.log('mouseClick', objects) - const object = objects[0] - this.selectById(object.userData.entityId) - - } else { - // 如果没有选中任何对象,清除选中状态 - this.selectById(null) - } - } - } - - private disposeRect = () => { - if (this.rectangle !== null) { - // 查找在这个矩形内的所有有效业务对象,并将他们添加进 viewport.state.multiSelectedObjects - this.calcRectangleObjectToState() - this.viewport.scene.remove(this.rectangle) - this.rectangle.geometry.dispose() - this.rectangle = null - } - this.recStartPos = null - this.viewport.controls.enabled = true // 启用控制器 - } - - private calcRectangleObjectToState = () => { - if (!this.rectangle || !this.recStartPos) return - - // 获取矩形的包围盒 - const box = new THREE.Box3().setFromObject(this.rectangle) - - // 获取盒子的 startX, startZ, endX, endZ - const startX = box.min.x - const startZ = box.min.z - const endX = box.max.x - const endZ = box.max.z - - // 查找所有在矩形内的对象 - const ids = this.viewport.itemFindManager.getItemsByRect(startX, startZ, endX, endZ) - - // 清空之前的多选对象 - this.viewport.state.multiSelectedObjects = [] - - // 遍历找到的对象,添加到多选对象中 - const multiSelectedObjects = [] - const multiSelectedItems = [] - const multiSelectedEntityIds = [] - for (const id of ids) { - const object = this.viewport.entityManager.findObjectById(id) - if (object.userData.entityId && object.userData.t) { - const item = this.viewport.entityManager.findItemById(object.userData.entityId) - if (item && item.dt.protected !== true) { - multiSelectedObjects.push(object) - multiSelectedItems.push(item) - multiSelectedEntityIds.push(id) - } - } - } - - // 触发多选对象更新事件 - this.viewport.state.multiSelectedObjects = markRaw(multiSelectedObjects) - this.viewport.state.multiSelectedItems = markRaw(multiSelectedItems) - this.viewport.state.multiSelectedEntityIds = multiSelectedEntityIds - EventBus.dispatch('multiSelectedObjectsChanged', { - viewport: markRaw(this.viewport), - multiSelectedObjects: this.viewport.state.multiSelectedObjects, - multiSelectedItems: this.viewport.state.multiSelectedItems, - multiSelectedEntityIds: this.viewport.state.multiSelectedEntityIds - }) - } - -} diff --git a/src/core/engine/SceneHelp.ts b/src/core/engine/SceneHelp.ts index a7350cd..99ce47c 100644 --- a/src/core/engine/SceneHelp.ts +++ b/src/core/engine/SceneHelp.ts @@ -36,7 +36,7 @@ export default class SceneHelp { if (gridOption.axesEnabled) { this.axesHelper = createAxes(gridOption.axesSize, gridOption.axesColor, gridOption.axesWidth) - this.axesHelper.position.setY(0.03) // 确保网格在地面上方 + this.axesHelper.position.setY(0) // 确保网格在地面上方 this.scene.add(this.axesHelper) } @@ -44,7 +44,7 @@ export default class SceneHelp { gridHelper.material.color.setHex(gridOption.gridColor) gridHelper.material.opacity = gridOption.gridOpacity gridHelper.material.transparent = true - gridHelper.position.setY(0.02) // 确保网格在地面上方 + gridHelper.position.setY(0) gridHelper.material.vertexColors = false if (!gridOption.gridEnabled) { gridHelper.visible = false @@ -57,7 +57,7 @@ export default class SceneHelp { gridHelper2.material.color.setHex(gridOption.gridColor) gridHelper2.material.transparent = false gridHelper2.material.vertexColors = false - gridHelper2.position.setY(0.01) // 确保网格在地面上方 + gridHelper2.position.setY(0) if (!gridOption.gridEnabled) { gridHelper2.visible = false } @@ -117,10 +117,6 @@ export default class SceneHelp { } else if (obj instanceof LineManageWrap) { // 默认会合并到 LineSegmentManager 中 - } else if (obj instanceof PointManageWrap) { - if (!obj.parent) { - obj.manager.syncMeshObject3D(obj) - } } } } diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index 1695620..e523ebe 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -9,15 +9,15 @@ import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import SceneHelp from './SceneHelp' -import SelectInspect from '../controls/SelectInspect' -import MouseMoveInspect from '../controls/MouseMoveInspect' +import SelectManager from '../manager/SelectManager' +import MouseMoveManager from '../manager/MouseMoveManager' import EntityManager from '../manager/EntityManager' import InteractionManager from '@/core/manager/InteractionManager' import { calcPositionUseSnap } from '@/core/ModelUtils' import StateManager from '@/core/manager/StateManager.ts' import EventBus from '@/runtime/EventBus.ts' import Constract from '@/core/Constract.ts' -import DragControl from '@/core/controls/DragControl.ts' +import DragManager from '@/core/manager/DragManager.ts' import type { PropertySetter } from '@/core/base/PropertyTypes.ts' import LabelManager from '@/core/manager/LabelManager.ts' import type InstancePointManager from '@/core/manager/InstancePointManager.ts' @@ -41,9 +41,9 @@ export default class Viewport { animationFrameId: any = null scene: SceneHelp - selectInspect = new SelectInspect() - mouseMoveInspect = new MouseMoveInspect() - dragControl = new DragControl() + selectManager = new SelectManager() + mouseMoveManager = new MouseMoveManager() + dragManager = new DragManager() labelManager = new LabelManager() entityManager = new EntityManager() itemFindManager = new ItemFindManager() @@ -53,9 +53,9 @@ export default class Viewport { stateManager: StateManager tools: IControls[] = [ - markRaw(this.selectInspect), - markRaw(this.mouseMoveInspect), - markRaw(this.dragControl), + markRaw(this.selectManager), + markRaw(this.mouseMoveManager), + markRaw(this.dragManager), markRaw(this.labelManager), markRaw(this.entityManager), markRaw(this.itemFindManager), @@ -217,10 +217,6 @@ export default class Viewport { this.renderer = renderer - - // 注册拖拽组件 - // this.dragControl = new EsDragControls(this) - // 性能监控 const statsControls = new Stats() this.statsControls = statsControls @@ -589,35 +585,15 @@ export default class Viewport { this.renderer.domElement = null } - if (this.interactionManager) { - this.interactionManager.dispose() - this.interactionManager = null - } - if (this.stateManager) { this.stateManager.dispose() this.stateManager = null } - if (this.entityManager) { - this.entityManager.dispose() - this.entityManager = null - } - if (this.controls) { this.controls.dispose() this.controls = null } - - if (this.dragControl) { - this.dragControl.dispose() - this.dragControl = null - } - - if (this.itemFindManager) { - this.itemFindManager.dispose() - this.itemFindManager = null - } } getIntersects(point: THREE.Vector2) { diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 8c5da48..b35bd84 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -449,11 +449,11 @@ export default class EntityManager { if (this.viewport.state.selectedEntityId === id) { // 如果当前选中的对象被替换了,更新选中状态 - this.viewport.selectInspect.selectById(id) + this.viewport.selectManager.selectById(id) } if (_.includes(this.viewport.state.multiSelectedEntityIds, id)) { // 如果当前多选的对象被替换了,更新多选状态 - this.viewport.selectInspect.multiSelectByIds(this.viewport.state.multiSelectedEntityIds) + this.viewport.selectManager.multiSelectByIds(this.viewport.state.multiSelectedEntityIds) } } diff --git a/src/core/manager/InstanceMeshManager.ts b/src/core/manager/InstanceMeshManager.ts index f59a898..8dc67e9 100644 --- a/src/core/manager/InstanceMeshManager.ts +++ b/src/core/manager/InstanceMeshManager.ts @@ -174,6 +174,24 @@ export class InstanceMeshWrap { return this } + set matrix(matrix: THREE.Matrix4) { + this.setMatrix4(matrix) + } + + get matrix(): THREE.Matrix4 { + const block = this.manager.blocks[this.blockIndex] + const matrix = new THREE.Matrix4() + block.instancedMesh.getMatrixAt(this.meshIndex, matrix) + return matrix + } + + applyMatrix4(targetMatrix: THREE.Matrix4): InstanceMeshWrap { + const matrix = this.matrix + const newMatrix = matrix.multiplyMatrices(targetMatrix, matrix) + this.setMatrix4(newMatrix) + return this + } + dispose() { this.manager.delete(this) this.parent = null diff --git a/src/core/manager/InstancePointManager.ts b/src/core/manager/InstancePointManager.ts index 3c0cf8a..40a0615 100644 --- a/src/core/manager/InstancePointManager.ts +++ b/src/core/manager/InstancePointManager.ts @@ -2,6 +2,8 @@ import * as THREE from 'three' import type Viewport from '@/core/engine/Viewport.ts' import { Vector3 } from 'three/src/math/Vector3' import InstanceMeshBlock from '@/core/manager/InstanceMeshBlock.ts' +import { getMatrixFromTf } from '@/core/ModelUtils.ts' +import { InstanceMeshWrap } from '@/core/manager/InstanceMeshManager.ts' /** * 点实例管理器 @@ -92,25 +94,27 @@ export default class InstancePointManager { wrap.userData.t = item.t wrap.userData.entityId = item.id - let [position, rotation, scale] = item.tf - if (option.position) { - position = option.position.toArray() - } - if (option.rotation) { - rotation = option.rotation.toArray() - } - if (option.scale) { - scale = option.scale.toArray() - } + wrap.setMatrix4(getMatrixFromTf(item.tf)) - wrap.position.set(position[0], position[1], position[2]) - wrap.rotation.set( - THREE.MathUtils.degToRad(rotation[0]), - THREE.MathUtils.degToRad(rotation[1]), - THREE.MathUtils.degToRad(rotation[2]) - ) - wrap.scale.set(scale[0], scale[1], scale[2]) - this.syncMeshObject3D(wrap) + // let [position, rotation, scale] = item.tf + // if (option.position) { + // position = option.position.toArray() + // } + // if (option.rotation) { + // rotation = option.rotation.toArray() + // } + // if (option.scale) { + // scale = option.scale.toArray() + // } + // + // wrap.position.set(position[0], position[1], position[2]) + // wrap.rotation.set( + // THREE.MathUtils.degToRad(rotation[0]), + // THREE.MathUtils.degToRad(rotation[1]), + // THREE.MathUtils.degToRad(rotation[2]) + // ) + // wrap.scale.set(scale[0], scale[1], scale[2]) + // this.syncMeshObject3D(wrap) } /** @@ -133,30 +137,46 @@ export default class InstancePointManager { return this.__uuidMap.get(uuid) } - syncMeshObject3D(wrap: PointManageWrap) { - if (wrap.meshIndex < 0) { - console.error('InstancePointManager: Invalid meshIndex for wrap', wrap) + setBlockMatrixAt(wrap: PointManageWrap, matrix: THREE.Matrix4) { + const block = this.blocks[wrap.blockIndex] + if (!block) { + console.warn(`InstanceMeshManager: Block ${wrap.blockIndex} not found!`) return } - if (!wrap.visible) { - this.dummy.scale.set(0, 0, 0) - } else { - this.dummy.position.copy(wrap.position) - this.dummy.rotation.copy(wrap.rotation) - this.dummy.scale.copy(wrap.scale) - } - this.dummy.updateMatrix() - const block = this.blocks[wrap.blockIndex] - if (!wrap.parent) { + if (!block.__indexIdMap.has(wrap.meshIndex)) { wrap.parent = block.instancedMesh this.__uuidMap.set(wrap.uuid, wrap) block.__indexIdMap.set(wrap.meshIndex, wrap.uuid) } - block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) + block.instancedMesh.setMatrixAt(wrap.meshIndex, matrix) block.instancedMesh.instanceMatrix.needsUpdate = true } + // syncMeshObject3D(wrap: PointManageWrap) { + // if (wrap.meshIndex < 0) { + // console.error('InstancePointManager: Invalid meshIndex for wrap', wrap) + // return + // } + // if (!wrap.visible) { + // this.dummy.scale.set(0, 0, 0) + // } else { + // this.dummy.position.copy(wrap.position) + // this.dummy.rotation.copy(wrap.rotation) + // this.dummy.scale.copy(wrap.scale) + // } + // this.dummy.updateMatrix() + // + // const block = this.blocks[wrap.blockIndex] + // if (!wrap.parent) { + // wrap.parent = block.instancedMesh + // this.__uuidMap.set(wrap.uuid, wrap) + // block.__indexIdMap.set(wrap.meshIndex, wrap.uuid) + // } + // block.instancedMesh.setMatrixAt(wrap.meshIndex, this.dummy.matrix) + // block.instancedMesh.instanceMatrix.needsUpdate = true + // } + /** * 删除点实例 * @param id 点ID @@ -262,13 +282,12 @@ export class PointManageWrap { uuid: string name: string visible: boolean - readonly position = new THREE.Vector3() - readonly rotation = new THREE.Euler() - readonly scale = new THREE.Vector3(1, 1, 1) + //@ts-ignore userData: UserData = {} updateWorldMatrix() { + } get type() { @@ -291,16 +310,26 @@ export class PointManageWrap { this.parent = null } - applyMatrix4(matrix: THREE.Matrix4): PointManageWrap { - const position = new THREE.Vector3() - const quaternion = new THREE.Quaternion() - const scale = new THREE.Vector3() - matrix.decompose(position, quaternion, scale) + setMatrix4(matrix: THREE.Matrix4): PointManageWrap { + this.manager.setBlockMatrixAt(this, matrix) + return this + } + + set matrix(matrix: THREE.Matrix4) { + this.setMatrix4(matrix) + } + + get matrix(): THREE.Matrix4 { + const block = this.manager.blocks[this.blockIndex] + const matrix = new THREE.Matrix4() + block.instancedMesh.getMatrixAt(this.meshIndex, matrix) + return matrix + } - this.position.copy(position) - this.rotation.setFromQuaternion(quaternion) - this.scale.copy(scale) - this.manager.syncMeshObject3D(this) + applyMatrix4(targetMatrix: THREE.Matrix4): PointManageWrap { + const matrix = this.matrix + const newMatrix = matrix.multiplyMatrices(targetMatrix, matrix) + this.setMatrix4(newMatrix) return this } diff --git a/src/core/manager/StateManager.ts b/src/core/manager/StateManager.ts index de8bc2d..87414ed 100644 --- a/src/core/manager/StateManager.ts +++ b/src/core/manager/StateManager.ts @@ -175,9 +175,9 @@ export default class StateManager { // 更新选中框 if (needDeleteSelectBox) { - this.viewport.selectInspect.selectById(null) + this.viewport.selectManager.selectById(null) } else if (needUpdateSelectBox) { - this.viewport.selectInspect.selectById(this.viewport.state.selectedEntityId) + this.viewport.selectManager.selectById(this.viewport.state.selectedEntityId) } // 重新计算并更新多选框 @@ -188,7 +188,7 @@ export default class StateManager { newMultiSelectedEntityIds.push(entityId) } } - this.viewport.selectInspect.multiSelectByIds(newMultiSelectedEntityIds) + this.viewport.selectManager.multiSelectByIds(newMultiSelectedEntityIds) } // 获取被改过的数据, 覆盖之前的数据 diff --git a/src/editor/widgets/modeltree/ModeltreeViewJs.js b/src/editor/widgets/modeltree/ModeltreeViewJs.js index 0894228..6268e09 100644 --- a/src/editor/widgets/modeltree/ModeltreeViewJs.js +++ b/src/editor/widgets/modeltree/ModeltreeViewJs.js @@ -31,7 +31,7 @@ export default defineComponent({ console.log(this.$refs.grid1, evt) const { data } = evt if (data?.id) { - this.viewport.selectInspect.selectById(data.id) + this.viewport.selectManager.selectById(data.id) this.viewport.cameraToEntity(data.id) } else { diff --git a/src/example/ExampleUtil.js b/src/example/ExampleUtil.js index 18be015..e8a0567 100644 --- a/src/example/ExampleUtil.js +++ b/src/example/ExampleUtil.js @@ -106,7 +106,7 @@ export function buildPointPerformanceData(t, rows, cols) { node.tf[0][1] = 0.01 node.tf[0][2] = col * spacingZ node.tf[1] = [0, 0, 0] - node.tf[2] = [1, 1.5, 1] + node.tf[2] = [1, 1, 1] data.set(node.id, node) } } diff --git a/src/example/example1.js b/src/example/example1.js index c87483c..3c8f56d 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -326,10 +326,81 @@ export default { catalogCode: 'f3', t: 'floor', items: [ { + id: 'gs1', + t: 'gstore', + v: true, + tf: [[0, 0, 0], [0, 0, 0], [1, 0.5, 1]], + dt: { in: [], out: [], center: [] } + } + ] + }, + { + catalogCode: 'f5', t: 'floor', + items: [ + // pallet + { + id: 'pallet1', + t: 'pallet', + v: true, + tf: [[-5, 0, -5], [0, 0, 0], [1, 1, 2]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'pallet2', + t: 'pallet', + v: true, + tf: [[0, 0, -5], [0, 0, 0], [1, 1, 1]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'pallet3', + t: 'pallet', + v: true, + tf: [[5, 0, -5], [0, 0, 0], [2, 1, 1]], + dt: { in: [], out: [], center: [] } + }, + // tote + { + id: 'tote1', + t: 'tote', + v: true, + tf: [[-5, 0, 0], [0, 0, 0], [1, 1, 2]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'tote2', + t: 'tote', + v: true, + tf: [[0, 0, 0], [0, 0, 0], [1, 1, 1]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'tote3', + t: 'tote', + v: true, + tf: [[5, 0, 0], [0, 0, 0], [2, 1, 1]], + dt: { in: [], out: [], center: [] } + }, + // carton + { id: 'carton1', t: 'carton', v: true, - tf: [[0, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]], + tf: [[-5, 0, 5], [0, 0, 0], [1, 1, 2]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'carton2', + t: 'carton', + v: true, + tf: [[0, 0, 5], [0, 0, 0], [1, 1, 1]], + dt: { in: [], out: [], center: [] } + }, + { + id: 'carton3', + t: 'carton', + v: true, + tf: [[5, 0, 5], [0, 0, 0], [2, 1, 1]], dt: { in: [], out: [], center: [] } } ] @@ -396,7 +467,7 @@ export default { }, { catalogCode: '__f2', t: 'floor', - items: buildPointPerformanceData('pallet', 200, 500) + items: buildPointPerformanceData('carton', 200, 500) }, { catalogCode: '__f3', t: 'floor', @@ -478,7 +549,7 @@ export default { bayCount: 15, // 列数 aisleCount: 16, // 巷道数 hideFloor: false, // 隐藏底板 - shuttleRackDepth:7.6, + shuttleRackDepth: 7.6, bayRail: [ // 横行巷道 { afterBay: 2, // 横行巷道所在位置 @@ -880,6 +951,7 @@ export default { { catalogCode: 'f2', label: '二楼 (f2)' }, { catalogCode: 'f3', label: '三楼 (f3)' }, { catalogCode: 'f4', label: 'AGV测试区 (f4)' }, + { catalogCode: 'f5', label: '标准容器 (f5)' }, { catalogCode: 'OUT', label: '外场 (OUT)' }, { catalogCode: 'fe', label: '楼层电梯 (fe)' } ] diff --git a/src/modules/carton/CartonEntity.ts b/src/modules/carton/CartonEntity.ts index 9cf671f..1f872cd 100644 --- a/src/modules/carton/CartonEntity.ts +++ b/src/modules/carton/CartonEntity.ts @@ -1,5 +1,26 @@ import BaseEntity from '@/core/base/BaseItemEntity.ts' +import Constract from '@/core/Constract.ts' +import * as THREE from 'three' -export default class PalletEntity extends BaseEntity { +export default class CartonSetting extends BaseEntity { + /** + * 默认高度 + */ + defulePositionY: number = Constract.HEIGHT_WAY + /** + * 默认缩放 + */ + defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + + /** + * 默认旋转角度 + */ + defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + + + /** + * 默认颜色 + */ + defaultColor: THREE.Color = new THREE.Color(0xc29a70) } diff --git a/src/modules/carton/CartonRenderer.ts b/src/modules/carton/CartonRenderer.ts index 5028d53..98d56f2 100644 --- a/src/modules/carton/CartonRenderer.ts +++ b/src/modules/carton/CartonRenderer.ts @@ -5,7 +5,7 @@ import InstancePointManager from '@/core/manager/InstancePointManager.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' import MODULE_GLB_File from '@/assets/Models/carton.glb?url' import MODULE_3DS_TEX from '@/assets/Models/carton.jpg?url' -import { load3DModule, loadByUrl, loadGlbModule, loadTexture } from '@/core/ModelUtils.ts' +import { loadGlbModule, loadTexture, processModel } from '@/core/ModelUtils.ts' /** * 货架货位渲染器 @@ -34,12 +34,14 @@ export default class PalletRenderer extends BaseRenderer { ]).then(([_, glbGroup, cartonTexture]) => { const mesh = glbGroup.children[0] as THREE.Mesh - this.cartonGeometry = mesh.geometry + this.cartonMaterial = new THREE.MeshPhongMaterial({ color: 0xc29a70 }) // mesh.material as THREE.Material - this.cartonGeometry.scale(0.01, 0.01, 0.01) - // this.cartonGeometry.rotateX(-Math.PI / 2) - this.cartonGeometry.center() - this.cartonGeometry.translate(0, 0.3, 0) + + // this.cartonGeometry = mesh.geometry + // this.cartonGeometry.scale(0.01, 0.01, 0.01) + // this.cartonGeometry.center() + // this.cartonGeometry.translate(0, 0.3, 0) + this.cartonGeometry = processModel(mesh) cartonTexture.flipY = true cartonTexture.wrapS = THREE.RepeatWrapping diff --git a/src/modules/pallet/PalletRenderer.ts b/src/modules/pallet/PalletRenderer.ts index 88f357a..dff295e 100644 --- a/src/modules/pallet/PalletRenderer.ts +++ b/src/modules/pallet/PalletRenderer.ts @@ -3,9 +3,9 @@ import BaseRenderer from '@/core/base/BaseRenderer.ts' import Constract from '@/core/Constract.ts' import InstancePointManager from '@/core/manager/InstancePointManager.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' -import MODULE_3DS_File from '@/assets/Models/Pallet.3ds?url' -import MODULE_3DS_TEX from '@/assets/Models/PallTex.png?url' -import { load3DModule, loadByUrl, loadTexture } from '@/core/ModelUtils.ts' +import MODULE_GLB_File from '@/assets/Models/Pallet.glb?url' +import MODULE_3DS_TEX from '@/assets/Models/Pallet.jpg?url' +import { load3DModule, loadByUrl, loadGlbModule, loadTexture, processModel } from '@/core/ModelUtils.ts' /** * 货架货位渲染器 @@ -20,26 +20,35 @@ export default class PalletRenderer extends BaseRenderer { readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) readonly defaultUserData = { - color: 0xcfc195 + color: 0x2b5d94 } palletGeometry: THREE.BufferGeometry palletMaterial: THREE.Material + + init() { return Promise.all([ super.init(), - loadByUrl(MODULE_3DS_File), + loadGlbModule(MODULE_GLB_File), loadTexture(MODULE_3DS_TEX) - ]).then(([_, { data: queue3dsFile }, queueTexture]) => { - const mesh = load3DModule(queue3dsFile, '.3ds').children[0] as THREE.Mesh - this.palletGeometry = mesh.geometry.rotateX(-Math.PI / 2) - this.palletMaterial = mesh.material as THREE.Material + ]).then(([_, glbGroup, cartonTexture]) => { + const mesh = glbGroup.children[0] as THREE.Mesh + + this.palletGeometry = processModel(mesh) + this.palletMaterial = new THREE.MeshPhongMaterial({ color: 0x2b5d94 }) + + cartonTexture.flipY = true + cartonTexture.wrapS = THREE.RepeatWrapping + cartonTexture.wrapT = THREE.RepeatWrapping + cartonTexture.repeat.set(0.5, 0.5) + //@ts-ignore this.palletMaterial.color.set(this.defaultUserData.color) //@ts-ignore - this.palletMaterial.map = queueTexture + this.palletMaterial.normalMap = cartonTexture }) } diff --git a/src/types/ModelTypes.ts b/src/types/ModelTypes.ts index 821e34c..2e2a70d 100644 --- a/src/types/ModelTypes.ts +++ b/src/types/ModelTypes.ts @@ -67,7 +67,7 @@ export interface PointManagerReference { id: string } -export type Object3DLike = Object3D | LineManageWrap | PointManageWrap | InstanceMeshWrap +export type Object3DLike = Object3D | PointManageWrap | InstanceMeshWrap /** * 坐标的范指型, 可以是 THREE.Vector3 或者三元数组