Browse Source

Merge remote-tracking branch 'origin/master'

master
lizw-2015 6 months ago
parent
commit
edf833099d
  1. 4
      src/core/Constract.ts
  2. 45
      src/core/base/BaseRenderer.ts
  3. 48
      src/core/engine/Viewport.ts
  4. 177
      src/core/manager/InstancePointManager.ts
  5. 207
      src/core/manager/LabelManager.ts
  6. 269
      src/core/manager/LineSegmentManager.ts
  7. 120
      src/core/manager/ResourceManager.ts
  8. 182
      src/modules/measure/MeasureRenderer.ts

4
src/core/Constract.ts

@ -31,6 +31,8 @@ const Constract = Object.freeze({
HEIGHT_RACK: 0,
HEIGHT_WAY: 0.01,
HEIGHT_WAY_LABEL: 0.03,
HEIGHT_WAY_LINE: 0.02
HEIGHT_WAY_LINE: 0.02,
MAX_MEASURE_INSTANCES: 1000,
})
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 { getLineId } from '@/core/ModelUtils.ts'
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()
}
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

48
src/core/engine/Viewport.ts

@ -22,6 +22,9 @@ import EventBus from '@/runtime/EventBus.ts'
import Constract from '@/core/Constract.ts'
import DragControl from '@/core/controls/DragControl.ts'
import type { PropertySetter } from '@/core/base/PropertyTypes.ts'
import LabelManager from '@/core/manager/LabelManager.ts'
import type InstancePointManager from '@/core/manager/InstancePointManager.ts'
import type LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
/**
*
@ -40,11 +43,13 @@ export default class Viewport {
selectInspect = new SelectInspect()
mouseMoveInspect = new MouseMoveInspect()
dragControl = new DragControl()
labelManager = new LabelManager()
tools: IControls[] = [
markRaw(this.selectInspect),
markRaw(this.mouseMoveInspect),
markRaw(this.dragControl)
markRaw(this.dragControl),
markRaw(this.labelManager)
]
// 状态管理器
@ -56,6 +61,12 @@ export default class Viewport {
// 交互管理器
interactionManager = new InteractionManager()
// 点实例管理器 moduleName -> InstancePointManager
pointManagerMap: Map<string, InstancePointManager> = new Map()
// 线段实例管理器 moduleName -> InstancePointManager
lineSegmentManagerMap: Map<string, LineSegmentManager> = new Map()
// 监听窗口大小变化
resizeObserver?: ResizeObserver
@ -327,23 +338,23 @@ export default class Viewport {
*/
animate() {
this.animationFrameId = requestAnimationFrame(this.animate.bind(this))
this.renderView()
if (window['lineMaterial']) {
this.offset -= 0.002
window['lineMaterial'].dashOffset = this.offset
for (const lineSegmentManager of this.lineSegmentManagerMap.values()) {
if (lineSegmentManager.needsUpdate) {
lineSegmentManager.updateGeometry()
}
}
}
/**
*
*/
renderView() {
this.statsControls?.update()
this.renderer?.render(this.scene.scene, this.camera)
this.css2DRenderer.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
// }
}
/**
@ -459,6 +470,23 @@ export default class Viewport {
dispose() {
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) {
for (const tool of this.tools) {
if (tool.dispose) {

177
src/core/manager/InstancePointManager.ts

@ -0,0 +1,177 @@
import * as THREE from 'three'
import type IControls from '@/core/controls/IControls.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 {
private readonly viewport: Viewport
private readonly instancedMesh: THREE.InstancedMesh
private readonly freeIndices: number[] = []
private readonly maxInstanceCount: number
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()
/**
*
* @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.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)
this.dummy.scale.set(0, 0, 0)
for (let i = 0; i < maxInstances; i++) {
this.dummy.updateMatrix()
this.instancedMesh.setMatrixAt(i, this.dummy.matrix)
this.freeIndices.push(i)
}
this.instancedMesh.instanceMatrix.needsUpdate = true
}
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
}
}

207
src/core/manager/LabelManager.ts

@ -0,0 +1,207 @@
import * as THREE from 'three'
import type IControls from '@/core/controls/IControls.ts'
import type Viewport from '@/core/engine/Viewport.ts'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Text } from 'troika-three-text'
import SimSunTTF from '@/assets/fonts/simsunb.ttf'
export interface LabelOption {
/**
*
*/
name?: string
/**
* 使 HTML
*/
useHtmlLabel: boolean
/**
* ex: css='14px', text=0.4
*/
fontSize: number | string
/**
* ex: '#ffffff'
*/
color: string
/**
* ex: css='5px 8px', text=0.2
*/
padding?: number | string
text?: string
}
/**
*
*/
export default class LabelManager implements IControls {
viewport: Viewport
private labelMap: Map<string, Text | CSS2DObject> = new Map()
init(viewport: Viewport): void {
this.viewport = viewport
}
createOrUpdateLabelByDistance(parentObj: THREE.Object3D, startPos: THREE.Vector3, endPos: THREE.Vector3, option: LabelOption): Text | CSS2DObject {
let labelObj = this.labelMap.get(parentObj.userData.labelObjectId)
if (!labelObj) {
labelObj = this.createLabel(parentObj, option)
}
const position = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5)
labelObj.position.set(position.x, position.y + 0.3, position.z - 0.2)
// 计算距离
const distance = startPos.distanceTo(endPos)
const text = distance.toFixed(2) + ' m'
this.updateLabel(parentObj, text)
return labelObj
}
createLabel(parentObj: THREE.Object3D, option: LabelOption): Text | CSS2DObject {
const labelObj = this.createLabelObject(option)
parentObj.userData.labelObjectId = labelObj.uuid
this.viewport.scene.add(labelObj)
if (labelObj instanceof CSS2DObject) {
labelObj.element.innerHTML = option.text
} else if (labelObj instanceof Text) {
// 让文本朝向摄像机
labelObj.quaternion.copy(this.viewport.camera.quaternion)
labelObj.text = option.text
labelObj.sync()
}
return labelObj
}
updateLabel(parentObj: THREE.Object3D, text: string) {
const labelObj = this.labelMap.get(parentObj.userData.labelObjectId)
if (labelObj) {
if (labelObj instanceof CSS2DObject) {
labelObj.element.innerHTML = text
} else if (labelObj instanceof Text) {
labelObj.text = text
labelObj.sync()
}
} else {
console.warn('Label not found for parent object:', parentObj)
}
}
removeLabel(parentObj: THREE.Object3D) {
if (parentObj?.userData?.labelObjectId) {
const labelObj = this.labelMap.get(parentObj.userData.labelObjectId)
this.labelMap.delete(labelObj.uuid)
this.viewport.scene.remove(labelObj)
labelObj.dispose()
parentObj.userData.labelObjectId = undefined
}
}
removeById(id: string): void {
const labelObj = this.labelMap.get(id)
if (labelObj) {
this.viewport.scene.remove(labelObj)
this.labelMap.delete(id)
labelObj.dispose()
}
}
private createLabelObject(option: LabelOption): Text | CSS2DObject {
if (option.useHtmlLabel) {
const div = document.createElement('div')
div.className = 'css2dObjectLabel'
div.innerHTML = option.text
div.style.padding = (option.padding as string)
div.style.color = option.color
div.style.fontSize = (option.fontSize as string)
div.style.position = 'absolute'
div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)'
div.style.borderRadius = '12px'
div.style.top = '0px'
div.style.left = '0px'
// div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件
const obj = new CSS2DObject(div)
obj.uuid = system.createUUID()
obj.name = option.name
this.labelMap.set(obj.uuid, obj)
return obj
} else {
const label = new Text()
label.uuid = system.createUUID()
label.text = option.text
label.font = SimSunTTF
label.fontSize = (option.fontSize as number)
label.color = option.color
label.opacity = 0.8
label.padding = (option.padding as number)
label.anchorX = 'center'
label.anchorY = 'middle'
label.depthOffset = 1
label.backgroundColor = '#000000' // 黑色背景
label.backgroundOpacity = 0.6 // 背景半透明
label.padding = 0.2 // 内边距
label.material.depthTest = false
label.name = option.name
label.sync()
this.labelMap.set(label.uuid, label)
return label
}
}
dispose(): void {
// 清理资源
this.viewport = undefined
}
// /**
// * 手动创建标签示例
// */
// createLabel(text: string): Text | CSS2DObject {
// if (this.useHtmlLabel) {
// const div = document.createElement('div')
// div.className = 'css2dObjectLabel'
// div.innerHTML = text
// div.style.padding = '5px 8px'
// div.style.color = '#fff'
// div.style.fontSize = '14px'
// div.style.position = 'absolute'
// div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)'
// div.style.borderRadius = '12px'
// div.style.top = '0px'
// div.style.left = '0px'
// // div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件
// const obj = new CSS2DObject(div)
// obj.name = MeasureRenderer.LABEL_NAME
// return obj
//
// } else {
// const label = new Text()
// label.text = text
// label.font = SimSunTTF
// label.fontSize = 0.4
// label.color = '#333333'
// label.opacity = 0.8
// label.padding = 0.2
// label.anchorX = 'center'
// label.anchorY = 'middle'
// label.depthOffset = 1
// label.backgroundColor = '#000000' // 黑色背景
// label.backgroundOpacity = 0.6 // 背景半透明
// label.padding = 0.2 // 内边距
// label.material.depthTest = false
// label.name = MeasureRenderer.LABEL_NAME
//
// label.sync()
// return label
// }
// }
}

269
src/core/manager/LineSegmentManager.ts

@ -0,0 +1,269 @@
import * as THREE from 'three'
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 {
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
/**
* 线
* @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.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() {
this.viewport.scene.remove(this.lineSegments)
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;
}

120
src/core/manager/ResourceManager.ts

@ -0,0 +1,120 @@
import * as THREE from 'three'
import type Viewport from '@/core/engine/Viewport.ts'
/**
*
* Three.js
*
*/
export default class ResourceManager {
public static readonly instance: ResourceManager = new ResourceManager()
viewport: Viewport
geometries: Map<string, THREE.BufferGeometry> = new Map()
materials: Map<string, THREE.Material> = new Map()
textures: Map<string, THREE.Texture> = new Map()
meshes: Map<string, THREE.Mesh> = new Map()
createGeometry(key: string, geometry: THREE.BufferGeometry) {
this.geometries.set(key, geometry)
return geometry
}
createMaterial(key: string, material: THREE.Material) {
this.materials.set(key, material)
return material
}
createTexture(key: string, texture: THREE.Texture) {
this.textures.set(key, texture)
return texture
}
createMesh(key: string, mesh: THREE.Mesh) {
this.meshes.set(key, mesh)
return mesh
}
getGeometry(key: string) {
return this.geometries.get(key)
}
getMaterial(key: string) {
return this.materials.get(key)
}
getTexture(key: string) {
return this.textures.get(key)
}
getMesh(key: string) {
return this.meshes.get(key)
}
disposeGeometry(key: string) {
const geometry = this.geometries.get(key)
if (geometry) {
geometry.dispose()
this.geometries.delete(key)
}
}
disposeMaterial(key: string) {
const material = this.materials.get(key)
if (material) {
material.dispose()
this.materials.delete(key)
}
}
disposeTexture(key: string) {
const texture = this.textures.get(key)
if (texture) {
texture.dispose()
this.textures.delete(key)
}
}
disposeMesh(key: string) {
const mesh = this.meshes.get(key)
if (mesh) {
if (mesh.geometry) {
mesh.geometry.dispose()
}
if (mesh.material) {
if (Array.isArray(mesh.material)) {
mesh.material.forEach(mat => mat.dispose())
} else {
mesh.material.dispose()
}
}
this.meshes.delete(key)
}
}
dispose(): void {
// 清理资源
this.geometries.forEach(geometry => geometry.dispose())
this.materials.forEach(material => material.dispose())
this.textures.forEach(texture => texture.dispose())
this.meshes.forEach(mesh => {
if (mesh.geometry) {
mesh.geometry.dispose()
}
if (mesh.material) {
if (Array.isArray(mesh.material)) {
mesh.material.forEach(mat => mat.dispose())
} else {
mesh.material.dispose()
}
}
})
this.geometries.clear()
this.materials.clear()
this.textures.clear()
this.meshes.clear()
this.viewport = undefined
}
}

182
src/modules/measure/MeasureRenderer.ts

@ -4,11 +4,9 @@ import { getLineId } from '@/core/ModelUtils.ts'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { numberToString } from '@/utils/webutils.ts'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Text } from 'troika-three-text'
import SimSunTTF from '@/assets/fonts/simsunb.ttf'
import Constract from '@/core/Constract.ts'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
/**
*
@ -26,34 +24,29 @@ export default class MeasureRenderer extends BaseRenderer {
public useHtmlLabel = false
pointMaterial: THREE.Material
lineMaterial: LineMaterial
pointMaterial: THREE.Material = new THREE.MeshBasicMaterial({
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 defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1)
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0)
constructor(itemTypeName: string) {
super(itemTypeName)
createPointManager(): InstancePointManager {
return InstancePointManager.create(this.tempViewport, this.pointGeometry, this.pointMaterial, Constract.MAX_MEASURE_INSTANCES)
}
async init() {
this.pointMaterial = new THREE.SpriteMaterial({
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()
])
createLineSegmentManager(): LineSegmentManager {
return LineSegmentManager.create(this.tempViewport, this.lineMaterial)
}
/**
@ -114,92 +107,57 @@ export default class MeasureRenderer extends BaseRenderer {
//
// this.group.add(...objects)
// }
//
// afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: THREE.Object3D) {
// super.afterCreateOrUpdateLine(start, end, type, option, object)
//
// const startPoint = this.tempViewport?.entityManager.findObjectById(start.id)
// const endPoint = this.tempViewport?.entityManager.findObjectById(end.id)
//
// const p0 = startPoint.position
// const p1 = endPoint.position
//
// const dist = p0.distanceTo(p1)
// const label = numberToString(dist) + ' m'
//
// const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5)
// let labelObj: Text | CSS2DObject | undefined = object.userData.labelObj
// if (!labelObj || !labelObj.parent) {
// labelObj = this.createLabel(label)
// this.group.add(labelObj)
// object.userData.labelObj = labelObj
// }
//
// labelObj.position.set(position.x, position.y + 0.3, position.z - 0.2)
//
// if (this.useHtmlLabel) {
// labelObj.element.innerHTML = label
//
// } else {
// // 让文本朝向摄像机
// labelObj.quaternion.copy(this.tempViewport.camera.quaternion)
// labelObj.text = label
// labelObj.sync()
// }
// }
//
// afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: THREE.Object3D) {
// super.afterDeleteLine(start, end, type, option, object)
//
// // 删除标签
// const labelObj = object.userData?.labelObj
// if (labelObj && labelObj.parent) {
// labelObj.parent.remove(labelObj)
// }
// }
//
// /**
// * 创建标签
// */
// createLabel(text: string): Text | CSS2DObject {
// if (this.useHtmlLabel) {
// const div = document.createElement('div')
// div.className = 'css2dObjectLabel'
// div.innerHTML = text
// div.style.padding = '5px 8px'
// div.style.color = '#fff'
// div.style.fontSize = '14px'
// div.style.position = 'absolute'
// div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)'
// div.style.borderRadius = '12px'
// div.style.top = '0px'
// div.style.left = '0px'
// // div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件
// const obj = new CSS2DObject(div)
// obj.name = MeasureRenderer.LABEL_NAME
// return obj
//
// } else {
// const label = new Text()
// label.text = text
// label.font = SimSunTTF
// label.fontSize = 0.4
// label.color = '#333333'
// label.opacity = 0.8
// label.padding = 0.2
// label.anchorX = 'center'
// label.anchorY = 'middle'
// label.depthOffset = 1
// label.backgroundColor = '#000000' // 黑色背景
// label.backgroundOpacity = 0.6 // 背景半透明
// label.padding = 0.2 // 内边距
// label.material.depthTest = false
// label.name = MeasureRenderer.LABEL_NAME
//
// label.sync()
// return label
// }
// }
afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: THREE.Object3D) {
super.afterCreateOrUpdateLine(start, end, type, option, object)
const startPoint = this.tempViewport?.entityManager.findObjectById(start.id)
const endPoint = this.tempViewport?.entityManager.findObjectById(end.id)
this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, {
useHtmlLabel: this.useHtmlLabel,
fontSize: 0.4,
color: '#333333'
})
// const p0 = startPoint.position
// const p1 = endPoint.position
//
// const dist = p0.distanceTo(p1)
// const label = numberToString(dist) + ' m'
//
// const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5)
// let labelObj: Text | CSS2DObject | undefined = object.userData.labelObj
// if (!labelObj || !labelObj.parent) {
// labelObj = this.createLabel(label)
// this.appendToScene(labelObj)
// object.userData.labelObj = labelObj
// }
//
// labelObj.position.set(position.x, position.y + 0.3, position.z - 0.2)
//
// if (this.useHtmlLabel) {
// labelObj.element.innerHTML = label
//
// } else {
// // 让文本朝向摄像机
// labelObj.quaternion.copy(this.tempViewport.camera.quaternion)
// labelObj.text = label
// labelObj.sync()
// }
}
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: THREE.Object3D) {
super.afterDeleteLine(start, end, type, option, object)
this.tempViewport.labelManager.removeLabel(object)
// 删除标签
// const labelObj = object.userData?.labelObj
// if (labelObj && labelObj.parent) {
// labelObj.parent.remove(labelObj)
// }
}
dispose() {
super.dispose()

Loading…
Cancel
Save