//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2001. // ------------------------------------------------------------------------- // File name: AIPoint.cpp // Version: v1.00 // Created: 10/10/2001 by Timur. // Compilers: Visual C++ 6.0 // Description: CAIPoint implementation. // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "AIPoint.h" #include "ErrorReport.h" #include "..\AIPointPanel.h" #include #include "..\Viewport.h" #include "Settings.h" #include //#include #include #define AIWAYPOINT_RADIUS 0.3f #define DIR_VECTOR Vec3(0,-1,0) ////////////////////////////////////////////////////////////////////////// // CBase implementation. ////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNCREATE(CAIPoint,CBaseObject) int CAIPoint::m_rollupId = 0; CAIPointPanel* CAIPoint::m_panel = 0; float CAIPoint::m_helperScale = 1; ////////////////////////////////////////////////////////////////////////// CAIPoint::CAIPoint() { m_aiNode = 0; m_bIndoorEntrance = false; m_bIndoors = false; m_aiType = EAIPOINT_WAYPOINT; m_bLinksValid = false; m_bIgnoreUpdateLinks = false; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::Done() { m_bLinksValid = false; if (m_aiNode) { IGraph *aiGraph = GetIEditor()->GetSystem()->GetAISystem()->GetNodeGraph(); if (aiGraph) { aiGraph->Disconnect(m_aiNode,false); if (m_bIndoorEntrance) { aiGraph->RemoveEntrance( m_aiNode->nBuildingID,m_aiNode ); } } m_aiNode = 0; } while (!m_links.empty()) { CAIPoint *obj = GetLink(0); if (obj) RemoveLink( obj ); else m_links.erase(m_links.begin()); } CBaseObject::Done(); } ////////////////////////////////////////////////////////////////////////// bool CAIPoint::Init( IEditor *ie,CBaseObject *prev,const CString &file ) { m_bLinksValid = false; SetColor( RGB(0,128,255) ); bool res = CBaseObject::Init( ie,prev,file ); if (IsCreateGameObjects()) if (m_aiType != EAIPOINT_HIDE) { // Create AI graph node in game. IGraph *aiGraph = GetIEditor()->GetSystem()->GetAISystem()->GetNodeGraph(); if (aiGraph) m_aiNode = aiGraph->CreateNewNode(); } if (prev) { CAIPoint *pOriginal = (CAIPoint *)prev; SetAIType(pOriginal->m_aiType); } return res; } ////////////////////////////////////////////////////////////////////////// CString CAIPoint::GetTypeDescription() const { if (m_aiType == EAIPOINT_HIDE) { return CString(GetClassDesc()->ClassName()) + "(Hide)"; } else if (m_aiType == EAIPOINT_ENTRY) { return CString(GetClassDesc()->ClassName()) + "(Entry)"; } else if (m_aiType == EAIPOINT_EXIT) { return CString(GetClassDesc()->ClassName()) + "(Exit)"; } return GetClassDesc()->ClassName(); }; ////////////////////////////////////////////////////////////////////////// float CAIPoint::GetRadius() { return AIWAYPOINT_RADIUS*m_helperScale * gSettings.gizmo.helpersScale; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::SetPos( const Vec3d &pos ) { Vec3d oldpos = GetWorldPos(); CBaseObject::SetPos( pos ); /* if (m_aiNode) { m_aiNode->data.m_pos = pos; m_buildingId = -1; m_bIndoors = false; IAISystem *pAISystem = GetIEditor()->GetSystem()->GetAISystem(); int buildingId;IVisArea *pArea; if (pAISystem && pAISystem->CheckInside(pos,buildingId,pArea)) { m_aiNode->nBuildingID = buildingId; m_aiNode->pArea = pArea; if (m_buildingId != buildingId) m_bLinksValid = false; m_buildingId = buildingId; m_pArea= pArea; m_bIndoors = true; } } else { m_bIndoors = false; IAISystem *pAISystem = GetIEditor()->GetSystem()->GetAISystem(); if (!pAISystem) return; IGraph *pGraph = pAISystem->GetNodeGraph(); if (!pGraph) return; int buildingId;IVisArea *pArea; if (pAISystem->CheckInside(pos,buildingId,pArea)) { if (m_buildingId != buildingId) m_bLinksValid = false; m_buildingId = buildingId; m_pArea= pArea; m_bIndoors = true; } // for hide points int numLinks = m_links.size(); for (int i = 0; i < numLinks; i++) { // go trough all the links in the waypoint CAIPoint *pnt = GetLink(i); if (!pnt || !pnt->m_aiNode) continue; Matrix44 m; m.SetIdentity(); //m.RotateMatrix_fix(GetAngles()); m=GetRotationZYX44(-GetAngles()*gf_DEGTORAD)*m; //NOTE: angles in radians and negated //CHANGED_BY_IVO //Vec3 dir = m.TransformVector(DIR_VECTOR); Vec3 dir = GetTransposed44(m) * (DIR_VECTOR); pGraph->RemoveHidePoint(pnt->m_aiNode,oldpos,dir); pGraph->AddHidePoint(pnt->m_aiNode,pos,dir); // and update the point there } } */ } ////////////////////////////////////////////////////////////////////////// void CAIPoint::SetAngles( const Vec3d &angles ) { //if (m_aiType == EAIPOINT_HIDE) { CBaseObject::SetAngles(angles); } } void CAIPoint::SetScale( const Vec3d &angles ) { } ////////////////////////////////////////////////////////////////////////// bool CAIPoint::HitTest( HitContext &hc ) { Vec3 origin = GetWorldPos(); float radius = GetRadius(); Vec3 w = origin - hc.raySrc; w = hc.rayDir.Cross( w ); float d = w.GetLengthSquared(); if (d < radius*radius + hc.distanceTollerance) { Vec3 i0; if (Intersect::Ray_SphereFirst( Ray(hc.raySrc,hc.rayDir),Sphere(origin,radius),i0)) { hc.dist = GetDistance(hc.raySrc,i0); return true; } hc.dist = GetDistance(hc.raySrc,origin); return true; } return false; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::GetBoundBox( BBox &box ) { Vec3 pos = GetWorldPos(); float r = GetRadius(); box.min = pos - Vec3(r,r,r); box.max = pos + Vec3(r,r,r); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::GetLocalBounds( BBox &box ) { float r = GetRadius(); box.min = -Vec3(r,r,r); box.max = Vec3(r,r,r); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::BeginEditParams( IEditor *ie,int flags ) { CBaseObject::BeginEditParams( ie,flags ); // Unselect all links. for (int i = 0; i < GetLinkCount(); i++) SelectLink(i,false); if (!m_panel) { m_panel = new CAIPointPanel; m_rollupId = ie->AddRollUpPage( ROLLUP_OBJECTS,"AIPoint Parameters",m_panel ); } if (m_panel) m_panel->SetObject( this ); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::EndEditParams( IEditor *ie ) { if (m_panel) { ie->RemoveRollUpPage( ROLLUP_OBJECTS,m_rollupId ); } m_rollupId = 0; m_panel = 0; // Unselect all links. for (int i = 0; i < GetLinkCount(); i++) SelectLink(i,false); CBaseObject::EndEditParams( ie ); } ////////////////////////////////////////////////////////////////////////// int CAIPoint::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags ) { if (event == eMouseMove || event == eMouseLDown) { Vec3 pos; if (GetIEditor()->GetAxisConstrains() != AXIS_TERRAIN) { pos = view->MapViewToCP(point); } else { // Snap to terrain. bool hitTerrain; pos = view->ViewToWorld( point,&hitTerrain ); if (hitTerrain) { pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y) + GetRadius(); } pos = view->SnapToGrid(pos); } SetPos( pos ); if (event == eMouseLDown) return MOUSECREATE_OK; return MOUSECREATE_CONTINUE; } return CBaseObject::MouseCreateCallback( view,event,point,flags ); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::Display( DisplayContext &dc ) { COLORREF color = GetColor(); COLORREF clrSelectedLink = GetColor(); if (!m_bIndoors) { // Draw invalid node. color = RGB(255,0,0); } Vec3 wp = GetWorldPos(); if (m_aiType == EAIPOINT_HIDE) { color = RGB(0,255,0); //<> Timur solve this in a better way. if (IsSelected()) dc.SetSelectedColor(); else dc.SetColor( color,1 ); //Vec3 arrowTrg = GetWorldTM().TransformPoint(0.5f*Vec3d(0,-1,0)); //dc.DrawArrow(wp,arrowTrg); Matrix44 tm = GetWorldTM(); float sz = m_helperScale * gSettings.gizmo.helpersScale; tm.ScaleMatRow( Vec3(sz,sz,sz) ); dc.RenderObject( STATOBJECT_HIDEPOINT,tm ); } else if (m_aiType == EAIPOINT_ENTRY || m_aiType == EAIPOINT_EXIT) { if (IsSelected()) dc.SetSelectedColor(); else dc.SetColor( color,1 ); Matrix44 tm = GetWorldTM(); float sz = m_helperScale * gSettings.gizmo.helpersScale; tm.ScaleMatRow( Vec3(sz,sz,sz) ); dc.RenderObject( STATOBJECT_ENTRANCE,GetWorldTM() ); } else { dc.SetColor( color,1 ); dc.DrawBall( wp,GetRadius() ); if (IsSelected()) { dc.SetSelectedColor( 0.3f ); dc.renderer->DrawBall( wp,GetRadius()+0.1f ); } } int numLinks = m_links.size(); for (int i = 0; i < numLinks; i++) { CAIPoint *pnt = GetLink(i); if (!pnt) continue; if (m_links[i].selected) { dc.SetSelectedColor(); dc.DrawLine( wp+Vec3(0,0,0.05f),pnt->GetWorldPos()+Vec3(0,0,0.05f) ); } else { if (pnt->m_aiType == EAIPOINT_HIDE) color = RGB(0,255,0); dc.SetColor( color ); dc.DrawLine( wp,pnt->GetWorldPos() ); } } DrawDefault( dc ); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::Serialize( CObjectArchive &ar ) { m_bIgnoreUpdateLinks = true; CBaseObject::Serialize( ar ); m_bIgnoreUpdateLinks = false; XmlNodeRef xmlNode = ar.node; if (ar.bLoading) { m_bIgnoreUpdateLinks = true; int aiType = 0; xmlNode->getAttr( "AIType",aiType ); // Load Links. m_links.clear(); XmlNodeRef links = xmlNode->findChild( "Links" ); if (links) { m_bLinksValid = false; for (int i = 0; i < links->getChildCount(); i++) { XmlNodeRef pnt = links->getChild(i); Link link; link.object = 0; pnt->getAttr( "Id",link.id ); m_links.push_back(link); } } SetAIType((EAIPointType)aiType); m_bIgnoreUpdateLinks = false; if (ar.bUndo) UpdateLinks(); } else { xmlNode->setAttr( "AIType",(int)m_aiType ); // Save Links. if (!m_links.empty()) { XmlNodeRef links = xmlNode->newChild( "Links" ); for (int i = 0; i < m_links.size(); i++) { XmlNodeRef pnt = links->newChild( "Link" ); CAIPoint* link = GetLink(i); if (!link) continue; pnt->setAttr( "Id",link->GetId() ); } } } } ////////////////////////////////////////////////////////////////////////// XmlNodeRef CAIPoint::Export( const CString &levelPath,XmlNodeRef &xmlNode ) { /* XmlNodeRef objNode = CBaseObject::Export( levelPath,xmlNode ); objNode->setAttr( "Id",GetId() ); // Save Links. if (!m_links.empty()) { XmlNodeRef links = xmlNode->newChild( "Links" ); for (int i = 0; i < m_links.size(); i++) { XmlNodeRef pnt = links->newChild( "Link" ); pnt->setAttr( "Id",m_links[i].id ); } } return CBaseObject::Export( levelPath,xmlNode ); */ return 0; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::OnEvent( ObjectEvent event ) { switch (event) { //case EVENT_AFTER_LOAD: // After all objects loaded. case EVENT_REFRESH: // when refreshing level. // Recalculate indoors. SetPos( GetPos() ); // After loading reconnect all links. UpdateLinks(); break; case EVENT_CLEAR_AIGRAPH: m_aiNode = 0; break; case EVENT_MISSION_CHANGE: { // After mission have been changed, AI graph invalidates all pointers, create them again. // Create AI graph node in game. IGraph *aiGraph = GetIEditor()->GetSystem()->GetAISystem()->GetNodeGraph(); if (aiGraph) { m_bLinksValid = false; if (m_aiType != EAIPOINT_HIDE) { m_aiNode = aiGraph->CreateNewNode(); } UpdateLinks(); } } break; } } ////////////////////////////////////////////////////////////////////////// CAIPoint* CAIPoint::GetLink( int index ) { assert( index >= 0 && index < m_links.size() ); if (!m_links[index].object) { CBaseObject *obj = FindObject(m_links[index].id); if (obj && obj->IsKindOf(RUNTIME_CLASS(CAIPoint))) { ((CAIPoint*)obj)->AddLink( this,false ); m_links[index].object = (CAIPoint*)obj; } } return m_links[index].object; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::AddLink( CAIPoint* obj,bool bNeighbour ) { if (obj == this) return; GUID id = obj->GetId(); for (int i = 0; i < m_links.size(); i++) { if (m_links[i].object == obj || m_links[i].id == id) return; } if (m_aiType == EAIPOINT_HIDE) { if (obj->m_aiType == EAIPOINT_HIDE) { Warning( _T("Cannot connect hide spots") ); return; } } Link link; link.object = obj; link.id = id; m_links.push_back( link ); m_bLinksValid = false; if (!bNeighbour) { obj->AddLink( this,true ); UpdateLinks(); } else if (m_aiType == EAIPOINT_HIDE) { UpdateLinks(); } } ////////////////////////////////////////////////////////////////////////// void CAIPoint::RemoveLink( CAIPoint* obj,bool bNeighbour ) { if (obj == this || obj == 0) return; if (m_aiType == EAIPOINT_HIDE && obj->m_aiNode) { IAISystem *aiSystem = GetIEditor()->GetSystem()->GetAISystem(); IGraph *aiGraph = aiSystem->GetNodeGraph(); if (aiGraph) { aiGraph->RemoveHidePoint(obj->m_aiNode,m_currHidePos,m_currHideDir ); } } int index = -1; GUID id = obj->GetId(); for (int i = 0; i < m_links.size(); i++) { if (m_links[i].object == obj || m_links[i].id == id) { index = i; break; } } if (index < 0) return; m_links.erase( m_links.begin() + index ); m_bLinksValid = false; if (!bNeighbour) { obj->RemoveLink( this,true ); UpdateLinks(); } } ////////////////////////////////////////////////////////////////////////// void CAIPoint::UpdateLinks() { if (m_bLinksValid || m_bIgnoreUpdateLinks) return; IAISystem *aiSystem = GetIEditor()->GetSystem()->GetAISystem(); if (!aiSystem) return; IGraph *aiGraph = aiSystem->GetNodeGraph(); if (!aiGraph) return; m_bIndoors = false; if (m_aiType != EAIPOINT_HIDE) { if (!m_aiNode) return; IVisArea *pArea = NULL; int nBuildingId = -1; Vec3 wp = GetWorldPos(); if (aiSystem->CheckInside(wp,nBuildingId,pArea)) { m_bIndoors = true; } aiGraph->Disconnect(m_aiNode,false); if (m_bIndoorEntrance) { aiGraph->RemoveEntrance( m_aiNode->nBuildingID,m_aiNode ); m_bIndoorEntrance = false; } m_aiNode->nBuildingID = nBuildingId; m_aiNode->pArea = m_pArea; m_aiNode->data.m_pos = wp; if (m_aiType == EAIPOINT_ENTRY && m_aiNode->nBuildingID >= 0) { aiGraph->AddIndoorEntrance( m_aiNode->nBuildingID,m_aiNode ); m_bIndoorEntrance = true; } else if (m_aiType == EAIPOINT_EXIT && m_aiNode->nBuildingID >= 0) { aiGraph->AddIndoorEntrance( m_aiNode->nBuildingID,m_aiNode,true); m_bIndoorEntrance = true; } int numLinks = GetLinkCount(); for (int i = 0; i < numLinks; i++) { CAIPoint *lnk = GetLink(i); if (!lnk) continue; if (!lnk->m_aiNode) { if (lnk->m_aiType == EAIPOINT_HIDE) { Vec3 wp = lnk->GetWorldPos(); Matrix44 m = lnk->GetWorldTM(); m.NoScale(); Vec3 dir = m.TransformVectorOLD(DIR_VECTOR); aiGraph->AddHidePoint(m_aiNode,wp,dir ); } } else { aiGraph->Connect(m_aiNode,lnk->m_aiNode); } } } else { Vec3 wp = GetWorldPos(); Matrix44 m = GetWorldTM(); m.NoScale(); Vec3 dir = m.TransformVectorOLD(DIR_VECTOR); int numLinks = GetLinkCount(); for (int i = 0; i < numLinks; i++) { CAIPoint *lnk = GetLink(i); if (!lnk || !lnk->m_aiNode) continue; aiGraph->RemoveHidePoint( lnk->m_aiNode,m_currHidePos,m_currHideDir ); aiGraph->AddHidePoint(lnk->m_aiNode,wp,dir ); } m_currHidePos = wp; m_currHideDir = dir; } m_bLinksValid = true; } ////////////////////////////////////////////////////////////////////////// void CAIPoint::SetAIType( EAIPointType type ) { if (type == m_aiType) return; StoreUndo( "Set AIPoint Type" ); EAIPointType oldType = m_aiType; m_aiType = type; if (m_aiType == EAIPOINT_HIDE) { IAISystem *aiSystem = GetIEditor()->GetSystem()->GetAISystem(); IGraph *aiGraph = aiSystem->GetNodeGraph(); if (m_aiNode && aiGraph) { //aiGraph->DeleteNode(m_aiNode); aiGraph->Disconnect(m_aiNode); m_aiNode = 0; } } else if (!m_aiNode) { IAISystem *aiSystem = GetIEditor()->GetSystem()->GetAISystem(); IGraph *aiGraph = aiSystem->GetNodeGraph(); if (aiGraph) m_aiNode = aiGraph->CreateNewNode(); } m_bLinksValid = false; UpdateLinks(); } ////////////////////////////////////////////////////////////////////////// //! Invalidates cached transformation matrix. void CAIPoint::InvalidateTM() { CBaseObject::InvalidateTM(); m_bLinksValid = false; UpdateLinks(); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::StartPick() { if (m_panel) m_panel->StartPick(); } ////////////////////////////////////////////////////////////////////////// void CAIPoint::Validate( CErrorReport *report ) { CBaseObject::Validate( report ); if (!m_bIndoors && m_aiType != EAIPOINT_HIDE) { CErrorRecord err; err.error.Format( "AI Point is not valid %s (Must be indoors)",(const char*)GetName() ); err.pObject = this; err.severity = CErrorRecord::ESEVERITY_WARNING; err.flags = CErrorRecord::FLAG_AI; report->ReportError(err); } }