<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>blog.tartiflop</title>
	
	<link>http://blog.tartiflop.com</link>
	<description>post tenebras flex</description>
	<pubDate>Mon, 15 Dec 2008 18:18:31 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/tartiflop" type="application/rss+xml" /><item>
		<title>Musical Interlude - General Elektriks</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/485782514/</link>
		<comments>http://blog.tartiflop.com/2008/12/musical-interlude-general-elektriks/#comments</comments>
		<pubDate>Mon, 15 Dec 2008 18:18:31 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Music]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=299</guid>
		<description><![CDATA[Discover General Elektriks!
]]></description>
			<content:encoded><![CDATA[<div style="width:220px;height:55px;"><object width="220" height="55"><param name="movie" value="http://www.deezer.com/embedded/small-widget-v2.swf?idSong=33039&#038;colorBackground=0x555552&#038;textColor1=0xFFFFFF&#038;colorVolume=0x39D1FD&#038;autoplay=0"></param><embed src="http://www.deezer.com/embedded/small-widget-v2.swf?idSong=33039&#038;colorBackground=0x525252&#038;textColor1=0xFFFFFF&#038;colorVolume=0x39D1FD&#038;autoplay=0" type="application/x-shockwave-flash" width="220" height="55"></embed></object><br /><font size='1' color ='#000000'>Discover <a href='http://www.deezer.com/en/general-elektriks.html'>General Elektriks</a>!</font></div>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=kqHmGh"><img src="http://feeds.feedburner.com/~a/tartiflop?i=kqHmGh" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/485782514" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/musical-interlude-general-elektriks/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/musical-interlude-general-elektriks/</feedburner:origLink></item>
		<item>
		<title>First steps in Away3D : Part 7 - Movie and video materials</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/485764856/</link>
		<comments>http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-7-movie-and-video-materials/#comments</comments>
		<pubDate>Mon, 15 Dec 2008 17:47:33 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Away3D]]></category>

		<category><![CDATA[Actionscript 3]]></category>

		<category><![CDATA[animation]]></category>

		<category><![CDATA[movie material]]></category>

		<category><![CDATA[Tweener]]></category>

		<category><![CDATA[video stream]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=289</guid>
		<description><![CDATA[Having looked at the different types of texture mapped and coloured materials with different lighting aspects, I want to discuss briefly a couple of more interactive and dynamic materials. These don&#8217;t necessarily add realism to a scene but can be very useful for 3D website development to provide a richer user experience. To do this [...]]]></description>
			<content:encoded><![CDATA[<p>Having looked at the different types of texture mapped and coloured materials with different lighting aspects, I want to discuss briefly a couple of more interactive and dynamic materials. These don&#8217;t necessarily add <em>realism</em> to a scene but can be very useful for 3D website development to provide a richer user experience. To do this we use the <em>MovieMaterial</em> and <em>VideoMaterial</em>. If you&#8217;re interested here is an  equivalent tutorial for <a href="http://blog.tartiflop.com/2008/09/first-steps-in-papervision3d-part-8-movie-materials/">movie materials in Papervision3D</a>.</p>
<p>Previous articles summary :</p>
<ul>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-1-getting-started/">First steps in Away3D : Part 1 - Getting started</a><br />
Creation of a new Away3D project within eclipse or Flex Builder 3 and a simple example of a 3D scene illustrating some basic Away3D classes.</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-2-animation/">First steps in Away3D : Part 2 - Animation</a><br />
Adding some basic animation to the scene by modifying 3D object parameters when entering a frame.</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-3-texture-mapping/">First steps in Away3D : Part 3 - Texture mapping</a><br />
Create materials using bitmap data with the aim of having more detailed or realistic objects.
</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-4-scene-interaction/">First steps in Away3D : Part 4 - Scene interaction</a><br />
Listen to mouse events to interact with the scene and with individual rendered objects.
</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away-3d-part-5-lighting-and-shading/">First steps in Away3D : Part 5 - Lighting and shading</a><br />
A light source is added to the scene and the materials are changed to illustrate how Away3D renders objects with different shading characteristics.
</li>
<li><a href="http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-6-normal-mapping-and-environment-mapping/">First steps in Away3D : Part 6 - Normal mapping and environment mapping</a><br />
Additional realism is added to the scene by applying normal maps (to give a higher level of shading characteristics) and environment maps (as an alternative to simple lighting models).
</li>
</ul>
<p>This article will be looking at two materials available in Away3D: the <em>MovieMaterial</em> for rendering Flash movies onto a surface and <em>VideoMaterial</em> to show Flash video streams on an object (using <em>.flv</em> files). As well as being able to show another Flash animation, mouse events are mapped to the <em>MovieMaterial</em> allowing it to be interactive, even in a 3D environment. </p>
<p>This article is taking advantage of the very latest types of materials available with Away3D: to be able to compile the examples and develop your own you may need to use the repository (SVN) version of Away3D. If you don&#8217;t have <em>away3d.materials.VideoMaterial</em> available in your Away3D source then this is the case. Here&#8217;s an article on <a href="http://blog.tartiflop.com/2008/11/downloading-and-compiling-away3d-sources-with-svn-in-eclipse/">downloading and installing Away3D from SNV</a> if you need help.</p>
<p>To illustrate these different materials we&#8217;re going to create three simple planes rotated and translated to form three sides of a cube. Two of these will be rendered using <em>MovieMaterial</em>s and the third with a Flash video stream using a <em>VideoMaterial</em>. One of the <em>MovieMaterial</em> planes will be interactive and I&#8217;ll show how a external Flash movie can be embedded in the compiled animation.</p>
<p>So let&#8217;s look at the code. For this example we will actually have three ActionScript classes: the main Away3D class and two additional classes to be used for the individual <em>MovieMaterial</em> cube faces. The latter two will be shown at the end of the article just for completeness. </p>
<div id="codeSnippet">
<pre>
<blockquote>

package {
&nbsp; import away3d.cameras.HoverCamera3D;
&nbsp; import away3d.containers.ObjectContainer3D;
&nbsp; import away3d.containers.Scene3D;
&nbsp; import away3d.containers.View3D;
&nbsp; import away3d.core.base.Object3D;
&nbsp; import away3d.core.render.Renderer;
&nbsp; import away3d.events.MouseEvent3D;
&nbsp; import away3d.materials.MovieMaterial;
&nbsp; import away3d.materials.VideoMaterial;
&nbsp; import away3d.primitives.Plane;
&nbsp;
&nbsp; import caurina.transitions.Tweener;
&nbsp;
&nbsp; import flash.display.Sprite;
&nbsp; import flash.display.StageAlign;
&nbsp; import flash.display.StageScaleMode;
&nbsp; import flash.events.Event;
&nbsp; import flash.filters.BlurFilter;
&nbsp;
&nbsp; [SWF(backgroundColor="#222222")]
&nbsp;
&nbsp; public class Example007 extends Sprite {

 &nbsp; &nbsp; private var videoURL:String = "http://www.tartiflop.com/away3d/FirstSteps/AmIWrong.flv";

&nbsp; &nbsp; [Embed(source="/../assets/DrawTool.swf")]
&nbsp; &nbsp; private var DrawToolEmbedded:Class;

&nbsp; &nbsp; private var scene:Scene3D;
&nbsp; &nbsp; private var camera:HoverCamera3D;
&nbsp; &nbsp; private var view:View3D;

&nbsp; &nbsp; private var planeGroup:ObjectContainer3D;
&nbsp; &nbsp;
&nbsp; &nbsp; private var doRotation:Boolean = true;
&nbsp; &nbsp;
&nbsp; &nbsp; public function Example007() {
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // set up the stage
&nbsp; &nbsp; &nbsp; stage.align = StageAlign.TOP_LEFT;
&nbsp; &nbsp; &nbsp; stage.scaleMode = StageScaleMode.NO_SCALE;

&nbsp; &nbsp; &nbsp; // Add resize event listener
&nbsp; &nbsp; &nbsp; stage.addEventListener(Event.RESIZE, onResize);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Initialise Away3D
&nbsp; &nbsp; &nbsp; init3D();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create the 3D objects
&nbsp; &nbsp; &nbsp; createScene();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Initialise frame-enter loop
&nbsp; &nbsp; &nbsp; this.addEventListener(Event.ENTER_FRAME, loop);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Initialise all 3D components.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function init3D():void {

&nbsp; &nbsp; &nbsp; // Create a new scene where all the 3D object will be rendered
&nbsp; &nbsp; &nbsp; scene = new Scene3D();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new camera, passing some initialisation parameters
&nbsp; &nbsp; &nbsp; camera = new HoverCamera3D({zoom:25, focus:30, distance:200});
&nbsp; &nbsp; &nbsp; camera.yfactor = 1;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new view that encapsulates the scene and the camera
&nbsp; &nbsp; &nbsp; view = new View3D({scene:scene, camera:camera});

&nbsp; &nbsp; &nbsp; // center the view to the middle of the stage
&nbsp; &nbsp; &nbsp; view.x = stage.stageWidth / 2;
&nbsp; &nbsp; &nbsp; view.y = stage.stageHeight / 2;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // ensure that the z-order is calculated correctly
&nbsp; &nbsp; &nbsp; view.renderer = Renderer.CORRECT_Z_ORDER;
&nbsp; &nbsp; &nbsp;
   &nbsp; &nbsp; &nbsp; addChild(view);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Create the objects of the scene
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function createScene():void {

&nbsp; &nbsp; &nbsp; // Video material using a flash streaming video URL
&nbsp; &nbsp; &nbsp; var frontMaterial:VideoMaterial = new VideoMaterial({file:videoURL});
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Movie material from an embedded flash animation
&nbsp; &nbsp; &nbsp; var topMaterial:MovieMaterial = new MovieMaterial(new DrawToolEmbedded(), {lockW:320, lockH:240, interactive:true, smooth:true, precision:5});
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Movie material from another class
&nbsp; &nbsp; &nbsp; var leftMaterial:MovieMaterial = new MovieMaterial(new Pong(), {smooth:true, precision:5});

&nbsp; &nbsp; &nbsp; // Create three planes with different material, blurred by default
&nbsp; &nbsp; &nbsp; // and position them them to create tree sides of a cube
&nbsp; &nbsp; &nbsp; var topPlane:Plane = new Plane({material:topMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; topPlane.rotationY = -180;
&nbsp; &nbsp; &nbsp; topPlane.y = 50;
&nbsp; &nbsp; &nbsp; topPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var leftPlane:Plane = new Plane({material:leftMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; leftPlane.rotationZ = -90;
&nbsp; &nbsp; &nbsp; leftPlane.rotationY = -90;
&nbsp; &nbsp; &nbsp; leftPlane.x = 50;
&nbsp; &nbsp; &nbsp; leftPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var frontPlane:Plane = new Plane({material:frontMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; frontPlane.rotationX = -90;
&nbsp; &nbsp; &nbsp; frontPlane.rotationZ = 180;
&nbsp; &nbsp; &nbsp; frontPlane.z = 50;
&nbsp; &nbsp; &nbsp; frontPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create an object container to group the sides of the cube
&nbsp; &nbsp; &nbsp; planeGroup = new ObjectContainer3D();
&nbsp; &nbsp; &nbsp; scene.addChild(planeGroup);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(topPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(leftPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(frontPlane);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Add mouse listeners to each plane for mouse down, over and out events
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseOut(onMouseLeavesObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseOut(onMouseLeavesObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseOut(onMouseLeavesObject);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Frame-enter event handler
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function loop(event:Event):void {
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // update camera position
&nbsp; &nbsp; &nbsp; updateCamera();
&nbsp; &nbsp; &nbsp; camera.hover();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Render the 3D scene
&nbsp; &nbsp; &nbsp; view.render();
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Update the camera position from mouse positions
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function updateCamera():void {
&nbsp; &nbsp; &nbsp; if (doRotation) {
&nbsp; &nbsp; &nbsp; &nbsp; camera.targetpanangle =  (stage.stageWidth - stage.mouseX) / stage.stageWidth * 90;
&nbsp; &nbsp; &nbsp; &nbsp; camera.targettiltangle = (stage.stageHeight - stage.mouseY) / stage.stageHeight * 70
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse click on plane. Makes the camera look
&nbsp; &nbsp;  * directly at the plane and move closer to it.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseClickOnObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; doRotation = false;

&nbsp; &nbsp; &nbsp; // Calculate angles necessary for camera&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var theta:Number = Math.atan2(object.x, object.z);
&nbsp; &nbsp; &nbsp; var len:Number = Math.sqrt(object.x*object.x + object.z*object.z);
&nbsp; &nbsp; &nbsp; var phi:Number = Math.atan2(object.y, len);

&nbsp; &nbsp; &nbsp; // rotate camera position
&nbsp; &nbsp; &nbsp; camera.targetpanangle = theta * 180 / Math.PI;
&nbsp; &nbsp; &nbsp; camera.targettiltangle = phi * 180 / Math.PI;

&nbsp; &nbsp; &nbsp; // move camera towards plane
&nbsp; &nbsp; &nbsp; Tweener.addTween(camera, {distance:150, time:0.5, transition:"easeOutSine"});
&nbsp; &nbsp; }&nbsp; &nbsp; 

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse over plane. Removes the blur filter.
&nbsp; &nbsp;  */&nbsp; &nbsp;
&nbsp; &nbsp; private function onMouseOverObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; object.filters = new Array();
&nbsp; &nbsp; }&nbsp; &nbsp;
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse out of plane. Adds blur filter and moves
&nbsp; &nbsp;  * camera away from plane if it isn't already.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseLeavesObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;

&nbsp; &nbsp; &nbsp; object.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp; Tweener.addTween(camera, {distance:200, time:0.5, transition:"easeOutSine"});
&nbsp; &nbsp; &nbsp; doRotation = true;
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Resize the scene when the stage resizes
&nbsp; &nbsp;  */&nbsp;
&nbsp; &nbsp; private function onResize(event:Event):void {
&nbsp; &nbsp; &nbsp; view.x = stage.stageWidth / 2;
&nbsp; &nbsp; &nbsp; view.y = stage.stageHeight / 2;
&nbsp; &nbsp; }

&nbsp; }
&nbsp;
}
</blockquote>
</pre>
</div>
<p>This (along with the other ActionScript classes shown below) produces a cube that rotates as the mouse moves. One face shows a Flash video stream (<a href="http://www.myspace.com/etiennedecrecy">Etienne de Crécy</a> : Am I Wrong), another is an interactive drawable surface and the third a simple <a href="http://en.wikipedia.org/wiki/Pong"><em>Pong</em></a> simulation. Each face is blurred until the mouse enters it. Clicking on a face moves the camera directly above it and moves towards it. Moving the mouse outside of a face blurs the face again and the cube regains its original size. You can see the finished result by clicking on the image below.</p>
<p><a href="http://www.tartiflop.com/away3d/FirstSteps/Example007.swf" class="lightwindow" params="lightwindow_width=640,lightwindow_height=480" title=""><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://blog.tartiflop.com/wp-content/uploads/2008/12/example007.png" border="0" width="480" height="320"/></a></p>
<p>As usual, the code for <em>Example007</em> follows the same style as shown in the previous articles of this series. Lets look at what has changed.</p>
<p>The constructor and initialisation of Away3D elements is virtually identical to before so not worth looking at here. Lets move onto the scene creation (<em>createScene()</em>)and see how we use these new materials. To start off with the materials are created.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; &nbsp; // Video material using a flash streaming video URL
&nbsp; &nbsp; &nbsp; var frontMaterial:VideoMaterial = new VideoMaterial({file:videoURL});
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Movie material from an embedded flash animation
&nbsp; &nbsp; &nbsp; var topMaterial:MovieMaterial = new MovieMaterial(new DrawToolEmbedded(), {lockW:320, lockH:240, interactive:true, smooth:true, precision:5});
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Movie material from another class
&nbsp; &nbsp; &nbsp; var leftMaterial:MovieMaterial = new MovieMaterial(new Pong(), {smooth:true, precision:5});
</blockquote>
</pre>
</div>
<p>As you can see, the <em>VideoMaterial</em> is very easy to create - simply pass the URL of the Flash video stream in the initialisation parameters and its ready! Compared to Papervision3D this is much easier (in fact, the internet connection and creation of the video stream is encapsulated in the <em>VideoMaterial</em> class). If you want the video to loop you can set the <em>loop</em> parameter to true, also sent in the initialisation parameters array. If you want to pause/play the video then you can access the <em>netStream</em> object directly from the material to control the playback.</p>
<p>The <em>MovieMaterial</em> is similarly easy to create. The constructor takes a <em>Sprite</em> object which will be mapped to the surface. I show two cases here: one where the object is an instantiation of a class in the same project, another where we embed a previously compiled Flash movie. Other than the <em>smooth</em> and <em>precision</em> parameters as we discussed in the <a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-3-texture-mapping/">texture mapping tutorial</a>, we can indicate that the material should be interactive by specifying the <em>interactive</em> parameter to be true. Similarly we can force a size of the <em>Sprite</em> by giving the <em>lockW</em> and <em>lockH</em> parameters being the width and height - note that these have no relation to the dimensions of the object to which the material is mapped.</p>
<p>These materials are used just like all the other materials presented in this series of articles: simply create an object and pass the material to it.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; &nbsp; // Create three planes with different material, blurred by default
&nbsp; &nbsp; &nbsp; // and position them them to create tree sides of a cube
&nbsp; &nbsp; &nbsp; var topPlane:Plane = new Plane({material:topMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; topPlane.rotationY = -180;
&nbsp; &nbsp; &nbsp; topPlane.y = 50;
&nbsp; &nbsp; &nbsp; topPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var leftPlane:Plane = new Plane({material:leftMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; leftPlane.rotationZ = -90;
&nbsp; &nbsp; &nbsp; leftPlane.rotationY = -90;
&nbsp; &nbsp; &nbsp; leftPlane.x = 50;
&nbsp; &nbsp; &nbsp; leftPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var frontPlane:Plane = new Plane({material:frontMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; frontPlane.rotationX = -90;
&nbsp; &nbsp; &nbsp; frontPlane.rotationZ = 180;
&nbsp; &nbsp; &nbsp; frontPlane.z = 50;
&nbsp; &nbsp; &nbsp; frontPlane.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create an object container to group the sides of the cube
&nbsp; &nbsp; &nbsp; planeGroup = new ObjectContainer3D();
&nbsp; &nbsp; &nbsp; scene.addChild(planeGroup);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(topPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(leftPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(frontPlane);
</blockquote>
</pre>
</div>
<p>As you can see, three planes are created, as shown in previous articles, and a different material passed to each one. These planes are then rotated and translated to form three faces of a cube. I also added a <em>BlurFilter</em> just to show how adding effects to Away3D objects is very simple as well.</p>
<p>Moving onto the mouse event listeners, for this example I don&#8217;t have a stage mouse listener to rotate the scene, only listeners for the <em>MouseEvent3D</em> events. Here, for each face, I add a mouse down, mouse over and mouse up listener that are triggered only when the mouse interacts with a specific 3D object.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; &nbsp; // Add mouse listeners to each plane for mouse down, over and out events
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseDown(onMouseClickOnObject);
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseOver(onMouseOverObject);
&nbsp; &nbsp; &nbsp; topPlane.addOnMouseOut(onMouseLeavesObject);
&nbsp; &nbsp; &nbsp; leftPlane.addOnMouseOut(onMouseLeavesObject);
&nbsp; &nbsp; &nbsp; frontPlane.addOnMouseOut(onMouseLeavesObject);
</blockquote>
</pre>
</div>
<p>Looking first at the mouse down listener, the objective is to make the camera look directly at a cube face.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse click on plane. Makes the camera look
&nbsp; &nbsp;  * directly at the plane and move closer to it.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseClickOnObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; doRotation = false;

&nbsp; &nbsp; &nbsp; // Calculate angles necessary for camera&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var theta:Number = Math.atan2(object.x, object.z);
&nbsp; &nbsp; &nbsp; var len:Number = Math.sqrt(object.x*object.x + object.z*object.z);
&nbsp; &nbsp; &nbsp; var phi:Number = Math.atan2(object.y, len);

&nbsp; &nbsp; &nbsp; // rotate camera position
&nbsp; &nbsp; &nbsp; camera.targetpanangle = theta * 180 / Math.PI;
&nbsp; &nbsp; &nbsp; camera.targettiltangle = phi * 180 / Math.PI;

&nbsp; &nbsp; &nbsp; // move camera towards plane
&nbsp; &nbsp; &nbsp; Tweener.addTween(camera, {distance:150, time:0.5, transition:"easeOutSine"});
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>From the object location we can calculate a camera <em>tilt</em> and <em>pan</em> angle. Using the properties <em>targetpanangle</em> and <em>targettiltangle</em> the camera moves gently towards the desired position. To move the camera towards the object I&#8217;ve added a <em>Tweener</em> call (as we looked at in <a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-4-scene-interaction/">scene interaction</a>). In this function we also turn off the automatic camera movement (the camera follows the mouse otherwise as shown below).</p>
<p>Moving on to the mouse over event listener, here we simply remove the <em>BlurFilter</em> making the face come into focus.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse over plane. Removes the blur filter.
&nbsp; &nbsp;  */&nbsp; &nbsp;
&nbsp; &nbsp; private function onMouseOverObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; object.filters = new Array();
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>Finally for the listeners, the mouse out event listener adds the <em>BlurFilter</em>, ensures that the camera moves freely with the mouse movement again and executes another <em>Tweener</em> call to take the camera back to its original distance.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse out of plane. Adds blur filter and moves
&nbsp; &nbsp;  * camera away from plane if it isn't already.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseLeavesObject(event:MouseEvent3D):void {
&nbsp; &nbsp; &nbsp; var object:Object3D = event.object;

&nbsp; &nbsp; &nbsp; object.filters.push(new BlurFilter(8, 8));
&nbsp; &nbsp; &nbsp; Tweener.addTween(camera, {distance:200, time:0.5, transition:"easeOutSine"});
&nbsp; &nbsp; &nbsp; doRotation = true;
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>That leaves us with just the event loop and the camera update function. You&#8217;ll notice that the update loop is very similar to before - there is no rotation applied to the scene objects this time but we do update the camera position.</p>
<p>To move the camera automatically, the relative mouse position on the screen is simply converted into pan and tilt angles and applied to the camera target angles as shown below. </p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Update the camera position from mouse positions
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function updateCamera():void {
&nbsp; &nbsp; &nbsp; if (doRotation) {
&nbsp; &nbsp; &nbsp; &nbsp; camera.targetpanangle =  (stage.stageWidth - stage.mouseX) / stage.stageWidth * 90;
&nbsp; &nbsp; &nbsp; &nbsp; camera.targettiltangle = (stage.stageHeight - stage.mouseY) / stage.stageHeight * 70
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>Note that if the user has clicked on a cube face then the <em>doRotation</em> boolean is false allowing the user to interact more easily with the different movie or video stream material.</p>
<p>And that&#8217;s all there is to it! As you&#8217;ll see, mouse events are mapped effectively to the embedded Flash animations, even in 3D, and the streaming of video is very clean and very simple to implement. Hopefully this has provided a useful introduction to these types of materials, don&#8217;t hesitate to look into the code itself to understand them more. As always comments, questions and suggestions are very welcome too!</p>
<p>As promised, just for completeness, you&#8217;ll find the source for the drawing tool movie and the automated Pong game below - going into the detail of these is out of the scope of this article !</p>
<p>DrawTool.as :</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {
&nbsp; import flash.display.Sprite;
&nbsp; import flash.events.MouseEvent;
&nbsp; import flash.text.TextField;
&nbsp; import flash.text.TextFieldAutoSize;
&nbsp; import flash.text.TextFormat;
&nbsp; 

&nbsp; public class DrawTool extends Sprite {

&nbsp; &nbsp; private var isDrawing:Boolean = false;
&nbsp; &nbsp; private var sprite:Sprite;

&nbsp; &nbsp; public function DrawTool() {

&nbsp; &nbsp; &nbsp; // create a drawing surface
&nbsp; &nbsp; &nbsp; sprite = new Sprite();
&nbsp; &nbsp; &nbsp; sprite.graphics.beginFill(0xEEEEEE);
&nbsp; &nbsp; &nbsp; sprite.graphics.moveTo(0, 0);
&nbsp; &nbsp; &nbsp; sprite.graphics.lineTo(320, 0);
&nbsp; &nbsp; &nbsp; sprite.graphics.lineTo(320, 240);
&nbsp; &nbsp; &nbsp; sprite.graphics.lineTo(0, 240);
&nbsp; &nbsp; &nbsp; sprite.graphics.endFill();
&nbsp; &nbsp; &nbsp; addChild(sprite);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // create text and format
&nbsp; &nbsp; &nbsp; var textFormat:TextFormat = new TextFormat();
&nbsp; &nbsp; &nbsp; textFormat.size = 30;
&nbsp; &nbsp; &nbsp; textFormat.font = "Arial";
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var text:TextField = new TextField();
&nbsp; &nbsp; &nbsp; text.x = 50;
&nbsp; &nbsp; &nbsp; text.y = 100;
&nbsp; &nbsp; &nbsp; text.textColor = 0x222222;
&nbsp; &nbsp; &nbsp; text.text = "click to draw!";
&nbsp; &nbsp; &nbsp; text.setTextFormat(textFormat);
&nbsp; &nbsp; &nbsp; text.autoSize = TextFieldAutoSize.LEFT;
&nbsp; &nbsp; &nbsp; text.selectable = false;
&nbsp; &nbsp; &nbsp; addChild(text);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // listen to mouse events
&nbsp; &nbsp; &nbsp; this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
&nbsp; &nbsp; &nbsp; this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
&nbsp; &nbsp; &nbsp; this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse down event. Starts drawing circles.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseDown(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; isDrawing = true;
&nbsp; &nbsp; &nbsp; drawCircle(this.mouseX, this.mouseY);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse up event. Stops drawing circles.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseUp(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; isDrawing = false;
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Event listener for mouse move event. Draws a circle.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseMove(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; if (isDrawing) {
&nbsp; &nbsp; &nbsp; &nbsp; drawCircle(this.mouseX, this.mouseY);
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Function to draw a circle.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function drawCircle(x:int, y:int):void {
&nbsp; &nbsp; &nbsp; sprite.graphics.beginFill(Math.random() * 0xFFFFFF, 0.5);
&nbsp; &nbsp; &nbsp; sprite.graphics.drawCircle(x, y, 5);
&nbsp; &nbsp; &nbsp; sprite.graphics.endFill();
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; }
}
</blockquote>
</pre>
</div>
<p>Pong.as :</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {
&nbsp; import flash.display.Sprite;
&nbsp; import flash.events.Event;
&nbsp;
&nbsp; [SWF(backgroundColor="#000000")]

&nbsp; /**
&nbsp;  * Simple computerised Pong copy.
&nbsp;  */
&nbsp; public class Pong extends Sprite {

&nbsp; &nbsp; private static const COURT_WIDTH:Number = 320;
&nbsp; &nbsp; private static const COURT_HEIGHT:Number = 240;
&nbsp; &nbsp; private static const BALL_WIDTH:Number = 5;
&nbsp; &nbsp; private static const BAT_WIDTH:Number = 5;
&nbsp; &nbsp; private static const BAT_HEIGHT:Number = 30;
&nbsp; &nbsp; private static const COLOUR:Number = 0xDDDDDD;

&nbsp; &nbsp; private var player1:Sprite;
&nbsp; &nbsp; private var player2:Sprite;
&nbsp; &nbsp; private var ball:Sprite;
&nbsp; &nbsp;
&nbsp; &nbsp; private var ballSpeedX:Number;
&nbsp; &nbsp; private var ballSpeedY:Number;
&nbsp; &nbsp; private var activePlayer:int;
&nbsp; &nbsp; private var playerIsMoving:Boolean = false;
&nbsp; &nbsp; private var playerSpeed:Number;
&nbsp; &nbsp; private var playerDestination:Number;

&nbsp; &nbsp; public function Pong() {

&nbsp; &nbsp; &nbsp; createCourt();
&nbsp; &nbsp; &nbsp; addPlayer1();
&nbsp; &nbsp; &nbsp; addPlayer2();
&nbsp; &nbsp; &nbsp; addBall();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; this.addEventListener(Event.ENTER_FRAME, onFrameEnter);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Creates the court with net
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function createCourt():void {
&nbsp; &nbsp; &nbsp; var background:Sprite = new Sprite();
&nbsp; &nbsp; &nbsp; background.graphics.beginFill(0x000000);
&nbsp; &nbsp; &nbsp; background.graphics.moveTo(0, 0);
&nbsp; &nbsp; &nbsp; background.graphics.lineTo(COURT_WIDTH, 0);
&nbsp; &nbsp; &nbsp; background.graphics.lineTo(COURT_WIDTH, COURT_HEIGHT);
&nbsp; &nbsp; &nbsp; background.graphics.lineTo(0, COURT_HEIGHT);
&nbsp; &nbsp; &nbsp; background.graphics.endFill();
&nbsp; &nbsp; &nbsp; addChild(background);

&nbsp; &nbsp; &nbsp; var net:Sprite = new Sprite();

&nbsp; &nbsp; &nbsp; var nPoints:Number = 32;
&nbsp; &nbsp; &nbsp; var pointHeight:Number = (COURT_HEIGHT / nPoints);
&nbsp; &nbsp; &nbsp; var drawHeight:Number = pointHeight * 0.6;
&nbsp; &nbsp; &nbsp; var drawWidth:Number = drawHeight / 2;

&nbsp; &nbsp; &nbsp; // Create dashed net
&nbsp; &nbsp; &nbsp; for (var i:Number = 0; i &lt; nPoints; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; var x:Number = COURT_WIDTH / 2 - drawWidth / 2;
&nbsp; &nbsp; &nbsp; &nbsp; var y:Number = i*pointHeight;
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.beginFill(COLOUR);
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.moveTo(x, y);
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.lineTo(x+drawWidth, y);
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.lineTo(x+drawWidth, y+drawHeight);
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.lineTo(x, y+drawHeight);
&nbsp; &nbsp; &nbsp; &nbsp; net.graphics.endFill();
&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; addChild(net);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Add left-hand player
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function addPlayer1():void {
&nbsp; &nbsp; &nbsp; player1 = new Sprite();
&nbsp; &nbsp; &nbsp; createBat(player1);
&nbsp; &nbsp; &nbsp; player1.x = 20;
&nbsp; &nbsp; &nbsp; player1.y = (COURT_HEIGHT / 2) - (BAT_HEIGHT / 2);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; addChild(player1);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Add right-hand player
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function addPlayer2():void {
&nbsp; &nbsp; &nbsp; player2 = new Sprite();
&nbsp; &nbsp; &nbsp; createBat(player2);
&nbsp; &nbsp; &nbsp; player2.x = COURT_WIDTH - 20 - BAT_WIDTH;
&nbsp; &nbsp; &nbsp; player2.y = (COURT_HEIGHT / 2) - (BAT_HEIGHT / 2);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; addChild(player2);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Create a bat
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function createBat(player:Sprite):void {
&nbsp; &nbsp; &nbsp; player.graphics.beginFill(COLOUR);
&nbsp; &nbsp; &nbsp; player.graphics.moveTo(0, 0);
&nbsp; &nbsp; &nbsp; player.graphics.lineTo(BAT_WIDTH, 0);
&nbsp; &nbsp; &nbsp; player.graphics.lineTo(BAT_WIDTH, BAT_HEIGHT);
&nbsp; &nbsp; &nbsp; player.graphics.lineTo(0, BAT_HEIGHT);
&nbsp; &nbsp; &nbsp; player.graphics.endFill();
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Add ball to scene and initialise speeds
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function addBall():void {
&nbsp; &nbsp; &nbsp; ball = new Sprite();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; ball.graphics.beginFill(COLOUR);
&nbsp; &nbsp; &nbsp; ball.graphics.moveTo(0, 0);
&nbsp; &nbsp; &nbsp; ball.graphics.lineTo(BALL_WIDTH, 0);
&nbsp; &nbsp; &nbsp; ball.graphics.lineTo(BALL_WIDTH, BALL_WIDTH);
&nbsp; &nbsp; &nbsp; ball.graphics.lineTo(0, BALL_WIDTH);
&nbsp; &nbsp; &nbsp; ball.graphics.endFill();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; ball.x = 0;
&nbsp; &nbsp; &nbsp; ball.y = 20;

&nbsp; &nbsp; &nbsp; addChild(ball);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; ballSpeedX = 6;
&nbsp; &nbsp; &nbsp; ballSpeedY = 5;
&nbsp; &nbsp; &nbsp; activePlayer = 1;
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Called at every frame
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onFrameEnter(event:Event):void {
&nbsp; &nbsp; &nbsp; // update ball position
&nbsp; &nbsp; &nbsp; updateBall();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // update player position
&nbsp; &nbsp; &nbsp; updateActivePlayer();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // detect hits
&nbsp; &nbsp; &nbsp; hitTest();
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Updates the ball position taking into account court dimensions
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function updateBall():void {
&nbsp; &nbsp; &nbsp; ball.x = ball.x + ballSpeedX;
&nbsp; &nbsp; &nbsp; ball.y = ball.y + ballSpeedY;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Detect if ball escapes a player
&nbsp; &nbsp; &nbsp; if (ball.x &gt; COURT_WIDTH - BALL_WIDTH) {
&nbsp; &nbsp; &nbsp; &nbsp; ball.x = 0;
&nbsp; &nbsp; &nbsp; &nbsp; ball.y = COURT_HEIGHT / 2;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ballSpeedY = Math.random() * 10;
&nbsp; &nbsp; &nbsp; } else if (ball.x &lt; 0) {
&nbsp; &nbsp; &nbsp; &nbsp; ball.x = COURT_WIDTH - BALL_WIDTH;
&nbsp; &nbsp; &nbsp; &nbsp; ball.y = COURT_HEIGHT / 2;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; ballSpeedY = -Math.random() * 10;
&nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; // Detect wall hits: invert ball y-direction and initiate player position calculation
&nbsp; &nbsp; &nbsp; if (ball.y &lt; 0) {
&nbsp; &nbsp; &nbsp; &nbsp; ball.y = 0;
&nbsp; &nbsp; &nbsp; &nbsp; ballSpeedY = -ballSpeedY;
&nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = false;
&nbsp; &nbsp; &nbsp; } else if (ball.y &gt; COURT_HEIGHT - BALL_WIDTH) {
&nbsp; &nbsp; &nbsp; &nbsp; ball.y = COURT_HEIGHT - BALL_WIDTH;
&nbsp; &nbsp; &nbsp; &nbsp; ballSpeedY = -ballSpeedY;
&nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = false;
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Updates player position or calculates new position
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function updateActivePlayer():void {

&nbsp; &nbsp; &nbsp; var player:Sprite;
&nbsp; &nbsp; &nbsp; var calculatedTime:Number;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // calculate time for ball to reach player
&nbsp; &nbsp; &nbsp; if (activePlayer == 0) {
&nbsp; &nbsp; &nbsp; &nbsp; player = player1;
&nbsp; &nbsp; &nbsp; &nbsp; calculatedTime = Math.abs(player.x + BAT_WIDTH - ball.x) / Math.abs(ballSpeedX);
&nbsp; &nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; player = player2;
&nbsp; &nbsp; &nbsp; &nbsp; calculatedTime = Math.abs(player.x - (ball.x + BALL_WIDTH)) / Math.abs(ballSpeedX);
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; if (playerIsMoving) {
&nbsp; &nbsp; &nbsp; &nbsp; // update player position
&nbsp; &nbsp; &nbsp; &nbsp; player.y = player.y + playerSpeed;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // ensure that player does not leave the court
&nbsp; &nbsp; &nbsp; &nbsp; if (player.y &gt; COURT_HEIGHT - BAT_HEIGHT) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; player.y = COURT_HEIGHT - BAT_HEIGHT;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = false;
&nbsp; &nbsp; &nbsp; &nbsp; } else if (player.y &lt; 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; player.y = 0;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // determine if player is more or less in position
&nbsp; &nbsp; &nbsp; &nbsp; if (Math.abs(player.y - playerDestination) &lt; BAT_HEIGHT / 4) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; // calculate expected ball position
&nbsp; &nbsp; &nbsp; &nbsp; var calculatedY:Number = ball.y + ballSpeedY * calculatedTime;
&nbsp; &nbsp; &nbsp; &nbsp; var random:Boolean = false;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; if (calculatedY &lt; 0 || calculatedY &gt; COURT_HEIGHT) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; calculatedY = Math.random() * COURT_HEIGHT;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; random = true;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // calculate desired player position with a random element
&nbsp; &nbsp; &nbsp; &nbsp; playerDestination = calculatedY - (BAT_HEIGHT / 2) + (0.4 * (Math.random() - 0.5) * BAT_HEIGHT);
&nbsp; &nbsp; &nbsp; &nbsp; if (playerDestination &lt; 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerDestination = 0;
&nbsp; &nbsp; &nbsp; &nbsp; } else if (playerDestination &gt; COURT_HEIGHT - BAT_HEIGHT) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerDestination = COURT_HEIGHT - BAT_HEIGHT;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // Calculate a random speed for player
&nbsp; &nbsp; &nbsp; &nbsp; if (player.y &lt; playerDestination) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerSpeed = 5 + Math.random() * 10;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerSpeed = -5 + Math.random() * -10;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // increase speed if player can't work out ball position
&nbsp; &nbsp; &nbsp; &nbsp; if (random) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerSpeed = playerSpeed * 2;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; // only move player if really necessary (+ stupidity estimate)
&nbsp; &nbsp; &nbsp; &nbsp; if (Math.abs(playerDestination - player.y) &gt; BAT_HEIGHT / 2) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; playerIsMoving = true;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Determine if ball hits a bat
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function hitTest():void {
&nbsp; &nbsp; &nbsp; var player:Sprite;
&nbsp; &nbsp; &nbsp; var check:Boolean = false;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // check to see if ball is at same x position as bat
&nbsp; &nbsp; &nbsp; if (activePlayer == 0) {
&nbsp; &nbsp; &nbsp; &nbsp; player = player1;
&nbsp; &nbsp; &nbsp; &nbsp; if (ball.x &gt; player1.x &#038;&#038; ball.x &lt; player1.x + BAT_WIDTH) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; check = true;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; player = player2;
&nbsp; &nbsp; &nbsp; &nbsp; if (ball.x + BALL_WIDTH &gt; player2.x &#038;&#038; ball.x + BALL_WIDTH &lt; player2.x + BAT_WIDTH) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; check = true;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; if (check) {
&nbsp; &nbsp; &nbsp; &nbsp; // verify y position
&nbsp; &nbsp; &nbsp; &nbsp; if ((ball.y + BALL_WIDTH &lt;= player.y + BAT_HEIGHT) &#038;&#038; (ball.y &gt;= player.y)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // hit, change player
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; activePlayer = 1 - activePlayer;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // reverse ball direction
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ballSpeedX = -ballSpeedX;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // calculate new ball speed in y&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var batPosition:Number = (ball.y + (BALL_WIDTH / 2)) - (player.y + (BAT_HEIGHT / 2));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ballSpeedY =  batPosition / BAT_HEIGHT * 10 * (1 + Math.random() * 0.1);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; }
}
</blockquote>
</pre>
</div>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=bo8GX0"><img src="http://feeds.feedburner.com/~a/tartiflop?i=bo8GX0" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/485764856" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-7-movie-and-video-materials/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-7-movie-and-video-materials/</feedburner:origLink></item>
		<item>
		<title>Musical Interlude - Art Brut</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/483889151/</link>
		<comments>http://blog.tartiflop.com/2008/12/musical-interlude-art-brut/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 19:08:26 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Music]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=287</guid>
		<description><![CDATA[Discover Art Brut!
]]></description>
			<content:encoded><![CDATA[<div style="width:220px;height:55px;"><object width="220" height="55"><param name="movie" value="http://www.deezer.com/embedded/small-widget-v2.swf?idSong=1453317&#038;colorBackground=0x555552&#038;textColor1=0xFFFFFF&#038;colorVolume=0x39D1FD&#038;autoplay=0"></param><embed src="http://www.deezer.com/embedded/small-widget-v2.swf?idSong=1453317&#038;colorBackground=0x525252&#038;textColor1=0xFFFFFF&#038;colorVolume=0x39D1FD&#038;autoplay=0" type="application/x-shockwave-flash" width="220" height="55"></embed></object><br /><font size='1' color ='#000000'>Discover <a href='http://www.deezer.com/en/art-brut.html'>Art Brut</a>!</font></div>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=k7M6ka"><img src="http://feeds.feedburner.com/~a/tartiflop?i=k7M6ka" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/483889151" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/musical-interlude-art-brut/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/musical-interlude-art-brut/</feedburner:origLink></item>
		<item>
		<title>An Alchemy speed test</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/483882389/</link>
		<comments>http://blog.tartiflop.com/2008/12/an-alchemy-speed-test/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 18:59:14 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Alchemy]]></category>

		<category><![CDATA[Flex]]></category>

		<category><![CDATA[Actionscript 3]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=277</guid>
		<description><![CDATA[Following from my previous articles on setting up an Alchemy development environment in Flex Builder 3 and passing/returning objects to/from C++, I wanted to test some of the claims of the speed increase possible with the use of this tool.
With the particular interest in 3D Flash applications, I wanted to test specific mathematical operations using [...]]]></description>
			<content:encoded><![CDATA[<p>Following from my previous articles on <a href="http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/">setting up an Alchemy development environment in Flex Builder 3</a> and <a href="http://blog.tartiflop.com/2008/12/passingreturning-objects-tofrom-c-using-alchemy/">passing/returning objects to/from C++</a>, I wanted to test some of the claims of the speed increase possible with the use of this tool.</p>
<p>With the particular interest in 3D Flash applications, I wanted to test specific mathematical operations using vectors and matrices, namely cross products, normalisation, rotation matrix calculations and vector transformations.</p>
<p>I had initially aimed to create a mathematical library where these functions can be calculated on native ActionScript objects - for example create a function to calculate the cross product of two vectors. However one aspect of Alchemy that became immediately evident is that the cost of marshaling data through the AS3-C++ API is horrendously expensive. This is quite normal so I guess I was naive to expect good results from this. But to give you an example of how expensive this is, for a simple iterative calculation of the cross product of two vectors followed by a normalisation: if the mathematical functions are performed in C++, <em>ie</em> iteratively calling the Alchemy compiled functions, the result is about 1000 times slower than natively performing the calculations in AS3!</p>
<p>So, my first advice is: limit the number of Alchemy calls!!</p>
<p>Anyway, in this article I&#8217;ll concentrate on performing <em>pure</em> C++ speed tests (called from AS3)  - so computationally intensive calculations performed in a single Alchemy call - compared to the equivalent <em>pure</em> AS3 speed tests.</p>
<p>The tests performed here concentrate on vector and matrix operations. I&#8217;ve therefore created very simple Vector and Matrix classes in C++. The Vector class is used to perform dot product, normalisation and cross product operations as shown below.</p>
<p>Vector3D.h :</p>
<div id="codeSnippet">
<pre>
<blockquote>

#ifndef VECTOR3D_H_
#define VECTOR3D_H_

#include "AS3.h"

class Vector3D {

public :
&nbsp; Vector3D();
&nbsp; Vector3D(double x, double y, double z);
&nbsp; Vector3D(const AS3_Val&#038; as3Vector);
&nbsp; virtual ~Vector3D();

&nbsp; double dot(const Vector3D&#038; v) const;
&nbsp; Vector3D cross(const Vector3D&#038; v) const;
&nbsp; double modulus() const;
&nbsp; Vector3D normalise() const;

&nbsp; void setX(double x);
&nbsp; void setY(double y);
&nbsp; void setZ(double z);
&nbsp;
&nbsp; double getX() const;
&nbsp; double getY() const;
&nbsp; double getZ() const;

private :
&nbsp; double _x;
&nbsp; double _y;
&nbsp; double _z;

};

#endif /*VECTOR3D_H_*/
</blockquote>
</pre>
</div>
<p>Vector3D.cpp :</p>
<div id="codeSnippet">
<pre>
<blockquote>

#include "Vector3D.h"
#include &lt;cmath&gt;

Vector3D::Vector3D() :
&nbsp; _x(0),
&nbsp; _y(0),
&nbsp; _z(0) {
}

Vector3D::Vector3D(const AS3_Val&#038; as3Vector) {
&nbsp; AS3_ObjectValue(as3Vector, "x:DoubleType, y:DoubleType, z:DoubleType", &#038;_x, &#038;_y, &#038;_z);
}

Vector3D::Vector3D(double x, double y, double z) :
&nbsp; _x(x),
&nbsp; _y(y),
&nbsp; _z(z) {
}

Vector3D::~Vector3D() {
}

double Vector3D::dot(const Vector3D&#038; v) const {
&nbsp; return v._x*_x + v._y*_y + v._z*_z;
}

Vector3D Vector3D::cross(const Vector3D&#038; v) const {

&nbsp; Vector3D result;
&nbsp; result._x = _y*v._z - _z*v._y;
&nbsp; result._y = _z*v._x - _x*v._z;
&nbsp; result._z = _x*v._y - _y*v._x;
&nbsp;
&nbsp; return result;
}

double Vector3D::modulus() const {
&nbsp; return std::sqrt(_x*_x + _y*_y + _z*_z);
}

Vector3D Vector3D::normalise() const {
&nbsp; double mod = modulus();
&nbsp; return Vector3D(_x/mod, _y/mod, _z/mod);
}

void Vector3D::setX(double x) {
&nbsp; _x = x;
}

void Vector3D::setY(double y) {
&nbsp; _y = y;
}

void Vector3D::setZ(double z) {
&nbsp; _z = z;
}

double Vector3D::getX() const {
&nbsp; return _x;
}

double Vector3D::getY() const {
&nbsp; return _y;
}

double Vector3D::getZ() const {
&nbsp; return _z;
}
</blockquote>
</pre>
</div>
<p>One point, specific to Alchemy, is in one of the constructors for the Vector3D: the properties are extracted from the passed AS3 Vector3D object, as discussed in my previous article.</p>
<p>The Matrix3D C++ class is as follows.</p>
<p>Matrix3D.h :</p>
<div id="codeSnippet">
<pre>
<blockquote>

#ifndef MATRIX3D_H_
#define MATRIX3D_H_

#include "Vector3D.h"

class Matrix3D {

public :
&nbsp; Matrix3D();
&nbsp; virtual ~Matrix3D();

&nbsp; void setRotationX(double degrees);
&nbsp; void setRotationY(double degrees);
&nbsp; void setRotationZ(double degrees);

&nbsp; void setIdentity();

&nbsp; Vector3D transformVector(const Vector3D&#038; vector) const;

private :
&nbsp; double _M00;
&nbsp; double _M01;
&nbsp; double _M02;
&nbsp; double _M10;
&nbsp; double _M11;
&nbsp; double _M12;
&nbsp; double _M20;
&nbsp; double _M21;
&nbsp; double _M22;

};

#endif /*MATRIX3D_H_*/
</blockquote>
</pre>
</div>
<p>Matrix3D.cpp :</p>
<div id="codeSnippet">
<pre>
<blockquote>

#include "Matrix3D.h"
#include &lt;cmath&gt;

Matrix3D::Matrix3D() :
&nbsp; _M00(1),
&nbsp; _M01(0),
&nbsp; _M02(0),
&nbsp; _M10(0),
&nbsp; _M11(1),
&nbsp; _M12(0),
&nbsp; _M20(0),
&nbsp; _M21(0),
&nbsp; _M22(1) {
}

Matrix3D::~Matrix3D() {
}

void Matrix3D::setIdentity() {
&nbsp; _M00 = 1;
&nbsp; _M01 = 0;
&nbsp; _M02 = 0;
&nbsp; _M10 = 0;
&nbsp; _M11 = 1;
&nbsp; _M12 = 0;
&nbsp; _M20 = 0;
&nbsp; _M21 = 0;
&nbsp; _M22 = 1;
}

void Matrix3D::setRotationX(double degrees) {
&nbsp; setIdentity();
&nbsp; double radians = degrees / 180 * M_PI;
&nbsp;
&nbsp; _M11 = cos(radians);
&nbsp; _M12 = -sin(radians);
&nbsp; _M21 = sin(radians);
&nbsp; _M22 = cos(radians);
}

void Matrix3D::setRotationY(double degrees) {
&nbsp; setIdentity();
&nbsp; double radians = degrees / 180 * M_PI;
&nbsp;
&nbsp; _M00 = cos(radians);
&nbsp; _M02 = sin(radians);
&nbsp; _M20 = -sin(radians);
&nbsp; _M22 = cos(radians);
}

void Matrix3D::setRotationZ(double degrees) {
&nbsp; setIdentity();
&nbsp; double radians = degrees / 180 * M_PI;
&nbsp;
&nbsp; _M00 = cos(radians);
&nbsp; _M01 = -sin(radians);
&nbsp; _M10 = sin(radians);
&nbsp; _M11 = cos(radians);
}

Vector3D Matrix3D::transformVector(const Vector3D&#038; vector) const {
&nbsp; Vector3D result;
&nbsp;
&nbsp; result.setX(_M00*vector.getX() + _M01*vector.getY() + _M02*vector.getZ());
&nbsp; result.setY(_M10*vector.getX() + _M11*vector.getY() + _M12*vector.getZ());
&nbsp; result.setZ(_M20*vector.getX() + _M21*vector.getY() + _M22*vector.getZ());
&nbsp;
&nbsp; return result;
}
</blockquote>
</pre>
</div>
<p>One of the objectives of using the Matrix3D class is to test the performance of the trigonometric functions. A common source of intensive calculations in 3D graphics is the rotation of vectors so this provides a useful test directly aimed at this field.</p>
<p>Two tests are to be examined: one for cross product calculations and another for matrix transformations. These are defined in the <em>main.cpp</em> file.</p>
<div id="codeSnippet">
<pre>
<blockquote>

#include "AS3.h"
#include "Vector3D.h"
#include "Matrix3D.h"

AS3_Val speedTest1(void* self, AS3_Val args) {

&nbsp; // Declare AS3 variables
&nbsp; AS3_Val as3Vector1;
&nbsp; AS3_Val as3Vector2;
&nbsp;
&nbsp; // Extract variables from arguments array
&nbsp; AS3_ArrayValue(args, "AS3ValType, AS3ValType", &#038;as3Vector1, &#038;as3Vector2);
&nbsp;
&nbsp; // Create native C++ objects with AS3 parameters
&nbsp; Vector3D vector1(as3Vector1);
&nbsp; Vector3D vector2(as3Vector2);
&nbsp;
&nbsp; Vector3D vector3;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; // Speed test : calculate cross products and normalise
&nbsp; for (int i = 0; i &lt; 1000000; i++) {
&nbsp; &nbsp; vector3 = vector1.cross(vector2);
&nbsp; &nbsp; vector3 = vector3.normalise();
&nbsp; &nbsp; vector1 = vector2;
&nbsp; &nbsp; vector2 = vector3;
&nbsp; }

&nbsp; // Obtain a class descriptor for the AS3 Vector3D class
&nbsp; AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));
&nbsp; AS3_Val params = AS3_Array("");
&nbsp;
&nbsp; // Construct a new AS3 Vector3D object with empty parameters
&nbsp; AS3_Val result = AS3_New(vector3DClass, params);
&nbsp;
&nbsp; // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
&nbsp; AS3_Set(result, AS3_String("x"), AS3_Number(vector3.getX()));
&nbsp; AS3_Set(result, AS3_String("y"), AS3_Number(vector3.getY()));
&nbsp; AS3_Set(result, AS3_String("z"), AS3_Number(vector3.getZ()));

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(params);
&nbsp; AS3_Release(vector3DClass);
&nbsp;
&nbsp; // return the AS3 Vector
&nbsp; return result;
}

AS3_Val speedTest2(void* self, AS3_Val args) {

&nbsp; // Declare AS3 variable
&nbsp; AS3_Val as3Vector;
&nbsp;
&nbsp; // Extract variables from arguments array
&nbsp; AS3_ArrayValue(args, "AS3ValType", &#038;as3Vector);

&nbsp; // Create native C++ object with AS3 parameters
&nbsp; Vector3D vector(as3Vector);
&nbsp;
&nbsp; Vector3D copy = vector;
&nbsp;
&nbsp; Matrix3D rotationX;
&nbsp; Matrix3D rotationY;
&nbsp; Matrix3D rotationZ;
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; // Speed test : calculate rotation matrices and transform vector
&nbsp; for (int i = 0; i &lt; 1000; i++) {
&nbsp; &nbsp; vector = copy;
&nbsp; &nbsp; for (double ang = 0; ang &lt; 180; ang++) {
&nbsp; &nbsp; &nbsp; rotationX.setRotationX(ang);
&nbsp; &nbsp; &nbsp; rotationY.setRotationY(ang);
&nbsp; &nbsp; &nbsp; rotationZ.setRotationZ(ang);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; vector = rotationX.transformVector(vector);
&nbsp; &nbsp; &nbsp; vector = rotationY.transformVector(vector);
&nbsp; &nbsp; &nbsp; vector = rotationZ.transformVector(vector);
&nbsp; &nbsp; }
&nbsp; }

&nbsp; // Obtain a class descriptor for the AS3 Vector3D class
&nbsp; AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));
&nbsp; AS3_Val params = AS3_Array("");
&nbsp;
&nbsp; // Construct a new AS3 Vector3D object with empty parameters
&nbsp; AS3_Val result = AS3_New(vector3DClass, params);
&nbsp;
&nbsp; // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
&nbsp; AS3_Set(result, AS3_String("x"), AS3_Number(vector.getX()));
&nbsp; AS3_Set(result, AS3_String("y"), AS3_Number(vector.getY()));
&nbsp; AS3_Set(result, AS3_String("z"), AS3_Number(vector.getZ()));

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(params);
&nbsp; AS3_Release(vector3DClass);
&nbsp;
&nbsp; // return the AS3 Vector
&nbsp; return result;
}

/**
 * Main entry point for Alchemy compiler. Declares all functions available
 * through the Alchemy bridge.
 */
int main() {
&nbsp; // Declare all methods exposed to AS3 typed as Function instances
&nbsp; AS3_Val speedTest1Method = AS3_Function(NULL, speedTest1);
&nbsp; AS3_Val speedTest2Method = AS3_Function(NULL, speedTest2);

&nbsp; // Construct an object that contains references to all the functions
&nbsp; AS3_Val result = AS3_Object("speedTest1:AS3ValType, speedTest2:AS3ValType", speedTest1Method, speedTest2Method);

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(speedTest1Method);
&nbsp; AS3_Release(speedTest2Method);

&nbsp; // Notify the bridge of what has been created -- THIS DOES NOT RETURN!
&nbsp; AS3_LibInit(result);

&nbsp; // Should never get here!
&nbsp; return 0;
}
</blockquote>
</pre>
</div>
<p>For an explanation of the code and the C++ API of Alchemy, I&#8217;ll refer you to my previous article on <a href="http://blog.tartiflop.com/2008/12/passingreturning-objects-tofrom-c-using-alchemy/">passing and returning objects to and from C++ using Alchemy</a>.</p>
<p>The first test, <em>speedTest1</em>, performs 1,000,000 times the cross product of two vectors (initially passed by AS3) followed by a normalisation. The resulting vector is used in the following iteration. At the end of all the iterations, the final vector is returned to AS3.</p>
<p>The second test, <em>speedTest2</em>, calculates rotation vectors around the x, y and z axes. A vector (initially passed by AS3), is then rotated by each matrix individually. This is repeated for 180 steps, increasing the angle of rotation by 1 degree at a time. This again is repeated for a total of 1,000 iterations. The final vector is returned to AS3.</p>
<p>Let&#8217;s have a look now at the ActionScript class that calls these tests, and the equivalent pure AS3 tests.</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {

&nbsp; import cmodule.vector.CLibInit;
&nbsp;
&nbsp; import flash.display.Sprite;
&nbsp; import flash.display.StageAlign;
&nbsp; import flash.display.StageScaleMode;
&nbsp; import flash.geom.Matrix3D;
&nbsp; import flash.geom.Vector3D;
&nbsp; import flash.text.TextField;
&nbsp; import flash.text.TextFieldAutoSize;
&nbsp; import flash.utils.getTimer;

&nbsp; public class AlchemySpeedTest extends Sprite {

&nbsp; &nbsp; private var vectorUtils:Object;

&nbsp; &nbsp; public function AlchemySpeedTest() {

&nbsp; &nbsp; &nbsp; // Set up the stage
&nbsp; &nbsp; &nbsp; stage.align = StageAlign.TOP_LEFT;
&nbsp; &nbsp; &nbsp; stage.scaleMode = StageScaleMode.NO_SCALE;

&nbsp; &nbsp; &nbsp; // Create the Alchemy bridge to C++ methods
&nbsp; &nbsp; &nbsp; var loader:CLibInit = new CLibInit;
&nbsp; &nbsp; &nbsp; vectorUtils = loader.init();

&nbsp; &nbsp; &nbsp; // Create a text field&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var timerText:TextField = new TextField();
&nbsp; &nbsp; &nbsp; timerText.autoSize = TextFieldAutoSize.LEFT;
&nbsp; &nbsp; &nbsp; addChild(timerText);
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Initialise a timer
&nbsp; &nbsp; &nbsp; var time0:int = getTimer()

&nbsp; &nbsp; &nbsp; // Perform the speed test
&nbsp; &nbsp; &nbsp; var vector:Vector3D = speedTest1();
&nbsp; &nbsp; &nbsp; //var vector:Vector3D = speedTest2();
&nbsp; &nbsp; &nbsp; //var vector:Vector3D = nativeSpeedTest1();
&nbsp; &nbsp; &nbsp; //var vector:Vector3D = nativeSpeedTest2();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Calculate the elapsed time
&nbsp; &nbsp; &nbsp; var time1:int = getTimer()
&nbsp; &nbsp; &nbsp; var totalTime:int = time1 - time0;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Display elapsed time and final vector
&nbsp; &nbsp; &nbsp; timerText.text = "Time taken = " + totalTime + " vector = (" + vector.x + ", " + vector.y + ", " + vector.z + ")";
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Speed test using C++ to iteratively calculate the cross products of two vectors
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function speedTest1():Vector3D {
&nbsp; &nbsp; &nbsp; var vector1:Vector3D = new Vector3D(0.123, 0.456, 0.789);
&nbsp; &nbsp; &nbsp; var vector2:Vector3D = new Vector3D(0.987, 0.654, 0.321);

&nbsp; &nbsp; &nbsp; return vectorUtils.speedTest1(vector1, vector2);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Speed test using C++ to iteratively calculate rotation matrices and apply these to a vector
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function speedTest2():Vector3D {
&nbsp; &nbsp; &nbsp; var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; return vectorUtils.speedTest2(vector);&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Speed test using AS3 to iteratively calculate the cross products of two vectors
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function nativeSpeedTest1():Vector3D {
&nbsp; &nbsp; &nbsp; var vector1:Vector3D = new Vector3D(0.123, 0.456, 0.789);
&nbsp; &nbsp; &nbsp; var vector2:Vector3D = new Vector3D(0.987, 0.654, 0.321);
&nbsp; &nbsp; &nbsp; var vector3:Vector3D;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; var time0:int = getTimer()
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; for (var i:int = 0; i &lt; 1000000; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; vector3 = vector1.crossProduct(vector2);
&nbsp; &nbsp; &nbsp; &nbsp; vector3.normalize();
&nbsp; &nbsp; &nbsp; &nbsp; vector1 = vector2;
&nbsp; &nbsp; &nbsp; &nbsp; vector2 = vector3;
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; return vector3;
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Speed test using AS3 to iteratively calculate rotation matrices and apply these to a vector
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function nativeSpeedTest2():Vector3D {
&nbsp; &nbsp; &nbsp; var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);

&nbsp; &nbsp; &nbsp; var copy:Vector3D = vector.clone();

&nbsp; &nbsp; &nbsp; var rotationX:Matrix3D = new Matrix3D();
&nbsp; &nbsp; &nbsp; var rotationY:Matrix3D = new Matrix3D();
&nbsp; &nbsp; &nbsp; var rotationZ:Matrix3D = new Matrix3D();

&nbsp; &nbsp; &nbsp; for (var i:int = 0; i &lt; 1000; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; vector = copy.clone();
&nbsp; &nbsp; &nbsp; &nbsp; for (var ang:Number = 0; ang &lt; 180; ang++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationX.identity();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationX.appendRotation(ang, Vector3D.X_AXIS);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationY.identity();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationY.appendRotation(ang, Vector3D.Y_AXIS);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationZ.identity();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rotationZ.appendRotation(ang, Vector3D.Z_AXIS);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vector = rotationX.transformVector(vector);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vector = rotationY.transformVector(vector);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vector = rotationZ.transformVector(vector);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; return vector;
&nbsp; &nbsp; }

&nbsp; }
}
</blockquote>
</pre>
</div>
<p>Without going into too many details of the code, you&#8217;ll see that in the constructor we can choose one of four tests: speedTest1 and speedTest2, as discussed above, and nativeSpeedTest1 and nativeSpeedTest2 which perform the same calculations but using pure ActionScript classes. The time taken to perform the calculations is then displayed along with the final vector so that we can be sure that the results are the same in a <em>TextField</em>.</p>
<p>To make reasonable comparisons I&#8217;ve tried to make the object creation in both ActionScript and C++ relatively equal: creating objects takes time so can obfuscate the obtained timing results. If you find any glaring differences between the C++ and ActionScript versions then please let me know and I&#8217;ll modify this post.</p>
<p>If you&#8217;d like to take a look at the whole project (set up using automake and ant as shown in my previous article on <a href="http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/">setting up a development environment for Alchemy in Flex Builder 3</a>) then you can find all the files <a href="http://www.tartiflop.com/alchemy/AlchemySpeedTest/srcview/">here</a>.</p>
<p><strong>Results</strong><br />
The native and Alchemy speed tests were compared initially to ensure that they both produce the same results. One surprising result was that for the matrix rotation test, the resulting vector diverged progressively as the number of iterations increased. This is presumably because of rounding errors being different between C++ (which uses double floating point values) and ActionScript. To limit this, you&#8217;ll notice that the vector is reset before the inner iteration over the 180 angles.</p>
<p>More important are the timing results&#8230; and the winner is&#8230; !</p>
<p>For speedTest1 (vector cross product and normalisation) I obtained the following:<br />
Alchemy :&nbsp;1309ms (averaged from 4 runs: 1346, 1285, 1284, 1322)<br />
Native :&nbsp;1192ms (averaged from 4 runs: 1232, 1147, 1176, 12123)</p>
<p>For speedTest2 (rotation matrix creation and vector transformation) the following times were obtained:<br />
Alchemy :&nbsp;814ms (averaged from 4 runs: 803, 826, 814, 813)<br />
Native :&nbsp;792ms (averaged from 4 runs: 774, 787, 789, 816)</p>
<p><strong>Conclusion</strong><br />
As you can see, even with computationally intensive calculations, native ActionScript beats Alchemy compiled C++. Shame - I was expecting huge improvements! And don&#8217;t forget that calling Alchemy code is <em>very</em> expensive - these tests have minimised this cost.</p>
<p>But is it really surprising? After all, we&#8217;re not executing natively compiled C++ code: we&#8217;re executing C++ bytecode compiled for the ActionScript virtual machine. Plus the native ActionScript functions have already been optimised.</p>
<p>Going to some extent to explain this, this article at <a href="http://www.automatastudios.com/2008/11/21/understanding-adobe-alchemy/">Automata Studios on Understanding Adobe Alchemy</a> (who used Alchemy to port OggVorbis to ActionScript 3) provides very interesting reading. As they say in the article:</p>
<p><em>&#8220;&#8230; Knowing that Alchemy is just spitting out the same AVM2 bytecode that Flash and Flex spit out it is pretty confusing how Alchemy code could be faster than standard ActionScript. In fact, it is not faster across the board - just in specific types of operations and when the length of a task can be used to overcome Alchemy’s intrinsic overhead&#8230;.</em></p>
<p>And also:</p>
<p><em>&#8220;&#8230; Now, what are these operations that Alchemy does so well? Memory access and function calls. Alchemy compiled code utilizes new bytecodes added to FP10 for working with ByteArrays - which as you’ll remember are what make up the “RAM” in Alchemy. &#8230;&#8221;</em></p>
<p>So the result seems somewhat less attractive than that claimed by Adobe (<em>&#8220;&#8230; Ideally suited for computation-intensive use cases (&#8230;) performance can be considerably faster than ActionScript 3.0 &#8230;&#8221;</em>) and much more specific to the type of operations being performed.</p>
<p>The tests shown here are of course very limited in their scope: the idea is to provoke some discussion about where Alchemy can be beneficial rather than just stating that Alchemy will produce pure gold in all situations.</p>
<p>One area which may be of interest is that of <a href="http://en.wikipedia.org/wiki/Green_threads"><em>green threads</em></a> as stated in the above article. However these threads are platform independent and are executed in the virtual machine rather on the native OS. This limitation means that the benefits of multi-core processors cannot be tapped into&#8230; so can they really produce reasonable results when calculations are performed in parallel?</p>
<p>Anyway, I hope this has been of interest and of some use - as always comments, suggestions and questions are welcome!</p>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=MiO8eJ"><img src="http://feeds.feedburner.com/~a/tartiflop?i=MiO8eJ" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/483882389" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/an-alchemy-speed-test/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/an-alchemy-speed-test/</feedburner:origLink></item>
		<item>
		<title>Passing/Returning objects to/from C++ using Alchemy</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/483800293/</link>
		<comments>http://blog.tartiflop.com/2008/12/passingreturning-objects-tofrom-c-using-alchemy/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 17:06:26 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Alchemy]]></category>

		<category><![CDATA[Flex]]></category>

		<category><![CDATA[Actionscript 3]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=280</guid>
		<description><![CDATA[Following from my last article on setting up an Alchemy development environment in Flex Builder 3, I wanted to illustrate briefly how objects can be passed to C++ methods using Alchemy (and similarly how the ActionScript properties can be extracted) and how we can return objects to the calling AS3 code.
This article has been prompted [...]]]></description>
			<content:encoded><![CDATA[<p>Following from my last article on <a href="http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/">setting up an Alchemy development environment in Flex Builder 3</a>, I wanted to illustrate briefly how objects can be passed to C++ methods using Alchemy (and similarly how the ActionScript properties can be extracted) and how we can return objects to the calling AS3 code.</p>
<p>This article has been prompted since I found very little useful documentation on the web, so hopefully this&#8217;ll be useful for other beginners! Other than the <a href="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Developing_with_Alchemy:C_API">C++ API for Alchemy provided by Adobe</a> (which is essential), I&#8217;ve found the following links very useful in providing concrete examples of getting started in Alchemy:</p>
<ul>
<li><a href="http://manfred.dschini.org/2008/11/20/creating-a-button-with-alchemy/">Creating a button with Alchemy</a> by Manfred Webber</li>
<li><a href="http://labs.adobe.com/wiki/index.php/Alchemy:Libraries">The OggVorbis library sources</a> to be compiled with AS3</li>
</ul>
<p>These provided the essential lines of code necessary to understand the API, and be able to pass objects to C/C++ and similarly return objects to ActionScript.</p>
<p>The following is some C code to illustrate this. Essentially a <em>flash.geom.Vector3D</em> object is passed to a function, <em>foo</em>, which then returns a copy of the object to the calling function.</p>
<div id="codeSnippet">
<pre>
<blockquote>

#include "AS3.h"

AS3_Val foo(void* self, AS3_Val args) {

&nbsp; // declare local variables
&nbsp; double x;
&nbsp; double y;
&nbsp; double z;
&nbsp;
&nbsp; // ******* Passing objects as parameters to C++ *******
&nbsp;
&nbsp; // Declare AS3 variable
&nbsp; AS3_Val as3Vector;
&nbsp;
&nbsp; // Extract variables from arguments array (in this case a flash.geom.Vector3D)
&nbsp; AS3_ArrayValue(args, "AS3ValType", &#038;as3Vector);

&nbsp; // Extract properties from object and store in local variables
&nbsp; AS3_ObjectValue(as3Vector, "x:DoubleType, y:DoubleType, z:DoubleType", &#038;x, &#038;y, &#038;z);

&nbsp; // ******* Returning objects to AS3 *******

&nbsp; // Obtain a class descriptor for the AS3 Vector3D class
&nbsp; AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));
&nbsp; AS3_Val params = AS3_Array("");
&nbsp;
&nbsp; // Construct a new AS3 Vector3D object with empty parameters
&nbsp; AS3_Val result = AS3_New(vector3DClass, params);
&nbsp;
&nbsp; // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
&nbsp; AS3_Set(result, AS3_String("x"), AS3_Number(x));
&nbsp; AS3_Set(result, AS3_String("y"), AS3_Number(y));
&nbsp; AS3_Set(result, AS3_String("z"), AS3_Number(z));

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(params);
&nbsp; AS3_Release(vector3DClass);
&nbsp;
&nbsp; // return the AS3 flash.geom.Vector3D object
&nbsp; return result;
}

/**
 * Main entry point for Alchemy compiler. Declares all functions available
 * through the Alchemy bridge.
 */
int main() {
&nbsp; // Declare all methods exposed to AS3 typed as Function instances
&nbsp; AS3_Val fooMethod = AS3_Function(NULL, foo);

&nbsp; // Construct an object that contains references to all the functions
&nbsp; AS3_Val result = AS3_Object("foo:AS3ValType", fooMethod);

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(fooMethod);

&nbsp; // Notify the bridge of what has been created -- THIS DOES NOT RETURN!
&nbsp; AS3_LibInit(result);

&nbsp; // Should never get here!
&nbsp; return 0;
}
</blockquote>
</pre>
</div>
<p>Let&#8217;s take a look at this bit by bit. Starting with the function <em>foo</em>, all functions visible to ActionScript have to be declared in the same way:</p>
<div id="codeSnippet">
<pre>
<blockquote>

AS3_Val foo(void* self, AS3_Val args) {
</blockquote>
</pre>
</div>
<p>The return value is always an <em>AS3_Val</em> type and a function always receives a pointer along with an <em>AS3_Val</em> type argument. <em>AS3_Val</em> can be thought of as the <em>Object</em> type in AS3 - all concrete types inherit from this so utilities are needed to extract useful information from them.</p>
<p>First of all: extracting objects from the <em>args</em> parameter. Whatever, and however many, arguments are passed we always use the method <em>AS3_ArrayValue</em> to extract the real arguments: arguments are always passed in the form of an <em>Array</em>. In the example we do the following:</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // Declare AS3 variable
&nbsp; AS3_Val as3Vector;
&nbsp;
&nbsp; // Extract variables from arguments array (in this case a flash.geom.Vector3D)
&nbsp; AS3_ArrayValue(args, "AS3ValType", &#038;as3Vector);
</blockquote>
</pre>
</div>
<p>Here we declare an <em>AS3_Val</em> variable (implying that we are expecting an AS3 <em>Object</em>). If the types passed are primitive types (or String types) we could do the following:</p>
<div id="codeSnippet">
<pre>
<blockquote>

int arg0 = 0;
char* arg1 = NULL;
double arg2 = 0.0;
AS3_ArrayValue(arr, "IntType, StrType, DoubleType", &#038;arg0, &#038;arg1, &#038;arg2);
</blockquote>
</pre>
</div>
<p>Here the primitive AS3 types are converted to native C++ primitives.</p>
<p>Coming back to our example, we now have an AS3 <em>Object</em> (stored as an <em>AS3_Val</em> type) and we&#8217;d like to extract data from it. This is done by making an <em>AS3_ObjectValue</em> call.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // Extract properties from object and store in local variables
&nbsp; AS3_ObjectValue(as3Vector, "x:DoubleType, y:DoubleType, z:DoubleType", &#038;x, &#038;y, &#038;z);
</blockquote>
</pre>
</div>
<p>Here we pass the extracted <em>Object</em>, declare the identifiers and types of properties and a list of local variables to store the value in. For the <em>Vector3D</em> we want to obtain the x, y and z properties which are converted to local <em>DoubleType</em> values.</p>
<p>To return an ActionScript object, we need to perform a <em>lookup</em> for the class name with the relevant namespace. From this we can get a class descriptor which can then be instantiated. </p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // Obtain a class descriptor for the AS3 Vector3D class
&nbsp; AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));
</blockquote>
</pre>
</div>
<p> Here, for example, we obtain an <em>AS3_Val</em> representing the class <em>flash.geom.Vector3D</em>, using the <em>AS3_NSGet</em> function. We can then create an object from this.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; AS3_Val params = AS3_Array("");
&nbsp;
&nbsp; // Construct a new AS3 Vector3D object with empty parameters
&nbsp; AS3_Val result = AS3_New(vector3DClass, params);
</blockquote>
</pre>
</div>
<p>This object is create with no parameters using the <em>AS3_New</em> function. Again, the result is stored in the base <em>AS3_Val</em> type. Having created a new object we can then modify its properties.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
&nbsp; AS3_Set(result, AS3_String("x"), AS3_Number(x));
&nbsp; AS3_Set(result, AS3_String("y"), AS3_Number(y));
&nbsp; AS3_Set(result, AS3_String("z"), AS3_Number(z));
</blockquote>
</pre>
</div>
<p>The <em>AS3_Set</em> function takes an AS3 <em>Object</em>, a property identifier and a value, correctly cast to the required AS3 value type, in this case an <em>AS3_Number</em> type. Note that I&#8217;ve tried to pass these values in the <em>params</em> Array but have never been successful - if anyone has any tips on doing this I&#8217;d be happy to hear from you.</p>
<p>So, now we have an AS3 object created and its properties set. Now we need to do a bit of memory management before returning the object. This is done using the <em>AS3_Release</em> function.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(params);
&nbsp; AS3_Release(vector3DClass);
</blockquote>
</pre>
</div>
<p>Finally,  we return the created object:</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; // return the AS3 flash.geom.Vector3D object
&nbsp; return result;
</blockquote>
</pre>
</div>
<p>To export the function <em>foo</em> to ActionScript, the following <em>main</em> function is called - the Alchemy compiler always looks for this function, so its here that we always declare our interface. For the above example the following is necessary:</p>
<div id="codeSnippet">
<pre>
<blockquote>

int main() {
&nbsp; // Declare all methods exposed to AS3 typed as Function instances
&nbsp; AS3_Val fooMethod = AS3_Function(NULL, foo);

&nbsp; // Construct an object that contains references to all the functions
&nbsp; AS3_Val result = AS3_Object("foo:AS3ValType", fooMethod);

&nbsp; // Release what's no longer needed
&nbsp; AS3_Release(fooMethod);

&nbsp; // Notify the bridge of what has been created -- THIS DOES NOT RETURN!
&nbsp; AS3_LibInit(result);

&nbsp; // Should never get here!
&nbsp; return 0;
}
</blockquote>
</pre>
</div>
<p>Without going into too many details, essentially we create <em>Function</em> objects containing the native C/C++ methods. These are then given an AS3 interface using the <em>AS3_Object</em> function and then passed to the <em>AS3_LibInit</em> function which provides the entry point from ActionScript.</p>
<p>The following ActionScript class shows how we call the Alchemy compiled functions, passing a Vector3D object and obtaining another one in return.</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {

&nbsp; import cmodule.vector.CLibInit;
&nbsp;
&nbsp; import flash.display.Sprite;
&nbsp; import flash.geom.Vector3D;

&nbsp; public class Test extends Sprite {

&nbsp; &nbsp; public function Test() {

&nbsp; &nbsp; &nbsp; // Create the Alchemy bridge to C++ methods
&nbsp; &nbsp; &nbsp; var loader:CLibInit = new CLibInit;
&nbsp; &nbsp; &nbsp; var alchemyTest:Object = loader.init();

&nbsp; &nbsp; &nbsp; var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);
&nbsp; &nbsp; &nbsp; var returnVector:Vector3D = alchemyTest.foo(vector);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; trace("Return vector = (" + returnVector.x + ", " + returnVector.y + ", " + returnVector.z + ")");
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; }

&nbsp; }
}
</blockquote>
</pre>
</div>
<p>Our connection to the C/C++ code is performed by creating a new <em>CLibInit</em> function and calling <em>init</em>.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; &nbsp; // Create the Alchemy bridge to C++ methods
&nbsp; &nbsp; &nbsp; var loader:CLibInit = new CLibInit;
&nbsp; &nbsp; &nbsp; var alchemyTest:Object = loader.init();
</blockquote>
</pre>
</div>
<p>This creates a basic ActionScript <em>Object</em>. The functions cannot be seen at the time of compilation (at least not in Flex Builder) but are obtained at run time. The resulting function call on <em>foo</em> is shown as follows:</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; &nbsp; var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);
&nbsp; &nbsp; &nbsp; var returnVector:Vector3D = alchemyTest.foo(vector);
</blockquote>
</pre>
</div>
<p>As required, we pass a Vector3D and get a new Vector3D object in return.</p>
<p>And that&#8217;s it! If you&#8217;d like to have a look at the source and the project set-up together (using the same automake and ant files as discussed in the previous article) then you can find them <a href="http://www.tartiflop.com/alchemy/AlchemyTest/srcview/">here</a>.</p>
<p>This is a very quick introduction to passing and returning objects using Alchemy. The API provides many more possibilities but this articles aims to illustrate some of the basic, but essential, functions available - for other beginners I hope this is useful! In my next article I&#8217;ll look at some of the capabilities of Alchemy.</p>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=MpCsYl"><img src="http://feeds.feedburner.com/~a/tartiflop?i=MpCsYl" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/483800293" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/passingreturning-objects-tofrom-c-using-alchemy/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/passingreturning-objects-tofrom-c-using-alchemy/</feedburner:origLink></item>
		<item>
		<title>Setting up an Alchemy development environment in Flex Builder 3</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/482891538/</link>
		<comments>http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 17:53:38 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Alchemy]]></category>

		<category><![CDATA[Flex]]></category>

		<category><![CDATA[Actionscript 3]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=271</guid>
		<description><![CDATA[I&#8217;ve recently been reading a lot of articles recently about the new Alchemy research project at Adobe Labs. At first glances this looks very exciting: compile optimised C/C++ code to be executed within an ActionScript 3 Flash movie. As quoting from the Alchemy home page :
&#8220;&#8230; Ideally suited for computation-intensive use cases, such as audio/video [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently been reading a lot of articles recently about the new <a href="http://labs.adobe.com/technologies/alchemy/">Alchemy research project at Adobe Labs</a>. At first glances this looks very exciting: compile optimised C/C++ code to be executed within an ActionScript 3 Flash movie. As quoting from the Alchemy home page :</p>
<p><em>&#8220;&#8230; Ideally suited for computation-intensive use cases, such as audio/video transcoding, data manipulation, XML parsing, cryptographic functions or physics simulation, performance can be considerably faster than ActionScript 3.0 and anywhere from 2-10x slower than native C/C++ code.&#8221;</em></p>
<p>Sounds good! Ideal even when thinking about 3D graphics that are heavily dependent on vector and matrix calculations&#8230;</p>
<p>So, I decided to take a look and see how easy it was to integrate C++ code into an ActionScript project and what the benefits were. I discovered fairly quickly that documention on the web is fairly limited which has prompted this blog entry. Also, I&#8217;m a fan of the eclipse development environment and like all my work to be concentrated within the same window so wanted the Alchemy development to be done in parallel to the ActionScript development. </p>
<p>First off, it should be noted that this article is aimed mainly at Mac users: Hopefully there are things along the way that will be useful for Windows but I&#8217;m going to be looking at an <em>automake</em> utility to compile the Alchemy code which may not work on Windows (maybe under Cygwin, but I&#8217;ve not tried it).</p>
<p>Here are a few links to get started anyway:</p>
<ul>
<li><a href ="http://labs.adobe.com/technologies/alchemy/">The main Alchemy home page at Adobe Labs</a></li>
<li><a href ="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Getting_Started">Essential Adobe guide for installing and getting started with Alchemy</a></li>
<li><a href ="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Developing_with_Alchemy:C_API">The C/C++ API</a></li>
<li><a href ="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Developing_with_Alchemy:AS3_API">The ActionScript 3 API</a></li>
<li><a href ="http://labs.adobe.com/wiki/index.php/Alchemy:Libraries">A couple of libraries built with Alchemy</a></li>
</ul>
<p>First of all <a href="http://labs.adobe.com/downloads/alchemy.html">download Alchemy Toolkit</a> from Adobe. Personally I copied the extracted directory to the Applications folder in my home directory. Then follow the instruction given on the <a href="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Getting_Started"><em>Getting Started</em></a> page.</p>
<p><strong>NOTE !!</strong> I had huge trouble to start with because I was running a shell with <em>tcsh</em>: you must have <em>bash</em> (which is anyway the default for Mac) running for this to work! </p>
<p>Once I managed to compile the C code using the Alchemy tools, I went straight to Flex Builder 3 to try to integrate the ActionScript and compiled <em>swf</em> together.</p>
<p>In Flex Builder (or the Eclipse plugin as I&#8217;m using), create a new ActionScript project called EchoTest, as is the case for the ActionScript class shown on the Alchemy <em>Getting Started</em> page. Now we need to ensure that the project is compiled for Flash Player 10 (this is easily possible if you have Flex Builder 3.0.2 installed). Go to the project <em>Properties</em> menu item, select <em>ActionScript Compiler</em> and for <em>Require Flash Player version</em> enter 10.0.0 and click <em>OK</em>.</p>
<p>In the newly created <em>EchoTest</em> class, copy the code from the <em>GettingStarted</em> page that is linked to compiled C.</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {
&nbsp; import flash.display.Sprite;
&nbsp; import cmodule.stringecho.CLibInit;
&nbsp;
&nbsp; public class EchoTest extends Sprite {
&nbsp;
&nbsp; &nbsp; public function EchoTest() {
&nbsp;
&nbsp; &nbsp; &nbsp; var loader:CLibInit = new CLibInit;
&nbsp; &nbsp; &nbsp; var lib:Object = loader.init();
&nbsp; &nbsp; &nbsp; trace(lib.echo("foo"));
&nbsp; &nbsp; }
&nbsp; }
}
</blockquote>
</pre>
</div>
<p>This won&#8217;t compile because we need to link to the Alchemy-compiled C code. Create a <em>bin</em> directory at the root of the project and copy <em>stringecho.swf</em>, compiled before, here. Go into the project <em>Properties</em> menu item, select <em>ActionScript Build Path</em>, go to the <em>Library Path</em> tab and select <em>Add SWC&#8230;</em>. Browse to the <em>bin</em> directory and select <em>stringecho.swf</em>. Now when you click on <em>OK</em> you should find that the project compiles.</p>
<p>To see anything happen you need to run in debug mode&#8230; even then its not very exciting: you&#8217;ll just see <em>foo</em> written in the Console.</p>
<p>The main objective (for me at least) is to set up an environment where we can compile the C/C++ code with Alchemy within the Flex environment. For this I&#8217;m going to be combining some <em>automake</em> files with shell scripts and link them to Eclipse with an <em>ant</em> build file.</p>
<p>Lets first of all copy the alchemy source in the project. Create a directory called <em>alchemy</em> at the root of the project. Into this, copy the C file from the Alchemy samples (ALCHEMY_HOME/samples/stringecho/stringecho.c).</p>
<p>For automake we need a <em>configure.in</em> file and (depending on the project) several <em>Makefile.am</em> files.</p>
<p>At the project root, copy the following into <em>configure.in</em> :</p>
<div id="codeSnippet">
<pre>
<blockquote>

dnl Process this file with autoconf to produce a configure script.
AC_INIT(alchemyTest, 0.0.1)
AC_CONFIG_AUX_DIR(config)
AC_CONFIG_SRCDIR(alchemy/stringecho.c)

AM_INIT_AUTOMAKE

dnl Compiler
AC_PROG_CXX
AC_PROG_CC
AC_LANG(C++)
AC_LANG(C)

CFLAGS=""
AM_CFLAGS="-Wall -O3 -swc "
AC_SUBST(AM_CFLAGS)
CXXFLAGS=""
AM_CXXFLAGS="-Wall -O3 -swc "
AC_SUBST(AM_CXXFLAGS)

dnl Initialise top_srcdir
top_srcdir=.

AC_OUTPUT([
Makefile
alchemy/Makefile
])
</blockquote>
</pre>
</div>
<p>Create a <em>Makefile.am</em> file at the project root as well containing the following line (indicating simply which is the main alchemy source directory):</p>
<div id="codeSnippet">
<pre>
<blockquote>

SUBDIRS = alchemy
</blockquote>
</pre>
</div>
<p>Finally in the <em>alchemy</em> directory, copy the following also into a <em>Makefile.am</em> file :</p>
<div id="codeSnippet">
<pre>
<blockquote>

INCLUDES = -I$(top_srcdir)/alchemy

bin_PROGRAMS = stringecho.swc

stringecho_swc_SOURCES = \
&nbsp; stringecho.c
</blockquote>
</pre>
</div>
<p>That&#8217;s all that&#8217;s needed for the automake part. Now we need a couple of shell scripts to be called from <em>ant</em> and that take into account the environment variables necessary for the Alchemy tools.</p>
<p>At the root, create a file called init and copy the following:</p>
<div id="codeSnippet">
<pre>
<blockquote>

#!/bin/sh

if [ ! -d config ]
then
&nbsp; mkdir config;
fi

if [ ! -x AUTHORS ]
then
&nbsp; touch AUTHORS;
&nbsp; touch README;
&nbsp; touch NEWS;
&nbsp; touch ChangeLog;
fi

aclocal -I config
autoconf
automake --gnu --add-missing

if [ ! -d obj ]
then
&nbsp; mkdir obj;
fi

path=`pwd`

cd obj
../configure --prefix=`pwd`/..

cd $path
</blockquote>
</pre>
</div>
<p>Once it is created, make sure it is executable by changing the file permissions (<em>chmod +x init</em>). This will essentially create any necessary directories and files (for automake) and launch the configuration process. This will then create all the necessary Makefiles.</p>
<p>In the same directory (the root) create a file called build (that should also be executable) and copy this:</p>
<div id="codeSnippet">
<pre>
<blockquote>

#!/bin/bash

ALCHEMY_HOME=$HOME/Applications/Alchemy
source $ALCHEMY_HOME/alchemy-setup

PATH=$ALCHEMY_HOME/achacks:$PATH
export PATH

cd obj
make -e install
</blockquote>
</pre>
</div>
<p>Here we specify the home of Alchemy (so, obviously, change the directories accordingly) and we then launch the compilation, passing the environment variables at the same time (with the <em>-e</em> flag).</p>
<p>Finally, we come to the <em>ant</em> build file. The following should be copied into <em>build.xml</em> at the project root.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&lt;project name="EchoTest" basedir="."&gt;

&nbsp; &lt;property name="builddir" value="."/&gt;

&nbsp; &lt;target name="compile alchemy"&gt;
&nbsp; &nbsp; &lt;exec executable="${builddir}/build"/&gt;
&nbsp; &lt;/target&gt;

&nbsp; &lt;target name="init alchemy"&gt;
&nbsp; &nbsp; &lt;exec executable="${builddir}/init"&gt;
&nbsp; &nbsp; &lt;/exec&gt;
&nbsp; &lt;/target&gt;

&nbsp; &lt;target name="remove obj"&gt;
&nbsp; &nbsp; &lt;exec executable="rm"&gt;
&nbsp; &nbsp; &nbsp; &lt;arg line="-fr"/&gt;
&nbsp; &nbsp; &nbsp; &lt;arg line="obj"/&gt;
&nbsp; &nbsp; &lt;/exec&gt;
&nbsp; &lt;/target&gt;

&nbsp;
&lt;/project&gt;
</blockquote>
</pre>
</div>
<p>This includes a couple of targets to initialise the compilation (create the Makefiles) and compile the C source. A final target just removes the compiles object files.</p>
<p>in the end you should have a project structure that looks something like this:</p>
<p><a href="http://blog.tartiflop.com/wp-content/uploads/2008/12/alchemy-devel.png"><img src="http://blog.tartiflop.com/wp-content/uploads/2008/12/alchemy-devel.png" alt="" title="" width="331" height="646" class="aligncenter size-full wp-image-272" /></a></p>
<p>So let&#8217;s test this. Open the <em>Ant</em> view in eclipse by selection the menu <em>Window</em>, <em>Show View</em>, <em>Other&#8230;</em> and then searching for the <em>Ant</em> view. Drag and drop <em>build.xml</em> into this view and you should find the targets <em>compile alchemy</em> and <em>init alchemy</em> available.</p>
<p><strong>NOTE !!!</strong> For this to work you need to lauch eclipse from the command line using the command <em>tcsh -e ./eclipse</em> from the installed eclipse directory. I don&#8217;t know why, but ant behaves differently - I presume because environment variables are passed to it! If you could tell me why, or how to avoid this, I&#8217;d be very happy to hear from you!</p>
<p>So, back again, assuming that eclipse is launched as <em>tcsh -c ./eclipse</em>, double click on the <em>init alchemy</em> target. You should hopefully see the configuration process in the console terminated with <em>BUILD SUCCESS</em>! This means that the Makefiles are ready. You can now double click on <em>build alchemy</em> to compile the C code. If it works you should see output that resembles that shown of the compilation on the <a href="http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Getting_Started"><em>Getting Started</em></a> page.</p>
<p>The <em>init</em> process is typically necessary only once. From now on when modifying the C code just launch the <em>build</em> target.</p>
<p>This has now compiled <em>stringecho.swc</em> in the <em>bin</em> directory. Refresh the workspace (fn_key + F5 on a Mac) if it doesn&#8217;t refresh automatically which then recompiles the ActionScript if a change has occurred. In this case, I&#8217;m simply showing a build process so there&#8217;s no difference to the result. However, in future projects, you&#8217;ll find that every time you recompile using the Alchemy tools you&#8217;ll need to refresh the workspace otherwise the ActionScript project isn&#8217;t updated.</p>
<p>Also to note, when editing the C or C++ files, I&#8217;d recommend using the <a href="http://www.eclipse.org/cdt/">CDT plugin for eclipse</a> rather than an external editor - again it avoids switching application for compiling the same project!</p>
<p>Anyway, hope this is of some use to people getting started with Alchemy (like me!). If you find any problems then please let me know&#8230; same if you have any comments. Next I&#8217;ll be looking at testing some of what Alchemy is supposed to be capable of!</p>

<p><a href="http://feeds.feedburner.com/~a/tartiflop?a=l5C0S8"><img src="http://feeds.feedburner.com/~a/tartiflop?i=l5C0S8" border="0"></img></a></p><img src="http://feeds.feedburner.com/~r/tartiflop/~4/482891538" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/feed/</wfw:commentRss>
		<feedburner:origLink>http://blog.tartiflop.com/2008/12/setting-up-an-alchemy-development-environment-in-flex-builder-3/</feedburner:origLink></item>
		<item>
		<title>First steps in Away3D : Part 6 - Normal mapping and environment mapping</title>
		<link>http://feeds.feedburner.com/~r/tartiflop/~3/477699769/</link>
		<comments>http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-6-normal-mapping-and-environment-mapping/#comments</comments>
		<pubDate>Sun, 07 Dec 2008 18:32:55 +0000</pubDate>
		<dc:creator>tartiflop</dc:creator>
		
		<category><![CDATA[Away3D]]></category>

		<category><![CDATA[Actionscript 3]]></category>

		<category><![CDATA[bump mapping]]></category>

		<category><![CDATA[environment mapping]]></category>

		<category><![CDATA[lighting]]></category>

		<category><![CDATA[normal mapping]]></category>

		<category><![CDATA[texture mapping]]></category>

		<guid isPermaLink="false">http://blog.tartiflop.com/?p=257</guid>
		<description><![CDATA[r]]></description>
			<content:encoded><![CDATA[<p>Back again after a break longer than predicted! In this tutorial I&#8217;ll show how we can add more stunning visual effects to objects by applying normal maps (to give the impression that an object is composed of many more triangles from the rendered shading) and environment maps (for objects to appear shiny and reflecting the surrounding environment). An equivalent tutorial for Papervision3D can be found at my post on <a href="http://blog.tartiflop.com/2008/08/first-steps-in-papervision3d-part-7-texture-mapping-with-lighting-bump-mapping-and-environment-mapping/">texture mapping with lighting, bump mapping and environment mapping</a>.</p>
<p>Previous articles summary :</p>
<ul>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-1-getting-started/">First steps in Away3D : Part 1 - Getting started</a><br />
Creation of a new Away3D project within eclipse or Flex Builder 3 and a simple example of a 3D scene illustrating some basic Away3D classes.</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-2-animation/">First steps in Away3D : Part 2 - Animation</a><br />
Adding some basic animation to the scene by modifying 3D object parameters when entering a frame.</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-3-texture-mapping/">First steps in Away3D : Part 3 - Texture mapping</a><br />
Create materials using bitmap data with the aim of having more detailed or realistic objects.
</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-4-scene-interaction/">First steps in Away3D : Part 4 - Scene interaction</a><br />
Listen to mouse events to interact with the scene and with individual rendered objects.
</li>
<li><a href="http://blog.tartiflop.com/2008/11/first-steps-in-away-3d-part-5-lighting-and-shading/">First steps in Away3D : Part 5 - Lighting and shading</a><br />
A light source is added to the scene and the materials are changed to illustrate how Away3D renders objects with different shading characteristics.
</li>
</ul>
<p>Normal maps - or Dot3 bump maps - can be seen as something of an evolution from standard bump maps. Bump maps, as used in Papervision3D, contain a single value describing a displacement perpendicular to a surface. Normal maps, however, are more detailed because they contain three axis elements, effectively describing the normal to a specific point on a surface, replacing the triangle normal entirely. Whereas a bump map would be rendered the same for any point of view, a normal map is rendered differently depending on the viewer&#8217;s position: hence giving a much better impression of realism.</p>
<p>Normal maps are mapped in much the same way as a texture map, using the same <em>uv</em> values as for a texture. The bitmap data used in a normal map is split into the three components red, green and blue, each one giving a representation of the normal vector components in <em>x</em>, <em>y</em> and <em>z</em> respectively. So, normal values between -1 and 1 are discretised to an integer value between 0 and 255.</p>
<p>Normal maps greatly increase the performance of rendering a 3D object since the normals are given directly rather than requiring a calculation from triangles. So, a complex model requiring thousands of triangles can be reduced to, say, a few hundred (a low-poly model) with a normal map being derived from the original model. You can check out wikipedia for more information on <a href="http://en.wikipedia.org/wiki/Normal_mapping">normal mapping</a>. For an example of a low-poly model using a normal map generated from a more complex one, check out this <a href="http://away3d.com/away3d-normal-mapped-bust">demo of normal mapping</a> at Away3D.</p>
<p>Environment mapping, or reflection mapping, is a technique used to map a surrounding environment onto an object giving the impression that the object has a mirrored surface. The technique is much more efficient than ray tracing and, even though it is not exact, still produces realistic effects. Again, wikipedia contains some useful <a href="http://en.wikipedia.org/wiki/Environment_mapping">information on environment mapping</a>. </p>
<p>In this tutorial I&#8217;m going to show normal mapping on a cube and a sphere and environment mapping on a sphere. Following from the last post on <a href="http://blog.tartiflop.com/2008/11/first-steps-in-away-3d-part-5-lighting-and-shading/">Lighting and Shading</a>, we&#8217;re going to be using another couple of <em>CompositeMaterials</em>: namely the <em>Dot3BitmapMaterial</em> and the <em>EnviroBitmapMaterial</em>.</p>
<p>It should be noted that while normal mapping can produce a more realistic effect than bump mapping, it can be more complex to implement. Since a normal map replaces the normal to a surface we cannot use the same map on an object that has many faces (unless the normal map has been created specifically for example as shown in the Away3D demo above). This is a problem for example for Primitives. A <em>Cube</em> for example has six faces with six discrete <em>principal</em> normals - if we use the same normal map for each surface then each face will have the same normal data and hence be rendered identically, rather than each face being independent. Currently we therefore have to provide six independent normal maps.</p>
<p>My method for this example however is to create a cube using six individual planes using the same normal map and rotating and translating them: the normal map data is then rendered correctly for each face.</p>
<p>I also had a problem retrieving normal maps from the web for use in this tutorial. To help me I created a small utility that takes bump (displacement) map data and converts this into normal map data for either a plane or a sphere. You can try this out yourselves by checking out my article on <a href="http://blog.tartiflop.com/2008/12/displacement-map-to-normal-map-converter/">displacement map to normal map conversion</a>. With this utility you are able to specify the direction of the displacement (for a plane) or the direction against which the angle <em>phi</em> is calculated for a sphere.</p>
<p>So, lets get on with the code! Below you&#8217;ll see the next example in this series, producing three rotating texture-mapped objects each one rendered with either an environment map or normal map.</p>
<div id="codeSnippet">
<pre>
<blockquote>

package {
&nbsp; import away3d.cameras.HoverCamera3D;
&nbsp; import away3d.containers.ObjectContainer3D;
&nbsp; import away3d.containers.Scene3D;
&nbsp; import away3d.containers.View3D;
&nbsp; import away3d.core.render.Renderer;
&nbsp; import away3d.core.utils.Cast;
&nbsp; import away3d.lights.DirectionalLight3D;
&nbsp; import away3d.materials.Dot3BitmapMaterial;
&nbsp; import away3d.materials.EnviroBitmapMaterial;
&nbsp; import away3d.primitives.Plane;
&nbsp; import away3d.primitives.Sphere;
&nbsp;
&nbsp; import flash.display.Bitmap;
&nbsp; import flash.display.BitmapData;
&nbsp; import flash.display.Sprite;
&nbsp; import flash.display.StageAlign;
&nbsp; import flash.display.StageScaleMode;
&nbsp; import flash.events.Event;
&nbsp; import flash.events.MouseEvent;
&nbsp;
&nbsp; [SWF(backgroundColor="#000000")]
&nbsp;
&nbsp; public class Example006 extends Sprite {

&nbsp; &nbsp; [Embed(source="/../assets/away3D.png")] private var Away3DImage:Class;
&nbsp; &nbsp; private var away3DBitmap:Bitmap = new Away3DImage();

&nbsp; &nbsp; [Embed(source="/../assets/normalMap.png")] private var NormalImage:Class;
&nbsp; &nbsp; private var normalBitmap:Bitmap = new NormalImage();

&nbsp; &nbsp; [Embed(source="/../assets/asteroidNormal.png")] private var SphereNormalImage:Class;
&nbsp; &nbsp; private var sphereNormalBitmap:Bitmap = new SphereNormalImage();

&nbsp; &nbsp; [Embed(source="/../assets/checker.jpg")] private var CheckerImage:Class;
&nbsp; &nbsp; private var checkerBitmap:Bitmap = new CheckerImage();

&nbsp; &nbsp; [Embed(source="/../assets/envMap.png")] private var EnvironmentImage:Class;
&nbsp; &nbsp; private var envBitmap:Bitmap = new EnvironmentImage();

&nbsp; &nbsp; private static const ORBITAL_RADIUS:Number = 150;
&nbsp; &nbsp; private static const CAMERA_ORBIT:Number = 600;

&nbsp; &nbsp; private var scene:Scene3D;
&nbsp; &nbsp; private var camera:HoverCamera3D;
&nbsp; &nbsp; private var view:View3D;
&nbsp;
&nbsp; &nbsp; private var planeGroup:ObjectContainer3D;
&nbsp; &nbsp; private var normalSphere:Sphere;
&nbsp; &nbsp; private var envSphere:Sphere;
&nbsp; &nbsp; private var doRotation:Boolean = false;
&nbsp; &nbsp; private var lastMouseX:int;
&nbsp; &nbsp; private var lastMouseY:int;
&nbsp; &nbsp; private var lastPanAngle:Number = 60;
&nbsp; &nbsp; private var lastTiltAngle:Number = -60;

&nbsp; &nbsp;
&nbsp; &nbsp; public function Example006() {
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // set up the stage
&nbsp; &nbsp; &nbsp; stage.align = StageAlign.TOP_LEFT;
&nbsp; &nbsp; &nbsp; stage.scaleMode = StageScaleMode.NO_SCALE;

&nbsp; &nbsp; &nbsp; // Add resize event listener
&nbsp; &nbsp; &nbsp; stage.addEventListener(Event.RESIZE, onResize);
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Listen to mouse up and down events on the stage
&nbsp; &nbsp; &nbsp; stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
&nbsp; &nbsp; &nbsp; stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);

&nbsp; &nbsp; &nbsp; // Initialise Away3D
&nbsp; &nbsp; &nbsp; init3D();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create the 3D objects
&nbsp; &nbsp; &nbsp; createScene();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Initialise frame-enter loop
&nbsp; &nbsp; &nbsp; this.addEventListener(Event.ENTER_FRAME, loop);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Initialise all 3D components.
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function init3D():void {

&nbsp; &nbsp; &nbsp; // Create a new scene where all the 3D object will be rendered
&nbsp; &nbsp; &nbsp; scene = new Scene3D();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new camera, passing some initialisation parameters
&nbsp; &nbsp; &nbsp; camera = new HoverCamera3D({zoom:25, focus:30, distance:600});
&nbsp; &nbsp; &nbsp; camera.targetpanangle = camera.panangle = -10;
&nbsp; &nbsp; &nbsp; camera.targettiltangle = camera.tiltangle = 20;
&nbsp; &nbsp; &nbsp; camera.yfactor = 1;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new view that encapsulates the scene and the camera
&nbsp; &nbsp; &nbsp; view = new View3D({scene:scene, camera:camera});

&nbsp; &nbsp; &nbsp; // center the view to the middle of the stage
&nbsp; &nbsp; &nbsp; view.x = stage.stageWidth / 2;
&nbsp; &nbsp; &nbsp; view.y = stage.stageHeight / 2;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // ensure that the z-order is calculated correctly
&nbsp; &nbsp; &nbsp; view.renderer = Renderer.CORRECT_Z_ORDER;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; addChild(view);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Create the objects and lighting of the scene
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function createScene():void {

&nbsp; &nbsp; &nbsp; // Create 3 different materials: two normal mapped ones (planar and spherical) and an
&nbsp; &nbsp; &nbsp; // environment mapped one.
&nbsp; &nbsp; &nbsp; var normalMapMaterial:Dot3BitmapMaterial = new Dot3BitmapMaterial(Cast.bitmap(away3DBitmap), Cast.bitmap(normalBitmap), {smooth:true, precision:5});
&nbsp; &nbsp; &nbsp; var envMapMaterial:EnviroBitmapMaterial = new EnviroBitmapMaterial(Cast.bitmap(checkerBitmap), Cast.bitmap(envBitmap), {smooth:true, precision:5});
&nbsp; &nbsp; &nbsp; var sphereNormalMapMaterial:Dot3BitmapMaterial = new Dot3BitmapMaterial(new BitmapData(1, 1, false, 0x666666), Cast.bitmap(sphereNormalBitmap), {smooth:true, precision:5});

&nbsp; &nbsp; &nbsp; // create a new directional white light source with specific ambient, diffuse and specular parameters
&nbsp; &nbsp; &nbsp; var light:DirectionalLight3D = new DirectionalLight3D({color:0xFFFFFF, ambient:0.25, diffuse:0.75, specular:0.9});
&nbsp; &nbsp; &nbsp; light.x = 10000;
&nbsp; &nbsp; &nbsp; light.z = 50000;
&nbsp; &nbsp; &nbsp; light.y = 50000;
&nbsp; &nbsp; &nbsp; scene.addChild(light);

&nbsp; &nbsp; &nbsp; // Create six with the same (normal mapped) material and position them them to create a cube
&nbsp; &nbsp; &nbsp; var topPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; topPlane.y = 50;
&nbsp; &nbsp; &nbsp; var leftPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; leftPlane.rotationZ = 90;
&nbsp; &nbsp; &nbsp; leftPlane.x = -50;
&nbsp; &nbsp; &nbsp; var frontPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; frontPlane.rotationX = 90;
&nbsp; &nbsp; &nbsp; frontPlane.z = -50;
&nbsp; &nbsp; &nbsp; var bottomPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; bottomPlane.rotationX = 180;
&nbsp; &nbsp; &nbsp; bottomPlane.y = -50;
&nbsp; &nbsp; &nbsp; var rightPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; rightPlane.rotationZ = -90;
&nbsp; &nbsp; &nbsp; rightPlane.x = 50;
&nbsp; &nbsp; &nbsp; var backPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; backPlane.rotationX = -90;
&nbsp; &nbsp; &nbsp; backPlane.z = 50;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create an object container to group the sides of the cube
&nbsp; &nbsp; &nbsp; planeGroup = new ObjectContainer3D();
&nbsp; &nbsp; &nbsp; scene.addChild(planeGroup);

&nbsp; &nbsp; &nbsp; planeGroup.addChild(topPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(leftPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(frontPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(bottomPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(rightPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(backPlane);
&nbsp; &nbsp; &nbsp; planeGroup.x = -100;
&nbsp; &nbsp; &nbsp; planeGroup.z = 100;

&nbsp; &nbsp; &nbsp; // Create a sphere with normal-mapped material
&nbsp; &nbsp; &nbsp; normalSphere = new Sphere({material:sphereNormalMapMaterial, radius:65, segmentsW:10, segmentsH:10, ownCanvas:true});
&nbsp; &nbsp; &nbsp; normalSphere.x = 100;
&nbsp; &nbsp; &nbsp; normalSphere.z = 100;
&nbsp; &nbsp; &nbsp; scene.addChild(normalSphere);

&nbsp; &nbsp; &nbsp; // Create a sphere with environment-mapped material
&nbsp; &nbsp; &nbsp; envSphere = new Sphere({material:envMapMaterial, radius:65, segmentsW:10, segmentsH:10, ownCanvas:true});
&nbsp; &nbsp; &nbsp; envSphere.z = -90;
&nbsp; &nbsp; &nbsp; scene.addChild(envSphere);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Frame-enter event handler
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function loop(event:Event):void {
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // rotate the objects
&nbsp; &nbsp; &nbsp; planeGroup.rotationY += 2;
&nbsp; &nbsp; &nbsp; normalSphere.rotationY += 2;
&nbsp; &nbsp; &nbsp; envSphere.rotationY += 2;

&nbsp; &nbsp; &nbsp; // update camera position
&nbsp; &nbsp; &nbsp; updateCamera();
&nbsp; &nbsp; &nbsp; camera.hover();  

&nbsp; &nbsp; &nbsp; // Render the 3D scene
&nbsp; &nbsp; &nbsp; view.render();
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Update the camera position from mouse movements
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function updateCamera():void {
&nbsp; &nbsp; &nbsp; if (doRotation) {
&nbsp; &nbsp; &nbsp; &nbsp; camera.targetpanangle = 0.5 * (stage.mouseX - lastMouseX) + lastPanAngle;
&nbsp; &nbsp; &nbsp; &nbsp; camera.targettiltangle = 0.5 * (stage.mouseY - lastMouseY) + lastTiltAngle;
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Mouse down listener for camera rotation
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onMouseDown(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; lastPanAngle = camera.targetpanangle;
&nbsp; &nbsp; &nbsp; lastTiltAngle = camera.targettiltangle;
&nbsp; &nbsp; &nbsp; lastMouseX = stage.mouseX;
&nbsp; &nbsp; &nbsp; lastMouseY = stage.mouseY;
&nbsp; &nbsp; &nbsp; doRotation = true;
&nbsp; &nbsp; &nbsp; stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Mouse up listener for camera rotation
&nbsp; &nbsp;  */
&nbsp; &nbsp;  private function onMouseUp(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; doRotation = false;
&nbsp; &nbsp; &nbsp; stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
&nbsp; &nbsp; }

&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Mouse stage leave listener for camera rotation
&nbsp; &nbsp;  */
&nbsp; &nbsp; private function onStageMouseLeave(event:Event):void {
&nbsp; &nbsp; &nbsp; doRotation = false;
&nbsp; &nbsp; &nbsp; stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
&nbsp; &nbsp; }
&nbsp; &nbsp;
&nbsp; &nbsp; /**
&nbsp; &nbsp;  * Resize the scene when the stage resizes
&nbsp; &nbsp;  */&nbsp;
&nbsp; &nbsp; private function onResize(event:Event):void {
&nbsp; &nbsp; &nbsp; view.x = stage.stageWidth / 2;
&nbsp; &nbsp; &nbsp; view.y = stage.stageHeight / 2;
&nbsp; &nbsp; }

&nbsp; }
&nbsp;
&nbsp;
}
</blockquote>
</pre>
</div>
<p>If you want to use the same images as in this example then away3D.png and checker.jpg you&#8217;ll find in the previous post on <a href="http://blog.tartiflop.com/2008/11/first-steps-in-away3d-part-3-texture-mapping/">texture mapping in Away3D</a>. The new images are shown below.</p>
<p><a href="http://blog.tartiflop.com/wp-content/uploads/2008/12/normalmap.png"><img src="http://blog.tartiflop.com/wp-content/uploads/2008/12/normalmap-150x150.png" alt="" title="" margin="10" width="150" height="150" class="alignnone size-thumbnail wp-image-261" /></a> <a href="http://blog.tartiflop.com/wp-content/uploads/2008/12/asteroidnormal.jpg"><img src="http://blog.tartiflop.com/wp-content/uploads/2008/12/asteroidnormal-150x150.jpg" alt="" title="" margin="10" width="150" height="150" class="alignnone size-thumbnail wp-image-262" /></a> <a href="http://blog.tartiflop.com/wp-content/uploads/2008/12/envmap.png"><img src="http://blog.tartiflop.com/wp-content/uploads/2008/12/envmap-150x150.png" alt="" title="" margin="10" width="150" height="150" class="alignnone size-thumbnail wp-image-263" /></a></p>
<p>The first two images (the normal maps) were created using the utility described above. The first one from a bump map I used for a <a href="http://blog.tartiflop.com/2008/08/first-steps-in-papervision3d-part-7-texture-mapping-with-lighting-bump-mapping-and-environment-mapping/">Papervision3D tutorial on bump mapping</a>, the second one comes from a bump map I found on the internet at <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=263350">gamedev.net</a>. The environment map comes from and old OpenGL example I discovered on my hard disk&#8230; if you search for &#8220;opengl sphere map&#8221; in Google you&#8217;ll find plenty of instances!</p>
<p>Once its all compiled you should see three rotating objects with either normal and environment mapped data. Click on the image below to see the Flash movie. You can click anywhere and move the mouse to rotate the scene.</p>
<p><a href="http://www.tartiflop.com/away3d/FirstSteps/Example006.swf" class="lightwindow" params="lightwindow_width=640,lightwindow_height=480" title=""><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://blog.tartiflop.com/wp-content/uploads/2008/12/example006.png" border="0" /></a></p>
<p>So, lets go into more detail on each part of the code. There&#8217;s nothing too complicated here but a few changes from the last time&#8230;</p>
<p>The first change comes from the initialisation of the 3D basics of the scene, specifically with the camera.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; private function init3D():void {

&nbsp; &nbsp; &nbsp; // Create a new scene where all the 3D object will be rendered
&nbsp; &nbsp; &nbsp; scene = new Scene3D();
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new camera, passing some initialisation parameters
&nbsp; &nbsp; &nbsp; camera = new HoverCamera3D({zoom:25, focus:30, distance:600});
&nbsp; &nbsp; &nbsp; camera.targetpanangle = camera.panangle = -10;
&nbsp; &nbsp; &nbsp; camera.targettiltangle = camera.tiltangle = 20;
&nbsp; &nbsp; &nbsp; camera.yfactor = 1;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create a new view that encapsulates the scene and the camera
&nbsp; &nbsp; &nbsp; view = new View3D({scene:scene, camera:camera});

&nbsp; &nbsp; &nbsp; // center the view to the middle of the stage
&nbsp; &nbsp; &nbsp; view.x = stage.stageWidth / 2;
&nbsp; &nbsp; &nbsp; view.y = stage.stageHeight / 2;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // ensure that the z-order is calculated correctly
&nbsp; &nbsp; &nbsp; view.renderer = Renderer.CORRECT_Z_ORDER;
&nbsp; &nbsp; &nbsp;
   &nbsp; &nbsp; &nbsp; addChild(view);
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>I&#8217;ve now chosen to use a <em>HoverCamera3D</em>. This simplifies the movement and positioning of the camera. You&#8217;ll remember that previously I used the basic <em>Camera3D</em> class and had to calculate specific values of <em>x</em>, <em>y</em> and <em>z</em> as well as redirect the camera towards the origin. Much of this is included in the <em>HoverCamera3D</em> class. By specifying <em>targettiltangle</em> and <em>targetpanangle</em> the camera will calculate a trajectory necessary to move the camera to a new position and keep it focused on a particular target. To maintain the orbital radius of the camera we set the <em>yfactor</em> to 1 otherwise a more elliptical orbit is calculated.</p>
<p>You&#8217;ll see towards the end of the code how we perform an interaction with the camera. If we look at the <em>loop</em> function, called on every frame-enter event we can see how the camera position is updated.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; private function loop(event:Event):void {
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // rotate the objects
&nbsp; &nbsp; &nbsp; planeGroup.rotationY += 2;
&nbsp; &nbsp; &nbsp; normalSphere.rotationY += 2;
&nbsp; &nbsp; &nbsp; envSphere.rotationY += 2;

&nbsp; &nbsp; &nbsp; // update camera position
&nbsp; &nbsp; &nbsp; updateCamera();
&nbsp; &nbsp; &nbsp; camera.hover();  

&nbsp; &nbsp; &nbsp; // Render the 3D scene
&nbsp; &nbsp; &nbsp; view.render();
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>The object rotation is self evident so we&#8217;ll ignore that. The <em>updateCamera</em> function recalculates the camera position from mouse position. More importantly the <em>camera.hover()</em> call makes the camera move gradually towards the <em>target</em> tilt and pan angles giving a smoother animation of the camera than in previous examples.</p>
<p>For completeness, the <em>updateCamera</em> function and the <em>mouseDown</em> event listener are shown below.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; private function updateCamera():void {
&nbsp; &nbsp; &nbsp; if (doRotation) {
&nbsp; &nbsp; &nbsp; &nbsp; camera.targetpanangle = 0.5 * (stage.mouseX - lastMouseX) + lastPanAngle;
&nbsp; &nbsp; &nbsp; &nbsp; camera.targettiltangle = 0.5 * (stage.mouseY - lastMouseY) + lastTiltAngle;
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;
&nbsp; &nbsp; private function onMouseDown(event:MouseEvent):void {
&nbsp; &nbsp; &nbsp; lastPanAngle = camera.targetpanangle;
&nbsp; &nbsp; &nbsp; lastTiltAngle = camera.targettiltangle;
&nbsp; &nbsp; &nbsp; lastMouseX = stage.mouseX;
&nbsp; &nbsp; &nbsp; lastMouseY = stage.mouseY;
&nbsp; &nbsp; &nbsp; doRotation = true;
&nbsp; &nbsp; &nbsp; stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>As you can see, the target angles are updated in the <em>updateCamera</em> function, taking into account the current mouse position. The <em>onMouseDown</em> function performs the necessary initialisation of pan and tilt angles. It also adds an event listener to detect when the mouse leaves the stage: this is useful because if the mouse button is released outside the stage, the <em>onMouseUp</em> listener is not called and we can obtain a state in which the scene is continuously rotated even if the mouse button is up. </p>
<p>So, on to more interesting elements of the code: normal mapped and environment mapped materials!</p>
<p>All the interesting stuff happens in <em>createScene</em> where the materials and objects are created.</p>
<div id="codeSnippet">
<pre>
<blockquote>

&nbsp; &nbsp; private function createScene():void {

&nbsp; &nbsp; &nbsp; // Create 3 different materials: two normal mapped ones (planar and spherical) and an
&nbsp; &nbsp; &nbsp; // environment mapped one.
&nbsp; &nbsp; &nbsp; var normalMapMaterial:Dot3BitmapMaterial = new Dot3BitmapMaterial(Cast.bitmap(away3DBitmap), Cast.bitmap(normalBitmap), {smooth:true, precision:5});
&nbsp; &nbsp; &nbsp; var envMapMaterial:EnviroBitmapMaterial = new EnviroBitmapMaterial(Cast.bitmap(checkerBitmap), Cast.bitmap(envBitmap), {smooth:true, precision:5});
&nbsp; &nbsp; &nbsp; var sphereNormalMapMaterial:Dot3BitmapMaterial = new Dot3BitmapMaterial(new BitmapData(1, 1, false, 0x666666), Cast.bitmap(sphereNormalBitmap), {smooth:true, precision:5});

&nbsp; &nbsp; &nbsp; // create a new directional white light source with specific ambient, diffuse and specular parameters
&nbsp; &nbsp; &nbsp; var light:DirectionalLight3D = new DirectionalLight3D({color:0xFFFFFF, ambient:0.25, diffuse:0.75, specular:0.9});
&nbsp; &nbsp; &nbsp; light.x = 10000;
&nbsp; &nbsp; &nbsp; light.z = 50000;
&nbsp; &nbsp; &nbsp; light.y = 50000;
&nbsp; &nbsp; &nbsp; scene.addChild(light);

&nbsp; &nbsp; &nbsp; // Create six with the same (normal mapped) material and position them them to create a cube
&nbsp; &nbsp; &nbsp; var topPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; topPlane.y = 50;
&nbsp; &nbsp; &nbsp; var leftPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; leftPlane.rotationZ = 90;
&nbsp; &nbsp; &nbsp; leftPlane.x = -50;
&nbsp; &nbsp; &nbsp; var frontPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; frontPlane.rotationX = 90;
&nbsp; &nbsp; &nbsp; frontPlane.z = -50;
&nbsp; &nbsp; &nbsp; var bottomPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; bottomPlane.rotationX = 180;
&nbsp; &nbsp; &nbsp; bottomPlane.y = -50;
&nbsp; &nbsp; &nbsp; var rightPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; rightPlane.rotationZ = -90;
&nbsp; &nbsp; &nbsp; rightPlane.x = 50;
&nbsp; &nbsp; &nbsp; var backPlane:Plane = new Plane({material:normalMapMaterial, width:100, height:100, segmentsW:2, segmentsH:2, ownCanvas:true});
&nbsp; &nbsp; &nbsp; backPlane.rotationX = -90;
&nbsp; &nbsp; &nbsp; backPlane.z = 50;
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; // Create an object container to group the sides of the cube
&nbsp; &nbsp; &nbsp; planeGroup = new ObjectContainer3D();
&nbsp; &nbsp; &nbsp; scene.addChild(planeGroup);

&nbsp; &nbsp; &nbsp; planeGroup.addChild(topPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(leftPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(frontPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(bottomPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(rightPlane);
&nbsp; &nbsp; &nbsp; planeGroup.addChild(backPlane);
&nbsp; &nbsp; &nbsp; planeGroup.x = -100;
&nbsp; &nbsp; &nbsp; planeGroup.z = 100;

&nbsp; &nbsp; &nbsp; // Create a sphere with normal-mapped material
&nbsp; &nbsp; &nbsp; normalSphere = new Sphere({material:sphereNormalMapMaterial, radius:65, segmentsW:10, segmentsH:10, ownCanvas:true});
&nbsp; &nbsp; &nbsp; normalSphere.x = 100;
&nbsp; &nbsp; &nbsp; normalSphere.z = 100;
&nbsp; &nbsp; &nbsp; scene.addChild(normalSphere);

&nbsp; &nbsp; &nbsp; // Create a sphere with environment-mapped material
&nbsp; &nbsp; &nbsp; envSphere = new Sphere({material:envMapMaterial, radius:65, segmentsW:10, segmentsH:10, ownCanvas:true});
&nbsp; &nbsp; &nbsp; envSphere.z = -90;
&nbsp; &nbsp; &nbsp; scene.addChild(envSphere);
&nbsp; &nbsp; }
</blockquote>
</pre>
</div>
<p>To start off with the three different materials are created. Firstly a <em>Dot3BitmapMaterial</em> taking the Away3D texture and a random normal map. I&#8217;ve chosen to smooth the material and have increased the precision of rendering to 5 pixels. The second material uses the <em>EnviroBitmapMaterial</em> using the checkered texture and the OpenGl sphere-map for the environment data, again smoothed as before. Incidentally, the shininess of the material can be modified by specifying the <em>reflectiveness</em> parameter in the initialisation parameters, taking a value between 0 and 1 (for most reflective). The final <em>Dot3BitmapMaterial</em> material, rather than using a bitmap file, creates a plain grey bitmap. The normal map comes from the asteroid spherical normal map.</p>
<p>We add a <em>DirectionalLight3D</em> to the scene to provide uniform lighting, independent of light distance. It should be noted that the <em>Dot3BitmapMaterial</em> will not currently work with a <em>PointLight3D</em>. Also, the <em>EnviroBitmapMaterial</em> is independent of the source: the environment map provides the equivalent shading.</p>
<p>For the <em>cube</em> we create six <em>Plane</em>s, each using the same material. These are then rotated and translated to form a cube. They are finally added to a <em>ObjectContainer3D</em> so that we can rotate all of them together in the <em>loop</em> function. Note as well that, as before, each object needs to have its own canvas which is specified within the initialisation parameters.</p>
<p>The two spheres are then created: one using an environment map, the other with normal mapped data. These are then positioned and added to the scene, again with independent canvases. </p>
<p>And that&#8217;s all there is to it! As you&#8217;ll see, the normal map produces fantastic results and really adds a feeling of depth to the object surface. The environment map really gives the impression that the object is very shiny. These effects are very simple to produce in Away3D but be aware too that added realism comes at a cost - the frame rate is bound to be slower than without these effects - so be warned!</p>
<p>Anyway, I hope this has been a useful addition to basic lighting and shading. As always, let me know if you have any comments or questions!</p>
<p>Next article:</p>
<ul>
<li><a href="http://blog.tartiflop.com/2008/12/first-steps-in-away3d-part-7-movie-and-video-materials/">First steps in Away3D : Part 7 - Movie and video materials</a>Add depth to website by displaying interactive movies or videos as materials on 3D surfaces.
</li>
</ul>

<p><a href="http://feeds.feedburner.com