commit ambient occlusion for terrain

This commit is contained in:
rdb 2011-10-08 18:02:38 +00:00
parent 58124af9f5
commit feaac60df4
3 changed files with 75 additions and 44 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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<pvector<NodePath> > _blocks;
pvector<pvector<unsigned short> > _levels;
pvector<pvector<unsigned short> > _old_levels;
public:
static TypeHandle get_class_type() {
return _type_handle;