Panda3D
geoMipTerrain.I
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file geoMipTerrain.I
10  * @author rdb
11  * @date 2007-06-29
12  */
13 
14 #include "config_grutil.h"
15 
16 /**
17  *
18  */
19 INLINE GeoMipTerrain::
20 GeoMipTerrain(const std::string &name) {
21  _root = NodePath(name);
22  _root_flattened = false;
23  _xsize = 0;
24  _ysize = 0;
25  _block_size = 16;
26  _max_level = 4; // Always log(_block_size) / log(2.0)
27  _min_level = 0;
28  _factor = 100.0;
29  _near = 16.0;
30  _far = 128.0;
31  _use_near_far = false;
32  _has_color_map = false;
33  PT(PandaNode) tmpnode = new PandaNode("tmp_focal");
34  _auto_flatten = AFM_off;
35  _focal_point = NodePath(tmpnode);
36  _focal_is_temporary = true;
37  _is_dirty = true;
38  _bruteforce = false;
39  _stitching = false;
40 }
41 
42 /**
43  * This will not remove the terrain node itself. To have the terrain itself
44  * also deleted, please call remove_node() prior to destruction.
45  */
46 INLINE GeoMipTerrain::
48 }
49 
50 /**
51  * Returns a reference to the heightfield (a PNMImage) contained inside
52  * GeoMipTerrain. You can use the reference to alter the heightfield.
53  */
56  return _heightfield;
57 }
58 
59 /**
60  * Returns a reference to the color map (a PNMImage) contained inside
61  * GeoMipTerrain. You can use the reference to alter the color map.
62  */
65  return _color_map;
66 }
67 
68 /**
69  * Sets a boolean specifying whether the terrain will be rendered bruteforce.
70  * If the terrain is rendered bruteforce, there will be no Level of Detail,
71  * and the update() call will only update the terrain if it is marked dirty.
72  */
73 INLINE void GeoMipTerrain::
74 set_bruteforce(bool bf) {
75  if (bf == true && _bruteforce == false) {
76  _is_dirty = true;
77  }
78  _bruteforce = bf;
79 }
80 
81 /**
82  * Returns a boolean whether the terrain is rendered bruteforce or not. See
83  * set_bruteforce for more information.
84  */
85 INLINE bool GeoMipTerrain::
87  return _bruteforce;
88 }
89 
90 /**
91  * The terrain can be automatically flattened (using flatten_light,
92  * flatten_medium, or flatten_strong) after each update. This only affects
93  * future updates, it doesn't flatten the current terrain.
94  */
95 INLINE void GeoMipTerrain::
96 set_auto_flatten(int mode) {
97  _auto_flatten = mode;
98 }
99 
100 /**
101  * Sets the focal point. GeoMipTerrain generates high-resolution terrain
102  * around the focal point, and progressively lower and lower resolution
103  * terrain as you get farther away. If a point is supplied and not a
104  * NodePath, make sure it's relative to the terrain. Only the x and y
105  * coordinates of the focal point are taken in respect.
106  */
107 INLINE void GeoMipTerrain::
108 set_focal_point(double x, double y) {
109  if (!_focal_is_temporary) {
110  PT(PandaNode) tmpnode = new PandaNode("tmp_focal");
111  _focal_point = NodePath(tmpnode);
112  }
113  _focal_point.set_pos(_root, x, y, 0);
114  _focal_is_temporary = true;
115 }
116 INLINE void GeoMipTerrain::
117 set_focal_point(const LPoint2d &fp) {
118  set_focal_point(fp.get_x(), fp.get_y());
119 }
120 INLINE void GeoMipTerrain::
121 set_focal_point(const LPoint2f &fp) {
122  set_focal_point(double(fp.get_x()), double(fp.get_y()));
123 }
124 INLINE void GeoMipTerrain::
125 set_focal_point(const LPoint3d &fp) {
126  set_focal_point(fp.get_x(), fp.get_y());
127 }
128 INLINE void GeoMipTerrain::
129 set_focal_point(const LPoint3f &fp) {
130  set_focal_point(double(fp.get_x()), double(fp.get_y()));
131 }
132 INLINE void GeoMipTerrain::
133 set_focal_point(NodePath fp) {
134  if (_focal_is_temporary) {
135  _focal_point.remove_node();
136  }
137  _focal_point = fp;
138  _focal_is_temporary = false;
139 }
140 
141 /**
142  * Returns the focal point, as a NodePath. If you have set it to be just a
143  * point, it will return an empty node at the focal position.
144  */
147  return _focal_point;
148 }
149 
150 /**
151  * Returns the root of the terrain. This is a single PandaNode to which all
152  * the rest of the terrain is parented. The generate and update operations
153  * replace the nodes which are parented to this root, but they don't replace
154  * this root itself.
155  */
157 get_root() const {
158  return _root;
159 }
160 
161 /**
162  * Sets the minimum level of detail at which blocks may be generated by
163  * generate() or update(). The default value is 0, which is the highest
164  * quality. This value is also taken in respect when generating the terrain
165  * bruteforce.
166  */
167 INLINE void GeoMipTerrain::
168 set_min_level(unsigned short minlevel) {
169  _min_level = minlevel;
170 }
171 
172 /**
173  * Gets the minimum level of detail at which blocks may be generated by
174  * generate() or update(). The default value is 0, which is the highest
175  * quality.
176  */
177 INLINE unsigned short GeoMipTerrain::
179  return _min_level;
180 }
181 
182 /**
183  * Returns the highest level possible for this block size. When a block is at
184  * this level, it will be the worst quality possible.
185  */
186 INLINE unsigned short GeoMipTerrain::
188  return _max_level;
189 }
190 
191 /**
192  * Gets the block size.
193  */
194 INLINE unsigned short GeoMipTerrain::
196  return _block_size;
197 }
198 
199 /**
200  * Sets the block size. If it is not a power of two, the closest power of two
201  * is used.
202  */
203 INLINE void GeoMipTerrain::
204 set_block_size(unsigned short newbs) {
205  if (is_power_of_two(newbs)) {
206  _block_size = newbs;
207  } else {
208  if (is_power_of_two(newbs - 1)) {
209  _block_size = newbs - 1;
210  } else {
211  if (is_power_of_two(newbs + 1)) {
212  _block_size = newbs + 1;
213  } else {
214  _block_size = (unsigned short) pow(2.0,
215  floor(log((double) newbs) / log(2.0) + 0.5));
216  }
217  }
218  }
219  _max_level = (unsigned short) (log((double) _block_size) / log(2.0));
220  _is_dirty = true;
221 }
222 
223 /**
224  * Returns a bool indicating whether the terrain is marked 'dirty', that means
225  * the terrain has to be regenerated on the next update() call, because for
226  * instance the heightfield has changed. Once the terrain has been
227  * regenerated, the dirty flag automatically gets reset internally.
228  */
229 INLINE bool GeoMipTerrain::
231  return _is_dirty;
232 }
233 
234 /**
235  * DEPRECATED method. Use set_near/far instead. Sets the quality factor at
236  * which blocks must be generated. The higher this level, the better quality
237  * the terrain will be, but more expensive to render. A value of 0 makes the
238  * terrain the lowest quality possible, depending on blocksize. The default
239  * value is 100.
240  */
241 INLINE void GeoMipTerrain::
242 set_factor(PN_stdfloat factor) {
243  grutil_cat.debug() << "Using deprecated method set_factor, use set_near and set_far instead!\n";
244  _use_near_far = false;
245  _factor = factor;
246 }
247 
248 /**
249  * Sets the near and far LOD distances in one call.
250  */
251 INLINE void GeoMipTerrain::
252 set_near_far(double input_near, double input_far) {
253  _use_near_far = true;
254  _near = input_near;
255  _far = input_far;
256 }
257 
258 /**
259  * Sets the near LOD distance, at which the terrain will be rendered at
260  * highest quality. This distance is in the terrain's coordinate space!
261  */
262 INLINE void GeoMipTerrain::
263 set_near(double input_near) {
264  _use_near_far = true;
265  _near = input_near;
266 }
267 
268 /**
269  * Sets the far LOD distance, at which the terrain will be rendered at lowest
270  * quality. This distance is in the terrain's coordinate space!
271  */
272 INLINE void GeoMipTerrain::
273 set_far(double input_far) {
274  _use_near_far = true;
275  _far = input_far;
276 }
277 
278 /**
279  * Returns the far LOD distance in the terrain coordinate space
280  */
281 INLINE double GeoMipTerrain::
283  return _far;
284 }
285 
286 /**
287  * Returns the near LOD distance in the terrain coordinate space
288  */
289 INLINE double GeoMipTerrain::
291  return _near;
292 }
293 
294 /**
295  * Returns the automatic-flatten mode (e.g., off, flatten_light,
296  * flatten_medium, or flatten_strong)
297  */
298 INLINE int GeoMipTerrain::
300  return _auto_flatten;
301 }
302 
303 /**
304  * Returns the NodePath of the specified block. If auto-flatten is enabled
305  * and the node is getting removed during the flattening process, it will
306  * still return a NodePath with the appropriate terrain chunk, but it will be
307  * in a temporary scenegraph. Please note that this returns a const object
308  * and you can not modify the node. Modify the heightfield instead.
309  */
310 INLINE const NodePath GeoMipTerrain::
311 get_block_node_path(unsigned short mx, unsigned short my) {
312  nassertr(mx < _blocks.size(), NodePath::fail());
313  nassertr(my < _blocks[mx].size(), NodePath::fail());
314  return _blocks[mx][my];
315 }
316 
317 /**
318  * Gets the coordinates of the block at the specified position. This position
319  * must be relative to the terrain, not to render. Returns an array
320  * containing two values: the block x and the block y coords. If the
321  * positions are out of range, the closest block is taken. Note that the
322  * VecBase returned does not represent a vector, position, or rotation, but it
323  * contains the block index of the block which you can use in
324  * GeoMipTerrain::get_block_node_path.
325  */
326 INLINE LVecBase2 GeoMipTerrain::
327 get_block_from_pos(double x, double y) {
328  if (x < 0) x = 0;
329  if (y < 0) y = 0;
330  if (x > _xsize - 1) x = _xsize - 1;
331  if (y > _ysize - 1) y = _ysize - 1;
332  x = floor(x / _block_size);
333  y = floor(y / _block_size);
334  return LVecBase2(x, y);
335 }
336 /**
337  * Calculates the level for the given mipmap.
338  */
339 INLINE unsigned short GeoMipTerrain::
340 lod_decide(unsigned short mx, unsigned short my) {
341  PN_stdfloat cx = mx;
342  PN_stdfloat cy = my;
343  cx = (cx * _block_size + _block_size / 2) * _root.get_sx();
344  cy = (cy * _block_size + _block_size / 2) * _root.get_sy();
345  PN_stdfloat d;
346  if (_use_near_far) {
347  d = sqrt(pow(_focal_point.get_x(_root) - cx, 2) +
348  pow(_focal_point.get_y(_root) - cy, 2));
349  if (d < _near) {
350  return 0;
351  } else if (d > _far) {
352  return _max_level;
353  } else {
354  return (unsigned short)((d - _near) / (_far - _near) * _max_level * (1.0 - (_min_level / _max_level)) + _min_level);
355  }
356  } else {
357  if (_factor > 0.0) {
358  d = sqrt(pow(_focal_point.get_x(_root) - cx, 2) +
359  pow(_focal_point.get_y(_root) - cy, 2)) / _factor;
360  } else {
361  d = _max_level;
362  }
363  return short(floor(d));
364  }
365 }
366 
367 /**
368  * Loads the specified heightmap image file into the heightfield. Returns
369  * true if succeeded, or false if an error has occured. If the heightmap is
370  * not a power of two plus one, it is scaled up using a gaussian filter.
371  */
372 INLINE bool GeoMipTerrain::
373 set_heightfield(const PNMImage &image) {
374  if (image.get_color_space() == CS_sRGB) {
375  // Probably a mistaken metadata setting on the file.
376  grutil_cat.warning()
377  << "Heightfield image is specified to have sRGB color space!\n"
378  "Panda applies gamma correction, which will probably cause "
379  "it to produce incorrect results.\n";
380  }
381 
382  // Before we apply anything, validate the size.
383  if (is_power_of_two(image.get_x_size() - 1) &&
384  is_power_of_two(image.get_y_size() - 1)) {
385  _heightfield = image;
386  _is_dirty = true;
387  _xsize = _heightfield.get_x_size();
388  _ysize = _heightfield.get_y_size();
389  return true;
390  } else {
391  grutil_cat.error()
392  << "Specified image does not have a power-of-two-plus-one size!\n";
393  }
394  return false;
395 }
396 
397 /**
398  * Loads the specified image as color map. The next time generate() is
399  * called, the terrain is painted with this color map using the vertex color
400  * column. Returns a boolean indicating whether the operation has succeeded.
401  */
402 INLINE bool GeoMipTerrain::
403 set_color_map(const Filename &filename, PNMFileType *ftype) {
404  if (_color_map.read(filename, ftype)) {
405  _is_dirty = true;
406  _has_color_map = true;
407  return true;
408  }
409  return false;
410 }
411 
412 INLINE bool GeoMipTerrain::
413 set_color_map(const PNMImage &image) {
414  _color_map.copy_from(image);
415  _is_dirty = true;
416  _has_color_map = true;
417  return true;
418 }
419 
420 INLINE bool GeoMipTerrain::
421 set_color_map(const Texture *tex) {
422  tex->store(_color_map);
423  _is_dirty = true;
424  return true;
425 }
426 
427 INLINE bool GeoMipTerrain::
428 set_color_map(const std::string &path) {
429  return set_color_map(Filename(path));
430 }
431 
432 /**
433  * Returns whether a color map has been set.
434  */
435 INLINE bool GeoMipTerrain::
436 has_color_map() const {
437  return _has_color_map;
438 }
439 
440 /**
441  * Clears the color map.
442  */
443 INLINE void GeoMipTerrain::
445  if (_has_color_map) {
446  _color_map.clear();
447  _has_color_map = false;
448  }
449 }
450 
451 /**
452  * If this value is true, the LOD level at the borders of the terrain will be
453  * 0. This is useful if you have multiple terrains attached and you want to
454  * stitch them together, to fix seams. This setting also has effect when
455  * bruteforce is enabled, although in that case you are probably better off
456  * with setting the minlevels to the same value.
457  */
458 INLINE void GeoMipTerrain::
459 set_border_stitching(bool stitching) {
460  if (stitching && !_stitching) {
461  _is_dirty = true;
462  }
463  _stitching = stitching;
464 }
465 
466 /**
467  * Returns the current stitching setting. False by default, unless
468  * set_stitching has been set.
469  */
470 INLINE bool GeoMipTerrain::
472  return _stitching;
473 }
474 
475 /**
476  * Get the elevation at a certain pixel of the image. This function does NOT
477  * linearly interpolate. For that, use GeoMipTerrain::get_elevation()
478  * instead.
479  */
480 INLINE double GeoMipTerrain::
481 get_pixel_value(int x, int y) {
482  x = std::max(std::min(x,int(_xsize-1)),0);
483  y = std::max(std::min(y,int(_ysize-1)),0);
484  if (_heightfield.is_grayscale()) {
485  return double(_heightfield.get_bright(x, y));
486  } else {
487  return double(_heightfield.get_red(x, y))
488  + double(_heightfield.get_green(x, y)) / 256.0
489  + double(_heightfield.get_blue(x, y)) / 65536.0;
490  }
491 }
492 INLINE double GeoMipTerrain::
493 get_pixel_value(unsigned short mx, unsigned short my, int x, int y) {
494  nassertr_always(mx < (_xsize - 1) / _block_size, false);
495  nassertr_always(my < (_ysize - 1) / _block_size, false);
496  return get_pixel_value(mx * _block_size + x, (_ysize - 1) -
497  (my * _block_size + y));
498 }
499 
500 /**
501  * Fetches the terrain normal at (x,y), where the input coordinate is
502  * specified in pixels. This ignores the current LOD level and instead
503  * provides an accurate number. Terrain scale is NOT taken into account! To
504  * get accurate normals, please divide it by the terrain scale and normalize
505  * it again!
506  */
507 INLINE LVector3 GeoMipTerrain::
508 get_normal(unsigned short mx, unsigned short my, int x, int y) {
509  nassertr_always(mx < (_xsize - 1) / _block_size, false);
510  nassertr_always(my < (_ysize - 1) / _block_size, false);
511  return get_normal(mx * _block_size + x, (_ysize - 1) -
512  (my * _block_size + y));
513 }
514 
515 /**
516  * Returns a bool whether the given int i is a power of two or not.
517  */
518 INLINE bool GeoMipTerrain::
519 is_power_of_two(unsigned int i) {
520  return !((i - 1) & i);
521 }
522 
523 /**
524  * Returns the part of the number right of the floating-point.
525  */
526 INLINE float GeoMipTerrain::
527 f_part(float i) {
528  return i - floor(i);
529 }
530 INLINE double GeoMipTerrain::
531 f_part(double i) {
532  return i - floor(i);
533 }
534 
535 /**
536  * Used to calculate vertex numbers. Only to be used internally.
537  */
538 INLINE int GeoMipTerrain::
539 sfav(int n, int powlevel, int mypowlevel) {
540  double t = n - 1;
541  t /= pow(2.0, powlevel - mypowlevel);
542  t = double(int(t > 0.0 ? t + 0.5 : t - 0.5));
543  t *= pow(2.0, powlevel - mypowlevel);
544  return int(t);
545 }
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
bool is_dirty()
Returns a bool indicating whether the terrain is marked 'dirty', that means the terrain has to be reg...
void set_factor(PN_stdfloat factor)
DEPRECATED method.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition: pnmImage.I:243
bool set_color_map(const Filename &filename, PNMFileType *type=nullptr)
Loads the specified image as color map.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
float get_blue(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:774
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
const NodePath get_block_node_path(unsigned short mx, unsigned short my)
Returns the NodePath of the specified block.
PNMImage & heightfield()
Returns a reference to the heightfield (a PNMImage) contained inside GeoMipTerrain.
Definition: geoMipTerrain.I:55
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
unsigned short get_min_level()
Gets the minimum level of detail at which blocks may be generated by generate() or update().
double get_far()
Returns the far LOD distance in the terrain coordinate space.
bool store(PNMImage &pnmimage) const
Saves the texture to the indicated PNMImage, but does not write it to disk.
Definition: texture.I:432
void set_bruteforce(bool bf)
Sets a boolean specifying whether the terrain will be rendered bruteforce.
Definition: geoMipTerrain.I:74
PNMImage & color_map()
Returns a reference to the color map (a PNMImage) contained inside GeoMipTerrain.
Definition: geoMipTerrain.I:64
~GeoMipTerrain()
This will not remove the terrain node itself.
Definition: geoMipTerrain.I:47
unsigned short get_block_size()
Gets the block size.
int get_y_size() const
Returns the number of pixels in the Y direction.
int get_x_size() const
Returns the number of pixels in the X direction.
float get_red(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:756
double get_near()
Returns the near LOD distance in the terrain coordinate space.
void set_near_far(double input_near, double input_far)
Sets the near and far LOD distances in one call.
bool has_color_map() const
Returns whether a color map has been set.
static NodePath fail()
Creates a NodePath with the ET_fail error type set.
Definition: nodePath.I:149
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
float get_green(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:765
bool get_bruteforce()
Returns a boolean whether the terrain is rendered bruteforce or not.
Definition: geoMipTerrain.I:86
void clear_color_map()
Clears the color map.
unsigned short get_max_level()
Returns the highest level possible for this block size.
LVector3 get_normal(int x, int y)
Fetches the terrain normal at (x, y), where the input coordinate is specified in pixels.
bool get_border_stitching()
Returns the current stitching setting.
void set_border_stitching(bool stitching)
If this value is true, the LOD level at the borders of the terrain will be 0.
int get_flatten_mode()
Returns the automatic-flatten mode (e.g., off, flatten_light, flatten_medium, or flatten_strong)
void set_near(double input_near)
Sets the near LOD distance, at which the terrain will be rendered at highest quality.
void set_auto_flatten(int mode)
The terrain can be automatically flattened (using flatten_light, flatten_medium, or flatten_strong) a...
Definition: geoMipTerrain.I:96
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:591
void set_block_size(unsigned short newbs)
Sets the block size.
void set_far(double input_far)
Sets the far LOD distance, at which the terrain will be rendered at lowest quality.
static bool is_grayscale(ColorType color_type)
This static variant of is_grayscale() returns true if the indicated image type represents a grayscale...
void copy_from(const PNMImage &copy)
Makes this image become a copy of the other image.
Definition: pnmImage.cxx:105
bool set_heightfield(const Filename &filename, PNMFileType *type=nullptr)
Loads the specified heightmap image file into the heightfield.
NodePath get_root() const
Returns the root of the terrain.
float get_bright(int x, int y) const
Returns the linear brightness of the given xel, as a linearized float in the range 0....
Definition: pnmImage.I:855
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
LVecBase2 get_block_from_pos(double x, double y)
Gets the coordinates of the block at the specified position.
void set_min_level(unsigned short minlevel)
Sets the minimum level of detail at which blocks may be generated by generate() or update().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath get_focal_point() const
Returns the focal point, as a NodePath.