home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / directx2 / sdk / samples / flip2d / flipcube.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-28  |  28.7 KB  |  996 lines

  1. /**************************************************************************
  2.  
  3.     FLIPCUBE.CPP - A spinning cube demo for DirectDraw
  4.  
  5.     basic page fliping app, just render to the back buffer and flip
  6.     that is all I do.
  7.  
  8.  **************************************************************************/
  9. /**************************************************************************
  10.  
  11.     (C) Copyright 1995-1996 Microsoft Corp.  All rights reserved.
  12.  
  13.     You have a royalty-free right to use, modify, reproduce and 
  14.     distribute the Sample Files (and/or any modified version) in 
  15.     any way you find useful, provided that you agree that 
  16.     Microsoft has no warranty obligations or liability for any 
  17.     Sample Application Files which are modified. 
  18.  
  19.  **************************************************************************/
  20.  
  21. #include <windows.h>
  22. #include <windowsx.h>
  23. #include <mmsystem.h>
  24. #include <ddraw.h>
  25.  
  26. #include "flipcube.h"
  27. #include "dumb3d.h"
  28.  
  29. // code is in tri.cpp
  30. extern void Triangle8(BYTE *p, int next_scan, POINT P0, POINT P1, POINT P2, DWORD c);
  31.  
  32. /**************************************************************************
  33.   Global Variables
  34.  **************************************************************************/
  35.  
  36. static char szAppName[]="DirectDraw Spinning Cube";
  37.  
  38. static HINSTANCE  hInstApp;
  39. static BOOL       fAppActive;
  40. static BOOL       fAppPaused;
  41. static HWND       hwndApp;
  42. static HACCEL     hAccelApp;
  43. static HFONT      AppFont;
  44. static SIZE       ScreenSize;
  45. static BOOL       fDrawWithGDI;
  46.  
  47. /**************************************************************************
  48.   DirectDraw Globals
  49.  **************************************************************************/
  50.  
  51. IDirectDraw            *dd;
  52. IDirectDrawSurface     *FrontBuffer;
  53. IDirectDrawSurface     *BackBuffer;
  54. IDirectDrawPalette     *Palette;
  55.  
  56. /**************************************************************************
  57.   dumb 3D Globals
  58.  **************************************************************************/
  59.  
  60. //*** Cube vertices, normals, shades, and modeling transform
  61. static point_4 CubeVertices[8] =
  62. {
  63.   point_4( -10,  10, -10 ),
  64.   point_4( -10,  10,  10 ),
  65.   point_4(  10,  10,  10 ),
  66.   point_4(  10,  10, -10 ),
  67.   point_4(  10, -10, -10 ),
  68.   point_4(  10, -10,  10 ),
  69.   point_4( -10, -10,  10 ),
  70.   point_4( -10, -10, -10 )
  71. };
  72. static vector_4   CubeSurfaceNormals[6];
  73. static real       CubeSurfaceShades[6];
  74. static matrix_4x4 CubeTransform;
  75.  
  76. //*** Cube edges - ordered indices into the vertex array
  77. const int CubeFaces[6][4] =
  78. {
  79.   0, 1, 2, 3,
  80.   2, 1, 6, 5,
  81.   3, 2, 5, 4,
  82.   0, 3, 4, 7,
  83.   1, 0, 7, 6,
  84.   4, 5, 6, 7
  85. };
  86.  
  87. //*** Cube colors - one RGB color per surface
  88. const unsigned char CubeColors[6][3] =
  89. {
  90.   240,  20,  20,    // Unsaturated Red
  91.    20, 240,  20,    // Unsaturated Green
  92.    20,  20, 240,    // Unsaturated Blue
  93.   128,  64,   0,    // Brown
  94.   240,  20, 240,    // Unsaturated Magenta
  95.   240, 240,  20     // Unsaturated Yellow
  96. };
  97.  
  98. //*** Lighting
  99. vector_4   LightSourceDirection;
  100. const real AmbientLight = 0.2;
  101.  
  102. //*** Viewing and perspective
  103. static matrix_4x4  ViewPerspective;
  104. static point_4     Viewpoint(60, 60, 60);
  105. static vector_4    Up(0, 1, 0);
  106. static point_4     Origin;
  107.  
  108. //*** Interaction
  109. static POINT       Move;
  110. static POINT       Last;
  111.  
  112. /**************************************************************************
  113.    Internal function declarations
  114.  **************************************************************************/
  115.  
  116. LONG  CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
  117. BOOL  AppIdle(void);
  118. void  RenderFrame(void);
  119.  
  120. void  TransformCube(matrix_4x4 const &Transform);
  121. BOOL  ProjectAndDrawCube(HDC hdc, int XOffset, int YOffset);
  122. BOOL  ProjectAndDrawCube(IDirectDrawSurface *pdds, int XOffset, int YOffset);
  123.  
  124. /**************************************************************************
  125.   AppAbout
  126.  
  127.   Description:
  128.     This function handles messages belonging to the "About" dialog box.
  129.   The only message that it looks for is WM_COMMAND, indicating the user
  130.   has pressed the "OK" button.
  131.  **************************************************************************/
  132.  
  133. BOOL FAR PASCAL AppAbout(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
  134. {
  135.   switch (msg)
  136.   {
  137.     case WM_COMMAND:
  138.       if (LOWORD(wParam) == IDOK)
  139.         EndDialog(hwnd, TRUE);
  140.       break;
  141.  
  142.     case WM_INITDIALOG:
  143.       return TRUE;
  144.   }
  145.   return FALSE;
  146. }
  147.  
  148. /**************************************************************************
  149.   DDInit
  150.  
  151.   Description:
  152.     initialize all the DirectDraw specific stuff
  153.  **************************************************************************/
  154.  
  155. BOOL DDInit()
  156. {
  157.     HRESULT err;
  158.  
  159.     err = DirectDrawCreate(NULL, &dd, NULL);
  160.  
  161.     if (err != DD_OK)
  162.         return FALSE;
  163.  
  164.     err = dd->SetCooperativeLevel(hwndApp,
  165.         DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
  166.  
  167.     if (err != DD_OK)
  168.         return FALSE;
  169.  
  170.     return TRUE;
  171. }
  172.  
  173. /**************************************************************************
  174.   DDSetMode
  175.  **************************************************************************/
  176.  
  177. BOOL DDSetMode(int width, int height, int bpp)
  178. {
  179.     HRESULT err;
  180.  
  181.     err = dd->SetDisplayMode(width, height, bpp);
  182.  
  183.     if (err != DD_OK)
  184.         return FALSE;
  185.  
  186.     ScreenSize.cx = width;
  187.     ScreenSize.cy = height;
  188.  
  189.     // get rid of any previous surfaces.
  190.     if (BackBuffer)  BackBuffer->Release(),     BackBuffer = NULL;
  191.     if (FrontBuffer) FrontBuffer->Release(),    FrontBuffer = NULL;
  192.     if (Palette)     Palette->Release(),        Palette = NULL;
  193.  
  194.     //
  195.     // Create surfaces
  196.     //
  197.     // what we want is a tripple buffered surface in video memory
  198.     // so we try to create this first.
  199.     //
  200.     // if we cant get a triple buffered surface, we try again
  201.     // for a double buffered surface (still must be in video memory)
  202.     //
  203.     // if we cant get a double buffered surface, we try for a double
  204.     // buffered surface not being specific about video memory, we will
  205.     // get back a main-memory surface, that work use HW page flipping
  206.     // but at least we run.
  207.     //
  208.     // NOTE you need to recreate the surfaces for a new display mode
  209.     // they wont work when/if the mode is changed.
  210.     //
  211.     DDSURFACEDESC ddsd;
  212.  
  213.     ZeroMemory(&ddsd, sizeof(ddsd));
  214.     ddsd.dwSize = sizeof(ddsd);
  215.     ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  216.     ddsd.dwBackBufferCount = 2;
  217.     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
  218.                           DDSCAPS_FLIP |
  219.                           DDSCAPS_COMPLEX |
  220.                           DDSCAPS_VIDEOMEMORY;
  221.  
  222.     // try to get a triple buffered video memory surface.
  223.     err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  224.  
  225.     if (err != DD_OK)
  226.     {
  227.         // try to get a double buffered video memory surface.
  228.         ddsd.dwBackBufferCount = 1;
  229.         err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  230.     }
  231.  
  232.     if (err != DD_OK)
  233.     {
  234.         // settle for a main memory surface.
  235.         ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
  236.         err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  237.     }
  238.  
  239.     if (err != DD_OK)
  240.         return FALSE;
  241.  
  242.     // get a pointer to the back buffer
  243.     DDSCAPS caps;
  244.     caps.dwCaps = DDSCAPS_BACKBUFFER;
  245.     err = FrontBuffer->GetAttachedSurface(&caps, &BackBuffer);
  246.  
  247.     if (err != DD_OK)
  248.         return FALSE;
  249.  
  250.     // create a palette if we are in a paletized display mode.
  251.     //
  252.     // NOTE because we want to be able to show dialog boxs and
  253.     // use our menu, we leave the windows reserved colors as is
  254.     // so things dont look ugly.
  255.     //
  256.     // palette is setup like so:
  257.     //
  258.     //      10      windows system colors
  259.     //      64      red wash
  260.     //      64      grn wash
  261.     //      64      blu wash
  262.     //
  263.     PALETTEENTRY ape[256];
  264.     HDC hdc = GetDC(NULL);
  265.     if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
  266.     {
  267.         // get the current windows colors.
  268.         GetSystemPaletteEntries(hdc, 0, 256, ape);
  269.  
  270.         // make a red, grn, and blu wash for our cube.
  271.         for (int i=0; i<64; i++)
  272.         {
  273.             ape[10+64*0+i].peRed   = i * 255/63;
  274.             ape[10+64*0+i].peGreen = 0;
  275.             ape[10+64*0+i].peBlue  = 0;
  276.  
  277.             ape[10+64*1+i].peRed   = 0;
  278.             ape[10+64*1+i].peGreen = i * 255/63;
  279.             ape[10+64*1+i].peBlue  = 0;
  280.  
  281.             ape[10+64*2+i].peRed   = 0;
  282.             ape[10+64*2+i].peGreen = 0;
  283.             ape[10+64*2+i].peBlue  = i * 255/63;
  284.         }
  285.  
  286.         // create the palette.
  287.         err = dd->CreatePalette(DDPCAPS_8BIT, ape, &Palette, NULL);
  288.  
  289.         if (err == DD_OK)
  290.         {
  291.             FrontBuffer->SetPalette(Palette);
  292.         }
  293.     }
  294.     ReleaseDC(NULL, hdc);
  295.  
  296.     if (AppFont)
  297.         DeleteObject(AppFont);
  298.  
  299.     AppFont = CreateFont(width < 640 ? 24 : 48,
  300.         0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
  301.         ANSI_CHARSET,
  302.         OUT_DEFAULT_PRECIS,
  303.         CLIP_DEFAULT_PRECIS,
  304.         NONANTIALIASED_QUALITY,
  305.         VARIABLE_PITCH,
  306.         "Comic Sans MS");
  307.  
  308.     return TRUE;
  309. }
  310.  
  311.  
  312. /**************************************************************************
  313.   DDTerm
  314.  **************************************************************************/
  315.  
  316. void DDTerm()
  317. {
  318.     if (BackBuffer)  BackBuffer->Release(),     BackBuffer = NULL;
  319.     if (FrontBuffer) FrontBuffer->Release(),    FrontBuffer = NULL;
  320.     if (Palette)     Palette->Release(),        Palette = NULL;
  321.     if (dd)          dd->Release(),             dd = NULL;
  322. }
  323.  
  324. /**************************************************************************
  325.   ModeCallback
  326.  **************************************************************************/
  327.  
  328. HRESULT CALLBACK ModeCallback(LPDDSURFACEDESC pdds, LPVOID lParam)
  329. {
  330.     HMENU hmenu = (HMENU)lParam;
  331.     char ach[80];
  332.     int n;
  333.     int width  = pdds->dwWidth;
  334.     int height = pdds->dwHeight;
  335.     int bpp    = pdds->ddpfPixelFormat.dwRGBBitCount;
  336.  
  337.     n = GetMenuItemCount(hmenu);
  338.     wsprintf(ach,"%dx%dx%d",width,height,bpp);
  339.     AppendMenu(hmenu,MF_STRING,MENU_MODE+n,ach);
  340.  
  341.     MENUITEMINFO mii;
  342.  
  343.     // pack the mode info into a DWORD and set the extra item data.
  344.     mii.cbSize = sizeof(mii);
  345.     mii.fMask = MIIM_DATA;
  346.     mii.dwItemData = width | (height << 12) | (bpp << 24);
  347.     SetMenuItemInfo(hmenu, MENU_MODE+n, MF_BYCOMMAND, &mii);
  348.  
  349.     //return S_TRUE to stop enuming modes, S_FALSE to continue
  350.     return S_FALSE;
  351. }
  352.  
  353. /**************************************************************************
  354.   AppInit
  355.  
  356.   Description:
  357.     This is called when the application is first loaded. It initializes
  358.   all variables, registers the window class, and creates the main app
  359.   window.
  360.  **************************************************************************/
  361.  
  362. BOOL AppInit(HINSTANCE hInst,HINSTANCE hPrev,int sw,LPSTR szCmdLine)
  363. {
  364.   WNDCLASS cls;
  365.  
  366.   /* Save instance handle for DialogBoxes */
  367.   hInstApp = hInst;
  368.  
  369.   if (!hPrev)
  370.   {
  371.     //***  Register a class for the main application window
  372.     cls.hCursor        = LoadCursor(0,IDC_ARROW);
  373.  
  374.     //*** Just for fun, we'll draw our own spinning cube icon.
  375.     cls.hIcon          = LoadIcon(hInst, "AppIcon");
  376.     cls.lpszMenuName   = "AppMenu";
  377.     cls.lpszClassName  = szAppName;
  378.     cls.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);
  379.     cls.hInstance      = hInst;
  380.     cls.style          = CS_VREDRAW | CS_HREDRAW;
  381.     cls.lpfnWndProc    = (WNDPROC)AppWndProc;
  382.     cls.cbClsExtra     = 0;
  383.     cls.cbWndExtra     = 0;
  384.  
  385.     if (!RegisterClass(&cls))
  386.       return FALSE;
  387.   }
  388.  
  389.   hAccelApp = LoadAccelerators(hInst, "AppAccel");
  390.  
  391.   //*** Set and normalize the light source
  392.   LightSourceDirection = vector_4(50, 30, -15);
  393.   LightSourceDirection.Normalize();
  394.  
  395.   //*** Distance to view plane:
  396.   ViewPerspective.SetElement(3, 2, 1/300.0);
  397.   ViewPerspective.SetElement(3, 3, 0);
  398.  
  399.   //*** Viewport scaling - some arbitrary number like 3.5 will do
  400.   ViewPerspective.SetElement(0, 0, 3.5);
  401.   ViewPerspective.SetElement(1, 1, 3.5);
  402.  
  403.   //*** Calculate the initial normals and shades
  404.   TransformCube(CubeTransform);
  405.  
  406.   //*** Then generate an interesting rotation for the spin
  407.   CubeTransform.ConcatenateYRotation(6.0);
  408.   CubeTransform.ConcatenateXRotation(3.5);
  409.   CubeTransform.ConcatenateZRotation(2.0);
  410.  
  411.   hwndApp = CreateWindowEx(
  412.                   WS_EX_APPWINDOW,
  413.                   szAppName,           // Class name
  414.                   szAppName,           // Caption
  415.                   WS_POPUP |
  416.                   WS_SYSMENU |
  417.                   WS_CAPTION,
  418.                   0, 0,                // Position
  419.                   640,480,             // Size
  420.                   0,                   // Parent window (no parent)
  421.                   0,                   // use class menu
  422.                   hInst,               // handle to window instance
  423.                   0                    // no params to pass on
  424.                   );
  425.   ShowWindow(hwndApp,sw);
  426.   UpdateWindow(hwndApp);
  427.  
  428.   if (!DDInit())
  429.     return FALSE;
  430.  
  431.   // Enumerate all posible display modes, and stick them in our menu.
  432.   // we use the extra item DWORD of a menu item to store the mode info
  433.   HMENU hmenu = CreatePopupMenu();
  434.   dd->EnumDisplayModes(0,NULL,(LPVOID)hmenu,ModeCallback);
  435.   AppendMenu(GetMenu(hwndApp),MF_POPUP,(UINT)hmenu,"Modes");
  436.  
  437.   if (!DDSetMode(640,480,8) &&
  438.       !DDSetMode(640,480,16))
  439.     return FALSE;
  440.  
  441.   return TRUE;
  442. }
  443.  
  444. /**************************************************************************
  445.   WinMain
  446.  
  447.   Description:
  448.     The main procedure for the App.  After initializing, it just goes
  449.   into a message-processing loop until it gets a WM_QUIT message.
  450.  **************************************************************************/
  451.  
  452. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  453. {
  454.   MSG     msg;
  455.  
  456.   //*** Call initialization procedure
  457.   if (!AppInit(hInst,hPrev,sw,szCmdLine))
  458.     return FALSE;
  459.  
  460.     //*** Polling messages from event queue until quit
  461.   for (;;)
  462.   {
  463.     if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  464.     {
  465.       if (msg.message == WM_QUIT)
  466.         break;
  467.  
  468.       if (!hwndApp || !TranslateAccelerator(hwndApp, hAccelApp, &msg))
  469.       {
  470.         TranslateMessage(&msg);
  471.         DispatchMessage(&msg);
  472.       }
  473.     }
  474.     else
  475.     {
  476.       if (AppIdle())
  477.         WaitMessage();
  478.     }
  479.   }
  480.  
  481.   DDTerm();
  482.   return msg.wParam;
  483. }
  484.  
  485. /**************************************************************************
  486.   AppPause
  487.  
  488.  **************************************************************************/
  489.  
  490. void AppPause(BOOL f)
  491. {
  492.     if (f)
  493.     {
  494.         DDSCAPS caps;
  495.         FrontBuffer->GetCaps(&caps);
  496.  
  497.         // if we are in ModeX go back to a windows mode
  498.         // so we can see the menu or dialog box.
  499.  
  500.         if (caps.dwCaps & DDSCAPS_MODEX)
  501.         {
  502.             DDSetMode(640,480,8);
  503.         }
  504.  
  505.         fAppPaused = TRUE;
  506.         dd->FlipToGDISurface();
  507.         DrawMenuBar(hwndApp);
  508.         RedrawWindow(hwndApp, NULL, NULL, RDW_FRAME);
  509.     }
  510.     else
  511.     {
  512.         fAppPaused = FALSE;
  513.     }
  514. }
  515.  
  516. /**************************************************************************
  517.   AppIdle
  518.  
  519.   return TRUE if the app is idle
  520.   return FALSE if the app is not idle.
  521.  
  522.   Description:
  523.  **************************************************************************/
  524.  
  525. BOOL AppIdle()
  526. {
  527.   //*** Spin while the app is active, lbutton is up, and spinning is on.
  528.   //*** Spin while the app is iconized.
  529.   if (fAppActive && !fAppPaused)
  530.   {
  531.     //*** If the app is active, spin the cube and redraw
  532.     if (GetAsyncKeyState(VK_LBUTTON) < 0)
  533.     {
  534.         if(Move.x || Move.y)
  535.         {
  536.             matrix_4x4 Movement;
  537.             Movement.ConcatenateYRotation(Move.x);
  538.             Movement.ConcatenateXRotation(Move.y);
  539.             Move.x = Move.y = 0;
  540.             TransformCube(Movement);
  541.         }
  542.     }
  543.     else
  544.     {
  545.         TransformCube(CubeTransform);
  546.     }
  547.     RenderFrame();
  548.     return FALSE;
  549.   }
  550.   else
  551.   {
  552.     //*** Don't do anything when not the active app
  553.     return TRUE;
  554.   }
  555. }
  556.  
  557. /**************************************************************************
  558.   RenderFrame
  559.  
  560.   render the frame into the back buffer and do a page flip.
  561.  
  562.   things to NOTE:
  563.  
  564.     we use the blter to clear the backbuffer, this usualy is a big
  565.     win blters are real fast.
  566.  
  567.     we use GDI to draw the frame rate, and info text
  568.  
  569.     we either use GDI to draw the faces of the cube, or our own code
  570.     based on the fDrawWithGDI global variable.
  571.  
  572.  **************************************************************************/
  573.  
  574. int FrameRate;
  575. int FrameCount;
  576. int FrameCount0;
  577. DWORD FrameTime;
  578. DWORD FrameTime0;
  579.  
  580. void RenderFrame()
  581. {
  582.   HDC hdc;
  583.  
  584.   //*** always need to handle DDERR_SURFACELOST, this will happen
  585.   //*** when we get switched away from.
  586.  
  587.   if (FrontBuffer->IsLost() == DDERR_SURFACELOST)
  588.     FrontBuffer->Restore();
  589.  
  590.   //*** use the blter to do a color fill to clear the back buffer
  591.  
  592.   DDBLTFX ddbltfx;
  593.   ddbltfx.dwSize = sizeof(ddbltfx);
  594.   ddbltfx.dwFillColor = 0;
  595.   BackBuffer->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&ddbltfx);
  596.  
  597.   //*** based on the fDrawWithGDI global variable, we either
  598.   //*** render the polygons ourself or let GDI do it
  599.  
  600.   BOOL fGDI = fDrawWithGDI;
  601.  
  602.   //*** render the cube with our own code.
  603.   //*** we need to do this outside of the GetDC, because we cant
  604.   //*** lock a buffer while we have a DC on it.
  605.   //*** if ProjectAndDrawCube returns FALSE it did not want to
  606.   //*** draw for some reason, so go ahead and use GDI
  607.  
  608.   if (!fGDI)
  609.      fGDI = !ProjectAndDrawCube(BackBuffer, ScreenSize.cx/2, ScreenSize.cy/2);
  610.  
  611.   if (BackBuffer->GetDC(&hdc) == DD_OK)
  612.   {
  613.     //*** use GDI to draw the cube.
  614.     if (fGDI)
  615.       ProjectAndDrawCube(hdc, ScreenSize.cx/2, ScreenSize.cy/2);
  616.  
  617.     //*** draw stats, like frame number and frame rate
  618.  
  619.     char ach[128];
  620.     int len;
  621.     static char szHelp[] = "F10=Menu F7=Smaller F8=Larger";
  622.  
  623.     SetBkMode(hdc, TRANSPARENT);
  624.     SelectObject(hdc, AppFont);
  625.  
  626.     len = wsprintf(ach, "FPS %02d Frame %05d", FrameRate, FrameCount);
  627.  
  628.     SetTextColor(hdc, RGB(255, 255, 0));
  629.     TextOut(hdc, 0, 0, ach, len);
  630.     TextOut(hdc, 0, ScreenSize.cy-(ScreenSize.cx<640 ? 24:48),szHelp,sizeof(szHelp)-1);
  631.  
  632.     BackBuffer->ReleaseDC(hdc);
  633.   }
  634.  
  635.   //*** we have rendered the backbuffer, call flip so we can see it
  636.   FrontBuffer->Flip(NULL, DDFLIP_WAIT);
  637.  
  638.   FrameCount++;
  639.   FrameTime = timeGetTime();
  640.  
  641.   if (FrameTime - FrameTime0 > 1000)
  642.   {
  643.     FrameRate = (FrameCount - FrameCount0) * 1000 / (FrameTime - FrameTime0);
  644.     FrameTime0 = FrameTime;
  645.     FrameCount0 = FrameCount;
  646.   }
  647. }
  648.  
  649. /**************************************************************************
  650.   AppWndProc
  651.  
  652.   Description:
  653.     Main window proc. Standard Windows fare.
  654.  **************************************************************************/
  655.  
  656. LONG CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
  657. {
  658.   RECT Rect;
  659.  
  660.   switch (msg)
  661.   {
  662.     case WM_CREATE:
  663.       break;
  664.  
  665.     case WM_ACTIVATEAPP:
  666.       //*** Keep track of whether or not the app is in the foreground
  667.       fAppActive = (BOOL)wParam;
  668.       break;
  669.  
  670.     case WM_SETCURSOR:
  671.       if (fAppActive && !fAppPaused)
  672.       {
  673.         SetCursor(NULL);
  674.         return 1;
  675.       }
  676.       break;
  677.  
  678.     case WM_ENTERMENULOOP:
  679.       AppPause(TRUE);
  680.       break;
  681.  
  682.     case WM_EXITMENULOOP:
  683.       AppPause(FALSE);
  684.       break;
  685.  
  686.     case WM_INITMENUPOPUP:
  687.         CheckMenuItem((HMENU)wParam, MENU_GDI, fDrawWithGDI ? MF_CHECKED : MF_UNCHECKED);
  688.         break;
  689.  
  690.     case WM_COMMAND:
  691.       switch(LOWORD(wParam))
  692.       {
  693.         case MENU_ABOUT:
  694.           AppPause(TRUE);
  695.           DialogBox(hInstApp, "AppAbout", hwnd, (DLGPROC)AppAbout);
  696.           AppPause(FALSE);
  697.           break;
  698.  
  699.         case MENU_EXIT:
  700.           PostMessage(hwnd, WM_CLOSE, 0, 0L);
  701.           break;
  702.  
  703.         case MENU_LARGER:
  704.         {
  705.           matrix_4x4  m;
  706.           m.SetElement(0,0,1.5);
  707.           m.SetElement(1,1,1.5);
  708.           m.SetElement(2,2,1.5);
  709.           TransformCube(m);
  710.           break;
  711.         }
  712.  
  713.         case MENU_SMALLER:
  714.         {
  715.           matrix_4x4  m;
  716.           m.SetElement(0,0,.9);
  717.           m.SetElement(1,1,.9);
  718.           m.SetElement(2,2,.9);
  719.           TransformCube(m);
  720.           break;
  721.         }
  722.  
  723.         case MENU_GDI:
  724.           fDrawWithGDI = !fDrawWithGDI;
  725.           break;
  726.       }
  727.       if (LOWORD(wParam) >= MENU_MODE && LOWORD(wParam) < MENU_MODE+100)
  728.       {
  729.         MENUITEMINFO mii;
  730.  
  731.         mii.cbSize = sizeof(mii);
  732.         mii.fMask = MIIM_DATA;
  733.         GetMenuItemInfo(GetMenu(hwnd), LOWORD(wParam), MF_BYCOMMAND, &mii);
  734.  
  735.         DDSetMode(
  736.             (mii.dwItemData >> 0)  & 0xFFF,
  737.             (mii.dwItemData >> 12) & 0xFFF,
  738.             (mii.dwItemData >> 24) & 0x0FF);
  739.       }
  740.       return 0L;
  741.  
  742.     case WM_DESTROY:
  743.       hwndApp = NULL;
  744.       PostQuitMessage(0);
  745.       break;
  746.  
  747.     case WM_PAINT:
  748.       break;
  749.  
  750.     case WM_MOVE:
  751.     case WM_SIZE:
  752.     case WM_DISPLAYCHANGE:
  753.       if (fAppActive && !IsIconic(hwnd))
  754.       {
  755.         SetRect(&Rect, 0, GetSystemMetrics(SM_CYCAPTION), GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
  756.         AdjustWindowRectEx(&Rect, WS_POPUP | WS_CAPTION, FALSE, 0);
  757.         SetWindowPos(hwnd, NULL, Rect.left, Rect.top, Rect.right-Rect.left, Rect.bottom-Rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
  758.       }
  759.       break;
  760.  
  761.     case WM_LBUTTONDOWN:
  762.       //*** Get the start location for mouse rotations
  763.       Last.x = LOWORD(lParam);
  764.       Last.y = HIWORD(lParam);
  765.       break;
  766.  
  767.     case WM_MOUSEMOVE:
  768.       //*** While the mouse button is down, keep track of movement
  769.       //*** to update the eye position
  770.       if(GetKeyState(VK_LBUTTON) < 0)
  771.       {
  772.         Move.x = (int)LOWORD(lParam) - Last.x;
  773.         Move.y = (int)HIWORD(lParam) - Last.y;
  774.         Last.x = LOWORD(lParam);
  775.         Last.y = HIWORD(lParam);
  776.       }
  777.       break;
  778.   }
  779.  
  780.   return DefWindowProc(hwnd,msg,wParam,lParam);
  781. }
  782.  
  783. /**************************************************************************
  784.   TransformCube
  785.  
  786.   Description:
  787.     Transforms the cube vertices by the current rotation matrix.
  788.     Recalculates normals and flat shade values for the
  789.   directional light source.
  790.  **************************************************************************/
  791.  
  792. void TransformCube(matrix_4x4 const &Transform)
  793. {
  794.   int i;
  795.  
  796.   //*** Transform the cube by the matrix
  797.   for (i = 0; i < 8; ++i)
  798.     CubeVertices[i] = Transform * CubeVertices[i];
  799.  
  800.   //*** Recalculate normals and shades
  801.   for (i = 0; i < 6; ++i)
  802.   {
  803.     //*** Normals are perpendicular to two edges of the cube
  804.     vector_4 Edge1, Edge2;
  805.     Edge1 = CubeVertices[CubeFaces[i][1]] - CubeVertices[CubeFaces[i][0]];
  806.     Edge2 = CubeVertices[CubeFaces[i][3]] - CubeVertices[CubeFaces[i][0]];
  807.     CubeSurfaceNormals[i] = CrossProduct(Edge1, Edge2);
  808.     CubeSurfaceNormals[i].Normalize();
  809.  
  810.     //*** Cosine shading based on the surface normal, clamped to [0, 1]
  811.     real Shade = DotProduct(CubeSurfaceNormals[i], LightSourceDirection);
  812.     Shade = Shade + AmbientLight;
  813.     if (Shade < 0) Shade = 0;
  814.     else if (Shade > 1.0) Shade = 1.0;
  815.     CubeSurfaceShades[i] = Shade;
  816.   }
  817. }
  818.  
  819. /**************************************************************************
  820.   ProjectAndDrawCube
  821.  
  822.   Description:
  823.     Projects the cube vertices for the current viewpoint then culls
  824.   in screen space and draws into the DC via GDI.
  825.  **************************************************************************/
  826.  
  827. BOOL ProjectAndDrawCube(HDC hdc, int XOffset, int YOffset)
  828. {
  829.   //*** Create a viewing transform for the current eye position
  830.   vector_4 ViewDirection = Origin - Viewpoint;
  831.   ViewDirection.Normalize();
  832.   view_transform View(Viewpoint, ViewDirection, Up);
  833.  
  834.   //*** Transform and project the vertices into screen space
  835.   int i;
  836.   POINT aScreenVertices[8];
  837.   for (i = 0; i < 8; ++i)
  838.   {
  839.     point_4 Temp = View * CubeVertices[i];
  840.     Temp = ViewPerspective * Temp;
  841.     Temp.Homogenize();
  842.  
  843.     aScreenVertices[i].x = (int)Temp.GetX() + XOffset;
  844.     aScreenVertices[i].y = (int)Temp.GetY() + YOffset;
  845.   }
  846.  
  847.   SelectPen(hdc, GetStockPen(NULL_PEN));
  848.  
  849.   for (i = 0; i < 6; ++i)
  850.   {
  851.     //*** Standard culling operation based on the z value of the
  852.     //*** cross product of the edges: are the vertices oriented in the
  853.     //*** counterclockwise or clockwise direction?
  854.     real v1 = aScreenVertices[ CubeFaces[i][2] ].x -
  855.       aScreenVertices[ CubeFaces[i][1] ].x;
  856.     real w1 = aScreenVertices[ CubeFaces[i][0] ].x -
  857.       aScreenVertices[ CubeFaces[i][1] ].x;
  858.     real v2 = aScreenVertices[ CubeFaces[i][2] ].y -
  859.       aScreenVertices[ CubeFaces[i][1] ].y;
  860.     real w2 = aScreenVertices[ CubeFaces[i][0] ].y -
  861.       aScreenVertices[ CubeFaces[i][1] ].y;
  862.     if ((v1*w2 - v2*w1) <= 0)
  863.       continue;
  864.  
  865.     //*** Create a brush for the shaded face color using the selected dither
  866.  
  867.     HBRUSH hbr;
  868.  
  869.     //*** Get the shading colors
  870.  
  871.     int Red, Green, Blue;
  872.  
  873.     Red   = (int)(CubeColors[i][0] * CubeSurfaceShades[i]);
  874.     Green = (int)(CubeColors[i][1] * CubeSurfaceShades[i]);
  875.     Blue  = (int)(CubeColors[i][2] * CubeSurfaceShades[i]);
  876.  
  877.     //*** Create the dithered or PALETTERGB brush
  878.  
  879.     COLORREF cr;
  880.  
  881.     cr = RGB(Red, Green, Blue);
  882.     hbr = CreateSolidBrush(cr);
  883.  
  884.     //*** Collect the correct points in an array
  885.     POINT aQuadVertices[4];
  886.     for (int j = 0; j < 4; ++j)
  887.       aQuadVertices[j] = aScreenVertices[ CubeFaces[i][j] ];
  888.  
  889.     //*** Use GDI to draw the face
  890.     hbr = SelectBrush(hdc, hbr);
  891.     Polygon(hdc, aQuadVertices, 4);
  892.     hbr = SelectBrush(hdc, hbr);
  893.     DeleteObject(hbr);
  894.   }
  895.  
  896.   return TRUE;
  897. }
  898.  
  899. /**************************************************************************
  900.   ProjectAndDrawCube
  901.  
  902.   Description:
  903.     Projects the cube vertices for the current viewpoint then culls
  904.   in screen space and draws them into a DirectDrawSurface via custom code
  905.  **************************************************************************/
  906.  
  907. BOOL ProjectAndDrawCube(IDirectDrawSurface *pdds, int XOffset, int YOffset)
  908. {
  909.   //*** Lock the DirectDraw surface
  910.   DDSURFACEDESC ddsd;
  911.   ddsd.dwSize = sizeof(ddsd);
  912.  
  913.   if (pdds->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK)
  914.     return FALSE;
  915.  
  916.   //*** This code only works for 8bpp
  917.   if (ddsd.ddpfPixelFormat.dwRGBBitCount != 8)
  918.   {
  919.     pdds->Unlock(NULL);
  920.     return FALSE;
  921.   }
  922.  
  923.   //*** Create a viewing transform for the current eye position
  924.   vector_4 ViewDirection = Origin - Viewpoint;
  925.   ViewDirection.Normalize();
  926.   view_transform View(Viewpoint, ViewDirection, Up);
  927.  
  928.   //*** Transform and project the vertices into screen space
  929.   int i;
  930.   POINT aScreenVertices[8];
  931.   for (i = 0; i < 8; ++i)
  932.   {
  933.     point_4 Temp = View * CubeVertices[i];
  934.     Temp = ViewPerspective * Temp;
  935.     Temp.Homogenize();
  936.  
  937.     aScreenVertices[i].x = (int)Temp.GetX() + XOffset;
  938.     aScreenVertices[i].y = (int)Temp.GetY() + YOffset;
  939.  
  940.     //*** !!! OUR CODE DOES NOT CLIP, SO FAIL IF WE NEED CLIPPING
  941.     if (aScreenVertices[i].x < 0 || aScreenVertices[i].x >= ScreenSize.cx ||
  942.         aScreenVertices[i].y < 0 || aScreenVertices[i].y >= ScreenSize.cy)
  943.     {
  944.       pdds->Unlock(NULL);
  945.       return FALSE;
  946.     }
  947.   }
  948.  
  949.   for (i = 0; i < 6; ++i)
  950.   {
  951.     //*** Standard culling operation based on the z value of the
  952.     //*** cross product of the edges: are the vertices oriented in the
  953.     //*** counterclockwise or clockwise direction?
  954.     real v1 = aScreenVertices[ CubeFaces[i][2] ].x -
  955.       aScreenVertices[ CubeFaces[i][1] ].x;
  956.     real w1 = aScreenVertices[ CubeFaces[i][0] ].x -
  957.       aScreenVertices[ CubeFaces[i][1] ].x;
  958.     real v2 = aScreenVertices[ CubeFaces[i][2] ].y -
  959.       aScreenVertices[ CubeFaces[i][1] ].y;
  960.     real w2 = aScreenVertices[ CubeFaces[i][0] ].y -
  961.       aScreenVertices[ CubeFaces[i][1] ].y;
  962.     if ((v1*w2 - v2*w1) <= 0)
  963.       continue;
  964.  
  965.     //*** Get the shading color, palette is setup like so:
  966.     //*** 10 system, 64 red, 64 green, 64 blue
  967.  
  968.     BYTE color;
  969.  
  970.     if (CubeColors[i][0] >= 128)
  971.         color = (BYTE)(10 + 0*64 + (63 * CubeSurfaceShades[i]));
  972.     else if (CubeColors[i][1] >= 128)
  973.         color = (BYTE)(10 + 1*64 + (63 * CubeSurfaceShades[i]));
  974.     else
  975.         color = (BYTE)(10 + 2*64 + (63 * CubeSurfaceShades[i]));
  976.  
  977.     //*** Use code in tri.cpp draw the face
  978.  
  979.     Triangle8((BYTE*)ddsd.lpSurface, ddsd.lPitch,
  980.         aScreenVertices[CubeFaces[i][0]],
  981.         aScreenVertices[CubeFaces[i][1]],
  982.         aScreenVertices[CubeFaces[i][2]],
  983.         color);
  984.  
  985.     Triangle8((BYTE*)ddsd.lpSurface, ddsd.lPitch,
  986.         aScreenVertices[CubeFaces[i][2]],
  987.         aScreenVertices[CubeFaces[i][3]],
  988.         aScreenVertices[CubeFaces[i][0]],
  989.         color);
  990.   }
  991.  
  992.   //*** Never ever forget to unlock!
  993.   pdds->Unlock(NULL);
  994.   return TRUE;
  995. }
  996.