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_