PropertyBinding.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /**
  2. *
  3. * A track bound to a real value in the scene graph.
  4. *
  5. * @author Ben Houston / http://clara.io/
  6. * @author David Sarno / http://lighthaus.us/
  7. */
  8. THREE.PropertyBinding = function ( rootNode, trackName ) {
  9. this.rootNode = rootNode;
  10. this.trackName = trackName;
  11. var parseResults = THREE.PropertyBinding.parseTrackName( trackName );
  12. this.directoryName = parseResults.directoryName || null;
  13. this.nodeName = parseResults.nodeName;
  14. this.material = parseResults.material;
  15. this.materialIndex = parseResults.materialIndex;
  16. this.propertyName = parseResults.propertyName || null;
  17. this.propertyIndex = parseResults.propertyIndex || -1;
  18. this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName );
  19. };
  20. THREE.PropertyBinding.prototype = {
  21. constructor: THREE.PropertyBinding,
  22. set: function( value ) {
  23. console.log( "PropertyBinding.set( " + value + ")" );
  24. var targetObject = this.node;
  25. // ensure there is a value node
  26. if( ! targetObject ) {
  27. console.log( " trying to update node for track: " + this.trackName + " but it wasn't found." );
  28. return;
  29. }
  30. if( this.material ) {
  31. targetObject = targetObject.material;
  32. if( this.materialIndex !== undefined && this.materialIndex !== null && this.materialIndex >= 0 ) {
  33. if( targetObject.materials ) {
  34. targetObject = targetObject.materials[ this.materialIndex ];
  35. }
  36. else {
  37. console.log( " trying to submaterial via index, but no materials exist:", targetObject );
  38. }
  39. }
  40. }
  41. // ensure there is a value property on the node
  42. var nodeProperty = targetObject[ this.propertyName ];
  43. if( ! nodeProperty ) {
  44. console.log( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject );
  45. return;
  46. }
  47. // access a sub element of the property array (only primitives are supported right now)
  48. if( ( this.propertyIndex.length && this.propertyIndex.length > 0 ) || this.propertyIndex >= 0 ) {
  49. console.log( ' update property array ' + this.propertyName + '[' + this.propertyIndex + '] via assignment.' );
  50. nodeProperty[ this.propertyIndex ] = value;
  51. }
  52. // must use copy for Object3D.Euler/Quaternion
  53. else if( nodeProperty.copy ) {
  54. console.log( ' update property ' + this.name + '.' + this.propertyName + ' via a set() function.' );
  55. nodeProperty.copy( value );
  56. }
  57. // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
  58. else {
  59. console.log( ' update property ' + this.name + '.' + this.propertyName + ' via assignment.' );
  60. targetObject[ this.propertyName ] = value;
  61. }
  62. // trigger node dirty
  63. if( targetObject.needsUpdate !== undefined ) { // material
  64. console.log( ' triggering material as dirty' );
  65. this.node.needsUpdate = true;
  66. }
  67. if( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
  68. console.log( ' triggering node as dirty' );
  69. targetObject.matrixWorldNeedsUpdate = true;
  70. }
  71. },
  72. get: function() {
  73. throw new Error( "TODO" );
  74. }
  75. };
  76. THREE.PropertyBinding.parseTrackName = function( trackName ) {
  77. // matches strings in the form of:
  78. // nodeName.property
  79. // nodeName.property[accessor]
  80. // nodeName.material.property[accessor]
  81. // uuid.property[accessor]
  82. // uuid.material.property[accessor]
  83. // parentName/nodeName.property
  84. // parentName/parentName/nodeName.property[index]
  85. // created and tested via https://regex101.com/#javascript
  86. var re = /^(([\w]+\/)*)([\w-]+)?(\.material(\[([\w]+)\])?)?(\.([\w]+)(\[([\w]+)\])?)?$/;
  87. var matches = re.exec(trackName);
  88. if( ! matches ) {
  89. throw new Error( "cannot parse trackName at all: " + trackName );
  90. }
  91. if (matches.index === re.lastIndex) {
  92. re.lastIndex++;
  93. }
  94. var results = {
  95. directoryName: matches[0],
  96. nodeName: matches[3], // allowed to be null, specified root node.
  97. material: ( matches[4] && matches[4].length > 0 ),
  98. materialIndex: matches[6],
  99. propertyName: matches[8],
  100. propertyIndex: matches[10] // allowed to be null, specifies that the whole property is set.
  101. };
  102. console.log( "PropertyBinding.parseTrackName", trackName, results, matches );
  103. if( results.propertyName === null || results.propertyName.length === 0 ) {
  104. throw new Error( "can not parse propertyName from trackName: " + trackName );
  105. }
  106. return results;
  107. };
  108. // TODO: Cache this at some point
  109. THREE.PropertyBinding.findNode = function( root, nodeName ) {
  110. console.log( 'AnimationUtils.findNode( ' + root.name + ', nodeName: ' + nodeName + ')');
  111. if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
  112. console.log( ' root.' );
  113. return root;
  114. }
  115. // (2) search into skeleton bones.
  116. if( root.skeleton ) {
  117. var searchSkeleton = function( skeleton ) {
  118. for( var i = 0; i < skeleton.bones.length; i ++ ) {
  119. var bone = skeleton.bones[i];
  120. if( bone.name === nodeName ) {
  121. return bone;
  122. }
  123. }
  124. return null;
  125. };
  126. var bone = searchSkeleton( root.skeleton );
  127. if( bone ) {
  128. console.log( ' bone: ' + bone.name + '.' );
  129. return bone;
  130. }
  131. }
  132. // (3) search into node subtree.
  133. if( root.children ) {
  134. var searchNodeSubtree = function( children ) {
  135. for( var i = 0; i < children.length; i ++ ) {
  136. var childNode = children[i];
  137. if( childNode.name === nodeName || childNode.uuid === nodeName ) {
  138. return childNode;
  139. }
  140. var result = searchNodeSubtree( childNode.children );
  141. if( result ) return result;
  142. }
  143. return null;
  144. };
  145. var subTreeNode = searchNodeSubtree( root.children );
  146. if( subTreeNode ) {
  147. console.log( ' node: ' + subTreeNode.name + '.' );
  148. return subTreeNode;
  149. }
  150. }
  151. console.log( " <null>. No node found for name: " + nodeName );
  152. return null;
  153. }