/*=========================================================================== Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved. Component: Intersection Description: Authors: Travis Brown-John Revisions Date Author Revision 2001/02/02 Tbrown-John Created ===========================================================================*/ #include #include #include #include #include #include #include // Math includes. // #ifdef TOOLS #include #else #include #endif Intersection::Intersection( void ) : mBigIntersection( 0 ), mIndex( -1 ), mpTrafficControl( NULL ), mnRoadsIn( 0 ), mnRoadsOut( 0 ), mfRotation( 0.0f ), mfRadius( 15.0f ) { mpAnimEntityList.Allocate(2); int i; for ( i = 0; i < MAX_ROADS; i++ ) { mRoadListIn[ i ] = 0; mRoadListOut[ i ] = 0; } mLightControl.SetIntersection( this ); mNWayControl.SetIntersection( this ); } Intersection::Intersection( TriggerLocator* pLocator ) : mBigIntersection( 0 ), mIndex( -1 ), mpTrafficControl( NULL ), mnRoadsIn( 0 ), mnRoadsOut( 0 ), mfRotation( 0.0f ), mfRadius( 15.0f ) { mpAnimEntityList.Allocate(2); int i; for ( i = 0; i < MAX_ROADS; i++ ) { mRoadListIn[ i ] = 0; mRoadListOut[ i ] = 0; } mLightControl.SetIntersection( this ); mNWayControl.SetIntersection( this ); } Intersection::~Intersection( void ) { mWaitingRoads.Clear(); mShortestRoadsToAdjacentIntersectionsWithMultiplier.Clear(); mShortestRoadsToAdjacentIntersectionsNoMultiplier.Clear(); mOutgoingShortcuts.Clear(); if( mBigIntersection ) { mBigIntersection->routesWithMultiplier.Clear(); mBigIntersection->routesNoMultiplier.Clear(); delete mBigIntersection; } } // Simulate the intersection when there are cars nearby. /* ============================================================================== Intersection::Update ============================================================================== Description: Comment Parameters: ( unsigned int dt ) Return: void ============================================================================= */ void Intersection::Update( unsigned int dt ) { if( mpTrafficControl != NULL ) { mpTrafficControl->Update( dt ); } } // advance the traffic in roads 'RoadMask' according to light state. /* ============================================================================== Intersection::AdvanceTraffic ============================================================================== Description: Comment Parameters: ( unsigned int RoadMask, unsigned int state ) Return: void ============================================================================= */ void Intersection::AdvanceTraffic( unsigned int RoadMask, unsigned int state ) const { int i; for ( i = 0; i < MAX_ROADS; i++ ) { unsigned int mask = 1 << i; if ( RoadMask & mask ) { if ( mRoadListIn[ i ] ) { unsigned int j; for ( j = 0; j < mRoadListIn[i]->GetNumLanes(); j++ ) { Lane* pLane = mRoadListIn[i]->GetLane( j ); if ( pLane ) { pLane->NotifyWaitingTraffic( state ); } } } } } } void Intersection::AdvanceNextWaitingRoad() { if( mWaitingRoads.mUseSize > 0 ) { Road* road = mWaitingRoads[0]; for( unsigned int i=0; iGetNumLanes(); i++ ) { road->GetLane(i)->NotifyWaitingTraffic( TrafficControl::GREEN ); } mWaitingRoads.RemoveKeepOrder( 0 ); } } // Is the intersection clear of all cars. /* ============================================================================== Intersection::IsIntersectionClear ============================================================================== Description: Comment Parameters: ( void ) Return: bool ============================================================================= */ bool Intersection::IsIntersectionClear( void ) const { // TODO: Implement this properly. return true; } // add a road to the in list. /* ============================================================================== Intersection::AddRoadIn ============================================================================== Description: Comment Parameters: ( Road *pRoad ) Return: void ============================================================================= */ void Intersection::AddRoadIn( Road *pRoad ) { mRoadListIn[ mnRoadsIn ] = pRoad; mnRoadsIn++; } // add a road to the out list. /* ============================================================================== Intersection::AddRoadOut ============================================================================== Description: Comment Parameters: ( Road *pRoad ) Return: void ============================================================================= */ void Intersection::AddRoadOut( Road *pRoad ) { mRoadListOut[ mnRoadsOut ] = pRoad; mnRoadsOut++; } // Find the road pointer in the road list. /* ============================================================================== Intersection::FindRoadIn ============================================================================== Description: Comment Parameters: ( const Road* pRoad ) Return: int ============================================================================= */ int Intersection::FindRoadIn( const Road* pRoad ) const { unsigned int i; for ( i = 0; i < mnRoadsIn; i++ ) { if ( pRoad == mRoadListIn[ i ] ) { // found it. return i; } } // not found return -1; } // Find the road pointer in the road list. /* ============================================================================== Intersection::FindRoadOut ============================================================================== Description: Comment Parameters: ( const Road* pRoad ) Return: int ============================================================================= */ int Intersection::FindRoadOut( const Road* pRoad ) const { unsigned int i; for ( i = 0; i < mnRoadsOut; i++ ) { if ( pRoad == mRoadListOut[ i ] ) { // found it. return i; } } // not found return -1; } /* ============================================================================== Intersection::GetLocation ============================================================================== Description: Comment Parameters: ( void ) Return: const ============================================================================= */ void Intersection::GetLocation( rmt::Vector& location ) const { location = mLocation; } void Intersection::PopulateShortestRoads( SwapArray& roadsToAdjacentInts, bool useMultiplier ) { //////////// // First search through the IN roads, tallying up the shortest, unique IN roads // that get us to distinct adjacent intersections for( int i = 0; i< static_cast(mnRoadsIn); i++ ) { Road* road = mRoadListIn[i]; const Intersection* in = road->GetSourceIntersection(); // if the road loops back onto same intersection, forget it if( in == this ) { continue; } // if this is a shortcut road, forget it if( road->GetShortCut() ) { continue; } RoadManager::ShortestRoad sr; sr.isOutRoad = false; sr.road = road; sr.cost = road->GetRoadLength(); sr.cost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f; // see if we already have this intersection listed // If not, add it. // If found, check if this road is shorter than road listed RoadManager::ShortestRoad* testRoad = NULL; const Intersection* testInt = NULL; bool found = false; for( int j=0; jroad->GetRoadLength(); if( testRoad->isOutRoad ) { testInt = testRoad->road->GetDestinationIntersection(); } else { testInt = testRoad->road->GetSourceIntersection(); testCost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f; } if( testInt == in ) { // ok, found "in" in our list... // if the length of road is shorter, store this road // instead of testRoad found = true; if( sr.cost < testCost ) { roadsToAdjacentInts.Remove( j ); roadsToAdjacentInts.Add( sr ); } // we need to break here since we modified the usesize. break; } } // if we never found it in any of our lists, add it to the list of IN roads if( !found ) { roadsToAdjacentInts.Add( sr ); } } ///////////////////// // Now do the same for the OUT roads for( int i = 0; i(mnRoadsOut); i++ ) { Road* road = mRoadListOut[i]; const Intersection* in = road->GetDestinationIntersection(); // if the road loops back onto same intersection, forget it if( in == this ) { continue; } // if this is a shortcut road, add it to the shortcut list, // but skip adding it to adjacency list if( road->GetShortCut() ) { continue; } RoadManager::ShortestRoad sr; sr.isOutRoad = true; sr.road = road; sr.cost = road->GetRoadLength(); // see if we already have this intersection listed // If not, add it. // If found, check if this road is shorter than road listed RoadManager::ShortestRoad* testRoad = NULL; const Intersection* testInt = NULL; bool found = false; for( int j=0; jroad->GetRoadLength(); if( testRoad->isOutRoad ) { testInt = testRoad->road->GetDestinationIntersection(); } else { testInt = testRoad->road->GetSourceIntersection(); testCost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f; } if( testInt == in ) { // ok, "in" already exists in our list... // if the length of road is shorter, store this road // instead of testRoad found = true; if( sr.cost < testCost ) { roadsToAdjacentInts.Remove( j ); roadsToAdjacentInts.Add( sr ); } // we need to break here since we modified the usesize. break; } } // if we never found it in any of our lists, add it to the list of IN roads if( !found ) { roadsToAdjacentInts.Add( sr ); } } } /* ============================================================================== Intersection::SortRoads ============================================================================== Description: Sorts the roads into clockwise order. Pick a road to start, add it to the head of the sorted list. Find the first road to the right of that, add it to the sorted list. Repeat until all roads have been added to the sorted list. Parameters: ( void ) Return: void ============================================================================= */ void Intersection::SortRoads( void ) { struct roadOrientation { Road* pRoad; float fAngle; }; // a copy of the list of the roads leading in to this intersection. // roadOrientation roadListIn[ MAX_ROADS ]; // a copy of the list of the roads leading out of this intersection. // roadOrientation roadListOut[ MAX_ROADS ]; for ( int i = 0; i < MAX_ROADS; i++ ) { roadListIn[ i ].pRoad = mRoadListIn[ i ]; if ( mRoadListIn[ i ] ) { rmt::Vector to, from; // Get the facing vector by finding the two points that connect the road. // roadListIn[ i ].pRoad->GetSourceIntersection()->GetLocation( from ); this->GetLocation( to ); // Get the vector between and flip it. // Choreo considers 0 0 -1 = 0 degrees. // to.Sub( from ); to.Scale( -1.0f ); roadListIn[ i ].fAngle = choreo::GetWorldAngle( to.x, to.z ); } roadListOut[ i ].pRoad = mRoadListOut[ i ]; if ( mRoadListOut[ i ] ) { rmt::Vector to, from; // Get the facing vector by finding the two points that connect the road. // roadListOut[ i ].pRoad->GetDestinationIntersection()->GetLocation( from ); this->GetLocation( to ); // Get the vector between and flip it. // Choreo considers 0 0 -1 = 0 degrees. // to.Sub( from ); to.Scale( -1.0f ); roadListOut[ i ].fAngle = choreo::GetWorldAngle( to.x, to.z ); } } for ( int i = 0; i < MAX_ROADS; i++ ) { roadOrientation* pRoadOrientation = 0; // A larger angle than would ever be stored. // float fMinAngle = rmt::PI * 4; for ( int j = 0; j < MAX_ROADS; j++ ) { // Take the smallest remaining angle each time through. // if ( roadListIn[ j ].pRoad && roadListIn[ j ].fAngle < fMinAngle ) { // This is the best candidate so far. // fMinAngle = roadListIn[ j ].fAngle; // So store it for later. // pRoadOrientation = &roadListIn[ j ]; } } if ( pRoadOrientation ) { // Store this road in sorted order. // mRoadListIn[ i ] = pRoadOrientation->pRoad; // Remove the road from consideration. // pRoadOrientation->pRoad = 0; } else { mRoadListIn[ i ] = 0; } } for ( int i = 0; i < MAX_ROADS; i++ ) { roadOrientation* pRoadOrientation = 0; // A larger angle than would ever be stored. // float fMinAngle = rmt::PI * 4; for ( int j = 0; j < MAX_ROADS; j++ ) { // Take the smallest remaining angle each time through. // if ( roadListOut[ j ].pRoad && roadListOut[ j ].fAngle < fMinAngle ) { // This is the best candidate so far. // fMinAngle = roadListOut[ j ].fAngle; // So store it for later. // pRoadOrientation = &roadListOut[ j ]; } } if ( pRoadOrientation ) { // Store this road in sorted order. // mRoadListOut[ i ] = pRoadOrientation->pRoad; // Remove the road from consideration. // pRoadOrientation->pRoad = 0; } else { mRoadListOut[ i ] = 0; } } ////////////////////////////////////////////////////////////////////////// // store shortcut roads going OUT of this intersection.. // Note, shortcuts can only be taken one way (e.g. a jump), so we // only consider the OUTGOING shortcut roads... SwapArray outShortcutRoads; HeapMgr()->PushHeap(GMA_TEMP); outShortcutRoads.Allocate( MAX_ROADS ); HeapMgr()->PopHeap(GMA_TEMP); for( int i = 0; i(mnRoadsOut); i++ ) { Road* road = mRoadListOut[i]; const Intersection* in = road->GetDestinationIntersection(); // if the road loops back onto same intersection, forget it if( in == this ) { continue; } // if this is a shortcut road, add it to the shortcut list, // but skip adding it to adjacency list if( road->GetShortCut() ) { outShortcutRoads.Add( road ); continue; } } // also, store away the list of shortcut roads if( outShortcutRoads.mUseSize > 0 ) { mOutgoingShortcuts.Allocate( outShortcutRoads.mUseSize ); for( int i=0; i roadsToAdjacentInts; HeapMgr()->PushHeap(GMA_TEMP); roadsToAdjacentInts.Allocate( MAX_ROADS*2 ); HeapMgr()->PopHeap(GMA_TEMP); PopulateShortestRoads( roadsToAdjacentInts, true ); if( roadsToAdjacentInts.mUseSize > 0 ) { mShortestRoadsToAdjacentIntersectionsWithMultiplier.Allocate( roadsToAdjacentInts.mUseSize ); for( int i=0; i 0 ) { mShortestRoadsToAdjacentIntersectionsNoMultiplier.Allocate( roadsToAdjacentInts.mUseSize ); for( int i=0; i 2 ) { mBigIntersection = new RoadManager::BigIntersection; mBigIntersection->in = this; } //////////////////////////////////////////////////////////////////////////// // allocate space for waiting roads... only need to do it for IN roads // because they're the ones approaching the intersection if( mType != Intersection::NO_STOP ) { if( mnRoadsIn > 0 ) { mWaitingRoads.Allocate( mnRoadsIn ); } } //////////////////////////////////////////////////////////////////////////// // Do some checking here, shall we? // Every intersection must have at least 1 neighbor (must be attached to rest of world) #if( RAD_TUNE || RAD_DEBUG ) if( numAdjacentInts <= 0 ) { char msg[256]; sprintf( msg, "Intersection at (%0.2f,%0.2f,%0.2f) is not joined to any other\n" " intersection by any road!\n", mLocation.x, mLocation.y, -mLocation.z ); rTuneAssertMsg( false, msg ); } #endif #ifdef RAD_DEBUG for( int i = 0; iisOutRoad ) { testInt = (Intersection*) shortestRoad->road->GetSourceIntersection(); } else { testInt = (Intersection*) shortestRoad->road->GetDestinationIntersection(); } rAssert( testInt == this ); } for( int i = 0; iisOutRoad ) { testInt = (Intersection*) shortestRoad->road->GetSourceIntersection(); } else { testInt = (Intersection*) shortestRoad->road->GetDestinationIntersection(); } rAssert( testInt == this ); } #endif #ifdef TOOLS // char baseMsg [1000]; // char fullMsg [1100]; /* // Every intersection should have at minimum 1 OUT road if( mnRoadsOut <= 0 ) { sprintf( fullMsg, "You are doomed. Intersection at (%f,%f,%f)\n" " does not have an out road.\n", mLocation.x, mLocation.y, -1*mLocation.z ); rTuneAssertMsg( false, fullMsg ); } */ /* // Road segments coming in and going out of this intersection MUST have same values // of y where they hit the intersection. If this is not so, it is due to BAD ROAD DATA. // For the sake of traffic, the intersection must be FLAT (so Traffic only computes // a spline in 2D rather than 3D). // // If the y values of any two segments do not match up, cars will "hop" when // they transit through the intersection between these segments. // // Please notify Sheik to check the road data around nearby intersections. // sprintf( baseMsg, "\nMismatching y-values at intersection (%f,%f,%f).\n" " Check if y values are same for all IN & OUT road segments attached\n" " to this intersection. Check hypergraph to see if the roadnodes leading\n" " IN and OUT of this intersection contain all the proper roadsegments.\n" " If you skip this, Traffic cars will \"hop\" when they transit through\n" " the intersection between the road segments with mismatching y values.\n" " Better to report error to me (Dusit) or Sheik.\n\n", mLocation.x, mLocation.y, -1*mLocation.z ); float ep = 0.001f; for( unsigned int m=0; mGetRoadSegment ( mRoadListIn[m]->GetNumRoadSegments()-1 ); rTuneAssert( inSeg != NULL ); rmt::Vector p1, p2; inSeg->GetCorner( 1, p1 ); inSeg->GetCorner( 2, p2 ); sprintf( fullMsg, "%s Offending road: IN \"%s\"\n", baseMsg, mRoadListIn[m]->GetName() ); rTuneAssertMsg( rmt::Epsilon( p1.y, p2.y, ep ), fullMsg ); for( unsigned int n=0; nGetRoadSegment( 0 ); rTuneAssert( outSeg != NULL ); rmt::Vector q0, q3; outSeg->GetCorner( 0, q0 ); outSeg->GetCorner( 3, q3 ); sprintf( fullMsg, "%s Offending roads: IN \"%s\", OUT \"%s\"\n", baseMsg, mRoadListIn[m]->GetName(), mRoadListOut[n]->GetName() ); rTuneAssertMsg( rmt::Epsilon( p1.y, q0.y, ep ) && rmt::Epsilon( p1.y, q3.y, ep ), fullMsg ); } } */ #endif } //============================================================================== // Intersection::GetType //============================================================================== // Description: Comment // // Parameters: () // // Return: Intersection::Type // //============================================================================== Intersection::Type Intersection::GetType() const { return mType; } //============================================================================== // Intersection::SetType //============================================================================== // Description: Comment // // Parameters: ( Type type ) // // Return: void // //============================================================================== void Intersection::SetType( Type type ) { mType = type; switch( mType ) { case NO_STOP: { mpTrafficControl = NULL; } break; case N_WAY: { mpTrafficControl = &mNWayControl; } break; default: { mpTrafficControl = NULL; } break; } } //============================================================================= // Intersection::IsPointInIntersection //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector& point ) // // Return: bool // //============================================================================= bool Intersection::IsPointInIntersection( rmt::Vector& point ) const { rmt::Vector vectorBetween; vectorBetween.x = point.x - mLocation.x; vectorBetween.y = 0.0f; vectorBetween.z = point.z - mLocation.z; vectorBetween.x *= vectorBetween.x; vectorBetween.z *= vectorBetween.z; vectorBetween.x += vectorBetween.z; return ( vectorBetween.x <= rmt::Sqr( mfRadius ) ); } void Intersection::FindGoodTrafficLane( const Road& road, unsigned int preferredLaneIndex, Lane*& outLane, unsigned int& outLaneIndex ) { Lane* lane = road.GetLane(preferredLaneIndex); int numTrafficVehicles = 0; if( lane != NULL ) { numTrafficVehicles = lane->mTrafficVehicles.mUseSize; rAssert( numTrafficVehicles >= 0 ); if( numTrafficVehicles < lane->GetDensity() ) { outLane = lane; outLaneIndex = preferredLaneIndex; return; } } // if can't use the preferred lane, gotta find the first lane that can support my vehicle... for( unsigned int i=0; imTrafficVehicles.mUseSize; rAssert( numTrafficVehicles >= 0 ); if( numTrafficVehicles < lane->GetDensity() ) { outLane = lane; outLaneIndex = i; return; } } // finally, can't find any lane, return NULL... outLane = NULL; outLaneIndex = 0; return; } void Intersection::GetLeftTurnForTraffic( const Road& inRoad, unsigned int preferredLaneIndex, Road*& outRoad, Lane*& outLane, unsigned int& outLaneIndex ) { outRoad = NULL; outLane = NULL; outLaneIndex = 0; int index = FindRoadIn( &inRoad ); rAssert( index != -1 ); if( index == -1 ) { return; } if( mnRoadsOut <= 0 ) { return; } if( mnRoadsOut == 1 ) { //No roads out. Or only a straight out. rDebugString( "Intersection has only one OUT road!\n" ); outRoad = mRoadListOut[0]; // Pick an outLane & outLaneIndex FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex ); if( outLane == NULL ) { return; } } else { // To find which turn will give a closest approximation to a "LEFT" // turn, we turn our inDir by 90 degrees (to the left!) and // iterate through the outDirs for one that's STRAIGHTEST along it. // rmt::Vector origInDir; rmt::Vector inDir, outDir; // Grab normal of trailing edge of the last segment of IN road (for comparison) RoadSegment* segment; unsigned int nSegments = inRoad.GetNumRoadSegments(); assert( nSegments > 0 ); segment = inRoad.GetRoadSegment( nSegments - 1 ); // TODO: // Don't use Edge Normals, use Direction of Segment's LANE (but we'll have to // pass in lane info) segment->GetEdgeNormal( 2, origInDir ); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. origInDir.y = 0.0f; origInDir.Normalize(); // *** SQUARE ROOT! *** // copy origInDir... we'll be modifying inDir to suit our needs inDir = origInDir; inDir.Scale(-1.0f); // turn inDir CCW 90 degrees about y axis (the ideal left turn // will be 180 angle from this vector) rmt::Vector temp; temp.Set( -1 * inDir.z, inDir.y, inDir.x ); inDir = temp; float cosAlpha = 0.0f; // Let bestCosAlpha be, initially, the WORST value possible // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD! // As we iterate through the OUT roads, we want to find one with most // alpha, or smallest cosAlpha // float bestCosAlpha = 1.0f; bool stillLookingForFirstRoad = true; outRoad = NULL; outLane = NULL; outLaneIndex = 0; unsigned int i = 0; for( i ; iGetRoadSegment(0); segment->GetEdgeNormal(0, outDir); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. outDir.y = 0.0f; outDir.Normalize(); // *** SQUARE ROOT! *** // Skip this OUT road if it just goes back the way we came // This prevents U-turn at an intersection when there are // other roads to take, even if they don't turn the way // we want. The decision is really up to traffic control or // some other entity; for now, we hardcode it in the Intersection if( outDir.Equals( origInDir * -1, 0.05f ) ) { continue; } cosAlpha = inDir.Dot(outDir); // *** NOTE *** // If contention arises between a left and a right turn that // both have bestCosAlpha, this inequality will favor the last // best match. if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) ) { Lane* laneCandidate = NULL; unsigned int laneIndexCandidate = 0; FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate ); if( laneCandidate != NULL ) { stillLookingForFirstRoad = false; bestCosAlpha = cosAlpha; outRoad = mRoadListOut[i]; outLane = laneCandidate; outLaneIndex = laneIndexCandidate; } } }// end of FOR LOOP }// end of ELSE } void Intersection::GetStraightForTraffic( const Road& inRoad, unsigned int preferredLaneIndex, Road*& outRoad, Lane*& outLane, unsigned int& outLaneIndex ) { outRoad = NULL; outLane = NULL; outLaneIndex = 0; int index = FindRoadIn( &inRoad ); rAssert( index != -1 ); if( index == -1 ) { return; } if( mnRoadsOut <= 0 ) { return; } if ( mnRoadsOut == 1 ) { rDebugString( "Intersection has only one OUT road!\n" ); outRoad = mRoadListOut[0]; // Pick an outLane & outLaneIndex FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex ); if( outLane == NULL ) { return; } } else { // Iterate through all the OUT roads to find which one gives us the angle closest to // going STRAIGHT (180) // rmt::Vector origInDir; rmt::Vector inDir, outDir; // Grab normal of trailing edge of the last segment of IN road (for comparison) RoadSegment* segment; unsigned int nSegments = inRoad.GetNumRoadSegments(); assert( nSegments > 0 ); segment = inRoad.GetRoadSegment( nSegments - 1 ); segment->GetEdgeNormal( 2, origInDir ); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. origInDir.y = 0.0f; origInDir.Normalize(); // *** SQUARE ROOT! *** inDir = origInDir; inDir.Scale(-1.0f); float cosAlpha = 0.0f; // Let bestCosAlpha be, initially, the WORST value possible // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD! // As we iterate through the OUT roads, we want to find one with most // alpha, or smallest cosAlpha // float bestCosAlpha = 1.0f; bool stillLookingForFirstRoad = true; unsigned int i = 0; for( i ; iGetRoadSegment(0); segment->GetEdgeNormal(0, outDir); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. outDir.y = 0.0f; outDir.Normalize(); // *** SQUARE ROOT! *** // Skip this OUT road if it just goes back the way we came // This prevents U-turn at an intersection when there are // other roads to take, even if they don't turn the way // we want. The decision is really up to traffic control or // some other entity; for now, we hardcode it in the Intersection if( outDir.Equals( origInDir * -1, 0.05f ) ) { continue; } cosAlpha = inDir.Dot(outDir); // *** NOTE *** // This inequality will favor the last best match. (If another road // has cosAlpha = current bestCosAlpha, we ignore it & go with the // most recent one). if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) ) { Lane* laneCandidate = NULL; unsigned int laneIndexCandidate = 0; FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate ); if( laneCandidate != NULL ) { stillLookingForFirstRoad = false; bestCosAlpha = cosAlpha; outRoad = mRoadListOut[i]; outLane = laneCandidate; outLaneIndex = laneIndexCandidate; } } } } } // Given the current road, this should return a ref to the right turn road. void Intersection::GetRightTurnForTraffic( const Road& inRoad, unsigned int preferredLaneIndex, Road*& outRoad, Lane*& outLane, unsigned int& outLaneIndex ) { outRoad = NULL; outLane = NULL; outLaneIndex = 0; int index = FindRoadIn( &inRoad ); rAssert( index != -1 ); if( index == -1 ) { return; } if( mnRoadsOut <= 0 ) { return; } if( mnRoadsOut == 1 ) { //No roads out. Or only a straight out. rDebugString( "Intersection has only one OUT road!\n" ); outRoad = mRoadListOut[0]; // Pick an outLane & outLaneIndex FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex ); if( outLane == NULL ) { return; } } else { // To find which turn will give a closest approximation to a "LEFT" // turn, we turn our inDir by 90 degrees (to the left!) and // iterate through the outDirs for one that's STRAIGHTEST along it. // rmt::Vector origInDir; rmt::Vector inDir, outDir; // Grab normal of trailing edge of the last segment of IN road (for comparison) RoadSegment* segment; unsigned int nSegments = inRoad.GetNumRoadSegments(); assert( nSegments > 0 ); segment = inRoad.GetRoadSegment( nSegments - 1 ); segment->GetEdgeNormal( 2, origInDir ); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. origInDir.y = 0.0f; origInDir.Normalize(); // *** SQUARE ROOT! *** // copy origInDir... we'll be modifying inDir to suit our needs inDir = origInDir; inDir.Scale(-1.0f); // turn inDir CW 90 degrees about y axis (the ideal right turn // is at 180 angle from this vector) rmt::Vector temp; temp.Set( inDir.z, inDir.y, -1 * inDir.x ); inDir = temp; float cosAlpha = 0.0f; // Let bestCosAlpha be, initially, the WORST value possible // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD! // As we iterate through the OUT roads, we want to find one with most // alpha, or smallest cosAlpha // float bestCosAlpha = 1.0f; bool stillLookingForFirstRoad = true; unsigned int i = 0; for( i ; iGetRoadSegment(0); segment->GetEdgeNormal(0, outDir); // *** NOTE *** // We rely on the assumption that the intersection is HORIZONTALLY FLAT // Else, we'll have to project the In & Out vectors onto the plane of // the intersection. outDir.y = 0.0f; outDir.Normalize(); // *** SQUARE ROOT! *** // Skip this OUT road if it just goes back the way we came // This prevents U-turn at an intersection when there are // other roads to take, even if they don't turn the way // we want. The decision is really up to traffic control or // some other entity; for now, we hardcode it in the Intersection if( outDir.Equals( origInDir * -1, 0.05f ) ) { continue; } cosAlpha = inDir.Dot(outDir); // *** NOTE *** // If contention arises between a left and a right turn that // both have bestCosAlpha, this inequality will favor the last // best match. if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) ) { Lane* laneCandidate = NULL; unsigned int laneIndexCandidate = 0; FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate ); if( laneCandidate != NULL ) { stillLookingForFirstRoad = false; bestCosAlpha = cosAlpha; outRoad = mRoadListOut[i]; outLane = laneCandidate; outLaneIndex = laneIndexCandidate; } } } } } void Intersection::GetOtherIntersection ( bool useMultiplier, Intersection* knownIntersection, Intersection*& otherIntersection, RoadManager::ShortestRoad*& road ) { rAssert( knownIntersection != NULL ); // works only for non-big intersections if( mBigIntersection != NULL ) { rAssert( false ); otherIntersection = NULL; road = NULL; return; } SwapArray* array = useMultiplier ? &mShortestRoadsToAdjacentIntersectionsWithMultiplier : &mShortestRoadsToAdjacentIntersectionsNoMultiplier ; // I'm a non-big intersection if I'm connected to either 1 or 2 other // intersections rAssert( 1 <= array->mUseSize && array->mUseSize <= 2 ); for( int i=0; imUseSize; i++ ) { RoadManager::ShortestRoad* shortRoad = &((*array)[i]); rAssert( shortRoad ); Intersection* otherIn = NULL; if( shortRoad->isOutRoad ) { otherIn = (Intersection*) shortRoad->road->GetDestinationIntersection(); } else { otherIn = (Intersection*) shortRoad->road->GetSourceIntersection(); } if( otherIn != knownIntersection ) { road = shortRoad; otherIntersection = otherIn; return; } } }