threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TransformAnimationPlugin.ts 7.3KB

пре 1 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {Quaternion, Vector3} from 'three'
  2. import {AViewerPluginSync, ThreeViewer} from '../../viewer'
  3. import {UiObjectConfig} from 'uiconfig.js'
  4. import {PopmotionPlugin} from './PopmotionPlugin'
  5. import {IObject3D} from '../../core'
  6. // todo make a serializable object like CameraView for proper ui state management
  7. export interface TSavedTransform {
  8. position: Vector3
  9. quaternion: Quaternion
  10. scale: Vector3
  11. name?: string
  12. }
  13. /**
  14. * Transform Animation Plugin
  15. *
  16. * Helper plugin to save, load and animate between different transforms(position, rotation, scale) on objects.
  17. * Also adds a UI to add and animate transforms on objects.
  18. * Requires the PopmotionPlugin to animate.
  19. *
  20. * @category Plugins
  21. */
  22. export class TransformAnimationPlugin extends AViewerPluginSync<''> {
  23. public static readonly PluginType = 'TransformAnimationPlugin'
  24. toJSON: any = undefined
  25. enabled = true
  26. dependencies = [PopmotionPlugin]
  27. constructor() {
  28. super()
  29. }
  30. onAdded(viewer: ThreeViewer): void {
  31. super.onAdded(viewer)
  32. viewer.scene.addEventListener('addSceneObject', this._addSceneObject)
  33. }
  34. onRemove(viewer: ThreeViewer): void {
  35. viewer.scene.removeEventListener('addSceneObject', this._addSceneObject)
  36. return super.onRemove(viewer)
  37. }
  38. private _addSceneObject = (e: any)=>{
  39. const object = e.object as IObject3D
  40. object?.traverse && object.traverse((o: IObject3D)=>{
  41. if (!o.userData[TransformAnimationPlugin.PluginType]) {
  42. o.userData[TransformAnimationPlugin.PluginType] = {
  43. transforms: [] as TSavedTransform[],
  44. }
  45. }
  46. // if (!o.userData[TransformAnimationPlugin.PluginType].transforms) {
  47. // o.userData[TransformAnimationPlugin.PluginType].transforms = []
  48. // }
  49. // for old files, todo remove later
  50. o.userData[TransformAnimationPlugin.PluginType]!.transforms?.forEach((t, i)=>{
  51. if (t.name === undefined) t.name = 'Transform ' + i
  52. })
  53. const uiConfig: UiObjectConfig = {
  54. type: 'folder',
  55. label: 'Transform Animation',
  56. children: [
  57. {
  58. type: 'button',
  59. label: 'Add Current Transform',
  60. value: ()=>{
  61. this.addTransform(o)
  62. uiConfig?.uiRefresh?.()
  63. },
  64. },
  65. ()=>o.userData[TransformAnimationPlugin.PluginType]?.transforms.map((t: TSavedTransform, i: number)=>({
  66. type: 'folder',
  67. label: t.name || `Transform ${i}`,
  68. children: [
  69. {
  70. type: 'input',
  71. label: 'Name',
  72. property: [t, 'name'],
  73. },
  74. {
  75. type: 'vec3',
  76. label: 'Position',
  77. property: [t, 'position'],
  78. },
  79. {
  80. type: 'vec3',
  81. label: 'Quaternion',
  82. property: [t, 'quaternion'],
  83. },
  84. {
  85. type: 'vec3',
  86. label: 'Scale',
  87. property: [t, 'scale'],
  88. },
  89. {
  90. type: 'button',
  91. label: 'Set',
  92. value: ()=>{
  93. this.setTransform(o, t)
  94. },
  95. },
  96. {
  97. type: 'button',
  98. label: 'Animate',
  99. value: ()=>{
  100. this.animateTransform(o, t)
  101. },
  102. }],
  103. })),
  104. ],
  105. }
  106. o.uiConfig?.children?.push(uiConfig) // todo check if already exists
  107. })
  108. }
  109. addTransform(o: IObject3D, name?: string) {
  110. if (!o.userData[TransformAnimationPlugin.PluginType]) {
  111. o.userData[TransformAnimationPlugin.PluginType] = {
  112. transforms: [] as TSavedTransform[],
  113. }
  114. }
  115. const transform = {
  116. name: name || 'Transform ' + (o.userData[TransformAnimationPlugin.PluginType]!.transforms.length + 1),
  117. position: o.position.clone(),
  118. quaternion: o.quaternion.clone(),
  119. scale: o.scale.clone(),
  120. }
  121. o.userData[TransformAnimationPlugin.PluginType]!.transforms.push(transform)
  122. return transform
  123. }
  124. setTransform(o: IObject3D, tr: TSavedTransform|number|string) {
  125. const t = this.getSavedTransform(tr, o)
  126. if (!t) return
  127. o.position.copy(t.position)
  128. o.quaternion.copy(t.quaternion)
  129. o.scale.copy(t.scale)
  130. o.setDirty?.()
  131. o.uiConfig?.uiRefresh?.()
  132. }
  133. getSavedTransform(tr: TSavedTransform | number | string, o: IObject3D) {
  134. return typeof tr === 'number' ?
  135. o.userData[TransformAnimationPlugin.PluginType]?.transforms[tr] :
  136. typeof tr === 'string' ?
  137. o.userData[TransformAnimationPlugin.PluginType]?.transforms.find(t1 => t1.name === tr) :
  138. tr
  139. }
  140. animateTransform(o: IObject3D, tr: TSavedTransform|number|string, duration = 2000) {
  141. const popmotion = this._viewer?.getPlugin(PopmotionPlugin)
  142. if (!popmotion) {
  143. this._viewer?.console.error('PopmotionPlugin required for animation')
  144. }
  145. const t = this.getSavedTransform(tr, o)
  146. if (!t) return
  147. // todo stop all existing animations(for the current model) like CameraView?
  148. const pos = new Vector3()
  149. const q = new Quaternion()
  150. const s = new Vector3()
  151. const op = o.position.clone()
  152. const oq = o.quaternion.clone()
  153. const os = o.scale.clone()
  154. const ep = t.position
  155. const eq = t.quaternion
  156. const es = t.scale
  157. return popmotion?.animate({
  158. from: 0,
  159. to: 1,
  160. duration: duration,
  161. onUpdate: (v: number) => {
  162. pos.lerpVectors(op, ep, v)
  163. q.slerpQuaternions(oq, eq, v)
  164. s.lerpVectors(os, es, v)
  165. o.position.copy(pos)
  166. o.quaternion.copy(q)
  167. o.scale.copy(s)
  168. this._viewer?.setDirty()
  169. this._viewer?.renderManager.resetShadows()
  170. // o.setDirty?.()
  171. // o.uiConfig?.uiRefresh?.()
  172. },
  173. onStop: () => {
  174. o.position.copy(t.position)
  175. o.quaternion.copy(t.quaternion)
  176. o.scale.copy(t.scale)
  177. o.setDirty?.()
  178. o.uiConfig?.uiRefresh?.()
  179. },
  180. })
  181. }
  182. }
  183. declare module '../../core/IObject' {
  184. interface IObject3DUserData {
  185. [TransformAnimationPlugin.PluginType]?: {
  186. transforms: TSavedTransform[]
  187. }
  188. }
  189. }