PropertyBinding.js 13.6 KB
Newer Older
1 2
/**
 *
3 4 5
 * A reference to a real property in the scene graph.
 *
 *
6 7
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
8
 * @author tschw
9 10
 */

T
tschw 已提交
11
THREE.PropertyBinding = function ( rootNode, path, parsedPath ) {
12

13
	this.path = path;
T
tschw 已提交
14 15
	this.parsedPath = parsedPath ||
			THREE.PropertyBinding.parseTrackName( path );
16

T
tschw 已提交
17 18
	this.node = THREE.PropertyBinding.findNode(
			rootNode, this.parsedPath.nodeName ) || rootNode;
19

T
tschw 已提交
20
	this.rootNode = rootNode;
21

22 23
};

24
THREE.PropertyBinding.prototype = {
25

26
	constructor: THREE.PropertyBinding,
27

28 29 30 31
	getValue: function getValue_unbound( targetArray, offset ) {

		this.bind();
		this.getValue( targetArray, offset );
32

33 34 35 36 37
		// Note: This class uses a State pattern on a per-method basis:
		// 'bind' sets 'this.getValue' / 'setValue' and shadows the
		// prototype version of these methods with one that represents
		// the bound state. When the property is not found, the methods
		// become no-ops.
38

39 40
	},

41
	setValue: function getValue_unbound( sourceArray, offset ) {
B
Ben Houston 已提交
42

43 44
		this.bind();
		this.setValue( sourceArray, offset );
45

46
	},
B
Ben Houston 已提交
47

48 49
	// create getter / setter pair for a property in the scene graph
	bind: function() {
50

T
tschw 已提交
51 52 53 54 55 56
		var targetObject = this.node,
			parsedPath = this.parsedPath,

			objectName = parsedPath.objectName,
			propertyName = parsedPath.propertyName,
			propertyIndex = parsedPath.propertyIndex;
B
Ben Houston 已提交
57

58
		if ( ! targetObject ) {
59

T
tschw 已提交
60 61
			targetObject = THREE.PropertyBinding.findNode(
					this.rootNode, parsedPath.nodeName ) || this.rootNode;
62

63
			this.node = targetObject;
64

65
		}
66

67 68 69
		// set fail state so we can just 'return' on error
		this.getValue = this._getValue_unavailable;
		this.setValue = this._setValue_unavailable;
70

71
 		// ensure there is a value node
T
tschw 已提交
72 73
		if ( ! targetObject ) {

74
			console.error( "  trying to update node for track: " + this.path + " but it wasn't found." );
75
			return;
T
tschw 已提交
76

77
		}
78

79
		if ( objectName ) {
80

T
tschw 已提交
81 82 83 84 85 86 87
			var objectIndex = parsedPath.objectIndex;

			// special cases were we need to reach deeper into the hierarchy to get the face materials....
			switch ( objectName ) {

				case 'materials':

88
					if ( ! targetObject.material ) {
T
tschw 已提交
89 90 91

						console.error( '  can not bind to material as node does not have a material', this );
						return;
92

93 94
					}

95
					if ( ! targetObject.material.materials ) {
T
tschw 已提交
96 97 98 99 100 101 102 103 104 105 106 107

						console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
						return;

					}

					targetObject = targetObject.material.materials;

					break;

				case 'bones':

108
					if ( ! targetObject.skeleton ) {
T
tschw 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122

						console.error( '  can not bind to bones as node does not have a skeleton', this );
						return;

					}

					// potential future optimization: skip this if propertyIndex is already an integer
					// and convert the integer string to a true integer.

					targetObject = targetObject.skeleton.bones;

					// support resolving morphTarget names into indices.
					for ( var i = 0; i < targetObject.length; i ++ ) {

123
						if ( targetObject[ i ].name === objectIndex ) {
T
tschw 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

							objectIndex = i;
							break;

						}

					}

					break;

				default:

					if ( targetObject[ objectName ] === undefined ) {

						console.error( '  can not bind to objectName of node, undefined', this );
						return;

					}

					targetObject = targetObject[ objectName ];

145
			}
146

T
tschw 已提交
147 148 149

			if ( objectIndex !== undefined ) {

150
				if ( targetObject[ objectIndex ] === undefined ) {
T
tschw 已提交
151

152
					console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
153
					return;
T
tschw 已提交
154

155
				}
156

T
tschw 已提交
157 158
				targetObject = targetObject[ objectIndex ];

159 160
			}

161
		}
162

T
tschw 已提交
163 164 165
		// resolve property
		var nodeProperty = targetObject[ propertyName ];

166
		if ( nodeProperty === undefined ) {
T
tschw 已提交
167 168 169 170 171

			var nodeName = parsedPath.nodeName;

			console.error( "  trying to update property for track: " + nodeName +
					'.' + propertyName + " but it wasn't found.", targetObject );
172
			return;
T
tschw 已提交
173

174
		}
175

176
		// determine versioning scheme
T
tschw 已提交
177
		var versioning = this.Versioning.None;
178

T
tschw 已提交
179
		if ( targetObject.needsUpdate !== undefined ) { // material
180

T
tschw 已提交
181 182
			versioning = this.Versioning.NeedsUpdate;
			this.targetObject = targetObject;
183

T
tschw 已提交
184
		} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
185

T
tschw 已提交
186 187
			versioning = this.Versioning.MatrixWorldNeedsUpdate;
			this.targetObject = targetObject;
188 189 190

		}

T
tschw 已提交
191 192 193 194 195
		// determine how the property gets bound
		var bindingType = this.BindingType.Direct;

		if ( propertyIndex !== undefined ) {
			// access a sub element of the property array (only primitives are supported right now)
196

T
tschw 已提交
197
			if ( propertyName === "morphTargetInfluences" ) {
B
Ben Houston 已提交
198
				// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
199

200
				// support resolving morphTarget names into indices.
T
tschw 已提交
201 202
				if ( ! targetObject.geometry ) {

203
					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
T
tschw 已提交
204 205
					return;

206
				}
T
tschw 已提交
207 208 209

				if ( ! targetObject.geometry.morphTargets ) {

210
					console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
T
tschw 已提交
211 212
					return;

213
				}
214

T
tschw 已提交
215 216
				for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {

217
					if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
T
tschw 已提交
218 219

						propertyIndex = i;
220
						break;
T
tschw 已提交
221

222
					}
T
tschw 已提交
223

224
				}
T
tschw 已提交
225

226
			}
227

T
tschw 已提交
228
			bindingType = this.BindingType.ArrayElement;
229

T
tschw 已提交
230 231
			this.resolvedProperty = nodeProperty;
			this.propertyIndex = propertyIndex;
232

T
tschw 已提交
233 234
		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
			// must use copy for Object3D.Euler/Quaternion
235

T
tschw 已提交
236
			bindingType = this.BindingType.HasFromToArray;
237

T
tschw 已提交
238
			this.resolvedProperty = nodeProperty;
239

240
		} else if ( nodeProperty.length !== undefined ) {
T
tschw 已提交
241 242 243 244 245

			bindingType = this.BindingType.EntireArray;

			this.resolvedProperty = nodeProperty;

T
tschw 已提交
246
		} else {
247

T
tschw 已提交
248
			this.propertyName = propertyName;
249

T
tschw 已提交
250
		}
251

T
tschw 已提交
252 253 254
		// select getter / setter
		this.getValue = this.GetterByBindingType[ bindingType ];
		this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
255

T
tschw 已提交
256
	},
257

T
tschw 已提交
258
	unbind: function() {
259

T
tschw 已提交
260
		this.node = null;
261

T
tschw 已提交
262 263 264 265
		// back to the prototype version of getValue / setValue
		// note: avoiding to mutate the shape of 'this' via 'delete'
		this.getValue = this._getValue_unbound;
		this.setValue = this._setValue_unbound;
266

T
tschw 已提交
267
	}
268

T
tschw 已提交
269
};
270

T
tschw 已提交
271
Object.assign( THREE.PropertyBinding.prototype, { // prototype, continued
272

T
tschw 已提交
273 274 275
	// these are used to "bind" a nonexistent property
	_getValue_unavailable: function() {},
	_setValue_unavailable: function() {},
276

T
tschw 已提交
277 278 279
	// initial state of these methods that calls 'bind'
	_getValue_unbound: THREE.PropertyBinding.prototype.getValue,
	_setValue_unbound: THREE.PropertyBinding.prototype.setValue,
280

T
tschw 已提交
281 282
	BindingType: {
		Direct: 0,
T
tschw 已提交
283 284 285
		EntireArray: 1,
		ArrayElement: 2,
		HasFromToArray: 3
T
tschw 已提交
286
	},
287

T
tschw 已提交
288 289 290 291 292
	Versioning: {
		None: 0,
		NeedsUpdate: 1,
		MatrixWorldNeedsUpdate: 2
	},
293

T
tschw 已提交
294
	GetterByBindingType: [
295

T
tschw 已提交
296
		function getValue_direct( buffer, offset ) {
297

T
tschw 已提交
298
			buffer[ offset ] = this.node[ this.propertyName ];
299

T
tschw 已提交
300
		},
301

T
tschw 已提交
302 303
		function getValue_array( buffer, offset ) {

304
			var source = this.resolvedProperty;
T
tschw 已提交
305 306 307 308 309 310 311 312 313

			for ( var i = 0, n = source.length; i !== n; ++ i ) {

				buffer[ offset ++ ] = source[ i ];

			}

		},

T
tschw 已提交
314
		function getValue_arrayElement( buffer, offset ) {
315

T
tschw 已提交
316
			buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];
317

T
tschw 已提交
318
		},
319

T
tschw 已提交
320
		function getValue_toArray( buffer, offset ) {
321

T
tschw 已提交
322
			this.resolvedProperty.toArray( buffer, offset );
323

T
tschw 已提交
324
		}
325

T
tschw 已提交
326
	],
327

T
tschw 已提交
328
	SetterByBindingTypeAndVersioning: [
329

T
tschw 已提交
330 331
		[
			// Direct
332

T
tschw 已提交
333
			function setValue_direct( buffer, offset ) {
334

T
tschw 已提交
335
				this.node[ this.propertyName ] = buffer[ offset ];
336

T
tschw 已提交
337
			},
338

T
tschw 已提交
339
			function setValue_direct_setNeedsUpdate( buffer, offset ) {
340

T
tschw 已提交
341 342 343 344 345 346 347 348 349
				this.node[ this.propertyName ] = buffer[ offset ];
				this.targetObject.needsUpdate = true;

			},

			function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {

				this.node[ this.propertyName ] = buffer[ offset ];
				this.targetObject.matrixWorldNeedsUpdate = true;
T
tschw 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

			}

		], [

			// EntireArray

			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;
394 395 396

			}

T
tschw 已提交
397
		], [
398

T
tschw 已提交
399
			// ArrayElement
400

T
tschw 已提交
401
			function setValue_arrayElement( buffer, offset ) {
402

T
tschw 已提交
403
				this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
404

T
tschw 已提交
405
			},
406

T
tschw 已提交
407
			function setValue_arrayElement_setNeedsUpdate( buffer, offset ) {
408

T
tschw 已提交
409 410
				this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
				this.targetObject.needsUpdate = true;
411

T
tschw 已提交
412
			},
413

T
tschw 已提交
414
			function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {
415

T
tschw 已提交
416 417
				this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
				this.targetObject.matrixWorldNeedsUpdate = true;
418

T
tschw 已提交
419
			}
B
Ben Houston 已提交
420

T
tschw 已提交
421
		], [
422

T
tschw 已提交
423
			// HasToFromArray
424

T
tschw 已提交
425
			function setValue_fromArray( buffer, offset ) {
426

T
tschw 已提交
427
				this.resolvedProperty.fromArray( buffer, offset );
428

T
tschw 已提交
429
			},
430

T
tschw 已提交
431
			function setValue_fromArray_setNeedsUpdate( buffer, offset ) {
432

T
tschw 已提交
433 434
				this.resolvedProperty.fromArray( buffer, offset );
				this.targetObject.needsUpdate = true;
435

T
tschw 已提交
436
			},
437

T
tschw 已提交
438 439 440 441
			function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {

				this.resolvedProperty.fromArray( buffer, offset );
				this.targetObject.matrixWorldNeedsUpdate = true;
442

443
			}
B
Ben Houston 已提交
444

T
tschw 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
		]

	]

} );

THREE.PropertyBinding.Composite =
		function( targetGroup, path, optionalParsedPath ) {

	var parsedPath = optionalParsedPath ||
			THREE.PropertyBinding.parseTrackName( path );

	this._targetGroup = targetGroup;
	this._bindings = targetGroup.subscribe_( path, parsedPath );

};

THREE.PropertyBinding.Composite.prototype = {

	constructor: THREE.PropertyBinding.Composite,

	getValue: function( array, offset ) {

		this.bind(); // bind all binding

		var firstValidIndex = this._targetGroup.nCachedObjects_,
			binding = this._bindings[ firstValidIndex ];

		// and only call .getValue on the first
		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();

500
		}
501 502 503 504 505

	},

	unbind: function() {

T
tschw 已提交
506
		var bindings = this._bindings;
507

T
tschw 已提交
508 509 510 511 512 513
		for ( var i = this._targetGroup.nCachedObjects_,
				n = bindings.length; i !== n; ++ i ) {

			bindings[ i ].unbind();

		}
514

515 516 517 518
	}

};

T
tschw 已提交
519
THREE.PropertyBinding.create = function( root, path, parsedPath ) {
520

T
tschw 已提交
521
	if ( ! ( root instanceof THREE.AnimationObjectGroup ) ) {
522

T
tschw 已提交
523
		return new THREE.PropertyBinding( root, path, parsedPath );
524

T
tschw 已提交
525 526 527 528 529 530 531
	} else {

		return new THREE.PropertyBinding.Composite( root, path, parsedPath );

	}

};
532

533
THREE.PropertyBinding.parseTrackName = function( trackName ) {
534 535

	// matches strings in the form of:
536 537
	//    nodeName.property
	//    nodeName.property[accessor]
538
	//    nodeName.material.property[accessor]
539
	//    uuid.property[accessor]
540
	//    uuid.objectName[objectIndex].propertyName[propertyIndex]
541 542
	//    parentName/nodeName.property
	//    parentName/parentName/nodeName.property[index]
543
	//	  .bone[Armature.DEF_cog].position
544 545
	// created and tested via https://regex101.com/#javascript

C
Coder 已提交
546
	var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_.:\- ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/;
547 548 549
	var matches = re.exec( trackName );

	if ( ! matches ) {
550 551

		throw new Error( "cannot parse trackName at all: " + trackName );
552

553 554
	}

555 556 557 558 559
	if ( matches.index === re.lastIndex ) {

		re.lastIndex++;

	}
560

561
	var results = {
562 563 564 565 566 567
		// directoryName: matches[ 1 ], // (tschw) currently unused
		nodeName: matches[ 3 ], 	// allowed to be null, specified root node.
		objectName: matches[ 5 ],
		objectIndex: matches[ 7 ],
		propertyName: matches[ 9 ],
		propertyIndex: matches[ 11 ]	// allowed to be null, specifies that the whole property is set.
568 569
	};

570 571
	if ( results.propertyName === null || results.propertyName.length === 0 ) {

572
		throw new Error( "can not parse propertyName from trackName: " + trackName );
573

574 575 576
	}

	return results;
577 578 579

};

580
THREE.PropertyBinding.findNode = function( root, nodeName ) {
581

582
	if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
583

T
tschw 已提交
584
		return root;
585

T
tschw 已提交
586
	}
587

T
tschw 已提交
588
	// search into skeleton bones.
589
	if ( root.skeleton ) {
590

T
tschw 已提交
591
		var searchSkeleton = function( skeleton ) {
592

T
tschw 已提交
593
			for( var i = 0; i < skeleton.bones.length; i ++ ) {
594

595
				var bone = skeleton.bones[ i ];
596

597
				if ( bone.name === nodeName ) {
598

T
tschw 已提交
599
					return bone;
600

T
tschw 已提交
601 602
				}
			}
603

T
tschw 已提交
604
			return null;
605

T
tschw 已提交
606
		};
607

T
tschw 已提交
608
		var bone = searchSkeleton( root.skeleton );
609

610
		if ( bone ) {
M
Mr.doob 已提交
611

T
tschw 已提交
612
			return bone;
613 614

		}
T
tschw 已提交
615
	}
616

T
tschw 已提交
617
	// search into node subtree.
618
	if ( root.children ) {
619

T
tschw 已提交
620
		var searchNodeSubtree = function( children ) {
621

T
tschw 已提交
622
			for( var i = 0; i < children.length; i ++ ) {
623

624
				var childNode = children[ i ];
625

626
				if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
627

T
tschw 已提交
628
					return childNode;
629

T
tschw 已提交
630
				}
631

T
tschw 已提交
632
				var result = searchNodeSubtree( childNode.children );
633

634
				if ( result ) return result;
635

T
tschw 已提交
636
			}
637

638
			return null;
639

T
tschw 已提交
640
		};
641 642 643

		var subTreeNode = searchNodeSubtree( root.children );

644
		if ( subTreeNode ) {
645 646 647 648 649 650 651 652

			return subTreeNode;

		}

	}

	return null;
T
tschw 已提交
653

M
Michael Herzog 已提交
654
};