topical media & game development

talk show tell print

lib-flex-cover-com-dougmccune-containers-BasePV3DContainer.ax

lib-flex-cover-com-dougmccune-containers-BasePV3DContainer.ax (swf ) [ flash ] flex


  package com.dougmccune.containers
  {
          import caurina.transitions.Tweener;
          
          import com.dougmccune.containers.materials.FlexMaterial;
          import com.dougmccune.containers.materials.ReflectionFlexMaterial;
          
          import flash.display.DisplayObject;
          import flash.display.Sprite;
          import flash.events.Event;
          import flash.events.MouseEvent;
          import flash.events.TimerEvent;
          import flash.utils.Dictionary;
          import flash.utils.Timer;
          
          import mx.containers.ViewStack;
          import mx.core.ContainerCreationPolicy;
          import mx.core.mx_internal;
          
          import org.papervision3d.Papervision3D;
          import org.papervision3d.cameras.Camera3D;
          import org.papervision3d.core.proto.CameraObject3D;
          import org.papervision3d.materials.MovieMaterial;
          import org.papervision3d.objects.DisplayObject3D;
          import org.papervision3d.objects.Plane;
          import org.papervision3d.scenes.MovieScene3D;
          import org.papervision3d.scenes.Scene3D;
          
          use namespace mx_internal;
          
          [Style(name="horizontalSpacing", type="Number", format="Length", inherit="no")]
          [Style(name="verticalSpacing", type="Number", format="Length", inherit="no")]
          
          public class @ax-lib-flex-cover-com-dougmccune-containers-BasePV3DContainer extends ViewStack
          {
                  
The time for each tween. Setting this lower will make the animations faster (but maybe choppier if the CPU can't keep up).

  
                  public var tweenDuration:Number = 1;
                  
                  
Is the reflection enabled? If so we create two 3D planes for each child. This effectively means that PaperVision has to render twice as many polygons if you enable the reflection, whch will slow performance. But it looks nice.

  
                  public function get reflectionEnabled():Boolean {
                          return _reflectionEnabled;
                  }
                  
                  public function set reflectionEnabled(value:Boolean):void {
                          _reflectionEnabled = value;
                  }
                  
                  private var _reflectionEnabled:Boolean = false;
                  
                  
The number of segments used for the PaperVision Planes that are created. The lower the number the better the performance, but you'll notice distortion in your images when they are rotated. For some types of images a value as low as 1 or 2 will work fine, but for things wil horizontal lines or text, you'll have to go higher.

  
                  public var segments:Number = 6;
                  
                  
@private We're going to have a sprite that contains our PV3D scene added to the display list, which falls outside of the stuff that gets clipped normally by the container. So if we want to clip the content like you would expect from a Container then we have to do our own clipping.

  
                  private var clippingMask:Sprite;
                  
                  
@private This is the main Sprite that will get rendered with our 3D scene.

  
                  private var pv3dSprite:Sprite;
                  
                  
@private The Scene3D that PaperVision will render. This will get rendered to the pv3dSprite object.

  
                  protected var scene:Scene3D;
                  
                  
@private The Camera3D object that controls how we render the scene.

  
                  protected var camera:CameraObject3D;
                  
                  
@private A Dictionary we'll use to store a reference to the Plane object we create. The key will be the DisplayObject and the value will be the Plane, that way we can take any child DIsplayObject and look up the 3D Plane.

  
                  private var objectsToPlanes:Dictionary;
                  
                  
@private Same thing for the reflection Planes. Gotta be able to take any DIsplayObject and look up the reflection for that object.

  
                  private var objectsToReflections:Dictionary;
                  
                  
@private We want to detect clicks on the 3D Planes, but we don't want to use the complex interactivity crap in PV3D. (It's not actually crap, it's awesome, but it's slow). So instead, since we're using a MovieScene3D, we can simply access the container proeprty of any 3D object to get access to the DisplayObjec that it is in. Then we can use this container for our mouse click detection. So we need to be able take any of those container DIsplayObjects and look up the original child that it's associated with.

  
                  private var containersToObjects:Dictionary;
                  
                  
@private When the 3D transition is complete and the selected child faces the user face on, then we want to substitute the real child in it's place, so that the user can interact with it. To do this we use a timer that gets reset everytime a tween is started. Then once the tween has successfully completed, which means the selected child is directly facing the user, we do the old switcheroo.

  
                  private var timer:Timer;
                  
                  public function @ax-lib-flex-cover-com-dougmccune-containers-BasePV3DContainer():void {
                          super();
                          
                          //since we need to show all the children we have to make sure that
                          //creationPolicy is set to all. Otherwise the other non-selected 
                          //children would be blank until they were selected and that would look lame.
                          this.creationPolicy = ContainerCreationPolicy.ALL;
                          
                          //crate our dictionaries, using weak keys
                          objectsToPlanes = new Dictionary(true);
                          objectsToReflections = new Dictionary(true);
                          containersToObjects = new Dictionary(true);
                          
                          timer = new Timer(tweenDuration*1000, 1);
                          timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerComplete);
                          
                          pv3dSprite = new Sprite();
                          setupScene();
                  }
                  
                  override protected function createChildren():void {
                          super.createChildren();
                          
                          clippingMask = new Sprite();
                          rawChildren.addChild(clippingMask);
                          
                          rawChildren.addChildAt(pv3dSprite, 0);
                          
                          //we're just going to render the 3D scene on every frame
                          this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                  }
                  
                  
                  
                  
                  
                  protected function setupScene():void {
                          //turn off the debugging trace statements for PV3D
                          Papervision3D.VERBOSE = false;
                          
                          //create a new MovieScene3D and tell it to draw to pv3dSprite
                          scene = new MovieScene3D(pv3dSprite);
                          
                          //create a new Camera3D
                          camera = new Camera3D();
                          camera.z = -200;
                  }
                  
                  protected function enterFrameHandler(event:Event):void {
                          try {
                                  if(selectedChild != null){
                                          var plane:Plane = objectsToPlanes[selectedChild];
                                          
                                          if(Tweener.isTweening(plane)){
                                                  scene.renderCamera(camera);
                                          }
  
                                  }
                          }
                          catch(e:Error) { }
                  }
                  
                  override public function addChild(child:DisplayObject):DisplayObject {
                          var child:DisplayObject = super.addChild(child);
                          
                          if(reflectionEnabled) {
                                  var reflMaterial:MovieMaterial = new ReflectionFlexMaterial(child);
                                  
                                  var reflection:Plane = new Plane(reflMaterial, child.width, child.height, segments, segments);
                                  scene.addChild(reflection);
                                  
                                  objectsToReflections[child] = reflection;
                          }
                  
                          var material:MovieMaterial = new FlexMaterial(child, true);
                          material.smooth = true;
                          
                          var plane:Plane = new Plane(material, child.width, child.height, segments, segments);        
                          scene.addChild(plane);
                          
                          containersToObjects[plane.container] = child;
                          
                          //once the Plane is added to the scene we can access the container property, which we use to handle
                          //mouse clicks
                          plane.container.addEventListener(MouseEvent.CLICK, containerClicked);
                          
                          objectsToPlanes[child] = plane;
                          
                          return child;
                  }
                  
                  protected function lookupPlane(child:DisplayObject):DisplayObject3D {
                          return objectsToPlanes[child];
                  }
                  
                  protected function lookupReflection(child:DisplayObject):DisplayObject3D {
                          return objectsToReflections[child];
                  }
                  
                  private function containerClicked(event:MouseEvent):void {
                          var child:DisplayObject = containersToObjects[event.currentTarget];
                          
                          var index:int = getChildIndex(child);
                          selectedIndex = index;
                  }
                  
                  
Whenever we remove a child we also remove the planes that we had created for it.

  
                  override public function removeChild(child:DisplayObject):DisplayObject {
                          var plane:Plane = objectsToPlanes[child];
                          
                          scene.removeChild(plane);
                          
                          if(reflectionEnabled) {
                                  var refl:Plane = objectsToReflections[child];
                                  scene.removeChild(refl);
                          }
                          
                          return super.removeChild(child);
                  }
                  
                  
                  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
                          super.updateDisplayList(unscaledWidth, unscaledHeight);
                          
                          //sometimes our child ordering gets jacked. Make sure our pv3d sprite is below the actual children
                          //which is important once we show the real child display object
                          if(rawChildren.contains(pv3dSprite)) {
                                  if(border) {
                                          rawChildren.setChildIndex(pv3dSprite, 0);
                                          rawChildren.setChildIndex(DisplayObject(border), 0);
                                  }
                                  else {
                                          rawChildren.setChildIndex(pv3dSprite, 0);
                                  }
                          }
                          
                          clippingMask.graphics.clear();
                          
                          if(clipContent) {
                                  clippingMask.graphics.beginFill(0x000000);
                                  clippingMask.graphics.drawRect(0,0, unscaledWidth, unscaledHeight);
                                  pv3dSprite.mask = clippingMask;
                          }
                          
                          pv3dSprite.y = unscaledHeight/2;
                          pv3dSprite.x = unscaledWidth/2;
                          
                          layoutChildren(unscaledWidth, unscaledHeight);
                          
                  }
                  
                  protected function layoutChildren(unscaledWidth:Number, unscaledHeight:Number):void {
                          if(timer.running) {
                                  timer.reset();
                          }
                          
                          timer.start();
                  }
                  
                  private function timerComplete(event:TimerEvent):void {
                          showVisibleChild();
                  }
                  
                  private function showVisibleChild():void {
                          if(selectedChild != null) {
                                  selectedChild.visible = true;
                                  
                                  var plane:Plane = objectsToPlanes[selectedChild];
                                  plane.container.visible = false;
                                  
                                  if(border) {
                                          
                                          rawChildren.setChildIndex(pv3dSprite, 0);
                                          rawChildren.setChildIndex(DisplayObject(border), 0);
                                  }
                                  else {
                                          rawChildren.setChildIndex(pv3dSprite, 0);
                                  }
                          }
                  }
          }
  }


(C) Æliens 18/6/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.