You pass the width and the height of the bitmaps that you will be manipulating to the class constructor. The classes's centeral method, 'mapBitmapData', which we have seen in action on the previous page within the CubeMenu class, will then map, in other words distort, any such bitmap onto any quadrangle.
We have dealt with distorting bitmaps before. In our previous tutorial, Rotating Bitmaps in 3D, 3D Menu on a Cube in ActionScript 3, we have rotated and distorted bitmaps. Except we didn't have the perspective effect. Wihtout perspective, the projection of each face onto the view plane is always a parallelogram. Since any rectangle can be mapped onto a parallelogram using an affine mapping, the distortion was easily accomplished using a suitably chosen transform matrix for each of the bitmap faces.
In another of our tutorials: Loading Images and Spinning them in 3D in Terms of Custom AS3 Classes, we had distortion for perspective but in a particular case when rotation was only about the vertical axis. In that case we accomplished the distortion by cutting our bitmaps into thin vertical slices. The same approach could theoretically work in the general case. We could cut our bitmaps into tiny squares and distort each square. Except that the number of calculations becomes a bit large. Needlessly large, as there is a better way of accomplishing the same effect. To map a given bitmap onto an arbitrary quadrangle, the method described below can be used.
The basic idea consists of dividing our bitmap (more precisely, the associated BitmapData object) into a rectangular mesh of a given horizontal and vertical finess. Then, we divide the quadrangle onto which we want to map our bitmap into a corresponding mesh (the 'corresponding' mesh is obtained by an intuitively clear algorithm). Each rectangle we split into two triangles. For a 2 by 2 mesh the picture may look as follows:
Then we map each triangle onto the corresponding triangle using the 'beginBitmapFill' method of the graphics property of a display object. There are two reason why this works. First, any triangle can be mapped onto any triangle using an affine transformation (we have only three and not four vertices to contend with). Second, the 'beginBitmapFill' method has an optional parameter, a transform matrix, that you can pass to it. The matrix will transform the underlying BitmapData object including the portion of it that constitues our current triangle, and thus making it possible to transform a given triangular region of a Bitamdata object onto another triangular region. This method accomplishes good results with relatively small number of vertical and horizontal subdivisions. The default in our class is 5 by 5; in our CubeMenu, we use 10 by 10.
When you keep the above picture in your mind, the code for the class, which you will find in the zip package below, is very easy to follow.
The BitmapTransformer class has two public methods:
- The constructor evoked with the key word 'new': new BitmapTransformer(w:Number,h:Number,hdiv:int=5,vdiv:int=5)
The class constructor takes as parameters the width and the height (w and h)
of the BitmapData objects to which 'mapBitmapData' method will be applied.
One instance of BitmapTransformer can manipulate more than one BitmapData object,
but all of them have to have the same dimensions. The optional parameters,
hdiv, vdiv control the number of subdivisions of a rectangular bitmap
horizontally and vertically. Clearly, the higher values, the better picture.
In our cube menu we use 10, 10. 5 and 5 usually suffice for good results.
- instance.mapBitmapData(bd:BitmapData,topl:Point,topr:Point,botr:Point,botl:Point, cont:*):void The main method of the class, 'mapBitmapData', takes a BitmapData object of the dimensions set by the constructor and maps it onto an arbitrary quadrangle. The method does it by splitting each subrectangle of the distorted mesh into two triangles, and filling each triangle with the distorted portion of the bitmap via 'beginBitmapFill'. To use 'beginBitmapFill', we need to draw the distorted triangle in some target container in which the distorted bitmap will eventually reside. In our cube menu, each side resides in a Sprite, side0, side1,...,side5. So for each side we draw in the corresponding Sprite, side0, side1, and so on. The method takes the following parameters: a BitmapData object, Points that represent vertices of the distortion quadrangle in a specific order: top-left, top-right, bottom-right, bottom-left. The last parameter is the target object where drawing will take place. It can be a Sprite, a Shape, or a MovieClip, depending on your application. The reason why all of this works is that beginBitmapFill method takes a transform matrix as a possible parameter. Any three points can be mapped onto any three points using an affine transformation; that is, using a transform matrix. (Three yes, four no.) In the method's body, we calculate the right transform matrix to apply to our bitmap at each step based on the positions of the vertices of the original and of the distorted triangles.
- Download all 'fla', 'as', and 'jpg' files corresponding to this tutorial: menu3d.zip
Note: Although our class is written differently, it uses similar ideas to the earlier work by Andre Michelle of www.andre-michelle.com, Thomas Pfeifrer of www.flashsandy.org, and Ruben Swieringa of www.rubenswieringa.com.
On the next page: the ImageLoader class.