| @@ -156,6 +156,15 @@ export interface ICamera<TE extends ICameraEventMap = ICameraEventMap> extends C | |||
| * @param eventOptions | |||
| */ | |||
| setViewToMain(eventOptions: Pick<ICameraEventMap['setView'], 'ui'>): void | |||
| /** | |||
| * Set the canvas which is used as dom element in controls, etc. | |||
| * This is done by the viewer/scene when main camera is changed | |||
| * @param canvas | |||
| * @param refresh | |||
| */ | |||
| setCanvas(canvas: HTMLCanvasElement|undefined, refresh?: boolean): void; // todo make optional | |||
| // region inherited type fixes | |||
| // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | |||
| @@ -100,7 +100,7 @@ export interface IObject3DEventMap extends Object3DEventMap{ | |||
| camera?: ICamera | null | |||
| bubbleToParent: boolean | |||
| // object: IObject3D | |||
| // todo | |||
| } | |||
| cameraUpdate: { | |||
| ui?: boolean | |||
| @@ -8,6 +8,7 @@ export interface ICameraControlsEventMap { | |||
| export interface ICameraControls<TE extends ICameraControlsEventMap = ICameraControlsEventMap> extends IUiConfigContainer<void, 'panel'>, EventDispatcher<TE> { | |||
| object: Object3D | |||
| enabled: boolean | |||
| domElement?: HTMLElement | Document; | |||
| dispose(): void | |||
| @@ -330,7 +330,10 @@ export class OrthographicCamera2<TE extends ICameraEventMap = ICameraEventMap> e | |||
| refreshCameraControls(setDirty = true): void { | |||
| if (!this._controlsCtors) return // class not initialized | |||
| if (this._controls) { | |||
| if (this._currentControlsMode !== this.controlsMode || this !== this._controls.object) { // in-case camera changed or mode changed | |||
| if (this._currentControlsMode !== this.controlsMode || | |||
| this !== this._controls.object || | |||
| this._controls.domElement && this._canvas !== this._controls.domElement | |||
| ) { // in-case camera changed or mode changed | |||
| this._disposeCameraControls() | |||
| this._initCameraControls() | |||
| } | |||
| @@ -450,6 +453,13 @@ export class OrthographicCamera2<TE extends ICameraEventMap = ICameraEventMap> e | |||
| // iObjectCommons.dispose and dispatch event dispose is called automatically because of updateObject3d | |||
| } | |||
| setCanvas(canvas: HTMLCanvasElement|undefined, refresh = true) { | |||
| this._canvas = canvas | |||
| if (!refresh) return | |||
| this.refreshCameraControls() | |||
| this.refreshAspect(false) | |||
| } | |||
| // endregion | |||
| // region ui | |||
| @@ -312,7 +312,10 @@ export class PerspectiveCamera2<TE extends ICameraEventMap = ICameraEventMap> ex | |||
| refreshCameraControls(setDirty = true): void { | |||
| if (!this._controlsCtors) return // class not initialized | |||
| if (this._controls) { | |||
| if (this._currentControlsMode !== this.controlsMode || this !== this._controls.object) { // in-case camera changed or mode changed | |||
| if (this._currentControlsMode !== this.controlsMode || | |||
| this !== this._controls.object || | |||
| this._controls.domElement && this._canvas !== this._controls.domElement | |||
| ) { // in-case camera changed or mode changed | |||
| this._disposeCameraControls() | |||
| this._initCameraControls() | |||
| } | |||
| @@ -458,6 +461,13 @@ export class PerspectiveCamera2<TE extends ICameraEventMap = ICameraEventMap> ex | |||
| // iObjectCommons.dispose and dispatch event dispose is called automatically because of updateObject3d | |||
| } | |||
| setCanvas(canvas: HTMLCanvasElement|undefined, refresh = true) { | |||
| this._canvas = canvas | |||
| if (!refresh) return | |||
| this.refreshCameraControls() | |||
| this.refreshAspect(false) | |||
| } | |||
| // endregion | |||
| // region ui | |||
| @@ -18,7 +18,7 @@ export const iCameraCommons = { | |||
| this.dispatchEvent({...options, type: 'cameraUpdate', bubbleToParent: true}) // this sets dirty in the viewer | |||
| iObjectCommons.setDirty.call(this, {refreshScene: false, ...options}) | |||
| }, | |||
| activateMain: function(this: ICamera, options: Omit<ICameraEventMap['activateMain'], 'bubbleToParent'> = {}, _internal = false, _refresh = true): void { | |||
| activateMain: function(this: ICamera, options: Omit<ICameraEventMap['activateMain'], 'bubbleToParent'> = {}, _internal = false, _refresh = true, canvas?: HTMLCanvasElement): void { | |||
| if (!_internal) { | |||
| if (options.camera === null) return this.deactivateMain(options, _internal, _refresh) | |||
| return this.dispatchEvent({ | |||
| @@ -31,13 +31,15 @@ export const iCameraCommons = { | |||
| this.userData.__isMainCamera = true | |||
| this.userData.__lastScale = this.scale.clone() | |||
| this.scale.divide(this.getWorldScale(new Vector3())) // make unit scale, for near far and all | |||
| if (_refresh) { | |||
| if (canvas) this.setCanvas(canvas, _refresh) | |||
| else if (_refresh) { | |||
| this.refreshCameraControls(false) | |||
| this.refreshAspect(false) | |||
| } | |||
| this.setDirty({change: 'activateMain', ...options}) | |||
| // console.log({...this._camera.modelObject.position}) | |||
| }, | |||
| deactivateMain: function(this: ICamera, options: Omit<ICameraEventMap['activateMain'], 'bubbleToParent'> = {}, _internal = false, _refresh = true): void { | |||
| deactivateMain: function(this: ICamera, options: Omit<ICameraEventMap['activateMain'], 'bubbleToParent'> = {}, _internal = false, _refresh = true, clearCanvas = false): void { | |||
| if (!_internal) return this.dispatchEvent({ | |||
| type: 'activateMain', ...options, | |||
| camera: null, | |||
| @@ -49,6 +51,8 @@ export const iCameraCommons = { | |||
| this.scale.copy(this.userData.__lastScale) | |||
| delete this.userData.__lastScale | |||
| } | |||
| if (clearCanvas) this.setCanvas(undefined, _refresh) | |||
| else if (_refresh) this.refreshCameraControls(false) | |||
| if (_refresh) { | |||
| this.refreshCameraControls(false) | |||
| } | |||
| @@ -234,4 +234,10 @@ export {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter' | |||
| export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' | |||
| export * from 'three/examples/jsm/utils/BufferGeometryUtils.js' | |||
| export type {Event, EventListener} from 'three' | |||
| export type {Event, EventListener} from 'three' | |||
| import type {Event} from 'three' | |||
| // todo export from three | |||
| export type EventListener2<TEventType extends string, TEventMap extends Record<TEventType, any>, TTarget> = ( | |||
| event: TEventMap[TEventType] & Event<TEventType, TTarget>, | |||
| ) => void | |||
| @@ -19,6 +19,7 @@ import { | |||
| IMaterial, | |||
| IObject3D, | |||
| IObjectProcessor, | |||
| ISceneEventMap, | |||
| ITexture, | |||
| OrthographicCamera2, | |||
| PerspectiveCamera2, | |||
| @@ -68,6 +69,7 @@ import {DropzonePlugin, DropzonePluginOptions} from '../plugins/interaction/Drop | |||
| import {TonemapPlugin} from '../plugins/postprocessing/TonemapPlugin' | |||
| import {VERSION} from './version' | |||
| import {OrbitControls3} from '../three' | |||
| import {EventListener2} from '../three/Threejs' | |||
| // todo make proper event map | |||
| export interface IViewerEvent extends BaseEvent, Partial<IAnimationLoopEvent> { | |||
| @@ -1266,7 +1268,7 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView | |||
| await tr.exportFile(blob, name) | |||
| } | |||
| private _setActiveCameraView(event: any = {}): void { | |||
| private _setActiveCameraView: EventListener2<'setView'|'activateMain', ISceneEventMap, RootScene> = (event) => { | |||
| if (event.type === 'setView') { | |||
| if (!event.camera) { | |||
| this.console.warn('Cannot find camera', event) | |||
| @@ -1274,8 +1276,12 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView | |||
| } | |||
| const camera = this._scene.mainCamera | |||
| camera.setViewFromCamera(event.camera) // default is worldSpace | |||
| } else if (event.type === 'activateMain') | |||
| } else if (event.type === 'activateMain') { | |||
| event.camera?.setCanvas(this._canvas, false) | |||
| // this._scene.mainCamera.setCanvas(undefined, false) // todo is this required? | |||
| this._scene.mainCamera = event.camera || undefined // event.camera should have been upgraded when added to the scene. | |||
| } | |||
| this._scene.mainCamera = event.camera || undefined // event.camera should have been upgraded when added to the scene. | |||
| } | |||
| private _resolvePluginOrClass<T extends IViewerPlugin>(plugin: T | Class<T>, ...args: ConstructorParameters<Class<T>>): T|undefined { | |||