summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/ray.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-11-03 20:48:31 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-11-03 21:19:22 +0100
commit85ea7c827f4933d11527f5172748206654bafe9b (patch)
treeb955e5fb3584f75b275da5ee7c322f5c3db2cf76 /crawl-ref/source/ray.cc
parent88d00c26d2909136d0cb1df8d58e2b449ca91908 (diff)
downloadcrawl-ref-85ea7c827f4933d11527f5172748206654bafe9b.tar.gz
crawl-ref-85ea7c827f4933d11527f5172748206654bafe9b.zip
Split up ray_def::bounce and implement better corner handling.
Rays shot directly at diagonal walls should now reflect appropriately. For this, diagonal walls are assumed to be flat. Example: ...### @***## ...*.# ...*.. It's different from before, but I think it's probably a nice change without huge impact.
Diffstat (limited to 'crawl-ref/source/ray.cc')
-rw-r--r--crawl-ref/source/ray.cc202
1 files changed, 144 insertions, 58 deletions
diff --git a/crawl-ref/source/ray.cc b/crawl-ref/source/ray.cc
index 83b25d091c..536a6f3fd1 100644
--- a/crawl-ref/source/ray.cc
+++ b/crawl-ref/source/ray.cc
@@ -274,56 +274,43 @@ geom::vector _fudge_corner(const geom::vector &w, const reflect_grid &rg)
return (v);
}
-void ray_def::bounce(const reflect_grid &rg)
+// Bounce a ray leaving (0,0) through the positive side
+// along a diagonal corridor between (0,1) and (1,0) until
+// it's inside (1,1).
+geom::ray _bounce_diag_corridor(const geom::ray &rorig)
{
- ASSERT(_valid());
- ASSERT(!rg(rg_o)); // The cell we bounce from is not solid.
-#ifdef DEBUG
- const coord_def old_pos = pos();
-#endif
-
- // Translate to cell (0,0).
- geom::vector p(pos().x, pos().y);
- geom::ray rtrans;
- rtrans.start = r.start - p;
- rtrans.dir = r.dir;
-
- if (on_corner)
+ geom::ray r = rorig;
+ geom::form wall(1, -1);
+ // The actual walls: geom::line l1(1, -1, 0.5), l2(1, -1, -0.5);
+ geom::line k(1, 1, 2.5);
+ ASSERT(k.f(r.dir) > 0); // We're actually moving towards k.
+ ASSERT(!geom::parallel(r.dir, geom::form(1, -1)));
+ // Now bounce back and forth between l1 and l2 until we hit k.
+ while (!double_is_zero(geom::intersect(r, k)))
{
- // Move a little bit towards cell center (0.5, 0.5).
- rtrans.start = 0.9 * rtrans.start + 0.1 * geom::vector(0.5, 0.5);
- on_corner = false;
- ASSERT(in_diamond_int(rtrans.start));
+ r.to_grid(diamonds, false);
+ r.dir = reflect(r.dir, wall);
}
+ // Now pointing inside the destination cell (1,1) -- move inside.
+ r.to_grid(diamonds, true);
+ return (r);
+}
- // Move to the diamond edge to determine the side.
- coord_def side;
- bool corner = 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);
- double d2 = diamonds.ls2.index(rtrans.start);
- if (double_is_integral(d2))
- side += iround(d2) ? coord_def(1,-1) : coord_def(-1,1);
- ASSERT(corner == (side.x == 0 || side.y == 0));
-
- // In the corner case, we have side == (+-2, 0) or (0, +-2); reduce:
- if (corner)
- {
- side.x = side.x / 2;
- side.y = side.y / 2;
- }
-
- // Mirror rtrans to have it leave through the positive side.
- geom::ray rmirr = _mirror(rtrans, side);
-
- // TODO: on a corner, reflect back unless we're on a diagonal.
+// Bounce a ray leaving (0,0) properly through one of the sides
+// of the diamond.
+// r is positioned on the edge already, and side says which
+// side this is.
+geom::ray _bounce_noncorner(const geom::ray &r, const coord_def &side,
+ const reflect_grid &rg)
+{
+ // Mirror r to have it leave through the positive side.
+ geom::ray rmirr = _mirror(r, 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 = (side.x != 0) && rg(rg_o + dx);
- bool ry = (side.y != 0) && rg(rg_o + dy);
+ 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);
@@ -331,20 +318,7 @@ void ray_def::bounce(const reflect_grid &rg)
// Now go through the cases separately.
if (rx && ry && !rxy)
{
- // Tricky case: diagonal corridor.
- geom::form wall(1, -1);
- // The actual walls: geom::line l1(1, -1, 0.5), l2(1, -1, -0.5);
- geom::line k(1, 1, 2.5);
- ASSERT(k.f(rmirr.dir) > 0); // We're actually moving towards k.
- ASSERT(!geom::parallel(rmirr.dir, geom::form(1, -1)));
- // Now bounce back and forth between l1 and l2 until we hit k.
- while (!double_is_zero(geom::intersect(rmirr, k)))
- {
- rmirr.to_grid(diamonds, false);
- rmirr.dir = reflect(rmirr.dir, wall);
- }
- // Now pointing inside the destination cell (1,1) -- move inside.
- rmirr.to_grid(diamonds, true);
+ rmirr = _bounce_diag_corridor(rmirr);
}
else
{
@@ -370,16 +344,128 @@ void ray_def::bounce(const reflect_grid &rg)
// the next one.
rmirr.to_grid(diamonds, true);
if (in_non_diamond_int(rmirr.start))
- on_corner = _advance_from_non_diamond(&rmirr);
+ _advance_from_non_diamond(&rmirr);
}
}
// Mirror back.
- rtrans = _mirror(rmirr, side);
+ return (_mirror(rmirr, side));
+}
+
+geom::form _corner_wall(const coord_def &side, const reflect_grid &rg)
+{
+ coord_def e;
+ if (side.x == 0)
+ e = coord_def(1, 0);
+ else
+ e = coord_def(0, 1);
+ ASSERT(!rg(rg_o) && rg(rg_o+side));
+ // Reflect back by an orthogonal wall...
+ coord_def wall = e;
+ // unless the wall is clearly diagonal:
+ // ##.
+ // #*. (with side.y == -1)
+ if (rg(rg_o+e) && rg(rg_o+side+e) && !rg(rg_o-e) && !rg(rg_o+side-e))
+ {
+ // diagonal wall through side and e
+ wall = side - e;
+ }
+ else if (rg(rg_o-e) && rg(rg_o+side-e) && !rg(rg_o+e) && !rg(rg_o+side+e))
+ {
+ // diagonal wall through side and -e
+ wall = side + e;
+ }
+ return (geom::form(wall.y, -wall.x));
+}
+
+// Bounce a ray that leaves cell (0,0) through a corner. We could
+// just fudge it a little, but want to be consistent for rays
+// shot in cardinal directions.
+geom::ray _bounce_corner(const geom::ray &rorig, const coord_def &side,
+ const reflect_grid &rg)
+{
+ geom::ray r = rorig;
+ geom::form f = _corner_wall(side, rg);
+
+ if (r.dir.x == 0 || r.dir.y == 0)
+ {
+ // To keep cardinal rays nicely in the middle,
+ // we reflect them earlier.
+ r.start.x = r.start.y = 0.5;
+ r.dir = geom::reflect(r.dir, f);
+ ASSERT(r.dir.x == 0 || r.dir.y == 0);
+ }
+ else
+ {
+ // Others are reflected at the corner.
+ r.dir = geom::reflect(r.dir, f);
+ if (f.a != 0 && f.b != 0)
+ {
+ // Diagonal wall: to the next diamond, then inside.
+ r.to_grid(diamonds, false);
+ r.to_grid(diamonds, true);
+ }
+ else
+ {
+ // Back inside diamond (0,0).
+ r.to_grid(diamonds, true);
+ }
+ }
+ return (r);
+}
+
+void ray_def::bounce(const reflect_grid &rg)
+{
+ ASSERT(_valid());
+ ASSERT(!rg(rg_o)); // The cell we bounce from is not solid.
+#ifdef DEBUG
+ const coord_def old_pos = pos();
+#endif
+
+ // Translate to cell (0,0).
+ geom::vector p(pos().x, pos().y);
+ geom::ray rtrans;
+ rtrans.start = r.start - p;
+ rtrans.dir = r.dir;
+
+ if (on_corner)
+ {
+ // Move a little bit towards cell center (0.5, 0.5).
+ rtrans.start = 0.9 * rtrans.start + 0.1 * geom::vector(0.5, 0.5);
+ on_corner = false;
+ ASSERT(in_diamond_int(rtrans.start));
+ }
+
+ // Move to the diamond edge to determine the side.
+ coord_def side;
+ bool corner = 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);
+ double d2 = diamonds.ls2.index(rtrans.start);
+ if (double_is_integral(d2))
+ side += iround(d2) ? coord_def(1,-1) : coord_def(-1,1);
+ ASSERT(corner == (side.x == 0 || side.y == 0));
+
+ // In the corner case, we have side == (+-2, 0) or (0, +-2); reduce:
+ if (corner)
+ {
+ side.x = side.x / 2;
+ side.y = side.y / 2;
+ }
+
+ if (corner)
+ rtrans = _bounce_corner(rtrans, side, rg);
+ else
+ rtrans = _bounce_noncorner(rtrans, side, rg);
+
// Translate back.
r.start = rtrans.start + p;
r.dir = rtrans.dir;
+ // Set on_corner if we happen to have ended up on a corner.
+ on_corner = is_corner(r.start);
+
ASSERT(_valid());
ASSERT(!rg(pos() - old_pos + rg_o));
}