PropertyMixer.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { Quaternion } from '../math/Quaternion.js';
  2. /**
  3. *
  4. * Buffered scene graph property that allows weighted accumulation.
  5. *
  6. *
  7. * @author Ben Houston / http://clara.io/
  8. * @author David Sarno / http://lighthaus.us/
  9. * @author tschw
  10. */
  11. function PropertyMixer( binding, typeName, valueSize ) {
  12. this.binding = binding;
  13. this.valueSize = valueSize;
  14. var bufferType = Float64Array,
  15. mixFunction;
  16. switch ( typeName ) {
  17. case 'quaternion':
  18. mixFunction = this._slerp;
  19. break;
  20. case 'string':
  21. case 'bool':
  22. bufferType = Array;
  23. mixFunction = this._select;
  24. break;
  25. default:
  26. mixFunction = this._lerp;
  27. }
  28. this.buffer = new bufferType( valueSize * 4 );
  29. // layout: [ incoming | accu0 | accu1 | orig ]
  30. //
  31. // interpolators can use .buffer as their .result
  32. // the data then goes to 'incoming'
  33. //
  34. // 'accu0' and 'accu1' are used frame-interleaved for
  35. // the cumulative result and are compared to detect
  36. // changes
  37. //
  38. // 'orig' stores the original state of the property
  39. this._mixBufferRegion = mixFunction;
  40. this.cumulativeWeight = 0;
  41. this.useCount = 0;
  42. this.referenceCount = 0;
  43. }
  44. Object.assign( PropertyMixer.prototype, {
  45. // accumulate data in the 'incoming' region into 'accu<i>'
  46. accumulate: function ( accuIndex, weight ) {
  47. // note: happily accumulating nothing when weight = 0, the caller knows
  48. // the weight and shouldn't have made the call in the first place
  49. var buffer = this.buffer,
  50. stride = this.valueSize,
  51. offset = accuIndex * stride + stride,
  52. currentWeight = this.cumulativeWeight;
  53. if ( currentWeight === 0 ) {
  54. // accuN := incoming * weight
  55. for ( var i = 0; i !== stride; ++ i ) {
  56. buffer[ offset + i ] = buffer[ i ];
  57. }
  58. currentWeight = weight;
  59. } else {
  60. // accuN := accuN + incoming * weight
  61. currentWeight += weight;
  62. var mix = weight / currentWeight;
  63. this._mixBufferRegion( buffer, offset, 0, mix, stride );
  64. }
  65. this.cumulativeWeight = currentWeight;
  66. },
  67. // apply the state of 'accu<i>' to the binding when accus differ
  68. apply: function ( accuIndex ) {
  69. var stride = this.valueSize,
  70. buffer = this.buffer,
  71. offset = accuIndex * stride + stride,
  72. weight = this.cumulativeWeight,
  73. binding = this.binding;
  74. this.cumulativeWeight = 0;
  75. if ( weight < 1 ) {
  76. // accuN := accuN + original * ( 1 - cumulativeWeight )
  77. var originalValueOffset = stride * 3;
  78. this._mixBufferRegion(
  79. buffer, offset, originalValueOffset, 1 - weight, stride );
  80. }
  81. for ( var i = stride, e = stride + stride; i !== e; ++ i ) {
  82. if ( buffer[ i ] !== buffer[ i + stride ] ) {
  83. // value has changed -> update scene graph
  84. binding.setValue( buffer, offset );
  85. break;
  86. }
  87. }
  88. },
  89. // remember the state of the bound property and copy it to both accus
  90. saveOriginalState: function () {
  91. var binding = this.binding;
  92. var buffer = this.buffer,
  93. stride = this.valueSize,
  94. originalValueOffset = stride * 3;
  95. binding.getValue( buffer, originalValueOffset );
  96. // accu[0..1] := orig -- initially detect changes against the original
  97. for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) {
  98. buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];
  99. }
  100. this.cumulativeWeight = 0;
  101. },
  102. // apply the state previously taken via 'saveOriginalState' to the binding
  103. restoreOriginalState: function () {
  104. var originalValueOffset = this.valueSize * 3;
  105. this.binding.setValue( this.buffer, originalValueOffset );
  106. },
  107. // mix functions
  108. _select: function ( buffer, dstOffset, srcOffset, t, stride ) {
  109. if ( t >= 0.5 ) {
  110. for ( var i = 0; i !== stride; ++ i ) {
  111. buffer[ dstOffset + i ] = buffer[ srcOffset + i ];
  112. }
  113. }
  114. },
  115. _slerp: function ( buffer, dstOffset, srcOffset, t ) {
  116. Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
  117. },
  118. _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
  119. var s = 1 - t;
  120. for ( var i = 0; i !== stride; ++ i ) {
  121. var j = dstOffset + i;
  122. buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;
  123. }
  124. }
  125. } );
  126. export { PropertyMixer };
粤ICP备19079148号