stratgame
changeset 5:2e38715de41b tip
terrain
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 27 May 2012 07:00:48 +0300 |
parents | cd12944a8ea8 |
children | |
files | level/src/level.cc level/src/level.h level/src/terrain.cc level/src/terrain.h |
diffstat | 4 files changed, 236 insertions(+), 196 deletions(-) [+] |
line diff
1.1 --- a/level/src/level.cc Fri May 25 05:28:20 2012 +0300 1.2 +++ b/level/src/level.cc Sun May 27 07:00:48 2012 +0300 1.3 @@ -1,8 +1,9 @@ 1.4 #include <stdio.h> 1.5 #include <string.h> 1.6 +#include "opengl.h" 1.7 #include "level.h" 1.8 #include "datapath.h" 1.9 -#include "opengl.h" 1.10 +#include "terrain.h" 1.11 1.12 using namespace tinyxml2; 1.13 1.14 @@ -34,20 +35,34 @@ 1.15 XMLNode *node = xml.RootElement()->FirstChild(); 1.16 do { 1.17 XMLElement *elem = node->ToElement(); 1.18 - if(elem) { 1.19 - if(strcmp(elem->Name(), "map") == 0) { 1.20 - LevelMap map; 1.21 + if(!elem) { 1.22 + fprintf(stderr, "ignoring non-element child of <level>\n"); 1.23 + continue; 1.24 + } 1.25 1.26 - if(!map.load(elem)) { 1.27 - pop_data_path(); 1.28 - return false; 1.29 - } 1.30 - levelmaps[map.get_name()] = std::move(map); 1.31 + if(strcmp(elem->Name(), "terrain") == 0) { 1.32 + if(terrain) { 1.33 + fprintf(stderr, "<level> must only have a single terrain child! ignoring\n"); 1.34 + continue; 1.35 + } 1.36 1.37 - } else { 1.38 - fprintf(stderr, "ignoring unrecognized element: %s\n", elem->Name()); 1.39 + terrain = new Terrain; 1.40 + if(!terrain->load(elem)) { 1.41 + fprintf(stderr, "failed to load terrain\n"); 1.42 + pop_data_path(); 1.43 + return false; 1.44 } 1.45 + 1.46 + float val; 1.47 + if(elem->QueryFloatAttribute("size", &val) == 0) { 1.48 + printf("terrain size: %f\n", val); 1.49 + terrain->set_size(val); 1.50 + } 1.51 + } else { 1.52 + fprintf(stderr, "ignoring unexpected child of <level>: %s\n", elem->Name()); 1.53 + continue; 1.54 } 1.55 + 1.56 } while((node = node->NextSibling())); 1.57 1.58 pop_data_path(); 1.59 @@ -56,148 +71,5 @@ 1.60 1.61 void Level::draw() const 1.62 { 1.63 - auto iter = levelmaps.find("height"); 1.64 - if(iter == levelmaps.end()) { 1.65 - return; 1.66 - } 1.67 - 1.68 - img_pixmap *hmap = (img_pixmap*)iter->second.get_pixmap(); 1.69 - 1.70 - glBegin(GL_POINTS); 1.71 - for(int i=0; i<hmap->height; i++) { 1.72 - for(int j=0; j<hmap->width; j++) { 1.73 - int height; 1.74 - img_getpixel1i(hmap, j, i, &height); 1.75 - 1.76 - float x = (float)j / (float)hmap->width - 0.5; 1.77 - float y = height / 255.0; 1.78 - float z = (float)i / (float)hmap->height - 0.5; 1.79 - 1.80 - glColor3f(y, 0.0, 1.0 - y); 1.81 - glVertex3f(x, y, z); 1.82 - } 1.83 - } 1.84 - glEnd(); 1.85 + terrain->draw(); 1.86 } 1.87 - 1.88 - 1.89 -LevelMap::LevelMap() 1.90 -{ 1.91 - init(); 1.92 -} 1.93 - 1.94 -void LevelMap::init() 1.95 -{ 1.96 - scale = 1.0f; 1.97 - img_init(&img); 1.98 - name = 0; 1.99 -} 1.100 - 1.101 -LevelMap::~LevelMap() 1.102 -{ 1.103 - destroy(); 1.104 -} 1.105 - 1.106 -void LevelMap::destroy() 1.107 -{ 1.108 - if(img.pixels) { 1.109 - img_destroy(&img); 1.110 - } 1.111 - delete [] name; 1.112 -} 1.113 - 1.114 -// copy 1.115 -LevelMap::LevelMap(const LevelMap &map) 1.116 -{ 1.117 - init(); 1.118 - *this = map; 1.119 -} 1.120 - 1.121 -LevelMap &LevelMap::operator =(const LevelMap &map) 1.122 -{ 1.123 - if(this != &map) { 1.124 - destroy(); 1.125 - 1.126 - scale = map.scale; 1.127 - img_init(&img); 1.128 - img_copy(&img, (img_pixmap*)&map.img); 1.129 - 1.130 - name = new char[strlen(map.name) + 1]; 1.131 - strcpy(name, map.name); 1.132 - } 1.133 - return *this; 1.134 -} 1.135 - 1.136 -// move semantics 1.137 -LevelMap::LevelMap(LevelMap &&map) 1.138 -{ 1.139 - init(); 1.140 - *this = std::move(map); 1.141 -} 1.142 - 1.143 -LevelMap &LevelMap::operator =(LevelMap &&map) 1.144 -{ 1.145 - if(this != &map) { 1.146 - destroy(); 1.147 - 1.148 - scale = map.scale; 1.149 - 1.150 - img = map.img; 1.151 - map.img.pixels = 0; 1.152 - map.img.name = 0; 1.153 - 1.154 - name = map.name; 1.155 - map.name = 0; 1.156 - } 1.157 - 1.158 - return *this; 1.159 -} 1.160 - 1.161 -bool LevelMap::load(XMLElement *xelem) 1.162 -{ 1.163 - char fname[PATH_MAX]; 1.164 - 1.165 - const char *attr = xelem->Attribute("type"); 1.166 - if(!attr) { 1.167 - fprintf(stderr, "map element without a type attribute\n"); 1.168 - return false; 1.169 - } 1.170 - name = new char[strlen(attr) + 1]; 1.171 - strcpy(name, attr); 1.172 - 1.173 - attr = xelem->Attribute("file"); 1.174 - if(!attr) { 1.175 - fprintf(stderr, "map element without a file attribute\n"); 1.176 - return false; 1.177 - } 1.178 - if(!find_file(attr, fname)) { 1.179 - fprintf(stderr, "failed to locate image: %s\n", attr); 1.180 - return false; 1.181 - } 1.182 - 1.183 - if(img_load(&img, fname) == -1) { 1.184 - fprintf(stderr, "failed to load image: %s\n", fname); 1.185 - return false; 1.186 - } 1.187 - 1.188 - float val; 1.189 - if(xelem->QueryFloatAttribute("scale", &val) == 0) { 1.190 - scale = val; 1.191 - } 1.192 - return true; 1.193 -} 1.194 - 1.195 -float LevelMap::get_scale() const 1.196 -{ 1.197 - return scale; 1.198 -} 1.199 - 1.200 -const img_pixmap *LevelMap::get_pixmap() const 1.201 -{ 1.202 - return &img; 1.203 -} 1.204 - 1.205 -const char *LevelMap::get_name() const 1.206 -{ 1.207 - return name; 1.208 -}
2.1 --- a/level/src/level.h Fri May 25 05:28:20 2012 +0300 2.2 +++ b/level/src/level.h Sun May 27 07:00:48 2012 +0300 2.3 @@ -6,12 +6,12 @@ 2.4 #include <imago2.h> 2.5 #include "tinyxml2.h" 2.6 2.7 -class LevelMap; 2.8 +class Terrain; 2.9 2.10 class Level { 2.11 private: 2.12 tinyxml2::XMLDocument xml; 2.13 - std::map<std::string, LevelMap> levelmaps; 2.14 + Terrain *terrain; 2.15 2.16 public: 2.17 Level(); 2.18 @@ -22,31 +22,4 @@ 2.19 void draw() const; 2.20 }; 2.21 2.22 -class LevelMap { 2.23 -private: 2.24 - float scale; 2.25 - img_pixmap img; 2.26 - char *name; 2.27 - 2.28 - void init(); 2.29 - void destroy(); 2.30 - 2.31 -public: 2.32 - LevelMap(); 2.33 - ~LevelMap(); 2.34 - 2.35 - LevelMap(const LevelMap &map); 2.36 - LevelMap &operator =(const LevelMap &map); 2.37 - 2.38 - // move constructor/op= 2.39 - LevelMap(LevelMap &&map); 2.40 - LevelMap &operator =(LevelMap &&map); 2.41 - 2.42 - bool load(tinyxml2::XMLElement *xelem); 2.43 - 2.44 - float get_scale() const; 2.45 - const img_pixmap *get_pixmap() const; 2.46 - const char *get_name() const; 2.47 -}; 2.48 - 2.49 #endif // LEVEL_H_
3.1 --- a/level/src/terrain.cc Fri May 25 05:28:20 2012 +0300 3.2 +++ b/level/src/terrain.cc Sun May 27 07:00:48 2012 +0300 3.3 @@ -1,8 +1,16 @@ 3.4 +#include <stdio.h> 3.5 +#include <assert.h> 3.6 +#include "opengl.h" 3.7 +#include <imago2.h> 3.8 #include "terrain.h" 3.9 +#include "datapath.h" 3.10 + 3.11 +using namespace tinyxml2; 3.12 3.13 Terrain::Terrain() 3.14 { 3.15 root = 0; 3.16 + size = 2; 3.17 } 3.18 3.19 Terrain::~Terrain() 3.20 @@ -10,16 +18,36 @@ 3.21 delete root; 3.22 } 3.23 3.24 +bool Terrain::load(XMLElement *xelem) 3.25 +{ 3.26 + root = new TerrainNode; 3.27 + return root->load(xelem); 3.28 +} 3.29 + 3.30 +void Terrain::set_size(float sz) 3.31 +{ 3.32 + size = sz; 3.33 +} 3.34 + 3.35 float Terrain::get_height(float x, float y) const 3.36 { 3.37 return root->get_height(x, y); 3.38 } 3.39 3.40 +void Terrain::draw() const 3.41 +{ 3.42 + root->draw(0, 0, size, size); 3.43 +} 3.44 + 3.45 + 3.46 +// ---- terrain quadtree node ---- 3.47 3.48 TerrainNode::TerrainNode() 3.49 { 3.50 - height = 0; 3.51 - child[0] = child[1] = child[2] = child[3]; 3.52 + map_xsz = map_ysz = 0; 3.53 + map_scale = 1.0; 3.54 + hmap = 0; 3.55 + child[0] = child[1] = child[2] = child[3] = 0; 3.56 } 3.57 3.58 TerrainNode::~TerrainNode() 3.59 @@ -27,27 +55,177 @@ 3.60 for(auto c : child) { 3.61 delete c; 3.62 } 3.63 - delete [] height; 3.64 + img_free_pixels(hmap); 3.65 +} 3.66 + 3.67 +bool TerrainNode::load(XMLElement *xelem) 3.68 +{ 3.69 + XMLNode *node = xelem->FirstChild(); 3.70 + do { 3.71 + XMLElement *celem = node->ToElement(); 3.72 + if(!celem) { 3.73 + fprintf(stderr, "ignoring non-element child of <terrain>\n"); 3.74 + continue; 3.75 + } 3.76 + 3.77 + const char *attr, *name = celem->Name(); 3.78 + 3.79 + if(strcmp(name, "terrain") == 0) { 3.80 + // child is a recursive terrain element 3.81 + int cidx = 0; 3.82 + if((attr = celem->Attribute("index"))) { 3.83 + if(celem->QueryIntAttribute("index", &cidx) != 0) { 3.84 + fprintf(stderr, "invalid index attribute in terrain: %s\n", attr); 3.85 + return false; 3.86 + } 3.87 + } 3.88 + 3.89 + if(child[cidx]) { 3.90 + fprintf(stderr, "duplicate terrain child with index %d in terrain\n", cidx); 3.91 + return false; 3.92 + } 3.93 + 3.94 + child[cidx] = new TerrainNode; 3.95 + if(!child[cidx]->load(celem)) { 3.96 + return false; 3.97 + } 3.98 + } else if(strcmp(name, "map") == 0) { 3.99 + // child is an map of some kind 3.100 + // XXX for now we only care about heightmaps 3.101 + 3.102 + if((attr = celem->Attribute("type")) && strcmp(attr, "height") != 0) { 3.103 + fprintf(stderr, "ignoring map of type %s in terrain\n", attr); 3.104 + continue; 3.105 + } 3.106 + 3.107 + if((attr = celem->Attribute("file"))) { 3.108 + char fname[PATH_MAX]; 3.109 + 3.110 + if(!find_file(attr, fname)) { 3.111 + fprintf(stderr, "failed to locate map: %s\n", attr); 3.112 + return false; 3.113 + } 3.114 + 3.115 + if(!load_heightmap(fname)) { 3.116 + fprintf(stderr, "failed to load map: %s\n", fname); 3.117 + return false; 3.118 + } 3.119 + } else { 3.120 + fprintf(stderr, "ignoring map without file attribute in terrain\n"); 3.121 + continue; 3.122 + } 3.123 + 3.124 + float val; 3.125 + if(celem->QueryFloatAttribute("scale", &val) == 0) { 3.126 + map_scale = val; 3.127 + } 3.128 + } 3.129 + } while((node = node->NextSibling())); 3.130 + 3.131 + return true; 3.132 +} 3.133 + 3.134 + 3.135 +bool TerrainNode::load_heightmap(const char *fname) 3.136 +{ 3.137 + img_free_pixels(hmap); 3.138 + 3.139 + if(!(hmap = (unsigned char*)img_load_pixels(fname, &map_xsz, &map_ysz, IMG_FMT_GREY8))) { 3.140 + fprintf(stderr, "failed to load heightmap: %s\n", fname); 3.141 + return false; 3.142 + } 3.143 + return true; 3.144 +} 3.145 + 3.146 +bool TerrainNode::is_leaf() const 3.147 +{ 3.148 + if(!child[0]) { 3.149 + assert(!child[1] && !child[2] && !child[3]); 3.150 + return true; 3.151 + } 3.152 + return false; 3.153 +} 3.154 + 3.155 +static float child_coord(float x) 3.156 +{ 3.157 + return (x - 0.5) * 2; 3.158 +} 3.159 + 3.160 +static int quadrant(float x, float y) 3.161 +{ 3.162 + int xpos = x >= 0 ? 1 : 0; 3.163 + int ypos = y >= 0 ? 1 : 0; 3.164 + return ypos * 2 + xpos; 3.165 } 3.166 3.167 TerrainNode *TerrainNode::get_child(float x, float y) 3.168 { 3.169 - int xidx = x >= 0.0 ? 1 : 0; 3.170 - int yidx = y >= 0.0 ? 1 : 0; 3.171 - return child[y * 2 + x]; 3.172 + if(is_leaf()) { 3.173 + return this; 3.174 + } 3.175 + 3.176 + int cidx = quadrant(x, y); 3.177 + int cx = child_coord(x); 3.178 + int cy = child_coord(y); 3.179 + 3.180 + return child[cidx]->get_child(cx, cy); 3.181 } 3.182 3.183 const TerrainNode *TerrainNode::get_child(float x, float y) const 3.184 { 3.185 - int xidx = x >= 0.0 ? 1 : 0; 3.186 - int yidx = y >= 0.0 ? 1 : 0; 3.187 - return child[y * 2 + x]; 3.188 + return ((TerrainNode*)this)->get_child(x, y); 3.189 } 3.190 3.191 float TerrainNode::get_height(float x, float y) const 3.192 { 3.193 - TerrainNode *child = get_child(x, y); 3.194 - if(child) { 3.195 + if(is_leaf()) { 3.196 + int tx = (x * 0.5 + 0.5) * map_xsz; 3.197 + int ty = (y * 0.5 + 0.5) * map_ysz; 3.198 + assert(tx >= 0 && tx < map_xsz); 3.199 + assert(ty >= 0 && ty < map_ysz); 3.200 + return map_scale * hmap[ty * map_xsz + tx] / 255.0; 3.201 + } 3.202 3.203 + int cidx = quadrant(x, y); 3.204 + int cx = child_coord(x); 3.205 + int cy = child_coord(y); 3.206 + 3.207 + return child[cidx]->get_height(cx, cy); 3.208 +} 3.209 + 3.210 +void TerrainNode::draw(float x, float y, float width, float height) const 3.211 +{ 3.212 + static const float offset[][2] = {{-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25}}; 3.213 + 3.214 + if(!is_leaf()) { 3.215 + float half_w = width / 2.0; 3.216 + float half_h = height / 2.0; 3.217 + 3.218 + for(int i=0; i<4; i++) { 3.219 + child[i]->draw(x + offset[i][0] * width, y + offset[i][1] * height, half_w, half_h); 3.220 + } 3.221 + return; 3.222 } 3.223 + 3.224 + float dx = width / map_xsz; 3.225 + float dy = height / map_ysz; 3.226 + unsigned char *hptr = hmap; 3.227 + 3.228 + glBegin(GL_POINTS); 3.229 + 3.230 + float verty = y - height / 2.0; 3.231 + for(int i=0; i<map_ysz; i++) { 3.232 + 3.233 + float vertx = x - width / 2.0; 3.234 + for(int j=0; j<map_xsz; j++) { 3.235 + float height = map_scale * *hptr++ / 255.0; 3.236 + 3.237 + glColor3f(height, 0, 1.0 - height); 3.238 + glVertex3f(vertx, height, verty); 3.239 + 3.240 + vertx += dx; 3.241 + } 3.242 + verty += dy; 3.243 + } 3.244 + glEnd(); 3.245 }
4.1 --- a/level/src/terrain.h Fri May 25 05:28:20 2012 +0300 4.2 +++ b/level/src/terrain.h Sun May 27 07:00:48 2012 +0300 4.3 @@ -3,6 +3,7 @@ 4.4 4.5 #include <string> 4.6 #include <map> 4.7 +#include "tinyxml2.h" 4.8 4.9 class TerrainNode; 4.10 4.11 @@ -10,17 +11,26 @@ 4.12 private: 4.13 TerrainNode *root; 4.14 4.15 + float size; 4.16 + 4.17 public: 4.18 Terrain(); 4.19 ~Terrain(); 4.20 4.21 + bool load(tinyxml2::XMLElement *xelem); 4.22 + 4.23 + void set_size(float sz); 4.24 float get_height(float x, float y) const; 4.25 + 4.26 + void draw() const; 4.27 }; 4.28 4.29 4.30 class TerrainNode { 4.31 private: 4.32 - unsigned char *height; 4.33 + int map_xsz, map_ysz; 4.34 + float map_scale; 4.35 + unsigned char *hmap; 4.36 4.37 public: 4.38 TerrainNode *child[4]; 4.39 @@ -28,10 +38,17 @@ 4.40 TerrainNode(); 4.41 ~TerrainNode(); 4.42 4.43 + bool load(tinyxml2::XMLElement *xelem); 4.44 + bool load_heightmap(const char *fname); 4.45 + 4.46 + bool is_leaf() const; 4.47 + 4.48 TerrainNode *get_child(float x, float y); 4.49 const TerrainNode *get_child(float x, float y) const; 4.50 4.51 float get_height(float x, float y) const; 4.52 + 4.53 + void draw(float x, float y, float width, float height) const; 4.54 }; 4.55 4.56 #endif // TERRAIN_H_