diff --git a/panda/src/grutil/geoMipTerrain.I b/panda/src/grutil/geoMipTerrain.I index 5e5745a543..c3ec8f8a09 100644 --- a/panda/src/grutil/geoMipTerrain.I +++ b/panda/src/grutil/geoMipTerrain.I @@ -1,7 +1,5 @@ // 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. //////////////////////////////////////////////////////////////////// INLINE GeoMipTerrain:: -~GeoMipTerrain() { +~GeoMipTerrain() { } //////////////////////////////////////////////////////////////////// @@ -364,7 +362,7 @@ get_near() { //////////////////////////////////////////////////////////////////// // Function: GeoMipTerrain::get_flatten_mode // 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) //////////////////////////////////////////////////////////////////// INLINE int GeoMipTerrain:: @@ -420,7 +418,7 @@ get_block_from_pos(double x, double y) { //////////////////////////////////////////////////////////////////// INLINE unsigned short GeoMipTerrain:: lod_decide(unsigned short mx, unsigned short my) { - float cx = mx; + float cx = mx; float cy = my; cx = (cx * _block_size + _block_size / 2) * _root.get_sx(); 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. //////////////////////////////////////////////////////////////////// INLINE bool GeoMipTerrain:: -has_color_map() { +has_color_map() const { return _has_color_map; } diff --git a/panda/src/grutil/geoMipTerrain.cxx b/panda/src/grutil/geoMipTerrain.cxx index db15b8aef9..3942a44d5b 100644 --- a/panda/src/grutil/geoMipTerrain.cxx +++ b/panda/src/grutil/geoMipTerrain.cxx @@ -1,5 +1,5 @@ // Filename: geoMipTerrain.cxx -// Created by: pro-rsoft (29jun07) +// Created by: rdb (29Jun07) // //////////////////////////////////////////////////////////////////// // @@ -45,13 +45,13 @@ PT(GeomNode) GeoMipTerrain:: generate_block(unsigned short mx, unsigned short my, unsigned short level) { - + nassertr(mx < (_xsize - 1) / _block_size, NULL); nassertr(my < (_ysize - 1) / _block_size, NULL); unsigned short center = _block_size / 2; unsigned int vcounter = 0; - + // Create the format PT(GeomVertexArrayFormat) array = new GeomVertexArrayFormat(); if (_has_color_map) { @@ -73,7 +73,7 @@ generate_block(unsigned short mx, vdata->unclean_set_num_rows((_block_size + 1) * (_block_size + 1)); GeomVertexWriter cwriter; if (_has_color_map) { - cwriter=GeomVertexWriter(vdata, "color" ); + cwriter = GeomVertexWriter(vdata, "color"); } GeomVertexWriter vwriter (vdata, "vertex" ); GeomVertexWriter twriter (vdata, "texcoord"); @@ -90,7 +90,7 @@ generate_block(unsigned short mx, level = min(max(_min_level, level), _max_level); unsigned short reallevel = level; level = int(pow(2.0, int(level))); - + // Neighbor levels and junctions unsigned short lnlevel = 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 bjunction = (bnlevel != reallevel); bool tjunction = (tnlevel != reallevel); - + // Confusing note: // the variable level contains not the actual level as described // 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. unsigned short lowblocksize = _block_size / level + 1; - + for (int x = 0; x <= _block_size; x++) { for (int y = 0; y <= _block_size; y++) { if ((x % level) == 0 && (y % level) == 0) { @@ -245,14 +245,14 @@ generate_block(unsigned short mx, PT(Geom) geom = new Geom(vdata); geom->add_primitive(prim); geom->set_bounds_type(BoundingVolume::BT_box); - + 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; - + return node; } @@ -262,7 +262,7 @@ generate_block(unsigned short mx, // Description: Fetches the elevation at (x, y), where the input // coordinate is specified in pixels. This ignores // 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. // Terrain scale is NOT taken into account! To get // accurate normals, please multiply this with the @@ -355,12 +355,46 @@ make_slope_image() { normal.get_y() / _root.get_sy(), normal.get_z() / _root.get_sz()); 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; } +//////////////////////////////////////////////////////////////////// +// 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 // Access: Published @@ -480,7 +514,7 @@ update() { // However, if we call flatten_strong on the root, // then the root will contain unpredictable stuff. // 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. //////////////////////////////////////////////////////////////////// bool GeoMipTerrain:: @@ -488,12 +522,12 @@ root_flattened() { if (_root_flattened) { return true; } - + // The following code is error-checking code. It actually verifies // that the terrain blocks are underneath the root, and that nothing // else is underneath the root. It is not very efficient, and should // eventually be removed once we're sure everything works. - + int total = 0; unsigned int xsize = _blocks.size(); 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"; return true; } - + // The default. return false; } @@ -525,21 +559,21 @@ auto_flatten() { if (_auto_flatten == AFM_off) { return; } - + // Creating a backup node causes the SceneGraphReducer // to operate in a nondestructive manner. This protects // the terrain blocks themselves from the flattener. NodePath np("Backup Node"); np.node()->copy_children(_root.node()); - + // Check if the root's children have changed unexpectedly. switch(_auto_flatten) { case AFM_light: _root.flatten_light(); break; case AFM_medium: _root.flatten_medium(); break; case AFM_strong: _root.flatten_strong(); break; } - + _root_flattened = true; } @@ -619,13 +653,13 @@ set_heightfield(const Filename &filename, PNMFileType *ftype) { 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 != (unsigned int)imgheader.get_x_size() || reqy != (unsigned int)imgheader.get_y_size()) { grutil_cat.warning() @@ -635,14 +669,14 @@ set_heightfield(const Filename &filename, PNMFileType *ftype) { _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(); diff --git a/panda/src/grutil/geoMipTerrain.h b/panda/src/grutil/geoMipTerrain.h index ebc55c26d3..45be5cb39f 100644 --- a/panda/src/grutil/geoMipTerrain.h +++ b/panda/src/grutil/geoMipTerrain.h @@ -1,7 +1,5 @@ // 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: INLINE GeoMipTerrain(const string &name); INLINE ~GeoMipTerrain(); - + INLINE PNMImage &heightfield(); bool set_heightfield(const Filename &filename, PNMFileType *type = NULL); 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 Texture *image); INLINE bool set_color_map(const string &path); - INLINE bool has_color_map(); + INLINE bool has_color_map() const; 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); 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); INLINE void set_bruteforce(bool bf); INLINE bool get_bruteforce(); - + // The flatten mode specifies whether the terrain nodes are flattened // together after each terrain update. enum AutoFlattenMode { @@ -75,9 +74,9 @@ PUBLISHED: // FM_strong: the terrain is flattened using flatten_strong. AFM_strong = 3, }; - + INLINE void set_auto_flatten(int mode); - + // The focal point is the point at which the terrain will have the // highest quality (lowest level of detail). Parts farther away from // 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 NodePath get_focal_point() const; INLINE NodePath get_root() const; - + INLINE void set_block_size(unsigned short newbs); INLINE unsigned short get_block_size(); INLINE unsigned short get_max_level(); @@ -113,16 +112,16 @@ PUBLISHED: PNMImage make_slope_image(); void generate(); bool update(); - + private: - + PT(GeomNode) generate_block(unsigned short mx, unsigned short my, unsigned short level); bool update_block(unsigned short mx, unsigned short my, signed short level = -1, bool forced = false); void calc_levels(); void auto_flatten(); bool root_flattened(); - + INLINE bool is_power_of_two(unsigned int i); INLINE float f_part(float 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 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; bool _root_flattened; @@ -155,7 +154,7 @@ private: pvector > _blocks; pvector > _levels; pvector > _old_levels; - + public: static TypeHandle get_class_type() { return _type_handle;