|
|
#include "pch.c"
#pragma hdrstop
#include <time.h>
#include "mazedlg.h"
#include <assert.h>
MazeView vw; MazeSolution sol;
MazeOptions maze_options = { FALSE, // depth_test
RENDER_FLAT, // render mode (WALLS)
RENDER_FLAT, // render mode (FLOOR)
RENDER_FLAT, // render mode (CEILING)
FALSE, // frame_count
FALSE, // top_view
TRUE, // eye_view
FALSE, // single_step
FALSE, // all_alpha
FALSE, // bDither
1 // nrats
};
float gfAspect = 1.0f; // aspect ratio
#define SST_NEW_MAZE 0
#define SST_MAZE_GROW 1
#define SST_SOLVING 2
#define SST_MAZE_SHRINK 3
#define SST_ROTATE 4
int solve_state = SST_NEW_MAZE;
// Table of textures used. The first entries contain 1 texture for each main
// surface, followed by textures used for various objects
TEXTURE all_textures[NUM_TEXTURES];
// Environment associated with the texture (repetition, palette rotation,..)
// For now, have one for each of the textures in all_textures. But, this
// could be a per-object thing.
TEX_ENV gTexEnv[NUM_TEXTURES];
// texture environment mode
int gTexEnvMode;
// Texture resources for the main surfaces
TEX_RES gTexResSurf[NUM_DEF_SURFACE_TEXTURES] = { {TEX_BMP, IDB_BRICK}, // default textures
{TEX_BMP, IDB_WOOD}, {TEX_BMP, IDB_CASTLE}, {TEX_A8, IDB_CURL4}, // mandelbrot textures
{TEX_A8, IDB_BHOLE4}, {TEX_A8, IDB_SNOWFLAK}, {TEX_A8, IDB_SWIRLX4} }; // Texture resources for objects
TEX_RES gTexResObject[NUM_OBJECT_TEXTURES] = { {TEX_A8, IDB_START}, {TEX_A8, IDB_END}, {TEX_A8, IDB_RAT}, {TEX_A8, IDB_AD}, {TEX_BMP, IDB_COVER} }; SSContext gssc;
void maze_Init( void *data ); void RotateTexturePalettes( TEX_ENV *pTexEnv, int count, int iRot );
enum { TIMER_START = 0, TIMER_STOP };
void Draw(void) { GLbitfield mask;
mask = 0; if (maze_options.depth_test) { mask |= GL_DEPTH_BUFFER_BIT; } if (maze_options.render[FLOOR] == RENDER_NONE || maze_options.render[CEILING] == RENDER_NONE || maze_options.render[WALLS] == RENDER_NONE || maze_height < 1.0f) { mask |= GL_COLOR_BUFFER_BIT; } if (mask != 0) { glClear(mask); }
// Rotate the palettes of any paletted texures by 1
RotateTexturePalettes( gTexEnv, NUM_TEXTURES, 1 );
if (maze_options.eye_view) { DrawMaze(&vw); }
if (maze_options.top_view) { DrawTopView(&vw); } glFlush(); }
MazeGoal maze_goals[MAX_GOALS]; int ngoals;
Object special_obj[MAX_SPECIALS];
#define MAX_LETTERS 3
Object letters_obj[MAX_LETTERS];
typedef struct _Rat { Object obj; MazeView vw; MazeSolution sol; } Rat; Rat rats[MAX_RATS];
void NewMazeList(void) { glNewList(maze_walls_list, GL_COMPILE); DrawMazeWalls(); glEndList(); }
void UpdateRatPosition(Rat *rat) { MoveObject(&rat->obj, rat->vw.pos.x, rat->vw.pos.y); // Invert the angle because view angles move opposite to
// object angles
rat->obj.ang = FaAdd(0, -rat->vw.ang); }
// Simple routines to pick cells at random while guaranteeing
// that those cells are unoccupied
static int rnd_cells[MAZE_CELLS]; static int nrnd_cells;
void InitRandomCells(void) { int i;
nrnd_cells = MAZE_CELLS; for (i = 0; i < nrnd_cells; i++) { rnd_cells[i] = i; } }
void PickRandomCell(IntPt2 *pos) { int idx, t;
#if DBG
if (nrnd_cells == 0) { MessageBox(GetDesktopWindow(), TEXT("Out of random cells"), NULL, MB_OK); pos->x = 0; pos->y = 0; return; } #endif
idx = rand() % nrnd_cells; nrnd_cells--; t = rnd_cells[idx]; rnd_cells[idx] = rnd_cells[nrnd_cells]; pos->x = t % MAZE_GRID; pos->y = t / MAZE_GRID; }
void RemoveRandomCell(IntPt2 *pos) { int i, idx;
for (i = 0; i < nrnd_cells; i++) { idx = rnd_cells[i]; if ((idx % MAZE_GRID) == pos->x && (idx / MAZE_GRID) == pos->y) { nrnd_cells--; rnd_cells[i] = rnd_cells[nrnd_cells]; return; } } }
void NewMaze(void) { IntPt2 cell, start_cell; int i, nspecials, nads; static BOOL firstMaze = TRUE;
// If not in full screen mode, move the maze window around after it's solved
if( !gbTurboMode && !firstMaze ) ss_RandomWindowPos();
InitRandomCells(); if (!InitMaze(&start_cell, maze_goals, &ngoals)) { printf("InitMaze failed\n"); exit(1); }
RemoveRandomCell(&start_cell); cell.x = maze_goals[GOAL_END].clx; cell.y = maze_goals[GOAL_END].cly; RemoveRandomCell(&cell);
nspecials = rand() % MAX_SPECIALS; for (i = 0; i < nspecials; i++) { PickRandomCell(&cell); maze_goals[ngoals].clx = cell.x; maze_goals[ngoals].cly = cell.y; maze_goals[ngoals].user = &special_obj[i]; special_obj[i].w = FMAZE_CELL_SIZE/4; special_obj[i].h = FxFltVal(.25); special_obj[i].z = special_obj[i].h; special_obj[i].col = 15; special_obj[i].draw_style = DRAW_SPECIAL; special_obj[i].draw_arg = rand() % SPECIAL_ARG_COUNT; special_obj[i].ang = FaDeg(0); special_obj[i].user1 = (rand() % 6)+2; special_obj[i].user2 = rand() % 5; special_obj[i].user3 = 0; PlaceObject(&special_obj[i], CellToMfx(maze_goals[ngoals].clx)+FMAZE_CELL_SIZE/2, CellToMfx(maze_goals[ngoals].cly)+FMAZE_CELL_SIZE/2); ngoals++; } while (i < MAX_SPECIALS) { special_obj[i].cell = NULL; i++; }
nads = (rand() % (MAX_LETTERS*2))-MAX_LETTERS+1; for (i = 0; i < nads; i++) { PickRandomCell(&cell); letters_obj[i].w = FMAZE_CELL_SIZE/3; letters_obj[i].h = FxFltVal(.33); letters_obj[i].z = FxFltVal(.5); letters_obj[i].col = 15; letters_obj[i].draw_style = DRAW_POLYGON; letters_obj[i].pTexEnv = &gTexEnv[ TEX_AD ]; letters_obj[i].ang = FaDeg(0); PlaceObject(&letters_obj[i], CellToMfx(cell.x)+FMAZE_CELL_SIZE/2, CellToMfx(cell.y)+FMAZE_CELL_SIZE/2); } while (i < MAX_LETTERS) { letters_obj[i].cell = NULL; i++; }
for (i = 0; i < maze_options.nrats; i++) { PickRandomCell(&cell); rats[i].obj.w = FMAZE_CELL_SIZE/4; rats[i].obj.h = FxFltVal(.125); rats[i].obj.z = rats[i].obj.h; rats[i].obj.col = 16; rats[i].obj.draw_style = DRAW_POLYGON; rats[i].obj.pTexEnv = &gTexEnv[ TEX_RAT ]; SolveMazeStart(&rats[i].vw, &maze_cells[0][0], MAZE_GRID, MAZE_GRID, &cell, SOL_DIR_LEFT, NULL, 0, (rand() % 1000) > 500 ? SOL_TURN_LEFT : SOL_TURN_RIGHT, &rats[i].sol); // Need to force this to NULL when a new maze is generated so
// that moving doesn't try to use old maze data
rats[i].obj.cell = NULL; UpdateRatPosition(&rats[i]); } NewMazeList(); SolveMazeStart(&vw, &maze_cells[0][0], MAZE_GRID, MAZE_GRID, &start_cell, SOL_DIR_RIGHT, maze_goals, ngoals, (rand() % 1000) > 500 ? SOL_TURN_LEFT : SOL_TURN_RIGHT, &sol);
solve_state = SST_MAZE_GROW; firstMaze = FALSE; }
/**************************************************************************\
* Step * * Main draw proc * \**************************************************************************/
static MazeGoal *found_goal = NULL; static int rot_step;
void Step(void *data) { int i; MazeGoal *goal;
switch(solve_state) { case SST_NEW_MAZE: view_rot = 0; maze_height = 0.0f; NewMaze(); break;
case SST_SOLVING: for (i = 0; i < maze_options.nrats; i++) { SolveMazeStep(&rats[i].vw, &rats[i].sol); UpdateRatPosition(&rats[i]); } goal = SolveMazeStep(&vw, &sol); if (goal == &maze_goals[GOAL_END]) { solve_state = SST_MAZE_SHRINK; } else if (goal != NULL) { solve_state = SST_ROTATE; found_goal = goal; rot_step = 0; } break;
case SST_MAZE_GROW: maze_height += .025f; if (maze_height >= 1.0f) { solve_state = SST_SOLVING; } break; case SST_MAZE_SHRINK: maze_height -= .025f; if (maze_height <= 0.0f) { solve_state = SST_NEW_MAZE; } break;
case SST_ROTATE: view_rot += 10.0; if (++rot_step == 18) { Object *sp_obj;
sp_obj = (Object *)found_goal->user; RemoveObject(sp_obj); solve_state = SST_SOLVING; ngoals--; if (found_goal < maze_goals+ngoals) { memmove(found_goal, found_goal+1, sizeof(MazeGoal)*(size_t)((ULONG_PTR)(ngoals-(found_goal-maze_goals)))); } SolveMazeSetGoals(&sol, maze_goals, ngoals); found_goal = NULL;
if (view_rot >= 360.0) { view_rot = 0.0; } else { view_rot = 180.0; } } break; }
Draw();
for (i = 0; i < MAX_SPECIALS; i++) { // Increment rotations of any specials still present in the maze
if (special_obj[i].cell != NULL) { special_obj[i].ang = FaAdd(special_obj[i].ang, FaDeg(special_obj[i].user1)); special_obj[i].user3 += special_obj[i].user2; } } }
void UpdateModes(void) { if (maze_options.depth_test) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } }
/**************************************************************************\
* Reshape * * - called on resize, expose * - always called on app startup * \**************************************************************************/
void Reshape(int width, int height, void *data ) { gfAspect = height == 0 ? 1.0f: (float) width / (float) height; }
/******************************Public*Routine******************************\
* VerifyTextureFiles * * Check that any user specified textures are valid * - MUST be called at ss_Init, or any error msgs will not display properly * \**************************************************************************/
void VerifyTextureFiles(void) { int i;
ss_DisableTextureErrorMsgs();
for (i = 0; i < NUM_SURFACES; i++) { if( gTexInfo[i].bTex && !gTexInfo[i].bDefTex ) { if( !ss_VerifyTextureFile( &gTexInfo[i].texFile ) ) // use default texture
gTexInfo[i].bDefTex = TRUE; } } }
/******************************Public*Routine******************************\
* CalcRep * * Figure out repetion based on size. * * - Use 128 pixels as a unit repetition reference * - Size is always a power of 2 * \**************************************************************************/
static int CalcRep( int size ) { double pow2; int pow2ref = 8; int rep;
pow2 = log((double)size) / log((double)2.0); rep = 1 + pow2ref - (int)pow2; return rep; }
//mf: tradeoff
#define MAX_TEX_DIM 128
//#define MAX_TEX_DIM 256
/******************************Public*Routine******************************\
* CalcTexRep * * Figure out texture repetition based on texture size. * * - mf: ? double for walls/ceiling ? * \**************************************************************************/
static void CalcTexRep( TEXTURE *pTex, IPOINT2D *pTexRep ) { if( pTex->width >= MAX_TEX_DIM ) pTexRep->x = 1; else pTexRep->x = CalcRep( pTex->width );
if( pTex->height >= MAX_TEX_DIM ) pTexRep->y = 1; else pTexRep->y = CalcRep( pTex->height ); }
/******************************Public*Routine******************************\
* LoadTextures * * Load textures from .bmp files or from embedded resources, using * global texture info structure. * * For now, this also sets render modes, since if texturing is off for a * surface, we always use RENDER_FLAT * * History: * - Nov. 95 [marcfo] : Creation * - Jan. 96 [marcfo] : Load surface and object textures separately. The 3 * surface textures occupy the first locations of the all_textures[] * array, and the object textures follow. * \**************************************************************************/
static void LoadTextures(void) { int i; TEXTURE *pTex = all_textures; TEX_ENV *pTexEnv = gTexEnv; TEX_RES *pTexRes;
assert( (NUM_SURFACES + NUM_OBJECT_TEXTURES) == NUM_TEXTURES );
// load surface textures (wall, floor, ceiling)
for (i = 0; i < NUM_SURFACES; i++, pTex++, pTexEnv++) { maze_options.render[i] = RENDER_FLAT; if( gTexInfo[i].bTex ) { pTexEnv->pTex = pTex; pTexEnv->bTransp = FALSE;
// Load user or default texture for the surface
if( !gTexInfo[i].bDefTex && ss_LoadTextureFile( &gTexInfo[i].texFile, pTex )) { } else { // Load default resource texture
pTexRes = &gTexResSurf[ gTexInfo[i].iDefTex ]; if( !ss_LoadTextureResource( pTexRes, pTex ) ) continue; if( ss_PalettedTextureEnabled() && (pTexRes->type == TEX_A8) ) { pTexEnv->bPalRot = TRUE; pTexEnv->iPalRot = ss_iRand( 0xff ); } else pTexEnv->bPalRot = FALSE; }
maze_options.render[i] = RENDER_TEXTURED;
// Figure out texture repetition
CalcTexRep( pTex, &pTexEnv->texRep ); // We would have to set texture parameters per object here,
// but we just use default ones already set by ss_LoadTexture*
} }
// load object textures
pTexRes = gTexResObject; for( i = 0; i < NUM_OBJECT_TEXTURES; i++, pTex++, pTexEnv++, pTexRes++ ) { if( ss_LoadTextureResource( pTexRes, pTex ) ) pTexEnv->pTex = pTex; else pTexEnv->pTex = NULL;
pTexEnv->bTransp = FALSE;
// For now we don't do palrot's on any of the object textures
pTexEnv->bPalRot = FALSE; // No repetition
pTexEnv->texRep.x = pTexEnv->texRep.y = 1; }
// Set transparency for some of the textures
ss_SetTextureTransparency( gTexEnv[TEX_START].pTex, 0.42f, FALSE ); ss_SetTextureTransparency( gTexEnv[TEX_END].pTex, 0.4f, FALSE ); ss_SetTextureTransparency( gTexEnv[TEX_AD].pTex, 0.4f, FALSE );
#if 0
// Enough already with the transparency!
ss_SetTextureTransparency( &all_textures[TEX_COVER], 0.6f, FALSE ); ss_SetTextureTransparency( &all_textures[TEX_WALL], 0.5f, FALSE ); #endif
// Enable transparency for some of the texture environments
gTexEnv[TEX_START].bTransp = TRUE; gTexEnv[TEX_END].bTransp = TRUE; gTexEnv[TEX_AD].bTransp = TRUE; }
void UseTextureEnv( TEX_ENV *pTexEnv ) { static HTEXTURE hCurTex = (HTEXTURE) -1; HTEXTURE hTex = pTexEnv->pTex;
// We cache the current texture for 'no texture object' case
if( !ss_TextureObjectsEnabled() && (hCurTex == hTex) ) return; // same texture, no need to send it down
// Make this texture current
ss_SetTexture( hTex );
// Set texture palette if necessary
if( pTexEnv->bPalRot && !ss_TextureObjectsEnabled() && ss_PalettedTextureEnabled() ) {
// We need to send down the (rotated) palette
ss_SetTexturePalette( hTex, pTexEnv->iPalRot ); }
hCurTex = hTex; }
/******************************Public*Routine******************************\
* RotateTexturePalettes * * If paletted texturing is enabled, go through the supplied list of texture * environments, and if any have a palette, increment its rotation by the * supplied iRot value. * \**************************************************************************/
void RotateTexturePalettes( TEX_ENV *pTexEnv, int count, int iRot ) { int i;
if( !ss_PalettedTextureEnabled() || !pTexEnv ) return;
for( ; count; count--, pTexEnv++ ) {
if( !pTexEnv->pTex || !pTexEnv->pTex->pal ) continue;
if( pTexEnv->bPalRot ) { // increment palette rotation
pTexEnv->iPalRot += iRot; if( pTexEnv->iPalRot >= pTexEnv->pTex->pal_size ) pTexEnv->iPalRot = 0; // Only send down the new palette if texture objects are enabled,
// since otherwise it will be sent down when texture is
// 'made current'
if( ss_TextureObjectsEnabled() ) { ss_SetTexturePalette( pTexEnv->pTex, pTexEnv->iPalRot ); } } } }
/******************************Public*Routine******************************\
* InitStretchInfo * \**************************************************************************/
static void InitStretchInfo( STRETCH_INFO *pStretch ) { ISIZE screen;
pStretch->baseWidth = 320; pStretch->baseHeight = 200; pStretch->bRatioMode = FALSE; }
/******************************Public*Routine******************************\
* SetFloaterInfo * * Set the size and position of the floating child window * \**************************************************************************/
static void SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild ) { float hdtvAspect = 9.0f / 16.0f; ISIZE *pChildSize = &pChild->size;
// Set width according to user-specified size
// (giSize range is 0..100)
// set min size as 1/3 parent width
pChildSize->width = (int) ((0.333f + 2.0f*giSize/300.0f) * pParentSize->width);
// Scale height for hdtv aspect ratio
pChildSize->height = (int) (hdtvAspect * pChildSize->width + 0.5f); // Ensure height not too big
SS_CLAMP_TO_RANGE2( pChildSize->height, 0, pParentSize->height );
pChild->pos.x = (pParentSize->width - pChildSize->width) / 2; pChild->pos.y = (pParentSize->height - pChildSize->height) / 2; }
/******************************Public*Routine******************************\
* ss_Init * * Initialize - called on first entry into ss. * Called BEFORE gl is initialized! * Just do basic stuff here, like set up callbacks, verify dialog stuff, etc. * * Fills global SSContext structure with required data, and returns ptr * to it. * \**************************************************************************/
SSContext * ss_Init( void ) { getIniSettings();
VerifyTextureFiles();
// Set callbacks
ss_InitFunc( maze_Init ); ss_UpdateFunc( Step ); ss_ReshapeFunc( Reshape );
gssc.depthType = (maze_options.depth_test) ? SS_DEPTH16 : SS_DEPTH_NONE;
// Currently stretch and floater mutex
gssc.bDoubleBuf = TRUE; // will get turned off if stretch used
if( gbTurboMode ) { // We stretch out the drawing area to the full size of the main window
gssc.bFloater = FALSE; gssc.bStretch = TRUE; InitStretchInfo( &gssc.stretchInfo ); } else { // A static centered floating window is created in the main window
FLOATER_INFO *pFloater = &gssc.floaterInfo;
gssc.bFloater = TRUE; gssc.bStretch = FALSE; pFloater->bMotion = FALSE; pFloater->ChildSizeFunc = SetFloaterInfo; }
return &gssc; }
/******************************Public*Routine******************************\
* maze_Init * * Initializes OpenGL state * \**************************************************************************/ void maze_Init( void *data ) { float fv4[4];
if (!FxInitialize(FA_TABLE_SIZE, 0)) { printf("FxInit failed\n"); exit(1); }
glShadeModel( GL_FLAT ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glClearDepth(1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
if( (giImageQual == IMAGEQUAL_DEFAULT) || gbTurboMode ) { maze_options.bDither = FALSE; glDisable( GL_DITHER ); } else { maze_options.bDither = TRUE; glEnable( GL_DITHER ); }
// Load textures and set render modes
LoadTextures(); fv4[0] = MAZE_SIZE/2.0f; fv4[1] = MAZE_SIZE/2.0f; fv4[2] = 10.0f; fv4[3] = 1.0f; glLightfv(GL_LIGHT0, GL_POSITION, fv4); glEnable(GL_LIGHT0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Check which texture environment function to use for objects
if( ss_fOnGL11() ) gTexEnvMode = GL_REPLACE; else gTexEnvMode = GL_MODULATE;
maze_walls_list = glGenLists(1); UpdateModes(); }
|