Seamlessly Combining 2D and 3D in Flash with Planes, Part 1 [AS3] · Jun 22, 05:50 PM

Integrating Papervision3D in a useful way can be tricky. Suppose you’ve created some useful website widget. When the user clicks the settings button, the widget does a 3D-flip and shows the settings controls on the other side. To achieve this in AS3 and Papervision3D, temporarily transfer both display components into a plane, using MovieMaterial. Hide the component, spin the plane by tweening rotationY (or rotationX), and when the tween is complete make the settings component visible.

In the case where you’re transferring only one display component to one Papervision3D viewport, it’s pretty simple to implement this kind of functionality. It starts to get tricky when you want to transfer multiple display components to one viewport and have those components interact in some way with a 3D world.

To make this task easier, confine 2d to 3d transfers to planes placed in the XY plane where Z=0. Also, lets not move the camera. With these constrains in mind, set up your camera like so (last update: 01.14.09):

    var _staticDistanceCamera:Number = 1000;
    camera = new Camera3D();
    camera.zoom = 2;
    camera.focus = _staticDistanceCamera / camera.zoom;

With this camera, when a plane is added with Z=0, it should appear to-scale.

Now, how do you properly transfer a display component (MovieClip or Sprite) to the viewport? What we are talking about here is transferring something from the Flash coordinate system where the top-left corner is coordinate (0,0); right and down are positive, up and left are negative. We are transferring to a system where the center of the space is coordinate (0,0); up and right are positive, down and left are negative.

To complicate matters further, my characterization of 2D and 3D coordinate systems is not the whole store. You can in fact work in multiple coordinate systems. To illustrate this concept in 2D, we aren’t actually always working in a coordinate system where the top-left (TL) corner is (0,0). For example, by adding an onResize function to the stage that positions a Sprite called mainContainer in the exact center whenever the stage is resized, we can create a 2d coordinate system where the exact center is (0,0). If you add another Sprite called spriteContent as a child of mainContainer, it will appear in the center of the screen. To position spriteContent at the left of the screen you would do something like this:

spriteContent.x = - spriteContent.stage.stageWidth/2;

In both 2D and 3D, it is possible to create new coordinate systems by nesting instances of DisplayObject (in 2D) and by nesting instances of DisplayObject3D (in Papervision3D). For the rest of this article, I will only be using the default TL coordinate system for 2D. The reason for this is that in AS3, the DisplayObject class (and all subclasses of DisplayObject) has a getBounds() function which returns the position of the object relative to any other DisplayObject. Therefore, to get the position of a sprite in relation to your Papervision3D viewport you can do:

positionRect:Rectangle = sprite.getBounds( viewport );

At this point, remember that positionRect is in terms of the TL 2D coordinate system where the (0,0) coordinate is the TL corner of your Papervision3D viewport. In order to use this position information, we must perform some math to transfer the sprite to the 3D scene.

First, a pretty illustration to visualize what we’re trying to do:

In the illustration, each box represents a different coordinate system. I’ve just discussed the coordinate systems on the left and the right. The system in the middle is a 3D coordinate system where the origin is at the top-left corner of the viewport. In order to maintain this coordinate system, the DisplayObject3D used as the system’s container needs to be translated to the new origin every time the viewport is resized. In my application, I have a render function that loops through an array of containers and aligns each at this origin point:

  1. private function render():void {
  2. // static TL 3d containers
  3. for each (var cont:DisplayObject3D in static3dContainers) {
  4. cont.x = camera.x - viewport.viewportWidth / 2;
  5. cont.y = camera.y + viewport.viewportHeight / 2;
  6. }
  7.  
  8. renderer.renderScene(scene, camera, viewport);
  9. }

Without further ado, here is the set of utility functions that I’m currently using to transfer an object from one coordinate space to another:

  1. /**
  2. * Place a DisplayObject3D inside a new DisplayObject3D container,
  3. * and align the top-left corner of the do3d to the origin of the container;
  4. * apply the plane's orignal transformation to the container
  5. * @paramplane
  6. * @paramwwidth of do3d (or nested plane)
  7. * @paramhheight of do3d (or nested plane)
  8. * @returna DisplayObject3D container with plane as child
  9. */
  10. private function do3dAlignTL3d(do3d:DisplayObject3D, w:Number, h:Number):DisplayObject3D {
  11. var cont:DisplayObject3D = new DisplayObject3D();
  12. cont.x = do3d.x - w/2; //cont.x = plane.x - w / 2;
  13. cont.y = do3d.y + h/2; //cont.y = plane.y + h / 2;
  14. cont.z = do3d.z;
  15. cont.addChild(do3d);
  16. do3d.x = w / 2;
  17. do3d.y = -h / 2;
  18. do3d.z = 0;
  19. return cont;
  20. }
  21.  
  22. /**
  23. * Place a DisplayObject3D inside a new DisplayObject3D container,
  24. * and align the top-center of the do3d to the origin of the container;
  25. * apply the plane's orignal transformation to the container
  26. * @paramplane
  27. * @paramwwidth of do3d (or nested plane)
  28. * @paramhheight of do3d (or nested plane)
  29. * @returna DisplayObject3D container with plane as child
  30. */
  31. private function do3dAlignTC3d(do3d:DisplayObject3D, w:Number, h:Number):DisplayObject3D {
  32. var cont:DisplayObject3D = new DisplayObject3D();
  33. cont.x = do3d.x;
  34. cont.y = do3d.y + h/2;
  35. cont.z = do3d.z;
  36. cont.addChild(do3d);
  37. do3d.y = -h / 2;
  38. do3d.z = 0;
  39. return cont;
  40. }
  41.  
  42. /**
  43. * Transforms (from Normal Flash Coordinate system) a TL2d point to a StaticTL3d point.
  44. * @parampoint in TL2d coord system
  45. * @return point transformed to TLStatic3d coord system
  46. */
  47. private function TL2dtoTLStatic3d(point:Point):Point {
  48. return new Point(point.x, -point.y);
  49. }
  50.  
  51. /**
  52. * Transforms a TL3d point, to a StaticTL3d point.
  53. * @parampoint in TL3d coord system
  54. * @return point transformed to TLStatic3d coord system
  55. */
  56. private function TL3dtoTLStatic3d(point:Point):Point {
  57. return new Point(point.x + viewport.width / 2, point.y - viewport.height/2 );
  58. }
  59.  
  60. /**
  61. * Transforms a StaticTL3d point, to a TL3d point.
  62. * @parampoint in StaticTL3d coord system
  63. * @return point transformed to TL3d coord system
  64. */
  65. private function TLStatic3dtoTL3d(point:Point):Point {
  66. return new Point(point.x - viewport.width / 2, point.y + viewport.height/2 );
  67. }
  68.  
  69. /**
  70. * Transforms a TL2d point, to a TL3d point.
  71. * @parampoint in TL2d coord system
  72. * @return point transformed to TL3d coord system
  73. */
  74. private function TL2dtoTL3d(point:Point):Point {
  75. return new Point(point.x - viewport.width/2, -point.y + viewport.height/2 );
  76. }
  77. Download this code: /files/3dCoordFunctions.txt

I’m thinking about integrating some of this functionality into the upcoming AS3 version of the AFW Template. I’d love to get some feedback from people on this. Part 2 of this article series will provide a working example of when you would do this stuff. I know I’ve really glossed over some stuff in this article, so if you have any questions, please post a comment.

— Pickle

---

Comment

  1. Given what I have just read regarding positioning etc (which went way over my head!!) you might just be the man to help me. I currently have a 3d rotating cube. It is in a movieClip, the movie clip is animated on the root time line using the transition manger class (it zooms in, see website), however rather than zoom in from the centre and end in the centre, it zooms from top left to centre, I think this is something to do with the position of the hidden faces within the movieclip (the faces that make up the cube when rendered) as they are positioned at top left. However by moving the faces to center I simply end up moving the final cube off centre!! Make sense?? PLease, please help..I am confused..I only just started using flash and papervision. I can send you .fla if need. Thanks in advance. Ryan

    Ryan Gibson · Nov 19, 02:25 AM · #

  2. FYI — If you don’t understand this article, it’s probably my fault, not yours (it’s not my finer work).

    As for you problem, I can’t really tell if you’re creating this “zoom” effect by scaling the movieclip, or scaling/moving the 3d objects.

    Anyway, this might work:

    * create a viewport big enough to hold the final size of the cube.

    * Center the cube in the viewport (Make sure you can answer this question: is the origin of a cube it’s exact center?)

    * Center the movieclip

    * Create the zoom effect by scaling the cube, or translating along the z-axis.

    If that doesn’t help, you might try asking the same question on the PV3D mailing list. Most people there are better than me at this kind of thing.

    Pickle · Nov 20, 12:53 AM · #

Textile Help