home *** CD-ROM | disk | FTP | other *** search
/ Learn 3D Graphics Programming on the PC / Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso / rwwin / rfview.c_ / rfview.bin
Text File  |  1995-11-14  |  145KB  |  3,775 lines

  1. /**********************************************************************
  2.  *
  3.  * File :     rfView.c
  4.  *
  5.  * Abstract : Object viewer rfView.c mostly taken from rwView
  6.  *
  7.  *            This application had been written to be compatible with
  8.  *            both the fixed and floating-point versions of the
  9.  *            RenderWare library, i.e., it uses the macros CREAL,
  10.  *            INT2REAL, RAdd, RDiv, RSub etc. If your application is
  11.  *            intended for the floating-point version of the library
  12.  *            only these macros are not necessary.
  13.  *
  14.  *            Please note that this application is intended for
  15.  *            demonstration purposes only. No support will be
  16.  *            provided for this code and it comes with no warranty.
  17.  *
  18.  **********************************************************************
  19.  *
  20.  * This file is a product of Criterion Software Ltd.
  21.  *
  22.  * This file is provided as is with no warranties of any kind and is
  23.  * provided without any obligation on Criterion Software Ltd. or
  24.  * Canon Inc. to assist in its use or modification.
  25.  *
  26.  * Criterion Software Ltd. will not, under any
  27.  * circumstances, be liable for any lost revenue or other damages arising
  28.  * from the use of this file.
  29.  *
  30.  * Copyright (c) 1994, 1995 Criterion Software Ltd.
  31.  * All Rights Reserved.
  32.  *
  33.  * RenderWare is a trademark of Canon Inc.
  34.  *
  35.  **********************************************************************/
  36.  
  37. #if       defined(__WINDOWS_386__)
  38.  
  39. /*
  40.  * Watcom strangeness...
  41.  */
  42. #define  INCLUDE_SHELLAPI_H
  43. #define  INCLUDE_COMMDLG_H
  44. #include <windows.h>
  45.  
  46. #else
  47.  
  48. #include <windows.h>
  49. #include <shellapi.h>
  50. #include <commdlg.h>
  51.  
  52. #endif
  53.  
  54. #include <stdlib.h>
  55. #include <stdio.h>
  56. #include <string.h>
  57. #include <math.h>
  58.  
  59. #include <rwlib.h>
  60. #include <rwwin.h>
  61.  
  62. #include "resource.h"
  63.  
  64. #include "common.h"
  65. #include "pal.h"
  66. #include "settings.h"
  67. #include "object.h"
  68. #include "popmenu.h"
  69. #include "lightprp.h"
  70. #include "clumpprp.h"
  71. #include "color.h"
  72. #include "pick.h"
  73.  
  74. #define MAIN
  75. #include "rfview.h"
  76.  
  77. #include "viewdlg.h"
  78. #include "booksub.h" 
  79. #include "viewsub.h"
  80. #include "spline.h"
  81.  
  82.  
  83. LRESULT CALLBACK
  84. MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
  85.  
  86.  
  87. /**********************************************************************/
  88.  
  89. /*
  90.  * Perform any necessary Windows application initialization. Basically,
  91.  * this means registering the window class for this application.
  92.  */
  93. static BOOL
  94. InitApplication(HANDLE instance)
  95. {
  96.     WNDCLASS wndClass;
  97.  
  98.     wndClass.style         = CS_BYTEALIGNWINDOW;
  99.     wndClass.lpfnWndProc   = (WNDPROC)MainWndProc;
  100.     wndClass.cbClsExtra    = 0; 
  101.     wndClass.cbWndExtra    = 0; 
  102.     wndClass.hInstance     = instance; 
  103.     wndClass.hIcon         = NULL;
  104.     wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  105.     wndClass.hbrBackground = NULL; 
  106.     wndClass.lpszMenuName  = MAKEINTRESOURCE(IDR_RWVIEW_MENU); 
  107.     wndClass.lpszClassName = RWVIEWCLASSNAME;
  108.  
  109.     if (RegisterClass(&wndClass) != (ATOM)0)
  110.         return TRUE;
  111.     else
  112.         return FALSE;
  113. }
  114.  
  115. /**********************************************************************/
  116.  
  117. /*
  118.  * Perform any necessary initialization for this instance of the
  119.  * application. This simply means creating the application's main
  120.  * window.
  121.  */
  122. static HWND
  123. InitInstance(HANDLE instance)
  124. {
  125.     /*
  126.      * Create the MS Window's window instance for this application. The
  127.      * initial window size is given by DEFAULT_WINDOW_WIDTH and
  128.      * DEFAULT_WINDOW_HEIGHT.
  129.      */
  130.     return CreateWindow(RWVIEWCLASSNAME,
  131.                         WINDOWTITLE,
  132.                         WS_OVERLAPPEDWINDOW,
  133.                         CW_USEDEFAULT, CW_USEDEFAULT,
  134.                         DEFAULTWINDOWWIDTH, DEFAULTWINDOWHEIGHT,
  135.                         NULL, NULL, instance, NULL);
  136. }
  137.  
  138. /**********************************************************************/
  139.  
  140. /*
  141.  * Ensure that the depth of the output display is acceptable.
  142.  * RenderWare will operate correctly at all display depths
  143.  * but it works best when running on an 8-bit or 16-bit
  144.  * display. If the display depth is not 8-bit or 16-bit we
  145.  * will display a message explaining the situation and
  146.  * ask the user if she or he wishes to continue.
  147.  */
  148. static BOOL
  149. IsGoodDisplayDepth(HWND window)
  150. {
  151.     HDC  dc;
  152.     int  depth;
  153.     char colString[10];
  154.     char buffer[400];
  155.  
  156.     dc = GetDC(window);
  157.     depth = GetDeviceCaps(dc, BITSPIXEL);
  158.     ReleaseDC(window, dc);
  159.     if ((depth != 8) && (depth != 16))
  160.     {
  161.         if (depth < 8)
  162.             wsprintf(colString, "%d", 1 << depth);
  163.         else
  164.             strcpy(colString, "true");
  165.         wsprintf(buffer,
  166. "Your video adapter is currently running in %s color mode.\n"
  167. "Although the Viewer will operate correctly in this mode,\n"
  168. "we recommend that you change the mode of your video adapter\n"
  169. "to 256 color mode or 65536 color mode.\n"
  170. "Do you wish to continue?",
  171.                  colString);
  172.                  
  173.         if (MessageBox(window, buffer, WINDOWTITLE,
  174.                        MB_YESNO | MB_APPLMODAL | MB_ICONEXCLAMATION) == IDYES)
  175.             return TRUE;
  176.         else
  177.             return FALSE;
  178.     }
  179.     else
  180.     {
  181.         return TRUE;
  182.     }
  183. }
  184.  
  185. /**********************************************************************/
  186.  
  187. /*
  188.  * Reset the camera to its default position and orientation. We could
  189.  * use RwResetCamera() instead but we wish to leave the other camera
  190.  * settings such as viewwindow alone.
  191.  */
  192. static void
  193. ResetCamera(RwCamera *camera)
  194. {
  195.     RwPushScratchMatrix();
  196.         RwScaleMatrix(RwScratchMatrix(),
  197.                       CREAL(-1.0), CREAL(1.0), CREAL(1.0),
  198.                       rwREPLACE);
  199.         RwTranslateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(0.0),
  200.                           DEFAULTCAMERADISTANCE, rwPOSTCONCAT);
  201.         RwTransformCamera(camera, RwScratchMatrix(), rwREPLACE);
  202.     RwPopScratchMatrix();
  203. }
  204.  
  205. /**********************************************************************/
  206.  
  207. /*
  208.  * Size the highlight box to fit the given clump.
  209.  */
  210. static RwClump *
  211. SizeHighlightToFitClump(RwClump *clump)
  212. {
  213.     RwV3d  bll;
  214.     RwV3d  fur;
  215.     RwReal w;
  216.     RwReal h;
  217.     RwReal d;
  218.     RwReal x;
  219.     RwReal y;
  220.     RwReal z;
  221.     
  222.     RwGetClumpLocalBBox(clump, &bll, &fur);
  223.         
  224.     /*
  225.      * Scale the highlight clump to fit.
  226.      */
  227.     w = RSub(fur.x, bll.x);
  228.     h = RSub(fur.y, bll.y);
  229.     d = RSub(fur.z, bll.z);
  230.     x = RAdd(bll.x, RDiv(w, CREAL(2.0)));
  231.     y = RAdd(bll.y, RDiv(h, CREAL(2.0)));
  232.     z = RAdd(bll.z, RDiv(d, CREAL(2.0)));
  233.     RwPushScratchMatrix();
  234.         RwScaleMatrix(RwScratchMatrix(), w, h, d, rwREPLACE);
  235.         RwTranslateMatrix(RwScratchMatrix(), x, y, z, rwPOSTCONCAT);
  236.         RwTransformClumpJoint(HighlightClump, RwScratchMatrix(), rwREPLACE);
  237.     RwPopScratchMatrix();
  238.     
  239.     return clump;
  240. }
  241.  
  242. /**********************************************************************/
  243.  
  244. static void
  245. SelectClump(HWND window, RwClump *clump)
  246. {
  247.     /*
  248.      * Remember the currently picked object.
  249.      */
  250.     SelectedClump = clump;
  251.     
  252.     /*
  253.      * Don't spin it until the user has rotated the object.
  254.      */        
  255.     SpinClump = FALSE;
  256.     
  257.     /*
  258.      * Size the highlight to fit.
  259.      */
  260.     SizeHighlightToFitClump(clump);
  261.     
  262.     /*
  263.      * Update the menus to reflect the picked clump.
  264.      */
  265.     EnableMenuItem(GetMenu(window), IDM_EDIT_DELETE,    MF_BYCOMMAND | MF_ENABLED);
  266.     EnableMenuItem(GetMenu(window), IDM_EDIT_DUPLICATE, MF_BYCOMMAND | MF_ENABLED);
  267.     EnableMenuItem(GetMenu(window), IDM_CAMERA_LOOKAT,  MF_BYCOMMAND | MF_ENABLED);
  268.     EnableMenuItem(GetMenu(window), IDM_CAMERA_MOVETO,  MF_BYCOMMAND | MF_ENABLED);
  269.     
  270.     
  271.     switch(GETOBJECTTYPE(clump))
  272.     {
  273.      case rfIsLight:
  274.         EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
  275.         EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET,      MF_BYCOMMAND | MF_GRAYED);
  276.         EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
  277.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVE,        MF_BYCOMMAND | MF_GRAYED);
  278.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS,      MF_BYCOMMAND | MF_GRAYED);
  279.      break;
  280.      case rfIsClump: case rfNoMoveClump:
  281.         EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
  282.         EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET,      MF_BYCOMMAND | MF_ENABLED);
  283.         EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
  284.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVE,        MF_BYCOMMAND | MF_ENABLED);
  285.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS,      MF_BYCOMMAND | MF_ENABLED);     
  286.      break;
  287.      case rfIsSprite:
  288.         EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
  289.         EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET,      MF_BYCOMMAND | MF_ENABLED);
  290.         EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
  291.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVE,        MF_BYCOMMAND | MF_ENABLED);
  292.         EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS,      MF_BYCOMMAND | MF_ENABLED);         
  293.      break;
  294.      }
  295. }
  296.  
  297.  
  298.  
  299. /**********************************************************************/
  300.  
  301. /*
  302.  * This function initializes the 3D (i.e. RenderWare) components of the
  303.  * application. This function opens the RenderWare library, creates a
  304.  * camera, a scene, a light and a matrix for spinning.
  305.  */
  306. static BOOL
  307. Init3D(HWND window)
  308. {
  309.     char        shapePath[RWMAXPATHLEN];
  310.     char       *s;
  311.     RwRaster   *backdrop;
  312.     RwRGBColor  color;
  313.     /*
  314.      * Check the display depth and if it is not one of the optimal depths
  315.      * for RenderWare (8-bit or 16-bit). If it is not then warn the user
  316.      * and check whether they wish to continue or not.
  317.      */
  318.     if (!IsGoodDisplayDepth(window))            return FALSE;    // CHANGE DISPLAY DEPTH
  319.        
  320.     /*
  321.      * Attempt to open (and initialize) the RenderWare library.
  322.      * Explicitly specifying the Windows driver.
  323.      */
  324.  
  325.     if (!RwOpen("MSWindows", NULL))
  326.     {
  327.         MessageBox(window,
  328.                    "Could not open the RenderWare library",
  329.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  330.         return FALSE;
  331.     }
  332.  
  333.   /*
  334.      arg.option = rwWINUSEDIBS;
  335.      arg.value = 0L;  // ignored for rwWINUSEDUBS
  336.      if (!RwOpenExt("MSWindows", NULL,1,&arg))
  337.     {
  338.         MessageBox(window,
  339.                    "Could not open the RenderWare library",
  340.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  341.         return FALSE;
  342.     }
  343.   */
  344.     
  345.     /*
  346.      * Create the camera which will be used for rendering. The initial window
  347.      * size the application will create is given by DEFAULT_WINDOW_WIDTH and
  348.      * DEFAULT_WINDOW_HEIGHT. However, when we create a camera we have to
  349.      * specify the largest size that camera's viewport can ever be. As the
  350.      * camera's viewport is the same size as the window's client area we
  351.      * have to specify the size as the maximum size of the window.
  352.      *
  353.      * If we compile with CONSTRAIN_SIZE then we restrict the window size
  354.      * (using WM_GETMINMAXINFO) to MAXIMUMWINDOWWIDTH by MAXIMUMWINDOWHEIGHT.
  355.      * If CONTRAIN_SIZE is not specified we let the window grow to the full
  356.      * size of the screen.
  357.      */
  358. #if defined(CONSTRAIN_SIZE)
  359.     Camera = RwCreateCamera(MAXIMUMWINDOWWIDTH, MAXIMUMWINDOWHEIGHT, NULL);
  360. #else
  361.     Camera = RwCreateCamera((RwInt32)GetSystemMetrics(SM_CXFULLSCREEN),
  362.                             (RwInt32)GetSystemMetrics(SM_CYFULLSCREEN), NULL);
  363. #endif
  364.     if (Camera == NULL)
  365.     {
  366.         /*
  367.          * As with RwOpen(), the most common cause for a failure to create
  368.          * a camera is insufficient memory so we will explicitly check for
  369.          * this condition and report it. Otherwise a general error is issued.
  370.          */
  371.         if (RwGetError() == E_RW_NOMEM)
  372.         {
  373.             MessageBox(window,
  374.                        "Insufficient memory to create the 3D camera",
  375.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  376.         }
  377.         else
  378.         {
  379.             MessageBox(window, "Could not create the 3D camera",
  380.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  381.         }
  382.         RwClose();
  383.  
  384.         return FALSE;
  385.     }
  386.  
  387.     /*
  388.      * Set the camera's background color to the color saved in the
  389.      * initialization file. The initialization file stores the
  390.      * colors as a Windows format COLORREF so we need to convert
  391.      * it to a RenderWare RwRGBColor.
  392.      */
  393.     ColorRefToRGB(BackgroundColor, &color);
  394.     RwSetCameraBackColorStruct(Camera, &color);
  395.  
  396.     /*
  397.      * Move the camera to its default position.
  398.      */
  399.     ResetCamera(Camera);
  400.     
  401.     /*
  402.      * If we have read a non-empty backdrop filename from the
  403.      * settings file attempt to load that backdrop raster. If
  404.      * we don't succeed we don't consider it a fatal error -
  405.      * we simply continue.
  406.      */
  407.     if (BackdropFileName[0] != '\0')
  408.     {
  409.         backdrop = RwReadRaster(BackdropFileName,
  410.                                 rwGAMMARASTER | rwDITHERRASTER);
  411.         if (backdrop != NULL)
  412.             RwSetCameraBackdrop(Camera, backdrop);
  413.     }
  414.     
  415.  
  416.     BirdsEyeCamera = RwCreateCamera(128, 128, NULL);
  417.     if (BirdsEyeCamera == NULL)
  418.     {
  419.         /*
  420.          * As with RwOpen(), the most common cause for a failure to create
  421.          * a camera is insufficient memory so we will explicitly check for
  422.          * this condition and report it. Otherwise a general error is issued.
  423.          */
  424.         if (RwGetError() == E_RW_NOMEM)
  425.         {
  426.             MessageBox(window,
  427.                        "Insufficient memory to create the bird's eye camera",
  428.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  429.         }
  430.         else
  431.         {
  432.             MessageBox(window, "Could not create the bird's eye camera",
  433.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  434.         }
  435.         RwDestroyCamera(Camera);
  436.         RwClose();
  437.  
  438.         return FALSE;
  439.     }
  440.                                                        
  441.     /*
  442.      * Initialize the bird's eye camera for a nice viewpoint and fixed
  443.      * 128 x 128 viewport (the size of a texture).
  444.      */
  445.   
  446.     RwSetCameraPosition(BirdsEyeCamera, CREAL(0.0), CREAL(3.0), CREAL(0.0));
  447.     RwPointCamera(BirdsEyeCamera, CREAL(0.0), CREAL(0.0), CREAL(0.0) );
  448.     RwSetCameraBackColor(BirdsEyeCamera, CREAL(0.0), CREAL(0.0), CREAL(1.0));
  449.     RwSetCameraViewport(BirdsEyeCamera, 0, 0, 128, 128);
  450.  
  451.     
  452.     /*
  453.      * Create the initial scene for the viewer. The initial
  454.      * scene consists of a default light and a new texture
  455.      * dictionary to store the textures loaded by clumps
  456.      * added to the scene.
  457.      */
  458.     Scene = CreateViewerScene();
  459.     if (Scene == NULL)
  460.     {
  461.         RwDestroyCamera(Camera);
  462.         Camera = NULL;
  463.         RwClose();
  464.         MessageBox(window, "Could not create the 3D Scene",
  465.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  466.         return FALSE;
  467.     }
  468.         
  469.     /*
  470.      * Create the spin matrix.
  471.      */
  472.     SpinMatrix = RwCreateMatrix();
  473.     if (SpinMatrix == NULL)
  474.     {
  475.         DestroyViewerScene(Scene);
  476.         Scene = NULL;
  477.         RwDestroyCamera(Camera);
  478.         Camera = NULL;
  479.         RwClose();
  480.         MessageBox(window,
  481.                    "Could not create the RenderWare matrix",
  482.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  483.         return FALSE;
  484.     }
  485.     
  486.     /*
  487.      * Create the highlight clump. The highlight clump is a yellow, wireframe
  488.      * box which surrounds the picked clump.
  489.      */
  490.     RwModelBegin();
  491.         RwClumpBegin();
  492.             RwSetSurface(CREAL(0.75), CREAL(0.0), CREAL(0.0));
  493.             RwSetSurfaceColor(CREAL(1.0), CREAL(1.0), CREAL(0.0));
  494.             RwSetSurfaceGeometrySampling(rwWIREFRAME);
  495.             
  496.             RwBlock(CREAL(1.0), CREAL(1.0), CREAL(1.0));
  497.         RwClumpEnd(&HighlightClump);
  498.     RwModelEnd();
  499.     
  500.     if (HighlightClump == NULL)
  501.     {
  502.         DestroyViewerScene(Scene);
  503.         Scene = NULL;
  504.         RwDestroyCamera(Camera);
  505.         Camera = NULL;
  506.         RwClose();
  507.         MessageBox(window, "Could not create the highlight object",
  508.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  509.         return FALSE;
  510.     }
  511.     
  512.     /*
  513.      * Initialize the last opened filename with the first directory
  514.      * in the RenderWare search path (if any). This gives us a good
  515.      * chance of opening into a directory with interesting scripts
  516.      * in it.
  517.      */
  518.     RwGetShapePath(shapePath);
  519.     if (shapePath[0] == '\0')
  520.     {
  521.         /*
  522.          * The shape path is empty so initialize the last filename
  523.          * opened to empty.
  524.          */
  525.         LastFileName[0] = '\0';
  526.          
  527.     }
  528.     else
  529.     {
  530.         s = strchr(shapePath, ';');
  531.         if (s != NULL)
  532.         {
  533.             /*
  534.              * We have located the end of the first directory.
  535.              * So truncate the shape path to this point.
  536.              */
  537.             *s = '\0';
  538.         }
  539.         
  540.         /*
  541.          * Copy the directory to the last filename. Append a backquote
  542.          * so we know this is a directory and not a full filename.
  543.          */
  544.         strcpy(LastFileName, shapePath);
  545.         strcat(LastFileName, "\\");        
  546.     }
  547.  
  548.     /*
  549.      * All the 3D components are now successfully initialized, so 
  550.      * work can begin...
  551.      */
  552.     RwIsOpen = TRUE;
  553.  
  554.     return TRUE;
  555. }
  556.  
  557. /**********************************************************************/
  558.  
  559. /*
  560.  * This function shuts down the 3D (i.e. RenderWare) components of the
  561.  * application in a polite fashion.
  562.  */
  563. static void
  564. TidyUp3D(void)
  565. {
  566.     /*
  567.      * Destroy the highlight clump.
  568.      */
  569.     if (HighlightClump != NULL)
  570.         RwDestroyClump(HighlightClump);
  571.     
  572.     /*
  573.      * Destroy the spin matrix.
  574.      */
  575.     if (SpinMatrix != NULL)
  576.         RwDestroyMatrix(SpinMatrix);
  577.    
  578.     /*
  579.      * Destroy the scene. This will destroy the scene itself, the contents of
  580.      * the scene, i.e. any clumps and lights in that scene and any textures
  581.      * loaded by the clumps in the scene.
  582.      */
  583.     if (Scene != NULL)
  584.         DestroyViewerScene(Scene);
  585.     
  586.     if (Camera != NULL)
  587.     {
  588.         /*
  589.          * Destroy the camera's backdrop (if any). Backdrops are not
  590.          * automatically destroyed by RwDestroyCamera() so we must
  591.          * manually destroy the backdrop.
  592.          */
  593.         if (RwGetCameraBackdrop(Camera))
  594.             RwDestroyRaster(RwGetCameraBackdrop(Camera));
  595.  
  596.         /*
  597.          * Destroy the camera.
  598.          */
  599.         RwDestroyCamera(Camera);
  600.     }
  601.  
  602.     /*
  603.      * Close the library. This will free up any internal resources and
  604.      * textures loaded.
  605.      */
  606.     RwClose();
  607. }
  608.  
  609.  
  610. /**********************************************************************/
  611.  /*
  612.  * Attempt to load an object from the given file. The actual object type
  613.  * to load is determined from the file extension. This function will
  614.  * read RenderWare clumps (.rwx), Backdrops (.bmp), Palettes (.pal) and Splines (.rrc)
  615.  */
  616. static BOOL
  617. LoadObject(HWND window, char *pathName)
  618. {
  619.     HCURSOR         oldCursor;
  620.     RwClump        *clump;
  621.     RwRaster       *backdrop;
  622.     char            buffer[128];
  623.     char            drive[_MAX_DRIVE];
  624.     char            dir[_MAX_DIR];
  625.     char            fName[_MAX_FNAME];
  626.     char            ext[_MAX_EXT];
  627.     RwInt32         numPalEntries;
  628.     RwPaletteEntry  palEntries[256];
  629.     RwInt32         firstEntry;
  630.     RwInt32         lastEntry;
  631.     int len;
  632.     FILE *file;
  633.         
  634.     /*
  635.      * We use the file extension to decide what kind of object we
  636.      * are reading so we decompose the filename into its components.
  637.      */
  638.     _splitpath(pathName, drive, dir, fName, ext);
  639.     
  640.     if (stricmp(ext, ".rwx") == 0)
  641.     {
  642.         /*
  643.          * Its a RenderWare script file so read the clump
  644.          * and add it to the viewer's scene.
  645.          */
  646.          
  647.         /*
  648.          * This may take some time so show the wait cursor.
  649.          */
  650.         oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  651.         clump = ReadClumpObj(pathName);
  652.         SetCursor(oldCursor);
  653.         if (clump != NULL)
  654.         {
  655.             AddClumpObjToScene(Scene, clump);
  656.             return TRUE;
  657.         }
  658.         else
  659.         {
  660.             /*
  661.              * The read failed. There are a large number of reasons why this
  662.              * could happen. We will issue specific messages for the most
  663.              * common errors and a generic catch all for the rest.
  664.              */
  665.             switch (RwGetError())
  666.             {                    
  667.                 case E_RW_NOFILE:
  668.                 case E_RW_BADOPEN:
  669.                     /*
  670.                      * Could not open the file...
  671.                      */
  672.                     sprintf(buffer, "The file %s could not be opened", pathName);
  673.                     MessageBox(window, buffer, WINDOWTITLE,
  674.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  675.                     break;
  676.  
  677.                 case E_RW_NOMEM:
  678.                     /*
  679.                      * Ran out of memory...
  680.                      */
  681.                     sprintf(buffer, "Insufficient memory to load object %s",
  682.                             pathName);
  683.                     MessageBox(window, buffer, WINDOWTITLE,
  684.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  685.                     break;
  686.                     
  687.                 default:
  688.                     /*
  689.                      * Generic, catch all error message.
  690.                      */
  691.                     sprintf(buffer, "The 3D object %s could not be read",
  692.                             pathName);
  693.                     MessageBox(window, buffer, WINDOWTITLE,
  694.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  695.                     break;
  696.             }
  697.             return FALSE;
  698.         }        
  699.     }
  700.     else 
  701.        if (stricmp(ext, ".bmp") == 0)
  702.        {
  703.         if(!rfReadSprite)
  704.          {
  705.         /*
  706.          * Its a Windows bitmap file so read the bitmap as a
  707.          * raster and make it the backdrop of the camera.
  708.          */
  709.  
  710.         /*
  711.          * This may take some time so show the wait cursor.
  712.          */
  713.         oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  714.         backdrop = RwReadRaster(pathName, rwGAMMARASTER | rwDITHERRASTER);
  715.         SetCursor(oldCursor);
  716.         if (backdrop != NULL)
  717.            {
  718.             if (RwGetCameraBackdrop(Camera) != NULL)
  719.                 /*
  720.                  * The camera has an existing backdrop so destroy it.
  721.                  */
  722.                 RwDestroyRaster(RwGetCameraBackdrop(Camera));
  723.             RwSetCameraBackdrop(Camera, backdrop);
  724.             EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE, MF_BYCOMMAND | MF_ENABLED);
  725.             
  726.             /*
  727.              * Update the setting's backdrop filename and flag the fact
  728.              * that the settings have changed.
  729.              */
  730.             strcpy(BackdropFileName, pathName);
  731.             SettingsChanged = TRUE;
  732.             
  733.             UpdateCameraBackdropPosition(Camera);
  734.             return TRUE;
  735.         }
  736.        }
  737.        else  // load (.bmp) onto a sprite
  738.        {
  739.         clump = CreateSpriteObj(pathName);       // create the sprite
  740.         if (clump != NULL)
  741.         {
  742.             RwAddClumpToScene(Scene, clump);
  743.             return TRUE;
  744.         }
  745.        }
  746.  
  747.             /*
  748.              * The read failed. There are a large number of reasons why this
  749.              * could happen. We will issue specific messages for the most
  750.              * common errors and a generic catch all for the rest.
  751.              */
  752.             switch (RwGetError())
  753.             {                    
  754.                 case E_RW_NOFILE:
  755.                 case E_RW_BADOPEN:
  756.                     /*
  757.                      * Could not open the file...
  758.                      */
  759.                     sprintf(buffer, "The file %s could not be opened", pathName);
  760.                     MessageBox(window, buffer, WINDOWTITLE,
  761.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  762.                     break;
  763.  
  764.                 case E_RW_NOMEM:
  765.                     /*
  766.                      * Ran out of memory...
  767.                      */
  768.                     sprintf(buffer, "Insufficient memory to load backdrop %s",
  769.                             pathName);
  770.                     MessageBox(window, buffer, WINDOWTITLE,
  771.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  772.                     break;
  773.                     
  774.                 default:
  775.                     /*
  776.                      * Generic, catch all error message.
  777.                      */
  778.                     sprintf(buffer, "The backdrop %s could not be read",
  779.                             pathName);
  780.                     MessageBox(window, buffer, WINDOWTITLE,
  781.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  782.                     break;
  783.             }
  784.             return FALSE;
  785.  
  786.     }
  787.     else if (stricmp(ext, ".pal") == 0)
  788.     {
  789.         /*
  790.          * This may take some time so show the wait cursor.
  791.          */
  792.         oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  793.         numPalEntries = ReadPalette(pathName, palEntries);
  794.         SetCursor(oldCursor);
  795.         
  796.         if (numPalEntries > 0)
  797.         {
  798.             /*
  799.              * We have successfully read a new palette. Textures are
  800.              * not rematched when we load a new palette so they will
  801.              * look strange if we simply set the new palette. To avoid
  802.              * this potential visual strangeness we discard all textures
  803.              * and geometry when we load a new palette. We obviously
  804.              * don't want to do this without user confirmation, so ask
  805.              * the user if she or he really wishes to change the palette.
  806.              */
  807.             if (MessageBox(window,
  808.                            "In order to load a new palette the viewer must\n"
  809.                            "discard the contents of the 3D Scene.\n"
  810.                            "Do you wish to continue?",
  811.                            WINDOWTITLE,
  812.                            MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
  813.             {
  814.                 /*
  815.                  * Destroy the current scene.
  816.                  */
  817.                 DestroyViewerScene(Scene);
  818.                  
  819.                 /*
  820.                  * It is also necessary to destory the camera's backdrop (as it
  821.                  * will have been matched against the previous palette).
  822.                  */
  823.                 if (RwGetCameraBackdrop(Camera) != NULL)
  824.                 {
  825.                     RwDestroyRaster(RwGetCameraBackdrop(Camera));
  826.                     RwSetCameraBackdrop(Camera, NULL);
  827.                 }
  828.  
  829.                 /*
  830.                  * Create the new scene.
  831.                  */
  832.                 Scene = CreateViewerScene();
  833.                 if (Scene == NULL)
  834.                 {
  835.                     /*
  836.                      * Failing to create the new scene is a fatal error.
  837.                      * Display an error message and exit.
  838.                      */
  839.                     MessageBox(window, "Could not create the new 3D Scene",
  840.                                WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
  841.                     SendMessage(window, WM_CLOSE, 0, 0L);
  842.                     return FALSE;
  843.                 }
  844.                 
  845.                 /*
  846.                  * As this is a new world there is no picked object yet.
  847.                  */
  848.                 DeselectClump(window);
  849.                 
  850.                 /*
  851.                  * Set the new palette.
  852.                  */
  853.                 RwGetDeviceInfo(rwFIRSTPALETTEENTRY, &firstEntry, sizeof(firstEntry));
  854.                 RwGetDeviceInfo(rwLASTPALETTEENTRY, &lastEntry, sizeof(lastEntry));
  855.                 if (numPalEntries > ((lastEntry - firstEntry)))
  856.                     numPalEntries = ((lastEntry - firstEntry));
  857.                 RwSetPaletteEntries(firstEntry, numPalEntries, palEntries, 0L);
  858.                 
  859.                 return TRUE;
  860.             }
  861.             return FALSE;
  862.         }
  863.         else
  864.         {
  865.             /*
  866.              * Generic, catch all error message.
  867.              */
  868.             sprintf(buffer, "The palette %s could not be read",
  869.                     pathName);
  870.             MessageBox(window, buffer, WINDOWTITLE,
  871.                        MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  872.             return FALSE;
  873.         }
  874.     }
  875.     else if (stricmp(ext, ".rrc") == 0)  // read a spline
  876.     {
  877.      file = fopen(pathName, "r");
  878.      if (file != NULL) {
  879.       /*
  880.        * The file should be a spline
  881.       */
  882.       coaster = (RollerCoasterType *)malloc(sizeof(RollerCoasterType));
  883.       if(coaster!=NULL)
  884.        {
  885.          /* Read the description of the rollercoaster. */
  886.          fgets(coaster->description, sizeof(coaster->description), file);
  887.          len = strlen(coaster->description);
  888.          if ((len > 0) && (coaster->description[len - 1] == '\n'))
  889.            coaster->description[len - 1] = '\0';
  890.   
  891.          coaster->spline = ReadCoasterSpline(file);
  892.          if (coaster->spline == NULL)
  893.           {
  894.            free(coaster);
  895.            fclose(file);
  896.            return FALSE;
  897.           }
  898.            
  899.          /* Create the clumps representing the control points of the spline. */
  900.          if (!CreateCoasterControlPointClumps(coaster))
  901.           {
  902.            free(coaster);
  903.            fclose(file);
  904.            return FALSE;
  905.           }
  906.           
  907.          /* Convert the spline description into a coaster clump. */
  908.          coaster->coasterClump = CreateCoasterClump(coaster);
  909.          if (coaster->coasterClump == NULL)
  910.          {
  911.            RwDestroySpline(coaster->spline);
  912.            free(coaster);
  913.            fclose(file);
  914.            return FALSE;
  915.          }
  916.          RwAddClumpToScene(Scene, coaster->coasterClump);
  917.         
  918.         /* Initialize the spline parameter and spline parameter delta. */
  919.         coaster->t      = CREAL(0.0);
  920.         coaster->tDelta = DEFAULTSPLINEDELTA;
  921.         
  922.         /* Save the filename. */
  923.         strcpy(coaster->filename, pathName);
  924.         RenderScene(window); 
  925.        }
  926.       }
  927.      fclose(file);
  928.      return TRUE;
  929.      }
  930.     else
  931.      {
  932.         /*
  933.          * The file type was unknown so flag an error and fail.
  934.          */
  935.         wsprintf(buffer, "The format of %s is unknown", pathName); 
  936.         MessageBox(window, buffer, WINDOWTITLE,
  937.                    MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  938.         return FALSE;
  939.      }
  940.  
  941. /**********************************************************************/
  942.  
  943. /*
  944.  * Parse the names of any .rwx files on the command line, read those
  945.  * files and add them to the scene. This allows RwView to be associated
  946.  * with .rwx files in the file manager and be started automatically when
  947.  * a .rwx file is double-clicked.
  948.  */
  949. static BOOL
  950. ReadFromCommandLine(HWND window, char *cmdLine)
  951. {
  952.     char *s1;
  953.     char *s2;
  954.     char  fileName[_MAX_PATH];
  955.  
  956.     s1 = &cmdLine[0];
  957.     s2 = &fileName[0];
  958.     while (*s1)
  959.     {
  960.         /*
  961.          * Extract one file name from the list.
  962.          */
  963.         while (*s1 && (*s1 != ' '))
  964.             *s2++ = *s1++;
  965.         *s2 = '\0';
  966.  
  967.         if (fileName[0])
  968.         {
  969.             /*
  970.              * Attempt to load an object. The actual object type is
  971.              * determined by LoadObject() from the extension of the file
  972.              * name.
  973.              *
  974.              * NOTE: We don't include these files in the MRU file list.
  975.              */
  976.             if (!LoadObject(window, fileName))
  977.                 return FALSE;
  978.         }
  979.  
  980.         s2 = &fileName[0];
  981.     }
  982.     return TRUE;
  983. }
  984.  
  985. /**********************************************************************/
  986.  
  987. #if defined(CONSTRAIN_SIZE)
  988.  
  989. /*
  990.  * Handle queries about the window's maximum extent. We only do this
  991.  * if we are constraining the size of the window.
  992.  */
  993. static void
  994. OnGetMinMaxInfo(MINMAXINFO FAR *minMaxInfo)
  995. {
  996.     /*
  997.      * Constraint the window to the maximum size defined by the constants
  998.      * MAXIMUM_WINDOW_WIDTH and MAXIMUM_WINDOW_HEIGHT.
  999.      */
  1000.     minMaxInfo->ptMaxSize.x      = MAXIMUMWINDOWWIDTH;
  1001.     minMaxInfo->ptMaxSize.y      = MAXIMUMWINDOWHEIGHT;
  1002.     minMaxInfo->ptMaxTrackSize.x = MAXIMUMWINDOWWIDTH;
  1003.     minMaxInfo->ptMaxTrackSize.y = MAXIMUMWINDOWHEIGHT;
  1004. }
  1005.  
  1006. #endif
  1007.  
  1008. /**********************************************************************/
  1009.  
  1010. /*
  1011.  * Handle measure item requests. Currently we user ownerdraw items for
  1012.  * the titles of the popup menus. This function computes the size
  1013.  * necessary for these items.
  1014.  */
  1015. static void
  1016. OnMeasureItem(HWND window, MEASUREITEMSTRUCT FAR *measureItem)
  1017. {
  1018.     HDC  dc;
  1019.     SIZE size;
  1020.     
  1021.     switch (measureItem->itemID)
  1022.     {
  1023.         case IDM_LIGHTPOPUP_LABEL:
  1024.             dc = GetDC(window);
  1025.             MeasurePopupMenuLabel(dc, "Light", &size);
  1026.             ReleaseDC(window, dc);
  1027.             measureItem->itemWidth  = size.cx;
  1028.             measureItem->itemHeight = size.cy;
  1029.             break;
  1030.         case IDM_CLUMPPOPUP_LABEL:
  1031.             dc = GetDC(window);
  1032.             MeasurePopupMenuLabel(dc, "Clump", &size);
  1033.             ReleaseDC(window, dc);
  1034.             measureItem->itemWidth  = size.cx;
  1035.             measureItem->itemHeight = size.cy;
  1036.             break;
  1037.         case IDM_BACKGROUNDPOPUP_LABEL:
  1038.             dc = GetDC(window);
  1039.             MeasurePopupMenuLabel(dc, "Background", &size);
  1040.             ReleaseDC(window, dc);
  1041.             measureItem->itemWidth  = size.cx;
  1042.             measureItem->itemHeight = size.cy;
  1043.             break;
  1044.     }
  1045. }
  1046.  
  1047. /**********************************************************************/
  1048.  
  1049. static void
  1050. OnDrawItem(HWND window, DRAWITEMSTRUCT FAR *drawItem)
  1051. {
  1052.     HDC  dc;
  1053.     RECT rect;
  1054.     window=window;  // avoid warning 
  1055.     /*
  1056.      * Cache interesting stuff for eash access.
  1057.      */
  1058.     dc   = drawItem->hDC;
  1059.     rect = drawItem->rcItem;
  1060.             
  1061.     switch (drawItem->itemID)
  1062.     {
  1063.         case IDM_LIGHTPOPUP_LABEL:
  1064.             DrawPopupMenuLabel(dc, &rect, "Light");
  1065.             break;
  1066.  
  1067.         case IDM_CLUMPPOPUP_LABEL:
  1068.             DrawPopupMenuLabel(dc, &rect, "Clump");
  1069.             break;
  1070.  
  1071.         case IDM_BACKGROUNDPOPUP_LABEL:
  1072.             DrawPopupMenuLabel(dc, &rect, "Background");
  1073.             break;
  1074.     }
  1075. }
  1076.             
  1077. /**********************************************************************/
  1078.  
  1079. /*
  1080.  * Handle window resize. The most important job here is to change the
  1081.  * size of the camera's viewport (if preserving the aspect ratio of
  1082.  * the viewport is necessary, then the viewwindow should also be
  1083.  * changed at this point to reflect then new aspect ratio of the
  1084.  * viewport).
  1085.  */
  1086. static void
  1087. OnSize(HWND window, int w, int h)
  1088. {
  1089.     RwReal vw;
  1090.     RwReal vh;
  1091.     
  1092.     /*
  1093.      * The window has been resized. Therefore, it is necessary to
  1094.      * to modify the camera's viewport to be the same size as the
  1095.      * client area of the window.
  1096.      */
  1097.     RwSetCameraViewport(Camera, 0, 0, w, h);
  1098.     
  1099.     /*
  1100.      * Preserve the aspect ratio of the image by resizing the
  1101.      * viewindow size in proportion to to viewport size. Be ensuring
  1102.      * that the ratios of width to height of the viewport
  1103.      * and viewwindow are the same the rendered image will not
  1104.      * be distorted or stretched.
  1105.      */
  1106.     if (w > h)
  1107.     {
  1108.         vw = DEFAULTVIEWWINDOWSIZE;
  1109.         vh = RDiv(RMul(INT2REAL(h), DEFAULTVIEWWINDOWSIZE), INT2REAL(w));
  1110.     }
  1111.     else
  1112.     {
  1113.         vw = RDiv(RMul(INT2REAL(w), DEFAULTVIEWWINDOWSIZE), INT2REAL(h));
  1114.         vh = DEFAULTVIEWWINDOWSIZE;
  1115.     }
  1116.     RwSetCameraViewwindow(Camera, vw, vh);
  1117.  
  1118.     if (RwGetCameraBackdrop(Camera) != NULL)
  1119.         UpdateCameraBackdropPosition(Camera);
  1120.  
  1121.     /*
  1122.      * When the viewport has changed size we need to re-render the
  1123.      * scene for the new viewport size.
  1124.      */
  1125.     RenderScene(window);
  1126. }
  1127.         
  1128. /**********************************************************************/
  1129.  
  1130. /*
  1131.  * This functions handles the left mouse button going down. Its main
  1132.  * job is to determine the kind of action to be taken when the mouse
  1133.  * moves, such as spinning a clump, or panning the camera. This involves
  1134.  * examining the virtual keys that were depressed when the mouse button
  1135.  * went down and attempting to pick a clump under the mouse pointer
  1136.  * position.
  1137.  */
  1138. static void
  1139. OnLButtonDown(HWND window, POINT *point, WPARAM vKeys)
  1140. {
  1141.     RwPickRecord  pick;
  1142.     RwV3d         p;
  1143.     RwV3d         o;
  1144.  
  1145.      /*
  1146.      * Turn the bird's eye sprite off to ensure we don't pick on it.
  1147.      */
  1148.     RwSetClumpState(BirdsEyeSprite, rwOFF);
  1149.   /*
  1150.      * If the left button is depressed anywhere in the client area of the
  1151.      * window then the animated spin is cancelled.
  1152.      */
  1153.     SpinClump = FALSE;
  1154.  
  1155.     /*
  1156.      * The mouse move action is based on whether an object is picked or
  1157.      * not. Therefore, attempt to pick an object in the scene.
  1158.      */
  1159.     if (RwPickScene(Scene, point->x, point->y, Camera, &pick))
  1160.     {
  1161.         switch (pick.type)
  1162.         {
  1163.             case rwNAPICKOBJECT:
  1164.                 /*
  1165.                  * No clump was picked. The background was clicked on.
  1166.                  * The action taken depends on whether the camera has
  1167.                  * a backdrop or not. If the camera has a backdrop then
  1168.                  * if CONTROL and SHIFT were held down, the backdrop
  1169.                  * is destroyed, otherwise the backdrop will be scrolled
  1170.                  * on mouse move. If the camera has no backdrop then 
  1171.                  * no action will be taken.
  1172.                  */
  1173.                 DeselectClump(window);
  1174.                 RenderScene(window);
  1175.  
  1176.                 if (RwGetCameraBackdrop(Camera))
  1177.                 {
  1178.                     MouseMoveMode = mmScrollBackdrop;
  1179.                 }
  1180.                 else
  1181.                 {
  1182.                     MouseMoveMode = mmNoAction;
  1183.                 }
  1184.                 break;
  1185.  
  1186.             case rwPICKCLUMP:
  1187.                 /*
  1188.                  * A clump was picked, so remember which clump was picked
  1189.                  * in order that it may be manipulated later when the
  1190.                  * mouse moves.
  1191.                  */
  1192.                 SelectClump(window, pick.object.clump.clump);
  1193.                 RenderScene(window);
  1194.                     
  1195.                 /*
  1196.                  * When we drag a clump we don't want the origin of the
  1197.                  * clump to jump to the mouse pointer position. We want
  1198.                  * to keep the drag offset by the distance of the initial
  1199.                  * pick from the origin of the clump. So we compute the
  1200.                  * initial pick position and store it away.
  1201.                  */
  1202.                 GetClumpPositionUnderPointer(SelectedClump, Camera, point->x, point->y, &p);
  1203.                 RwGetClumpOrigin(SelectedClump, &o);
  1204.                 PickOffset.x = RSub(o.x, p.x);
  1205.                 PickOffset.y = RSub(o.y, p.y);
  1206.                 PickOffset.z = RSub(o.z, p.z);
  1207.  
  1208.                 /*
  1209.                  * Determine the exact nature of the manipulation of
  1210.                  * the clump on mouse move by examining the virtual key
  1211.                  * state. If the shift key was depressed, then drag
  1212.                  * the clump, otherwise, spin it.
  1213.                  */
  1214.                 if (vKeys & MK_SHIFT)
  1215.                     MouseMoveMode = mmDragClump;
  1216.                 else if (vKeys & MK_CONTROL)
  1217.                     MouseMoveMode = mmDragClumpInZ;
  1218.                 else
  1219.                     MouseMoveMode = mmSpinClump;
  1220.                 break;
  1221.         }
  1222.     }
  1223.  
  1224.     /*
  1225.      * If any form of action is to be taken on mouse move, remember the
  1226.      * the current x and y position of the mouse and capture future
  1227.      * mouse movement.
  1228.      */
  1229.     if (MouseMoveMode != mmNoAction)
  1230.     {
  1231.         SetCapture(window);
  1232.         LastX = point->x;
  1233.         LastY = point->y;
  1234.     }
  1235.  
  1236.     /*
  1237.      * Turn the bird's eye sprite back on.
  1238.      */
  1239.     RwSetClumpState(BirdsEyeSprite, rwON);
  1240.     
  1241. }
  1242.  
  1243. /**********************************************************************/
  1244.  
  1245. /*
  1246.  * This functions handles the right mouse button going down. Its main
  1247.  * job is to determine the kind of action to be taken when the mouse
  1248.  * moves such as panning the camera.
  1249.  */
  1250. static void
  1251. OnRButtonDown(HWND window, POINT *point, WPARAM vKeys)
  1252. {
  1253.     RwPickRecord  pick;
  1254.     RwClump      *clump;
  1255.     
  1256.     if (vKeys & MK_SHIFT)
  1257.     {
  1258.         MouseMoveMode = mmMoveAndTurnCamera;
  1259.     }
  1260.     else if (vKeys & MK_CONTROL)
  1261.     {
  1262.         MouseMoveMode = mmTiltCamera;
  1263.     }
  1264.     else
  1265.     {   
  1266.         /*
  1267.          * Right mouse button down brings up an appropriate menu
  1268.          * for the object picked on (on just tracks the camera if
  1269.          * no object is picked).
  1270.          */
  1271.         if (RwPickScene(Scene, point->x, point->y, Camera, &pick))
  1272.         {
  1273.             if (pick.type == rwPICKCLUMP)
  1274.             {
  1275.                 clump = pick.object.clump.clump;
  1276.                 
  1277.                 /*
  1278.                  * The object we picked on becomes the currently
  1279.                  * selected clump and rerender the scene so the
  1280.                  * highlight clump is displayed.
  1281.                  */
  1282.                 SelectClump(window, clump);
  1283.                 RenderScene(window);
  1284.                 
  1285.                 /*
  1286.                  * We have picked on a clump. Does the clump picked
  1287.                  * represent a light or is it just a clump?
  1288.                  */
  1289.                 if (ISCLUMPLIGHT(clump))
  1290.                 {
  1291.                     /*
  1292.                      * Its a light, so bring up the light pop-up
  1293.                      * menu.
  1294.                      */
  1295.                     ClientToScreen(window, point);
  1296.                     TrackPopupMenu(LightMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  1297.                                    point->x, point->y, 0, window, NULL);
  1298.                 }
  1299.                 else
  1300.                 {
  1301.                     /*
  1302.                      * Its a clump, so bring up the clump pop-up
  1303.                      * menu.
  1304.                      */
  1305.                     ClientToScreen(window, point);
  1306.                     TrackPopupMenu(ClumpMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  1307.                                    point->x, point->y, 0, window, NULL);
  1308.                 }
  1309.             }
  1310.             else
  1311.             {
  1312.                 /*
  1313.                  * Its the background menu. Depending on whether we are displaying
  1314.                  * a backdrop or the straight backdrop color we enable and disable
  1315.                  * pop-up menu options.
  1316.                  */
  1317.                 if (RwGetCameraBackdrop(Camera) == NULL)
  1318.                 {
  1319.                     EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR,  MF_BYCOMMAND | MF_ENABLED);
  1320.                     EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_DELETE, MF_BYCOMMAND | MF_GRAYED);
  1321.                 }
  1322.                 else
  1323.                 {
  1324.                     if (CenterBackdrop)
  1325.                         EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR,  MF_BYCOMMAND | MF_ENABLED);
  1326.                     else
  1327.                         EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR,  MF_BYCOMMAND | MF_GRAYED);
  1328.                     EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_DELETE, MF_BYCOMMAND | MF_ENABLED);
  1329.                 }
  1330.                 
  1331.                 /*
  1332.                  * Display the background menu.
  1333.                  */
  1334.                 ClientToScreen(window, point);
  1335.                 TrackPopupMenu(BackgroundMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  1336.                                point->x, point->y, 0, window, NULL);
  1337.             }
  1338.         }
  1339.         MouseMoveMode = mmNoAction;
  1340.     }
  1341.     
  1342.     /*
  1343.      * If any form of action is to be taken on mouse move, remember the
  1344.      * the current x and y position of the mouse and capture future
  1345.      * mouse movement.
  1346.      */
  1347.     if (MouseMoveMode != mmNoAction)
  1348.     {
  1349.         SetCapture(window);
  1350.         LastX = point->x;
  1351.         LastY = point->y;
  1352.     }
  1353. }
  1354.  
  1355. /**********************************************************************/
  1356.  
  1357. /*
  1358.  * Handle a movement of the mouse. If a previous left or right mouse
  1359.  * button down event has set a mouse move mode then this function will
  1360.  * take the necessary actions. For example, pan and zooming the camera,
  1361.  * panning the light, dragging or spinning a clump etc.
  1362.  */
  1363. static void
  1364. OnMouseMove(HWND window, POINT *point)
  1365. {
  1366.     RwClump    *parent;
  1367.     RwMatrix4d *tmpMatrix;
  1368.     RwMatrix4d *worldToLocal;
  1369.     RwV3d       up;
  1370.     RwV3d       right;
  1371.     RwV3d       at;
  1372.     RwReal      yDelta;
  1373.     RwReal      xAngle;
  1374.     RwReal      yAngle;
  1375.     RwInt32     xOffset;
  1376.     RwInt32     yOffset;
  1377.     RwV3d       p;
  1378.     RwV3d       o;
  1379.  
  1380.     /*
  1381.      * MouseMoveMode tells us what kind of action to perform.
  1382.      */
  1383.     switch (MouseMoveMode)
  1384.     {
  1385.         case mmNoAction:
  1386.             break;
  1387.  
  1388.         case mmScrollBackdrop:
  1389.             /*
  1390.              * We are scrolling the camera's backdrop. Movement of the
  1391.              * mouse in X scrolls the backdrop horizontally. Movement of
  1392.              * the mouse in Y scrolls the backdrop vertically.
  1393.              */
  1394.             RwGetCameraBackdropOffset(Camera, &xOffset, &yOffset);
  1395.             xOffset += (LastX - point->x);
  1396.             yOffset += (LastY - point->y);
  1397.             RwSetCameraBackdropOffset(Camera, xOffset, yOffset);
  1398.             break;
  1399.  
  1400.         case mmMoveAndTurnCamera:
  1401.             /*
  1402.              * We are moving and turning the camera. Movement of the
  1403.              * mouse in the X direction will turns the camera to the left
  1404.              * and right, and movement of the mouse in the Y direction
  1405.              * moves the camera forward (along its look at) or back..
  1406.              */
  1407.             yDelta = RDiv(INT2REAL(LastY - point->y), CREAL(50.0));
  1408.  
  1409.             /*
  1410.              * Move the camera back to the origin, as we wish to pan about
  1411.              * the origin of the world and not about the origin of the
  1412.              * camera.
  1413.              */
  1414.             RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), yDelta);
  1415.  
  1416.             /*
  1417.              * Pan the camera by mouse X delta degrees.
  1418.              */
  1419.             RwPanCamera(Camera, RDiv(INT2REAL(LastX - point->x), CREAL(4.0)));
  1420.             break;
  1421.  
  1422.         case mmTiltCamera:
  1423.             RwTiltCamera(Camera, INT2REAL(point->y - LastY));
  1424.             break;
  1425.  
  1426.         case mmSpinClump:
  1427.         
  1428.             /* Only spin clumps and lights */
  1429.           if((GETOBJECTTYPE(SelectedClump)==rfIsClump) || (GETOBJECTTYPE(SelectedClump)==rfIsLight))
  1430.             {
  1431.              if(GETOBJECTTYPE(SelectedClump)==rfNoMoveClump) break;    // don't move unmoveable clump
  1432.             /*
  1433.              * Compute the angles of spin (simply derived from the mouse move deltas).
  1434.              */
  1435.             yAngle = INT2REAL(point->x - LastX);
  1436.             xAngle = INT2REAL(point->y - LastY);
  1437.  
  1438.             /*
  1439.              * In RwView, a clump is spun about its own, local coordinate system,
  1440.              * origin, rather than the origin of the world coordinate system. There
  1441.              * are a number of ways this could be achieved, but the most convenient
  1442.              * is to simply apply the rotations to the clump's joint (articulation)
  1443.              * transform.
  1444.              *
  1445.              * A further important point is that the axes of rotation must be the
  1446.              * camera's Look Up and Look Right vector rather than simply
  1447.              * [CREAL(0.0), CREAL(1.0), CREAL(0.0)] and
  1448.              * [CREAL(1.0), CREAL(0.0), CREAL(0.0)]. This ensures that, if the camera
  1449.              * has been panned, tilted, revolved or transformed so that it no longer
  1450.              * looks down the Z axis, user interaction will still operate correctly,
  1451.              * i.e. moving the mouse to the left will spin the clump clockwise etc.
  1452.              *
  1453.              * Therefore, the first stage is to get the camera's Look Up and
  1454.              * Look Right vectors.
  1455.              */
  1456.             RwGetCameraLookUp(Camera, &up);
  1457.             RwGetCameraLookRight(Camera, &right);
  1458.  
  1459.             /*
  1460.              * Unfortunately, rotation about the camera's Look Up and Look Right
  1461.              * vectors is complicated if the clump being manipulated is a child
  1462.              * clump (i.e. not the root of a clump hierarchy). If this is the
  1463.              * case, the camera's vectors have to be transformed into the
  1464.              * coordinate space of the parent of the clump being manipulated.
  1465.              */
  1466.             parent = RwGetClumpParent(SelectedClump);
  1467.             if (parent != NULL)
  1468.             {
  1469.                 /*
  1470.                  * Get a handle to a couple of temporary matrices.
  1471.                  */
  1472.                 tmpMatrix    = RwPushScratchMatrix();
  1473.                 worldToLocal = RwPushScratchMatrix();
  1474.  
  1475.                 /*
  1476.                  * Get the parent clump's LTM (which maps local coordinates to
  1477.                  * world space).
  1478.                  */
  1479.                 RwGetClumpLTM(parent, tmpMatrix);
  1480.  
  1481.                 /*
  1482.                  * Invert it so that it maps world coordinates to the parent's
  1483.                  * local coordinate space.
  1484.                  */
  1485.                 RwInvertMatrix(tmpMatrix, worldToLocal);
  1486.  
  1487.                 /*
  1488.                  * And transform the camera's vectors into local space.
  1489.                  */
  1490.                 RwTransformVector(&up, worldToLocal);
  1491.                 RwNormalize(&up);
  1492.                 RwTransformVector(&right, worldToLocal);
  1493.                 RwNormalize(&right);
  1494.  
  1495.                 /*
  1496.                  * Discard the temporary matrices.
  1497.                  */
  1498.                 RwPopScratchMatrix();
  1499.                 RwPopScratchMatrix();
  1500.             }
  1501.  
  1502.             /*
  1503.              * Apply the rotations.
  1504.              */
  1505.             RwRotateMatrix(SpinMatrix, up.x, up.y, up.z, yAngle, rwREPLACE);
  1506.             RwRotateMatrix(SpinMatrix, right.x, right.y, right.z, xAngle, rwPOSTCONCAT);
  1507.  
  1508.             /*
  1509.              * Apply the resulting, composite transformation to the clump.
  1510.              */
  1511.             RwTransformClumpJoint(SelectedClump, SpinMatrix, rwPOSTCONCAT);
  1512.             
  1513.             /*
  1514.              * Does the clump represent a light? If so change the light's
  1515.              * orientation to match that of the clump.
  1516.              */
  1517.             if (ISCLUMPLIGHT(SelectedClump))
  1518.             {
  1519.                 RwGetClumpLTM(SelectedClump, RwScratchMatrix());
  1520.                 RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
  1521.             }
  1522.             else
  1523.             {
  1524.                 /*
  1525.                  * If the clump is not representing a light enable the momentum spin.
  1526.                  * (Giving a light momentum is a little strange).
  1527.                  */
  1528.                 SpinClump = TRUE;
  1529.             }
  1530.           }  
  1531.           break;
  1532.  
  1533.           case mmDragClump:
  1534.             /* Don't drag splines */
  1535.             if((GETOBJECTTYPE(SelectedClump)==rfIsSpline)|| (GETOBJECTTYPE(SelectedClump)==rfNoMoveClump)) break;
  1536.             
  1537.             /*
  1538.              * Get the 3D position of the mouse pointer on the plane parallel
  1539.              * to the camera on which the picked clump lies.
  1540.              */
  1541.             GetClumpPositionUnderPointer(SelectedClump, Camera, point->x, point->y, &p);
  1542.             p.x = RAdd(p.x, PickOffset.x);
  1543.             p.y = RAdd(p.y, PickOffset.y);
  1544.             p.z = RAdd(p.z, PickOffset.z);
  1545.             RwGetClumpOrigin(SelectedClump, &o);
  1546.             p.x = RSub(p.x, o.x);
  1547.             p.y = RSub(p.y, o.y);
  1548.             p.z = RSub(p.z, o.z);
  1549.             RwPushScratchMatrix();
  1550.                 RwTranslateMatrix(RwScratchMatrix(), p.x, p.y, p.z, rwREPLACE);
  1551.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1552.             RwPopScratchMatrix();
  1553.                                                                               
  1554.             /*
  1555.              * Does the clump represent a light? If so change the light's
  1556.              * orientation to match that of the clump.
  1557.              */
  1558.             if (ISCLUMPLIGHT(SelectedClump))
  1559.             {
  1560.                 RwGetClumpLTM(SelectedClump, RwScratchMatrix());
  1561.                 RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
  1562.             }
  1563.  
  1564.             break;
  1565.                 
  1566.         case mmDragClumpInZ:
  1567.             /* Don't drag splines in Z */
  1568.             if((GETOBJECTTYPE(SelectedClump)==rfIsSpline)|| (GETOBJECTTYPE(SelectedClump)==rfNoMoveClump)) break;
  1569.             
  1570.             RwPushScratchMatrix();
  1571.                 /*
  1572.                  * Compute the amount to translate the object by. This is simply
  1573.                  * derived from the mouse deltas scaled by some arbitrary quantity
  1574.                  * to prevent objects moving too "quickly".
  1575.                  */
  1576.                 yDelta = RDiv(INT2REAL(LastY - point->y), CREAL(50.0));
  1577.  
  1578.                 /*
  1579.                  * In a similar fashion to  spinning a clump we must take into account
  1580.                  * the camera's orientation when dragging a clump. This is done by
  1581.                  * translating along the camera's Look At vector (scaled appropriately)
  1582.                  * rather than the clump's local axes.
  1583.                  */
  1584.                 RwGetCameraLookAt(Camera, &at);
  1585.  
  1586.                 /*
  1587.                  * See the case for mmSpinClump: for a description of why the
  1588.                  * following is necessary.
  1589.                  */
  1590.                 parent = RwGetClumpParent(SelectedClump);
  1591.                 if (parent != NULL)
  1592.                 {
  1593.                     tmpMatrix    = RwPushScratchMatrix();
  1594.                     worldToLocal = RwPushScratchMatrix();
  1595.                     RwGetClumpLTM(parent, tmpMatrix);
  1596.                     RwInvertMatrix(tmpMatrix, worldToLocal);
  1597.                     RwTransformVector(&at, worldToLocal);
  1598.                     RwNormalize(&at);
  1599.                     RwPopScratchMatrix();
  1600.                     RwPopScratchMatrix();
  1601.                 }
  1602.  
  1603.                 /*
  1604.                  * Perform the translation.
  1605.                  */
  1606.                 RwTranslateMatrix(RwScratchMatrix(),
  1607.                                   RMul(at.x, yDelta), RMul(at.y, yDelta),
  1608.                                   RMul(at.z, yDelta), rwREPLACE);
  1609.  
  1610.                 /*
  1611.                  * Apply the resulting, composite transform to the clump.
  1612.                  */
  1613.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1614.                                   
  1615.             RwPopScratchMatrix();
  1616.  
  1617.             /*
  1618.              * Does the clump represent a light? If so change the light's
  1619.              * orientation to match that of the clump.
  1620.              */
  1621.             if (ISCLUMPLIGHT(SelectedClump))
  1622.             {
  1623.                 RwGetClumpLTM(SelectedClump, RwScratchMatrix());
  1624.                 RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
  1625.             }
  1626.  
  1627.             break;
  1628.     }
  1629.  
  1630.     if (MouseMoveMode != mmNoAction)
  1631.     {
  1632.         /*
  1633.          * Re-render the scene and copy the results to the display.
  1634.          */
  1635.         RenderScene(window);
  1636.     
  1637.         /*
  1638.          * Remember the current X and Y for next time.
  1639.          */
  1640.         LastX = point->x;
  1641.         LastY = point->y;
  1642.     }
  1643. }
  1644.  
  1645. /**********************************************************************/
  1646.  
  1647. /*
  1648.  * Handle the left mouse button comming back up. The basic action is
  1649.  * to turn off mouse move actions and release mouse capture.
  1650.  */
  1651. static void
  1652. OnLButtonUp(void)
  1653. {
  1654.     /*
  1655.      * If we were engaged in a mouse move action and the button has come
  1656.      * back up, then terminate the action and release mouse capture.
  1657.      */
  1658.     if (MouseMoveMode != mmNoAction)
  1659.     {
  1660.         MouseMoveMode = mmNoAction;
  1661.         ReleaseCapture();
  1662.     }
  1663. }
  1664.  
  1665. /**********************************************************************/
  1666.  
  1667. /*
  1668.  * Handle the right mouse button comming back up. The basic action is
  1669.  * to turn of mouse move actions and release mouse capture.
  1670.  */
  1671. static void
  1672. OnRButtonUp(void)
  1673. {
  1674.     /*
  1675.      * If we were engaged in a mouse move action and the button has come
  1676.      * back up, then terminate the action and release mouse capture.
  1677.      */
  1678.     if (MouseMoveMode != mmNoAction)
  1679.     {
  1680.         MouseMoveMode = mmNoAction;
  1681.         ReleaseCapture();
  1682.     }
  1683. }
  1684.  
  1685. /**********************************************************************/
  1686.  
  1687. /*
  1688.  * Handle an MS Window's drop message, this function will attempt to
  1689.  * load the file dropped as a RenderWare clump. If that fails, it will
  1690.  * then attempt to load the file as a raster and make it the camera's
  1691.  * backdrop.
  1692.  */
  1693. static void
  1694. OnDrop(HWND window, HDROP drop)
  1695. {
  1696.     int  i;
  1697.     int  numFiles;
  1698.     char path[_MAX_PATH];
  1699.  
  1700.     /*
  1701.      * Get the number of dropped files.
  1702.      */
  1703.     numFiles = DragQueryFile(drop, (UINT)-1, NULL, 0U);
  1704.  
  1705.     /*
  1706.      * Attempt to load each file in turn.
  1707.      */
  1708.     for (i = 0; i < numFiles; i++)
  1709.     {
  1710.         /*
  1711.          * Attempt to load the object whose file was dropped. The
  1712.          * actual object type to load is determined by LoadObject()
  1713.          * from the file extenstion.
  1714.          */
  1715.         DragQueryFile(drop, i, path, _MAX_PATH);
  1716.         if (LoadObject(window, path))
  1717.         {
  1718.             /*
  1719.              * Update the MRU file list and menus.
  1720.              */
  1721.             DeleteMRUFileMenuItems(window);
  1722.             AddFileToMRUFileList(path);
  1723.             AppendMRUFileMenuItems(window);
  1724.         }
  1725.         else
  1726.         {
  1727.             DragFinish(drop);
  1728.             return;
  1729.         }
  1730.     }
  1731.     DragFinish(drop);
  1732.  
  1733.     /*
  1734.      * As new objects have been loaded we must re-render the scene.
  1735.      */
  1736.     RenderScene(window);
  1737. }
  1738.  
  1739. /**********************************************************************/
  1740.  
  1741. /*
  1742.  * Handle the WM_PAINT message by simply copying the rendering
  1743.  * already performed (an stored in the camera's image buffer) to the
  1744.  * output window. There is no need to re-render as nothing in the
  1745.  * scene has changed since the last render. HandleSize() re-renders
  1746.  * when the viewport changes and HandleDrop() re-renders when
  1747.  * clumps are added.
  1748.  */
  1749. static void
  1750. OnPaint(HWND window)
  1751. {
  1752.     HDC      dc;
  1753.     PAINTSTRUCT  paintStruct;
  1754.  
  1755.     dc = BeginPaint(window, &paintStruct);
  1756.  
  1757.     /*
  1758.      * The truly optimal thing to do would be to get the damaged area
  1759.      * of the window that needs updating and set that to be the damaged
  1760.      * area of the viewport. If this was done then only the portion of 
  1761.      * viewport corresponding to damaged area of the window would be
  1762.      * copied. However, as WM_PAINTs are rare in comparison to timer
  1763.      * expiries or mouse moves (where the real rendering effort goes)
  1764.      * there is little to be gained from damaging only a portion of
  1765.      * the display. Therefore, we invalidate the entire viewport and
  1766.      * copy it all to the window.
  1767.      */
  1768.     RwInvalidateCameraViewport(Camera);
  1769.  
  1770.     /*
  1771.      * Copy the viewport to the display.
  1772.      */
  1773.     RwShowCameraImage(Camera, (void*)(DWORD)dc);
  1774.             
  1775.     EndPaint(window, &paintStruct);
  1776. }
  1777.  
  1778.  
  1779. /*
  1780.  * Handle menu selections.
  1781.  */
  1782. static void
  1783. OnMenu(HWND window, WPARAM item)
  1784. {
  1785.     OPENFILENAME  ofn;
  1786.     int           len;
  1787.     char          fileName[_MAX_PATH];
  1788.     char          directory[_MAX_PATH];
  1789.     char          dir[_MAX_DIR];
  1790.     char          ext[_MAX_EXT];
  1791.     FARPROC       dialogProc;
  1792.     RwV3d         point;
  1793.     RwV3d         right;
  1794.     RwV3d         up;
  1795.     RwClump      *newClump;
  1796.     RwLight      *light;
  1797.     HCURSOR       oldCursor;
  1798.     RwRGBColor    color;
  1799.     int        rfSideCode=rfSideA;
  1800.     char          buffer[128];
  1801.     RwBool        status;
  1802.     RwTexture     *applyTexture;
  1803.     RwTextureModes rfTexModes;
  1804.     RwPolygon3d     *applyPolygon;
  1805.     RwClump  *rootClump;
  1806.     RwReal angle=CREAL(0.0);
  1807.     int i;
  1808.     RwInt32 rfStart,rfLength,rfRet;
  1809.     RwPaletteOptions rfGamma;
  1810.     RwPaletteEntry* rfPalette;  
  1811.     RwCombineOperation combine=rwPOSTCONCAT;
  1812.     BOOL badret;
  1813.     short rval=0,gval=0,bval=0;
  1814.     ClumpUserData * clumpData;
  1815.  
  1816.             
  1817.     switch (item)
  1818.     {              
  1819.         // ********************************************** make a clump unMoveable ******************************************
  1820.         /* clumps can be made unMoveable.  Once so, they can only be deleted.  They can not be changed back to moveable */
  1821.                 case IDM_NoMoveClump:
  1822.             if(GETOBJECTTYPE(SelectedClump)==rfIsClump) {  // if selected object is a clump
  1823.                 rootClump = RwGetClumpRoot(SelectedClump); // get the clump root
  1824.                 clumpData = RwGetClumpData(rootClump);     // get the clump (root) data
  1825.                 clumpData->datatype = rfNoMoveClump;     // change object type to NoMoveClump
  1826.                 RwSetClumpData(rootClump,clumpData);        // set it
  1827.             }
  1828.         break;
  1829.  
  1830.         // ********************************************** turn all lights on and off ******************************************
  1831.         case RF_LIGHTSON:
  1832.            RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)RwSetLightState, rwON);
  1833.         break;
  1834.         case RF_LIGHTSOFF:
  1835.            RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)RwSetLightState, rwOFF); 
  1836.         break;
  1837.      
  1838.         // ********************************************** splines ****************************************** 
  1839.         /* Hide or show the spline track on the current spline */
  1840.         case IDM_HIDESPLINETRACK:
  1841.          if (HideSpline)
  1842.             {
  1843.                 /* We are currently not viewing spline path. So turn it on and uncheck the menu item.*/
  1844.                 HideSpline = FALSE;
  1845.                 CheckMenuItem(GetMenu(window), IDM_HIDESPLINETRACK, MF_BYCOMMAND | MF_UNCHECKED);
  1846.                 EnableCoasterTrack(coaster);
  1847.             }
  1848.             else
  1849.             {
  1850.                 /* We are currently viewing spline path. So turn it off and check the menu item.*/
  1851.                 HideSpline = TRUE;
  1852.                 CheckMenuItem(GetMenu(window), IDM_HIDESPLINETRACK, MF_BYCOMMAND | MF_CHECKED);
  1853.                 DisableCoasterTrack(coaster); 
  1854.             }
  1855.          RenderScene(window);
  1856.         break;
  1857.         
  1858.         /* hide the control points on the current spline */
  1859.         case IDM_HIDESPLINECONTROLPOINTS: 
  1860.          if (HideControlPoints)
  1861.             {
  1862.                 /* We are currently not viewing control points. So turn them on and uncheck the menu item.*/
  1863.                 HideControlPoints = FALSE;
  1864.                 CheckMenuItem(GetMenu(window), IDM_HIDESPLINECONTROLPOINTS, MF_BYCOMMAND | MF_UNCHECKED);
  1865.                 EnableCoasterControlPoints(coaster);
  1866.  
  1867.             }
  1868.             else
  1869.             {
  1870.                 /* We are currently viewing control points. So turn them off and check the menu item.*/
  1871.                 HideControlPoints = TRUE;
  1872.                 CheckMenuItem(GetMenu(window), IDM_HIDESPLINECONTROLPOINTS, MF_BYCOMMAND | MF_CHECKED);
  1873.                 DisableCoasterControlPoints(coaster);
  1874.             }
  1875.          RenderScene(window);
  1876.         break;
  1877.     
  1878.         /* delete a spline */
  1879.         case IDM_DELSPLINE:
  1880.          DeleteCoaster(coaster);
  1881.          RenderScene(window);
  1882.         break;
  1883.     
  1884.         /* move the currently selected object on the spline. If the currently selected object is not on the  spline,
  1885.            then place it at the start of the spline (0.0) and move it.*/
  1886.         case IDM_MOVEOBJECTONSPLINE:
  1887.           if (TravelSpline)
  1888.             {
  1889.                 /* We are currently moving on spline so stop moving and uncheck the menu item.
  1890.                  */
  1891.                 TravelSpline = FALSE;
  1892.                 rfRepeatMode = FALSE;
  1893.                 CheckMenuItem(GetMenu(window), IDM_MOVEOBJECTONSPLINE, MF_BYCOMMAND | MF_UNCHECKED);
  1894.             }
  1895.             else
  1896.             {
  1897.                 /* We are currently notmoving on spline so start moving and check the menu item. */
  1898.                 TravelSpline = TRUE;
  1899.                 rfRepeatMode = TRUE;
  1900.                 CheckMenuItem(GetMenu(window), IDM_MOVEOBJECTONSPLINE, MF_BYCOMMAND | MF_CHECKED);
  1901.             }
  1902.         break;
  1903.         
  1904.         /* place the currently selected object onto the start of the spline (0.0).  Orient it such that Y is up and
  1905.            z points positively along the spline */
  1906.         case IDM_PLACEOBJECTONSPLINE:    // only place clumps on spline
  1907.           if (GETOBJECTTYPE(SelectedClump)==rfIsClump)
  1908.           {
  1909.             if(GETOBJECTTYPE(SelectedClump)==rfNoMoveClump) break;
  1910.             coaster->t = (float)0.0;
  1911.             PositionObjectOnSpline(coaster,SelectedClump);
  1912.             RenderScene(window);
  1913.           }
  1914.         break;
  1915.     
  1916.         /* accelerate at the start of the spline and decelerate at the end of the spline */
  1917.         case IDM_SMOOTHEND:
  1918.           if (UseSmoothEnd)
  1919.             {
  1920.                 /* We are currently using smooth ends so don't use them and uncheck the menu item.*/
  1921.                 UseSmoothEnd = FALSE;
  1922.                 CheckMenuItem(GetMenu(window), IDM_SMOOTHEND, MF_BYCOMMAND | MF_UNCHECKED);
  1923.             }
  1924.             else
  1925.             {
  1926.                 /* We are currently not using smooth ends so use them  So turn them back on and check the menu item. */
  1927.                 UseSmoothEnd = TRUE;
  1928.                 CheckMenuItem(GetMenu(window), IDM_SMOOTHEND, MF_BYCOMMAND | MF_CHECKED);
  1929.             }
  1930.         break;
  1931.     
  1932.  
  1933.         // ********************************************** camera ****************************************** 
  1934.         case ID_BCAM:  // bird's eye camera
  1935.             CurrentCamera = BirdsEyeCamera;  CurrentWindow = window;
  1936.  
  1937.             dialogProc = MakeProcInstance(CameraDlgProc, AppInstance);
  1938.             if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_CAMERA),
  1939.                                window, dialogProc)==IDOK)
  1940.               {    
  1941.                RenderScene(window);
  1942.               }
  1943.             FreeProcInstance(dialogProc);
  1944.         break;
  1945.             
  1946.         case ID_VCAM:  // main camera
  1947.             CurrentCamera = Camera;  CurrentWindow = window;
  1948.  
  1949.             dialogProc = MakeProcInstance(CameraDlgProc, AppInstance);
  1950.             if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_CAMERA),
  1951.                                window, dialogProc)==IDOK)
  1952.               {    
  1953.                RenderScene(window);
  1954.               }
  1955.             FreeProcInstance(dialogProc);
  1956.         break;
  1957.        
  1958.         // ********************************************** debug ******************************************  
  1959.         /* Display the DEBUG box. */
  1960.         case IDM_DEBUG:
  1961.             dialogProc = MakeProcInstance(DebugDlgProc, AppInstance);
  1962.             DialogBox(AppInstance, MAKEINTRESOURCE(IDD_DEBUG), window, dialogProc);
  1963.             FreeProcInstance(dialogProc);
  1964.    
  1965.         break;
  1966.            
  1967.         // ********************************************** palette ****************************************** 
  1968.         /* build either a red, green, blue or gray ramp beginning at 10 and ending at 242 */
  1969.         case ID_PALETTE_RED: case ID_PALETTE_GREEN: case ID_PALETTE_BLUE: case ID_PALETTE_GRAY:
  1970.            rfPalette = (RwPaletteEntry*) malloc(256 * sizeof(RwPaletteEntry));        
  1971.            rfGamma = rwOFF;
  1972.            switch(item)
  1973.            {
  1974.             case ID_PALETTE_RED:
  1975.             rval=1; gval=0; bval=0;
  1976.             break;
  1977.             case ID_PALETTE_GREEN:
  1978.             rval=0; gval=1; bval=0;
  1979.             break;
  1980.             case ID_PALETTE_BLUE:
  1981.             rval=0; gval=0; bval=1;
  1982.             break;
  1983.             case ID_PALETTE_GRAY:
  1984.             rval=1; gval=1; bval=1;
  1985.             break;
  1986.             
  1987.            }
  1988.            
  1989.            /* build the color ramp from 10 to 242 */
  1990.            rfStart = 10;  rfLength = 232;
  1991.            for (i=0; i<rfLength; i++) 
  1992.               {
  1993.                rfPalette[i].r = (unsigned char)((i+10)*rval);
  1994.                rfPalette[i].g = (unsigned char)((i+10)*gval);
  1995.                rfPalette[i].b = (unsigned char)((i+10)*bval);
  1996.               }
  1997.            rfRet = RwSetPaletteEntries(rfStart,rfLength,(RwPaletteEntry *) rfPalette, rfGamma);
  1998.            free(rfPalette);
  1999.            RenderScene(window);
  2000.         break;
  2001.            
  2002.         /* build a default palette consisting of several ramps */
  2003.         case ID_PALETTE_DEFAULT:
  2004.            rfPalette = (RwPaletteEntry*) malloc(256 * sizeof(RwPaletteEntry));        
  2005.            rfGamma = rwOFF;
  2006.            RtDefaultPalette(rfPalette);
  2007.            rfStart = 10;  rfLength = 10*MAXLUM;
  2008.            rfRet = RwSetPaletteEntries(rfStart,rfLength,(RwPaletteEntry *) rfPalette, rfGamma);
  2009.            free(rfPalette);
  2010.            RenderScene(window);
  2011.         break;
  2012.       
  2013.         // ********************************************** textures ******************************************   
  2014.         // cubic texture map clump
  2015.         case ID_CUBIC:
  2016.           RwCubicTexturizeClump(SelectedClump);
  2017.              RenderScene(window);
  2018.            break;
  2019.   
  2020.         // spherical texture map clump
  2021.         case ID_SPHERICAL:
  2022.           RwSphericalTexturizeClump(SelectedClump);
  2023.              RenderScene(window);
  2024.            break;
  2025.  
  2026.         // environmental texture map the clump
  2027.         case ID_ENVIRON:
  2028.             /* rotate the selected object according to the user rotate values */
  2029.             rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(1.0);   rfRel.vc.z = CREAL(0.0);  angle = (RwReal)rfRot.val;   combine = rfRot.combine;    
  2030.     
  2031.             /* build the rotate (local or global) matrix */
  2032.             RwPushScratchMatrix(); 
  2033.              RwGetClumpOrigin(SelectedClump,&rfRel.pos);
  2034.              rfRel.up.x = CREAL(0.0); rfRel.up.y = CREAL(1.0); rfRel.up.z = CREAL(0.0);
  2035.              rfRel.at.x = CREAL(0.0); rfRel.at.y = CREAL(0.0); rfRel.at.z = CREAL(1.0);
  2036.              rfRel.right.x = CREAL(1.0); rfRel.right.y = CREAL(0.0); rfRel.right.z = CREAL(0.0);
  2037.              
  2038.              if(combine==rwPRECONCAT) {
  2039.               RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2040.               RwTransformVector(&rfRel.up,RwScratchMatrix());
  2041.               RwTransformVector(&rfRel.at,RwScratchMatrix()); 
  2042.               RwTransformVector(&rfRel.right,RwScratchMatrix());
  2043.    
  2044.               RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE);
  2045.               RwRotateMatrix(RwScratchMatrix(),
  2046.                  (rfRel.vc.x * rfRel.right.x) + (rfRel.vc.y * rfRel.up.x) + (rfRel.vc.z * rfRel.at.x), 
  2047.                  (rfRel.vc.x * rfRel.right.y) + (rfRel.vc.y * rfRel.up.y) + (rfRel.vc.z * rfRel.at.y),
  2048.                  (rfRel.vc.x * rfRel.right.z) + (rfRel.vc.y * rfRel.up.z) + (rfRel.vc.z * rfRel.at.z),
  2049.                  angle,rwPOSTCONCAT);    
  2050.               RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT);
  2051.              }
  2052.           else {
  2053.              RwRotateMatrix(RwScratchMatrix(),rfRel.vc.x, rfRel.vc.y, rfRel.vc.z, angle, rwREPLACE);                    
  2054.              }
  2055.              
  2056.            /* rotate the object according to the user rotate value */
  2057.            if(rfRot.repeat!=1) rfRepeatMode = TRUE;
  2058.            for (i=0; i<rfRot.repeat; i++) {
  2059.               RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2060.               RwBeginCameraUpdate(Camera,(void *)window);  // necessary for EnvMap
  2061.                RwEnvMapClump(SelectedClump); // perform environment map
  2062.               RwEndCameraUpdate(Camera);
  2063.               if(i == rfRot.repeat-1) rfRepeatMode = FALSE;
  2064.               RenderScene(window);
  2065.              }
  2066.            RwPopScratchMatrix();
  2067.         break;
  2068.              
  2069.         // LIT the texture to the tagged side of our selected object
  2070.         case ID_LITA:  case ID_LITB:   case ID_LITC:   case ID_LITD:
  2071.                 switch(item) {
  2072.                   case ID_LITA: rfSideCode = rfSideA; break;
  2073.                   case ID_LITB: rfSideCode = rfSideB; break;     
  2074.                   case ID_LITC: rfSideCode = rfSideC; break;
  2075.                   case ID_LITD: rfSideCode = rfSideD; break;
  2076.                   }
  2077.    
  2078.             applyPolygon = RwFindTaggedPolygon(SelectedClump,rfSideCode); 
  2079.             rfTexModes=RwGetPolygonTextureModes(applyPolygon);
  2080.                RwSetPolygonTextureModes(applyPolygon,rfTexModes ^ rwLIT);
  2081.                RenderScene(window);
  2082.         break;
  2083.        
  2084.         // Filter the texture to the tagged side of our selected object
  2085.         case ID_FILTERA:  case ID_FILTERB:   case ID_FILTERC:   case ID_FILTERD:
  2086.                 switch(item) {
  2087.                   case ID_FILTERA: rfSideCode = rfSideA; break;
  2088.                   case ID_FILTERB: rfSideCode = rfSideB; break;     
  2089.                   case ID_FILTERC: rfSideCode = rfSideC; break;
  2090.                   case ID_FILTERD: rfSideCode = rfSideD; break;
  2091.                   }
  2092.    
  2093.             applyPolygon = RwFindTaggedPolygon(SelectedClump,rfSideCode); 
  2094.             rfTexModes=RwGetPolygonTextureModes(applyPolygon);
  2095.                RwSetPolygonTextureModes(applyPolygon,rfTexModes ^ rwFILTER);
  2096.                RenderScene(window);
  2097.             break;
  2098.        
  2099.         // ForeShorten the texture to the tagged side of our selected object
  2100.         case ID_FOREA:  case ID_FOREB:   case ID_FOREC:   case ID_FORED:
  2101.                 switch(item) {
  2102.                   case ID_FOREA: rfSideCode = rfSideA; break;
  2103.                   case ID_FOREB: rfSideCode = rfSideB; break;     
  2104.                   case ID_FOREC: rfSideCode = rfSideC; break;
  2105.                   case ID_FORED: rfSideCode = rfSideD; break;
  2106.                   }
  2107.    
  2108.             applyPolygon = RwFindTaggedPolygon(SelectedClump,rfSideCode); 
  2109.             rfTexModes=RwGetPolygonTextureModes(applyPolygon);
  2110.                RwSetPolygonTextureModes(applyPolygon,rfTexModes ^ rwFORESHORTEN);
  2111.                RenderScene(window);
  2112.         break;
  2113.        
  2114.         // apply a texture to our selected object
  2115.         case ID_APPLYA:  case ID_APPLYB:   case ID_APPLYC:   case ID_APPLYD:  case ID_APPLYALL:
  2116.  
  2117.              _splitpath(LastFileName, directory, dir, fileName, ext);
  2118.             strcat(fileName, ext);
  2119.             strcat(directory, dir);
  2120.             len = strlen(directory);
  2121.             if ((len > 0) && (directory[len - 1] == '\\'))
  2122.                 directory[len - 1] = '\0';
  2123.             
  2124.             /* select the texture to map onto the selected object */
  2125.             memset(&ofn, 0, sizeof(OPENFILENAME));
  2126.             ofn.lStructSize = sizeof(OPENFILENAME);
  2127.             ofn.hwndOwner = window;
  2128.             ofn.lpstrFilter = "Texture (*.bmp)\0*.bmp\0";
  2129.             ofn.nFilterIndex = 1;
  2130.             ofn.lpstrFile= fileName;
  2131.             ofn.nMaxFile = sizeof(fileName);
  2132.             ofn.lpstrInitialDir = directory;
  2133.             ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  2134.             if (GetOpenFileName(&ofn))
  2135.             {
  2136.                 /* Save the file name for next time   */
  2137.                 strcpy(LastFileName, fileName);
  2138.                 
  2139.                 /* Read the texture */
  2140.                 applyTexture=RwReadTexture(fileName);
  2141.                 
  2142.                 /* if successful, then apply the texture to one tagged side or all sides */
  2143.                 if (applyTexture!=NULL)
  2144.                 {
  2145.                  switch(item) {
  2146.                   case ID_APPLYA:
  2147.                     rfSideCode = rfSideA;
  2148.                   break;
  2149.                   case ID_APPLYB:
  2150.                     rfSideCode = rfSideB;
  2151.                     break;     
  2152.                   case ID_APPLYC:
  2153.                     rfSideCode = rfSideC;
  2154.                     break;
  2155.                   case ID_APPLYD:
  2156.                     rfSideCode = rfSideD;
  2157.                     break;
  2158.                   case ID_APPLYALL:
  2159.                     rfSideCode = rfSideAll;
  2160.                     break;
  2161.                   }
  2162.                 if(rfSideCode==rfSideAll)
  2163.                   {
  2164.                    RwForAllPolygonsInClumpPointer(SelectedClump, (RwPolygon3dFuncPointer)RwSetPolygonTexture, NULL);
  2165.                     RwForAllPolygonsInClumpPointer(SelectedClump, (RwPolygon3dFuncPointer)RwSetPolygonTexture, applyTexture);}
  2166.                 else
  2167.                   {
  2168.                    applyPolygon = RwFindTaggedPolygon(SelectedClump,rfSideCode); 
  2169.                    RwSetPolygonTexture(applyPolygon, NULL);
  2170.                     RwSetPolygonTexture(applyPolygon, applyTexture);
  2171.                    }
  2172.                 RenderScene(window);
  2173.                 }
  2174.             }
  2175.         break;
  2176.  
  2177.       // ******************************************* Pre and Post Concat ***************************************
  2178.         // translate then rotate preconcat
  2179.         case ID_PRECON1:
  2180.             RwPushScratchMatrix();
  2181.                 RwTranslateMatrix(RwScratchMatrix(),CREAL(0.0),CREAL(0),CREAL(1.2),rwREPLACE);
  2182.                 RwRotateMatrix(RwScratchMatrix(),CREAL(1),CREAL(0),CREAL(0),CREAL(45.0),rwPRECONCAT);
  2183.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
  2184.             RwPopScratchMatrix();
  2185.             RenderScene(window);
  2186.         break;
  2187.  
  2188.         // translate then rotate postconcat
  2189.         case ID_POSTCON1:
  2190.             RwPushScratchMatrix();
  2191.                 RwTranslateMatrix(RwScratchMatrix(),CREAL(0.0),CREAL(0),CREAL(1.2),rwREPLACE);
  2192.                 RwRotateMatrix(RwScratchMatrix(),CREAL(1),CREAL(0),CREAL(0),CREAL(45.0),rwPOSTCONCAT); 
  2193.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
  2194.             RwPopScratchMatrix();
  2195.             RenderScene(window);
  2196.         break;
  2197.  
  2198.         // rotate then translate preconcat
  2199.         case ID_PRECON2:
  2200.             RwPushScratchMatrix();
  2201.                 RwRotateMatrix(RwScratchMatrix(),CREAL(1),CREAL(0),CREAL(0),CREAL(45.0),rwREPLACE);   
  2202.                 RwTranslateMatrix(RwScratchMatrix(),CREAL(0.0),CREAL(0),CREAL(1.2),rwPRECONCAT);         
  2203.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
  2204.             RwPopScratchMatrix();
  2205.             RenderScene(window);
  2206.         break;
  2207.   
  2208.         // rotate then translate postconcat
  2209.         case ID_POSTCON2:
  2210.             RwPushScratchMatrix();
  2211.                 RwRotateMatrix(RwScratchMatrix(),CREAL(1),CREAL(0),CREAL(0),CREAL(45.0),rwREPLACE); 
  2212.                 RwTranslateMatrix(RwScratchMatrix(),CREAL(0.0),CREAL(0),CREAL(1.2),rwPOSTCONCAT);      
  2213.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
  2214.             RwPopScratchMatrix();
  2215.             RenderScene(window);
  2216.         break;
  2217.         
  2218.  
  2219.                                                     
  2220.         // **************************************** pan, revolve, and tilt *************************************
  2221.         /* pan the selected object */
  2222.         case IDC_PAN:
  2223.          RwPushScratchMatrix(); 
  2224.           RwGetClumpOrigin(SelectedClump,&rfRel.pos);
  2225.           // about local Y  UP
  2226.           rfRel.up.x = CREAL(0.0); rfRel.up.y = CREAL(1.0); rfRel.up.z = CREAL(0.0);
  2227.           RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2228.           RwTransformVector(&rfRel.up,RwScratchMatrix());
  2229.           RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE);
  2230.           RwRotateMatrix(RwScratchMatrix(),rfRel.up.x, rfRel.up.y, rfRel.up.z, (RwReal)rfLoc.val,rwPOSTCONCAT);    
  2231.           RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT);
  2232.  
  2233.           if(rfLoc.repeat!=1) rfRepeatMode = TRUE;
  2234.            for (i=0; i<rfLoc.repeat; i++) {
  2235.               RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2236.               if(i == rfLoc.repeat-1) rfRepeatMode = FALSE;
  2237.               RenderScene(window);
  2238.              }
  2239.          RwPopScratchMatrix();
  2240.        break;
  2241.  
  2242.        /* revolve the selected object */
  2243.        case IDC_REVOLVE:
  2244.         RwPushScratchMatrix(); 
  2245.          RwGetClumpOrigin(SelectedClump,&rfRel.pos);       
  2246.          // about local Z  AT
  2247.          rfRel.at.x = CREAL(0.0); rfRel.at.y = CREAL(0.0); rfRel.at.z = CREAL(1.0);
  2248.          RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2249.          RwTransformVector(&rfRel.at,RwScratchMatrix());
  2250.          RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE); 
  2251.          RwRotateMatrix(RwScratchMatrix(),rfRel.at.x, rfRel.at.y, rfRel.at.z, (RwReal)rfLoc.val,rwPOSTCONCAT);          
  2252.          RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT);
  2253.        
  2254.          if(rfLoc.repeat!=1) rfRepeatMode = TRUE;
  2255.           for (i=0; i<rfLoc.repeat; i++) {
  2256.               RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2257.               if(i == rfLoc.repeat-1) rfRepeatMode = FALSE;
  2258.               RenderScene(window);
  2259.              }      
  2260.         RwPopScratchMatrix();
  2261.        break;
  2262.        
  2263.        /* tilt the selected object */
  2264.        case IDC_TILT:
  2265.         RwPushScratchMatrix(); 
  2266.          // about local X  RIGHT 
  2267.          RwGetClumpOrigin(SelectedClump,&rfRel.pos);
  2268.          rfRel.right.x = CREAL(1.0); rfRel.right.y = CREAL(0.0); rfRel.right.z = CREAL(0.0);
  2269.          RwGetClumpLTM(SelectedClump,RwScratchMatrix());        
  2270.          RwTransformVector(&rfRel.right,RwScratchMatrix());
  2271.          RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE);
  2272.          RwRotateMatrix(RwScratchMatrix(),rfRel.right.x, rfRel.right.y, rfRel.right.z,(RwReal) rfLoc.val, rwPOSTCONCAT);
  2273.          RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT); 
  2274.          
  2275.          if(rfLoc.repeat!=1) rfRepeatMode = TRUE;
  2276.           for (i=0; i<rfLoc.repeat; i++) {
  2277.               RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2278.               if(i == rfLoc.repeat-1) rfRepeatMode = FALSE;
  2279.               RenderScene(window);
  2280.              }
  2281.         RwPopScratchMatrix();
  2282.        break;
  2283.        
  2284.        /* pan, revolve and tilt setup */
  2285.        case IDD_PRT_SETUP:
  2286.           dialogProc = MakeProcInstance(PRTDlgProc, AppInstance);
  2287.           if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_PRT_SETUP),
  2288.                                window, dialogProc)==IDCANCEL)
  2289.            {    
  2290.             FreeProcInstance(dialogProc);
  2291.             return;
  2292.            }
  2293.           FreeProcInstance(dialogProc);
  2294.        break;
  2295.        
  2296.        // ********************************************** translate ******************************************
  2297.        // translate the selected object about the X, Y, Z or User (local or global) defined axis
  2298.        case ID_TRANSX:  case ID_TRANSY: case ID_TRANSZ:   case ID_TRANSU:
  2299.         switch(item) {
  2300.          case ID_TRANSX:
  2301.            rfRel.vc.x = rfTran.x;  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = CREAL(0.0);  combine = rfTran.combine;    
  2302.          break;
  2303.          case ID_TRANSY:
  2304.            rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = rfTran.y;   rfRel.vc.z = CREAL(0.0);  combine = rfTran.combine;    
  2305.          break;
  2306.          case ID_TRANSZ:
  2307.            rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = rfTran.z;  combine = rfTran.combine;   
  2308.          break;
  2309.          case ID_TRANSU:      // user defined through dialog
  2310.             dialogProc = MakeProcInstance(TranslateDlgProc, AppInstance);
  2311.             if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(RDD_TRANSLATE),
  2312.                                window, dialogProc, (LPARAM)SelectedClump)==IDCANCEL)
  2313.               {    
  2314.                FreeProcInstance(dialogProc);
  2315.                return;
  2316.               }
  2317.             rfRel.vc.x = rfTran.x; rfRel.vc.y = rfTran.y; rfRel.vc.z = rfTran.z; combine = rfTran.combine;
  2318.          break;
  2319.          }
  2320.  
  2321.         rfRel.up.x = CREAL(0.0); rfRel.up.y = CREAL(1.0); rfRel.up.z = CREAL(0.0);
  2322.         rfRel.at.x = CREAL(0.0); rfRel.at.y = CREAL(0.0); rfRel.at.z = CREAL(1.0);
  2323.         rfRel.right.x = CREAL(1.0); rfRel.right.y = CREAL(0.0); rfRel.right.z = CREAL(0.0);
  2324.                            
  2325.         RwPushScratchMatrix();
  2326.           if(combine==rwPRECONCAT) {
  2327.             RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2328.             RwTransformVector(&rfRel.up,RwScratchMatrix());
  2329.             RwTransformVector(&rfRel.at,RwScratchMatrix()); 
  2330.             RwTransformVector(&rfRel.right,RwScratchMatrix());
  2331.             }
  2332.             
  2333.          RwTranslateMatrix(RwScratchMatrix(),
  2334.           (rfRel.vc.x * rfRel.right.x) + (rfRel.vc.y * rfRel.up.x) + (rfRel.vc.z * rfRel.at.x), 
  2335.           (rfRel.vc.x * rfRel.right.y) + (rfRel.vc.y * rfRel.up.y) + (rfRel.vc.z * rfRel.at.y),
  2336.           (rfRel.vc.x * rfRel.right.z) + (rfRel.vc.y * rfRel.up.z) + (rfRel.vc.z * rfRel.at.z),rwREPLACE);
  2337.          RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2338.                 
  2339.         RwPopScratchMatrix();
  2340.         RenderScene(window);
  2341.       break;         
  2342.              
  2343.       // ******************************************* scale ***************************************
  2344.       // scale the selected object about the X, Y, Z or User (local or global) defined axis
  2345.       case ID_SCALEX:      case ID_SCALEY:   case ID_SCALEZ:  case ID_SCALEU:
  2346.        switch(item) {
  2347.         case ID_SCALEX:
  2348.           rfRel.vc.x = rfScale.x;  rfRel.vc.y = CREAL(1.0);   rfRel.vc.z = CREAL(1.0);   combine = rfScale.combine;  
  2349.         break;
  2350.         case ID_SCALEY:
  2351.           rfRel.vc.x = CREAL(1.0);  rfRel.vc.y = rfScale.y;   rfRel.vc.z = CREAL(1.0);   combine = rfScale.combine;    
  2352.         break;
  2353.         case ID_SCALEZ:
  2354.           rfRel.vc.x = CREAL(1.0);  rfRel.vc.y = CREAL(1.0);   rfRel.vc.z = rfScale.z;   combine = rfScale.combine;
  2355.         break;
  2356.         case ID_SCALEU:
  2357.          dialogProc = MakeProcInstance(ScaleDlgProc, AppInstance);
  2358.          if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(RDD_SCALE),
  2359.               window, dialogProc, (LPARAM)SelectedClump)==IDCANCEL){    
  2360.                 FreeProcInstance(dialogProc);
  2361.                 return;
  2362.                 }
  2363.          rfRel.vc.x = rfScale.x; rfRel.vc.y = rfScale.y; rfRel.vc.z = rfScale.z;
  2364.          combine = rfScale.combine;
  2365.          FreeProcInstance(dialogProc);
  2366.         break;
  2367.         }
  2368.    
  2369.         RwPushScratchMatrix(); 
  2370.          RwGetClumpOrigin(SelectedClump,&rfRel.pos);
  2371.          rfRel.up.x = CREAL(0.0); rfRel.up.y = CREAL(1.0); rfRel.up.z = CREAL(0.0);
  2372.          rfRel.at.x = CREAL(0.0); rfRel.at.y = CREAL(0.0); rfRel.at.z = CREAL(1.0);
  2373.          rfRel.right.x = CREAL(1.0); rfRel.right.y = CREAL(0.0); rfRel.right.z = CREAL(0.0);
  2374.              
  2375.          if(combine==rwPRECONCAT) {
  2376.            RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2377.            RwTransformVector(&rfRel.up,RwScratchMatrix());
  2378.            RwTransformVector(&rfRel.at,RwScratchMatrix()); 
  2379.            RwTransformVector(&rfRel.right,RwScratchMatrix());
  2380.            RwNormalize(&rfRel.up);
  2381.            RwNormalize(&rfRel.right);
  2382.            RwNormalize(&rfRel.at);
  2383.            }
  2384.                  
  2385.          RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE);
  2386.          RwScaleMatrix(RwScratchMatrix(),
  2387.            (rfRel.vc.x * rfRel.right.x) + (rfRel.vc.y * rfRel.up.x) + (rfRel.vc.z * rfRel.at.x), 
  2388.            (rfRel.vc.x * rfRel.right.y) + (rfRel.vc.y * rfRel.up.y) + (rfRel.vc.z * rfRel.at.y),
  2389.            (rfRel.vc.x * rfRel.right.z) + (rfRel.vc.y * rfRel.up.z) + (rfRel.vc.z * rfRel.at.z),rwPOSTCONCAT);    
  2390.          RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT);
  2391.                  
  2392.          RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2393.          RenderScene(window);
  2394.  
  2395.         RwPopScratchMatrix();
  2396.        break;
  2397.           
  2398.        // ******************************************* rotate ***************************************
  2399.        // rotate the selected object about the X, Y, Z or User (local or global) defined axis
  2400.        case ID_ROTX:      case ID_ROTY:   case ID_ROTZ:  case ID_ROTU:
  2401.         switch(item) {
  2402.          case ID_ROTX:
  2403.           rfRel.vc.x = CREAL(1.0);  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = CREAL(0.0);  angle = (RwReal)rfRot.val;   combine = rfRot.combine;  
  2404.          break;
  2405.          case ID_ROTY:
  2406.           rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(1.0);   rfRel.vc.z = CREAL(0.0);  angle = (RwReal)rfRot.val;   combine = rfRot.combine;    
  2407.          break;
  2408.          case ID_ROTZ:
  2409.           rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = CREAL(1.0);  angle = (RwReal)rfRot.val;   combine = rfRot.combine;
  2410.          break;
  2411.          case ID_ROTU:
  2412.           dialogProc = MakeProcInstance(RotateDlgProc, AppInstance);
  2413.           if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(RDD_ROTATE),
  2414.                 window, dialogProc, (LPARAM)SelectedClump)==IDCANCEL) {    
  2415.                   FreeProcInstance(dialogProc);
  2416.                   return;
  2417.                   }
  2418.           rfRel.vc.x = rfRot.x; rfRel.vc.y = rfRot.y; rfRel.vc.z = rfRot.z;
  2419.           angle = (RwReal) rfRot.val;    combine = (RwReal)rfRot.combine;
  2420.           FreeProcInstance(dialogProc);
  2421.          break;
  2422.          }                 
  2423.    
  2424.          RwPushScratchMatrix(); 
  2425.           RwGetClumpOrigin(SelectedClump,&rfRel.pos);
  2426.           rfRel.up.x = CREAL(0.0); rfRel.up.y = CREAL(1.0); rfRel.up.z = CREAL(0.0);
  2427.           rfRel.at.x = CREAL(0.0); rfRel.at.y = CREAL(0.0); rfRel.at.z = CREAL(1.0);
  2428.           rfRel.right.x = CREAL(1.0); rfRel.right.y = CREAL(0.0); rfRel.right.z = CREAL(0.0);
  2429.              
  2430.           if(combine==rwPRECONCAT) {
  2431.            RwGetClumpLTM(SelectedClump,RwScratchMatrix());
  2432.            RwTransformVector(&rfRel.up,RwScratchMatrix());
  2433.            RwTransformVector(&rfRel.at,RwScratchMatrix()); 
  2434.            RwTransformVector(&rfRel.right,RwScratchMatrix());
  2435.    
  2436.            RwTranslateMatrix(RwScratchMatrix(),-rfRel.pos.x,-rfRel.pos.y,-rfRel.pos.z,rwREPLACE);
  2437.            RwRotateMatrix(RwScratchMatrix(),
  2438.               (rfRel.vc.x * rfRel.right.x) + (rfRel.vc.y * rfRel.up.x) + (rfRel.vc.z * rfRel.at.x), 
  2439.               (rfRel.vc.x * rfRel.right.y) + (rfRel.vc.y * rfRel.up.y) + (rfRel.vc.z * rfRel.at.y),
  2440.               (rfRel.vc.x * rfRel.right.z) + (rfRel.vc.y * rfRel.up.z) + (rfRel.vc.z * rfRel.at.z),angle,rwPOSTCONCAT);    
  2441.            RwTranslateMatrix(RwScratchMatrix(),rfRel.pos.x,rfRel.pos.y,rfRel.pos.z,rwPOSTCONCAT);
  2442.            }
  2443.            else {
  2444.              RwRotateMatrix(RwScratchMatrix(),rfRel.vc.x, rfRel.vc.y, rfRel.vc.z, angle, rwREPLACE);                    
  2445.              }
  2446.              
  2447.           if(rfRot.repeat!=1) rfRepeatMode = TRUE;
  2448.            for (i=0; i<rfRot.repeat; i++) {
  2449.               RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2450.               if(i == rfRot.repeat-1) rfRepeatMode = FALSE;
  2451.               RenderScene(window);
  2452.              }
  2453.            RwPopScratchMatrix();
  2454.           break;
  2455.   
  2456.        // ********************************************** Joint Rotate ****************************************** 
  2457.        // rotate joing the selected object about the X, Y, Z or User (local or global) defined axis
  2458.        case ID_JROTX:      case ID_JROTY:   case ID_JROTZ:  case ID_JROTU:
  2459.         switch(item) {
  2460.          case ID_JROTX:
  2461.           rfRel.vc.x = CREAL(1.0);  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = CREAL(0.0);  angle = (RwReal)rfRot.val; 
  2462.          break;
  2463.          case ID_JROTY:
  2464.           rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(1.0);   rfRel.vc.z = CREAL(0.0);  angle = (RwReal)rfRot.val;    
  2465.          break;
  2466.          case ID_JROTZ:
  2467.           rfRel.vc.x = CREAL(0.0);  rfRel.vc.y = CREAL(0.0);   rfRel.vc.z = CREAL(1.0);  angle = (RwReal)rfRot.val;
  2468.          break;
  2469.          case ID_JROTU:
  2470.           dialogProc = MakeProcInstance(RotateDlgProc, AppInstance);
  2471.           if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(RDD_ROTATE),
  2472.                window, dialogProc, (LPARAM)SelectedClump)==IDCANCEL) {    
  2473.                  FreeProcInstance(dialogProc);
  2474.                  return;
  2475.                  }
  2476.           rfRel.vc.x = rfRot.x; rfRel.vc.y = rfRot.y; rfRel.vc.z = rfRot.z;
  2477.           angle = (RwReal) rfRot.val;
  2478.           FreeProcInstance(dialogProc);
  2479.          break;
  2480.          }
  2481.    
  2482.         RwPushScratchMatrix(); 
  2483.          RwRotateMatrix(RwScratchMatrix(),rfRel.vc.x, rfRel.vc.y, rfRel.vc.z, angle, rwREPLACE);                    
  2484.   
  2485.           if(rfRot.repeat!=1) rfRepeatMode = TRUE;
  2486.           for (i=0; i<rfRot.repeat; i++) {
  2487.             RwTransformClumpJoint(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
  2488.             if(i == rfRot.repeat-1) rfRepeatMode = FALSE;
  2489.               RenderScene(window);
  2490.               }
  2491.          RwPopScratchMatrix();
  2492.       break;
  2493.                   
  2494.      
  2495.       // ********************************************** materials ******************************************
  2496.       // turn on/off material animation
  2497.       case ID_MANIMATE:
  2498.             if (rfSurf.MatAnimate) { // turn it off
  2499.                 rfSurf.MatAnimate = FALSE;
  2500.                 CheckMenuItem(GetMenu(window), ID_MANIMATE, MF_BYCOMMAND | MF_UNCHECKED);
  2501.                 }
  2502.              else  { // turn it on
  2503.                 rfSurf.MatAnimate = TRUE;
  2504.                 CheckMenuItem(GetMenu(window), ID_MANIMATE, MF_BYCOMMAND | MF_CHECKED);
  2505.                 }
  2506.       break;
  2507.       
  2508.       // turn on/off ambient component of material animation
  2509.       case MRF_AMBIENT:
  2510.             if (rfSurf.Ambient) { // turn it off
  2511.                 rfSurf.Ambient = FALSE;
  2512.                 CheckMenuItem(GetMenu(window), MRF_AMBIENT, MF_BYCOMMAND | MF_UNCHECKED);
  2513.                 }
  2514.              else  { // turn it on
  2515.                 rfSurf.Ambient = TRUE;
  2516.                 CheckMenuItem(GetMenu(window), MRF_AMBIENT, MF_BYCOMMAND | MF_CHECKED);
  2517.                 }
  2518.       break;
  2519.  
  2520.       // turn on/off diffuse component of material animation
  2521.       case MRF_DIFFUSE:
  2522.             if (rfSurf.Diffuse) { // turn it off
  2523.                 rfSurf.Diffuse = FALSE;
  2524.                 CheckMenuItem(GetMenu(window), MRF_DIFFUSE, MF_BYCOMMAND | MF_UNCHECKED);
  2525.                 }
  2526.              else  { // turn it on
  2527.                 rfSurf.Diffuse = TRUE;
  2528.                 CheckMenuItem(GetMenu(window), MRF_DIFFUSE, MF_BYCOMMAND | MF_CHECKED);
  2529.                 }
  2530.             break;
  2531.  
  2532.       // turn on/off specular component of material animation
  2533.       case MRF_SPECULAR:
  2534.             if (rfSurf.Specular) { // turn it off
  2535.                 rfSurf.Specular = FALSE;
  2536.                 CheckMenuItem(GetMenu(window), MRF_SPECULAR, MF_BYCOMMAND | MF_UNCHECKED);
  2537.                 }
  2538.              else  { // turn it on
  2539.                 rfSurf.Specular = TRUE;
  2540.                 CheckMenuItem(GetMenu(window), MRF_SPECULAR, MF_BYCOMMAND | MF_CHECKED);
  2541.                 }
  2542.       break;
  2543.  
  2544.        // ********************************************** select Chapters for path ******************************************   
  2545.       case IDM_CHAP5:
  2546.          strcpy(fileName,"\\l3d\\chap5\\box"); RwSetShapePath(fileName, rwREPLACE);  
  2547.          strcpy(fileName,"\\l3d\\chap5\\poly"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2548.          strcpy(fileName,"\\l3d\\chap5\\robot"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2549.          strcpy(fileName,"\\l3d\\chap5\\tex1"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2550.       break;
  2551.       case IDM_CHAP6:
  2552.          strcpy(fileName,"\\l3d\\chap6\\kitchen"); RwSetShapePath(fileName, rwREPLACE);  
  2553.          strcpy(fileName,"\\l3d\\chap6\\ship"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2554.          strcpy(fileName,"\\l3d\\chap6\\robot"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2555.        break;
  2556.       case IDM_CHAP7:
  2557.          strcpy(fileName,"\\l3d\\chap7\\ball"); RwSetShapePath(fileName, rwREPLACE);  
  2558.          strcpy(fileName,"\\l3d\\chap7\\color"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2559.        break;
  2560.        case IDM_CHAP8:
  2561.          strcpy(fileName,"\\l3d\\chap8\\box"); RwSetShapePath(fileName, rwREPLACE);  
  2562.          strcpy(fileName,"\\l3d\\chap8\\fore"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2563.          strcpy(fileName,"\\l3d\\chap8\\turn"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2564.          strcpy(fileName,"\\l3d\\chap8\\wall"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2565.       break;
  2566.        case IDM_CHAP9:
  2567.          strcpy(fileName,"\\l3d\\chap9\\ani"); RwSetShapePath(fileName, rwREPLACE);  
  2568.          strcpy(fileName,"\\l3d\\chap9\\box"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2569.          strcpy(fileName,"\\l3d\\chap9\\mask"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2570.          strcpy(fileName,"\\l3d\\chap9\\poly"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2571.          strcpy(fileName,"\\l3d\\chap9\\ship"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2572.          strcpy(fileName,"\\l3d\\chap9\\tex1"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2573.          strcpy(fileName,"\\l3d\\chap9\\tex2"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2574.       break;
  2575.       case IDM_CHAP10:
  2576.          strcpy(fileName,"\\l3d\\chap10"); RwSetShapePath(fileName, rwREPLACE);  
  2577.       break;
  2578.       case IDM_CHAP11:
  2579.          strcpy(fileName,"\\l3d\\chap11"); RwSetShapePath(fileName, rwREPLACE);  
  2580.       break;
  2581.       case IDM_CHAP12:
  2582.          strcpy(fileName,"\\l3d\\chap12\\car"); RwSetShapePath(fileName, rwREPLACE);  
  2583.          strcpy(fileName,"\\l3d\\chap12\\f16"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2584.          strcpy(fileName,"\\l3d\\chap12\\tracks"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2585.          strcpy(fileName,"\\l3d\\chap12\\triad"); RwSetShapePath(fileName, rwPOSTCONCAT);
  2586.       break;
  2587.       case IDM_CHAP13:
  2588.          strcpy(fileName,"\\l3d\\chap13\\compos"); RwSetShapePath(fileName, rwREPLACE);  
  2589.          strcpy(fileName,"\\l3d\\chap13\\coral"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2590.       break;
  2591.       case IDM_CHAP15:
  2592.          strcpy(fileName,"\\l3d\\chap15"); RwSetShapePath(fileName, rwREPLACE);  
  2593.       break;
  2594.       case IDM_CHAP19:
  2595.          strcpy(fileName,"\\l3d\\chap19\\dolphin"); RwSetShapePath(fileName, rwREPLACE);  
  2596.          strcpy(fileName,"\\l3d\\chap19\\face"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2597.          strcpy(fileName,"\\l3d\\chap19\\include"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2598.          strcpy(fileName,"\\l3d\\chap19\\misc"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2599.          strcpy(fileName,"\\l3d\\chap19\\reflect"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2600.          strcpy(fileName,"\\l3d\\chap19\\robot"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2601.          strcpy(fileName,"\\l3d\\chap19\\share"); RwSetShapePath(fileName, rwPOSTCONCAT);
  2602.          strcpy(fileName,"\\l3d\\chap19\\triad"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2603.          strcpy(fileName,"\\l3d\\chap19\\tv"); RwSetShapePath(fileName, rwPOSTCONCAT);  
  2604.       break;     
  2605.        // ********************************************** amimate textures ******************************************   
  2606.        /* each animate cycle, a certain number of frames are skipped.  The more frames that are skipped,
  2607.        the faster the animation plays */
  2608.        // increment the number of frames to skip per frame to speed the animation
  2609.        case IDM_FASTER:
  2610.         rfChangeAnimateSpeed(SelectedClump,1);
  2611.        break;
  2612.        
  2613.        // decrement the number of frames to skip per frame to slow the animation
  2614.        case IDM_SLOWER:
  2615.         rfChangeAnimateSpeed(SelectedClump,-1);      
  2616.        break;
  2617.  
  2618.        /* Each timer cycle, the textureRate amount is added to an accumulator.  Every overflow of the accumulator
  2619.           causes the texture animation to go to its next frame */
  2620.        // accelerate the texture frame rate by making the textureRate larger
  2621.        case IDM_ACCELERATE:
  2622.          textureRate += (float)0.1;
  2623.          if(textureRate>=1.0) textureRate=(float)1.0;
  2624.        break;
  2625.        
  2626.        // decelerate the texture frame rate by making the textureRate smaller
  2627.        case IDM_DECELERATE:
  2628.         textureRate -= (float)0.1;
  2629.         if(textureRate<=0.1) textureRate=(float)0.1;     
  2630.        break;
  2631.    
  2632.         // ********************************************** modify raster ******************************************
  2633.         // change a texture raster for either 8-bit or 16-bit display modes
  2634.         case IDM_MODTEXRASTER:
  2635.              if(DisplayDepth==16)
  2636.                  badret = rfModifyTextureRaster16(SelectedClump);
  2637.               else
  2638.                  badret = rfModifyTextureRaster8(SelectedClump);
  2639.                if(badret)
  2640.                {
  2641.                 RenderScene(window);
  2642.                }
  2643.          break;
  2644.   
  2645.          // change a backdrop raster for either 8-bit or 16-bit display modes
  2646.          case IDM_MODBACKRASTER:
  2647.                CurrentWindow = window;
  2648.                if(DisplayDepth==16)
  2649.                 badret = rfModifyBackdropRaster16(Camera);
  2650.               else
  2651.                 badret = rfModifyBackdropRaster8(Camera);
  2652.              if(badret)
  2653.                {
  2654.                 RwInvalidateCameraViewport(Camera);
  2655.                 RenderScene(window);
  2656.                }
  2657.          break;       
  2658.  
  2659.         // ********************************************** sprites ******************************************
  2660.         //when set, a (.bmp) file is read onto a newly created sprite as opposed to the background raster
  2661.         case MRF_READSPRITES:
  2662.            if (rfReadSprite) { // turn it off
  2663.                 rfReadSprite = FALSE;
  2664.                 CheckMenuItem(GetMenu(window), MRF_READSPRITES, MF_BYCOMMAND | MF_UNCHECKED);
  2665.                 }
  2666.              else  { // turn it on
  2667.                 rfReadSprite = TRUE;
  2668.                 CheckMenuItem(GetMenu(window), MRF_READSPRITES, MF_BYCOMMAND | MF_CHECKED);
  2669.                 }
  2670.         break;
  2671.   
  2672.        // ********************************************** bird's eye view ******************************************
  2673.        // when set, a sprite is created with all 256 colors present as 16 by 16 color boxes thereby showing the entire palette
  2674.        case MRF_PALETTEVIEW:
  2675.           if (!rfPaletteView) { // turn it on
  2676.                 rfPaletteView = TRUE;
  2677.                 RwSetClumpState(PaletteSprite, rwON);
  2678.                 CheckMenuItem(GetMenu(window), MRF_PALETTEVIEW, MF_BYCOMMAND | MF_CHECKED);
  2679.                 }
  2680.              else  { // turn it off
  2681.                 rfPaletteView = FALSE;
  2682.                 RwSetClumpState(PaletteSprite, rwOFF);
  2683.                 CheckMenuItem(GetMenu(window), MRF_PALETTEVIEW, MF_BYCOMMAND | MF_UNCHECKED);
  2684.  
  2685.  
  2686.                 }        
  2687.             RenderScene(window);
  2688.         break;
  2689.         
  2690.        
  2691.        // ********************************************** bird's eye view ******************************************
  2692.        /* when on, a bird's eye sprite is created and onto the raster of this sprite is placed a second camera viewport */
  2693.         case MRF_BIRDSEYE:
  2694.           if (rfBirdsEye) { // turn it off
  2695.                 rfBirdsEye = FALSE;
  2696.                 CheckMenuItem(GetMenu(window), MRF_BIRDSEYE, MF_BYCOMMAND | MF_UNCHECKED);
  2697.                 }
  2698.              else  { // turn it on
  2699.                 rfBirdsEye = TRUE;
  2700.                 CheckMenuItem(GetMenu(window), MRF_BIRDSEYE, MF_BYCOMMAND | MF_CHECKED);
  2701.                 }        
  2702.             RenderScene(window);
  2703.         break;
  2704.         
  2705.        
  2706.         // ********************************************** regular rwView ******************************************     
  2707.         case IDM_FILE_OPEN:
  2708.             /*
  2709.              * Use a common dialog to get the name of the file to load.
  2710.              * We intialize things with the directory and name of the last
  2711.              * filename loaded.
  2712.              */
  2713.             _splitpath(LastFileName, directory, dir, fileName, ext);
  2714.             strcat(fileName, ext);
  2715.             strcat(directory, dir);
  2716.             len = strlen(directory);
  2717.             if ((len > 0) && (directory[len - 1] == '\\'))
  2718.                 directory[len - 1] = '\0';
  2719.             
  2720.             memset(&ofn, 0, sizeof(OPENFILENAME));
  2721.             ofn.lStructSize = sizeof(OPENFILENAME);
  2722.             ofn.hwndOwner = window;
  2723.             ofn.lpstrFilter = "RW Object (*.rwx)\0*.rwx\0Backdrop (*.bmp)\0*.bmp\0Spline (*.rrc)\0*.rrc\0Palette (*.pal)\0*.pal\0";
  2724.             ofn.nFilterIndex = 1;
  2725.             ofn.lpstrFile= fileName;
  2726.             ofn.nMaxFile = sizeof(fileName);
  2727.             ofn.lpstrInitialDir = directory;
  2728.             ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  2729.             if (GetOpenFileName(&ofn))
  2730.             {
  2731.                 /*
  2732.                  * Save the file name for next time.
  2733.                  */
  2734.                 strcpy(LastFileName, fileName);
  2735.                 
  2736.                 /*
  2737.                  * Use LoadObject() to load an object of the appropriate type from
  2738.                  * the given file. LoadObject() determines the object type to load
  2739.                  * from the extension of the file.
  2740.                  */
  2741.                 if (LoadObject(window, fileName))
  2742.                 {
  2743.                     /*
  2744.                      * Update the MRU file list and menus.
  2745.                      */
  2746.                     DeleteMRUFileMenuItems(window);
  2747.                     AddFileToMRUFileList(fileName);
  2748.                     AppendMRUFileMenuItems(window);
  2749.                     RenderScene(window);
  2750.                 }
  2751.             }
  2752.             break;
  2753.             
  2754.         case IDM_FILE_SAVE:
  2755.             /*
  2756.              * Ensure there is a clump and its not the representation
  2757.              * of a light (we don't save those).
  2758.              */
  2759.             if ((SelectedClump != NULL) && (!ISCLUMPLIGHT(SelectedClump)))
  2760.             {
  2761.                 /*
  2762.                  * Write out under the filename stored with the clump.
  2763.                  */
  2764.                 oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2765.                 status = RwWriteShape(GETCLUMPFILENAME(SelectedClump), SelectedClump);
  2766.                 SetCursor(oldCursor);
  2767.                 
  2768.                 if (!status)
  2769.                 {
  2770.                     sprintf(buffer, "Could not write the file %s",
  2771.                             GETCLUMPFILENAME(SelectedClump));
  2772.                     MessageBox(window, buffer, WINDOWTITLE,
  2773.                                MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  2774.                 }
  2775.             }
  2776.             break;
  2777.             
  2778.         case IDM_FILE_SAVEAS:
  2779.             /*
  2780.              * Ensure there is a clump and its not the representation
  2781.              * of a light (we don't save those).
  2782.              */
  2783.             if ((SelectedClump != NULL) && (!ISCLUMPLIGHT(SelectedClump)))
  2784.             {
  2785.                 /*
  2786.                  * Use a common dialog to get the name of the file to save.
  2787.                  * We intialize things with the current filename of the clump
  2788.                  * to be saved.
  2789.                  */
  2790.                 _splitpath(GETCLUMPFILENAME(SelectedClump), directory, dir, fileName, ext);
  2791.                 strcat(fileName, ext);
  2792.                 strcat(directory, dir);
  2793.                 len = strlen(directory);
  2794.                 if ((len > 0) && (directory[len - 1] == '\\'))
  2795.                     directory[len - 1] = '\0';
  2796.             
  2797.                 memset(&ofn, 0, sizeof(OPENFILENAME));
  2798.                 ofn.lStructSize = sizeof(OPENFILENAME);
  2799.                 ofn.hwndOwner = window;
  2800.                 ofn.lpstrFilter = "RW Object (*.rwx)\0*.rwx\0";
  2801.                 ofn.nFilterIndex = 1;
  2802.                 ofn.lpstrFile= fileName;
  2803.                 ofn.nMaxFile = sizeof(fileName);
  2804.                 ofn.lpstrInitialDir = directory;
  2805.                 ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
  2806.                 if (GetSaveFileName(&ofn))
  2807.                 {
  2808.                     /*
  2809.                      * Write out under the new filename.
  2810.                      */
  2811.                     oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2812.                     status = RwWriteShape(fileName, SelectedClump);
  2813.                     SetCursor(oldCursor);
  2814.                 
  2815.                     if (!status)
  2816.                     {
  2817.                         sprintf(buffer, "Could not write the file %s",
  2818.                                 fileName);
  2819.                         MessageBox(window, buffer, WINDOWTITLE,
  2820.                                    MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
  2821.                     }
  2822.                 }
  2823.                 
  2824.                 /*
  2825.                  * Update the clump's stored filename to reflect the new file it
  2826.                  * is stored int.
  2827.                  */
  2828.                 strcpy(GETCLUMPFILENAME(SelectedClump), fileName);
  2829.             }
  2830.             break;
  2831.             
  2832.         case IDM_FILE_EXIT:
  2833.             /*
  2834.              * Close the viewer.
  2835.              */
  2836.             SendMessage(window, WM_CLOSE, 0, 0L);
  2837.             break;
  2838.         
  2839.         case IDM_FILE_MRUFILE:
  2840.         case IDM_FILE_MRUFILE + 1:
  2841.         case IDM_FILE_MRUFILE + 2:
  2842.         case IDM_FILE_MRUFILE + 3:
  2843.             if (LoadObject(window, MRUFiles[item - IDM_FILE_MRUFILE]))
  2844.                 RenderScene(window);
  2845.             break;
  2846.             
  2847.         case IDM_EDIT_DELETE:
  2848.             /*
  2849.              * Delete the picked clump (if there is one) but don't delete a sprite or spline.
  2850.              */
  2851.            if( (GETOBJECTTYPE(SelectedClump)==rfIsClump) || (GETOBJECTTYPE(SelectedClump)==rfIsLight)
  2852.             || (GETOBJECTTYPE(SelectedClump)==rfNoMoveClump)  || (GETOBJECTTYPE(SelectedClump)==rfIsSprite)) 
  2853.             {
  2854.              if (SelectedClump != NULL)
  2855.               {
  2856.                 DestroyClumpObj(SelectedClump);
  2857.                 DeselectClump(window);
  2858.                 RenderScene(window);
  2859.               }
  2860.             }
  2861.             break;
  2862.             
  2863.         case IDM_SCENE_NEW:
  2864.             /*
  2865.              * Ensure the user really wishes to discard this scene and its
  2866.              * contents.
  2867.              */
  2868.             if (MessageBox(window, "Discard this 3D Scene?", WINDOWTITLE,
  2869.                            MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
  2870.             {                          
  2871.                  CreateNewScene(window);
  2872.              }
  2873.             break;
  2874.         
  2875.         case IDM_SCENE_BACKGROUNDCOLOR:
  2876.         case IDM_BACKGROUNDPOPUP_COLOR:
  2877.             /*
  2878.              * Change the background color of the world.
  2879.              */
  2880.             dialogProc = MakeProcInstance(ColorDlgProc, AppInstance);
  2881.             RwGetCameraBackColor(Camera, &color);
  2882.             if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_COLORPICKER),
  2883.                                window, dialogProc, (LPARAM)&color) == IDOK)
  2884.             {
  2885.                 /*
  2886.                  * Change the background camera's background color and re-render.
  2887.                  */
  2888.                 SettingsChanged = TRUE;
  2889.                 BackgroundColor = RGBToColorRef(&color);
  2890.                 RwSetCameraBackColorStruct(Camera, &color);
  2891.                 RwInvalidateCameraViewport(Camera);
  2892.                 RenderScene(window);
  2893.             }
  2894.             FreeProcInstance(dialogProc);
  2895.             break;
  2896.             
  2897.         case IDM_CAMERA_ELEVATIONVIEW:
  2898.         case IDM_CAMERA_RESET:
  2899.             /*
  2900.              * Reset the camera's viewing position.
  2901.              */
  2902.             ResetCamera(Camera);
  2903.             RenderScene(window);
  2904.             break;
  2905.             
  2906.         case IDM_CAMERA_PLANVIEW:
  2907.             /*
  2908.              * Put the camera in plan view, i.e., looking down.
  2909.              */
  2910.             RwSetCameraPosition(Camera, CREAL(0.0), DEFAULTCAMERADISTANCE, CREAL(0.0));
  2911.             RwPointCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.0));
  2912.             RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(0.0), CREAL(-1.0));
  2913.             RenderScene(window);
  2914.             break;
  2915.             
  2916.         case IDM_CAMERA_SIDEVIEW:
  2917.             /*
  2918.              * Put the camera in side view.
  2919.              */
  2920.             RwSetCameraPosition(Camera, DEFAULTCAMERADISTANCE, CREAL(0.0), CREAL(0.0));
  2921.             RwPointCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.0));
  2922.             RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2923.             RenderScene(window);
  2924.             break;
  2925.             
  2926.         case IDM_LIGHT_NEWDIRECTIONAL:
  2927.             light = CreateLightObj(rwDIRECTIONAL);
  2928.             if (light != NULL)
  2929.             {
  2930.                 SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
  2931.                 AddLightObjToScene(Scene, light);
  2932.                 RenderScene(window);
  2933.             }
  2934.             else
  2935.             {
  2936.                 MessageBox(window,
  2937.                            "Could not create the RenderWare light",
  2938.                            WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  2939.             }
  2940.             break;
  2941.             
  2942.         case IDM_LIGHT_NEWPOINT:
  2943.             light = CreateLightObj(rwPOINT);
  2944.             if (light != NULL)
  2945.             {
  2946.                 SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
  2947.                 AddLightObjToScene(Scene, light);
  2948.                 RenderScene(window);
  2949.             }
  2950.             else
  2951.             {
  2952.                 MessageBox(window,
  2953.                            "Could not create the RenderWare light",
  2954.                            WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  2955.             }
  2956.             break;
  2957.             
  2958.         case IDM_LIGHT_NEWCONICAL:
  2959.             light = CreateLightObj(rwCONICAL);
  2960.             if (light != NULL)
  2961.             {
  2962.                 SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
  2963.                 AddLightObjToScene(Scene, light);
  2964.                 RenderScene(window);
  2965.             }
  2966.             else
  2967.             {
  2968.                 MessageBox(window,
  2969.                            "Could not create the RenderWare light",
  2970.                            WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  2971.             }
  2972.             break;
  2973.             
  2974.         case IDM_OPTIONS_SHOWLIGHTS:
  2975.             if (ShowLights)
  2976.             {
  2977.                 /*
  2978.                  * If the menu item is currently checked then lights are shown so turn them off and
  2979.                  * uncheck the menu.
  2980.                  */
  2981.                 ShowLights = FALSE;
  2982.                 RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)SetLightObjVisibleState, rwOFF);
  2983.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED);
  2984.             }
  2985.             else
  2986.             {
  2987.                 /*
  2988.                  * If the menu item is currently unchecked then lights are not shown so turn them on and
  2989.                  * check the menu.
  2990.                  */
  2991.                 ShowLights = TRUE;
  2992.                 RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)SetLightObjVisibleState, rwON);
  2993.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED);
  2994.             }
  2995.             SettingsChanged = TRUE;
  2996.             RenderScene(window);
  2997.             break;
  2998.             
  2999.         case IDM_OPTIONS_SHOWHIGHLIGHT:
  3000.             if (ShowHighlight)
  3001.             {
  3002.                 /*
  3003.                  * We are currently showing highlights. So turn the highlight off and
  3004.                  * uncheck the menu item.
  3005.                  */
  3006.                 ShowHighlight = FALSE;
  3007.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT, MF_BYCOMMAND | MF_UNCHECKED);
  3008.             }
  3009.             else
  3010.             {
  3011.                 /*
  3012.                  * We are currently not showing highlights. So turn the highlight on and
  3013.                  * check the menu item.
  3014.                  */
  3015.                 ShowHighlight = TRUE;
  3016.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT, MF_BYCOMMAND | MF_CHECKED);
  3017.             }
  3018.             SettingsChanged = TRUE;
  3019.             RenderScene(window);
  3020.             break;
  3021.             
  3022.         case IDM_OPTIONS_PLAYMOVIES:
  3023.             if (PlayMovies)
  3024.             {
  3025.                 /*
  3026.                  * We are currently playing movies (multi-frame textures). So turn
  3027.                  * the playing off and uncheck the menu item.
  3028.                  */
  3029.                 PlayMovies = FALSE;
  3030.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES, MF_BYCOMMAND | MF_UNCHECKED);
  3031.             }
  3032.             else
  3033.             {
  3034.                 /*
  3035.                  * We currently are not playing movies (multi-frame textures). So turn
  3036.                  * the playing on and check the menu item.
  3037.                  */
  3038.                 PlayMovies = TRUE;
  3039.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES, MF_BYCOMMAND | MF_CHECKED);
  3040.             }
  3041.             SettingsChanged = TRUE;
  3042.             break;
  3043.             
  3044.  
  3045.         case IDM_OPTIONS_MOMENTUM:
  3046.             if (Momentum)
  3047.             {
  3048.                 /*
  3049.                  * We are currently have object momentum. So turn it off and uncheck
  3050.                  * the menu item.
  3051.                  */
  3052.                 Momentum = FALSE;
  3053.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM, MF_BYCOMMAND | MF_UNCHECKED);
  3054.             }
  3055.             else
  3056.             {
  3057.                 /*
  3058.                  * We are currently don't have object momentum. So turn it on and check
  3059.                  * the menu item.
  3060.                  */
  3061.                 Momentum = TRUE;
  3062.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM, MF_BYCOMMAND | MF_CHECKED);
  3063.             }
  3064.             SettingsChanged = TRUE;
  3065.             break;
  3066.             
  3067.         case IDM_OPTIONS_CENTERBACKDROP:
  3068.             if (CenterBackdrop)
  3069.             {
  3070.                 /*
  3071.                  * We are currently a centered backdrop. So turn it off and uncheck
  3072.                  * the menu item.
  3073.                  */
  3074.                 CenterBackdrop = FALSE;
  3075.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP, MF_BYCOMMAND | MF_UNCHECKED);
  3076.             }
  3077.             else
  3078.             {
  3079.                 /*
  3080.                  * We are currently don't have a centered backdrop. So turn it on and check
  3081.                  * the menu item.
  3082.                  */
  3083.                 CenterBackdrop = TRUE;
  3084.                 CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP, MF_BYCOMMAND | MF_CHECKED);
  3085.             }
  3086.             UpdateCameraBackdropPosition(Camera);
  3087.             RenderScene(window);
  3088.             SettingsChanged = TRUE;
  3089.             break;
  3090.             
  3091.         case IDM_OPTIONS_SEARCHPATH:
  3092.             /* Display the search path box   */
  3093.             dialogProc = MakeProcInstance(SearchPathDlgProc, AppInstance);
  3094.             DialogBox(AppInstance, MAKEINTRESOURCE(IDD_SEARCHPATH), window, dialogProc);
  3095.             FreeProcInstance(dialogProc);
  3096.             break;
  3097.  
  3098.         case IDM_CLUMP_RESET:
  3099.         case IDM_CLUMPPOPUP_RESET:
  3100.             /*
  3101.              * Reset the clump to its default position, orientation and scaling.
  3102.              */
  3103.             RwPushScratchMatrix();
  3104.                 RwIdentityMatrix(RwScratchMatrix());
  3105.                 RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
  3106.                 RwTransformClumpJoint(SelectedClump, RwScratchMatrix(), rwREPLACE);
  3107.             RwPopScratchMatrix();
  3108.             RenderScene(window);
  3109.             break;
  3110.             
  3111.         case IDM_CAMERA_MOVETO:
  3112.         case IDM_LIGHTPOPUP_MOVETO:
  3113.         case IDM_CLUMPPOPUP_MOVETO:
  3114.             /*
  3115.              * Alight the camera with the position and orientation of the given object.
  3116.              */
  3117.             RwPushScratchMatrix();
  3118.                 RwGetClumpLTM(SelectedClump, RwScratchMatrix());
  3119.                 RwTransformCamera(Camera, RwScratchMatrix(), rwREPLACE);
  3120.             RwPopScratchMatrix();
  3121.             RenderScene(window);
  3122.             break;
  3123.             
  3124.         case IDM_CAMERA_LOOKAT:
  3125.         case IDM_LIGHTPOPUP_LOOKAT:
  3126.         case IDM_CLUMPPOPUP_LOOKAT:
  3127.             /*
  3128.              * Make the camera look at the picked object.
  3129.              */
  3130.             RwGetClumpOrigin(SelectedClump, &point);
  3131.             RwPointCamera(Camera, point.x, point.y, point.z);
  3132.             RenderScene(window);
  3133.             break;
  3134.             
  3135.         case IDM_EDIT_DUPLICATE:
  3136.         case IDM_LIGHTPOPUP_DUPLICATE:
  3137.         case IDM_CLUMPPOPUP_DUPLICATE:
  3138.             /*
  3139.              * Duplicate the selected object (which may be a clump
  3140.              * or a light and the clump which represents it).
  3141.              */
  3142.             oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  3143.             newClump = DuplicateClumpObj(SelectedClump);
  3144.             SetCursor(oldCursor);
  3145.             if (newClump != NULL)
  3146.             {
  3147.                 /*
  3148.                  * Move the new clump slightly (an arbitrary amount) to
  3149.                  * the right and down (in camera space) so the new and
  3150.                  * old objects don't occupy the same space.
  3151.                  */
  3152.                 RwGetCameraLookRight(Camera, &right);
  3153.                 right.x = RMul(CREAL(0.2), right.x);
  3154.                 right.y = RMul(CREAL(0.2), right.y);
  3155.                 right.z = RMul(CREAL(0.2), right.z);
  3156.                 RwGetCameraLookUp(Camera, &up);
  3157.                 up.x = RMul(CREAL(-0.2), up.x);
  3158.                 up.y = RMul(CREAL(-0.2), up.y);
  3159.                 up.z = RMul(CREAL(-0.2), up.z);
  3160.                 RwPushScratchMatrix();
  3161.                     RwTranslateMatrix(RwScratchMatrix(), right.x, right.y, right.z, rwREPLACE);
  3162.                     RwTranslateMatrix(RwScratchMatrix(), up.x, up.y, up.z, rwPOSTCONCAT);
  3163.                     RwTransformClump(newClump, RwScratchMatrix(), rwPOSTCONCAT);
  3164.                        
  3165.                     if (ISCLUMPLIGHT(newClump))
  3166.                     {
  3167.                         light = GETCLUMPLIGHT(newClump);
  3168.                         SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
  3169.                         
  3170.                         /*
  3171.                          * If the clump is the representation of a light reposition
  3172.                          * the light to match the new position of the clump.
  3173.                          */
  3174.                         RwGetClumpLTM(newClump, RwScratchMatrix());
  3175.                         RwTransformLight(light, RwScratchMatrix(), rwREPLACE);
  3176.                     }
  3177.                 RwPopScratchMatrix();
  3178.                 RenderScene(window);
  3179.             }
  3180.             else
  3181.             {
  3182.                 MessageBox(window,
  3183.                            "Could not duplicate the RenderWare object",
  3184.                            WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  3185.             }
  3186.             break;
  3187.             
  3188.         case IDM_LIGHTPOPUP_DELETE:
  3189.         case IDM_CLUMPPOPUP_DELETE:
  3190.             /*
  3191.              * Delete the picked clump. If the picked clump is the
  3192.              * representation of a light, delete the associated
  3193.              * light.
  3194.              */
  3195.             DestroyClumpObj(SelectedClump);
  3196.             DeselectClump(window);
  3197.             RenderScene(window);
  3198.             break;
  3199.             
  3200.    
  3201.        case IDM_LIGHTPOPUP_PROPERTIES:
  3202.         case IDM_LIGHT_PROPERTIES:
  3203.             /*
  3204.              * Display the light properties dialog.
  3205.              */
  3206.             dialogProc = MakeProcInstance(LightPropsDlgProc, AppInstance);
  3207.             if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_LIGHTPROPS),
  3208.                                window, dialogProc, (LPARAM)GETCLUMPLIGHT(SelectedClump)) == IDOK)
  3209.                 RenderScene(window);
  3210.             FreeProcInstance(dialogProc);
  3211.             break;
  3212.  
  3213.         case IDM_CLUMPPOPUP_PROPERTIES:
  3214.         case IDM_CLUMP_PROPERTIES:
  3215.             /*
  3216.              * Display the clump properties dialog.
  3217.              */
  3218.             dialogProc = MakeProcInstance(ClumpPropsDlgProc, AppInstance);
  3219.             DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_CLUMPPROPS),
  3220.                            window, dialogProc, (LPARAM)SelectedClump);
  3221.             FreeProcInstance(dialogProc);
  3222.             break;
  3223.             
  3224.         case IDM_SCENE_BACKDROPDELETE:
  3225.         case IDM_BACKGROUNDPOPUP_DELETE:
  3226.             /*
  3227.              * Delete the camera's backdrop (if there is one).
  3228.              */
  3229.             if (RwGetCameraBackdrop(Camera) != NULL)
  3230.             {
  3231.                 RwDestroyRaster(RwGetCameraBackdrop(Camera));
  3232.                 RwSetCameraBackdrop(Camera, NULL);
  3233.                 EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE, MF_BYCOMMAND | MF_GRAYED);
  3234.                 BackdropFileName[0] = '\0';
  3235.                 SettingsChanged = TRUE;
  3236.                 RenderScene(window);
  3237.             }
  3238.             break;
  3239.  
  3240.          case IDM_HELP_ABOUT:
  3241.             /*
  3242.              * Display the about box.
  3243.              */
  3244.             dialogProc = MakeProcInstance(AboutBoxDlgProc, AppInstance);
  3245.             DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), window, dialogProc);
  3246.             FreeProcInstance(dialogProc);
  3247.             break;  
  3248.  
  3249.     }
  3250. }
  3251.  
  3252. /**********************************************************************/
  3253.  
  3254. /*
  3255.  * Handle MS Window's timer expiry. This function will perform any
  3256.  * animation actions necessary, including spinning clumps and animating
  3257.  * textures.
  3258.  */
  3259. static void
  3260. OnTimer(HWND window)
  3261. {
  3262.     BOOL render = FALSE;
  3263.     RwPolygon3d *myPoly;
  3264.     RwMaterial *myMaterial=RwCreateMaterial();
  3265.     RwInt32 rate;       
  3266.     
  3267.      /* ********************************************** surface animation ************************************* */
  3268.      // If surface is on, then change the surface of the selected clump
  3269.      if(rfSurf.Ambient) {  // update the ambient reflection coefficient
  3270.        RwForAllPolygonsInClumpReal(SelectedClump, RwSetPolygonAmbient, rfSurf.vAmbient);
  3271.        rfSurf.vAmbient += rfSurf.AmbientInc;    if(rfSurf.vAmbient>=CREAL(1.0)) rfSurf.vAmbient=CREAL(0.0);
  3272.        render = TRUE;
  3273.        }
  3274.      if(rfSurf.Diffuse) {  // update the diffuse reflection coefficient
  3275.        RwForAllPolygonsInClumpReal(SelectedClump, RwSetPolygonDiffuse, rfSurf.vDiffuse);
  3276.        rfSurf.vDiffuse += rfSurf.DiffuseInc;    if(rfSurf.vDiffuse>=CREAL(1.0)) rfSurf.vDiffuse=CREAL(0.0);
  3277.        render = TRUE;
  3278.        }
  3279.      if(rfSurf.Specular) {  // update the specular reflection coefficient
  3280.        RwForAllPolygonsInClumpReal(SelectedClump, RwSetPolygonSpecular, rfSurf.vSpecular);
  3281.        rfSurf.vSpecular += rfSurf.SpecularInc;    if(rfSurf.vSpecular>=CREAL(1.0)) rfSurf.vSpecular=CREAL(0.0);
  3282.        render = TRUE;
  3283.        }
  3284.       if(rfSurf.MatAnimate) { // update the ambient reflection coefficient of the tagged polygon of the selected clump
  3285.        myPoly = RwFindTaggedPolygon(SelectedClump,WINDOWTAG);
  3286.        myMaterial = RwGetPolygonMaterial(myPoly);
  3287.        if(myPoly!=NULL) {
  3288.           RwSetMaterialAmbient(myMaterial,rfSurf.vAmbient);
  3289.           rfSurf.vAmbient += rfSurf.AmbientInc;    if(rfSurf.vAmbient>=CREAL(1.0)) rfSurf.vAmbient=CREAL(0.0);
  3290.           render = TRUE;
  3291.           }
  3292.        }      
  3293.  
  3294.     // if an object is traveling the spline then update its location
  3295.     if(TravelSpline) {  
  3296.       coaster->t = RAdd(coaster->t,coaster->tDelta);  // advance to next spline locatin
  3297.       PositionObjectOnSpline(coaster,SelectedClump);
  3298.       render = TRUE;   
  3299.     }
  3300.      
  3301.        
  3302.      /*                                    
  3303.      * Determine if there is a clump to spin (we also disable the spin
  3304.      * if the mouse is being dragged).
  3305.      */
  3306.     if (Momentum && SpinClump && (MouseMoveMode == mmNoAction))
  3307.     {
  3308.         /*
  3309.          * Spin the last clump picked by the last computed spin matrix.
  3310.          */
  3311.         RwTransformClumpJoint(SelectedClump, SpinMatrix, rwPOSTCONCAT);
  3312.         render = TRUE;
  3313.         
  3314.         if ((FrameNumber++ & 0x7f) == 0)
  3315.         {
  3316.             /*
  3317.              * Every 128 frames (a somewhat arbitrary frequency) we call
  3318.              * RwOrthoNormalizeMatrix() on the clump's joint matrix to
  3319.              * correct any errors which may have crept into it during
  3320.              * successive matrix concatenations. This is necessary due
  3321.              * to the inevitable accuracy limitations of fixed-point
  3322.              * numbers. It is unlikely that this action will be necessary
  3323.              * in your own applications unless you perform a very large
  3324.              * number of incremental rotation matrix concatenations (as    
  3325.              * is done here). If this is the case with your application,
  3326.              * periodic use of RwOrthoNormalizeMatrix() will ensure
  3327.              * that these rounding errors will be eliminated.
  3328.              *
  3329.              * We call RwOrthoNormalizeMatrix() in the floating-point
  3330.              * version of RwView as well, as it is not a particularly
  3331.              * expensive operation and the same problems of rounding
  3332.              * errors in matrices can occur when using floating-point.
  3333.              * Although they are more uncommon.
  3334.              */
  3335.             RwPushScratchMatrix();
  3336.                 RwGetClumpJointMatrix(SelectedClump, RwScratchMatrix());
  3337.                 RwOrthoNormalizeMatrix(RwScratchMatrix(), RwScratchMatrix());
  3338.                 RwTransformClumpJoint(SelectedClump, RwScratchMatrix(), rwREPLACE);
  3339.             RwPopScratchMatrix();
  3340.         }
  3341.     }
  3342.  
  3343.  
  3344.     // if texture animation is enabled then 
  3345.     if (PlayMovies)
  3346.     {
  3347.         rate = (int) accumulatedRate;    // new rate is the integer porition of the accumulated rate
  3348.         accumulatedRate += textureRate;  // add the texture rate increment to the accumulated rate
  3349.         if((int) accumulatedRate > rate)  // only upgrade when integer overflow occurs
  3350.           {
  3351.           RwForAllNamedTextures(RwTextureNextFrame);  // do for all named textures
  3352.           render = TRUE;
  3353.           }
  3354.     }
  3355.  
  3356.     if (render)
  3357.     {
  3358.         /*
  3359.          * Re-render the scene if necessary.
  3360.          */
  3361.         RenderScene(window);
  3362.     }
  3363. }
  3364.  
  3365.  
  3366.  
  3367.          
  3368.  
  3369. /**********************************************************************/
  3370.  
  3371. /*
  3372.  * The window procedure for this application's window.
  3373.  */
  3374. LRESULT CALLBACK
  3375. MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
  3376. {
  3377.     POINT point;
  3378. #if defined(WIN32)
  3379.     POINTS points;
  3380. #endif
  3381.     
  3382.     switch (message)
  3383.     {
  3384.         case WM_CREATE:
  3385.             /*
  3386.              * Create the pop-up menus.
  3387.              */
  3388.             if (!CreatePopupMenus())
  3389.                 return -1L;
  3390.             
  3391.             /*
  3392.              * Ensure the menu items are in the correct state.
  3393.              */
  3394.             EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE,
  3395.                            MF_BYCOMMAND | (RwGetCameraBackdrop(Camera) != NULL ? MF_ENABLED : MF_GRAYED));
  3396.             CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT,
  3397.                           MF_BYCOMMAND | (ShowHighlight ? MF_CHECKED : MF_UNCHECKED));
  3398.             CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS,
  3399.                           MF_BYCOMMAND | (ShowLights ? MF_CHECKED : MF_UNCHECKED));
  3400.             CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES,
  3401.                           MF_BYCOMMAND | (PlayMovies ? MF_CHECKED : MF_UNCHECKED));
  3402.             CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM,
  3403.                           MF_BYCOMMAND | (Momentum ? MF_CHECKED : MF_UNCHECKED));
  3404.             CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP,
  3405.                           MF_BYCOMMAND | (CenterBackdrop ? MF_CHECKED : MF_UNCHECKED));
  3406.                           
  3407.             AppendMRUFileMenuItems(window);
  3408.             
  3409.             /*
  3410.              * There is not initially selected clump.
  3411.              */
  3412.             DeselectClump(window);
  3413.  
  3414.             /*
  3415.              * Clumps are loaded into the scene by drag and drop.
  3416.              * So make this window a drop site.
  3417.              */
  3418.             DragAcceptFiles(window, TRUE);
  3419.  
  3420.             /*
  3421.              * In this application, 3D animation is driven by window's
  3422.              * timer messages, so turn a timer on.
  3423.              */
  3424.             SetTimer(window, 1, 20, NULL);
  3425.             
  3426.              // *********************************** initialize the animating materials variables  ************************
  3427.              rfSurf.vAmbient = CREAL(0.0);  rfSurf.vDiffuse=CREAL(0.0); rfSurf.vSpecular=CREAL(0.0);
  3428.              rfSurf.AmbientInc = CREAL(0.01);  rfSurf.DiffuseInc=CREAL(0.01); rfSurf.SpecularInc=CREAL(0.01);
  3429.              rfReadSprite=FALSE;          // use BMP as backdrop not sprites
  3430.              rfBirdsEye=FALSE;            // don't display bird's eye sprite
  3431.              rfPaletteView=FALSE;             // don't display palette sprite
  3432.              RwSetClumpState(PaletteSprite, rwOFF); // start with off
  3433.              rfSurf.Ambient=FALSE;            // don't animate ambient
  3434.              rfSurf.Diffuse=FALSE;            // don't diffuse ambient
  3435.              rfSurf.Specular=FALSE;           // don't specular ambient
  3436.              rfSurf.MatAnimate=FALSE;         // don't animate ambient of tagged polygon
  3437.                                                  
  3438.              // *********************************************** initialize debugging ***************************************
  3439.              rfDebug.Severity=rwWARNING;
  3440.              rfDebug.Message=rwDISABLE;
  3441.              rfDebug.Assertion=rwENABLE;
  3442.              rfDebug.Script=rwDISABLE;
  3443.              rfDebug.Output=rwENABLE;
  3444.              rfDebug.Trace=rwENABLE;
  3445.       
  3446.              // *********************************************** initialize the transformations  *******************************
  3447.              // rotation about the Y axis in 15 degrees repreating 10 times according to the world coordiante system
  3448.              rfScale.x=CREAL(1.0); rfScale.y=CREAL(1.0); rfScale.z=CREAL(1.0); rfScale.combine=rwPOSTCONCAT;
  3449.              
  3450.              // rotation about the Y axis in 15 degrees repreating 10 times according to the world coordiante system
  3451.              rfTran.x=CREAL(0.0); rfTran.y=CREAL(0.0); rfTran.z=CREAL(0.0); rfTran.combine=rwPOSTCONCAT;
  3452.              
  3453.              // rotation about the Y axis in 15 degrees repreating 10 times according to the world coordiante system
  3454.              rfRot.x=CREAL(0.0); rfRot.y=CREAL(1.0); rfRot.z=CREAL(0.0); rfRot.val=15; rfRot.repeat=10;   rfRot.combine=rwPOSTCONCAT;
  3455.              
  3456.              // pan, revolve and tilt is set to move in 15 degree increments repeating 10 times
  3457.              rfLoc.val=15; rfLoc.repeat=10;  
  3458.              
  3459.              // camera is set to move in 0.2 increments repeating 10 times
  3460.              rfCam.val=CREAL(0.2); rfCam.repeat=10;
  3461.    
  3462.              // figure out the display depth
  3463.              DisplayDepth = Is8or16bpp(window); 
  3464.              
  3465.              //************************************************ Spline **********************************************************
  3466.              HideSpline = FALSE;
  3467.              CheckMenuItem(GetMenu(window), IDM_HIDESPLINETRACK, MF_BYCOMMAND | MF_UNCHECKED);
  3468.              HideControlPoints = FALSE;
  3469.              CheckMenuItem(GetMenu(window), IDM_HIDESPLINECONTROLPOINTS, MF_BYCOMMAND | MF_UNCHECKED);             
  3470.              UseSmoothEnd = FALSE;
  3471.              CheckMenuItem(GetMenu(window), IDM_SMOOTHEND, MF_BYCOMMAND | MF_UNCHECKED);             
  3472.              TravelSpline = FALSE;
  3473.              CheckMenuItem(GetMenu(window), IDM_MOVEOBJECTONSPLINE, MF_BYCOMMAND | MF_UNCHECKED);                    
  3474.              return 0L;
  3475.  
  3476. #if defined(CONSTRAIN_SIZE)
  3477.         case WM_GETMINMAXINFO:
  3478. #if defined(__WINDOWS_386__)
  3479.             /*
  3480.              * The MK_FP32() is necessary under Watcom to convert
  3481.              * an Windows 16 bit far pointer to a 32 bit far pointer.
  3482.              */
  3483.             OnGetMinMaxInfo((MINMAXINFO FAR *)MK_FP32((void*)lParam));
  3484. #else
  3485.             OnGetMinMaxInfo((MINMAXINFO FAR *)lParam);
  3486. #endif
  3487.             return 0L;
  3488. #endif
  3489.  
  3490.         case WM_SIZE:
  3491.             if (RwIsOpen)
  3492.                 OnSize(window, LOWORD(lParam), HIWORD(lParam));
  3493.             return 0L;
  3494.         
  3495.         case WM_MEASUREITEM:
  3496. #if defined(__WINDOWS_386__)
  3497.             /*
  3498.              * The MK_FP32() is necessary under Watcom to convert
  3499.              * an Windows 16 bit far pointer to a 32 bit far pointer.
  3500.              */
  3501.             OnMeasureItem(window, (MEASUREITEMSTRUCT FAR *)MK_FP32((void*)lParam));
  3502. #else
  3503.             OnMeasureItem(window, (MEASUREITEMSTRUCT FAR *)lParam);
  3504. #endif
  3505.             return 0L;
  3506.             
  3507.         case WM_DRAWITEM:
  3508. #if defined(__WINDOWS_386__)
  3509.             /*
  3510.              * The MK_FP32() is necessary under Watcom to convert
  3511.              * an Windows 16 bit far pointer to a 32 bit far pointer.
  3512.              */
  3513.             OnDrawItem(window, (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam));
  3514. #else
  3515.             OnDrawItem(window, (DRAWITEMSTRUCT FAR *)lParam);
  3516. #endif
  3517.             return 0L;
  3518.  
  3519.         case WM_DROPFILES:
  3520.             if (RwIsOpen)
  3521.             {
  3522.                 /*
  3523.                  * Turn the timer off for the duration of the drop
  3524.                  */
  3525.                 KillTimer(window, 1);
  3526.                 
  3527.                 OnDrop(window, (HDROP)wParam);
  3528.                 /*
  3529.                  * Turn the timer back on to start the animations back
  3530.                  * up.
  3531.                  */
  3532.                 SetTimer(window, 1, 20, NULL);
  3533.             }
  3534.             return 0L;
  3535.  
  3536.         case WM_LBUTTONDOWN:
  3537.             if (RwIsOpen)
  3538.             {
  3539. #if defined(WIN32)
  3540.                 points = MAKEPOINTS(lParam);
  3541.                 POINTSTOPOINT(point, points);
  3542. #else
  3543.                 point = MAKEPOINT(lParam);
  3544. #endif
  3545.                 OnLButtonDown(window, &point, wParam);
  3546.             }
  3547.             return 0L;
  3548.  
  3549.         case WM_RBUTTONDOWN:
  3550.             if (RwIsOpen)
  3551.             {
  3552. #if defined(WIN32)
  3553.                 points = MAKEPOINTS(lParam);
  3554.                 POINTSTOPOINT(point, points);
  3555. #else
  3556.                 point = MAKEPOINT(lParam);
  3557. #endif
  3558.                 OnRButtonDown(window, &point, wParam);
  3559.             }
  3560.             return 0L;
  3561.  
  3562.         case WM_MOUSEMOVE:
  3563.             if (RwIsOpen)
  3564.             {
  3565.                 if (MouseMoveMode != mmNoAction)
  3566.                 {
  3567. #if defined(WIN32)
  3568.                     points = MAKEPOINTS(lParam);
  3569.                     POINTSTOPOINT(point, points);
  3570. #else
  3571.                     point = MAKEPOINT(lParam);
  3572. #endif
  3573.                     OnMouseMove(window, &point);
  3574.                 }
  3575.             }
  3576.             return 0L;
  3577.  
  3578.         case WM_LBUTTONUP:
  3579.             if (RwIsOpen)
  3580.                 OnLButtonUp();
  3581.             return 0L;
  3582.  
  3583.         case WM_RBUTTONUP:
  3584.             if (RwIsOpen)
  3585.                 OnRButtonUp();
  3586.             return 0L;
  3587.  
  3588.         case WM_PAINT:
  3589.             if (RwIsOpen)
  3590.                 OnPaint(window);
  3591.             return 0L;
  3592.             
  3593.         case WM_COMMAND:
  3594.             if (RwIsOpen)
  3595.             {
  3596. #ifdef WIN32
  3597.                 HWND hwndCtl = (HWND) lParam;
  3598.                 WORD wID = LOWORD(wParam);
  3599.                 WORD wNotifyCode = HIWORD(wParam);
  3600. #else
  3601.                 HWND hwndCtl = (HWND) LOWORD(lParam);
  3602.                 WORD wID = wParam;
  3603.                 WORD wNotifyCode = HIWORD(lParam);
  3604. #endif
  3605.                 /*
  3606.                  * Turn the timer off for the duration of the command
  3607.                  * action.
  3608.                  */
  3609.                 KillTimer(window, 1);
  3610.                 
  3611.                 if (hwndCtl == (HWND)0)
  3612.                     OnMenu(window, wID);
  3613.                 
  3614.                 /*
  3615.                  * Turn the timer back on to start the animations back
  3616.                  * up.
  3617.                  */
  3618.                 SetTimer(window, 1, 20, NULL);
  3619.             }
  3620.             return 0L;
  3621.             
  3622.         case WM_TIMER:
  3623.             if (RwIsOpen)
  3624.                 OnTimer(window);
  3625.             return 0L;
  3626.             
  3627.         case WM_CLOSE:
  3628.             /*
  3629.              * If the settings have changed then ask the user if she or he
  3630.              * wishes to save them.
  3631.              */
  3632.             if (SettingsChanged)
  3633.             {
  3634.                 if (MessageBox(window, "Save the new 3D Object Viewer settings?",
  3635.                                WINDOWTITLE, MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
  3636.                     WriteSettings(TRUE);
  3637.                 else
  3638.                     WriteSettings(FALSE);
  3639.             }
  3640.             else
  3641.             {
  3642.                 WriteSettings(FALSE);
  3643.             }
  3644.             DestroyWindow(window);
  3645.             return 0L;
  3646.  
  3647.         case WM_DESTROY:          
  3648.             /*
  3649.              * The window is going away so it is no longer a drop site.
  3650.              */
  3651.             DragAcceptFiles(window, FALSE);
  3652.  
  3653.             /*
  3654.              * Turn the timer off.
  3655.              */
  3656.             KillTimer(window, 1);
  3657.             
  3658.             /*
  3659.              * Destroy the pop-up menus.
  3660.              */
  3661.             DestroyPopupMenus();
  3662.  
  3663.             /*
  3664.              * Quit message handling.
  3665.              */
  3666.             PostQuitMessage(0);
  3667.             return 0L;
  3668.     }
  3669.  
  3670.     /*
  3671.      * Let Windows handle all other messages.
  3672.      */
  3673.     return DefWindowProc(window, message, wParam, lParam);
  3674. }
  3675.  
  3676. /**********************************************************************/
  3677.  
  3678. /*
  3679.  * Windows application entry point.
  3680.  */
  3681. int PASCAL
  3682. WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
  3683. {
  3684.     MSG    msg;         
  3685.     HWND   window;
  3686.     HACCEL accel;
  3687.  
  3688.     /*
  3689.      * Cache the instance handle in a global variable for later use.
  3690.      */ 
  3691.     AppInstance = instance;
  3692.  
  3693.     if (prevInstance)
  3694.     {
  3695.         /*
  3696.          * Only allow one viewer application to run at any one time.
  3697.          */
  3698.         MessageBox(NULL,
  3699.                    "The RenderWare 3D Object Viewer is already running."
  3700.                    "Only a single 3D Object Viewer can run at any one time",
  3701.                    WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
  3702.         return FALSE;
  3703.     }
  3704.     
  3705.     /*
  3706.      * Register the window class.
  3707.      */
  3708.     if (!InitApplication(instance))
  3709.         return FALSE;       
  3710.  
  3711.     /*
  3712.      * Read the initial settings from the initialization file.
  3713.      */
  3714.     ReadSettings();
  3715.  
  3716.     /*
  3717.      * Create the window.
  3718.      */
  3719.     window = InitInstance(instance);
  3720.     if (window == NULL)
  3721.         return FALSE;
  3722.  
  3723.     /*
  3724.      * Initialize the 3D (RenderWare) components of the app.
  3725.      */
  3726.     if (!Init3D(window))
  3727.     {
  3728.         DestroyWindow(window);
  3729.         return FALSE;
  3730.     }
  3731.     
  3732.     /*
  3733.      * Parse any command line parameters.
  3734.      */
  3735.     if (!ReadFromCommandLine(window, cmdLine))
  3736.     {
  3737.         TidyUp3D();
  3738.         DestroyWindow(window);
  3739.         return FALSE;
  3740.     }
  3741.  
  3742.     /*
  3743.      * Show the window, and refresh it.
  3744.      */
  3745.     ShowWindow(window, cmdShow); 
  3746.     UpdateWindow(window);
  3747.     
  3748.     /*
  3749.      * Load the accelerators.
  3750.      */
  3751.     accel = LoadAccelerators(AppInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
  3752.  
  3753.     /*
  3754.      * Enter the message processing loop.
  3755.      */
  3756.     while (GetMessage(&msg, NULL, 0U, 0U))
  3757.     {
  3758.         if (!TranslateAccelerator(window, accel, &msg))
  3759.         {
  3760.             TranslateMessage(&msg);
  3761.             DispatchMessage(&msg);
  3762.         }
  3763.     }
  3764.     
  3765.     /*
  3766.      * Tidy up the 3D (RenderWare) components of the application.
  3767.      */
  3768.     TidyUp3D();
  3769.  
  3770.     return msg.wParam;
  3771. }
  3772.  
  3773.  
  3774.