1181 lines
28 KiB
C
1181 lines
28 KiB
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];
|
|
}
|
|
|
|
#if 0
|
|
// Make some of the walls partially covered
|
|
n = (rand() % 50)+1;
|
|
while (n-- > 0)
|
|
{
|
|
Wall *wall;
|
|
int dir;
|
|
|
|
// The wall picked cannot be an edge wall because that
|
|
// would allow walking out of the maze
|
|
i = ((rand() >> 8) % (MAZE_GRID-2))+1;
|
|
j = ((rand() >> 8) % (MAZE_GRID-2))+1;
|
|
dir = (rand() >> 13) % 4;
|
|
wall = maze_cells[i][j].walls[dir];
|
|
if (wall != NULL)
|
|
{
|
|
wall->pTexEnv = &gTexEnv[TEX_END];
|
|
maze_cells[i][j].can_see |= (MAZE_WALL_LEFT_PARTIAL << dir);
|
|
switch(dir)
|
|
{
|
|
case WIDX_LEFT:
|
|
if (j > 0)
|
|
{
|
|
maze_cells[i][j-1].can_see |= MAZE_WALL_RIGHT_PARTIAL;
|
|
}
|
|
break;
|
|
case WIDX_RIGHT:
|
|
if (j < MAZE_GRID-1)
|
|
{
|
|
maze_cells[i][j+1].can_see |= MAZE_WALL_LEFT_PARTIAL;
|
|
}
|
|
break;
|
|
case WIDX_UP:
|
|
if (i > 0)
|
|
{
|
|
maze_cells[i-1][j].can_see |= MAZE_WALL_DOWN_PARTIAL;
|
|
}
|
|
break;
|
|
case WIDX_DOWN:
|
|
if (i < MAZE_GRID-1)
|
|
{
|
|
maze_cells[i+1][j].can_see |= MAZE_WALL_UP_PARTIAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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 0
|
|
// Unnecessary at the moment, but might be handy later
|
|
if (special && !maze_options.depth_test)
|
|
{
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
#endif
|
|
|
|
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();
|
|
//mf: if image being stretched, gfAspect isn't enought to make this straight -
|
|
// need to compensate by using aspect of base dimensions as well.
|
|
//mf? maybe use glScale ?
|
|
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);
|
|
#if 1
|
|
glBegin(GL_POLYGON);
|
|
glVertex2f(width, 0.0f);
|
|
glVertex2f(-width*SQRT2_2, width*0.5f);
|
|
glVertex2f(-width*SQRT2_2, -width*0.5f);
|
|
glEnd();
|
|
#else
|
|
glRectf(-width, -width, width, width);
|
|
#endif
|
|
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();
|
|
}
|