stratgame

view 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 source
1 #include <stdio.h>
2 #include <assert.h>
3 #include "opengl.h"
4 #include <imago2.h>
5 #include "terrain.h"
6 #include "datapath.h"
8 using namespace tinyxml2;
10 Terrain::Terrain()
11 {
12 root = 0;
13 size = 2;
14 }
16 Terrain::~Terrain()
17 {
18 delete root;
19 }
21 bool Terrain::load(XMLElement *xelem)
22 {
23 root = new TerrainNode;
24 return root->load(xelem);
25 }
27 void Terrain::set_size(float sz)
28 {
29 size = sz;
30 }
32 float Terrain::get_height(float x, float y) const
33 {
34 return root->get_height(x, y);
35 }
37 void Terrain::draw() const
38 {
39 root->draw(0, 0, size, size);
40 }
43 // ---- terrain quadtree node ----
45 TerrainNode::TerrainNode()
46 {
47 map_xsz = map_ysz = 0;
48 map_scale = 1.0;
49 hmap = 0;
50 child[0] = child[1] = child[2] = child[3] = 0;
51 }
53 TerrainNode::~TerrainNode()
54 {
55 for(auto c : child) {
56 delete c;
57 }
58 img_free_pixels(hmap);
59 }
61 bool TerrainNode::load(XMLElement *xelem)
62 {
63 XMLNode *node = xelem->FirstChild();
64 do {
65 XMLElement *celem = node->ToElement();
66 if(!celem) {
67 fprintf(stderr, "ignoring non-element child of <terrain>\n");
68 continue;
69 }
71 const char *attr, *name = celem->Name();
73 if(strcmp(name, "terrain") == 0) {
74 // child is a recursive terrain element
75 int cidx = 0;
76 if((attr = celem->Attribute("index"))) {
77 if(celem->QueryIntAttribute("index", &cidx) != 0) {
78 fprintf(stderr, "invalid index attribute in terrain: %s\n", attr);
79 return false;
80 }
81 }
83 if(child[cidx]) {
84 fprintf(stderr, "duplicate terrain child with index %d in terrain\n", cidx);
85 return false;
86 }
88 child[cidx] = new TerrainNode;
89 if(!child[cidx]->load(celem)) {
90 return false;
91 }
92 } else if(strcmp(name, "map") == 0) {
93 // child is an map of some kind
94 // XXX for now we only care about heightmaps
96 if((attr = celem->Attribute("type")) && strcmp(attr, "height") != 0) {
97 fprintf(stderr, "ignoring map of type %s in terrain\n", attr);
98 continue;
99 }
101 if((attr = celem->Attribute("file"))) {
102 char fname[PATH_MAX];
104 if(!find_file(attr, fname)) {
105 fprintf(stderr, "failed to locate map: %s\n", attr);
106 return false;
107 }
109 if(!load_heightmap(fname)) {
110 fprintf(stderr, "failed to load map: %s\n", fname);
111 return false;
112 }
113 } else {
114 fprintf(stderr, "ignoring map without file attribute in terrain\n");
115 continue;
116 }
118 float val;
119 if(celem->QueryFloatAttribute("scale", &val) == 0) {
120 map_scale = val;
121 }
122 }
123 } while((node = node->NextSibling()));
125 return true;
126 }
129 bool TerrainNode::load_heightmap(const char *fname)
130 {
131 img_free_pixels(hmap);
133 if(!(hmap = (unsigned char*)img_load_pixels(fname, &map_xsz, &map_ysz, IMG_FMT_GREY8))) {
134 fprintf(stderr, "failed to load heightmap: %s\n", fname);
135 return false;
136 }
137 return true;
138 }
140 bool TerrainNode::is_leaf() const
141 {
142 if(!child[0]) {
143 assert(!child[1] && !child[2] && !child[3]);
144 return true;
145 }
146 return false;
147 }
149 static float child_coord(float x)
150 {
151 return (x - 0.5) * 2;
152 }
154 static int quadrant(float x, float y)
155 {
156 int xpos = x >= 0 ? 1 : 0;
157 int ypos = y >= 0 ? 1 : 0;
158 return ypos * 2 + xpos;
159 }
161 TerrainNode *TerrainNode::get_child(float x, float y)
162 {
163 if(is_leaf()) {
164 return this;
165 }
167 int cidx = quadrant(x, y);
168 int cx = child_coord(x);
169 int cy = child_coord(y);
171 return child[cidx]->get_child(cx, cy);
172 }
174 const TerrainNode *TerrainNode::get_child(float x, float y) const
175 {
176 return ((TerrainNode*)this)->get_child(x, y);
177 }
179 float TerrainNode::get_height(float x, float y) const
180 {
181 if(is_leaf()) {
182 int tx = (x * 0.5 + 0.5) * map_xsz;
183 int ty = (y * 0.5 + 0.5) * map_ysz;
184 assert(tx >= 0 && tx < map_xsz);
185 assert(ty >= 0 && ty < map_ysz);
186 return map_scale * hmap[ty * map_xsz + tx] / 255.0;
187 }
189 int cidx = quadrant(x, y);
190 int cx = child_coord(x);
191 int cy = child_coord(y);
193 return child[cidx]->get_height(cx, cy);
194 }
196 void TerrainNode::draw(float x, float y, float width, float height) const
197 {
198 static const float offset[][2] = {{-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25}};
200 if(!is_leaf()) {
201 float half_w = width / 2.0;
202 float half_h = height / 2.0;
204 for(int i=0; i<4; i++) {
205 child[i]->draw(x + offset[i][0] * width, y + offset[i][1] * height, half_w, half_h);
206 }
207 return;
208 }
210 float dx = width / map_xsz;
211 float dy = height / map_ysz;
212 unsigned char *hptr = hmap;
214 glBegin(GL_POINTS);
216 float verty = y - height / 2.0;
217 for(int i=0; i<map_ysz; i++) {
219 float vertx = x - width / 2.0;
220 for(int j=0; j<map_xsz; j++) {
221 float height = map_scale * *hptr++ / 255.0;
223 glColor3f(height, 0, 1.0 - height);
224 glVertex3f(vertx, height, verty);
226 vertx += dx;
227 }
228 verty += dy;
229 }
230 glEnd();
231 }