summaryrefslogtreecommitdiff
path: root/src/fov.c
blob: cdc7dc86135376c899a88f056ce18fe7bd4bab62 (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
96
97
#include <stdint.h>
#include <math.h>
#include <engine/fov.h>
#include <engine/utils.h>

void fov_shadowcast_rec(const void *map, const v2_i32 mapsize,
                        bool (*visblocking)(const void*), i32 *lightmap,
                        const i32 range, v2_i32 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.x + dx * xx + dy * xy;
			const i32 mapy = src.y + 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.x
				&&  mapy >= 0 && mapy < (long)mapsize.y) {
					// TODO: Calculate proper dist from source
					f32 x_2 = (src.x - mapx) * (src.x - mapx);
					f32 y_2 = (src.y - mapy) * (src.y - mapy);
					lightmap[mapy*mapsize.x + mapx] = MAX(lightmap[mapy*mapsize.x + mapx], range - sqrt((f32)(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 */
				       * (mapsize.x * 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 v2_i32 mapsize,
                    bool (*visblocking)(const void*), i32 *lightmap,
                    const i32 range, const v2_i32 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.y * mapsize.x + src.x] = range;
}