UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

175 lines 7.06 kB
import { Quaternion, Vector3 } from 'three'; import { AViewerPluginSync } from '../../viewer'; import { PopmotionPlugin } from './PopmotionPlugin'; /** * Transform Animation Plugin * * Helper plugin to save, load and animate between different transforms(position, rotation, scale) on objects. * Also adds a UI to add and animate transforms on objects. * Requires the PopmotionPlugin to animate. * * @category Plugins */ export class TransformAnimationPlugin extends AViewerPluginSync { constructor() { super(); this.toJSON = undefined; this.enabled = true; this.dependencies = [PopmotionPlugin]; this._addSceneObject = (e) => { const object = e.object; object?.traverse && !object.isWidget && object.traverse((o) => { if (o.isWidget) return; // if (!o.userData[TransformAnimationPlugin.PluginType].transforms) { // o.userData[TransformAnimationPlugin.PluginType].transforms = [] // } // for old files, todo remove later o.userData[TransformAnimationPlugin.PluginType]?.transforms?.forEach((t, i) => { if (t.name === undefined) t.name = 'Transform ' + i; }); const uiConfig = { type: 'folder', label: 'Transform Animation', children: [ { type: 'button', label: 'Add Current Transform', value: () => { this.addTransform(o); uiConfig?.uiRefresh?.(); }, }, () => o.userData[TransformAnimationPlugin.PluginType]?.transforms.map((t, i) => ({ type: 'folder', label: t.name || `Transform ${i}`, children: [ { type: 'input', label: 'Name', property: [t, 'name'], }, { type: 'vec3', label: 'Position', property: [t, 'position'], }, { type: 'vec3', label: 'Quaternion', property: [t, 'quaternion'], }, { type: 'vec3', label: 'Scale', property: [t, 'scale'], }, { type: 'button', label: 'Set', value: () => { this.setTransform(o, t); }, }, { type: 'button', label: 'Animate', value: () => { this.animateTransform(o, t); }, } ], })), ], }; o.uiConfig?.children?.push(uiConfig); // todo check if already exists }); }; } onAdded(viewer) { super.onAdded(viewer); viewer.scene.addEventListener('addSceneObject', this._addSceneObject); } onRemove(viewer) { viewer.scene.removeEventListener('addSceneObject', this._addSceneObject); return super.onRemove(viewer); } addTransform(o, name) { if (!o.userData[TransformAnimationPlugin.PluginType]) { o.userData[TransformAnimationPlugin.PluginType] = { transforms: [], }; } const transform = { name: name || 'Transform ' + (o.userData[TransformAnimationPlugin.PluginType].transforms.length + 1), position: o.position.clone(), quaternion: o.quaternion.clone(), scale: o.scale.clone(), }; o.userData[TransformAnimationPlugin.PluginType].transforms.push(transform); return transform; } setTransform(o, tr) { const t = this.getSavedTransform(tr, o); if (!t) return; o.position.copy(t.position); o.quaternion.copy(t.quaternion); o.scale.copy(t.scale); o.setDirty?.(); o.uiConfig?.uiRefresh?.(); } getSavedTransform(tr, o) { return typeof tr === 'number' ? o.userData[TransformAnimationPlugin.PluginType]?.transforms[tr] : typeof tr === 'string' ? o.userData[TransformAnimationPlugin.PluginType]?.transforms.find(t1 => t1.name === tr) : tr; } animateTransform(o, tr, duration = 2000) { const popmotion = this._viewer?.getPlugin(PopmotionPlugin); if (!popmotion) { this._viewer?.console.error('PopmotionPlugin required for animation'); } const t = this.getSavedTransform(tr, o); if (!t) return; // todo stop all existing animations(for the current model) like CameraView? const pos = new Vector3(); const q = new Quaternion(); const s = new Vector3(); const op = o.position.clone(); const oq = o.quaternion.clone(); const os = o.scale.clone(); const ep = t.position; const eq = t.quaternion; const es = t.scale; return popmotion?.animate({ from: 0, to: 1, duration: duration, onUpdate: (v) => { pos.lerpVectors(op, ep, v); q.slerpQuaternions(oq, eq, v); s.lerpVectors(os, es, v); o.position.copy(pos); o.quaternion.copy(q); o.scale.copy(s); this._viewer?.setDirty(); this._viewer?.renderManager.resetShadows(); // o.setDirty?.() // o.uiConfig?.uiRefresh?.() }, onStop: () => { o.position.copy(t.position); o.quaternion.copy(t.quaternion); o.scale.copy(t.scale); o.setDirty?.(); o.uiConfig?.uiRefresh?.(); }, }); } } TransformAnimationPlugin.PluginType = 'TransformAnimationPlugin'; //# sourceMappingURL=TransformAnimationPlugin.js.map