/*===========================================================================
 *
 * D3DApp.CPP - Dave Humphrey (uesp@m0use.net), 30 October 2000
 *
 *=========================================================================*/

	/* Include Files */
#include "d3dapp.h"


#undef  __FUNC__
#define __FUNC__ "CD3DApp::CD3DApp()"
/*===========================================================================
 *
 * Class CD3DApp Constructor
 *
 *=========================================================================*/
CD3DApp::CD3DApp() {
  QuitMain = FALSE;
  Initialized = FALSE;
  Minimized = FALSE;
  ResizingDisabled = FALSE;
  Dithering = FALSE;
  DisplayFrameRate = TRUE;
  InSurfaceMode = FALSE;
  AntiAlias = TRUE;
  Activated = TRUE;
  AllowMovement = TRUE;
  AllowSelectObject = TRUE;
  CallBackOnSelectObject = TRUE;
  HasSelectedObject = FALSE;
  FollowSurface = TRUE;
  BitsPerPixel = 0;
  CameraHeight = D3DVAL(50.0);
    
  RenderQuality = D3DRMLIGHT_ON | D3DRMFILL_SOLID | D3DRMSHADE_GOURAUD;
  TextureQuality = D3DRMTEXTURE_NEAREST;

  MovementSpeed = D3DVAL(5.0);
  RotationSpeed = D3DVAL(3.0);
  FrameRate = D3DVAL(0.0);
  ViewBackLength = D3DVAL(50000.0);

  pAcceleratorName = NULL;
  pIconName = NULL;
  pWindowCaption = NULL;
  pMenuName = NULL;
  pClassName = NULL;
  pCurrentSurfaceMode = NULL;

	/* Initialize handles to NULL */
  hMainWindow = NULL;
  hInstance = NULL;
  hAccelerator = NULL;

	/* Initialize the counter variables */
  UsePerformanceCounter = QueryPerformanceFrequency(&PerformanceFreq);
  LastRenderTick = 0;
  LastRenderCounter.QuadPart = 0;

	/* Callback functions */
  pWindowProc = NULL;
  pOnSelectObjectCallBack = NULL;
  pBuildScene = NULL;
  
	/* Direct3D objects */
  pSelectedFace = NULL;
  pSelectedVisual = NULL;
  pDirect3D = NULL;
  pDDClipper = NULL;
  pDevice = NULL;
  pView = NULL;
  pScene = NULL;
  pCamera = NULL;

	/* Exclusive mode specific variables */
  #if D3D_EXCLUSIVE_MODE
    pPrimarySurface = NULL;	
    pDirectDraw = NULL;
    pBackSurface = NULL;
    pDirectDraw1 = NULL;
  #endif
 }
/*===========================================================================
 *		End of Class CD3DApp Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::Destroy()"
/*===========================================================================
 *
 * Class CD3DApp Destructor
 *
 *=========================================================================*/
void CD3DApp::Destroy() {
  
	/* Unallocate memory */
  DestroyPointer(pAcceleratorName);
  DestroyPointer(pIconName);
  DestroyPointer(pMenuName);
  DestroyPointer(pClassName);
  DestroyPointer(pWindowCaption);

	/* Release D3D objects as required */
  RELEASE(pSelectedFace);
  RELEASE(pSelectedVisual);
  RELEASE(pScene);
  RELEASE(pCamera);
  RELEASE(pView);
  RELEASE(pDevice);
  RELEASE(pDirect3D);
  RELEASE(pDDClipper);

  		/* Exclusive mode specific variables */
  #if D3D_EXCLUSIVE_MODE
    RELEASE(pBackSurface);
    RELEASE(pDDSurface);
    RELEASE(pDirectDraw);
  #endif

		/* Reset the global Direct3D object */
  SetD3DRM(NULL);

  QuitMain = TRUE;
  Initialized = FALSE;
  Minimized = FALSE;
  HasSelectedObject = FALSE;
  InSurfaceMode = FALSE;
  LastRenderTick = 0;
  LastRenderCounter.QuadPart = 0;
  FrameRate = D3DVAL(0.0);
  pWindowProc = NULL;
  pBuildScene = NULL;
  pCurrentSurfaceMode = NULL;
  hMainWindow = NULL;
  hInstance   = NULL;

	/* Accelerator tables loaded with LoadAccelerators() are freed
	 * auotmatically when the program terminates. */
  hAccelerator = NULL;	
 }
/*===========================================================================
 *		End of Class CD3DApp Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateCameraFrame()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateCamereaFrame (void);
 *
 * Creates the camera frame which will be the viewport of the user into the
 * 3D world.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateCameraFrame (void) {
  HRESULT Result;

	/* Assert valid objects */
  ASSERT(pDirect3D != NULL);		/* Need a valid Direct3D and scene objects */
  ASSERT(pScene    != NULL);	
  ASSERT(pCamera   == NULL);		/* Don't want to recreate a valid camera */

	/* Create the camera frame */
  Result = pDirect3D->CreateFrame(pScene, &pCamera);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the camera frame!");
    return (FALSE);
   }

	/* Set the initial position the camera in the frame */
  Result = pCamera->SetPosition(pScene, D3DVAL(0.0), D3DVAL(2.0), D3DVAL(0.0));

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to position the camera in the scene!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateCameraFrame()
 *=========================================================================*/


	/* Exclusive mode specific mode */
#if D3D_EXCLUSIVE_MODE

#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateD3DExclusiveMode()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateD3DExclusiveMode (void);
 *
 * Creates and initializes exclusive mode.  Only valid if the macro
 * D3D_EXCLUSIVE_MODE is defined as TRUE.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateD3DExclusiveMode (void) {
  HRESULT   Result;

	/* Assert valid objects */
  ASSERT(pDirectDraw == NULL);		/* Don't recreate over a valid object */
  ASSERT(hMainWindow != NULL);		/* Need a window handle for the application */

	/* Create the DirectDraw object */
  Result = CreateDirectDrawObject(&pDirectDraw);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the DirectDraw object!");
    return (FALSE);
   }
	
	/* Set the DirectDraw coorperation to exclusive and fullscreen */
  Result = pDirectDraw->SetCooperativeLevel(hMainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to initialize the DirectDraw object!");
    return (FALSE);
   }

	/* Change to the desired display mode */
	/* TODO: This should allow for some mode selection including auto-selection */
  Result = pDirectDraw->SetDisplayMode(640, 480, 16, 0, 0);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the DirectDraw display mode!");
    return (FALSE);
   }

	/* Create the exclusive mode primary/back surfaces */
  return (CreateExModeSurfaces());
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateD3DExclusiveMode()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateExModeSurfaces()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateExModeSurfaces (void);
 *
 * Creates and initializes primary and back buffer surfaces required for
 * page flipping in exclusive mode.  Only valid if the macro
 * D3D_EXCLUSIVE_MODE is defined as TRUE.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateExModeSurfaces (void) {
  HRESULT   Result;
  ddscaps_t ddSurfaceCaps; 
  ddsdesc_t ddSurfaceDesc;

	/* Assert valid objects */
  ASSERT(pDirectDraw     != NULL);		/* Need a valid DirectDraw object */
  ASSERT(pPrimarySurcace == NULL);		/* Don't recreate over valid objects */
  ASSERT(pBackSurface    == NULL);

	/* Setup the primary surface description */	
  memset(&ddSurfaceDesc, 0, sizeof(ddSurfaceDesc));
  ddSurfaceDesc.dwSize = sizeof(ddSurfaceDesc);
  ddSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  ddSurfaceDesc.dwWidth = 640;
  ddSurfaceDesc.dwHeight = 480;
  ddSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
  ddSurfaceDesc.dwBackBufferCount = 1;
  Result = pDirectDraw->CreateSurface(&ddSurfaceDesc, &pPrimarySurface, NULL);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the DirectDraw primary surface!");
    return (FALSE);
   }
    
	/* Get the back buffer surface for later use */
  ddSurfaceCaps.dwCaps = DDSCAPS_BACKBUFFER;
  Result = pPrimarySurface->GetAttachedSurface(&ddSurfaceCaps, &pBackSurface);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get the back buffer surface!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateExModeSurfaces()
 *=========================================================================*/

#endif /* End of if D3D_EXCLUSIVE_MODE */


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateDevice()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateDevice (void);
 *
 * Creates the Direct3D device from the current Direct3D object and the
 * given width/height of the window (if not in exclusive mode).  Returns 
 * FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateDevice (void) {
    
	/* Assert valid objects */
  ASSERT(hMainWindow != NULL);
 
	/* Create the D3DRM device depending on whether exclusive mode is used */
  #if D3D_EXCLUSIVE_MODE
    return (CreateDevice(0, 0)); 

  #else
    RECT    WindowRect;
    boolean Result;

		/* Get the size of the application window */
    Result = GetClientRect(hMainWindow, &WindowRect);
 
    if (!Result) {
      SET_WIN_ERROR2("Failed to retrieve the size of the window!");
      return (FALSE);
     }

    return (CreateDevice(WindowRect.right, WindowRect.bottom)); 
  #endif

 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateDevice()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateDevice()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateDevice (Width, Height);
 *
 * Creates the Direct3D device from the current Direct3D object and the
 * given width/height (if not in exclusive mode).  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateDevice (const int Width, const int Height) {
  HRESULT Result;
  
	/* Assert valid objects */
  ASSERT(pDirect3D   != NULL);	/* Need a valid Direct3D object */
  ASSERT(pDevice     == NULL);	/* Don't want to recreate a valid object */

 	/* Create the D3DRM device from this window and using the 'best' driver
	 * depending on whether exclusive mode is requested. */
  #if D3D_EXCLUSIVE_MODE
    Result = pDirect3D->CreateDeviceFromSurface(NULL, pDirectDraw, pBackSurface, &pDevice);

  #else

		/* Ensure a valid window size */
    if (Width == 0 || Height == 0) {
      SET_D3D_ERROR2(D3DRMERR_BADVALUE, "Received an invalid 0 width or height!");
      return (FALSE);
     }

		/* Create the device from the current clipper object */
    Result = pDirect3D->CreateDeviceFromClipper(pDDClipper, NULL, Width, Height, &pDevice);
  #endif

	/* Ensure the creation of the device was successfull */
  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to find a valid Direct3D device!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateDevice()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateDDClipper()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateDDClipper (void);
 *
 * Creates the DirectDraw clipper object and attaches it to the main window.
 * Only valid if not in exclusive mode. Returns  FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateDDClipper (void) {
  HRESULT Result;

	/* Assert valid objects */
  ASSERT(pDirect3D != NULL);	/* Need a valid Direct3D object */
  ASSERT(pDDClipper  == NULL);	/* Don't want to recreate a valid object */

	/* Create the clipper object */
  Result = DirectDrawCreateClipper(0, &pDDClipper, NULL);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the DirectDraw clipper!");
    return (FALSE);
   }

	/* Associate the clipper to the main window */
  Result = pDDClipper->SetHWnd(0, hMainWindow);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to associate the main window with the DirectDraw clipper!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateDDClipper()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::CreateMainWindow()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateMainWindow (void);
 *
 * Creates the application window and returns TRUE on success.  Does not
 * update or show the window.
 *
 *=========================================================================*/
boolean CD3DApp::CreateMainWindow (void) {
  WNDCLASS WindowClass;
  DWORD    Flags;  

	/* Check object states */
  ASSERT(hMainWindow == NULL);
  ASSERT(hInstance   != NULL);

	/* Setup the window class structure */
  WindowClass.style = CS_HREDRAW | CS_VREDRAW;
  WindowClass.lpfnWndProc = pWindowProc;
  WindowClass.cbClsExtra = 0;
  WindowClass.cbWndExtra = sizeof(DWORD);
  WindowClass.hInstance = hInstance;
  WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  WindowClass.lpszMenuName = pMenuName;
  WindowClass.lpszClassName = pClassName;

	/* Set the window icon resource, if any */
  if (pIconName == NULL)
    WindowClass.hIcon = NULL;
  else 
    WindowClass.hIcon = LoadIcon(hInstance, pIconName);

	/* Attempt to register the class */
  if (!RegisterClass(&WindowClass)) {
    SET_WIN_ERROR2("Failed to register the window class!");
    return (FALSE);
   }

      /* Set the window flags based on resizing options */
  if (ResizingDisabled)
    Flags = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
            WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
  else
    Flags = WS_OVERLAPPEDWINDOW;

	/* Attempt to create the window */
  hMainWindow = CreateWindow (pClassName, pWindowCaption, Flags,  CW_USEDEFAULT,    CW_USEDEFAULT,    
			      300, 300, NULL, NULL, hInstance,	NULL);       

	/* Ensure the window was successfully created */
  if (hMainWindow == NULL) {
    SET_WIN_ERROR2("Failed to create the window!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateMainWindow()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateSceneFrame()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateSceneFrame (void);
 *
 * Creates the scene frame which will be the root frame for future 3D objects.
 * Returns  FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateSceneFrame (void) {
  HRESULT Result;

	/* Assert valid objects */
  ASSERT(pDirect3D != NULL);		/* Need a valid Direct3D object */
  ASSERT(pScene    == NULL);		/* Don't want to recreate a valid scene */

	/* Create the master scene frame */ 
  Result = pDirect3D->CreateFrame(NULL, &pScene);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the master scene frame!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateSceneFrame()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::CreateView()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean CreateView (void);
 *
 * Creates the Direct3D viewpoty from the current device.  Returns FALSE on
 * any error.
 *
 *=========================================================================*/
boolean CD3DApp::CreateView (void) {
  HRESULT Result;

	/* Assert valid objects */
  ASSERT(pDirect3D != NULL);		/* Need a valid Direct3D and other objects */
  ASSERT(pCamera   != NULL);
  ASSERT(pDevice   != NULL);
  ASSERT(pView     == NULL);		/* Don't reinitialize the view object */

	/* Create the Direct3D viewport using the camera frame and the width
	 * and height of the device */
  Result = pDirect3D->CreateViewport(pDevice, pCamera, 0, 0, pDevice->GetWidth(), pDevice->GetHeight(), &pView);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create a valid Direct3D view!");
    Initialized = FALSE;
    return (FALSE);
   }

	/* Set the back render plane of the view */
  Result = pView->SetBack(ViewBackLength);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the Direct3D back render length!");
    return (FALSE);
   }

	/* Set the render quality, fill mode, lighting state and color shade info */
  Result = SetRenderState();
  if (!Result) return (FALSE);
 
	/* Return success */
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::CreateView()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::D3DInitialization()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean D3DInitialization (void);
 *
 * Standard intialization function.  Performs all the initialization
 * needed to get the app running.
 *
 *=========================================================================*/
boolean CD3DApp::D3DInitialization (void) {
  boolean Result;

	/* Check objects for valid states */
  ASSERT(pDirect3D == NULL);	/* We don't want to re-initialize over valid objects */
  	
	/* Create the Direct3D, Scene, and Camera objects */
  Result = CreateDirect3DObject(&pDirect3D);
  if (Result) Result = CreateSceneFrame();
  if (Result) Result = CreateCameraFrame();

	/* Perform Exclusive mode specific initialization */
  #if (D3D_EXCLUSIVE_MODE)
    if (Result) Result = CreateD3DExclusiveMode();

  #else
		/* Clipper is only required if not in exclusive mode */
    if (Result) Result = CreateDDClipper();		
  #endif

	/* Create the Direct3D device and view */
  if (Result) Result = CreateDevice();
  if (Result) Result = CreateView();
      
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::D3DInitialization()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::D3DWinMain()"
/*===========================================================================
 *
 * Class CD3DApp Method - int D3DWinMain (ThisInstance, PrevInstance, 
 *					  CommandLine, CommandShow);
 *
 * A standard windows entry point for D3D applications. Just call from
 * your own WinMain() to use.  Returns non-zero on failure.
 *
 *=========================================================================*/
int CD3DApp::D3DWinMain (HINSTANCE ThisInstance, HINSTANCE PrevInstance, 
		         LPSTR CommandLine, int CommandShow) {
  boolean Result;
  MSG     Message;
  int     FailCount = 0;

	/* Initialize the applicate and create the main window */
  Result = Initialize(ThisInstance, CommandShow);

	/* Ensure the user has been notified of any error */
  if (!Result) {
    if (!ErrorHandler.HaveNotifiedUser()) ErrorHandler.Notify();
    return(1);
   }

	/* Main application event loop */
  while (!QuitMain) {

		/* Check the message queue until no immediate messages remain */
    while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {

		/* Are we quitting? */
      if (Message.message == WM_QUIT) {
        TerminateApp();
        break;
       }

		/* Check the accelerator table, if any */
      if (hAccelerator == NULL) continue;

      if (!TranslateAccelerator(Message.hwnd, hAccelerator, &Message)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
       }
     } /* End of while peeking messages */

		/* Do we need to quit yet? */
    if (QuitMain) break;

		/* Render a scene if the application state is valid */
    if (CanRenderScene()) {

		/* Attempt to render a frame, notifying the user of any errors */
      if (!Render()) {
        FailCount++;
	if (!ErrorHandler.HaveNotifiedUser()) ErrorHandler.Notify();
       }

		/* Abort if rendering fails more than twice */
      if (FailCount > 2) {
        SET_EXT_ERROR2(ERR_CUSTOM, "Rendering has failed too many times...Aborting execution!");
	ErrorHandler.Notify();
        TerminateApp();
        break;
       }
     } /* End of if rendering... */
    else
      WaitMessage();
     
   } /* End of while (!QuitMain)... */

	/* Destroy the main window and exit */
  DestroyWindow(hMainWindow);
  return (Message.wParam);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::D3DWinMain();
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::DoFollowSurface()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean DoFollowSurface (void);
 *
 * Attempt to get the camera to follow the surface.  Returns FALSE on any
 * error.
 *
 *=========================================================================*/
boolean CD3DApp::DoFollowSurface (void) { 
  HRESULT   Result;
  D3DVECTOR CameraVector;
  D3DVECTOR SurfacePoint;

	/* Ignore if not following the surface currently */
  if (!FollowSurface) return (TRUE);

	/* Ensure valid objects */
  ASSERT(pCamera != NULL);
  ASSERT(pScene  != NULL);

	/* Attempt to retrieve the camera position */
  Result = pCamera->GetPosition(pScene, &CameraVector);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to retrieve the camera position!");
    return (FALSE);
   }

	/* Attempt to find the surface point above/below the camera */
  Result = FindSurface(pScene, CameraVector, SurfacePoint);
  if (!Result) return (FALSE);

	/* Move the camera up slightly */
  CameraVector.y = SurfacePoint.y + (float) CameraHeight;
  Result = pCamera->SetPosition(pScene, CameraVector.x, CameraVector.y, CameraVector.z);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the camera position!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::DoFollowSurface()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::DrawFrameRate()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean DrawFrameRate (void);
 *
 * Displays the frame rate in the main window.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::DrawFrameRate (void) {
  boolean  Result;
  HDC	   hDC;
  char     FrameRateBuffer[64];
  int      BufferSize;
  int	   PrevBKMode;

	/* Ignore if we aren't displaying the frame rate currently */
  if (!DisplayFrameRate) return (TRUE);
    
	/* Get the device context for the display */
  hDC = GetDisplayDC();
  if (hDC == NULL) return (FALSE);

	/* Set the background mode */
  PrevBKMode = SetBkMode(hDC, TRANSPARENT);

	/* Create a frame rate string and output to DC */
  BufferSize = sprintf (FrameRateBuffer, "%.1f fps", FrameRate);
  TextOut(hDC, 10, 30, FrameRateBuffer, BufferSize);
  SetBkMode(hDC, PrevBKMode);

	/* Release the display device context */
  Result = ReleaseDisplayDC(hDC);
  return (Result);
 }
/*===========================================================================
 *		End of Class CD3DApp::DrawFrameRate()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::GetDirectDraw()"
/*===========================================================================
 *
 * Class CD3DApp Method - CDirectDraw* GetDirectDraw (void);
 *
 * Returns the current DirectDraw object for the application.  If the app is
 * not initialization or not in exclusive mode, NULL will be returned. 
 *
 *=========================================================================*/
CDirectDraw* CD3DApp::GetDirectDraw (void) {

  #if (D3D_EXCLUSIVE_MODE)
    ASSERT(pDirectDraw != NULL);
    return (pDirectDraw);
  #else
    return (NULL);
  #endif

 }
/*===========================================================================
 *		End of Class Method CD3DApp::GetDirectDraw()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__  "CD3DApp::GetPrimarySurface()"
/*===========================================================================
 *
 * Class CD3DApp Method - CDDSurface* GetPrimarySurface (void);
 *
 * Returns the current Primary Surface for the application.  If the app is
 * not initialization or not in exclusive mode, NULL will be returned. 
 *
 *=========================================================================*/
CDDSurface* CD3DApp::GetPrimarySurface (void) {

  #if (D3D_EXCLUSIVE_MODE)
    ASSERT(pPrimarySurface != NULL);
    return (pPrimarySurface);
  #else
    return (NULL);
  #endif

 }
/*===========================================================================
 *		End of Class Method CD3DApp::GetPrimarySurface()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::FindSurface()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean FindSurface (pRefFrame, SourcePoint, DestPoint);
 *
 * Attempt to find a surface directly above/below the given point in the 
 * main frame.  Returns TRUE on success or FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::FindSurface (CD3DFrame* pRefFrame, const d3dvector_t& SourcePoint, d3dvector_t& DestPoint) {
  CD3DPickedArray2*	pPickedArray;
  CD3DVisual*		pVisual;
  CD3DFrameArray*	pFrameArray;
  d3dpickdesc2_t	PickDesc;
  d3dray_t		Ray;
  HRESULT		Result;
  
	/* Ensure valid input */
  ASSERT(pRefFrame  != NULL);

	/* Chose the down direction from the source point */
  Ray.dvDir = D3DVECTOR(0.0, -1.0, 0.0);
  Ray.dvPos = SourcePoint;

	/* Find the surface */
  Result = pRefFrame->RayPick(NULL, &Ray, D3DRMRAYPICK_IGNOREFURTHERPRIMITIVES | D3DRMRAYPICK_INTERPOLATENORMAL, &pPickedArray);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to RayPick the reference frame!");
    return (FALSE);
   }

	/* Nothing found, try in the up direction */
  if (pPickedArray->GetSize() == 0) {
    RELEASE(pPickedArray);
    Ray.dvPos.y += (float) 10000.0;
  
		/* Find the surface */
    Result = pRefFrame->RayPick(NULL, &Ray, D3DRMRAYPICK_IGNOREFURTHERPRIMITIVES | D3DRMRAYPICK_INTERPOLATENORMAL, &pPickedArray);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to RayPick the reference frame!");
      return (FALSE);
     }

		/* Ensure something was found */
    if (pPickedArray->GetSize() == 0) {
      SystemLog.Printf ("Failed to find any surfaces at point (%.0f, %.0f, %.0f)!", SourcePoint.x, SourcePoint.y, SourcePoint.z);
      RELEASE(pPickedArray);
      return (FALSE);
     }
   }

	/* Attempt to retreive the first object in the pick array */
  Result = pPickedArray->GetPick(0, &pVisual, &pFrameArray, &PickDesc);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get the first object in PickedArray!");
    RELEASE(pPickedArray);
    return (FALSE);
   }

	/* Save the found destination point on the surface */
  DestPoint = PickDesc.dvPosition;

	/* Release objects and return success*/
  RELEASE(pVisual);
  RELEASE(pFrameArray);
  RELEASE(pPickedArray);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::FindSurface()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::GetDisplayDC()"
/*===========================================================================
 *
 * Class CD3DApp Method - hDC GetDisplayDC (void);
 *
 * Attempt to retrieve the main display Device Context.  Returns NULL on 
 * any error.
 *
 *=========================================================================*/
HDC CD3DApp::GetDisplayDC (void) {
  HDC hDC;
  
	/* Get the device context for the display */
  #if (D3D_EXCLUSIVE_MODE)
    HRESULT  Result;

	/* Ensure a valid primary surface */
    ASSERT(pBackSurface != NULL);
    Result = pBackSurface->GetDC(&hDC);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to retrieve the device context for the primary surface!");
      return (NULL);
     }

  #else
	/* Ensure a valid window handle */
    ASSERT(hMainWindow != NULL);		
    hDC = GetDC(hMainWindow);
 
    if (hDC == NULL) {
      SET_WIN_ERROR2("Failed to retrieve the device context for the main window!");
      return (NULL);
     }

  #endif

  return (hDC);
 }
/*===========================================================================
 *		End of Class CD3DApp::GetDisplayDC()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::GetElapsedRenderTime()"
/*===========================================================================
 *
 * Class CD3DApp Method - float GetElapsedRenderTime (void);
 *
 * Retrieves the time, in seconds, since the last render.  On any error, such
 * as an invalid last render time, returns 0.
 *
 *=========================================================================*/
float CD3DApp::GetElapsedRenderTime (void) {
  boolean	Result;
  float		ElapsedTime;

	/* Use the high precision counter */
  if (UsePerformanceCounter) {
    LARGE_INTEGER CurrentCount;
    LARGE_INTEGER CounterDiff;

    Result = QueryPerformanceCounter(&CurrentCount);

    if (Result) {
      CounterDiff.QuadPart = CurrentCount.QuadPart - LastRenderCounter.QuadPart;

      if (LastRenderCounter.QuadPart == 0 || CounterDiff.QuadPart <= 0) {
        LastRenderCounter = CurrentCount;
        return ((float)0.0);
       }

      ElapsedTime = (float) ((double)CounterDiff.QuadPart/(double)PerformanceFreq.QuadPart);
      LastRenderCounter = CurrentCount;
      return (ElapsedTime);
     }
   }

	/* Have to use the regular clock timer */
  clock_t CurrentTick = clock();
  clock_t TickDiff = clock() - CurrentTick;

  if (LastRenderTick == 0 || TickDiff <= 0) {
    LastRenderTick = CurrentTick;
    return ((float)0.0);
   }

  ElapsedTime = (float)((float)TickDiff/(float)CLOCKS_PER_SEC);
  LastRenderTick = CurrentTick;
  return (ElapsedTime);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::GetElapsedRenderTime()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::GetPositionInFrontOfCamera()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean GetPositionInFrontOfCamera (Vector, pRefFrame);
 *
 * Attempt to retrieve a vector to a position in front of the current
 * camera relative to the given reference frame.  Returns FALSE on any
 * error.
 *
 *=========================================================================*/
boolean CD3DApp::GetPositionInFrontOfCamera (d3dvector_t& Vector, LPDIRECT3DRMFRAME2 pRefFrame) {
  HRESULT   Result;
  D3DVECTOR Up;
  D3DVECTOR Direction;

	/* Ensure valid objects */
  ASSERT(pCamera   != NULL);
  ASSERT(pRefFrame != NULL);

	/* Retreive the camera's position relative to the scene */
  Result = pCamera->GetPosition(pRefFrame, &Vector);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get camera position!");
    return (FALSE);
   }

	/* Get the camera's orientation */
  Result = pCamera->GetOrientation(pRefFrame, &Direction, &Up);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get camera orientation!");
    return (FALSE);
   }

	/* Adjust the vector to place it in front of the camera */
  Direction.x = -Direction.x;
  Vector += Direction * MovementSpeed;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::GetPositionInFrontOfCamera()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::HandleMessages()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean HandleMessages (Result, hWindow, Message, wParam, lParam);
 *
 * Handles any general window messages. Call from your WindowProc()
 * procedure.  Returns FALSE if the message was not handled.
 *
 *=========================================================================*/
boolean CD3DApp::HandleMessages (LRESULT& Result, HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam) {
  boolean bResult;
      
	/* Ignore if not initialized. The default window procedure
	 * should be called. */
  if (!IsInitialized()) return (FALSE);

	/* Let any active surface mode object handle the message first */
  if (IsInSurfaceMode()) {
    if (pCurrentSurfaceMode->IsActive()) {
      bResult = pCurrentSurfaceMode->HandleMessages(Result, hWindow, Message, wParam, lParam);
      if (bResult) return (TRUE);
     }
   }

  switch (Message) {
    case WM_KEYUP:
      if (!IsInSurfaceMode()) return OnKeyUp(Result, wParam);
      break;
    case WM_KEYDOWN:
      if (!IsInSurfaceMode()) return OnKeyDown(Result, wParam);
      break;
    case WM_LBUTTONDOWN:
      if (!IsInSurfaceMode()) return OnLButtonDown(Result, wParam, LOWORD(lParam), HIWORD(lParam));
      break;
    case WM_DESTROY:
      TerminateApp();
      return (TRUE);
    case WM_SIZE:	/* Handle resizing of the window */
      return OnSize(Result, LOWORD(lParam), HIWORD(lParam));
    case WM_ACTIVATE:	/* Create a Windows specific D3DRM window device to handle this message */
      return OnActivate(Result, wParam);
    case WM_PAINT: 
      return OnPaint(Result, hWindow);
    case WM_DISPLAYCHANGE:
      return OnDisplayChange(Result, hWindow);	    
    default: 
      return (FALSE);
   }

  return (FALSE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::HandleMessage()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::Initialize()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean Initialize (ThisInstance, CommandShow);
 *
 * Standard intialization function.  Performs all the initialization
 * needed to get the app running.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::Initialize (HINSTANCE ThisInstance, int CommandShow) {
  boolean Result;

	/* Ignore if already initialized */
  if (IsInitialized()) return (TRUE);
  hInstance = ThisInstance;

	/* Create the application window and initialize Direct3D */
  Result = CreateMainWindow();
  if (Result) Result = UpdateBitsPerPixel();
  if (Result) Result = D3DInitialization();

	/* Build the intial scene as specified by the user, if valid */
  if (Result && pBuildScene != NULL) {
    Result = pBuildScene();
   }

	/* Abort if any of the previous actions resulted in failure */
  if (!Result) return (FALSE);

	/* Load the acceleration table, if any.  It appears that if 
	 * accelerators aren't loaded, the application locks-up. */
  if (pAcceleratorName != NULL) {
    hAccelerator = LoadAccelerators(hInstance, pAcceleratorName);

    if (hAccelerator == NULL) {
      SET_WIN_ERROR3("Failed to load the accelerator table '%s'!", pAcceleratorName);
      return (FALSE);
     }
   }

	/* Display and update the main window */
  ShowWindow(hMainWindow, CommandShow);
  Result = UpdateWindow(hMainWindow);

  if (!Result) {
    SET_WIN_ERROR2("Failed show and update the main application window!");
    return (FALSE);
   }

	/* Return Success */
  Initialized = TRUE;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::Initialize()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::MoveScene()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean MoveScene (void);
 *
 * Moves the scene 'tick' according to the time elasped since the last
 * render.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::MoveScene (void) {
  HRESULT  Result;
  float    ElaspedTime;

	/* Ensure a valid scene object */
  if (pScene == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }
  
	/* Compute the time correction factor and frame rate */
  ElaspedTime = GetElapsedRenderTime();

  if (ElaspedTime != 0.0) 
    FrameRate = D3DVAL(1.0/ElaspedTime);
  else
    FrameRate = D3DVAL(0.0);

	/* Apply the tick to the scene */
  Result = pScene->Move(ElaspedTime);

  if (FAILED(Result)) {
    SET_D3D_ERROR3(Result, "Failed to move the scene by %.3f sec!", ElaspedTime);
    return (FALSE);
   }

   	/* Get the camera to follow the surface if required */
  Result = DoFollowSurface();
  //if (!Result) return (FALSE);
  
  return (TRUE);
 }
/*===========================================================================
 *		End of Class CD3DApp::MoveScene()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnActivate()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnActivate (lResult, wParam);
 *
 * Called when the main window is activated. Create a Windows specific 
 * D3DRM window device to handle this message.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::OnActivate (LRESULT& lResult, WPARAM wParam) {
  CD3DWinDevice* pWinDev;
  HRESULT	 Result;

  	/* Ensure a valid Direct3D device */
  ASSERT(pDevice != NULL);

	/* Toggle the activated flag */
  if (LOWORD(wParam) == WA_INACTIVE) 
    Activated = FALSE;
  else
    Activated = TRUE;

	/* Attempt to get the window device from the device */
  Result = pDevice->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &pWinDev);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create Direct3D windows device to handle WM_ACTIVATE event!");
    return (FALSE);
   }
	
	/* Handle the activate message */
  Result = pWinDev->HandleActivate(wParam);
  if (FAILED(Result)) SET_EXT_ERROR2(ERR_CUSTOM, "Failed to handle the WM_ACTIVATE event!");
  RELEASE(pWinDev);
  
	/* Ensure the display is updated on a activate */
  if (Activated) Render();

	/* Set the Windows return value and return success */
  lResult = 0;
  return (TRUE);
 }      
/*===========================================================================
 *		End of Class Event CD3DApp::OnActivate()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnDisplayRange()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnDisplayChange (lResult, hWindow);
 *
 * Handles any display change messages.  Creates a temporary DirectDraw and
 * surface to see if the display change was created by an application requesting
 * exclusive mode.  If so, just ignore it.  Otherwise update the device and
 * view appropiately.  Returns TRUE if the event was properly handled. 
 *
 *=========================================================================*/
boolean CD3DApp::OnDisplayChange (LRESULT& lResult, HWND hWindow) {
  CDirectDraw*	pDDraw = NULL;
  CDDSurface*	pDDSurface = NULL;
  ddsdesc_t	ddSurfaceDesc;
  HRESULT	Result;

	/* We should ignore this if in exclusive mode */
  #if D3D_EXCLUSIVE_MODE
    lResult = 0;
    return (TRUE);
  #endif

	/* Create a temporary DirectDraw object to see if this display change was
	 * due to another app requesting exclusive mode. */
  Result = CreateDirectDrawObject(&pDDraw);
  if (!Result) return (FALSE);

  Result = pDDraw->SetCooperativeLevel(hWindow, DDSCL_NORMAL | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
 
  if (FAILED(Result)) {
    RELEASE(pDDraw);
    return (FALSE);
   }

	/* Set the temporary surface attributes and create the surface */
  memset(&ddSurfaceDesc, 0, sizeof(ddSurfaceDesc));
  ddSurfaceDesc.dwSize = sizeof(ddSurfaceDesc);
  ddSurfaceDesc.dwFlags = DDSD_CAPS;
  ddSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  Result = pDDraw->CreateSurface(&ddSurfaceDesc, &pDDSurface, NULL);

	/* An exclusive mode generated WM_DISPLAYCHANGE, ignore it */
  if (Result == DDERR_NOEXCLUSIVEMODE) {
    RELEASE(pDDraw);
    return (FALSE);
   }

	/* Release the temporary Surface and DirectDraw objects */
  if (SUCCEEDED(Result)) RELEASE(pDDSurface);
  RELEASE(pDDraw);	    
  Initialized = FALSE;

	/* Create a new device and view */
  Result = CreateDevice();
  if (Result) Result = CreateView();

	/* On failure we must terminate the application */
  if (!Result) {
    TerminateApp();
    return (TRUE);
   }

  Initialized = TRUE;
  lResult = 0;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Event CD3DApp::OnDisplayChange()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnKeyDown()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnKeyDown (lResult, KeyDown);
 *
 * Called when a key is pressed
 *
 *=========================================================================*/
boolean CD3DApp::OnKeyDown (LRESULT& lResult, int KeyCode) {
  D3DVECTOR Direction;
  D3DVECTOR Up;
  D3DVECTOR Right;

	/* Ensure valid objects */
  ASSERT(pCamera != NULL);

	/* Compute valid directions of the camera frame */
  pCamera->GetOrientation(pScene, &Direction, &Up);
  D3DRMVectorCrossProduct(&Right, &Up, &Direction);
  Direction = Normalize(Direction)*MovementSpeed;
  Up.x /= D3DVAL(4.0);
  Up.y /= D3DVAL(4.0);
  Up.z /= D3DVAL(4.0);
  Right.x /= D3DVAL(4.0);
  Right.y /= D3DVAL(4.0);
  Right.z /= D3DVAL(4.0);
  lResult = 0;

  switch (KeyCode) {
    case 'q':
    case 'Q':
      TerminateApp();
      return (TRUE);
    case VK_LEFT:
    case VK_NUMPAD4:
      if (!AllowMovement) break;
      pCamera->SetRotation(pScene, 0, 1, 0, -RotationSpeed);
      return (TRUE);
    case VK_NUMPAD6:
    case VK_RIGHT:
      if (!AllowMovement) break;
      pCamera->SetRotation(pScene, 0, 1, 0, RotationSpeed);
      return (TRUE);
    case VK_NUMPAD8:
    case VK_UP:
      if (!AllowMovement) break;
      pCamera->SetVelocity(pScene, Direction.x, 0.0, Direction.z, FALSE);
      return (TRUE);
    case VK_NUMPAD2:
    case VK_DOWN:
      if (!AllowMovement) break;
      pCamera->SetVelocity(pScene, -Direction.x, 0.0, -Direction.z, FALSE);
      return (TRUE);
    case VK_PRIOR:
    case VK_NUMPAD9:
      if (!AllowMovement) break;
      pCamera->SetRotation(pScene, Right.x, 0, Right.z, (float) (RotationSpeed/10.0));
      return (TRUE);
    case VK_NEXT: 
    case VK_NUMPAD3:
      if (!AllowMovement) break;
      pCamera->SetRotation(pScene, Right.x, 0, Right.z,  (float) (-RotationSpeed/10.0));
      return (TRUE);
   }

	/* Event was not handled */    
  return (FALSE);
 }      
/*===========================================================================
 *		End of Class Event CD3DApp::OnKeyDown()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnKeyUp()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnKeyUp (lResult, KeyCode);
 *
 * Called when a key is pressed. Returns TRUE if the event was handled.
 *
 *=========================================================================*/
boolean CD3DApp::OnKeyUp (LRESULT& lResult, int KeyCode) {
  lResult = 0;
  
  switch (KeyCode) {
    case VK_PRIOR:
    case VK_NEXT:
    case VK_NUMPAD4:
    case VK_NUMPAD6:
    case VK_NUMPAD9:
    case VK_NUMPAD3:
    case VK_RIGHT:
    case VK_LEFT:
      if (!AllowMovement) break;
      pCamera->SetRotation(pScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
      return (TRUE);
    case VK_NUMPAD8:
    case VK_NUMPAD2:
    case VK_UP:
    case VK_DOWN:
      if (!AllowMovement) break;
      pCamera->SetVelocity(pScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), FALSE);
      return (TRUE);
    }
    
	/* Event was not handled */
  return (FALSE);
 }      
/*===========================================================================
 *		End of Class Event CD3DApp::OnKeyUp()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnLButtonDown()"
/*===========================================================================
 *
 * Class CD3DApp Event - LRESULT OnLMouseDown (lResult, KeyFlags, XPos, YPos);
 *
 * Called when the Left-mouse down is pressed in the main window.
 * Returns TRUE if the event was handled.
 *
 *=========================================================================*/
boolean CD3DApp::OnLButtonDown (LRESULT& lResult, int KeyFlags, int XPos, int YPos) {

	/* Attempt to select objects in the window */
  PickObjects(pView, XPos, YPos);

  lResult = 0;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Event CD3DApp::OnLButtonDown()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CD3DApp Event - LRESULT OnPaint (lResult, hWindow);
 *
 * Called when the main window is needs to be repainted.  Create a Windows
 * specific D3DRM window device to handle this message.
 *
 *=========================================================================*/
boolean CD3DApp::OnPaint (LRESULT& lResult, HWND hWindow) {
  RECT			WindowRect;
  PAINTSTRUCT		PaintStruct;
  CD3DWinDevice*	pWinDev = NULL;
  BOOL			bResult;
  HRESULT		Result;

	/* Ignore if we aren't initialized yet */
  if (!IsInitialized()) return (FALSE);

	/* Ensure valid objects */
  ASSERT(pDevice != NULL);

	/* Get the area we need to update */
  bResult = GetUpdateRect(hWindow, &WindowRect, FALSE);
  if (!bResult) return (FALSE);

	/* Start the window painting */
  BeginPaint(hWindow, &PaintStruct);

	/* Attempt to get a windows device from the D3D device */
  Result = pDevice->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &pWinDev);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create Windows device to handle WM_PAINT!");
    return (FALSE);
   }

	/* Handle the paint event */
  Result = pWinDev->HandlePaint(PaintStruct.hdc);
  if (FAILED(Result)) SET_EXT_ERROR2(ERR_CUSTOM, "Failed to handle WM_PAINT message!");

	/* Terminate painting */
  RELEASE(pWinDev);
  EndPaint(hWindow, &PaintStruct);
    
  lResult = 0;
  return (TRUE);
 }      
/*===========================================================================
 *		End of Class Event CD3DApp::OnPaint()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::OnSize()"
/*===========================================================================
 *
 * Class CD3DApp EVent - boolean OnSize (lResult, Width, Height);
 *
 * Protected event which occurs when the user attempts to resize the
 * window.  Returns TRUE if the event was handled.
 *
 *=========================================================================*/
boolean CD3DApp::OnSize (LRESULT& lResult, int Width, int Height) { 
  boolean Result;

	/* Should ignore this in exclusive mode */
  #if (D3D_EXCLUSIVE_MODE)
    lResult = 0;
    return (TRUE);
  #endif

	/* Ensure valid objects */
  ASSERT(pView != NULL);
  ASSERT(pDevice != NULL);

	/* Check for a minimized message */
  if (Width == 0 || Height == 0) {
    Minimized = TRUE;
    ResetLastRender();
    lResult = 0;
    return (TRUE);
   }
  	
	/* Release the current view and device objects */	
  Initialized = FALSE;
  RELEASE(pView);
  RELEASE(pDevice);

	/* Create a new device and view for the window */
  Result = CreateDevice(Width, Height);
  if (Result) Result = CreateView();

	/* Must terminate application on error */
  if (!Result) {
    ErrorHandler.Notify();
    TerminateApp();
    lResult = 0;
    return (TRUE);
   }

	/* Reset appropriate states */
  Initialized = TRUE;
  Minimized = FALSE;
  ResetLastRender();

	/* Return success */
  lResult = 0;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Event CD3DApp::OnSize()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::PickObjects()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean PickObjects (pViewport, XPos, YPos);
 *
 * Attempt to pick objects in the given viewport at the specific coordinates.
 * Returns TRUE if any object was picked.  Attempt to select the object and
 * display info depending on the application state.
 *
 *=========================================================================*/
boolean CD3DApp::PickObjects (CD3DView* pViewport, const int XPos, const int YPos) {
  CD3DPickedArray* pPickedArray;
  CD3DVisual*      pVisual;
  CD3DFrameArray*  pFrameArray;
  CD3DFaceArray*   pFaceArray;
  CD3DFace*	   pFace;
  CD3DMeshBuilder* pMesh;
  d3dpickdesc_t    PickDesc;
  HRESULT	   Result;

  	/* Ignore if we aren't allowing object selection currently */
  if (!AllowSelectObject) return (FALSE);
  
	/* Ensure valid input */
  ASSERT(pViewport != NULL);

	/* Attempt to find objects in scene */
  Result = pViewport->Pick(XPos, YPos, &pPickedArray);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error picking object in viewport!");
    return (FALSE); 
   }

	/* Nothing found */
  if (pPickedArray->GetSize() == 0) {
    RELEASE(pPickedArray);
    return (FALSE);
   }

	/* Select the first pick object in the array */
  Result = pPickedArray->GetPick(0, &pVisual, &pFrameArray, &PickDesc);

  if (pVisual == NULL || FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get the first picked object in the array!");
    RELEASE(pPickedArray);
    return (FALSE);
   }

	/* Attempt to get the mesh builder from the visual object */
  Result = pVisual->QueryInterface(IID_CD3DMeshBuilder, (void **) &pMesh);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to retrieve the mesh builder from the visual object!");
    RELEASE(pPickedArray);
    RELEASE(pVisual);
    return (FALSE);
   }

	/* Attempt to get the face array from the object */  
  Result = pMesh->GetFaces(&pFaceArray);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to retrieve the face array from the mesh builder!");
    RELEASE(pMesh);
    RELEASE(pPickedArray);
    RELEASE(pVisual);
    return (FALSE);
   }

	/* Ensure a valid face index (so, I'm paranoid) */
  ASSERT(pFaceArray->GetSize() > PickDesc.ulFaceIdx);

	/* Get the face object */
  Result = pFaceArray->GetElement(PickDesc.ulFaceIdx, &pFace); 

  if (FAILED(Result)) {
    SET_D3D_ERROR3(Result, "Failed to retrieve the face %d from the face array !", PickDesc.ulFaceIdx);
    RELEASE(pFaceArray);
    RELEASE(pMesh);
    RELEASE(pPickedArray);
    RELEASE(pVisual);
    return (FALSE);
   }

	/* Select the object if required */
  SelectObject(pVisual, pFace, PickDesc);

	/* Unallocate objects */
  RELEASE(pFrameArray);
  RELEASE(pPickedArray);
  RELEASE(pFaceArray);
  RELEASE(pMesh);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::PickObjects()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::ReleaseDisplayDC()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean ReleaseDisplayDC (void);
 *
 * Releases a device context previously obtained with GetDisplayDC().
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::ReleaseDisplayDC (HDC hDC) {
  
	/* Release the device context for the display */
  #if (D3D_EXCLUSIVE_MODE)
    HRESULT  Result;

	/* Ensure a valid primary surface */
    ASSERT(pBackSurface != NULL);
    Result = pBackSurface->ReleaseDC(&hDC);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to release the device context for the back surface!");
      return (FALSE);
     }

  #else 
    boolean Result;
    
    	/* Ensure a valid window handle */
    ASSERT(hMainWindow != NULL);		
    Result = ReleaseDC(hMainWindow, hDC);
 
    if (Result != 1) {
      SET_WIN_ERROR2("Failed to release the device context for the main window!");
      return (FALSE);
     }

  #endif

  return (TRUE);
 }
/*===========================================================================
 *		End of Class CD3DApp::ReleaseDisplayDC()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::RemoveSurfaceMode()"
/*===========================================================================
 *
 * Class CD3DApp Method - void RemoveSurfaceMode (void);
 *
 * Removes any current surface mode object.
 *
 *=========================================================================*/
void CD3DApp::RemoveSurfaceMode (void) {

	/* Ensure there is a surface mode to remove */
  if (pCurrentSurfaceMode == NULL) return;

  InSurfaceMode = FALSE;
  pCurrentSurfaceMode->Destroy();
  pCurrentSurfaceMode = NULL;
  Render();
 }
/*===========================================================================
 *		End of Class Method CD3DApp::RemoveSurfaceMode()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::Render()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean Render (void);
 *
 * Updates the window with one frame.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::Render (void) {
  HRESULT  Result;

	/* Ensure we should be rendering the scene currently */
  if (!CanRenderScene()) return (TRUE);

  	/* Tick the scene, applying time correction to the movement */
  Result = MoveScene();
  if (!Result) return (FALSE);

	/* Clear the viewport */
  Result = pView->Clear();

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to clear viewport!");
    return FALSE;
   }

	/* Render the scene to the viewport */
  Result = pView->Render(pScene);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to render scene!");
    return (FALSE);
   }
  
	/* Update the entire view */
  pView->ForceUpdate(pView->GetX(), pView->GetY(), pView->GetX()+pView->GetWidth(), pView->GetY() + pView->GetHeight());
  Result = pDevice->Update();

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to update device after render!");
    return (FALSE);
   }

	/* Display the frame rate if required */
  Result = DrawFrameRate();

	/* Flip surfaces only if we are in exclusive mode */
  #if (D3D_EXCLUSIVE_MODE)
    ASSERT(pDDSurface != NULL);

		/* Flip the primary/back buffers */
    Result = pDDSurface->Flip(NULL, DDFLIP_WAIT);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to flip the primary and back surfaces!");
      return (FALSE);
     }
  #endif

  return (TRUE);
 }
/*===========================================================================
 *		End of Class CD3DApp::Render()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::SelectObject()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean SelectObject (pVisual, pFace, PickDesc);
 *
 * Attempt to set the currently selected object.  Returns TRUE on success
 * or FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::SelectObject (CD3DVisual* pVisual, CD3DFace* pFace, d3dpickdesc_t& PickDesc) {
  int Result;

	/* Ensure valid objects */
  ASSERT(pVisual   != NULL);
  ASSERT(pFace     != NULL);

   	/* Unselect the current objects, if any */
  UnSelectObject();

	/* Are we allowed to select objects? */
  if (!AllowSelectObject) return (FALSE);

	/* Copy the object pointers/values */
  pSelectedVisual = pVisual;
  pSelectedFace = pFace;
  SelectedPickDesc = PickDesc;
  HasSelectedObject = TRUE;

	/* Record and change the color of the selected face */
  OldSelectedFaceColor = pSelectedFace->GetColor();
  pSelectedFace->SetColorRGB(D3DVAL(1.0), D3DVAL(0.1), D3DVAL(0.1));

	/* Call the display plane callback function if required, updating the
	 * output display beforehand. */
  if (pOnSelectObjectCallBack != NULL && CallBackOnSelectObject) { 
    Result = Render();
    if (!Result) return (FALSE);
    pOnSelectObjectCallBack (pVisual->GetAppData(), SelectedPickDesc.ulFaceIdx);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::SelectObject()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::SetFillMode()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean SetFillMode (FillMode);
 *
 * Changes the rendering quality fill mode (point, wireframe, or solid).
 * Returns FALSE on any error.
 * 
 *=========================================================================*/
boolean CD3DApp::SetFillMode(d3dfillmode_t FillMode) {
  d3dfillmode_t OldFillMode = (d3dfillmode_t) (RenderQuality & D3DRMFILL_MASK);
  HRESULT	Result;

	/* Ensure a valid device object */
  ASSERT(pDevice != NULL);

	/* Extract the render quality information */
  RenderQuality = (RenderQuality & ~D3DRMFILL_MASK) | FillMode;
  Result = pDevice->SetQuality(RenderQuality);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the render quality while changing the fill mode!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::SetFillMode()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::SetRenderState()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean SetRenderState (void);
 *
 * Sets the render state (quality, dithering, texture BPP, etc...).
 * Returns the D3D return code.
 *
 *=========================================================================*/
boolean CD3DApp::SetRenderState (void) {
  HRESULT Result;

	/* Ensure valid objects */
  ASSERT(pDirect3D != NULL);
  ASSERT(pDevice   != NULL);

	/* Set the render quality (light toggle, fill mode, shade mode) */
  if (pDevice->GetQuality() != RenderQuality) {
    Result = pDevice->SetQuality(RenderQuality);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to set the Direct3D device quality!");
      return (FALSE);
     }
   }

	/* Set dithering toggle */
  if (pDevice->GetDither() != Dithering) {
    Result = pDevice->SetDither(Dithering);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to change the dithering mode of the Direct3D device!");
      return (FALSE);
     }
   }

	/* Set the texture quality (point or linear filtering) */
  if (pDevice->GetTextureQuality() != TextureQuality) {
    Result = pDevice->SetTextureQuality(TextureQuality);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to change the texture quality of the Direct3D device!");
      return (FALSE);
     }
    }

	/* Set shade info based on current bits per pixel */
  switch (BitsPerPixel) {
    case 1:
      Result = pDevice->SetShades(4);
      if (FAILED(Result)) break;
      Result = pDirect3D->SetDefaultTextureShades(4);
      break;
    case 16:
      Result = pDevice->SetShades(32);
      if (FAILED(Result)) break;
      Result = pDirect3D->SetDefaultTextureColors(64);
      if (FAILED(Result)) break;
      Result = pDirect3D->SetDefaultTextureShades(32);
      break;
    case 24:
    case 32:
      Result = pDevice->SetShades(256);
      if (FAILED(Result)) break;
      Result = pDirect3D->SetDefaultTextureColors(64);
      if (FAILED(Result)) break;
      Result = pDirect3D->SetDefaultTextureShades(256);
      break;
    }

	/* Check for errors */
  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to change the texture shades/colors of the Direct3D device!");
    return (FALSE); 
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::SetRenderState()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::SetSurfaceMode()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean SetSurfaceMode (pSurfaceMode);
 *
 * Sets the current surface mode.  Destroys the previous mode, if any and
 * returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::SetSurfaceMode (CDDSurfaceMode* pSurfaceMode) {
  boolean Result;
  
	/* Ensure valid input */
  ASSERT(pSurfaceMode != NULL);

	/* Remove the previous surface mode, if any */
  RemoveSurfaceMode();

	/* Initialize the new surface mode */
  Result = pSurfaceMode->InitSurfaceMode(hMainWindow, this);
  if (!Result) return (FALSE);
  
	/* Set the surface mode to be the active component */
  pSurfaceMode->SetActive();
  InSurfaceMode = TRUE;
  pCurrentSurfaceMode = pSurfaceMode;
  pSurfaceMode->Update();

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::SetSurfaceMofe()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::TerminateApp()"
/*===========================================================================
 *
 * Class CD3DApp Method - void TerminateApp (void);
 *
 * Destroys all the D3D objects.
 *
 *=========================================================================*/
void CD3DApp::TerminateApp (void) {
  Initialized = FALSE;
  QuitMain = TRUE;

	/* Destroy exclusive mode specific objects */
  #if (D3D_EXCLUSIVE_MODE)
    RELEASE(pBackSurface);
    RELEASE(pPrimarySurface);
    RELEASE(pDirectDraw);
  #endif

  RELEASE(pSelectedFace);
  RELEASE(pSelectedVisual);
  RELEASE(pScene);
  RELEASE(pCamera);
  RELEASE(pView);
  RELEASE(pDevice);
  RELEASE(pDirect3D);
  RELEASE(pDDClipper);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::TerminateApp()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::UnSelectObject()"
/*===========================================================================
 *
 * Class CD3DApp Method - void UnSelectObject (void);
 *
 * Unselects any currently selected objects.
 *
 *=========================================================================*/
void CD3DApp::UnSelectObject (void) {

	/* Delete the currently selected item objects, if any */
  if (HasSelectedObject) {

		/* Ensure valid objects */
    ASSERT(pSelectedFace != NULL);
    ASSERT(pSelectedVisual != NULL);

		/* Reset the selected face color */
    pSelectedFace->SetColor(OldSelectedFaceColor);

		/* Delete the selected objects */
    RELEASE(pSelectedVisual);
    RELEASE(pSelectedFace);

		/* Reset the description of the selected object */
    SelectedPickDesc.lGroupIdx = 0;
    SelectedPickDesc.ulFaceIdx = 0;
    HasSelectedObject = FALSE;
   }

 }
/*===========================================================================
 *		End of Class Method CD3DApp::UnSelectObject()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CD3DApp::UpdateBitsPerPixel()"
/*===========================================================================
 *
 * Class CD3DApp Method - boolean UpdateBitsPerPixel (void);
 *
 * Updates the BitsPerPixel member variable from the current settings 
 * of the main application window.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CD3DApp::UpdateBitsPerPixel (void) {
  HDC hDC; 

	/* Attempt to get a Device Context from the main window */
  hDC = GetDC(hMainWindow);

  if (hDC == NULL) { 
    SET_WIN_ERROR2("Failed to retrieve a device context for the main window!");
    return (FALSE);
   }

	/* Record the BPP from the device capabilities */
  BitsPerPixel = GetDeviceCaps(hDC, BITSPIXEL);

	/* Release the Device Context and return success */
  ReleaseDC(hMainWindow, hDC);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CD3DApp::UpdateBitsPerPixel()
 *=========================================================================*/