From 85ea7c827f4933d11527f5172748206654bafe9b Mon Sep 17 00:00:00 2001 From: Robert Vollmert Date: Tue, 3 Nov 2009 20:48:31 +0100 Subject: 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. --- crawl-ref/source/ray.cc | 202 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 58 deletions(-) (limited to 'crawl-ref/source/ray.cc') 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)); } -- cgit v1.2.3-54-g00ecf