#include "stdafx.h" #include "TerrainMapEngine.h" #include "TerrainSeamlessWorldInfoForClient.h" #include "TerrainAttributeInfoForMap.h" #include #include #include "SDebug_Util.h" /// 2라는 수치는 기존에 길찾기 할 때 쓰이던 수치이다. int CTerrainMapEngine::MIN_ATTRIBUTE_COLLISION_LENGTH = 2; //void CTerrainMapEngine::CreateAttributePolygonInfoForMap( const char* szAttributePolygonFileName, int nMapX, int nMapY ) void CTerrainMapEngine::CreateAttributePolygonInfoForMap( const K3DVector& vStart, const K3DVector& vEnd ) { float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nStartMapX = (int)(vStart.x / fMapLen); int nStartMapY = (int)(vStart.y / fMapLen); int nEndMapX = (int)(vEnd.x / fMapLen); int nEndMapY = (int)(vEnd.y / fMapLen); int nTemp; if(nStartMapX > nEndMapX) { nTemp = nEndMapX; nEndMapX = nStartMapX; nStartMapX = nTemp; } if(nStartMapY > nEndMapY) { nTemp = nEndMapY; nEndMapY = nStartMapY; nStartMapY = nTemp; } assert( ( nStartMapX >= 0 && nStartMapY >= 0 ) && "Map Index Wrong!!!" ); int numMapsToLoad = (nEndMapX - nStartMapX + 1) * (nEndMapY - nStartMapY + 1); int numAttributeInfos = m_vAttributeInfos.size(); bool bNeedLoading = false; int nRes = 0; //if(numAttributeInfos != numMapsToLoad) //{ // bNeedLoading = true; //} if(numAttributeInfos < numMapsToLoad) { bNeedLoading = true; } else { //for(int i = 0; i < numMapsToLoad; i++) for(int i = 0; i < numAttributeInfos; i++) { for(int j = nStartMapY; j <= nEndMapY; j++) { for(int k = nStartMapX; k <= nEndMapX; k++) { if(!m_vAttributeInfos[i]->CheckMap(k, j)) ++nRes; } } } if(numMapsToLoad != nRes) bNeedLoading = true; } std::string strFileName; if(bNeedLoading) { SAFE_DELETE_VECTOR(m_vAttributeInfos); //SAFE_DELETE_VECTOR(m_vAttributePolygons); DeleteAttrQuadTree(); //if(m_bDrawAttributeWire) m_prWireAttributePolygon->Clear(); if(m_bCreateAttributeWire) m_prWireAttributePolygon->Clear(); for(int i = nStartMapY; i <= nEndMapY; i++) { for(int j = nStartMapX; j <= nEndMapX; j++) { strFileName = m_pSeamlessWorldInfo->GetAttributePolygonFileName( j, i ); if( strFileName.length() <= 0 ) continue; //CTerrainAttributeInfoForMap* pAttrubiteInfo = new CTerrainAttributeInfoForMap( this, strFileName.c_str(), j, i, m_bDrawAttributeWire ); CTerrainAttributeInfoForMap* pAttrubiteInfo = new CTerrainAttributeInfoForMap( this, strFileName.c_str(), j, i, m_bCreateAttributeWire ); m_vAttributeInfos.push_back(pAttrubiteInfo); } } } //if( m_pCurAttributePolygonInfo == NULL ) //{ // m_pCurAttributePolygonInfo = new CTerrainAttributeInfoForMap( this, szAttributePolygonFileName, nMapX, nMapY, m_bDrawAttributeWire ); //} //else //{ // if( m_pCurAttributePolygonInfo->CheckMap(nMapX, nMapY)) // { // SAFE_DELETE(m_pCurAttributePolygonInfo); // SAFE_DELETE_VECTOR(m_vAttributePolygons); // if(m_bDrawAttributeWire) m_prWireAttributePolygon->Clear(); // m_pCurAttributePolygonInfo = new CTerrainAttributeInfoForMap( this, szAttributePolygonFileName, nMapX, nMapY, m_bDrawAttributeWire ); // } //} //if( m_pCurAttributePolygonInfo == NULL ) //{ // m_pCurAttributePolygonInfo = new CTerrainAttributeInfoForMap( this, szAttributePolygonFileName, nMapX, nMapY, m_bDrawAttributeWire ); //} //else //{ // if( m_pCurAttributePolygonInfo->CheckMap(nMapX, nMapY)) // { // SAFE_DELETE(m_pCurAttributePolygonInfo); // SAFE_DELETE_VECTOR(m_vAttributePolygons); // // if(m_bDrawAttributeWire) m_prWireAttributePolygon->Clear(); // m_pCurAttributePolygonInfo = new CTerrainAttributeInfoForMap( this, szAttributePolygonFileName, nMapX, nMapY, m_bDrawAttributeWire ); // } //} if( m_bDrawAttributeWire ) { m_prWireQuadTree->Clear(); struct QuadTreeSegmentCollector { void operator()( const attribute_point_quadtree_t::Node& node ) { mSegments.push_back( node.GetEffectiveArea() ); } std::vector< X2D::Rect< int > > mSegments; }; QuadTreeSegmentCollector Collector; m_AttrPointQuadTree.Iterate( Collector ); if( !Collector.mSegments.empty() ) { K3DVector v1, v2, v3, v4; WORD tile = 0; for( std::vector< X2D::Rect< int > >::iterator it = Collector.mSegments.begin(); it != Collector.mSegments.end(); ++it ) { X2D::Rect< int >& Area = (*it); v1.x = (K3DVALUE)Area.GetLeft(); v1.y = (K3DVALUE)Area.GetTop(); v1.z = 15.f; v2.x = (K3DVALUE)Area.GetRight(); v2.y = (K3DVALUE)Area.GetTop(); v2.z = 15.f; v3.x = (K3DVALUE)Area.GetRight(); v3.y = (K3DVALUE)Area.GetBottom(); v3.z = 15.f; v4.x = (K3DVALUE)Area.GetLeft(); v4.y = (K3DVALUE)Area.GetBottom(); v4.z = 15.f; m_prWireQuadTree->AddLine( v1, v2, KColor( 0, 255, 0, 255 ) ); m_prWireQuadTree->AddLine( v2, v3, KColor( 0, 255, 0, 255 ) ); m_prWireQuadTree->AddLine( v3, v4, KColor( 0, 255, 0, 255 ) ); m_prWireQuadTree->AddLine( v4, v1, KColor( 0, 255, 0, 255 ) ); } } } } bool CTerrainMapEngine::CollisionToLine( const K3DVector& vStart, const K3DVector& vEnd ) { X2D::Line line( vStart.x, vStart.y, vEnd.x, vEnd.y ); if( m_AttrPolygonsQuadTree.Collision( line ) == false ) { return false; } // 끝지점이 못가는 지역이라면 일단 허용한다. X2D::Point ptStart( vStart.x, vStart.y ); X2D::Point ptEnd( vEnd.x, vEnd.y ); if( m_AttrPolygonsQuadTree.Collision( ptStart ) == false && m_AttrPolygonsQuadTree.Collision( ptEnd ) == true ) { return false; } return true; } void CTerrainMapEngine::FindDetour( const K3DVector& vStart, const K3DVector& vEnd, PATH& vPath, bool bRestraint ) { CreateAttributePolygonInfoForMap(vStart, vEnd); X2D::Point ptStart, ptEnd; ptStart.x = static_cast(vStart.x); ptStart.y = static_cast(vStart.y); ptEnd.x = static_cast(vEnd.x); ptEnd.y = static_cast(vEnd.y); static size_t s_unLimit = 500; typedef X2D::IndexedPoint< int > indexed_point_t; typedef X2D::LeakyPolygon< int > leaky_polygon_t; typedef std::vector< indexed_point_t* > indexed_point_vector_t; typedef std::vector< leaky_polygon_t* > leaky_polygon_vector_t; indexed_point_vector_t vIndexedPoints; leaky_polygon_vector_t vLeakyPolygons; // ptStart와 ptEnd를 포함하는 직각 삼각형을 만든다. X2D::Point ptRegion[3]; ptRegion[0] = ptStart - ptEnd; //각도 제한 크기를 두배로 확대 시켜 약화 ptRegion[0].x = ptRegion[0].x*2; ptRegion[0].y = ptRegion[0].y*2; ptRegion[1].x = -ptRegion[0].y; ptRegion[1].y = ptRegion[0].x; ptRegion[2].x = ptRegion[0].y; ptRegion[2].y = -ptRegion[0].x; ptRegion[0] += ptEnd; ptRegion[1] += ptEnd; ptRegion[2] += ptEnd; X2D::Polygon plRegion((X2D::Point*) ptRegion, ((X2D::Point*) ptRegion) + 3); const int c_nOfsLimit = 10; bool bBreak = false; { X2D::Line line( vStart.x, vStart.y, vEnd.x, vEnd.y ); if( m_AttrPolygonsQuadTree.Collision( line ) == false ) { vPath.push_back(ptStart); vPath.push_back(ptEnd); goto _DRAWWIRE; } } /// 2012.02.22 길찾기 블럭을 임의로 추가하는 테스트 코드 - prodongi #ifdef KMOVE_DEBUG static bool testAdd = false; static bool testDel = false; /// x0 y0 x1 y1 x2 y2 static int cl[3][10] = { { 7037, 6691, 6917, 6967, 6942, 6979, 7061, 6714, 7037, 6691 }, { 6942, 6979, 6808, 6682, 6786, 6719, 6917, 6967, 6942, 6979 }, { 6786, 6719, 7061, 6714, 7037, 6691, 6808, 6682, 6786, 6719 } }; static X2D::IndexedPoint< int >* testP[3] = { NULL, NULL, NULL }; if (testAdd) { testAdd = false; if (!testP[0]) { for (int i = 0; i < 3; ++i) { X2D::Point* p = new X2D::Point[5]; p[0].Set(cl[i][0], cl[i][1]); p[1].Set(cl[i][2], cl[i][3]); p[2].Set(cl[i][4], cl[i][5]); p[3].Set(cl[i][6], cl[i][7]); p[4].Set(cl[i][8], cl[i][9]); X2D::Polygon* polygon = new X2D::Polygon; if (polygon->Set(p, p+3)) { int index = 0; testP[i] = new X2D::IndexedPoint(polygon, index); m_AttrPointQuadTree.DuplicateAdd(testP[i], X2D::IndexedPoint< int >::InputQuadTree()); } } } } if (testDel) { testDel = false; if (testP[0]) { for (int i = 0; i < 3; ++i) { X2D::Point t(cl[i][0], cl[i][1]); m_AttrPointQuadTree.Remove(t); SAFE_DELETE(testP[i]); } } } #endif if( m_AttrPolygonsQuadTree.Collision( ptStart ) ) { X2D::Point< int > ptTemp = ptStart; for( int i = 0; i <= c_nOfsLimit; i++ ) { if( bBreak ) break; for( int nY = -i; nY <= i; nY++ ) { if( bBreak ) break; for( int nX = -i; nX <= i; nX++ ) { if( bBreak ) break; if( nY != -i && nY != i && nX != -i && nX != i ) continue; ptTemp = ptStart; ptTemp.x += nX; ptTemp.y += nY; if( m_AttrPolygonsQuadTree.Collision( ptTemp ) ) { if( i == c_nOfsLimit && nY == c_nOfsLimit && nX == c_nOfsLimit ) { assert( 0 ); ptTemp = ptStart; } } else { bBreak = true; } } } } ptStart = ptTemp; } // // 디버그용 코드 by blackfish -> //X2D::Point< int > ptTemp( 164148, 52534 ); //if( !m_AttrPointQuadTree.Collision( ptTemp ) ) //{ // assert( 0 ); //} //// <- 디버그용 코드 by blackfish //// 디버그용 코드 by blackfish -> ////X2D::Line< int > theLine( ptStart.x, ptStart.y, ptEnd.x, ptEnd.y ); //X2D::Box< int > theBox( ptStart, ptEnd ); ////m_AttrPointQuadTree.Enum( theLine, &vIndexedPoints ); //m_AttrPointQuadTree.Enum( theBox, &vIndexedPoints ); //_oprint( "Start Point\n"); //_oprint( "X : %d, Y : %d\n", ptStart.x, ptStart.y ); //_oprint( "End Point\n"); //_oprint( "X : %d, Y : %d\n", ptEnd.x, ptEnd.y ); //_oprint( "Number of Polygons : %d\n", vIndexedPoints.size() ); //for( size_t i = 0; i < vIndexedPoints.size(); i++ ) //{ // _oprint( "Polygon %d\n", i ); // _oprint( "Number of Points : %d\n", vIndexedPoints[ i ]->Size() ); // for( size_t j = 0; j < vIndexedPoints[ i ]->Size(); j++ ) // { // _oprint( "Point %d\n", j ); // _oprint( "X : %d, Y : %d\n", vIndexedPoints[ i ]->GetPoint( j ).x, vIndexedPoints[ i ]->GetPoint( j ).y ); // } //} //vIndexedPoints.erase( vIndexedPoints.begin(), vIndexedPoints.end() ); //// <- 디버그용 코드 by blackfish if(m_AttrPolygonsQuadTree.Collision(ptEnd)) { if(vPath.size() > s_unLimit) vPath.clear(); else vPath.erase(vPath.begin(), vPath.end()); X2D::Point ptThePoint; int intersectDistance; if( getIntersectInfoNearbyEndPoint( ptStart, ptEnd, ptThePoint, intersectDistance ) == true ) { ptEnd = ptThePoint; } else { X2D::Point< int > ptTemp = ptEnd; for( int i = 0; i <= c_nOfsLimit; i++ ) { if( bBreak ) break; for( int nY = -i; nY <= i; nY++ ) { if( bBreak ) break; for( int nX = -i; nX <= i; nX++ ) { if( bBreak ) break; if( nY != -i && nY != i && nX != -i && nX != i ) continue; ptTemp = ptEnd; ptTemp.x += nX; ptTemp.y += nY; if( m_AttrPolygonsQuadTree.Collision( ptTemp ) ) { if( i == c_nOfsLimit && nY == c_nOfsLimit && nX == c_nOfsLimit ) { assert( 0 ); ptTemp = ptEnd; } } else { bBreak = true; } } } } ptEnd = ptTemp; } } if( plRegion.Size() >= 3 ) { if( bRestraint ) { m_AttrPointQuadTree.EnumLoose( plRegion, &vIndexedPoints ); } else { m_AttrPointQuadTree.Enum( &vIndexedPoints ); } } if(vPath.size() > s_unLimit) vPath.clear(); //else vPath.erase(vPath.begin(), vPath.end()); // Find에서 해주니까 필요없다. // make leaky polygon resource ------------------------------------------------------------------------------------------------- { std::sort( vIndexedPoints.begin(), vIndexedPoints.end(), indexed_point_t::Less() ); vIndexedPoints.erase( std::unique( vIndexedPoints.begin(), vIndexedPoints.end() ), vIndexedPoints.end() ); indexed_point_vector_t::iterator itPt = vIndexedPoints.begin(), endPt = vIndexedPoints.end(); std::vector< indexed_point_t > vPtBuffer; xmod::cyclic_index< int > CurrGlobalIndex( 0, vIndexedPoints.size() ); xmod::cyclic_index< int > NextGlobalIndex( 0, vIndexedPoints.size() ); indexed_point_vector_t LocalPoints; for( ; itPt != endPt; ++itPt, ++CurrGlobalIndex ) { NextGlobalIndex = CurrGlobalIndex + 1; indexed_point_t* pCurrGlobalPoint = *itPt; indexed_point_t* pNextGlobalPoint = vIndexedPoints.at( NextGlobalIndex ); LocalPoints.push_back( pCurrGlobalPoint ); if( !pNextGlobalPoint->hasSameOwner( *pCurrGlobalPoint ) || pCurrGlobalPoint == pNextGlobalPoint || CurrGlobalIndex == CurrGlobalIndex.getMax() ) { // end of point list which has a same owner // let's find hole in the point list const size_t LocalSize = LocalPoints.size(); bool FoundHole = false; xmod::cyclic_index< int > CurrIndex( 0, LocalSize ); xmod::cyclic_index< int > PrevIndex( 0, LocalSize ); CurrIndex = CurrIndex.getMax(); for( size_t index = 0; index < LocalSize; ++index, --CurrIndex ) { PrevIndex = CurrIndex - 1; if( !LocalPoints.at( CurrIndex )->isPrevLinked( *LocalPoints.at( PrevIndex ) ) ) { FoundHole = true; break; } } if( FoundHole ) { // we found a hole in the point list, the index of hole is CurrIndex. xmod::cyclic_index< int > NextIndex( 0, LocalSize ); for( size_t index = 0; index < LocalSize; ++index, ++CurrIndex ) { NextIndex = CurrIndex + 1; indexed_point_t* pCurrPoint = LocalPoints.at( CurrIndex ); indexed_point_t* pNextPoint = LocalPoints.at( NextIndex ); vPtBuffer.push_back( *pCurrPoint ); if( !pNextPoint->isPrevLinked( *pCurrPoint ) ) { // found a dead end vLeakyPolygons.push_back( new leaky_polygon_t( vPtBuffer.begin(), vPtBuffer.end() ) ); vPtBuffer.clear(); } } } else { for( size_t index = 0; index < LocalSize; ++index ) vPtBuffer.push_back( *LocalPoints.at( index ) ); vLeakyPolygons.push_back( new leaky_polygon_t( vPtBuffer.begin(), vPtBuffer.end() ) ); vPtBuffer.clear(); } LocalPoints.clear(); } } } // ----------------------------------------------------------------------------------------------------------------------------- //X2D::PathFinder< int, X2D::Polygon* >::Find( vIndexedPoints.begin(), vIndexedPoints.end(), ptStart, ptEnd, vPath, 1000 ); X2D::PathFinder< int, X2D::LeakyPolygon* >::Find( vLeakyPolygons.begin(), vLeakyPolygons.end(), ptStart, ptEnd, vPath ); bool bCancelPathFind = vPath.empty(); if( vPath.empty() ) { vPath.push_back( ptStart ); vPath.push_back( ptEnd ); } if( bRestraint && !vPath.empty() ) { for(size_t i = 1; i + 1 < vPath.size() ; i++) // ptStart와 ptEnd는 빼고 계산한다 { assert( vPath.size() > i && "i 범위 Over" ); if(!plRegion.IsInclude(vPath[i])) // vPath 중 plRegion 밖으로 나간 point 가 있다면 { bCancelPathFind = true; break; } } } if( bCancelPathFind ) { if(vPath.size() > s_unLimit) vPath.clear(); else vPath.erase(vPath.begin(), vPath.end()); X2D::Point ptThePoint; int intersectDistance; if( getIntersectInfo(ptStart, ptEnd, ptThePoint, intersectDistance) ) { if( ptStart.GetDistance( ptThePoint ) > 2 ) // 충돌 폴리곤이랑 붙어있을때 뻘짓하는 거 방지 { vPath.push_back(ptStart); vPath.push_back(ptThePoint); } } if(m_bDrawAttributeWire ) goto _DRAWWIRE; } //if(vIndexedPoints.size() > s_unLimit) vIndexedPoints.clear(); //else vIndexedPoints.erase(vIndexedPoints.begin(), vIndexedPoints.end()); _DRAWWIRE: if(m_bDrawAttributeWire) { int size = vPath.size(); m_prWireDetour->Clear(); K3DVertex v1, v2; WORD wTile=0; for(int i = 1; i < size; i++) { v1.x = (float) vPath[i - 1].x; v1.y = (float) vPath[i - 1].y; GetTerrainHeight(v1.x, v1.y, v1.z,wTile ); v1.z += 20.0f; v2.x = (float) vPath[i].x; v2.y = (float) vPath[i].y; GetTerrainHeight(v2.x, v2.y, v2.z,wTile ); v2.z += 20.f; m_prWireDetour->AddLine(v1, v2, KColor(255, 192, 203, 255)); } // draw candidate polygon for( leaky_polygon_vector_t::iterator it = vLeakyPolygons.begin(); it != vLeakyPolygons.end(); ++it ) { X2D::LeakyPolygon< int >& Polygon = *(*it); for( size_t index = 0; index < Polygon.Size(); ++index ) { v1.x = (float)Polygon.GetPoint( index ).x; v1.y = (float)Polygon.GetPoint( index ).y; GetTerrainHeight( v1.x, v1.y, v1.z, wTile ); v1.z += 20.0f; v2.x = (float)Polygon.GetNextPoint( index ).x; v2.y = (float)Polygon.GetNextPoint( index ).y; GetTerrainHeight( v2.x, v2.y, v2.z, wTile ); v2.z += 20.0f; m_prWireDetour->AddLine( v1, v2, KColor( 255, 255, 0, 255 ) ); } } } for( leaky_polygon_vector_t::iterator it = vLeakyPolygons.begin(); it != vLeakyPolygons.end(); ++it ) if( (*it) ) delete *it; /* X2D::Point ptStart( vStart.x, vStart.y ), ptEnd( vEnd.x, vEnd.y ); X2D::Line line( vStart.x, vStart.y, vEnd.x, vEnd.y ); if( m_AttrPolygonsQuadTree.Collision( line ) == false ) { vPath.push_back(ptStart); vPath.push_back(ptEnd); return; } bool bSkipFind = false; if( m_AttrPolygonsQuadTree.Collision( ptStart ) ) { const int c_nOfsLimit = 10; bool bBreak = false; X2D::Point< int > ptTemp = ptStart; for( int i = 0; i <= c_nOfsLimit; i++ ) { if( bBreak ) break; for( int nY = -i; nY <= i; nY++ ) { if( bBreak ) break; for( int nX = -i; nX <= i; nX++ ) { if( bBreak ) break; if( nY != -i && nY != i && nX != -i && nX != i ) continue; ptTemp = ptStart; ptTemp.x += nX; ptTemp.y += nY; if( m_AttrPolygonsQuadTree.Collision( ptTemp ) ) { if( i == c_nOfsLimit && nY == c_nOfsLimit && nX == c_nOfsLimit ) { assert( 0 ); ptTemp = ptStart; } } else { bBreak = true; } } } } ptStart = ptTemp; } if(m_AttrPolygonsQuadTree.Collision(ptEnd)) { const size_t s_unLimit = 500; if(vPath.size() > s_unLimit) vPath.clear(); else vPath.erase(vPath.begin(), vPath.end()); X2D::Point ptThePoint; int intersectDistance; if( getIntersectInfo(ptStart, ptEnd, ptThePoint, intersectDistance) ) { if( ptStart.GetDistance( ptThePoint ) > 2 ) // 충돌 폴리곤이랑 붙어있을때 뻘짓하는 거 방지 { vPath.push_back(ptStart); vPath.push_back(ptThePoint); } } bSkipFind = true; } if( bSkipFind == false ) { X2D::Box boxRegion( ptStart, ptEnd ); std::vector *> pathFindPolygons; m_AttrPolygonsQuadTree.Enum( boxRegion, &pathFindPolygons ); X2D::PathFinder< int, X2D::Polygon* >::Find( pathFindPolygons.begin(), pathFindPolygons.end(), ptStart, ptEnd, vPath ); // 길찾기가 실패했다면 직선 거리로 가장 가까이 출돌 되는 곳으로 이동한다. if( vPath.empty() ) { X2D::Point ptThePoint; int intersectDistance; if( getIntersectInfo(ptStart, ptEnd, ptThePoint, intersectDistance) ) { if( ptStart.GetDistance( ptThePoint ) > 2 ) // 충돌 폴리곤이랑 붙어있을때 뻘짓하는 거 방지 { vPath.push_back(ptStart); vPath.push_back(ptThePoint); } } } } if(m_bDrawAttributeWire) { int size = vPath.size(); m_prWireDetour->Clear(); K3DVertex v1, v2; WORD wTile=0; for(int i = 1; i < size; i++) { v1.x = (float) vPath[i - 1].x; v1.y = (float) vPath[i - 1].y; GetTerrainHeight(v1.x, v1.y, v1.z,wTile ); v1.z += 20.0f; v2.x = (float) vPath[i].x; v2.y = (float) vPath[i].y; GetTerrainHeight(v2.x, v2.y, v2.z,wTile ); v2.z += 20.f; m_prWireDetour->AddLine(v1, v2, KColor(255, 192, 203, 255)); } } */ } void CTerrainMapEngine::addCustomBlock(unsigned int handle, int* line) { X2D::Point* p = new X2D::Point[2]; p[0].Set(line[0], line[1]); p[1].Set(line[2], line[3]); m_blockList.insert(std::make_pair(handle, p)); } void CTerrainMapEngine::delCustomBlock(unsigned int handle) { std::map*>::iterator it = m_blockList.find(handle); if (it == m_blockList.end()) return ; delete[] it->second; m_blockList.erase(it); } bool CTerrainMapEngine::collisionAttrQuad(float sx, float sy, float& ex, float& ey, float& collisionTheta, bool checkStartZeroLen) { X2D::Point ptStart, ptEnd; ptStart.x = (int)sx; ptStart.y = (int)sy; ptEnd.x = (int)ex; ptEnd.y = (int)ey; #ifdef KMOVE_DEBUG CreateAttributePolygonInfoForMap(K3DVector(sx, sy, 0.0f), K3DVector(ex, ey, 0.0f)); #endif X2D::Point ptThePoint; X2D::Line intersectLine; int intersectDistance; if (getIntersectInfo(ptStart, ptEnd, ptThePoint, intersectDistance, &intersectLine)) { K3DVector a((float)intersectLine.begin.x, (float)intersectLine.begin.y, 0.0f); K3DVector b((float)intersectLine.end.x, (float)intersectLine.end.y, 0.0f); K3DVector c(sx, sy, 0.0f); int len = (int)sMathUtil::getIntersectDistance(a, b, c); if( len <= CTerrainMapEngine::MIN_ATTRIBUTE_COLLISION_LENGTH) { if (0 == len && checkStartZeroLen) { /// 끼인 경우를 방지 하기 위한 코드 /// 시작 위치가 바운딩 라인과 겹쳐 있는 경우에 이동이 되지 않고 있다. /// 목적 위치로의 길찾기가 가능 할 경우에는 이동이 되야 되기 때문에 길찾기가 가능한지를 체크하고 가능하다면 /// 시작 위치에서 목적 위치로의 각 축에 1을 더해 줘서 다시 교차 점을 찾는다. 1을 더해 준 이유는 교차점 검사가 정수형이고 /// 다시 끼이는걸 방지하기 위해서이다. K3DVector s(sx, sy, 0.0f); K3DVector e(ex, ey, 0.0f); PATH path; FindDetour(s, e, path, true); if (!path.empty()) { if (ptEnd.x > ptStart.x) s.x++; if (ptEnd.x < ptStart.x) s.x--; if (ptEnd.y > ptStart.y) s.y++; if (ptEnd.y < ptStart.y) s.y--; path.clear(); return collisionAttrQuad(s.x, s.y, ex, ey, collisionTheta, false); } path.clear(); } collisionTheta = getDegreeBetweenLine(ptStart, ptEnd, intersectLine.begin, intersectLine.end, ptThePoint); return true; } else { K3DVector interP((float)ptThePoint.x, (float)ptThePoint.y, 0.0f); K3DVector perP = sMathUtil::getPerpendicularVector(a, b, c, interP, (float)CTerrainMapEngine::MIN_ATTRIBUTE_COLLISION_LENGTH); ptEnd.x = (int)perP.x; ptEnd.y = (int)perP.y; if (m_AttrPolygonsQuadTree.Collision(ptEnd)) { return true; } ex = perP.x; ey = perP.y; return false; } } else { /// 충돌 지점을 못 찾았지만 이동할려는 위치가 갈수 없는 지역일 경우가 있다. if (m_AttrPolygonsQuadTree.Collision(ptEnd)) { return true; } } return false; } bool CTerrainMapEngine::collisionCustomBlock(float sx, float sy, float& ex, float& ey) { X2D::Line ray(sx, sy, ex, ey); /// 시작 점 //X2D::Point sp(sx, sy); X2D::Point i_p; /// 시작점과 가장 가까운 충돌 점 //X2D::Point min_ip; //bool intersection = false; //int min_len = -1; std::map*>::iterator it = m_blockList.begin(); for (; it != m_blockList.end(); ++it) { X2D::Point* p = it->second; X2D::Line line(p[0].x, p[0].y, p[1].x, p[1].y); if (ray.GetIntersectPoint(line, &i_p)) { X2D::Line temp_line(sx, sy,i_p.x, i_p.y ); if( m_AttrPolygonsQuadTree.Collision( temp_line ) == false ) { //ex = sx;//i_p.x; //ey = sy;//i_p.y; ex = i_p.x; ey = i_p.y; return true; } /* intersection = true; if (-1 == min_len) { min_ip = i_p; min_len = i_p.GetDistance(sp); } else { int len = i_p.GetDistance(sp); if (len < min_len) { min_ip = i_p; min_len = len; } } */ } } /* if (intersection) { /// 최소 거리보다 작으면 그냥 멈춰 있게 한다 if (2 >= min_len) { ex = sx; ey = sy; return true; } /// normalize X2D::Point n; n = min_ip - sp; int sqr = n.GetDistance(X2D::Point(0, 0)); n /= sqr; /// 충돌 위치를 2만큼 줄여준다 min_len -= 2; n *= min_len; ex = sx + n.x; ey = sy + n.y; return true; } */ return false; } bool CTerrainMapEngine::collisionCustomBlock(float sx, float sy, float& ex, float& ey, std::vector const& blockList) { if (2 > blockList.size()) return false; X2D::Line ray(sx, sy, ex, ey); X2D::Point i_p; size_t size = blockList.size(); size_t si = 0; for (; si < size-1; ++si) { size_t ei = si + 1; X2D::Line line(blockList[si].x, blockList[si].y, blockList[ei].x, blockList[ei].y); if (ray.GetIntersectPoint(line, &i_p)) { X2D::Line temp_line(sx, sy,i_p.x, i_p.y ); if( m_AttrPolygonsQuadTree.Collision( temp_line ) == false ) { //ex = sx;//i_p.x; //ey = sy;//i_p.y; ex = i_p.x; ey = i_p.y; return true; } } } return false; } bool CTerrainMapEngine::getIntersectInfo(X2D::Point const& ptStart, X2D::Point const& ptEnd, X2D::Point& ptThePoint, int& intersectDistance, X2D::Line* intersectLine) { X2D::Line ray(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); int nSmallestSquareDist = abs((ptEnd.x - ptStart.x) * (ptEnd.x - ptStart.x) + (ptEnd.y - ptStart.y) * (ptEnd.y - ptStart.y)); X2D::Box boxRegion(ptStart, ptEnd); std::vector *> vPolygons; m_AttrPolygonsQuadTree.EnumLoose(boxRegion, &vPolygons); bool isIntersect = false; for( std::vector *>::iterator it = vPolygons.begin(); it != vPolygons.end(); ++it ) { for( size_t my_idx = 0; my_idx < (*it)->Size(); ++my_idx ) { X2D::Point ptTemp; X2D::Line currLine = (*it)->GetSegment( my_idx ); if( ray.GetIntersectPoint( currLine, &ptTemp ) ) { int nSquareDist = abs((ptTemp.x - ptStart.x) * (ptTemp.x - ptStart.x) + (ptTemp.y - ptStart.y) * (ptTemp.y - ptStart.y)); if(nSmallestSquareDist > nSquareDist) { nSmallestSquareDist = nSquareDist; ptThePoint = ptTemp; intersectDistance = nSmallestSquareDist; if (intersectLine) { *intersectLine = currLine; } isIntersect = true; // 충돌위치 버퍼 처리 int depth = 5; for (int i=0;i ptThePoint.x && ptThePoint.x > ptStart.x ) ptThePoint.x--; if (ptEnd.y < ptThePoint.y && ptThePoint.y < ptStart.y ) ptThePoint.y++; if (ptEnd.y > ptThePoint.y && ptThePoint.y > ptStart.y ) ptThePoint.y--; } // } } } } return isIntersect; } bool CTerrainMapEngine::getIntersectInfoNearbyEndPoint(X2D::Point const& ptStart, X2D::Point const& ptEnd, X2D::Point& ptThePoint, int& intersectDistance, X2D::Line* intersectLine) { X2D::Line ray(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); int nSmallestSquareDist = abs((ptEnd.x - ptStart.x) * (ptEnd.x - ptStart.x) + (ptEnd.y - ptStart.y) * (ptEnd.y - ptStart.y)); X2D::Box boxRegion(ptStart, ptEnd); std::vector *> vPolygons; m_AttrPolygonsQuadTree.EnumLoose(boxRegion, &vPolygons); bool isIntersect = false; for( std::vector *>::iterator it = vPolygons.begin(); it != vPolygons.end(); ++it ) { for( size_t my_idx = 0; my_idx < (*it)->Size(); ++my_idx ) { X2D::Point ptTemp; X2D::Line currLine = (*it)->GetSegment( my_idx ); if( ray.GetIntersectPoint( currLine, &ptTemp ) ) { int nSquareDist = abs( (ptEnd.x - ptTemp.x) * (ptEnd.x - ptTemp.x) + (ptEnd.y - ptTemp.y) * (ptEnd.y - ptTemp.y)); if(nSmallestSquareDist > nSquareDist) { if( m_AttrPolygonsQuadTree.Collision( ptTemp ) == false ) { nSmallestSquareDist = nSquareDist; ptThePoint = ptTemp; intersectDistance = nSmallestSquareDist; if (intersectLine) { *intersectLine = currLine; } isIntersect = true; } } } } } return isIntersect; } float CTerrainMapEngine::getDegreeBetweenLine(X2D::Point const& sp, X2D::Point const& ep, X2D::Point const& i_sp, X2D::Point const& i_ep, X2D::Point const& intersectP) { X2D::Point ri_ep, ri_sp; /// ep와 i_ep의 각을 구한다. float theta = calcTheta(ep, i_ep, intersectP); /// 양수로 만듬 sMathUtil::makeAbsTheta(theta); X2D::Point xr(1, 0); if (theta < sMathUtil::degree90) { ri_ep = i_ep - i_sp; theta = calcTheta(ri_ep, xr, X2D::Point(0, 0)); if (ri_ep.y < 0.0f) theta = sMathUtil::degree360 - theta; } else { ri_sp = i_sp - i_ep; theta = calcTheta(ri_sp, xr, X2D::Point(0, 0)); if (ri_sp.y < 0.0f) theta = sMathUtil::degree360 - theta; } return theta; } float CTerrainMapEngine::calcTheta(X2D::Point const& _p1, X2D::Point const& _p2, X2D::Point const& origin) { X2D::Point __p1 = _p1 - origin; X2D::Point __p2 = _p2 - origin; K3DVector p1(__p1.x, __p1.y, 0.0f); K3DVector p2(__p2.x, __p2.y, 0.0f); p1.Normalize(); p2.Normalize(); float dot = p1.x * p2.x + p1.y * p2.y; float theta = acosf(dot); return theta; }