cleanup Terrain, use a single linear array instead of nested vectors
authorTero Marttila <terom@fixme.fi>
Tue, 20 Jan 2009 22:00:24 +0200
changeset 406 a2e35ca66c74
parent 405 7a8c7a0a1261
child 407 443f6f7abcfb
cleanup Terrain, use a single linear array instead of nested vectors
src/Network/Client.cc
src/Network/Server.cc
src/Terrain.cc
src/Terrain.hh
--- a/src/Network/Client.cc	Sat Jan 17 02:58:36 2009 +0200
+++ b/src/Network/Client.cc	Tue Jan 20 22:00:24 2009 +0200
@@ -34,7 +34,7 @@
 
     // the terrain byte array
     size_t terrain_size = map_w * map_h;
-    uint8_t terrain_buf[map_w][map_h];
+    uint8_t terrain_buf[map_w * map_h];
 
     // read uncompressed terrain data
     size_t inflate_size = pkt.read_uncompressed(terrain_buf, terrain_size);
@@ -43,15 +43,12 @@
     if (inflate_size != terrain_size)
         throw Error("Corrupt terrain data");
 
-    // translate map data to terrain vector
-    for (PixelDimension x = 0; x < map_w; x++) {
-        for (PixelDimension y = 0; y < map_h; y++) {
-            terrain.terrain[x][y] = (TerrainType) terrain_buf[x][y];
-        }
-    }
+    // XXX: rework access
+    if (terrain.getDimensions() != PixelCoordinate(map_w, map_h))
+        throw Error("terrain is of the wrong size");
 
-    // update the pixbuf
-    terrain.generatePixelBuffer();
+    // load into terrain
+    terrain.loadFromBuffer(terrain_buf);
 }
         
 void NetworkClient::player_quit (NetworkClientRemotePlayer *player) {
--- a/src/Network/Server.cc	Sat Jan 17 02:58:36 2009 +0200
+++ b/src/Network/Server.cc	Tue Jan 20 22:00:24 2009 +0200
@@ -240,14 +240,9 @@
 
     // translate to a byte array
     size_t terrain_size = map.x * map.y;
-    uint8_t terrain_buf[map.x][map.y];
     
-    // copy over from terrain vector
-    for (PixelDimension x = 0; x < map.x; x++) {
-        for (PixelDimension y = 0; y < map.y; y++) {
-            terrain_buf[x][y] = (uint8_t) terrain.terrain[x][y];
-        }
-    }
+    // get terrain buffer
+    const uint8_t *terrain_buf = terrain.getTerrainBuffer();
 
     // allocate our packet...
     BigNetworkPacket pkt (
--- a/src/Terrain.cc	Sat Jan 17 02:58:36 2009 +0200
+++ b/src/Terrain.cc	Tue Jan 20 22:00:24 2009 +0200
@@ -19,205 +19,251 @@
 };
 
 Terrain::Terrain (void) :
-    map_width(0), map_height(0)
+    terrain_buf(NULL), width(0), height(0)
 {
 }
 
-Terrain::Terrain (PixelDimension map_width, PixelDimension map_height, int seed) :
-    terrain(map_width, std::vector<TerrainType>(map_height, TERRAIN_DIRT)),
-    map_width(map_width), 
-    map_height(map_height)
+Terrain::Terrain (PixelDimension width, PixelDimension height, int seed) :
+    terrain_buf(NULL),
+    width(width), 
+    height(height)
 {
+    // allocate+generate random terrain
     generateTerrain(seed);
 }
 
-Terrain::Terrain (const Terrain &t) 
-{
-    map_width = t.map_width;
-    map_height = t.map_height;
-    terrain = t.terrain;
+Terrain::~Terrain (void) {
+    // free terrain data
+    delete[] terrain_buf;
+}
 
+void Terrain::generateTerrain (int seed) {
+    // shouldn't be generated yet
+    assert(!terrain_buf);
+
+    // set random number generator seed.
+    srand(seed); 
+
+    // allocate terrain buffer
+    terrain_buf = new TerrainPixel[width * height];
+
+    // fill with dirt
+    memset(terrain_buf, TERRAIN_DIRT, width * height);
+
+    // some constants to control random generation
+    const int min_range = 25;
+    const int max_range = 80;
+    const int num = 50;
+    const int rock_rarity = 4;
+
+    // generate \a num random circles
+    for (int i = 0; i < num; i++) {
+        // circle origin
+        PixelCoordinate mid (rand() % width, rand() % height);
+
+        // radius
+        int range = rand() % (max_range - min_range) + min_range;
+
+        // circle type
+        TerrainType type = TERRAIN_EMPTY;
+
+        // tweak to make sure that there's a circle in the midle of the cave
+        if (i == 0) {
+            mid.x = width / 2;
+            mid.y = height / 2;
+            range = 150;
+
+        } else if (rand() % rock_rarity == 0) {
+            // some rock
+           type = TERRAIN_ROCK;
+        }
+
+        // iterate over the area of the circle
+        for (
+            PixelDimension y = std::max((PixelDimension) 0, mid.y - range);
+            y < std::min(height, mid.y + range);
+            y++
+        ) {
+            for (
+                PixelDimension x = std::max((PixelDimension) 0, mid.x - range); 
+                x < std::min(width, mid.x + range); 
+                x++
+            ) {
+                // inside radius?
+                if ((x - mid.x) * (x - mid.x) + (y - mid.y) * (y - mid.y) < range * range) 
+                    terrain_buf[y * width + x] = (TerrainPixel) type;
+            }
+        } 
+    }
+    
+    // update pixel buffer
     generatePixelBuffer();
 }
 
-/*
- * Texture generation util functions
- */
-static void fractal_step(std::vector<double>& land, int size, double str, int dist) {
-    for(int i = 0; i < size; i += dist*2) {
-        for(int j = dist; j < size; j += dist*2) {
-            double sum = 0;
-            sum += land[((i+size-dist)%size)+(j*size)];
-            sum += land[i+((j+size-dist)%size)*size];
-            sum += land[((i+dist)%size)+j*size];
-            sum += land[i+((j+dist)%size)*size];
-            land[i+j*size] = sum/4 + (rand()%10000-5000)*str;
-        }
-    }
-    for(int i = dist; i < size; i += dist*2) {
-        for(int j = 0; j < size; j += dist*2) {
-            double sum = 0;
-            sum += land[((i+size-dist)%size)+(j*size)];
-            sum += land[i+((j+size-dist)%size)*size];
-            sum += land[((i+dist)%size)+j*size];
-            sum += land[i+((j+dist)%size)*size];
-            land[i+j*size] = sum/4 + (rand()%10000-5000)*str;
+void Terrain::generatePixelBuffer (void) {
+    // initialize textures
+    generateTexture();
+
+    // create the pixel buffer of the correct size
+    pixbuf = CL_PixelBuffer(width, height, 4 * width, CL_PixelFormat::rgba8888);
+
+    // iterate over each pixel
+    for (PixelDimension x = 0; x < width; x++) {
+        for (PixelDimension y = 0; y < height; y++) {
+            // draw textureized pixel color
+            pixbuf.draw_pixel(x, y, getTexturePixel(x, y));
         }
     }
 }
 
-static void fractal_diamond(std::vector<double>& land, int size, double str, int dist) {
-    for(int i = dist; i < size; i += dist*2) {
-        for(int j = dist; j < size; j += dist*2) {
+/*
+ * Texture generation utility functions
+ */
+static void fractal_step (std::vector<double>& land, int size, double str, int dist) {
+    for (int i = 0; i < size; i += dist * 2) {
+        for (int j = dist; j < size; j += dist * 2) {
             double sum = 0;
-            sum += land[((i+size-dist)%size)+(((j+size-dist)%size)*size)];
-            sum += land[((i+dist)%size)+((j+size-dist)%size)*size];
-            sum += land[(i+size-dist)%size+((j+dist)%size)*size];
-            sum += land[(i+dist)%size+((j+dist)%size)*size];
-            land[i+j*size] = sum/4 + (rand()%10000-5000)*str;
+            sum += land[(i + size - dist) % size + j * size];
+            sum += land[i + ((j + size - dist) % size) * size];
+            sum += land[(i + dist) % size + j * size];
+            sum += land[i + ((j + dist) % size) * size];
+            land[i + j * size] = sum / 4 + (rand() % 10000 - 5000) * str;
+        }
+    }
+    for (int i = dist; i < size; i += dist * 2) {
+        for (int j = 0; j < size; j += dist * 2) {
+            double sum = 0;
+            sum += land[(i + size - dist) % size + j * size];
+            sum += land[i + ((j + size - dist) % size) * size];
+            sum += land[(i + dist) % size + j * size];
+            sum += land[i + ((j + dist)  % size) * size];
+            land[i + j * size] = sum / 4 + (rand() % 10000 - 5000) * str;
         }
     }
 }
 
-void Terrain::generate_texture (void) {
+static void fractal_diamond (std::vector<double>& land, int size, double str, int dist) {
+    for (int i = dist; i < size; i += dist*2) {
+        for (int j = dist; j < size; j += dist*2) {
+            double sum = 0;
+            sum += land[(i + size - dist) % size + ((j + size - dist) % size) * size];
+            sum += land[(i + dist) % size + ((j + size - dist) % size) * size];
+            sum += land[(i + size - dist) % size + ((j + dist) % size) * size];
+            sum += land[(i + dist) % size + ((j + dist) % size) * size];
+            land[i + j * size] = sum / 4 + (rand() % 10000 - 5000) * str;
+        }
+    }
+}
+
+void Terrain::generateTexture (void) {
+    // texture is a 128x128 px pattern
     int texturesize = 128;
+    
+    // store the generated texture
     texture = std::vector<std::vector<int> >(texturesize, std::vector<int>(texturesize));
-    std::vector<double> land(texture.size()*texture.size());
+    
+    // generate texture into this
+    std::vector<double> land(texture.size() * texture.size());
+
+    // XXX: magic constants with unintelligble names
     double str = 0.8;
     double H = 0.8;
-    for(int i = 512; i >= 1; i /= 2) {
+    
+    // do some magic
+    for (int i = 512; i >= 1; i /= 2) {
         fractal_diamond(land, texturesize, str, i);
         fractal_step(land, texturesize, str, i);
         str *= H;
     }
+
     double min = 100000;
     double max = -100000;
-    for(int i = 0; i < texturesize*texturesize; i++) {
-        if(land[i] < min) min = land[i];
-        if(land[i] > max) max = land[i];
+    
+    // find the range of minimum and maximum values
+    for (int i = 0; i < texturesize * texturesize; i++) {
+        if (land[i] < min) min = land[i];
+        if (land[i] > max) max = land[i];
     }
+    
+    // normalize min down to zero
     max -= min;
-    for(int i = 0; i < texturesize*texturesize; i++) {
+    
+    // normalize values to [0, max]
+    for (int i = 0; i < texturesize * texturesize; i++) {
         land[i] -= min;
     }
-    for(int i = 0; i < texturesize*texturesize; i++) {
-        land[i] = land[i]*255/max;
-        texture[i%texturesize][i/texturesize] = (int)(land[i]);
+    
+    // copy+mangle land to texture as integers
+    for (int i = 0; i < texturesize * texturesize; i++) {
+        land[i] = land[i] * 255 / max;
+        texture[i % texturesize][i / texturesize] = (int) land[i];
     }
 }
 
-/**
- * Changes color depending on x and y values
- * x and y should be valid coordinates (not outside)
- */
-void Terrain::noisifyPixel(CL_Color& color, PixelCoordinate pc) {
+static int normalizeRange (int min, int val, int max) {
+    if (val < min)
+        return min;
+
+    else if (val > max)
+        return max;
+
+    else 
+        return val;
+}
+
+CL_Color Terrain::getTexturePixel (PixelDimension x, PixelDimension y) {
+    CL_Color color;
     int texture_fade = 8;
-    switch (terrain[pc.x][pc.y]) {
-    case TERRAIN_EMPTY:
-        break;
-    case TERRAIN_DIRT:
-        texture_fade = 4;
-        break;
-    case TERRAIN_ROCK:
-        texture_fade = 2;
-        break;
+    
+    // determine fade constant
+    switch (getType(x, y)) {
+        case TERRAIN_EMPTY:
+            color = COLOR_EMPTY;
+            break;
+
+        case TERRAIN_DIRT:
+            color = COLOR_DIRT;
+            texture_fade = 4;
+            break;
+
+        case TERRAIN_ROCK:
+            color = COLOR_ROCK;
+            texture_fade = 2;
+            break;
     }
-    int tx = (pc.x + texture_fade * 37) % texture.size();
-    int ty = (pc.y + texture_fade * 37) % texture[0].size();
+    
+    // calculate texture coordinate
+    int tx = (x + texture_fade * 37) % texture.size();
+    int ty = (y + texture_fade * 37) % texture[0].size();
+    
+    // get color components
     int red = color.get_red();
     int green = color.get_green();
     int blue = color.get_blue();
-
+    
+    // apply noise from texture
     red += (texture[tx][ty] - 128) / texture_fade;
     green += (texture[tx][ty] - 128) / texture_fade;
     blue += (texture[tx][ty] - 128) / texture_fade;
-
-    if (red < 0)
-        red = 0;
-    else if (red >= 256)
-        red = 255;
-
-    if (green < 0)
-        green = 0;
-    else if (green >= 256)
-        green = 255;
-
-    if (blue < 0)
-        blue = 0;
-    else if (blue >= 256)
-        blue = 255;
-
-    color = CL_Color(red, green, blue);
+    
+    // normalize colors to within range and return new color    
+    return CL_Color(
+            normalizeRange(0, red, 255),
+            normalizeRange(0, green, 255),
+            normalizeRange(0, blue, 255)
+    );
 }
 
-/**
- * Sets to color the correct color of pixel in (x,y)
- */
-void Terrain::loadPixelColor(CL_Color& color, PixelCoordinate pc) {
-    if ((pc.x < 0) || (pc.y < 0) || (pc.x >= map_width) || (pc.y >= map_height)) {
-        color = CL_Color(0, 0, 0);
-        return;
-    }
-
-    switch (terrain[pc.x][pc.y]) {
-    case TERRAIN_EMPTY:
-        color = COLOR_EMPTY;
-        break;
-
-    case TERRAIN_DIRT:
-        color = COLOR_DIRT;
-        break;
-
-    case TERRAIN_ROCK:
-        color = COLOR_ROCK;
-        break;
-    }
-        
-    noisifyPixel(color, pc);
+const TerrainPixel* Terrain::getTerrainBuffer (void) const {
+    return terrain_buf;
 }
-
-void Terrain::generatePixelBuffer (void) {
-    // initialze texture
-    generate_texture();
-
-    // create pixel buffer
-    pixbuf = CL_PixelBuffer(map_width, map_height, 4 * map_width, CL_PixelFormat::rgba8888);
+    
+void Terrain::loadFromBuffer (const TerrainPixel *buf) {
+    // copy bytes
+    memcpy(terrain_buf, buf, width * height);
 
-    CL_Color color;
-
-    for (PixelDimension x = 0; x < map_width; x++) {
-        for (PixelDimension y = 0; y < map_height; y++) {
-            PixelCoordinate pc(x, y);
-
-            loadPixelColor(color, pc);
-
-            pixbuf.draw_pixel(pc.x, pc.y, color);
-        }
-    }
-}
-
-PixelCoordinate Terrain::getPixelCoordinate (Vector point) const {
-    // XXX: assume 1:1
-    return PixelCoordinate((int) point.x, (int) point.y);
-}
-
-PixelCoordinate Terrain::getDimensions (void) const {
-    return PixelCoordinate(map_width, map_height);
-}
-
-TerrainType Terrain::getType (PixelDimension px, PixelDimension py) const {
-    if ((px < 0) || (py < 0) ||(px >= map_width) || (py >= map_height))
-        return TERRAIN_ROCK;
-
-    return terrain[px][py];
-}
-
-TerrainType Terrain::getType (PixelCoordinate pc) const {
-    return getType(pc.x, pc.y);
-}
-
-TerrainType Terrain::getType (Vector point) const {
-    return getType((PixelDimension) point.x, (PixelDimension) point.y);
+    // regenerate pixbuf
+    generatePixelBuffer();
 }
 
 bool Terrain::collides (const Vector &point) const {
@@ -225,85 +271,74 @@
 }
 
 bool Terrain::collides (const Vector &begin, const Vector &end) const {
-    // TODO: Maybe we should have another function prototype that also
-    // returns the point where we collided.
+    // TODO: Maybe there should be another function that also returns the point where we collided.
 
-    // We'll use Bresenhams line algorithm to go trough all the
-    // "pixels" of the line.
+    // use Bresenhams line algorithm to go trough all the "pixels" of the line.
     PixelCoordinate b = getPixelCoordinate(begin);
     PixelCoordinate e = getPixelCoordinate(end);
 
+    // steepness of line (large angle against horizontal)
     bool steep = (abs(e.y - b.y) > abs(e.x - b.x)); // k > 1
-
+    
+    // if the line is steep, swap the x and y coordinates
     if (steep) { 
-        // Line is steep -> swap x and y coordinates
         std::swap(b.x, b.y);
         std::swap(e.x, e.y);
     }
-
-    if (b.x > e.x) { 
-        // Line goes down -> make it go up
+    
+    // normalize so that the line goes upwards
+    if (b.x > e.x)
         std::swap(b, e);
-    }
-
+    
+    // line length
     PixelDimension dx = e.x - b.x, dy = abs(e.y - b.y);
     PixelDimension err = dx / 2;
-    PixelDimension ystep, y = b.y;
-
-    // Is the line ascending or descending
-    if (b.y < e.y) 
-        ystep = 1;
-
-    else 
-        ystep = -1;
+    PixelDimension y = b.y;
+    
+    // ascending or descending line?
+    PixelDimension ystep = b.y < e.y ? 1 : -1;
 
-    // Go trough the line
-    for (PixelDimension x =  b.x; x <= e.x; x++) {
+    // iterate through the pixels on the line
+    for (PixelDimension x = b.x; x <= e.x; x++) {
         if (steep) { 
-            // X and Y coordinates must be switched if steep
-            if (getType(y, x) != TERRAIN_EMPTY) { 
-                // Collision!
+            // x and y coordinates must be switched if steep
+            if (getType(y, x) != TERRAIN_EMPTY)
                 return true;
-            }
+
         } else {
-            if (getType(x, y) != TERRAIN_EMPTY) { 
-                // Collision!
+            if (getType(x, y) != TERRAIN_EMPTY) 
                 return true;
-            }
+
         }
 
         err = err - dy;
 
         if (err < 0) { 
-            // Check if we want to make an ystep
+            // check if we want to make an ystep
             y = y + ystep;
             err = err + dx;
         }
     }
 
-    return false; // No Collision
+    // no collision
+    return false; 
 }
 
 void Terrain::removeGround (const Vector &pos, float radius) {
-    // TODO: Implement. Some circle algoritmh should be usefull here,
-    // though the current impelementation doesn't seem too bad either.
-
+    // scale arguments to pixel units
     PixelCoordinate mid = getPixelCoordinate(pos);
-    PixelDimension r = (unsigned int) radius; // XXX: scale
+    PixelDimension r = scale(radius); 
+    
+    // iterate through the circle's pixels
+    for (PixelDimension j = mid.y - r; j < mid.y + r; j++) {
+        for (PixelDimension i = mid.x - r; i < mid.x + r; i++) {
+            // getType also returns TERRAIN_ROCK if out-of-bounds
+            if (getType(i, j) != TERRAIN_ROCK) { 
 
-    for (PixelDimension i = mid.x - r; i < mid.x + r; i++) {
-        for (PixelDimension j = mid.y-r; j < mid.y+r; j++) {
-            PixelCoordinate pc(i, j);
-
-            if (getType(pc) != TERRAIN_ROCK) { 
-                // getType returns ROCK if out of bounds
-
+                // within radius?
                 if ((i - mid.x) * (i - mid.x) + (j - mid.y) * (j - mid.y) < r * r) {
-                    terrain[i][j] = TERRAIN_EMPTY;
-
-                    CL_Color color;
-                    loadPixelColor(color, pc);
-                    pixbuf.draw_pixel(pc.x, pc.y, color);
+                    // update terrain buf
+                    setType(i, j, TERRAIN_EMPTY);
                 }
             }
         }
@@ -339,12 +374,8 @@
     return 0;
 }
 
-/**
- * point should be ground and prevPoint air, but it's easy to assure that
- * @param point - pixel on ground to which was collided
- * @param prevPoint - pixel where we are when we collide
- */
 Vector Terrain::getNormal(Vector point, Vector prevPoint) const {
+    // XXX: cleanup
     PixelCoordinate p = getPixelCoordinate(point);
 
     assert(point != prevPoint);
@@ -375,66 +406,13 @@
     return normal;
 }
 
-// TODO: This could better :)
-// TODO: And this need some cleaning :)
-void Terrain::generateTerrain (int seed) {
-    srand(seed); // Set random number generator seed.
-
-    // Some constants to control random generation
-    const int min_range = 25;
-    const int max_range = 80;
-    const int num = 50;
-    const int rock_rarity = 4;
-
-    // Generate circles (or whatever)
-    for (int i = 0; i < num; i++) {
-        // Random generate circle attributes
-        PixelCoordinate mid(rand() % map_width, rand() % map_width);
-
-        int range = rand()%(max_range-min_range)+min_range;
-
-        // Make sure that there's a circle in the midle of the cave
-        if (i == 0) {
-            mid.x = map_width / 2;
-            mid.y = map_height / 2;
-            range = 150;
-        }
-
-        TerrainType type = TERRAIN_EMPTY;
-
-        if (rand() % rock_rarity == 0) {
-            type = TERRAIN_ROCK;
-        }
-
-        // Loops for every pixel of the cirlcle (or square as it seems
-        // now)
-        for (
-            PixelDimension x = std::max((PixelDimension) 0, mid.x - range); 
-            x < std::min(map_width, mid.x + range); 
-            x++
-        ) {
-            for (
-                PixelDimension y = std::max((PixelDimension) 0, mid.y - range);
-                y < std::min(map_height, mid.y + range);
-                y++
-            ) {
-                if ((x - mid.x) * (x - mid.x) + (y - mid.y) * (y - mid.y) < range * range) {
-                    terrain[x][y] = type;
-                }
-            }
-        } 
-    }
+void Terrain::draw (Graphics *g, PixelCoordinate camera) {
+    // XXX: can we optimize this somehow?
     
-    // regenerate pixel buffer
-    this->generatePixelBuffer();
-}
-
-void Terrain::draw (Graphics *g, PixelCoordinate camera) {
+    // load the terrain pixbuf as a surface
     CL_Surface surf (pixbuf);
+    
+    // draw it onto the graphics, offset by camera position
     surf.draw(-camera.x, -camera.y, g->get_gc());
 }
 
-std::vector<std::vector<TerrainType> > Terrain::getTerrain() const {
-    return terrain;
-}
-
--- a/src/Terrain.hh	Sat Jan 17 02:58:36 2009 +0200
+++ b/src/Terrain.hh	Tue Jan 20 22:00:24 2009 +0200
@@ -8,13 +8,26 @@
 #include "Types.hh"
 #include "Config.hh"
 
+/**
+ * Different types of terrain available
+ */
 enum TerrainType {
-    TERRAIN_EMPTY, 
-    TERRAIN_DIRT, 
-    TERRAIN_ROCK
+    /** Empty space, air */
+    TERRAIN_EMPTY   = 0x00,
+
+    /** Dirt, which can be destroyed/dug through */
+    TERRAIN_DIRT    = 0x01,
+
+    /** Indestructible rock */
+    TERRAIN_ROCK    = 0x02
 };
 
 /**
+ * Terrain "pixel" type
+ */
+typedef uint8_t TerrainPixel;
+
+/**
  * Terrain class. Represents game terrain and contains member
  * functions to manipulate terrain and get info about it.
  * 
@@ -25,57 +38,78 @@
  * and it uses pixelcoordinates internally.
  */ 
 class Terrain {
-public: // XXX: until we fix Network's access to this
-    std::vector<std::vector<TerrainType> > terrain;    
+protected:
+    /** The terrain data is stored as a linear array in row-major order, with width*height elements */
+    TerrainPixel *terrain_buf;
+
+    /** Terrain dimensions */
+    PixelDimension width, height;
     
-    /**
-     * Generates pixelbuffer from terrain. Should be used only on
-     * constructors because this is expected to be slow.
-     */
-    void generatePixelBuffer();
-
-protected:
-    // terrain dimensions
-    PixelDimension map_width, map_height;
-
-    // We can pre-render the terrain as a pixel buffer and then just modify this
+    /** We pre-render the textured terrain data for display */
     CL_PixelBuffer pixbuf;
 
-    // terrain texture
+    // XXX: terrain texture
     std::vector<std::vector<int> > texture;
 
     /**
      * Default constructor. The width/height are set to zero and the terrain is invalid until it gets updated
-     *
-     * @param scale The "real" width and height of the terrain.
      */
     Terrain (void);
 
     /**
      * Constructor.
      *
-     * @param scale The "real" width and height of the terrain.
-     * @param seed Random number generator seed used to create the
-     * terrain.
+     * @param width terrain width
+     * @param height terrain height
+     * @param seed andom number generator seed used to generate the random terrain.
      */
-    Terrain (PixelDimension map_width, PixelDimension map_height, int seed);
-
-    /**
-     * Copy constructor.
-     *
-     * @param t Terrain to be copied.
-     */
-    Terrain (const Terrain &t);
+    Terrain (PixelDimension width, PixelDimension height, int seed);
 
     /**
      * Destructor
      */
-    ~Terrain (void) {}
+    ~Terrain (void);
+
+private:
+    /* No copying */
+    Terrain (const Terrain &copy);
+    Terrain &operator= (const Terrain &copy);
 
 protected:
-    void generate_texture();
-    void noisifyPixel(CL_Color& col, PixelCoordinate pc);
+    /**
+     * Set induvidual pixel value, updates both terrain_buf and pixbuf
+     */
+    inline void setType (PixelDimension x, PixelDimension y, TerrainType t) {
+        terrain_buf[y * width + x] = (TerrainPixel) t;
 
+        // XXX: locking?
+        pixbuf.draw_pixel(x, y, getTexturePixel(x, y));
+    }
+
+    /**
+     * Generate random terrain using given seed
+     *
+     * @param seed seed for the random number generator
+     */
+    void generateTerrain (int seed);
+
+    /**
+     * Regenerates our pixbuf from the current terrain. This is a slow process, but using the pixbuffer directly
+     * greatly speeds up drawing.
+     */
+    void generatePixelBuffer (void);
+
+    /**
+     * Generates an area of random texture using a fractal-based algorithm, this can then be applied to the terrain
+     * pixels using noisifyPixel
+     */
+    void generateTexture();
+    
+    /**
+     * Applies noise generated using generateTexture to generate a slightly varying color for the terrain pixel
+     * at \a pc.
+     */
+    CL_Color getTexturePixel (PixelDimension x, PixelDimension y);
 
     /**
      * Scale parameter to "pixels"
@@ -83,12 +117,9 @@
      * @param x Scaled value
      * @return Corresponding value in pixels
      */
-    PixelDimension scale (float x) const;
-
-    /**
-     * Sets to color the correct color of pixel in (x,y)
-     */
-    void loadPixelColor (CL_Color& color, PixelCoordinate pc);
+    inline PixelDimension scale (float x) const {
+        return (int) x;
+    }
 
  public:
     /**
@@ -97,92 +128,94 @@
      * @param point Point in "real" units
      * @return Int vector
      */
-    PixelCoordinate getPixelCoordinate (Vector point) const;
+    inline PixelCoordinate getPixelCoordinate (Vector point) const {
+        return PixelCoordinate(scale(point.x), scale(point.y));
+    }
 
     /**
      * Return the terrain dimensions à la a PixelCoordinate
      */
-    PixelCoordinate getDimensions (void) const;
+    inline PixelCoordinate getDimensions (void) const {
+        return PixelCoordinate(width, height);
+    }
 
     /**
-     * Return the type of terrain at given position. Returns ROCK if
-     * given point is not inside terrain area.
+     * Return the type of terrain at given position. Returns TERRAIN_ROCK if given point is not inside terrain area.
      *
-     * @param x X coordinate
-     * @param y Y coordinate
-     * @return Terrain type
+     * @param x terrain x coordinate
+     * @param y terrain x coordinate
+     * @return terrain pixel type
      */
-    TerrainType getType (PixelDimension px, PixelDimension py) const;
-    TerrainType getType (PixelCoordinate pc) const;
-    TerrainType getType (Vector point) const;
+    TerrainType getType (PixelDimension x, PixelDimension y) const {
+        // XXX: optimize access by removing error checking?
+        if (x < 0 || y < 0 || x >= width || y >= height)
+            return TERRAIN_ROCK;
+
+        return (TerrainType) terrain_buf[y * width + x];
+    }
+
+    inline TerrainType getType (PixelCoordinate pc) const {
+        return getType(pc.x, pc.y);
+    }
+    
+    
+    inline TerrainType getType (Vector point) const {
+        return getType(getPixelCoordinate(point));
+    }
 
     /**
-     * Check if given point has some terrain.
+     * Get raw read-only terrain data buffer
      *
-     * @param point Point that is in scaled units.
+     * @see terrain_buf
+     */
+    const TerrainPixel* getTerrainBuffer (void) const;
+
+    /**
+     * Load new terrain data from given buffer. Must be the right size
+     *
+     * @see terrain_buf
+     */
+    void loadFromBuffer (const TerrainPixel *buf);
+
+    /**
+     * Check if the given point is terrain
      */
     bool collides (const Vector &point) const;
+
     /**
-     * Check if given line collides with terrain.
+     * Check if the given line collides with terrain
      *
-     * @param begin Line begin point in scaled units.
-     * @param end Line end point in scaled units.
+     * @param begin line starting point
+     * @param end line ending point
      */
     bool collides (const Vector &begin, const Vector &end) const;
     
     /**
-     * Remove a circular area from terrain.
+     * Remove a circular area of terrain
      *
-     * @param pos Circle center
-     * @param r Circle radius
+     * @param pos circle center
+     * @param r circle radius
      */ 
     void removeGround (const Vector &pos, float r);
 
     /**
-     * Return normal for the given point.
+     * Returns direction normal vector for the given \a point. 
      *
-     * @param point Point for which the normal is calculated.
-     * @return Normal vector ((0,0) if there's no terrain)
+     * \a point should be ground, and \a prevPoint air.
+     *
+     * @param point the ground that we collided into
+     * @param prevPoint position in air where we were before the collision
+     * @return direction ormal vector, or (0,0) if there's no terrain
      */
     Vector getNormal (Vector point, Vector prevPoint) const;
 
     /**
-     * Generate random terrain.
+     * Draw the terrain onto the given graphics context
      *
-     * @param seed Seed for the randomnumber generator.
-     */
-    void generateTerrain (int seed);
-
-    /**
-     * Draw the terrain for given graphicscontext.
-     *
-     * @param gc CL_GraphicContext
-     * @param camera - upper left corner of screen
+     * @param gc Graphics to draw on
+     * @param camera view position
      */
     virtual void draw (Graphics *g, PixelCoordinate camera = PixelCoordinate(0, 0));
-
-    /**
-     * Draw part of the terrain for given graphiscontext.
-     *
-     * @param gc CL_GraphicContext
-     * @param center Center of the rectangle drawn.
-     * @param dimension Dimensions of the rectangle.
-     */
-    //void draw(CL_GraphicContext &gc, Vector center, Vector dimensions);
-
-    /**
-     * Set terrain.
-     *
-     * @param terrain Terrain.
-     */
-    void setTerrain (const std::vector<std::vector<TerrainType> > &terrain);
-    
-    /**
-     * Get terrain.
-     *
-     * @return Terrain.
-     */
-    std::vector<std::vector<TerrainType> > getTerrain() const;
 };
 
 #endif