2964 lines
70 KiB
C++
2964 lines
70 KiB
C++
// Graph.cpp: implementation of the CGraph class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "Graph.h"
|
|
#include "Heuristic.h"
|
|
#include "CAISystem.h"
|
|
|
|
|
|
#if !defined(LINUX)
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
|
|
#include "Cry_Math.h"
|
|
#include <ISystem.h>
|
|
#include <Cry_Camera.h>
|
|
|
|
#include <IRenderer.h>
|
|
#include <ILog.h>
|
|
#include <I3DEngine.h>
|
|
#include <algorithm>
|
|
#include <IConsole.h>
|
|
#include "IPhysics.h"
|
|
#include "AIObject.h"
|
|
#include "VertexList.h"
|
|
#include <CryFile.h>
|
|
|
|
#if defined(WIN32) && defined(_DEBUG)
|
|
#include <crtdbg.h>
|
|
#define DEBUG_NEW_NORMAL_CLIENTBLOCK(file, line) new(_NORMAL_BLOCK, file, line)
|
|
#define new DEBUG_NEW_NORMAL_CLIENTBLOCK( __FILE__, __LINE__)
|
|
#endif
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CGraph::CGraph(CAISystem *pSystem)
|
|
{
|
|
|
|
m_pFirst = CreateNewNode(true);//new GraphNode;
|
|
m_pSafeFirst = m_pFirst;
|
|
m_pCurrent = m_pFirst;
|
|
m_pCurrent->data.Reset();
|
|
m_pCurrent->link.clear();
|
|
|
|
nNodes = 0;
|
|
m_pHeuristic = 0;
|
|
m_nTagged = 0;
|
|
m_pPathBegin = 0;
|
|
m_pPathfinderCurrent = 0;
|
|
m_pWalkBackCurrent = 0;
|
|
m_bBeautifying = true;
|
|
|
|
m_pAISystem = pSystem;
|
|
m_lstTagTracker.reserve(1000);
|
|
m_lstMarkTracker.reserve(1000);
|
|
}
|
|
|
|
CGraph::~CGraph()
|
|
{
|
|
m_vNodes.clear();
|
|
DeleteGraph(m_pSafeFirst,0);
|
|
char str[255];
|
|
sprintf(str,"Released %d nodes\n",nNodes);
|
|
OutputDebugString(str);
|
|
m_mapEntrances.clear();
|
|
if (m_pHeuristic)
|
|
{
|
|
delete m_pHeuristic;
|
|
m_pHeuristic = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
GraphNode *CGraph::GetCurrent()
|
|
{
|
|
return m_pCurrent;
|
|
}
|
|
|
|
|
|
GraphNode *CGraph::GetEnclosing(const Vec3d &pos,GraphNode *pNode, bool bOutdoorOnly)
|
|
{
|
|
|
|
if (bOutdoorOnly)
|
|
{
|
|
if (!pNode)
|
|
DebugWalk(m_pFirst,pos);
|
|
else
|
|
DebugWalk(pNode,pos);
|
|
|
|
return m_pCurrent;
|
|
}
|
|
|
|
IVisArea *pGoalArea;
|
|
int nGoalBuilding;
|
|
if (!m_pAISystem->CheckInside(pos,nGoalBuilding,pGoalArea))
|
|
{
|
|
nGoalBuilding = -1;
|
|
pGoalArea = NULL;
|
|
}
|
|
|
|
if (nGoalBuilding<0)
|
|
{
|
|
if (!pNode)
|
|
DebugWalk(m_pFirst,pos);
|
|
else
|
|
DebugWalk(pNode,pos);
|
|
|
|
m_pFirst = m_pCurrent;
|
|
return m_pCurrent;
|
|
}
|
|
else
|
|
{
|
|
GraphNode *pEntrance = GetEntrance(nGoalBuilding,pos);
|
|
if (!pEntrance)
|
|
{
|
|
if (!pGoalArea)
|
|
AIWarning("No entrance for navigation area nr %d. The position is (%.3f,%.3f,%.3f)",nGoalBuilding,pos.x,pos.y,pos.z);
|
|
else
|
|
AIWarning("No entrance into some indoors or some visareas not connected trough portals (NR:%d).(%3f,%3f,%3f)",nGoalBuilding,pos.x,pos.y,pos.z);
|
|
}
|
|
else
|
|
{
|
|
if (pGoalArea)
|
|
IndoorDebugWalk(pEntrance,pos,pGoalArea);
|
|
else
|
|
IndoorDebugWalk(pEntrance,pos);
|
|
}
|
|
}
|
|
|
|
return m_pCurrent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGraph::Connect(GraphNode *one, GraphNode *two)
|
|
{
|
|
|
|
if (one==two)
|
|
return;
|
|
|
|
if (!one || !two)
|
|
return;
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=one->link.begin();vi!=one->link.end();vi++)
|
|
{
|
|
if ( (*vi).pLink == two)
|
|
return;
|
|
}
|
|
|
|
GraphLink NewLink;
|
|
NewLink.pLink = two;
|
|
if ((one==m_pSafeFirst) || (two==m_pSafeFirst))
|
|
NewLink.fMaxRadius = -1.f; // do not EVER go to the safe first node while tracing
|
|
else
|
|
NewLink.fMaxRadius = 100.f; // default big value
|
|
// if (one->link.size()==3)
|
|
// int a=5;
|
|
one->link.push_back(NewLink);
|
|
two->AddRef();
|
|
|
|
// connect two to one
|
|
Connect(two,one);
|
|
|
|
if (m_pCurrent == m_pSafeFirst)
|
|
{
|
|
// connect dummy first node to graph
|
|
if (one->nBuildingID==-1)
|
|
{
|
|
Connect(m_pSafeFirst,one);
|
|
m_pCurrent=one;
|
|
m_pFirst = m_pCurrent;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CGraph::WriteToFile(const char *pname)
|
|
{
|
|
#ifdef __MWERKS__
|
|
#warning Code not implemented under CodeWarrior
|
|
#else
|
|
|
|
m_vLinks.clear();
|
|
m_vLinksDesc.clear();
|
|
m_vBuffer.clear();
|
|
m_lstSaveStack.clear();
|
|
|
|
m_vBuffer.reserve(100000);
|
|
|
|
GraphNode *pNext;
|
|
GraphNode *pCurrent = m_pSafeFirst;
|
|
while (pNext = WriteLine(pCurrent)) pCurrent=pNext;
|
|
|
|
CCryFile file;
|
|
if( false != file.Open( pname, "wb" ) )
|
|
{
|
|
int iNumber = m_vBuffer.size();
|
|
int nFileVersion = BAI_FILE_VERSION;
|
|
int nRandomNr = 0;
|
|
|
|
file.Write( &nFileVersion, sizeof( int ) );
|
|
file.Write( &m_vBBoxMin.x, sizeof( float ) );
|
|
file.Write( &m_vBBoxMin.y, sizeof( float ) );
|
|
file.Write( &m_vBBoxMin.z, sizeof( float ) );
|
|
file.Write( &m_vBBoxMax.x, sizeof( float ) );
|
|
file.Write( &m_vBBoxMax.y, sizeof( float ) );
|
|
file.Write( &m_vBBoxMax.z, sizeof( float ) );
|
|
|
|
// write the triangle descriptors
|
|
file.Write( &iNumber, sizeof( int ) );
|
|
file.Write( &m_vBuffer[ 0 ], iNumber * sizeof( NodeDescriptor ) );
|
|
//iNumber = m_vLinks.size();
|
|
//WriteFile(hf,&iNumber,sizeof(int),&written,NULL);
|
|
//WriteFile(hf,&m_vLinks[0],iNumber*sizeof(int),&written,NULL);
|
|
|
|
m_pAISystem->m_VertexList.WriteToFile( file );
|
|
|
|
iNumber = m_vLinksDesc.size();
|
|
file.Write( &iNumber, sizeof( int ) );
|
|
if (iNumber>0)
|
|
file.Write( &m_vLinksDesc[ 0 ], iNumber * sizeof( LinkDescriptor ) );
|
|
|
|
file.Close();
|
|
|
|
m_vLinks.clear();
|
|
m_vLinksDesc.clear();
|
|
m_vNodes.clear();
|
|
m_vBuffer.clear();
|
|
//m_mapEntrances.clear();
|
|
ClearMarks();
|
|
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifdef __MWERKS__
|
|
#warning Code not implemented under CodeWarrior
|
|
#else
|
|
|
|
|
|
GraphNode *CGraph::WriteLine( GraphNode *pNode)
|
|
{
|
|
// write the id of the node
|
|
NodeDescriptor desc;
|
|
desc.id = (INT_PTR) pNode; //AMD Port
|
|
if (pNode == m_pSafeFirst) desc.id=1;
|
|
|
|
///desc.pArea = pNode->pArea;
|
|
desc.building = pNode->nBuildingID;
|
|
|
|
desc.data = pNode->data;
|
|
desc.bEntrance = false;
|
|
desc.bExit = false;
|
|
desc.bCreated = pNode->bCreated;
|
|
// check if this node is an entrance;
|
|
EntranceMap::iterator ei;
|
|
ei = m_mapEntrances.find(desc.building);
|
|
if (ei!=m_mapEntrances.end())
|
|
{
|
|
while ( (ei!=m_mapEntrances.end()) && (ei->first == desc.building))
|
|
{
|
|
if ( (ei->second) == pNode )
|
|
desc.bEntrance = true;
|
|
++ei;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ei=m_mapExits.find(desc.building);
|
|
if (ei!=m_mapExits.end())
|
|
{
|
|
while ( (ei!=m_mapExits.end()) && (ei->first == desc.building))
|
|
{
|
|
if ( (ei->second) == pNode )
|
|
desc.bExit = true;
|
|
++ei;
|
|
}
|
|
}
|
|
}
|
|
//desc.bEntrance = (m_mapEntrances.find(desc.building) != m_mapEntrances.end());
|
|
|
|
MarkNode(pNode);
|
|
|
|
if (!pNode->vertex.empty())
|
|
{
|
|
desc.nObstacles = pNode->vertex.size();
|
|
if (desc.nObstacles>10)
|
|
desc.nObstacles=10;
|
|
|
|
int i=0;
|
|
ObstacleIndexVector::iterator li;
|
|
for (li=pNode->vertex.begin();li!=pNode->vertex.end() && i<10 ;li++,i++)
|
|
{
|
|
desc.obstacle[i] = (*li);
|
|
//desc.obstacle[i].vPos = (*li).vPos;
|
|
//desc.obstacle[i].vDir = (*li).vDir;
|
|
}
|
|
|
|
if (i==10)
|
|
m_pAISystem->m_pSystem->GetILog()->Log("\003FOUND INDOOR WAYPOINT WITH MORE THAN 10 HIDEPOINTS LINKED TO IT. SURPLUS HIDEPOINTS IGNORED");
|
|
}
|
|
else
|
|
desc.nObstacles = 0;
|
|
|
|
m_vBuffer.push_back(desc);
|
|
|
|
|
|
// now write the links
|
|
// first size
|
|
/* int size = pNode->link.size();
|
|
|
|
m_vLinks.push_back(desc.id);
|
|
m_vLinks.push_back(size);
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pNode->link.begin();vi!=pNode->link.end();vi++)
|
|
{
|
|
GraphNode *pToPush = (*vi).pLink;
|
|
if (pToPush == m_pSafeFirst)
|
|
m_vLinks.push_back(1);
|
|
else
|
|
m_vLinks.push_back((int) pToPush);
|
|
|
|
m_lstSaveStack.remove(pToPush);
|
|
|
|
if (pToPush->mark)
|
|
continue;
|
|
|
|
|
|
m_lstSaveStack.push_back(pToPush);
|
|
}*/
|
|
|
|
|
|
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pNode->link.begin();vi!=pNode->link.end();vi++)
|
|
{
|
|
GraphNode *pToPush = (*vi).pLink;
|
|
|
|
LinkDescriptor ld;
|
|
ld.nSourceNode = desc.id;
|
|
if (pToPush == m_pSafeFirst)
|
|
ld.nTargetNode = 1;
|
|
else
|
|
ld.nTargetNode = (INT_PTR) pToPush;
|
|
ld.fMaxPassRadius = (*vi).fMaxRadius;
|
|
ld.nStartIndex = (*vi).nStartIndex;
|
|
ld.nEndIndex = (*vi).nEndIndex;
|
|
ld.vEdgeCenter = (*vi).vEdgeCenter;
|
|
ld.vWayOut = (*vi).vWayOut;
|
|
|
|
m_vLinksDesc.push_back(ld);
|
|
|
|
if (pToPush->mark)
|
|
{
|
|
m_lstSaveStack.remove(pToPush);
|
|
continue;
|
|
}
|
|
|
|
if (std::find(m_lstSaveStack.begin(),m_lstSaveStack.end(),pToPush) == m_lstSaveStack.end())
|
|
m_lstSaveStack.push_back(pToPush);
|
|
}
|
|
|
|
// Disconnect(pNode);
|
|
// if (pNode == m_pFirst)
|
|
// delete pNode;
|
|
|
|
if (m_lstSaveStack.empty())
|
|
return NULL;
|
|
else
|
|
{
|
|
GraphNode *pNext;
|
|
while (!m_lstSaveStack.empty())
|
|
{
|
|
pNext = m_lstSaveStack.front();
|
|
m_lstSaveStack.pop_front();
|
|
|
|
if (!pNext->mark)
|
|
break;
|
|
}
|
|
|
|
if (pNext->mark)
|
|
return NULL;
|
|
else
|
|
return pNext;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
void CGraph::DebugWalk(GraphNode *pNode, const Vec3d &pos)
|
|
{
|
|
// get out of indoor
|
|
if (pNode->nBuildingID>=0)
|
|
{
|
|
pNode=GetEntrance(pNode->nBuildingID,pos);
|
|
VectorOfLinks::iterator vli,iend = pNode->link.end();
|
|
for (vli=pNode->link.begin();vli!=iend;++vli)
|
|
if ((*vli).pLink->nBuildingID<0)
|
|
{
|
|
pNode = (*vli).pLink;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClearMarks();
|
|
GraphNode *pNextNode = pNode;
|
|
GraphNode *pPrevNode = 0;
|
|
int iterations=0;
|
|
while (pPrevNode != pNextNode)
|
|
{
|
|
iterations++;
|
|
pPrevNode = pNextNode;
|
|
pNextNode = GREEDYStep(pPrevNode,pos);
|
|
}
|
|
|
|
m_pAISystem->f7+=iterations;
|
|
m_pAISystem->f7/=2.f;
|
|
|
|
m_pCurrent = pNextNode; // or pPrevNode, they are the same
|
|
ClearMarks();
|
|
m_mapGreedyWalkCandidates.clear();
|
|
|
|
if (!m_pCurrent)
|
|
CryError("[AIERROR] located in NULL graph node... Try regenerating triangulation, or submit a bug report.");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGraph::DrawPath(IRenderer *pRenderer)
|
|
{
|
|
|
|
|
|
|
|
//if (m_lstVisited.empty()) return;
|
|
|
|
ListNodes::iterator i;
|
|
CandidateMap::iterator ci;
|
|
pRenderer->SetMaterialColor(1,0,0,1);
|
|
//ci=m_lstVisited.begin();
|
|
GraphNode *pStart = (ci->second);
|
|
// for (ci++;ci!=m_lstVisited.end();ci++)
|
|
{
|
|
GraphNode *pEnd = (ci->second);
|
|
|
|
Vec3d stpos = pStart->data.m_pos;
|
|
Vec3d endpos = pEnd->data.m_pos;
|
|
stpos.z+=1;
|
|
endpos.z+=1;
|
|
|
|
pRenderer->DrawLine(stpos, endpos );
|
|
|
|
pStart = pEnd;
|
|
|
|
}
|
|
|
|
if (m_lstPath.empty()) return;
|
|
|
|
|
|
Vec3d vStartPos;
|
|
ListPositions::iterator pi;
|
|
pRenderer->ResetToDefault();
|
|
pi=m_lstPath.begin();
|
|
vStartPos = (*pi);
|
|
for (pi++;pi!=m_lstPath.end();pi++)
|
|
{
|
|
//GraphNode *pEnd = (*i);
|
|
Vec3d vEndPos = (*pi);
|
|
|
|
// Vec3d stpos = pStart->data.m_pos;
|
|
// Vec3d endpos = pEnd->data.m_pos;
|
|
vStartPos.z+=1;
|
|
vEndPos.z+=1;
|
|
|
|
pRenderer->DrawLine(vStartPos, vEndPos );
|
|
|
|
vStartPos = vEndPos;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CGraph::GetFieldCenter(Vec3d &pos)
|
|
{
|
|
pos = m_pCurrent->data.m_pos;
|
|
}
|
|
|
|
|
|
void CGraph::DEBUG_DrawCenters(GraphNode *pNode, IRenderer *pRenderer, int dist)
|
|
{
|
|
return;
|
|
|
|
if (dist > 10 ) return;
|
|
//if (pNode->bDebug) return;
|
|
|
|
//pNode->bDebug = true;
|
|
pRenderer->ResetToDefault();
|
|
if (pNode->data.bWater)
|
|
pRenderer->SetMaterialColor(0,0,1.0,1.0);
|
|
else
|
|
pRenderer->SetMaterialColor(pNode->data.fSlope,(float)(1.0-pNode->data.fSlope),(float) (1.0-pNode->data.fSlope),1.0);
|
|
|
|
pRenderer->DrawBall(pNode->data.m_pos,1.5f);
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pNode->link.begin();vi!=pNode->link.end();vi++)
|
|
{
|
|
Vec3d start, end;
|
|
start = pNode->data.m_pos;
|
|
start.x+=0.5f;
|
|
start.y+=0.5f;
|
|
end = (*vi).pLink->data.m_pos;
|
|
end.y+=0.5f;
|
|
end.x+=0.5f;
|
|
pRenderer->DrawLine(start, end);
|
|
DEBUG_DrawCenters((*vi).pLink,pRenderer,dist+1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int CGraph::WalkAStar(GraphNode *pBegin, GraphNode *pEnd, int &nIterations)
|
|
{
|
|
m_lstCurrentHistory.clear();
|
|
ClearPath(); // clear the previously generated path
|
|
// m_lstVisited.clear();
|
|
if (!ClearTags()) // clear all the tags in the diagram
|
|
return PATHFINDER_CLEANING_GRAPH;
|
|
|
|
|
|
if ((!pBegin) || (!pEnd)) return PATHFINDER_NOPATH;
|
|
|
|
// lets check if last generated path was similar to this one
|
|
if (!m_lstLastPath.empty())
|
|
{
|
|
if (m_lstLastPath.back() == pEnd)
|
|
{
|
|
if (CanReuseLastPath(pBegin))
|
|
return PATHFINDER_BEAUTIFYINGPATH;
|
|
}
|
|
}
|
|
|
|
|
|
m_pPathfinderCurrent = pBegin;
|
|
m_pPathBegin = pBegin;
|
|
|
|
m_nAStarDistance = 0;
|
|
m_pPathfinderCurrent->fDistance = 0;
|
|
|
|
//m_fDistance = (pBegin->data.m_pos - pEnd->data.m_pos).GetLength();
|
|
m_mapCandidates.clear();
|
|
m_pPathfinderCurrent = ASTARStep(pBegin, pEnd);
|
|
while (m_pPathfinderCurrent && !m_mapCandidates.empty() && (m_pPathfinderCurrent != pEnd) && (nIterations--))
|
|
m_pPathfinderCurrent = ASTARStep(m_pPathfinderCurrent, pEnd);
|
|
|
|
if (!m_pPathfinderCurrent)
|
|
return PATHFINDER_NOPATH;
|
|
if (m_pPathfinderCurrent == pEnd)
|
|
return PATHFINDER_WALKINGBACK;
|
|
|
|
return PATHFINDER_STILLTRACING;
|
|
|
|
}
|
|
|
|
GraphNode * CGraph::ASTARStep(GraphNode *pBegin, GraphNode *pEnd)
|
|
{
|
|
TagNode(pBegin);
|
|
if (pEnd == pBegin)
|
|
{
|
|
pEnd->fHeuristic = 10000 - (float)m_nAStarDistance;
|
|
return pEnd; // reached the end
|
|
}
|
|
|
|
// this piece evaluates simple distance heuristic for backtracking----------
|
|
///float thisdist = (pBegin->data.m_pos - m_pPathBegin->data.m_pos).GetLength();
|
|
//pBegin->fHeuristic = 1.f - (thisdist / m_fDistance);
|
|
//--------------------------------------------------------------------------
|
|
pBegin->fHeuristic = 10000 - (float)m_nAStarDistance;
|
|
m_nAStarDistance++;
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pBegin->link.begin();vi!=pBegin->link.end();vi++)
|
|
{
|
|
// if ((*vi).fMaxRadius >= 1.f)
|
|
if ((*vi).fMaxRadius >= m_pRequester->m_fPassRadius)
|
|
{
|
|
EvaluateNode( (*vi).pLink, pEnd, pBegin);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_mapCandidates.empty())
|
|
{
|
|
CandidateHistoryMap::reverse_iterator ci = m_mapCandidates.rbegin();
|
|
|
|
GraphNode *pNextNode = 0;
|
|
if (ci!=m_mapCandidates.rend())
|
|
{
|
|
pNextNode = (ci->second).pNode;
|
|
|
|
while ((pNextNode == pBegin)||(pNextNode->tag))
|
|
{
|
|
m_mapCandidates.erase((++ci).base());
|
|
ci = m_mapCandidates.rbegin();
|
|
if (ci==m_mapCandidates.rend())
|
|
break;
|
|
pNextNode = (ci->second).pNode;
|
|
}
|
|
}
|
|
|
|
if (ci==m_mapCandidates.rend())
|
|
return 0;
|
|
|
|
m_lstCurrentHistory = (ci->second).lstParents;
|
|
float f = ci->first;
|
|
m_mapCandidates.erase((++ci).base());
|
|
if (GetAISystem()->m_cvDrawPath->GetIVal()==2)
|
|
m_lstVisited.insert(CandidateMap::iterator::value_type(f,pNextNode));
|
|
return pNextNode;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
|
|
void CGraph::TagNode(GraphNode *pNode)
|
|
{
|
|
pNode->tag = true;
|
|
m_lstTagTracker.push_back(pNode);
|
|
}
|
|
|
|
bool CGraph::ClearTags()
|
|
{
|
|
|
|
int nIterations = PATHFINDER_ITERATIONS;
|
|
|
|
while (!m_lstTagTracker.empty() && (nIterations--))
|
|
{
|
|
GraphNode *pLastNode = m_lstTagTracker.back();
|
|
pLastNode->tag = false;
|
|
pLastNode->fHeuristic = -9999.f;
|
|
m_lstTagTracker.pop_back();
|
|
}
|
|
|
|
if (!m_lstTagTracker.empty())
|
|
return false; // we are still cleaning up
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGraph::EvaluateNode(GraphNode *pNode,GraphNode *pEnd, GraphNode *pParent)
|
|
{
|
|
if (!pNode) return;
|
|
if (pNode->tag) return;
|
|
float desirability=0;
|
|
float thisdist = (pNode->data.m_pos - m_vRealPathfinderEnd).GetLength();
|
|
|
|
desirability = 1.f - (thisdist / m_fDistance) * 0.5f;
|
|
desirability += m_pHeuristic->Estimate(pNode, this) * 0.5f;
|
|
|
|
NodeWithHistory nwh;
|
|
nwh.pNode = pNode;
|
|
if (m_lstCurrentHistory.size() > 1 )
|
|
m_lstCurrentHistory.pop_back();
|
|
m_lstCurrentHistory.push_front(pParent);
|
|
nwh.lstParents = m_lstCurrentHistory;
|
|
m_mapCandidates.insert(CandidateHistoryMap::iterator::value_type(desirability,nwh));
|
|
}
|
|
|
|
int CGraph::ContinueAStar(GraphNode *pEnd, int &nIterations)
|
|
{
|
|
if (!pEnd) return PATHFINDER_NOPATH;
|
|
if (!m_pHeuristic) return PATHFINDER_NOPATH;
|
|
|
|
//int nIterations = PATHFINDER_ITERATIONS;
|
|
|
|
m_pPathfinderCurrent = ASTARStep(m_pPathfinderCurrent, pEnd);
|
|
while (!m_mapCandidates.empty() && (m_pPathfinderCurrent != pEnd) && (nIterations--))
|
|
m_pPathfinderCurrent = ASTARStep(m_pPathfinderCurrent, pEnd);
|
|
|
|
if (!m_pPathfinderCurrent)
|
|
return PATHFINDER_NOPATH;
|
|
if (m_pPathfinderCurrent == pEnd)
|
|
return PATHFINDER_WALKINGBACK;
|
|
|
|
return PATHFINDER_STILLTRACING;
|
|
}
|
|
|
|
int CGraph::WalkBack(GraphNode *pBegin, GraphNode *pEnd, int &nIterations)
|
|
{
|
|
//int nIterations = PATHFINDER_ITERATIONS;
|
|
//GraphNode *pCurrent;
|
|
if (!m_pWalkBackCurrent)
|
|
{
|
|
m_lstNodeStack.clear();
|
|
m_lstPath.clear();
|
|
m_pWalkBackCurrent = pBegin;
|
|
}
|
|
//else
|
|
//m_pWalkBackCurrent = m_pWalkBackCurrent;
|
|
|
|
float maxHeur = m_pWalkBackCurrent->fHeuristic;
|
|
while (m_pWalkBackCurrent!=pEnd && --nIterations)
|
|
{
|
|
m_lstPath.push_front(m_pWalkBackCurrent->data.m_pos); // push in path
|
|
m_lstNodeStack.push_front(m_pWalkBackCurrent); // push in nodestack
|
|
|
|
GraphNode *pNext = 0;
|
|
float maxheur = m_pWalkBackCurrent->fHeuristic;
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=m_pWalkBackCurrent->link.begin(); vi!=m_pWalkBackCurrent->link.end(); vi++)
|
|
{
|
|
GraphNode *pLink = (*vi).pLink;
|
|
if (pLink->fHeuristic > maxheur && (*vi).fMaxRadius>=1.f)
|
|
{
|
|
maxheur = pLink->fHeuristic;
|
|
pNext = pLink;
|
|
}
|
|
}
|
|
|
|
m_pWalkBackCurrent->fHeuristic = -9999.f;
|
|
|
|
if (pNext)
|
|
m_pWalkBackCurrent = pNext;
|
|
else
|
|
{
|
|
// dead end hit... retrace
|
|
// try to continue moving with a revised heuristic
|
|
for (vi=m_pWalkBackCurrent->link.begin(); vi!=m_pWalkBackCurrent->link.end(); vi++)
|
|
{
|
|
GraphNode *pLink = (*vi).pLink;
|
|
if (pLink->fHeuristic > m_pWalkBackCurrent->fHeuristic)
|
|
{
|
|
maxheur = pLink->fHeuristic;
|
|
pNext = pLink;
|
|
}
|
|
}
|
|
|
|
if (pNext)
|
|
m_pWalkBackCurrent = pNext;
|
|
else
|
|
{
|
|
if (!m_lstPath.empty())
|
|
m_lstPath.pop_front();
|
|
if (!m_lstNodeStack.empty())
|
|
m_lstNodeStack.pop_front();
|
|
if (m_lstNodeStack.empty())
|
|
return PATHFINDER_NOPATH;;
|
|
m_pWalkBackCurrent = m_lstNodeStack.front();
|
|
if (!m_lstNodeStack.empty())
|
|
m_lstNodeStack.pop_front();
|
|
if (!m_lstPath.empty())
|
|
m_lstPath.pop_front();
|
|
}
|
|
|
|
}
|
|
|
|
// if (std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pCurrent) != m_lstNodeStack.end())
|
|
// DEBUG_BREAK;
|
|
}
|
|
|
|
if (m_pWalkBackCurrent == pEnd)
|
|
{
|
|
m_pWalkBackCurrent = 0;
|
|
m_mapGreedyWalkCandidates.clear();
|
|
if (std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pEnd)==m_lstNodeStack.end())
|
|
{
|
|
m_lstNodeStack.push_front(pEnd);
|
|
m_lstPath.push_front(pEnd->data.m_pos);
|
|
}
|
|
m_lstLastPath.clear();
|
|
m_lstLastPath.insert(m_lstLastPath.begin(),m_lstNodeStack.begin(),m_lstNodeStack.end());
|
|
return PATHFINDER_BEAUTIFYINGPATH;
|
|
}
|
|
else
|
|
return PATHFINDER_WALKINGBACK;
|
|
}
|
|
|
|
|
|
|
|
void CGraph::ClearPath()
|
|
{
|
|
m_lstPath.clear();
|
|
}
|
|
|
|
void CGraph::DeleteGraph(GraphNode *pNode, int depth)
|
|
{
|
|
|
|
VectorOfLinks::iterator vi;
|
|
|
|
m_lstDeleteStack.push_front(pNode);
|
|
|
|
while (!m_lstDeleteStack.empty())
|
|
{
|
|
GraphNode *pCurrentNode = m_lstDeleteStack.front();
|
|
m_lstDeleteStack.pop_front();
|
|
|
|
// put all links into the node stack
|
|
for (vi=pCurrentNode->link.begin();vi!=pCurrentNode->link.end();vi++)
|
|
{
|
|
GraphNode *pLink =(*vi).pLink;
|
|
if (std::find(m_lstDeleteStack.begin(),m_lstDeleteStack.end(),pLink) == m_lstDeleteStack.end())
|
|
m_lstDeleteStack.push_front(pLink);
|
|
}
|
|
|
|
Disconnect(pCurrentNode);
|
|
/*if (!pCurrentNode->link.empty())
|
|
{
|
|
// delink this node from all his adjacent nodes
|
|
for (vi=pCurrentNode->link.begin();vi!=pCurrentNode->link.end();vi++)
|
|
{
|
|
GraphNode *pLink = (*vi);
|
|
if ((found = std::find(pLink->link.begin(),pLink->link.end(),pCurrentNode)) != pLink->link.end())
|
|
{
|
|
(*found)->Release();
|
|
pLink->link.erase(found);
|
|
}
|
|
}
|
|
|
|
|
|
GraphNode *pNext= pCurrentNode->link.back();
|
|
m_lstNodeStack.push_front(pNext);
|
|
pCurrentNode->link.pop_back();
|
|
}
|
|
else
|
|
{
|
|
if (pCurrentNode != m_pFirst)
|
|
DeleteNode(pCurrentNode);
|
|
else
|
|
delete m_pFirst;
|
|
//if (pCurrentNode->Release())
|
|
//{
|
|
// delete pCurrentNode;
|
|
// nNodes++;
|
|
//}
|
|
m_lstNodeStack.pop_front();
|
|
}
|
|
*/
|
|
if (pCurrentNode == m_pSafeFirst)
|
|
m_pSafeFirst = 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CGraph::ClearDebugFlag(GraphNode *pNode)
|
|
{
|
|
//if (!pNode->bDebug) return;
|
|
|
|
// pNode->bDebug = false;
|
|
|
|
//VectorOfLinks::iterator vi;
|
|
//for (vi=pNode->link.begin();vi!=pNode->link.end();vi++)
|
|
// ClearDebugFlag((*vi).pLink);
|
|
}
|
|
|
|
GraphNode * CGraph::CheckClosest(GraphNode *pCurrent, const Vec3d &pos)
|
|
{
|
|
|
|
if (!pCurrent)
|
|
{
|
|
DebugWalk(m_pCurrent,pos);
|
|
return m_pCurrent;
|
|
}
|
|
else
|
|
{
|
|
float dist = (pCurrent->data.m_pos - pos).GetLength();
|
|
GraphNode *pReturnNode = pCurrent;
|
|
|
|
VectorOfLinks::iterator si;
|
|
for (si=pCurrent->link.begin();si!=pCurrent->link.end();si++)
|
|
{
|
|
GraphNode *pNode = (*si).pLink;
|
|
float cmpdist = (pNode->data.m_pos - pos).GetLength();
|
|
if (cmpdist < dist)
|
|
pReturnNode = pNode;
|
|
}
|
|
|
|
if (pReturnNode != pCurrent)
|
|
return CheckClosest(pReturnNode, pos);
|
|
else
|
|
return pReturnNode;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CGraph::Disconnect(GraphNode * pDisconnected, bool bDelete)
|
|
{
|
|
// if the node we are disconnecting is the current node, move the current
|
|
// to one of his links, or the root if it has no links
|
|
|
|
if (m_pSafeFirst)
|
|
{
|
|
if (m_pSafeFirst->link.empty())
|
|
return;
|
|
}
|
|
|
|
|
|
if (pDisconnected == m_pCurrent)
|
|
{
|
|
if (!pDisconnected->link.empty())
|
|
{
|
|
m_pCurrent = pDisconnected->link.front().pLink;
|
|
}
|
|
else
|
|
m_pCurrent = m_pSafeFirst;
|
|
}
|
|
|
|
|
|
|
|
// if its the root that is being disconnected, move it
|
|
if (m_pFirst == pDisconnected)
|
|
{
|
|
if (!pDisconnected->link.empty())
|
|
{
|
|
m_pFirst = pDisconnected->link.front().pLink;
|
|
}
|
|
else
|
|
{
|
|
if (m_pFirst!=m_pSafeFirst)
|
|
m_pFirst = m_pSafeFirst;
|
|
else
|
|
m_pFirst = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now disconnect this node from its links
|
|
if (!pDisconnected->link.empty())
|
|
{
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pDisconnected->link.begin();vi!=pDisconnected->link.end();vi++)
|
|
{
|
|
GraphNode *pLink = (*vi).pLink;
|
|
if (!pLink->link.empty())
|
|
{
|
|
VectorOfLinks::iterator li;
|
|
for (li=pLink->link.begin();li!=pLink->link.end();li++)
|
|
{
|
|
GraphNode *pBackLink = (*li).pLink;
|
|
if (pBackLink == pDisconnected)
|
|
{
|
|
pLink->link.erase(li);
|
|
pDisconnected->Release();
|
|
pLink->Release();
|
|
break;
|
|
}
|
|
} // li
|
|
}
|
|
} // vi
|
|
}
|
|
|
|
|
|
pDisconnected->link.clear();
|
|
|
|
if (bDelete)
|
|
DeleteNode(pDisconnected);
|
|
|
|
if (!m_pSafeFirst)
|
|
return;
|
|
|
|
if (pDisconnected != m_pSafeFirst && m_pSafeFirst->link.empty())
|
|
{
|
|
GraphNode *pFirst = m_pFirst;
|
|
// we have disconnected the link to the dummy safe node - relink it to any outdoor node of the graph
|
|
if (pFirst == m_pSafeFirst)
|
|
{
|
|
if (m_pCurrent == m_pSafeFirst)
|
|
{
|
|
// try any entrance
|
|
if (!m_mapEntrances.empty())
|
|
pFirst= (m_mapEntrances.begin()->second);
|
|
else
|
|
{
|
|
AIError("!Could not recover from deletion of Safe graph node. Try deleting .bai files and regenerating.");
|
|
return;
|
|
}
|
|
|
|
}
|
|
else
|
|
pFirst = m_pCurrent;
|
|
}
|
|
|
|
if (pFirst->nBuildingID==-1)
|
|
Connect(m_pSafeFirst,pFirst);
|
|
else
|
|
{
|
|
GraphNode *pEntrance = GetEntrance(pFirst->nBuildingID,Vec3d(0,0,0));
|
|
if (pEntrance)
|
|
{
|
|
VectorOfLinks::iterator vli,iend = pEntrance->link.end();
|
|
for (vli = pEntrance->link.begin();vli!=iend;++vli)
|
|
if ( (*vli).pLink->nBuildingID == -1 )
|
|
{
|
|
Connect(m_pSafeFirst,(*vli).pLink);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// walk that will always produce a result, for indoors
|
|
void CGraph::IndoorDebugWalk(GraphNode * pNode, const Vec3d & pos, IVisArea *pTargetArea)
|
|
{
|
|
|
|
if (pNode->pArea)
|
|
{
|
|
float this_dist = (pos - pNode->data.m_pos).GetLength();
|
|
m_mapGreedyWalkCandidates.insert(CandidateMap::iterator::value_type(this_dist,pNode));
|
|
}
|
|
|
|
FillGreedyMap(pNode,pos,pTargetArea,false);
|
|
|
|
|
|
if (m_mapGreedyWalkCandidates.empty())
|
|
{
|
|
AIWarning("No nodes found for this indoor or navigation modifier area apart from entrance!!");
|
|
m_pCurrent = pNode;
|
|
}
|
|
else
|
|
{
|
|
m_pCurrent = (m_mapGreedyWalkCandidates.begin())->second;
|
|
ClearMarks();
|
|
m_mapGreedyWalkCandidates.clear();
|
|
|
|
}
|
|
|
|
if (!m_pCurrent)
|
|
CryError("[AIERROR] located in NULL graph node... Try regenerating triangulation, or submit a bug report.");
|
|
}
|
|
|
|
// Clears the tags of the graph without time-slicing the operation
|
|
void CGraph::ClearTagsNow(void)
|
|
{
|
|
while (!ClearTags());
|
|
}
|
|
|
|
|
|
// Check whether a position is within a node's triangle
|
|
bool CGraph::PointInTriangle(const Vec3d & pos, GraphNode * pNode)
|
|
{
|
|
if (pNode->vertex.empty())
|
|
return false;
|
|
|
|
bool bSide=false;
|
|
// check first and last
|
|
//Vec3d edge = (pNode->vertex.back()).vPos - (pNode->vertex.front()).vPos;
|
|
Vec3d vFront = m_pAISystem->m_VertexList.GetVertex(pNode->vertex.front()).vPos;
|
|
Vec3d edge = m_pAISystem->m_VertexList.GetVertex(pNode->vertex.back()).vPos - vFront;
|
|
Vec3d test = pos - vFront;
|
|
|
|
float cross = edge.x * test.y - edge.y * test.x;
|
|
|
|
|
|
if (cross>0)
|
|
bSide = true;
|
|
|
|
for (unsigned int i=1;i<pNode->vertex.size();i++)
|
|
{
|
|
Vec3d vI = m_pAISystem->m_VertexList.GetVertex(pNode->vertex[i]).vPos;
|
|
edge = m_pAISystem->m_VertexList.GetVertex(pNode->vertex[i-1]).vPos - vI;
|
|
test = pos - vI;
|
|
cross = edge.x * test.y - edge.y * test.x;
|
|
|
|
if ((cross<0) && bSide)
|
|
return false;
|
|
if ((cross>0) && !bSide)
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// uses mark for internal graph operation without disturbing the pathfinder
|
|
void CGraph::MarkNode(GraphNode * pNode)
|
|
{
|
|
pNode->mark = true;
|
|
m_lstMarkTracker.push_back(pNode);
|
|
}
|
|
|
|
|
|
|
|
// clears the marked nodes
|
|
void CGraph::ClearMarks(bool bJustClear)
|
|
{
|
|
if (bJustClear)
|
|
{
|
|
m_lstMarkTracker.resize(0);
|
|
return;
|
|
}
|
|
|
|
while (!m_lstMarkTracker.empty())
|
|
{
|
|
m_lstMarkTracker.back()->mark = false;
|
|
m_lstMarkTracker.pop_back();
|
|
}
|
|
}
|
|
|
|
// iterative function to quickly converge on the target position in the graph
|
|
GraphNode * CGraph::GREEDYStep(GraphNode * pBegin, const Vec3d & pos, bool bIndoor)
|
|
{
|
|
|
|
MarkNode(pBegin);
|
|
|
|
if (bIndoor)
|
|
{
|
|
//indoor
|
|
|
|
float d_dist = (pos - pBegin->data.m_pos).GetLength();
|
|
if (d_dist < 1.f)
|
|
return pBegin;
|
|
|
|
VectorOfLinks::iterator vi;
|
|
if (pBegin->link.empty())
|
|
return pBegin;
|
|
for (vi=pBegin->link.begin();vi!=pBegin->link.end();vi++)
|
|
{
|
|
GraphNode *pLink = (*vi).pLink;
|
|
if ((pLink->mark) || (pLink->nBuildingID==-1)) // don't go outside
|
|
continue;
|
|
float this_dist = (pos - pLink->data.m_pos).GetLength();
|
|
m_mapGreedyWalkCandidates.insert(CandidateMap::iterator::value_type(this_dist,pLink));
|
|
}
|
|
|
|
if (!m_mapGreedyWalkCandidates.empty())
|
|
{
|
|
CandidateMap::iterator ci = m_mapGreedyWalkCandidates.end();
|
|
ci--;
|
|
GraphNode *pNextNode = (ci->second);
|
|
if (pNextNode == pBegin)
|
|
{
|
|
ci--;
|
|
pNextNode = (ci->second);
|
|
}
|
|
m_mapGreedyWalkCandidates.erase(ci);
|
|
return pNextNode;
|
|
}
|
|
else
|
|
{
|
|
return pBegin;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// outdoor
|
|
if (PointInTriangle(pos,pBegin))
|
|
return pBegin; // we have arrived
|
|
|
|
if (pBegin == m_pSafeFirst)
|
|
{
|
|
if (pBegin->link.empty())
|
|
return pBegin;
|
|
m_mapGreedyWalkCandidates.insert(CandidateMap::iterator::value_type(0,pBegin->link.front().pLink));
|
|
}
|
|
else
|
|
{
|
|
VectorOfLinks::iterator vi;
|
|
if (pBegin!=m_pSafeFirst || !pBegin->mark)
|
|
{
|
|
for (vi=pBegin->link.begin();vi!=pBegin->link.end();vi++)
|
|
{
|
|
GraphNode *pLink = (*vi).pLink;
|
|
if (pLink->mark || (pLink->nBuildingID!=-1)) // dont go in marked or indoors
|
|
continue;
|
|
// float this_dist = (pos - pLink->data.m_pos).GetLength();
|
|
if (pBegin->vertex.empty())
|
|
continue;
|
|
|
|
Vec3d midpoint = (m_pAISystem->m_VertexList.GetVertex(pBegin->vertex[(*vi).nStartIndex]).vPos + m_pAISystem->m_VertexList.GetVertex(pBegin->vertex[(*vi).nEndIndex]).vPos)/2.f;
|
|
midpoint.z = pos.z;
|
|
Vec3d dir1 = pos-midpoint;
|
|
Vec3d vWayOut = (*vi).vWayOut;
|
|
vWayOut.z = 0.f;//pos.z;
|
|
dir1.Normalize();
|
|
|
|
float this_dist = (1.f-dir1.Dot(vWayOut));
|
|
this_dist*= (pLink->data.m_pos - pos).GetLength()/30.f;
|
|
|
|
m_mapGreedyWalkCandidates.insert(CandidateMap::iterator::value_type(this_dist,pLink));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_mapGreedyWalkCandidates.empty())
|
|
{
|
|
|
|
GraphNode *pNextNode;
|
|
do {
|
|
if (m_mapGreedyWalkCandidates.empty())
|
|
{
|
|
AIWarning("Could not locate position (%.3f,%.3f,%.3f) in triangulation. Call Petar!",pos.x,pos.y,pos.z);
|
|
return pBegin;
|
|
}
|
|
pNextNode= (m_mapGreedyWalkCandidates.begin()->second);
|
|
m_mapGreedyWalkCandidates.erase(m_mapGreedyWalkCandidates.begin());
|
|
|
|
} while (pNextNode == pBegin);
|
|
|
|
return pNextNode;
|
|
|
|
}
|
|
else
|
|
return pBegin;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
// adds an entrance for easy traversing later
|
|
void CGraph::AddIndoorEntrance(int nBuildingID, GraphNode* pNode, bool bExitOnly)
|
|
{
|
|
|
|
if (m_pSafeFirst)
|
|
{
|
|
if (m_pSafeFirst->link.empty())
|
|
return;
|
|
}
|
|
|
|
if (bExitOnly)
|
|
m_mapExits.insert(EntranceMap::iterator::value_type(nBuildingID,pNode));
|
|
else
|
|
m_mapEntrances.insert(EntranceMap::iterator::value_type(nBuildingID,pNode));
|
|
|
|
if (pNode->link.empty())
|
|
{
|
|
// it has to be connected to the outside
|
|
GraphNode *pOutsideNode = GetEnclosing(pNode->data.m_pos,0,true);
|
|
Connect(pNode,pOutsideNode);
|
|
}
|
|
else
|
|
{
|
|
VectorOfLinks::iterator gi;
|
|
bool bOutsideLink = false;
|
|
for (gi=pNode->link.begin();gi!=pNode->link.end();gi++)
|
|
{
|
|
if ( (*gi).pLink->nBuildingID == -1 )
|
|
bOutsideLink = true;
|
|
}
|
|
|
|
if (!bOutsideLink)
|
|
{
|
|
// it has to be connected to the outside
|
|
GraphNode *pOutsideNode = GetEnclosing(pNode->data.m_pos,0,true);
|
|
Connect(pNode,pOutsideNode);
|
|
}
|
|
|
|
}
|
|
|
|
if (bExitOnly)
|
|
{
|
|
VectorOfLinks::iterator gi;
|
|
for (gi=pNode->link.begin();gi!=pNode->link.end();gi++)
|
|
{
|
|
if ( (*gi).pLink->nBuildingID == -1 )
|
|
{
|
|
GraphNode *pOutNode = (*gi).pLink;
|
|
VectorOfLinks::iterator ii;
|
|
for (ii=pOutNode->link.begin();ii!=pOutNode->link.end();ii++)
|
|
{
|
|
if ( (*ii).pLink == pNode)
|
|
(*ii).fMaxRadius = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Reads the AI graph from a specified file
|
|
bool CGraph::ReadFromFile(const char * szName)
|
|
{
|
|
#ifdef __MWERKS__
|
|
#warning Code not implemented under CodeWarrior
|
|
#else
|
|
|
|
CCryFile file;;
|
|
if (file.Open( szName,"rb"))
|
|
{
|
|
ReadNodes( file );
|
|
return true;
|
|
}
|
|
//[Timur]
|
|
/*
|
|
|
|
HANDLE hf;
|
|
hf = CreateFile(szName,GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
|
|
|
|
if (hf != INVALID_HANDLE_VALUE)
|
|
{
|
|
ReadNodes(hf);
|
|
CloseHandle(hf);
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
return false;
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifdef __MWERKS__
|
|
#warning Code not implemented under CodeWarrior
|
|
#else
|
|
// reads all the nodes in a map
|
|
bool CGraph::ReadNodes( CCryFile &file )
|
|
{
|
|
//assert(_heapchk()==_HEAPOK);
|
|
|
|
//DWORD read;
|
|
int iNumber;
|
|
Vec3d mins,maxs;
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Verifying BAI file version");
|
|
file.Read(&iNumber, sizeof(int) );
|
|
if (iNumber != BAI_FILE_VERSION)
|
|
{
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\002$3[AISYSTEM WARNING] Wrong BAI file version!! Delete them and regenerate them in the editor.");
|
|
return false;
|
|
}
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Reading BBOX");
|
|
file.Read( &mins.x, sizeof(float) );
|
|
file.Read( &mins.y, sizeof(float) );
|
|
file.Read( &mins.z, sizeof(float) );
|
|
|
|
file.Read( &maxs.x, sizeof(float) );
|
|
file.Read( &maxs.y, sizeof(float) );
|
|
file.Read( &maxs.z, sizeof(float) );
|
|
|
|
SetBBox(mins,maxs);
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Reading node descriptors");
|
|
|
|
file.Read( &iNumber, sizeof(int) );
|
|
|
|
if (iNumber>0)
|
|
{
|
|
m_vBuffer.resize(iNumber);
|
|
file.Read( &m_vBuffer[0], iNumber*sizeof(NodeDescriptor) );
|
|
}
|
|
|
|
m_vNodes.clear();
|
|
// m_vNodes.resize(iNumber);
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Creating graph nodes");
|
|
|
|
I3DEngine *pEngine = m_pAISystem->m_pSystem->GetI3DEngine();
|
|
NodeBuffer::iterator ni;
|
|
int index=0;
|
|
for (ni=m_vBuffer.begin();ni!=m_vBuffer.end();ni++,index++)
|
|
{
|
|
NodeDescriptor buffer = (*ni);
|
|
GraphNode *pNode = CreateNewNode(!buffer.bCreated);//new GraphNode;//&m_vNodes[index];
|
|
|
|
if (buffer.bCreated)
|
|
m_pAISystem->CheckInside(buffer.data.m_pos,pNode->nBuildingID,pNode->pArea);
|
|
else
|
|
{
|
|
pNode->nBuildingID=-1;
|
|
pNode->pArea = 0;
|
|
}
|
|
|
|
pNode->data = buffer.data;
|
|
pNode->vertex.clear();
|
|
if (buffer.nObstacles)
|
|
{
|
|
pNode->vertex.reserve(buffer.nObstacles);
|
|
for (int i=0;i<buffer.nObstacles;i++)
|
|
{
|
|
int nVertexIndex = buffer.obstacle[i];
|
|
//ObstacleData od = buffer.obstacle[i];
|
|
pNode->vertex.push_back(nVertexIndex);
|
|
//pNode->vertex.push_back(buffer.obstacle[i]);
|
|
|
|
}
|
|
}
|
|
m_mapReadNodes.insert(EntranceMap::iterator::value_type((EntranceMap::key_type)buffer.id,pNode));
|
|
|
|
if (buffer.bEntrance)
|
|
m_mapEntrances.insert(EntranceMap::iterator::value_type(pNode->nBuildingID,pNode));
|
|
|
|
if (buffer.bExit)
|
|
m_mapExits.insert(EntranceMap::iterator::value_type(pNode->nBuildingID,pNode));
|
|
|
|
// if ((pNode->nBuildingID < 0) && (pNode->vertex.empty()) && buffer.id!=1)
|
|
// AIWarning("![AIERROR] A node was found that had buildingid:%d during export, but now it has buildingid:-1 [pos:(x:%.3f,y:%.3f,z:%.3f)]. Please make sure that all your navigation modifiers are correctly exported.",
|
|
// buffer.building,buffer.data.m_pos.x,buffer.data.m_pos.y,buffer.data.m_pos.z);
|
|
|
|
|
|
}
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Reading vertex list");
|
|
|
|
m_pAISystem->m_VertexList.ReadFromFile(file);
|
|
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Reading links");
|
|
|
|
/* file.Read( &iNumber, sizeof(int) );
|
|
m_vLinks.resize(iNumber);
|
|
file.Read( &m_vLinks[0], iNumber * sizeof(int) );
|
|
*/
|
|
|
|
file.Read( &iNumber, sizeof(int) );
|
|
if (iNumber>0)
|
|
{
|
|
m_vLinksDesc.resize(iNumber);
|
|
file.Read( &m_vLinksDesc[0], iNumber * sizeof(LinkDescriptor) );
|
|
}
|
|
|
|
EntranceMap::iterator ei,link;
|
|
|
|
ei = m_mapReadNodes.find(1);
|
|
if (ei==m_mapReadNodes.end())
|
|
{
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\001[AIERROR] FIRST NODE NOT FOUND !!!!");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Disconnect(m_pSafeFirst);
|
|
//delete m_pFirst;
|
|
m_pSafeFirst = (ei->second);
|
|
m_pFirst = m_pSafeFirst;
|
|
m_pCurrent = m_pSafeFirst;
|
|
}
|
|
|
|
m_pAISystem->m_pSystem->GetILog()->UpdateLoadingScreen("\003[AISYSTEM] Reconnecting links (might take some time:)");
|
|
|
|
if (!m_vLinksDesc.empty())
|
|
{
|
|
LinkDescBuffer::iterator iend = m_vLinksDesc.end();
|
|
for (LinkDescBuffer::iterator ldbi=m_vLinksDesc.begin();ldbi!=iend;++ldbi)
|
|
{
|
|
LinkDescriptor &ldb = (*ldbi);
|
|
ei=m_mapReadNodes.find((EntranceMap::key_type)ldb.nSourceNode);
|
|
GraphNode *pNode = ei->second;
|
|
link = m_mapReadNodes.find((EntranceMap::key_type)ldb.nTargetNode);
|
|
if (link==m_mapReadNodes.end() || ei==m_mapReadNodes.end())
|
|
AIError("!Read a link to a node which could not be found!");
|
|
else
|
|
{
|
|
Connect(pNode,link->second);
|
|
|
|
for (VectorOfLinks::iterator vli=pNode->link.begin();vli!=pNode->link.end();vli++)
|
|
if ( (*vli).pLink == link->second )
|
|
{
|
|
(*vli).fMaxRadius = ldb.fMaxPassRadius;
|
|
(*vli).nStartIndex = ldb.nStartIndex;
|
|
(*vli).nEndIndex = ldb.nEndIndex;
|
|
(*vli).vWayOut = ldb.vWayOut;
|
|
(*vli).vEdgeCenter = ldb.vEdgeCenter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*int count = 0;
|
|
while (count < iNumber)
|
|
{
|
|
// get id of node
|
|
ei = m_mapReadNodes.find(m_vLinks[count++]);
|
|
GraphNode *pNode = (ei->second);
|
|
int nrlinks = m_vLinks[count++];
|
|
pNode->link.reserve(nrlinks);
|
|
while(nrlinks--)
|
|
{
|
|
link = m_mapReadNodes.find(m_vLinks[count++]);
|
|
Connect(pNode,(link->second));
|
|
ResolveLinkData(pNode,(link->second));
|
|
}
|
|
}
|
|
*/
|
|
|
|
m_vBuffer.clear();
|
|
m_mapReadNodes.clear();
|
|
m_vLinksDesc.clear();
|
|
//assert(_heapchk()==_HEAPOK);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// defines bounding rectangle of this graph
|
|
void CGraph::SetBBox(const Vec3d & min, const Vec3d & max)
|
|
{
|
|
m_vBBoxMin = min;
|
|
m_vBBoxMax = max;
|
|
}
|
|
|
|
// how is that for descriptive naming of functions ??
|
|
bool CGraph::OutsideOfBBox(const Vec3d & pos)
|
|
{
|
|
if (pos.x < m_vBBoxMin.x)
|
|
return true;
|
|
if (pos.x > m_vBBoxMax.x)
|
|
return true;
|
|
|
|
if (pos.y < m_vBBoxMin.y)
|
|
return true;
|
|
if (pos.y > m_vBBoxMax.y)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void CGraph::FillGreedyMap(GraphNode * pNode, const Vec3d &pos, IVisArea *pTargetArea, bool bStayInArea)
|
|
{
|
|
MarkNode(pNode);
|
|
|
|
if (pNode->link.empty())
|
|
return;
|
|
|
|
GraphNode *pNext = 0;
|
|
VectorOfLinks::iterator pi,piend = pNode->link.end();
|
|
for (pi=pNode->link.begin();pi!=piend;++pi)
|
|
{
|
|
GraphNode *pNow = (*pi).pLink;
|
|
if ( (pNow->mark) || (pNow->nBuildingID!=pNode->nBuildingID))
|
|
continue;
|
|
if (bStayInArea && (pNow->pArea != pTargetArea))
|
|
continue;
|
|
|
|
float thisdist = (pos - pNow->data.m_pos).GetLength();
|
|
m_mapGreedyWalkCandidates.insert(CandidateMap::iterator::value_type(thisdist,pNow));
|
|
|
|
// this snippet will make sure we only check all points inside the target area - not the whole building
|
|
if (pTargetArea && pNow->pArea == pTargetArea)
|
|
FillGreedyMap(pNow,pos,pTargetArea,true);
|
|
else
|
|
FillGreedyMap(pNow,pos,pTargetArea,false);
|
|
}
|
|
|
|
}
|
|
|
|
bool CGraph::RemoveEntrance(int nBuildingID, GraphNode * pNode)
|
|
{
|
|
EntranceMap::iterator ei= m_mapEntrances.find(nBuildingID);
|
|
if (ei!=m_mapEntrances.end())
|
|
{
|
|
while ((ei->first == nBuildingID) && ei!=m_mapEntrances.end())
|
|
{
|
|
if (ei->second == pNode)
|
|
{
|
|
m_mapEntrances.erase(ei);
|
|
return true;
|
|
}
|
|
ei++;
|
|
}
|
|
}
|
|
|
|
ei = m_mapExits.find(nBuildingID);
|
|
if (ei!=m_mapExits.end())
|
|
{
|
|
while ((ei->first == nBuildingID) && ei!=m_mapExits.end())
|
|
{
|
|
if (ei->second == pNode)
|
|
{
|
|
m_mapExits.erase(ei);
|
|
return true;
|
|
}
|
|
ei++;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CGraph::RemoveIndoorNodes(void)
|
|
{
|
|
if (m_mapEntrances.empty())
|
|
return;
|
|
|
|
VectorOfLinks::iterator vi;
|
|
|
|
m_lstDeleteStack.push_front((m_mapEntrances.begin())->second);
|
|
|
|
while (!m_lstDeleteStack.empty())
|
|
{
|
|
GraphNode *pCurrentNode = m_lstDeleteStack.front();
|
|
m_lstDeleteStack.pop_front();
|
|
|
|
// put all links into the node stack only if they are indoor nodes
|
|
for (vi=pCurrentNode->link.begin();vi!=pCurrentNode->link.end();vi++)
|
|
{
|
|
GraphNode *pLink =(*vi).pLink;
|
|
if (pLink->nBuildingID>=0)
|
|
{
|
|
if (std::find(m_lstDeleteStack.begin(),m_lstDeleteStack.end(),pLink) == m_lstDeleteStack.end())
|
|
m_lstDeleteStack.push_front(pLink);
|
|
}
|
|
}
|
|
|
|
Disconnect(pCurrentNode);
|
|
}
|
|
|
|
m_mapEntrances.clear();
|
|
|
|
}
|
|
|
|
void CGraph::REC_RemoveNodes(GraphNode * pNode)
|
|
{
|
|
if (pNode->link.empty())
|
|
return;
|
|
|
|
if (pNode->nBuildingID == -1)
|
|
return;
|
|
|
|
MarkNode(pNode);
|
|
|
|
VectorOfLinks vecLinks;
|
|
vecLinks.insert(vecLinks.begin(),pNode->link.begin(),pNode->link.end());
|
|
VectorOfLinks::iterator i;
|
|
for (i=vecLinks.begin();i!=vecLinks.end();i++)
|
|
{
|
|
GraphNode *pLink = (*i).pLink;
|
|
if ((pLink->nBuildingID == pNode->nBuildingID) && (!pLink->mark))
|
|
REC_RemoveNodes(pLink);
|
|
}
|
|
|
|
Disconnect(pNode);
|
|
}
|
|
|
|
GraphNode * CGraph::CreateNewNode(bool bFromTriangulation)
|
|
{
|
|
nNodes++;
|
|
GraphNode *pRet = new GraphNode;
|
|
if (bFromTriangulation)
|
|
pRet->bCreated = false;
|
|
pRet->AddRef();
|
|
return pRet;
|
|
}
|
|
|
|
void CGraph::DeleteNode(GraphNode * pNode)
|
|
{
|
|
if (pNode->Release())
|
|
{
|
|
delete pNode;
|
|
nNodes--;
|
|
}
|
|
}
|
|
|
|
int CGraph::SelectNodesInSphere(const Vec3d & vCenter, float fRadius, GraphNode *pStart)
|
|
{
|
|
GraphNode *pNode = GetEnclosing(vCenter);
|
|
ClearMarks();
|
|
m_lstSelected.clear();
|
|
|
|
if (pNode->nBuildingID == -1)
|
|
SelectNodeRecursive(pNode,vCenter,fRadius);
|
|
else
|
|
{
|
|
SelectNodesRecursiveIndoors(pNode,vCenter,fRadius,0);
|
|
}
|
|
|
|
ClearMarks();
|
|
|
|
return m_lstSelected.size();
|
|
}
|
|
|
|
void CGraph::SelectNodeRecursive(GraphNode* pNode, const Vec3d & vCenter, float fRadius)
|
|
{
|
|
if (pNode->vertex.empty())
|
|
return;
|
|
|
|
if (pNode->mark)
|
|
return;
|
|
|
|
MarkNode(pNode);
|
|
|
|
|
|
bool stillin = false;
|
|
ObstacleIndexVector::iterator oi;
|
|
for (oi=pNode->vertex.begin();oi!=pNode->vertex.end();oi++)
|
|
{
|
|
ObstacleData od = m_pAISystem->m_VertexList.GetVertex((*oi));
|
|
float flength = GetLengthSquared((od.vPos - vCenter));
|
|
if (flength < (fRadius*fRadius))
|
|
{
|
|
if ( std::find(m_lstSelected.begin(),m_lstSelected.end(),od) == m_lstSelected.end())
|
|
m_lstSelected.push_back(od);
|
|
stillin = true;
|
|
}
|
|
}
|
|
|
|
if (!stillin)
|
|
return;
|
|
|
|
// go trough all the links of this one
|
|
VectorOfLinks::iterator gi;
|
|
for (gi=pNode->link.begin();gi!=pNode->link.end();gi++)
|
|
{
|
|
if ((*gi).pLink->nBuildingID<0)
|
|
SelectNodeRecursive((*gi).pLink,vCenter,fRadius);
|
|
}
|
|
|
|
}
|
|
|
|
void CGraph::SelectNodesRecursiveIndoors(GraphNode * pNode, const Vec3d & vCenter, float fRadius, float fDistance)
|
|
{
|
|
if (pNode->nBuildingID == -1)
|
|
return; // DO NOT go outdoors
|
|
|
|
if (fDistance>fRadius)
|
|
return;
|
|
|
|
if (pNode->mark)
|
|
return;
|
|
|
|
MarkNode(pNode);
|
|
|
|
|
|
bool stillin = false;
|
|
|
|
if ( GetLengthSquared((pNode->data.m_pos - vCenter)) < 2*(fRadius*fRadius))
|
|
stillin = true;
|
|
|
|
ObstacleIndexVector::iterator oi;
|
|
for (oi=pNode->vertex.begin();oi!=pNode->vertex.end();oi++)
|
|
{
|
|
ObstacleData od = m_pAISystem->m_VertexList.GetVertex((*oi));
|
|
float flength = GetLengthSquared((od.vPos - vCenter));
|
|
// float fPathLength = fDistance + (pNode->data.m_pos - od.vPos).GetLength();
|
|
// if (fPathLength*fPathLength > flength*2.f)
|
|
// continue;
|
|
if (flength < (fRadius*fRadius))
|
|
{
|
|
if ( std::find(m_lstSelected.begin(),m_lstSelected.end(),od) == m_lstSelected.end())
|
|
m_lstSelected.push_back(od);
|
|
stillin = true;
|
|
}
|
|
}
|
|
|
|
if (!stillin)
|
|
return;
|
|
|
|
// go trough all the links of this one
|
|
VectorOfLinks::iterator gi;
|
|
for (gi=pNode->link.begin();gi!=pNode->link.end();gi++)
|
|
{
|
|
GraphNode *pNextNode = (*gi).pLink;
|
|
SelectNodesRecursiveIndoors(pNextNode,vCenter,fRadius,fDistance+(pNode->data.m_pos - pNextNode->data.m_pos).GetLength());
|
|
}
|
|
}
|
|
|
|
void CGraph::AddHidePoint(GraphNode* pOwner, const Vec3d & pos, const Vec3d & dir)
|
|
{
|
|
if (!pOwner)
|
|
return;
|
|
|
|
|
|
ObstacleData od;
|
|
od.vPos = pos;
|
|
od.vDir = dir;
|
|
|
|
index_t i = m_pAISystem->m_VertexList.FindVertex(od);
|
|
|
|
|
|
|
|
if (i<0)
|
|
{
|
|
//pOwner->vertex.push_back(od);
|
|
pOwner->vertex.push_back(m_pAISystem->m_VertexList.AddVertex(od));
|
|
}
|
|
else
|
|
{
|
|
ObstacleIndexVector::iterator oi = std::find(pOwner->vertex.begin(),pOwner->vertex.end(),i);
|
|
if (oi!=pOwner->vertex.end())
|
|
{
|
|
m_pAISystem->m_VertexList.ModifyVertex(i).vPos = pos;
|
|
m_pAISystem->m_VertexList.ModifyVertex(i).vDir = dir;
|
|
}
|
|
else
|
|
pOwner->vertex.push_back(i);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CGraph::RemoveHidePoint(GraphNode * pOwner, const Vec3d & pos, const Vec3d & dir)
|
|
{
|
|
if (!pOwner)
|
|
return;
|
|
|
|
ObstacleIndexVector::iterator oi;
|
|
for ( oi = pOwner->vertex.begin();oi != pOwner->vertex.end(); oi++ )
|
|
{
|
|
ObstacleData od = m_pAISystem->m_VertexList.GetVertex((*oi));
|
|
if ( ( (od.vPos - pos).GetLength() < 0.0001 ) )
|
|
{
|
|
pOwner->vertex.erase(oi);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraph::DisconnectUnreachable(void)
|
|
{
|
|
GraphNode *pCurrent = m_pFirst;
|
|
|
|
m_lstNodeStack.clear();
|
|
ClearMarks();
|
|
|
|
|
|
m_lstNodeStack.push_back(pCurrent);
|
|
ray_hit rh;
|
|
|
|
while (!m_lstNodeStack.empty())
|
|
{
|
|
MarkNode(pCurrent);
|
|
vectorf start = pCurrent->data.m_pos;
|
|
|
|
VectorOfLinks linkVector;
|
|
linkVector = pCurrent->link;
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=linkVector.begin();vi!=linkVector.end();vi++)
|
|
{
|
|
|
|
if (!(*vi).pLink->mark)
|
|
{
|
|
if (std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),(*vi).pLink) == m_lstNodeStack.end())
|
|
m_lstNodeStack.push_front((*vi).pLink);
|
|
}
|
|
|
|
// check connectivity
|
|
|
|
vectorf end = (*vi).pLink->data.m_pos;
|
|
int cnt = m_pAISystem->GetPhysicalWorld()->RayWorldIntersection(start,end-start,ent_static, 0,&rh,1);
|
|
|
|
if (cnt)
|
|
DisconnectLink(pCurrent,(*vi).pLink);
|
|
|
|
}
|
|
|
|
pCurrent = m_lstNodeStack.front();
|
|
m_lstNodeStack.pop_front();
|
|
|
|
}
|
|
|
|
ClearMarks();
|
|
}
|
|
|
|
|
|
void CGraph::DisconnectLink(GraphNode * one, GraphNode * two, bool bOneWay)
|
|
{
|
|
if (!one || !two)
|
|
return;
|
|
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=one->link.begin();vi!=one->link.end();vi++)
|
|
{
|
|
if ( (*vi).pLink == two)
|
|
{
|
|
one->link.erase(vi);
|
|
DeleteNode(two); // this will lower the ref count... will delete if appropriate
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bOneWay)
|
|
return;
|
|
|
|
for (vi=two->link.begin();vi!=two->link.end();vi++)
|
|
{
|
|
if ( (*vi).pLink == one)
|
|
{
|
|
two->link.erase(vi);
|
|
DeleteNode(one); // this will lower the ref count... will delete if appropriate
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int CGraph::BeautifyPath(int &nIterations, const Vec3d &start, const Vec3d &end)
|
|
{
|
|
//return PATHFINDER_PATHFOUND;
|
|
if (!m_pAISystem->m_cvBeautifyPath->GetIVal())
|
|
{
|
|
return PATHFINDER_PATHFOUND;
|
|
}
|
|
|
|
// try to skip unnecessary wiggling around
|
|
if (m_bBeautifying)
|
|
{
|
|
m_lstPath.clear();
|
|
if (m_lstNodeStack.size()==1)
|
|
{
|
|
m_lstPath.push_back(end);
|
|
return PATHFINDER_PATHFOUND;
|
|
}
|
|
|
|
m_iFirst = m_lstNodeStack.begin();
|
|
m_iThird = m_lstNodeStack.end();
|
|
m_bBeautifying = false;
|
|
m_vBeautifierStart = start;
|
|
|
|
//CryLogAlways("Start BEAUTIFY PATH for: %s ",m_pRequester->GetName());
|
|
//CryLogAlways("From position (%.3f,%.3f,%.3f)",start.x,start.y,start.z);
|
|
//CryLogAlways("To position (%.3f,%.3f,%.3f)",end.x,end.y,end.z);
|
|
}
|
|
//else
|
|
//{
|
|
// CryLogAlways("continuing beautification for: %s ",m_pRequester->GetName());
|
|
//}
|
|
|
|
|
|
while ((nIterations--) && (m_iFirst!=m_lstNodeStack.end()))
|
|
{
|
|
ListNodes::iterator iNextFirst = m_lstNodeStack.end();
|
|
m_iSecond = m_iFirst;
|
|
m_iSecond++;
|
|
|
|
Vec3d vRealStart,vRealEnd;
|
|
|
|
bool bSkipEval = false;
|
|
|
|
if ((*m_iFirst)->nBuildingID < 0)
|
|
{
|
|
while (m_iSecond != m_lstNodeStack.end())
|
|
{
|
|
GraphNode *pFirst = (*m_iFirst);
|
|
GraphNode *pSecond = (*m_iSecond);
|
|
|
|
if (pSecond->nBuildingID >= 0)
|
|
{
|
|
if (!bSkipEval)
|
|
{
|
|
bSkipEval = true;
|
|
iNextFirst = m_lstNodeStack.end();
|
|
}
|
|
// if (m_iThird!=m_lstNodeStack.end())
|
|
// {
|
|
// m_lstPath.push_back( (*m_iThird)->data.m_pos);
|
|
// m_iFirst = m_iThird;
|
|
// }
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
// traverse list from iFirst to iSecond testing edges as you go
|
|
bool bPassable = true; // optimistic expectation
|
|
ListNodes::iterator iPairStart=m_iFirst;
|
|
for (;iPairStart!=m_iSecond;iPairStart++)
|
|
{
|
|
ListNodes::iterator iPairEnd = iPairStart;
|
|
|
|
iPairEnd++;
|
|
|
|
Vec3d EdgStart;
|
|
Vec3d EdgEnd;
|
|
Vec3d EdgDir;
|
|
Vec3d EdgCenter;
|
|
|
|
|
|
VectorOfLinks::iterator vli;
|
|
for (vli=(*iPairStart)->link.begin();vli!=(*iPairStart)->link.end();vli++)
|
|
{
|
|
|
|
|
|
if ((*vli).pLink == (*iPairEnd))
|
|
{
|
|
|
|
if ((m_iFirst == m_lstNodeStack.begin()) || (m_lstPath.empty()))
|
|
vRealStart = start;
|
|
else
|
|
vRealStart = m_lstPath.back();
|
|
|
|
|
|
if ((*m_iSecond) == (*m_lstNodeStack.rbegin()))
|
|
vRealEnd = end;
|
|
else
|
|
vRealEnd = (*m_iSecond)->data.m_pos;
|
|
|
|
|
|
|
|
EdgStart = m_pAISystem->m_VertexList.GetVertex((*iPairStart)->vertex[(*vli).nStartIndex]).vPos;
|
|
|
|
EdgEnd = m_pAISystem->m_VertexList.GetVertex((*iPairStart)->vertex[(*vli).nEndIndex]).vPos;
|
|
EdgDir = EdgEnd - EdgStart;
|
|
EdgCenter =(*vli).vEdgeCenter;
|
|
EdgCenter.z = 0;
|
|
vRealStart.z = 0;
|
|
vRealEnd.z = 0;
|
|
float s=-1,t=-1;
|
|
|
|
|
|
|
|
if (m_pAISystem->SegmentsIntersect(vRealStart, vRealEnd-vRealStart,EdgStart,EdgDir,s,t))
|
|
{
|
|
if ( s>0.f && s<1.f && t>0.f && t<1.f)
|
|
{
|
|
|
|
m_vLastIntersection = EdgStart+t*EdgDir;
|
|
float fCenterDist = (m_vLastIntersection - EdgCenter).GetLength();
|
|
//if (fCenterDist > ((*vli).fMaxRadius-1.f))
|
|
if (fCenterDist > ((*vli).fMaxRadius-m_pRequester->m_fPassRadius))
|
|
{
|
|
bPassable = false;
|
|
}
|
|
|
|
}
|
|
else
|
|
bPassable = false;
|
|
}
|
|
else
|
|
{
|
|
bPassable = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bPassable)
|
|
{
|
|
float fEdgeLength =EdgDir.Length();
|
|
//float dist_from_obstacle = 0.6f;
|
|
float dist_from_obstacle = m_pRequester->m_fPassRadius;
|
|
|
|
// if we have more space, do not get too close to the obstacle
|
|
if ((m_pRequester->m_fPassRadius < 1.f) && ((*vli).fMaxRadius > 1.f))
|
|
dist_from_obstacle = 1.f;
|
|
if ((EdgStart-m_vLastIntersection).Length() < (EdgEnd-m_vLastIntersection).Length() )
|
|
{
|
|
// closer to start edge
|
|
float fFullDistToCenter = (EdgStart - EdgCenter).Length();
|
|
float minT = fFullDistToCenter - (*vli).fMaxRadius + dist_from_obstacle;
|
|
minT/=fEdgeLength;
|
|
m_vBeautifierStart = EdgStart+minT*EdgDir;
|
|
}
|
|
else
|
|
{
|
|
// closer to end edge
|
|
float fFullDistToCenter = (EdgEnd - EdgCenter).Length();
|
|
float minT = fFullDistToCenter - (*vli).fMaxRadius + dist_from_obstacle;
|
|
minT/=fEdgeLength;
|
|
m_vBeautifierStart = EdgEnd-minT*EdgDir;
|
|
}
|
|
|
|
/* float minT = ((fEdgeLength/2.f)-((*vli).fMaxRadius-dist_from_obstacle)) / fEdgeLength;
|
|
// which edge is closer to the intersection point
|
|
if ((EdgStart-m_vLastIntersection).Length() < (EdgEnd-m_vLastIntersection).Length() )
|
|
m_vBeautifierStart = EdgStart+minT*EdgDir;
|
|
else
|
|
m_vBeautifierStart = EdgEnd-minT*EdgDir;*/
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bPassable)
|
|
{
|
|
m_iThird = m_iSecond;
|
|
if (bSkipEval)
|
|
{
|
|
iNextFirst = m_lstNodeStack.end();
|
|
bSkipEval = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bSkipEval)
|
|
{
|
|
iNextFirst = iPairStart;
|
|
bSkipEval = true;
|
|
}
|
|
}
|
|
|
|
m_iSecond++;
|
|
}
|
|
|
|
if (!bSkipEval)
|
|
{
|
|
// if the third is not valid, then add midpoint od first and second, and then second
|
|
if (m_iThird == m_lstNodeStack.end())
|
|
{
|
|
m_iSecond = m_iFirst;
|
|
m_iSecond++;
|
|
if (m_iSecond!=m_lstNodeStack.end() && ((*m_iSecond)->nBuildingID <0))
|
|
{
|
|
///m_lstPath.push_back(vBestPass);
|
|
VectorOfLinks::iterator vli;
|
|
for (vli=(*m_iSecond)->link.begin();vli!=(*m_iSecond)->link.end();vli++)
|
|
{
|
|
if ((*vli).pLink == (*m_iFirst))
|
|
m_lstPath.push_back((*vli).vEdgeCenter);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
m_iSecond = m_iFirst;
|
|
m_iSecond++;
|
|
if ((m_iSecond!=m_iThird) && (m_iSecond != m_lstNodeStack.end()))
|
|
{
|
|
// there are some nodes to delete, so delete them
|
|
for (ListNodes::iterator li=m_iSecond; (li!=m_iThird) && (li!=m_lstNodeStack.end());li=m_lstNodeStack.erase(li));
|
|
}
|
|
|
|
//if (m_iThird == --m_lstNodeStack.end())
|
|
if ((*m_iThird) == (*m_lstNodeStack.rbegin()))
|
|
{
|
|
m_lstPath.push_back(end);
|
|
}
|
|
else
|
|
{
|
|
m_lstPath.push_back( (*m_iThird)->data.m_pos);
|
|
//vBestPass.z = m_pAISystem->m_pSystem->GetI3DEngine()->GetTerrainElevation(vBestPass.x, vBestPass.y);
|
|
//m_lstPath.push_back( vBestPass);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (iNextFirst != m_lstNodeStack.end())
|
|
{
|
|
I3DEngine *pEngine = m_pAISystem->m_pSystem->GetI3DEngine();
|
|
if (pEngine)
|
|
m_vBeautifierStart.z = pEngine->GetTerrainElevation(m_vBeautifierStart.x,m_vBeautifierStart.y);
|
|
m_lstPath.push_back(m_vBeautifierStart);
|
|
m_iFirst = iNextFirst;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_lstPath.push_back( (*m_iFirst)->data.m_pos);
|
|
}
|
|
|
|
m_iThird = m_lstNodeStack.end();
|
|
m_iFirst++;
|
|
}
|
|
|
|
if (m_iFirst==m_lstNodeStack.end())
|
|
m_lstPath.push_back(end);
|
|
|
|
|
|
if (m_iFirst == m_lstNodeStack.end())
|
|
{
|
|
m_bBeautifying = true;
|
|
m_lstNodeStack.clear();
|
|
if (m_lstPath.size() > 2)
|
|
{
|
|
int todel = 1;
|
|
ListPositions::iterator li = m_lstPath.begin();
|
|
Vec3d firstpos = (*li++)-start;
|
|
Vec3d secondpos= (*li)-start;
|
|
while ((*li) == m_lstPath.front())
|
|
{
|
|
secondpos= (*++li)-start;;
|
|
todel++;
|
|
}
|
|
|
|
if (firstpos.Dot(secondpos) < 0)
|
|
{
|
|
while (todel--)
|
|
{
|
|
if (!m_lstPath.empty())
|
|
m_lstPath.pop_front();
|
|
}
|
|
}
|
|
}
|
|
return PATHFINDER_PATHFOUND;
|
|
}
|
|
|
|
//CryLogAlways("----Ran out of iterations(%.3f,%.3f,%.3f)",(*m_iFirst)->data.m_pos.x,(*m_iFirst)->data.m_pos.y,(*m_iFirst)->data.m_pos.z);
|
|
return PATHFINDER_BEAUTIFYINGPATH;
|
|
}
|
|
|
|
|
|
|
|
void CGraph::ResolveLinkData(GraphNode* pOne, GraphNode* pTwo)
|
|
{
|
|
if (pOne->nBuildingID>=0 || pTwo->nBuildingID>=0)
|
|
return;
|
|
|
|
if (pOne->vertex.empty() || pTwo->vertex.empty())
|
|
return;
|
|
|
|
int iOneEdge1=-1,iOneEdge2=-1;
|
|
int iTwoEdge1=-1,iTwoEdge2=-1;
|
|
|
|
for (unsigned int i=0;i<pOne->vertex.size();i++)
|
|
for (unsigned int j=0;j<pTwo->vertex.size();j++)
|
|
{
|
|
/* if ( (fabs(pOne->vertex[i].vPos.x - pTwo->vertex[j].vPos.x) < 0.0001f) &&
|
|
(fabs(pOne->vertex[i].vPos.y - pTwo->vertex[j].vPos.y) < 0.0001f) &&
|
|
(fabs(pOne->vertex[i].vPos.z - pTwo->vertex[j].vPos.z) < 0.0001f) )
|
|
*/
|
|
// if (IsEquivalent(pOne->vertex[i].vPos,pTwo->vertex[j].vPos,0.001f))
|
|
//if (pOne->vertex[i].vPos==pTwo->vertex[j].vPos )
|
|
if (pOne->vertex[i]==pTwo->vertex[j])
|
|
{
|
|
if (iOneEdge1<0)
|
|
iOneEdge1 = i;
|
|
else
|
|
iOneEdge2 = i;
|
|
|
|
if (iTwoEdge1<0)
|
|
iTwoEdge1 = j;
|
|
else
|
|
iTwoEdge2 = j;
|
|
}
|
|
}
|
|
|
|
// find triangle normal for one
|
|
|
|
Vec3d OneNormal = (m_pAISystem->m_VertexList.GetVertex(pOne->vertex[0]).vPos - m_pAISystem->m_VertexList.GetVertex(pOne->vertex[1]).vPos).Cross(m_pAISystem->m_VertexList.GetVertex(pOne->vertex[2]).vPos - m_pAISystem->m_VertexList.GetVertex(pOne->vertex[1]).vPos);
|
|
Vec3d TwoNormal = (m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[0]).vPos - m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[1]).vPos).Cross(m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[2]).vPos - m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[1]).vPos);
|
|
|
|
if ( iOneEdge1 * iOneEdge2 < 0)
|
|
int a=5;
|
|
|
|
if ( iTwoEdge1 * iTwoEdge2 < 0)
|
|
int a=5;
|
|
|
|
int iNoOne = 3 - (iOneEdge1 + iOneEdge2);
|
|
int iNoTwo = 3 - (iTwoEdge1 + iTwoEdge2);
|
|
|
|
// find which link from one goes to two
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pOne->link.begin();vi!=pOne->link.end();vi++)
|
|
{
|
|
if ((*vi).pLink == pTwo)
|
|
{
|
|
(*vi).nStartIndex = iOneEdge1;
|
|
(*vi).nEndIndex = iOneEdge2;
|
|
|
|
Vec3d edge = m_pAISystem->m_VertexList.GetVertex(pOne->vertex[iOneEdge1]).vPos - m_pAISystem->m_VertexList.GetVertex(pOne->vertex[iOneEdge2]).vPos;
|
|
|
|
/// if (pOne->vertex[iOneEdge1].bOccupied && pOne->vertex[iOneEdge2].bOccupied)
|
|
// (*vi).fMaxRadius = -1;
|
|
|
|
Vec3d vCross(edge.Cross(TwoNormal));
|
|
float fLen = vCross.GetLength();
|
|
Vec3d normal = fLen>0 ? vCross/fLen : Vec3d(0,0,0);
|
|
float fdot = normal.Dot(m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iNoTwo]).vPos - (m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iTwoEdge1]).vPos + edge/2.f));
|
|
|
|
(*vi).vWayOut = normal;
|
|
if (fdot > 0 )
|
|
(*vi).vWayOut *=-1.f;
|
|
}
|
|
}
|
|
|
|
for (vi=pTwo->link.begin();vi!=pTwo->link.end();vi++)
|
|
{
|
|
if ((*vi).pLink == pOne)
|
|
{
|
|
(*vi).nStartIndex = iTwoEdge1;
|
|
(*vi).nEndIndex = iTwoEdge2;
|
|
|
|
Vec3d edge = m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iTwoEdge1]).vPos - m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iTwoEdge2]).vPos;
|
|
|
|
//if (pTwo->vertex[iTwoEdge1].bOccupied && pTwo->vertex[iTwoEdge2].bOccupied)
|
|
// (*vi).fMaxRadius = -1;
|
|
|
|
Vec3d vCross(edge.Cross(TwoNormal));
|
|
float fLen = vCross.GetLength();
|
|
Vec3d normal = fLen>0 ? vCross/fLen : Vec3d(0,0,0);
|
|
float fdot = normal.Dot(m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iNoTwo]).vPos - (m_pAISystem->m_VertexList.GetVertex(pTwo->vertex[iTwoEdge1]).vPos + edge/2.f));
|
|
|
|
(*vi).vWayOut = normal;
|
|
if (fdot > 0 )
|
|
(*vi).vWayOut *=-1.f;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CGraph::ConnectNodes(ListNodes & lstNodes)
|
|
{
|
|
|
|
// clear degenerate triangles
|
|
ListNodes::iterator it;
|
|
for (it=lstNodes.begin();it!=lstNodes.end();)
|
|
{
|
|
GraphNode *pCurrent = (*it);
|
|
ObstacleIndexVector::iterator obi,obi2;
|
|
bool bRemoved = false;
|
|
|
|
for (obi=pCurrent->vertex.begin();obi!=pCurrent->vertex.end();obi++)
|
|
{
|
|
for (obi2=pCurrent->vertex.begin();obi2!=pCurrent->vertex.end();obi2++)
|
|
{
|
|
if (obi==obi2)
|
|
continue;
|
|
if ( (*obi) == (*obi2) )
|
|
{
|
|
it=lstNodes.erase(it);
|
|
bRemoved = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bRemoved)
|
|
break;
|
|
}
|
|
if (!bRemoved)
|
|
it++;
|
|
}
|
|
|
|
|
|
// reconnect triangles in nodes list
|
|
ListNodes::iterator it1,it2;
|
|
for (it1=lstNodes.begin();it1!=lstNodes.end();it1++)
|
|
{
|
|
GraphNode *pCurrent = (*it1);
|
|
for (it2=lstNodes.begin();it2!=lstNodes.end();it2++)
|
|
{
|
|
GraphNode *pCandidate = (*it2);
|
|
ObstacleIndexVector::iterator obCur,obNext;
|
|
for (obCur=pCurrent->vertex.begin();obCur!=pCurrent->vertex.end();obCur++)
|
|
{
|
|
obNext = obCur;
|
|
obNext++;
|
|
if (obNext==pCurrent->vertex.end())
|
|
obNext = pCurrent->vertex.begin();
|
|
|
|
ObstacleIndexVector::iterator obCan,obNextCan;
|
|
for (obCan=pCandidate->vertex.begin();obCan!=pCandidate->vertex.end();obCan++)
|
|
{
|
|
obNextCan = obCan;
|
|
obNextCan++;
|
|
if (obNextCan == pCandidate->vertex.end())
|
|
obNextCan = pCandidate->vertex.begin();
|
|
|
|
/*
|
|
if ( (fabs((*obCur).vPos.x - (*obCan).vPos.x) < 0.0001f) &&
|
|
(fabs((*obCur).vPos.y - (*obCan).vPos.y) < 0.0001f) &&
|
|
(fabs((*obCur).vPos.z - (*obCan).vPos.z) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.x - (*obNextCan).vPos.x) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.y - (*obNextCan).vPos.y) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.z - (*obNextCan).vPos.z) < 0.0001f) )
|
|
*/
|
|
if ( ((*obCur)==(*obCan)) && ((*obNext)==(*obNextCan)) )
|
|
{
|
|
if (pCandidate!=pCurrent)
|
|
{
|
|
Connect(pCurrent,pCandidate);
|
|
ResolveLinkData(pCurrent,pCandidate);
|
|
}
|
|
}
|
|
|
|
/*
|
|
if ( (fabs((*obCur).vPos.x - (*obNextCan).vPos.x) < 0.0001f) &&
|
|
(fabs((*obCur).vPos.y - (*obNextCan).vPos.y) < 0.0001f) &&
|
|
(fabs((*obCur).vPos.z - (*obNextCan).vPos.z) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.x - (*obCan).vPos.x) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.y - (*obCan).vPos.y) < 0.0001f) &&
|
|
(fabs((*obNext).vPos.z - (*obCan).vPos.z) < 0.0001f) )
|
|
*/
|
|
if ( ((*obCur)==(*obNextCan)) && ((*obNext)==(*obCan)) )
|
|
{
|
|
if (pCandidate!=pCurrent)
|
|
{
|
|
Connect(pCurrent,pCandidate);
|
|
ResolveLinkData(pCurrent,pCandidate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (it1=lstNodes.begin();it1!=lstNodes.end();it1++)
|
|
{
|
|
GraphNode *pCurrent = (*it1);
|
|
VectorOfLinks::iterator vi;
|
|
for (vi=pCurrent->link.begin();vi!=pCurrent->link.end();vi++)
|
|
ResolveLinkData(pCurrent,(*vi).pLink);
|
|
}
|
|
|
|
}
|
|
|
|
void CGraph::FillGraphNodeData(GraphNode* pNode)
|
|
{
|
|
I3DEngine *pEngine = m_pAISystem->m_pSystem->GetI3DEngine();
|
|
|
|
//CheckClockness(pNode);
|
|
|
|
ObstacleIndexVector::iterator oi;
|
|
for (oi=pNode->vertex.begin();oi!=pNode->vertex.end();oi++)
|
|
{
|
|
pNode->data.m_pos.x += m_pAISystem->m_VertexList.GetVertex((*oi)).vPos.x;
|
|
pNode->data.m_pos.y += m_pAISystem->m_VertexList.GetVertex((*oi)).vPos.y;
|
|
pNode->data.m_pos.z += m_pAISystem->m_VertexList.GetVertex((*oi)).vPos.z;
|
|
}
|
|
|
|
pNode->data.m_pos.x/=3.f;
|
|
pNode->data.m_pos.y/=3.f;
|
|
pNode->data.m_pos.z/=3.f;
|
|
|
|
// pNode->data.m_pos.x = (v1->x + v2->x + v3->x) / 3.f;
|
|
// pNode->data.m_pos.y = (v1->y + v2->y + v3->y) / 3.f;
|
|
float x = pNode->data.m_pos.x ;
|
|
float y = pNode->data.m_pos.y ;
|
|
// put it on the terrain
|
|
float z = pEngine->GetTerrainElevation(x,y);
|
|
pNode->data.m_pos.z = z+0.2f; // elevate it a little bit
|
|
|
|
// calculate slope index
|
|
float he,ve;
|
|
he = ( pEngine->GetTerrainElevation(x+1,y) - pEngine->GetTerrainElevation(x-1,y) ) / 2.f; // tg(alpha)
|
|
he= (float) fabs(he);
|
|
if ( he>1.f ) he = 1.0f; //for now, no slopes beyond 45 degrees
|
|
ve = ( pEngine->GetTerrainElevation(x,y+1) - pEngine->GetTerrainElevation(x,y-1) ) / 2.f; // tg(alpha)
|
|
ve= (float) fabs(ve);
|
|
if ( ve>1.f ) ve = 1.0f; //for now, no slopes beyond 45 degrees
|
|
if (he>ve) pNode->data.fSlope = he;
|
|
else pNode->data.fSlope = ve;
|
|
|
|
//check if point in water
|
|
if (pEngine->IsPointInWater(Vec3d(pNode->data.m_pos)))
|
|
pNode->data.bWater = true;
|
|
|
|
// check if point in shadow
|
|
// if (pEngine->IsPointInShadow(Vec3d(pNode->data.m_pos)))
|
|
// pNode->data.bShadow = true;
|
|
|
|
}
|
|
|
|
void CGraph::SetCurrentHeuristic(unsigned int heuristic_type)
|
|
{
|
|
if (m_pHeuristic)
|
|
{
|
|
delete m_pHeuristic;
|
|
m_pHeuristic = 0;
|
|
}
|
|
|
|
switch (heuristic_type)
|
|
{
|
|
case AIHEURISTIC_DEFAULT:
|
|
m_pHeuristic = new CHeuristic;
|
|
break;
|
|
case AIHEURISTIC_STANDARD:
|
|
m_pHeuristic = new CStandardHeuristic;
|
|
break;
|
|
case AIHEURISTIC_VEHICLE:
|
|
m_pHeuristic = new CVehicleHeuristic;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
void CGraph::ResolveTotalLinkData(void)
|
|
{
|
|
m_lstNodeStack.clear();
|
|
m_lstNodeStack.push_back(m_pSafeFirst);
|
|
|
|
while (!m_lstNodeStack.empty())
|
|
{
|
|
GraphNode *pCurrent = m_lstNodeStack.front();
|
|
m_lstNodeStack.pop_front();
|
|
|
|
MarkNode(pCurrent);
|
|
|
|
for (VectorOfLinks::iterator vi=pCurrent->link.begin();vi!=pCurrent->link.end();vi++)
|
|
{
|
|
ResolveLinkData(pCurrent,(*vi).pLink);
|
|
|
|
if (!vi->pLink->mark)
|
|
{
|
|
if (std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),vi->pLink) == m_lstNodeStack.end())
|
|
m_lstNodeStack.push_back(vi->pLink);
|
|
}
|
|
}
|
|
}
|
|
|
|
ClearMarks();
|
|
}
|
|
|
|
bool CGraph::CanReuseLastPath(GraphNode * pBegin)
|
|
{
|
|
ListNodes::iterator i = m_lstLastPath.begin();
|
|
|
|
while (i!=m_lstLastPath.end())
|
|
{
|
|
if ( (*i) == pBegin )
|
|
{
|
|
m_lstNodeStack.clear();
|
|
m_lstNodeStack.insert(m_lstNodeStack.begin(),i,m_lstLastPath.end());
|
|
m_lstPath.clear();
|
|
for (ListNodes::iterator fi = i;fi!=m_lstLastPath.end();fi++)
|
|
m_lstPath.push_back((*fi)->data.m_pos);
|
|
return true;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
GraphNode * CGraph::GetThirdNode(const Vec3d & vFirst, const Vec3d & vSecond, const Vec3d &vThird)
|
|
{
|
|
GraphNode *pFirst = GetEnclosing(vFirst);
|
|
GraphNode *pSecond = GetEnclosing(vSecond);
|
|
GraphNode *pThird = GetEnclosing(vThird);
|
|
|
|
VectorOfLinks::iterator vli;
|
|
if (pFirst->link.size()>2)
|
|
{
|
|
for (vli=pFirst->link.begin();vli!=pFirst->link.end();vli++)
|
|
if (((*vli).pLink != pSecond) && ((*vli).pLink!=pThird))
|
|
return (*vli).pLink;
|
|
}
|
|
else
|
|
{
|
|
for (vli=pFirst->link.begin();vli!=pFirst->link.end();vli++)
|
|
if ( (*vli).pLink != pSecond )
|
|
return (*vli).pLink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CGraph::Reset(void)
|
|
{
|
|
m_pWalkBackCurrent = 0;
|
|
m_pPathfinderCurrent = 0;
|
|
m_lstCurrentHistory.clear();
|
|
m_lstLastPath.clear();
|
|
m_lstNodeStack.clear();
|
|
m_bBeautifying = true;
|
|
}
|
|
|
|
GraphNode * CGraph::GetEntrance(int nBuildingID,const Vec3d &pos)
|
|
{
|
|
GraphNode *pEntrance=0;
|
|
float mindist = 1000000;
|
|
EntranceMap::iterator ei = m_mapEntrances.find(nBuildingID);
|
|
if (ei != m_mapEntrances.end())
|
|
{
|
|
pEntrance=ei->second;
|
|
if (m_mapEntrances.count(nBuildingID) > 1)
|
|
{
|
|
mindist = GetLengthSquared((pEntrance->data.m_pos-pos));
|
|
for (;ei!=m_mapEntrances.end();ei++)
|
|
{
|
|
if (ei->first != nBuildingID)
|
|
break;
|
|
float curr_dist = GetLengthSquared(((ei->second)->data.m_pos-pos));
|
|
if (curr_dist<=mindist)
|
|
{
|
|
pEntrance = ei->second;
|
|
mindist = curr_dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ei = m_mapExits.find(nBuildingID);
|
|
if (ei != m_mapExits.end())
|
|
{
|
|
pEntrance=ei->second;
|
|
if (m_mapExits.count(nBuildingID) > 1)
|
|
{
|
|
mindist = GetLengthSquared((pEntrance->data.m_pos-pos));
|
|
for (;ei!=m_mapExits.end();ei++)
|
|
{
|
|
if (ei->first != nBuildingID)
|
|
break;
|
|
float curr_dist = GetLengthSquared(((ei->second)->data.m_pos-pos));
|
|
if (curr_dist<=mindist)
|
|
{
|
|
pEntrance = ei->second;
|
|
mindist = curr_dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return pEntrance;
|
|
}
|
|
|
|
void CGraph::RemoveDegenerateTriangle(GraphNode * pDegenerate, bool bRecurse)
|
|
{
|
|
VectorOfLinks::iterator li, liend = pDegenerate->link.end();
|
|
for (li=pDegenerate->link.begin();li!=liend;++li)
|
|
{
|
|
Vec3d vStart = m_pAISystem->m_VertexList.GetVertex(pDegenerate->vertex[(*li).nStartIndex]).vPos;
|
|
Vec3d vEnd = m_pAISystem->m_VertexList.GetVertex(pDegenerate->vertex[(*li).nEndIndex]).vPos;
|
|
|
|
if (IsEquivalent(vStart,vEnd,0.1f))
|
|
break;
|
|
}
|
|
|
|
if (li==liend)
|
|
return;
|
|
|
|
int noStartIndex = (*li).nStartIndex;
|
|
int noEndIndex = (*li).nEndIndex;
|
|
|
|
int noVertex = 3 - (noStartIndex+noEndIndex);
|
|
GraphNode *pLink1=0,*pLink2=0;
|
|
VectorOfLinks::iterator vol,ivolend = pDegenerate->link.end();
|
|
for (vol=pDegenerate->link.begin();vol!=ivolend;++vol)
|
|
{
|
|
if ( ( (*vol).nStartIndex == noVertex) ||
|
|
( (*vol).nEndIndex == noVertex ))
|
|
{
|
|
// we have one of the other two links - supposedly correct
|
|
if ( (*vol).nStartIndex == noStartIndex || (*vol).nEndIndex == noStartIndex)
|
|
pLink1 = (*vol).pLink;
|
|
else
|
|
pLink2 = (*vol).pLink;
|
|
}
|
|
}
|
|
|
|
if (!pLink1 || !pLink2)
|
|
AIError("!Bad triangle found while trying to eliminate a degenerate triangle.");
|
|
|
|
if (bRecurse)
|
|
RemoveDegenerateTriangle((*li).pLink,false);
|
|
|
|
Disconnect(pDegenerate);
|
|
Connect(pLink1,pLink2);
|
|
|
|
if (pLink1->mark)
|
|
pLink1->mark = false;
|
|
|
|
if (pLink2->mark)
|
|
pLink2->mark = false;
|
|
}
|
|
|
|
|
|
void CGraph::FindTrapNodes(GraphNode *pNode, int recCount)
|
|
{
|
|
if (recCount<0)
|
|
return;
|
|
MarkNode(pNode);
|
|
|
|
if (pNode->nBuildingID>=0) // no inside
|
|
return;
|
|
|
|
if (!pNode->link.empty())
|
|
{
|
|
bool bTrapped = true;
|
|
bool bCanHoldPlayer = false;
|
|
VectorOfLinks::iterator vli, vliend = pNode->link.end();
|
|
for (vli=pNode->link.begin();vli!=vliend;++vli)
|
|
{
|
|
GraphLink glink = (*vli);
|
|
if (glink.fMaxRadius>0.6f)
|
|
bTrapped = false;
|
|
Vec3d start = m_pAISystem->m_VertexList.GetVertex((*vli).nStartIndex).vPos;
|
|
Vec3d end = m_pAISystem->m_VertexList.GetVertex((*vli).nEndIndex).vPos;
|
|
if ( GetLength(start-end) > 0.6)
|
|
bCanHoldPlayer = true;
|
|
|
|
}
|
|
|
|
if (bTrapped && bCanHoldPlayer)
|
|
{
|
|
m_lstTrapNodes.push_back(pNode);
|
|
}
|
|
|
|
for (vli=pNode->link.begin();vli!=vliend;++vli)
|
|
{
|
|
GraphLink glink = (*vli);
|
|
FindTrapNodes(glink.pLink,recCount-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraph::FixDegenerateTriangles(void)
|
|
{
|
|
m_lstNodeStack.clear();
|
|
m_lstNodeStack.push_back(m_pSafeFirst);
|
|
|
|
// traverse all the graph
|
|
while (!m_lstNodeStack.empty())
|
|
{
|
|
GraphNode *pCurrent = m_lstNodeStack.front();
|
|
m_lstNodeStack.pop_front();
|
|
|
|
MarkNode(pCurrent);
|
|
|
|
|
|
|
|
// add neighbors to list
|
|
for (VectorOfLinks::iterator vi=pCurrent->link.begin();vi!=pCurrent->link.end();vi++)
|
|
{
|
|
if (!vi->pLink->mark)
|
|
{
|
|
if (std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),vi->pLink) == m_lstNodeStack.end())
|
|
m_lstNodeStack.push_back(vi->pLink);
|
|
}
|
|
}
|
|
|
|
if (pCurrent == m_pSafeFirst)
|
|
continue;
|
|
|
|
// check for degeneracy
|
|
float fDegen = pCurrent->GetDegeneracyValue();
|
|
if (fDegen<0.01f)
|
|
{
|
|
GraphNode *pPair = 0;
|
|
float fTSize = (float) GetAISystem()->m_pSystem->GetI3DEngine()->GetTerrainSize();
|
|
VectorOfLinks::iterator vli = pCurrent->link.begin(),vliend = pCurrent->link.end();
|
|
for (;vli!=vliend;++vli)
|
|
{
|
|
if ( (*vli).fMaxRadius>0 ) // make sure you do not break forbidden areas
|
|
{
|
|
GraphNode *pCandidate = (*vli).pLink;
|
|
if (pCandidate==m_pSafeFirst)
|
|
continue;
|
|
// find the link in the candidate that leads to the current node
|
|
VectorOfLinks::iterator vback = pCandidate->link.begin(),vbackend = pCandidate->link.end();
|
|
for (;vback!=vbackend;++vback)
|
|
{
|
|
if ( (*vback).pLink == pCurrent )
|
|
break;
|
|
}
|
|
|
|
// create the two new nodes
|
|
GraphNode *p1 = CreateNewNode();
|
|
GraphNode *p2 = CreateNewNode();
|
|
|
|
p1->vertex.push_back( pCurrent->vertex[(*vli).nStartIndex] );
|
|
p1->vertex.push_back( pCurrent->vertex[3 - ((*vli).nEndIndex+ (*vli).nStartIndex)] );
|
|
p1->vertex.push_back( pCandidate->vertex[3 - ((*vback).nEndIndex+ (*vback).nStartIndex)] );
|
|
FillGraphNodeData(p1);
|
|
|
|
p2->vertex.push_back( pCurrent->vertex[(*vli).nEndIndex] );
|
|
p2->vertex.push_back( pCurrent->vertex[3 - ((*vli).nEndIndex+ (*vli).nStartIndex)] );
|
|
p2->vertex.push_back( pCandidate->vertex[3 - ((*vback).nEndIndex+ (*vback).nStartIndex)] );
|
|
FillGraphNodeData(p2);
|
|
|
|
float p1Deg = p1->GetDegeneracyValue();
|
|
float p2Deg = p2->GetDegeneracyValue();
|
|
if ( (p1Deg>fDegen) && (p2Deg>fDegen) )
|
|
{
|
|
fDegen = (p1Deg<p2Deg)? p2Deg : p1Deg;
|
|
pPair=pCandidate;
|
|
}
|
|
|
|
Disconnect(p1);
|
|
Disconnect(p2);
|
|
}
|
|
}
|
|
|
|
if (pPair)
|
|
{
|
|
|
|
// find the link in the candidate that leads to the current node
|
|
vli = pCurrent->link.begin(),vliend = pCurrent->link.end();
|
|
for (;vli!=vliend;++vli)
|
|
{
|
|
if ( (*vli).pLink == pPair )
|
|
break;
|
|
}
|
|
|
|
VectorOfLinks::iterator vback = pPair->link.begin(),vbackend = pPair->link.end();
|
|
for (;vback!=vbackend;++vback)
|
|
{
|
|
if ( (*vback).pLink == pCurrent )
|
|
break;
|
|
}
|
|
|
|
// create the two new nodes
|
|
GraphNode *p1 = CreateNewNode();
|
|
GraphNode *p2 = CreateNewNode();
|
|
|
|
ListNodes lstNewNodes;
|
|
|
|
p1->vertex.push_back( pCurrent->vertex[(*vli).nStartIndex] );
|
|
p1->vertex.push_back( pCurrent->vertex[3 - ((*vli).nEndIndex+ (*vli).nStartIndex)] );
|
|
p1->vertex.push_back( pPair->vertex[3 - ((*vback).nEndIndex+ (*vback).nStartIndex)] );
|
|
FillGraphNodeData(p1);
|
|
|
|
p2->vertex.push_back( pCurrent->vertex[(*vli).nEndIndex] );
|
|
p2->vertex.push_back( pCurrent->vertex[3 - ((*vli).nEndIndex+ (*vli).nStartIndex)] );
|
|
p2->vertex.push_back( pPair->vertex[3 - ((*vback).nEndIndex+ (*vback).nStartIndex)] );
|
|
FillGraphNodeData(p2);
|
|
|
|
lstNewNodes.push_back(p1);
|
|
lstNewNodes.push_back(p2);
|
|
// push all neighbors of existing 2 triangles
|
|
vback = pPair->link.begin(),vbackend = pPair->link.end();
|
|
for (;vback!=vbackend;++vback)
|
|
{
|
|
if ( std::find(lstNewNodes.begin(),lstNewNodes.end(),(*vback).pLink)==lstNewNodes.end() )
|
|
{
|
|
if (( (*vback).pLink!=m_pSafeFirst) && ((*vback).pLink!=pCurrent))
|
|
lstNewNodes.push_back((*vback).pLink);
|
|
}
|
|
}
|
|
vback = pCurrent->link.begin(),vbackend = pCurrent->link.end();
|
|
for (;vback!=vbackend;++vback)
|
|
{
|
|
if ( std::find(lstNewNodes.begin(),lstNewNodes.end(),(*vback).pLink)==lstNewNodes.end())
|
|
{
|
|
if (( (*vback).pLink!=m_pSafeFirst) && ((*vback).pLink!=pPair) )
|
|
lstNewNodes.push_back((*vback).pLink);
|
|
}
|
|
}
|
|
|
|
|
|
Disconnect(pPair);
|
|
Disconnect(pCurrent);
|
|
|
|
ListNodes::iterator lif = std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pPair);
|
|
while (lif != m_lstNodeStack.end())
|
|
{
|
|
m_lstNodeStack.erase(lif);
|
|
lif = std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pPair);
|
|
}
|
|
lif = std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pCurrent);
|
|
while (lif != m_lstNodeStack.end())
|
|
{
|
|
m_lstNodeStack.erase(lif);
|
|
lif = std::find(m_lstNodeStack.begin(),m_lstNodeStack.end(),pCurrent);
|
|
}
|
|
|
|
ConnectNodes(lstNewNodes);
|
|
}
|
|
|
|
}
|
|
// find optimal rearrangement
|
|
// rearrange
|
|
}
|
|
|
|
ClearMarks();
|
|
}
|
|
|
|
void CGraph::DisableInSphere(const Vec3 &pos,float fRadius)
|
|
{
|
|
GetNodesInSphere(pos,fRadius);
|
|
ListNodes::iterator li = m_lstNodesInsideSphere.begin(),liend = m_lstNodesInsideSphere.end();
|
|
for (;li!=liend;++li)
|
|
{
|
|
GraphNode *pNode = (*li);
|
|
if (pNode->nBuildingID<0)
|
|
continue;
|
|
|
|
VectorOfLinks::iterator vli=pNode->link.begin(),vliend=pNode->link.end();
|
|
for (;vli!=vliend;++vli)
|
|
{
|
|
(*vli).fMaxRadius = -1; // forbid this link
|
|
|
|
GraphNode *pLink = (*vli).pLink;
|
|
VectorOfLinks::iterator bi=pLink->link.begin(),biend=pLink->link.end();
|
|
for (;bi!=biend;++bi)
|
|
{
|
|
if ((*bi).pLink == pNode)
|
|
{
|
|
(*bi).fMaxRadius = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CGraph::EnableInSphere(const Vec3 &pos,float fRadius)
|
|
{
|
|
GetNodesInSphere(pos,fRadius);
|
|
ListNodes::iterator li = m_lstNodesInsideSphere.begin(),liend = m_lstNodesInsideSphere.end();
|
|
for (;li!=liend;++li)
|
|
{
|
|
GraphNode *pNode = (*li);
|
|
if (pNode->nBuildingID<0)
|
|
continue;
|
|
|
|
VectorOfLinks::iterator vli=pNode->link.begin(),vliend=pNode->link.end();
|
|
for (;vli!=vliend;++vli)
|
|
{
|
|
(*vli).fMaxRadius = 100; // allow this link
|
|
|
|
GraphNode *pLink = (*vli).pLink;
|
|
VectorOfLinks::iterator bi=pLink->link.begin(),biend=pLink->link.end();
|
|
for (;bi!=biend;++bi)
|
|
{
|
|
if ((*bi).pLink == pNode)
|
|
{
|
|
(*bi).fMaxRadius = 100;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int CGraph::GetNodesInSphere(const Vec3 &pos, float fRadius)
|
|
{
|
|
ClearTagsNow();
|
|
m_lstNodesInsideSphere.clear();
|
|
GraphNode *pNode = GetEnclosing(pos);
|
|
|
|
if (pNode->nBuildingID<0)
|
|
return 0;
|
|
|
|
|
|
m_lstNodesInsideSphere.push_front(pNode);
|
|
while(m_lstNodesInsideSphere.front()->tag == false)
|
|
{
|
|
|
|
ListNodes::iterator li = m_lstNodesInsideSphere.begin(),liend = m_lstNodesInsideSphere.end();
|
|
for (;li!=liend;)
|
|
{
|
|
ListNodes::iterator linext = li;
|
|
++linext;
|
|
if (linext!=liend)
|
|
{
|
|
if ((*linext)->tag)
|
|
break;
|
|
}
|
|
++li;
|
|
}
|
|
|
|
if (li!=liend)
|
|
pNode = (*li);
|
|
TagNode(pNode);
|
|
|
|
VectorOfLinks::iterator vli=pNode->link.begin(),vliend=pNode->link.end();
|
|
for (;vli!=vliend;++vli)
|
|
{
|
|
GraphNode *pLink = (*vli).pLink;
|
|
if (pLink->tag)
|
|
continue;
|
|
if ( GetLength(pLink->data.m_pos-pos) < fRadius )
|
|
{
|
|
m_lstNodesInsideSphere.push_front(pLink);
|
|
}
|
|
}
|
|
}
|
|
|
|
ClearTagsNow();
|
|
return m_lstNodesInsideSphere.size();
|
|
|
|
}
|