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

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