Erain 3D
-->

Author: Max Pellizzaro
Date: November 3rd 2007
Update: February 09th 2008
version: 3.0

Movie Material Interactivity

Objective of the tutorial

In this tutorial we will discover the possibility to use what in Sandy3D is called: “Interactivity”. With this term Sandy refers to the possibility to enable interaction with a movie material that you have placed on a primitive of Sandy.

This tutorial will show you how you can move a picture on a wall. The wall is actually a separated movie clip that implements the logic to move the picture, and the movie clip is used to skin a Sandy plane3D.

How to

Set up the external movie clip

We will set up two different wall. One that have the “monalisa” as a picture and one that has the “Japanese Garder”. The two movie clips are identical, only the picture and color are different.
To set up one these two movie clips you needs to do these basic steps:

The custom class I have created is called Clip.as and you can see from the code below that it allows you to move the movie clip with the standard drag and drop flash feature.

package
{
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	
	public class MyClip extends MovieClip
	{
		private var clip_mc:Clip;
		
		public function MyClip()
		{
		  clip_mc = new Clip();
		  this.addChild(clip_mc);
		  clip_mc.buttonMode = true;
          clip_mc.x=100;
          clip_mc.y=100;
		  clip_mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		  clip_mc.addEventListener(MouseEvent.MOUSE_UP, mouseReleasedHandler);
		}
		
		private function mouseDownHandler(event:MouseEvent):void {
         clip_mc.startDrag();
        }
		
		private function mouseReleasedHandler(event:MouseEvent):void {
         clip_mc.stopDrag();
		}
	}
}

Here below you can see an example on what the movie clip will look like, use the mouse to drag and drop the monalisa.

And this is the archive that collect all the required data for both the movie clips that will be used in this tutorial.

paintingclips.rar

Set up

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

example019.rar

Update after rel 3.0.1

example019_b.rar

The AS Code

package
{
   import flash.display.Sprite; 
   import flash.events.*;
   import flash.ui.*;
   import flash.net.URLRequest;
   import flash.display.Bitmap;
   import flash.display.BitmapData;
   
   import sandy.core.Scene3D;
   import sandy.core.data.*;
   import sandy.core.scenegraph.*;
   import sandy.materials.*;
   import sandy.materials.attributes.*;
   import sandy.primitive.*;
   import sandy.util.*;
   import sandy.events.*;

   public class Example019 extends Sprite 
   {
      private var scene:Scene3D;
	  private var camera:Camera3D;
	  private var plane:Plane3D;
	  private var plane2:Plane3D;
	  private var plane3:Plane3D;
	  private var queue:LoaderQueue;
	  private var img:Parquet=new Parquet();
      private var bitmap:Bitmap=new Bitmap(img);
	  
     public function Example019()
     {
	   queue = new LoaderQueue();
       queue.add( "myClip", new URLRequest("MyClipMona.swf") );
	   queue.add( "myClip2", new URLRequest("MyClipGarden.swf") );
       queue.addEventListener(SandyEvent.QUEUE_COMPLETE, loadComplete );
       queue.start();
	 }
	 
	 private function loadComplete(event:QueueEvent)
      { 
		 // We create the camera
		 camera = new Camera3D( 600, 400 );
		 camera.x = 100;
		 camera.y = 0;
		 camera.z = -450;
		 camera.pan -=14;
		 //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, keyPressedHandler);
      }

      // 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 define a movie material
		var materialAttr03:MaterialAttributes = new MaterialAttributes( 
				                            new OutlineAttributes(3, 0xFC5858, 1));
		
		//queue.data["myClip"].rotation = -90;
		var material01:MovieMaterial = new MovieMaterial( 
				queue.data["myClip"],0,materialAttr03, false, 200, 350);
	    var app01:Appearance = new Appearance( material01);
		
		var material02:MovieMaterial = new MovieMaterial( 
				queue.data["myClip2"],0,materialAttr03, false, 200, 350);
	    var app02:Appearance = new Appearance( material02);

		
		plane = new Plane3D( "thePlane", 200, 350, 10, 10, Plane3D.XY_ALIGNED, "tri" );
	    plane.enableBackFaceCulling = false;
		plane.enableInteractivity = true;
		plane.appearance = app01;
		plane.pan = -70;
		plane.tilt = 0;
		plane.x = -110;
		
		plane2 = new Plane3D( "thePlane2", 200, 350, 10, 10, Plane3D.XY_ALIGNED, "tri" );
		plane2.enableBackFaceCulling = false;
		plane2.enableInteractivity = true;
		plane2.appearance = app02;
		//plane2.roll = -90;
		plane2.tilt = 0;
		plane2.x = 130;
		plane2.z = 160;
		
		var material03:BitmapMaterial = new BitmapMaterial( bitmap.bitmapData );
        material03.lightingEnable = true;
        var app03:Appearance  = new Appearance( material03 );
		plane3 = new Plane3D( "thePlane3", 400, 800, 10, 10, Plane3D.ZX_ALIGNED, "tri" );
		plane3.appearance = app03;
		plane3.y = -100;
		plane3.enableForcedDepth = true;
		plane3.forcedDepth = 999999;
		
		// we now add all the object to the root group:
		g.addChild(plane);
		g.addChild(plane2);
		g.addChild(plane3);
		  
		return g;
      }

      // The Event.ENTER_FRAME event handler tells the world to render
      private function enterFrameHandler( event : Event ) : void
      {
       scene.render();
      }
	  
	  private function keyPressedHandler(event:flash.events.KeyboardEvent):void 
	  {
	   switch(event.keyCode) {
		case Keyboard.UP:
			camera.tilt +=2;
			break;
		case Keyboard.DOWN:
			camera.tilt -=2;
			break;
		case Keyboard.LEFT:
			camera.pan -=2;
			break;
		case Keyboard.RIGHT:
			camera.pan +=2;
			break;
		case Keyboard.PAGE_DOWN:
			camera.z -=5;
			break;
		case Keyboard.PAGE_UP:
			camera.z +=5;
			break;	
			
		}
	  }
   }
}

Examining the code

When examining the code you will not find anything new from the previous lab except one single line:

plane.enableInteractivity = true;

This is the ONLY line that makes all the difference. This line tells Sandy3D to interact with the movieclip object that has been used as a MaterialAttribute for a primitive (in this case the Plane3D). In our case we have used this property for both the plane used as the two walls or our art gallery.

And now let’s see the result !

The output

Drag and drop the two paintings on the walls, and use arrow key and PAGE_UP and PAGE_DOWN key to rotate and move the camera in and out.

Important considerations

I have decided to finish the tutorial with some important considerations about the use of these properties. We are not sure if these considerations refers to limitation of this functionality or not, but it is important the make the user aware of what is the best way to use this functionality.