# HG changeset patch # User John Tsiombikas # Date 1338091248 -10800 # Node ID 2e38715de41be25a001878ab3a342f00939da465 # Parent cd12944a8ea842c423c6d7f194223cefe1297fb7 terrain diff -r cd12944a8ea8 -r 2e38715de41b level/src/level.cc --- a/level/src/level.cc Fri May 25 05:28:20 2012 +0300 +++ b/level/src/level.cc Sun May 27 07:00:48 2012 +0300 @@ -1,8 +1,9 @@ #include #include +#include "opengl.h" #include "level.h" #include "datapath.h" -#include "opengl.h" +#include "terrain.h" using namespace tinyxml2; @@ -34,20 +35,34 @@ XMLNode *node = xml.RootElement()->FirstChild(); do { XMLElement *elem = node->ToElement(); - if(elem) { - if(strcmp(elem->Name(), "map") == 0) { - LevelMap map; + if(!elem) { + fprintf(stderr, "ignoring non-element child of \n"); + continue; + } - if(!map.load(elem)) { - pop_data_path(); - return false; - } - levelmaps[map.get_name()] = std::move(map); + if(strcmp(elem->Name(), "terrain") == 0) { + if(terrain) { + fprintf(stderr, " must only have a single terrain child! ignoring\n"); + continue; + } - } else { - fprintf(stderr, "ignoring unrecognized element: %s\n", elem->Name()); + terrain = new Terrain; + if(!terrain->load(elem)) { + fprintf(stderr, "failed to load terrain\n"); + pop_data_path(); + return false; } + + float val; + if(elem->QueryFloatAttribute("size", &val) == 0) { + printf("terrain size: %f\n", val); + terrain->set_size(val); + } + } else { + fprintf(stderr, "ignoring unexpected child of : %s\n", elem->Name()); + continue; } + } while((node = node->NextSibling())); pop_data_path(); @@ -56,148 +71,5 @@ void Level::draw() const { - auto iter = levelmaps.find("height"); - if(iter == levelmaps.end()) { - return; - } - - img_pixmap *hmap = (img_pixmap*)iter->second.get_pixmap(); - - glBegin(GL_POINTS); - for(int i=0; iheight; i++) { - for(int j=0; jwidth; j++) { - int height; - img_getpixel1i(hmap, j, i, &height); - - float x = (float)j / (float)hmap->width - 0.5; - float y = height / 255.0; - float z = (float)i / (float)hmap->height - 0.5; - - glColor3f(y, 0.0, 1.0 - y); - glVertex3f(x, y, z); - } - } - glEnd(); + terrain->draw(); } - - -LevelMap::LevelMap() -{ - init(); -} - -void LevelMap::init() -{ - scale = 1.0f; - img_init(&img); - name = 0; -} - -LevelMap::~LevelMap() -{ - destroy(); -} - -void LevelMap::destroy() -{ - if(img.pixels) { - img_destroy(&img); - } - delete [] name; -} - -// copy -LevelMap::LevelMap(const LevelMap &map) -{ - init(); - *this = map; -} - -LevelMap &LevelMap::operator =(const LevelMap &map) -{ - if(this != &map) { - destroy(); - - scale = map.scale; - img_init(&img); - img_copy(&img, (img_pixmap*)&map.img); - - name = new char[strlen(map.name) + 1]; - strcpy(name, map.name); - } - return *this; -} - -// move semantics -LevelMap::LevelMap(LevelMap &&map) -{ - init(); - *this = std::move(map); -} - -LevelMap &LevelMap::operator =(LevelMap &&map) -{ - if(this != &map) { - destroy(); - - scale = map.scale; - - img = map.img; - map.img.pixels = 0; - map.img.name = 0; - - name = map.name; - map.name = 0; - } - - return *this; -} - -bool LevelMap::load(XMLElement *xelem) -{ - char fname[PATH_MAX]; - - const char *attr = xelem->Attribute("type"); - if(!attr) { - fprintf(stderr, "map element without a type attribute\n"); - return false; - } - name = new char[strlen(attr) + 1]; - strcpy(name, attr); - - attr = xelem->Attribute("file"); - if(!attr) { - fprintf(stderr, "map element without a file attribute\n"); - return false; - } - if(!find_file(attr, fname)) { - fprintf(stderr, "failed to locate image: %s\n", attr); - return false; - } - - if(img_load(&img, fname) == -1) { - fprintf(stderr, "failed to load image: %s\n", fname); - return false; - } - - float val; - if(xelem->QueryFloatAttribute("scale", &val) == 0) { - scale = val; - } - return true; -} - -float LevelMap::get_scale() const -{ - return scale; -} - -const img_pixmap *LevelMap::get_pixmap() const -{ - return &img; -} - -const char *LevelMap::get_name() const -{ - return name; -} diff -r cd12944a8ea8 -r 2e38715de41b level/src/level.h --- a/level/src/level.h Fri May 25 05:28:20 2012 +0300 +++ b/level/src/level.h Sun May 27 07:00:48 2012 +0300 @@ -6,12 +6,12 @@ #include #include "tinyxml2.h" -class LevelMap; +class Terrain; class Level { private: tinyxml2::XMLDocument xml; - std::map levelmaps; + Terrain *terrain; public: Level(); @@ -22,31 +22,4 @@ void draw() const; }; -class LevelMap { -private: - float scale; - img_pixmap img; - char *name; - - void init(); - void destroy(); - -public: - LevelMap(); - ~LevelMap(); - - LevelMap(const LevelMap &map); - LevelMap &operator =(const LevelMap &map); - - // move constructor/op= - LevelMap(LevelMap &&map); - LevelMap &operator =(LevelMap &&map); - - bool load(tinyxml2::XMLElement *xelem); - - float get_scale() const; - const img_pixmap *get_pixmap() const; - const char *get_name() const; -}; - #endif // LEVEL_H_ diff -r cd12944a8ea8 -r 2e38715de41b level/src/terrain.cc --- a/level/src/terrain.cc Fri May 25 05:28:20 2012 +0300 +++ b/level/src/terrain.cc Sun May 27 07:00:48 2012 +0300 @@ -1,8 +1,16 @@ +#include +#include +#include "opengl.h" +#include #include "terrain.h" +#include "datapath.h" + +using namespace tinyxml2; Terrain::Terrain() { root = 0; + size = 2; } Terrain::~Terrain() @@ -10,16 +18,36 @@ delete root; } +bool Terrain::load(XMLElement *xelem) +{ + root = new TerrainNode; + return root->load(xelem); +} + +void Terrain::set_size(float sz) +{ + size = sz; +} + float Terrain::get_height(float x, float y) const { return root->get_height(x, y); } +void Terrain::draw() const +{ + root->draw(0, 0, size, size); +} + + +// ---- terrain quadtree node ---- TerrainNode::TerrainNode() { - height = 0; - child[0] = child[1] = child[2] = child[3]; + map_xsz = map_ysz = 0; + map_scale = 1.0; + hmap = 0; + child[0] = child[1] = child[2] = child[3] = 0; } TerrainNode::~TerrainNode() @@ -27,27 +55,177 @@ for(auto c : child) { delete c; } - delete [] height; + img_free_pixels(hmap); +} + +bool TerrainNode::load(XMLElement *xelem) +{ + XMLNode *node = xelem->FirstChild(); + do { + XMLElement *celem = node->ToElement(); + if(!celem) { + fprintf(stderr, "ignoring non-element child of \n"); + continue; + } + + const char *attr, *name = celem->Name(); + + if(strcmp(name, "terrain") == 0) { + // child is a recursive terrain element + int cidx = 0; + if((attr = celem->Attribute("index"))) { + if(celem->QueryIntAttribute("index", &cidx) != 0) { + fprintf(stderr, "invalid index attribute in terrain: %s\n", attr); + return false; + } + } + + if(child[cidx]) { + fprintf(stderr, "duplicate terrain child with index %d in terrain\n", cidx); + return false; + } + + child[cidx] = new TerrainNode; + if(!child[cidx]->load(celem)) { + return false; + } + } else if(strcmp(name, "map") == 0) { + // child is an map of some kind + // XXX for now we only care about heightmaps + + if((attr = celem->Attribute("type")) && strcmp(attr, "height") != 0) { + fprintf(stderr, "ignoring map of type %s in terrain\n", attr); + continue; + } + + if((attr = celem->Attribute("file"))) { + char fname[PATH_MAX]; + + if(!find_file(attr, fname)) { + fprintf(stderr, "failed to locate map: %s\n", attr); + return false; + } + + if(!load_heightmap(fname)) { + fprintf(stderr, "failed to load map: %s\n", fname); + return false; + } + } else { + fprintf(stderr, "ignoring map without file attribute in terrain\n"); + continue; + } + + float val; + if(celem->QueryFloatAttribute("scale", &val) == 0) { + map_scale = val; + } + } + } while((node = node->NextSibling())); + + return true; +} + + +bool TerrainNode::load_heightmap(const char *fname) +{ + img_free_pixels(hmap); + + if(!(hmap = (unsigned char*)img_load_pixels(fname, &map_xsz, &map_ysz, IMG_FMT_GREY8))) { + fprintf(stderr, "failed to load heightmap: %s\n", fname); + return false; + } + return true; +} + +bool TerrainNode::is_leaf() const +{ + if(!child[0]) { + assert(!child[1] && !child[2] && !child[3]); + return true; + } + return false; +} + +static float child_coord(float x) +{ + return (x - 0.5) * 2; +} + +static int quadrant(float x, float y) +{ + int xpos = x >= 0 ? 1 : 0; + int ypos = y >= 0 ? 1 : 0; + return ypos * 2 + xpos; } TerrainNode *TerrainNode::get_child(float x, float y) { - int xidx = x >= 0.0 ? 1 : 0; - int yidx = y >= 0.0 ? 1 : 0; - return child[y * 2 + x]; + if(is_leaf()) { + return this; + } + + int cidx = quadrant(x, y); + int cx = child_coord(x); + int cy = child_coord(y); + + return child[cidx]->get_child(cx, cy); } const TerrainNode *TerrainNode::get_child(float x, float y) const { - int xidx = x >= 0.0 ? 1 : 0; - int yidx = y >= 0.0 ? 1 : 0; - return child[y * 2 + x]; + return ((TerrainNode*)this)->get_child(x, y); } float TerrainNode::get_height(float x, float y) const { - TerrainNode *child = get_child(x, y); - if(child) { + if(is_leaf()) { + int tx = (x * 0.5 + 0.5) * map_xsz; + int ty = (y * 0.5 + 0.5) * map_ysz; + assert(tx >= 0 && tx < map_xsz); + assert(ty >= 0 && ty < map_ysz); + return map_scale * hmap[ty * map_xsz + tx] / 255.0; + } + int cidx = quadrant(x, y); + int cx = child_coord(x); + int cy = child_coord(y); + + return child[cidx]->get_height(cx, cy); +} + +void TerrainNode::draw(float x, float y, float width, float height) const +{ + static const float offset[][2] = {{-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25}}; + + if(!is_leaf()) { + float half_w = width / 2.0; + float half_h = height / 2.0; + + for(int i=0; i<4; i++) { + child[i]->draw(x + offset[i][0] * width, y + offset[i][1] * height, half_w, half_h); + } + return; } + + float dx = width / map_xsz; + float dy = height / map_ysz; + unsigned char *hptr = hmap; + + glBegin(GL_POINTS); + + float verty = y - height / 2.0; + for(int i=0; i #include +#include "tinyxml2.h" class TerrainNode; @@ -10,17 +11,26 @@ private: TerrainNode *root; + float size; + public: Terrain(); ~Terrain(); + bool load(tinyxml2::XMLElement *xelem); + + void set_size(float sz); float get_height(float x, float y) const; + + void draw() const; }; class TerrainNode { private: - unsigned char *height; + int map_xsz, map_ysz; + float map_scale; + unsigned char *hmap; public: TerrainNode *child[4]; @@ -28,10 +38,17 @@ TerrainNode(); ~TerrainNode(); + bool load(tinyxml2::XMLElement *xelem); + bool load_heightmap(const char *fname); + + bool is_leaf() const; + TerrainNode *get_child(float x, float y); const TerrainNode *get_child(float x, float y) const; float get_height(float x, float y) const; + + void draw(float x, float y, float width, float height) const; }; #endif // TERRAIN_H_