예제 #1
0
CollisionResult GD_API PolygonCollisionTest(Polygon2d& p1,
                                            Polygon2d& p2,
                                            bool ignoreTouchingEdges) {
  if (p1.vertices.size() < 3 || p2.vertices.size() < 3) {
    CollisionResult result;
    result.collision = false;
    result.move_axis.x = 0.0f;
    result.move_axis.y = 0.0f;
    return result;
  }

  p1.ComputeEdges();
  p2.ComputeEdges();

  sf::Vector2f edge;
  sf::Vector2f move_axis(0, 0);
  sf::Vector2f mtd(0, 0);

  float min_dist = FLT_MAX;

  CollisionResult result;

  // Iterate over all the edges composing the polygons
  for (std::size_t i = 0; i < p1.vertices.size() + p2.vertices.size(); i++) {
    if (i < p1.vertices.size())  // or <=
    {
      edge = p1.edges[i];
    } else {
      edge = p2.edges[i - p1.vertices.size()];
    }

    sf::Vector2f axis(
        -edge.y, edge.x);  // Get the axis to which polygons will be projected
    normalise(axis);

    float minA = 0;
    float minB = 0;
    float maxA = 0;
    float maxB = 0;

    project(axis, p1, minA, maxA);
    project(axis, p2, minB, maxB);

    float dist = distance(minA, maxA, minB, maxB);
    if (dist > 0.0f || (dist == 0.0 && ignoreTouchingEdges)) {
      // If the projections on the axis do not overlap, then
      // there is no collision
      result.collision = false;
      result.move_axis.x = 0.0f;
      result.move_axis.y = 0.0f;

      return result;
    }

    float absDist = std::abs(dist);

    if (absDist < min_dist) {
      min_dist = absDist;
      move_axis = axis;
    }
  }

  result.collision = true;

  sf::Vector2f d = p1.ComputeCenter() - p2.ComputeCenter();
  if (dotProduct(d, move_axis) < 0.0f) move_axis = -move_axis;
  result.move_axis = move_axis * min_dist;

  return result;
}
예제 #2
0
RaycastResult GD_API PolygonRaycastTest(
    Polygon2d& poly, float startX, float startY, float endX, float endY) {
  RaycastResult result;
  result.collision = false;

  if (poly.vertices.size() < 2) {
    return result;
  }

  poly.ComputeEdges();
  sf::Vector2f p, q, r, s;
  float minSqDist = FLT_MAX;

  // Ray segment: p + t*r, with p = start and r = end - start
  p.x = startX;
  p.y = startY;
  r.x = endX - startX;
  r.y = endY - startY;

  for (int i = 0; i < poly.edges.size(); i++) {
    // Edge segment: q + u*s
    q = poly.vertices[i];
    s = poly.edges[i];
    sf::Vector2f deltaQP = q - p;
    float crossRS = crossProduct(r, s);
    float t = crossProduct(deltaQP, s) / crossRS;
    float u = crossProduct(deltaQP, r) / crossRS;

    // Collinear
    if (abs(crossRS) <= 0.0001 && abs(crossProduct(deltaQP, r)) <= 0.0001) {
      // Project the ray and the edge to work on floats, keeping linearity
      // through t
      sf::Vector2f axis(r.x, r.y);
      normalise(axis);
      float rayA = 0.0f;
      float rayB = dotProduct(axis, r);
      float edgeA = dotProduct(axis, deltaQP);
      float edgeB = dotProduct(axis, deltaQP + s);
      // Get overlapping range
      float minOverlap = std::max(std::min(rayA, rayB), std::min(edgeA, edgeB));
      float maxOverlap = std::min(std::max(rayA, rayB), std::max(edgeA, edgeB));
      if (minOverlap > maxOverlap) {
        return result;
      }
      result.collision = true;
      // Zero distance ray
      if (rayB == 0.0f) {
        result.closePoint = p;
        result.closeSqDist = 0.0f;
        result.farPoint = p;
        result.farSqDist = 0.0f;
      }
      float t1 = minOverlap / abs(rayB);
      float t2 = maxOverlap / abs(rayB);
      result.closePoint = p + t1 * r;
      result.closeSqDist = t1 * t1 * (r.x * r.x + r.y * r.y);
      result.farPoint = p + t2 * r;
      result.farSqDist = t2 * t2 * (r.x * r.x + r.y * r.y);

      return result;
    } else if (crossRS != 0 && 0 <= t && t <= 1 && 0 <= u && u <= 1) {
      sf::Vector2f point = p + t * r;

      float sqDist = (point.x - startX) * (point.x - startX) +
                     (point.y - startY) * (point.y - startY);
      if (sqDist < minSqDist) {
        if (!result.collision) {
          result.farPoint = point;
          result.farSqDist = sqDist;
        }
        minSqDist = sqDist;
        result.closePoint = point;
        result.closeSqDist = sqDist;
        result.collision = true;
      } else {
        result.farPoint = point;
        result.farSqDist = sqDist;
      }
    }
  }

  return result;
}