BSPTree2D* BSPTree2D::create( const PointList& inPointList, std::string& outState, double inTol /*= 1.0e-13*/, size_t inMaxNodeCount /*= 7000*/, size_t numAxialDivsn /*= 0*/ ) { Segment::List seglist; PointList::const_iterator itrP1 = inPointList.begin(); PointList::const_iterator itrP2 = inPointList.begin(); for(itrP2++;itrP2 != inPointList.end();itrP1++,itrP2++) seglist.push_back(Segment(*itrP1,*itrP2)); outState.clear(); Node* baseNode = new Node(seglist,outState,inTol,inMaxNodeCount,numAxialDivsn); if(!outState.empty()) { delete baseNode; return nullptr; } return new BSPTree2D(baseNode,inTol); }
BSPTree2D* BSPTree2D::create( const PointArray& inPointArray, std::string& outState, double inTol /*= 1.0e-13*/, size_t inMaxNodeCount /*= 7000*/, size_t numAxialDivsn /*= 0*/ ) { Segment::List seglist; for(auto itPoint = inPointArray.begin(); itPoint != --inPointArray.end();) { auto & p1 = *itPoint, & p2 = *(++itPoint); seglist.push_back(Segment(p1, p2)); } outState.clear(); Node* baseNode = new Node(seglist,outState,inTol,inMaxNodeCount,numAxialDivsn); if(!outState.empty()) { delete baseNode; return nullptr; } return new BSPTree2D(baseNode,inTol); }
void Object::doCollision(Background &bg) { WallSet set; const TileMap &tilemap = bg.getTileMap(); /* get tile bounds of circle */ int l = (int)floor((pos.x-radius)/8), r = (int)ceil((pos.x+radius)/8); int t = (int)floor((pos.y-radius)/8), b = (int)ceil((pos.y+radius)/8); /* add all walls declared for the tiles */ for (int j= t; j < b; j++) for (int i= l; i < r; i++) { const TileMapEntry *tme = tilemap.index(i,j); set.addFromTile(tme); } /* we want to ignore certain walls (tops of ladders when climbing through them, and one way walls). This loop removes walls from the ignore list if the object doesn't intersect with it */ { Wall::CPListIterator i; for (i = ignore.begin(); i != ignore.end();) { const Segment &s = (**i).wall.segment; if (!s.intersect(Circle(pos, radius))) ignore.erase(i++); else ++i; } } /* all the collision gets messed up when the sometimes here/sometimes not walls enter the equation */ bool irregularWalls = false; { WallSet::Iterator i; for (i = set.begin(); i != set.end(); ++i) { const Edge::EdgeType &type = (**i).wall.type; if (type == Edge::LADDER_TOP || type == Edge::ONE_WAY) { irregularWalls = true; break; } } } /* check all walls found above for collision */ WallSet::Iterator i; Segment::List collide; for (i = set.begin(); i != set.end(); ++i) { const Wall &w = (**i); const Segment &s = w.wall.segment; preProcessWall(**i); /* don't process it if it's in the ignore list... */ if (std::find(ignore.begin(), ignore.end(), &w) != ignore.end()) continue; /* we wait to do segment-circle collision if we're doing complicated collision */ if (!irregularWalls) { /* ...or if it doesn't even intersect the circle */ if (!s.intersect(Circle(pos, radius))) continue; } if (processWall(**i)) { if (irregularWalls) collide.push_back((**i).wall.segment); else collideWall((**i).wall.segment); } } /* the problem with using normal collision: if we have some walls that are here today/gone tomorrow the collision will be irregular because of the way segment-circle collision works. when the circle collides with two walls with the same normal near their intersection point, one of the segments will (probably) provide a shunt vector that is not the normal, whereas the other (probably) will. This will push the object in the direction of the vector, even though it shouldn't. how to fix it (basic idea): * combine collinear segment that intersect at a point like in Walls::addWall * sometimes walls will exist on the other side of this newly created wall. If the object is on the normal side, remove this "T-Bone" wall. */ if (irregularWalls) { /* combine collinear segments that intersect at a point, like in Walls::addWall */ Segment::ListIterator k, l; for (k = collide.begin(); k != collide.end();) { bool removed = false; const Segment &s = *k; l = k; for (++l; l != collide.end(); ++l) { const Segment &t = *l; Point p0, p1; Segment::IntersectType it = s.intersect(t,p0,p1); if (it != Segment::COLLINEAR_POINT) continue; if (s.normal != t.normal) continue; if (p0 == t.p0) collide.push_back(Segment(s.p0, t.p1)); else collide.push_back(Segment(t.p0, s.p1)); collide.erase(k++); if (k == l) collide.erase(k++); else collide.erase(l); removed = true; break; } if (!removed) ++k; } /* look for edges that T-bone another segment */ for (k = collide.begin(); k != collide.end();) { bool removed = false; const Segment &s = *k; l = k; for (++l; l != collide.end(); ++l) { const Segment &t = *l; Point p0, p1; Segment::IntersectType it = s.intersect(t,p0,p1); if (it != Segment::POINT) continue; /* we know that the combined segments are at the end of the collide list, therefore if two segments are T-boned, the top of the "T" must be t and the base of the "T" must be s. Therefore, if the intersection point is t, we know it can't be a T-bone. */ if (p0 == t.p0 || p0 == t.p1) continue; /* find which point connects the top of the "T" to the base. */ if (p0 == s.p0) { /* we'll erase the base if the top normal points away from the base and if the object is on the top normal side */ if (!t.faces(s.p1) && t.faces(pos)) { collide.erase(k++); removed = true; break; } } else if (p0 == s.p1) { /* we'll erase the base if the top normal points away from the base and if the object is on the top normal side */ if (!t.faces(s.p0) && t.faces(pos)) { collide.erase(k++); removed = true; break; } } } if (!removed) ++k; } /* shunt object around to avoid collision */ for (k = collide.begin(); k != collide.end(); ++k) { const Segment &s = *k; /* this check is necessary, if we check walls without moving the object, we get false positives */ if (s.intersect(Circle(pos, radius))) collideWall(*k); } } }