| "browser": "dist/index.js", | "browser": "dist/index.js", | ||||
| "exports": { | "exports": { | ||||
| ".": { | ".": { | ||||
| "import": "./dist/index.mjs", | |||||
| "types": "./dist/index.d.ts", | |||||
| "require": "./dist/index.js" | |||||
| "types": "./dist/index.d.ts", | |||||
| "import": "./dist/index.mjs", | |||||
| "require": "./dist/index.js" | |||||
| }, | }, | ||||
| "./dist/": { | "./dist/": { | ||||
| "import": "./dist/", | "import": "./dist/", | ||||
| "require": "./dist/" | "require": "./dist/" | ||||
| }, | }, | ||||
| "./lib": { | "./lib": { | ||||
| "import": "./lib/index.js", | |||||
| "types": "./lib/index.d.ts" | |||||
| "types": "./lib/index.d.ts", | |||||
| "import": "./lib/index.js" | |||||
| }, | }, | ||||
| "./lib/": { | "./lib/": { | ||||
| "import": "./lib/", | "import": "./lib/", |
| import {AViewerPluginEventMap, AViewerPluginSync, ThreeViewer} from '../../viewer' | import {AViewerPluginEventMap, AViewerPluginSync, ThreeViewer} from '../../viewer' | ||||
| import {absMax, now, onChange, onChange2, PointerDragHelper, serialize} from 'ts-browser-helpers' | import {absMax, now, onChange, onChange2, PointerDragHelper, serialize} from 'ts-browser-helpers' | ||||
| import {uiButton, uiDropdown, uiFolderContainer, uiMonitor, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js' | import {uiButton, uiDropdown, uiFolderContainer, uiMonitor, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js' | ||||
| import {AnimationAction, AnimationClip, AnimationMixer, LoopOnce, LoopRepeat} from 'three' | |||||
| import {AnimationAction, AnimationClip, AnimationMixer, EventListener2, LoopOnce, LoopRepeat, Scene} from 'three' | |||||
| import {ProgressivePlugin} from '../pipeline/ProgressivePlugin' | import {ProgressivePlugin} from '../pipeline/ProgressivePlugin' | ||||
| import {IObject3D} from '../../core' | |||||
| import {IObject3D, ISceneEventMap} from '../../core' | |||||
| import {generateUUID} from '../../three' | import {generateUUID} from '../../three' | ||||
| import type {FrameFadePlugin} from '../pipeline/FrameFadePlugin' | import type {FrameFadePlugin} from '../pipeline/FrameFadePlugin' | ||||
| } | } | ||||
| } | } | ||||
| protected _objectAdded = (ev: any)=>{ | |||||
| protected _objectAdded: EventListener2<'addSceneObject', ISceneEventMap, Scene> = (ev)=>{ | |||||
| const object = ev.object as IObject3D | const object = ev.object as IObject3D | ||||
| if (!this._viewer) return | if (!this._viewer) return | ||||
| let changed = false | let changed = false | ||||
| const isInRoot = ev.options?.addToRoot // for model stage etc | |||||
| object.traverse((obj)=>{ | object.traverse((obj)=>{ | ||||
| if (!this._viewer) return | if (!this._viewer) return | ||||
| object.userData.gltfAnim_SyncMaxDuration = true | object.userData.gltfAnim_SyncMaxDuration = true | ||||
| } // todo: check why do we need to do this? wont this create problems with looping or is it for that so that looping works in sync. | } // todo: check why do we need to do this? wont this create problems with looping or is it for that so that looping works in sync. | ||||
| const mixer = new AnimationMixer(this._viewer.scene.modelRoot) // add to modelRoot so it works with GLTF export... | |||||
| const mixer = new AnimationMixer(isInRoot ? this._viewer.scene : this._viewer.scene.modelRoot) // add to modelRoot so it works with GLTF export... | |||||
| const actions = clips.map(an=>mixer.clipAction(an).setLoop(this.loopAnimations ? LoopRepeat : LoopOnce, this.loopRepetitions)) | const actions = clips.map(an=>mixer.clipAction(an).setLoop(this.loopAnimations ? LoopRepeat : LoopOnce, this.loopRepetitions)) | ||||
| actions.forEach(ac=>ac.clampWhenFinished = true) | actions.forEach(ac=>ac.clampWhenFinished = true) | ||||
| this.animations.push({ | this.animations.push({ | ||||
| mixer, clips, actions, duration, | mixer, clips, actions, duration, | ||||
| }) | }) | ||||
| // todo remove on object dispose | |||||
| // todo remove on object dispose/remove | |||||
| changed = true | changed = true | ||||
| * @param options waitForProgressive: wait for progressive rendering to finish, default: true | * @param options waitForProgressive: wait for progressive rendering to finish, default: true | ||||
| */ | */ | ||||
| async getFile(filename?: string, options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {waitForProgressive: true}): Promise<File|undefined> { | async getFile(filename?: string, options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {waitForProgressive: true}): Promise<File|undefined> { | ||||
| options.getDataUrl = false | |||||
| return await this._getFile(filename || this.filename, options) as File | |||||
| return await this._getFile(filename || this.filename, {...options, getDataUrl: false}) as File | |||||
| } | } | ||||
| /** | /** | ||||
| * @param options waitForProgressive: wait for progressive rendering to finish, default: true | * @param options waitForProgressive: wait for progressive rendering to finish, default: true | ||||
| */ | */ | ||||
| async getDataUrl(options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {}): Promise<string> { | async getDataUrl(options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {}): Promise<string> { | ||||
| options.getDataUrl = true | |||||
| return await this._getFile('', options) as string ?? '' | |||||
| return await this._getFile('', {...options, getDataUrl: true}) as string ?? '' | |||||
| } | } | ||||
| private async _getFile(filename: string, options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {}): Promise<File|string|undefined> { | private async _getFile(filename: string, options: CanvasSnapshotOptions&{waitForProgressive?: boolean} = {}): Promise<File|string|undefined> { | ||||
| await this._viewer?.doOnce('postFrame') | |||||
| const viewer = this._viewer | const viewer = this._viewer | ||||
| const canvas = this._viewer?.canvas | const canvas = this._viewer?.canvas | ||||
| if (!viewer || !canvas) return undefined | if (!viewer || !canvas) return undefined |
| export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' | export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' | ||||
| export * from 'three/examples/jsm/utils/BufferGeometryUtils.js' | export * from 'three/examples/jsm/utils/BufferGeometryUtils.js' | ||||
| export type {Event, EventListener, EventListener2} from 'three' | export type {Event, EventListener, EventListener2} from 'three' | ||||
| export type {MeshPhysicalMaterialParameters, MeshBasicMaterialParameters, MaterialParameters} from 'three' |
| isType: (obj: any) => obj.isMaterial || obj.metadata?.type === 'Material', | isType: (obj: any) => obj.isMaterial || obj.metadata?.type === 'Material', | ||||
| serialize: (obj: any, meta?: SerializationMetaType) => { | serialize: (obj: any, meta?: SerializationMetaType) => { | ||||
| if (!obj?.isMaterial) throw new Error('Expected a material') | if (!obj?.isMaterial) throw new Error('Expected a material') | ||||
| if (meta?.materials[obj.uuid]) return {uuid: obj.uuid, resource: 'materials'} | |||||
| if (meta?.materials?.[obj.uuid]) return {uuid: obj.uuid, resource: 'materials'} | |||||
| if (obj.userData.rootPath) { | if (obj.userData.rootPath) { | ||||
| // todo | // todo | ||||
| // it works for textures because image(Source) are immutable | // it works for textures because image(Source) are immutable | ||||
| obj.userData = {} | obj.userData = {} | ||||
| let res = {} as any | let res = {} as any | ||||
| try { | try { | ||||
| res = obj.toJSON(meta, true) // copying userData is handled in toJSON, see MeshStandardMaterial2 | |||||
| res = obj.toJSON(meta || meta2, true) // copying userData is handled in toJSON, see MeshStandardMaterial2 | |||||
| serializeMaterialUserData(res, userData, meta) | serializeMaterialUserData(res, userData, meta) | ||||
| res.userData.uuid = userData.uuid | res.userData.uuid = userData.uuid | ||||
| // todo: override generator to mention that this is a custom serializer? | // todo: override generator to mention that this is a custom serializer? |