PropertyMixer.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { Quaternion } from '../math/Quaternion';
  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. PropertyMixer.prototype = {
  45. constructor: PropertyMixer,
  46. // accumulate data in the 'incoming' region into 'accu<i>'
  47. accumulate: function( accuIndex, weight ) {
  48. // note: happily accumulating nothing when weight = 0, the caller knows
  49. // the weight and shouldn't have made the call in the first place
  50. var buffer = this.buffer,
  51. stride = this.valueSize,
  52. offset = accuIndex * stride + stride,
  53. currentWeight = this.cumulativeWeight;
  54. if ( currentWeight === 0 ) {
  55. // accuN := incoming * weight
  56. for ( var i = 0; i !== stride; ++ i ) {
  57. buffer[ offset + i ] = buffer[ i ];
  58. }
  59. currentWeight = weight;
  60. } else {
  61. // accuN := accuN + incoming * weight
  62. currentWeight += weight;
  63. var mix = weight / currentWeight;
  64. this._mixBufferRegion( buffer, offset, 0, mix, stride );
  65. }
  66. this.cumulativeWeight = currentWeight;
  67. },
  68. // apply the state of 'accu<i>' to the binding when accus differ
  69. apply: function( accuIndex ) {
  70. var stride = this.valueSize,
  71. buffer = this.buffer,
  72. offset = accuIndex * stride + stride,
  73. weight = this.cumulativeWeight,
  74. binding = this.binding;
  75. this.cumulativeWeight = 0;
  76. if ( weight < 1 ) {
  77. // accuN := accuN + original * ( 1 - cumulativeWeight )
  78. var originalValueOffset = stride * 3;
  79. this._mixBufferRegion(
  80. buffer, offset, originalValueOffset, 1 - weight, stride );
  81. }
  82. for ( var i = stride, e = stride + stride; i !== e; ++ i ) {
  83. if ( buffer[ i ] !== buffer[ i + stride ] ) {
  84. // value has changed -> update scene graph
  85. binding.setValue( buffer, offset );
  86. break;
  87. }
  88. }
  89. },
  90. // remember the state of the bound property and copy it to both accus
  91. saveOriginalState: function() {
  92. var binding = this.binding;
  93. var buffer = this.buffer,
  94. stride = this.valueSize,
  95. originalValueOffset = stride * 3;
  96. binding.getValue( buffer, originalValueOffset );
  97. // accu[0..1] := orig -- initially detect changes against the original
  98. for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) {
  99. buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];
  100. }
  101. this.cumulativeWeight = 0;
  102. },
  103. // apply the state previously taken via 'saveOriginalState' to the binding
  104. restoreOriginalState: function() {
  105. var originalValueOffset = this.valueSize * 3;
  106. this.binding.setValue( this.buffer, originalValueOffset );
  107. },
  108. // mix functions
  109. _select: function( buffer, dstOffset, srcOffset, t, stride ) {
  110. if ( t >= 0.5 ) {
  111. for ( var i = 0; i !== stride; ++ i ) {
  112. buffer[ dstOffset + i ] = buffer[ srcOffset + i ];
  113. }
  114. }
  115. },
  116. _slerp: function( buffer, dstOffset, srcOffset, t, stride ) {
  117. Quaternion.slerpFlat( buffer, dstOffset,
  118. buffer, dstOffset, buffer, srcOffset, t );
  119. },
  120. _lerp: function( buffer, dstOffset, srcOffset, t, stride ) {
  121. var s = 1 - t;
  122. for ( var i = 0; i !== stride; ++ i ) {
  123. var j = dstOffset + i;
  124. buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;
  125. }
  126. }
  127. };
  128. export { PropertyMixer };
粤ICP备19079148号