36 (
"geomipterrain-incorrect-normals",
false,
37 PRC_DESC(
"If true, uses the incorrect normal vector calculation that " 38 "was used in Panda3D versions 1.9.0 and earlier. If false, " 39 "uses the correct calculation. For backward compatibility, " 40 "the default value is true in 1.9 releases, and false in " 41 "Panda3D 1.10.0 and above."));
52 generate_block(
unsigned short mx,
54 unsigned short level) {
56 nassertr(mx < (_xsize - 1) / _block_size,
nullptr);
57 nassertr(my < (_ysize - 1) / _block_size,
nullptr);
59 unsigned short center = _block_size / 2;
60 unsigned int vcounter = 0;
65 array->add_column(InternalName::get_color(), 4,
66 Geom::NT_stdfloat, Geom::C_color);
68 array->add_column(InternalName::get_vertex(), 3,
69 Geom::NT_stdfloat, Geom::C_point);
70 array->add_column(InternalName::get_texcoord(), 2,
71 Geom::NT_stdfloat, Geom::C_texcoord);
72 array->add_column(InternalName::get_normal(), 3,
73 Geom::NT_stdfloat, Geom::C_normal);
76 format->add_array(array);
80 GeomVertexFormat::register_format(format), Geom::UH_stream);
98 level = min(max(_min_level, level), _max_level);
99 unsigned short reallevel = level;
100 level = int(pow(2.0,
int(level)));
103 unsigned short lnlevel = get_neighbor_level(mx, my, -1, 0);
104 unsigned short rnlevel = get_neighbor_level(mx, my, 1, 0);
105 unsigned short bnlevel = get_neighbor_level(mx, my, 0, -1);
106 unsigned short tnlevel = get_neighbor_level(mx, my, 0, 1);
107 bool ljunction = (lnlevel != reallevel);
108 bool rjunction = (rnlevel != reallevel);
109 bool bjunction = (bnlevel != reallevel);
110 bool tjunction = (tnlevel != reallevel);
117 unsigned short lowblocksize = _block_size / level + 1;
119 PN_stdfloat cmap_xratio = _color_map.
get_x_size() / (PN_stdfloat)_xsize;
120 PN_stdfloat cmap_yratio = _color_map.
get_y_size() / (PN_stdfloat)_ysize;
122 PN_stdfloat tc_xscale = 1.0f / PN_stdfloat(_xsize - 1);
123 PN_stdfloat tc_yscale = 1.0f / PN_stdfloat(_ysize - 1);
125 for (
int x = 0; x <= _block_size; x++) {
126 for (
int y = 0; y <= _block_size; y++) {
127 if ((x % level) == 0 && (y % level) == 0) {
128 if (_has_color_map) {
130 int((mx * _block_size + x) * cmap_xratio),
131 int((my * _block_size + y) * cmap_yratio));
134 vwriter.
set_data3(x - 0.5 * _block_size, y - 0.5 * _block_size, get_pixel_value(mx, my, x, y));
135 twriter.
set_data2((mx * _block_size + x) * tc_xscale,
136 (my * _block_size + y) * tc_yscale);
139 if (x > 0 && y > 0) {
141 if (x == level && ljunction) {
142 if (y > level && y < _block_size) {
143 prim->add_vertex(min(max(sfav(y / level, lnlevel, reallevel), 0), lowblocksize - 1));
144 prim->add_vertex(vcounter - 1);
145 prim->add_vertex(vcounter);
146 prim->close_primitive();
148 if (f_part((y / level) / PN_stdfloat(pow(2.0,
int(lnlevel - reallevel)))) == 0.5) {
149 prim->add_vertex(min(max(sfav(y / level + 1, lnlevel, reallevel), 0), lowblocksize - 1));
150 prim->add_vertex(min(max(sfav(y / level - 1, lnlevel, reallevel), 0), lowblocksize - 1));
151 prim->add_vertex(vcounter);
152 prim->close_primitive();
155 (!(bjunction && y == level && x > level && x < _block_size) &&
156 !(rjunction && x == _block_size) &&
157 !(tjunction && y == _block_size && x > level && x < _block_size))) {
158 if ((x <= center && y <= center) || (x > center && y > center)) {
160 prim->add_vertex(vcounter - lowblocksize - 1);
161 prim->add_vertex(vcounter - 1);
162 prim->add_vertex(vcounter);
164 prim->add_vertex(vcounter);
165 prim->add_vertex(vcounter - lowblocksize);
166 prim->add_vertex(vcounter - lowblocksize - 1);
170 prim->add_vertex(vcounter);
171 prim->add_vertex(vcounter - lowblocksize);
172 prim->add_vertex(vcounter - 1);
174 prim->add_vertex(vcounter - 1);
175 prim->add_vertex(vcounter - lowblocksize);
176 prim->add_vertex(vcounter - lowblocksize - 1);
179 prim->close_primitive();
182 if (x == _block_size - level && rjunction) {
183 if (y > level && y < _block_size) {
184 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level, rnlevel, reallevel), 0), lowblocksize - 1));
185 prim->add_vertex(vcounter);
186 prim->add_vertex(vcounter - 1);
187 prim->close_primitive();
189 if (f_part((y / level) / PN_stdfloat(pow(2.0,
int(rnlevel - reallevel)))) == 0.5) {
190 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level - 1, rnlevel, reallevel), 0), lowblocksize - 1));
191 prim->add_vertex(lowblocksize * (lowblocksize - 1) + min(max(sfav(y / level + 1, rnlevel, reallevel), 0), lowblocksize - 1));
192 prim->add_vertex(vcounter);
193 prim->close_primitive();
197 if (y == level && bjunction) {
198 if (x > level && x < _block_size) {
199 prim->add_vertex(vcounter);
200 prim->add_vertex(vcounter - lowblocksize);
201 prim->add_vertex(min(max(sfav(x / level, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
202 prim->close_primitive();
204 if (f_part((x / level) / PN_stdfloat(pow(2.0,
int(bnlevel - reallevel)))) == 0.5) {
205 prim->add_vertex(min(max(sfav(x / level - 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
206 prim->add_vertex(min(max(sfav(x / level + 1, bnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize);
207 prim->add_vertex(vcounter);
208 prim->close_primitive();
211 (!(ljunction && x == level && y > level && y < _block_size) &&
212 !(tjunction && y == _block_size) &&
213 !(rjunction && x == _block_size && y > level && y < _block_size))) {
214 if ((x <= center && y <= center) || (x > center && y > center)) {
216 prim->add_vertex(vcounter);
217 prim->add_vertex(vcounter - lowblocksize);
218 prim->add_vertex(vcounter - lowblocksize - 1);
220 prim->add_vertex(vcounter - lowblocksize - 1);
221 prim->add_vertex(vcounter - 1);
222 prim->add_vertex(vcounter);
226 prim->add_vertex(vcounter);
227 prim->add_vertex(vcounter - lowblocksize);
228 prim->add_vertex(vcounter - 1);
230 prim->add_vertex(vcounter - 1);
231 prim->add_vertex(vcounter - lowblocksize);
232 prim->add_vertex(vcounter - lowblocksize - 1);
235 prim->close_primitive();
238 if (y == _block_size - level && tjunction) {
239 if (x > level && x < _block_size) {
240 prim->add_vertex(min(max(sfav(x / level, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
241 prim->add_vertex(vcounter - lowblocksize);
242 prim->add_vertex(vcounter);
243 prim->close_primitive();
245 if (f_part((x / level) / PN_stdfloat(pow(2.0,
int(tnlevel - reallevel)))) == 0.5) {
246 prim->add_vertex(min(max(sfav(x / level + 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
247 prim->add_vertex(min(max(sfav(x / level - 1, tnlevel, reallevel), 0), lowblocksize - 1) * lowblocksize + lowblocksize - 1);
248 prim->add_vertex(vcounter);
249 prim->close_primitive();
259 geom->add_primitive(prim);
260 geom->set_bounds_type(BoundingVolume::BT_box);
262 std::ostringstream sname;
263 sname <<
"gmm" << mx <<
"x" << my;
266 node->set_bounds_type(BoundingVolume::BT_box);
267 _old_levels.at(mx).at(my) = reallevel;
283 y = (_ysize - 1) - y;
284 if (x < 0.0) x = 0.0;
285 if (y < 0.0) y = 0.0;
286 unsigned int xlo = (
unsigned int) x;
287 unsigned int ylo = (
unsigned int) y;
288 if (xlo > _xsize - 2)
290 if (ylo > _ysize - 2)
292 unsigned int xhi = xlo + 1;
293 unsigned int yhi = ylo + 1;
294 double xoffs = x - xlo;
295 double yoffs = y - ylo;
296 double grayxlyl = get_pixel_value(xlo, ylo);
297 double grayxhyl = get_pixel_value(xhi, ylo);
298 double grayxlyh = get_pixel_value(xlo, yhi);
299 double grayxhyh = get_pixel_value(xhi, yhi);
300 double lerpyl = grayxhyl * xoffs + grayxlyl * (1.0 - xoffs);
301 double lerpyh = grayxhyh * xoffs + grayxlyh * (1.0 - xoffs);
302 return lerpyh * yoffs + lerpyl * (1.0 - yoffs);
324 if (px >=
int(_xsize)) px--;
325 if (py >=
int(_ysize)) py--;
326 double drx = get_pixel_value(nx, y) - get_pixel_value(px, y);
327 double dry = get_pixel_value(x, py) - get_pixel_value(x, ny);
328 LVector3 normal(drx * 0.5, dry * 0.5, 1);
331 if (geomipterrain_incorrect_normals) {
332 normal[0] = -normal[0];
351 for (
unsigned int x = 0; x < _xsize; ++x) {
352 for (
unsigned int y = 0; y < _ysize; ++y) {
354 normal.set(normal.get_x() / _root.get_sx(),
355 normal.get_y() / _root.get_sy(),
356 normal.get_z() / _root.get_sz());
358 result.
set_xel(x, y, normal.angle_deg(LVector3::up()) / 90.0);
372 _color_map =
PNMImage(_xsize, _ysize);
376 for (
unsigned int x = 0; x < _xsize; ++x) {
377 for (
unsigned int y = 0; y < _ysize; ++y) {
378 _color_map.
set_xel(x, _ysize - y - 1, get_pixel_value(x, y));
386 for (
unsigned int x = 0; x < _xsize; ++x) {
387 for (
unsigned int y = 0; y < _ysize; ++y) {
388 _color_map.
set_xel(x, y, (get_pixel_value(x, _ysize - y - 1) - _color_map.
get_gray(x, y)) * contrast + brightness);
392 _has_color_map =
true;
402 if (_xsize < 3 || _ysize < 3) {
403 grutil_cat.error() <<
"No valid heightfield image has been set!\n";
410 _old_levels.resize(
int((_xsize - 1) / _block_size));
411 _root_flattened =
false;
412 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
413 _old_levels[mx].resize(
int((_ysize - 1) / _block_size));
415 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
419 tvector.push_back(_root.
attach_new_node(generate_block(mx, my, _levels[mx][my])));
421 tvector[my].set_pos((mx + 0.5) * _block_size, (my + 0.5) * _block_size, 0);
423 _blocks.push_back(tvector);
440 if (_xsize < 3 || _ysize < 3) {
441 grutil_cat.error() <<
"No valid heightfield image has been set!\n";
447 }
else if (!_bruteforce) {
449 if (root_flattened()) {
451 unsigned int xsize = _blocks.size();
452 for (
unsigned int tx = 0; tx < xsize; tx++) {
453 unsigned int ysize = _blocks[tx].size();
454 for (
unsigned int ty = 0;ty < ysize; ty++) {
455 _blocks[tx][ty].reparent_to(_root);
458 _root_flattened =
false;
460 bool returnVal =
false;
461 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
462 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
463 bool isUpd (update_block(mx, my));
464 if (isUpd && mx > 0 && _old_levels[mx - 1][my] == _levels[mx - 1][my]) {
465 if (update_block(mx - 1, my, -1,
true)) {
469 if (isUpd && mx < (_ysize - 1)/_block_size - 1
470 && _old_levels[mx + 1][my] == _levels[mx + 1][my]) {
471 if (update_block(mx + 1, my, -1,
true)) {
475 if (isUpd && my > 0 && _old_levels[mx][my - 1] == _levels[mx][my - 1]) {
476 if (update_block(mx, my - 1, -1,
true)) {
480 if (isUpd && my < (_ysize - 1)/_block_size - 1
481 && _old_levels[mx][my + 1] == _levels[mx][my + 1]) {
482 if (update_block(mx, my + 1, -1,
true)) {
505 if (_root_flattened) {
515 unsigned int xsize = _blocks.size();
516 for (
unsigned int tx = 0; tx < xsize; tx++) {
517 unsigned int ysize = _blocks[tx].size();
518 for (
unsigned int ty = 0;ty < ysize; ty++) {
519 if (_blocks[tx][ty].get_node(1) != _root.
node()) {
520 grutil_cat.error() <<
"GeoMipTerrain: root node unexpectedly mangled!\n";
527 grutil_cat.error() <<
"GeoMipTerrain: root node unexpectedly mangled: " << total <<
" vs " << (_root.
node()->
get_num_children()) <<
"\n";
540 if (_auto_flatten == AFM_off) {
549 np.node()->copy_children(_root.
node());
552 switch(_auto_flatten) {
558 _root_flattened =
true;
567 nassertv(_xsize >= 3 && _ysize >= 3);
569 for (
unsigned int mx = 0; mx < (_xsize - 1) / _block_size; mx++) {
571 for (
unsigned int my = 0; my < (_ysize - 1) / _block_size; my++) {
573 tvector.push_back(0);
575 tvector.push_back(min(
short(max(_min_level, lod_decide(mx, my))),
579 _levels.push_back(tvector);
596 update_block(
unsigned short mx,
unsigned short my,
597 signed short level,
bool forced) {
598 nassertr_always(!_is_dirty,
false);
599 nassertr_always(mx < (_xsize - 1) / _block_size,
false);
600 nassertr_always(my < (_ysize - 1) / _block_size,
false);
602 level = _levels[mx][my];
604 level = min(max(_min_level, (
unsigned short) level), _max_level);
605 if (forced || _old_levels[mx][my] != level) {
607 generate_block(mx, my, level)->replace_node(_blocks[mx][my].node());
626 if (!is_power_of_two(imgheader.
get_x_size() - 1) ||
627 !is_power_of_two(imgheader.
get_y_size() - 1)) {
629 unsigned int reqx, reqy;
630 reqx = max(3, (
int) pow(2.0, ceil(log((
double) max(2, imgheader.
get_x_size() - 1)) / log(2.0))) + 1);
631 reqy = max(3, (
int) pow(2.0, ceil(log((
double) max(2, imgheader.
get_y_size() - 1)) / log(2.0))) + 1);
634 if (reqx != (
unsigned int)imgheader.
get_x_size() ||
635 reqy != (
unsigned int)imgheader.
get_y_size()) {
637 <<
"Rescaling heightfield image " << filename
639 <<
" to " << reqx <<
"x" << reqy <<
" pixels.\n";
647 <<
"Heightfield image is specified to have sRGB color space!\n" 648 "Panda applies gamma correction, which will probably cause " 649 "it to produce incorrect results.\n";
653 if (!_heightfield.
read(filename, ftype)) {
655 grutil_cat.error() <<
"Failed to read heightfield image " << filename <<
"!\n";
664 grutil_cat.error() <<
"Failed to load heightfield image " << filename <<
"!\n";
672 unsigned short GeoMipTerrain::
673 get_neighbor_level(
unsigned short mx,
unsigned short my,
short dmx,
short dmy) {
676 if ((
int)mx + (
int)dmx < 0 || (
int)mx + (
int)dmx >= ((
int)_xsize - 1) / (
int)_block_size ||
677 (
int)my + (
int)dmy < 0 || (
int)my + (
int)dmy >= ((
int)_ysize - 1) / (
int)_block_size) {
678 return (_stitching) ? _max_level : min(max(_min_level, _levels[mx][my]), _max_level);
682 return min(max(_min_level, _levels[mx][my]), _max_level);
686 if (_levels[mx + dmx][my + dmy] > _levels[mx][my]) {
687 return min(max(_min_level, _levels[mx + dmx][my + dmy]), _max_level);
689 return min(max(_min_level, _levels[mx][my]), _max_level);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
int flatten_light()
Analyzes the geometry below this node and reports the number of vertices, triangles,...
bool unclean_set_num_rows(int n)
This method behaves like set_num_rows(), except the new data is not initialized.
void set_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row.
This is a convenience class to specialize ConfigVariable as a boolean type.
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class of a family of classes that represent particular image file types that PNMImag...
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
void generate()
(Re)generates the entire terrain, erasing the current.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_read_size()
Undoes the effect of a previous call to set_read_size().
void set_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row.
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,...
get_num_children
Returns the number of child nodes this node has.
void gaussian_filter(float radius=1.0)
This flavor of gaussian_filter() will apply the filter over the entire image without resizing or copy...
double get_elevation(double x, double y)
Fetches the elevation at (x, y), where the input coordinate is specified in pixels.
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
The name of a file, such as a texture file or an Egg file.
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...
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.
get_name
Returns the name of the referenced node.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int flatten_strong()
The strongest possible flattening.
PandaNode * node() const
Returns the referenced node of the path.
Defines a series of disconnected triangles.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
TypeHandle is the identifier used to differentiate C++ class types.
bool set_heightfield(const Filename &filename, PNMFileType *type=nullptr)
Loads the specified heightmap image file into the heightfield.
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.
PT(GeomNode) GeoMipTerrain
Generates a chunk of terrain based on the level specified.
int flatten_medium()
A more thorough flattening than flatten_light(), this first applies all the transforms,...
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...
A node that holds Geom objects, renderable pieces of geometry.
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
PNMImage make_slope_image()
Returns a new grayscale image containing the slope angles.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.