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(); 
}