PropertyMixer.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. import { Quaternion } from '../math/Quaternion.js';
  2. function PropertyMixer( binding, typeName, valueSize ) {
  3. this.binding = binding;
  4. this.valueSize = valueSize;
  5. let mixFunction,
  6. mixFunctionAdditive,
  7. setIdentity;
  8. // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ]
  9. //
  10. // interpolators can use .buffer as their .result
  11. // the data then goes to 'incoming'
  12. //
  13. // 'accu0' and 'accu1' are used frame-interleaved for
  14. // the cumulative result and are compared to detect
  15. // changes
  16. //
  17. // 'orig' stores the original state of the property
  18. //
  19. // 'add' is used for additive cumulative results
  20. //
  21. // 'work' is optional and is only present for quaternion types. It is used
  22. // to store intermediate quaternion multiplication results
  23. switch ( typeName ) {
  24. case 'quaternion':
  25. mixFunction = this._slerp;
  26. mixFunctionAdditive = this._slerpAdditive;
  27. setIdentity = this._setAdditiveIdentityQuaternion;
  28. this.buffer = new Float64Array( valueSize * 6 );
  29. this._workIndex = 5;
  30. break;
  31. case 'string':
  32. case 'bool':
  33. mixFunction = this._select;
  34. // Use the regular mix function and for additive on these types,
  35. // additive is not relevant for non-numeric types
  36. mixFunctionAdditive = this._select;
  37. setIdentity = this._setAdditiveIdentityOther;
  38. this.buffer = new Array( valueSize * 5 );
  39. break;
  40. default:
  41. mixFunction = this._lerp;
  42. mixFunctionAdditive = this._lerpAdditive;
  43. setIdentity = this._setAdditiveIdentityNumeric;
  44. this.buffer = new Float64Array( valueSize * 5 );
  45. }
  46. this._mixBufferRegion = mixFunction;
  47. this._mixBufferRegionAdditive = mixFunctionAdditive;
  48. this._setIdentity = setIdentity;
  49. this._origIndex = 3;
  50. this._addIndex = 4;
  51. this.cumulativeWeight = 0;
  52. this.cumulativeWeightAdditive = 0;
  53. this.useCount = 0;
  54. this.referenceCount = 0;
  55. }
  56. Object.assign( PropertyMixer.prototype, {
  57. // accumulate data in the 'incoming' region into 'accu<i>'
  58. accumulate: function ( accuIndex, weight ) {
  59. // note: happily accumulating nothing when weight = 0, the caller knows
  60. // the weight and shouldn't have made the call in the first place
  61. const buffer = this.buffer,
  62. stride = this.valueSize,
  63. offset = accuIndex * stride + stride;
  64. let currentWeight = this.cumulativeWeight;
  65. if ( currentWeight === 0 ) {
  66. // accuN := incoming * weight
  67. for ( let i = 0; i !== stride; ++ i ) {
  68. buffer[ offset + i ] = buffer[ i ];
  69. }
  70. currentWeight = weight;
  71. } else {
  72. // accuN := accuN + incoming * weight
  73. currentWeight += weight;
  74. const mix = weight / currentWeight;
  75. this._mixBufferRegion( buffer, offset, 0, mix, stride );
  76. }
  77. this.cumulativeWeight = currentWeight;
  78. },
  79. // accumulate data in the 'incoming' region into 'add'
  80. accumulateAdditive: function ( weight ) {
  81. const buffer = this.buffer,
  82. stride = this.valueSize,
  83. offset = stride * this._addIndex;
  84. if ( this.cumulativeWeightAdditive === 0 ) {
  85. // add = identity
  86. this._setIdentity();
  87. }
  88. // add := add + incoming * weight
  89. this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride );
  90. this.cumulativeWeightAdditive += weight;
  91. },
  92. // apply the state of 'accu<i>' to the binding when accus differ
  93. apply: function ( accuIndex ) {
  94. const stride = this.valueSize,
  95. buffer = this.buffer,
  96. offset = accuIndex * stride + stride,
  97. weight = this.cumulativeWeight,
  98. weightAdditive = this.cumulativeWeightAdditive,
  99. binding = this.binding;
  100. this.cumulativeWeight = 0;
  101. this.cumulativeWeightAdditive = 0;
  102. if ( weight < 1 ) {
  103. // accuN := accuN + original * ( 1 - cumulativeWeight )
  104. const originalValueOffset = stride * this._origIndex;
  105. this._mixBufferRegion(
  106. buffer, offset, originalValueOffset, 1 - weight, stride );
  107. }
  108. if ( weightAdditive > 0 ) {
  109. // accuN := accuN + additive accuN
  110. this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride );
  111. }
  112. for ( let i = stride, e = stride + stride; i !== e; ++ i ) {
  113. if ( buffer[ i ] !== buffer[ i + stride ] ) {
  114. // value has changed -> update scene graph
  115. binding.setValue( buffer, offset );
  116. break;
  117. }
  118. }
  119. },
  120. // remember the state of the bound property and copy it to both accus
  121. saveOriginalState: function () {
  122. const binding = this.binding;
  123. const buffer = this.buffer,
  124. stride = this.valueSize,
  125. originalValueOffset = stride * this._origIndex;
  126. binding.getValue( buffer, originalValueOffset );
  127. // accu[0..1] := orig -- initially detect changes against the original
  128. for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) {
  129. buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];
  130. }
  131. // Add to identity for additive
  132. this._setIdentity();
  133. this.cumulativeWeight = 0;
  134. this.cumulativeWeightAdditive = 0;
  135. },
  136. // apply the state previously taken via 'saveOriginalState' to the binding
  137. restoreOriginalState: function () {
  138. const originalValueOffset = this.valueSize * 3;
  139. this.binding.setValue( this.buffer, originalValueOffset );
  140. },
  141. _setAdditiveIdentityNumeric: function () {
  142. const startIndex = this._addIndex * this.valueSize;
  143. const endIndex = startIndex + this.valueSize;
  144. for ( let i = startIndex; i < endIndex; i ++ ) {
  145. this.buffer[ i ] = 0;
  146. }
  147. },
  148. _setAdditiveIdentityQuaternion: function () {
  149. this._setAdditiveIdentityNumeric();
  150. this.buffer[ this._addIndex * 4 + 3 ] = 1;
  151. },
  152. _setAdditiveIdentityOther: function () {
  153. const startIndex = this._origIndex * this.valueSize;
  154. const targetIndex = this._addIndex * this.valueSize;
  155. for ( let i = 0; i < this.valueSize; i ++ ) {
  156. this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ];
  157. }
  158. },
  159. // mix functions
  160. _select: function ( buffer, dstOffset, srcOffset, t, stride ) {
  161. if ( t >= 0.5 ) {
  162. for ( let i = 0; i !== stride; ++ i ) {
  163. buffer[ dstOffset + i ] = buffer[ srcOffset + i ];
  164. }
  165. }
  166. },
  167. _slerp: function ( buffer, dstOffset, srcOffset, t ) {
  168. Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
  169. },
  170. _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) {
  171. const workOffset = this._workIndex * stride;
  172. // Store result in intermediate buffer offset
  173. Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset );
  174. // Slerp to the intermediate result
  175. Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t );
  176. },
  177. _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
  178. const s = 1 - t;
  179. for ( let i = 0; i !== stride; ++ i ) {
  180. const j = dstOffset + i;
  181. buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;
  182. }
  183. },
  184. _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) {
  185. for ( let i = 0; i !== stride; ++ i ) {
  186. const j = dstOffset + i;
  187. buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t;
  188. }
  189. }
  190. } );
  191. export { PropertyMixer };