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