bounding-sphere-helper.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { createRenderObject, GraphicsRenderObject, getNextMaterialId } from 'mol-gl/render-object'
  7. import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
  8. import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
  9. import { Mesh } from 'mol-geo/geometry/mesh/mesh';
  10. import { ParamDefinition as PD } from 'mol-util/param-definition';
  11. import Scene from 'mol-gl/scene';
  12. import { WebGLContext } from 'mol-gl/webgl/context';
  13. import { Sphere3D } from 'mol-math/geometry';
  14. import { Color } from 'mol-util/color';
  15. import { ColorNames } from 'mol-util/color/tables';
  16. import { TransformData } from 'mol-geo/geometry/transform-data';
  17. import { sphereVertexCount } from 'mol-geo/primitive/sphere';
  18. import { ValueCell } from 'mol-util';
  19. import { Geometry } from 'mol-geo/geometry/geometry';
  20. export const DebugHelperParams = {
  21. sceneBoundingSpheres: PD.Boolean(false, { description: 'Show scene bounding spheres.' }),
  22. objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
  23. instanceBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of instances.' }),
  24. }
  25. export type DebugHelperParams = typeof DebugHelperParams
  26. export type DebugHelperProps = PD.Values<DebugHelperParams>
  27. type BoundingSphereData = { boundingSphere: Sphere3D, renderObject: GraphicsRenderObject, mesh: Mesh }
  28. export class BoundingSphereHelper {
  29. readonly scene: Scene
  30. private readonly parent: Scene
  31. private _props: DebugHelperProps
  32. private objectsData = new Map<GraphicsRenderObject, BoundingSphereData>()
  33. private instancesData = new Map<GraphicsRenderObject, BoundingSphereData>()
  34. private sceneData: BoundingSphereData | undefined
  35. constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
  36. this.scene = Scene.create(ctx)
  37. this.parent = parent
  38. this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props }
  39. }
  40. update() {
  41. const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.grey)
  42. if (newSceneData) this.sceneData = newSceneData
  43. this.parent.forEach((r, ro) => {
  44. const objectData = this.objectsData.get(ro)
  45. const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato)
  46. if (newObjectData) this.objectsData.set(ro, newObjectData)
  47. if (ro.type === 'mesh' || ro.type === 'lines' || ro.type === 'points') {
  48. const instanceData = this.instancesData.get(ro)
  49. const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, {
  50. aTransform: ro.values.aTransform,
  51. matrix: ro.values.matrix,
  52. transform: ro.values.transform,
  53. extraTransform: ro.values.extraTransform,
  54. uInstanceCount: ro.values.uInstanceCount,
  55. instanceCount: ro.values.instanceCount,
  56. aInstance: ro.values.aInstance,
  57. })
  58. if (newInstanceData) this.instancesData.set(ro, newInstanceData)
  59. }
  60. })
  61. this.objectsData.forEach((objectData, ro) => {
  62. if (!this.parent.has(ro)) {
  63. this.scene.remove(objectData.renderObject)
  64. this.objectsData.delete(ro)
  65. }
  66. })
  67. this.instancesData.forEach((instanceData, ro) => {
  68. if (!this.parent.has(ro)) {
  69. this.scene.remove(instanceData.renderObject)
  70. this.instancesData.delete(ro)
  71. }
  72. })
  73. this.scene.update(void 0, false);
  74. }
  75. syncVisibility() {
  76. if (this.sceneData) {
  77. this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres
  78. }
  79. this.parent.forEach((_, ro) => {
  80. const objectData = this.objectsData.get(ro)
  81. if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres
  82. const instanceData = this.instancesData.get(ro)
  83. if (instanceData) instanceData.renderObject.state.visible = ro.state.visible && this._props.instanceBoundingSpheres
  84. })
  85. }
  86. clear() {
  87. this.sceneData = undefined
  88. this.objectsData.clear()
  89. this.scene.clear()
  90. }
  91. get isEnabled() {
  92. return this._props.sceneBoundingSpheres || this._props.objectBoundingSpheres || this._props.instanceBoundingSpheres
  93. }
  94. get props() { return this._props as Readonly<DebugHelperProps> }
  95. setProps (props: Partial<DebugHelperProps>) {
  96. Object.assign(this._props, props)
  97. if (this.isEnabled) this.update()
  98. }
  99. }
  100. function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, transform?: TransformData) {
  101. if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) {
  102. const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh)
  103. const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, transform)
  104. if (data) {
  105. ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(mesh))
  106. } else {
  107. scene.add(renderObject)
  108. }
  109. return { boundingSphere: Sphere3D.clone(boundingSphere), renderObject, mesh }
  110. }
  111. }
  112. function createBoundingSphereMesh(boundingSphere: Sphere3D, mesh?: Mesh) {
  113. const detail = 2
  114. const vertexCount = sphereVertexCount(detail)
  115. const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
  116. if (boundingSphere.radius) addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
  117. return MeshBuilder.getMesh(builderState)
  118. }
  119. const boundingSphereHelberMaterialId = getNextMaterialId()
  120. function createBoundingSphereRenderObject(mesh: Mesh, color: Color, transform?: TransformData) {
  121. const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
  122. return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, boundingSphereHelberMaterialId)
  123. }