threepipe
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Object3DWidgetsPlugin.ts 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import {UiObjectConfig} from 'uiconfig.js'
  2. import {IWidget} from '../../core'
  3. import {AViewerPluginSync, ThreeViewer} from '../../viewer'
  4. import {IEvent, onChange} from 'ts-browser-helpers'
  5. import {Object3D} from 'three'
  6. import {CameraHelper2, DirectionalLightHelper2, PointLightHelper2, SpotLightHelper2} from '../../three'
  7. export interface IObject3DHelper<T extends Object3D&IWidget = Object3D&IWidget>{
  8. Create: (o: Object3D)=>T,
  9. Check: (o: Object3D)=>boolean,
  10. }
  11. /**
  12. * Adds light and camera helpers/gizmos in the viewer.
  13. * A helper is automatically created when any supported light or camera is added to the scene.
  14. * @category Plugin
  15. */
  16. export class Object3DWidgetsPlugin extends AViewerPluginSync<''> {
  17. @onChange(Object3DWidgetsPlugin.prototype.setDirty)
  18. enabled = true
  19. public static readonly PluginType = 'Object3DWidgetsPlugin'
  20. helpers: IObject3DHelper[] = [
  21. DirectionalLightHelper2,
  22. SpotLightHelper2,
  23. PointLightHelper2,
  24. CameraHelper2,
  25. ]
  26. setDirty() {
  27. this.widgets?.forEach(w => w.visible = !this.isDisabled())
  28. this._viewer?.setDirty()
  29. }
  30. toJSON: any = null
  31. constructor(enabled = true) {
  32. super()
  33. this.enabled = enabled
  34. }
  35. private _widgetRoot = new Object3D()
  36. onAdded(viewer: ThreeViewer) {
  37. super.onAdded(viewer)
  38. viewer.scene.addEventListener('addSceneObject', this._addSceneObject)
  39. viewer.scene.addObject(this._widgetRoot)
  40. }
  41. onRemove(viewer: ThreeViewer) {
  42. viewer.scene.removeEventListener('addSceneObject', this._addSceneObject)
  43. this.widgets.forEach(w => w.dispose && w.dispose())
  44. this.widgets = []
  45. this._widgetRoot.removeFromParent()
  46. this._widgetRoot.clear()
  47. super.onRemove(viewer)
  48. }
  49. private _addSceneObject = (e: any)=>{
  50. this._createWidgets(e.object)
  51. }
  52. refresh() {
  53. this._createWidgets(this._viewer?.scene.modelRoot)
  54. }
  55. widgets: (IWidget&Object3D)[] = []
  56. private _widgetDisposed = (e: IEvent<any>)=> this._unregisterWidget(e.target)
  57. private _registerWidget(w: IWidget&Object3D) {
  58. this.widgets.push(w)
  59. w.addEventListener('dispose', this._widgetDisposed) // todo: maybe unregister when removed from parent, dispose makes little sense.
  60. }
  61. private _unregisterWidget(w: IWidget&Object3D) {
  62. w.removeEventListener('dispose', this._widgetDisposed)
  63. const i = this.widgets.indexOf(w)
  64. if (i >= 0) this.widgets.splice(i, 1)
  65. }
  66. private _createWidgets(o?: Object3D) {
  67. o?.traverse((l: any) => {
  68. const widget = this.widgets.find(w => w.object === l)
  69. if (widget) {
  70. widget.update && widget.update()
  71. return
  72. }
  73. const helpers = this.helpers.filter(h => h.Check(l))
  74. helpers.forEach(h => {
  75. const w = h.Create(l)
  76. w.visible = !this.isDisabled()
  77. this._widgetRoot.add(w)
  78. this._registerWidget(w)
  79. })
  80. })
  81. }
  82. uiConfig: UiObjectConfig = {
  83. type: 'folder',
  84. label: 'Widgets',
  85. children: [
  86. {
  87. type: 'checkbox',
  88. label: 'Enabled',
  89. property: [this, 'enabled'],
  90. },
  91. {
  92. type: 'button',
  93. label: 'Refresh',
  94. value: ()=>this.refresh(),
  95. },
  96. ],
  97. }
  98. }