Przeglądaj źródła

AnimationMixer: Move validate/optimize call, allow opt-out.

Don McCurdy 7 lat temu
rodzic
commit
9d3a8c2a1c

+ 7 - 1
docs/api/animation/AnimationClip.html

@@ -69,7 +69,7 @@
 		<h3>[method:AnimationClip optimize]()</h3>
 		<p>
 			Optimizes each track by removing equivalent sequential keys (which are common in morph target
-			sequences).
+			sequences). Called automatically by [page:AnimationMixer]'s clipAction() method.
 		</p>
 
 		<h3>[method:null resetDuration]()</h3>
@@ -83,6 +83,12 @@
 			Trims all tracks to the clip's duration.
 		</p>
 
+		<h3>[method:AnimationClip validate]()</h3>
+		<p>
+			Performs minimal validation on each track in the clip. Called automatically by
+			[page:AnimationMixer]'s clipAction() method.
+		</p>
+
 
 		<h2>Static Methods</h2>
 

+ 6 - 4
docs/api/animation/AnimationMixer.html

@@ -49,16 +49,18 @@
 		<h2>Methods</h2>
 
 
-		<h3>[method:AnimationAction clipAction]([param:AnimationClip clip], [param:Object3D optionalRoot])</h3>
+		<h3>[method:AnimationAction clipAction]([param:AnimationClip clip], [param:Object3D optionalRoot], [param:Boolean needsValidateAndOptimize])</h3>
 		<p>
 			Returns an [page:AnimationAction] for the passed clip, optionally using a root object different
 			from the mixer's default root. The first parameter can be either an [page:AnimationClip] object
 			or the name of an AnimationClip.<br /><br />
 
-			If an action fitting these parameters doesn't yet exist, it will be created by this method.<br /><br />
+			Automatically calls .validate() and .optimize() on the clip, unless 'needsValidateAndOptimize'
+			parameter is set to false.<br /><br />
 
-			Note: Calling this method several times with the same parameters returns always the same clip
-			instance.
+			If an action fitting the clip and root parameters doesn't yet exist, it will be created by
+			this method. Calling this method several times with the same clip and root parameters always
+			returns the same clip instance.
 		</p>
 
 		<h3>[method:AnimationAction existingAction]([param:AnimationClip clip], [param:Object3D optionalRoot])</h3>

+ 18 - 3
docs/api/animation/KeyframeTrack.html

@@ -91,6 +91,16 @@
 		<h2>Properties</h2>
 
 
+		<h3>[property:Boolean isOptimized]</h3>
+		<p>
+			Whether the track has been optimized with .optimize().
+		</p>
+
+		<h3>[property:Boolean isValidated]</h3>
+		<p>
+			Whether the track has been successfully validated with .validate().
+		</p>
+
 		<h3>[property:String name]</h3>
 		<p>
 			The track's name can refer to [page:Geometry.morphTargets morph targets] or
@@ -204,8 +214,8 @@
 
 		<h3>[method:null optimize]()</h3>
 		<p>
-			Removes equivalent sequential keys, which are common in morph target sequences. Called
-			automatically by the constructor.
+			Removes equivalent sequential keys, which are common in morph target sequences. Calling this
+			method will set [page:.isOptimized isOptimized] to true, and additional calls will do nothing.
 		</p>
 
 		<h3>[method:null scale]()</h3>
@@ -236,7 +246,12 @@
 
 		<h3>[method:null validate]()</h3>
 		<p>
-			Performs minimal validation on the tracks. Called automatically by the constructor.<br /><br />
+			Performs minimal validation on the tracks.  Calling this method will set
+			[page:.isValidated isValidated] to true if validation succeeds, and additional calls will do
+			nothing.
+		</p>
+
+		<p>
 			This method logs errors to the console, if a track is empty, if the [page:.valueSize value size] is not valid, if an item
 			in the [page:.times times] or [page:.values values] array is not a valid number or if the items in the *times* array are out of order.
 		</p>

+ 12 - 2
src/animation/AnimationClip.js

@@ -28,8 +28,6 @@ function AnimationClip( name, duration, tracks ) {
 
 	}
 
-	this.optimize();
-
 }
 
 Object.assign( AnimationClip, {
@@ -341,6 +339,18 @@ Object.assign( AnimationClip.prototype, {
 
 	},
 
+	validate: function () {
+
+		for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+			this.tracks[ i ].validate();
+
+		}
+
+		return this;
+
+	},
+
 	optimize: function () {
 
 		for ( var i = 0; i < this.tracks.length; i ++ ) {

+ 8 - 1
src/animation/AnimationMixer.js

@@ -518,7 +518,7 @@ AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototy
 	// return an action for a clip optionally using a custom root target
 	// object (this method allocates a lot of dynamic memory in case a
 	// previously unknown clip/root combination is specified)
-	clipAction: function ( clip, optionalRoot ) {
+	clipAction: function ( clip, optionalRoot, needsValidateAndOptimize ) {
 
 		var root = optionalRoot || this._root,
 			rootUuid = root.uuid,
@@ -555,6 +555,13 @@ AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototy
 		// clip must be known when specified via string
 		if ( clipObject === null ) return null;
 
+		if ( needsValidateAndOptimize !== false ) {
+
+			clipObject.validate();
+			clipObject.optimize();
+
+		}
+
 		// allocate all resources required to run it
 		var newAction = new AnimationAction( this, clipObject, optionalRoot );
 

+ 11 - 3
src/animation/KeyframeTrack.js

@@ -34,10 +34,10 @@ function KeyframeTrack( name, times, values, interpolation ) {
 	this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
 	this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
 
-	this.setInterpolation( interpolation || this.DefaultInterpolation );
+	this.isValidated = false;
+	this.isOptimized = false;
 
-	this.validate();
-	this.optimize();
+	this.setInterpolation( interpolation || this.DefaultInterpolation );
 
 }
 
@@ -353,6 +353,8 @@ Object.assign( KeyframeTrack.prototype, {
 	// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
 	validate: function () {
 
+		if ( this.isValidated ) return true;
+
 		var valid = true;
 
 		var valueSize = this.getValueSize();
@@ -423,6 +425,8 @@ Object.assign( KeyframeTrack.prototype, {
 
 		}
 
+		this.isValidated = valid;
+
 		return valid;
 
 	},
@@ -431,6 +435,8 @@ Object.assign( KeyframeTrack.prototype, {
 	// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
 	optimize: function () {
 
+		if ( this.isOptimized ) return this;
+
 		var times = this.times,
 			values = this.values,
 			stride = this.getValueSize(),
@@ -529,6 +535,8 @@ Object.assign( KeyframeTrack.prototype, {
 
 		}
 
+		this.isOptimized = true;
+
 		return this;
 
 	}

+ 6 - 0
test/unit/src/animation/AnimationClip.tests.js

@@ -72,6 +72,12 @@ export default QUnit.module( 'Animation', () => {
 
 		} );
 
+		QUnit.todo( "validate", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
 	} );
 
 } );

+ 25 - 4
test/unit/src/animation/KeyframeTrack.tests.js

@@ -4,6 +4,7 @@
 /* global QUnit */
 
 import { KeyframeTrack } from '../../../../src/animation/KeyframeTrack';
+import { NumberKeyframeTrack } from '../../../../src/animation/tracks/NumberKeyframeTrack';
 
 export default QUnit.module( 'Animation', () => {
 
@@ -108,15 +109,35 @@ export default QUnit.module( 'Animation', () => {
 
 		} );
 
-		QUnit.todo( "validate", ( assert ) => {
+		QUnit.test( 'validate', ( assert ) => {
 
-			assert.ok( false, "everything's gonna be alright" );
+			var track = new NumberKeyframeTrack( '.material.opacity', [ 0, 1 ], [ 0, NaN ] );
+
+			track.isValidated = true;
+			assert.ok( track.validate() );
+			assert.ok( track.isValidated );
+
+			track.isValidated = false;
+			assert.notOk( track.validate() );
+			assert.notOk( track.isValidated );
 
 		} );
 
-		QUnit.todo( "optimize", ( assert ) => {
+		QUnit.test( 'optimize', ( assert ) => {
 
-			assert.ok( false, "everything's gonna be alright" );
+			var track = new NumberKeyframeTrack( '.material.opacity', [ 0, 1, 2, 3, 4 ], [ 0, 0, 0, 0, 1 ] );
+
+			assert.equal( track.values.length, 5 );
+
+			track.isOptimized = true;
+			track.optimize();
+
+			assert.equal( track.values.length, 5 );
+
+			track.isOptimized = false;
+			track.optimize();
+
+			assert.equal( track.values.length, 3 );
 
 		} );