SSMAZE.C

#include "pch.c" 
#pragma hdrstop
#include "maze_std.h"

#define VIEW_ANG 90

float maze_height;
double view_rot;
int maze_walls_list;

extern TEX_ENV gTexEnv[];

typedef struct _FxRay2
{
FxPt2 p;
FxVec2 d;
} FxRay2;

BYTE maze_desc[MAZE_ARRAY][MAZE_ARRAY];

Cell maze_cells[MAZE_GRID][MAZE_GRID];
#define CellAt(x, y) (&maze_cells[y][x])

typedef struct _Wall
{
FxPt2 f, t;
int col;
TEX_ENV *pTexEnv; // points to texture environment
} Wall;

FxPt2 fmaze_pts[N_MAZE_PTS];
Wall maze[N_MAZE_WALLS];
int nwalls;

typedef struct _WallHit
{
Cell *cell;
int cx, cy;
WallFlags flag;
} WallHit;

void AddObject(Object *obj, Cell *cell)
{
obj->next = cell->contents;
cell->contents = obj;
obj->cell = cell;
}

void PlaceObject(Object *obj, FxValue x, FxValue y)
{
Cell *cell;
int cx, cy;

cx = MfxToCell(x);
cy = MfxToCell(y);
cell = CellAt(cx, cy);

obj->p.x = x;
obj->p.y = y;

AddObject(obj, cell);
}

void RemoveObject(Object *obj)
{
Object *o, *op;

if (obj->cell != NULL)
{
op = NULL;
for (o = obj->cell->contents; o != obj; o = o->next)
{
op = o;
}

if (op == NULL)
{
obj->cell->contents = obj->next;
}
else
{
op->next = obj->next;
}

obj->cell = NULL;
}
}

void MoveObject(Object *obj, FxValue x, FxValue y)
{
int cx, cy;
Cell *cell;

obj->p.x = x;
obj->p.y = y;

cx = MfxToCell(x);
cy = MfxToCell(y);
cell = CellAt(cx, cy);

if (cell == obj->cell)
{
return;
}

RemoveObject(obj);
AddObject(obj, cell);
}

Object start_obj, end_obj;

BOOL InitMaze(IntPt2 *start_cell, MazeGoal *goals, int *ngoals)
{
int i, j, n;
FxPt2 p;

if (!GenerateMaze(MAZE_GRID, MAZE_GRID, &maze_desc[0][0]))
{
return FALSE;
}

p.y = FxVal(0);
n = 0;
for (i = 0; i < MAZE_ARRAY; i++)
{
p.x = FxVal(0);
for (j = 0; j < MAZE_ARRAY; j++)
{
fmaze_pts[n].x = p.x;
fmaze_pts[n++].y = p.y;
p.x += FMAZE_CELL_SIZE;
}
p.y += FMAZE_CELL_SIZE;
}

nwalls = 0;
for (i = 0; i < MAZE_ARRAY; i++)
{
for (j = 0; j < MAZE_ARRAY; j++)
{
if (i < MAZE_ARRAY-1 && j < MAZE_ARRAY-1)
{
maze_cells[i][j].can_see = 0;
maze_cells[i][j].contents = NULL;
memset(maze_cells[i][j].walls, 0, 4*sizeof(Wall *));
}

if (maze_desc[i][j] & MAZE_WALL_HORZ)
{
if (j == MAZE_ARRAY-1)
{
printf("MAZE_WALL_HORZ at right edge\n");
return FALSE;
}

maze[nwalls].f = fmaze_pts[i*MAZE_ARRAY+j];
maze[nwalls].t = fmaze_pts[i*MAZE_ARRAY+j+1];
maze[nwalls].col = (i+j+1) & 1;
maze[nwalls].pTexEnv = &gTexEnv[TEX_WALL];

if (i > 0)
{
maze_cells[i-1][j].can_see |= MAZE_WALL_DOWN;
maze_cells[i-1][j].walls[WIDX_DOWN] = &maze[nwalls];
}
if (i < MAZE_ARRAY-1)
{
maze_cells[i][j].can_see |= MAZE_WALL_UP;
maze_cells[i][j].walls[WIDX_UP] = &maze[nwalls];
}

nwalls++;
}

if (maze_desc[i][j] & MAZE_WALL_VERT)
{
if (i == MAZE_ARRAY-1)
{
printf("MAZE_WALL_VERT at bottom edge\n");
return FALSE;
}

maze[nwalls].f = fmaze_pts[i*MAZE_ARRAY+j];
maze[nwalls].t = fmaze_pts[(i+1)*MAZE_ARRAY+j];
maze[nwalls].col = (i+j) & 1;
maze[nwalls].pTexEnv = &gTexEnv[TEX_WALL];

if (j > 0)
{
maze_cells[i][j-1].can_see |= MAZE_WALL_RIGHT;
maze_cells[i][j-1].walls[WIDX_RIGHT] = &maze[nwalls];
}
if (j < MAZE_ARRAY-1)
{
maze_cells[i][j].can_see |= MAZE_WALL_LEFT;
maze_cells[i][j].walls[WIDX_LEFT] = &maze[nwalls];
}

nwalls++;
}
}
}

// Always place the start on the left and
// the end on the right. This guarantees that there'll be
// some traversing of the maze for the solution
// Since the maze generator guarantees that the entire maze is
// fully connected, the solution can always be found

start_cell->x = 0;
start_cell->y = rand() % MAZE_GRID;

*ngoals = 1;
goals[0].clx = MAZE_GRID-1;
goals[0].cly = rand() % MAZE_GRID;

start_obj.w = FMAZE_CELL_SIZE/6;
start_obj.h = FxFltVal(.166);
start_obj.z = FxFltVal(.5);
start_obj.col = 12;
start_obj.draw_style = DRAW_POLYGON;
start_obj.pTexEnv = &gTexEnv[ TEX_START ];
start_obj.ang = FaDeg(0);
PlaceObject(&start_obj,
CellToMfx(start_cell->x)+FMAZE_CELL_SIZE/2,
CellToMfx(start_cell->y)+FMAZE_CELL_SIZE/2);

end_obj.w = FMAZE_CELL_SIZE/6;
end_obj.h = FxFltVal(.166);
end_obj.z = FxFltVal(.5);
end_obj.col = 10;
end_obj.draw_style = DRAW_POLYGON;
end_obj.pTexEnv = &gTexEnv[ TEX_END ];
end_obj.ang = FaDeg(0);
PlaceObject(&end_obj,
CellToMfx(goals[0].clx)+FMAZE_CELL_SIZE/2,
CellToMfx(goals[0].cly)+FMAZE_CELL_SIZE/2);

// Reset some of the walls' textures to the OpenGL cover
// for some variety
i = (rand() % 5)+1;
while (i-- > 0)
{
j = rand() % nwalls;
maze[j].pTexEnv = &gTexEnv[TEX_COVER];
}

return TRUE;
}

#define PO_WALL 0
#define PO_PARTIAL 1
#define PO_COUNT 2

typedef struct _PaintWall
{
Wall *wall;
} PaintWall;

typedef struct _PaintPartial
{
Object *obj;
} PaintPartial;

typedef struct _PaintObject
{
int type;
union
{
PaintWall wall;
PaintPartial partial;
} u;
FxValue depth;
struct _PaintObject *closer;
} PaintObject;

#define N_PAINT_OBJECTS (4*MAZE_CELLS)
PaintObject paint[N_PAINT_OBJECTS];
int npaint;

void WallCoords(int x, int y, WallFlags flag, FxPt2 *f, FxPt2 *t)
{
t->x = f->x = CellToMfx(x);
t->y = f->y = CellToMfx(y);
if (flag & MAZE_WALL_LEFT)
{
t->y += FMAZE_CELL_SIZE;
}
else if (flag & MAZE_WALL_UP)
{
t->x += FMAZE_CELL_SIZE;
}
else if (flag & MAZE_WALL_RIGHT)
{
f->x += FMAZE_CELL_SIZE;
t->x = f->x;
t->y += FMAZE_CELL_SIZE;
}
else if (flag & MAZE_WALL_DOWN)
{
f->y += FMAZE_CELL_SIZE;
t->y = f->y;
t->x += FMAZE_CELL_SIZE;
}
}

void AddPaintWall(Cell *cell, int widx)
{
PaintWall *pw;

if (npaint == N_PAINT_OBJECTS)
{
printf("Paint list full\n");
return;
}

pw = &paint[npaint].u.wall;
paint[npaint].type = PO_WALL;
npaint++;

pw->wall = cell->walls[widx];
}

void AddPaintWalls(Cell *cell, WallFlags wf)
{
if (wf & MAZE_WALL_LEFT)
{
AddPaintWall(cell, WIDX_LEFT);
}
if (wf & MAZE_WALL_RIGHT)
{
AddPaintWall(cell, WIDX_RIGHT);
}
if (wf & MAZE_WALL_DOWN)
{
AddPaintWall(cell, WIDX_DOWN);
}
if (wf & MAZE_WALL_UP)
{
AddPaintWall(cell, WIDX_UP);
}
}

void AddPaintPartial(Object *obj)
{
PaintPartial *pp;

if (npaint == N_PAINT_OBJECTS)
{
printf("Paint list full\n");
return;
}

pp = &paint[npaint].u.partial;
paint[npaint].type = PO_PARTIAL;
npaint++;

pp->obj = obj;
}

void AddCell(int x, int y, WallFlags wf)
{
Cell *cell;
Object *obj;

wf |= MAZE_CONTENTS;
cell = CellAt(x, y);
if ((cell->unseen & wf) == 0)
{
return;
}

AddPaintWalls(cell, (WallFlags)(wf & cell->unseen));

if (cell->unseen & MAZE_CONTENTS)
{
for (obj = cell->contents; obj; obj = obj->next)
{
AddPaintPartial(obj);
}
}

cell->unseen &= ~wf;
}

void TraceCells(FxPt2 *ip, FxVec2 *dp, WallHit *hit)
{
int cx, cy;
int sgnx, sgny;
FxVec2 dg, dst;
FxPt2 fp, g;
WallFlags xwf, ywf, iwf, xpf, ypf;
FxValue sx, sy;

cx = MfxToCell(ip->x);
cy = MfxToCell(ip->y);

fp = *ip;

#ifdef TRACEDEB
printf("pt %ld,%ld dp %ld,%ld\n", fp.x, fp.y, dp.x, dp.y);
#endif

if (dp->x < 0)
{
g.x = CellToMfx(cx)-FX_MIN_VALUE;
dg.x = -FMAZE_CELL_SIZE;
sgnx = -1;
xwf = MAZE_WALL_LEFT;
xpf = MAZE_WALL_LEFT_PARTIAL;
}
else
{
g.x = CellToMfx(cx+1);
dg.x = FMAZE_CELL_SIZE;
sgnx = 1;
xwf = MAZE_WALL_RIGHT;
xpf = MAZE_WALL_RIGHT_PARTIAL;
if (dp->x == 0)
{
xwf |= MAZE_WALL_LEFT;
xpf |= MAZE_WALL_LEFT_PARTIAL;
}
}
if (dp->y < 0)
{
g.y = CellToMfx(cy)-FX_MIN_VALUE;
dg.y = -FMAZE_CELL_SIZE;
sgny = -1;
ywf = MAZE_WALL_UP;
ypf = MAZE_WALL_UP_PARTIAL;
}
else
{
g.y = CellToMfx(cy+1);
dg.y = FMAZE_CELL_SIZE;
sgny = 1;
ywf = MAZE_WALL_DOWN;
ypf = MAZE_WALL_DOWN_PARTIAL;
if (dp->y == 0)
{
ywf |= MAZE_WALL_UP;
ypf |= MAZE_WALL_UP_PARTIAL;
}
}

for (;;)
{
AddCell(cx, cy, (WallFlags)(xwf | ywf));

dst.x = (g.x-fp.x)*sgnx;
dst.y = (g.y-fp.y)*sgny;
sx = FxMul(dst.x, dp->y);
if (sx < 0)
{
sx = -sx;
}
sy = FxMul(dst.y, dp->x);
if (sy < 0)
{
sy = -sy;
}

#ifdef TRACEDEB
printf("dx %ld, sx %ld, dy %ld, sy %ld\n", dst.x, sx, dst.y, sy);
#endif

if (sx <= sy)
{
if ((maze_cells[cy][cx].can_see & xwf) &&
(maze_cells[cy][cx].can_see & xpf) == 0)
{
iwf = xwf;
break;
}

fp.x = g.x;
fp.y += FxDiv(sx, dp->x)*sgnx*sgny;
if (fp.y == g.y)
{
if ((maze_cells[cy][cx].can_see & ywf) &&
(maze_cells[cy][cx].can_see & ypf) == 0)
{
iwf = ywf;
break;
}
cy += sgny;
g.y += dg.y;
}
cx += sgnx;
g.x += dg.x;
}
else
{
if ((maze_cells[cy][cx].can_see & ywf) &&
(maze_cells[cy][cx].can_see & ypf) == 0)
{
iwf = ywf;
break;
}

fp.y = g.y;
fp.x += FxDiv(sy, dp->y)*sgnx*sgny;
if (fp.x == g.x)
{
if ((maze_cells[cy][cx].can_see & xwf) &&
(maze_cells[cy][cx].can_see & xpf) == 0)
{
iwf = xwf;
break;
}
cx += sgnx;
g.x += dg.x;
}
cy += sgny;
g.y += dg.y;
}
}
hit->cell = CellAt(cx, cy);
hit->cx = cx;
hit->cy = cy;
hit->flag = iwf;
}

void TraceView(MazeView *vw)
{
FaAngle acc;
FxVec2 vcc;
WallHit hit;
int rc;

acc = FaAdd(vw->ang, FaDeg(VIEW_ANG)/2);

for (rc = 0; rc < VIEW_ANG; rc++)
{
vcc.x = FaCos(acc);
vcc.y = FaSin(acc);

TraceCells(&vw->pos, &vcc, &hit);

acc = FaAdd(acc, -FaDeg(1));
}
}

static void WallCompute(PaintObject *po, MazeView *vw,
FxValue cs, FxValue sn)
{
FxPt2 mid;
Wall *wall;

wall = po->u.wall.wall;

// Compute depth at midpoint of wall
// Eye coordinate depth increases along the X so
// we only need to transform it

mid.x = (wall->f.x+wall->t.x)/2-vw->pos.x;
mid.y = (wall->f.y+wall->t.y)/2-vw->pos.y;

po->depth = FxMul(mid.x, cs)+FxMul(mid.y, sn);
}

static void PartialCompute(PaintObject *po, MazeView *vw,
FxValue cs, FxValue sn)
{
PaintPartial *pp;
FxPt2 c;

pp = &po->u.partial;

// Compute depth at center of partial

c.x = pp->obj->p.x-vw->pos.x;
c.y = pp->obj->p.y-vw->pos.y;

po->depth = FxMul(c.x, cs)+FxMul(c.y, sn);
}

typedef void (*PoComputeFn)(PaintObject *po, MazeView *vw,
FxValue cs, FxValue sn);
static PoComputeFn PoCompute[PO_COUNT] =
{
WallCompute,
PartialCompute
};

static float colors[17][3] =
{
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f,
0.0f, 0.5f, 0.5f,
0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.5f,
0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f,
0.75f, 0.75f, 0.75f,
0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f,
0.75f, 0.39f, 0.0f
};

#define WALL_SET 0
#define FLOOR_SET 1
#define CEILING_SET 2

static float *smooth_sets[3][2][4] =
{
&colors[1][0], &colors[2][0], &colors[4][0], &colors[7][0],
&colors[2][0], &colors[1][0], &colors[7][0], &colors[4][0],
&colors[10][0], &colors[2][0], &colors[4][0], &colors[6][0],
&colors[10][0], &colors[2][0], &colors[4][0], &colors[6][0],
&colors[9][0], &colors[1][0], &colors[2][0], &colors[3][0],
&colors[9][0], &colors[1][0], &colors[2][0], &colors[3][0]
};

static float *flat_sets[3][2][4] =
{
&colors[8][0], &colors[8][0], &colors[8][0], &colors[8][0],
&colors[15][0], &colors[15][0], &colors[15][0], &colors[15][0],
&colors[2][0], &colors[2][0], &colors[2][0], &colors[2][0],
&colors[2][0], &colors[2][0], &colors[2][0], &colors[2][0],
&colors[9][0], &colors[9][0], &colors[9][0], &colors[9][0],
&colors[9][0], &colors[9][0], &colors[9][0], &colors[9][0]
};

void SetAlphaCol(GLfloat *fv3)
{
if (maze_options.all_alpha)
{
GLfloat fv4[4];

fv4[0] = fv3[0];
fv4[1] = fv3[1];
fv4[2] = fv3[2];
fv4[3] = 0.5f;
glColor4fv(fv4);
}
else
{
glColor3fv(fv3);
}
}

static void WallDraw(PaintObject *po, MazeView *vw)
{
Wall *wall;
float fx, fy, tx, ty, cx, cy, nx, ny;
float **col_set;
int reps;
int rept;
GLenum old_env;

wall = po->u.wall.wall;
reps = wall->pTexEnv->texRep.x;
rept = wall->pTexEnv->texRep.y;

fx = (float)FxFlt(wall->f.x);
fy = (float)FxFlt(wall->f.y);
tx = (float)FxFlt(wall->t.x);
ty = (float)FxFlt(wall->t.y);
nx = -(ty-fy);
ny = (tx-fx);
cx = (float)FxFlt(vw->pos.x);
cy = (float)FxFlt(vw->pos.y);

col_set = &flat_sets[WALL_SET][wall->col][0];
switch(maze_options.render[WALLS])
{
case RENDER_NONE:
return;
case RENDER_SMOOTH:
col_set = &smooth_sets[WALL_SET][wall->col][0];
break;
case RENDER_FLAT:
case RENDER_TEXTURED:
break;
}

// Compute dot product with wall normal to determine
// wall direction. We need to know the wall direction
// in order to ensure that the wall texture faces the
// correct direction
UseTextureEnv(wall->pTexEnv);

if (wall->pTexEnv->bTransp)
{
if (!maze_options.all_alpha)
{
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &old_env);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, gTexEnvMode);
glEnable(GL_BLEND);
}
}

glBegin(GL_POLYGON);
if ((fx-cx)*nx+(fy-cy)*ny > 0)
{
glTexCoord2d(0, 0);
SetAlphaCol(col_set[0]);
glVertex3f(fx, fy, 0.0f);
glTexCoord2d(reps, 0);
SetAlphaCol(col_set[1]);
glVertex3f(tx, ty, 0.0f);
glTexCoord2d(reps, rept);
SetAlphaCol(col_set[2]);
glVertex3f(tx, ty, maze_height);
glTexCoord2d(0, rept);
SetAlphaCol(col_set[3]);
glVertex3f(fx, fy, maze_height);
}
else
{
glTexCoord2d(reps, 0);
SetAlphaCol(col_set[0]);
glVertex3f(fx, fy, 0.0f);
glTexCoord2d(0, 0);
SetAlphaCol(col_set[1]);
glVertex3f(tx, ty, 0.0f);
glTexCoord2d(0, rept);
SetAlphaCol(col_set[2]);
glVertex3f(tx, ty, maze_height);
glTexCoord2d(reps, rept);
SetAlphaCol(col_set[3]);
glVertex3f(fx, fy, maze_height);
}
glEnd();

if (wall->pTexEnv->bTransp)
{
if (!maze_options.all_alpha)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, old_env);
glDisable(GL_BLEND);
}
}
}

void (APIENTRY *convex_solids[SPECIAL_ARG_COUNT])(GLdouble radius) =
{
auxSolidIcosahedron,
auxSolidOctahedron,
auxSolidDodecahedron,
auxSolidTetrahedron
};

static void PartialDraw(PaintObject *po, MazeView *vw)
{
PaintPartial *pp;
float w, h, cx, cy, cz, vx, vy, fx, fy, fz, tx, ty, tz;
float cs, sn;
GLenum old_env;

pp = &po->u.partial;

w = (float)FxFlt(pp->obj->w);
h = (float)FxFlt(pp->obj->h);

// Partials are billboarded so we want it to always be
// perpendicular to the view direction

cs = (float)FxFlt(FaCos(vw->ang));
sn = (float)FxFlt(FaSin(vw->ang));
vx = -sn*w;
vy = cs*w;

cx = (float)FxFlt(pp->obj->p.x);
cy = (float)FxFlt(pp->obj->p.y);
cz = (float)FxFlt(pp->obj->z);

fx = cx-vx;
fy = cy-vy;
fz = (cz-h)*maze_height;
tx = cx+vx;
ty = cy+vy;
tz = (cz+h)*maze_height;

if (maze_options.render[WALLS] == RENDER_TEXTURED)
{
glDisable(GL_TEXTURE_2D);
}

switch(pp->obj->draw_style)
{
case DRAW_POLYGON:
glEnable(GL_TEXTURE_2D);
if (!maze_options.all_alpha)
{
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &old_env);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, gTexEnvMode);
glEnable(GL_BLEND);
}
UseTextureEnv( pp->obj->pTexEnv );
SetAlphaCol(colors[15]);
glBegin(GL_POLYGON);
glNormal3f(cs, sn, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(fx, fy, fz);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(tx, ty, fz);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(tx, ty, tz);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(fx, fy, tz);
glEnd();
glDisable(GL_TEXTURE_2D);
if (!maze_options.all_alpha)
{
glDisable(GL_BLEND);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, old_env);
}
break;

case DRAW_SPECIAL:
SetAlphaCol(colors[pp->obj->col]);

glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glEnable(GL_LIGHTING);
glEnable(GL_CULL_FACE);
glEnable(GL_DITHER);
glPushMatrix();

glTranslated(cx, cy, cz*maze_height);
glScaled(1.0, 1.0, maze_height);
glRotated(FaFltDegVal(pp->obj->ang), 0, 0, 1);
glRotated(pp->obj->user3, 0, 1, 0);
// Must use convex objects since depth testing can be off
convex_solids[pp->obj->draw_arg](w);

glPopMatrix();
if( !maze_options.bDither )
glDisable(GL_DITHER);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glDisable(GL_AUTO_NORMAL);
glDisable(GL_NORMALIZE);
break;
}

if (maze_options.render[WALLS] == RENDER_TEXTURED)
{
glEnable(GL_TEXTURE_2D);
}
}

typedef void (*PoDrawFn)(PaintObject *po, MazeView *vw);
static PoDrawFn PoDraw[PO_COUNT] =
{
WallDraw,
PartialDraw
};


void RenderZPlane(int render, TEX_ENV *pTexEnv, int set, float zval)
{
float **col_set;
int reps = pTexEnv->texRep.x;
int rept = pTexEnv->texRep.y;

switch(render)
{
case RENDER_NONE:
break;
case RENDER_TEXTURED:
UseTextureEnv(pTexEnv);
glEnable(GL_TEXTURE_2D);
// Fall through
case RENDER_FLAT:
case RENDER_SMOOTH:
col_set = &flat_sets[set][0][0];
if (render == RENDER_SMOOTH)
{
col_set = &smooth_sets[set][0][0];
}

glBegin(GL_POLYGON);

// Switch texture orientation dependent on surface type
if( set == CEILING_SET ) {
glTexCoord2f((float)reps*MAZE_SIZE, 0.0f);
glColor3fv(col_set[0]);
glVertex3f(0.0f, 0.0f, zval);
glTexCoord2f(0.0f, 0.0f);
glColor3fv(col_set[1]);
glVertex3f((float)MAZE_SIZE, 0.0f, zval);
glTexCoord2f(0.0f, (float)rept*MAZE_SIZE);
glColor3fv(col_set[2]);
glVertex3f((float)MAZE_SIZE, (float)MAZE_SIZE, zval);
glTexCoord2f((float)reps*MAZE_SIZE, (float)rept*MAZE_SIZE);
glColor3fv(col_set[3]);
glVertex3f(0.0f, (float)MAZE_SIZE, zval);
} else {
glTexCoord2f(0.0f, 0.0f);
glColor3fv(col_set[0]);
glVertex3f(0.0f, 0.0f, zval);
glTexCoord2f((float)reps*MAZE_SIZE, 0.0f);
glColor3fv(col_set[1]);
glVertex3f((float)MAZE_SIZE, 0.0f, zval);
glTexCoord2f((float)reps*MAZE_SIZE, (float)rept*MAZE_SIZE);
glColor3fv(col_set[2]);
glVertex3f((float)MAZE_SIZE, (float)MAZE_SIZE, zval);
glTexCoord2f(0.0f, (float)rept*MAZE_SIZE);
glColor3fv(col_set[3]);
glVertex3f(0.0f, (float)MAZE_SIZE, zval);
}

glEnd();

if (render == RENDER_TEXTURED)
{
glDisable(GL_TEXTURE_2D);
}
break;
}
}

void Render(MazeView *vw)
{
FxValue cs, sn;
PaintObject *sorted, *so, *pso;
PaintObject *po;
int i;
FxPt2 at;
BOOL special;
float viewHeight;

cs = FaCos(vw->ang);
sn = FaSin(vw->ang);

at.x = vw->pos.x+cs;
at.y = vw->pos.y+sn;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glRotated(view_rot, 0, 0, 1);
gluPerspective(VIEW_ANG, 1, .01, 100);
viewHeight = 0.5f;
gluLookAt(FxFlt(vw->pos.x), FxFlt(vw->pos.y), viewHeight,
FxFlt(at.x), FxFlt(at.y), viewHeight,
0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

RenderZPlane(maze_options.render[FLOOR], &gTexEnv[TEX_FLOOR], FLOOR_SET, 0.0f);
RenderZPlane(maze_options.render[CEILING], &gTexEnv[TEX_CEILING], CEILING_SET, 1.0f);

sorted = NULL;
special = FALSE;
for (i = 0, po = paint; i < npaint; i++, po++)
{
if (po->type == PO_PARTIAL &&
po->u.partial.obj->draw_style == DRAW_SPECIAL)
{
special = TRUE;
}

PoCompute[po->type](po, vw, cs, sn);

for (so = sorted, pso = NULL; so; pso = so, so = so->closer)
{
if (so->depth <= po->depth)
{
break;
}
}
if (pso == NULL)
{
sorted = po;
}
else
{
pso->closer = po;
}
po->closer = so;
}

if (maze_options.render[WALLS] == RENDER_TEXTURED)
{
glEnable(GL_TEXTURE_2D);
}

for (so = sorted; so; so = so->closer)
{
PoDraw[so->type](so, vw);
}

if (maze_options.render[WALLS] == RENDER_TEXTURED)
{
glDisable(GL_TEXTURE_2D);
}
}

void InitPaint(void)
{
int i, j;

npaint = 0;
for (i = 0; i < MAZE_GRID; i++)
{
for (j = 0; j < MAZE_GRID; j++)
{
maze_cells[i][j].unseen = maze_cells[i][j].can_see | MAZE_CONTENTS;
}
}
}

void DrawMaze(MazeView *vw)
{
InitPaint();
TraceView(vw);
Render(vw);
}

void DrawMazeWalls(void)
{
int w;
Wall *wall;

wall = maze;

glColor3f(1.0f, 1.0f, 1.0f);

glBegin(GL_LINES);
for (w = 0; w < nwalls; w++)
{
glVertex2f((float)FxFltVal(wall->f.x), (float)FxFltVal(wall->f.y));
glVertex2f((float)FxFltVal(wall->t.x), (float)FxFltVal(wall->t.y));
wall++;
}
glEnd();
}

#define SQRT2_2 0.707107f

void DrawTopView(MazeView *vw)
{
int c;
Cell *cell;
Object *obj;
float vx, vy, cx, cy, width, ang;
extern float gfAspect;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D( -MAZE_SIZE/2.0, MAZE_SIZE/2.0,
-MAZE_SIZE/2.0/gfAspect, MAZE_SIZE/2.0/gfAspect );
glMatrixMode(GL_MODELVIEW);

glPushMatrix();

ang = (float)FaFltDegVal(vw->ang)+90.0f;
glRotatef(ang, 0.0f, 0.0f, 1.0f);
vx = (float)FxFltVal(vw->pos.x);
vy = (float)FxFltVal(vw->pos.y);
glTranslatef(-vx, -vy, 0.0f);

#define AA_LINES 1
#ifdef AA_LINES
// Turn on antialiased lines
glEnable( GL_BLEND );
glEnable( GL_LINE_SMOOTH );
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
#endif

glCallList(maze_walls_list);

#ifdef AA_LINES
glDisable( GL_BLEND );
glDisable( GL_LINE_SMOOTH );
#endif

// Objects aren't put in the walls display list so that they
// can move around

cell = &maze_cells[0][0];
for (c = 0; c < MAZE_CELLS; c++)
{
for (obj = cell->contents; obj != NULL; obj = obj->next)
{
cx = (float)FxFltVal(obj->p.x);
cy = (float)FxFltVal(obj->p.y);
width = (float)FxFltVal(obj->w);

glColor3fv(colors[obj->col]);

glPushMatrix();
glTranslatef(cx, cy, 0.0f);
glRotated(FaFltDegVal(obj->ang), 0, 0, 1);
glBegin(GL_POLYGON);
glVertex2f(width, 0.0f);
glVertex2f(-width*SQRT2_2, width*0.5f);
glVertex2f(-width*SQRT2_2, -width*0.5f);
glEnd();
glPopMatrix();
}

cell++;
}

glPopMatrix();

// Draw self
glColor3f(0.0f, 0.0f, 1.0f);
width = MAZE_CELL_SIZE/4.0f;
glBegin(GL_POLYGON);
glVertex2f(0.0f, width);
glVertex2f(width*0.5f, -width*SQRT2_2);
glVertex2f(-width*0.5f, -width*SQRT2_2);
glEnd();
}