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  }