#include <string.h>
#include <math.h>
+/*! \brief Transverse-step delta-y for each octant
+ */
int trn_dy[8] = {
0, -1, 1, 0, 0, 1, -1, 0
};
+/*! \brief Transverse-step delta-x for each octant
+ */
int trn_dx[8] = {
1, 0, 0, 1, -1, 0, 0, -1
};
+/*! \brief Radial-step delta-y for each octant
+ */
int rdl_dy[8] = {
-1, 0, 0, 1, 1, 0, 0, -1
};
+/*! \brief Radial-step delta-x for each octant
+ */
int rdl_dx[8] = {
0, 1, 1, 0, 0, -1, -1, 0
};
static void compute_row(Radiance *rad, int octant, int radius, double inmost_slope, double outmost_slope);
+/*! \brief Default blocking function
+ *
+ * This function returns true if the specified coordinates are out of bounds
+ * or the terrain at the specified position is opaque.
+ */
static bool dflt_blk(int y, int x)
{
return ((y <= 0) || (x <= 0) || (y >= DUN_HEIGHT - 1) || (x >= DUN_WIDTH - 1) || (terrain_is_opaque(terrain[y][x])));
}
+/*! \brief Function for marking affected cells as explored
+ *
+ * This function marks the specified tile as explored, triggers a symbol
+ * update, and returns true.
+ */
static bool mark_explored(int y, int x, void *pvt)
{
mapflags[y][x] |= MAPFLAG_EXPLORED;
return true;
}
-/*! \fn
- * \brief Return the inner (cardinal-wards) blocking limit of an octant-relative coordinate
+/*! \brief Return the inner (cardinal-wards) blocking limit of an octant-relative coordinate
*/
static inline double inner_slope(int rdl, int trn)
{
return trn ? ((trn - 0.5) / ((double) rdl)) : 0.0;
}
+/*! \brief Return the innermost (cardinal-wards) visible slope of an octant-relative coordinate
+ */
static inline double inner_visible_slope(int rdl, int trn)
{
return trn ? ((trn - 0.5) / (rdl + 0.5)) : 0.0;
}
-/*! \fn
- * \brief Return the outer (diagonal-wards) blocking limit of an octant-relative coordinate
+
+/*! \brief Return the outer (diagonal-wards) blocking limit of an octant-relative coordinate
*/
+
static inline double outer_slope(int rdl, int trn)
{
return (rdl == trn) ? 1.0 : ((trn + 0.5) / (double) rdl);
}
+/*! \brief Return the outermost (diagonal-wards) visible slope of an octant-relative coordinate
+ */
+
static inline double outer_visible_slope(int rdl, int trn)
{
return (rdl == trn) ? 1.0 : ((trn + 0.5) / (rdl - 0.5));
}
+/*! \brief Reset the affected flags of a Radiance object
+ */
void clear_radiance(Radiance *rad)
{
memset(&(rad->affected), '\0', sizeof rad->affected);
}
+/*! \brief Start computing one octant of a Radiance
+ */
static inline void compute_octant(Radiance *rad, int octant)
{
compute_row(rad, octant, 1, 0.0, 1.0);
}
+/*! \brief Compute one (partial) row of a Radiance and recurse.
+ */
static void compute_row(Radiance *rad, int octant, int radius, double inmost_slope, double outmost_slope)
{
int trn;
int outer_idx;
int inner_idx;
double isl;
- double ivs;
- double ovs;
+
+ /*
+ * We should never get the slopes mismatched like this, but if we do,
+ * return immediately.
+ */
if (inmost_slope >= outmost_slope)
{
return;
}
+ /*
+ * The inner_idx and outer_idx values are used to control which tiles of
+ * the current row we actually examine.
+ */
inner_idx = 0.5 + (inmost_slope * radius);
outer_idx = 0.5 + (outmost_slope * radius);
+ /*
+ * dx and dy - the position of the "cursor" relative to the centre of
+ * the potentially affected area - are calculated using the rdl_* and trn_*
+ * arrays.
+ */
dx = radius * rdl_dx[octant] + outer_idx * trn_dx[octant];
dy = radius * rdl_dy[octant] + outer_idx * trn_dy[octant];
+ /*
+ * Iterate over the range of transverse deflections from outer_idx to
+ * inner_idx.
+ */
for (trn = outer_idx; trn >= inner_idx; --trn)
{
+ /* Get the inner "grazing" slope of the diamond inside the tile. */
isl = inner_slope(radius, trn);
+ /* Mark the tile being examined as affected. */
rad->affected[MAX_FOV_RADIUS + dy][MAX_FOV_RADIUS + dx] = true;
if (block_flag)
{
+ /* If the previous tile was opaque... */
if (rad->opaque_fun(dy + rad->centre_y, dx + rad->centre_x))
{
+ /* ... then if this one is too, update the slope */
if (outmost_slope > isl)
{
outmost_slope = isl;
}
else
{
+ /* ... and if not, clear the blocking flag. */
block_flag = false;
}
}
else
{
+ /* Otherwise... */
if (rad->opaque_fun(dy + rad->centre_y, dx + rad->centre_x))
{
+ /* ... if the current tile is opaque, set the blocking flag. */
block_flag = true;
+ /*
+ * and if we're not examining the first tile of this strip,
+ * and we're not examining the last row of the octant,
+ * recursively examine the diagonalwards portion of the
+ * next row.
+ */
if ((trn < outer_idx) && (radius < rad->radius))
{
compute_row(rad, octant, radius + 1, outer_slope(radius, trn), outmost_slope);
}
+ /*
+ * Update the outermost slope to reflect what is occluded by
+ * this cell.
+ */
if (outmost_slope > isl)
{
outmost_slope = isl;
}
}
}
+ /*
+ * Advance whichever of dx or dy is the transverse coordinate for this
+ * octant.
+ */
dx -= trn_dx[octant];
dy -= trn_dy[octant];
}
if ((radius < rad->radius) && !block_flag)
{
+ /*
+ * If we haven't reached the last row, and the last tile we examined
+ * in this row wasn't opaque, recursively compute the next row.
+ */
compute_row(rad, octant, radius + 1, inmost_slope, outmost_slope);
}
}
void compute_radiance(Radiance *rad)
{
int oct;
+ /* Compute the eight octants in order. */
for (oct = 0; oct < 8; ++oct)
{
compute_octant(rad, oct);
}
+ /* Mark the centre as (un)affected according to the Radiance's setup. */
if (rad->exclude_centre)
{
rad->affected[MAX_FOV_RADIUS][MAX_FOV_RADIUS] = false;
}
else
{
- rad->affected[MAX_FOV_RADIUS][MAX_FOV_RADIUS] = false;
+ rad->affected[MAX_FOV_RADIUS][MAX_FOV_RADIUS] = true;
}
}
int x;
int i;
int j;
+
switch (rad->order)
{
case Reo_ascending:
compute_radiance(&player_fov);
resolve_radiance(&player_fov);
}
+
/* fov.c */
// vim:cindent