This website uses cookies to improve user experience. By using our website you consent to all cookies in accordance with our Cookie Policy. X

Basics of Stage3D part IV: working with shaders [expert]

Nowadays shaders have become an inharent part of the 3D graphics, it is basically impossible to display a picture without creating a single shading program. Which is why, this time around we will take a quick look on how to work with shaders by adding a simple lighting to our virtual world. In Flash shaders are created using a series of instructions called AGAL, which is a very different language from ActionSctipt that requires a complete walkthrough on its own for full understanding - those however are easy to find on the Internet so for now we will only focus on the most basic stuff needed for lighting to work.
Returning to the code from previous article first thing we should do is add new Fragment Shader:
private const FRAGMENT_SHADER_LIGHT:String = "dp3 ft1, fc2, v1 n"+
"neg ft1, ft1 n"+
"max ft1, ft1, fc0 n"+
"mul ft2, fc4, ft1 n"+
"mul ft2, ft2, fc3 n"+
"add oc, ft2, fc1";
and Vertex Shader:
private const VERTEX_SHADER_LIGHT:String = "mov vt0, va0n"+
"m44 op, vt0, vc0n"+
"nrm vt1.xyz, va0.xyzn"+
"mov vt1.w, va0.wn"+	
"mov v1, vt1n" +
"mov v2, va1";
(The code has been borrowed from http://blog.norbz.net/2012/04/stage3d-agal-from-scratch-part-vii-let-there-be-light)
Above shaders will render triangles by taking each pixel and applying a light or dark color depending on their relative position to the camera. Before we do anything with that code we still need to compile it into a special "program" using the AGALMiniAssembler:
vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER_LIGHT, false);
fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER_LIGHT, false);

programPairLight = renderContext.createProgram();
programPairLight.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
(Don't forget to prepare the programPairLight variable)
So, right now we have two shaders: the standardprogramPair and light generating programPairLight. Only thing left to do is to decide which one we will be using by calling the setProgram function in Context3D. To better showcase the difference lets create another Box3D (lets call it "box3DLight") and then add the following code before present() but after the "box3D" rendering:
boxMat = box3DLight.getMatrix3D();
boxMat.identity();
boxMat.append(rotMat);
boxMat.appendTranslation(posVec.x + 1, posVec.y + 1, posVec.z+1);

finalTransform.identity();
finalTransform.append(boxMat);
finalTransform.append(world);
finalTransform.append(camera);
finalTransform.append(projection);
	
renderContext.setProgram(programPairLight);
renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);

var p:Vector3D = camera.position.clone();
p.normalize();
var m3d:Matrix3D = finalTransform.clone();
m3d.invert();
p = m3d.transformVector(p);
p.normalize();
p.negate();

renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, Vector.<Number>([p.x,p.y,p.z,1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, Vector.<Number>([1, 1, 1, 1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, Vector.<Number>([0.6, 0.6, 0.6, 1]) );

box3DLight.render(renderContext);
That is a lot to take in, but what really matters to use happens after setProgram ("boxMat" and "finalTransform" operations are previously discussed transformations from 3D to 2D). The term "program" is not used here without a reason as just like any other program on our computer shaders do a mathematical operations on variables and constants, only by using a very low-leveled language which is also quite limited as it only support up to 8 variables at a time and they only in program's memory. That is not the case for constants, although they are still limited to a maximum of 8, they can be supplied from the outside using either setProgramConstantsFromVector or setProgramConstantsFromMatrix functions - precisely what the code above is doing, it supplies the camera position ("p" variable) and light's color.
When done, we should see something like this:
This application only uses two shaders but there is nothing stopping us from create much, much and then just switch them around using the setProgram function just as we are doing with the lineStyle function when working with Graphics.
Whole code is available here: Tutorial3D_light.zip.

Basics of Stage3D part III: working with rotation [expert]

Continuing from where we left off this time around I want to write a bit about rotations. Anyone who started working with Flash's Stage3D without previous experience in 3D will sooner or later run into unforeseen results with appendRotation() function. To make things even less inviting for newcomers, looking for support on this topic on the Internet most routes lead to one answer "you will need complex numbers for that (quaternions to be exact). Hmm? Okay, quaternions might be really useful, but if you don't need 3D calculations to solve 2D rotations, it would be logical to assume that you also don't need 4D numbers to solve 3D problems, especially when being a newcomer to this topic.
Well, it doesn't matter right now so lets just move along, starting with the code from the previous post: Tutorial3D-2.zip, lets remove all those cubes and only leave one box, as that is all we need. By the way you could also create two additional variables: one for a Vector3D and one for Matrix3D - it will come in handy.
Generally rotation can be added in two ways: locally and globally, or in other words by either taking into the account the existing transformation or not. Which ever way we choose it is worth noting that existing transformation also includes any translation that might be applied to the matrix, which is why it is a good idea to make our lives easier and the rotation in separate Matrix3D object, only appending it to the existing object when needed. So lets start with the global transformation and creating a separate matrix (if you haven't done so already):
private var rotMat:Matrix3D = new Matrix3D();
Next, we will modify the handling of keyboard arrow keys by including the rotation for rotMat:
switch(key&0x0000ff) {
	case 0x00000f: // forward
		rotMat.appendRotation(SPEED, Vector3D.X_AXIS);
		break;
	case 0x0000f0: // backward
		rotMat.appendRotation(-SPEED, Vector3D.X_AXIS);
		break;
}
switch(key&0x00ff00) {
	case 0x000f00: // left
		rotMat.appendRotation(SPEED, Vector3D.Y_AXIS);
		break;
	case 0x00f000: // right
		rotMat.appendRotation(-SPEED, Vector3D.Y_AXIS);
		break;
}
Now only thing left to do is add that rotation to the box3D object, before it has its final transformation created in finalTransform:
var boxMat:Matrix3D = box3D.getMatrix3D();
boxMat.identity();
boxMat.append(rotMat);
Which should result in this:
To make this example a bit more interesting and also check if the rotation is correctly applied, that is without being affected by the translation, lets use the Vector3D I've mentioned earlier. Like this:
private var posVec:Vector3D = new Vector3D();
Working with vectors should not bring any troubles at this stage, so I will only mention we will have to append it in proper place - right before the boxMat.append(rotMat); insert this line:
boxMat.appendTranslation(posVec.x, posVec.y, posVec.z);
In turn getting this:
So the global transformation isn't really that complicated, but there is still the matter of local rotation. Fortunately it is quite trivial and comes down to only finding the right axis:
switch(key&0x0000ff) {
	case 0x00000f: // forward
		rotMat.appendRotation(SPEED, rotMat.transformVector(Vector3D.X_AXIS));
		posVec.y += SPEED/50;
		break;
	case 0x0000f0: // backward
		rotMat.appendRotation(-SPEED, rotMat.transformVector(Vector3D.X_AXIS));
		posVec.y -= SPEED/50;
		break;
}
switch(key&0x00ff00) {
	case 0x000f00: // left
		rotMat.appendRotation(SPEED, rotMat.transformVector(Vector3D.Y_AXIS));
		posVec.x -= SPEED/50;
		break;
	case 0x00f000: // right
		rotMat.appendRotation(-SPEED, rotMat.transformVector(Vector3D.Y_AXIS));
		posVec.x += SPEED/50;
		break;
}
The X_AXIS vector will be transformed the object's local system, which will give us the exact location of the X axis we will use for the rotation. If done right at least one edge (depending on the direction of the rotation) will stay in same place (minus the translation):
Source code: Tutorial3D_rot.zip

Basics of Stage3D part II: camera movement and pointAt function [expert]

In the previous part of this article I wrote about extending an ActionScript3 Documentation example of 3D graphics to make it work with multiple objects in such way that every object could still be transformed separately. The example also has a bit about the matrix used for camera movement ("view"), which truth to be told did not work like it should because it was missing some basic set-up. And this is where it gets unpleasant for people that are new to the 3d graphics, as information available on the Internet about that set-up are rather mixed and mostly confusing, especially since proper camera movement will actually depend on 3D environment settings. That is why this part of Stage3D basics article will be dedicated to virtual camera and its construction, positioning and pointAt function usage.

Starting where we left of two weeks ago (source code from the guide's previous part: Tutorial3D.zip) is a good idea since we already have some basic camera stuff laid down. Although creating a separate class for the camera would probably be a good idea, the already existing matrix by it self is sufficient for now. In any case, the matrix is ready, however results of its transformation are somewhat… unpredictable, or at least not something we could expect (i.e. x+=-5 will actually result in movement to the right instead of left). This is related to a small omission on Adobe's part in the rendering function, which can cause some sleepless nights. What I am exactly talking about? Lets have a look how finalTransform matrix is created in render function:

finalTransform.identity();   
finalTransform.append(box.getMatrix3D());   
finalTransform.append(world);   
finalTransform.append(view);   
finalTransform.append(projection);
It doesn't seem like there is anything out of ordinary here, but if we stop for a moment and take a close look - lets say we want to move whole world to the left, and camera to the right, and for that we set world to x = -10 and view to x = 5. In perfect world there would 15 units of space between world and camera now, but of course it won't be the case in our application and everything because how finalTransform is being made. Lets take another look, world is moved by -10 and view by +5:
finalTransform.append(-10);   
finalTransform.append(5);
And what the result will be? Of course every object will be moved to the left (-10), then to the right (+5), in the end ending up on x = -5. True, it's very basic stuff, but it is very important to understand that it is not the camera that we are moving but the whole world around it. And this is that little detail thingy we were missing: we aren't moving the camera to the left, but whole to the right! Of course it could be done by just saying "simply move the camera in opposite direction", but … is bad idea - rotation and positioning could be done that way, but poinatAt function will be pretty much useless. Fortunately the solution is very simple, we just have to invert the view matrix:
var camera:Matrix3D = view.clone();  
camera.invert();
Yup, that is it. Whole render function will look like this:
private function render(event:Event):void {  
renderContext.clear(.3, .3, .3);  
var camera:Matrix3D = view.clone();  
camera.invert();  
for each(var box:Box3D in vBox) {  
finalTransform.identity();  
finalTransform.append(box.getMatrix3D());  
finalTransform.append(world);  
finalTransform.append(camera);  
finalTransform.append(projection);  
renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);  
box.render(renderContext);  
}  
renderContext.present();  
}
Those two lines of code are enough to make our camera work as intended, view translation to the left will in face move it to the left and what is most important pointAt will actually work, which I will explain shortly. Right now it is best to test out the camera movement and hopefully get something like this:
(Movement on the arrow keys)
Just in case you can always use the code available at the very and of this post.
Well, now that everything is properly set up, it is time to use pointAt function. And to think that without the knowledge about view matrix inversion prior to finalTransform construction, pointAt would never work as intended! This however doesn't solve all of the problems, there is still a question of parameters used by pointAt, because obviously default ones won't work. Those parameters are:
1. Target's position - matrix will be turned to face that point.
2. Facing direction - in other words "front" direction without any transformations; for camera it always will be Vector3D(0, 0, -1).
3. Up direction - which is the "rising" direction; by default Vector3D(0, -1, 0).
That should be enough. Properly working pointAt should look like this:
Unfortunately, translation won't be affect by the pointAt transformation - lets leave that for another day.
Source code: Tutorial3D-2.zip

Basics of Stage3D part I: displaying multiple objects [advanced]

Two weeks ago I've wrote about support in Flash Professional for Flash Player 11 while mentioning about using the example of 3D rendering from the documentation ( http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#includeExamplesSummary). However using a pre-made example is one thing and understanding it is whole different matter - that is why this time around I will write about Stage3D (as Adobe like to call there 3D acceleration in Flash).
Just in case the whole code from that example (along with additional libraries needed to run it) is available here: Context3DExample.zip

In the first part of this article I will write a bit about creating and displaying multiple objects while still allowing for separate transformations on each of them. To make this simpler I suggest making a class for 3D cubes, using the already existing code (from the example in the documentation) responsible for creating geometry: IndexBuffer and VertexBuffer. Such class might look like this:

private var vbBuffer:VertexBuffer3D;
private var ibIndex:IndexBuffer3D;
private var m3D:Matrix3D;
private var nCount:int;

public function Box3D(context:Context3D) {
			
            var triangles:Vector.<uint> = Vector.<uint>([...]);
	ibIndex = context.createIndexBuffer(triangles.length);
	ibIndex.uploadFromVector(triangles, 0, triangles.length);
			
	const dataPerVertex:int = 6;
	var vertexData:Vector.<Number> = Vector.<Number>([...]);
	vbBuffer = context.createVertexBuffer(vertexData.length / dataPerVertex, dataPerVertex);
	vbBuffer.uploadFromVector(vertexData, 0, vertexData.length / dataPerVertex);
					
	m3D = new Matrix3D();
	nCount = 12;	
}
				
public function render(context:Context3D):void {
	context.setVertexBufferAt( 0, vbBuffer, 0, Context3DVertexBufferFormat.FLOAT_3 );
	context.setVertexBufferAt( 1, vbBuffer, 3, Context3DVertexBufferFormat.FLOAT_3 );
	context.drawTriangles(ibIndex, 0, nCount);
}
				
public function getMatrix3D():Matrix3D {return m3D;}
Nothing especially complicated here - just remember that m3D matrix will hold the transformation (position, scale) so it is best to build the cube around 0,0,0 coordinates and then later move it with appendTranslation function. Also, one more thing worth pointing out is that setVertexBufferAt is used to set which VertexBuffer will be used to draw triangles (drawTriangles function) and it is called twice, because first it points out the vertexes and next the colours.
So, creating a class for cubes is pretty simple, the problems start when we decide to display them. Assuming all those boxes are stored in vBox vector, the render function used in Context3Dexample needs to be changed to this:
private function render(event:Event):void {
	renderContext.clear(.3, .3, .3);
			
	for each(var box:Box3D in vBox) {
		finalTransform.identity();
		finalTransform.append(box.getMatrix3D());
		finalTransform.append(world);
		finalTransform.append(view);
		finalTransform.append(projection);
	renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);
				
		box.render(renderContext);
	}
			
	renderContext.present();
}
Here it gets a bit more interesting thanks to the two most important things: setProgramConstantsFromMatrix function and finalTransform matrix construction. With setProgramConstantsFromMatrix function we set the matrix (transformations) for the rendered object and it cannot be skipped (or at least it won't make any sense to do this, since you won't be able to see anything on the screen), while finalTransform will be used to create that matrix.
This stuff will probably be pretty confusing to people not familiar with 3D graphics, but the basic idea to remember is that every object (cubs in our case) is actually never modified during the runtime. In other words we create our cube, define its triangles and set some colours, but by it self the cube does not have specific position in the system - we use matrices for that. But the thing is matrices also don't do anything special by themselves, until we call the setProgramConstantsFromMatrix function to combine them. Note however this works somewhat like a filter - "Program" (external code used for rendering process) takes the geometry and passes it through the matrix, displaying results to the screen while not modifying any variables.
Now as for the finalTransform - we start each animation frame by resetting this matrix (identity function) to make sure it contains absolutely no transformations. This is very important for append operations, because existing transformations affect the results, so for example a translation by 10 units to the left followed by a 90 degrees turn will place an object in different spot than by starting with 90 degrees turn and then following it with a translation to the left by 10 units. Just like in the real world the direction we will take on "move to the left" command will be different depending on the side we are already facing. In any case it is best to experiment with this, while remembering that order of matrices addition is extremely important and that is why finalTransform is created by appending other matrices in this particular order: world, view (camera) and projection (defines how we see the world).

As usual the whole code is packed and ready to download from here: Tutorial3D.zip

3D engine for beginners III [advanced]

In previous parts of this guide dedicated to creating a very bare-bone 3D engine I've mentioned how to display the illusion of perspective and how to control camera movements. This time we will handle rotation. It will be the final chapter of this guide since any further immersion in the topic will require knowledge of more advanced issues, which are better to handle with a "proper" engine (like for example Alternativa3D or Away3D). Not to mention the newest Flash version with number 11 was extended with 3D acceleration, which basically means ActionScript can now be used to create a proper 3D game in likes of Crysis or Gears of War (especially since Adobe presented Unreal Engine 3 working in Flash).
But that is a different story, for now let's focus on that rotation.

1. However before we do anything create two new constants and one variable:

const SPEED:Number = 20;
const ROTATION:Number = Math.PI/16;
var a:Number = 0;
SPEED will be the speed of camera movement, while ROTATION will be the speed of rotation. The a variable will be used to store current rotation angle.

2. Do you remember which part of the code was responsible for transformation from 3D into 2D? We will modify it to include turning of the camera:

var nx:Number;
var nz:Number;
var rx:Number;
var rz:Number;
var bx2:Array = new Array(bx.length);
var by2:Array = new Array(by.length);
var i:Number = 0;
while(i<bz.length) {
	nx = cx-bx[i];
	nz = cz-bz[i];
	rx = cx+( nx*Math.cos(-a) )-( nz*Math.sin(-a) );
	rz = cz+( nx*Math.sin(-a) )+( nz*Math.cos(-a) );
	bx2[i] = vw+( ((cx-rx) / (cz-rz) )*vw);
	by2[i] = vh+( ((cy-by[i]) / (cz-rz) )*vh);
	++i;
}
Not much of a change, I've added additional variables for better clarity and the rotation formula (along the Y axis, which is exactly the same as in any other FPS game). You will probably find nothing shocking here if math is no stranger to you. Long story short for each point we calculate its translation (in case of x: nx = cx-bx[i];), which is rotated (( nx*Math.cos(-a) )-( nz*Math.sin(-a) );) and put back into position ( rx = cx+...). Newly created point is then transformed from 3D into 2D using the method we are already familiar with.

3. Now the only thing left to do is to modify the camera movement to include an option for turning around. We will accomplish this by changing how key presses are translated into actions by the switch(key) command. It is also a good opportunity to optimise the code - replace it with this:

switch(key&0x0000ff) {
case 0x00000f:
		cz += Math.cos(-a)*SPEED;
		cx += Math.sin(-a)*SPEED;
	break;
	case 0x0000f0:
		cz -= Math.cos(-a)*SPEED;
		cx -= Math.sin(-a)*SPEED;
		break;
}
switch(key&0x00ff00) {
	case 0x000f00:
	a += ROTATION;
		break;
	case 0x00f000:
		a -= ROTATION;
		break;
}
In the previous version switch was used to check every possible combination within the key variable, while now switch extracts only a part of key - first one gets up and down keys (0x000f0f&0x0000ff will result in 0x00000f which means the information about the left key, 0x000f00, is omitted), while the second one gets left and right.
Okay, so now we handle the key presses better, but what is actually happening here? Let's start from the end: left and right keys are used to turn (change the angle), while up and down are used for forward and backward movement. Of course since now we have to deal with rotation, the definition of "forward" constantly changes and that is why we need to use cos and sin functions which in this case calculate the translation vector based on the angle.

4. Time to test this out. Final result should look more or less like this:
(Movement with arrow keys)


And the whole class is available here: box3d_rot.zip

3D engine for beginners II [advanced]

In the previous part of this guide I've described how to create a basic 3D engine capable of creating a three-dimensional cube, however only in a static image which admittedly wasn't very impressive. That is why this time around we will expend it by adding camera movement.
Lets start by creating two keyboard events within the constructor of our box3d class:
stage.addEventListener(KeyboardEvent.KEY_DOWN,eventKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP,eventKeyUp);
(Don't forget to import flash.events.KeyboardEvent!)
Those listener were attached to the stage because any other object would require changes to the "focus" value which we don't need nor want to as stage is completely sufficient. Anyway, we need two functions to handle those events:
private function eventKeyDown(e:KeyboardEvent):void {

}
private function eventKeyUp(e:KeyboardEvent):void {

}
Before we continue I would suggest checking if Flash correctly handles key presses with trace(e.keyCode); put into one of the listeners.
In the next step we will work on the actual key presses capture and translation into camera movement, which even though can be done this way:
if(e.keyCode == Keyboard.LEFT) cx -= 20;
else if(e.keyCode == Keyboard.RIGHT) cx += 20;
We won't bother with it as it results in a jerky and limiting animation. Instead we will implement a bit more advanced solution based on bitwise operations - but before that, we need a new variable in our class: var key:Number = 0; which we will use in the following way:
private function eventKeyDown(e:KeyboardEvent):void {
	switch(e.keyCode) {
		case Keyboard.UP:
			key = key | 0x00000f;
			break;
		case Keyboard.DOWN:
			key = key | 0x0000f0;
			break;
		case Keyboard.LEFT:
			key = key | 0x000f00;
			break;
		case Keyboard.RIGHT:
			key = key | 0x00f000;
			break;
	}
}
(Don't forget to import flash.ui.Keyboard!)
OK, I admit, this might look a bit confusing at first (especially for people that are not familiar with bitwise operations or hexadecimal numbers), but in few moments everything will become clear. This code sets key variable to certain values depending on the pressed keys, so for example holding "up" and "left" keys will result with "0x000f0f" value being set to key. It is the outcome of bitwise operations done on hexadecimal numbers - by taking "0x000f00" and "0x00000f" and applying | (OR) to them we get "0x000f0f". That line symbol | represents a bitwise "or" and in some situations works a bit like addition - 100|1 will return 101 (more information can be found in ActionScript's documentation). But anyway, we don't really care about that, it is easier to think of key as a kind of light-board where 0 = light is off (a key is not pressed) and f = light is on (a key is pressed). 0x00ffff = all keys pressed, 0x00f00f0 = only up and right, 0x000000 = no keys are pressed etc.
However eventKeyDown only servers to switch the light on. For the full picture we need the opposite function:
private function eventKeyUp(e:KeyboardEvent):void {
	switch(e.keyCode) {
		case Keyboard.UP:
			key = key ^ 0x00000f;
			break;
		case Keyboard.DOWN:
			key = key ^ 0x0000f0;
			break;
		case Keyboard.LEFT:
			key = key ^ 0x000f00;
			break;
		case Keyboard.RIGHT:
			key = key ^ 0x00f000;
			break;
	}
}
In this case the bitwise ^ (XOR) acts a little like subtraction, where "0x000f0f ^ 0x00000f" will result in "0x000f00".
Well, I think the worst is behind us and now we only need to convert key value into the proper camera movement. To achieve this lets had back into the render function (which we created in the previous part of this guide) and before the graphics.clear(); line add the following code:
switch(key) {
	case 0x00000f: // ^
		cz += 20;
	break;
	case 0x0000f0: // v
		cz -= 20;
		break;
	case 0x000f00: // <
		cx -= 20;
		break;
	case 0x00f000: // >
		cx += 20;
		break;
}
Almost over. Compile the application and make sure everything is working as intended. If you had tried operating the camera straight from the eventKeyDown or eventKeyUp functions you will surely notice an improvement in animation (if not I suggest going back and comparing it to the current solution).
In any case, there is still one more little thing - moving diagonally. Using two keys at once will set key to a number that the switch(key) does not handle, so to finish things off we need to add four more clauses:
	case 0x000f0f:
		cz += 20;
		cx -= 20;
		break;
	case 0x00f00f:
		cz += 20;
		cx += 20;
		break;
	case 0x000ff0:
		cz -= 20;
		cx -= 20;
		break;
	case 0x00f0f0:
		cz -= 20;
		cx += 20;
		break;
That is it. Just in case you can download the whole class from here: box3d_2.zip
And the application itself is presented here: box3d.swf

3D engine for beginners I [basic]

If anyone ever wanted to create their own 3D engine in Flash (and not only) not sure of what to expect, surely they met them self with a lot of mathematics and advanced programming routines during the search for information. It might be a little disheartening, but the truth is you don't need all that fancy stuff to create something basic, like for example: an application that displays a 3D cube.
That is why this post will be dedicated to people who are completely new to the topic of creating a 3D engine (which can display that 3D cube).

All of it will fit into one class - for best effect it should be the document's class. In case you are wondering the "Document Class" can be created on properties tab.

(Enter the class name and press the pencil icon).
After creating the new class we should prepare all the variables that we will need in our little engine. Lets start with the camera position:

var cx:Number = 0;
var cy:Number = 0;
var cz:Number = 0;
Nothing fancy here - camera position will determinate what we see on the screen. I think it is best if I take this moment to explain what effects will the movement have (since we don't have to stick to mathematics' rules and switch stuff like up and down around):
cx is the left(-) or the right(+) movement.
cy is the up(-) or down(+) movement.
cz is the backward/"away from the screen"(-) or forward/"to the screen"(+) movement.
OK, now we need a point where the perspective will focus too:
var vx:Number = stage.stageWidth/2;
var vy:Number = stage.stageHeight/2;
The vanishing point is usually in the middle of the screen and the easiest way to get is to divide the screen in half. Of course nothing stops us from defining a different point (but this will required some modifications to the transformation equation I will talk about later). By the way, the 0,0 point is located on the upper left corner of the screen and everything to the right or down from it consists of positive numbers only.
The last set of variables defines the 3D cube:
var bx:Array = [-50, 50, 50, -50, -50, 50, 50, -50];
var by:Array = [-50, -50, 50, 50, -50, -50, 50, 50];
var bz:Array = [200, 200, 200, 200, 300, 300, 300, 300];
Those three arrays hold points that describe our three-dimensional box. For example if we take bx[0],by[0],by[0] we get the upper left corner of the front facing wall.
Now that we have all the needed parameters we can proceed to create our 3D rendering function. To achieve the smoothest animation it's best to connect it with the ENTER_FRAME event - that is why we will write the following line within the constructor:
addEventListener(Event.ENTER_FRAME,render);
Don't forget to import flash.events.Event! As you can see I named my function "render" and for now it looks like this:
private function render(e:Event):void {
 graphics.clear();
}
Just a standard function definition. We will need the graphics.clear(); line because the process of rendering always looks the same - when entering a new animation frame we remove the old graphics and insert new ones (inserting will be explain in a moment). Anyway, it is time to move on onto the most important part of the application, which is the transformation from 3D into the 2D (because currently no monitor screen can handle anything else than 2D image):
var bx2:Array = new Array(bx.length);
var by2:Array = new Array(by.length);
var i:Number = 0;
while (i<bx.length) {
	bx2[i] = vx+(((cx-bx[i])/(cz-bz[i]))*vx);
	by2[i] = vy+(((cy-by[i])/(cz-bz[i]))*vy);
	++i;
}
So first things first:
var bx2:Array = new Array(bx.length);
var by2:Array = new Array(by.length);
Those arrays will hold all 2D points after the transformation.
Then, in the loop, we have:
bx2[i] = vx+(((cx-bx[i])/(cz-bz[i]))*vx);
by2[i] = vy+(((cy-by[i])/(cz-bz[i]))*vy);
Which is the code responsible for that transformation. Quite frankly you don't need to understand it to use it efficiently, so feel free to skip the following explanation and perhaps return to it later:
cx-bx[i] - we are subtracting a point's coordinate from the camera's position to retrieve the translation vector. It's a standard method when dealing with transformations, because we don't care where the point is, but rather how far it is from the camera - relative information like that is easier to carry between spaces.
cz-bz[i] - here we calculate the distance from the camera. We need this value to create the illusion of perspective.
(cx-bx[i])/(cz-bz[i]) - and here we create that illusion of perspective, so objects will seem to be getting smaller when moving further away.
*vx - after we are done with the perspective it is time to include the viewing angle. Only not in the scope we would expect it to be as bigger number stretch the image (make the angle smaller). Clearly it has nothing to do with the vanishing point's vx that we defined but it just so happens that the best value here is also the half of the screen.
vx+ - and finally our vanishing point.

Now that we have all our points in 2D, it is time to display them:

//drawing:
graphics.lineStyle(1, 0xffffff, 100);
//front face:
graphics.moveTo(bx2[0], by2[0]);
graphics.lineTo(bx2[1], by2[1]);
graphics.lineTo(bx2[2], by2[2]);
graphics.lineTo(bx2[3], by2[3]);
graphics.lineTo(bx2[0], by2[0]);
//back face:
graphics.moveTo(bx2[4], by2[4]);
graphics.lineTo(bx2[5], by2[5]);
graphics.lineTo(bx2[6], by2[6]);
graphics.lineTo(bx2[7], by2[7]);
graphics.lineTo(bx2[4], by2[4]);
//connecting lines:
graphics.moveTo(bx2[0], by2[0]);
graphics.lineTo(bx2[4], by2[4]);
graphics.moveTo(bx2[1], by2[1]);
graphics.lineTo(bx2[5], by2[5]);
graphics.moveTo(bx2[2], by2[2]);
graphics.lineTo(bx2[6], by2[6]);
graphics.moveTo(bx2[3], by2[3]);
graphics.lineTo(bx2[7], by2[7]);
The graphics.lineStyle(1, 0xffffff, 100); part describes the style of the drawing line. First value is the width (in pixels), second is the color, and the last one is transparency.
Next we have the drawing itself, starting from the front face:

Then we add the back face:

And connect them at the end:

(Of course in the application this will be done instantly).
If everything went smoothing an image of 3D cube should be visible on the screen (just like in the last picture).
In the next step we should implement some kind of camera movement but this will be explained later posts.

Whole source code is available here: box3d.zip