2931 lines
91 KiB
C++
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);
|
|
}
|
|
} |