stratgame
diff level/src/terrain.cc @ 5:2e38715de41b
terrain
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 27 May 2012 07:00:48 +0300 |
parents | cd12944a8ea8 |
children |
line diff
1.1 --- a/level/src/terrain.cc Fri May 25 05:28:20 2012 +0300 1.2 +++ b/level/src/terrain.cc Sun May 27 07:00:48 2012 +0300 1.3 @@ -1,8 +1,16 @@ 1.4 +#include <stdio.h> 1.5 +#include <assert.h> 1.6 +#include "opengl.h" 1.7 +#include <imago2.h> 1.8 #include "terrain.h" 1.9 +#include "datapath.h" 1.10 + 1.11 +using namespace tinyxml2; 1.12 1.13 Terrain::Terrain() 1.14 { 1.15 root = 0; 1.16 + size = 2; 1.17 } 1.18 1.19 Terrain::~Terrain() 1.20 @@ -10,16 +18,36 @@ 1.21 delete root; 1.22 } 1.23 1.24 +bool Terrain::load(XMLElement *xelem) 1.25 +{ 1.26 + root = new TerrainNode; 1.27 + return root->load(xelem); 1.28 +} 1.29 + 1.30 +void Terrain::set_size(float sz) 1.31 +{ 1.32 + size = sz; 1.33 +} 1.34 + 1.35 float Terrain::get_height(float x, float y) const 1.36 { 1.37 return root->get_height(x, y); 1.38 } 1.39 1.40 +void Terrain::draw() const 1.41 +{ 1.42 + root->draw(0, 0, size, size); 1.43 +} 1.44 + 1.45 + 1.46 +// ---- terrain quadtree node ---- 1.47 1.48 TerrainNode::TerrainNode() 1.49 { 1.50 - height = 0; 1.51 - child[0] = child[1] = child[2] = child[3]; 1.52 + map_xsz = map_ysz = 0; 1.53 + map_scale = 1.0; 1.54 + hmap = 0; 1.55 + child[0] = child[1] = child[2] = child[3] = 0; 1.56 } 1.57 1.58 TerrainNode::~TerrainNode() 1.59 @@ -27,27 +55,177 @@ 1.60 for(auto c : child) { 1.61 delete c; 1.62 } 1.63 - delete [] height; 1.64 + img_free_pixels(hmap); 1.65 +} 1.66 + 1.67 +bool TerrainNode::load(XMLElement *xelem) 1.68 +{ 1.69 + XMLNode *node = xelem->FirstChild(); 1.70 + do { 1.71 + XMLElement *celem = node->ToElement(); 1.72 + if(!celem) { 1.73 + fprintf(stderr, "ignoring non-element child of <terrain>\n"); 1.74 + continue; 1.75 + } 1.76 + 1.77 + const char *attr, *name = celem->Name(); 1.78 + 1.79 + if(strcmp(name, "terrain") == 0) { 1.80 + // child is a recursive terrain element 1.81 + int cidx = 0; 1.82 + if((attr = celem->Attribute("index"))) { 1.83 + if(celem->QueryIntAttribute("index", &cidx) != 0) { 1.84 + fprintf(stderr, "invalid index attribute in terrain: %s\n", attr); 1.85 + return false; 1.86 + } 1.87 + } 1.88 + 1.89 + if(child[cidx]) { 1.90 + fprintf(stderr, "duplicate terrain child with index %d in terrain\n", cidx); 1.91 + return false; 1.92 + } 1.93 + 1.94 + child[cidx] = new TerrainNode; 1.95 + if(!child[cidx]->load(celem)) { 1.96 + return false; 1.97 + } 1.98 + } else if(strcmp(name, "map") == 0) { 1.99 + // child is an map of some kind 1.100 + // XXX for now we only care about heightmaps 1.101 + 1.102 + if((attr = celem->Attribute("type")) && strcmp(attr, "height") != 0) { 1.103 + fprintf(stderr, "ignoring map of type %s in terrain\n", attr); 1.104 + continue; 1.105 + } 1.106 + 1.107 + if((attr = celem->Attribute("file"))) { 1.108 + char fname[PATH_MAX]; 1.109 + 1.110 + if(!find_file(attr, fname)) { 1.111 + fprintf(stderr, "failed to locate map: %s\n", attr); 1.112 + return false; 1.113 + } 1.114 + 1.115 + if(!load_heightmap(fname)) { 1.116 + fprintf(stderr, "failed to load map: %s\n", fname); 1.117 + return false; 1.118 + } 1.119 + } else { 1.120 + fprintf(stderr, "ignoring map without file attribute in terrain\n"); 1.121 + continue; 1.122 + } 1.123 + 1.124 + float val; 1.125 + if(celem->QueryFloatAttribute("scale", &val) == 0) { 1.126 + map_scale = val; 1.127 + } 1.128 + } 1.129 + } while((node = node->NextSibling())); 1.130 + 1.131 + return true; 1.132 +} 1.133 + 1.134 + 1.135 +bool TerrainNode::load_heightmap(const char *fname) 1.136 +{ 1.137 + img_free_pixels(hmap); 1.138 + 1.139 + if(!(hmap = (unsigned char*)img_load_pixels(fname, &map_xsz, &map_ysz, IMG_FMT_GREY8))) { 1.140 + fprintf(stderr, "failed to load heightmap: %s\n", fname); 1.141 + return false; 1.142 + } 1.143 + return true; 1.144 +} 1.145 + 1.146 +bool TerrainNode::is_leaf() const 1.147 +{ 1.148 + if(!child[0]) { 1.149 + assert(!child[1] && !child[2] && !child[3]); 1.150 + return true; 1.151 + } 1.152 + return false; 1.153 +} 1.154 + 1.155 +static float child_coord(float x) 1.156 +{ 1.157 + return (x - 0.5) * 2; 1.158 +} 1.159 + 1.160 +static int quadrant(float x, float y) 1.161 +{ 1.162 + int xpos = x >= 0 ? 1 : 0; 1.163 + int ypos = y >= 0 ? 1 : 0; 1.164 + return ypos * 2 + xpos; 1.165 } 1.166 1.167 TerrainNode *TerrainNode::get_child(float x, float y) 1.168 { 1.169 - int xidx = x >= 0.0 ? 1 : 0; 1.170 - int yidx = y >= 0.0 ? 1 : 0; 1.171 - return child[y * 2 + x]; 1.172 + if(is_leaf()) { 1.173 + return this; 1.174 + } 1.175 + 1.176 + int cidx = quadrant(x, y); 1.177 + int cx = child_coord(x); 1.178 + int cy = child_coord(y); 1.179 + 1.180 + return child[cidx]->get_child(cx, cy); 1.181 } 1.182 1.183 const TerrainNode *TerrainNode::get_child(float x, float y) const 1.184 { 1.185 - int xidx = x >= 0.0 ? 1 : 0; 1.186 - int yidx = y >= 0.0 ? 1 : 0; 1.187 - return child[y * 2 + x]; 1.188 + return ((TerrainNode*)this)->get_child(x, y); 1.189 } 1.190 1.191 float TerrainNode::get_height(float x, float y) const 1.192 { 1.193 - TerrainNode *child = get_child(x, y); 1.194 - if(child) { 1.195 + if(is_leaf()) { 1.196 + int tx = (x * 0.5 + 0.5) * map_xsz; 1.197 + int ty = (y * 0.5 + 0.5) * map_ysz; 1.198 + assert(tx >= 0 && tx < map_xsz); 1.199 + assert(ty >= 0 && ty < map_ysz); 1.200 + return map_scale * hmap[ty * map_xsz + tx] / 255.0; 1.201 + } 1.202 1.203 + int cidx = quadrant(x, y); 1.204 + int cx = child_coord(x); 1.205 + int cy = child_coord(y); 1.206 + 1.207 + return child[cidx]->get_height(cx, cy); 1.208 +} 1.209 + 1.210 +void TerrainNode::draw(float x, float y, float width, float height) const 1.211 +{ 1.212 + static const float offset[][2] = {{-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25}}; 1.213 + 1.214 + if(!is_leaf()) { 1.215 + float half_w = width / 2.0; 1.216 + float half_h = height / 2.0; 1.217 + 1.218 + for(int i=0; i<4; i++) { 1.219 + child[i]->draw(x + offset[i][0] * width, y + offset[i][1] * height, half_w, half_h); 1.220 + } 1.221 + return; 1.222 } 1.223 + 1.224 + float dx = width / map_xsz; 1.225 + float dy = height / map_ysz; 1.226 + unsigned char *hptr = hmap; 1.227 + 1.228 + glBegin(GL_POINTS); 1.229 + 1.230 + float verty = y - height / 2.0; 1.231 + for(int i=0; i<map_ysz; i++) { 1.232 + 1.233 + float vertx = x - width / 2.0; 1.234 + for(int j=0; j<map_xsz; j++) { 1.235 + float height = map_scale * *hptr++ / 255.0; 1.236 + 1.237 + glColor3f(height, 0, 1.0 - height); 1.238 + glVertex3f(vertx, height, verty); 1.239 + 1.240 + vertx += dx; 1.241 + } 1.242 + verty += dy; 1.243 + } 1.244 + glEnd(); 1.245 }