/** * 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 data structure containing settings. */ public void fov_settings_init(fov_settings_type settings) { settings.shape = fov_shape_type.FOV_SHAPE_CIRCLE_PRECALCULATE; settings.corner_peek = fov_corner_peek_type.FOV_CORNER_NOPEEK; settings.opaque_apply = fov_opaque_apply_type.FOV_OPAQUE_APPLY; settings.m_OnOpaqueEventHandler = null; settings.m_OnApplyEventHandler = null; settings.heights.Clear(); settings.numheights = 0; }
/** * Calculate a full circle field of view from a source at (x,y). * * \param settings data structure containing settings. * \param map map data structure to be passed to callbacks. * \param source 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. */ public void fov_circle(fov_settings_type settings, map _map, Color source, int source_x, int source_y, uint radius) { fov_private_data_type data = new fov_private_data_type(); 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); }
private static uint height(fov_settings_type settings, int x, uint maxdist) { if (maxdist > settings.numheights) { settings.numheights = maxdist; } if (settings.heights != null) { if (!settings.heights.ContainsKey(maxdist - 1)) { settings.heights.Add(maxdist - 1, precalculate_heights(maxdist)); } if (settings.heights.ContainsKey(maxdist - 1)) { List <uint> val = null; settings.heights.TryGetValue(maxdist - 1, out val); return(val[Mathf.Abs(x)]); } } return(0); }
/* Octants -------------------------------------------------------- */ private static void fov_octant( fov_private_data_type data, int dx, float start_slope, float end_slope, int signx, int signy, char rx, char ry, bool apply_edge, bool apply_diag) { int x = 0; int y = 0; int dy, dy0, dy1; uint h = 0; int prev_blocked = -1; float end_slope_next; fov_settings_type settings = data.settings; if (dx == 0) { fov_octant(data, dx + 1, start_slope, end_slope, signx, signy, rx, ry, apply_edge, apply_diag); return; } else if ((uint)dx > data.radius) { return; } dy0 = (int)(0.5f + ((float)dx) * start_slope); dy1 = (int)(0.5f + ((float)dx) * end_slope); if (rx == 'x') { x = data.source_x + signx * dx; } else if (rx == 'y') { y = data.source_y + signx * dx; } if (ry == 'y') { y = data.source_y + signy * dy0; } else if (ry == 'x') { x = data.source_x + 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_type.FOV_SHAPE_CIRCLE_PRECALCULATE: h = height(settings, dx, data.radius); break; case fov_shape_type.FOV_SHAPE_CIRCLE: h = (uint)Mathf.Sqrt((float)(data.radius * data.radius - dx * dx)); break; case fov_shape_type.FOV_SHAPE_OCTAGON: h = (uint)(data.radius - dx) << 1; break; default: h = data.radius; break; } ; if ((uint)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) { if (ry == 'y') { y = data.source_y + signy * dy; } else if (ry == 'x') { x = data.source_x + signy * dy; } if (settings.m_OnOpaqueEventHandler(data._map, x, y)) { if (settings.opaque_apply == fov_opaque_apply_type.FOV_OPAQUE_APPLY && (apply_edge || dy > 0)) { settings.m_OnApplyEventHandler(data._map, x, y, dx, dy, data.source); } if (prev_blocked == 0) { end_slope_next = fov_slope((float)dx + 0.5f, (float)dy - 0.5f); fov_octant(data, dx + 1, start_slope, end_slope_next, signx, signy, rx, ry, apply_edge, apply_diag); } prev_blocked = 1; } else { if (apply_edge || dy > 0) { settings.m_OnApplyEventHandler(data._map, x, y, dx, dy, 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(data, dx + 1, start_slope, end_slope, signx, signy, rx, ry, apply_edge, apply_diag); } }
/** * 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 data structure containing settings. * \param map map data structure to be passed to callbacks. * \param source 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. */ public void fov_beam(fov_settings_type settings, map _map, Color source, int source_x, int source_y, uint radius, fov_direction_type direction, float angle) { fov_private_data_type data = new fov_private_data_type(); 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); } /* 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; if (direction == fov_direction_type.FOV_EAST) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_WEST) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_NORTH) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_SOUTH) { end_slope = betweenf(a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ start_slope = betweenf(2.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ start_slope = betweenf(4.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } } if (direction == fov_direction_type.FOV_NORTHEAST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_NORTHWEST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_SOUTHEAST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, 0.0f, end_slope); } } if (direction == fov_direction_type.FOV_SOUTHWEST) { start_slope = betweenf(1.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMY, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPN, data, 1, start_slope, 1.0f); if (a - 1.0f > Mathf.Epsilon) { /* a > 1.0f */ end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPY, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMN, data, 1, 0.0f, end_slope); } if (a - 2.0f > Mathf.Epsilon) { /* a > 2.0f */ start_slope = betweenf(3.0f - a, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PPN, data, 1, start_slope, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_MMY, data, 1, start_slope, 1.0f); } if (a - 3.0f > Mathf.Epsilon) { /* a > 3.0f */ end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); _fov_octant_part(fov_octants_part.FOV_OCTANT_PMN, data, 1, 0.0f, end_slope); _fov_octant_part(fov_octants_part.FOV_OCTANT_MPY, data, 1, 0.0f, end_slope); } } }
/** * Free any memory that may have been cached in the settings * structure. * * \param settings data structure containing settings. */ public void fov_settings_free(fov_settings_type settings) { settings.heights.Clear(); settings.numheights = 0; }
/** * Set the function used to apply lighting to a map tile. * * \param settings data structure containing settings. * \param f The function called to apply lighting to a map tile. */ public void fov_settings_set_apply_lighting_function(fov_settings_type settings, OnApplyEventHandler eventHandler) { settings.m_OnApplyEventHandler = eventHandler; }
/** * Set the function used to test whether a map tile is opaque. * * \param settings data structure containing settings. * \param f The function called to test whether a map tile is opaque. */ public void fov_settings_set_opacity_test_function(fov_settings_type settings, OnOpaqueEventHandler eventHandler) { settings.m_OnOpaqueEventHandler = eventHandler; }
/** * Whether to call the apply callback on opaque tiles. * * \param settings 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. */ public void fov_settings_set_opaque_apply(fov_settings_type settings, fov_opaque_apply_type value) { settings.opaque_apply = value; }
/** * <em>NOT YET IMPLEMENTED</em>. * * Set whether sources will peek around corners. * * \param settings 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 */ public void fov_settings_set_corner_peek(fov_settings_type settings, fov_corner_peek_type value) { settings.corner_peek = value; }
/** * Set the shape of the field of view. * * \param settings 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. */ public void fov_settings_set_shape(fov_settings_type settings, fov_shape_type value) { settings.shape = value; }