Explorar el Código

Overhauled T-Junction and border-decision math, added border stitching

rdb hace 16 años
padre
commit
d5af6f1cce
Se han modificado 3 ficheros con 170 adiciones y 126 borrados
  1. 32 67
      panda/src/grutil/geoMipTerrain.I
  2. 133 54
      panda/src/grutil/geoMipTerrain.cxx
  3. 5 5
      panda/src/grutil/geoMipTerrain.h

+ 32 - 67
panda/src/grutil/geoMipTerrain.I

@@ -1,6 +1,5 @@
 // Filename: geoMipTerrain.I
 // Created by:  pro-rsoft (29jun07)
-// Last updated by: pro-rsoft (30oct08)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -40,6 +39,7 @@ GeoMipTerrain(const string &name) {
   _focal_is_temporary = true;
   _is_dirty = true;
   _bruteforce = false;
+  _stitching = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -420,45 +420,6 @@ lod_decide(unsigned short mx, unsigned short my) {
 //               If the heightmap is not a power of two plus one,
 //               it is scaled up using a gaussian filter.
 ////////////////////////////////////////////////////////////////////
-INLINE bool GeoMipTerrain::
-set_heightfield(const Filename &filename, PNMFileType *ftype) {
-  // First, we need to load the header to determine the size and format.
-  PNMImageHeader imgheader;
-  if (imgheader.read_header(filename, ftype)) {
-    // Copy over the header to the heightfield image.
-    _heightfield.copy_header_from(imgheader);
-    
-    if(!is_power_of_two(imgheader.get_x_size() - 1) || !is_power_of_two(imgheader.get_y_size() - 1)) {
-      // Calculate the nearest power-of-two-plus-one size.
-      unsigned int reqx, reqy;
-      reqx = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_x_size() - 1)) / log(2.0))) + 1);
-      reqy = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_y_size() - 1)) / log(2.0))) + 1);
-      
-      // If it's not a valid size, tell PNMImage to resize it.
-      if (reqx != imgheader.get_x_size() || reqy != imgheader.get_y_size()) {
-        grutil_cat.warning() << "Rescaling heightfield image " << filename << " to "
-                                             << reqx << " by " << reqy << " pixels\n";
-        _heightfield.set_read_size(reqx, reqy);
-      }
-    }
-    
-    // Read the real image now
-    if (!_heightfield.read(filename, ftype)) {
-      _heightfield.clear_read_size();
-      grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n";
-      return false;
-    }
-    
-    _is_dirty = true;
-    _xsize = _heightfield.get_x_size();
-    _ysize = _heightfield.get_y_size();
-    return true;
-  } else {
-    grutil_cat.error() << "Failed to load heightfield image " << filename << "!\n";
-  }
-  return false;
-}
-
 INLINE bool GeoMipTerrain::
 set_heightfield(const PNMImage &image) {
   // Before we apply anything, validate the size.
@@ -541,6 +502,37 @@ clear_color_map() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeoMipTerrain::set_border_stitching
+//       Access: Published
+//  Description: If this value is true, the LOD level at the
+//               borders of the terrain will be 0. This is useful
+//               if you have multiple terrains attached and you
+//               want to stitch them together, to fix seams.
+//               This setting also has effect when bruteforce is
+//               enabled, although in that case you are probably
+//               better off with setting the minlevels to the same
+//               value.
+////////////////////////////////////////////////////////////////////
+INLINE void GeoMipTerrain::
+set_border_stitching(bool stitching) {
+  if (stitching && !_stitching) {
+    _is_dirty = true;
+  }
+  _stitching = stitching;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeoMipTerrain::get_stitching
+//       Access: Published
+//  Description: Returns the current stitching setting. False by
+//               default, unless set_stitching has been set.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeoMipTerrain::
+get_border_stitching() {
+  return _stitching;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeoMipTerrain::get_pixel_value
 //       Access: Private
@@ -587,33 +579,6 @@ get_normal(unsigned short mx, unsigned short my, int x, int y) {
                     (my * _block_size + y));
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeoMipTerrain::int2str
-//       Access: Private
-//  Description: Converts the given int to a std::string.
-////////////////////////////////////////////////////////////////////
-INLINE std::string GeoMipTerrain::
-int_to_str(int i) {
-  std::stringstream ss;
-  std::string str;
-  ss << i;
-  ss >> str;
-  return str;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeoMipTerrain::str2int
-//       Access: Private
-//  Description: Converts the given std::string to an int.
-////////////////////////////////////////////////////////////////////
-INLINE int GeoMipTerrain::
-str_to_int(std::string str) {
-  std::istringstream strin(str);
-  int i;
-  strin >> i;
-  return i;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeoMipTerrain::is_power_of_two
 //       Access: Private

+ 133 - 54
panda/src/grutil/geoMipTerrain.cxx

@@ -1,6 +1,5 @@
 // Filename: geoMipTerrain.cxx
 // Created by:  pro-rsoft (29jun07)
-// Last updated by: pro-rsoft (17jan09)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -82,17 +81,24 @@ generate_block(unsigned short mx,
 
   if (_bruteforce) {
     // LOD Level when rendering bruteforce is always 0 (no lod)
+    // Unless a minlevel is set- this is handled later.
     level = 0;
   }
 
   // Do some calculations with the level
-  level = min(short(max(_min_level, level)), short(_max_level));
+  level = min(max(_min_level, level), _max_level);
   unsigned short reallevel = level;
   level = int(pow(2.0, int(level)));
   
-  // Stores the max and min heights.
-  float minh = 1.0f;
-  float maxh = 0.0f;
+  // Neighbor levels and junctions
+  unsigned short lnlevel = get_neighbor_level(mx, my, -1,  0);
+  unsigned short rnlevel = get_neighbor_level(mx, my,  1,  0);
+  unsigned short bnlevel = get_neighbor_level(mx, my,  0, -1);
+  unsigned short tnlevel = get_neighbor_level(mx, my,  0,  1);
+  bool ljunction = (lnlevel != reallevel);
+  bool rjunction = (rnlevel != reallevel);
+  bool bjunction = (bnlevel != reallevel);
+  bool tjunction = (tnlevel != reallevel);
   
   // Confusing note:
   // the variable level contains not the actual level as described
@@ -101,49 +107,43 @@ generate_block(unsigned short mx,
 
   // This is the number of vertices at the certain level.
   unsigned short lowblocksize = _block_size / level + 1;
-  
+srand(time(NULL));  
   for (int x = 0; x <= _block_size; x++) {
     for (int y = 0; y <= _block_size; y++) {
       if ((x % level) == 0 && (y % level) == 0) {
         if (_has_color_map) {
-          LVecBase4d color = _color_map.get_xel_a(int((mx * _block_size + x)
-                                  / double(_xsize) * _color_map.get_x_size()),
-                                                      int((my * _block_size + y)
-                                  / double(_ysize) * _color_map.get_y_size()));
-          cwriter.add_data4f(color.get_x(), color.get_y(),
-                             color.get_z(), color.get_w());
+          //LVecBase4d color = _color_map.get_xel_a(int((mx * _block_size + x)
+                                  /// double(_xsize) * _color_map.get_x_size()),
+//                                                      int((my * _block_size + y)
+//                                  / double(_ysize) * _color_map.get_y_size()));
+          //cwriter.add_data4f(LCAST(float, color));
+
+          cwriter.add_data4f(rand() / float(RAND_MAX), rand() / float(RAND_MAX), rand() / float(RAND_MAX), 1);
         }
         float pval = get_pixel_value(mx, my, x, y);
-        if (pval < minh) minh = pval;
-        if (pval > maxh) maxh = pval;
         vwriter.add_data3f(x - 0.5 * _block_size, y - 0.5 * _block_size, pval);
         twriter.add_data2f((mx * _block_size + x) / double(_xsize - 1),
                            (my * _block_size + y) / double(_ysize - 1));
         nwriter.add_data3f(get_normal(mx, my, x, y));
         if (x > 0 && y > 0) {
-          //left border
-          if (!_bruteforce && x == level && mx > 0 && _levels[mx - 1][my] > reallevel) {
+          // Left border
+          if (x == level && ljunction) {
             if (y > level && y < _block_size) {
-              prim->add_vertex(min(max(sfav(y / level, _levels[mx - 1][my], reallevel), 0), lowblocksize - 1));
+              prim->add_vertex(min(max(sfav(y / level, lnlevel, reallevel), 0), lowblocksize - 1));
               prim->add_vertex(vcounter - 1);
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
-            if (f_part((y / level) / float(pow(2.0, int(_levels[mx - 1][my] - reallevel)))) == 0.5) {
-              prim->add_vertex(min(max(sfav(y / level + 1, _levels[mx - 1][my], reallevel), 0), lowblocksize - 1));
-              prim->add_vertex(min(max(sfav(y / level - 1, _levels[mx - 1][my], reallevel), 0), lowblocksize - 1));
+            if (f_part((y / level) / float(pow(2.0, int(lnlevel - reallevel)))) == 0.5) {
+              prim->add_vertex(min(max(sfav(y / level + 1, lnlevel, reallevel), 0), lowblocksize - 1));
+              prim->add_vertex(min(max(sfav(y / level - 1, lnlevel, reallevel), 0), lowblocksize - 1));
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
-          } else if (_bruteforce ||
-                      (!(y == level && x > level && x < _block_size && my > 0
-                                          && _levels[mx][my - 1] > reallevel) &&
-                       !(x == _block_size && mx < (_xsize - 1) / (_block_size) - 1
-                                          && _levels[mx + 1][my] > reallevel) &&
-                       !(x == _block_size && y > level && y < _block_size && mx < (_xsize - 1) / (_block_size) - 1
-                                          && _levels[mx + 1][my] > reallevel) &&
-                       !(y == _block_size && x > level && x < _block_size && my < (_ysize - 1) / (_block_size) - 1
-                                          && _levels[mx][my + 1] > reallevel))) {
+          } else if (
+             (!(bjunction && y == level && x > level && x < _block_size) &&
+              !(rjunction && x == _block_size) &&
+              !(tjunction && y == _block_size && x > level && x < _block_size))) {
             if ((x <= center && y <= center) || (x > center && y > center)) {
               if (x > center) {
                 prim->add_vertex(vcounter - lowblocksize - 1);
@@ -167,36 +167,39 @@ generate_block(unsigned short mx,
             }
             prim->close_primitive();
           }
-          //right border
-          if (!_bruteforce && x == _block_size - level && mx < (_xsize - 1) / (_block_size) - 1 && _levels[mx + 1][my] > reallevel) {
-            if (y > level && y < _block_size - level + 1) {
-              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level, _levels[mx + 1][my], reallevel), 0), lowblocksize - 1));
+          // Right border
+          if (x == _block_size - level && rjunction) {
+            if (y > level && y < _block_size) {
+              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level, rnlevel, reallevel), 0), lowblocksize - 1));
               prim->add_vertex(vcounter);
               prim->add_vertex(vcounter - 1);
               prim->close_primitive();
             }
-            if (f_part((y / level)/float(pow(2.0, int(_levels[mx + 1][my]-reallevel)))) == 0.5) {
-              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level - 1, _levels[mx + 1][my], reallevel), 0), lowblocksize - 1));
-              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level + 1, _levels[mx + 1][my], reallevel), 0), lowblocksize - 1));
+            if (f_part((y / level) / float(pow(2.0, int(rnlevel - reallevel)))) == 0.5) {
+              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level - 1, rnlevel, reallevel), 0), lowblocksize - 1));
+              prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level + 1, rnlevel, reallevel), 0), lowblocksize - 1));
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
           }
-          //bottom border
-          if (!_bruteforce && y == level && my > 0 && _levels[mx][my - 1] > reallevel) {
+          // Bottom border
+          if (y == level && bjunction) {
             if (x > level && x < _block_size) {
               prim->add_vertex(vcounter);
               prim->add_vertex(vcounter - lowblocksize);
-              prim->add_vertex(min(max(sfav(x / level, _levels[mx][my - 1], reallevel), 0), lowblocksize - 1) * lowblocksize);
+              prim->add_vertex(min(max(sfav(x / level, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
               prim->close_primitive();
             }
-            if (f_part((x / level)/float(pow(2.0, int(_levels[mx][my - 1]-reallevel)))) == 0.5) {
-              prim->add_vertex(min(max(sfav(x / level - 1, _levels[mx][my - 1], reallevel), 0), lowblocksize - 1) * lowblocksize);
-              prim->add_vertex(min(max(sfav(x / level + 1, _levels[mx][my - 1], reallevel), 0), lowblocksize - 1) * lowblocksize);
+            if (f_part((x / level) / float(pow(2.0, int(bnlevel - reallevel)))) == 0.5) {
+              prim->add_vertex(min(max(sfav(x / level - 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
+              prim->add_vertex(min(max(sfav(x / level + 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
-          } else if (_bruteforce || (!(x == level && y > level && y < _block_size && mx > 0 && _levels[mx - 1][my] > reallevel) && !(x == _block_size && y > level && y < _block_size && mx < (_xsize - 1) / (_block_size) - 1 && _levels[mx + 1][my] > reallevel) && !(x == _block_size && y > level && y < _block_size && mx < (_xsize - 1) / (_block_size) - 1 && _levels[mx + 1][my] > reallevel) && !(y == _block_size && my < (_ysize - 1) / (_block_size) - 1 && _levels[mx][my + 1] > reallevel))) {
+          } else if (
+             (!(ljunction && x == level && y > level && y < _block_size) &&
+              !(tjunction && y == _block_size) &&
+              !(rjunction && x == _block_size && y > level && y < _block_size))) {
             if ((x <= center && y <= center) || (x > center && y > center)) {
               if (y > center) {
                 prim->add_vertex(vcounter);
@@ -220,17 +223,17 @@ generate_block(unsigned short mx,
             }
             prim->close_primitive();
           }
-          //top border
-          if (!_bruteforce && y == _block_size - level && my < (_xsize - 1) / (_block_size) - 1 && _levels[mx][my + 1] > reallevel) {
-            if (x > level && x < _block_size - level + 1) {
-              prim->add_vertex(min(max(sfav(x / level, _levels[mx][my + 1], reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
+          // Top border
+          if (y == _block_size - level && tjunction) {
+            if (x > level && x < _block_size) {
+              prim->add_vertex(min(max(sfav(x / level, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
               prim->add_vertex(vcounter - lowblocksize);
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
-            if (f_part((x / level)/float(pow(2.0, int(_levels[mx][my + 1]-reallevel)))) == 0.5) {
-              prim->add_vertex(min(max(sfav(x / level + 1, _levels[mx][my + 1], reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
-              prim->add_vertex(min(max(sfav(x / level - 1, _levels[mx][my + 1], reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
+            if (f_part((x / level) / float(pow(2.0, int(tnlevel - reallevel)))) == 0.5) {
+              prim->add_vertex(min(max(sfav(x / level + 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
+              prim->add_vertex(min(max(sfav(x / level - 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
               prim->add_vertex(vcounter);
               prim->close_primitive();
             }
@@ -244,8 +247,10 @@ generate_block(unsigned short mx,
   PT(Geom) geom = new Geom(vdata);
   geom->add_primitive(prim);
   geom->set_bounds_type(BoundingVolume::BT_box);
-
-  PT(GeomNode) node = new GeomNode("gmm" + int_to_str(mx) + "x" + int_to_str(my));
+  
+  ostringstream sname;
+  sname << "gmm" << mx << "x" << my;
+  PT(GeomNode) node = new GeomNode(sname.str());
   node->add_geom(geom);
   node->set_bounds_type(BoundingVolume::BT_box);
   _old_levels.at(mx).at(my) = reallevel;
@@ -381,7 +386,7 @@ generate() {
   _root_flattened = false;
   for (unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
     _old_levels[mx].resize(int((_ysize - 1) / _block_size));
-    pvector<NodePath> tvector; //create temporary row
+    pvector<NodePath> tvector; // Create temporary row
     for (unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
       if (_bruteforce) {
         tvector.push_back(_root.attach_new_node(generate_block(mx, my, 0)));
@@ -390,7 +395,7 @@ generate() {
       }
       tvector[my].set_pos((mx + 0.5) * _block_size, (my + 0.5) * _block_size, 0);
     }
-    _blocks.push_back(tvector); //push the new row of NodePaths into the 2d vect
+    _blocks.push_back(tvector); // Push the new row of NodePaths into the 2d vect
     tvector.clear();
   }
   auto_flatten();
@@ -600,3 +605,77 @@ update_block(unsigned short mx, unsigned short my,
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeoMipTerrain::set_heightfield
+//       Access: Published
+//  Description: Loads the specified heightmap image file into
+//               the heightfield. Returns true if succeeded, or
+//               false if an error has occured.
+//               If the heightmap is not a power of two plus one,
+//               it is scaled up using a gaussian filter.
+////////////////////////////////////////////////////////////////////
+bool GeoMipTerrain::
+set_heightfield(const Filename &filename, PNMFileType *ftype) {
+  // First, we need to load the header to determine the size and format.
+  PNMImageHeader imgheader;
+  if (imgheader.read_header(filename, ftype)) {
+    // Copy over the header to the heightfield image.
+    _heightfield.copy_header_from(imgheader);
+    
+    if(!is_power_of_two(imgheader.get_x_size() - 1) || !is_power_of_two(imgheader.get_y_size() - 1)) {
+      // Calculate the nearest power-of-two-plus-one size.
+      unsigned int reqx, reqy;
+      reqx = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_x_size() - 1)) / log(2.0))) + 1);
+      reqy = max(3, (int) pow(2.0, ceil(log((double) max(2, imgheader.get_y_size() - 1)) / log(2.0))) + 1);
+      
+      // If it's not a valid size, tell PNMImage to resize it.
+      if (reqx != imgheader.get_x_size() || reqy != imgheader.get_y_size()) {
+        grutil_cat.warning() << "Rescaling heightfield image " << filename << " to "
+                                             << reqx << " by " << reqy << " pixels\n";
+        _heightfield.set_read_size(reqx, reqy);
+      }
+    }
+    
+    // Read the real image now
+    if (!_heightfield.read(filename, ftype)) {
+      _heightfield.clear_read_size();
+      grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n";
+      return false;
+    }
+    
+    _is_dirty = true;
+    _xsize = _heightfield.get_x_size();
+    _ysize = _heightfield.get_y_size();
+    return true;
+  } else {
+    grutil_cat.error() << "Failed to load heightfield image " << filename << "!\n";
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeoMipTerrain::get_neighbor_level
+//       Access: Private
+//  Description: Helper function for generate().
+////////////////////////////////////////////////////////////////////
+unsigned short GeoMipTerrain::
+get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy) {
+  // If we're across the terrain border, check if we want stitching.
+  // If not, return the same level as this one - it won't have to make junctions.
+  if (mx + dmx < 0 || mx + dmx >= (_xsize - 1) / _block_size ||
+      my + dmy < 0 || my + dmy >= (_ysize - 1) / _block_size) {
+    return (_stitching) ? _max_level : min(max(_min_level, _levels[mx][my]), _max_level);
+  }
+  // If we're rendering bruteforce, the level must be the same as this one.
+  if (_bruteforce) {
+    return min(max(_min_level, _levels[mx][my]), _max_level);
+  }
+  // Only if the level is higher than the current.
+  // Otherwise, the junctions will be made for the other chunk.
+  if (_levels[mx + dmx][my + dmy] > _levels[mx][my]) {
+    return min(max(_min_level, _levels[mx + dmx][my + dmy]), _max_level);
+  } else {
+    return min(max(_min_level, _levels[mx][my]), _max_level);
+  }
+}
+

+ 5 - 5
panda/src/grutil/geoMipTerrain.h

@@ -1,6 +1,5 @@
 // Filename: geoMipTerrain.h
 // Created by:  pro-rsoft (29jun07)
-// Last updated by: pro-rsoft (25sep08)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -44,8 +43,7 @@ PUBLISHED:
   INLINE ~GeoMipTerrain();
   
   INLINE PNMImage &heightfield();
-  INLINE bool set_heightfield(const Filename &filename,
-                                    PNMFileType *type = NULL);
+  bool set_heightfield(const Filename &filename, PNMFileType *type = NULL);
   INLINE bool set_heightfield(const PNMImage &image);
   INLINE bool set_heightfield(const string &path);
   INLINE PNMImage &color_map();
@@ -104,6 +102,8 @@ PUBLISHED:
   INLINE const NodePath get_block_node_path(unsigned short mx,
                                             unsigned short my);
   INLINE LVecBase2f get_block_from_pos(double x, double y);
+  INLINE void set_border_stitching(bool stitching);
+  INLINE bool get_border_stitching();
   
   PNMImage make_slope_image();
   void generate();
@@ -118,8 +118,6 @@ private:
   void auto_flatten();
   bool root_flattened();
   
-  INLINE std::string int_to_str(int i);
-  INLINE int str_to_int(std::string str);
   INLINE bool is_power_of_two(unsigned int i);
   INLINE float f_part(float i);
   INLINE double f_part(double i);
@@ -127,6 +125,7 @@ private:
   INLINE double get_pixel_value(int x, int y);
   INLINE double get_pixel_value(unsigned short mx, unsigned short my, int x, int y);
   INLINE unsigned short lod_decide(unsigned short mx, unsigned short my);
+  unsigned short get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy);
   
   NodePath _root;
   int _auto_flatten;
@@ -147,6 +146,7 @@ private:
   NodePath _focal_point;
   bool _focal_is_temporary;
   unsigned short _min_level;
+  bool _stitching;
   pvector<pvector<NodePath> > _blocks;
   pvector<pvector<unsigned short> > _levels;
   pvector<pvector<unsigned short> > _old_levels;