| @@ -1,4 +1,5 @@ | |||
| dist | |||
| lib | |||
| docs | |||
| /index.html | |||
| examples/**/*.js | |||
| @@ -19,7 +19,7 @@ function replaceImports(code) { | |||
| .replaceAll(` from '../`, ` from '${rootPath+examplePath}`) | |||
| } | |||
| setupCodePreview( | |||
| document.getElementById('canvas-container') || document.querySelector('.code-preview-container') || document.body, | |||
| document.querySelector('.code-preview-container') || document.getElementById('canvas-container') || document.body, | |||
| scripts, | |||
| scripts.map(s=>s.textContent ? 'js' : s.split('.').pop()), // title | |||
| scripts.map(s=>(typeof s === 'string' && s.endsWith('.js')) ? s : (codePath+examplePath+window.location.pathname.split('/examples/').pop().replace('index.html', '')+(s.textContent ? 'index.html' : s))), // todo: github link | |||
| @@ -240,6 +240,7 @@ | |||
| <li><a href="./camera-view-plugin/">Camera View (Animation) Plugin </a></li> | |||
| <li><a href="./dropzone-plugin/">Dropzone (Drag & Drop) Plugin </a></li> | |||
| <li><a href="./fullscreen-plugin/">FullScreen Plugin </a></li> | |||
| <li><a href="./geometry-generator-plugin/">Geometry Generator Plugin </a></li> | |||
| </ul> | |||
| <h2 class="category">Import</h2> | |||
| <ul> | |||
| @@ -92,21 +92,21 @@ | |||
| "rollup-plugin-license": "^3.0.1", | |||
| "rollup-plugin-glsl": "^1.3.0", | |||
| "rollup-plugin-postcss": "^4.0.2", | |||
| "stats.js": "^0.17.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2018/package.tgz", | |||
| "tslib": "^2.5.0", | |||
| "typedoc": "^0.24.7", | |||
| "typescript": "^5.0.4", | |||
| "typescript-plugin-css-modules": "^5.0.1", | |||
| "uiconfig.js": "^0.0.9", | |||
| "@rollup/plugin-replace": "^5.0.2", | |||
| "popmotion": "^11.0.5" | |||
| }, | |||
| "dependencies": { | |||
| "stats.js": "^0.17.0", | |||
| "uiconfig.js": "^0.0.9", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1018/package.tgz", | |||
| "@types/webxr": "^0.5.1", | |||
| "@types/wicg-file-system-access": "^2020.9.5", | |||
| "ts-browser-helpers": "^0.8.0" | |||
| "ts-browser-helpers": "^0.9.0" | |||
| }, | |||
| "//": { | |||
| "dependencies": { | |||
| @@ -52,7 +52,7 @@ export interface AssetManagerOptions{ | |||
| storage?: Cache | Storage | boolean | |||
| } | |||
| export type AddAssetOptions = AddObjectOptions & { | |||
| export interface AddAssetOptions extends AddObjectOptions{ | |||
| /** | |||
| * Automatically set any loaded HDR, EXR file as the scene environment map | |||
| * @default true | |||
| @@ -1,5 +1,5 @@ | |||
| import {BaseEvent, EventDispatcher, LoadingManager, Object3D} from 'three' | |||
| import {AnyOptions, IDisposable} from 'ts-browser-helpers' | |||
| import {IDisposable} from 'ts-browser-helpers' | |||
| import {IAsset, IFile} from './IAsset' | |||
| import {ILoader} from './IImporter' | |||
| import {ICamera, IMaterial, IObject3D, ITexture} from '../core' | |||
| @@ -59,7 +59,7 @@ export interface IImportResultUserData{ | |||
| __sourceBlob?: IFile | |||
| } | |||
| export type ProcessRawOptions = { | |||
| export interface ProcessRawOptions { | |||
| /** | |||
| * default = true, toggle to control the processing of the raw objects in the proecssRaw method | |||
| */ | |||
| @@ -94,7 +94,9 @@ export type ProcessRawOptions = { | |||
| * @deprecated use processRaw instead | |||
| */ | |||
| processImported?: boolean, // same as processRaw | |||
| } & AnyOptions | |||
| [key: string]: any | |||
| } | |||
| export interface LoadFileOptions { | |||
| fileHandler?: any, // custom {@link ILoader} for the file | |||
| @@ -105,9 +107,9 @@ export interface LoadFileOptions { | |||
| rootPath?: string, // internal use | |||
| } | |||
| export type ImportFilesOptions = ProcessRawOptions & LoadFileOptions & {allowedExtensions?: string[]} | |||
| export interface ImportFilesOptions extends ProcessRawOptions, LoadFileOptions {allowedExtensions?: string[]} | |||
| export type ImportAssetOptions = { | |||
| export interface ImportAssetOptions extends ProcessRawOptions, LoadFileOptions { | |||
| /** | |||
| * Default = false. If true, the asset will be imported again on subsequent calls, even if it is already imported. | |||
| */ | |||
| @@ -129,7 +131,7 @@ export type ImportAssetOptions = { | |||
| * Pass a custom file to use for the import. This will be used in the importer, and nothing will be fetched from the path | |||
| */ | |||
| importedFile?: IFile, | |||
| } & ProcessRawOptions & LoadFileOptions & AnyOptions | |||
| } | |||
| export type IAssetImporterEventTypes = 'onLoad' | 'onProgress' | 'onStop' | 'onError' | 'onStart' | 'loaderCreate' | 'importFile' | 'importFiles' | 'processRaw' | 'processRawStart' | |||
| export interface IAssetImporter extends EventDispatcher<BaseEvent, IAssetImporterEventTypes>, IDisposable { | |||
| @@ -54,13 +54,13 @@ export interface ICamera<E extends ICameraEvent = ICameraEvent, ET extends ICame | |||
| */ | |||
| position: Vector3, | |||
| // todo: make disable/enable functions with key like in FrameFadePlugin | |||
| interactionsEnabled: boolean; | |||
| readonly interactionsEnabled: boolean; | |||
| setInteractions(enabled: boolean, by: string): void; | |||
| /** | |||
| * Check whether user can interact with this camera. | |||
| * Interactions can be enabled/disabled in a variety of ways, | |||
| * like {@link interactionsEnabled}, {@link controlsMode}, {@link isMainCamera} property | |||
| * like {@link setInteractions}, {@link controlsMode}, {@link isMainCamera} property | |||
| */ | |||
| readonly canUserInteract: boolean; | |||
| @@ -1,4 +1,4 @@ | |||
| import {BufferGeometry, Event, NormalBufferAttributes, NormalOrGLBufferAttributes} from 'three' | |||
| import {BufferGeometry, Event, NormalBufferAttributes, NormalOrGLBufferAttributes, Vector3} from 'three' | |||
| import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js' | |||
| import {AnyOptions} from 'ts-browser-helpers' | |||
| import {IObject3D} from './IObject' | |||
| @@ -18,6 +18,7 @@ export interface IGeometry<Attributes extends NormalOrGLBufferAttributes = Norma | |||
| refreshUi(): void; | |||
| uiConfig?: UiObjectConfig | |||
| appliedMeshes: Set<IObject3D> | |||
| center(offset?: Vector3, keepWorldPosition?: boolean): this | |||
| // Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation | |||
| userData: IGeometryUserData | |||
| @@ -78,6 +78,8 @@ export interface IObject3DUserData extends IImportResultUserData { | |||
| autoScaleRadius?: number | |||
| autoScaled?: boolean | |||
| geometriesCentered?: boolean | |||
| /** | |||
| * should this object be taken into account when calculating bounding box, default true | |||
| @@ -239,7 +241,6 @@ export interface IObject3D<E extends Event = IObject3DEvent, ET = IObject3DEvent | |||
| parent: IObject3D | null | |||
| children: IObject3D[] | |||
| // endregion | |||
| } | |||
| @@ -16,6 +16,17 @@ export interface AddObjectOptions { | |||
| * @default false | |||
| */ | |||
| autoCenter?: boolean, | |||
| /** | |||
| * Automatically center the geometries(pivots) in the object hierarchy before adding. | |||
| * @default false | |||
| */ | |||
| centerGeometries?: boolean, | |||
| /** | |||
| * This centers the geometry while keeping the world position, i.e the mesh(Object3D) positions will change. | |||
| * {@link centerGeometries} must be true for this to work. | |||
| * @default true | |||
| */ | |||
| centerGeometriesKeepPosition?: boolean, | |||
| /** | |||
| * Add a license to the object | |||
| */ | |||
| @@ -27,6 +38,7 @@ export interface AddObjectOptions { | |||
| autoScale?: boolean | |||
| /** | |||
| * Radius to use for {@link autoScale} | |||
| * {@link autoScale} must be true for this to work. | |||
| * @default 2 | |||
| */ | |||
| autoScaleRadius?: number | |||
| @@ -124,7 +124,7 @@ export class PerspectiveCamera2 extends PerspectiveCamera implements ICamera { | |||
| // const ae = this._canvas.addEventListener | |||
| // todo: this breaks UI. | |||
| // todo: this breaks tweakpane UI. | |||
| // this._canvas.addEventListener = (type: string, listener: any, options1: any) => { // see https://github.com/mrdoob/three.js/pull/19782 | |||
| // ae(type, listener, type === 'wheel' && typeof options1 !== 'boolean' ? { | |||
| // ...typeof options1 === 'object' ? options1 : {}, | |||
| @@ -141,21 +141,38 @@ export class PerspectiveCamera2 extends PerspectiveCamera implements ICamera { | |||
| // @serialize('camOptions') //todo handle deserialization of this | |||
| // region interactionsEnabled | |||
| private _interactionsEnabled = true | |||
| get canUserInteract() { | |||
| return this._interactionsEnabled && this.isMainCamera && this.controlsMode !== '' | |||
| } | |||
| // private _interactionsEnabled = true | |||
| // | |||
| // get interactionsEnabled(): boolean { | |||
| // return this._interactionsEnabled | |||
| // } | |||
| // | |||
| // set interactionsEnabled(value: boolean) { | |||
| // if (this._interactionsEnabled !== value) { | |||
| // this._interactionsEnabled = value | |||
| // this.refreshCameraControls(true) | |||
| // } | |||
| // } | |||
| private _interactionsDisabledBy = new Set<string>() | |||
| get interactionsEnabled(): boolean { | |||
| return this._interactionsEnabled | |||
| return this._interactionsDisabledBy.size === 0 | |||
| } | |||
| set interactionsEnabled(value: boolean) { | |||
| if (this._interactionsEnabled !== value) { | |||
| this._interactionsEnabled = value | |||
| this.refreshCameraControls(true) | |||
| setInteractions(enabled: boolean, by: string): void { | |||
| const size = this._interactionsDisabledBy.size | |||
| if (enabled) { | |||
| this._interactionsDisabledBy.delete(by) | |||
| } else { | |||
| this._interactionsDisabledBy.add(by) | |||
| } | |||
| if (size !== this._interactionsDisabledBy.size) this.refreshCameraControls(true) | |||
| } | |||
| get canUserInteract() { | |||
| return this.interactionsEnabled && this.isMainCamera && this.controlsMode !== '' | |||
| } | |||
| // endregion | |||
| @@ -17,6 +17,24 @@ export const iGeometryCommons = { | |||
| superDispose.call(this) | |||
| }, | |||
| upgradeGeometry: upgradeGeometry, | |||
| center: (superCenter: BufferGeometry['center']): IGeometry['center'] => | |||
| function(this: IGeometry, offset?: Vector3, keepWorldPosition = false): IGeometry { | |||
| if (keepWorldPosition) { | |||
| offset = offset ? offset.clone() : new Vector3() | |||
| superCenter.call(this, offset) | |||
| offset.negate() | |||
| const meshes = this.appliedMeshes | |||
| for (const m of meshes) { | |||
| m.updateMatrix() | |||
| m.position.copy(offset).applyMatrix4(m.matrix) | |||
| m.setDirty() | |||
| } | |||
| } else { | |||
| superCenter.call(this, offset) | |||
| } | |||
| this.setDirty() | |||
| return this | |||
| }, | |||
| makeUiConfig: function(this: IGeometry): UiObjectConfig { | |||
| if (this.uiConfig) return this.uiConfig | |||
| return { | |||
| @@ -37,20 +55,13 @@ export const iGeometryCommons = { | |||
| label: 'Center Geometry', | |||
| value: () => { | |||
| this.center() | |||
| this.setDirty() | |||
| }, | |||
| }, | |||
| { | |||
| type: 'button', | |||
| label: 'Center Geometry (keep position)', | |||
| value: () => { | |||
| const offset = new Vector3() | |||
| this.center(offset) | |||
| const meshes = this.appliedMeshes | |||
| meshes.forEach(m=>{ | |||
| m.position.sub(offset) | |||
| m.setDirty && m.setDirty() | |||
| }) | |||
| this.center(undefined, true) | |||
| }, | |||
| }, | |||
| { | |||
| @@ -154,6 +165,7 @@ function upgradeGeometry(this: IGeometry) { | |||
| this.assetType = 'geometry' | |||
| this.dispose = iGeometryCommons.dispose(this.dispose) | |||
| this.center = iGeometryCommons.center(this.center) | |||
| if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty | |||
| if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi | |||
| @@ -185,7 +185,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| } | |||
| /** | |||
| Load model root scene exported to GLTF format. Used internally by {@link ThreeViewer.addSceneObject}. | |||
| * Load model root scene exported to GLTF format. Used internally by {@link ThreeViewer.addSceneObject}. | |||
| * @param obj | |||
| * @param options | |||
| */ | |||
| @@ -227,7 +227,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| .map(c=>this.addObject(c, {...options, clearSceneObjects: false, disposeSceneObjects: false})) | |||
| } | |||
| private _addObject3D(model: IObject3D|null, {autoCenter = false, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void { | |||
| private _addObject3D(model: IObject3D|null, {autoCenter = false, centerGeometries = false, centerGeometriesKeepPosition = true, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void { | |||
| const obj = model | |||
| if (!obj) { | |||
| console.error('Invalid object, cannot add to scene.') | |||
| @@ -247,6 +247,14 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| } else { | |||
| obj.userData.autoScaled = true // mark as auto-scaled, so that autoScale is not called again when file is reloaded. | |||
| } | |||
| if (centerGeometries && !obj.userData.geometriesCentered) { | |||
| obj.traverse((o)=>{ | |||
| if (o.geometry) o.geometry.center(undefined, centerGeometriesKeepPosition) | |||
| }) | |||
| obj.userData.geometriesCentered = true | |||
| } else { | |||
| obj.userData.geometriesCentered = true // mark as centered, so that geometry center is not called again when file is reloaded. | |||
| } | |||
| if (license) obj.userData.license = [obj.userData.license, license].filter(v=>v).join(', ') | |||
| @@ -388,6 +396,14 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| private _v1 = new Vector3() | |||
| private _v2 = new Vector3() | |||
| /** | |||
| * For Programmatically toggling autoNearFar. This property is not supposed to be in the UI or serialized. | |||
| * Use camera.userData.autoNearFar for UI and serialization | |||
| * This is used in PickingPlugin | |||
| * autoNearFar will still be disabled if this is true and camera.userData.autoNearFar is false | |||
| */ | |||
| autoNearFarEnabled = true | |||
| /** | |||
| * Refreshes the scene active camera near far values, based on the scene bounding box. | |||
| * This is called automatically every time the camera is updated. | |||
| @@ -395,7 +411,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| refreshActiveCameraNearFar(): void { | |||
| const camera = this.mainCamera as ICamera | |||
| if (!camera) return | |||
| if (camera.userData.autoNearFar === false) { | |||
| if (!this.autoNearFarEnabled || camera.userData.autoNearFar === false) { | |||
| camera.near = camera.userData.minNearPlane ?? 0.5 | |||
| camera.far = camera.userData.maxFarPlane ?? 1000 | |||
| return | |||
| @@ -88,23 +88,9 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC | |||
| onAdded(viewer: ThreeViewer): void { | |||
| super.onAdded(viewer) | |||
| let interactionsDisabled = false // we need this because interactionsEnabled is also set in PickingPlugin | |||
| // todo: move to PopmotionPlugin | |||
| // todo: remove event listener | |||
| viewer.addEventListener('preFrame', (_: any)=>{ | |||
| if (/* this.seekOnScroll || */ this._animating) { | |||
| if (this._viewer!.scene.mainCamera.interactionsEnabled) { | |||
| this._viewer!.scene.mainCamera.interactionsEnabled = false | |||
| interactionsDisabled = true | |||
| // console.log(interactionsDisabled) | |||
| } | |||
| } else if (interactionsDisabled) { | |||
| this._viewer!.scene.mainCamera.interactionsEnabled = true | |||
| interactionsDisabled = false | |||
| // console.log(interactionsDisabled) | |||
| } | |||
| // console.log(ev.deltaTime) | |||
| // this._updaters.forEach(u=>{ | |||
| @@ -263,6 +249,9 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC | |||
| this._currentView = view | |||
| this._animating = true | |||
| this._viewer?.scene.mainCamera.setInteractions(false, CameraViewPlugin.PluginType) // todo: also for seekOnScroll | |||
| this.dispatchEvent({type: 'startViewChange', view}) | |||
| const popmotion = this._viewer?.getPlugin(PopmotionPlugin) | |||
| @@ -279,6 +268,7 @@ export class CameraViewPlugin extends AViewerPluginSync<'viewChange'|'startViewC | |||
| if (throwOnStop) throw e | |||
| }) | |||
| this._viewer?.scene.mainCamera.setInteractions(true, CameraViewPlugin.PluginType) | |||
| this._animating = false | |||
| this._viewer?.setDirty() | |||
| @@ -1,6 +1,6 @@ | |||
| import {IPassID, IPipelinePass} from '../../postprocessing' | |||
| import {AViewerPluginSync, ISerializedConfig, ThreeViewer} from '../../viewer' | |||
| import {AnyFunction, serialize} from 'ts-browser-helpers' | |||
| import {serialize, wrapThisFunction} from 'ts-browser-helpers' | |||
| import {SerializationMetaType} from '../../utils' | |||
| import {uiConfig, uiToggle} from 'uiconfig.js' | |||
| @@ -71,10 +71,3 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend | |||
| } | |||
| } | |||
| function wrapThisFunction<T extends AnyFunction, T2>(f1: ()=>void, f2?: T): T { | |||
| return function(this: T2, ...args: Parameters<T>) { | |||
| f1() | |||
| return f2 && f2.call(this, ...args) | |||
| } as T | |||
| } | |||
| @@ -38,7 +38,7 @@ export class DropzonePlugin extends AViewerPluginSync<'drop'> { | |||
| @serialize() autoImport = true | |||
| /** | |||
| * Automatically add dropped and imported assets to the scene. | |||
| Works only if {@link autoImport} is true. | |||
| * Works only if {@link autoImport} is true. | |||
| */ | |||
| @uiToggle() @serialize() autoAdd = true | |||
| @@ -58,6 +58,8 @@ export class DropzonePlugin extends AViewerPluginSync<'drop'> { | |||
| importConfig: true, | |||
| autoScale: true, | |||
| autoScaleRadius: 2, | |||
| centerGeometries: false, // in the whole hierarchy | |||
| centerGeometriesKeepPosition: true, // this centers while keeping world position | |||
| license: '', | |||
| clearSceneObjects: false, | |||
| disposeSceneObjects: false, | |||
| @@ -54,4 +54,6 @@ export class OrbitControls3 extends OrbitControls implements IUiConfigContainer, | |||
| throttleUpdate = 60 // throttle to 60 updates per second (implemented in OrbitControls.js.update() method) | |||
| // todo add to three-ts-types | |||
| stopDamping!: () => void | |||
| } | |||
| @@ -22,7 +22,6 @@ export class SelectionWidget extends Group implements IWidget { | |||
| const scale = bbox.getBoundingSphere(new Sphere()).radius | |||
| this.scale.setScalar(scale * this.boundingScaleMultiplier) | |||
| this.setVisible(true) | |||
| } else { | |||
| this.setVisible(false) | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| export type {IEvent, IEventDispatcher} from 'ts-browser-helpers' | |||
| export type {ImageCanvasOptions} from 'ts-browser-helpers' | |||
| export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrArrOp} from 'ts-browser-helpers' | |||
| export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrFuncOp, ValOrArrOp} from 'ts-browser-helpers' | |||
| export type {Serializer} from 'ts-browser-helpers' | |||
| export {PointerDragHelper} from 'ts-browser-helpers' | |||
| export {Damper} from 'ts-browser-helpers' | |||
| @@ -11,13 +11,13 @@ export {escapeRegExp, getFilenameFromPath, parseFileExtension, replaceAll, toTit | |||
| export {prettyScrollbar} from 'ts-browser-helpers' | |||
| export {blobToDataURL, downloadBlob, downloadFile, uploadFile, mobileAndTabletCheck} from 'ts-browser-helpers' | |||
| export {LinearToSRGB, SRGBToLinear, colorToDataUrl} from 'ts-browser-helpers' | |||
| export {onChange, onChange2, onChange3, serialize, serializable} from 'ts-browser-helpers' | |||
| export {onChange, onChange2, onChange3, onChangeDispatchEvent, serialize, serializable} from 'ts-browser-helpers' | |||
| export {aesGcmDecrypt, aesGcmEncrypt} from 'ts-browser-helpers' | |||
| export {verifyPermission, writeFile, getFileHandle, getNewFileHandle, readFile} from 'ts-browser-helpers' | |||
| export {embedUrlRefs, htmlToCanvas, htmlToPng, htmlToSvg} from 'ts-browser-helpers' | |||
| export {imageToCanvas, imageBitmapToBase64, imageUrlToImageData, imageDataToCanvas, isWebpExportSupported, canvasFlipY} from 'ts-browser-helpers' | |||
| export {absMax, clearBit, updateBit} from 'ts-browser-helpers' | |||
| export {includesAll} from 'ts-browser-helpers' | |||
| export {includesAll, wrapThisFunction, findLastIndex} from 'ts-browser-helpers' | |||
| export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers' | |||
| export {deepAccessObject, getKeyByValue, objectHasOwn, objectMap2, objectMap} from 'ts-browser-helpers' | |||
| export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers' | |||
| @@ -58,7 +58,7 @@ export abstract class AViewerPlugin<T extends string = string, TViewer extends T | |||
| else this.fromJSON?.(state) | |||
| } | |||
| protected _viewerListeners: PartialRecord<IViewerEventTypes, (e: Event)=>void> = {} | |||
| protected _viewerListeners: PartialRecord<IViewerEventTypes, (e: IViewerEvent)=>void> = {} | |||
| protected _onViewerEvent = (e: IViewerEvent)=> { | |||
| const et = e.eType | |||
| et && this._viewerListeners[et]?.(e) | |||
| @@ -60,7 +60,7 @@ import {VERSION} from './version' | |||
| import {Easing} from 'popmotion' | |||
| import {OrbitControls3} from '../three' | |||
| export type IViewerEvent = BaseEvent & { | |||
| export interface IViewerEvent extends BaseEvent, Partial<IAnimationLoopEvent> { | |||
| type: '*'|'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled' | |||
| eType?: '*'|'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled' | |||
| [p: string]: any | |||