Erain 3D
-->

Author: Max Pellizzaro
Date: February 18th 2008
version: 3.0

3DS MAX moving wheels of the camero: Part II

Objective of the tutorial

In this tutorial we will learn how to move the wheels of the camero we have just imported in the previous tutorial. You will learn to use an important attribute called geometryCenter. This attribute will allows us to move the center of the Shape3D whenever we want, allowing us to rotate each object to any point of rotation we want to use. Be sure that you have completed the previuos tutorial.

How to

Set up the 3DMAX model

Have a look at the previous tutorial

Set up

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

example0072.rar

The AS Code

package
{
   import flash.display.*; 
   import flash.events.*;
   import flash.ui.*;
   import flash.net.URLRequest;
 
   import sandy.core.Scene3D;
   import sandy.core.data.*;
   import sandy.core.scenegraph.*;
   import sandy.materials.*;
   import sandy.materials.attributes.*;
   import sandy.primitive.*;
   import sandy.parser.*;
   import sandy.util.*;
   import sandy.events.*;
 
   public class Example0072 extends Sprite 
   {
      	  private var scene:Scene3D;
	  private var camera:Camera3D;
	  private var tg:TransformGroup;
	  private var car:Shape3D;
	  private var wheelLF:Shape3D;
	  private var wheelRF:Shape3D;
	  private var wheelLR:Shape3D;
	  private var wheelRR:Shape3D;
	  private var queue:LoaderQueue;
      	  private var parserStack:ParserStack;
 
      public function Example0072()
      { 
		var parser:IParser = Parser.create("assets/models/ASE/car.ASE",Parser.ASE );
		var parserLF:IParser = Parser.create("assets/models/ASE/wheel_Front_L.ASE",Parser.ASE );
	    	var parserRF:IParser = Parser.create("assets/models/ASE/wheel_Front_R.ASE",Parser.ASE );
	    	var parserLR:IParser = Parser.create("assets/models/ASE/wheel_Rear_L.ASE",Parser.ASE );
	    	var parserRR:IParser = Parser.create("assets/models/ASE/wheel_Rear_R.ASE",Parser.ASE );
 
		parserStack = new ParserStack();
		parserStack.add("carParser",parser);
		parserStack.add("wheelLFParser",parserLF);
		parserStack.add("wheelRFParser",parserRF);
		parserStack.add("wheelLRParser",parserLR);
		parserStack.add("wheelRRParser",parserRR);
		parserStack.addEventListener(ParserStack.COMPLETE, parserComplete );
		parserStack.start();
 
	  }
 
	  private function parserComplete(pEvt:Event ):void
      {
		car = parserStack.getGroupByName("carParser").children[0] as Shape3D;
		wheelLF = parserStack.getGroupByName("wheelLFParser").children[0] as Shape3D;
	    	wheelRF = parserStack.getGroupByName("wheelRFParser").children[0] as Shape3D;
		wheelLR = parserStack.getGroupByName("wheelLRParser").children[0] as Shape3D;
		wheelRR = parserStack.getGroupByName("wheelRRParser").children[0] as Shape3D;
		loadSkins();
	  }
 
 
	  private function loadSkins(){
	   	queue = new LoaderQueue();
       		queue.add( "carSkin", new URLRequest("assets/textures/car.jpg") );
	   	queue.add( "wheels", new URLRequest("assets/textures/wheel.jpg") );
 
	   	queue.addEventListener(SandyEvent.QUEUE_COMPLETE, loadComplete );
       		queue.start();
	  }
 
	  // Create the scene graph based on the root Group of the scene
      private function loadComplete(event:QueueEvent)
      {
		 camera = new Camera3D( 600, 300 );
		 camera.y = 30;
		 camera.z = -150;
		 camera.near = 10;
 
 
		 var root:Group = createScene();
		 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, keyPressedHandler);
      }
 
      private function createScene():Group
      {
		 // Create the root Group
		 var g:Group = new Group();
 
		 // We need to create a transformGroup
		 tg = new TransformGroup('myGroup');
 
		 var material:BitmapMaterial = new BitmapMaterial( queue.data["carSkin"].bitmapData );
		 var app:Appearance = new Appearance( material );
		 car.appearance = app;
 
		 var materialW:BitmapMaterial = new BitmapMaterial( queue.data["wheels"].bitmapData );
		 var appW:Appearance = new Appearance( materialW );
		 wheelLF.appearance = appW;
		 wheelRF.appearance = appW;
		 wheelLR.appearance = appW;
		 wheelRR.appearance = appW;
 
		 // use single container = False
		 car.useSingleContainer = false;
		 wheelLF.useSingleContainer = false;
		 wheelRF.useSingleContainer = false;
		 wheelLR.useSingleContainer = false;
		 wheelRR.useSingleContainer = false;
 
		 // change  geometry center for wheels
		 wheelRF.geometryCenter = new Vector(-24,-11.5,-48.5);
		 wheelRF.x += 24;
		 wheelRF.y += 11.5;
		 wheelRF.z += 48.5;
 
		 wheelLF.geometryCenter = new Vector(24.5,-11.5,-48.5);
		 wheelLF.x -= 24.5;
		 wheelLF.y += 11.5;
		 wheelLF.z += 48.5;
 
		 wheelRR.geometryCenter = new Vector(-24,-11.5,+41);
		 wheelRR.x += 24;
		 wheelRR.y += 11.5;
		 wheelRR.z -= 41;
 
		 wheelLR.geometryCenter = new Vector(24.5,-11.5,+41);
		 wheelLR.x -= 24.5;
		 wheelLR.y += 11.5;
		 wheelLR.z -= 41;
 
		 tg.addChild( wheelRF );
		 tg.addChild( wheelLF );
		 tg.addChild( wheelRR );
		 tg.addChild( wheelLR );
		 tg.addChild( car );
 
		 tg.rotateY = -130;
 
		 g.addChild( tg );
		 return g;
      }
 
      private function keyPressedHandler(event:flash.events.KeyboardEvent):void 
      {
	   switch(event.keyCode) {
		case Keyboard.UP:
			wheelLF.tilt +=2;
			wheelRF.tilt +=2;
			wheelRR.tilt +=2;
			wheelLR.tilt +=2;
			break;
		case Keyboard.DOWN:
			wheelLF.tilt -=2;
			wheelRF.tilt -=2;
			wheelRR.tilt -=2;
			wheelLR.tilt -=2;
			break;
		case Keyboard.LEFT:
		    if(wheelLF.pan>=-40){
			 wheelLF.tilt =0;
			 wheelRF.tilt =0;
			 wheelLF.pan -=2;
			 wheelRF.pan -=2;
			}
			break;
		case Keyboard.RIGHT:
		    if(wheelLF.pan<=40){
			 wheelLF.tilt =0;
			 wheelRF.tilt =0;
			 wheelLF.pan +=2;
			 wheelRF.pan +=2;
			}
			break;
		case Keyboard.PAGE_DOWN:
			tg.z -=5;
			break;
		case Keyboard.PAGE_UP:
			tg.z +=5;
			break;
		}
		if(tg.z < -100){
		  tg.visible = false;
		} else if (tg.z > -100) {
		  tg.visible = true;
		}
	  }
	  // The Event.ENTER_FRAME event handler tells the world to render
      private function enterFrameHandler( event : Event ) : void
      {
		//tg.pan += 1;
		scene.render();
      }
   }
}

Examining the code

The code to import the entire 3DMAX models has been examined in the previous tutorial and will not be re-explained in this tutorial. We will concentrate here only on the part of the code that is needed to allow the rotation of the wheels.

One might think that rotating the wheels it’s just a matter of rotating the imported objects, but this is not true. In fact rotating the imported wheels will result in the output we are showing here.

Use the arrow keys to see what will happen.

In order to have the correct and desired movement of the wheels we need to move the center of rotation of each shape3D. In fact if we do not change anything the center of rotation of each element is the center of the car. Lucky for us Sandy provide with a nice attribute that due the job for us: geometryCenter

I did some test in order to almost get the right center of rotation for each wheels, and after the definition of the new center of rotation, I had to move back the wheel where it’s original position was. These are my results:

wheelRF.geometryCenter = new Vector(-24,-11.5,-48.5);
wheelRF.x += 24;
wheelRF.y += 11.5;
wheelRF.z += 48.5;
 
wheelLF.geometryCenter = new Vector(24.5,-11.5,-48.5);
wheelLF.x -= 24.5;
wheelLF.y += 11.5;
wheelLF.z += 48.5;
 
wheelRR.geometryCenter = new Vector(-24,-11.5,+41);
wheelRR.x += 24;
wheelRR.y += 11.5;
wheelRR.z -= 41;
 
wheelLR.geometryCenter = new Vector(24.5,-11.5,+41);
wheelLR.x -= 24.5;
wheelLR.y += 11.5;
wheelLR.z -= 41;

And now let’s see the result!

The output

Use the arrow keys to rotate the wheels around and the PAGE_UP and the PAGE_DOWN to zoom in and zoom out the camera. Have fun!

In the next and last tutorial of this series you will learn how to move the wheels and the car together.