home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / flip3d / flipcube.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-10  |  37.8 KB  |  1,299 lines

  1. /**************************************************************************
  2.  
  3.     FLIP3D.CPP - A spinning cube demo for DirectDraw
  4.  
  5.     using Direct3D imediate mode (RenderPrimitive) for rendering.
  6.  
  7.     uses D3DTLVERTEX, this app does all its own transform and lighting
  8.     dumb3d.cpp is our "3d" package.
  9.  
  10.     basic page fliping app, just render to the back buffer and flip
  11.     that is all I do.
  12.  
  13.  **************************************************************************/
  14. /**************************************************************************
  15.  
  16.     (C) Copyright 1995-1997 Microsoft Corp.  All rights reserved.
  17.  
  18.     You have a royalty-free right to use, modify, reproduce and
  19.     distribute the Sample Files (and/or any modified version) in
  20.     any way you find useful, provided that you agree that
  21.     Microsoft has no warranty obligations or liability for any
  22.     Sample Application Files which are modified.
  23.  
  24.  **************************************************************************/
  25.  
  26. #define D3D_OVERLOADS
  27.  
  28. #include <windows.h>
  29. #include <windowsx.h>
  30. #include <mmsystem.h>
  31. #include <ddraw.h>
  32. #include <d3d.h>
  33. #include <math.h>
  34.  
  35. #include "flipcube.h"
  36. #include "dumb3d.h"
  37. #include "d3dtex.h"
  38.  
  39. /**************************************************************************
  40.   Global Variables
  41.  **************************************************************************/
  42.  
  43. char szAppName[]="Flip3D";
  44.  
  45. HINSTANCE  hInstApp;
  46. BOOL       fAppActive;
  47. BOOL       fAppPaused;
  48. HWND       hwndApp;
  49. HACCEL     hAccelApp;
  50. HFONT      AppFont;
  51. SIZE       ScreenSize;
  52.  
  53. /**************************************************************************
  54.   DirectDraw Globals
  55.  **************************************************************************/
  56.  
  57. IDirectDraw            *dd;
  58. IDirectDrawSurface     *FrontBuffer;
  59. IDirectDrawSurface     *BackBuffer;
  60. IDirectDrawPalette     *Palette;
  61. char                    DeviceName[128];
  62.  
  63. /**************************************************************************
  64.   Direct3D Globals
  65.  **************************************************************************/
  66.  
  67. IDirect3D2             *d3d;
  68. IDirect3DDevice2       *d3dDevice;
  69. IDirect3DViewport2     *d3dViewport;
  70. IDirect3DLight         *d3dLight;
  71. char                   *d3dName;
  72.  
  73. D3DCULL                 CullMode = D3DCULL_CCW;
  74.  
  75. D3DTexture              Texture;
  76.  
  77. D3DTEXTUREHANDLE TextureHandle      = 0;
  78. BOOL             TexturePerspective = TRUE;
  79. BOOL             TextureDither      = FALSE;
  80. D3DTEXTUREBLEND  TextureBlend       = D3DTBLEND_MODULATE;
  81. D3DMATERIALHANDLE hMat                = 0;
  82. LPDIRECT3DMATERIAL2 lpMat            =NULL;
  83.  
  84. /**************************************************************************
  85.   dumb 3D Globals
  86.  **************************************************************************/
  87.  
  88. //*** Cube vertices, normals, shades, and modeling transform
  89. static point_4 CubeVertices[8] =
  90. {
  91.   point_4( -10,  10, -10 ),
  92.   point_4( -10,  10,  10 ),
  93.   point_4(  10,  10,  10 ),
  94.   point_4(  10,  10, -10 ),
  95.   point_4(  10, -10, -10 ),
  96.   point_4(  10, -10,  10 ),
  97.   point_4( -10, -10,  10 ),
  98.   point_4( -10, -10, -10 )
  99. };
  100. static vector_4   CubeSurfaceNormals[6];
  101. static real       CubeSurfaceShades[6];
  102. static matrix_4x4 CubeTransform;
  103.  
  104. //*** Cube edges - ordered indices into the vertex array
  105. const int CubeFaces[6][4] =
  106. {
  107.   0, 1, 2, 3,
  108.   2, 1, 6, 5,
  109.   3, 2, 5, 4,
  110.   0, 3, 4, 7,
  111.   1, 0, 7, 6,
  112.   4, 5, 6, 7
  113. };
  114.  
  115. //*** Cube colors - one RGB color per surface
  116. const unsigned char CubeColors[6][3] =
  117. {
  118.   240,  20,  20,    // Unsaturated Red
  119.    20, 240,  20,    // Unsaturated Green
  120.    20,  20, 240,    // Unsaturated Blue
  121.   128,  64,   0,    // Brown
  122.   240,  20, 240,    // Unsaturated Magenta
  123.   240, 240,  20     // Unsaturated Yellow
  124. };
  125.  
  126. //*** Lighting
  127. vector_4   LightSourceDirection;
  128. const real AmbientLight = 0.2;
  129.  
  130. //*** Viewing and perspective
  131. static matrix_4x4  ViewPerspective;
  132. static point_4     Viewpoint(60, 60, 60);
  133. static vector_4    Up(0, 1, 0);
  134. static point_4     Origin;
  135.  
  136. //*** Interaction
  137. static POINT       Move;
  138. static POINT       Last;
  139.  
  140. /**************************************************************************
  141.    Internal function declarations
  142.  **************************************************************************/
  143. LONG  CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
  144. BOOL  AppIdle(void);
  145. void  ClearFrame(void);
  146. void  RenderFrame(void);
  147.  
  148. void  TransformCube(matrix_4x4 const &Transform);
  149.  
  150. /**************************************************************************
  151.  **************************************************************************/
  152. BOOL  ProjectAndDrawCube(IDirect3DDevice2 *pd3d, int XOffset, int YOffset);
  153.  
  154. /**************************************************************************
  155.  **************************************************************************/
  156. void BuildDeviceMenu(void);
  157. void BuildModeMenu(void);
  158. BOOL DDSetMode(int,int,int);
  159.  
  160. /**************************************************************************
  161.   AppAbout
  162.  
  163.   Description:
  164.     This function handles messages belonging to the "About" dialog box.
  165.   The only message that it looks for is WM_COMMAND, indicating the user
  166.   has pressed the "OK" button.
  167.  **************************************************************************/
  168.  
  169. BOOL FAR PASCAL AppAbout(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
  170. {
  171.   switch (msg)
  172.   {
  173.     case WM_COMMAND:
  174.       if (LOWORD(wParam) == IDOK)
  175.         EndDialog(hwnd, TRUE);
  176.       break;
  177.  
  178.     case WM_INITDIALOG:
  179.       return TRUE;
  180.   }
  181.   return FALSE;
  182. }
  183.  
  184. /**************************************************************************
  185.   DDTerm
  186.  **************************************************************************/
  187.  
  188. #define RELEASE(x) if (x) { x->Release(); x = NULL; }
  189.  
  190. void DDTerm(BOOL fAll=TRUE)
  191. {
  192.     RELEASE(d3dDevice);
  193.     RELEASE(d3dViewport);
  194.     RELEASE(d3dLight);
  195.  
  196.     RELEASE(BackBuffer);
  197.     RELEASE(FrontBuffer);
  198.     RELEASE(Palette);
  199.     RELEASE(lpMat);
  200.  
  201.     Texture.Release();
  202.  
  203.     if (fAll)
  204.     {
  205.         RELEASE(d3d);
  206.         RELEASE(dd);
  207.     }
  208. }
  209.  
  210. /**************************************************************************
  211.  * FindDeviceCallback
  212.  **************************************************************************/
  213.  
  214. BOOL CALLBACK FindDeviceCallback(GUID* lpGUID, LPSTR szName, LPSTR szDevice, LPVOID lParam)
  215. {
  216.     char ach[128];
  217.     char * szFind = (char *)lParam;
  218.  
  219.     wsprintf(ach,"%s (%s)",szName, szDevice);
  220.  
  221.     if (lstrcmpi(szFind, szDevice) == 0 || lstrcmpi(szFind, ach) == 0)
  222.     {
  223.         DirectDrawCreate(lpGUID, &dd, NULL);
  224.         return DDENUMRET_CANCEL;
  225.     }
  226.     return DDENUMRET_OK;
  227. }
  228.  
  229. /**************************************************************************
  230.   DDInit
  231.  
  232.   Description:
  233.     initialize all the DirectDraw/Direct3D specific stuff
  234.  **************************************************************************/
  235.  
  236. BOOL DDInit(char *szDevice=NULL)
  237. {
  238.     HRESULT err;
  239.  
  240.     DDTerm();
  241.  
  242.     if (szDevice && szDevice[0])
  243.         DirectDrawEnumerate(FindDeviceCallback, (LPVOID)szDevice);
  244.  
  245.     if (dd == NULL)
  246.     {
  247.         szDevice = NULL;
  248.         err = DirectDrawCreate(NULL, &dd, NULL);
  249.     }
  250.  
  251.     if (dd == NULL)
  252.     {
  253.         MessageBox(hwndApp, "This app requires DirectDraw", szAppName, MB_OK);
  254.         return FALSE;
  255.     }
  256.  
  257.     err = dd->SetCooperativeLevel(hwndApp,
  258.         DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
  259.  
  260.     if (err != DD_OK)
  261.         return FALSE;
  262.  
  263.     err = dd->QueryInterface(IID_IDirect3D2, (void**)&d3d);
  264.  
  265.     if (err != DD_OK)
  266.     {
  267.         MessageBox(hwndApp, "This app requires DirectX 5.0", szAppName, MB_OK);
  268.         return FALSE;
  269.     }
  270.     //
  271.     //Not imp
  272.     //
  273.     BuildModeMenu();
  274.  
  275.     if (!DDSetMode(640,480,16) &&
  276.         !DDSetMode(640,480,8))
  277.     {
  278.         return FALSE;
  279.     }
  280.  
  281.     char ach[128];
  282.     wsprintf(ach, "%s - %s %s", szAppName, szDevice ? szDevice : "DISPLAY", d3dName);
  283.     SetWindowText(hwndApp, ach);
  284.  
  285.     return TRUE;
  286. }
  287.  
  288. /**************************************************************************
  289.   Init3DState
  290.  **************************************************************************/
  291. BOOL Init3DState()
  292. {
  293.     D3DMATERIAL mat;
  294.     
  295.     /*
  296.      * Set default render state
  297.      */
  298.     d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, 0);
  299.     d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, 0);
  300.     d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT);
  301.  
  302.     /*
  303.      * load our single texture map from a resource
  304.      */
  305.     Texture.Load(d3dDevice, "Win95");
  306. //  Texture.Load(d3dDevice, "Checker");
  307.     TextureHandle = Texture.GetHandle();
  308.  
  309.     if (d3d->CreateMaterial(&lpMat, NULL) != D3D_OK) {
  310.         return FALSE;
  311.     }
  312.     memset(&mat, 0, sizeof(D3DMATERIAL));
  313.     mat.dwSize = sizeof(D3DMATERIAL);
  314.     mat.diffuse.r = (D3DVALUE)1.0;
  315.     mat.diffuse.g = (D3DVALUE)1.0;
  316.     mat.diffuse.b = (D3DVALUE)1.0;
  317.     mat.ambient.r = (D3DVALUE)1.0;
  318.     mat.ambient.g = (D3DVALUE)1.0;
  319.     mat.ambient.b = (D3DVALUE)1.0;
  320.     mat.specular.r = (D3DVALUE)1.0;
  321.     mat.specular.g = (D3DVALUE)1.0;
  322.     mat.specular.b = (D3DVALUE)1.0;
  323.     mat.power = (float)40.0;
  324.     mat.hTexture = TextureHandle;
  325.     mat.dwRampSize = 1;
  326.     lpMat->SetMaterial(&mat);
  327.     lpMat->GetHandle(d3dDevice, &hMat);
  328.  
  329.     return TRUE;
  330. }
  331.  
  332. /**************************************************************************
  333.   Init3D
  334.  **************************************************************************/
  335. BOOL Init3D()
  336. {
  337.     HRESULT err = E_FAIL;
  338.  
  339.     //
  340.     // create a Direct3D device.
  341.     // first try HAL, then RGB, RAMP
  342.     //
  343.     d3dName = "HAL";
  344.     err = d3d->CreateDevice(IID_IDirect3DHALDevice, BackBuffer, &d3dDevice);
  345.     
  346.     if (err != DD_OK)
  347.     {
  348.         d3dName = "RGB";
  349.         err = d3d->CreateDevice(IID_IDirect3DRGBDevice, BackBuffer, &d3dDevice);
  350.     }
  351.  
  352.     if (err != DD_OK)
  353.     {
  354.         d3dName = "RAMP";
  355.         err = d3d->CreateDevice(IID_IDirect3DRampDevice, BackBuffer, &d3dDevice);
  356.     }
  357.  
  358.     if (err != DD_OK)
  359.         return FALSE;
  360.  
  361.     //
  362.     // now make a Viewport
  363.     //
  364.     err = d3d->CreateViewport(&d3dViewport, NULL);
  365.  
  366.     if (err != DD_OK)
  367.         return FALSE;
  368.  
  369.     err = d3dDevice->AddViewport(d3dViewport);
  370.  
  371.     /*
  372.      * Setup the viewport for a reasonable viewing area
  373.      */
  374.     D3DVIEWPORT2 viewData;
  375.     memset(&viewData, 0, sizeof(D3DVIEWPORT2));
  376.     viewData.dwSize = sizeof(D3DVIEWPORT2);
  377.     viewData.dwX = 0;
  378.     viewData.dwY = 0;
  379.     viewData.dwWidth  = ScreenSize.cx;
  380.     viewData.dwHeight = ScreenSize.cy;
  381.     viewData.dvClipX = -1.0f;
  382.     viewData.dvClipWidth = 2.0f;
  383.     viewData.dvClipHeight = (D3DVALUE)(ScreenSize.cy * 2.0 / ScreenSize.cx);
  384.     viewData.dvClipY = viewData.dvClipHeight / 2.0f;
  385.     viewData.dvMinZ = 0.0f;
  386.     viewData.dvMaxZ = 1.0f;
  387.     err = d3dViewport->SetViewport2(&viewData);
  388.  
  389.     if (err != DD_OK)
  390.     return FALSE;
  391.  
  392.     err = d3dDevice->SetCurrentViewport(d3dViewport);
  393.  
  394.     if (err != DD_OK)
  395.         return FALSE;
  396.  
  397.     if (!Init3DState())
  398.     return FALSE;
  399.  
  400.     return TRUE;
  401. }
  402.  
  403. /**************************************************************************
  404.   DDSetMode
  405.  **************************************************************************/
  406.  
  407. BOOL DDSetMode(int width, int height, int bpp)
  408. {
  409.     HRESULT err;
  410.  
  411.     err = dd->SetDisplayMode(width, height, bpp);
  412.  
  413.     if (err != DD_OK)
  414.         return FALSE;
  415.  
  416.     ScreenSize.cx = width;
  417.     ScreenSize.cy = height;
  418.  
  419.     // get rid of any previous surfaces.
  420.     DDTerm(FALSE);
  421.  
  422.     //
  423.     // Create surfaces
  424.     //
  425.     // what we want is a double buffered surface in video memory
  426.     // so we try to create this first.
  427.     //
  428.     // if we cant get a double buffered surface, we try for a double
  429.     // buffered surface not being specific about video memory, we will
  430.     // get back a main-memory surface, that wont use HW page flipping
  431.     // but at least we run.
  432.     //
  433.     // NOTE you need to recreate the surfaces for a new display mode
  434.     // they wont work when/if the mode is changed.
  435.     //
  436.     DDSURFACEDESC ddsd;
  437.  
  438.     ZeroMemory(&ddsd, sizeof(ddsd));
  439.     ddsd.dwSize = sizeof(ddsd);
  440.     ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  441.     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
  442.                           DDSCAPS_FLIP |
  443.                           DDSCAPS_COMPLEX |
  444.                           DDSCAPS_3DDEVICE |
  445.                           DDSCAPS_VIDEOMEMORY;
  446. #ifdef TRIPLE_BUFFER
  447.     ddsd.dwBackBufferCount = 2;
  448. #else
  449.     ddsd.dwBackBufferCount = 1;
  450. #endif
  451.  
  452.     // try to get a triple/double buffered video memory surface.
  453.     err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  454.  
  455.     // try to get a double buffered video memory surface.
  456.     if (err != DD_OK && ddsd.dwBackBufferCount == 2)
  457.     {
  458.        ddsd.dwBackBufferCount = 1;
  459.        err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  460.     }
  461.  
  462.     if (err != DD_OK)
  463.     {
  464.         // settle for a main memory surface.
  465.         ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
  466.         err = dd->CreateSurface(&ddsd, &FrontBuffer, NULL);
  467.     }
  468.  
  469.     if (err != DD_OK)
  470.         return FALSE;
  471.  
  472.     // get a pointer to the back buffer
  473.     DDSCAPS caps;
  474.     caps.dwCaps = DDSCAPS_BACKBUFFER;
  475.     err = FrontBuffer->GetAttachedSurface(&caps, &BackBuffer);
  476.  
  477.     if (err != DD_OK)
  478.         return FALSE;
  479.  
  480.     // create a palette if we are in a paletized display mode.
  481.     //
  482.     // NOTE because we want to be able to show dialog boxs and
  483.     // use our menu, we leave the windows reserved colors as is
  484.     // so things dont look ugly.
  485.     //
  486.     // palette is setup like so:
  487.     //
  488.     //      10      windows system colors
  489.     //      64      red wash
  490.     //      64      grn wash
  491.     //      64      blu wash
  492.     //
  493.     if (bpp == 8)
  494.     {
  495.         PALETTEENTRY ape[256];
  496.  
  497.         // get the current windows colors.
  498.         GetPaletteEntries((HPALETTE)GetStockObject(DEFAULT_PALETTE), 0,  10, &ape[0]);
  499.         GetPaletteEntries((HPALETTE)GetStockObject(DEFAULT_PALETTE), 10, 10, &ape[246]);
  500.  
  501.         // make a red, grn, and blu wash for our cube.
  502.         for (int i=0; i<64; i++)
  503.         {
  504.             ape[10+64*0+i].peRed   = i * 255/63;
  505.             ape[10+64*0+i].peGreen = 0;
  506.             ape[10+64*0+i].peBlue  = 0;
  507.  
  508.             ape[10+64*1+i].peRed   = 0;
  509.             ape[10+64*1+i].peGreen = i * 255/63;
  510.             ape[10+64*1+i].peBlue  = 0;
  511.  
  512.             ape[10+64*2+i].peRed   = 0;
  513.             ape[10+64*2+i].peGreen = 0;
  514.             ape[10+64*2+i].peBlue  = i * 255/63;
  515.         }
  516.  
  517.         // create the palette.
  518.         err = dd->CreatePalette(DDPCAPS_8BIT, ape, &Palette, NULL);
  519.  
  520.         if (err == DD_OK)
  521.         {
  522.             FrontBuffer->SetPalette(Palette);
  523.             BackBuffer->SetPalette(Palette);
  524.         }
  525.     }
  526.  
  527.     if (!Init3D())
  528.         return FALSE;
  529.  
  530.     if (AppFont)
  531.         DeleteObject(AppFont);
  532.  
  533.     AppFont = CreateFont(width < 640 ? 24 : 48,
  534.         0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
  535.         ANSI_CHARSET,
  536.         OUT_DEFAULT_PRECIS,
  537.         CLIP_DEFAULT_PRECIS,
  538.         NONANTIALIASED_QUALITY,
  539.         VARIABLE_PITCH,
  540.         "Comic Sans MS");
  541.  
  542.     return TRUE;
  543. }
  544.  
  545. /**************************************************************************
  546.   BuildModeMenu
  547.  **************************************************************************/
  548. #define MAKEMENUITEMDATA(width, height, bpp) \
  549.         (width) | ((height) << 12) | ((bpp) << 24)
  550.  
  551. HRESULT CALLBACK BuildModeMenuCallback(LPDDSURFACEDESC pdds, LPVOID lParam)
  552. {
  553.     HMENU hmenu = (HMENU)lParam;
  554.     char ach[80];
  555.     int n;
  556.     int width  = pdds->dwWidth;
  557.     int height = pdds->dwHeight;
  558.     int bpp    = pdds->ddpfPixelFormat.dwRGBBitCount;
  559.  
  560.     n = GetMenuItemCount(hmenu);
  561.     wsprintf(ach,"%dx%dx%d",width,height,bpp);
  562.     AppendMenu(hmenu,MF_STRING,MENU_MODE+n,ach);
  563.  
  564.     MENUITEMINFO mii;
  565.  
  566.     // pack the mode info into a DWORD and set the extra item data.
  567.     mii.cbSize = sizeof(mii);
  568.     mii.fMask = MIIM_DATA;
  569.     mii.dwItemData = MAKEMENUITEMDATA(width, height, bpp);
  570.     SetMenuItemInfo(hmenu, MENU_MODE+n, MF_BYCOMMAND, &mii);
  571.  
  572.     //return S_TRUE to stop enuming modes, S_FALSE to continue
  573.     return S_FALSE;
  574. }
  575.  
  576. void BuildModeMenu()
  577. {
  578.     // Enumerate all posible display modes, and stick them in our menu.
  579.     // we use the extra item DWORD of a menu item to store the mode info
  580.  
  581.     DeleteMenu(GetMenu(hwndApp), 2, MF_BYPOSITION);
  582.  
  583.     HMENU hmenu = CreatePopupMenu();
  584.     dd->EnumDisplayModes(0,NULL,(LPVOID)hmenu,BuildModeMenuCallback);
  585.     AppendMenu(GetMenu(hwndApp),MF_POPUP,(UINT)hmenu,"&Modes");
  586. }
  587.  
  588. /**************************************************************************
  589.  * BuildDeviceMenu
  590.  **************************************************************************/
  591.  
  592. BOOL CALLBACK BuildDeviceMenuCallback(GUID* lpGUID, LPSTR szName, LPSTR szDevice, LPVOID lParam)
  593. {
  594.     HMENU hmenu = (HMENU)lParam;
  595.     char ach[80];
  596.     int n;
  597.  
  598.     n = GetMenuItemCount(hmenu);
  599.     wsprintf(ach,"%s (%s)",szName, szDevice);
  600.     AppendMenu(hmenu,MF_STRING,MENU_DEVICE+n,ach);
  601.     return DDENUMRET_OK;
  602. }
  603.  
  604. void BuildDeviceMenu()
  605. {
  606.     HMENU hmenu = CreatePopupMenu();
  607.     DirectDrawEnumerate(BuildDeviceMenuCallback, (LPVOID)hmenu);
  608.     AppendMenu(GetMenu(hwndApp),MF_POPUP,(UINT)hmenu,"&Device");
  609. }
  610.  
  611. /**************************************************************************
  612.   AppInit
  613.  
  614.   Description:
  615.     This is called when the application is first loaded. It initializes
  616.   all variables, registers the window class, and creates the main app
  617.   window.
  618.  **************************************************************************/
  619.  
  620. BOOL AppInit(HINSTANCE hInst,HINSTANCE hPrev,int sw,LPSTR szCmdLine)
  621. {
  622.   WNDCLASS cls;
  623.  
  624.   /* Save instance handle for DialogBoxes */
  625.   hInstApp = hInst;
  626.  
  627.   if (!hPrev)
  628.   {
  629.     //***  Register a class for the main application window
  630.     cls.hCursor        = LoadCursor(0,IDC_ARROW);
  631.  
  632.     //*** Just for fun, we'll draw our own spinning cube icon.
  633.     cls.hIcon          = LoadIcon(hInst, "AppIcon");
  634.     cls.lpszMenuName   = "AppMenu";
  635.     cls.lpszClassName  = szAppName;
  636.     cls.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);
  637.     cls.hInstance      = hInst;
  638.     cls.style          = CS_VREDRAW | CS_HREDRAW;
  639.     cls.lpfnWndProc    = (WNDPROC)AppWndProc;
  640.     cls.cbClsExtra     = 0;
  641.     cls.cbWndExtra     = 0;
  642.  
  643.     if (!RegisterClass(&cls))
  644.       return FALSE;
  645.   }
  646.  
  647.   hAccelApp = LoadAccelerators(hInst, "AppAccel");
  648.  
  649.   //*** Set and normalize the light source
  650.   LightSourceDirection = vector_4(50, 30, -15);
  651.   LightSourceDirection.Normalize();
  652.  
  653.   //*** Distance to view plane:
  654.   ViewPerspective.SetElement(3, 2, 1/300.0);
  655.   ViewPerspective.SetElement(3, 3, 0);
  656.  
  657.   //*** Viewport scaling - some arbitrary number like 3.5 will do
  658.   ViewPerspective.SetElement(0, 0, 3.5);
  659.   ViewPerspective.SetElement(1, 1, 3.5);
  660.  
  661.   //*** Calculate the initial normals and shades
  662.   TransformCube(CubeTransform);
  663.  
  664.   //*** Then generate an interesting rotation for the spin
  665.   CubeTransform.ConcatenateYRotation(6.0);
  666.   CubeTransform.ConcatenateXRotation(3.5);
  667.   CubeTransform.ConcatenateZRotation(2.0);
  668.  
  669.   hwndApp = CreateWindowEx(
  670.                   WS_EX_APPWINDOW,
  671.                   szAppName,           // Class name
  672.                   szAppName,           // Caption
  673.                   WS_POPUP |
  674.                   WS_SYSMENU |
  675.                   WS_CAPTION,
  676.           0, 0,            // Position
  677.           640,480,           // Size
  678.                   0,                   // Parent window (no parent)
  679.                   0,                   // use class menu
  680.                   hInst,               // handle to window instance
  681.                   0                    // no params to pass on
  682.                   );
  683.   ShowWindow(hwndApp,sw);
  684.   UpdateWindow(hwndApp);
  685.  
  686.   BuildDeviceMenu();
  687.  
  688.   //
  689.   // read the device name from WIN.INI so we come up on the last device
  690.   // that the user picked.
  691.   //
  692.   GetProfileString(szAppName, "Device", "", DeviceName, sizeof(DeviceName));
  693.  
  694.   if (!DDInit(DeviceName))
  695.       return FALSE;
  696.  
  697.   return TRUE;
  698. }
  699.  
  700. /**************************************************************************
  701.   WinMain
  702.  
  703.   Description:
  704.     The main procedure for the App.  After initializing, it just goes
  705.   into a message-processing loop until it gets a WM_QUIT message.
  706.  **************************************************************************/
  707.  
  708. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  709. {
  710.   MSG     msg;
  711.  
  712.   //*** Call initialization procedure
  713.   if (!AppInit(hInst,hPrev,sw,szCmdLine))
  714.     return FALSE;
  715.  
  716.   //*** Polling messages from event queue until quit
  717.   for (;;)
  718.   {
  719.     if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  720.     {
  721.       if (msg.message == WM_QUIT)
  722.         break;
  723.  
  724.       if (!hwndApp || !TranslateAccelerator(hwndApp, hAccelApp, &msg))
  725.       {
  726.         TranslateMessage(&msg);
  727.         DispatchMessage(&msg);
  728.       }
  729.     }
  730.     else
  731.     {
  732.       if (AppIdle())
  733.         WaitMessage();
  734.     }
  735.   }
  736.  
  737.   DDTerm();
  738.   return msg.wParam;
  739. }
  740.  
  741. /**************************************************************************
  742.   AppPause
  743.  
  744.  **************************************************************************/
  745.  
  746. void AppPause(BOOL f)
  747. {
  748.     if (f)
  749.     {
  750.         DDSCAPS caps;
  751.         FrontBuffer->GetCaps(&caps);
  752.  
  753.         // if we are in ModeX go back to a windows mode
  754.         // so we can see the menu or dialog box.
  755.  
  756.         if (caps.dwCaps & DDSCAPS_MODEX)
  757.         {
  758.             DDSetMode(640,480,8);
  759.         }
  760.  
  761.         fAppPaused = TRUE;
  762.         dd->FlipToGDISurface();
  763.         DrawMenuBar(hwndApp);
  764.         RedrawWindow(hwndApp, NULL, NULL, RDW_FRAME);
  765.     }
  766.     else
  767.     {
  768.         fAppPaused = FALSE;
  769.     }
  770. }
  771.  
  772. /**************************************************************************
  773.   MagnifyCube
  774.  
  775.   Description:
  776.     Magnify the cube the indicated number of times.  A negative number
  777.     makes it smaller.
  778.  **************************************************************************/
  779.  
  780. void MagnifyCube(double times)
  781. {
  782.     matrix_4x4 m;
  783.     double factor = pow(1.5, times);
  784.     m.SetElement(0,0,factor);
  785.     m.SetElement(1,1,factor);
  786.     m.SetElement(2,2,factor);
  787.     TransformCube(m);
  788. }
  789.  
  790. /**************************************************************************
  791.   AppIdle
  792.  
  793.   return TRUE if the app is idle
  794.   return FALSE if the app is not idle.
  795.  
  796.   Description:
  797.  **************************************************************************/
  798.  
  799. BOOL AppIdle()
  800. {
  801.   //*** Spin while the app is active, lbutton is up, and spinning is on.
  802.  
  803.   //*** Spin while the app is iconized.
  804.   if (fAppActive && !fAppPaused)
  805.   {
  806.     //*** If the app is active, spin the cube and redraw
  807.     if (GetAsyncKeyState(VK_LBUTTON) < 0)
  808.     {
  809.         if(Move.x || Move.y)
  810.         {
  811.             matrix_4x4 Movement;
  812.             Movement.ConcatenateYRotation(Move.x);
  813.             Movement.ConcatenateXRotation(Move.y);
  814.             Move.x = Move.y = 0;
  815.             TransformCube(Movement);
  816.         }
  817.     }
  818.     else
  819.     {
  820.         TransformCube(CubeTransform);
  821.     }
  822.     RenderFrame();
  823.     return FALSE;
  824.   }
  825.   else
  826.   {
  827.     //*** Don't do anything when not the active app
  828.     return TRUE;
  829.   }
  830. }
  831.  
  832. /**************************************************************************
  833.   RenderFrame
  834.  
  835.   render the frame into the back buffer and do a page flip.
  836.  
  837.   things to NOTE:
  838.  
  839.     we use the blter to clear the backbuffer, this usualy is a big
  840.     win blters are real fast.
  841.  
  842.     we use GDI to draw the frame rate, and info text
  843.  
  844.  **************************************************************************/
  845.  
  846. int FrameRate;
  847. int FrameCount;
  848. int FrameCount0;
  849. DWORD FrameTime;
  850. DWORD FrameTime0;
  851.  
  852. void RenderFrame()
  853. {
  854.   HDC hdc;
  855.  
  856.   //*** always need to handle DDERR_SURFACELOST, this will happen
  857.   //*** when we get switched away from.
  858.  
  859.   if (FrontBuffer->IsLost() == DDERR_SURFACELOST)
  860.   {
  861.      FrontBuffer->Restore();
  862.      Texture.Restore();
  863.   }
  864.  
  865.   //*** use the blter to do a color fill to clear the back buffer
  866.  
  867.   DDBLTFX ddbltfx;
  868.   ddbltfx.dwSize = sizeof(ddbltfx);
  869.   ddbltfx.dwFillColor = 0;
  870.   BackBuffer->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&ddbltfx);
  871.  
  872.   ProjectAndDrawCube(d3dDevice, ScreenSize.cx/2, ScreenSize.cy/2);
  873.  
  874.   if (BackBuffer->GetDC(&hdc) == DD_OK)
  875.   {
  876.     //*** draw stats, like frame number and frame rate
  877.  
  878.     char ach[128];
  879.     int len;
  880.     static char szHelp[] = "F10=Menu F7=Smaller F8=Larger";
  881.  
  882.     SetBkMode(hdc, TRANSPARENT);
  883.     SelectObject(hdc, AppFont);
  884.  
  885.     len = wsprintf(ach, "FPS %02d Frame %05d (%s)", FrameRate, FrameCount, d3dName);
  886.  
  887.     SetTextColor(hdc, RGB(255, 255, 0));
  888.     TextOut(hdc, 0, 0, ach, len);
  889.     TextOut(hdc, 0, ScreenSize.cy-(ScreenSize.cx<640 ? 24:48),szHelp,sizeof(szHelp)-1);
  890.  
  891.     BackBuffer->ReleaseDC(hdc);
  892.   }
  893.  
  894.   //*** we have rendered the backbuffer, call flip so we can see it
  895.   FrontBuffer->Flip(NULL, DDFLIP_WAIT);
  896.  
  897.   FrameCount++;
  898.   FrameTime = timeGetTime();
  899.  
  900.   if (FrameTime - FrameTime0 > 1000)
  901.   {
  902.     FrameRate = (FrameCount - FrameCount0) * 1000 / (FrameTime - FrameTime0);
  903.     FrameTime0 = FrameTime;
  904.     FrameCount0 = FrameCount;
  905.   }
  906. }
  907.  
  908. /**************************************************************************
  909.   AppInitMenuPopup
  910.  
  911.     If it's the Cube menu, then check or uncheck the GDI item accordingly.
  912.  
  913.     If it's the mode list popup, then add a MENUBARBREAK as necessary.
  914.  
  915.  **************************************************************************/
  916.  
  917. void AppInitMenuPopup(HWND hwnd, HMENU hmenu, UINT uPos, BOOL fSys)
  918. {
  919.     if (fSys) return;       /* Don't mess with the sysmenu */
  920.  
  921.     switch (uPos) {
  922.     case 0:                 /* Cube menu */
  923.         CheckMenuItem(hmenu, MENU_CULL_NONE, CullMode == D3DCULL_NONE ? MF_CHECKED : MF_UNCHECKED);
  924.         CheckMenuItem(hmenu, MENU_CULL_CW,   CullMode == D3DCULL_CW   ? MF_CHECKED : MF_UNCHECKED);
  925.         CheckMenuItem(hmenu, MENU_CULL_CCW,  CullMode == D3DCULL_CCW  ? MF_CHECKED : MF_UNCHECKED);
  926.  
  927.         CheckMenuItem(hmenu, MENU_TEXTURE,    TextureHandle ? MF_CHECKED : MF_UNCHECKED);
  928.         CheckMenuItem(hmenu, MENU_DITHER,     TextureDither ? MF_CHECKED : MF_UNCHECKED);
  929.         CheckMenuItem(hmenu, MENU_PERSPECTIVE,TexturePerspective ? MF_CHECKED : MF_UNCHECKED);
  930.         CheckMenuItem(hmenu, MENU_MODULATE,   TextureBlend == D3DTBLEND_MODULATE ? MF_CHECKED : MF_UNCHECKED);
  931.         CheckMenuItem(hmenu, MENU_COPY,       TextureBlend == D3DTBLEND_COPY ? MF_CHECKED : MF_UNCHECKED);
  932.         CheckMenuItem(hmenu, MENU_DECAL,      TextureBlend == D3DTBLEND_DECAL ? MF_CHECKED : MF_UNCHECKED);
  933.         break;
  934.  
  935.     case 1:                 /* Device menu */
  936.     case 2:                 /* Mode menu */
  937.         /*
  938.          *  Compute how many menu items fit on the screen.
  939.          *
  940.          *  Note that we use ScreenSize instead of SM_CYSCREEN.
  941.          *  This allows us to do the right thing in the face of
  942.          *  multiple monitors.  (ScreenSize is the size of our
  943.          *  monitor.)
  944.          */
  945.         int cmi;
  946.  
  947.         cmi = GetMenuItemCount(hmenu);
  948.         if (cmi) {
  949.  
  950.             RECT rcClient;
  951.             GetClientRect(hwnd, &rcClient);
  952.  
  953.             RECT rc;
  954.             GetMenuItemRect(hwnd, hmenu, 0, &rc);
  955.  
  956.             int dyMenuItem = rc.bottom - rc.top;
  957.  
  958.             /*
  959.              *  Aargh.  If the menu has never appeared yet, USER
  960.              *  returns an empty rectangle.  DON'T DIVIDE BY ZERO!
  961.              *
  962.              *  In such case, we use the height of the menu bar.
  963.              *  Not perfect, but close enough.  And it happens only
  964.              *  once.
  965.              */
  966.             if (dyMenuItem == 0) {
  967.                 dyMenuItem = GetSystemMetrics(SM_CYMENU);
  968.             }
  969.  
  970.             /*
  971.              *  Aargh.  You can't change MF_MENUBARBREAK without
  972.              *  also changing the text, so we have to get the
  973.              *  old text and set it back.
  974.              *
  975.              *  While we're here, we may as well put a check-box
  976.              *  next to the item that matches our current screen res.
  977.              */
  978.  
  979.             DDSURFACEDESC ddsd;
  980.             ZeroMemory(&ddsd, sizeof(ddsd));
  981.             ddsd.dwSize = sizeof(ddsd);
  982.             FrontBuffer->GetSurfaceDesc(&ddsd);
  983.             int width  = ddsd.dwWidth;
  984.             int height = ddsd.dwHeight;
  985.             int bpp    = ddsd.ddpfPixelFormat.dwRGBBitCount;
  986.             DWORD dwItemDataMatch = MAKEMENUITEMDATA(width, height, bpp);
  987.  
  988.             MENUITEMINFO mii;
  989.             TCHAR tsz[80];
  990.  
  991.             mii.cbSize = sizeof(mii);
  992.             mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_STATE;
  993.             mii.dwTypeData = tsz;
  994.  
  995.             /*
  996.              *  Compute the number of MI's that fit in our client area.
  997.              *  Note: Client area, not screen size.  This ensures that
  998.              *  the menu pops in a reasonable location.
  999.              */
  1000.  
  1001.             int cmiScreen = rcClient.bottom / dyMenuItem;
  1002.  
  1003.             for (int imi = 0; imi < cmi; imi++) {
  1004.                 mii.cch = sizeof(tsz) / sizeof(tsz[0]);
  1005.                 if (GetMenuItemInfo(hmenu, imi, MF_BYPOSITION, &mii)) {
  1006.                     if (imi > 0 && imi % cmiScreen == 0) {
  1007.                         mii.fType |= MFT_MENUBARBREAK;
  1008.                     } else {
  1009.                         mii.fType &= ~MFT_MENUBARBREAK;
  1010.                     }
  1011.  
  1012.                     if (mii.dwItemData == dwItemDataMatch) {
  1013.                         mii.fState |= MFS_CHECKED;
  1014.                     } else {
  1015.                         mii.fState &= ~MFS_CHECKED;
  1016.                     }
  1017.  
  1018.                     SetMenuItemInfo(hmenu, imi, MF_BYPOSITION, &mii);
  1019.                 }
  1020.             }
  1021.  
  1022.         }
  1023.         break;
  1024.     }
  1025.  
  1026. }
  1027.  
  1028. /**************************************************************************
  1029.   AppWndProc
  1030.  
  1031.   Description:
  1032.     Main window proc. Standard Windows fare.
  1033.  **************************************************************************/
  1034.  
  1035. LONG CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
  1036. {
  1037.   switch (msg)
  1038.   {
  1039.     case WM_CREATE:
  1040.       break;
  1041.  
  1042.     case WM_ACTIVATEAPP:
  1043.       //*** Keep track of whether or not the app is in the foreground
  1044.       fAppActive = (BOOL)wParam;
  1045.       break;
  1046.  
  1047.     case WM_SETCURSOR:
  1048.       if (fAppActive && !fAppPaused && BackBuffer)
  1049.       {
  1050.         SetCursor(NULL);
  1051.         return 1;
  1052.       }
  1053.       break;
  1054.  
  1055.     case WM_ENTERMENULOOP:
  1056.       AppPause(TRUE);
  1057.       break;
  1058.  
  1059.     case WM_EXITMENULOOP:
  1060.       AppPause(FALSE);
  1061.       break;
  1062.  
  1063.     case WM_INITMENUPOPUP:
  1064.         AppInitMenuPopup(hwnd, (HMENU)wParam,
  1065.                          (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam));
  1066.         break;
  1067.  
  1068.     case WM_COMMAND:
  1069.       switch(LOWORD(wParam))
  1070.       {
  1071.         case MENU_ABOUT:
  1072.           AppPause(TRUE);
  1073.           DialogBox(hInstApp, "AppAbout", hwnd, (DLGPROC)AppAbout);
  1074.           AppPause(FALSE);
  1075.           break;
  1076.  
  1077.         case MENU_EXIT:
  1078.           PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1079.           break;
  1080.  
  1081.         case MENU_LARGER:
  1082.           MagnifyCube(+1.0);
  1083.           break;
  1084.  
  1085.         case MENU_SMALLER:
  1086.           MagnifyCube(-1.0);
  1087.           break;
  1088.  
  1089.         case MENU_TEXTURE:
  1090.           TextureHandle = TextureHandle ? 0 : Texture.GetHandle();
  1091.           break;
  1092.  
  1093.         case MENU_PERSPECTIVE:  TexturePerspective = !TexturePerspective; break;
  1094.         case MENU_DITHER:       TextureDither      = !TextureDither; break;
  1095.  
  1096.         case MENU_MODULATE:     TextureBlend = D3DTBLEND_MODULATE; break;
  1097.         case MENU_COPY:         TextureBlend = D3DTBLEND_COPY;     break;
  1098.         case MENU_DECAL:        TextureBlend = D3DTBLEND_DECAL;    break;
  1099.  
  1100.         case MENU_CULL_NONE:    CullMode = D3DCULL_NONE; break;
  1101.         case MENU_CULL_CW:      CullMode = D3DCULL_CW;   break;
  1102.         case MENU_CULL_CCW:     CullMode = D3DCULL_CCW;  break;
  1103.       }
  1104.       if (LOWORD(wParam) >= MENU_DEVICE && LOWORD(wParam) < MENU_DEVICE+100)
  1105.       {
  1106.         GetMenuString(GetMenu(hwnd), LOWORD(wParam), DeviceName, sizeof(DeviceName), MF_BYCOMMAND);
  1107.         WriteProfileString(szAppName, "Device", DeviceName);
  1108.         DDInit(DeviceName);
  1109.       }
  1110.       if (LOWORD(wParam) >= MENU_MODE && LOWORD(wParam) < MENU_MODE+100)
  1111.       {
  1112.         MENUITEMINFO mii;
  1113.  
  1114.         mii.cbSize = sizeof(mii);
  1115.         mii.fMask = MIIM_DATA;
  1116.         GetMenuItemInfo(GetMenu(hwnd), LOWORD(wParam), MF_BYCOMMAND, &mii);
  1117.  
  1118.         DDSetMode(
  1119.             (mii.dwItemData >> 0)  & 0xFFF,
  1120.             (mii.dwItemData >> 12) & 0xFFF,
  1121.             (mii.dwItemData >> 24) & 0x0FF);
  1122.       }
  1123.       return 0L;
  1124.  
  1125.     case WM_DESTROY:
  1126.       hwndApp = NULL;
  1127.       PostQuitMessage(0);
  1128.       break;
  1129.  
  1130.     case WM_PAINT:
  1131.       break;
  1132.  
  1133. #if 0   // fullscreen apps dont need to do this!
  1134.         // and using SM_CYSCREEN is incorrect for other devices.
  1135.     case WM_MOVE:
  1136.     case WM_SIZE:
  1137.     case WM_DISPLAYCHANGE:
  1138.       if (fAppActive && !IsIconic(hwnd))
  1139.       {
  1140.     RECT Rect;
  1141.     SetRect(&Rect, 0, GetSystemMetrics(SM_CYCAPTION), GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
  1142.         AdjustWindowRectEx(&Rect, WS_POPUP | WS_CAPTION, FALSE, 0);
  1143.         SetWindowPos(hwnd, NULL, Rect.left, Rect.top, Rect.right-Rect.left, Rect.bottom-Rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
  1144.       }
  1145.       break;
  1146. #endif
  1147.  
  1148.     case WM_LBUTTONDOWN:
  1149.       //*** Get the start location for mouse rotations
  1150.       Last.x = LOWORD(lParam);
  1151.       Last.y = HIWORD(lParam);
  1152.       break;
  1153.  
  1154.     case WM_MOUSEMOVE:
  1155.       //*** While the mouse button is down, keep track of movement
  1156.       //*** to update the eye position
  1157.       if(GetKeyState(VK_LBUTTON) < 0)
  1158.       {
  1159.         Move.x = (int)LOWORD(lParam) - Last.x;
  1160.         Move.y = (int)HIWORD(lParam) - Last.y;
  1161.         Last.x = LOWORD(lParam);
  1162.         Last.y = HIWORD(lParam);
  1163.       }
  1164.       break;
  1165.   }
  1166.  
  1167.   return DefWindowProc(hwnd,msg,wParam,lParam);
  1168. }
  1169.  
  1170. /**************************************************************************
  1171.   TransformCube
  1172.  
  1173.   Description:
  1174.     Transforms the cube vertices by the current rotation matrix.
  1175.     Recalculates normals and flat shade values for the
  1176.   directional light source.
  1177.  **************************************************************************/
  1178.  
  1179. void TransformCube(matrix_4x4 const &Transform)
  1180. {
  1181.   int i;
  1182.  
  1183.   //*** Transform the cube by the matrix
  1184.   for (i = 0; i < 8; ++i)
  1185.     CubeVertices[i] = Transform * CubeVertices[i];
  1186.  
  1187.   //*** Recalculate normals and shades
  1188.   for (i = 0; i < 6; ++i)
  1189.   {
  1190.     //*** Normals are perpendicular to two edges of the cube
  1191.     vector_4 Edge1, Edge2;
  1192.     Edge1 = CubeVertices[CubeFaces[i][1]] - CubeVertices[CubeFaces[i][0]];
  1193.     Edge2 = CubeVertices[CubeFaces[i][3]] - CubeVertices[CubeFaces[i][0]];
  1194.     CubeSurfaceNormals[i] = CrossProduct(Edge1, Edge2);
  1195.     CubeSurfaceNormals[i].Normalize();
  1196.  
  1197.     //*** Cosine shading based on the surface normal, clamped to [0, 1]
  1198.     real Shade = DotProduct(CubeSurfaceNormals[i], LightSourceDirection);
  1199.  
  1200.     if (CullMode != D3DCULL_CCW)
  1201.     {
  1202.         if (Shade < 0) Shade = -Shade;
  1203.     }
  1204.  
  1205.     Shade = Shade + AmbientLight;
  1206.     if (Shade < 0) Shade = 0;
  1207.     else if (Shade > 1.0) Shade = 1.0;
  1208.     CubeSurfaceShades[i] = Shade;
  1209.   }
  1210. }
  1211.  
  1212. /**************************************************************************
  1213.   ProjectAndDrawCube
  1214.  
  1215.   Description:
  1216.     Projects the cube vertices for the current viewpoint then culls
  1217.   in screen space and draws into the DC via GDI.
  1218.  **************************************************************************/
  1219.  
  1220. BOOL ProjectAndDrawCube(IDirect3DDevice2 *dev, int XOffset, int YOffset)
  1221. {
  1222.   HRESULT err;
  1223.  
  1224.   //*** Create a viewing transform for the current eye position
  1225.   vector_4 ViewDirection = Origin - Viewpoint;
  1226.   ViewDirection.Normalize();
  1227.   view_transform View(Viewpoint, ViewDirection, Up);
  1228.  
  1229.   //*** Transform and project the vertices into screen space
  1230.   int i;
  1231.   float aScreenVerticesX[8];
  1232.   float aScreenVerticesY[8];
  1233.   float aScreenVerticesZ[8];
  1234.   float aScreenVerticesW[8];
  1235.  
  1236.   for (i = 0; i < 8; ++i)
  1237.   {
  1238.     point_4 Temp = View * CubeVertices[i];
  1239.     Temp = ViewPerspective * Temp;
  1240.     Temp.Homogenize();
  1241.  
  1242.     aScreenVerticesX[i] = (float)Temp.GetX() + XOffset;
  1243.     aScreenVerticesY[i] = (float)Temp.GetY() + YOffset;
  1244.     aScreenVerticesZ[i] = (float)Temp.GetZ();
  1245.     aScreenVerticesW[i] = (float)Temp.GetW();
  1246.  
  1247.   }
  1248.  
  1249.   err = dev->BeginScene();
  1250.  
  1251.   //
  1252.   // NOTE NOTE NOTE NOTE NOTE
  1253.   // dont be as stupid as this sample, dont change the render
  1254.   // state every frame unless you need to.
  1255.   //
  1256.   dev->SetLightState(D3DLIGHTSTATE_MATERIAL,            hMat);
  1257.   dev->SetRenderState(D3DRENDERSTATE_CULLMODE,           CullMode);
  1258.   dev->SetRenderState(D3DRENDERSTATE_DITHERENABLE,       TextureDither);
  1259.   dev->SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE, TexturePerspective);
  1260.   dev->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,    TextureBlend);
  1261.   dev->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,      TextureHandle);
  1262.  
  1263.   for (i = 0; i < 6; ++i)
  1264.   {
  1265.     //*** Get the shading colors
  1266.  
  1267.     int Red, Green, Blue;
  1268.  
  1269.     Red   = (int)(CubeColors[i][0] * CubeSurfaceShades[i]);
  1270.     Green = (int)(CubeColors[i][1] * CubeSurfaceShades[i]);
  1271.     Blue  = (int)(CubeColors[i][2] * CubeSurfaceShades[i]);
  1272.  
  1273.     //*** Collect the correct points in an array
  1274.     D3DTLVERTEX TLVertices[6];
  1275.  
  1276.     for (int j = 0; j < 4; ++j)
  1277.     {
  1278.         TLVertices[j].sx = aScreenVerticesX[CubeFaces[i][j]];
  1279.         TLVertices[j].sy = aScreenVerticesY[CubeFaces[i][j]];
  1280.         TLVertices[j].sz = aScreenVerticesZ[CubeFaces[i][j]];
  1281.         TLVertices[j].rhw = (float)(1.0 / aScreenVerticesW[CubeFaces[i][j]]);
  1282.         TLVertices[j].color = RGB_MAKE(Red, Green, Blue);
  1283.         TLVertices[j].specular = RGB_MAKE(0,0,0);
  1284.     }
  1285.  
  1286.     TLVertices[0].tu = 0.0f; TLVertices[0].tv = 0.0f;
  1287.     TLVertices[1].tu = 1.0f; TLVertices[1].tv = 0.0f;
  1288.     TLVertices[2].tu = 1.0f; TLVertices[2].tv = 1.0f;
  1289.     TLVertices[3].tu = 0.0f; TLVertices[3].tv = 1.0f;
  1290.  
  1291.     //*** Use DrawPrim to draw the face
  1292.     err = dev->DrawPrimitive(D3DPT_TRIANGLEFAN, D3DVT_TLVERTEX, TLVertices, 4, D3DDP_WAIT);
  1293.   }
  1294.  
  1295.   err = dev->EndScene();
  1296.  
  1297.   return TRUE;
  1298. }
  1299.