home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Learn 3D Graphics Programming on the PC
/
Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso
/
rwwin
/
rwview.c_
/
rwview.bin
Wrap
Text File
|
1995-11-14
|
114KB
|
3,344 lines
/**********************************************************************
*
* File : rwview.c
*
* Abstract : A very simple, sample RenderWare application for
* Microsoft Windows. This application has very little
* functionality, but is simply intended as a demonstration
* of how to use the RenderWare API.
*
* This application had been written to be compatible with
* both the fixed and floating-point versions of the
* RenderWare library, i.e., it uses the macros CREAL,
* INT2REAL, RAdd, RDiv, RSub etc. If your application is
* intended for the floating-point version of the library
* only these macros are not necessary.
*
* Please note that this application is intended for
* demonstration purposes only. No support will be
* provided for this code and it comes with no warranty.
*
**********************************************************************
*
* Building RWVIEW.EXE...
*
**********************************************************************
*
* Watcom C/C++ 9.5 and 10.0 and RenderWare V1.4 static library
*
* This application can be built using the Watcom compiler and the
* fixed-point RenderWare v1.35 library as follows:
* wcc386p /I=\rw\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
* /DRWFIXED /fo=rwview.obj rwview.c
* wlink option caseexact option stack=32768 name rwview system win386
* file rwview.obj library \rw\lib\rwwrxp.lib
* wbind rwview -n
*
* (Note for Watcom 10.0 substitue the command wcc386p with wcc386).
*
* This application can be built using the Watcom compiler and the
* floating-point RenderWare v1.35 library as follows:
* wcc386p /I=\rw\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
* /DRWFLOAT /fo=rwview.obj rwview.c
* wlink option caseexact option stack=32768 name rwview system win386
* file rwview.obj library \rw\lib\rwwrlp.lib
* wbind rwview -n
*
* (Note for Watcom 10.0 substitue the command wcc386p with wcc386).
*
**********************************************************************
*
* Microsoft Visual C++ 1.5 and RenderWare V1.4 DLL
*
* The fixed-point rwview.exe project for Visual C++ 1.5 must have
* the following options set:
*
* Compiler Options:
* Memory Model
* Model: Large
* Preprocessos
* Symbols: RWFIXED
* Include path: \rwwin\include
*
* Linker Options:
* Input
* Libraries: ..., \rwwin\lib\rwxv.lib
* Windows libraries
* SHELL
*
* (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
* in your search path).
*
* The floating-point rwview.exe project for Visual C++ 1.5 must have
* the following options set:
*
* Compiler Options:
* Memory Model
* Model: Large
* Preprocessos
* Symbols: RWFLOAT
* Include path: \rwwin\include
*
* Linker Options:
* Input
* Libraries: ..., \rwwin\lib\rwlv.lib
* Windows libraries
* SHELL
*
* (In addition the fixed point RenderWare V1.4 DLL rwl.dll must be
* in your search path).
*
**********************************************************************
*
* Borland C++ 4.0 and RenderWare V1.4 DLL
*
* The fixed-point rwview.exe project for Borland C++ 4.0 must have
* the following options set:
*
* New Project:
* Target Type Application (.EXE)
* Platform Windows 3.x (16)
* Target Model Large
* Standard Libraries
* OWL Unchecked
* Class Library Unchecked
* Runtime Checked
* BWCC Unchecked
* Dynamic Checked
* Static Unchecked
* Advanced
* .cpp Node Unchecked
* .c Node Checked
* No Source Node Unchecked
* .rc node Unchecked
* .def node Unchecked
*
* Project Options:
* Directories
* Include ...; \rwwin\include
* Compiler
* Defines ...; RWFIXED
* Floating Point No Floating Point must NOT be checked.
* 16-Bit Compiler
* Calling Conventions C
* Memory Model Large
*
* You must add the node \rwwin\lib\rwxb.lib to your project.
*
* (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
* in your search path).
*
* The floating-point rwview.exe project for Borland C++ 4.0 must have
* the following options set:
*
* New Project:
* Target Type Application (.EXE)
* Platform Windows 3.x (16)
* Target Model Large
* Standard Libraries
* OWL Unchecked
* Class Library Unchecked
* Runtime Checked
* BWCC Unchecked
* Dynamic Checked
* Static Unchecked
* Advanced
* .cpp Node Unchecked
* .c Node Checked
* No Source Node Unchecked
* .rc node Unchecked
* .def node Unchecked
*
* Project Options:
* Directories
* Include ...; \rwwin\include
* Compiler
* Defines ...; RWFLOAT
* Floating Point No Floating Point must NOT be checked.
* 16-Bit Compiler
* Calling Conventions C
* Memory Model Large
*
* You must add the node \rwwin\lib\rwlb.lib to your project.
*
* (In addition the fixed point RenderWare V1.4 DLL rwx.dll must be
* in your search path).
*
**********************************************************************
*
* 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) 1994, 1995 Criterion Software Ltd.
* All Rights Reserved.
*
* RenderWare is a trademark of Canon Inc.
*
**********************************************************************/
/**********************************************************************
*
* Header files.
*
**********************************************************************/
#if defined(__WINDOWS_386__)
/*
* Watcom strangeness...
*/
#define INCLUDE_SHELLAPI_H
#define INCLUDE_COMMDLG_H
#include <windows.h>
#else
#include <windows.h>
#include <shellapi.h>
#include <commdlg.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <rwlib.h>
#include <rwwin31.h>
#include "resource.h"
#include "common.h"
#include "pal.h"
#include "settings.h"
#include "object.h"
#include "popmenu.h"
#include "lightprp.h"
#include "clumpprp.h"
#include "color.h"
#include "pick.h"
/**********************************************************************
*
* Application constants.
*
**********************************************************************/
/*
* Class name for the MS Window's window class.
*/
#define RWVIEWCLASSNAME "RwViewClass"
/*
* Window title.
*/
#define WINDOWTITLE "RenderWare 3D Object Viewer"
/*
* Default size of the viewer's window.
*/
#define DEFAULTWINDOWWIDTH 440
#define DEFAULTWINDOWHEIGHT 450
#if defined(CONSTRAIN_SIZE)
/*
* Maximum size of the viewer's window.
*/
#define MAXIMUMWINDOWWIDTH 512
#define MAXIMUMWINDOWHEIGHT 522
#endif
/*
* The default size of the viewwindow.
*/
#define DEFAULTVIEWWINDOWSIZE CREAL(0.4)
/*
* Default distance of the camera from the origin.
*/
#define DEFAULTCAMERADISTANCE CREAL(7.0)
/**********************************************************************
*
* Forward functions.
*
**********************************************************************/
BOOL CALLBACK
SearchPathDlgProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK
AboutBoxDlgProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK
MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
/**********************************************************************
*
* Type definitions.
*
**********************************************************************/
/*
* This enumerated type tells us what kind of action should be taken
* on mouse events.
*/
typedef enum
{
mmNoAction,
mmScrollBackdrop,
mmMoveAndTurnCamera,
mmTiltCamera,
mmSpinClump,
mmDragClump,
mmDragClumpInZ
} MMMode;
/**********************************************************************
*
* Application global variables.
*
**********************************************************************/
/*
* 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;
/*
* This variable is used to remember which clump was last picked
* when spinning or dragging a clump on mouse move events.
*/
static RwClump *SelectedClump = NULL;
/*
* This is the clump used to highlight the currently picked clump.
*/
static RwClump *HighlightClump = 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;
/*
* This variable tells us whether the clump should be spun with
* momentum. Why do we need this as well as the Momentum setting?
* Well Momentum is TRUE if an object can have momentum (even if it
* does not currently have moment where SpinClump is TRUE only if we
* are actually spinning a clump with momentum currently.
*/
static BOOL SpinClump = NULL;
/*
* 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;
/*
* 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;
/*
* This stores the offset from the initial pick position to the origin
* of the picked clump. It used so that we drag objects by their picked
* position rather than by their origin.
*/
static RwV3d PickOffset;
/*
* Current animation frame number. This is incremented each time a
* frame is drawn on timer expiry. The purpose of this variable is
* to enable us to periodically (based on the number of frames) call
* RwOrthoNormalizeMatrix() to correct any errors which have crept
* into the clump's joint matrix because of the inevitable
* restrictions on the accuracy of fixed-point numbers. See
* HandleTimer for more details.
*/
static int FrameNumber = 0;
/*
* This flag indicates whether the 3D components of the application
* have been successfully initialized as yet. It is used to guard
* the message loop handler functions from being invoked before the
* 3D components of the application are successfully initialized.
*/
static BOOL RwIsOpen = FALSE;
/*
* This is the last filename opened by the viewer. This is stored
* so that we can initialize the file open dialog with the last file
* loaded each time.
*/
static char LastFileName[_MAX_PATH];
/**********************************************************************
*
* Functions.
*
**********************************************************************/
/**********************************************************************/
/*
* Perform any necessary Windows application initialization. Basically,
* this means registering the window class for this application.
*/
static BOOL
InitApplication(HANDLE instance)
{
WNDCLASS wndClass;
wndClass.style = CS_BYTEALIGNWINDOW;
wndClass.lpfnWndProc = (WNDPROC)MainWndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = instance;
wndClass.hIcon = NULL;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = NULL;
wndClass.lpszMenuName = MAKEINTRESOURCE(IDR_RWVIEW_MENU);
wndClass.lpszClassName = RWVIEWCLASSNAME;
if (RegisterClass(&wndClass) != (ATOM)0)
return TRUE;
else
return FALSE;
}
/**********************************************************************/
/*
* Perform any necessary initialization for this instance of the
* application. This simply means creating the application's 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.
*/
return CreateWindow(RWVIEWCLASSNAME,
WINDOWTITLE,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
DEFAULTWINDOWWIDTH, DEFAULTWINDOWHEIGHT,
NULL, NULL, instance, NULL);
}
/**********************************************************************/
/*
* Ensure that the depth of the output display is acceptable.
* RenderWare will operate correctly at all display depths
* but it works best when running on an 8-bit or 16-bit
* display. If the display depth is not 8-bit or 16-bit we
* will display a message explaining the situation and
* ask the user if she or he wishes to continue.
*/
static BOOL
IsGoodDisplayDepth(HWND window)
{
HDC dc;
int depth;
char colString[10];
char buffer[400];
dc = GetDC(window);
depth = GetDeviceCaps(dc, BITSPIXEL);
ReleaseDC(window, dc);
if ((depth != 8) && (depth != 16))
{
if (depth < 8)
wsprintf(colString, "%d", 1 << depth);
else
strcpy(colString, "true");
wsprintf(buffer,
"Your video adapter is currently running in %s color mode.\n"
"Although the Viewer will operate correctly in this mode,\n"
"we recommend that you change the mode of your video adapter\n"
"to 256 color mode or 65536 color mode.\n"
"Do you wish to continue?",
colString);
if (MessageBox(window, buffer, WINDOWTITLE,
MB_YESNO | MB_APPLMODAL | MB_ICONEXCLAMATION) == IDYES)
return TRUE;
else
return FALSE;
}
else
{
return TRUE;
}
}
/**********************************************************************/
/*
* Reset the camera to its default position and orientation. We could
* use RwResetCamera() instead but we wish to leave the other camera
* settings such as viewwindow alone.
*/
static void
ResetCamera(RwCamera *camera)
{
RwPushScratchMatrix();
RwScaleMatrix(RwScratchMatrix(),
CREAL(-1.0), CREAL(1.0), CREAL(1.0),
rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(0.0),
DEFAULTCAMERADISTANCE, rwPOSTCONCAT);
RwTransformCamera(camera, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
}
/**********************************************************************/
/*
* Size the highlight box to fit the given clump.
*/
static RwClump *
SizeHighlightToFitClump(RwClump *clump)
{
RwV3d bll;
RwV3d fur;
RwReal w;
RwReal h;
RwReal d;
RwReal x;
RwReal y;
RwReal z;
RwGetClumpLocalBBox(clump, &bll, &fur);
/*
* Scale the highlight clump to fit.
*/
w = RSub(fur.x, bll.x);
h = RSub(fur.y, bll.y);
d = RSub(fur.z, bll.z);
x = RAdd(bll.x, RDiv(w, CREAL(2.0)));
y = RAdd(bll.y, RDiv(h, CREAL(2.0)));
z = RAdd(bll.z, RDiv(d, CREAL(2.0)));
RwPushScratchMatrix();
RwScaleMatrix(RwScratchMatrix(), w, h, d, rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), x, y, z, rwPOSTCONCAT);
RwTransformClumpJoint(HighlightClump, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
return clump;
}
/**********************************************************************/
static void
SelectClump(HWND window, RwClump *clump)
{
/*
* Remember the currently picked object.
*/
SelectedClump = clump;
/*
* Don't spin it until the user as rotated the object.
*/
SpinClump = FALSE;
/*
* Size the highlight to fit.
*/
SizeHighlightToFitClump(clump);
/*
* Update the menus to reflect the picked clump.
*/
EnableMenuItem(GetMenu(window), IDM_EDIT_DELETE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_EDIT_DUPLICATE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_CAMERA_LOOKAT, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_CAMERA_MOVETO, MF_BYCOMMAND | MF_ENABLED);
if (ISCLUMPLIGHT(clump))
{
/*
* Its a light...
*/
EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_FILE_SAVE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS, MF_BYCOMMAND | MF_GRAYED);
}
else
{
/*
* Its a clump...
*/
EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_FILE_SAVE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS, MF_BYCOMMAND | MF_ENABLED);
}
}
/**********************************************************************/
static void
DeselectClump(HWND window)
{
SelectedClump = NULL;
SpinClump = FALSE;
/*
* Update the menus to reflect no picked clump.
*/
EnableMenuItem(GetMenu(window), IDM_FILE_SAVE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_FILE_SAVEAS, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_EDIT_DELETE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_EDIT_DUPLICATE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_LIGHT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_RESET, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CLUMP_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CAMERA_LOOKAT, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetMenu(window), IDM_CAMERA_MOVETO, MF_BYCOMMAND | MF_GRAYED);
}
/**********************************************************************/
/*
* Create a viewer scene. This involves pushing a new texture dictionary
* to hold the textures loaded into the scene (so we can discard these
* textures when the scene is destroyed), creating a scene and an
* initial light source.
*/
static RwScene *
CreateViewerScene(void)
{
RwScene *scene;
RwLight *light;
/*
* Create a new texture dictionary to store the textures loaded
* when objects are added to scene.
*/
if (!RwTextureDictBegin())
return NULL;
/*
* Create the scene to hold the clumps and lights the viewer
* views.
*/
scene = RwCreateScene();
if (scene == NULL)
{
RwTextureDictEnd();
return NULL;
}
/*
* Create the default light.
*/
light = CreateLightObj(rwDIRECTIONAL);
if (light == NULL)
{
RwDestroyScene(scene);
RwTextureDictEnd();
return NULL;
}
/*
* Add the light and its associated clump to the scene.
*/
SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
AddLightObjToScene(scene, light);
return scene;
}
/**********************************************************************/
/*
* Destroy the viewer scene. This function discard the scene (and all
* its clumps and lights) and all the textures loaded by the objects
* in this world.
*/
static void
DestroyViewerScene(RwScene *scene)
{
/*
* Destory all the clumps (and thier lights if applicable). We
* would normally just let RwDestoryScene() do this for us but
* in this case we need to free the user-data associated with
* each clump. We do this by enumerating the DestoryClumpObj()
* function over all the clumps in the scene.
*/
RwForAllClumpsInScene(scene, (RwClumpFuncVoid)DestroyClumpObj);
/*
* Destory the scene.
*/
RwDestroyScene(scene);
/*
* Discard all the textures loaded by the objects in this scene
* (all the textures loaded are stored in the topmost texture
* dictionary - by popping this dictionary we free all the loaded
* textures as well).
*/
RwTextureDictEnd();
}
/**********************************************************************/
static void
UpdateCameraBackdropPosition(RwCamera *camera)
{
RwRaster *raster;
RwInt32 offsetX;
RwInt32 offsetY;
RwInt32 x;
RwInt32 y;
RwInt32 w;
RwInt32 h;
RwInt32 vpw;
RwInt32 vph;
if (RwGetCameraBackdrop(camera) != NULL)
{
/*
* Get the viewport size of the camera.
*/
RwGetCameraViewport(camera, NULL, NULL, &vpw, &vph);
if (CenterBackdrop)
{
/*
* We are centering the backdrop so get the dimensions
* of the backdrop raster.
*/
raster = RwGetCameraBackdrop(camera);
w = RwGetRasterWidth(raster);
h = RwGetRasterHeight(raster);
x = (vpw - w) / 2;
y = (vph - h) / 2;
if (x < 0)
{
/*
* The backdrop is wider than the viewport so set the
* width of the viewport rectangle to the width of the
* viewport and set the offset of the backdrop to ensure
* the center of the backdrop raster is visible.
*/
offsetX = -x;
x = 0;
w = vpw;
}
else
{
offsetX = 0;
}
if (y < 0)
{
/*
* The backdrop is higher than the viewport so set the
* height of the viewport rectangle to the height of the
* viewport and set the offset of the backdrop to ensure
* the center of the backdrop raster is visible.
*/
offsetY = -y;
y = 0;
h = vph;
}
else
{
offsetY = 0;
}
RwSetCameraBackdropOffset(camera, offsetX, offsetY);
RwSetCameraBackdropViewportRect(camera, x, y, w, h);
}
else
{
/*
* The backdrop is tiled so fill the entire viewport with the
* backdrop.
*/
RwSetCameraBackdropOffset(camera, 0, 0);
RwSetCameraBackdropViewportRect(camera, 0, 0, vpw, vph);
}
}
}
/**********************************************************************/
/*
* 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 and a matrix for spinning.
*/
static BOOL
Init3D(HWND window)
{
char shapePath[RWMAXPATHLEN];
char *s;
RwRaster *backdrop;
RwRGBColor color;
/*
* Check the display depth and if it is not one of the optimal depths
* for RenderWare (8-bit or 16-bit). If it is not then warn the user
* and check whether they wish to continue or not.
*/
if (!IsGoodDisplayDepth(window))
return FALSE;
/*
* Attempt to open (and initialize) the RenderWare library.
* Explicitly specifying the Windows driver.
*/
if (!RwOpen("MSWindows", NULL))
{
MessageBox(window,
"Could not open the RenderWare library",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* 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. However, when we create a camera we have to
* specify the largest size that camera's viewport can ever be. As the
* camera's viewport is the same size as the window's client area we
* have to specify the size as the maximum size of the window.
*
* If we compile with CONSTRAIN_SIZE then we restrict the window size
* (using WM_GETMINMAXINFO) to MAXIMUMWINDOWWIDTH by MAXIMUMWINDOWHEIGHT.
* If CONTRAIN_SIZE is not specified we let the window grow to the full
* size of the screen.
*/
#if defined(CONSTRAIN_SIZE)
Camera = RwCreateCamera(MAXIMUMWINDOWWIDTH, MAXIMUMWINDOWHEIGHT, NULL);
#else
Camera = RwCreateCamera((RwInt32)GetSystemMetrics(SM_CXFULLSCREEN),
(RwInt32)GetSystemMetrics(SM_CYFULLSCREEN), NULL);
#endif
if (Camera == NULL)
{
/*
* As with RwOpen(), the most common cause for a failure to create
* a camera is insufficient memory so we will explicitly check for
* this condition and report it. Otherwise a general error is issued.
*/
if (RwGetError() == E_RW_NOMEM)
{
MessageBox(window,
"Insufficient memory to create the 3D camera",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
else
{
MessageBox(window, "Could not create the 3D camera",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
RwClose();
return FALSE;
}
/*
* Set the camera's background color to the color saved in the
* initialization file. The initialization file stores the
* colors as a Windows format COLORREF so we need to convert
* it to a RenderWare RwRGBColor.
*/
ColorRefToRGB(BackgroundColor, &color);
RwSetCameraBackColorStruct(Camera, &color);
/*
* Move the camera to its default position.
*/
ResetCamera(Camera);
/*
* If we have read a non-empty backdrop filename from the
* settings file attempt to load that backdrop raster. If
* we don't succeed we don't consider it a fatal error -
* we simply continue.
*/
if (BackdropFileName[0] != '\0')
{
backdrop = RwReadRaster(BackdropFileName,
rwGAMMARASTER | rwDITHERRASTER);
if (backdrop != NULL)
RwSetCameraBackdrop(Camera, backdrop);
}
/*
* Create the initial scene for the viewer. The initial
* scene consists of a default light and a new texture
* dictionary to store the textures loaded by clumps
* added to the scene.
*/
Scene = CreateViewerScene();
if (Scene == NULL)
{
RwDestroyCamera(Camera);
Camera = NULL;
RwClose();
MessageBox(window, "Could not create the 3D Scene",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Create the spin matrix.
*/
SpinMatrix = RwCreateMatrix();
if (SpinMatrix == NULL)
{
DestroyViewerScene(Scene);
Scene = NULL;
RwDestroyCamera(Camera);
Camera = NULL;
RwClose();
MessageBox(window,
"Could not create the RenderWare matrix",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Create the highlight clump. The highlight clump is a yellow, wireframe
* box which surrounds the picked clump.
*/
RwModelBegin();
RwClumpBegin();
RwSetSurface(CREAL(0.75), CREAL(0.0), CREAL(0.0));
RwSetSurfaceColor(CREAL(1.0), CREAL(1.0), CREAL(0.0));
RwSetSurfaceGeometrySampling(rwWIREFRAME);
RwBlock(CREAL(1.0), CREAL(1.0), CREAL(1.0));
RwClumpEnd(&HighlightClump);
RwModelEnd();
if (HighlightClump == NULL)
{
DestroyViewerScene(Scene);
Scene = NULL;
RwDestroyCamera(Camera);
Camera = NULL;
RwClose();
MessageBox(window, "Could not create the highlight object",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* Initialize the last opened filename with the first directory
* in the RenderWare search path (if any). This gives us a good
* chance of opening into a directory with interesting scripts
* in it.
*/
RwGetShapePath(shapePath);
if (shapePath[0] == '\0')
{
/*
* The shape path is empty so initialize the last filename
* opened to empty.
*/
LastFileName[0] = '\0';
}
else
{
s = strchr(shapePath, ';');
if (s != NULL)
{
/*
* We have located the end of the first directory.
* So truncate the shape path to this point.
*/
*s = '\0';
}
/*
* Copy the directory to the last filename. Append a backquote
* so we know this is a directory and not a full filename.
*/
strcpy(LastFileName, shapePath);
strcat(LastFileName, "\\");
}
/*
* All the 3D components are now successfully initialized, so
* work can begin...
*/
RwIsOpen = TRUE;
return TRUE;
}
/**********************************************************************/
/*
* This function shuts down the 3D (i.e. RenderWare) components of the
* application in a polite fashion.
*/
static void
TidyUp3D(void)
{
/*
* Destroy the highlight clump.
*/
if (HighlightClump != NULL)
RwDestroyClump(HighlightClump);
/*
* Destroy the spin matrix.
*/
if (SpinMatrix != NULL)
RwDestroyMatrix(SpinMatrix);
/*
* Destroy the scene. This will destroy the scene itself, the contents of
* the scene, i.e. any clumps and lights in that scene and any textures
* loaded by the clumps in the scene.
*/
if (Scene != NULL)
DestroyViewerScene(Scene);
if (Camera != NULL)
{
/*
* Destroy the camera's backdrop (if any). Backdrops are not
* automatically destroyed by RwDestroyCamera() so we must
* manually destroy the backdrop.
*/
if (RwGetCameraBackdrop(Camera))
RwDestroyRaster(RwGetCameraBackdrop(Camera));
/*
* Destroy the camera.
*/
RwDestroyCamera(Camera);
}
/*
* Close the library. This will free up any internal resources and
* textures loaded.
*/
RwClose();
}
/**********************************************************************/
/*
* Render the scene and copy it to the window and device context
* given. This function encapsulates the very common RenderWare
* for rendering and updating the display.
*/
static void
RenderScene(HWND window)
{
HDC dc;
/*
* Setup the current camera and perform all necessary initialization
* before rendering takes place.
*/
RwBeginCameraUpdate(Camera, (void *)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);
/*
* If there is a picked clump then display the highlight clump
* over the picked clump.
*/
if ((SelectedClump != NULL) && ShowHighlight)
{
RwPushScratchMatrix();
RwGetClumpLTM(SelectedClump, RwScratchMatrix());
RwTransformClump(HighlightClump, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
RwRenderClump(HighlightClump);
}
/*
* 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
* Windows, RenderWare requires a device context for the client
* area of the window. This is passed in as the device specific
* parameter of the RwShowCameraImage() call.
*/
dc = GetDC(window);
RwShowCameraImage(Camera, (void*)(DWORD)dc);
ReleaseDC(window, dc);
}
/**********************************************************************/
/*
* Attempt to load an object from the given file. The actual object type
* to load is determined from the file extension. This function will
* read RenderWare clumps (.rwx), Backdrops (.bmp) and Palettes (.pal).
*/
static BOOL
LoadObject(HWND window, char *pathName)
{
HCURSOR oldCursor;
RwClump *clump;
RwRaster *backdrop;
char buffer[128];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fName[_MAX_FNAME];
char ext[_MAX_EXT];
RwInt32 numPalEntries;
RwPaletteEntry palEntries[256];
RwInt32 firstEntry;
RwInt32 lastEntry;
/*
* We use the file extension to decide what kind of object we
* are reading so we decompose the filename into its components.
*/
_splitpath(pathName, drive, dir, fName, ext);
if (stricmp(ext, ".rwx") == 0)
{
/*
* Its a RenderWare script file so read the clump
* and add it to the viewer's scene.
*/
/*
* This may take some time so show the wait cursor.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
clump = ReadClumpObj(pathName);
SetCursor(oldCursor);
if (clump != NULL)
{
AddClumpObjToScene(Scene, clump);
return TRUE;
}
else
{
/*
* The read failed. There are a large number of reasons why this
* could happen. We will issue specific messages for the most
* common errors and a generic catch all for the rest.
*/
switch (RwGetError())
{
case E_RW_NOFILE:
case E_RW_BADOPEN:
/*
* Could not open the file...
*/
sprintf(buffer, "The file %s could not be opened", pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer, "Insufficient memory to load object %s",
pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
default:
/*
* Generic, catch all error message.
*/
sprintf(buffer, "The 3D object %s could not be read",
pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
}
return FALSE;
}
}
else if (stricmp(ext, ".bmp") == 0)
{
/*
* Its a Windows bitmap file so read the bitmap as a
* raster and make it the backdrop of the camera.
*/
/*
* This may take some time so show the wait cursor.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
backdrop = RwReadRaster(pathName, rwGAMMARASTER | rwDITHERRASTER);
SetCursor(oldCursor);
if (backdrop != NULL)
{
if (RwGetCameraBackdrop(Camera) != NULL)
/*
* The camera has an existing backdrop so destroy it.
*/
RwDestroyRaster(RwGetCameraBackdrop(Camera));
RwSetCameraBackdrop(Camera, backdrop);
EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE, MF_BYCOMMAND | MF_ENABLED);
/*
* Update the setting's backdrop filename and flag the fact
* that the settings have changed.
*/
strcpy(BackdropFileName, pathName);
SettingsChanged = TRUE;
UpdateCameraBackdropPosition(Camera);
return TRUE;
}
else
{
/*
* The read failed. There are a large number of reasons why this
* could happen. We will issue specific messages for the most
* common errors and a generic catch all for the rest.
*/
switch (RwGetError())
{
case E_RW_NOFILE:
case E_RW_BADOPEN:
/*
* Could not open the file...
*/
sprintf(buffer, "The file %s could not be opened", pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer, "Insufficient memory to load backdrop %s",
pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
default:
/*
* Generic, catch all error message.
*/
sprintf(buffer, "The backdrop %s could not be read",
pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
break;
}
return FALSE;
}
}
else if (stricmp(ext, ".pal") == 0)
{
/*
* This may take some time so show the wait cursor.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
numPalEntries = ReadPalette(pathName, palEntries);
SetCursor(oldCursor);
if (numPalEntries > 0)
{
/*
* We have successfully read a new palette. Textures are
* not rematched when we load a new palette so they will
* look strange if we simply set the new palette. To avoid
* this potential visual strangeness we discard all textures
* and geometry when we load a new palette. We obviously
* don't want to do this without user confirmation, so ask
* the user if she or he really wishes to change the palette.
*/
if (MessageBox(window,
"In order to load a new palette the viewer must\n"
"discard the contents of the 3D Scene.\n"
"Do you wish to continue?",
WINDOWTITLE,
MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
{
/*
* Destroy the current scene.
*/
DestroyViewerScene(Scene);
/*
* It is also necessary to destory the camera's backdrop (as it
* will have been matched against the previous palette).
*/
if (RwGetCameraBackdrop(Camera) != NULL)
{
RwDestroyRaster(RwGetCameraBackdrop(Camera));
RwSetCameraBackdrop(Camera, NULL);
}
/*
* Create the new scene.
*/
Scene = CreateViewerScene();
if (Scene == NULL)
{
/*
* Failing to create the new scene is a fatal error.
* Display an error message and exit.
*/
MessageBox(window, "Could not create the new 3D Scene",
WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
SendMessage(window, WM_CLOSE, 0, 0L);
return FALSE;
}
/*
* As this is a new world there is no picked object yet.
*/
DeselectClump(window);
/*
* Set the new palette.
*/
RwGetDeviceInfo(rwFIRSTPALETTEENTRY, &firstEntry, sizeof(firstEntry));
RwGetDeviceInfo(rwLASTPALETTEENTRY, &lastEntry, sizeof(lastEntry));
if (numPalEntries > ((lastEntry - firstEntry)))
numPalEntries = ((lastEntry - firstEntry));
RwSetPaletteEntries(firstEntry, numPalEntries, palEntries, 0L);
return TRUE;
}
return FALSE;
}
else
{
/*
* Generic, catch all error message.
*/
sprintf(buffer, "The palette %s could not be read",
pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
return FALSE;
}
}
else
{
/*
* The file type was unknown so flag an error and fail.
*/
wsprintf(buffer, "The format of %s is unknown", pathName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
return FALSE;
}
}
/**********************************************************************/
/*
* Parse the names of any .rwx files on the command line, read those
* files and add them to the scene. This allows RwView to be associated
* with .rwx files in the file manager and be started automatically when
* a .rwx file is double-clicked.
*/
static BOOL
ReadFromCommandLine(HWND window, char *cmdLine)
{
char *s1;
char *s2;
char fileName[_MAX_PATH];
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 an object. The actual object type is
* determined by LoadObject() from the extension of the file
* name.
*
* NOTE: We don't include these files in the MRU file list.
*/
if (!LoadObject(window, fileName))
return FALSE;
}
s2 = &fileName[0];
}
return TRUE;
}
/**********************************************************************/
#if defined(CONSTRAIN_SIZE)
/*
* Handle queries about the window's maximum extent. We only do this
* if we are constraining the size of the window.
*/
static void
OnGetMinMaxInfo(MINMAXINFO FAR *minMaxInfo)
{
/*
* Constraint the window to the maximum size defined by the constants
* MAXIMUM_WINDOW_WIDTH and MAXIMUM_WINDOW_HEIGHT.
*/
minMaxInfo->ptMaxSize.x = MAXIMUMWINDOWWIDTH;
minMaxInfo->ptMaxSize.y = MAXIMUMWINDOWHEIGHT;
minMaxInfo->ptMaxTrackSize.x = MAXIMUMWINDOWWIDTH;
minMaxInfo->ptMaxTrackSize.y = MAXIMUMWINDOWHEIGHT;
}
#endif
/**********************************************************************/
/*
* Handle measure item requests. Currently we user ownerdraw items for
* the titles of the popup menus. This function computes the size
* necessary for these items.
*/
static void
OnMeasureItem(HWND window, MEASUREITEMSTRUCT FAR *measureItem)
{
HDC dc;
SIZE size;
switch (measureItem->itemID)
{
case IDM_LIGHTPOPUP_LABEL:
dc = GetDC(window);
MeasurePopupMenuLabel(dc, "Light", &size);
ReleaseDC(window, dc);
measureItem->itemWidth = size.cx;
measureItem->itemHeight = size.cy;
break;
case IDM_CLUMPPOPUP_LABEL:
dc = GetDC(window);
MeasurePopupMenuLabel(dc, "Clump", &size);
ReleaseDC(window, dc);
measureItem->itemWidth = size.cx;
measureItem->itemHeight = size.cy;
break;
case IDM_BACKGROUNDPOPUP_LABEL:
dc = GetDC(window);
MeasurePopupMenuLabel(dc, "Background", &size);
ReleaseDC(window, dc);
measureItem->itemWidth = size.cx;
measureItem->itemHeight = size.cy;
break;
}
}
/**********************************************************************/
static void
OnDrawItem(HWND window, DRAWITEMSTRUCT FAR *drawItem)
{
HDC dc;
RECT rect;
/*
* Cache interesting stuff for eash access.
*/
dc = drawItem->hDC;
rect = drawItem->rcItem;
switch (drawItem->itemID)
{
case IDM_LIGHTPOPUP_LABEL:
DrawPopupMenuLabel(dc, &rect, "Light");
break;
case IDM_CLUMPPOPUP_LABEL:
DrawPopupMenuLabel(dc, &rect, "Clump");
break;
case IDM_BACKGROUNDPOPUP_LABEL:
DrawPopupMenuLabel(dc, &rect, "Background");
break;
}
}
/**********************************************************************/
/*
* Handle window resize. The most important job here is to change the
* size of the camera's viewport (if preserving the aspect ratio of
* the viewport is necessary, then the viewwindow should also be
* changed at this point to reflect then new aspect ratio of the
* viewport).
*/
static void
OnSize(HWND window, int w, int h)
{
RwReal vw;
RwReal vh;
/*
* 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, 0, w, h);
/*
* Preserve the aspect ratio of the image by resizing the
* viewindow size in proportion to to viewport size. Be ensuring
* that the ratios of width to height of the viewport
* and viewwindow are the same the rendered image will not
* be distorted or stretched.
*/
if (w > h)
{
vw = DEFAULTVIEWWINDOWSIZE;
vh = RDiv(RMul(INT2REAL(h), DEFAULTVIEWWINDOWSIZE), INT2REAL(w));
}
else
{
vw = RDiv(RMul(INT2REAL(w), DEFAULTVIEWWINDOWSIZE), INT2REAL(h));
vh = DEFAULTVIEWWINDOWSIZE;
}
RwSetCameraViewwindow(Camera, vw, vh);
if (RwGetCameraBackdrop(Camera) != NULL)
UpdateCameraBackdropPosition(Camera);
/*
* When the viewport has changed size we need to re-render the
* scene for the new viewport size.
*/
RenderScene(window);
}
/**********************************************************************/
/*
* 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.
*/
static void
OnLButtonDown(HWND window, POINT *point, WPARAM vKeys)
{
RwPickRecord pick;
RwV3d p;
RwV3d o;
/*
* If the left button is depressed anywhere in the client area of the
* window then the animated spin is cancelled.
*/
SpinClump = FALSE;
/*
* 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, point->x, point->y, Camera, &pick))
{
switch (pick.type)
{
case rwNAPICKOBJECT:
/*
* No clump was picked. The background was clicked on.
* The action taken depends on whether the camera has
* a backdrop or not. If the camera has a backdrop then
* if CONTROL and SHIFT were held down, the backdrop
* is destroyed, otherwise the backdrop will be scrolled
* on mouse move. If the camera has no backdrop then
* no action will be taken.
*/
DeselectClump(window);
RenderScene(window);
if (RwGetCameraBackdrop(Camera))
{
MouseMoveMode = mmScrollBackdrop;
}
else
{
MouseMoveMode = mmNoAction;
}
break;
case rwPICKCLUMP:
/*
* A clump was picked, so remember which clump was picked
* in order that it may be manipulated later when the
* mouse moves.
*/
SelectClump(window, pick.object.clump.clump);
RenderScene(window);
/*
* When we drag a clump we don't want the origin of the
* clump to jump to the mouse pointer position. We want
* to keep the drag offset by the distance of the initial
* pick from the origin of the clump. So we compute the
* initial pick position and store it away.
*/
GetClumpPositionUnderPointer(SelectedClump, Camera, point->x, point->y, &p);
RwGetClumpOrigin(SelectedClump, &o);
PickOffset.x = RSub(o.x, p.x);
PickOffset.y = RSub(o.y, p.y);
PickOffset.z = RSub(o.z, p.z);
/*
* 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 (vKeys & MK_SHIFT)
MouseMoveMode = mmDragClump;
else if (vKeys & MK_CONTROL)
MouseMoveMode = mmDragClumpInZ;
else
MouseMoveMode = mmSpinClump;
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)
{
SetCapture(window);
LastX = point->x;
LastY = point->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 panning the camera.
*/
static void
OnRButtonDown(HWND window, POINT *point, WPARAM vKeys)
{
RwPickRecord pick;
RwClump *clump;
if (vKeys & MK_SHIFT)
{
MouseMoveMode = mmMoveAndTurnCamera;
}
else if (vKeys & MK_CONTROL)
{
MouseMoveMode = mmTiltCamera;
}
else
{
/*
* Right mouse button down brings up an appropriate menu
* for the object picked on (on just tracks the camera if
* no object is picked).
*/
if (RwPickScene(Scene, point->x, point->y, Camera, &pick))
{
if (pick.type == rwPICKCLUMP)
{
clump = pick.object.clump.clump;
/*
* The object we picked on becomes the currently
* selected clump and rerender the scene so the
* highlight clump is displayed.
*/
SelectClump(window, clump);
RenderScene(window);
/*
* We have picked on a clump. Does the clump picked
* represent a light or is it just a clump?
*/
if (ISCLUMPLIGHT(clump))
{
/*
* Its a light, so bring up the light pop-up
* menu.
*/
ClientToScreen(window, point);
TrackPopupMenu(LightMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point->x, point->y, 0, window, NULL);
}
else
{
/*
* Its a clump, so bring up the clump pop-up
* menu.
*/
ClientToScreen(window, point);
TrackPopupMenu(ClumpMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point->x, point->y, 0, window, NULL);
}
}
else
{
/*
* Its the background menu. Depending on whether we are displaying
* a backdrop or the straight backdrop color we enable and disable
* pop-up menu options.
*/
if (RwGetCameraBackdrop(Camera) == NULL)
{
EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_DELETE, MF_BYCOMMAND | MF_GRAYED);
}
else
{
if (CenterBackdrop)
EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR, MF_BYCOMMAND | MF_ENABLED);
else
EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_COLOR, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(BackgroundMenu, IDM_BACKGROUNDPOPUP_DELETE, MF_BYCOMMAND | MF_ENABLED);
}
/*
* Display the background menu.
*/
ClientToScreen(window, point);
TrackPopupMenu(BackgroundMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point->x, point->y, 0, window, NULL);
}
}
MouseMoveMode = mmNoAction;
}
/*
* 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);
LastX = point->x;
LastY = point->y;
}
}
/**********************************************************************/
/*
* Handle a movement of the mouse. If a previous left or right 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
OnMouseMove(HWND window, POINT *point)
{
RwClump *parent;
RwMatrix4d *tmpMatrix;
RwMatrix4d *worldToLocal;
RwV3d up;
RwV3d right;
RwV3d at;
RwReal yDelta;
RwReal xAngle;
RwReal yAngle;
RwInt32 xOffset;
RwInt32 yOffset;
RwV3d p;
RwV3d o;
/*
* MouseMoveMode tells us what kind of action to perform.
*/
switch (MouseMoveMode)
{
case mmNoAction:
break;
case mmScrollBackdrop:
/*
* We are scrolling the camera's backdrop. Movement of the
* mouse in X scrolls the backdrop horizontally. Movement of
* the mouse in Y scrolls the backdrop vertically.
*/
RwGetCameraBackdropOffset(Camera, &xOffset, &yOffset);
xOffset += (LastX - point->x);
yOffset += (LastY - point->y);
RwSetCameraBackdropOffset(Camera, xOffset, yOffset);
break;
case mmMoveAndTurnCamera:
/*
* We are moving and turning the camera. Movement of the
* mouse in the X direction will turns the camera to the left
* and right, and movement of the mouse in the Y direction
* moves the camera forward (along its look at) or back..
*/
yDelta = RDiv(INT2REAL(LastY - point->y), CREAL(50.0));
/*
* 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), yDelta);
/*
* Pan the camera by mouse X delta degrees.
*/
RwPanCamera(Camera, RDiv(INT2REAL(LastX - point->x), CREAL(4.0)));
break;
case mmTiltCamera:
RwTiltCamera(Camera, INT2REAL(point->y - LastY));
break;
case mmSpinClump:
/*
* Compute the angles of spin (simply derived from the mouse move deltas).
*/
yAngle = INT2REAL(point->x - LastX);
xAngle = INT2REAL(point->y - LastY);
/*
* In RwView, 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 is 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(SelectedClump);
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(SelectedClump, SpinMatrix, rwPOSTCONCAT);
/*
* Does the clump represent a light? If so change the light's
* orientation to match that of the clump.
*/
if (ISCLUMPLIGHT(SelectedClump))
{
RwGetClumpLTM(SelectedClump, RwScratchMatrix());
RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
}
else
{
/*
* If the clump is not representing a light enable the momentum spin.
* (Giving a light momentum is a little strange).
*/
SpinClump = TRUE;
}
break;
case mmDragClump:
/*
* Get the 3D position of the mouse pointer on the plane parallel
* to the camera on which the picked clump lies.
*/
GetClumpPositionUnderPointer(SelectedClump, Camera, point->x, point->y, &p);
p.x = RAdd(p.x, PickOffset.x);
p.y = RAdd(p.y, PickOffset.y);
p.z = RAdd(p.z, PickOffset.z);
RwGetClumpOrigin(SelectedClump, &o);
p.x = RSub(p.x, o.x);
p.y = RSub(p.y, o.y);
p.z = RSub(p.z, o.z);
RwPushScratchMatrix();
RwTranslateMatrix(RwScratchMatrix(), p.x, p.y, p.z, rwREPLACE);
RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
/*
* Does the clump represent a light? If so change the light's
* orientation to match that of the clump.
*/
if (ISCLUMPLIGHT(SelectedClump))
{
RwGetClumpLTM(SelectedClump, RwScratchMatrix());
RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
}
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 - point->y), CREAL(50.0));
/*
* In a similar fashion to spinning a clump we must take into account
* the camera's orientation when dragging a clump. This is done by
* translating along the camera's Look At vector (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(SelectedClump);
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 to the clump.
*/
RwTransformClump(SelectedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
/*
* Does the clump represent a light? If so change the light's
* orientation to match that of the clump.
*/
if (ISCLUMPLIGHT(SelectedClump))
{
RwGetClumpLTM(SelectedClump, RwScratchMatrix());
RwTransformLight(GETCLUMPLIGHT(SelectedClump), RwScratchMatrix(), rwREPLACE);
}
break;
}
if (MouseMoveMode != mmNoAction)
{
/*
* Re-render the scene and copy the results to the display.
*/
RenderScene(window);
/*
* Remember the current X and Y for next time.
*/
LastX = point->x;
LastY = point->y;
}
}
/**********************************************************************/
/*
* Handle the left mouse button comming back up. The basic action is
* to turn off mouse move actions and release mouse capture.
*/
static void
OnLButtonUp(void)
{
/*
* 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 the right mouse button comming back up. The basic action is
* to turn of mouse move actions and release mouse capture.
*/
static void
OnRButtonUp(void)
{
/*
* 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 file dropped as a RenderWare clump. If that fails, it will
* then attempt to load the file as a raster and make it the camera's
* backdrop.
*/
static void
OnDrop(HWND window, HDROP drop)
{
int i;
int numFiles;
char path[_MAX_PATH];
/*
* Get the number of dropped files.
*/
numFiles = DragQueryFile(drop, (UINT)-1, NULL, 0U);
/*
* Attempt to load each file in turn.
*/
for (i = 0; i < numFiles; i++)
{
/*
* Attempt to load the object whose file was dropped. The
* actual object type to load is determined by LoadObject()
* from the file extenstion.
*/
DragQueryFile(drop, i, path, _MAX_PATH);
if (LoadObject(window, path))
{
/*
* Update the MRU file list and menus.
*/
DeleteMRUFileMenuItems(window);
AddFileToMRUFileList(path);
AppendMRUFileMenuItems(window);
}
else
{
DragFinish(drop);
return;
}
}
DragFinish(drop);
/*
* As new objects have been loaded we must re-render the scene.
*/
RenderScene(window);
}
/**********************************************************************/
/*
* Handle the WM_PAINT message by simply copying the rendering
* already performed (an stored in the camera's image buffer) to the
* output window. There is no need to re-render as nothing in the
* scene has changed since the last render. HandleSize() re-renders
* when the viewport changes and HandleDrop() re-renders when
* clumps are added.
*/
static void
OnPaint(HWND window)
{
HDC dc;
PAINTSTRUCT paintStruct;
dc = BeginPaint(window, &paintStruct);
/*
* The truly optimal thing to do would be to get the damaged area
* of the window that needs updating and set that to be the damaged
* area of the viewport. If this was done then only the portion of
* viewport corresponding to damaged area of the window would be
* copied. However, as WM_PAINTs are rare in comparison to timer
* expiries or mouse moves (where the real rendering effort goes)
* there is little to be gained from damaging only a portion of
* the display. Therefore, we invalidate the entire viewport and
* copy it all to the window.
*/
RwInvalidateCameraViewport(Camera);
/*
* Copy the viewport to the display.
*/
RwShowCameraImage(Camera, (void*)(DWORD)dc);
EndPaint(window, &paintStruct);
}
/**********************************************************************/
/*
* Handle menu selections.
*/
static void
OnMenu(HWND window, WPARAM item)
{
OPENFILENAME ofn;
int len;
char fileName[_MAX_PATH];
char directory[_MAX_PATH];
char dir[_MAX_DIR];
char ext[_MAX_EXT];
FARPROC dialogProc;
RwV3d point;
RwV3d right;
RwV3d up;
RwClump *newClump;
RwLight *light;
HCURSOR oldCursor;
RwRGBColor color;
char buffer[128];
RwBool status;
switch (item)
{
case IDM_FILE_OPEN:
/*
* Use a common dialog to get the name of the file to load.
* We intialize things with the directory and name of the last
* filename loaded.
*/
_splitpath(LastFileName, directory, dir, fileName, ext);
strcat(fileName, ext);
strcat(directory, dir);
len = strlen(directory);
if ((len > 0) && (directory[len - 1] == '\\'))
directory[len - 1] = '\0';
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = window;
ofn.lpstrFilter = "RW Object (*.rwx)\0*.rwx\0Backdrop (*.bmp)\0*.bmp\0Palette (*.pal)\0*.pal\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile= fileName;
ofn.nMaxFile = sizeof(fileName);
ofn.lpstrInitialDir = directory;
ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn))
{
/*
* Save the file name for next time.
*/
strcpy(LastFileName, fileName);
/*
* Use LoadObject() to load an object of the appropriate type from
* the given file. LoadObject() determines the object type to load
* from the extension of the file.
*/
if (LoadObject(window, fileName))
{
/*
* Update the MRU file list and menus.
*/
DeleteMRUFileMenuItems(window);
AddFileToMRUFileList(fileName);
AppendMRUFileMenuItems(window);
RenderScene(window);
}
}
break;
case IDM_FILE_SAVE:
/*
* Ensure there is a clump and its not the representation
* of a light (we don't save those).
*/
if ((SelectedClump != NULL) && (!ISCLUMPLIGHT(SelectedClump)))
{
/*
* Write out under the filename stored with the clump.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
status = RwWriteShape(GETCLUMPFILENAME(SelectedClump), SelectedClump);
SetCursor(oldCursor);
if (!status)
{
sprintf(buffer, "Could not write the file %s",
GETCLUMPFILENAME(SelectedClump));
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
}
}
break;
case IDM_FILE_SAVEAS:
/*
* Ensure there is a clump and its not the representation
* of a light (we don't save those).
*/
if ((SelectedClump != NULL) && (!ISCLUMPLIGHT(SelectedClump)))
{
/*
* Use a common dialog to get the name of the file to save.
* We intialize things with the current filename of the clump
* to be saved.
*/
_splitpath(GETCLUMPFILENAME(SelectedClump), directory, dir, fileName, ext);
strcat(fileName, ext);
strcat(directory, dir);
len = strlen(directory);
if ((len > 0) && (directory[len - 1] == '\\'))
directory[len - 1] = '\0';
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = window;
ofn.lpstrFilter = "RW Object (*.rwx)\0*.rwx\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile= fileName;
ofn.nMaxFile = sizeof(fileName);
ofn.lpstrInitialDir = directory;
ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
if (GetSaveFileName(&ofn))
{
/*
* Write out under the new filename.
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
status = RwWriteShape(fileName, SelectedClump);
SetCursor(oldCursor);
if (!status)
{
sprintf(buffer, "Could not write the file %s",
fileName);
MessageBox(window, buffer, WINDOWTITLE,
MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
}
}
/*
* Update the clump's stored filename to reflect the new file it
* is stored int.
*/
strcpy(GETCLUMPFILENAME(SelectedClump), fileName);
}
break;
case IDM_FILE_EXIT:
/*
* Close the viewer.
*/
SendMessage(window, WM_CLOSE, 0, 0L);
break;
case IDM_FILE_MRUFILE:
case IDM_FILE_MRUFILE + 1:
case IDM_FILE_MRUFILE + 2:
case IDM_FILE_MRUFILE + 3:
if (LoadObject(window, MRUFiles[item - IDM_FILE_MRUFILE]))
RenderScene(window);
break;
case IDM_EDIT_DELETE:
/*
* Delete the picked clump (if there is one).
*/
if (SelectedClump != NULL)
{
DestroyClumpObj(SelectedClump);
DeselectClump(window);
RenderScene(window);
}
break;
case IDM_SCENE_NEW:
/*
* Ensure the user really wishes to discard this scene and its
* contents.
*/
if (MessageBox(window, "Discard this 3D Scene?", WINDOWTITLE,
MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
{
/*
* Discard the existing scene.
*/
DestroyViewerScene(Scene);
/*
* We also need to discard the camera's backdrop (as this too
* is a bitmap which has been matched against the existing
* palette.
*/
if (RwGetCameraBackdrop(Camera) != NULL)
{
RwDestroyRaster(RwGetCameraBackdrop(Camera));
RwSetCameraBackdrop(Camera, NULL);
EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE, MF_BYCOMMAND | MF_ENABLED);
BackdropFileName[0] = '\0';
SettingsChanged = TRUE;
}
/*
* Create a new scene.
*/
Scene = CreateViewerScene();
if (Scene == NULL)
{
/*
* Failing to create the new scene is a fatal error. So
* we display an error message and exit.
*/
MessageBox(window, "Could not create the new 3D Scene",
WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
SendMessage(window, WM_CLOSE, 0, 0L);
}
/*
* This is a new world so there is no longer any picked object.
*/
DeselectClump(window);
}
break;
case IDM_SCENE_BACKGROUNDCOLOR:
case IDM_BACKGROUNDPOPUP_COLOR:
/*
* Change the background color of the world.
*/
dialogProc = MakeProcInstance(ColorDlgProc, AppInstance);
RwGetCameraBackColor(Camera, &color);
if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_COLORPICKER),
window, dialogProc, (LPARAM)&color) == IDOK)
{
/*
* Change the background camera's background color and re-render.
*/
SettingsChanged = TRUE;
BackgroundColor = RGBToColorRef(&color);
RwSetCameraBackColorStruct(Camera, &color);
RwInvalidateCameraViewport(Camera);
RenderScene(window);
}
FreeProcInstance(dialogProc);
break;
case IDM_CAMERA_ELEVATIONVIEW:
case IDM_CAMERA_RESET:
/*
* Reset the camera's viewing position.
*/
ResetCamera(Camera);
RenderScene(window);
break;
case IDM_CAMERA_PLANVIEW:
/*
* Put the camera in plan view, i.e., looking down.
*/
RwSetCameraPosition(Camera, CREAL(0.0), DEFAULTCAMERADISTANCE, CREAL(0.0));
RwPointCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.0));
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(0.0), CREAL(-1.0));
RenderScene(window);
break;
case IDM_CAMERA_SIDEVIEW:
/*
* Put the camera in side view.
*/
RwSetCameraPosition(Camera, DEFAULTCAMERADISTANCE, CREAL(0.0), CREAL(0.0));
RwPointCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.0));
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RenderScene(window);
break;
case IDM_LIGHT_NEWDIRECTIONAL:
light = CreateLightObj(rwDIRECTIONAL);
if (light != NULL)
{
SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
AddLightObjToScene(Scene, light);
RenderScene(window);
}
else
{
MessageBox(window,
"Could not create the RenderWare light",
WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
break;
case IDM_LIGHT_NEWPOINT:
light = CreateLightObj(rwPOINT);
if (light != NULL)
{
SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
AddLightObjToScene(Scene, light);
RenderScene(window);
}
else
{
MessageBox(window,
"Could not create the RenderWare light",
WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
break;
case IDM_LIGHT_NEWCONICAL:
light = CreateLightObj(rwCONICAL);
if (light != NULL)
{
SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
AddLightObjToScene(Scene, light);
RenderScene(window);
}
else
{
MessageBox(window,
"Could not create the RenderWare light",
WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
break;
case IDM_OPTIONS_SHOWLIGHTS:
if (ShowLights)
{
/*
* If the menu item is currently checked then lights are shown so turn them off and
* uncheck the menu.
*/
ShowLights = FALSE;
RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)SetLightObjVisibleState, rwOFF);
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED);
}
else
{
/*
* If the menu item is currently unchecked then lights are not shown so turn them on and
* check the menu.
*/
ShowLights = TRUE;
RwForAllLightsInSceneInt(Scene, (RwLightFuncInt)SetLightObjVisibleState, rwON);
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED);
}
SettingsChanged = TRUE;
RenderScene(window);
break;
case IDM_OPTIONS_SHOWHIGHLIGHT:
if (ShowHighlight)
{
/*
* We are currently showing highlights. So turn the highlight off and
* uncheck the menu item.
*/
ShowHighlight = FALSE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT, MF_BYCOMMAND | MF_UNCHECKED);
}
else
{
/*
* We are currently not showing highlights. So turn the highlight on and
* check the menu item.
*/
ShowHighlight = TRUE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT, MF_BYCOMMAND | MF_CHECKED);
}
SettingsChanged = TRUE;
RenderScene(window);
break;
case IDM_OPTIONS_PLAYMOVIES:
if (PlayMovies)
{
/*
* We are currently playing movies (multi-frame textures). So turn
* the playing off and uncheck the menu item.
*/
PlayMovies = FALSE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES, MF_BYCOMMAND | MF_UNCHECKED);
}
else
{
/*
* We currently are not playing movies (multi-frame textures). So turn
* the playing on and check the menu item.
*/
PlayMovies = TRUE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES, MF_BYCOMMAND | MF_CHECKED);
}
SettingsChanged = TRUE;
break;
case IDM_OPTIONS_MOMENTUM:
if (Momentum)
{
/*
* We are currently have object momentum. So turn it off and uncheck
* the menu item.
*/
Momentum = FALSE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM, MF_BYCOMMAND | MF_UNCHECKED);
}
else
{
/*
* We are currently don't have object momentum. So turn it on and check
* the menu item.
*/
Momentum = TRUE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM, MF_BYCOMMAND | MF_CHECKED);
}
SettingsChanged = TRUE;
break;
case IDM_OPTIONS_CENTERBACKDROP:
if (CenterBackdrop)
{
/*
* We are currently a centered backdrop. So turn it off and uncheck
* the menu item.
*/
CenterBackdrop = FALSE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP, MF_BYCOMMAND | MF_UNCHECKED);
}
else
{
/*
* We are currently don't have a centered backdrop. So turn it on and check
* the menu item.
*/
CenterBackdrop = TRUE;
CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP, MF_BYCOMMAND | MF_CHECKED);
}
UpdateCameraBackdropPosition(Camera);
RenderScene(window);
SettingsChanged = TRUE;
break;
case IDM_OPTIONS_SEARCHPATH:
/*
* Display the about box.
*/
dialogProc = MakeProcInstance(SearchPathDlgProc, AppInstance);
DialogBox(AppInstance, MAKEINTRESOURCE(IDD_SEARCHPATH), window, dialogProc);
FreeProcInstance(dialogProc);
break;
case IDM_CLUMP_RESET:
case IDM_CLUMPPOPUP_RESET:
/*
* Reset the clump to its default position, orientation and scaling.
*/
RwPushScratchMatrix();
RwIdentityMatrix(RwScratchMatrix());
RwTransformClump(SelectedClump, RwScratchMatrix(), rwREPLACE);
RwTransformClumpJoint(SelectedClump, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
RenderScene(window);
break;
case IDM_CAMERA_MOVETO:
case IDM_LIGHTPOPUP_MOVETO:
case IDM_CLUMPPOPUP_MOVETO:
/*
* Alight the camera with the position and orientation of the given object.
*/
RwPushScratchMatrix();
RwGetClumpLTM(SelectedClump, RwScratchMatrix());
RwTransformCamera(Camera, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
RenderScene(window);
break;
case IDM_CAMERA_LOOKAT:
case IDM_LIGHTPOPUP_LOOKAT:
case IDM_CLUMPPOPUP_LOOKAT:
/*
* Make the camera look at the picked object.
*/
RwGetClumpOrigin(SelectedClump, &point);
RwPointCamera(Camera, point.x, point.y, point.z);
RenderScene(window);
break;
case IDM_EDIT_DUPLICATE:
case IDM_LIGHTPOPUP_DUPLICATE:
case IDM_CLUMPPOPUP_DUPLICATE:
/*
* Duplicate the selected object (which may be a clump
* or a light and the clump which represents it).
*/
oldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
newClump = DuplicateClumpObj(SelectedClump);
SetCursor(oldCursor);
if (newClump != NULL)
{
/*
* Move the new clump slightly (an arbitrary amount) to
* the right and down (in camera space) so the new and
* old objects don't occupy the same space.
*/
RwGetCameraLookRight(Camera, &right);
right.x = RMul(CREAL(0.2), right.x);
right.y = RMul(CREAL(0.2), right.y);
right.z = RMul(CREAL(0.2), right.z);
RwGetCameraLookUp(Camera, &up);
up.x = RMul(CREAL(-0.2), up.x);
up.y = RMul(CREAL(-0.2), up.y);
up.z = RMul(CREAL(-0.2), up.z);
RwPushScratchMatrix();
RwTranslateMatrix(RwScratchMatrix(), right.x, right.y, right.z, rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), up.x, up.y, up.z, rwPOSTCONCAT);
RwTransformClump(newClump, RwScratchMatrix(), rwPOSTCONCAT);
if (ISCLUMPLIGHT(newClump))
{
light = GETCLUMPLIGHT(newClump);
SetLightObjVisibleState(light, (ShowLights ? rwON : rwOFF));
/*
* If the clump is the representation of a light reposition
* the light to match the new position of the clump.
*/
RwGetClumpLTM(newClump, RwScratchMatrix());
RwTransformLight(light, RwScratchMatrix(), rwREPLACE);
}
RwPopScratchMatrix();
RenderScene(window);
}
else
{
MessageBox(window,
"Could not duplicate the RenderWare object",
WINDOWTITLE, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
break;
case IDM_LIGHTPOPUP_DELETE:
case IDM_CLUMPPOPUP_DELETE:
/*
* Delete the picked clump. If the picked clump is the
* representation of a light, delete the associated
* light.
*/
DestroyClumpObj(SelectedClump);
DeselectClump(window);
RenderScene(window);
break;
case IDM_LIGHTPOPUP_PROPERTIES:
case IDM_LIGHT_PROPERTIES:
/*
* Display the light properties dialog.
*/
dialogProc = MakeProcInstance(LightPropsDlgProc, AppInstance);
if (DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_LIGHTPROPS),
window, dialogProc, (LPARAM)GETCLUMPLIGHT(SelectedClump)) == IDOK)
RenderScene(window);
FreeProcInstance(dialogProc);
break;
case IDM_CLUMPPOPUP_PROPERTIES:
case IDM_CLUMP_PROPERTIES:
/*
* Display the clump properties dialog.
*/
dialogProc = MakeProcInstance(ClumpPropsDlgProc, AppInstance);
DialogBoxParam(AppInstance, MAKEINTRESOURCE(IDD_CLUMPPROPS),
window, dialogProc, (LPARAM)SelectedClump);
FreeProcInstance(dialogProc);
break;
case IDM_SCENE_BACKDROPDELETE:
case IDM_BACKGROUNDPOPUP_DELETE:
/*
* Delete the camera's backdrop (if there is one).
*/
if (RwGetCameraBackdrop(Camera) != NULL)
{
RwDestroyRaster(RwGetCameraBackdrop(Camera));
RwSetCameraBackdrop(Camera, NULL);
EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE, MF_BYCOMMAND | MF_GRAYED);
BackdropFileName[0] = '\0';
SettingsChanged = TRUE;
RenderScene(window);
}
break;
case IDM_HELP_CONTENTS:
/*
* Display the help file.
*/
WinHelp(window, "RWVIEW.HLP", HELP_CONTENTS, 0L);
break;
case IDM_HELP_ABOUT:
/*
* Display the about box.
*/
dialogProc = MakeProcInstance(AboutBoxDlgProc, AppInstance);
DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), window, dialogProc);
FreeProcInstance(dialogProc);
break;
}
}
/**********************************************************************/
/*
* Handle MS Window's timer expiry. This function will perform any
* animation actions necessary, including spinning clumps and animating
* textures.
*/
static void
OnTimer(HWND window)
{
BOOL render;
render = FALSE;
/*
* Determine if there is a clump to spin (we also disable the spin
* if the mouse is being dragged).
*/
if (Momentum && SpinClump && (MouseMoveMode == mmNoAction))
{
/*
* Spin the last clump picked by the last computed spin matrix.
*/
RwTransformClumpJoint(SelectedClump, SpinMatrix, rwPOSTCONCAT);
render = TRUE;
if ((FrameNumber++ & 0x7f) == 0)
{
/*
* Every 128 frames (a somewhat arbitrary frequency) we call
* RwOrthoNormalizeMatrix() on the clump's joint matrix to
* correct any errors which may have crept into it during
* successive matrix concatenations. This is necessary due
* to the inevitable accuracy limitations of fixed-point
* numbers. It is unlikely that this action will be necessary
* in your own applications unless you perform a very large
* number of incremental rotation matrix concatenations (as
* is done here). If this is the case with your application,
* periodic use of RwOrthoNormalizeMatrix() will ensure
* that these rounding errors will be eliminated.
*
* We call RwOrthoNormalizeMatrix() in the floating-point
* version of RwView as well, as it is not a particularly
* expensive operation and the same problems of rounding
* errors in matrices can occur when using floating-point.
* Although they are more uncommon.
*/
RwPushScratchMatrix();
RwGetClumpJointMatrix(SelectedClump, RwScratchMatrix());
RwOrthoNormalizeMatrix(RwScratchMatrix(), RwScratchMatrix());
RwTransformClumpJoint(SelectedClump, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
}
}
if (PlayMovies)
{
/*
* Play movies. Enumerate over all the textures in the texture
* dictionary stack calling RwTextureNextFrame() to bump the
* current frame pointer of each texture. For single frame textures
* this is a no-op.
*/
RwForAllNamedTextures(RwTextureNextFrame);
render = TRUE;
}
if (render)
{
/*
* Re-render the scene if necessary.
*/
RenderScene(window);
}
}
/**********************************************************************/
/*
* The RenderWare search path dialog procedure.
*/
BOOL CALLBACK
SearchPathDlgProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
{
char searchPath[RWMAXPATHLEN];
switch (message)
{
case WM_INITDIALOG:
/*
* Initialize the edit control with the current RenderWare
* search path.
*/
RwGetShapePath(searchPath);
SetDlgItemText(dialog, IDC_SEARCHPATH_PATH, searchPath);
SetFocus(GetDlgItem(dialog, IDC_SEARCHPATH_PATH));
return FALSE;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
/*
* Set the RenderWare search path to the contents of
* the edit control.
*/
GetDlgItemText(dialog, IDC_SEARCHPATH_PATH, searchPath, sizeof(searchPath));
RwSetShapePath(searchPath, rwREPLACE);
EndDialog(dialog, IDOK);
break;
case IDCANCEL:
EndDialog(dialog, IDCANCEL);
break;
}
return TRUE;
}
return FALSE;
}
/**********************************************************************/
/*
* The about box dialog procedure.
*/
BOOL CALLBACK
AboutBoxDlgProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
{
char version[80];
RwBool isFixed;
RwBool isDebug;
RwInt32 depth;
RwBool usingDIBs;
RwBool usingWinG;
switch (message)
{
case WM_INITDIALOG:
/*
* Get information about the RenderWare library being used and
* set the appropriate fields of the dialog with these values.
*/
RwGetSystemInfo(rwVERSIONSTRING, &version, sizeof(version));
RwGetSystemInfo(rwFIXEDPOINTLIB, &isFixed, sizeof(isFixed));
RwGetSystemInfo(rwDEBUGGINGLIB, &isDebug, sizeof(isDebug));
RwGetDeviceInfo(rwRENDERDEPTH, &depth, sizeof(depth));
RwGetDeviceInfo(rwWINUSINGDIBS, &usingDIBs, sizeof(usingDIBs));
RwGetDeviceInfo(rwWINUSINGWING, &usingWinG, sizeof(usingWinG));
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREVERSION, version);
SetDlgItemText(dialog, IDC_ABOUT_RENDERWARENUMERICS,
(isFixed ? "Fixed-point" : "Floating-point"));
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREKERNEL,
(isDebug ? "Debugging" : "Retail"));
SetDlgItemText(dialog, IDC_ABOUT_RENDERWARERENDERING,
((depth == 8L) ? "8-bit (256 color)" : "16-bit (65536 color)"));
if (usingDIBs)
{
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREBITMAPS, "DIBs");
}
else if (usingWinG)
{
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREBITMAPS, "WinG");
}
else
{
#if defined(WIN32)
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREBITMAPS, "DIB Sections");
#else
SetDlgItemText(dialog, IDC_ABOUT_RENDERWAREBITMAPS, "DDBs");
#endif
}
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
EndDialog(dialog, IDOK);
break;
}
return TRUE;
}
return FALSE;
}
/**********************************************************************/
/*
* The window procedure for this application's window.
*/
LRESULT CALLBACK
MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
POINT point;
#if defined(WIN32)
POINTS points;
#endif
switch (message)
{
case WM_CREATE:
/*
* Create the pop-up menus.
*/
if (!CreatePopupMenus())
return -1L;
/*
* Ensure the menu items are in the correct state.
*/
EnableMenuItem(GetMenu(window), IDM_SCENE_BACKDROPDELETE,
MF_BYCOMMAND | (RwGetCameraBackdrop(Camera) != NULL ? MF_ENABLED : MF_GRAYED));
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWHIGHLIGHT,
MF_BYCOMMAND | (ShowHighlight ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(GetMenu(window), IDM_OPTIONS_SHOWLIGHTS,
MF_BYCOMMAND | (ShowLights ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(GetMenu(window), IDM_OPTIONS_PLAYMOVIES,
MF_BYCOMMAND | (PlayMovies ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(GetMenu(window), IDM_OPTIONS_MOMENTUM,
MF_BYCOMMAND | (Momentum ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(GetMenu(window), IDM_OPTIONS_CENTERBACKDROP,
MF_BYCOMMAND | (CenterBackdrop ? MF_CHECKED : MF_UNCHECKED));
AppendMRUFileMenuItems(window);
/*
* There is not initially selected clump.
*/
DeselectClump(window);
/*
* 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.
*/
SetTimer(window, 1, 20, NULL);
return 0L;
#if defined(CONSTRAIN_SIZE)
case WM_GETMINMAXINFO:
#if defined(__WINDOWS_386__)
/*
* The MK_FP32() is necessary under Watcom to convert
* an Windows 16 bit far pointer to a 32 bit far pointer.
*/
OnGetMinMaxInfo((MINMAXINFO FAR *)MK_FP32((void*)lParam));
#else
OnGetMinMaxInfo((MINMAXINFO FAR *)lParam);
#endif
return 0L;
#endif
case WM_SIZE:
if (RwIsOpen)
OnSize(window, LOWORD(lParam), HIWORD(lParam));
return 0L;
case WM_MEASUREITEM:
#if defined(__WINDOWS_386__)
/*
* The MK_FP32() is necessary under Watcom to convert
* an Windows 16 bit far pointer to a 32 bit far pointer.
*/
OnMeasureItem(window, (MEASUREITEMSTRUCT FAR *)MK_FP32((void*)lParam));
#else
OnMeasureItem(window, (MEASUREITEMSTRUCT FAR *)lParam);
#endif
return 0L;
case WM_DRAWITEM:
#if defined(__WINDOWS_386__)
/*
* The MK_FP32() is necessary under Watcom to convert
* an Windows 16 bit far pointer to a 32 bit far pointer.
*/
OnDrawItem(window, (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam));
#else
OnDrawItem(window, (DRAWITEMSTRUCT FAR *)lParam);
#endif
return 0L;
case WM_DROPFILES:
if (RwIsOpen)
{
/*
* Turn the timer off for the duration of the drop
*/
KillTimer(window, 1);
OnDrop(window, (HDROP)wParam);
/*
* Turn the timer back on to start the animations back
* up.
*/
SetTimer(window, 1, 20, NULL);
}
return 0L;
case WM_LBUTTONDOWN:
if (RwIsOpen)
{
#if defined(WIN32)
points = MAKEPOINTS(lParam);
POINTSTOPOINT(point, points);
#else
point = MAKEPOINT(lParam);
#endif
OnLButtonDown(window, &point, wParam);
}
return 0L;
case WM_RBUTTONDOWN:
if (RwIsOpen)
{
#if defined(WIN32)
points = MAKEPOINTS(lParam);
POINTSTOPOINT(point, points);
#else
point = MAKEPOINT(lParam);
#endif
OnRButtonDown(window, &point, wParam);
}
return 0L;
case WM_MOUSEMOVE:
if (RwIsOpen)
{
if (MouseMoveMode != mmNoAction)
{
#if defined(WIN32)
points = MAKEPOINTS(lParam);
POINTSTOPOINT(point, points);
#else
point = MAKEPOINT(lParam);
#endif
OnMouseMove(window, &point);
}
}
return 0L;
case WM_LBUTTONUP:
if (RwIsOpen)
OnLButtonUp();
return 0L;
case WM_RBUTTONUP:
if (RwIsOpen)
OnRButtonUp();
return 0L;
case WM_PAINT:
if (RwIsOpen)
OnPaint(window);
return 0L;
case WM_COMMAND:
if (RwIsOpen)
{
/*
* Turn the timer off for the duration of the command
* action.
*/
KillTimer(window, 1);
if ((HWND)LOWORD(lParam) == (HWND)0)
OnMenu(window, wParam);
/*
* Turn the timer back on to start the animations back
* up.
*/
SetTimer(window, 1, 20, NULL);
}
return 0L;
case WM_TIMER:
if (RwIsOpen)
OnTimer(window);
return 0L;
case WM_CLOSE:
/*
* If the settings have changed then ask the user if she or he
* wishes to save them.
*/
if (SettingsChanged)
{
if (MessageBox(window, "Save the new 3D Object Viewer settings?",
WINDOWTITLE, MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES)
WriteSettings(TRUE);
else
WriteSettings(FALSE);
}
else
{
WriteSettings(FALSE);
}
DestroyWindow(window);
return 0L;
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);
/*
* Destroy the pop-up menus.
*/
DestroyPopupMenus();
/*
* Quit message handling.
*/
PostQuitMessage(0);
return 0L;
}
/*
* Let Windows handle all other messages.
*/
return DefWindowProc(window, message, wParam, lParam);
}
/**********************************************************************/
/*
* Windows application entry point.
*/
int PASCAL
WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
{
MSG msg;
HWND window;
HACCEL accel;
/*
* Cache the instance handle in a global variable for later use.
*/
AppInstance = instance;
if (prevInstance)
{
/*
* Only allow one viewer application to run at any one time.
*/
MessageBox(NULL,
"The RenderWare 3D Object Viewer is already running."
"Only a single 3D Object Viewer can run at any one time",
WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
return FALSE;
}
/*
* Register the window class.
*/
if (!InitApplication(instance))
return FALSE;
/*
* Read the initial settings from the initialization file.
*/
ReadSettings();
/*
* Create the window.
*/
window = InitInstance(instance);
if (window == NULL)
return FALSE;
/*
* 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);
/*
* Load the accelerators.
*/
accel = LoadAccelerators(AppInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
/*
* Enter the message processing loop.
*/
while (GetMessage(&msg, NULL, 0U, 0U))
{
if (!TranslateAccelerator(window, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*
* Tidy up the 3D (RenderWare) components of the application.
*/
TidyUp3D();
return msg.wParam;
}
/**********************************************************************/