viewerModel.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, AnimationGroup, Animatable, AbstractMesh, Tools, Scene, SceneLoader, Observable, SceneLoaderProgressEvent, Tags, ParticleSystem, Skeleton, IDisposable, Nullable, Animation } from "babylonjs";
  2. import { IModelConfiguration } from "../configuration/configuration";
  3. import { IModelAnimation, GroupModelAnimation, AnimationPlayMode } from "./modelAnimation";
  4. export class ViewerModel implements IDisposable {
  5. public loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
  6. private _animations: Array<IModelAnimation>;
  7. public meshes: Array<AbstractMesh>;
  8. public particleSystems: Array<ParticleSystem>;
  9. public skeletons: Array<Skeleton>;
  10. public currentAnimation: IModelAnimation;
  11. public onLoadedObservable: Observable<ViewerModel>;
  12. public onLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
  13. public onLoadErrorObservable: Observable<{ message: string; exception: any }>;
  14. constructor(private _modelConfiguration: IModelConfiguration, private _scene: Scene, disableAutoLoad = false) {
  15. this.onLoadedObservable = new Observable();
  16. this.onLoadErrorObservable = new Observable();
  17. this.onLoadProgressObservable = new Observable();
  18. this._animations = [];
  19. if (!disableAutoLoad) {
  20. this._initLoad();
  21. }
  22. }
  23. public load() {
  24. if (this.loader) {
  25. Tools.Error("Model was already loaded or in the process of loading.");
  26. } else {
  27. this._initLoad();
  28. }
  29. }
  30. //public getAnimations() {
  31. // return this._animations;
  32. //}
  33. public getAnimationNames() {
  34. return this._animations.map(a => a.name);
  35. }
  36. protected _getAnimationByName(name: string): Nullable<IModelAnimation> {
  37. // can't use .find, noe available on IE
  38. let filtered = this._animations.filter(a => a.name === name);
  39. // what the next line means - if two animations have the same name, they will not be returned!
  40. if (filtered.length === 1) {
  41. return filtered[0];
  42. } else {
  43. return null;
  44. }
  45. }
  46. public playAnimation(name: string): IModelAnimation {
  47. let animation = this._getAnimationByName(name);
  48. if (animation) {
  49. if (this.currentAnimation) {
  50. this.currentAnimation.stop();
  51. }
  52. this.currentAnimation = animation;
  53. animation.start();
  54. return animation;
  55. } else {
  56. throw new Error("aniamtion not found - " + name);
  57. }
  58. }
  59. private _initLoad() {
  60. if (!this._modelConfiguration || !this._modelConfiguration.url) {
  61. return Tools.Error("No model URL to load.");
  62. }
  63. let parts = this._modelConfiguration.url.split('/');
  64. let filename = parts.pop() || this._modelConfiguration.url;
  65. let base = parts.length ? parts.join('/') + '/' : './';
  66. let plugin = this._modelConfiguration.loader;
  67. //temp solution for animation group handling
  68. let animationsArray = this._scene.animationGroups.slice();
  69. this.loader = SceneLoader.ImportMesh(undefined, base, filename, this._scene, (meshes, particleSystems, skeletons) => {
  70. meshes.forEach(mesh => {
  71. Tags.AddTagsTo(mesh, "viewerMesh");
  72. });
  73. this.meshes = meshes;
  74. this.particleSystems = particleSystems;
  75. this.skeletons = skeletons;
  76. // check if this is a gltf loader and load the animations
  77. /*if (this.loader['_loader'] && this.loader['_loader']['_gltf'] && this.loader['_loader']['_gltf'].animations) {
  78. this.loader['_loader']['_gltf'].animations.forEach(animation => {
  79. this._animations.push(new GroupModelAnimation(animation._babylonAnimationGroup));
  80. });
  81. }*/
  82. if (this.loader.name === 'gltf') {
  83. this._scene.animationGroups.forEach(ag => {
  84. // add animations that didn't exist before
  85. if (animationsArray.indexOf(ag) === -1) {
  86. this._animations.push(new GroupModelAnimation(ag));
  87. }
  88. })
  89. } else {
  90. skeletons.forEach((skeleton, idx) => {
  91. let ag = new BABYLON.AnimationGroup("animation-" + idx, this._scene);
  92. skeleton.getAnimatables().forEach(a => {
  93. if (a.animations[0]) {
  94. ag.addTargetedAnimation(a.animations[0], a);
  95. }
  96. });
  97. this._animations.push(new GroupModelAnimation(ag));
  98. });
  99. }
  100. if (this._modelConfiguration.animation) {
  101. if (this._modelConfiguration.animation.playOnce) {
  102. this._animations.forEach(a => {
  103. a.playMode = AnimationPlayMode.ONCE;
  104. });
  105. }
  106. if (this._modelConfiguration.animation.autoStart && this._animations.length) {
  107. let animationName = this._modelConfiguration.animation.autoStart === true ?
  108. this._animations[0].name : this._modelConfiguration.animation.autoStart;
  109. this.playAnimation(animationName);
  110. }
  111. }
  112. this.onLoadedObservable.notifyObserversWithPromise(this);
  113. }, (progressEvent) => {
  114. this.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
  115. }, (e, m, exception) => {
  116. this.onLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception });
  117. }, plugin)!;
  118. this.loader['animationStartMode'] = 0;
  119. }
  120. public dispose() {
  121. this.particleSystems.forEach(ps => ps.dispose());
  122. this.skeletons.forEach(s => s.dispose());
  123. this._animations.forEach(ag => ag.dispose());
  124. this.meshes.forEach(m => m.dispose());
  125. }
  126. }