From 0ddf2cdcb915211928f9045791cbba249e96f858 Mon Sep 17 00:00:00 2001 From: Robert Vollmert Date: Sat, 31 Oct 2009 21:38:14 +0100 Subject: 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. --- crawl-ref/source/beam.cc | 2 +- crawl-ref/source/ray.cc | 157 +++++++++++++++++++++++++++++++++++++++++++---- crawl-ref/source/ray.h | 2 + 3 files changed, 148 insertions(+), 13 deletions(-) (limited to 'crawl-ref') diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index d8ab5d18c8..173d4140cc 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -1636,7 +1636,7 @@ void bolt::bounce() bounce_pos = ray.pos(); reflect_grid rg; for (adjacent_iterator ai(ray.pos()); ai; ++ai) - rg(*ai) = feat_is_solid(grd(ray.pos())); + rg(*ai - ray.pos() + rg_o) = feat_is_solid(grd(*ai)); ray.bounce(rg); range_used += 2; } 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(floor(d)); } +static int iround(double d) +{ + return static_cast(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() diff --git a/crawl-ref/source/ray.h b/crawl-ref/source/ray.h index 6748090348..92373eddea 100644 --- a/crawl-ref/source/ray.h +++ b/crawl-ref/source/ray.h @@ -10,6 +10,7 @@ #include "geom2d.h" typedef FixedArray reflect_grid; +const coord_def rg_o = coord_def(1,1); struct ray_def { @@ -23,6 +24,7 @@ struct ray_def coord_def pos() const; bool advance(); + coord_def move_to_side(); void bounce(const reflect_grid &rg); void regress(); }; -- cgit v1.2.3-54-g00ecf