Panda3D

geoMipTerrain.cxx

00001 // Filename: geoMipTerrain.cxx
00002 // Created by:  pro-rsoft (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_float32, Geom::C_color);
00060   }
00061   array->add_column(InternalName::make("vertex"), 3,
00062                                             Geom::NT_float32, Geom::C_point);
00063   array->add_column(InternalName::make("texcoord"), 2,
00064                                             Geom::NT_float32, Geom::C_texcoord);
00065   array->add_column(InternalName::make("normal"), 3,
00066                                             Geom::NT_float32, 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_data4f(LCAST(float, color));
00121         }
00122         vwriter.add_data3f(x - 0.5 * _block_size, y - 0.5 * _block_size, get_pixel_value(mx, my, x, y));
00123         twriter.add_data2f((mx * _block_size + x) / double(_xsize - 1),
00124                            (my * _block_size + y) / double(_ysize - 1));
00125         nwriter.add_data3f(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) / float(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) / float(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) / float(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) / float(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 //               LVector3f 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 LVector3f 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   LVector3f 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       LVector3f 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_gray(x, y, normal.angle_deg(LVector3f::up()) / 90.0);
00359     }
00360   }
00361   return result;
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: GeoMipTerrain::generate
00366 //       Access: Published
00367 //  Description: (Re)generates the entire terrain, erasing the
00368 //               current.
00369 //               This call un-flattens the terrain, so make sure
00370 //               you have set auto-flatten if you want to keep
00371 //               your terrain flattened.
00372 ////////////////////////////////////////////////////////////////////
00373 void GeoMipTerrain::
00374 generate() {
00375   if (_xsize < 3 || _ysize < 3) {
00376     grutil_cat.error() << "No valid heightfield image has been set!\n";
00377     return;
00378   }
00379   calc_levels();
00380   _root.node()->remove_all_children();
00381   _blocks.clear();
00382   _old_levels.clear();
00383   _old_levels.resize(int((_xsize - 1) / _block_size));
00384   _root_flattened = false;
00385   for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
00386     _old_levels[mx].resize(int((_ysize - 1) / _block_size));
00387     pvector<NodePath> tvector; // Create temporary row
00388     for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
00389       if (_bruteforce) {
00390         tvector.push_back(_root.attach_new_node(generate_block(mx, my, 0)));
00391       } else {
00392         tvector.push_back(_root.attach_new_node(generate_block(mx, my, _levels[mx][my])));
00393       }
00394       tvector[my].set_pos((mx + 0.5) * _block_size, (my + 0.5) * _block_size, 0);
00395     }
00396     _blocks.push_back(tvector); // Push the new row of NodePaths into the 2d vect
00397     tvector.clear();
00398   }
00399   auto_flatten();
00400   _is_dirty = false;
00401 }
00402 
00403 ////////////////////////////////////////////////////////////////////
00404 //     Function: GeoMipTerrain::update
00405 //       Access: Published
00406 //  Description: Loops through all of the terrain blocks, and
00407 //               checks whether they need to be updated.
00408 //               If that is indeed the case, it regenerates the
00409 //               mipmap. Returns a true when the terrain has
00410 //               changed. Returns false when the terrain isn't
00411 //               updated at all. If there is no terrain yet,
00412 //               it generates the entire terrain.
00413 //               This call un-flattens the terrain, so make sure
00414 //               you have set auto-flatten if you want to keep
00415 //               your terrain flattened.
00416 ////////////////////////////////////////////////////////////////////
00417 bool GeoMipTerrain::
00418 update() {
00419   if (_xsize < 3 || _ysize < 3) {
00420     grutil_cat.error() << "No valid heightfield image has been set!\n";
00421     return false;
00422   }
00423   if (_is_dirty) {
00424     generate();
00425     return true;
00426   } else if (!_bruteforce) {
00427     calc_levels();
00428     if (root_flattened()) {
00429       _root.node()->remove_all_children();
00430       unsigned int xsize = _blocks.size();
00431       for (unsigned int tx = 0; tx < xsize; tx++) {
00432         unsigned int ysize = _blocks[tx].size();
00433         for (unsigned int ty = 0;ty < ysize; ty++) {
00434           _blocks[tx][ty].reparent_to(_root);
00435         }
00436       }
00437       _root_flattened = false;
00438     }
00439     bool returnVal = false;
00440     for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
00441       for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
00442         bool isUpd (update_block(mx, my));
00443         if (isUpd && mx > 0 && _old_levels[mx - 1][my] == _levels[mx - 1][my]) {
00444           if (update_block(mx - 1, my, -1, true)) {
00445             returnVal = true;
00446           }
00447         }
00448         if (isUpd && mx < (_ysize - 1)/_block_size - 1
00449                   && _old_levels[mx + 1][my] == _levels[mx + 1][my]) {
00450           if (update_block(mx + 1, my, -1, true)) {
00451             returnVal = true;
00452           }
00453         }
00454         if (isUpd && my > 0 && _old_levels[mx][my - 1] == _levels[mx][my - 1]) {
00455           if (update_block(mx, my - 1, -1, true)) {
00456             returnVal = true;
00457           }
00458         }
00459         if (isUpd && my < (_ysize - 1)/_block_size - 1
00460                   && _old_levels[mx][my + 1] == _levels[mx][my + 1]) {
00461           if (update_block(mx, my + 1, -1, true)) {
00462             returnVal = true;
00463           }
00464         }
00465         if (isUpd) {
00466           returnVal = true;
00467         }
00468       }
00469     }
00470     auto_flatten();
00471     return returnVal;
00472   }
00473   return false;
00474 }
00475 
00476 ////////////////////////////////////////////////////////////////////
00477 //     Function: GeoMipTerrain::root_flattened
00478 //       Access: Private
00479 //  Description: Normally, the root's children are the terrain blocks.
00480 //               However, if we call flatten_strong on the root,
00481 //               then the root will contain unpredictable stuff.
00482 //               This function returns true if the root has been
00483 //               flattened, and therefore, does not contain the 
00484 //               terrain blocks.
00485 ////////////////////////////////////////////////////////////////////
00486 bool GeoMipTerrain::
00487 root_flattened() {
00488   if (_root_flattened) {
00489     return true;
00490   }
00491   
00492   // The following code is error-checking code.  It actually verifies
00493   // that the terrain blocks are underneath the root, and that nothing
00494   // else is underneath the root.  It is not very efficient, and should
00495   // eventually be removed once we're sure everything works.
00496   
00497   int total = 0;
00498   unsigned int xsize = _blocks.size();
00499   for (unsigned int tx = 0; tx < xsize; tx++) {
00500     unsigned int ysize = _blocks[tx].size();
00501     for (unsigned int ty = 0;ty < ysize; ty++) {
00502       if (_blocks[tx][ty].get_node(1) != _root.node()) {
00503         grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled!\n";
00504         return true;
00505       }
00506       total += 1;
00507     }
00508   }
00509   if (total != _root.node()->get_num_children()) {
00510     grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled: " << total << " vs " << (_root.node()->get_num_children()) << "\n";
00511     return true;
00512   }
00513   
00514   // The default.
00515   return false;
00516 }
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: GeoMipTerrain::auto_flatten
00520 //       Access: Private
00521 //  Description: Flattens the geometry under the root.
00522 ////////////////////////////////////////////////////////////////////
00523 void GeoMipTerrain::
00524 auto_flatten() {
00525   if (_auto_flatten == AFM_off) {
00526     return;
00527   }
00528   
00529   // Creating a backup node causes the SceneGraphReducer
00530   // to operate in a nondestructive manner.  This protects
00531   // the terrain blocks themselves from the flattener.
00532 
00533   NodePath np("Backup Node");
00534   np.node()->copy_children(_root.node());
00535   
00536   // Check if the root's children have changed unexpectedly.
00537   switch(_auto_flatten) {
00538   case AFM_light:  _root.flatten_light();  break;
00539   case AFM_medium: _root.flatten_medium(); break;
00540   case AFM_strong: _root.flatten_strong(); break;
00541   }
00542   
00543   _root_flattened = true;
00544 }
00545 
00546 ////////////////////////////////////////////////////////////////////
00547 //     Function: GeoMipTerrain::calc_levels
00548 //       Access: Private
00549 //  Description: Loops through all of the terrain blocks, and
00550 //               calculates on what level they should be generated.
00551 ////////////////////////////////////////////////////////////////////
00552 void GeoMipTerrain::
00553 calc_levels() {
00554   nassertv(_xsize >= 3 && _ysize >= 3);
00555   _levels.clear();
00556   for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
00557     pvector<unsigned short> tvector; //create temporary row
00558     for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
00559       if(_bruteforce) {
00560         tvector.push_back(0);
00561       } else {
00562         tvector.push_back(min(short(max(_min_level, lod_decide(mx, my))),
00563                               short(_max_level)));
00564       }
00565     }
00566     _levels.push_back(tvector); //push the new row of levels into the 2d vector
00567     tvector.clear();
00568   }
00569 }
00570 
00571 ////////////////////////////////////////////////////////////////////
00572 //     Function: GeoMipTerrain::update_block
00573 //       Access: Private
00574 //  Description: Checks whether the specified mipmap at (mx,my)
00575 //               needs to be updated, if so, it regenerates the
00576 //               mipmap. Returns a true when it has generated
00577 //               a mipmap. Returns false when the mipmap is already
00578 //               at the desired level, or when there is no terrain
00579 //               to update. Note: This does not affect neighboring
00580 //               blocks, so does NOT fix t-junctions. You will have
00581 //               to fix that by forced updating the neighboring
00582 //               chunks as well, with the same levels.
00583 //               NOTE: do NOT call this when the terrain is marked
00584 //               dirty. If the terrain is dirty, you will need to
00585 //               call update() or generate() first.
00586 //               You can check this by calling GeoMipTerrain::is_dirty().
00587 ////////////////////////////////////////////////////////////////////
00588 bool GeoMipTerrain::
00589 update_block(unsigned short mx, unsigned short my,
00590                                   signed short level, bool forced) {
00591   nassertr_always(!_is_dirty, false);
00592   nassertr_always(mx < (_xsize - 1) / _block_size, false);
00593   nassertr_always(my < (_ysize - 1) / _block_size, false);
00594   if (level < 0) {
00595     level = _levels[mx][my];
00596   }
00597   level = min(max(_min_level, (unsigned short) level), _max_level);
00598   if (forced || _old_levels[mx][my] != level) { // If the level has changed...
00599     // Replaces the chunk with a regenerated one.
00600     generate_block(mx, my, level)->replace_node(_blocks[mx][my].node());
00601     return true;
00602   }
00603   return false;
00604 }
00605 
00606 ////////////////////////////////////////////////////////////////////
00607 //     Function: GeoMipTerrain::set_heightfield
00608 //       Access: Published
00609 //  Description: Loads the specified heightmap image file into
00610 //               the heightfield. Returns true if succeeded, or
00611 //               false if an error has occured.
00612 //               If the heightmap is not a power of two plus one,
00613 //               it is scaled up using a gaussian filter.
00614 ////////////////////////////////////////////////////////////////////
00615 bool GeoMipTerrain::
00616 set_heightfield(const Filename &filename, PNMFileType *ftype) {
00617   // First, we need to load the header to determine the size and format.
00618   PNMImageHeader imgheader;
00619   if (imgheader.read_header(filename, ftype)) {
00620     // Copy over the header to the heightfield image.
00621     _heightfield.copy_header_from(imgheader);
00622     
00623     if(!is_power_of_two(imgheader.get_x_size() - 1) || !is_power_of_two(imgheader.get_y_size() - 1)) {
00624       // Calculate the nearest power-of-two-plus-one size.
00625       unsigned int reqx, reqy;
00626       reqx = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_x_size() - 1)) / log(2.0))) + 1);
00627       reqy = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_y_size() - 1)) / log(2.0))) + 1);
00628       
00629       // If it's not a valid size, tell PNMImage to resize it.
00630       if (reqx != imgheader.get_x_size() || reqy != imgheader.get_y_size()) {
00631         grutil_cat.warning() << "Rescaling heightfield image " << filename
00632                              << " from " << imgheader.get_x_size() << "x" << imgheader.get_y_size()
00633                              << " to " << reqx << "x" << reqy << " pixels.\n";
00634         _heightfield.set_read_size(reqx, reqy);
00635       }
00636     }
00637     
00638     // Read the real image now
00639     if (!_heightfield.read(filename, ftype)) {
00640       _heightfield.clear_read_size();
00641       grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n";
00642       return false;
00643     }
00644     
00645     _is_dirty = true;
00646     _xsize = _heightfield.get_x_size();
00647     _ysize = _heightfield.get_y_size();
00648     return true;
00649   } else {
00650     grutil_cat.error() << "Failed to load heightfield image " << filename << "!\n";
00651   }
00652   return false;
00653 }
00654 
00655 ////////////////////////////////////////////////////////////////////
00656 //     Function: GeoMipTerrain::get_neighbor_level
00657 //       Access: Private
00658 //  Description: Helper function for generate().
00659 ////////////////////////////////////////////////////////////////////
00660 unsigned short GeoMipTerrain::
00661 get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy) {
00662   // If we're across the terrain border, check if we want stitching.
00663   // If not, return the same level as this one - it won't have to make junctions.
00664   if (mx + dmx < 0 || mx + dmx >= (_xsize - 1) / _block_size ||
00665       my + dmy < 0 || my + dmy >= (_ysize - 1) / _block_size) {
00666     return (_stitching) ? _max_level : min(max(_min_level, _levels[mx][my]), _max_level);
00667   }
00668   // If we're rendering bruteforce, the level must be the same as this one.
00669   if (_bruteforce) {
00670     return min(max(_min_level, _levels[mx][my]), _max_level);
00671   }
00672   // Only if the level is higher than the current.
00673   // Otherwise, the junctions will be made for the other chunk.
00674   if (_levels[mx + dmx][my + dmy] > _levels[mx][my]) {
00675     return min(max(_min_level, _levels[mx + dmx][my + dmy]), _max_level);
00676   } else {
00677     return min(max(_min_level, _levels[mx][my]), _max_level);
00678   }
00679 }
00680 
 All Classes Functions Variables Enumerations