Panda3D
|
00001 // Filename: geoMipTerrain.cxx 00002 // Created by: rdb (29Jun07) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "geoMipTerrain.h" 00016 00017 #include "geomVertexFormat.h" 00018 #include "geomVertexArrayFormat.h" 00019 #include "internalName.h" 00020 #include "geomVertexData.h" 00021 #include "geomVertexWriter.h" 00022 #include "geomTristrips.h" 00023 #include "geomTriangles.h" 00024 #include "geom.h" 00025 #include "geomNode.h" 00026 #include "boundingBox.h" 00027 #include "config_grutil.h" 00028 00029 #include "sceneGraphReducer.h" 00030 00031 #include "collideMask.h" 00032 00033 TypeHandle GeoMipTerrain::_type_handle; 00034 00035 //////////////////////////////////////////////////////////////////// 00036 // Function: GeoMipTerrain::generate_block 00037 // Access: Private 00038 // Description: Generates a chunk of terrain based on the level 00039 // specified. As arguments it takes the x and y coords 00040 // of the mipmap to be generated, and the level of 00041 // detail. T-Junctions for neighbor-mipmaps with 00042 // different levels are also taken into account. 00043 //////////////////////////////////////////////////////////////////// 00044 PT(GeomNode) GeoMipTerrain:: 00045 generate_block(unsigned short mx, 00046 unsigned short my, 00047 unsigned short level) { 00048 00049 nassertr(mx < (_xsize - 1) / _block_size, NULL); 00050 nassertr(my < (_ysize - 1) / _block_size, NULL); 00051 00052 unsigned short center = _block_size / 2; 00053 unsigned int vcounter = 0; 00054 00055 // Create the format 00056 PT(GeomVertexArrayFormat) array = new GeomVertexArrayFormat(); 00057 if (_has_color_map) { 00058 array->add_column(InternalName::make("color"), 4, 00059 Geom::NT_stdfloat, Geom::C_color); 00060 } 00061 array->add_column(InternalName::make("vertex"), 3, 00062 Geom::NT_stdfloat, Geom::C_point); 00063 array->add_column(InternalName::make("texcoord"), 2, 00064 Geom::NT_stdfloat, Geom::C_texcoord); 00065 array->add_column(InternalName::make("normal"), 3, 00066 Geom::NT_stdfloat, Geom::C_vector); 00067 PT(GeomVertexFormat) format = new GeomVertexFormat(); 00068 format->add_array(array); 00069 00070 // Create vertex data and writers 00071 PT(GeomVertexData) vdata = new GeomVertexData(_root.get_name(), 00072 GeomVertexFormat::register_format(format), Geom::UH_stream); 00073 vdata->unclean_set_num_rows((_block_size + 1) * (_block_size + 1)); 00074 GeomVertexWriter cwriter; 00075 if (_has_color_map) { 00076 cwriter = GeomVertexWriter(vdata, "color"); 00077 } 00078 GeomVertexWriter vwriter (vdata, "vertex" ); 00079 GeomVertexWriter twriter (vdata, "texcoord"); 00080 GeomVertexWriter nwriter (vdata, "normal" ); 00081 PT(GeomTriangles) prim = new GeomTriangles(Geom::UH_stream); 00082 00083 if (_bruteforce) { 00084 // LOD Level when rendering bruteforce is always 0 (no lod) 00085 // Unless a minlevel is set- this is handled later. 00086 level = 0; 00087 } 00088 00089 // Do some calculations with the level 00090 level = min(max(_min_level, level), _max_level); 00091 unsigned short reallevel = level; 00092 level = int(pow(2.0, int(level))); 00093 00094 // Neighbor levels and junctions 00095 unsigned short lnlevel = get_neighbor_level(mx, my, -1, 0); 00096 unsigned short rnlevel = get_neighbor_level(mx, my, 1, 0); 00097 unsigned short bnlevel = get_neighbor_level(mx, my, 0, -1); 00098 unsigned short tnlevel = get_neighbor_level(mx, my, 0, 1); 00099 bool ljunction = (lnlevel != reallevel); 00100 bool rjunction = (rnlevel != reallevel); 00101 bool bjunction = (bnlevel != reallevel); 00102 bool tjunction = (tnlevel != reallevel); 00103 00104 // Confusing note: 00105 // the variable level contains not the actual level as described 00106 // in the GeoMipMapping paper. That is stored in reallevel, 00107 // while the variable level contains 2^reallevel. 00108 00109 // This is the number of vertices at the certain level. 00110 unsigned short lowblocksize = _block_size / level + 1; 00111 00112 for (int x = 0; x <= _block_size; x++) { 00113 for (int y = 0; y <= _block_size; y++) { 00114 if ((x % level) == 0 && (y % level) == 0) { 00115 if (_has_color_map) { 00116 LVecBase4d color = _color_map.get_xel_a(int((mx * _block_size + x) 00117 / double(_xsize) * _color_map.get_x_size()), 00118 int((my * _block_size + y) 00119 / double(_ysize) * _color_map.get_y_size())); 00120 cwriter.add_data4(LCAST(PN_stdfloat, color)); 00121 } 00122 vwriter.add_data3(x - 0.5 * _block_size, y - 0.5 * _block_size, get_pixel_value(mx, my, x, y)); 00123 twriter.add_data2((mx * _block_size + x) / double(_xsize - 1), 00124 (my * _block_size + y) / double(_ysize - 1)); 00125 nwriter.add_data3(get_normal(mx, my, x, y)); 00126 if (x > 0 && y > 0) { 00127 // Left border 00128 if (x == level && ljunction) { 00129 if (y > level && y < _block_size) { 00130 prim->add_vertex(min(max(sfav(y / level, lnlevel, reallevel), 0), lowblocksize - 1)); 00131 prim->add_vertex(vcounter - 1); 00132 prim->add_vertex(vcounter); 00133 prim->close_primitive(); 00134 } 00135 if (f_part((y / level) / PN_stdfloat(pow(2.0, int(lnlevel - reallevel)))) == 0.5) { 00136 prim->add_vertex(min(max(sfav(y / level + 1, lnlevel, reallevel), 0), lowblocksize - 1)); 00137 prim->add_vertex(min(max(sfav(y / level - 1, lnlevel, reallevel), 0), lowblocksize - 1)); 00138 prim->add_vertex(vcounter); 00139 prim->close_primitive(); 00140 } 00141 } else if ( 00142 (!(bjunction && y == level && x > level && x < _block_size) && 00143 !(rjunction && x == _block_size) && 00144 !(tjunction && y == _block_size && x > level && x < _block_size))) { 00145 if ((x <= center && y <= center) || (x > center && y > center)) { 00146 if (x > center) { 00147 prim->add_vertex(vcounter - lowblocksize - 1); 00148 prim->add_vertex(vcounter - 1); 00149 prim->add_vertex(vcounter); 00150 } else { 00151 prim->add_vertex(vcounter); 00152 prim->add_vertex(vcounter - lowblocksize); 00153 prim->add_vertex(vcounter - lowblocksize - 1); 00154 } 00155 } else { 00156 if (x > center) { 00157 prim->add_vertex(vcounter); 00158 prim->add_vertex(vcounter - lowblocksize); 00159 prim->add_vertex(vcounter - 1); 00160 } else { 00161 prim->add_vertex(vcounter - 1); 00162 prim->add_vertex(vcounter - lowblocksize); 00163 prim->add_vertex(vcounter - lowblocksize - 1); 00164 } 00165 } 00166 prim->close_primitive(); 00167 } 00168 // Right border 00169 if (x == _block_size - level && rjunction) { 00170 if (y > level && y < _block_size) { 00171 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level, rnlevel, reallevel), 0), lowblocksize - 1)); 00172 prim->add_vertex(vcounter); 00173 prim->add_vertex(vcounter - 1); 00174 prim->close_primitive(); 00175 } 00176 if (f_part((y / level) / PN_stdfloat(pow(2.0, int(rnlevel - reallevel)))) == 0.5) { 00177 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level - 1, rnlevel, reallevel), 0), lowblocksize - 1)); 00178 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level + 1, rnlevel, reallevel), 0), lowblocksize - 1)); 00179 prim->add_vertex(vcounter); 00180 prim->close_primitive(); 00181 } 00182 } 00183 // Bottom border 00184 if (y == level && bjunction) { 00185 if (x > level && x < _block_size) { 00186 prim->add_vertex(vcounter); 00187 prim->add_vertex(vcounter - lowblocksize); 00188 prim->add_vertex(min(max(sfav(x / level, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize); 00189 prim->close_primitive(); 00190 } 00191 if (f_part((x / level) / PN_stdfloat(pow(2.0, int(bnlevel - reallevel)))) == 0.5) { 00192 prim->add_vertex(min(max(sfav(x / level - 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize); 00193 prim->add_vertex(min(max(sfav(x / level + 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize); 00194 prim->add_vertex(vcounter); 00195 prim->close_primitive(); 00196 } 00197 } else if ( 00198 (!(ljunction && x == level && y > level && y < _block_size) && 00199 !(tjunction && y == _block_size) && 00200 !(rjunction && x == _block_size && y > level && y < _block_size))) { 00201 if ((x <= center && y <= center) || (x > center && y > center)) { 00202 if (y > center) { 00203 prim->add_vertex(vcounter); 00204 prim->add_vertex(vcounter - lowblocksize);// 00205 prim->add_vertex(vcounter - lowblocksize - 1); 00206 } else { 00207 prim->add_vertex(vcounter - lowblocksize - 1); 00208 prim->add_vertex(vcounter - 1);// 00209 prim->add_vertex(vcounter); 00210 } 00211 } else { 00212 if (y > center) { 00213 prim->add_vertex(vcounter);// 00214 prim->add_vertex(vcounter - lowblocksize); 00215 prim->add_vertex(vcounter - 1); 00216 } else { 00217 prim->add_vertex(vcounter - 1); 00218 prim->add_vertex(vcounter - lowblocksize); 00219 prim->add_vertex(vcounter - lowblocksize - 1);// 00220 } 00221 } 00222 prim->close_primitive(); 00223 } 00224 // Top border 00225 if (y == _block_size - level && tjunction) { 00226 if (x > level && x < _block_size) { 00227 prim->add_vertex(min(max(sfav(x / level, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1); 00228 prim->add_vertex(vcounter - lowblocksize); 00229 prim->add_vertex(vcounter); 00230 prim->close_primitive(); 00231 } 00232 if (f_part((x / level) / PN_stdfloat(pow(2.0, int(tnlevel - reallevel)))) == 0.5) { 00233 prim->add_vertex(min(max(sfav(x / level + 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1); 00234 prim->add_vertex(min(max(sfav(x / level - 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1); 00235 prim->add_vertex(vcounter); 00236 prim->close_primitive(); 00237 } 00238 } 00239 } 00240 vcounter++; 00241 } 00242 } 00243 } 00244 00245 PT(Geom) geom = new Geom(vdata); 00246 geom->add_primitive(prim); 00247 geom->set_bounds_type(BoundingVolume::BT_box); 00248 00249 ostringstream sname; 00250 sname << "gmm" << mx << "x" << my; 00251 PT(GeomNode) node = new GeomNode(sname.str()); 00252 node->add_geom(geom); 00253 node->set_bounds_type(BoundingVolume::BT_box); 00254 _old_levels.at(mx).at(my) = reallevel; 00255 00256 return node; 00257 } 00258 00259 //////////////////////////////////////////////////////////////////// 00260 // Function: GeoMipTerrain::get_elevation 00261 // Access: Published 00262 // Description: Fetches the elevation at (x, y), where the input 00263 // coordinate is specified in pixels. This ignores 00264 // the current LOD level and instead provides an 00265 // accurate number. Linear blending is used for 00266 // non-integral coordinates. 00267 // Terrain scale is NOT taken into account! To get 00268 // accurate normals, please multiply this with the 00269 // terrain Z scale! 00270 // 00271 // trueElev = terr.get_elevation(x,y) * terr.get_sz(); 00272 //////////////////////////////////////////////////////////////////// 00273 double GeoMipTerrain:: 00274 get_elevation(double x, double y) { 00275 y = (_ysize - 1) - y; 00276 unsigned int xlo = (unsigned int) x; 00277 unsigned int ylo = (unsigned int) y; 00278 if (xlo < 0) xlo = 0; 00279 if (ylo < 0) ylo = 0; 00280 if (xlo > _xsize - 2) 00281 xlo = _xsize - 2; 00282 if (ylo > _ysize - 2) 00283 ylo = _ysize - 2; 00284 unsigned int xhi = xlo + 1; 00285 unsigned int yhi = ylo + 1; 00286 double xoffs = x - xlo; 00287 double yoffs = y - ylo; 00288 double grayxlyl = get_pixel_value(xlo, ylo); 00289 double grayxhyl = get_pixel_value(xhi, ylo); 00290 double grayxlyh = get_pixel_value(xlo, yhi); 00291 double grayxhyh = get_pixel_value(xhi, yhi); 00292 double lerpyl = grayxhyl * xoffs + grayxlyl * (1.0 - xoffs); 00293 double lerpyh = grayxhyh * xoffs + grayxlyh * (1.0 - xoffs); 00294 return lerpyh * yoffs + lerpyl * (1.0 - yoffs); 00295 } 00296 00297 //////////////////////////////////////////////////////////////////// 00298 // Function: GeoMipTerrain::get_normal 00299 // Access: Published 00300 // Description: Fetches the terrain normal at (x, y), where the 00301 // input coordinate is specified in pixels. This 00302 // ignores the current LOD level and instead provides 00303 // an accurate number. 00304 // Terrain scale is NOT taken into account! To get 00305 // accurate normals, please divide it by the 00306 // terrain scale and normalize it again, like this: 00307 // 00308 // LVector3 normal (terr.get_normal(x, y)); 00309 // normal.set(normal.get_x() / root.get_sx(), 00310 // normal.get_y() / root.get_sy(), 00311 // normal.get_z() / root.get_sz()); 00312 // normal.normalize(); 00313 //////////////////////////////////////////////////////////////////// 00314 LVector3 GeoMipTerrain:: 00315 get_normal(int x, int y) { 00316 int nx = x - 1; 00317 int px = x + 1; 00318 int ny = y - 1; 00319 int py = y + 1; 00320 if (nx < 0) nx++; 00321 if (ny < 0) ny++; 00322 if (px >= int(_xsize)) px--; 00323 if (py >= int(_ysize)) py--; 00324 double drx = get_pixel_value(px, y) - get_pixel_value(nx, y); 00325 double dry = get_pixel_value(x, py) - get_pixel_value(x, ny); 00326 LVector3 normal(drx * 0.5, dry * 0.5, 1); 00327 normal.normalize(); 00328 00329 return normal; 00330 } 00331 00332 //////////////////////////////////////////////////////////////////// 00333 // Function: GeoMipTerrain::make_slope_image 00334 // Access: Published 00335 // Description: Returns a new grayscale image containing the slope 00336 // angles. A white pixel value means a vertical slope, 00337 // while a black pixel will mean that the terrain is 00338 // entirely flat at that pixel. 00339 // You can translate it to degrees by mapping the 00340 // greyscale values from 0 to 90 degrees. 00341 // The resulting image will have the same size as the 00342 // heightfield image. 00343 // The scale will be taken into respect -- meaning, 00344 // if you change the terrain scale, the slope image 00345 // will need to be regenerated in order to be correct. 00346 //////////////////////////////////////////////////////////////////// 00347 PNMImage GeoMipTerrain:: 00348 make_slope_image() { 00349 PNMImage result (_xsize, _ysize); 00350 result.make_grayscale(); 00351 for (unsigned int x = 0; x < _xsize; ++x) { 00352 for (unsigned int y = 0; y < _ysize; ++y) { 00353 LVector3 normal (get_normal(x, y)); 00354 normal.set(normal.get_x() / _root.get_sx(), 00355 normal.get_y() / _root.get_sy(), 00356 normal.get_z() / _root.get_sz()); 00357 normal.normalize(); 00358 result.set_xel(x, y, normal.angle_deg(LVector3::up()) / 90.0); 00359 } 00360 } 00361 return result; 00362 } 00363 00364 //////////////////////////////////////////////////////////////////// 00365 // Function: GeoMipTerrain::calc_ambient_occlusion 00366 // Access: Published 00367 // Description: Calculates an approximate for the ambient occlusion 00368 // and stores it in the color map, so that it will be 00369 // written to the vertex colors. Any existing color 00370 // map will be discarded. 00371 // You need to call this before generating the geometry. 00372 //////////////////////////////////////////////////////////////////// 00373 void GeoMipTerrain:: 00374 calc_ambient_occlusion(PN_stdfloat radius, PN_stdfloat contrast, PN_stdfloat brightness) { 00375 _color_map = PNMImage(_xsize, _ysize); 00376 _color_map.make_grayscale(); 00377 _color_map.set_maxval(_heightfield.get_maxval()); 00378 00379 for (unsigned int x = 0; x < _xsize; ++x) { 00380 for (unsigned int y = 0; y < _ysize; ++y) { 00381 _color_map.set_xel(x, _ysize - y - 1, get_pixel_value(x, y)); 00382 } 00383 } 00384 00385 // We use the cheap old method of subtracting a blurred version 00386 // of the heightmap from the heightmap, and using that as lightmap. 00387 _color_map.gaussian_filter(radius); 00388 00389 for (unsigned int x = 0; x < _xsize; ++x) { 00390 for (unsigned int y = 0; y < _ysize; ++y) { 00391 _color_map.set_xel(x, y, (get_pixel_value(x, _ysize - y - 1) - _color_map.get_gray(x, y)) * contrast + brightness); 00392 } 00393 } 00394 00395 _has_color_map = true; 00396 } 00397 00398 //////////////////////////////////////////////////////////////////// 00399 // Function: GeoMipTerrain::generate 00400 // Access: Published 00401 // Description: (Re)generates the entire terrain, erasing the 00402 // current. 00403 // This call un-flattens the terrain, so make sure 00404 // you have set auto-flatten if you want to keep 00405 // your terrain flattened. 00406 //////////////////////////////////////////////////////////////////// 00407 void GeoMipTerrain:: 00408 generate() { 00409 if (_xsize < 3 || _ysize < 3) { 00410 grutil_cat.error() << "No valid heightfield image has been set!\n"; 00411 return; 00412 } 00413 calc_levels(); 00414 _root.node()->remove_all_children(); 00415 _blocks.clear(); 00416 _old_levels.clear(); 00417 _old_levels.resize(int((_xsize - 1) / _block_size)); 00418 _root_flattened = false; 00419 for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) { 00420 _old_levels[mx].resize(int((_ysize - 1) / _block_size)); 00421 pvector<NodePath> tvector; // Create temporary row 00422 for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) { 00423 if (_bruteforce) { 00424 tvector.push_back(_root.attach_new_node(generate_block(mx, my, 0))); 00425 } else { 00426 tvector.push_back(_root.attach_new_node(generate_block(mx, my, _levels[mx][my]))); 00427 } 00428 tvector[my].set_pos((mx + 0.5) * _block_size, (my + 0.5) * _block_size, 0); 00429 } 00430 _blocks.push_back(tvector); // Push the new row of NodePaths into the 2d vect 00431 tvector.clear(); 00432 } 00433 auto_flatten(); 00434 _is_dirty = false; 00435 } 00436 00437 //////////////////////////////////////////////////////////////////// 00438 // Function: GeoMipTerrain::update 00439 // Access: Published 00440 // Description: Loops through all of the terrain blocks, and 00441 // checks whether they need to be updated. 00442 // If that is indeed the case, it regenerates the 00443 // mipmap. Returns a true when the terrain has 00444 // changed. Returns false when the terrain isn't 00445 // updated at all. If there is no terrain yet, 00446 // it generates the entire terrain. 00447 // This call un-flattens the terrain, so make sure 00448 // you have set auto-flatten if you want to keep 00449 // your terrain flattened. 00450 //////////////////////////////////////////////////////////////////// 00451 bool GeoMipTerrain:: 00452 update() { 00453 if (_xsize < 3 || _ysize < 3) { 00454 grutil_cat.error() << "No valid heightfield image has been set!\n"; 00455 return false; 00456 } 00457 if (_is_dirty) { 00458 generate(); 00459 return true; 00460 } else if (!_bruteforce) { 00461 calc_levels(); 00462 if (root_flattened()) { 00463 _root.node()->remove_all_children(); 00464 unsigned int xsize = _blocks.size(); 00465 for (unsigned int tx = 0; tx < xsize; tx++) { 00466 unsigned int ysize = _blocks[tx].size(); 00467 for (unsigned int ty = 0;ty < ysize; ty++) { 00468 _blocks[tx][ty].reparent_to(_root); 00469 } 00470 } 00471 _root_flattened = false; 00472 } 00473 bool returnVal = false; 00474 for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) { 00475 for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) { 00476 bool isUpd (update_block(mx, my)); 00477 if (isUpd && mx > 0 && _old_levels[mx - 1][my] == _levels[mx - 1][my]) { 00478 if (update_block(mx - 1, my, -1, true)) { 00479 returnVal = true; 00480 } 00481 } 00482 if (isUpd && mx < (_ysize - 1)/_block_size - 1 00483 && _old_levels[mx + 1][my] == _levels[mx + 1][my]) { 00484 if (update_block(mx + 1, my, -1, true)) { 00485 returnVal = true; 00486 } 00487 } 00488 if (isUpd && my > 0 && _old_levels[mx][my - 1] == _levels[mx][my - 1]) { 00489 if (update_block(mx, my - 1, -1, true)) { 00490 returnVal = true; 00491 } 00492 } 00493 if (isUpd && my < (_ysize - 1)/_block_size - 1 00494 && _old_levels[mx][my + 1] == _levels[mx][my + 1]) { 00495 if (update_block(mx, my + 1, -1, true)) { 00496 returnVal = true; 00497 } 00498 } 00499 if (isUpd) { 00500 returnVal = true; 00501 } 00502 } 00503 } 00504 auto_flatten(); 00505 return returnVal; 00506 } 00507 return false; 00508 } 00509 00510 //////////////////////////////////////////////////////////////////// 00511 // Function: GeoMipTerrain::root_flattened 00512 // Access: Private 00513 // Description: Normally, the root's children are the terrain blocks. 00514 // However, if we call flatten_strong on the root, 00515 // then the root will contain unpredictable stuff. 00516 // This function returns true if the root has been 00517 // flattened, and therefore, does not contain the 00518 // terrain blocks. 00519 //////////////////////////////////////////////////////////////////// 00520 bool GeoMipTerrain:: 00521 root_flattened() { 00522 if (_root_flattened) { 00523 return true; 00524 } 00525 00526 // The following code is error-checking code. It actually verifies 00527 // that the terrain blocks are underneath the root, and that nothing 00528 // else is underneath the root. It is not very efficient, and should 00529 // eventually be removed once we're sure everything works. 00530 00531 int total = 0; 00532 unsigned int xsize = _blocks.size(); 00533 for (unsigned int tx = 0; tx < xsize; tx++) { 00534 unsigned int ysize = _blocks[tx].size(); 00535 for (unsigned int ty = 0;ty < ysize; ty++) { 00536 if (_blocks[tx][ty].get_node(1) != _root.node()) { 00537 grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled!\n"; 00538 return true; 00539 } 00540 total += 1; 00541 } 00542 } 00543 if (total != _root.node()->get_num_children()) { 00544 grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled: " << total << " vs " << (_root.node()->get_num_children()) << "\n"; 00545 return true; 00546 } 00547 00548 // The default. 00549 return false; 00550 } 00551 00552 //////////////////////////////////////////////////////////////////// 00553 // Function: GeoMipTerrain::auto_flatten 00554 // Access: Private 00555 // Description: Flattens the geometry under the root. 00556 //////////////////////////////////////////////////////////////////// 00557 void GeoMipTerrain:: 00558 auto_flatten() { 00559 if (_auto_flatten == AFM_off) { 00560 return; 00561 } 00562 00563 // Creating a backup node causes the SceneGraphReducer 00564 // to operate in a nondestructive manner. This protects 00565 // the terrain blocks themselves from the flattener. 00566 00567 NodePath np("Backup Node"); 00568 np.node()->copy_children(_root.node()); 00569 00570 // Check if the root's children have changed unexpectedly. 00571 switch(_auto_flatten) { 00572 case AFM_light: _root.flatten_light(); break; 00573 case AFM_medium: _root.flatten_medium(); break; 00574 case AFM_strong: _root.flatten_strong(); break; 00575 } 00576 00577 _root_flattened = true; 00578 } 00579 00580 //////////////////////////////////////////////////////////////////// 00581 // Function: GeoMipTerrain::calc_levels 00582 // Access: Private 00583 // Description: Loops through all of the terrain blocks, and 00584 // calculates on what level they should be generated. 00585 //////////////////////////////////////////////////////////////////// 00586 void GeoMipTerrain:: 00587 calc_levels() { 00588 nassertv(_xsize >= 3 && _ysize >= 3); 00589 _levels.clear(); 00590 for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) { 00591 pvector<unsigned short> tvector; //create temporary row 00592 for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) { 00593 if(_bruteforce) { 00594 tvector.push_back(0); 00595 } else { 00596 tvector.push_back(min(short(max(_min_level, lod_decide(mx, my))), 00597 short(_max_level))); 00598 } 00599 } 00600 _levels.push_back(tvector); //push the new row of levels into the 2d vector 00601 tvector.clear(); 00602 } 00603 } 00604 00605 //////////////////////////////////////////////////////////////////// 00606 // Function: GeoMipTerrain::update_block 00607 // Access: Private 00608 // Description: Checks whether the specified mipmap at (mx,my) 00609 // needs to be updated, if so, it regenerates the 00610 // mipmap. Returns a true when it has generated 00611 // a mipmap. Returns false when the mipmap is already 00612 // at the desired level, or when there is no terrain 00613 // to update. Note: This does not affect neighboring 00614 // blocks, so does NOT fix t-junctions. You will have 00615 // to fix that by forced updating the neighboring 00616 // chunks as well, with the same levels. 00617 // NOTE: do NOT call this when the terrain is marked 00618 // dirty. If the terrain is dirty, you will need to 00619 // call update() or generate() first. 00620 // You can check this by calling GeoMipTerrain::is_dirty(). 00621 //////////////////////////////////////////////////////////////////// 00622 bool GeoMipTerrain:: 00623 update_block(unsigned short mx, unsigned short my, 00624 signed short level, bool forced) { 00625 nassertr_always(!_is_dirty, false); 00626 nassertr_always(mx < (_xsize - 1) / _block_size, false); 00627 nassertr_always(my < (_ysize - 1) / _block_size, false); 00628 if (level < 0) { 00629 level = _levels[mx][my]; 00630 } 00631 level = min(max(_min_level, (unsigned short) level), _max_level); 00632 if (forced || _old_levels[mx][my] != level) { // If the level has changed... 00633 // Replaces the chunk with a regenerated one. 00634 generate_block(mx, my, level)->replace_node(_blocks[mx][my].node()); 00635 return true; 00636 } 00637 return false; 00638 } 00639 00640 //////////////////////////////////////////////////////////////////// 00641 // Function: GeoMipTerrain::set_heightfield 00642 // Access: Published 00643 // Description: Loads the specified heightmap image file into 00644 // the heightfield. Returns true if succeeded, or 00645 // false if an error has occured. 00646 // If the heightmap is not a power of two plus one, 00647 // it is scaled up using a gaussian filter. 00648 //////////////////////////////////////////////////////////////////// 00649 bool GeoMipTerrain:: 00650 set_heightfield(const Filename &filename, PNMFileType *ftype) { 00651 // First, we need to load the header to determine the size and format. 00652 PNMImageHeader imgheader; 00653 if (imgheader.read_header(filename, ftype)) { 00654 // Copy over the header to the heightfield image. 00655 _heightfield.copy_header_from(imgheader); 00656 00657 if(!is_power_of_two(imgheader.get_x_size() - 1) || !is_power_of_two(imgheader.get_y_size() - 1)) { 00658 // Calculate the nearest power-of-two-plus-one size. 00659 unsigned int reqx, reqy; 00660 reqx = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_x_size() - 1)) / log(2.0))) + 1); 00661 reqy = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_y_size() - 1)) / log(2.0))) + 1); 00662 00663 // If it's not a valid size, tell PNMImage to resize it. 00664 if (reqx != (unsigned int)imgheader.get_x_size() || reqy != (unsigned int)imgheader.get_y_size()) { 00665 grutil_cat.warning() 00666 << "Rescaling heightfield image " << filename 00667 << " from " << imgheader.get_x_size() << "x" << imgheader.get_y_size() 00668 << " to " << reqx << "x" << reqy << " pixels.\n"; 00669 _heightfield.set_read_size(reqx, reqy); 00670 } 00671 } 00672 00673 // Read the real image now 00674 if (!_heightfield.read(filename, ftype)) { 00675 _heightfield.clear_read_size(); 00676 grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n"; 00677 return false; 00678 } 00679 00680 _is_dirty = true; 00681 _xsize = _heightfield.get_x_size(); 00682 _ysize = _heightfield.get_y_size(); 00683 return true; 00684 } else { 00685 grutil_cat.error() << "Failed to load heightfield image " << filename << "!\n"; 00686 } 00687 return false; 00688 } 00689 00690 //////////////////////////////////////////////////////////////////// 00691 // Function: GeoMipTerrain::get_neighbor_level 00692 // Access: Private 00693 // Description: Helper function for generate(). 00694 //////////////////////////////////////////////////////////////////// 00695 unsigned short GeoMipTerrain:: 00696 get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy) { 00697 // If we're across the terrain border, check if we want stitching. 00698 // If not, return the same level as this one - it won't have to make junctions. 00699 if ((int)mx + (int)dmx < 0 || (int)mx + (int)dmx >= ((int)_xsize - 1) / (int)_block_size || 00700 (int)my + (int)dmy < 0 || (int)my + (int)dmy >= ((int)_ysize - 1) / (int)_block_size) { 00701 return (_stitching) ? _max_level : min(max(_min_level, _levels[mx][my]), _max_level); 00702 } 00703 // If we're rendering bruteforce, the level must be the same as this one. 00704 if (_bruteforce) { 00705 return min(max(_min_level, _levels[mx][my]), _max_level); 00706 } 00707 // Only if the level is higher than the current. 00708 // Otherwise, the junctions will be made for the other chunk. 00709 if (_levels[mx + dmx][my + dmy] > _levels[mx][my]) { 00710 return min(max(_min_level, _levels[mx + dmx][my + dmy]), _max_level); 00711 } else { 00712 return min(max(_min_level, _levels[mx][my]), _max_level); 00713 } 00714 } 00715