GRIM: Better sector adjacency checking that also works with overlapping sectors.

This commit is contained in:
Joni Vähämäki 2011-05-11 20:21:13 +08:00 committed by Pawel Kolodziejski
parent 54d9ad429f
commit 4af8ffadd6
3 changed files with 73 additions and 27 deletions

View File

@ -573,10 +573,22 @@ void Actor::walkTo(const Graphics::Vector3d &p) {
}
}
Graphics::Line3d line;
if (!inClosed && sector->isAdjacentTo(s, &line)) {
Common::List<Graphics::Line3d> 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<PathNode *>::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);

View File

@ -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<Graphics::Line3d> 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<Graphics::Line3d> bridges;
Common::List<Graphics::Line3d>::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 {

View File

@ -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<Graphics::Line3d> Sector::getBridgesTo(Sector *sector) const;
Graphics::Vector3d getProjectionToPlane(Graphics::Vector3d point) const;
Graphics::Vector3d getProjectionToPuckVector(Graphics::Vector3d v) const;