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

  1. /**********************************************************************
  2.  *
  3.  * File :     rwroller.c
  4.  *
  5.  * Abstract : A virtual Rollercoaster for RenderWare and Microsoft
  6.  *            Windows.
  7.  *
  8.  *            This application had been written to be compatible with
  9.  *            both the fixed and floating-point versions of the
  10.  *            RenderWare library, i.e., it uses the macros CREAL,
  11.  *            INT2REAL, RAdd, RDiv, RSub etc. If your application is
  12.  *            intended for the floating-point version of the library
  13.  *            only these macros are not necessary.
  14.  *
  15.  *            Please note that this application is intended for
  16.  *            demonstration purposes only. No support will be
  17.  *            provided for this code and it comes with no warranty.
  18.  *
  19.  **********************************************************************
  20.  *
  21.  * Building RWROLLER.EXE...
  22.  *
  23.  **********************************************************************
  24.  *
  25.  * Watcom C/C++ 9.5 and 10.0 and RenderWare V1.4 static library
  26.  *
  27.  * This application can be built using the Watcom compiler and the
  28.  * fixed-point RenderWare V1.4 library as follows:
  29.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
  30.  *         /DRWFIXED /fo=rwroller.obj rwroller.c
  31.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
  32.  *         /DRWFIXED /fo=rolltype.obj rolltype.c
  33.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
  34.  *         /DRWFIXED /fo=rwpal.obj rwpal.c
  35.  * wlink option caseexact option stack=32768 name rwroller system win386
  36.  *         file rwroller.obj rolltype.obj rwpal.obj library
  37.  *         \rwwin\lib\rwwrxp.lib
  38.  * wbind rwroller -R rwroller,rc
  39.  *
  40.  * (Note for Watcom 10.0 substitue the command wcc386p with wcc386).
  41.  *
  42.  * This application can be built using the Watcom compiler and the
  43.  * floating-point RenderWare V1.4 library as follows:
  44.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
  45.  *         /DRWFLOAT /fo=rwroller.obj rwroller.c
  46.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
  47.  *         /DRWFLOAT /fo=rolltype.obj rolltype.c
  48.  * wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
  49.  *         /DRWFLOAT /fo=rwpal.obj rwpal.c
  50.  * wlink option caseexact option stack=32768 name rwroller system win386
  51.  *         file rwroller.obj rolltype.obj rwpal.obj library
  52.  *         \rwwin\lib\rwwrlp.lib
  53.  * wbind rwroller -R rwroller.rc
  54.  *
  55.  * (Note for Watcom 10.0 substitue the command wcc386p with wcc386).
  56.  *
  57.  **********************************************************************
  58.  *
  59.  * Microsoft Visual C++ 1.5 and RenderWare V1.4 DLL
  60.  *
  61.  * The fixed-point rwview.exe project for Visual C++ 1.5 must have
  62.  * the following options set:
  63.  *
  64.  * The project should contain the following files:
  65.  *     rwroller.c
  66.  *     rwroller.def
  67.  *     rwroller.rc
  68.  *     rolltype.c
  69.  *     rwpal.c
  70.  *
  71.  * Compiler Options:
  72.  *     Memory Model
  73.  *         Model:         Large
  74.  *     Preprocessos
  75.  *         Symbols:       RWFIXED
  76.  *         Include path:  \rwwin\include
  77.  *
  78.  * Linker Options:
  79.  *     Input
  80.  *         Libraries:      ..., \rwwin\lib\rwxv.lib
  81.  *     Windows libraries
  82.  *                        COMMDLG
  83.  *                        SHELL
  84.  *
  85.  * (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
  86.  * in your search path).
  87.  *
  88.  * The floating-point rwview.exe project for Visual C++ 1.5 must have
  89.  * the following options set:
  90.  *
  91.  * The project should contain the following files:
  92.  *     rwroller.c
  93.  *     rwroller.def
  94.  *     rwroller.rc
  95.  *     rolltype.c
  96.  *     rwpal.c
  97.  *
  98.  * Compiler Options:
  99.  *     Memory Model
  100.  *         Model:         Large
  101.  *     Preprocessos
  102.  *         Symbols:       RWFLOAT
  103.  *         Include path:  \rwwin\include
  104.  *
  105.  * Linker Options:
  106.  *     Input
  107.  *         Libraries:      ..., \rwwin\lib\rwlv.lib
  108.  *     Windows libraries
  109.  *                        COMMDLG
  110.  *                        SHELL
  111.  *
  112.  * (In addition the fixed point RenderWare V1.4 DLL rwl.dll must be
  113.  * in your search path).
  114.  *
  115.  **********************************************************************
  116.  *
  117.  * Borland C++ 4.0 and RenderWare V1.4 DLL
  118.  *
  119.  * The fixed-point rwroller.exe project for Borland C++ 4.0 must have
  120.  * the following options set:
  121.  *
  122.  * New Project:
  123.  *         Target Type    Application (.EXE)
  124.  *         Platform       Windows 3.x (16)
  125.  *         Target Model   Large
  126.  *     Standard Libraries
  127.  *         OWL            Unchecked
  128.  *         Class Library  Unchecked
  129.  *         Runtime        Checked
  130.  *         BWCC           Unchecked
  131.  *         Dynamic        Checked
  132.  *         Static         Unchecked
  133.  *     Advanced
  134.  *         .cpp Node      Unchecked
  135.  *         .c Node        Checked
  136.  *         No Source Node Unchecked
  137.  *         .rc node       Unchecked
  138.  *         .def node      Unchecked
  139.  *
  140.  * Project Options:
  141.  *     Directories
  142.  *         Include        ...; \rwwin\include
  143.  *     Compiler
  144.  *         Defines        ...; RWFIXED
  145.  *     16-Bit Compiler
  146.  *         Calling Conventions C
  147.  *         Memory Model   Large
  148.  *
  149.  * You must add the nodes rolltype.c, rwpal.c and \rwwin\lib\rwxb.lib
  150.  * to your project.
  151.  *
  152.  * (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
  153.  * in your search path).
  154.  *
  155.  * The floating-point rwroller.exe project for Borland C++ 4.0 must have
  156.  * the following options set:
  157.  *
  158.  * New Project:
  159.  *         Target Type    Application (.EXE)
  160.  *         Platform       Windows 3.x (16)
  161.  *         Target Model   Large
  162.  *     Standard Libraries
  163.  *         OWL            Unchecked
  164.  *         Class Library  Unchecked
  165.  *         Runtime        Checked
  166.  *         BWCC           Unchecked
  167.  *         Dynamic        Checked
  168.  *         Static         Unchecked
  169.  *     Advanced
  170.  *         .cpp Node      Unchecked
  171.  *         .c Node        Checked
  172.  *         No Source Node Unchecked
  173.  *         .rc node       Unchecked
  174.  *         .def node      Unchecked
  175.  *
  176.  * Project Options:
  177.  *     Directories
  178.  *         Include        ...; \rwwin\include
  179.  *     Compiler
  180.  *         Defines        ...; RWFLOAT
  181.  *     16-Bit Compiler
  182.  *         Calling Conventions C
  183.  *         Memory Model   Large
  184.  *
  185.  * You must add the nodes rolltype.c, rwpal.c and \rwwin\lib\rwxb.lib
  186.  * to your project.
  187.  *
  188.  * (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
  189.  * in your search path).
  190.  *
  191.  **********************************************************************
  192.  *
  193.  * This file is a product of Criterion Software Ltd.
  194.  * 
  195.  * This file is provided as is with no warranties of any kind and is 
  196.  * provided without any obligation on Criterion Software Ltd. or
  197.  * Canon Inc. to assist in its use or modification.
  198.  * 
  199.  * Criterion Software Ltd. will not, under any
  200.  * circumstances, be liable for any lost revenue or other damages arising
  201.  * from the use of this file.
  202.  *
  203.  * Copyright (c) 1994, 1995 Criterion Software Ltd.
  204.  * All Rights Reserved.
  205.  * 
  206.  * RenderWare is a trademark of Canon Inc.
  207.  *
  208.  **********************************************************************/
  209.  
  210. /**********************************************************************
  211.  *
  212.  * Header files.
  213.  *
  214.  **********************************************************************/
  215.                     
  216. #if defined(__WINDOWS_386__)
  217.  
  218. /*
  219.  * Watcom specific...
  220.  */
  221. #define INCLUDE_SHELLAPI_H
  222. #define INCLUDE_COMMDLG_H
  223. #include <windows.h>
  224.  
  225. #else
  226.  
  227. #include <windows.h>
  228. #include <shellapi.h>
  229. #include <commdlg.h>
  230.  
  231. #endif
  232.  
  233. #include <stdlib.h>
  234. #include <stdio.h>
  235. #include <math.h>
  236. #include <string.h>
  237. #if !defined(__WINDOWS_386__)
  238. #include <memory.h>
  239. #endif
  240.  
  241. #include <rwlib.h>
  242. #include <rwwin.h>
  243.  
  244. #include "resource.h"
  245. #include "rolltype.h"
  246. #include "pick.h"
  247. #include "common.h"
  248. #include "palette.h"
  249.  
  250. /**********************************************************************
  251.  *
  252.  * Application constants.
  253.  *
  254.  **********************************************************************/
  255.  
  256. /*
  257.  * Class name for the MS Window's window class.
  258.  */
  259. #define RWROLLERCLASSNAME       "RwRollerClass"
  260.  
  261. /*
  262.  * The default window title (and the title for error dialogs).
  263.  */
  264. #define WINDOWTITLE             "RenderWare Rollercoaster"
  265.  
  266. /*
  267.  * Name of the palette and backdrop files to load.
  268.  */
  269. #define PALETTEFILENAME         "rwroller.pal"
  270. #define BACKDROPFILENAME        "mount64.bmp"
  271.  
  272. /*
  273.  * Default size of the viewer's window.
  274.  */
  275. #define DEFAULT_WINDOW_WIDTH    320
  276. #define DEFAULT_WINDOW_HEIGHT   220
  277.  
  278. /*
  279.  * Maximum size of the viewer's window.
  280.  */
  281. #define MAXIMUM_WINDOW_WIDTH    640
  282. #define MAXIMUM_WINDOW_HEIGHT   480
  283.  
  284. /*
  285.  * Default distance of the camera from the origin.
  286.  */
  287. #define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
  288.  
  289. /**********************************************************************
  290.  *
  291.  * Forward functions.
  292.  *
  293.  **********************************************************************/
  294.  
  295. #ifndef   __WINDOWS_386__
  296. /*
  297.  * Stealth API function to allow the app to connect to the DLL even if
  298.  * it is already in use. This should only be used when the DLL thinks
  299.  * it is in use (but isn't) because a previous client has crashed.
  300.  */
  301. extern void
  302. _rwResetReferenceCount(void);
  303. #endif
  304.  
  305. LRESULT CALLBACK
  306. MainWndProc(HWND window, UINT mesage, WPARAM wParam, LPARAM lParam);
  307.  
  308. /**********************************************************************
  309.  *
  310.  * Type definitions.
  311.  *
  312.  **********************************************************************/
  313.  
  314. /*
  315.  * The camera's motion mode.
  316.  */
  317. typedef enum
  318. {
  319.     cmNONE,            /* No camera motion. */
  320.     cmRIDE,            /* Camera "rides" the rollercoaster. */
  321.     cmSPOTTER,        /* "Spotter" camera rotates around the rollercoaster car. */
  322.     cmTRAIL,        /* Camera trails the car on the rollercoaster. */
  323.     cmLEAD,            /* Camera "leads" the car on the rollercoaster. */
  324.     cmELEVATION,    /* Camera pans around and zooms into the rollercoaster elevation. */
  325.     cmPLAN            /* Camera pans around and zooms into the rollercoaster plan. */
  326. } CMMode;
  327.  
  328. /*
  329.  * This enumerated type tells us what kind of action should be taken
  330.  * on mouse events.
  331.  */
  332. typedef enum
  333. {
  334.     mmNONE,             /* No mouse move action. */
  335.     mmMOVECAMERA,     /* Move the camera on mouse move. */
  336.     mmALTMOVECAMERA, /* Alternative camera motion on mouse move. */
  337.     mmMOVECONTROL,     /* Move a spline control point on mouse move. */
  338.     mmMOVELIGHT         /* Move the light. */
  339. } MMMode;
  340.  
  341. /*
  342.  * The direction of motion of the viewer on the track.
  343.  */
  344. typedef enum
  345. {
  346.     vmLEVEL,
  347.     vmCLIMBING,
  348.     vmFALLING    
  349. } VMMode;
  350.  
  351. /**********************************************************************
  352.  *
  353.  * Application global variables.
  354.  *
  355.  **********************************************************************/
  356.  
  357. /*
  358.  * Instance handle for this application.
  359.  */
  360. static HANDLE      AppInstance;
  361.  
  362. /*
  363.  * Drive from which the application was launched.
  364.  */
  365. static char        AppPath[_MAX_PATH];
  366.  
  367. /*
  368.  * The global, single camera instance.
  369.  */
  370. static RwCamera   *Camera = NULL;
  371.  
  372. static CMMode      CameraMode = cmRIDE;
  373.  
  374. static RwReal      CameraAngle = CREAL(0.0);
  375.  
  376. /*
  377.  * This variable tells us what kind of action to take on a mouse move.
  378.  * The action depends on the object that was picked when the mouse button
  379.  * went down, and on the selection of virtual keys that were depressed at
  380.  * that time. By default no action is taken on mouse move.
  381.  */
  382. static MMMode      MouseMoveMode = mmNONE;
  383.  
  384. /*
  385.  * Global variables used to remember the last mouse X and Y coordinates
  386.  * when involved in a  pan, zoom, drag or spin.
  387.  */
  388. static int         LastX;
  389. static int         LastY;
  390.  
  391. /*
  392.  * Current distance the camera is from the origin. This is stored to
  393.  * help us pan and zoom the camera.
  394.  */
  395. static RwReal      CameraDistance = DEFAULT_CAMERA_DISTANCE;
  396.  
  397. /*
  398.  * Current angle of tilt applied to the camera.
  399.  */
  400. static RwReal      CameraTilt = CREAL(0.0);
  401.  
  402. /*
  403.  * This flag indicates whether the 3D components of the application
  404.  * have been successfully initialized as yet. It is used to guard
  405.  * the message loop handler functions from being invoked before the
  406.  * 3D components of the application are successfully initialized.
  407.  */
  408. static BOOL        ThreeDInitialized = FALSE;
  409.  
  410. /*
  411.  * The current rollercoaster.
  412.  */
  413. static RollerCoasterType *CurrentCoaster = NULL;
  414.  
  415. /*
  416.  * Default coaster filename table.
  417.  */
  418. static char DefaultCoasterFilenames[4][_MAX_PATH] =
  419. {
  420.     "track1.rrc",
  421.     "track2.rrc",
  422.     "track3.rrc",
  423.     "track4.rrc"
  424. };
  425.  
  426. /*
  427.  * Filename used for communication with the Choose dialog.
  428.  */
  429. static char CoasterFilename[_MAX_PATH];
  430.  
  431. /*
  432.  * The current control point picked (if any).
  433.  */
  434. static int ControlPoint  = 0;
  435.  
  436. /*
  437.  * Whether to use WinG or not. By default we do (unless we are running
  438.  * under Windows95 or Windows NT 3.5 where we ignore WinG and use
  439.  * DIB Sections which are much nicer).
  440.  */
  441. static BOOL UseWinG = TRUE;
  442.  
  443. /*
  444.  * Can we stretch bitmaps efficiently.
  445.  */
  446. static BOOL CanStretch = FALSE;
  447.  
  448. /*
  449.  * If we can stretch, are we stretching.
  450.  */
  451. static BOOL DoStretch = FALSE;
  452.  
  453. /*
  454.  * Whether to show the coaster's car or not.
  455.  */
  456. static BOOL ShowCar = TRUE;
  457.  
  458. /**********************************************************************
  459.  *
  460.  * Functions.
  461.  *
  462.  **********************************************************************/
  463.  
  464. /**********************************************************************/
  465.  
  466. static BOOL
  467. TrackBackdropToCamera(RwCamera *camera)
  468. {                        
  469.     RwRaster *backdrop;
  470.     RwInt32   backdropWidth;
  471.     RwInt32   backdropHeight;
  472.     RwV3d     at;
  473.     RwReal    angle;
  474.     RwInt32   xOffset;
  475.     RwInt32   yOffset;
  476.     RwInt32   windowWidth;
  477.     RwInt32   windowHeight;
  478.  
  479.     backdrop = RwGetCameraBackdrop(camera);
  480.     
  481.     /*
  482.      * No-op if the camera has no backdrop.
  483.      */
  484.     if (backdrop != NULL)
  485.     {
  486.         /*
  487.          * Get the window and backdrop dimensions.
  488.          */            
  489.         RwGetCameraViewport(camera, NULL, NULL, &windowWidth, &windowHeight);
  490.         
  491.         backdropWidth  = RwGetRasterWidth(backdrop);
  492.         backdropHeight = RwGetRasterHeight(backdrop);
  493.         
  494.         /*
  495.          * We use the look at vector for determining both
  496.          * horizontal and vertical positioning.
  497.          */
  498.         RwGetCameraLookAt(camera, &at);
  499.         
  500.         /*
  501.          * Compute the horizontal position. This is done
  502.          * by compute the camera's angle of rotate about
  503.          * the Z axis. The angle is then directly converted
  504.          * into a horizontal backdrop offset.
  505.          */
  506.  
  507.         /*
  508.          * Compute the angle in the range 0 => PI.
  509.          */
  510.         at.y = CREAL(0.0);
  511.         RwNormalize(&at);
  512.         if (at.z > CREAL(1.0))
  513.             at.z = CREAL(1.0);
  514.         else if (at.z < CREAL(-1.0))
  515.             at.z = CREAL(-1.0);
  516.         angle = FL2REAL(acos(REAL2FL(at.z)));
  517.         
  518.         /*
  519.          * Get the angle in the range 0 => 2 PI
  520.          */
  521.         if (at.x < CREAL(0.0))
  522.             angle = RSub(CREAL(M_2PI), angle);
  523.             
  524.         /*
  525.          * The backdrop X offset is derived simply from
  526.          * the angle computed above.
  527.          */
  528.         xOffset = -REAL2INT(RDiv(RMul(angle, INT2REAL(backdropWidth)), CREAL(M_2PI)));
  529.         
  530.         /*
  531.          * Compute the vertical position. This is done by getting
  532.          * the angle between the look at vector and the Y axis. We
  533.          * simply use the dot product (cosine of the angle) multiplied
  534.          * by a scale factor to compute the vertical offset.
  535.          */
  536.         RwGetCameraLookAt(Camera, &at);
  537.         yOffset = REAL2INT(RAdd(INT2REAL(windowHeight / 2), RMul(RMul(at.y, CREAL(1.5)), INT2REAL(windowHeight / 2)))) - (backdropHeight / 2);
  538.                        
  539.         RwSetCameraBackdropOffset(Camera, xOffset, 0);
  540.         RwSetCameraBackdropViewportRect(Camera, 0, yOffset, windowWidth, backdropHeight);
  541.     }
  542.     return TRUE;    
  543. }
  544.  
  545. /**********************************************************************/
  546.  
  547. /*
  548.  * Update the application menu to reflect the current application
  549.  * state.
  550.  */
  551. static void
  552. UpdateMenu(HWND window)
  553. {
  554.     HMENU menu;
  555.     
  556.     menu = GetMenu(window);
  557.     
  558.     CheckMenuItem(menu, IDM_VIEWPOINT_RIDE,
  559.         MF_BYCOMMAND | (CameraMode == cmRIDE ? MF_CHECKED : MF_UNCHECKED));
  560.     CheckMenuItem(menu, IDM_VIEWPOINT_SPOTTER,
  561.         MF_BYCOMMAND | (CameraMode == cmSPOTTER ? MF_CHECKED : MF_UNCHECKED));
  562.     CheckMenuItem(menu, IDM_VIEWPOINT_TRAIL,
  563.         MF_BYCOMMAND | (CameraMode == cmTRAIL ? MF_CHECKED : MF_UNCHECKED));
  564.     CheckMenuItem(menu, IDM_VIEWPOINT_LEAD,
  565.         MF_BYCOMMAND | (CameraMode == cmLEAD ? MF_CHECKED : MF_UNCHECKED));
  566.     CheckMenuItem(menu, IDM_EDIT_SIDEVIEW,
  567.         MF_BYCOMMAND | (CameraMode == cmELEVATION ? MF_CHECKED : MF_UNCHECKED));
  568.     CheckMenuItem(menu, IDM_EDIT_TOPVIEW,
  569.         MF_BYCOMMAND | (CameraMode == cmPLAN ? MF_CHECKED : MF_UNCHECKED));
  570.               
  571.     /*
  572.      * Can we stretch output (are we using WinG or DIB Sections)?
  573.      */
  574.     EnableMenuItem(menu, IDM_OPTION_STRETCH,
  575.         MF_BYCOMMAND | (CanStretch ? MF_ENABLED : MF_GRAYED));
  576.     
  577.     /*
  578.      * Are we stretching?
  579.      */
  580.     CheckMenuItem(menu, IDM_OPTION_STRETCH,
  581.         MF_BYCOMMAND | (DoStretch ? MF_CHECKED : MF_UNCHECKED));
  582.  
  583.     CheckMenuItem(menu, IDM_OPTION_SHOWCAR,
  584.         MF_BYCOMMAND | (ShowCar ? MF_CHECKED : MF_UNCHECKED));
  585. }
  586.  
  587. /**********************************************************************/
  588.  
  589. /*
  590.  * Put camera into ride mode.
  591.  */
  592. static void
  593. SwitchToRideView(HWND window)
  594. {
  595.     CameraMode = cmRIDE;
  596.     SwitchToCoasterRideMode(CurrentCoaster);
  597.     UpdateMenu(window);
  598. }
  599.  
  600. /**********************************************************************/
  601.  
  602. /*
  603.  * Put camera into ride mode.
  604.  */
  605. static void
  606. SwitchToSpotterView(HWND window)
  607. {
  608.     CameraMode = cmSPOTTER;
  609.     SwitchToCoasterRideMode(CurrentCoaster);
  610.     UpdateMenu(window);
  611. }
  612.  
  613. /**********************************************************************/
  614.  
  615. /*
  616.  * Put camera into trail mode.
  617.  */
  618. static void
  619. SwitchToTrailView(HWND window)
  620. {
  621.     CameraMode = cmTRAIL;
  622.     SwitchToCoasterRideMode(CurrentCoaster);
  623.     UpdateMenu(window);
  624. }
  625.  
  626. /**********************************************************************/
  627.  
  628. /*
  629.  * Put camera into lead mode.
  630.  */
  631. static void
  632. SwitchToLeadView(HWND window)
  633. {
  634.     CameraMode = cmLEAD;
  635.     SwitchToCoasterRideMode(CurrentCoaster);
  636.     UpdateMenu(window);
  637. }
  638.  
  639. /**********************************************************************/
  640.  
  641. /*
  642.  * Put camera into elevation mode.
  643.  */
  644. static void
  645. SwitchToElevationView(HWND window)
  646. {
  647.     CameraMode = cmELEVATION;
  648.     SwitchToCoasterEditMode(CurrentCoaster);
  649.  
  650.     RwSetCameraPosition(Camera,
  651.                         CurrentCoaster->elevationPosition.x,
  652.                         CurrentCoaster->elevationPosition.y,
  653.                         CurrentCoaster->elevationPosition.z);
  654.     RwPointCamera(Camera,
  655.                   CREAL(0.0),
  656.                   CurrentCoaster->elevationPosition.y,
  657.                   CREAL(0.0));
  658.     RwSetCameraLookUp(Camera,
  659.                       CREAL(0.0),
  660.                       CREAL(1.0),
  661.                       CREAL(0.0));
  662.     TrackBackdropToCamera(Camera);                      
  663.     UpdateMenu(window);
  664. }
  665.  
  666. /**********************************************************************/
  667.  
  668. /*
  669.  * Put the camera into plan mode.
  670.  */
  671. static void
  672. SwitchToPlanView(HWND window)
  673. {
  674.     CameraMode = cmPLAN;
  675.     SwitchToCoasterEditMode(CurrentCoaster);
  676.  
  677.     RwSetCameraPosition(Camera,
  678.                         CurrentCoaster->planPosition.x,
  679.                         CurrentCoaster->planPosition.y,
  680.                         CurrentCoaster->planPosition.z);
  681.  
  682.     /*
  683.      * Unsatisfactory hack to get the camera aligned in the
  684.      * way we wish. This itself will fail if the camera's is
  685.      * looking down the X-axis already - sigh!
  686.      */
  687.     RwSetCameraLookUp(Camera,
  688.                       CREAL(1.0),
  689.                       CREAL(0.0),
  690.                       CREAL(0.0));
  691.     RwPointCamera(Camera,
  692.                   CurrentCoaster->planPosition.x,
  693.                   CREAL(0.0),
  694.                   CurrentCoaster->planPosition.z);
  695.     RwSetCameraLookUp(Camera,
  696.                       CREAL(0.0),
  697.                       CREAL(0.0),
  698.                       CREAL(1.0));
  699.                       
  700.     UpdateMenu(window);
  701. }
  702.  
  703. /**********************************************************************/
  704.  
  705. static void
  706. SwitchCoaster(HWND window, RollerCoasterType *coaster)
  707. {
  708.     DestroyRollerCoaster(CurrentCoaster);
  709.     CurrentCoaster = coaster;
  710.     switch (CameraMode)
  711.     {
  712.         case cmRIDE:
  713.             SwitchToRideView(window);
  714.             break;
  715.         case cmSPOTTER:
  716.             SwitchToSpotterView(window);
  717.             break;
  718.         case cmTRAIL:
  719.             SwitchToTrailView(window);
  720.             break;
  721.         case cmLEAD:
  722.             SwitchToLeadView(window);
  723.             break;
  724.         case cmELEVATION:
  725.             SwitchToElevationView(window);
  726.             break;
  727.         case cmPLAN:
  728.             SwitchToPlanView(window);
  729.             break;
  730.     }
  731.     if (ShowCar)
  732.         EnableCoasterCar(CurrentCoaster);
  733.     else
  734.         DisableCoasterCar(CurrentCoaster);
  735.     SetWindowText(window, CurrentCoaster->description);
  736. }
  737.  
  738. /**********************************************************************/
  739.  
  740. /*
  741.  * Dialog procedure for the rollercoaster about dialog.
  742.  */
  743. BOOL CALLBACK
  744. CoasterAboutDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
  745. {
  746.     DRAWITEMSTRUCT FAR *drawItemStruct;    
  747.     RECT FAR           *rect;
  748.     HDC                 dc;
  749.     HDC                 bitmapDC;
  750.     HBITMAP             oldBitmap;
  751.     BITMAP              bitmapInfo;
  752.     int                 x;
  753.     int                 y;
  754.     int                 width;
  755.     int                 height;
  756.     
  757.     static HBITMAP      renderWareLogo = NULL;
  758.     
  759.     switch (message)
  760.     {
  761.         case WM_INITDIALOG:
  762.             renderWareLogo = LoadBitmap(AppInstance, MAKEINTRESOURCE(IDB_RWLOGO));
  763.             return TRUE;
  764.             
  765.         case WM_DRAWITEM:
  766. #if defined(__WINDOWS_386__)
  767.             drawItemStruct = (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam); 
  768. #else
  769.             drawItemStruct = (DRAWITEMSTRUCT *)lParam; 
  770. #endif
  771.             rect = &drawItemStruct->rcItem;
  772.             dc   = drawItemStruct->hDC;
  773.                 
  774.             switch (drawItemStruct->CtlID)
  775.             {
  776.                 case IDC_RENDERWARELOGO:
  777.                     GetObject(renderWareLogo, sizeof(BITMAP), &bitmapInfo);
  778.                     width  = bitmapInfo.bmWidth;
  779.                     height = bitmapInfo.bmHeight;
  780.                     x      = rect->left + (((rect->right  - rect->left) - width) / 2);
  781.                     y      = rect->top  + (((rect->bottom - rect->top)  - height) / 2);
  782.                     bitmapDC = CreateCompatibleDC(dc);
  783.                     oldBitmap = SelectObject(bitmapDC, renderWareLogo);
  784.                     BitBlt(dc, x, y, width, height, bitmapDC, 0, 0, SRCCOPY);
  785.                     SelectObject(bitmapDC, oldBitmap);
  786.                     DeleteDC(bitmapDC);  
  787.                     break;
  788.             }
  789.             return TRUE;
  790.             
  791.         case WM_COMMAND:
  792.             switch (wParam)
  793.             {
  794.                 case IDOK:
  795.                     DeleteObject(renderWareLogo);
  796.                     EndDialog(dialog, IDOK);
  797.                     return TRUE;                    
  798.             }
  799.     }
  800.     return FALSE;
  801. }
  802.  
  803. /**********************************************************************/
  804.  
  805. /*
  806.  * Dialog procedure for the rollercoaster menu dialog.
  807.  */
  808. BOOL CALLBACK
  809. CoasterMenuDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
  810. {
  811.     DRAWITEMSTRUCT FAR       *drawItemStruct;    
  812.     RECT FAR                 *rect;
  813.     HDC                       dc;
  814.     HPEN                      topLeftPen;
  815.     HPEN                      bottomRightPen;
  816.     HPEN                      pen;
  817.     HPEN                      oldPen;
  818.     int                       i;
  819.     RollerCoasterType        *coaster;
  820.     OPENFILENAME              openFileName;
  821.     char                      filename[_MAX_PATH];
  822.     static int                numCoasters;
  823.     static RollerCoasterType *coasters[4];
  824.     static RwCamera          *camera = NULL;
  825.     
  826.     switch (message)
  827.     {
  828.         case WM_INITDIALOG:
  829.             /*
  830.              * Create the camera used in this dialog.
  831.              */
  832.             camera = RwCreateCamera(200, 200, NULL);
  833.             if (camera == NULL)
  834.                 return FALSE;
  835.             RwSetCameraBackColor(camera, CREAL(0.0), CREAL(0.0), CREAL(1.0));
  836.             RwSetCameraViewwindow(camera, CREAL(0.4), CREAL(0.4));
  837.             RwTiltCamera(camera, CREAL(30.0));
  838.             RwVCMoveCamera(camera, CREAL(0.0), CREAL(0.0), CREAL(-3.0));
  839.             
  840.             /*
  841.              * Attempt to load the four default rollercoasters.
  842.              */
  843.             numCoasters = 0;
  844.             for (i = 0; i < 4; i++)
  845.             {
  846.                 strcpy(filename, AppPath);
  847.                 strcat(filename, DefaultCoasterFilenames[i]);
  848.                 coasters[numCoasters] = ReadRollerCoaster(filename);
  849.                 if (coasters[numCoasters] != NULL)
  850.                 {
  851.                     DisableCoasterControlPoints(coasters[numCoasters]);
  852.                     DisableCoasterCar(coasters[numCoasters]);
  853.                     numCoasters++;
  854.                 }
  855.             }
  856.                 
  857.             for (i = 0; i < numCoasters; i++)
  858.                 SetDlgItemText(dialog, IDC_LABEL_ONE + i, coasters[i]->description);
  859.             
  860.             SetTimer(dialog, 2, 20, NULL);
  861.             
  862.             return TRUE;
  863.             
  864.         case WM_DRAWITEM:
  865. #if defined(__WINDOWS_386__)
  866.             drawItemStruct = (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam); 
  867. #else
  868.             drawItemStruct = (DRAWITEMSTRUCT *)lParam; 
  869. #endif
  870.             if (camera != NULL)
  871.             {
  872.                 rect = &drawItemStruct->rcItem;
  873.                 dc   = drawItemStruct->hDC;
  874.                 
  875.                 pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
  876.                 oldPen = SelectObject(dc, pen);
  877.                 MoveToEx(dc, rect->left, rect->top, NULL);
  878.                 LineTo(dc, rect->right - 1, rect->top);
  879.                 LineTo(dc, rect->right - 1, rect->bottom - 1);
  880.                 LineTo(dc, rect->left, rect->bottom - 1);
  881.                 LineTo(dc, rect->left, rect->top);
  882.                 SelectObject(dc, oldPen);
  883.                 DeleteObject(pen);
  884.  
  885.                 if (drawItemStruct->itemState & ODS_SELECTED)
  886.                 {
  887.                     topLeftPen     = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  888.                     bottomRightPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  889.                 }
  890.                 else
  891.                 {
  892.                     topLeftPen     = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  893.                     bottomRightPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  894.                 }
  895.                 
  896.                 oldPen = SelectObject(dc, topLeftPen);
  897.  
  898.                 MoveToEx(dc, rect->left + 1,  rect->top + 1, NULL);
  899.                 LineTo(dc, rect->right - 2, rect->top + 1);
  900.                 MoveToEx(dc, rect->left + 1,  rect->top + 2, NULL);
  901.                 LineTo(dc, rect->right - 3, rect->top + 2);
  902.                 MoveToEx(dc, rect->left + 1,  rect->top + 3, NULL);
  903.                 LineTo(dc, rect->left + 1,  rect->bottom - 2);
  904.                 MoveToEx(dc, rect->left + 2,  rect->top + 3, NULL);
  905.                 LineTo(dc, rect->left + 2,  rect->bottom - 3);
  906.                 
  907.                 SelectObject(dc, oldPen);
  908.                 
  909.                 oldPen = SelectObject(dc, bottomRightPen);
  910.  
  911.                 MoveToEx(dc, rect->right - 2, rect->bottom - 2, NULL);
  912.                 LineTo(dc, rect->left + 1,  rect->bottom - 2);
  913.                 MoveToEx(dc, rect->right - 2, rect->bottom - 3, NULL);
  914.                 LineTo(dc, rect->left + 2,  rect->bottom - 3);
  915.                 MoveToEx(dc, rect->right - 2, rect->bottom - 4, NULL);
  916.                 LineTo(dc, rect->right - 2, rect->top + 2);
  917.                 MoveToEx(dc, rect->right - 3, rect->bottom - 4, NULL);
  918.                 LineTo(dc, rect->right - 3, rect->top + 3);
  919.                 
  920.                 SelectObject(dc, oldPen);
  921.  
  922.                 DeleteObject(topLeftPen);
  923.                 DeleteObject(bottomRightPen);
  924.  
  925.                 coaster = coasters[drawItemStruct->CtlID - IDC_ROLLER_ONE];
  926.                 if (coaster != NULL)
  927.                 {
  928.                     RwSetCameraViewport(camera,
  929.                                         rect->left + 3,
  930.                                         rect->top + 3,
  931.                                         (rect->right  - rect->left) - 6,
  932.                                         (rect->bottom - rect->top)  - 6);
  933.                     RwInvalidateCameraViewport(camera);
  934.                     RwBeginCameraUpdate(camera, (void *)(DWORD)dialog);
  935.                         RwClearCameraViewport(camera);
  936.                         RwRenderScene(coaster->scene);
  937.                     RwEndCameraUpdate(camera);
  938.                     RwShowCameraImage(camera, (void *)(DWORD)dc);
  939.                 }
  940.             }
  941.             return TRUE;
  942.             
  943.         case WM_TIMER:
  944.             if (camera != NULL)
  945.             {
  946.                 RwPushScratchMatrix();
  947.                     RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0), CREAL(10.0), rwREPLACE);
  948.                     RwTransformCamera(camera, RwScratchMatrix(), rwPOSTCONCAT);
  949.                 RwPopScratchMatrix();
  950.                 InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_ONE), NULL, FALSE);
  951.                 InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_TWO), NULL, FALSE);
  952.                 InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_THREE), NULL, FALSE);
  953.                 InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_FOUR), NULL, FALSE);
  954.             }
  955.             return TRUE;
  956.             
  957.         case WM_COMMAND:
  958.             switch (wParam)
  959.             {
  960.                 case IDC_ROLLER_ONE:
  961.                 case IDC_ROLLER_TWO:
  962.                 case IDC_ROLLER_THREE:
  963.                 case IDC_ROLLER_FOUR:
  964.                     strcpy(CoasterFilename, DefaultCoasterFilenames[wParam - IDC_ROLLER_ONE]);
  965.                     KillTimer(dialog, 2);
  966.                     for (i = 0; i < numCoasters; i++)
  967.                         DestroyRollerCoaster(coasters[i]);
  968.                     if (camera != NULL)
  969.                     {
  970.                         RwDestroyCamera(camera);
  971.                         camera = NULL;
  972.                     }
  973.                     EndDialog(dialog, IDOK);
  974.                     return TRUE;
  975.                     
  976.                 case IDC_ROLLER_MORE:
  977.                     KillTimer(dialog, 2);
  978.                     filename[0] = '\0';
  979.                     memset(&openFileName, '\0', sizeof(OPENFILENAME));
  980.                     openFileName.lStructSize  = sizeof(OPENFILENAME);
  981.                     openFileName.hwndOwner    = dialog;
  982.                     openFileName.lpstrFilter  = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
  983.                     openFileName.nFilterIndex = 1;
  984.                     openFileName.lpstrFile    = &filename[0];
  985.                     openFileName.nMaxFile     = sizeof(filename);
  986.                     openFileName.Flags        = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  987.             
  988.                     if (GetOpenFileName(&openFileName))
  989.                     {
  990.                         strcpy(CoasterFilename, filename);
  991.                         KillTimer(dialog, 2);
  992.                         for (i = 0; i < numCoasters; i++)
  993.                             DestroyRollerCoaster(coasters[i]);
  994.                         if (camera != NULL)
  995.                         {
  996.                             RwDestroyCamera(camera);
  997.                             camera = NULL;
  998.                         }
  999.                         EndDialog(dialog, IDOK);
  1000.                     }
  1001.                     else
  1002.                     {
  1003.                         SetTimer(dialog, 2, 20, NULL);
  1004.                     }
  1005.             
  1006.                     return TRUE;
  1007.                     
  1008.                 case IDCANCEL:
  1009.                     KillTimer(dialog, 2);
  1010.                     for (i = 0; i < numCoasters; i++)
  1011.                         DestroyRollerCoaster(coasters[i]);
  1012.                     if (camera != NULL)
  1013.                     {
  1014.                         RwDestroyCamera(camera);
  1015.                         camera = NULL;
  1016.                     }
  1017.                     EndDialog(dialog, IDCANCEL);
  1018.                     return TRUE;                    
  1019.             }
  1020.     }
  1021.     
  1022.     return FALSE;
  1023. }
  1024.  
  1025. /**********************************************************************/
  1026.  
  1027. /*
  1028.  * Dialog procedure for the rollercoaster menu dialog.
  1029.  */
  1030. BOOL CALLBACK
  1031. CoasterDescriptionDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
  1032. {
  1033.     switch (message)
  1034.     {
  1035.         case WM_INITDIALOG:
  1036.             SetDlgItemText(dialog, IDC_COASTER_DESCRIPTION, CurrentCoaster->description);
  1037.             SetFocus(GetDlgItem(dialog, IDC_COASTER_DESCRIPTION));
  1038.             return TRUE;
  1039.             
  1040.         case WM_COMMAND:
  1041.             switch (wParam)
  1042.             {
  1043.                 case IDOK:
  1044.                     GetDlgItemText(dialog, IDC_COASTER_DESCRIPTION,
  1045.                                    CurrentCoaster->description,
  1046.                                    sizeof(CurrentCoaster->description));
  1047.                     EndDialog(dialog, IDOK);
  1048.                     return TRUE;
  1049.                 case IDCANCEL:
  1050.                     EndDialog(dialog, IDCANCEL);
  1051.                     return TRUE; 
  1052.             }
  1053.     }
  1054.     
  1055.     return FALSE;
  1056. }
  1057.  
  1058. /**********************************************************************/
  1059.  
  1060. /*
  1061.  * Perform any necessary MS Windows application initialization. Basically,
  1062.  * this means registering the window class for this application.
  1063.  */
  1064. static BOOL
  1065. InitApplication(HANDLE instance)
  1066. {
  1067.     WNDCLASS windowClass;
  1068.  
  1069.     windowClass.style         = CS_BYTEALIGNWINDOW;
  1070.     windowClass.lpfnWndProc   = (WNDPROC)MainWndProc;
  1071.     windowClass.cbClsExtra    = 0; 
  1072.     windowClass.cbWndExtra    = 0; 
  1073.     windowClass.hInstance     = instance; 
  1074.     windowClass.hIcon         = NULL;
  1075.     windowClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  1076.     windowClass.hbrBackground = NULL; 
  1077.     windowClass.lpszMenuName  = MAKEINTRESOURCE(IDR_APPMENU); 
  1078.     windowClass.lpszClassName = RWROLLERCLASSNAME;
  1079.  
  1080.     return RegisterClass(&windowClass);
  1081. }
  1082.  
  1083. /**********************************************************************/
  1084.  
  1085. /*
  1086.  * Perform any necessary initialization for this instance of the
  1087.  * application. This simply means creating the application's main
  1088.  * window.
  1089.  */
  1090. static HWND
  1091. InitInstance(HANDLE instance)
  1092. {
  1093.     /*
  1094.      * Create the MS Window's window instance for this application. The
  1095.      * initial window size is given by DEFAULT_WINDOW_WIDTH and
  1096.      * DEFAULT_WINDOW_HEIGHT. The window is not given a title as we
  1097.      * set it during Init3D() with information about the version of
  1098.      * RenderWare being used.
  1099.      */
  1100.     return CreateWindow(RWROLLERCLASSNAME, "",
  1101.                         WS_OVERLAPPEDWINDOW,
  1102.                         CW_USEDEFAULT, CW_USEDEFAULT,
  1103.                         DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT,
  1104.                         NULL, NULL, instance, NULL);
  1105. }
  1106.  
  1107. /**********************************************************************/
  1108.  
  1109. /*
  1110.  * This function initializes the 3D (i.e. RenderWare) components of the
  1111.  * application. This function opens the RenderWare library, creates a
  1112.  * camera, a scene, a light and a matrix for spinning.
  1113.  */
  1114. static BOOL
  1115. Init3D(HWND window)
  1116. {
  1117.     char            buffer[_MAX_PATH];
  1118.     RwRaster       *backdrop;
  1119.     RwOpenArgument  arg;
  1120.     RwInt32         numOptions;
  1121.  
  1122.     /*
  1123.      * Warn the user that the display is not in one of RenderWare's native
  1124.      * rendering depths and recommend that (for best performance) they
  1125.      * switch to an 8- or 16-bit mode.
  1126.      */
  1127.     CheckDisplayDepth(window);
  1128.  
  1129. #if defined(WIN32)
  1130.     /*
  1131.      * If we are building a Win32 executable then we will not explicitly
  1132.      * select WinG as DIB Sections are likely to ba available and DIB
  1133.      * Sections are much more super than WinG.
  1134.      */
  1135.     numOptions = 0L;
  1136. #else
  1137.     /*
  1138.      * If we are running on Windows 3.1 then we will select WinG (so we can
  1139.      * have stretching) unless the program has been launched with the -g
  1140.      * option to disable WinG.
  1141.      */
  1142.     if (UseWinG)
  1143.     {
  1144.         arg.option = rwWINUSEWING;
  1145.         numOptions = 1L;
  1146.     }
  1147.     else
  1148.     {
  1149.         numOptions = 0L;
  1150.     }
  1151. #endif
  1152.     if (!RwOpenExt("MSWindows", NULL, numOptions, &arg))
  1153.     {
  1154.         MessageBox(window, "Could not open the RenderWare library",
  1155.                    WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  1156.         return FALSE;
  1157.     }
  1158.     
  1159.     /*
  1160.      * By requesting that the library use WinG (or DIB Sections) we
  1161.      * wanted to enable bitmap stretching. However, we don't always
  1162.      * get what we want (for example if WinG is not installed) so we
  1163.      * must verify that we can indeed stretch efificiently before
  1164.      * enabling stretching. This function checks this.
  1165.      */
  1166.     CanStretch = AllowStretching(window);
  1167.  
  1168.     /*
  1169.      * Set up the shape path to get scripts and textures from local
  1170.      * sub-directories or from the directory from which the application
  1171.      * was run.
  1172.      */
  1173.     strcpy(buffer, AppPath);
  1174.     strcat(buffer, "SCRIPTS");
  1175.     RwSetShapePath(buffer, rwPRECONCAT);
  1176.     strcpy(buffer, AppPath);
  1177.     strcat(buffer, "TEXTURES");
  1178.     RwSetShapePath(buffer, rwPRECONCAT);
  1179.     RwSetShapePath(AppPath, rwPRECONCAT);
  1180.  
  1181.     /*
  1182.      * Install the application's optimized palette.
  1183.      */
  1184.     strcpy(buffer, AppPath);
  1185.     strcat(buffer, PALETTEFILENAME);
  1186.     CheckAndReadPalette(buffer);
  1187.  
  1188.     /*
  1189.      * Create the camera which will be used for rendering. The initial window
  1190.      * size the application will create is given by DEFAULT_WINDOW_WIDTH and
  1191.      * DEFAULT_WINDOW_HEIGHT which is currently 400x400. However, the camera
  1192.      * will be created with a maximum viewport size which is the same size
  1193.      * as the maximum window size (specified by MAXIMUM_WINDOW_WIDTH and
  1194.      * MAXIMUM_WINDOW_HEIGHT). Thus, the camera's viewport can grow as large
  1195.      * as the window.
  1196.      */
  1197.     Camera = RwCreateCamera(MAXIMUM_WINDOW_WIDTH, MAXIMUM_WINDOW_HEIGHT, NULL);
  1198.     if (!Camera)
  1199.     {
  1200.         /*
  1201.          * As with RwOpen(), the most common cause for a failure to create
  1202.          * a camera is insufficient memory so we will explicitly check for
  1203.          * this condition and report it. Otherwise a general error is issued.
  1204.          */
  1205.         if (RwGetError() == E_RW_NOMEM)
  1206.         {
  1207.             MessageBox(window,
  1208.                        "Insufficient memory to create the RenderWare camera",
  1209.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  1210.         }
  1211.         else
  1212.         {
  1213.             MessageBox(window, "Could not create the RenderWare camera",
  1214.                        WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  1215.         }
  1216.         RwClose();
  1217.  
  1218.         return FALSE;
  1219.     }
  1220.  
  1221.     /*
  1222.      * Set the camera's background color to blue.
  1223.      */
  1224.     RwSetCameraBackColor(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.67));
  1225.  
  1226.     /*
  1227.      * By default, the camera lies on the X-Z plane and points down Z
  1228.      * into the screen. We shall retain the camera's orientation, but move
  1229.      * the camera DEFAULT_CAMERA_DISTANCE units down Z away from the screen.
  1230.      */
  1231.     RwTiltCamera(Camera, CameraTilt);
  1232.     RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  1233.     RwSetCameraViewwindow(Camera, CREAL(0.8), CREAL(0.8));
  1234.  
  1235.     /*
  1236.      * Load and set the backdrop backdrop (we will consider the lack of a
  1237.      * backdrop to be non-fatal).
  1238.      */
  1239.     backdrop = RwReadRaster(BACKDROPFILENAME, rwGAMMARASTER | rwDITHERRASTER);
  1240.     if (backdrop != NULL)
  1241.         RwSetCameraBackdrop(Camera, backdrop);
  1242.     
  1243.     /*
  1244.      * Attempt to load the fourth default rollercoaster by default (Puke
  1245.      * Mountain). If we can't then we will just create a new rollercoaster
  1246.      * instead.
  1247.      */
  1248.     strcpy(buffer, AppPath);
  1249.     strcat(buffer, DefaultCoasterFilenames[3]);
  1250.     CurrentCoaster = ReadRollerCoaster(buffer);
  1251.     if (CurrentCoaster == NULL)
  1252.     {
  1253.         /*
  1254.          * Could not read the default coaster so create a new
  1255.          * coaster instead.
  1256.          */
  1257.         CurrentCoaster = CreateRollerCoaster();
  1258.     }
  1259.     
  1260.     /*
  1261.      * If we can't read the default coaster or create a new
  1262.      * one then we must give up and exit.
  1263.      */
  1264.     if (CurrentCoaster == NULL)
  1265.     {
  1266.         RwDestroyCamera(Camera);
  1267.         RwClose();
  1268.         return FALSE;
  1269.     }
  1270.     SwitchToRideView(window);
  1271.     SetWindowText(window, CurrentCoaster->description);
  1272.     
  1273.     /*
  1274.      * All the 3D components are now successfully initialized, so 
  1275.      * work can begin...
  1276.      */
  1277.     ThreeDInitialized = TRUE;
  1278.  
  1279.     return TRUE;
  1280. }
  1281.  
  1282. /**********************************************************************/
  1283.  
  1284. /*
  1285.  * This function shuts down the 3D (i.e. RenderWare) components of the
  1286.  * application in a polite fashion.
  1287.  */
  1288. static void
  1289. TidyUp3D(void)
  1290. {
  1291.     /*
  1292.      * Destroy the current coaster.
  1293.      */
  1294.     if (CurrentCoaster)
  1295.         DestroyRollerCoaster(CurrentCoaster);
  1296.  
  1297.     /*
  1298.      * Destroy the camera's backdrop (if any). Backdrops are not
  1299.      * automatically destroyed by RwDestroyCamera() so we must
  1300.      * manually destroy the backdrop.
  1301.      */
  1302.     if (RwGetCameraBackdrop(Camera))
  1303.         RwDestroyRaster(RwGetCameraBackdrop(Camera));
  1304.  
  1305.     /*
  1306.      * Destroy the camera.
  1307.      */
  1308.     RwDestroyCamera(Camera);
  1309.  
  1310.     /*
  1311.      * Close the library. This will free up any internal resources and
  1312.      * textures loaded.
  1313.      */
  1314.     RwClose();
  1315. }
  1316.  
  1317. /**********************************************************************/
  1318.  
  1319. /*
  1320.  * Render the scene and copy it to the window and device context
  1321.  * given. This function encapsulates the very common RenderWare
  1322.  * for rendering and updating the display.
  1323.  */
  1324. static void
  1325. RenderScene(HWND window, HDC dc)
  1326. {
  1327.     /*
  1328.      * Setup the current camera and perform all necessary initialization
  1329.      * before rendering takes place.
  1330.      */
  1331.     RwBeginCameraUpdate(Camera, (void *)(DWORD)window);
  1332.  
  1333.         /*
  1334.          * Clear the areas of the camera's viewport which were damaged
  1335.          * last time round. If this call is not made, ghost images of
  1336.          * previous rendering will remain.
  1337.          */
  1338.         RwClearCameraViewport(Camera);
  1339.  
  1340.         /*
  1341.          * Re-render the entire scene.
  1342.          */
  1343.         RwRenderScene(CurrentCoaster->scene);
  1344.  
  1345.     /*
  1346.      * Perform all necessary housekeeping after rendering is complete.
  1347.      * After this call, the camera's image buffer will be ready to be
  1348.      * copied to the display.
  1349.      */
  1350.     RwEndCameraUpdate(Camera);
  1351.  
  1352.     /*
  1353.      * Copy the camera's image buffer to the output window.
  1354.      */
  1355.     RwShowCameraImage(Camera, (void*)(DWORD)dc);
  1356. }
  1357.  
  1358. /**********************************************************************/
  1359.  
  1360. /*
  1361.  * Handle window resize. The most important job here is to change the
  1362.  * size of the camera's viewport (if preserving the aspect ratio of
  1363.  * the viewport is necessary, then the viewwindow should also be
  1364.  * changed at this point to reflect then new aspect ratio of the
  1365.  * viewport).
  1366.  */
  1367. static void
  1368. OnSize(HWND window, int width, int height)
  1369. {
  1370.     HDC dc;
  1371.     
  1372.     RwWinOutputSize winOutputSize;
  1373.  
  1374.     /*
  1375.      * Set the output size (using the RwDeviceControl()) to the
  1376.      * size specified.
  1377.      */
  1378.     winOutputSize.width  = (RwInt32)width;
  1379.     winOutputSize.height = (RwInt32)height;
  1380.     winOutputSize.camera = Camera;
  1381.     RwDeviceControl(rwWINSETOUTPUTSIZE, 0L, &winOutputSize, sizeof(RwWinOutputSize));
  1382.  
  1383.     if (DoStretch)
  1384.     {
  1385.         /*
  1386.          * Set the viewport (and backdrop) to half the width and height specified.
  1387.          */
  1388.         RwSetCameraViewport(Camera, 0, 0, width / 2, height / 2);
  1389.     }
  1390.     else
  1391.     {
  1392.         RwSetCameraViewport(Camera, 0, 0, width, height);
  1393.     }
  1394.     
  1395.     TrackBackdropToCamera(Camera);
  1396.  
  1397.     /*
  1398.      * When the viewport has changed size we need to re-render the
  1399.      * scene for the new viewport size.
  1400.      */
  1401.     dc = GetDC(window);
  1402.     RenderScene(window, dc);
  1403.     ReleaseDC(window, dc);
  1404. }
  1405.         
  1406. /**********************************************************************/
  1407.  
  1408. /*
  1409.  * Handle menu selections.
  1410.  */
  1411. static void
  1412. OnMenu(HWND window, WPARAM item)
  1413. {
  1414.     RollerCoasterType *coaster;
  1415.     HDC                dc;
  1416.     OPENFILENAME       openFileName;
  1417.     char               filename[_MAX_PATH];
  1418.     RECT               rect;
  1419.     static FARPROC     dialogProcInstance;
  1420.     
  1421.     switch (item)
  1422.     {
  1423.         case IDM_FILE_NEW:
  1424.             coaster = CreateRollerCoaster();
  1425.             if (coaster != NULL)
  1426.             {
  1427.                 SwitchCoaster(window, coaster);
  1428.                 dc = GetDC(window);
  1429.                 RenderScene(window, dc);
  1430.                 ReleaseDC(window, dc);
  1431.             }
  1432.             else
  1433.             {
  1434.                 MessageBox(window, "Error creating new rollercoaster", "RenderWare Roller",
  1435.                            MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  1436.             }
  1437.             break;
  1438.             
  1439.         case IDM_FILE_OPEN:
  1440.             dialogProcInstance = MakeProcInstance(CoasterMenuDialogProc, AppInstance);
  1441.             if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ROLLERCOASTER_MENU),
  1442.                           window, dialogProcInstance) == IDOK)
  1443.             {
  1444.                 coaster = ReadRollerCoaster(CoasterFilename);
  1445.                 if (coaster != NULL)
  1446.                 {
  1447.                     SwitchCoaster(window, coaster);
  1448.                     dc = GetDC(window);
  1449.                     RenderScene(window, dc);
  1450.                     ReleaseDC(window, dc);
  1451.                 }
  1452.             }
  1453.             FreeProcInstance(dialogProcInstance);
  1454.             break;
  1455.             
  1456.         case IDM_FILE_SAVE:
  1457.             if (CurrentCoaster->filename[0] != '\0')
  1458.             {
  1459.                 if (!WriteRollerCoaster(CurrentCoaster, CurrentCoaster->filename))
  1460.                 {
  1461.                     MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  1462.                 }
  1463.             }
  1464.             else
  1465.             {
  1466.                 filename[0] = '\0';
  1467.                 memset(&openFileName, '\0', sizeof(OPENFILENAME));
  1468.                 openFileName.lStructSize = sizeof(OPENFILENAME);
  1469.                 openFileName.hwndOwner   = window;
  1470.                 openFileName.lpstrFilter = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
  1471.                 openFileName.lpstrFile   = &filename[0];
  1472.                 openFileName.nMaxFile    = sizeof(filename);
  1473.                 openFileName.Flags       = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
  1474.             
  1475.                 if (GetSaveFileName(&openFileName))
  1476.                 {
  1477.                     if (!WriteRollerCoaster(CurrentCoaster, filename))
  1478.                     {
  1479.                         MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  1480.                     }
  1481.                 }
  1482.             }
  1483.             break;
  1484.             
  1485.         case IDM_FILE_SAVEAS:
  1486.             filename[0] = '\0';
  1487.             memset(&openFileName, '\0', sizeof(OPENFILENAME));
  1488.             openFileName.lStructSize = sizeof(OPENFILENAME);
  1489.             openFileName.hwndOwner   = window;
  1490.             openFileName.lpstrFilter = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
  1491.             openFileName.lpstrFile   = &filename[0];
  1492.             openFileName.nMaxFile    = sizeof(filename);
  1493.             openFileName.Flags       = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
  1494.             
  1495.             if (GetSaveFileName(&openFileName))
  1496.             {
  1497.                 if (!WriteRollerCoaster(CurrentCoaster, filename))
  1498.                 {
  1499.                     MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  1500.                 }
  1501.             }
  1502.             break;
  1503.             
  1504.  
  1505.         case IDM_FILE_EXIT:
  1506.             DestroyWindow(window);
  1507.             break;
  1508.             
  1509.         case IDM_EDIT_DESCRIPTION:
  1510.             dialogProcInstance = MakeProcInstance(CoasterDescriptionDialogProc, AppInstance);
  1511.             if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ROLLERCOASTER_DESCRIPTION),
  1512.                           window, dialogProcInstance) == IDOK)
  1513.                 SetWindowText(window, CurrentCoaster->description);
  1514.             FreeProcInstance(dialogProcInstance);  
  1515.             break;
  1516.             
  1517.         case IDM_EDIT_SIDEVIEW:
  1518.             SwitchToElevationView(window);
  1519.             break;
  1520.         
  1521.         case IDM_EDIT_TOPVIEW:
  1522.             SwitchToPlanView(window);
  1523.             break;
  1524.  
  1525.         case IDM_VIEWPOINT_RIDE:
  1526.             SwitchToRideView(window);
  1527.             break;
  1528.  
  1529.         case IDM_VIEWPOINT_SPOTTER:
  1530.             SwitchToSpotterView(window);
  1531.             break;
  1532.  
  1533.         case IDM_VIEWPOINT_TRAIL:
  1534.             SwitchToTrailView(window);
  1535.             break;
  1536.  
  1537.         case IDM_VIEWPOINT_LEAD:
  1538.             SwitchToLeadView(window);
  1539.             break;
  1540.             
  1541.         case IDM_OPTION_STRETCH:
  1542.             if (GetMenuState(GetMenu(window), IDM_OPTION_STRETCH,
  1543.                 MF_BYCOMMAND) == MF_CHECKED)
  1544.             {
  1545.                 DoStretch = FALSE;
  1546.             }
  1547.             else
  1548.             {
  1549.                 DoStretch = TRUE;
  1550.             }
  1551.             UpdateMenu(window);
  1552.                
  1553.             GetClientRect(window, &rect);
  1554.             OnSize(window, rect.right - rect.left, rect.bottom - rect.top);
  1555.             break;
  1556.                
  1557.        case IDM_OPTION_SHOWCAR:
  1558.             if (GetMenuState(GetMenu(window), IDM_OPTION_SHOWCAR,
  1559.                              MF_BYCOMMAND) == MF_CHECKED)
  1560.             {
  1561.                 ShowCar = FALSE;
  1562.                 DisableCoasterCar(CurrentCoaster);
  1563.             }
  1564.             else
  1565.             {
  1566.                 ShowCar = TRUE;
  1567.                 EnableCoasterCar(CurrentCoaster);
  1568.             }
  1569.             UpdateMenu(window);
  1570.             break;
  1571.  
  1572.         case IDM_HELP_ABOUT:
  1573.             dialogProcInstance = MakeProcInstance(CoasterAboutDialogProc, AppInstance);
  1574.             DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ABOUT),
  1575.                       window, dialogProcInstance);
  1576.             FreeProcInstance(dialogProcInstance);
  1577.             break;
  1578.     }
  1579. }
  1580.  
  1581. /**********************************************************************/
  1582.  
  1583. /*
  1584.  * Handle queries about the window's maximum extent.
  1585.  */
  1586. static void
  1587. OnGetMinMaxInfo(MINMAXINFO FAR *minmaxinfo)
  1588. {
  1589.     /*
  1590.      * Constraint the window to the maximum size defined by the constants
  1591.      * MAXIMUM_WINDOW_WIDTH and MAXIMUM_WINDOW_HEIGHT.
  1592.      */
  1593.     minmaxinfo->ptMaxSize.x      = MAXIMUM_WINDOW_WIDTH;
  1594.     minmaxinfo->ptMaxSize.y      = MAXIMUM_WINDOW_HEIGHT;
  1595.     minmaxinfo->ptMaxTrackSize.x = MAXIMUM_WINDOW_WIDTH;
  1596.     minmaxinfo->ptMaxTrackSize.y = MAXIMUM_WINDOW_HEIGHT;
  1597. }
  1598.  
  1599. /**********************************************************************/
  1600.  
  1601. /*
  1602.  * This functions handles the left mouse button going down. Its main
  1603.  * job is to determine the kind of action to be taken when the mouse
  1604.  * moves, such as spinning a clump, or panning the camera. This involves
  1605.  * examining the virtual keys that were depressed when the mouse button
  1606.  * went down and attempting to pick a clump under the mouse pointer
  1607.  * position.
  1608.  */
  1609. static void
  1610. OnLButtonDown(HWND window, int x, int y, WPARAM vKeys)
  1611. {
  1612.     switch (CameraMode)
  1613.     {
  1614.         case cmRIDE:
  1615.         case cmSPOTTER:
  1616.         case cmTRAIL:
  1617.         case cmLEAD:
  1618.             /*
  1619.              * The left button does nothing when riding the
  1620.              * coaster.
  1621.              */
  1622.             MouseMoveMode = mmNONE;
  1623.             break;
  1624.                     
  1625.         case cmELEVATION:
  1626.         case cmPLAN:
  1627.             /*
  1628.              * If a control point is picked, move the control
  1629.              * point, otherwise, move the camera.
  1630.              */
  1631.             if (DoStretch)
  1632.                 ControlPoint = IsCoasterControlPointPicked(CurrentCoaster, Camera, x / 2, y / 2);
  1633.             else
  1634.                 ControlPoint = IsCoasterControlPointPicked(CurrentCoaster, Camera, x, y);
  1635.             if (ControlPoint == 0)
  1636.             {
  1637.                 if (vKeys & MK_SHIFT)
  1638.                     MouseMoveMode = mmALTMOVECAMERA;
  1639.                 else
  1640.                     MouseMoveMode = mmMOVECAMERA;
  1641.             }
  1642.             else
  1643.             {
  1644.                 MouseMoveMode = mmMOVECONTROL;
  1645.             }
  1646.             break;
  1647.     }
  1648.  
  1649.     /*
  1650.      * If any form of action is to be taken on mouse move, remember the
  1651.      * the current x and y position of the mouse and capture future
  1652.      * mouse movement.
  1653.      */
  1654.     if (MouseMoveMode != mmNONE)
  1655.     {
  1656.         SetCapture(window);
  1657.         LastX = x;
  1658.         LastY = y;
  1659.         KillTimer(window, 1);
  1660.     }
  1661. }
  1662.  
  1663. /**********************************************************************/
  1664.  
  1665. /*
  1666.  * This functions handles the right mouse button going down. Its main
  1667.  * job is to determine the kind of action to be taken when the mouse
  1668.  * moves such as panning the camera.
  1669.  */
  1670. static void
  1671. OnRButtonDown(HWND window, int x, int y, WPARAM vKeys)
  1672. {
  1673.     MouseMoveMode = mmMOVELIGHT;
  1674.     
  1675.     /*
  1676.      * If any form of action is to be taken on mouse move, remember the
  1677.      * the current x and y position of the mouse and capture future
  1678.      * mouse movement.
  1679.      */
  1680.     if (MouseMoveMode != mmNONE)
  1681.     {
  1682.         SetCapture(window);
  1683.         LastX = x;
  1684.         LastY = y;
  1685.     }
  1686. }
  1687.  
  1688. /**********************************************************************/
  1689.  
  1690. /*
  1691.  * Handle a movement of the mouse. If a previous left or right mouse
  1692.  * button down event has set a mouse move mode then this function will
  1693.  * take the necessary actions. For example, pan and zooming the camera,
  1694.  * panning the light, dragging or spinning a clump etc.
  1695.  */
  1696. static void
  1697. OnMouseMove(HWND window, int x, int y)
  1698. {
  1699.     HDC   dc;
  1700.     RwV3d position;
  1701.     RwV3d newPos;
  1702.  
  1703.     /*
  1704.      * MouseMoveMode tells us what kind of action to perform.
  1705.      */
  1706.     switch (MouseMoveMode)
  1707.     {
  1708.         case mmNONE:
  1709.             break;
  1710.             
  1711.         case mmMOVECAMERA:
  1712.             switch (CameraMode)
  1713.             {
  1714.                 case cmNONE:
  1715.                 case cmRIDE:
  1716.                 case cmSPOTTER:
  1717.                 case cmTRAIL:
  1718.                 case cmLEAD:
  1719.                     break;
  1720.                 
  1721.                 case cmELEVATION:
  1722.                     RwPushScratchMatrix();
  1723.                         RwRotateMatrix(RwScratchMatrix(),
  1724.                                        CREAL(0.0),
  1725.                                        CREAL(1.0),
  1726.                                        CREAL(0.0),
  1727.                                        RDiv(INT2REAL(LastX - x), CREAL(5.0)),
  1728.                                        rwREPLACE);
  1729.                         RwTransformCamera(Camera, RwScratchMatrix(), rwPOSTCONCAT);
  1730.                     RwPopScratchMatrix();
  1731.                     RwVCMoveCamera(Camera,
  1732.                                    CREAL(0.0),
  1733.                                    CREAL(0.0),
  1734.                                    RDiv(INT2REAL(LastY - y), CREAL(50.0)));
  1735.                     RwGetCameraPosition(Camera, &position);
  1736.                     if (position.x < CREAL(-1.5))
  1737.                         position.x = CREAL(-1.5);
  1738.                     if (position.x > CREAL( 1.5))
  1739.                         position.x = CREAL( 1.5);
  1740.                     if (position.z < CREAL(-1.5))
  1741.                         position.z = CREAL(-1.5);
  1742.                     if (position.z > CREAL( 1.5))
  1743.                         position.z = CREAL( 1.5);
  1744.                     RwSetCameraPosition(Camera, position.x, position.y, position.z);
  1745.                     TrackBackdropToCamera(Camera);
  1746.                     break;
  1747.                 
  1748.                 case cmPLAN:
  1749.                     RwVCMoveCamera(Camera,
  1750.                                    RDiv(INT2REAL(LastX - x), CREAL(50.0)),
  1751.                                    RDiv(INT2REAL(y - LastY), CREAL(50.0)),
  1752.                                    CREAL(0.0));
  1753.                     RwGetCameraPosition(Camera, &position);
  1754.                     if (position.x < CREAL(-1.0))
  1755.                         position.x = CREAL(-1.0);
  1756.                     if (position.x > CREAL( 1.0))
  1757.                         position.x = CREAL( 1.0);
  1758.                     if (position.z < CREAL(-1.0))
  1759.                         position.z = CREAL(-1.0);
  1760.                     if (position.z > CREAL( 1.0))
  1761.                         position.z = CREAL( 1.0);
  1762.                     RwSetCameraPosition(Camera, position.x, position.y, position.z);
  1763.                     break;
  1764.             }
  1765.             break;
  1766.             
  1767.         case mmALTMOVECAMERA:
  1768.             switch (CameraMode)
  1769.             {
  1770.                 case cmNONE:
  1771.                 case cmRIDE:
  1772.                 case cmSPOTTER:
  1773.                 case cmTRAIL:
  1774.                 case cmLEAD:
  1775.                     break;
  1776.                 
  1777.                 case cmELEVATION:
  1778.                     RwVCMoveCamera(Camera,
  1779.                                    CREAL(0.0),
  1780.                                    RDiv(INT2REAL(y - LastY), CREAL(50.0)),
  1781.                                    CREAL(0.0));
  1782.                     RwGetCameraPosition(Camera, &position);
  1783.                     if (position.y < DEFAULTGROUNDLEVEL)
  1784.                         position.y = DEFAULTGROUNDLEVEL;
  1785.                     if (position.y > DEFAULTSKYLEVEL)
  1786.                         position.y = DEFAULTSKYLEVEL;
  1787.                     RwSetCameraPosition(Camera, position.x, position.y, position.z);
  1788.                     TrackBackdropToCamera(Camera);
  1789.                     break;
  1790.                 
  1791.                 case cmPLAN:
  1792.                     RwVCMoveCamera(Camera,
  1793.                                    CREAL(0.0),
  1794.                                    CREAL(0.0),
  1795.                                    RDiv(INT2REAL(y - LastY), CREAL(50.0)));
  1796.                     RwGetCameraPosition(Camera, &position);
  1797.                     if (position.y < DEFAULTGROUNDLEVEL)
  1798.                         position.y = DEFAULTGROUNDLEVEL;
  1799.                     if (position.y > DEFAULTSKYLEVEL)
  1800.                         position.y = DEFAULTSKYLEVEL;
  1801.                     RwSetCameraPosition(Camera, position.x, position.y, position.z);
  1802.                     break;
  1803.             }
  1804.             break;
  1805.             
  1806.         case mmMOVECONTROL:
  1807.             RwPushScratchMatrix();
  1808.                 /*
  1809.                  * This function (from pick.c) returns the new position for a clump which lies under
  1810.                  * the given viewport coordinates. This allows as to drag control points exactly
  1811.                  * under the mouse pointer.
  1812.                  */
  1813.                 if (DoStretch)
  1814.                     GetClumpPositionUnderPointer(COASTERCONTROLPOINT(CurrentCoaster, ControlPoint),
  1815.                                                  Camera, x / 2, y / 2, &newPos);
  1816.                 else
  1817.                     GetClumpPositionUnderPointer(COASTERCONTROLPOINT(CurrentCoaster, ControlPoint),
  1818.                                                  Camera, x, y, &newPos);
  1819.                 RwTranslateMatrix(RwScratchMatrix(), newPos.x, newPos.y, newPos.z, rwREPLACE);
  1820.                 TransformCoasterControlPoint(CurrentCoaster, ControlPoint, RwScratchMatrix());
  1821.             RwPopScratchMatrix();
  1822.             break;
  1823.                 
  1824.         case mmMOVELIGHT:
  1825.             /*
  1826.              * We are panning the light about the origin. We will rotate
  1827.              * the light about the Y axis for movements of the mouse in
  1828.              * X and rotate the light about the X axis for movements of
  1829.              * the mouse in Y. In this case we will ignore the effects
  1830.              * of camera orientation changes.
  1831.              */
  1832.             RwPushScratchMatrix();
  1833.                 /*
  1834.                  * Replace the CTM with a rotation matrix about Y. The number
  1835.                  * of degrees of rotation is given by the mouse X delta.
  1836.                  */
  1837.                 RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0),
  1838.                                INT2REAL(LastX - x), rwREPLACE);
  1839.                                
  1840.                 /*
  1841.                  * Postconcat another rotation onto the CTM. The new rotation
  1842.                  * is a rotation about X, the angle being given by the mouse
  1843.                  * Y delta.
  1844.                  */
  1845.                 RwRotateMatrix(RwScratchMatrix(), CREAL(1.0), CREAL(0.0), CREAL(0.0),
  1846.                                INT2REAL(LastY - y), rwPOSTCONCAT);
  1847.  
  1848.                 /*
  1849.                  * Transform the light by the resultant rotations.
  1850.                  */
  1851.                 RwTransformLight(CurrentCoaster->light, RwScratchMatrix(), rwPOSTCONCAT);
  1852.             RwPopScratchMatrix();
  1853.             break;
  1854.     }
  1855.  
  1856.     if (MouseMoveMode != mmNONE)
  1857.     {
  1858.         /*
  1859.          * Re-render the scene and copy the results to the display.
  1860.          */
  1861.         dc = GetDC(window);
  1862.         RenderScene(window, dc);
  1863.         ReleaseDC(window, dc);
  1864.     
  1865.         /*
  1866.          * Remember the current X and Y for next time.
  1867.          */
  1868.         LastX = x;
  1869.         LastY = y;
  1870.     }
  1871. }
  1872.  
  1873. /**********************************************************************/
  1874.  
  1875. /*
  1876.  * Handle the left mouse button comming back up. The basic action is
  1877.  * to turn off mouse move actions and release mouse capture.
  1878.  */
  1879. static void
  1880. OnLButtonUp(HWND window)
  1881. {
  1882.     HDC dc;
  1883.     
  1884.     /*
  1885.      * If we were engaged in a mouse move action and the button has come
  1886.      * back up, then terminate the action and release mouse capture.
  1887.      */
  1888.     switch (MouseMoveMode)
  1889.     {
  1890.         case mmNONE:
  1891.             break;
  1892.             
  1893.         case mmMOVECAMERA:
  1894.         case mmALTMOVECAMERA:
  1895.             MouseMoveMode = mmNONE;
  1896.             ReleaseCapture();
  1897.             switch (CameraMode)
  1898.             {
  1899.                 case cmRIDE:
  1900.                 case cmSPOTTER:
  1901.                 case cmTRAIL:
  1902.                 case cmLEAD:
  1903.                     break;
  1904.                     
  1905.                 case cmELEVATION:
  1906.                     RwGetCameraPosition(Camera, &CurrentCoaster->elevationPosition);
  1907.                     break;
  1908.                     
  1909.                 case cmPLAN:
  1910.                     RwGetCameraPosition(Camera, &CurrentCoaster->planPosition);
  1911.                     break;
  1912.             }
  1913.             SetTimer(window, 1, 20, NULL);
  1914.             break;
  1915.             
  1916.         case mmMOVECONTROL:
  1917.             MouseMoveMode = mmNONE;
  1918.             ReleaseCapture();
  1919.             UpdateCoasterClump(CurrentCoaster);
  1920.             dc = GetDC(window);
  1921.             RenderScene(window, dc);
  1922.             ReleaseDC(window, dc);
  1923.             SetTimer(window, 1, 20, NULL);
  1924.             break;
  1925.     }
  1926. }
  1927.  
  1928. /**********************************************************************/
  1929.  
  1930. /*
  1931.  * Handle the right mouse button comming back up. The basic action is
  1932.  * to turn of mouse move actions and release mouse capture.
  1933.  */
  1934. static void
  1935. OnRButtonUp(HWND window)
  1936. {
  1937.     /*
  1938.      * If we were engaged in a mouse move action and the button has come
  1939.      * back up, then terminate the action and release mouse capture.
  1940.      */
  1941.     if (MouseMoveMode != mmNONE)
  1942.     {
  1943.         MouseMoveMode = mmNONE;
  1944.         ReleaseCapture();
  1945.     }
  1946. }
  1947.  
  1948. /**********************************************************************/
  1949.  
  1950. /*
  1951.  * Handle an MS Window's drop message, this function will attempt to
  1952.  * load the file dropped as a RenderWare clump. If that fails, it will
  1953.  * then attempt to load the file as a raster and make it the camera's
  1954.  * backdrop.
  1955.  */
  1956. static void
  1957. OnDrop(HWND window, HDROP drop)
  1958. {
  1959.     int      i;
  1960.     int      numFiles;
  1961.     char     path[_MAX_PATH];
  1962.     HDC      dc;
  1963.     RwClump *clump;
  1964.  
  1965.     /*
  1966.      * Get the number of dropped files.
  1967.      */
  1968.     numFiles = DragQueryFile(drop, (UINT)-1, NULL, 0U);
  1969.  
  1970.     /*
  1971.      * Attempt to load each file in turn.
  1972.      */
  1973.     for (i = 0; i < numFiles; i++)
  1974.     {
  1975.         /*
  1976.          * LoadClumpOrBackdrop() is called to attempt to load either a 
  1977.          * clump or texture from the file. If a texture is read then a
  1978.          * sprite using that texture is created and returned.
  1979.          */
  1980.         DragQueryFile(drop, i, path, _MAX_PATH);
  1981.         
  1982.         clump = RwReadShape(path);
  1983.         if (clump)
  1984.             RwAddClumpToScene(CurrentCoaster->scene, clump);
  1985.     }
  1986.     DragFinish(drop);
  1987.  
  1988.     /*
  1989.      * As new objects have been loaded we must re-render the scene.
  1990.      */
  1991.     dc = GetDC(window);
  1992.     RenderScene(window, dc);
  1993.     ReleaseDC(window, dc);
  1994. }
  1995.  
  1996. /**********************************************************************/
  1997.  
  1998. /*
  1999.  * Handle the WM_PAINT message by simply copying the rendering
  2000.  * already performed (an stored in the camera's image buffer) to the
  2001.  * output window. There is no need to re-render as nothing in the
  2002.  * scene has changed since the last render. HandleSize() re-renders
  2003.  * when the viewport changes and HandleDrop() re-renders when
  2004.  * clumps are added.
  2005.  */
  2006. static void
  2007. OnPaint(HWND window)
  2008. {
  2009.     HDC         dc;
  2010.     PAINTSTRUCT     paintStruct;
  2011.  
  2012.     dc = BeginPaint(window, &paintStruct);
  2013.  
  2014.     /*
  2015.      * The truly optimal thing to do would be to get the damaged area
  2016.      * of the window that needs updating and set that to be the damaged
  2017.      * area of the viewport. If this was done then only the portion of 
  2018.      * viewport corresponding to damaged area of the window would be
  2019.      * copied. However, as WM_PAINTs are rare in comparison to timer
  2020.      * expiries or mouse moves (where the real rendering effort goes)
  2021.      * there is little to be gained from damaging only a portion of
  2022.      * the display. Therefore, we invalidate the entire viewport and
  2023.      * copy it all to the window.
  2024.      */
  2025.     RwInvalidateCameraViewport(Camera);
  2026.  
  2027.     /*
  2028.      * Copy the viewport to the display.
  2029.      */
  2030.     RwShowCameraImage(Camera, (void*)(DWORD)dc);
  2031.              
  2032.     EndPaint(window, &paintStruct);
  2033. }
  2034.  
  2035. /**********************************************************************/
  2036.  
  2037. /*
  2038.  * Handle MS Window's timer expiry. This function will perform any
  2039.  * animation actions necessary, including spinning clumps and animating
  2040.  * textures.
  2041.  */
  2042. static void
  2043. OnTimer(HWND window)
  2044. {
  2045.     HDC       dc;
  2046.     RwReal    x;
  2047.     RwReal    y;
  2048.     RwReal    z;
  2049.      
  2050.     if (MouseMoveMode == mmNONE)
  2051.     {
  2052.         switch (CameraMode)
  2053.         {
  2054.             case cmRIDE:
  2055.                 UpdateViewParameters(CurrentCoaster);
  2056.                 RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2057.                 RwSetCameraLookAt(Camera,
  2058.                                   CurrentCoaster->tAt.x,
  2059.                                   CurrentCoaster->tAt.y,
  2060.                                   CurrentCoaster->tAt.z);
  2061.                 x = RAdd(CurrentCoaster->tPosition.x, RMul(CurrentCoaster->tUp.x, CREAL(0.1)));
  2062.                 y = RAdd(CurrentCoaster->tPosition.y, RMul(CurrentCoaster->tUp.y, CREAL(0.1)));
  2063.                 z = RAdd(CurrentCoaster->tPosition.z, RMul(CurrentCoaster->tUp.z, CREAL(0.1)));
  2064.                 RwSetCameraPosition(Camera, x, y, z);
  2065.                 TrackBackdropToCamera(Camera);
  2066.                 UpdateCoasterVelocity(CurrentCoaster);
  2067.                 break;
  2068.             
  2069.             case cmSPOTTER:
  2070.                 UpdateViewParameters(CurrentCoaster);
  2071.                 RwSetCameraPosition(Camera,
  2072.                                     CurrentCoaster->tPosition.x,
  2073.                                     CurrentCoaster->tPosition.y,
  2074.                                     CurrentCoaster->tPosition.z);
  2075.                 RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2076.                 RwSetCameraLookAt(Camera, CREAL(0.0), CREAL(0.0), CREAL(1.0));
  2077.                 RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2078.                 RwPanCamera(Camera, CameraAngle);
  2079.                 RwTiltCamera(Camera, CREAL(40.0));
  2080.                 RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.20));
  2081.                 TrackBackdropToCamera(Camera);
  2082.                 UpdateCoasterVelocity(CurrentCoaster);
  2083.                 
  2084.                 CameraAngle = RAdd(CameraAngle, CREAL(5.0));
  2085.                 if (CameraAngle > CREAL(360.0))
  2086.                     CameraAngle = RSub(CameraAngle, CREAL(360.0));
  2087.                 break;
  2088.                 
  2089.             case cmTRAIL:
  2090.                 UpdateViewParameters(CurrentCoaster);
  2091.                 RwSetCameraPosition(Camera,
  2092.                                     CurrentCoaster->tPosition.x,
  2093.                                     CurrentCoaster->tPosition.y,
  2094.                                     CurrentCoaster->tPosition.z);
  2095.                 RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2096.                 RwSetCameraLookAt(Camera,
  2097.                                   CurrentCoaster->tAt.x,
  2098.                                   CurrentCoaster->tAt.y,
  2099.                                   CurrentCoaster->tAt.z);
  2100.                 RwSetCameraLookUp(Camera,
  2101.                                   CurrentCoaster->tUp.x,
  2102.                                   CurrentCoaster->tUp.y,
  2103.                                   CurrentCoaster->tUp.z);
  2104.                 RwTiltCamera(Camera, CREAL(45.0));
  2105.                 RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.15));
  2106.                 TrackBackdropToCamera(Camera);
  2107.                 UpdateCoasterVelocity(CurrentCoaster);
  2108.                 break;
  2109.  
  2110.             case cmLEAD:
  2111.                 UpdateViewParameters(CurrentCoaster);
  2112.                 RwSetCameraPosition(Camera,
  2113.                                     CurrentCoaster->tPosition.x,
  2114.                                     CurrentCoaster->tPosition.y,
  2115.                                     CurrentCoaster->tPosition.z);
  2116.                 RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
  2117.                 RwSetCameraLookAt(Camera,
  2118.                                   CurrentCoaster->tAt.x,
  2119.                                   CurrentCoaster->tAt.y,
  2120.                                   CurrentCoaster->tAt.z);
  2121.                 RwSetCameraLookUp(Camera,
  2122.                                   CurrentCoaster->tUp.x,
  2123.                                   CurrentCoaster->tUp.y,
  2124.                                   CurrentCoaster->tUp.z);
  2125.                 RwPanCamera(Camera, CREAL(180.0));
  2126.                 RwTiltCamera(Camera, CREAL(60.0));
  2127.                 RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.15));
  2128.                 TrackBackdropToCamera(Camera);
  2129.                 UpdateCoasterVelocity(CurrentCoaster);
  2130.                 break;
  2131.  
  2132.             case cmELEVATION:
  2133.             case cmPLAN:
  2134.                 UpdateViewParameters(CurrentCoaster);
  2135.                 UpdateCoasterVelocity(CurrentCoaster);
  2136.                 break;
  2137.         }
  2138.     }
  2139.  
  2140.     /*
  2141.      * Re-render the scene and copy the results to the display.
  2142.      */
  2143.     dc = GetDC(window);
  2144.     RenderScene(window, dc);
  2145.     ReleaseDC(window, dc);
  2146. }
  2147.  
  2148. /**********************************************************************/
  2149.  
  2150. /*
  2151.  * The window procedure for this application's window.
  2152.  */
  2153. LRESULT CALLBACK
  2154. MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
  2155. {
  2156. #if defined(WIN32)
  2157.     POINTS point;
  2158. #else
  2159.     POINT point;
  2160. #endif
  2161.  
  2162.     switch (message)
  2163.     {
  2164.         case WM_CREATE:
  2165.             /*
  2166.              * Clumps are loaded into the scene by drag and drop.
  2167.              * So make this window a drop site.
  2168.              */
  2169.             DragAcceptFiles(window, TRUE);
  2170.  
  2171.             /*
  2172.              * In this application, 3D animation is driven by window's
  2173.              * timer messages, so turn a timer on.
  2174.              */
  2175.             SetTimer(window, 1, 20, NULL);
  2176.             return 0L;
  2177.  
  2178.         case WM_GETMINMAXINFO:
  2179.             /*
  2180.              * The MK_FP32() is necessary under Watcom to convert
  2181.              * an MS Windows 16 bit far pointer to a 32 bit far pointer.
  2182.              */                 
  2183. #if defined(__WINDOWS_386__)
  2184.             OnGetMinMaxInfo((MINMAXINFO FAR*)MK_FP32((void*)lParam));
  2185. #else
  2186.             OnGetMinMaxInfo((MINMAXINFO FAR*)lParam); 
  2187. #endif
  2188.             return 0L;
  2189.                         
  2190.         case WM_SIZE:
  2191.             if (ThreeDInitialized)
  2192.                 OnSize(window, LOWORD(lParam), HIWORD(lParam));
  2193.             return 0L;
  2194.  
  2195.         case WM_DROPFILES:
  2196.             if (ThreeDInitialized)
  2197.                 OnDrop(window, (HDROP)wParam);
  2198.             return 0L;
  2199.             
  2200.         case WM_COMMAND:
  2201.             if (LOWORD(lParam) == 0)
  2202.             {
  2203.                 KillTimer(window, 1);
  2204.                 OnMenu(window, wParam);
  2205.                 SetTimer(window, 1, 20, NULL);
  2206.             }
  2207.             return 0L;
  2208.  
  2209.         case WM_LBUTTONDOWN:
  2210.             if (ThreeDInitialized)
  2211.             {
  2212. #if defined(WIN32)
  2213.                 point = MAKEPOINTS(lParam);
  2214. #else
  2215.                 point = MAKEPOINT(lParam);
  2216. #endif
  2217.                 OnLButtonDown(window, point.x, point.y, wParam);
  2218.             }
  2219.             return 0L;
  2220.  
  2221.         case WM_RBUTTONDOWN:
  2222.             if (ThreeDInitialized)
  2223.             {
  2224. #if defined(WIN32)
  2225.                 point = MAKEPOINTS(lParam);
  2226. #else
  2227.                 point = MAKEPOINT(lParam);
  2228. #endif
  2229.                 OnRButtonDown(window, point.x, point.y, wParam);
  2230.             }
  2231.             return 0L;
  2232.  
  2233.         case WM_MOUSEMOVE:
  2234.             if (ThreeDInitialized)
  2235.             {
  2236.                 if (MouseMoveMode != mmNONE)
  2237.                 {
  2238. #if defined(WIN32)
  2239.                     point = MAKEPOINTS(lParam);
  2240. #else
  2241.                     point = MAKEPOINT(lParam);
  2242. #endif
  2243.                     OnMouseMove(window, point.x, point.y);
  2244.                 }
  2245.             }
  2246.             return 0L;
  2247.  
  2248.         case WM_LBUTTONUP:
  2249.             if (ThreeDInitialized)
  2250.             {
  2251.                 OnLButtonUp(window);
  2252.             }
  2253.             return 0L;
  2254.  
  2255.         case WM_RBUTTONUP:
  2256.             if (ThreeDInitialized)
  2257.                 OnRButtonUp(window);
  2258.             return 0L;
  2259.  
  2260.         case WM_PAINT:
  2261.             if (ThreeDInitialized)
  2262.                 OnPaint(window);
  2263.             return 0L;
  2264.  
  2265.         case WM_TIMER:
  2266.             if (ThreeDInitialized)
  2267.                 OnTimer(window);
  2268.             return 0L;
  2269.             
  2270.         case WM_DESTROY:          
  2271.             /*
  2272.              * The window is going away so it is no longer a drop site.
  2273.              */
  2274.             DragAcceptFiles(window, FALSE);
  2275.  
  2276.             /*
  2277.              * Turn the timer off.
  2278.              */
  2279.             KillTimer(window, 1);
  2280.  
  2281.             /*
  2282.              * Quit message handling.
  2283.              */
  2284.             PostQuitMessage(0);
  2285.             return 0L;
  2286.     }
  2287.  
  2288.     /*
  2289.      * Let Windows handle all other messages.
  2290.      */
  2291.     return DefWindowProc(window, message, wParam, lParam);
  2292. }
  2293.  
  2294. /**********************************************************************/
  2295.  
  2296. /*
  2297.  * Parse the command line arguments.
  2298.  */
  2299. static void
  2300. ParseCommandLineArguments(LPSTR cmdLine)
  2301. {
  2302.     char *c;
  2303.     char *option;
  2304.     
  2305.     c = cmdLine;
  2306.     
  2307.     while (*c != '\0')
  2308.     {
  2309.         option = strchr(c, '-');
  2310.         if (option == NULL)
  2311.             option = strchr(c, '/');
  2312.         if (option == NULL)
  2313.             return;             
  2314.     
  2315.         if (++option == '\0')
  2316.             return;
  2317.         
  2318.         switch (*option)
  2319.         {
  2320.             case 'g':
  2321.             case 'G':
  2322.                 UseWinG = !UseWinG;
  2323.                 break;
  2324.         }
  2325.         
  2326.         c = (option + 1);
  2327.     }
  2328. }
  2329.  
  2330. /**********************************************************************/
  2331.  
  2332. /*
  2333.  * MS Windows application entry point.
  2334.  */
  2335. int PASCAL
  2336. WinMain(HANDLE instance, HANDLE prevInstance, LPSTR cmdLine, int cmdShow)
  2337. {
  2338.     MSG  message;          
  2339.     HWND window;
  2340.     char buffer[_MAX_PATH];
  2341.     int  pathLen;
  2342.     char drive[_MAX_DRIVE];
  2343.     char dir[_MAX_DIR];
  2344.     
  2345.     /*
  2346.      * Remember the instance handle in a global variable for later use.
  2347.      */    
  2348.     AppInstance = instance;
  2349.  
  2350.     if (prevInstance)
  2351.     {
  2352.         /*           
  2353.          * Only allow one viewer application to run at any one time.
  2354.          */
  2355.         MessageBox(NULL, "The RenderWare Rollercoaster is already running...",
  2356.                    WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
  2357.         return FALSE;
  2358.     }
  2359.     
  2360.     /*
  2361.      * Parse the command line arguments.
  2362.      */
  2363.     ParseCommandLineArguments(cmdLine);
  2364.     
  2365.     /*
  2366.      * Register the window class.
  2367.      */
  2368.     if (!InitApplication(instance))
  2369.     { 
  2370.         return FALSE;        
  2371.     }
  2372.  
  2373.     /*
  2374.      * Create the window.
  2375.      */
  2376.     window = InitInstance(instance);
  2377.     if (window == NULL)
  2378.         return FALSE;
  2379.         
  2380.     /*
  2381.      * Identify the directory from which the application is being
  2382.      * run. We will use this for getting hold of scripts and
  2383.      * textures, and also for loading coaster files. We ensure that
  2384.      * the path identified ends in a backslash.
  2385.      */
  2386.     GetModuleFileName(instance, buffer, sizeof(buffer));
  2387.     _splitpath(buffer, drive, dir, NULL, NULL); /* Caution, PC specific function. */
  2388.     strcpy(AppPath, drive);
  2389.     strcat(AppPath, dir);
  2390.     pathLen = strlen(AppPath);
  2391.     if (pathLen > 0)
  2392.     {
  2393.         if (AppPath[pathLen - 1] != '\\')
  2394.         {
  2395.             AppPath[pathLen] = '\\';
  2396.             AppPath[pathLen + 1] = '\0';
  2397.         }
  2398.     }
  2399.  
  2400.     /*
  2401.      * Initialize the 3D (RenderWare) components of the app.
  2402.      */
  2403.     if (!Init3D(window))
  2404.     {
  2405.         DestroyWindow(window);
  2406.         return FALSE;
  2407.     }
  2408.  
  2409.     /*
  2410.      * Show the window, and refresh it.
  2411.      */
  2412.     ShowWindow(window, cmdShow); 
  2413.     UpdateWindow(window);
  2414.  
  2415.     /*
  2416.      * Enter the message processing loop.
  2417.      */
  2418.     while (GetMessage(&message, NULL, 0U, 0U))
  2419.     {
  2420.         TranslateMessage(&message);
  2421.         DispatchMessage(&message);
  2422.     }
  2423.  
  2424.     /*
  2425.      * Tidy up the 3D (RenderWare) components of the application.
  2426.      */
  2427.     TidyUp3D();
  2428.  
  2429.     return message.wParam;
  2430. }
  2431.  
  2432. /**********************************************************************/
  2433.