diff options
author | Robert Vollmert <rvollmert@gmx.net> | 2009-10-31 21:38:14 +0100 |
---|---|---|
committer | Robert Vollmert <rvollmert@gmx.net> | 2009-11-01 21:45:26 +0100 |
commit | 0ddf2cdcb915211928f9045791cbba249e96f858 (patch) | |
tree | bbe7e9c9fd4ac68f72c0f340f8058b134feafdd1 /crawl-ref/source/ray.cc | |
parent | be58c68878c08c4d1373c2f38c9ef11d66010d06 (diff) | |
download | crawl-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.cc | 157 |
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() |