babylon.skeleton.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. var BABYLON;
  2. (function (BABYLON) {
  3. var Skeleton = (function () {
  4. function Skeleton(name, id, scene) {
  5. this.name = name;
  6. this.id = id;
  7. this.bones = new Array();
  8. this.needInitialSkinMatrix = false;
  9. this._isDirty = true;
  10. this._meshesWithPoseMatrix = new Array();
  11. this._identity = BABYLON.Matrix.Identity();
  12. this._ranges = {};
  13. this.bones = [];
  14. this._scene = scene;
  15. scene.skeletons.push(this);
  16. this.prepare();
  17. //make sure it will recalculate the matrix next time prepare is called.
  18. this._isDirty = true;
  19. }
  20. // Members
  21. Skeleton.prototype.getTransformMatrices = function (mesh) {
  22. if (this.needInitialSkinMatrix && mesh._bonesTransformMatrices) {
  23. return mesh._bonesTransformMatrices;
  24. }
  25. return this._transformMatrices;
  26. };
  27. Skeleton.prototype.getScene = function () {
  28. return this._scene;
  29. };
  30. // Methods
  31. Skeleton.prototype.createAnimationRange = function (name, from, to) {
  32. // check name not already in use
  33. if (!this._ranges[name]) {
  34. this._ranges[name] = new BABYLON.AnimationRange(name, from, to);
  35. for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
  36. if (this.bones[i].animations[0]) {
  37. this.bones[i].animations[0].createRange(name, from, to);
  38. }
  39. }
  40. }
  41. };
  42. Skeleton.prototype.deleteAnimationRange = function (name, deleteFrames) {
  43. if (deleteFrames === void 0) { deleteFrames = true; }
  44. for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
  45. if (this.bones[i].animations[0]) {
  46. this.bones[i].animations[0].deleteRange(name, deleteFrames);
  47. }
  48. }
  49. this._ranges[name] = undefined; // said much faster than 'delete this._range[name]'
  50. };
  51. Skeleton.prototype.getAnimationRange = function (name) {
  52. return this._ranges[name];
  53. };
  54. /**
  55. * note: This is not for a complete retargeting, only between very similar skeleton's with only possible bone length differences
  56. */
  57. Skeleton.prototype.copyAnimationRange = function (source, name, rescaleAsRequired) {
  58. if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
  59. if (this._ranges[name] || !source.getAnimationRange(name)) {
  60. return false;
  61. }
  62. var ret = true;
  63. var frameOffset = this._getHighestAnimationFrame() + 1;
  64. // make a dictionary of source skeleton's bones, so exact same order or doublely nested loop is not required
  65. var boneDict = {};
  66. var sourceBones = source.bones;
  67. for (var i = 0, nBones = sourceBones.length; i < nBones; i++) {
  68. boneDict[sourceBones[i].name] = sourceBones[i];
  69. }
  70. for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
  71. var boneName = this.bones[i].name;
  72. var sourceBone = boneDict[boneName];
  73. if (sourceBone) {
  74. ret = ret && this.bones[i].copyAnimationRange(sourceBone, name, frameOffset, rescaleAsRequired);
  75. }
  76. else {
  77. BABYLON.Tools.Warn("copyAnimationRange: not same rig, missing source bone " + boneName);
  78. ret = false;
  79. }
  80. }
  81. // do not call createAnimationRange(), since it also is done to bones, which was already done
  82. var range = source.getAnimationRange(name);
  83. this._ranges[name] = new BABYLON.AnimationRange(name, range.from + frameOffset, range.to + frameOffset);
  84. return ret;
  85. };
  86. Skeleton.prototype.returnToRest = function () {
  87. for (var index = 0; index < this.bones.length; index++) {
  88. this.bones[index].returnToRest();
  89. }
  90. };
  91. Skeleton.prototype._getHighestAnimationFrame = function () {
  92. var ret = 0;
  93. for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
  94. if (this.bones[i].animations[0]) {
  95. var highest = this.bones[i].animations[0].getHighestFrame();
  96. if (ret < highest) {
  97. ret = highest;
  98. }
  99. }
  100. }
  101. return ret;
  102. };
  103. Skeleton.prototype.beginAnimation = function (name, loop, speedRatio, onAnimationEnd) {
  104. var range = this.getAnimationRange(name);
  105. if (!range) {
  106. return null;
  107. }
  108. this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
  109. };
  110. Skeleton.prototype._markAsDirty = function () {
  111. this._isDirty = true;
  112. };
  113. Skeleton.prototype._registerMeshWithPoseMatrix = function (mesh) {
  114. this._meshesWithPoseMatrix.push(mesh);
  115. };
  116. Skeleton.prototype._unregisterMeshWithPoseMatrix = function (mesh) {
  117. var index = this._meshesWithPoseMatrix.indexOf(mesh);
  118. if (index > -1) {
  119. this._meshesWithPoseMatrix.splice(index, 1);
  120. }
  121. };
  122. Skeleton.prototype._computeTransformMatrices = function (targetMatrix, initialSkinMatrix) {
  123. for (var index = 0; index < this.bones.length; index++) {
  124. var bone = this.bones[index];
  125. var parentBone = bone.getParent();
  126. if (parentBone) {
  127. bone.getLocalMatrix().multiplyToRef(parentBone.getWorldMatrix(), bone.getWorldMatrix());
  128. }
  129. else {
  130. if (initialSkinMatrix) {
  131. bone.getLocalMatrix().multiplyToRef(initialSkinMatrix, bone.getWorldMatrix());
  132. }
  133. else {
  134. bone.getWorldMatrix().copyFrom(bone.getLocalMatrix());
  135. }
  136. }
  137. bone.getInvertedAbsoluteTransform().multiplyToArray(bone.getWorldMatrix(), targetMatrix, index * 16);
  138. }
  139. this._identity.copyToArray(targetMatrix, this.bones.length * 16);
  140. };
  141. Skeleton.prototype.prepare = function () {
  142. if (!this._isDirty) {
  143. return;
  144. }
  145. if (this.needInitialSkinMatrix) {
  146. for (var index = 0; index < this._meshesWithPoseMatrix.length; index++) {
  147. var mesh = this._meshesWithPoseMatrix[index];
  148. if (!mesh._bonesTransformMatrices || mesh._bonesTransformMatrices.length !== 16 * (this.bones.length + 1)) {
  149. mesh._bonesTransformMatrices = new Float32Array(16 * (this.bones.length + 1));
  150. }
  151. var poseMatrix = mesh.getPoseMatrix();
  152. // Prepare bones
  153. for (var boneIndex = 0; boneIndex < this.bones.length; boneIndex++) {
  154. var bone = this.bones[boneIndex];
  155. if (!bone.getParent()) {
  156. var matrix = bone.getBaseMatrix();
  157. matrix.multiplyToRef(poseMatrix, BABYLON.Tmp.Matrix[0]);
  158. bone._updateDifferenceMatrix(BABYLON.Tmp.Matrix[0]);
  159. }
  160. }
  161. this._computeTransformMatrices(mesh._bonesTransformMatrices, poseMatrix);
  162. }
  163. }
  164. else {
  165. if (!this._transformMatrices || this._transformMatrices.length !== 16 * (this.bones.length + 1)) {
  166. this._transformMatrices = new Float32Array(16 * (this.bones.length + 1));
  167. }
  168. this._computeTransformMatrices(this._transformMatrices, null);
  169. }
  170. this._isDirty = false;
  171. this._scene._activeBones += this.bones.length;
  172. };
  173. Skeleton.prototype.getAnimatables = function () {
  174. if (!this._animatables || this._animatables.length !== this.bones.length) {
  175. this._animatables = [];
  176. for (var index = 0; index < this.bones.length; index++) {
  177. this._animatables.push(this.bones[index]);
  178. }
  179. }
  180. return this._animatables;
  181. };
  182. Skeleton.prototype.clone = function (name, id) {
  183. var result = new Skeleton(name, id || name, this._scene);
  184. for (var index = 0; index < this.bones.length; index++) {
  185. var source = this.bones[index];
  186. var parentBone = null;
  187. if (source.getParent()) {
  188. var parentIndex = this.bones.indexOf(source.getParent());
  189. parentBone = result.bones[parentIndex];
  190. }
  191. var bone = new BABYLON.Bone(source.name, result, parentBone, source.getBaseMatrix().clone(), source.getRestPose().clone());
  192. BABYLON.Tools.DeepCopy(source.animations, bone.animations);
  193. }
  194. return result;
  195. };
  196. Skeleton.prototype.dispose = function () {
  197. this._meshesWithPoseMatrix = [];
  198. // Animations
  199. this.getScene().stopAnimation(this);
  200. // Remove from scene
  201. this.getScene().removeSkeleton(this);
  202. };
  203. Skeleton.prototype.serialize = function () {
  204. var serializationObject = {};
  205. serializationObject.name = this.name;
  206. serializationObject.id = this.id;
  207. serializationObject.bones = [];
  208. serializationObject.needInitialSkinMatrix = this.needInitialSkinMatrix;
  209. for (var index = 0; index < this.bones.length; index++) {
  210. var bone = this.bones[index];
  211. var serializedBone = {
  212. parentBoneIndex: bone.getParent() ? this.bones.indexOf(bone.getParent()) : -1,
  213. name: bone.name,
  214. matrix: bone.getLocalMatrix().toArray(),
  215. rest: bone.getRestPose().toArray()
  216. };
  217. serializationObject.bones.push(serializedBone);
  218. if (bone.length) {
  219. serializedBone.length = bone.length;
  220. }
  221. if (bone.animations && bone.animations.length > 0) {
  222. serializedBone.animation = bone.animations[0].serialize();
  223. }
  224. serializationObject.ranges = [];
  225. for (var name in this._ranges) {
  226. var range = {};
  227. range.name = name;
  228. range.from = this._ranges[name].from;
  229. range.to = this._ranges[name].to;
  230. serializationObject.ranges.push(range);
  231. }
  232. }
  233. return serializationObject;
  234. };
  235. Skeleton.Parse = function (parsedSkeleton, scene) {
  236. var skeleton = new Skeleton(parsedSkeleton.name, parsedSkeleton.id, scene);
  237. skeleton.needInitialSkinMatrix = parsedSkeleton.needInitialSkinMatrix;
  238. for (var index = 0; index < parsedSkeleton.bones.length; index++) {
  239. var parsedBone = parsedSkeleton.bones[index];
  240. var parentBone = null;
  241. if (parsedBone.parentBoneIndex > -1) {
  242. parentBone = skeleton.bones[parsedBone.parentBoneIndex];
  243. }
  244. var rest = parsedBone.rest ? BABYLON.Matrix.FromArray(parsedBone.rest) : null;
  245. var bone = new BABYLON.Bone(parsedBone.name, skeleton, parentBone, BABYLON.Matrix.FromArray(parsedBone.matrix), rest);
  246. if (parsedBone.length) {
  247. bone.length = parsedBone.length;
  248. }
  249. if (parsedBone.animation) {
  250. bone.animations.push(BABYLON.Animation.Parse(parsedBone.animation));
  251. }
  252. }
  253. // placed after bones, so createAnimationRange can cascade down
  254. if (parsedSkeleton.ranges) {
  255. for (var index = 0; index < parsedSkeleton.ranges.length; index++) {
  256. var data = parsedSkeleton.ranges[index];
  257. skeleton.createAnimationRange(data.name, data.from, data.to);
  258. }
  259. }
  260. return skeleton;
  261. };
  262. return Skeleton;
  263. })();
  264. BABYLON.Skeleton = Skeleton;
  265. })(BABYLON || (BABYLON = {}));