From f545cf0276e95c9dca33d36d1a0cfe3b4995473a Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Wed, 23 May 2018 21:28:29 -0400 Subject: somethign --- vendor/fov.c | 427 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/fov.h | 245 ++++++++++++++++++++++++++++++++++ 2 files changed, 672 insertions(+) create mode 100644 vendor/fov.c create mode 100644 vendor/fov.h (limited to 'vendor') diff --git a/vendor/fov.c b/vendor/fov.c new file mode 100644 index 0000000..74ed5c8 --- /dev/null +++ b/vendor/fov.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2006, Greg McIntyre + * All rights reserved. See the file named COPYING in the distribution + * for more details. + */ + +#include +#include +#include +#define __USE_ISOC99 1 +#include +#include +#include +#include "fov.h" + +/* ++---++---++---++---+ +| || || || | +| || || || | +| || || || | ++---++---++---++---+ 2 ++---++---++---+##### +| || || |##### +| || || |##### +| || || |##### ++---++---++---+#####X 1 <-- y ++---++---++---++---+ +| || || || | +| @ || || || | <-- srcy centre -> dy = 0.5 = y - 0.5 +| || || || | ++---++---++---++---+ 0 +0 1 2 3 4 + ^ ^ + | | + srcx x -> dx = 3.5 = x + 0.5 +centre + +Slope from @ to X. + ++---++---++---++---+ +| || || || | +| || || || | +| || || || | ++---++---++---++---+ 2 ++---++---++---++---+ +| || || || | +| || || || | +| || || || | ++---++---++---+X---+ 1 <-- y ++---++---++---+##### +| || || |##### +| @ || || |##### <-- srcy centre -> dy = 0.5 = y - 0.5 +| || || |##### ++---++---++---+##### 0 +0 1 2 3 + ^ ^ + | | + srcx x -> dx = 2.5 = x - 0.5 +centre + +Slope from @ to X +*/ + + +/* Types ---------------------------------------------------------- */ + +/** \cond INTERNAL */ +typedef struct { + /*@observer@*/ fov_settings_type *settings; + /*@observer@*/ void *map; + /*@observer@*/ void *source; + int source_x; + int source_y; + unsigned radius; +} fov_private_data_type; +/** \endcond */ + +/* Options -------------------------------------------------------- */ + +void fov_settings_init(fov_settings_type *settings) { + settings->shape = FOV_SHAPE_CIRCLE_PRECALCULATE; + settings->corner_peek = FOV_CORNER_NOPEEK; + settings->opaque_apply = FOV_OPAQUE_APPLY; + settings->opaque = NULL; + settings->apply = NULL; + settings->heights = NULL; + settings->numheights = 0; +} + +void fov_settings_set_shape(fov_settings_type *settings, + fov_shape_type value) { + settings->shape = value; +} + +void fov_settings_set_corner_peek(fov_settings_type *settings, + fov_corner_peek_type value) { + settings->corner_peek = value; +} + +void fov_settings_set_opaque_apply(fov_settings_type *settings, + fov_opaque_apply_type value) { + settings->opaque_apply = value; +} + +void fov_settings_set_opacity_test_function(fov_settings_type *settings, + bool (*f)(void *map, + int x, int y)) { + settings->opaque = f; +} + +void fov_settings_set_apply_lighting_function(fov_settings_type *settings, + void (*f)(void *map, + int x, int y, + int dx, int dy, + void *src)) { + settings->apply = f; +} + +/* Circular FOV --------------------------------------------------- */ + +/*@null@*/ static unsigned *precalculate_heights(unsigned maxdist) { + unsigned i; + unsigned *result = (unsigned *)malloc((maxdist+2)*sizeof(unsigned)); + if (result) { + for (i = 0; i <= maxdist; ++i) { + result[i] = (unsigned)sqrtf((float)(maxdist*maxdist - i*i)); + } + result[maxdist+1] = 0; + } + return result; +} + +static unsigned height(fov_settings_type *settings, int x, + unsigned maxdist) { + unsigned **newheights; + + if (maxdist > settings->numheights) { + newheights = (unsigned **)calloc((size_t)maxdist, sizeof(unsigned*)); + if (newheights != NULL) { + if (settings->heights != NULL && settings->numheights > 0) { + /* Copy the pointers to the heights arrays we've already + * calculated. Once copied out, we can free the old + * array of pointers. */ + memcpy(newheights, settings->heights, + settings->numheights*sizeof(unsigned*)); + free(settings->heights); + } + settings->heights = newheights; + settings->numheights = maxdist; + } + } + if (settings->heights) { + if (settings->heights[maxdist-1] == NULL) { + settings->heights[maxdist-1] = precalculate_heights(maxdist); + } + if (settings->heights[maxdist-1] != NULL) { + return settings->heights[maxdist-1][abs(x)]; + } + } + return 0; +} + +void fov_settings_free(fov_settings_type *settings) { + unsigned i; + if (settings != NULL) { + if (settings->heights != NULL && settings->numheights > 0) { + /*@+forloopexec@*/ + for (i = 0; i < settings->numheights; ++i) { + unsigned *h = settings->heights[i]; + if (h != NULL) { + free(h); + } + settings->heights[i] = NULL; + } + /*@=forloopexec@*/ + free(settings->heights); + settings->heights = NULL; + settings->numheights = 0; + } + } +} + +/* Slope ---------------------------------------------------------- */ + +static float fov_slope(float dx, float dy) { + if (dx <= -FLT_EPSILON || dx >= FLT_EPSILON) { + return dy/dx; + } else { + return 0.0; + } +} + +/* Octants -------------------------------------------------------- */ + +#define FOV_DEFINE_OCTANT(signx, signy, rx, ry, nx, ny, nf, apply_edge, apply_diag) \ + static void fov_octant_##nx##ny##nf( \ + fov_private_data_type *data, \ + int dx, \ + float start_slope, \ + float end_slope) { \ + int x, y, dy, dy0, dy1; \ + unsigned h; \ + int prev_blocked = -1; \ + float end_slope_next; \ + fov_settings_type *settings = data->settings; \ + \ + if (dx == 0) { \ + fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope); \ + return; \ + } else if ((unsigned)dx > data->radius) { \ + return; \ + } \ + \ + dy0 = (int)(0.5f + ((float)dx)*start_slope); \ + dy1 = (int)(0.5f + ((float)dx)*end_slope); \ + \ + rx = data->source_##rx signx dx; \ + ry = data->source_##ry signy dy0; \ + \ + if (!apply_diag && dy1 == dx) { \ + /* We do diagonal lines on every second octant, so they don't get done twice. */ \ + --dy1; \ + } \ + \ + switch (settings->shape) { \ + case FOV_SHAPE_CIRCLE_PRECALCULATE: \ + h = height(settings, dx, data->radius); \ + break; \ + case FOV_SHAPE_CIRCLE: \ + h = (unsigned)sqrtf((float)(data->radius*data->radius - dx*dx)); \ + break; \ + case FOV_SHAPE_OCTAGON: \ + h = (data->radius - dx)<<1; \ + break; \ + default: \ + h = data->radius; \ + break; \ + }; \ + if ((unsigned)dy1 > h) { \ + if (h == 0) { \ + return; \ + } \ + dy1 = (int)h; \ + } \ + \ + /*fprintf(stderr, "(%2d) = [%2d .. %2d] (%f .. %f), h=%d,edge=%d\n", \ + dx, dy0, dy1, ((float)dx)*start_slope, \ + 0.5f + ((float)dx)*end_slope, h, apply_edge);*/ \ + \ + for (dy = dy0; dy <= dy1; ++dy) { \ + ry = data->source_##ry signy dy; \ + \ + if (settings->opaque(data->map, x, y)) { \ + if (settings->opaque_apply == FOV_OPAQUE_APPLY && (apply_edge || dy > 0)) { \ + settings->apply(data->map, x, y, x - data->source_x, y - data->source_y, data->source); \ + } \ + if (prev_blocked == 0) { \ + end_slope_next = fov_slope((float)dx + 0.5f, (float)dy - 0.5f); \ + fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope_next); \ + } \ + prev_blocked = 1; \ + } else { \ + if (apply_edge || dy > 0) { \ + settings->apply(data->map, x, y, x - data->source_x, y - data->source_y, data->source); \ + } \ + if (prev_blocked == 1) { \ + start_slope = fov_slope((float)dx - 0.5f, (float)dy - 0.5f); \ + } \ + prev_blocked = 0; \ + } \ + } \ + \ + if (prev_blocked == 0) { \ + fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope); \ + } \ + } + +FOV_DEFINE_OCTANT(+,+,x,y,p,p,n,true,true) +FOV_DEFINE_OCTANT(+,+,y,x,p,p,y,true,false) +FOV_DEFINE_OCTANT(+,-,x,y,p,m,n,false,true) +FOV_DEFINE_OCTANT(+,-,y,x,p,m,y,false,false) +FOV_DEFINE_OCTANT(-,+,x,y,m,p,n,true,true) +FOV_DEFINE_OCTANT(-,+,y,x,m,p,y,true,false) +FOV_DEFINE_OCTANT(-,-,x,y,m,m,n,false,true) +FOV_DEFINE_OCTANT(-,-,y,x,m,m,y,false,false) + + +/* Circle --------------------------------------------------------- */ + +static void _fov_circle(fov_private_data_type *data) { + /* + * Octants are defined by (x,y,r) where: + * x = [p]ositive or [n]egative x increment + * y = [p]ositive or [n]egative y increment + * r = [y]es or [n]o for reflecting on axis x = y + * + * \pmy|ppy/ + * \ | / + * \ | / + * mpn\|/ppn + * ----@---- + * mmn/|\pmn + * / | \ + * / | \ + * /mmy|mpy\ + */ + fov_octant_ppn(data, 1, (float)0.0f, (float)1.0f); + fov_octant_ppy(data, 1, (float)0.0f, (float)1.0f); + fov_octant_pmn(data, 1, (float)0.0f, (float)1.0f); + fov_octant_pmy(data, 1, (float)0.0f, (float)1.0f); + fov_octant_mpn(data, 1, (float)0.0f, (float)1.0f); + fov_octant_mpy(data, 1, (float)0.0f, (float)1.0f); + fov_octant_mmn(data, 1, (float)0.0f, (float)1.0f); + fov_octant_mmy(data, 1, (float)0.0f, (float)1.0f); +} + +void fov_circle(fov_settings_type *settings, + void *map, + void *source, + int source_x, + int source_y, + unsigned radius) { + fov_private_data_type data; + + data.settings = settings; + data.map = map; + data.source = source; + data.source_x = source_x; + data.source_y = source_y; + data.radius = radius; + + _fov_circle(&data); +} + +/** + * Limit x to the range [a, b]. + */ +static float betweenf(float x, float a, float b) { + if (x - a < FLT_EPSILON) { /* x < a */ + return a; + } else if (x - b > FLT_EPSILON) { /* x > b */ + return b; + } else { + return x; + } +} + +#define BEAM_DIRECTION(d, p1, p2, p3, p4, p5, p6, p7, p8) \ + if (direction == d) { \ + end_slope = betweenf(a, 0.0f, 1.0f); \ + fov_octant_##p1(&data, 1, 0.0f, end_slope); \ + fov_octant_##p2(&data, 1, 0.0f, end_slope); \ + if (a - 1.0f > FLT_EPSILON) { /* a > 1.0f */ \ + start_slope = betweenf(2.0f - a, 0.0f, 1.0f); \ + fov_octant_##p3(&data, 1, start_slope, 1.0f); \ + fov_octant_##p4(&data, 1, start_slope, 1.0f); \ + } \ + if (a - 2.0f > FLT_EPSILON) { /* a > 2.0f */ \ + end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); \ + fov_octant_##p5(&data, 1, 0.0f, end_slope); \ + fov_octant_##p6(&data, 1, 0.0f, end_slope); \ + } \ + if (a - 3.0f > FLT_EPSILON) { /* a > 3.0f */ \ + start_slope = betweenf(4.0f - a, 0.0f, 1.0f); \ + fov_octant_##p7(&data, 1, start_slope, 1.0f); \ + fov_octant_##p8(&data, 1, start_slope, 1.0f); \ + } \ + } + +#define BEAM_DIRECTION_DIAG(d, p1, p2, p3, p4, p5, p6, p7, p8) \ + if (direction == d) { \ + start_slope = betweenf(1.0f - a, 0.0f, 1.0f); \ + fov_octant_##p1(&data, 1, start_slope, 1.0f); \ + fov_octant_##p2(&data, 1, start_slope, 1.0f); \ + if (a - 1.0f > FLT_EPSILON) { /* a > 1.0f */ \ + end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); \ + fov_octant_##p3(&data, 1, 0.0f, end_slope); \ + fov_octant_##p4(&data, 1, 0.0f, end_slope); \ + } \ + if (a - 2.0f > FLT_EPSILON) { /* a > 2.0f */ \ + start_slope = betweenf(3.0f - a, 0.0f, 1.0f); \ + fov_octant_##p5(&data, 1, start_slope, 1.0f); \ + fov_octant_##p6(&data, 1, start_slope, 1.0f); \ + } \ + if (a - 3.0f > FLT_EPSILON) { /* a > 3.0f */ \ + end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); \ + fov_octant_##p7(&data, 1, 0.0f, end_slope); \ + fov_octant_##p8(&data, 1, 0.0f, end_slope); \ + } \ + } + +void fov_beam(fov_settings_type *settings, void *map, void *source, + int source_x, int source_y, unsigned radius, + fov_direction_type direction, float angle) { + + fov_private_data_type data; + float start_slope, end_slope, a; + + data.settings = settings; + data.map = map; + data.source = source; + data.source_x = source_x; + data.source_y = source_y; + data.radius = radius; + + if (angle <= 0.0f) { + return; + } else if (angle >= 360.0f) { + _fov_circle(&data); + return; + } + + /* Calculate the angle as a percentage of 45 degrees, halved (for + * each side of the centre of the beam). e.g. angle = 180.0f means + * half the beam is 90.0 which is 2x45, so the result is 2.0. + */ + a = angle/90.0f; + + BEAM_DIRECTION(FOV_EAST, ppn, pmn, ppy, mpy, pmy, mmy, mpn, mmn); + BEAM_DIRECTION(FOV_WEST, mpn, mmn, pmy, mmy, ppy, mpy, ppn, pmn); + BEAM_DIRECTION(FOV_NORTH, mpy, mmy, mmn, pmn, mpn, ppn, pmy, ppy); + BEAM_DIRECTION(FOV_SOUTH, pmy, ppy, mpn, ppn, mmn, pmn, mmy, mpy); + BEAM_DIRECTION_DIAG(FOV_NORTHEAST, pmn, mpy, mmy, ppn, mmn, ppy, mpn, pmy); + BEAM_DIRECTION_DIAG(FOV_NORTHWEST, mmn, mmy, mpn, mpy, pmy, pmn, ppy, ppn); + BEAM_DIRECTION_DIAG(FOV_SOUTHEAST, ppn, ppy, pmy, pmn, mpn, mpy, mmn, mmy); + BEAM_DIRECTION_DIAG(FOV_SOUTHWEST, pmy, mpn, ppy, mmn, ppn, mmy, pmn, mpy); +} diff --git a/vendor/fov.h b/vendor/fov.h new file mode 100644 index 0000000..b27f59c --- /dev/null +++ b/vendor/fov.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2006-2007, Greg McIntyre. All rights reserved. See the file + * named COPYING in the distribution for more details. + */ + +/** + * \mainpage Field of View Library + * + * \section about About + * + * This is a C library which implements a course-grained lighting + * algorithm suitable for tile-based games such as roguelikes. + * + * \section copyright Copyright + * + * \verbinclude COPYING + * + * \section thanks Thanks + * + * Thanks to Björn Bergström + * for the algorithm. + * + */ + +/** + * \file fov.h + * Field-of-view algorithm for dynamically casting light/shadow on a + * low resolution 2D raster. + */ +#ifndef LIBFOV_HEADER +#define LIBFOV_HEADER + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Eight-way directions. */ +typedef enum { + FOV_EAST = 0, + FOV_NORTHEAST, + FOV_NORTH, + FOV_NORTHWEST, + FOV_WEST, + FOV_SOUTHWEST, + FOV_SOUTH, + FOV_SOUTHEAST +} fov_direction_type; + +/** Values for the shape setting. */ +typedef enum { + FOV_SHAPE_CIRCLE_PRECALCULATE, + FOV_SHAPE_SQUARE, + FOV_SHAPE_CIRCLE, + FOV_SHAPE_OCTAGON +} fov_shape_type; + +/** Values for the corner peek setting. */ +typedef enum { + FOV_CORNER_NOPEEK, + FOV_CORNER_PEEK +} fov_corner_peek_type; + +/** Values for the opaque apply setting. */ +typedef enum { + FOV_OPAQUE_APPLY, + FOV_OPAQUE_NOAPPLY +} fov_opaque_apply_type; + +/** @cond INTERNAL */ +typedef /*@null@*/ unsigned *height_array_t; +/** @endcond */ + +typedef struct { + /** Opacity test callback. */ + /*@null@*/ bool (*opaque)(void *map, int x, int y); + + /** Lighting callback to set lighting on a map tile. */ + /*@null@*/ void (*apply)(void *map, int x, int y, int dx, int dy, void *src); + + /** Shape setting. */ + fov_shape_type shape; + + /** Whether to peek around corners. */ + fov_corner_peek_type corner_peek; + + /** Whether to call apply on opaque tiles. */ + fov_opaque_apply_type opaque_apply; + + /** \cond INTERNAL */ + + /** Pre-calculated data. \internal */ + /*@null@*/ height_array_t *heights; + + /** Size of pre-calculated data. \internal */ + unsigned numheights; + + /** \endcond */ +} fov_settings_type; + +/** The opposite direction to that given. */ +#define fov_direction_opposite(direction) ((fov_direction_type)(((direction)+4)&0x7)) + +/** + * Set all the default options. You must call this option when you + * create a new settings data structure. + * + * These settings are the defaults used: + * + * - shape: FOV_SHAPE_CIRCLE_PRECALCULATE + * - corner_peek: FOV_CORNER_NOPEEK + * - opaque_apply: FOV_OPAQUE_APPLY + * + * Callbacks still need to be set up after calling this function. + * + * \param settings Pointer to data structure containing settings. + */ +void fov_settings_init(fov_settings_type *settings); + +/** + * Set the shape of the field of view. + * + * \param settings Pointer to data structure containing settings. + * \param value One of the following values, where R is the radius: + * + * - FOV_SHAPE_CIRCLE_PRECALCULATE \b (default): Limit the FOV to a + * circle with radius R by precalculating, which consumes more memory + * at the rate of 4*(R+2) bytes per R used in calls to fov_circle. + * Each radius is only calculated once so that it can be used again. + * Use fov_free() to free this precalculated data's memory. + * + * - FOV_SHAPE_CIRCLE: Limit the FOV to a circle with radius R by + * calculating on-the-fly. + * + * - FOV_SHAPE_OCTOGON: Limit the FOV to an octogon with maximum radius R. + * + * - FOV_SHAPE_SQUARE: Limit the FOV to an R*R square. + */ +void fov_settings_set_shape(fov_settings_type *settings, fov_shape_type value); + +/** + * NOT YET IMPLEMENTED. + * + * Set whether sources will peek around corners. + * + * \param settings Pointer to data structure containing settings. + * \param value One of the following values: + * + * - FOV_CORNER_PEEK \b (default): Renders: +\verbatim + ........ + ........ + ........ + ..@# + ...# +\endverbatim + * - FOV_CORNER_NOPEEK: Renders: +\verbatim + ...... + ..... + .... + ..@# + ...# +\endverbatim + */ +void fov_settings_set_corner_peek(fov_settings_type *settings, fov_corner_peek_type value); + +/** + * Whether to call the apply callback on opaque tiles. + * + * \param settings Pointer to data structure containing settings. + * \param value One of the following values: + * + * - FOV_OPAQUE_APPLY \b (default): Call apply callback on opaque tiles. + * - FOV_OPAQUE_NOAPPLY: Do not call the apply callback on opaque tiles. + */ +void fov_settings_set_opaque_apply(fov_settings_type *settings, fov_opaque_apply_type value); + +/** + * Set the function used to test whether a map tile is opaque. + * + * \param settings Pointer to data structure containing settings. + * \param f The function called to test whether a map tile is opaque. + */ +void fov_settings_set_opacity_test_function(fov_settings_type *settings, bool (*f)(void *map, int x, int y)); + +/** + * Set the function used to apply lighting to a map tile. + * + * \param settings Pointer to data structure containing settings. + * \param f The function called to apply lighting to a map tile. + */ +void fov_settings_set_apply_lighting_function(fov_settings_type *settings, void (*f)(void *map, int x, int y, int dx, int dy, void *src)); + +/** + * Free any memory that may have been cached in the settings + * structure. + * + * \param settings Pointer to data structure containing settings. + */ +void fov_settings_free(fov_settings_type *settings); + +/** + * Calculate a full circle field of view from a source at (x,y). + * + * \param settings Pointer to data structure containing settings. + * \param map Pointer to map data structure to be passed to callbacks. + * \param source Pointer to data structure holding source of light. + * \param source_x x-axis coordinate from which to start. + * \param source_y y-axis coordinate from which to start. + * \param radius Euclidean distance from (x,y) after which to stop. + */ +void fov_circle(fov_settings_type *settings, void *map, void *source, + int source_x, int source_y, unsigned radius +); + +/** + * Calculate a field of view from source at (x,y), pointing + * in the given direction and with the given angle. The larger + * the angle, the wider, "less focused" the beam. Each side of the + * line pointing in the direction from the source will be half the + * angle given such that the angle specified will be represented on + * the raster. + * + * \param settings Pointer to data structure containing settings. + * \param map Pointer to map data structure to be passed to callbacks. + * \param source Pointer to data structure holding source of light. + * \param source_x x-axis coordinate from which to start. + * \param source_y y-axis coordinate from which to start. + * \param radius Euclidean distance from (x,y) after which to stop. + * \param direction One of eight directions the beam of light can point. + * \param angle The angle at the base of the beam of light, in degrees. + */ +void fov_beam(fov_settings_type *settings, void *map, void *source, + int source_x, int source_y, unsigned radius, + fov_direction_type direction, float angle +); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif -- cgit 1.4.1