#include "map.h" #include "maths.h" #include int map_size(int w, int h) { return sizeof(Map) + w * h * sizeof(Map_Tile) + w * h * sizeof(Map_Light_Tile); } Map_Tile* map_tiles(Map* m) { return (Map_Tile*)&m[1]; } const Map_Tile* map_tilesc(const Map* m) { return (Map_Tile*)&m[1]; } Map_Light_Tile* map_light_tiles(Map* m) { return (Map_Light_Tile*)&map_tiles(m)[m->w * m->h]; } const Map_Light_Tile* map_light_tilesc(const Map* m) { return (Map_Light_Tile*)&map_tilesc(m)[m->w * m->h]; } typedef struct { Line a, b; } Ray; void init_ray(Ray* r, const int* f, const int* t) { int fa[2], ta[2]; int fb[2], tb[2]; fa[0] = f[0]; fa[1] = f[1]; ta[0] = t[0]; ta[1] = t[1]; fb[0] = f[0]; fb[1] = f[2]; tb[0] = t[0]; tb[1] = t[2]; init_line(&r->a, fa, ta); init_line(&r->b, fb, tb); } void step_ray(Ray* r) { step_line(&r->a); while (r->a.x != r->b.x) step_line(&r->b); } void get_ray(const Ray* r, int* d) { d[0] = r->a.x; d[1] = r->a.y; d[2] = r->b.y; } int trace_ray( Map* m, const int* f, const int* t ) { Ray r; int pos[3], w[3]; init_ray(&r, f, t); while (1) { int mp[2], coord; get_ray(&r, pos); mp[0] = pos[0] >> fbits; mp[1] = pos[1] >> fbits; if (mp[0] < 0) goto end; if (mp[1] < 0) goto end; if (mp[0] >= m->w) goto end; if (mp[1] >= m->h) goto end; if (pos[2] < 0) { /* hit the floor */ goto end; } if (pos[2] >= f1) { /* hit the ceiling */ goto end; } coord = mp[0] + mp[1] * m->w; if (map_tilesc(m)[coord]) { goto end; } step_ray(&r); } end: return vec_dist(w, pos, t, 3) < 2; } void init_tile(Map* m, int tx, int ty, Map_Light_Tile* t) { int i, x, y; Map_Fragment full = { { 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff }, { { 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff }, }}; (void)m; (void)tx; (void)ty; for (i = y = 0; y < map_light_tile_size; y++) { for (x = 0; x < map_light_tile_size; x++, i++) { t->fragments[i] = full; } } } Colour bake_pixel( Map* m, Colour src, const int* fr, const Map_Light* light, int tx, int ty, int x, int y, int side ) { int to[3]; switch (side) { case 0: /* ceiling */ to[0] = (tx + x * 32); to[1] = (ty + y * 32); to[2] = f1; break; case 1: /* floor */ to[0] = (tx + x * 32); to[1] = (ty + y * 32); to[2] = 0; break; case 2: to[0] = ty + f1; to[1] = tx + f1 - x * 32; to[2] = f1 - (y * 32); break; case 3: to[0] = ty + x * 32; to[1] = tx + f1; to[2] = f1 - (y * 32); break; case 4: to[0] = tx + f1; to[1] = ty + f1 - x * 32; to[2] = f1 - (y * 32); break; case 5: to[0] = tx + x * 32; to[1] = ty + f1; to[2] = f1 - (y * 32); break; default: return make_white(); } if (trace_ray(m, fr, to)) { int w[3]; int atten = vec_dist(w, fr, to, 3); atten = mini( (f1 << fbits) / maxi(1, (atten * atten) >> fbits), f1 ) - 1; atten = (atten * light->brightness) >> fbits; return col_add(src, col_scl( light->c, (unsigned char)(atten / 2) )); return make_white(); } return src; } void bake_tile( Map* m, int tx, int ty, Map_Light_Tile* t, const Map_Light* light ) { int i, x, y, fr[3]; tx <<= fbits; ty <<= fbits; fr[0] = light->x; fr[1] = light->y; fr[2] = light->z; for (i = y = 0; y < map_light_tile_size; y++) { for (x = 0; x < map_light_tile_size; x++, i++) { Map_Fragment* f = &t->fragments[i]; #define bp(t, s) \ f->t = bake_pixel(m, f->t, fr, light, tx, ty, x, y, s); bp(u, 0); bp(d, 1); bp(side[0], 2); bp(side[1], 3); bp(side[2], 4); bp(side[3], 5); } } } void bake_light(Map* m, const Map_Light* light) { int x, y, i; Map_Light_Tile* lt = map_light_tiles(m); for (i = y = 0; y < m->h; y++) { for (x = 0; x < m->w; x++, i++) { bake_tile(m, x, y, <[i], light); } } } void bake_map(Map* m) { Map_Light l = { f1 * 2 + 100, f1 * 2, f1/4, f1, { 0xff, 0xff, 0xff, 0xff } }; bake_light(m, &l); } const Map_Fragment* sample_map_light( const Map* m, int tx, int ty, int u, int v ) { int coord; const Map_Light_Tile* t = &map_light_tilesc(m)[tx + ty * m->w]; int x = (u * map_light_tile_size) >> fbits; int y = (v * map_light_tile_size) >> fbits; x &= map_light_tile_size - 1; y &= map_light_tile_size - 1; coord = x + y * map_light_tile_size; return &t->fragments[coord]; }