Example #1
0
int Transform_Polygon2D(POLYGON2D_PTR poly, float roate, float tx, float ty) {
    // test for valid pointer
    if (!poly)
        return (0);

    // test for negative rotation angle
    if (roate < 0)
        roate += 360;

    // loop and rotate each point, very crude, no lookup!!!
    for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++) {
        poly->vlist_trans[curr_vert].x += tx;
        poly->vlist_trans[curr_vert].y += ty;
        // perform rotation
        float xr = (float) poly->vlist_trans[curr_vert].x * Fast_Cos(roate)
                - (float) poly->vlist_trans[curr_vert].y * Fast_Sin(roate);

        float yr = (float) poly->vlist_trans[curr_vert].x * Fast_Sin(roate)
                + (float) poly->vlist_trans[curr_vert].y * Fast_Cos(roate);

        // store result back
        poly->vlist_trans[curr_vert].x = xr;
        poly->vlist_trans[curr_vert].y = yr;

    } // end for curr_vert

    // return success
    return (1);
}
Example #2
0
int Rotate_Polygon2D(POLYGON2D_PTR poly, float theta) {
    // this function rotates the local coordinates of the polygon

    // test for valid pointer
    if (!poly)
        return (0);

    // test for negative rotation angle
    if (theta < 0)
        theta += 360;

    // loop and rotate each point, very crude, no lookup!!!
    for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++) {
        // perform rotation
        float xr = (float) poly->vlist_trans[curr_vert].x * Fast_Cos(theta)
                - (float) poly->vlist_trans[curr_vert].y * Fast_Sin(theta);

        float yr = (float) poly->vlist_trans[curr_vert].x * Fast_Sin(theta)
                + (float) poly->vlist_trans[curr_vert].y * Fast_Cos(theta);

        // store result back
        poly->vlist_trans[curr_vert].x = xr;
        poly->vlist_trans[curr_vert].y = yr;

    } // end for curr_vert

    // return success
    return (1);

} // end Rotate_Polygon2D
Example #3
0
void uav2D_init(void) {
  // initialize uav2d
  uav2D.state       = 1;         // turn it on
  uav2D.num_verts   = 38;
  uav2D.x0          = eeprom_buffer.params.Atti_mp_posX;       // position it
  uav2D.y0          = eeprom_buffer.params.Atti_mp_posY;

  int index = 0, i = 0;
  const int lX = 5;
  const int stepY = 11;
  const int hX = 22;
  for (index = 0; index < uav2D.num_verts / 2 - 1; )
  {
    VECTOR2D_INITXYZ(&(uav2D.vlist_local[index]), -lX, stepY * (i + 1));
    VECTOR2D_INITXYZ(&(uav2D.vlist_local[index + 1]), lX, stepY * (i + 1));
    index += 2;
    i++;
  }
  VECTOR2D_INITXYZ(&(uav2D.vlist_local[index]), -hX, 0);
  VECTOR2D_INITXYZ(&(uav2D.vlist_local[index + 1]), hX, 0);
  index += 2;

  i = 0;
  for (; index < uav2D.num_verts; )
  {
    VECTOR2D_INITXYZ(&(uav2D.vlist_local[index]), -lX, -stepY * (i + 1));
    VECTOR2D_INITXYZ(&(uav2D.vlist_local[index + 1]), lX, -stepY * (i + 1));
    index += 2;
    i++;
  }


  // do a quick scale on the vertices
  Scale_Polygon2D(&uav2D, atti_mp_scale, atti_mp_scale);

  // initialize roll scale
  rollscale2D.state       = 1;         // turn it on
  rollscale2D.num_verts   = 13;
  rollscale2D.x0          = eeprom_buffer.params.Atti_mp_posX;       // position it
  rollscale2D.y0          = eeprom_buffer.params.Atti_mp_posY;


  int x, y, theta;
  int mp = (rollscale2D.num_verts - 1) / 2;
  i = mp;
  int radio = 38;
  int arcStep = (mp * 10) / 6;
  for (index = 0; index < mp; index++)
  {
    theta = i * arcStep;
    x = radio * Fast_Sin(theta);
    y = radio * Fast_Cos(theta);
    VECTOR2D_INITXYZ(&(rollscale2D.vlist_local[index]), -x, -y);
    VECTOR2D_INITXYZ(&(rollscale2D.vlist_local[rollscale2D.num_verts - 1 - index]), x, -y);
    i--;
  }

  VECTOR2D_INITXYZ(&(rollscale2D.vlist_local[index]), 0, -radio);
  Scale_Polygon2D(&rollscale2D, atti_mp_scale, atti_mp_scale);
}
Example #4
0
void Build_CAM4DV1_Matrix_UVN(CAM4DV1_PTR cam, int mode)
{
	MATRIX4X4 mt_inv;
	MATRIX4X4 mt_uvn;
	MATRIX4X4 mtmp;

	//Ïà»úƽÒƾØÕóµÄÄæ¾ØÕó
	Mat_Init_4X4(&mt_inv, 1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 0, 1,
		-cam->pos.x, -cam->pos.y, -cam->pos.z, 1);

	if (UVN_MODE_SPHERICAL == mode)
	{
		float phi = cam->dir.x;
		float theta = cam->dir.y;

		float sin_phi = Fast_Sin(phi);
		float cos_phi = Fast_Cos(theta);

		float sin_theta = Fast_Sin(theta);
		float cos_theta = Fast_Cos(theta);

		cam->target.x = -1 * sin_phi * sin_theta;
		cam->target.y = 1 * cos_phi;
		cam->target.z = 1 * sin_phi * cos_theta;
	}

	VECTOR4D_Build(&cam->pos, &cam->target, &cam->n);
	
	VECTOR4D_INITXYZ(&cam->v, 0, 1, 0);

	VECTOR4D_Cross(&cam->v, &cam->n, &cam->u);
	
	VECTOR4D_Cross(&cam->n, &cam->u, &cam->v);
	
	VECTOR4D_Normalize(&cam->u);
	VECTOR4D_Normalize(&cam->v);
	VECTOR4D_Normalize(&cam->n);

	Mat_Init_4X4(&mt_uvn, cam->u.x, cam->v.x, cam->n.x, 0,
		cam->u.y, cam->v.y, cam->n.y, 0,
		cam->u.z, cam->v.z, cam->n.z, 0,
		0, 0, 0, 1);

	Mat_Mul_4X4(&mt_inv, &mt_uvn, &cam->mcam);
}
Example #5
0
void Build_CAM4D_Matrix_UVM(LPCAM4D cam, int mode)
{
	MATRIX_4X4 mt_inv;
	MATRIX_4X4 mt_uvn;
	MATRIX_4X4 mtmp;

	MATRIX_INIT_4X4(&mt_inv,  1,           0,           0,          0,
		                      0,           1,           0,          0,
							  0,           0,           1,          0,
							 -cam->pos.x, -cam->pos.y, -cam->pos.z, 1);

	if (mode == UVN_MODE_SPHERICAL)
	{
		float phi   = cam->dir.x;
		float theta = cam->dir.y;

		float sin_phi   = Fast_Sin(phi);
		float cos_phi   = Fast_Cos(phi);

		float sin_theta = Fast_Sin(theta);
		float cos_theta = Fast_Cos(theta);

		cam->target.x = -1 * sin_phi * sin_theta;
		cam->target.y =  1 * cos_phi;
		cam->target.z =  1 * sin_phi * cos_theta;
	}

	VECTOR4D_BUILD(&cam->pos, &cam->target, &cam->n);

	VECTOR4D_INITXYZ(&cam->v, 0, 1, 0);

	VECTOR4D_CROSS(&cam->v, &cam->n, &cam->u);

	VECTOR4D_CROSS(&cam->n, &cam->u, &cam->v);

	VECTOR4D_NORMALIZE(&cam->u);
	VECTOR4D_NORMALIZE(&cam->v);
	VECTOR4D_NORMALIZE(&cam->n);

	MATRIX_INIT_4X4(&mt_uvn, cam->u.x, cam->v.x, cam->n.x, 0, 
		                     cam->u.y, cam->v.y, cam->n.y, 0, 
							 cam->u.z, cam->v.z, cam->n.z, 0, 
							 0,        0,        0,        1);

	MATRIX_MUL_4X4(&mt_inv, &mt_uvn, &cam->mWordToCam);
}
Example #6
0
void Build_CAM4D_Matrix_UVN(CAM4D_PTR cam, int mode)
{
	Matrix4d mt_inv,
		mt_uvn;

	mt_inv << 1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0.
		- cam->pos(0), -cam->pos(1), -cam->pos(2), 1;

	if (mode == UVN_MODE_SPHERICAL) {
		float phi = cam->dir(0);
		float theta = cam->dir(1);

		float sin_phi = Fast_Sin(phi);
		float cos_phi = Fast_Cos(phi);
		float sin_theta = Fast_Sin(theta);
		float cos_theta = Fast_Cos(theta);

		cam->target(0) = -1 * sin_phi * sin_theta;
		cam->target(1) = 1 * cos_phi;
		cam->target(2) = 1 * sin_phi * cos_theta;
	}

	cam->n = cam->target - cam->pos;
	Vector3d t1, t2, t3;
	t1 = Vector3d(cam->n(0), cam->n(1), cam->n(2));
	t2 = Vector3d(0, 1, 0);
	t3 = t2.cross(t1);
	t2 = t1.cross(t3);

	cam->u = Vector4d(t3(0), t3(1), t3(2), 1);
	cam->v = Vector4d(t2(0), t2(1), t2(2), 1);

	cam->u.normalize();
	cam->v.normalize();
	cam->n.normalize();

	mt_uvn << cam->u(0), cam->v(0), cam->n(0), 0,
		cam->u(1), cam->v(1), cam->n(1), 0,
		cam->u(2), cam->v(2), cam->n(2), 0,
		0, 0, 0, 1;
	cam->mcam = mt_inv * mt_uvn;
}
Example #7
0
void Build_CAM4D_Matrix_Euler(LPCAM4D cam, int camRotSeq)
{
	MATRIX_4X4 mt_inv;
	MATRIX_4X4 mx_inv;
	MATRIX_4X4 my_inv;
	MATRIX_4X4 mz_inv;
	MATRIX_4X4 mrot;
	MATRIX_4X4 mtmp;

	MATRIX_INIT_4X4(&mt_inv,  1,           0,           0,          0,
		                      0,           1,           0,          0,
							  0,           0,           1,          0,
							 -cam->pos.x, -cam->pos.y, -cam->pos.z, 1);

	float xTheta = cam->dir.x;
	float yTheta = cam->dir.y;
	float zTheta = cam->dir.z;

	float cosTheta =  Fast_Cos(xTheta);
	float sinTheta = -Fast_Sin(xTheta);

	MATRIX_INIT_4X4(&mx_inv, 1,  0,        0,        0,
		                     0,  cosTheta, sinTheta, 0, 
							 0, -sinTheta, cosTheta, 0, 
							 0,  0,        0,        1);

	cosTheta =  Fast_Cos(yTheta);
	sinTheta = -Fast_Sin(yTheta);

	MATRIX_INIT_4X4(&my_inv, cosTheta, 0, -sinTheta, 0,
		                     0,        1,  0,        0, 
							 sinTheta, 0,  cosTheta, 0, 
							 0,        0,  0,        1);

	cosTheta =  Fast_Cos(zTheta);
	sinTheta = -Fast_Sin(zTheta);

	MATRIX_INIT_4X4(&mz_inv,  cosTheta, sinTheta, 0, 0,
		                     -sinTheta, cosTheta, 0, 0, 
							  0,        0,        1, 0,
							  0,        0,        0, 1);

	switch (camRotSeq)
	{
	case CAM_ROT_SEQ_XYZ:
		MATRIX_MUL_4X4(&mx_inv, &my_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &mz_inv, &mrot);
		break;

    case CAM_ROT_SEQ_YXZ:
		MATRIX_MUL_4X4(&my_inv, &mx_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &mz_inv, &mrot);
		break;

	case CAM_ROT_SEQ_XZY:
		MATRIX_MUL_4X4(&mx_inv, &mx_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &my_inv, &mrot);
		break;

	case CAM_ROT_SEQ_YZX:
		MATRIX_MUL_4X4(&my_inv, &mz_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &mx_inv, &mrot);
		break;

	case CAM_ROT_SEQ_ZYX:
		MATRIX_MUL_4X4(&mz_inv, &my_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &mx_inv, &mrot);
		break;

	case CAM_ROT_SEQ_ZXY:
		MATRIX_MUL_4X4(&mz_inv, &mx_inv, &mtmp);
		MATRIX_MUL_4X4(&mtmp,   &my_inv, &mrot);
		break;

	default:
		break;
	}

	MATRIX_MUL_4X4(&mt_inv, &mrot, &cam->mWordToCam);
}
Example #8
0
void Build_XYZ_Rotation_Matrix4X4(float        xTheta, 
								  float        yTheta, 
								  float        zTheta, 
								  LPMATRIX_4X4 pmRotation)
{
	MATRIX_4X4 mx;
	MATRIX_4X4 my;
	MATRIX_4X4 mz;
	MATRIX_4X4 mtemp;

	float sinTheta = 0;
	float cosTheta = 0;
	int   rotSeq   = 0;

	MATRIX_IDENTITY_4X4(pmRotation);

	if (fabs(xTheta) > EPSILON_E5)
		rotSeq = rotSeq | 1;
	if (fabs(yTheta) > EPSILON_E5)
		rotSeq = rotSeq | 2;
	if (fabs(zTheta) > EPSILON_E5)
		rotSeq = rotSeq | 4;

	switch (rotSeq)
	{
	case 0:
		break;

	case 1:
		cosTheta = Fast_Cos(xTheta);
		sinTheta = Fast_Sin(xTheta);

		MATRIX_INIT_4X4(&mx, 1,  0,        0,        0, 
			                 0,  cosTheta, sinTheta, 0,
							 0, -sinTheta, cosTheta, 0,
							 0,  0,        0,        1);

		MATRIX_COPY_4X4(pmRotation, &mx);
		break;

	case 2:
		cosTheta = Fast_Cos(yTheta);
		sinTheta = Fast_Sin(yTheta);

		MATRIX_INIT_4X4(&my, cosTheta, 0, -sinTheta, 0, 
			                 0,        1, 0,         0,
							 sinTheta, 0, cosTheta,  0,
							 0,        0, 0,         1);

		MATRIX_COPY_4X4(pmRotation, &my);
		break;

	case 3:
		cosTheta = Fast_Cos(xTheta);
		sinTheta = Fast_Sin(xTheta);

		MATRIX_INIT_4X4(&mx, 1,  0,        0,        0, 
			                 0,  cosTheta, sinTheta, 0,
							 0, -sinTheta, cosTheta, 0,
							 0,  0,        0,        1);

		cosTheta = Fast_Cos(yTheta);
		sinTheta = Fast_Sin(yTheta);

		MATRIX_INIT_4X4(&my, cosTheta, 0, -sinTheta, 0, 
			                 0,        1, 0,         0,
							 sinTheta, 0, cosTheta,  0,
							 0,        0, 0,         1);

		MATRIX_MUL_4X4(&mx, &my, pmRotation);
		break;

	case 4:
		cosTheta = Fast_Cos(zTheta);
		sinTheta = Fast_Sin(zTheta);

		MATRIX_INIT_4X4(&mz,  cosTheta, sinTheta, 0, 0, 
			                 -sinTheta, cosTheta, 0, 0,
							  0,        0,        1, 0,
							  0,        0,        0, 1);

		MATRIX_COPY_4X4(&pmRotation, &mz);
		break;

	case 5:
		cosTheta = Fast_Cos(xTheta);
		sinTheta = Fast_Sin(xTheta);

		MATRIX_INIT_4X4(&mx, 1,  0,        0,        0, 
			                 0,  cosTheta, sinTheta, 0,
							 0, -sinTheta, cosTheta, 0,
							 0,  0,        0,        1);

		cosTheta = Fast_Cos(zTheta);
		sinTheta = Fast_Sin(zTheta);

		MATRIX_INIT_4X4(&mz,  cosTheta, sinTheta, 0, 0, 
			                 -sinTheta, cosTheta, 0, 0,
							  0,        0,        1, 0,
							  0,        0,        0, 1);

		MATRIX_MUL_4X4(&mx, &mz, pmRotation);
		break;

	case 6:
		cosTheta = Fast_Cos(yTheta);
		sinTheta = Fast_Sin(yTheta);

		MATRIX_INIT_4X4(&my, cosTheta, 0, -sinTheta, 0, 
			                 0,        1, 0,         0,
							 sinTheta, 0, cosTheta,  0,
							 0,        0, 0,         1);

		cosTheta = Fast_Cos(zTheta);
		sinTheta = Fast_Sin(zTheta);

		MATRIX_INIT_4X4(&mz,  cosTheta, sinTheta, 0, 0, 
			                 -sinTheta, cosTheta, 0, 0,
							  0,        0,        1, 0,
							  0,        0,        0, 1);

		MATRIX_MUL_4X4(&my, &mz, pmRotation);
		break;

	case 7:
		cosTheta = Fast_Cos(xTheta);
		sinTheta = Fast_Sin(xTheta);

		MATRIX_INIT_4X4(&mx, 1,  0,        0,        0, 
			                 0,  cosTheta, sinTheta, 0,
							 0, -sinTheta, cosTheta, 0,
							 0,  0,        0,        1);

		cosTheta = Fast_Cos(yTheta);
		sinTheta = Fast_Sin(yTheta);

		MATRIX_INIT_4X4(&my, cosTheta, 0, -sinTheta, 0, 
			                 0,        1, 0,         0,
							 sinTheta, 0, cosTheta,  0,
							 0,        0, 0,         1);

		cosTheta = Fast_Cos(zTheta);
		sinTheta = Fast_Sin(zTheta);

		MATRIX_INIT_4X4(&mz,  cosTheta, sinTheta, 0, 0, 
			                 -sinTheta, cosTheta, 0, 0,
							  0,        0,        1, 0,
							  0,        0,        0, 1);

		MATRIX_MUL_4X4(&mx, &my, &mtemp);
		MATRIX_MUL_4X4(&mtemp, &mz, pmRotation);
		break;

	default:
		break;
	}
}
Example #9
0
void Build_CAM4DV1_Matrix_Euler(CAM4DV1_PTR cam, int cam_rot_seq)
{
	MATRIX4X4 mt_inv;
	MATRIX4X4 mx_inv;
	MATRIX4X4 my_inv;
	MATRIX4X4 mz_inv;
	MATRIX4X4 mrot;
	MATRIX4X4 mtmp;

	//Ïà»úƽÒƾØÕóµÄÄæ¾ØÕó
	Mat_Init_4X4(&mt_inv, 1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0,
		-cam->pos.x, -cam->pos.y, -cam->pos.z, 1);

	//Ðýת¾ØÕó
	//ÌáÈ¡Å·À­½Ç
	float theta_x = cam->dir.x;
	float theta_y = cam->dir.y;
	float theta_z = cam->dir.z;

	float cos_theta = Fast_Cos(theta_x); //cos(-x) = cos(x)
	float sin_theta = -Fast_Sin(theta_x);	//sin(-x) = -sin(x)
	Mat_Init_4X4(&mx_inv, 1, 0, 0, 0,
		0, cos_theta, sin_theta, 0,
		0, -sin_theta, cos_theta, 0,
		0, 0, 0, 1);

	cos_theta = Fast_Cos(theta_y); //cos(-x) = cos(x)
	sin_theta = -Fast_Sin(theta_y);	//sin(-x) = -sin(x)
	Mat_Init_4X4(&my_inv, cos_theta, 0, -sin_theta, 0,
		0, 1, 0, 0,
		sin_theta, 0, cos_theta, 0,
		0, 0, 0, 1);

	cos_theta = Fast_Cos(theta_z); //cos(-x) = cos(x)
	sin_theta = -Fast_Sin(theta_z);	//sin(-x) = -sin(x)
	Mat_Init_4X4(&mz_inv, 
		cos_theta, sin_theta, 0, 0,
		-sin_theta, cos_theta, 0, 0,
		0, 0, 1, 0,
		0, 0, 0, 1);

	switch (cam_rot_seq)
	{
	case CAM_ROT_SEQ_XYZ:
		Mat_Mul_4X4(&mx_inv, &my_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &mz_inv, &mrot);
		break;
	case CAM_ROT_SEQ_YXZ:
		Mat_Mul_4X4(&my_inv, &mx_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &mz_inv, &mrot);
		break;
	case CAM_ROT_SEQ_XZY:
		Mat_Mul_4X4(&mx_inv, &mz_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &my_inv, &mrot);
		break;
	case CAM_ROT_SEQ_YZX:
		Mat_Mul_4X4(&my_inv, &mz_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &mx_inv, &mrot);
		break;
	case CAM_ROT_SEQ_ZYX:
		Mat_Mul_4X4(&mz_inv, &my_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &mx_inv, &mrot);
		break;
	case CAM_ROT_SEQ_ZXY:
		Mat_Mul_4X4(&mz_inv, &mx_inv, &mtmp);
		Mat_Mul_4X4(&mtmp, &my_inv, &mrot);
		break;
	default:
		break;
	}

	Mat_Mul_4X4(&mt_inv, &mrot, &cam->mcam);
}
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

// these are used to create a circling camera
static float view_angle = 0; 
static float camera_distance = 6000;
static VECTOR4D pos = {0,0,0,0};
static float tank_speed;
static float turning = 0;

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
Draw_Rectangle(0,0, WINDOW_WIDTH-1, WINDOW_HEIGHT/2, RGB16Bit(0,140,192), lpddsback);

// draw the ground
Draw_Rectangle(0,WINDOW_HEIGHT/2, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, RGB16Bit(103,62,3), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// reset the render list
Reset_RENDERLIST4DV1(&rend_list);

// allow user to move camera

// turbo
if (keyboard_state[DIK_SPACE])
    tank_speed = 5*TANK_SPEED;
else
    tank_speed = TANK_SPEED;

// forward/backward
if (keyboard_state[DIK_UP])
   {
   // move forward
   cam.pos.x += tank_speed*Fast_Sin(cam.dir.y);
   cam.pos.z += tank_speed*Fast_Cos(cam.dir.y);
   } // end if

if (keyboard_state[DIK_DOWN])
   {
   // move backward
   cam.pos.x -= tank_speed*Fast_Sin(cam.dir.y);
   cam.pos.z -= tank_speed*Fast_Cos(cam.dir.y);
   } // end if

// rotate
if (keyboard_state[DIK_RIGHT])
   {
   cam.dir.y+=3;
   
   // add a little turn to object
   if ((turning+=2) > 15)
      turning=15;

   } // end if

if (keyboard_state[DIK_LEFT])
   {
   cam.dir.y-=3;

   // add a little turn to object
   if ((turning-=2) < -15)
      turning=-15;

   } // end if
else // center heading again
   {
   if (turning > 0)
       turning-=1;
   else
   if (turning < 0)
       turning+=1;

   } // end else

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

// insert the tanks in the world
for (index = 0; index < NUM_TANKS; index++)
    {
    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV1(&obj_tank);

    // generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(0, tanks[index].w, 0, &mrot);

    // rotate the local coords of the object
    Transform_OBJECT4DV1(&obj_tank, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

    // set position of tank
    obj_tank.world_pos.x = tanks[index].x;
    obj_tank.world_pos.y = tanks[index].y;
    obj_tank.world_pos.z = tanks[index].z;

    // attempt to cull object   
    if (!Cull_OBJECT4DV1(&obj_tank, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       // if we get here then the object is visible at this world position
       // so we can insert it into the rendering list
       // perform local/model to world transform
       Model_To_World_OBJECT4DV1(&obj_tank, TRANSFORM_TRANS_ONLY);

       // insert the object into render list
       Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_tank);
       } // end if
 
    } // end for

// insert the player into the world
// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV1(&obj_player);

// set position of tank
obj_player.world_pos.x = cam.pos.x+300*Fast_Sin(cam.dir.y);
obj_player.world_pos.y = cam.pos.y-70;
obj_player.world_pos.z = cam.pos.z+300*Fast_Cos(cam.dir.y);

// generate rotation matrix around y axis
Build_XYZ_Rotation_MATRIX4X4(0, cam.dir.y+turning, 0, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV1(&obj_player, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV1(&obj_player, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_player);


// insert the towers in the world
for (index = 0; index < NUM_TOWERS; index++)
    {
    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV1(&obj_tower);

    // set position of tower
    obj_tower.world_pos.x = towers[index].x;
    obj_tower.world_pos.y = towers[index].y;
    obj_tower.world_pos.z = towers[index].z;

    // attempt to cull object   
    if (!Cull_OBJECT4DV1(&obj_tower, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       // if we get here then the object is visible at this world position
       // so we can insert it into the rendering list
       // perform local/model to world transform
       Model_To_World_OBJECT4DV1(&obj_tower);

       // insert the object into render list
       Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_tower);
       } // end if
 
    } // end for

// seed number generator so that modulation of markers is always the same
srand(13);

// insert the ground markers into the world
for (int index_x = 0; index_x < NUM_POINTS_X; index_x++)
    for (int index_z = 0; index_z < NUM_POINTS_Z; index_z++)
        {
        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV1(&obj_marker);

        // set position of tower
        obj_marker.world_pos.x = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_x*POINT_SIZE;
        obj_marker.world_pos.y = obj_marker.max_radius;
        obj_marker.world_pos.z = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_z*POINT_SIZE;

        // attempt to cull object   
        if (!Cull_OBJECT4DV1(&obj_marker, &cam, CULL_OBJECT_XYZ_PLANES))
           {
           // if we get here then the object is visible at this world position
           // so we can insert it into the rendering list
           // perform local/model to world transform
           Model_To_World_OBJECT4DV1(&obj_marker);

           // insert the object into render list
           Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_marker);
           } // end if

        } // end for

// remove backfaces
Remove_Backfaces_RENDERLIST4DV1(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV1(&rend_list, &cam);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV1(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV1(&rend_list, &cam);

sprintf(work_string,"pos:[%f, %f, %f] heading:[%f] elev:[%f]", 
        cam.pos.x, cam.pos.y, cam.pos.z, cam.dir.y, cam.dir.x); 

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-20, RGB(0,255,0), lpddsback);

// draw instructions
Draw_Text_GDI("Press ESC to exit. Press Arrow Keys to Move. Space for TURBO.", 0, 0, RGB(0,255,0), lpddsback);

// lock the back buffer
DDraw_Lock_Back_Surface();

// render the object
Draw_RENDERLIST4DV1_Wire16(&rend_list, back_buffer, back_lpitch);

// unlock the back buffer
DDraw_Unlock_Back_Surface();

// flip the surfaces
DDraw_Flip();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
 
} // end Game_Main
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

// these are used to create a circling camera
static float view_angle = 0; 
static float camera_distance = 6000;
static VECTOR4D pos = {0,0,0,0};
static float tank_speed;
static float turning = 0;
// state variables for different rendering modes and help
static int wireframe_mode = 1;
static int backface_mode  = 1;
static int lighting_mode  = 1;
static int help_mode      = 1;
static int zsort_mode     = 1;

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
//Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT/2, RGB16Bit(0,35,50), lpddsback);

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT/2-1, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(20,12,0), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// modes and lights

// wireframe mode
if (keyboard_state[DIK_W])
   {
   // toggle wireframe mode
   if (++wireframe_mode > 1)
       wireframe_mode=0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// backface removal
if (keyboard_state[DIK_B])
   {
   // toggle backface removal
   backface_mode = -backface_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// lighting
if (keyboard_state[DIK_L])
   {
   // toggle lighting engine completely
   lighting_mode = -lighting_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle ambient light
if (keyboard_state[DIK_A])
   {
   // toggle ambient light
   if (lights[AMBIENT_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle infinite light
if (keyboard_state[DIK_I])
   {
   // toggle ambient light
   if (lights[INFINITE_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle point light
if (keyboard_state[DIK_P])
   {
   // toggle point light
   if (lights[POINT_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// toggle spot light
if (keyboard_state[DIK_S])
   {
   // toggle spot light
   if (lights[SPOT_LIGHT2_INDEX].state == LIGHTV1_STATE_ON)
      lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// help menu
if (keyboard_state[DIK_H])
   {
   // toggle help menu 
   help_mode = -help_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// z-sorting
if (keyboard_state[DIK_Z])
   {
   // toggle z sorting
   zsort_mode = -zsort_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

static float plight_ang = 0, slight_ang = 0; // angles for light motion

// move point light source in ellipse around game world
lights[POINT_LIGHT_INDEX].pos.x = 1000*Fast_Cos(plight_ang);
lights[POINT_LIGHT_INDEX].pos.y = 100;
lights[POINT_LIGHT_INDEX].pos.z = 1000*Fast_Sin(plight_ang);

if ((plight_ang+=3) > 360)
    plight_ang = 0;

// move spot light source in ellipse around game world
lights[SPOT_LIGHT2_INDEX].pos.x = 1000*Fast_Cos(slight_ang);
lights[SPOT_LIGHT2_INDEX].pos.y = 200;
lights[SPOT_LIGHT2_INDEX].pos.z = 1000*Fast_Sin(slight_ang);

if ((slight_ang-=5) < 0)
    slight_ang = 360;

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

// use these to rotate objects
static float x_ang = 0, y_ang = 0, z_ang = 0;

//////////////////////////////////////////////////////////////////////////
// constant shaded water

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_constant_water);

// set position of constant shaded water
obj_constant_water.world_pos.x = -50;
obj_constant_water.world_pos.y = 0;
obj_constant_water.world_pos.z = 120;

// generate rotation matrix around y axis
Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_constant_water, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_constant_water, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_constant_water,0);

//////////////////////////////////////////////////////////////////////////
// flat shaded water

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_flat_water);

// set position of constant shaded water
obj_flat_water.world_pos.x = 0;
obj_flat_water.world_pos.y = 0;
obj_flat_water.world_pos.z = 120;

// generate rotation matrix around y axis
Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_flat_water, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_flat_water, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_flat_water,0);

//////////////////////////////////////////////////////////////////////////
// gouraud shaded water

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_gouraud_water);

// set position of constant shaded water
obj_gouraud_water.world_pos.x = 50;
obj_gouraud_water.world_pos.y = 0;
obj_gouraud_water.world_pos.z = 120;

// generate rotation matrix around y axis
Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_gouraud_water, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_gouraud_water, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_gouraud_water,0);

// update rotation angles
if ((x_ang+=1) > 360) x_ang = 0;
if ((y_ang+=2) > 360) y_ang = 0;
if ((z_ang+=3) > 360) z_ang = 0;

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// light scene all at once 
if (lighting_mode==1)
   Light_RENDERLIST4DV2_World16(&rend_list, &cam, lights, 4);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, Spot=%d | Zsort [%s], BckFceRM [%s]", 
                                                                                 ((lighting_mode == 1) ? "ON" : "OFF"),
                                                                                 lights[AMBIENT_LIGHT_INDEX].state,
                                                                                 lights[INFINITE_LIGHT_INDEX].state, 
                                                                                 lights[POINT_LIGHT_INDEX].state,
                                                                                 lights[SPOT_LIGHT2_INDEX].state,
                                                                                 ((zsort_mode == 1) ? "ON" : "OFF"),
                                                                                 ((backface_mode == 1) ? "ON" : "OFF"));

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34, RGB(0,255,0), lpddsback);

// draw instructions
Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(0,255,0), lpddsback);

// should we display help
int text_y = 16;
if (help_mode==1)
    {
    // draw help menu
    Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<S>..............Toggle spot light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<B>..............Toggle backface removal.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help

// lock the back buffer
DDraw_Lock_Back_Surface();

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   Draw_RENDERLIST4DV2_Solid16(&rend_list, back_buffer, back_lpitch);

// unlock the back buffer
DDraw_Unlock_Back_Surface();

// flip the surfaces
DDraw_Flip();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
 
} // end Game_Main
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

static float plight_ang = 0, 
             slight_ang = 0; // angles for light motion

// use these to rotate objects
static float x_ang = 0, y_ang = 0, z_ang = 0;

// state variables for different rendering modes and help
static int wireframe_mode   = 1;
static int backface_mode    = 1;
static int lighting_mode    = 1;
static int help_mode        = 1;
static int zsort_mode       = 1;
static int x_clip_mode      = 1;
static int y_clip_mode      = 1;
static int z_clip_mode      = 1;

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(255,120,255), lpddsback);

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT*.38, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(25,50,110), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// modes and lights

// wireframe mode
if (keyboard_state[DIK_W]) 
   {
   // toggle wireframe mode
   if (++wireframe_mode > 1)
       wireframe_mode=0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// backface removal
if (keyboard_state[DIK_B])
   {
   // toggle backface removal
   backface_mode = -backface_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// lighting
if (keyboard_state[DIK_L])
   {
   // toggle lighting engine completely
   lighting_mode = -lighting_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle ambient light
if (keyboard_state[DIK_A])
   {
   // toggle ambient light
   if (lights2[AMBIENT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle infinite light
if (keyboard_state[DIK_I])
   {
   // toggle ambient light
   if (lights2[INFINITE_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle point light
if (keyboard_state[DIK_P])
   {
   // toggle point light
   if (lights2[POINT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   // toggle point light
   if (lights2[POINT_LIGHT2_INDEX].state == LIGHTV2_STATE_ON)
      lights2[POINT_LIGHT2_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[POINT_LIGHT2_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// help menu
if (keyboard_state[DIK_H])
   {
   // toggle help menu 
   help_mode = -help_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// z-sorting
if (keyboard_state[DIK_Z])
   {
   // toggle z sorting
   zsort_mode = -zsort_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// move to next object
if (keyboard_state[DIK_O])
   {
   VECTOR4D old_pos;
   old_pos = obj_work->world_pos;

   if (++curr_object >= NUM_OBJECTS)
      curr_object = 0;

   // update pointer
   obj_work = &obj_array[curr_object];
   obj_work->world_pos = old_pos;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// forward/backward
if (keyboard_state[DIK_UP])
   {
   // move forward
   if ( (cam_speed+=1) > MAX_SPEED) cam_speed = MAX_SPEED;
   } // end if
else
if (keyboard_state[DIK_DOWN])
   {
   // move backward
   if ((cam_speed-=1) < -MAX_SPEED) cam_speed = -MAX_SPEED;
   } // end if

// rotate around y axis or yaw
if (keyboard_state[DIK_RIGHT])
   {
   cam.dir.y+=5;
   } // end if

if (keyboard_state[DIK_LEFT])
   {
   cam.dir.y-=5;
   } // end if

// motion section /////////////////////////////////////////////////////////

// terrain following, simply find the current cell we are over and then
// index into the vertex list and find the 4 vertices that make up the 
// quad cell we are hovering over and then average the values, and based
// on the current height and the height of the terrain push the player upward

// the terrain generates and stores some results to help with terrain following
//ivar1 = columns;
//ivar2 = rows;
//fvar1 = col_vstep;
//fvar2 = row_vstep;

int cell_x = (cam.pos.x  + TERRAIN_WIDTH/2) / obj_terrain.fvar1;
int cell_y = (cam.pos.z  + TERRAIN_HEIGHT/2) / obj_terrain.fvar1;

static float terrain_height, delta;

// test if we are on terrain
if ( (cell_x >=0) && (cell_x < obj_terrain.ivar1) && (cell_y >=0) && (cell_y < obj_terrain.ivar2) )
   {
   // compute vertex indices into vertex list of the current quad
   int v0 = cell_x + cell_y*obj_terrain.ivar2;
   int v1 = v0 + 1;
   int v2 = v1 + obj_terrain.ivar2;
   int v3 = v0 + obj_terrain.ivar2;   

   // now simply index into table 
   terrain_height = 0.25 * (obj_terrain.vlist_trans[v0].y + obj_terrain.vlist_trans[v1].y +
                            obj_terrain.vlist_trans[v2].y + obj_terrain.vlist_trans[v3].y);

   // compute height difference
   delta = terrain_height - (cam.pos.y - gclearance);

   // test for penetration
   if (delta > 0)
      {
      // apply force immediately to camera (this will give it a springy feel)
      vel_y+=(delta * (VELOCITY_SCALER));

      // test for pentration, if so move up immediately so we don't penetrate geometry
      cam.pos.y+=(delta*CAM_HEIGHT_SCALER);

      // now this is more of a hack than the physics model :) let move the front
      // up and down a bit based on the forward velocity and the gradient of the 
      // hill
      cam.dir.x -= (delta*PITCH_CHANGE_RATE);
 
      } // end if

   } // end if

// decelerate camera
if (cam_speed > (CAM_DECEL) ) cam_speed-=CAM_DECEL;
else
if (cam_speed < (-CAM_DECEL) ) cam_speed+=CAM_DECEL;
else
   cam_speed = 0;

// force camera to seek a stable orientation
if (cam.dir.x > (neutral_pitch+PITCH_RETURN_RATE)) cam.dir.x -= (PITCH_RETURN_RATE);
else
if (cam.dir.x < (neutral_pitch-PITCH_RETURN_RATE)) cam.dir.x += (PITCH_RETURN_RATE);
 else 
   cam.dir.x = neutral_pitch;

// apply gravity
vel_y+=gravity;

// test for absolute sea level and push upward..
if (cam.pos.y < sea_level)
   { 
   vel_y = 0; 
   cam.pos.y = sea_level;
   } // end if

// move camera
cam.pos.x += cam_speed*Fast_Sin(cam.dir.y);
cam.pos.z += cam_speed*Fast_Cos(cam.dir.y);
cam.pos.y += vel_y;

// move point light source in ellipse around game world
lights2[POINT_LIGHT_INDEX].pos.x = 1000*Fast_Cos(plight_ang);
lights2[POINT_LIGHT_INDEX].pos.y = 200;
lights2[POINT_LIGHT_INDEX].pos.z = 1000*Fast_Sin(plight_ang);

// move point light source in ellipse around game world
lights2[POINT_LIGHT2_INDEX].pos.x = 500*Fast_Cos(-2*plight_ang);
lights2[POINT_LIGHT2_INDEX].pos.y = 400;
lights2[POINT_LIGHT2_INDEX].pos.z = 1000*Fast_Sin(-2*plight_ang);

if ((plight_ang+=3) > 360)
    plight_ang = 0;

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

//////////////////////////////////////////////////////////////////////////
// the terrain

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_terrain);

// generate rotation matrix around y axis
//Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

MAT_IDENTITY_4X4(&mrot); 

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_terrain, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_terrain, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_terrain,0);

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// render the shaded object that projects the shadow

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(obj_work);

// update rotation angle of object
obj_work->ivar1+=3.0;

if (obj_work->ivar1 >= 360)
    obj_work->ivar1 = 0;

// set position of object 
obj_work->world_pos.x = 200*Fast_Cos(obj_work->ivar1);
obj_work->world_pos.y = 200+50*Fast_Sin(3*obj_work->ivar1);
obj_work->world_pos.z = 200*Fast_Sin(obj_work->ivar1);

// generate rotation matrix around y axis
Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(obj_work, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(obj_work, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, obj_work,0);

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// draw all the light objects to represent the position of light sources

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_light_array[INDEX_GREEN_LIGHT_INDEX]);

// set position of object to light
obj_light_array[INDEX_GREEN_LIGHT_INDEX].world_pos = lights2[POINT_LIGHT_INDEX].pos;

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_light_array[INDEX_GREEN_LIGHT_INDEX], &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_light_array[INDEX_GREEN_LIGHT_INDEX], TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_light_array[INDEX_GREEN_LIGHT_INDEX],0);

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_light_array[INDEX_WHITE_LIGHT_INDEX]);

// set position of object to light
obj_light_array[INDEX_WHITE_LIGHT_INDEX].world_pos = lights2[POINT_LIGHT2_INDEX].pos;

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_light_array[INDEX_WHITE_LIGHT_INDEX], &mrot, TRANSFORM_LOCAL_TO_TRANS,1);
 
// perform world transform
Model_To_World_OBJECT4DV2(&obj_light_array[INDEX_WHITE_LIGHT_INDEX], TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_light_array[INDEX_WHITE_LIGHT_INDEX],0);

////////////////////////////////////////////////////////////////////////////////////

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;
debug_polys_lit_per_frame = 0;

// prepare to make first pass at rendering target, so we can alpha blend in the shadows
// on the next pass

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, CLIP_POLY_X_PLANE | CLIP_POLY_Y_PLANE | CLIP_POLY_Z_PLANE );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// lock the back buffer
DDraw_Lock_Back_Surface();

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {
   // perspective mode affine texturing
      // set up rendering context
      rc.attr =    RENDER_ATTR_ZBUFFER  
              // | RENDER_ATTR_ALPHA  
              // | RENDER_ATTR_MIPMAP  
              // | RENDER_ATTR_BILERP
                 | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

   // initialize zbuffer to 0 fixed point
   Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

   // set up remainder of rendering context
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 0;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;

   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16_2(&rc);
   } // end if

// now make second rendering pass and draw shadow(s)

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

//////////////////////////////////////////////////////////////////////////
// shadow object

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&shadow_obj);

// compute terrain cell shadow is over
cell_x = (obj_work->world_pos.x  + TERRAIN_WIDTH/2) / obj_terrain.fvar1;
cell_y = (obj_work->world_pos.z  + TERRAIN_HEIGHT/2) / obj_terrain.fvar1;

// compute vertex indices into vertex list of the current quad
int v0 = cell_x + cell_y*obj_terrain.ivar2;
int v1 = v0 + 1;
int v2 = v1 + obj_terrain.ivar2; 
int v3 = v0 + obj_terrain.ivar2;   

// now simply index into table 
terrain_height = MAX(    MAX(obj_terrain.vlist_trans[v0].y, obj_terrain.vlist_trans[v1].y), 
                         MAX(obj_terrain.vlist_trans[v2].y, obj_terrain.vlist_trans[v3].y) );

// update position
shadow_obj.world_pos   = obj_work->world_pos;
shadow_obj.world_pos.y = terrain_height+10;

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// transform the local coords of the object
Transform_OBJECT4DV2(&shadow_obj, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&shadow_obj, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &shadow_obj,0);

//////////////////////////////////////////////////////////////////////////

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, CLIP_POLY_X_PLANE | CLIP_POLY_Y_PLANE | CLIP_POLY_Z_PLANE );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {
   // perspective mode affine texturing
      // set up rendering context
      rc.attr =    RENDER_ATTR_ZBUFFER  
                 | RENDER_ATTR_ALPHA  
              // | RENDER_ATTR_MIPMAP  
              // | RENDER_ATTR_BILERP
                 | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

   // initialize zbuffer to 0 fixed point
   //Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

   // set up remainder of rendering context
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 0;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;

   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16_3(&rc);
   } // end if

// unlock the back buffer
DDraw_Unlock_Back_Surface();

// draw cockpit
//Draw_BOB16(&cockpit, lpddsback);

#if 1

sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, BckFceRM [%s]", 
                                                                                 ((lighting_mode == 1) ? "ON" : "OFF"),
                                                                                 lights2[AMBIENT_LIGHT_INDEX].state,
                                                                                 lights2[INFINITE_LIGHT_INDEX].state, 
                                                                                 lights2[POINT_LIGHT_INDEX].state,
                                                                                 ((backface_mode == 1) ? "ON" : "OFF"));

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34, RGB(0,255,0), lpddsback);

// draw instructions
Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(0,255,0), lpddsback);

// should we display help
int text_y = 16;
if (help_mode==1)
    {
    // draw help menu
    Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<B>..............Toggle backface removal.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<O>..............Select different objects.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help

sprintf(work_string,"Polys Rendered: %d, Polys lit: %d", debug_polys_rendered_per_frame, debug_polys_lit_per_frame);
Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16, RGB(0,255,0), lpddsback);

sprintf(work_string,"CAM [%5.2f, %5.2f, %5.2f], CELL [%d, %d]",  cam.pos.x, cam.pos.y, cam.pos.z, cell_x, cell_y);

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16-16, RGB(0,255,0), lpddsback);

#endif

// flip the surfaces
DDraw_Flip2();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
 
} // end Game_Main
Example #13
0
// 使用3D图形算法来获取图片旋转后的四个顶点位置
void C3DTransform::GetQuadByAnimateValue(int nDegreeX, int nDegreeY, int nDegreeZ, int nZOffset, Quad* pOutQuad)
{
	// .局部坐标(以矩形的中心作为局部坐标的原点)
	float x = (float)m_nSrcWndWidth/2;
	float y = (float)m_nSrcWndHeight/2;

	POINT3D  pt3dModel[4] = { 
		{-x, y, 0}, {x, y, 0}, {x, -y, 0}, {-x, -y, 0}
	};
	POINT3D pt3DWorld[4] = {0};
	POINT3D pt3DCamera[4] = {0};

#pragma region // .局部坐标->世界坐标
	{
		// .以当前值作为角度进行旋转
		float fDegreeX = (float)nDegreeX;
		float fDegreeY = (float)nDegreeY;
		float fDegreeZ = (float)nDegreeZ;

		MATRIX_4_4  matTemp1, matTemp2;
		MATRIX_4_4  matRotateY, matRotateX, matRotateZ;

		MATRIX_4_4_PTR pLeftArg = NULL;
		if (0 != fDegreeY)
		{
			MAT_IDENTITY_4_4(&matRotateY);
			matRotateY.M00 = Fast_Cos(fDegreeY);
			matRotateY.M02 = -Fast_Sin(fDegreeY);
			matRotateY.M20 = Fast_Sin(fDegreeY);
			matRotateY.M22 = Fast_Cos(fDegreeY);
			
			pLeftArg = &matRotateY;
		}

		if (0 != fDegreeX)
		{

			MAT_IDENTITY_4_4(&matRotateX);
			matRotateX.M11 = Fast_Cos(fDegreeX);
			matRotateX.M12 = Fast_Sin(fDegreeX);
			matRotateX.M21 = -Fast_Sin(fDegreeX);
			matRotateX.M22 = Fast_Cos(fDegreeX);

			if (NULL == pLeftArg)
			{
				pLeftArg = &matRotateX;
			}
			else
			{
				Mat_Mul_4X4(pLeftArg, &matRotateX, &matTemp1);
				pLeftArg = &matTemp1;
			}
		}

		if (0 != fDegreeZ)
		{

			MAT_IDENTITY_4_4(&matRotateZ);
			matRotateZ.M00 = Fast_Cos(fDegreeZ);
			matRotateZ.M01 = Fast_Sin(fDegreeZ);
			matRotateZ.M10 = -Fast_Sin(fDegreeZ);
			matRotateZ.M11 = Fast_Cos(fDegreeZ);

			if (NULL == pLeftArg)
			{
				pLeftArg = &matRotateZ;
			}
			else
			{
				Mat_Mul_4X4(pLeftArg, &matRotateZ, &matTemp2);
				pLeftArg = &matTemp2;
			}
		}

		if (pLeftArg)
		{
			for (int i = 0; i < 4; i++)
				Mat_Mul_VECTOR3D_4X4(&pt3dModel[i], pLeftArg, &pt3DWorld[i]);
		}
		else
		{
			for (int i = 0; i < 4; i++)
			{
				pt3DWorld[i].x = pt3dModel[i].x;
				pt3DWorld[i].y = pt3dModel[i].y;
			}
		}

		// .由于仍然是平移到世界坐标的 (0,0,0)位置,因此不用计算平移
		for (int i = 0; i < 4; i++)
		{
			pt3DWorld[i].z += nZOffset;
		}
	}
#pragma endregion

	// 相机位置(这里默认将视平面放在与矩形所在面的同一位置,这样透视出来的坐标直接就可以
	// 当成屏幕坐标来用了,省了一步操作。
	// 但是需要注意的是,如果nCameraPos过小的话看到的图像就会缩小,
	CAMERA camerpos = {0};
	float  fCameraPos = 2000.0f;  // 相机位置
	float  d = fCameraPos;      // 相机与视平面的距离。将两值设成一样,避免了一次到屏幕坐标的转换

	VECTOR4D_INITXYZ(&camerpos.WorldPos, 0,0, -fCameraPos);

#pragma region // 世界坐标转换为相机坐标
	{
		// 平移矩阵
		MATRIX_4_4  matCameraTrans = {0};
		MAT_IDENTITY_4_4(&matCameraTrans);
		matCameraTrans.M30 = -camerpos.WorldPos.x;
		matCameraTrans.M31 = -camerpos.WorldPos.y;
		matCameraTrans.M32 = -camerpos.WorldPos.z;
		
		// 相机角度为0,不旋转

		for (int i = 0; i < 4; i++)
		{
			Mat_Mul_VECTOR3D_4X4(&pt3DWorld[i], &matCameraTrans, &pt3DCamera[i]);
		}
	}
#pragma endregion

#pragma region // 相机坐标转换为透视坐标
	POINT3D pt3DPerspectivePos[4];
	for (int i = 0; i < 4; i++)
	{
		float z = pt3DCamera[i].z;  // 这里的z是用于和d相比的距离,不是坐标. 当d值取的比较小时,会导致z为负
		float i_z = 1/z;
		if (pt3DCamera[i].z != 0)
		{
			pt3DPerspectivePos[i].x = d * pt3DCamera[i].x * i_z;  // nCameraPos相当于d
			pt3DPerspectivePos[i].y = d * pt3DCamera[i].y * i_z;  // 
		}
	}
#pragma endregion

	// 转换到屏幕坐标上
	for (int i = 0; i < 4; ++i)
	{
		pt3DPerspectivePos[i].x += (m_nSrcWndWidth>>1);
		pt3DPerspectivePos[i].y = -pt3DPerspectivePos[i].y;
		pt3DPerspectivePos[i].y += (m_nSrcWndHeight>>1);
	}

	// 赋值给返回值
	for (int i = 0; i < 4; i++)
	{
		pOutQuad->pos[2*i]   = (int)pt3DPerspectivePos[i].x;
		pOutQuad->pos[2*i+1] = (int)pt3DPerspectivePos[i].y;
	}
}
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

    static MATRIX4X4 mrot;   // general rotation matrix

    static float plight_ang = 0,
                 slight_ang = 0; // angles for light motion

// use these to rotate objects
    static float x_ang = 0, y_ang = 0, z_ang = 0;

// state variables for different rendering modes and help
    static int wireframe_mode = 1;
    static int backface_mode  = 1;
    static int lighting_mode  = 1;
    static int help_mode      = 1;
    static int zsort_mode     = 1;
    static int x_clip_mode    = 1;
    static int y_clip_mode    = 1;
    static int z_clip_mode    = 1;
    static int z_buffer_mode  = 0;
    static int display_mode   = 1;
    static char *z_buffer_modes[3] = {"Z Buffering", "1/Z Buffering", "NO buffering"};

    char work_string[256]; // temp string

    int index; // looping var

// start the timing clock
    Start_Clock();

// clear the drawing surface
//DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
//Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(250,190,80), lpddsback);
    lpddsback->Blt(NULL, background.images[0], NULL, DDBLT_WAIT, NULL);

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT*.5, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(190,190,230), lpddsback);

// read keyboard and other devices here
    DInput_Read_Keyboard();

// game logic here...

// reset the render list
    Reset_RENDERLIST4DV2(&rend_list);

// modes and lights

// wireframe mode
    if (keyboard_state[DIK_W])
    {
        // toggle wireframe mode
        if (++wireframe_mode > 1)
            wireframe_mode=0;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// backface removal
    if (keyboard_state[DIK_B])
    {
        // toggle backface removal
        backface_mode = -backface_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// lighting
    if (keyboard_state[DIK_L])
    {
        // toggle lighting engine completely
        lighting_mode = -lighting_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle ambient light
    if (keyboard_state[DIK_A])
    {
        // toggle ambient light
        if (lights2[AMBIENT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle infinite light
    if (keyboard_state[DIK_I])
    {
        // toggle ambient light
        if (lights2[INFINITE_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle point light
    if (keyboard_state[DIK_P])
    {
        // toggle point light
        if (lights2[POINT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// toggle spot light
    if (keyboard_state[DIK_S])
    {
        // toggle spot light
        if (lights2[SPOT_LIGHT2_INDEX].state == LIGHTV2_STATE_ON)
            lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// help menu
    if (keyboard_state[DIK_H])
    {
        // toggle help menu
        help_mode = -help_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// z-sorting
    if (keyboard_state[DIK_S])
    {
        // toggle z sorting
        zsort_mode = -zsort_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// z buffer
// 0 - z buffer
// 1 - 1/z buffer
// 2 - no buffer
    if (keyboard_state[DIK_Z])
    {
        // toggle z buffer
        if (++z_buffer_mode > 2)
            z_buffer_mode = 0;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// display mode
    if (keyboard_state[DIK_D])
    {
        // toggle display mode
        display_mode = -display_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// forward/backward
    if (keyboard_state[DIK_UP])
    {
        // move forward
        if ( (cam_speed+=1) > MAX_SPEED) cam_speed = MAX_SPEED;
    } // end if
    else if (keyboard_state[DIK_DOWN])
    {
        // move backward
        if ((cam_speed-=1) < -MAX_SPEED) cam_speed = -MAX_SPEED;
    } // end if

// rotate around y axis or yaw
    if (keyboard_state[DIK_RIGHT])
    {
        cam.dir.y+=5;
    } // end if

    if (keyboard_state[DIK_LEFT])
    {
        cam.dir.y-=5;
    } // end if


// move to next object
    if (keyboard_state[DIK_N])
    {
        if (++curr_object >= NUM_OBJECTS)
            curr_object = 0;

        // update pointer
        obj_work = &obj_array[curr_object];
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// decelerate camera
    if (cam_speed > (CAM_DECEL) ) cam_speed-=CAM_DECEL;
    else if (cam_speed < (-CAM_DECEL) ) cam_speed+=CAM_DECEL;
    else
        cam_speed = 0;

// move camera
    cam.pos.x += cam_speed*Fast_Sin(cam.dir.y);
    cam.pos.z += cam_speed*Fast_Cos(cam.dir.y);

// move point light source in ellipse around game world
    lights2[POINT_LIGHT_INDEX].pos.x = 1000*Fast_Cos(plight_ang);
    lights2[POINT_LIGHT_INDEX].pos.y = 100;
    lights2[POINT_LIGHT_INDEX].pos.z = 1000*Fast_Sin(plight_ang);

    if ((plight_ang+=3) > 360)
        plight_ang = 0;

// move spot light source in ellipse around game world
    lights2[SPOT_LIGHT2_INDEX].pos.x = 1000*Fast_Cos(slight_ang);
    lights2[SPOT_LIGHT2_INDEX].pos.y = 200;
    lights2[SPOT_LIGHT2_INDEX].pos.z = 1000*Fast_Sin(slight_ang);

    if ((slight_ang-=5) < 0)
        slight_ang = 360;

    obj_work->world_pos.x = cam.pos.x + 150*Fast_Sin(cam.dir.y);
    obj_work->world_pos.y = cam.pos.y + 0;
    obj_work->world_pos.z = cam.pos.z + 150*Fast_Cos(cam.dir.y);

// generate camera matrix
    Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

////////////////////////////////////////////////////////
// insert the scenery into universe
    for (index = 0; index < NUM_SCENE_OBJECTS; index++)
    {
        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV2(&obj_scene);

        // set position of tower
        obj_scene.world_pos.x = scene_objects[index].x;
        obj_scene.world_pos.y = scene_objects[index].y;
        obj_scene.world_pos.z = scene_objects[index].z;

        // attempt to cull object
        if (!Cull_OBJECT4DV2(&obj_scene, &cam, CULL_OBJECT_XYZ_PLANES))
        {
            MAT_IDENTITY_4X4(&mrot);

            // rotate the local coords of the object
            Transform_OBJECT4DV2(&obj_scene, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

            // perform world transform
            Model_To_World_OBJECT4DV2(&obj_scene, TRANSFORM_TRANS_ONLY);

            // insert the object into render list
            Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_scene,0);

        } // end if

    } // end for

///////////////////////////////////////////////////////////////
// insert the player object into universe

// reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV2(obj_work);

// generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(x_ang, cam.dir.y + y_ang, z_ang, &mrot);

//MAT_IDENTITY_4X4(&mrot);

// rotate the local coords of the object
    Transform_OBJECT4DV2(obj_work, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
    Model_To_World_OBJECT4DV2(obj_work, TRANSFORM_TRANS_ONLY);

// insert the object into render list
    Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, obj_work,0);

// update rotation angles
    if ((x_ang+=.2) > 360) x_ang = 0;
    if ((y_ang+=.4) > 360) y_ang = 0;
    if ((z_ang+=.8) > 360) z_ang = 0;

// reset number of polys rendered
    debug_polys_rendered_per_frame = 0;
    debug_polys_lit_per_frame = 0;

// remove backfaces
    if (backface_mode==1)
        Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
    World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
    Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, ((x_clip_mode == 1) ? CLIP_POLY_X_PLANE : 0) |
                              ((y_clip_mode == 1) ? CLIP_POLY_Y_PLANE : 0) |
                              ((z_clip_mode == 1) ? CLIP_POLY_Z_PLANE : 0) );

// light scene all at once
    if (lighting_mode==1)
    {
        Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
        Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
    } // end if

// sort the polygon list (hurry up!)
    if (zsort_mode == 1)
        Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
    Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
    Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// lock the back buffer
    DDraw_Lock_Back_Surface();

// reset number of polys rendered
    debug_polys_rendered_per_frame = 0;

// render the renderinglist
    if (wireframe_mode  == 0)
        Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
    else if (wireframe_mode  == 1)
    {
        // z buffer mode
        if (z_buffer_mode == 0)
        {
            // initialize zbuffer to 16000 fixed point
            Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

            // set up rendering context
            rc.attr =    RENDER_ATTR_ZBUFFER
                         // | RENDER_ATTR_ALPHA
                         // | RENDER_ATTR_MIPMAP
                         // | RENDER_ATTR_BILERP
                         | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

        } // end if
        else // 1/z buffer mode
            if (z_buffer_mode == 1)
            {
                // initialize 1/z buffer to 0 fixed point
                Clear_Zbuffer(&zbuffer, (0 << FIXP16_SHIFT));

                // set up rendering context
                rc.attr =    RENDER_ATTR_INVZBUFFER
                             // | RENDER_ATTR_ALPHA
                             // | RENDER_ATTR_MIPMAP
                             // | RENDER_ATTR_BILERP
                             | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;
            } // end if
            else // no buffering mode
                if (z_buffer_mode == 2)
                {
                    // set up rendering context
                    rc.attr =    RENDER_ATTR_NOBUFFER
                                 // | RENDER_ATTR_ALPHA
                                 // | RENDER_ATTR_MIPMAP
                                 // | RENDER_ATTR_BILERP
                                 | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;
                } // end if

        // set up remainder of rendering context
        rc.video_buffer   = back_buffer;
        rc.lpitch         = back_lpitch;
        rc.mip_dist       = 4500;
        rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
        rc.zpitch         = WINDOW_WIDTH*4;
        rc.rend_list      = &rend_list;
        rc.texture_dist   = 0;
        rc.alpha_override = -1;

        // render scene
        Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16(&rc);
    } // end if

// display z buffer graphically (sorta)
    if (display_mode==-1)
    {
// use z buffer visualization mode
// copy each line of the z buffer into the back buffer and translate each z pixel
// into a color
        USHORT *screen_ptr = (USHORT *)back_buffer;
        UINT   *zb_ptr    =  (UINT *)zbuffer.zbuffer;

        for (int y = 0; y < WINDOW_HEIGHT; y++)
        {
            for (int x = 0; x < WINDOW_WIDTH; x++)
            {
                // z buffer is 32 bit, so be carefull
                UINT zpixel = zb_ptr[x + y*WINDOW_WIDTH];
                zpixel = (zpixel/4096 & 0xffff);
                screen_ptr[x + y* (back_lpitch >> 1)] = (USHORT)zpixel;
            } // end for
        } // end for y

    } // end if
Example #15
0
static void display(void)
{
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    static float view_angle = 0;
    static float camera_distance = 6000;
    static VECTOR4D pos = { 0, 0, 0, 0 };
    static float tank_speed;
    static float turning = 0;

    if (keyboard_state[VK_SPACE])
        tank_speed = 5 * TANK_SPEED;
    else
        tank_speed = TANK_SPEED;

    // forward/backward
    if (keyboard_state['W'] || keyboard_state['w'])
    {
        // move forward
        cam.pos.x -= tank_speed*Fast_Sin(cam.dir.y);
        cam.pos.z -= tank_speed*Fast_Cos(cam.dir.y);
    } // end if

    if (keyboard_state['S'] || keyboard_state['s'])
    {
        // move backward
        cam.pos.x += tank_speed*Fast_Sin(cam.dir.y);
        cam.pos.z += tank_speed*Fast_Cos(cam.dir.y);
    } // end if

    // rotate
    if (keyboard_state['D'] || keyboard_state['d'])
    {
        cam.dir.y -= TANK_ROTATE_SPEED;

        // add a little turn to object
        if ((turning -= 2) < -15)
            turning = -15;

    } // end if

    if (keyboard_state['a'] || keyboard_state['A'])
    {
        cam.dir.y += TANK_ROTATE_SPEED;

        // add a little turn to object
        if ((turning += 2) > 15)
            turning = 15;

    } // end if
    else // center heading again
    {
        if (turning > 0)
            turning -= 1;
        else if (turning < 0)
            turning += 1;

    } // end else

    //cam.dir.y += 0.05;
    //if (cam.dir.y > 360)
    //	cam.dir.y = 0;

    //Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);
    //Rotate_XYZ_OBJECT4DV1(&obj, 0, 1, 0);
    //Model_To_World_OBJECT4DV1(&obj);
    //Reset_OBJECT4DV1(&obj);
    //Cull_OBJECT4DV1(&obj, &cam, CULL_OBJECT_X_PLANE | CULL_OBJECT_Y_PLANE | CULL_OBJECT_Z_PLANE);
    //Remove_Backfaces_OBJECT4DV1(&obj, &cam);
    //World_To_Camera_OBJECT4DV1(&obj, &cam);
    //Camera_To_Perspective_Screen_OBJECT4DV1(&obj, &cam);
    //memset(buffer, 0, sizeof(UINT)* WINDOW_WIDTH * WINDOW_HEIGHT);
    for (int i = 0; i < g_width * g_height; ++i)
    {
        buffer[i] = 0;// 0xffffffff;
    }
    //Draw_OBJECT4DV1_Solid(&obj, (UCHAR *)buffer, WINDOW_WIDTH);
    //Draw_OBJECT4DV1_Wire(&obj, (UCHAR *)buffer, WINDOW_WIDTH);
    //glBindVertexArray(VAOs[VAO_1]);
    //glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    //glBufferData(GL_ARRAY_BUFFER, sizeof(POINT4D)* obj.num_vertices, obj.vlist_trans, GL_DYNAMIC_DRAW);
    ////glDrawArrays(GL_TRIANGLES, 0, obj.num_polys);

    //int polys = 0;
    //for (int i = 0, j = 0; i < obj.num_polys; ++i)
    //{Z
    //	if (!(obj.plist[i].state & POLY4DV1_STATE_ACTIVE) || (obj.plist[i].state & POLY4DV1_STATE_CLIPPED)
    //		|| (obj.plist[i].state & POLY4DV1_STATE_BACKFACE))
    //		continue;
    //	indexBuffer[j++] = obj.plist[i].vert[0];
    //	indexBuffer[j++] = obj.plist[i].vert[1];
    //	indexBuffer[j++] = obj.plist[i].vert[2];
    //	++polys;
    //}

    //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* 3 * polys, indexBuffer, GL_STATIC_DRAW);

    //glDrawElements(GL_TRIANGLES, polys * 3, GL_UNSIGNED_INT, 0);
    //glFlush();

    Reset_RENDERLIST4DV1(&rend_list);

    static MATRIX4X4 mrot;   // general rotation matrix

    // generate camera matrix
    Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

    // insert the tanks in the world
    for (int index = 0; index < NUM_TANKS; index++)
    {
        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV1(&obj_tank);

        // generate rotation matrix around y axis
        Build_XYZ_Rotation_MATRIX4X4(0, tanks[index].y, 0, &mrot);

        // rotate the local coords of the object
        Transform_OBJECT4DV1(&obj_tank, &mrot, TRANSFORM_LOCAL_TO_TRANS, 1);

        // set position of tank
        obj_tank.world_pos.x = tanks[index].x;
        obj_tank.world_pos.y = tanks[index].y;
        obj_tank.world_pos.z = tanks[index].z;

        // attempt to cull object
        if (!Cull_OBJECT4DV1(&obj_tank, &cam, CULL_OBJECT_XYZ_PLANES))
        {
            // if we get here then the object is visible at this world position
            // so we can insert it into the rendering list
            // perform local/model to world transform
            Model_To_World_OBJECT4DV1(&obj_tank, TRANSFORM_TRANS_ONLY);
            // insert the object into render list
            Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_tank);
        } // end if

    } // end for

    // insert the player into the world
    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV1(&obj_player);

    // set position of tank
    obj_player.world_pos.x = cam.pos.x - 300 * Fast_Sin(cam.dir.y);
    obj_player.world_pos.y = cam.pos.y - 70;
    obj_player.world_pos.z = cam.pos.z - 300 * Fast_Cos(cam.dir.y);

    // generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(0, cam.dir.y + turning, 0, &mrot);


    //lights[0].pos.x = 300 * Fast_Sin(cam.dir.y);
    //lights[0].pos.z = 300 * Fast_Cos(cam.dir.y);

    // rotate the local coords of the object
    Transform_OBJECT4DV1(&obj_player, &mrot, TRANSFORM_LOCAL_TO_TRANS, 1);

    // perform world transform
    Model_To_World_OBJECT4DV1(&obj_player, TRANSFORM_TRANS_ONLY);

    Cull_OBJECT4DV1(&obj_player, &cam, CULL_OBJECT_XYZ_PLANES);

    // insert the object into render list
    Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_player);

    // insert the towers in the world
    for (int index = 0; index < NUM_TOWERS; index++)
    {
        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV1(&obj_tower);

        // set position of tower
        obj_tower.world_pos.x = towers[index].x;
        obj_tower.world_pos.y = towers[index].y;
        obj_tower.world_pos.z = towers[index].z;

        // attempt to cull object
        if (!Cull_OBJECT4DV1(&obj_tower, &cam, CULL_OBJECT_XYZ_PLANES))
        {
            // if we get here then the object is visible at this world position
            // so we can insert it into the rendering list
            // perform local/model to world transform
            Model_To_World_OBJECT4DV1(&obj_tower);

            // insert the object into render list
            Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_tower);
        } // end if

    } // end for

    // seed number generator so that modulation of markers is always the same
    srand(13);

    // insert the ground markers into the world
    for (int index_x = 0; index_x < NUM_POINTS_X; index_x++)
        for (int index_z = 0; index_z < NUM_POINTS_Z; index_z++)
        {
            // reset the object (this only matters for backface and object removal)
            Reset_OBJECT4DV1(&obj_marker);

            // set position of tower
            obj_marker.world_pos.x = RAND_RANGE(-100, 100) - UNIVERSE_RADIUS + index_x*POINT_SIZE;
            obj_marker.world_pos.y = obj_marker.max_radius;
            obj_marker.world_pos.z = RAND_RANGE(-100, 100) - UNIVERSE_RADIUS + index_z*POINT_SIZE;

            // attempt to cull object
            if (!Cull_OBJECT4DV1(&obj_marker, &cam, CULL_OBJECT_XYZ_PLANES))
            {
                // if we get here then the object is visible at this world position
                // so we can insert it into the rendering list
                // perform local/model to world transform
                Model_To_World_OBJECT4DV1(&obj_marker);

                // insert the object into render list
                Insert_OBJECT4DV1_RENDERLIST4DV1(&rend_list, &obj_marker);
            } // end if

        } // end for

    // remove backfaces
    Remove_Backfaces_RENDERLIST4DV1(&rend_list, &cam);

    // apply world to camera transform
    World_To_Camera_RENDERLIST4DV1(&rend_list, &cam);

    Sort_RENDERLIST4DV1(&rend_list, SORT_POLYLIST_NEARZ);

    Light_RENDERLIST4DV1_World(&rend_list, &cam, lights, 1);

    Camera_To_Perspective_Screen_RENDERLIST4DV1(&rend_list, &cam);

    Draw_RENDERLIST4DV1_Wire(&rend_list, (UCHAR *)buffer, g_width);
    //Draw_RENDERLIST4DV1_Solid(&rend_list, (UCHAR *)buffer, g_width);

    glDrawPixels(g_width, g_height, GL_BGRA, GL_UNSIGNED_BYTE, buffer);

    glutSwapBuffers();
}
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

// these are used to create a circling camera
static float view_angle = 0; 
static float camera_distance = 6000;
static VECTOR4D pos = {0,0,0,0};
static float tank_speed;
static float turning = 0;
// state variables for different rendering modes and help
static int wireframe_mode = 1;
static int backface_mode  = 1;
static int lighting_mode  = 1;
static int help_mode      = 1;
static int zsort_mode     = 1;

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
//DDraw_Fill_Surface(lpddsback, 0);

#if 1

// draw the sky
//Draw_Rectangle(0,0, WINDOW_WIDTH-1, WINDOW_HEIGHT/2, 166, lpddsback);
//Draw_Rectangle(0,WINDOW_HEIGHT/2, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, rgblookup[RGB16Bit565(115,42,16)], lpddsback);
//Draw_Rectangle(0,0, WINDOW_WIDTH-1, WINDOW_HEIGHT/2, rgblookup[RGB16Bit565(0,140,192)], lpddsback);
//Draw_Rectangle(0,0, WINDOW_WIDTH-1, WINDOW_HEIGHT/2, RGB16Bit(0,140,192), lpddsback);
Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT/2, RGB16Bit(0,35,50), lpddsback);


// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT/2, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, 28, lpddsback);
//Draw_Rectangle(0,WINDOW_HEIGHT/2, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, rgblookup[RGB16Bit565(115,42,16)], lpddsback);
//Draw_Rectangle(0,WINDOW_HEIGHT/2, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, RGB16Bit(103,62,3), lpddsback);
Draw_Rectangle(0,WINDOW_HEIGHT/2-1, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(20,12,0), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// allow user to move camera

// turbo
if (keyboard_state[DIK_SPACE])
    tank_speed = 5*TANK_SPEED;
else
    tank_speed = TANK_SPEED;

// forward/backward
if (keyboard_state[DIK_UP])
   {
   // move forward
   cam.pos.x += tank_speed*Fast_Sin(cam.dir.y);
   cam.pos.z += tank_speed*Fast_Cos(cam.dir.y);
   } // end if

if (keyboard_state[DIK_DOWN])
   {
   // move backward
   cam.pos.x -= tank_speed*Fast_Sin(cam.dir.y);
   cam.pos.z -= tank_speed*Fast_Cos(cam.dir.y);
   } // end if

// rotate
if (keyboard_state[DIK_RIGHT])
   {
   cam.dir.y+=3;
   
   // add a little turn to object
   if ((turning+=2) > 25)
      turning=25;
   } // end if

if (keyboard_state[DIK_LEFT])
   {
   cam.dir.y-=3;

   // add a little turn to object
   if ((turning-=2) < -25)
      turning=-25;

   } // end if
else // center heading again
   {
   if (turning > 0)
       turning-=1;
   else
   if (turning < 0)
       turning+=1;

   } // end else

// modes and lights

// wireframe mode
if (keyboard_state[DIK_W])
   {
   // toggle wireframe mode
   if (++wireframe_mode > 1)
       wireframe_mode=0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// backface removal
if (keyboard_state[DIK_B])
   {
   // toggle backface removal
   backface_mode = -backface_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// lighting
if (keyboard_state[DIK_L])
   {
   // toggle lighting engine completely
   lighting_mode = -lighting_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle ambient light
if (keyboard_state[DIK_A])
   {
   // toggle ambient light
   if (lights[AMBIENT_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle infinite light
if (keyboard_state[DIK_I])
   {
   // toggle ambient light
   if (lights[INFINITE_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle point light
if (keyboard_state[DIK_P])
   {
   // toggle point light
   if (lights[POINT_LIGHT_INDEX].state == LIGHTV1_STATE_ON)
      lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// toggle spot light
if (keyboard_state[DIK_S])
   {
   // toggle spot light
   if (lights[SPOT_LIGHT2_INDEX].state == LIGHTV1_STATE_ON)
      lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_OFF;
   else
      lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// help menu
if (keyboard_state[DIK_H])
   {
   // toggle help menu 
   help_mode = -help_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// z-sorting
if (keyboard_state[DIK_Z])
   {
   // toggle z sorting
   zsort_mode = -zsort_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

static float plight_ang = 0, slight_ang = 0; // angles for light motion

// move point light source in ellipse around game world
lights[POINT_LIGHT_INDEX].pos.x = 4000*Fast_Cos(plight_ang);
lights[POINT_LIGHT_INDEX].pos.y = 200;
lights[POINT_LIGHT_INDEX].pos.z = 4000*Fast_Sin(plight_ang);

if ((plight_ang+=3) > 360)
    plight_ang = 0;

// move spot light source in ellipse around game world
lights[SPOT_LIGHT2_INDEX].pos.x = 2000*Fast_Cos(slight_ang);
lights[SPOT_LIGHT2_INDEX].pos.y = 200;
lights[SPOT_LIGHT2_INDEX].pos.z = 2000*Fast_Sin(slight_ang);

if ((slight_ang-=5) < 0)
    slight_ang = 360;

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

// insert the player into the world
// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_player);

// set position of tank

obj_player.world_pos.x = cam.pos.x+300*Fast_Sin(cam.dir.y);
obj_player.world_pos.y = cam.pos.y-70;
obj_player.world_pos.z = cam.pos.z+300*Fast_Cos(cam.dir.y);


// generate rotation matrix around y axis
static int turn=0; 
Build_XYZ_Rotation_MATRIX4X4(1, cam.dir.y+turning, 2, &mrot);

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_player, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_player, TRANSFORM_TRANS_ONLY);

//Light_OBJECT4DV2_World16(&obj_player, &cam, lights, 4);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_player,0);

#if 1

//////////////////////////////////////////////////////////

// insert the tanks in the world
for (index = 0; index < NUM_TANKS; index++)
    {
    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV2(&obj_tank);

    // generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(0, tanks[index].w, 0, &mrot);

    // rotate the local coords of the object
    Transform_OBJECT4DV2(&obj_tank, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

    // set position of tank
    obj_tank.world_pos.x = tanks[index].x;
    obj_tank.world_pos.y = tanks[index].y; 
    obj_tank.world_pos.z = tanks[index].z; 

    // attempt to cull object   
    if (!Cull_OBJECT4DV2(&obj_tank, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       // if we get here then the object is visible at this world position
       // so we can insert it into the rendering list
       // perform local/model to world transform
       Model_To_World_OBJECT4DV2(&obj_tank, TRANSFORM_TRANS_ONLY);

       //Light_OBJECT4DV2_World16(&obj_tank, &cam, lights, 4);

       // insert the object into render list
       Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_tank,0);
       } // end if
 
    } // end for

////////////////////////////////////////////////////////


// insert the towers in the world
for (index = 0; index < NUM_TOWERS; index++)
    {
    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV2(&obj_tower);

    // set position of tower
    obj_tower.world_pos.x = towers[index].x;
    obj_tower.world_pos.y = towers[index].y;
    obj_tower.world_pos.z = towers[index].z;

    // attempt to cull object   
    if (!Cull_OBJECT4DV2(&obj_tower, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       // if we get here then the object is visible at this world position
       // so we can insert it into the rendering list
       // perform local/model to world transform
       Model_To_World_OBJECT4DV2(&obj_tower);

       //Light_OBJECT4DV2_World16(&obj_tower, &cam, lights, 4);

       // insert the object into render list
       Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_tower,0);

       } // end if
 
    } // end for

///////////////////////////////////////////////////////////////

// seed number generator so that modulation of markers is always the same
srand(13);

static int mcount = 0, mdir = 2;

mcount+=mdir;
if (mcount > 200 || mcount < -200) { mdir=-mdir; mcount+=mdir; }

// insert the ground markers into the world
for (int index_x = 0; index_x < NUM_POINTS_X; index_x++)
    for (int index_z = 0; index_z < NUM_POINTS_Z; index_z++)
        {
        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV2(&obj_marker);

        // set position of tower
        obj_marker.world_pos.x = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_x*POINT_SIZE;
        obj_marker.world_pos.y = obj_marker.max_radius[0] + 50*Fast_Sin(index_x*10+Fast_Sin(index_z)+mcount);
        obj_marker.world_pos.z = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_z*POINT_SIZE;

        // attempt to cull object   
        if (!Cull_OBJECT4DV2(&obj_marker, &cam, CULL_OBJECT_XYZ_PLANES))
           {
           // if we get here then the object is visible at this world position
           // so we can insert it into the rendering list
           // perform local/model to world transform
           Model_To_World_OBJECT4DV2(&obj_marker);

           //Light_OBJECT4DV2_World16(&obj_marker, &cam, lights, 4);

           // insert the object into render list
           Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_marker,0);

           } // end if

        } // end for

////////////////////////////////////////////////////////////////////////

#endif

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// light scene all at once 
if (lighting_mode==1)
   Light_RENDERLIST4DV2_World16(&rend_list, &cam, lights, 4);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

sprintf(work_string,"pos:[%f, %f, %f] heading:[%f] elev:[%f], polys[%d]", 
        cam.pos.x, cam.pos.y, cam.pos.z, cam.dir.y, cam.dir.x, debug_polys_rendered_per_frame); 

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-20, RGB(0,255,0), lpddsback);

sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, Spot=%d | Zsort [%s], BckFceRM [%s]", 
                                                                                 ((lighting_mode == 1) ? "ON" : "OFF"),
                                                                                 lights[AMBIENT_LIGHT_INDEX].state,
                                                                                 lights[INFINITE_LIGHT_INDEX].state, 
                                                                                 lights[POINT_LIGHT_INDEX].state,
                                                                                 lights[SPOT_LIGHT2_INDEX].state,
                                                                                 ((zsort_mode == 1) ? "ON" : "OFF"),
                                                                                 ((backface_mode == 1) ? "ON" : "OFF"));

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34, RGB(0,255,0), lpddsback);

// draw instructions
Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(0,255,0), lpddsback);

// should we display help
int text_y = 16;
if (help_mode==1)
    {
    // draw help menu
    Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<S>..............Toggle spot light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<B>..............Toggle backface removal.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<RIGHT ARROW>....Rotate player right.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<LEFT ARROW>.....Rotate player left.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<UP ARROW>.......Move player forward.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<DOWN ARROW>.....Move player backward.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<SPACE BAR>......Turbo.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help

// lock the back buffer
DDraw_Lock_Back_Surface();

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   Draw_RENDERLIST4DV2_Solid16(&rend_list, back_buffer, back_lpitch);

#endif

// unlock the back buffer
DDraw_Unlock_Back_Surface();

// flip the surfaces
DDraw_Flip();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
 
} // end Game_Main
Example #17
0
void Build_CAM4D_Matrix_Euler(CAM4D_PTR cam, int cam_rot_seq)
{
	Matrix4d mt_inv,	// camera transpose
		mx_inv,			// camera rotate around x-axis
		my_inv,
		mz_inv,
		mrot;			// product of all rotation;

	mt_inv << 1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0,
		-cam->pos(0), -cam->pos(1), -cam->pos(2), 1;

	float theta_x = cam->dir(0);
	float theta_y = cam->dir(1);
	float theta_z = cam->dir(2);

	float cos_theta = Fast_Cos(theta_x);
	float sin_theta = -Fast_Sin(theta_x);
	mx_inv << 1, 0, 0, 0,
		0, cos_theta, sin_theta, 0,
		0, -sin_theta, cos_theta, 0,
		0, 0, 0, 1;

	cos_theta = Fast_Cos(theta_y);
	sin_theta = -Fast_Sin(theta_y);
	my_inv << cos_theta, 0, -sin_theta, 0,
		0, 1, 0, 0,
		sin_theta, 0, cos_theta, 0,
		0, 0, 0, 1;

	cos_theta = Fast_Cos(theta_z);
	sin_theta = -Fast_Sin(theta_z);
	mz_inv << cos_theta, sin_theta, 0, 0,
		-sin_theta, cos_theta, 0, 0,
		0, 0, 1, 0,
		0, 0, 0, 1;

	switch (cam_rot_seq)
	{
	case CAM_ROT_SEQ_XYZ: {
		mrot = mz_inv * my_inv * mx_inv;
	} break;
	case CAM_ROT_SEQ_YXZ: {
		mrot = mz_inv * mx_inv * my_inv;
	} break;
	case CAM_ROT_SEQ_XZY: {
		mrot = my_inv * mz_inv * mx_inv;
	} break;
	case CAM_ROT_SEQ_YZX: {
		mrot = mx_inv * mz_inv * my_inv;
	} break;
	case CAM_ROT_SEQ_ZYX: {
		mrot = mx_inv * my_inv * mz_inv;
	} break;
	case CAM_ROT_SEQ_ZXY: {
		mrot = my_inv * mx_inv * mz_inv;
	} break;
	default:
		break;
	}
	cam->mcam = mrot * mt_inv;
}
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

static float plight_ang = 0, 
             slight_ang = 0; // angles for light motion

// use these to rotate objects
static float x_ang = 0, y_ang = 0, z_ang = 0;

// state variables for different rendering modes and help
static int wireframe_mode   = 1;
static int backface_mode    = 1;
static int lighting_mode    = 1;
static int help_mode        = 1;
static int zsort_mode       = -1;
static int x_clip_mode      = 1;
static int y_clip_mode      = 1;
static int z_clip_mode      = 1;

static float hl = 300, // artificial light height
             ks = 1.25; // generic scaling factor to make things look good

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
Draw_Rectangle(0,0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, RGB16Bit(50,50,200), lpddsback);

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT*.38, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(25,50,110), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// modes and lights

// wireframe mode
if (keyboard_state[DIK_W]) 
   {
   // toggle wireframe mode
   if (++wireframe_mode > 1)
       wireframe_mode=0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// backface removal
if (keyboard_state[DIK_B])
   {
   // toggle backface removal
   backface_mode = -backface_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// lighting
if (keyboard_state[DIK_L])
   {
   // toggle lighting engine completely
   lighting_mode = -lighting_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle ambient light
if (keyboard_state[DIK_A])
   {
   // toggle ambient light
   if (lights2[AMBIENT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle infinite light
if (keyboard_state[DIK_I])
   {
   // toggle ambient light
   if (lights2[INFINITE_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle point light
if (keyboard_state[DIK_P])
   {
   // toggle point light
   if (lights2[POINT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   // toggle point light
   if (lights2[POINT_LIGHT2_INDEX].state == LIGHTV2_STATE_ON)
      lights2[POINT_LIGHT2_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[POINT_LIGHT2_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// help menu
if (keyboard_state[DIK_H])
   {
   // toggle help menu 
   help_mode = -help_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// z-sorting
if (keyboard_state[DIK_Z])
   {
   // toggle z sorting
   zsort_mode = -zsort_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// next animation
if (keyboard_state[DIK_2])
   {
   if (++obj_md2.anim_state >= NUM_MD2_ANIMATIONS)
      obj_md2.anim_state = 0;  

   Set_Animation_MD2(&obj_md2, obj_md2.anim_state, MD2_ANIM_SINGLE_SHOT);

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// previous animation
if (keyboard_state[DIK_1])
   {
   if (--obj_md2.anim_state < 0)
      obj_md2.anim_state = NUM_MD2_ANIMATIONS-1;  

   Set_Animation_MD2(&obj_md2, obj_md2.anim_state, MD2_ANIM_SINGLE_SHOT);

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// replay animation
if (keyboard_state[DIK_3])
   {
   Set_Animation_MD2(&obj_md2, obj_md2.anim_state, MD2_ANIM_SINGLE_SHOT);
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// replay animation
if (keyboard_state[DIK_4])
   {
   Set_Animation_MD2(&obj_md2, obj_md2.anim_state, MD2_ANIM_LOOP);
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// forward/backward
if (keyboard_state[DIK_UP])
   {
   // move forward
   if ( (cam_speed+=1) > MAX_SPEED) cam_speed = MAX_SPEED;
   } // end if
else
if (keyboard_state[DIK_DOWN])
   {
   // move backward
   if ((cam_speed-=1) < -MAX_SPEED) cam_speed = -MAX_SPEED;
   } // end if

// rotate around y axis or yaw
if (keyboard_state[DIK_RIGHT])
   {
   cam.dir.y+=5;

   // scroll the background
   Scroll_Bitmap(&background_bmp, -10);
   } // end if

if (keyboard_state[DIK_LEFT])
   {
   cam.dir.y-=5;

   // scroll the background
   Scroll_Bitmap(&background_bmp, 10);
   } // end if


// scroll sky slowly
Scroll_Bitmap(&background_bmp, -1);

// motion section /////////////////////////////////////////////////////////

// terrain following, simply find the current cell we are over and then
// index into the vertex list and find the 4 vertices that make up the 
// quad cell we are hovering over and then average the values, and based
// on the current height and the height of the terrain push the player upward

// the terrain generates and stores some results to help with terrain following
//ivar1 = columns;
//ivar2 = rows;
//fvar1 = col_vstep;
//fvar2 = row_vstep;

int cell_x = (cam.pos.x  + TERRAIN_WIDTH/2) / obj_terrain.fvar1;
int cell_y = (cam.pos.z  + TERRAIN_HEIGHT/2) / obj_terrain.fvar1;

static float terrain_height, delta;

// test if we are on terrain
if ( (cell_x >=0) && (cell_x < obj_terrain.ivar1) && (cell_y >=0) && (cell_y < obj_terrain.ivar2) )
   {
   // compute vertex indices into vertex list of the current quad
   int v0 = cell_x + cell_y*obj_terrain.ivar2;
   int v1 = v0 + 1;
   int v2 = v1 + obj_terrain.ivar2;
   int v3 = v0 + obj_terrain.ivar2;   

   // now simply index into table 
   terrain_height = 0.25 * (obj_terrain.vlist_trans[v0].y + obj_terrain.vlist_trans[v1].y +
                            obj_terrain.vlist_trans[v2].y + obj_terrain.vlist_trans[v3].y);

   // compute height difference
   delta = terrain_height - (cam.pos.y - gclearance);

   // test for penetration
   if (delta > 0)
      {
      // apply force immediately to camera (this will give it a springy feel)
      vel_y+=(delta * (VELOCITY_SCALER));

      // test for pentration, if so move up immediately so we don't penetrate geometry
      cam.pos.y+=(delta*CAM_HEIGHT_SCALER);

      // now this is more of a hack than the physics model :) let move the front
      // up and down a bit based on the forward velocity and the gradient of the 
      // hill
      cam.dir.x -= (delta*PITCH_CHANGE_RATE);
 
      } // end if

   } // end if

// decelerate camera
if (cam_speed > (CAM_DECEL) ) cam_speed-=CAM_DECEL;
else
if (cam_speed < (-CAM_DECEL) ) cam_speed+=CAM_DECEL;
else
   cam_speed = 0;

// force camera to seek a stable orientation
if (cam.dir.x > (neutral_pitch+PITCH_RETURN_RATE)) cam.dir.x -= (PITCH_RETURN_RATE);
else
if (cam.dir.x < (neutral_pitch-PITCH_RETURN_RATE)) cam.dir.x += (PITCH_RETURN_RATE);
 else 
   cam.dir.x = neutral_pitch;

// apply gravity
vel_y+=gravity;

// test for absolute sea level and push upward..
if (cam.pos.y < sea_level)
   { 
   vel_y = 0; 
   cam.pos.y = sea_level;
   } // end if

// move camera
cam.pos.x += cam_speed*Fast_Sin(cam.dir.y);
cam.pos.z += cam_speed*Fast_Cos(cam.dir.y);
cam.pos.y += vel_y;

// move point light source in ellipse around game world
lights2[POINT_LIGHT_INDEX].pos.x = 500*Fast_Cos(plight_ang);
//lights2[POINT_LIGHT_INDEX].pos.y = 200;
lights2[POINT_LIGHT_INDEX].pos.z = 500*Fast_Sin(plight_ang);

// move point light source in ellipse around game world
lights2[POINT_LIGHT2_INDEX].pos.x = 200*Fast_Cos(-2*plight_ang);
//lights2[POINT_LIGHT2_INDEX].pos.y = 400;
lights2[POINT_LIGHT2_INDEX].pos.z = 200*Fast_Sin(-2*plight_ang);

if ((plight_ang+=1) > 360)
    plight_ang = 0;

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

//////////////////////////////////////////////////////////////////////////
// the terrain

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_terrain);

// generate rotation matrix around y axis
//Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);

MAT_IDENTITY_4X4(&mrot); 

// rotate the local coords of the object
Transform_OBJECT4DV2(&obj_terrain, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_terrain, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_terrain,0);

//////////////////////////////////////////////////////////////////////////

int v0, v1, v2, v3; // used to track vertices

VECTOR4D pl,  // position of the light
         po,  // position of the occluder object/vertex
         vlo, // vector from light to object
         ps;  // position of the shadow

float    rs,  // radius of shadow 
         t;   // parameter t

//////////////////////////////////////////////////////////////////////////
// render model, this next section draws each copy of the mech model
//////////////////////////////////////////////////////////////////////////

// animate the model
Animate_MD2(&obj_md2);

// extract the frame of animation from vertex banks
Extract_MD2_Frame(&obj_model,  // pointer to destination object
                  &obj_md2);   // md2 object to extract frame from

// set position of object 
obj_model.world_pos.x = 0;
obj_model.world_pos.y = 100;
obj_model.world_pos.z = 0;

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_model);

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// transform the local coords of the object
Transform_OBJECT4DV2(&obj_model, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_model, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_model,0);

// set position of object 
obj_model.world_pos.x = 0;
obj_model.world_pos.y = 100;
obj_model.world_pos.z = 200;

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_model);

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// transform the local coords of the object
Transform_OBJECT4DV2(&obj_model, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_model, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_model,0);
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
// draw all the light objects to represent the position of light sources

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_light_array[INDEX_RED_LIGHT_INDEX]);

// set position of object to light
obj_light_array[INDEX_RED_LIGHT_INDEX].world_pos = lights2[POINT_LIGHT_INDEX].pos;

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// transform the local coords of the object
Transform_OBJECT4DV2(&obj_light_array[INDEX_RED_LIGHT_INDEX], &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_light_array[INDEX_RED_LIGHT_INDEX], TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_light_array[INDEX_RED_LIGHT_INDEX],0);

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_light_array[INDEX_YELLOW_LIGHT_INDEX]);

// set position of object to light
obj_light_array[INDEX_YELLOW_LIGHT_INDEX].world_pos = lights2[POINT_LIGHT2_INDEX].pos;

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// transform the local coords of the object
Transform_OBJECT4DV2(&obj_light_array[INDEX_YELLOW_LIGHT_INDEX], &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_light_array[INDEX_YELLOW_LIGHT_INDEX], TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_light_array[INDEX_YELLOW_LIGHT_INDEX],0);

////////////////////////////////////////////////////////////////////////////////////

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;
debug_polys_lit_per_frame = 0;

// perform rendering pass one

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, CLIP_POLY_X_PLANE | CLIP_POLY_Y_PLANE | CLIP_POLY_Z_PLANE );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// lock the back buffer
DDraw_Lock_Back_Surface();

// draw background
Draw_Bitmap16(&background_bmp, back_buffer, back_lpitch,0);

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {
   // perspective mode affine texturing
      // set up rendering context
      rc.attr =    RENDER_ATTR_ZBUFFER  
              // | RENDER_ATTR_ALPHA  
              // | RENDER_ATTR_MIPMAP  
              // | RENDER_ATTR_BILERP
                 | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

   // initialize zbuffer to 0 fixed point
   Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

   // set up remainder of rendering context
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 0;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;

   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16_2(&rc);
   } // end if

// now make second rendering pass and draw shadow

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

//////////////////////////////////////////////////////////////////////////
// project shaded object into shadow by projecting it's vertices onto
// the ground plane

// reset the object (this only matters for backface and object removal)
Reset_OBJECT4DV2(&obj_model);

// save the shading attributes/color of each polygon, and override them with
// attributes of a shadow then restore them
int pcolor[OBJECT4DV2_MAX_POLYS],    // used to store color
    pattr[OBJECT4DV2_MAX_POLYS];     // used to store attribute

// save all the color and attributes for each polygon
for (int pindex = 0; pindex < obj_model.num_polys; pindex++)
    {
    // save attribute and color
    pattr[pindex]  = obj_model.plist[pindex].attr;
    pcolor[pindex] = obj_model.plist[pindex].color;
  
    // set attributes for shadow rendering
    obj_model.plist[pindex].attr    = POLY4DV2_ATTR_RGB16 | POLY4DV2_ATTR_SHADE_MODE_CONSTANT | POLY4DV2_ATTR_TRANSPARENT;
    obj_model.plist[pindex].color   = RGB16Bit(50,50,50) + (7 << 24);

    } // end for pindex

// create identity matrix
MAT_IDENTITY_4X4(&mrot);

// solve for t when the projected vertex intersects ground plane
pl = lights2[POINT_LIGHT_INDEX].pos;

// transform each local/model vertex of the object mesh and store result
// in "transformed" vertex list, note 
for (int vertex=0; vertex < obj_model.num_vertices; vertex++)
    {
    POINT4D presult; // hold result of each transformation

    // compute parameter t0 when projected ray pierces y=0 plane
    VECTOR4D vi;

    // set position of object 
    obj_model.world_pos.x = 0;
    obj_model.world_pos.y = 100;
    obj_model.world_pos.z = 0;

    // transform coordinates to worldspace right now...
    VECTOR4D_Add(&obj_model.vlist_local[vertex].v, &obj_model.world_pos, &vi);

    float t0 = -pl.y / (vi.y - pl.y);

    // transform point
    obj_model.vlist_trans[vertex].v.x = pl.x + t0*(vi.x - pl.x);
    obj_model.vlist_trans[vertex].v.y = 10.0; // pl.y + t0*(vi.y - pl.y);
    obj_model.vlist_trans[vertex].v.z = pl.z + t0*(vi.z - pl.z);
    obj_model.vlist_trans[vertex].v.w = 1.0;

    } // end for index

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_model,0);

// and now second shadow object from second light source...

// solve for t when the projected vertex intersects
pl = lights2[POINT_LIGHT_INDEX].pos; 

// transform each local/model vertex of the object mesh and store result
// in "transformed" vertex list
for (vertex=0; vertex < obj_model.num_vertices; vertex++)
    {
    POINT4D presult; // hold result of each transformation

    // compute parameter t0 when projected ray pierces y=0 plane
    VECTOR4D vi;

    // set position of object 
    obj_model.world_pos.x = 0;
    obj_model.world_pos.y = 100;
    obj_model.world_pos.z = 200;

    // transform coordinates to worldspace right now...
    VECTOR4D_Add(&obj_model.vlist_local[vertex].v, &obj_model.world_pos, &vi);

    float t0 = -pl.y / (vi.y - pl.y);

    // transform point
    obj_model.vlist_trans[vertex].v.x = pl.x + t0*(vi.x - pl.x);
    obj_model.vlist_trans[vertex].v.y = 10.0; // pl.y + t0*(vi.y - pl.y);
    obj_model.vlist_trans[vertex].v.z = pl.z + t0*(vi.z - pl.z);
    obj_model.vlist_trans[vertex].v.w = 1.0;

    } // end for index

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_model,0);

// restore attributes and color
for (pindex = 0; pindex < obj_model.num_polys; pindex++)
    {
    // save attribute and color
    obj_model.plist[pindex].attr  = pattr[pindex];
    obj_model.plist[pindex].color = pcolor[pindex]; 
  
    } // end for pindex

//////////////////////////////////////////////////////////////////////////

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, CLIP_POLY_X_PLANE | CLIP_POLY_Y_PLANE | CLIP_POLY_Z_PLANE );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// render the object

if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {
   // perspective mode affine texturing
   // set up rendering context
   rc.attr =    RENDER_ATTR_ZBUFFER  
                | RENDER_ATTR_ALPHA  
             // | RENDER_ATTR_MIPMAP  
             // | RENDER_ATTR_BILERP
                | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

   // initialize zbuffer to 0 fixed point
   //Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

   // set up remainder of rendering context
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 0;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;

   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16_3(&rc);
   } // end if

// unlock the back buffer
DDraw_Unlock_Back_Surface();

// draw cockpit
//Draw_BOB16(&cockpit, lpddsback);


// draw instructions
Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(255,255,255), lpddsback);

// should we display help
int text_y = 16;
if (help_mode==1)
    {
    // draw help menu
    Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<B>..............Toggle backface removal.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<Z>..............Toggle Z-sorting.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<1>,<2>..........Previous/Next Animation.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<3>,<4>..........Play Animation Single Shot/Looped.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help


sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, BckFceRM [%s], Zsort[%s]", 
                                                                                 ((lighting_mode == 1) ? "ON" : "OFF"),
                                                                                 lights2[AMBIENT_LIGHT_INDEX].state,
                                                                                 lights2[INFINITE_LIGHT_INDEX].state, 
                                                                                 lights2[POINT_LIGHT_INDEX].state,
                                                                                 ((backface_mode == 1) ? "ON" : "OFF"),
                                                                                 ((zsort_mode == 1) ? "ON" : "OFF") );
Draw_Text_GDI(work_string, 0+1, WINDOW_HEIGHT-34+1, RGB(0,0,0), lpddsback);
Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34, RGB(255,255,255), lpddsback);

sprintf(work_string,"Polys Rendered: %d, Polys lit: %d Anim[%d]=%s Frm=%d", debug_polys_rendered_per_frame, debug_polys_lit_per_frame, obj_md2.anim_state,md2_anim_strings[obj_md2.anim_state], obj_md2.curr_frame );
Draw_Text_GDI(work_string, 0+1, WINDOW_HEIGHT-34-2*16+1, RGB(0,0,0), lpddsback);
Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-2*16, RGB(255,255,255), lpddsback);

sprintf(work_string,"CAM [%5.2f, %5.2f, %5.2f], CELL [%d, %d]",  cam.pos.x, cam.pos.y, cam.pos.z, cell_x, cell_y);
Draw_Text_GDI(work_string, 0+1, WINDOW_HEIGHT-34-3*16+1, RGB(0,0,0), lpddsback);
Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-3*16, RGB(255,255,255), lpddsback);

// flip the surfaces
DDraw_Flip2();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
  
} // end Game_Main
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

static MATRIX4X4 mrot;   // general rotation matrix

static float plight_ang = 0, 
             slight_ang = 0; // angles for light motion

static float alpha_override = 0,
             alpha_inc = .25;

// use these to rotate objects
static float x_ang = 0, y_ang = 0, z_ang = 0;

// state variables for different rendering modes and help
static int wireframe_mode = 1;
static int backface_mode  = 1;
static int lighting_mode  = 1;
static int help_mode      = 1;
static int zsort_mode     = 1;
static int x_clip_mode    = 1;
static int y_clip_mode    = 1;
static int z_clip_mode    = 1;
static int z_buffer_mode  = 1;
static int display_mode   = 1;
static float turning      = 0;
static int pass_mode      = 2;

char work_string[256]; // temp string

int index; // looping var

// start the timing clock
Start_Clock();

// clear the drawing surface 
//DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
//Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(0,0,0), lpddsback);
lpddsback->Blt(NULL, background.images[0], NULL, DDBLT_WAIT, NULL); 

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT*.5, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(190,190,230), lpddsback);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// modes and lights

// wireframe mode
if (keyboard_state[DIK_W]) 
   {
   // toggle wireframe mode
   if (++wireframe_mode > 1)
       wireframe_mode=0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// backface removal
if (keyboard_state[DIK_B])
   {
   // toggle backface removal
   backface_mode = -backface_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// lighting
if (keyboard_state[DIK_L])
   {
   // toggle lighting engine completely
   lighting_mode = -lighting_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle ambient light
if (keyboard_state[DIK_A])
   {
   // toggle ambient light
   if (lights2[AMBIENT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle infinite light
if (keyboard_state[DIK_I])
   {
   // toggle ambient light
   if (lights2[INFINITE_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// toggle point light
if (keyboard_state[DIK_P])
   {
   // toggle point light
   if (lights2[POINT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// toggle spot light
if (keyboard_state[DIK_S])
   {
   // toggle spot light
   if (lights2[SPOT_LIGHT2_INDEX].state == LIGHTV2_STATE_ON)
      lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_OFF;
   else
      lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_ON;

   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if


// help menu
if (keyboard_state[DIK_H])
   {
   // toggle help menu 
   help_mode = -help_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// reflection pass mode
if (keyboard_state[DIK_N])
   {
   // toggle help menu 
   if (++pass_mode > 2) pass_mode = 0;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// z-sorting
if (keyboard_state[DIK_S])
   {
   // toggle z sorting
   zsort_mode = -zsort_mode;
   Wait_Clock(100); // wait, so keyboard doesn't bounce
   } // end if

// forward/backward
if (keyboard_state[DIK_UP])
   {
   // move forward
   if ( (cam_speed+=1) > MAX_SPEED) cam_speed = MAX_SPEED;
   } // end if
else
if (keyboard_state[DIK_DOWN])
   {
   // move backward
   if ((cam_speed-=1) < -MAX_SPEED) cam_speed = -MAX_SPEED;
   } // end if

// rotate
if (keyboard_state[DIK_RIGHT])
   {
   cam.dir.y+=3;
   
   // add a little turn to object
   if ((turning+=2) > 25)
      turning=25;
   } // end if

if (keyboard_state[DIK_LEFT])
   {
   cam.dir.y-=3;

   // add a little turn to object
   if ((turning-=2) < -25)
      turning=-25;

   } // end if
else // center heading again
   {
   if (turning > 0)
       turning-=1;
   else
   if (turning < 0)
       turning+=1;

   } // end else

// decelerate camera
if (cam_speed > (CAM_DECEL) ) cam_speed-=CAM_DECEL;
else
if (cam_speed < (-CAM_DECEL) ) cam_speed+=CAM_DECEL;
else
   cam_speed = 0;

// move camera
cam.pos.x += cam_speed*Fast_Sin(cam.dir.y);
cam.pos.y = 200;
cam.pos.z += cam_speed*Fast_Cos(cam.dir.y);

// move point light source in ellipse around game world
lights2[POINT_LIGHT_INDEX].pos.x = 1000*Fast_Cos(plight_ang);
lights2[POINT_LIGHT_INDEX].pos.y = 100;
lights2[POINT_LIGHT_INDEX].pos.z = 1000*Fast_Sin(plight_ang);

if ((plight_ang+=3) > 360)
    plight_ang = 0;

// move spot light source in ellipse around game world
lights2[SPOT_LIGHT2_INDEX].pos.x = 1000*Fast_Cos(slight_ang);
lights2[SPOT_LIGHT2_INDEX].pos.y = 200;
lights2[SPOT_LIGHT2_INDEX].pos.z = 1000*Fast_Sin(slight_ang);

if ((slight_ang-=5) < 0)
    slight_ang = 360;

// update rotation angles
if ((x_ang+=.2) > 360) x_ang = 0;
if ((y_ang+=.4) > 360) y_ang = 0;
if ((z_ang+=.8) > 360) z_ang = 0;

// generate camera matrix
Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

// lock the back buffer
DDraw_Lock_Back_Surface();

// render the terrain first /////////////////////////////////
if (pass_mode >= 0)
{
// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// rotate the local coords of the object
MAT_IDENTITY_4X4(&mrot); 
Transform_OBJECT4DV2(&obj_terrain, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

// perform world transform
Model_To_World_OBJECT4DV2(&obj_terrain, TRANSFORM_TRANS_ONLY);

// insert the object into render list
Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_terrain,0);

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, ((x_clip_mode == 1) ? CLIP_POLY_X_PLANE : 0) | 
                                            ((y_clip_mode == 1) ? CLIP_POLY_Y_PLANE : 0) | 
                                            ((z_clip_mode == 1) ? CLIP_POLY_Z_PLANE : 0) );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);


//   // get an identity matrix
//   MAT_IDENTITY_4X4(&mrot); 
//   mrot.M11 = -1;

   // transform the rendering list by x-z plane reflection matrix
   Transform_RENDERLIST4DV2(&rend_list, &mrot, TRANSFORM_TRANS_ONLY);  

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the renderinglist
if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {
   // initialize zbuffer to 16000 fixed point
   Clear_Zbuffer(&zbuffer, (16000 << FIXP16_SHIFT));

   // set up rendering context
   rc.attr         = RENDER_ATTR_ZBUFFER  
                     | RENDER_ATTR_ALPHA  
                     //| RENDER_ATTR_MIPMAP  
                     //| RENDER_ATTR_BILERP
                     | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;
   
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 3500;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;
 
   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16(&rc);
   } // end if

} // end if
//////////////////////////////////////////////////////////

if (pass_mode >= 1)
{
// render the reflection of the objects

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// insert the scenery into universe
for (index = 0; index < NUM_SCENE_OBJECTS; index++)
    {
    // select proper object first
    obj_work = &obj_array[(int)scene_objects[index].w];

    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV2(obj_work);

    // set position of tower
    obj_work->world_pos.x = scene_objects[index].x;
    obj_work->world_pos.y = scene_objects[index].y;
    obj_work->world_pos.z = scene_objects[index].z;

    // move objects
    scene_objects[index].x+=scene_objects_vel[index].x;
    scene_objects[index].y+=scene_objects_vel[index].y;
    scene_objects[index].z+=scene_objects_vel[index].z;

    // test for out of bounds
    if (scene_objects[index].x >= UNIVERSE_RADIUS || scene_objects[index].x <= -UNIVERSE_RADIUS)
       { 
       scene_objects_vel[index].x=-scene_objects_vel[index].x;
       scene_objects[index].x+=scene_objects_vel[index].x;
       } // end if

    if (scene_objects[index].y >= (UNIVERSE_RADIUS/2) || scene_objects[index].y <= -(UNIVERSE_RADIUS/2))
       { 
       scene_objects_vel[index].y=-scene_objects_vel[index].y;
       scene_objects[index].y+=scene_objects_vel[index].y;
       } // end if

    if (scene_objects[index].z >= UNIVERSE_RADIUS  || scene_objects[index].z <= -UNIVERSE_RADIUS)
       { 
       scene_objects_vel[index].z=-scene_objects_vel[index].z;
       scene_objects[index].z+=scene_objects_vel[index].z;
       } // end if

    // attempt to cull object   
    if (!Cull_OBJECT4DV2(obj_work, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       MAT_IDENTITY_4X4(&mrot);
 
       // rotate the local coords of the object
       Transform_OBJECT4DV2(obj_work, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

       // perform world transform
       Model_To_World_OBJECT4DV2(obj_work, TRANSFORM_TRANS_ONLY);

       // insert the object into render list
       Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, obj_work,0);

       } // end if
 
    } // end for

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, ((x_clip_mode == 1) ? CLIP_POLY_X_PLANE : 0) | 
                                            ((y_clip_mode == 1) ? CLIP_POLY_Y_PLANE : 0) | 
                                            ((z_clip_mode == 1) ? CLIP_POLY_Z_PLANE : 0) );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// get an identity matrix
MAT_IDENTITY_4X4(&mrot); 
mrot.M11 = -1; mrot.M31 = -450;

// transform the rendering list by x-z plane reflection matrix
Transform_RENDERLIST4DV2(&rend_list, &mrot, TRANSFORM_TRANS_ONLY);  

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the renderinglist
if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {

   // set up rendering context
   rc.attr         = RENDER_ATTR_NOBUFFER  
                     | RENDER_ATTR_ALPHA  
                     //| RENDER_ATTR_MIPMAP  
                     //| RENDER_ATTR_BILERP
                     | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;
   
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 3500;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = 1;
 
   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16(&rc);
   } // end if

} // end if

//////////////////////////////////////////////////////////

if (pass_mode >= 2)
{
// render the objects now

// reset the render list
Reset_RENDERLIST4DV2(&rend_list);

// insert the scenery into universe
for (index = 0; index < NUM_SCENE_OBJECTS; index++)
    {
    // select proper object first
    obj_work = &obj_array[(int)scene_objects[index].w];

    // reset the object (this only matters for backface and object removal)
    Reset_OBJECT4DV2(obj_work);

    // set position of tower
    obj_work->world_pos.x = scene_objects[index].x;
    obj_work->world_pos.y = scene_objects[index].y;
    obj_work->world_pos.z = scene_objects[index].z;

    // move objects
    scene_objects[index].x+=scene_objects_vel[index].x;
    scene_objects[index].y+=scene_objects_vel[index].y;
    scene_objects[index].z+=scene_objects_vel[index].z;

    // test for out of bounds
    if (scene_objects[index].x >= UNIVERSE_RADIUS || scene_objects[index].x <= -UNIVERSE_RADIUS)
       { 
       scene_objects_vel[index].x=-scene_objects_vel[index].x;
       scene_objects[index].x+=scene_objects_vel[index].x;
       } // end if

    if (scene_objects[index].y >= (UNIVERSE_RADIUS/2) || scene_objects[index].y <= -(UNIVERSE_RADIUS/2))
       { 
       scene_objects_vel[index].y=-scene_objects_vel[index].y;
       scene_objects[index].y+=scene_objects_vel[index].y;
       } // end if

    if (scene_objects[index].z >= UNIVERSE_RADIUS  || scene_objects[index].z <= -UNIVERSE_RADIUS)
       { 
       scene_objects_vel[index].z=-scene_objects_vel[index].z;
       scene_objects[index].z+=scene_objects_vel[index].z;
       } // end if

    // attempt to cull object   
    if (!Cull_OBJECT4DV2(obj_work, &cam, CULL_OBJECT_XYZ_PLANES))
       {
       MAT_IDENTITY_4X4(&mrot);
 
       // rotate the local coords of the object
       Transform_OBJECT4DV2(obj_work, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

       // perform world transform
       Model_To_World_OBJECT4DV2(obj_work, TRANSFORM_TRANS_ONLY);

       // insert the object into render list
       Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, obj_work,0);

       } // end if
 
    } // end for

// remove backfaces
if (backface_mode==1)
   Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, ((x_clip_mode == 1) ? CLIP_POLY_X_PLANE : 0) | 
                                            ((y_clip_mode == 1) ? CLIP_POLY_Y_PLANE : 0) | 
                                            ((z_clip_mode == 1) ? CLIP_POLY_Z_PLANE : 0) );

// light scene all at once 
if (lighting_mode==1)
   {
   Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
   Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
   } // end if

// sort the polygon list (hurry up!)
if (zsort_mode == 1)
   Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// get an identity matrix
//MAT_IDENTITY_4X4(&mrot); 
//mrot.M11 = -1; mrot.M31 = -400;

// transform the rendering list by x-z plane reflection matrix
Transform_RENDERLIST4DV2(&rend_list, &mrot, TRANSFORM_TRANS_ONLY);  

// apply camera to perspective transformation
Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// reset number of polys rendered
debug_polys_rendered_per_frame = 0;

// render the renderinglist
if (wireframe_mode  == 0)
   Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
else
if (wireframe_mode  == 1)
   {

   // set up rendering context
   rc.attr         = RENDER_ATTR_ZBUFFER  
                     | RENDER_ATTR_ALPHA  
                     //| RENDER_ATTR_MIPMAP  
                     //| RENDER_ATTR_BILERP
                     | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;
   
   rc.video_buffer   = back_buffer;
   rc.lpitch         = back_lpitch;
   rc.mip_dist       = 3500;
   rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
   rc.zpitch         = WINDOW_WIDTH*4;
   rc.rend_list      = &rend_list;
   rc.texture_dist   = 0;
   rc.alpha_override = -1;
 
   // render scene
   Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16(&rc);
   } // end if

} // end if

// unlock the back buffer
DDraw_Unlock_Back_Surface();

sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, Spot=%d, BckFceRM [%s], Zsort [%s]", 
                                                                                 ((lighting_mode == 1) ? "ON" : "OFF"),
                                                                                 lights[AMBIENT_LIGHT_INDEX].state,
                                                                                 lights[INFINITE_LIGHT_INDEX].state, 
                                                                                 lights[POINT_LIGHT_INDEX].state,
                                                                                 lights[SPOT_LIGHT2_INDEX].state,
                                                                                 ((backface_mode == 1) ? "ON" : "OFF"),
                                                                                 ((zsort_mode == 1) ? "ON" : "OFF"));


Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16, RGB(0,255,0), lpddsback);

// draw instructions
Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(0,255,0), lpddsback);

// should we display help
int text_y = 16;
if (help_mode==1)
    {
    // draw help menu
    Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<S>..............Toggle spot light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<N>..............Enable next rendering pass.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<B>..............Toggle backface removal.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<S>..............Toggle Z sorting.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
    Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help

sprintf(work_string,"Polys Rendered: %d, Polys lit: %d", debug_polys_rendered_per_frame, debug_polys_lit_per_frame);
Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16, RGB(0,255,0), lpddsback);

sprintf(work_string,"CAM [%5.2f, %5.2f, %5.2f]",  cam.pos.x, cam.pos.y, cam.pos.z);

Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16-16, RGB(0,255,0), lpddsback);

// flip the surfaces
DDraw_Flip2();

// sync to 30ish fps 
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);
 
} // end Game_Main
Example #20
0
Matrix4d Build_XYZ_Rotation_MATRIX4X4(float theta_x, float theta_y, float theta_z)
{
	Matrix4d mx, my, mz, mtmp;
	float sin_theta = 0, cos_theta = 0;
	int rot_seq = 0;

	mtmp = Matrix4d::Identity(4, 4);

	if (fabs(theta_x) > EPSILON_E5)
		rot_seq = rot_seq | 1;
	if (fabs(theta_y) > EPSILON_E5)
		rot_seq = rot_seq | 2;
	if (fabs(theta_z) > EPSILON_E5)
		rot_seq = rot_seq | 4;

	switch (rot_seq) {
	case 0:
	{
		return mtmp;
	} break;
	case 1:
	{
		cos_theta = Fast_Cos(theta_x);
		sin_theta = Fast_Sin(theta_x);
		mx << 1, 0, 0, 0,
			0, cos_theta, sin_theta, 0,
			0, -sin_theta, cos_theta, 0,
			0, 0, 0, 1;
		mtmp = mx;
		return mtmp;
	} break;
	case 2:
	{
		cos_theta = Fast_Cos(theta_y);
		sin_theta = Fast_Sin(theta_y);
		my << cos_theta, 0, -sin_theta, 0,
			0, 1, 0, 0,
			sin_theta, 0, cos_theta, 0,
			0, 0, 0, 1;
		mtmp = my;
		return mtmp;
	} break;
	case 3:
	{
		cos_theta = Fast_Cos(theta_x);
		sin_theta = Fast_Sin(theta_x);
		mx << 1, 0, 0, 0,
			0, cos_theta, sin_theta, 0,
			0, -sin_theta, cos_theta, 0,
			0, 0, 0, 1;
		cos_theta = Fast_Cos(theta_y);
		sin_theta = Fast_Sin(theta_y);
		my << cos_theta, 0, -sin_theta, 0,
			0, 1, 0, 0,
			sin_theta, 0, cos_theta, 0,
			0, 0, 0, 1;
		mtmp = my * mx;
		return mtmp;
	} break;
	case 4:
	{
		cos_theta = Fast_Cos(theta_z);
		sin_theta = Fast_Sin(theta_z);
		mz << cos_theta, sin_theta, 0, 0,
			-sin_theta, cos_theta, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1;
		mtmp = mz;
		return mtmp;
	} break;
	case 5:
	{
		cos_theta = Fast_Cos(theta_x);
		sin_theta = Fast_Sin(theta_x);
		mx << 1, 0, 0, 0,
			0, cos_theta, sin_theta, 0,
			0, -sin_theta, cos_theta, 0,
			0, 0, 0, 1;
		cos_theta = Fast_Cos(theta_z);
		sin_theta = Fast_Sin(theta_z);
		mz << cos_theta, sin_theta, 0, 0,
			-sin_theta, cos_theta, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1;
		mtmp = mz * mx;
		return mtmp;
	} break;
	case 6:
	{
		cos_theta = Fast_Cos(theta_y);
		sin_theta = Fast_Sin(theta_y);
		my << cos_theta, 0, -sin_theta, 0,
			0, 1, 0, 0,
			sin_theta, 0, cos_theta, 0,
			0, 0, 0, 1;
		cos_theta = Fast_Cos(theta_z);
		sin_theta = Fast_Sin(theta_z);
		mz << cos_theta, sin_theta, 0, 0,
			-sin_theta, cos_theta, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1;
		mtmp = mz * my;
		return mtmp;
	} break;
	case 7:
	{
		cos_theta = Fast_Cos(theta_x);
		sin_theta = Fast_Sin(theta_x);
		mx << 1, 0, 0, 0,
			0, cos_theta, sin_theta, 0,
			0, -sin_theta, cos_theta, 0,
			0, 0, 0, 1;
		cos_theta = Fast_Cos(theta_y);
		sin_theta = Fast_Sin(theta_y);
		my << cos_theta, 0, -sin_theta, 0,
			0, 1, 0, 0,
			sin_theta, 0, cos_theta, 0,
			0, 0, 0, 1;
		cos_theta = Fast_Cos(theta_z);
		sin_theta = Fast_Sin(theta_z);
		mz << cos_theta, sin_theta, 0, 0,
			-sin_theta, cos_theta, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1;
		mtmp = mz * my * mx;
		return mtmp;
	} break;
	default:
		return mtmp;
		break;
	}
}
int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

    static MATRIX4X4 mrot;   // general rotation matrix

    static float plight_ang = 0,
                 slight_ang = 0; // angles for light motion

// use these to rotate objects
    static float x_ang = 0, y_ang = 0, z_ang = 0;

// state variables for different rendering modes and help
    static int wireframe_mode = 1;
    static int backface_mode  = 1;
    static int lighting_mode  = 1;
    static int help_mode      = 1;
    static int zsort_mode     = 1;
    static int x_clip_mode    = 1;
    static int y_clip_mode    = 1;
    static int z_clip_mode    = 1;
    static int bilinear_mode  = 1;

    char work_string[256]; // temp string

    int index; // looping var

// start the timing clock
    Start_Clock();

// clear the drawing surface
//DDraw_Fill_Surface(lpddsback, 0);

// draw the sky
//Draw_Rectangle(0,0, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(250,190,80), lpddsback);
    lpddsback->Blt(NULL, background.images[0], NULL, DDBLT_WAIT, NULL);

// draw the ground
//Draw_Rectangle(0,WINDOW_HEIGHT*.5, WINDOW_WIDTH, WINDOW_HEIGHT, RGB16Bit(190,190,230), lpddsback);

// read keyboard and other devices here
    DInput_Read_Keyboard();

// game logic here...

// reset the render list
    Reset_RENDERLIST4DV2(&rend_list);

// modes and lights

// wireframe mode
    if (keyboard_state[DIK_W])
    {
        // toggle wireframe mode
        if (++wireframe_mode > 1)
            wireframe_mode=0;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// lighting
    if (keyboard_state[DIK_L])
    {
        // toggle lighting engine completely
        lighting_mode = -lighting_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle ambient light
    if (keyboard_state[DIK_A])
    {
        // toggle ambient light
        if (lights2[AMBIENT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[AMBIENT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle infinite light
    if (keyboard_state[DIK_I])
    {
        // toggle ambient light
        if (lights2[INFINITE_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[INFINITE_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle point light
    if (keyboard_state[DIK_P])
    {
        // toggle point light
        if (lights2[POINT_LIGHT_INDEX].state == LIGHTV2_STATE_ON)
            lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[POINT_LIGHT_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// toggle spot light
    if (keyboard_state[DIK_S])
    {
        // toggle spot light
        if (lights2[SPOT_LIGHT2_INDEX].state == LIGHTV2_STATE_ON)
            lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_OFF;
        else
            lights2[SPOT_LIGHT2_INDEX].state = LIGHTV2_STATE_ON;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// help menu
    if (keyboard_state[DIK_H])
    {
        // toggle help menu
        help_mode = -help_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// z-sorting
    if (keyboard_state[DIK_S])
    {
        // toggle z sorting
        zsort_mode = -zsort_mode;
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if


// forward/backward
    if (keyboard_state[DIK_UP])
    {
        // move forward
        if ( (cam_speed+=1) > MAX_SPEED) cam_speed = MAX_SPEED;
    } // end if
    else if (keyboard_state[DIK_DOWN])
    {
        // move backward
        if ((cam_speed-=1) < -MAX_SPEED) cam_speed = -MAX_SPEED;
    } // end if

// rotate around y axis or yaw
    if (keyboard_state[DIK_RIGHT])
    {
        cam.dir.y+=5;
    } // end if

    if (keyboard_state[DIK_LEFT])
    {
        cam.dir.y-=5;
    } // end if

// move to next object
    if (keyboard_state[DIK_N])
    {
        if (++curr_object >= NUM_OBJECTS)
            curr_object = 0;

        // update pointer
        obj_work = &obj_array[curr_object];
        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// toggle bilinear mode
    if (keyboard_state[DIK_B])
    {
        bilinear_mode = -bilinear_mode;

        Wait_Clock(100); // wait, so keyboard doesn't bounce
    } // end if

// decelerate camera
    if (cam_speed > (CAM_DECEL) ) cam_speed-=CAM_DECEL;
    else if (cam_speed < (-CAM_DECEL) ) cam_speed+=CAM_DECEL;
    else
        cam_speed = 0;

// move camera
    cam.pos.x += cam_speed*Fast_Sin(cam.dir.y);
    cam.pos.z += cam_speed*Fast_Cos(cam.dir.y);

// move point light source in ellipse around game world
    lights2[POINT_LIGHT_INDEX].pos.x = 1000*Fast_Cos(plight_ang);
    lights2[POINT_LIGHT_INDEX].pos.y = 100;
    lights2[POINT_LIGHT_INDEX].pos.z = 1000*Fast_Sin(plight_ang);

    if ((plight_ang+=3) > 360)
        plight_ang = 0;

// move spot light source in ellipse around game world
    lights2[SPOT_LIGHT2_INDEX].pos.x = 1000*Fast_Cos(slight_ang);
    lights2[SPOT_LIGHT2_INDEX].pos.y = 200;
    lights2[SPOT_LIGHT2_INDEX].pos.z = 1000*Fast_Sin(slight_ang);

    if ((slight_ang-=5) < 0)
        slight_ang = 360;

// generate camera matrix
    Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);

// reset BHV for culling
    BHV_Reset_Tree(&bhv_tree);

// now cull the BHV ... die polygons die!
    bhv_nodes_visited = 0;
    BHV_FrustrumCull(&bhv_tree,                 // the root of the BHV
                     &cam,                     // camera to cull relative to
                     CULL_OBJECT_XYZ_PLANES);  // clipping planes to consider

// statistic tracking
    int objects_processed = 0;

////////////////////////////////////////////////////////
// insert the scenery into universe
    for (index = 0; index < NUM_SCENE_OBJECTS; index++)
    {
        // test if container has been culled already by BHV
        if (scene_objects[index].state & OBJECT4DV2_STATE_CULLED)
            continue;

        // reset the object (this only matters for backface and object removal)
        Reset_OBJECT4DV2(obj_work);

        objects_processed++;

        // set position of object
        obj_work->world_pos.x = scene_objects[index].pos.x;
        obj_work->world_pos.y = scene_objects[index].pos.y;
        obj_work->world_pos.z = scene_objects[index].pos.z;

        // rotate object
        if ((scene_objects[index].rot.y+=scene_objects[index].auxi[0]) >= 360)
            scene_objects[index].rot.y = 0;

        // attempt to cull object
        if (!Cull_OBJECT4DV2(obj_work, &cam, CULL_OBJECT_XYZ_PLANES))
        {
            MAT_IDENTITY_4X4(&mrot);

            // generate rotation matrix around y axis
            Build_XYZ_Rotation_MATRIX4X4(0, scene_objects[index].rot.y, 0, &mrot);

            // rotate the local coords of the object
            Transform_OBJECT4DV2(obj_work, &mrot, TRANSFORM_LOCAL_TO_TRANS,1);

            // perform world transform
            Model_To_World_OBJECT4DV2(obj_work, TRANSFORM_TRANS_ONLY);

            // insert the object into render list
            Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list,obj_work,0);

        } // end if

    } // end for

// reset number of polys rendered
    debug_polys_rendered_per_frame = 0;
    debug_polys_lit_per_frame = 0;

// remove backfaces
    if (backface_mode==1)
        Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam);

// apply world to camera transform
    World_To_Camera_RENDERLIST4DV2(&rend_list, &cam);

// clip the polygons themselves now
    Clip_Polys_RENDERLIST4DV2(&rend_list, &cam, ((x_clip_mode == 1) ? CLIP_POLY_X_PLANE : 0) |
                              ((y_clip_mode == 1) ? CLIP_POLY_Y_PLANE : 0) |
                              ((z_clip_mode == 1) ? CLIP_POLY_Z_PLANE : 0) );

// light scene all at once
    if (lighting_mode==1)
    {
        Transform_LIGHTSV2(lights2, 4, &cam.mcam, TRANSFORM_LOCAL_TO_TRANS);
        Light_RENDERLIST4DV2_World2_16(&rend_list, &cam, lights2, 4);
    } // end if

// sort the polygon list (hurry up!)
    if (zsort_mode == 1)
        Sort_RENDERLIST4DV2(&rend_list,  SORT_POLYLIST_AVGZ);

// apply camera to perspective transformation
    Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam);

// apply screen transform
    Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam);

// lock the back buffer
    DDraw_Lock_Back_Surface();

// reset number of polys rendered
    debug_polys_rendered_per_frame = 0;

// render the renderinglist
    if (wireframe_mode  == 0)
        Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch);
    else if (wireframe_mode  == 1)
    {
        // initialize zbuffer to 16000 fixed point
        Clear_Zbuffer(&zbuffer, (000 << FIXP16_SHIFT));

        // set up rendering context
        rc.attr         = RENDER_ATTR_INVZBUFFER
                          // | RENDER_ATTR_ALPHA
                          // | RENDER_ATTR_MIPMAP
                          | (bilinear_mode==1 ? RENDER_ATTR_BILERP : 0)
                          | RENDER_ATTR_TEXTURE_PERSPECTIVE_AFFINE;

        rc.video_buffer   = back_buffer;
        rc.lpitch         = back_lpitch;
        rc.mip_dist       = 3500;
        rc.zbuffer        = (UCHAR *)zbuffer.zbuffer;
        rc.zpitch         = WINDOW_WIDTH*4;
        rc.rend_list      = &rend_list;
        rc.texture_dist   = 0;
        rc.alpha_override = 5;

        // render scene
        Draw_RENDERLIST4DV2_RENDERCONTEXTV1_16(&rc);


    } // end if

// unlock the back buffer
    DDraw_Unlock_Back_Surface();

    sprintf(work_string,"Lighting [%s]: Ambient=%d, Infinite=%d, Point=%d, Spot=%d, Zsort [%s]",
            ((lighting_mode == 1) ? "ON" : "OFF"),
            lights[AMBIENT_LIGHT_INDEX].state,
            lights[INFINITE_LIGHT_INDEX].state,
            lights[POINT_LIGHT_INDEX].state,
            lights[SPOT_LIGHT2_INDEX].state,
            ((zsort_mode == 1) ? "ON" : "OFF"));

    Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16, RGB(0,255,0), lpddsback);

// draw instructions
    Draw_Text_GDI("Press ESC to exit. Press <H> for Help.", 0, 0, RGB(0,255,0), lpddsback);

// should we display help
    int text_y = 16;
    if (help_mode==1)
    {
        // draw help menu
        Draw_Text_GDI("<A>..............Toggle ambient light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<I>..............Toggle infinite light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<P>..............Toggle point light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<S>..............Toggle spot light source.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<N>..............Next object.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<W>..............Toggle wire frame/solid mode.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<S>..............Toggle Z sorting.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<H>..............Toggle Help.", 0, text_y+=12, RGB(255,255,255), lpddsback);
        Draw_Text_GDI("<ESC>............Exit demo.", 0, text_y+=12, RGB(255,255,255), lpddsback);

    } // end help

    sprintf(work_string,"Polys Rendered: %d, Polys lit: %d, Object Pre-Culled by BHV: %d, Nodes visited in BHV Tree: %d",
            debug_polys_rendered_per_frame,
            debug_polys_lit_per_frame,
            NUM_SCENE_OBJECTS - objects_processed,
            bhv_nodes_visited);


    Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16, RGB(0,255,0), lpddsback);

    sprintf(work_string,"CAM [%5.2f, %5.2f, %5.2f]",  cam.pos.x, cam.pos.y, cam.pos.z);

    Draw_Text_GDI(work_string, 0, WINDOW_HEIGHT-34-16-16-16, RGB(0,255,0), lpddsback);

// flip the surfaces
    DDraw_Flip2();

// sync to 30ish fps
    Wait_Clock(30);

// check of user is trying to exit
    if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
        PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
    return(1);

} // end Game_Main