Panda3D

geoMipTerrain.cxx

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 
 All Classes Functions Variables Enumerations