From bc7cdb32068257a8dd841873d25bb3993898a147 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sat, 30 Jan 2010 12:44:05 +0000 Subject: [PATCH] Cleanups related to tilemap positions: - 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 | 4 +- src/math/aatriangle.hpp | 23 +++------- src/object/particlesystem_interactive.cpp | 8 ++-- src/object/tilemap.cpp | 52 ++++++++++++---------- src/object/tilemap.hpp | 30 ++++++++----- src/supertux/collision.cpp | 20 ++++----- src/supertux/sector.cpp | 74 ++++++++++++++----------------- 7 files changed, 101 insertions(+), 110 deletions(-) diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index e794ce4f4..9e5c545b9 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -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)); diff --git a/src/math/aatriangle.hpp b/src/math/aatriangle.hpp index 6eea71f14..db0ca5dad 100644 --- a/src/math/aatriangle.hpp +++ b/src/math/aatriangle.hpp @@ -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; }; diff --git a/src/object/particlesystem_interactive.cpp b/src/object/particlesystem_interactive.cpp index 331884bf3..9923c03c7 100644 --- a/src/object/particlesystem_interactive.cpp +++ b/src/object/particlesystem_interactive.cpp @@ -82,6 +82,7 @@ ParticleSystem_Interactive::collision(Particle* object, Vector movement) for(std::list::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; diff --git a/src/object/tilemap.cpp b/src/object/tilemap.cpp index 4a144e639..7dce250ab 100644 --- a/src/object/tilemap.cpp +++ b/src/object/tilemap.cpp @@ -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 diff --git a/src/object/tilemap.hpp b/src/object/tilemap.hpp index 8ada93655..9c1f76ed7 100644 --- a/src/object/tilemap.hpp +++ b/src/object/tilemap.hpp @@ -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; diff --git a/src/supertux/collision.cpp b/src/supertux/collision.cpp index f3dc04168..8ac482057 100644 --- a/src/supertux/collision.cpp +++ b/src/supertux/collision.cpp @@ -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); diff --git a/src/supertux/sector.cpp b/src/supertux/sector.cpp index e0ebde1a6..8c96095a3 100644 --- a/src/supertux/sector.cpp +++ b/src/supertux/sector.cpp @@ -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::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::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::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; -- 2.11.0