SkinnedMesh.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /**
  2. * @author mikael emtinger / http://gomo.se/
  3. * @author alteredq / http://alteredqualia.com/
  4. */
  5. THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
  6. THREE.Mesh.call( this, geometry, material );
  7. this.useVertexTexture = useVertexTexture;
  8. this.identityMatrix = new THREE.Matrix4();
  9. this.bones = [];
  10. this.boneMatrices = null;
  11. // init bones
  12. // TODO: remove bone creation as there is no reason (other than
  13. // convenience) for THREE.SkinnedMesh to do this.
  14. var bone, gbone, p, q, s;
  15. var bones = [];
  16. if ( this.geometry && this.geometry.bones !== undefined ) {
  17. for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
  18. gbone = this.geometry.bones[ b ];
  19. p = gbone.pos;
  20. q = gbone.rotq;
  21. s = gbone.scl;
  22. bone = new Bone( this );
  23. bones.push( bone );
  24. bone.name = gbone.name;
  25. bone.position.set( p[0], p[1], p[2] );
  26. bone.quaternion.set( q[0], q[1], q[2], q[3] );
  27. if ( s !== undefined ) {
  28. bone.scale.set( s[0], s[1], s[2] );
  29. } else {
  30. bone.scale.set( 1, 1, 1 );
  31. }
  32. }
  33. for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
  34. gbone = this.geometry.bones[ b ];
  35. if ( gbone.parent !== -1 ) {
  36. bones[ gbone.parent ].add( bones[ b ] );
  37. }
  38. }
  39. }
  40. this.bind( bones );
  41. };
  42. THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
  43. THREE.SkinnedMesh.prototype.bind = function( bones ) {
  44. bones = bones || [];
  45. this.bones = bones.slice( 0 );
  46. this.boneInverses = undefined;
  47. // update bone matrices and/or texture
  48. this.initBoneMatrices();
  49. // update bone inverses
  50. this.pose();
  51. };
  52. THREE.SkinnedMesh.prototype.initBoneInverses = function () {
  53. this.boneInverses = [];
  54. for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
  55. var inverse = new THREE.Matrix4();
  56. if ( this.bones[ b ] ) {
  57. inverse.getInverse( this.bones[ b ].matrixWorld );
  58. }
  59. inverse.multiply( this.matrixWorld );
  60. this.boneInverses.push( inverse );
  61. }
  62. };
  63. THREE.SkinnedMesh.prototype.initBoneMatrices = function () {
  64. var nBones = this.bones.length;
  65. // clear the old bone texture and float array
  66. this.boneMatrices = null;
  67. this.boneTextureWidth = undefined;
  68. this.boneTextureHeight = undefined;
  69. if ( this.boneTexture ) {
  70. this.boneTexture.dispose();
  71. this.boneTexture = undefined;
  72. }
  73. // create a bone texture or an array of floats
  74. if ( this.useVertexTexture ) {
  75. // layout (1 matrix = 4 pixels)
  76. // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
  77. // with 8x8 pixel texture max 16 bones (8 * 8 / 4)
  78. // 16x16 pixel texture max 64 bones (16 * 16 / 4)
  79. // 32x32 pixel texture max 256 bones (32 * 32 / 4)
  80. // 64x64 pixel texture max 1024 bones (64 * 64 / 4)
  81. var size;
  82. if ( nBones > 256 )
  83. size = 64;
  84. else if ( nBones > 64 )
  85. size = 32;
  86. else if ( nBones > 16 )
  87. size = 16;
  88. else
  89. size = 8;
  90. this.boneTextureWidth = size;
  91. this.boneTextureHeight = size;
  92. this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
  93. this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
  94. this.boneTexture.minFilter = THREE.NearestFilter;
  95. this.boneTexture.magFilter = THREE.NearestFilter;
  96. this.boneTexture.generateMipmaps = false;
  97. this.boneTexture.flipY = false;
  98. } else {
  99. this.boneMatrices = new Float32Array( 16 * nBones );
  100. }
  101. };
  102. THREE.SkinnedMesh.prototype.updateBoneMatrices = function () {
  103. var offsetMatrix = new THREE.Matrix4();
  104. var invMatrixWorld = new THREE.Matrix4().getInverse( this.matrixWorld );
  105. // make a snapshot of the bones' rest position
  106. if ( this.boneInverses === undefined ) {
  107. this.initBoneInverses();
  108. }
  109. // flatten bone matrices to array
  110. for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
  111. // compute the offset between the current and the original transform;
  112. var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
  113. offsetMatrix.multiplyMatrices( invMatrixWorld, matrix );
  114. offsetMatrix.multiply( this.boneInverses[ b ] );
  115. offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
  116. }
  117. if ( this.useVertexTexture ) {
  118. this.boneTexture.needsUpdate = true;
  119. }
  120. };
  121. THREE.SkinnedMesh.prototype.pose = function () {
  122. this.updateMatrixWorld( true );
  123. // force recomputation of bone inverses
  124. this.boneInverses = undefined;
  125. this.updateBoneMatrices();
  126. this.normalizeSkinWeights();
  127. };
  128. THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
  129. if ( this.geometry instanceof THREE.Geometry ) {
  130. for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
  131. var sw = this.geometry.skinWeights[ i ];
  132. var scale = 1.0 / sw.lengthManhattan();
  133. if ( scale !== Infinity ) {
  134. sw.multiplyScalar( scale );
  135. } else {
  136. sw.set( 1 ); // this will be normalized by the shader anyway
  137. }
  138. }
  139. } else {
  140. // skinning weights assumed to be normalized for THREE.BufferGeometry
  141. }
  142. };
  143. THREE.SkinnedMesh.prototype.clone = function ( object ) {
  144. if ( object === undefined ) {
  145. object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture );
  146. }
  147. THREE.Mesh.prototype.clone.call( this, object );
  148. return object;
  149. };