Panda3D
Loading...
Searching...
No Matches
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 */
19INLINE GeoMipTerrain::
20GeoMipTerrain(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 */
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 */
64color_map() {
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 */
73INLINE void GeoMipTerrain::
74set_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 */
85INLINE 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 */
95INLINE void GeoMipTerrain::
96set_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 */
107INLINE void GeoMipTerrain::
108set_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}
116INLINE void GeoMipTerrain::
117set_focal_point(const LPoint2d &fp) {
118 set_focal_point(fp.get_x(), fp.get_y());
119}
120INLINE void GeoMipTerrain::
121set_focal_point(const LPoint2f &fp) {
122 set_focal_point(double(fp.get_x()), double(fp.get_y()));
123}
124INLINE void GeoMipTerrain::
125set_focal_point(const LPoint3d &fp) {
126 set_focal_point(fp.get_x(), fp.get_y());
127}
128INLINE void GeoMipTerrain::
129set_focal_point(const LPoint3f &fp) {
130 set_focal_point(double(fp.get_x()), double(fp.get_y()));
131}
132INLINE void GeoMipTerrain::
133set_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 */
146get_focal_point() const {
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 */
157get_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 */
168set_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 */
177INLINE 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 */
186INLINE unsigned short GeoMipTerrain::
188 return _max_level;
189}
190
191/**
192 * Gets the block size.
193 */
194INLINE 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 */
204set_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 */
230is_dirty() {
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 */
242set_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 */
252set_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 */
263set_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 */
273set_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 */
281INLINE double GeoMipTerrain::
282get_far() {
283 return _far;
284}
285
286/**
287 * Returns the near LOD distance in the terrain coordinate space
288 */
289INLINE double GeoMipTerrain::
290get_near() {
291 return _near;
292}
293
294/**
295 * Returns the automatic-flatten mode (e.g., off, flatten_light,
296 * flatten_medium, or flatten_strong)
297 */
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 */
311get_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 */
326INLINE LVecBase2 GeoMipTerrain::
327get_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 */
339INLINE unsigned short GeoMipTerrain::
340lod_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 */
373set_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 */
403set_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
412INLINE bool GeoMipTerrain::
413set_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
420INLINE bool GeoMipTerrain::
421set_color_map(const Texture *tex) {
422 tex->store(_color_map);
423 _is_dirty = true;
424 return true;
425}
426
427INLINE bool GeoMipTerrain::
428set_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 */
436has_color_map() const {
437 return _has_color_map;
438}
439
440/**
441 * Clears the color map.
442 */
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 */
459set_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 */
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 */
480INLINE double GeoMipTerrain::
481get_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}
492INLINE double GeoMipTerrain::
493get_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 */
507INLINE LVector3 GeoMipTerrain::
508get_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 */
518INLINE bool GeoMipTerrain::
519is_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 */
526INLINE float GeoMipTerrain::
527f_part(float i) {
528 return i - floor(i);
529}
530INLINE double GeoMipTerrain::
531f_part(double i) {
532 return i - floor(i);
533}
534
535/**
536 * Used to calculate vertex numbers. Only to be used internally.
537 */
538INLINE int GeoMipTerrain::
539sfav(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}
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
PNMImage & color_map()
Returns a reference to the color map (a PNMImage) contained inside GeoMipTerrain.
const NodePath get_block_node_path(unsigned short mx, unsigned short my)
Returns the NodePath of the specified block.
bool is_dirty()
Returns a bool indicating whether the terrain is marked 'dirty', that means the terrain has to be reg...
void set_min_level(unsigned short minlevel)
Sets the minimum level of detail at which blocks may be generated by generate() or update().
bool set_color_map(const Filename &filename, PNMFileType *type=nullptr)
Loads the specified image as color map.
bool set_heightfield(const Filename &filename, PNMFileType *type=nullptr)
Loads the specified heightmap image file into the heightfield.
void set_near(double input_near)
Sets the near LOD distance, at which the terrain will be rendered at highest quality.
void set_bruteforce(bool bf)
Sets a boolean specifying whether the terrain will be rendered bruteforce.
unsigned short get_max_level()
Returns the highest level possible for this block size.
void set_auto_flatten(int mode)
The terrain can be automatically flattened (using flatten_light, flatten_medium, or flatten_strong) a...
unsigned short get_block_size()
Gets the block size.
double get_far()
Returns the far LOD distance in the terrain coordinate space.
LVector3 get_normal(int x, int y)
Fetches the terrain normal at (x, y), where the input coordinate is specified in pixels.
void set_near_far(double input_near, double input_far)
Sets the near and far LOD distances in one call.
~GeoMipTerrain()
This will not remove the terrain node itself.
void set_block_size(unsigned short newbs)
Sets the block size.
void clear_color_map()
Clears the color map.
void set_far(double input_far)
Sets the far LOD distance, at which the terrain will be rendered at lowest quality.
unsigned short get_min_level()
Gets the minimum level of detail at which blocks may be generated by generate() or update().
void set_border_stitching(bool stitching)
If this value is true, the LOD level at the borders of the terrain will be 0.
NodePath get_root() const
Returns the root of the terrain.
void set_factor(PN_stdfloat factor)
DEPRECATED method.
PNMImage & heightfield()
Returns a reference to the heightfield (a PNMImage) contained inside GeoMipTerrain.
int get_flatten_mode()
Returns the automatic-flatten mode (e.g., off, flatten_light, flatten_medium, or flatten_strong)
bool get_bruteforce()
Returns a boolean whether the terrain is rendered bruteforce or not.
bool get_border_stitching()
Returns the current stitching setting.
double get_near()
Returns the near LOD distance in the terrain coordinate space.
NodePath get_focal_point() const
Returns the focal point, as a NodePath.
bool has_color_map() const
Returns whether a color map has been set.
LVecBase2 get_block_from_pos(double x, double y)
Gets the coordinates of the block at the specified position.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition nodePath.cxx:627
static NodePath fail()
Creates a NodePath with the ET_fail error type set.
Definition nodePath.I:149
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition pnmFileType.h:32
int get_x_size() const
Returns the number of pixels in the X direction.
static bool is_grayscale(ColorType color_type)
This static variant of is_grayscale() returns true if the indicated image type represents a grayscale...
int get_y_size() const
Returns the number of pixels in the Y direction.
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
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition pnmImage.cxx:48
float get_blue(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition pnmImage.I:788
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:869
void copy_from(const PNMImage &copy)
Makes this image become a copy of the other image.
Definition pnmImage.cxx:105
float get_green(int x, int y) const
Returns the green component color at the indicated pixel.
Definition pnmImage.I:779
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition pnmImage.cxx:278
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition pnmImage.I:332
float get_red(int x, int y) const
Returns the red component color at the indicated pixel.
Definition pnmImage.I:770
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
bool store(PNMImage &pnmimage) const
Saves the texture to the indicated PNMImage, but does not write it to disk.
Definition texture.I:433
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.