Microsoft Corporation
September 1997
This paper describes Microsoft® DirectAnimation™, the component of the DirectX® API family that provides animation and integrated media support for Web pages, CD-ROM titles, and multimedia applications. DirectAnimation supports many media types, including two-dimensional (2-D) vector graphics, 3-D graphics, sprites, audio, and video. It also provides a timing and event model that lets you synchronize events, and can be applied uniformly across different media types.
DirectAnimation can be used both as a stand alone application programming Interface (API) and as an integrated component of the Microsoft Internet Explorer 4.0 minimal install. This provides unprecedented animation and multimedia capabilities built into a major Internet browser (and no special download is needed). DirectAnimation is integrated with Dynamic Hypertext Markup Language (DHTML), and hence is especially suitable for adding compact, lightweight animation effects to Web pages.
The DirectAnimation Programming Model
Units of Measurement and Coordinate Systems
Model Construction and Performance Issues
Microsoft DirectAnimation can be used both as a stand-alone API and as an integrated component of the Microsoft Internet Explorer 4.0 minimal install. This provides unprecedented animation and multimedia capabilities built into a major Internet browser. DirectAnimation is integrated with DHTML, and hence is especially suitable for adding compact, lightweight animation effects to Web pages.
DirectAnimation is the component of the DirectX API family that provides animation and integrated media support for Web pages, CD-ROM titles, and multimedia applications. DirectAnimation supports many media types, including 2-D vector graphics, 3-D graphics, sprites, audio, and video. It also provides a timing and event model that lets you synchronize events, and can be applied uniformly across different media types.
The Component Object Model–based (COM-based) DirectAnimation API and run time can be accessed in different ways by different user groups. HTML authors can integrate animation using the DirectAnimation multimedia controls. Developers using Microsoft Visual Basic®, Scripting Edition (VBScript), JScript™, or Java applets can program animations for Web pages with DHTML integration. And if you're working with Java, Visual Basic, or C++, you can develop interactive ActiveX™ controls or applications. Although the examples here are written in JScript, the concepts are equivalent for the other supported languages.
If you want to develop a multimedia title or integrate different media types in your application, you'll soon find that this is fairly demanding of both time and effort. Traditionally, there are four classes of multimedia/animation API developed by four different communities: 2-D vector graphics, sprite animation, 3-D graphics, and audio/video. Not only do you have to be proficient in each of these APIs for the different media types (with their different programming models), but you also have to integrate the different outputs and provide a run-time engine that manages and controls the animation. On top of that, making the content ready for deployment on the Web (using the Web as either the primary platform or as a teaser for a title) means quite a bit of extra work.
One alternative is going outside your code and creating content using one of the commercial multimedia authoring tools, or prepackaging the media-rich part of the content into a canned movie. But content created through an authoring tool or in the form of a movie is hard to integrate with other code components, such as displaying live data or getting user input for interactivity. In other words, this becomes a black box piece of media with restrictions that are not compatible with the flexibility of computers and interactive animation.
DirectAnimation brings new possibilities, especially when it comes to combining different types of media in the same content. With DirectAnimation, programmers need to learn only a few concepts that they will be able to apply repeatedly across different media types. As our examples will illustrate, programmers need only specify the high-level details of the animation declaratively; they don't need to deal with the fine details of managing the animation since this is provided by the DirectAnimation run time.
DirectAnimation provides three key services to developers: an animation runtime engine accessible through a high-level API; a common programming model for different media types; and advanced animation features, resolution independence, and runtime optimizations. DirectAnimation also supports a number of media formats including 2-D vector graphics and text (these types are intrinsic to the library), sprites (BMP, GIF, animated GIF, plus all image types supported by Microsoft Internet Explorer 4.0), 3-D models (.X files and VRML), all sound formats supported by DirectShow, and all streamed media types supported by Microsoft DirectShow™, including upcoming Microsoft NetShow™ support.
DirectAnimation is a COM API. A set of convenience classes is also included for Java developers, providing useful abstractions on top of the COM API and taking advantage of some Java features. Since it's COM-based, the API is accessible from JScript, Visual Basic, VBScript, and C/C++ simply by importing the COM interfaces (Microsoft Visual Basic version 5.0 and Microsoft Visual C++® version 5.0 are recommended). The API is also accessible to HTML page authors through the DirectAnimation controls provided with Internet Explorer 4.0.
DirectAnimation is implemented on top of DirectShow and the DirectX® foundation APIs, including DirectDraw®, Direct3D®, and DirectSound®. DirectAnimation content benefits from any hardware acceleration of these underlying technologies. Also, by building on the capabilities of DirectShow (previously known as ActiveMovie™), DirectAnimation can support different media stream formats and can be extended through DirectShow codec filters.
Creating DirectAnimation content consists of two steps: defining the model and running it. You can think of the first step as setting up a spreadsheet or (if you're already multimedia savvy) creating a scene graph for a retained-mode API. This model-creation step specifies which DirectAnimation behavior objects to use and their relationships to each other, their responses to user input, and how the objects change over time. While the model is being executed, callbacks into application code can be triggered and applications have the ability to modify the model, dynamically altering the animation.
Let's take a look at a "Hello World" example (see Figure 1) to go over some basic concepts. Note that the animation description is inlined with the HTML in a single file. We start this example by instantiating the DirectAnimation unified media control. This control can be used in any environment supporting ActiveX components. In particular, the DirectAnimation unified media control can be used in Internet Explorer 3.0 or 4.0, where it can be used as either a windowed or windowless control.
<HTML>
<HEAD>
<TITLE>DirectAnimation MIND Article</TITLE>
</HEAD>
<BODY BGCOLOR=WHITE TOPMARGIN=15 LEFTMARGIN=20>
<FONT FACE="Verdana, Arial, Helvetica" SIZE=2>
<CENTER>
<H1>Hello World in DirectAnimation</H1>
</CENTER>
<OBJECT ID="DAControl"
STYLE="position:absolute; left:50; top:100; width:300;height:300"
CLASSID="CLSID:B6FFC24C-7E13-11D0-9B47-00C04FC2F51D">
</OBJECT>
<SCRIPT LANGUAGE="JScript">
<!--
// The DirectAnimation library
m = DAControl.PixelLibrary;
// define the elements of the animation
textClr = m.ColorRgb(1, 0, 0);
font = m.DefaultFont.Color(textClr).Size(48);
textImg = m.TextImage("Hello World", font);
finalImg = m.Overlay(textImg, m.SolidColorImage(m.Blue));
// set the image to be displayed
DAControl.Image = finalImg;
// start the animation
DAControl.Start()
//-->
</SCRIPT>
</BODY>
</HTML>
Figure 1: Hello World in DirectAnimation
From the control, we extract a library of methods that we call m (for media). Through this library we access the DirectAnimation functionality. For this simple example, we defined the "Hello World" string, drew it in red, and overlaid it on a blue background. At this point, all the parts for the animation have been defined. The animation will be played by the control once we give it the command to start. Note that this animation contains only constant elements; nothing is changing over time (not a very exciting animation, but a valid use of the API nevertheless).
The unified media control has two very important properties. The first is the Image property, used to set the image to be displayed by the control. The second is the Sound property, used to set the sound the control will play. Although the control takes only one image and one sound, it is very easy to combine several images or different sounds into single ones. The Start method causes the control to execute the animation and to look for and react to user input.
The examples throughout this paper build on this simple one. The code fragments shown plug directly into this same basic framework. If you don't have access to the media that accompanies this paper, you can easily substitute your own media files instead.
Building animation models in DirectAnimation involves a process of using values in expressions to construct new values. This is akin to the numeric arithmetic expressions common in all programming languages. Technically, common DirectAnimation calls don't produce side-effects; each call returns a new value/object. For example, the following line is useless because the returned value is not assigned to anything, and hence can't be used later:
font.Size(64); // not useful
The correct usage is:
font = font.Size(64); //font now has a new size of 64
In the "Hello World" example, we came across number, color, string, font, and image values. We used a numeric size and a color value to produce a font. We then combined a string and a font to produce an image. This image was combined with a solid blue background to produce the final image passed to the control. We will show later how DirectAnimation extends this familiar process of using expressions on values to produce new values, and how it applies it to time-varying values and a rich set of media types.
DirectAnimation makes it easy to represent entities that vary over time and the temporal relations between them. It does so by introducing the notion of a behavior as a family of data types of time-varying values. In this section we'll illustrate some key points about behaviors by making use of the simplest and most malleable one, number behaviors.
The following code constructs a number that starts at 0.0 and increases by 1 unit every second (since this is a floating point value, you get fractional increments, not just integers; see Figure 2):
HueNum = m.LocalTime;
We use this time-varying number to produce a time-varying color, defined by its hue, saturation, and luminance. This is known as an Hsl color.
textClr = m.ColorHslAnim(hueNum, m.DANumber(0.5), m.DANumber(0.5));
Try using this color in the earlier "Hello World" example:
font = m.DefaultFont.Color(textClr).Size(48);
Note that number behaviors like HueNum are of type DANumber (the DA stands for DirectAnimation). The method m.DANumber converts a regular number to a DANumber. The Anim suffix in some methods, such as the one in ColorHslAnim in the earlier example, indicates variations of functions that take behaviors as parameters, as opposed to constants like the method ColorHsl would. For those cases where you are not going to animate parameters, you can use the functions without the Anim suffix. In that case, m.ColorHsl(0.3,0.5,0.5) would yield a constant color.
The HueNum number behavior that we've constructed here cycles through all the color hues in one second. Let's slow it down by doing some arithmetic on the number behaviors. All commonly supported floating point operations are available to DANumbers. These are provided as method invocations that take DANumbers as parameters:
period = 3; // in seconds
timeNum = m.Div(m.LocalTime, m.DANumber(period)); // m.LocalTime/period
Unfortunately, the syntax is awfully verbose for doing simple arithmetic with number behaviors. This is an aspect of the host language JScript, and not of DirectAnimation. In languages that support operator overloading and automatic type casting, the expression would simply be:
timeNum = m.LocalTime/period;
Another useful facility for constructing a number behavior is:
sizeNum = m.SlowInSlowOut(36, 64, 3, 0).RepeatForever();
font = m.DefaultFont.Color(textClr).SizeAnim(sizeNum);
SlowInSlowOut constructs a number that interpolates between two values over a specified period of time. It also takes a sharpness parameter, which at 0.0 (like the previous example) gives linear interpolation, at 1.0 gives maximum slow-in-slow-out, and at –1.0 gives maximum fast-in-fast-out.
The DANumber class provides the ToString method, which allows us to construct time-varying strings from number behaviors:
textImg = m.TextImageAnim(m.LocalTime.ToString(2), font);
This gives us the time relative to when the animation was started to a 0.01 second precision. (The ToString parameter specifies the decimal places of precision, in this case 2.) Combining all the snippets above, we end up with a time-varying color, size, and string combined into a time-varying image.
Figure 2. Start time in an animation
DirectAnimation provides an extensive set of methods for constructing and manipulating number behaviors. Such behaviors are used to construct higher-level behaviors like we've seen here with the color behavior and the string behavior. We will also see them used for controlling position, orientation, angles, image opacity, and 2-D and 3-D transforms.
There are some other number behaviors that we'll define here and use throughout the examples for convenience:
negOneNum = m.DANumber(-1);
zeroNum = m.DANumber(0);
halfNum = m.DANumber(0.5);
oneNum = m.DANumber(1);
twoNum = m.DANumber(2);
Note that these are constant number behaviors since their value does not change over time. On the other hand, when doing animation it is useful to have number behaviors that do vary over time. With this in mind, we also defined a number behavior called oscillatingNum. This number is constructed based on the sine function (Sin), which returns values ranging from –1 to 1. As input to the sine function, we will use localTime, which will be interpreted as an ever-increasing angular value, in radians. The result will be a number that varies continuously from 0, to 1, to –1, and back to 0.
oscillatingNum = m.Sin(m.LocalTime);
Sometimes we want values that show noncontinuous variation. For example, we may want a number behavior that starts at 0, goes to 3, and then restarts at 0. We can easily construct such a behavior by using the modulo function. Modulo is an operation that divides two numbers, an operand and a base, and returns the remainder. This operation guarantees a result that ranges from 0 to the value of the base.
// this gives a number that starts at 0
// increases to 3 and restarts at 0 again
modNumber = m.Mod(m.LocalTime, m.DANumber(3));
These two repeating, animated number behaviors, oscillatingNum and modNum, are extremely useful for controlling the motion of or animating elements in content. Usually you need to scale the value to suit your needs.
DirectAnimation is an object-oriented API, with the Behavior class one of its most important base classes. Most of the raw media used in DirectAnimation content is created through external tools, then imported into DirectAnimation and abstracted as a behavior of the respective type. This encapsulation allows different media to be treated uniformly, independent of its type or origin. Media files are imported by specifying their relative paths or Uniform Resource Locators (URLs). A few media types, namely text and 2-D vector graphics, can be created directly through DirectAnimation calls.
m = DAControl.PixelLibrary;
roofFillImg = m.HatchDiagonalCross(m.Red, 15);
roofImg = m.Polyline(new Array(-85,-50, 0,-125, 85,-50)).close().
Fill(m.DefaultLineStyle, roofFillImg);
wallFillImg = m.SolidColorImage(m.Teal);
wallImg = m.Polyline(new Array(-75,50, -75,-50, 75,-50, 75,50)).
Fill(m.DefaultLineStyle, wallFillImg);
doorArr = new Array(-25,50, -25,0, 25,0, 25,50);
doorFillImg = m.GradientPolygon(doorArr, new Array(m.Red, m.Green,
m.Blue, m.Yellow));
doorImg = m.Polyline(doorArr).
Fill(m.DefaultLineStyle, doorFillImg);
grassFillImg = m.SolidColorImage(m.Green);
grassImg = m.Polyline(new Array(-150,75, -150,50, 150,50, 150,75)).close().
Fill(m.DefaultLineStyle, grassFillImg);
finalImg = m.OverlayArray(new Array(doorImg, roofImg, wallImg, grassImg));
Figure 3. Script for a 2-D Vector Model of a House
Figure 4. 2-D model of a house
One of the most frequently used classes derived from the Behavior class is the Image behavior through which content is displayed. The Image behavior type provides some basic 2-D image manipulation capabilities such as clipping, cropping, opacity, and transforms. It also supports the aggregation of image components. Other visual types such as 3-D geometry or 2-D vectors have rendering methods that produce images.
Importing image files creates an Image behavior. DirectAnimation determines whether the image is supported based on the file's extension. Image behavior objects can be used as background pictures, sprites, or even as textures for 3-D objects.
// import the sprite
spriteImage = m.ImportImage("image.jpg");
// animate the opacity of the sprite since
// the oscillating number
// ranges from –1 to 1, add 1
// to it (making it 0 to 2), then
// divide by 2 (making it vary from 0 to 1).
transparentImg = spriteImage.OpacityAnim(
m.Div(m.Add(oscillatingNum, oneNum),twoNum) );
In addition to the opacity method shown in this example, the Image behavior class provides methods that allow you to clip the image with a polygon or with a behavior known as a matte, to crop the image with a rectangle, to cause the image to repeat by tiling it, and to transform the image with a 3 × 2 matrix (allowing for scaling, translation, and shear).
DirectAnimation provides a set of 2-D vector construction methods. The supported primitives include lines, arcs, ovals, and gradient fills. In general, the drawing methods take as a parameter an array of points. It is also possible to specify time-varying number behaviors as points, resulting in animated 2-D primitives. The simple example shown in Figure 3 builds the house shown in Figure 4.
The Fill method allows you to specify whether the primitive should be stroked, filled, or both at the same time. Through the LineStyle object, you can control parameters such as line width, joint style, and line color.
As was the case for 2-D images, the file's extension determines if a sound file is supported or not. When a sound file is imported, it returns a DAImportation object. From DAImportation object, you can extract the Sound behavior. DAImportation object also contains other useful information such as the length of the sound.
// Bring in the media
mySndObj = m.ImportSound("sound.wav");
mySnd = mySnd.SoundObj.PanAnim(oscillatingNum);
The Sound behavior class provides methods that cause the sound to loop and control the sound's phase, pan, and gain. Note that you have to set the sound property on the control for sound to be played in your animation.
DAControl.Sound = mySnd.Loop(); // pass a looping sound
When imported, movies create two behaviors: an Image and a Sound. As was the case with importing sounds, a DAImportationObject is created. In the case of a movie, the DAImportationObject contains both a Sound and an Image behavior.
movie = m.ImportMovie("movie.avi");
// A movie is treated as an image and a sound.
// Here we make the movie's video track loop, by
// only allowing time to range from 0 to the duration
// of the movie.
movieImg = movie.Image.SubstituteTime(m.mod(m.LocalTime,movie.Duration));
// Now we make the movie's sound loop.
// Sound is a continuous media, so all we need to do is
// create a looping version of the sound track.
movieSnd = movie.Sound.Loop();
// Set the image to be displayed and sound to play.
DAControl.Image = movieImg;
DAControl.Sound = movieSnd;
DirectAnimation makes extensive use of DirectShow when dealing with streamed media. Any file type or codec supported by DirectShow works with DirectAnimation. The movie doesn't have to be a physical file, but can actually be a live feed streamed over the network. Depending on its contents, importing a movie may return just an Image or a Sound behavior. You would get an EmptyImage or Silence as the behavior for the corresponding missing media. If you want to, you can ignore either the video or soundtrack after importing a movie.
The Image behavior created by importing a movie is time-varying. Depending on the value of time, the Image behavior takes on a different value (a different picture). This time-varying characteristic of the Image behavior is key when trying to make a movie play in a loop; we make time cycle from zero to the length of the movie, then restart at zero. For this we can use the modulo operation discussed earlier, as shown in the following code:
movie.Image.SubstituteTime(m.mod(m.LocalTime,movie.Duration));
Independent of its time-varying nature, the Image behavior created by the importMovie method can be used in the same manner as the Image behavior returned by importing 2-D images, including combining it with other images and using it as a texture on 3-D objects.
Text is constructed by associating a string with a DAFontStyle. The DAFontStyle object allows you to specify several different aspects of the text such as the size, color, and font to be used. If the specified font is not available in the target machine, a default font is substituted.
fontStyle = m.Font("Times", 64, m.Red);
mediaTextImg = m.TextImage("Media", fontStyle.Italic());
//Let's animate the opacity of the text
mediaTextImg = mediaTextImage.OpacityAnim(m.Add(halfNum,
m.Mul(halfNum, oscillatingNum)));
Once you create an Image behavior by using the TextImage method, you can use the resulting image object as you would any other image. In the previous example we varied the opacity of the text.
You may have noticed that in all the examples, media importation was done synchronously. DirectAnimation does allow for asynchronous importation of media, with a proxy being displayed while the media is loading. Check the DirectAnimation Software Development Kit (SDK) for examples.
At this point you've seen how to construct different media types and change some of their attributes over time. Now let's see how we can coordinate and control the movement of media in the page over time. We will start by examining the coordinate system used by DirectAnimation, so that you can understand how to apply transformations later on.
The basic units of measure in DirectAnimation are the meter (for distance), the second (for time), and radians (for angles). There are also convenience functions that let you specify quantities in terms of other units. For instance, there are variations of functions that take angle parameters in degrees, and facilities for specifying measures in pixel units.
DirectAnimation provides the tools to specify animations in a resolution-independent fashion. Alternatively, you can use resolution-dependent techniques so that the animation size varies with the resolution setting. The DirectAnimation coordinate system is meter-based, center-origin, and right-handed (positive y goes upward).
DirectAnimation also provides facilities for pixel specification. These are basically auxiliary classes that convert from user-specified parameters in pixels to the internal meter-based representation. These include a pixel-construction mode provided through PixelLibrary and a conversion factor that converts from pixels to meters. The latter is called Pixel and is a DANumber. An interesting side-effect of Pixel being a number behavior is that, if the monitor's resolution changes dynamically—either because the user selected a new mode or because the window was moved to a monitor with different characteristics (under Microsoft Windows® 98)—your animation adapts to the change automatically without your intervention.
In the pixel-construction mode, all positional information is specified in terms of pixels in relation to the pixel coordinate system. This coordinate system is similar to the DirectAnimation coordinate system but is left-handed (positive y goes down). It is intended to be similar to that of Dynamic HTML, only the latter is upper-left-corner centered, so the conversion between the pixel coordinate system in DirectAnimation and the DHTML coordinate system is a simple translation.
To illustrate, let's contrast two pixel and meter versions of basically the same animation (see Figures 5 and 6). We use the meter library (see Figure 6), where the minor changes are shown in blue.
m = DAControl.PixelLibrary;
dim = 100; // in pixels
halfDim = dim/2;
// Define a red oval.
redOvalImg = m.Oval(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Red));
redTransXf = m.Translate2(0, halfDim);
// Define a green oval.
greenOvalImg = m.Oval(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Green));
greenTransXf = m.Translate2(0, -halfDim);
// Rotation by 45 degrees per sec around 3-D first diagonal
rotXf = m.Rotate3RateDegrees(m.Vector3(1,1,1),45).ParallelTransform2();
redOvalImg = redOvalImg.Transform(rotXf).Transform(redTransXf);
greenOvalImg = greenOvalImg.Transform(rotXf).Transform(greenTransXf);
finalImg = m.Overlay(redOvalImg, greenOvalImg);
Figure 5. Animate ovals in pixel mode
m = DAControl.MeterLibrary;
dim = 0.03; // in meters
halfDim = dim/2;
// Define a red oval.
redOvalImg = m.Oval(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Red));
redTransXf = m.Translate2(0, -halfDim);
// Define a green oval.
greenOvalImg = m.Oval(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Green));
greenTransXf = m.Translate2(0, halfDim);
// Rotation by 45 degrees per sec around 3-D first diagonal
rotXf = m.Rotate3Rate(m.Vector3(1,1,1),Math.PI/4).ParallelTransform2();
redOvalImg = redOvalImg.Transform(rotXf).Transform(redTransXf);
greenOvalImg = greenOvalImg.Transform(rotXf).Transform(greenTransXf);
finalImg = m.Overlay(redOvalImg, greenOvalImg);
Figure 6. Animate oval in meter mode
Be careful when using the pixel mode; it is a construction mode only and is intended as a convenience for simple 2-D animation. For advanced animation it's more appropriate to use the meter mode. For example, in doing 3-D or in situations where the animation needs to extract behaviors from previously specified ones, it's better to use the meter mode, whereas the Pixel conversion factor comes in handy for explicitly mapping pixel values to meters.
Transformations are the movers and shakers of animations. DirectAnimation provides a wide set of facilities for constructing both 2-D and 3-D behavior transformations. The traditional affine (4 × 3) and projective (4 × 4) transformations are supported, including ways to combine them and to formulate hierarchical, articulate structures based on them.
When an image is imported, the resulting image is centered at the origin. If we want to slide the image so that it is left-aligned with the origin, we would have to apply a translation. So, starting with the finalImage from the previous example, we first find out its size by getting the bounding box of the image. The bounding box is defined in terms of two DAPoint2 behaviors, Min and Max, each with an x and a y coordinate.
finalImgBBox = finalImg.BoundingBox;
We then determine how much translation the image needs along the x axis. Since this example is based on the meter version of the library being used, the bounding box is always returned in meters. If you were using the pixel-based library, you would have to divide the value of the bounding box by m.Pixel.
translationNum = finalImgBBox.Max.X;
Unless the image is empty, its Max coordinate will have positive x and y values (remember that imported images are centered at 0,0). So, to move to the right, we will translate the image by this x coordinate.
Next, we apply that translation to the image:
FinalImg = finalImg.Transform(m.Translate2Anim(translationNum, zeroNum));
Based on these basic concepts of position and translation, Figures 7 and 8 show a collage of 2-D graphic primitives with animate affine transformations applied to them. This code is pretty much self-explanatory after the material that we covered earlier, which is a testament to the simplicity with which animation can be expressed in DirectAnimation. We use a sinusoidal number behavior that oscillates from 0 to 1, –1, and back to 0 in a specified period. From this number we construct a variety of transform behaviors. We use it for scale, translation, shear, and sinusoidal (rotational) motion.
m = DAControl.PixelLibrary;
period = 3; // Time in seconds
dim = 100; // Space in pixels
hDim = 3*dim/4;
qDim = dim/2;
sinNum = m.Sin(m.Mul(m.LocalTime, m.DANumber(2*Math.PI/period)));
// Construct a red oval with corresponding transform
redImg = m.Oval(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Red));
redXf = m.Compose2(m.Translate2(hDim, hDim),
m.Scale2UniformAnim(sinNum));
// Construct a green rounded rectangle with corresponding transform
greenImg = m.RoundRect(dim,dim,qDim,qDim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Green));
greenXf = m.Compose2(m.Translate2(hDim, -hDim), m.YShear2Anim(sinNum));
// Construct a blue rectangle with corresponding transform
blueImg = m.Rect(dim,dim).Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Blue));
blueXf = m.Compose2(m.Translate2(-hDim, -hDim),
m.Translate2Anim(m.DANumber(0), m.Mul(m.DANumber(25), sinNum)));
// Construct a purple pie with corresponding transform
purpleImg = m.PieDegrees(-60, -120, dim, dim).
Fill(m.DefaultLineStyle,
m.SolidColorImage(m.Purple));
purpleXf = m.Compose2(m.Translate2(-hDim,hDim),m.Rotate2Anim(sinNum));
// Construct transformed versions of these shapes.
redImg = redImg.Transform(redXf);
greenImg = greenImg.Transform(greenXf);
blueImg = blueImg.Transform(blueXf);
purpleImg = purpleImg.Transform(purpleXf);
// Combine them into one image.
finalImg = m.OverlayArray(new Array(redImg,greenImg,blueImg,purpleImg));
// Set the rotating oval, an image, as the model to be displayed.
DAControl.Image = finalImg;
// Start the animation.
DAControl.Start()
Figure 7. A script with a variety of 2-D affine transforms
Figure 8. Translation, shearing, rotation, and scale
This animation has two parameters that could be easily tweaked: period, which controls the speed of the animation, and dim, which controls its size. DirectAnimation supports time and its manipulation as traditional graphics systems do for spatial dimensions.
DirectAnimation provides a way to use affine 3-D transforms on 2-D primitives. We used this feature already in Figures 5 and 6. For the fun of it, let's use that same 3-D rotation and apply it to finalImage. We can do so by adding the following two lines:
rotXf = m.Rotate3RateDegrees(m.Vector3(1,1,1), 45).ParallelTransform2();
finalImg = finalImg.Transform(rotXf);
ParallelTransform2 takes a 3-D affine transform and converts it to a 2-D transform, which then can be applied to 2-D entities. (An orthographic camera is assumed in doing this conversion.)
This is a good example of the modularity and reuse provided in DirectAnimation. The key point here is that a whole animation with all its details is encapsulated as a single behavioral value, finalImg (an image behavior), which in turn is used as a value for constructing a higher-level animation in a straightforward and simple fashion.
In addition, what we just did illustrates a simple hierarchical articulation. Each shape animates in its local coordinate system, and then a global transform is applied to animate the combined parts in a global coordinate system.
So far we've seen how behaviors are time-varying values that we combine in expressions to produce new higher-level values. In this section we will show how behaviors can also react to events and switch from one behavioral value to another. A behavior is capable of encapsulating not only time variability, but also event reactivity, so in the broad sense they are reactive behaviors.
Let's say we would like the time-varying text color above to switch to red when we press the left mouse button. We can achieve this using the Until method as follows:
textClr = m.ColorHslAnim(hueNum, m.DANumber(0.5), m.DANumber(0.5));
textClr = m.Until(textClr, m.LeftButtonDown, m.Red);
Until takes two behaviors and an event. It constructs a value that starts as the first behavior until the event occurs, at which point it switches to the second behavior. Clearly, both behaviors need to be of the same type.
This is the simplest form of a reactive behavior and is sufficient for most situations. For advanced users, we'll give a quick summary of more complex reactive behaviors.
The following code shows how to snapshot the value of a behavior at the time an event happens:
textClr = m.UntilEx(textClr, m.LeftButtonDown.SnapShot(textClr));
UntilEx (for extended) takes a starting behavior and an event that produces the behavior that gets invoked. In this case it is a SnapShot event since it produces a snapshot (a constant behavior) of its parameter. The following code shows how to cycle through two behaviors:
basicClr = m.ColorHslAnim(hueNum, m.DANumber(0.5), m.DANumber(0.5));
textClr = new ActiveXObject("DirectAnimation.DAColor");
textClr.Init(m.Until(basicClr, m.LeftButtonDown,
m.Until(m.Red, m.LeftButtonDown, textClr)));
Basically, a cyclic behavior starts with one value, switches to the second, and then goes back to the first. To do this we need to forward declare a behavior, and use it in constructing the cyclic graph. (Fans of C++ will be familiar with this dilemma.) Furthermore, notice the nested use of Until. Since it returns a behavior, it is possible to use an Until expression in place of a behavior of the same type.
As we mentioned earlier, there are two distinct steps in a DirectAnimation program: creating the model and executing the animation. Now let's go into this concept in more detail.
During the creation of the model, the behaviors are defined, but no actual rendering takes place. For example, the overlay function, which takes two image behaviors and combines them, does not create a bitmap of the rendering of the two images when it executes the overlay statement. The call merely constructs an internal data structure that indicates the manner in which the two images should be combined. The actual processing of this data structure takes place when the animation is running. This allows DirectAnimation to perform optimizations if the media is changing and cache those images that remain constant, even if they're only constant for certain intervals. Also, it can skip the rendering of parts that are not visible altogether.
Given this retained model approach, the heavy work is done in the animation engine, when the animation model is processed for display. Thus, high-level languages such as VBScript and JScript are only involved in the definition of the model. This means that you will get the same run-time performance regardless of the language you use. The only exception to this is when there are callbacks from your animation into your code, in which case the performance will be influenced by the application code that executes and the host language.
Geometry behaviors are constructed by importing prefabricated models in .X or .wrl file formats. These models can then be transformed, textured with images (including animated and interactive images), assigned material properties, lit, and projected via a camera into image behaviors, which then get displayed.
Consider the example in Figure 9. We construct a perspective camera and a point light, and then we import a cube that we color and spin. We then light the cube and render it via the camera. This illustrates the high-level abstraction in DirectAnimation and how it hides a lot of the details that in traditional 3-D systems get in the way of all but a handful of highly experienced programmers. 3-D should not be that difficult, and it isn't if you use DirectAnimation. In DirectAnimation, the number of concepts that the programmer needs to deal with corresponds roughly to the ones in the targeted animation itself.
<HTML><HEAD>
<TITLE>DirectAnimation MIND Article</TITLE>
</HEAD>
<BODY BGCOLOR=WHITE TOPMARGIN=15 LEFTMARGIN=20>
<FONT FACE="Verdana, Arial, Helvetica" SIZE=2>
<CENTER><H1>Spinning Cube</H1></CENTER>
<OBJECT ID="DAControl"
STYLE="position:relative; left:50; top:0;width:600;height:400"
CLASSID="CLSID:B6FFC24C-7E13-11D0-9B47-00C04FC2F51D">
</OBJECT>
<SCRIPT LANGUAGE="JScript">
<!--
m = DAControl.MeterLibrary;
dim = 0.03; // in meters
camera = m.PerspectiveCamera(2*dim, 1.1*dim);
light = m.PointLight.Transform(m.Translate3(dim/2, dim/4, 1.5*dim));
cubeGeo = m.ImportGeometry("cube.x").DiffuseColor(m.Teal).
Transform(m.Compose3Array(new Array(
m.Scale3Uniform(dim/2),
m.Rotate3RateDegrees(m.XVector3, 60),
m.Rotate3-Degrees(m.YVector3, 60))));
finalImg = m.UnionGeometry(cubeGeo, light).Render(camera);
DAControl.Image = finalImg;
// start the animation
DAControl.Start();
//-->
</SCRIPT></BODY></HTML>
Figure 9. A rotating cube with a light and camera.
The DirectAnimation SDK includes an extensive set of documents and samples and can be downloaded from the Microsoft DirectX Web site (http://www.microsoft.com/directx/resources/devdl.htm). Professional-level subscribers of the MSDN™ Library and above will receive the SDK with their Library subscriptions.
We've set up public newsgroups on the msnews.microsoft.com NNTP server as a forum for the DirectAnimation user community. Any questions you have about DirectAnimation can be posted on microsoft.public.multimedia.directx.danimation.controls (for users of the DirectAnimation controls), or microsoft.public.multimedia.directx.danimation.programming (for programmers of the DirectAnimation API).
The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.
This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS DOCUMENT.