src/Terrain.hh
author Tero Marttila <terom@fixme.fi>
Wed, 21 Jan 2009 00:21:42 +0200
changeset 409 1a03ff151abc
parent 408 e6cfc44266af
child 411 106aaf6eadfe
permissions -rw-r--r--
add --terrain-seed and --terrain-size arguments, plus bugfixes
#ifndef TERRAIN_HH
#define TERRAIN_HH

#include "Vector.hh"
#include "GraphicsPointer.hh"
#include "Types.hh"
#include "Config.hh"

/**
 * Different types of terrain available
 */
enum TerrainType {
    /** 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 configuration
 */
struct TerrainConfig {
    /** Size of the terrain field*/
    PixelCoordinate dimensions;

    /** Set to nonzero to generate random map */
    int random_seed;
    
    /** Defaults */
    TerrainConfig (void) : dimensions(TERRAIN_WIDTH, TERRAIN_HEIGHT), random_seed(TERRAIN_RANDOM_SEED) { }
};

#include <vector>

/**
 * Terrain class. Represents game terrain and contains member
 * functions to manipulate terrain and get info about it.
 * 
 * Terrain resolution is a constant that is defined in
 * configuration. Terrain has a scale (i.e. the width and height in
 * "real" units. Scaling is needed because physics simulation uses
 * "real" units. The idea is that this class is used with "real" units
 * and it uses pixelcoordinates internally.
 */ 
class 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;
    
    /** We pre-render the textured terrain data for display */
    CL_PixelBuffer pixbuf;

    // XXX: terrain texture
    std::vector<std::vector<int> > texture;

public:    
    /**
     * Construct a new terrain based on the given configuration
     *
     * @param config a TerrainConfig describing how to build the terrain
     */
    explicit Terrain (const TerrainConfig &config);

    /**
     * Construct the terrain using the provided terrain data. The given \a terrain_buf must be a linear array in the
     * same format as Terrain::terrain_buf, meaning a row-major order array with width * height elements. The buffer
     * must be allocated on the heap using 'new []', and ownership will be transferred (i.e. the buffer is not copied,
     * and will eventually be delete []'d).
     *
     * @param width terrain width
     * @param height terrain height
     * @param terrain_buf dynamically allocated width * height array of terrain data
     */
    Terrain (PixelDimension width, PixelDimension height, TerrainPixel *terrain_buf);

    /**
     * Destructor, frees our terrain buffer
     */
    ~Terrain (void);

private:
    /* No copying */
    Terrain (const Terrain &copy);
    Terrain &operator= (const Terrain &copy);

protected:
    /**
     * 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"
     *
     * @param x Scaled value
     * @return Corresponding value in pixels
     */
    inline PixelDimension scale (float x) const {
        return (int) x;
    }

 public:
    /**
     * Get pixel location of a point that is in "real" units.
     *
     * @param point Point in "real" units
     * @return Int vector
     */
    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 {
        return PixelCoordinate(width, height);
    }

    /**
     * Return dimensions in component form
     */
    PixelDimension getWidth (void) const {
        return width;
    }

    PixelDimension getHeight (void) const {
        return height;
    }

    /**
     * Return the type of terrain at given position. Returns TERRAIN_ROCK if given point is not inside terrain area.
     *
     * @param x terrain x coordinate
     * @param y terrain x coordinate
     * @return terrain pixel type
     */
    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));
    }

    /**
     * Get raw read-only terrain data buffer
     *
     * @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 the given line collides with terrain
     *
     * @param begin line starting point
     * @param end line ending point
     */
    bool collides (const Vector &begin, const Vector &end) const;
    
    /**
     * Remove a circular area of terrain
     *
     * @param pos circle center
     * @param r circle radius
     */ 
    void removeGround (const Vector &pos, float r);

    /**
     * Returns direction normal vector for the given \a point. 
     *
     * \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;

    /**
     * Draw the terrain onto the given graphics context
     *
     * @param gc Graphics to draw on
     * @param camera view position
     */
    virtual void draw (Graphics *g, PixelCoordinate camera = PixelCoordinate(0, 0));
};

#endif