home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chapter70 / dispset.c
C/C++ Source or Header  |  1997-06-18  |  62KB  |  1,883 lines

  1. /* dispset.c
  2.     
  3.    Win32 program to demonstrate EnumDisplaySettings() and
  4.    ChangeDisplaySettings(), and to demonstrate using the
  5.    normal mouse APIs for game input. Note that DirectInput
  6.    provides better mouse support (movement granularity is
  7.    finer, and one call per frame suffices for DirectInput),
  8.    but is not yet available on all systems or for Windows
  9.    NT.
  10.  
  11.    Program pops up a listbox of available video modes;
  12.    the user selects one by double-clicking, then navigates
  13.    around using the mouse for orientation and the keyboard
  14.    for linear motion.
  15.  
  16.    Modified from zsort.c, a demonstration program for
  17.    z-sorted hidden-surface removal; all zsort comments
  18.    have been left in.
  19.  
  20.    Note: loss of focus (such as Alt-Tab) is not handled in
  21.    this program; consequently, when you Alt-Tab away, the
  22.    video mode and mouse speed remain as set by zsort. In a
  23.    commercial-quality game, RestoreDefaultMode() and
  24.    StopGameMouse() would be called on loss of focus, and
  25.    SetVMode() and StartGameMouse() would be called when
  26.    focus was reacquired.
  27.  
  28.    Note: the fact that the mouse clamps at the border of
  29.    the screen is the motivation for multiple calls per
  30.    frame to AccumulateGameMouseMove(); these calls need
  31.    to be close enough together in time so that the mouse
  32.    can never reach the edge of the screen. These calls
  33.    should be spaced roughly the same time apart and should
  34.    be kept to a reasonably low level (Quake does 6 calls
  35.    per frame), because the calls themselves do take some
  36.    time.
  37.  
  38.    Note: resolution is freely changeable on Win95 and NT,
  39.    but bits-per-pixel is only changeable on the fly under
  40.    NT, not Win95. This will cause fewer modes to be be
  41.    listed on Win95 than on NT for a given video adapter.
  42. */
  43.  
  44. /*
  45.    Win32 program to demonstrate z-sorted spans.
  46.  
  47.    Derived from the VC++ generic sample application.
  48.    Tested with VC++ 2.0 running on Windows NT 3.5.
  49.  
  50.    Note: in this implementation, polygon faces must not be
  51.    interpenetrating. Also, correct sorting is not guaranteed
  52.    if two polygonal objects butt up against each other. In other
  53.    words, each polygonal object must be made of a continuous,
  54.    non-self-intersecting skin, and polygonal objects must not
  55.    interpenetrate or touch in order for proper sorting to result.
  56.    More complex, slower sorting is required to make those cases
  57.    work reliably.
  58.    
  59.    Note: polygon processing could be considerably more efficient
  60.    if polygons shared common edges and edges shared common vertices.
  61.    Also, indirection to vertices could be used to avoid having to
  62.    copy all the vertices during every clip test. Outcode-type
  63.    testing could be used to determine completely clipped or
  64.    unclipped polygons ahead of time, avoiding the need to clip and
  65.    copy entirely for such polygons. Outcode-type tests work best in
  66.    viewspace, with the frustum normalized so that the field of view
  67.    is 90 degrees, so simple compares, rather than dot products, can
  68.    be used to categorize points with respect to the frustum. See
  69.    _Computer Graphics_, by Foley & van Dam, or _Procedural Elements
  70.    of Computer Graphics_, by Rogers, for further information.
  71. */
  72.  
  73. #include <windows.h>       // required for all Windows applications
  74. #include <stdlib.h>
  75. #include <stdio.h>
  76. #include <math.h>
  77.  
  78. #define MAX_POLY_VERTS      8       // assumes polygons have no more than
  79.                                     //  four sides and are clipped a
  80.                                     //  maximum of four times by frustum.
  81.                                     //  Must be increased for more sides
  82.                                     //  or more clip planes
  83. #define MAX_SCREEN_HEIGHT   2048
  84. #define MOVEMENT_SPEED      3.0
  85. #define VMOVEMENT_SPEED     3.0
  86. #define MAX_MOVEMENT_SPEED  30.0
  87. #define PI                  3.141592
  88. #define ROLL_SPEED          (PI/20.0)
  89. #define PITCH_SPEED         (PI/20.0)
  90. #define YAW_SPEED           (PI/20.0)
  91. #define MAX_COORD           0x4000
  92. #define NUM_FRUSTUM_PLANES  4
  93. #define CLIP_PLANE_EPSILON  0.0001
  94. #define MAX_SPANS           10000
  95. #define MAX_SURFS           1000
  96. #define MAX_EDGES           5000
  97.  
  98.  
  99. typedef struct {
  100.     double v[3];
  101. } point_t;
  102.  
  103. typedef struct {
  104.     double   x, y;
  105. } point2D_t;
  106.  
  107. typedef struct {
  108.     int x, y;
  109.     int count;
  110.     int color;
  111. } span_t;
  112.  
  113. typedef struct {
  114.     double  distance;
  115.     point_t normal;
  116. } plane_t;
  117.  
  118. typedef struct {
  119.     int         color;
  120.     int         numverts;
  121.     point_t     verts[MAX_POLY_VERTS];
  122.     plane_t     plane;
  123. } polygon_t;
  124.  
  125. typedef struct surf_s {
  126.     struct surf_s   *pnext, *pprev;
  127.     int             color;
  128.     int             visxstart;
  129.     double          zinv00, zinvstepx, zinvstepy;
  130.     int             state;
  131. } surf_t;
  132.  
  133. typedef struct {
  134.     int         numverts;
  135.     point2D_t   verts[MAX_POLY_VERTS];
  136. } polygon2D_t;
  137.  
  138. typedef struct convexobject_s {
  139.     struct convexobject_s   *pnext;
  140.     point_t                 center;
  141.     int                     numpolys;
  142.     polygon_t               *ppoly;
  143. } convexobject_t;
  144.  
  145. typedef struct edge_s {
  146.     int             x;
  147.     int             xstep;
  148.     int             leading;
  149.     surf_t          *psurf;
  150.     struct edge_s   *pnext, *pprev;
  151.     struct edge_s   *pnextremove;
  152. } edge_t;
  153.  
  154. BITMAPINFO *pbmiDIB;        // pointer to the BITMAPINFO
  155. char *pDIB, *pDIBBase;        // pointers to DIB section we'll draw into
  156. HBITMAP hDIBSection;        // handle of DIB section
  157. HINSTANCE hInst;            // current instance
  158. char szAppName[] = "Clip";  // The name of this application
  159. char szTitle[]   = "3D clipping demo"; // The title bar text
  160. HPALETTE hpalold, hpalDIB;
  161. HWND hwndOutput, hwndLB;
  162. int DIBWidth, DIBHeight;
  163. int DIBPitch;
  164. double  roll, pitch, yaw;
  165. double  currentspeed;
  166. point_t currentpos;
  167. double  fieldofview, xcenter, ycenter;
  168. double  xscreenscale, yscreenscale, maxscale;
  169. double  maxscreenscaleinv;
  170. int     numobjects;
  171. double  speedscale = 1.0;
  172. double  mousespeedscale = 0.01;
  173. plane_t frustumplanes[NUM_FRUSTUM_PLANES];
  174.  
  175. double  mroll[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
  176. double  mpitch[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
  177. double  myaw[3][3] =  {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
  178. point_t vpn, vright, vup;
  179. point_t xaxis = {1, 0, 0};
  180. point_t zaxis = {0, 0, 1};
  181.  
  182. polygon_t polys0[] = {
  183. {15, 4, {{-10,10,-10}, {10,10,-10}, {10,-10,-10}, {-10,-10,-10}},
  184.     {10, {0, 0, -1}}},
  185. {14, 4, {{10,10,-10}, {10,10,10}, {10,-10,10}, {10,-10,-10}},
  186.     {10, {1, 0, 0}}},
  187. {13, 4, {{10,10,10}, {-10,10,10}, {-10,-10,10}, {10,-10,10}},
  188.     {10, {0, 0, 1}}},
  189. {12, 4, {{-10,10,10}, {-10,10,-10}, {-10,-10,-10}, {-10,-10,10}},
  190.     {10, {-1, 0, 0}}},
  191. {11, 4, {{-10,10,-10}, {-10,10,10}, {10,10,10}, {10,10,-10}},
  192.     {10, {0, 1, 0}}},
  193. {10, 4, {{-10,-10,-10}, {10,-10,-10}, {10,-10,10}, {-10,-10,10}},
  194.     {10, {0, -1, 0}}},
  195. };
  196.  
  197. polygon_t polys1[] = {
  198. {1, 4, {{-200,0,-200}, {-200,0,200},
  199.         {200,0,200}, {200,0,-200}}, {0, {0, 1, 0}}},
  200. };
  201.  
  202. polygon_t polys2[] = {
  203. {6, 4, {{0,10,0}, {20,10,0}, {10,10,-10}, {0,10,-10}},
  204.     {10, {0, 1, 0}}},
  205. {6, 4, {{-10,10,10}, {0,10,10}, {0,10,0}, {-10,10,0}},
  206.     {10, {0, 1, 0}}},
  207. {6, 4, {{0,10,0}, {0,10,-10}, {-10,10,-10}, {-10,10,0}},
  208.     {10, {0, 1, 0}}},
  209. {5, 4, {{0,-10,0}, {0,-10,-10}, {10,-10,-10}, {20,-10,0}},
  210.     {10, {0, -1, 0}}},
  211. {5, 4, {{-10,-10,10}, {-10,-10,0}, {0,-10,0}, {0,-10,10}},
  212.     {10, {0, -1, 0}}},
  213. {5, 4, {{-10,-10,0}, {-10,-10,-10}, {0,-10,-10}, {0,-10,0}},
  214.     {10, {0, -1, 0}}},
  215. {4, 4, {{-10,10,-10}, {10,10,-10}, {10,-10,-10}, {-10,-10,-10}},
  216.     {10, {0, 0, -1}}},
  217. {3, 4, {{10,10,-10}, {20,10,0}, {20,-10,0}, {10,-10,-10}},
  218.     {14.14, {0.707, 0, -0.707}}},
  219. {2, 4, {{20,10,0}, {0,10,0}, {0,-10,0}, {20,-10,0}},
  220.     {0, {0, 0, 1}}},
  221. {9, 4, {{0,10,0}, {0,10,10}, {0,-10,10}, {0,-10,0}},
  222.     {0, {1, 0, 0}}},
  223. {15, 4, {{0,10,10}, {-10,10,10}, {-10,-10,10}, {0,-10,10}},
  224.     {10, {0, 0, 1}}},
  225. {14, 4, {{-10,10,10}, {-10,10,-10}, {-10,-10,-10}, {-10,-10,10}},
  226.     {10, {-1, 0, 0}}},
  227. };
  228.  
  229. extern convexobject_t   objecthead;
  230.  
  231. convexobject_t objects[] = {
  232. {&objects[1], {-50,0,70}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  233. {&objects[2], {0,20,70}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  234. {&objects[3], {50,0,70}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  235. {&objects[4], {-50,0,-70}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  236. {&objects[5], {0,20,-70}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  237. {&objects[6], {50,30,-70}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  238. {&objects[7], {-50,15,0}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  239. {&objects[8], {50,15,0}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  240. {&objects[9], {0,50,0}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  241. {&objects[10], {-100,100,115}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  242. {&objects[11], {-100,150,120}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  243. {&objects[12], {100,200,100}, sizeof(polys0) / sizeof(polys0[0]), polys0},
  244. {&objects[13], {100,100,100}, sizeof(polys2) / sizeof(polys2[0]), polys2},
  245. {&objecthead, {0,-20,0}, sizeof(polys1) / sizeof(polys1[0]), polys1},
  246. };
  247.  
  248. // Head and tail for the object list
  249. convexobject_t objecthead = {&objects[0]};
  250.  
  251. // Span, edge, and surface lists
  252. span_t  spans[MAX_SPANS];
  253. edge_t  edges[MAX_EDGES];
  254. surf_t  surfs[MAX_SURFS];
  255.  
  256. // Bucket list of new edges to add on each scan line
  257. edge_t  newedges[MAX_SCREEN_HEIGHT];
  258.  
  259. // Bucket list of edges to remove on each scan line
  260. edge_t  *removeedges[MAX_SCREEN_HEIGHT];
  261.  
  262. // Head and tail for the active edge list
  263. edge_t  edgehead;
  264. edge_t  edgetail;
  265.  
  266. // Edge used as sentinel of new edge lists
  267. edge_t  maxedge = {0x7FFFFFFF};
  268.  
  269. // Head/tail/sentinel/background surface of active surface stack
  270. surf_t  surfstack;
  271.  
  272. // pointers to next available surface and edge
  273. surf_t  *pavailsurf;
  274. edge_t  *pavailedge;
  275.  
  276. int window_center_x, window_center_y;
  277. int currentcolor;
  278. int inLB, curLBIndex;
  279.  
  280. typedef struct {
  281.     unsigned int width;
  282.     unsigned int height;
  283.     unsigned int bpp;
  284.     char description[20];
  285. } VMODE;
  286.  
  287. #define MAXVMODES   100
  288. VMODE vmodes[MAXVMODES];
  289. int numvmodes;
  290.  
  291. VMODE lowresmodes[] = {
  292.  {320, 200, 8}, {320, 200, 15}, {320, 200, 16}, {320, 200, 24}, {320, 200, 32},
  293.  {320, 240, 8}, {320, 240, 15}, {320, 240, 16}, {320, 240, 24}, {320, 240, 32},
  294.  {320, 350, 8}, {320, 350, 15}, {320, 350, 16}, {320, 350, 24}, {320, 350, 32},
  295.  {320, 400, 8}, {320, 400, 15}, {320, 400, 16}, {320, 400, 24}, {320, 400, 32},
  296.  {320, 480, 8}, {320, 480, 15}, {320, 480, 16}, {320, 480, 24}, {320, 480, 32},
  297.  {360, 200, 8}, {360, 200, 15}, {360, 200, 16}, {360, 200, 24}, {360, 200, 32},
  298.  {360, 240, 8}, {360, 240, 15}, {360, 240, 16}, {360, 240, 24}, {360, 240, 32},
  299.  {360, 350, 8}, {360, 350, 15}, {360, 350, 16}, {360, 350, 24}, {360, 350, 32},
  300.  {360, 400, 8}, {360, 400, 15}, {360, 400, 16}, {360, 400, 24}, {360, 400, 32},
  301.  {360, 480, 8}, {360, 480, 15}, {360, 480, 16}, {360, 480, 24}, {360, 480, 32},
  302.  {400, 300, 8}, {400, 300, 15}, {400, 300, 16}, {400, 300, 24}, {400, 300, 32},
  303.  {512, 384, 8}, {512, 384, 15}, {512, 384, 16}, {512, 384, 24}, {512, 384, 32},
  304. };
  305.  
  306. #define NUMLOWRESMODES (sizeof(lowresmodes)/sizeof(lowresmodes[0]))
  307.  
  308. BOOL InitApplication(HANDLE);
  309. BOOL InitInstance(HANDLE, int);
  310. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  311. int StartGameMouse (void);
  312. void StopGameMouse (void);
  313. void AccumulateGameMouseMove (void);
  314. void GetGameMouseMoveForFrame (int * mouse_x_move, int * mouse_y_move);
  315. void DoMouseMove(void);
  316. void UpdateWorld(void);
  317. int PutUpModeListBox(void);
  318. void RestoreDefaultMode(void);
  319. void SetVMode(int curLBIndex);
  320.  
  321. /////////////////////////////////////////////////////////////////////
  322. // WinMain
  323. /////////////////////////////////////////////////////////////////////
  324. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  325.     LPSTR lpCmdLine, int nCmdShow)
  326. {
  327.     MSG msg;
  328.     HANDLE hAccelTable;
  329.  
  330.     if (!hPrevInstance) {       // Other instances of app running?
  331.         if (!InitApplication(hInstance)) { // Initialize shared things
  332.             return (FALSE);     // Exits if unable to initialize
  333.         }
  334.     }
  335.  
  336.     // Perform initializations that apply to a specific instance
  337.     if (!InitInstance(hInstance, nCmdShow)) {
  338.         return (FALSE);
  339.     }
  340.  
  341.     if (!PutUpModeListBox())
  342.         return (FALSE);
  343.  
  344.     hAccelTable = LoadAccelerators (hInstance, szAppName);
  345.  
  346.     // Acquire and dispatch messages until a WM_QUIT message is
  347.     // received
  348.     for (;;) {
  349.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  350.             do {
  351.                 if (msg.message == WM_QUIT) {
  352.                     goto Done;
  353.                 }
  354.                   if (!TranslateAccelerator (msg.hwnd, hAccelTable,
  355.                           &msg)) {
  356.                     TranslateMessage(&msg);// xlates virt keycodes
  357.                        DispatchMessage(&msg); // Dispatches msg to window
  358.                       }
  359.             } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
  360.         }
  361.  
  362.         if (!inLB)
  363.         {
  364.             // Do any mouse-controlled move for this frame
  365.             DoMouseMove();
  366.  
  367.             // Update the world
  368.             UpdateWorld();
  369.         }
  370.     }
  371.  
  372. Done:
  373.     return (msg.wParam); // Returns the value from PostQuitMessage
  374.  
  375.     lpCmdLine; // This will prevent 'unused formal parameter' warnings
  376. }
  377.  
  378. /////////////////////////////////////////////////////////////////////
  379. // InitApplication
  380. /////////////////////////////////////////////////////////////////////
  381. BOOL InitApplication(HINSTANCE hInstance)
  382. {
  383.         WNDCLASS  wc;
  384.  
  385.         // Fill in window class structure with parameters that
  386.         // describe the main window.
  387.         wc.style         = CS_HREDRAW | CS_VREDRAW;
  388.         wc.lpfnWndProc   = (WNDPROC)WndProc;
  389.         wc.cbClsExtra    = 0;
  390.         wc.cbWndExtra    = 0;
  391.         wc.hInstance     = hInstance;
  392.         wc.hIcon         = LoadIcon (hInstance, szAppName);
  393.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  394.         wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  395.         wc.lpszMenuName  = szAppName;
  396.         wc.lpszClassName = szAppName;
  397.  
  398.         // Register the window class and return success/failure code.
  399.         return (RegisterClass(&wc));
  400. }
  401.  
  402.  
  403. /////////////////////////////////////////////////////////////////////
  404. // Now that we know the video mode, make the window the right size
  405. // and create the DIB and set up the palette as needed.
  406. /////////////////////////////////////////////////////////////////////
  407. void SetUpToDraw(void)
  408. {
  409.     INT                i, j, k;
  410.     LOGPALETTE *    plogpal;
  411.     PUSHORT         pusTemp;
  412.     HPALETTE        hpal;
  413.     HDC             hdc;
  414.  
  415.     DIBWidth = GetSystemMetrics(SM_CXSCREEN);
  416.     DIBHeight = GetSystemMetrics(SM_CYSCREEN);
  417.     window_center_x = DIBWidth / 2;
  418.     window_center_y = DIBHeight / 2;
  419.  
  420.     SetWindowPos(hwndOutput,
  421.                 HWND_TOP,
  422.                 0,
  423.                 0,
  424.                 DIBWidth,
  425.                 DIBHeight,
  426.                 0);
  427.  
  428.     hdc = GetDC(hwndOutput);
  429.  
  430.     // For 256-color mode, set up the palette for maximum speed
  431.     // in copying to the screen. If not a 256-color mode, the
  432.     // adapter isn't palettized, so we'll just use the default
  433.     // palette. However, we will run a lot slower in this case
  434.     // due to translation while copying
  435.     if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
  436.         // This is a 256-color palettized mode.
  437.         // Set up and realize our palette and the identity color
  438.         // table for the DIB (identity means that DIB color
  439.         // indices and screen palette indices match exactly, so
  440.         // GDI doesn't have to do any translation when copying
  441.         // from DIB to screen. This helps performance a lot)
  442.         plogpal = malloc(sizeof(LOGPALETTE) +
  443.                 256 * sizeof(PALETTEENTRY));
  444.  
  445.         // Take up all physical palette entries, to flush out
  446.         // anything that's currently in the palette
  447.         plogpal->palVersion = 0x300;
  448.         plogpal->palNumEntries = 236;
  449.         for (i=0; i<236; i++) {
  450.             plogpal->palPalEntry[i].peRed = i;
  451.             plogpal->palPalEntry[i].peGreen = 0;
  452.             plogpal->palPalEntry[i].peBlue = 0;
  453.             plogpal->palPalEntry[i].peFlags =
  454.                     PC_RESERVED | PC_NOCOLLAPSE;
  455.         }
  456.  
  457.         hpal = CreatePalette(plogpal);
  458.         hpalold = SelectPalette(hdc, hpal, FALSE);
  459.         RealizePalette(hdc);
  460.         SelectPalette(hdc, hpalold, FALSE);
  461.         DeleteObject(hpal);
  462.  
  463.         // Now set up the 6value-6value-6value RGB palette,
  464.         // followed by 20 gray levels, that we want to work with
  465.         // into the physical palette
  466.         for (i=0; i<6; i++) {
  467.             for (j=0; j<6; j++) {
  468.                 for (k=0; k<6; k++) {
  469.                     plogpal->palPalEntry[i*36+j*6+k].peRed =
  470.                             i*255/6;
  471.                     plogpal->palPalEntry[i*36+j*6+k].peGreen =
  472.                             j*255/6;
  473.                     plogpal->palPalEntry[i*36+j*6+k].peBlue =
  474.                                k*255/6;
  475.                     plogpal->palPalEntry[i*36+j*6+k].peFlags =
  476.                         PC_RESERVED | PC_NOCOLLAPSE;
  477.                 }
  478.             }
  479.         }
  480.  
  481.         for (i=0; i<20; i++) {
  482.             plogpal->palPalEntry[i+216].peRed = i*255/20;
  483.             plogpal->palPalEntry[i+216].peGreen = i*255/20;
  484.             plogpal->palPalEntry[i+216].peBlue = i*255/20;
  485.             plogpal->palPalEntry[i+216].peFlags =
  486.                     PC_RESERVED | PC_NOCOLLAPSE;
  487.         }
  488.  
  489.         hpal = CreatePalette(plogpal);
  490.         SelectPalette(hdc, hpal, FALSE);
  491.         RealizePalette(hdc);
  492.  
  493.         // Get back the 256 colors now in the physical palette,
  494.         // which are the 236 we just selected, plus the 20 static
  495.         // colors
  496.         GetSystemPaletteEntries(hdc, 0, 256, plogpal->palPalEntry);
  497.  
  498.         for (i=10; i<246; i++) {
  499.             plogpal->palPalEntry[i].peFlags =
  500.                     PC_RESERVED | PC_NOCOLLAPSE;
  501.         }
  502.  
  503.         // Now create a logical palette that exactly matches the
  504.         // physical palette, and realize it. This is the palette
  505.         // into which the DIB pixel values will be indices
  506.         plogpal->palNumEntries = 256;
  507.  
  508.         hpalDIB = CreatePalette(plogpal);
  509.         SelectPalette(hdc, hpalDIB, FALSE);
  510.         DeleteObject(hpal);
  511.         RealizePalette(hdc);
  512.         SelectPalette(hdc, hpalold, FALSE);
  513.            free(plogpal);
  514.     }
  515.  
  516.     // Finally, set up the DIB section
  517.     pbmiDIB = malloc(sizeof(BITMAPINFO) - 4 + 256*sizeof(USHORT));
  518.     pbmiDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  519.     pbmiDIB->bmiHeader.biWidth = DIBWidth;
  520.     pbmiDIB->bmiHeader.biHeight = DIBHeight;
  521.     pbmiDIB->bmiHeader.biPlanes = 1;
  522.     pbmiDIB->bmiHeader.biBitCount = 8;
  523.     pbmiDIB->bmiHeader.biCompression = BI_RGB;
  524.     pbmiDIB->bmiHeader.biSizeImage = 0;
  525.     pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
  526.     pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
  527.     pbmiDIB->bmiHeader.biClrUsed = 256;
  528.     pbmiDIB->bmiHeader.biClrImportant = 256;
  529.  
  530.     pusTemp = (PUSHORT) pbmiDIB->bmiColors;
  531.  
  532.     for (i=0; i<256; i++) {
  533.         *pusTemp++ = i;
  534.     }
  535.  
  536.     hDIBSection = CreateDIBSection (hdc, pbmiDIB, DIB_PAL_COLORS,
  537.                       &pDIBBase, NULL, 0);
  538.  
  539.     if (pbmiDIB->bmiHeader.biHeight > 0)
  540.     {
  541.         pDIB = (pDIBBase + (DIBHeight - 1) * DIBWidth);
  542.         DIBPitch = -DIBWidth;   // bottom-up
  543.     }
  544.     else
  545.     {
  546.         pDIB = pDIBBase;
  547.         DIBPitch = DIBWidth;    // top-down
  548.     }
  549.  
  550.     // Clear the DIB
  551.     memset(pDIBBase, 0, DIBWidth*DIBHeight);
  552.  
  553.     ReleaseDC(hwndOutput, hdc);
  554.  
  555.     // Set the initial location, direction, and speed
  556.     roll = 0.0;
  557.     pitch = 0.0;
  558.     yaw = 0.0;
  559.     currentspeed = 0.0;
  560.     currentpos.v[0] = 0.0;
  561.     currentpos.v[1] = 0.0;
  562.     currentpos.v[2] = 0.0;
  563.     fieldofview = 2.0;
  564.     xscreenscale = DIBWidth / fieldofview;
  565.     yscreenscale = DIBHeight / fieldofview;
  566.     maxscale = max(xscreenscale, yscreenscale);
  567.     maxscreenscaleinv = 1.0 / maxscale;
  568.     xcenter = DIBWidth / 2.0 - 0.5;
  569.     ycenter = DIBHeight / 2.0 - 0.5;
  570.  
  571.     numobjects = sizeof(objects) / sizeof(objects[0]);
  572. }
  573.  
  574.  
  575. /////////////////////////////////////////////////////////////////////
  576. // InitInstance
  577. /////////////////////////////////////////////////////////////////////
  578. BOOL InitInstance(
  579.         HINSTANCE       hInstance,
  580.         int             nCmdShow)
  581. {
  582.         HWND            hwnd; // Main window handle.
  583.  
  584.         // Save the instance handle in static variable, which will be
  585.         // used in many subsequent calls from this application to
  586.         // Windows
  587.         hInst = hInstance; // Store inst handle in our global variable
  588.  
  589.         // Create a main window for this application instance
  590.         DIBWidth = GetSystemMetrics(SM_CXSCREEN);
  591.         DIBHeight = GetSystemMetrics(SM_CYSCREEN);
  592.         xcenter = DIBWidth / 2.0 - 0.5;
  593.         ycenter = DIBHeight / 2.0 - 0.5;
  594.    
  595.         hwnd = CreateWindow(
  596.                 szAppName,           // See RegisterClass() call.
  597.                 szTitle,             // Text for window title bar.
  598.                 WS_POPUP,            // Window style.
  599.                 0,
  600.                 0,
  601.                 DIBWidth,
  602.                 DIBHeight,
  603.                 NULL,
  604.                 NULL,
  605.                 hInstance,
  606.                 NULL
  607.         );
  608.  
  609.         // If window could not be created, return "failure"
  610.         if (!hwnd) {
  611.             return (FALSE);
  612.         }
  613.  
  614.         // Make the window visible and draw it
  615.         ShowWindow(hwnd, nCmdShow); // Show the window
  616.         UpdateWindow(hwnd);         // Sends WM_PAINT message
  617.  
  618.         hwndOutput = hwnd;
  619.  
  620.         return (TRUE);              // We succeeded...
  621. }
  622.  
  623. /////////////////////////////////////////////////////////////////////
  624. // WndProc
  625. /////////////////////////////////////////////////////////////////////
  626. LRESULT CALLBACK WndProc(
  627.                 HWND hwnd,         // window handle
  628.                 UINT message,      // type of message
  629.                 WPARAM uParam,     // additional information
  630.                 LPARAM lParam)     // additional information
  631. {
  632.     int wmId, wmEvent;
  633.     UINT fwSizeType;
  634.     int oldDIBWidth, oldDIBHeight;
  635.     HBITMAP holdDIBSection;
  636.     HDC hdc;
  637.  
  638.     switch (message) {
  639.  
  640.     case WM_COMMAND:  // message: command from application menu
  641.  
  642.         wmId    = LOWORD(uParam);
  643.         wmEvent = HIWORD(uParam);
  644.  
  645.         switch (wmId) {
  646.         case 1:
  647.             if (wmEvent == LBN_DBLCLK)
  648.             {
  649.                 curLBIndex = SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
  650.                 DestroyWindow(hwndLB);
  651.                 SetVMode(curLBIndex);
  652.                 SetUpToDraw();
  653.                 StartGameMouse();
  654.                 inLB = 0;
  655.             }
  656.             break;
  657.  
  658.         default:
  659.             return (DefWindowProc(hwnd, message, uParam, lParam));
  660.         }
  661.         break;
  662.  
  663.     case WM_KEYDOWN:
  664.         switch (uParam) {
  665.  
  666.          case VK_ESCAPE:
  667.             DestroyWindow (hwnd);
  668.             break;
  669.  
  670.         case VK_DOWN:
  671.             currentspeed -= MOVEMENT_SPEED * speedscale;
  672.             if (currentspeed < -(MAX_MOVEMENT_SPEED * speedscale))
  673.                 currentspeed = -(MAX_MOVEMENT_SPEED * speedscale);
  674.             break;
  675.  
  676.         case VK_UP:
  677.             currentspeed += MOVEMENT_SPEED * speedscale;
  678.             if (currentspeed > (MAX_MOVEMENT_SPEED * speedscale))
  679.                 currentspeed = (MAX_MOVEMENT_SPEED * speedscale);
  680.             break;
  681.  
  682.         case 'N':
  683.             roll += ROLL_SPEED * speedscale;
  684.             if (roll >= (PI * 2))
  685.                 roll -= PI * 2;
  686.             break;
  687.  
  688.         case 'M':
  689.             roll -= ROLL_SPEED * speedscale;
  690.             if (roll < 0)
  691.                 roll += PI * 2;
  692.             break;
  693.  
  694.         case 'A':
  695.             pitch -= PITCH_SPEED * speedscale;
  696.             if (pitch < 0)
  697.                 pitch += PI * 2;
  698.             break;
  699.  
  700.         case 'Z':
  701.             pitch += PITCH_SPEED * speedscale;
  702.             if (pitch >= (PI * 2))
  703.                 pitch -= PI * 2;
  704.             break;
  705.  
  706.         case 'D':
  707.             currentpos.v[1] += VMOVEMENT_SPEED;
  708.             break;
  709.  
  710.         case 'C':
  711.             currentpos.v[1] -= VMOVEMENT_SPEED;
  712.             break;
  713.  
  714.         case VK_LEFT:
  715.             yaw -= YAW_SPEED * speedscale;
  716.             if (yaw < 0)
  717.                 yaw += PI * 2;
  718.             break;
  719.  
  720.         case VK_RIGHT:
  721.             yaw += YAW_SPEED * speedscale;
  722.             if (yaw >= (PI * 2))
  723.                 yaw -= PI * 2;
  724.             break;
  725.  
  726.         default:
  727.             break;
  728.         }
  729.         return(0);
  730.  
  731.     case WM_KEYUP:
  732.         switch (uParam) {
  733.  
  734.         case VK_SUBTRACT:
  735.             fieldofview *= 0.9;
  736.             xscreenscale = DIBWidth / fieldofview;
  737.             yscreenscale = DIBHeight / fieldofview;
  738.             maxscale = max(xscreenscale, yscreenscale);
  739.             break;
  740.  
  741.         case VK_ADD:
  742.             fieldofview *= 1.1;
  743.             xscreenscale = DIBWidth / fieldofview;
  744.             yscreenscale = DIBHeight / fieldofview;
  745.             maxscale = max(xscreenscale, yscreenscale);
  746.             break;
  747.  
  748.         case 'F':
  749.             speedscale *= 1.1;
  750.             break;
  751.  
  752.         case 'S':
  753.             speedscale *= 0.9;
  754.             break;
  755.  
  756.         default:
  757.             break;
  758.         }
  759.         return(0);
  760.  
  761.     case WM_SIZE:    // window size changed
  762.         fwSizeType = uParam;
  763.         if (fwSizeType != SIZE_MINIMIZED) {
  764.             // Skip when this is called before the first DIB
  765.             // section is created
  766.             if (hDIBSection == 0)
  767.                 break;
  768.  
  769.             oldDIBWidth = DIBWidth;
  770.             oldDIBHeight = DIBHeight;
  771.  
  772.             DIBWidth = LOWORD(lParam);
  773.             DIBWidth = (DIBWidth + 3) & ~3;
  774.             DIBHeight = HIWORD(lParam);
  775.  
  776.             if ((DIBHeight < 10) || (DIBWidth < 10))
  777.             {
  778.             // Keep the DIB section big enough so we don't start
  779.             // drawing outside the DIB (the window can get smaller,
  780.             // but we don't really care, and GDI will clip the
  781.             // blts for us)
  782.                 DIBWidth = oldDIBWidth;
  783.                 DIBHeight = oldDIBHeight;
  784.             }
  785.  
  786.             // Resize the DIB section to the new size
  787.             holdDIBSection = hDIBSection;
  788.             pbmiDIB->bmiHeader.biWidth = DIBWidth;
  789.             pbmiDIB->bmiHeader.biHeight = DIBHeight;
  790.  
  791.             hdc = GetDC(hwnd);
  792.             hDIBSection = CreateDIBSection (hdc, pbmiDIB,
  793.                     DIB_PAL_COLORS, &pDIBBase, NULL, 0);
  794.  
  795.             if (hDIBSection) {
  796.                 // Success
  797.                 DeleteObject(holdDIBSection);
  798.  
  799.                 if (pbmiDIB->bmiHeader.biHeight > 0)
  800.                 {
  801.                     pDIB = (pDIBBase + (DIBHeight - 1) * DIBWidth);
  802.                     DIBPitch = -DIBWidth;   // bottom-up
  803.                 }
  804.                 else
  805.                 {
  806.                     pDIB = pDIBBase;
  807.                     DIBPitch = DIBWidth;    // top-down
  808.                 }
  809.  
  810.                 xscreenscale = DIBWidth / fieldofview;
  811.                 yscreenscale = DIBHeight / fieldofview;
  812.                 maxscale = max(xscreenscale, yscreenscale);
  813.                 xcenter = DIBWidth / 2.0 - 0.5;
  814.                 ycenter = DIBHeight / 2.0 - 0.5;
  815.             } else {
  816.                 // Failed, just use old size
  817.                 pbmiDIB->bmiHeader.biWidth = oldDIBWidth;
  818.                 pbmiDIB->bmiHeader.biHeight = oldDIBHeight;
  819.                 DIBWidth = oldDIBWidth;
  820.                 DIBHeight = oldDIBHeight;
  821.             }
  822.  
  823.             // Clear the DIB
  824.             memset(pDIBBase, 0, DIBWidth*DIBHeight);
  825.         }
  826.         break;
  827.  
  828.     case WM_DESTROY:  // message: window being destroyed
  829.         // put back the default mouse and video mode settings
  830.         StopGameMouse();
  831.         RestoreDefaultMode();
  832.  
  833.         free(pbmiDIB);
  834.         DeleteObject(hDIBSection);
  835.         DeleteObject(hpalold);
  836.                         
  837.         PostQuitMessage(0);
  838.         break;
  839.  
  840.     default:          // Passes it on if unproccessed
  841.         return (DefWindowProc(hwnd, message, uParam, lParam));
  842.     }
  843.     return (0);
  844. }
  845.  
  846. /////////////////////////////////////////////////////////////////////
  847. // 3-D dot product.
  848. /////////////////////////////////////////////////////////////////////
  849. double DotProduct(point_t *vec1, point_t *vec2)
  850. {
  851.     return vec1->v[0] * vec2->v[0] +
  852.            vec1->v[1] * vec2->v[1] +
  853.            vec1->v[2] * vec2->v[2];
  854. }
  855.  
  856. /////////////////////////////////////////////////////////////////////
  857. // 3-D cross product.
  858. /////////////////////////////////////////////////////////////////////
  859. void CrossProduct(point_t *in1, point_t *in2, point_t *out)
  860. {
  861.     out->v[0] = in1->v[1] * in2->v[2] - in1->v[2] * in2->v[1];
  862.     out->v[1] = in1->v[2] * in2->v[0] - in1->v[0] * in2->v[2];
  863.     out->v[2] = in1->v[0] * in2->v[1] - in1->v[1] * in2->v[0];
  864. }
  865.  
  866. /////////////////////////////////////////////////////////////////////
  867. // Concatenate two 3x3 matrices.
  868. /////////////////////////////////////////////////////////////////////
  869. void MConcat(double in1[3][3], double in2[3][3], double out[3][3])
  870. {
  871.     int     i, j;
  872.  
  873.     for (i=0 ; i<3 ; i++)
  874.     {
  875.         for (j=0 ; j<3 ; j++)
  876.         {
  877.             out[i][j] = in1[i][0] * in2[0][j] +
  878.                         in1[i][1] * in2[1][j] +
  879.                         in1[i][2] * in2[2][j];
  880.         }
  881.     }
  882. }
  883.  
  884. /////////////////////////////////////////////////////////////////////
  885. // Project viewspace polygon vertices into screen coordinates.
  886. // Note that the y axis goes up in worldspace and viewspace, but
  887. // goes down in screenspace.
  888. /////////////////////////////////////////////////////////////////////
  889. void ProjectPolygon (polygon_t *ppoly, polygon2D_t *ppoly2D)
  890. {
  891.     int     i;
  892.     double  zrecip;
  893.  
  894.     for (i=0 ; i<ppoly->numverts ; i++)
  895.     {
  896.         zrecip = 1.0 / ppoly->verts[i].v[2];
  897.         ppoly2D->verts[i].x =
  898.                 ppoly->verts[i].v[0] * zrecip * maxscale + xcenter;
  899.         ppoly2D->verts[i].y = ycenter -
  900.                 (ppoly->verts[i].v[1] * zrecip * maxscale);
  901.     }
  902.  
  903.     ppoly2D->numverts = ppoly->numverts;
  904. }
  905.  
  906. /////////////////////////////////////////////////////////////////////
  907. // Move the view position and set the world->view transform.
  908. /////////////////////////////////////////////////////////////////////
  909. void UpdateViewPos()
  910. {
  911.     int     i;
  912.     point_t motionvec;
  913.     double  s, c, mtemp1[3][3], mtemp2[3][3];
  914.  
  915.     // Move in the view direction, across the x-y plane, as if
  916.     // walking. This approach moves slower when looking up or
  917.     // down at more of an angle
  918.     motionvec.v[0] = DotProduct(&vpn, &xaxis);
  919.     motionvec.v[1] = 0.0;
  920.     motionvec.v[2] = DotProduct(&vpn, &zaxis);
  921.  
  922.     for (i=0 ; i<3 ; i++)
  923.     {
  924.         currentpos.v[i] += motionvec.v[i] * currentspeed;
  925.         if (currentpos.v[i] > MAX_COORD)
  926.             currentpos.v[i] = MAX_COORD;
  927.         if (currentpos.v[i] < -MAX_COORD)
  928.             currentpos.v[i] = -MAX_COORD;
  929.     }
  930.  
  931.     // Set up the world-to-view rotation.
  932.     // Note: much of the work done in concatenating these matrices
  933.     // can be factored out, since it contributes nothing to the
  934.     // final result; multiply the three matrices together on paper
  935.     // to generate a minimum equation for each of the 9 final elements
  936.     s = sin(roll);
  937.     c = cos(roll);
  938.     mroll[0][0] = c;
  939.     mroll[0][1] = s;
  940.     mroll[1][0] = -s;
  941.     mroll[1][1] = c;
  942.  
  943.     s = sin(pitch);
  944.     c = cos(pitch);
  945.     mpitch[1][1] = c;
  946.     mpitch[1][2] = s;
  947.     mpitch[2][1] = -s;
  948.     mpitch[2][2] = c;
  949.  
  950.     s = sin(yaw);
  951.     c = cos(yaw);
  952.     myaw[0][0] = c;
  953.     myaw[0][2] = -s;
  954.     myaw[2][0] = s;
  955.     myaw[2][2] = c;
  956.  
  957.     MConcat(mroll, myaw, mtemp1);
  958.     MConcat(mpitch, mtemp1, mtemp2);
  959.  
  960.     // Break out the rotation matrix into vright, vup, and vpn.
  961.     // We could work directly with the matrix; breaking it out
  962.     // into three vectors is just to make things clearer
  963.     for (i=0 ; i<3 ; i++)
  964.     {
  965.         vright.v[i] = mtemp2[0][i];
  966.         vup.v[i] = mtemp2[1][i];
  967.         vpn.v[i] = mtemp2[2][i];
  968.     }
  969.  
  970.     // Simulate crude friction
  971.     if (currentspeed > (MOVEMENT_SPEED * speedscale / 2.0))
  972.         currentspeed -= MOVEMENT_SPEED * speedscale / 2.0;
  973.     else if (currentspeed < -(MOVEMENT_SPEED * speedscale / 2.0))
  974.         currentspeed += MOVEMENT_SPEED * speedscale / 2.0;
  975.     else
  976.         currentspeed = 0.0;
  977. }
  978.  
  979. /////////////////////////////////////////////////////////////////////
  980. // Rotate a vector from viewspace to worldspace.
  981. /////////////////////////////////////////////////////////////////////
  982. void BackRotateVector(point_t *pin, point_t *pout)
  983. {
  984.     int     i;
  985.  
  986.     // Rotate into the world orientation
  987.     for (i=0 ; i<3 ; i++)
  988.     {
  989.         pout->v[i] = pin->v[0] * vright.v[i] +
  990.                      pin->v[1] * vup.v[i] +
  991.                      pin->v[2] * vpn.v[i];
  992.     }
  993. }
  994.  
  995. /////////////////////////////////////////////////////////////////////
  996. // Transform a point from worldspace to viewspace.
  997. /////////////////////////////////////////////////////////////////////
  998. void TransformPoint(point_t *pin, point_t *pout)
  999. {
  1000.     int     i;
  1001.     point_t tvert;
  1002.  
  1003.     // Translate into a viewpoint-relative coordinate
  1004.     for (i=0 ; i<3 ; i++)
  1005.     {
  1006.         tvert.v[i] = pin->v[i] - currentpos.v[i];
  1007.     }
  1008.  
  1009.     // Rotate into the view orientation
  1010.     pout->v[0] = DotProduct(&tvert, &vright);
  1011.     pout->v[1] = DotProduct(&tvert, &vup);
  1012.     pout->v[2] = DotProduct(&tvert, &vpn);
  1013. }
  1014.  
  1015. /////////////////////////////////////////////////////////////////////
  1016. // Transform a polygon from worldspace to viewspace.
  1017. /////////////////////////////////////////////////////////////////////
  1018. void TransformPolygon(polygon_t *pinpoly, polygon_t *poutpoly)
  1019. {
  1020.     int     i;
  1021.  
  1022.     for (i=0 ; i<pinpoly->numverts ; i++)
  1023.     {
  1024.         TransformPoint(&pinpoly->verts[i], &poutpoly->verts[i]);
  1025.     }
  1026.  
  1027.     poutpoly->numverts = pinpoly->numverts;
  1028. }
  1029.  
  1030. /////////////////////////////////////////////////////////////////////
  1031. // Returns true if polygon faces the viewpoint, assuming a clockwise
  1032. // winding of vertices as seen from the front.
  1033. /////////////////////////////////////////////////////////////////////
  1034. int PolyFacesViewer(polygon_t *ppoly, plane_t *pplane)
  1035. {
  1036.     int     i;
  1037.     point_t viewvec;
  1038.  
  1039.     for (i=0 ; i<3 ; i++)
  1040.     {
  1041.         viewvec.v[i] = ppoly->verts[0].v[i] - currentpos.v[i];
  1042.     }
  1043.  
  1044.     // Use an epsilon here so we don't get polygons tilted so
  1045.     // sharply that the gradients are unusable or invalid
  1046.     if (DotProduct (&viewvec, &pplane->normal) < -0.01)
  1047.         return 1;
  1048.     else
  1049.         return 0;
  1050. }
  1051.  
  1052. /////////////////////////////////////////////////////////////////////
  1053. // Set up a clip plane with the specified normal.
  1054. /////////////////////////////////////////////////////////////////////
  1055. void SetWorldspaceClipPlane(point_t *normal, plane_t *plane)
  1056. {
  1057.  
  1058.     // Rotate the plane normal into worldspace
  1059.     BackRotateVector(normal, &plane->normal);
  1060.  
  1061.     plane->distance = DotProduct(¤tpos, &plane->normal) +
  1062.             CLIP_PLANE_EPSILON;
  1063. }
  1064.  
  1065. /////////////////////////////////////////////////////////////////////
  1066. // Set up the planes of the frustum, in worldspace coordinates.
  1067. /////////////////////////////////////////////////////////////////////
  1068. void SetUpFrustum(void)
  1069. {
  1070.     double  angle, s, c;
  1071.     point_t normal;
  1072.  
  1073.     angle = atan(2.0 / fieldofview * maxscale / xscreenscale);
  1074.     s = sin(angle);
  1075.     c = cos(angle);
  1076.  
  1077.     // Left clip plane
  1078.     normal.v[0] = s;
  1079.     normal.v[1] = 0;
  1080.     normal.v[2] = c;
  1081.     SetWorldspaceClipPlane(&normal, &frustumplanes[0]);
  1082.  
  1083.     // Right clip plane
  1084.     normal.v[0] = -s;
  1085.     SetWorldspaceClipPlane(&normal, &frustumplanes[1]);
  1086.  
  1087.     angle = atan(2.0 / fieldofview * maxscale / yscreenscale);
  1088.     s = sin(angle);
  1089.     c = cos(angle);
  1090.  
  1091.     // Bottom clip plane
  1092.     normal.v[0] = 0;
  1093.     normal.v[1] = s;
  1094.     normal.v[2] = c;
  1095.     SetWorldspaceClipPlane(&normal, &frustumplanes[2]);
  1096.  
  1097.     // Top clip plane
  1098.     normal.v[1] = -s;
  1099.     SetWorldspaceClipPlane(&normal, &frustumplanes[3]);
  1100. }
  1101.  
  1102. /////////////////////////////////////////////////////////////////////
  1103. // Clip a polygon to a plane.
  1104. /////////////////////////////////////////////////////////////////////
  1105. int ClipToPlane(polygon_t *pin, plane_t *pplane, polygon_t *pout)
  1106. {
  1107.     int     i, j, nextvert, curin, nextin;
  1108.     double  curdot, nextdot, scale;
  1109.     point_t *pinvert, *poutvert;
  1110.  
  1111.     pinvert = pin->verts;
  1112.     poutvert = pout->verts;
  1113.  
  1114.     curdot = DotProduct(pinvert, &pplane->normal);
  1115.     curin = (curdot >= pplane->distance);
  1116.  
  1117.     for (i=0 ; i<pin->numverts ; i++)
  1118.     {
  1119.         nextvert = (i + 1) % pin->numverts;
  1120.  
  1121.         // Keep the current vertex if it's inside the plane
  1122.         if (curin)
  1123.             *poutvert++ = *pinvert;
  1124.  
  1125.         nextdot = DotProduct(&pin->verts[nextvert], &pplane->normal);
  1126.         nextin = (nextdot >= pplane->distance);
  1127.  
  1128.         // Add a clipped vertex if one end of the current edge is
  1129.         // inside the plane and the other is outside
  1130.         if (curin != nextin)
  1131.         {
  1132.             scale = (pplane->distance - curdot) /
  1133.                     (nextdot - curdot);
  1134.             for (j=0 ; j<3 ; j++)
  1135.             {
  1136.                 poutvert->v[j] = pinvert->v[j] +
  1137.                     ((pin->verts[nextvert].v[j] - pinvert->v[j]) *
  1138.                      scale);
  1139.             }
  1140.             poutvert++;
  1141.         }
  1142.  
  1143.         curdot = nextdot;
  1144.         curin = nextin;
  1145.         pinvert++;
  1146.     }
  1147.  
  1148.     pout->numverts = poutvert - pout->verts;
  1149.     if (pout->numverts < 3)
  1150.         return 0;
  1151.  
  1152.     return 1;
  1153. }
  1154.  
  1155. /////////////////////////////////////////////////////////////////////
  1156. // Clip a polygon to the frustum.
  1157. /////////////////////////////////////////////////////////////////////
  1158. int ClipToFrustum(polygon_t *pin, polygon_t *pout)
  1159. {
  1160.     int         i, curpoly;
  1161.     polygon_t   tpoly[2], *ppoly;
  1162.  
  1163.     curpoly = 0;
  1164.     ppoly = pin;
  1165.  
  1166.     for (i=0 ; i<(NUM_FRUSTUM_PLANES-1); i++)
  1167.     {
  1168.         if (!ClipToPlane(ppoly,
  1169.                          &frustumplanes[i],
  1170.                          &tpoly[curpoly]))
  1171.         {
  1172.             return 0;
  1173.         }
  1174.         ppoly = &tpoly[curpoly];
  1175.         curpoly ^= 1;
  1176.     }
  1177.  
  1178.     return ClipToPlane(ppoly,
  1179.                        &frustumplanes[NUM_FRUSTUM_PLANES-1],
  1180.                        pout);
  1181. }
  1182.  
  1183. /////////////////////////////////////////////////////////////////////
  1184. // Add the polygon's edges to the global edge table.
  1185. /////////////////////////////////////////////////////////////////////
  1186. void AddPolygonEdges (plane_t *plane, polygon2D_t *screenpoly)
  1187. {
  1188.     double  distinv, deltax, deltay, slope;
  1189.     int     i, nextvert, numverts, temp, topy, bottomy, height;
  1190.     edge_t  *pedge;
  1191.  
  1192.     numverts = screenpoly->numverts;
  1193.  
  1194.     // Clamp the polygon's vertices just in case some very near
  1195.     // points have wandered out of range due to floating-point
  1196.     // imprecision
  1197.     for (i=0 ; i<numverts ; i++)
  1198.     {
  1199.         if (screenpoly->verts[i].x < -0.5)
  1200.             screenpoly->verts[i].x = -0.5;
  1201.         if (screenpoly->verts[i].x > ((double)DIBWidth - 0.5))
  1202.             screenpoly->verts[i].x = (double)DIBWidth - 0.5;
  1203.         if (screenpoly->verts[i].y < -0.5)
  1204.             screenpoly->verts[i].y = -0.5;
  1205.         if (screenpoly->verts[i].y > ((double)DIBHeight - 0.5))
  1206.             screenpoly->verts[i].y = (double)DIBHeight - 0.5;
  1207.                 }
  1208.  
  1209.     // Add each edge in turn
  1210.     for (i=0 ; i<numverts ; i++)
  1211.     {
  1212.         nextvert = i + 1;
  1213.         if (nextvert >= numverts)
  1214.             nextvert = 0;
  1215.  
  1216.         topy = (int)ceil(screenpoly->verts[i].y);
  1217.         bottomy = (int)ceil(screenpoly->verts[nextvert].y);
  1218.         height = bottomy - topy;
  1219.         if (height == 0)
  1220.             continue;       // doesn't cross any scan lines
  1221.         if (height < 0)
  1222.         {
  1223.             // Leading edge
  1224.             temp = topy;
  1225.             topy = bottomy;
  1226.             bottomy = temp;
  1227.  
  1228.             pavailedge->leading = 1;
  1229.  
  1230.             deltax = screenpoly->verts[i].x -
  1231.                      screenpoly->verts[nextvert].x;
  1232.             deltay = screenpoly->verts[i].y -
  1233.                      screenpoly->verts[nextvert].y;
  1234.             slope = deltax / deltay;
  1235.  
  1236.             // Edge coordinates are in 16.16 fixed point
  1237.             pavailedge->xstep = (int)(slope * (float)0x10000);
  1238.             pavailedge->x = (int)((screenpoly->verts[nextvert].x +
  1239.                 ((float)topy - screenpoly->verts[nextvert].y) *
  1240.                 slope) * (float)0x10000);
  1241.         }
  1242.         else
  1243.         {
  1244.             // Trailing edge
  1245.             pavailedge->leading = 0;
  1246.  
  1247.             deltax = screenpoly->verts[nextvert].x -
  1248.                      screenpoly->verts[i].x;
  1249.             deltay = screenpoly->verts[nextvert].y -
  1250.                      screenpoly->verts[i].y;
  1251.             slope = deltax / deltay;
  1252.  
  1253.             // Edge coordinates are in 16.16 fixed point
  1254.             pavailedge->xstep = (int)(slope * (float)0x10000);
  1255.             pavailedge->x = (int)((screenpoly->verts[i].x +
  1256.                 ((float)topy - screenpoly->verts[i].y) * slope) *
  1257.                 (float)0x10000);
  1258.         }
  1259.  
  1260.         // Put the edge on the list to be added on top scan
  1261.         pedge = &newedges[topy];
  1262.         while (pedge->pnext->x < pavailedge->x)
  1263.             pedge = pedge->pnext;
  1264.         pavailedge->pnext = pedge->pnext;
  1265.         pedge->pnext = pavailedge;
  1266.  
  1267.         // Put the edge on the list to be removed after final scan
  1268.         pavailedge->pnextremove = removeedges[bottomy - 1];
  1269.         removeedges[bottomy - 1] = pavailedge;
  1270.  
  1271.         // Associate the edge with the surface we'll create for
  1272.         // this polygon
  1273.         pavailedge->psurf = pavailsurf;
  1274.  
  1275.         // Make sure we don't overflow the edge array
  1276.         if (pavailedge < &edges[MAX_EDGES])
  1277.             pavailedge++;
  1278.     }
  1279.  
  1280.     // Create the surface, so we'll know how to sort and draw from
  1281.     // the edges
  1282.     pavailsurf->state = 0;
  1283.     pavailsurf->color = currentcolor;
  1284.  
  1285.     // Set up the 1/z gradients from the polygon, calculating the
  1286.     // base value at screen coordinate 0,0 so we can use screen
  1287.     // coordinates directly when calculating 1/z from the gradients
  1288.     distinv = 1.0 / plane->distance;
  1289.     pavailsurf->zinvstepx = plane->normal.v[0] * distinv *
  1290.             maxscreenscaleinv * (fieldofview / 2.0);
  1291.     pavailsurf->zinvstepy = -plane->normal.v[1] * distinv *
  1292.             maxscreenscaleinv * (fieldofview / 2.0);
  1293.     pavailsurf->zinv00 = plane->normal.v[2] * distinv -
  1294.             xcenter * pavailsurf->zinvstepx -
  1295.             ycenter * pavailsurf->zinvstepy;
  1296.  
  1297.     // Make sure we don't overflow the surface array
  1298.     if (pavailsurf < &surfs[MAX_SURFS])
  1299.         pavailsurf++;
  1300. }
  1301.  
  1302. /////////////////////////////////////////////////////////////////////
  1303. // Scan all the edges in the global edge table into spans.
  1304. /////////////////////////////////////////////////////////////////////
  1305. void ScanEdges (void)
  1306. {
  1307.     int     x, y;
  1308.     double  fx, fy, zinv, zinv2;
  1309.     edge_t  *pedge, *pedge2, *ptemp;
  1310.     span_t  *pspan;
  1311.     surf_t  *psurf, *psurf2;
  1312.  
  1313.     pspan = spans;
  1314.  
  1315.     // Set up the active edge list as initially empty, containing
  1316.     // only the sentinels (which are also the background fill). Most
  1317.     // of these fields could be set up just once at start-up
  1318.     edgehead.pnext = &edgetail;
  1319.     edgehead.pprev = NULL;
  1320.     edgehead.x = -0xFFFF;           // left edge of screen
  1321.     edgehead.leading = 1;
  1322.     edgehead.psurf = &surfstack;
  1323.  
  1324.     edgetail.pnext = NULL;          // mark edge of list
  1325.     edgetail.pprev = &edgehead;
  1326.     edgetail.x = DIBWidth << 16;    // right edge of screen
  1327.     edgetail.leading = 0;
  1328.     edgetail.psurf = &surfstack;
  1329.  
  1330.     // The background surface is the entire stack initially, and
  1331.     // is infinitely far away, so everything sorts in front of it.
  1332.     // This could be set just once at start-up
  1333.     surfstack.pnext = surfstack.pprev = &surfstack;
  1334.     surfstack.color = 0;
  1335.     surfstack.zinv00 = -999999.0;
  1336.     surfstack.zinvstepx = surfstack.zinvstepy = 0.0;
  1337.  
  1338.     for (y=0 ; y<DIBHeight ; y++)
  1339.     {
  1340.         fy = (double)y;
  1341.  
  1342.         // Sort in any edges that start on this scan
  1343.         pedge = newedges[y].pnext;
  1344.         pedge2 = &edgehead;
  1345.         while (pedge != &maxedge)
  1346.         {
  1347.             while (pedge->x > pedge2->pnext->x)
  1348.                 pedge2 = pedge2->pnext;
  1349.  
  1350.             ptemp = pedge->pnext;
  1351.             pedge->pnext = pedge2->pnext;
  1352.             pedge->pprev = pedge2;
  1353.             pedge2->pnext->pprev = pedge;
  1354.             pedge2->pnext = pedge;
  1355.  
  1356.             pedge2 = pedge;
  1357.             pedge = ptemp;
  1358.         }
  1359.  
  1360.         // Scan out the active edges into spans
  1361.  
  1362.         // Start out with the left background edge already inserted,
  1363.         // and the surface stack containing only the background
  1364.         surfstack.state = 1;
  1365.         surfstack.visxstart = 0;
  1366.  
  1367.         for (pedge=edgehead.pnext ; pedge ; pedge=pedge->pnext)
  1368.         {
  1369.             psurf = pedge->psurf;
  1370.  
  1371.             if (pedge->leading)
  1372.             {
  1373.                 // It's a leading edge. Figure out where it is
  1374.                 // relative to the current surfaces and insert in
  1375.                 // the surface stack; if it's on top, emit the span
  1376.                 // for the current top.
  1377.                 // First, make sure the edges don't cross
  1378.                 if (++psurf->state == 1)
  1379.                 {
  1380.                     fx = (double)pedge->x * (1.0 / (double)0x10000);
  1381.                     // Calculate the surface's 1/z value at this pixel
  1382.                     zinv = psurf->zinv00 + psurf->zinvstepx * fx +
  1383.                             psurf->zinvstepy * fy;
  1384.  
  1385.                     // See if that makes it a new top surface
  1386.                     psurf2 = surfstack.pnext;
  1387.                     zinv2 = psurf2->zinv00 + psurf2->zinvstepx * fx +
  1388.                             psurf2->zinvstepy * fy;
  1389.                     if (zinv >= zinv2)
  1390.                     {
  1391.                         // It's a new top surface
  1392.                         // emit the span for the current top
  1393.                         x = (pedge->x + 0xFFFF) >> 16;
  1394.                         pspan->count = x - psurf2->visxstart;
  1395.                         if (pspan->count > 0)
  1396.                         {
  1397.                             pspan->y = y;
  1398.                             pspan->x = psurf2->visxstart;
  1399.                             pspan->color = psurf2->color;
  1400.  
  1401.                             // Make sure we don't overflow
  1402.                             // the span array
  1403.                             if (pspan < &spans[MAX_SPANS])
  1404.                                 pspan++;
  1405.                         }
  1406.  
  1407.                         psurf->visxstart = x;
  1408.  
  1409.                         // Add the edge to the stack
  1410.                         psurf->pnext = psurf2;
  1411.                         psurf2->pprev = psurf;
  1412.                         surfstack.pnext = psurf;
  1413.                         psurf->pprev = &surfstack;
  1414.                     }
  1415.                     else
  1416.                     {
  1417.                         // Not a new top; sort into the surface stack.
  1418.                         // Guaranteed to terminate due to sentinel
  1419.                         // background surface
  1420.                         do
  1421.                         {
  1422.                             psurf2 = psurf2->pnext;
  1423.                             zinv2 = psurf2->zinv00 +
  1424.                                     psurf2->zinvstepx * fx +
  1425.                                     psurf2->zinvstepy * fy;
  1426.                         } while (zinv < zinv2);
  1427.  
  1428.                         // Insert the surface into the stack
  1429.                         psurf->pnext = psurf2;
  1430.                         psurf->pprev = psurf2->pprev;
  1431.                         psurf2->pprev->pnext = psurf;
  1432.                         psurf2->pprev = psurf;
  1433.                     }
  1434.                 }
  1435.             }
  1436.             else
  1437.             {
  1438.                 // It's a trailing edge; if this was the top surface,
  1439.                 // emit the span and remove it.
  1440.                 // First, make sure the edges didn't cross
  1441.                 if (--psurf->state == 0)
  1442.                 {
  1443.                     if (surfstack.pnext == psurf)
  1444.                     {
  1445.                         // It's on top, emit the span
  1446.                         x = ((pedge->x + 0xFFFF) >> 16);
  1447.                         pspan->count = x - psurf->visxstart;
  1448.                         if (pspan->count > 0)
  1449.                         {
  1450.                             pspan->y = y;
  1451.                             pspan->x = psurf->visxstart;
  1452.                             pspan->color = psurf->color;
  1453.  
  1454.                             // Make sure we don't overflow
  1455.                             // the span array
  1456.                             if (pspan < &spans[MAX_SPANS])
  1457.                                 pspan++;
  1458.                         }
  1459.  
  1460.                         psurf->pnext->visxstart = x;
  1461.                     }
  1462.  
  1463.                     // Remove the surface from the stack
  1464.                     psurf->pnext->pprev = psurf->pprev;
  1465.                     psurf->pprev->pnext = psurf->pnext;
  1466.                 }
  1467.             }
  1468.         }
  1469.  
  1470.         // Remove edges that are done
  1471.         pedge = removeedges[y];
  1472.         while (pedge)
  1473.         {
  1474.             pedge->pprev->pnext = pedge->pnext;
  1475.             pedge->pnext->pprev = pedge->pprev;
  1476.             pedge = pedge->pnextremove;
  1477.         }
  1478.  
  1479.         // Step the remaining edges one scan line, and re-sort
  1480.         for (pedge=edgehead.pnext ; pedge != &edgetail ; )
  1481.         {
  1482.             ptemp = pedge->pnext;
  1483.  
  1484.             // Step the edge
  1485.             pedge->x += pedge->xstep;
  1486.  
  1487.             // Move the edge back to the proper sorted location,
  1488.             // if necessary
  1489.             while (pedge->x < pedge->pprev->x)
  1490.             {
  1491.                 pedge2 = pedge->pprev;
  1492.                 pedge2->pnext = pedge->pnext;
  1493.                 pedge->pnext->pprev = pedge2;
  1494.                 pedge2->pprev->pnext = pedge;
  1495.                 pedge->pprev = pedge2->pprev;
  1496.                 pedge->pnext = pedge2;
  1497.                 pedge2->pprev = pedge;
  1498.             }
  1499.  
  1500.             pedge = ptemp;
  1501.         }
  1502.     }
  1503.  
  1504.     pspan->x = -1;  // mark the end of the list
  1505. }
  1506.  
  1507. /////////////////////////////////////////////////////////////////////
  1508. // Draw all the spans that were scanned out.
  1509. /////////////////////////////////////////////////////////////////////
  1510. void DrawSpans (void)
  1511. {
  1512.     span_t  *pspan;
  1513.  
  1514.     for (pspan=spans ; pspan->x != -1 ; pspan++)
  1515.     {
  1516.         memset (pDIB + (DIBPitch * pspan->y) + pspan->x,
  1517.                 pspan->color,
  1518.                 pspan->count);
  1519.     }
  1520. }
  1521.  
  1522. /////////////////////////////////////////////////////////////////////
  1523. // Clear the lists of edges to add and remove on each scan line.
  1524. /////////////////////////////////////////////////////////////////////
  1525. void ClearEdgeLists(void)
  1526. {
  1527.     int i;
  1528.  
  1529.     for (i=0 ; i<DIBHeight ; i++)
  1530.     {
  1531.         newedges[i].pnext = &maxedge;
  1532.         removeedges[i] = NULL;
  1533.     }
  1534. }
  1535.  
  1536. /////////////////////////////////////////////////////////////////////
  1537. // Render the current state of the world to the screen.
  1538. /////////////////////////////////////////////////////////////////////
  1539. void UpdateWorld()
  1540. {
  1541.     HPALETTE        holdpal;
  1542.     HDC             hdcScreen, hdcDIBSection;
  1543.     HBITMAP         holdbitmap;
  1544.     polygon2D_t     screenpoly;
  1545.     polygon_t       *ppoly, tpoly0, tpoly1, tpoly2;
  1546.     convexobject_t  *pobject;
  1547.     int             i, j, k;
  1548.     plane_t         plane;
  1549.     point_t         tnormal;
  1550.  
  1551.     UpdateViewPos();
  1552.     SetUpFrustum();
  1553.     ClearEdgeLists();
  1554.     pavailsurf = surfs;
  1555.     pavailedge = edges;
  1556.  
  1557.  
  1558.     // Draw all visible faces in all objects
  1559.     pobject = objecthead.pnext;
  1560.  
  1561.     // Accumulate the latest mouse movement and recenter the mouse before
  1562.     // building the edge list. In a real game, this would need to be done
  1563.     // about 50 times a second total, in as close to equal intervals as
  1564.     // possible
  1565.     AccumulateGameMouseMove();
  1566.  
  1567.     while (pobject != &objecthead)
  1568.     {
  1569.         ppoly = pobject->ppoly;
  1570.  
  1571.         for (i=0 ; i<pobject->numpolys ; i++)
  1572.         {
  1573.             // Move the polygon relative to the object center
  1574.             tpoly0.numverts = ppoly[i].numverts;
  1575.             for (j=0 ; j<tpoly0.numverts ; j++)
  1576.             {
  1577.                 for (k=0 ; k<3 ; k++)
  1578.                     tpoly0.verts[j].v[k] = ppoly[i].verts[j].v[k] +
  1579.                             pobject->center.v[k];
  1580.             }
  1581.  
  1582.             if (PolyFacesViewer(&tpoly0, &ppoly[i].plane))
  1583.             {
  1584.                 if (ClipToFrustum(&tpoly0, &tpoly1))
  1585.                 {
  1586.                     currentcolor = ppoly[i].color;
  1587.                     TransformPolygon (&tpoly1, &tpoly2);
  1588.                     ProjectPolygon (&tpoly2, &screenpoly);
  1589.  
  1590.                     // Move the polygon's plane into viewspace
  1591.                     // First move it into worldspace (object relative)
  1592.                     tnormal = ppoly[i].plane.normal;
  1593.                     plane.distance = ppoly[i].plane.distance +
  1594.                         DotProduct (&pobject->center, &tnormal);
  1595.                     // Now transform it into viewspace
  1596.                     // Determine the distance from the viewpont
  1597.                     plane.distance -=
  1598.                           DotProduct (¤tpos, &tnormal);
  1599.                     // Rotate the normal into view orientation
  1600.                     plane.normal.v[0] =
  1601.                             DotProduct (&tnormal, &vright);
  1602.                     plane.normal.v[1] =
  1603.                             DotProduct (&tnormal, &vup);
  1604.                     plane.normal.v[2] =
  1605.                             DotProduct (&tnormal, &vpn);
  1606.  
  1607.                     AddPolygonEdges (&plane, &screenpoly);
  1608.                 }
  1609.             }
  1610.         }
  1611.  
  1612.         pobject = pobject->pnext;
  1613.     }
  1614.  
  1615.     // Accumulate the latest mouse movement and recenter the mouse before
  1616.     // scanning the edges. In a real game, this would need to be done
  1617.     // about 50 times a second total, in as close to equal intervals as
  1618.     // possible
  1619.     AccumulateGameMouseMove();
  1620.  
  1621.     ScanEdges ();
  1622.  
  1623.     // Accumulate the latest mouse movement and recenter the mouse before
  1624.     // drawing the spans. In a real game, this would need to be done about
  1625.     // 50 times a second total, in as close to equal intervals as possible
  1626.     AccumulateGameMouseMove();
  1627.  
  1628.     DrawSpans ();
  1629.  
  1630.     // Accumulate the latest mouse movement and recenter the mouse before
  1631.     // bltting to the screen. In a real game, this would need to be done
  1632.     // about 50 times a second total, in as close to equal intervals as
  1633.     // possible
  1634.     AccumulateGameMouseMove();
  1635.  
  1636.     // We've drawn the frame; copy it to the screen
  1637.     hdcScreen = GetDC(hwndOutput);
  1638.     holdpal = SelectPalette(hdcScreen, hpalDIB, FALSE);
  1639.     RealizePalette(hdcScreen);
  1640.  
  1641.     hdcDIBSection = CreateCompatibleDC(hdcScreen);
  1642.     holdbitmap = SelectObject(hdcDIBSection, hDIBSection);
  1643.  
  1644.     BitBlt(hdcScreen, 0, 0, DIBWidth, DIBHeight, hdcDIBSection,
  1645.            0, 0, SRCCOPY);
  1646.  
  1647.     SelectPalette(hdcScreen, holdpal, FALSE);
  1648.     ReleaseDC(hwndOutput, hdcScreen);
  1649.     SelectObject(hdcDIBSection, holdbitmap);
  1650.     DeleteDC(hdcDIBSection);
  1651. }
  1652.  
  1653.  
  1654. /////////////////////////////////////////////////////////////////////
  1655. // Do mouse move.
  1656. /////////////////////////////////////////////////////////////////////
  1657. void DoMouseMove(void)
  1658. {
  1659.     int mouse_x_move, mouse_y_move;
  1660.  
  1661.     GetGameMouseMoveForFrame (&mouse_x_move, &mouse_y_move);
  1662.  
  1663.     yaw += YAW_SPEED * mousespeedscale * mouse_x_move;
  1664.  
  1665.     if (yaw < 0)
  1666.         yaw += PI * 2;
  1667.     else if (yaw >= (PI * 2))
  1668.         yaw -= PI * 2;
  1669.  
  1670.     pitch += PITCH_SPEED * mousespeedscale * mouse_y_move;
  1671.  
  1672.     if (pitch < 0)
  1673.         pitch += PI * 2;
  1674.     else if (pitch >= (PI * 2))
  1675.         pitch -= PI * 2;
  1676. }
  1677.  
  1678.  
  1679. /////////////////////////////////////////////////////////////////////
  1680. // functions to start up and shutdown the game configuration for
  1681. // the mouse, and to accumulate mouse moves over multiple calls
  1682. // during a frame and to calculate the total move for the frame
  1683. /////////////////////////////////////////////////////////////////////
  1684.  
  1685. // the two 0 values disable the two acceleration thresholds, and the
  1686. // 1 value specifies medium mouse speed
  1687. static int     originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
  1688. static int     mouse_x_accum, mouse_y_accum;
  1689. static POINT   current_pos;
  1690.  
  1691. int StartGameMouse (void)
  1692. {
  1693.    if (!SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0))
  1694.       return 0;
  1695.  
  1696.    if (!SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0))
  1697.       return 0;
  1698.  
  1699.    ShowCursor (FALSE);
  1700.    SetCursorPos (window_center_x, window_center_y);
  1701.    return 1;
  1702. }
  1703.  
  1704. void StopGameMouse (void)
  1705. {
  1706.    SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
  1707.    ShowCursor (TRUE);
  1708. }
  1709.  
  1710. void AccumulateGameMouseMove (void)
  1711. {
  1712.    GetCursorPos (¤t_pos);
  1713.  
  1714.    mouse_x_accum += current_pos.x - window_center_x;
  1715.    mouse_y_accum += current_pos.y - window_center_y;
  1716.  
  1717.    // force the mouse to the center, so there's room to move
  1718.    SetCursorPos (window_center_x, window_center_y);
  1719. }
  1720.  
  1721. void GetGameMouseMoveForFrame (int * mouse_x_move, int * mouse_y_move)
  1722. {
  1723.    GetCursorPos (¤t_pos);
  1724.    *mouse_x_move = current_pos.x - window_center_x + mouse_x_accum;
  1725.    *mouse_y_move = current_pos.y - window_center_y + mouse_y_accum;
  1726.    mouse_x_accum = 0;
  1727.    mouse_y_accum = 0;
  1728.  
  1729.    // force the mouse to the center, so there's room to move
  1730.    SetCursorPos (window_center_x, window_center_y);
  1731. }
  1732.  
  1733.  
  1734. /////////////////////////////////////////////////////////////////////
  1735. // Adds a video mode to the list if it's not a duplicate and can
  1736. // actually be set.
  1737. /////////////////////////////////////////////////////////////////////
  1738. void TryToAddToList(DEVMODE *pdevmode)
  1739. {
  1740.     int     i;
  1741.  
  1742.     // see if this is a duplicate mode (can happen because of refresh
  1743.     // rates, or because we explicitly try all the low-res modes)
  1744.     for (i=0; i<numvmodes; i++)
  1745.     {
  1746.         if ((pdevmode->dmBitsPerPel == vmodes[i].bpp) &&
  1747.             (pdevmode->dmPelsWidth == vmodes[i].width) &&
  1748.             (pdevmode->dmPelsHeight == vmodes[i].height))
  1749.         {
  1750.             // it's a duplicate mode
  1751.             return;
  1752.         }
  1753.     }
  1754.  
  1755.     // do a mode set test (doesn't actually do the mode set, but
  1756.     // reports whether it would have succeeded)
  1757.     if (ChangeDisplaySettings(pdevmode, CDS_TEST | CDS_FULLSCREEN) !=
  1758.         DISP_CHANGE_SUCCESSFUL)
  1759.     {
  1760.         return;
  1761.     }
  1762.  
  1763.     // filter outrageous values so the sprintf below is safe
  1764.     if ((pdevmode->dmBitsPerPel > 32) ||
  1765.         (pdevmode->dmPelsWidth > 9999) ||
  1766.         (pdevmode->dmPelsHeight > 9999))
  1767.     {
  1768.         return;
  1769.     }
  1770.  
  1771.     // it's a new, valid mode; add this to the list
  1772.     vmodes[numvmodes].bpp = pdevmode->dmBitsPerPel;
  1773.     vmodes[numvmodes].width = pdevmode->dmPelsWidth;
  1774.     vmodes[numvmodes].height = pdevmode->dmPelsHeight;
  1775.     sprintf (vmodes[numvmodes].description, "%dx%d, %d bpp",
  1776.              pdevmode->dmPelsWidth, pdevmode->dmPelsHeight,
  1777.              pdevmode->dmBitsPerPel);
  1778.     numvmodes++;
  1779. }
  1780.  
  1781.  
  1782. /////////////////////////////////////////////////////////////////////
  1783. // Puts up video mode list box.
  1784. /////////////////////////////////////////////////////////////////////
  1785. int PutUpModeListBox(void)
  1786. {
  1787.     int i, modenum, done;
  1788.     DEVMODE devmode;
  1789.  
  1790.  
  1791.     // enumerate all available video modes
  1792.     numvmodes = 0;
  1793.     modenum = 0;
  1794.     done = 0;
  1795.  
  1796.     do
  1797.     {
  1798.         done = !EnumDisplaySettings(NULL, modenum, &devmode);
  1799.         TryToAddToList(&devmode);
  1800.         modenum++;
  1801.     } while (!done & (numvmodes < MAXVMODES));
  1802.  
  1803.     // low-res modes don't always enumerate; ask about them explicitly
  1804.  
  1805.     // make sure the driver doesn't just answer yes to all tests
  1806.     devmode.dmBitsPerPel = 8;
  1807.     devmode.dmPelsWidth = 42;
  1808.     devmode.dmPelsHeight = 37;
  1809.     devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
  1810.  
  1811.     if (ChangeDisplaySettings(&devmode, CDS_TEST | CDS_FULLSCREEN) !=
  1812.         DISP_CHANGE_SUCCESSFUL)
  1813.     {
  1814.         for (i=0; (i<NUMLOWRESMODES) && (numvmodes < MAXVMODES); i++)
  1815.         {
  1816.             devmode.dmSize = sizeof(devmode);
  1817.             devmode.dmBitsPerPel = lowresmodes[i].bpp;
  1818.             devmode.dmPelsWidth = lowresmodes[i].width;
  1819.             devmode.dmPelsHeight = lowresmodes[i].height;
  1820.             devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
  1821.             TryToAddToList(&devmode);
  1822.         }
  1823.     }
  1824.  
  1825.     // let user select desired mode from listbox
  1826.     hwndLB = CreateWindow("listbox",
  1827.         NULL,
  1828.         WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LBS_NOTIFY,
  1829.         DIBWidth / 2 - 150,
  1830.         DIBHeight / 2 - 200,
  1831.         300,
  1832.         400,
  1833.         hwndOutput,
  1834.         (HANDLE)1,
  1835.         hInst,
  1836.         NULL);
  1837.  
  1838.     if (!hwndLB)
  1839.         return 0;
  1840.  
  1841.     SetFocus(hwndLB);
  1842.  
  1843.     for (i=0; i<numvmodes; i++)
  1844.     {
  1845.         SendMessage(hwndLB, LB_ADDSTRING, 0, (LONG)(LPSTR)vmodes[i].description);
  1846.     }
  1847.  
  1848.     inLB = 1;
  1849.  
  1850.     return 1;
  1851. }
  1852.  
  1853.  
  1854. /////////////////////////////////////////////////////////////////////
  1855. // Restores default desktop video mode.
  1856. /////////////////////////////////////////////////////////////////////
  1857. void RestoreDefaultMode(void)
  1858. {
  1859.     ChangeDisplaySettings(NULL, CDS_FULLSCREEN);
  1860. }
  1861.  
  1862.  
  1863. /////////////////////////////////////////////////////////////////////
  1864. // Sets the mode vmodes[curLBIndex].
  1865. /////////////////////////////////////////////////////////////////////
  1866. void SetVMode(int curLBIndex)
  1867. {
  1868.     DEVMODE devmode;
  1869.  
  1870.     devmode.dmSize = sizeof(devmode);
  1871.     devmode.dmBitsPerPel = vmodes[curLBIndex].bpp;
  1872.     devmode.dmPelsWidth = vmodes[curLBIndex].width;
  1873.     devmode.dmPelsHeight = vmodes[curLBIndex].height;
  1874.     devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
  1875.  
  1876.     if (ChangeDisplaySettings(&devmode, CDS_FULLSCREEN) !=
  1877.         DISP_CHANGE_SUCCESSFUL)
  1878.     {
  1879.         // mode set failed; we'll just run in the desktop mode
  1880.     }
  1881. }
  1882.  
  1883.