int Draw_Missile(void) { // this function draws the missile // test if missile is alive for (int index=0; index<32; index++) { if (missile_state[index]==1) { // lock secondary buffer DDraw_Lock_Back_Surface(); // draw the missile in green Draw_Clip_Line16(missile_x[index], missile_y[index], missile_x[index], missile_y[index]+6, RGB16Bit(255,255,255),back_buffer, back_lpitch); // unlock surface DDraw_Unlock_Back_Surface(); } // end if } // end for // return failure return(0); } // end Draw_Missile
int Draw_Missile(void) { // this function draws the missile // test if missile is alive if (missile_state==1) { // lock secondary buffer DDraw_Lock_Back_Surface(); // draw the missile in green Draw_Clip_Line16(missile_x, missile_y, missile_x, missile_y+6, RGB16Bit(255,255,255),back_buffer, back_lpitch); // unlock surface DDraw_Unlock_Back_Surface(); // return success return(1); } // end if // return failure return(0); } // end Draw_Missle
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 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_Init(void *parms) { // this function is where you do all the initialization // for your game int index; // looping var // start up DirectDraw (replace the parms as you desire) DDraw_Init2(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP,0); // initialize directinput DInput_Init(); // acquire the keyboard DInput_Init_Keyboard(); // add calls to acquire other directinput devices here... // initialize directsound and directmusic DSound_Init(); DMusic_Init(); // hide the mouse if (!WINDOWED_APP) ShowCursor(FALSE); // seed random number generator srand(Start_Clock()); Open_Error_File("MD2ERROR.TXT"); // initialize math engine Build_Sin_Cos_Tables(); // initialize the camera with 90 FOV, normalized coordinates Init_CAM4DV1(&cam, // the camera object CAM_MODEL_EULER, // the euler model &cam_pos, // initial camera position &cam_dir, // initial camera angles &cam_target, // no target 10.0, // near and far clipping planes 12000.0, 90.0, // field of view in degrees WINDOW_WIDTH, // size of final screen viewport WINDOW_HEIGHT); VECTOR4D terrain_pos = {0,0,0,0}; Generate_Terrain_OBJECT4DV2(&obj_terrain, // pointer to object TERRAIN_WIDTH, // width in world coords on x-axis TERRAIN_HEIGHT, // height (length) in world coords on z-axis TERRAIN_SCALE, // vertical scale of terrain "height_grass_40_40_01.bmp", // filename of height bitmap encoded in 256 colors "stone256_256_01.bmp", // "grass256_256_01.bmp", //"checker2562562.bmp", // filename of texture map RGB16Bit(255,255,255), // color of terrain if no texture &terrain_pos, // initial position NULL, // initial rotations POLY4DV2_ATTR_RGB16 //| POLY4DV2_ATTR_SHADE_MODE_FLAT | POLY4DV2_ATTR_SHADE_MODE_GOURAUD | POLY4DV2_ATTR_SHADE_MODE_TEXTURE); // set a scaling vector VECTOR4D_INITXYZ(&vscale, 20, 20, 20); // load all the light objects in for (int index_obj=0; index_obj < NUM_LIGHT_OBJECTS; index_obj++) { Load_OBJECT4DV2_COB2(&obj_light_array[index_obj], object_light_filenames[index_obj], &vscale, &vpos, &vrot, VERTEX_FLAGS_INVERT_WINDING_ORDER | VERTEX_FLAGS_TRANSFORM_LOCAL | VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD ,0 ); } // end for index // set current object curr_light_object = 0; obj_light = &obj_light_array[curr_light_object]; // set up lights Reset_Lights_LIGHTV2(lights2, MAX_LIGHTS); // create some working colors white.rgba = _RGBA32BIT(255,255,255,0); gray.rgba = _RGBA32BIT(100,100,100,0); black.rgba = _RGBA32BIT(0,0,0,0); red.rgba = _RGBA32BIT(255,0,0,0); green.rgba = _RGBA32BIT(0,255,0,0); blue.rgba = _RGBA32BIT(0,0,255,0); orange.rgba = _RGBA32BIT(255,128,0,0); yellow.rgba = _RGBA32BIT(255,255,0,0); // ambient light Init_Light_LIGHTV2(lights2, AMBIENT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_AMBIENT, // ambient light type gray, black, black, // color for ambient term only NULL, NULL, // no need for pos or dir 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D dlight_dir = {-1,1,-1,1}; // directional light Init_Light_LIGHTV2(lights2, INFINITE_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_INFINITE, // infinite light type black, gray, black, // color for diffuse term only NULL, &dlight_dir, // need direction only 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D plight_pos = {0,500,0,1}; // point light Init_Light_LIGHTV2(lights2, POINT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_POINT, // pointlight type black, orange, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.001,0, // linear attenuation only 0,0,1); // spotlight info NA // point light Init_Light_LIGHTV2(lights2, POINT_LIGHT2_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_POINT, // pointlight type black, yellow, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.002,0, // linear attenuation only 0,0,1); // spotlight info NA VECTOR4D slight2_pos = {0,200,0,1}; VECTOR4D slight2_dir = {-1,1,-1,1}; // create lookup for lighting engine RGB_16_8_IndexedRGB_Table_Builder(DD_PIXEL_FORMAT565, // format we want to build table for palette, // source palette rgblookup); // lookup table // create the z buffer Create_Zbuffer(&zbuffer, WINDOW_WIDTH, WINDOW_HEIGHT, ZBUFFER_ATTR_32BIT); // build alpha lookup table RGB_Alpha_Table_Builder(NUM_ALPHA_LEVELS, rgb_alpha_table); // load background sounds wind_sound_id = DSound_Load_WAV("STATIONTHROB.WAV"); // start the sounds DSound_Play(wind_sound_id, DSBPLAY_LOOPING); DSound_Set_Volume(wind_sound_id, 100); #if 0 // load in the cockpit image Create_BOB(&cockpit, 0,0,800,600,2, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Bitmap_File(&bitmap16bit, "lego02.BMP"); Load_Frame_BOB16(&cockpit, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); Load_Bitmap_File(&bitmap16bit, "lego02b.BMP"); Load_Frame_BOB16(&cockpit, &bitmap16bit,1,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); #endif // load background image that scrolls Load_Bitmap_File(&bitmap16bit, "sunset800_600_03.bmp"); Create_Bitmap(&background_bmp,0,0,800,600,16); Load_Image_Bitmap16(&background_bmp, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); static VECTOR4D vs = {4,4,4,1}; static VECTOR4D vp = {0,0,0,1}; // load the md2 object Load_Object_MD2(&obj_md2, // the loaded md2 file placed in container "./md2/q2mdl-tekkblade/tris.md2", // "D:/Games/quakeII/baseq2/players/male/tris.md2", // the filename of the .MD2 model &vs, &vp, NULL, "./md2/q2mdl-tekkblade/blade_black.bmp", //"D:/Games/quakeII/baseq2/players/male/claymore.bmp", // the texture filename for the model POLY4DV2_ATTR_RGB16 | POLY4DV2_ATTR_SHADE_MODE_FLAT | POLY4DV2_ATTR_SHADE_MODE_TEXTURE, RGB16Bit(255,255,255), VERTEX_FLAGS_SWAP_YZ); // control ordering etc. // prepare OBJECT4DV2 for md2 Prepare_OBJECT4DV2_For_MD2(&obj_model, // pointer to destination object &obj_md2); // md2 object to extract frame from // set the animation Set_Animation_MD2(&obj_md2,MD2_ANIM_STATE_STANDING_IDLE, MD2_ANIM_LOOP); #if 0 // play with these for more speed :) // set single precission _control87( _PC_24, _MCW_PC ); // set to flush mode _control87( _DN_FLUSH, _MCW_DN ); // set rounding mode _control87( _RC_NEAR, _MCW_RC ); #endif // return success return(1); } // end Game_Init
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
int Game_Init(void *parms) { // this function is where you do all the initialization // for your game int index; // looping var // start up DirectDraw (replace the parms as you desire) DDraw_Init2(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP,0); // initialize directinput DInput_Init(); // acquire the keyboard DInput_Init_Keyboard(); // add calls to acquire other directinput devices here... // initialize directsound and directmusic DSound_Init(); DMusic_Init(); // hide the mouse if (!WINDOWED_APP) ShowCursor(FALSE); // seed random number generator srand(Start_Clock()); Open_Error_File("ERROR.TXT"); // initialize math engine Build_Sin_Cos_Tables(); // initialize the camera with 90 FOV, normalized coordinates Init_CAM4DV1(&cam, // the camera object CAM_MODEL_EULER, // the euler model &cam_pos, // initial camera position &cam_dir, // initial camera angles &cam_target, // no target 10.0, // near and far clipping planes 12000.0, 120.0, // field of view in degrees WINDOW_WIDTH, // size of final screen viewport WINDOW_HEIGHT); #if 0 VECTOR4D terrain_pos = {0,0,0,0}; Generate_Terrain_OBJECT4DV2(&obj_terrain, // pointer to object TERRAIN_WIDTH, // width in world coords on x-axis TERRAIN_HEIGHT, // height (length) in world coords on z-axis TERRAIN_SCALE, // vertical scale of terrain "checkerheight05.bmp", // filename of height bitmap encoded in 256 colors "checker256256.bmp", // filename of texture map RGB16Bit(255,255,255), // color of terrain if no texture &terrain_pos, // initial position NULL, // initial rotations POLY4DV2_ATTR_RGB16 | POLY4DV2_ATTR_SHADE_MODE_CONSTANT // | POLY4DV2_ATTR_SHADE_MODE_FLAT // | POLY4DV2_ATTR_SHADE_MODE_GOURAUD | POLY4DV2_ATTR_SHADE_MODE_TEXTURE); #endif // set a scaling vector VECTOR4D_INITXYZ(&vscale,TERRAIN_WIDTH,1.00,TERRAIN_HEIGHT); // set position VECTOR4D_INITXYZ(&vpos, 0, 0, 0); Load_OBJECT4DV2_COB2(&obj_terrain, "plane01.cob", &vscale, &vpos, &vrot, VERTEX_FLAGS_SWAP_YZ | VERTEX_FLAGS_TRANSFORM_LOCAL /* VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD*/,0 ); // set a scaling vector VECTOR4D_INITXYZ(&vscale,60.00,60.00,60.00); // load all the objects in for (int index_obj=0; index_obj < NUM_OBJECTS; index_obj++) { Load_OBJECT4DV2_COB2(&obj_array[index_obj], object_filenames[index_obj], &vscale, &vpos, &vrot, VERTEX_FLAGS_SWAP_YZ | VERTEX_FLAGS_TRANSFORM_LOCAL /* VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD*/,0 ); } // end for index_obj // position the scenery objects randomly for (index = 0; index < NUM_SCENE_OBJECTS; index++) { // randomly position object scene_objects[index].x = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS); scene_objects[index].y = 75; // RAND_RANGE(-(UNIVERSE_RADIUS/2), (UNIVERSE_RADIUS/2)); scene_objects[index].z = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS); // select random object, use w to store value scene_objects[index].w = RAND_RANGE(0,NUM_OBJECTS-1); } // end for // select random velocities for (index = 0; index < NUM_SCENE_OBJECTS; index++) { // randomly position object scene_objects_vel[index].x = RAND_RANGE(-MAX_VEL, MAX_VEL); scene_objects_vel[index].y = 0; // RAND_RANGE(-MAX_VEL, MAX_VEL); scene_objects_vel[index].z = RAND_RANGE(-MAX_VEL, MAX_VEL); } // end for // set up lights Reset_Lights_LIGHTV2(lights2, MAX_LIGHTS); // create some working colors white.rgba = _RGBA32BIT(255,255,255,0); gray.rgba = _RGBA32BIT(150,150,150,0); black.rgba = _RGBA32BIT(0,0,0,0); red.rgba = _RGBA32BIT(255,0,0,0); green.rgba = _RGBA32BIT(0,255,0,0); blue.rgba = _RGBA32BIT(0,0,255,0); // ambient light Init_Light_LIGHTV2(lights2, // array of lights to work with AMBIENT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_AMBIENT, // ambient light type gray, black, black, // color for ambient term only NULL, NULL, // no need for pos or dir 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D dlight_dir = {-1,0,-1,1}; // directional light Init_Light_LIGHTV2(lights2, // array of lights to work with INFINITE_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_INFINITE, // infinite light type black, gray, black, // color for diffuse term only NULL, &dlight_dir, // need direction only 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D plight_pos = {0,200,0,1}; // point light Init_Light_LIGHTV2(lights2, // array of lights to work with POINT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_POINT, // pointlight type black, green, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.002,0, // linear attenuation only 0,0,1); // spotlight info NA VECTOR4D slight2_pos = {0,1000,0,1}; VECTOR4D slight2_dir = {-1,0,-1,1}; // spot light2 Init_Light_LIGHTV2(lights2, // array of lights to work with SPOT_LIGHT2_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_SPOTLIGHT2, // spot light type 2 black, red, black, // color for diffuse term only &slight2_pos, &slight2_dir, // need pos only 0,.001,0, // linear attenuation only 0,0,1); // create lookup for lighting engine RGB_16_8_IndexedRGB_Table_Builder(DD_PIXEL_FORMAT565, // format we want to build table for palette, // source palette rgblookup); // lookup table // create the z buffer Create_Zbuffer(&zbuffer, WINDOW_WIDTH, WINDOW_HEIGHT, ZBUFFER_ATTR_32BIT); // build alpha lookup table RGB_Alpha_Table_Builder(NUM_ALPHA_LEVELS, rgb_alpha_table); // load in the background Create_BOB(&background, 0,0,800,600,1, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Bitmap_File(&bitmap16bit, "cloud03.bmp"); Load_Frame_BOB16(&background, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); // return success return(1); } // end Game_Init
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
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! 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_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... // lock the back buffer DDraw_Lock_Back_Surface(); // draw a randomly positioned gouraud triangle with 3 random vertex colors POLYF4DV2 face; // set the vertices face.tvlist[0].x = (int)RAND_RANGE(0, screen_width - 1); face.tvlist[0].y = (int)RAND_RANGE(0, screen_height - 1); face.lit_color[0] = RGB16Bit(RAND_RANGE(0,255), RAND_RANGE(0,255), RAND_RANGE(0,255)); face.tvlist[1].x = (int)RAND_RANGE(0, screen_width - 1); face.tvlist[1].y = (int)RAND_RANGE(0, screen_height - 1); face.lit_color[1] = RGB16Bit(RAND_RANGE(0,255), RAND_RANGE(0,255), RAND_RANGE(0,255)); face.tvlist[2].x = (int)(int)RAND_RANGE(0, screen_width - 1); face.tvlist[2].y = (int)(int)RAND_RANGE(0, screen_height - 1); face.lit_color[2] = RGB16Bit(RAND_RANGE(0,255), RAND_RANGE(0,255), RAND_RANGE(0,255)); // draw the gouraud shaded triangle Draw_Gouraud_Triangle16(&face, back_buffer, back_lpitch); // unlock the back buffer DDraw_Unlock_Back_Surface(); // draw instructions Draw_Text_GDI("Press ESC to exit.", 0, 0, RGB(0,255,0), lpddsback); // flip the surfaces DDraw_Flip(); // wait a sec to see pretty triangle Wait_Clock(100); // 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_Init(void *parms) { // this function is where you do all the initialization // for your game int index; // looping var // start up DirectDraw (replace the parms as you desire) DDraw_Init(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP); // initialize directinput DInput_Init(); // acquire the keyboard DInput_Init_Keyboard(); // add calls to acquire other directinput devices here... // initialize directsound and directmusic DSound_Init(); DMusic_Init(); // hide the mouse if (!WINDOWED_APP) ShowCursor(FALSE); // seed random number generator srand(Start_Clock()); Open_Error_File("ERROR.TXT"); // initialize math engine Build_Sin_Cos_Tables(); // initialize the camera with 90 FOV, normalized coordinates Init_CAM4DV1(&cam, // the camera object CAM_MODEL_EULER, // the euler model &cam_pos, // initial camera position &cam_dir, // initial camera angles &cam_target, // no target 40.0, // near and far clipping planes 12000.0, 90.0, // field of view in degrees WINDOW_WIDTH, // size of final screen viewport WINDOW_HEIGHT); VECTOR4D terrain_pos = {0,0,0,0}; Generate_Terrain_OBJECT4DV2(&obj_terrain, // pointer to object TERRAIN_WIDTH, // width in world coords on x-axis TERRAIN_HEIGHT, // height (length) in world coords on z-axis TERRAIN_SCALE, // vertical scale of terrain "checkerheight01.bmp", // filename of height bitmap encoded in 256 colors "checker256256.bmp", // filename of texture map RGB16Bit(255,255,255), // color of terrain if no texture &terrain_pos, // initial position NULL, // initial rotations POLY4DV2_ATTR_RGB16 | POLY4DV2_ATTR_SHADE_MODE_FLAT // | POLY4DV2_ATTR_SHADE_MODE_GOURAUD | POLY4DV2_ATTR_SHADE_MODE_TEXTURE); // the shading attributes we would like // set up lights Reset_Lights_LIGHTV2(lights2, MAX_LIGHTS); // create some working colors white.rgba = _RGBA32BIT(255,255,255,0); gray.rgba = _RGBA32BIT(100,100,100,0); black.rgba = _RGBA32BIT(0,0,0,0); red.rgba = _RGBA32BIT(255,0,0,0); green.rgba = _RGBA32BIT(0,255,0,0); blue.rgba = _RGBA32BIT(0,0,255,0); // ambient light Init_Light_LIGHTV2(lights2, AMBIENT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_AMBIENT, // ambient light type gray, black, black, // color for ambient term only NULL, NULL, // no need for pos or dir 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D dlight_dir = {-1,1,-1,1}; // directional light Init_Light_LIGHTV2(lights2, INFINITE_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_INFINITE, // infinite light type black, gray, black, // color for diffuse term only NULL, &dlight_dir, // need direction only 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D plight_pos = {0,200,0,1}; // point light Init_Light_LIGHTV2(lights2, POINT_LIGHT_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_POINT, // pointlight type black, green, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.001,0, // linear attenuation only 0,0,1); // spotlight info NA // point light Init_Light_LIGHTV2(lights2, POINT_LIGHT2_INDEX, LIGHTV2_STATE_ON, // turn the light on LIGHTV2_ATTR_POINT, // pointlight type black, blue, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.002,0, // linear attenuation only 0,0,1); // spotlight info NA VECTOR4D slight2_pos = {0,200,0,1}; VECTOR4D slight2_dir = {-1,1,-1,1}; // create lookup for lighting engine RGB_16_8_IndexedRGB_Table_Builder(DD_PIXEL_FORMAT565, // format we want to build table for palette, // source palette rgblookup); // lookup table // create the z buffer Create_Zbuffer(&zbuffer, WINDOW_WIDTH, WINDOW_HEIGHT, ZBUFFER_ATTR_32BIT); // load background sounds wind_sound_id = DSound_Load_WAV("WIND.WAV"); // start the sounds DSound_Play(wind_sound_id, DSBPLAY_LOOPING); DSound_Set_Volume(wind_sound_id, 100); #if 0 // load in the cockpit image Load_Bitmap_File(&bitmap16bit, "lego01.BMP"); Create_BOB(&cockpit, 0,0,800,600,1, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Frame_BOB16(&cockpit, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); #endif // set single precission //_control87( _PC_24, MCW_PC ); // return success return(1); } // end Game_Init
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! int index; // looping var // start the timing clock Start_Clock(); // clear the drawing surface //DDraw_Fill_Surface(lpddsback, 0); // lock back buffer and copy background into it DDraw_Lock_Back_Surface(); // draw background Draw_Bitmap16(&background_bmp, back_buffer, back_lpitch,0); // draw table HLine16(TABLE_MIN_X, TABLE_MAX_X, TABLE_MIN_Y, RGB16Bit(0,255,0), back_buffer, back_lpitch); HLine16(TABLE_MIN_X, TABLE_MAX_X, TABLE_MAX_Y, RGB16Bit(0,255,0), back_buffer, back_lpitch); VLine16(TABLE_MIN_Y, TABLE_MAX_Y, TABLE_MIN_X, RGB16Bit(0,255,0), back_buffer, back_lpitch); VLine16(TABLE_MIN_Y, TABLE_MAX_Y, TABLE_MAX_X, RGB16Bit(0,255,0), back_buffer, back_lpitch); // unlock back surface DDraw_Unlock_Back_Surface(); // read keyboard DInput_Read_Keyboard(); // check for change of e if (keyboard_state[DIK_RIGHT]) cof_E+=.01; else if (keyboard_state[DIK_LEFT]) cof_E-=.01; float total_ke_x = 0, total_ke_y = 0; // move all the balls and compute system momentum for (index=0; index < NUM_BALLS; index++) { // move the ball balls[index].varsF[INDEX_X]+=balls[index].varsF[INDEX_XV]; balls[index].varsF[INDEX_Y]+=balls[index].varsF[INDEX_YV]; // add x,y contributions to kinetic energy total_ke_x+=(balls[index].varsF[INDEX_XV]*balls[index].varsF[INDEX_XV]*balls[index].varsF[INDEX_MASS]); total_ke_y+=(balls[index].varsF[INDEX_YV]*balls[index].varsF[INDEX_YV]*balls[index].varsF[INDEX_MASS]); } // end fof // test for boundary collision with virtual table edge, no need for collision // response here, I know what's going to happen :) for (index=0; index < NUM_BALLS; index++) { if ((balls[index].varsF[INDEX_X] >= TABLE_MAX_X-BALL_RADIUS) || (balls[index].varsF[INDEX_X] <= TABLE_MIN_X+BALL_RADIUS)) { // invert velocity balls[index].varsF[INDEX_XV] = -balls[index].varsF[INDEX_XV]; balls[index].varsF[INDEX_X]+=balls[index].varsF[INDEX_XV]; balls[index].varsF[INDEX_Y]+=balls[index].varsF[INDEX_YV]; // start a hit sound Ball_Sound(); } // end if if ((balls[index].varsF[INDEX_Y] >= TABLE_MAX_Y-BALL_RADIUS) || (balls[index].varsF[INDEX_Y] <= TABLE_MIN_Y+BALL_RADIUS)) { // invert velocity balls[index].varsF[INDEX_YV] =-balls[index].varsF[INDEX_YV]; balls[index].varsF[INDEX_X]+=balls[index].varsF[INDEX_XV]; balls[index].varsF[INDEX_Y]+=balls[index].varsF[INDEX_YV]; // play sound Ball_Sound(); } // end if } // end for index // draw the balls for (index=0; index < NUM_BALLS; index++) { balls[index].x = balls[index].varsF[INDEX_X]+0.5-BALL_RADIUS; balls[index].y = balls[index].varsF[INDEX_Y]+0.5-BALL_RADIUS; Draw_BOB16(&balls[index], lpddsback); } // end for // draw the velocity vectors DDraw_Lock_Back_Surface(); for (index=0; index < NUM_BALLS; index++) { Draw_Clip_Line16(balls[index].varsF[INDEX_X]+0.5, balls[index].varsF[INDEX_Y]+0.5, balls[index].varsF[INDEX_X]+2*balls[index].varsF[INDEX_XV]+0.5, balls[index].varsF[INDEX_Y]+2*balls[index].varsF[INDEX_YV]+0.5, RGB16Bit(255,255,255), back_buffer, back_lpitch); } // end for DDraw_Unlock_Back_Surface(); // draw the title Draw_Text_GDI("(16-Bit Version) ELASTIC Object-Object Collision Response DEMO, Press <ESC> to Exit.",10, 10,RGB(255,255,255), lpddsback); // draw the title sprintf(buffer,"Coefficient of Restitution e=%f, use <RIGHT>, <LEFT> arrow to change.", cof_E); Draw_Text_GDI(buffer,10, 30,RGB(255,255,255), lpddsback); sprintf(buffer,"Total System Kinetic Energy Sum(1/2MiVi^2)=%f ",0.5*sqrt(total_ke_x*total_ke_x+total_ke_y*total_ke_y)); Draw_Text_GDI(buffer,10, 465, RGB(255,255,255), lpddsback); // flip the surfaces DDraw_Flip(); // run collision response algorithm here Collision_Response(); // sync to 30 fps = 1/30sec = 33 ms Wait_Clock(33); // check of user is trying to exit if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE]) { PostMessage(main_window_handle, WM_DESTROY,0,0); // stop all sounds DSound_Stop_All_Sounds(); } // end if // return success return(1); } // end Game_Main
void Collision_Response(void) { // this function does all the "real" physics to determine if there has // been a collision between any ball and any other ball, if there is a collision // the function uses the mass of each ball along with the intial velocities to // compute the resulting velocities // from the book we know that in general // va2 = (e+1)*mb*vb1+va1(ma - e*mb)/(ma+mb) // vb2 = (e+1)*ma*va1+vb1(ma - e*mb)/(ma+mb) // and the objects will have direction vectors co-linear to the normal // of the point of collision, but since we are using spheres here as the objects // we know that the normal to the point of collision is just the vector from the // center's of each object, thus the resulting velocity vector of each ball will // be along this normal vector direction // step 1: test each object against each other object and test for a collision // there are better ways to do this other than a double nested loop, but since // there are a small number of objects this is fine, also we want to somewhat model // if two or more balls hit simulataneously for (int ball_a = 0; ball_a < NUM_BALLS; ball_a++) { for (int ball_b = ball_a+1; ball_b < NUM_BALLS; ball_b++) { if (ball_a == ball_b) continue; // compute the normal vector from a->b float nabx = (balls[ball_b].varsF[INDEX_X] - balls[ball_a].varsF[INDEX_X] ); float naby = (balls[ball_b].varsF[INDEX_Y] - balls[ball_a].varsF[INDEX_Y] ); float length = sqrt(nabx*nabx + naby*naby); // is there a collision? if (length <= 2.0*(BALL_RADIUS*.75)) { // the balls have made contact, compute response // compute the response coordinate system axes // normalize normal vector nabx/=length; naby/=length; // compute the tangential vector perpendicular to normal, simply rotate vector 90 float tabx = -naby; float taby = nabx; // draw collision DDraw_Lock_Primary_Surface(); // blue is normal Draw_Clip_Line16(balls[ball_a].varsF[INDEX_X]+0.5, balls[ball_a].varsF[INDEX_Y]+0.5, balls[ball_a].varsF[INDEX_X]+20*nabx+0.5, balls[ball_a].varsF[INDEX_Y]+20*naby+0.5, RGB16Bit(0,0,255), primary_buffer, primary_lpitch); // yellow is tangential Draw_Clip_Line16(balls[ball_a].varsF[INDEX_X]+0.5, balls[ball_a].varsF[INDEX_Y]+0.5, balls[ball_a].varsF[INDEX_X]+20*tabx+0.5, balls[ball_a].varsF[INDEX_Y]+20*taby+0.5, RGB16Bit(0,255,255), primary_buffer, primary_lpitch); DDraw_Unlock_Primary_Surface(); // tangential is also normalized since it's just a rotated normal vector // step 2: compute all the initial velocities // notation ball: (a,b) initial: i, final: f, n: normal direction, t: tangential direction float vait = DOT_PRODUCT(balls[ball_a].varsF[INDEX_XV], balls[ball_a].varsF[INDEX_YV], tabx, taby); float vain = DOT_PRODUCT(balls[ball_a].varsF[INDEX_XV], balls[ball_a].varsF[INDEX_YV], nabx, naby); float vbit = DOT_PRODUCT(balls[ball_b].varsF[INDEX_XV], balls[ball_b].varsF[INDEX_YV], tabx, taby); float vbin = DOT_PRODUCT(balls[ball_b].varsF[INDEX_XV], balls[ball_b].varsF[INDEX_YV], nabx, naby); // now we have all the initial velocities in terms of the n and t axes // step 3: compute final velocities after collision, from book we have // note: all this code can be optimized, but I want you to see what's happening :) float ma = balls[ball_a].varsF[INDEX_MASS]; float mb = balls[ball_b].varsF[INDEX_MASS]; float vafn = (mb*vbin*(cof_E+1) + vain*(ma - cof_E*mb)) / (ma + mb); float vbfn = (ma*vain*(cof_E+1) - vbin*(ma - cof_E*mb)) / (ma + mb); // now luckily the tangential components are the same before and after, so float vaft = vait; float vbft = vbit; // and that's that baby! // the velocity vectors are: // object a (vafn, vaft) // object b (vbfn, vbft) // the only problem is that we are in the wrong coordinate system! we need to // translate back to the original x,y coordinate system, basically we need to // compute the sum of the x components relative to the n,t axes and the sum of // the y components relative to the n,t axis, since n,t may both have x,y // components in the original x,y coordinate system float xfa = vafn*nabx + vaft*tabx; float yfa = vafn*naby + vaft*taby; float xfb = vbfn*nabx + vbft*tabx; float yfb = vbfn*naby + vbft*taby; // store results balls[ball_a].varsF[INDEX_XV] = xfa; balls[ball_a].varsF[INDEX_YV] = yfa; balls[ball_b].varsF[INDEX_XV] = xfb; balls[ball_b].varsF[INDEX_YV] = yfb; // update position balls[ball_a].varsF[INDEX_X]+=balls[ball_a].varsF[INDEX_XV]; balls[ball_a].varsF[INDEX_Y]+=balls[ball_a].varsF[INDEX_YV]; balls[ball_b].varsF[INDEX_X]+=balls[ball_b].varsF[INDEX_XV]; balls[ball_b].varsF[INDEX_Y]+=balls[ball_b].varsF[INDEX_YV]; } // end if } // end for ball2 } // end for ball1 } // end Collision_Response
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! int index; // looping var static rotate = 0; // start the timing clock Start_Clock(); // lock back buffer and copy background into it DDraw_Lock_Back_Surface(); // draw background Draw_Bitmap16(&background_bmp, back_buffer, back_lpitch,0); // draw shape Draw_Polygon2D16(&shape, back_buffer, back_lpitch); // have a little fun if (++rotate > 10) { Rotate_Polygon2D(&shape,1); rotate=0; } // unlock back surface DDraw_Unlock_Back_Surface(); // read keyboard DInput_Read_Keyboard(); // move the balls and compute collisions Compute_Collisions(); // draw the balls for (index=0; index < NUM_BALLS; index++) { balls[index].x = balls[index].varsF[INDEX_X]+0.5-BALL_RADIUS; balls[index].y = balls[index].varsF[INDEX_Y]+0.5-BALL_RADIUS; Draw_BOB16(&balls[index], lpddsback); } // end for // draw the velocity vectors DDraw_Lock_Back_Surface(); for (index=0; index < NUM_BALLS; index++) { Draw_Clip_Line16(balls[index].varsF[INDEX_X]+0.5, balls[index].varsF[INDEX_Y]+0.5, balls[index].varsF[INDEX_X]+2*balls[index].varsF[INDEX_XV]+0.5, balls[index].varsF[INDEX_Y]+2*balls[index].varsF[INDEX_YV]+0.5, RGB16Bit(255,255,255), back_buffer, back_lpitch); } // end for DDraw_Unlock_Back_Surface(); // draw the title Draw_Text_GDI("(16-Bit Version) Object to Contour Collision DEMO, Press <ESC> to Exit.",10, 10,RGB(255,255,255), lpddsback); // flip the surfaces DDraw_Flip(); // run collision algorithm here Compute_Collisions(); // sync to 30 fps = 1/30sec = 33 ms Wait_Clock(33); // check of user is trying to exit if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE]) { PostMessage(main_window_handle, WM_DESTROY,0,0); // stop all sounds DSound_Stop_All_Sounds(); } // end if // return success return(1); } // end Game_Main
int Game_Init(void *parms) { // this function is where you do all the initialization // for your game int index; // looping varsIable char filename[80]; // used to build up filenames // seed random number generate srand(Start_Clock()); // initialize directdraw, very important that in the call // to setcooperativelevel that the flag DDSCL_MULTITHREADED is used // which increases the response of directX graphics to // take the global critical section more frequently DDraw_Init(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP); // load background image Load_Bitmap_File(&bitmap8bit, "GREENGRID24.BMP"); Create_Bitmap(&background_bmp,0,0,640,480,16); Load_Image_Bitmap16(&background_bmp, &bitmap8bit,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap8bit); // load the bitmaps Load_Bitmap_File(&bitmap8bit, "POOLBALLS24.BMP"); // create master ball Create_BOB(&balls[0],0,0,24,24,6,BOB_ATTR_MULTI_FRAME | BOB_ATTR_VISIBLE, DDSCAPS_SYSTEMMEMORY,0,16); // load the imagery in for (index=0; index < 6; index++) Load_Frame_BOB16(&balls[0], &bitmap8bit, index, index,0,BITMAP_EXTRACT_MODE_CELL); // create all the clones for (index=1; index < NUM_BALLS; index++) Clone_BOB(&balls[0], &balls[index]); // now set the initial conditions of all the balls for (index=0; index < NUM_BALLS; index++) { // set position in center of object balls[index].varsF[INDEX_X] = RAND_RANGE( SHAPE_CENTER_X-50, SHAPE_CENTER_X+50); balls[index].varsF[INDEX_Y] = RAND_RANGE( SHAPE_CENTER_Y-50, SHAPE_CENTER_Y+50); do { // set initial velocity balls[index].varsF[INDEX_XV] = RAND_RANGE(-100, 100)/30; balls[index].varsF[INDEX_YV] = RAND_RANGE(-100, 100)/30; } while (balls[index].varsF[INDEX_XV]==0 && balls[index].varsF[INDEX_XV]==0); // set ball color balls[index].curr_frame = rand()%6; } // end for index // unload bitmap image Unload_Bitmap_File(&bitmap8bit); // define points of shape VERTEX2DF shape_vertices[10] = { 328-SHAPE_CENTER_X,60-SHAPE_CENTER_Y, 574-SHAPE_CENTER_X,162-SHAPE_CENTER_Y, 493-SHAPE_CENTER_X,278-SHAPE_CENTER_Y, 605-SHAPE_CENTER_X,384-SHAPE_CENTER_Y, 484-SHAPE_CENTER_X,433-SHAPE_CENTER_Y, 306-SHAPE_CENTER_X,349-SHAPE_CENTER_Y, 150-SHAPE_CENTER_X,413-SHAPE_CENTER_Y, 28-SHAPE_CENTER_X,326-SHAPE_CENTER_Y, 152-SHAPE_CENTER_X,281-SHAPE_CENTER_Y, 73-SHAPE_CENTER_X,138-SHAPE_CENTER_Y }; // initialize shape shape.state = 1; // turn it on shape.num_verts = 10; shape.x0 = SHAPE_CENTER_X; shape.y0 = SHAPE_CENTER_Y; shape.xv = 0; shape.yv = 0; shape.color = RGB16Bit(0,255,0); // green shape.vlist = new VERTEX2DF [shape.num_verts]; for (index = 0; index < shape.num_verts; index++) shape.vlist[index] = shape_vertices[index]; // hide the mouse if (!WINDOWED_APP) ShowCursor(FALSE); // initialize directinput DInput_Init(); // acquire the keyboard only DInput_Init_Keyboard(); // build the 360 degree look ups Build_Sin_Cos_Tables(); // initilize DirectSound DSound_Init(); // load background sounds ball_ids[0] = DSound_Load_WAV("PBALL.WAV"); // clone sounds for (index=1; index<8; index++) ball_ids[index] = DSound_Replicate_Sound(ball_ids[0]); // return success return(1); } // end Game_Init
int Game_Main(void *parms, int num_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! int index; // looping var int dx,dy; // general deltas used in collision detection static int player_moving = 0; // tracks player motion // check of user is trying to exit if (KEY_DOWN(VK_ESCAPE) || KEY_DOWN(VK_SPACE)) PostMessage(main_window_handle, WM_DESTROY,0,0); // start the timing clock Start_Clock(); // clear the drawing surface DDraw_Fill_Surface(lpddsback, 0); // lock the back buffer DDraw_Lock_Back_Surface(); // draw the background reactor image Draw_Bitmap16(&reactor, back_buffer, back_lpitch, 0); // unlock the back buffer DDraw_Unlock_Back_Surface(); // get player input // get the keyboard data lpdikey->GetDeviceState(256, (LPVOID)keyboard_state); // reset motion flag player_moving = 0; // test direction of motion, this is a good example of testing the keyboard // although the code could be optimized this is more educational if (keyboard_state[DIK_RIGHT] && keyboard_state[DIK_UP]) { // move skelaton skelaton.x+=2; skelaton.y-=2; dx=2; dy=-2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_NEAST) Set_Animation_BOB(&skelaton,SKELATON_NEAST); } // end if else if (keyboard_state[DIK_LEFT] && keyboard_state[DIK_UP]) { // move skelaton skelaton.x-=2; skelaton.y-=2; dx=-2; dy=-2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_NWEST) Set_Animation_BOB(&skelaton,SKELATON_NWEST); } // end if else if (keyboard_state[DIK_LEFT] && keyboard_state[DIK_DOWN]) { // move skelaton skelaton.x-=2; skelaton.y+=2; dx=-2; dy=2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_SWEST) Set_Animation_BOB(&skelaton,SKELATON_SWEST); } // end if else if (keyboard_state[DIK_RIGHT] && keyboard_state[DIK_DOWN]) { // move skelaton skelaton.x+=2; skelaton.y+=2; dx=2; dy=2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_SEAST) Set_Animation_BOB(&skelaton,SKELATON_SEAST); } // end if else if (keyboard_state[DIK_RIGHT]) { // move skelaton skelaton.x+=2; dx=2; dy=0; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_EAST) Set_Animation_BOB(&skelaton,SKELATON_EAST); } // end if else if (keyboard_state[DIK_LEFT]) { // move skelaton skelaton.x-=2; dx=-2; dy=0; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_WEST) Set_Animation_BOB(&skelaton,SKELATON_WEST); } // end if else if (keyboard_state[DIK_UP]) { // move skelaton skelaton.y-=2; dx=0; dy=-2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_NORTH) Set_Animation_BOB(&skelaton,SKELATON_NORTH); } // end if else if (keyboard_state[DIK_DOWN]) { // move skelaton skelaton.y+=2; dx=0; dy=+2; // set motion flag player_moving = 1; // check animation needs to change if (skelaton.curr_animation != SKELATON_SOUTH) Set_Animation_BOB(&skelaton,SKELATON_SOUTH); } // end if // only animate if player is moving if (player_moving) { // animate skelaton Animate_BOB(&skelaton); // see if skelaton hit a wall // lock surface, so we can scan it DDraw_Lock_Back_Surface(); // call the color scanner with WALL_COLOR the color of the walls // try to center the scan on the feet of the player // note since we are uin 16-bit mode, we need to scan the 16 bit value then compare // it against the 16-bit color code for the green pixel which has values RB(41,231,41) // but depending if this is a 5.5.5 or 5.6.5 the 16-bit value will be different, however // during ddraw_init RGB16Bit() was vectored (function pointer) to either 5.5.5 or 5.6.5 // depending on the actual surface mode, so it should all work out :) if (Color_Scan16(skelaton.x+16, skelaton.y+16, skelaton.x+skelaton.width-16, skelaton.y+skelaton.height-16, RGB16Bit(WALL_COLOR_R, WALL_COLOR_G, WALL_COLOR_B), RGB16Bit(WALL_COLOR_R, WALL_COLOR_G, WALL_COLOR_B), back_buffer,back_lpitch)) { // back the skelaton up along its last trajectory skelaton.x-=dx; skelaton.y-=dy; } // end if // done, so unlock DDraw_Unlock_Back_Surface(); // check if skelaton is off screen if (skelaton.x < 0 || skelaton.x > (screen_width - skelaton.width)) skelaton.x-=dx; if (skelaton.y < 0 || skelaton.y > (screen_height - skelaton.height)) skelaton.y-=dy; } // end if // draw the skelaton Draw_BOB16(&skelaton, lpddsback); // draw some text Draw_Text_GDI("I STILL HAVE A BONE TO PICK!",0,screen_height - 32,RGB(32,32,32),lpddsback); Draw_Text_GDI("(16-Bit Version) USE ARROW KEYS TO MOVE, <ESC> TO EXIT.",0,0,RGB(32,32,32),lpddsback); // flip the surfaces DDraw_Flip(); // sync to 30 fps Wait_Clock(30); // return success return(1); } // end Game_Main