Browse Source

红选模式

master
修宁 7 months ago
parent
commit
cc24f8451d
  1. 157
      src/core/controls/SelectInspect.ts
  2. 28
      src/core/engine/Viewport.ts
  3. 21
      src/core/manager/EntityManager.ts
  4. 2
      src/editor/widgets/property/PropertyView.vue
  5. 2
      src/modules/way/WayRenderer.ts
  6. 3
      src/runtime/EventBus.ts

157
src/core/controls/SelectInspect.ts

@ -19,7 +19,12 @@ export default class SelectInspect implements IControls {
/** /**
* 线 * 线
*/ */
material: LineMaterial = new LineMaterial({ color: 0xffff00, linewidth: 2 }) yellowMaterial: LineMaterial = new LineMaterial({ color: 0xffff00, linewidth: 2 })
/**
* 线
*/
redMaterial: LineMaterial = new LineMaterial({ color: 0xff0000, linewidth: 2 })
/** /**
* *
@ -76,6 +81,11 @@ export default class SelectInspect implements IControls {
this.updateSelectionBox(this.viewport.state.selectedObject) this.updateSelectionBox(this.viewport.state.selectedObject)
}) })
EventBus.on('multiSelectedObjectsChanged', (data) => {
// 如果多选对象发生变化,清除当前选中对象的包围盒线框
this.updateMultiSelectionBoxes(data.multiSelectedObjects)
})
EventBus.on('selectedObjectPropertyChanged', (data) => { EventBus.on('selectedObjectPropertyChanged', (data) => {
this.updateSelectionBox(this.viewport.state.selectedObject) this.updateSelectionBox(this.viewport.state.selectedObject)
}) })
@ -90,6 +100,69 @@ export default class SelectInspect implements IControls {
}) })
} }
redSelectionGroup = new THREE.Group()
private updateMultiSelectionBoxes(multiSelectedObjects: THREE.Object3D[]) {
// 为所有多选对象创建包围盒线框
this.clearRedSelectionBoxes()
if (!multiSelectedObjects || multiSelectedObjects.length === 0) {
return
}
for (const object of multiSelectedObjects) {
if (object.userData.entityId) {
this.createRedSelectionBox(object)
}
}
}
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)
}
createRedSelectionBox(object: THREE.Object3D) {
// 如果对象没有 entityId,则不创建包围盒线框
if (!object.userData.entityId) {
return
}
// 如果选中的对象小于 0.5,要扩展包围盒
const RED_EXPAND_AMOUNT = 0.01 // 扩展包围盒的大小
// 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)
object.updateWorldMatrix(false, true)
const box = new THREE.Box3().setFromObject(object)
box.expandByScalar(RED_EXPAND_AMOUNT)
const size = new THREE.Vector3()
box.getSize(size)
const center = new THREE.Vector3()
box.getCenter(center)
// 创建包围盒几何体
const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z)
const edgesGeometry = new THREE.EdgesGeometry(helperGeometry)
const lineGeom = new LineGeometry()
// @ts-ignore
lineGeom.setPositions(edgesGeometry.attributes.position.array)
const selectionBox = new Line2(lineGeom, this.redMaterial)
selectionBox.computeLineDistances()
selectionBox.position.copy(center)
this.redSelectionGroup.add(selectionBox)
}
/** /**
* 线 * 线
*/ */
@ -101,12 +174,13 @@ export default class SelectInspect implements IControls {
} }
this.selectionId = selectedObject.userData?.entityId this.selectionId = selectedObject.userData?.entityId
const expandAmount = 0.2 // 扩展包围盒的大小 // 如果选中的对象小于 0.5,要扩展包围盒
const YELLOW_EXPAND_AMOUNT = 0.03 // 扩展包围盒的大小
// 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb) // 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)
selectedObject.updateWorldMatrix(false, true) selectedObject.updateWorldMatrix(false, true)
const box = new THREE.Box3().setFromObject(selectedObject) const box = new THREE.Box3().setFromObject(selectedObject)
box.expandByScalar(expandAmount) box.expandByScalar(YELLOW_EXPAND_AMOUNT)
const size = new THREE.Vector3() const size = new THREE.Vector3()
box.getSize(size) box.getSize(size)
@ -117,16 +191,13 @@ export default class SelectInspect implements IControls {
// 创建包围盒几何体 // 创建包围盒几何体
const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z) const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z)
const edgesGeometry = new THREE.EdgesGeometry(helperGeometry) const edgesGeometry = new THREE.EdgesGeometry(helperGeometry)
// 使用 LineGeometry 包装 edgesGeometry
const lineGeom = new LineGeometry() const lineGeom = new LineGeometry()
// @ts-ignore // @ts-ignore
lineGeom.setPositions(edgesGeometry.attributes.position.array) lineGeom.setPositions(edgesGeometry.attributes.position.array)
const selectionBox = new Line2(lineGeom, this.material) const selectionBox = new Line2(lineGeom, this.yellowMaterial)
selectionBox.computeLineDistances() selectionBox.computeLineDistances()
selectionBox.position.copy(center) selectionBox.position.copy(center)
selectionBox.name = 'selectionBox'
this.selectionBox = selectionBox this.selectionBox = selectionBox
console.log('selectedItem', this.viewport.state.selectedItem) console.log('selectedItem', this.viewport.state.selectedItem)
@ -162,7 +233,7 @@ export default class SelectInspect implements IControls {
if (this.recStartPos) { if (this.recStartPos) {
// 创建矩形 // 创建矩形
this.rectangle = new THREE.Mesh( this.rectangle = new THREE.Mesh(
new THREE.PlaneGeometry(1, 1), new THREE.PlaneGeometry(0.001, 0.001),
this.rectMaterial this.rectMaterial
) )
this.rectangle.name = 'selectRectangle' this.rectangle.name = 'selectRectangle'
@ -176,6 +247,7 @@ export default class SelectInspect implements IControls {
} }
} }
updateRectangle(position: THREE.Vector3) { updateRectangle(position: THREE.Vector3) {
if (!this.rectangle || !this.recStartPos) return if (!this.rectangle || !this.recStartPos) return
// console.log('updateRectangle', this.recStartPos, position) // console.log('updateRectangle', this.recStartPos, position)
@ -201,6 +273,8 @@ export default class SelectInspect implements IControls {
// 记录鼠标按下位置 // 记录鼠标按下位置
this.recStartPos = this.viewport.getClosestIntersection(event) this.recStartPos = this.viewport.getClosestIntersection(event)
this.createRectangle() this.createRectangle()
this.viewport.controls.enabled = false // 禁用控制器
} else { } else {
// 为 click 事件添加处理逻辑 // 为 click 事件添加处理逻辑
this.clickTime = Date.now() this.clickTime = Date.now()
@ -220,11 +294,14 @@ export default class SelectInspect implements IControls {
disposeRect() { disposeRect() {
if (this.rectangle !== null) { if (this.rectangle !== null) {
// 查找在这个矩形内的所有有效业务对象,并将他们添加进 viewport.state.multiSelectedObjects
this.multipleSelectedObjects()
this.viewport.scene.remove(this.rectangle) this.viewport.scene.remove(this.rectangle)
this.rectangle.geometry.dispose() this.rectangle.geometry.dispose()
this.rectangle = null this.rectangle = null
} }
this.recStartPos = null this.recStartPos = null
this.viewport.controls.enabled = true // 启用控制器
} }
onMouseUp(event: MouseEvent) { onMouseUp(event: MouseEvent) {
@ -253,7 +330,71 @@ export default class SelectInspect implements IControls {
selectedObjectMeta: this.viewport.state.selectedObjectMeta selectedObjectMeta: this.viewport.state.selectedObjectMeta
}) })
} }
} else {
// 如果没有选中任何对象,清除选中状态
this.viewport.state.selectedObject = null
this.viewport.state.selectedItem = null
this.viewport.state.selectedEntityId = null
this.viewport.state.selectedObjectMeta = null
EventBus.dispatch('selectedObjectChanged', {
viewport: markRaw(this.viewport),
selectedObject: null,
selectedItem: null,
selectedEntityId: null,
selectedObjectMeta: null
})
}
} }
} }
private multipleSelectedObjects() {
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 objects = this.viewport.entityManager.getObjectsInBox(startX, startZ, endX, endZ)
// 清空之前的多选对象
this.viewport.state.multiSelectedObjects = []
// 遍历找到的对象,添加到多选对象中
const multiSelectedObjects = []
const multiSelectedItems = []
const multiSelectedEntityIds = []
const multiSelectedObjectMetas = []
for (const object of objects) {
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)
multiSelectedObjectMetas.push(getMeta(object.userData.t))
}
} }
} }
// 触发多选对象更新事件
this.viewport.state.multiSelectedObjects = markRaw(objects)
this.viewport.state.multiSelectedItems = markRaw(multiSelectedItems)
this.viewport.state.multiSelectedEntityIds = multiSelectedEntityIds
this.viewport.state.multiSelectedObjectMetas = multiSelectedObjectMetas
EventBus.dispatch('multiSelectedObjectsChanged', {
viewport: markRaw(this.viewport),
multiSelectedObjects: this.viewport.state.multiSelectedObjects,
multiSelectedItems: this.viewport.state.multiSelectedItems,
multiSelectedEntityIds: this.viewport.state.multiSelectedEntityIds,
multiSelectedObjectMetas: this.viewport.state.multiSelectedObjectMetas
})
}
}

28
src/core/engine/Viewport.ts

@ -66,7 +66,17 @@ export default class Viewport {
isReady: false, isReady: false,
isUpdating: false, isUpdating: false,
cursorMode: 'normal', cursorMode: 'normal',
selectedObject: null,
selectedObject: undefined,
selectedItem: undefined,
selectedEntityId: undefined,
selectedObjectMeta: undefined,
multiSelectedObjects: [],
multiSelectedItems: [],
multiSelectedEntityIds: [],
multiSelectedObjectMetas: [],
view3DMode: Constract.Mode2D, view3DMode: Constract.Mode2D,
camera: { camera: {
position: { x: 0, y: 0, z: 0 }, position: { x: 0, y: 0, z: 0 },
@ -550,20 +560,22 @@ export interface ViewportState {
cursorMode: string // CursorMode, cursorMode: string // CursorMode,
/** /**
* *
*/ */
selectedObject: THREE.Object3D | undefined selectedObject: THREE.Object3D | undefined
selectedItem: ItemJson | undefined selectedItem: ItemJson | undefined
selectedEntityId: string | undefined selectedEntityId: string | undefined
selectedObjectMeta: IMeta | undefined
view3DMode: string // Constract.Mode2D | Constract.Mode3D
/** /**
* *
*/ */
selectedObjectMeta: IMeta | undefined multiSelectedObjects: THREE.Object3D[]
multiSelectedItems: ItemJson[]
multiSelectedEntityIds: string[]
multiSelectedObjectMetas: IMeta[]
view3DMode: string // Constract.Mode2D | Constract.Mode3D
/** /**
* *

21
src/core/manager/EntityManager.ts

@ -548,6 +548,27 @@ export default class EntityManager {
r => getClosestObject(r.object) r => getClosestObject(r.object)
).filter(obj => obj?.userData && obj.userData.selectable !== false) ).filter(obj => obj?.userData && obj.userData.selectable !== false)
} }
/**
*
*/
getObjectsInBox(startX: number, startZ: number, endX: number, endZ: number) {
const box = new THREE.Box2(
new THREE.Vector2(startX, startZ),
new THREE.Vector2(endX, endZ)
)
const objectsInBox: THREE.Object3D[] = []
for (const [id, objects] of this.objects.entries()) {
for (const obj of objects) {
if (box.containsPoint(new Vector2(obj.position.x, obj.position.z))) {
objectsInBox.push(obj)
}
}
}
return objectsInBox
}
} }
interface LineDiffItem { interface LineDiffItem {

2
src/editor/widgets/property/PropertyView.vue

@ -93,12 +93,14 @@ export default {
selectedObjectChanged(state) { selectedObjectChanged(state) {
const data = state.selectedItem; const data = state.selectedItem;
console.log("selectedObjectChanged data", data) console.log("selectedObjectChanged data", data)
if(data) {
this.viewport.stateManager.beginStateUpdate() this.viewport.stateManager.beginStateUpdate()
const item = _.find(this.viewport.stateManager.vdata.items, item => item.id === data.id) const item = _.find(this.viewport.stateManager.vdata.items, item => item.id === data.id)
// item.tf[0][0] = item.tf[0][0] / 2; // item.tf[0][0] = item.tf[0][0] / 2;
console.log("selectedObjectChanged item", item) console.log("selectedObjectChanged item", item)
// _.extend(item, data) // _.extend(item, data)
this.viewport.stateManager.endStateUpdate() this.viewport.stateManager.endStateUpdate()
}
}, },
}, },
mounted() { mounted() {

2
src/modules/way/WayRenderer.ts

@ -87,7 +87,7 @@ export default class WayRenderer extends BaseRenderer {
const width = 1 const width = 1
const curve = new THREE.LineCurve3(startPosition, endPosition) const curve = new THREE.LineCurve3(startPosition, endPosition)
const tubeGeometry = new THREE.TubeGeometry(curve, 1, width / 2, 8, false) const tubeGeometry = new THREE.TubeGeometry(curve, 1, width / 2, 2, false)
const lineMesh = new THREE.Mesh(tubeGeometry, this.lineMaterial) const lineMesh = new THREE.Mesh(tubeGeometry, this.lineMaterial)
group.add(lineMesh) group.add(lineMesh)

3
src/runtime/EventBus.ts

@ -6,7 +6,8 @@ export type DispatchNames = 'selectedObjectChanged' |
'catalogChanged' | 'catalogChanged' |
'dataLoadComplete' | 'dataLoadComplete' |
'entityDeleted' | 'entityDeleted' |
'selectedObjectPropertyChanged' 'selectedObjectPropertyChanged' |
'multiSelectedObjectsChanged'
export default { export default {
dispatch(name: DispatchNames, data?: any) { dispatch(name: DispatchNames, data?: any) {

Loading…
Cancel
Save