home *** CD-ROM | disk | FTP | other *** search
/ Learn 3D Graphics Programming on the PC / Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso / rwdos / dosview.c < prev    next >
C/C++ Source or Header  |  1995-02-15  |  42KB  |  1,640 lines

  1. /**********************************************************************
  2.  *
  3.  * File :     dosview.c
  4.  *
  5.  * Abstract : A very simple, sample RenderWare application for
  6.  *            MS-Dos / PC-Dos. This application has very little
  7.  *            functionality, but is simply intended as a demonstration
  8.  *            of how to use the RenderWare API.
  9.  *
  10.  *            This application had been written to be compatible with
  11.  *            both the fixed and floating-point versions of the
  12.  *            RenderWare library, i.e., it uses the macros CREAL,
  13.  *            INT2REAL, RAdd, RDiv, RSub etc. If your application is
  14.  *            intended for the floating-point version of the library
  15.  *            only these macros are not necessary.
  16.  *
  17.  *            Please note that this application is intended for
  18.  *            demonstration purposes only. No support will be
  19.  *            provided for this code and it comes with no warranty.
  20.  *
  21.  * This file is a product of Criterion Software Ltd.
  22.  *
  23.  * This file is provided as is with no warranties of any kind and is
  24.  * provided without any obligation on Criterion Software Ltd. or
  25.  * Canon Inc. to assist in its use or modification.
  26.  *
  27.  * Criterion Software Ltd. and Canon Inc. will not, under any
  28.  * circumstances, be liable for any lost revenue or other damages arising
  29.  * from the use of this file.
  30.  *
  31.  * Copyright (c) 1991, 1992, 1993. Canon Inc.
  32.  * All Rights Reserved.
  33.  *
  34.  **********************************************************************/
  35.  
  36. /****************************************************************************
  37.  Includes
  38.  */
  39.  
  40. #include <stdlib.h>
  41. #include <stdio.h>
  42. #include <string.h>
  43.  
  44. #include <i86.h>
  45. #include <math.h>    /* Required for floating point */
  46.  
  47. #include "rwlib.h"
  48. #include "rwdos.h"
  49. #include "doswrap.h"
  50.  
  51. /****************************************************************************
  52.  Types
  53.  */
  54.  
  55. /**********************************************************************
  56.  *
  57.  * Application constants.
  58.  *
  59.  **********************************************************************/
  60.  
  61. #define DELETE 8
  62.  
  63. #define BOOL int
  64.  
  65. /*
  66.  * MS Windows compatible defines
  67.  */
  68.  
  69. #define MK_CONTROL 0x4
  70. #define MK_SHIFT 0x2
  71.  
  72. /*
  73.  * Depending which video mode is being used the colur used will change
  74.  */
  75.  
  76. /*
  77.  * Default distance of the camera from the origin.
  78.  */
  79. #define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
  80.  
  81.  
  82. /**********************************************************************
  83.  *
  84.  * Type definitions.
  85.  *
  86.  **********************************************************************/
  87.  
  88. /*
  89.  * This enumerated type tells us what kind of action should be taken
  90.  * on mouse events.
  91.  */
  92. typedef enum
  93. {
  94.     MMNoAction,
  95.     MMPanAndZoomCamera,
  96.     MMTiltCamera,
  97.     MMSpinClump,
  98.     MMDragClump,
  99.     MMDragClumpInZ,
  100.     MMPanLight
  101. } MMMode;
  102.  
  103. /*
  104.  * This enumerated type tells us what kind of animation action should
  105.  * be taken on timer messages. It is used to allow us to spin clumps
  106.  * for a "momentum" effect.
  107.  */
  108. typedef enum
  109. {
  110.     ANoAction,
  111.     ASpinClump
  112. } AMode;
  113.  
  114. /**********************************************************************
  115.  *
  116.  * Application global variables.
  117.  *
  118.  **********************************************************************/
  119.  
  120. /*
  121.  * String for clearing status line
  122.  */
  123.  
  124. static char sGClear[]="                                        ";
  125.  
  126. /*
  127.  * Texts colour
  128.  */
  129.  
  130. static int nGTextColour;
  131.  
  132.  
  133. /*
  134.  * Global RenderWare object pointers. In this simple application we
  135.  * make use of only a single scene, camera and light.
  136.  */
  137. static RwScene    *Scene  = NULL;
  138. static RwCamera   *Camera = NULL;
  139. static RwLight    *Light  = NULL;
  140.  
  141. /*
  142.  * This variable is used to remember which clump was last picked
  143.  * when spinning or dragging a clump on mouse move events.
  144.  */
  145. static RwClump    *PickedClump = NULL;
  146.  
  147. /*
  148.  * This matrix is used when spinning a clump, it is stored globally
  149.  * so that it is not necessary to re-build the matrix for each frame of
  150.  * animation.
  151.  */
  152. static RwMatrix4d *SpinMatrix = NULL;
  153.  
  154. /*
  155.  * This variable tells us what kind of action to take on a mouse move.
  156.  * The action depends on the object that was picked when the mouse button
  157.  * went down, and on the selection of virtual keys that were depressed at
  158.  * that time. By default no action is taken on mouse move.
  159.  */
  160. static MMMode      MouseMoveMode = MMNoAction;
  161.  
  162. /*
  163.  * This variable tells us what kind of action to take on timer
  164.  * expiry. It is used to give clumps momentum, i.e. the clump
  165.  * will continue to spin after the user releases the mouse button.
  166.  * By default no animation action is taken.
  167.  */
  168. static AMode       AnimMode = ANoAction;
  169.  
  170. /*
  171.  * Global variables used to remember the last mouse X and Y coordinates
  172.  * when involved in a pan, zoom, drag or spin.
  173.  */
  174. static int         LastX;
  175. static int         LastY;
  176.  
  177. /*
  178.  * Current distance the camera is from the origin. This is stored to
  179.  * help us pan and zoom the camera.
  180.  */
  181. static RwReal      CameraDistance = DEFAULT_CAMERA_DISTANCE;
  182.  
  183. /*
  184.  * Current angle of tilt applied to the camera.
  185.  */
  186. static RwReal      CameraTilt = CREAL(0.0);
  187.  
  188. /*
  189.  * Current animation frame number. This is incremented each time a
  190.  * frame is drawn on timer expiry. The purpose of this variable is
  191.  * to enable us to periodically (based on the number of frames) call
  192.  * RwOrthoNormalizeMatrix() to correct any errors which have crept
  193.  * into the clump's joint matrix because of the inevitable
  194.  * restrictions on the accuracy of fixed-point numbers. See
  195.  * HandleTimer for more details.
  196.  */
  197. static int         FrameNumber = 0;
  198.  
  199. /*
  200.  * This flag indicates whether the 3D components of the application
  201.  * have been successfully initialized as yet. It is used to guard
  202.  * the message loop handler functions from being invoked before the
  203.  * 3D components of the application are successfully initialized.
  204.  */
  205. static BOOL        ThreeDInitialized = FALSE;
  206.  
  207. /* The size of the screen
  208.  */
  209.  
  210. static int nGScrWidth;
  211. static int nGScrHeight;
  212.  
  213. /****************************************************************************
  214.  DosPrintString
  215.  
  216.  On entry     : xcord
  217.                         : ycord
  218.                         : string
  219.                         : colour
  220.  On exit        :
  221.  */
  222.  
  223. void DosPrintString(int nX,int nY,char *sString,int nCol)
  224. {
  225.     RwPrintChar pcPrint;
  226.  
  227.     pcPrint.x = nX;
  228.     pcPrint.y = nY;
  229.     pcPrint.color = nCol;
  230.  
  231.     for (;(*sString);sString++)  {
  232.         pcPrint.c = (*sString);
  233.     RwDeviceControl(rwPRINTCHAR,0,&pcPrint,sizeof(RwPrintChar));
  234.         pcPrint.x+=8;
  235.     };
  236. }
  237.  
  238.  
  239. /****************************************************************************
  240.  DosTimer
  241.  
  242.  On entry    :
  243.  On exit    : Timer (in milliseconds)
  244.  */
  245.  
  246. int DosTimer(void)
  247. {
  248.     union REGS r;
  249.     int nTime;
  250.  
  251.     r.h.ah=0;
  252.  
  253.     int386(0x1a,&r,&r);
  254.  
  255.     nTime = ((r.w.cx)<<16)|(r.w.dx);
  256.  
  257.     return (nTime*55);
  258. }
  259.  
  260. /****************************************************************************
  261.  DosGetKey
  262.  
  263.  Get the ascii key code of any depressed key. (Do not wait for a key press.
  264.  -> return 0 if no key is pressed)
  265.  
  266.  On entry    :
  267.  On exit     : Key pressed in ascii (or 0 if no key pressed)
  268.  */
  269.  
  270. int DosGetKey(void)
  271. {
  272.     union REGPACK rp;
  273.  
  274.     memset(&rp,0,sizeof(rp));
  275.     rp.h.ah = 0x06;
  276.     rp.h.dl = 0xff;
  277.  
  278.     intr(0x21,&rp);
  279.  
  280.     if (!(rp.w.flags & 0x40 )) {       /* Check Z flag */
  281.         /* Got key */
  282.  
  283.         if (rp.h.al) {
  284.             return ((int)rp.h.al);
  285.         };
  286.  
  287.         memset(&rp,0,sizeof(rp));
  288.         rp.h.ah = 0x06;
  289.         rp.h.dl = 0xff;
  290.         intr(0x21,&rp);
  291.  
  292.         if (!(rp.w.flags & 0x40)) {
  293.             return ((int)rp.h.al);
  294.         };
  295.  
  296.         return (rp.h.al|0x80);
  297.     };
  298.  
  299.     return 0;
  300. }
  301.  
  302. /****************************************************************************
  303.  DosShiftCtrl
  304.  
  305.  Finds the status of the 'Shift/Control' type keys.
  306.  
  307.  On entry    :
  308.  On exit    : Bit         Meaning
  309.                         0                Right Shift
  310.                         1                Left Shift
  311.                         2                Ctrl
  312.                         3                Alt
  313.                         4       Scroll Lock
  314.                         5                Num Lock
  315.                         6             Caps Lock
  316.                         7                Insert on
  317.  */
  318.  
  319. int DosShiftCtrl(void)
  320. {
  321.     union REGPACK rp;
  322.  
  323.     memset(&rp,0,sizeof(rp));
  324.  
  325.     rp.h.ah=0x02;
  326.     intr(0x16,&rp);
  327.  
  328.     return ((int)rp.h.al);
  329. }
  330.  
  331.  
  332. /**********************************************************************/
  333.  
  334. /*
  335.  * If using the status bars then show the polgon & vertex count
  336.    of the picked clump in the right status bar */
  337.  
  338. ShowPickedClumpData(void)
  339. {
  340.   char string[]="                                ";
  341.   if (PickedClump)
  342.   {
  343.     sprintf (string, "%d polygons, %d vertices",
  344.               RwGetClumpNumPolygons(PickedClump),
  345.               RwGetClumpNumVertices(PickedClump));
  346.  
  347.         DosPrintString(0,nGScrHeight-8,string,nGTextColour);
  348.   }
  349.   else
  350.     {
  351.         DosPrintString(0,nGScrHeight-8,string,0);
  352.     };
  353. }
  354.  
  355. /**********************************************************************/
  356.  
  357. /*
  358.  * This function initializes the 3D (i.e. RenderWare) components of the
  359.  * application. This function opens the RenderWare library, creates a
  360.  * camera, a scene, a light and a matrix for spinning. A user-draw may
  361.  * also be created if USERDRAW_LABELS is defined.
  362.  */
  363. static BOOL
  364. Init3D(char *sFilename)
  365. {
  366.     char     windowText[128];
  367.     char     version[30];
  368.     char     buffer[128];
  369.     int      param;
  370.     int i;
  371.     RwReal naWhite[]={CREAL(1.0),CREAL(1.0),CREAL(1.0)};
  372.     long nError;
  373.  
  374.     /*
  375.     * Attempt to open (and initialize) the RenderWare library.
  376.     */
  377.     if (!RwOpen("DOSMOUSE", &nError))
  378.     {
  379.         printf("Unable to access renderware!!\n");
  380.         switch (nError) {
  381.             case E_RW_DOS_MODE_UNAVAILABLE: {
  382.                printf("The installed VESA card is unable to switch to the resolution");
  383.                 printf(" requested.\n");
  384.                 printf("Either install a different video adapter or use a ");
  385.                 printf("supported video mode.");
  386.                 break;
  387.             };
  388.             case E_RW_DOS_NO_VESA_BIOS: {
  389.                 printf("A VESA bios is unavailable on this machine.\n");
  390.                 printf("Either use a VESA compatible Video Adapter or install a ");
  391.                 printf("VESA bios emulation TSR.\n");
  392.                 break;
  393.             };
  394.             case E_RW_DOS_INCOMPATIBLE_BIOS: {
  395.                 printf("The VESA bios on this machine is not of high enough version ");
  396.                 printf("to function\ncorrectly with RenderWare. Use a version 1.0 or");
  397.                 printf(" higher VESA bios or TSR.\n");
  398.                 break;
  399.             };
  400.             case E_RW_DOS_NO_MOUSE: {
  401.                 printf("No Microsoft compatible mouse driver present.\n");
  402.                 printf("Install a microsoft compatible mouse driver and try again.\n");
  403.                 break;
  404.             };
  405.             default: {
  406.                 printf("Unknown Error !!!!!!!!!!!!!!!\n");
  407.                 break;
  408.             };
  409.         };
  410.         return FALSE;
  411.     }
  412.  
  413.     /* Set up character set */
  414.  
  415.   RwGetDeviceInfo(rwSCRHEIGHT,&nGScrHeight,sizeof(int));
  416.   RwGetDeviceInfo(rwSCRWIDTH,&nGScrWidth,sizeof(int));
  417.   nGTextColour = RwDeviceControl(rwSCRGETCOLOR,0,naWhite,sizeof(RwReal[3]));
  418.  
  419.     /*--- Only look for scripts and textures in subdirectories under the current
  420.     one. RWSHAPEPATH need not be set then */
  421.  
  422.     RwSetShapePath(".",rwPRECONCAT);
  423.  
  424.     strcpy(buffer,sFilename);
  425.  
  426.     i = strlen(buffer);
  427.     while((buffer[i] != '\\')&&(i>=0)) {
  428.         i--;
  429.     };
  430.  
  431.  
  432.     if (i>=0) {
  433.         buffer[i+1] = 0;
  434.         strcat(buffer, "TEXTURES");
  435.         RwSetShapePath(buffer, rwPOSTCONCAT);
  436.  
  437.         buffer[i+1] = 0;
  438.         strcat(buffer, "SCRIPTS");
  439.         RwSetShapePath(buffer, rwPOSTCONCAT);
  440.     };
  441.  
  442.     RwSetShapePath("SCRIPTS", rwPRECONCAT);
  443.     RwSetShapePath("TEXTURES", rwPRECONCAT);
  444.  
  445.  
  446.     /*
  447.     * Label the display with information about the version of
  448.     * RenderWare being used. Its rather unlikely that
  449.     * RwGetSystemInfo() will fail so we ignore its return value.
  450.     */
  451.   RwGetSystemInfo(rwVERSIONSTRING, &version,sizeof(version));
  452.   RwGetSystemInfo(rwFIXEDPOINTLIB, ¶m,sizeof(param));
  453.     sprintf(windowText, "RenderWare(tm) V%s %s",
  454.         version, (param ? "Fixed" : "Float"));
  455.     DosPrintString(0,nGScrHeight-16,windowText,nGTextColour);
  456.  
  457.     /*
  458.     * Create the camera which will be used for rendering.
  459.     */
  460.  
  461.     Camera = RwCreateCamera(nGScrWidth,nGScrHeight-24, NULL);
  462.     if (!Camera)
  463.     {
  464.         /*
  465.           * As with RwOpen(), the most common cause for a failure to create
  466.           * a camera is insufficient memory so we will explicitly check for
  467.           * this condition and report it. Otherwise a general error is issued.
  468.           */
  469.         if (RwGetError() == E_RW_NOMEM)
  470.         {
  471.             RwClose();
  472.             printf("Insufficient memory to create the RenderWare(tm) camera\n");
  473.         }
  474.         else
  475.         {
  476.             RwClose();
  477.             printf("Error creating the RenderWare(tm) camera\n");
  478.         }
  479.         exit(-1);
  480.     }
  481.  
  482.  
  483.     /* RwSetCameraProjection(Camera,rwPARALLEL); */
  484.  
  485.  
  486.   RwSetCameraViewport(Camera, 0, 0, nGScrWidth, (nGScrHeight-24));
  487.  
  488.  
  489.  
  490.     /*
  491.     * Set the camera's background color to blue.
  492.     */
  493.   RwSetCameraBackColor(Camera, CREAL(0.3), CREAL(0.0), CREAL(0.0));
  494.  
  495.     /*
  496.     * By default, the camera lies on the X-Z plane and points down Z
  497.     * into the screen. We shall retain the camera's orientation, but move
  498.     * the camera DEFAULT_CAMERA_DISTANCE units down Z away from the screen.
  499.     */
  500.  
  501.     RwTiltCamera(Camera, CameraTilt);
  502.     RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  503.  
  504.     /*
  505.     * Another change from previous versions of RenderWare is the amount of
  506.     * prespective generated by the default viewwindow size. When converting
  507.     * applications from previous versions of RenderWare the simple rule is
  508.     * to divide the viewwindow size by five to get the same prespective effect
  509.     * as given under previous versions.
  510.     */
  511.     if (nGScrWidth >= nGScrHeight) {
  512.         RwSetCameraViewwindow(Camera,
  513.                               CREAL(1.0),
  514.                               RMul(CREAL(1.0),
  515.                                                     RDiv(INT2REAL(nGScrHeight),
  516.                                                     INT2REAL(nGScrWidth))));
  517.     } else {
  518.         RwSetCameraViewwindow(Camera,
  519.                               RMul(CREAL(1.0),
  520.                                                     RDiv(INT2REAL(nGScrWidth),
  521.                                                     INT2REAL(nGScrHeight))),
  522.                               CREAL(1.0));
  523.     };
  524.  
  525.     /*
  526.     * Create a scene which will contain the clumps to be rendered and the
  527.     * light or lights illuminating those clumps . In this very simple
  528.     * application it would be perfectly acceptable to use the default scene
  529.     * (as returned by RwDefaultScene()) for rendering. However, it is good
  530.     * practice to always create a scene which will be used for your rendering
  531.     * and only use the default scene as a bag for currently unused clumps and
  532.     * lights.
  533.     */
  534.  
  535.     Scene = RwCreateScene();
  536.     if (!Scene)
  537.     {
  538.         RwDestroyCamera(Camera);
  539.         RwClose();
  540.         printf("Error creating the RenderWare(tm) scene\n");
  541.         exit(-1);
  542.     }
  543.  
  544.     /*
  545.     * Our scene will be illuminated by a directional light. The illumination
  546.     * vector of the light is (-1.0, -1.0, -1.0) and its brightness will be 1.0.
  547.     */
  548.  
  549.     Light = RwCreateLight(rwDIRECTIONAL, CREAL(-1.0), CREAL(-1.0), CREAL(-1.0),
  550.                       CREAL(1.0));
  551.     if (!Light)
  552.     {
  553.         RwDestroyScene(Scene);
  554.         RwDestroyCamera(Camera);
  555.         RwClose();
  556.         printf("Error creating the RenderWare(tm) light\n");
  557.         exit(-1);
  558.     }
  559.  
  560.     /*
  561.     * Add the new light to our scene.
  562.     */
  563.     RwAddLightToScene(Scene, Light);
  564.  
  565.     /*
  566.     * Create the spin matrix.
  567.     */
  568.     SpinMatrix = RwCreateMatrix();
  569.     if (!SpinMatrix)
  570.     {
  571.         RwDestroyScene(Scene);
  572.         RwDestroyCamera(Camera);
  573.         RwClose();
  574.         printf("Error creating the RenderWare(tm) matrix\n");
  575.         exit(-1);
  576.     }
  577.  
  578.     /*
  579.     * All the 3D components are now successfully initialized, so
  580.     * work can begin...
  581.     */
  582.     ThreeDInitialized = TRUE;
  583.  
  584.     return TRUE;
  585. }
  586.  
  587. /**********************************************************************/
  588.  
  589. /*
  590.  * This function shuts down the 3D (i.e. RenderWare) components of the
  591.  * application in a polite fashion.
  592.  */
  593.  
  594. static void
  595. TidyUp3D()
  596. {
  597.     /*
  598.     * Destroy the spin matrix.
  599.     */
  600.     RwDestroyMatrix(SpinMatrix);
  601.  
  602.     /*
  603.     * Destroy the scene. This will destroy the contents of the scene,
  604.     * i.e. any clumps and lights in that scene. In this case destroying
  605.     * the scene will destroy the light we created in Init3D, and any
  606.     * clumps we have loaded and not already destroyed.
  607.     */
  608.     RwDestroyScene(Scene);
  609.  
  610.     /*
  611.     * Destroy the camera.
  612.     */
  613.     RwDestroyCamera(Camera);
  614.  
  615.     /*
  616.     * Close the library. This will free up any internal resources and
  617.     * textures loaded.
  618.     */
  619.     RwClose();
  620. }
  621.  
  622. /**********************************************************************/
  623.  
  624. /*
  625.  * Attempt to load the file with the given file name as either a clump
  626.  * or a texture (in which case a decal clump is created and returned).
  627.  */
  628.  
  629. static RwClump *
  630. LoadClumpOrTexture(char *fileName)
  631. {
  632.     RwClump    *clump;
  633.     RwTexture  *texture;
  634.     char        buffer[128];
  635.     RwErrorCode eCode;
  636.   char buffer2[128];
  637.  
  638.     DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  639.     DosPrintString(0,nGScrHeight-24,"Loading File...",nGTextColour);
  640.  
  641.     /*
  642.     * Attempt to load the file as a script file.
  643.     */
  644.     clump = RwReadShape(fileName);
  645.     if (!clump)
  646.         {
  647.         /*
  648.         * The load failed. This could be because of errors in the
  649.         * script file, insufficient memory, or the file containing
  650.         * garbage. We will now examine the error code returned to
  651.         * decide whether there was a script file error (in which case
  652.         * an error will be issued to the user), whether memory was
  653.         * exhausted (a different error is issued to the user), or
  654.         * the file contained garbage (in which case we will attempt
  655.         * to load the file as a texture instead of a script).
  656.         */
  657.         eCode = RwGetError();
  658.  
  659.         switch (eCode)
  660.             {
  661.             case E_RW_NOMEM:
  662.             /*
  663.             * Ran out of memory...
  664.             */
  665.  
  666.             sprintf(buffer, "Insufficient memory : %s",fileName);
  667.             buffer[40]='\0';
  668.             DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  669.             DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  670.             return NULL;
  671.  
  672.             case E_RW_NOFILE:
  673.             case E_RW_BADOPEN:
  674.             case E_RW_RSPARSE:
  675.             case E_RW_RSREAD:
  676.             case E_RW_READ:
  677.             /*
  678.             * We will (somewhat optimistically) assume that if we
  679.             * had a read or parse error on the stream then the
  680.             * file is not a script file but a texture instead.
  681.             * So attempt to load a texture. Note, as we use
  682.             * RwGetNamedTexture() to get the texture, the texture
  683.             * will not be reloaded if it has been previously loaded
  684.             * into RenderWare.
  685.             */
  686.             texture = RwGetNamedTexture(fileName);
  687.             if (texture)
  688.                 {
  689.                 /*
  690.                 * The file was indeed a texture, so attempt to
  691.                 * create a decal clump using this texture.
  692.                 */
  693.                 clump = RwCreateSprite(texture);
  694.                 if (!clump)
  695.                 {
  696.           RwGetTextureName(texture,buffer2,sizeof(buffer2));
  697.                     sprintf(buffer,
  698.             "Error texture->decal clump  %s",buffer2);
  699.                     buffer[40]='\0';
  700.                     DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  701.                     DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  702.                     return NULL;
  703.                 }
  704.             }
  705.             else
  706.                 {
  707.                 /*
  708.                 * The texture load failed, issue an error message
  709.                 * giving the general nature of the problem.
  710.                 */
  711.                 switch (RwGetError())
  712.                 {
  713.                 case E_RW_NOFILE:
  714.                 case E_RW_BADOPEN:
  715.                 /*
  716.                 * Could not open the file...
  717.                 */
  718.                 sprintf(buffer, "Error opening %s", fileName);
  719.                 buffer[40]='\0';
  720.                 DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  721.                 DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  722.                 return NULL;
  723.  
  724.                 case E_RW_NOMEM:
  725.                 /*
  726.                 * Ran out of memory...
  727.                 */
  728.                 sprintf(buffer,
  729.                     "No mem for texture %s",
  730.                     fileName);
  731.                 buffer[40]='\0';
  732.                 DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  733.                 DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  734.                 return NULL;
  735.  
  736.                 default:
  737.                 /*
  738.                 * We will not enumerate all the errors, so simply
  739.                 * issue a general failure report.
  740.                 */
  741.                 sprintf(buffer,
  742.                     "Error reading file %s",
  743.                     fileName);
  744.                 buffer[40]='\0';
  745.                 DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  746.                 DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  747.                 return NULL;
  748.                 }
  749.             }
  750.             break;
  751.  
  752.             default:
  753.             /*
  754.             * On any other error we will assume there is an error in
  755.             * the script file.
  756.             */
  757.             sprintf(buffer,
  758.             "Error in file %s <%i>",fileName,(int)eCode);
  759.             buffer[40]='\0';
  760.  
  761.             DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  762.             DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
  763.             return NULL;
  764.         }
  765.     }
  766.  
  767.  
  768.     /*
  769.     * Return the resulting clump.
  770.     */
  771.     DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
  772.     return clump;
  773. }
  774.  
  775. /**********************************************************************/
  776.  
  777. /*
  778.  * Parse the names of any .rwx files on the command line, read those
  779.  * files and add them to the scene. This allows DosView to be associated
  780.  * with .rwx files in the file manager and be started automatically when
  781.  * a .rwx file is double-clicked.
  782.  */
  783. static BOOL
  784. ReadFromCommandLine(int argc, char *argv[])
  785. {
  786.     int nCount;
  787.   RwClump *clump;
  788.  
  789.     for (nCount=1;nCount<argc;nCount++) {
  790.         clump = LoadClumpOrTexture(argv[nCount]);
  791.         if (clump) {
  792.             RwAddClumpToScene(Scene, clump);
  793.         };
  794.     };
  795.  
  796.     return 1;
  797. }
  798.  
  799. /**********************************************************************/
  800.  
  801. /*
  802.  * This functions handles the left mouse button going down. Its main
  803.  * job is to determine the kind of action to be taken when the mouse
  804.  * moves, such as spinning a clump, or panning the camera. This involves
  805.  * examining the virtual keys that were depressed when the mouse button
  806.  * went down and attempting to pick a clump under the mouse pointer
  807.  * position.
  808.  */
  809. static void
  810. HandleLeftButtonDown(int x, int y, int vKeys)
  811. {
  812.     RwPickRecord pick;
  813.     int nPos;
  814.     char sBuffer[40];
  815.     int nKey;
  816.   RwClump *clump;
  817.  
  818.     /* Check the area where the click took place */
  819.  
  820.     if (y>(nGScrHeight-16)) {
  821.         /* Its off the bottom of the screen -> enter filename */
  822.         RwDPointerRemove();
  823.         DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  824.         DosPrintString(0,nGScrHeight-8,"Filename :  @",nGTextColour);
  825.  
  826.         nPos = 0;
  827.         sBuffer[0]='\0';
  828.  
  829.         do {
  830.             nKey = DosGetKey();
  831.  
  832.             if ((nPos>0)&&(nKey==8)) {
  833.                 RwDPrintChar((nPos+11)<<3,nGScrHeight-8,'@',nGTextColour);
  834.                 RwDPrintChar((nPos+12)<<3,nGScrHeight-8,' ',nGTextColour);
  835.                 nPos--;
  836.                 sBuffer[nPos]='\0';
  837.             };
  838.  
  839.             if ((nPos<25)&&(nKey>32)&&(nKey<127)) {
  840.                 sBuffer[nPos]=nKey;
  841.                 nPos++;
  842.                 sBuffer[nPos]='\0';
  843.  
  844.                 RwDPrintChar((nPos+11)<<3,nGScrHeight-8,nKey,nGTextColour);
  845.                 RwDPrintChar((nPos+1+11)<<3,nGScrHeight-8,'@',nGTextColour);
  846.             };
  847.  
  848.         } while (nKey!=13);
  849.  
  850.  
  851.         if (nPos>0) {
  852.             clump = LoadClumpOrTexture(sBuffer);
  853.  
  854.             if (clump) {
  855.                 RwAddClumpToScene(Scene, clump);
  856.             };
  857.         };
  858.  
  859.         return;
  860.     };
  861.  
  862.     /*
  863.     * If the left button is depressed anywhere in the client area of the
  864.     * window then the animated spin is cancelled.
  865.     */
  866.     AnimMode = ANoAction;
  867.  
  868.     /*
  869.     * The mouse move action is based on whether an object is picked or
  870.     * not. Therefore, attempt to pick an object in the scene.
  871.     */
  872.     if (RwPickScene(Scene, x, y, Camera, &pick)) {
  873.         switch (pick.type) {
  874.             case rwNAPICKOBJECT:
  875.             /*
  876.             * Clicked on the background, so there is no drag action or
  877.             * picked clump.
  878.             */
  879.             MouseMoveMode = MMNoAction;
  880.             PickedClump   = NULL;
  881.             RwDPointerRemove();
  882.             DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  883.             DosPrintString(0,nGScrHeight-8,"Stop Clump Rotation",nGTextColour);
  884.             break;
  885.  
  886.             case rwPICKCLUMP:
  887.             /*
  888.             * If a clump was picked and both the shift and control
  889.             * virtual keys were depressed then we will destroy the
  890.             * clump (and hence remove it from the scene).
  891.             */
  892.             if ((vKeys & (MK_CONTROL | MK_SHIFT)) ==
  893.                   (MK_CONTROL | MK_SHIFT)) {
  894.                  RwDPointerRemove();
  895.  
  896.                 DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  897.                 DosPrintString(0,nGScrHeight-8,"Destroy Clump",nGTextColour);
  898.  
  899.                 /*
  900.                 * Destroy the clump (and remove it from the scene).
  901.                 */
  902.                 RwDestroyClump(pick.object.clump.clump);
  903.  
  904.                 /*
  905.                 * The clump has been destroyed so there is no mouse
  906.                 * move action and no picked clump.
  907.                 */
  908.                 MouseMoveMode = MMNoAction;
  909.                 PickedClump   = NULL;
  910.             }
  911.             else
  912.             {
  913.                 /*
  914.                 * A clump was picked, so remember which clump was picked
  915.                 * in order that it may be manipulated later when the
  916.                 * mouse moves.
  917.                 */
  918.                 PickedClump = pick.object.clump.clump;
  919.  
  920.                 /*
  921.                 * Determine the exact nature of the manipulation of
  922.                 * the clump on mouse move by examining the virtual key
  923.                 * state. If the shift key was depressed, then drag
  924.                 * the clump, otherwise, spin it.
  925.                 */
  926.                 if (vKeys & MK_SHIFT)
  927.                 {
  928.                     MouseMoveMode = MMDragClump;
  929.                     RwDPointerRemove();
  930.                     DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  931.                     DosPrintString(0,nGScrHeight-8,"Drag Clump",nGTextColour);
  932.                 }
  933.                 else if (vKeys & MK_CONTROL)
  934.                 {
  935.                     MouseMoveMode = MMDragClumpInZ;
  936.                     RwDPointerRemove();
  937.                     DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  938.                     DosPrintString(0,nGScrHeight-8,"Drag Clump in Z",nGTextColour);
  939.                 }
  940.                 else
  941.                 {
  942.                     MouseMoveMode = MMSpinClump;
  943.                     RwDPointerRemove();
  944.                     DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  945.                     DosPrintString(0,nGScrHeight-8,"Rotate Clump",nGTextColour);
  946.                 }
  947.             }
  948.             break;
  949.         }
  950.     }
  951.  
  952.     /*
  953.     * If any form of action is to be taken on mouse move, remember the
  954.     * the current x and y position of the mouse and capture future
  955.     * mouse movement.
  956.     */
  957.     if (MouseMoveMode != MMNoAction)
  958.     {
  959.         LastX = x;
  960.         LastY = y;
  961.     }
  962.  
  963.     RwDPointerRemove();
  964.     ShowPickedClumpData();
  965. }
  966.  
  967. /**********************************************************************/
  968.  
  969. /*
  970.  * This functions handles the right mouse button going down. Its main
  971.  * job is to determine the kind of action to be taken when the mouse
  972.  * moves such as panning the camera.
  973.  */
  974. static void
  975. HandleRightButtonDown(int x, int y, int vKeys)
  976. {
  977.  
  978.     if (vKeys & MK_CONTROL)
  979.     {
  980.         MouseMoveMode = MMPanLight;
  981.         RwDPointerRemove();
  982.         DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  983.         DosPrintString(0,nGScrHeight-8,"Pan Light",nGTextColour);
  984.     }
  985.     else if (vKeys & MK_SHIFT)
  986.     {
  987.         MouseMoveMode = MMTiltCamera;
  988.         RwDPointerRemove();
  989.         DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  990.         DosPrintString(0,nGScrHeight-8,"Tilt Camera",nGTextColour);
  991.     }
  992.     else
  993.     {
  994.         MouseMoveMode = MMPanAndZoomCamera;
  995.         RwDPointerRemove();
  996.         DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  997.         DosPrintString(0,nGScrHeight-8,"Pan and Zoom Camera",nGTextColour);
  998.     }
  999.  
  1000.     /*
  1001.     * If any form of action is to be taken on mouse move, remember the
  1002.     * the current x and y position of the mouse and capture future
  1003.     * mouse movement.
  1004.     */
  1005.     if (MouseMoveMode != MMNoAction)
  1006.     {
  1007.         LastX = x;
  1008.         LastY = y;
  1009.     }
  1010. }
  1011.  
  1012.  
  1013. /**********************************************************************/
  1014.  
  1015. /*
  1016.  * Handle a movement of the mouse. If a previous left or right mouse
  1017.  * button down event has set a mouse move mode then this function will
  1018.  * take the necessary actions. For example, pan and zooming the camera,
  1019.  * panning the light, dragging or spinning a clump etc.
  1020.  */
  1021. static void
  1022. HandleMouseMove(int x, int y)
  1023. {
  1024.     RwClump    *parent;
  1025.     RwMatrix4d *tmpMatrix;
  1026.     RwMatrix4d *worldToLocal;
  1027.     RwV3d       up;
  1028.     RwV3d       right;
  1029.     RwV3d       at;
  1030.     RwReal      xDelta;
  1031.     RwReal      yDelta;
  1032.     RwReal      xAngle;
  1033.     RwReal      yAngle;
  1034.  
  1035.     /*
  1036.     * MouseMoveMode tells us what kind of action to perform.
  1037.     */
  1038.     switch (MouseMoveMode) {
  1039.         case MMNoAction:
  1040.         break;
  1041.  
  1042.         case MMPanAndZoomCamera:
  1043.         /*
  1044.         * We are panning and zooming the camera. Movement of the
  1045.         * mouse in the X direction will pan the camera about the
  1046.         * origin of world coordinate space, and movement of the mouse
  1047.         * in the Y direction will zoom the camera into and out of the
  1048.         * the scene.
  1049.         */
  1050.  
  1051.         /*
  1052.         * Move the camera back to the origin, as we wish to pan about
  1053.         * the origin of the world and not about the origin of the
  1054.         * camera.
  1055.         */
  1056.         RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
  1057.  
  1058.         /*
  1059.         * Undo the tilt as we wish to pan about the world Y axis.
  1060.         */
  1061.         RwTiltCamera(Camera, -CameraTilt);
  1062.  
  1063.         /*
  1064.         * Pan the camera by mouse X delta degrees.
  1065.         */
  1066.         RwPanCamera(Camera, INT2REAL(LastX - x));
  1067.  
  1068.         /*
  1069.         * Zoom the camera by changing the distance the camera
  1070.         * is from the origin of the world by mouse Y delta divided
  1071.         * by 10 units.
  1072.         */
  1073.         CameraDistance = RAdd(CameraDistance,
  1074.                 RDiv(INT2REAL(LastY - y), CREAL(10.0)));
  1075.  
  1076.         /*
  1077.         * Redo the tilt.
  1078.         */
  1079.         RwTiltCamera(Camera, CameraTilt);
  1080.  
  1081.         /*
  1082.         * Move the camera out to its new distance from the world's
  1083.         * origin.
  1084.         */
  1085.         RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  1086.         break;
  1087.  
  1088.         case MMTiltCamera:
  1089.         /*
  1090.         * Move the camera back to the origin, as we wish to tilt about
  1091.         * the origin of the world and not about the origin of the
  1092.         * camera.
  1093.         */
  1094.         RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
  1095.  
  1096.         /*
  1097.         * Undo the existing tilt.
  1098.         */
  1099.         RwTiltCamera(Camera, -CameraTilt);
  1100.  
  1101.         /*
  1102.         * Compute the new angle of tilt.
  1103.         */
  1104.         CameraTilt = RAdd(CameraTilt, INT2REAL(LastY - y));
  1105.  
  1106.         /*
  1107.         * Apply the new tilt.
  1108.         */
  1109.         RwTiltCamera(Camera, CameraTilt);
  1110.  
  1111.         /*
  1112.         * Move the camera back out.
  1113.         */
  1114.         RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  1115.         break;
  1116.  
  1117.         case MMSpinClump:
  1118.         /*
  1119.         * Compute the angles of spin (simply derived from the mouse move deltas).
  1120.         */
  1121.         yAngle = INT2REAL(x - LastX);
  1122.         xAngle = INT2REAL(y - LastY);
  1123.  
  1124.         /*
  1125.         * In DosView, a clump is spun about its own, local coordinate system,
  1126.         * origin, rather than the origin of the world coordinate system. There
  1127.         * are a number of ways this could be achieved, but the most convenient
  1128.         * is to simply apply the rotations to the clump's joint (articulation)
  1129.         * transform.
  1130.         *
  1131.         * A further important point is that the axes of rotation must be the
  1132.         * camera's Look Up and Look Right vector rather than simply
  1133.         * [CREAL(0.0), CREAL(1.0), CREAL(0.0)] and
  1134.         * [CREAL(1.0), CREAL(0.0), CREAL(0.0)]. This ensures that, if the camera
  1135.         * has been panned, tilted, revolved or transformed so that it no longer
  1136.         * looks down the Z axis, user interaction will still operate correctly,
  1137.         * i.e. moving the mouse to the left will spin the clump clockwise etc.
  1138.         *
  1139.         * Therefore, the first stage is to get the camera's Look Up and
  1140.         * Look Right vectors.
  1141.         */
  1142.         RwGetCameraLookUp(Camera, &up);
  1143.         RwGetCameraLookRight(Camera, &right);
  1144.  
  1145.         /*
  1146.         * Unfortunately, rotation about the camera's Look Up and Look Right
  1147.         * vectors is complicated if the clump being manipulated is a child
  1148.         * clump (i.e. not the root of a clump hierarchy). If this is the
  1149.         * case, the camera's vectors have to be transformed into the
  1150.         * coordinate space of the parent of the clump being manipulated.
  1151.         */
  1152.         if ((parent = RwGetClumpParent(PickedClump))) {
  1153.             /*
  1154.             * Get a handle to a couple of temporary matrices.
  1155.             */
  1156.             tmpMatrix    = RwPushScratchMatrix();
  1157.             worldToLocal = RwPushScratchMatrix();
  1158.  
  1159.             /*
  1160.             * Get the parent clump's LTM (which maps local coordinates to
  1161.             * world space).
  1162.             */
  1163.             RwGetClumpLTM(parent, tmpMatrix);
  1164.  
  1165.             /*
  1166.             * Invert it so that it maps world coordinates to the parent's
  1167.             * local coordinate space.
  1168.             */
  1169.             RwInvertMatrix(tmpMatrix, worldToLocal);
  1170.  
  1171.             /*
  1172.             * And transform the camera's vectors into local space.
  1173.             */
  1174.             RwTransformVector(&up, worldToLocal);
  1175.             RwNormalize(&up);
  1176.             RwTransformVector(&right, worldToLocal);
  1177.             RwNormalize(&right);
  1178.  
  1179.             /*
  1180.             * Discard the temporary matrices.
  1181.             */
  1182.             RwPopScratchMatrix();
  1183.             RwPopScratchMatrix();
  1184.         }
  1185.  
  1186.         /*
  1187.         * Apply the rotations.
  1188.         */
  1189.         RwRotateMatrix(SpinMatrix, up.x, up.y, up.z, yAngle, rwREPLACE);
  1190.         RwRotateMatrix(SpinMatrix, right.x, right.y, right.z, xAngle, rwPOSTCONCAT);
  1191.  
  1192.         /*
  1193.         * Apply the resulting, composite transformation to the clump.
  1194.         */
  1195.         RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
  1196.  
  1197.         /*
  1198.         * As the mouse has moved enable the clump spin.
  1199.         */
  1200.         AnimMode = ASpinClump;
  1201.  
  1202.         break;
  1203.  
  1204.         case MMDragClump:
  1205.         RwPushScratchMatrix();
  1206.         /*
  1207.         * Compute the amount to translate the object by. This is simply
  1208.         * derived from the mouse deltas scaled by some arbitrary quantity
  1209.         * to prevent objects moving too "quickly".
  1210.         */
  1211.         xDelta = RDiv(INT2REAL(x - LastX), CREAL(50.0));
  1212.         yDelta = RDiv(INT2REAL(LastY - y), CREAL(50.0));
  1213.  
  1214.         /*
  1215.         * In a similar fashion to spinning a clump we must take into account
  1216.         * the camera's orientation when dragging a clump. This is done by
  1217.         * translating along the camera's look up and look right vectors
  1218.         * (scaled appropriately) rather than the clump's local axes.
  1219.         */
  1220.         RwGetCameraLookRight(Camera, &right);
  1221.         RwGetCameraLookUp(Camera, &up);
  1222.  
  1223.         /*
  1224.         * See the previous case for a description of why the following is
  1225.         * necessary.
  1226.         */
  1227.         if ((parent = RwGetClumpParent(PickedClump)))
  1228.         {
  1229.             tmpMatrix    = RwPushScratchMatrix();
  1230.             worldToLocal = RwPushScratchMatrix();
  1231.             RwGetClumpLTM(parent, tmpMatrix);
  1232.             RwInvertMatrix(tmpMatrix, worldToLocal);
  1233.             RwTransformVector(&up, worldToLocal);
  1234.             RwNormalize(&up);
  1235.             RwTransformVector(&right, worldToLocal);
  1236.             RwNormalize(&right);
  1237.             RwPopScratchMatrix();
  1238.             RwPopScratchMatrix();
  1239.         }
  1240.  
  1241.         /*
  1242.         * Perform the translations.
  1243.         */
  1244.         RwTranslateMatrix(RwScratchMatrix(),
  1245.                 RMul(right.x, xDelta), RMul(right.y, xDelta),
  1246.                 RMul(right.z, xDelta), rwREPLACE);
  1247.         RwTranslateMatrix(RwScratchMatrix(),
  1248.                 RMul(up.x, yDelta), RMul(up.y, yDelta),
  1249.                 RMul(up.z, yDelta), rwPOSTCONCAT);
  1250.  
  1251.         /*
  1252.         * Apply the resulting, composite transform to the clump.
  1253.         */
  1254.         RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1255.  
  1256.         RwPopScratchMatrix();
  1257.         break;
  1258.  
  1259.         case MMDragClumpInZ:
  1260.         RwPushScratchMatrix();
  1261.         /*
  1262.         * Compute the amount to translate the object by. This is simply
  1263.         * derived from the mouse deltas scaled by some arbitrary quantity
  1264.         * to prevent objects moving too "quickly".
  1265.         */
  1266.         yDelta = RDiv(INT2REAL(LastY - y), CREAL(50.0));
  1267.  
  1268.         /*
  1269.         * In a similar fashion to  spinning a clump we must take into account
  1270.         * the camera's orientation when dragging a clump. This is done by
  1271.         * translating along the camera's Look At vector (scaled appropriately)
  1272.         * rather than the clump's local axes.
  1273.         */
  1274.         RwGetCameraLookAt(Camera, &at);
  1275.  
  1276.         /*
  1277.         * See the case for MMSpinClump: for a description of why the
  1278.         * following is necessary.
  1279.         */
  1280.         if ((parent = RwGetClumpParent(PickedClump)))
  1281.         {
  1282.             tmpMatrix    = RwPushScratchMatrix();
  1283.             worldToLocal = RwPushScratchMatrix();
  1284.             RwGetClumpLTM(parent, tmpMatrix);
  1285.             RwInvertMatrix(tmpMatrix, worldToLocal);
  1286.             RwTransformVector(&at, worldToLocal);
  1287.             RwNormalize(&at);
  1288.             RwPopScratchMatrix();
  1289.             RwPopScratchMatrix();
  1290.         }
  1291.  
  1292.         /*
  1293.         * Perform the translation.
  1294.         */
  1295.         RwTranslateMatrix(RwScratchMatrix(),
  1296.                 RMul(at.x, yDelta), RMul(at.y, yDelta),
  1297.                 RMul(at.z, yDelta), rwREPLACE);
  1298.  
  1299.         /*
  1300.         * Apply the resulting, composite transform to the clump.
  1301.         */
  1302.         RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1303.  
  1304.         RwPopScratchMatrix();
  1305.         break;
  1306.  
  1307.         case MMPanLight:
  1308.         /*
  1309.         * We are panning the light about the origin. We will rotate
  1310.         * the light about the Y axis for movements of the mouse in
  1311.         * X and rotate the light about the X axis for movements of
  1312.         * the mouse in Y. In this case we will ignore the effects
  1313.         * of camera orientation changes.
  1314.         */
  1315.         RwPushScratchMatrix();
  1316.         /*
  1317.         * Replace the CTM with a rotation matrix about Y. The number
  1318.         * of degrees of rotation is given by the mouse X delta.
  1319.         */
  1320.         RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0),
  1321.               INT2REAL(LastX - x), rwREPLACE);
  1322.  
  1323.         /*
  1324.         * Postconcat another rotation onto the CTM. The new rotation
  1325.         * is a rotation about X, the angle being given by the mouse
  1326.         * Y delta.
  1327.         */
  1328.         RwRotateMatrix(RwScratchMatrix(), CREAL(1.0), CREAL(0.0), CREAL(0.0),
  1329.               INT2REAL(LastY - y), rwPOSTCONCAT);
  1330.  
  1331.         /*
  1332.         * Transform the light by the resultant rotations.
  1333.         */
  1334.         RwTransformLight(Light, RwScratchMatrix(),rwPOSTCONCAT);
  1335.         RwPopScratchMatrix();
  1336.         break;
  1337.     }
  1338.  
  1339.     /*
  1340.     * Remember the current X and Y for next time.
  1341.     */
  1342.     LastX = x;
  1343.     LastY = y;
  1344. }
  1345.  
  1346. /**********************************************************************/
  1347.  
  1348. /*
  1349.  * Handle the left mouse button comming back up. The basic action is
  1350.  * to turn off mouse move actions and release mouse capture.
  1351.  */
  1352.  
  1353. static void
  1354. HandleLeftButtonUp(void)
  1355. {
  1356.     /*
  1357.     * If we were engaged in a mouse move action and the button has come
  1358.     * back up, then terminate the action and release mouse capture.
  1359.     */
  1360.     if (MouseMoveMode != MMNoAction)
  1361.     {
  1362.         MouseMoveMode = MMNoAction;
  1363.     }
  1364.     RwDPointerRemove();
  1365.     DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  1366. }
  1367.  
  1368. /**********************************************************************/
  1369.  
  1370. /*
  1371.  * Handle the right mouse button comming back up. The basic action is
  1372.  * to turn of mouse move actions and release mouse capture.
  1373.  */
  1374. static void
  1375. HandleRightButtonUp(void)
  1376. {
  1377.     /*
  1378.     * If we were engaged in a mouse move action and the button has come
  1379.     * back up, then terminate the action and release mouse capture.
  1380.     */
  1381.     if (MouseMoveMode != MMNoAction)
  1382.     {
  1383.         MouseMoveMode = MMNoAction;
  1384.     }
  1385.     RwDPointerRemove();
  1386.     DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
  1387. }
  1388.  
  1389. /**********************************************************************/
  1390.  
  1391. /*
  1392.  * Handle MS Window's timer expiry. This function will perform any
  1393.  * animation actions necessary, including spinning clumps and animating
  1394.  * textures.
  1395.  */
  1396. static void
  1397. HandleTimer(void) {
  1398.     /*
  1399.     * Determine if there is a clump to spin.
  1400.     */
  1401.  
  1402.  
  1403.  
  1404.     if (PickedClump && (AnimMode != ANoAction) && (MouseMoveMode == MMNoAction))
  1405.         {
  1406.         switch (AnimMode) {
  1407.             case ASpinClump:
  1408.             /*
  1409.             * Spin the last clump picked by the last computed spin matrix.
  1410.             */
  1411.  
  1412.  
  1413.  
  1414.             RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
  1415.  
  1416.             FrameNumber++;
  1417.             if ((FrameNumber & 0x7f) == 0) {
  1418.                 /*
  1419.                 * Every 128 frames (a somewhat arbitrary frequency) we call
  1420.                 * RwOrthoNormalizeMatrix() on the clump's joint matrix to
  1421.                 * correct any errors which may have crept into it during
  1422.                 * successive matrix concatenations. This is necessary due
  1423.                 * to the inevitable accuracy limitations of fixed-point
  1424.                 * numbers. It is unlikely that this action will be necessary
  1425.                 * in your own applications unless you perform a very large
  1426.                 * number of incremental rotation matrix concatenations (as
  1427.                 * is done here). If this is the case with your application,
  1428.                 * periodic use of RwOrthoNormalizeMatrix() will ensure
  1429.                 * that these rounding errors will be eliminated.
  1430.                 *
  1431.                 * We call RwOrthoNormalizeMatrix() in the floating-point
  1432.                 * version of DosView as well, as it is not a particularly
  1433.                 * expensive operation and the same problems of rounding
  1434.                 * errors in matrices can occur when using floating-point.
  1435.                 * Although they are more uncommon.
  1436.                 */
  1437.  
  1438.                 RwPushScratchMatrix();
  1439.  
  1440.                 RwGetClumpJointMatrix(PickedClump, RwScratchMatrix());
  1441.  
  1442.                 RwOrthoNormalizeMatrix(RwScratchMatrix(), RwScratchMatrix());
  1443.  
  1444.                 RwTransformClumpJoint(PickedClump, RwScratchMatrix(), rwREPLACE);
  1445.  
  1446.                 RwPopScratchMatrix();
  1447.  
  1448.             }
  1449.  
  1450.             break;
  1451.         }
  1452.     }
  1453.  
  1454.     /*
  1455.     * Animate textures. Enumerate over all the textures in the texture
  1456.     * dictionary stack calling RwTextureNextFrame() to bump the
  1457.     * current frame pointer of each texture. For single frame textures
  1458.     * this is a no-op.
  1459.     */
  1460.  
  1461.  
  1462.   if (!(FrameNumber&15)) {
  1463.     RwForAllNamedTextures(RwTextureNextFrame);
  1464.   };
  1465.  
  1466.  
  1467.     /*
  1468.     * See the description of HandlePaint() for a description of this common
  1469.     * RenderWare cliche for rendering a scene and copying it to the display.
  1470.     */
  1471.  
  1472.   RwBeginCameraUpdate(Camera,NULL);
  1473.   RwClearCameraViewport(Camera);
  1474.     RwRenderScene(Scene);
  1475.     RwEndCameraUpdate(Camera);
  1476.  
  1477.     RwShowCameraImage(Camera, NULL);
  1478. }
  1479.  
  1480.  
  1481. /****************************************************************************
  1482.  Main
  1483.  */
  1484.  
  1485. void main(int nArgc,char *saArgv[])
  1486. {
  1487.     int nKey;
  1488.     int nMouseX,nMouseY,nMouseBut,nOldMouseBut,nOldMouseX,nOldMouseY;
  1489.     int nDX,nDY;
  1490.     int nChange;
  1491.     int nCtrlShift;
  1492.  
  1493.     if (!Init3D(saArgv[0]))
  1494.     {
  1495.         exit(-1);
  1496.     };
  1497.  
  1498.     /*
  1499.     * Parse any command line parameters.
  1500.     */
  1501.     if (!ReadFromCommandLine(nArgc, saArgv))
  1502.     {
  1503.         TidyUp3D();
  1504.         exit(-1);
  1505.     };
  1506.  
  1507.     RwDPointerSetPosition(nGScrWidth>>1,nGScrHeight>>1);
  1508.  
  1509.   RwDPointerDisplay(&nOldMouseX,&nOldMouseY,&nOldMouseBut);
  1510.  
  1511.     /* Create pointer */
  1512.  
  1513.     nKey = DosGetKey();
  1514.  
  1515.     while (nKey!=27) {        /* ESC quits */
  1516.  
  1517.         RwDPointerDisplay(&nMouseX,&nMouseY,&nMouseBut);
  1518.  
  1519.         nKey = DosGetKey();
  1520.  
  1521.         nCtrlShift = DosShiftCtrl();
  1522.  
  1523.         nDX =(nMouseX-nOldMouseX);
  1524.         nDY =(nMouseY-nOldMouseY);
  1525.  
  1526.         nChange = (nMouseBut&(2+8)) | ( (nOldMouseBut&(2+8)) >>1 );
  1527.  
  1528.         switch (nChange) {
  1529.             case 0+0:
  1530.             case 2+1:
  1531.             case 8+4:
  1532.             case 8+2+4+1: {
  1533.                 /* No change */
  1534.                 break;
  1535.             };
  1536.             case 2:
  1537.             case 8+2+4: {
  1538.  
  1539.                 /* Left Button Down */
  1540.  
  1541.                 HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
  1542.  
  1543.  
  1544.                 break;
  1545.             };
  1546.             case 8:
  1547.             case 8+2+1: {
  1548.                 /* Right Button Down */
  1549.  
  1550.  
  1551.                 HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
  1552.  
  1553.  
  1554.                 break;
  1555.             };
  1556.             case 8+1: {
  1557.                 /* Right down left Up */
  1558.  
  1559.  
  1560.                 HandleLeftButtonUp();
  1561.                 HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
  1562.  
  1563.  
  1564.                 break;
  1565.             };
  1566.             case 2+4: {
  1567.                 /* Right up left Down */
  1568.  
  1569.  
  1570.                 HandleRightButtonUp();
  1571.                 HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
  1572.  
  1573.  
  1574.                 break;
  1575.             };
  1576.             case 8+2: {
  1577.                 /* Left down RIght Down */
  1578.  
  1579.  
  1580.                 HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
  1581.                 HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
  1582.  
  1583.  
  1584.                 break;
  1585.             };
  1586.             case 1+4: {
  1587.                 /* Left up Right Up */
  1588.  
  1589.  
  1590.                 HandleRightButtonUp();
  1591.                 HandleLeftButtonUp();
  1592.  
  1593.  
  1594.                 break;
  1595.             };
  1596.             case 1:
  1597.             case 8+4+1: {
  1598.                 /* Left up */
  1599.  
  1600.  
  1601.                 HandleLeftButtonUp();
  1602.  
  1603.  
  1604.                 break;
  1605.             };
  1606.             case 4:
  1607.             case 2+4+1: {
  1608.                 /* Right up */
  1609.  
  1610.  
  1611.                 HandleRightButtonUp();
  1612.  
  1613.  
  1614.                 break;
  1615.             };
  1616.         };
  1617.  
  1618.         if (nDX||nDY) {
  1619.             /* Mouse Move  */
  1620.              HandleMouseMove(nMouseX,nMouseY);
  1621.         };
  1622.  
  1623.         HandleTimer();
  1624.  
  1625.         nOldMouseX = nMouseX;
  1626.         nOldMouseY = nMouseY;
  1627.         nOldMouseBut = nMouseBut;
  1628.     };
  1629.  
  1630.  
  1631.     /*
  1632.     * Tidy up the 3D (RenderWare) components of the application.
  1633.     */
  1634.  
  1635.     TidyUp3D();
  1636.  
  1637.     exit(0);
  1638. }
  1639.  
  1640.