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