From 4af8ffadd675bf353b05cef8c923d65f71d2a9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20V=C3=A4h=C3=A4m=C3=A4ki?= Date: Wed, 11 May 2011 20:21:13 +0800 Subject: [PATCH] GRIM: Better sector adjacency checking that also works with overlapping sectors. --- engines/grim/actor.cpp | 22 +++++++---- engines/grim/walkplane.cpp | 76 ++++++++++++++++++++++++++++---------- engines/grim/walkplane.h | 2 +- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/engines/grim/actor.cpp b/engines/grim/actor.cpp index 219d37ac302..11c0afee84d 100644 --- a/engines/grim/actor.cpp +++ b/engines/grim/actor.cpp @@ -573,10 +573,22 @@ void Actor::walkTo(const Graphics::Vector3d &p) { } } Graphics::Line3d line; - if (!inClosed && sector->isAdjacentTo(s, &line)) { + Common::List bridges; + if (!inClosed) { + bridges = sector->getBridgesTo(s); + } + while (!bridges.empty()) { + line = bridges.back(); + bridges.pop_back(); PathNode *n = NULL; + Graphics::Vector3d pos = s->getClosestPoint(_destPos); + Graphics::Line3d l(node->pos, pos); + if (!line.intersectLine2d(l, &pos)) { + pos = line.middle(); + } + for (Common::List::iterator j = openList.begin(); j != openList.end(); ++j) { - if ((*j)->sect == s) { + if ((*j)->pos == pos) { n = *j; break; } @@ -591,11 +603,7 @@ void Actor::walkTo(const Graphics::Vector3d &p) { n = new PathNode; n->parent = node; n->sect = s; - n->pos = s->getClosestPoint(_destPos); - Graphics::Line3d l(node->pos, n->pos); - if (!line.intersectLine2d(l, &n->pos)) { - n->pos = line.middle(); - } + n->pos = pos; n->dist = (n->pos - _destPos).magnitude(); n->cost = node->cost + (n->pos - node->pos).magnitude(); openList.push_back(n); diff --git a/engines/grim/walkplane.cpp b/engines/grim/walkplane.cpp index 1c3005721e0..645f6a9c896 100644 --- a/engines/grim/walkplane.cpp +++ b/engines/grim/walkplane.cpp @@ -214,30 +214,68 @@ bool Sector::isPointInSector(Graphics::Vector3d point) const { return true; } -bool Sector::isAdjacentTo(Sector *sector, Graphics::Line3d *line) const { - int vertices[2] = {-1, -1}; - Graphics::Vector3d *sectorVertices = sector->getVertices(); - for (int j = 0; j < _numVertices; ++j) { - Graphics::Vector3d &vect = _vertices[j]; - for (int k = 0; k < sector->getNumVertices(); ++k) { - // We're not using "vect == sectorvertices[k]" here because sometimes - // that isn't true even for edjacent sectors, and breaks a walkTo like - // explained at https://github.com/residual/residual/issues/34 - if ((vect - sectorVertices[k]).magnitude() < 0.1 && j != vertices[0] && j != vertices[1]) { - if (vertices[0] > -1) { - vertices[1] = j; - if (line) { - *line = Graphics::Line3d(_vertices[vertices[0]], _vertices[vertices[1]]); - } - return true; - } else { - vertices[0] = j; +Common::List Sector::getBridgesTo(Sector *sector) const { + // This returns a list of "bridges", which are edges that can be travelled + // through to get to another sector. 0 bridges mean the sectors aren't + // connected. + + // The algorithm starts by considering all the edges of sector A + // bridges. It then narrows them down by cutting the bridges against + // sector B, so we end up with a list of lines which are at the border + // of sector A and inside sector B. + + Common::List bridges; + Common::List::iterator it; + + for (int i = 0; i < _numVertices; i++){ + bridges.push_back(Graphics::Line3d(_vertices[i], _vertices[i+1])); + } + + Graphics::Vector3d* sectorVertices = sector->getVertices(); + for (int i = 0; i < sector->getNumVertices(); i++) { + Graphics::Vector3d pos, edge, delta_b1, delta_b2; + Graphics::Line3d line(sectorVertices[i], sectorVertices[i+1]); + it = bridges.begin(); + while (it != bridges.end()) { + Graphics::Line3d& bridge = (*it); + edge = line.end() - line.begin(); + delta_b1 = bridge.begin() - line.begin(); + delta_b2 = bridge.end() - line.begin(); + bool b1_out = edge.x() * delta_b1.y() < edge.y() * delta_b1.x(); + bool b2_out = edge.x() * delta_b2.y() < edge.y() * delta_b2.x(); + + if (b1_out && b2_out) { + // Both points are outside. + it = bridges.erase(it); + continue; + } else if (b1_out) { + if (bridge.intersectLine2d(line, &pos)) { + bridge = Graphics::Line3d(pos, bridge.end()); + } + } else if (b2_out) { + if (bridge.intersectLine2d(line, &pos)) { + bridge = Graphics::Line3d(bridge.begin(), pos); } } + + if ((bridge.end() - bridge.begin()).magnitude() < 0.01f) { + it = bridges.erase(it); + continue; + } + ++it; } } - return false; + // All the bridges should be at the same height on both sectors. + while (it != bridges.end()) { + if (fabs(getProjectionToPlane((*it).begin()).z() - sector->getProjectionToPlane((*it).begin()).z()) > 0.01f || + fabs(getProjectionToPlane((*it).end()).z() - sector->getProjectionToPlane((*it).end()).z()) > 0.01f) { + it = bridges.erase(it); + continue; + } + ++it; + } + return bridges; } Graphics::Vector3d Sector::getProjectionToPlane(Graphics::Vector3d point) const { diff --git a/engines/grim/walkplane.h b/engines/grim/walkplane.h index 9dbe09a0ba7..90135040c79 100644 --- a/engines/grim/walkplane.h +++ b/engines/grim/walkplane.h @@ -67,7 +67,7 @@ public: SectorType getType() const { return _type; } // FIXME: Implement type de-masking bool isVisible() const { return _visible; } bool isPointInSector(Graphics::Vector3d point) const; - bool isAdjacentTo(Sector *sector, Graphics::Line3d *line) const; + Common::List Sector::getBridgesTo(Sector *sector) const; Graphics::Vector3d getProjectionToPlane(Graphics::Vector3d point) const; Graphics::Vector3d getProjectionToPuckVector(Graphics::Vector3d v) const;