예제 #1
0
void set_rectangle_rectangle_constraints(Constraints* constraints,
                                         const Rectf& r1, const Rectf& r2, const Vector& addl_ground_movement)
{
  float itop = r1.get_bottom() - r2.get_top();
  float ibottom = r2.get_bottom() - r1.get_top();
  float ileft = r1.get_right() - r2.get_left();
  float iright = r2.get_right() - r1.get_left();

  float vert_penetration = std::min(itop, ibottom);
  float horiz_penetration = std::min(ileft, iright);
  if(vert_penetration < horiz_penetration) {
    if(itop < ibottom) {
      constraints->constrain_bottom(r2.get_top(), addl_ground_movement.y);
      constraints->hit.bottom = true;
      constraints->ground_movement += addl_ground_movement;
    } else {
      constraints->constrain_top(r2.get_bottom(), addl_ground_movement.y);
      constraints->hit.top = true;
    }
  } else {
    if(ileft < iright) {
      constraints->constrain_right(r2.get_left(), addl_ground_movement.x);
      constraints->hit.right = true;
    } else {
      constraints->constrain_left(r2.get_right(), addl_ground_movement.x);
      constraints->hit.left = true;
    }
  }
}
예제 #2
0
/** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
static void get_hit_normal(const Rectf& r1, const Rectf& r2, CollisionHit& hit,
                           Vector& normal)
{
  float itop = r1.get_bottom() - r2.get_top();
  float ibottom = r2.get_bottom() - r1.get_top();
  float ileft = r1.get_right() - r2.get_left();
  float iright = r2.get_right() - r1.get_left();

  float vert_penetration = std::min(itop, ibottom);
  float horiz_penetration = std::min(ileft, iright);
  if(vert_penetration < horiz_penetration) {
    if(itop < ibottom) {
      hit.bottom = true;
      normal.y = vert_penetration;
    } else {
      hit.top = true;
      normal.y = -vert_penetration;
    }
  } else {
    if(ileft < iright) {
      hit.right = true;
      normal.x = horiz_penetration;
    } else {
      hit.left = true;
      normal.x = -horiz_penetration;
    }
  }
}
예제 #3
0
void
Sector::collision_tilemap(collision::Constraints* constraints,
                          const Vector& movement, const Rectf& dest,
                          MovingObject& object) const
{
  // calculate rectangle where the object will move
  float x1 = dest.get_left();
  float x2 = dest.get_right();
  float y1 = dest.get_top();
  float y2 = dest.get_bottom();

  for(auto i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
    TileMap* solids = *i;

    // test with all tiles in this rectangle
    Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2));

    for(int x = test_tiles.left; x < test_tiles.right; ++x) {
      for(int y = test_tiles.top; y < test_tiles.bottom; ++y) {
        const Tile* tile = solids->get_tile(x, y);
        if(!tile)
          continue;
        // skip non-solid tiles
        if(!tile->is_solid ())
          continue;
        Rectf tile_bbox = solids->get_tile_bbox(x, y);

        /* If the tile is a unisolid tile, the "is_solid()" function above
         * didn't do a thorough check. Calculate the position and (relative)
         * movement of the object and determine whether or not the tile is
         * solid with regard to those parameters. */
        if(tile->is_unisolid ()) {
          Vector relative_movement = movement
            - solids->get_movement(/* actual = */ true);

          if (!tile->is_solid (tile_bbox, object.get_bbox(), relative_movement))
            continue;
        } /* if (tile->is_unisolid ()) */

        if(tile->is_slope ()) { // slope tile
          AATriangle triangle;
          int slope_data = tile->getData();
          if (solids->get_drawing_effect() & VERTICAL_FLIP)
            slope_data = AATriangle::vertical_flip(slope_data);
          triangle = AATriangle(tile_bbox, slope_data);

          collision::rectangle_aatriangle(constraints, dest, triangle,
              solids->get_movement(/* actual = */ false));
        } else { // normal rectangular tile
          check_collisions(constraints, movement, dest, tile_bbox, NULL, NULL,
              solids->get_movement(/* actual = */ false));
        }
      }
    }
  }
}
예제 #4
0
  bool contains(const Rectf& other) const
  {
    // FIXME: This is overlaps(), not contains()!
    if (m_p1.x >= other.get_right() || other.get_left() >= get_right())
      return false;
    if (m_p1.y >= other.get_bottom() || other.get_top() >= get_bottom())
      return false;

    return true;
  }
예제 #5
0
void
EditorToolboxWidget::update_selection()
{
  Rectf select = normalize_selection();
  m_tiles->m_tiles.clear();
  m_tiles->m_width = static_cast<int>(select.get_width() + 1);
  m_tiles->m_height = static_cast<int>(select.get_height() + 1);

  int size = static_cast<int>(m_active_tilegroup->tiles.size());
  for (int y = static_cast<int>(select.get_top()); y <= static_cast<int>(select.get_bottom()); y++) {
    for (int x = static_cast<int>(select.get_left()); x <= static_cast<int>(select.get_right()); x++) {
      int tile_pos = y*4 + x + m_starting_tile;
      if (tile_pos < size && tile_pos >= 0) {
        m_tiles->m_tiles.push_back(m_active_tilegroup->tiles[tile_pos]);
      } else {
        m_tiles->m_tiles.push_back(0);
      }
    }
  }
}
예제 #6
0
bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect,
                          const AATriangle& triangle, const Vector& addl_ground_movement)
{
  if(!intersects(rect, (const Rectf&) triangle))
    return false;

  Vector normal;
  float c = 0.0;
  Vector p1;
  Rectf area;
  switch(triangle.dir & AATriangle::DEFORM_MASK) {
    case 0:
      area.p1 = triangle.bbox.p1;
      area.p2 = triangle.bbox.p2;
      break;
    case AATriangle::DEFORM_BOTTOM:
      area.p1 = Vector(triangle.bbox.p1.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
      area.p2 = triangle.bbox.p2;
      break;
    case AATriangle::DEFORM_TOP:
      area.p1 = triangle.bbox.p1;
      area.p2 = Vector(triangle.bbox.p2.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
      break;
    case AATriangle::DEFORM_LEFT:
      area.p1 = triangle.bbox.p1;
      area.p2 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p2.y);
      break;
    case AATriangle::DEFORM_RIGHT:
      area.p1 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p1.y);
      area.p2 = triangle.bbox.p2;
      break;
    default:
      assert(false);
  }

  switch(triangle.dir & AATriangle::DIRECTION_MASK) {
    case AATriangle::SOUTHWEST:
      p1 = Vector(rect.p1.x, rect.p2.y);
      makePlane(area.p1, area.p2, normal, c);
      break;
    case AATriangle::NORTHEAST:
      p1 = Vector(rect.p2.x, rect.p1.y);
      makePlane(area.p2, area.p1, normal, c);
      break;
    case AATriangle::SOUTHEAST:
      p1 = rect.p2;
      makePlane(Vector(area.p1.x, area.p2.y),
                Vector(area.p2.x, area.p1.y), normal, c);
      break;
    case AATriangle::NORTHWEST:
      p1 = rect.p1;
      makePlane(Vector(area.p2.x, area.p1.y),
                Vector(area.p1.x, area.p2.y), normal, c);
      break;
    default:
      assert(false);
  }

  float n_p1 = -(normal * p1);
  float depth = n_p1 - c;
  if(depth < 0)
    return false;

#if 0
  std::cout << "R: " << rect << " Tri: " << triangle << "\n";
  std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
#endif

  Vector outvec = normal * (depth + 0.2f);

  const float RDELTA = 3;
  if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
     || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
    set_rectangle_rectangle_constraints(constraints, rect, area);
  } else {
    if(outvec.x < 0) {
      constraints->constrain_right(rect.get_right() + outvec.x, addl_ground_movement.x);
      constraints->hit.right = true;
    } else {
      constraints->constrain_left(rect.get_left() + outvec.x, addl_ground_movement.x);
      constraints->hit.left = true;
    }

    if(outvec.y < 0) {
      constraints->constrain_bottom(rect.get_bottom() + outvec.y, addl_ground_movement.y);
      constraints->hit.bottom = true;
      constraints->ground_movement += addl_ground_movement;
    } else {
      constraints->constrain_top(rect.get_top() + outvec.y, addl_ground_movement.y);
      constraints->hit.top = true;
    }
    constraints->hit.slope_normal = normal;
  }

  return true;
}
예제 #7
0
파일: tile.cpp 프로젝트: amigadave/supertux
/* Check whether the object is already *in* the tile. If so, the tile
 * is non-solid. Otherwise, if the object is "above" (south slopes)
 * or "below" (north slopes), the tile will be solid. */
bool Tile::check_position_unisolid (const Rectf& obj_bbox,
                                    const Rectf& tile_bbox) const
{
  int slope_info;
  float tile_x;
  float tile_y;
  float gradient;
  float delta_x;
  float delta_y;
  float obj_x = 0.0;
  float obj_y = 0.0;

  /* If this is not a slope, this is - again - easy */
  if (!this->is_slope())
  {
    int dir = this->getData() & Tile::UNI_DIR_MASK;

    return ((dir == Tile::UNI_DIR_NORTH) && ((obj_bbox.get_bottom() - SHIFT_DELTA) <= tile_bbox.get_top()   ))
        || ((dir == Tile::UNI_DIR_SOUTH) && ((obj_bbox.get_top()    + SHIFT_DELTA) >= tile_bbox.get_bottom()))
        || ((dir == Tile::UNI_DIR_WEST ) && ((obj_bbox.get_right()  - SHIFT_DELTA) <= tile_bbox.get_left()  ))
        || ((dir == Tile::UNI_DIR_EAST ) && ((obj_bbox.get_left()   + SHIFT_DELTA) >= tile_bbox.get_right() ));
  }

  /* There are 20 different cases. For each case, calculate a line that
   * describes the slope's surface. The line is defined by x, y, and m, the
   * gradient. */
  slope_info = this->getData();
  switch (slope_info
      & (AATriangle::DIRECTION_MASK | AATriangle::DEFORM_MASK))
  {
    case AATriangle::SOUTHWEST:
    case AATriangle::SOUTHWEST | AATriangle::DEFORM_TOP:
    case AATriangle::SOUTHWEST | AATriangle::DEFORM_LEFT:
    case AATriangle::NORTHEAST:
    case AATriangle::NORTHEAST | AATriangle::DEFORM_TOP:
    case AATriangle::NORTHEAST | AATriangle::DEFORM_LEFT:
      tile_x = tile_bbox.get_left();
      tile_y = tile_bbox.get_top();
      gradient = 1.0;
      break;

    case AATriangle::SOUTHEAST:
    case AATriangle::SOUTHEAST | AATriangle::DEFORM_TOP:
    case AATriangle::SOUTHEAST | AATriangle::DEFORM_RIGHT:
    case AATriangle::NORTHWEST:
    case AATriangle::NORTHWEST | AATriangle::DEFORM_TOP:
    case AATriangle::NORTHWEST | AATriangle::DEFORM_RIGHT:
      tile_x = tile_bbox.get_right();
      tile_y = tile_bbox.get_top();
      gradient = -1.0;
      break;

    case AATriangle::SOUTHEAST | AATriangle::DEFORM_BOTTOM:
    case AATriangle::SOUTHEAST | AATriangle::DEFORM_LEFT:
    case AATriangle::NORTHWEST | AATriangle::DEFORM_BOTTOM:
    case AATriangle::NORTHWEST | AATriangle::DEFORM_LEFT:
      tile_x = tile_bbox.get_left();
      tile_y = tile_bbox.get_bottom();
      gradient = -1.0;
      break;

    case AATriangle::SOUTHWEST | AATriangle::DEFORM_BOTTOM:
    case AATriangle::SOUTHWEST | AATriangle::DEFORM_RIGHT:
    case AATriangle::NORTHEAST | AATriangle::DEFORM_BOTTOM:
    case AATriangle::NORTHEAST | AATriangle::DEFORM_RIGHT:
      tile_x = tile_bbox.get_right();
      tile_y = tile_bbox.get_bottom();
      gradient = 1.0;
      break;

    default:
      assert (23 == 42);
      return 0;
  }

  /* delta_x, delta_y: Gradient aware version of SHIFT_DELTA. Here, we set the
   * sign of the values only. Also, we determine here which corner of the
   * object's bounding box is the interesting one for us. */
  delta_x = 1.0 * SHIFT_DELTA;
  delta_y = 1.0 * SHIFT_DELTA;
  switch (slope_info & AATriangle::DIRECTION_MASK)
  {
    case AATriangle::SOUTHWEST:
      delta_x *= 1.0;
      delta_y *= -1.0;
      obj_x = obj_bbox.get_left();
      obj_y = obj_bbox.get_bottom();
      break;

    case AATriangle::SOUTHEAST:
      delta_x *= -1.0;
      delta_y *= -1.0;
      obj_x = obj_bbox.get_right();
      obj_y = obj_bbox.get_bottom();
      break;

    case AATriangle::NORTHWEST:
      delta_x *= 1.0;
      delta_y *= 1.0;
      obj_x = obj_bbox.get_left();
      obj_y = obj_bbox.get_top();
      break;

    case AATriangle::NORTHEAST:
      delta_x *= -1.0;
      delta_y *= 1.0;
      obj_x = obj_bbox.get_right();
      obj_y = obj_bbox.get_top();
      break;
  }

  /* Adapt the delta_x, delta_y and the gradient for the 26.6 deg and 63.4 deg
   * cases. */
  switch (slope_info & AATriangle::DEFORM_MASK)
  {
    case 0:
      delta_x *= .70710678118654752440; /* 1/sqrt(2) */
      delta_y *= .70710678118654752440; /* 1/sqrt(2) */
      break;

    case AATriangle::DEFORM_BOTTOM:
    case AATriangle::DEFORM_TOP:
      delta_x *= .44721359549995793928; /* 1/sqrt(5) */
      delta_y *= .89442719099991587856; /* 2/sqrt(5) */
      gradient *= 0.5;
      break;

    case AATriangle::DEFORM_LEFT:
    case AATriangle::DEFORM_RIGHT:
      delta_x *= .89442719099991587856; /* 2/sqrt(5) */
      delta_y *= .44721359549995793928; /* 1/sqrt(5) */
      gradient *= 2.0;
      break;
  }

  /* With a south slope, check if all points are above the line. If one point
   * isn't, the slope is not solid. => You can pass through a south-slope from
   * below but not from above. */
  if (((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHWEST)
   || ((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHEAST))
  {
    return !is_below_line(tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y);
  }
  /* northwest or northeast. Same as above, but inverted. You can pass from top
   * to bottom but not vice versa. */
  else
  {
    return !is_above_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y);
  }
} /* int check_position_unisolid */
예제 #8
0
void
Background::draw_image(DrawingContext& context, const Vector& pos_)
{
  Sizef level(Sector::current()->get_width(), Sector::current()->get_height());
  Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT);
  Sizef parallax_image_size = (1.0f - speed) * screen + level * speed;
  Rectf cliprect = context.get_cliprect();

  int start_x = static_cast<int>(floorf((cliprect.get_left()  - (pos_.x - image->get_width() /2.0f)) / image->get_width()));
  int end_x   = static_cast<int>(ceilf((cliprect.get_right()  - (pos_.x + image->get_width() /2.0f)) / image->get_width()))+1;
  int start_y = static_cast<int>(floorf((cliprect.get_top()   - (pos_.y - image->get_height()/2.0f)) / image->get_height()));
  int end_y   = static_cast<int>(ceilf((cliprect.get_bottom() - (pos_.y + image->get_height()/2.0f)) / image->get_height()))+1;

  switch(alignment)
  {
    case LEFT_ALIGNMENT:
      for(int y = start_y; y < end_y; ++y)
      {
        Vector p(pos_.x - parallax_image_size.width / 2.0f,
                 pos_.y + y * image->get_height()  - image->get_height() / 2.0f);
        context.draw_surface(image, p, layer);
      }
      break;

    case RIGHT_ALIGNMENT:
      for(int y = start_y; y < end_y; ++y)
      {
        Vector p(pos_.x + parallax_image_size.width / 2.0f - image->get_width(),
                 pos_.y + y * image->get_height() - image->get_height() / 2.0f);
        context.draw_surface(image, p, layer);
      }
      break;

    case TOP_ALIGNMENT:
      for(int x = start_x; x < end_x; ++x)
      {
        Vector p(pos_.x + x * image->get_width() - image->get_width() / 2.0f,
                 pos_.y - parallax_image_size.height / 2.0f);
        context.draw_surface(image, p, layer);
      }
      break;

    case BOTTOM_ALIGNMENT:
      for(int x = start_x; x < end_x; ++x)
      {
        Vector p(pos_.x + x * image->get_width()  - image->get_width() / 2.0f,
                 pos_.y - image->get_height() + parallax_image_size.height / 2.0f);
        context.draw_surface(image, p, layer);
      }
      break;

    case NO_ALIGNMENT:
      for(int y = start_y; y < end_y; ++y)
        for(int x = start_x; x < end_x; ++x)
        {
          Vector p(pos_.x + x * image->get_width()  - image->get_width()/2,
                   pos_.y + y * image->get_height() - image->get_height()/2);

          if (image_top.get() != NULL && (y < 0))
          {
            context.draw_surface(image_top, p, layer);
          }
          else if (image_bottom.get() != NULL && (y > 0))
          {
            context.draw_surface(image_bottom, p, layer);
          }
          else
          {
            context.draw_surface(image, p, layer);
          }
        }
      break;
  }
}
예제 #9
0
/** r1 is supposed to be moving, r2 a solid object */
void check_collisions(collision::Constraints* constraints,
                      const Vector& obj_movement, const Rectf& obj_rect, const Rectf& other_rect,
                      GameObject* object = NULL, MovingObject* other = NULL, const Vector& other_movement = Vector(0,0))
{
  if(!collision::intersects(obj_rect, other_rect))
    return;

  MovingObject *moving_object = dynamic_cast<MovingObject*> (object);
  CollisionHit dummy;
  if(other != NULL && object != NULL && !other->collides(*object, dummy))
    return;
  if(moving_object != NULL && other != NULL && !moving_object->collides(*other, dummy))
    return;

  // calculate intersection
  float itop    = obj_rect.get_bottom() - other_rect.get_top();
  float ibottom = other_rect.get_bottom() - obj_rect.get_top();
  float ileft   = obj_rect.get_right() - other_rect.get_left();
  float iright  = other_rect.get_right() - obj_rect.get_left();

  if(fabsf(obj_movement.y) > fabsf(obj_movement.x)) {
    if(ileft < SHIFT_DELTA) {
      constraints->constrain_right(other_rect.get_left(), other_movement.x);
      return;
    } else if(iright < SHIFT_DELTA) {
      constraints->constrain_left(other_rect.get_right(), other_movement.x);
      return;
    }
  } else {
    // shiftout bottom/top
    if(itop < SHIFT_DELTA) {
      constraints->constrain_bottom(other_rect.get_top(), other_movement.y);
      return;
    } else if(ibottom < SHIFT_DELTA) {
      constraints->constrain_top(other_rect.get_bottom(), other_movement.y);
      return;
    }
  }

  constraints->ground_movement += other_movement;
  if(other != NULL && object != NULL) {
    HitResponse response = other->collision(*object, dummy);
    if(response == ABORT_MOVE)
      return;

    if(other->get_movement() != Vector(0, 0)) {
      // TODO what todo when we collide with 2 moving objects?!?
      constraints->ground_movement += other->get_movement();
    }
  }

  float vert_penetration = std::min(itop, ibottom);
  float horiz_penetration = std::min(ileft, iright);
  if(vert_penetration < horiz_penetration) {
    if(itop < ibottom) {
      constraints->constrain_bottom(other_rect.get_top(), other_movement.y);
      constraints->hit.bottom = true;
    } else {
      constraints->constrain_top(other_rect.get_bottom(), other_movement.y);
      constraints->hit.top = true;
    }
  } else {
    if(ileft < iright) {
      constraints->constrain_right(other_rect.get_left(), other_movement.x);
      constraints->hit.right = true;
    } else {
      constraints->constrain_left(other_rect.get_right(), other_movement.x);
      constraints->hit.left = true;
    }
  }
}