15 #include "stBasicTerrain.h" 16 #include "geomVertexWriter.h" 25 static const SVertexAttribDesc st_attrib_end = VERTEX_ATTRIB_END();
63 _height_scale(copy._height_scale)
89 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
90 (
new GeomVertexArrayFormat(InternalName::get_vertex(), 3,
91 GeomEnums::NT_stdfloat, GeomEnums::C_point,
92 InternalName::get_texcoord(), 3,
93 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord));
94 set_vertex_format(format);
116 Filename fullpath = Filename::text_filename(terrain_filename);
117 vfs->resolve_filename(fullpath, get_model_path());
119 if (!vfs->exists(fullpath)) {
120 speedtree_cat.warning()
121 <<
"Couldn't find " << terrain_filename <<
"\n";
125 if (vfs->is_directory(fullpath)) {
126 fullpath =
Filename(fullpath,
"terrain.txt");
129 istream *in = vfs->open_read_file(fullpath,
true);
131 speedtree_cat.warning()
132 <<
"Couldn't open " << terrain_filename <<
"\n";
137 vfs->close_read_file(in);
160 while (in && !in.eof()) {
161 if (keyword ==
"area") {
166 _size = csqrt(area) * speedtree_area_scale;
168 }
else if (keyword ==
"height_scale") {
171 }
else if (keyword ==
"normalmap_b_scale") {
172 PN_stdfloat normalmap_b_scale;
173 in >> normalmap_b_scale;
175 }
else if (keyword ==
"heightmap") {
176 read_quoted_filename(_height_map, in, dirname);
178 }
else if (keyword ==
"texture") {
180 read_quoted_filename(splat._filename, in, dirname);
182 splat._color.set(1.0f, 1.0f, 1.0f, 1.0f);
183 _splat_layers.push_back(splat);
185 }
else if (keyword ==
"color") {
189 if (!_splat_layers.empty()) {
190 _splat_layers.back()._color.set(r, g, b, 1.0f);
193 }
else if (keyword ==
"ambient" || keyword ==
"diffuse" || keyword ==
"specular" || keyword ==
"emissive") {
197 }
else if (keyword ==
"shininess") {
202 speedtree_cat.error()
203 <<
"Invalid token " << keyword <<
" in " << pathname <<
"\n";
219 speedtree_cat.error()
220 <<
"Unexpected text in " << pathname <<
" at \"" << text <<
"\"\n";
226 if (!_splat_layers.empty()) {
227 _normal_map = _splat_layers[0]._filename;
228 _splat_layers.erase(_splat_layers.begin());
230 if (!_splat_layers.empty()) {
231 _splat_map = _splat_layers[0]._filename;
232 _splat_layers.erase(_splat_layers.begin());
256 if (!read_height_map()) {
273 return _height_data.calc_bilinear_interpolation(x / _size, y / _size);
286 return _height_data.calc_smooth(x / _size, y / _size, radius / _size);
300 return _slope_data.calc_bilinear_interpolation(x / _size, y / _size);
322 PN_stdfloat start_x, PN_stdfloat start_y,
323 PN_stdfloat size_xy,
int num_xy)
const {
324 nassertv(data->
get_format() == _vertex_format);
328 PN_stdfloat vertex_scale = 1.0 / (PN_stdfloat)(num_xy - 1);
329 PN_stdfloat texcoord_scale = 1.0 / _size;
330 for (
int xi = 0; xi < num_xy; ++xi) {
331 PN_stdfloat xt = xi * vertex_scale;
332 PN_stdfloat x = start_x + xt * size_xy;
333 for (
int yi = 0; yi < num_xy; ++yi) {
334 PN_stdfloat yt = yi * vertex_scale;
335 PN_stdfloat y = start_y + yt * size_xy;
339 vertex.set_data3(x, y, z);
340 texcoord.
set_data3(x * texcoord_scale, -y * texcoord_scale, 1.0f);
350 void STBasicTerrain::
351 output(ostream &out)
const {
360 void STBasicTerrain::
361 write(ostream &out,
int indent_level)
const {
362 indent(out, indent_level)
373 bool STBasicTerrain::
381 _min_height = FLT_MAX;
382 _max_height = FLT_MIN;
386 for (
int yi = image.
get_y_size() - 1; yi >= 0; --yi) {
387 for (
int xi = 0; xi < image.
get_x_size(); ++xi) {
389 PN_stdfloat v = rgba[0] + rgba[1] + rgba[2] + rgba[3];
391 _height_data._data[pi] = v;
393 _min_height = min(_min_height, v);
394 _max_height = max(_max_height, v);
409 void STBasicTerrain::
410 compute_slope(PN_stdfloat smoothing) {
411 nassertv(!_height_data._data.empty());
413 int width = _height_data._width;
414 int height = _height_data._height;
415 _slope_data.reset(width, height);
417 PN_stdfloat u_spacing = _size / (PN_stdfloat)width;
418 PN_stdfloat v_spacing = _size / (PN_stdfloat)height;
420 for (
int i = 0; i < width; ++i) {
421 int left = (i + width - 1) % width;
422 int right = (i + 1) % width;
424 for (
int j = 0; j < height; ++j) {
425 int top = (j + height - 1) % height;
426 int bottom = (j + 1) % height;
428 PN_stdfloat slope = 0.0f;
429 PN_stdfloat this_height = _height_data._data[i + j * width];
430 slope += catan2(cabs(this_height - _height_data._data[right + j * width]), u_spacing);
431 slope += catan2(cabs(this_height - _height_data._data[left + j * width]), u_spacing);
432 slope += catan2(cabs(this_height - _height_data._data[i + top * width]), v_spacing);
433 slope += catan2(cabs(this_height - _height_data._data[i + bottom * width]), v_spacing);
435 slope *= (0.5f / MathNumbers::pi_f);
440 _slope_data._data[i + j * width] = slope;
444 if (smoothing > 0.0f) {
446 InterpolationData<PN_stdfloat> smoothing_data;
447 smoothing_data.reset(width, height);
448 PN_stdfloat *smoothed = &smoothing_data._data[0];
450 int steps = int(smoothing);
451 PN_stdfloat last_interpolation = smoothing - steps;
453 for (
int si = 0; si < steps; ++si) {
456 for (
int i = 0; i < width; ++i) {
457 int left = (i + width - 1) % width;
458 int right = (i + 1) % width;
460 for (
int j = 0; j < height; ++j) {
461 int top = (j + height - 1) % height;
462 int bottom = (j + 1) % height;
464 smoothed[i + j * width] = (_slope_data._data[right + j * width] +
465 _slope_data._data[left + j * width] +
466 _slope_data._data[i + top * width] +
467 _slope_data._data[i + bottom * width] +
468 _slope_data._data[right + top * width] +
469 _slope_data._data[right + bottom * width] +
470 _slope_data._data[left + top * width] +
471 _slope_data._data[left + bottom * width]);
472 smoothed[i + j * width] *= 0.125f;
477 if (si == steps - 1) {
479 for (
int i = 0; i < width; ++i) {
480 for (
int j = 0; j < height; ++j) {
481 _slope_data._data[i + j * width] = interpolate(_slope_data._data[i + j * width], smoothed[i + j * width], last_interpolation);
487 _slope_data = smoothing_data;
500 void STBasicTerrain::
501 read_quoted_filename(
Filename &result, istream &in,
const Filename &dirname) {
508 if (filename.size() >= 2 && filename[0] ==
'"' && filename[filename.size() - 1] ==
'"') {
509 filename = filename.substr(1, filename.size() - 2);
string get_dirname() const
Returns the directory part of the filename.
string get_basename() const
Returns the basename part of the filename.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A specific implementation of STTerrain that supports basic heightmaps loaded from an image file...
void clear()
Resets the terrain to its initial, unloaded state.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
void output(ostream &out) const
Outputs the Namable.
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. ...
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
virtual PN_stdfloat get_height(PN_stdfloat x, PN_stdfloat y) const
After load_data() has been called, this should return the computed height value at point (x...
This is the base class for all three-component vectors and points.
This is the abstract base class that defines the interface needed to describe a terrain for rendering...
virtual void fill_vertices(GeomVertexData *data, PN_stdfloat start_x, PN_stdfloat start_y, PN_stdfloat size_xy, int num_xy) const
After load_data() has been called, this will be called occasionally to populate the vertices for a te...
virtual void load_data()
This will be called at some point after initialization.
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width...
bool setup_terrain(const Filename &terrain_filename)
Sets up the terrain by reading a terrain.txt file as defined by SpeedTree.
virtual PN_stdfloat get_slope(PN_stdfloat x, PN_stdfloat y) const
After load_data() has been called, this should return the directionless slope at point (x...
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.
const GeomVertexFormat * get_format() const
Returns a pointer to the GeomVertexFormat structure that defines this data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
bool is_local() const
Returns true if the filename is local, e.g.
virtual void clear()
Resets the terrain to its initial, unloaded state.
TypeHandle is the identifier used to differentiate C++ class types.
virtual PN_stdfloat get_smooth_height(PN_stdfloat x, PN_stdfloat y, PN_stdfloat radius) const
After load_data() has been called, this should return the approximate average height value over a cir...
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).