00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "stBasicTerrain.h"
00016 #include "geomVertexWriter.h"
00017 #include "pnmImage.h"
00018 #include "indent.h"
00019
00020 TypeHandle STBasicTerrain::_type_handle;
00021
00022
00023
00024 namespace SpeedTree {
00025 static const SVertexAttribDesc st_attrib_end = VERTEX_ATTRIB_END();
00026 }
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 STBasicTerrain::
00048 STBasicTerrain() {
00049 clear();
00050 }
00051
00052
00053
00054
00055
00056
00057
00058
00059 STBasicTerrain::
00060 STBasicTerrain(const STBasicTerrain ©) :
00061 STTerrain(copy),
00062 _size(copy._size),
00063 _height_scale(copy._height_scale)
00064 {
00065 }
00066
00067
00068
00069
00070
00071
00072 STBasicTerrain::
00073 ~STBasicTerrain() {
00074 }
00075
00076
00077
00078
00079
00080
00081 void STBasicTerrain::
00082 clear() {
00083 STTerrain::clear();
00084
00085 _height_map = "";
00086 _size = 1.0f;
00087 _height_scale = 1.0f;
00088
00089 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
00090 (new GeomVertexArrayFormat(InternalName::get_vertex(), 3,
00091 GeomEnums::NT_stdfloat, GeomEnums::C_point,
00092 InternalName::get_texcoord(), 3,
00093 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord));
00094 set_vertex_format(format);
00095 }
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 bool STBasicTerrain::
00110 setup_terrain(const Filename &terrain_filename) {
00111 _is_valid = false;
00112 set_name(terrain_filename.get_basename());
00113
00114 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00115
00116 Filename fullpath = Filename::text_filename(terrain_filename);
00117 vfs->resolve_filename(fullpath, get_model_path());
00118
00119 if (!vfs->exists(fullpath)) {
00120 speedtree_cat.warning()
00121 << "Couldn't find " << terrain_filename << "\n";
00122 return false;
00123 }
00124
00125 if (vfs->is_directory(fullpath)) {
00126 fullpath = Filename(fullpath, "terrain.txt");
00127 }
00128
00129 istream *in = vfs->open_read_file(fullpath, true);
00130 if (in == NULL) {
00131 speedtree_cat.warning()
00132 << "Couldn't open " << terrain_filename << "\n";
00133 return false;
00134 }
00135
00136 bool success = setup_terrain(*in, fullpath);
00137 vfs->close_read_file(in);
00138
00139 return success;
00140 }
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 bool STBasicTerrain::
00153 setup_terrain(istream &in, const Filename &pathname) {
00154 clear();
00155
00156 Filename dirname = pathname.get_dirname();
00157
00158 string keyword;
00159 in >> keyword;
00160 while (in && !in.eof()) {
00161 if (keyword == "area") {
00162
00163
00164 PN_stdfloat area;
00165 in >> area;
00166 _size = csqrt(area) * speedtree_area_scale;
00167
00168 } else if (keyword == "height_scale") {
00169 in >> _height_scale;
00170
00171 } else if (keyword == "normalmap_b_scale") {
00172 PN_stdfloat normalmap_b_scale;
00173 in >> normalmap_b_scale;
00174
00175 } else if (keyword == "heightmap") {
00176 read_quoted_filename(_height_map, in, dirname);
00177
00178 } else if (keyword == "texture") {
00179 SplatLayer splat;
00180 read_quoted_filename(splat._filename, in, dirname);
00181 in >> splat._tiling;
00182 splat._color.set(1.0f, 1.0f, 1.0f, 1.0f);
00183 _splat_layers.push_back(splat);
00184
00185 } else if (keyword == "color") {
00186
00187 PN_stdfloat r, g, b;
00188 in >> r >> g >> b;
00189 if (!_splat_layers.empty()) {
00190 _splat_layers.back()._color.set(r, g, b, 1.0f);
00191 }
00192
00193 } else if (keyword == "ambient" || keyword == "diffuse" || keyword == "specular" || keyword == "emissive") {
00194 PN_stdfloat r, g, b;
00195 in >> r >> g >> b;
00196
00197 } else if (keyword == "shininess") {
00198 PN_stdfloat s;
00199 in >> s;
00200
00201 } else {
00202 speedtree_cat.error()
00203 << "Invalid token " << keyword << " in " << pathname << "\n";
00204 return false;
00205 }
00206
00207 in >> keyword;
00208 }
00209
00210
00211 in >> ws;
00212
00213 if (!in.eof()) {
00214
00215
00216 in.clear();
00217 string text;
00218 in >> text;
00219 speedtree_cat.error()
00220 << "Unexpected text in " << pathname << " at \"" << text << "\"\n";
00221 return false;
00222 }
00223
00224
00225
00226 if (!_splat_layers.empty()) {
00227 _normal_map = _splat_layers[0]._filename;
00228 _splat_layers.erase(_splat_layers.begin());
00229 }
00230 if (!_splat_layers.empty()) {
00231 _splat_map = _splat_layers[0]._filename;
00232 _splat_layers.erase(_splat_layers.begin());
00233 }
00234
00235
00236 load_data();
00237
00238 return _is_valid;
00239 }
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252 void STBasicTerrain::
00253 load_data() {
00254 _is_valid = false;
00255
00256 if (!read_height_map()) {
00257 return;
00258 }
00259
00260 _is_valid = true;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 PN_stdfloat STBasicTerrain::
00272 get_height(PN_stdfloat x, PN_stdfloat y) const {
00273 return _height_data.calc_bilinear_interpolation(x / _size, y / _size);
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284 PN_stdfloat STBasicTerrain::
00285 get_smooth_height(PN_stdfloat x, PN_stdfloat y, PN_stdfloat radius) const {
00286 return _height_data.calc_smooth(x / _size, y / _size, radius / _size);
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 PN_stdfloat STBasicTerrain::
00299 get_slope(PN_stdfloat x, PN_stdfloat y) const {
00300 return _slope_data.calc_bilinear_interpolation(x / _size, y / _size);
00301 }
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 void STBasicTerrain::
00321 fill_vertices(GeomVertexData *data,
00322 PN_stdfloat start_x, PN_stdfloat start_y,
00323 PN_stdfloat size_xy, int num_xy) const {
00324 nassertv(data->get_format() == _vertex_format);
00325 GeomVertexWriter vertex(data, InternalName::get_vertex());
00326 GeomVertexWriter texcoord(data, InternalName::get_texcoord());
00327
00328 PN_stdfloat vertex_scale = 1.0 / (PN_stdfloat)(num_xy - 1);
00329 PN_stdfloat texcoord_scale = 1.0 / _size;
00330 for (int xi = 0; xi < num_xy; ++xi) {
00331 PN_stdfloat xt = xi * vertex_scale;
00332 PN_stdfloat x = start_x + xt * size_xy;
00333 for (int yi = 0; yi < num_xy; ++yi) {
00334 PN_stdfloat yt = yi * vertex_scale;
00335 PN_stdfloat y = start_y + yt * size_xy;
00336
00337 PN_stdfloat z = get_height(x, y);
00338
00339 vertex.set_data3(x, y, z);
00340 texcoord.set_data3(x * texcoord_scale, -y * texcoord_scale, 1.0f);
00341 }
00342 }
00343 }
00344
00345
00346
00347
00348
00349
00350 void STBasicTerrain::
00351 output(ostream &out) const {
00352 Namable::output(out);
00353 }
00354
00355
00356
00357
00358
00359
00360 void STBasicTerrain::
00361 write(ostream &out, int indent_level) const {
00362 indent(out, indent_level)
00363 << *this << "\n";
00364 }
00365
00366
00367
00368
00369
00370
00371
00372
00373 bool STBasicTerrain::
00374 read_height_map() {
00375 PNMImage image(_height_map);
00376 if (!image.is_valid()) {
00377 return false;
00378 }
00379
00380 _height_data.reset(image.get_x_size(), image.get_y_size());
00381 _min_height = FLT_MAX;
00382 _max_height = FLT_MIN;
00383
00384 PN_stdfloat scalar = _size * _height_scale / image.get_num_channels();
00385 int pi = 0;
00386 for (int yi = image.get_y_size() - 1; yi >= 0; --yi) {
00387 for (int xi = 0; xi < image.get_x_size(); ++xi) {
00388 LColord rgba = image.get_xel_a(xi, yi);
00389 PN_stdfloat v = rgba[0] + rgba[1] + rgba[2] + rgba[3];
00390 v *= scalar;
00391 _height_data._data[pi] = v;
00392 ++pi;
00393 _min_height = min(_min_height, v);
00394 _max_height = max(_max_height, v);
00395 }
00396 }
00397
00398 compute_slope(0.5f);
00399
00400 return true;
00401 }
00402
00403
00404
00405
00406
00407
00408
00409 void STBasicTerrain::
00410 compute_slope(PN_stdfloat smoothing) {
00411 nassertv(!_height_data._data.empty());
00412
00413 int width = _height_data._width;
00414 int height = _height_data._height;
00415 _slope_data.reset(width, height);
00416
00417 PN_stdfloat u_spacing = _size / (PN_stdfloat)width;
00418 PN_stdfloat v_spacing = _size / (PN_stdfloat)height;
00419
00420 for (int i = 0; i < width; ++i) {
00421 int left = (i + width - 1) % width;
00422 int right = (i + 1) % width;
00423
00424 for (int j = 0; j < height; ++j) {
00425 int top = (j + height - 1) % height;
00426 int bottom = (j + 1) % height;
00427
00428 PN_stdfloat slope = 0.0f;
00429 PN_stdfloat this_height = _height_data._data[i + j * width];
00430 slope += catan2(cabs(this_height - _height_data._data[right + j * width]), u_spacing);
00431 slope += catan2(cabs(this_height - _height_data._data[left + j * width]), u_spacing);
00432 slope += catan2(cabs(this_height - _height_data._data[i + top * width]), v_spacing);
00433 slope += catan2(cabs(this_height - _height_data._data[i + bottom * width]), v_spacing);
00434
00435 slope *= (0.5f / MathNumbers::pi_f);
00436
00437 if (slope > 1.0f) {
00438 slope = 1.0f;
00439 }
00440 _slope_data._data[i + j * width] = slope;
00441 }
00442 }
00443
00444 if (smoothing > 0.0f) {
00445
00446 InterpolationData<PN_stdfloat> smoothing_data;
00447 smoothing_data.reset(width, height);
00448 PN_stdfloat *smoothed = &smoothing_data._data[0];
00449
00450 int steps = int(smoothing);
00451 PN_stdfloat last_interpolation = smoothing - steps;
00452 ++steps;
00453 for (int si = 0; si < steps; ++si) {
00454
00455
00456 for (int i = 0; i < width; ++i) {
00457 int left = (i + width - 1) % width;
00458 int right = (i + 1) % width;
00459
00460 for (int j = 0; j < height; ++j) {
00461 int top = (j + height - 1) % height;
00462 int bottom = (j + 1) % height;
00463
00464 smoothed[i + j * width] = (_slope_data._data[right + j * width] +
00465 _slope_data._data[left + j * width] +
00466 _slope_data._data[i + top * width] +
00467 _slope_data._data[i + bottom * width] +
00468 _slope_data._data[right + top * width] +
00469 _slope_data._data[right + bottom * width] +
00470 _slope_data._data[left + top * width] +
00471 _slope_data._data[left + bottom * width]);
00472 smoothed[i + j * width] *= 0.125f;
00473 }
00474 }
00475
00476
00477 if (si == steps - 1) {
00478
00479 for (int i = 0; i < width; ++i) {
00480 for (int j = 0; j < height; ++j) {
00481 _slope_data._data[i + j * width] = interpolate(_slope_data._data[i + j * width], smoothed[i + j * width], last_interpolation);
00482 }
00483 }
00484
00485 } else {
00486
00487 _slope_data = smoothing_data;
00488 }
00489 }
00490 }
00491 }
00492
00493
00494
00495
00496
00497
00498
00499
00500 void STBasicTerrain::
00501 read_quoted_filename(Filename &result, istream &in, const Filename &dirname) {
00502 string filename;
00503 in >> filename;
00504
00505
00506
00507
00508 if (filename.size() >= 2 && filename[0] == '"' && filename[filename.size() - 1] == '"') {
00509 filename = filename.substr(1, filename.size() - 2);
00510 }
00511
00512 result = Filename::from_os_specific(filename);
00513 if (result.is_local()) {
00514 result = Filename(dirname, result);
00515 }
00516 }
00517