1
0
Fork 0
terragear/src/Airports/GenAirports/lights.cxx
2002-10-18 18:29:18 +00:00

2069 lines
66 KiB
C++

// lights.cxx -- Generate runway lighting
//
// Written by Curtis Olson, started February 2002.
//
// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org
//
// 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$
//
#include <simgear/math/sg_geodesy.hxx>
#include "lights.hxx"
// calculate the runway light direction vector. We take the center of
// one runway end - the center of the other end to get the direction
// of the runway. Combine this with an appropriate portion of the
// local 'up' vector based on the provide 'angle' gives the light
// direction vector for the runway.
static Point3D gen_runway_light_vector( const FGRunway& rwy_info,
double angle, bool recip ) {
double length;
// Generate the 4 corners of the runway
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( int i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D end1, end2;
if ( !recip ) {
end1 = (corner[0] + corner[1]) / 2.0;
end2 = (corner[2] + corner[3]) / 2.0;
} else {
end2 = (corner[0] + corner[1]) / 2.0;
end1 = (corner[2] + corner[3]) / 2.0;
}
Point3D cart1 = sgGeodToCart( end1 * SG_DEGREES_TO_RADIANS );
Point3D cart2 = sgGeodToCart( end2 * SG_DEGREES_TO_RADIANS );
cout << "cart1 = " << cart1 << " cart2 = " << cart2 << endl;
Point3D up = cart1;
length = up.distance3D( Point3D(0.0) );
up = up / length;
Point3D rwy_vec = cart2 - cart1;
cout << "rwy_vec = " << rwy_vec << endl;
// angle up specified amount
length = rwy_vec.distance3D( Point3D(0.0) );
double up_length = length * tan(angle * SG_DEGREES_TO_RADIANS);
Point3D light_vec = rwy_vec + (up * up_length);
length = light_vec.distance3D( Point3D(0.0) );
light_vec = light_vec / length;
return light_vec;
}
// calculate the runway length vector. We take the center of one
// runway end - the center of the other end to get the direction of
// the runway.
static Point3D gen_runway_length_vector( const FGRunway& rwy_info, bool recip )
{
double length;
// Generate the 4 corners of the runway
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( int i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D end1, end2;
if ( recip ) {
end2 = (corner[0] + corner[1]) / 2.0;
end1 = (corner[2] + corner[3]) / 2.0;
} else {
end1 = (corner[0] + corner[1]) / 2.0;
end2 = (corner[2] + corner[3]) / 2.0;
}
Point3D cart1 = sgGeodToCart( end1 * SG_DEGREES_TO_RADIANS );
Point3D cart2 = sgGeodToCart( end2 * SG_DEGREES_TO_RADIANS );
cout << "cart1 = " << cart1 << " cart2 = " << cart2 << endl;
Point3D rwy_vec = cart2 - cart1;
cout << "rwy_vec = " << rwy_vec << endl;
length = rwy_vec.distance3D( Point3D(0.0) );
rwy_vec = rwy_vec / length;
return rwy_vec;
}
// calculate a vector orthogonal to the runway direction in the
// surface plane. As you approach the runway, positive will be left.
static Point3D gen_runway_left_vector( const FGRunway& rwy_info, bool recip )
{
double length;
// Generate the 4 corners of the runway
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( int i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D end1, end2;
if ( recip ) {
end1 = corner[2];
end2 = corner[3];
} else {
end1 = corner[0];
end2 = corner[1];
}
Point3D cart1 = sgGeodToCart( end1 * SG_DEGREES_TO_RADIANS );
Point3D cart2 = sgGeodToCart( end2 * SG_DEGREES_TO_RADIANS );
cout << "cart1 = " << cart1 << " cart2 = " << cart2 << endl;
Point3D left_vec = cart2 - cart1;
cout << "left_vec = " << left_vec << endl;
length = left_vec.distance3D( Point3D(0.0) );
left_vec = left_vec / length;
return left_vec;
}
// generate runway edge lighting
// 60 meters spacing or the next number down that divides evenly.
static superpoly_list gen_runway_edge_lights( const FGRunway& rwy_info,
const string& kind, bool recip )
{
point_list w_lights; w_lights.clear();
point_list y_lights; y_lights.clear();
point_list w_normals; w_normals.clear();
point_list y_normals; y_normals.clear();
int i;
double len = rwy_info.length * SG_FEET_TO_METER;
int divs = (int)(len / 60.0) + 1;
Point3D normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 2.0, 2.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D inc1, inc2;
Point3D pt1, pt2;
if ( recip ) {
inc1 = (corner[3] - corner[0]) / divs;
inc2 = (corner[2] - corner[1]) / divs;
pt1 = corner[0];
pt2 = corner[1];
} else {
inc1 = (corner[0] - corner[3]) / divs;
inc2 = (corner[1] - corner[2]) / divs;
pt1 = corner[3];
pt2 = corner[2];
}
double dist = rwy_info.length;
double step = dist / divs;
w_lights.push_back( pt1 );
w_normals.push_back( normal );
w_lights.push_back( pt2 );
w_normals.push_back( normal );
dist -= step;
for ( i = 0; i < divs; ++i ) {
pt1 += inc1;
pt2 += inc2;
if ( dist > 2000.0 ) {
w_lights.push_back( pt1 );
w_normals.push_back( normal );
w_lights.push_back( pt2 );
w_normals.push_back( normal );
} else {
y_lights.push_back( pt1 );
y_normals.push_back( normal );
y_lights.push_back( pt2 );
y_normals.push_back( normal );
}
dist -= step;
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( w_lights, false );
normals_poly.add_contour( w_normals, false );
FGSuperPoly white;
white.set_poly( lights_poly );
white.set_normals( normals_poly );
if ( kind == "H" ) {
white.set_material( "RWY_WHITE_LIGHTS" );
} else if ( kind == "M" ) {
white.set_material( "RWY_WHITE_MEDIUM_LIGHTS" );
} else if ( kind == "L" ) {
white.set_material( "RWY_WHITE_LOW_LIGHTS" );
}
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( y_lights, false );
normals_poly.add_contour( y_normals, false );
FGSuperPoly yellow;
yellow.set_poly( lights_poly );
yellow.set_normals( normals_poly );
if ( kind == "H" ) {
yellow.set_material( "RWY_YELLOW_LIGHTS" );
} else if ( kind == "M" ) {
yellow.set_material( "RWY_YELLOW_MEDIUM_LIGHTS" );
} else if ( kind == "L" ) {
yellow.set_material( "RWY_YELLOW_LOW_LIGHTS" );
}
superpoly_list result; result.clear();
result.push_back( white );
result.push_back( yellow );
return result;
}
// generate threshold lights for a 3 degree approach
static superpoly_list gen_runway_threshold_lights( const FGRunway& rwy_info,
const string& kind,
float alt_m, bool recip )
{
point_list g_lights; g_lights.clear();
point_list g_normals; g_normals.clear();
point_list r_lights; r_lights.clear();
point_list r_normals; r_normals.clear();
int i;
cout << "gen threshold " << rwy_info.rwy_no << endl;
Point3D normal;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
// determine the start point.
Point3D ref1, ref2, ref3, ref4;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref1 = corner[0];
ref2 = corner[1];
ref3 = corner[3];
ref4 = corner[2];
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref1 = corner[2];
ref2 = corner[3];
ref3 = corner[1];
ref4 = corner[0];
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// offset 5' downwind
geo_direct_wgs_84 ( alt_m, ref1.lat(), ref1.lon(), length_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
ref1 = Point3D( lon, lat, 0.0 );
geo_direct_wgs_84 ( alt_m, ref2.lat(), ref2.lon(), length_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
ref2 = Point3D( lon, lat, 0.0 );
// offset 5' upwind
geo_direct_wgs_84 ( alt_m, ref3.lat(), ref3.lon(), length_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
ref3 = Point3D( lon, lat, 0.0 );
geo_direct_wgs_84 ( alt_m, ref4.lat(), ref4.lon(), length_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
ref4 = Point3D( lon, lat, 0.0 );
// five lights each
for ( int i = 0; i < 5; ++i ) {
g_lights.push_back( ref1 );
g_normals.push_back( normal );
g_lights.push_back( ref2 );
g_normals.push_back( normal );
r_lights.push_back( ref3 );
r_normals.push_back( normal );
r_lights.push_back( ref4 );
r_normals.push_back( normal );
// offset 10' towards center
geo_direct_wgs_84 ( alt_m, ref1.lat(), ref1.lon(), left_hdg,
-10 * SG_FEET_TO_METER, &lat, &lon, &r );
ref1 = Point3D( lon, lat, 0.0 );
geo_direct_wgs_84 ( alt_m, ref2.lat(), ref2.lon(), left_hdg,
10 * SG_FEET_TO_METER, &lat, &lon, &r );
ref2 = Point3D( lon, lat, 0.0 );
geo_direct_wgs_84 ( alt_m, ref3.lat(), ref3.lon(), left_hdg,
-10 * SG_FEET_TO_METER, &lat, &lon, &r );
ref3 = Point3D( lon, lat, 0.0 );
geo_direct_wgs_84 ( alt_m, ref4.lat(), ref4.lon(), left_hdg,
10 * SG_FEET_TO_METER, &lat, &lon, &r );
ref4 = Point3D( lon, lat, 0.0 );
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( g_lights, false );
normals_poly.add_contour( g_normals, false );
FGSuperPoly green;
green.set_poly( lights_poly );
green.set_normals( normals_poly );
green.set_material( "RWY_GREEN_LIGHTS" );
if ( kind == "H" ) {
green.set_material( "RWY_GREEN_LIGHTS" );
} else if ( kind == "M" ) {
green.set_material( "RWY_GREEN_MEDIUM_LIGHTS" );
} else if ( kind == "L" ) {
green.set_material( "RWY_GREEN_LOW_LIGHTS" );
}
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( r_lights, false );
normals_poly.add_contour( r_normals, false );
FGSuperPoly red;
red.set_poly( lights_poly );
red.set_normals( normals_poly );
if ( kind == "H" ) {
red.set_material( "RWY_RED_LIGHTS" );
} else if ( kind == "M" ) {
red.set_material( "RWY_RED_MEDIUM_LIGHTS" );
} else if ( kind == "L" ) {
red.set_material( "RWY_RED_LOW_LIGHTS" );
}
superpoly_list result; result.clear();
result.push_back( green );
result.push_back( red );
return result;
}
// generate runway center line lighting, 50' spacing.
static superpoly_list gen_runway_center_line_lights( const FGRunway& rwy_info,
bool recip )
{
point_list w_lights; w_lights.clear();
point_list r_lights; r_lights.clear();
point_list w_normals; w_normals.clear();
point_list r_normals; r_normals.clear();
int i;
double len = rwy_info.length;
// this should be 25' technically but I'm trying 50' to space things out
int divs = (int)(len / (50.0*SG_FEET_TO_METER)) + 1;
Point3D normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 2.0, 2.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D inc;
Point3D pt1, pt2;
if ( recip ) {
pt1 = (corner[0] + corner[1] ) / 2.0;
pt2 = (corner[2] + corner[3] ) / 2.0;
} else {
pt1 = (corner[2] + corner[3] ) / 2.0;
pt2 = (corner[0] + corner[1] ) / 2.0;
}
inc = (pt2 - pt1) / divs;
double dist = len;
double step = len / divs;
pt1 += inc; // move 25' in
dist -= step;
bool use_white = true;
while ( dist > 0.0 ) {
if ( dist > 3000.0 ) {
w_lights.push_back( pt1 );
w_normals.push_back( normal );
} else if ( dist > 1000.0 ) {
if ( use_white ) {
w_lights.push_back( pt1 );
w_normals.push_back( normal );
} else {
r_lights.push_back( pt1 );
r_normals.push_back( normal );
}
use_white = !use_white;
} else {
r_lights.push_back( pt1 );
r_normals.push_back( normal );
}
pt1 += inc;
pt1 += inc;
dist -= step;
dist -= step;
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( w_lights, false );
normals_poly.add_contour( w_normals, false );
FGSuperPoly white;
white.set_poly( lights_poly );
white.set_normals( normals_poly );
white.set_material( "RWY_WHITE_MEDIUM_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( r_lights, false );
normals_poly.add_contour( r_normals, false );
FGSuperPoly red;
red.set_poly( lights_poly );
red.set_normals( normals_poly );
red.set_material( "RWY_RED_MEDIUM_LIGHTS" );
superpoly_list result; result.clear();
result.push_back( white );
result.push_back( red );
return result;
}
// generate touch down zone lights
static FGSuperPoly gen_touchdown_zone_lights( const FGRunway& rwy_info,
float alt_m, bool recip )
{
point_list lights; lights.clear();
point_list normals; normals.clear();
int i;
cout << "gen touchdown zone lights " << rwy_info.rwy_no << endl;
Point3D normal;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
// determine the start point.
Point3D ref;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref = (corner[0] + corner[1]) / 2;
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref = (corner[2] + corner[3]) / 2;
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
normal = gen_runway_light_vector( rwy_info, 3.0, recip );
for ( i = 0; i < 30; ++i ) {
// offset 100' upwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
100 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
Point3D pt1 = ref;
// left side bar
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
36 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
pt1 = ref;
// right side bar
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-36 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( lights, false );
normals_poly.add_contour( normals, false );
FGSuperPoly result;
result.set_poly( lights_poly );
result.set_normals( normals_poly );
result.set_material( "RWY_WHITE_LIGHTS" );
return result;
}
// generate a simple 2 bar VASI for a 3 degree approach
static FGSuperPoly gen_vasi( const FGRunway& rwy_info, float alt_m,
bool recip )
{
point_list lights; lights.clear();
point_list normals; normals.clear();
int i;
cout << "gen vasi " << rwy_info.rwy_no << endl;
Point3D normal;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
// determine the start point.
Point3D ref;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref = corner[0];
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref = corner[2];
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
// offset 600' upwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
600 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
// offset 50' left
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), left_hdg,
50 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
// downwind bar
normal = gen_runway_light_vector( rwy_info, 2.5, recip );
// unit1
Point3D pt1 = ref;
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
// unit2
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
16 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
// unit3
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
16 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
// upwind bar
normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// unit1
pt1 = ref;
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), length_hdg,
700 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
// unit2
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
16 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
// unit3
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
16 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
1 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normals.push_back( normal );
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( lights, false );
normals_poly.add_contour( normals, false );
FGSuperPoly result;
result.set_poly( lights_poly );
result.set_normals( normals_poly );
result.set_material( "RWY_VASI_LIGHTS" );
return result;
}
// generate a simple PAPI for a 3 degree approach
static FGSuperPoly gen_papi( const FGRunway& rwy_info, float alt_m,
bool recip )
{
point_list lights; lights.clear();
point_list normals; normals.clear();
int i;
cout << "gen papi " << rwy_info.rwy_no << endl;
Point3D normal;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
// determine the start point.
Point3D ref;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref = corner[0];
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref = corner[2];
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
// offset 950' upwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
950 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
// offset 50' left
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), left_hdg,
50 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
// unit1
Point3D pt1 = ref;
lights.push_back( pt1 );
normal = gen_runway_light_vector( rwy_info, 3.5, recip );
normals.push_back( normal );
// unit2
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
30 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normal = gen_runway_light_vector( rwy_info, 3.167, recip );
normals.push_back( normal );
// unit3
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
30 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normal = gen_runway_light_vector( rwy_info, 2.833, recip );
normals.push_back( normal );
// unit4
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
30 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
lights.push_back( pt1 );
normal = gen_runway_light_vector( rwy_info, 2.5, recip );
normals.push_back( normal );
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( lights, false );
normals_poly.add_contour( normals, false );
FGSuperPoly result;
result.set_poly( lights_poly );
result.set_normals( normals_poly );
result.set_material( "RWY_VASI_LIGHTS" );
return result;
}
// generate REIL lights
static FGSuperPoly gen_reil( const FGRunway& rwy_info, float alt_m,
bool recip )
{
point_list lights; lights.clear();
point_list normals; normals.clear();
int i;
cout << "gen reil " << rwy_info.rwy_no << endl;
Point3D normal;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 0.0, 0.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
// determine the start point.
Point3D ref1, ref2;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref1 = corner[0];
ref2 = corner[1];
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref1 = corner[2];
ref2 = corner[3];
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
// offset 40' downwind
geo_direct_wgs_84 ( alt_m, ref1.lat(), ref1.lon(), length_hdg,
-40 * SG_FEET_TO_METER, &lat, &lon, &r );
ref1 = Point3D( lon, lat, 0.0 );
// offset 40' left
geo_direct_wgs_84 ( alt_m, ref1.lat(), ref1.lon(), left_hdg,
40 * SG_FEET_TO_METER, &lat, &lon, &r );
ref1 = Point3D( lon, lat, 0.0 );
lights.push_back( ref1 );
normal = gen_runway_light_vector( rwy_info, 10, recip );
normals.push_back( normal );
// offset 40' downwind
geo_direct_wgs_84 ( alt_m, ref2.lat(), ref2.lon(), length_hdg,
-40 * SG_FEET_TO_METER, &lat, &lon, &r );
ref2 = Point3D( lon, lat, 0.0 );
// offset 40' left
geo_direct_wgs_84 ( alt_m, ref2.lat(), ref2.lon(), left_hdg,
-40 * SG_FEET_TO_METER, &lat, &lon, &r );
ref2 = Point3D( lon, lat, 0.0 );
lights.push_back( ref2 );
normal = gen_runway_light_vector( rwy_info, 10, recip );
normals.push_back( normal );
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( lights, false );
normals_poly.add_contour( normals, false );
FGSuperPoly result;
result.set_poly( lights_poly );
result.set_normals( normals_poly );
result.set_material( "RWY_REIL_LIGHTS" );
return result;
}
// generate ALSF-II approach lighting scheme
static superpoly_list gen_alsf( const FGRunway& rwy_info,
float alt_m, int kind, bool recip )
{
point_list g_lights; g_lights.clear();
point_list w_lights; w_lights.clear();
point_list r_lights; r_lights.clear();
point_list s_lights; s_lights.clear();
point_list g_normals; g_normals.clear();
point_list w_normals; w_normals.clear();
point_list r_normals; r_normals.clear();
point_list s_normals; s_normals.clear();
int i, j;
cout << "gen ALSF lights " << rwy_info.rwy_no << endl;
Point3D normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// Generate the threshold lights
double len = rwy_info.length * SG_FEET_TO_METER;
int divs = (int)(len / 10.0) + 1;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 2.0, 2.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D inc1, inc2;
Point3D pt1, pt2;
if ( recip ) {
inc1 = (corner[0] - corner[1]) / divs;
inc2 = (corner[3] - corner[2]) / divs;
pt1 = corner[1];
pt2 = corner[2];
} else {
inc1 = (corner[2] - corner[3]) / divs;
inc2 = (corner[1] - corner[0]) / divs;
pt1 = corner[3];
pt2 = corner[0];
}
double dist = rwy_info.length;
double step = dist / divs;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
for ( i = 0; i < divs; ++i ) {
pt1 += inc1;
pt2 += inc2;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
}
// Generate long center bar of lights
// determine the start point.
Point3D ref_save;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref_save = (corner[0] + corner[1]) / 2;
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref_save = (corner[2] + corner[3]) / 2;
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
Point3D ref = ref_save;
for ( i = 0; i < 30; ++i ) {
// offset 100' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-100 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
w_lights.push_back( pt1 );
w_normals.push_back( normal );
// left 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
pt1 = ref;
// right 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
ref = ref_save;
if ( kind == 1 ) {
// Terminating bar
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 3 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
pt1 = ref;
// right 3 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
} else if ( kind == 2 ) {
// Generate red side row lights
for ( i = 0; i < 9; ++i ) {
// offset 100' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-100 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 3 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
36 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
pt1 = ref;
// right 3 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-36 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
}
}
if ( kind == 1 ) {
// Generate pre-threshold bar
ref = ref_save;
// offset 100' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-100 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
75 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
}
pt1 = ref;
// rioght 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-75 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
r_lights.push_back( pt1 );
r_normals.push_back( normal );
}
} else if ( kind == 2 ) {
// Generate -500 extra horizontal row of lights
ref = ref_save;
// offset 500' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-500 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 4 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
11.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 3; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
pt1 = ref;
// right 4 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-11.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 3; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
}
// Generate -1000' extra horizontal row of lights
ref = ref_save;
// offset 1000' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1000 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 8 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 7; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
pt1 = ref;
// right 8 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 7; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
// generate rabbit lights
ref = ref_save;
// start 1010' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1010 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
for ( i = 0; i < 21; ++i ) {
s_lights.push_back( ref );
s_normals.push_back( normal );
// offset 100' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-100 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( g_lights, false );
normals_poly.add_contour( g_normals, false );
FGSuperPoly green;
green.set_poly( lights_poly );
green.set_normals( normals_poly );
green.set_material( "RWY_GREEN_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( r_lights, false );
normals_poly.add_contour( r_normals, false );
FGSuperPoly red;
red.set_poly( lights_poly );
red.set_normals( normals_poly );
red.set_material( "RWY_RED_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( w_lights, false );
normals_poly.add_contour( w_normals, false );
FGSuperPoly white;
white.set_poly( lights_poly );
white.set_normals( normals_poly );
white.set_material( "RWY_WHITE_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( s_lights, false );
normals_poly.add_contour( s_normals, false );
FGSuperPoly sequenced;
sequenced.set_poly( lights_poly );
sequenced.set_normals( normals_poly );
sequenced.set_material( "RWY_SEQUENCED_LIGHTS" );
superpoly_list result; result.clear();
result.push_back( green );
result.push_back( red );
result.push_back( white );
result.push_back( sequenced );
return result;
}
// generate SSALS, SSALF, and SSALR approach lighting scheme (kind =
// S, F, or R)
static superpoly_list gen_ssalx( const FGRunway& rwy_info,
float alt_m, const string& kind, bool recip )
{
point_list g_lights; g_lights.clear();
point_list w_lights; w_lights.clear();
point_list r_lights; r_lights.clear();
point_list s_lights; s_lights.clear();
point_list g_normals; g_normals.clear();
point_list w_normals; w_normals.clear();
point_list r_normals; r_normals.clear();
point_list s_normals; s_normals.clear();
int i, j;
cout << "gen SSALx lights " << rwy_info.rwy_no << endl;
Point3D normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// Generate the threshold lights
double len = rwy_info.length * SG_FEET_TO_METER;
int divs = (int)(len / 10.0) + 1;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 2.0, 2.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D inc1, inc2;
Point3D pt1, pt2;
if ( recip ) {
inc1 = (corner[0] - corner[1]) / divs;
inc2 = (corner[3] - corner[2]) / divs;
pt1 = corner[1];
pt2 = corner[2];
} else {
inc1 = (corner[2] - corner[3]) / divs;
inc2 = (corner[1] - corner[0]) / divs;
pt1 = corner[3];
pt2 = corner[0];
}
double dist = rwy_info.length;
double step = dist / divs;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
for ( i = 0; i < divs; ++i ) {
pt1 += inc1;
pt2 += inc2;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
}
// Generate long center bar of lights (every 200')
// determine the start point.
Point3D ref_save;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref_save = (corner[0] + corner[1]) / 2;
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref_save = (corner[2] + corner[3]) / 2;
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
Point3D ref = ref_save;
for ( i = 0; i < 7; ++i ) {
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
w_lights.push_back( pt1 );
w_normals.push_back( normal );
// left 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
pt1 = ref;
// right 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-3.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
// Generate -1000' extra horizontal row of lights
ref = ref_save;
// offset 1000' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1000 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
pt1 = ref;
// right 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-15 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
if ( kind == "R" ) {
// generate 8 rabbit lights
ref = ref_save;
// start 1600' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1600 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
for ( i = 0; i < 8; ++i ) {
s_lights.push_back( ref );
s_normals.push_back( normal );
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
}
} else if ( kind == "F" ) {
// generate 3 sequenced lights aligned with last 3 light bars
ref = ref_save;
// start 1010' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1010 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
for ( i = 0; i < 3; ++i ) {
s_lights.push_back( ref );
s_normals.push_back( normal );
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
}
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( g_lights, false );
normals_poly.add_contour( g_normals, false );
FGSuperPoly green;
green.set_poly( lights_poly );
green.set_normals( normals_poly );
green.set_material( "RWY_GREEN_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( r_lights, false );
normals_poly.add_contour( r_normals, false );
FGSuperPoly red;
red.set_poly( lights_poly );
red.set_normals( normals_poly );
red.set_material( "RWY_RED_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( w_lights, false );
normals_poly.add_contour( w_normals, false );
FGSuperPoly white;
white.set_poly( lights_poly );
white.set_normals( normals_poly );
white.set_material( "RWY_WHITE_LIGHTS" );
superpoly_list result; result.clear();
result.push_back( green );
result.push_back( red );
result.push_back( white );
if ( s_lights.size() > 0 ) {
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( s_lights, false );
normals_poly.add_contour( s_normals, false );
FGSuperPoly sequenced;
sequenced.set_poly( lights_poly );
sequenced.set_normals( normals_poly );
sequenced.set_material( "RWY_SEQUENCED_LIGHTS" );
result.push_back( sequenced );
}
return result;
}
// generate MALS, MALSF, and MALSR approach lighting scheme (kind =
// ' ', F, or R)
static superpoly_list gen_malsx( const FGRunway& rwy_info,
float alt_m, const string& kind, bool recip )
{
point_list g_lights; g_lights.clear();
point_list w_lights; w_lights.clear();
point_list r_lights; r_lights.clear();
point_list s_lights; s_lights.clear();
point_list g_normals; g_normals.clear();
point_list w_normals; w_normals.clear();
point_list r_normals; r_normals.clear();
point_list s_normals; s_normals.clear();
int i, j;
cout << "gen SSALx lights " << rwy_info.rwy_no << endl;
Point3D normal = gen_runway_light_vector( rwy_info, 3.0, recip );
// Generate the threshold lights
double len = rwy_info.length * SG_FEET_TO_METER;
int divs = (int)(len / 10.0) + 1;
// using FGPolygon is a bit innefficient, but that's what the
// routine returns.
FGPolygon poly_corners = gen_runway_area_w_expand( rwy_info, 2.0, 2.0 );
point_list corner;
for ( i = 0; i < poly_corners.contour_size( 0 ); ++i ) {
corner.push_back( poly_corners.get_pt( 0, i ) );
}
Point3D inc1, inc2;
Point3D pt1, pt2;
if ( recip ) {
inc1 = (corner[0] - corner[1]) / divs;
inc2 = (corner[3] - corner[2]) / divs;
pt1 = corner[1];
pt2 = corner[2];
} else {
inc1 = (corner[2] - corner[3]) / divs;
inc2 = (corner[1] - corner[0]) / divs;
pt1 = corner[3];
pt2 = corner[0];
}
double dist = rwy_info.length;
double step = dist / divs;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
for ( i = 0; i < divs; ++i ) {
pt1 += inc1;
pt2 += inc2;
g_lights.push_back( pt1 );
g_normals.push_back( normal );
r_lights.push_back( pt2 );
r_normals.push_back( normal );
dist -= step;
}
// Generate long center bar of lights (every 200')
// determine the start point.
Point3D ref_save;
double length_hdg, left_hdg;
double lon, lat, r;
if ( recip ) {
ref_save = (corner[0] + corner[1]) / 2;
length_hdg = rwy_info.heading + 180.0;
if ( length_hdg > 360.0 ) { length_hdg -= 360.0; }
} else {
ref_save = (corner[2] + corner[3]) / 2;
length_hdg = rwy_info.heading;
}
left_hdg = length_hdg - 90.0;
if ( left_hdg < 0 ) { left_hdg += 360.0; }
cout << "length hdg = " << length_hdg
<< " left heading = " << left_hdg << endl;
Point3D ref = ref_save;
for ( i = 0; i < 7; ++i ) {
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
w_lights.push_back( pt1 );
w_normals.push_back( normal );
// left 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
pt1 = ref;
// right 2 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
// Generate -1000' extra horizontal row of lights
ref = ref_save;
// offset 1000' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1000 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
pt1 = ref;
// left 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
23 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
pt1 = ref;
// right 5 side lights
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-23 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
for ( j = 0; j < 4; ++j ) {
geo_direct_wgs_84 ( alt_m, pt1.lat(), pt1.lon(), left_hdg,
-2.5 * SG_FEET_TO_METER, &lat, &lon, &r );
pt1 = Point3D( lon, lat, 0.0 );
w_lights.push_back( pt1 );
w_normals.push_back( normal );
}
if ( kind == "R" ) {
// generate 5 rabbit lights
ref = ref_save;
// start 1600' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1600 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
for ( i = 0; i < 8; ++i ) {
s_lights.push_back( ref );
s_normals.push_back( normal );
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
}
} else if ( kind == "F" ) {
// generate 3 sequenced lights aligned with last 3 light bars
ref = ref_save;
// start 1010' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-1010 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
for ( i = 0; i < 3; ++i ) {
s_lights.push_back( ref );
s_normals.push_back( normal );
// offset 200' downwind
geo_direct_wgs_84 ( alt_m, ref.lat(), ref.lon(), length_hdg,
-200 * SG_FEET_TO_METER, &lat, &lon, &r );
ref = Point3D( lon, lat, 0.0 );
}
}
FGPolygon lights_poly; lights_poly.erase();
FGPolygon normals_poly; normals_poly.erase();
lights_poly.add_contour( g_lights, false );
normals_poly.add_contour( g_normals, false );
FGSuperPoly green;
green.set_poly( lights_poly );
green.set_normals( normals_poly );
green.set_material( "RWY_GREEN_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( r_lights, false );
normals_poly.add_contour( r_normals, false );
FGSuperPoly red;
red.set_poly( lights_poly );
red.set_normals( normals_poly );
red.set_material( "RWY_RED_LIGHTS" );
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( w_lights, false );
normals_poly.add_contour( w_normals, false );
FGSuperPoly white;
white.set_poly( lights_poly );
white.set_normals( normals_poly );
white.set_material( "RWY_WHITE_LIGHTS" );
superpoly_list result; result.clear();
result.push_back( green );
result.push_back( red );
result.push_back( white );
if ( s_lights.size() > 0 ) {
lights_poly.erase();
normals_poly.erase();
lights_poly.add_contour( s_lights, false );
normals_poly.add_contour( s_normals, false );
FGSuperPoly sequenced;
sequenced.set_poly( lights_poly );
sequenced.set_normals( normals_poly );
sequenced.set_material( "RWY_SEQUENCED_LIGHTS" );
result.push_back( sequenced );
}
return result;
}
// top level runway light generator
void gen_runway_lights( const FGRunway& rwy_info, float alt_m,
superpoly_list &lights ) {
cout << "gen runway lights " << rwy_info.rwy_no << " "
<< rwy_info.end1_flags << " " << rwy_info.end2_flags << endl;;
unsigned int i;
// Make edge lighting
string edge_type = rwy_info.surface_flags.substr(3,1);
if ( edge_type != (string)"N" ) {
// forward direction
superpoly_list s;
s = gen_runway_edge_lights( rwy_info, edge_type, false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
// reverse direction
s = gen_runway_edge_lights( rwy_info, edge_type, true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// Centerline lighting
if ( rwy_info.surface_flags.substr(0,1) == "Y" ) {
// forward direction
superpoly_list s;
s = gen_runway_center_line_lights( rwy_info, false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
// reverse direction
s = gen_runway_center_line_lights( rwy_info, true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// Touchdown zone lighting
if ( rwy_info.end1_flags.substr(0,1) == "Y" ) {
FGSuperPoly s = gen_touchdown_zone_lights( rwy_info, alt_m, false );
lights.push_back( s );
}
if ( rwy_info.end2_flags.substr(0,1) == "Y" ) {
FGSuperPoly s = gen_touchdown_zone_lights( rwy_info, alt_m, true );
lights.push_back( s );
}
// REIL lighting
if ( rwy_info.end1_flags.substr(1,1) == "Y" ) {
FGSuperPoly s = gen_reil( rwy_info, alt_m, false );
lights.push_back( s );
}
if ( rwy_info.end2_flags.substr(1,1) == "Y" ) {
FGSuperPoly s = gen_reil( rwy_info, alt_m, true );
lights.push_back( s );
}
// PAPI lighting
if ( rwy_info.end1_flags.substr(2,1) == "P" ) {
FGSuperPoly s = gen_papi( rwy_info, alt_m, false );
lights.push_back( s );
}
if ( rwy_info.end2_flags.substr(2,1) == "P" ) {
FGSuperPoly s = gen_papi( rwy_info, alt_m, true );
lights.push_back( s );
}
// VASI lighting
if ( rwy_info.end1_flags.substr(2,1) == "V" ) {
FGSuperPoly s = gen_vasi( rwy_info, alt_m, false );
lights.push_back( s );
}
if ( rwy_info.end2_flags.substr(2,1) == "V" ) {
FGSuperPoly s = gen_vasi( rwy_info, alt_m, true );
lights.push_back( s );
}
// Approach lighting
// ALSF-I
if ( rwy_info.end1_flags.substr(3,1) == "B" ) {
superpoly_list s = gen_alsf( rwy_info, alt_m, 1, false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "B" ) {
superpoly_list s = gen_alsf( rwy_info, alt_m, 1, true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// ALSF-II
if ( rwy_info.end1_flags.substr(3,1) == "C" ) {
superpoly_list s = gen_alsf( rwy_info, alt_m, 2, false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "C" ) {
superpoly_list s = gen_alsf( rwy_info, alt_m, 2, true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// MALS
if ( rwy_info.end1_flags.substr(3,1) == "G" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "x", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "G" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "x", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// MALSF
if ( rwy_info.end1_flags.substr(3,1) == "H" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "F", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "H" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "F", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// MALSR
if ( rwy_info.end1_flags.substr(3,1) == "J" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "R", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "J" ) {
superpoly_list s = gen_malsx( rwy_info, alt_m, "R", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// SSALS
if ( rwy_info.end1_flags.substr(3,1) == "S" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "S", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "S" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "S", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// SSALF
if ( rwy_info.end1_flags.substr(3,1) == "Q" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "F", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "Q" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "F", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// SSALR
if ( rwy_info.end1_flags.substr(3,1) == "R" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "R", false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(3,1) == "R" ) {
superpoly_list s = gen_ssalx( rwy_info, alt_m, "R", true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
// Many aproach lighting systems define the threshold lighting
// needed, but for those that don't (i.e. REIL) make threshold
// lighting
if ( rwy_info.end1_flags.substr(1,1) == "Y" ) {
// forward direction
superpoly_list s = gen_runway_threshold_lights( rwy_info, edge_type,
alt_m, false );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
if ( rwy_info.end2_flags.substr(1,1) == "Y" ) {
// reverse direction
superpoly_list s = gen_runway_threshold_lights( rwy_info, edge_type,
alt_m, true );
for ( i = 0; i < s.size(); ++i ) {
lights.push_back( s[i] );
}
}
}