src/Terrain.cc
changeset 185 25becd2cb026
child 204 4c386e9c950f
equal deleted inserted replaced
184:561892e2a30e 185:25becd2cb026
       
     1 #include "Terrain.hh"
       
     2 #include "Engine.hh"
       
     3 
       
     4 #include <cmath>
       
     5 #include <cassert>
       
     6 #include <algorithm>
       
     7 #include <ClanLib/display.h>
       
     8 
       
     9 Terrain::Terrain() {}
       
    10 Terrain::Terrain(const int &seed) 
       
    11     : terrain(MAP_WIDTH, std::vector<TerrainType>(MAP_HEIGHT, DIRT)){
       
    12     this->generateTerrain(seed);
       
    13 }
       
    14 Terrain::Terrain(const Terrain &t) {
       
    15     this->terrain = t.getTerrain();
       
    16     this->generatePixelBuffer();
       
    17 }
       
    18 
       
    19 void Terrain::generatePixelBuffer() {
       
    20     this->pixbuf = CL_PixelBuffer(MAP_WIDTH, MAP_HEIGHT, 4*MAP_WIDTH, 
       
    21                                   CL_PixelFormat::rgba8888);
       
    22 
       
    23     CL_Color color;
       
    24     for (uint16_t i = 0; i < MAP_WIDTH; i++) {
       
    25         for (uint16_t j = 0; j < MAP_HEIGHT; j++) {
       
    26             switch(terrain[i][j]) {
       
    27             case EMPTY:
       
    28                 color = COLOR_EMPTY;
       
    29                 break;
       
    30             case DIRT:
       
    31                 color = COLOR_DIRT;
       
    32                 break;
       
    33             case ROCK:
       
    34                 color = COLOR_ROCK;
       
    35                 break;
       
    36             default: // TODO: Shouldn't be here.
       
    37                 break; 
       
    38             }
       
    39             this->pixbuf.draw_pixel(i, j, color);
       
    40         }
       
    41     }
       
    42 }
       
    43 
       
    44 Vector Terrain::getPixelLocation(Vector point) const{
       
    45     return Vector(scale(point.x), 
       
    46                   scale(point.y));
       
    47 }
       
    48 
       
    49 uint16_t Terrain::scale(float x) const {
       
    50     return (uint16_t)(x/MAP_SCALE);
       
    51 }
       
    52 
       
    53 TerrainType Terrain::getType(int32_t x, int32_t y) const {
       
    54     if ((x < 0) || (y < 0) ||(x >= MAP_WIDTH) || (y >= MAP_HEIGHT)) {
       
    55         return ROCK;
       
    56     }
       
    57     return terrain[x][y];
       
    58 }
       
    59 TerrainType Terrain::getType(Vector point) const {
       
    60     return getType((int32_t)point.x, (int32_t)point.y);
       
    61 }
       
    62 
       
    63 bool Terrain::collides(const Vector &point) const {
       
    64     Vector coor = getPixelLocation(point);
       
    65     return (getType(coor.x, coor.y) != EMPTY);
       
    66 }
       
    67 
       
    68 bool Terrain::collides(const Vector &begin, const Vector &end) const {
       
    69     // TODO: Maybe we should have another function prototype that also
       
    70     // returns the point where we collided.
       
    71 
       
    72     // We'll use Bresenhams line algorithm to go trough all the
       
    73     // "pixels" of the line.
       
    74     Vector b = getPixelLocation(begin);
       
    75     Vector e = getPixelLocation(end);
       
    76 
       
    77     bool steep = (abs(e.y - b.y) > abs(e.x - b.x)); // k > 1
       
    78     if (steep) { // Line is steep -> swap x and y coordinates
       
    79         std::swap(b.x, b.y);
       
    80         std::swap(e.x, e.y);
       
    81     }
       
    82     if (b.x > e.x) { // Line goes down -> make it go up
       
    83         std::swap(b, e);
       
    84     }
       
    85     uint16_t dx = e.x - b.x;
       
    86     uint16_t dy = abs(e.y - b.y);
       
    87     int32_t err = dx/2;
       
    88     uint16_t ystep;
       
    89     uint16_t y = b.y;
       
    90     // Is the line ascending or descending
       
    91     if (b.y < e.y) ystep = 1;
       
    92     else ystep = -1;
       
    93     // Go trough the line
       
    94     for (uint16_t x =  b.x; x <= e.x; x++) {
       
    95         if (steep) { // X and Y coordinates must be switched if steep
       
    96             if (getType(y,x) != EMPTY) { // Collision!
       
    97                 return true;
       
    98             }
       
    99         } else {
       
   100             if (getType(x,y) != EMPTY) { // Collision!
       
   101                 return true;
       
   102             }
       
   103         }
       
   104         err = err - dy;
       
   105         if (err < 0) { // Check if we want to make an ystep
       
   106             y = y + ystep;
       
   107             err = err + dx;
       
   108         }
       
   109     }
       
   110     return false; // No Collision
       
   111 }
       
   112 
       
   113 void Terrain::removeGround(const Vector &pos, const float &radius) {
       
   114     // TODO: Implement. Some circle algoritmh should be usefull here,
       
   115     // though the current impelementation doesn't seem too bad either.
       
   116 
       
   117     Vector mid = getPixelLocation(pos);
       
   118     uint16_t r = scale(radius);
       
   119     for (uint16_t i = mid.x-r; i < mid.x+r; i++) {
       
   120         for (uint16_t j = mid.y-r; j < mid.y+r; j++) {
       
   121             if (getType(i, j) != ROCK) { // getType returns ROCK if
       
   122                                          // out of bounds
       
   123                 if ((i-mid.x)*(i-mid.x)+(j-mid.y)*(j-mid.y) < r*r) {
       
   124                     terrain[i][j] = EMPTY;
       
   125                     pixbuf.draw_pixel(i, j, COLOR_EMPTY);
       
   126                 }
       
   127             }
       
   128         }
       
   129     }
       
   130 }
       
   131 
       
   132 /**
       
   133  * Gets the index of the given coordinate direction
       
   134  * referring to the DIRECTIONS table in Physics.hh
       
   135  */
       
   136 int getDirectionIndex (Vector direction) {
       
   137     Vector dir = direction.roundToInt();
       
   138     if(dir.x == 0 && dir.y == -1) {
       
   139         return 0;
       
   140     } else if(dir.x == 1 && dir.y == -1) {
       
   141         return 1;
       
   142     } else if(dir.x == 1 && dir.y == 0) {
       
   143         return 2;
       
   144     } else if(dir.x == 1 && dir.y == 1) {
       
   145         return 3;
       
   146     } else if(dir.x == 0 && dir.y == 1) {
       
   147         return 4;
       
   148     } else if(dir.x == -1 && dir.y == 1) {
       
   149         return 5;
       
   150     } else if(dir.x == -1 && dir.y == 0) {
       
   151         return 6;
       
   152     } else if(dir.x == -1 && dir.y == -1) {
       
   153         return 7;
       
   154     }
       
   155     Engine::log(DEBUG, "Terrain.getDirectionIndex ") << "invalid direction: " << direction;
       
   156     return 0;
       
   157 }
       
   158 
       
   159 /**
       
   160  * point should be ground and prevPoint air, but it's easy to assure that
       
   161  * @param point - pixel on ground to which was collided
       
   162  * @param prevPoint - pixel where we are when we collide
       
   163  */
       
   164 Vector Terrain::getNormal(Vector point, Vector prevPoint) const {
       
   165     Vector p = getPixelLocation(point);
       
   166 
       
   167     assert(point != prevPoint);
       
   168 
       
   169     Vector normal(0,0);
       
   170 
       
   171     // These two must be rounded separately
       
   172     int dirIdx = getDirectionIndex(prevPoint.roundToInt() - point.roundToInt());
       
   173 //    dirIdx = (dirIdx+4)%8;
       
   174 
       
   175     std::cout << (prevPoint.roundToInt()) - (point.roundToInt()) << prevPoint-point << std::endl;
       
   176 
       
   177     normal += DIRECTIONS[dirIdx];
       
   178     for(int i = 1; i <= 2; i++) {
       
   179         if(getType(point + DIRECTIONS[(dirIdx+i+8)%8]) == EMPTY) {
       
   180             normal += DIRECTIONS[(dirIdx+i+8)%8];
       
   181         }
       
   182     }
       
   183     for(int i = 1; i <= 2; i++) {
       
   184         if(getType(point + DIRECTIONS[(dirIdx-i+8)%8]) == EMPTY) {
       
   185             normal += DIRECTIONS[(dirIdx-i+8)%8];
       
   186         }
       
   187     }
       
   188 
       
   189     Engine::log(DEBUG, "Physics.getNormal ") << "normal: " << normal << "   dirIdx: " << dirIdx;
       
   190 
       
   191     if(getType(point) == EMPTY || getType(prevPoint) != EMPTY) {
       
   192         Engine::log(DEBUG, "Physics.getNormal ") << "logic ground error";
       
   193     }
       
   194     
       
   195 
       
   196 //    for (int i = 0; i < 8; i++) {
       
   197 //        if (getType(p.x+DIRECTIONS[i].x, p.y+DIRECTIONS[i].y) == EMPTY) {
       
   198 //            normal += DIRECTIONS[i];
       
   199 //        }
       
   200 //    }
       
   201 
       
   202    
       
   203     // Special cases
       
   204     /*    Vector tmp = direction(direction(prevPoint-point) + direction(normal));
       
   205     Engine::log(DEBUG, "Terrain.getNormal") << "tmp: " << tmp;
       
   206     if (normal.length() == 0 || (tmp.x != 0 && tmp.y != 0 && getType(tmp.x, tmp.y) != EMPTY)) 
       
   207         normal = prevPoint - point; // Direct hit
       
   208     */
       
   209 //    Engine::log(DEBUG, "Terrain.getNormal") << "Normal: " << normal;
       
   210     return normal;
       
   211     return Vector(0,-1);
       
   212 }
       
   213 
       
   214 Vector direction(const Vector &v) {
       
   215     Vector tmp(v);
       
   216     if (tmp.length() > 0) tmp /= tmp.length();
       
   217     tmp.x = (uint16_t)(tmp.x);
       
   218     tmp.y = (uint16_t)(tmp.y);
       
   219     return tmp;
       
   220 }
       
   221 
       
   222 // TODO: This could better :)
       
   223 // TODO: And this need some cleaning :)
       
   224 void Terrain::generateTerrain(int seed) {
       
   225     srand(seed); // Set random number generator seed.
       
   226 
       
   227     // Some constants to control random generation
       
   228     const int min_range = 25;
       
   229     const int max_range = 80;
       
   230     const int num = 50;
       
   231     const int rock_rarity = 4;
       
   232 
       
   233     // Generate circles (or whatever)
       
   234     for (int i = 0; i < num; i++) {
       
   235         // Random generate circle attributes
       
   236         int midx = rand()%MAP_WIDTH;
       
   237         int midy = rand()%MAP_HEIGHT;
       
   238         int range = rand()%(max_range-min_range)+min_range;
       
   239 
       
   240         // Make sure that there's a circle in the midle of the cave
       
   241         if (i == 0) {
       
   242             midx = MAP_WIDTH/2;
       
   243             midy = MAP_WIDTH/2;
       
   244             range = 150;
       
   245         }
       
   246 
       
   247         TerrainType type = EMPTY;
       
   248         if (rand()%rock_rarity == 0) {
       
   249             type = ROCK;
       
   250         }
       
   251 
       
   252         // Loops for every pixel of the cirlcle (or square as it seems
       
   253         // now)
       
   254         for (int x = std::max(0, midx-range); 
       
   255              x < std::min((int32_t)MAP_WIDTH, midx+range); 
       
   256              x++) {
       
   257             for (int y = std::max(0, midy-range);
       
   258                     y < std::min((int32_t)MAP_HEIGHT, midy+range);
       
   259                     y++) {
       
   260                 
       
   261                 //terrain[x][y] = type;
       
   262 
       
   263                 if ((x-midx)*(x-midx)+(y-midy)*(y-midy) < range*range) {
       
   264                     terrain[x][y] = type;
       
   265                 }
       
   266             }
       
   267             
       
   268         } 
       
   269         
       
   270     }
       
   271     
       
   272     this->generatePixelBuffer();
       
   273 }
       
   274 
       
   275 void Terrain::draw(CL_GraphicContext *gc) {
       
   276     CL_Surface surf(this->pixbuf);
       
   277     surf.draw(0,0,gc);
       
   278 }
       
   279 
       
   280 std::vector<std::vector<TerrainType> > Terrain::getTerrain() const {
       
   281     return terrain;
       
   282 }
       
   283