Erain 3D
-->

Author: Max Pellizzaro
Date: October 29th 2007
version: 3.0

Accuracy vs Performance

Objective of the tutorial

In this tutorial I have decided to talk about something that it’s very important when you start building more complex objects and scenes. When you start learning a new programming language, or a new API such as Sandy in our case, first you want to see things moving around to have the feeling you are doing something. Then it comes a time where you are developing and trying more detail stuff and you knock your head on two topics: accuracy and performance. When designing your project you must take in account that accuracy come with the cost of lot of computation, and you must always find a good balance between the two. Maybe you have a computer very powerful, but it’s true that when you deliver your software not all the users has your machine.
With this tutorial you will learn these topics:

A good balance between all these aspect will produce an accurate and hopefully not too heavy project to run on your machine.

How to

Set up

The Document class must be changed to Example005.as The name of the class in the .as file and the name of the constructor now is: Example005.

example005.rar

The AS Code

package
{
   import flash.display.Sprite; 
   import flash.events.*;
   import flash.ui.*;
   import sandy.core.Scene3D;
   import sandy.core.data.*;
   import sandy.core.scenegraph.*;
   import sandy.materials.*;
   import sandy.materials.attributes.*;
   import sandy.primitive.*;
 
   public class Example005 extends Sprite 
   {
      private var scene:Scene3D;
	  private var camera:Camera3D;
	  private var tg:TransformGroup;
	  private var myCone:Cone;
	  private var myHedra:Hedra;
 
      public function Example005()
      { 
		 // We create the camera
		 camera = new Camera3D( 300, 300 );
		 camera.z = -400;
		 camera.lookAt(0,0,0);
 
		 // We create the "group" that is the tree of all the visible objects
         var root:Group = createScene();
 
		 // We create a Scene and we add the camera and the objects tree 
	     scene = new Scene3D( "scene", this, camera, root );
 
		 // Listen to the heart beat and render the scene
		 addEventListener( Event.ENTER_FRAME, enterFrameHandler );
		 stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
 
      }
 
      // Create the scene graph based on the root Group of the scene
      private function createScene():Group
      {
         // Create the root Group
         var g:Group = new Group();
 
		 // We need to create a transformGroup
		 tg = new TransformGroup('myGroup');
 
         // we now create the three planes and two spheres
		 var right:Shape3D = new Plane3D('right', 150, 100, 10, 10, 
							Plane3D.YZ_ALIGNED,'quad');
		 right.moveLateraly(100);
		 right.moveForward(50)
		 var left:Shape3D = new Plane3D('left', 150, 100, 10, 10, 
							Plane3D.YZ_ALIGNED,'quad');
		 left.moveLateraly(-100);
		 left.moveForward(50)
 
		 var back:Shape3D = new Plane3D('back', 100, 250, 10, 10, 
							Plane3D.XY_ALIGNED,'quad');
		 back.moveForward(100);
 
		 var bottom:Shape3D = new Plane3D('bottom', 150, 250, 10, 10, 
							Plane3D.ZX_ALIGNED,'quad');
		 bottom.moveForward(50);
		 bottom.moveUpwards(-50);
 
 
 
		 var materialAttr:MaterialAttributes = new MaterialAttributes( 
				new LineAttributes( 0.5, 0x2111BB, 0.4 ),
				new LightAttributes( true, 0.1)
				);
 
	     var material01:Material = new ColorMaterial( 0xFFCC33, 1, materialAttr );
	     material01.lightingEnable = false;
		 var material02:Material = new ColorMaterial( 0xFEA792, 1, materialAttr );
	     material02.lightingEnable = false;
	     var app01:Appearance = new Appearance( material01 );
		 var app02:Appearance = new Appearance( material02 );
 
		 // we add appearance to all our objects
		 right.enableBackFaceCulling = false;
		 right.appearance = app01;
		 left.enableBackFaceCulling = false;
		 left.appearance = app01;
		 back.enableBackFaceCulling = false;
		 back.appearance = app02;
		 bottom.enableBackFaceCulling = true;
		 bottom.appearance = app02;
 
		 right.useSingleContainer = false;
		 left.useSingleContainer = false;
		 back.useSingleContainer = false;
		 bottom.useSingleContainer = false;
 
		 // we add both object to the transform group		 
		 tg.addChild(right);
		 tg.addChild(left);
		 tg.addChild(back);
		 tg.addChild(bottom);
		 // we now add all the object to the root group:
		 g.addChild(tg);
 
		  return g;
      }
 
      // The Event.ENTER_FRAME event handler tells the world to render
      private function enterFrameHandler( event : Event ) : void
      {
         //myHedra.pan +=4;
		 //myCone.pan +=4;
		 scene.render();
      }
 
	  private function keyPressed(event:KeyboardEvent):void {
            switch(event.keyCode) {
				case Keyboard.UP:
					tg.tilt +=2;
					break;
				case Keyboard.DOWN:
					tg.tilt -=2;
					break;
				case Keyboard.RIGHT:
					tg.pan +=2;
					break;
				case Keyboard.LEFT:
					tg.pan -=2;
					break;
			}
        }
   }
}

Examining the code

By this time you should be familiar with defining the Camera3D and positioning into the Scene. You should be familiar also with event handler, so I won't explain them, but will concentrate on the code inside the createScene ()section.
With this tutorial we are going to build a “room”, yes a room with three walls and a floor, and we will place the camera in front of the room to see what we have produced.

var right:Shape3D = new Plane3D('right', 150, 100, 10, 10, 
						Plane3D.YZ_ALIGNED,'quad');
right.moveLateraly(100);
right.moveForward(50)
var left:Shape3D = new Plane3D('left', 150, 100, 10, 10, 
						Plane3D.YZ_ALIGNED,'quad');
left.moveLateraly(-100);
left.moveForward(50)
 
var back:Shape3D = new Plane3D('back', 100, 250, 10, 10, 
						Plane3D.XY_ALIGNED,'quad');
back.moveForward(100);
 
var bottom:Shape3D = new Plane3D('bottom', 150, 250, 10, 10, 
						Plane3D.ZX_ALIGNED,'quad');
bottom.moveForward(50);
bottom.moveUpwards(-50);

When defining these objects you can notice something new: the mode. There is a theorem in geometry that state that to define a single plane you need only three points: that is a triangle. This is the basis of all 3D software: each object is composed of triangles (I will not go in a deeper detail but it has to do with accuracy, lighting and more complex features about 3D). Sandy is a 3D API, and so all Sandy objects are composed by triangles. This is default behavior, but Sandy allows you to use a different mode: quad (that means quadratic). With this mode you decompose your objects in rectangles. No need to say that rendering less objects is easier that rendering more objects, but to the cost of less accuracy. But if we are modeling walls, this is the best option to use, so let’s put in practice. In all the Plane3D I have built you will find this mode (the last parameter of the constructor).

right.enableBackFaceCulling = false;
right.appearance = app01;
left.enableBackFaceCulling = false;
left.appearance = app01;
back.enableBackFaceCulling = false;
back.appearance = app02;
bottom.enableBackFaceCulling = true;
bottom.appearance = app02;

Do not scare about that name, and see what does it means. Face culling just means that back faces will not be rendered. Simple isn’t. By enabling the face culling you just tell the engine to do not render the back face. This comes handy when you don’t need to rotate the object, but what if you need to rotate? In our example we have enabled the backfaceculling only to the floor of our room, and let’s see what this means. The code to do so is simple: enableBackFaceCulling = false/true;

right.useSingleContainer = false;
left.useSingleContainer = false;
back.useSingleContainer = false;
bottom.useSingleContainer = false;

It took me some time to understand it, well to understand what the implication to set it true or false was. In order to fully understand it’s meaning one should know that in Action Script you work with “containers”, that are physical objects that hold something. The Sprite is a container, the class Movie is another container. Sandy use this concept in rendering the objects. Using a single container is by far most efficient, but you will lose accuracy, because the rendering of all the objects must be done inside the container and sometimes you might lose the depth or intersection between all the objects. Using different container give you the granularity to manage (render) each single object separately, so you will gain in term of accuracy. The two Flash objects at the end of this tutorial only difference between them because one set useSingleContainer to true and one set useSingleContainer to false.

And new let's see the result.

The output

Move UP, DOWN, RIGHT and LEFT key to play with both the Flash objects, and see the difference. My suggestion is to keep the RIGHT (or LEFT) key down and make 360 degree rotation and keep a close look to the intersections between planes. Do the same with the UP, DOWN key. Can you see the difference between the two ?