nuclear@5: #include nuclear@5: #include nuclear@5: #include "opengl.h" nuclear@5: #include nuclear@4: #include "terrain.h" nuclear@5: #include "datapath.h" nuclear@5: nuclear@5: using namespace tinyxml2; nuclear@4: nuclear@4: Terrain::Terrain() nuclear@4: { nuclear@4: root = 0; nuclear@5: size = 2; nuclear@4: } nuclear@4: nuclear@4: Terrain::~Terrain() nuclear@4: { nuclear@4: delete root; nuclear@4: } nuclear@4: nuclear@5: bool Terrain::load(XMLElement *xelem) nuclear@5: { nuclear@5: root = new TerrainNode; nuclear@5: return root->load(xelem); nuclear@5: } nuclear@5: nuclear@5: void Terrain::set_size(float sz) nuclear@5: { nuclear@5: size = sz; nuclear@5: } nuclear@5: nuclear@4: float Terrain::get_height(float x, float y) const nuclear@4: { nuclear@4: return root->get_height(x, y); nuclear@4: } nuclear@4: nuclear@5: void Terrain::draw() const nuclear@5: { nuclear@5: root->draw(0, 0, size, size); nuclear@5: } nuclear@5: nuclear@5: nuclear@5: // ---- terrain quadtree node ---- nuclear@4: nuclear@4: TerrainNode::TerrainNode() nuclear@4: { nuclear@5: map_xsz = map_ysz = 0; nuclear@5: map_scale = 1.0; nuclear@5: hmap = 0; nuclear@5: child[0] = child[1] = child[2] = child[3] = 0; nuclear@4: } nuclear@4: nuclear@4: TerrainNode::~TerrainNode() nuclear@4: { nuclear@4: for(auto c : child) { nuclear@4: delete c; nuclear@4: } nuclear@5: img_free_pixels(hmap); nuclear@5: } nuclear@5: nuclear@5: bool TerrainNode::load(XMLElement *xelem) nuclear@5: { nuclear@5: XMLNode *node = xelem->FirstChild(); nuclear@5: do { nuclear@5: XMLElement *celem = node->ToElement(); nuclear@5: if(!celem) { nuclear@5: fprintf(stderr, "ignoring non-element child of \n"); nuclear@5: continue; nuclear@5: } nuclear@5: nuclear@5: const char *attr, *name = celem->Name(); nuclear@5: nuclear@5: if(strcmp(name, "terrain") == 0) { nuclear@5: // child is a recursive terrain element nuclear@5: int cidx = 0; nuclear@5: if((attr = celem->Attribute("index"))) { nuclear@5: if(celem->QueryIntAttribute("index", &cidx) != 0) { nuclear@5: fprintf(stderr, "invalid index attribute in terrain: %s\n", attr); nuclear@5: return false; nuclear@5: } nuclear@5: } nuclear@5: nuclear@5: if(child[cidx]) { nuclear@5: fprintf(stderr, "duplicate terrain child with index %d in terrain\n", cidx); nuclear@5: return false; nuclear@5: } nuclear@5: nuclear@5: child[cidx] = new TerrainNode; nuclear@5: if(!child[cidx]->load(celem)) { nuclear@5: return false; nuclear@5: } nuclear@5: } else if(strcmp(name, "map") == 0) { nuclear@5: // child is an map of some kind nuclear@5: // XXX for now we only care about heightmaps nuclear@5: nuclear@5: if((attr = celem->Attribute("type")) && strcmp(attr, "height") != 0) { nuclear@5: fprintf(stderr, "ignoring map of type %s in terrain\n", attr); nuclear@5: continue; nuclear@5: } nuclear@5: nuclear@5: if((attr = celem->Attribute("file"))) { nuclear@5: char fname[PATH_MAX]; nuclear@5: nuclear@5: if(!find_file(attr, fname)) { nuclear@5: fprintf(stderr, "failed to locate map: %s\n", attr); nuclear@5: return false; nuclear@5: } nuclear@5: nuclear@5: if(!load_heightmap(fname)) { nuclear@5: fprintf(stderr, "failed to load map: %s\n", fname); nuclear@5: return false; nuclear@5: } nuclear@5: } else { nuclear@5: fprintf(stderr, "ignoring map without file attribute in terrain\n"); nuclear@5: continue; nuclear@5: } nuclear@5: nuclear@5: float val; nuclear@5: if(celem->QueryFloatAttribute("scale", &val) == 0) { nuclear@5: map_scale = val; nuclear@5: } nuclear@5: } nuclear@5: } while((node = node->NextSibling())); nuclear@5: nuclear@5: return true; nuclear@5: } nuclear@5: nuclear@5: nuclear@5: bool TerrainNode::load_heightmap(const char *fname) nuclear@5: { nuclear@5: img_free_pixels(hmap); nuclear@5: nuclear@5: if(!(hmap = (unsigned char*)img_load_pixels(fname, &map_xsz, &map_ysz, IMG_FMT_GREY8))) { nuclear@5: fprintf(stderr, "failed to load heightmap: %s\n", fname); nuclear@5: return false; nuclear@5: } nuclear@5: return true; nuclear@5: } nuclear@5: nuclear@5: bool TerrainNode::is_leaf() const nuclear@5: { nuclear@5: if(!child[0]) { nuclear@5: assert(!child[1] && !child[2] && !child[3]); nuclear@5: return true; nuclear@5: } nuclear@5: return false; nuclear@5: } nuclear@5: nuclear@5: static float child_coord(float x) nuclear@5: { nuclear@5: return (x - 0.5) * 2; nuclear@5: } nuclear@5: nuclear@5: static int quadrant(float x, float y) nuclear@5: { nuclear@5: int xpos = x >= 0 ? 1 : 0; nuclear@5: int ypos = y >= 0 ? 1 : 0; nuclear@5: return ypos * 2 + xpos; nuclear@4: } nuclear@4: nuclear@4: TerrainNode *TerrainNode::get_child(float x, float y) nuclear@4: { nuclear@5: if(is_leaf()) { nuclear@5: return this; nuclear@5: } nuclear@5: nuclear@5: int cidx = quadrant(x, y); nuclear@5: int cx = child_coord(x); nuclear@5: int cy = child_coord(y); nuclear@5: nuclear@5: return child[cidx]->get_child(cx, cy); nuclear@4: } nuclear@4: nuclear@4: const TerrainNode *TerrainNode::get_child(float x, float y) const nuclear@4: { nuclear@5: return ((TerrainNode*)this)->get_child(x, y); nuclear@4: } nuclear@4: nuclear@4: float TerrainNode::get_height(float x, float y) const nuclear@4: { nuclear@5: if(is_leaf()) { nuclear@5: int tx = (x * 0.5 + 0.5) * map_xsz; nuclear@5: int ty = (y * 0.5 + 0.5) * map_ysz; nuclear@5: assert(tx >= 0 && tx < map_xsz); nuclear@5: assert(ty >= 0 && ty < map_ysz); nuclear@5: return map_scale * hmap[ty * map_xsz + tx] / 255.0; nuclear@5: } nuclear@4: nuclear@5: int cidx = quadrant(x, y); nuclear@5: int cx = child_coord(x); nuclear@5: int cy = child_coord(y); nuclear@5: nuclear@5: return child[cidx]->get_height(cx, cy); nuclear@5: } nuclear@5: nuclear@5: void TerrainNode::draw(float x, float y, float width, float height) const nuclear@5: { nuclear@5: static const float offset[][2] = {{-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25}}; nuclear@5: nuclear@5: if(!is_leaf()) { nuclear@5: float half_w = width / 2.0; nuclear@5: float half_h = height / 2.0; nuclear@5: nuclear@5: for(int i=0; i<4; i++) { nuclear@5: child[i]->draw(x + offset[i][0] * width, y + offset[i][1] * height, half_w, half_h); nuclear@5: } nuclear@5: return; nuclear@4: } nuclear@5: nuclear@5: float dx = width / map_xsz; nuclear@5: float dy = height / map_ysz; nuclear@5: unsigned char *hptr = hmap; nuclear@5: nuclear@5: glBegin(GL_POINTS); nuclear@5: nuclear@5: float verty = y - height / 2.0; nuclear@5: for(int i=0; i