Promethe’s Blog Web, RIAs and chocolate spaghettis…

22Sep/080

DirectFlex : introducing the API layer

Remember remember... (the fifth of november ?!) what is the main point about DirectFlex ? It "looks like" DirectX (at least the API layer does). But what does that mean exactly ? It means that a lot of things are the same. Not only design concepts but also prototypes and names. But because I'm quite sure that most of the flash developpers never did any DirectX programmation, I think it's worth a little explanation. I won't talk very precisely about class definitions and prototypes but I will just try to explain the global logic of DirectFlex' API layer and how to use it to draw a simple cube.

The Vertex class

3D graphics is about drawing shapes in a 3D space. To describe those shapes, we use vertices. Considering a triangle, each corner is a vertex and will be represented by a Vertex object. Therefore, Vertex objects are basically a set of 3D coordinates that stands for the actual position of the Vertex in a 3D space. Vertex objects also contain other data such as texture coordinates, a color and a normal but we won't complicate things any further for the moment. The following code sample creates a Vertex:

// create a vertex in (x = 42, y = 23, z = 11)
var v : Vertex = new Vertex(42, 23, 11);

The VertexBuffer class

VertexBuffer objects are arrays of Vertex objects. Their implementation is actually quite more complicated because they have some internal optimizations. Still, VertexBuffer objects allow you to create a 3D shape using Vertex objects. Considering that a shape is made of triangles, the corresponding VertexBuffer object will contain multiple Vertex objects where each set of 3 Vertex is a triangle.

Lets take a cube for example. The cube has 6 faces, but each face is a square. As we can draw only triangles, each face will be made of 2 triangles. Our cube is made of 6 x 2 = 12 triangles and each triangle needs 3 vertices to be fully described. In the end, the VertexBuffer of a cube will contain 12 * 3 = 36 Vertex. Each set of 3 vertices describing the triangle to draw. The following code sample create a cube's VertexBuffer:

// create a cube VertexBuffer
var cube = new VertexBuffer(new Vertex(0.5, -0.5, 0.5), new Vertex(0.5, -0.5, -0.5), new Vertex(-0.5, -0.5, -0.5),
                            new Vertex(0.5, -0.5, 0.5), new Vertex(-0.5, -0.5, -0.5), new Vertex(-0.5, -0.5, 0.5),
                            new Vertex(-0.5, 0.5, -0.5), new Vertex(0.5, 0.5, -0.5), new Vertex(0.5, 0.5, 0.5),
                            new Vertex(-0.5, 0.5, 0.5), new Vertex(-0.5, 0.5, -0.5), new Vertex(0.5, 0.5, 0.5),
                            new Vertex(-0.5, -0.5, 0.5), new Vertex(0.5, -0.5, 0.5), new Vertex(-0.5, -0.5, 0.5),
                            new Vertex(-0.5, -0.5, 0.5), new Vertex(-0.5, 0.5, 0.5), new Vertex(0.5, 0.5, 0.5),
                            new Vertex(-0.5, 0.5, -0.5), new Vertex(-0.5, 0.5, 0.5), new Vertex(-0.5, -0.5, 0.5),
                            new Vertex(-0.5, -0.5, 0.5), new Vertex(-0.5, -0.5, -0.5), new Vertex(-0.5, 0.5, -0.5),
                            new Vertex(-0.5, -0.5, -0.5), new Vertex(0.5, -0.5, -0.5), new Vertex(-0.5, 0.5, -0.5),
                            new Vertex(-0.5, 0.5, -0.5), new Vertex(0.5, -0.5, -0.5), new Vertex(0.5, 0.5, -0.5),
                            new Vertex(0.5, 0.5, 0.5), new Vertex(0.5, -0.5, -0.5), new Vertex(0.5, -0.5, 0.5),
                            new Vertex(0.5, -0.5, -0.5), new Vertex(0.5, 0.5, 0.5), new Vertex(0.5, 0.5, -0.5));

The IndexBuffer class

With the previous example, it is quite obvious that a VertexBuffer alone is not the best way to describe 3D geometry. When you look at a cube, the most intuitive idea is that it is actually made of 8 vertices, not 36 ! Considering the resulting VertexBuffer, it is easy to see that vertices are created more than once with the exact same values because of the '3 vertices per triangle' logic. That is why IndexBuffer are handy : instead of describing the drawing order and the geometry in the same (Vertex) buffer, we separate them into a VertexBuffer (the geometry) and an IndexBuffer (the drawing order). Thus, an IndexBuffer is just an array of integers where each value references a cell of the VertexBuffer to describe how each Vertex has to be used. Our cube can then be represented as following :

// create the cube's VertexBuffer
var vb = new VertexBuffer(new Vertex(0.5, -0.5, 0.5),
                          new Vertex(0.5, -0.5, -0.5),
                          new Vertex(-0.5, -0.5, -0.5),
                          new Vertex(-0.5, -0.5, 0.5),
                          new Vertex(0.5, 0.5, 0.5),
                          new Vertex(0.5, 0.5, -0.5),
                          new Vertex(-0.5, 0.5, -0.5),
                          new Vertex(-0.5, 0.5, 0.5));
// create the cube's IndexBuffer
var ib = new IndexBuffer(0, 1, 2, 0, 2, 3,
                         6, 5, 4, 7, 6, 4,
                         4, 0, 3, 3, 7, 4,
                         6, 7, 3, 3, 2, 6,
                         2, 1, 6, 6, 1, 5,
                         4, 1, 0, 1, 4, 5);

The Device class

In DirectX, the Device (also called "context device") is an object that abtracts the graphics hardware. When you want to draw vertices, you give them to the Device and it uses its current configuration to render them. In DirectFlex, the Device class implements the Facade design pattern and is designed to abstract the whole "core" layer. The good thing is that the Device is a major abstraction feature : what is happening to the data stays hidden inside the core layer. If we rewrite the entire core layer, nothing will have to be changed inside the API layer! A Device object is created for each 3D display. The Device can be used in order to :

  • set transformations (world, view, projection)
  • set materials (color fills, textures)
  • draw primitives (VertexBuffer)
  • draw indexed primitives (VertexBuffer + IndexBuffer)
  • render the scene

It is quite obvious that anything you will try to display will have to interact with the Device... Lets take our favorite cube : it's time to draw it !

// create a device and render the cube's VertexBuffer
var device : Device = new Device(canvas);
 
// draw the VertexBuffer using the VertexBuffer : the first argument describe what kind of primitives 
// we are drawing
device.drawIndexedPrimitives(PrimitiveType.TRIANGLE_LIST, vb, ib);

The first argument of the Device constructor is a Canvas object. This object will be used by DirectFlex to know "where" and "how" it has to draw. This is what you get in the end (I added a transformation to show it's a real cube):

This screenshot was made using the new Flash 10 version of DirectFlex ! This is why it looks that crappy : lights and a lot of stuff are not handled yet.

What's next ?
Next time I will introduce 3D transformations (such as translation, rotation and scaling) and materials.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.