Browse Source

统一线段 / 统一点位管理. InstancePointManager / LineSegmentManager

master
修宁 6 months ago
parent
commit
ca12e043fb
  1. 4
      src/core/Constract.ts
  2. 45
      src/core/base/BaseRenderer.ts
  3. 44
      src/core/engine/Viewport.ts
  4. 183
      src/core/manager/InstancePointManager.ts
  5. 267
      src/core/manager/LineSegmentManager.ts
  6. 41
      src/modules/measure/MeasureRenderer.ts

4
src/core/Constract.ts

@ -31,6 +31,8 @@ const Constract = Object.freeze({
HEIGHT_RACK: 0, HEIGHT_RACK: 0,
HEIGHT_WAY: 0.01, HEIGHT_WAY: 0.01,
HEIGHT_WAY_LABEL: 0.03, HEIGHT_WAY_LABEL: 0.03,
HEIGHT_WAY_LINE: 0.02 HEIGHT_WAY_LINE: 0.02,
MAX_MEASURE_INSTANCES: 1000,
}) })
export default Constract export default Constract

45
src/core/base/BaseRenderer.ts

@ -2,6 +2,9 @@ import type Viewport from '@/core/engine/Viewport'
import * as THREE from 'three' import * as THREE from 'three'
import { getLineId } from '@/core/ModelUtils.ts' import { getLineId } from '@/core/ModelUtils.ts'
import { Line2 } from 'three/examples/jsm/lines/Line2' import { Line2 } from 'three/examples/jsm/lines/Line2'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import Constract from '@/core/Constract.ts'
import type LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
/** /**
* *
@ -27,6 +30,48 @@ export default abstract class BaseRenderer {
return Promise.resolve() return Promise.resolve()
} }
get pointManager(): InstancePointManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set. Please call beginRendererUpdate first.')
}
let pointManager = this.tempViewport.pointManagerMap.get(this.itemTypeName)
if (!pointManager) {
pointManager = this.createPointManager()
if (pointManager) {
this.tempViewport.pointManagerMap.set(this.itemTypeName, pointManager)
}
}
return pointManager
}
get lineSegmentManager(): LineSegmentManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set. Please call beginRendererUpdate first.')
}
let lineSegmentManager = this.tempViewport.lineSegmentManagerMap.get(this.itemTypeName)
if (!lineSegmentManager) {
lineSegmentManager = this.createLineSegmentManager()
if (lineSegmentManager) {
this.tempViewport.lineSegmentManagerMap.set(this.itemTypeName, lineSegmentManager)
}
}
return lineSegmentManager
}
/**
*
*/
createPointManager(): InstancePointManager {
return null
}
/**
* 线
*/
createLineSegmentManager(): LineSegmentManager {
return null
}
/** /**
* *
* @param viewport * @param viewport

44
src/core/engine/Viewport.ts

@ -23,7 +23,8 @@ import Constract from '@/core/Constract.ts'
import DragControl from '@/core/controls/DragControl.ts' import DragControl from '@/core/controls/DragControl.ts'
import type { PropertySetter } from '@/core/base/PropertyTypes.ts' import type { PropertySetter } from '@/core/base/PropertyTypes.ts'
import LabelManager from '@/core/manager/LabelManager.ts' import LabelManager from '@/core/manager/LabelManager.ts'
import ResourceManager from '@/core/manager/ResourceManager.ts' import type InstancePointManager from '@/core/manager/InstancePointManager.ts'
import type LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
/** /**
* *
@ -60,6 +61,12 @@ export default class Viewport {
// 交互管理器 // 交互管理器
interactionManager = new InteractionManager() interactionManager = new InteractionManager()
// 点实例管理器 moduleName -> InstancePointManager
pointManagerMap: Map<string, InstancePointManager> = new Map()
// 线段实例管理器 moduleName -> InstancePointManager
lineSegmentManagerMap: Map<string, LineSegmentManager> = new Map()
// 监听窗口大小变化 // 监听窗口大小变化
resizeObserver?: ResizeObserver resizeObserver?: ResizeObserver
@ -331,23 +338,23 @@ export default class Viewport {
*/ */
animate() { animate() {
this.animationFrameId = requestAnimationFrame(this.animate.bind(this)) this.animationFrameId = requestAnimationFrame(this.animate.bind(this))
this.renderView()
if (window['lineMaterial']) { for (const lineSegmentManager of this.lineSegmentManagerMap.values()) {
this.offset -= 0.002 if (lineSegmentManager.needsUpdate) {
window['lineMaterial'].dashOffset = this.offset lineSegmentManager.updateGeometry()
}
} }
}
/**
*
*/
renderView() {
this.statsControls?.update() this.statsControls?.update()
this.renderer?.render(this.scene.scene, this.camera) this.renderer?.render(this.scene.scene, this.camera)
this.css2DRenderer.render(this.scene.scene, this.camera) this.css2DRenderer.render(this.scene.scene, this.camera)
this.css3DRenderer.render(this.scene.scene, this.camera) this.css3DRenderer.render(this.scene.scene, this.camera)
// if (window['lineMaterial']) {
// this.offset -= 0.002
// window['lineMaterial'].dashOffset = this.offset
// }
} }
/** /**
@ -463,6 +470,23 @@ export default class Viewport {
dispose() { dispose() {
this.state.isReady = false this.state.isReady = false
if (this.pointManagerMap.size > 0) {
this.pointManagerMap.forEach((manager) => {
if (manager.dispose) {
manager.dispose()
}
})
this.pointManagerMap.clear()
}
if (this.lineSegmentManagerMap.size > 0) {
this.lineSegmentManagerMap.forEach((manager) => {
if (manager.dispose) {
manager.dispose()
}
})
this.lineSegmentManagerMap.clear()
}
if (this.tools) { if (this.tools) {
for (const tool of this.tools) { for (const tool of this.tools) {
if (tool.dispose) { if (tool.dispose) {

183
src/core/manager/InstancePointManager.ts

@ -1,30 +1,177 @@
import * as THREE from 'three' import * as THREE from 'three'
import type IControls from '@/core/controls/IControls.ts' import type IControls from '@/core/controls/IControls.ts'
import type Viewport from '@/core/engine/Viewport.ts' import type Viewport from '@/core/engine/Viewport.ts'
import { Vector3 } from 'three/src/math/Vector3'
import { Euler } from 'three/src/math/Euler'
export default class InstancePointManager implements IControls { export default class InstancePointManager {
viewport: Viewport private readonly viewport: Viewport
mesh: THREE.InstancedMesh private readonly instancedMesh: THREE.InstancedMesh
dummy: THREE.Object3D = new THREE.Object3D() private readonly freeIndices: number[] = []
count: number = 0 private readonly maxInstanceCount: number
maxInstances: number = 100000 private readonly geometry: THREE.BufferGeometry
private readonly material: THREE.Material
private readonly dummy: THREE.Object3D = new THREE.Object3D()
// itemId -> instanceId
private instanceData: Map<string, number> = new Map()
init(viewport: Viewport): void { /**
*
* @param item
* @returns ID (-1)
*/
createPoint(item: ItemJson): number {
if (this.freeIndices.length === 0) {
system.showErrorDialog('InstancePointManager is full')
return -1
}
const instanceId = this.freeIndices.pop()!
this.instanceData.set(item.id, instanceId)
this.updatePoint(item)
return instanceId
}
/**
*
* @param item
* @param option
*/
updatePoint(item: ItemJson, option: {
position?: Vector3
rotation?: Vector3
scale?: Vector3
} = {}): void {
const instanceId = this.instanceData.get(item.id)
if (instanceId === undefined) return
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()
}
this.dummy.position.set(position[0], position[1], position[2])
this.dummy.rotation.set(
THREE.MathUtils.degToRad(rotation[0]),
THREE.MathUtils.degToRad(rotation[1]),
THREE.MathUtils.degToRad(rotation[2])
)
this.dummy.scale.set(scale[0], scale[1], scale[2])
if (item.v === false) {
this.dummy.scale.set(0, 0, 0)
}
this.dummy.updateMatrix()
this.instancedMesh.setMatrixAt(instanceId, this.dummy.matrix)
this.instancedMesh.instanceMatrix.needsUpdate = true
}
/**
*
* @param id ID
*/
deletePoint(id: string): void {
const instanceId = this.instanceData.get(id)
if (instanceId === undefined) return
// 隐藏实例
this.dummy.scale.set(0, 0, 0)
this.dummy.updateMatrix()
this.instancedMesh.setMatrixAt(instanceId, this.dummy.matrix)
this.instancedMesh.instanceMatrix.needsUpdate = true
// 回收索引
this.freeIndices.push(instanceId)
this.instanceData.delete(id)
}
/**
*
* @param id ID
* @param target
*/
getWorldPosition(id: string, target: THREE.Vector3): void {
const instanceId = this.instanceData.get(id)
if (instanceId === undefined) return
const matrix = new THREE.Matrix4()
this.instancedMesh.getMatrixAt(instanceId, matrix)
target.setFromMatrixPosition(matrix)
}
// init(viewport: Viewport): void {
// this.viewport = viewport
//
// const geometry = new THREE.PlaneGeometry(0.25, 0.25)
// const material = new THREE.MeshBasicMaterial({
// color: 0xFFFF99,
// transparent: true,
// depthWrite: false,
// side: THREE.DoubleSide
// })
//
// this.mesh = new THREE.InstancedMesh(geometry, material, this.maxInstances)
// this.mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
// viewport.scene.add(this.mesh)
// }
public static create(
viewport: Viewport,
geometry: THREE.BufferGeometry,
material: THREE.Material,
maxInstances: number = 1000
): InstancePointManager {
return new InstancePointManager(viewport, geometry, material, maxInstances)
}
constructor(
viewport: Viewport,
geometry: THREE.BufferGeometry,
material: THREE.Material,
maxInstances: number = 1000
) {
this.viewport = viewport this.viewport = viewport
this.geometry = geometry
this.material = material
this.maxInstanceCount = maxInstances
this.instancedMesh = new THREE.InstancedMesh(geometry, material, maxInstances)
this.instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
viewport.scene.add(this.instancedMesh)
const geometry = new THREE.PlaneGeometry(0.25, 0.25) this.dummy.scale.set(0, 0, 0)
const material = new THREE.MeshBasicMaterial({ for (let i = 0; i < maxInstances; i++) {
color: 0xFFFF99, this.dummy.updateMatrix()
transparent: true, this.instancedMesh.setMatrixAt(i, this.dummy.matrix)
depthWrite: false, this.freeIndices.push(i)
side: THREE.DoubleSide }
})
this.instancedMesh.instanceMatrix.needsUpdate = true
this.mesh = new THREE.InstancedMesh(geometry, material, this.maxInstances)
this.mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
viewport.scene.add(this.mesh)
} }
dispose() { dispose() {
this.viewport.scene.remove(this.instancedMesh)
this.instancedMesh.geometry.dispose()
if (this.instancedMesh.material) {
if (Array.isArray(this.instancedMesh.material)) {
this.instancedMesh.material.forEach(mat => mat.dispose())
} else {
this.instancedMesh.material.dispose()
}
}
this.instancedMesh.dispose()
this.instanceData.clear()
this.freeIndices.length = 0
} }
} }

267
src/core/manager/LineSegmentManager.ts

@ -1,16 +1,269 @@
import * as THREE from 'three' import * as THREE from 'three'
import type IControls from '@/core/controls/IControls.ts'
import type Viewport from '@/core/engine/Viewport.ts' import type Viewport from '@/core/engine/Viewport.ts'
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry'
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
export default class LineSegmentManager implements IControls { /**
viewport: Viewport * 线
*/
export default class LineSegmentManager {
private readonly viewport: Viewport
private readonly lineGeometry: LineSegmentsGeometry
private readonly lineMaterial: LineMaterial
private readonly lineSegments: LineSegments2
private readonly segments: Map<string, LineSegmentData> = new Map()
public needsUpdate: boolean = false
private colorArray: Float32Array | null = null
private positionArray: Float32Array | null = null
init(viewport: Viewport): void { /**
* 线
* @param key 线
* @param start
* @param end
* @param color 线 ()
* @param userData ()
*/
createLine(key: string, start: THREE.Vector3Like, end: THREE.Vector3Like, color?: THREE.Color | number | string, userData?: any): void {
const startVec = start instanceof THREE.Vector3
? start.clone()
: new THREE.Vector3(start[0], start[1], start[2])
const endVec = end instanceof THREE.Vector3
? end.clone()
: new THREE.Vector3(end[0], end[1], end[2])
let colorObj: THREE.Color | undefined
if (color instanceof THREE.Color) {
colorObj = color
} else if (typeof color === 'number') {
colorObj = new THREE.Color(color)
} else if (typeof color === 'string') {
colorObj = new THREE.Color(color)
}
if (this.segments.has(key)) {
// 更新现有线段
const segment = this.segments.get(key)!
segment.start.copy(startVec)
segment.end.copy(endVec)
if (colorObj) segment.color = colorObj
if (userData) segment.userData = userData
} else {
// 创建新线段
this.segments.set(key, {
key,
start: startVec,
end: endVec,
color: colorObj,
visible: true,
userData
})
}
this.needsUpdate = true
}
/**
* 线
* @param key 线
* @param start
* @param end
*/
updateLinePosition(key: string, start: THREE.Vector3Like, end: THREE.Vector3Like): void {
const segment = this.segments.get(key)
if (!segment) return
if (start instanceof THREE.Vector3) {
segment.start.copy(start)
} else {
segment.start.set(start[0], start[1], start[2])
}
if (end instanceof THREE.Vector3) {
segment.end.copy(end)
} else {
segment.end.set(end[0], end[1], end[2])
}
this.needsUpdate = true
}
/**
* 线
* @param key 线
* @param color
*/
updateLineColor(key: string, color: THREE.Color | number | string): void {
const segment = this.segments.get(key)
if (!segment) return
if (color instanceof THREE.Color) {
segment.color = color
} else if (typeof color === 'number') {
segment.color = new THREE.Color(color)
} else if (typeof color === 'string') {
segment.color = new THREE.Color(color)
} else {
segment.color = undefined
}
this.needsUpdate = true
}
/**
* 线
* @param key 线
* @param visible
*/
setLineVisible(key: string, visible: boolean): void {
const segment = this.segments.get(key)
if (segment && segment.visible !== visible) {
segment.visible = visible
this.needsUpdate = true
}
}
/**
* 线
* @param key 线
*/
deleteLine(key: string): void {
if (this.segments.delete(key)) {
this.needsUpdate = true
}
}
/**
* 线
* @param key 线
*/
getLineData(key: string): LineSegmentData | undefined {
return this.segments.get(key)
}
/**
* ()
*/
updateGeometry(): void {
if (!this.needsUpdate) return
const segmentCount = this.segments.size
const positionCount = segmentCount * 6 // 每条线段2个点,每个点3个分量
const colorCount = segmentCount * 6 // 每个顶点3个颜色分量
// 初始化或调整数组大小
if (!this.positionArray || this.positionArray.length !== positionCount) {
this.positionArray = new Float32Array(positionCount)
}
if (!this.colorArray || this.colorArray.length !== colorCount) {
this.colorArray = new Float32Array(colorCount)
}
// 填充数据
let positionIndex = 0
let colorIndex = 0
const defaultColor = new THREE.Color(this.lineMaterial.color)
this.segments.forEach(segment => {
if (!segment.visible) {
// 对于不可见的线段,跳过位置设置但保留颜色为黑色
positionIndex += 6
// 设置颜色为黑色(透明)
this.colorArray![colorIndex++] = 0
this.colorArray![colorIndex++] = 0
this.colorArray![colorIndex++] = 0
this.colorArray![colorIndex++] = 0
this.colorArray![colorIndex++] = 0
this.colorArray![colorIndex++] = 0
return
}
// 设置起点位置
this.positionArray![positionIndex++] = segment.start.x
this.positionArray![positionIndex++] = segment.start.y
this.positionArray![positionIndex++] = segment.start.z
// 设置终点位置
this.positionArray![positionIndex++] = segment.end.x
this.positionArray![positionIndex++] = segment.end.y
this.positionArray![positionIndex++] = segment.end.z
// 设置起点颜色
const startColor = segment.color || defaultColor
this.colorArray![colorIndex++] = startColor.r
this.colorArray![colorIndex++] = startColor.g
this.colorArray![colorIndex++] = startColor.b
// 设置终点颜色
const endColor = segment.color || defaultColor
this.colorArray![colorIndex++] = endColor.r
this.colorArray![colorIndex++] = endColor.g
this.colorArray![colorIndex++] = endColor.b
})
// 更新几何体
this.lineGeometry.setPositions(this.positionArray)
this.lineGeometry.setColors(this.colorArray)
// 设置实例计数为可见线段数量
const visibleCount = Array.from(this.segments.values()).filter(s => s.visible).length
this.lineGeometry.instanceCount = visibleCount
this.needsUpdate = false
}
/**
* 线
*/
public static create(viewport: Viewport, lineMaterial: LineMaterial): LineSegmentManager {
return new LineSegmentManager(viewport, lineMaterial)
}
private constructor(viewport: Viewport, lineMaterial: LineMaterial) {
this.viewport = viewport this.viewport = viewport
this.lineMaterial = lineMaterial
this.lineGeometry = new LineSegmentsGeometry()
// 创建线段的渲染对象
this.lineSegments = new LineSegments2(this.lineGeometry, this.lineMaterial)
this.lineSegments.name = 'batch_line_segments'
this.lineSegments.frustumCulled = false
this.viewport.scene.add(this.lineSegments)
} }
dispose(): void { dispose() {
// 清理资源 this.viewport.scene.remove(this.lineSegments)
this.viewport = undefined this.lineGeometry.dispose()
if (this.lineMaterial) {
if (Array.isArray(this.lineMaterial)) {
this.lineMaterial.forEach(mat => mat.dispose())
} else {
this.lineMaterial.dispose()
}
}
this.segments.clear()
this.positionArray = null
this.colorArray = null
} }
} }
/**
* 线
*/
interface LineSegmentData {
key: string;
start: THREE.Vector3;
end: THREE.Vector3;
color?: THREE.Color;
visible?: boolean;
userData?: any;
}

41
src/modules/measure/MeasureRenderer.ts

@ -5,6 +5,8 @@ import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { Line2 } from 'three/examples/jsm/lines/Line2.js' import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import Constract from '@/core/Constract.ts' import Constract from '@/core/Constract.ts'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
/** /**
* *
@ -22,34 +24,29 @@ export default class MeasureRenderer extends BaseRenderer {
public useHtmlLabel = false public useHtmlLabel = false
pointMaterial: THREE.Material pointMaterial: THREE.Material = new THREE.MeshBasicMaterial({
lineMaterial: LineMaterial color: 0xFFFF99,
transparent: true,
depthWrite: false,
side: THREE.DoubleSide
})
pointGeometry = new THREE.PlaneGeometry(0.25, 0.25)
lineMaterial: LineMaterial = new LineMaterial({
color: 0xFF8C00,
linewidth: 1,
vertexColors: false,
dashed: false
})
readonly defulePositionY = Constract.HEIGHT_MEASURE readonly defulePositionY = Constract.HEIGHT_MEASURE
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1) readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1)
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0)
constructor(itemTypeName: string) { createPointManager(): InstancePointManager {
super(itemTypeName) return InstancePointManager.create(this.tempViewport, this.pointGeometry, this.pointMaterial, Constract.MAX_MEASURE_INSTANCES)
} }
async init() { createLineSegmentManager(): LineSegmentManager {
this.pointMaterial = new THREE.SpriteMaterial({ return LineSegmentManager.create(this.tempViewport, this.lineMaterial)
color: 0xFFFF99, // 0x303133,
transparent: true,
side: THREE.DoubleSide,
opacity: 1,
sizeAttenuation: true
})
this.lineMaterial = new LineMaterial({
color: 0xFF8C00,
linewidth: 1,
vertexColors: false,
dashed: false
})
return Promise.all([
super.init()
])
} }
/** /**

Loading…
Cancel
Save