| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| var RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; |
|
|
| function Composite( targetGroup, path, optionalParsedPath ) { |
|
|
| var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); |
|
|
| this._targetGroup = targetGroup; |
| this._bindings = targetGroup.subscribe_( path, parsedPath ); |
|
|
| } |
|
|
| Object.assign( Composite.prototype, { |
|
|
| getValue: function ( array, offset ) { |
|
|
| this.bind(); |
|
|
| var firstValidIndex = this._targetGroup.nCachedObjects_, |
| binding = this._bindings[ firstValidIndex ]; |
|
|
| |
| if ( binding !== undefined ) binding.getValue( array, offset ); |
|
|
| }, |
|
|
| setValue: function ( array, offset ) { |
|
|
| var bindings = this._bindings; |
|
|
| for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { |
|
|
| bindings[ i ].setValue( array, offset ); |
|
|
| } |
|
|
| }, |
|
|
| bind: function () { |
|
|
| var bindings = this._bindings; |
|
|
| for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { |
|
|
| bindings[ i ].bind(); |
|
|
| } |
|
|
| }, |
|
|
| unbind: function () { |
|
|
| var bindings = this._bindings; |
|
|
| for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { |
|
|
| bindings[ i ].unbind(); |
|
|
| } |
|
|
| } |
|
|
| } ); |
|
|
|
|
| function PropertyBinding( rootNode, path, parsedPath ) { |
|
|
| this.path = path; |
| this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); |
|
|
| this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; |
|
|
| this.rootNode = rootNode; |
|
|
| } |
|
|
| Object.assign( PropertyBinding, { |
|
|
| Composite: Composite, |
|
|
| create: function ( root, path, parsedPath ) { |
|
|
| if ( ! ( root && root.isAnimationObjectGroup ) ) { |
|
|
| return new PropertyBinding( root, path, parsedPath ); |
|
|
| } else { |
|
|
| return new PropertyBinding.Composite( root, path, parsedPath ); |
|
|
| } |
|
|
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| sanitizeNodeName: ( function () { |
|
|
| var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' ); |
|
|
| return function sanitizeNodeName( name ) { |
|
|
| return name.replace( /\s/g, '_' ).replace( reservedRe, '' ); |
|
|
| }; |
|
|
| }() ), |
|
|
| parseTrackName: function () { |
|
|
| |
| |
| |
| var wordChar = '[^' + RESERVED_CHARS_RE + ']'; |
| var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; |
|
|
| |
| |
| var directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', wordChar ); |
|
|
| |
| var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot ); |
|
|
| |
| |
| var objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', wordChar ); |
|
|
| |
| |
| var propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', wordChar ); |
|
|
| var trackRe = new RegExp( '' |
| + '^' |
| + directoryRe |
| + nodeRe |
| + objectRe |
| + propertyRe |
| + '$' |
| ); |
|
|
| var supportedObjectNames = [ 'material', 'materials', 'bones' ]; |
|
|
| return function parseTrackName( trackName ) { |
|
|
| var matches = trackRe.exec( trackName ); |
|
|
| if ( ! matches ) { |
|
|
| throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); |
|
|
| } |
|
|
| var results = { |
| |
| nodeName: matches[ 2 ], |
| objectName: matches[ 3 ], |
| objectIndex: matches[ 4 ], |
| propertyName: matches[ 5 ], |
| propertyIndex: matches[ 6 ] |
| }; |
|
|
| var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); |
|
|
| if ( lastDot !== undefined && lastDot !== - 1 ) { |
|
|
| var objectName = results.nodeName.substring( lastDot + 1 ); |
|
|
| |
| |
| |
| |
| if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) { |
|
|
| results.nodeName = results.nodeName.substring( 0, lastDot ); |
| results.objectName = objectName; |
|
|
| } |
|
|
| } |
|
|
| if ( results.propertyName === null || results.propertyName.length === 0 ) { |
|
|
| throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); |
|
|
| } |
|
|
| return results; |
|
|
| }; |
|
|
| }(), |
|
|
| findNode: function ( root, nodeName ) { |
|
|
| if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { |
|
|
| return root; |
|
|
| } |
|
|
| |
| if ( root.skeleton ) { |
|
|
| var bone = root.skeleton.getBoneByName( nodeName ); |
|
|
| if ( bone !== undefined ) { |
|
|
| return bone; |
|
|
| } |
|
|
| } |
|
|
| |
| if ( root.children ) { |
|
|
| var searchNodeSubtree = function ( children ) { |
|
|
| for ( var i = 0; i < children.length; i ++ ) { |
|
|
| var childNode = children[ i ]; |
|
|
| if ( childNode.name === nodeName || childNode.uuid === nodeName ) { |
|
|
| return childNode; |
|
|
| } |
|
|
| var result = searchNodeSubtree( childNode.children ); |
|
|
| if ( result ) return result; |
|
|
| } |
|
|
| return null; |
|
|
| }; |
|
|
| var subTreeNode = searchNodeSubtree( root.children ); |
|
|
| if ( subTreeNode ) { |
|
|
| return subTreeNode; |
|
|
| } |
|
|
| } |
|
|
| return null; |
|
|
| } |
|
|
| } ); |
|
|
| Object.assign( PropertyBinding.prototype, { |
|
|
| |
| _getValue_unavailable: function () {}, |
| _setValue_unavailable: function () {}, |
|
|
| BindingType: { |
| Direct: 0, |
| EntireArray: 1, |
| ArrayElement: 2, |
| HasFromToArray: 3 |
| }, |
|
|
| Versioning: { |
| None: 0, |
| NeedsUpdate: 1, |
| MatrixWorldNeedsUpdate: 2 |
| }, |
|
|
| GetterByBindingType: [ |
|
|
| function getValue_direct( buffer, offset ) { |
|
|
| buffer[ offset ] = this.node[ this.propertyName ]; |
|
|
| }, |
|
|
| function getValue_array( buffer, offset ) { |
|
|
| var source = this.resolvedProperty; |
|
|
| for ( var i = 0, n = source.length; i !== n; ++ i ) { |
|
|
| buffer[ offset ++ ] = source[ i ]; |
|
|
| } |
|
|
| }, |
|
|
| function getValue_arrayElement( buffer, offset ) { |
|
|
| buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; |
|
|
| }, |
|
|
| function getValue_toArray( buffer, offset ) { |
|
|
| this.resolvedProperty.toArray( buffer, offset ); |
|
|
| } |
|
|
| ], |
|
|
| SetterByBindingTypeAndVersioning: [ |
|
|
| [ |
| |
|
|
| function setValue_direct( buffer, offset ) { |
|
|
| this.targetObject[ this.propertyName ] = buffer[ offset ]; |
|
|
| }, |
|
|
| function setValue_direct_setNeedsUpdate( buffer, offset ) { |
|
|
| this.targetObject[ this.propertyName ] = buffer[ offset ]; |
| this.targetObject.needsUpdate = true; |
|
|
| }, |
|
|
| function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { |
|
|
| this.targetObject[ this.propertyName ] = buffer[ offset ]; |
| this.targetObject.matrixWorldNeedsUpdate = true; |
|
|
| } |
|
|
| ], [ |
|
|
| |
|
|
| function setValue_array( buffer, offset ) { |
|
|
| var dest = this.resolvedProperty; |
|
|
| for ( var i = 0, n = dest.length; i !== n; ++ i ) { |
|
|
| dest[ i ] = buffer[ offset ++ ]; |
|
|
| } |
|
|
| }, |
|
|
| function setValue_array_setNeedsUpdate( buffer, offset ) { |
|
|
| var dest = this.resolvedProperty; |
|
|
| for ( var i = 0, n = dest.length; i !== n; ++ i ) { |
|
|
| dest[ i ] = buffer[ offset ++ ]; |
|
|
| } |
|
|
| this.targetObject.needsUpdate = true; |
|
|
| }, |
|
|
| function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { |
|
|
| var dest = this.resolvedProperty; |
|
|
| for ( var i = 0, n = dest.length; i !== n; ++ i ) { |
|
|
| dest[ i ] = buffer[ offset ++ ]; |
|
|
| } |
|
|
| this.targetObject.matrixWorldNeedsUpdate = true; |
|
|
| } |
|
|
| ], [ |
|
|
| |
|
|
| function setValue_arrayElement( buffer, offset ) { |
|
|
| this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; |
|
|
| }, |
|
|
| function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { |
|
|
| this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; |
| this.targetObject.needsUpdate = true; |
|
|
| }, |
|
|
| function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { |
|
|
| this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; |
| this.targetObject.matrixWorldNeedsUpdate = true; |
|
|
| } |
|
|
| ], [ |
|
|
| |
|
|
| function setValue_fromArray( buffer, offset ) { |
|
|
| this.resolvedProperty.fromArray( buffer, offset ); |
|
|
| }, |
|
|
| function setValue_fromArray_setNeedsUpdate( buffer, offset ) { |
|
|
| this.resolvedProperty.fromArray( buffer, offset ); |
| this.targetObject.needsUpdate = true; |
|
|
| }, |
|
|
| function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { |
|
|
| this.resolvedProperty.fromArray( buffer, offset ); |
| this.targetObject.matrixWorldNeedsUpdate = true; |
|
|
| } |
|
|
| ] |
|
|
| ], |
|
|
| getValue: function getValue_unbound( targetArray, offset ) { |
|
|
| this.bind(); |
| this.getValue( targetArray, offset ); |
|
|
| |
| |
| |
| |
| |
|
|
| }, |
|
|
| setValue: function getValue_unbound( sourceArray, offset ) { |
|
|
| this.bind(); |
| this.setValue( sourceArray, offset ); |
|
|
| }, |
|
|
| |
| bind: function () { |
|
|
| var targetObject = this.node, |
| parsedPath = this.parsedPath, |
|
|
| objectName = parsedPath.objectName, |
| propertyName = parsedPath.propertyName, |
| propertyIndex = parsedPath.propertyIndex; |
|
|
| if ( ! targetObject ) { |
|
|
| targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; |
|
|
| this.node = targetObject; |
|
|
| } |
|
|
| |
| this.getValue = this._getValue_unavailable; |
| this.setValue = this._setValue_unavailable; |
|
|
| |
| if ( ! targetObject ) { |
|
|
| console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); |
| return; |
|
|
| } |
|
|
| if ( objectName ) { |
|
|
| var objectIndex = parsedPath.objectIndex; |
|
|
| |
| switch ( objectName ) { |
|
|
| case 'materials': |
|
|
| if ( ! targetObject.material ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); |
| return; |
|
|
| } |
|
|
| if ( ! targetObject.material.materials ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); |
| return; |
|
|
| } |
|
|
| targetObject = targetObject.material.materials; |
|
|
| break; |
|
|
| case 'bones': |
|
|
| if ( ! targetObject.skeleton ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); |
| return; |
|
|
| } |
|
|
| |
| |
|
|
| targetObject = targetObject.skeleton.bones; |
|
|
| |
| for ( var i = 0; i < targetObject.length; i ++ ) { |
|
|
| if ( targetObject[ i ].name === objectIndex ) { |
|
|
| objectIndex = i; |
| break; |
|
|
| } |
|
|
| } |
|
|
| break; |
|
|
| default: |
|
|
| if ( targetObject[ objectName ] === undefined ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); |
| return; |
|
|
| } |
|
|
| targetObject = targetObject[ objectName ]; |
|
|
| } |
|
|
|
|
| if ( objectIndex !== undefined ) { |
|
|
| if ( targetObject[ objectIndex ] === undefined ) { |
|
|
| console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); |
| return; |
|
|
| } |
|
|
| targetObject = targetObject[ objectIndex ]; |
|
|
| } |
|
|
| } |
|
|
| |
| var nodeProperty = targetObject[ propertyName ]; |
|
|
| if ( nodeProperty === undefined ) { |
|
|
| var nodeName = parsedPath.nodeName; |
|
|
| console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + |
| '.' + propertyName + ' but it wasn\'t found.', targetObject ); |
| return; |
|
|
| } |
|
|
| |
| var versioning = this.Versioning.None; |
|
|
| this.targetObject = targetObject; |
|
|
| if ( targetObject.needsUpdate !== undefined ) { |
|
|
| versioning = this.Versioning.NeedsUpdate; |
|
|
| } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { |
|
|
| versioning = this.Versioning.MatrixWorldNeedsUpdate; |
|
|
| } |
|
|
| |
| var bindingType = this.BindingType.Direct; |
|
|
| if ( propertyIndex !== undefined ) { |
|
|
| |
|
|
| if ( propertyName === "morphTargetInfluences" ) { |
|
|
| |
|
|
| |
| if ( ! targetObject.geometry ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); |
| return; |
|
|
| } |
|
|
| if ( targetObject.geometry.isBufferGeometry ) { |
|
|
| if ( ! targetObject.geometry.morphAttributes ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); |
| return; |
|
|
| } |
|
|
| for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) { |
|
|
| if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) { |
|
|
| propertyIndex = i; |
| break; |
|
|
| } |
|
|
| } |
|
|
|
|
| } else { |
|
|
| if ( ! targetObject.geometry.morphTargets ) { |
|
|
| console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.', this ); |
| return; |
|
|
| } |
|
|
| for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { |
|
|
| if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { |
|
|
| propertyIndex = i; |
| break; |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| bindingType = this.BindingType.ArrayElement; |
|
|
| this.resolvedProperty = nodeProperty; |
| this.propertyIndex = propertyIndex; |
|
|
| } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { |
|
|
| |
|
|
| bindingType = this.BindingType.HasFromToArray; |
|
|
| this.resolvedProperty = nodeProperty; |
|
|
| } else if ( Array.isArray( nodeProperty ) ) { |
|
|
| bindingType = this.BindingType.EntireArray; |
|
|
| this.resolvedProperty = nodeProperty; |
|
|
| } else { |
|
|
| this.propertyName = propertyName; |
|
|
| } |
|
|
| |
| this.getValue = this.GetterByBindingType[ bindingType ]; |
| this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; |
|
|
| }, |
|
|
| unbind: function () { |
|
|
| this.node = null; |
|
|
| |
| |
| this.getValue = this._getValue_unbound; |
| this.setValue = this._setValue_unbound; |
|
|
| } |
|
|
| } ); |
|
|
| |
| Object.assign( PropertyBinding.prototype, { |
|
|
| |
| _getValue_unbound: PropertyBinding.prototype.getValue, |
| _setValue_unbound: PropertyBinding.prototype.setValue, |
|
|
| } ); |
|
|
| export { PropertyBinding }; |
|
|