#include "StdAfx.h" #include //#include "Util.h" #include "K3DCamera.h" #include "KViewport.h" #include "K3DPCCP.h" #include "TerrainMapEngine.h" #include "TerrainSegmentLoadThread.h" #include "TerrainProp.h" #include "TerrainPrimitive.h" #include "TerrainSeamlessWorldInfoForClient.h" #include "TerrainTextureInfo.h" #include "TerrainPropInfoForClient.h" #include "TerrainScriptInfoForMap.h" #include "TerrainAttributeInfoForMap.h" #include "TerrainLowQualityWaterInfo.h" #include "TerrainGrassColonyInfoForMap.h" #include #include //#include #include "KSeqSpeedGrass.h" #include #include "PVSObject.h" #include "SDebug_Util.h" #include "GameDefine.h" /// 2010.11.10 - prodongi #include "SItemDB.h" #include "SGameLobbyDefine.h" #ifdef _EDIT_MAP_FILE_ #include "SGameSystem.h" #include "SGameAvatarFx.h" #endif #ifdef _EDIT_MAP_FILE_ extern SGameSystem* g_pCurrentGameSystem; #endif namespace { XMutex g_TerrainCS; // 지형 엔진은 뮤텍스여야함. const float c_fDefaultTerrainVisibleDistance = 2048.f; //AziaMafia LoadMap 2048.f; const float c_fDefaultPropLodDistanceRatio = 0.5f; //AziaMafia LoadMap 0.5f; // sonador #2.1.7.1 스피드 트리 퍼포먼스 증가 const char* c_szSeamlessWorldInfoFileName = "TerrainSeamlessWorld.cfg"; const char* c_szTextureInfoFileName = "TerrainTextureInfo.cfg"; const char* c_szPropInfoFileName = "TerrainPropInfo.cfg"; const TERRAINMAP_PROPERTIES::COLORING c_DefaultColoring; const DWORD c_dwLightChangingTime = 5000; const DWORD c_dwLightChangePeriod = 100; const int LOAD_SEGMENT_MAX_SIZE = 1; //동시에 추가 될 수 있는 세그먼트 수 inline float ChangeValue( float fSrc1, float fSrc2, float fSrc1Ratio ) { return (fSrc1 * fSrc1Ratio) + (fSrc2 * (1.f - fSrc1Ratio)); } inline void ChangeValue( K3DVector& rDes, const K3DVector& rSrc1, const K3DVector& rSrc2, float fSrc1Ratio ) { if( rSrc1 != rSrc2 ) { rDes.x = ChangeValue( rSrc1.x, rSrc2.x, fSrc1Ratio ); rDes.y = ChangeValue( rSrc1.y, rSrc2.y, fSrc1Ratio ); rDes.z = ChangeValue( rSrc1.z, rSrc2.z, fSrc1Ratio ); } else rDes = rSrc1; } inline void ChangeValue( K3DColor& rDes, const K3DColor& rSrc1, const K3DColor& rSrc2, float fSrc1Ratio ) { if( rSrc1 != rSrc2 ) { rDes.r = ChangeValue( rSrc1.r, rSrc2.r, fSrc1Ratio ); rDes.g = ChangeValue( rSrc1.g, rSrc2.g, fSrc1Ratio ); rDes.b = ChangeValue( rSrc1.b, rSrc2.b, fSrc1Ratio ); } else rDes = rSrc1; } inline bool Is2DCollision( float fSrcLeft, float fSrcTop, float fSrcRight, float fSrcBottom, float fDesLeft, float fDesTop, float fDesRight, float fDesBottom ) { return ( std::min( fSrcRight , fDesRight ) > std::max( fSrcLeft, fDesLeft ) && std::min( fSrcBottom, fDesBottom ) > std::max( fSrcTop , fDesTop ) ); } struct polygon_allocator { X2D::Polygon* operator()() { return new X2D::Polygon< int >(); } }; struct polygon_deleter { void operator()( X2D::Polygon* p ) { delete p; } }; }; DWORD CTerrainMapEngine::m_unLastRenderedTime = 0; unsigned CTerrainMapEngine::m_unLastRenderedTick = 0; bool CTerrainMapEngine::s_bEnableTerrainLOD = true; // { debug #ifdef PATHFIND_DEBUG std::vector< X2D::Point > g_vGreenPath; std::vector< X2D::Point > g_vLeftPath; std::vector< X2D::Point > g_vRightPath; std::vector< X2D::Line > g_vLineList; std::vector< std::vector< X2D::Point > > g_vPathList; std::vector< X2D::Point > g_vRedPoint; std::vector< X2D::Point > g_vBlackPoint; std::vector< X2D::Point > g_vBluePoint; std::vector< X2D::Point > g_vGreenPoint; std::vector< X2D::Point > g_vBigRedPoint; #endif // PATHFIND_DEBUG // } end CTerrainMapEngine::CTerrainMapEngine() : m_pDevice( NULL ) , m_pSeamlessWorldInfo( NULL ) , m_pTextureInfo( NULL ) , m_pPropInfo( NULL ) //, m_FilteringType( FILTERING_NONE ) , m_RenderMode( TERRAIN_RENDERMODE_DEFAULT ) , m_dwCurrentTime( 0 ) , m_dwColoringChangingRemainTime( 0 ) , m_dwPreviousSetColoringTime( 0 ) , m_pCustomMatrix( NULL ) , m_fCustomTextureSquareDist( 0.f ) , m_EventPolygonScanner( 1000000, 1000000 ) , m_EventAreaPolygonScanner( 1000000, 1000000 ) , m_pCurLocationInfo(NULL) , m_pCurEventAreaInfo(NULL) , m_fTerrainVisibleDistanceRatio(1.0f) , m_fTerrainSquareVisibleDistRatio(1.0f) , m_fPropLodDistanceRatio( 1.0f ) // sonador #2.1.7.1 스피드 트리 퍼포먼스 증가 , m_prWireAttributePolygon(NULL) , m_prWireDetour(NULL) , m_prWireQuadTree( NULL ) , m_bCreateAttributeWire(false) // TODO : 디버그용.. 나중에 false로 바꿔야 한다. by blackfish , m_bDrawAttributeWire(false) , m_nLocalPriority( -1 ) , m_pPVSObjectManager(NULL) #ifdef _DEV , m_AttrPolygonsQuadTree( 0, 0, 700000, 1000000 ) #else , m_AttrPolygonsQuadTree( 0, 0, 1000000, 1000000 ) #endif , m_AttrPointQuadTree( 1000000, 1000000 ) { m_nTarX = m_nTarY = 0; m_viewX = 0; m_viewY = 1; m_bEnableLightmap = false; m_fixVisibleDistance = 0; /// 2011.12.05 - prodongi #ifdef KMOVE_DEBUG SetDrawAttributePolygon(true); #endif } CTerrainMapEngine::~CTerrainMapEngine() { CTerrainProp::EndPropLoadingThread(); Release(); std::map*>::iterator it = m_blockList.begin(); for (; it != m_blockList.end(); ++it) { delete[] it->second; } m_blockList.clear(); } int CTerrainMapEngine::GetTileCount() { const CTerrainSeamlessWorldInfo* pSeamlessWorldInfo = GetSeamlessWorldInfo(); if( pSeamlessWorldInfo ) return pSeamlessWorldInfo->GetTileCountPerSegment(); assert(0); return -1; } bool CTerrainMapEngine::Initialize ( K3DRenderDevice* pDevice , K3DCamera* pCamera // , TERRAIN_FILTERING FilteringType , TERRAIN_RENDERMODE RenderMode , DWORD dwSegmentRemovePeriod , DWORD dwSegmentExpireTime , int nThreadCount/* = 3*/ ) { Release(); m_pDevice = pDevice; // Seamless 월드 정보 m_pSeamlessWorldInfo = new CTerrainSeamlessWorldInfoForClient(); if( !m_pSeamlessWorldInfo->Initialize( c_szSeamlessWorldInfoFileName, false ) ) { assert( false && "Seamless 월드 정보 파일 초기화 실패 (cfg파일 문법 오류 또는 비정상적인 맵 파일 존재)" ); SAFE_DELETE( m_pSeamlessWorldInfo ); return false; } // 지형 정보 m_pTextureInfo = new CTerrainTextureInfo(); if( !m_pTextureInfo->Initialize( c_szTextureInfoFileName, m_pDevice ) ) { assert( false && "지형 텍스쳐 정보 파일 초기화 실패" ); SAFE_DELETE( m_pTextureInfo ); return false; } // 장식물 정보 m_pPropInfo = new CTerrainPropInfoForClient(); if( !m_pPropInfo->Initialize( c_szPropInfoFileName ) ) { assert( false && "지형 장식물 정보 파일 초기화 실패" ); SAFE_DELETE( m_pPropInfo ); return false; } // 카메라 FOV설정 float fFOV = m_pSeamlessWorldInfo->GetFOV(); if( 0.f < fFOV ) pCamera->SetFOV( fFOV ); // 스레드 시작 #ifndef _RELEASE m_ThreadManager.StartThread( "CTerrainMapEngine", 1, THREAD_PRIORITY_BELOW_NORMAL ); #else m_ThreadManager.StartThread( "CTerrainMapEngine", nThreadCount ); #endif CTerrainProp::BeginPropLoadingThread(); //m_FilteringType = FilteringType; m_RenderMode = RenderMode; m_nTileCountPerSegment = m_pSeamlessWorldInfo->GetTileCountPerSegment(); m_nAttrCellCountPerSegment = m_nTileCountPerSegment * c_nAttrCountPerTile; m_fTileLength = m_pSeamlessWorldInfo->GetTileLength(); m_fAttrCellLength = m_fTileLength / float(c_nAttrCountPerTile); m_fSegmentLength = m_fTileLength * float(m_nTileCountPerSegment); int nSegmentCountPerMap = m_pSeamlessWorldInfo->GetSegmentCountPerMap(); const KSize& rMapCount = m_pSeamlessWorldInfo->GetMapCount(); m_nSegmentXCount = (rMapCount.cx * nSegmentCountPerMap); m_nSegmentYCount = (rMapCount.cy * nSegmentCountPerMap); m_dwSegmentExpireTime = dwSegmentExpireTime; m_dwSegmentRemovePeriod = dwSegmentRemovePeriod; m_dwPreviousSegmentRemoveTime = 0; ZeroMemory( m_spCommonIndexBuffer, sizeof( m_spCommonIndexBuffer ) ); // 세그먼트 공용 인덱스 버퍼 생성 m_spCommonIndexBuffer[TERRAIN_COMMON_IB] = CTerrainPrimitive::CreateCommonIndexBuffer( m_pSeamlessWorldInfo->GetTileCountPerSegment(), pDevice ); if( m_spCommonIndexBuffer[TERRAIN_COMMON_IB] == NULL ) return false; // 세그먼트 LOD 공용 인덱스 버퍼 생성 m_spCommonIndexBuffer[TERRAIN_COMMON_IB_LOD] = CTerrainPrimitive::CreateCommonIndexBuffer( c_nSegmentLodTileCount, pDevice ); if( m_spCommonIndexBuffer[TERRAIN_COMMON_IB_LOD] == NULL ) return false; m_spCommonIndexBuffer[TERRAIN_COMMON_IB_CRACK] = CTerrainPrimitive::CreateCommonCrackIndexBuffer( pDevice ); if( m_spCommonIndexBuffer[TERRAIN_COMMON_IB_CRACK] == NULL ) return false; // 가시거리 초기값으로 설정 SetTerrainVisibleDistance( c_fDefaultTerrainVisibleDistance ); // Picking 거리 SetPickingDistance( 0.f ); // 장식물 Lod 초기값으로 설정 // SetPropLodDistance( c_fDefaultTerrainVisibleDistance / 2 ); // sonador #2.1.7.1 스피드 트리 퍼포먼스 증가 SetPropLodDistanceRatio( c_fDefaultPropLodDistanceRatio ); // 대강의 가시 영역 m_rcVisibleSegment = KRect( 0, 0, 0, 0 ); // 원점 보정값 m_vtOriginCorrection = K3DVertex( 0.f, 0.f, 0.f ); //지역 관련 m_vOldTargetPosition = K3DVector(0.0f, 0.0f, 0.0f); m_nLocalState = _WORLD_; m_nWorldID = 0; m_prWireAttributePolygon = new KWireUtilPrimitive; m_prWireDetour = new KWireUtilPrimitive; m_prWireQuadTree = new KWireUtilPrimitive; m_pPVSObjectManager = new PVSObjectManager; return true; } void CTerrainMapEngine::Release() { ClearSegmentThread(); for( TERRAINSEGMENTMAP::iterator itSegment = m_SegmentMap.begin(); itSegment != m_SegmentMap.end(); itSegment++ ) delete itSegment->second; for( std::vector< CTerrainSegment* >::iterator it = m_DeleteSegmentList.begin(); it != m_DeleteSegmentList.end(); it++ ) delete (*it); std::vector< CTerrainLowQualityWaterInfo * >::iterator itLQWater = m_LQWaterInfo.begin(); for( ; itLQWater != m_LQWaterInfo.end(); itLQWater++ ) delete (*itLQWater); #ifdef _EDIT_MAP_FILE_ m_LQWaterInfo.clear(); #endif m_SegmentMap.clear(); m_ThreadManager.EndThread(); SAFE_DELETE( m_pPVSObjectManager ); SAFE_DELETE( m_pPropInfo ); SAFE_DELETE( m_pTextureInfo ); SAFE_DELETE( m_pSeamlessWorldInfo ); SAFE_DELETE( m_pCurLocationInfo ); SAFE_DELETE( m_pCurEventAreaInfo ); //ClearAttributePolygons(); //SAFE_DELETE_VECTOR(m_vAttributePolygons); DeleteAttrQuadTree(); SAFE_DELETE(m_prWireAttributePolygon); SAFE_DELETE(m_prWireDetour); SAFE_DELETE(m_prWireQuadTree); //SAFE_DELETE( m_pCurAttributePolygonInfo ); SAFE_DELETE_VECTOR(m_vAttributeInfos); //SAFE_DELETE_VECTOR(m_vGrassContainer); GRASS_COLONY_INFO_FOR_MAP::iterator itGrassColony = m_GrassColonyInfoForMap.begin(); for( ; itGrassColony != m_GrassColonyInfoForMap.end(); itGrassColony++ ) SAFE_DELETE( (*itGrassColony) ); m_GrassColonyInfoForMap.clear(); _oprint( "Total MiaProps %d\n", (int)m_MiaPropVector.size() ); m_MiaPropVector.clear(); } void CTerrainMapEngine::DeleteAttrQuadTree() { { int x = m_AttrPolygonsQuadTree.GetX(); int y = m_AttrPolygonsQuadTree.GetY(); int w = m_AttrPolygonsQuadTree.GetWidth(); int h = m_AttrPolygonsQuadTree.GetWidth(); X2D::Rect rc(x, y, w, h); //#ifdef _DEV // m_AttrPolygonsQuadTree.Remove( rc, polygon_deleter() ); //#else m_AttrPolygonsQuadTree.Remove( rc ); //#endif assert(m_AttrPolygonsQuadTree.getItemCount() == 0); } { int x = m_AttrPointQuadTree.GetX(); int y = m_AttrPointQuadTree.GetY(); int w = m_AttrPointQuadTree.GetWidth(); int h = m_AttrPointQuadTree.GetWidth(); X2D::Rect rc(x, y, w, h); m_AttrPointQuadTree.Remove(rc); assert(m_AttrPointQuadTree.getItemCount() == 0); } SAFE_DELETE_VECTOR( m_AttrIndexedPoints ); SAFE_DELETE_VECTOR( m_vecAttrPolygons ); } //KWireUtilPrimitive* CTerrainMapEngine::GetWireAttributePolygon() //{ // return m_prWireAttributePolygon; //} //void CTerrainMapEngine::SetFilteringType( TERRAIN_RENDERMODE Mode ) void CTerrainMapEngine::SetRenderMode( TERRAIN_RENDERMODE Mode ) { // 임시 ... 이걸 Preloading을 이용하게 할것 .. //if( m_FilteringType != Type ) if( m_RenderMode != Mode ) { //m_FilteringType = Type; m_RenderMode = Mode; ClearSegmentThread(); for( TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); it != m_SegmentMap.end(); it++ ) { AddSegmentThread( it->first ); // 새로운 필터링 방식으로 재생성 지시 //_oprint( "AddSegmentThread : SetFilteringType\n" ); _oprint( "AddSegmentThread : SetRenderMode\n" ); } } } void CTerrainMapEngine::SetTerrainVisibleDistance( float fDistance ) { _performance_print( "TerrainVisibleDistance : %f\n", fDistance ); m_fTerrainVisibleDistanceMax = fDistance; m_fTerrainSquareVisibleDistMax = (fDistance * fDistance); m_fTerrainVisibleDistance = m_fTerrainVisibleDistanceMax * m_fTerrainVisibleDistanceRatio; m_fTerrainSquareVisibleDist = m_fTerrainVisibleDistance * m_fTerrainVisibleDistance; m_maxDistSq = m_fTerrainSquareVisibleDist / (m_fSegmentLength * m_fSegmentLength); // sonador #2.1.7.1 스피드 트리 퍼포먼스 증가 this->SetPropLodDistanceRatio( m_fPropLodDistanceRatio ); } void CTerrainMapEngine::SetTerrainVisibleDistanceRatio( float fRatio ) { _performance_print( "TerrainVisibleDistanceRatio : %f\n", fRatio ); m_fTerrainVisibleDistanceRatio = fRatio; m_fTerrainSquareVisibleDistRatio = fRatio; m_fTerrainVisibleDistance = m_fTerrainVisibleDistanceMax * m_fTerrainVisibleDistanceRatio; m_fTerrainSquareVisibleDist = m_fTerrainVisibleDistance * m_fTerrainVisibleDistance; m_maxDistSq = m_fTerrainSquareVisibleDist / (m_fSegmentLength * m_fSegmentLength); // sonador #2.1.7.1 스피드 트리 퍼포먼스 증가 this->SetPropLodDistanceRatio( m_fPropLodDistanceRatio ); } void CTerrainMapEngine::SetPropLodDistanceRatio( float fRatio ) { m_fPropLodDistanceRatio = fRatio; m_fPropLodDistance = m_fTerrainVisibleDistance * m_fPropLodDistanceRatio; } float CTerrainMapEngine::GetLineCrossedPointTwoSides( const K3DCamera* pCamera, const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) { K3DVector vtPropPick, vtTerrainPick; //float fPropPick = GetPropLineCrossedPointTwoSides( rNear, rFar, vtPropPick ); float fPropPick = 1.0f; // 임시 코드 by blackfish -> K3DVector vtStoolPick; float fStoolPick = 1.0f; bool bStoolPick = GetStoolLineCrossedPoint( rNear, rFar, vtStoolPick ); if(bStoolPick) { fStoolPick = (vtStoolPick - rNear).Magnitude() / (rFar - rNear).Magnitude(); } // <- 임시 코드 by blackfish float fTerrainPick = GetTerrainLineCrossedPointTwoSides( rNear, rFar, vtTerrainPick, pCamera ); //float fTerrainPick = 1.0f; float fRet = 1.0f; //if( fPropPick != 1.0f || fTerrainPick != 1.0f) //{ // if(fPropPick < fTerrainPick) // { // rPickedPoint = vtPropPick; // fRet = fPropPick; // } // else // { // rPickedPoint = vtTerrainPick; // fRet = fTerrainPick; // } // //return true; //} if( fPropPick != 1.0f || fTerrainPick != 1.0f || fStoolPick != 1.0f) { if(fPropPick < fTerrainPick) { if(fPropPick < fStoolPick) { rPickedPoint = vtPropPick; fRet = fPropPick; } else { rPickedPoint = vtStoolPick; fRet = fStoolPick; } } else { if(fTerrainPick < fStoolPick) { rPickedPoint = vtTerrainPick; fRet = fTerrainPick; } else { rPickedPoint = vtStoolPick; fRet = fStoolPick; } } //return true; } //return false; return fRet; } TERRAIN_PICK_RESULT CTerrainMapEngine::GetLineCrossedPoint( const K3DCamera* pCamera, const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) { //for( TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); it != m_SegmentMap.end(); it++ ) // it->second->ResetStoolColor(); K3DVector vtStoolPick, vtTerrainPick; // bool bStoolPick = GetStoolLineCrossedPoint( rNear, rFar, vtStoolPick ); //bool bStoolPick = false; // bool bTerrainPick = GetTerrainLineCrossedPoint( rNear, rFar, vtTerrainPick, pCamera ); TERRAIN_PICK_RESULT nStoolPickResult = GetStoolLineCrossedPoint( rNear, rFar, vtStoolPick ); TERRAIN_PICK_RESULT nTerrainPickResult = GetTerrainLineCrossedPoint( rNear, rFar, vtTerrainPick, pCamera ); if( nStoolPickResult == TERRAIN_STOOL_PICK_SUCCECED ) { if( nTerrainPickResult == TERRAIN_PICK_SUCCECED ) rPickedPoint = (( vtStoolPick - rNear ).SquareMagnitude() < ( vtTerrainPick - rNear ).SquareMagnitude()) ? vtStoolPick : vtTerrainPick; else { rPickedPoint = vtStoolPick; return nStoolPickResult; } return nTerrainPickResult; } if( nTerrainPickResult == TERRAIN_PICK_SUCCECED ) { rPickedPoint = vtTerrainPick; return nTerrainPickResult; } return nTerrainPickResult; } bool CTerrainMapEngine::GetTerrainHeight( float x, float y, float& out, WORD& wTile, bool* pOutOnProp/*=NULL*/ ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); bool bCheck = false; if ( pOutOnProp ) *pOutOnProp = false; if( x >= 0.f && y >= 0.f ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ) ); if( it != m_SegmentMap.end() ) { bCheck = true; out = it->second->GetSegmentHeight( x, y ); wTile = it->second->GetTile( x, y ); float fStoolHeight = out; if( it->second->GetStoolHeight( x, y, fStoolHeight ) ) { if( out < fStoolHeight ) { out = fStoolHeight; if ( pOutOnProp ) *pOutOnProp = true; } } } } if( bCheck == false ) out = 0.f; return bCheck; } CTerrainProp* CTerrainMapEngine::GetTerrainProp( float x, float y, float& out ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); CTerrainProp* pTerrainProp = NULL; if( x >= 0.f && y >= 0.f ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ) ); if( it != m_SegmentMap.end() ) { out = it->second->GetSegmentHeight( x, y ); float fStoolHeight = out; pTerrainProp = it->second->GetTerrainProp( x, y, fStoolHeight ); } } return pTerrainProp; } bool CTerrainMapEngine::GetOnlyTerrainHeight( float x, float y, float& out ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); bool bCheck = false; if( x >= 0.f && y >= 0.f ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ) ); if( it != m_SegmentMap.end() ) { bCheck = true; out = it->second->GetSegmentHeight( x, y ); } } if( bCheck == false ) out = 0.f; return bCheck; } // KTripleColor GetColor( int nX, int nY ) const; bool CTerrainMapEngine::GetTerrainColor( float x, float y, KTripleColor &out ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); bool bCheck = false; // if ( pOutOnProp ) // *pOutOnProp = false; if( x >= 0.f && y >= 0.f ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ) ); if( it != m_SegmentMap.end() ) { bCheck = it->second->GetSegmentColor( x, y, out ); /* float fStoolHeight = out; if( it->second->GetStoolHeight( x, y, fStoolHeight ) ) { if( out < fStoolHeight ) { out = fStoolHeight; if ( pOutOnProp ) *pOutOnProp = true; } } */ } } if ( bCheck == false ) { out.r=out.g=out.b=0; } return bCheck; } void CTerrainMapEngine::GetTerrainTriangle(float x, float y, K3DVector& v1, K3DVector& v2, K3DVector& v3) const { THREAD_SYNCRONIZE( &g_TerrainCS ); v1.x = v1.y = v1.z = 0.0f; v2.x = v2.y = v2.z = 0.0f; v3.x = v3.y = v3.z = 0.0f; if( x >= 0.f && y >= 0.f ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ) ); if( it != m_SegmentMap.end() ) { float fTerrainHeight = it->second->GetSegmentHeight( x, y ); if( !it->second->GetStoolTriangle( x, y, v1, v2, v3, fTerrainHeight ) ) { it->second->GetSegmentTriangle( x, y, v1, v2, v3 ); } } } } void CTerrainMapEngine::GetNearVertices( float x, float y, int cx, int cy, K3DVertex* pGetVertices ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); int nTileX = int((x + (m_fTileLength / 2.f)) / m_fTileLength); int nTileY = int((y + (m_fTileLength / 2.f)) / m_fTileLength); int nTileSX = (nTileX - (cx / 2)); int nTileSY = (nTileY - (cy / 2)); int nTileEX = nTileSX + cx; int nTileEY = nTileSY + cy; int nFindSegmentIndex = (-1); TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.end(); for( nTileY = nTileSY; nTileY < nTileEY; ++nTileY ) { for( nTileX = nTileSX; nTileX < nTileEX; ++nTileX ) { K3DVertex vertex( 0.f, 0.f, 0.f ); if( 0 <= nTileX && 0 <= nTileY ) { int nSegmentIndex = GetSegmentIndex( nTileX / m_nTileCountPerSegment, nTileY / m_nTileCountPerSegment ); if( nFindSegmentIndex != nSegmentIndex ) { nFindSegmentIndex = nSegmentIndex; it = m_SegmentMap.find( nFindSegmentIndex ); } if( it != m_SegmentMap.end() ) vertex = it->second->GetVertex( nTileX % m_nTileCountPerSegment, nTileY % m_nTileCountPerSegment ); } vertex.z += 0.05f; *pGetVertices++ = vertex; } } } bool CTerrainMapEngine::IsBlockedAttribute( const KPoint& rAttrPos ) const { THREAD_SYNCRONIZE( &g_TerrainCS ); bool bBlock = true; if( rAttrPos.x >= 0.f && rAttrPos.y >= 0.f ) { int nSegmentIndex = GetSegmentIndex( rAttrPos.x / m_nAttrCellCountPerSegment, rAttrPos.y / m_nAttrCellCountPerSegment ); TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it != m_SegmentMap.end() ) bBlock = it->second->GetAttribute( rAttrPos.x % m_nAttrCellCountPerSegment, rAttrPos.y % m_nAttrCellCountPerSegment ); } return bBlock; } void CTerrainMapEngine::convertWorldToMapPos(int worldPosX, int worldPosY, int& mapX, int& mapY) { if (!m_pPVSObjectManager) return ; m_pPVSObjectManager->convertWorldToMapPos(worldPosX, worldPosY, mapX, mapY); } void CTerrainMapEngine::PreloadingSegments( float x, float y, bool bAsyncLoading/* = false*/, const K3DVector* pFrustum/* = NULL*/, int preloadingDistance ) { THREAD_SYNCRONIZE( &g_TerrainCS ); m_pPVSObjectManager->WarpLoad( x, y ); // _oprint( "CTerrainMapEngine::PreloadingSegments() ( %f,%f) %s\n", x, y, ( bAsyncLoading ? "ASYNC" : "SYNC" ) ); // 새로운 가시 영역 설정 m_fixVisibleDistance = preloadingDistance; /// 2011.11.08 - prodongi SetVisibleArea( x, y, preloadingDistance ); /* if( false ) // 주기적으로 지워 주므로.. 워프할‹š 지울 필요는 없을듯. (워프 시간이 넘 오래 걸림 -_-) by Testors. { // 가시 영역 바깥의 세그먼트 제거 TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); while( it != m_SegmentMap.end() ) { KPoint ptSegment = GetSegmentPos( it->first ); if( !IsInsideVisibleArea( ptSegment.x, ptSegment.y ) ) { // TODO : 저사양 물관련 ( 세그먼트 지우기 전에 해줘야 한다 ) DeleteWaterSegmentCount((*it).second->GetMapX(), (*it).second->GetMapY()); // _oprint( "m_SegmentMap 삭제 03\n" ); delete it->second; it = m_SegmentMap.erase( it ); //#ifndef NDEBUG // const int nSegmentIndex = GetSegmentIndex( ptSegment.x, ptSegment.y ); // _oprint( "MAP - PreloadingSegments : m_SegmentMap.erase [%d]\n", nSegmentIndex ); //#endif } else it++; } }*/ // 가시 영역 내의 세그먼트 생성 for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; if( m_SegmentMap.find( nSegmentIndex ) != m_SegmentMap.end() ) continue; if( bAsyncLoading ) { // 비동기 로딩 ( 스레드 로딩 지시 ) // _oprint( "AddSegmentThread( %d )\n", nSegmentIndex ); AddSegmentThread( nSegmentIndex ); } else { // 스레드 처리하지 않고 즉시 로딩 CTerrainSegmentLoadThread* pThread = CreatePreparedThread( nSegmentIndex ); pThread->onProcess( 0 ); // 즉시 PropLoading 걸어 준다. TERRAINSEGMENTMAP::iterator it = m_SegmentMap.find( nSegmentIndex ); if( it != m_SegmentMap.end() ) { bool bVisible = true; if( pFrustum ) { bVisible = it->second->IsFrustumCollide( pFrustum ); if( bVisible ) it->second->LoadAllProps( pFrustum ); } else { it->second->LoadAllProps(); } } delete pThread; } } } _performance_print( "Segment Size : %d\n", (int)m_SegmentMap.size() ); // coloring 설정 m_dwColoringChangingRemainTime = 0; m_CurrentMapColoring = GetSegmentColoring( x, y ); SetPrimaryLightToSegments(); } bool CTerrainMapEngine::IsCompletelyLoadedVisibleSegment() const { THREAD_SYNCRONIZE( &g_TerrainCS ); for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) != nSegmentIndex ) { TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) { /// 2011.07.05 false를 리턴하면 게임이 로딩 중에 멈추는 현상이 발생이 되서 continue로 수정 함 - prodongi /// 많은 검증 작업이 필요함 //return false; continue; } if( !it->second->IsPropLoadCompleted() ) { return false; } } } } return true; } void CTerrainMapEngine::SetVisibleArea( float x, float y, int fixVisibleDistance ) { float fTerrainVisibleDistance = m_fTerrainVisibleDistance; //최저 사양일 경우 세그먼트가 시야 범위 밖에 있어 프랍이 로딩이 안될경우가 발생함 임시로 VisibleArea는 LV3으로 유지 시킨다 float fVisibleDistance = m_fTerrainVisibleDistanceMax * GAME_TERRAIN_DISTANCE_LV3; //int nx, ny; //GetMapXY(K3DVector(x, y, 0), nx, ny); //if ((nx == 12) && (ny == 7)) fVisibleDistance = m_fTerrainVisibleDistanceMax * GAME_TERRAIN_DISTANCE_LV5; if( fTerrainVisibleDistance < fVisibleDistance ) fTerrainVisibleDistance = fVisibleDistance; if (fixVisibleDistance) fTerrainVisibleDistance = fixVisibleDistance; // 대강의 가시 영역을 구한다. m_rcVisibleSegment.left = int( (x - fTerrainVisibleDistance) / m_fSegmentLength ) - 1; m_rcVisibleSegment.top = int( (y - fTerrainVisibleDistance) / m_fSegmentLength ) - 1; m_rcVisibleSegment.right = int( (x + fTerrainVisibleDistance) / m_fSegmentLength ) + 1; m_rcVisibleSegment.bottom = int( (y + fTerrainVisibleDistance) / m_fSegmentLength ) + 1; if( m_rcVisibleSegment.left < 0 ) m_rcVisibleSegment.left = 0; if( m_rcVisibleSegment.top < 0 ) m_rcVisibleSegment.top = 0; if( m_rcVisibleSegment.right > m_nSegmentXCount ) m_rcVisibleSegment.right = m_nSegmentXCount; if( m_rcVisibleSegment.bottom > m_nSegmentYCount ) m_rcVisibleSegment.bottom = m_nSegmentYCount; } /* const CTerrainScriptInfoForMap* CTerrainMapEngine::GetMinimapInfo( int nMapPosX, int nMapPosY ) const { const KSize& rMapCount = m_pSeamlessWorldInfo->GetMapCount(); int nScriptIndex = (nMapPosY * rMapCount.cx) + nMapPosX; TERRAINCLIENT_EVLOCMAP::const_iterator itEvLoc = m_ClientEventLocationMap.find( nScriptIndex ); return (m_ClientEventLocationMap.end() != itEvLoc) ? itEvLoc->second : NULL; } */ void CTerrainMapEngine::WarpClear( float fWarpPosX, float fWarpPosY ) { DWORD RemoveStartTm = GetSafeTickCount(); if( !g_TerrainCS.TryLock() ) return; if( GetSafeTickCount() - RemoveStartTm > 80 ) { _oprint( "!!! RemoveUnnecessarySegments() Warning- LOCK!!! %dms\n", GetSafeTickCount() - RemoveStartTm ); } RemoveStartTm = GetSafeTickCount(); //지역 물은 Warp 되면, 클리어 해 버림. /* std::vector< CTerrainLowQualityWaterInfo * >::iterator itLQWater = m_LQWaterInfo.begin(); for( ; itLQWater != m_LQWaterInfo.end(); itLQWater++ ) delete (*itLQWater); m_LQWaterInfo.clear(); //풀 클리어 m_pGrassColonyInfoForMap->Clear();*/ //이벤트 폴리곤 클리어 float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(fWarpPosX / fMapLen); int nMapY = (int)(fWarpPosY / fMapLen); //새로운 지형이라면 다시 로드 아니면 패스 bool bReload = false; if( m_pCurLocationInfo == NULL ) bReload = true; else { if( m_pCurLocationInfo->CheckMap( nMapX, nMapY ) ) bReload = true; } if( bReload ) { m_EventPolygonScanner.ReInit( 1000000, 1000000 ); m_EventAreaPolygonScanner.ReInit( 1000000, 1000000 ); SAFE_DELETE( m_pCurLocationInfo ); SAFE_DELETE( m_pCurEventAreaInfo ); SAFE_DELETE_VECTOR(m_vAttributeInfos); DeleteAttrQuadTree(); K3DVector vStart( fWarpPosX, fWarpPosY, 0.0f ); CreateAttributePolygonInfoForMap( vStart, vStart ); } int total_cnt = (int)m_SegmentMap.size(); int delete_cnt = 0; DWORD DeleteTime = 0; //// 제거 작업할 때가 된 경우 //if( (m_dwCurrentTime - m_dwPreviousSegmentRemoveTime) > m_dwSegmentRemovePeriod ) { m_dwPreviousSegmentRemoveTime = m_dwCurrentTime; // 제거 작업 기록 시간 변경 //쓰레드에서 지워진것 삭제 { for( std::vector< CTerrainSegment* >::iterator it = m_DeleteSegmentList.begin(); it != m_DeleteSegmentList.end(); ) { delete (*it); ++delete_cnt; it = m_DeleteSegmentList.erase( it ); } } TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); while( it != m_SegmentMap.end() ) { KPoint ptSegment = GetSegmentPos( it->first ); //// 유통기한(?)이 경과하였고 가시영역에서 벗어나 있는 세그먼트를 제거한다. //if( (m_dwCurrentTime - it->second->GetRenderedTime()) > m_dwSegmentExpireTime && !IsInsideVisibleArea( ptSegment.x, ptSegment.y ) ) //{ CTerrainSegment* pSegment = it->second; // TODO : 저사양 물관련 ( 세그먼트 지우기 전에 해줘야 한다 ) DeleteWaterSegmentCount(pSegment->GetMapX(), pSegment->GetMapY()); DeleteSpeedGrassSegmentCount( pSegment ); DWORD dwPrev = GetSafeTickCount(); // _oprint( "m_SegmentMap 삭제 02\n" ); ++delete_cnt; delete it->second; it = m_SegmentMap.erase( it ); DeleteTime += ( GetSafeTickCount() - dwPrev ); //} //else // it++; } } g_TerrainCS.UnLock(); } void CTerrainMapEngine::AfterRender( unsigned long uRenderBitVector ) { //Render 후에 삭제가 이뤄져야 한다. - BERSERK RemoveUnnecessarySegments(); if( m_pTextureInfo ) m_pTextureInfo->RemoveUnnecessaryTexture(); } int g_TerrainSegments = 0; // TEMP - by Tyburn void CTerrainMapEngine::Render( unsigned long uRenderBitVector, K3DCamera* pCamera, KViewportObject** ppViewportList, int nViewportCount, int nLimitDistance/* = 0*/ ) { if( 0 != m_dwCurrentTime ) { K3DVertex vtCameraTarget; pCamera->GetTargetPos( vtCameraTarget.x, vtCameraTarget.y, vtCameraTarget.z ); //#ifndef NDEBUG int nRealSegmentX = vtCameraTarget.x/m_fSegmentLength; int nRealSegmentY = vtCameraTarget.y/m_fSegmentLength; m_pPVSObjectManager->SetRootSegment( nRealSegmentX, nRealSegmentY ); //#endif // 0번이 메인 viewport DrawSegments( uRenderBitVector, pCamera->GetCamPos(), vtCameraTarget, ppViewportList[0]->GetFrustum(), ppViewportList, nViewportCount, m_dwCurrentTime, nLimitDistance ); if(m_bDrawAttributeWire) { ppViewportList[0]->RegisterWire(m_prWireAttributePolygon); ppViewportList[0]->RegisterWire(m_prWireDetour); ppViewportList[0]->RegisterWire(m_prWireQuadTree); } g_TerrainSegments = m_SegmentMap.size(); // TEMP - by Tyburn } } bool CTerrainMapEngine::Process( DWORD dwTime, K3DCamera* pCamera/* = NULL*/ ) { if( m_dwCurrentTime == dwTime ) return true; m_dwCurrentTime = dwTime; if( NULL != pCamera ) { K3DVertex vtCameraTarget; pCamera->GetTargetPos( vtCameraTarget.x, vtCameraTarget.y, vtCameraTarget.z ); SetVisibleArea( vtCameraTarget.x, vtCameraTarget.y, m_fixVisibleDistance ); //ProcessColoringChange( vtCameraTarget.x, vtCameraTarget.y ); } ProcessSegmentThread(); //Loading TerrainSegment ProcessLoadSegment(); //세그먼트를 못찾은 Prop처리 ProcessMiaProp(); return true; } const TERRAINMAP_PROPERTIES::COLORING& CTerrainMapEngine::GetSegmentColoring( float x, float y ) const { if( x >= 0.f && y >= 0.f ) { int nSegmentIndex = GetSegmentIndex( int(x / m_fSegmentLength), int(y / m_fSegmentLength) ); TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it != m_SegmentMap.end() ) return it->second->GetMapColoring(); } return c_DefaultColoring; } void CTerrainMapEngine::SetPrimaryLightToSegments() { for( TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); it != m_SegmentMap.end(); it++ ) //it->second->SetTerrainLight( K3DLight( K3DColor(1,1,1), K3DColor(1,1,1), K3DColor(1,1,1), m_CurrentMapColoring.litPrimary.direction )/*m_CurrentMapColoring.litPrimary*/, m_pDevice ); it->second->SetTerrainLight( m_CurrentMapColoring.litPrimary, m_pDevice ); } bool CTerrainMapEngine::CreateSegment( int nSegmentX, int nSegmentY, CTerrainInfoForSegment* pTerrainInfo, CTerrainInfoForSegment::PROPLOAD_INFO& PropLoadInfo, CTerrainInfoForSegment::SPEEDGRASSLOAD_INFO & SpeedGrassInfo) { int nSegmentIndex = 0; // lightmap on/off 때문에 lock을 여기로 옮김 THREAD_SYNCRONIZE( &g_TerrainCS ); nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); // _oprint( "!!! CreateSegment : %d\n", nSegmentIndex ); if( (-1) == nSegmentIndex ) { delete pTerrainInfo; return false; } // 지형 정보에 현재의 라이트 설정 if( pTerrainInfo ) //pTerrainInfo->SetTerrainLight( K3DLight( K3DColor(1,1,1), K3DColor(1,1,1), K3DColor(1,1,1), m_CurrentMapColoring.litPrimary.direction ) ); //m_CurrentMapColoring.litPrimary pTerrainInfo->SetTerrainLight( m_CurrentMapColoring.litPrimary ); // 세그먼트 생성 ( pTerrainInfo는 세그먼트가 보유하다가 세그먼트 소멸시 해제된다. ) bool bResult = false; CTerrainSegment* pSegment = NULL; pSegment = new CTerrainSegment ( pTerrainInfo //, m_FilteringType , m_RenderMode , m_pSeamlessWorldInfo->GetTileLength() , m_pSeamlessWorldInfo->GetTileCountPerSegment() , (K3DIndexBuffer**)m_spCommonIndexBuffer , m_pTextureInfo , m_pPropInfo , m_pDevice , nSegmentIndex , bResult , m_dwCurrentTime , &m_vtOriginCorrection , nSegmentIndex , nSegmentX , nSegmentY , m_bEnableLightmap); if( !bResult ) { delete pSegment; return false; } // 이미 세그먼트가 존재한다면 먼저 삭제한다. ( 필터링 변경시 존재하는 것을 재생성 할 수 있다. ) TERRAINSEGMENTMAP::iterator it = m_SegmentMap.find( nSegmentIndex ); if( it != m_SegmentMap.end() ) { // TODO : 저사양 물관련 ( 세그먼트 지우기 전에 해줘야 한다 ) DeleteWaterSegmentCount((*it).second->GetMapX(), (*it).second->GetMapY()); DeleteSpeedGrassSegmentCount( (*it).second ); // _oprint( "m_SegmentMap 삭제 01 : %d\n", nSegmentIndex ); m_DeleteSegmentList.push_back( it->second ); //스레드 때문에 따로 지운다. m_SegmentMap.erase( it ); //#ifndef NDEBUG // _oprint( "MAP - CreateSegment : m_SegmentMap.erase [%d]\n", nSegmentIndex ); //#endif } // 인접한 세그먼트들로부터 prop정보를 받는다. TakeNeighborsPropInfo( nSegmentX, nSegmentY, pSegment ); // 세그먼트를 map에 삽입 m_SegmentMap.insert( std::make_pair( nSegmentIndex, pSegment ) ); // _oprint( "MAP - CreateSegment : m_SegmentMap.insert [%d]\n", nSegmentIndex ); int nSegmentCountPerMap = m_pSeamlessWorldInfo->GetSegmentCountPerMap(); int nMapX = (nSegmentX / nSegmentCountPerMap); int nMapY = (nSegmentY / nSegmentCountPerMap); pSegment->SetMapXY(nMapX, nMapY); // TODO : 저사양 물 관련 CTerrainLowQualityWaterInfo* pLQWater = FindLowQualityWaterInfo(nMapX, nMapY); if(pLQWater == NULL) { // 파일 열기 std::string strWaterName = m_pSeamlessWorldInfo->GetLQWaterFileName( nMapX, nMapY ).c_str(); KStream* pStream = KFileManager::Instance().CreateStreamFromResource( strWaterName.c_str(), true ); if( pStream ) { pLQWater = new CTerrainLowQualityWaterInfo(pStream, m_pSeamlessWorldInfo, strWaterName.c_str(), nMapX, nMapY); if( pLQWater->GetState() == CTerrainLowQualityWaterInfo::INFO_STATE::_DELETE_INFO_ ) { SAFE_DELETE( pLQWater ); } else { m_LQWaterInfo.push_back(pLQWater); } } } else pLQWater->AddSegMent(); //이맵에 대한 세그먼트가 하나 추가됐다 // 현재 세그먼트 상의 prop 생성 TERRAINPROP_VECTOR PropVector; if( 0 < PropLoadInfo.nPropLoadCount ) { PropVector.reserve( PropLoadInfo.nPropLoadCount ); CreateProps( PropVector , pTerrainInfo->GetOrigin() , (nMapY * m_pSeamlessWorldInfo->GetMapCount().cx) + nMapX , pSegment , PropLoadInfo.pPropLoadStructs , PropLoadInfo.nPropLoadCount ); } SAFE_DELETE_ARRAY( PropLoadInfo.pPropLoadStructs ); { THREAD_SYNCRONIZE( &g_TerrainCS ); // 생성된 prop정보를 자신을 비롯한 주변 세그먼트에 넘긴다. SetPropInfoToSegment( PropVector ); } CTerrainGrassColonyInfoForMap* pTerrainGrassColony = GetTerrainGrassColonyInfoForMap( nMapX, nMapY ); if( pTerrainGrassColony ) { pTerrainGrassColony->AddSegMentRef(); } else { CreateGrassColonyInfoForMap( nMapX, nMapY ); } if( SpeedGrassInfo.nSpeedGrassGroupCount > 0 ) { for( int nGroupCnt = 0; nGroupCnt < SpeedGrassInfo.nSpeedGrassGroupCount; ++nGroupCnt ) { CreateSpeedGrass( SpeedGrassInfo.pSpeedGrassGroup[nGroupCnt] , pTerrainInfo->GetOrigin() , (nMapY * m_pSeamlessWorldInfo->GetMapCount().cx) + nMapX , pSegment , nMapX , nMapY ); } SpeedGrassInfo.clear(); } return true; } /// 2010.11.10 - prodongi //#ifdef _DEV //extern bool g_bDebugMode; //#endif static inline void _GetNearestSegmentPosFrom( float x, float y, float segX, float segY, float segSize, float& outX, float& outY ) { float xmin = segX * segSize, xmax = xmin + segSize; float ymin = segY * segSize, ymax = ymin + segSize; if ( x < xmin ) outX = xmin; else if ( x > xmax ) outX = xmax; else outX = x; if ( y < ymin ) outY = ymin; else if ( y > ymax ) outY = ymax; else outY = y; } void CTerrainMapEngine::DrawSegments( const unsigned long uRenderBitVector, const K3DVertex& rCameraPos, const K3DVector& rTargetPos, const K3DVector* pFrustum, KViewportObject** ppViewportList, int nViewportCount, DWORD dwCurrentTime, int nLimitDistance/*=0*/ ) { THREAD_SYNCRONIZE( &g_TerrainCS ); static int nAddSegmentCnt = 0; static int nDrawSegmentCnt = 0; static int nDrawPropCnt = 0; static int nOldDrawSegmentCnt = 0; m_unLastRenderedTime = GetSafeTickCount(); ++m_unLastRenderedTick; bool bIsPVSAct = m_pPVSObjectManager->IsPVSActive(); TERRAINSEGMENTMAP::iterator it; for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) != nSegmentIndex ) { //// 디버그용 코드 by blackfish -> //static bool bDebug = false; ////static bool bDebug = true; //if(bDebug) //{ // if(nSegmentIndex != GetSegmentIndex((int) (rCameraTargetPos.x / m_fSegmentLength), (int) (rCameraTargetPos.y / m_fSegmentLength))) // continue; //} //// -> 디버그용 코드 by blackfish it = m_SegmentMap.find( nSegmentIndex ); if( it != m_SegmentMap.end() ) { //if( it->second->GetMapX() == 7 && it->second->GetMapY() == 3 ) //{ // int x = 0; //} float fNX, fNY; _GetNearestSegmentPosFrom( rCameraPos.x, rCameraPos.y, (float)nSegmentX, (float)nSegmentY, m_fSegmentLength, fNX, fNY ); // 카메라 타겟과의 거리 체크 float fGapX = rTargetPos.x - fNX; //GetSegmentCenterPosX( nSegmentX ); float fGapY = rTargetPos.y - fNY; //GetSegmentCenterPosY( nSegmentY ); float SquareDistance = (fGapX * fGapX) + (fGapY * fGapY); //if( SquareDistance <= m_fTerrainSquareVisibleDist ) { if( SquareDistance < m_fCustomTextureSquareDist ) it->second->SetCustomValid( true ); else it->second->SetCustomValid(); if( bIsPVSAct ) { if( it->second->PVSDrawSegment( uRenderBitVector, ppViewportList, nViewportCount, pFrustum, rCameraPos, m_fTerrainVisibleDistance, m_fPropLodDistance, SquareDistance / m_fTerrainSquareVisibleDist, dwCurrentTime, m_unLastRenderedTick, nDrawPropCnt, m_pPVSObjectManager ) ) { nDrawSegmentCnt++; } } else { // sonador 1.6.2 프랍 렌더링시 정렬 문제 해결 if( it->second->DrawSegment( uRenderBitVector, ppViewportList, nViewportCount, pFrustum, rTargetPos, m_fTerrainVisibleDistance, m_fPropLodDistance, SquareDistance / m_fTerrainSquareVisibleDist, dwCurrentTime, m_unLastRenderedTick, nDrawPropCnt, nLimitDistance ) ) { nDrawSegmentCnt++; } } if( IsEnableTerrainLOD() ) { if( nOldDrawSegmentCnt != nDrawSegmentCnt && m_RenderMode == TERRAIN_RENDERMODE_DEFAULT ) { for( int nCrackCnt = 0; nCrackCnt < TERRAIN_LOD_CRACK_MAX; ++nCrackCnt ) { int nNeighborsSegmentIndex = GetSegmentIndex( nSegmentX+c_nCrackBySegmentIndex[nCrackCnt][0], nSegmentY+c_nCrackBySegmentIndex[nCrackCnt][1] ); if( (-1) != nNeighborsSegmentIndex ) { TERRAINSEGMENTMAP::iterator itNeighbors = m_SegmentMap.find( nNeighborsSegmentIndex ); if( itNeighbors != m_SegmentMap.end() ) { it->second->SetNeighborsTerrainLoadLevel( itNeighbors->second->CalcLength( rCameraPos ), (TERRAIN_LOD_CRACK)nCrackCnt ); } } else { it->second->SetNeighborsTerrainLoadLevel( TERRAIN_LOD_LEVEL_MAX, (TERRAIN_LOD_CRACK)nCrackCnt ); } } nOldDrawSegmentCnt = nDrawSegmentCnt; } } } } else { AddDrawSegment( nSegmentIndex ); //AddSegmentThread( nSegmentIndex ); // 세그먼트를 생성시키도록 한다. //_oprint( "AddSegmentThread : DrawSegments\n" ); //nAddSegmentCnt++; } } } } //Occlusion Culling // ppViewportList[0]->OcclusionCulling(); /// 2010.11.10 - prodongi //#ifdef _DEV if (g_UserInfo.isMonkeyTail()) { extern bool g_bDebugMode; if( g_bDebugMode && 0 != HIBYTE(GetAsyncKeyState(VK_SHIFT)) && 0 != HIBYTE(GetAsyncKeyState(VK_CONTROL)) ) _oprint( "SeqMent : Draw[%d] Add[%d] - Prop[%d]\n", nDrawSegmentCnt, nAddSegmentCnt, nDrawPropCnt ); } //#endif nDrawSegmentCnt = nAddSegmentCnt = nDrawPropCnt = 0; nOldDrawSegmentCnt = 0; } void CTerrainMapEngine::RemoveUnnecessarySegments() { // assert( _CrtCheckMemory() && "Memory Error!!!" ); DWORD RemoveStartTm = GetSafeTickCount(); if( !g_TerrainCS.TryLock() ) return; if( GetSafeTickCount() - RemoveStartTm > 80 ) { _oprint( "!!! RemoveUnnecessarySegments() Warning- LOCK!!! %dms\n", GetSafeTickCount() - RemoveStartTm ); } RemoveStartTm = GetSafeTickCount(); int total_cnt = (int)m_SegmentMap.size(); int delete_cnt = 0; DWORD DeleteTime = 0; // 제거 작업할 때가 된 경우 if( (m_dwCurrentTime - m_dwPreviousSegmentRemoveTime) > m_dwSegmentRemovePeriod ) { m_dwPreviousSegmentRemoveTime = m_dwCurrentTime; // 제거 작업 기록 시간 변경 //쓰레드에서 지워진것 삭제 { for( std::vector< CTerrainSegment* >::iterator it = m_DeleteSegmentList.begin(); it != m_DeleteSegmentList.end(); ) { if( delete_cnt > 5 ) break; delete (*it); ++delete_cnt; it = m_DeleteSegmentList.erase( it ); } } TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); while( it != m_SegmentMap.end() ) { // 랙 관계로 한번에 5개 이상은 지우지 않도록 함. if( delete_cnt > 5 ) break; KPoint ptSegment = GetSegmentPos( it->first ); #ifndef _RELEASE DWORD d_time00 = it->second->GetRenderedTime(); DWORD d_time01 = m_dwSegmentExpireTime; #endif // 유통기한(?)이 경과하였고 가시영역에서 벗어나 있는 세그먼트를 제거한다. if( (m_dwCurrentTime - it->second->GetRenderedTime()) > m_dwSegmentExpireTime && !IsInsideVisibleArea( ptSegment.x, ptSegment.y ) ) { CTerrainSegment* pSegment = it->second; // TODO : 저사양 물관련 ( 세그먼트 지우기 전에 해줘야 한다 ) DeleteWaterSegmentCount(pSegment->GetMapX(), pSegment->GetMapY()); DeleteSpeedGrassSegmentCount( pSegment ); DWORD dwPrev = GetSafeTickCount(); // _oprint( "m_SegmentMap 삭제 02\n" ); ++delete_cnt; delete it->second; it = m_SegmentMap.erase( it ); DeleteTime += ( GetSafeTickCount() - dwPrev ); //#ifndef NDEBUG // const int nSegmentIndex = GetSegmentIndex( ptSegment.x, ptSegment.y ); // _oprint( "MAP - RemoveUnnecessarySegments : m_SegmentMap.erase [%d]\n", nSegmentIndex ); //#endif } else it++; } } if( GetSafeTickCount() - RemoveStartTm > 100 ) { _oprint( "!!! RemoveUnnecessarySegments() Warning- Total:%d Delete:%d DeleteTime:%d\n", total_cnt, delete_cnt, DeleteTime ); } g_TerrainCS.UnLock(); // assert( _CrtCheckMemory() && "Memory Error!!!" ); } // { [sonador][COLLIDABLE_CAMERA] bool CTerrainMapEngine::GetPropLineCrossedPointTwoSidesWithCollisionMesh ( const K3DVector& rNear , const K3DVector& rFar , float& fNearestT , K3DVector& rPickedPoint #ifdef _DEBUG , K3DVector* pCollidedPolygon #endif ) const { K3DVector rCandidatePickedPoint, rTempPickedPoint; bool bFound = false; for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) continue; // 시야에 보이는넘이 아니라면 KIN~ if( !IsRenderedSegment( (*it).second ) ) continue; // (*it) 의 프랍들에 대해 체크 if( (*it).second->GetPropLineCrossedPointTwoSidesWithCollisionMesh( rNear, rFar, fNearestT, rCandidatePickedPoint #ifdef _DEBUG , pCollidedPolygon #endif ) ) { bFound = true; } } } if( bFound ) { rPickedPoint = rCandidatePickedPoint; } return bFound; } // } // { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection) bool CTerrainMapEngine::GetTerrainEllipsoidCrossedPoint ( const K3DVector& rBase , const K3DVector& rVelocity , const K3DVector& rRadius , float& fNearestT , K3DVector& rPickedPoint #ifdef _DEBUG , K3DVector* pCollidedPolygon , std::vector< std::pair< K3DVector, KColor > >& vecDebugLine #endif ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif bool bFound = false; K3DVector vCandidatePickedPoint; K3DVector vNearInESpace ( rBase.x / rRadius.x, rBase.y / rRadius.y, rBase.z / rRadius.z ); K3DVector vVelocityInESpace ( rVelocity.x / rRadius.x, rVelocity.y / rRadius.y, rVelocity.z / rRadius.z ); K3DMatrix matCBMtoESpace; K3DMatrixIdentity( matCBMtoESpace ); matCBMtoESpace._11 = 1.0f / rRadius.x; matCBMtoESpace._22 = 1.0f / rRadius.y; matCBMtoESpace._33 = 1.0f / rRadius.z; for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) continue; if( !IsRenderedSegment( (*it).second ) ) continue; if( it->second->GetEllipsoidCrossedPoint( rBase, rBase + rVelocity, rRadius, vNearInESpace, vVelocityInESpace, matCBMtoESpace, fNearestT, vCandidatePickedPoint #ifdef _DEBUG , pCollidedPolygon, vecDebugLine #endif ) ) { bFound = true; } } } if( bFound ) { rPickedPoint.Set( vCandidatePickedPoint.x * rRadius.x, vCandidatePickedPoint.y * rRadius.y, vCandidatePickedPoint.z * rRadius.z ); #ifdef _DEBUG if( pCollidedPolygon ) { K3DMatrix matCBMtoRSpace; K3DMatrixInverse( matCBMtoRSpace, matCBMtoESpace ); for( int n = 0; n < 3; ++n ) K3DVectorTransform( pCollidedPolygon[ n ], pCollidedPolygon[ n ], matCBMtoRSpace ); } #endif } #ifndef NDEBUG // _oprint( "[시간 검사 GetTerrainLineCrossedPoint : %d]\n", GetSafeTickCount()-dwStartTime ); #endif return bFound; } // 2010.07.22 - prodongi //std::vector collidedMesh; // 전역변수로 중복체크.. 젠장 -.- std::vector collidedMesh; // 전역변수로 중복체크.. 젠장 -.- bool CTerrainMapEngine::GetPropEllipsoidCrossedPointTwoSides ( const K3DVector& rNear , const K3DVector& rFar , const K3DVector& rRadius , float& fNearestT , K3DVector& rPickedPoint #ifdef _DEBUG , K3DVector* pCollidedPolygon #endif ) const { bool bFound = false; K3DVector rCandidatePickedPoint; K3DVector vNearInESpace ( rNear.x / rRadius.x, rNear.y / rRadius.y, rNear.z / rRadius.z ); K3DVector vFarInESpace ( rFar.x / rRadius.x, rFar.y / rRadius.y, rFar.z / rRadius.z ); K3DMatrix matCBMtoESpace; K3DMatrixIdentity( matCBMtoESpace ); matCBMtoESpace._11 = 1.0f / rRadius.x; matCBMtoESpace._22 = 1.0f / rRadius.y; matCBMtoESpace._33 = 1.0f / rRadius.z; collidedMesh.clear(); for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) continue; // 시야에 보이는넘이 아니라면 KIN~ if( !IsRenderedSegment( (*it).second ) ) continue; // (*it) 의 프랍들에 대해 체크 if( (*it).second->GetPropEllipsoidCrossedPointTwoSidesWithCollisionMesh( rNear, rFar, vNearInESpace, vFarInESpace, matCBMtoESpace, fNearestT, rCandidatePickedPoint #ifdef _DEBUG , pCollidedPolygon #endif ) ) { bFound = true; } } } if( bFound ) { rPickedPoint.Set( rCandidatePickedPoint.x * rRadius.x, rCandidatePickedPoint.y * rRadius.y, rCandidatePickedPoint.z * rRadius.z ); #ifdef _DEBUG if( pCollidedPolygon ) { K3DMatrix matCBMtoRSpace; K3DMatrixInverse( matCBMtoRSpace, matCBMtoESpace ); for( int n = 0; n < 3; ++n ) K3DVectorTransform( pCollidedPolygon[ n ], pCollidedPolygon[ n ], matCBMtoRSpace ); } #endif } return bFound; } // } float CTerrainMapEngine::GetPropLineCrossedPointTwoSides( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif //bool bGetStoolPoint = false; float fT = 1.0f; float fSmallestT = 1.0f; K3DVector rCandidatePickedPoint, rTempPickedPoint; for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) continue; // 시야에 보이는넘이 아니라면 KIN~ if( !IsRenderedSegment( (*it).second ) ) continue; // (*it) 의 프랍들에 대해 체크 (*it).second->GetPropLineCrossedPointTwoSides( rNear, rFar, fSmallestT, rCandidatePickedPoint ); } } if(fSmallestT != 1.0f) { rPickedPoint = rCandidatePickedPoint; //return true; } #ifndef NDEBUG // _oprint( "[시간 검사 GetPropLineCrossedPointTwoSides : %d]\n", GetSafeTickCount()-dwStartTime ); #endif //return false; return fSmallestT; } TERRAIN_PICK_RESULT CTerrainMapEngine::GetStoolLineCrossedPoint( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif TERRAIN_PICK_RESULT nStoolPickResult = TERRAIN_STOOL_PICK_FAILED; float fStoolDistance = 1.f; K3DVector vtCurrentPoint; std::vector< CTerrainProp * > check_list; for( int nSegmentY = m_rcVisibleSegment.top; nSegmentY < m_rcVisibleSegment.bottom; ++nSegmentY ) { for( int nSegmentX = m_rcVisibleSegment.left; nSegmentX < m_rcVisibleSegment.right; ++nSegmentX ) { int nSegmentIndex = GetSegmentIndex( nSegmentX, nSegmentY ); if( (-1) == nSegmentIndex ) continue; TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.find( nSegmentIndex ); if( it == m_SegmentMap.end() ) continue; // 시야에 보이는넘이 아니라면 KIN~ if( !IsRenderedSegment( (*it).second ) ) continue; (*it).second->GetStoolLineCrossedPoint( rNear, rFar, rPickedPoint, nStoolPickResult, fStoolDistance, vtCurrentPoint, check_list ); } } #ifndef NDEBUG // _oprint( "[시간 검사 GetStoolLineCrossedPoint : %d]\n", GetSafeTickCount()-dwStartTime ); #endif return nStoolPickResult; } float CTerrainMapEngine::GetTerrainLineCrossedPointTwoSides( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint, const K3DCamera* pCamera ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif K3DVertex vtCamera; pCamera->GetCamPos( vtCamera.x, vtCamera.y, vtCamera.z ); float fT = 1.0f, fSmallestT = 1.0f; float fTerrainDistance = (0 < m_fPickingSquareDistance) ? m_fPickingSquareDistance : m_fTerrainSquareVisibleDist; for( TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.begin(); it != m_SegmentMap.end(); it++ ) { if( it->second->IsVisible() ) { KPoint ptSegment = GetSegmentPos( it->first ); float fGapX = vtCamera.x - GetSegmentCenterPosX( ptSegment.x ); float fGapY = vtCamera.y - GetSegmentCenterPosY( ptSegment.y ); float fCurrentDistance = ( (fGapX * fGapX) + (fGapY * fGapY) ); if( fTerrainDistance > fCurrentDistance ) { K3DVector vtCurrentPoint; fT = it->second->GetLineCrossedPointTwoSides( rNear, rFar, vtCurrentPoint ); if(fSmallestT > fT) { fSmallestT = fT; fTerrainDistance = fCurrentDistance; rPickedPoint = vtCurrentPoint; } } } } #ifndef NDEBUG // _oprint( "[시간 검사 GetTerrainLineCrossedPoint : %d]\n", GetSafeTickCount()-dwStartTime ); #endif return fSmallestT; } // 코드를 보면 알겠지만 지형 피킹 용으로만 써야 한다 - by blackfish TERRAIN_PICK_RESULT CTerrainMapEngine::GetTerrainLineCrossedPoint( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint, const K3DCamera* pCamera ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif K3DVertex vtStart = pCamera->GetTargetPos(); TERRAIN_PICK_RESULT nPickResult = TERRAIN_PICK_FAILED; float fTerrainDistance = (0 < m_fPickingSquareDistance) ? m_fPickingSquareDistance : m_fTerrainSquareVisibleDist; float fShortestDistance = fTerrainDistance; float fCurrentDistance = fShortestDistance; K3DVector vecTemp; K3DVector vtCurrentPoint; for( TERRAINSEGMENTMAP::const_iterator it = m_SegmentMap.begin(); it != m_SegmentMap.end(); it++ ) { if( it->second->IsVisible() ) { KPoint ptSegment = GetSegmentPos( it->first ); if( it->second->GetLineCrossedPoint( rNear, rFar, vtCurrentPoint ) ) { vecTemp = vtCurrentPoint - vtStart; vecTemp.z = 0.0f; fCurrentDistance = vecTemp.SquareMagnitude(); if( fShortestDistance > fCurrentDistance ) { nPickResult = TERRAIN_PICK_SUCCECED; fShortestDistance = fCurrentDistance; rPickedPoint = vtCurrentPoint; } else { if(nPickResult != TERRAIN_PICK_SUCCECED) nPickResult = TERRAIN_PICK_DISTANCE_FAILED; } } } } #ifndef NDEBUG // _oprint( "[시간 검사 GetTerrainLineCrossedPoint : %d]\n", GetSafeTickCount()-dwStartTime ); #endif return nPickResult; } void CTerrainMapEngine::GetPropMinMaxVertices( const CTerrainPropForGame* pProp, K3DVertex& rMin, K3DVertex& rMax ) const { #ifndef NDEBUG DWORD dwStartTime = GetSafeTickCount(); #endif const K3DBoundRotCube* pCube = pProp->GetBoundCube(); if( pCube ) { rMin = pCube->GetMinVertex(); rMax = pCube->GetMaxVertex(); } else { assert( 0 ); rMin = K3DVertex( -1, -1, -1 ); rMax = K3DVertex( 1, 1, 1 ); } //// 일단 stool의 영역을 모두 범위에 두도록 한다 ...... //const TERRAINSTOOL_VECTOR& rStoolVector = pProp->GetStoolVector(); //for( TERRAINSTOOL_VECTOR::const_iterator itStool = rStoolVector.begin(); itStool != rStoolVector.end(); itStool++ ) //{ // const K3DPoint& rStoolMin = (*itStool).GetMinPoint(); // const K3DPoint& rStoolMax = (*itStool).GetMaxPoint(); // if( rMin.x > rStoolMin.x ) rMin.x = rStoolMin.x; // if( rMin.y > rStoolMin.y ) rMin.y = rStoolMin.y; // if( rMax.x < rStoolMax.x ) rMax.x = rStoolMax.x; // if( rMax.y < rStoolMax.y ) rMax.y = rStoolMax.y; //} #ifndef NDEBUG // _oprint( "[시간 검사 GetPropMinMaxVertices : %d]\n", GetSafeTickCount()-dwStartTime ); #endif } struct EventLocationExplorer { EventLocationExplorer() { nPriority = std::numeric_limits::max(); strScript.clear(); } void operator()( const CTerrainMapEngine::EventPolygon & rh ) { if( nPriority > rh.nPriority ) { nPriority = rh.nPriority; strScript = rh.strScript; } } int nPriority; std::string strScript; }; struct EventAreaExplorer { EventAreaExplorer() { } ~EventAreaExplorer() { vecID.clear(); vecPolyNum.clear(); } void operator()( const CTerrainMapEngine::EventAreaPolygon & rh ) { vecID.push_back(rh.nID); vecPolyNum.push_back(rh.nPolygonNum); } std::vector vecID; std::vector vecPolyNum; }; void CTerrainMapEngine::CreateProps( TERRAINPROP_VECTOR& rPropVector, const K3DPoint& rOrigin, int nMapIndex, const CTerrainSegment* pThisSegment, const NFM_PROPSTRUCT_V15* pPropLoadStructs, int nStructCount ) { for( int n( 0 ); n < nStructCount; ++n ) { WORD wPropNum = pPropLoadStructs->wPropNum; //GetPropType 으로 NPC를 검사 할 순 없다. 풀도 COB를 사용 하기 때문에. { float x = (rOrigin.x + pPropLoadStructs->x); float y = (rOrigin.y + pPropLoadStructs->y); TERRAINPROP_ID dwTerrainPropID = CreateTerrainPropID( nMapIndex, pPropLoadStructs->nPropIndex ); // _oprint( "CreateProps : %d\n", nTerrainPropID ); CTerrainPropForGameSPtr spProp( new CTerrainPropForGame ( dwTerrainPropID , K3DVector( x, y, pThisSegment->GetSegmentHeight( x, y ) ) , K3DVector( pPropLoadStructs->fRotateX, pPropLoadStructs->fRotateY, pPropLoadStructs->fRotateZ ) , K3DVector( pPropLoadStructs->fScaleX, pPropLoadStructs->fScaleY, pPropLoadStructs->fScaleZ ) , pPropLoadStructs->fZOffset , wPropNum , m_pPropInfo , this , pThisSegment , pPropLoadStructs->bLockHeight , pThisSegment->GetSegmentIndex() , n , m_bEnableLightmap , pPropLoadStructs->nTextureGroupIndex ) ); //const K3DBoundRotCube* pBound = pProp->GetBoundCube(); //if( pBound ) //{ // K3DVector vMin = pBound->GetMinVertex(); // K3DVector vMax = pBound->GetMaxVertex(); // float fZ1 = pThisSegment->GetSegmentHeight( ((vMin.x + vMax.x)/2.f), ((vMin.y + vMax.y)/2.f) ); // pProp->SetZ( fZ1 ); //} if( pPropLoadStructs->bLockHeight ) spProp->SetZ( pPropLoadStructs->fLockHeight ); spProp->SetOriginCorrection( &m_vtOriginCorrection ); rPropVector.push_back( spProp ); } pPropLoadStructs++; } } void CTerrainMapEngine::CreateSpeedGrass( CTerrainInfoForSegment::SPEEDGRASSLOAD_INFO::SpeedGrassGroup & rSpeedGrassGroup, const K3DPoint& rOrigin, int nMapIndex, CTerrainSegment* pThisSegment, int nMapX, int nMapY ) { if( rSpeedGrassGroup.nSpeedGrassLoadCount > 0 ) { CTerrainSpeedGrassForGame* pTerrainSpeedGrass = new CTerrainSpeedGrassForGame( this, pThisSegment, rSpeedGrassGroup.nSpeedGrassID ); for( int nGrassCnt = 0; nGrassCnt < rSpeedGrassGroup.nSpeedGrassLoadCount; ++nGrassCnt ) { NFM_SPEED_GRASS_V1* pStruct = &rSpeedGrassGroup.pSpeedGrassLoadStructs[nGrassCnt]; K3DVector & vPosition = K3DVector( pStruct->px+rOrigin.x, pStruct->py+rOrigin.y, 0.0f ); K3DVector & vNormal = K3DVector( pStruct->nx, pStruct->ny, pStruct->nz ); vPosition.z = pThisSegment->GetSegmentHeight( vPosition.x, vPosition.y ); pTerrainSpeedGrass->AddGrass( vPosition, vNormal ); } GrassColonyInfo* pGrassColonyInfo = GetGrassColonyInfoForMap( nMapX, nMapY, pTerrainSpeedGrass->GetID() ); if( !pGrassColonyInfo ) { SAFE_DELETE( pTerrainSpeedGrass ); return; } pTerrainSpeedGrass->CreateGrassSeq( pGrassColonyInfo ); pThisSegment->AddSpeedGrass( pTerrainSpeedGrass ); } } void CTerrainMapEngine::TakeNeighborsPropInfo( int nThisSegmentX, int nThisSegmentY, CTerrainSegment* pThisSegment ) const { // 인접 8개의 세그먼트들의 prop들 중에서 영역이 걸친 prop들의 정보를 받는다. // ( 아직 pThisSegment가 std::map에 추가되기 전이므로 9개가 아닌 8개가 된다. ) int nSegmentSX = nThisSegmentX - 1; int nSegmentSY = nThisSegmentY - 1; int nSegmentEX = nThisSegmentX + 1; int nSegmentEY = nThisSegmentY + 1; K3DPoint ptMinSegment( float(nThisSegmentX ) * m_fSegmentLength, float(nThisSegmentY ) * m_fSegmentLength ); K3DPoint ptMaxSegment( float(nThisSegmentX + 1) * m_fSegmentLength, float(nThisSegmentY + 1) * m_fSegmentLength ); for( int nSegmentY = nSegmentSY; nSegmentY <= nSegmentEY; ++nSegmentY ) { for( int nSegmentX = nSegmentSX; nSegmentX <= nSegmentEX; ++nSegmentX ) { TERRAINSEGMENTMAP::const_iterator itNeighbor = m_SegmentMap.find( GetSegmentIndex( nSegmentX, nSegmentY ) ); if( itNeighbor != m_SegmentMap.end() ) { //// 인접 세그먼트의 prop정보 //const TERRAINPROP_VECTOR& rPropVector = itNeighbor->second->GetPropVector(); //for( TERRAINPROP_VECTOR::const_iterator itProp = rPropVector.begin(); itProp != rPropVector.end(); itProp++ ) //{ // K3DVertex vtMinProp, vtMaxProp; // GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); // // prop과 세그먼트 영역이 겹치는 경우 prop 정보를 추가시킨다. // if( Is2DCollision( vtMinProp.x, vtMinProp.y, vtMaxProp.x, vtMaxProp.y, ptMinSegment.x, ptMinSegment.y, ptMaxSegment.x, ptMaxSegment.y ) ) // pThisSegment->AddProp( *itProp ); //} ////const TERRAINPROP_VECTOR& rPropLoadingVector = itNeighbor->second->GetPropLoadingVector(); ////for( TERRAINPROP_VECTOR::const_iterator itProp = rPropLoadingVector.begin(); itProp != rPropLoadingVector.end(); itProp++ ) ////{ //// K3DVertex vtMinProp, vtMaxProp; //// GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); //// // prop과 세그먼트 영역이 겹치는 경우 prop 정보를 추가시킨다. //// if( Is2DCollision( vtMinProp.x, vtMinProp.y, vtMaxProp.x, vtMaxProp.y, ptMinSegment.x, ptMinSegment.y, ptMaxSegment.x, ptMaxSegment.y ) ) //// pThisSegment->AddProp( *itProp ); ////} //const TERRAINPROP_VECTOR& rPropLoadedVector = itNeighbor->second->GetPropLoadedVector(); //for( TERRAINPROP_VECTOR::const_iterator itProp = rPropLoadedVector.begin(); itProp != rPropLoadedVector.end(); itProp++ ) //{ // K3DVertex vtMinProp, vtMaxProp; // GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); // // prop과 세그먼트 영역이 겹치는 경우 prop 정보를 추가시킨다. // if( Is2DCollision( vtMinProp.x, vtMinProp.y, vtMaxProp.x, vtMaxProp.y, ptMinSegment.x, ptMinSegment.y, ptMaxSegment.x, ptMaxSegment.y ) ) // pThisSegment->AddProp( *itProp ); //} pThisSegment->TakeNeighborsPropInfo( (*itNeighbor).second, ptMinSegment, ptMaxSegment ); /* for(int i = 0; i < 3; i++) { TERRAINPROP_VECTOR rPropVector; // 인접 세그먼트의 prop정보 switch(i) { case 0: rPropVector = itNeighbor->second->GetPropVector(); break; case 1: rPropVector = itNeighbor->second->GetPropLoadingVector(); break; case 2: rPropVector = itNeighbor->second->GetPropLoadedVector(); break; } for( TERRAINPROP_VECTOR::iterator itProp = rPropVector.begin(); itProp != rPropVector.end(); itProp++ ) { K3DVertex vtMinProp, vtMaxProp; GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); // prop과 세그먼트 영역이 겹치는 경우 prop 정보를 추가시킨다. if( Is2DCollision( vtMinProp.x, vtMinProp.y, vtMaxProp.x, vtMaxProp.y, ptMinSegment.x, ptMinSegment.y, ptMaxSegment.x, ptMaxSegment.y ) ) pThisSegment->AddProp( *itProp ); } } */ } } } } //std::vector< DWORD > g_vVec; void CTerrainMapEngine::SetPropInfoToSegment(TERRAINPROP_VECTOR& rPropVector) { for( TERRAINPROP_VECTOR::iterator itProp = rPropVector.begin(); itProp != rPropVector.end(); itProp++ ) { K3DVertex vtMinProp, vtMaxProp; GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); /* bool bCheck = true; std::vector< DWORD >::iterator iter = g_vVec.begin(); for( ; iter != g_vVec.end(); iter++ ) { if( (*itProp)->GetID() == (*iter) ) bCheck = false; } if( bCheck ) { AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMinProp.y, vtMinProp.z ), K3DVertex( vtMaxProp.x, vtMinProp.y, vtMinProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMaxProp.y, vtMinProp.z ), K3DVertex( vtMaxProp.x, vtMaxProp.y, vtMinProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMinProp.y, vtMaxProp.z ), K3DVertex( vtMaxProp.x, vtMinProp.y, vtMaxProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMaxProp.y, vtMaxProp.z ), K3DVertex( vtMaxProp.x, vtMaxProp.y, vtMaxProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMinProp.y, vtMinProp.z ), K3DVertex( vtMinProp.x, vtMaxProp.y, vtMinProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMaxProp.x, vtMinProp.y, vtMinProp.z ), K3DVertex( vtMaxProp.x, vtMaxProp.y, vtMinProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMinProp.x, vtMinProp.y, vtMaxProp.z ), K3DVertex( vtMinProp.x, vtMaxProp.y, vtMaxProp.z ) ); AddWireAttributePolygonLine( K3DVertex( vtMaxProp.x, vtMinProp.y, vtMaxProp.z ), K3DVertex( vtMaxProp.x, vtMaxProp.y, vtMaxProp.z ) ); g_vVec.push_back( (*itProp)->GetID() ); }*/ // prop의 영역이 걸치는 세그먼트들 // float 오차때문인지 프랍이 걸치는 세그먼트를 찾을수 없는 버그가 발생 한다 세그먼트 영역을 1씩 늘려준다 bool bMiaProp = true; int nSegmentSX = int( vtMinProp.x / m_fSegmentLength ) - 1; int nSegmentSY = int( vtMinProp.y / m_fSegmentLength ) - 1; int nSegmentEX = int( vtMaxProp.x / m_fSegmentLength ) + 1; int nSegmentEY = int( vtMaxProp.y / m_fSegmentLength ) + 1; for( int nSegmentY = nSegmentSY; nSegmentY <= nSegmentEY; ++nSegmentY ) { for( int nSegmentX = nSegmentSX; nSegmentX <= nSegmentEX; ++nSegmentX ) { TERRAINSEGMENTMAP::iterator itSegment = m_SegmentMap.find( GetSegmentIndex( nSegmentX, nSegmentY ) ); if( itSegment != m_SegmentMap.end() ) { itSegment->second->AddProp( *itProp ); // prop 추가 bMiaProp = false; } } } #ifdef _DEBUG if( bMiaProp ) { m_MiaPropVector.push_back( *itProp ); assert( false && "프랍이 세그먼트를 못찾았다 MiaProp으로 등록됨" ); } #endif } } //어째서 프랍을 찾고나서 해당 세그먼트가 생성이 될까 ㅡ,.ㅡ //맵 지형 밖에 프랍이 위치해 있을경우 발생됨 void CTerrainMapEngine::ProcessMiaProp() { if( m_MiaPropVector.empty() ) return; for( TERRAINPROP_VECTOR::iterator itProp = m_MiaPropVector.begin(); itProp != m_MiaPropVector.end(); ) { K3DVertex vtMinProp, vtMaxProp; GetPropMinMaxVertices( (*itProp), vtMinProp, vtMaxProp ); bool bMiaProp = true; int nSegmentSX = int( vtMinProp.x / m_fSegmentLength ) - 1; int nSegmentSY = int( vtMinProp.y / m_fSegmentLength ) - 1; int nSegmentEX = int( vtMaxProp.x / m_fSegmentLength ) + 1; int nSegmentEY = int( vtMaxProp.y / m_fSegmentLength ) + 1; for( int nSegmentY = nSegmentSY; nSegmentY <= nSegmentEY; ++nSegmentY ) { for( int nSegmentX = nSegmentSX; nSegmentX <= nSegmentEX; ++nSegmentX ) { TERRAINSEGMENTMAP::iterator itSegment = m_SegmentMap.find( GetSegmentIndex( nSegmentX, nSegmentY ) ); if( itSegment != m_SegmentMap.end() ) { itSegment->second->AddProp( *itProp ); // prop 추가 bMiaProp = false; } } } if( bMiaProp ) { _oprint( "Failed to find Prop Segment [Prop ID: %d]\n", (*itProp)->GetID() ); ++itProp; } else { itProp = m_MiaPropVector.erase( itProp ); } } } CTerrainSegmentLoadThread* CTerrainMapEngine::CreatePreparedThread( int nSegmentIndex ) { int nTileCountPerSegment = m_pSeamlessWorldInfo->GetTileCountPerSegment(); int nSegmentCountPerMap = m_pSeamlessWorldInfo->GetSegmentCountPerMap(); const KSize& rMapCount = m_pSeamlessWorldInfo->GetMapCount(); KPoint ptSegment = GetSegmentPos( nSegmentIndex ); // 스레드 처리 객체 생성 CTerrainSegmentLoadThread* pThread = new CTerrainSegmentLoadThread ( this , nSegmentIndex , K3DPoint( float(ptSegment.x) * m_fSegmentLength, float(ptSegment.y) * m_fSegmentLength ) , nTileCountPerSegment , (nTileCountPerSegment * nSegmentCountPerMap) , m_pSeamlessWorldInfo->GetTileLength() ); pThread->SetMapPos( ptSegment.x, ptSegment.y ); // 세그먼트를 생성하기 위해 읽어들여야 할 총 9개의 세그먼트 정보를 넘겨준다. int nCount = 0; for( int nSegmentY = (ptSegment.y - 1); nSegmentY <= (ptSegment.y + 1); ++nSegmentY ) { for( int nSegmentX = (ptSegment.x - 1); nSegmentX <= (ptSegment.x + 1); ++nSegmentX ) { if( 0 <= nSegmentX && nSegmentX < m_nSegmentXCount && 0 <= nSegmentY && nSegmentY < m_nSegmentYCount ) { int nMapPosX = (nSegmentX / nSegmentCountPerMap); int nMapPosY = (nSegmentY / nSegmentCountPerMap); std::string strMapFileName = m_pSeamlessWorldInfo->GetMapFileName( nMapPosX, nMapPosY ); if( !strMapFileName.empty() ) { int nSegmentXInMap = (nSegmentX % nSegmentCountPerMap); int nSegmentYInMap = (nSegmentY % nSegmentCountPerMap); int nSegmentIndexInMap = (nSegmentYInMap * nSegmentCountPerMap) + nSegmentXInMap; pThread->SetMapFileNameAndSegmentIndex( nCount, strMapFileName.c_str(), nSegmentIndexInMap ); } } ++nCount; } } return pThread; } void CTerrainMapEngine::ProcessLoadSegment() { //이미 다른게 돌고 있으면 Skip if( m_SegmentThreadList.size() > LOAD_SEGMENT_MAX_SIZE ) return; for ( int nLoadCnt = 0; nLoadCnt < LOAD_SEGMENT_MAX_SIZE; ++nLoadCnt ) { float minDistSq = FLT_MAX; std::vector::iterator itBest; for ( std::vector::iterator it = m_LoadSegmentList.begin(); it != m_LoadSegmentList.end(); ++it ) { KPoint pos = GetSegmentPos( *it ); float x = pos.x - (float)m_nTarX, y = pos.y - (float)m_nTarY; float distSq = x*x + y*y; // 전방의 세그먼트를 빨리 로드하기 위해 거리를 줄인다. if ( m_viewX * x + m_viewY * y > 0 ) distSq /= 4.0f; if ( distSq < minDistSq ) { minDistSq = distSq; itBest = it; } } if ( minDistSq == FLT_MAX ) break; if ( minDistSq > m_maxDistSq ) { m_LoadSegmentList.clear(); break; } AddSegmentThread( *itBest ); m_LoadSegmentList.erase( itBest ); } } void CTerrainMapEngine::AddDrawSegment( int nSegmentIndex ) { for( std::vector::iterator it = m_LoadSegmentList.begin(); it != m_LoadSegmentList.end(); it++ ) if( (*it) == nSegmentIndex ) return; KPoint pos = GetSegmentPos( nSegmentIndex ); float x = pos.x - (float)m_nTarX, y = pos.y - (float)m_nTarY; float distSq = x*x + y*y; if ( distSq < m_maxDistSq ) m_LoadSegmentList.push_back( nSegmentIndex ); } //#define _NO_SEGMENT_THREAD_ void CTerrainMapEngine::AddSegmentThread( int nSegmentIndex ) { for( std::vector< CTerrainSegmentLoadThread* >::iterator it = m_SegmentThreadList.begin(); it != m_SegmentThreadList.end(); it++ ) if( (*it)->GetSegmentIndex() == nSegmentIndex ) return; // _oprint( "MAP THREAD LOADING : %d\n", nSegmentIndex ); CTerrainSegmentLoadThread* pThread = CreatePreparedThread( nSegmentIndex ); // 스레드 처리 객체 생성 및 준비 #ifdef _NO_SEGMENT_THREAD_ pThread->onProcess( 0 ); delete pThread; #else m_SegmentThreadList.push_back( pThread ); m_ThreadManager.Push( pThread ); // 스레드 시작 #endif } bool CTerrainSegmentLoadThread::onProcess( int nThreadNum ) { m_pTerrainInfo = new CTerrainInfoForSegment( m_ptSegmentOrigin, m_SegmentInitStruct, m_nTileCountPerSegment, m_fTileLength, m_PropLoadInfo, m_SpeedGrassLoadInfo ); // TODO : 지역 정보 관련 // if( !m_strLocationFileName.empty() ) // m_pMapEngine->CreateLocationInfoForMap(m_strLocationFileName.c_str(), m_nMapPosX, m_nMapPosY); // m_pLocationInfo = new CTerrainLocationInfoForMap( m_pMapEngine, m_strLocationFileName.c_str(), m_nMapPosX, m_nMapPosY, m_nTileCountPerMap ); // 세그먼트 생성 { KPoint ptSegment = m_pMapEngine->GetSegmentPos( GetSegmentIndex() ); m_pMapEngine->CreateSegment( ptSegment.x, ptSegment.y, GetTerrainInfo(), m_PropLoadInfo, GetSpeedGrassLoadInfo()); } return true; } struct _FnDeleteFinished { bool operator () ( CTerrainSegmentLoadThread* p ) { if ( p->IsFinished() ) { delete p; return true; } else return false; } }; void CTerrainMapEngine::ProcessSegmentThread() { SEGMENTTHREADLIST::iterator it = std::remove_if( m_SegmentThreadList.begin(), m_SegmentThreadList.end(), _FnDeleteFinished() ); m_SegmentThreadList.erase( it, m_SegmentThreadList.end() ); } void CTerrainMapEngine::ClearSegmentThread() { do // 스레드 리스트가 비워질 때까지 반복 { SEGMENTTHREADLIST::iterator it = std::remove_if( m_SegmentThreadList.begin(), m_SegmentThreadList.end(), _FnDeleteFinished() ); m_SegmentThreadList.erase( it, m_SegmentThreadList.end() ); } while( m_SegmentThreadList.size() > 0 ); } void CTerrainMapEngine::ProcessColoringChange( float x, float y ) { if( 0 == m_dwColoringChangingRemainTime ) { const TERRAINMAP_PROPERTIES::COLORING& rColoring = GetSegmentColoring( x, y ); if( rColoring != m_CurrentMapColoring ) { m_PreviousColoring = m_CurrentMapColoring; m_NextColoring = rColoring; // 하늘 타입은 바로 설정한다. m_CurrentMapColoring.dwSkyType = rColoring.dwSkyType; m_dwColoringChangingRemainTime = c_dwLightChangingTime; m_dwPreviousSetColoringTime = m_dwCurrentTime; } } else { DWORD dwElapsedTime = (m_dwCurrentTime - m_dwPreviousSetColoringTime); if( c_dwLightChangePeriod < dwElapsedTime ) { if( m_dwColoringChangingRemainTime > dwElapsedTime ) { m_dwColoringChangingRemainTime -= dwElapsedTime; m_dwPreviousSetColoringTime = m_dwCurrentTime; float fRatio = float(m_dwColoringChangingRemainTime) / float(c_dwLightChangingTime); const K3DLight& rPrevPrimary = m_PreviousColoring.litPrimary; const K3DLight& rNextPrimary = m_NextColoring.litPrimary; const K3DLight& rPrevSecondary = m_PreviousColoring.litSecondary; const K3DLight& rNextSecondary = m_NextColoring.litSecondary; K3DColor colorSky, colorFog; // 하늘 타입을 제외한 값들을 보간한다. ChangeValue( m_CurrentMapColoring.litPrimary.diffuse, rPrevPrimary.diffuse, rNextPrimary.diffuse, fRatio ); ChangeValue( m_CurrentMapColoring.litPrimary.ambient, rPrevPrimary.ambient, rNextPrimary.ambient, fRatio ); ChangeValue( m_CurrentMapColoring.litPrimary.direction, rPrevPrimary.direction, rNextPrimary.direction, fRatio ); ChangeValue( m_CurrentMapColoring.litSecondary.diffuse, rPrevSecondary.diffuse, rNextSecondary.diffuse, fRatio ); ChangeValue( m_CurrentMapColoring.litSecondary.ambient, rPrevSecondary.ambient, rNextSecondary.ambient, fRatio ); ChangeValue( m_CurrentMapColoring.litSecondary.direction, rPrevSecondary.direction, rNextSecondary.direction, fRatio ); ChangeValue( colorSky, K3DColor( m_PreviousColoring.colorSky ), K3DColor( m_NextColoring.colorSky ), fRatio ); ChangeValue( colorFog, K3DColor( m_PreviousColoring.colorFog ), K3DColor( m_NextColoring.colorFog ), fRatio ); m_CurrentMapColoring.colorSky = (KColor)colorSky; m_CurrentMapColoring.colorFog = (KColor)colorFog; m_CurrentMapColoring.fFogNear = ChangeValue( m_PreviousColoring.fFogNear, m_NextColoring.fFogNear, fRatio ); m_CurrentMapColoring.fFogFar = ChangeValue( m_PreviousColoring.fFogFar, m_NextColoring.fFogFar, fRatio ); } else { m_dwColoringChangingRemainTime = 0; m_CurrentMapColoring = m_NextColoring; } SetPrimaryLightToSegments(); } } } #ifdef _EDIT_MAP_FILE_ // 보이는 영역의 세그먼트들을 다시 로딩 m_SegmentMap에 넣는다 void CTerrainMapEngine::ReloadVisibleSegment() { float fx = 0.0f, fy = 0.0f; SGameAvatarEx* pLocal = g_pCurrentGameSystem->GetLocalPlayer(); if( pLocal ) { fx = pLocal->GetTransform()->_41; fy = pLocal->GetTransform()->_42; } float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(fx / fMapLen); int nMapY = (int)(fy / fMapLen); // 맵파일 std::string strFileName = m_pSeamlessWorldInfo->GetMapFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 미니맵 파일 int nMapX_Index = nMapX / 8; //MAP_PER_TILE_COUNT은 한 맵에 들어가는 미니맵 타일 갯수 int nMapY_Index = nMapY / 8; //MAP_PER_TILE_COUNT은 한 맵에 들어가는 미니맵 타일 갯수 std::string strMapName, strResult; strMapName = m_pSeamlessWorldInfo->GetMiniMapName( nMapX_Index, nMapY_Index ); int nTileX_Index = nMapY % 8; int nTileY_Index = nMapX % 8; strResult = CStringUtil::StringFormat( "V256_%s_%d_%d.jpg", strMapName.c_str(), nTileX_Index, nTileY_Index ); if( !KFileManager::Instance().IsValidResource( strResult.c_str() ) ) strResult = "V256_M000_000_0_0.jpg"; //화일이 없는 경우 if( strResult.size() ) DeleteFileForReload( &strResult ); // 위치정보 파일 strFileName = m_pSeamlessWorldInfo->GetLocationFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 속성 파일 strFileName = m_pSeamlessWorldInfo->GetAttributePolygonFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 풀정보 파일 strFileName = m_pSeamlessWorldInfo->GetGrassColonyInfoFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 스크립트 파일 strFileName = m_pSeamlessWorldInfo->GetScriptFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 미니맵 이미지 파일 strFileName = m_pSeamlessWorldInfo->GetMinimapImageFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 이벤트 영역 파일 strFileName = m_pSeamlessWorldInfo->GetEventAreaFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); // 물정보 파일 strFileName = m_pSeamlessWorldInfo->GetLQWaterFileName( nMapX, nMapY ); if( strFileName.size() ) DeleteFileForReload( &strFileName ); std::vector< CTerrainLowQualityWaterInfo * >::iterator itLQWater = m_LQWaterInfo.begin(); for( ; itLQWater != m_LQWaterInfo.end(); itLQWater++ ) delete (*itLQWater); m_LQWaterInfo.clear(); } void CTerrainMapEngine::DeleteFileForReload(std::string* pFileName) { if( pFileName == NULL ) return ; KStream* pMapFileStream = KFileManager::Instance().CreateStreamFromResource( pFileName->c_str(), true ); if( pMapFileStream ) { delete pMapFileStream; KFileManager::Instance().EraseStream( pFileName ); } } #endif void CTerrainMapEngine::CreateLocationInfoForMap( const char* szLocationFileName, int nMapX, int nMapY ) { if( m_pCurLocationInfo == NULL ) { m_pCurLocationInfo = new CTerrainLocationInfoForMap( this, szLocationFileName, nMapX, nMapY ); } else { if( m_pCurLocationInfo->CheckMap(nMapX, nMapY)) { SAFE_DELETE(m_pCurLocationInfo); m_pCurLocationInfo = new CTerrainLocationInfoForMap( this, szLocationFileName, nMapX, nMapY ); } } } void CTerrainMapEngine::CreateEventAreaInfoForMap( const char* szEventAreaFileName, int nMapX, int nMapY ) { if( m_pCurEventAreaInfo == NULL ) { m_pCurEventAreaInfo = new CTerrainEventAreaInfoForMap( this, szEventAreaFileName, nMapX, nMapY ); } else { if( m_pCurEventAreaInfo->CheckMap(nMapX, nMapY)) { SAFE_DELETE(m_pCurEventAreaInfo); m_pCurEventAreaInfo = new CTerrainEventAreaInfoForMap( this, szEventAreaFileName, nMapX, nMapY ); } } } //지역 정보 관련 void CTerrainMapEngine::SetEventPolygon( EventPolygon* EvPolygon ) { m_EventPolygonScanner.Add( *EvPolygon ); } void CTerrainMapEngine::SetEventAreaPolygon( EventAreaPolygon* EvPolygon ) { m_EventAreaPolygonScanner.Add( *EvPolygon ); } void CTerrainMapEngine::AddAttributePolygon( X2D::Polygon* pPolygon ) { for( int index = 0; index < pPolygon->Size(); ++index ) { X2D::IndexedPoint< int >* IndexedPoint = new X2D::IndexedPoint< int >( /*pPolygon->GetPoint( index ).x, pPolygon->GetPoint( index ).y, */pPolygon, index ); m_AttrPointQuadTree.DuplicateAdd( IndexedPoint, X2D::IndexedPoint< int >::InputQuadTree() ); m_AttrIndexedPoints.push_back( IndexedPoint ); } //#ifdef _DEV // X2D::Polygon* temp = new X2D::Polygon( *pPolygon ); // m_AttrPolygonsQuadTree.Add( temp, polygon_allocator(), polygon_deleter() ); //#else m_AttrPolygonsQuadTree.Add( pPolygon ); //#endif m_vecAttrPolygons.push_back( pPolygon ); } void CTerrainMapEngine::AddWireAttributePolygonLine(const K3DVertex& v1, const K3DVertex& v2) { if( m_bDrawAttributeWire ) m_prWireAttributePolygon->AddLine(v1, v2, KColor(0xFFFF0000)); } //void CTerrainMapEngine::AddGrassContainer( CGrassColonyInfoContainer* pContainer ) //{ // m_vGrassContainer.push_back( pContainer); //} void CTerrainMapEngine::SetDrawAttributePolygon(bool bDraw) { m_bDrawAttributeWire = bDraw; m_bCreateAttributeWire = bDraw; } /////////////////////////////////////////////////////////////////////////////////// void CTerrainMapEngine::GetPlayerPositionLocalID( K3DVector& vPosition, std::string & strScript ) { float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(vPosition.x / fMapLen); int nMapY = (int)(vPosition.y / fMapLen); std::string str = m_pSeamlessWorldInfo->GetLocationFileName( nMapX, nMapY ); CreateLocationInfoForMap(str.c_str(), nMapX, nMapY) ; X2D::Point< int > pt( (int)vPosition.x, (int)vPosition.y ); EventLocationExplorer result; m_EventPolygonScanner.Enum( pt, result ); if( result.strScript.empty() ) strScript = ""; else strScript = result.strScript; } CTerrainMapEngine::PLAYER_LOCAL_POSITION CTerrainMapEngine::FindLocationInfoForMap( K3DVector* vPosition ) { if( m_nLocalState == _IN_ ) m_nLocalState = _LOCAL_; else if(m_nLocalState == _OUT_) m_nLocalState = _WORLD_; int nWorldID = 0; //플레이어의 위치가 바뀔때만 검사 if( m_vOldTargetPosition.x != vPosition->x || m_vOldTargetPosition.y != vPosition->y ) { m_vOldTargetPosition.x = vPosition->x; m_vOldTargetPosition.y = vPosition->y; float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(vPosition->x / fMapLen); int nMapY = (int)(vPosition->y / fMapLen); nWorldID = m_pSeamlessWorldInfo->GetWorldID( nMapX, nMapY ); std::string str = m_pSeamlessWorldInfo->GetLocationFileName( nMapX, nMapY ); CreateLocationInfoForMap(str.c_str(), nMapX, nMapY) ; } else return m_nLocalState; X2D::Point< int > pt( (int)vPosition->x, (int)vPosition->y ); EventLocationExplorer result; m_EventPolygonScanner.Enum( pt, result ); if(!result.strScript.empty()) { //지역에 들어왔다면 if(m_nLocalState != _LOCAL_ || m_nLocalPriority != result.nPriority ) { m_nLocalPriority = result.nPriority; m_nLocalState = _IN_; m_strLocalScript = result.strScript; } } else { //지역에서 나갔다면 if( m_nLocalState == _LOCAL_ ) { m_nLocalState = _OUT_; m_nLocalPriority = -1; } else { //새로운 월드다~~~ if( m_nWorldID != nWorldID ) { m_nWorldID = nWorldID; m_nLocalState = _OUT_; m_nLocalPriority = -1; } } } return m_nLocalState; } void CTerrainMapEngine::ResetLocationInfo() { m_vOldTargetPosition = K3DVector(0.0f, 0.0f, 0.0f); m_nLocalState = _WORLD_; m_nWorldID = 0; m_nLocalPriority = -1; } /////////////////////////////////////////////////////////////////////////////////// void CTerrainMapEngine::GetPlayerPositionEventAreaID( K3DVector& vPosition, std::vector& vec, std::vector& polyNum ) { float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(vPosition.x / fMapLen); int nMapY = (int)(vPosition.y / fMapLen); std::string str = m_pSeamlessWorldInfo->GetEventAreaFileName( nMapX, nMapY ); CreateEventAreaInfoForMap(str.c_str(), nMapX, nMapY) ; X2D::Point< int > pt( (int)vPosition.x, (int)vPosition.y ); EventAreaExplorer result; m_EventAreaPolygonScanner.Enum( pt, result ); vec = result.vecID; polyNum = result.vecPolyNum; } /////////////////////////////////////////////////////////////////////////////////// const CTerrainMapEngine::PATH& CTerrainMapEngine::GetDetour() { return m_vDetourPath; } // TODO : 저사양 물 관련 CTerrainLowQualityWaterInfo* CTerrainMapEngine::FindLowQualityWaterInfo(int nMapX, int nMapY) { //최대 추가될수 있는 정보가 4개 뿐일듯 std::vector< CTerrainLowQualityWaterInfo * >::iterator iter = m_LQWaterInfo.begin(); for( ; iter != m_LQWaterInfo.end(); iter++ ) { if((*iter)->CheckMap(nMapX, nMapY)) //같다면 return (*iter); } //같은게 없다면 return NULL; } void CTerrainMapEngine::DeleteWaterSegmentCount(int nMapX, int nMapY ) { CTerrainLowQualityWaterInfo* pLQWater = FindLowQualityWaterInfo( nMapX, nMapY ); if(pLQWater) { pLQWater->DeleteSegMent(); //이맵에 대한 세그먼트가 하나 삭제됐다 /* if( pLQWater->GetState() == CTerrainLowQualityWaterInfo::INFO_STATE::_DELETE_INFO_ ) { int x = 0; x += 10; }*/ } } int CTerrainMapEngine::GetDefaultWorldID( K3DVector* vPosition ) { if( !m_pSeamlessWorldInfo ) return 0; float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); int nMapX = (int)(vPosition->x / fMapLen); int nMapY = (int)(vPosition->y / fMapLen); return m_pSeamlessWorldInfo->GetWorldID( nMapX, nMapY ); } void CTerrainMapEngine::GetMapXY( const K3DVector& vPosition, int& nMapX, int& nMapY ) { if( !m_pSeamlessWorldInfo ) return; float fMapLen = float( m_pSeamlessWorldInfo->GetTileCountPerSegment() ) * float(m_pSeamlessWorldInfo->GetSegmentCountPerMap() ) * m_pSeamlessWorldInfo->GetTileLength(); nMapX = (int)(vPosition.x / fMapLen); nMapY = (int)(vPosition.y / fMapLen); } void CTerrainMapEngine::CreateGrassColonyInfoForMap( int nMapX, int nMapY ) { /* int nSegmentCountPerMap = m_pSeamlessWorldInfo->GetSegmentCountPerMap(); int nMapX = ((int)fX / m_fSegmentLength) / nSegmentCountPerMap; int nMapY = ((int)fY / m_fSegmentLength) / nSegmentCountPerMap;*/ std::string strMapFileName = m_pSeamlessWorldInfo->GetMapFileName( nMapX, nMapY ); KStream* pMapFileStream = KFileManager::Instance().CreateStreamFromResource( strMapFileName.c_str(), true ); if( NULL != pMapFileStream ) { NFM_HEADER_V22 header; pMapFileStream->Read( &header, sizeof(header) ); //튕김 방지용 if( header.dwVersion != c_dwNFMCurrentVer ) { SAFE_DELETE( pMapFileStream ); return; } // ColonyInfo 파일 위치로 이동 pMapFileStream->Seek( long(header.dwGrassColonyOffset), KStream::seekSet ); int nGrassColonyInfoCnt = 0; pMapFileStream->Read( &nGrassColonyInfoCnt, sizeof(nGrassColonyInfoCnt) ); if( nGrassColonyInfoCnt > 0 ) { CTerrainGrassColonyInfoForMap* pTerrainGrassColonyInfoForMap = new CTerrainGrassColonyInfoForMap( nMapX, nMapY ); AddTerrainGrassColonyInfoForMap( pTerrainGrassColonyInfoForMap ); for( int nColonyInfo = 0; nColonyInfo < nGrassColonyInfoCnt; ++nColonyInfo ) { NFM_SPEED_GRASS_COLONY_INFO* pGrassColonyInfoSaveBuffer = new NFM_SPEED_GRASS_COLONY_INFO; pMapFileStream->Read( pGrassColonyInfoSaveBuffer, sizeof(NFM_SPEED_GRASS_COLONY_INFO) ); if( !pTerrainGrassColonyInfoForMap->GetGrassColonyInfo( pGrassColonyInfoSaveBuffer->nPolygonID ) ) { GrassColonyInfoMap* pGrassColonyInfoMap = new GrassColonyInfoMap; pGrassColonyInfoMap->SetGrassColonyInfoSaveBuffer( pMapFileStream, pGrassColonyInfoSaveBuffer ); pTerrainGrassColonyInfoForMap->AddGrassColonyInfo( pGrassColonyInfoMap ); } else { int nTexFileNameLength = 0; pMapFileStream->Read( &nTexFileNameLength, sizeof( nTexFileNameLength ) ); if( nTexFileNameLength > 0 ) { char* pTexFileName = new char[nTexFileNameLength]; pMapFileStream->Read( pTexFileName, sizeof( char ) * nTexFileNameLength ); SAFE_DELETE_ARRAY( pTexFileName ); } int nPolygonCount = 0; pMapFileStream->Read( &nPolygonCount, sizeof(nPolygonCount) ); for( int nPolygon = 0; nPolygon < nPolygonCount; nPolygon++ ) { int nPointCount; pMapFileStream->Read( &nPointCount, sizeof(nPointCount) ); POINT* pPoints = new POINT[ nPointCount ]; pMapFileStream->Read( pPoints, sizeof(POINT) * nPointCount ); delete[] pPoints; } } SAFE_DELETE_ARRAY( pGrassColonyInfoSaveBuffer ); } } SAFE_DELETE( pMapFileStream ); } } CTerrainGrassColonyInfoForMap* CTerrainMapEngine::GetTerrainGrassColonyInfoForMap( int nMapX, int nMapY ) { GRASS_COLONY_INFO_FOR_MAP::iterator iter = m_GrassColonyInfoForMap.begin(); for( ; iter != m_GrassColonyInfoForMap.end(); ++iter ) { if( (*iter)->CheckMap( nMapX, nMapY ) ) { return (*iter); } } return NULL; } void CTerrainMapEngine::AddTerrainGrassColonyInfoForMap( CTerrainGrassColonyInfoForMap* pTerrainGrassColonyInfoForMap ) { m_GrassColonyInfoForMap.push_back( pTerrainGrassColonyInfoForMap ); } GrassColonyInfo* CTerrainMapEngine::GetGrassColonyInfoForMap( int nMapX, int nMapY, int nPolygonID ) { CTerrainGrassColonyInfoForMap* pTerrainGrassColony = GetTerrainGrassColonyInfoForMap( nMapX, nMapY ); if( pTerrainGrassColony ) { return pTerrainGrassColony->GetGrassColonyInfo( nPolygonID ); } return NULL; } void CTerrainMapEngine::DeleteSpeedGrassSegmentCount( CTerrainSegment* pTerrainSegment ) { GRASS_COLONY_INFO_FOR_MAP::iterator iter = m_GrassColonyInfoForMap.begin(); for( ; iter != m_GrassColonyInfoForMap.end(); ) { if( (*iter)->CheckMap( pTerrainSegment->GetMapX(), pTerrainSegment->GetMapY() ) ) { if( (*iter)->DeleteSegMentRef() ) { SAFE_DELETE( (*iter) ); iter = m_GrassColonyInfoForMap.erase( iter ); continue; } } ++iter; } } void CTerrainMapEngine::RefreshLightMapEnable(bool bLightmapEnable) { THREAD_SYNCRONIZE(&g_TerrainCS); if( m_bEnableLightmap != bLightmapEnable ) { m_bEnableLightmap = bLightmapEnable; TERRAINSEGMENTMAP::iterator end = m_SegmentMap.end(); for(TERRAINSEGMENTMAP::iterator it = m_SegmentMap.begin(); it != end; ++it) it->second->RefreshTerrainLightmap(m_bEnableLightmap); } }