Well, if you're going to do it manually like you're doing right now, you're going to have a bad time.
What you need is actually two matrices: a projection matrix and a camera matrix. First you transform your triangles from world space (in the scene) to camera space (around the camera) and then from camera space to screen space (on the screen).
The first transformation is done using a camera matrix, while the second is done using a projection matrix.
Now, I'm going to assume you don't care about correct perspective, so I'll give you a matrix for an orthographic projection. It looks like there is no matrix class in standard AS3, so I'll give you a generic answer.
First, you will need to store 16 floating point numbers. They are aligned in the following configuration:
[ X1 Y1 Z1 WX ]
[ X2 Y2 Z2 WY ]
[ X3 Y3 Z3 WZ ]
[ TX TY TZ CZ ]
So that's four rows of four floating point numbers.
Let's build an identity matrix, which is a special matrix that doesn't change the input vector.
[ 1.0 0.0 0.0 0.0 ]
[ 0.0 1.0 0.0 0.0 ]
[ 0.0 0.0 1.0 0.0 ]
[ 0.0 0.0 0.0 1.0 ]
Next, we will need to know the viewport, the dimensions of the screen.
view_x = 0;
view_y = 0;
view_w = GetScreenWidth();
view_h = GetScreenHeight();
And also a value for z-near and z-far. But we can set that to -1 and 1, because we're not interested in perspective projection.
z_near = -1;
z_far = 1;
Now we can fill our matrix:
Identity();
values[X1] = 2.f / (view_w - view_x);
values[Y2] = 2.f / (view_y - view_h);
values[Z3] = -2.f / (z_far - z_near);
values[TX] = -(view_w + view_x) / (view_w - view_x);
values[TY] = -(view_y + view_h) / (view_y - view_h);
values[TZ] = -(z_far + z_near) / (z_far - z_near);
But was just the first step! Now we have a projection matrix, but not a camera matrix.
Again, start with an identity matrix:
Identity();
We'll need three vectors: the camera position, the camera target and a world up vector. World up is usually (0.0, 1.0, 0.0)
, so we'll keep it that way for now.
Vec3 camera_pos = GetCameraPosition();
Vec3 camera_target = GetCameraTarget();
Vec3 world_up = Vec3(0.0, 1.0, 0.0);
Now we can build three normalized vectors: f
, s
and u
.
Vec3 f = (camera_target - camera_position).GetNormalized();
Vec3 u = world_up.GetNormalized();
Vec3 s = f.GetCrossProduct(u).Normalize();
u = s.GetCrossProduct(f);
And now we can fill our camera matrix:
values[X1] = s.x;
values[X2] = s.y;
values[X3] = s.z;
values[WX] = 0.0;
values[Y1] = u.x;
values[Y2] = u.y;
values[Y3] = u.z;
values[WY] = 0.0;
values[Z1] = -f.x;
values[Z2] = -f.y;
values[Z3] = -f.z;
values[WZ] = 0.0;
values[TX] = -u.GetDotProduct(camera_position);
values[TY] = -s.GetDotProduct(camera_position);
values[TZ] = f.GetDotProduct(camera_position);
values[CZ] = 1.0;
Once you have these matrices, it becomes a piece of cake to transform coordinates to screen space:
Vec3 homogenized_position = camera_projection * camera_transform * src_position;
Here, we're transform the incoming src_position
with the matrices and the matrices with each other. Multiplying matrices:
Mat4x4 Mat4x4::operator * (Mat4x4 a_Other)
{
float a0, a1, a2, a3;
Mat4x4 result;
a0 = a_Other.values[X1];
a1 = a_Other.values[Y1];
a2 = a_Other.values[Z1];
a3 = a_Other.values[WX];
result.values[X1] = (a0 * values[X1]) + (a1 * values[X2]) + (a2 * values[X3]) + (a3 * values[TX]);
result.values[Y1] = (a0 * values[Y1]) + (a1 * values[Y2]) + (a2 * values[Y3]) + (a3 * values[TY]);
result.values[Z1] = (a0 * values[Z1]) + (a1 * values[Z2]) + (a2 * values[Z3]) + (a3 * values[TZ]);
result.values[WX] = (a0 * values[WX]) + (a1 * values[WY]) + (a2 * values[WZ]) + (a3 * values[CZ]);
a0 = a_Other.values[X2];
a1 = a_Other.values[Y2];
a2 = a_Other.values[Z2];
a3 = a_Other.values[WY];
result.values[X2] = (a0 * values[X1]) + (a1 * values[X2]) + (a2 * values[X3]) + (a3 * values[TX]);
result.values[Y2] = (a0 * values[Y1]) + (a1 * values[Y2]) + (a2 * values[Y3]) + (a3 * values[TY]);
result.values[Z2] = (a0 * values[Z1]) + (a1 * values[Z2]) + (a2 * values[Z3]) + (a3 * values[TZ]);
result.values[WY] = (a0 * values[WX]) + (a1 * values[WY]) + (a2 * values[WZ]) + (a3 * values[CZ]);
a0 = a_Other.values[X3];
a1 = a_Other.values[Y3];
a2 = a_Other.values[Z3];
a3 = a_Other.values[WZ];
result.values[X3] = (a0 * values[X1]) + (a1 * values[X2]) + (a2 * values[X3]) + (a3 * values[TX]);
result.values[Y3] = (a0 * values[Y1]) + (a1 * values[Y2]) + (a2 * values[Y3]) + (a3 * values[TY]);
result.values[Z3] = (a0 * values[Z1]) + (a1 * values[Z2]) + (a2 * values[Z3]) + (a3 * values[TZ]);
result.values[WZ] = (a0 * values[WX]) + (a1 * values[WY]) + (a2 * values[WZ]) + (a3 * values[CZ]);
a0 = a_Other.values[TX];
a1 = a_Other.values[TY];
a2 = a_Other.values[TZ];
a3 = a_Other.values[CZ];
result.values[TX] = (a0 * values[X1]) + (a1 * values[X2]) + (a2 * values[X3]) + (a3 * values[TX]);
result.values[TY] = (a0 * values[Y1]) + (a1 * values[Y2]) + (a2 * values[Y3]) + (a3 * values[TY]);
result.values[TZ] = (a0 * values[Z1]) + (a1 * values[Z2]) + (a2 * values[Z3]) + (a3 * values[TZ]);
result.values[CZ] = (a0 * values[WX]) + (a1 * values[WY]) + (a2 * values[WZ]) + (a3 * values[CZ]);
return result;
}
And multiplying a Vec3 with a matrix:
Vec3 Mat4x4::operator * (Vec3 a_Vector)
{
Vec3 result;
float x = (a_Vector.x * values[X1]) + (a_Vector.y * values[X2]) + (a_Vector.z * values[X3]) + values[TX];
float y = (a_Vector.x * values[Y1]) + (a_Vector.y * values[Y2]) + (a_Vector.z * values[Y3]) + values[TY];
float z = (a_Vector.x * values[Z1]) + (a_Vector.y * values[Z2]) + (a_Vector.z * values[Z3]) + values[TZ];
float w = (a_Vector.x * values[WX]) + (a_Vector.y * values[WY]) + (a_Vector.z * values[WZ]) + values[CZ];
result.x = x / w;
result.y = y / w;
result.z = z / w;
return result;
}
However, one last step is still required. These coordinates will be in homogenized space, ranging (-1, 1) for x and y. That's not what we want, so we'll convert it to pixel coordinates:
Vec2 screenspace_position = Vec2(
(homogenized_position.x * 0.5 + 0.5) * GetScreenWidth(),
(1.f - (homogenized_position.y * 0.5 + 0.5)) * GetScreenHeight()
);
If you are having trouble understanding any of this, first try to implement it. Look up things you don't know. Perhaps you don't know how to do the cross product of two Vec3's or what a normalized vector is. Figure out how to implement it, then try to understand what all this code does.