stratgame

annotate 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
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 }