X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FAirports%2Fgroundnetwork.cxx;h=a6ad1f5c44a8c0f4653d3314814f1cb7e643581e;hb=076f61f2b7e1f774bc827f34ab4a24dcc1998571;hp=3c65c8e48bee0d391d2569a0603c8e307ed89883;hpb=c79e2465dfb6d7387d92ff614101f12ea9a91e92;p=flightgear.git diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 3c65c8e48..a6ad1f5c4 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -24,9 +24,10 @@ # include #endif -#include +#include #include #include +#include #include #include @@ -40,14 +41,16 @@ #include #include #include +#include -#include +#include #include #include #include #include #include +#include #include @@ -56,52 +59,50 @@ #include "groundnetwork.hxx" using std::string; +using flightgear::NavDataCache; /*************************************************************************** * FGTaxiSegment **************************************************************************/ -FGTaxiSegment::FGTaxiSegment(int aStart, int aEnd, bool isPushBack) : +FGTaxiSegment::FGTaxiSegment(PositionedID aStart, PositionedID aEnd) : startNode(aStart), endNode(aEnd), - length(0), - heading(0), isActive(0), - isPushBackRoute(isPushBack), - start(0), - end(0), index(0), oppositeDirection(0) { }; -bool FGTaxiSegment::bindToNodes(const IndexTaxiNodeMap& nodes) +SGGeod FGTaxiSegment::getCenter() const { - IndexTaxiNodeMap::const_iterator it = nodes.find(startNode); - if (it == nodes.end()) { - return false; - } - - start = it->second; - - it = nodes.find(endNode); - if (it == nodes.end()) { - return false; - } - - end = it->second; - - start->addSegment(this); - double az2; + FGTaxiNode* start(getStart()), *end(getEnd()); + double heading, length, az2; SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length); - return true; + return SGGeodesy::direct(start->geod(), heading, length * 0.5); } -SGGeod FGTaxiSegment::getCenter() const +FGTaxiNodeRef FGTaxiSegment::getEnd() const { - return SGGeodesy::direct(start->geod(), heading, length * 0.5); + return FGPositioned::loadById(endNode); +} + +FGTaxiNodeRef FGTaxiSegment::getStart() const +{ + return FGPositioned::loadById(startNode); +} + +double FGTaxiSegment::getLength() const +{ + return dist(getStart()->cart(), getEnd()->cart()); +} + +double FGTaxiSegment::getHeading() const +{ + return SGGeodesy::courseDeg(getStart()->geod(), getEnd()->geod()); } + void FGTaxiSegment::block(int id, time_t blockTime, time_t now) { BlockListIterator i = blockTimes.begin(); @@ -144,81 +145,26 @@ void FGTaxiSegment::unblock(time_t now) /*************************************************************************** * FGTaxiRoute **************************************************************************/ -bool FGTaxiRoute::next(int *nde) -{ - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; - //if (nodes.size() != (routes.size()) +1) - // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl; - - if (currNode == nodes.end()) - return false; - *nde = *(currNode); - if (currNode != nodes.begin()) // make sure route corresponds to the end node - currRoute++; - currNode++; - return true; -}; - -bool FGTaxiRoute::next(int *nde, int *rte) +bool FGTaxiRoute::next(PositionedID *nde, int *rte) { - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; if (nodes.size() != (routes.size()) + 1) { - SG_LOG(SG_GENERAL, SG_ALERT, - "ALERT: Misconfigured TaxiRoute : " << nodes. - size() << " " << routes.size()); - throw sg_range_exception("misconfigured taxi route"); + SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size()); + throw sg_range_exception("Misconfigured taxi route"); } if (currNode == nodes.end()) return false; *nde = *(currNode); - //*rte = *(currRoute); - if (currNode != nodes.begin()) // Make sure route corresponds to the end node - { + if (currNode != nodes.begin()) { *rte = *(currRoute); currRoute++; } else { - // If currNode points to the first node, this means the aircraft is not on the taxi node - // yet. Make sure to return a unique identifyer in this situation though, because otherwise - // the speed adjust AI code may be unable to resolve whether two aircraft are on the same - // taxi route or not. the negative of the preceding route seems a logical choice, as it is - // unique for any starting location. - // Note that this is probably just a temporary fix until I get Parking / tower control working. + // Handle special case for the first node. *rte = -1 * *(currRoute); } currNode++; return true; }; - -void FGTaxiRoute::rewind(int route) -{ - int currPoint; - int currRoute; - first(); - do { - if (!(next(&currPoint, &currRoute))) { - SG_LOG(SG_GENERAL, SG_ALERT, - "Error in rewinding TaxiRoute: current" << currRoute << - " goal " << route); - } - } while (currRoute != route); -} - - - - /*************************************************************************** * FGGroundNetwork() **************************************************************************/ @@ -232,7 +178,6 @@ FGGroundNetwork::FGGroundNetwork() : parent(NULL) { hasNetwork = false; - foundRoute = false; totalDistance = 0; maxDistance = 0; //maxDepth = 1000; @@ -253,52 +198,16 @@ FGGroundNetwork::~FGGroundNetwork() // When I fix FGPositioned lifetimes (unloading-at-runtime support), this // will need to be re-visited so it can run safely during shutdown. #if 0 - //cerr << "Running Groundnetwork Destructor " << endl; - bool saveData = false; - ofstream cachefile; - if (fgGetBool("/sim/ai/groundnet-cache")) { - SGPath cacheData(globals->get_fg_home()); - cacheData.append("ai"); - string airport = parent->getId(); - - if ((airport) != "") { - char buffer[128]; - ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); - cacheData.append(buffer); - if (!cacheData.exists()) { - cacheData.create_dir(0777); - } - cacheData.append(airport + "-groundnet-cache.txt"); - cachefile.open(cacheData.str().c_str()); - saveData = true; - } - } - cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl; - for (FGTaxiNodeVectorIterator node = nodes.begin(); - node != nodes.end(); node++) { - if (saveData) { - cachefile << (*node)->getIndex () << " " - << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " " - << endl; - } - delete(*node); - } - nodes.clear(); - pushBackNodes.clear(); - for (FGTaxiSegmentVectorIterator seg = segments.begin(); - seg != segments.end(); seg++) { - delete(*seg); - } - segments.clear(); - if (saveData) { - cachefile.close(); - } + saveElevationCache(); #endif + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + delete seg; + } } -void FGGroundNetwork::saveElevationCache() { - //cerr << "Running Groundnetwork Destructor " << endl; +void FGGroundNetwork::saveElevationCache() +{ +#if 0 bool saveData = false; ofstream cachefile; if (fgGetBool("/sim/ai/groundnet-cache")) { @@ -312,7 +221,7 @@ void FGGroundNetwork::saveElevationCache() { airport[0], airport[1], airport[2]); cacheData.append(buffer); if (!cacheData.exists()) { - cacheData.create_dir(0777); + cacheData.create_dir(0755); } cacheData.append(airport + "-groundnet-cache.txt"); cachefile.open(cacheData.str().c_str()); @@ -331,59 +240,38 @@ void FGGroundNetwork::saveElevationCache() { if (saveData) { cachefile.close(); } +#endif } -void FGGroundNetwork::addSegment(FGTaxiSegment* seg) -{ - segments.push_back(seg); -} - -void FGGroundNetwork::addNode(FGTaxiNode* node) -{ - assert(node); - IndexTaxiNodeMap::iterator it = nodes.find(node->getIndex()); - if (it != nodes.end()) { - throw sg_range_exception(); - } - - nodes.insert(it, std::make_pair(node->getIndex(), node)); -} - -void FGGroundNetwork::addNodes(FGParkingVec * parkings) -{ - BOOST_FOREACH(FGParking* parking, *parkings) { - addNode(parking); - } -} - -void FGGroundNetwork::init() +void FGGroundNetwork::init(FGAirport* pr) { if (networkInitialized) { FGATCController::init(); //cerr << "FGground network already initialized" << endl; return; } + + parent = pr; + assert(parent); hasNetwork = true; nextSave = 0; int index = 1; - // bind segments to nodes + loadSegments(); + + // establish pairing of segments BOOST_FOREACH(FGTaxiSegment* segment, segments) { - if (!segment->bindToNodes(nodes)) { - SG_LOG(SG_GENERAL, SG_ALERT, "unable to bind taxiway segment"); - } - segment->setIndex(index++); - if (segment->isPushBack()) { - pushBackNodes.push_back(segment->getEnd()); + + if (segment->oppositeDirection) { + continue; // already established } - } - - // establish pairing of segments - BOOST_FOREACH(FGTaxiSegment* segment, segments) { - FGTaxiSegment* opp = segment->getEnd()->getArcTo(segment->getStart()); + + FGTaxiSegment* opp = findSegment(segment->endNode, segment->startNode); if (opp) { - segment->setOpposite(opp); + assert(opp->oppositeDirection == NULL); + segment->oppositeDirection = opp; + opp->oppositeDirection = segment; } } @@ -394,6 +282,18 @@ void FGGroundNetwork::init() networkInitialized = true; } +void FGGroundNetwork::loadSegments() +{ + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); +// iterate over all ground-net nodes in this airport + BOOST_FOREACH(PositionedID node, cache->groundNetNodes(parent->guid(), false)) { + // find all segments leaving the node + BOOST_FOREACH(PositionedID end, cache->groundNetEdgesFrom(node, false)) { + segments.push_back(new FGTaxiSegment(node, end)); + } + } +} + void FGGroundNetwork::parseCache() { SGPath cacheData(globals->get_fg_home()); @@ -403,13 +303,13 @@ void FGGroundNetwork::parseCache() if (airport.empty()) { return; } - +#if 0 char buffer[128]; ::snprintf(buffer, 128, "%c/%c/%c/", airport[0], airport[1], airport[2]); cacheData.append(buffer); if (!cacheData.exists()) { - cacheData.create_dir(0777); + cacheData.create_dir(0755); } int index; double elev; @@ -437,76 +337,28 @@ void FGGroundNetwork::parseCache() } } } +#endif } -int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) +int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) const { - double minDist = HUGE_VAL; - int index = -1; - - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - double d = SGGeodesy::distanceM(aGeod, i->second->geod()); - if (d < minDist) { - minDist = d; - index = i->first; - } - } - - return index; + const bool onRunway = false; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway); } -int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) +int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) const { - double minDist = HUGE_VAL; - int index = -1; - - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - if (!i->second->getIsOnRunway()) { - continue; - } - // check point lies on the runway - i.e that course from aGeod to the - // runway end, matches the runway heading - if (aRunway) { - double course = SGGeodesy::courseDeg(i->second->geod(), aRunway->end()); - double headingDiff = course - aRunway->headingDeg(); - SG_NORMALIZE_RANGE(headingDiff, -180.0, 180.0); - if (fabs(headingDiff) > 3.0) { // 3 degrees tolerance - continue; - } - } - - double d = SGGeodesy::distanceM(aGeod, i->second->geod()); - if (d < minDist) { - minDist = d; - index = i->first; - } - } - - return index; + const bool onRunway = true; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway, aRunway); } -FGTaxiNode* FGGroundNetwork::findNode(unsigned int idx) -{ - IndexTaxiNodeMap::iterator i = nodes.find(idx); - if (i == nodes.end()) { - return NULL; - } - - return i->second; +FGTaxiNodeRef FGGroundNetwork::findNode(PositionedID idx) const +{ + return FGPositioned::loadById(idx); } -FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) -{ /* - for (FGTaxiSegmentVectorIterator - itr = segments.begin(); - itr != segments.end(); itr++) - { - if (itr->getIndex() == idx) - return itr->getAddress(); - } - */ +FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) const +{ if ((idx > 0) && (idx <= segments.size())) return segments[idx - 1]; else { @@ -515,34 +367,57 @@ FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) } } +FGTaxiSegment* FGGroundNetwork::findSegment(PositionedID from, PositionedID to) const +{ + if (from == 0) { + return NULL; + } + + // completely boring linear search of segments. Can be improved if/when + // this ever becomes a hot-spot + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + if (seg->startNode != from) { + continue; + } + + if ((to == 0) || (seg->endNode == to)) { + return seg; + } + } + + return NULL; // not found +} + +static int edgePenalty(FGTaxiNode* tn) +{ + return (tn->type() == FGPositioned::PARKING ? 10000 : 0) + + (tn->getIsOnRunway() ? 1000 : 0); +} + +class ShortestPathData +{ +public: + ShortestPathData() : + score(HUGE_VAL) + {} + + double score; + FGTaxiNodeRef previousNode; +}; -FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, +FGTaxiRoute FGGroundNetwork::findShortestRoute(PositionedID start, PositionedID end, bool fullSearch) { //implements Dijkstra's algorithm to find shortest distance route from start to end //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm - - //double INFINITE = 100000000000.0; - // initialize scoring values - int nParkings = parent->getDynamics()->getNrOfParkings(); FGTaxiNodeVector unvisited; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + std::map searchData; - if (fullSearch) { - // create vector from map values - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - unvisited.push_back(i->second); - } - } else { - unvisited = pushBackNodes; + BOOST_FOREACH(PositionedID n, cache->groundNetNodes(parent->guid(), !fullSearch)) { + unvisited.push_back(findNode(n)); } - BOOST_FOREACH(FGTaxiNode* node, unvisited) { - node->setPathScore(HUGE_VAL); //infinity by all practical means - node->setPreviousNode(0); // - node->setPreviousSeg(0); // - } - FGTaxiNode *firstNode = findNode(start); if (!firstNode) { @@ -551,7 +426,7 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, << " at " << ((parent) ? parent->getId() : "")); return FGTaxiRoute(); } - firstNode->setPathScore(0); + searchData[firstNode].score = 0.0; FGTaxiNode *lastNode = findNode(end); if (!lastNode) @@ -565,11 +440,11 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, while (!unvisited.empty()) { FGTaxiNode *best = unvisited.front(); BOOST_FOREACH(FGTaxiNode* i, unvisited) { - if (i->getPathScore() < best->getPathScore()) { + if (searchData[i].score < searchData[best].score) { best = i; } } - + // remove 'best' from the unvisited set FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best); @@ -579,59 +454,45 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, break; } - BOOST_FOREACH(FGTaxiSegment* seg, best->arcs()) { - if (!fullSearch && !seg->isPushBack()) { - continue; // inelligible! - } - - FGTaxiNode *tgt = seg->getEnd(); - double alt = best->getPathScore() + seg->getLength() + - seg->getPenalty(nParkings); - if (alt < tgt->getPathScore()) { // Relax (u,v) - tgt->setPathScore(alt); - tgt->setPreviousNode(best); - tgt->setPreviousSeg(seg); + BOOST_FOREACH(PositionedID targetId, cache->groundNetEdgesFrom(best->guid(), !fullSearch)) { + FGTaxiNodeRef tgt = FGPositioned::loadById(targetId); + double edgeLength = dist(best->cart(), tgt->cart()); + double alt = searchData[best].score + edgeLength + edgePenalty(tgt); + if (alt < searchData[tgt].score) { // Relax (u,v) + searchData[tgt].score = alt; + searchData[tgt].previousNode = best; } } // of outgoing arcs/segments from current best node iteration } // of unvisited nodes remaining - if (lastNode->getPathScore() == HUGE_VAL) { + if (searchData[lastNode].score == HUGE_VAL) { // no valid route found if (fullSearch) { SG_LOG(SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " << parent->getId()); } - FGTaxiRoute empty; - return empty; - //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's - } else { - // assemble route from backtrace information - intVec nodes, routes; - FGTaxiNode *bt = lastNode; - while (bt->getPreviousNode() != 0) { - nodes.push_back(bt->getIndex()); - routes.push_back(bt->getPreviousSegment()->getIndex()); - bt = bt->getPreviousNode(); - } - nodes.push_back(start); - reverse(nodes.begin(), nodes.end()); - reverse(routes.begin(), routes.end()); - - return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0); - } -} - -int FGTaxiSegment::getPenalty(int nGates) -{ - int penalty = 0; - if (end->getIndex() < nGates) { - penalty += 10000; + + return FGTaxiRoute(); } - if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active. - penalty += 1000; + + // assemble route from backtrace information + PositionedIDVec nodes; + intVec routes; + FGTaxiNode *bt = lastNode; + + while (searchData[bt].previousNode != 0) { + nodes.push_back(bt->guid()); + FGTaxiSegment *segment = findSegment(searchData[bt].previousNode->guid(), bt->guid()); + int idx = segment->getIndex(); + routes.push_back(idx); + bt = searchData[bt].previousNode; + } - return penalty; + nodes.push_back(start); + reverse(nodes.begin(), nodes.end()); + reverse(routes.begin(), routes.end()); + return FGTaxiRoute(nodes, routes, searchData[lastNode].score, 0); } /* ATC Related Functions */ @@ -644,7 +505,10 @@ void FGGroundNetwork::announcePosition(int id, double radius, int leg, FGAIAircraft * aircraft) { - init(); + + assert(parent); + init(parent); + TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id alread has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route @@ -720,7 +584,7 @@ bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, Traffic if ((state >= minState) && (state <= maxState) && available) { if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl; - static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); int n = trans_num->getIntValue(); if (n == 0) { trans_num->setIntValue(-1); @@ -1294,7 +1158,6 @@ static void WorldCoordinate(osg::Matrix& obj_pos, double lat, void FGGroundNetwork::render(bool visible) { - SGMaterialLib *matlib = globals->get_matlib(); if (group) { //int nr = ; @@ -1317,7 +1180,8 @@ void FGGroundNetwork::render(bool visible) //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) { //double dx = 0; for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) { - // Handle start point + // Handle start point i.e. the segment that is connected to the aircraft itself on the starting end + // and to the the first "real" taxi segment on the other end. int pos = i->getCurrentPosition() - 1; if (pos >= 0) { @@ -1332,7 +1196,7 @@ void FGGroundNetwork::render(bool visible) double coveredDistance = length * 0.5; SGGeod center; SGGeodesy::direct(start, heading, coveredDistance, center, az2); - //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl; + //std::cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << std::endl; /////////////////////////////////////////////////////////////////////////////// // Make a helper function out of this osg::Matrix obj_pos; @@ -1345,7 +1209,7 @@ void FGGroundNetwork::render(bool visible) } else { elevationStart = ((i)->getAircraft()->_getAltitude()); } - double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationEnd = segments[pos]->getEnd()->getElevationM(); //cerr << "Using elevation " << elevationEnd << endl; if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) { @@ -1384,9 +1248,9 @@ void FGGroundNetwork::render(bool visible) //osg::Node *custom_obj; SGMaterial *mat; if (segments[pos]->hasBlock(now)) { - mat = matlib->find("UnidirectionalTaperRed"); + mat = matlib->find("UnidirectionalTaperRed", center); } else { - mat = matlib->find("UnidirectionalTaperGreen"); + mat = matlib->find("UnidirectionalTaperGreen", center); } if (mat) geode->setEffect(mat->get_effect()); @@ -1396,8 +1260,9 @@ void FGGroundNetwork::render(bool visible) group->addChild( obj_trans ); ///////////////////////////////////////////////////////////////////// } else { - //cerr << "BIG FAT WARNING: current position is here : " << pos << endl; + //std::cerr << "BIG FAT WARNING: current position is here : " << pos << std::endl; } + // Next: Draw the other taxi segments. for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) { osg::Matrix obj_pos; int k = (*j)-1; @@ -1406,8 +1271,8 @@ void FGGroundNetwork::render(bool visible) obj_trans->setDataVariance(osg::Object::STATIC); // Experimental: Calculate slope here, based on length, and the individual elevations - double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); - double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationStart = segments[k]->getStart()->getElevationM(); + double elevationEnd = segments[k]->getEnd ()->getElevationM(); if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { SGGeod center2 = segments[k]->getStart()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); @@ -1458,9 +1323,9 @@ void FGGroundNetwork::render(bool visible) //osg::Node *custom_obj; SGMaterial *mat; if (segments[k]->hasBlock(now)) { - mat = matlib->find("UnidirectionalTaperRed"); + mat = matlib->find("UnidirectionalTaperRed", segCenter); } else { - mat = matlib->find("UnidirectionalTaperGreen"); + mat = matlib->find("UnidirectionalTaperGreen", segCenter); } if (mat) geode->setEffect(mat->get_effect());