home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Learn 3D Graphics Programming on the PC
/
Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso
/
rwdos
/
dosmaze.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-15
|
57KB
|
1,902 lines
/**********************************************************************
*
* File : dosmaze.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.
*
**********************************************************************/
/**********************************************************************
*
* Header files.
*
**********************************************************************/
#include <i86.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h> /* Required for floating point */
#include "rwlib.h"
#include "rwtypes.h"
#include "rwdos.h"
#include "doswrap.h"
#include "palette.h"
/**********************************************************************
*
* Application constants.
*
**********************************************************************/
/* Key code for delete key */
#define DELETE 8
#define BOOL int
/*
* MS Windows compatible defines
*/
#define MK_CONTROL 0x4
#define MK_SHIFT 0x2
/**********************************************************************
*
* Type definitions.
*
**********************************************************************/
/*
* This enumerated type tells us what kind of action should be taken
* on mouse events.
*/
typedef enum
{
MMNoAction,
MMPanAndZoomCamera,
MMTiltCamera,
MMPanLight
} MMMode;
/**********************************************************************
*
* Application global variables.
*
**********************************************************************/
/*
Screen size
*/
static int nGScrWidth;
static int nGScrHeight;
/*
Colour Codes
*/
static int nGTextCol;
static int nGMapCol;
static int nGCrossCol;
/*
* The Menu option state
*/
static int DrawHUD;
/*
* 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 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 RwReal forward,pan;
/*
* 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;
/* These are stored in the ClumpData field and specify what animation
* to perform at each tick.
*/
#define DONOTHING 0
#define SPINFADE 1
#define SPIN 2
#define TUMBLE 3
/*
* Global variables used to remember the last mouse X and Y coordinates
* when involved in a pan, zoom, drag or spin.
*/
static int LastX;
static int LastY;
/*
* This 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;
#define DUNWIDTH 34
#define DUNHEIGHT 29
char map[DUNHEIGHT][DUNWIDTH] =
{
{".................................."},
{"...................1@4*..........."},
{"...................*..*..........."},
{"...................3..2..........."},
{"..............a*****..**d***......"},
{"..............****......****......"},
{"..............****......****......"},
{"..............***C......****......"},
{"..............3............3......"},
{".........e*****.......a*****......"},
{".........****.........****........"},
{".........****.........****........"},
{".........***B.........***A........"},
{".........3............3..........."},
{"......1***.........1***..........."},
{"......*............*.............."},
{"......b***.........b***..........."},
{"......****.........****..........."},
{"......****.........****..........."},
{"......****4*.......***B4*........."},
{"...........*............*........."},
{"...........2.........c***........."},
{"...........**d***....****........."},
{".............****....****........."},
{".............****..1*****........."},
{".............****..*.............."},
{"................2..3.............."},
{"................****.............."},
{".................................."}
};
#define CLOSEST CREAL(0.1)
#define NORTH 1
#define EAST 2
#define SOUTH 4
#define WEST 8
typedef struct _dcell
{
int tag;
RwClump *geometry,*hgeometry,*vgeometry;
RwMatrix4d *mat;
struct _dcell *horizontal,*vertical;
int walls;
RwScene *contents;
int dobacking;
} Dcell;
Dcell dungeon[DUNHEIGHT][DUNWIDTH];
/**********************************************************************
*
* Functions.
*
**********************************************************************/
/****************************************************************************
DosDrawLine
Draws a line from (xcord,ycord) to (xcord2,ycord2) on a cameras image raster.
On entry : Camera to write to
: xcord1
: ycord1
: xcord2
: ycord2
: colour
*/
void DosDrawLine(RwCamera *pCamera,
int xcord,
int ycord,
int xcord2,
int ycord2,
unsigned int colour)
{
RwReal x1,y1,x2,y2;
RwReal dx, dy, tmp;
int count;
int p, q;
unsigned char *cpScreen;
RwReal vpr,vpb;
RwRaster *rpRaster;
int nWidth,nHeight,nDepth,nStride;
rpRaster = (RwRaster *) RwGetCameraImage(pCamera);
nWidth = RwGetRasterWidth(rpRaster);
nHeight = RwGetRasterHeight(rpRaster);
if ((xcord<0)||(xcord>=nWidth) ||
(ycord<0)||(ycord>=nHeight)) {
return;
};
nDepth = RwGetRasterDepth(rpRaster);
nStride = RwGetRasterStride(rpRaster);
vpr = INT2REAL(nWidth);
vpb = INT2REAL(nHeight);
x1 = INT2REAL(xcord);
y1 = INT2REAL(ycord);
x2 = INT2REAL(xcord2);
y2 = INT2REAL(ycord2);
if (x2 < x1)
{
tmp = x1;
x1 = x2;
x2 = tmp;
tmp = y1;
y1 = y2;
y2 = tmp;
}
if (x2 == vpr)
{
if (x1 == vpr){
return;
};
dx = RSub(x2,x1);
dy = RSub(y2,y1);
dy = (dy < 0)? -dy : dy;
if (dy > dx) {
x2 = RSub(x2,CREAL( 1));
};
}
if ((y1 == vpb)||(y2 == vpb))
{
if (y1==y2) {
return;
};
if (y1 == vpb) {
y1 = RSub(y1,CREAL(1));
} else {
dx = RSub(x2,x1);
dy = RSub(y2,y1);
if (dy < dx) {
y2 = RSub(y2,CREAL(1));
};
}
}
dx = RSub(x2,x1);
dy = RSub(y2,y1);
p = REAL2INT(dx);
q = REAL2INT(dy);
if (q < 0) {
q = -q;
};
count = p;
if (count < q) {
count = q;
};
/* We can get x's and y's that fall on the first pixel outside the image
buffer. So we do some shortening here */
if (count)
{
dx = RDiv(dx,INT2REAL(count));
dy = RDiv(dy,INT2REAL(count));
};
count++;
/* Draw the line at the correct depth */
cpScreen = RwGetRasterPixels(rpRaster);
if (nDepth==8) {
while (count--)
{
cpScreen[nStride*(REAL2INT(y1))+(REAL2INT(x1))] = colour;
x1 = RAdd(x1,dx);
y1 = RAdd(y1,dy);
}
} else {
/* Assume 16 bit */
while (count--)
{
cpScreen[nStride*(REAL2INT(y1))+((REAL2INT(x1))<<1)] = colour;
cpScreen[nStride*(REAL2INT(y1))+((REAL2INT(x1))<<1) +1] = (colour>>8);
x1 = RAdd(x1,dx);
y1 = RAdd(y1,dy);
}
};
}
/****************************************************************************
DosPrintString
Print a string using the DOS character printing DeviceControl.
On entry : xcord
: ycord
: string
: colour
On exit :
*/
void DosPrintString(int nX,int nY,char *sString,int nCol)
{
for (;(*sString);sString++) {
RwDPrintChar(nX,nY,(*sString),nCol);
nX+=8;
};
}
/****************************************************************************
DosTimer
Uses the DOS 18 per sec timer to find time in millisecs.
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 key pressed (ascii), 0 if no key 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
Find the status of the Shift,Ctrl etc 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);
}
/**********************************************************************/
/*
* 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)
{
RwClump *Room,*RoomSky,*Turn,*TurnF,*TurnB,*clump;
RwMatrix4d *mat;
int x,y,dx,dy,i;
char windowText[128];
char version[30];
char buffer[128];
int param;
RwReal naTextCol[]={CREAL(1.0),CREAL(1.0),CREAL(1.0)};
RwReal naMapCol[]={CREAL(0.0),CREAL(1.0),CREAL(0.0)};
RwReal naCrossCol[]={CREAL(1.0),CREAL(0.0),CREAL(1.0)};
long nError;
RwReal naBlack[] = {CREAL(0.0),CREAL(0.0),CREAL(0.0)};
int nBlack;
RwPointerImage piImage;
/*
* Attempt to open (and initialize) the RenderWare library.
* Explicitly specifying the MS Windows driver. The MS Windows
* device driver requires the application's instance handle so
* this is passed in as the device specific parameter to RwOpen().
*/
if (!RwOpen("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;
}
/* Load in the palette */
CheckAndReadPalette("rwmaze.pal");
nGTextCol = RwDeviceControl(rwSCRGETCOLOR,0,naTextCol,sizeof(naTextCol));
nBlack = RwDeviceControl(rwSCRGETCOLOR,0,naBlack,sizeof(naBlack));
/* Rematch the pointer image */
piImage.image = NULL;
piImage.hotx=0;
piImage.hoty=0;
piImage.w = nBlack;
piImage.h = nGTextCol;
RwDeviceControl(rwPOINTERSETIMAGE,0,&piImage,sizeof(piImage));
/* Set up the DOS Character Set */
RwGetDeviceInfo(rwSCRHEIGHT,&nGScrHeight,sizeof(nGScrHeight));
RwGetDeviceInfo(rwSCRWIDTH,&nGScrWidth,sizeof(nGScrHeight));
nGMapCol = RwDeviceControl(rwSCRGETCOLOR,0,naMapCol,sizeof(naMapCol));
nGCrossCol = RwDeviceControl(rwSCRGETCOLOR,0,naCrossCol,sizeof(naCrossCol));
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);
/* RwOpenDebugStream("DEBUG:"); */
/*
* Label the window 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, "DosMaze V%s %s",
version, (param ? "Fixed" : "Float"));
DosPrintString(0,nGScrHeight-16,windowText,nGTextCol);
/*
* Create the camera which will be used for rendering. The initial window
* size the application will create is given by nGScrWidth and
* nGScrHeight-17 which depends on the screen res used.
*/
Camera = RwCreateCamera(nGScrWidth,nGScrHeight-17, 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);
}
/*
* The window has been resized. Therefore, it is necessary to
* to modify the camera's viewport to be the same size as the
* client area of the window.
*/
RwSetCameraViewport(Camera, 0, 0, nGScrWidth, nGScrHeight-24);
RwSetCameraBackdropViewportRect(Camera, 0,0,nGScrWidth,nGScrHeight-24);
/* aspect ratio of 1:1 */
if (nGScrWidth >= (nGScrHeight-24)) {
RwSetCameraViewwindow(Camera,
CREAL(1.0),
RMul(CREAL(1.0), RDiv(INT2REAL(nGScrHeight-24), INT2REAL(nGScrWidth))));
} else {
RwSetCameraViewwindow(Camera,
RMul(CREAL(1.0), RDiv(INT2REAL(nGScrWidth), INT2REAL(nGScrHeight-24))),
CREAL(1.0));
};
/*
* Set the camera's background color to cyan.
*/
RwSetCameraBackColor(Camera, CREAL(0.0), CREAL(0.8), CREAL(0.8));
RwSetCameraBackdrop(Camera, RwReadRaster("backgrou", rwDITHERRASTER) );
RwSetCameraBackdropOffset(Camera, 0,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(-0.5), CREAL(-1.0), CREAL(0.5),
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 maze components */
Turn = RwReadShape("turn.rwx");
if (!Turn) {
RwClose();
printf("Cant find turn.rwx\n");
exit(-1);
};
RwAddClumpToScene(Scene, Turn);
TurnF = RwReadShape("turnf.rwx");
if (!TurnF) {
RwClose();
printf("Cant find turnf.rwx\n");
exit(-1);
};
RwAddClumpToScene(Scene, TurnF);
TurnB = RwReadShape("turnb.rwx");
if (!TurnB) {
RwClose();
printf("Cant find turnb.rwx\n");
exit(-1);
};
RwAddClumpToScene(Scene, TurnB);
Room = RwReadShape("mazeroom.rwx");
if (!Room) {
RwClose();
printf("Cant find mazeroom.rwx\n");
exit(-1);
};
RwAddClumpToScene(Scene, Room);
RoomSky = RwReadShape("roomsky.rwx");
if (!RoomSky) {
RwClose();
printf("Cant find roomsky.rwx\n");
exit(-1);
};
RwAddClumpToScene(Scene, RoomSky);
/*
* We now scan the array map[][] and for each cell we initialise
* the corresponding dungeon[][] entry with the appropriate
* geometry for this cell. Since the room and corridor objects
* are larger than a single grid square, we arrange that the
* map[][] array specifies a tag in the top left corner of the
* room or corridor and the switch statement below ensures the
* related grids are initialised appropriately.
*/
for (y = 0; y<DUNHEIGHT; y++)
{
for (x = 0; x<DUNWIDTH; x++)
{
switch(map[y][x])
{
default:
break;
case '@':
RwSetCameraPosition(Camera, RAdd(CREAL(0.5),INT2REAL(x)),
CREAL(0.3),
RAdd(CREAL(0.5),INT2REAL(y)));
RwSetCameraLookAt(Camera, CREAL(1.0),CREAL(0),CREAL(0));
break;
case '1':
dungeon[y][x].tag = '1';
dungeon[y][x].geometry = Turn;
dungeon[y][x].hgeometry = TurnB;
dungeon[y][x].vgeometry = TurnF;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x),CREAL(0),INT2REAL(y),rwREPLACE);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y+2][x];
dungeon[y][x].horizontal = &dungeon[y][x+2];
dungeon[y][x].walls = WEST | NORTH;
dungeon[y][x+1] = dungeon[y][x];
dungeon[y][x+1].walls = NORTH | SOUTH;
dungeon[y+1][x] = dungeon[y][x];
dungeon[y+1][x].walls = WEST | EAST;
break;
case '2':
dungeon[y][x].tag = '2';
dungeon[y][x].geometry = Turn;
dungeon[y][x].hgeometry = TurnF;
dungeon[y][x].vgeometry = TurnB;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x),CREAL(0),INT2REAL(y+2),rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(90),rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y-1][x];
dungeon[y][x].horizontal = &dungeon[y+1][x+2];
dungeon[y][x].walls = WEST | EAST;
dungeon[y+1][x] = dungeon[y][x];
dungeon[y+1][x].walls = WEST | SOUTH;
dungeon[y+1][x+1] = dungeon[y][x];
dungeon[y+1][x+1].walls = NORTH | SOUTH;
break;
case '3':
dungeon[y][x].tag = '3';
dungeon[y][x].geometry = Turn;
dungeon[y][x].hgeometry = TurnB;
dungeon[y][x].vgeometry = TurnF;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x+1),CREAL(0),INT2REAL(y+2),rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(180),rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y-1][x];
dungeon[y][x].horizontal = &dungeon[y+1][x-2];
dungeon[y][x].walls = WEST | EAST;
dungeon[y+1][x-1] = dungeon[y][x];
dungeon[y+1][x-1].walls = NORTH | SOUTH;
dungeon[y+1][x] = dungeon[y][x];
dungeon[y+1][x].walls = SOUTH | EAST;
break;
case '4':
dungeon[y][x].tag = '4';
dungeon[y][x].geometry = Turn;
dungeon[y][x].hgeometry = TurnF;
dungeon[y][x].vgeometry = TurnB;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x+2),CREAL(0),INT2REAL(y),rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(-90),rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y+2][x+1];
dungeon[y][x].horizontal = &dungeon[y][x-1];
dungeon[y][x].walls = NORTH | SOUTH;
dungeon[y][x+1] = dungeon[y][x];
dungeon[y][x+1].walls = NORTH | EAST;
dungeon[y+1][x+1] = dungeon[y][x];
dungeon[y+1][x+1].walls = WEST | EAST;
break;
case 'a':
dungeon[y][x].tag = 'a';
dungeon[y][x].geometry = Room;
dungeon[y][x].hgeometry = Room;
dungeon[y][x].vgeometry = Room;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x),CREAL(0),INT2REAL(y), rwREPLACE);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y+4][x];
dungeon[y][x].horizontal = &dungeon[y][x+4];
dungeon[y][x].walls = 0;
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y+dy][x+dx] = dungeon[y][x];
for (dy=0; dy<4; dy++)
{
for (dx=0; dx<4; dx++)
{
if (dx == 0)
dungeon[y+dy][x+dx].walls |= WEST;
if (dx == 3)
dungeon[y+dy][x+dx].walls |= EAST;
if (dy == 0)
dungeon[y+dy][x+dx].walls |= NORTH;
if (dy == 3)
dungeon[y+dy][x+dx].walls |= SOUTH;
}
}
/* punch holes for the two doors */
dungeon[y][x+3].walls &= ~EAST;
dungeon[y+3][x].walls &= ~SOUTH;
break;
case 'b':
dungeon[y][x].tag = 'b';
dungeon[y][x].geometry = Room;
dungeon[y][x].hgeometry = Room;
dungeon[y][x].vgeometry = Room;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x),CREAL(0),INT2REAL(y+4), rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(90), rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y-1][x];
dungeon[y][x].horizontal = &dungeon[y+3][x+4];
dungeon[y][x].walls = 0;
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y+dy][x+dx] = dungeon[y][x];
for (dy=0; dy<4; dy++)
{
for (dx=0; dx<4; dx++)
{
if (dx == 0)
dungeon[y+dy][x+dx].walls |= WEST;
if (dx == 3)
dungeon[y+dy][x+dx].walls |= EAST;
if (dy == 0)
dungeon[y+dy][x+dx].walls |= NORTH;
if (dy == 3)
dungeon[y+dy][x+dx].walls |= SOUTH;
}
}
/* punch holes for the two doors */
dungeon[y][x].walls &= ~NORTH;
dungeon[y+3][x+3].walls &= ~EAST;
break;
case 'c':
dungeon[y][x].tag = 'c';
dungeon[y][x].geometry = Room;
dungeon[y][x].hgeometry = Room;
dungeon[y][x].vgeometry = Room;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x+4),CREAL(0),INT2REAL(y+4), rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(180), rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y-1][x+3];
dungeon[y][x].horizontal = &dungeon[y+3][x-1];
dungeon[y][x].walls = 0;
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y+dy][x+dx] = dungeon[y][x];
for (dy=0; dy<4; dy++)
{
for (dx=0; dx<4; dx++)
{
if (dx == 0)
dungeon[y+dy][x+dx].walls |= WEST;
if (dx == 3)
dungeon[y+dy][x+dx].walls |= EAST;
if (dy == 0)
dungeon[y+dy][x+dx].walls |= NORTH;
if (dy == 3)
dungeon[y+dy][x+dx].walls |= SOUTH;
}
}
/* punch holes for the two doors */
dungeon[y+3][x].walls &= ~WEST;
dungeon[y][x+3].walls &= ~NORTH;
break;
case 'd':
dungeon[y][x].tag = 'd';
dungeon[y][x].geometry = Room;
dungeon[y][x].hgeometry = Room;
dungeon[y][x].vgeometry = Room;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x+4),CREAL(0),INT2REAL(y), rwREPLACE);
RwRotateMatrix(mat, CREAL(0),CREAL(1),CREAL(0),CREAL(-90), rwPRECONCAT);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y+4][x+3];
dungeon[y][x].horizontal = &dungeon[y][x-1];
dungeon[y][x].walls = 0;
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y+dy][x+dx] = dungeon[y][x];
for (dy=0; dy<4; dy++)
{
for (dx=0; dx<4; dx++)
{
if (dx == 0)
dungeon[y+dy][x+dx].walls |= WEST;
if (dx == 3)
dungeon[y+dy][x+dx].walls |= EAST;
if (dy == 0)
dungeon[y+dy][x+dx].walls |= NORTH;
if (dy == 3)
dungeon[y+dy][x+dx].walls |= SOUTH;
}
}
/* punch holes for the two doors */
dungeon[y][x].walls &= ~WEST;
dungeon[y+3][x+3].walls &= ~SOUTH;
break;
case 'e':
dungeon[y][x].tag = 'e';
dungeon[y][x].geometry = RoomSky;
dungeon[y][x].hgeometry = RoomSky;
dungeon[y][x].vgeometry = RoomSky;
mat = RwCreateMatrix();
RwTranslateMatrix(mat,INT2REAL(x),CREAL(0),INT2REAL(y), rwREPLACE);
dungeon[y][x].mat = mat;
dungeon[y][x].vertical = &dungeon[y+4][x];
dungeon[y][x].horizontal = &dungeon[y][x+4];
dungeon[y][x].dobacking = 1;
dungeon[y][x].walls = 0;
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y+dy][x+dx] = dungeon[y][x];
for (dy=0; dy<4; dy++)
{
for (dx=0; dx<4; dx++)
{
if (dx == 0)
dungeon[y+dy][x+dx].walls |= WEST;
if (dx == 3)
dungeon[y+dy][x+dx].walls |= EAST;
if (dy == 0)
dungeon[y+dy][x+dx].walls |= NORTH;
if (dy == 3)
dungeon[y+dy][x+dx].walls |= SOUTH;
}
}
/* punch holes for the two doors */
dungeon[y][x+3].walls &= ~EAST;
dungeon[y+3][x].walls &= ~SOUTH;
break;
case 'A':
dungeon[y][x].contents = RwCreateScene();
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y-dy][x-dx].contents = dungeon[y][x].contents;
RwAddLightToScene(dungeon[y][x].contents, RwDuplicateLight(Light));
clump = RwReadShape("c.rwx");
RwTranslateMatrix(RwScratchMatrix(),CREAL(x-2),CREAL(0),CREAL(y-2), rwREPLACE);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, SPINFADE);
clump = RwReadShape("cube.rwx");
RwTranslateMatrix(RwScratchMatrix(), CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x),CREAL(0),CREAL(y), rwPRECONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, TUMBLE);
break;
case 'B':
dungeon[y][x].contents = RwCreateScene();
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y-dy][x-dx].contents = dungeon[y][x].contents;
RwAddLightToScene(dungeon[y][x].contents, RwDuplicateLight(Light));
clump = RwReadShape("cone.rwx");
RwTranslateMatrix(RwScratchMatrix(),CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x),CREAL(0),CREAL(y), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, TUMBLE);
clump = RwReadShape("flames.rwx");
RwTranslateMatrix(RwScratchMatrix(), CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x),CREAL(0),CREAL(y-3), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, DONOTHING);
clump = RwReadShape("flames.rwx");
RwTranslateMatrix(RwScratchMatrix(), CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x-2),CREAL(0),CREAL(y-3), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, DONOTHING);
clump = RwReadShape("flames.rwx");
RwTranslateMatrix(RwScratchMatrix(), CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x-1),CREAL(0),CREAL(y-1), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, DONOTHING);
RwForAllPolygonsInClumpReal(clump, RwSetPolygonOpacity, CREAL(0.5));
break;
case 'C':
dungeon[y][x].contents = RwCreateScene();
for (dy=0; dy<4; dy++)
for (dx=0; dx<4; dx++)
dungeon[y-dy][x-dx].contents = dungeon[y][x].contents;
RwAddLightToScene(dungeon[y][x].contents,
RwCreateLight(rwDIRECTIONAL,CREAL(0.5),CREAL(1),CREAL(0.5),CREAL(1)));
clump = RwReadShape("lampshade.rwx");
RwTranslateMatrix(RwScratchMatrix(),CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x-1),CREAL(1.5),CREAL(y-1), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, TUMBLE);
clump = RwReadShape("slab.rwx");
RwTranslateMatrix(RwScratchMatrix(),CREAL(0.5),CREAL(0),CREAL(0.5), rwREPLACE);
RwTranslateMatrix(RwScratchMatrix(), CREAL(x-1),CREAL(1.5),CREAL(y-1), rwPOSTCONCAT);
RwTransformClump(clump, RwScratchMatrix(), rwREPLACE);
RwAddClumpToScene(dungeon[y][x].contents, clump);
RwSetClumpData(clump, DONOTHING);
break;
}
}
}
/*
* 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()
{
int x,y;
/*
* Re-parse the map[][] array and destroy any RenderWare objects we
* we created in Init3D(). The geometry is all cleaned up by just
* doing a RwDestroyScene() which will automatically destroy all
* geometry in the scene along with any light sources.
*/
for (y=0; y<DUNHEIGHT; y++)
{
for (x=0; x<DUNWIDTH; x++)
{
switch(map[y][x])
{
default:
case '.':
case '*':
break;
case '1':
case '2':
case '3':
case '4':
RwDestroyMatrix(dungeon[y][x].mat);
break;
case 'a':
case 'b':
case 'c':
case 'd':
if (dungeon[y][x].contents)
RwDestroyScene(dungeon[y][x].contents);
RwDestroyMatrix(dungeon[y][x].mat);
break;
case 'R':
if (dungeon[y][x].contents)
RwDestroyScene(dungeon[y][x].contents);
RwDestroyMatrix(dungeon[y][x].mat);
break;
}
}
}
/*
* 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();
}
/**********************************************************************/
void
render(void)
{
RwV3d at;
Dcell *currcell;
int x,y;
/*
* Get the camera position and floor() so as to find the cell
* the camera is currently inside. We use this to limit
* the amount of geometry we draw just to the immediate
* vicinity of the camera to get the most performance.
*/
RwGetCameraPosition(Camera, &at);
x = REAL2INT(at.x);
y = REAL2INT(at.z);
if (x < 0 && x >= DUNWIDTH && y < 0 && y >= DUNHEIGHT)
return;
currcell = &dungeon[y][x];
if (currcell == NULL)
return;
RwBeginCameraUpdate(Camera,NULL);
/* if any of the cells we'll be drawing require the viewport to be cleared
* we better do it now.
*/
if (currcell->vertical->dobacking ||
currcell->horizontal->dobacking ||
currcell->dobacking)
RwClearCameraViewport(Camera);
/*
* Here we draw the two adjoining rooms/corridors followed by the
* the room we're currently inside. We do this by transforming the
* referenced geometry by the previously computed matrix stored with
* the cell, and drawing the geometry with RenderClump. Each room optionally
* has a RwScene associated with it which contains the contents of the
* room. Since we gaurantee that the contents is always completely inside
* the room, we can draw the contents using RwRenderScene to
* overpaint the walls of the room we draw using RwRenderClump().
*/
RwTransformClump(currcell->vertical->vgeometry,currcell->vertical->mat, rwREPLACE);
RwRenderClump(currcell->vertical->vgeometry);
if (currcell->vertical->contents)
RwRenderScene(currcell->vertical->contents);
RwTransformClump(currcell->horizontal->hgeometry,currcell->horizontal->mat, rwREPLACE);
RwRenderClump(currcell->horizontal->hgeometry);
if (currcell->horizontal->contents)
RwRenderScene(currcell->horizontal->contents);
RwTransformClump(currcell->geometry,currcell->mat, rwREPLACE);
RwRenderClump(currcell->geometry);
if (currcell->contents)
RwRenderScene(currcell->contents);
RwEndCameraUpdate(Camera);
/* overlay text onto RW image */
/* draw crosshair */
if (MouseMoveMode == MMPanAndZoomCamera)
{
DosDrawLine(Camera,(nGScrWidth>>1)-8,(nGScrHeight-24)>>1,
(nGScrWidth>>1)+8,(nGScrHeight-24)>>1,nGCrossCol);
DosDrawLine(Camera,nGScrWidth>>1,((nGScrHeight-24)>>1)-8,
nGScrWidth>>1,((nGScrHeight-24)>>1)+8,nGCrossCol);
}
if (DrawHUD)
{
int dx,dy;
int xc,yc;
for (dy=-4; dy<=4; dy++)
{
for (dx=-4; dx<=4; dx++)
{
if (((x+dx) >= 0) && ((x+dx) < DUNWIDTH) &&
((y+dy) >= 0) && ((y+dy) < DUNHEIGHT))
{
xc = 100 - ((4+1+4)*10) + (dx + 4) * 10;
yc = + (dy + 4) * 10;
if (dungeon[y+dy][x+dx].walls & NORTH)
{
DosDrawLine(Camera,xc,yc,xc+10,yc,nGMapCol);
}
if (dungeon[y+dy][x+dx].walls & WEST)
{
DosDrawLine(Camera,xc,yc,xc,yc+10,nGMapCol);
}
if (dungeon[y+dy][x+dx].walls & SOUTH)
{
DosDrawLine(Camera,xc,yc+10,xc+10,yc+10,nGMapCol);
}
if (dungeon[y+dy][x+dx].walls & EAST)
{
DosDrawLine(Camera,xc+10,yc,xc+10,yc+10,nGMapCol);
}
/* mark spot where we are */
if ((dx == 0) && (dy == 0)) {
DosDrawLine(Camera,xc+1,yc+1,xc+9,yc+1,nGTextCol);
DosDrawLine(Camera,xc+9,yc+1,xc+9,yc+9,nGTextCol);
DosDrawLine(Camera,xc+9,yc+9,xc+1,yc+9,nGTextCol);
DosDrawLine(Camera,xc+1,yc+9,xc+1,yc+1,nGTextCol);
};
}
}
}
}
RwShowCameraImage(Camera, NULL);
}
/**********************************************************************/
/*
* Move the camera forward. We cannot use RwVCMoveCamera() since the
* camera is potentially tilted up or down - we want to move forward
* only in the XZ plane. In addition we have some simple periodic
* movement in the Y direction to give a feeling of walking.
* We constrain the camera movement by examining the "walls" field
* of the current cell to determine whether the camera (aka player)
* is able to move in that direction.
*/
void
moveforward(RwReal forward)
{
RwV3d pos1,pos2;
RwV3d at;
int x1,y1,x2,y2;
static int dbounce;
RwGetCameraPosition(Camera, &pos1);
x1 = REAL2INT(pos1.x);
y1 = REAL2INT(pos1.z);
RwGetCameraLookAt(Camera, &at);
if (forward)
{
if (dbounce >= 8)
dbounce = 0;
RwWCMoveCamera(Camera, RMul(at.x,forward),
dbounce >= 4 ? CREAL(0.025) : CREAL(-0.025),
RMul(at.z,forward));
dbounce++;
}
RwGetCameraPosition(Camera, &pos2);
x2 = REAL2INT(pos2.x);
y2 = REAL2INT(pos2.z);
/* constrain movement based upon cell walls */
if (REAL2INT(pos2.x - CLOSEST) < x1 && (dungeon[y1][x1].walls & WEST))
{
pos2.x = INT2REAL(REAL2INT(pos1.x));
pos2.x += CLOSEST;
}
if (REAL2INT(pos2.x + CLOSEST) > x1 && (dungeon[y1][x1].walls & EAST))
{
pos2.x = INT2REAL(REAL2INT(pos1.x) + 1);
pos2.x -= CLOSEST;
}
if (REAL2INT(pos2.z - CLOSEST) < y1 && (dungeon[y1][x1].walls & NORTH))
{
pos2.z = INT2REAL(REAL2INT(pos1.z));
pos2.z += CLOSEST;
}
if (REAL2INT(pos2.z + CLOSEST) > y1 && (dungeon[y1][x1].walls & SOUTH))
{
pos2.z = INT2REAL(REAL2INT(pos1.z) + 1);
pos2.z -= CLOSEST;
}
/* special case diagonal move which causes problems */
if ((x1 != x2) && (y1 != y2))
{
if (y2 > y1)
{
if (dungeon[y2-1][x2].walls & SOUTH)
{
pos2.z = INT2REAL(REAL2INT(pos1.z) + 1);
pos2.z -= CLOSEST;
}
}
else
{
if (dungeon[y2+1][x2].walls & NORTH)
{
pos2.z = INT2REAL(REAL2INT(pos1.z));
pos2.z += CLOSEST;
}
}
}
RwSetCameraPosition(Camera, pos2.x, pos2.y, pos2.z);
}
/**********************************************************************/
/*
* 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)
{
if (vKeys & MK_CONTROL)
{
MouseMoveMode = MMPanLight;
}
else if (vKeys & MK_SHIFT)
{
MouseMoveMode = MMTiltCamera;
}
else
{
MouseMoveMode = MMPanAndZoomCamera;
forward = CREAL(0.0);
pan = CREAL(0.0);
}
/*
* 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;
}
}
/**********************************************************************/
/*
* 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;
}
else if (vKeys & MK_SHIFT)
{
MouseMoveMode = MMTiltCamera;
}
else
{
MouseMoveMode = MMPanAndZoomCamera;
forward = CREAL(0.0);
pan = CREAL(0.0);
}
/*
* 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)
{
int nXCentre,nYCentre;
RwInt32 offx,offy;
nXCentre = nGScrWidth >>1;
nYCentre = nGScrHeight >>1;
/*
* 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.
*/
/*
* Pan the camera by mouse X delta degrees.
*/
pan = RDiv(INT2REAL(nXCentre - x), CREAL(10.0));
/*
* Zoom the camera by changing the distance the camera
* is from the origin of the world by mouse Y delta divided
* by 100 units.
*/
forward = RDiv(INT2REAL(nYCentre - y), CREAL(400.0));
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.
*/
/*
* Pan the camera by mouse X delta degrees.
*/
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0),CREAL(1.0),CREAL(0.0),
INT2REAL(LastX - x),rwREPLACE);
RwTransformCameraOrientation(Camera, RwScratchMatrix());
RwTiltCamera(Camera, INT2REAL(y - LastY));
RwGetCameraBackdropOffset(Camera, &offx,&offy);
RwSetCameraBackdropOffset(Camera, offx - (LastX - x)*2, offy);
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();
}
/*
* 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()
{
/*
* 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;
}
}
/**********************************************************************/
/*
* 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()
{
RwSetCameraLookUp(Camera, CREAL(0.0),CREAL(1.0),CREAL(0.0));
/*
* 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;
}
}
/**********************************************************************/
/*
* Each Scene associated with a room enumerates the Clumps and calls
* this function to perform some simple behaviour. The choice of
* behaviour is determined by the low 8 bits of the clump's Data
* field. The bits above the low 8 bits are used as a counter to
* trigger when orthonormalisation must occur.
*/
RwClump *
dispatchbehav(RwClump *obj)
{
long val;
int ortho;
static RwV3d axis = {CREAL(1),CREAL(1),CREAL(1)};
static RwReal theta = CREAL(0);
static RwReal dtheta = CREAL(0.5);
RwReal opacity;
ortho = 0;
val = (long)RwGetClumpData(obj);
switch (val & 255)
{
case DONOTHING:
break;
case SPINFADE:
if ((val >> 8) & 32)
{
opacity = RDiv(INT2REAL((val >> 8) & 15), CREAL(16));
if (((val >> 8) & 31) >= 16)
opacity = RSub(CREAL(1),opacity);
}
else
opacity = 0;
opacity = RSub(CREAL(1),opacity);
RwForAllPolygonsInClumpReal(obj, RwSetPolygonOpacity, opacity);
/* fall-thru */
case SPIN:
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0),CREAL(1.0),CREAL(0.0),
CREAL(10), rwREPLACE);
RwTransformClumpJoint(obj, RwScratchMatrix(), rwPRECONCAT);
ortho = 1;
break;
case TUMBLE:
theta = RAdd(theta, dtheta);
if ((theta <= CREAL(-30)) || (theta >= CREAL(30)))
dtheta = -dtheta;
if (theta == CREAL(0))
{
axis.x = rand();
axis.y = rand();
axis.z = rand();
}
RwRotateMatrix(RwScratchMatrix(), axis.x, axis.y,axis.z,
theta, rwREPLACE);
RwTransformClumpJoint(obj, RwScratchMatrix(), rwPRECONCAT);
ortho = 1;
break;
}
/* Do periodic orthonormalisation if we're using rotation */
if (ortho)
{
val += 256;
RwSetClumpData(obj, val);
if (((val>>8) & 127) == 0)
{
RwGetClumpJointMatrix(obj, RwScratchMatrix());
RwOrthoNormalizeMatrix(RwScratchMatrix(), RwScratchMatrix());
RwTransformClumpJoint(obj, RwScratchMatrix(), rwREPLACE);
}
}
return(obj);
}
/**********************************************************************/
/*
* Handle MS Window's timer expiry. This function will perform any
* animation actions necessary, including spinning clumps and animating
* textures.
*/
static void
HandleTimer(void)
{
static int FrameNumber = 0;
RwInt32 dx,dy;
RwV3d at;
Dcell *currcell;
FrameNumber++;
/*
* 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.
*/
RwForAllNamedTextures(RwTextureNextFrame);
/*
* Perform any behaviour for each scene associated with visible
* clumps.
*/
RwGetCameraPosition(Camera, &at);
dx = REAL2INT(at.x);
dy = REAL2INT(at.z);
currcell = &dungeon[dy][dx];
if (currcell->vertical->contents)
RwForAllClumpsInScene(currcell->vertical->contents, dispatchbehav);
if (currcell->horizontal->contents)
RwForAllClumpsInScene(currcell->horizontal->contents, dispatchbehav);
if (currcell->contents)
RwForAllClumpsInScene(currcell->contents, dispatchbehav);
if (pan || forward)
{
RwRotateMatrix(RwScratchMatrix(), CREAL(0.0),CREAL(1.0),CREAL(0.0),pan,rwREPLACE);
RwTransformCameraOrientation(Camera, RwScratchMatrix());
moveforward(forward);
RwGetCameraBackdropOffset(Camera, &dx,&dy);
RwSetCameraBackdropOffset(Camera, dx-REAL2INT(pan)*2, dy);
}
render();
}
/****************************************************************************
Main
*/
void main(int nArgc,char *saArgv[])
{
int nKey;
int nMouseX,nMouseY,nMouseBut,nOldMouseBut,nOldMouseX,nOldMouseY;
int nDX,nDY;
int nChange;
int nCtrlShift;
/* Stop warnings */
nArgc = nArgc;
if (!Init3D(saArgv[0]))
{
exit(-1);
};
DrawHUD=1;
/*
* Parse any command line parameters.
*/
RwDPointerDisplay(&nOldMouseX,&nOldMouseY,&nOldMouseBut);
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);
}
/**********************************************************************/