Browse Source

commit ambient occlusion for terrain

rdb 14 years ago
parent
commit
feaac60df4

+ 5 - 7
panda/src/grutil/geoMipTerrain.I

@@ -1,7 +1,5 @@
 // Filename: geoMipTerrain.I
 // Filename: geoMipTerrain.I
-// Created by:  pro-rsoft (29jun07)
-// Modified by: CMU ETC Summer 2010 team (03aug10) (added getters
-//   for _auto_flatten, _near, _far).
+// Created by:  rdb (29Jun07)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -52,7 +50,7 @@ GeoMipTerrain(const string &name) {
 //               call remove_node() prior to destruction.
 //               call remove_node() prior to destruction.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeoMipTerrain::
 INLINE GeoMipTerrain::
-~GeoMipTerrain() { 
+~GeoMipTerrain() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -364,7 +362,7 @@ get_near() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeoMipTerrain::get_flatten_mode
 //     Function: GeoMipTerrain::get_flatten_mode
 //       Access: Published
 //       Access: Published
-//  Description: Returns the automatic-flatten mode (e.g., off, 
+//  Description: Returns the automatic-flatten mode (e.g., off,
 //               flatten_light, flatten_medium, or flatten_strong)
 //               flatten_light, flatten_medium, or flatten_strong)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeoMipTerrain::
 INLINE int GeoMipTerrain::
@@ -420,7 +418,7 @@ get_block_from_pos(double x, double y) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE unsigned short GeoMipTerrain::
 INLINE unsigned short GeoMipTerrain::
 lod_decide(unsigned short mx, unsigned short my) {
 lod_decide(unsigned short mx, unsigned short my) {
-  float cx = mx; 
+  float cx = mx;
   float cy = my;
   float cy = my;
   cx = (cx * _block_size + _block_size / 2) * _root.get_sx();
   cx = (cx * _block_size + _block_size / 2) * _root.get_sx();
   cy = (cy * _block_size + _block_size / 2) * _root.get_sy();
   cy = (cy * _block_size + _block_size / 2) * _root.get_sy();
@@ -520,7 +518,7 @@ set_color_map(const string &path) {
 //  Description: Returns whether a color map has been set.
 //  Description: Returns whether a color map has been set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeoMipTerrain::
 INLINE bool GeoMipTerrain::
-has_color_map() {
+has_color_map() const {
   return _has_color_map;
   return _has_color_map;
 }
 }
 
 

+ 56 - 22
panda/src/grutil/geoMipTerrain.cxx

@@ -1,5 +1,5 @@
 // Filename: geoMipTerrain.cxx
 // Filename: geoMipTerrain.cxx
-// Created by:  pro-rsoft (29jun07)
+// Created by:  rdb (29Jun07)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -45,13 +45,13 @@ PT(GeomNode) GeoMipTerrain::
 generate_block(unsigned short mx,
 generate_block(unsigned short mx,
                unsigned short my,
                unsigned short my,
                unsigned short level) {
                unsigned short level) {
-  
+
   nassertr(mx < (_xsize - 1) / _block_size, NULL);
   nassertr(mx < (_xsize - 1) / _block_size, NULL);
   nassertr(my < (_ysize - 1) / _block_size, NULL);
   nassertr(my < (_ysize - 1) / _block_size, NULL);
 
 
   unsigned short center = _block_size / 2;
   unsigned short center = _block_size / 2;
   unsigned int vcounter = 0;
   unsigned int vcounter = 0;
-  
+
   // Create the format
   // Create the format
   PT(GeomVertexArrayFormat) array = new GeomVertexArrayFormat();
   PT(GeomVertexArrayFormat) array = new GeomVertexArrayFormat();
   if (_has_color_map) {
   if (_has_color_map) {
@@ -73,7 +73,7 @@ generate_block(unsigned short mx,
   vdata->unclean_set_num_rows((_block_size + 1) * (_block_size + 1));
   vdata->unclean_set_num_rows((_block_size + 1) * (_block_size + 1));
   GeomVertexWriter cwriter;
   GeomVertexWriter cwriter;
   if (_has_color_map) {
   if (_has_color_map) {
-    cwriter=GeomVertexWriter(vdata, "color"  );
+    cwriter = GeomVertexWriter(vdata, "color");
   }
   }
   GeomVertexWriter vwriter (vdata, "vertex"  );
   GeomVertexWriter vwriter (vdata, "vertex"  );
   GeomVertexWriter twriter (vdata, "texcoord");
   GeomVertexWriter twriter (vdata, "texcoord");
@@ -90,7 +90,7 @@ generate_block(unsigned short mx,
   level = min(max(_min_level, level), _max_level);
   level = min(max(_min_level, level), _max_level);
   unsigned short reallevel = level;
   unsigned short reallevel = level;
   level = int(pow(2.0, int(level)));
   level = int(pow(2.0, int(level)));
-  
+
   // Neighbor levels and junctions
   // Neighbor levels and junctions
   unsigned short lnlevel = get_neighbor_level(mx, my, -1,  0);
   unsigned short lnlevel = get_neighbor_level(mx, my, -1,  0);
   unsigned short rnlevel = get_neighbor_level(mx, my,  1,  0);
   unsigned short rnlevel = get_neighbor_level(mx, my,  1,  0);
@@ -100,7 +100,7 @@ generate_block(unsigned short mx,
   bool rjunction = (rnlevel != reallevel);
   bool rjunction = (rnlevel != reallevel);
   bool bjunction = (bnlevel != reallevel);
   bool bjunction = (bnlevel != reallevel);
   bool tjunction = (tnlevel != reallevel);
   bool tjunction = (tnlevel != reallevel);
-  
+
   // Confusing note:
   // Confusing note:
   // the variable level contains not the actual level as described
   // the variable level contains not the actual level as described
   // in the GeoMipMapping paper. That is stored in reallevel,
   // in the GeoMipMapping paper. That is stored in reallevel,
@@ -108,7 +108,7 @@ generate_block(unsigned short mx,
 
 
   // This is the number of vertices at the certain level.
   // This is the number of vertices at the certain level.
   unsigned short lowblocksize = _block_size / level + 1;
   unsigned short lowblocksize = _block_size / level + 1;
-  
+
   for (int x = 0; x <= _block_size; x++) {
   for (int x = 0; x <= _block_size; x++) {
     for (int y = 0; y <= _block_size; y++) {
     for (int y = 0; y <= _block_size; y++) {
       if ((x % level) == 0 && (y % level) == 0) {
       if ((x % level) == 0 && (y % level) == 0) {
@@ -245,14 +245,14 @@ generate_block(unsigned short mx,
   PT(Geom) geom = new Geom(vdata);
   PT(Geom) geom = new Geom(vdata);
   geom->add_primitive(prim);
   geom->add_primitive(prim);
   geom->set_bounds_type(BoundingVolume::BT_box);
   geom->set_bounds_type(BoundingVolume::BT_box);
-  
+
   ostringstream sname;
   ostringstream sname;
   sname << "gmm" << mx << "x" << my;
   sname << "gmm" << mx << "x" << my;
   PT(GeomNode) node = new GeomNode(sname.str());
   PT(GeomNode) node = new GeomNode(sname.str());
   node->add_geom(geom);
   node->add_geom(geom);
   node->set_bounds_type(BoundingVolume::BT_box);
   node->set_bounds_type(BoundingVolume::BT_box);
   _old_levels.at(mx).at(my) = reallevel;
   _old_levels.at(mx).at(my) = reallevel;
-  
+
   return node;
   return node;
 }
 }
 
 
@@ -262,7 +262,7 @@ generate_block(unsigned short mx,
 //  Description: Fetches the elevation at (x, y), where the input
 //  Description: Fetches the elevation at (x, y), where the input
 //               coordinate is specified in pixels. This ignores
 //               coordinate is specified in pixels. This ignores
 //               the current LOD level and instead provides an
 //               the current LOD level and instead provides an
-//               accurate number. Linear blending is used for 
+//               accurate number. Linear blending is used for
 //               non-integral coordinates.
 //               non-integral coordinates.
 //               Terrain scale is NOT taken into account! To get
 //               Terrain scale is NOT taken into account! To get
 //               accurate normals, please multiply this with the
 //               accurate normals, please multiply this with the
@@ -355,12 +355,46 @@ make_slope_image() {
                  normal.get_y() / _root.get_sy(),
                  normal.get_y() / _root.get_sy(),
                  normal.get_z() / _root.get_sz());
                  normal.get_z() / _root.get_sz());
       normal.normalize();
       normal.normalize();
-      result.set_gray(x, y, normal.angle_deg(LVector3f::up()) / 90.0);
+      result.set_xel(x, y, normal.angle_deg(LVector3f::up()) / 90.0);
     }
     }
   }
   }
   return result;
   return result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeoMipTerrain::calc_ambient_occlusion
+//       Access: Published
+//  Description: Calculates an approximate for the ambient occlusion
+//               and stores it in the color map, so that it will be
+//               written to the vertex colors. Any existing color
+//               map will be discarded.
+//               You need to call this before generating the geometry.
+////////////////////////////////////////////////////////////////////
+void GeoMipTerrain::
+calc_ambient_occlusion(float radius, float contrast, float brightness) {
+  _color_map = PNMImage(_xsize, _ysize);
+  _color_map.make_grayscale();
+  _color_map.set_maxval(_heightfield.get_maxval());
+
+  for (unsigned int x = 0; x < _xsize; ++x) {
+    for (unsigned int y = 0; y < _ysize; ++y) {
+      _color_map.set_xel(x, _ysize - y - 1, get_pixel_value(x, y));
+    }
+  }
+
+  // We use the cheap old method of subtracting a blurred version
+  // of the heightmap from the heightmap, and using that as lightmap.
+  _color_map.gaussian_filter(radius);
+
+  for (unsigned int x = 0; x < _xsize; ++x) {
+    for (unsigned int y = 0; y < _ysize; ++y) {
+      _color_map.set_xel(x, y, (get_pixel_value(x, _ysize - y - 1) - _color_map.get_gray(x, y)) * contrast + brightness);
+    }
+  }
+
+  _has_color_map = true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeoMipTerrain::generate
 //     Function: GeoMipTerrain::generate
 //       Access: Published
 //       Access: Published
@@ -480,7 +514,7 @@ update() {
 //               However, if we call flatten_strong on the root,
 //               However, if we call flatten_strong on the root,
 //               then the root will contain unpredictable stuff.
 //               then the root will contain unpredictable stuff.
 //               This function returns true if the root has been
 //               This function returns true if the root has been
-//               flattened, and therefore, does not contain the 
+//               flattened, and therefore, does not contain the
 //               terrain blocks.
 //               terrain blocks.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool GeoMipTerrain::
 bool GeoMipTerrain::
@@ -488,12 +522,12 @@ root_flattened() {
   if (_root_flattened) {
   if (_root_flattened) {
     return true;
     return true;
   }
   }
-  
+
   // The following code is error-checking code.  It actually verifies
   // The following code is error-checking code.  It actually verifies
   // that the terrain blocks are underneath the root, and that nothing
   // that the terrain blocks are underneath the root, and that nothing
   // else is underneath the root.  It is not very efficient, and should
   // else is underneath the root.  It is not very efficient, and should
   // eventually be removed once we're sure everything works.
   // eventually be removed once we're sure everything works.
-  
+
   int total = 0;
   int total = 0;
   unsigned int xsize = _blocks.size();
   unsigned int xsize = _blocks.size();
   for (unsigned int tx = 0; tx < xsize; tx++) {
   for (unsigned int tx = 0; tx < xsize; tx++) {
@@ -510,7 +544,7 @@ root_flattened() {
     grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled: " << total << " vs " << (_root.node()->get_num_children()) << "\n";
     grutil_cat.error() << "GeoMipTerrain: root node unexpectedly mangled: " << total << " vs " << (_root.node()->get_num_children()) << "\n";
     return true;
     return true;
   }
   }
-  
+
   // The default.
   // The default.
   return false;
   return false;
 }
 }
@@ -525,21 +559,21 @@ auto_flatten() {
   if (_auto_flatten == AFM_off) {
   if (_auto_flatten == AFM_off) {
     return;
     return;
   }
   }
-  
+
   // Creating a backup node causes the SceneGraphReducer
   // Creating a backup node causes the SceneGraphReducer
   // to operate in a nondestructive manner.  This protects
   // to operate in a nondestructive manner.  This protects
   // the terrain blocks themselves from the flattener.
   // the terrain blocks themselves from the flattener.
 
 
   NodePath np("Backup Node");
   NodePath np("Backup Node");
   np.node()->copy_children(_root.node());
   np.node()->copy_children(_root.node());
-  
+
   // Check if the root's children have changed unexpectedly.
   // Check if the root's children have changed unexpectedly.
   switch(_auto_flatten) {
   switch(_auto_flatten) {
   case AFM_light:  _root.flatten_light();  break;
   case AFM_light:  _root.flatten_light();  break;
   case AFM_medium: _root.flatten_medium(); break;
   case AFM_medium: _root.flatten_medium(); break;
   case AFM_strong: _root.flatten_strong(); break;
   case AFM_strong: _root.flatten_strong(); break;
   }
   }
-  
+
   _root_flattened = true;
   _root_flattened = true;
 }
 }
 
 
@@ -619,13 +653,13 @@ set_heightfield(const Filename &filename, PNMFileType *ftype) {
   if (imgheader.read_header(filename, ftype)) {
   if (imgheader.read_header(filename, ftype)) {
     // Copy over the header to the heightfield image.
     // Copy over the header to the heightfield image.
     _heightfield.copy_header_from(imgheader);
     _heightfield.copy_header_from(imgheader);
-    
+
     if(!is_power_of_two(imgheader.get_x_size() - 1) || !is_power_of_two(imgheader.get_y_size() - 1)) {
     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.
       // Calculate the nearest power-of-two-plus-one size.
       unsigned int reqx, reqy;
       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);
       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);
       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 it's not a valid size, tell PNMImage to resize it.
       if (reqx != (unsigned int)imgheader.get_x_size() || reqy != (unsigned int)imgheader.get_y_size()) {
       if (reqx != (unsigned int)imgheader.get_x_size() || reqy != (unsigned int)imgheader.get_y_size()) {
         grutil_cat.warning()
         grutil_cat.warning()
@@ -635,14 +669,14 @@ set_heightfield(const Filename &filename, PNMFileType *ftype) {
         _heightfield.set_read_size(reqx, reqy);
         _heightfield.set_read_size(reqx, reqy);
       }
       }
     }
     }
-    
+
     // Read the real image now
     // Read the real image now
     if (!_heightfield.read(filename, ftype)) {
     if (!_heightfield.read(filename, ftype)) {
       _heightfield.clear_read_size();
       _heightfield.clear_read_size();
       grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n";
       grutil_cat.error() << "Failed to read heightfield image " << filename << "!\n";
       return false;
       return false;
     }
     }
-    
+
     _is_dirty = true;
     _is_dirty = true;
     _xsize = _heightfield.get_x_size();
     _xsize = _heightfield.get_x_size();
     _ysize = _heightfield.get_y_size();
     _ysize = _heightfield.get_y_size();

+ 14 - 15
panda/src/grutil/geoMipTerrain.h

@@ -1,7 +1,5 @@
 // Filename: geoMipTerrain.h
 // Filename: geoMipTerrain.h
-// Created by:  pro-rsoft (29jun07)
-// Modified by: CMU ETC Summer 2010 team (03aug10) (added
-//   get_flatten_mode(), get_near(), get_far() ).
+// Created by:  rdb (29Jun07)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -43,7 +41,7 @@ class EXPCL_PANDA_GRUTIL GeoMipTerrain : public TypedObject {
 PUBLISHED:
 PUBLISHED:
   INLINE GeoMipTerrain(const string &name);
   INLINE GeoMipTerrain(const string &name);
   INLINE ~GeoMipTerrain();
   INLINE ~GeoMipTerrain();
-  
+
   INLINE PNMImage &heightfield();
   INLINE PNMImage &heightfield();
   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 PNMImage &image);
@@ -54,15 +52,16 @@ PUBLISHED:
   INLINE bool set_color_map(const PNMImage &image);
   INLINE bool set_color_map(const PNMImage &image);
   INLINE bool set_color_map(const Texture *image);
   INLINE bool set_color_map(const Texture *image);
   INLINE bool set_color_map(const string &path);
   INLINE bool set_color_map(const string &path);
-  INLINE bool has_color_map();
+  INLINE bool has_color_map() const;
   INLINE void clear_color_map();
   INLINE void clear_color_map();
+  void calc_ambient_occlusion(float radius = 32, float contrast = 2.0f, float brightness = 0.75f);
   double get_elevation(double x, double y);
   double get_elevation(double x, double y);
   LVector3f get_normal(int x, int y);
   LVector3f get_normal(int x, int y);
-  INLINE LVector3f get_normal(unsigned short mx, unsigned short my, 
+  INLINE LVector3f get_normal(unsigned short mx, unsigned short my,
                                                           int x,int y);
                                                           int x,int y);
   INLINE void set_bruteforce(bool bf);
   INLINE void set_bruteforce(bool bf);
   INLINE bool get_bruteforce();
   INLINE bool get_bruteforce();
-  
+
   // The flatten mode specifies whether the terrain nodes are flattened
   // The flatten mode specifies whether the terrain nodes are flattened
   // together after each terrain update.
   // together after each terrain update.
   enum AutoFlattenMode {
   enum AutoFlattenMode {
@@ -75,9 +74,9 @@ PUBLISHED:
     // FM_strong: the terrain is flattened using flatten_strong.
     // FM_strong: the terrain is flattened using flatten_strong.
     AFM_strong  = 3,
     AFM_strong  = 3,
   };
   };
-  
+
   INLINE void set_auto_flatten(int mode);
   INLINE void set_auto_flatten(int mode);
-  
+
   // The focal point is the point at which the terrain will have the
   // The focal point is the point at which the terrain will have the
   // highest quality (lowest level of detail). Parts farther away from
   // highest quality (lowest level of detail). Parts farther away from
   // the focal point will have a lower quality (higher level of detail).
   // the focal point will have a lower quality (higher level of detail).
@@ -90,7 +89,7 @@ PUBLISHED:
   INLINE void set_focal_point(NodePath fnp);
   INLINE void set_focal_point(NodePath fnp);
   INLINE NodePath get_focal_point() const;
   INLINE NodePath get_focal_point() const;
   INLINE NodePath get_root() const;
   INLINE NodePath get_root() const;
-  
+
   INLINE void set_block_size(unsigned short newbs);
   INLINE void set_block_size(unsigned short newbs);
   INLINE unsigned short get_block_size();
   INLINE unsigned short get_block_size();
   INLINE unsigned short get_max_level();
   INLINE unsigned short get_max_level();
@@ -113,16 +112,16 @@ PUBLISHED:
   PNMImage make_slope_image();
   PNMImage make_slope_image();
   void generate();
   void generate();
   bool update();
   bool update();
-  
+
 private:
 private:
-  
+
   PT(GeomNode) generate_block(unsigned short mx, unsigned short my, unsigned short level);
   PT(GeomNode) generate_block(unsigned short mx, unsigned short my, unsigned short level);
   bool update_block(unsigned short mx, unsigned short my,
   bool update_block(unsigned short mx, unsigned short my,
                     signed short level = -1, bool forced = false);
                     signed short level = -1, bool forced = false);
   void calc_levels();
   void calc_levels();
   void auto_flatten();
   void auto_flatten();
   bool root_flattened();
   bool root_flattened();
-  
+
   INLINE bool is_power_of_two(unsigned int i);
   INLINE bool is_power_of_two(unsigned int i);
   INLINE float f_part(float i);
   INLINE float f_part(float i);
   INLINE double f_part(double i);
   INLINE double f_part(double i);
@@ -131,7 +130,7 @@ private:
   INLINE double get_pixel_value(unsigned short mx, unsigned short my, 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);
   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);
   unsigned short get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy);
-  
+
   NodePath _root;
   NodePath _root;
   int _auto_flatten;
   int _auto_flatten;
   bool _root_flattened;
   bool _root_flattened;
@@ -155,7 +154,7 @@ private:
   pvector<pvector<NodePath> > _blocks;
   pvector<pvector<NodePath> > _blocks;
   pvector<pvector<unsigned short> > _levels;
   pvector<pvector<unsigned short> > _levels;
   pvector<pvector<unsigned short> > _old_levels;
   pvector<pvector<unsigned short> > _old_levels;
-  
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;