home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Learn 3D Graphics Programming on the PC
/
Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso
/
rwwin
/
rwroller.c_
/
rwroller.bin
Wrap
Text File
|
1995-11-14
|
79KB
|
2,433 lines
/**********************************************************************
*
* File : rwroller.c
*
* Abstract : A virtual Rollercoaster for RenderWare and Microsoft
* Windows.
*
* 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 RWROLLER.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.4 library as follows:
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
* /DRWFIXED /fo=rwroller.obj rwroller.c
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
* /DRWFIXED /fo=rolltype.obj rolltype.c
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /fpc /s /j /ei /oneatx
* /DRWFIXED /fo=rwpal.obj rwpal.c
* wlink option caseexact option stack=32768 name rwroller system win386
* file rwroller.obj rolltype.obj rwpal.obj library
* \rwwin\lib\rwwrxp.lib
* wbind rwroller -R rwroller,rc
*
* (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.4 library as follows:
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
* /DRWFLOAT /fo=rwroller.obj rwroller.c
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
* /DRWFLOAT /fo=rolltype.obj rolltype.c
* wcc386p /I=\rwwin\include /zW /4r /zp4 /mf /7 /s /j /ei /oneatx
* /DRWFLOAT /fo=rwpal.obj rwpal.c
* wlink option caseexact option stack=32768 name rwroller system win386
* file rwroller.obj rolltype.obj rwpal.obj library
* \rwwin\lib\rwwrlp.lib
* wbind rwroller -R rwroller.rc
*
* (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:
*
* The project should contain the following files:
* rwroller.c
* rwroller.def
* rwroller.rc
* rolltype.c
* rwpal.c
*
* Compiler Options:
* Memory Model
* Model: Large
* Preprocessos
* Symbols: RWFIXED
* Include path: \rwwin\include
*
* Linker Options:
* Input
* Libraries: ..., \rwwin\lib\rwxv.lib
* Windows libraries
* COMMDLG
* 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:
*
* The project should contain the following files:
* rwroller.c
* rwroller.def
* rwroller.rc
* rolltype.c
* rwpal.c
*
* Compiler Options:
* Memory Model
* Model: Large
* Preprocessos
* Symbols: RWFLOAT
* Include path: \rwwin\include
*
* Linker Options:
* Input
* Libraries: ..., \rwwin\lib\rwlv.lib
* Windows libraries
* COMMDLG
* 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 rwroller.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
* 16-Bit Compiler
* Calling Conventions C
* Memory Model Large
*
* You must add the nodes rolltype.c, rwpal.c and \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 rwroller.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
* 16-Bit Compiler
* Calling Conventions C
* Memory Model Large
*
* You must add the nodes rolltype.c, rwpal.c and \rwwin\lib\rwxb.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 specific...
*/
#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 <math.h>
#include <string.h>
#if !defined(__WINDOWS_386__)
#include <memory.h>
#endif
#include <rwlib.h>
#include <rwwin.h>
#include "resource.h"
#include "rolltype.h"
#include "pick.h"
#include "common.h"
#include "palette.h"
/**********************************************************************
*
* Application constants.
*
**********************************************************************/
/*
* Class name for the MS Window's window class.
*/
#define RWROLLERCLASSNAME "RwRollerClass"
/*
* The default window title (and the title for error dialogs).
*/
#define WINDOWTITLE "RenderWare Rollercoaster"
/*
* Name of the palette and backdrop files to load.
*/
#define PALETTEFILENAME "rwroller.pal"
#define BACKDROPFILENAME "mount64.bmp"
/*
* Default size of the viewer's window.
*/
#define DEFAULT_WINDOW_WIDTH 320
#define DEFAULT_WINDOW_HEIGHT 220
/*
* Maximum size of the viewer's window.
*/
#define MAXIMUM_WINDOW_WIDTH 640
#define MAXIMUM_WINDOW_HEIGHT 480
/*
* Default distance of the camera from the origin.
*/
#define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
/**********************************************************************
*
* Forward functions.
*
**********************************************************************/
#ifndef __WINDOWS_386__
/*
* Stealth API function to allow the app to connect to the DLL even if
* it is already in use. This should only be used when the DLL thinks
* it is in use (but isn't) because a previous client has crashed.
*/
extern void
_rwResetReferenceCount(void);
#endif
LRESULT CALLBACK
MainWndProc(HWND window, UINT mesage, WPARAM wParam, LPARAM lParam);
/**********************************************************************
*
* Type definitions.
*
**********************************************************************/
/*
* The camera's motion mode.
*/
typedef enum
{
cmNONE, /* No camera motion. */
cmRIDE, /* Camera "rides" the rollercoaster. */
cmSPOTTER, /* "Spotter" camera rotates around the rollercoaster car. */
cmTRAIL, /* Camera trails the car on the rollercoaster. */
cmLEAD, /* Camera "leads" the car on the rollercoaster. */
cmELEVATION, /* Camera pans around and zooms into the rollercoaster elevation. */
cmPLAN /* Camera pans around and zooms into the rollercoaster plan. */
} CMMode;
/*
* This enumerated type tells us what kind of action should be taken
* on mouse events.
*/
typedef enum
{
mmNONE, /* No mouse move action. */
mmMOVECAMERA, /* Move the camera on mouse move. */
mmALTMOVECAMERA, /* Alternative camera motion on mouse move. */
mmMOVECONTROL, /* Move a spline control point on mouse move. */
mmMOVELIGHT /* Move the light. */
} MMMode;
/*
* The direction of motion of the viewer on the track.
*/
typedef enum
{
vmLEVEL,
vmCLIMBING,
vmFALLING
} VMMode;
/**********************************************************************
*
* Application global variables.
*
**********************************************************************/
/*
* Instance handle for this application.
*/
static HANDLE AppInstance;
/*
* Drive from which the application was launched.
*/
static char AppPath[_MAX_PATH];
/*
* The global, single camera instance.
*/
static RwCamera *Camera = NULL;
static CMMode CameraMode = cmRIDE;
static RwReal CameraAngle = CREAL(0.0);
/*
* 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 = mmNONE;
/*
* 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);
/*
* 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 ThreeDInitialized = FALSE;
/*
* The current rollercoaster.
*/
static RollerCoasterType *CurrentCoaster = NULL;
/*
* Default coaster filename table.
*/
static char DefaultCoasterFilenames[4][_MAX_PATH] =
{
"track1.rrc",
"track2.rrc",
"track3.rrc",
"track4.rrc"
};
/*
* Filename used for communication with the Choose dialog.
*/
static char CoasterFilename[_MAX_PATH];
/*
* The current control point picked (if any).
*/
static int ControlPoint = 0;
/*
* Whether to use WinG or not. By default we do (unless we are running
* under Windows95 or Windows NT 3.5 where we ignore WinG and use
* DIB Sections which are much nicer).
*/
static BOOL UseWinG = TRUE;
/*
* Can we stretch bitmaps efficiently.
*/
static BOOL CanStretch = FALSE;
/*
* If we can stretch, are we stretching.
*/
static BOOL DoStretch = FALSE;
/*
* Whether to show the coaster's car or not.
*/
static BOOL ShowCar = TRUE;
/**********************************************************************
*
* Functions.
*
**********************************************************************/
/**********************************************************************/
static BOOL
TrackBackdropToCamera(RwCamera *camera)
{
RwRaster *backdrop;
RwInt32 backdropWidth;
RwInt32 backdropHeight;
RwV3d at;
RwReal angle;
RwInt32 xOffset;
RwInt32 yOffset;
RwInt32 windowWidth;
RwInt32 windowHeight;
backdrop = RwGetCameraBackdrop(camera);
/*
* No-op if the camera has no backdrop.
*/
if (backdrop != NULL)
{
/*
* Get the window and backdrop dimensions.
*/
RwGetCameraViewport(camera, NULL, NULL, &windowWidth, &windowHeight);
backdropWidth = RwGetRasterWidth(backdrop);
backdropHeight = RwGetRasterHeight(backdrop);
/*
* We use the look at vector for determining both
* horizontal and vertical positioning.
*/
RwGetCameraLookAt(camera, &at);
/*
* Compute the horizontal position. This is done
* by compute the camera's angle of rotate about
* the Z axis. The angle is then directly converted
* into a horizontal backdrop offset.
*/
/*
* Compute the angle in the range 0 => PI.
*/
at.y = CREAL(0.0);
RwNormalize(&at);
if (at.z > CREAL(1.0))
at.z = CREAL(1.0);
else if (at.z < CREAL(-1.0))
at.z = CREAL(-1.0);
angle = FL2REAL(acos(REAL2FL(at.z)));
/*
* Get the angle in the range 0 => 2 PI
*/
if (at.x < CREAL(0.0))
angle = RSub(CREAL(M_2PI), angle);
/*
* The backdrop X offset is derived simply from
* the angle computed above.
*/
xOffset = -REAL2INT(RDiv(RMul(angle, INT2REAL(backdropWidth)), CREAL(M_2PI)));
/*
* Compute the vertical position. This is done by getting
* the angle between the look at vector and the Y axis. We
* simply use the dot product (cosine of the angle) multiplied
* by a scale factor to compute the vertical offset.
*/
RwGetCameraLookAt(Camera, &at);
yOffset = REAL2INT(RAdd(INT2REAL(windowHeight / 2), RMul(RMul(at.y, CREAL(1.5)), INT2REAL(windowHeight / 2)))) - (backdropHeight / 2);
RwSetCameraBackdropOffset(Camera, xOffset, 0);
RwSetCameraBackdropViewportRect(Camera, 0, yOffset, windowWidth, backdropHeight);
}
return TRUE;
}
/**********************************************************************/
/*
* Update the application menu to reflect the current application
* state.
*/
static void
UpdateMenu(HWND window)
{
HMENU menu;
menu = GetMenu(window);
CheckMenuItem(menu, IDM_VIEWPOINT_RIDE,
MF_BYCOMMAND | (CameraMode == cmRIDE ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_VIEWPOINT_SPOTTER,
MF_BYCOMMAND | (CameraMode == cmSPOTTER ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_VIEWPOINT_TRAIL,
MF_BYCOMMAND | (CameraMode == cmTRAIL ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_VIEWPOINT_LEAD,
MF_BYCOMMAND | (CameraMode == cmLEAD ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_EDIT_SIDEVIEW,
MF_BYCOMMAND | (CameraMode == cmELEVATION ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_EDIT_TOPVIEW,
MF_BYCOMMAND | (CameraMode == cmPLAN ? MF_CHECKED : MF_UNCHECKED));
/*
* Can we stretch output (are we using WinG or DIB Sections)?
*/
EnableMenuItem(menu, IDM_OPTION_STRETCH,
MF_BYCOMMAND | (CanStretch ? MF_ENABLED : MF_GRAYED));
/*
* Are we stretching?
*/
CheckMenuItem(menu, IDM_OPTION_STRETCH,
MF_BYCOMMAND | (DoStretch ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(menu, IDM_OPTION_SHOWCAR,
MF_BYCOMMAND | (ShowCar ? MF_CHECKED : MF_UNCHECKED));
}
/**********************************************************************/
/*
* Put camera into ride mode.
*/
static void
SwitchToRideView(HWND window)
{
CameraMode = cmRIDE;
SwitchToCoasterRideMode(CurrentCoaster);
UpdateMenu(window);
}
/**********************************************************************/
/*
* Put camera into ride mode.
*/
static void
SwitchToSpotterView(HWND window)
{
CameraMode = cmSPOTTER;
SwitchToCoasterRideMode(CurrentCoaster);
UpdateMenu(window);
}
/**********************************************************************/
/*
* Put camera into trail mode.
*/
static void
SwitchToTrailView(HWND window)
{
CameraMode = cmTRAIL;
SwitchToCoasterRideMode(CurrentCoaster);
UpdateMenu(window);
}
/**********************************************************************/
/*
* Put camera into lead mode.
*/
static void
SwitchToLeadView(HWND window)
{
CameraMode = cmLEAD;
SwitchToCoasterRideMode(CurrentCoaster);
UpdateMenu(window);
}
/**********************************************************************/
/*
* Put camera into elevation mode.
*/
static void
SwitchToElevationView(HWND window)
{
CameraMode = cmELEVATION;
SwitchToCoasterEditMode(CurrentCoaster);
RwSetCameraPosition(Camera,
CurrentCoaster->elevationPosition.x,
CurrentCoaster->elevationPosition.y,
CurrentCoaster->elevationPosition.z);
RwPointCamera(Camera,
CREAL(0.0),
CurrentCoaster->elevationPosition.y,
CREAL(0.0));
RwSetCameraLookUp(Camera,
CREAL(0.0),
CREAL(1.0),
CREAL(0.0));
TrackBackdropToCamera(Camera);
UpdateMenu(window);
}
/**********************************************************************/
/*
* Put the camera into plan mode.
*/
static void
SwitchToPlanView(HWND window)
{
CameraMode = cmPLAN;
SwitchToCoasterEditMode(CurrentCoaster);
RwSetCameraPosition(Camera,
CurrentCoaster->planPosition.x,
CurrentCoaster->planPosition.y,
CurrentCoaster->planPosition.z);
/*
* Unsatisfactory hack to get the camera aligned in the
* way we wish. This itself will fail if the camera's is
* looking down the X-axis already - sigh!
*/
RwSetCameraLookUp(Camera,
CREAL(1.0),
CREAL(0.0),
CREAL(0.0));
RwPointCamera(Camera,
CurrentCoaster->planPosition.x,
CREAL(0.0),
CurrentCoaster->planPosition.z);
RwSetCameraLookUp(Camera,
CREAL(0.0),
CREAL(0.0),
CREAL(1.0));
UpdateMenu(window);
}
/**********************************************************************/
static void
SwitchCoaster(HWND window, RollerCoasterType *coaster)
{
DestroyRollerCoaster(CurrentCoaster);
CurrentCoaster = coaster;
switch (CameraMode)
{
case cmRIDE:
SwitchToRideView(window);
break;
case cmSPOTTER:
SwitchToSpotterView(window);
break;
case cmTRAIL:
SwitchToTrailView(window);
break;
case cmLEAD:
SwitchToLeadView(window);
break;
case cmELEVATION:
SwitchToElevationView(window);
break;
case cmPLAN:
SwitchToPlanView(window);
break;
}
if (ShowCar)
EnableCoasterCar(CurrentCoaster);
else
DisableCoasterCar(CurrentCoaster);
SetWindowText(window, CurrentCoaster->description);
}
/**********************************************************************/
/*
* Dialog procedure for the rollercoaster about dialog.
*/
BOOL CALLBACK
CoasterAboutDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
{
DRAWITEMSTRUCT FAR *drawItemStruct;
RECT FAR *rect;
HDC dc;
HDC bitmapDC;
HBITMAP oldBitmap;
BITMAP bitmapInfo;
int x;
int y;
int width;
int height;
static HBITMAP renderWareLogo = NULL;
switch (message)
{
case WM_INITDIALOG:
renderWareLogo = LoadBitmap(AppInstance, MAKEINTRESOURCE(IDB_RWLOGO));
return TRUE;
case WM_DRAWITEM:
#if defined(__WINDOWS_386__)
drawItemStruct = (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam);
#else
drawItemStruct = (DRAWITEMSTRUCT *)lParam;
#endif
rect = &drawItemStruct->rcItem;
dc = drawItemStruct->hDC;
switch (drawItemStruct->CtlID)
{
case IDC_RENDERWARELOGO:
GetObject(renderWareLogo, sizeof(BITMAP), &bitmapInfo);
width = bitmapInfo.bmWidth;
height = bitmapInfo.bmHeight;
x = rect->left + (((rect->right - rect->left) - width) / 2);
y = rect->top + (((rect->bottom - rect->top) - height) / 2);
bitmapDC = CreateCompatibleDC(dc);
oldBitmap = SelectObject(bitmapDC, renderWareLogo);
BitBlt(dc, x, y, width, height, bitmapDC, 0, 0, SRCCOPY);
SelectObject(bitmapDC, oldBitmap);
DeleteDC(bitmapDC);
break;
}
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
DeleteObject(renderWareLogo);
EndDialog(dialog, IDOK);
return TRUE;
}
}
return FALSE;
}
/**********************************************************************/
/*
* Dialog procedure for the rollercoaster menu dialog.
*/
BOOL CALLBACK
CoasterMenuDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
{
DRAWITEMSTRUCT FAR *drawItemStruct;
RECT FAR *rect;
HDC dc;
HPEN topLeftPen;
HPEN bottomRightPen;
HPEN pen;
HPEN oldPen;
int i;
RollerCoasterType *coaster;
OPENFILENAME openFileName;
char filename[_MAX_PATH];
static int numCoasters;
static RollerCoasterType *coasters[4];
static RwCamera *camera = NULL;
switch (message)
{
case WM_INITDIALOG:
/*
* Create the camera used in this dialog.
*/
camera = RwCreateCamera(200, 200, NULL);
if (camera == NULL)
return FALSE;
RwSetCameraBackColor(camera, CREAL(0.0), CREAL(0.0), CREAL(1.0));
RwSetCameraViewwindow(camera, CREAL(0.4), CREAL(0.4));
RwTiltCamera(camera, CREAL(30.0));
RwVCMoveCamera(camera, CREAL(0.0), CREAL(0.0), CREAL(-3.0));
/*
* Attempt to load the four default rollercoasters.
*/
numCoasters = 0;
for (i = 0; i < 4; i++)
{
strcpy(filename, AppPath);
strcat(filename, DefaultCoasterFilenames[i]);
coasters[numCoasters] = ReadRollerCoaster(filename);
if (coasters[numCoasters] != NULL)
{
DisableCoasterControlPoints(coasters[numCoasters]);
DisableCoasterCar(coasters[numCoasters]);
numCoasters++;
}
}
for (i = 0; i < numCoasters; i++)
SetDlgItemText(dialog, IDC_LABEL_ONE + i, coasters[i]->description);
SetTimer(dialog, 2, 20, NULL);
return TRUE;
case WM_DRAWITEM:
#if defined(__WINDOWS_386__)
drawItemStruct = (DRAWITEMSTRUCT FAR *)MK_FP32((void*)lParam);
#else
drawItemStruct = (DRAWITEMSTRUCT *)lParam;
#endif
if (camera != NULL)
{
rect = &drawItemStruct->rcItem;
dc = drawItemStruct->hDC;
pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
oldPen = SelectObject(dc, pen);
MoveToEx(dc, rect->left, rect->top, NULL);
LineTo(dc, rect->right - 1, rect->top);
LineTo(dc, rect->right - 1, rect->bottom - 1);
LineTo(dc, rect->left, rect->bottom - 1);
LineTo(dc, rect->left, rect->top);
SelectObject(dc, oldPen);
DeleteObject(pen);
if (drawItemStruct->itemState & ODS_SELECTED)
{
topLeftPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
bottomRightPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
}
else
{
topLeftPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
bottomRightPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
}
oldPen = SelectObject(dc, topLeftPen);
MoveToEx(dc, rect->left + 1, rect->top + 1, NULL);
LineTo(dc, rect->right - 2, rect->top + 1);
MoveToEx(dc, rect->left + 1, rect->top + 2, NULL);
LineTo(dc, rect->right - 3, rect->top + 2);
MoveToEx(dc, rect->left + 1, rect->top + 3, NULL);
LineTo(dc, rect->left + 1, rect->bottom - 2);
MoveToEx(dc, rect->left + 2, rect->top + 3, NULL);
LineTo(dc, rect->left + 2, rect->bottom - 3);
SelectObject(dc, oldPen);
oldPen = SelectObject(dc, bottomRightPen);
MoveToEx(dc, rect->right - 2, rect->bottom - 2, NULL);
LineTo(dc, rect->left + 1, rect->bottom - 2);
MoveToEx(dc, rect->right - 2, rect->bottom - 3, NULL);
LineTo(dc, rect->left + 2, rect->bottom - 3);
MoveToEx(dc, rect->right - 2, rect->bottom - 4, NULL);
LineTo(dc, rect->right - 2, rect->top + 2);
MoveToEx(dc, rect->right - 3, rect->bottom - 4, NULL);
LineTo(dc, rect->right - 3, rect->top + 3);
SelectObject(dc, oldPen);
DeleteObject(topLeftPen);
DeleteObject(bottomRightPen);
coaster = coasters[drawItemStruct->CtlID - IDC_ROLLER_ONE];
if (coaster != NULL)
{
RwSetCameraViewport(camera,
rect->left + 3,
rect->top + 3,
(rect->right - rect->left) - 6,
(rect->bottom - rect->top) - 6);
RwInvalidateCameraViewport(camera);
RwBeginCameraUpdate(camera, (void *)(DWORD)dialog);
RwClearCameraViewport(camera);
RwRenderScene(coaster->scene);
RwEndCameraUpdate(camera);
RwShowCameraImage(camera, (void *)(DWORD)dc);
}
}
return TRUE;
case WM_TIMER:
if (camera != NULL)
{
RwPushScratchMatrix();
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0), CREAL(10.0), rwREPLACE);
RwTransformCamera(camera, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_ONE), NULL, FALSE);
InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_TWO), NULL, FALSE);
InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_THREE), NULL, FALSE);
InvalidateRect(GetDlgItem(dialog, IDC_ROLLER_FOUR), NULL, FALSE);
}
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case IDC_ROLLER_ONE:
case IDC_ROLLER_TWO:
case IDC_ROLLER_THREE:
case IDC_ROLLER_FOUR:
strcpy(CoasterFilename, DefaultCoasterFilenames[wParam - IDC_ROLLER_ONE]);
KillTimer(dialog, 2);
for (i = 0; i < numCoasters; i++)
DestroyRollerCoaster(coasters[i]);
if (camera != NULL)
{
RwDestroyCamera(camera);
camera = NULL;
}
EndDialog(dialog, IDOK);
return TRUE;
case IDC_ROLLER_MORE:
KillTimer(dialog, 2);
filename[0] = '\0';
memset(&openFileName, '\0', sizeof(OPENFILENAME));
openFileName.lStructSize = sizeof(OPENFILENAME);
openFileName.hwndOwner = dialog;
openFileName.lpstrFilter = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
openFileName.nFilterIndex = 1;
openFileName.lpstrFile = &filename[0];
openFileName.nMaxFile = sizeof(filename);
openFileName.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&openFileName))
{
strcpy(CoasterFilename, filename);
KillTimer(dialog, 2);
for (i = 0; i < numCoasters; i++)
DestroyRollerCoaster(coasters[i]);
if (camera != NULL)
{
RwDestroyCamera(camera);
camera = NULL;
}
EndDialog(dialog, IDOK);
}
else
{
SetTimer(dialog, 2, 20, NULL);
}
return TRUE;
case IDCANCEL:
KillTimer(dialog, 2);
for (i = 0; i < numCoasters; i++)
DestroyRollerCoaster(coasters[i]);
if (camera != NULL)
{
RwDestroyCamera(camera);
camera = NULL;
}
EndDialog(dialog, IDCANCEL);
return TRUE;
}
}
return FALSE;
}
/**********************************************************************/
/*
* Dialog procedure for the rollercoaster menu dialog.
*/
BOOL CALLBACK
CoasterDescriptionDialogProc(HWND dialog, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
SetDlgItemText(dialog, IDC_COASTER_DESCRIPTION, CurrentCoaster->description);
SetFocus(GetDlgItem(dialog, IDC_COASTER_DESCRIPTION));
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
GetDlgItemText(dialog, IDC_COASTER_DESCRIPTION,
CurrentCoaster->description,
sizeof(CurrentCoaster->description));
EndDialog(dialog, IDOK);
return TRUE;
case IDCANCEL:
EndDialog(dialog, IDCANCEL);
return TRUE;
}
}
return FALSE;
}
/**********************************************************************/
/*
* 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;
windowClass.lpfnWndProc = (WNDPROC)MainWndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instance;
windowClass.hIcon = NULL;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
windowClass.lpszClassName = RWROLLERCLASSNAME;
return RegisterClass(&windowClass);
}
/**********************************************************************/
/*
* 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. The window is not given a title as we
* set it during Init3D() with information about the version of
* RenderWare being used.
*/
return CreateWindow(RWROLLERCLASSNAME, "",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT,
NULL, NULL, instance, NULL);
}
/**********************************************************************/
/*
* 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 buffer[_MAX_PATH];
RwRaster *backdrop;
RwOpenArgument arg;
RwInt32 numOptions;
/*
* Warn the user that the display is not in one of RenderWare's native
* rendering depths and recommend that (for best performance) they
* switch to an 8- or 16-bit mode.
*/
CheckDisplayDepth(window);
#if defined(WIN32)
/*
* If we are building a Win32 executable then we will not explicitly
* select WinG as DIB Sections are likely to ba available and DIB
* Sections are much more super than WinG.
*/
numOptions = 0L;
#else
/*
* If we are running on Windows 3.1 then we will select WinG (so we can
* have stretching) unless the program has been launched with the -g
* option to disable WinG.
*/
if (UseWinG)
{
arg.option = rwWINUSEWING;
numOptions = 1L;
}
else
{
numOptions = 0L;
}
#endif
if (!RwOpenExt("MSWindows", NULL, numOptions, &arg))
{
MessageBox(window, "Could not open the RenderWare library",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
return FALSE;
}
/*
* By requesting that the library use WinG (or DIB Sections) we
* wanted to enable bitmap stretching. However, we don't always
* get what we want (for example if WinG is not installed) so we
* must verify that we can indeed stretch efificiently before
* enabling stretching. This function checks this.
*/
CanStretch = AllowStretching(window);
/*
* Set up the shape path to get scripts and textures from local
* sub-directories or from the directory from which the application
* was run.
*/
strcpy(buffer, AppPath);
strcat(buffer, "SCRIPTS");
RwSetShapePath(buffer, rwPRECONCAT);
strcpy(buffer, AppPath);
strcat(buffer, "TEXTURES");
RwSetShapePath(buffer, rwPRECONCAT);
RwSetShapePath(AppPath, rwPRECONCAT);
/*
* Install the application's optimized palette.
*/
strcpy(buffer, AppPath);
strcat(buffer, PALETTEFILENAME);
CheckAndReadPalette(buffer);
/*
* 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 maximum window size (specified by MAXIMUM_WINDOW_WIDTH and
* MAXIMUM_WINDOW_HEIGHT). Thus, the camera's viewport can grow as large
* as the window.
*/
Camera = RwCreateCamera(MAXIMUM_WINDOW_WIDTH, MAXIMUM_WINDOW_HEIGHT, NULL);
if (!Camera)
{
/*
* 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 RenderWare camera",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
else
{
MessageBox(window, "Could not create the RenderWare camera",
WINDOWTITLE, MB_OK | MB_ICONSTOP | MB_APPLMODAL);
}
RwClose();
return FALSE;
}
/*
* Set the camera's background color to blue.
*/
RwSetCameraBackColor(Camera, CREAL(0.0), CREAL(0.0), CREAL(0.67));
/*
* 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);
RwSetCameraViewwindow(Camera, CREAL(0.8), CREAL(0.8));
/*
* Load and set the backdrop backdrop (we will consider the lack of a
* backdrop to be non-fatal).
*/
backdrop = RwReadRaster(BACKDROPFILENAME, rwGAMMARASTER | rwDITHERRASTER);
if (backdrop != NULL)
RwSetCameraBackdrop(Camera, backdrop);
/*
* Attempt to load the fourth default rollercoaster by default (Puke
* Mountain). If we can't then we will just create a new rollercoaster
* instead.
*/
strcpy(buffer, AppPath);
strcat(buffer, DefaultCoasterFilenames[3]);
CurrentCoaster = ReadRollerCoaster(buffer);
if (CurrentCoaster == NULL)
{
/*
* Could not read the default coaster so create a new
* coaster instead.
*/
CurrentCoaster = CreateRollerCoaster();
}
/*
* If we can't read the default coaster or create a new
* one then we must give up and exit.
*/
if (CurrentCoaster == NULL)
{
RwDestroyCamera(Camera);
RwClose();
return FALSE;
}
SwitchToRideView(window);
SetWindowText(window, CurrentCoaster->description);
/*
* All the 3D components are now successfully initialized, so
* work can begin...
*/
ThreeDInitialized = 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 current coaster.
*/
if (CurrentCoaster)
DestroyRollerCoaster(CurrentCoaster);
/*
* 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 *)(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(CurrentCoaster->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.
*/
RwShowCameraImage(Camera, (void*)(DWORD)dc);
}
/**********************************************************************/
/*
* 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 width, int height)
{
HDC dc;
RwWinOutputSize winOutputSize;
/*
* Set the output size (using the RwDeviceControl()) to the
* size specified.
*/
winOutputSize.width = (RwInt32)width;
winOutputSize.height = (RwInt32)height;
winOutputSize.camera = Camera;
RwDeviceControl(rwWINSETOUTPUTSIZE, 0L, &winOutputSize, sizeof(RwWinOutputSize));
if (DoStretch)
{
/*
* Set the viewport (and backdrop) to half the width and height specified.
*/
RwSetCameraViewport(Camera, 0, 0, width / 2, height / 2);
}
else
{
RwSetCameraViewport(Camera, 0, 0, width, height);
}
TrackBackdropToCamera(Camera);
/*
* When the viewport has changed size we need to re-render the
* scene for the new viewport size.
*/
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
}
/**********************************************************************/
/*
* Handle menu selections.
*/
static void
OnMenu(HWND window, WPARAM item)
{
RollerCoasterType *coaster;
HDC dc;
OPENFILENAME openFileName;
char filename[_MAX_PATH];
RECT rect;
static FARPROC dialogProcInstance;
switch (item)
{
case IDM_FILE_NEW:
coaster = CreateRollerCoaster();
if (coaster != NULL)
{
SwitchCoaster(window, coaster);
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
}
else
{
MessageBox(window, "Error creating new rollercoaster", "RenderWare Roller",
MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
break;
case IDM_FILE_OPEN:
dialogProcInstance = MakeProcInstance(CoasterMenuDialogProc, AppInstance);
if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ROLLERCOASTER_MENU),
window, dialogProcInstance) == IDOK)
{
coaster = ReadRollerCoaster(CoasterFilename);
if (coaster != NULL)
{
SwitchCoaster(window, coaster);
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
}
}
FreeProcInstance(dialogProcInstance);
break;
case IDM_FILE_SAVE:
if (CurrentCoaster->filename[0] != '\0')
{
if (!WriteRollerCoaster(CurrentCoaster, CurrentCoaster->filename))
{
MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
}
else
{
filename[0] = '\0';
memset(&openFileName, '\0', sizeof(OPENFILENAME));
openFileName.lStructSize = sizeof(OPENFILENAME);
openFileName.hwndOwner = window;
openFileName.lpstrFilter = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
openFileName.lpstrFile = &filename[0];
openFileName.nMaxFile = sizeof(filename);
openFileName.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
if (GetSaveFileName(&openFileName))
{
if (!WriteRollerCoaster(CurrentCoaster, filename))
{
MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
}
}
break;
case IDM_FILE_SAVEAS:
filename[0] = '\0';
memset(&openFileName, '\0', sizeof(OPENFILENAME));
openFileName.lStructSize = sizeof(OPENFILENAME);
openFileName.hwndOwner = window;
openFileName.lpstrFilter = "Rollercoasters (*.rrc)\0*.rrc\0All files (*.*)\0*.*\0\0";
openFileName.lpstrFile = &filename[0];
openFileName.nMaxFile = sizeof(filename);
openFileName.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
if (GetSaveFileName(&openFileName))
{
if (!WriteRollerCoaster(CurrentCoaster, filename))
{
MessageBox(window, "Error writing rollercoaster...", "RenderWare Rollercoaster Error", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
}
}
break;
case IDM_FILE_EXIT:
DestroyWindow(window);
break;
case IDM_EDIT_DESCRIPTION:
dialogProcInstance = MakeProcInstance(CoasterDescriptionDialogProc, AppInstance);
if (DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ROLLERCOASTER_DESCRIPTION),
window, dialogProcInstance) == IDOK)
SetWindowText(window, CurrentCoaster->description);
FreeProcInstance(dialogProcInstance);
break;
case IDM_EDIT_SIDEVIEW:
SwitchToElevationView(window);
break;
case IDM_EDIT_TOPVIEW:
SwitchToPlanView(window);
break;
case IDM_VIEWPOINT_RIDE:
SwitchToRideView(window);
break;
case IDM_VIEWPOINT_SPOTTER:
SwitchToSpotterView(window);
break;
case IDM_VIEWPOINT_TRAIL:
SwitchToTrailView(window);
break;
case IDM_VIEWPOINT_LEAD:
SwitchToLeadView(window);
break;
case IDM_OPTION_STRETCH:
if (GetMenuState(GetMenu(window), IDM_OPTION_STRETCH,
MF_BYCOMMAND) == MF_CHECKED)
{
DoStretch = FALSE;
}
else
{
DoStretch = TRUE;
}
UpdateMenu(window);
GetClientRect(window, &rect);
OnSize(window, rect.right - rect.left, rect.bottom - rect.top);
break;
case IDM_OPTION_SHOWCAR:
if (GetMenuState(GetMenu(window), IDM_OPTION_SHOWCAR,
MF_BYCOMMAND) == MF_CHECKED)
{
ShowCar = FALSE;
DisableCoasterCar(CurrentCoaster);
}
else
{
ShowCar = TRUE;
EnableCoasterCar(CurrentCoaster);
}
UpdateMenu(window);
break;
case IDM_HELP_ABOUT:
dialogProcInstance = MakeProcInstance(CoasterAboutDialogProc, AppInstance);
DialogBox(AppInstance, MAKEINTRESOURCE(IDD_ABOUT),
window, dialogProcInstance);
FreeProcInstance(dialogProcInstance);
break;
}
}
/**********************************************************************/
/*
* Handle queries about the window's maximum extent.
*/
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 = MAXIMUM_WINDOW_WIDTH;
minmaxinfo->ptMaxSize.y = MAXIMUM_WINDOW_HEIGHT;
minmaxinfo->ptMaxTrackSize.x = MAXIMUM_WINDOW_WIDTH;
minmaxinfo->ptMaxTrackSize.y = MAXIMUM_WINDOW_HEIGHT;
}
/**********************************************************************/
/*
* 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, int x, int y, WPARAM vKeys)
{
switch (CameraMode)
{
case cmRIDE:
case cmSPOTTER:
case cmTRAIL:
case cmLEAD:
/*
* The left button does nothing when riding the
* coaster.
*/
MouseMoveMode = mmNONE;
break;
case cmELEVATION:
case cmPLAN:
/*
* If a control point is picked, move the control
* point, otherwise, move the camera.
*/
if (DoStretch)
ControlPoint = IsCoasterControlPointPicked(CurrentCoaster, Camera, x / 2, y / 2);
else
ControlPoint = IsCoasterControlPointPicked(CurrentCoaster, Camera, x, y);
if (ControlPoint == 0)
{
if (vKeys & MK_SHIFT)
MouseMoveMode = mmALTMOVECAMERA;
else
MouseMoveMode = mmMOVECAMERA;
}
else
{
MouseMoveMode = mmMOVECONTROL;
}
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 != mmNONE)
{
SetCapture(window);
LastX = x;
LastY = y;
KillTimer(window, 1);
}
}
/**********************************************************************/
/*
* 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, int x, int y, WPARAM vKeys)
{
MouseMoveMode = mmMOVELIGHT;
/*
* 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 != mmNONE)
{
SetCapture(window);
LastX = x;
LastY = 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, int x, int y)
{
HDC dc;
RwV3d position;
RwV3d newPos;
/*
* MouseMoveMode tells us what kind of action to perform.
*/
switch (MouseMoveMode)
{
case mmNONE:
break;
case mmMOVECAMERA:
switch (CameraMode)
{
case cmNONE:
case cmRIDE:
case cmSPOTTER:
case cmTRAIL:
case cmLEAD:
break;
case cmELEVATION:
RwPushScratchMatrix();
RwRotateMatrix(RwScratchMatrix(),
CREAL(0.0),
CREAL(1.0),
CREAL(0.0),
RDiv(INT2REAL(LastX - x), CREAL(5.0)),
rwREPLACE);
RwTransformCamera(Camera, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
RwVCMoveCamera(Camera,
CREAL(0.0),
CREAL(0.0),
RDiv(INT2REAL(LastY - y), CREAL(50.0)));
RwGetCameraPosition(Camera, &position);
if (position.x < CREAL(-1.5))
position.x = CREAL(-1.5);
if (position.x > CREAL( 1.5))
position.x = CREAL( 1.5);
if (position.z < CREAL(-1.5))
position.z = CREAL(-1.5);
if (position.z > CREAL( 1.5))
position.z = CREAL( 1.5);
RwSetCameraPosition(Camera, position.x, position.y, position.z);
TrackBackdropToCamera(Camera);
break;
case cmPLAN:
RwVCMoveCamera(Camera,
RDiv(INT2REAL(LastX - x), CREAL(50.0)),
RDiv(INT2REAL(y - LastY), CREAL(50.0)),
CREAL(0.0));
RwGetCameraPosition(Camera, &position);
if (position.x < CREAL(-1.0))
position.x = CREAL(-1.0);
if (position.x > CREAL( 1.0))
position.x = CREAL( 1.0);
if (position.z < CREAL(-1.0))
position.z = CREAL(-1.0);
if (position.z > CREAL( 1.0))
position.z = CREAL( 1.0);
RwSetCameraPosition(Camera, position.x, position.y, position.z);
break;
}
break;
case mmALTMOVECAMERA:
switch (CameraMode)
{
case cmNONE:
case cmRIDE:
case cmSPOTTER:
case cmTRAIL:
case cmLEAD:
break;
case cmELEVATION:
RwVCMoveCamera(Camera,
CREAL(0.0),
RDiv(INT2REAL(y - LastY), CREAL(50.0)),
CREAL(0.0));
RwGetCameraPosition(Camera, &position);
if (position.y < DEFAULTGROUNDLEVEL)
position.y = DEFAULTGROUNDLEVEL;
if (position.y > DEFAULTSKYLEVEL)
position.y = DEFAULTSKYLEVEL;
RwSetCameraPosition(Camera, position.x, position.y, position.z);
TrackBackdropToCamera(Camera);
break;
case cmPLAN:
RwVCMoveCamera(Camera,
CREAL(0.0),
CREAL(0.0),
RDiv(INT2REAL(y - LastY), CREAL(50.0)));
RwGetCameraPosition(Camera, &position);
if (position.y < DEFAULTGROUNDLEVEL)
position.y = DEFAULTGROUNDLEVEL;
if (position.y > DEFAULTSKYLEVEL)
position.y = DEFAULTSKYLEVEL;
RwSetCameraPosition(Camera, position.x, position.y, position.z);
break;
}
break;
case mmMOVECONTROL:
RwPushScratchMatrix();
/*
* This function (from pick.c) returns the new position for a clump which lies under
* the given viewport coordinates. This allows as to drag control points exactly
* under the mouse pointer.
*/
if (DoStretch)
GetClumpPositionUnderPointer(COASTERCONTROLPOINT(CurrentCoaster, ControlPoint),
Camera, x / 2, y / 2, &newPos);
else
GetClumpPositionUnderPointer(COASTERCONTROLPOINT(CurrentCoaster, ControlPoint),
Camera, x, y, &newPos);
RwTranslateMatrix(RwScratchMatrix(), newPos.x, newPos.y, newPos.z, rwREPLACE);
TransformCoasterControlPoint(CurrentCoaster, ControlPoint, RwScratchMatrix());
RwPopScratchMatrix();
break;
case mmMOVELIGHT:
/*
* 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. In this case we will ignore the effects
* of camera orientation changes.
*/
RwPushScratchMatrix();
/*
* Replace the CTM with a rotation matrix about Y. The number
* of degrees of rotation is given by the mouse X delta.
*/
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0), CREAL(1.0), CREAL(0.0),
INT2REAL(LastX - 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 - y), rwPOSTCONCAT);
/*
* Transform the light by the resultant rotations.
*/
RwTransformLight(CurrentCoaster->light, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
}
if (MouseMoveMode != mmNONE)
{
/*
* Re-render the scene and copy the results to the display.
*/
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
/*
* Remember the current X and Y for next time.
*/
LastX = x;
LastY = 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(HWND window)
{
HDC dc;
/*
* If we were engaged in a mouse move action and the button has come
* back up, then terminate the action and release mouse capture.
*/
switch (MouseMoveMode)
{
case mmNONE:
break;
case mmMOVECAMERA:
case mmALTMOVECAMERA:
MouseMoveMode = mmNONE;
ReleaseCapture();
switch (CameraMode)
{
case cmRIDE:
case cmSPOTTER:
case cmTRAIL:
case cmLEAD:
break;
case cmELEVATION:
RwGetCameraPosition(Camera, &CurrentCoaster->elevationPosition);
break;
case cmPLAN:
RwGetCameraPosition(Camera, &CurrentCoaster->planPosition);
break;
}
SetTimer(window, 1, 20, NULL);
break;
case mmMOVECONTROL:
MouseMoveMode = mmNONE;
ReleaseCapture();
UpdateCoasterClump(CurrentCoaster);
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
SetTimer(window, 1, 20, NULL);
break;
}
}
/**********************************************************************/
/*
* 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(HWND window)
{
/*
* 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 != mmNONE)
{
MouseMoveMode = mmNONE;
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];
HDC dc;
RwClump *clump;
/*
* 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++)
{
/*
* LoadClumpOrBackdrop() is called to attempt to load either a
* clump or texture from the file. If a texture is read then a
* sprite using that texture is created and returned.
*/
DragQueryFile(drop, i, path, _MAX_PATH);
clump = RwReadShape(path);
if (clump)
RwAddClumpToScene(CurrentCoaster->scene, clump);
}
DragFinish(drop);
/*
* As new objects have been loaded we must re-render the scene.
*/
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
}
/**********************************************************************/
/*
* 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 MS Window's timer expiry. This function will perform any
* animation actions necessary, including spinning clumps and animating
* textures.
*/
static void
OnTimer(HWND window)
{
HDC dc;
RwReal x;
RwReal y;
RwReal z;
if (MouseMoveMode == mmNONE)
{
switch (CameraMode)
{
case cmRIDE:
UpdateViewParameters(CurrentCoaster);
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RwSetCameraLookAt(Camera,
CurrentCoaster->tAt.x,
CurrentCoaster->tAt.y,
CurrentCoaster->tAt.z);
x = RAdd(CurrentCoaster->tPosition.x, RMul(CurrentCoaster->tUp.x, CREAL(0.1)));
y = RAdd(CurrentCoaster->tPosition.y, RMul(CurrentCoaster->tUp.y, CREAL(0.1)));
z = RAdd(CurrentCoaster->tPosition.z, RMul(CurrentCoaster->tUp.z, CREAL(0.1)));
RwSetCameraPosition(Camera, x, y, z);
TrackBackdropToCamera(Camera);
UpdateCoasterVelocity(CurrentCoaster);
break;
case cmSPOTTER:
UpdateViewParameters(CurrentCoaster);
RwSetCameraPosition(Camera,
CurrentCoaster->tPosition.x,
CurrentCoaster->tPosition.y,
CurrentCoaster->tPosition.z);
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RwSetCameraLookAt(Camera, CREAL(0.0), CREAL(0.0), CREAL(1.0));
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RwPanCamera(Camera, CameraAngle);
RwTiltCamera(Camera, CREAL(40.0));
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.20));
TrackBackdropToCamera(Camera);
UpdateCoasterVelocity(CurrentCoaster);
CameraAngle = RAdd(CameraAngle, CREAL(5.0));
if (CameraAngle > CREAL(360.0))
CameraAngle = RSub(CameraAngle, CREAL(360.0));
break;
case cmTRAIL:
UpdateViewParameters(CurrentCoaster);
RwSetCameraPosition(Camera,
CurrentCoaster->tPosition.x,
CurrentCoaster->tPosition.y,
CurrentCoaster->tPosition.z);
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RwSetCameraLookAt(Camera,
CurrentCoaster->tAt.x,
CurrentCoaster->tAt.y,
CurrentCoaster->tAt.z);
RwSetCameraLookUp(Camera,
CurrentCoaster->tUp.x,
CurrentCoaster->tUp.y,
CurrentCoaster->tUp.z);
RwTiltCamera(Camera, CREAL(45.0));
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.15));
TrackBackdropToCamera(Camera);
UpdateCoasterVelocity(CurrentCoaster);
break;
case cmLEAD:
UpdateViewParameters(CurrentCoaster);
RwSetCameraPosition(Camera,
CurrentCoaster->tPosition.x,
CurrentCoaster->tPosition.y,
CurrentCoaster->tPosition.z);
RwSetCameraLookUp(Camera, CREAL(0.0), CREAL(1.0), CREAL(0.0));
RwSetCameraLookAt(Camera,
CurrentCoaster->tAt.x,
CurrentCoaster->tAt.y,
CurrentCoaster->tAt.z);
RwSetCameraLookUp(Camera,
CurrentCoaster->tUp.x,
CurrentCoaster->tUp.y,
CurrentCoaster->tUp.z);
RwPanCamera(Camera, CREAL(180.0));
RwTiltCamera(Camera, CREAL(60.0));
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CREAL(-0.15));
TrackBackdropToCamera(Camera);
UpdateCoasterVelocity(CurrentCoaster);
break;
case cmELEVATION:
case cmPLAN:
UpdateViewParameters(CurrentCoaster);
UpdateCoasterVelocity(CurrentCoaster);
break;
}
}
/*
* Re-render the scene and copy the results to the display.
*/
dc = GetDC(window);
RenderScene(window, dc);
ReleaseDC(window, dc);
}
/**********************************************************************/
/*
* The window procedure for this application's window.
*/
LRESULT CALLBACK
MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
#if defined(WIN32)
POINTS point;
#else
POINT point;
#endif
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.
*/
SetTimer(window, 1, 20, NULL);
return 0L;
case WM_GETMINMAXINFO:
/*
* The MK_FP32() is necessary under Watcom to convert
* an MS Windows 16 bit far pointer to a 32 bit far pointer.
*/
#if defined(__WINDOWS_386__)
OnGetMinMaxInfo((MINMAXINFO FAR*)MK_FP32((void*)lParam));
#else
OnGetMinMaxInfo((MINMAXINFO FAR*)lParam);
#endif
return 0L;
case WM_SIZE:
if (ThreeDInitialized)
OnSize(window, LOWORD(lParam), HIWORD(lParam));
return 0L;
case WM_DROPFILES:
if (ThreeDInitialized)
OnDrop(window, (HDROP)wParam);
return 0L;
case WM_COMMAND:
if (LOWORD(lParam) == 0)
{
KillTimer(window, 1);
OnMenu(window, wParam);
SetTimer(window, 1, 20, NULL);
}
return 0L;
case WM_LBUTTONDOWN:
if (ThreeDInitialized)
{
#if defined(WIN32)
point = MAKEPOINTS(lParam);
#else
point = MAKEPOINT(lParam);
#endif
OnLButtonDown(window, point.x, point.y, wParam);
}
return 0L;
case WM_RBUTTONDOWN:
if (ThreeDInitialized)
{
#if defined(WIN32)
point = MAKEPOINTS(lParam);
#else
point = MAKEPOINT(lParam);
#endif
OnRButtonDown(window, point.x, point.y, wParam);
}
return 0L;
case WM_MOUSEMOVE:
if (ThreeDInitialized)
{
if (MouseMoveMode != mmNONE)
{
#if defined(WIN32)
point = MAKEPOINTS(lParam);
#else
point = MAKEPOINT(lParam);
#endif
OnMouseMove(window, point.x, point.y);
}
}
return 0L;
case WM_LBUTTONUP:
if (ThreeDInitialized)
{
OnLButtonUp(window);
}
return 0L;
case WM_RBUTTONUP:
if (ThreeDInitialized)
OnRButtonUp(window);
return 0L;
case WM_PAINT:
if (ThreeDInitialized)
OnPaint(window);
return 0L;
case WM_TIMER:
if (ThreeDInitialized)
OnTimer(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);
/*
* Quit message handling.
*/
PostQuitMessage(0);
return 0L;
}
/*
* Let Windows handle all other messages.
*/
return DefWindowProc(window, message, wParam, lParam);
}
/**********************************************************************/
/*
* Parse the command line arguments.
*/
static void
ParseCommandLineArguments(LPSTR cmdLine)
{
char *c;
char *option;
c = cmdLine;
while (*c != '\0')
{
option = strchr(c, '-');
if (option == NULL)
option = strchr(c, '/');
if (option == NULL)
return;
if (++option == '\0')
return;
switch (*option)
{
case 'g':
case 'G':
UseWinG = !UseWinG;
break;
}
c = (option + 1);
}
}
/**********************************************************************/
/*
* MS Windows application entry point.
*/
int PASCAL
WinMain(HANDLE instance, HANDLE prevInstance, LPSTR cmdLine, int cmdShow)
{
MSG message;
HWND window;
char buffer[_MAX_PATH];
int pathLen;
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
/*
* Remember 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 Rollercoaster is already running...",
WINDOWTITLE, MB_OK | MB_APPLMODAL | MB_ICONSTOP);
return FALSE;
}
/*
* Parse the command line arguments.
*/
ParseCommandLineArguments(cmdLine);
/*
* Register the window class.
*/
if (!InitApplication(instance))
{
return FALSE;
}
/*
* Create the window.
*/
window = InitInstance(instance);
if (window == NULL)
return FALSE;
/*
* Identify the directory from which the application is being
* run. We will use this for getting hold of scripts and
* textures, and also for loading coaster files. We ensure that
* the path identified ends in a backslash.
*/
GetModuleFileName(instance, buffer, sizeof(buffer));
_splitpath(buffer, drive, dir, NULL, NULL); /* Caution, PC specific function. */
strcpy(AppPath, drive);
strcat(AppPath, dir);
pathLen = strlen(AppPath);
if (pathLen > 0)
{
if (AppPath[pathLen - 1] != '\\')
{
AppPath[pathLen] = '\\';
AppPath[pathLen + 1] = '\0';
}
}
/*
* Initialize the 3D (RenderWare) components of the app.
*/
if (!Init3D(window))
{
DestroyWindow(window);
return FALSE;
}
/*
* Show the window, and refresh it.
*/
ShowWindow(window, cmdShow);
UpdateWindow(window);
/*
* Enter the message processing loop.
*/
while (GetMessage(&message, NULL, 0U, 0U))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
/*
* Tidy up the 3D (RenderWare) components of the application.
*/
TidyUp3D();
return message.wParam;
}
/**********************************************************************/