diff --git a/src/Airports/GenAirports850/CMakeLists.txt b/src/Airports/GenAirports850/CMakeLists.txt index 81c46a27..6d5d250f 100644 --- a/src/Airports/GenAirports850/CMakeLists.txt +++ b/src/Airports/GenAirports850/CMakeLists.txt @@ -1,20 +1,17 @@ - - add_executable(genapts850 - apt_surface.hxx apt_surface.cxx - build.cxx build.hxx + airport.hxx airport.cxx + apt_surface.hxx apt_surface.cxx + beznode.hxx + closedpoly.hxx closedpoly.cxx + convex_hull.hxx convex_hull.cxx elevations.cxx elevations.hxx global.hxx - heli_gen.cxx heli_gen.hxx - lights.hxx lights.cxx - main.cxx + linearfeature.hxx linearfeature.cxx + main.cxx + parser.hxx parser.cxx point2d.cxx point2d.hxx poly_extra.cxx poly_extra.hxx - runway.cxx runway.hxx - rwy_common.cxx rwy_common.hxx - rwy_gen.cxx rwy_gen.hxx - rwy_simple.cxx rwy_simple.hxx - taxiway.cxx taxiway.hxx + runway.cxx runway.hxx texparams.hxx ) @@ -25,6 +22,7 @@ target_link_libraries(genapts850 ${SIMGEAR_CORE_LIBRARIES} ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} ${GPC_LIBRARY} - ${NEWMAT_LIBRARY}) + ${NEWMAT_LIBRARY} + osg osgDB osgGA osgUtil osgViewer) install(TARGETS genapts850 RUNTIME DESTINATION bin) diff --git a/src/Airports/GenAirports850/airport.cxx b/src/Airports/GenAirports850/airport.cxx new file mode 100644 index 00000000..466ef46f --- /dev/null +++ b/src/Airports/GenAirports850/airport.cxx @@ -0,0 +1,2048 @@ +#include "beznode.hxx" +#include "runway.hxx" +#include "airport.hxx" + +#include <list> +#include <map> +#include <string> + +#include <simgear/compiler.h> +#include <simgear/structure/exception.hxx> +#include <simgear/debug/logstream.hxx> +#include <simgear/bucket/newbucket.hxx> +#include <simgear/math/sg_geodesy.hxx> +#include <simgear/math/SGGeometry.hxx> +#include <simgear/io/sg_binobj.hxx> +#include <simgear/misc/texcoord.hxx> + +#include <Polygon/polygon.hxx> +#include <Polygon/superpoly.hxx> +#include <Polygon/chop.hxx> + +#include <Geometry/poly_support.hxx> +#include <Output/output.hxx> + +#include "elevations.hxx" +#include "poly_extra.hxx" + +Airport::Airport( int c, char* def) +{ + int numParams; + char d[100]; + char tmp[16]; + int x, y; + + code = c; + + SG_LOG(SG_GENERAL, SG_DEBUG, "sscanf " << def); + numParams = sscanf(def, "%d %d %d %s %ls", &altitude, &x, &y, tmp, d); + SG_LOG(SG_GENERAL, SG_DEBUG, "done "); + SG_LOG(SG_GENERAL, SG_DEBUG, "got " << altitude << ", " << tmp << ", " << d); + + altitude *= SG_FEET_TO_METER; + icao = tmp; + description = d; +} + +void Airport::BuildOsg( osg::Group* airport ) +{ + // draw for OSG + int i; + +#if 0 + for (i=0; i<runways.size(); i++) + { + runways.at(i)->BuildOsg( airport ); + } +#endif + + for (i=0; i<pavements.size(); i++) + { + SG_LOG(SG_GENERAL, SG_DEBUG, " Adding pavement " << i << "to airport"); + pavements.at(i)->BuildOsg(airport); + } + +#if 0 + for (i=0; i<features.size(); i++) + { + features.at(i)->Finish(airport); + } +#endif +} + + +// TODO: Add to object +// calculate texture coordinates for runway section using the provided +// texturing parameters. Returns a mirror polygon to the runway, +// except each point is the texture coordinate of the corresponding +// point in the original polygon. +static TGPolygon rwy_section_tex_coords( const TGPolygon& in_poly, const TGTexParams& tp, const bool clip_result ) +{ + int i, j; + TGPolygon result; + result.erase(); + // double length = rwy.length * SG_FEET_TO_METER; + // double width = rwy.width * SG_FEET_TO_METER; + + 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 ); + Point3D p, t; + double x, y, tx, ty; + + for ( i = 0; i < in_poly.contours(); ++i ) { + for ( j = 0; j < in_poly.contour_size( i ); ++j ) { + p = in_poly.get_pt( i, j ); + SG_LOG(SG_GENERAL, SG_DEBUG, "point = " << p); + + // + // 1. Calculate distance and bearing from the center of + // the runway + // + + // 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 = " << 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; + // tx = ((int)(tx * 100)) / 100.0; + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ")"); + + if ( clip_result) { + if ( tx < 0.0 ) { tx = 0.0; } + if ( tx > 1.0 ) { tx = 1.0; } + } + + // ty = (y - min.y()) / (max.y() - min.y()); + ty = y / length; + tmp = y / length; + ty = tmp * (maxv - minv) + minv; + // ty = ((int)(ty * 100)) / 100.0; + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << ty << ")"); + + if ( clip_result ) { + if ( ty < 0.0 ) { ty = 0.0; } + if ( ty > 1.0 ) { ty = 1.0; } + } + + t = Point3D( tx, ty, 0 ); + SG_LOG(SG_GENERAL, SG_DEBUG, " (" << tx << ", " << ty << ")"); + + result.add_node( i, t ); + } + } + + return result; +} + +// TODO : Add somewhere +// Determine node elevations of a point_list based on the provided +// TGAptSurface. Offset is added to the final elevation +static point_list calc_elevations( TGAptSurface &surf, + const point_list& geod_nodes, + double offset ) +{ + point_list result = geod_nodes; + for ( unsigned int i = 0; i < result.size(); ++i ) { + double elev = surf.query( result[i].lon(), result[i].lat() ); + result[i].setelev( elev + offset ); + } + + return result; +} + + +// Determine node elevations of each node of a TGPolygon based on the +// provided TGAptSurface. Offset is added to the final elevation +static TGPolygon calc_elevations( TGAptSurface &surf, + const TGPolygon& poly, + double offset ) +{ + TGPolygon result; + for ( int i = 0; i < poly.contours(); ++i ) { + point_list contour = poly.get_contour( i ); + point_list elevated = calc_elevations( surf, contour, offset ); + + result.add_contour( elevated, poly.get_hole_flag(i) ); + } + + return result; +} + +void Airport::BuildBtg(const string& root, const string_list& elev_src ) +{ + TGPolygon apt_base; + TGPolygon apt_clearing; + TGPolygon accum; + accum.erase(); + + superpoly_list rwy_polys; + texparams_list texparams; + int i, j, k; + char buf[120]; // For debugging output + Point3D p; + + // parse main airport information + double apt_lon = 0.0, apt_lat = 0.0; + int rwy_count = 0; + + // Find the average of all the runway long / lats + // TODO : Need runway object... + + + for (i=0; i<runways.size(); i++) + { + printf("runway %d from (%lf,%lf) to (%lf,%lf) : midpoint is (%lf,%lf)\n", + i, + runways[i]->GetStart().x(), runways[i]->GetStart().y(), + runways[i]->GetEnd().x(), runways[i]->GetEnd().y(), + runways[i]->GetMidpoint().x(), runways[i]->GetMidpoint().y() + ); + + apt_lon += runways.at(i)->GetMidpoint().x(); + apt_lat += runways.at(i)->GetMidpoint().y(); + } + apt_lon = apt_lon / (double)runways.size(); + apt_lat = apt_lat / (double)runways.size(); + + printf("Average is (%lf,%lf\n", apt_lon, apt_lat); + + SGBucket b( apt_lon, apt_lat ); + SG_LOG(SG_GENERAL, SG_INFO, b.gen_base_path() << "/" << b.gen_index_str()); + + // Build precision runways first + for (i=0; i<runways.size(); i++ ) + { + if ( runways[i]->IsPrecision() ) + { + runways[i]->BuildBtg( altitude, &rwy_polys, &texparams, &accum, &apt_base, &apt_clearing ); + } + } + + // Now generate pavements + if (pavements.size()) + { + for ( i=0; i<pavements.size(); i++ ) + { + SG_LOG(SG_GENERAL, SG_ALERT, "Build Pavement Poly " << i); + pavements[i]->BuildBtg( altitude, &rwy_polys, &texparams, &accum, &apt_base, &apt_clearing ); + } + } + else + { + SG_LOG(SG_GENERAL, SG_ALERT, "no pavements"); + } + + // wipe out the pavements to save memory + pavements.clear(); + + // Then the linear features + for ( i=0; i< 1; i++ ) + { + // not yet... + } + + if ( apt_base.total_size() == 0 ) + { + SG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated"); + return; + } + else + { + SG_LOG(SG_GENERAL, SG_ALERT, "Lookin good"); + } + + TGPolygon filled_base = tgPolygonStripHoles( apt_base ); + TGPolygon divided_base = tgPolygonSplitLongEdges( filled_base, 200.0 ); + TGPolygon base_poly = tgPolygonDiff( divided_base, accum ); + + // do this when generating the polys themselves... + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes/remove dups section = " << k << " " << rwy_polys[k].get_material()); + TGPolygon poly = rwy_polys[k].get_poly(); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size()); + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + poly = remove_dups( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size()); + + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + poly = reduce_degeneracy( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after reduce_degeneracy() = " << poly.total_size()); + + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + rwy_polys[k].set_poly( poly ); + } + // END do this when generating polys + + // add segments to polygons to remove any possible "T" + // intersections + TGTriNodes tmp_nodes; + + // build temporary node list + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( poly.get_pt(i, j) ); + } + } + } + for ( i = 0; i < base_poly.contours(); ++i ) + { + for ( j = 0; j < base_poly.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( base_poly.get_pt(i, j) ); + } + } + + // the divided base could contain points not found in base_poly, + // so we should add them because the skirt needs them. + for ( i = 0; i < divided_base.contours(); ++i ) + { + for ( j = 0; j < divided_base.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( divided_base.get_pt(i, j) ); + } + } + + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + poly = add_nodes_to_poly( poly, tmp_nodes ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size()); + + rwy_polys[k].set_poly( poly ); + } + + // One more pass to try to get rid of other yukky stuff + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + + SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size()); + + poly = remove_dups( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size()); + poly = remove_bad_contours( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_bad() = " << poly.total_size()); + poly = remove_tiny_contours( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_tiny_contours() = " << poly.total_size()); + + rwy_polys[k].set_poly( poly ); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes base "); + SG_LOG(SG_GENERAL, SG_DEBUG, " before: " << base_poly); + SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_nodes size = " << tmp_nodes.get_node_list().size()); + + base_poly = add_nodes_to_poly( base_poly, tmp_nodes ); + SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly); + + // write_polygon( base_poly, "base-add" ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove dups base "); + base_poly = remove_dups( base_poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove bad contours base"); + base_poly = remove_bad_contours( base_poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove small contours base"); + base_poly = remove_tiny_contours( base_poly ); + // write_polygon( base_poly, "base-fin" ); + SG_LOG(SG_GENERAL, SG_DEBUG, " after clean up: " << base_poly); + + // tesselate the polygons and prepair them for final output + for ( i = 0; i < (int)rwy_polys.size(); ++i ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating section = " << i << " flag = " << rwy_polys[i].get_flag()); + + TGPolygon poly = rwy_polys[i].get_poly(); + SG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << poly.contours() << " total points before = " << poly.total_size()); + TGPolygon tri = polygon_tesselate_alt( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after = " << tri.total_size()); + + TGPolygon tc; + if ( rwy_polys[i].get_flag() == "taxi" ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "taxiway, no clip"); + tc = rwy_section_tex_coords( tri, texparams[i], false ); + } + else + { + tc = rwy_section_tex_coords( tri, texparams[i], true ); + } + + rwy_polys[i].set_tris( tri ); + rwy_polys[i].set_texcoords( tc ); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating base"); + TGPolygon base_tris = polygon_tesselate_alt( base_poly ); + + // + // We should now have the runway polygons all generated with their + // corresponding triangles and texture coordinates, and the + // surrounding base area. + // + // Next we need to create the output lists ... vertices, normals, + // texture coordinates, and tri-strips + // + + // traverse the tri list and create ordered node and texture + // coordinate lists + + TGTriNodes nodes, normals, texcoords; + nodes.clear(); + 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; + + // calculate "the" normal for this airport + p.setx( base_tris.get_pt(0, 0).x() * SGD_DEGREES_TO_RADIANS ); + p.sety( base_tris.get_pt(0, 0).y() * SGD_DEGREES_TO_RADIANS ); + p.setz( 0 ); + Point3D vnt = sgGeodToCart( p ); + + // SG_LOG(SG_GENERAL, SG_DEBUG, "geod = " << p); + // SG_LOG(SG_GENERAL, SG_DEBUG, "cart = " << tmp); + + SGVec3d tmp( vnt.x(), vnt.y(), vnt.z() ); + tmp = normalize(tmp); + Point3D vn( tmp.x(), tmp.y(), tmp.z() ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "found normal for this airport = " << tmp); + + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); + // TGPolygon tri_poly = rwy_tris[k]; + TGPolygon tri_poly = rwy_polys[k].get_tris(); + TGPolygon tri_txs = rwy_polys[k].get_texcoords(); + string material = rwy_polys[k].get_material(); + SG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); + SG_LOG(SG_GENERAL, SG_DEBUG, "poly size = " << tri_poly.contours()); + SG_LOG(SG_GENERAL, SG_DEBUG, "texs size = " << tri_txs.contours()); + for ( i = 0; i < tri_poly.contours(); ++i ) + { + tri_v.clear(); + tri_n.clear(); + tri_tc.clear(); + for ( j = 0; j < tri_poly.contour_size(i); ++j ) + { + p = tri_poly.get_pt( i, j ); + index = nodes.unique_add( p ); + tri_v.push_back( index ); + + // use 'the' normal + index = normals.unique_add( vn ); + tri_n.push_back( index ); + + Point3D tc = tri_txs.get_pt( i, j ); + 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 ); + } + } + + // add base points + std::vector< SGVec2f > base_txs; + int_list base_tc; + + for ( i = 0; i < base_tris.contours(); ++i ) + { + tri_v.clear(); + tri_n.clear(); + tri_tc.clear(); + for ( j = 0; j < base_tris.contour_size(i); ++j ) + { + p = base_tris.get_pt( i, j ); + index = nodes.unique_add( p ); + tri_v.push_back( index ); + + index = normals.unique_add( vn ); + tri_n.push_back( index); + } + tris_v.push_back( tri_v ); + tris_n.push_back( tri_n ); + tri_materials.push_back( "Grass" ); + + std::vector < SGGeod > geodNodes; + for ( j = 0; j < nodes.get_node_list().size(); j++ ) + { + Point3D node = nodes.get_node_list()[j]; + geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) ); + } + base_txs.clear(); + base_txs = sgCalcTexCoords( b, geodNodes, tri_v ); + + base_tc.clear(); + for ( j = 0; j < (int)base_txs.size(); ++j ) + { + SGVec2f tc = base_txs[j]; + // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc); + index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) ); + base_tc.push_back( index ); + } + tris_tc.push_back( base_tc ); + } + + // on rare occasion, one or more of the divided base points can be + // missed. Make sure they are all in the node list so we can + // build a proper skirt. + + for ( i = 0; i < divided_base.contours(); ++i ) + { + for ( j = 0; j < divided_base.contour_size( i ); ++j ) + { + nodes.unique_add( divided_base.get_pt(i, j) ); + } + } + + // Now that we have assembled all the airport geometry nodes into + // a list, calculate an "average" airport elevation based on all + // the actual airport node points. This is more useful than + // calculating an average over the entire airport surface because + // it avoids biases introduced from the surrounding area if the + // airport is located in a bowl or on a hill. + + double average = tgAverageElevation( root, elev_src, nodes.get_node_list() ); + // cout << "average airport elevation = " << average << endl; + + // Now build the fitted airport surface ... + + // calculation min/max coordinates of airport area + Point3D min_deg(9999.0, 9999.0, 0), max_deg(-9999.0, -9999.0, 0); + for ( j = 0; j < (int)nodes.get_node_list().size(); ++j ) + { + Point3D p = nodes.get_node_list()[j]; + if ( p.lon() < min_deg.lon() ) + { + min_deg.setlon( p.lon() ); + } + if ( p.lon() > max_deg.lon() ) + { + max_deg.setlon( p.lon() ); + } + if ( p.lat() < min_deg.lat() ) + { + min_deg.setlat( p.lat() ); + } + if ( p.lat() > max_deg.lat() ) + { + max_deg.setlat( p.lat() ); + } + } + +#if 0 + // extend the min/max coordinates of airport area to cover all + // lights as well + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + for ( j = 0; + j < (int)rwy_lights[i].get_poly().get_contour(0).size(); + ++j ) + { + Point3D p = rwy_lights[i].get_poly().get_contour(0)[j]; + if ( p.lon() < min_deg.lon() ) + { + min_deg.setlon( p.lon() ); + } + if ( p.lon() > max_deg.lon() ) + { + max_deg.setlon( p.lon() ); + } + if ( p.lat() < min_deg.lat() ) + { + min_deg.setlat( p.lat() ); + } + if ( p.lat() > max_deg.lat() ) + { + max_deg.setlat( p.lat() ); + } + } + } +#endif + + // need newmat.... + + // Extend the area a bit so we don't have wierd things on the edges + double dlon = max_deg.lon() - min_deg.lon(); + double dlat = max_deg.lat() - min_deg.lat(); + min_deg.setlon( min_deg.lon() - 0.01 * dlon ); + max_deg.setlon( max_deg.lon() + 0.01 * dlon ); + min_deg.setlat( min_deg.lat() - 0.01 * dlat ); + max_deg.setlat( max_deg.lat() + 0.01 * dlat ); + SG_LOG(SG_GENERAL, SG_DEBUG, "min = " << min_deg << " max = " << max_deg ); + + TGAptSurface apt_surf( root, elev_src, min_deg, max_deg, average ); + SG_LOG(SG_GENERAL, SG_INFO, "Airport surface created"); + + // calculate node elevations + SG_LOG(SG_GENERAL, SG_INFO, "Computing airport node elevations"); + point_list geod_nodes = calc_elevations( apt_surf, + nodes.get_node_list(), + 0.0 ); + divided_base = calc_elevations( apt_surf, divided_base, 0.0 ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "DIVIDED"); + SG_LOG(SG_GENERAL, SG_DEBUG, divided_base); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Done with base calc_elevations()"); + +#if 0 + SG_LOG(SG_GENERAL, SG_INFO, "Computing beacon node elevations"); + point_list beacon_nodes = calc_elevations( apt_surf, beacons, 0.0 ); + SG_LOG(SG_GENERAL, SG_INFO, "Computing tower node elevations"); + point_list tower_nodes = calc_elevations( apt_surf, towers, 0.0 ); + SG_LOG(SG_GENERAL, SG_INFO, "Computing windsock node elevations"); + point_list windsock_nodes = calc_elevations( apt_surf, windsocks, 0.0 ); +#endif + + // add base skirt (to hide potential cracks) + // + // this has to happen after we've calculated the node elevations + // but before we convert to wgs84 coordinates + + int uindex, lindex; + + for ( i = 0; i < divided_base.contours(); ++i ) + { + strip_v.clear(); + strip_n.clear(); + strip_tc.clear(); + + // prime the pump ... + p = divided_base.get_pt( i, 0 ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + // use 'the' normal. We are pushing on two nodes so we + // need to push on two normals. + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (in init)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + + // loop through the list + for ( j = 1; j < divided_base.contour_size(i); ++j ) + { + p = divided_base.get_pt( i, j ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (in loop)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + } + + // close off the loop + p = divided_base.get_pt( i, 0 ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (at end)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + + strips_v.push_back( strip_v ); + strips_n.push_back( strip_n ); + strip_materials.push_back( "Grass" ); + + std::vector < SGGeod > geodNodes; + for ( j = 0; j < nodes.get_node_list().size(); j++ ) + { + Point3D node = nodes.get_node_list()[j]; + geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) ); + } + base_txs.clear(); + base_txs = sgCalcTexCoords( b, geodNodes, strip_v ); + + base_tc.clear(); + for ( j = 0; j < (int)base_txs.size(); ++j ) + { + SGVec2f tc = base_txs[j]; + // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc); + index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) ); + base_tc.push_back( index ); + } + strips_tc.push_back( base_tc ); + } + + +#if 0 + // add light points + + superpoly_list tmp_light_list; tmp_light_list.clear(); + typedef map < string, double, less<string> > elev_map_type; + typedef elev_map_type::const_iterator const_elev_map_iterator; + elev_map_type elevation_map; + + SG_LOG(SG_GENERAL, SG_INFO, "Computing runway/approach lighting elevations"); + + // pass one, calculate raw elevations from Array + + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + TGTriNodes light_nodes; + light_nodes.clear(); + point_list lights_v = rwy_lights[i].get_poly().get_contour(0); + for ( j = 0; j < (int)lights_v.size(); ++j ) + { + p = lights_v[j]; + index = light_nodes.simple_add( p ); + } + + // calculate light node elevations + + point_list geod_light_nodes = calc_elevations( apt_surf, light_nodes.get_node_list(), 0.5 ); + TGPolygon p; + p.add_contour( geod_light_nodes, 0 ); + TGSuperPoly s; + s.set_poly( p ); + tmp_light_list.push_back( s ); + + string flag = rwy_lights[i].get_flag(); + if ( flag != (string)"" ) + { + double max = -9999; + const_elev_map_iterator it = elevation_map.find( flag ); + if ( it != elevation_map.end() ) + { + max = elevation_map[flag]; + } + for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) + { + if ( geod_light_nodes[j].z() > max ) + { + max = geod_light_nodes[j].z(); + } + } + elevation_map[flag] = max; + SG_LOG( SG_GENERAL, SG_DEBUG, flag << " max = " << max ); + } + } + + SG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations()"); + + // pass two, for each light group check if we need to lift (based + // on flag) and do so, then output next structures. + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + // tmp_light_list is a parallel structure to rwy_lights + point_list geod_light_nodes = tmp_light_list[i].get_poly().get_contour(0); + + // this is a little round about, but what we want to calculate the + // light node elevations as ground + an offset so we do them + // seperately, then we add them back into nodes to get the index + // out, but also add them to geod_nodes to maintain consistancy + // between these two lists. + point_list light_normals = rwy_lights[i].get_normals().get_contour(0); + pt_v.clear(); + pt_n.clear(); + for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) + { + p = geod_light_nodes[j]; + index = nodes.simple_add( p ); + pt_v.push_back( index ); + geod_nodes.push_back( p ); + + index = normals.unique_add( light_normals[j] ); + pt_n.push_back( index ); + } + pts_v.push_back( pt_v ); + pts_n.push_back( pt_n ); + pt_materials.push_back( rwy_lights[i].get_material() ); + } +#endif + + // calculate wgs84 mapping of nodes + std::vector< SGVec3d > wgs84_nodes; + for ( i = 0; i < (int)geod_nodes.size(); ++i ) + { + SGGeod geod = SGGeod::fromDegM( geod_nodes[i].x(), geod_nodes[i].y(), geod_nodes[i].z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << geod_nodes[i] ); + SGVec3d cart = SGVec3d::fromGeod(geod); + SG_LOG(SG_GENERAL, SG_DEBUG, " cart pt = " << cart ); + wgs84_nodes.push_back( cart ); + } + SGSphered d; + for ( i = 0; i < 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 objpath = root + "/AirportObj"; + string name = icao + ".btg"; + + std::vector< SGVec3f > normals_3f; + for ( i=0; i < 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 ( i=0; i < 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( objpath, name, b ); + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + + // write out airport object reference + write_index( objpath, b, name ); + +#if 0 + // write out beacon references + for ( i = 0; i < (int)beacon_nodes.size(); ++i ) + { + write_index_shared( objpath, b, beacon_nodes[i], + "Models/Airport/beacon.xml", + 0.0 ); + } + + // write out tower references + for ( i = 0; i < (int)tower_nodes.size(); ++i ) + { + write_index_shared( objpath, b, tower_nodes[i], + "Models/Airport/tower.xml", + 0.0 ); + } + + // write out windsock references + for ( i = 0; i < (int)windsock_nodes.size(); ++i ) + { + if ( windsock_types[i] == 0 ) + { + write_index_shared( objpath, b, windsock_nodes[i], + "Models/Airport/windsock.xml", + 0.0 ); + } + else + { + write_index_shared( objpath, b, windsock_nodes[i], + "Models/Airport/windsock_lit.xml", + 0.0 ); + } + } +#endif + + string holepath = root + "/AirportArea"; + // long int poly_index = poly_index_next(); + // write_boundary( holepath, b, hull, poly_index ); + tgChopNormalPolygon( holepath, "Hole", divided_base, true ); + tgChopNormalPolygon( holepath, "Airport", apt_clearing, false ); +} + + +#if 0 // RFERENCE from GenApts.... + +// build 3d airport +void build_airport( string airport_id, float alt_m, + string_list& runways_raw, + string_list& beacons_raw, + string_list& towers_raw, + string_list& windsocks_raw, + const string& root, + const string_list& elev_src ) +{ + int i, j, k; + + superpoly_list rwy_polys; + texparams_list texparams; + + // poly_list rwy_tris, rwy_txs; + TGPolygon runway, runway_a, runway_b, clipped_a, clipped_b; + TGPolygon split_a, split_b; + TGPolygon apt_base; + TGPolygon apt_clearing; + Point3D p; + + TGPolygon accum; + accum.erase(); + + // parse main airport information + double apt_lon = 0.0, apt_lat = 0.0; + int rwy_count = 0; + + SG_LOG( SG_GENERAL, SG_INFO, "Building " << airport_id ); + + // parse runways/taxiways and generate the vertex list + runway_list runways; runways.clear(); + runway_list taxiways; taxiways.clear(); + + for ( i = 0; i < (int)runways_raw.size(); ++i ) { + ++rwy_count; + + string rwy_str = runways_raw[i]; + vector<string> token = simgear::strutils::split( rwy_str ); + + TGRunway rwy; + + SG_LOG(SG_GENERAL, SG_DEBUG, rwy_str); + rwy.rwy_no = token[3]; + rwy.really_taxiway = (rwy.rwy_no == "xxx"); + rwy.generated = false; + + rwy.lat = atof( token[1].c_str() ); + apt_lat += rwy.lat; + + rwy.lon = atof( token[2].c_str() ); + apt_lon += rwy.lon; + + rwy.heading = atof( token[4].c_str() ); + + rwy.length = atoi( token[5].c_str() ); + rwy.width = atoi( token[8].c_str() ); + + + string rwy_displ_threshold = token[6]; + vector<string> displ + = simgear::strutils::split( rwy_displ_threshold, "." ); + rwy.disp_thresh1 = atoi( displ[0].c_str() ); + rwy.disp_thresh2 = atoi( displ[1].c_str() ); + + string rwy_stopway = token[7]; + vector<string> stop + = simgear::strutils::split( rwy_stopway, "." ); + rwy.stopway1 = atoi( stop[0].c_str() ); + rwy.stopway2 = atoi( stop[1].c_str() ); + + rwy.lighting_flags = token[9]; + rwy.surface_code = atoi( token[10].c_str() ); + rwy.shoulder_code = token[11]; + rwy.marking_code = atoi( token[12].c_str() ); + rwy.smoothness = atof( token[13].c_str() ); + rwy.dist_remaining = (atoi( token[14].c_str() ) == 1 ); + + if (token.size()>15) { + string vasi_angles = token[15]; + vector<string> vasis = simgear::strutils::split( vasi_angles, "." ); + rwy.gs_angle1 = atof( vasis[0].c_str() ) * 0.01; + rwy.gs_angle2 = atof( vasis[1].c_str() ) * 0.01; + } else { + rwy.gs_angle1 = rwy.gs_angle2 = 3.0; + } + + SG_LOG( SG_GENERAL, SG_DEBUG, " no = " << rwy.rwy_no); + SG_LOG( SG_GENERAL, SG_DEBUG, " lat = " << rwy.lat); + SG_LOG( SG_GENERAL, SG_DEBUG, " lon = " << rwy.lon); + SG_LOG( SG_GENERAL, SG_DEBUG, " hdg = " << rwy.heading); + SG_LOG( SG_GENERAL, SG_DEBUG, " len = " << rwy.length); + SG_LOG( SG_GENERAL, SG_DEBUG, " width = " << rwy.width); + SG_LOG( SG_GENERAL, SG_DEBUG, " lighting = " << rwy.lighting_flags); + SG_LOG( SG_GENERAL, SG_DEBUG, " sfc = " << rwy.surface_code); + SG_LOG( SG_GENERAL, SG_DEBUG, " mrkgs = " << rwy.marking_code); + SG_LOG( SG_GENERAL, SG_DEBUG, " dspth1= " << rwy.disp_thresh1); + SG_LOG( SG_GENERAL, SG_DEBUG, " stop1 = " << rwy.stopway1); + SG_LOG( SG_GENERAL, SG_DEBUG, " dspth2= " << rwy.disp_thresh2); + SG_LOG( SG_GENERAL, SG_DEBUG, " stop2 = " << rwy.stopway2); + + if ( rwy.really_taxiway ) { + taxiways.push_back( rwy ); + } else { + runways.push_back( rwy ); + } + } + SG_LOG(SG_GENERAL, SG_INFO, "Runway count = " << runways.size() ); + SG_LOG(SG_GENERAL, SG_INFO, "Taxiway count = " << taxiways.size() ); + + + SGBucket b( apt_lon / (double)rwy_count, apt_lat / (double)rwy_count ); + SG_LOG(SG_GENERAL, SG_INFO, b.gen_base_path() << "/" << b.gen_index_str()); + + point_list beacons; beacons.clear(); + for ( i = 0; i < (int)beacons_raw.size(); ++i ) { + string beacon_str = beacons_raw[i]; + vector<string> token = simgear::strutils::split( beacon_str ); + + Point3D beacon; + + SG_LOG(SG_GENERAL, SG_INFO, beacon_str); + + beacon.setlat( atof( token[1].c_str() ) ); + beacon.setlon( atof( token[2].c_str() ) ); + + SG_LOG( SG_GENERAL, SG_DEBUG, " beacon = " << beacon ); + + beacons.push_back( beacon ); + } + + point_list towers; towers.clear(); + for ( i = 0; i < (int)towers_raw.size(); ++i ) { + string tower_str = towers_raw[i]; + vector<string> token = simgear::strutils::split( tower_str ); + + Point3D tower; + + SG_LOG(SG_GENERAL, SG_INFO, tower_str); + + tower.setlat( atof( token[1].c_str() ) ); + tower.setlon( atof( token[2].c_str() ) ); + + if (!atoi(token[4].c_str())) { + // Ralf Gerlich: Skip towers that shall not be drawn + continue; + } + + SG_LOG( SG_GENERAL, SG_DEBUG, " tower = " << tower ); + + towers.push_back( tower ); + } + + point_list windsocks; windsocks.clear(); + int_list windsock_types; windsock_types.clear(); + for ( i = 0; i < (int)windsocks_raw.size(); ++i ) { + string windsock_str = windsocks_raw[i]; + vector<string> token = simgear::strutils::split( windsock_str ); + + Point3D windsock; + + SG_LOG(SG_GENERAL, SG_INFO, windsock_str); + + windsock.setlat( atof( token[1].c_str() ) ); + windsock.setlon( atof( token[2].c_str() ) ); + + string windsock_type = token[3]; + + SG_LOG( SG_GENERAL, SG_DEBUG, " windsock = " << windsock ); + + windsocks.push_back( windsock ); + if ( windsock_type == "0" ) { + windsock_types.push_back( 0 ); + } else { + windsock_types.push_back( 1 ); + } + } + + TGSuperPoly sp; + TGTexParams tp; + + + // First pass: generate the precision runways since these have + // precidence + for ( i = 0; i < (int)runways.size(); ++i ) { + if ( runways[i].marking_code == 3 /* Precision */ ) { + build_runway( runways[i], alt_m, + &rwy_polys, &texparams, &accum, + &apt_base, &apt_clearing ); + } + } + + // 2nd pass: generate the non-precision and visual runways + for ( i = 0; i < (int)runways.size(); ++i ) { + if ( runways[i].marking_code == 2 /* Non-precision */ + || runways[i].marking_code == 1 /* Visual */ ) + { + if ( runways[i].surface_code != 13 /* Water */ ) { + // only build non-water runways + build_runway( runways[i], alt_m, + &rwy_polys, &texparams, &accum, + &apt_base, &apt_clearing ); + } + } + } + + // 3rd pass: generate all remaining runways not covered in the first pass + for ( i = 0; i < (int)runways.size(); ++i ) { + if ( runways[i].marking_code != 3 /* Precision */ + && runways[i].marking_code != 2 /* Non-precision */ + && runways[i].marking_code != 1 /* Visual */ ) + { + if ( runways[i].surface_code != 6 /* Asphalt Helipad */ && + runways[i].surface_code != 7 /* Concrete Helipad */ && + runways[i].surface_code != 8 /* Turf Helipad */ && + runways[i].surface_code != 9 /* Dirt Helipad */ && + runways[i].surface_code != 13 /* Water/buoy runway */ ) + { + // only build non-water and non-heliport runways + build_runway( runways[i], alt_m, + &rwy_polys, &texparams, &accum, + &apt_base, &apt_clearing ); + } + } + } + + // 4th pass: generate all taxiways + +#if 0 + // we want to generate in order of largest size first so this will + // look a little weird, but that's all I'm doing, otherwise a + // simple list traversal would work fine. + bool done = false; + while ( !done ) { + // find the largest taxiway + int largest_idx = -1; + double max_size = 0; + for ( i = 0; i < (int)taxiways.size(); ++i ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "taxiway i = " << i ); + double size = taxiways[i].length * taxiways[i].width; + if ( size > max_size && !taxiways[i].generated ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "taxiway max i = " << i ); + max_size = size; + largest_idx = i; + } + } + + if ( largest_idx >= 0 ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "generating " << largest_idx ); + build_runway( taxiways[largest_idx], alt_m, + &rwy_polys, &texparams, &accum, + &apt_base, &apt_clearing ); + taxiways[largest_idx].generated = true; + } else { + SG_LOG( SG_GENERAL, SG_DEBUG, "done with taxiways." ); + done = true; + } + } +#else + /* Ralf Gerlich: Generate Taxiways in specified order from bottom to top */ + for ( i=0; i<taxiways.size(); ++i ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "generating " << i ); + build_runway( taxiways[i], alt_m, + &rwy_polys, &texparams, &accum, + &apt_base, &apt_clearing ); + taxiways[i].generated = true; + } +#endif + + // Now generate small surface for each beacon + TGPolygon obj_base, obj_safe_base; + double obj_hdg = runways[0].heading; + for ( i = 0; i < (int)beacons.size(); ++i ) { + obj_base = gen_wgs84_area( beacons[i], 20.0, 0.0, 0.0, 20.0, + obj_hdg, alt_m, false ); + obj_safe_base = gen_wgs84_area( beacons[i], 40.0, 0.0, 0.0, 40.0, + obj_hdg, alt_m, false ); + + apt_base = tgPolygonUnion( obj_base, apt_base ); + apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing ); + } + + // Now generate small surface for each tower + for ( i = 0; i < (int)towers.size(); ++i ) { + obj_base = gen_wgs84_area( towers[i], 20.0, 0.0, 0.0, 20.0, + obj_hdg, alt_m, false ); + obj_safe_base = gen_wgs84_area( towers[i], 40.0, 0.0, 0.0, 40.0, + obj_hdg, alt_m, false ); + + apt_base = tgPolygonUnion( obj_base, apt_base ); + apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing ); + } + + // Now generate small surface for each windsock + for ( i = 0; i < (int)windsocks.size(); ++i ) { + obj_base = gen_wgs84_area( windsocks[i], 20.0, 0.0, 0.0, 20.0, + obj_hdg, alt_m, false ); + obj_safe_base = gen_wgs84_area( windsocks[i], 40.0, 0.0, 0.0, 40.0, + obj_hdg, alt_m, false ); + + apt_base = tgPolygonUnion( obj_base, apt_base ); + apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing ); + } + + // 5th pass: generate runway lights + superpoly_list rwy_lights; rwy_lights.clear(); + for ( i = 0; i < (int)runways.size(); ++i ) { + gen_runway_lights( runways[i], alt_m, rwy_lights, &apt_base ); + } + + // 6th pass: generate all taxiway lights + for ( i = 0; i < (int)taxiways.size(); ++i ) { + gen_taxiway_lights( taxiways[i], alt_m, rwy_lights ); + } + + // write_polygon( accum, "accum" ); + // write_polygon( apt_base, "base" ); + // write_polygon( apt_clearing, "clear" ); + if ( apt_base.total_size() == 0 ) { + SG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated"); + return; + } + + // generate convex hull (no longer) + // TGPolygon hull = convex_hull(apt_pts); + + TGPolygon filled_base = tgPolygonStripHoles( apt_base ); + // write_polygon( filled_base, "base" ); + TGPolygon divided_base = tgPolygonSplitLongEdges( filled_base, 200.0 ); + // write_polygon( divided_base, "divided-base" ); + TGPolygon base_poly = tgPolygonDiff( divided_base, accum ); + // write_polygon( base_poly, "base-raw" ); + + char buf[120]; // For debugging output + // Try to remove duplicated nodes and other degeneracies + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes/remove dups section = " << k << " " << rwy_polys[k].get_material()); + TGPolygon poly = rwy_polys[k].get_poly(); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size()); + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + poly = remove_dups( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size()); + + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + poly = reduce_degeneracy( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after reduce_degeneracy() = " << poly.total_size()); + + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size(i); ++j ) + { + Point3D tmp = poly.get_pt(i, j); + snprintf(buf, 119, " %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, buf); + } + } + + rwy_polys[k].set_poly( poly ); + } + +********************************************************************************************************************************************************************************************************************** + // I AM HERE : I need to start creating the airport base + + // add segments to polygons to remove any possible "T" + // intersections + TGTriNodes tmp_nodes; + + // build temporary node list + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + for ( i = 0; i < poly.contours(); ++i ) + { + for ( j = 0; j < poly.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( poly.get_pt(i, j) ); + } + } + } + for ( i = 0; i < base_poly.contours(); ++i ) + { + for ( j = 0; j < base_poly.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( base_poly.get_pt(i, j) ); + } + } + + // the divided base could contain points not found in base_poly, + // so we should add them because the skirt needs them. + for ( i = 0; i < divided_base.contours(); ++i ) + { + for ( j = 0; j < divided_base.contour_size( i ); ++j ) + { + tmp_nodes.unique_add( divided_base.get_pt(i, j) ); + } + } + + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + poly = add_nodes_to_poly( poly, tmp_nodes ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size()); + + rwy_polys[k].set_poly( poly ); + } + + // One more pass to try to get rid of other yukky stuff + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + TGPolygon poly = rwy_polys[k].get_poly(); + + SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size()); + + poly = remove_dups( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size()); + poly = remove_bad_contours( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_bad() = " << poly.total_size()); + poly = remove_tiny_contours( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_tiny_contours() = " << poly.total_size()); + + rwy_polys[k].set_poly( poly ); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes base "); + SG_LOG(SG_GENERAL, SG_DEBUG, " before: " << base_poly); + SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_nodes size = " << tmp_nodes.get_node_list().size()); + + base_poly = add_nodes_to_poly( base_poly, tmp_nodes ); + SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly); + + // write_polygon( base_poly, "base-add" ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove dups base "); + base_poly = remove_dups( base_poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove bad contours base"); + base_poly = remove_bad_contours( base_poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "remove small contours base"); + base_poly = remove_tiny_contours( base_poly ); + // write_polygon( base_poly, "base-fin" ); + SG_LOG(SG_GENERAL, SG_DEBUG, " after clean up: " << base_poly); + + // tesselate the polygons and prepair them for final output + + for ( i = 0; i < (int)rwy_polys.size(); ++i ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating section = " << i << " flag = " << rwy_polys[i].get_flag()); + + TGPolygon poly = rwy_polys[i].get_poly(); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size()); + TGPolygon tri = polygon_tesselate_alt( poly ); + SG_LOG(SG_GENERAL, SG_DEBUG, "total size after = " << tri.total_size()); + + TGPolygon tc; + if ( rwy_polys[i].get_flag() == "taxi" ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "taxiway, no clip"); + tc = rwy_section_tex_coords( tri, texparams[i], false ); + } + else + { + tc = rwy_section_tex_coords( tri, texparams[i], true ); + } + + rwy_polys[i].set_tris( tri ); + rwy_polys[i].set_texcoords( tc ); + rwy_polys[i].set_tri_mode( GL_TRIANGLES ); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating base"); + TGPolygon base_tris = polygon_tesselate_alt( base_poly ); + + // + // We should now have the runway polygons all generated with their + // corresponding triangles and texture coordinates, and the + // surrounding base area. + // + // Next we need to create the output lists ... vertices, normals, + // texture coordinates, and tri-strips + // + + // traverse the tri list and create ordered node and texture + // coordinate lists + + TGTriNodes nodes, normals, texcoords; + nodes.clear(); + 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; + + // calculate "the" normal for this airport + p.setx( base_tris.get_pt(0, 0).x() * SGD_DEGREES_TO_RADIANS ); + p.sety( base_tris.get_pt(0, 0).y() * SGD_DEGREES_TO_RADIANS ); + p.setz( 0 ); + Point3D vnt = sgGeodToCart( p ); + + // SG_LOG(SG_GENERAL, SG_DEBUG, "geod = " << p); + // SG_LOG(SG_GENERAL, SG_DEBUG, "cart = " << tmp); + + sgdVec3 tmp; + sgdSetVec3( tmp, vnt.x(), vnt.y(), vnt.z() ); + sgdNormalizeVec3( tmp ); + + Point3D vn( tmp[0], tmp[1], tmp[2] ); + SG_LOG(SG_GENERAL, SG_DEBUG, "found normal for this airport = " << tmp); + + for ( k = 0; k < (int)rwy_polys.size(); ++k ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); + // TGPolygon tri_poly = rwy_tris[k]; + TGPolygon tri_poly = rwy_polys[k].get_tris(); + TGPolygon tri_txs = rwy_polys[k].get_texcoords(); + string material = rwy_polys[k].get_material(); + SG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); + SG_LOG(SG_GENERAL, SG_DEBUG, "poly size = " << tri_poly.contours()); + SG_LOG(SG_GENERAL, SG_DEBUG, "texs size = " << tri_txs.contours()); + for ( i = 0; i < tri_poly.contours(); ++i ) + { + tri_v.clear(); + tri_n.clear(); + tri_tc.clear(); + for ( j = 0; j < tri_poly.contour_size(i); ++j ) + { + p = tri_poly.get_pt( i, j ); + index = nodes.unique_add( p ); + tri_v.push_back( index ); + + // use 'the' normal + index = normals.unique_add( vn ); + tri_n.push_back( index ); + + Point3D tc = tri_txs.get_pt( i, j ); + 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 ); + } + } + + // add base points + std::vector< SGVec2f > base_txs; + int_list base_tc; + for ( i = 0; i < base_tris.contours(); ++i ) + { + tri_v.clear(); + tri_n.clear(); + tri_tc.clear(); + for ( j = 0; j < base_tris.contour_size(i); ++j ) + { + p = base_tris.get_pt( i, j ); + index = nodes.unique_add( p ); + tri_v.push_back( index ); + + index = normals.unique_add( vn ); + tri_n.push_back( index); + } + tris_v.push_back( tri_v ); + tris_n.push_back( tri_n ); + tri_materials.push_back( "Grass" ); + + std::vector < SGGeod > geodNodes; + for ( j = 0; j < nodes.get_node_list().size(); j++ ) + { + Point3D node = nodes.get_node_list()[j]; + geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) ); + } + base_txs.clear(); + base_txs = sgCalcTexCoords( b, geodNodes, tri_v ); + + base_tc.clear(); + for ( j = 0; j < (int)base_txs.size(); ++j ) + { + SGVec2f tc = base_txs[j]; + // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc); + index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) ); + base_tc.push_back( index ); + } + tris_tc.push_back( base_tc ); + } + + // on rare occasion, one or more of the divided base points can be + // missed. Make sure they are all in the node list so we can + // build a proper skirt. + + for ( i = 0; i < divided_base.contours(); ++i ) + { + for ( j = 0; j < divided_base.contour_size( i ); ++j ) + { + nodes.unique_add( divided_base.get_pt(i, j) ); + } + } + + // Now that we have assembled all the airport geometry nodes into + // a list, calculate an "average" airport elevation based on all + // the actual airport node points. This is more useful than + // calculating an average over the entire airport surface because + // it avoids biases introduced from the surrounding area if the + // airport is located in a bowl or on a hill. + + double average = tgAverageElevation( root, elev_src, nodes.get_node_list() ); + // cout << "average airport elevation = " << average << endl; + + // Now build the fitted airport surface ... + + // calculation min/max coordinates of airport area + Point3D min_deg(9999.0, 9999.0, 0), max_deg(-9999.0, -9999.0, 0); + for ( j = 0; j < (int)nodes.get_node_list().size(); ++j ) + { + Point3D p = nodes.get_node_list()[j]; + if ( p.lon() < min_deg.lon() ) + { + min_deg.setlon( p.lon() ); + } + if ( p.lon() > max_deg.lon() ) + { + max_deg.setlon( p.lon() ); + } + if ( p.lat() < min_deg.lat() ) + { + min_deg.setlat( p.lat() ); + } + if ( p.lat() > max_deg.lat() ) + { + max_deg.setlat( p.lat() ); + } + } + + // extend the min/max coordinates of airport area to cover all + // lights as well + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + for ( j = 0; + j < (int)rwy_lights[i].get_poly().get_contour(0).size(); + ++j ) + { + Point3D p = rwy_lights[i].get_poly().get_contour(0)[j]; + if ( p.lon() < min_deg.lon() ) + { + min_deg.setlon( p.lon() ); + } + if ( p.lon() > max_deg.lon() ) + { + max_deg.setlon( p.lon() ); + } + if ( p.lat() < min_deg.lat() ) + { + min_deg.setlat( p.lat() ); + } + if ( p.lat() > max_deg.lat() ) + { + max_deg.setlat( p.lat() ); + } + } + } + + // Extend the area a bit so we don't have wierd things on the edges + double dlon = max_deg.lon() - min_deg.lon(); + double dlat = max_deg.lat() - min_deg.lat(); + min_deg.setlon( min_deg.lon() - 0.01 * dlon ); + max_deg.setlon( max_deg.lon() + 0.01 * dlon ); + min_deg.setlat( min_deg.lat() - 0.01 * dlat ); + max_deg.setlat( max_deg.lat() + 0.01 * dlat ); + cout << "min = " << min_deg << " max = " << max_deg << endl; + + TGAptSurface apt_surf( root, elev_src, min_deg, max_deg, average ); + SG_LOG(SG_GENERAL, SG_INFO, "Airport surface created"); + + // calculate node elevations + SG_LOG(SG_GENERAL, SG_INFO, "Computing airport node elevations"); + point_list geod_nodes = calc_elevations( apt_surf, + nodes.get_node_list(), + 0.0 ); + divided_base = calc_elevations( apt_surf, divided_base, 0.0 ); + SG_LOG(SG_GENERAL, SG_DEBUG, "DIVIDED"); + SG_LOG(SG_GENERAL, SG_DEBUG, divided_base); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Done with base calc_elevations()"); + + SG_LOG(SG_GENERAL, SG_INFO, "Computing beacon node elevations"); + point_list beacon_nodes = calc_elevations( apt_surf, beacons, 0.0 ); + SG_LOG(SG_GENERAL, SG_INFO, "Computing tower node elevations"); + point_list tower_nodes = calc_elevations( apt_surf, towers, 0.0 ); + SG_LOG(SG_GENERAL, SG_INFO, "Computing windsock node elevations"); + point_list windsock_nodes = calc_elevations( apt_surf, windsocks, 0.0 ); + + // add base skirt (to hide potential cracks) + // + // this has to happen after we've calculated the node elevations + // but before we convert to wgs84 coordinates + int uindex, lindex; + + for ( i = 0; i < divided_base.contours(); ++i ) + { + strip_v.clear(); + strip_n.clear(); + strip_tc.clear(); + + // prime the pump ... + p = divided_base.get_pt( i, 0 ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + // use 'the' normal. We are pushing on two nodes so we + // need to push on two normals. + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (in init)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + + // loop through the list + for ( j = 1; j < divided_base.contour_size(i); ++j ) + { + p = divided_base.get_pt( i, j ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (in loop)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + } + + // close off the loop + p = divided_base.get_pt( i, 0 ); + uindex = nodes.find( p ); + if ( uindex >= 0 ) + { + Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20); + SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower); + lindex = nodes.simple_add( lower ); + geod_nodes.push_back( lower ); + strip_v.push_back( lindex ); + strip_v.push_back( uindex ); + + index = normals.unique_add( vn ); + strip_n.push_back( index ); + strip_n.push_back( index ); + } + else + { + string message = "Ooops missing node when building skirt (at end)"; + SG_LOG( SG_GENERAL, SG_INFO, message << " " << p ); + throw sg_exception( message ); + } + + strips_v.push_back( strip_v ); + strips_n.push_back( strip_n ); + strip_materials.push_back( "Grass" ); + + std::vector < SGGeod > geodNodes; + for ( j = 0; j < nodes.get_node_list().size(); j++ ) + { + Point3D node = nodes.get_node_list()[j]; + geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) ); + } + base_txs.clear(); + base_txs = sgCalcTexCoords( b, geodNodes, strip_v ); + + base_tc.clear(); + for ( j = 0; j < (int)base_txs.size(); ++j ) + { + SGVec2f tc = base_txs[j]; + // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc); + index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) ); + base_tc.push_back( index ); + } + strips_tc.push_back( base_tc ); + } + +#if 0 + // add light points + + superpoly_list tmp_light_list; tmp_light_list.clear(); + typedef map < string, double, less<string> > elev_map_type; + typedef elev_map_type::const_iterator const_elev_map_iterator; + elev_map_type elevation_map; + + SG_LOG(SG_GENERAL, SG_INFO, "Computing runway/approach lighting elevations"); + + // pass one, calculate raw elevations from Array + + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + TGTriNodes light_nodes; + light_nodes.clear(); + point_list lights_v = rwy_lights[i].get_poly().get_contour(0); + for ( j = 0; j < (int)lights_v.size(); ++j ) + { + p = lights_v[j]; + index = light_nodes.simple_add( p ); + } + + // calculate light node elevations + + point_list geod_light_nodes = calc_elevations( apt_surf, light_nodes.get_node_list(), 0.5 ); + TGPolygon p; + p.add_contour( geod_light_nodes, 0 ); + TGSuperPoly s; + s.set_poly( p ); + tmp_light_list.push_back( s ); + + string flag = rwy_lights[i].get_flag(); + if ( flag != (string)"" ) + { + double max = -9999; + const_elev_map_iterator it = elevation_map.find( flag ); + if ( it != elevation_map.end() ) + { + max = elevation_map[flag]; + } + for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) + { + if ( geod_light_nodes[j].z() > max ) + { + max = geod_light_nodes[j].z(); + } + } + elevation_map[flag] = max; + SG_LOG( SG_GENERAL, SG_DEBUG, flag << " max = " << max ); + } + } + + SG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations()"); + + // pass two, for each light group check if we need to lift (based + // on flag) and do so, then output next structures. + for ( i = 0; i < (int)rwy_lights.size(); ++i ) + { + // tmp_light_list is a parallel structure to rwy_lights + point_list geod_light_nodes = tmp_light_list[i].get_poly().get_contour(0); + + // this is a little round about, but what we want to calculate the + // light node elevations as ground + an offset so we do them + // seperately, then we add them back into nodes to get the index + // out, but also add them to geod_nodes to maintain consistancy + // between these two lists. + point_list light_normals = rwy_lights[i].get_normals().get_contour(0); + pt_v.clear(); + pt_n.clear(); + for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) + { + p = geod_light_nodes[j]; + index = nodes.simple_add( p ); + pt_v.push_back( index ); + geod_nodes.push_back( p ); + + index = normals.unique_add( light_normals[j] ); + pt_n.push_back( index ); + } + pts_v.push_back( pt_v ); + pts_n.push_back( pt_n ); + pt_materials.push_back( rwy_lights[i].get_material() ); + } +#endif + + // calculate wgs84 mapping of nodes + std::vector< SGVec3d > wgs84_nodes; + for ( i = 0; i < (int)geod_nodes.size(); ++i ) + { + SGGeod geod = SGGeod::fromDegM( geod_nodes[i].x(), geod_nodes[i].y(), geod_nodes[i].z() ); + SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << geod_nodes[i] ); + SGVec3d cart = SGVec3d::fromGeod(geod); + SG_LOG(SG_GENERAL, SG_DEBUG, " cart pt = " << cart ); + wgs84_nodes.push_back( cart ); + } + SGSphered d; + for ( i = 0; i < wgs84_nodes.size(); ++i ) + { + d.expandBy(wgs84_nodes[ i ]); + } + + SGVec3d gbs_center = d.getCenter(); + double gbs_radius = d.getRadius(); + cout << "gbs center = " << gbs_center << endl; + 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 objpath = root + "/AirportObj"; + string name = airport_id + ".btg"; + + std::vector< SGVec3f > normals_3f; + for ( i=0; i < 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 ( i=0; i < 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( objpath, name, b ); + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + + // write out airport object reference + write_index( objpath, b, name ); + + // write out beacon references + for ( i = 0; i < (int)beacon_nodes.size(); ++i ) + { + write_index_shared( objpath, b, beacon_nodes[i], + "Models/Airport/beacon.xml", + 0.0 ); + } + + // write out tower references + for ( i = 0; i < (int)tower_nodes.size(); ++i ) + { + write_index_shared( objpath, b, tower_nodes[i], + "Models/Airport/tower.xml", + 0.0 ); + } + + // write out windsock references + for ( i = 0; i < (int)windsock_nodes.size(); ++i ) + { + if ( windsock_types[i] == 0 ) + { + write_index_shared( objpath, b, windsock_nodes[i], + "Models/Airport/windsock.xml", + 0.0 ); + } + else + { + write_index_shared( objpath, b, windsock_nodes[i], + "Models/Airport/windsock_lit.xml", + 0.0 ); + } + } + + string holepath = root + "/AirportArea"; + // long int poly_index = poly_index_next(); + // write_boundary( holepath, b, hull, poly_index ); + tgChopNormalPolygon( holepath, "Hole", divided_base, true ); + tgChopNormalPolygon( holepath, "Airport", apt_clearing, false ); +} +#endif + + diff --git a/src/Airports/GenAirports850/airport.hxx b/src/Airports/GenAirports850/airport.hxx new file mode 100644 index 00000000..28ef85f4 --- /dev/null +++ b/src/Airports/GenAirports850/airport.hxx @@ -0,0 +1,49 @@ +#ifndef _AIRPORT_H_ +#define _AIRPORT_H_ + +#include <stdio.h> +#include <stdlib.h> + +#include "runway.hxx" +#include "closedpoly.hxx" +#include "linearfeature.hxx" + +using std::string; + +class Airport +{ +public: + Airport( int c, char* def); + + void AddRunway( Runway* runway ) + { + runways.push_back( runway ); + } + + void AddPavement( ClosedPoly* pavement ) + { + pavements.push_back( pavement ); + } + + void AddFeature( LinearFeature* feature ) + { + features.push_back( feature ); + } + + void BuildOsg( osg::Group* airport ); + void BuildBtg( const string& root, const string_list& elev_src ); + +private: + int code; // airport, heliport or sea port + int altitude; // in meters + string icao; // airport code + string description; // description + + PavementList pavements; + FeatureList features; + RunwayList runways; +}; + +typedef std::vector <Airport *> AirportList; + +#endif diff --git a/src/Airports/GenAirports850/beznode.hxx b/src/Airports/GenAirports850/beznode.hxx new file mode 100644 index 00000000..af75d3da --- /dev/null +++ b/src/Airports/GenAirports850/beznode.hxx @@ -0,0 +1,205 @@ +#ifndef _BEZNODE_H_ +#define _BEZNODE_H_ + +#include <string.h> +#include <float.h> +#include <iostream> + +#include <osg/Vec3d> +#include <Geometry/point3d.hxx> +#include <simgear/debug/logstream.hxx> + +// TODO: where to put this +inline osg::Vec3d SGPoint3DtoOSGVec3d( Point3D p ) +{ + return osg::Vec3d( p.x(), p.y(), p.z() ); +} + +// TEMP... +inline Point3D CalculateQuadraticLocation( Point3D p0, Point3D cp, Point3D p1, double t ) +{ + Point3D result; + + double term1 = (1.0f - t) * (1.0f - t); + double term2 = 2 * (1.0f - t) * t; + double term3 = t * t; + + result = (p0 * term1) + (cp * term2) + (p1 * term3); + + return result; +} + +// TODO: Should be in a math library +inline Point3D CalculateCubicLocation( Point3D p0, Point3D cp0, Point3D cp1, Point3D p1, double t ) +{ + Point3D result; + + double term1 = (1.0f - t) * (1.0f - t) * (1.0f - t); + double term2 = 3 * (1.0f - t) * (1.0f - t) * t; + double term3 = 3 * (1.0f - t) * t * t; + double term4 = t * t * t; + + result = (p0 * term1) + (cp0 * term2) + (cp1 * term3) + (p1 * term4); + + return result; +} + +inline double CalculateTheta( Point3D p0, Point3D p1, Point3D p2 ) +{ + Point3D u, v; + double udist, vdist, uv_dot, tmp; + + // u . v = ||u|| * ||v|| * cos(theta) + + u = p1 - p0; + udist = sqrt( u.x() * u.x() + u.y() * u.y() ); + // printf("udist = %.6f\n", udist); + + v = p1 - p2; + vdist = sqrt( v.x() * v.x() + v.y() * v.y() ); + // printf("vdist = %.6f\n", vdist); + + uv_dot = u.x() * v.x() + u.y() * v.y(); + // printf("uv_dot = %.6f\n", uv_dot); + + tmp = uv_dot / (udist * vdist); + // printf("tmp = %.6f\n", tmp); + + return acos(tmp); +} + +#define BEZIER_DETAIL (8) +#define LINE_WIDTH (0.75) +#define WIREFRAME (1) + +#define CURVE_NONE (0) +#define CURVE_LINEAR (1) +#define CURVE_QUADRATIC (2) +#define CURVE_CUBIC (3) + + +class BezNode +{ +public: + BezNode( Point3D l ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f) + { + loc = l; + mark = 0; + light = 0; + } + + BezNode( double lat, double lon ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f) + { + loc = Point3D( lon, lat, 0.0f ); + mark = 0; + light = 0; + } + + BezNode( Point3D l, Point3D cp ) + { + loc = l; + next_cp = cp; + prev_cp = Mirror(cp); + mark = 0; + light = 0; + } + + BezNode( double lat, double lon, double cp_lat, double cp_lon ) + { + loc = Point3D( lon, lat, 0.0f ); + next_cp = Point3D( cp_lon, cp_lat, 0.0f ); + prev_cp = Mirror( next_cp ); + mark = 0; + light = 0; + } + + Point3D Mirror( Point3D pt ) + { + // mirror given point about our location + return (loc - (pt - loc)); + } + + void SetMarking( int m ) + { + mark = m; + } + + int GetMarking( ) + { + return mark; + } + + void SetLighting( int l ) + { + light = l; + } + + int GetLighting( ) + { + return light; + } + + bool IsAt( double lat, double lon ) + { + return ( (loc.lat() == lat) && (loc.lon() == lon) ); + } + + bool HasPrevCp() + { + return ( (prev_cp.x() != 0.0f) && (prev_cp.y() != 0.0f) ); + } + + bool HasNextCp() + { + return ( (next_cp.x() != 0.0f) && (next_cp.y() != 0.0f) ); + } + + Point3D GetLoc() + { + return loc; + } + + Point3D GetPrevCp() + { + return prev_cp; + } + + Point3D GetNextCp() + { + return next_cp; + } + + void ClearNextCp(void) + { + next_cp = Point3D(0.0f,0.0f, 0.0f); + } + + void SetNextCp( double lat, double lon ) + { + next_cp = Point3D(lon, lat, 0.0f); + } + + // TODO: log levels and macros (does terragear have them?) + void Print() + { + SG_LOG(SG_GENERAL, SG_DEBUG, + "\tLoc(" << loc.x() << "," << loc.y() << ")" << + "\tprev_cp(" << prev_cp.x() << "," << prev_cp.y() << ")" << + "\tnext_cp(" << next_cp.x() << "," << next_cp.y() << ")" ); + } + +private: + Point3D loc; + Point3D prev_cp; + Point3D next_cp; + int mark; + int light; +}; + + +// array of BezNodes make a contour +typedef std::vector <BezNode *> BezContour; +typedef std::vector <BezContour *> BezContourArray; + +#endif + diff --git a/src/Airports/GenAirports850/closedpoly.cxx b/src/Airports/GenAirports850/closedpoly.cxx new file mode 100644 index 00000000..cf705f6d --- /dev/null +++ b/src/Airports/GenAirports850/closedpoly.cxx @@ -0,0 +1,656 @@ +#include <stdlib.h> +#include <list> + +#include <simgear/debug/logstream.hxx> +#include <simgear/math/sg_geodesy.hxx> + +#include <osg/Geode> +#include <osg/PolygonMode> +#include <osgUtil/Tessellator> + +#include <Geometry/poly_support.hxx> + +#include "beznode.hxx" +#include "convex_hull.hxx" +#include "closedpoly.hxx" + + +ClosedPoly::ClosedPoly( int st, float s, float th, char* desc ) +{ + surface_type = st; + smoothness = s; + texture_heading = th; + if ( desc ) + { + strcpy( description, desc ); + } + else + { + strcpy( description, "none" ); + } + + boundary = NULL; + cur_contour = NULL; + cur_feat = NULL; + cur_marking = 0; +} + +void ClosedPoly::AddNode( BezNode* node ) +{ + // if this is the first node of the contour - create a new contour + if (!cur_contour) + { + cur_contour = new BezContour; + } + cur_contour->push_back( node ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "CLOSEDPOLY::ADDNODE : (" << node->GetLoc().x() << "," << node->GetLoc().y() << ")"); + // if recording a linear feature on the pavement, add this node + // to it as well + // TODO: just doing marking now, need lighting as well + if (cur_feat) + { + SG_LOG(SG_GENERAL, SG_DEBUG, " Adding node (" << node->GetLoc().x() << "," << node->GetLoc().y() << ") to current linear feature " << cur_marking); + cur_feat->AddNode( node ); + + // if it should end, end it, and add to feature list + if (cur_marking != node->GetMarking()) + { + SG_LOG(SG_GENERAL, SG_DEBUG, " Node has marking " << node->GetMarking() << " end it"); + features.push_back( cur_feat ); + cur_feat = NULL; + cur_marking = 0; + } + } + + // should we start a new feature here? + SG_LOG(SG_GENERAL, SG_DEBUG, " Node has marking " << node->GetMarking() << " cur marking is " << cur_marking); + if ( (cur_marking == 0) && (node->GetMarking())) + { + // Yes - create a new linear feature + // TODO: With offset, as all pavement markings should be + // a bit in from the edge + SG_LOG(SG_GENERAL, SG_DEBUG, " Starting a new linear feature"); + cur_feat = new LinearFeature(NULL); + cur_feat->AddNode( node ); + cur_marking = node->GetMarking(); + } +} + +void ClosedPoly::CreateConvexHull( void ) +{ + TGPolygon convexHull; + point_list nodes; + Point3D p; + int i; + + for (i=0; i<boundary->size(); i++) + { + p = boundary->at(i)->GetLoc(); + nodes.push_back( p ); + } + + convexHull = convex_hull( nodes ); + hull = convexHull.get_contour(0); +} + +int ClosedPoly::CloseCurContour() +{ + SG_LOG(SG_GENERAL, SG_DEBUG, "Close Contour"); + + // if we are recording a pavement marking - it must be closed - + // add the first node of the poly + if (cur_feat) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "We still have an active linear feature - add the first node to close it"); + + cur_feat->AddNode( cur_contour->at(0) ); + + features.push_back( cur_feat ); + cur_feat = NULL; + cur_marking = 0; + } + + // add the contour to the poly - first one is the outer boundary + // subsequent contours are holes + if ( boundary == NULL ) + { + boundary = cur_contour; + + // generate the convex hull from the bezcontour node locations + CreateConvexHull(); + + cur_contour = NULL; + } + else + { + holes.push_back( cur_contour ); + cur_contour = NULL; + } +} + +void ClosedPoly::ConvertContour( BezContour* src, point_list *dst, bool reverse ) +{ + BezNode* prevNode; + BezNode* curNode; + BezNode* nextNode; + + Point3D prevLoc; + Point3D curLoc; + Point3D nextLoc; + Point3D cp1; + Point3D cp2; + + int start, end, inc; + + int curve_type = CURVE_LINEAR; + int i; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Creating a contour with " << src->size() << " nodes"); + + // clear anything in this point list + dst->empty(); + + if (reverse) + { + start = src->size()-1; + end = -1; + inc = -1; + } + else + { + start = 0; + end = src->size(); + inc = 1; + } + + // iterate through each bezier node in the contour + for (i=0; i <= src->size()-1; i++) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nHandling Node " << i << "\n\n"); + + if (i == 0) + { + // set prev node to last in the contour, as all contours must be closed + prevNode = src->at( src->size()-1 ); + } + else + { + // otherwise, it's just the previous index + prevNode = src->at( i-1 ); + } + + curNode = src->at(i); + + if (i < src->size() - 1) + { + nextNode = src->at(i+1); + } + else + { + // for the last node, next is the first. as all contours are closed + nextNode = src->at(0); + } + + // determine the type of curve from prev (just to get correct prev location) + // once we start drawing the curve from cur to next, we can just remember the prev loc + if (prevNode->HasNextCp()) + { + // curve from prev is cubic or quadratic + if(curNode->HasPrevCp()) + { + // curve from prev is cubic : calculate the last location on the curve + prevLoc = CalculateCubicLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + else + { + // curve from prev is quadratic : use prev node next cp + prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + } + else + { + // curve from prev is quadratic or linear + if( curNode->HasPrevCp() ) + { + // curve from prev is quadratic : calculate the last location on the curve + prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + else + { + // curve from prev is linear : just use prev node location + prevLoc = prevNode->GetLoc(); + } + } + + // now determine how we will iterate from current node to next node + if( curNode->HasNextCp() ) + { + // next curve is cubic or quadratic + if( nextNode->HasPrevCp() ) + { + // curve is cubic : need both control points + curve_type = CURVE_CUBIC; + cp1 = curNode->GetNextCp(); + cp2 = nextNode->GetPrevCp(); + } + else + { + // curve is quadratic using current nodes cp as the cp + curve_type = CURVE_QUADRATIC; + cp1 = curNode->GetNextCp(); + } + } + else + { + // next curve is quadratic or linear + if( nextNode->HasPrevCp() ) + { + // curve is quadratic using next nodes cp as the cp + curve_type = CURVE_QUADRATIC; + cp1 = nextNode->GetPrevCp(); + } + else + { + // curve is linear + curve_type = CURVE_LINEAR; + } + } + + // initialize current location + curLoc = curNode->GetLoc(); + if (curve_type != CURVE_LINEAR) + { + for (int p=0; p<BEZIER_DETAIL; p++) + { + // calculate next location + if (curve_type == CURVE_QUADRATIC) + { + nextLoc = CalculateQuadraticLocation( curNode->GetLoc(), cp1, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) ); + } + else + { + nextLoc = CalculateCubicLocation( curNode->GetLoc(), cp1, cp2, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) ); + } + + // add the pavement vertex + // convert from lat/lon to geo + // (maybe later) - check some simgear objects... + dst->push_back( curLoc ); + if (p==0) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "adding Curve Anchor node (type " << curve_type << ") at (" << curLoc.x() << "," << curLoc.y() << ")"); + } + else + { + SG_LOG(SG_GENERAL, SG_DEBUG, " add bezier node (type " << curve_type << ") at (" << curLoc.x() << "," << curLoc.y() << ")"); + } + + // now set set prev and cur locations for the next iteration + prevLoc = curLoc; + curLoc = nextLoc; + } + } + else + { + nextLoc = nextNode->GetLoc(); + + // just add the one vertex - linear + dst->push_back( curLoc ); + SG_LOG(SG_GENERAL, SG_DEBUG, "adding Linear Anchor node at (" << curLoc.x() << "," << curLoc.y() << ")"); + } + } + + // Add the first point again? + // dst->push_back( src->at(0)->GetLoc() ); +} + +void ExpandPoint( Point3D *prev, Point3D *cur, Point3D *next, double expand_by, double *heading, double *offset ) +{ + int turn_direction; + bool reverse_dir = false; + double offset_dir; + double next_dir; + double az1, az2; + double dist; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Find average angle for contour: prev (" << *prev << "), " + "cur (" << *cur << "), " + "next (" << *next << ")" ); + + // first, find if the line turns left or right ar src + // for this, take the cross product of the vectors from prev to src, and src to next. + // if the cross product is negetive, we've turned to the left + // if the cross product is positive, we've turned to the right + // if the cross product is 0, then we need to use the direction passed in + SGVec3d dir1 = prev->toSGVec3d() - cur->toSGVec3d(); + dir1 = normalize(dir1); + + SGVec3d dir2 = next->toSGVec3d() - cur->toSGVec3d(); + dir2 = normalize(dir2); + + // Now find the average + SGVec3d avg = dir1 + dir2; + avg = normalize(avg); + + // find the offset angle + geo_inverse_wgs_84( avg.y(), avg.x(), 0.0f, 0.0f, &offset_dir, &az2, &dist); + + // find the direction to the next point + geo_inverse_wgs_84( cur->y(), cur->x(), next->y(), next->x(), &next_dir, &az2, &dist); + + // calculate correct distance for the offset point + *offset = (expand_by)/sin(SGMiscd::deg2rad(offset_dir-next_dir)); + *heading = offset_dir; + + SG_LOG(SG_GENERAL, SG_DEBUG, "heading is " << *heading << " distance is " << *offset ); +} + +void ClosedPoly::ExpandContour( point_list& src, TGPolygon& dst, double dist ) +{ + point_list expanded_boundary; + Point3D prevPoint, curPoint, nextPoint; + double theta; + double expand_by; + double expanded_x, expanded_y; + Point3D expanded_point; + + double h1, h2, h3; + double o1, o2, o3; + double az2; + int i; + + // iterate through each bezier node in the contour + for (i=0; i<src.size(); i++) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nExpanding point " << i << "\n\n"); + + if (i == 0) + { + // set prev node to last in the contour, as all contours must be closed + prevPoint = src.at( src.size()-1 ); + } + else + { + // otherwise, it's just the last index + prevPoint = src.at( i-1 ); + } + + curPoint = src.at(i); + + if (i<src.size()-1) + { + nextPoint = src.at(i+1); + } + else + { + // for the last node, next is the first. as all contours are closed + nextPoint = src.at(0); + } + + // calculate the angle between cur->prev and cur->next + theta = SGMiscd::rad2deg(CalculateTheta(prevPoint, curPoint, nextPoint)); + + if ( theta < 90.0 ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 1 (theta < 90) " << description << ": theta is " << theta ); + + // calculate the expanded point heading and offset from current point + ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 ); + + if (o1 > dist*2.0) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\ntheta is " << theta << " distance is " << o1 << " CLAMPING to " << dist*2 ); + o1 = dist*2; + + if (o1 > 100) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY " << description << ": theta is " << theta << " distance is " << o1 << " WHAT HAPPENED " ); + exit(1); + } + } + + geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 ); + expanded_point = Point3D( expanded_x, expanded_y, 0.0f ); + expanded_boundary.push_back( expanded_point ); + } + else if ( abs(theta - 180.0) < 0.1 ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 2 (theta close to 180) " << description << ": theta is " << theta ); + + // calculate the expanded point heading and offset from current point + ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 ); + + // straight line blows up math - dist should be exactly as given + o1 = dist; + + geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 ); + expanded_point = Point3D( expanded_x, expanded_y, 0.0f ); + expanded_boundary.push_back( expanded_point ); + } + else + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 3 (fall through) " << description << ": theta is " << theta ); + + // calculate the expanded point heading and offset from current point + ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 ); + + if (o1 > dist*2) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "\ntheta is " << theta << " distance is " << o1 << " WHAT HAPPENED " ); + exit(1); + } + + geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 ); + expanded_point = Point3D( expanded_x, expanded_y, 0.0f ); + expanded_boundary.push_back( expanded_point ); + } + } + + dst.add_contour( expanded_boundary, 9 ); +} + +osg::DrawArrays* ClosedPoly::CreateOsgPrimitive( point_list contour, osg::Vec3Array* vpave ) +{ + int i; + Point3D p; + osg::DrawArrays* primitive; + int start_vert = vpave->size(); + + for (i=0; i<contour.size(); i++) + { + p = contour[i]; + vpave->push_back( osg::Vec3d(p.x(), p.y(), 0.0) ); + } + + primitive = new osg::DrawArrays(osg::PrimitiveSet::POLYGON, start_vert, vpave->size()-start_vert); + + return primitive; +} + +// finish the poly - convert to TGPolygon, and tesselate +int ClosedPoly::Finish() +{ + BezContour* src_contour; + point_list dst_contour; + BezNode* node; + + // error handling + if (boundary == NULL) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "no boundary"); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Converting a poly with " << holes.size() << " holes"); + + if (boundary != NULL) + { + // create the boundary + ConvertContour( boundary, &dst_contour, false ); + + // and add it to the geometry + pre_tess.add_contour( dst_contour, 0 ); + + // The convert the hole contours + for (int i=0; i<holes.size(); i++) + { + dst_contour.clear(); + ConvertContour( holes[i], &dst_contour, false ); + pre_tess.add_contour( dst_contour, 1 ); + } + } + + // save memory by deleting unneeded resources + delete boundary; + boundary = NULL; + + // The convert the hole contours + holes.clear(); +} + +int ClosedPoly::BuildOsg( osg::Group* airport ) +{ + BezContour* contour; + BezNode* node; + osg::Geode* geode_pave; // has the stateset, and geometry for this poly + osg::Geometry* pavement; + osg::StateSet* ss_pave; // just a simple stateset for now + osg::PolygonMode* polymode; + osg::Vec4Array* col_pave; // just grey for now... + osg::Vec3Array* v_pave; // ALL verticies (boundary and holes) + osg::DrawArrays* primitive; // an index array for boundary / hole + int num_verts; // number of verticies in a contour + int i; // ? + + if ( pre_tess.contours() ) + { + // Setup a polygonmode for wireframe debuging + polymode = new osg::PolygonMode; + polymode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE ); + + // Create a drawable for the pavement poly (boundary and holes) + pavement = new osg::Geometry; + + // make pavement grey : do this when reading the poly + col_pave = new osg::Vec4Array; + col_pave->push_back(osg::Vec4(0.5f,0.5f,0.5f,1.0f)); + pavement->setColorArray(col_pave); + pavement->setColorBinding(osg::Geometry::BIND_OVERALL); + + // set up pavement stateset + geode_pave = new osg::Geode(); + ss_pave = new osg::StateSet(); + +#if WIREFRAME + ss_pave->setAttribute( polymode ); +#endif + geode_pave->setStateSet(ss_pave); + + v_pave = new osg::Vec3Array; + pavement->setVertexArray(v_pave); + + // create the boundary + SG_LOG(SG_GENERAL, SG_DEBUG, " Adding pavement boundary"); + primitive = CreateOsgPrimitive( pre_tess.get_contour( 0 ), v_pave ); + if (hull.size() > 0) + { + SG_LOG(SG_GENERAL, SG_DEBUG, " boundary has " << hull.size() << "verts" ); + } + + // add the hole contours + for (i=1; i<pre_tess.contours(); i++) + { + primitive = CreateOsgPrimitive( pre_tess.get_contour( i ), v_pave ); + pavement->addPrimitiveSet(primitive); + } + + osgUtil::Tessellator *tess = new osgUtil::Tessellator; + tess->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); + tess->retessellatePolygons(*pavement); + + geode_pave->addDrawable(pavement); + airport->addChild(geode_pave); + +#if 0 // debug ExpandPoly + TGPolygon base; + ExpandPoly( hull, base, 20 ); + primitive = CreateOsgPrimitive( base.get_contour( 0 ), v_pave ); + pavement->addPrimitiveSet(primitive); +#endif + + geode_pave->addDrawable(pavement); + airport->addChild(geode_pave); + + // add the pavement markings +#if 0 + printf("ClosedPoly::Finish - Finish %d linear features\n", features.size()); + for (int feat=0; feat<features.size(); feat++) + { + features.at(feat)->Finish(airport); + } +#endif + } +} + +int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ) +{ + TGPolygon base, safe_base; + string material; + int j, k; + + material = "pa_tiedown"; + + // verify the poly has been generated + if ( pre_tess.contours() ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: original poly has " << pre_tess.contours() << " contours"); + + // do this before clipping and generating the base + pre_tess = remove_dups( pre_tess ); + pre_tess = reduce_degeneracy( pre_tess ); + + for (int c=0; c<pre_tess.contours(); c++) + { + for (int pt=0; pt<pre_tess.contour_size(c); pt++) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: contour " << c << " pt " << pt << ": (" << pre_tess.get_pt(c, pt).x() << "," << pre_tess.get_pt(c, pt).y() << ")" ); + } + } + + + TGSuperPoly sp; + TGTexParams tp; + + TGPolygon clipped = tgPolygonDiff( pre_tess, *accum ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: clipped poly has " << clipped.contours() << " contours"); + + TGPolygon split = tgPolygonSplitLongEdges( clipped, 400.0 ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: split poly has " << split.contours() << " contours"); + + + sp.erase(); + sp.set_poly( split ); + sp.set_material( material ); + rwy_polys->push_back( sp ); + SG_LOG(SG_GENERAL, SG_DEBUG, "clipped = " << clipped.contours()); + *accum = tgPolygonUnion( pre_tess, *accum ); + tp = TGTexParams( pre_tess.get_pt(0,0), 20.0 /* TODO poly width */, 20.0 /* TODO poly length */, texture_heading ); + texparams->push_back( tp ); + + ExpandContour( hull, base, 20.0 ); + ExpandContour( hull, safe_base, 50.0 ); + + // add this to the airport clearing + *apt_clearing = tgPolygonUnion( safe_base, *apt_clearing); + + // and add the clearing to the base + *apt_base = tgPolygonUnion( base, *apt_base ); + } + + // clean up to save ram : we're done here... + + + return 1; +} diff --git a/src/Airports/GenAirports850/closedpoly.hxx b/src/Airports/GenAirports850/closedpoly.hxx new file mode 100644 index 00000000..2271c583 --- /dev/null +++ b/src/Airports/GenAirports850/closedpoly.hxx @@ -0,0 +1,66 @@ +#ifndef _BEZPOLY_H_ +#define _BEZPOLY_H_ + +#include "beznode.hxx" +#include "linearfeature.hxx" + +#include <Polygon/polygon.hxx> +#include <Polygon/superpoly.hxx> +#include <Geometry/point3d.hxx> + +#include "texparams.hxx" + +#include <osg/Geometry> +#include <osg/Vec3d> + +using std::string; + +class ClosedPoly +{ +public: + ClosedPoly( int st, float s, float th, char* desc ); + + void AddNode( BezNode* node ); + int CloseCurContour(); + int Finish(); + int BuildOsg( osg::Group* airport ); + int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ); + +private: + //osg::DrawArrays* CreatePrimitive( BezContour* contour, osg::Vec3Array* v_pave ); + // convert the BezierPoly to a normal Poly (adding nodes for the curves) + void CreateConvexHull( void ); + void ConvertContour( BezContour* src, point_list *dst, bool reverse ); + osg::DrawArrays* CreateOsgPrimitive( point_list contour, osg::Vec3Array* vpave ); + void ExpandContour( point_list& src, TGPolygon& dst, double dist ); + + int surface_type; + float smoothness; + float texture_heading; + char description[64]; + + // outer boundary definition as bezier nodes + BezContour* boundary; + + // holes + BezContourArray holes; + + // contour that nodes will be added until done + BezContour* cur_contour; + + + // outer boundary as convex hull + point_list hull; + + // Converted polygon after parsing complete + TGPolygon pre_tess; + + // pavement definitions can have multiple linear features (markings) + LinearFeature* cur_feat; + FeatureList features; + int cur_marking; +}; + +typedef std::vector <ClosedPoly *> PavementList; + +#endif diff --git a/src/Airports/GenAirports850/convex_hull.cxx b/src/Airports/GenAirports850/convex_hull.cxx new file mode 100644 index 00000000..62df7b87 --- /dev/null +++ b/src/Airports/GenAirports850/convex_hull.cxx @@ -0,0 +1,252 @@ +// convex_hull.cxx -- calculate the convex hull of a set of points +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id: convex_hull.cxx,v 1.12 2004-11-19 22:25:49 curt Exp $ +// + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <math.h> +#include <stdio.h> + +#include <map> + +#include <simgear/compiler.h> +#include <simgear/constants.h> +#include <simgear/structure/exception.hxx> + +#include <simgear/constants.h> + +#include "convex_hull.hxx" +#include "point2d.hxx" + +using std::less; +using std::map; + + +// stl map typedefs +typedef map < double, double, less<double> > map_container; +typedef map_container::iterator map_iterator; + + +// Calculate theta of angle (a, b, c) +double calc_angle(Point3D a, Point3D b, Point3D c) { + Point3D u, v; + double udist, vdist, uv_dot, tmp; + + // u . v = ||u|| * ||v|| * cos(theta) + + u.setx( b.x() - a.x() ); + u.sety( b.y() - a.y() ); + udist = sqrt( u.x() * u.x() + u.y() * u.y() ); + // printf("udist = %.6f\n", udist); + + v.setx( b.x() - c.x() ); + v.sety( b.y() - c.y() ); + vdist = sqrt( v.x() * v.x() + v.y() * v.y() ); + // printf("vdist = %.6f\n", vdist); + + uv_dot = u.x() * v.x() + u.y() * v.y(); + // printf("uv_dot = %.6f\n", uv_dot); + + tmp = uv_dot / (udist * vdist); + // printf("tmp = %.6f\n", tmp); + + return acos(tmp); +} + + +// Test to see if angle(Pa, Pb, Pc) < 180 degrees +bool test_point(Point3D Pa, Point3D Pb, Point3D Pc) { + double a1, a2; + + Point3D origin( 0.0 ); + + Point3D a( cos(Pa.y()) * Pa.x(), + sin(Pa.y()) * Pa.x(), 0 ); + + Point3D b( cos(Pb.y()) * Pb.x(), + sin(Pb.y()) * Pb.x(), 0 ); + + Point3D c( cos(Pc.y()) * Pc.x(), + sin(Pc.y()) * Pc.x(), 0 ); + + // printf("a is %.6f %.6f\n", a.x, a.y); + // printf("b is %.6f %.6f\n", b.x, b.y); + // printf("c is %.6f %.6f\n", c.x, c.y); + + a1 = calc_angle(a, b, origin); + a2 = calc_angle(origin, b, c); + + // printf("a1 = %.2f a2 = %.2f\n", a1 * SGD_RADIANS_TO_DEGREES, a2 * SGD_RADIANS_TO_DEGREES); + + return ( (a1 + a2) < SGD_PI ); +} + + +// calculate the convex hull of a set of points, return as a list of +// point2d. The algorithm description can be found at: +// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html +TGPolygon convex_hull( const point_list& input_list ) { + int i; + + map_iterator map_current, map_next, map_next_next, map_last; + + // list of translated points + point_list trans_list; + + // points sorted by radian degrees + map_container radians_map; + + // will contain the convex hull + TGPolygon con_hull; + + Point3D p, Pa, Pb, Pc, result; + double sum_x, sum_y; + int in_count, last_size; + + // STEP ONE: Find an average midpoint of the input set of points + in_count = input_list.size(); + sum_x = sum_y = 0.0; + + for ( i = 0; i < in_count; ++i ) { + sum_x += input_list[i].x(); + sum_y += input_list[i].y(); + } + + Point3D average( sum_x / in_count, sum_y / in_count, 0 ); + + // printf("Average center point is %.4f %.4f\n", average.x, average.y); + + // STEP TWO: Translate input points so average is at origin + trans_list.clear(); + + for ( i = 0; i < in_count; ++i ) { + p = Point3D( input_list[i].x() - average.x(), + input_list[i].y() - average.y(), 0 ); + // printf("%.6f %.6f\n", p.x, p.y); + trans_list.push_back( p ); + } + + // STEP THREE: convert to radians and sort by theta + radians_map.clear(); + + for ( i = 0; i < in_count; ++i ) { + p = cart_to_polar_2d( trans_list[i] ); + if ( p.x() > radians_map[p.y()] ) { + radians_map[p.y()] = p.x(); + } + } + + /* + // printf("Sorted list\n"); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.setx( (*map_current).first ); + p.sety( (*map_current).second ); + + printf("p is %.6f %.6f\n", p.x(), p.y()); + } + */ + + // STEP FOUR: traverse the sorted list and eliminate everything + // not on the perimeter. + // printf("Traversing list\n"); + + // double check list size ... this should never fail because a + // single runway will always generate four points. + if ( radians_map.size() < 3 ) { + throw sg_exception("convex hull not possible with < 3 points"); + } + + // ensure that we run the while loop at least once + last_size = radians_map.size() + 1; + + while ( last_size > (int)radians_map.size() ) { + // printf("Running an iteration of the graham scan algorithm\n"); + last_size = radians_map.size(); + + map_current = radians_map.begin(); + while ( map_current != radians_map.end() ) { + // get first element + Pa.sety( (*map_current).first ); + Pa.setx( (*map_current).second ); + + // get second element + map_next = map_current; + ++map_next; + if ( map_next == radians_map.end() ) { + map_next = radians_map.begin(); + } + Pb.sety( (*map_next).first ); + Pb.setx( (*map_next).second ); + + // get third element + map_next_next = map_next; + ++map_next_next; + if ( map_next_next == radians_map.end() ) { + map_next_next = radians_map.begin(); + } + Pc.sety( (*map_next_next).first ); + Pc.setx( (*map_next_next).second ); + + // printf("Pa is %.6f %.6f\n", Pa.y(), Pa.x()); + // printf("Pb is %.6f %.6f\n", Pb.y(), Pb.x()); + // printf("Pc is %.6f %.6f\n", Pc.y(), Pc.x()); + + if ( test_point(Pa, Pb, Pc) ) { + // printf("Accepted a point\n"); + // accept point, advance Pa, Pb, and Pc. + ++map_current; + } else { + // printf("REJECTED A POINT\n"); + // reject point, delete it and advance only Pb and Pc + map_next = map_current; + ++map_next; + if ( map_next == radians_map.end() ) { + map_next = radians_map.begin(); + } + radians_map.erase( map_next ); + } + } + } + + // translate back to correct lon/lat + // printf("Final sorted convex hull\n"); + con_hull.erase(); + map_current = radians_map.begin(); + map_last = radians_map.end(); + for ( ; map_current != map_last ; ++map_current ) { + p.sety( (*map_current).first ); + p.setx( (*map_current).second ); + + result.setx( cos(p.y()) * p.x() + average.x() ); + result.sety( sin(p.y()) * p.x() + average.y() ); + + // printf("%.6f %.6f\n", result.x, result.y); + + con_hull.add_node(0, result); + } + + return con_hull; +} diff --git a/src/Airports/GenAirports850/convex_hull.hxx b/src/Airports/GenAirports850/convex_hull.hxx new file mode 100644 index 00000000..de863c34 --- /dev/null +++ b/src/Airports/GenAirports850/convex_hull.hxx @@ -0,0 +1,47 @@ +// convex_hull.hxx -- calculate the convex hull of a set of points +// +// Written by Curtis Olson, started September 1998. +// +// Copyright (C) 1998 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id: convex_hull.hxx,v 1.5 2004-11-19 22:25:49 curt Exp $ +// + + +#ifndef _CONVEX_HULL_HXX +#define _CONVEX_HULL_HXX + + +#include <list> + +#include <simgear/math/sg_types.hxx> + +#include <Polygon/polygon.hxx> + +#include "point2d.hxx" + + + +// calculate the convex hull of a set of points, return as a list of +// point2d. The algorithm description can be found at: +// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html +TGPolygon convex_hull( const point_list& input_list ); + + +#endif // _CONVEX_HULL_HXX + + diff --git a/src/Airports/GenAirports850/elevations.cxx b/src/Airports/GenAirports850/elevations.cxx index 39971073..376689bb 100644 --- a/src/Airports/GenAirports850/elevations.cxx +++ b/src/Airports/GenAirports850/elevations.cxx @@ -145,7 +145,6 @@ double tgAverageElevation( const string &root, const string_list elev_src, // lookup node elevations for each point in the specified simple // matrix. Returns average of all points. - void tgCalcElevations( const string &root, const string_list elev_src, SimpleMatrix &Pts, const double average ) { @@ -252,7 +251,6 @@ void tgCalcElevations( const string &root, const string_list elev_src, << grid_average); } - // clamp all elevations to the specified range void tgClampElevations( SimpleMatrix &Pts, diff --git a/src/Airports/GenAirports850/elevations.hxx b/src/Airports/GenAirports850/elevations.hxx index c1cc0754..b6f158aa 100644 --- a/src/Airports/GenAirports850/elevations.hxx +++ b/src/Airports/GenAirports850/elevations.hxx @@ -48,9 +48,7 @@ double tgAverageElevation( const string &root, const string_list elev_src, // lookup node elevations for each point in the specified nurbs++ // matrix. -void tgCalcElevations( const string &root, const string_list elev_src, - SimpleMatrix &Pts, double average ); +void tgCalcElevations( const string &root, const string_list elev_src, SimpleMatrix &Pts, double average ); // clamp all elevations to the specified range -void tgClampElevations( SimpleMatrix &Pts, - double center_m, double max_clamp_m ); +void tgClampElevations( SimpleMatrix &Pts, double center_m, double max_clamp_m ); diff --git a/src/Airports/GenAirports850/linearfeature.cxx b/src/Airports/GenAirports850/linearfeature.cxx new file mode 100644 index 00000000..dfc4147e --- /dev/null +++ b/src/Airports/GenAirports850/linearfeature.cxx @@ -0,0 +1,550 @@ +#include "beznode.hxx" +#include "linearfeature.hxx" +#include "math.h" + +#include <simgear/math/sg_geodesy.hxx> +#include <simgear/math/SGVec3.hxx> +#include <simgear/math/SGMisc.hxx> + +#include <osg/Geode> +#include <osg/Geometry> +#include <osg/Group> +#include <osg/PolygonMode> +#include <osg/PolygonOffset> + +int LinearFeature::Finish( osg::Group* airport ) +{ + BezNode* node; + int i, j; + int prev_dir = 0; + double direction; + double dist1, dist2; + + printf("Finish a %s Linear Feature with %d verticies\n", closed ? "closed" : "open", contour.size()); + + BezNode* prevNode; + BezNode* curNode; + BezNode* nextNode; + + Point3D prevLoc; + Point3D curLoc; + Point3D nextLoc; + + Point3D st_cur1; + Point3D st_cur2; + double st_curx, st_cury, az2; + + // create a DrawElement for the stripe triangle strip + int stripe_idx = 0; + int cur_marking = 0; + + osg::Vec3dArray* v_stripe = new osg::Vec3dArray; + + for (i=0; i<contour.size(); i++) + { + printf("\nHandling Node %d\n\n", i ); + + if (i == 0) + { + // if closed, set prev node to last in the contour + if (closed) + { + prevNode = contour.at( contour.size()-1 ); + } + else + { + prevNode = NULL; + } + } + else + { + prevNode = contour.at( i-1 ); + } + + curNode = contour.at(i); + + if (i<contour.size()-1) + { + nextNode = contour.at(i+1); + } + else + { + // if closed, set next node to the first in the contour + if (closed) + { + nextNode = contour.at(0); + } + else + { + nextNode = NULL; + } + } + + // determine the type of curve from prev (just to get correct prev location) + // once we start drawing the curve from cur to next, we can just remember the prev loc + if (prevNode) + { + if (prevNode->HasNextCp()) + { + // curve from prev is cubic or quadratic + if(curNode->HasPrevCp()) + { + // curve from prev is cubic : calculate the last location on the curve + prevLoc = CalculateCubicLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + else + { + // curve from prev is quadratic : use prev node next cp + prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + } + else + { + // curve from prev is quadratic or linear + if( curNode->HasPrevCp() ) + { + // curve from prev is quadratic : calculate the last location on the curve + prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) ); + } + else + { + // curve from prev is linear : just use prev node location + prevLoc = prevNode->GetLoc(); + } + } + } + else + { + prevLoc = Point3D(0.0f, 0.0f, 0.0f); + } + + int curve_type = CURVE_LINEAR; + Point3D cp1; + Point3D cp2; + + if ( nextNode) + { + // now determine how we will iterate through cur node to next node + if( curNode->HasNextCp() ) + { + // next curve is cubic or quadratic + if( nextNode->HasPrevCp() ) + { + // curve is cubic : need both control points + curve_type = CURVE_CUBIC; + cp1 = curNode->GetNextCp(); + cp2 = nextNode->GetPrevCp(); + } + else + { + // curve is quadratic using current nodes cp as the cp + curve_type = CURVE_QUADRATIC; + cp1 = curNode->GetNextCp(); + } + } + else + { + // next curve is quadratic or linear + if( nextNode->HasPrevCp() ) + { + // curve is quadratic using next nodes cp as the cp + curve_type = CURVE_QUADRATIC; + cp1 = nextNode->GetPrevCp(); + } + else + { + // curve is linear + curve_type = CURVE_LINEAR; + } + } + } + else + { + curve_type = CURVE_NONE; + } + + // initialize current location + curLoc = curNode->GetLoc(); + switch( curve_type ) + { + case CURVE_QUADRATIC: + case CURVE_CUBIC: + for (int p=0; p<BEZIER_DETAIL; p++) + { + // calculate next location + if (curve_type == CURVE_QUADRATIC) + { + nextLoc = CalculateQuadraticLocation( curNode->GetLoc(), cp1, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) ); + } + else + { + nextLoc = CalculateCubicLocation( curNode->GetLoc(), cp1, cp2, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) ); + } + + // set the verticies + // convert from lat/lon to geodisc + // (maybe later) - check some simgear objects... + // printf("Added vertex at %lf,%lf\n", + // add the pavement vertex + if (p==0) + { + printf("adding Curve Anchor node (type %d) at (%lf,%lf)\n", curve_type, curLoc.x(), curLoc.y()); + } + else + { + printf(" add bezier node (type %d) at (%lf,%lf)\n", curve_type, curLoc.x(), curLoc.y()); + } + + // find the average direction at this vertex... + direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 ); + + printf("got dir for bezier : prev_dir %d, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2); + + // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim. + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 ); + st_cur1 = Point3D( st_curx, st_cury, 0.0f ); + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) ); + + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 ); + st_cur2 = Point3D( st_curx, st_cury, 0.0f ); + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) ); + + printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", + curLoc.x(), curLoc.y(), + st_cur1.x(), st_cur1.y(), + st_cur2.x(), st_cur2.y() + ); + + // now set set prev and cur locations for the next iteration + prevLoc = curLoc; + curLoc = nextLoc; + } + break; + + case CURVE_LINEAR: + nextLoc = nextNode->GetLoc(); + + printf("adding Linear Anchor node at (%lf,%lf)\n", curLoc.x(), curLoc.y()); + + // find the average direction at this vertex... + direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 ); + + printf("got dir for linear : prev_dir %d, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2); + + // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim. + printf("calc direct: curLoc is (%lf,%lf), direction is %lf, dist is %lf\n", curLoc.x(), curLoc.y(), direction, dist1); + + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 ); + st_cur1 = Point3D( st_curx, st_cury, 0.0f ); + + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) ); + + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 ); + st_cur2 = Point3D( st_curx, st_cury, 0.0f ); + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) ); + + printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", + curLoc.x(), curLoc.y(), + st_cur1.x(), st_cur1.y(), + st_cur2.x(), st_cur2.y() + ); + break; + + case CURVE_NONE: + nextLoc = Point3D(0.0f, 0.0f, 0.0f); + + // we need to add the last verticies based on cur and prev position. + // find the average direction at this vertex... + direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 ); + + printf("got dir for term points : prev_dir %lf, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2); + + // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim. + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 ); + st_cur1 = Point3D( st_curx, st_cury, 0.0f ); + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) ); + + geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 ); + st_cur2 = Point3D( st_curx, st_cury, 0.0f ); + v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) ); + + printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", + curLoc.x(), curLoc.y(), + st_cur1.x(), st_cur1.y(), + st_cur2.x(), st_cur2.y() + ); + break; + } + } + + printf("End feature %d \n", v_stripe->size() ); + airport->addChild(FinishMarking( v_stripe ) ); +} + +int LinearFeature::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ) +{ + string material; + + return 1; +} + + +// this should be in the class +// TODO: Should Be AddMidMarkingVerticies - and handle offset (used in LinearFeature::Finish only) +double CalcMiddleMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 ) +{ + int turn_direction; + bool reverse_dir = false; + double offset_dir; + double next_dir; + double az1, az2; + double dist; + + printf("Find averate angle for mark: prev (%lf,%lf), cur(%lf,%lf), next(%lf,%lf)\n", prev->x(), prev->y(), cur->x(), cur->y(), next->x(), next->y()); + + // first, find if the line turns left or right ar src + // for this, take the cross product of the vectors from prev to src, and src to next. + // if the cross product is negetive, we've turned to the left + // if the cross product is positive, we've turned to the right + // if the cross product is 0, then we need to use the direction passed in + SGVec3d dir1 = cur->toSGVec3d() - prev->toSGVec3d(); + dir1 = normalize(dir1); + + printf("normalized dir1 is (%lf,%lf)\n", dir1.x(), dir1.y()); + + SGVec3d dir2 = next->toSGVec3d() - cur->toSGVec3d(); + dir2 = normalize(dir2); + + printf("normalized dir2 is (%lf,%lf)\n", dir2.x(), dir2.y()); + + SGVec3d cp = cross(dir1,dir2); + if (cp.z() < 0.0) + { + turn_direction = -1; + } + else if (cp.z() > 0.0) + { + turn_direction = 1; + } + else + { + turn_direction = 0; + } + + printf("turn direction is %d\n", turn_direction); + + // Now find the average + SGVec3d avg = -dir1 + dir2; + + printf("avg is (%lf,%lf)\n", avg.x(), avg.y()); + + // now determine which way the direction needs to point + if ((wind == -1) && (turn_direction == 1)) + { + reverse_dir = true; + } + if ((wind == 1) && (turn_direction == -1)) + { + reverse_dir = true; + } + + printf("reverse dir is %d\n", reverse_dir); + + // find the offset angle + geo_inverse_wgs_84( 0.0f, 0.0f, avg.x(), avg.y(), &offset_dir, &az2, &dist); + if (reverse_dir) + { + offset_dir += 180; + while (offset_dir >= 360.0) + { + offset_dir -= 360.0; + } + } + printf("offset direction is %lf\n", offset_dir); + + // find the direction to the next point + geo_inverse_wgs_84( cur->x(), cur->y(), next->x(), next->y(), &next_dir, &az2, &dist); + printf("next direction is %lf\n", next_dir); + + // calculate correct distance for the offset point + *dist1 = (-LINE_WIDTH)/sin(SGMiscd::deg2rad(next_dir-offset_dir)); + *dist2 = (LINE_WIDTH)/sin(SGMiscd::deg2rad(next_dir-offset_dir)); + + return offset_dir; +} + +// TODO: Should Be AddStartMarkingVerticies - and handle offset (used in LinearFeature::Finish only) +double CalcStartMarkingVerticies( Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 ) +{ + double offset_dir; + double az1, az2; + double dist; + + printf("Find start angle for mark: cur(%lf,%lf), next(%lf,%lf)\n", cur->x(), cur->y(), next->x(), next->y()); + + // find the offset angle + geo_inverse_wgs_84( cur->x(), cur->y(), next->x(), next->y(), &offset_dir, &az2, &dist); + offset_dir -= 90; + if (offset_dir < 0) + { + offset_dir += 360; + } + + printf("offset direction is %lf\n", offset_dir); + + // calculate correct distance for the offset point + *dist1 = (-LINE_WIDTH); + *dist2 = (LINE_WIDTH); + + return offset_dir; +} + +// TODO: Should Be AddEndMarkingVerticies - and handle offset (used in LinearFeature::Finish only) +double CalcEndMarkingVerticies( Point3D *prev, Point3D *cur, int wind, int *prev_dir, double *dist1, double *dist2 ) +{ + double offset_dir; + double az1, az2; + double dist; + + printf("Find end angle for mark: prev(%lf,%lf), cur(%lf,%lf)\n", prev->x(), prev->y(), cur->x(), cur->y()); + + // find the offset angle + geo_inverse_wgs_84( prev->x(), prev->y(), cur->x(), cur->y(), &offset_dir, &az2, &dist); + offset_dir -= 90; + if (offset_dir < 0) + { + offset_dir += 360; + } + + printf("offset direction is %lf\n", offset_dir); + + // calculate correct distance for the offset point + *dist1 = (-LINE_WIDTH); + *dist2 = (LINE_WIDTH); + + return offset_dir; +} + +// TODO: Should Be AddMarkingVerticies - and handle offset (used in LinearFeature::Finish only) +double CalcMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 ) +{ + double offset_dir; + + // first, we need to see if we want to average the directions (we have both prev, and next) + if ( (prev->x() == 0.0f) && (prev->y() == 0.0f) ) + { + // direction is 90 degrees less than from current to next + offset_dir = CalcStartMarkingVerticies( cur, next, wind, prev_dir, dist1, dist2 ); + printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2); + } + else if ( (next->x() == 0.0f) && (next->y() == 0.0f) ) + { + // direction is 90 degrees less than from current to next + offset_dir = CalcEndMarkingVerticies( prev, cur, wind, prev_dir, dist1, dist2 ); + printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2); + } + else + { + offset_dir = CalcMiddleMarkingVerticies( prev, cur, next, wind, prev_dir, dist1, dist2 ); + printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2); + } + + printf("return from CalcMarkingVerticies: dirst1: %lf, dist2: %lf\n", *dist1, *dist2); + + return offset_dir; +} + +// need to refactor this stuff. +osg::Geode* FinishMarking( osg::Vec3dArray* verticies ) +{ + osg::Geometry* stripe = NULL; + osg::Geode* geode_stripe = NULL; + osg::StateSet* ss_stripe = NULL; + osg::PolygonMode* polymode = NULL; + osg::Vec4Array* col_stripe = NULL; + int j; + + // set up polygon offset + osg::PolygonOffset* polyoffset = new osg::PolygonOffset; + polyoffset->setFactor(-1.00f); + polyoffset->setUnits(-1.00f); + + polymode = new osg::PolygonMode; + polymode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE ); + + col_stripe = new osg::Vec4Array; + col_stripe->push_back(osg::Vec4(1.0f,1.0f,0.0f,1.0f)); + + // Create a new stripe geometry + stripe = new osg::Geometry; + stripe->setColorArray(col_stripe); + stripe->setColorBinding(osg::Geometry::BIND_OVERALL); + + ss_stripe = new osg::StateSet(); + ss_stripe->setAttributeAndModes(polyoffset,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); +#if WIREFRAME + ss_stripe->setAttributeAndModes(polymode,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); +#endif + geode_stripe = new osg::Geode(); + geode_stripe->setStateSet(ss_stripe); + + stripe->setVertexArray(verticies); + + // create the index array for the stripe + osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLE_STRIP,verticies->size())); + + for (j=0; j<verticies->size(); j++) + { + drawElements[j] = j; + } + + for (int k=0; k<j; k++) + { + printf("index %d is %d\n", k, drawElements[k]); + } + + stripe->addPrimitiveSet(&drawElements); + + geode_stripe->addDrawable(stripe); + + return geode_stripe; +} + +// TODO: Add this into Linear Feature +osg::Vec3dArray* StartMarking() +{ + osg::Vec3dArray* v_marking = new osg::Vec3dArray; + + return v_marking; +} + +// TODO: move this into LinearFeature +osg::Vec3dArray* CheckMarking(int cur_marking, int new_marking, osg::Vec3dArray* v_marking, osg::Group* airport) +{ + // check if we need to finish a marking + printf("Check Marking : current is %d, new is %d\n", cur_marking, new_marking); + + if ( (v_marking != NULL) && (cur_marking != new_marking) ) + { + printf("End current marking %d and start new marking %d : mark poly has %d verticies\n", cur_marking, new_marking, v_marking->size() ); + for (int k=0; k<v_marking->size(); k++) + { + printf("vertex %d is (%lf, %lf)\n", k, v_marking->at(k).x(), v_marking->at(k).y()); + } + + // END THE CURRENT MARKING + airport->addChild(FinishMarking( v_marking ) ); + v_marking = NULL; + } + + // Start recording a new marking + if ( (v_marking == NULL) && (new_marking != 0) ) + { + // start a new marking + printf("Start new marking %d\n", new_marking); + + v_marking = StartMarking(); + } + + return v_marking; +} diff --git a/src/Airports/GenAirports850/linearfeature.hxx b/src/Airports/GenAirports850/linearfeature.hxx new file mode 100644 index 00000000..adf7ece1 --- /dev/null +++ b/src/Airports/GenAirports850/linearfeature.hxx @@ -0,0 +1,67 @@ +#ifndef _LINEARFEATURE_H_ +#define _LINEARFEATURE_H_ + +#include <Polygon/polygon.hxx> +#include <Polygon/superpoly.hxx> +#include <Geometry/point3d.hxx> + +#include "texparams.hxx" + +#include <osg/Group> + +using std::string; + +class LinearFeature +{ +public: + LinearFeature( char* desc ) + { + if ( desc ) + { + strcpy( description, desc ); + } + else + { + strcpy( description, "none" ); + } + + offset = 0.0f; + closed = false; + } + + void AddNode( BezNode* b ) + { + contour.push_back( b ); + } + + void SetClosed() + { + closed = true; + } + + int Finish( osg::Group* airport ); + int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ); + +private: + char description[256]; + + // contour definition + BezContour contour; + + // TODO : Implement offset + double offset; + + bool closed; +}; + +typedef std::vector <LinearFeature *> FeatureList; + +// add this to the class +extern double CalcMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 ); + +// don't know what to do with these +extern osg::Geode* FinishMarking( osg::Vec3dArray* verticies ); +extern osg::Vec3dArray* CheckMarking(int cur_marking, int new_marking, osg::Vec3dArray* v_marking, osg::Group* airport); + +#endif + diff --git a/src/Airports/GenAirports850/main.cxx b/src/Airports/GenAirports850/main.cxx index b9a08473..3f3403fd 100644 --- a/src/Airports/GenAirports850/main.cxx +++ b/src/Airports/GenAirports850/main.cxx @@ -1,67 +1,85 @@ -// main.cxx -- main loop -// -// Written by Curtis Olson, started March 1998. -// -// Copyright (C) 1998 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id: main.cxx,v 1.37 2005/12/19 15:53:21 curt Exp $ -// +/* OpenSceneGraph example, osgshaderterrain. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ +#include <osg/AlphaFunc> +#include <osg/Billboard> +#include <osg/BlendFunc> +#include <osg/Depth> +#include <osg/Geode> +#include <osg/Geometry> +#include <osg/Group> +#include <osg/GL2Extensions> +#include <osg/Material> +#include <osg/Math> +#include <osg/MatrixTransform> +#include <osg/PolygonMode> +#include <osg/PolygonOffset> +#include <osg/Program> +#include <osg/Projection> +#include <osg/Shader> +#include <osg/ShapeDrawable> +#include <osg/StateSet> +#include <osg/Switch> +#include <osg/Texture2D> +#include <osg/Uniform> -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif +#include <osgDB/ReadFile> +#include <osgDB/FileUtils> -#include <simgear/compiler.h> -#include <simgear/structure/exception.hxx> -#include <simgear/misc/sg_path.hxx> +#include <osgUtil/Tessellator> +#include <osgUtil/SmoothingVisitor> +#include <osgText/Text> +#include <osgViewer/Viewer> +#include <osgViewer/ViewerEventHandlers> +#include <osgUtil/Optimizer> -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif +#include <osgGA/TrackballManipulator> +#include <osgGA/FlightManipulator> +#include <osgGA/TerrainManipulator> +#include <osgGA/StateSetManipulator> -#include <list> -#include <vector> - -#include <stdio.h> #include <string.h> -#include <string> #include <iostream> -#include <simgear/constants.h> -#include <simgear/bucket/newbucket.hxx> #include <simgear/debug/logstream.hxx> +#include <simgear/misc/sg_path.hxx> + #include <simgear/misc/sgstream.hxx> -#include <simgear/misc/strutils.hxx> +//#include <simgear/misc/strutils.hxx> #include <Polygon/index.hxx> -#include <Geometry/util.hxx> -#include "build.hxx" +#include "beznode.hxx" +#include "closedpoly.hxx" +#include "linearfeature.hxx" +#include "parser.hxx" -using std::vector; -using std::cout; -using std::endl; - - -int nudge = 10; -double slope_max = 0.2; - -static int is_in_range( string_list & runway_list, float min_lat, float max_lat, float min_lon, float max_lon ); +// TODO : Modularize this function +// IDEAS +// 1) Start / Stop MarkStyle +// 2) Start / Stop LightStyle +// 3) holes +// 4) CreatePavementSS(pavement type) +// 5) CreateMarkingSS(marking type) +// 6) CalcStripeOffsets should be AddMarkingVerticies, and take the 2 distances from pavement edge... +// SCRAP ALL IDEAS - Markings are encapsulated in LinearFeature class. +// Start creates a new object +// End closes the object (and should add it to a list - either in the parser, or ClosedPoly) // Display usage static void usage( int argc, char **argv ) { @@ -73,6 +91,7 @@ static void usage( int argc, char **argv ) { << "[ --airport=abcd ] [--tile=<tile>] [--chunk=<chunk>] [--verbose] [--help]"); } + void setup_default_elevation_sources(string_list& elev_src) { elev_src.push_back( "SRTM2-Africa-3" ); elev_src.push_back( "SRTM2-Australia-3" ); @@ -86,58 +105,29 @@ void setup_default_elevation_sources(string_list& elev_src) { elev_src.push_back( "SRTM-30" ); } -// Display help and usage -static void help( int argc, char **argv, const string_list& elev_src ) { - cout << "genapts generates airports for use in generating scenery for the FlightGear flight simulator. "; - cout << "Airport, runway, and taxiway vector data and attributes are input, and generated 3D airports "; - cout << "are output for further processing by the TerraGear scenery creation tools. "; - cout << "\n\n"; - cout << "The standard input file is runways.dat.gz which is found in $FG_ROOT/Airports. "; - cout << "This file is periodically generated for the FlightGear project by Robin Peel, who "; - cout << "maintains an airport database for both the X-Plane and FlightGear simulators. "; - cout << "The format of this file is documented on the FlightGear web site. "; - cout << "Any other input file corresponding to this format may be used as input to genapts. "; - cout << "Input files may be gzipped or left as plain text as required. "; - cout << "\n\n"; - cout << "Processing all the world's airports takes a *long* time. To cut down processing time "; - cout << "when only some airports are required, you may refine the input selection either by airport "; - cout << "or by area. By airport, either one airport can be specified using --airport=abcd, where abcd is "; - cout << "a valid airport code eg. --airport-id=KORD, or a starting airport can be specified using --start-id=abcd "; - cout << "where once again abcd is a valid airport code. In this case, all airports in the file subsequent to the "; - cout << "start-id are done. This is convienient when re-starting after a previous error. "; - cout << "\nAn input area may be specified by lat and lon extent using min and max lat and lon. "; - cout << "Alternatively, you may specify a chunk (10 x 10 degrees) or tile (1 x 1 degree) using a string "; - cout << "such as eg. w080n40, e000s27. "; - cout << "\nAn input file containing only a subset of the world's "; - cout << "airports may of course be used."; - cout << "\n\n"; - cout << "It is necessary to generate the elevation data for the area of interest PRIOR TO GENERATING THE AIRPORTS. "; - cout << "Failure to do this will result in airports being generated with an elevation of zero. "; - cout << "The following subdirectories of the work-dir will be searched for elevation files:\n\n"; - - string_list::const_iterator elev_src_it; - for (elev_src_it = elev_src.begin(); elev_src_it != elev_src.end(); elev_src_it++) { - cout << *elev_src_it << "\n"; - } - cout << "\n"; - usage( argc, argv ); -} +// TODO: where do these belong +int nudge = 10; +double slope_max = 0.2; -// reads the apt_full file and extracts and processes the individual -// airport records -int main( int argc, char **argv ) { +int main(int argc, char **argv) +{ float min_lon = -180; float max_lon = 180; float min_lat = -90; float max_lat = 90; - bool ready_to_go = true; + bool ready_to_go = true; + bool view_osg = false; string_list elev_src; elev_src.clear(); setup_default_elevation_sources(elev_src); - sglog().setLogLevels( SG_GENERAL, SG_INFO ); + // Set verbose + sglog().setLogLevels( SG_GENERAL, SG_BULK ); +// sglog().setLogLevels( SG_GENERAL, SG_INFO ); + + SG_LOG(SG_GENERAL, SG_INFO, "Run genapt"); // parse arguments string work_dir = ""; @@ -145,63 +135,109 @@ int main( int argc, char **argv ) { string start_id = ""; string airport_id = ""; int arg_pos; - for (arg_pos = 1; arg_pos < argc; arg_pos++) { + + for (arg_pos = 1; arg_pos < argc; arg_pos++) + { string arg = argv[arg_pos]; - if ( arg.find("--work=") == 0 ) { + if ( arg.find("--work=") == 0 ) + { work_dir = arg.substr(7); - } else if ( arg.find("--input=") == 0 ) { - input_file = arg.substr(8); - } else if ( arg.find("--terrain=") == 0 ) { + } + else if ( arg.find("--input=") == 0 ) + { + input_file = arg.substr(8); + } + else if ( arg.find("--terrain=") == 0 ) + { elev_src.push_back( arg.substr(10) ); - } else if ( arg.find("--start-id=") == 0 ) { - start_id = arg.substr(11); - ready_to_go = false; - } else if ( arg.find("--nudge=") == 0 ) { - nudge = atoi( arg.substr(8).c_str() ); - } else if ( arg.find("--min-lon=") == 0 ) { - min_lon = atof( arg.substr(10).c_str() ); - } else if ( arg.find("--max-lon=") == 0 ) { - max_lon = atof( arg.substr(10).c_str() ); - } else if ( arg.find("--min-lat=") == 0 ) { - min_lat = atof( arg.substr(10).c_str() ); - } else if ( arg.find("--max-lat=") == 0 ) { - max_lat = atof( arg.substr(10).c_str() ); - } else if ( arg.find("--chunk=") == 0 ) { - tg::Rectangle rectangle = tg::parseChunk(arg.substr(8).c_str(), - 10.0); + } + else if ( arg.find("--start-id=") == 0 ) + { + start_id = arg.substr(11); + ready_to_go = false; + } + else if ( arg.find("--nudge=") == 0 ) + { + nudge = atoi( arg.substr(8).c_str() ); + } + else if ( arg.find("--min-lon=") == 0 ) + { + min_lon = atof( arg.substr(10).c_str() ); + } + else if ( arg.find("--max-lon=") == 0 ) + { + max_lon = atof( arg.substr(10).c_str() ); + } + else if ( arg.find("--min-lat=") == 0 ) + { + min_lat = atof( arg.substr(10).c_str() ); + } + else if ( arg.find("--max-lat=") == 0 ) + { + max_lat = atof( arg.substr(10).c_str() ); + } +#if 0 + else if ( arg.find("--chunk=") == 0 ) + { + tg::Rectangle rectangle = tg::parseChunk(arg.substr(8).c_str(), 10.0); min_lon = rectangle.getMin().x(); min_lat = rectangle.getMin().y(); max_lon = rectangle.getMax().x(); max_lat = rectangle.getMax().y(); - } else if ( arg.find("--tile=") == 0 ) { + } + else if ( arg.find("--tile=") == 0 ) + { tg::Rectangle rectangle = tg::parseTile(arg.substr(7).c_str()); min_lon = rectangle.getMin().x(); min_lat = rectangle.getMin().y(); max_lon = rectangle.getMax().x(); max_lat = rectangle.getMax().y(); - } else if ( arg.find("--airport=") == 0 ) { - airport_id = arg.substr(10).c_str(); - ready_to_go = false; - } else if ( arg == "--clear-dem-path" ) { - elev_src.clear(); - } else if ( arg.find("--dem-path=") == 0 ) { - elev_src.push_back( arg.substr(11) ); - } else if ( (arg.find("--verbose") == 0) || (arg.find("-v") == 0) ) { - sglog().setLogLevels( SG_GENERAL, SG_BULK ); - } else if ( (arg.find("--max-slope=") == 0) ) { - slope_max = atof( arg.substr(12).c_str() ); - } else if ( (arg.find("--help") == 0) || (arg.find("-h") == 0) ) { - help( argc, argv, elev_src ); - exit(-1); - } else { - usage( argc, argv ); - exit(-1); - } + } +#endif + else if ( arg.find("--airport=") == 0 ) + { + airport_id = arg.substr(10).c_str(); + ready_to_go = false; + } + else if ( arg == "--clear-dem-path" ) + { + elev_src.clear(); + } + else if ( arg.find("--dem-path=") == 0 ) + { + elev_src.push_back( arg.substr(11) ); + } + else if ( (arg.find("--verbose") == 0) || (arg.find("-v") == 0) ) + { + sglog().setLogLevels( SG_GENERAL, SG_BULK ); + } + else if ( arg.find("--view") == 0 ) + { + SG_LOG(SG_GENERAL, SG_INFO, "Found --view : view OSG model" ); + view_osg = true; + } + else if ( (arg.find("--max-slope=") == 0) ) + { + slope_max = atof( arg.substr(12).c_str() ); + } +#if 0 + else if ( (arg.find("--help") == 0) || (arg.find("-h") == 0) ) + { + help( argc, argv, elev_src ); + exit(-1); + } +#endif + else + { + usage( argc, argv ); + exit(-1); + } } SG_LOG(SG_GENERAL, SG_INFO, "Input file = " << input_file); SG_LOG(SG_GENERAL, SG_INFO, "Terrain sources = "); - for ( unsigned int i = 0; i < elev_src.size(); ++i ) { + for ( unsigned int i = 0; i < elev_src.size(); ++i ) + { SG_LOG(SG_GENERAL, SG_INFO, " " << work_dir << "/" << elev_src[i] ); } SG_LOG(SG_GENERAL, SG_INFO, "Work directory = " << work_dir); @@ -210,26 +246,29 @@ int main( int argc, char **argv ) { SG_LOG(SG_GENERAL, SG_INFO, "Latitude = " << min_lat << ':' << max_lat); if (max_lon < min_lon || max_lat < min_lat || - min_lat < -90 || max_lat > 90 || - min_lon < -180 || max_lon > 180) { + min_lat < -90 || max_lat > 90 || + min_lon < -180 || max_lon > 180) + { SG_LOG(SG_GENERAL, SG_ALERT, "Bad longitude or latitude"); - exit(1); + exit(1); } - if ( work_dir == "" ) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Error: no work directory specified." ); - usage( argc, argv ); - exit(-1); + if ( work_dir == "" ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "Error: no work directory specified." ); + usage( argc, argv ); + exit(-1); } - if ( input_file == "" ) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Error: no input file." ); - exit(-1); + if ( input_file == "" ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "Error: no input file." ); + exit(-1); } // make work directory + SG_LOG(SG_GENERAL, SG_INFO, "Creating airportarea directory"); + string airportareadir=work_dir+"/AirportArea"; SGPath sgp( airportareadir ); sgp.append( "dummy" ); @@ -242,264 +281,60 @@ int main( int argc, char **argv ) { poly_index_init( counter_file ); sg_gzifstream in( input_file ); - if ( !in.is_open() ) { + if ( !in.is_open() ) + { SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << input_file ); exit(-1); } - string_list runways_list; - string_list water_rw_list; - string_list beacon_list; - string_list tower_list; - string_list windsock_list; - string_list light_list; + SG_LOG(SG_GENERAL, SG_INFO, "Creating parser"); - vector<string> token; - string last_apt_id = ""; - string last_apt_info = ""; - string last_apt_type = ""; - string line; - char tmp[2048]; + // Create the parser... + Parser* parser = new Parser(input_file); - while ( ! in.eof() ) { - in.getline(tmp, 2048); - line = tmp; - SG_LOG( SG_GENERAL, SG_DEBUG, "-> '" << line << "'" ); - if ( line.length() ) { - token = simgear::strutils::split( line ); - if ( token.size() ) { - SG_LOG( SG_GENERAL, SG_DEBUG, "token[0] " << token[0] ); - } - } else { - token.clear(); - } + SG_LOG(SG_GENERAL, SG_INFO, "Parse katl"); + parser->Parse((char*)"katl"); - if ( !line.length() || !token.size() ) { - // empty line, skip - } else if ( (token[0] == "#") || (token[0] == "//") ) { - // comment, skip - } else if ( token[0] == "I" ) { - // First line, indicates IBM (i.e. DOS line endings I - // believe.) + if (view_osg) + { + // just view in OSG + osg::Group* airportNode; - // move past this line and read and discard the next line - // which is the version and copyright information - in.getline(tmp, 2048); - vector<string> vers_token = simgear::strutils::split( tmp ); - SG_LOG( SG_GENERAL, SG_INFO, "Data version = " << vers_token[0] ); - } else if ( token[0] == "1" /* Airport */ || - token[0] == "16" /* Seaplane base */ || - token[0] == "17" /* Heliport */ ) { + SG_LOG(SG_GENERAL, SG_INFO, "View OSG"); + airportNode = parser->CreateOsgGroup(); - // extract some airport runway info - string rwy; - float lat, lon; - - string id = token[4]; - int elev = atoi( token[1].c_str() ); - SG_LOG( SG_GENERAL, SG_INFO, "Next airport = " << id << " " << elev ); + // construct the viewer. + osgViewer::Viewer viewer; - if ( !last_apt_id.empty()) { - if ( runways_list.size() ) { - vector<string> rwy_token - = simgear::strutils::split( runways_list[0] ); - if ( token[0] == "100" ){ - rwy = token[8]; - lat = atof( token[9].c_str() ); - lon = atof( token[10].c_str() ); - } + // add the thread model handler + viewer.addEventHandler(new osgViewer::ThreadingHandler); - if ( airport_id.length() && airport_id == last_apt_id ) { - ready_to_go = true; - } else if ( start_id.length() && start_id == last_apt_id ) { - ready_to_go = true; - } + // add the window size toggle handler + viewer.addEventHandler(new osgViewer::WindowSizeHandler); - if ( ready_to_go ) { - // check point our location - char command[256]; - sprintf( command, - "echo before building %s >> %s", - last_apt_id.c_str(), - lastaptfile.c_str() ); - system( command ); + // add model to viewer. + viewer.setSceneData( airportNode ); + viewer.setUpViewAcrossAllScreens(); - // process previous record - // process_airport(last_apt_id, runways_list, argv[2]); - try { - if ( last_apt_type == "16" /* Seaplane base */ ) { - // skip building seaplane bases - } else { - if( is_in_range( runways_list, min_lat, max_lat, min_lon, max_lon ) ) { - build_airport( last_apt_id, - elev * SG_FEET_TO_METER, - runways_list, - water_rw_list, - beacon_list, - tower_list, - windsock_list, - light_list, - work_dir, elev_src ); - } - } - } catch (sg_exception &e) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Failed to build airport = " - << last_apt_id ); - SG_LOG( SG_GENERAL, SG_ALERT, "Exception: " - << e.getMessage() ); - exit(-1); - } - if ( airport_id.length() ) { - ready_to_go = false; - } - } - } else { - if(!airport_id.length()) { - SG_LOG(SG_GENERAL, SG_INFO, - "ERRO: No runways, skipping = " << id); - } - } - } + // create the windows and run the threads. + viewer.realize(); + + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); - last_apt_id = id; - last_apt_info = line; - last_apt_type = token[0]; + osgUtil::Optimizer optimzer; + optimzer.optimize(airportNode); - // clear runway list for start of next airport - runways_list.clear(); - water_rw_list.clear(); - beacon_list.clear(); - tower_list.clear(); - windsock_list.clear(); - light_list.clear(); - } else if ( token[0] == "100" || token[0] == "102") { - // runway entry (Land or heli) - runways_list.push_back(line); - } else if ( token[0] == "101" ) { - // Water runway. Here we don't have to create any textures. - // Only place some buoys - water_rw_list.push_back(line); - } else if ( token[0] == "18" ) { - // beacon entry - beacon_list.push_back(line); - } else if ( token[0] == "14" ) { - // control tower entry - tower_list.push_back(line); - } else if ( token[0] == "19" ) { - // windsock entry - windsock_list.push_back(line); - } else if ( token[0] == "21" ) { - // PAPI / VASI list - light_list.push_back(line); - } else if ( token[0] == "15" ) { - // ignore custom startup locations - } else if ( token[0] == "50" || token[0] == "51" || token[0] == "52" - || token[0] == "53" || token[0] == "54" || token[0] == "55" - || token[0] == "56" ) - { - // ignore frequency entries - } else if ( token[0] == "99" ) { - SG_LOG( SG_GENERAL, SG_ALERT, "End of file reached" ); - } else if ( token[0] == "00" ) { - // ?? - } else if ( token[0] >= "110" ) { - //ignore taxiways for now - } else if ( token[0] == "10" ) { - //ignore old taxiways for now - } else { - SG_LOG( SG_GENERAL, SG_ALERT, - "Unknown line in file: " << line ); - exit(-1); - } + viewer.run(); + } + else + { + // write a .btg file.... + SG_LOG(SG_GENERAL, SG_INFO, "Write BTG"); + parser->WriteBtg(work_dir, elev_src); } - cout << "last_apt_id.length() = " << last_apt_id.length() << endl; - - if ( !last_apt_id.empty()) { - char ctmp, tmpid[32], rwy[32]; - string id; - float lat, lon; - int elev = 0; - - if ( runways_list.size() ) { - sscanf( runways_list[0].c_str(), "%c %s %s %f %f", - &ctmp, tmpid, rwy, &lat, &lon ); - } - - if ( start_id.length() && start_id == last_apt_id ) { - ready_to_go = true; - } - - if ( ready_to_go ) { - // check point our location - char command[256]; - sprintf( command, - "echo before building %s >> %s", - last_apt_id.c_str(), - lastaptfile.c_str() ); - system( command ); - - // process previous record - // process_airport(last_apt_id, runways_list, argv[2]); - try { - if ( last_apt_type == "16" /* Seaplane base */ ) { - // skip building seaplane bases - } else { - if( is_in_range( runways_list, min_lat, max_lat, min_lon, max_lon ) ) { - build_airport( last_apt_id, elev * SG_FEET_TO_METER, - runways_list, - water_rw_list, - beacon_list, - tower_list, - windsock_list, - light_list, - work_dir, elev_src ); - } - } - } catch (sg_exception &e) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Failed to build airport = " - << last_apt_id ); - SG_LOG( SG_GENERAL, SG_ALERT, "Exception: " - << e.getMessage() ); - exit(-1); - } - } else { - SG_LOG(SG_GENERAL, SG_INFO, "Skipping airport " << id); - } - } - - SG_LOG(SG_GENERAL, SG_INFO, "[FINISHED CORRECTLY]"); + SG_LOG(SG_GENERAL, SG_INFO, "Done"); return 0; } -static int is_in_range( string_list & runways_raw, float min_lat, float max_lat, float min_lon, float max_lon ) -{ - int i; - int rwy_count = 0; - double apt_lon = 0.0, apt_lat = 0.0; - - for ( i = 0; i < (int)runways_raw.size(); ++i ) { - ++rwy_count; - - string rwy_str = runways_raw[i]; - vector<string> token = simgear::strutils::split( rwy_str ); - - apt_lat += atof( token[1].c_str() ); - apt_lon += atof( token[2].c_str() ); - } - - if( rwy_count > 0 ) { - apt_lat /= rwy_count; - apt_lon /= rwy_count; - } - - if( apt_lat >= min_lat && apt_lat <= max_lat && - apt_lon >= min_lon && apt_lon <= max_lon ) { - return 1; - } - - return 0; -} diff --git a/src/Airports/GenAirports850/parser.cxx b/src/Airports/GenAirports850/parser.cxx new file mode 100644 index 00000000..13214a50 --- /dev/null +++ b/src/Airports/GenAirports850/parser.cxx @@ -0,0 +1,451 @@ +#include <simgear/debug/logstream.hxx> +#include <simgear/misc/sgstream.hxx> + +#include "parser.hxx" + +#if 0 +int Parser::ParseBoundry(char* line) +{ + return 0; +} +#endif + +int Parser::Parse( char* icao ) +{ + string line; + char tmp[2048]; + + sg_gzifstream in( filename ); + if ( !in.is_open() ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << filename ); + exit(-1); + } + + while ( !in.eof() ) + { + in.getline(tmp, 2048); + line = tmp; + + // Parse the line + ParseLine(tmp); + } + + return 1; +} + +BezNode* Parser::ParseNode( int type, char* line, BezNode* prevNode ) +{ + double lat, lon; + double ctrl_lat, ctrl_lon; + int mark_type, light_type; + BezNode *curNode = NULL; + + bool hasCtrl; + bool close; + bool term; + bool hasMarking = false; + bool hasLighting = false; + int numParams; + + switch(type) + { + case NODE_CODE: + hasCtrl = false; + close = false; + term = false; + break; + + case BEZIER_NODE_CODE: + hasCtrl = true; + close = false; + term = false; + break; + + case CLOSE_NODE_CODE: + hasCtrl = false; + close = true; + term = false; + break; + + case CLOSE_BEZIER_NODE_CODE: + hasCtrl = true; + close = true; + term = false; + break; + + case TERM_NODE_CODE: + hasCtrl = false; + close = false; + term = true; + break; + + case TERM_BEZIER_NODE_CODE: + hasCtrl = true; + close = false; + term = true; + break; + } + + // parse the line + if (hasCtrl) + { + numParams = sscanf(line, "%lf %lf %lf %lf %d %d", &lat, &lon, &ctrl_lat, &ctrl_lon, &mark_type, &light_type); + if (numParams > 4) + { + hasMarking = true; + } + if (numParams > 5) + { + hasLighting = true; + } + } + else + { + numParams = sscanf(line, "%lf %lf %d %d", &lat, &lon, &mark_type, &light_type); + if (numParams > 2) + { + hasMarking = true; + } + if (numParams > 3) + { + hasLighting = true; + } + } + + if ( (prevNode) && (prevNode->IsAt( lat, lon )) ) + { + curNode = prevNode; + + // editing already existent node + if (hasCtrl) + { + // we have ctrl info -> set it + curNode->SetNextCp(ctrl_lat,ctrl_lon); + } + else + { + // no control info - make sure we clear anything in next + curNode->ClearNextCp(); + } + } + + // if this is a new node, add it - as first part never has prev cp + if (curNode == NULL) + { + if (hasCtrl) + { + curNode = new BezNode(lat, lon, ctrl_lat, ctrl_lon); + } + else + { + curNode = new BezNode(lat, lon); + } + } + + if (hasMarking) + { + curNode->SetMarking( mark_type ); + } + + if (hasLighting) + { + curNode->SetLighting( light_type ); + } + + return curNode; +} + +LinearFeature* Parser::ParseFeature( char* line ) +{ + LinearFeature* feature; + + if (strlen( line )) + { + feature = new LinearFeature(line); + } + else + { + feature = new LinearFeature(NULL); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Creating Linear Feature with desription \"" << line << "\""); + + return feature; +} + +ClosedPoly* Parser::ParsePavement( char* line ) +{ + ClosedPoly* poly; + int st = 0; + float s = 0.0f; + float th = 0.0f; + char desc[256]; + char *d = NULL; + int numParams; + + numParams = sscanf(line, "%d %f %f %ls", &st, &s, &th, desc); + + if (numParams == 4) + { + d = strstr(line,desc); + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Creating Closed Poly with st " << st << " smoothness " << s << " thexture heading " << th << " and description " << d); + poly = new ClosedPoly(st, s, th, d); + + return poly; +} + +int Parser::SetState( int state ) +{ + // if we are currently parsing pavement, the oly way we know we are done + // is when we get a non-node line to parse + if ( cur_airport && cur_state == STATE_PARSE_PAVEMENT ) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Closing and Adding pavement"); + cur_pavement->Finish(); + cur_airport->AddPavement( cur_pavement ); + cur_pavement = NULL; + } + + cur_state = state; +} + +// TODO: This should be a loop here, and main should just pass the file name and airport code... +int Parser::ParseLine(char* line) +{ + char* tok; + int code; + + BezNode* cur_node = NULL; + + // Get the number code + tok = strtok(line, " \t\r\n"); + + if (tok) + { + line += strlen(tok)+1; + code = atoi(tok); + + switch(code) + { + case LAND_AIRPORT_CODE: + case SEA_AIRPORT_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing land airport: " << line); + cur_airport = new Airport( code, line ); + airports.push_back( cur_airport ); + break; + case HELIPORT_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing heliport: " << line); + break; + + case LAND_RUNWAY_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing runway: " << line); + cur_runway = new Runway(line); + if (cur_airport) + { + cur_airport->AddRunway( cur_runway ); + } + break; + + case WATER_RUNWAY_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing water runway: " << line); + break; + case HELIPAD_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing helipad: " << line); + break; + + case PAVEMENT_CODE: + SetState( STATE_PARSE_PAVEMENT ); + cur_pavement = ParsePavement( line ); + break; + + case LINEAR_FEATURE_CODE: + SetState( STATE_PARSE_FEATURE ); + cur_feat = ParseFeature( line ); + break; + + case BOUNDRY_CODE: + SetState( STATE_PARSE_BOUNDARY ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing airport boundary: " << line); +// ParseBoundry(line); + break; + + case NODE_CODE: + case BEZIER_NODE_CODE: + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing node: " << line); + cur_node = ParseNode( code, line, prev_node ); + + if ( prev_node && (cur_node != prev_node) ) + { + // prev node is done - process it\n"); + if ( cur_state == STATE_PARSE_PAVEMENT ) + { + cur_pavement->AddNode( prev_node ); + } + else if ( cur_state == STATE_PARSE_FEATURE ) + { + cur_feat->AddNode( prev_node ); + } + } + + prev_node = cur_node; + break; + + case CLOSE_NODE_CODE: + case CLOSE_BEZIER_NODE_CODE: + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing close loop node: " << line); + cur_node = ParseNode( code, line, prev_node ); + + if ( cur_state == STATE_PARSE_PAVEMENT ) + { + if (cur_node != prev_node) + { + cur_pavement->AddNode( prev_node ); + cur_pavement->AddNode( cur_node ); + } + else + { + cur_pavement->AddNode( cur_node ); + } + cur_pavement->CloseCurContour(); + } + else if ( cur_state == STATE_PARSE_FEATURE ) + { + if (cur_node != prev_node) + { + cur_feat->AddNode( prev_node ); + cur_feat->AddNode( cur_node ); + } + else + { + cur_feat->AddNode( cur_node ); + } + if (cur_airport) + { + cur_airport->AddFeature( cur_feat ); + } + SetState( STATE_NONE ); + } + prev_node = NULL; + cur_node = NULL; + break; + + case TERM_NODE_CODE: + case TERM_BEZIER_NODE_CODE: + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing termination node: " << line); + cur_node = ParseNode( code, line, prev_node ); + + if ( cur_state == STATE_PARSE_FEATURE ) + { + if (cur_node != prev_node) + { + cur_feat->AddNode( prev_node ); + cur_feat->AddNode( cur_node ); + } + else + { + cur_feat->AddNode( cur_node ); + } + if (cur_airport) + { + cur_airport->AddFeature( cur_feat ); + } + SetState( STATE_NONE ); + } + prev_node = NULL; + cur_node = NULL; + break; + + case AIRPORT_VIEWPOINT_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing viewpoint: " << line); + break; + case AIRPLANE_STARTUP_LOCATION_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing airplane startup location: " << line); + break; + case LIGHT_BEACON_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing light beacon: " << line); + break; + case WINDSOCK_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing windsock: " << line); + break; + case TAXIWAY_SIGN: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing taxiway sign: " << line); + break; + case LIGHTING_OBJECT: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing lighting object: " << line); + break; + case COMM_FREQ1_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 1: " << line); + break; + case COMM_FREQ2_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 2: " << line); + break; + case COMM_FREQ3_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 3: " << line); + break; + case COMM_FREQ4_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 4: " << line); + break; + case COMM_FREQ5_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 5: " << line); + break; + case COMM_FREQ6_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 6: " << line); + break; + case COMM_FREQ7_CODE: + SetState( STATE_PARSE_SIMPLE ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 7: " << line); + break; + case END_OF_FILE : + SG_LOG(SG_GENERAL, SG_DEBUG, "Reached end of file"); + SetState( STATE_DONE ); + break; + } + } + + return 0; +} + +void Parser::WriteBtg( const string& root, const string_list& elev_src ) +{ + int i; + + for (i=0; i<airports.size(); i++) + { + airports[i]->BuildBtg( root, elev_src ); + } +} + +osg::Group* Parser::CreateOsgGroup( void ) +{ + osg::Group* airportNode = new osg::Group(); + int i; + + for (i=0; i<airports.size(); i++) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Add airport " << i << " to node"); + airports[i]->BuildOsg( airportNode ); + } + + return airportNode; +} + diff --git a/src/Airports/GenAirports850/parser.hxx b/src/Airports/GenAirports850/parser.hxx new file mode 100644 index 00000000..a8f9328c --- /dev/null +++ b/src/Airports/GenAirports850/parser.hxx @@ -0,0 +1,99 @@ +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include "beznode.hxx" +#include "closedpoly.hxx" +#include "linearfeature.hxx" +#include "runway.hxx" +#include "airport.hxx" + +#define STATE_NONE (0) +#define STATE_PARSE_SIMPLE (1) +#define STATE_PARSE_BOUNDARY (2) +#define STATE_PARSE_PAVEMENT (3) +#define STATE_PARSE_FEATURE (4) +#define STATE_DONE (10) + +#define MAXLINE (256) + +#define LAND_AIRPORT_CODE (1) +#define SEA_AIRPORT_CODE (16) +#define HELIPORT_CODE (17) + +#define LAND_RUNWAY_CODE (100) +#define WATER_RUNWAY_CODE (101) +#define HELIPAD_CODE (102) + +#define PAVEMENT_CODE (110) +#define LINEAR_FEATURE_CODE (120) +#define BOUNDRY_CODE (130) + +#define NODE_CODE (111) +#define BEZIER_NODE_CODE (112) +#define CLOSE_NODE_CODE (113) +#define CLOSE_BEZIER_NODE_CODE (114) +#define TERM_NODE_CODE (115) +#define TERM_BEZIER_NODE_CODE (116) + +#define AIRPORT_VIEWPOINT_CODE (14) +#define AIRPLANE_STARTUP_LOCATION_CODE (15) +#define LIGHT_BEACON_CODE (18) +#define WINDSOCK_CODE (19) +#define TAXIWAY_SIGN (20) +#define LIGHTING_OBJECT (21) + +#define COMM_FREQ1_CODE (50) +#define COMM_FREQ2_CODE (51) +#define COMM_FREQ3_CODE (52) +#define COMM_FREQ4_CODE (53) +#define COMM_FREQ5_CODE (54) +#define COMM_FREQ6_CODE (55) +#define COMM_FREQ7_CODE (56) + +#define END_OF_FILE (99) + +class Parser +{ +public: + Parser(string& f) + { + filename = f; + cur_airport = NULL; + cur_pavement = NULL; + cur_feat = NULL; + prev_node = NULL; + cur_state = STATE_NONE; + } + + int Parse( char* icao ); + void WriteBtg( const string& root, const string_list& elev_src ); + osg::Group* CreateOsgGroup( void ); + +private: + int SetState( int state ); + + BezNode* ParseNode( int type, char* line, BezNode* prevNode ); + LinearFeature* ParseFeature( char* line ); + ClosedPoly* ParsePavement( char* line ); + osg::Geode* ParseRunway(char* line ); + int ParseLine( char* line ); + +// int ParseBoundry(char* line, Node* airport); + + string filename; + + // a polygon conists of an array of contours + // (first is outside boundry, remaining are holes) + Airport* cur_airport; + Runway* cur_runway; + ClosedPoly* cur_pavement; + LinearFeature* cur_feat; + BezNode* prev_node; + + AirportList airports; + + int cur_state; +}; + +#endif + diff --git a/src/Airports/GenAirports850/runway.cxx b/src/Airports/GenAirports850/runway.cxx index eac68614..9b4f5328 100644 --- a/src/Airports/GenAirports850/runway.cxx +++ b/src/Airports/GenAirports850/runway.cxx @@ -1,49 +1,113 @@ -// area.c -- routines to assist with inserting "areas" into FG terrain -// -// Written by Curtis Olson, started March 1998. -// -// Copyright (C) 1998 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id: runway.cxx,v 1.18 2004-11-19 22:25:49 curt Exp $ -// - -#include <math.h> -#include <stdio.h> - -#include <simgear/constants.h> +#include <simgear/compiler.h> +#include <simgear/structure/exception.hxx> #include <simgear/debug/logstream.hxx> -#include <simgear/math/sg_types.hxx> +#include <simgear/bucket/newbucket.hxx> #include <simgear/math/sg_geodesy.hxx> +#include <osg/Geode> +#include <osg/Geometry> +#include <osg/Group> + +#include <Polygon/polygon.hxx> + +#include "beznode.hxx" #include "runway.hxx" -#include "point2d.hxx" +extern int nudge; -// given a runway center point, length, width, and heading, and -// altitude (meters) generate the lon and lat 4 corners using wgs84 -// math. -TGPolygon gen_wgs84_area( Point3D origin, - double length_m, - double displ1, double displ2, - double width_m, - double heading_deg, - double alt_m, - bool add_mid ) +Runway::Runway(char* definition) +{ + double az2; + + // format: + // runway width surface shoulder smoothness centerline lights edge lighting distance remaining signs + // 100 46.02 2 1 0.00 1 2 1 + // + // runway number runway end lat runway end long threshold overrun markings approach lighting + // 09L 33.63470475 -084.44798671 0.00 120.09 3 7 + // + // touchdown zone lighting runway end identifier lights + // 0 1 + // + // runway number runway end lat runway end long threshold overrun markings approach lighting + // 27R 33.63469907 -084.40893004 0.00 120.09 3 6 + // + // touchdown zone lighting runway end identifier lights + // 0 1 + + // Parse the line + // 46.02 2 1 0.00 1 2 1 09L 33.63470475 -084.44798671 0.00 120.09 3 7 0 1 27R 33.63469907 -084.40893004 0.00 120.09 3 6 0 1 + + // int fscanf(FILE *stream, const char *format, ...); + sscanf(definition, "%lf %d %d %lf %d %d %d %s %lf %lf %lf %lf %d %d %d %d %s %lf %lf %lf %lf %d %d %d %d", + &width, &surface, &shoulder, &smoothness, ¢erline_lights, &edge_lights, &dist_remain_signs, + rwnum[0], &lat[0], &lon[0], &threshold[0], &overrun[0], &marking[0], &approach_lights[0], &tz_lights[0], &reil[0], + rwnum[1], &lat[1], &lon[1], &threshold[1], &overrun[1], &marking[1], &approach_lights[1], &tz_lights[1], &reil[1] + ); + + // calculate runway heading and length (used a lot) + geo_inverse_wgs_84( lat[0], lon[0], lat[1], lon[1], &heading, &az2, &length ); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Read runway: (" << lon[0] << "," << lat[0] << ") to (" << lon[1] << "," << lat[1] << ") heading: " << heading << " length: " << length << " width: " << width ); +} + +int Runway::BuildOsg ( osg::Group* airport ) +{ + // calculated distance, and azimuths + double az1, az2, dist; + + // rectangle verticies + double v0_lat, v0_lon, v1_lat, v1_lon, v2_lat, v2_lon, v3_lat, v3_lon; + + // Create a node for the runway + osg::Geode* geode = new osg::Geode(); + + osg::Geometry* geometry = new osg::Geometry; + osg::Vec3dArray& v = *(new osg::Vec3dArray(4)); + + // first, find the runway direction vector + // static int geo_inverse_wgs_84( double lat1, double lon1, double lat2, double lon2, double *az1, double *az2, double *s ) + geo_inverse_wgs_84( lat[0], lon[0], lat[1], lon[1], &az1, &az2, &dist); + + // now, need to caculate the 4 verticies + // static int geo_direct_wgs_84( double lat1, double lon1, double az1, double s, double *lat2, double *lon2, double *az2 ) + geo_direct_wgs_84( lat[0], lon[0], az1+90, width/2, &v0_lat, &v0_lon, &az2 ); + geo_direct_wgs_84( v0_lat, v0_lon, az1, dist, &v1_lat, &v1_lon, &az2 ); + geo_direct_wgs_84( v1_lat, v1_lon, az1-90, width, &v2_lat, &v2_lon, &az2 ); + geo_direct_wgs_84( v2_lat, v2_lon, az1+180, dist, &v3_lat, &v3_lon, &az2 ); + + // convert from lat/lon to geodisc + // (maybe later) - check some simgear objects... + v[0].set(v0_lat, v0_lon, 0.0f); + v[1].set(v1_lat, v1_lon, 0.0f); + v[2].set(v2_lat, v2_lon, 0.0f); + v[3].set(v3_lat, v3_lon, 0.0f); + + geometry->setVertexArray(&v); + + osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLE_STRIP,4)); + geometry->addPrimitiveSet(&drawElements); + + drawElements[0] = 3; + drawElements[1] = 0; + drawElements[2] = 2; + drawElements[3] = 1; + + geode->addDrawable(geometry); + + airport->addChild( geode ); + + return 0; +} + +TGPolygon Runway::gen_wgs84_area( Point3D origin, + double length_m, + double displ1, double displ2, + double width_m, + double heading_deg, + double alt_m, + bool add_mid ) { TGPolygon result_list; double length_hdg = heading_deg; @@ -111,53 +175,19 @@ TGPolygon gen_wgs84_area( Point3D origin, } -// generate an area for a runway with expantion specified as a scale -// factor (return result points in degrees) -TGPolygon gen_runway_area_w_scale( const TGRunway& runway, - double alt_m, - double length_scale, - double width_scale ) { - - TGPolygon result_list; - Point3D origin(runway.lon, runway.lat, 0); - - result_list = gen_wgs84_area( origin, - runway.length*length_scale, - 0.0, 0.0, - runway.width*width_scale, - runway.heading, alt_m, false ); - - // display points - SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ scale (new way)"); - for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) { - SG_LOG(SG_GENERAL, SG_DEBUG, " " << result_list.get_pt(0, i)); - } - - return result_list; -} - - // generate an area for a runway with expansion specified in meters // (return result points in degrees) -TGPolygon gen_runway_area_w_extend( const TGRunway& runway, - double alt_m, - double length_extend, - double displ1, double displ2, - double width_extend ) { - +TGPolygon Runway::gen_runway_area_w_extend( double alt_m, double length_extend, double displ1, double displ2, double width_extend ) +{ TGPolygon result_list; - Point3D origin(runway.lon, runway.lat, 0); + Point3D origin = GetMidpoint(); - result_list - = gen_wgs84_area( origin, - runway.length + 2.0*length_extend, - displ1, displ2, - runway.width + 2.0*width_extend, - runway.heading, alt_m, false ); + result_list = gen_wgs84_area( origin, length + 2.0*length_extend, displ1, displ2, width + 2.0*width_extend, heading, alt_m, false ); // display points SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ extend (new way)"); - for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) { + for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) + { SG_LOG(SG_GENERAL, SG_DEBUG, " " << result_list.get_pt(0, i)); } @@ -166,26 +196,253 @@ TGPolygon gen_runway_area_w_extend( const TGRunway& runway, // generate an area for a runway and include midpoints -TGPolygon gen_runway_w_mid( const TGRunway& runway, - double alt_m, - double length_extend_m, - double width_extend_m ) { +TGPolygon Runway::gen_runway_w_mid( double alt_m, double length_extend_m, double width_extend_m ) +{ TGPolygon result_list; - Point3D origin(runway.lon, runway.lat, 0); + Point3D origin = GetMidpoint(); - result_list = gen_wgs84_area( origin, - runway.length - + 2.0*length_extend_m, - 0.0, 0.0, - runway.width - + 2.0 * width_extend_m, - runway.heading, alt_m, true ); + result_list = gen_wgs84_area( origin, length + 2.0*length_extend_m, 0.0, 0.0, width + 2.0 * width_extend_m, heading, alt_m, true ); // display points SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ mid (new way)"); - for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) { + for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) + { SG_LOG(SG_GENERAL, SG_DEBUG, " " << result_list.get_pt(0, i)); } return result_list; } + +#if 0 +void Runway::gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ) +{ + Point3D mp, end[2]; + int i; + + // create the runway in two halves, as markings may be different in each direction + end[0] = GetStart(); + mp = GetMidpoint(); + end[1] = GetEnd(); + + for (i=0; i<2; i++) + { + gen_simple_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum ); + } +} +#endif + +void Runway::gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ) +{ + int j, k; + + TGPolygon runway = gen_runway_w_mid( alt_m, 0.0, 0.0 ); + + // runway half "a" + TGPolygon runway_a; + runway_a.erase(); + runway_a.add_node( 0, runway.get_pt(0, 0) ); + runway_a.add_node( 0, runway.get_pt(0, 1) ); + runway_a.add_node( 0, runway.get_pt(0, 2) ); + runway_a.add_node( 0, runway.get_pt(0, 5) ); + + // runway half "b" + TGPolygon runway_b; + runway_b.erase(); + runway_b.add_node( 0, runway.get_pt(0, 5) ); + runway_b.add_node( 0, runway.get_pt(0, 2) ); + runway_b.add_node( 0, runway.get_pt(0, 3) ); + runway_b.add_node( 0, runway.get_pt(0, 4) ); + + Point3D p; + SG_LOG(SG_GENERAL, SG_DEBUG, "raw runway pts (a half)"); + for ( j = 0; j < runway_a.contour_size( 0 ); ++j ) + { + p = runway_a.get_pt(0, j); + SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p); + } + SG_LOG(SG_GENERAL, SG_DEBUG, "raw runway pts (b half)"); + for ( j = 0; j < runway_b.contour_size( 0 ); ++j ) + { + p = runway_b.get_pt(0, j); + SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p); + } + + TGSuperPoly sp; + TGTexParams tp; + + TGPolygon clipped_a = tgPolygonDiff( runway_a, *accum ); + TGPolygon split_a = tgPolygonSplitLongEdges( clipped_a, 400.0 ); + sp.erase(); + sp.set_poly( split_a ); + sp.set_material( material ); + rwy_polys->push_back( sp ); + SG_LOG(SG_GENERAL, SG_DEBUG, "clipped_a = " << clipped_a.contours()); + *accum = tgPolygonUnion( runway_a, *accum ); + tp = TGTexParams( runway_a.get_pt(0,0), width, length/2.0, heading ); + texparams->push_back( tp ); + + TGPolygon clipped_b = tgPolygonDiff( runway_b, *accum ); + TGPolygon split_b = tgPolygonSplitLongEdges( clipped_b, 400.0 ); + sp.erase(); + sp.set_poly( split_b ); + sp.set_material( material ); + rwy_polys->push_back( sp ); + SG_LOG(SG_GENERAL, SG_DEBUG, "clipped_b = " << clipped_b.contours()); + *accum = tgPolygonUnion( runway_b, *accum ); + tp = TGTexParams( runway_b.get_pt(0,2), width, length/2.0, heading+180.0 ); + texparams->push_back( tp ); + + // print runway points + SG_LOG(SG_GENERAL, SG_DEBUG, "clipped runway pts (a)"); + for ( j = 0; j < clipped_a.contours(); ++j ) + { + for ( k = 0; k < clipped_a.contour_size( j ); ++k ) + { + p = clipped_a.get_pt(j, k); + SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p); + } + } + + // print runway points + SG_LOG(SG_GENERAL, SG_DEBUG, "clipped runway pts (b)"); + for ( j = 0; j < clipped_b.contours(); ++j ) + { + for ( k = 0; k < clipped_b.contour_size( j ); ++k ) + { + p = clipped_b.get_pt(j, k); + SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p); + } + } + +#if 0 + gen_runway_stopway( rwy_info, runway_a, runway_b, + material, + rwy_polys, texparams, accum ); +#endif +} + +#if 0 +void Runway::gen_marked_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ) +{ + Point3D mp, end[2]; + int i; + + // create the runway in two halves, as markings may be different in each direction + end[0] = GetStart(); + mp = GetMidpoint(); + end[1] = GetEnd(); + + for (i=0; i<2; i++) + { + // first, create half 'a' + switch( marking[i] ) + { + case 0: + case 1: // Visual + SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Visual marking"); + gen_visual_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum ); + break; + + case 2: // non-precision + SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Non Precision marking"); + gen_non_precision_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum ); + break; + + case 3: // precision + SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Precision marking"); + //gen_precision_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum ); + gen_simple_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum ); + break; + + default: // unknown + SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has unknown marking"); + break; + } + } +} +#endif + +int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ) +{ + TGPolygon base, safe_base; + string material; + + if ( surface == 1 /* Asphalt */ ) + { + material = "pa_tiedown"; + } + else if ( surface == 2 /* Concrete */ ) + { + material = "pc_tiedown"; + } + else if ( surface == 3 /* Turf/Grass */ ) + { + material = "grass_rwy"; + } + else if ( surface == 4 /* Dirt */ || surface == 5 /* Gravel */ ) + { + material = "dirt_rwy"; + } + else if ( surface == 12 /* Dry Lakebed */ ) + { + material = "dirt_rwy"; + } + else if ( surface == 13 /* Water runway (buoy's?) */ ) + { + // water + } + else + { + SG_LOG(SG_GENERAL, SG_WARN, "surface_code = " << surface); + throw sg_exception("unknown runway type!"); + } + + // first, check the surface type - anything but concrete and asphalt are easy + switch( surface ) + { + case 1: // asphalt: + case 2: // concrete + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: asphalt or concrete" << surface); + gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum ); + break; + + case 3: // Grass + case 4: // Dirt + case 5: // Gravel + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Turf, Dirt or Gravel" << surface ); + gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum ); + break; + + case 12: // dry lakebed + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Dry Lakebed"); + break; + + case 13: // water + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Water"); + break; + + case 14: // snow + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Snow"); + break; + + case 15: // transparent + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: transparent"); + break; + + default: // unknown + SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: unknown" << surface); + break; + } + + // generate area around runways + base = gen_runway_area_w_extend( 0.0, 20.0, -overrun[0], -overrun[1], 20.0 ); + + // also clear a safe area around the runway + safe_base = gen_runway_area_w_extend( 0.0, 180.0, -overrun[0], -overrun[1], 50.0 ); + + // add this to the airport clearing + *apt_clearing = tgPolygonUnion(safe_base, *apt_clearing); + + // and add the clearing to the base + *apt_base = tgPolygonUnion( base, *apt_base ); +} diff --git a/src/Airports/GenAirports850/runway.hxx b/src/Airports/GenAirports850/runway.hxx index 34f6ab1d..fa335982 100644 --- a/src/Airports/GenAirports850/runway.hxx +++ b/src/Airports/GenAirports850/runway.hxx @@ -1,129 +1,84 @@ -// runway.hxx -- class to store runway info -// -// Written by Curtis Olson, started November 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id: runway.hxx,v 1.16 2005-04-20 18:20:15 curt Exp $ -// +#ifndef _RUNWAY_H_ +#define _RUNWAY_H_ - -#ifndef _RUNWAY_HXX -#define _RUNWAY_HXX - - -#include <string> -#include <vector> - -#include <Geometry/point3d.hxx> +#include <stdio.h> +#include <stdlib.h> #include <Polygon/polygon.hxx> +#include <Polygon/superpoly.hxx> +#include <Geometry/point3d.hxx> +#include "texparams.hxx" -struct TGRunway { - int type; - std::string rwy_no1; - std::string rwy_no2; +#include <osg/Group> - double lon; - double lat; - double heading; - double length; - double width; - double disp_thresh1; - double disp_thresh2; - double stopway1; - double stopway2; +using std::string; - int surface_code; - int shoulder_code; - double smoothness; - bool centre_lights; - int edge_lights; - int marking_code1; - int marking_code2; - int alc1_flag; - int alc2_flag; - bool has_tdz1; - bool has_tdz2; - int reil1; - int reil2; +class Runway +{ +public: + Runway(char* def); - bool dist_remaining; + bool IsPrecision() + { + return true; + } - TGPolygon threshold; - TGPolygon tens, tens_margin, ones, ones_margin; - TGPolygon letter, letter_margin_left, letter_margin_right; - TGPolygon pre_td_zone; - TGPolygon td3_zone, td2_zone, td1a_zone, td1b_zone; - TGPolygon aim_point; - bool generated; + Point3D GetStart(void) + { + return ( Point3D( lon[0], lat[0], 0.0f )); + } + + Point3D GetEnd(void) + { + return ( Point3D( lon[1], lat[1], 0.0f )); + } + + Point3D GetMidpoint(void) + { + return ( Point3D( (lon[0]+lon[1])/2.0f, (lat[0]+lat[1])/2.0f, 0.0f) ); + } + + int BuildOsg( osg::Group* airport ); + int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing ); + +private: + // data for whole runway + int surface; + int shoulder; + int centerline_lights; + int edge_lights; + int dist_remain_signs; + + double width; + double length; + double heading; + double smoothness; + + // data for each end + char rwnum[2][16]; + double lat[2]; + double lon[2]; + double threshold[2]; + double overrun[2]; + + int marking[2]; + int approach_lights[2]; + int tz_lights[2]; + int reil[2]; + + // Build Helpers + TGPolygon gen_wgs84_area( Point3D origin, double length_m, double displ1, double displ2, double width_m, double heading_deg, double alt_m, bool add_mid ); + TGPolygon gen_runway_w_mid( double alt_m, double length_extend_m, double width_extend_m ); +// void gen_runway_section( const TGPolygon& runway, double startl_pct, double endl_pct, double startw_pct, double endw_pct, double minu, double maxu, double minv, double maxv, double heading, +// const string& prefix, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ); +// void gen_runway_stopway( const TGPolygon& runway_a, const TGPolygon& runway_b, const string& prefix, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon* accum ); + TGPolygon gen_runway_area_w_extend( double alt_m, double length_extend, double displ1, double displ2, double width_extend ); + + void gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ); + void gen_marked_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum ); }; -typedef std::vector < TGRunway > runway_list; -typedef runway_list::iterator runway_list_iterator; -typedef runway_list::const_iterator const_runway_list_iterator; -struct TGLightobj { - double lon; - double lat; - int type; - double heading; - double glideslope; - std::string rwy_name; -}; -typedef std::vector < TGLightobj > light_list; -typedef light_list::iterator light_list_iterator; -typedef light_list::const_iterator const_light_list_iterator; +typedef std::vector <Runway *> RunwayList; - - - -// given a runway center point, length, width, and heading, and -// altitude (meters) generate the lon and lat 4 corners using wgs84 -// math. -TGPolygon gen_wgs84_area( Point3D origin, - double length_m, - double displ1, double displ2, - double width_m, - double heading_deg, - double alt_m, - bool add_mid ); - -// generate an area for a runway with expantion specified as a scale -// factor (return result points in degrees) -TGPolygon gen_runway_area_w_scale( const TGRunway& runway, - double alt_m, - double length_scale = 1.0, - double width_scale = 1.0 ); - -// generate an area for a runway with expansion specified in meters -// (return result points in degrees) -TGPolygon gen_runway_area_w_extend( const TGRunway& runway, - double alt_m, - double length_extend, - double displ1, double displ2, - double width_extend ); - - -// generate an area for half a runway -TGPolygon gen_runway_w_mid( const TGRunway& runway, - double alt_m, - double length_extend_m, - double width_extend_m ); - - -#endif // _RUNWAY_HXX +#endif