15 #include "geoMipTerrain.h"
17 #include "geomVertexFormat.h"
18 #include "geomVertexArrayFormat.h"
19 #include "internalName.h"
20 #include "geomVertexData.h"
21 #include "geomVertexWriter.h"
22 #include "geomTristrips.h"
23 #include "geomTriangles.h"
26 #include "boundingBox.h"
27 #include "config_grutil.h"
29 #include "sceneGraphReducer.h"
31 #include "collideMask.h"
34 (
"geomipterrain-incorrect-normals",
true,
35 PRC_DESC(
"If true, uses the incorrect normal vector calculation that "
36 "was used in Panda3D versions 1.9.0 and earlier. If false, "
37 "uses the correct calculation. For backward compatibility, "
38 "the default value is true in 1.9 releases, and false in "
39 "Panda3D 1.10.0 and above."));
53 generate_block(
unsigned short mx,
55 unsigned short level) {
57 nassertr(mx < (_xsize - 1) / _block_size, NULL);
58 nassertr(my < (_ysize - 1) / _block_size, NULL);
60 unsigned short center = _block_size / 2;
61 unsigned int vcounter = 0;
64 PT(GeomVertexArrayFormat) array = new GeomVertexArrayFormat();
66 array->add_column(InternalName::get_color(), 4,
67 Geom::NT_stdfloat, Geom::C_color);
69 array->add_column(InternalName::get_vertex(), 3,
70 Geom::NT_stdfloat, Geom::C_point);
71 array->add_column(InternalName::get_texcoord(), 2,
72 Geom::NT_stdfloat, Geom::C_texcoord);
73 array->add_column(InternalName::get_normal(), 3,
74 Geom::NT_stdfloat, Geom::C_normal);
76 PT(GeomVertexFormat) format = new GeomVertexFormat();
77 format->add_array(array);
81 GeomVertexFormat::register_format(format),
Geom::UH_stream);
82 vdata->unclean_set_num_rows((_block_size + 1) * (_block_size + 1));
99 level = min(max(_min_level, level), _max_level);
100 unsigned short reallevel = level;
101 level = int(pow(2.0,
int(level)));
104 unsigned short lnlevel = get_neighbor_level(mx, my, -1, 0);
105 unsigned short rnlevel = get_neighbor_level(mx, my, 1, 0);
106 unsigned short bnlevel = get_neighbor_level(mx, my, 0, -1);
107 unsigned short tnlevel = get_neighbor_level(mx, my, 0, 1);
108 bool ljunction = (lnlevel != reallevel);
109 bool rjunction = (rnlevel != reallevel);
110 bool bjunction = (bnlevel != reallevel);
111 bool tjunction = (tnlevel != reallevel);
119 unsigned short lowblocksize = _block_size / level + 1;
121 PN_stdfloat cmap_xratio = _color_map.get_x_size() / (PN_stdfloat)_xsize;
122 PN_stdfloat cmap_yratio = _color_map.get_y_size() / (PN_stdfloat)_ysize;
124 PN_stdfloat tc_xscale = 1.0f / PN_stdfloat(_xsize - 1);
125 PN_stdfloat tc_yscale = 1.0f / PN_stdfloat(_ysize - 1);
127 for (
int x = 0; x <= _block_size; x++) {
128 for (
int y = 0; y <= _block_size; y++) {
129 if ((x % level) == 0 && (y % level) == 0) {
130 if (_has_color_map) {
132 int((mx * _block_size + x) * cmap_xratio),
133 int((my * _block_size + y) * cmap_yratio));
136 vwriter.set_data3(x - 0.5 * _block_size, y - 0.5 * _block_size, get_pixel_value(mx, my, x, y));
137 twriter.set_data2((mx * _block_size + x) * tc_xscale,
138 (my * _block_size + y) * tc_yscale);
139 nwriter.set_data3(get_normal(mx, my, x, y));
141 if (x > 0 && y > 0) {
143 if (x == level && ljunction) {
144 if (y > level && y < _block_size) {
145 prim->add_vertex(min(max(sfav(y / level, lnlevel, reallevel), 0), lowblocksize - 1));
146 prim->add_vertex(vcounter - 1);
147 prim->add_vertex(vcounter);
148 prim->close_primitive();
150 if (f_part((y / level) / PN_stdfloat(pow(2.0,
int(lnlevel - reallevel)))) == 0.5) {
151 prim->add_vertex(min(max(sfav(y / level + 1, lnlevel, reallevel), 0), lowblocksize - 1));
152 prim->add_vertex(min(max(sfav(y / level - 1, lnlevel, reallevel), 0), lowblocksize - 1));
153 prim->add_vertex(vcounter);
154 prim->close_primitive();
157 (!(bjunction && y == level && x > level && x < _block_size) &&
158 !(rjunction && x == _block_size) &&
159 !(tjunction && y == _block_size && x > level && x < _block_size))) {
160 if ((x <= center && y <= center) || (x > center && y > center)) {
162 prim->add_vertex(vcounter - lowblocksize - 1);
163 prim->add_vertex(vcounter - 1);
164 prim->add_vertex(vcounter);
166 prim->add_vertex(vcounter);
167 prim->add_vertex(vcounter - lowblocksize);
168 prim->add_vertex(vcounter - lowblocksize - 1);
172 prim->add_vertex(vcounter);
173 prim->add_vertex(vcounter - lowblocksize);
174 prim->add_vertex(vcounter - 1);
176 prim->add_vertex(vcounter - 1);
177 prim->add_vertex(vcounter - lowblocksize);
178 prim->add_vertex(vcounter - lowblocksize - 1);
181 prim->close_primitive();
184 if (x == _block_size - level && rjunction) {
185 if (y > level && y < _block_size) {
186 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level, rnlevel, reallevel), 0), lowblocksize - 1));
187 prim->add_vertex(vcounter);
188 prim->add_vertex(vcounter - 1);
189 prim->close_primitive();
191 if (f_part((y / level) / PN_stdfloat(pow(2.0,
int(rnlevel - reallevel)))) == 0.5) {
192 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level - 1, rnlevel, reallevel), 0), lowblocksize - 1));
193 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level + 1, rnlevel, reallevel), 0), lowblocksize - 1));
194 prim->add_vertex(vcounter);
195 prim->close_primitive();
199 if (y == level && bjunction) {
200 if (x > level && x < _block_size) {
201 prim->add_vertex(vcounter);
202 prim->add_vertex(vcounter - lowblocksize);
203 prim->add_vertex(min(max(sfav(x / level, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
204 prim->close_primitive();
206 if (f_part((x / level) / PN_stdfloat(pow(2.0,
int(bnlevel - reallevel)))) == 0.5) {
207 prim->add_vertex(min(max(sfav(x / level - 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
208 prim->add_vertex(min(max(sfav(x / level + 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
209 prim->add_vertex(vcounter);
210 prim->close_primitive();
213 (!(ljunction && x == level && y > level && y < _block_size) &&
214 !(tjunction && y == _block_size) &&
215 !(rjunction && x == _block_size && y > level && y < _block_size))) {
216 if ((x <= center && y <= center) || (x > center && y > center)) {
218 prim->add_vertex(vcounter);
219 prim->add_vertex(vcounter - lowblocksize);
220 prim->add_vertex(vcounter - lowblocksize - 1);
222 prim->add_vertex(vcounter - lowblocksize - 1);
223 prim->add_vertex(vcounter - 1);
224 prim->add_vertex(vcounter);
228 prim->add_vertex(vcounter);
229 prim->add_vertex(vcounter - lowblocksize);
230 prim->add_vertex(vcounter - 1);
232 prim->add_vertex(vcounter - 1);
233 prim->add_vertex(vcounter - lowblocksize);
234 prim->add_vertex(vcounter - lowblocksize - 1);
237 prim->close_primitive();
240 if (y == _block_size - level && tjunction) {
241 if (x > level && x < _block_size) {
242 prim->add_vertex(min(max(sfav(x / level, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
243 prim->add_vertex(vcounter - lowblocksize);
244 prim->add_vertex(vcounter);
245 prim->close_primitive();
247 if (f_part((x / level) / PN_stdfloat(pow(2.0,
int(tnlevel - reallevel)))) == 0.5) {
248 prim->add_vertex(min(max(sfav(x / level + 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
249 prim->add_vertex(min(max(sfav(x / level - 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
250 prim->add_vertex(vcounter);
251 prim->close_primitive();
261 geom->add_primitive(prim);
265 sname << "gmm" << mx << "x" << my;
267 node->add_geom(geom);
269 _old_levels.at(mx).at(my) = reallevel;
289 get_elevation(
double x,
double y) {
290 y = (_ysize - 1) - y;
291 if (x < 0.0) x = 0.0;
292 if (y < 0.0) y = 0.0;
293 unsigned int xlo = (
unsigned int) x;
294 unsigned int ylo = (
unsigned int) y;
295 if (xlo > _xsize - 2)
297 if (ylo > _ysize - 2)
299 unsigned int xhi = xlo + 1;
300 unsigned int yhi = ylo + 1;
301 double xoffs = x - xlo;
302 double yoffs = y - ylo;
303 double grayxlyl = get_pixel_value(xlo, ylo);
304 double grayxhyl = get_pixel_value(xhi, ylo);
305 double grayxlyh = get_pixel_value(xlo, yhi);
306 double grayxhyh = get_pixel_value(xhi, yhi);
307 double lerpyl = grayxhyl * xoffs + grayxlyl * (1.0 - xoffs);
308 double lerpyh = grayxhyh * xoffs + grayxlyh * (1.0 - xoffs);
309 return lerpyh * yoffs + lerpyl * (1.0 - yoffs);
337 if (px >=
int(_xsize)) px--;
338 if (py >=
int(_ysize)) py--;
339 double drx = get_pixel_value(nx, y) - get_pixel_value(px, y);
340 double dry = get_pixel_value(x, py) - get_pixel_value(x, ny);
341 LVector3 normal(drx * 0.5, dry * 0.5, 1);
344 if (geomipterrain_incorrect_normals) {
345 normal[0] = -normal[0];
370 for (
unsigned int x = 0; x < _xsize; ++x) {
371 for (
unsigned int y = 0; y < _ysize; ++y) {
373 normal.set(normal.get_x() / _root.get_sx(),
374 normal.get_y() / _root.get_sy(),
375 normal.get_z() / _root.get_sz());
394 _color_map =
PNMImage(_xsize, _ysize);
398 for (
unsigned int x = 0; x < _xsize; ++x) {
399 for (
unsigned int y = 0; y < _ysize; ++y) {
400 _color_map.
set_xel(x, _ysize - y - 1, get_pixel_value(x, y));
408 for (
unsigned int x = 0; x < _xsize; ++x) {
409 for (
unsigned int y = 0; y < _ysize; ++y) {
410 _color_map.
set_xel(x, y, (get_pixel_value(x, _ysize - y - 1) - _color_map.
get_gray(x, y)) * contrast + brightness);
414 _has_color_map =
true;
428 if (_xsize < 3 || _ysize < 3) {
429 grutil_cat.error() <<
"No valid heightfield image has been set!\n";
436 _old_levels.resize(
int((_xsize - 1) / _block_size));
437 _root_flattened =
false;
438 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
439 _old_levels[mx].resize(
int((_ysize - 1) / _block_size));
441 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
445 tvector.push_back(_root.
attach_new_node(generate_block(mx, my, _levels[mx][my])));
447 tvector[my].set_pos((mx + 0.5) * _block_size, (my + 0.5) * _block_size, 0);
449 _blocks.push_back(tvector);
472 if (_xsize < 3 || _ysize < 3) {
473 grutil_cat.error() <<
"No valid heightfield image has been set!\n";
479 }
else if (!_bruteforce) {
481 if (root_flattened()) {
483 unsigned int xsize = _blocks.size();
484 for (
unsigned int tx = 0; tx < xsize; tx++) {
485 unsigned int ysize = _blocks[tx].size();
486 for (
unsigned int ty = 0;ty < ysize; ty++) {
487 _blocks[tx][ty].reparent_to(_root);
490 _root_flattened =
false;
492 bool returnVal =
false;
493 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
494 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
495 bool isUpd (update_block(mx, my));
496 if (isUpd && mx > 0 && _old_levels[mx - 1][my] == _levels[mx - 1][my]) {
497 if (update_block(mx - 1, my, -1,
true)) {
501 if (isUpd && mx < (_ysize - 1)/_block_size - 1
502 && _old_levels[mx + 1][my] == _levels[mx + 1][my]) {
503 if (update_block(mx + 1, my, -1,
true)) {
507 if (isUpd && my > 0 && _old_levels[mx][my - 1] == _levels[mx][my - 1]) {
508 if (update_block(mx, my - 1, -1,
true)) {
512 if (isUpd && my < (_ysize - 1)/_block_size - 1
513 && _old_levels[mx][my + 1] == _levels[mx][my + 1]) {
514 if (update_block(mx, my + 1, -1,
true)) {
541 if (_root_flattened) {
551 unsigned int xsize = _blocks.size();
552 for (
unsigned int tx = 0; tx < xsize; tx++) {
553 unsigned int ysize = _blocks[tx].size();
554 for (
unsigned int ty = 0;ty < ysize; ty++) {
555 if (_blocks[tx][ty].get_node(1) != _root.
node()) {
556 grutil_cat.error() <<
"GeoMipTerrain: root node unexpectedly mangled!\n";
563 grutil_cat.error() <<
"GeoMipTerrain: root node unexpectedly mangled: " << total <<
" vs " << (_root.
node()->
get_num_children()) <<
"\n";
578 if (_auto_flatten == AFM_off) {
587 np.node()->copy_children(_root.
node());
590 switch(_auto_flatten) {
596 _root_flattened =
true;
607 nassertv(_xsize >= 3 && _ysize >= 3);
609 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
611 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
613 tvector.push_back(0);
615 tvector.push_back(min(
short(max(_min_level, lod_decide(mx, my))),
619 _levels.push_back(tvector);
642 update_block(
unsigned short mx,
unsigned short my,
643 signed short level,
bool forced) {
644 nassertr_always(!_is_dirty,
false);
645 nassertr_always(mx < (_xsize - 1) / _block_size,
false);
646 nassertr_always(my < (_ysize - 1) / _block_size,
false);
648 level = _levels[mx][my];
650 level = min(max(_min_level, (
unsigned short) level), _max_level);
651 if (forced || _old_levels[mx][my] != level) {
653 generate_block(mx, my, level)->replace_node(_blocks[mx][my].node());
676 if (!is_power_of_two(imgheader.
get_x_size() - 1) ||
677 !is_power_of_two(imgheader.
get_y_size() - 1)) {
679 unsigned int reqx, reqy;
680 reqx = max(3, (
int) pow(2.0, ceil(log((
double) max(2, imgheader.
get_x_size() - 1)) / log(2.0))) + 1);
681 reqy = max(3, (
int) pow(2.0, ceil(log((
double) max(2, imgheader.
get_y_size() - 1)) / log(2.0))) + 1);
684 if (reqx != (
unsigned int)imgheader.
get_x_size() ||
685 reqy != (
unsigned int)imgheader.
get_y_size()) {
687 <<
"Rescaling heightfield image " << filename
689 <<
" to " << reqx <<
"x" << reqy <<
" pixels.\n";
697 <<
"Heightfield image is specified to have sRGB color space!\n"
698 "Panda applies gamma correction, which will probably cause "
699 "it to produce incorrect results.\n";
703 if (!_heightfield.
read(filename, ftype)) {
705 grutil_cat.error() <<
"Failed to read heightfield image " << filename <<
"!\n";
714 grutil_cat.error() <<
"Failed to load heightfield image " << filename <<
"!\n";
724 unsigned short GeoMipTerrain::
725 get_neighbor_level(
unsigned short mx,
unsigned short my,
short dmx,
short dmy) {
728 if ((
int)mx + (
int)dmx < 0 || (
int)mx + (
int)dmx >= ((
int)_xsize - 1) / (
int)_block_size ||
729 (
int)my + (
int)dmy < 0 || (
int)my + (
int)dmy >= ((
int)_ysize - 1) / (
int)_block_size) {
730 return (_stitching) ? _max_level : min(max(_min_level, _levels[mx][my]), _max_level);
734 return min(max(_min_level, _levels[mx][my]), _max_level);
738 if (_levels[mx + dmx][my + dmy] > _levels[mx][my]) {
739 return min(max(_min_level, _levels[mx + dmx][my + dmy]), _max_level);
741 return min(max(_min_level, _levels[mx][my]), _max_level);
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
GeoMipTerrain, meaning Panda3D GeoMipMapping, can convert a heightfield image into a 3D terrain...
This is a convenience class to specialize ConfigVariable as a boolean type.
bool read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
This is the base class of a family of classes that represent particular image file types that PNMImag...
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
PandaNode * node() const
Returns the referenced node of the path.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
void generate()
(Re)generates the entire terrain, erasing the current.
void clear_read_size()
Undoes the effect of a previous call to set_read_size().
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
void calc_ambient_occlusion(PN_stdfloat radius=32, PN_stdfloat contrast=2.0f, PN_stdfloat brightness=0.75f)
Calculates an approximate for the ambient occlusion and stores it in the color map, so that it will be written to the vertex colors.
void gaussian_filter(float radius=1.0)
This flavor of gaussian_filter() will apply the filter over the entire image without resizing or copy...
The name of a file, such as a texture file or an Egg file.
void set_data4f(float x, float y, float z, float w)
Sets the write row to a particular 4-component value, and advances the write row. ...
void remove_all_children(Thread *current_thread=Thread::get_current_thread())
Removes all the children from the node at once, including stashed children.
void copy_header_from(const PNMImageHeader &header)
Copies just the header information into this image.
float angle_deg(const LVector3f &other) const
Returns the angle between this vector and the other one, expressed in degrees.
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
LVector3 get_normal(int x, int y)
Fetches the terrain normal at (x, y), where the input coordinate is specified in pixels.
int flatten_light()
Lightly flattens out the hierarchy below this node by applying transforms, colors, and texture matrices from the nodes onto the vertices, but does not remove any nodes.
bool set_heightfield(const Filename &filename, PNMFileType *type=NULL)
Loads the specified heightmap image file into the heightfield.
This is the base class for all three-component vectors and points.
int flatten_strong()
The strongest possible flattening.
Defines a series of disconnected triangles.
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
TypeHandle is the identifier used to differentiate C++ class types.
void make_grayscale()
Converts the image from RGB to grayscale.
void set_read_size(int x_size, int y_size)
Specifies the size to we'd like to scale the image upon reading it.
bool normalize()
Normalizes the vector in place.
int flatten_medium()
A more thorough flattening than flatten_light(), this first applies all the transforms, colors, and texture matrices from the nodes onto the vertices, and then removes unneeded grouping nodes–nodes that have exactly one child, for instance, but have no special properties in themselves.
bool update()
Loops through all of the terrain blocks, and checks whether they need to be updated.
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
A node that holds Geom objects, renderable pieces of geometry.
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
PNMImage make_slope_image()
Returns a new grayscale image containing the slope angles.