Files
Leviathan/Client/Game/engine/TerrainEngine/TerrainMapEngine.cpp
T
2026-06-01 12:46:52 +02:00

2931 lines
91 KiB
C++

#include "StdAfx.h"
#include <geometry/X2DPolygon.h>
//#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 <mmo/ArTime.h>
#include <toolkit/ILock.h>
//#include <geometry/X2DPathFinder.h>
#include "KSeqSpeedGrass.h"
#include <algorithm>
#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<int>* operator()()
{
return new X2D::Polygon< int >();
}
};
struct polygon_deleter
{
void operator()( X2D::Polygon<int>* 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<int> > g_vGreenPath;
std::vector< X2D::Point<int> > g_vLeftPath;
std::vector< X2D::Point<int> > g_vRightPath;
std::vector< X2D::Line<int> > g_vLineList;
std::vector< std::vector< X2D::Point<int> > > g_vPathList;
std::vector< X2D::Point<int> > g_vRedPoint;
std::vector< X2D::Point<int> > g_vBlackPoint;
std::vector< X2D::Point<int> > g_vBluePoint;
std::vector< X2D::Point<int> > g_vGreenPoint;
std::vector< X2D::Point<int> > 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<unsigned int, X2D::Point<int>*>::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<int> 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<int> 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<string> collidedMesh; // 전역변수로 중복체크.. 젠장 -.-
std::vector<DWORD> 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<int>::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<int> vecID;
std::vector<int> 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<int>::iterator itBest;
for ( std::vector<int>::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<int>::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<int>* 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<int>* temp = new X2D::Polygon<int>( *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<int>& vec, std::vector<int>& 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);
}
}