/////////////////////////////////////////////////////////////////////////////// // // Nav_mgr.cpp // // /////////////////////////////////////////////////////////////////////////////// #include "navigation\nav_mgr.hpp" #include "navigation\nav_plan.hpp" #include "objects\nav_node_place_holder.hpp" #include "objects\nav_connection_place_holder.hpp" nav_mgr* nav_mgr::m_sThis = NULL; //============================================================================= // // // // //============================================================================= nav_mgr::nav_mgr(void) : m_HorizontalNodeSearchRange(2000.0f), m_VerticalNodeSearchRange(300.0f), m_SearchCount(0), m_NavConnectionModeEnabled(false), m_CheapestNode(SLOT_NULL), m_FirstNavNodeForConnection(0), m_FirstSelectedObject(SLOT_NULL), m_NavTestStart(SLOT_NULL), m_NavTestEnd(SLOT_NULL) { if ( m_sThis != NULL ) { ASSERT(FALSE); } Init(); } //============================================================================= // // // // //============================================================================= nav_mgr::~nav_mgr() { } xbool nav_mgr::UpdateNavPlan( vector3& thisPoint, nav_plan& thisPlan ) { m_CheapestSolution = 99999999.0f; m_SearchCount++; slot_id nearestNode = thisPlan.GetStartingPoint(); if( nearestNode == SLOT_NULL ) { nearestNode = LocateNearestNode( thisPoint ); } // thisPlan.SetDestination(thisPoint); slot_id destinationNode = LocateNearestNode( thisPlan.GetDestination() ); // Handle the special case of the the starting node being the destination if( nearestNode == thisPlan.GetDestinationNode() ) { thisPlan.Reset(); thisPlan.AddPoint( SLOT_NULL , destinationNode ); return true; } m_NavNodes[nearestNode].SetSearchInfo(SLOT_NULL,SLOT_NULL,m_SearchCount,m_SearchStepNumber,0.0f); m_CheapestNode = nearestNode; s32 tries = 200; while ( m_CheapestNode != destinationNode && tries ) { slot_id tempID = m_CheapestNode; m_CheapestNode = m_NavNodes[m_CheapestNode].GetNextInList(); AddConnectedNodesToOpenList( tempID ); tries--; } slot_id tempID; s32 count=0; tempID = m_CheapestNode; // slot_id head; for( count =0; count < kMAX_CONNECTIONS; count++ ) { m_NavNodes[count].SetIsInPath(false); } count = 0; while (tempID != SLOT_NULL) { if(m_NavNodes[tempID].GetPred() != SLOT_NULL) count++; m_NavNodes[tempID].SetIsInPath(true); tempID = m_NavNodes[tempID].GetPred(); } tempID = m_CheapestNode; while(count >= 0 ) { thisPlan.AddPoint(m_NavNodes[tempID].GetConnectionUsedInSearch(), tempID, count); tempID = m_NavNodes[tempID].GetPred(); count--; } // while(head != -1) // { // char tempString[256]; // x_sprintf( tempString,"Step: %d Node: %d Cost: %f\n", count,head,m_NavNodes[head].GetCostToNode() ); // head = m_NavNodes[head].GetNextInList(); // count++; // } // thisPlan.AddPoint() if (tries > 0 ) { thisPlan.SetCompletePath(true); return true; } else { thisPlan.SetCompletePath(false); return false; } } void nav_mgr::AddConnectedNodesToOpenList( slot_id nearestNode ) { m_SearchStepNumber++; s32 count; for(count = 0; count < k_MAX_NODE_CONNECTIONS; count++ ) { slot_id connectionID = m_NavNodes[nearestNode].GetConnectionByIndex( count ); if(connectionID != SLOT_NULL ) { slot_id nodeToAdd = m_Connections[connectionID].GetOtherEnd(nearestNode); // if it is not in this search if( ( m_NavNodes[nodeToAdd].GetSearchNumber() != m_SearchCount) ) { m_NavNodes[nodeToAdd].SetCost( m_NavNodes[nearestNode].GetCostToNode() + m_Connections[connectionID].GetCost() ); m_NavNodes[nodeToAdd].SetConnectionUsedInSearch( connectionID ); InsertNodeIntoList( nodeToAdd, nearestNode, connectionID ); } else if( m_NavNodes[nodeToAdd].GetSearchStepNumber() == m_SearchStepNumber ) { // if it is in the list already but it was from this same call to add nodes // we need to check see which is cheaper and add the cheaper one // FIXME! } else { // else it is in the list and wasn't from this pass } } } } void nav_mgr::InsertNodeIntoList( slot_id thisNode, slot_id parent, slot_id connectionID ) { // slot_id currentID = m_CheapestNode; // Nodes are inserted in sorted order. m_CheapestNode is the index to the current cheapest // node in the list and is therefore the start of the list. To inser the node we get it's // cost and walk through the list until it's less than the next one or until the next index // is SLOT_NULL // if the cheapest node is SLOT_NULL then this is the first item to be entered. if ( m_CheapestNode == SLOT_NULL ) { m_CheapestNode = thisNode; m_NavNodes[thisNode].SetSearchInfo( SLOT_NULL, parent,m_SearchCount, m_SearchStepNumber, m_NavNodes[parent].GetCostToNode() + m_Connections [ connectionID ].GetCost() ); return; } // if we make it here then at least one item is already in the list. We step through the list // till we slot_id m_HigherCostNode, nodeToInsertAfter; m_HigherCostNode = m_CheapestNode; nodeToInsertAfter = SLOT_NULL; f32 thisCost = m_NavNodes[thisNode].GetCostToNode(); // This while loop should walk through the list till it hits a node that is higher cost than // itself or until it hits the end of the list. if(thisCost < m_NavNodes[m_CheapestNode].GetCostToNode() ) { m_CheapestNode = thisNode; } while( m_HigherCostNode != SLOT_NULL && thisCost > m_NavNodes[m_HigherCostNode].GetCostToNode() ) { nodeToInsertAfter = m_HigherCostNode; m_HigherCostNode = m_NavNodes[m_HigherCostNode].GetNextInList(); } // if this is the new cheapest node... if(nodeToInsertAfter == SLOT_NULL ) { m_CheapestNode = thisNode; m_NavNodes[thisNode].SetSearchInfo( m_HigherCostNode, parent,m_SearchCount, m_SearchStepNumber, m_NavNodes[parent].GetCostToNode() + m_Connections [ connectionID ].GetCost() ); } else //if(m_HigherCostNode != SLOT_NULL ) { ASSERT(nodeToInsertAfter != SLOT_NULL ); // to insert in the list we first make the previous point to this one m_NavNodes[nodeToInsertAfter].SetNextInList( thisNode ); // then make this one point to the next one m_NavNodes[thisNode].SetSearchInfo( m_HigherCostNode, parent,m_SearchCount, m_SearchStepNumber, m_NavNodes[parent].GetCostToNode() + m_Connections [ connectionID ].GetCost() ); } } //============================================================================= // // // // //============================================================================= nav_node& nav_mgr::GetNavNode(slot_id thisNavNode ) { ASSERT(thisNavNode != SLOT_NULL && thisNavNode < kMAX_NAV_NODES ); return (m_NavNodes[thisNavNode] ); } //============================================================================= // // // // //============================================================================= nav_connection& nav_mgr::GetConnection( slot_id thisNavConnection ) { ASSERT(thisNavConnection != SLOT_NULL && thisNavConnection < kMAX_CONNECTIONS ); return ( m_Connections[thisNavConnection] ); } //============================================================================= // // // // //============================================================================= slot_id nav_mgr::GetNewNavNode( void ) { slot_id anId = GetFirstOpenNavNode(); GetNavNode(anId ).Reset(); m_NavNodes[anId].SetIsInUse(); return anId; } //============================================================================= // // // // //============================================================================= void nav_mgr::Init ( void ) { // singleton class. Need to keep track of the singleton pointer m_sThis = this; s32 count; for( count = 0; count < kMAX_STEPS; count++) m_CurrentPath[count] = SLOT_NULL; for(count = 0; count < kMAX_NAV_NODES; count++) { m_NavNodes[count].Reset(); m_NavNodes[count].SetSlotID( count ); } for(count = 0; count < kMAX_CONNECTIONS; count++) { m_Connections[count].Reset(); } m_Destination.Zero(); } slot_id nav_mgr::LocateNearestNode ( vector3& thisPoint ) { f32 MinDistance = 999999999.0f; slot_id NearestSlot = SLOT_NULL; m_SearchCount++; // SLOWEST POSSIBLE WAY TO DO THIS. :P Sorry, just for now. Need to get a spatial // Database in place for the real version or get these into the database. s32 count; for( count = 0; count < kMAX_NAV_NODES; count++ ) { if( m_NavNodes[count].IsInUse() ) { f32 thisLength = ( m_NavNodes[count].GetPosition() - thisPoint).LengthSquared(); if(thisLength < MinDistance ) { MinDistance = thisLength; NearestSlot = count; } } } return NearestSlot; } //============================================================================= void nav_mgr::LocateAllNodesInArea ( bbox& Area, xarray& Nodes, u32 flags ) { s32 count; for( count = 0; count < kMAX_NAV_NODES; count++ ) { if( m_NavNodes[count].IsInUse() ) { if( flags == nav_node::FLAG_ALL || flags & m_NavNodes[count].GetAttributes() ) { if( Area.Intersect( &m_NavNodes[count].GetPosition(), 1 ) ) { Nodes.Append( &m_NavNodes[count] ); } } } } } //============================================================================= //============================================================================= // // // // //============================================================================= slot_id nav_mgr::GetFirstOpenNavNode ( void ) { slot_id count; for(count=0 ; countGetNavNode(count).SetPlaceHolder(aGuid ); object *tempObject = g_ObjMgr.GetObjectByGuid( aGuid ); nav_node_place_holder &aNNPH = nav_node_place_holder::GetSafeType(*tempObject); aNNPH.SetNode( count ); // aNNPH.OnMove(nav_mgr::GetNavMgr()->GetNavNode(count).GetPosition()); count++; } count =0; while ( count < kMAX_CONNECTIONS && m_Connections[count].GetEndNode() != SLOT_NULL ) { guid node1, node2; node1 = m_NavNodes[m_Connections[count].GetStartNode()].GetPlaceHolder(); node2 = m_NavNodes[m_Connections[count].GetEndNode()].GetPlaceHolder(); AddGuidToNavConnection(node1,count); AddGuidToNavConnection(node2,count); count++; } count =0; while ( m_NavNodes[count].IsInUse() ) { guid aGuid = nav_mgr::GetNavMgr()->GetNavNode(count).GetPlaceHolder(); object *tempObject = g_ObjMgr.GetObjectByGuid( aGuid ); nav_node_place_holder &aNNPH = nav_node_place_holder::GetSafeType(*tempObject); aNNPH.OnMove( nav_mgr::GetNavMgr()->GetNavNode(count).GetPosition() ); count++; } // x_fclose(tempFile); // m_hConnections.SetName( String ); // m_hConnections.GetPointer(); // Force to load now } } void nav_mgr::SaveNavMap(const char* fileName ) { X_FILE* tempFile = x_fopen(fileName, "wb" ); // ASSERT(tempFile); // fileio File; // File.Save(tempFile,*this,false); s32 count = 0; if (tempFile) { for( count =0; count< kMAX_CONNECTIONS;count++ ) { m_Connections[count].Save(tempFile); } for( count =0; count< kMAX_NAV_NODES;count++ ) { m_NavNodes[count].Save(tempFile); } x_fclose(tempFile); } } void nav_mgr::AddGuidToNavConnection( guid aGuid, slot_id thisConnection) { object *tempObject = g_ObjMgr.GetObjectByGuid(aGuid); if ( ( tempObject ) && (tempObject->IsKindOf(nav_node_place_holder::GetRTTI())) ) // SB - Crash FIX!! { if(m_FirstSelectedObject == SLOT_NULL) { m_FirstSelectedObject = tempObject->GetSlotID(); } else if( m_FirstSelectedObject == tempObject->GetSlotID() ) { // The clicked the same node again... m_FirstSelectedObject = SLOT_NULL; // return false; } else { // if we have one and they selected // slot_id anID = nav_mgr::GetNavMgr()->GetNewConnection(); guid tempGuid = g_ObjMgr.CreateObject(object::TYPE_NAV_CONNECTION_PLACE_HOLDER ); object *tempNNCPH = g_ObjMgr.GetObjectByGuid( tempGuid ); nav_connection_place_holder &aNNCPH = nav_connection_place_holder::GetSafeType( *tempNNCPH); slot_id anID = thisConnection; aNNCPH.SetConnection( anID ); nav_mgr::GetNavMgr()->GetConnection(anID).SetPlaceHolder(aNNCPH.GetGuid()); object *tempNNPH = g_ObjMgr.GetObjectBySlot(m_FirstSelectedObject ); nav_node_place_holder &aNavNode1 = nav_node_place_holder::GetSafeType(*tempNNPH); object *tempObject2 = g_ObjMgr.GetObjectBySlot(tempObject->GetSlotID() ); nav_node_place_holder &aNavNode2 = nav_node_place_holder::GetSafeType(*tempObject2); slot_id connectedNavNode = aNNCPH.GetConnection(); nav_connection &aNC = nav_mgr::GetNavMgr()->GetConnection(connectedNavNode); aNC.SetNodes(aNavNode1.GetNode() , aNavNode2.GetNode() ); nav_node &aNN1 = nav_mgr::GetNavMgr()->GetNavNode(aNavNode1.GetNode() ); nav_node &aNN2 = nav_mgr::GetNavMgr()->GetNavNode(aNavNode2.GetNode() ); aNN1.AddConnection(connectedNavNode); aNN2.AddConnection(connectedNavNode); aNNCPH.OnMove(aNC.GetPosition() ); m_FirstSelectedObject = SLOT_NULL; // return false; } } // return true; } void nav_mgr::SetNavTestStart( guid thisNode ) { object *tempNNCPH = g_ObjMgr.GetObjectByGuid( thisNode); if(tempNNCPH && tempNNCPH->GetType() == object::TYPE_NAV_NODE_PLACE_HOLDER ) { nav_node_place_holder &aNNPH = nav_node_place_holder ::GetSafeType( *tempNNCPH); m_NavTestStart = aNNPH.GetNode(); } } void nav_mgr::SetNavTestEnd ( guid thisNode ) { object *tempNNCPH = g_ObjMgr.GetObjectByGuid( thisNode); if(tempNNCPH && tempNNCPH->GetType() == object::TYPE_NAV_NODE_PLACE_HOLDER ) { nav_node_place_holder &aNNPH = nav_node_place_holder ::GetSafeType( *tempNNCPH); m_NavTestEnd = aNNPH.GetNode(); } } void nav_mgr::TestNav(void) { if( m_NavTestStart != SLOT_NULL && m_NavTestEnd != SLOT_NULL ) { nav_plan aPlan; aPlan.SetStartingPoint( m_NavTestStart); aPlan.SetDestination(m_NavNodes[m_NavTestEnd].GetPosition() ); UpdateNavPlan(m_NavNodes[m_NavTestEnd].GetPosition() ,aPlan ); } } void nav_mgr::FileIO( fileio& File ) { // fileio File; // return( File.PreLoad( FP ) ); s32 count = 0; for( count = 0; count < kMAX_NAV_NODES; count++ ) { m_NavNodes[count].FileIO( File ); } for( count = 0; count < kMAX_CONNECTIONS; count++ ) { m_Connections[count].FileIO( File ); } } void nav_mgr::SanityCheck(void) { s32 count; for(count = 0; count < kMAX_NAV_NODES; count++ ) { } } /** * Initialise the cost of each node to the maximum possible cost. */ /* extern Node* source; extern Node* target; source->cost = 0.0f; // could be almost any data-structure vector open; open.insert( source ); while (!open.empty()) { // pick the next node as the one with the best estimate Node* current = FindBest( &open ); // check if we've reached the target, and if so, return the result! if (current == target) return true; // scan all the neighbours of the current node for (Edge* e = current->edges.begin(); e != current->edges.end(); e++) { // determine the cost of this node for taking the current edge Node* neighbour = e->GetEndNode(); const float cost = current->cost + e->length; // if it's worse, then we just skip it if (cost > neigbour->cost) continue; // remove this neighbour from the list open.remove( neigbour ); // remember that the path via the current node is better neighbour->cost = cost; neighbour->parent = current; // and compute the estimated path length if this neighbour is used next neighbour->estimate = neighbour->cost + VectorLength( neighbour->pos - target->pos ); // and allow the algorithm to scan this node when it becomes the best option open.insert( neighbour ); } } */ /* //============================================================================= // // // // //============================================================================= xbool nav_mgr::UpdateNavPlan ( vector3& thisPoint, nav_plan& thisPlan ) { xbool NotThereYet = true; s32 StepsSearched = 0; m_CheapestSolution = 99999999.0f; slot_id nearestNode = LocateNearestNode( thisPoint ); slot_id destinationNode = LocateNearestNode( thisPlan.GetDestination() ); if( nearestNode == thisPlan.GetDestination() ) { thisPlan.AddPoint(SLOT_NULL, destinationNode ); return true; } // Ok, if the nearest node to us is not the nearest node to the destination then it's time // to do some searching. // First thing to do is add all the connection costs and nodes from the nearest node to get // us started ResetSearchNodes(); AddNodeToSearch( nearestNode ); s32 MaxTries = 128; while( NotThereYet && MaxTries ) { MaxTries--; s32 indexOfCurentCheapestNode = GetCheapestNode(); if( m_PathNodes[indexOfCurentCheapestNode].m_Node == destinationNode ) { UpdateNavPlan(thisPlan, ); } } /* m_SearchCount++; // First we check to make sure that the navPlan's final goal hasn't changed if ( thisPlan.GetDestination() != thisPoint ) { // if it changed we need to start over thisPlan.Reset(); thisPlan.SetDestination( thisPoint ); } vector3 WorkingPoint; slot_id WorkingNode; // DestinationNode; // Figure out what node to start at if( thisPlan.GetStartingPoint() != SLOT_NULL ) { WorkingNode = LocateNearestNode( thisPoint ); } else { WorkingNode = thisPlan.GetLastPoint(); } // At this point we should have a Working Node that is where we will be starting s32 StepCount = 0; // object* tempObject = g_ObjMgr.GetObjectBySlot( WorkingNode ); // nav_node CurrentNavNode = nav_node::GetSaveType( *tempObject ); while ( NotThereYet && StepCount < k_MAX_SEARCH_NODES ) { int connectionCount; for(connectionCount = 0; connectionCount< k_MAX_NODE_CONNECTIONS ; connectionCount++) { // if(CurrentNavNode.GetConnectionByIndex(connectionCount ) != SLOT_NULL ) { } } } /* return false; } //============================================================================= // // // // //============================================================================= slot_id nav_mgr::LocateNearestNode ( vector3& thisPoint ) { f32 MinDistance = 999999999.0f; slot_id NearestSlot = SLOT_NULL; m_SearchCount++; // SLOWEST POSSIBLE WAY TO DO THIS. :P Sorry, just for now. Need to get a spatial // Database in place for the real version or get these into the database. s32 count; for( count = 0; count < kMAX_NAV_NODES; count++ ) { if( m_NavNodes[count].IsInUse() ) { f32 thisLength = ( m_NavNodes[count].GetPosition() - thisPoint).LengthSquared(); if(thisLength < MinDistance ) { MinDistance = thisLength; NearestSlot = count; } } } return NearestSlot; /* bbox volumeToCheck; volumeToCheck.Translate ( thisPoint ); volumeToCheck.Inflate ( m_HorizontalNodeSearchRange, m_VerticalNodeSearchRange, m_HorizontalNodeSearchRange ); g_ObjMgr.SelectBBox( object::ATTR_ALL, volumeToCheck, object::TYPE_NAVNODE ); slot_id anID = g_ObjMgr.StartLoop(); while ( anID != SLOT_NULL ) { f32 thisDistance = g_ObjMgr.GetObjectBySlot(anID)->GetPosition().Difference(thisPoint); if( thisDistance < MinDistance ) { MinDistance = thisDistance; NearestSlot = anID; } anID = g_ObjMgr.GetNextResult( anID ); } g_ObjMgr.EndLoop(); // if NearestSlot is neg 1 then we need to walk ALL nav nodes to find nearest if ( NearestSlot != SLOT_NULL ) { anID = g_ObjMgr.GetFirst( object::type::TYPE_NAVNODE ); if( anID == SLOT_NULL ) { // e_throw("No Navnodes in obj_mgr"); return SLOT_NULL; } while( anID != SLOT_NULL ) { f32 thisDistance = g_ObjMgr.GetObjectBySlot(anID)->GetPosition().Difference(thisPoint); if( thisDistance < MinDistance ) { MinDistance = thisDistance; NearestSlot = anID; } anID = g_ObjMgr.GetNext( anID ); } } // At this time we are guaranteed to have a slot and a distance unless there were no // nav_node in which case it should throw an exception return anID; */ /* } //============================================================================= // // // // //============================================================================= void nav_mgr::Init ( void ) { // singleton class. Need to keep track of the singleton pointer m_sThis = this; s32 count; for( count = 0; count < kMAX_STEPS; count++) m_CurrentPath[count] = SLOT_NULL; for(count = 0; count < kMAX_NAV_NODES; count++) { m_NavNodes[count].Reset(); } for(count = 0; count < kMAX_CONNECTIONS; count++) { m_Connections[count].Reset(); } m_Destination.Zero(); } //============================================================================= // // // // //============================================================================= slot_id nav_mgr::GetFirstOpenConnection ( void ) { slot_id count; for(count=0 ; count=0 && thisNavNode < kMAX_NAV_NODES ); return (m_NavNodes[thisNavNode] ); } //============================================================================= // // // // //============================================================================= nav_connection& nav_mgr::GetConnection( slot_id thisNavConnection ) { ASSERT(thisNavConnection >=0 && thisNavConnection < kMAX_CONNECTIONS ); return ( m_Connections[thisNavConnection] ); } //============================================================================= // // // // //============================================================================= slot_id nav_mgr::GetNewConnection( void ) { slot_id anId = GetFirstOpenConnection(); GetConnection( anId ).Reset(); return anId; } //============================================================================= // // // // //============================================================================= void nav_mgr::StartNavConnectionCreation ( slot_id firstPoint ) { m_FirstNavNodeForConnection = firstPoint; } //============================================================================= // // // // //============================================================================= void nav_mgr::CompleteNavConnection( slot_id secondPoint ) { if ( m_FirstNavNodeForConnection != 0 ) { slot_id anID = GetFirstOpenConnection(); // GetConnection(anID).SetNodes( m_FirstNavNodeForConnection, secondPoint ); } else { ASSERT(false); } } void nav_mgr::ResetSearchNodes(void) { s32 count; for(count = 0; count < k_MAX_SEARCH_NODES; count++ ) { m_PathNodes[count].m_Node = SLOT_NULL; m_PathNodes[count].m_Connection = SLOT_NULL; m_PathNodes[count].m_TotalCost = -1.0f; m_PathNodes[count].m_SearchStep = 0; } } /////////////////////////////////////////////////////////////////////////////////////////////////// // // AddNodeToSearch // // Adds all the nodes to current list of nodes and costs // // /////////////////////////////////////////////////////////////////////////////////////////////////// void nav_mgr::AddNodeToSearch( slot_id nearestNode, f32 startingCost, slot_id destinationNode ) { nav_node &thisNavNode = GetNavNode(nearestNode); ASSERT( thisNavNode.IsInUse() ); m_SearchStep++; s32 count; // walk through the connections that are tied to this node for(count = 0; count