visibility determination
[vrlugburz] / src / player.c
index ab7d818..61a5989 100644 (file)
@@ -1,5 +1,7 @@
 #include "player.h"
 
+static const int step[][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
+
 void init_player(struct player *p)
 {
        memset(p, 0, sizeof *p);
@@ -15,7 +17,7 @@ void init_player(struct player *p)
 
 void update_player_dir(struct player *p)
 {
-       int dir;
+       int prev_dir = p->dir;
        float angle;
 
        /* TODO: take vrot into account */
@@ -24,13 +26,15 @@ void update_player_dir(struct player *p)
 
        p->theta = angle;       /* renormalize theta */
        p->dir = (int)(4.0f * angle / TWO_PI + 0.5) & 3;
+
+       if(p->dir != prev_dir) {
+               p->vis = 0;     /* invalidate visibility list */
+       }
 }
 
 void move_player(struct player *p, int right, int fwd)
 {
-       static const int step[][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        int fdir, rdir;
-       float angle;
 
        update_player_dir(p);
 
@@ -38,6 +42,8 @@ void move_player(struct player *p, int right, int fwd)
        rdir = (fdir + 1) & 3;
        p->cx += step[fdir][0] * fwd + step[rdir][0] * right;
        p->cy += step[fdir][1] * fwd + step[rdir][1] * right;
+
+       p->vis = 0;     /* invalidate visibility list */
 }
 
 void turn_player(struct player *p, int turn)
@@ -48,6 +54,8 @@ void turn_player(struct player *p, int turn)
 
        update_player_dir(p);
        p->theta = (float)p->dir * HALF_PI;     /* snap theta */
+
+       p->vis = 0;     /* invalidate visibility list */
 }
 
 void upd_player_xform(struct player *p)
@@ -65,9 +73,67 @@ void upd_player_xform(struct player *p)
        cgm_mpretranslate(p->view_xform, -pos.x, -pos.y, -pos.z);
 }
 
+static void vis_visit(struct player *p, int cx, int cy, int *cvis)
+{
+       int i, j, nx, ny, dx, dy;
+       struct level *lvl = p->lvl;
+       struct cell *cell;
+
+       if(cx < 0 || cx >= lvl->width || cy < 0 || cy >= lvl->height) {
+               return;
+       }
+       cell = lvl->cells + cy * lvl->width + cx;
+
+       /* stop when we encounter a solid cell */
+       if(cell->type == CELL_SOLID) {
+               return;
+       }
+
+       dx = cx - p->cx;
+       dy = cy - p->cy;
+       /* stop beyond the maximum visibility distance (manhattan) */
+       if(abs(dx) > lvl->visdist || abs(dy) > lvl->visdist) {
+               return;
+       }
+
+       /* dot product */
+       if(step[p->dir][0] * dx + step[p->dir][1] * dy < 0) {
+               return; /* cell is behind the player */
+       }
+
+       cvis[cy * lvl->width + cx] = 1;         /* mark as visited before recursing */
+
+       /* visit neighboring nodes before adding current cell */
+       for(i=0; i<3; i++) {
+               ny = cy - 1 + i;
+               if(ny < 0) continue;
+               if(ny >= lvl->height) break;
+
+               for(j=0; j<3; j++) {
+                       if(i == 1 && j == 1) continue;
+                       nx = cx - 1 + j;
+                       if(nx < 0) continue;
+                       if(nx >= lvl->width) break;
+
+                       if(!cvis[ny * lvl->width + nx]) {
+                               vis_visit(p, nx, ny, cvis);
+                       }
+               }
+       }
+
+       /* then add this cell to the visible list */
+       cell->next = p->vis;
+       p->vis = cell;
+}
+
 void upd_player_vis(struct player *p)
 {
-       p->vis = 0;
+       int *cvis;
+       struct level *lvl = p->lvl;
 
+       cvis = alloca(lvl->width * lvl->height * sizeof *cvis);
+       memset(cvis, 0, lvl->width * lvl->height * sizeof *cvis);
 
+       p->vis = 0;
+       vis_visit(p, p->cx, p->cy, cvis);
 }