3D


3D Transformations

To convert a 3d (x,y,z) coordinate to a 2d (x,y) screen coordinate, use the formulae below.

2dX = ((512*x) / (512+z)) + Xorigion
2dY = (-(512*y) / (512+z)) + Yorigion

By changing the 512 to a different number you can alter the perspective effect.


Polygon visibility check (Backface removal)

If you have a 2d polygon with points (vertexs) at (x0,y0),(x1,y1) and (x2,y2), and the points are in a clockwise direction when the polygon is facing towards the screen (i.e. when it's visible), then the following formula tells you if the polygon is visible and should be drawn -

z=(x1-x0)*(y2-y0)-(y1-y0)*(x2-x0);

If z is less than 0 the polygon is not visible.


Simple Polygon Rasterizer

There is lots of advanced source code on the net for 3D graphics, so here is a very simple, but fast, function for drawing a plain polygon.


#include <string.h> //for memset()

typedef unsigned char UCHAR;

//function defs
int glDo2DEdge(int x1,int y1,int x2,int y2);
void glHLineXclip(UCHAR col,int x1,int x2,int y);

//left & right edge arrays for clipped polygon
int glpl[MAXPOLYHEIGHT],glpr[MAXPOLYHEIGHT];//(fill in screen height)

//PRE-SET VARIABLES - set before calling poly function

//minimum and maximum allowable pixel coordinates
//for current screen mode (i.e. 0,0 & 639,479 for 640x480).
int glXMIN,glYMIN,glXMAX,glYMAX;

//pointer to screen memory 
UCHAR *glPTR;

//step in bytes (for 256 color) from 1 line of screen memory to the next 
int glLINESTEP;


//draw convex plain 256color polygon - point lists must be clockwise
int gl2DPoly(UCHAR col, int sides, int *vx,int *vy)
{
  int top=vy[0],bottom=vy[0],left=vx[0],right=vx[0],height,y;
  UCHAR *dst;

  //get top and bottom, left and right points
  for(y=1; y<sides; y++) {
    if (vy[y]<top) top=vy[y]; if (vy[y]>bottom) bottom=vy[y];
    if (vx[y]<left) left=vx[y]; if (vx[y]>right) right=vx[y];
  }
  
  //check polygon is in viewpoint at all
  if (left>glXMAX || right<glXMIN || top>glYMAX || bottom<glYMIN) return(0);

  height=bottom-top+1;
  if (height<1) return(0);//error!

  //now scan-convert all edges and store left/right edges in edge arrays
  for(y=0; y<sides-1; y++) glDo2DEdge(vx[y],vy[y], vx[y+1],vy[y+1]);
  glDo2DEdge(vx[sides-1],vy[sides-1], vx[0],vy[0]);

  //clip top & bottom for drawing
  if (top<glYMIN) top=glYMIN;
  if (bottom>glYMAX) bottom=glYMAX;
  
  //draw polygon - check if X clipping of scanlines necessary first
  if (left>=glXMIN && right<=glXMAX) {
    dst=glPTR+(top*glLINESTEP);
    for(y=top; y<=bottom; y++) {
      if (glpr[y]<glpl[y]) continue;//error
      memset(dst+glpl[y], col, glpr[y]-glpl[y]+1); dst+=glLINESTEP;
    }
  }
  else {//else do x-clipped lines
    for(y=top; y<=bottom; y++) {
      if (glpr[y]<glpl[y]) continue;//error
      glHLineXclip(col,glpl[y],glpr[y],y);
    }
  }

  return(0);
}


//Horizontal edge-clipped line
void glHLineXclip(UCHAR col,int x1,int x2,int y)
{
  //if line in buffer - draw
  if (x1<=glXMAX && x2>=glXMIN) {
    //clip line
    if (x1<glXMIN) x1=glXMIN;
    if (x2>glXMAX) x2=glXMAX;
    memset(glPTR+(y*glLINESTEP)+x1, col, x2-x1+1);
  }
}


//CALCULATE EDGE X VALUES
//fixed point version - clipped to glYMIN/MAX
int glDo2DEdge(int x1,int y1, int x2,int y2)
{
  int y;
  int dx=x2-x1,dy=y2-y1;
  int x,xstep;//screen x pos * step 
  int side=1;
  int top,bottom;

  //get top & bottom Y of line
  if (y1<y2) {top=y1; bottom=y2;}
  else {bottom=y1; top=y2;}
  //check if on screen at all
  if (top>glYMAX || bottom<glYMIN) return(1);//off screen top/bottom
  
  //get edge side (left or right) - if horizontal, do simple
  if (dy==0) {  
    if (x1<x2) {glpl[y1]=x1; glpr[y1]=x2;}
    else {glpl[y1]=x2; glpr[y1]=x1;}
    return(1);
  } 
  //and get positive y distance
  if (dy<0) {side=0; dy=-dy;}

  //get xstep - fixed point
  xstep=(dx<<16)/dy; x=(x1<<16)+32768;

  //if no Y clipping needed, do txtr lines quickly
  if (top>=glYMIN && bottom<=glYMAX) {
    //loop through edge points & store
    if (side==1) {//right
      y=y1;
      //first point
      glpr[y++]=x>>16;
      //rest
      do {
        x+=xstep; 
        glpr[y++]=x>>16;
      }
      while(y<=y2);
    }
    else {//left
      y=y1;
      //first point
      glpl[y--]=x>>16;
      //rest
      do {
        x+=xstep;
        glpl[y--]=x>>16;
      }
      while(y>=y2);
    }
    return(1);//non-clipped edge done
  }

  //CLIPPED edge -
  //get start clip distance
  int clipskip=0;
  if (y1<glYMIN) clipskip=glYMIN-y1;
  if (y1>glYMAX) clipskip=y1-glYMAX;
  //clip y2
  if (y2<glYMIN) y2=glYMIN;
  if (y2>glYMAX) y2=glYMAX;
  //skip clipped points
  x+=xstep*clipskip;

  //loop through edge points & store
  if (side==1) {//right
    y=y1+clipskip;
    //rest
    do {
      glpr[y++]=x>>16;
      x+=xstep; 
    }
    while(y<=y2);
  }
  else {//left
    y=y1-clipskip;
    //rest
    do {
      glpl[y--]=x>>16; 
      x+=xstep;
    }
    while(y>=y2);
  }
    
  return(1);
}


BACK TO MAIN