summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/ray.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-10-31 21:38:14 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-11-01 21:45:26 +0100
commit0ddf2cdcb915211928f9045791cbba249e96f858 (patch)
treebbe7e9c9fd4ac68f72c0f340f8058b134feafdd1 /crawl-ref/source/ray.cc
parentbe58c68878c08c4d1373c2f38c9ef11d66010d06 (diff)
downloadcrawl-ref-0ddf2cdcb915211928f9045791cbba249e96f858.tar.gz
crawl-ref-0ddf2cdcb915211928f9045791cbba249e96f858.zip
Implement reflection (except for the diagonal corridor case).
Reflection is modelled on diamond-shaped features, with gaps filled in as far as they don't affect LOS. Working on a case by case analysis: After normalization, a ray leaves cell * through the south-east diamond face. Then determine the line of reflection in each case.
Diffstat (limited to 'crawl-ref/source/ray.cc')
-rw-r--r--crawl-ref/source/ray.cc157
1 files changed, 145 insertions, 12 deletions
diff --git a/crawl-ref/source/ray.cc b/crawl-ref/source/ray.cc
index f93cf12668..64fb706bf3 100644
--- a/crawl-ref/source/ray.cc
+++ b/crawl-ref/source/ray.cc
@@ -27,12 +27,16 @@ static int ifloor(double d)
return static_cast<int>(floor(d));
}
+static int iround(double d)
+{
+ return static_cast<int>(round(d));
+}
+
static bool double_is_integral(double d)
{
return (double_is_zero(d - round(d)));
}
-
// Is v in a diamond?
static bool in_diamond(const geom::vector &v)
{
@@ -65,6 +69,21 @@ coord_def ray_def::pos() const
return (coord_def(x, y));
}
+static bool _advance_from_non_diamond(geom::ray *r)
+{
+ if (!r->to_next_cell(diamonds))
+ {
+ ASSERT(in_diamond_int(r->start));
+ return (false);
+ }
+ else
+ {
+ // r is now on a corner, going from non-diamond to non-diamond.
+ ASSERT(is_corner(r->start));
+ return (true);
+ }
+}
+
// Return true if we didn't hit a corner, hence if this
// is a good ray so far.
bool ray_def::advance()
@@ -92,27 +111,141 @@ bool ray_def::advance()
// Now inside a non-diamond.
ASSERT(!in_diamond(r.start));
- if (!r.to_next_cell(diamonds))
+ on_corner = _advance_from_non_diamond(&r);
+ return (!on_corner);
+}
+
+static geom::ray _mirror(const geom::ray &rorig, const coord_def &side)
+{
+ geom::ray r = rorig;
+ if (side.x == -1)
{
- ASSERT(in_diamond_int(r.start));
- return (true);
+ r.start.x = 1.0 - r.start.x;
+ r.dir.x = -r.dir.x;
}
- else
+ if (side.y == -1)
{
- // r is now on a corner, going from non-diamond to non-diamond.
- ASSERT(is_corner(r.start));
- on_corner = true;
- return (false);
+ r.start.y = 1.0 - r.start.y;
+ r.dir.y = -r.dir.y;
}
+ return (r);
}
void ray_def::bounce(const reflect_grid &rg)
{
+#ifdef DEBUG_REFLECTION
+ mprf(MSGCH_DIAGNOSTICS, "mirroring ray: (%f,%f) + t*(%f,%f)",
+ r.start.x, r.start.y, r.dir.x, r.dir.y);
+#endif
ASSERT(in_diamond(r.start));
- // Find out which side of the diamond r leaves through.
- geom::ray copy = r;
- r.dir = -r.dir;
+ // Translate to cell (0,0).
+ geom::vector p(pos().x, pos().y);
+ geom::ray rtrans;
+ rtrans.start = r.start - p;
+ rtrans.dir = r.dir;
+
+ // Move to the diamond edge to determine the side.
+ coord_def side;
+ rtrans.to_grid(diamonds, false);
+ double d1 = diamonds.ls1.index(rtrans.start);
+ if (double_is_integral(d1))
+ side = (iround(d1) ? coord_def(1,1) : coord_def(-1,-1));
+ else
+ {
+ double d2 = diamonds.ls2.index(rtrans.start);
+ ASSERT(double_is_integral(d2));
+ side = (iround(d2) ? coord_def(1,-1) : coord_def(-1,1));
+ }
+ // TODO: for rays in cardinal directions, this is arbitrary
+ // and can lead to bad behaviour.
+
+ // Mirror rtrans to have it leave through the positive side.
+ geom::ray rmirr = _mirror(rtrans, side);
+
+ // Determine which of the three relevant cells are bouncy.
+ const coord_def dx = coord_def(side.x, 0);
+ const coord_def dy = coord_def(0, side.y);
+ bool rx = rg(rg_o + dx);
+ bool ry = rg(rg_o + dy);
+ bool rxy = rg(rg_o + dx + dy);
+ // One of the three neighbours on this side must be bouncy.
+ ASSERT(rx || ry || rxy);
+
+#ifdef DEBUG_REFLECTION
+ mprf(MSGCH_DIAGNOSTICS, "bouncy cells: rx=%d, ry=%d, rxy=%d", rx, ry, rxy);
+#endif
+
+ // Now go through the cases separately.
+ if (rx && ry && !rxy)
+ {
+ // Tricky case: diagonal corridor.
+ // TODO: Implement this.
+ rmirr.dir = -rmirr.dir;
+ }
+ else
+ {
+ // These all reduce to reflection at one line.
+ geom::line l;
+ if (rxy)
+ {
+ if (rx && ry)
+ {
+ // x + y = 1.5
+ l.f = geom::form(1, 1);
+ l.val = 1.5;
+ }
+ else if (!rx && !ry)
+ {
+ // x + y = 2.5
+ l.f = geom::form(1, 1);
+ l.val = 2.5;
+ }
+ else if (rx)
+ {
+ // x = 1
+ l.f = geom::form(1, 0);
+ l.val = 1;
+ }
+ else if (ry)
+ {
+ // y = 1
+ l.f = geom::form(0, 1);
+ l.val = 1;
+ }
+ }
+ else if (rx)
+ {
+ // y = x - 0.5
+ l.f = geom::form(1, -1);
+ l.val = 0.5;
+ }
+ else // ry
+ {
+ // y = x + 0.5
+ l.f = geom::form(1, -1);
+ l.val = -0.5;
+ }
+ double t = intersect(rmirr, l);
+ ASSERT(double_is_zero(t) || t >= 0);
+ rmirr.advance(t);
+ rmirr.dir = reflect(rmirr.dir, l.f);
+ // Depending on the case, we're on some diamond edge
+ // or between diamonds. We'll just move safely into
+ // the next one.
+ rmirr.to_grid(diamonds, true);
+ if (!in_diamond(rmirr.start))
+ on_corner = _advance_from_non_diamond(&rmirr);
+ }
+ // Mirror back.
+ rtrans = _mirror(rmirr, side);
+ // Translate back.
+ r.start = rtrans.start + p;
+ r.dir = rtrans.dir;
+#ifdef DEBUG_REFLECTION
+ mprf(MSGCH_DIAGNOSTICS, "mirrored ray: (%f,%f) + t*(%f,%f)",
+ r.start.x, r.start.y, r.dir.x, r.dir.y);
+#endif
}
void ray_def::regress()