M3G 1.1 -- Jun 22, 2005

javax.microedition.m3g
Class AnimationController

java.lang.Object
  extended byjavax.microedition.m3g.Object3D
      extended byjavax.microedition.m3g.AnimationController

public class AnimationController
extends Object3D

Controls the position, speed and weight of an animation sequence.

In anything other than the simplest scenes, an animation sequence will require control of more than one property of more than one object. For example, a jointed figure performing a single gesture is usually thought of as a single animation, yet it involves the coordinated control of the position and orientation of many different objects.

We define an animation sequence to mean a set of individual AnimationTracks that are controlled by a single AnimationController. Each AnimationTrack object contains all the data required to control a single animatable property on one target object.

An AnimationController object enables its associated animation sequence as a whole to be paused, stopped, restarted, fast-forwarded, rewound, arbitrarily repositioned, or deactivated. More formally, it defines a linear mapping from world time to sequence time.

The detailed behaviour of how the data flows through the animation system as a whole is documented in the AnimationTrack class.

Animation application

In both immediate and retained mode, animations are explicitly applied to target objects by calling the animate method on the target Object3D itself. This re-evaluates the values of all object properties that have one or more animations attached. Animations are also applied to the children of the target object, so the application is free to choose between calling myWorld.animate to animate everything in myWorld at once, or applying animations to more fine-grained groups of objects individually.

Animation controllers have an active interval, specified by minimum and maximum world time values, during which the animation controller is active. Animations controlled by inactive animation controllers have no effect on their target objects and are simply ignored during animation application.

Animation weighting

Each animation controller has a weight associated with it. The contributions of all active animations targeting the same property at the same time are blended together by their respective weights. Formally, the value of a scalar property P as a function of weights wi and contributions Pi is:

For vector-valued properties, the above formula is applied for each vector component separately.

For most types of animation, the simple weighted sum as shown above is sufficient, but for orientation values the implementation is required to normalize the resulting quaternion. The quaternion must be normalized even if there is only one active animation controller, and that controller has unit weight, so that no actual weighting or blending takes place. Note also that the individual contributing quaternions Pi must not be normalized prior to weighting.

Timing and speed control

AnimationController specifies a linear mapping between world time, passed in to Object3D.animate, and sequence time that is used in sampling the associated keyframe data.

The sequence time is calculated directly from the given world time at each call to animate, instead of storing it internally. This is to avoid undesirable accumulation of rounding errors and any artifacts potentially resulting from that. It also simplifies the usage of the animation system by making it effectively stateless (as opposed to a traditional state machine design).

The mapping from world time to sequence time is parameterized by three constants, specified in AnimationController, and one variable, the world time, that is passed in to animate. The formula for calculating the sequence time ts corresponding to a given world time tw is:

where

The reference point (twref, tsref) is specified with the setPosition method and the speed with the setSpeed method (note that setting the speed may also change the reference point).

Sequence time can be visualized, in a coordinate system where world time is on the horizontal and sequence time on the vertical axis, as a line having slope s and passing through the point (twref, tsref).

As an example of the relationship between world time and sequence time, imagine a world where the current time is 5000 milliseconds since the start. An animation was started (from 0 ms sequence time) at 3000 ms, running at half speed. The animation was started 2000 ms ago, but because the speed is 0.5, the actual required sequence time tsref is 1000 ms. Here, we would have tw = 5000 ms, twref = 3000 ms, tsref = 0 ms, and s = 0.5 in the formula above.

Note that the unit of time is not explicitly specified anywhere in the API or the file format. It is strongly recommended that applications and content creation tools express times in milliseconds by default. Arbitrary units can, however, be used in specific applications if mandated by range or precision requirements.

Synchronized animation

We assume that synchronization of animation with other media types is only based on the world time passed from the controlling application. No synchronization events or other mechanisms are provided for this purpose. In the case of synchronizing animation to music, for example, the current elapsed time is often available directly from the music player library.

Example usage

As an example of using animation, consider a case where we want a light source to pulsate between red and green, moving along a curved path. In both immediate and retained mode, this involves creating keyframe sequences and associating them with the light node, as illustrated in Example 1 below.

To apply the animation to the light object in our rendering loop, we must call the animate method, as shown in Example 2.

See Also:
Binary format, AnimationTrack, KeyframeSequence, Object3D
Examples:
(1) Creating an animation.
Light light = new Light();	// Create a light node

// Load a motion path from a stream, assuming it's the first
// object there

Object3D[] objects = Loader.load("http://www.ex.com/ex.m3g");
KeyframeSequence motion = (KeyframeSequence) objects[0];

// Create a color keyframe sequence, with keyframes at 0 ms
// and 500 ms, and a total duration of 1000 ms. The animate
// method will throw an exception if it encounters a
// KeyframeSequence whose duration has not been set or whose
// keyframes are out of order. Note that the Loader
// automatically validates any sequences that are loaded from
// a file.

KeyframeSequence blinking = new KeyframeSequence(2, 3, 
                                      KeyframeSequence.LINEAR);
blinking.setKeyframe(0,   0, new float[] { 1.0f, 0.0f, 0.0f });
blinking.setKeyframe(1, 500, new float[] { 0.0f, 1.0f, 0.0f });
blinking.setDuration(1000);

AnimationTrack blink = new AnimationTrack(blinking, 
                                         AnimationTrack.COLOR);
AnimationTrack move = new AnimationTrack(motion, 
                                   AnimationTrack.TRANSLATION);
light.addAnimationTrack(blink);
light.addAnimationTrack(move);

// Create an AnimationController and make it control both the
// blinking and the movement of our light

AnimationController lightAnim = new AnimationController();
blink.setController(lightAnim);
move.setController(lightAnim);

// Start the animation when world time reaches 2 seconds, stop
// at 5 s.  There is only one reference point for this
// animation: sequence time must be zero at world time 2000
// ms. The animation will be running at normal speed (1.0, the
// default).

lightAnim.setActiveInterval(2000, 5000);
lightAnim.setPosition(0, 2000);
(2) Applying the animation during rendering.
appTime += 30;		// advance time by 30 ms each frame
light.animate(appTime);

// Assume 'myGraphics3D' is the Graphics3D object we draw into.
// In immediate mode, node transforms are ignored, so we get
// our animated transformation into a local Transform object,
// "lightToWorld". As its name implies, the transformation is
// from the Light node's local coordinates to world space.

light.getTransform(lightToWorld);
myGraphics3D.resetLights();
myGraphics3D.addLight(light, lightToWorld);

Constructor Summary
AnimationController()
          Creates a new AnimationController object.
 
Method Summary
 int getActiveIntervalEnd()
          Retrieves the ending time of the current active interval of this animation controller, in world time units.
 int getActiveIntervalStart()
          Retrieves the starting time of the current active interval of this animation controller, in world time units.
 float getPosition(int worldTime)
          Retrieves the sequence time that corresponds to the given world time.
 int getRefWorldTime()
          Returns the current reference world time.
 float getSpeed()
          Retrieves the currently set playback speed of this animation controller.
 float getWeight()
          Retrieves the currently set blending weight for this animation controller.
 void setActiveInterval(int start, int end)
          Sets the world time interval during which this animation controller is active.
 void setPosition(float sequenceTime, int worldTime)
          Sets a new playback position, relative to world time, for this animation controller.
 void setSpeed(float speed, int worldTime)
          Sets a new playback speed for this animation.
 void setWeight(float weight)
          Sets the blending weight for this animation controller.
 
Methods inherited from class javax.microedition.m3g.Object3D
addAnimationTrack, animate, duplicate, find, getAnimationTrack, getAnimationTrackCount, getReferences, getUserID, getUserObject, removeAnimationTrack, setUserID, setUserObject
   

Constructor Detail

AnimationController

public AnimationController()

Creates a new AnimationController object. The default values for the new object are:

Method Detail

setActiveInterval

public void setActiveInterval(int start,
                              int end)

Sets the world time interval during which this animation controller is active.

This animation controller will subsequently be active when the world time t is such that start <= t < end, and inactive outside of that range. As a special case, if start and end are set to the same value, this animation controller is always active.

Note that changing the active interval has no effect on the mapping from world time to sequence time.

Parameters:
start - the starting time of the active interval, in world time units (inclusive)
end - the ending time of the active interval, in world time units (exclusive)
Throws:
java.lang.IllegalArgumentException - if start > end
See Also:
getActiveIntervalStart, getActiveIntervalEnd

getActiveIntervalStart

public int getActiveIntervalStart()

Retrieves the starting time of the current active interval of this animation controller, in world time units. The value returned is the same that was last set with setActiveInterval, or if it has not been called yet, the default value set at construction.

Returns:
the starting time of the active interval
See Also:
setActiveInterval

getActiveIntervalEnd

public int getActiveIntervalEnd()

Retrieves the ending time of the current active interval of this animation controller, in world time units. The value returned is the same that was last set with setActiveInterval, or if it has not been called yet, the default value set at construction.

Returns:
the ending time of the active interval
See Also:
setActiveInterval

setSpeed

public void setSpeed(float speed,
                     int worldTime)

Sets a new playback speed for this animation. The speed is set as a factor of the nominal speed of the animation: 1.0 is normal playback speed (as specified by the keyframe times in the associated animation tracks), 2.0 is double speed, and -1.0 is reverse playback at normal speed. A speed of 0.0 freezes the animation.

The speed setting effectively specifies how much to advance the internal playback position of this animation for a given increment in the global world time.

The internal reference point is modified so that sequence time at the given world time remains unchanged. This allows the application to change the speed without causing the animation to "jump" forward or backward. To get the desired effect, the application should pass its current world time to this method. This is the time that the application has most recently used in animate, or the time that it is next going to use.

The reference point (twref, tsref) and speed (s) are updated based on the given world time and speed as follows:

Note that the computation of the new reference sequence time takes place before updating the speed. See the class description for the formula that getPosition uses, and for more discussion on animation timing.

Parameters:
speed - new playback speed; 1.0 is normal speed
worldTime - reference world time; the value of sequence time at this point will remain constant during the speed change
See Also:
getSpeed

getSpeed

public float getSpeed()

Retrieves the currently set playback speed of this animation controller.

Returns:
the current playback speed
See Also:
setSpeed

setPosition

public void setPosition(float sequenceTime,
                        int worldTime)

Sets a new playback position, relative to world time, for this animation controller. This sets the internal reference point (twref, tsref) to (worldTime, sequenceTime) to shift the animation to the new position.

Parameters:
sequenceTime - the desired playback position in sequence time units
worldTime - the world time at which the sequence time must be equal to sequenceTime
See Also:
getPosition

getPosition

public float getPosition(int worldTime)

Retrieves the sequence time that corresponds to the given world time. The returned value is computed with the formula given in the class description. Note that because the result may be a fractional number, it is returned as a float, not integer.

Parameters:
worldTime - world time to get the corresponding sequence time of
Returns:
animation sequence position in number of time units elapsed since the beginning of this animation, until worldTime
See Also:
setPosition

getRefWorldTime

public int getRefWorldTime()
Returns the current reference world time.

Returns:
the current reference world time
Since:
M3G 1.1
See Also:
setPosition

setWeight

public void setWeight(float weight)

Sets the blending weight for this animation controller. The blending weight must be positive or zero. Setting the weight to zero disables this animation controller; that is, the controller is subsequently not active even within its active range. If the weight is non-zero, the animations controlled by this controller contribute to their target properties as described in the class description.

Parameters:
weight - the new blending weight
Throws:
java.lang.IllegalArgumentException - if weight < 0
See Also:
getWeight

getWeight

public float getWeight()

Retrieves the currently set blending weight for this animation controller.

Returns:
the current blending weight
See Also:
setWeight

M3G 1.1 -- Jun 22, 2005

Copyright © 2005 Nokia Corporation. See the Copyright Notice for details.