home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Learn 3D Graphics Programming on the PC
/
Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso
/
rwdos
/
dosview.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-15
|
42KB
|
1,640 lines
/**********************************************************************
*
* File : dosview.c
*
* Abstract : A very simple, sample RenderWare application for
* MS-Dos / PC-Dos. 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.
*
* 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. and Canon Inc. will not, under any
* circumstances, be liable for any lost revenue or other damages arising
* from the use of this file.
*
* Copyright (c) 1991, 1992, 1993. Canon Inc.
* All Rights Reserved.
*
**********************************************************************/
/****************************************************************************
Includes
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <i86.h>
#include <math.h> /* Required for floating point */
#include "rwlib.h"
#include "rwdos.h"
#include "doswrap.h"
/****************************************************************************
Types
*/
/**********************************************************************
*
* Application constants.
*
**********************************************************************/
#define DELETE 8
#define BOOL int
/*
* MS Windows compatible defines
*/
#define MK_CONTROL 0x4
#define MK_SHIFT 0x2
/*
* Depending which video mode is being used the colur used will change
*/
/*
* Default distance of the camera from the origin.
*/
#define DEFAULT_CAMERA_DISTANCE CREAL(-7.0)
/**********************************************************************
*
* Type definitions.
*
**********************************************************************/
/*
* This enumerated type tells us what kind of action should be taken
* on mouse events.
*/
typedef enum
{
MMNoAction,
MMPanAndZoomCamera,
MMTiltCamera,
MMSpinClump,
MMDragClump,
MMDragClumpInZ,
MMPanLight
} MMMode;
/*
* This enumerated type tells us what kind of animation action should
* be taken on timer messages. It is used to allow us to spin clumps
* for a "momentum" effect.
*/
typedef enum
{
ANoAction,
ASpinClump
} AMode;
/**********************************************************************
*
* Application global variables.
*
**********************************************************************/
/*
* String for clearing status line
*/
static char sGClear[]=" ";
/*
* Texts colour
*/
static int nGTextColour;
/*
* Global RenderWare object pointers. In this simple application we
* make use of only a single scene, camera and light.
*/
static RwScene *Scene = NULL;
static RwCamera *Camera = NULL;
static RwLight *Light = NULL;
/*
* This variable is used to remember which clump was last picked
* when spinning or dragging a clump on mouse move events.
*/
static RwClump *PickedClump = 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 what kind of action to take on a mouse move.
* The action depends on the object that was picked when the mouse button
* went down, and on the selection of virtual keys that were depressed at
* that time. By default no action is taken on mouse move.
*/
static MMMode MouseMoveMode = MMNoAction;
/*
* This variable tells us what kind of action to take on timer
* expiry. It is used to give clumps momentum, i.e. the clump
* will continue to spin after the user releases the mouse button.
* By default no animation action is taken.
*/
static AMode AnimMode = ANoAction;
/*
* Global variables used to remember the last mouse X and Y coordinates
* when involved in a pan, zoom, drag or spin.
*/
static int LastX;
static int LastY;
/*
* Current distance the camera is from the origin. This is stored to
* help us pan and zoom the camera.
*/
static RwReal CameraDistance = DEFAULT_CAMERA_DISTANCE;
/*
* Current angle of tilt applied to the camera.
*/
static RwReal CameraTilt = CREAL(0.0);
/*
* 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 ThreeDInitialized = FALSE;
/* The size of the screen
*/
static int nGScrWidth;
static int nGScrHeight;
/****************************************************************************
DosPrintString
On entry : xcord
: ycord
: string
: colour
On exit :
*/
void DosPrintString(int nX,int nY,char *sString,int nCol)
{
RwPrintChar pcPrint;
pcPrint.x = nX;
pcPrint.y = nY;
pcPrint.color = nCol;
for (;(*sString);sString++) {
pcPrint.c = (*sString);
RwDeviceControl(rwPRINTCHAR,0,&pcPrint,sizeof(RwPrintChar));
pcPrint.x+=8;
};
}
/****************************************************************************
DosTimer
On entry :
On exit : Timer (in milliseconds)
*/
int DosTimer(void)
{
union REGS r;
int nTime;
r.h.ah=0;
int386(0x1a,&r,&r);
nTime = ((r.w.cx)<<16)|(r.w.dx);
return (nTime*55);
}
/****************************************************************************
DosGetKey
Get the ascii key code of any depressed key. (Do not wait for a key press.
-> return 0 if no key is pressed)
On entry :
On exit : Key pressed in ascii (or 0 if no key pressed)
*/
int DosGetKey(void)
{
union REGPACK rp;
memset(&rp,0,sizeof(rp));
rp.h.ah = 0x06;
rp.h.dl = 0xff;
intr(0x21,&rp);
if (!(rp.w.flags & 0x40 )) { /* Check Z flag */
/* Got key */
if (rp.h.al) {
return ((int)rp.h.al);
};
memset(&rp,0,sizeof(rp));
rp.h.ah = 0x06;
rp.h.dl = 0xff;
intr(0x21,&rp);
if (!(rp.w.flags & 0x40)) {
return ((int)rp.h.al);
};
return (rp.h.al|0x80);
};
return 0;
}
/****************************************************************************
DosShiftCtrl
Finds the status of the 'Shift/Control' type keys.
On entry :
On exit : Bit Meaning
0 Right Shift
1 Left Shift
2 Ctrl
3 Alt
4 Scroll Lock
5 Num Lock
6 Caps Lock
7 Insert on
*/
int DosShiftCtrl(void)
{
union REGPACK rp;
memset(&rp,0,sizeof(rp));
rp.h.ah=0x02;
intr(0x16,&rp);
return ((int)rp.h.al);
}
/**********************************************************************/
/*
* If using the status bars then show the polgon & vertex count
of the picked clump in the right status bar */
ShowPickedClumpData(void)
{
char string[]=" ";
if (PickedClump)
{
sprintf (string, "%d polygons, %d vertices",
RwGetClumpNumPolygons(PickedClump),
RwGetClumpNumVertices(PickedClump));
DosPrintString(0,nGScrHeight-8,string,nGTextColour);
}
else
{
DosPrintString(0,nGScrHeight-8,string,0);
};
}
/**********************************************************************/
/*
* 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. A user-draw may
* also be created if USERDRAW_LABELS is defined.
*/
static BOOL
Init3D(char *sFilename)
{
char windowText[128];
char version[30];
char buffer[128];
int param;
int i;
RwReal naWhite[]={CREAL(1.0),CREAL(1.0),CREAL(1.0)};
long nError;
/*
* Attempt to open (and initialize) the RenderWare library.
*/
if (!RwOpen("DOSMOUSE", &nError))
{
printf("Unable to access renderware!!\n");
switch (nError) {
case E_RW_DOS_MODE_UNAVAILABLE: {
printf("The installed VESA card is unable to switch to the resolution");
printf(" requested.\n");
printf("Either install a different video adapter or use a ");
printf("supported video mode.");
break;
};
case E_RW_DOS_NO_VESA_BIOS: {
printf("A VESA bios is unavailable on this machine.\n");
printf("Either use a VESA compatible Video Adapter or install a ");
printf("VESA bios emulation TSR.\n");
break;
};
case E_RW_DOS_INCOMPATIBLE_BIOS: {
printf("The VESA bios on this machine is not of high enough version ");
printf("to function\ncorrectly with RenderWare. Use a version 1.0 or");
printf(" higher VESA bios or TSR.\n");
break;
};
case E_RW_DOS_NO_MOUSE: {
printf("No Microsoft compatible mouse driver present.\n");
printf("Install a microsoft compatible mouse driver and try again.\n");
break;
};
default: {
printf("Unknown Error !!!!!!!!!!!!!!!\n");
break;
};
};
return FALSE;
}
/* Set up character set */
RwGetDeviceInfo(rwSCRHEIGHT,&nGScrHeight,sizeof(int));
RwGetDeviceInfo(rwSCRWIDTH,&nGScrWidth,sizeof(int));
nGTextColour = RwDeviceControl(rwSCRGETCOLOR,0,naWhite,sizeof(RwReal[3]));
/*--- Only look for scripts and textures in subdirectories under the current
one. RWSHAPEPATH need not be set then */
RwSetShapePath(".",rwPRECONCAT);
strcpy(buffer,sFilename);
i = strlen(buffer);
while((buffer[i] != '\\')&&(i>=0)) {
i--;
};
if (i>=0) {
buffer[i+1] = 0;
strcat(buffer, "TEXTURES");
RwSetShapePath(buffer, rwPOSTCONCAT);
buffer[i+1] = 0;
strcat(buffer, "SCRIPTS");
RwSetShapePath(buffer, rwPOSTCONCAT);
};
RwSetShapePath("SCRIPTS", rwPRECONCAT);
RwSetShapePath("TEXTURES", rwPRECONCAT);
/*
* Label the display with information about the version of
* RenderWare being used. Its rather unlikely that
* RwGetSystemInfo() will fail so we ignore its return value.
*/
RwGetSystemInfo(rwVERSIONSTRING, &version,sizeof(version));
RwGetSystemInfo(rwFIXEDPOINTLIB, ¶m,sizeof(param));
sprintf(windowText, "RenderWare(tm) V%s %s",
version, (param ? "Fixed" : "Float"));
DosPrintString(0,nGScrHeight-16,windowText,nGTextColour);
/*
* Create the camera which will be used for rendering.
*/
Camera = RwCreateCamera(nGScrWidth,nGScrHeight-24, 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)
{
RwClose();
printf("Insufficient memory to create the RenderWare(tm) camera\n");
}
else
{
RwClose();
printf("Error creating the RenderWare(tm) camera\n");
}
exit(-1);
}
/* RwSetCameraProjection(Camera,rwPARALLEL); */
RwSetCameraViewport(Camera, 0, 0, nGScrWidth, (nGScrHeight-24));
/*
* Set the camera's background color to blue.
*/
RwSetCameraBackColor(Camera, CREAL(0.3), CREAL(0.0), CREAL(0.0));
/*
* 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);
/*
* Another change from previous versions of RenderWare is the amount of
* prespective generated by the default viewwindow size. When converting
* applications from previous versions of RenderWare the simple rule is
* to divide the viewwindow size by five to get the same prespective effect
* as given under previous versions.
*/
if (nGScrWidth >= nGScrHeight) {
RwSetCameraViewwindow(Camera,
CREAL(1.0),
RMul(CREAL(1.0),
RDiv(INT2REAL(nGScrHeight),
INT2REAL(nGScrWidth))));
} else {
RwSetCameraViewwindow(Camera,
RMul(CREAL(1.0),
RDiv(INT2REAL(nGScrWidth),
INT2REAL(nGScrHeight))),
CREAL(1.0));
};
/*
* Create a scene which will contain the clumps to be rendered and the
* light or lights illuminating those clumps . In this very simple
* application it would be perfectly acceptable to use the default scene
* (as returned by RwDefaultScene()) for rendering. However, it is good
* practice to always create a scene which will be used for your rendering
* and only use the default scene as a bag for currently unused clumps and
* lights.
*/
Scene = RwCreateScene();
if (!Scene)
{
RwDestroyCamera(Camera);
RwClose();
printf("Error creating the RenderWare(tm) scene\n");
exit(-1);
}
/*
* Our scene will be illuminated by a directional light. The illumination
* vector of the light is (-1.0, -1.0, -1.0) and its brightness will be 1.0.
*/
Light = RwCreateLight(rwDIRECTIONAL, CREAL(-1.0), CREAL(-1.0), CREAL(-1.0),
CREAL(1.0));
if (!Light)
{
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
printf("Error creating the RenderWare(tm) light\n");
exit(-1);
}
/*
* Add the new light to our scene.
*/
RwAddLightToScene(Scene, Light);
/*
* Create the spin matrix.
*/
SpinMatrix = RwCreateMatrix();
if (!SpinMatrix)
{
RwDestroyScene(Scene);
RwDestroyCamera(Camera);
RwClose();
printf("Error creating the RenderWare(tm) matrix\n");
exit(-1);
}
/*
* 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()
{
/*
* Destroy the spin matrix.
*/
RwDestroyMatrix(SpinMatrix);
/*
* Destroy the scene. This will destroy the contents of the scene,
* i.e. any clumps and lights in that scene. In this case destroying
* the scene will destroy the light we created in Init3D, and any
* clumps we have loaded and not already destroyed.
*/
RwDestroyScene(Scene);
/*
* Destroy the camera.
*/
RwDestroyCamera(Camera);
/*
* Close the library. This will free up any internal resources and
* textures loaded.
*/
RwClose();
}
/**********************************************************************/
/*
* Attempt to load the file with the given file name as either a clump
* or a texture (in which case a decal clump is created and returned).
*/
static RwClump *
LoadClumpOrTexture(char *fileName)
{
RwClump *clump;
RwTexture *texture;
char buffer[128];
RwErrorCode eCode;
char buffer2[128];
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,"Loading File...",nGTextColour);
/*
* Attempt to load the file as a script file.
*/
clump = RwReadShape(fileName);
if (!clump)
{
/*
* The load failed. This could be because of errors in the
* script file, insufficient memory, or the file containing
* garbage. We will now examine the error code returned to
* decide whether there was a script file error (in which case
* an error will be issued to the user), whether memory was
* exhausted (a different error is issued to the user), or
* the file contained garbage (in which case we will attempt
* to load the file as a texture instead of a script).
*/
eCode = RwGetError();
switch (eCode)
{
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer, "Insufficient memory : %s",fileName);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
case E_RW_NOFILE:
case E_RW_BADOPEN:
case E_RW_RSPARSE:
case E_RW_RSREAD:
case E_RW_READ:
/*
* We will (somewhat optimistically) assume that if we
* had a read or parse error on the stream then the
* file is not a script file but a texture instead.
* So attempt to load a texture. Note, as we use
* RwGetNamedTexture() to get the texture, the texture
* will not be reloaded if it has been previously loaded
* into RenderWare.
*/
texture = RwGetNamedTexture(fileName);
if (texture)
{
/*
* The file was indeed a texture, so attempt to
* create a decal clump using this texture.
*/
clump = RwCreateSprite(texture);
if (!clump)
{
RwGetTextureName(texture,buffer2,sizeof(buffer2));
sprintf(buffer,
"Error texture->decal clump %s",buffer2);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
}
}
else
{
/*
* The texture load failed, issue an error message
* giving the general nature of the problem.
*/
switch (RwGetError())
{
case E_RW_NOFILE:
case E_RW_BADOPEN:
/*
* Could not open the file...
*/
sprintf(buffer, "Error opening %s", fileName);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
case E_RW_NOMEM:
/*
* Ran out of memory...
*/
sprintf(buffer,
"No mem for texture %s",
fileName);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
default:
/*
* We will not enumerate all the errors, so simply
* issue a general failure report.
*/
sprintf(buffer,
"Error reading file %s",
fileName);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
}
}
break;
default:
/*
* On any other error we will assume there is an error in
* the script file.
*/
sprintf(buffer,
"Error in file %s <%i>",fileName,(int)eCode);
buffer[40]='\0';
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-24,buffer,nGTextColour);
return NULL;
}
}
/*
* Return the resulting clump.
*/
DosPrintString(0,nGScrHeight-24,sGClear,nGTextColour);
return clump;
}
/**********************************************************************/
/*
* Parse the names of any .rwx files on the command line, read those
* files and add them to the scene. This allows DosView to be associated
* with .rwx files in the file manager and be started automatically when
* a .rwx file is double-clicked.
*/
static BOOL
ReadFromCommandLine(int argc, char *argv[])
{
int nCount;
RwClump *clump;
for (nCount=1;nCount<argc;nCount++) {
clump = LoadClumpOrTexture(argv[nCount]);
if (clump) {
RwAddClumpToScene(Scene, clump);
};
};
return 1;
}
/**********************************************************************/
/*
* 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
HandleLeftButtonDown(int x, int y, int vKeys)
{
RwPickRecord pick;
int nPos;
char sBuffer[40];
int nKey;
RwClump *clump;
/* Check the area where the click took place */
if (y>(nGScrHeight-16)) {
/* Its off the bottom of the screen -> enter filename */
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Filename : @",nGTextColour);
nPos = 0;
sBuffer[0]='\0';
do {
nKey = DosGetKey();
if ((nPos>0)&&(nKey==8)) {
RwDPrintChar((nPos+11)<<3,nGScrHeight-8,'@',nGTextColour);
RwDPrintChar((nPos+12)<<3,nGScrHeight-8,' ',nGTextColour);
nPos--;
sBuffer[nPos]='\0';
};
if ((nPos<25)&&(nKey>32)&&(nKey<127)) {
sBuffer[nPos]=nKey;
nPos++;
sBuffer[nPos]='\0';
RwDPrintChar((nPos+11)<<3,nGScrHeight-8,nKey,nGTextColour);
RwDPrintChar((nPos+1+11)<<3,nGScrHeight-8,'@',nGTextColour);
};
} while (nKey!=13);
if (nPos>0) {
clump = LoadClumpOrTexture(sBuffer);
if (clump) {
RwAddClumpToScene(Scene, clump);
};
};
return;
};
/*
* If the left button is depressed anywhere in the client area of the
* window then the animated spin is cancelled.
*/
AnimMode = ANoAction;
/*
* The mouse move action is based on whether an object is picked or
* not. Therefore, attempt to pick an object in the scene.
*/
if (RwPickScene(Scene, x, y, Camera, &pick)) {
switch (pick.type) {
case rwNAPICKOBJECT:
/*
* Clicked on the background, so there is no drag action or
* picked clump.
*/
MouseMoveMode = MMNoAction;
PickedClump = NULL;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Stop Clump Rotation",nGTextColour);
break;
case rwPICKCLUMP:
/*
* If a clump was picked and both the shift and control
* virtual keys were depressed then we will destroy the
* clump (and hence remove it from the scene).
*/
if ((vKeys & (MK_CONTROL | MK_SHIFT)) ==
(MK_CONTROL | MK_SHIFT)) {
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Destroy Clump",nGTextColour);
/*
* Destroy the clump (and remove it from the scene).
*/
RwDestroyClump(pick.object.clump.clump);
/*
* The clump has been destroyed so there is no mouse
* move action and no picked clump.
*/
MouseMoveMode = MMNoAction;
PickedClump = NULL;
}
else
{
/*
* A clump was picked, so remember which clump was picked
* in order that it may be manipulated later when the
* mouse moves.
*/
PickedClump = pick.object.clump.clump;
/*
* Determine the exact nature of the manipulation of
* the clump on mouse move by examining the virtual key
* state. If the shift key was depressed, then drag
* the clump, otherwise, spin it.
*/
if (vKeys & MK_SHIFT)
{
MouseMoveMode = MMDragClump;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Drag Clump",nGTextColour);
}
else if (vKeys & MK_CONTROL)
{
MouseMoveMode = MMDragClumpInZ;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Drag Clump in Z",nGTextColour);
}
else
{
MouseMoveMode = MMSpinClump;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Rotate Clump",nGTextColour);
}
}
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)
{
LastX = x;
LastY = y;
}
RwDPointerRemove();
ShowPickedClumpData();
}
/**********************************************************************/
/*
* 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
HandleRightButtonDown(int x, int y, int vKeys)
{
if (vKeys & MK_CONTROL)
{
MouseMoveMode = MMPanLight;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Pan Light",nGTextColour);
}
else if (vKeys & MK_SHIFT)
{
MouseMoveMode = MMTiltCamera;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Tilt Camera",nGTextColour);
}
else
{
MouseMoveMode = MMPanAndZoomCamera;
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
DosPrintString(0,nGScrHeight-8,"Pan and Zoom Camera",nGTextColour);
}
/*
* 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)
{
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
HandleMouseMove(int x, int y)
{
RwClump *parent;
RwMatrix4d *tmpMatrix;
RwMatrix4d *worldToLocal;
RwV3d up;
RwV3d right;
RwV3d at;
RwReal xDelta;
RwReal yDelta;
RwReal xAngle;
RwReal yAngle;
/*
* MouseMoveMode tells us what kind of action to perform.
*/
switch (MouseMoveMode) {
case MMNoAction:
break;
case MMPanAndZoomCamera:
/*
* We are panning and zooming the camera. Movement of the
* mouse in the X direction will pan the camera about the
* origin of world coordinate space, and movement of the mouse
* in the Y direction will zoom the camera into and out of the
* the scene.
*/
/*
* Move the camera back to the origin, as we wish to pan about
* the origin of the world and not about the origin of the
* camera.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
/*
* Undo the tilt as we wish to pan about the world Y axis.
*/
RwTiltCamera(Camera, -CameraTilt);
/*
* Pan the camera by mouse X delta degrees.
*/
RwPanCamera(Camera, INT2REAL(LastX - x));
/*
* Zoom the camera by changing the distance the camera
* is from the origin of the world by mouse Y delta divided
* by 10 units.
*/
CameraDistance = RAdd(CameraDistance,
RDiv(INT2REAL(LastY - y), CREAL(10.0)));
/*
* Redo the tilt.
*/
RwTiltCamera(Camera, CameraTilt);
/*
* Move the camera out to its new distance from the world's
* origin.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
break;
case MMTiltCamera:
/*
* Move the camera back to the origin, as we wish to tilt about
* the origin of the world and not about the origin of the
* camera.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), -CameraDistance);
/*
* Undo the existing tilt.
*/
RwTiltCamera(Camera, -CameraTilt);
/*
* Compute the new angle of tilt.
*/
CameraTilt = RAdd(CameraTilt, INT2REAL(LastY - y));
/*
* Apply the new tilt.
*/
RwTiltCamera(Camera, CameraTilt);
/*
* Move the camera back out.
*/
RwVCMoveCamera(Camera, CREAL(0.0), CREAL(0.0), CameraDistance);
break;
case MMSpinClump:
/*
* Compute the angles of spin (simply derived from the mouse move deltas).
*/
yAngle = INT2REAL(x - LastX);
xAngle = INT2REAL(y - LastY);
/*
* In DosView, 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.
*/
if ((parent = RwGetClumpParent(PickedClump))) {
/*
* Get a handle to a couple of temporary matrices.
*/
tmpMatrix = RwPushScratchMatrix();
worldToLocal = RwPushScratchMatrix();
/*
* Get the parent clump's LTM (which maps local coordinates to
* world space).
*/
RwGetClumpLTM(parent, tmpMatrix);
/*
* Invert it so that it maps world coordinates to the parent's
* local coordinate space.
*/
RwInvertMatrix(tmpMatrix, worldToLocal);
/*
* And transform the camera's vectors into local space.
*/
RwTransformVector(&up, worldToLocal);
RwNormalize(&up);
RwTransformVector(&right, worldToLocal);
RwNormalize(&right);
/*
* Discard the temporary matrices.
*/
RwPopScratchMatrix();
RwPopScratchMatrix();
}
/*
* Apply the rotations.
*/
RwRotateMatrix(SpinMatrix, up.x, up.y, up.z, yAngle, rwREPLACE);
RwRotateMatrix(SpinMatrix, right.x, right.y, right.z, xAngle, rwPOSTCONCAT);
/*
* Apply the resulting, composite transformation to the clump.
*/
RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
/*
* As the mouse has moved enable the clump spin.
*/
AnimMode = ASpinClump;
break;
case MMDragClump:
RwPushScratchMatrix();
/*
* Compute the amount to translate the object by. This is simply
* derived from the mouse deltas scaled by some arbitrary quantity
* to prevent objects moving too "quickly".
*/
xDelta = RDiv(INT2REAL(x - LastX), CREAL(50.0));
yDelta = RDiv(INT2REAL(LastY - 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 up and look right vectors
* (scaled appropriately) rather than the clump's local axes.
*/
RwGetCameraLookRight(Camera, &right);
RwGetCameraLookUp(Camera, &up);
/*
* See the previous case for a description of why the following is
* necessary.
*/
if ((parent = RwGetClumpParent(PickedClump)))
{
tmpMatrix = RwPushScratchMatrix();
worldToLocal = RwPushScratchMatrix();
RwGetClumpLTM(parent, tmpMatrix);
RwInvertMatrix(tmpMatrix, worldToLocal);
RwTransformVector(&up, worldToLocal);
RwNormalize(&up);
RwTransformVector(&right, worldToLocal);
RwNormalize(&right);
RwPopScratchMatrix();
RwPopScratchMatrix();
}
/*
* Perform the translations.
*/
RwTranslateMatrix(RwScratchMatrix(),
RMul(right.x, xDelta), RMul(right.y, xDelta),
RMul(right.z, xDelta), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(),
RMul(up.x, yDelta), RMul(up.y, yDelta),
RMul(up.z, yDelta), rwPOSTCONCAT);
/*
* Apply the resulting, composite transform to the clump.
*/
RwTransformClump(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
case MMDragClumpInZ:
RwPushScratchMatrix();
/*
* Compute the amount to translate the object by. This is simply
* derived from the mouse deltas scaled by some arbitrary quantity
* to prevent objects moving too "quickly".
*/
yDelta = RDiv(INT2REAL(LastY - 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.
*/
if ((parent = RwGetClumpParent(PickedClump)))
{
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(PickedClump, RwScratchMatrix(), rwPOSTCONCAT);
RwPopScratchMatrix();
break;
case MMPanLight:
/*
* We are panning the light about the origin. We will rotate
* the light about the Y axis for movements of the mouse in
* X and rotate the light about the X axis for movements of
* the mouse in Y. 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(Light, RwScratchMatrix(),rwPOSTCONCAT);
RwPopScratchMatrix();
break;
}
/*
* 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
HandleLeftButtonUp(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;
}
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
}
/**********************************************************************/
/*
* Handle the right mouse button comming back up. The basic action is
* to turn of mouse move actions and release mouse capture.
*/
static void
HandleRightButtonUp(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;
}
RwDPointerRemove();
DosPrintString(0,nGScrHeight-8,sGClear,nGTextColour);
}
/**********************************************************************/
/*
* Handle MS Window's timer expiry. This function will perform any
* animation actions necessary, including spinning clumps and animating
* textures.
*/
static void
HandleTimer(void) {
/*
* Determine if there is a clump to spin.
*/
if (PickedClump && (AnimMode != ANoAction) && (MouseMoveMode == MMNoAction))
{
switch (AnimMode) {
case ASpinClump:
/*
* Spin the last clump picked by the last computed spin matrix.
*/
RwTransformClumpJoint(PickedClump, SpinMatrix, rwPOSTCONCAT);
FrameNumber++;
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 DosView 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(PickedClump, RwScratchMatrix());
RwOrthoNormalizeMatrix(RwScratchMatrix(), RwScratchMatrix());
RwTransformClumpJoint(PickedClump, RwScratchMatrix(), rwREPLACE);
RwPopScratchMatrix();
}
break;
}
}
/*
* Animate textures. 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.
*/
if (!(FrameNumber&15)) {
RwForAllNamedTextures(RwTextureNextFrame);
};
/*
* See the description of HandlePaint() for a description of this common
* RenderWare cliche for rendering a scene and copying it to the display.
*/
RwBeginCameraUpdate(Camera,NULL);
RwClearCameraViewport(Camera);
RwRenderScene(Scene);
RwEndCameraUpdate(Camera);
RwShowCameraImage(Camera, NULL);
}
/****************************************************************************
Main
*/
void main(int nArgc,char *saArgv[])
{
int nKey;
int nMouseX,nMouseY,nMouseBut,nOldMouseBut,nOldMouseX,nOldMouseY;
int nDX,nDY;
int nChange;
int nCtrlShift;
if (!Init3D(saArgv[0]))
{
exit(-1);
};
/*
* Parse any command line parameters.
*/
if (!ReadFromCommandLine(nArgc, saArgv))
{
TidyUp3D();
exit(-1);
};
RwDPointerSetPosition(nGScrWidth>>1,nGScrHeight>>1);
RwDPointerDisplay(&nOldMouseX,&nOldMouseY,&nOldMouseBut);
/* Create pointer */
nKey = DosGetKey();
while (nKey!=27) { /* ESC quits */
RwDPointerDisplay(&nMouseX,&nMouseY,&nMouseBut);
nKey = DosGetKey();
nCtrlShift = DosShiftCtrl();
nDX =(nMouseX-nOldMouseX);
nDY =(nMouseY-nOldMouseY);
nChange = (nMouseBut&(2+8)) | ( (nOldMouseBut&(2+8)) >>1 );
switch (nChange) {
case 0+0:
case 2+1:
case 8+4:
case 8+2+4+1: {
/* No change */
break;
};
case 2:
case 8+2+4: {
/* Left Button Down */
HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
break;
};
case 8:
case 8+2+1: {
/* Right Button Down */
HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
break;
};
case 8+1: {
/* Right down left Up */
HandleLeftButtonUp();
HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
break;
};
case 2+4: {
/* Right up left Down */
HandleRightButtonUp();
HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
break;
};
case 8+2: {
/* Left down RIght Down */
HandleRightButtonDown(nMouseX,nMouseY,nCtrlShift);
HandleLeftButtonDown(nMouseX,nMouseY,nCtrlShift);
break;
};
case 1+4: {
/* Left up Right Up */
HandleRightButtonUp();
HandleLeftButtonUp();
break;
};
case 1:
case 8+4+1: {
/* Left up */
HandleLeftButtonUp();
break;
};
case 4:
case 2+4+1: {
/* Right up */
HandleRightButtonUp();
break;
};
};
if (nDX||nDY) {
/* Mouse Move */
HandleMouseMove(nMouseX,nMouseY);
};
HandleTimer();
nOldMouseX = nMouseX;
nOldMouseY = nMouseY;
nOldMouseBut = nMouseBut;
};
/*
* Tidy up the 3D (RenderWare) components of the application.
*/
TidyUp3D();
exit(0);
}