If you are referring to rasterization, you may find it easier to use these:
INT iHalfScreenX = g_piResolution.x >> 1;
INT iHalfScreenY = g_piResolution.y >> 1;
pntReturn.x = (LONG)((FLOAT)iHalfScreenX + (FLOAT)iHalfScreenX * (d3vTarg.fX / d3vTarg.fZ));
pntReturn.y = (LONG)((FLOAT)iHalfScreenY - (FLOAT)iHalfScreenX * (d3vTarg.fY / d3vTarg.fZ));
Do not perform this equation if d3vTarg.fZ is 0.
This small section does not account for the rotation of the camera.
This is the whole code written for my Doom® 3 perfect-lock auto-aim.
// Transform a 3-D point into screen coordinates. Returns TRUE if the coordinate is actually on the screen.
BOOL Rasterize( PDoom3Vector pd3vPlayerPos, PDoom3Direction pd3dPlayerDirection, PDoom3Vector pd3vTargetPos, POINT &pntReturn ) {
Doom3Vector d3vTarg;
// Copy the target vector so we don’t change it.
memcpy( &d3vTarg, pd3vTargetPos, sizeof( d3vTarg ) );
// Translate the vector to the center of the world, which would literally mean
// our player.
d3vTarg.fX -= pd3vPlayerPos->fX;
d3vTarg.fY -= pd3vPlayerPos->fY;
d3vTarg.fZ -= pd3vPlayerPos->fZ;
// The pitch and yaw are in radians.
FLOAT fPitch = 0.0f - D3DXToRadian( pd3dPlayerDirection->fPitch );
FLOAT fYaw = D3DXToRadian( pd3dPlayerDirection->fYaw - 90.0f );
// Rotate accordingly. This must be done in this order!
if ( fYaw != 0.0f ) {
FLOAT fTemp = d3vTarg.fZ * (FLOAT)cos( fYaw ) - d3vTarg.fX * (FLOAT)sin( fYaw );
d3vTarg.fX = d3vTarg.fX * (FLOAT)cos( fYaw ) + d3vTarg.fZ * (FLOAT)sin( fYaw );
d3vTarg.fZ = fTemp;
}
if ( fPitch != 0.0f ) {
FLOAT fTemp = d3vTarg.fY * (FLOAT)cos( fPitch ) - d3vTarg.fZ * (FLOAT)sin( fPitch );
d3vTarg.fZ = d3vTarg.fZ * (FLOAT)cos( fPitch ) + d3vTarg.fY * (FLOAT)sin( fPitch );
d3vTarg.fY = fTemp;
}
// The result will be where the enemy is in relation to our player.
// From this 3-D point, we can directly determine the 2-D screen point where the 3-D point would be drawn.
// This is useful for drawing targets over objects or determining if an object is on the screen.
if ( d3vTarg.fZ == 0.0f ) {
pntReturn.x = (LONG)(d3vTarg.fX * (FLOAT)(g_pntResolution.x >> 1) + (FLOAT)(g_pntResolution.x >> 1));
pntReturn.y = (LONG)(d3vTarg.fY * (FLOAT)(g_pntResolution.y >> 1) + (FLOAT)(g_pntResolution.x >> 1));
}
else {
pntReturn.x = (LONG)((FLOAT)(g_pntResolution.x >> 1) + (FLOAT)(g_pntResolution.x >> 1) * (d3vTarg.fX / d3vTarg.fZ));
pntReturn.y = (LONG)((FLOAT)(g_pntResolution.y >> 1) - (FLOAT)(g_pntResolution.x >> 1) * (d3vTarg.fY / d3vTarg.fZ));
}
if ( d3vTarg.fZ < 0.0f ) { return FALSE; }
if ( pntReturn.x < 0 || pntReturn.x >= g_pntResolution.x ) { return FALSE; }
if ( pntReturn.y < 0 || pntReturn.y >= g_pntResolution.y ) { return FALSE; }
return TRUE;
}
For my auto-aim, I manually transform according to the rotation of my player’s head (camera [math shown]) but in your engine you are going to have more control over how your rotations work and you should use an optimized rotation routine.
This routine is also not optimized for handling a massive number of points.
You would want to handle your rotations separately from your rasterization, using the first code I posted as your primary rasterization method.
Pass it the final X, Y, and Z points of each vertex (or whatever) after you have rotated the whole scene according to the camera.
You can modify the formula to handle view ports also, and note that I used iHalfScreenX in both sides of the formula. This is to keep a 1-to-1 aspect ratio. Aspect ratios can be added to the equation as well.
I have not benchmarked it for speed.
L. Spiro