| change: object | change: object | ||||
| } | } | ||||
| export interface ICameraControls<TE extends ICameraControlsEventMap = ICameraControlsEventMap> extends IUiConfigContainer<void, 'panel'>, EventDispatcher<TE> { | |||||
| export interface ICameraControls<TE extends ICameraControlsEventMap = ICameraControlsEventMap> extends IUiConfigContainer<void, 'panel'|'folder'>, EventDispatcher<TE> { | |||||
| object: Object3D | object: Object3D | ||||
| enabled: boolean | enabled: boolean | ||||
| domElement?: HTMLElement | Document; | domElement?: HTMLElement | Document; |
| this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'visible'}) | this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'visible'}) | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | |||||
| type: 'button', | |||||
| label: 'Pick/Focus', // todo: move to the plugin that does the picking | |||||
| value: ()=>{ | |||||
| this.dispatchEvent({type: 'select', ui: true, object: this, bubbleToParent: true, focusCamera: true}) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| type: 'button', | |||||
| label: 'Pick Parent', // todo: move to the plugin that does the picking | |||||
| hidden: ()=>!this.parent, | |||||
| value: ()=>{ | |||||
| const parent = this.parent | |||||
| if (parent) { | |||||
| parent.dispatchEvent({type: 'select', ui: true, bubbleToParent: true, object: parent}) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| // moved to PickingPlugin | |||||
| // { | |||||
| // type: 'button', | |||||
| // label: 'Pick/Focus', // todo: move to the plugin that does the picking | |||||
| // value: ()=>{ | |||||
| // this.dispatchEvent({type: 'select', ui: true, object: this, bubbleToParent: true, focusCamera: true}) | |||||
| // }, | |||||
| // }, | |||||
| // { | |||||
| // type: 'button', | |||||
| // label: 'Pick Parent', // todo: move to the plugin that does the picking | |||||
| // hidden: ()=>!this.parent, | |||||
| // value: ()=>{ | |||||
| // const parent = this.parent | |||||
| // if (parent) { | |||||
| // parent.dispatchEvent({type: 'select', ui: true, bubbleToParent: true, object: parent}) | |||||
| // } | |||||
| // }, | |||||
| // }, | |||||
| { | { | ||||
| type: 'input', | type: 'input', | ||||
| label: 'Name', | label: 'Name', |
| // this.controls.update() // this should be done automatically postFrame | // this.controls.update() // this should be done automatically postFrame | ||||
| } | } | ||||
| // if (!this.controls || !this.controls.enabled) { | // if (!this.controls || !this.controls.enabled) { | ||||
| if (this.userData.autoLookAtTarget) { | |||||
| else if (this.userData.autoLookAtTarget) { | |||||
| this.lookAt(this.target) | this.lookAt(this.target) | ||||
| } | } | ||||
| // } | // } |
| import {AViewerPluginSync, ThreeViewer} from '../../viewer' | import {AViewerPluginSync, ThreeViewer} from '../../viewer' | ||||
| import {TControlsCtor} from '../../core' | |||||
| import {IScene, ISceneEventMap, TControlsCtor} from '../../core' | |||||
| import {Event2, EventListener2} from 'three' | |||||
| export abstract class ACameraControlsPlugin extends AViewerPluginSync { | export abstract class ACameraControlsPlugin extends AViewerPluginSync { | ||||
| readonly enabled = true | readonly enabled = true | ||||
| onAdded(viewer: ThreeViewer): void { | onAdded(viewer: ThreeViewer): void { | ||||
| super.onAdded(viewer) | super.onAdded(viewer) | ||||
| this._cameraChanged({camera: viewer.scene.mainCamera}) | |||||
| // @ts-expect-error hack | |||||
| this._cameraChanged({camera: viewer.scene.mainCamera, lastCamera: undefined}) | |||||
| viewer.scene.addEventListener('mainCameraChange', this._cameraChanged) | viewer.scene.addEventListener('mainCameraChange', this._cameraChanged) | ||||
| } | } | ||||
| onRemove(viewer: ThreeViewer): void { | onRemove(viewer: ThreeViewer): void { | ||||
| this._cameraChanged({lastCamera: viewer.scene.mainCamera}) | |||||
| // @ts-expect-error hack | |||||
| this._cameraChanged({lastCamera: viewer.scene.mainCamera, camera: undefined}) | |||||
| viewer.scene.removeEventListener('mainCameraChange', this._cameraChanged) | viewer.scene.removeEventListener('mainCameraChange', this._cameraChanged) | ||||
| super.onRemove(viewer) | super.onRemove(viewer) | ||||
| } | } | ||||
| private _cameraChanged = (e: any) => { | |||||
| private _cameraChanged: EventListener2<'mainCameraChange', ISceneEventMap, IScene> = (e: Partial<Event2<'mainCameraChange', ISceneEventMap, IScene>>) => { | |||||
| e.lastCamera?.removeControlsCtor?.(this.controlsKey) | e.lastCamera?.removeControlsCtor?.(this.controlsKey) | ||||
| e.camera?.setControlsCtor?.(this.controlsKey, this._controlsCtor) | e.camera?.setControlsCtor?.(this.controlsKey, this._controlsCtor) | ||||
| } | } |
| import {Class, onChange, serialize} from 'ts-browser-helpers' | import {Class, onChange, serialize} from 'ts-browser-helpers' | ||||
| import {AViewerPluginEventMap, AViewerPluginSync, ThreeViewer} from '../../viewer' | import {AViewerPluginEventMap, AViewerPluginSync, ThreeViewer} from '../../viewer' | ||||
| import {BoxSelectionWidget, ObjectPicker, SelectionWidget} from '../../three' | import {BoxSelectionWidget, ObjectPicker, SelectionWidget} from '../../three' | ||||
| import {IObject3D, IScene, ISceneEventMap} from '../../core' | |||||
| import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js' | |||||
| import {IMaterial, IObject3D, IScene, ISceneEventMap} from '../../core' | |||||
| import {UiObjectConfig} from 'uiconfig.js' | |||||
| import {FrameFadePlugin} from '../pipeline/FrameFadePlugin' | import {FrameFadePlugin} from '../pipeline/FrameFadePlugin' | ||||
| import {type UndoManagerPlugin} from './UndoManagerPlugin' | import {type UndoManagerPlugin} from './UndoManagerPlugin' | ||||
| import {ObjectPickerEventMap} from '../../three/utils/ObjectPicker' | import {ObjectPickerEventMap} from '../../three/utils/ObjectPicker' | ||||
| import {CameraViewPlugin} from '../animation/CameraViewPlugin' | |||||
| export interface PickingPluginEventMap extends AViewerPluginEventMap{ | export interface PickingPluginEventMap extends AViewerPluginEventMap{ | ||||
| selectedObjectChanged: {object: IObject3D|null} | selectedObjectChanged: {object: IObject3D|null} | ||||
| private _hoverWidget?: SelectionWidget | private _hoverWidget?: SelectionWidget | ||||
| private _pickUi: boolean | private _pickUi: boolean | ||||
| dependencies = [CameraViewPlugin] | |||||
| get hoverEnabled() { | get hoverEnabled() { | ||||
| return this._picker?.hoverEnabled ?? false | return this._picker?.hoverEnabled ?? false | ||||
| } | } | ||||
| this._viewer.scene.autoNearFarEnabled = !selected // for widgets etc, this can be removed when they are rendered in a separate pass | this._viewer.scene.autoNearFarEnabled = !selected // for widgets etc, this can be removed when they are rendered in a separate pass | ||||
| if (this._pickUi) { | if (this._pickUi) { | ||||
| const sUiConfig = (selected as IUiConfigContainer)?.uiConfig | |||||
| const ui = this.uiConfig | const ui = this.uiConfig | ||||
| ui.children = [...this._uiConfigChildren] | ui.children = [...this._uiConfigChildren] | ||||
| if (sUiConfig) ui.children.push(sUiConfig) | |||||
| if (selected) { | |||||
| ui.children.push( | |||||
| { | |||||
| type: 'button', | |||||
| label: 'Focus', | |||||
| value: ()=>{ | |||||
| // const selected = this.getSelectedObject() | |||||
| if (selected.assetType && selected.parentRoot) // todo also check if acceptChildEvents is set on some parent? | |||||
| selected.dispatchEvent({type: 'select', ui: true, object: selected, bubbleToParent: true, focusCamera: true}) | |||||
| else | |||||
| this.setSelectedObject(selected, true) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| type: 'button', | |||||
| label: 'Select Parent', | |||||
| hidden: ()=>!selected.parent, | |||||
| value: ()=>{ | |||||
| const parent = selected.parent | |||||
| if (parent) { | |||||
| if (parent.assetType && parent.parentRoot) // todo also check if acceptChildEvents is set on some parent? | |||||
| parent.dispatchEvent({type: 'select', ui: true, bubbleToParent: true, object: parent}) | |||||
| else | |||||
| this.setSelectedObject(parent, false) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| ) | |||||
| let c = selected.uiConfig | |||||
| if (c) ui.children.push(c) | |||||
| else { | |||||
| // check materials | |||||
| const mats = selected.materials ?? [selected.material as IMaterial] | |||||
| for (const m of mats) { | |||||
| c = m?.uiConfig | |||||
| if (c) ui.children.push(c) | |||||
| } | |||||
| } | |||||
| } | |||||
| ui.uiRefresh?.() | ui.uiRefresh?.() | ||||
| } | } | ||||
| @uiInput() @serialize() autoPushTarget = false | @uiInput() @serialize() autoPushTarget = false | ||||
| @uiInput() @serialize() autoPullTarget = false | @uiInput() @serialize() autoPullTarget = false | ||||
| @uiInput() @serialize() minDistance = 0.35 | @uiInput() @serialize() minDistance = 0.35 | ||||
| @uiInput() @serialize() maxDistance = 1000 | |||||
| @uiInput() @serialize() maxDistance = 1e6 // should be Infinity but this breaks the UI | |||||
| @uiInput() @serialize() minZoom = 0.01 | @uiInput() @serialize() minZoom = 0.01 | ||||
| @uiInput() @serialize() maxZoom = 1000 | |||||
| @uiInput() @serialize() maxZoom = 1e6 // should be Infinity but this breaks the UI | |||||
| @uiInput() @serialize() minPolarAngle = 0 | @uiInput() @serialize() minPolarAngle = 0 | ||||
| @uiInput() @serialize() maxPolarAngle = Math.PI | @uiInput() @serialize() maxPolarAngle = Math.PI |