Deconstructing Flash 10 3D

In the comments of this post, Emanuele Cipolloni put forth the idea that Flash 10 3D is simply done behind the scenes using drawTriangles. That was a interesting realization for me. Made sense, so I decided to test it out. Basically, how would I create a 3D engine like Flash 10 3D using drawTriangles?

1. I’d get the bounds of an object as a rectangle.

2. Create a bitmap of that width and height and draw the object into it.

3. Transform the four corner points of the bounds using standard 3D formulas.

4. Use these transformed points as vertices for triangle drawing, and the bitmap as a bitmap fill.

Here’s what I came up with. The image on the left (the only image you can see at first) is simply an imported bitmap in a movie clip sitting on stage. Click on it, and I do steps 1-4 repeatedly, drawing the triangles in the right part of the stage. The rotationY is increased on each frame.

[kml_flashembed movie=”http://www.bit-101.com/blog/wp-content/uploads/2008/11/rotation.swf” height=”280″ width=”600″ /]

Essentially, the same thing. The built in version is a lot smoother and cleaner looking, but I’m only using two triangles. And I think they are throwing some smoothing into the mix somewhere. (Perhaps even a slight blur???)

Here’s the code. Beware, it’s crappy, crappy, crappy code. Done on the train and bus on the way to work. Begs to be cleaned up, but I’ve proved my point and probably won’t do much more with it unless I can think of some practical use for it.

[as]var r:Rectangle = box.getBounds(box);

var bmpd:BitmapData = new BitmapData(r.width, r.height, true, 0);
//var bmp:Bitmap = addChild(new Bitmap(bmpd)) as Bitmap;

bmpd.draw(box, new Matrix(1, 0, 0, 1, -r.x, -r.y));
var rotY:Number = 0;
var scales:Array;
root.transform.perspectiveProjection.projectionCenter = new Point(200, 140);

var running:Boolean = false;
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
running = !running;
if(running)
{
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
else
{
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
function enterFrameHandler(event:Event):void
{
tick();
}

function tick():void
{
var ry:Number = -rotY * Math.PI / 180;
scales = new Array();
var p0:Point = rotateY(r.top, r.left, ry);
var p1:Point = rotateY(r.top, r.right, ry);
var p2:Point = rotateY(r.bottom, r.right, ry);
var p3:Point = rotateY(r.bottom, r.left, ry);

var vertices:Vector. = new Vector.();
vertices.push(p0.x + 600, p0.y + 140);
vertices.push(p1.x + 600, p1.y + 140);
vertices.push(p2.x + 600, p2.y + 140);
vertices.push(p3.x + 600, p3.y + 140);

var indices:Vector. = new Vector.();
indices.push(0, 1, 3);
indices.push(1, 2, 3);

var uvtData:Vector. = new Vector.();
uvtData.push(0, 0, scales[0]);
uvtData.push(0, 1, scales[1]);
uvtData.push(1, 1, scales[2]);
uvtData.push(1, 0, scales[3]);

graphics.clear();
graphics.beginBitmapFill(bmpd);
graphics.drawTriangles(vertices, indices, uvtData);

rotY += 3;
box.rotationY = rotY;
}

function rotateY(xpos:Number, ypos:Number, a:Number):Point
{
var x1:Number = xpos * Math.cos(a);
var z1:Number = xpos * Math.sin(a);
var foc:Number = root.transform.perspectiveProjection.focalLength;
var scale:Number = foc / (foc + z1);
scales.push(scale);
return new Point(x1 * scale, ypos * scale);
}[/as]

This entry was posted in ActionScript, Flash. Bookmark the permalink.

11 Responses to Deconstructing Flash 10 3D

  1. TK says:

    Excellent. Keith, thanks for the insight!
    – TK

  2. Dan says:

    Well yes, that’s roughly how 3D engines like Away3D and PaperVision work. 🙂
    The interesting thing about the new Flash 3D feature seems to me how they could be combined with those engines. Obviously Flash 3D planes have a *lot* better performance than the engine’s planes. However the engines have convenient tools like cameras, collada importers etc..
    So being able to use say PaperVision to display a collada model together with a big Flash 3D plane as the floor seems to make a lot of sense.

  3. kp says:

    Dan, yeah, I have a good idea how those engines work, and am aware of the pre-Flash 10 triangle drawing routines that are out there. I guess I just figured there was something deeper going on. Took the magic out of it for me. 🙂

  4. Iain says:

    and just think… it’s only been 8 YEARS since we got hardware accelerated 3D in Director 🙁

    You go Flash!

  5. kp says:

    Rober, I commented on your response. My money is still on triangles at some level. 🙂

    Iain, yay Director! How’s that working out for you? 😉

  6. There is a workaround for the rasterizing of vectors that occurs when they are given a z-value… basically you use the new 3D api to manipulate 3D points and project them to 2D values, then re-draw your vector in 2D. I posted an example here:

    http://www.dafishinsea.com/blog/2008/11/08/prevent-bitmapification/

    Still it would be much nicer if you didn’t have to do this. I think it would also be nice if you could say lineTo(x,y,z) as well.

  7. This is the link where originally I picked up the story, it is a blog post from Trevor McCauley and being a “shark” that swim in the Adobe ocean, he probably knows the real story: http://www.senocular.com/?id=2.44

  8. Chris says:

    I understand the basic idea of “m=F/(F+Z)”, but what kind of math is involved to give that “Objects are closer than they appear” effect of bending the zoom level? I would like to understant the very basic concept behind it.

  9. claude says:

    I think that understanding this example makes 90% of the path to CS4 3D.
    I have a question who disturbs me:
    scale info seems redundant to me .
    It is taken into account in the calculation of vertices x,y and also in uvtdata.
    If it is removed in uvtdata, it still works.
    If somebody replys me, i will have a better sleep next night. thank you
    Claude

  10. David says:

    Hi Keith, you can make it smooth by setting your bitmapFill up like this:
    beginBitmapFill(bmpd,null,true,true);

    I’m pretty sure the 2 3D rotating objects will appear identical.

    Cheers,

    David

Leave a Reply