From cc0d913712868f2a8b5b834d328d82f18c7cb62a Mon Sep 17 00:00:00 2001 From: Peter Sadrozinski Date: Fri, 5 Oct 2012 20:34:31 -0400 Subject: [PATCH] - refactor construct.cxx into smaller files - remove Match - tile matching now handled in tgconstruct_shared.cxx --- src/BuildTiles/Main/CMakeLists.txt | 22 +- src/BuildTiles/Main/construct.cxx | 2832 ----------------- src/BuildTiles/Main/construct.hxx | 567 ---- src/BuildTiles/Main/main.cxx | 34 +- src/BuildTiles/Main/tgconstruct.cxx | 235 ++ src/BuildTiles/Main/tgconstruct.hxx | 271 ++ src/BuildTiles/Main/tgconstruct_cleanup.cxx | 210 ++ src/BuildTiles/Main/tgconstruct_clip.cxx | 328 ++ src/BuildTiles/Main/tgconstruct_debug.cxx | 206 ++ src/BuildTiles/Main/tgconstruct_elevation.cxx | 256 ++ src/BuildTiles/Main/tgconstruct_landclass.cxx | 215 ++ src/BuildTiles/Main/tgconstruct_lookup.cxx | 82 + src/BuildTiles/Main/tgconstruct_math.cxx | 190 ++ src/BuildTiles/Main/tgconstruct_output.cxx | 252 ++ src/BuildTiles/Main/tgconstruct_poly.cxx | 419 +++ src/BuildTiles/Main/tgconstruct_shared.cxx | 551 ++++ src/BuildTiles/Main/tgconstruct_tesselate.cxx | 81 + src/BuildTiles/Main/tgconstruct_texture.cxx | 187 ++ src/BuildTiles/Main/tglandclass.cxx | 80 + src/BuildTiles/Main/tglandclass.hxx | 159 + src/BuildTiles/Main/tgshape.cxx | 135 + src/BuildTiles/Main/tgshape.hxx | 71 + 22 files changed, 3965 insertions(+), 3418 deletions(-) delete mode 100644 src/BuildTiles/Main/construct.cxx delete mode 100644 src/BuildTiles/Main/construct.hxx create mode 100644 src/BuildTiles/Main/tgconstruct.cxx create mode 100644 src/BuildTiles/Main/tgconstruct.hxx create mode 100644 src/BuildTiles/Main/tgconstruct_cleanup.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_clip.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_debug.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_elevation.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_landclass.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_lookup.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_math.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_output.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_poly.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_shared.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_tesselate.cxx create mode 100644 src/BuildTiles/Main/tgconstruct_texture.cxx create mode 100644 src/BuildTiles/Main/tglandclass.cxx create mode 100644 src/BuildTiles/Main/tglandclass.hxx create mode 100644 src/BuildTiles/Main/tgshape.cxx create mode 100644 src/BuildTiles/Main/tgshape.hxx diff --git a/src/BuildTiles/Main/CMakeLists.txt b/src/BuildTiles/Main/CMakeLists.txt index 8d767d1a..7ad76239 100644 --- a/src/BuildTiles/Main/CMakeLists.txt +++ b/src/BuildTiles/Main/CMakeLists.txt @@ -1,6 +1,24 @@ +include_directories(${GPC_INCLUDE_DIR}) + add_executable(tg-construct - construct.cxx - construct.hxx + tgconstruct.hxx + tgconstruct.cxx + tgconstruct_cleanup.cxx + tgconstruct_clip.cxx + tgconstruct_debug.cxx + tgconstruct_elevation.cxx + tgconstruct_landclass.cxx + tgconstruct_lookup.cxx + tgconstruct_math.cxx + tgconstruct_output.cxx + tgconstruct_poly.cxx + tgconstruct_shared.cxx + tgconstruct_tesselate.cxx + tgconstruct_texture.cxx + tglandclass.cxx + tglandclass.hxx + tgshape.cxx + tgshape.hxx priorities.cxx priorities.hxx usgs.cxx diff --git a/src/BuildTiles/Main/construct.cxx b/src/BuildTiles/Main/construct.cxx deleted file mode 100644 index 037381d6..00000000 --- a/src/BuildTiles/Main/construct.cxx +++ /dev/null @@ -1,2832 +0,0 @@ -// construct.cxx -- Class to manage the primary data used in the -// construction process -// -// Written by Curtis Olson, started May 1999. -// -// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "construct.hxx" -#include "usgs.hxx" - -using std::string; - -double gSnap = 0.00000001; // approx 1 mm - -static const double cover_size = 1.0 / 120.0; -static const double half_cover_size = cover_size * 0.5; - -static unsigned int cur_poly_id = 0; - -// If we don't offset land use squares by some amount, then we can get -// land use square boundaries coinciding with tile boundaries. -// -// This can foul up our edge matching scheme because a subsequently -// generated adjacent tile might be forced to have edge nodes not -// found in the first tile and not on the shared edge. This can lead -// to gaps. If we put skirts around everything that might hide the -// problem. -static const double quarter_cover_size = cover_size * 0.25; - -// Constructor -TGConstruct::TGConstruct(): - useUKGrid(false), - ignoreLandmass(false), - debug_all(false), - ds_id((void*)-1) -{ } - - -// Destructor -TGConstruct::~TGConstruct() { - array.close(); - - // land class polygons - polys_in.clear(); - polys_clipped.clear(); - - // All Nodes - nodes.clear(); -} - -// paths -void TGConstruct::set_paths( const std::string work, const std::string share, const std::string output, const std::vector load ) { - work_base = work; - share_base = share; - output_base = output; - load_dirs = load; -} - -void TGConstruct::set_options( bool uk_grid, bool ignore_lm, double n ) { - useUKGrid = uk_grid; - ignoreLandmass = ignore_lm; - nudge = n; -} - -void TGConstruct::set_debug( std::string path, std::vector area_defs, std::vector shape_defs ) -{ - SG_LOG(SG_GENERAL, SG_ALERT, "Set debug Path " << path); - - debug_path = path; - - /* Find any ids for our tile */ - for (unsigned int i=0; i< area_defs.size(); i++) { - string dsd = area_defs[i]; - size_t d_pos = dsd.find(":"); - string tile = dsd.substr(0, d_pos); - - if( tile == bucket.gen_index_str() ) { - dsd.erase(0, d_pos+1); - - if ( dsd == "all" ) { - debug_all = true; - } else { - std::stringstream ss(dsd); - int i; - - while (ss >> i) - { - SG_LOG(SG_GENERAL, SG_ALERT, "Adding debug file " << i); - - debug_areas.push_back(i); - - if (ss.peek() == ',') - ss.ignore(); - } - } - } - } - - for (unsigned int i=0; i< shape_defs.size(); i++) { - string dsd = shape_defs[i]; - size_t d_pos = dsd.find(":"); - string tile = dsd.substr(0, d_pos); - - if( tile == bucket.gen_index_str() ) { - dsd.erase(0, d_pos+1); - - if ( dsd == "all" ) { - debug_all = true; - } else { - std::stringstream ss(dsd); - int i; - - while (ss >> i) - { - SG_LOG(SG_GENERAL, SG_ALERT, "Adding debug file " << i); - - debug_shapes.push_back(i); - - if (ss.peek() == ',') - ss.ignore(); - } - } - } - } -} - -bool TGConstruct::IsDebugShape( unsigned int id ) -{ - bool is_debug = false; - - /* Check global flag */ - if ( debug_all ) { - is_debug = true; - } else { - for (unsigned int i=0; i> skipcomment; - while ( !in.eof() ) { - in >> first_line; - if ( first_line == "#2D" ) { - poly3d = false; - with_tp = false; - - in >> poly_name; - num_polys = 1; - } else if ( first_line == "#2D_WITH_MASK" ) { - poly3d = false; - with_tp = false; - - in >> poly_name; - in >> num_polys; - } else if ( first_line == "#2D_WITH_TPS" ) { - poly3d = false; - with_tp = true; - - in >> poly_name; - in >> num_polys; - } else if ( first_line == "#3D" ) { - poly3d = true; - with_tp = false; - - in >> poly_name; - num_polys = 1; - } else { - // support old format (default to 2d) - poly3d = false; - with_tp = false; - - poly_name = first_line; - num_polys = 1; - } - poly_type = get_area_type( poly_name ); - - int area = (int)poly_type; - string material = get_area_name( area ); - - // Generate a new Shape for the poly - TGShape shape; - TGSuperPoly sp; - - for (k=0; k> x; - in >> y; - in >> width; - in >> length; - in >> heading; - in >> minu; - in >> maxu; - in >> minv; - in >> maxv; - - tp.set_ref( Point3D(x, y, 0.0f) ); - tp.set_width( width ); - tp.set_length( length ); - tp.set_heading( heading ); - tp.set_minu( minu ); - tp.set_maxu( maxu ); - tp.set_minv( minv ); - tp.set_maxv( maxv ); - } - - in >> contours; - - poly.erase(); - - for ( i = 0; i < contours; ++i ) { - in >> count; - - if ( count < 3 ) { - SG_LOG( SG_CLIPPER, SG_ALERT, "Polygon with less than 3 data points." ); - exit(-1); - } - - in >> hole_flag; - - in >> startx; - in >> starty; - if ( poly3d ) { - in >> startz; - } else { - startz = -9999.0; - } - - p = Point3D(startx+nudge, starty+nudge, startz); - p.snap( gSnap ); - poly.add_node( i, p ); - - if ( poly3d ) { - nodes.unique_add_fixed_elevation( p ); - } else { - nodes.unique_add( p ); - } - - for ( j = 1; j < count - 1; ++j ) { - in >> x; - in >> y; - if ( poly3d ) { - in >> z; - } else { - z = -9999.0; - } - p = Point3D( x+nudge, y+nudge, z ); - p.snap( gSnap ); - poly.add_node( i, p ); - if ( poly3d ) { - nodes.unique_add_fixed_elevation( p ); - } else { - nodes.unique_add( p ); - } - } - - in >> lastx; - in >> lasty; - if ( poly3d ) { - in >> lastz; - } else { - lastz = -9999.0; - } - - if ( (fabs(startx - lastx) < SG_EPSILON) && - (fabs(starty - lasty) < SG_EPSILON) && - (fabs(startz - lastz) < SG_EPSILON) ) { - // last point same as first, discard - } else { - p = Point3D( lastx+nudge, lasty+nudge, lastz ); - p.snap( gSnap ); - poly.add_node( i, p ); - if ( poly3d ) { - nodes.unique_add_fixed_elevation( p ); - } else { - nodes.unique_add( p ); - } - } - } - - poly = remove_dups( poly ); - - sp.set_poly( poly ); - sp.set_material( material ); - shape.sps.push_back( sp ); - - if ( with_tp ) { - shape.textured = true; - shape.tps.push_back( tp ); - } - else - { - shape.textured = false; - } - - in >> skipcomment; - } - - // Once the full poly is loaded, build the clip mask - shape.BuildMask(); - shape.area = area; - shape.id = cur_poly_id++; - - polys_in.add_shape( area, shape ); - - if ( IsDebugShape( shape.id ) ) { - WriteDebugShape( "loaded", shape ); - } - } - - return true; -} - - -// Load a polygon definition file containing osgb36 Eastings and Northings -// and convert them to WGS84 Latitude and Longitude -bool TGConstruct::load_osgb36_poly(const string& path) { - string poly_name; - AreaType poly_type; - int contours, count, i, j; - int hole_flag; - double startx, starty, x, y, lastx, lasty; - - SG_LOG( SG_CLIPPER, SG_INFO, "Loading " << path << " ..." ); - - sg_gzifstream in( path ); - - if ( !in ) { - SG_LOG( SG_CLIPPER, SG_ALERT, "Cannot open file: " << path ); - exit(-1); - } - - TGPolygon poly; - - Point3D p; - Point3D OSRef; - in >> skipcomment; - while ( !in.eof() ) { - in >> poly_name; - SG_LOG( SG_CLIPPER, SG_INFO, "poly name = " << poly_name); - poly_type = get_area_type( poly_name ); - SG_LOG( SG_CLIPPER, SG_INFO, "poly type (int) = " << (int)poly_type); - in >> contours; - SG_LOG( SG_CLIPPER, SG_INFO, "num contours = " << contours); - - poly.erase(); - - for ( i = 0; i < contours; ++i ) { - in >> count; - - if ( count < 3 ) { - SG_LOG( SG_CLIPPER, SG_ALERT, "Polygon with less than 3 data points." ); - exit(-1); - } - - in >> hole_flag; - - in >> startx; - in >> starty; - OSRef = Point3D(startx, starty, -9999.0); - - //Convert from OSGB36 Eastings/Northings to WGS84 Lat/Lon - //Note that startx and starty themselves must not be altered since we compare them with unaltered lastx and lasty later - p = OSGB36ToWGS84(OSRef); - - poly.add_node( i, p ); - nodes.unique_add( p ); - - SG_LOG( SG_CLIPPER, SG_BULK, "0 = " << startx << ", " << starty ); - - for ( j = 1; j < count - 1; ++j ) { - in >> x; - in >> y; - OSRef = Point3D( x, y, -9999.0 ); - p = OSGB36ToWGS84(OSRef); - - poly.add_node( i, p ); - nodes.unique_add( p ); - SG_LOG( SG_CLIPPER, SG_BULK, j << " = " << x << ", " << y ); - } - - in >> lastx; - in >> lasty; - - if ( (fabs(startx - lastx) < SG_EPSILON) && - (fabs(starty - lasty) < SG_EPSILON) ) { - // last point same as first, discard - } else { - OSRef = Point3D( lastx, lasty, -9999.0 ); - p = OSGB36ToWGS84(OSRef); - - poly.add_node( i, p ); - nodes.unique_add( p ); - SG_LOG( SG_CLIPPER, SG_BULK, count - 1 << " = " << lastx << ", " << lasty ); - } - } - - // TODO : Make like OGR - int area = (int)poly_type; - string material = get_area_name( area ); - add_poly(area, poly, material); - // END TODO - - in >> skipcomment; - } - - return true; -} - -// load all 2d polygons from the specified load disk directories and -// clip against each other to resolve any overlaps -int TGConstruct::LoadLandclassPolys( void ) { - int i; - - string base = bucket.gen_base_path(); - string poly_path; - int count = 0; - - polys_in.clear(); - - // load 2D polygons from all directories provided - for ( i = 0; i < (int)load_dirs.size(); ++i ) { - poly_path = work_base + "/" + load_dirs[i] + '/' + base; - - string tile_str = bucket.gen_index_str(); - simgear::Dir d(poly_path); - if (!d.exists()) { - SG_LOG(SG_GENERAL, SG_DEBUG, "directory not found: " << poly_path); - continue; - } - - simgear::PathList files = d.children(simgear::Dir::TYPE_FILE); - SG_LOG( SG_CLIPPER, SG_ALERT, files.size() << " Polys in " << d.path() ); - - BOOST_FOREACH(const SGPath& p, files) { - if (p.file_base() != tile_str) { - continue; - } - - string lext = p.complete_lower_extension(); - if ((lext == "arr") || (lext == "arr.gz") || (lext == "btg.gz") || - (lext == "fit") || (lext == "fit.gz") || (lext == "ind")) - { - // skipped! - } else if (lext == "osgb36") { - SG_LOG(SG_GENERAL, SG_ALERT, " Loading osgb36 poly definition file " << p.file()); - load_osgb36_poly( p.str() ); - ++count; - } else { - load_poly( p.str() ); - SG_LOG(SG_GENERAL, SG_ALERT, " Loaded " << p.file()); - ++count; - } - } // of directory file children - } - SG_LOG(SG_GENERAL, SG_ALERT, " Total polys used for this tile: " << count ); - return count; -} - -// Add a polygon to a list, merging if possible. -// -// Merge a polygon with an existing one if possible, append a new one -// otherwise; this function is used by actual_load_landcover, below, -// to reduce the number of separate polygons. -void TGConstruct::add_to_polys ( TGPolygon &accum, const TGPolygon &poly) { - if ( accum.contours() > 0 ) { - accum = tgPolygonUnionClipper( accum, poly ); - } else { - accum = poly; - } -} - -// make the area specified area, look up the land cover type, and add -// it to polys -void TGConstruct::make_area( const LandCover &cover, TGPolygon *polys, - double x1, double y1, double x2, double y2, - double half_dx, double half_dy ) -{ - const double fudge = 0.0001; // (0.0001 degrees =~ 10 meters) - - AreaType area = get_landcover_type( cover, - x1 + half_dx, y1 + half_dy, - x2 - x1, y2 - y1 ); - - if ( area != get_default_area_type() ) { - // Create a square polygon and merge it into the list. - TGPolygon poly; - poly.erase(); - poly.add_node(0, Point3D(x1 - fudge, y1 - fudge, 0.0)); - poly.add_node(0, Point3D(x1 - fudge, y2 + fudge, 0.0)); - poly.add_node(0, Point3D(x2 + fudge, y2 + fudge, 0.0)); - poly.add_node(0, Point3D(x2 + fudge, y1 - fudge, 0.0)); - - if ( measure_roughness( poly ) < 1.0 ) { - add_to_polys(polys[area], poly); - } - } -} - -// Come up with a "rough" metric for the roughness of the terrain -// coverted by a polygon -double TGConstruct::measure_roughness( TGPolygon &poly ) { - int i; - unsigned int j; - - // find the elevation range - double max_z = -9999.0; - double min_z = 9999.0; - - for ( i = 0; i < poly.contours(); ++i ) { - point_list points = poly.get_contour( i ); - for ( j = 0; j < points.size(); ++j ) { - double z; - z = array.altitude_from_grid( points[j].x() * 3600.0, - points[j].y() * 3600.0 ); - if ( z < -9000 ) { - z = array.closest_nonvoid_elev( points[j].x() * 3600.0, - points[j].y() * 3600.0 ); - } - - if ( z < min_z ) { - min_z = z; - } - if ( z > max_z ) { - max_z = z; - } - } - } - - double diff = max_z - min_z; - - // 50m difference in polygon elevation range yields a roughness - // metric of 1.0. Less than 1.0 is relatively flat. More than - // 1.0 is relatively rough. - - SG_LOG(SG_GENERAL, SG_ALERT, "roughness = " << diff / 50.0 ); - - return diff / 50.0; -} - -AreaType TGConstruct::get_landcover_type (const LandCover &cover, double xpos, double ypos, double dx, double dy) -{ - // Look up the land cover for the square - int cover_value = cover.getValue(xpos, ypos); - AreaType area = translateUSGSCover(cover_value); - - if ( area != get_default_area_type() ) { - // Non-default area is fine. - return area; - } else { - // If we're stuck with the default area, try to borrow from a - // neighbour. - for (double x = xpos - dx; x <= xpos + dx; x += dx) { - for (double y = ypos - dy; y < ypos + dx; y += dy) { - if (x != xpos || y != ypos) { - cover_value = cover.getValue(x, y); - area = translateUSGSCover(cover_value); - if (area != get_default_area_type() ) { - return area; - } - } - } - } - } - - // OK, give up and return default - return get_default_area_type(); -} - - -// Generate polygons from land-cover raster. Horizontally- or -// vertically-adjacent polygons will be merged automatically. -int TGConstruct::load_landcover() -{ - int count = 0; - - try { - - LandCover cover(get_cover()); - TGPolygon polys[TG_MAX_AREA_TYPES]; - TGPolygon poly; // working polygon - - // Get the lower left (SW) corner of the tile - double base_lon = bucket.get_center_lon() - - 0.5 * bucket.get_width() - - quarter_cover_size; - double base_lat = bucket.get_center_lat() - - 0.5 * bucket.get_height() - - quarter_cover_size; - - SG_LOG(SG_GENERAL, SG_ALERT, "raster land cover: tile at " << base_lon << ',' << base_lat); - - double max_lon = bucket.get_center_lon() - + 0.5 * bucket.get_width(); - double max_lat = bucket.get_center_lat() - + 0.5 * bucket.get_height(); - - SG_LOG(SG_GENERAL, SG_ALERT, "raster land cover: extends to " << max_lon << ',' << max_lat); - - double x1 = base_lon; - double y1 = base_lat; - double x2 = x1 + cover_size; - double y2 = y1 + cover_size; - - while ( x1 < max_lon ) { - while ( y1 < max_lat ) { - make_area( cover, polys, x1, y1, x2, y2, half_cover_size, half_cover_size ); - - y1 = y2; - y2 += cover_size; - } - - x1 = x2; - x2 += cover_size; - y1 = base_lat; - y2 = y1 + cover_size; - } - - // Now that we're finished looking up land cover, we have a list - // of lists of polygons, one (possibly-empty) list for each area - // type. Add the remaining polygons to the clipper. - for ( int i = 0; i < TG_MAX_AREA_TYPES; i++ ) { - if ( polys[i].contours() ) { - // TODO : REMOVE add_poly - add_poly( i, polys[i], get_area_name((AreaType)i )); - count++; - } - } - } catch ( string e ) { - SG_LOG(SG_GENERAL, SG_ALERT, "Died with exception: " << e); - exit(-1); - } - - // Return the number of polygons actually read. - return count; -} - -void TGConstruct::FixTJunctions( void ) { - int before, after; - - // traverse each poly, and add intermediate nodes - for ( unsigned int i = 0; i < TG_MAX_AREA_TYPES; ++i ) { - for( unsigned int j = 0; j < polys_clipped.area_size(i); ++j ) { - for( unsigned int k = 0; k < polys_clipped.shape_size(i, j); ++k ) { - TGPolygon current = polys_clipped.get_poly(i, j, k); - - before = current.total_size(); - current = add_tgnodes_to_poly( current, &nodes ); - after = current.total_size(); - - if (before != after) { - SG_LOG( SG_CLIPPER, SG_INFO, "Fixed T-Junctions in " << get_area_name( (AreaType)i ) << ":" << j+1 << "-" << k << " of " << (int)polys_clipped.area_size(i) << " nodes increased from " << before << " to " << after ); - } - - /* Save it back */ - polys_clipped.set_poly( i, j, k, current ); - } - } - } -} - -void TGConstruct::calc_gc_course_dist( const Point3D& start, const Point3D& dest, - double *course, double *dist ) -{ - SGGeoc gs = start.toSGGeoc(); - SGGeoc gd = dest.toSGGeoc(); - *course = SGGeoc::courseRad(gs, gd); - *dist = SGGeoc::distanceM(gs, gd); -} - -// calculate spherical distance between two points (lon, lat specified -// in degrees, result returned in meters) -double TGConstruct::distanceSphere( const Point3D p1, const Point3D p2 ) { - Point3D r1( p1.x() * SG_DEGREES_TO_RADIANS, - p1.y() * SG_DEGREES_TO_RADIANS, - p1.z() ); - Point3D r2( p2.x() * SG_DEGREES_TO_RADIANS, - p2.y() * SG_DEGREES_TO_RADIANS, - p2.z() ); - - double course, dist_m; - calc_gc_course_dist( r1, r2, &course, &dist_m ); - - return dist_m; -} - -// fix the elevations of the geodetic nodes -// This should be done in the nodes class itself, except for the need for the triangle type -// hopefully, this will get better when we have the area lookup via superpoly... -void TGConstruct::CalcElevations( void ) -{ - TGPolyNodes tri_nodes; - double e1, e2, e3, min; - int n1, n2, n3; - Point3D p; - - SG_LOG(SG_GENERAL, SG_ALERT, "fixing node heights"); - - for (int i = 0; i < (int)nodes.size(); ++i) { - TGNode node = nodes.get_node( i ); - Point3D pos = node.GetPosition(); - - if ( !node.GetFixedPosition() ) { - // set elevation as interpolated point from DEM data. - nodes.SetElevation( i, array.altitude_from_grid(pos.x() * 3600.0, pos.y() * 3600.0) ); - } - } - - // now flatten some stuff - for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { - if ( is_lake_area( (AreaType)area ) ) { - for (int shape = 0; shape < (int)polys_clipped.area_size(area); ++shape ) { - for (int segment = 0; segment < (int)polys_clipped.shape_size(area, shape); ++segment ) { - - SG_LOG( SG_CLIPPER, SG_INFO, "Flattening " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) ); - tri_nodes = polys_clipped.get_tri_idxs( area, shape, segment ); - - for (int tri=0; tri < tri_nodes.contours(); tri++) { - if (tri_nodes.contour_size( tri ) != 3) { - SG_LOG(SG_GENERAL, SG_ALERT, "triangle doesnt have 3 nodes" << tri_nodes.contour_size( tri ) ); - exit(0); - } - - n1 = tri_nodes.get_pt( tri, 0 ); - e1 = nodes.get_node(n1).GetPosition().z(); - n2 = tri_nodes.get_pt( tri, 1 ); - e2 = nodes.get_node(n2).GetPosition().z(); - n3 = tri_nodes.get_pt( tri, 2 ); - e3 = nodes.get_node(n3).GetPosition().z(); - - min = e1; - if ( e2 < min ) { min = e2; } - if ( e3 < min ) { min = e3; } - - nodes.SetElevation( n1, min ); - nodes.SetElevation( n2, min ); - nodes.SetElevation( n3, min ); - } - } - } - } - - if ( is_stream_area( (AreaType)area ) ) { - for (int shape = 0; shape < (int)polys_clipped.area_size(area); ++shape ) { - for (int segment = 0; segment < (int)polys_clipped.shape_size(area, shape); ++segment ) { - - SG_LOG( SG_CLIPPER, SG_INFO, "Flattening " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) ); - tri_nodes = polys_clipped.get_tri_idxs( area, shape, segment ); - - for (int tri=0; tri < tri_nodes.contours(); tri++) { - if (tri_nodes.contour_size( tri ) != 3) { - SG_LOG(SG_GENERAL, SG_ALERT, "triangle doesnt have 3 nodes" << tri_nodes.contour_size( tri ) ); - exit(0); - } - - point_list raw_nodes = nodes.get_geod_nodes(); - - n1 = tri_nodes.get_pt( tri, 0 ); - e1 = nodes.get_node(n1).GetPosition().z(); - n2 = tri_nodes.get_pt( tri, 1 ); - e2 = nodes.get_node(n2).GetPosition().z(); - n3 = tri_nodes.get_pt( tri, 2 ); - e3 = nodes.get_node(n3).GetPosition().z(); - - min = e1; - p = raw_nodes[n1]; - - if ( e2 < min ) { min = e2; p = raw_nodes[n2]; } - if ( e3 < min ) { min = e3; p = raw_nodes[n3]; } - - double d1 = distanceSphere( p, raw_nodes[n1] ); - double d2 = distanceSphere( p, raw_nodes[n2] ); - double d3 = distanceSphere( p, raw_nodes[n3] ); - - double max1 = d1 * 0.20 + min; - double max2 = d2 * 0.20 + min; - double max3 = d3 * 0.20 + min; - - if ( max1 < e1 ) { nodes.SetElevation( n1, max1 ); } - if ( max2 < e2 ) { nodes.SetElevation( n2, max2 ); } - if ( max3 < e3 ) { nodes.SetElevation( n3, max3 ); } - } - } - } - } - - if ( is_road_area( (AreaType)area ) ) { - for (int shape = 0; shape < (int)polys_clipped.area_size(area); ++shape ) { - for (int segment = 0; segment < (int)polys_clipped.shape_size(area, shape); ++segment ) { - - SG_LOG( SG_CLIPPER, SG_INFO, "Flattening " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) ); - tri_nodes = polys_clipped.get_tri_idxs( area, shape, segment ); - - for (int tri=0; tri < tri_nodes.contours(); tri++) { - if (tri_nodes.contour_size( tri ) != 3) { - SG_LOG(SG_GENERAL, SG_ALERT, "triangle doesnt have 3 nodes" << tri_nodes.contour_size( tri ) ); - exit(0); - } - - point_list raw_nodes = nodes.get_geod_nodes(); - - n1 = tri_nodes.get_pt( tri, 0 ); - e1 = nodes.get_node(n1).GetPosition().z(); - n2 = tri_nodes.get_pt( tri, 1 ); - e2 = nodes.get_node(n2).GetPosition().z(); - n3 = tri_nodes.get_pt( tri, 2 ); - e3 = nodes.get_node(n3).GetPosition().z(); - - min = e1; - p = raw_nodes[n1]; - - if ( e2 < min ) { min = e2; p = raw_nodes[n2]; } - if ( e3 < min ) { min = e3; p = raw_nodes[n3]; } - - double d1 = distanceSphere( p, raw_nodes[n1] ); - double d2 = distanceSphere( p, raw_nodes[n2] ); - double d3 = distanceSphere( p, raw_nodes[n3] ); - - double max1 = d1 * 0.30 + min; - double max2 = d2 * 0.30 + min; - double max3 = d3 * 0.30 + min; - - if ( max1 < e1 ) { nodes.SetElevation( n1, max1 ); } - if ( max2 < e2 ) { nodes.SetElevation( n2, max2 ); } - if ( max3 < e3 ) { nodes.SetElevation( n3, max3 ); } - } - } - } - } - - if ( is_ocean_area( (AreaType)area ) ) { - for (int shape = 0; shape < (int)polys_clipped.area_size(area); ++shape ) { - for (int segment = 0; segment < (int)polys_clipped.shape_size(area, shape); ++segment ) { - - SG_LOG( SG_CLIPPER, SG_INFO, "Flattening " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) ); - tri_nodes = polys_clipped.get_tri_idxs( area, shape, segment ); - - for (int tri=0; tri < tri_nodes.contours(); tri++) { - if (tri_nodes.contour_size( tri ) != 3) { - SG_LOG(SG_GENERAL, SG_ALERT, "triangle doesnt have 3 nodes" << tri_nodes.contour_size( tri ) ); - exit(0); - } - - n1 = tri_nodes.get_pt( tri, 0 ); - n2 = tri_nodes.get_pt( tri, 1 ); - n3 = tri_nodes.get_pt( tri, 2 ); - - nodes.SetElevation( n1, 0.0 ); - nodes.SetElevation( n2, 0.0 ); - nodes.SetElevation( n3, 0.0 ); - } - } - } - } - } -} - -TGPolygon TGConstruct::area_tex_coords( const TGPolygon& tri ) -{ - TGPolygon result; - result.erase(); - - // lots of conversion needed to use simgear API - perhaps we need a new simgear API? - for (int c=0; c conv_geods; - point_list tex_coords; - - for (int i = 0; i < (int)nodes.size(); ++i ) - { - SGGeod conv_geod = SGGeod::fromDegM( nodes[i].x(), nodes[i].y(), nodes[i].z() ); - SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << nodes[i] ); - conv_geods.push_back( conv_geod ); - } - - // now calculate texture coordinates - // generate identity interger list... - std::vector< int > node_idx; - for (int i = 0; i < (int)conv_geods.size(); i++) { - node_idx.push_back(i); - } - - std::vector< SGVec2f > tp_list = sgCalcTexCoords( bucket, conv_geods, node_idx ); - // generate a contour of texture coordinates from the tp list - for (int i = 0; i < (int)tp_list.size(); i++) - { - tex_coords.push_back( Point3D::fromSGVec2( tp_list[i] ) ); - } - result.add_contour( tex_coords, 0 ); - } - - return result; -} - -TGPolygon TGConstruct::linear_tex_coords( const TGPolygon& tri, const TGTexParams& tp ) -{ - TGPolygon result; - int i, j; - - result.erase(); - - Point3D ref = tp.get_ref(); - double width = tp.get_width(); - double length = tp.get_length(); - double heading = tp.get_heading(); - double minu = tp.get_minu(); - double maxu = tp.get_maxu(); - double minv = tp.get_minv(); - double maxv = tp.get_maxv(); - SG_LOG( SG_GENERAL, SG_DEBUG, "section ref = " << ref ); - SG_LOG( SG_GENERAL, SG_DEBUG, " width = " << width ); - SG_LOG( SG_GENERAL, SG_DEBUG, " length = " << length ); - SG_LOG( SG_GENERAL, SG_DEBUG, " heading = " << heading ); - SG_LOG( SG_GENERAL, SG_DEBUG, " minv = " << minv ); - SG_LOG( SG_GENERAL, SG_DEBUG, " maxv = " << maxv ); - SG_LOG( SG_GENERAL, SG_DEBUG, " heading = " << heading ); - - Point3D p, t; - double x, y, tx, ty; - - for ( i = 0; i < tri.contours(); ++i ) - { - for ( j = 0; j < tri.contour_size( i ); ++j ) - { - p = tri.get_pt( i, j ); - SG_LOG(SG_GENERAL, SG_DEBUG, "tex coords for contour " << i << " point " << j << ": " << p ); - - // - // 1. Calculate distance and bearing from the center of - // the feature - // - - // given alt, lat1, lon1, lat2, lon2, calculate starting - // and ending az1, az2 and distance (s). Lat, lon, and - // azimuth are in degrees. distance in meters - double az1, az2, dist; - geo_inverse_wgs_84( 0, ref.y(), ref.x(), p.y(), p.x(), - &az1, &az2, &dist ); - SG_LOG(SG_GENERAL, SG_DEBUG, "basic course from ref = " << az2); - - // - // 2. Rotate this back into a coordinate system where Y - // runs the length of the runway and X runs crossways. - // - - double course = az2 - heading; - while ( course < -360 ) { course += 360; } - while ( course > 360 ) { course -= 360; } - SG_LOG( SG_GENERAL, SG_DEBUG, - " course = " << course << " dist = " << dist ); - - // - // 3. Convert from polar to cartesian coordinates - // - - x = sin( course * SGD_DEGREES_TO_RADIANS ) * dist; - y = cos( course * SGD_DEGREES_TO_RADIANS ) * dist; - SG_LOG(SG_GENERAL, SG_DEBUG, " x = " << x << " y = " << y); - - // - // 4. Map x, y point into texture coordinates - // - double tmp; - - tmp = x / width; - tx = tmp * (maxu - minu) + minu; - - if ( tx < -1.0 ) { tx = -1.0; } - if ( tx > 1.0 ) { tx = 1.0; } - - SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ")"); - - ty = (y/length) + minv; - SG_LOG(SG_GENERAL, SG_DEBUG, " (" << ty << ")"); - - t = Point3D( tx, ty, 0 ); - SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ", " << ty << ")"); - - result.add_node( i, t ); - } - } - - return result; -} - -// collect custom objects and move to scenery area -void TGConstruct::AddCustomObjects( void ) { - // Create/open the output .stg file for writing - SGPath dest_d(output_base.c_str()); - dest_d.append(bucket.gen_base_path().c_str()); - string dest_dir = dest_d.str_native(); - SGPath dest_i(dest_d); - dest_i.append(bucket.gen_index_str()); - dest_i.concat(".stg"); - string dest_ind = dest_i.str_native(); - - FILE *fp; - if ( (fp = fopen( dest_ind.c_str(), "w" )) == NULL ) { - SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: opening " << dest_ind << " for writing!" ); - exit(-1); - } - - // Start with the default custom object which is the base terrain - fprintf(fp, "OBJECT_BASE %s.btg\n", bucket.gen_index_str().c_str()); - - char line[2048]; // big enough? - char token[256]; - char name[256]; - - for ( int i = 0; i < (int)load_dirs.size(); ++i ) { - SGPath base(work_base.c_str()); - base.append(load_dirs[i]); - base.append( bucket.gen_base_path() ); - SGPath index(base); - index.append( bucket.gen_index_str() ); - index.concat(".ind"); - string index_file = index.str_native(); - - sg_gzifstream in( index_file ); - - if ( ! in.is_open() ) { - //No custom objects - } else { - while ( ! in.eof() ) { - SG_LOG( SG_GENERAL, SG_INFO, "Collecting custom objects from " << index_file ); - in.getline(line, 2048); - SG_LOG( SG_GENERAL, SG_INFO, "line = " << line ); - - int result = sscanf( line, "%s %s", token, name ); - SG_LOG( SG_GENERAL, SG_INFO, "scanf scanned " << result << " tokens" ); - - if ( result > 0 ) { - SG_LOG( SG_GENERAL, SG_INFO, "token = " << token << " name = " << name ); - - if ( strcmp( token, "OBJECT" ) == 0 ) { - base.append(name); - base.concat(".gz"); - string basecom = base.str_native(); -#ifdef _MSC_VER - string command = "copy " + basecom + " " + dest_dir; -#else - string command = "cp " + basecom + " " + dest_dir; -#endif - SG_LOG( SG_GENERAL, SG_INFO, "running " << command ); - system( command.c_str() ); - - fprintf(fp, "OBJECT %s\n", name); - } else { - fprintf(fp, "%s\n", line); - } - } - } - } - } - - fclose(fp); -} - -// Attempt to merge slivers into a list of polygons. -// -// For each sliver contour, see if a union with another polygon yields -// a polygon with no increased contours (i.e. the sliver is adjacent -// and can be merged.) If so, replace the clipped polygon with the -// new polygon that has the sliver merged in. -void TGConstruct::merge_slivers( TGLandclass& clipped, poly_list& slivers_list ) { - TGPolygon poly, result, slivers, sliver; - point_list contour; - int original_contours, result_contours; - bool done; - int area, shape, segment, i, j; - int merged = 0; - int total = 0; - - for ( i = 0; i < (int)slivers_list.size(); i++ ) { - slivers = slivers_list[i]; - - for ( j = 0; j < slivers.contours(); ++j ) { - // make the sliver polygon - contour = slivers.get_contour( j ); - total++; - - sliver.erase(); - sliver.add_contour( contour, 0 ); - done = false; - - for ( area = 0; area < TG_MAX_AREA_TYPES && !done; ++area ) { - if ( is_hole_area( area ) ) { - // don't merge a non-hole sliver in with a hole - continue; - } - - for ( shape = 0; shape < (int)clipped.area_size(area) && !done; ++shape ) { - unsigned int shape_id = clipped.get_shape( area, shape ).id; - - for ( segment = 0; segment < (int)clipped.shape_size(area, shape) && !done; ++segment ) { - - poly = clipped.get_poly( area, shape, segment ); - original_contours = poly.contours(); - result = tgPolygonUnionClipper( poly, sliver ); - result_contours = result.contours(); - - if ( original_contours == result_contours ) { - SG_LOG(SG_GENERAL, SG_INFO, "MERGED SLIVER " << i << ", " << j << " into area " << get_area_name( (AreaType)area ) << " id: " << shape_id << " segment: " << segment ); - - clipped.set_poly( area, shape, segment, result ); - merged++; - - /* add the sliver to the clip_mask, too */ - TGPolygon mask = clipped.get_mask( area, shape ); - result = tgPolygonUnionClipper( mask, sliver ); - clipped.set_mask( area, shape, result ); - - if ( IsDebugShape( shape_id ) ) { - WriteDebugShape( "with_slivers", clipped.get_shape( area, shape ) ); - } - - done = true; - } - } - } - } - } - } - - slivers_list.clear(); - - SG_LOG(SG_GENERAL, SG_INFO, " UNMERGED SLIVERS: " << total - merged ); -} - -bool TGConstruct::ClipLandclassPolys( void ) { - TGPolygon clipped, tmp; - TGPolygon remains; - TGPolygon safety_base; - poly_list slivers; - int i, j; - Point3D p; - point2d min, max; - bool debug_area, debug_shape; - static int accum_idx = 0; - -#if !USE_ACCUMULATOR - TGPolygon accum; -#endif - - // Get clip bounds - min.x = bucket.get_center_lon() - 0.5 * bucket.get_width(); - min.y = bucket.get_center_lat() - 0.5 * bucket.get_height(); - max.x = bucket.get_center_lon() + 0.5 * bucket.get_width(); - max.y = bucket.get_center_lat() + 0.5 * bucket.get_height(); - -#if USE_ACCUMULATOR - - tgPolygonInitClipperAccumulator(); - -#else - accum.erase(); -#endif - - // set up clipping tile : and remember to add the nodes! - safety_base.erase(); - - p = Point3D(min.x, min.y, -9999.0); - safety_base.add_node( 0, p ); - nodes.unique_add( p ); - - p = Point3D(max.x, min.y, -9999.0); - safety_base.add_node( 0, p ); - nodes.unique_add( p ); - - p = Point3D(max.x, max.y, -9999.0); - safety_base.add_node( 0, p ); - nodes.unique_add( p ); - - p = Point3D(min.x, max.y, -9999.0); - safety_base.add_node( 0, p ); - nodes.unique_add( p ); - - // set up land mask, we clip most things to this since it is our - // best representation of land vs. ocean. If we have other less - // accurate data that spills out into the ocean, we want to just - // clip it. - // also set up a mask for all water and islands - TGPolygon land_mask, water_mask, island_mask; - land_mask.erase(); - water_mask.erase(); - island_mask.erase(); - - for ( i = 0; i < TG_MAX_AREA_TYPES; i++ ) { - if ( is_landmass_area( i ) && !ignoreLandmass ) { - for ( unsigned int j = 0; j < polys_in.area_size(i); ++j ) { - land_mask = tgPolygonUnionClipper( land_mask, polys_in.get_mask(i, j) ); - - } - - } else if ( is_water_area( i ) ) { - for (unsigned int j = 0; j < polys_in.area_size(i); j++) { - water_mask = tgPolygonUnionClipper( water_mask, polys_in.get_mask(i, j) ); - } - } else if ( is_island_area( i ) ) { - for (unsigned int j = 0; j < polys_in.area_size(i); j++) { - island_mask = tgPolygonUnionClipper( island_mask, polys_in.get_mask(i, j) ); - } - } - } - - // Dump the masks - if ( debug_all || debug_shapes.size() || debug_areas.size() ) { - WriteDebugPoly( "land_mask", "", land_mask ); - WriteDebugPoly( "water_mask", "", water_mask ); - WriteDebugPoly( "island_mask", "", island_mask ); - } - - // process polygons in priority order - for ( i = 0; i < TG_MAX_AREA_TYPES; ++i ) { - debug_area = IsDebugArea( i ); - - for( j = 0; j < (int)polys_in.area_size(i); ++j ) { - TGPolygon current = polys_in.get_mask(i, j); - debug_shape = IsDebugShape( polys_in.get_shape( i, j ).id ); - - SG_LOG( SG_CLIPPER, SG_INFO, "Clipping " << get_area_name( (AreaType)i ) << ":" << j+1 << " of " << polys_in.area_size(i) << " accum_idx: " << accum_idx ); - - tmp = current; - - // if not a hole, clip the area to the land_mask - if ( !ignoreLandmass && !is_hole_area( i ) ) { - tmp = tgPolygonIntClipper( tmp, land_mask ); - } - - // if a water area, cut out potential islands - if ( is_water_area( i ) ) { - // clip against island mask - tmp = tgPolygonDiffClipper( tmp, island_mask ); - } - - if ( debug_area || debug_shape ) { - char layer[32]; - char name[32]; - sprintf(layer, "pre_clip_%d", polys_in.get_shape( i, j ).id ); - sprintf(name, "shape %d,%d", i,j); - WriteDebugPoly( layer, name, tmp ); - - sprintf(layer, "pre_clip_accum_%d_%d", accum_idx, polys_in.get_shape( i, j ).id ); - sprintf(name, "shape_accum %d,%d", i,j); - -#if USE_ACCUMULATOR - tgPolygonDumpAccumulator( ds_name, layer, name ); -#else - WriteDebugPoly( layer, name, accum ); -#endif - } - - // simplify polygon before clipping - tmp = tgPolygonSimplify( tmp ); - -#if USE_ACCUMULATOR - clipped = tgPolygonDiffClipperWithAccumulator( tmp ); -#else - clipped = tgPolygonDiff( tmp, accum ); -#endif - - if ( debug_area || debug_shape ) { - char layer[32]; - char name[32]; - sprintf(layer, "post_clip_%d", polys_in.get_shape( i, j ).id ); - sprintf(name, "shape %d,%d", i,j); - WriteDebugPoly( layer, name, clipped ); - } - - // only add to output list if the clip left us with a polygon - if ( clipped.contours() > 0 ) { - -#if FIND_SLIVERS - // move slivers from clipped polygon to slivers polygon - tgPolygonFindSlivers( clipped, slivers ); -#endif - - // add the sliverless result polygon to the clipped polys list - if ( clipped.contours() > 0 ) { - TGShape shape; - - // copy all of the superpolys and texparams - shape.SetMask( clipped ); - shape.textured = polys_in.get_textured( i, j ); - shape.id = polys_in.get_shape( i, j ).id; - - shape.area= polys_in.get_shape( i, j ).area; - shape.sps = polys_in.get_shape( i, j ).sps; - shape.tps = polys_in.get_shape( i, j ).tps; - - // shape.sps.push_back( sp ); - polys_clipped.add_shape( i, shape ); - - if ( debug_area || debug_shape ) { - WriteDebugShape( "clipped", shape ); - } - } - } - -#if USE_ACCUMULATOR - if ( debug_shape ) { - tgPolygonAddToClipperAccumulator( tmp, true ); - } else { - tgPolygonAddToClipperAccumulator( tmp, false ); - } -#else - accum = tgPolygonUnionClipper( tmp, accum ); -#endif - - if ( debug_area || debug_shape ) { - char layer[32]; - char name[32]; - sprintf(layer, "post_clip_accum_%d_%d", accum_idx, polys_in.get_shape( i, j ).id ); - sprintf(name, "shape_accum %d,%d", i,j); -#if USE_ACCUMULATOR - tgPolygonDumpAccumulator( ds_name, layer, name ); -#else - WriteDebugPoly( layer, name, accum ); -#endif - } - - accum_idx++; - } - } - - if ( debug_all || debug_shapes.size() || debug_areas.size() ) { - // Dump the sliver list - WriteDebugPolys( "poly_slivers", slivers ); - } - -#if FIND_SLIVERS - // Now, merge any slivers with clipped polys - merge_slivers(polys_clipped, slivers); -#endif - - slivers.clear(); - - // finally, what ever is left over goes to ocean -#if USE_ACCUMULATOR - remains = tgPolygonDiffClipperWithAccumulator( safety_base ); -#else - remains = tgPolygonDiffClipper( safety_base, accum ); -#endif - - if ( remains.contours() > 0 ) { - // cout << "remains contours = " << remains.contours() << endl; - // move slivers from remains polygon to slivers polygon - -#if FIND_SLIVERS - tgPolygonFindSlivers( remains, slivers ); -#endif - // cout << " After sliver move:" << endl; - // cout << " remains = " << remains.contours() << endl; - // cout << " slivers = " << slivers.contours() << endl; - -#if FIND_SLIVERS - // merge any slivers with previously clipped - // neighboring polygons - if ( slivers.size() > 0 ) { - - if ( debug_all || debug_shapes.size() || debug_areas.size() ) { - // Dump the sliver list - WriteDebugPolys( "remains_slivers", slivers ); - } - - merge_slivers(polys_clipped, slivers); - } -#endif - - if ( remains.contours() > 0 ) { - TGSuperPoly sp; - TGShape shape; - - string material = get_area_name(get_sliver_target_area_type()); - - sp.set_material( material ); - sp.set_poly( remains ); - shape.SetMask( remains ); - shape.textured = false; - shape.sps.push_back( sp ); - - polys_clipped.add_shape( (int)get_sliver_target_area_type(), shape ); - } - } - -#if USE_ACCUMULATOR - - tgPolygonFreeClipperAccumulator(); - -#endif - - // Once clipping is complete, intersect the individual segments with their clip masks - for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { - for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - SG_LOG( SG_CLIPPER, SG_INFO, "Seperating segments from clip mask for " << get_area_name( (AreaType)area ) << ":" << shape+1 << " of " << polys_clipped.area_size(area) ); - polys_clipped.get_shape(area, shape).IntersectPolys(); - } - } - - // Now make sure any newly added intersection nodes are added to the tgnodes - for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { - for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - for (unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++) { - TGPolygon poly = polys_clipped.get_poly( area, shape, segment ); - - SG_LOG( SG_CLIPPER, SG_INFO, "Collecting nodes for " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); - - for (int con=0; con < poly.contours(); con++) { - for (int node = 0; node < poly.contour_size( con ); node++) { - // ensure we have all nodes... - nodes.unique_add( poly.get_pt( con, node ) ); - } - } - } - } - } - - return true; -} - -bool TGNodesSortByLon( const TGNode& n1, const TGNode& n2 ) -{ - return ( n1.GetPosition().x() < n2.GetPosition().x() ); -} - -// TODO : Add to TGNodes class -#if 0 - -static void dump_lat_nodes( TGConstruct& c, double lat ) { - node_list all_nodes = c.get_nodes()->get_node_list(); - node_list sorted_nodes; - for (unsigned int i=0; i= 0) { - tri_nodes.add_node( tri, idx ); - } else { - exit(0); - } - } - } - polys_clipped.set_tri_idxs(area, shape, segment, tri_nodes); - } - } - } -} - -void TGConstruct::LookupFacesPerNode( void ) -{ - SG_LOG(SG_GENERAL, SG_ALERT, "LookupFacesPerNode"); - - // Add each face that includes a node to the node's face list - for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { - for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon tris = polys_clipped.get_tris(area, shape, segment); - - for (int tri=0; tri < tris.contours(); tri++) { - for (int sub = 0; sub < tris.contour_size(tri); sub++) { - int n = nodes.find( tris.get_pt( tri, sub ) ); - nodes.AddFace( n, area, shape, segment, tri ); - } - } - } - } - } -} - -double TGConstruct::calc_tri_area( int_list& triangle_nodes ) { - Point3D p1 = nodes.get_node( triangle_nodes[0] ).GetPosition(); - Point3D p2 = nodes.get_node( triangle_nodes[1] ).GetPosition(); - Point3D p3 = nodes.get_node( triangle_nodes[2] ).GetPosition(); - - return triangle_area( p1, p2, p3 ); -} - -static SGVec3d calc_normal( double area, Point3D p1, Point3D p2, Point3D p3 ) { - SGVec3d v1, v2, normal; - - // do some sanity checking. With the introduction of landuse - // areas, we can get some long skinny triangles that blow up our - // "normal" calculations here. Let's check for really small - // triangle areas and check if one dimension of the triangle - // coordinates is nearly coincident. If so, assign the "default" - // normal of straight up. - - bool degenerate = false; - const double area_eps = 1.0e-12; - if ( area < area_eps ) { - degenerate = true; - } - - if ( fabs(p1.x() - p2.x()) < SG_EPSILON && fabs(p1.x() - p3.x()) < SG_EPSILON ) { - degenerate = true; - } - if ( fabs(p1.y() - p2.y()) < SG_EPSILON && fabs(p1.y() - p3.y()) < SG_EPSILON ) { - degenerate = true; - } - if ( fabs(p1.z() - p2.z()) < SG_EPSILON && fabs(p1.z() - p3.z()) < SG_EPSILON ) { - degenerate = true; - } - - if ( degenerate ) { - normal = normalize(SGVec3d(p1.x(), p1.y(), p1.z())); - } else { - v1[0] = p2.x() - p1.x(); - v1[1] = p2.y() - p1.y(); - v1[2] = p2.z() - p1.z(); - v2[0] = p3.x() - p1.x(); - v2[1] = p3.y() - p1.y(); - v2[2] = p3.z() - p1.z(); - normal = normalize(cross(v1, v2)); - } - - return normal; -} - -void TGConstruct::calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ) { - // for each face in the superpoly, calculate a face normal - SGVec3d normal; - TGPolyNodes tri_nodes = sp.get_tri_idxs(); - int_list face_nodes; - double_list face_areas; - point_list face_normals; - double area; - - face_normals.clear(); - face_areas.clear(); - - for (int i=0; iface_areas.size(); - for ( int j = 0; j < num_faces; j++ ) { - normal = neighbor_faces->face_normals[j]; - face_area = neighbor_faces->face_areas[j]; - SG_LOG(SG_GENERAL, SG_ALERT, "\tAdding face area " << face_area << " and normal " << normal << " to node " << node.GetPosition() ); - - normal *= face_area; - total_area += face_area; - average += normal; - } - } - - average /= total_area; - nodes.SetNormal( i, average ); - } -} - -void TGConstruct::TesselatePolys( void ) -{ - // tesselate the polygons and prepair them for final output - point_list poly_extra; - Point3D min, max; - - for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { - for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - unsigned int id = polys_clipped.get_shape( area, shape ).id; - - if ( IsDebugShape( id ) ) { - char layer[32]; - sprintf( layer, " pretess-%d", id ); - WriteDebugShape( layer, polys_clipped.get_shape(area, shape) ); - } - - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon poly = polys_clipped.get_poly(area, shape, segment); - - poly.get_bounding_box(min, max); - poly_extra = nodes.get_geod_inside( min, max ); - - SG_LOG( SG_CLIPPER, SG_INFO, "Tesselating " << get_area_name( (AreaType)area ) << "(" << area << "): " << - shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) << - ": id = " << id ); - - if ( IsDebugShape( id ) ) { - SG_LOG( SG_CLIPPER, SG_INFO, std::setprecision(12) << std::fixed << poly ); - } - - TGPolygon tri = polygon_tesselate_alt_with_extra_cgal( poly, poly_extra, false ); - - // ensure all added nodes are accounted for - for (int k=0; k< tri.contours(); k++) { - for (int l = 0; l < tri.contour_size(k); l++) { - // ensure we have all nodes... - nodes.unique_add( tri.get_pt( k, l ) ); - } - } - - // Save the triangulation - polys_clipped.set_tris( area, shape, segment, tri ); - } - } - } -} - -void TGConstruct::WriteBtgFile( void ) -{ - TGTriNodes normals, texcoords; - normals.clear(); - texcoords.clear(); - - group_list pts_v; pts_v.clear(); - group_list pts_n; pts_n.clear(); - string_list pt_materials; pt_materials.clear(); - - group_list tris_v; tris_v.clear(); - group_list tris_n; tris_n.clear(); - group_list tris_tc; tris_tc.clear(); - string_list tri_materials; tri_materials.clear(); - - group_list strips_v; strips_v.clear(); - group_list strips_n; strips_n.clear(); - group_list strips_tc; strips_tc.clear(); - string_list strip_materials; strip_materials.clear(); - - int index; - int_list pt_v, tri_v, strip_v; - int_list pt_n, tri_n, strip_n; - int_list tri_tc, strip_tc; - - for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { - unsigned int area_tris; - - // only tesselate non holes - if ( !is_hole_area( area ) ) { - area_tris = 0; - - for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - SG_LOG( SG_CLIPPER, SG_INFO, "Ouput nodes for " << get_area_name( (AreaType)area ) << ":" << - shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); - - TGPolyNodes tri_nodes = polys_clipped.get_tri_idxs(area, shape, segment); - TGPolygon tri_txs = polys_clipped.get_texcoords(area, shape, segment); - string material; - - for (int k = 0; k < tri_nodes.contours(); ++k) { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - for (int l = 0; l < tri_nodes.contour_size(k); ++l) { - index = tri_nodes.get_pt( k, l ); - tri_v.push_back( index ); - - // add the node's normal - index = normals.unique_add( nodes.GetNormal( index ) ); - tri_n.push_back( index ); - - Point3D tc = tri_txs.get_pt( k, l ); - index = texcoords.unique_add( tc ); - tri_tc.push_back( index ); - } - tris_v.push_back( tri_v ); - tris_n.push_back( tri_n ); - tris_tc.push_back( tri_tc ); - - switch ( area_tris / 32768 ) { - case 0: - material = polys_clipped.get_material(area, shape, segment); - break; - - case 1: - material = polys_clipped.get_material(area, shape, segment) + "_1"; - break; - - case 2: - material = polys_clipped.get_material(area, shape, segment) + "_2"; - break; - - case 3: - material = polys_clipped.get_material(area, shape, segment) + "_3"; - break; - } - - tri_materials.push_back( material ); - - area_tris++; - } - } - } - } - } - - std::vector< SGVec3d > wgs84_nodes = nodes.get_wgs84_nodes_as_SGVec3d(); - SGSphered d; - - for (int i = 0; i < (int)wgs84_nodes.size(); ++i) - { - d.expandBy(wgs84_nodes[ i ]); - } - - SGVec3d gbs_center = d.getCenter(); - double gbs_radius = d.getRadius(); - SG_LOG(SG_GENERAL, SG_DEBUG, "gbs center = " << gbs_center); - SG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping"); - SG_LOG(SG_GENERAL, SG_DEBUG, " center = " << gbs_center << " radius = " << gbs_radius ); - - // null structures - group_list fans_v; fans_v.clear(); - group_list fans_n; fans_n.clear(); - group_list fans_tc; fans_tc.clear(); - string_list fan_materials; fan_materials.clear(); - - string base = output_base; - string binname = bucket.gen_index_str(); - binname += ".btg"; - string txtname = bucket.gen_index_str(); - txtname += ".txt"; - - std::vector< SGVec3f > normals_3f; - for (int i=0; i < (int)normals.get_node_list().size(); i++ ) - { - Point3D node = normals.get_node_list()[i]; - normals_3f.push_back( node.toSGVec3f() ); - } - - std::vector< SGVec2f > texcoords_2f; - for (int i=0; i < (int)texcoords.get_node_list().size(); i++ ) - { - Point3D node = texcoords.get_node_list()[i]; - texcoords_2f.push_back( node.toSGVec2f() ); - } - - SGBinObject obj; - - obj.set_gbs_center( gbs_center ); - obj.set_gbs_radius( gbs_radius ); - obj.set_wgs84_nodes( wgs84_nodes ); - obj.set_normals( normals_3f ); - obj.set_texcoords( texcoords_2f ); - obj.set_pts_v( pts_v ); - obj.set_pts_n( pts_n ); - obj.set_pt_materials( pt_materials ); - obj.set_tris_v( tris_v ); - obj.set_tris_n( tris_n ); - obj.set_tris_tc( tris_tc ); - obj.set_tri_materials( tri_materials ); - obj.set_strips_v( strips_v ); - obj.set_strips_n( strips_n ); - obj.set_strips_tc( strips_tc ); - obj.set_strip_materials( strip_materials ); - obj.set_fans_v( fans_v ); - obj.set_fans_n( fans_n ); - obj.set_fans_tc( fans_tc ); - obj.set_fan_materials( fan_materials ); - - bool result; - result = obj.write_bin( base, binname, bucket ); - if ( !result ) - { - throw sg_exception("error writing file. :-("); - } - if (debug_all || debug_shapes.size() || debug_areas.size() ) - { - result = obj.write_ascii( base, txtname, bucket ); - if ( !result ) - { - throw sg_exception("error writing file. :-("); - } - } -} - -void TGConstruct::CleanClippedPolys() { - - // Clean the polys - for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { - for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - unsigned int id = polys_clipped.get_shape( area, shape ).id; - - // step 1 : snap - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon poly = polys_clipped.get_poly(area, shape, segment); - poly = snap(poly, gSnap); - polys_clipped.set_poly( area, shape, segment, poly ); - } - - if ( IsDebugShape( id ) ) { - WriteDebugShape( "snapped", polys_clipped.get_shape( area, shape ) ); - } - - // step 2 : remove_dups - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon poly = polys_clipped.get_poly(area, shape, segment); - poly = remove_dups( poly ); - polys_clipped.set_poly( area, shape, segment, poly ); - } - - if ( IsDebugShape( id ) ) { - WriteDebugShape( "rem dupes", polys_clipped.get_shape( area, shape ) ); - } - - // step 3 : remove_bad_contours - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon poly = polys_clipped.get_poly(area, shape, segment); - poly = remove_bad_contours( poly ); - polys_clipped.set_poly( area, shape, segment, poly ); - } - - if ( IsDebugShape( id ) ) { - WriteDebugShape( "rem bad contours", polys_clipped.get_shape( area, shape ) ); - } - -// todo - add up all segments in a shape for printout -#if 0 - after = poly.total_size(); - if (before != after) { - SG_LOG( SG_CLIPPER, SG_INFO, "Cleanined poly " << get_area_name( (AreaType)area ) << - ":" << shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) << " before: " << before << " after: " << after ); - } -#endif - - } - } -} - -void TGConstruct::AverageEdgeElevations( void ) -{ - for ( unsigned int i = 0; i < neighbor_faces.size(); i++ ) { - TGNeighborFaces faces = neighbor_faces[i]; - double elevation = 0.0; - unsigned int num_elevations = faces.elevations.size(); - - for ( unsigned int j = 0; j < num_elevations; j++ ) { - elevation += faces.elevations[j]; - } - - elevation = elevation / num_elevations; - - /* Find this node, and update it's elevation */ - int idx = nodes.find( faces.node ); - TGNode node = nodes.get_node( idx ); - - if ( !node.GetFixedPosition() ) { - // set elevation as the average between all tiles that have it - nodes.SetElevation( idx, elevation ); - } - } -} - -void TGConstruct::CalcTextureCoordinates( void ) -{ - for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { - for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { - for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { - TGPolygon poly = polys_clipped.get_poly(area, shape, segment); - SG_LOG( SG_CLIPPER, SG_INFO, "Texturing " << get_area_name( (AreaType)area ) << "(" << area << "): " << - shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); - - TGPolygon tri = polys_clipped.get_tris( area, shape, segment ); - TGPolygon tc; - - if ( polys_clipped.get_textured( area, shape ) ) { - SG_LOG(SG_GENERAL, SG_DEBUG, "USE TEXTURE PARAMS for tex coord calculations" ); - tc = linear_tex_coords( tri, polys_clipped.get_texparams(area, shape, segment) ); - } else { - SG_LOG(SG_GENERAL, SG_DEBUG, "USE SIMGEAR for tex coord calculations" ); - tc = area_tex_coords( tri ); - } - polys_clipped.set_texcoords( area, shape, segment, tc ); - } - } - } -} - -void TGConstruct::SaveSharedEdgeData( int stage ) -{ - string dir; - string file; - - switch( stage ) { - case 1: - { - point_list north, south, east, west; - int nCount; - - nodes.get_geod_edge( bucket, north, south, east, west ); - - dir = share_base + "/stage1/" + bucket.gen_base_path(); - - SGPath sgp( dir ); - sgp.append( "dummy" ); - sgp.create_dir( 0755 ); - - file = dir + "/" + bucket.gen_index_str() + "_edges"; - std::ofstream ofs_e( file.c_str() ); - - // first, set the precision - ofs_e << std::setprecision(12); - ofs_e << std::fixed; - - // north - nCount = north.size(); - ofs_e << nCount << "\n"; - for (int i=0; i> count; - - for (int i=0; i> node; - - // look to see if we already have this node - // If we do, (it's a corner) add more faces to it. - // otherwise, initialize it with our elevation data - pFaces = FindNeighborFaces( node ); - if ( !pFaces ) { - pFaces = AddNeighborFaces( node ); - - // new face - let's add our elevation first - int idx = nodes.find( node ); - if (idx >= 0) { - TGNode local = nodes.get_node( idx ); - pFaces->elevations.push_back( local.GetPosition().z() ); - } - } - - // remember all of the elevation data for the node, so we can average - pFaces->elevations.push_back( node.z() ); - - in >> num_faces; - for (int j=0; j> area; - pFaces->face_areas.push_back( area ); - - in >> normal; - pFaces->face_normals.push_back( normal ); - } - } -} - -void TGConstruct::SaveSharedEdgeDataStage2( void ) -{ - string dir; - string file; - point_list north, south, east, west; - std::ofstream ofs_e; - int nCount; - - nodes.get_geod_edge( bucket, north, south, east, west ); - - dir = share_base + "/stage2/" + bucket.gen_base_path(); - - SGPath sgp( dir ); - sgp.append( "dummy" ); - sgp.create_dir( 0755 ); - - - // north edge - file = dir + "/" + bucket.gen_index_str() + "_north_edge"; - ofs_e.open( file.c_str() ); - ofs_e << std::setprecision(12); - ofs_e << std::fixed; - - nCount = north.size(); - ofs_e << nCount << "\n"; - for (int i=0; i> nCount; - SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " north boundary"); - for (int i=0; i> pt; - north.push_back(pt); - } - - // South - ifs_edges >> nCount; - SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " south boundary"); - for (int i=0; i> pt; - south.push_back(pt); - } - - // East - ifs_edges >> nCount; - SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " east boundary"); - for (int i=0; i> pt; - east.push_back(pt); - } - - // West - ifs_edges >> nCount; - SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " west boundary"); - for (int i=0; i> pt; - west.push_back(pt); - } - - ifs_edges.close(); - } -} - -void TGConstruct::LoadSharedEdgeData( int stage ) -{ - switch( stage ) { - case 1: - { - // we need to read just 4 buckets for stage 1 - 1 for each edge - point_list north, south, east, west; - SGBucket nb, sb, eb, wb; - double clon = bucket.get_center_lon(); - double clat = bucket.get_center_lat(); - - // Read North tile and add its southern nodes - nb = sgBucketOffset(clon, clat, 0, 1); - LoadNeighboorEdgeDataStage1( nb, north, south, east, west ); - // Add southern nodes from northern tile - for (unsigned int i=0; i> polys_clipped; - ifs_cp.close(); - - file = dir + "/" + bucket.gen_index_str() + "_nodes"; - - std::ifstream ifs_n( file.c_str() ); - ifs_n >> nodes; - ifs_n.close(); - break; - } - - case 2: // Load the clipped polys and node list - { - dir = share_base + "/stage2/" + bucket.gen_base_path(); - file = dir + "/" + bucket.gen_index_str() + "_clipped_polys"; - - std::ifstream ifs_cp( file.c_str() ); - ifs_cp >> polys_clipped; - ifs_cp.close(); - - file = dir + "/" + bucket.gen_index_str() + "_nodes"; - - std::ifstream ifs_n( file.c_str() ); - ifs_n >> nodes; - ifs_n.close(); - break; - } - } -} - -// master construction routine -// TODO : Split each step into its own function, and move -// into seperate files by major functionality -// loading, clipping, tesselating, normals, and output -// Also, we are still calculating some thing more than one -// (like face area - need to move this into superpoly ) -void TGConstruct::ConstructBucketStage1() { - - SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); - - /* If we have some debug IDs, create a datasource */ - if ( debug_shapes.size() || debug_all || debug_areas.size() ) { - sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); - SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); - } else { - strcpy( ds_name, "" ); - } - - // STEP 1) - // Load grid of elevation data (Array) - LoadElevationArray(); - - // STEP 2) - // Clip 2D polygons against one another - if ( LoadLandclassPolys() == 0 ) { - // don't build the tile if there is no 2d data ... it *must* - // be ocean and the sim can build the tile on the fly. - return; - } - - // STEP 3) - // Load the land use polygons if the --cover option was specified - if ( get_cover().size() > 0 ) { - load_landcover(); - } - - // STEP 4) - // Clip the Landclass polygons - ClipLandclassPolys(); - - // STEP 5) - // Clean the polys - after this, we shouldn't change their shape (other than slightly for - // fix T-Junctions - as This is the end of the first pass for multicore design - CleanClippedPolys(); - - // STEP 6) - // Save the tile boundary info for stage 2 (just x,y coords of points on the boundary) - SaveSharedEdgeData( 1 ); -} - -void TGConstruct::ConstructBucketStage2() { - - SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); - - /* If we have some debug IDs, create a datasource */ - if ( debug_shapes.size() || debug_all ) { - sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); - SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); - } else { - strcpy( ds_name, "" ); - } - - // STEP 7) - // Need the array of elevation data for stage 2 - LoadElevationArray(); - - // STEP 6) - // Merge in Shared data - should just be x,y nodes on the borders from stage1 - LoadSharedEdgeData( 1 ); - - // STEP 7) - // Fix T-Junctions by finding nodes that lie close to polygon edges, and - // inserting them into the edge - FixTJunctions(); - - // STEP 8) - // Generate triangles - we can't generate the node-face lookup table - // until all polys are tesselated, as extra nodes can still be generated - TesselatePolys(); - - // STEP 9) - // Generate triangle vertex coordinates to node index lists - // NOTE: After this point, no new nodes can be added - LookupNodesPerVertex(); - - // STEP 10) - // Interpolate elevations, and flatten stuff - CalcElevations(); - - // STEP 11) - // Generate face_connected list - LookupFacesPerNode(); - - // STEP 12) - // Save the tile boundary info for stage 3 - // includes elevation info, and a list of connected triangles - SaveSharedEdgeData( 2 ); -} - -void TGConstruct::ConstructBucketStage3() { - - SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); - - /* If we have some debug IDs, create a datasource */ - if ( debug_shapes.size() || debug_all ) { - sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); - SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); - } else { - strcpy( ds_name, "" ); - } - - // Load in the neighbor faces and elevation data - LoadSharedEdgeDataStage2(); - - // STEP 12) - // Average out the elevation for nodes on tile boundaries - AverageEdgeElevations(); - - // STEP 12) - // Calculate Face Normals - CalcFaceNormals(); - - // STEP 13) - // Calculate Point Normals - CalcPointNormals(); - -#if 0 - if ( c.get_cover().size() > 0 ) { - // Now for all the remaining "default" land cover polygons, assign - // each one it's proper type from the land use/land cover - // database. - fix_land_cover_assignments( c ); - } -#endif - - // STEP 14) - // Calculate Texture Coordinates - CalcTextureCoordinates(); - - // STEP 16) - // Generate the btg file - WriteBtgFile(); - - // STEP 17) - // Write Custom objects to .stg file - AddCustomObjects(); -} diff --git a/src/BuildTiles/Main/construct.hxx b/src/BuildTiles/Main/construct.hxx deleted file mode 100644 index d5240e63..00000000 --- a/src/BuildTiles/Main/construct.hxx +++ /dev/null @@ -1,567 +0,0 @@ -// construct.hxx -- Class to manage the primary data used in the -// construction process -// -// Written by Curtis Olson, started May 1999. -// -// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id: construct.hxx,v 1.13 2004-11-19 22:25:49 curt Exp $ - - -#ifndef _CONSTRUCT_HXX -#define _CONSTRUCT_HXX - - -#ifndef __cplusplus -# error This library requires C++ -#endif - - -#define TG_MAX_AREA_TYPES 128 - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include "priorities.hxx" - -#define FIND_SLIVERS (1) -#define USE_ACCUMULATOR (1) - - - - -class TGShape -{ -public: - TGPolygon clip_mask; - bool textured; - superpoly_list sps; - texparams_list tps; - AreaType area; - unsigned int id; - - void GetName( char* name ) const - { - sprintf( name, "%s_%d", get_area_name( (AreaType)area ).c_str(), id ); - } - - void SetMask( TGPolygon mask ) - { - clip_mask = mask; - } - - void BuildMask( void ) - { - TGPolygon poly; - clip_mask.erase(); - - for (unsigned int i=0; i 1 ) { - TGPolygon original, intersect; - - for (unsigned int i=0; i> ( std::istream&, TGShape& ); - friend std::ostream& operator<< ( std::ostream&, const TGShape& ); -}; - -// input from stream -inline std::istream& operator >> ( std::istream& in, TGShape& p) -{ - int i, count; - - // First, load the clipmask - in >> p.clip_mask; - - // Then load superpolys - in >> count; - for (i=0; i> sp; - p.sps.push_back( sp ); - } - - // Then load texparams - in >> count; - for (i=0; i> tp; - p.tps.push_back( tp ); - } - - // Load the id, area type and textured flag - in >> p.id; - in >> p.area; - in >> p.textured; - - return in; -} - -inline std::ostream& operator<< ( std::ostream& out, const TGShape& p ) -{ - int i, count; - TGSuperPoly sp; - TGTexParams tp; - - // First, save the clipmask - out << p.clip_mask; - - // Then save superpolys - count = p.sps.size(); - out << count << "\n"; - for (i=0; i shape_list; -typedef shape_list::iterator shape_list_iterator; -typedef shape_list::const_iterator const_shape_list_iterator; - - - - - - - -class TGLandclass -{ -public: - void clear(void) - { - int i; - - for (i=0; i> ( std::istream&, TGLandclass& ); - friend std::ostream& operator<< ( std::ostream&, const TGLandclass& ); - -private: - shape_list shapes[TG_MAX_AREA_TYPES]; -}; - -// input from stream -inline std::istream& operator >> ( std::istream& in, TGLandclass& lc) -{ - int i, j, count; - - // Load all landclass shapes - for (i=0; i> count; - - for (j=0; j> shape; - lc.shapes[i].push_back( shape ); - } - } - - return in; -} - -inline std::ostream& operator<< ( std::ostream& out, const TGLandclass& lc ) -{ - int i, j, count; - TGShape shape; - - // Save all landclass shapes - for (i=0; i neighbor_face_list; -typedef neighbor_face_list::iterator neighbor_face_list_iterator; -typedef neighbor_face_list::const_iterator const_neighbor_face_list_iterator; - - -class TGConstruct { - -private: - - // path to land-cover file (if any) - std::string cover; - - // paths - std::string work_base; - std::string output_base; - std::string share_base; - - std::vector load_dirs; - - // flag indicating whether to align texture coords within the UK - // with the UK grid - bool useUKGrid; - - // flag indicating whether to ignore the landmass - bool ignoreLandmass; - - // I think we should remove this - double nudge; - - // path to the debug shapes - std::string debug_path; - - bool debug_all; - - // list of shapes to dump during debug - std::vector debug_areas; - std::vector debug_shapes; - - // OGR encode variables - // For debug: - void* ds_id; // If we are going to build shapefiles - void* l_id; // datasource and layer IDs - char ds_name[128]; - char layer_name[128]; - char feature_name[128]; - - // this bucket - SGBucket bucket; - - // Elevation data - TGArray array; - - // land class polygons - TGLandclass polys_in; - TGLandclass polys_clipped; - - // All Nodes - TGNodes nodes; - - // Neighbor Faces - neighbor_face_list neighbor_faces; - -private: - // Load Data - void LoadElevationArray( void ); - int LoadLandclassPolys( void ); - // Load Data Helpers - bool load_poly(const std::string& path); - bool load_osgb36_poly(const std::string& path); - void add_poly(int area, const TGPolygon &poly, std::string material); - - // Clip Data - bool ClipLandclassPolys( void ); - // Clip Helpers - void move_slivers( TGPolygon& in, TGPolygon& out ); - void merge_slivers( TGLandclass& clipped, poly_list& slivers_list ); - - // Shared edge Matching - void SaveSharedEdgeDataStage2( void ); - void LoadSharedEdgeDataStage2( void ); - void WriteSharedEdgeNeighboorFaces( std::ofstream& ofs_e, Point3D pt ); - void LoadSharedEdgeData( int stage ); - void LoadNeighboorEdgeDataStage1( SGBucket& b, point_list& north, point_list& south, point_list& east, point_list& west ); - - void SaveSharedEdgeData( int stage ); - - void ReadNeighborFaces( std::ifstream& ifs_e ); - void WriteNeighborFaces( std::ofstream& ofs_e, Point3D pt ); - TGNeighborFaces* AddNeighborFaces( Point3D node ); - TGNeighborFaces* FindNeighborFaces( Point3D node ); - - // Polygon Cleaning - void CleanClippedPolys( void ); - void FixTJunctions( void ); - - // Tesselation - void TesselatePolys( void ); - - // Elevation and Flattening - void CalcElevations( void ); - void AverageEdgeElevations( void ); - - // Normals and texture coords - void LookupNodesPerVertex( void ); - void LookupFacesPerNode( void ); - void CalcFaceNormals( void ); - void CalcPointNormals( void ); - void CalcTextureCoordinates( void ); - // Helpers - TGPolygon linear_tex_coords( const TGPolygon& tri, const TGTexParams& tp ); - TGPolygon area_tex_coords( const TGPolygon& tri ); - - // Output - void WriteBtgFile( void ); - void AddCustomObjects( void ); - - // Misc - void calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ); - double calc_tri_area( int_list& triangle_nodes ); - - // debug - bool IsDebugShape( unsigned int id ); - bool IsDebugArea( unsigned int area ); - - void WriteDebugShape( const char* layer_name, const TGShape& shape ); - void WriteDebugPoly( const char* layer_name, const char* name, const TGPolygon& poly ); - void WriteDebugPolys( const char* layer_name, const poly_list& polys ); - -public: - // Constructor - TGConstruct(); - - // Destructor - ~TGConstruct(); - - void set_bucket( SGBucket b ) { bucket = b; } - - // New shared edge matching - void SaveToIntermediateFiles( int stage ); - void LoadFromIntermediateFiles( int stage ); - - // Three stage construct - void ConstructBucketStage1(); - void ConstructBucketStage2(); - void ConstructBucketStage3(); - - void calc_gc_course_dist( const Point3D& start, const Point3D& dest, - double *course, double *dist ); - double distanceSphere( const Point3D p1, const Point3D p2 ); - - int load_landcover (); - void add_to_polys( TGPolygon &accum, const TGPolygon &poly); - double measure_roughness( TGPolygon &poly ); - AreaType get_landcover_type (const LandCover &cover, double xpos, double ypos, double dx, double dy); - void make_area( const LandCover &cover, TGPolygon *polys, - double x1, double y1, double x2, double y2, - double half_dx, double half_dy ); - - // land cover file - inline std::string get_cover () const { return cover; } - inline void set_cover (const std::string &s) { cover = s; } - - // paths - void set_paths( const std::string work, const std::string share, const std::string output, const std::vector load_dirs ); - -#if 0 - inline std::string get_work_base() const { return work_base; } - inline void set_work_base( const std::string s ) { work_base = s; } - inline std::string get_output_base() const { return output_base; } - inline void set_output_base( const std::string s ) { output_base = s; } - inline std::string get_share_base() const { return share_base; } - inline void set_share_base( const std::string s ) { share_base = s; } - inline void set_load_dirs( const std::vector ld ) { load_dirs = ld; } -#endif - - void set_options( bool uk_grid, bool ignore_lm, double n ); - -#if 0 - // UK grid flag - inline bool get_useUKGrid() const { return useUKGrid; } - inline void set_useUKGrid( const bool b ) { useUKGrid = b; } - - // Nudge - inline void set_nudge( double n ) { nudge = n; } - - // ignore landmass flag - inline void set_ignore_landmass( const bool b) { ignoreLandmass = b; } -#endif - - // TODO : REMOVE - inline TGNodes* get_nodes() { return &nodes; } - - // node list in geodetic coords (with fixed elevation) - inline point_list get_geod_nodes() const { return nodes.get_geod_nodes(); } - - // normal list (for each point) in cart coords (for smooth - // shading) - inline point_list get_point_normals() const { return nodes.get_normals(); } - - // Debug - void set_debug( std::string path, std::vector area_defs, std::vector shape_defs ); -}; - - -#endif // _CONSTRUCT_HXX diff --git a/src/BuildTiles/Main/main.cxx b/src/BuildTiles/Main/main.cxx index 79372261..9cc0ec7e 100644 --- a/src/BuildTiles/Main/main.cxx +++ b/src/BuildTiles/Main/main.cxx @@ -29,36 +29,36 @@ # include #endif -#include +//#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include -#include -#include +//#include +//#include #include -#include -#include -#include +//#include +//#include +//#include -#include -#include +//#include +//#include -#include +//#include #include -#include +//#include -#include "construct.hxx" +#include "tgconstruct.hxx" #include "usgs.hxx" using std::string; using std::vector; -using namespace std; +//using namespace std; vector load_dirs; double nudge=0.0; @@ -327,7 +327,7 @@ int main(int argc, char **argv) { stage1->set_paths( work_dir, share_dir, output_dir, load_dirs ); stage1->set_options( useUKgrid, ignoreLandmass, nudge ); stage1->set_bucket( b_cur ); - stage1->set_debug( debug_dir, debug_defs ); + stage1->set_debug( debug_dir, debug_area_defs, debug_shape_defs ); stage1->ConstructBucketStage1(); stage1->SaveToIntermediateFiles(1); diff --git a/src/BuildTiles/Main/tgconstruct.cxx b/src/BuildTiles/Main/tgconstruct.cxx new file mode 100644 index 00000000..557d14b9 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct.cxx @@ -0,0 +1,235 @@ +// construct.cxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +//#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include +//#include + +//#include +//#include + +#include "tgconstruct.hxx" +//#include "usgs.hxx" + +//using std::string; + +// Constructor +TGConstruct::TGConstruct(): + useUKGrid(false), + ignoreLandmass(false), + debug_all(false), + ds_id((void*)-1) +{ } + + +// Destructor +TGConstruct::~TGConstruct() { + array.close(); + + // land class polygons + polys_in.clear(); + polys_clipped.clear(); + + // All Nodes + nodes.clear(); +} + +// TGConstruct: Setup +void TGConstruct::set_paths( const std::string work, const std::string share, const std::string output, const std::vector load ) { + work_base = work; + share_base = share; + output_base = output; + load_dirs = load; +} + +void TGConstruct::set_options( bool uk_grid, bool ignore_lm, double n ) { + useUKGrid = uk_grid; + ignoreLandmass = ignore_lm; + nudge = n; +} + +// master construction routine +// TODO : Split each step into its own function, and move +// into seperate files by major functionality +// loading, clipping, tesselating, normals, and output +// Also, we are still calculating some thing more than one +// (like face area - need to move this into superpoly ) +void TGConstruct::ConstructBucketStage1() { + // First, set the precision of floating point logging: + SG_LOG(SG_GENERAL, SG_ALERT, std::setprecision(12) << std::fixed); + SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); + + /* If we have some debug IDs, create a datasource */ + if ( debug_shapes.size() || debug_all ) { + sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); + SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); + } else { + strcpy( ds_name, "" ); + } + + // STEP 1) + // Load grid of elevation data (Array) + LoadElevationArray(); + + // STEP 2) + // Clip 2D polygons against one another + if ( LoadLandclassPolys() == 0 ) { + // don't build the tile if there is no 2d data ... it *must* + // be ocean and the sim can build the tile on the fly. + return; + } + + // STEP 3) + // Load the land use polygons if the --cover option was specified + if ( get_cover().size() > 0 ) { + load_landcover(); + } + + // STEP 4) + // Clip the Landclass polygons + ClipLandclassPolys(); + + // STEP 5) + // Clean the polys - after this, we shouldn't change their shape (other than slightly for + // fix T-Junctions - as This is the end of the first pass for multicore design + CleanClippedPolys(); + + // STEP 6) + // Save the tile boundary info for stage 2 (just x,y coords of points on the boundary) + SaveSharedEdgeData( 1 ); +} + +void TGConstruct::ConstructBucketStage2() { + // First, set the precision of floating point logging: + SG_LOG(SG_GENERAL, SG_ALERT, std::setprecision(12) << std::fixed); + SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); + + /* If we have some debug IDs, create a datasource */ + if ( debug_shapes.size() || debug_all ) { + sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); + SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); + } else { + strcpy( ds_name, "" ); + } + + // STEP 7) + // Need the array of elevation data for stage 2 + LoadElevationArray(); + + // STEP 6) + // Merge in Shared data - should just be x,y nodes on the borders from stage1 + LoadSharedEdgeData( 1 ); + + // STEP 7) + // Fix T-Junctions by finding nodes that lie close to polygon edges, and + // inserting them into the edge + FixTJunctions(); + + // STEP 8) + // Generate triangles - we can't generate the node-face lookup table + // until all polys are tesselated, as extra nodes can still be generated + TesselatePolys(); + + // STEP 9) + // Generate triangle vertex coordinates to node index lists + // NOTE: After this point, no new nodes can be added + LookupNodesPerVertex(); + + // STEP 10) + // Interpolate elevations, and flatten stuff + CalcElevations(); + + // STEP 11) + // Generate face_connected list + LookupFacesPerNode(); + + // STEP 12) + // Save the tile boundary info for stage 3 + // includes elevation info, and a list of connected triangles + SaveSharedEdgeData( 2 ); +} + +void TGConstruct::ConstructBucketStage3() { + // First, set the precision of floating point logging: + SG_LOG(SG_GENERAL, SG_ALERT, std::setprecision(12) << std::fixed); + SG_LOG(SG_GENERAL, SG_ALERT, "\nConstructing tile ID " << bucket.gen_index_str() << " in " << bucket.gen_base_path() ); + + /* If we have some debug IDs, create a datasource */ + if ( debug_shapes.size() || debug_all ) { + sprintf(ds_name, "%s/constructdbg_%s", debug_path.c_str(), bucket.gen_index_str().c_str() ); + SG_LOG(SG_GENERAL, SG_ALERT, "Debug_string: " << ds_name ); + } else { + strcpy( ds_name, "" ); + } + + // Load in the neighbor faces and elevation data + LoadSharedEdgeDataStage2(); + + // STEP 12) + // Average out the elevation for nodes on tile boundaries + AverageEdgeElevations(); + + // STEP 12) + // Calculate Face Normals + CalcFaceNormals(); + + // STEP 13) + // Calculate Point Normals + CalcPointNormals(); + +#if 0 + if ( c.get_cover().size() > 0 ) { + // Now for all the remaining "default" land cover polygons, assign + // each one it's proper type from the land use/land cover + // database. + fix_land_cover_assignments( c ); + } +#endif + + // STEP 14) + // Calculate Texture Coordinates + CalcTextureCoordinates(); + + // STEP 16) + // Generate the btg file + WriteBtgFile(); + + // STEP 17) + // Write Custom objects to .stg file + AddCustomObjects(); +} \ No newline at end of file diff --git a/src/BuildTiles/Main/tgconstruct.hxx b/src/BuildTiles/Main/tgconstruct.hxx new file mode 100644 index 00000000..f29c4847 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct.hxx @@ -0,0 +1,271 @@ +// construct.hxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.hxx,v 1.13 2004-11-19 22:25:49 curt Exp $ + + +#ifndef _CONSTRUCT_HXX +#define _CONSTRUCT_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#define TG_MAX_AREA_TYPES 128 + +//#include +//#include + +//#include +//#include +//#include +//#include + +#include + +//#include +//#include +#include + +#include + +#include "tglandclass.hxx" +//#include "priorities.hxx" + +#define FIND_SLIVERS (0) +#define USE_ACCUMULATOR (1) + + +// Stage2 shared edge data +struct TGNeighborFaces { +public: + Point3D node; + + double_list elevations; // we'll take the average + double_list face_areas; + point_list face_normals; +}; + +typedef std::vector < TGNeighborFaces > neighbor_face_list; +typedef neighbor_face_list::iterator neighbor_face_list_iterator; +typedef neighbor_face_list::const_iterator const_neighbor_face_list_iterator; + + +class TGConstruct { + +private: + + // path to land-cover file (if any) + std::string cover; + + // paths + std::string work_base; + std::string output_base; + std::string share_base; + + std::vector load_dirs; + + const static double gSnap = 0.00000001; // approx 1 mm + + // flag indicating whether to align texture coords within the UK + // with the UK grid + bool useUKGrid; + + // flag indicating whether to ignore the landmass + bool ignoreLandmass; + + // I think we should remove this + double nudge; + + // path to the debug shapes + std::string debug_path; + + bool debug_all; + + // list of shapes to dump during debug + std::vector debug_areas; + std::vector debug_shapes; + + // OGR encode variables + // For debug: + void* ds_id; // If we are going to build shapefiles + void* l_id; // datasource and layer IDs + char ds_name[128]; + char layer_name[128]; + char feature_name[128]; + + // this bucket + SGBucket bucket; + + // Elevation data + TGArray array; + + // land class polygons + TGLandclass polys_in; + TGLandclass polys_clipped; + + // All Nodes + TGNodes nodes; + + // Neighbor Faces + neighbor_face_list neighbor_faces; + +private: + // Load Data + void LoadElevationArray( void ); + int LoadLandclassPolys( void ); + // Load Data Helpers + bool load_poly(const std::string& path); + bool load_osgb36_poly(const std::string& path); + void add_poly(int area, const TGPolygon &poly, std::string material); + + // Clip Data + bool ClipLandclassPolys( void ); + // Clip Helpers + void move_slivers( TGPolygon& in, TGPolygon& out ); + void merge_slivers( TGLandclass& clipped, poly_list& slivers_list ); + + // Shared edge Matching + void SaveSharedEdgeDataStage2( void ); + void LoadSharedEdgeDataStage2( void ); + void WriteSharedEdgeNeighboorFaces( std::ofstream& ofs_e, Point3D pt ); + void LoadSharedEdgeData( int stage ); + void LoadNeighboorEdgeDataStage1( SGBucket& b, point_list& north, point_list& south, point_list& east, point_list& west ); + + void SaveSharedEdgeData( int stage ); + + void ReadNeighborFaces( std::ifstream& ifs_e ); + void WriteNeighborFaces( std::ofstream& ofs_e, Point3D pt ); + TGNeighborFaces* AddNeighborFaces( Point3D node ); + TGNeighborFaces* FindNeighborFaces( Point3D node ); + + // Polygon Cleaning + void CleanClippedPolys( void ); + void FixTJunctions( void ); + + // Tesselation + void TesselatePolys( void ); + + // Elevation and Flattening + void CalcElevations( void ); + void AverageEdgeElevations( void ); + + // Normals and texture coords + void LookupNodesPerVertex( void ); + void LookupFacesPerNode( void ); + void CalcFaceNormals( void ); + void CalcPointNormals( void ); + void CalcTextureCoordinates( void ); + // Helpers + SGVec3d calc_normal( double area, Point3D p1, Point3D p2, Point3D p3 ); + TGPolygon linear_tex_coords( const TGPolygon& tri, const TGTexParams& tp ); + TGPolygon area_tex_coords( const TGPolygon& tri ); + + // Output + void WriteBtgFile( void ); + void AddCustomObjects( void ); + + // Misc + void calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ); + double calc_tri_area( int_list& triangle_nodes ); + + // debug + bool IsDebugShape( unsigned int id ); + bool IsDebugArea( unsigned int area ); + + void WriteDebugShape( const char* layer_name, const TGShape& shape ); + void WriteDebugPoly( const char* layer_name, const char* name, const TGPolygon& poly ); + void WriteDebugPolys( const char* layer_name, const poly_list& polys ); + +public: + // Constructor + TGConstruct(); + + // Destructor + ~TGConstruct(); + + void set_bucket( SGBucket b ) { bucket = b; } + + // New shared edge matching + void SaveToIntermediateFiles( int stage ); + void LoadFromIntermediateFiles( int stage ); + + // Three stage construct + void ConstructBucketStage1(); + void ConstructBucketStage2(); + void ConstructBucketStage3(); + + int load_landcover (); + double measure_roughness( TGPolygon &poly ); + AreaType get_landcover_type (const LandCover &cover, double xpos, double ypos, double dx, double dy); + void make_area( const LandCover &cover, TGPolygon *polys, + double x1, double y1, double x2, double y2, + double half_dx, double half_dy ); + + // land cover file + inline std::string get_cover () const { return cover; } + inline void set_cover (const std::string &s) { cover = s; } + + // paths + void set_paths( const std::string work, const std::string share, const std::string output, const std::vector load_dirs ); + +#if 0 + inline std::string get_work_base() const { return work_base; } + inline void set_work_base( const std::string s ) { work_base = s; } + inline std::string get_output_base() const { return output_base; } + inline void set_output_base( const std::string s ) { output_base = s; } + inline std::string get_share_base() const { return share_base; } + inline void set_share_base( const std::string s ) { share_base = s; } + inline void set_load_dirs( const std::vector ld ) { load_dirs = ld; } +#endif + + void set_options( bool uk_grid, bool ignore_lm, double n ); + +#if 0 + // UK grid flag + inline bool get_useUKGrid() const { return useUKGrid; } + inline void set_useUKGrid( const bool b ) { useUKGrid = b; } + + // Nudge + inline void set_nudge( double n ) { nudge = n; } + + // ignore landmass flag + inline void set_ignore_landmass( const bool b) { ignoreLandmass = b; } +#endif + + // TODO : REMOVE + inline TGNodes* get_nodes() { return &nodes; } + + // node list in geodetic coords (with fixed elevation) + inline point_list get_geod_nodes() const { return nodes.get_geod_nodes(); } + + // normal list (for each point) in cart coords (for smooth + // shading) + inline point_list get_point_normals() const { return nodes.get_normals(); } + + // Debug + void set_debug( std::string path, std::vector area_defs, std::vector shape_defs ); +}; + + +#endif // _CONSTRUCT_HXX diff --git a/src/BuildTiles/Main/tgconstruct_cleanup.cxx b/src/BuildTiles/Main/tgconstruct_cleanup.cxx new file mode 100644 index 00000000..65c5af9b --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_cleanup.cxx @@ -0,0 +1,210 @@ +// construct.cxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +//#include + +//#include +#include + +#include +#include + +#include "tgconstruct.hxx" + +using std::string; + +void TGConstruct::FixTJunctions( void ) { + int before, after; + + // traverse each poly, and add intermediate nodes + for ( unsigned int i = 0; i < TG_MAX_AREA_TYPES; ++i ) { + for( unsigned int j = 0; j < polys_clipped.area_size(i); ++j ) { + for( unsigned int k = 0; k < polys_clipped.shape_size(i, j); ++k ) { + TGPolygon current = polys_clipped.get_poly(i, j, k); + + before = current.total_size(); + current = add_tgnodes_to_poly( current, &nodes ); + after = current.total_size(); + + if (before != after) { + SG_LOG( SG_CLIPPER, SG_INFO, "Fixed T-Junctions in " << get_area_name( (AreaType)i ) << ":" << j+1 << "-" << k << " of " << (int)polys_clipped.area_size(i) << " nodes increased from " << before << " to " << after ); + } + + /* Save it back */ + polys_clipped.set_poly( i, j, k, current ); + } + } + } +} + +// Attempt to merge slivers into a list of polygons. +// +// For each sliver contour, see if a union with another polygon yields +// a polygon with no increased contours (i.e. the sliver is adjacent +// and can be merged.) If so, replace the clipped polygon with the +// new polygon that has the sliver merged in. +void TGConstruct::merge_slivers( TGLandclass& clipped, poly_list& slivers_list ) { + TGPolygon poly, result, slivers, sliver; + point_list contour; + int original_contours, result_contours; + bool done; + int area, shape, segment, i, j; + int merged = 0; + int total = 0; + + for ( i = 0; i < (int)slivers_list.size(); i++ ) { + slivers = slivers_list[i]; + + for ( j = 0; j < slivers.contours(); ++j ) { + // make the sliver polygon + contour = slivers.get_contour( j ); + total++; + + sliver.erase(); + sliver.add_contour( contour, 0 ); + done = false; + + for ( area = 0; area < TG_MAX_AREA_TYPES && !done; ++area ) { + if ( is_hole_area( area ) ) { + // don't merge a non-hole sliver in with a hole + continue; + } + + for ( shape = 0; shape < (int)clipped.area_size(area) && !done; ++shape ) { + unsigned int shape_id = clipped.get_shape( area, shape ).id; + + for ( segment = 0; segment < (int)clipped.shape_size(area, shape) && !done; ++segment ) { + + poly = clipped.get_poly( area, shape, segment ); + original_contours = poly.contours(); + result = tgPolygonUnion( poly, sliver ); + result_contours = result.contours(); + + if ( original_contours == result_contours ) { + SG_LOG(SG_GENERAL, SG_INFO, "MERGED SLIVER " << i << ", " << j << " into area " << get_area_name( (AreaType)area ) << " id: " << shape_id << " segment: " << segment ); + + clipped.set_poly( area, shape, segment, result ); + merged++; + + /* add the sliver to the clip_mask, too */ + TGPolygon mask = clipped.get_mask( area, shape ); + result = tgPolygonUnion( mask, sliver ); + clipped.set_mask( area, shape, result ); + + if ( IsDebugShape( shape_id ) ) { + WriteDebugShape( "with_slivers", clipped.get_shape( area, shape ) ); + } + + done = true; + } + } + } + } + } + } + + slivers_list.clear(); + + SG_LOG(SG_GENERAL, SG_INFO, " UNMERGED SLIVERS: " << total - merged ); +} + +void TGConstruct::CleanClippedPolys() { + + // Clean the polys + for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { + for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + unsigned int id = polys_clipped.get_shape( area, shape ).id; + + // step 1 : snap + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon poly = polys_clipped.get_poly(area, shape, segment); + poly = snap(poly, gSnap); + polys_clipped.set_poly( area, shape, segment, poly ); + } + + if ( IsDebugShape( id ) ) { + WriteDebugShape( "snapped", polys_clipped.get_shape( area, shape ) ); + } + + // step 2 : remove_dups + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon poly = polys_clipped.get_poly(area, shape, segment); + poly = remove_dups( poly ); + polys_clipped.set_poly( area, shape, segment, poly ); + } + + if ( IsDebugShape( id ) ) { + WriteDebugShape( "rem dupes", polys_clipped.get_shape( area, shape ) ); + } + + // step 3 : remove_bad_contours + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon poly = polys_clipped.get_poly(area, shape, segment); + poly = remove_bad_contours( poly ); + polys_clipped.set_poly( area, shape, segment, poly ); + } + + if ( IsDebugShape( id ) ) { + WriteDebugShape( "rem bad contours", polys_clipped.get_shape( area, shape ) ); + } + +// todo - add up all segments in a shape for printout +#if 0 + after = poly.total_size(); + if (before != after) { + SG_LOG( SG_CLIPPER, SG_INFO, "Cleanined poly " << get_area_name( (AreaType)area ) << + ":" << shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) << " before: " << before << " after: " << after ); + } +#endif + + } + } +} + +void TGConstruct::AverageEdgeElevations( void ) +{ + for ( unsigned int i = 0; i < neighbor_faces.size(); i++ ) { + TGNeighborFaces faces = neighbor_faces[i]; + double elevation = 0.0; + unsigned int num_elevations = faces.elevations.size(); + + for ( unsigned int j = 0; j < num_elevations; j++ ) { + elevation += faces.elevations[j]; + } + + elevation = elevation / num_elevations; + + /* Find this node, and update it's elevation */ + int idx = nodes.find( faces.node ); + TGNode node = nodes.get_node( idx ); + + if ( !node.GetFixedPosition() ) { + // set elevation as the average between all tiles that have it + nodes.SetElevation( idx, elevation ); + } + } +} \ No newline at end of file diff --git a/src/BuildTiles/Main/tgconstruct_clip.cxx b/src/BuildTiles/Main/tgconstruct_clip.cxx new file mode 100644 index 00000000..b52e87f4 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_clip.cxx @@ -0,0 +1,328 @@ +// tgconstruct_clip.cxx -- handle polygon clipping +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "tgconstruct.hxx" + +using std::string; + +bool TGConstruct::ClipLandclassPolys( void ) { + TGPolygon clipped, tmp; + TGPolygon remains; + TGPolygon safety_base; + poly_list slivers; + int i, j; + Point3D p; + point2d min, max; + +#if !USE_ACCUMULATOR + TGPolygon accum; +#endif + + // Get clip bounds + min.x = bucket.get_center_lon() - 0.5 * bucket.get_width(); + min.y = bucket.get_center_lat() - 0.5 * bucket.get_height(); + max.x = bucket.get_center_lon() + 0.5 * bucket.get_width(); + max.y = bucket.get_center_lat() + 0.5 * bucket.get_height(); + +#if USE_ACCUMULATOR + + tgPolygonInitClipperAccumulator(); + +#else + accum.erase(); +#endif + + // set up clipping tile : and remember to add the nodes! + safety_base.erase(); + + p = Point3D(min.x, min.y, -9999.0); + safety_base.add_node( 0, p ); + nodes.unique_add( p ); + + p = Point3D(max.x, min.y, -9999.0); + safety_base.add_node( 0, p ); + nodes.unique_add( p ); + + p = Point3D(max.x, max.y, -9999.0); + safety_base.add_node( 0, p ); + nodes.unique_add( p ); + + p = Point3D(min.x, max.y, -9999.0); + safety_base.add_node( 0, p ); + nodes.unique_add( p ); + + // set up land mask, we clip most things to this since it is our + // best representation of land vs. ocean. If we have other less + // accurate data that spills out into the ocean, we want to just + // clip it. + // also set up a mask for all water and islands + TGPolygon land_mask, water_mask, island_mask; + land_mask.erase(); + water_mask.erase(); + island_mask.erase(); + + for ( i = 0; i < TG_MAX_AREA_TYPES; i++ ) { + if ( is_landmass_area( i ) && !ignoreLandmass ) { + for ( unsigned int j = 0; j < polys_in.area_size(i); ++j ) { + land_mask = tgPolygonUnion( polys_in.get_mask(i, j), land_mask ); + } + + } else if ( is_water_area( i ) ) { + for (unsigned int j = 0; j < polys_in.area_size(i); j++) { + water_mask = tgPolygonUnion( polys_in.get_mask(i, j), water_mask ); + } + } else if ( is_island_area( i ) ) { + for (unsigned int j = 0; j < polys_in.area_size(i); j++) { + island_mask = tgPolygonUnion( polys_in.get_mask(i, j), island_mask ); + } + } + } + + // Dump the masks + if ( debug_all || debug_shapes.size() ) { + WriteDebugPoly( "land_mask", "", land_mask ); + WriteDebugPoly( "water_mask", "", water_mask ); + WriteDebugPoly( "island_mask", "", island_mask ); + } + + // process polygons in priority order + for ( i = 0; i < TG_MAX_AREA_TYPES; ++i ) { + for( j = 0; j < (int)polys_in.area_size(i); ++j ) { + TGPolygon current = polys_in.get_mask(i, j); + TGPolygon before; + char layer_name[64]; + + SG_LOG( SG_CLIPPER, SG_INFO, "Clipping " << get_area_name( (AreaType)i ) << "(" << i << "):" << j+1 << " of " << polys_in.area_size(i) << " id " << polys_in.get_shape( i, j ).id ); + + tmp = current; + + if ( IsDebugShape( polys_in.get_shape( i, j ).id ) ) { + char name[32]; + sprintf(name, "shape %d,%d", i,j); + sprintf(layer_name, "premask_%d_%d_%d", i, j, polys_in.get_shape( i, j ).id); + WriteDebugPoly( layer_name, name, tmp ); + + //sprintf(name, "accum %d,%d", i,j); + //WriteDebugPoly( layer_name, name, accum ); + } + + // if not a hole, clip the area to the land_mask + if ( !ignoreLandmass && !is_hole_area( i ) ) { + before = tmp; + + tmp = tgPolygonInt( tmp, land_mask ); + + if (tmp.total_size() != before.total_size()) { + SG_LOG( SG_CLIPPER, SG_INFO, "Clip w/land mask gave odd result" ); + // exit(0); + } + } + + // if a water area, cut out potential islands + if ( is_water_area( i ) ) { + // clip against island mask + tmp = tgPolygonDiff( tmp, island_mask ); + + if (tmp.total_size() != before.total_size()) { + SG_LOG( SG_CLIPPER, SG_INFO, "Clip w/island mask gave odd result" ); + // exit(0); + } + } + + if ( IsDebugShape( polys_in.get_shape( i, j ).id ) ) { + char name[32]; + sprintf(name, "shape %d,%d", i,j); + sprintf(layer_name, "preclip_shape_%d_%d_%d", i, j, polys_in.get_shape( i, j ).id); + WriteDebugPoly( layer_name, name, tmp ); + +#if !USE_ACCUMULATOR + sprintf(name, "accum %d,%d", i,j); + sprintf(layer_name, "preclip_accum_%d_%d", i, j); + WriteDebugPoly( layer_name, name, accum ); +#endif + } + + // set debug for this clip + if ( (i == 14) && ( j == 0 ) ) { + sglog().setLogLevels( SG_ALL, SG_DEBUG ); + } + +#if USE_ACCUMULATOR + clipped = tgPolygonDiffClipperWithAccumulator( tmp ); +#else + clipped = tgPolygonDiff( tmp, accum ); +#endif + + sglog().setLogLevels( SG_ALL, SG_INFO ); + + + // only add to output list if the clip left us with a polygon + if ( clipped.contours() > 0 ) { + +#if FIND_SLIVERS + // move slivers from clipped polygon to slivers polygon + tgPolygonFindSlivers( clipped, slivers ); +#endif + + // add the sliverless result polygon to the clipped polys list + if ( clipped.contours() > 0 ) { + TGShape shape; + + // copy all of the superpolys and texparams + shape.SetMask( clipped ); + shape.textured = polys_in.get_textured( i, j ); + shape.id = polys_in.get_shape( i, j ).id; + + shape.area= polys_in.get_shape( i, j ).area; + shape.sps = polys_in.get_shape( i, j ).sps; + shape.tps = polys_in.get_shape( i, j ).tps; + + // shape.sps.push_back( sp ); + polys_clipped.add_shape( i, shape ); + + if ( IsDebugShape( shape.id ) ) { + WriteDebugShape( "clipped", shape ); + } + } + } + +#if USE_ACCUMULATOR + tgPolygonAddToClipperAccumulator( tmp, false ); +#else + accum = tgPolygonUnion( tmp, accum ); +#endif + + if ( IsDebugShape( polys_in.get_shape( i, j ).id ) ) { + char name[32]; + sprintf(name, "shape %d,%d", i,j); + sprintf(layer_name, "postclip_shape_%d_%d_%d", i, j, polys_in.get_shape( i, j ).id ); + WriteDebugPoly( layer_name, name, tmp ); + +#if !USE_ACCUMULATOR + sprintf(name, "accum %d,%d", i,j); + sprintf(layer_name, "postclip_accum_%d_%d", i, j); + WriteDebugPoly( layer_name, name, accum ); +#endif + } + } + } + + if ( debug_all || debug_shapes.size() ) { + // Dump the sliver list + WriteDebugPolys( "poly_slivers", slivers ); + } + +#if FIND_SLIVERS + // Now, merge any slivers with clipped polys + merge_slivers(polys_clipped, slivers); +#endif + + slivers.clear(); + + // finally, what ever is left over goes to ocean +#if USE_ACCUMULATOR + remains = tgPolygonDiffClipperWithAccumulator( safety_base ); +#else + remains = tgPolygonDiff( safety_base, accum ); +#endif + + if ( remains.contours() > 0 ) { + // cout << "remains contours = " << remains.contours() << endl; + // move slivers from remains polygon to slivers polygon + +#if FIND_SLIVERS + tgPolygonFindSlivers( remains, slivers ); +#endif + // cout << " After sliver move:" << endl; + // cout << " remains = " << remains.contours() << endl; + // cout << " slivers = " << slivers.contours() << endl; + +#if FIND_SLIVERS + // merge any slivers with previously clipped + // neighboring polygons + if ( slivers.size() > 0 ) { + + if ( debug_all || debug_shapes.size() ) { + // Dump the sliver list + WriteDebugPolys( "remains_slivers", slivers ); + } + + merge_slivers(polys_clipped, slivers); + } +#endif + + if ( remains.contours() > 0 ) { + TGSuperPoly sp; + TGShape shape; + + string material = get_area_name(get_sliver_target_area_type()); + + sp.set_material( material ); + sp.set_poly( remains ); + shape.SetMask( remains ); + shape.textured = false; + shape.sps.push_back( sp ); + + polys_clipped.add_shape( (int)get_sliver_target_area_type(), shape ); + } + } + +#if USE_ACCUMULATOR + + tgPolygonFreeClipperAccumulator(); + +#endif + + // Once clipping is complete, intersect the individual segments with their clip masks + for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { + for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + SG_LOG( SG_CLIPPER, SG_INFO, "Seperating segments from clip mask for " << get_area_name( (AreaType)area ) << ":" << shape+1 << " of " << polys_clipped.area_size(area) ); + polys_clipped.get_shape(area, shape).IntersectPolys(); + } + } + + // Now make sure any newly added intersection nodes are added to the tgnodes + for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { + for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + for (unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++) { + TGPolygon poly = polys_clipped.get_poly( area, shape, segment ); + + SG_LOG( SG_CLIPPER, SG_INFO, "Collecting nodes for " << get_area_name( (AreaType)area ) << ":" << shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); + + for (int con=0; con < poly.contours(); con++) { + for (int node = 0; node < poly.contour_size( con ); node++) { + // ensure we have all nodes... + nodes.unique_add( poly.get_pt( con, node ) ); + } + } + } + } + } + + return true; +} diff --git a/src/BuildTiles/Main/tgconstruct_debug.cxx b/src/BuildTiles/Main/tgconstruct_debug.cxx new file mode 100644 index 00000000..ba3e4af6 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_debug.cxx @@ -0,0 +1,206 @@ +// tgconstruct_debug.cxx -- Class toimplement construct debug +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include "tgconstruct.hxx" + +using std::string; + +void TGConstruct::set_debug( std::string path, std::vector area_defs, std::vector shape_defs ) +{ + SG_LOG(SG_GENERAL, SG_ALERT, "Set debug Path " << path); + + debug_path = path; + + /* Find any ids for our tile */ + for (unsigned int i=0; i< area_defs.size(); i++) { + string dsd = area_defs[i]; + size_t d_pos = dsd.find(":"); + string tile = dsd.substr(0, d_pos); + + if( tile == bucket.gen_index_str() ) { + dsd.erase(0, d_pos+1); + + if ( dsd == "all" ) { + debug_all = true; + } else { + std::stringstream ss(dsd); + int i; + + while (ss >> i) + { + SG_LOG(SG_GENERAL, SG_ALERT, "Adding debug file " << i); + + debug_areas.push_back(i); + + if (ss.peek() == ',') + ss.ignore(); + } + } + } + } + + for (unsigned int i=0; i< shape_defs.size(); i++) { + string dsd = shape_defs[i]; + size_t d_pos = dsd.find(":"); + string tile = dsd.substr(0, d_pos); + + if( tile == bucket.gen_index_str() ) { + dsd.erase(0, d_pos+1); + + if ( dsd == "all" ) { + debug_all = true; + } else { + std::stringstream ss(dsd); + int i; + + while (ss >> i) + { + SG_LOG(SG_GENERAL, SG_ALERT, "Adding debug file " << i); + + debug_shapes.push_back(i); + + if (ss.peek() == ',') + ss.ignore(); + } + } + } + } +} + +bool TGConstruct::IsDebugShape( unsigned int id ) +{ + bool is_debug = false; + + /* Check global flag */ + if ( debug_all ) { + is_debug = true; + } else { + for (unsigned int i=0; iget_node_list(); + node_list sorted_nodes; + for (unsigned int i=0; i +#endif + +#include + +#include "tgconstruct.hxx" + +using std::string; + +// Load elevation data from an Array file (a regular grid of elevation data) +// and return list of fitted nodes. +void TGConstruct::LoadElevationArray( void ) { + string base = bucket.gen_base_path(); + int i; + + for ( i = 0; i < (int)load_dirs.size(); ++i ) { + string array_path = work_base + "/" + load_dirs[i] + "/" + base + "/" + bucket.gen_index_str(); + + if ( array.open(array_path) ) { + break; + } else { + SG_LOG(SG_GENERAL, SG_DEBUG, "Failed to open Array file " << array_path); + } + } + + array.parse( bucket ); + array.remove_voids( ); + + point_list corner_list = array.get_corner_list(); + for (unsigned int i=0; i +#endif + +#include + +#include "tgconstruct.hxx" +#include "usgs.hxx" + +using std::string; + +// If we don't offset land use squares by some amount, then we can get +// land use square boundaries coinciding with tile boundaries. +// +// This can foul up our edge matching scheme because a subsequently +// generated adjacent tile might be forced to have edge nodes not +// found in the first tile and not on the shared edge. This can lead +// to gaps. If we put skirts around everything that might hide the +// problem. +static const double cover_size = 1.0 / 120.0; +static const double half_cover_size = cover_size * 0.5; +static const double quarter_cover_size = cover_size * 0.25; + +// make the area specified area, look up the land cover type, and add +// it to polys +void TGConstruct::make_area( const LandCover &cover, TGPolygon *polys, + double x1, double y1, double x2, double y2, + double half_dx, double half_dy ) +{ + const double fudge = 0.0001; // (0.0001 degrees =~ 10 meters) + + AreaType area = get_landcover_type( cover, + x1 + half_dx, y1 + half_dy, + x2 - x1, y2 - y1 ); + + if ( area != get_default_area_type() ) { + // Create a square polygon and merge it into the list. + TGPolygon poly; + poly.erase(); + poly.add_node(0, Point3D(x1 - fudge, y1 - fudge, 0.0)); + poly.add_node(0, Point3D(x1 - fudge, y2 + fudge, 0.0)); + poly.add_node(0, Point3D(x2 + fudge, y2 + fudge, 0.0)); + poly.add_node(0, Point3D(x2 + fudge, y1 - fudge, 0.0)); + + if ( measure_roughness( poly ) < 1.0 ) { + // Add this poly to the area accumulator + if ( polys[area].contours() > 0 ) { + polys[area] = tgPolygonUnion( polys[area], poly ); + } else { + polys[area] = poly; + } + } + } +} + +// Come up with a "rough" metric for the roughness of the terrain +// coverted by a polygon +double TGConstruct::measure_roughness( TGPolygon &poly ) { + int i; + unsigned int j; + + // find the elevation range + double max_z = -9999.0; + double min_z = 9999.0; + + for ( i = 0; i < poly.contours(); ++i ) { + point_list points = poly.get_contour( i ); + for ( j = 0; j < points.size(); ++j ) { + double z; + z = array.altitude_from_grid( points[j].x() * 3600.0, + points[j].y() * 3600.0 ); + if ( z < -9000 ) { + z = array.closest_nonvoid_elev( points[j].x() * 3600.0, + points[j].y() * 3600.0 ); + } + + if ( z < min_z ) { + min_z = z; + } + if ( z > max_z ) { + max_z = z; + } + } + } + + double diff = max_z - min_z; + + // 50m difference in polygon elevation range yields a roughness + // metric of 1.0. Less than 1.0 is relatively flat. More than + // 1.0 is relatively rough. + + SG_LOG(SG_GENERAL, SG_ALERT, "roughness = " << diff / 50.0 ); + + return diff / 50.0; +} + +AreaType TGConstruct::get_landcover_type (const LandCover &cover, double xpos, double ypos, double dx, double dy) +{ + // Look up the land cover for the square + int cover_value = cover.getValue(xpos, ypos); + AreaType area = translateUSGSCover(cover_value); + + if ( area != get_default_area_type() ) { + // Non-default area is fine. + return area; + } else { + // If we're stuck with the default area, try to borrow from a + // neighbour. + for (double x = xpos - dx; x <= xpos + dx; x += dx) { + for (double y = ypos - dy; y < ypos + dx; y += dy) { + if (x != xpos || y != ypos) { + cover_value = cover.getValue(x, y); + area = translateUSGSCover(cover_value); + if (area != get_default_area_type() ) { + return area; + } + } + } + } + } + + // OK, give up and return default + return get_default_area_type(); +} + + +// Generate polygons from land-cover raster. Horizontally- or +// vertically-adjacent polygons will be merged automatically. +int TGConstruct::load_landcover() +{ + int count = 0; + + try { + + LandCover cover(get_cover()); + TGPolygon polys[TG_MAX_AREA_TYPES]; + TGPolygon poly; // working polygon + + // Get the lower left (SW) corner of the tile + double base_lon = bucket.get_center_lon() + - 0.5 * bucket.get_width() + - quarter_cover_size; + double base_lat = bucket.get_center_lat() + - 0.5 * bucket.get_height() + - quarter_cover_size; + + SG_LOG(SG_GENERAL, SG_ALERT, "raster land cover: tile at " << base_lon << ',' << base_lat); + + double max_lon = bucket.get_center_lon() + + 0.5 * bucket.get_width(); + double max_lat = bucket.get_center_lat() + + 0.5 * bucket.get_height(); + + SG_LOG(SG_GENERAL, SG_ALERT, "raster land cover: extends to " << max_lon << ',' << max_lat); + + double x1 = base_lon; + double y1 = base_lat; + double x2 = x1 + cover_size; + double y2 = y1 + cover_size; + + while ( x1 < max_lon ) { + while ( y1 < max_lat ) { + make_area( cover, polys, x1, y1, x2, y2, half_cover_size, half_cover_size ); + + y1 = y2; + y2 += cover_size; + } + + x1 = x2; + x2 += cover_size; + y1 = base_lat; + y2 = y1 + cover_size; + } + + // Now that we're finished looking up land cover, we have a list + // of lists of polygons, one (possibly-empty) list for each area + // type. Add the remaining polygons to the clipper. + for ( int i = 0; i < TG_MAX_AREA_TYPES; i++ ) { + if ( polys[i].contours() ) { + // TODO : REMOVE add_poly + add_poly( i, polys[i], get_area_name((AreaType)i )); + count++; + } + } + } catch ( string e ) { + SG_LOG(SG_GENERAL, SG_ALERT, "Died with exception: " << e); + exit(-1); + } + + // Return the number of polygons actually read. + return count; +} diff --git a/src/BuildTiles/Main/tgconstruct_lookup.cxx b/src/BuildTiles/Main/tgconstruct_lookup.cxx new file mode 100644 index 00000000..6ae999b1 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_lookup.cxx @@ -0,0 +1,82 @@ +// tgconstruct_lookup.cxx -- Lookup function to store precomputed pointers +// greatly optimize certain stages +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "tgconstruct.hxx" + +// This function populates the Superpoly tri_idx polygon. +// This polygon is a mirror of tris, except the verticies are +// indexes into the node array (cast as unsigned long) +void TGConstruct::LookupNodesPerVertex( void ) +{ + SG_LOG(SG_GENERAL, SG_ALERT, "LookupNodexPerVertex"); + + // for each node, traverse all the triangles - and create face lists + for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { + for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon tris = polys_clipped.get_tris( area, shape, segment ); + TGPolyNodes tri_nodes; + int idx; + + for (int tri=0; tri < tris.contours(); tri++) { + for (int vertex = 0; vertex < tris.contour_size(tri); vertex++) { + idx = nodes.find( tris.get_pt( tri, vertex ) ); + if (idx >= 0) { + tri_nodes.add_node( tri, idx ); + } else { + exit(0); + } + } + } + polys_clipped.set_tri_idxs(area, shape, segment, tri_nodes); + } + } + } +} + +void TGConstruct::LookupFacesPerNode( void ) +{ + SG_LOG(SG_GENERAL, SG_ALERT, "LookupFacesPerNode"); + + // Add each face that includes a node to the node's face list + for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { + for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon tris = polys_clipped.get_tris(area, shape, segment); + + for (int tri=0; tri < tris.contours(); tri++) { + for (int sub = 0; sub < tris.contour_size(tri); sub++) { + int n = nodes.find( tris.get_pt( tri, sub ) ); + nodes.AddFace( n, area, shape, segment, tri ); + } + } + } + } + } +} diff --git a/src/BuildTiles/Main/tgconstruct_math.cxx b/src/BuildTiles/Main/tgconstruct_math.cxx new file mode 100644 index 00000000..a4c1d692 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_math.cxx @@ -0,0 +1,190 @@ +// tgconstruct_math.cxx -- Implement needed math functions +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "tgconstruct.hxx" + +//using std::string; + +double TGConstruct::calc_tri_area( int_list& triangle_nodes ) { + Point3D p1 = nodes.get_node( triangle_nodes[0] ).GetPosition(); + Point3D p2 = nodes.get_node( triangle_nodes[1] ).GetPosition(); + Point3D p3 = nodes.get_node( triangle_nodes[2] ).GetPosition(); + + return triangle_area( p1, p2, p3 ); +} + +SGVec3d TGConstruct::calc_normal( double area, Point3D p1, Point3D p2, Point3D p3 ) { + SGVec3d v1, v2, normal; + + // do some sanity checking. With the introduction of landuse + // areas, we can get some long skinny triangles that blow up our + // "normal" calculations here. Let's check for really small + // triangle areas and check if one dimension of the triangle + // coordinates is nearly coincident. If so, assign the "default" + // normal of straight up. + + bool degenerate = false; + const double area_eps = 1.0e-12; + if ( area < area_eps ) { + degenerate = true; + } + + if ( fabs(p1.x() - p2.x()) < SG_EPSILON && fabs(p1.x() - p3.x()) < SG_EPSILON ) { + degenerate = true; + } + if ( fabs(p1.y() - p2.y()) < SG_EPSILON && fabs(p1.y() - p3.y()) < SG_EPSILON ) { + degenerate = true; + } + if ( fabs(p1.z() - p2.z()) < SG_EPSILON && fabs(p1.z() - p3.z()) < SG_EPSILON ) { + degenerate = true; + } + + if ( degenerate ) { + normal = normalize(SGVec3d(p1.x(), p1.y(), p1.z())); + } else { + v1[0] = p2.x() - p1.x(); + v1[1] = p2.y() - p1.y(); + v1[2] = p2.z() - p1.z(); + v2[0] = p3.x() - p1.x(); + v2[1] = p3.y() - p1.y(); + v2[2] = p3.z() - p1.z(); + normal = normalize(cross(v1, v2)); + } + + return normal; +} + +void TGConstruct::calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ) { + // for each face in the superpoly, calculate a face normal + SGVec3d normal; + TGPolyNodes tri_nodes = sp.get_tri_idxs(); + int_list face_nodes; + double_list face_areas; + point_list face_normals; + double area; + + face_normals.clear(); + face_areas.clear(); + + for (int i=0; iface_areas.size(); + for ( int j = 0; j < num_faces; j++ ) { + normal = neighbor_faces->face_normals[j]; + face_area = neighbor_faces->face_areas[j]; + SG_LOG(SG_GENERAL, SG_ALERT, "\tAdding face area " << face_area << " and normal " << normal << " to node " << node.GetPosition() ); + + normal *= face_area; + total_area += face_area; + average += normal; + } + } + + average /= total_area; + nodes.SetNormal( i, average ); + } +} diff --git a/src/BuildTiles/Main/tgconstruct_output.cxx b/src/BuildTiles/Main/tgconstruct_output.cxx new file mode 100644 index 00000000..da73b591 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_output.cxx @@ -0,0 +1,252 @@ +// tgconstruct_output.cxx --Handle writing out the btg and stg files +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "tgconstruct.hxx" + +using std::string; + +// collect custom objects and move to scenery area +void TGConstruct::AddCustomObjects( void ) { + // Create/open the output .stg file for writing + SGPath dest_d(output_base.c_str()); + dest_d.append(bucket.gen_base_path().c_str()); + string dest_dir = dest_d.str_native(); + SGPath dest_i(dest_d); + dest_i.append(bucket.gen_index_str()); + dest_i.concat(".stg"); + string dest_ind = dest_i.str_native(); + + FILE *fp; + if ( (fp = fopen( dest_ind.c_str(), "w" )) == NULL ) { + SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: opening " << dest_ind << " for writing!" ); + exit(-1); + } + + // Start with the default custom object which is the base terrain + fprintf(fp, "OBJECT_BASE %s.btg\n", bucket.gen_index_str().c_str()); + + char line[2048]; // big enough? + char token[256]; + char name[256]; + + for ( int i = 0; i < (int)load_dirs.size(); ++i ) { + SGPath base(work_base.c_str()); + base.append(load_dirs[i]); + base.append( bucket.gen_base_path() ); + SGPath index(base); + index.append( bucket.gen_index_str() ); + index.concat(".ind"); + string index_file = index.str_native(); + + sg_gzifstream in( index_file ); + + if ( ! in.is_open() ) { + //No custom objects + } else { + while ( ! in.eof() ) { + SG_LOG( SG_GENERAL, SG_INFO, "Collecting custom objects from " << index_file ); + in.getline(line, 2048); + SG_LOG( SG_GENERAL, SG_INFO, "line = " << line ); + + int result = sscanf( line, "%s %s", token, name ); + SG_LOG( SG_GENERAL, SG_INFO, "scanf scanned " << result << " tokens" ); + + if ( result > 0 ) { + SG_LOG( SG_GENERAL, SG_INFO, "token = " << token << " name = " << name ); + + if ( strcmp( token, "OBJECT" ) == 0 ) { + SGPath srcbase(base); + srcbase.append(name); + srcbase.concat(".gz"); + string basecom = srcbase.str_native(); +#ifdef _MSC_VER + string command = "copy " + basecom + " " + dest_dir; +#else + string command = "cp " + basecom + " " + dest_dir; +#endif + SG_LOG( SG_GENERAL, SG_INFO, "running " << command ); + system( command.c_str() ); + + fprintf(fp, "OBJECT %s\n", name); + } else { + fprintf(fp, "%s\n", line); + } + } + } + } + } + + fclose(fp); +} + +void TGConstruct::WriteBtgFile( void ) +{ + TGTriNodes normals, texcoords; + normals.clear(); + texcoords.clear(); + + group_list pts_v; pts_v.clear(); + group_list pts_n; pts_n.clear(); + string_list pt_materials; pt_materials.clear(); + + group_list tris_v; tris_v.clear(); + group_list tris_n; tris_n.clear(); + group_list tris_tc; tris_tc.clear(); + string_list tri_materials; tri_materials.clear(); + + group_list strips_v; strips_v.clear(); + group_list strips_n; strips_n.clear(); + group_list strips_tc; strips_tc.clear(); + string_list strip_materials; strip_materials.clear(); + + int index; + int_list pt_v, tri_v, strip_v; + int_list pt_n, tri_n, strip_n; + int_list tri_tc, strip_tc; + + for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { + // only tesselate non holes + if ( !is_hole_area( area ) ) { + for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + SG_LOG( SG_CLIPPER, SG_INFO, "Ouput nodes for " << get_area_name( (AreaType)area ) << ":" << + shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); + + TGPolyNodes tri_nodes = polys_clipped.get_tri_idxs(area, shape, segment); + TGPolygon tri_txs = polys_clipped.get_texcoords(area, shape, segment); + string material = polys_clipped.get_material(area, shape, segment); + + for (int k = 0; k < tri_nodes.contours(); ++k) { + tri_v.clear(); + tri_n.clear(); + tri_tc.clear(); + for (int l = 0; l < tri_nodes.contour_size(k); ++l) { + index = tri_nodes.get_pt( k, l ); + tri_v.push_back( index ); + + // add the node's normal + index = normals.unique_add( nodes.GetNormal( index ) ); + tri_n.push_back( index ); + + Point3D tc = tri_txs.get_pt( k, l ); + index = texcoords.unique_add( tc ); + tri_tc.push_back( index ); + } + tris_v.push_back( tri_v ); + tris_n.push_back( tri_n ); + tris_tc.push_back( tri_tc ); + tri_materials.push_back( material ); + } + } + } + } + } + + std::vector< SGVec3d > wgs84_nodes = nodes.get_wgs84_nodes_as_SGVec3d(); + SGSphered d; + + for (int i = 0; i < (int)wgs84_nodes.size(); ++i) + { + d.expandBy(wgs84_nodes[ i ]); + } + + SGVec3d gbs_center = d.getCenter(); + double gbs_radius = d.getRadius(); + SG_LOG(SG_GENERAL, SG_DEBUG, "gbs center = " << gbs_center); + SG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping"); + SG_LOG(SG_GENERAL, SG_DEBUG, " center = " << gbs_center << " radius = " << gbs_radius ); + + // null structures + group_list fans_v; fans_v.clear(); + group_list fans_n; fans_n.clear(); + group_list fans_tc; fans_tc.clear(); + string_list fan_materials; fan_materials.clear(); + + string base = output_base; + string binname = bucket.gen_index_str(); + binname += ".btg"; + string txtname = bucket.gen_index_str(); + txtname += ".txt"; + + std::vector< SGVec3f > normals_3f; + for (int i=0; i < (int)normals.get_node_list().size(); i++ ) + { + Point3D node = normals.get_node_list()[i]; + normals_3f.push_back( node.toSGVec3f() ); + } + + std::vector< SGVec2f > texcoords_2f; + for (int i=0; i < (int)texcoords.get_node_list().size(); i++ ) + { + Point3D node = texcoords.get_node_list()[i]; + texcoords_2f.push_back( node.toSGVec2f() ); + } + + SGBinObject obj; + + obj.set_gbs_center( gbs_center ); + obj.set_gbs_radius( gbs_radius ); + obj.set_wgs84_nodes( wgs84_nodes ); + obj.set_normals( normals_3f ); + obj.set_texcoords( texcoords_2f ); + obj.set_pts_v( pts_v ); + obj.set_pts_n( pts_n ); + obj.set_pt_materials( pt_materials ); + obj.set_tris_v( tris_v ); + obj.set_tris_n( tris_n ); + obj.set_tris_tc( tris_tc ); + obj.set_tri_materials( tri_materials ); + obj.set_strips_v( strips_v ); + obj.set_strips_n( strips_n ); + obj.set_strips_tc( strips_tc ); + obj.set_strip_materials( strip_materials ); + obj.set_fans_v( fans_v ); + obj.set_fans_n( fans_n ); + obj.set_fans_tc( fans_tc ); + obj.set_fan_materials( fan_materials ); + + bool result; + result = obj.write_bin( base, binname, bucket ); + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + if (debug_all || debug_shapes.size()) + { + result = obj.write_ascii( base, txtname, bucket ); + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + } +} diff --git a/src/BuildTiles/Main/tgconstruct_poly.cxx b/src/BuildTiles/Main/tgconstruct_poly.cxx new file mode 100644 index 00000000..22e60e46 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_poly.cxx @@ -0,0 +1,419 @@ +// tgconstruct_poly.cxx -- load and handle polygon data +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include +#include + +#include "tgconstruct.hxx" + +using std::string; + +static unsigned int cur_poly_id = 0; + +// Add a polygon to the clipper. - only used by load_osgb36_poly - make that function more like ogr load +void TGConstruct::add_poly( int area, const TGPolygon &poly, string material ) { + TGShape shape; + TGSuperPoly sp; + + if ( area < TG_MAX_AREA_TYPES ) { + sp.set_poly( poly ); + sp.set_material( material ); + + shape.sps.push_back( sp ); + + polys_in.add_shape( area, shape ); + } else { + SG_LOG( SG_CLIPPER, SG_ALERT, "Polygon type out of range = " << area); + exit(-1); + } +} + +bool TGConstruct::load_poly(const string& path) { + bool poly3d = false; + bool with_tp = false; + string first_line; + string poly_name; + AreaType poly_type; + int contours, count, i, j, k; + int hole_flag; + int num_polys; + double startx, starty, startz, x, y, z, lastx, lasty, lastz; + + sg_gzifstream in( path ); + + if ( !in ) { + SG_LOG( SG_CLIPPER, SG_ALERT, "Cannot open file: " << path ); + exit(-1); + } + + TGPolygon poly; + TGTexParams tp; + Point3D p; + + // (this could break things, why is it here) in >> skipcomment; + while ( !in.eof() ) { + in >> first_line; + if ( first_line == "#2D" ) { + poly3d = false; + with_tp = false; + + in >> poly_name; + num_polys = 1; + } else if ( first_line == "#2D_WITH_MASK" ) { + poly3d = false; + with_tp = false; + + in >> poly_name; + in >> num_polys; + } else if ( first_line == "#2D_WITH_TPS" ) { + poly3d = false; + with_tp = true; + + in >> poly_name; + in >> num_polys; + } else if ( first_line == "#3D" ) { + poly3d = true; + with_tp = false; + + in >> poly_name; + num_polys = 1; + } else { + // support old format (default to 2d) + poly3d = false; + with_tp = false; + + poly_name = first_line; + num_polys = 1; + } + poly_type = get_area_type( poly_name ); + + int area = (int)poly_type; + string material; + + // only allow 1000 shapes per material + int extension = polys_in.area_size( area ) / 1000; + + if (extension) + { + char buff[32]; + sprintf( buff, "%s_%d", get_area_name( area ).c_str(), extension ); + material = buff; + } + else + { + material = get_area_name( area ); + } + + + // Generate a new Shape for the poly + TGShape shape; + TGSuperPoly sp; + + for (k=0; k> x; + in >> y; + in >> width; + in >> length; + in >> heading; + in >> minu; + in >> maxu; + in >> minv; + in >> maxv; + + tp.set_ref( Point3D(x, y, 0.0f) ); + tp.set_width( width ); + tp.set_length( length ); + tp.set_heading( heading ); + tp.set_minu( minu ); + tp.set_maxu( maxu ); + tp.set_minv( minv ); + tp.set_maxv( maxv ); + } + + in >> contours; + + poly.erase(); + + for ( i = 0; i < contours; ++i ) { + in >> count; + + if ( count < 3 ) { + SG_LOG( SG_CLIPPER, SG_ALERT, "Polygon with less than 3 data points." ); + exit(-1); + } + + in >> hole_flag; + + in >> startx; + in >> starty; + if ( poly3d ) { + in >> startz; + } else { + startz = -9999.0; + } + + p = Point3D(startx+nudge, starty+nudge, startz); + p.snap( gSnap ); + poly.add_node( i, p ); + + if ( poly3d ) { + nodes.unique_add_fixed_elevation( p ); + } else { + nodes.unique_add( p ); + } + + for ( j = 1; j < count - 1; ++j ) { + in >> x; + in >> y; + if ( poly3d ) { + in >> z; + } else { + z = -9999.0; + } + p = Point3D( x+nudge, y+nudge, z ); + p.snap( gSnap ); + poly.add_node( i, p ); + if ( poly3d ) { + nodes.unique_add_fixed_elevation( p ); + } else { + nodes.unique_add( p ); + } + } + + in >> lastx; + in >> lasty; + if ( poly3d ) { + in >> lastz; + } else { + lastz = -9999.0; + } + + if ( (fabs(startx - lastx) < SG_EPSILON) && + (fabs(starty - lasty) < SG_EPSILON) && + (fabs(startz - lastz) < SG_EPSILON) ) { + // last point same as first, discard + } else { + p = Point3D( lastx+nudge, lasty+nudge, lastz ); + p.snap( gSnap ); + poly.add_node( i, p ); + if ( poly3d ) { + nodes.unique_add_fixed_elevation( p ); + } else { + nodes.unique_add( p ); + } + } + } + + poly = remove_dups( poly ); + + sp.set_poly( poly ); + sp.set_material( material ); + shape.sps.push_back( sp ); + + if ( with_tp ) { + shape.textured = true; + shape.tps.push_back( tp ); + } + else + { + shape.textured = false; + } + + in >> skipcomment; + } + + // Once the full poly is loaded, build the clip mask + shape.BuildMask(); + shape.area = area; + shape.id = cur_poly_id++; + + polys_in.add_shape( area, shape ); + + if ( IsDebugShape( shape.id ) ) { + WriteDebugShape( "loaded", shape ); + } + } + + return true; +} + + +// Load a polygon definition file containing osgb36 Eastings and Northings +// and convert them to WGS84 Latitude and Longitude +bool TGConstruct::load_osgb36_poly(const string& path) { + string poly_name; + AreaType poly_type; + int contours, count, i, j; + int hole_flag; + double startx, starty, x, y, lastx, lasty; + + SG_LOG( SG_CLIPPER, SG_INFO, "Loading " << path << " ..." ); + + sg_gzifstream in( path ); + + if ( !in ) { + SG_LOG( SG_CLIPPER, SG_ALERT, "Cannot open file: " << path ); + exit(-1); + } + + TGPolygon poly; + + Point3D p; + Point3D OSRef; + in >> skipcomment; + while ( !in.eof() ) { + in >> poly_name; + SG_LOG( SG_CLIPPER, SG_INFO, "poly name = " << poly_name); + poly_type = get_area_type( poly_name ); + SG_LOG( SG_CLIPPER, SG_INFO, "poly type (int) = " << (int)poly_type); + in >> contours; + SG_LOG( SG_CLIPPER, SG_INFO, "num contours = " << contours); + + poly.erase(); + + for ( i = 0; i < contours; ++i ) { + in >> count; + + if ( count < 3 ) { + SG_LOG( SG_CLIPPER, SG_ALERT, "Polygon with less than 3 data points." ); + exit(-1); + } + + in >> hole_flag; + + in >> startx; + in >> starty; + OSRef = Point3D(startx, starty, -9999.0); + + //Convert from OSGB36 Eastings/Northings to WGS84 Lat/Lon + //Note that startx and starty themselves must not be altered since we compare them with unaltered lastx and lasty later + p = OSGB36ToWGS84(OSRef); + + poly.add_node( i, p ); + nodes.unique_add( p ); + + SG_LOG( SG_CLIPPER, SG_BULK, "0 = " << startx << ", " << starty ); + + for ( j = 1; j < count - 1; ++j ) { + in >> x; + in >> y; + OSRef = Point3D( x, y, -9999.0 ); + p = OSGB36ToWGS84(OSRef); + + poly.add_node( i, p ); + nodes.unique_add( p ); + SG_LOG( SG_CLIPPER, SG_BULK, j << " = " << x << ", " << y ); + } + + in >> lastx; + in >> lasty; + + if ( (fabs(startx - lastx) < SG_EPSILON) && + (fabs(starty - lasty) < SG_EPSILON) ) { + // last point same as first, discard + } else { + OSRef = Point3D( lastx, lasty, -9999.0 ); + p = OSGB36ToWGS84(OSRef); + + poly.add_node( i, p ); + nodes.unique_add( p ); + SG_LOG( SG_CLIPPER, SG_BULK, count - 1 << " = " << lastx << ", " << lasty ); + } + } + + // TODO : Make like OGR + int area = (int)poly_type; + string material = get_area_name( area ); + add_poly(area, poly, material); + // END TODO + + in >> skipcomment; + } + + return true; +} + +// load all 2d polygons from the specified load disk directories and +// clip against each other to resolve any overlaps +int TGConstruct::LoadLandclassPolys( void ) { + int i; + + string base = bucket.gen_base_path(); + string poly_path; + int count = 0; + + polys_in.clear(); + + // load 2D polygons from all directories provided + for ( i = 0; i < (int)load_dirs.size(); ++i ) { + poly_path = work_base + "/" + load_dirs[i] + '/' + base; + + string tile_str = bucket.gen_index_str(); + simgear::Dir d(poly_path); + if (!d.exists()) { + SG_LOG(SG_GENERAL, SG_DEBUG, "directory not found: " << poly_path); + continue; + } + + simgear::PathList files = d.children(simgear::Dir::TYPE_FILE); + SG_LOG( SG_CLIPPER, SG_ALERT, files.size() << " Polys in " << d.path() ); + + BOOST_FOREACH(const SGPath& p, files) { + if (p.file_base() != tile_str) { + continue; + } + + string lext = p.complete_lower_extension(); + if ((lext == "arr") || (lext == "arr.gz") || (lext == "btg.gz") || + (lext == "fit") || (lext == "fit.gz") || (lext == "ind")) + { + // skipped! + } else if (lext == "osgb36") { + SG_LOG(SG_GENERAL, SG_ALERT, " Loading osgb36 poly definition file " << p.file()); + load_osgb36_poly( p.str() ); + ++count; + } else { + load_poly( p.str() ); + SG_LOG(SG_GENERAL, SG_ALERT, " Loaded " << p.file()); + ++count; + } + } // of directory file children + } + SG_LOG(SG_GENERAL, SG_ALERT, " Total polys used for this tile: " << count ); + return count; +} diff --git a/src/BuildTiles/Main/tgconstruct_shared.cxx b/src/BuildTiles/Main/tgconstruct_shared.cxx new file mode 100644 index 00000000..99bf0395 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_shared.cxx @@ -0,0 +1,551 @@ +// construct_intermediate.cxx -- Handle all intermediate and shared data files +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include + +#include "tgconstruct.hxx" + +using std::string; + +void TGConstruct::SaveSharedEdgeData( int stage ) +{ + string dir; + string file; + + switch( stage ) { + case 1: + { + point_list north, south, east, west; + int nCount; + + nodes.get_geod_edge( bucket, north, south, east, west ); + + dir = share_base + "/stage1/" + bucket.gen_base_path(); + + SGPath sgp( dir ); + sgp.append( "dummy" ); + sgp.create_dir( 0755 ); + + file = dir + "/" + bucket.gen_index_str() + "_edges"; + std::ofstream ofs_e( file.c_str() ); + + // first, set the precision + ofs_e << std::setprecision(12); + ofs_e << std::fixed; + + // north + nCount = north.size(); + ofs_e << nCount << "\n"; + for (int i=0; i> count; + + for (int i=0; i> node; + + // look to see if we already have this node + // If we do, (it's a corner) add more faces to it. + // otherwise, initialize it with our elevation data + pFaces = FindNeighborFaces( node ); + if ( !pFaces ) { + pFaces = AddNeighborFaces( node ); + + // new face - let's add our elevation first + int idx = nodes.find( node ); + if (idx >= 0) { + TGNode local = nodes.get_node( idx ); + pFaces->elevations.push_back( local.GetPosition().z() ); + } + } + + // remember all of the elevation data for the node, so we can average + pFaces->elevations.push_back( node.z() ); + + in >> num_faces; + for (int j=0; j> area; + pFaces->face_areas.push_back( area ); + + in >> normal; + pFaces->face_normals.push_back( normal ); + } + } +} + +void TGConstruct::SaveSharedEdgeDataStage2( void ) +{ + string dir; + string file; + point_list north, south, east, west; + std::ofstream ofs_e; + int nCount; + + nodes.get_geod_edge( bucket, north, south, east, west ); + + dir = share_base + "/stage2/" + bucket.gen_base_path(); + + SGPath sgp( dir ); + sgp.append( "dummy" ); + sgp.create_dir( 0755 ); + + + // north edge + file = dir + "/" + bucket.gen_index_str() + "_north_edge"; + ofs_e.open( file.c_str() ); + ofs_e << std::setprecision(12); + ofs_e << std::fixed; + + nCount = north.size(); + ofs_e << nCount << "\n"; + for (int i=0; i> nCount; + SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " north boundary"); + for (int i=0; i> pt; + north.push_back(pt); + } + + // South + ifs_edges >> nCount; + SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " south boundary"); + for (int i=0; i> pt; + south.push_back(pt); + } + + // East + ifs_edges >> nCount; + SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " east boundary"); + for (int i=0; i> pt; + east.push_back(pt); + } + + // West + ifs_edges >> nCount; + SG_LOG( SG_CLIPPER, SG_INFO, "loading " << nCount << "Points on " << b.gen_index_str() << " west boundary"); + for (int i=0; i> pt; + west.push_back(pt); + } + + ifs_edges.close(); + } +} + +void TGConstruct::LoadSharedEdgeData( int stage ) +{ + switch( stage ) { + case 1: + { + // we need to read just 4 buckets for stage 1 - 1 for each edge + point_list north, south, east, west; + SGBucket nb, sb, eb, wb; + double clon = bucket.get_center_lon(); + double clat = bucket.get_center_lat(); + + // Read North tile and add its southern nodes + nb = sgBucketOffset(clon, clat, 0, 1); + LoadNeighboorEdgeDataStage1( nb, north, south, east, west ); + // Add southern nodes from northern tile + for (unsigned int i=0; i> polys_clipped; + ifs_cp.close(); + + file = dir + "/" + bucket.gen_index_str() + "_nodes"; + + std::ifstream ifs_n( file.c_str() ); + ifs_n >> nodes; + ifs_n.close(); + break; + } + + case 2: // Load the clipped polys and node list + { + dir = share_base + "/stage2/" + bucket.gen_base_path(); + file = dir + "/" + bucket.gen_index_str() + "_clipped_polys"; + + std::ifstream ifs_cp( file.c_str() ); + ifs_cp >> polys_clipped; + ifs_cp.close(); + + file = dir + "/" + bucket.gen_index_str() + "_nodes"; + + std::ifstream ifs_n( file.c_str() ); + ifs_n >> nodes; + ifs_n.close(); + break; + } + } +} diff --git a/src/BuildTiles/Main/tgconstruct_tesselate.cxx b/src/BuildTiles/Main/tgconstruct_tesselate.cxx new file mode 100644 index 00000000..d2ade0b4 --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_tesselate.cxx @@ -0,0 +1,81 @@ +// construct.cxx -- Class to manage the primary data used in the +// construction process +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +//#include +#include + +#include +//#include + +#include "tgconstruct.hxx" + +//using std::string; + +void TGConstruct::TesselatePolys( void ) +{ + // tesselate the polygons and prepair them for final output + point_list poly_extra; + Point3D min, max; + + for (unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++) { + for (unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + unsigned int id = polys_clipped.get_shape( area, shape ).id; + + if ( IsDebugShape( id ) ) { + WriteDebugShape( "preteselate", polys_clipped.get_shape(area, shape) ); + } + + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon poly = polys_clipped.get_poly(area, shape, segment); + + poly.get_bounding_box(min, max); + poly_extra = nodes.get_geod_inside( min, max ); + + SG_LOG( SG_CLIPPER, SG_INFO, "Tesselating " << get_area_name( (AreaType)area ) << "(" << area << "): " << + shape+1 << "-" << segment << " of " << (int)polys_clipped.area_size(area) << + ": id = " << id ); + + if ( IsDebugShape( id ) ) { + SG_LOG( SG_CLIPPER, SG_INFO, poly ); + } + + TGPolygon tri = polygon_tesselate_alt_with_extra_cgal( poly, poly_extra, false ); + + // ensure all added nodes are accounted for + for (int k=0; k< tri.contours(); k++) { + for (int l = 0; l < tri.contour_size(k); l++) { + // ensure we have all nodes... + nodes.unique_add( tri.get_pt( k, l ) ); + } + } + + // Save the triangulation + polys_clipped.set_tris( area, shape, segment, tri ); + } + } + } +} diff --git a/src/BuildTiles/Main/tgconstruct_texture.cxx b/src/BuildTiles/Main/tgconstruct_texture.cxx new file mode 100644 index 00000000..e58db5ce --- /dev/null +++ b/src/BuildTiles/Main/tgconstruct_texture.cxx @@ -0,0 +1,187 @@ + +// tgconstruct_texture.cxx --Handle texture coordinate generation in tgconstruct +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "tgconstruct.hxx" + +TGPolygon TGConstruct::area_tex_coords( const TGPolygon& tri ) +{ + TGPolygon result; + result.erase(); + + // lots of conversion needed to use simgear API - perhaps we need a new simgear API? + for (int c=0; c conv_geods; + point_list tex_coords; + + for (int i = 0; i < (int)nodes.size(); ++i ) + { + SGGeod conv_geod = SGGeod::fromDegM( nodes[i].x(), nodes[i].y(), nodes[i].z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << nodes[i] ); + conv_geods.push_back( conv_geod ); + } + + // now calculate texture coordinates + // generate identity interger list... + std::vector< int > node_idx; + for (int i = 0; i < (int)conv_geods.size(); i++) { + node_idx.push_back(i); + } + + std::vector< SGVec2f > tp_list = sgCalcTexCoords( bucket, conv_geods, node_idx ); + // generate a contour of texture coordinates from the tp list + for (int i = 0; i < (int)tp_list.size(); i++) + { + tex_coords.push_back( Point3D::fromSGVec2( tp_list[i] ) ); + } + result.add_contour( tex_coords, 0 ); + } + + return result; +} + +TGPolygon TGConstruct::linear_tex_coords( const TGPolygon& tri, const TGTexParams& tp ) +{ + TGPolygon result; + int i, j; + + result.erase(); + + Point3D ref = tp.get_ref(); + double width = tp.get_width(); + double length = tp.get_length(); + double heading = tp.get_heading(); + double minu = tp.get_minu(); + double maxu = tp.get_maxu(); + double minv = tp.get_minv(); + double maxv = tp.get_maxv(); + SG_LOG( SG_GENERAL, SG_DEBUG, "section ref = " << ref ); + SG_LOG( SG_GENERAL, SG_DEBUG, " width = " << width ); + SG_LOG( SG_GENERAL, SG_DEBUG, " length = " << length ); + SG_LOG( SG_GENERAL, SG_DEBUG, " heading = " << heading ); + SG_LOG( SG_GENERAL, SG_DEBUG, " minv = " << minv ); + SG_LOG( SG_GENERAL, SG_DEBUG, " maxv = " << maxv ); + SG_LOG( SG_GENERAL, SG_DEBUG, " heading = " << heading ); + + Point3D p, t; + double x, y, tx, ty; + + for ( i = 0; i < tri.contours(); ++i ) + { + for ( j = 0; j < tri.contour_size( i ); ++j ) + { + p = tri.get_pt( i, j ); + SG_LOG(SG_GENERAL, SG_DEBUG, "tex coords for contour " << i << " point " << j << ": " << p ); + + // + // 1. Calculate distance and bearing from the center of + // the feature + // + + // given alt, lat1, lon1, lat2, lon2, calculate starting + // and ending az1, az2 and distance (s). Lat, lon, and + // azimuth are in degrees. distance in meters + double az1, az2, dist; + geo_inverse_wgs_84( 0, ref.y(), ref.x(), p.y(), p.x(), + &az1, &az2, &dist ); + SG_LOG(SG_GENERAL, SG_DEBUG, "basic course from ref = " << az2); + + // + // 2. Rotate this back into a coordinate system where Y + // runs the length of the runway and X runs crossways. + // + + double course = az2 - heading; + while ( course < -360 ) { course += 360; } + while ( course > 360 ) { course -= 360; } + SG_LOG( SG_GENERAL, SG_DEBUG, + " course = " << course << " dist = " << dist ); + + // + // 3. Convert from polar to cartesian coordinates + // + + x = sin( course * SGD_DEGREES_TO_RADIANS ) * dist; + y = cos( course * SGD_DEGREES_TO_RADIANS ) * dist; + SG_LOG(SG_GENERAL, SG_DEBUG, " x = " << x << " y = " << y); + + // + // 4. Map x, y point into texture coordinates + // + double tmp; + + tmp = x / width; + tx = tmp * (maxu - minu) + minu; + + if ( tx < -1.0 ) { tx = -1.0; } + if ( tx > 1.0 ) { tx = 1.0; } + + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ")"); + + ty = (y/length) + minv; + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << ty << ")"); + + t = Point3D( tx, ty, 0 ); + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ", " << ty << ")"); + + result.add_node( i, t ); + } + } + + return result; +} + +void TGConstruct::CalcTextureCoordinates( void ) +{ + for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) { + for( unsigned int shape = 0; shape < polys_clipped.area_size(area); shape++ ) { + for ( unsigned int segment = 0; segment < polys_clipped.shape_size(area, shape); segment++ ) { + TGPolygon poly = polys_clipped.get_poly(area, shape, segment); + SG_LOG( SG_CLIPPER, SG_INFO, "Texturing " << get_area_name( (AreaType)area ) << "(" << area << "): " << + shape+1 << "-" << segment << " of " << polys_clipped.area_size(area) ); + + TGPolygon tri = polys_clipped.get_tris( area, shape, segment ); + TGPolygon tc; + + if ( polys_clipped.get_textured( area, shape ) ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "USE TEXTURE PARAMS for tex coord calculations" ); + tc = linear_tex_coords( tri, polys_clipped.get_texparams(area, shape, segment) ); + } else { + SG_LOG(SG_GENERAL, SG_DEBUG, "USE SIMGEAR for tex coord calculations" ); + tc = area_tex_coords( tri ); + } + polys_clipped.set_texcoords( area, shape, segment, tc ); + } + } + } +} diff --git a/src/BuildTiles/Main/tglandclass.cxx b/src/BuildTiles/Main/tglandclass.cxx new file mode 100644 index 00000000..27fb2065 --- /dev/null +++ b/src/BuildTiles/Main/tglandclass.cxx @@ -0,0 +1,80 @@ +// TGLandclass.cxx -- Class toSimnplify dealing with shape heiarchy: +// landclass contains each area (layer) of a tile +// Each area is a list of shapes +// A shape has 1 or more segments +// (when the shape represents line data) +// And the segment is a superpoly, containing +// - a polygon, triangulation, point normals, face normals, etc. +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.hxx,v 1.13 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "tglandclass.hxx" + +void TGLandclass::clear(void) +{ + int i; + + for (i=0; i> ( std::istream& in, TGLandclass& lc) +{ + int i, j, count; + + // Load all landclass shapes + for (i=0; i> count; + + for (j=0; j> shape; + lc.shapes[i].push_back( shape ); + } + } + + return in; +} + +std::ostream& operator<< ( std::ostream& out, const TGLandclass& lc ) +{ + int i, j, count; + TGShape shape; + + // Save all landclass shapes + for (i=0; i> ( std::istream&, TGLandclass& ); + friend std::ostream& operator<< ( std::ostream&, const TGLandclass& ); + +private: + shape_list shapes[TG_MAX_AREA_TYPES]; +}; + +#endif // _TGLANDCLASS_HXX diff --git a/src/BuildTiles/Main/tgshape.cxx b/src/BuildTiles/Main/tgshape.cxx new file mode 100644 index 00000000..c0731b10 --- /dev/null +++ b/src/BuildTiles/Main/tgshape.cxx @@ -0,0 +1,135 @@ +// TGShape.cxx -- Class to handle polygons shapes generated in ogr-decode +// A shape may consist of many polygons when it is generated +// from a polyline. They are kept together to speed up clipping +// but also must be represented as seperate polygons in order to +// keep track of textur parameters. +// +// Written by Curtis Olson, started May 1999. +// +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "tgshape.hxx" + +void TGShape::GetName( char* name ) const +{ + sprintf( name, "%s_%d", get_area_name( (AreaType)area ).c_str(), id ); +} + +void TGShape::SetMask( TGPolygon mask ) +{ + clip_mask = mask; +} + +void TGShape::BuildMask( void ) +{ + TGPolygon poly; + clip_mask.erase(); + + for (unsigned int i=0; i 1 ) { + TGPolygon original, intersect; + + for (unsigned int i=0; i> ( std::istream& in, TGShape& p) +{ + int i, count; + + // First, load the clipmask + in >> p.clip_mask; + + // Then load superpolys + in >> count; + for (i=0; i> sp; + p.sps.push_back( sp ); + } + + // Then load texparams + in >> count; + for (i=0; i> tp; + p.tps.push_back( tp ); + } + + // Load the id, area type and textured flag + in >> p.id; + in >> p.area; + in >> p.textured; + + return in; +} + +std::ostream& operator<< ( std::ostream& out, const TGShape& p ) +{ + int i, count; + TGSuperPoly sp; + TGTexParams tp; + + // First, save the clipmask + out << p.clip_mask; + + // Then save superpolys + count = p.sps.size(); + out << count << "\n"; + for (i=0; i +//#include + +//#include +//#include + +#include +#include + +#include "priorities.hxx" + +class TGShape +{ +public: + TGPolygon clip_mask; + bool textured; + superpoly_list sps; + texparams_list tps; + AreaType area; + unsigned int id; + + void GetName( char* name ) const; + void SetMask( TGPolygon mask ); + void BuildMask( void ); + void IntersectPolys( void ); + + // Friends for serialization + friend std::istream& operator>> ( std::istream&, TGShape& ); + friend std::ostream& operator<< ( std::ostream&, const TGShape& ); +}; + +typedef std::vector < TGShape > shape_list; +typedef shape_list::iterator shape_list_iterator; +typedef shape_list::const_iterator const_shape_list_iterator; + +#endif // _TGSHAPE_HXX \ No newline at end of file