<HTML>
<HEAD>
<TITLE>DirectX Media SDK DirectAnimation Tutorial</TITLE>
<LINK REL=STYLESHEET HREF="../../../../../webcd/da/tutorial.css">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=iso8859-1">
<META NAME="MS.LOCALE" CONTENT="EN-US">
<META NAME="ROBOTS" CONTENT="noindex">
</HEAD>
<BODY BGCOLOR="WHITE" LINK=#0033CC TOPMARGIN=0 LEFTMARGIN=20>
<!--TOOLBAR_START-->
<!--TOOLBAR_EXEMPT-->
<!--TOOLBAR_END-->

<DIV CLASS="flush">
<TABLE WIDTH=100% BORDER=0 BGCOLOR=#000000>
<TR ALIGN=RIGHT><TD VALIGN=TOP><FONT CLASS=desc1 SIZE=2>
<IMG SRC="../../../../../media/image/d_prev.gif"
 WIDTH=71 HEIGHT=16 BORDER=0 ALT="Previous Topic">
<A HREF="../../../../../webcd/da/tutorial_main.htm" TARGET="_top">
<IMG SRC="../../../../../media/image/d_home.gif" WIDTH=48 HEIGHT=16 BORDER=0 ALT="Tutorial Home Page"></A>
<IMG SRC="../../../../../media/image/d_next.gif" WIDTH=48 HEIGHT=16 BORDER=0 ALT="Next Topic">
</FONT></TD><TD WIDTH=30></TD></TR></DIV></TABLE>
<FONT CLASS=title><CENTER>GeoApplet2: A Cube Spinning at 5 Frames Per Second</CENTER></FONT>
<BR>
<FONT CLASS=desc1>
This applet constructs a spinning cube to fit in the view window.  It
illustrates how to import a geometry, manipulate it, and view it
with a camera.  Unlike GeoApplet1, which uses time-varying behaviors
for the rotation angle of the spinning cube, this applet uses switchers
to control these same parameters.  It also explicitly controls the sampling
and rendering of the model by overriding the tick() method.
<BR>
</FONT><BR>
<TABLE WIDTH=100%><TR><TD WIDTH=5%><TD><FONT CLASS=code>

<BR>
import com.ms.dxmedia.*;            </FONT><FONT CLASS=desc2> direct animation libraries</FONT><FONT CLASS=code><BR>
import java.awt.*;                  </FONT><FONT CLASS=desc2> for getting the applet dimension</FONT><FONT CLASS=code><BR>
import java.net.*;                  </FONT><FONT CLASS=desc2> for URLs</FONT><FONT CLASS=code><BR>
import java.applet.Applet;<BR>
</FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Create a DXMCanvas object and add it to the applet.</TD></TR></FONT></TABLE>
<FONT CLASS=code>public class GeoApplet2 extends Applet {<BR>
<DIV STYLE="margin-left: 0.25in">public void init() {<BR>
</DIV><DIV STYLE="margin-left: 0.5in">setLayout(new BorderLayout());<BR>
GeoCanvas cv = new GeoCanvas(this);<BR>
add("Center", cv);<BR>
</DIV><DIV STYLE="margin-left: 0.25in">}<BR>
</DIV>}<BR>
</FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Override the tick method in DXMCanvas class to control the frame
 rate.  Also create a switcher for each animating angle of the cube.</TD></TR></FONT></TABLE>
<FONT CLASS=code>class GeoCanvas extends DXMCanvas {<BR>
<BR>
<DIV STYLE="margin-left: 0.125in">public GeoCanvas(GeoApplet2 geoa) {<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> The rotation angles of the cube are switchers.  Initialize each of
 them to a zero number behavior.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">NumberBvr zeroNum = Statics.toBvr(0);<BR>
_yAngleSw = new ModifiableBehavior(zeroNum);<BR>
_zAngleSw = new ModifiableBehavior(zeroNum);<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Now set the model.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">setModel(new GeoModel(geoa, this));<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Override the tick() to control the frame generation explicitly.
 Before calling super.tick(), update the values of the switchers
 for the next frame.  This is called automatically by the system.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">public void tick() {<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Calculate the next tick time.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">nextTickTime += 1/DESIREDFRAMERATE;<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> getCurrentTime returns the DirectAnimation global time.  It starts
 at zero when the model is started.  Switch the rotating angle of
 the cube based on the current time here.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">double currTime = getCurrentTime();<BR>
_yAngleSw.switchTo(Statics.toBvr(currTime));<BR>
_zAngleSw.switchTo(Statics.toBvr(currTime*1.3));<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Explicitly sample and display the model.  Without this, you'd
 see a blank window.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">super.tick();<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Calculate how long the applet needs to sleep to achieve the desired
  frame rate. sleep() takes milliseconds so multiply by 1000.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">long remainingTime = Math.round(1000*(nextTickTime - getCurrentTime()));<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> If you're running behind, sleep(0).</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">if (remainingTime &lt 0) remainingTime = 0;<BR>
</DIV><BR>
<DIV STYLE="margin-left: 0.25in">try { Thread.sleep(remainingTime); } catch(InterruptedException e) {}<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> This is an approximate frame rate.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">static final double DESIREDFRAMERATE = 5;<BR>
double nextTickTime = 0;<BR>
ModifiableBehavior _yAngleSw, _zAngleSw;<BR>
</DIV>}<BR>
</FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> This class extends the Model class.  The createModel method in this class
 is where you construct your animation.</TD></TR></FONT></TABLE>
<FONT CLASS=code>class GeoModel extends Model {<BR>
</FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Get the size of the applet in the constructor, and stores it in the
 member variables.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">GeoModel(GeoApplet2 geoa, GeoCanvas cv) {<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Get the size of the applet.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">Dimension dim = geoa.getSize();<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> The base unit of measure for DirectAnimation is the meter.
 Convert the size into meters by multiplying it with the pixelBvr.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">_halfWidthNum = mul(toBvr(dim.width*0.5),pixelBvr);<BR>
_halfHeightNum = mul(toBvr(dim.height*0.5),pixelBvr);<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> The spinning angles of the cube are switchers defined in the
 GeoApplet2 class.  Save the references away so you can use them
 in the createModel() method.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">_yAngleNum = (NumberBvr)cv._yAngleSw.getBvr();<BR>
_zAngleNum = (NumberBvr)cv._yAngleSw.getBvr();<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> This function scales the geometry uniformly to fit in the unit box
 and centers it at the origin.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">GeometryBvr mapToCenteredUnitBox(GeometryBvr geo) {<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Find the extent and the center of the geometry.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">Bbox3Bvr bbx3 = geo.boundingBox();<BR>
Point3Bvr minPt3 = bbx3.getMin();<BR>
Point3Bvr maxPt3 = bbx3.getMax();<BR>
</DIV><BR>
<DIV STYLE="margin-left: 0.25in">Vector3Bvr extVec3 = sub(maxPt3, minPt3);<BR>
Point3Bvr centerPt3 = add(minPt3, extVec3.div(toBvr(2)));<BR>
</DIV><BR>
<DIV STYLE="margin-left: 0.25in">NumberBvr maxExtNum = max(extVec3.getX(),<BR>
</DIV><DIV STYLE="margin-left: 1.875in">max(extVec3.getY(),<BR>
</DIV><DIV STYLE="margin-left: 2.125in">extVec3.getZ()));<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Move the object to the origin, and resize it.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">Transform3Bvr normalizingXf3 =<BR>
</DIV><DIV STYLE="margin-left: 1.3125in">compose(scale3(div(toBvr(1), maxExtNum)),<BR>
</DIV><DIV STYLE="margin-left: 1.8125in">translate(sub(origin3, centerPt3)));<BR>
</DIV><BR>
<DIV STYLE="margin-left: 0.25in">return geo.transform(normalizingXf3);<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Create the animation in the createModel method.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">public void createModel(BvrsToRun blist) {<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Build up a URL to import relative to.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">URL mediaBase = getImportBase();<BR>
URL geoBase = buildURL(mediaBase, "geometry/");<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Import a cube, scale it to the unit cube and center it at the origin.
 The resulting cube has a bounding box of -0.5 to 0.5 in all 3 dimensions.</TD></TR></FONT></TABLE>
<FONT CLASS=code><BR>
<DIV STYLE="margin-left: 0.25in">GeometryBvr geo = importGeometry(buildURL(geoBase, "cube.x"));<BR>
geo = mapToCenteredUnitBox(geo);<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Spin the cube around the origin.  Find the smaller of the halfWidth and
 halfHeight of the view window, and use it to resize the cube.  The
 rendered image for the cube will be about half the size of the viewport.
 The part of the cube that's exactly on the viewing plane (Z = 0) will
 be exactly half the size of the viewport, the part that's behind the
 plane will be smaller, and the part that's in front will be bigger.
 The rotation angles around the yVector and zVector are switchers.
 The applet that displays the model updates their values frequently
 to create a spinning cube.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">NumberBvr minHalfExtNum = min(_halfWidthNum, _halfHeightNum);<BR>
geo = geo.transform(<BR>
</DIV><DIV STYLE="margin-left: 1.0in">compose(scale3(minHalfExtNum),  </FONT><FONT CLASS=desc2> sacle to fit half viewport</FONT><FONT CLASS=code><BR>
</DIV><DIV STYLE="margin-left: 1.5in">compose(rotate(yVector3, _yAngleNum),<BR>
</DIV><DIV STYLE="margin-left: 2.0in">rotate(zVector3, _zAngleNum))));<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> The hue of the color is a time-varying behavior.  You can mix
 imperative (modifying the switchers and controlling the frame loop
 explicitly) and declarative (define the behaviors at a higher level)
 programming in DirectAnimation, and they'll work just fine.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">geo = geo.diffuseColor(colorHsl(mul(localTime, toBvr(0.3)),<BR>
</DIV><DIV STYLE="margin-left: 2.25in">toBvr(0.8), toBvr(0.6)));<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> The near plane should be bigger than the max Z (in this case,
 sqrt(2)*minHalfExtNum after the cube is scaled to fit half viewport)
 of the bounding box of the spinning cube, so the cube doesn't get
 clipped out  The projection point determines the amount of perspective
 you'll see.  The farther away it is from the viewing plane, the less
 perspective will result.  Set it to 5 times the near plane here.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">NumberBvr maxZNum = mul(sqrt(toBvr(2)), minHalfExtNum);<BR>
NumberBvr projPointNum = mul(toBvr(5), maxZNum);<BR>
CameraBvr cam = perspectiveCamera(projPointNum, maxZNum);<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> Overlay the cube on top of the color image background,
 and get the image displayed.</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.25in">setImage(overlay(union(geo, directionalLight).render(cam),<BR>
</DIV><DIV STYLE="margin-left: 1.3125in">solidColorImage(red)));<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> max: returns max(x, y)</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">public static NumberBvr max(NumberBvr x, NumberBvr y) {<BR>
</DIV><DIV STYLE="margin-left: 0.25in">return (NumberBvr) cond(gt(y, x), y, x);<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV></FONT>
<BR>
<TABLE><TR><TD WIDTH=500><FONT CLASS=desc1> min: returns min(x, y)</TD></TR></FONT></TABLE>
<FONT CLASS=code><DIV STYLE="margin-left: 0.125in">public static NumberBvr min(NumberBvr x, NumberBvr y) {<BR>
</DIV><DIV STYLE="margin-left: 0.25in">return (NumberBvr) cond(lt(y, x), y, x);<BR>
</DIV><DIV STYLE="margin-left: 0.125in">}<BR>
</DIV><BR>
<DIV STYLE="margin-left: 0.125in">private NumberBvr _yAngleNum, _zAngleNum;<BR>
private NumberBvr _halfWidthNum, _halfHeightNum;<BR>
</DIV>}<BR>
<BR>
</TABLE><BR>
</FONT><FONT CLASS=link><A ID=cpslug HREF="../../../../../help/da/dalegal.htm" TARGET="_top">&#169; 1997 Microsoft Corporation. All rights reserved. Terms of Use.</A></FONT>
</TABLE>
<BR>
<BR>
<DIV CLASS="flush">
<TABLE WIDTH=100% BORDER=0 BGCOLOR=#000000>
<TR ALIGN=RIGHT><TD VALIGN=TOP><FONT CLASS=desc1 SIZE=2>
<IMG SRC="../../../../../media/image/d_prev.gif"
 WIDTH=71 HEIGHT=16 BORDER=0 ALT="Previous Topic">
<A HREF="../../../../../webcd/da/tutorial_main.htm" TARGET="_top">
<IMG SRC="../../../../../media/image/d_home.gif" WIDTH=48 HEIGHT=16 BORDER=0 ALT="Tutorial Home Page"></A>
<IMG SRC="../../../../../media/image/d_next.gif" WIDTH=48 HEIGHT=16 BORDER=0 ALT="Next Topic">
</FONT></TD><TD WIDTH=30></TD></TR></DIV>

</BODY>
</HTML>
