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

  1. /**********************************************************************
  2.  *
  3.  * File :     rwpaint.c
  4.  *
  5.  * Abstract : RenderWare paint demo. A very simple RenderWare demonstration
  6.  *            that allows the user to paint loaded objects.
  7.  *
  8.  **********************************************************************
  9.  *
  10.  * This file is a product of Criterion Software Ltd.
  11.  *
  12.  * This file is provided as is with no warranties of any kind and is
  13.  * provided without any obligation on Criterion Software Ltd. or
  14.  * Canon Inc. to assist in its use or modification.
  15.  *
  16.  * Criterion Software Ltd. will not, under any
  17.  * circumstances, be liable for any lost revenue or other damages arising
  18.  * from the use of this file.
  19.  *
  20.  * Copyright (c) 1995 Criterion Software Ltd.
  21.  * All Rights Reserved.
  22.  *
  23.  * RenderWare is a trademark of Canon Inc.
  24.  *
  25.  ************************************************************************/
  26.  
  27. /**********************************************************************
  28.  *
  29.  * Header files.
  30.  *
  31.  **********************************************************************/
  32.  
  33. #ifdef    __WINDOWS_386__
  34.  
  35. /*
  36.  * Watcom specific...
  37.  */
  38. #define INCLUDE_SHELLAPI_H
  39. #define INCLUDE_COMMDLG_H
  40. #include <windows.h>
  41.  
  42. #else /* __WINDOWS_386__ */
  43.  
  44. #include <windows.h>
  45. #include <shellapi.h>
  46. #include <commdlg.h>
  47.  
  48. #endif /* __WINDOWS_386__ */
  49.  
  50. #include <stdlib.h>
  51. #include <stdio.h>
  52. #include <string.h>
  53.  
  54. #include "rwlib.h"
  55. #include "rwwin.h"
  56.  
  57. #include "common.h"
  58.  
  59. #include "resource.h"
  60. #include "editmat.h"
  61.  
  62. /**********************************************************************
  63.  *
  64.  * Application constants.
  65.  *
  66.  **********************************************************************/
  67.  
  68. /*
  69.  * Class name for the MS Window's class.
  70.  */
  71. #define RWPAINT_CLASS_NAME      "RwPaintClass"
  72. #define RWPAINT_TOOL_CLASS_NAME "RwPaintToolClass"
  73.  
  74. #define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
  75.  
  76. #define TIMER_TICKS 20
  77.  
  78. #define TOOL_HEIGHT 34
  79. #define BUTTON_OFFSET_X 10
  80. #define BUTTON_OFFSET_Y 5
  81. #define BUTTON_WIDTH 24
  82. #define BUTTON_HEIGHT (24)
  83. #define BUTTON_SPACE 6
  84.  
  85. #define MOVE_BUTTON     1
  86. #define PAINT_BUTTON    2
  87.  
  88. #define MATERIAL_CHANGE     (UINT)(WM_USER + 1)
  89.  
  90. /*
  91.  * Error dialog box title.
  92.  */
  93. #define ERROR_DIALOG_TITLE "RenderWare Painter Error"
  94.  
  95. /*
  96.  * Default size of the viewer's window.
  97.  */
  98. #define DEFAULT_WINDOW_WIDTH    400
  99. #define DEFAULT_WINDOW_HEIGHT   (400+TOOL_HEIGHT)
  100.  
  101. #define MAXIMUM_WINDOW_WIDTH    512
  102. #define MAXIMUM_WINDOW_HEIGHT   (512+TOOL_HEIGHT)
  103.  
  104. /*
  105.  * Default distance of the camera from the origin.
  106.  */
  107. #define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
  108.  
  109. /*
  110.  * Comment this line in if object labeling using User Draws is
  111.  * desired.
  112.  */
  113. #define USERDRAW_LABELSx
  114.  
  115. /**********************************************************************
  116.  *
  117.  * Type definitions.
  118.  *
  119.  **********************************************************************/
  120.  
  121. /*
  122.  * This enumerated type tells us what kind of action should be taken
  123.  * on mouse events.
  124.  */
  125. typedef enum
  126. {
  127.     MMNoAction,
  128.     MMPanAndZoomCamera,
  129.     MMTiltCamera,
  130.     MMSpinClump,
  131.     MMDragClump,
  132.     MMDragClumpInZ,
  133.     MMPanLight
  134. }
  135. MMMode;
  136.  
  137. /*
  138.  * This enumerated type tells us what kind of animated action should
  139.  * be taken on timer messages. It is used to allow us to spin clumps
  140.  * and decals for a momentum effect.
  141.  */
  142. typedef enum
  143. {
  144.     ANoAction,
  145.     ASpinClump
  146. }
  147. AMode;
  148.  
  149. typedef struct
  150. {
  151.     char *UserDraw;
  152.     char *FullName;
  153. }
  154. CLUMP_DATA;
  155.  
  156. /**********************************************************************
  157.  *
  158.  * Application global variables.
  159.  *
  160.  **********************************************************************/
  161.  
  162. #ifdef USERDRAW_LABELS
  163.  
  164. /*
  165.  * Device status flag. Set to True if the device is using dibs and hence 
  166.  * will not support userdraws
  167.  */
  168. static int UsingDib = TRUE;
  169.  
  170. #endif
  171.  
  172. /*
  173.  * Application instance handle for this application.
  174.  */
  175. static HANDLE AppInstance;
  176.  
  177. /*
  178.  * Global RenderWare object pointers. In this simple application we
  179.  * make use of only a single scene, camera and light.
  180.  */
  181. static RwScene *Scene = NULL;
  182. static RwCamera *Camera = NULL;
  183. static RwLight *Light = NULL;
  184.  
  185. /*
  186.  * This variable is used to remember which clump was last picked
  187.  * when spinning or dragging a clump on mouse move events.
  188.  * ClumpWithUserDraw is used to hold the 'current clump' when saving
  189.  */
  190. static RwClump *PickedClump = NULL;
  191. static RwClump *ClumpWithUserDraw = NULL;
  192.  
  193. /*
  194.  * This matrix is used when spinning a clump, it is stored globally
  195.  * so that it is not necessary to re-build the matrix for each frame of
  196.  * animation.
  197.  */
  198. static RwMatrix4d *SpinMatrix = NULL;
  199.  
  200. #ifdef USERDRAW_LABELS
  201. /*
  202.  * This is the user draw instance used to label the currently selected
  203.  * clump. It is added to clumps as they become selected.
  204.  */
  205. static RwUserDraw *UserDraw = NULL;
  206.  
  207. #endif
  208.  
  209. /*
  210.  * This variable tells us what kind of action to take on a mouse move.
  211.  * The action depends on the object that was picked when the mouse button
  212.  * went down, and on the selection of virtual keys that were depressed at
  213.  * that time. By default, no action is taken on mouse move.
  214.  */
  215. static MMMode MouseMoveMode = MMNoAction;
  216.  
  217. /*
  218.  * This variable tells us what kind of action to take on timer
  219.  * expiry. It is used to give both clumps and decals momentum, i.e. they
  220.  * will continue to spin after the user releases the mouse button. By
  221.  * default, no animation action is taken.
  222.  */
  223. static AMode AnimMode = ANoAction;
  224.  
  225. /*
  226.  * Global variables used to remember the last mouse X and Y coordinates
  227.  * when involved in a pan, zoom, drag or spin.
  228.  */
  229. static int LastX;
  230. static int LastY;
  231.  
  232. /*
  233.  * Current distance the camera is from the origin. This is stored to
  234.  * help us pan and zoom the camera.
  235.  */
  236. static RwReal CameraDistance = DEFAULT_CAMERA_DISTANCE;
  237.  
  238. /*
  239.  * Current angle of tilt applied to the camera.
  240.  */
  241. static RwReal CameraTilt = CREAL(0.0);
  242.  
  243. #ifdef    USERDRAW_LABELS
  244.  
  245. /*
  246.  * The font to be used in rendering user draws.
  247.  */
  248. static HFONT Font = (HFONT) 0;
  249.  
  250. #endif /* USERDRAW_LABELS */
  251.  
  252. /*
  253.  * This flag indicates whether the 3D components of the application
  254.  * have been successfully initialized as yet. It used to gaurd
  255.  * the message loop handler functions from being invoked before the
  256.  * 3D stuff is successfully initialized.
  257.  */
  258. static BOOL ThreeDInitialized = FALSE;
  259.  
  260. static RwMaterial *Material;
  261. static int PaintMode = 0;
  262. static int LeftButtonDown;
  263. static HWND MainWindow;
  264. static HWND But1;
  265. static HWND But2;
  266. static HWND PaintWnd;
  267.  
  268. /**********************************************************************
  269.  *
  270.  * Forward functions.
  271.  *
  272.  **********************************************************************/
  273.  
  274. extern LRESULT CALLBACK
  275.   MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
  276. extern LRESULT CALLBACK
  277.   ToolWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
  278.  
  279. /**********************************************************************/
  280.  
  281. /*
  282.  * Perform any necessary MS Windows application initialization. Basically,
  283.  * this means registering the window class for this application.
  284.  */
  285. static BOOL
  286. InitApplication(HANDLE instance)
  287. {
  288.     WNDCLASS windowClass;
  289.  
  290.     windowClass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
  291.     windowClass.lpfnWndProc = (WNDPROC) MainWndProc;
  292.     windowClass.cbClsExtra = 0;
  293.     windowClass.cbWndExtra = 0;
  294.     windowClass.hInstance = instance;
  295.     windowClass.hIcon = LoadIcon(AppInstance, MAKEINTRESOURCE(RW_ICON));
  296.     windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  297.     windowClass.hbrBackground = NULL;
  298.     windowClass.lpszMenuName = "MENU_1";
  299.     windowClass.lpszClassName = RWPAINT_CLASS_NAME;
  300.  
  301.     if (RegisterClass(&windowClass) == 0)
  302.         return FALSE;
  303.  
  304.     windowClass.style = 0;
  305.     windowClass.lpfnWndProc = (WNDPROC) ToolWndProc;
  306.     windowClass.cbClsExtra = 0;
  307.     windowClass.cbWndExtra = 0;
  308.     windowClass.hInstance = instance;
  309.     windowClass.hIcon = NULL;
  310.     windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  311.     windowClass.hbrBackground = GetStockObject(LTGRAY_BRUSH);
  312.     windowClass.lpszMenuName = NULL;
  313.     windowClass.lpszClassName = RWPAINT_TOOL_CLASS_NAME;
  314.  
  315.     if (RegisterClass(&windowClass) == 0)
  316.         return FALSE;
  317.  
  318.     return TRUE;
  319. }
  320.  
  321. /**********************************************************************/
  322.  
  323. /*
  324.  * Perform any necessary initalization for this instance of the
  325.  * application. This simply means creating the applications main window.
  326.  */
  327. static HWND
  328. InitInstance(HANDLE instance)
  329. {
  330.     /*
  331.      * Create the MS Window's window instance for this application. The
  332.      * initial window size is given by DEFAULT_WINDOW_WIDTH and
  333.      * DEFAULT_WINDOW_HEIGHT. However, when we create the RenderWare
  334.      * camera in Init3D() we specify a maximum camera viewport which is
  335.      * the same size as the screen rather than the window. This is to
  336.      * allow the window to be maximed.
  337.      */
  338.     return CreateWindow(RWPAINT_CLASS_NAME, "RenderWare Object Painter",
  339.                         WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  340.                         CW_USEDEFAULT, CW_USEDEFAULT,
  341.                         DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT,
  342.                         NULL, NULL, instance, NULL);
  343. }
  344.  
  345. /**********************************************************************/
  346.  
  347. /*
  348.  * Perform any necessary initalization for child windows (toolbar)
  349.  */
  350. static HWND
  351. InitChildren(HINSTANCE instance, HWND parent)
  352. {
  353.     HWND window;
  354.  
  355.     window = CreateWindow(RWPAINT_TOOL_CLASS_NAME, "",
  356.                           WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
  357.                           0, 0, DEFAULT_WINDOW_WIDTH, TOOL_HEIGHT, parent,
  358.                           NULL, instance, NULL);
  359.  
  360.     But1 = CreateWindow("button", "Move",
  361.                         WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
  362.               BUTTON_OFFSET_X, BUTTON_OFFSET_Y, BUTTON_WIDTH, BUTTON_HEIGHT,
  363.                         window, (HMENU) 1, instance, NULL);
  364.  
  365.     But2 = CreateWindow("button", "Paint",
  366.                         WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
  367.                         BUTTON_OFFSET_X + BUTTON_SPACE + BUTTON_WIDTH,
  368.                         BUTTON_OFFSET_Y, BUTTON_WIDTH, BUTTON_HEIGHT,
  369.                         window, (HMENU) 2, instance, NULL);
  370.  
  371.     PostMessage(But1, BM_SETSTATE, 1, 0L);
  372.     PostMessage(But2, BM_SETSTATE, 0, 0L);
  373.  
  374.     return window;
  375. }
  376.  
  377. /**********************************************************************/
  378.  
  379. #ifdef USERDRAW_LABELS
  380.  
  381. /*
  382.  * This function is the call back function for the user draw used to
  383.  * label the current selected clump. It demonstrates user draws and
  384.  * the use of MS Windows GDI facilities such as TrueType fonts with
  385.  * RenderWare.
  386.  */
  387. static RwUserDraw *
  388. UserDrawCallBack(RwUserDraw * userDraw, void *camImage, RwRect * rect,
  389.                  void *data)
  390. {
  391.     HDC devContext;
  392.     HPEN pen;
  393.     HPEN oldPen;
  394.     HBRUSH brush;
  395.     HBRUSH oldBrush;
  396.     HFONT oldFont;
  397.     int oldMode;
  398.     SIZE size;
  399.  
  400.     /*
  401.      * If we have no user data, then there is no string to draw, so
  402.      * exit.
  403.      */
  404.     if (!data)
  405.         return (userDraw);
  406.  
  407.     /*
  408.      * If we are doing DIB rendering then we can't use GDI so exit.
  409.      */
  410.  
  411.     if (UsingDib)
  412.         return (userDraw);
  413.  
  414.     /*
  415.      * Under MS Windows, camImage is, in fact, an HDC. I will make this
  416.      * explicit by assigning it to devContext.
  417.      */
  418.     devContext = (HDC) camImage;
  419.  
  420.     /*
  421.      * Render the background of the user draw.
  422.      */
  423.     pen = GetStockObject(NULL_PEN);
  424.     brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  425.     oldPen = SelectObject(devContext, pen);
  426.     oldBrush = SelectObject(devContext, brush);
  427.     Rectangle(devContext, (int) rect->x, (int) rect->y, (int) (rect->x + rect->w),
  428.               (int) (rect->y + rect->h));
  429.     SelectObject(devContext, oldBrush);
  430.     SelectObject(devContext, oldPen);
  431.     DeleteObject(brush);
  432.  
  433.     /*
  434.      * Outline it in the traditional, MS Windows way.
  435.      */
  436.     pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  437.     oldPen = SelectObject(devContext, pen);
  438.     MoveToEx(devContext, (int) rect->x, (int) (rect->y + (rect->h - 1)), NULL);
  439.     LineTo(devContext, (int) rect->x, (int) rect->y);
  440.     LineTo(devContext, (int) (rect->x + rect->w), (int) rect->y);
  441.     SelectObject(devContext, oldPen);
  442.     DeleteObject(pen);
  443.     pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  444.     oldPen = SelectObject(devContext, pen);
  445.     MoveToEx(devContext, (int) rect->x, (int) (rect->y + (rect->h - 1)), NULL);
  446.     LineTo(devContext, (int) (rect->x + (rect->w - 1)), (int) (rect->y + (rect->h - 1)));
  447.     LineTo(devContext, (int) (rect->x + (rect->w - 1)), (int) rect->y);
  448.     SelectObject(devContext, oldPen);
  449.     DeleteObject(pen);
  450.  
  451.     /*
  452.      * If we found the font we wanted, select it in, otherwise simply
  453.      * use the default font.
  454.      */
  455.     if (Font)
  456.         oldFont = SelectObject(devContext, Font);
  457.  
  458.     oldMode = SetBkMode(devContext, TRANSPARENT);
  459.  
  460.     /*
  461.      * Compute the text extent so we can centre it in the user draw
  462.      * rect.
  463.      */
  464.     GetTextExtentPoint(devContext, data, strlen(data), &size);
  465.     TextOut(devContext, (int) (rect->x + ((rect->w / 2) - (size.cx / 2)))
  466.             ,(int) (rect->y + ((rect->h / 2) - (size.cy / 2))),
  467.             data, strlen(data));
  468.     SetBkMode(devContext, oldMode);
  469.  
  470.     /*
  471.      * Select the original font back, if necessary.
  472.      */
  473.     if (Font)
  474.         SelectObject(devContext, oldFont);
  475.     return (userDraw);
  476. }
  477.  
  478. #endif
  479.  
  480. /**********************************************************************/
  481.  
  482. /*
  483.  * This function initializes the 3D (i.e. RenderWare) components of the
  484.  * application. This function opens the RenderWare library, creates a
  485.  * camera, a scene, a light, a matrix for spinning and loads an initial
  486.  * clump.
  487.  */
  488. static BOOL
  489. Init3D(HWND window)
  490. {
  491.     char windowText[128];
  492.     char version[30];
  493.  
  494. #ifdef    USERDRAW_LABELS
  495.     HDC dc;
  496.     LOGFONT logFont;
  497.     HFONT oldFont;
  498.     SIZE size;
  499.  
  500. #endif /* USERDRAW_LABELS */
  501.  
  502.     /*
  503.      * Attempt to open (and initialize) the RenderWare library.
  504.      * Explicitly specifying the MS Windows driver. The MS Windows
  505.      * device driver requires the application's instance handle so
  506.      * this is passed in as the device specific parameter to RwOpen().
  507.      */
  508.     if (!RwOpen("MSWindows", NULL))
  509.     {
  510.         if (RwGetError() == E_RW_NOMEM)
  511.         {
  512.             MessageBox(window,
  513.                        "Insufficient memory to open the RenderWare library",
  514.                     ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  515.         }
  516.         else
  517.         {
  518.             MessageBox(window, "Error opening the RenderWare library",
  519.                     ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  520.         }
  521.         return FALSE;
  522.     }
  523.  
  524.     /*
  525.      * Get the version number for display in the title bar.
  526.      */
  527.  
  528.     RwGetSystemInfo(rwVERSIONSTRING, version, sizeof (version));
  529.     wsprintf(windowText, "RenderWare %s Object Painter", version);
  530.     SetWindowText(window, windowText);
  531.  
  532.     /*
  533.      * Determine if userdraw functinality is available and set the global flag
  534.      * accordingly
  535.      */
  536. #ifdef USERDRAW_LABELS
  537.  
  538.     RwGetDeviceInfo(rwWINIMAGEISDIB, &UsingDib, sizeof (UsingDib));
  539.  
  540. #endif
  541.  
  542.     /*
  543.      * Create the camera which will be used for rendering. The initial window
  544.      * size the application will create is given by DEFAULT_WINDOW_WIDTH and
  545.      * DEFAULT_WINDOW_HEIGHT which is currently 400x400. However, the camera
  546.      * will be created with a maximum viewport size which is the same size
  547.      * as the screen. This will allow the window to be maximized correctly.
  548.      *
  549.      * Also, under MS Windows, RwCreateCamera() needs a device context for
  550.      * the window which will be used as the destination by RwShowCameraImage()
  551.      * So a device context handle for the output window is passed in.
  552.      */
  553.     Camera = RwCreateCamera(MAXIMUM_WINDOW_WIDTH, MAXIMUM_WINDOW_HEIGHT, NULL);
  554.     if (!Camera)
  555.     {
  556.         if (RwGetError() == E_RW_NOMEM)
  557.         {
  558.             MessageBox(window,
  559.                        "Insufficient memory to create the RenderWare camera",
  560.                     ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  561.         }
  562.         else
  563.         {
  564.             MessageBox(window, "Error creating the RenderWare camera",
  565.                     ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  566.         }
  567.         RwClose();
  568.         return FALSE;
  569.     }
  570.  
  571.     RwSetCameraViewwindow(Camera, CREAL(0.2), CREAL(0.2));
  572.  
  573.     /*
  574.      * Set the camera's background color to black.
  575.      */
  576.     RwSetCameraBackColor(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.8));
  577.  
  578.     /*
  579.      * By default, the camera lies on the X-Z plane and points down Z
  580.      * into the screen. We shall retain the camera's orientation, but move
  581.      * the camera DEFAULT_CAMERA_DISTANCE units down Z away from the screen.
  582.      */
  583.     RwTiltCamera(Camera, CameraTilt);
  584.     RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  585.  
  586.     /*
  587.      * Create a scene which will contain the clumps to be rendered and the
  588.      * light or lights illuminating those clumps . In this very simple
  589.      * application it would be perfectly acceptable to use the default scene
  590.      * (as returned by RwDefaultScene()) for rendering. However, it is good
  591.      * practice to always create a scene which will be used for your rendering
  592.      * and only use the default scene as a bag for currently unused clumps and
  593.      * lights.
  594.      */
  595.     Scene = RwCreateScene();
  596.     if (!Scene)
  597.     {
  598.         RwDestroyCamera(Camera);
  599.         RwClose();
  600.         MessageBox(window, "Error creating the RenderWare scene",
  601.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  602.         return FALSE;
  603.     }
  604.  
  605.     /*
  606.      * Our scene will be illuminated by a directional light. The illumination
  607.      * vector of the light is (0.1, -1.0, -0.5) and its brightness will be 1.0.
  608.      */
  609.     Light = RwCreateLight(rwDIRECTIONAL, CREAL(0.1), CREAL(-1.0), CREAL(-0.5),
  610.                           CREAL(1.0));
  611.     if (!Light)
  612.     {
  613.         RwDestroyScene(Scene);
  614.         RwDestroyCamera(Camera);
  615.         RwClose();
  616.         MessageBox(window, "Error creating the RenderWare light",
  617.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  618.         return FALSE;
  619.     }
  620.  
  621.     /*
  622.      * Add the new light to our scene.
  623.      */
  624.     RwAddLightToScene(Scene, Light);
  625.  
  626.     /*
  627.      * Create the spin matrix.
  628.      */
  629.     SpinMatrix = RwCreateMatrix();
  630.     if (!SpinMatrix)
  631.     {
  632.         RwDestroyScene(Scene);
  633.         RwDestroyCamera(Camera);
  634.         RwClose();
  635.         MessageBox(window, "Error creating the RenderWare matrix",
  636.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  637.         return FALSE;
  638.     }
  639.  
  640.     Material = RwCreateMaterial();
  641.     if (Material == NULL)
  642.     {
  643.         RwDestroyMatrix(SpinMatrix);
  644.         RwDestroyScene(Scene);
  645.         RwDestroyCamera(Camera);
  646.         RwClose();
  647.         MessageBox(window, "Error creating the RenderWare material",
  648.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  649.         return FALSE;
  650.     }
  651.  
  652. #ifdef USERDRAW_LABELS
  653.  
  654.     /*
  655.      * Get the font to be used for rendering the user draws. If we can't
  656.      * find the font, we simply ignore it as the rest of WinView is
  657.      * written to use the default font if Font is 0.
  658.      */
  659.     memset(&logFont, 0, sizeof (LOGFONT));
  660.     logFont.lfHeight = 24;
  661.     strcpy(logFont.lfFaceName, "Arial Italic");
  662.     Font = CreateFontIndirect(&logFont);
  663.  
  664.     /*
  665.      * We need to specify the size of the user draw which will label
  666.      * the currently selected clump. The label will be the leaf of
  667.      * the clump's filename which, under DOS, will be a maximum of
  668.      * eight characters. So the extent of a (wide) eight character
  669.      * string is computed with the desired font in place. The extent
  670.      * plus some padding is the size of the user draw.
  671.      */
  672.     dc = GetDC(window);
  673.     if (Font)
  674.         oldFont = SelectObject(dc, Font);
  675.     GetTextExtentPoint(dc, "WWWWWWWW", 8, &size);
  676.     if (Font)
  677.         SelectObject(dc, oldFont);
  678.     ReleaseDC(window, dc);
  679.  
  680.     /*
  681.      * Create the single user draw instance we will use to label the
  682.      * currently selected clump. We will simply add this user draw
  683.      * to a clump as they become selected. The size of the user draw
  684.      * will be based on the string extent computed above. The user
  685.      * draw will be centred just below the clump, to achieve this,
  686.      * the clump's alignment is rwALIGNTOP and the parent's alignment
  687.      * is rwALIGNBOTTOM (i.e. the top of the user draw is aligned with
  688.      * the bottom of the clump's bounding box).
  689.      */
  690.     UserDraw = RwCreateUserDraw(rwBBOXALIGN, rwALIGNTOP, 0, 5,
  691.                                 size.cx + 5, size.cy + 5,
  692.                                 UserDrawCallBack);
  693.     if (!UserDraw)
  694.     {
  695.         RwDestroyScene(Scene);
  696.         RwDestroyCamera(Camera);
  697.         RwClose();
  698.         MessageBox(window, "Error creating the RenderWare UserDraw",
  699.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  700.         return FALSE;
  701.     }
  702.     RwSetUserDrawParentAlignment(UserDraw, rwALIGNBOTTOM);
  703.  
  704. #endif
  705.  
  706.     /*
  707.      * All the 3D components are now successfully initialized, so 
  708.      * work can begin...
  709.      */
  710.     ThreeDInitialized = TRUE;
  711.  
  712.     return TRUE;
  713. }
  714.  
  715. /**********************************************************************/
  716.  
  717. #ifdef    USERDRAW_LABELS
  718.  
  719. /*
  720.  * This function creates a user data structure for a newely loaded
  721.  * clump read from the file with the given name.
  722.  */
  723. static RwClump *
  724. CreateClumpUserData(RwClump * clump, char *fileName)
  725. {
  726.     char name[_MAX_FNAME];
  727.     CLUMP_DATA *clumpData;
  728.  
  729.     /*
  730.      * Get the leaf of the filename of the clump and store that
  731.      * away. The name is used to label clumps with user draws
  732.      * when they are selected.
  733.      */
  734.     _splitpath(fileName, NULL, NULL, name, NULL);
  735.  
  736.     /*
  737.      * Allocate store for the leaf of the filename.
  738.      */
  739.     clumpData = (CLUMP_DATA *) malloc(sizeof (CLUMP_DATA));
  740.     if (clumpData == NULL)
  741.         return NULL;
  742.     clumpData->UserDraw = strdup(name);
  743.     clumpData->FullName = strdup(fileName);
  744.  
  745.     /*
  746.      * Attach the user data structure to the clump.
  747.      */
  748.     RwSetClumpData(clump, clumpData);
  749.  
  750.     return clump;
  751. }
  752.  
  753. #endif /* USERDRAW_LABELS */
  754.  
  755. /**********************************************************************/
  756.  
  757. #ifdef    USERDRAW_LABELS
  758.  
  759. /*
  760.  * This function destroys a clump user data structure.
  761.  */
  762. static RwClump *
  763. DestroyClumpUserData(RwClump * clump)
  764. {
  765.     CLUMP_DATA *user;
  766.  
  767.     user = RwGetClumpData(clump);
  768.     if (user != NULL)
  769.     {
  770.         free(user->UserDraw);
  771.         free(user->FullName);
  772.         free(user);
  773.         RwSetClumpData(clump, NULL);
  774.     }
  775.     return clump;
  776. }
  777.  
  778. #endif /* USERDRAW_LABELS */
  779.  
  780. /**********************************************************************/
  781.  
  782. /*
  783.  * This function shuts the 3D (i.e. RenderWare) components of the library
  784.  * down in a polite fashion.
  785.  */
  786. static void
  787. TidyUp3D()
  788. {
  789.  
  790. #ifdef USERDRAW_LABELS
  791.     /*
  792.      * Destroy the user draw. If the user draw has been added to a clump
  793.      * when the clump is destroyed then the user draw will also be
  794.      * destroyed. But here, the user draw might not currently be added
  795.      * to a clump so we destroy it explicitly.
  796.      */
  797.     RwDestroyUserDraw(UserDraw);
  798.  
  799.     /*
  800.      * Destroy the font.
  801.      */
  802.     if (Font)
  803.         DeleteObject(Font);
  804.  
  805.     /*
  806.      * Destroying the scene will destroy all the clump in the scene
  807.      * automatically. However, the user data of the clump's will not be
  808.      * destroy, so destroy all the user data now.
  809.      */
  810.     RwForAllClumpsInScene(Scene, DestroyClumpUserData);
  811. #endif
  812.  
  813.     /*
  814.      * Destroy the spin matrix.
  815.      */
  816.     RwDestroyMatrix(SpinMatrix);
  817.  
  818.     /*
  819.      * Destroy the scene. This will destroy the contents of the scene,
  820.      * i.e. any clumps and lights in that scene. In this case destroying
  821.      * the scene will destroy the light we created in Init3D, and any
  822.      * clumps we have loaded and not already destroyed.
  823.      */
  824.     RwDestroyScene(Scene);
  825.  
  826.     /*
  827.      * Destroy the camera.
  828.      */
  829.     RwDestroyCamera(Camera);
  830.  
  831.     /*
  832.      * Close the library. This will free up any internal resources and
  833.      * textures loaded.
  834.      */
  835.     RwClose();
  836. }
  837.  
  838. /**********************************************************************/
  839.  
  840. /*
  841.  * Attempt to load the file with the given file name as either a clump
  842.  * or a texture (in which case a decal clump is created and returned).
  843.  */
  844. static RwClump *
  845. LoadClumpOrTexture(HWND window, char *fileName)
  846. {
  847.     RwClump *clump;
  848.     RwTexture *texture;
  849.     char textureName[128];
  850.     char buffer[128];
  851.     HCURSOR oldCursor;
  852.  
  853.     /*
  854.      * Attempt to load the file as a script file.
  855.      */
  856.     oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  857.     clump = RwReadShape(fileName);
  858.     SetCursor(oldCursor);
  859.     if (!clump)
  860.     {
  861.         /*
  862.          * The load failed for some reason, this could be because of
  863.          * errors in the script file, insufficent memory, or the file
  864.          * containing garbage. We will now examine the error code
  865.          * returned to decide whether there was a script file error
  866.          * (in which case an error will be issued to the user), whether
  867.          * memory was exahusted (a different error is issued to the user),
  868.          * or the file contained garbage (in which case we will attempt
  869.          * to load the file as a texture instead of a script).
  870.          */
  871.         switch (RwGetError())
  872.         {
  873.             case E_RW_NOMEM:
  874.                 /*
  875.                  * Ran out of memory...
  876.                  */
  877.                 sprintf(buffer, "Insufficient memory to load script %s",
  878.                         fileName);
  879.                 MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  880.                            MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  881.                 return NULL;
  882.  
  883.             case E_RW_NOFILE:
  884.             case E_RW_BADOPEN:
  885.             case E_RW_RSPARSE:
  886.             case E_RW_RSREAD:
  887.             case E_RW_READ:
  888.                 /*
  889.                  * We will (somewhat optimistically) assume that if we
  890.                  * had a read or parse error on the stream then the 
  891.                  * file is not a script file but a texture instead.
  892.                  * So attempt to load a texture. Note, as we use
  893.                  * RwGetNamedTexture() to get the texture, the texture
  894.                  * will not be reread if it has already been loaded into
  895.                  * RenderWare.
  896.                  */
  897.                 oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  898.                 texture = RwGetNamedTexture(fileName);
  899.                 SetCursor(oldCursor);
  900.                 if (texture)
  901.                 {
  902.                     /*
  903.                      * The file was indeed a texture, so attempt to
  904.                      * create a sprite clump using this texture.
  905.                      */
  906.                     clump = RwCreateSprite(texture);
  907.                     if (!clump)
  908.                     {
  909.                         RwGetTextureName(texture, textureName, sizeof (textureName));
  910.                         sprintf(buffer,
  911.                                 "Error creating a sprite from texture %s", textureName);
  912.  
  913.                         MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  914.                                  MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  915.                         return NULL;
  916.                     }
  917.                 }
  918.                 else
  919.                 {
  920.                     /*
  921.                      * The texture load failed, issue an error message
  922.                      * giving the general nature of the problem.
  923.                      */
  924.                     switch (RwGetError())
  925.                     {
  926.                         case E_RW_NOFILE:
  927.                         case E_RW_BADOPEN:
  928.                             /*
  929.                              * Could not open the file...
  930.                              */
  931.                             sprintf(buffer, "Error opening %s", fileName);
  932.                             MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  933.                                  MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  934.                             return NULL;
  935.  
  936.                         case E_RW_NOMEM:
  937.                             /*
  938.                              * Ran out of memory...
  939.                              */
  940.                             sprintf(buffer,
  941.                                     "Insufficient memory to load texture %s",
  942.                                     fileName);
  943.                             MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  944.                                  MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  945.                             return NULL;
  946.  
  947.                         default:
  948.                             /*
  949.                              * We will no enumerate all the errors, so simply
  950.                              * issue a general failure report.
  951.                              */
  952.                             sprintf(buffer,
  953.                                     "Error reading the file %s",
  954.                                     fileName);
  955.                             MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  956.                                  MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  957.                             return NULL;
  958.                     }
  959.                 }
  960.                 break;
  961.  
  962.             default:
  963.                 /*
  964.                  * On any other error we will assume there is an error in
  965.                  * the script file.
  966.                  */
  967.                 sprintf(buffer,
  968.                         "Error reading the script file %s",
  969.                         fileName);
  970.                 MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  971.                            MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  972.                 return NULL;
  973.         }
  974.     }
  975.     if (clump && PaintWnd != NULL)  /* ensure m.e is up to date */
  976.         MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
  977.  
  978. #ifdef    USERDRAW_LABELS
  979.     /*
  980.      * Attach the leaf name of the file the clump was read from
  981.      * to the clump as user data. This is used as the label to
  982.      * render with the user draw.
  983.      */
  984.     if (!CreateClumpUserData(clump, fileName))
  985.     {
  986.         RwDestroyClump(clump);
  987.         sprintf(buffer, "Insufficient memory to create clump %s", fileName);
  988.         MessageBox(window, buffer, ERROR_DIALOG_TITLE,
  989.                    MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
  990.         return NULL;
  991.     }
  992. #endif /* USERDRAW_LABELS */
  993.  
  994.     /*
  995.      * Return the resulting clump.
  996.      */
  997.     return clump;
  998. }
  999.  
  1000. /**********************************************************************/
  1001.  
  1002. /*
  1003.  * Parse the names of any rwx files on the command line, read those
  1004.  * files and add them to out scene. This allows WinView to be associated
  1005.  * with rwx files in the file manager and be started automatically when
  1006.  * an rwx file is double clicked.
  1007.  */
  1008. static BOOL
  1009. ReadFromCommandLine(HWND window, char *cmdLine)
  1010. {
  1011.     char *s1;
  1012.     char *s2;
  1013.     char fileName[_MAX_PATH];
  1014.     RwClump *clump;
  1015.  
  1016.     s1 = &cmdLine[0];
  1017.     s2 = &fileName[0];
  1018.     while (*s1)
  1019.     {
  1020.         /*
  1021.          * Extract one file name from the list.
  1022.          */
  1023.         while (*s1 && (*s1 != ' '))
  1024.             *s2++ = *s1++;
  1025.         *s2 = '\0';
  1026.  
  1027.         if (fileName[0])
  1028.         {
  1029.             /*
  1030.              * Attempt to load a clump or texture with that name. It is
  1031.              * not necessary to issue an error message as LoadClumpOrTexture()
  1032.              * will do that for us.
  1033.              */
  1034.             clump = LoadClumpOrTexture(window, fileName);
  1035.             if (clump)
  1036.                 RwAddClumpToScene(Scene, clump);
  1037.         }
  1038.  
  1039.         s2 = &fileName[0];
  1040.     }
  1041.     return TRUE;
  1042. }
  1043.  
  1044. /**********************************************************************/
  1045.  
  1046. static void
  1047. HandleGetMinMaxInfo(MINMAXINFO FAR * minmaxinfo)
  1048. {
  1049.     minmaxinfo->ptMaxSize.x = MAXIMUM_WINDOW_WIDTH;
  1050.     minmaxinfo->ptMaxSize.y = MAXIMUM_WINDOW_HEIGHT;
  1051.     minmaxinfo->ptMaxTrackSize.x = MAXIMUM_WINDOW_WIDTH;
  1052.     minmaxinfo->ptMaxTrackSize.y = MAXIMUM_WINDOW_HEIGHT;
  1053. }
  1054.  
  1055. /**********************************************************************/
  1056.  
  1057. static void
  1058. HandleSize(HWND window, int width, int height, HWND ToolBar)
  1059. {
  1060.     /*
  1061.      * The window has been resized, therefore, it is necessary to
  1062.      * to modify the camera's viewport to be the same size as the
  1063.      * client area of the window.
  1064.      */
  1065.     RwSetCameraViewport(Camera, 0, TOOL_HEIGHT, width, height - TOOL_HEIGHT);
  1066.  
  1067.     /*
  1068.      * Keep toolbar the correct size
  1069.      */
  1070.     MoveWindow(ToolBar, 0, 0, width, TOOL_HEIGHT, 1);
  1071.     /*
  1072.      * Ensure the entire window is repainted. Note, we stop MS Windows
  1073.      * repainting the window as RenderWare paint the entire background.
  1074.      */
  1075.     InvalidateRect(window, NULL, FALSE);
  1076. }
  1077.  
  1078. /**********************************************************************/
  1079.  
  1080. /*
  1081.  * This functions handles the left mouse button going down. Its main
  1082.  * job is to determine the kind of action to be taken when the mouse
  1083.  * moves, such as spinning a clump, or panning the camera. This involves
  1084.  * examining the virtual keys that were depressed when the mouse button
  1085.  * went down and attempting to pick a clump under the mouse pointer
  1086.  * position when the mouse went down.
  1087.  */
  1088. static void
  1089. HandleLeftButtonDown(HWND window, int x, int y, WPARAM vKeys)
  1090. {
  1091.     RwPickRecord pick;
  1092.     POINT pos;
  1093.  
  1094.     /*
  1095.      * If the left button is depressed anywhere in the client area of the
  1096.      * window then the animated spin is cancelled.
  1097.      */
  1098.     AnimMode = ANoAction;
  1099.     LeftButtonDown = 1;
  1100.     SetCapture(window);
  1101.  
  1102.     /*
  1103.      * If the control key is not depressed, then the mouse move action
  1104.      * is based on whether an object is picked or not. Therefore,
  1105.      * attempt to pick an object in the scene.
  1106.      */
  1107.     if (RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
  1108.     {
  1109.         switch (pick.type)
  1110.         {
  1111.             case rwNAPICKOBJECT:
  1112.                 /*
  1113.                  * Clicked on the background, so there is no drag action,
  1114.                  * no picked clump and any animation action should be cancelled.
  1115.                  */
  1116.                 MouseMoveMode = MMNoAction;
  1117.                 PickedClump = NULL;
  1118.                 break;
  1119.  
  1120.             case rwPICKCLUMP:
  1121.                 /*
  1122.                  * If a clump was picked and both the shift and control
  1123.                  * virtual keys were depressed then we will destroy the
  1124.                  * clump (and hence remove it from the scene).
  1125.                  */
  1126.                 if ((vKeys & (MK_CONTROL | MK_SHIFT)) ==
  1127.                     (MK_CONTROL | MK_SHIFT))
  1128.                 {
  1129.  
  1130. #ifdef USERDRAW_LABELS
  1131.                     /*
  1132.                        * If this clump owns the user draw, then we must
  1133.                        * remove the user draw from the clump before it
  1134.                        * is destroyed to prevent it being destroyed also.
  1135.                      */
  1136.                     if (RwGetUserDrawOwner(UserDraw) == pick.object.clump.clump)
  1137.                         RwRemoveUserDrawFromClump(UserDraw);
  1138.  
  1139.                     /*
  1140.                      * Destroy the user data of the clump (if it has any) as user
  1141.                      * data is not destroyed automatically. The user data is the
  1142.                      * leaf file name of the clump added by LoadClumpOrTexture().
  1143.                      */
  1144.                     DestroyClumpUserData(pick.object.clump.clump);
  1145. #endif
  1146.  
  1147.                     /*
  1148.                      * Destroy the clump (and remove it from the scene).
  1149.                      */
  1150.                     RwDestroyClump(pick.object.clump.clump);
  1151.  
  1152.                     /*
  1153.                      * Force a redraw of the scene by invalidating
  1154.                      * the window's client area. Prevent MS Windows
  1155.                      * from erasing the window's client area as RenderWare
  1156.                      * will erase the background of the camera.
  1157.                      */
  1158.                     InvalidateRect(window, NULL, FALSE);
  1159.  
  1160.                     /*
  1161.                      * The clump has been destroyed so there is no mouse
  1162.                      * move action, no picked clump and the animation action
  1163.                      * should be cancelled.
  1164.                      */
  1165.                     MouseMoveMode = MMNoAction;
  1166.                     AnimMode = ANoAction;
  1167.                     PickedClump = NULL;
  1168.                 }
  1169.                 else
  1170.                 {
  1171.                     /*
  1172.                      * A clump was picked, so remember which clump was picked
  1173.                      * in order that it may be manipulated later when the
  1174.                      * mouse moves.
  1175.                      */
  1176.                     PickedClump = pick.object.clump.clump;
  1177.  
  1178.                     /*
  1179.                      * Determine the exact nature of the manipulation of
  1180.                      * the clump on mouse move by examining the virtual key
  1181.                      * state. If the shift key was depressed, then drag
  1182.                      * the clump, otherwise, spin it.
  1183.                      */
  1184.                     if (PaintMode == 0)
  1185.                     {
  1186.                         if (vKeys & MK_SHIFT)
  1187.                             MouseMoveMode = MMDragClump;
  1188.                         else if (vKeys & MK_CONTROL)
  1189.                             MouseMoveMode = MMDragClumpInZ;
  1190.                         else
  1191.                             MouseMoveMode = MMSpinClump;
  1192.  
  1193. #ifdef USERDRAW_LABELS
  1194.                         /*
  1195.                            * Add the user draw to this (the now selected clump) and
  1196.                            * set the user draw's data to the root clump's data (which
  1197.                            * is the leaf file name of the clump). The leaf name is
  1198.                            * attached to the root clump, rather than every clump in
  1199.                            * the hierarchy, so avoid the necessity of replicating
  1200.                            * the string in many different locations.
  1201.                          */
  1202.                         ClumpWithUserDraw = PickedClump;
  1203.                         RwAddUserDrawToClump(PickedClump, UserDraw);
  1204.                         RwSetUserDrawData(UserDraw, ((CLUMP_DATA *)
  1205.                               RwGetClumpData(RwGetClumpRoot(PickedClump)))->
  1206.                                           UserDraw);
  1207. #endif
  1208.                     }
  1209.                     else
  1210.                     {
  1211.                         if (vKeys & MK_SHIFT)
  1212.                         {
  1213.                             RwSphericalTexturizeClump(pick.object.clump.clump);
  1214.                             RwForAllPolygonsInClumpPointer(pick.object.clump.clump,
  1215.                               (RwPolygon3dFuncPointer) RwSetPolygonMaterial,
  1216.                                              RwDuplicateMaterial(Material));
  1217.                         }
  1218.                         else
  1219.                         {
  1220.                             RwSetPolygonMaterial(pick.object.clump.polygon, RwDuplicateMaterial(Material));
  1221.                         }
  1222.                     }
  1223.                 }
  1224.                 break;
  1225.         }
  1226.     }
  1227.  
  1228.     /*
  1229.      * If any form of action is to be taken on mouse move, remember the
  1230.      * the current x and y position of the mouse and capture future
  1231.      * mouse movement.
  1232.      */
  1233.     if (MouseMoveMode != MMNoAction)
  1234.     {
  1235.         pos.x = x;
  1236.         pos.y = y;
  1237.         ClientToScreen(window, &pos);
  1238.         LastX = pos.x;
  1239.         LastY = pos.y;
  1240.     }
  1241. }
  1242.  
  1243. /**********************************************************************/
  1244.  
  1245. /*
  1246.  * This functions handles the right mouse button going down. Its main
  1247.  * job is to determine the kind of action to be taken when the mouse
  1248.  * moves, such as stopping spinning a clump, or panning the camera.
  1249.  */
  1250. static void
  1251. HandleRightButtonDown(HWND window, int x, int y, WPARAM vKeys)
  1252. {
  1253.     POINT pos;
  1254.  
  1255.     if (vKeys & MK_CONTROL)
  1256.     {
  1257.         MouseMoveMode = MMPanLight;
  1258.     }
  1259.     else if (vKeys & MK_SHIFT)
  1260.     {
  1261.         MouseMoveMode = MMTiltCamera;
  1262.     }
  1263.     else
  1264.     {
  1265.         MouseMoveMode = MMPanAndZoomCamera;
  1266.     }
  1267.  
  1268.     /*
  1269.      * If any form of action is to be taken on mouse move, remember the
  1270.      * the current x and y position of the mouse and capture future
  1271.      * mouse movement.
  1272.      */
  1273.     if (MouseMoveMode != MMNoAction)
  1274.     {
  1275.         SetCapture(window);
  1276.         pos.x = x;
  1277.         pos.y = y;
  1278.         ClientToScreen(window, &pos);
  1279.         LastX = pos.x;
  1280.         LastY = pos.y;
  1281.     }
  1282. }
  1283.  
  1284. /**********************************************************************/
  1285.  
  1286. /*
  1287.  * Handle a movement of the mouse. If a previous left mouse button
  1288.  * down event has set a mouse move mode then this function will take
  1289.  * the necessary actions. For example, pan and zooming the camera,
  1290.  * panning the light, dragging or spinning a clump etc.
  1291.  */
  1292. static void
  1293. HandleMouseMove(HWND window, int x, int y)
  1294. {
  1295.     HDC dc;
  1296.     RwClump *parent;
  1297.     RwMatrix4d *tmpMatrix;
  1298.     RwMatrix4d *worldToLocal;
  1299.     RwV3d up;
  1300.     RwV3d right;
  1301.     RwV3d at;
  1302.     RwReal xDelta;
  1303.     RwReal yDelta;
  1304.     RwReal xAngle;
  1305.     RwReal yAngle;
  1306.     POINT pos;
  1307.     RwPickRecord pick;
  1308.  
  1309.     pos.x = x;
  1310.     pos.y = y;
  1311.     ClientToScreen(window, &pos);
  1312.  
  1313.     if (PaintMode == 1 && LeftButtonDown && RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
  1314.     {
  1315.         if (pick.type == rwPICKCLUMP)
  1316.         {
  1317.             RwSetPolygonMaterial(pick.object.clump.polygon, RwDuplicateMaterial(Material));
  1318.             return;
  1319.         }
  1320.     }
  1321.     /*
  1322.      * MouseMoveMode (previously set by HandleLeftButtonDown()) tells us what
  1323.      * kind of action to perform.
  1324.      */
  1325.     switch (MouseMoveMode)
  1326.     {
  1327.         case MMNoAction:
  1328.             break;
  1329.  
  1330.         case MMPanAndZoomCamera:
  1331.             /*
  1332.              * We are panning and zooming the camera. Movement of the
  1333.              * mouse in the X direction will pan the camera about the
  1334.              * origin of world coordinate space, and movement of the mouse
  1335.              * in the Y direction will zoom the camera into and out of the
  1336.              * the scene.
  1337.              */
  1338.  
  1339.             /*
  1340.              * Move the camera back to the origin, as we wish to pan about
  1341.              * the origin of the world and not about the origin of the
  1342.              * camera.
  1343.              */
  1344.             RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
  1345.  
  1346.             /*
  1347.              * Undo the tilt as we wish to pan about the world Y axis.
  1348.              */
  1349.             RwTiltCamera(Camera, -CameraTilt);
  1350.  
  1351.             /*
  1352.              * Pan the camera by mouse X delta degrees.
  1353.              */
  1354.             RwPanCamera(Camera, INT2REAL(LastX - pos.x));
  1355.  
  1356.             /*
  1357.              * Zoom the camera by changing the distance the camera
  1358.              * is from the origin of the world by mouse Y delta divided
  1359.              * by 10 units.
  1360.              */
  1361.             CameraDistance = RAdd(CameraDistance,
  1362.                                 RDiv(INT2REAL(LastY - pos.y), CREAL(10.0)));
  1363.  
  1364.             /*
  1365.              * Redo the tilt.
  1366.              */
  1367.             RwTiltCamera(Camera, CameraTilt);
  1368.  
  1369.             /*
  1370.              * Move the camera out to its new distance from the world's
  1371.              * origin.
  1372.              */
  1373.             RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  1374.             break;
  1375.  
  1376.         case MMTiltCamera:
  1377.             /*
  1378.              * Move the camera back to the origin, as we wish to tilt about
  1379.              * the origin of the world and not about the origin of the
  1380.              * camera.
  1381.              */
  1382.             RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
  1383.  
  1384.             /*
  1385.              * Undo the existing tilt.
  1386.              */
  1387.             RwTiltCamera(Camera, -CameraTilt);
  1388.  
  1389.             /*
  1390.              * Compute the new angle of tilt.
  1391.              */
  1392.             CameraTilt = RAdd(CameraTilt, INT2REAL(LastY - pos.y));
  1393.  
  1394.             /*
  1395.              * Apply the new tilt.
  1396.              */
  1397.             RwTiltCamera(Camera, CameraTilt);
  1398.  
  1399.             /*
  1400.              * Move the camera out to its new distance from the world's
  1401.              * origin.
  1402.              */
  1403.             RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
  1404.             break;
  1405.  
  1406.         case MMSpinClump:
  1407.             /*
  1408.              * Compute the angles of spin (simply derived from the mouse move deltas).
  1409.              */
  1410.             yAngle = INT2REAL(pos.x - LastX);
  1411.             xAngle = INT2REAL(pos.y - LastY);
  1412.  
  1413.             /*
  1414.              * In WinView, a clump is spun about its own, local coordinate system,
  1415.              * origin, rather than the origin of the world coordinate system. There
  1416.              * are a number of ways this could be achieved, but the most convenient
  1417.              * is to simply apply the rotations to the clump's joint (articulation)
  1418.              * transform.
  1419.              *
  1420.              * A further important point is that the axes of rotation must be the
  1421.              * camera's Look Up and Look Right vector rather than simply
  1422.              * [CREAL(0.0), CREAL(1.0), CREAL(0.0)] and
  1423.              * [CREAL(1.0), CREAL(0.0), CREAL(0.0)]. This ensures that, if the camera
  1424.              * has been panned, tilted, revolved or transformed so that it no longer
  1425.              * looks down the Z axis, user interaction will still operate correctly,
  1426.              * i.e. moving the mouse to the left will spin the clump clockwise etc.
  1427.              *
  1428.              * Therefore, the first stage is to get the camera's Look Up and
  1429.              * Look Right vectors.
  1430.              */
  1431.             RwGetCameraLookUp(Camera, &up);
  1432.             RwGetCameraLookRight(Camera, &right);
  1433.  
  1434.             /*
  1435.              * Unfortunately, rotation about the camera's Look Up and Look Right
  1436.              * vectors is complicated if the clump being manipulated a child clump
  1437.              * (i.e. not the root of a clump hierarchy). If this is the case, the
  1438.              * camera's vectors have to be transformed into the coordinate space
  1439.              * of the parent of the clump, being manipulated.
  1440.              */
  1441.             parent = RwGetClumpParent(PickedClump);
  1442.             if (parent != NULL)
  1443.             {
  1444.                 /*
  1445.                  * Get a handle to a couple of temporary matrices.
  1446.                  */
  1447.                 tmpMatrix = RwPushScratchMatrix();
  1448.                 worldToLocal = RwPushScratchMatrix();
  1449.  
  1450.                 /*
  1451.                  * Get the parent clump's LTM (which maps local coordinates to
  1452.                  * world space).
  1453.                  */
  1454.                 RwGetClumpLTM(parent, tmpMatrix);
  1455.  
  1456.                 /*
  1457.                  * Invert it so that it maps world coordinates to the parent's
  1458.                  * local coordinate space.
  1459.                  */
  1460.                 RwInvertMatrix(tmpMatrix, worldToLocal);
  1461.  
  1462.                 /*
  1463.                  * And transform the camera's vectors into local space.
  1464.                  */
  1465.                 RwTransformVector(&up, worldToLocal);
  1466.                 RwNormalize(&up);
  1467.                 RwTransformVector(&right, worldToLocal);
  1468.                 RwNormalize(&right);
  1469.  
  1470.                 /*
  1471.                  * Discard the temporary matrices.
  1472.                  */
  1473.                 RwPopScratchMatrix();
  1474.                 RwPopScratchMatrix();
  1475.             }
  1476.  
  1477.             /*
  1478.              * Apply the rotations.
  1479.              */
  1480.             RwRotateMatrix(SpinMatrix, up.x, up.y, up.z, yAngle, rwREPLACE);
  1481.             RwRotateMatrix(SpinMatrix, right.x, right.y, right.z, xAngle, rwPOSTCONCAT);
  1482.  
  1483.             /*
  1484.              * Apply the resulting, composite transformation to the clump.
  1485.              */
  1486.             RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
  1487.  
  1488.             /*
  1489.              * As the mouse has move enable the clump spin.
  1490.              */
  1491.             AnimMode = ASpinClump;
  1492.  
  1493.             break;
  1494.  
  1495.         case MMDragClump:
  1496.             RwPushScratchMatrix();
  1497.             /*
  1498.              * Compute the amount to translate the object by. This is simply
  1499.              * derived from the mouse deltas scaled by some arbitrary quantity
  1500.              * to prevent objects moving too "quickly".
  1501.              */
  1502.             xDelta = RDiv(INT2REAL(pos.x - LastX), CREAL(50.0));
  1503.             yDelta = RDiv(INT2REAL(LastY - pos.y), CREAL(50.0));
  1504.  
  1505.             /*
  1506.              * Similarly to spinning a clump we must take account of the camera's
  1507.              * orientation when dragging a clump. This is done by translating along
  1508.              * the camera's look up and look right vectors (scaled appropriately)
  1509.              * rather than the clump's local axes.
  1510.              */
  1511.             RwGetCameraLookRight(Camera, &right);
  1512.             RwGetCameraLookUp(Camera, &up);
  1513.  
  1514.             /*
  1515.              * See the previous case for a description of why the following is
  1516.              * necessary.
  1517.              */
  1518.             parent = RwGetClumpParent(PickedClump);
  1519.             if (parent != NULL)
  1520.             {
  1521.                 tmpMatrix = RwPushScratchMatrix();
  1522.                 worldToLocal = RwPushScratchMatrix();
  1523.                 RwGetClumpLTM(parent, tmpMatrix);
  1524.                 RwInvertMatrix(tmpMatrix, worldToLocal);
  1525.                 RwTransformVector(&up, worldToLocal);
  1526.                 RwNormalize(&up);
  1527.                 RwTransformVector(&right, worldToLocal);
  1528.                 RwNormalize(&right);
  1529.                 RwPopScratchMatrix();
  1530.                 RwPopScratchMatrix();
  1531.             }
  1532.  
  1533.             /*
  1534.              * Perform the translations.
  1535.              */
  1536.             RwTranslateMatrix(RwScratchMatrix(),
  1537.                               RMul(right.x, xDelta), RMul(right.y, xDelta),
  1538.                               RMul(right.z, xDelta), rwREPLACE);
  1539.             RwTranslateMatrix(RwScratchMatrix(),
  1540.                               RMul(up.x, yDelta), RMul(up.y, yDelta),
  1541.                               RMul(up.z, yDelta), rwPOSTCONCAT);
  1542.  
  1543.             /*
  1544.              * Apply the result, composite transform the clump.
  1545.              */
  1546.             RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1547.  
  1548.             RwPopScratchMatrix();
  1549.             break;
  1550.  
  1551.         case MMDragClumpInZ:
  1552.             RwPushScratchMatrix();
  1553.             /*
  1554.              * Compute the amount to translate the object by. This is simply
  1555.              * derived from the mouse deltas scaled by some arbitrary quantity
  1556.              * to prevent objects moving too "quickly".
  1557.              */
  1558.             yDelta = RDiv(INT2REAL(LastY - pos.y), CREAL(50.0));
  1559.  
  1560.             /*
  1561.              * Similarly to spinning a clump we must take account of the camera's
  1562.              * orientation when dragging a clump. This is done by translating along
  1563.              * the camera's look at (scaled appropriately) rather than the clump's
  1564.              * local axes.
  1565.              */
  1566.             RwGetCameraLookAt(Camera, &at);
  1567.  
  1568.             /*
  1569.              * See the case for MMSpinClump: for a description of why the
  1570.              * following is necessary.
  1571.              */
  1572.             parent = RwGetClumpParent(PickedClump);
  1573.             if (parent != NULL)
  1574.             {
  1575.                 tmpMatrix = RwPushScratchMatrix();
  1576.                 worldToLocal = RwPushScratchMatrix();
  1577.                 RwGetClumpLTM(parent, tmpMatrix);
  1578.                 RwInvertMatrix(tmpMatrix, worldToLocal);
  1579.                 RwTransformVector(&at, worldToLocal);
  1580.                 RwNormalize(&at);
  1581.                 RwPopScratchMatrix();
  1582.                 RwPopScratchMatrix();
  1583.             }
  1584.  
  1585.             /*
  1586.              * Perform the translation.
  1587.              */
  1588.             RwTranslateMatrix(RwScratchMatrix(),
  1589.                               RMul(at.x, yDelta), RMul(at.y, yDelta),
  1590.                               RMul(at.z, yDelta), rwREPLACE);
  1591.  
  1592.             /*
  1593.              * Apply the resulting, composite transform the clump.
  1594.              */
  1595.             RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
  1596.  
  1597.             RwPopScratchMatrix();
  1598.             break;
  1599.  
  1600.         case MMPanLight:
  1601.             /*
  1602.              * We are panning the light about the origin. We will rotate
  1603.              * the light about the Y axis for movements of the mouse in
  1604.              * X and rotate the light about the X axis for movements of
  1605.              * the mouse in Y.
  1606.              */
  1607.             RwPushScratchMatrix();
  1608.             /*
  1609.              * Replace the CTM with a rotation matrix about Y. The numbers
  1610.              * of degrees of rotation is given by the mouse X delta.
  1611.              */
  1612.             RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0),
  1613.                            INT2REAL(LastX - pos.x), rwREPLACE);
  1614.  
  1615.             /*
  1616.              * Postconcat another rotation onto the CTM. The new rotation
  1617.              * is a rotation about X, the angle being given by the mouse
  1618.              * Y delta.
  1619.              */
  1620.             RwRotateMatrix(RwScratchMatrix(), CREAL(1.0), CREAL(0.0), CREAL(0.0),
  1621.                            INT2REAL(LastY - pos.y), rwPOSTCONCAT);
  1622.  
  1623.             /*
  1624.              * Transform the light by the resultant rotations.
  1625.              */
  1626.             RwTransformLight(Light, RwScratchMatrix(), rwPOSTCONCAT);
  1627.             RwPopScratchMatrix();
  1628.             break;
  1629.     }
  1630.  
  1631.     /*
  1632.      * See the description of HandlePaint() for a description of this
  1633.      * common RenderWare programming cliche for rendering a scene and
  1634.      * copying it to the display.
  1635.      */
  1636.     RwInvalidateCameraViewport(Camera);
  1637.     RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
  1638.     RwClearCameraViewport(Camera);
  1639.     RwRenderScene(Scene);
  1640.     RwEndCameraUpdate(Camera);
  1641.     dc = GetDC(window);
  1642.     RwShowCameraImage(Camera, (void *) (DWORD) dc);
  1643.     ReleaseDC(window, dc);
  1644.  
  1645.     /*
  1646.      * Remember the current X and Y for next time.
  1647.      */
  1648.     LastX = pos.x;
  1649.     LastY = pos.y;
  1650. }
  1651.  
  1652. /**********************************************************************/
  1653.  
  1654. /*
  1655.  * Handle the left mouse button comming back up. The basic action is
  1656.  * to turn of mouse move actions, and release mouse capture. Also,
  1657.  * if the previous mouse move action was a spin then this function will
  1658.  * turn on animation to continue the spin (and the objects "momentum").
  1659.  */
  1660. static void
  1661. HandleLeftButtonUp()
  1662. {
  1663.     /*
  1664.      * If we were engaged in a mouse move action and the button has come
  1665.      * back up, then terminate the action and release mouse capture.
  1666.      */
  1667.     LeftButtonDown = 0;
  1668.     ReleaseCapture();
  1669.     if (MouseMoveMode != MMNoAction)
  1670.     {
  1671.         MouseMoveMode = MMNoAction;
  1672.     }
  1673. }
  1674.  
  1675. /**********************************************************************/
  1676.  
  1677. /*
  1678.  * Handle the left mouse button comming back up. The basic action is
  1679.  * to turn of mouse move actions, and release mouse capture. Also,
  1680.  * if the previous mouse move action was a spin then this function will
  1681.  * turn on animation to continue the spin (and the objects "momentum").
  1682.  */
  1683. static void
  1684. HandleRightButtonUp()
  1685. {
  1686.     /*
  1687.      * If we were engaged in a mouse move action and the button has come
  1688.      * back up, then terminate the action and release mouse capture.
  1689.      */
  1690.     if (MouseMoveMode != MMNoAction)
  1691.     {
  1692.         MouseMoveMode = MMNoAction;
  1693.         ReleaseCapture();
  1694.     }
  1695. }
  1696.  
  1697. /**********************************************************************/
  1698.  
  1699. /*
  1700.  * Handle an MS Window's drop message, this function will attempt to
  1701.  * load the files dropped as RenderWare clumps. If that fails, it will
  1702.  * then attempt to load the files as textures, which will the be used
  1703.  * to create decal clumps.
  1704.  */
  1705. static void
  1706. HandleDrop(HWND window, HDROP drop)
  1707. {
  1708.     RwClump *clump;
  1709.     char path[_MAX_PATH];
  1710.     int i;
  1711.  
  1712.     /*
  1713.      * A file has been dropped onto the window. This function assumes 
  1714.      * the file is either a RenderWare script file or a texture file.
  1715.      * Taking the path of least resistance, it does not attempt to
  1716.      * determine which is which, it simply tries to load it as
  1717.      * a script file, and if that fails it attempts to load it as
  1718.      * a texture. Not particularly elegant, but simple.
  1719.      */
  1720.     for (i = DragQueryFile(drop, (UINT) - 1, path, _MAX_PATH) - 1; i >= 0; --i)
  1721.     {
  1722.         DragQueryFile(drop, (UINT) i, path, _MAX_PATH);
  1723.         clump = LoadClumpOrTexture(window, path);
  1724.         if (clump)
  1725.         {
  1726.             /*
  1727.                * A clump was successfully loaded, so add the new clump to
  1728.                * our scene.
  1729.              */
  1730.             RwAddClumpToScene(Scene, clump);
  1731.  
  1732.             /*
  1733.                * Force a redraw of the window, to ensure the new clump is
  1734.                * visible.
  1735.              */
  1736.             InvalidateRect(window, NULL, FALSE);
  1737.         }
  1738.     }
  1739.     DragFinish(drop);
  1740. }
  1741.  
  1742. /**********************************************************************/
  1743.  
  1744. /*
  1745.  * Handle window paint messages by rerendering the entire scene and
  1746.  * copying the camera's entire viewport to the display.
  1747.  */
  1748. static void
  1749. HandlePaint(HWND window)
  1750. {
  1751.     HDC dc;
  1752.     PAINTSTRUCT paintStruct;
  1753.  
  1754.     dc = BeginPaint(window, &paintStruct);
  1755.  
  1756.     /*
  1757.      * As we are repainting the entire window we invalidate the
  1758.      * camera's viewport to ensure the whole window gets updated
  1759.      * (and not simply the parts that will be damaged by the render
  1760.      * operation).
  1761.      */
  1762.     RwInvalidateCameraViewport(Camera);
  1763.  
  1764.     /*
  1765.      * The following is a common RenderWare programming cliche.
  1766.      * It performs a render of a scene into a camera and copies
  1767.      * the resultant image to the display. Note, that this segment
  1768.      * of code will re-render the scene and then copy it to the
  1769.      * display. This is not strictly necessary. All that really needs
  1770.      * be done for a window repaint is to copy the camera's image buffer
  1771.      * (containing the results of the last render) to the display.
  1772.      * However, in this simple application, several situations which
  1773.      * require the scene to be re-rendered (for example, when adding
  1774.      * a clump), perform a re-render simply by invalidating the
  1775.      * window's client area, and hence invoking this code.
  1776.      */
  1777.     RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
  1778.  
  1779.     /*
  1780.      * Clear the areas of the camera's viewport which were damaged
  1781.      * last time round. If this call is not made, ghost images of
  1782.      * previous rendering will remain.
  1783.      */
  1784.     RwClearCameraViewport(Camera);
  1785.  
  1786.     /*
  1787.      * Re-render the entire scene.
  1788.      */
  1789.     RwRenderScene(Scene);
  1790.  
  1791.     /*
  1792.      * Perform all necessary housekeeping after rendering is complete.
  1793.      * After this call, the camera's image buffer will be ready to be
  1794.      * copied to the display.
  1795.      */
  1796.     RwEndCameraUpdate(Camera);
  1797.  
  1798.     /*
  1799.      * Copy the camera's image buffer to the output window. Under
  1800.      * MS Windows, RenderWare requires both the window handle of the
  1801.      * output window and a device context for the client area of the
  1802.      * window. Therefore, a LONG is created which holds both the
  1803.      * window handle and device context of the output window. This 
  1804.      * LONG is then passed in as the device specific parameter of
  1805.      * the RwShowCameraImage() call.
  1806.      */
  1807.     RwShowCameraImage(Camera, (void *) (DWORD) dc);
  1808.  
  1809.     EndPaint(window, &paintStruct);
  1810. }
  1811.  
  1812. /**********************************************************************/
  1813.  
  1814. /*
  1815.  * Handle MS Window's timer expiry. This function will perform any
  1816.  * animation actions necessary, including spinning clumps and animating
  1817.  * textures.
  1818.  */
  1819. static void
  1820. HandleTimer(HWND window)
  1821. {
  1822.     HDC devContext;
  1823.  
  1824.     if (PickedClump && (AnimMode != ANoAction) && (MouseMoveMode == MMNoAction))
  1825.     {
  1826.         switch (AnimMode)
  1827.         {
  1828.             case ASpinClump:
  1829.                 /*
  1830.                  * Spin the last clump picked by the last computed spin matrix.
  1831.                  */
  1832.                 RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
  1833.                 break;
  1834.         }
  1835.     }
  1836.  
  1837.     /*
  1838.      * Animate textures. Enumerate over all the textures in the texture
  1839.      * dictionary calling RwTextureNextFrame to bump the current frame
  1840.      * pointer.
  1841.      */
  1842.     RwForAllNamedTextures(RwTextureNextFrame);
  1843.  
  1844.     /*
  1845.      * See the description of HandlePaint() for a description of this common
  1846.      * RenderWare cliche for rendering a scene and copying it to the display.
  1847.      */
  1848.     RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
  1849.     RwClearCameraViewport(Camera);
  1850.     RwRenderScene(Scene);
  1851.     RwEndCameraUpdate(Camera);
  1852.     devContext = GetDC(window);
  1853.     RwShowCameraImage(Camera, (void *) (DWORD) devContext);
  1854.     ReleaseDC(window, devContext);
  1855. }
  1856.  
  1857. /**********************************************************************/
  1858.  
  1859. /*
  1860.  * The window procedure for the toolbar
  1861.  */
  1862. LRESULT CALLBACK
  1863. ToolWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
  1864. {
  1865.     PAINTSTRUCT paintStruct;
  1866.     HDC devContext;
  1867.     RECT rect;
  1868.     HPEN pen, oldPen;
  1869.  
  1870.     switch (message)
  1871.     {
  1872.         case WM_COMMAND:
  1873.             switch (HIWORD(lParam))
  1874.             {
  1875.                 case BN_CLICKED:
  1876.                     switch (wParam)
  1877.                     {
  1878.                         case 1:
  1879.                             SendMessage(But1, BM_SETSTATE, 1, 0L);
  1880.                             SendMessage(But2, BM_SETSTATE, 0, 0L);
  1881.                             PaintMode = 0;
  1882.                             EnableWindow(But1, FALSE);
  1883.                             EnableWindow(But2, TRUE);
  1884.                             SetFocus(GetParent(But1));
  1885.                             return 0;
  1886.                         case 2:
  1887.                             SendMessage(But1, BM_SETSTATE, 0, 0L);
  1888.                             SendMessage(But2, BM_SETSTATE, 1, 0L);
  1889.                             EnableWindow(But1, TRUE);
  1890.                             EnableWindow(But2, FALSE);
  1891.                             PaintMode = 1;
  1892.                             PaintWnd = MaterialEditor(Material, AppInstance, MainWindow,
  1893.                                                MainWindow, MATERIAL_CHANGE);
  1894.                             SetFocus(GetParent(But2));
  1895.                             PostMessage(PaintWnd, WM_SYSCOMMAND, SC_RESTORE, 0L);
  1896.                             return 0;
  1897.                     }
  1898.                     break;
  1899.             }
  1900.             break;
  1901.         case WM_PAINT:
  1902.             devContext = BeginPaint(window, &paintStruct);
  1903.             GetClientRect(window, &rect);
  1904.             pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  1905.             oldPen = SelectObject(devContext, pen);
  1906.             MoveToEx(devContext, rect.left, rect.bottom, NULL);
  1907.             LineTo(devContext, rect.left, rect.top);
  1908.             LineTo(devContext, rect.right, rect.top);
  1909.             SelectObject(devContext, oldPen);
  1910.             DeleteObject(pen);
  1911.             pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  1912.             oldPen = SelectObject(devContext, pen);
  1913.             LineTo(devContext, rect.right, rect.bottom - 1);
  1914.             LineTo(devContext, rect.left, rect.bottom - 1);
  1915.  
  1916.             SelectObject(devContext, oldPen);
  1917.             DeleteObject(pen);
  1918.             EndPaint(window, &paintStruct);
  1919.             return 0;
  1920.         case WM_MEASUREITEM:
  1921.             {
  1922.                 MEASUREITEMSTRUCT FAR *m;
  1923.  
  1924. #ifdef    __WINDOWS_386__
  1925.                 m = (MEASUREITEMSTRUCT FAR *) MK_FP32((void *) lParam);
  1926. #else /* __WINDOWS_386__ */
  1927.                 m = (MEASUREITEMSTRUCT FAR *) lParam;
  1928. #endif /* __WINDOWS_386__ */
  1929.  
  1930.                 m->itemWidth = 24;
  1931.                 m->itemHeight = 24;
  1932.  
  1933.                 return TRUE;
  1934.             }
  1935.         case WM_DRAWITEM:
  1936.             {
  1937.                 DRAWITEMSTRUCT far *pDis;
  1938.                 HDC hdcMemory;
  1939.                 HBITMAP hbmpMyBitmap, hbmpOld;
  1940.                 BITMAP bm;
  1941.  
  1942. #ifdef    __WINDOWS_386__
  1943.                 pDis = (DRAWITEMSTRUCT FAR *) MK_FP32((void *) lParam);
  1944. #else /* __WINDOWS_386__ */
  1945.                 pDis = (DRAWITEMSTRUCT FAR *) lParam;
  1946. #endif /* __WINDOWS_386__ */
  1947.  
  1948.                 hbmpMyBitmap = LoadBitmap(
  1949. #ifdef WIN32
  1950.                        (HINSTANCE) GetWindowLong(MainWindow, GWL_HINSTANCE),
  1951. #else
  1952.                        (HINSTANCE) GetWindowWord(MainWindow, GWW_HINSTANCE),
  1953. #endif
  1954.                                              MAKEINTRESOURCE
  1955.                                              (pDis->hwndItem == But1 ?
  1956.                                               moveup : paintup));
  1957.                 GetObject(hbmpMyBitmap, sizeof (BITMAP), &bm);
  1958.  
  1959.                 hdcMemory = CreateCompatibleDC(pDis->hDC);
  1960.                 hbmpOld = SelectObject(hdcMemory, hbmpMyBitmap);
  1961.  
  1962.                 BitBlt(pDis->hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMemory, 0, 0,
  1963.                        SendMessage(pDis->hwndItem, BM_GETSTATE, 0, 0) & 4 ?
  1964.                        NOTSRCCOPY : SRCCOPY);
  1965.                 SelectObject(hdcMemory, hbmpOld);
  1966.                 DeleteDC(hdcMemory);
  1967.                 DeleteObject(hbmpMyBitmap);
  1968.                 return TRUE;
  1969.             }
  1970.     }
  1971.     return DefWindowProc(window, message, wParam, lParam);
  1972. }
  1973.  
  1974. /**********************************************************************/
  1975.  
  1976. static void
  1977. LoadFile(HWND window)
  1978. {
  1979.     OPENFILENAME ofn;
  1980.     static char path[_MAX_PATH];
  1981.     static char initDir[_MAX_PATH];
  1982.     static char file[20];
  1983.     static char ext[10];
  1984.     RwClump *clump;
  1985.  
  1986.     memset(&ofn, 0, sizeof (ofn));
  1987.  
  1988.     if (initDir[0] == '\0')
  1989.     {
  1990.         strcpy(initDir, getenv("RWSHAPEPATH"));
  1991.         if (strchr(initDir, ';'))
  1992.             *strchr(initDir, ';') = '\0';
  1993.     }
  1994.  
  1995.     sprintf(path, "%s%s", file, ext);
  1996.     ofn.lStructSize = sizeof (ofn);
  1997.     ofn.hwndOwner = window;
  1998.     ofn.lpstrFile = path;
  1999.     ofn.nMaxFile = sizeof (path);
  2000.     ofn.lpstrInitialDir = initDir;
  2001.     ofn.lpstrFilter = "Scripts  (*.rwx)\0*.rwx\0"
  2002.         "All Files (*.*)\0*.*\0";
  2003.     ofn.nFilterIndex = 1;
  2004.     ofn.lpstrTitle = "Load Object";
  2005.     ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
  2006.     KillTimer(window, 1);
  2007.     if (GetOpenFileName(&ofn))
  2008.     {
  2009.         clump = LoadClumpOrTexture(window, path);
  2010.         if (clump != NULL)
  2011.         {
  2012.             RwAddClumpToScene(Scene, clump);
  2013.             InvalidateRect(window, NULL, FALSE);
  2014.         }
  2015.         _splitpath(path, initDir, initDir + 2, file, ext);
  2016.     }
  2017.     SetTimer(window, 1, TIMER_TICKS, NULL);
  2018. }
  2019.  
  2020. /**********************************************************************/
  2021.  
  2022. static void
  2023. SaveFile(HWND window, char *fullPath)
  2024. {
  2025.     OPENFILENAME ofn;
  2026.     static char path[_MAX_PATH];
  2027.     static char initDir[_MAX_PATH];
  2028.     static char file[20];
  2029.     static char ext[10];
  2030.     CLUMP_DATA *p;
  2031.  
  2032.     memset(&ofn, 0, sizeof (ofn));
  2033.  
  2034.     _splitpath(fullPath, initDir, initDir + 2, file, ext);
  2035.  
  2036.     sprintf(path, "%s%s", file, ext);
  2037.     ofn.lStructSize = sizeof (ofn);
  2038.     ofn.hwndOwner = window;
  2039.     ofn.lpstrFile = path;
  2040.     ofn.nMaxFile = sizeof (path);
  2041.     ofn.lpstrInitialDir = initDir;
  2042.     ofn.lpstrFilter = "Scripts  (*.rwx)\0*.rwx\0"
  2043.         "All Files (*.*)\0*.*\0";
  2044.     ofn.nFilterIndex = 1;
  2045.     ofn.lpstrTitle = "Save Object";
  2046.     ofn.lpstrDefExt = "rwx";
  2047.     ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
  2048.     KillTimer(window, 1);
  2049.     if (GetSaveFileName(&ofn))
  2050.     {
  2051.         if (RwWriteShape(path, ClumpWithUserDraw) == 0)
  2052.         {
  2053.             MessageBox(window, "Unable to save file", NULL, MB_ICONEXCLAMATION | MB_OK);
  2054.         }
  2055.         else
  2056.         {
  2057.             p = RwGetClumpData(ClumpWithUserDraw);
  2058.             if (p != NULL)
  2059.             {
  2060.                 free(p->FullName);
  2061.                 free(p->UserDraw);
  2062.                 p->FullName = strdup(path);
  2063.                 if (p->FullName != NULL)
  2064.                     _splitpath(p->FullName, NULL, NULL, path, NULL);
  2065.                 p->UserDraw = strdup(path);
  2066. #ifdef    USERDRAW_LABELS
  2067.                 if (p->UserDraw != NULL)
  2068.                     RwSetUserDrawData(UserDraw, p->UserDraw);
  2069. #endif /* USERDRAW_LABELS */
  2070.                 InvalidateRect(window, NULL, FALSE);
  2071.             }
  2072.             _splitpath(path, initDir, initDir + 2, file, ext);
  2073.         }
  2074.     }
  2075.     SetTimer(window, 1, TIMER_TICKS, NULL);
  2076. }
  2077.  
  2078. /**********************************************************************/
  2079.  
  2080. static void
  2081. HandleCommand(HWND window, WPARAM wParam)
  2082. {
  2083.     char buf[_MAX_PATH + 100];
  2084.  
  2085.     switch (wParam)
  2086.     {
  2087.         case MENU_LOAD:
  2088.             LoadFile(window);
  2089.             return;
  2090.         case MENU_SAVE:
  2091.             if (ClumpWithUserDraw != NULL)
  2092.             {
  2093.                 if (!RwWriteShape(((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName, ClumpWithUserDraw))
  2094.                 {
  2095.                     sprintf(buf, "Error writing script file %s", ((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName);
  2096.                     MessageBox(window, buf, NULL, MB_ICONEXCLAMATION | MB_OK | MB_APPLMODAL);
  2097.                 }
  2098.             }
  2099.             return;
  2100.         case MENU_SAVEAS:
  2101.             if (ClumpWithUserDraw != NULL)
  2102.                 SaveFile(window, ((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName);
  2103.             return;
  2104.         case MENU_EXIT:
  2105.             PostQuitMessage(0);
  2106.             return;
  2107.     }
  2108. }
  2109.  
  2110. /**********************************************************************/
  2111.  
  2112. /*
  2113.  * Double clicking selects the paint window with the material under it.
  2114.  */
  2115.  
  2116. static void
  2117. HandleDoubleClick(HWND window, int x, int y)
  2118. {
  2119.     RwPickRecord pick;
  2120.  
  2121.     if (RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
  2122.     {
  2123.         if (pick.type == rwPICKCLUMP)
  2124.         {
  2125.             RwCopyMaterial(RwGetPolygonMaterial(pick.object.clump.polygon), Material);
  2126.             PaintWnd = MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
  2127.         }
  2128.     }
  2129. }
  2130.  
  2131. /**********************************************************************/
  2132.  
  2133. /*
  2134.  * The window procedure for this application's window.
  2135.  */
  2136. LRESULT CALLBACK
  2137. MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
  2138. {
  2139. #ifdef    WIN32
  2140.     POINTS point;
  2141.  
  2142. #else /* WIN32 */
  2143.     POINT point;
  2144.  
  2145. #endif /* WIN32 */
  2146.  
  2147.     static HWND ToolBar;
  2148.  
  2149.     switch (message)
  2150.     {
  2151.         case WM_CREATE:
  2152.             /*
  2153.              * Clumps are loaded into the scene by drag and drop.
  2154.              * So make this window a drop site.
  2155.              */
  2156.             DragAcceptFiles(window, TRUE);
  2157.  
  2158.             /*
  2159.              * In this application, 3D animation is driven by window's
  2160.              * timer messages, so turn a timer on.
  2161.              */
  2162.             MainWindow = window;
  2163.             ToolBar = InitChildren(AppInstance, window);
  2164.             SetTimer(window, 1, 20, NULL);
  2165.             return 0L;
  2166.  
  2167.         case WM_COMMAND:
  2168.             HandleCommand(window, wParam);
  2169.             return 0L;
  2170.  
  2171.         case WM_GETMINMAXINFO:
  2172. #ifdef    __WINDOWS_386__
  2173.             HandleGetMinMaxInfo((MINMAXINFO FAR *) MK_FP32((void *) lParam));
  2174. #else /* __WINDOWS_386__ */
  2175.             HandleGetMinMaxInfo((MINMAXINFO FAR *) lParam);
  2176. #endif /* __WINDOWS_386__ */
  2177.             return 0L;
  2178.  
  2179.         case WM_SIZE:
  2180.             if (ThreeDInitialized)
  2181.             {
  2182. #ifdef    WIN32
  2183.                 point = MAKEPOINTS(lParam);
  2184. #else /* WIN32 */
  2185.                 point = MAKEPOINT(lParam);
  2186. #endif /* WIN32 */
  2187.                 HandleSize(window, point.x, point.y, ToolBar);
  2188.             }
  2189.             return 0L;
  2190.  
  2191.         case WM_DROPFILES:
  2192.             if (ThreeDInitialized)
  2193.                 HandleDrop(window, (HDROP) wParam);
  2194.             return 0L;
  2195.  
  2196.         case WM_LBUTTONDOWN:
  2197.             if (ThreeDInitialized)
  2198. #ifdef    WIN32
  2199.                 point = MAKEPOINTS(lParam);
  2200. #else /* WIN32 */
  2201.                 point = MAKEPOINT(lParam);
  2202. #endif /* WIN32 */
  2203.             HandleLeftButtonDown(window, point.x, point.y, wParam);
  2204.             return 0L;
  2205.  
  2206.         case WM_RBUTTONDOWN:
  2207.             if (ThreeDInitialized)
  2208. #ifdef    WIN32
  2209.                 point = MAKEPOINTS(lParam);
  2210. #else /* WIN32 */
  2211.                 point = MAKEPOINT(lParam);
  2212. #endif /* WIN32 */
  2213.             HandleRightButtonDown(window, point.x, point.y, wParam);
  2214.             return 0L;
  2215.  
  2216.         case WM_MOUSEMOVE:
  2217.             if (ThreeDInitialized)
  2218.             {
  2219.                 if (MouseMoveMode != MMNoAction || PaintMode == 1)
  2220. #ifdef    WIN32
  2221.                     point = MAKEPOINTS(lParam);
  2222. #else /* WIN32 */
  2223.                     point = MAKEPOINT(lParam);
  2224. #endif /* WIN32 */
  2225.                 HandleMouseMove(window, point.x, point.y);
  2226.             }
  2227.             return 0L;
  2228.  
  2229.         case WM_LBUTTONDBLCLK:
  2230.         case WM_RBUTTONDBLCLK:
  2231.             if (ThreeDInitialized)
  2232.             {
  2233. #ifdef    WIN32
  2234.                 point = MAKEPOINTS(lParam);
  2235. #else /* WIN32 */
  2236.                 point = MAKEPOINT(lParam);
  2237. #endif /* WIN32 */
  2238.                 HandleDoubleClick(window, point.x, point.y);
  2239.             }
  2240.             return 0L;
  2241.  
  2242.         case WM_LBUTTONUP:
  2243.             if (ThreeDInitialized)
  2244.                 HandleLeftButtonUp();
  2245.             return 0L;
  2246.  
  2247.         case WM_RBUTTONUP:
  2248.             if (ThreeDInitialized)
  2249.                 HandleRightButtonUp();
  2250.             return 0L;
  2251.  
  2252.         case WM_PAINT:
  2253.             if (ThreeDInitialized)
  2254.                 HandlePaint(window);
  2255.             return 0L;
  2256.  
  2257.         case WM_TIMER:
  2258.             if (ThreeDInitialized)
  2259.                 HandleTimer(window);
  2260.             return 0L;
  2261.  
  2262.         case MATERIAL_CHANGE:
  2263.             /*
  2264.                * code here for material change notification 
  2265.                * lParam is pointer to material (the same as went in)
  2266.                * or null if window is closing
  2267.                * wParam is a bitfield of changed attributes.
  2268.              */
  2269.             if (lParam == 0)
  2270.                 PaintWnd = NULL;
  2271.             return 0L;
  2272.  
  2273.         case WM_CHAR:
  2274.             if ((wParam == 'p') || (wParam == 'P'))
  2275.             {
  2276.                 PaintWnd = MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
  2277.                 return 0L;
  2278.             }
  2279.             break;
  2280.  
  2281.         case WM_DESTROY:
  2282.             /*
  2283.              * The window is going away so it is no longer a drop site.
  2284.              */
  2285.             DragAcceptFiles(window, FALSE);
  2286.  
  2287.             /*
  2288.              * Turn the timer off.
  2289.              */
  2290.             KillTimer(window, 1);
  2291.  
  2292.             /*
  2293.              * Quit message handling.
  2294.              */
  2295.             PostQuitMessage(0);
  2296.             return 0L;
  2297.     }
  2298.  
  2299.     /*
  2300.      * Let windows handle all other messages.
  2301.      */
  2302.     return DefWindowProc(window, message, wParam, lParam);
  2303. }
  2304.  
  2305. /**********************************************************************/
  2306.  
  2307. /*
  2308.  * MS Windows application entry point.
  2309.  */
  2310. int PASCAL
  2311. WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
  2312. {
  2313.     MSG message;
  2314.     HWND window;
  2315.  
  2316.     /*
  2317.      * Remember the application instance in a global variable for later use.
  2318.      */
  2319.     AppInstance = instance;
  2320.  
  2321.     if (prevInstance)
  2322.     {
  2323.         /*
  2324.          * Only allow one viewer application to run at any one time.
  2325.          */
  2326.         MessageBox((HWND) 0, "WinPaint is already running...",
  2327.                    ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
  2328.         return FALSE;
  2329.     }
  2330.  
  2331.     /*
  2332.      * Register the window class.
  2333.      */
  2334.     if (!InitApplication(instance))
  2335.         return FALSE;
  2336.  
  2337.     /*
  2338.      * Create the window.
  2339.      */
  2340.     window = InitInstance(instance);
  2341.     if (window == NULL)
  2342.         return FALSE;
  2343.     CheckDisplayDepth(window);
  2344.  
  2345.     /*
  2346.      * Initialize the 3D (RenderWare) components of the app.
  2347.      */
  2348.     if (!Init3D(window))
  2349.     {
  2350.         DestroyWindow(window);
  2351.         return FALSE;
  2352.     }
  2353.  
  2354.     /*
  2355.      * Parse any command line parameters.
  2356.      */
  2357.     if (!ReadFromCommandLine(window, cmdLine))
  2358.     {
  2359.         TidyUp3D();
  2360.         DestroyWindow(window);
  2361.         return FALSE;
  2362.     }
  2363.  
  2364.     /*
  2365.      * Show the window, and refresh it.
  2366.      */
  2367.     ShowWindow(window, cmdShow);
  2368.     UpdateWindow(window);
  2369.  
  2370.     /*
  2371.      * Enter the message processing loop.
  2372.      */
  2373.     while (GetMessage(&message, (HWND) NULL, (UINT) NULL, (UINT) NULL))
  2374.     {
  2375.         if ((PaintWnd == NULL) || !IsDialogMessage(PaintWnd, &message))
  2376.         {
  2377.             TranslateMessage(&message);
  2378.             DispatchMessage(&message);
  2379.         }
  2380.     }
  2381.  
  2382.     /*
  2383.      * Tidy up the 3D (RenderWare) components of the application.
  2384.      */
  2385.     TidyUp3D();
  2386.  
  2387.     return message.wParam;
  2388. }
  2389. /**********************************************************************/
  2390.