PropertyMixer.js 4.0 KB

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