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
Wrap
Text File
|
1995-11-14
|
78KB
|
2,390 lines
/**********************************************************************
*
* File : rwpaint.c
*
* Abstract : RenderWare paint demo. A very simple RenderWare demonstration
* that allows the user to paint loaded objects.
*
**********************************************************************
*
* This file is a product of Criterion Software Ltd.
*
* This file is provided as is with no warranties of any kind and is
* provided without any obligation on Criterion Software Ltd. or
* Canon Inc. to assist in its use or modification.
*
* Criterion Software Ltd. will not, under any
* circumstances, be liable for any lost revenue or other damages arising
* from the use of this file.
*
* Copyright (c) 1995 Criterion Software Ltd.
* All Rights Reserved.
*
* RenderWare is a trademark of Canon Inc.
*
************************************************************************/
/**********************************************************************
*
* Header files.
*
**********************************************************************/
#ifdef __WINDOWS_386__
/*
* Watcom specific...
*/
#define INCLUDE_SHELLAPI_H
#define INCLUDE_COMMDLG_H
#include <windows.h>
#else /* __WINDOWS_386__ */
#include <windows.h>
#include <shellapi.h>
#include <commdlg.h>
#endif /* __WINDOWS_386__ */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rwlib.h"
#include "rwwin.h"
#include "common.h"
#include "resource.h"
#include "editmat.h"
/**********************************************************************
*
* Application constants.
*
**********************************************************************/
/*
* Class name for the MS Window's class.
*/
#define RWPAINT_CLASS_NAME "RwPaintClass"
#define RWPAINT_TOOL_CLASS_NAME "RwPaintToolClass"
#define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
#define TIMER_TICKS 20
#define TOOL_HEIGHT 34
#define BUTTON_OFFSET_X 10
#define BUTTON_OFFSET_Y 5
#define BUTTON_WIDTH 24
#define BUTTON_HEIGHT (24)
#define BUTTON_SPACE 6
#define MOVE_BUTTON 1
#define PAINT_BUTTON 2
#define MATERIAL_CHANGE (UINT)(WM_USER + 1)
/*
* Error dialog box title.
*/
#define ERROR_DIALOG_TITLE "RenderWare Painter Error"
/*
* Default size of the viewer's window.
*/
#define DEFAULT_WINDOW_WIDTH 400
#define DEFAULT_WINDOW_HEIGHT (400+TOOL_HEIGHT)
#define MAXIMUM_WINDOW_WIDTH 512
#define MAXIMUM_WINDOW_HEIGHT (512+TOOL_HEIGHT)
/*
* Default distance of the camera from the origin.
*/
#define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
/*
* Comment this line in if object labeling using User Draws is
* desired.
*/
#define USERDRAW_LABELSx
/**********************************************************************
*
* Type definitions.
*
**********************************************************************/
/*
* This enumerated type tells us what kind of action should be taken
* on mouse events.
*/
typedef enum
{
MMNoAction,
MMPanAndZoomCamera,
MMTiltCamera,
MMSpinClump,
MMDragClump,
MMDragClumpInZ,
MMPanLight
}
MMMode;
/*
* This enumerated type tells us what kind of animated action should
* be taken on timer messages. It is used to allow us to spin clumps
* and decals for a momentum effect.
*/
typedef enum
{
ANoAction,
ASpinClump
}
AMode;
typedef struct
{
char *UserDraw;
char *FullName;
}
CLUMP_DATA;
/**********************************************************************
*
* Application global variables.
*
**********************************************************************/
#ifdef USERDRAW_LABELS
/*
* Device status flag. Set to True if the device is using dibs and hence
* will not support userdraws
*/
static int UsingDib = TRUE;
#endif
/*
* Application instance handle for this application.
*/
static HANDLE AppInstance;
/*
* Global RenderWare object pointers. In this simple application we
* make use of only a single scene, camera and light.
*/
static RwScene *Scene = NULL;
static RwCamera *Camera = NULL;
static RwLight *Light = NULL;
/*
* This variable is used to remember which clump was last picked
* when spinning or dragging a clump on mouse move events.
* ClumpWithUserDraw is used to hold the 'current clump' when saving
*/
static RwClump *PickedClump = NULL;
static RwClump *ClumpWithUserDraw = NULL;
/*
* This matrix is used when spinning a clump, it is stored globally
* so that it is not necessary to re-build the matrix for each frame of
* animation.
*/
static RwMatrix4d *SpinMatrix = NULL;
#ifdef USERDRAW_LABELS
/*
* This is the user draw instance used to label the currently selected
* clump. It is added to clumps as they become selected.
*/
static RwUserDraw *UserDraw = NULL;
#endif
/*
* This variable tells us what kind of action to take on a mouse move.
* The action depends on the object that was picked when the mouse button
* went down, and on the selection of virtual keys that were depressed at
* that time. By default, no action is taken on mouse move.
*/
static MMMode MouseMoveMode = MMNoAction;
/*
* This variable tells us what kind of action to take on timer
* expiry. It is used to give both clumps and decals momentum, i.e. they
* will continue to spin after the user releases the mouse button. By
* default, no animation action is taken.
*/
static AMode AnimMode = ANoAction;
/*
* Global variables used to remember the last mouse X and Y coordinates
* when involved in a pan, zoom, drag or spin.
*/
static int LastX;
static int LastY;
/*
* Current distance the camera is from the origin. This is stored to
* help us pan and zoom the camera.
*/
static RwReal CameraDistance = DEFAULT_CAMERA_DISTANCE;
/*
* Current angle of tilt applied to the camera.
*/
static RwReal CameraTilt = CREAL(0.0);
#ifdef USERDRAW_LABELS
/*
* The font to be used in rendering user draws.
*/
static HFONT Font = (HFONT) 0;
#endif /* USERDRAW_LABELS */
/*
* This flag indicates whether the 3D components of the application
* have been successfully initialized as yet. It used to gaurd
* the message loop handler functions from being invoked before the
* 3D stuff is successfully initialized.
*/
static BOOL ThreeDInitialized = FALSE;
static RwMaterial *Material;
static int PaintMode = 0;
static int LeftButtonDown;
static HWND MainWindow;
static HWND But1;
static HWND But2;
static HWND PaintWnd;
/**********************************************************************
*
* Forward functions.
*
**********************************************************************/
extern LRESULT CALLBACK
MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
extern LRESULT CALLBACK
ToolWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
/**********************************************************************/
/*
* Perform any necessary MS Windows application initialization. Basically,
* this means registering the window class for this application.
*/
static BOOL
InitApplication(HANDLE instance)
{
WNDCLASS windowClass;
windowClass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
windowClass.lpfnWndProc = (WNDPROC) MainWndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instance;
windowClass.hIcon = LoadIcon(AppInstance, MAKEINTRESOURCE(RW_ICON));
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = "MENU_1";
windowClass.lpszClassName = RWPAINT_CLASS_NAME;
if (RegisterClass(&windowClass) == 0)
return FALSE;
windowClass.style = 0;
windowClass.lpfnWndProc = (WNDPROC) ToolWndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instance;
windowClass.hIcon = NULL;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = GetStockObject(LTGRAY_BRUSH);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = RWPAINT_TOOL_CLASS_NAME;
if (RegisterClass(&windowClass) == 0)
return FALSE;
return TRUE;
}
/**********************************************************************/
/*
* Perform any necessary initalization for this instance of the
* application. This simply means creating the applications main window.
*/
static HWND
InitInstance(HANDLE instance)
{
/*
* Create the MS Window's window instance for this application. The
* initial window size is given by DEFAULT_WINDOW_WIDTH and
* DEFAULT_WINDOW_HEIGHT. However, when we create the RenderWare
* camera in Init3D() we specify a maximum camera viewport which is
* the same size as the screen rather than the window. This is to
* allow the window to be maximed.
*/
return CreateWindow(RWPAINT_CLASS_NAME, "RenderWare Object Painter",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT,
NULL, NULL, instance, NULL);
}
/**********************************************************************/
/*
* Perform any necessary initalization for child windows (toolbar)
*/
static HWND
InitChildren(HINSTANCE instance, HWND parent)
{
HWND window;
window = CreateWindow(RWPAINT_TOOL_CLASS_NAME, "",
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, DEFAULT_WINDOW_WIDTH, TOOL_HEIGHT, parent,
NULL, instance, NULL);
But1 = CreateWindow("button", "Move",
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
BUTTON_OFFSET_X, BUTTON_OFFSET_Y, BUTTON_WIDTH, BUTTON_HEIGHT,
window, (HMENU) 1, instance, NULL);
But2 = CreateWindow("button", "Paint",
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
BUTTON_OFFSET_X + BUTTON_SPACE + BUTTON_WIDTH,
BUTTON_OFFSET_Y, BUTTON_WIDTH, BUTTON_HEIGHT,
window, (HMENU) 2, instance, NULL);
PostMessage(But1, BM_SETSTATE, 1, 0L);
PostMessage(But2, BM_SETSTATE, 0, 0L);
return window;
}
/**********************************************************************/
#ifdef USERDRAW_LABELS
/*
* This function is the call back function for the user draw used to
* label the current selected clump. It demonstrates user draws and
* the use of MS Windows GDI facilities such as TrueType fonts with
* RenderWare.
*/
static RwUserDraw *
UserDrawCallBack(RwUserDraw * userDraw, void *camImage, RwRect * rect,
void *data)
{
HDC devContext;
HPEN pen;
HPEN oldPen;
HBRUSH brush;
HBRUSH oldBrush;
HFONT oldFont;
int oldMode;
SIZE size;
/*
* If we have no user data, then there is no string to draw, so
* exit.
*/
if (!data)
return (userDraw);
/*
* If we are doing DIB rendering then we can't use GDI so exit.
*/
if (UsingDib)
return (userDraw);
/*
* Under MS Windows, camImage is, in fact, an HDC. I will make this
* explicit by assigning it to devContext.
*/
devContext = (HDC) camImage;
/*
* Render the background of the user draw.
*/
pen = GetStockObject(NULL_PEN);
brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
oldPen = SelectObject(devContext, pen);
oldBrush = SelectObject(devContext, brush);
Rectangle(devContext, (int) rect->x, (int) rect->y, (int) (rect->x + rect->w),
(int) (rect->y + rect->h));
SelectObject(devContext, oldBrush);
SelectObject(devContext, oldPen);
DeleteObject(brush);
/*
* Outline it in the traditional, MS Windows way.
*/
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
oldPen = SelectObject(devContext, pen);
MoveToEx(devContext, (int) rect->x, (int) (rect->y + (rect->h - 1)), NULL);
LineTo(devContext, (int) rect->x, (int) rect->y);
LineTo(devContext, (int) (rect->x + rect->w), (int) rect->y);
SelectObject(devContext, oldPen);
DeleteObject(pen);
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
oldPen = SelectObject(devContext, pen);
MoveToEx(devContext, (int) rect->x, (int) (rect->y + (rect->h - 1)), NULL);
LineTo(devContext, (int) (rect->x + (rect->w - 1)), (int) (rect->y + (rect->h - 1)));
LineTo(devContext, (int) (rect->x + (rect->w - 1)), (int) rect->y);
SelectObject(devContext, oldPen);
DeleteObject(pen);
/*
* If we found the font we wanted, select it in, otherwise simply
* use the default font.
*/
if (Font)
oldFont = SelectObject(devContext, Font);
oldMode = SetBkMode(devContext, TRANSPARENT);
/*
* Compute the text extent so we can centre it in the user draw
* rect.
*/
GetTextExtentPoint(devContext, data, strlen(data), &size);
TextOut(devContext, (int) (rect->x + ((rect->w / 2) - (size.cx / 2)))
,(int) (rect->y + ((rect->h / 2) - (size.cy / 2))),
data, strlen(data));
SetBkMode(devContext, oldMode);
/*
* Select the original font back, if necessary.
*/
if (Font)
SelectObject(devContext, oldFont);
return (userDraw);
}
#endif
/**********************************************************************/
/*
* This function initializes the 3D (i.e. RenderWare) components of the
* application. This function opens the RenderWare library, creates a
* camera, a scene, a light, a matrix for spinning and loads an initial
* clump.
*/
static BOOL
Init3D(HWND window)
{
char windowText[128];
char version[30];
#ifdef USERDRAW_LABELS
HDC dc;
LOGFONT logFont;
HFONT oldFont;
SIZE size;
#endif /* USERDRAW_LABELS */
/*
* Attempt to open (and initialize) the RenderWare library.
* Explicitly specifying the MS Windows driver. The MS Windows
* device driver requires the application's instance handle so
* this is passed in as the device specific parameter to RwOpen().
*/
if (!RwOpen("MSWindows", NULL))
{
if (RwGetError() == E_RW_NOMEM)
{
MessageBox(window,
"Insufficient memory to open the RenderWare library",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
else
{
MessageBox(window, "Error opening the RenderWare library",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
return FALSE;
}
/*
* Get the version number for display in the title bar.
*/
RwGetSystemInfo(rwVERSIONSTRING, version, sizeof (version));
wsprintf(windowText, "RenderWare %s Object Painter", version);
SetWindowText(window, windowText);
/*
* Determine if userdraw functinality is available and set the global flag
* accordingly
*/
#ifdef USERDRAW_LABELS
RwGetDeviceInfo(rwWINIMAGEISDIB, &UsingDib, sizeof (UsingDib));
#endif
/*
* Create the camera which will be used for rendering. The initial window
* size the application will create is given by DEFAULT_WINDOW_WIDTH and
* DEFAULT_WINDOW_HEIGHT which is currently 400x400. However, the camera
* will be created with a maximum viewport size which is the same size
* as the screen. This will allow the window to be maximized correctly.
*
* Also, under MS Windows, RwCreateCamera() needs a device context for
* the window which will be used as the destination by RwShowCameraImage()
* So a device context handle for the output window is passed in.
*/
Camera = RwCreateCamera(MAXIMUM_WINDOW_WIDTH, MAXIMUM_WINDOW_HEIGHT, NULL);
if (!Camera)
{
if (RwGetError() == E_RW_NOMEM)
{
MessageBox(window,
"Insufficient memory to create the RenderWare camera",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
else
{
MessageBox(window, "Error creating the RenderWare camera",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
RwClose();
return FALSE;
}
RwSetCameraViewwindow(Camera, CREAL(0.2), CREAL(0.2));
/*
* Set the camera's background color to black.
*/
RwSetCameraBackColor(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.8));
/*
* By default, the camera lies on the X-Z plane and points down Z
* into the screen. We shall retain the camera's orientation, but move
* the camera DEFAULT_CAMERA_DISTANCE units down Z away from the screen.
*/
RwTiltCamera(Camera, CameraTilt);
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
/*
* Create a scene which will contain the clumps to be rendered and the
* light or lights illuminating those clumps . In this very simple
* application it would be perfectly acceptable to use the default scene
* (as returned by RwDefaultScene()) for rendering. However, it is good
* practice to always create a scene which will be used for your rendering
* and only use the default scene as a bag for currently unused clumps and
* lights.
*/
Scene = RwCreateScene();
if (!Scene)
{
RwDestroyCamera(Camera);
RwClose();
MessageBox(window, "Error creating the RenderWare scene",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Our scene will be illuminated by a directional light. The illumination
* vector of the light is (0.1, -1.0, -0.5) and its brightness will be 1.0.
*/
Light = RwCreateLight(rwDIRECTIONAL, CREAL(0.1), CREAL(-1.0), CREAL(-0.5),
CREAL(1.0));
if (!Light)
{
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
MessageBox(window, "Error creating the RenderWare light",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Add the new light to our scene.
*/
RwAddLightToScene(Scene, Light);
/*
* Create the spin matrix.
*/
SpinMatrix = RwCreateMatrix();
if (!SpinMatrix)
{
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
MessageBox(window, "Error creating the RenderWare matrix",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
Material = RwCreateMaterial();
if (Material == NULL)
{
RwDestroyMatrix(SpinMatrix);
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
MessageBox(window, "Error creating the RenderWare material",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
#ifdef USERDRAW_LABELS
/*
* Get the font to be used for rendering the user draws. If we can't
* find the font, we simply ignore it as the rest of WinView is
* written to use the default font if Font is 0.
*/
memset(&logFont, 0, sizeof (LOGFONT));
logFont.lfHeight = 24;
strcpy(logFont.lfFaceName, "Arial Italic");
Font = CreateFontIndirect(&logFont);
/*
* We need to specify the size of the user draw which will label
* the currently selected clump. The label will be the leaf of
* the clump's filename which, under DOS, will be a maximum of
* eight characters. So the extent of a (wide) eight character
* string is computed with the desired font in place. The extent
* plus some padding is the size of the user draw.
*/
dc = GetDC(window);
if (Font)
oldFont = SelectObject(dc, Font);
GetTextExtentPoint(dc, "WWWWWWWW", 8, &size);
if (Font)
SelectObject(dc, oldFont);
ReleaseDC(window, dc);
/*
* Create the single user draw instance we will use to label the
* currently selected clump. We will simply add this user draw
* to a clump as they become selected. The size of the user draw
* will be based on the string extent computed above. The user
* draw will be centred just below the clump, to achieve this,
* the clump's alignment is rwALIGNTOP and the parent's alignment
* is rwALIGNBOTTOM (i.e. the top of the user draw is aligned with
* the bottom of the clump's bounding box).
*/
UserDraw = RwCreateUserDraw(rwBBOXALIGN, rwALIGNTOP, 0, 5,
size.cx + 5, size.cy + 5,
UserDrawCallBack);
if (!UserDraw)
{
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
MessageBox(window, "Error creating the RenderWare UserDraw",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
RwSetUserDrawParentAlignment(UserDraw, rwALIGNBOTTOM);
#endif
/*
* All the 3D components are now successfully initialized, so
* work can begin...
*/
ThreeDInitialized = TRUE;
return TRUE;
}
/**********************************************************************/
#ifdef USERDRAW_LABELS
/*
* This function creates a user data structure for a newely loaded
* clump read from the file with the given name.
*/
static RwClump *
CreateClumpUserData(RwClump * clump, char *fileName)
{
char name[_MAX_FNAME];
CLUMP_DATA *clumpData;
/*
* Get the leaf of the filename of the clump and store that
* away. The name is used to label clumps with user draws
* when they are selected.
*/
_splitpath(fileName, NULL, NULL, name, NULL);
/*
* Allocate store for the leaf of the filename.
*/
clumpData = (CLUMP_DATA *) malloc(sizeof (CLUMP_DATA));
if (clumpData == NULL)
return NULL;
clumpData->UserDraw = strdup(name);
clumpData->FullName = strdup(fileName);
/*
* Attach the user data structure to the clump.
*/
RwSetClumpData(clump, clumpData);
return clump;
}
#endif /* USERDRAW_LABELS */
/**********************************************************************/
#ifdef USERDRAW_LABELS
/*
* This function destroys a clump user data structure.
*/
static RwClump *
DestroyClumpUserData(RwClump * clump)
{
CLUMP_DATA *user;
user = RwGetClumpData(clump);
if (user != NULL)
{
free(user->UserDraw);
free(user->FullName);
free(user);
RwSetClumpData(clump, NULL);
}
return clump;
}
#endif /* USERDRAW_LABELS */
/**********************************************************************/
/*
* This function shuts the 3D (i.e. RenderWare) components of the library
* down in a polite fashion.
*/
static void
TidyUp3D()
{
#ifdef USERDRAW_LABELS
/*
* Destroy the user draw. If the user draw has been added to a clump
* when the clump is destroyed then the user draw will also be
* destroyed. But here, the user draw might not currently be added
* to a clump so we destroy it explicitly.
*/
RwDestroyUserDraw(UserDraw);
/*
* Destroy the font.
*/
if (Font)
DeleteObject(Font);
/*
* Destroying the scene will destroy all the clump in the scene
* automatically. However, the user data of the clump's will not be
* destroy, so destroy all the user data now.
*/
RwForAllClumpsInScene(Scene, DestroyClumpUserData);
#endif
/*
* Destroy the spin matrix.
*/
RwDestroyMatrix(SpinMatrix);
/*
* Destroy the scene. This will destroy the contents of the scene,
* i.e. any clumps and lights in that scene. In this case destroying
* the scene will destroy the light we created in Init3D, and any
* clumps we have loaded and not already destroyed.
*/
RwDestroyScene(Scene);
/*
* Destroy the camera.
*/
RwDestroyCamera(Camera);
/*
* Close the library. This will free up any internal resources and
* textures loaded.
*/
RwClose();
}
/**********************************************************************/
/*
* Attempt to load the file with the given file name as either a clump
* or a texture (in which case a decal clump is created and returned).
*/
static RwClump *
LoadClumpOrTexture(HWND window, char *fileName)
{
RwClump *clump;
RwTexture *texture;
char textureName[128];
char buffer[128];
HCURSOR oldCursor;
/*
* Attempt to load the file as a script file.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
clump = RwReadShape(fileName);
SetCursor(oldCursor);
if (!clump)
{
/*
* The load failed for some reason, this could be because of
* errors in the script file, insufficent memory, or the file
* containing garbage. We will now examine the error code
* returned to decide whether there was a script file error
* (in which case an error will be issued to the user), whether
* memory was exahusted (a different error is issued to the user),
* or the file contained garbage (in which case we will attempt
* to load the file as a texture instead of a script).
*/
switch (RwGetError())
{
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer, "Insufficient memory to load script %s",
fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
case E_RW_NOFILE:
case E_RW_BADOPEN:
case E_RW_RSPARSE:
case E_RW_RSREAD:
case E_RW_READ:
/*
* We will (somewhat optimistically) assume that if we
* had a read or parse error on the stream then the
* file is not a script file but a texture instead.
* So attempt to load a texture. Note, as we use
* RwGetNamedTexture() to get the texture, the texture
* will not be reread if it has already been loaded into
* RenderWare.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
texture = RwGetNamedTexture(fileName);
SetCursor(oldCursor);
if (texture)
{
/*
* The file was indeed a texture, so attempt to
* create a sprite clump using this texture.
*/
clump = RwCreateSprite(texture);
if (!clump)
{
RwGetTextureName(texture, textureName, sizeof (textureName));
sprintf(buffer,
"Error creating a sprite from texture %s", textureName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
}
}
else
{
/*
* The texture load failed, issue an error message
* giving the general nature of the problem.
*/
switch (RwGetError())
{
case E_RW_NOFILE:
case E_RW_BADOPEN:
/*
* Could not open the file...
*/
sprintf(buffer, "Error opening %s", fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer,
"Insufficient memory to load texture %s",
fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
default:
/*
* We will no enumerate all the errors, so simply
* issue a general failure report.
*/
sprintf(buffer,
"Error reading the file %s",
fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
}
}
break;
default:
/*
* On any other error we will assume there is an error in
* the script file.
*/
sprintf(buffer,
"Error reading the script file %s",
fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
}
}
if (clump && PaintWnd != NULL) /* ensure m.e is up to date */
MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
#ifdef USERDRAW_LABELS
/*
* Attach the leaf name of the file the clump was read from
* to the clump as user data. This is used as the label to
* render with the user draw.
*/
if (!CreateClumpUserData(clump, fileName))
{
RwDestroyClump(clump);
sprintf(buffer, "Insufficient memory to create clump %s", fileName);
MessageBox(window, buffer, ERROR_DIALOG_TITLE,
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
return NULL;
}
#endif /* USERDRAW_LABELS */
/*
* Return the resulting clump.
*/
return clump;
}
/**********************************************************************/
/*
* Parse the names of any rwx files on the command line, read those
* files and add them to out scene. This allows WinView to be associated
* with rwx files in the file manager and be started automatically when
* an rwx file is double clicked.
*/
static BOOL
ReadFromCommandLine(HWND window, char *cmdLine)
{
char *s1;
char *s2;
char fileName[_MAX_PATH];
RwClump *clump;
s1 = &cmdLine[0];
s2 = &fileName[0];
while (*s1)
{
/*
* Extract one file name from the list.
*/
while (*s1 && (*s1 != ' '))
*s2++ = *s1++;
*s2 = '\0';
if (fileName[0])
{
/*
* Attempt to load a clump or texture with that name. It is
* not necessary to issue an error message as LoadClumpOrTexture()
* will do that for us.
*/
clump = LoadClumpOrTexture(window, fileName);
if (clump)
RwAddClumpToScene(Scene, clump);
}
s2 = &fileName[0];
}
return TRUE;
}
/**********************************************************************/
static void
HandleGetMinMaxInfo(MINMAXINFO FAR * minmaxinfo)
{
minmaxinfo->ptMaxSize.x = MAXIMUM_WINDOW_WIDTH;
minmaxinfo->ptMaxSize.y = MAXIMUM_WINDOW_HEIGHT;
minmaxinfo->ptMaxTrackSize.x = MAXIMUM_WINDOW_WIDTH;
minmaxinfo->ptMaxTrackSize.y = MAXIMUM_WINDOW_HEIGHT;
}
/**********************************************************************/
static void
HandleSize(HWND window, int width, int height, HWND ToolBar)
{
/*
* The window has been resized, therefore, it is necessary to
* to modify the camera's viewport to be the same size as the
* client area of the window.
*/
RwSetCameraViewport(Camera, 0, TOOL_HEIGHT, width, height - TOOL_HEIGHT);
/*
* Keep toolbar the correct size
*/
MoveWindow(ToolBar, 0, 0, width, TOOL_HEIGHT, 1);
/*
* Ensure the entire window is repainted. Note, we stop MS Windows
* repainting the window as RenderWare paint the entire background.
*/
InvalidateRect(window, NULL, FALSE);
}
/**********************************************************************/
/*
* This functions handles the left mouse button going down. Its main
* job is to determine the kind of action to be taken when the mouse
* moves, such as spinning a clump, or panning the camera. This involves
* examining the virtual keys that were depressed when the mouse button
* went down and attempting to pick a clump under the mouse pointer
* position when the mouse went down.
*/
static void
HandleLeftButtonDown(HWND window, int x, int y, WPARAM vKeys)
{
RwPickRecord pick;
POINT pos;
/*
* If the left button is depressed anywhere in the client area of the
* window then the animated spin is cancelled.
*/
AnimMode = ANoAction;
LeftButtonDown = 1;
SetCapture(window);
/*
* If the control key is not depressed, then the mouse move action
* is based on whether an object is picked or not. Therefore,
* attempt to pick an object in the scene.
*/
if (RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
{
switch (pick.type)
{
case rwNAPICKOBJECT:
/*
* Clicked on the background, so there is no drag action,
* no picked clump and any animation action should be cancelled.
*/
MouseMoveMode = MMNoAction;
PickedClump = NULL;
break;
case rwPICKCLUMP:
/*
* If a clump was picked and both the shift and control
* virtual keys were depressed then we will destroy the
* clump (and hence remove it from the scene).
*/
if ((vKeys & (MK_CONTROL | MK_SHIFT)) ==
(MK_CONTROL | MK_SHIFT))
{
#ifdef USERDRAW_LABELS
/*
* If this clump owns the user draw, then we must
* remove the user draw from the clump before it
* is destroyed to prevent it being destroyed also.
*/
if (RwGetUserDrawOwner(UserDraw) == pick.object.clump.clump)
RwRemoveUserDrawFromClump(UserDraw);
/*
* Destroy the user data of the clump (if it has any) as user
* data is not destroyed automatically. The user data is the
* leaf file name of the clump added by LoadClumpOrTexture().
*/
DestroyClumpUserData(pick.object.clump.clump);
#endif
/*
* Destroy the clump (and remove it from the scene).
*/
RwDestroyClump(pick.object.clump.clump);
/*
* Force a redraw of the scene by invalidating
* the window's client area. Prevent MS Windows
* from erasing the window's client area as RenderWare
* will erase the background of the camera.
*/
InvalidateRect(window, NULL, FALSE);
/*
* The clump has been destroyed so there is no mouse
* move action, no picked clump and the animation action
* should be cancelled.
*/
MouseMoveMode = MMNoAction;
AnimMode = ANoAction;
PickedClump = NULL;
}
else
{
/*
* A clump was picked, so remember which clump was picked
* in order that it may be manipulated later when the
* mouse moves.
*/
PickedClump = pick.object.clump.clump;
/*
* Determine the exact nature of the manipulation of
* the clump on mouse move by examining the virtual key
* state. If the shift key was depressed, then drag
* the clump, otherwise, spin it.
*/
if (PaintMode == 0)
{
if (vKeys & MK_SHIFT)
MouseMoveMode = MMDragClump;
else if (vKeys & MK_CONTROL)
MouseMoveMode = MMDragClumpInZ;
else
MouseMoveMode = MMSpinClump;
#ifdef USERDRAW_LABELS
/*
* Add the user draw to this (the now selected clump) and
* set the user draw's data to the root clump's data (which
* is the leaf file name of the clump). The leaf name is
* attached to the root clump, rather than every clump in
* the hierarchy, so avoid the necessity of replicating
* the string in many different locations.
*/
ClumpWithUserDraw = PickedClump;
RwAddUserDrawToClump(PickedClump, UserDraw);
RwSetUserDrawData(UserDraw, ((CLUMP_DATA *)
RwGetClumpData(RwGetClumpRoot(PickedClump)))->
UserDraw);
#endif
}
else
{
if (vKeys & MK_SHIFT)
{
RwSphericalTexturizeClump(pick.object.clump.clump);
RwForAllPolygonsInClumpPointer(pick.object.clump.clump,
(RwPolygon3dFuncPointer) RwSetPolygonMaterial,
RwDuplicateMaterial(Material));
}
else
{
RwSetPolygonMaterial(pick.object.clump.polygon, RwDuplicateMaterial(Material));
}
}
}
break;
}
}
/*
* If any form of action is to be taken on mouse move, remember the
* the current x and y position of the mouse and capture future
* mouse movement.
*/
if (MouseMoveMode != MMNoAction)
{
pos.x = x;
pos.y = y;
ClientToScreen(window, &pos);
LastX = pos.x;
LastY = pos.y;
}
}
/**********************************************************************/
/*
* This functions handles the right mouse button going down. Its main
* job is to determine the kind of action to be taken when the mouse
* moves, such as stopping spinning a clump, or panning the camera.
*/
static void
HandleRightButtonDown(HWND window, int x, int y, WPARAM vKeys)
{
POINT pos;
if (vKeys & MK_CONTROL)
{
MouseMoveMode = MMPanLight;
}
else if (vKeys & MK_SHIFT)
{
MouseMoveMode = MMTiltCamera;
}
else
{
MouseMoveMode = MMPanAndZoomCamera;
}
/*
* If any form of action is to be taken on mouse move, remember the
* the current x and y position of the mouse and capture future
* mouse movement.
*/
if (MouseMoveMode != MMNoAction)
{
SetCapture(window);
pos.x = x;
pos.y = y;
ClientToScreen(window, &pos);
LastX = pos.x;
LastY = pos.y;
}
}
/**********************************************************************/
/*
* Handle a movement of the mouse. If a previous left mouse button
* down event has set a mouse move mode then this function will take
* the necessary actions. For example, pan and zooming the camera,
* panning the light, dragging or spinning a clump etc.
*/
static void
HandleMouseMove(HWND window, int x, int y)
{
HDC dc;
RwClump *parent;
RwMatrix4d *tmpMatrix;
RwMatrix4d *worldToLocal;
RwV3d up;
RwV3d right;
RwV3d at;
RwReal xDelta;
RwReal yDelta;
RwReal xAngle;
RwReal yAngle;
POINT pos;
RwPickRecord pick;
pos.x = x;
pos.y = y;
ClientToScreen(window, &pos);
if (PaintMode == 1 && LeftButtonDown && RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
{
if (pick.type == rwPICKCLUMP)
{
RwSetPolygonMaterial(pick.object.clump.polygon, RwDuplicateMaterial(Material));
return;
}
}
/*
* MouseMoveMode (previously set by HandleLeftButtonDown()) tells us what
* kind of action to perform.
*/
switch (MouseMoveMode)
{
case MMNoAction:
break;
case MMPanAndZoomCamera:
/*
* We are panning and zooming the camera. Movement of the
* mouse in the X direction will pan the camera about the
* origin of world coordinate space, and movement of the mouse
* in the Y direction will zoom the camera into and out of the
* the scene.
*/
/*
* Move the camera back to the origin, as we wish to pan about
* the origin of the world and not about the origin of the
* camera.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
/*
* Undo the tilt as we wish to pan about the world Y axis.
*/
RwTiltCamera(Camera, -CameraTilt);
/*
* Pan the camera by mouse X delta degrees.
*/
RwPanCamera(Camera, INT2REAL(LastX - pos.x));
/*
* Zoom the camera by changing the distance the camera
* is from the origin of the world by mouse Y delta divided
* by 10 units.
*/
CameraDistance = RAdd(CameraDistance,
RDiv(INT2REAL(LastY - pos.y), CREAL(10.0)));
/*
* Redo the tilt.
*/
RwTiltCamera(Camera, CameraTilt);
/*
* Move the camera out to its new distance from the world's
* origin.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
break;
case MMTiltCamera:
/*
* Move the camera back to the origin, as we wish to tilt about
* the origin of the world and not about the origin of the
* camera.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
/*
* Undo the existing tilt.
*/
RwTiltCamera(Camera, -CameraTilt);
/*
* Compute the new angle of tilt.
*/
CameraTilt = RAdd(CameraTilt, INT2REAL(LastY - pos.y));
/*
* Apply the new tilt.
*/
RwTiltCamera(Camera, CameraTilt);
/*
* Move the camera out to its new distance from the world's
* origin.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
break;
case MMSpinClump:
/*
* Compute the angles of spin (simply derived from the mouse move deltas).
*/
yAngle = INT2REAL(pos.x - LastX);
xAngle = INT2REAL(pos.y - LastY);
/*
* In WinView, a clump is spun about its own, local coordinate system,
* origin, rather than the origin of the world coordinate system. There
* are a number of ways this could be achieved, but the most convenient
* is to simply apply the rotations to the clump's joint (articulation)
* transform.
*
* A further important point is that the axes of rotation must be the
* camera's Look Up and Look Right vector rather than simply
* [CREAL(0.0), CREAL(1.0), CREAL(0.0)] and
* [CREAL(1.0), CREAL(0.0), CREAL(0.0)]. This ensures that, if the camera
* has been panned, tilted, revolved or transformed so that it no longer
* looks down the Z axis, user interaction will still operate correctly,
* i.e. moving the mouse to the left will spin the clump clockwise etc.
*
* Therefore, the first stage is to get the camera's Look Up and
* Look Right vectors.
*/
RwGetCameraLookUp(Camera, &up);
RwGetCameraLookRight(Camera, &right);
/*
* Unfortunately, rotation about the camera's Look Up and Look Right
* vectors is complicated if the clump being manipulated a child clump
* (i.e. not the root of a clump hierarchy). If this is the case, the
* camera's vectors have to be transformed into the coordinate space
* of the parent of the clump, being manipulated.
*/
parent = RwGetClumpParent(PickedClump);
if (parent != NULL)
{
/*
* Get a handle to a couple of temporary matrices.
*/
tmpMatrix = RwPushScratchMatrix();
worldToLocal = RwPushScratchMatrix();
/*
* Get the parent clump's LTM (which maps local coordinates to
* world space).
*/
RwGetClumpLTM(parent, tmpMatrix);
/*
* Invert it so that it maps world coordinates to the parent's
* local coordinate space.
*/
RwInvertMatrix(tmpMatrix, worldToLocal);
/*
* And transform the camera's vectors into local space.
*/
RwTransformVector(&up, worldToLocal);
RwNormalize(&up);
RwTransformVector(&right, worldToLocal);
RwNormalize(&right);
/*
* Discard the temporary matrices.
*/
RwPopScratchMatrix();
RwPopScratchMatrix();
}
/*
* Apply the rotations.
*/
RwRotateMatrix(SpinMatrix, up.x, up.y, up.z, yAngle, rwREPLACE);
RwRotateMatrix(SpinMatrix, right.x, right.y, right.z, xAngle, rwPOSTCONCAT);
/*
* Apply the resulting, composite transformation to the clump.
*/
RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
/*
* As the mouse has move enable the clump spin.
*/
AnimMode = ASpinClump;
break;
case MMDragClump:
RwPushScratchMatrix();
/*
* Compute the amount to translate the object by. This is simply
* derived from the mouse deltas scaled by some arbitrary quantity
* to prevent objects moving too "quickly".
*/
xDelta = RDiv(INT2REAL(pos.x - LastX), CREAL(50.0));
yDelta = RDiv(INT2REAL(LastY - pos.y), CREAL(50.0));
/*
* Similarly to spinning a clump we must take account of the camera's
* orientation when dragging a clump. This is done by translating along
* the camera's look up and look right vectors (scaled appropriately)
* rather than the clump's local axes.
*/
RwGetCameraLookRight(Camera, &right);
RwGetCameraLookUp(Camera, &up);
/*
* See the previous case for a description of why the following is
* necessary.
*/
parent = RwGetClumpParent(PickedClump);
if (parent != NULL)
{
tmpMatrix = RwPushScratchMatrix();
worldToLocal = RwPushScratchMatrix();
RwGetClumpLTM(parent, tmpMatrix);
RwInvertMatrix(tmpMatrix, worldToLocal);
RwTransformVector(&up, worldToLocal);
RwNormalize(&up);
RwTransformVector(&right, worldToLocal);
RwNormalize(&right);
RwPopScratchMatrix();
RwPopScratchMatrix();
}
/*
* Perform the translations.
*/
RwTranslateMatrix(RwScratchMatrix(),
RMul(right.x, xDelta), RMul(right.y, xDelta),
RMul(right.z, xDelta), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(),
RMul(up.x, yDelta), RMul(up.y, yDelta),
RMul(up.z, yDelta), rwPOSTCONCAT);
/*
* Apply the result, composite transform the clump.
*/
RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
case MMDragClumpInZ:
RwPushScratchMatrix();
/*
* Compute the amount to translate the object by. This is simply
* derived from the mouse deltas scaled by some arbitrary quantity
* to prevent objects moving too "quickly".
*/
yDelta = RDiv(INT2REAL(LastY - pos.y), CREAL(50.0));
/*
* Similarly to spinning a clump we must take account of the camera's
* orientation when dragging a clump. This is done by translating along
* the camera's look at (scaled appropriately) rather than the clump's
* local axes.
*/
RwGetCameraLookAt(Camera, &at);
/*
* See the case for MMSpinClump: for a description of why the
* following is necessary.
*/
parent = RwGetClumpParent(PickedClump);
if (parent != NULL)
{
tmpMatrix = RwPushScratchMatrix();
worldToLocal = RwPushScratchMatrix();
RwGetClumpLTM(parent, tmpMatrix);
RwInvertMatrix(tmpMatrix, worldToLocal);
RwTransformVector(&at, worldToLocal);
RwNormalize(&at);
RwPopScratchMatrix();
RwPopScratchMatrix();
}
/*
* Perform the translation.
*/
RwTranslateMatrix(RwScratchMatrix(),
RMul(at.x, yDelta), RMul(at.y, yDelta),
RMul(at.z, yDelta), rwREPLACE);
/*
* Apply the resulting, composite transform the clump.
*/
RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
case MMPanLight:
/*
* We are panning the light about the origin. We will rotate
* the light about the Y axis for movements of the mouse in
* X and rotate the light about the X axis for movements of
* the mouse in Y.
*/
RwPushScratchMatrix();
/*
* Replace the CTM with a rotation matrix about Y. The numbers
* of degrees of rotation is given by the mouse X delta.
*/
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0),
INT2REAL(LastX - pos.x), rwREPLACE);
/*
* Postconcat another rotation onto the CTM. The new rotation
* is a rotation about X, the angle being given by the mouse
* Y delta.
*/
RwRotateMatrix(RwScratchMatrix(), CREAL(1.0), CREAL(0.0), CREAL(0.0),
INT2REAL(LastY - pos.y), rwPOSTCONCAT);
/*
* Transform the light by the resultant rotations.
*/
RwTransformLight(Light, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
}
/*
* See the description of HandlePaint() for a description of this
* common RenderWare programming cliche for rendering a scene and
* copying it to the display.
*/
RwInvalidateCameraViewport(Camera);
RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
RwClearCameraViewport(Camera);
RwRenderScene(Scene);
RwEndCameraUpdate(Camera);
dc = GetDC(window);
RwShowCameraImage(Camera, (void *) (DWORD) dc);
ReleaseDC(window, dc);
/*
* Remember the current X and Y for next time.
*/
LastX = pos.x;
LastY = pos.y;
}
/**********************************************************************/
/*
* Handle the left mouse button comming back up. The basic action is
* to turn of mouse move actions, and release mouse capture. Also,
* if the previous mouse move action was a spin then this function will
* turn on animation to continue the spin (and the objects "momentum").
*/
static void
HandleLeftButtonUp()
{
/*
* If we were engaged in a mouse move action and the button has come
* back up, then terminate the action and release mouse capture.
*/
LeftButtonDown = 0;
ReleaseCapture();
if (MouseMoveMode != MMNoAction)
{
MouseMoveMode = MMNoAction;
}
}
/**********************************************************************/
/*
* Handle the left mouse button comming back up. The basic action is
* to turn of mouse move actions, and release mouse capture. Also,
* if the previous mouse move action was a spin then this function will
* turn on animation to continue the spin (and the objects "momentum").
*/
static void
HandleRightButtonUp()
{
/*
* If we were engaged in a mouse move action and the button has come
* back up, then terminate the action and release mouse capture.
*/
if (MouseMoveMode != MMNoAction)
{
MouseMoveMode = MMNoAction;
ReleaseCapture();
}
}
/**********************************************************************/
/*
* Handle an MS Window's drop message, this function will attempt to
* load the files dropped as RenderWare clumps. If that fails, it will
* then attempt to load the files as textures, which will the be used
* to create decal clumps.
*/
static void
HandleDrop(HWND window, HDROP drop)
{
RwClump *clump;
char path[_MAX_PATH];
int i;
/*
* A file has been dropped onto the window. This function assumes
* the file is either a RenderWare script file or a texture file.
* Taking the path of least resistance, it does not attempt to
* determine which is which, it simply tries to load it as
* a script file, and if that fails it attempts to load it as
* a texture. Not particularly elegant, but simple.
*/
for (i = DragQueryFile(drop, (UINT) - 1, path, _MAX_PATH) - 1; i >= 0; --i)
{
DragQueryFile(drop, (UINT) i, path, _MAX_PATH);
clump = LoadClumpOrTexture(window, path);
if (clump)
{
/*
* A clump was successfully loaded, so add the new clump to
* our scene.
*/
RwAddClumpToScene(Scene, clump);
/*
* Force a redraw of the window, to ensure the new clump is
* visible.
*/
InvalidateRect(window, NULL, FALSE);
}
}
DragFinish(drop);
}
/**********************************************************************/
/*
* Handle window paint messages by rerendering the entire scene and
* copying the camera's entire viewport to the display.
*/
static void
HandlePaint(HWND window)
{
HDC dc;
PAINTSTRUCT paintStruct;
dc = BeginPaint(window, &paintStruct);
/*
* As we are repainting the entire window we invalidate the
* camera's viewport to ensure the whole window gets updated
* (and not simply the parts that will be damaged by the render
* operation).
*/
RwInvalidateCameraViewport(Camera);
/*
* The following is a common RenderWare programming cliche.
* It performs a render of a scene into a camera and copies
* the resultant image to the display. Note, that this segment
* of code will re-render the scene and then copy it to the
* display. This is not strictly necessary. All that really needs
* be done for a window repaint is to copy the camera's image buffer
* (containing the results of the last render) to the display.
* However, in this simple application, several situations which
* require the scene to be re-rendered (for example, when adding
* a clump), perform a re-render simply by invalidating the
* window's client area, and hence invoking this code.
*/
RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
/*
* Clear the areas of the camera's viewport which were damaged
* last time round. If this call is not made, ghost images of
* previous rendering will remain.
*/
RwClearCameraViewport(Camera);
/*
* Re-render the entire scene.
*/
RwRenderScene(Scene);
/*
* Perform all necessary housekeeping after rendering is complete.
* After this call, the camera's image buffer will be ready to be
* copied to the display.
*/
RwEndCameraUpdate(Camera);
/*
* Copy the camera's image buffer to the output window. Under
* MS Windows, RenderWare requires both the window handle of the
* output window and a device context for the client area of the
* window. Therefore, a LONG is created which holds both the
* window handle and device context of the output window. This
* LONG is then passed in as the device specific parameter of
* the RwShowCameraImage() call.
*/
RwShowCameraImage(Camera, (void *) (DWORD) dc);
EndPaint(window, &paintStruct);
}
/**********************************************************************/
/*
* Handle MS Window's timer expiry. This function will perform any
* animation actions necessary, including spinning clumps and animating
* textures.
*/
static void
HandleTimer(HWND window)
{
HDC devContext;
if (PickedClump && (AnimMode != ANoAction) && (MouseMoveMode == MMNoAction))
{
switch (AnimMode)
{
case ASpinClump:
/*
* Spin the last clump picked by the last computed spin matrix.
*/
RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
break;
}
}
/*
* Animate textures. Enumerate over all the textures in the texture
* dictionary calling RwTextureNextFrame to bump the current frame
* pointer.
*/
RwForAllNamedTextures(RwTextureNextFrame);
/*
* See the description of HandlePaint() for a description of this common
* RenderWare cliche for rendering a scene and copying it to the display.
*/
RwBeginCameraUpdate(Camera, (void *) (DWORD) window);
RwClearCameraViewport(Camera);
RwRenderScene(Scene);
RwEndCameraUpdate(Camera);
devContext = GetDC(window);
RwShowCameraImage(Camera, (void *) (DWORD) devContext);
ReleaseDC(window, devContext);
}
/**********************************************************************/
/*
* The window procedure for the toolbar
*/
LRESULT CALLBACK
ToolWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC devContext;
RECT rect;
HPEN pen, oldPen;
switch (message)
{
case WM_COMMAND:
switch (HIWORD(lParam))
{
case BN_CLICKED:
switch (wParam)
{
case 1:
SendMessage(But1, BM_SETSTATE, 1, 0L);
SendMessage(But2, BM_SETSTATE, 0, 0L);
PaintMode = 0;
EnableWindow(But1, FALSE);
EnableWindow(But2, TRUE);
SetFocus(GetParent(But1));
return 0;
case 2:
SendMessage(But1, BM_SETSTATE, 0, 0L);
SendMessage(But2, BM_SETSTATE, 1, 0L);
EnableWindow(But1, TRUE);
EnableWindow(But2, FALSE);
PaintMode = 1;
PaintWnd = MaterialEditor(Material, AppInstance, MainWindow,
MainWindow, MATERIAL_CHANGE);
SetFocus(GetParent(But2));
PostMessage(PaintWnd, WM_SYSCOMMAND, SC_RESTORE, 0L);
return 0;
}
break;
}
break;
case WM_PAINT:
devContext = BeginPaint(window, &paintStruct);
GetClientRect(window, &rect);
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
oldPen = SelectObject(devContext, pen);
MoveToEx(devContext, rect.left, rect.bottom, NULL);
LineTo(devContext, rect.left, rect.top);
LineTo(devContext, rect.right, rect.top);
SelectObject(devContext, oldPen);
DeleteObject(pen);
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
oldPen = SelectObject(devContext, pen);
LineTo(devContext, rect.right, rect.bottom - 1);
LineTo(devContext, rect.left, rect.bottom - 1);
SelectObject(devContext, oldPen);
DeleteObject(pen);
EndPaint(window, &paintStruct);
return 0;
case WM_MEASUREITEM:
{
MEASUREITEMSTRUCT FAR *m;
#ifdef __WINDOWS_386__
m = (MEASUREITEMSTRUCT FAR *) MK_FP32((void *) lParam);
#else /* __WINDOWS_386__ */
m = (MEASUREITEMSTRUCT FAR *) lParam;
#endif /* __WINDOWS_386__ */
m->itemWidth = 24;
m->itemHeight = 24;
return TRUE;
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT far *pDis;
HDC hdcMemory;
HBITMAP hbmpMyBitmap, hbmpOld;
BITMAP bm;
#ifdef __WINDOWS_386__
pDis = (DRAWITEMSTRUCT FAR *) MK_FP32((void *) lParam);
#else /* __WINDOWS_386__ */
pDis = (DRAWITEMSTRUCT FAR *) lParam;
#endif /* __WINDOWS_386__ */
hbmpMyBitmap = LoadBitmap(
#ifdef WIN32
(HINSTANCE) GetWindowLong(MainWindow, GWL_HINSTANCE),
#else
(HINSTANCE) GetWindowWord(MainWindow, GWW_HINSTANCE),
#endif
MAKEINTRESOURCE
(pDis->hwndItem == But1 ?
moveup : paintup));
GetObject(hbmpMyBitmap, sizeof (BITMAP), &bm);
hdcMemory = CreateCompatibleDC(pDis->hDC);
hbmpOld = SelectObject(hdcMemory, hbmpMyBitmap);
BitBlt(pDis->hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMemory, 0, 0,
SendMessage(pDis->hwndItem, BM_GETSTATE, 0, 0) & 4 ?
NOTSRCCOPY : SRCCOPY);
SelectObject(hdcMemory, hbmpOld);
DeleteDC(hdcMemory);
DeleteObject(hbmpMyBitmap);
return TRUE;
}
}
return DefWindowProc(window, message, wParam, lParam);
}
/**********************************************************************/
static void
LoadFile(HWND window)
{
OPENFILENAME ofn;
static char path[_MAX_PATH];
static char initDir[_MAX_PATH];
static char file[20];
static char ext[10];
RwClump *clump;
memset(&ofn, 0, sizeof (ofn));
if (initDir[0] == '\0')
{
strcpy(initDir, getenv("RWSHAPEPATH"));
if (strchr(initDir, ';'))
*strchr(initDir, ';') = '\0';
}
sprintf(path, "%s%s", file, ext);
ofn.lStructSize = sizeof (ofn);
ofn.hwndOwner = window;
ofn.lpstrFile = path;
ofn.nMaxFile = sizeof (path);
ofn.lpstrInitialDir = initDir;
ofn.lpstrFilter = "Scripts (*.rwx)\0*.rwx\0"
"All Files (*.*)\0*.*\0";
ofn.nFilterIndex = 1;
ofn.lpstrTitle = "Load Object";
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
KillTimer(window, 1);
if (GetOpenFileName(&ofn))
{
clump = LoadClumpOrTexture(window, path);
if (clump != NULL)
{
RwAddClumpToScene(Scene, clump);
InvalidateRect(window, NULL, FALSE);
}
_splitpath(path, initDir, initDir + 2, file, ext);
}
SetTimer(window, 1, TIMER_TICKS, NULL);
}
/**********************************************************************/
static void
SaveFile(HWND window, char *fullPath)
{
OPENFILENAME ofn;
static char path[_MAX_PATH];
static char initDir[_MAX_PATH];
static char file[20];
static char ext[10];
CLUMP_DATA *p;
memset(&ofn, 0, sizeof (ofn));
_splitpath(fullPath, initDir, initDir + 2, file, ext);
sprintf(path, "%s%s", file, ext);
ofn.lStructSize = sizeof (ofn);
ofn.hwndOwner = window;
ofn.lpstrFile = path;
ofn.nMaxFile = sizeof (path);
ofn.lpstrInitialDir = initDir;
ofn.lpstrFilter = "Scripts (*.rwx)\0*.rwx\0"
"All Files (*.*)\0*.*\0";
ofn.nFilterIndex = 1;
ofn.lpstrTitle = "Save Object";
ofn.lpstrDefExt = "rwx";
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
KillTimer(window, 1);
if (GetSaveFileName(&ofn))
{
if (RwWriteShape(path, ClumpWithUserDraw) == 0)
{
MessageBox(window, "Unable to save file", NULL, MB_ICONEXCLAMATION | MB_OK);
}
else
{
p = RwGetClumpData(ClumpWithUserDraw);
if (p != NULL)
{
free(p->FullName);
free(p->UserDraw);
p->FullName = strdup(path);
if (p->FullName != NULL)
_splitpath(p->FullName, NULL, NULL, path, NULL);
p->UserDraw = strdup(path);
#ifdef USERDRAW_LABELS
if (p->UserDraw != NULL)
RwSetUserDrawData(UserDraw, p->UserDraw);
#endif /* USERDRAW_LABELS */
InvalidateRect(window, NULL, FALSE);
}
_splitpath(path, initDir, initDir + 2, file, ext);
}
}
SetTimer(window, 1, TIMER_TICKS, NULL);
}
/**********************************************************************/
static void
HandleCommand(HWND window, WPARAM wParam)
{
char buf[_MAX_PATH + 100];
switch (wParam)
{
case MENU_LOAD:
LoadFile(window);
return;
case MENU_SAVE:
if (ClumpWithUserDraw != NULL)
{
if (!RwWriteShape(((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName, ClumpWithUserDraw))
{
sprintf(buf, "Error writing script file %s", ((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName);
MessageBox(window, buf, NULL, MB_ICONEXCLAMATION | MB_OK | MB_APPLMODAL);
}
}
return;
case MENU_SAVEAS:
if (ClumpWithUserDraw != NULL)
SaveFile(window, ((CLUMP_DATA *) RwGetClumpData(ClumpWithUserDraw))->FullName);
return;
case MENU_EXIT:
PostQuitMessage(0);
return;
}
}
/**********************************************************************/
/*
* Double clicking selects the paint window with the material under it.
*/
static void
HandleDoubleClick(HWND window, int x, int y)
{
RwPickRecord pick;
if (RwPickScene(Scene, x, y - TOOL_HEIGHT, Camera, &pick))
{
if (pick.type == rwPICKCLUMP)
{
RwCopyMaterial(RwGetPolygonMaterial(pick.object.clump.polygon), Material);
PaintWnd = MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
}
}
}
/**********************************************************************/
/*
* The window procedure for this application's window.
*/
LRESULT CALLBACK
MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
#ifdef WIN32
POINTS point;
#else /* WIN32 */
POINT point;
#endif /* WIN32 */
static HWND ToolBar;
switch (message)
{
case WM_CREATE:
/*
* Clumps are loaded into the scene by drag and drop.
* So make this window a drop site.
*/
DragAcceptFiles(window, TRUE);
/*
* In this application, 3D animation is driven by window's
* timer messages, so turn a timer on.
*/
MainWindow = window;
ToolBar = InitChildren(AppInstance, window);
SetTimer(window, 1, 20, NULL);
return 0L;
case WM_COMMAND:
HandleCommand(window, wParam);
return 0L;
case WM_GETMINMAXINFO:
#ifdef __WINDOWS_386__
HandleGetMinMaxInfo((MINMAXINFO FAR *) MK_FP32((void *) lParam));
#else /* __WINDOWS_386__ */
HandleGetMinMaxInfo((MINMAXINFO FAR *) lParam);
#endif /* __WINDOWS_386__ */
return 0L;
case WM_SIZE:
if (ThreeDInitialized)
{
#ifdef WIN32
point = MAKEPOINTS(lParam);
#else /* WIN32 */
point = MAKEPOINT(lParam);
#endif /* WIN32 */
HandleSize(window, point.x, point.y, ToolBar);
}
return 0L;
case WM_DROPFILES:
if (ThreeDInitialized)
HandleDrop(window, (HDROP) wParam);
return 0L;
case WM_LBUTTONDOWN:
if (ThreeDInitialized)
#ifdef WIN32
point = MAKEPOINTS(lParam);
#else /* WIN32 */
point = MAKEPOINT(lParam);
#endif /* WIN32 */
HandleLeftButtonDown(window, point.x, point.y, wParam);
return 0L;
case WM_RBUTTONDOWN:
if (ThreeDInitialized)
#ifdef WIN32
point = MAKEPOINTS(lParam);
#else /* WIN32 */
point = MAKEPOINT(lParam);
#endif /* WIN32 */
HandleRightButtonDown(window, point.x, point.y, wParam);
return 0L;
case WM_MOUSEMOVE:
if (ThreeDInitialized)
{
if (MouseMoveMode != MMNoAction || PaintMode == 1)
#ifdef WIN32
point = MAKEPOINTS(lParam);
#else /* WIN32 */
point = MAKEPOINT(lParam);
#endif /* WIN32 */
HandleMouseMove(window, point.x, point.y);
}
return 0L;
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
if (ThreeDInitialized)
{
#ifdef WIN32
point = MAKEPOINTS(lParam);
#else /* WIN32 */
point = MAKEPOINT(lParam);
#endif /* WIN32 */
HandleDoubleClick(window, point.x, point.y);
}
return 0L;
case WM_LBUTTONUP:
if (ThreeDInitialized)
HandleLeftButtonUp();
return 0L;
case WM_RBUTTONUP:
if (ThreeDInitialized)
HandleRightButtonUp();
return 0L;
case WM_PAINT:
if (ThreeDInitialized)
HandlePaint(window);
return 0L;
case WM_TIMER:
if (ThreeDInitialized)
HandleTimer(window);
return 0L;
case MATERIAL_CHANGE:
/*
* code here for material change notification
* lParam is pointer to material (the same as went in)
* or null if window is closing
* wParam is a bitfield of changed attributes.
*/
if (lParam == 0)
PaintWnd = NULL;
return 0L;
case WM_CHAR:
if ((wParam == 'p') || (wParam == 'P'))
{
PaintWnd = MaterialEditor(Material, AppInstance, window, window, MATERIAL_CHANGE);
return 0L;
}
break;
case WM_DESTROY:
/*
* The window is going away so it is no longer a drop site.
*/
DragAcceptFiles(window, FALSE);
/*
* Turn the timer off.
*/
KillTimer(window, 1);
/*
* Quit message handling.
*/
PostQuitMessage(0);
return 0L;
}
/*
* Let windows handle all other messages.
*/
return DefWindowProc(window, message, wParam, lParam);
}
/**********************************************************************/
/*
* MS Windows application entry point.
*/
int PASCAL
WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
{
MSG message;
HWND window;
/*
* Remember the application instance in a global variable for later use.
*/
AppInstance = instance;
if (prevInstance)
{
/*
* Only allow one viewer application to run at any one time.
*/
MessageBox((HWND) 0, "WinPaint is already running...",
ERROR_DIALOG_TITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Register the window class.
*/
if (!InitApplication(instance))
return FALSE;
/*
* Create the window.
*/
window = InitInstance(instance);
if (window == NULL)
return FALSE;
CheckDisplayDepth(window);
/*
* Initialize the 3D (RenderWare) components of the app.
*/
if (!Init3D(window))
{
DestroyWindow(window);
return FALSE;
}
/*
* Parse any command line parameters.
*/
if (!ReadFromCommandLine(window, cmdLine))
{
TidyUp3D();
DestroyWindow(window);
return FALSE;
}
/*
* Show the window, and refresh it.
*/
ShowWindow(window, cmdShow);
UpdateWindow(window);
/*
* Enter the message processing loop.
*/
while (GetMessage(&message, (HWND) NULL, (UINT) NULL, (UINT) NULL))
{
if ((PaintWnd == NULL) || !IsDialogMessage(PaintWnd, &message))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
}
/*
* Tidy up the 3D (RenderWare) components of the application.
*/
TidyUp3D();
return message.wParam;
}
/**********************************************************************/