diff --git a/src/Cockpit/Makefile.am b/src/Cockpit/Makefile.am index 23d0f5fc6..307f8f2f8 100644 --- a/src/Cockpit/Makefile.am +++ b/src/Cockpit/Makefile.am @@ -7,6 +7,7 @@ libCockpit_a_SOURCES = \ hud_card.cxx hud_dnst.cxx hud_gaug.cxx hud_inst.cxx \ hud_labl.cxx hud_ladr.cxx \ hud_lat.cxx hud_lon.cxx \ + hud_rwy.cxx \ hud_scal.cxx hud_tbi.cxx \ kt_70.cxx kt_70.hxx \ marker_beacon.cxx marker_beacon.hxx \ diff --git a/src/Cockpit/hud.cxx b/src/Cockpit/hud.cxx index 268bb943a..043eeb8ee 100644 --- a/src/Cockpit/hud.cxx +++ b/src/Cockpit/hud.cxx @@ -585,6 +585,29 @@ readTBI(const SGPropertyNode * node) return p; } //end readTBI +static instr_item * +readRunway(const SGPropertyNode * node) { + name = node->getStringValue("name"); + x = node->getIntValue("x"); + y = node->getIntValue("y"); + width = node->getIntValue("width"); + height = node->getIntValue("height"); + scaling = node->getDoubleValue("scale"); + working = node->getBoolValue("working",true); + runway_instr *ri = new runway_instr(x,y,width,height,scaling,working); + double scale = node->getDoubleValue("arrow_scale",1.0); + ri->setDrawArrow((scale>0)?true:false); + ri->setDrawArrowAlways((scale>0)?node->getBoolValue("arrow_always"):false); + ri->setStippleOutline(node->getIntValue("outer_stipple",0xFFFF)); + ri->setStippleCenterline(node->getIntValue("center_stipple",0xFFFF)); + ri->setArrowRotationRadius(node->getDoubleValue("arrow_radius")); + ri->setArrowScale(scale); + ri->setLineScale(node->getDoubleValue("line_scale",1.0)); + ri->setScaleDist(node->getDoubleValue("scale_dist_nm")); + SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name); + return (instr_item *) ri; +} + int readInstrument(const SGPropertyNode * node) { @@ -643,6 +666,18 @@ int readInstrument(const SGPropertyNode * node) }//for - tbis } + + const SGPropertyNode * rwy_group = node->getNode("runways"); + if (rwy_group != 0) { + int nRwy = rwy_group->nChildren(); + for (int j = 0; j < nRwy; j++) { + SG_LOG( SG_COCKPIT, SG_DEBUG, + "************** Reading runway properties" ); + HIptr = readRunway(rwy_group->getChild(j)); + HUD_deque.insert( HUD_deque.begin(), HIptr); + + }//for - runways + } return 0; }//end readinstrument diff --git a/src/Cockpit/hud.hxx b/src/Cockpit/hud.hxx index 56afcbc6e..8c1d3532f 100644 --- a/src/Cockpit/hud.hxx +++ b/src/Cockpit/hud.hxx @@ -59,8 +59,10 @@ #include #include
#include
+#include #include "hud_opts.hxx" +#include SG_USING_STD(deque); SG_USING_STD(vector); @@ -680,6 +682,50 @@ public: typedef lon_label * pLonlabel; +// +// fgRunway_instr This class is responsible for rendering the active runway +// in the hud (if visible). +class runway_instr : public instr_item +{ +private: + void boundPoint(sgdVec3 v, sgdVec3 m); + bool boundOutsidePoints(sgdVec3 v, sgdVec3 m); + bool drawLine(sgdVec3 a1, sgdVec3 a2, sgdVec3 p1, sgdVec3 p2); + void drawArrow(); + FGRunway get_active_runway(); + void get_rwy_points(sgdVec3 *points); + void setLineWidth(void); + + sgdVec3 points3d[6],points2d[6]; + double mm[16],pm[16], arrowScale, arrowRad, lnScale, scaleDist, default_pitch, default_heading; + int view[4]; + FGRunway runway; + FGViewer* cockpit_view; + unsigned short stippleOut,stippleCen; + bool drawIA,drawIAAlways; + RECT location; + POINT center; + +public: + runway_instr( int x, + int y, + int width, + int height, + float scale_data, + bool working = true); + + virtual void draw( void ); // Required method in base class + void setArrowRotationRadius(double radius); + void setArrowScale(double scale); // Scales the runway indication arrow + void setDrawArrow(bool draw); // Draws arrow when runway is not visible in HUD if draw=true + void setDrawArrowAlways(bool draw); //Always draws arrow if draw=true; + void setLineScale(double scale); //Sets the maximum line scale + void setScaleDist(double dist_nm); //Sets the distance where to start scaling the lines + void setStippleOutline(unsigned short stipple); //Sets the stipple pattern of the outline of the runway + void setStippleCenterline(unsigned short stipple); //Sets the stipple patter of the center line of the runway +}; + + // // instr_scale This class is an abstract base class for both moving // scale and moving needle (fixed scale) indicators. It diff --git a/src/Cockpit/hud_rwy.cxx b/src/Cockpit/hud_rwy.cxx new file mode 100644 index 000000000..0b0377491 --- /dev/null +++ b/src/Cockpit/hud_rwy.cxx @@ -0,0 +1,418 @@ +// hud_rwy.cxx -- An instrument that renders a virtual runway on the HUD +// +// Written by Aaron Wilson & Phillip Merritt, Nov 2004. +// +// Copyright (C) 2004 Aaron Wilson, Aaron.I.Wilson@nasa.gov +// Copyright (C) 2004 Phillip Merritt, Phillip.M.Merritt@nasa.gov +// +// 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. +// + +#include "hud.hxx" + +#include +#include
+#include
+#include +#include +#include +#include +#include +#include +#include +#include + +runway_instr::runway_instr(int x, + int y, + int width, + int height, + float scale_data, + bool working):instr_item(x,y,width,height,NULL,scale_data,0,working) +{ + runway = get_active_runway(); + get_rwy_points(points3d); + stippleOut=0xFFFF; + stippleCen=0xFFFF; + arrowScale = 1.0; + arrowRad = 0.0; + view[0] = 0; + view[1] = 0; + view[2] = 640; + view[3] = 480; + center.x = view[2]>>1; + center.y = view[3]>>1; + location.left = center.x-(width>>1)+x; + location.right = center.x+(width>>1)+x; + location.bottom = center.y-(height>>1)+y; + location.top = center.y+(height>>1)+y; + cockpit_view = globals->get_viewmgr()->get_view(0); + default_heading = fgGetDouble("/sim/view[0]/config/pitch-heading-deg",0.0); + default_pitch = fgGetDouble("/sim/view[0]/config/pitch-pitch-deg",0.0); +} + +void runway_instr::draw() { + if (!is_broken()) { + glPushAttrib(GL_LINE_STIPPLE | GL_LINE_STIPPLE_PATTERN | GL_LINE_WIDTH); + float modelView[4][4],projMat[4][4]; + bool anyLines; + //Get the current view + FGViewer* curr_view = globals->get_viewmgr()->get_current_view(); + int curr_view_id = globals->get_viewmgr()->get_current(); + double gpo = curr_view->getGoalPitchOffset_deg(); + double gho = curr_view->getGoalHeadingOffset_deg(); + double po = curr_view->getPitchOffset_deg(); + double ho = curr_view->getHeadingOffset_deg(); + + double yaw = -(cockpit_view->getHeadingOffset_deg()-default_heading)*SG_DEGREES_TO_RADIANS; + double pitch = (cockpit_view->getPitchOffset_deg()-default_pitch)*SG_DEGREES_TO_RADIANS; + //double roll = fgGetDouble("/sim/view[0]/config/roll-offset-deg",0.0) //TODO: adjust for default roll offset + double sPitch = sin(pitch), cPitch = cos(pitch), + sYaw = sin(yaw), cYaw = cos(yaw); + + //Assuming that the "Cockpit View" is always at position zero!!! + if (curr_view_id != 0) { + globals->get_viewmgr()->set_view(0); + globals->get_viewmgr()->copyToCurrent(); + } + //Set the camera to the cockpit view to get the view of the runway from the cockpit + ssgSetCamera((sgVec4 *)cockpit_view->get_VIEW()); + //Get the currently active runway and the 3d points + runway = get_active_runway(); + get_rwy_points(points3d); + //Get the current project matrix + ssgGetProjectionMatrix(projMat); +// const sgVec4 *viewMat = globals->get_current_view()->get_VIEW(); + //Get the current model view matrix (cockpit view) + ssgGetModelviewMatrix(modelView); + //Create a rotation matrix to correct for any offsets (other than default offsets) to the model view matrix + sgMat4 xy; //rotation about the Rxy, negate the sin's on Ry + xy[0][0]=cYaw; xy[1][0]=0.0f; xy[2][0]=-sYaw; xy[3][0]=0.0f; + xy[0][1]=sPitch*-sYaw; xy[1][1]=cPitch; xy[2][1]=-sPitch*cYaw; xy[3][1]=0.0f; + xy[0][2]=cPitch*sYaw; xy[1][2]=sPitch; xy[2][2]=cPitch*cYaw; xy[3][2]=0.0f; + xy[0][3]=0.0f; xy[1][3]=0.0f; xy[2][3]=0.0f; xy[3][3]=1.0f; + //Re-center the model view + sgPostMultMat4(modelView,xy); + //copy float matrices to double + for (int i=0; i<4; i++) { + for (int j=0; j<4; j++) { + int idx = (i*4)+j; + mm[idx] = (double)modelView[i][j]; + pm[idx] = (double)projMat[i][j]; + } + } + //Calculate the 2D points via gluProject + int result = GL_TRUE; + for (int i=0; i<6; i++) { + result = gluProject(points3d[i][0],points3d[i][1],points3d[i][2],mm,pm,view,&points2d[i][0],&points2d[i][1],&points2d[i][2]); + } + //set the line width based on our distance from the runway + setLineWidth(); + //Draw the runway lines on the HUD + glEnable(GL_LINE_STIPPLE); + glLineStipple(1,stippleOut); + anyLines = + drawLine(points3d[0],points3d[1],points2d[0],points2d[1]) | //draw top + drawLine(points3d[2],points3d[1],points2d[2],points2d[1]) | //draw right + drawLine(points3d[2],points3d[3],points2d[2],points2d[3]) | //draw bottom + drawLine(points3d[3],points3d[0],points2d[3],points2d[0]); //draw left + glLineStipple(1,stippleCen); + anyLines |= drawLine(points3d[5],points3d[4],points2d[5],points2d[4]); //draw center + //Check to see if arrow needs drawn + if ((!anyLines && drawIA) || drawIAAlways) { + drawArrow(); //draw indication arrow + } + //Restore the current view and any offsets + if (curr_view_id != 0) { + globals->get_viewmgr()->set_view(curr_view_id); + globals->get_viewmgr()->copyToCurrent(); + curr_view->setHeadingOffset_deg(ho); + curr_view->setPitchOffset_deg(po); + curr_view->setGoalHeadingOffset_deg(gho); + curr_view->setGoalPitchOffset_deg(gpo); + } + //Set the camera back to the current view + ssgSetCamera((sgVec4 *)curr_view); + glPopAttrib(); + }//if not broken +} + +FGRunway runway_instr::get_active_runway() { + FGEnvironment stationweather = + ((FGEnvironmentMgr *)globals->get_subsystem("environment"))->getEnvironment(); + double hdg = stationweather.get_wind_from_heading_deg(); + FGRunway runway; + globals->get_runways()->search( fgGetString("/sim/presets/airport-id"), int(hdg), &runway); + return runway; +} + +void runway_instr::get_rwy_points(sgdVec3 *points3d) { + static Point3D center = globals->get_scenery()->get_center(); + + //Get the current tile center + Point3D currentCenter = globals->get_scenery()->get_center(); + Point3D tileCenter = currentCenter; + if (center != currentCenter) //if changing tiles + tileCenter = center; //use last center + double alt = current_aircraft.fdm_state->get_Runway_altitude()*SG_FEET_TO_METER; + double length = (runway.length/2.0)*SG_FEET_TO_METER; + double width = (runway.width/2.0)*SG_FEET_TO_METER; + double frontLat,frontLon,backLat,backLon,az,tempLat,tempLon; + + geo_direct_wgs_84(alt,runway.lat,runway.lon,runway.heading,length,&backLat,&backLon,&az); + sgGeodToCart(backLat*SG_DEGREES_TO_RADIANS,backLon*SG_DEGREES_TO_RADIANS,alt,points3d[4]); + + geo_direct_wgs_84(alt,runway.lat,runway.lon,runway.heading+180,length,&frontLat,&frontLon,&az); + sgGeodToCart(frontLat*SG_DEGREES_TO_RADIANS,frontLon*SG_DEGREES_TO_RADIANS,alt,points3d[5]); + + geo_direct_wgs_84(alt,backLat,backLon,runway.heading+90,width,&tempLat,&tempLon,&az); + sgGeodToCart(tempLat*SG_DEGREES_TO_RADIANS,tempLon*SG_DEGREES_TO_RADIANS,alt,points3d[0]); + + geo_direct_wgs_84(alt,backLat,backLon,runway.heading-90,width,&tempLat,&tempLon,&az); + sgGeodToCart(tempLat*SG_DEGREES_TO_RADIANS,tempLon*SG_DEGREES_TO_RADIANS,alt,points3d[1]); + + geo_direct_wgs_84(alt,frontLat,frontLon,runway.heading-90,width,&tempLat,&tempLon,&az); + sgGeodToCart(tempLat*SG_DEGREES_TO_RADIANS,tempLon*SG_DEGREES_TO_RADIANS,alt,points3d[2]); + + geo_direct_wgs_84(alt,frontLat,frontLon,runway.heading+90,width,&tempLat,&tempLon,&az); + sgGeodToCart(tempLat*SG_DEGREES_TO_RADIANS,tempLon*SG_DEGREES_TO_RADIANS,alt,points3d[3]); + + for(int i = 0; i < 6; i++) + { + points3d[i][0] -= tileCenter.x(); + points3d[i][1] -= tileCenter.y(); + points3d[i][2] -= tileCenter.z(); + } + center = currentCenter; +} + +bool runway_instr::drawLine(sgdVec3 a1, sgdVec3 a2, sgdVec3 point1, sgdVec3 point2) { + sgdVec3 p1, p2; + sgdCopyVec3(p1, point1); + sgdCopyVec3(p2, point2); + bool p1Inside = (p1[0]>=location.left && p1[0]<=location.right && p1[1]>=location.bottom && p1[1]<=location.top); + bool p1Insight = (p1[2] >= 0.0 && p1[2] < 1.0); + bool p1Valid = p1Insight && p1Inside; + bool p2Inside = (p2[0]>=location.left && p2[0]<=location.right && p2[1]>=location.bottom && p2[1]<=location.top); + bool p2Insight = (p2[2] >= 0.0 && p2[2] < 1.0); + bool p2Valid = p2Insight && p2Inside; + + if (p1Valid && p2Valid) { //Both project points are valid, draw the line + glBegin(GL_LINES); + glVertex2d(p1[0],p1[1]); + glVertex2d(p2[0],p2[1]); + glEnd(); + } + else if (p1Valid) { //p1 is valid and p2 is not, calculate a new valid point + sgdVec3 vec = {a2[0]-a1[0], a2[1]-a1[1], a2[2]-a1[2]}; + //create the unit vector + sgdScaleVec3(vec,1.0/sgdLengthVec3(vec)); + sgdVec3 newPt; + sgdCopyVec3(newPt,a1); + sgdAddVec3(newPt,vec); + if (gluProject(newPt[0],newPt[1],newPt[2],mm,pm,view,&p2[0],&p2[1],&p2[2]) && (p2[2]>0&&p2[2]<1.0) ) { + boundPoint(p1,p2); + glBegin(GL_LINES); + glVertex2d(p1[0],p1[1]); + glVertex2d(p2[0],p2[1]); + glEnd(); + } + } + else if (p2Valid) { //p2 is valid and p1 is not, calculate a new valid point + sgdVec3 vec = {a1[0]-a2[0], a1[1]-a2[1], a1[2]-a2[2]}; + //create the unit vector + sgdScaleVec3(vec,1.0/sgdLengthVec3(vec)); + sgdVec3 newPt; + sgdCopyVec3(newPt,a2); + sgdAddVec3(newPt,vec); + if (gluProject(newPt[0],newPt[1],newPt[2],mm,pm,view,&p1[0],&p1[1],&p1[2]) && (p1[2]>0&&p1[2]<1.0)) { + boundPoint(p2,p1); + glBegin(GL_LINES); + glVertex2d(p2[0],p2[1]); + glVertex2d(p1[0],p1[1]); + glEnd(); + } + } + else if (p1Insight && p2Insight) { //both points are insight, but not inside + bool v = boundOutsidePoints(p1,p2); + if (v) { + glBegin(GL_LINES); + glVertex2d(p1[0],p1[1]); + glVertex2d(p2[0],p2[1]); + glEnd(); + } + return v; + } + //else both points are not insight, don't draw anything + return (p1Valid && p2Valid); +} + +void runway_instr::boundPoint(sgdVec3 v, sgdVec3 m) { + double y = v[1]; + if(m[1] < v[1]) { + y = location.bottom; + } + else if(m[1] > v[1]) { + y = location.top; + } + if (m[0] == v[0]) { + m[1]=y; + return; //prevent divide by zero + } + double slope = (m[1]-v[1])/(m[0]-v[0]); + m[0] = (y-v[1])/slope + v[0]; + m[1] = y; + if (m[0] < location.left) { + m[0] = location.left; + m[1] = slope * (location.left-v[0])+v[1]; + } + else if (m[0] > location.right) { + m[0] = location.right; + m[1] = slope * (location.right-v[0])+v[1]; + } +} + +bool runway_instr::boundOutsidePoints(sgdVec3 v, sgdVec3 m) { + bool pointsInvalid = (v[1]>location.top && m[1]>location.top) || + (v[1]location.right && m[0]>location.right) || + (v[0]v[1]) { + m[1]=location.top; + v[1]=location.bottom; + } + else { + v[1]=location.top; + m[1]=location.bottom; + } + return true; + } + if (m[1] == v[1]) { //y's are equal, horizontal line + if (m[0] > v[0]) { + m[0] = location.right; + v[0] = location.left; + } + else { + v[0] = location.right; + m[0] = location.left; + } + return true; + } + double slope = (m[1]-v[1])/(m[0]-v[0]); + double b = v[1]-(slope*v[0]); + double y1 = slope * location.left + b; + double y2 = slope * location.right + b; + double x1 = (location.bottom - b) / slope; + double x2 = (location.top - b) / slope; + int counter = 0; + if (y1 >= location.bottom && y1 <= location.top) { + v[0] = location.left; + v[1] = y1; + counter++; + } + if (y2 >= location.bottom && y2 <= location.top) { + if (counter > 0) { + m[0] = location.right; + m[1] = y2; + } + else { + v[0] = location.right; + v[1] = y2; + } + counter++; + } + if (x1 >= location.left && x1 <= location.right) { + if (counter > 0) { + m[0] = x1; + m[1] = location.bottom; + } + else { + v[0] = x1; + v[1] = location.bottom; + } + counter++; + } + if (x2 >= location.left && x2 <= location.right) { + m[0] = x1; + m[1] = location.bottom; + counter++; + } + return (counter == 2); +} + +void runway_instr::drawArrow() { + Point3D ac,rwy; + ac.setlat(current_aircraft.fdm_state->get_Latitude_deg()); + ac.setlon(current_aircraft.fdm_state->get_Longitude_deg()); + rwy.setlat(runway.lat); + rwy.setlon(runway.lon); + float theta = GetHeadingFromTo(ac,rwy); + theta -= fgGetDouble("/orientation/heading-deg"); + theta = -theta; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslated((location.right+location.left)/2.0,(location.top+location.bottom)/2.0,0.0); + glRotated(theta,0.0,0.0,1.0); + glTranslated(0.0,arrowRad,0.0); + glScaled(arrowScale,arrowScale,0.0); + glBegin(GL_TRIANGLES); + glVertex2d(-5.0,12.5); + glVertex2d(0.0,25.0); + glVertex2d(5.0,12.5); + glEnd(); + glBegin(GL_QUADS); + glVertex2d(-2.5,0.0); + glVertex2d(-2.5,12.5); + glVertex2d(2.5,12.5); + glVertex2d(2.5,0.0); + glEnd(); + glPopMatrix(); +} + +void runway_instr::setLineWidth() { + //Calculate the distance from the runway, A + double course, distance; + calc_gc_course_dist(Point3D(runway.lon*SGD_DEGREES_TO_RADIANS, runway.lat*SGD_DEGREES_TO_RADIANS, 0.0), + Point3D(current_aircraft.fdm_state->get_Longitude(),current_aircraft.fdm_state->get_Latitude(), 0.0 ), + &course, &distance); + distance *= SG_METER_TO_NM; + //Get altitude above runway, B + double alt_nm = get_agl(); + static const SGPropertyNode *startup_units_node + = fgGetNode("/sim/startup/units"); + if (!strcmp(startup_units_node->getStringValue(), "feet")) + alt_nm *= SG_FEET_TO_METER*SG_METER_TO_NM; + else + alt_nm *= SG_METER_TO_NM; + //Calculate distance away from runway, C = v(AČ+BČ) + distance = sqrt(alt_nm*alt_nm + distance*distance); + if (distance < scaleDist) + glLineWidth( 1.0+( (lnScale-1)*( (scaleDist-distance)/scaleDist))); + else + glLineWidth( 1.0 ); + +} + +void runway_instr::setArrowRotationRadius(double radius) { arrowRad = radius; } +void runway_instr::setArrowScale(double scale) { arrowScale = scale; } +void runway_instr::setDrawArrow(bool draw) {drawIA = draw;} +void runway_instr::setDrawArrowAlways(bool draw) {drawIAAlways = draw;} +void runway_instr::setLineScale(double scale) {lnScale = scale;} +void runway_instr::setScaleDist(double dist_m) {scaleDist = dist_m;} +void runway_instr::setStippleOutline(unsigned short stipple) {stippleOut = stipple;} +void runway_instr::setStippleCenterline(unsigned short stipple){stippleCen = stipple;} +