summaryrefslogtreecommitdiff
path: root/src/fov.c
blob: d7c674633e3310c349090732ff4928371e8fc1d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <daw/utils/fov.h>
#include <daw/utils.h>
#include <math.h>
#include <stdint.h>

void fov_shadowcast_rec(const void* map, const ivec2 mapsize,
                        bool (*visblocking)(const void*), i32* lightmap,
                        const i32 range, ivec2 src, const i32 row, f32 start,
                        const f32 end, const i8 xx, const i8 xy, const i8 yx,
                        const i8 yy) {

  if (start < end) return;

  const i32 range_2 = range * range;
  f32 new_start = start;

  for (i32 i = row; i <= range; i++) {
    i32 dx = (-1 * i) - 1;
    i32 dy = -1 * i;

    bool blocked = false;

    while (dx <= 0) {
      dx += 1;

      const i32 mapx = src[0] + dx * xx + dy * xy;
      const i32 mapy = src[1] + dx * yx + dy * yy;

      const f32 slope_l = (((f32)dx) - 0.5f) / (((f32)dy) + 0.5f);
      const f32 slope_r = (((f32)dx) + 0.5f) / (((f32)dy) - 0.5f);

      if (start < slope_r) continue;
      if (end > slope_l) break;

      if (dx * dx + dy * dy < range_2) {
        /* set as visible */
        if (mapx >= 0 && mapx < (long)mapsize[0] && mapy >= 0 &&
            mapy < (long)mapsize[1]) {
          // TODO: Calculate proper dist from source
          i32 x_2 = (src[0] - mapx) * (src[0] - mapx);
          i32 y_2 = (src[1] - mapy) * (src[1] - mapy);
          lightmap[mapy * mapsize[0] + mapx] =
              MAX(lightmap[mapy * mapsize[0] + mapx],
                  (i32)(range - sqrt((f64)(x_2 + y_2))));
        }
      }

      /* sizeof(i32) is the size of enums */
      /* -- unless the compiler doesn't follow standard behaviour */
      const bool is_blocked = visblocking(
          (void*)((u64)map
                 + sizeof(i32)                     /* ~ enum size */
                 * (usize)(mapsize[0] * mapy + mapx) /* index */
                  ));

      if (blocked) {
        if (!is_blocked) {
          new_start = slope_r;
        } else {
          blocked = false;
          start = new_start;
        }
      } else if (!is_blocked && i < range) {
        blocked = true;
        fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src,
                           i + 1, start, slope_l, xx, xy, yx, yy);
        new_start = slope_r;
      }
    }

    if (blocked) break;
  }
}

/* http://www.roguebasin.com/index.php?title=FOV_using_recursive_shadowcasting
 */
void fov_shadowcast(const void* map, const ivec2 mapsize,
                    bool (*visblocking)(const void*), i32* lightmap,
                    const i32 range, const ivec2 src) {

  const i8 m[4][8] = {
      {1, 0, 0, -1, -1, 0, 0, 1},
      {0, 1, -1, 0, 0, -1, 1, 0},
      {0, 1, 1, 0, 0, -1, -1, 0},
      {1, 0, 0, 1, -1, 0, 0, -1},
  };

  for (i32 oct = 0; oct < 8; oct++) {
    fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src, 1, 1.0,
                       0.0, m[0][oct], m[1][oct], m[2][oct], m[3][oct]);
  }

  /* The center is the most lit square */
  lightmap[src[1] * mapsize[0] + src[0]] = range;
}