28 static const SVertexAttribDesc st_attrib_end = VERTEX_ATTRIB_END();
61 _height_scale(copy._height_scale)
85 GeomEnums::NT_stdfloat, GeomEnums::C_point,
86 InternalName::get_texcoord(), 3,
87 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord));
88 set_vertex_format(format);
106 Filename fullpath = Filename::text_filename(terrain_filename);
109 if (!vfs->
exists(fullpath)) {
110 speedtree_cat.warning()
111 <<
"Couldn't find " << terrain_filename <<
"\n";
116 fullpath =
Filename(fullpath,
"terrain.txt");
121 speedtree_cat.warning()
122 <<
"Couldn't open " << terrain_filename <<
"\n";
147 while (in && !in.eof()) {
148 if (keyword ==
"area") {
153 _size = csqrt(area) * speedtree_area_scale;
155 }
else if (keyword ==
"height_scale") {
158 }
else if (keyword ==
"normalmap_b_scale") {
159 PN_stdfloat normalmap_b_scale;
160 in >> normalmap_b_scale;
162 }
else if (keyword ==
"heightmap") {
163 read_quoted_filename(_height_map, in, dirname);
165 }
else if (keyword ==
"texture") {
167 read_quoted_filename(splat._filename, in, dirname);
169 splat._color.set(1.0f, 1.0f, 1.0f, 1.0f);
170 _splat_layers.push_back(splat);
172 }
else if (keyword ==
"color") {
176 if (!_splat_layers.empty()) {
177 _splat_layers.back()._color.set(r, g, b, 1.0f);
180 }
else if (keyword ==
"ambient" || keyword ==
"diffuse" || keyword ==
"specular" || keyword ==
"emissive") {
184 }
else if (keyword ==
"shininess") {
189 speedtree_cat.error()
190 <<
"Invalid token " << keyword <<
" in " << pathname <<
"\n";
205 speedtree_cat.error()
206 <<
"Unexpected text in " << pathname <<
" at \"" << text <<
"\"\n";
211 if (!_splat_layers.empty()) {
212 _normal_map = _splat_layers[0]._filename;
213 _splat_layers.erase(_splat_layers.begin());
215 if (!_splat_layers.empty()) {
216 _splat_map = _splat_layers[0]._filename;
217 _splat_layers.erase(_splat_layers.begin());
237 if (!read_height_map()) {
250 get_height(PN_stdfloat x, PN_stdfloat y)
const {
251 return _height_data.calc_bilinear_interpolation(x / _size, y / _size);
261 return _height_data.calc_smooth(x / _size, y / _size, radius / _size);
271 get_slope(PN_stdfloat x, PN_stdfloat y)
const {
272 return _slope_data.calc_bilinear_interpolation(x / _size, y / _size);
288 PN_stdfloat start_x, PN_stdfloat start_y,
289 PN_stdfloat size_xy,
int num_xy)
const {
290 nassertv(data->get_format() == _vertex_format);
294 PN_stdfloat vertex_scale = 1.0 / (PN_stdfloat)(num_xy - 1);
295 PN_stdfloat texcoord_scale = 1.0 / _size;
296 for (
int xi = 0; xi < num_xy; ++xi) {
297 PN_stdfloat xt = xi * vertex_scale;
298 PN_stdfloat x = start_x + xt * size_xy;
299 for (
int yi = 0; yi < num_xy; ++yi) {
300 PN_stdfloat yt = yi * vertex_scale;
301 PN_stdfloat y = start_y + yt * size_xy;
306 texcoord.
set_data3(x * texcoord_scale, -y * texcoord_scale, 1.0f);
314 void STBasicTerrain::
315 output(ostream &out)
const {
322 void STBasicTerrain::
323 write(ostream &out,
int indent_level)
const {
332 bool STBasicTerrain::
335 if (!image.is_valid()) {
339 _height_data.reset(image.get_x_size(), image.get_y_size());
340 _min_height = FLT_MAX;
341 _max_height = FLT_MIN;
343 PN_stdfloat scalar = _size * _height_scale / image.get_num_channels();
345 for (
int yi = image.get_y_size() - 1; yi >= 0; --yi) {
346 for (
int xi = 0; xi < image.get_x_size(); ++xi) {
347 LColord rgba = image.get_xel_a(xi, yi);
348 PN_stdfloat v = rgba[0] + rgba[1] + rgba[2] + rgba[3];
350 _height_data._data[pi] = v;
352 _min_height = std::min(_min_height, v);
353 _max_height = std::max(_max_height, v);
366 void STBasicTerrain::
367 compute_slope(PN_stdfloat smoothing) {
368 nassertv(!_height_data._data.empty());
370 int width = _height_data._width;
371 int height = _height_data._height;
372 _slope_data.reset(width, height);
374 PN_stdfloat u_spacing = _size / (PN_stdfloat)width;
375 PN_stdfloat v_spacing = _size / (PN_stdfloat)height;
377 for (
int i = 0; i < width; ++i) {
378 int left = (i + width - 1) % width;
379 int right = (i + 1) % width;
381 for (
int j = 0; j < height; ++j) {
382 int top = (j + height - 1) % height;
383 int bottom = (j + 1) % height;
385 PN_stdfloat slope = 0.0f;
386 PN_stdfloat this_height = _height_data._data[i + j * width];
387 slope += catan2(cabs(this_height - _height_data._data[right + j * width]), u_spacing);
388 slope += catan2(cabs(this_height - _height_data._data[left + j * width]), u_spacing);
389 slope += catan2(cabs(this_height - _height_data._data[i + top * width]), v_spacing);
390 slope += catan2(cabs(this_height - _height_data._data[i + bottom * width]), v_spacing);
392 slope *= (0.5f / MathNumbers::pi_f);
397 _slope_data._data[i + j * width] = slope;
401 if (smoothing > 0.0f) {
403 InterpolationData<PN_stdfloat> smoothing_data;
404 smoothing_data.reset(width, height);
405 PN_stdfloat *smoothed = &smoothing_data._data[0];
407 int steps = int(smoothing);
408 PN_stdfloat last_interpolation = smoothing - steps;
410 for (
int si = 0; si < steps; ++si) {
413 for (
int i = 0; i < width; ++i) {
414 int left = (i + width - 1) % width;
415 int right = (i + 1) % width;
417 for (
int j = 0; j < height; ++j) {
418 int top = (j + height - 1) % height;
419 int bottom = (j + 1) % height;
421 smoothed[i + j * width] = (_slope_data._data[right + j * width] +
422 _slope_data._data[left + j * width] +
423 _slope_data._data[i + top * width] +
424 _slope_data._data[i + bottom * width] +
425 _slope_data._data[right + top * width] +
426 _slope_data._data[right + bottom * width] +
427 _slope_data._data[left + top * width] +
428 _slope_data._data[left + bottom * width]);
429 smoothed[i + j * width] *= 0.125f;
434 if (si == steps - 1) {
436 for (
int i = 0; i < width; ++i) {
437 for (
int j = 0; j < height; ++j) {
438 _slope_data._data[i + j * width] = interpolate(_slope_data._data[i + j * width], smoothed[i + j * width], last_interpolation);
444 _slope_data = smoothing_data;
454 void STBasicTerrain::
455 read_quoted_filename(
Filename &result, istream &in,
const Filename &dirname) {
461 if (filename.size() >= 2 && filename[0] ==
'"' && filename[filename.size() - 1] ==
'"') {
462 filename = filename.substr(1, filename.size() - 2);
The name of a file, such as a texture file or an Egg file.
std::string get_basename() const
Returns the basename part of the filename.
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
bool is_local() const
Returns true if the filename is local, e.g.
std::string get_dirname() const
Returns the directory part of the filename.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
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.
void output(std::ostream &out) const
Outputs the Namable.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
A specific implementation of STTerrain that supports basic heightmaps loaded from an image file,...
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...
bool setup_terrain(const Filename &terrain_filename)
Sets up the terrain by reading a terrain.txt file as defined by SpeedTree.
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 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,...
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,...
void clear()
Resets the terrain to its initial, unloaded state.
virtual void load_data()
This will be called at some point after initialization.
This is the abstract base class that defines the interface needed to describe a terrain for rendering...
virtual void clear()
Resets the terrain to its initial, unloaded state.
TypeHandle is the identifier used to differentiate C++ class types.
A hierarchy of directories and files that appears to be one continuous file system,...
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.