Cleanups related to tilemap positions:
authorFlorian Forster <supertux@octo.it>
Sat, 30 Jan 2010 12:44:05 +0000 (12:44 +0000)
committerFlorian Forster <supertux@octo.it>
Sat, 30 Jan 2010 12:44:05 +0000 (12:44 +0000)
- Combine TileMap::{x,y}_offset into a vector TileMap::offset.

- Factor out some widely duplicated code into new TileMap methods
  get_tile_position, get_bbox, get_tile_bbox, get_tiles_overlapping.

- AATriangle contains a Rectf instead of two vectors.

- Some simplification to Sector::is_free_of_tiles.

- BadGuy::might_fall passed a zero-width rectangle to
  Sector::is_free_of_tiles.  With the new code, such a rectangle can slip
  between two tiles without overlapping either one, giving an incorrect
  result.  So change the BadGuy::might_fall rectangle to have width 1.

Thanks to Matt McCutchen for this patch.

SVN-Revision: 6297

src/badguy/badguy.cpp
src/math/aatriangle.hpp
src/object/particlesystem_interactive.cpp
src/object/tilemap.cpp
src/object/tilemap.hpp
src/supertux/collision.cpp
src/supertux/sector.cpp

index e794ce4..9e5c545 100644 (file)
@@ -481,9 +481,9 @@ BadGuy::might_fall(int height)
   float y2 = bbox.p2.y + 1 + height;
   if (dir == LEFT) {
     x1 = bbox.p1.x - 1;
-    x2 = bbox.p1.x - 1;
+    x2 = bbox.p1.x;
   } else {
-    x1 = bbox.p2.x + 1;
+    x1 = bbox.p2.x;
     x2 = bbox.p2.x + 1;
   }
   return Sector::current()->is_free_of_statics(Rectf(x1, y1, x2, y2));
index 6eea71f..db0ca5d 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef HEADER_SUPERTUX_MATH_AATRIANGLE_HPP
 #define HEADER_SUPERTUX_MATH_AATRIANGLE_HPP
 
-#include "math/vector.hpp"
+#include "math/rectf.hpp"
 
 /**
  * An axis-aligned triangle (ie. a triangle where 2 sides are parallel to the x-
@@ -53,31 +53,18 @@ public:
 
 public:
   AATriangle() :
-    p1(),
-    p2(),
+    bbox(),
     dir(SOUTHWEST)
   {
   }
-  AATriangle(const Vector& v1, const Vector& v2, int newdir) :
-    p1(v1),
-    p2(v2),
+  AATriangle(const Rectf& newbbox, int newdir) :
+    bbox(newbbox),
     dir(newdir)
   {
   }
 
-  float get_width() const
-  {
-    return p2.x - p1.x; 
-  }
-
-  float get_height() const
-  { 
-    return p2.y - p1.y; 
-  }
-
 public:
-  Vector p1;
-  Vector p2;
+  Rectf bbox;
   int dir;
 };
 
index 331884b..9923c03 100644 (file)
@@ -82,6 +82,7 @@ ParticleSystem_Interactive::collision(Particle* object, Vector movement)
 
   for(std::list<TileMap*>::const_iterator i = Sector::current()->solid_tilemaps.begin(); i != Sector::current()->solid_tilemaps.end(); i++) {
     TileMap* solids = *i;
+    // FIXME Handle a nonzero tilemap offset
     for(int x = starttilex; x*32 < max_x; ++x) {
       for(int y = starttiley; y*32 < max_y; ++y) {
         const Tile* tile = solids->get_tile(x, y);
@@ -91,18 +92,15 @@ ParticleSystem_Interactive::collision(Particle* object, Vector movement)
         if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
           continue;
 
+        Rectf rect = solids->get_tile_bbox(x, y);
         if(tile->getAttributes() & Tile::SLOPE) { // slope tile
-          AATriangle triangle;
-          Vector p1(x*32, y*32);
-          Vector p2((x+1)*32, (y+1)*32);
-          triangle = AATriangle(p1, p2, tile->getData());
+          AATriangle triangle = AATriangle(rect, tile->getData());
 
           if(rectangle_aatriangle(&constraints, dest, triangle)) {
             if(tile->getAttributes() & Tile::WATER)
               water = true;
           }
         } else { // normal rectangular tile
-          Rectf rect(x*32, y*32, (x+1)*32, (y+1)*32);
           if(intersects(dest, rect)) {
             if(tile->getAttributes() & Tile::WATER)
               water = true;
index 4a144e6..7dce250 100644 (file)
@@ -34,8 +34,7 @@ TileMap::TileMap(const TileSet *new_tileset) :
   width(0),
   height(0), 
   z_pos(0), 
-  x_offset(0), 
-  y_offset(0), 
+  offset(Vector(0,0)),
   movement(0,0),
   drawing_effect(NO_EFFECT),
   alpha(1.0), 
@@ -56,8 +55,7 @@ TileMap::TileMap(const Reader& reader) :
   width(-1),
   height(-1), 
   z_pos(0), 
-  x_offset(0),
-  y_offset(0),
+  offset(Vector(0,0)),
   movement(Vector(0,0)), 
   drawing_effect(NO_EFFECT),
   alpha(1.0), 
@@ -88,8 +86,7 @@ TileMap::TileMap(const Reader& reader) :
     path->read(*pathLisp);
     walker.reset(new PathWalker(path.get(), /*running*/false));
     Vector v = path->get_base();
-    set_x_offset(v.x);
-    set_y_offset(v.y);
+    set_offset(v);
   }
 
   std::string draw_target_s = "normal";
@@ -138,8 +135,7 @@ TileMap::TileMap(const TileSet *new_tileset, std::string name, int z_pos,
   width(0),
   height(0), 
   z_pos(z_pos), 
-  x_offset(0), 
-  y_offset(0), 
+  offset(Vector(0,0)),
   movement(Vector(0,0)),
   drawing_effect(NO_EFFECT), 
   alpha(1.0), 
@@ -179,9 +175,8 @@ TileMap::update(float elapsed_time)
   // if we have a path to follow, follow it
   if (walker.get()) {
     Vector v = walker->advance(elapsed_time);
-    movement = Vector(v.x-get_x_offset(), std::max(0.0f,v.y-get_y_offset()));
-    set_x_offset(v.x);
-    set_y_offset(v.y);
+    movement = Vector(v.x-get_offset().x, std::max(0.0f,v.y-get_offset().y));
+    set_offset(v);
   }
 }
 
@@ -209,20 +204,16 @@ TileMap::draw(DrawingContext& context)
   context.set_translation(Vector(int(trans_x * speed_x),
                                  int(trans_y * speed_y)));
 
-  int tsx = int((context.get_translation().x - x_offset) / 32); // tilestartindex x
-  int tsy = int((context.get_translation().y - y_offset) / 32); // tilestartindex y
-  tsx = std::max(tsx, 0);
-  tsy = std::max(tsy, 0);
-  float start_x = tsx * 32 + x_offset;
-  float start_y = tsy * 32 + y_offset;
-  float end_x = start_x + SCREEN_WIDTH + 32;
-  float end_y = start_y + SCREEN_HEIGHT + 32;
+  Rectf draw_rect = Rectf(context.get_translation(),
+        context.get_translation() + Vector(SCREEN_WIDTH, SCREEN_HEIGHT));
+  Rect t_draw_rect = get_tiles_overlapping(draw_rect);
+  Vector start = get_tile_position(t_draw_rect.left, t_draw_rect.top);
 
   Vector pos;
   int tx, ty;
 
-  for(pos.x = start_x, tx = tsx; (pos.x < end_x) && (tx < width); pos.x += 32, ++tx) {
-    for(pos.y = start_y, ty = tsy; (pos.y < end_y) && (ty < height); pos.y += 32, ++ty) {
+  for(pos.x = start.x, tx = t_draw_rect.left; tx < t_draw_rect.right; pos.x += 32, ++tx) {
+    for(pos.y = start.y, ty = t_draw_rect.top; ty < t_draw_rect.bottom; pos.y += 32, ++ty) {
       int index = ty*width + tx;
       assert (index >= 0);
       assert (index < (width * height));
@@ -329,6 +320,19 @@ TileMap::resize(int new_width, int new_height, int fill_id)
   width = new_width;
 }
 
+Rect
+TileMap::get_tiles_overlapping(const Rectf &rect) const
+{
+  Rectf rect2 = rect;
+  rect2.move(-offset);
+
+  int t_left   = std::max(0     , int(floorf(rect2.get_left  () / 32)));
+  int t_right  = std::min(width , int(ceilf (rect2.get_right () / 32)));
+  int t_top    = std::max(0     , int(floorf(rect2.get_top   () / 32)));
+  int t_bottom = std::min(height, int(ceilf (rect2.get_bottom() / 32)));
+  return Rect(t_left, t_top, t_right, t_bottom);
+}
+
 void
 TileMap::set_solid(bool solid)
 {
@@ -356,7 +360,8 @@ TileMap::get_tile(int x, int y) const
 uint32_t
 TileMap::get_tile_id_at(const Vector& pos) const
 {
-  return get_tile_id(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32);
+  Vector xy = (pos - offset) / 32;
+  return get_tile_id(int(xy.x), int(xy.y));
 }
 
 const Tile*
@@ -376,7 +381,8 @@ TileMap::change(int x, int y, uint32_t newtile)
 void
 TileMap::change_at(const Vector& pos, uint32_t newtile)
 {
-  change(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32, newtile);
+  Vector xy = (pos - offset) / 32;
+  change(int(xy.x), int(xy.y), newtile);
 }
 
 void
index 8ada936..9c1f76e 100644 (file)
@@ -73,22 +73,31 @@ public:
   size_t get_height() const
   { return height; }
 
-  float get_x_offset() const
-  { return x_offset; }
-
-  float get_y_offset() const
-  { return y_offset; }
+  Vector get_offset() const
+  { return offset; }
 
   const Vector& get_movement() const
   {
     return movement;
   }
 
-  void set_x_offset(float x_offset)
-  { this->x_offset = x_offset; }
+  void set_offset(const Vector &offset)
+  { this->offset = offset; }
+
+  /* Returns the position of the upper-left corner of
+   * tile (x, y) in the sector. */
+  Vector get_tile_position(int x, int y) const
+  { return offset + Vector(x,y) * 32; }
+
+  Rectf get_bbox() const
+  { return Rectf(get_tile_position(0, 0), get_tile_position(width, height)); }
+
+  Rectf get_tile_bbox(int x, int y) const
+  { return Rectf(get_tile_position(x, y), get_tile_position(x+1, y+1)); }
 
-  void set_y_offset(float y_offset)
-  { this->y_offset = y_offset; }
+  /* Returns the half-open rectangle of (x, y) tile indices
+   * that overlap the given rectangle in the sector. */
+  Rect get_tiles_overlapping(const Rectf &rect) const;
 
   int get_layer() const
   { return z_pos; }
@@ -154,8 +163,7 @@ private:
   float speed_y;
   int width, height;
   int z_pos;
-  float x_offset;
-  float y_offset;
+  Vector offset;
   Vector movement; /**< The movement that happened last frame */
 
   DrawingEffect drawing_effect;
index f3dc041..8ac4820 100644 (file)
@@ -59,24 +59,24 @@ bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect,
   Rectf area;
   switch(triangle.dir & AATriangle::DEFORM_MASK) {
     case 0:
-      area.p1 = triangle.p1;
-      area.p2 = triangle.p2;
+      area.p1 = triangle.bbox.p1;
+      area.p2 = triangle.bbox.p2;
       break;
     case AATriangle::DEFORM1:
-      area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
-      area.p2 = triangle.p2;
+      area.p1 = Vector(triangle.bbox.p1.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
+      area.p2 = triangle.bbox.p2;
       break;
     case AATriangle::DEFORM2:
-      area.p1 = triangle.p1;
-      area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+      area.p1 = triangle.bbox.p1;
+      area.p2 = Vector(triangle.bbox.p2.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
       break;
     case AATriangle::DEFORM3:
-      area.p1 = triangle.p1;
-      area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+      area.p1 = triangle.bbox.p1;
+      area.p2 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p2.y);
       break;
     case AATriangle::DEFORM4:
-      area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
-      area.p2 = triangle.p2;
+      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);
index e0ebde1..8c96095 100644 (file)
@@ -409,7 +409,7 @@ Sector::fix_old_tiles()
       for(size_t y=0; y < solids->get_height(); ++y) {
         uint32_t    id   = solids->get_tile_id(x, y);
         const Tile *tile = solids->get_tile(x, y);
-        Vector pos(solids->get_x_offset() + x*32, solids->get_y_offset() + y*32);
+        Vector pos = solids->get_tile_position(x, y);
 
         if(id == 112) {
           add_object(new InvisibleBlock(pos));
@@ -439,8 +439,8 @@ Sector::fix_old_tiles()
     for(size_t x=0; x < tm->get_width(); ++x) {
       for(size_t y=0; y < tm->get_height(); ++y) {
         uint32_t id = tm->get_tile_id(x, y);
-        Vector pos(tm->get_x_offset() + x*32, tm->get_y_offset() + y*32);
-        Vector center(pos.x + 16, pos.y + 16);
+        Vector pos = tm->get_tile_position(x, y);
+        Vector center = pos + Vector(16, 16);
 
         // torch
         if (id == 1517) {
@@ -966,13 +966,10 @@ Sector::collision_tilemap(collision::Constraints* constraints,
     TileMap* solids = *i;
 
     // test with all tiles in this rectangle
-    int starttilex = int(x1 - solids->get_x_offset()) / 32;
-    int starttiley = int(y1 - solids->get_y_offset()) / 32;
-    int max_x = int(x2 - solids->get_x_offset());
-    int max_y = int(y2+1 - solids->get_y_offset());
+    Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2));
 
-    for(int x = starttilex; x*32 < max_x; ++x) {
-      for(int y = starttiley; y*32 < max_y; ++y) {
+    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;
@@ -986,18 +983,16 @@ Sector::collision_tilemap(collision::Constraints* constraints,
             continue;
         }
 
+        Rectf rect = solids->get_tile_bbox(x, y);
         if(tile->getAttributes() & Tile::SLOPE) { // slope tile
           AATriangle triangle;
-          Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
-          Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
           int slope_data = tile->getData();
           if (solids->get_drawing_effect() == VERTICAL_FLIP)
             slope_data = AATriangle::vertical_flip(slope_data);
-          triangle = AATriangle(p1, p2, slope_data);
+          triangle = AATriangle(rect, slope_data);
 
           collision::rectangle_aatriangle(constraints, dest, triangle, solids->get_movement());
         } else { // normal rectangular tile
-          Rectf rect(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset(), (x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
           check_collisions(constraints, movement, dest, rect, NULL, NULL, solids->get_movement());
         }
       }
@@ -1018,21 +1013,19 @@ Sector::collision_tile_attributes(const Rectf& dest) const
     TileMap* solids = *i;
 
     // test with all tiles in this rectangle
-    int starttilex = int(x1 - solids->get_x_offset()) / 32;
-    int starttiley = int(y1 - solids->get_y_offset()) / 32;
-    int max_x = int(x2 - solids->get_x_offset());
-    int max_y = int(y2+1 - solids->get_y_offset());
-    int max_y_ice = int(max_y + SHIFT_DELTA);
+    Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2));
+    // For ice (only), add a little fudge to recognize tiles Tux is standing on.
+    Rect test_tiles_ice = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2 + SHIFT_DELTA));
 
-    for(int x = starttilex; x*32 < max_x; ++x) {
+    for(int x = test_tiles.left; x < test_tiles.right; ++x) {
       int y;
-      for(y = starttiley; y*32 < max_y; ++y) {
+      for(y = test_tiles.top; y < test_tiles.bottom; ++y) {
         const Tile* tile = solids->get_tile(x, y);
         if(!tile)
           continue;
         result |= tile->getAttributes();
       }
-      for(; y*32 < max_y_ice; ++y) {
+      for(; y < test_tiles_ice.bottom; ++y) {
         const Tile* tile = solids->get_tile(x, y);
         if(!tile)
           continue;
@@ -1365,24 +1358,26 @@ Sector::is_free_of_tiles(const Rectf& rect, const bool ignoreUnisolid) const
     TileMap* solids = *i;
 
     // test with all tiles in this rectangle
-    int starttilex = int(rect.p1.x - solids->get_x_offset()) / 32;
-    int starttiley = int(rect.p1.y - solids->get_y_offset()) / 32;
-    int max_x = int(rect.p2.x - solids->get_x_offset());
-    int max_y = int(rect.p2.y - solids->get_y_offset());
+    Rect test_tiles = solids->get_tiles_overlapping(rect);
 
-    for(int x = starttilex; x*32 <= max_x; ++x) {
-      for(int y = starttiley; y*32 <= max_y; ++y) {
+    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;
+        if(!(tile->getAttributes() & Tile::SOLID))
+          continue;
+        if((tile->getAttributes() & Tile::UNISOLID) && ignoreUnisolid)
+          continue;
         if(tile->getAttributes() & Tile::SLOPE) {
           AATriangle triangle;
-          Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
-          Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
-          triangle = AATriangle(p1, p2, tile->getData());
+          Rectf tbbox = solids->get_tile_bbox(x, y);
+          triangle = AATriangle(tbbox, tile->getData());
           Constraints constraints;
-          if(collision::rectangle_aatriangle(&constraints, rect, triangle) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
+          if(!collision::rectangle_aatriangle(&constraints, rect, triangle))
+            continue;
         }
-        if((tile->getAttributes() & Tile::SOLID) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
+        // We have a solid tile that overlaps the given rectangle.
+        return false;
       }
     }
   }
@@ -1503,10 +1498,11 @@ Sector::inside(const Rectf& rect) const
 {
   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
     TileMap* solids = *i;
-    bool horizontally = ((rect.p2.x >= 0 + solids->get_x_offset()) && (rect.p1.x <= solids->get_width() * 32 + solids->get_x_offset()));
-    bool vertically = (rect.p1.y <= solids->get_height() * 32 + solids->get_y_offset());
 
-    if (horizontally && vertically)
+    Rectf bbox = solids->get_bbox();
+    bbox.p1.y = -INFINITY; // pretend the tilemap extends infinitely far upwards
+
+    if (bbox.contains(rect))
       return true;
   }
   return false;
@@ -1519,9 +1515,7 @@ Sector::get_width() const
   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
       i != solid_tilemaps.end(); i++) {
     TileMap* solids = *i;
-    if ((solids->get_width() * 32 + solids->get_x_offset()) > width) {
-      width = solids->get_width() * 32 + solids->get_x_offset();
-    }
+    width = std::max(width, solids->get_bbox().get_right());
   }
 
   return width;
@@ -1534,9 +1528,7 @@ Sector::get_height() const
   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
       i != solid_tilemaps.end(); i++) {
     TileMap* solids = *i;
-    if ((solids->get_height() * 32 + solids->get_y_offset()) > height) {
-      height = solids->get_height() * 32 + solids->get_y_offset();
-    }
+    height = std::max(height, solids->get_bbox().get_bottom());
   }
 
   return height;