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

410 lines
15 KiB
C++

#pragma once
#include "stdafx.h"
#include "TerrainInfoForSegment.h"
//#include "SDebug_Util.h"
// pInitStruct는 9개가 연속해서 있어야 한다.
CTerrainInfoForSegment::CTerrainInfoForSegment( const K3DPoint& rOrigin, const INITSTRUCT* pInitStruct, int nTileCount, float fTileLength, PROPLOAD_INFO& rPropLoadInfo, SPEEDGRASSLOAD_INFO& rSpeedGrassLoadInfo )
: m_ptOrigin( rOrigin )
, m_nTileCount( nTileCount )
, m_nVertexCount( nTileCount + 1 )
, m_fTileLength( fTileLength )
{
m_DefaultData.fHeight = 0.f;
m_DefaultData.wAttribute = 0xFFFF;
m_DefaultData.Color = KTripleColor( 0, 0, 0 );
// 확장된 크기로 버퍼를 잡는다.
m_nExtendedTileCount = (c_nTileExtendUpper + m_nTileCount + c_nTileExtendLower);
m_pExtendedVertexStruct = new NFM_VERTEXSTRUCT_V11[ m_nExtendedTileCount * m_nExtendedTileCount ];
// 로드되지 않는 영역 밖의 세그먼트 부분을 위해 0으로 초기화 한다.
::ZeroMemory( m_pExtendedVertexStruct, sizeof(NFM_VERTEXSTRUCT_V11) * m_nExtendedTileCount * m_nExtendedTileCount );
// 세그먼트 갯수 만큼 타일 헤더 만들어 준다.
::ZeroMemory( m_SegmentHeader.tile, sizeof(m_SegmentHeader.tile) );
// 맵 파일들 로드
LoadMapFiles( pInitStruct, rPropLoadInfo, rSpeedGrassLoadInfo );
// vertex normal vector 정보 생성
m_pVertexNormal = new K3DVector[ m_nVertexCount * m_nVertexCount ];
CTerrainFaceNormalInfo FaceNormalInfo( KRect( -1, -1, m_nTileCount + 1, m_nTileCount + 1 ), this );
K3DVector* pNormal = m_pVertexNormal;
for( int nY = 0; nY < m_nVertexCount; ++nY )
{
for( int nX = 0; nX < m_nVertexCount; ++nX )
{
Normalize( *pNormal++ =
FaceNormalInfo.GetFaceNormalData( nX - 1, nY - 1 )->vtFaceNormalUpper +
FaceNormalInfo.GetFaceNormalData( nX , nY )->vtFaceNormalLower +
FaceNormalInfo.GetFaceNormalData( nX - 1, nY )->vtFaceNormalLower +
FaceNormalInfo.GetFaceNormalData( nX - 1, nY )->vtFaceNormalUpper +
FaceNormalInfo.GetFaceNormalData( nX , nY - 1 )->vtFaceNormalUpper +
FaceNormalInfo.GetFaceNormalData( nX , nY - 1 )->vtFaceNormalLower );
}
}
}
CTerrainInfoForSegment::~CTerrainInfoForSegment()
{
delete [] m_pVertexNormal;
delete [] m_pExtendedVertexStruct;
}
// 세그먼트에 사용되고 있는 타일들의 종류를 모두 얻는다.
void CTerrainInfoForSegment::GetUsingTileList( std::list<WORD>& rGetList ) const
{
rGetList.push_back(m_SegmentHeader.tile[0]);
rGetList.push_back(m_SegmentHeader.tile[1]);
rGetList.push_back(m_SegmentHeader.tile[2]);
}
WORD CTerrainInfoForSegment::GetTile( int nX, int nY ) const
{
if(GetVertexStruct( nX, nY )->wFillBits[1] != 0)
return m_SegmentHeader.tile[2];
else if(GetVertexStruct( nX, nY )->wFillBits[0] != 0)
return m_SegmentHeader.tile[1];
else
return m_SegmentHeader.tile[0];
}
WORD CTerrainInfoForSegment::GetFillBit( int nStage, int nX, int nY ) const
{
int bit_pos = (((nX+64)%4) + ((nY+64)%4)*4)*2;
DWORD bits = GetVertexStruct( (nX+64)/4-16, (nY+64)/4-16 )->wFillBits[nStage];
return (WORD)((bits>>bit_pos) & 3);
}
float CTerrainInfoForSegment::GetHeight( int nX, int nY ) const
{
return GetVertexStruct( nX, nY )->fHeight;
}
unsigned __int64 CTerrainInfoForSegment::GetAttrData( int nX, int nY ) const
{
return GetVertexStruct( nX, nY )->wAttribute;
}
KTripleColor CTerrainInfoForSegment::GetColor( int nX, int nY ) const
{
const KTripleColor& rColor = GetVertexStruct( nX, nY )->Color;
K3DColor colorLight( m_litTerrain.ambient + (m_litTerrain.diffuse * max( 0.f, DotProduct(GetVertexNormalVector( nX, nY ), -m_litTerrain.direction) )) );
if( colorLight.r > 1.f ) colorLight.r = 1.f;
if( colorLight.g > 1.f ) colorLight.g = 1.f;
if( colorLight.b > 1.f ) colorLight.b = 1.f;
return KTripleColor
( (unsigned char)( colorLight.r * float(rColor.r) )
, (unsigned char)( colorLight.g * float(rColor.g) )
, (unsigned char)( colorLight.b * float(rColor.b) ) );
}
K3DVertex CTerrainInfoForSegment::GetVertex( int nX, int nY ) const
{
return K3DVertex(
m_ptOrigin.x + ( float( nX ) * m_fTileLength ),
m_ptOrigin.y + ( float( nY ) * m_fTileLength ),
GetHeight( nX, nY ) );
}
K3DVector CTerrainInfoForSegment::GetVertexNormalVector( int nX, int nY ) const
{
return ( 0 <= nX && nX < m_nVertexCount && 0 <= nY && nY < m_nVertexCount ) ? m_pVertexNormal[ (nY * m_nVertexCount) + nX ] : c_DefaultNormal;
}
BYTE CTerrainInfoForSegment::GetVertexCoverageFactor( int nStage, int nX, int nY ) const
{
// 당근 Cov..Factor는 1stage나 2stage만 물어보아야한다.
return (BYTE)(GetFillBit( nStage - 1, nX, nY )*255/3);
}
bool CTerrainInfoForSegment::GetAttribute( int nAttrX, int nAttrY ) const
{
int nBitFlag_pos = ((nAttrY % c_nAttrCountPerTile) * c_nAttrCountPerTile) + (nAttrX % c_nAttrCountPerTile);
return (0 != ((GetAttrData( nAttrX / c_nAttrCountPerTile, nAttrY / c_nAttrCountPerTile ) >> nBitFlag_pos) & 1));
}
WORD CTerrainInfoForSegment::GetGameTile( float x, float y ) const
{
int nX = (int)( (x - m_ptOrigin.x) / m_fTileLength );
int nY = (int)( (y - m_ptOrigin.y) / m_fTileLength );
return GetTile( nX, nY );
}
float CTerrainInfoForSegment::GetZ( float x, float y ) const // 세그먼트 상대 좌표가 아닌 월드 좌표.
{
int nX = (int)( (x - m_ptOrigin.x) / m_fTileLength );
int nY = (int)( (y - m_ptOrigin.y) / m_fTileLength );
K3DVertex vt1 = GetVertex( nX, nY );
K3DVertex vt2 = GetVertex( nX + 1, nY );
K3DVertex vt3 = GetVertex( nX , nY + 1 );
K3DVertex vt4 = GetVertex( nX + 1, nY + 1 );
const K3DVertex* p1;
const K3DVertex* p2;
const K3DVertex* p3;
if( (x - vt1.x) + (y - vt1.y) < m_fTileLength )
{ p1 = &vt3; p2 = &vt2; p3 = &vt1; }
else
{ p1 = &vt2; p2 = &vt3; p3 = &vt4; }
float fTilt1 = ( y - (p1->y) ) / ( (p2->y) - (p1->y) );
float fTilt2 = ( y - (p1->y) ) / ( (p3->y) - (p1->y) );
float z1 = p1->z + ( p2->z - p1->z ) * fTilt1;
float x1 = p1->x + ( p2->x - p1->x ) * fTilt1;
float z2 = p1->z + ( p3->z - p1->z ) * fTilt2;
float x2 = p1->x + ( p3->x - p1->x ) * fTilt2;
return (z1 + ( z2 - z1 ) * ( x - x1 ) / ( x2 - x1 ));
}
// float CTerrainInfoForSegment::GetZ( float x, float y ) const
KTripleColor CTerrainInfoForSegment::GetColorByWorldCoordinate( float x, float y ) const // 세그먼트 상대 좌표가 아닌 월드 좌표
{
int nX = (int)( (x - m_ptOrigin.x) / m_fTileLength );
int nY = (int)( (y - m_ptOrigin.y) / m_fTileLength );
// Get 4 grid corner colors
const KTripleColor& rColor00 = GetVertexStruct( nX, nY )->Color;
const KTripleColor& rColor01 = GetVertexStruct( nX, nY+1 )->Color;
const KTripleColor& rColor10 = GetVertexStruct( nX+1, nY )->Color;
const KTripleColor& rColor11 = GetVertexStruct( nX+1, nY+1 )->Color;
// Blend color
float dx, dy, dxi, dyi;
dx=(x-(nX*m_fTileLength))/m_fTileLength;
dy=(y-(nY*m_fTileLength))/m_fTileLength;
if (dx<0.) dx=0; else if (dx>1.) dx=1.;
if (dy<0.) dy=0; else if (dy>1.) dy=1.;
dxi=1.f-dx;
dyi=1.f-dy;
K3DVector cl, cr, c;
cl.x=rColor00.r*dy + rColor01.r*dyi;
cl.y=rColor00.g*dy + rColor01.g*dyi;
cl.z=rColor00.b*dy + rColor01.b*dyi;
cr.x=rColor10.r*dy + rColor11.r*dyi;
cr.y=rColor10.g*dy + rColor11.g*dyi;
cr.z=rColor10.b*dy + rColor11.b*dyi;
c.x=cl.x*dx + cr.x*dxi;
c.y=cl.y*dx + cr.y*dxi;
c.z=cl.z*dx + cr.z*dxi;
if (c.x<0.) c.x=0.; else if (c.x>255.) c.x=255.;
if (c.y<0.) c.y=0.; else if (c.y>255.) c.y=255.;
if (c.z<0.) c.z=0.; else if (c.z>255.) c.z=255.;
// Map float to byte
KTripleColor cReturn;
cReturn.r=(unsigned char)c.x;
cReturn.g=(unsigned char)c.y;
cReturn.b=(unsigned char)c.z;
return cReturn;
}
void CTerrainInfoForSegment::GetTriangle( float x, float y, K3DVector& v1, K3DVector& v2, K3DVector& v3 ) const // 세그먼트 상대 좌표가 아닌 월드 좌표.
{
int nX = (int)( (x - m_ptOrigin.x) / m_fTileLength );
int nY = (int)( (y - m_ptOrigin.y) / m_fTileLength );
K3DVertex vt1 = GetVertex( nX, nY );
K3DVertex vt2 = GetVertex( nX + 1, nY );
K3DVertex vt3 = GetVertex( nX , nY + 1 );
K3DVertex vt4 = GetVertex( nX + 1, nY + 1 );
if( (x - vt1.x) + (y - vt1.y) < m_fTileLength )
{ v1 = vt3; v2 = vt2; v3 = vt1; }
else
{ v1 = vt2; v2 = vt3; v3 = vt4; }
}
const NFM_VERTEXSTRUCT_V11* CTerrainInfoForSegment::GetVertexStruct( int nX, int nY ) const
{
nX += c_nTileExtendUpper;
nY += c_nTileExtendUpper; // 여분 만큼 보정
return ( 0 <= nX && nX < m_nExtendedTileCount && 0 <= nY && nY < m_nExtendedTileCount )
? (m_pExtendedVertexStruct + (nY * m_nExtendedTileCount) + nX) : &m_DefaultData;
}
void CTerrainInfoForSegment::LoadMapFiles( const INITSTRUCT* pInitStruct, PROPLOAD_INFO& rPropLoadInfo, SPEEDGRASSLOAD_INFO& rSpeedGrassLoadInfo )
{
// 중앙의 맵 파일에서 속성과 prop 정보 읽기
if( LoadPropertiesAndProps( pInitStruct + 4, rPropLoadInfo, rSpeedGrassLoadInfo ) )
{
// 9개의 세그먼트 각각 읽고 쓰는 위치와 크기 ( 중앙 1개를 제외한 주변의 8개는 일부분만 사용하므로 필요한 만큼만 읽기 위함이다. )
int LoadPos[3], SavePos[3], CopyLength[3];
LoadPos[0] = m_nTileCount - c_nTileExtendUpper;
LoadPos[1] = 0;
LoadPos[2] = 0;
SavePos[0] = 0;
SavePos[1] = c_nTileExtendUpper;
SavePos[2] = c_nTileExtendUpper + m_nTileCount;
CopyLength[0] = c_nTileExtendUpper;
CopyLength[1] = m_nTileCount;
CopyLength[2] = c_nTileExtendLower;
int nTotalTileCountPerSegment = (m_nTileCount * m_nTileCount);
std::string strCurrentMapFileName;
KStream* pMapFileStream = NULL;
NFM_HEADER_V22 header;
for( int nY( 0 ); nY < 3; ++nY )
{
for( int nX( 0 ); nX < 3; ++nX )
{
// 이미 해당 파일의 스트림이 열려있다면 그대로 사용하고, 파일명이 다르다면 다시 연다.
if( strCurrentMapFileName != pInitStruct->strMapFileName || NULL == pMapFileStream )
{
strCurrentMapFileName = pInitStruct->strMapFileName;
SAFE_DELETE( pMapFileStream );
// 파일명이 비어있다면 ( 영역 밖의 세그먼트를 의미 ) 맵 파일을 열지 않고 NULL인 채로 남겨둔다.
// ( 또한 스트림 얻기가 실패해도 NULL이 들어갈 것이다. )
if( !strCurrentMapFileName.empty() )
pMapFileStream = KFileManager::Instance().CreateStreamFromResource( strCurrentMapFileName.c_str(), true );
// 헤더 읽기 ( 파일 체크는 CTerrainSeamlessWorldInfo::Initialize()에서 이루어졌으므로 다시 체크 안함. )
if( NULL != pMapFileStream ) pMapFileStream->Read( &header, sizeof(header) );
}
if( NULL != pMapFileStream )
{
// 지형 세그먼트 데이터가 시작하는 파일 위치로 이동
pMapFileStream->Seek( (long)header.dwTerrainSegmentOffset, KStream::seekSet );
// 읽고자 하는 세그먼트 인덱스만큼 다시 뒤로 이동
pMapFileStream->Seek( pInitStruct->nSegmentIndexInMap * ((sizeof(NFM_VERTEXSTRUCT_V11) * nTotalTileCountPerSegment) + sizeof(NFM_SEGMENTHEADER_V11)), KStream::seekCur );
// 버텍스 정보를 읽는다.
bool bHeaderLoad = false;
if( nX == 1 && nY == 1 ) bHeaderLoad = true;
LoadSegment( pMapFileStream, LoadPos[nX], LoadPos[nY], SavePos[nX], SavePos[nY], CopyLength[nX], CopyLength[nY], bHeaderLoad );
}
pInitStruct++;
}
}
SAFE_DELETE( pMapFileStream );
}
}
bool CTerrainInfoForSegment::LoadPropertiesAndProps( const INITSTRUCT* pInitStruct, PROPLOAD_INFO& rPropLoadInfo, SPEEDGRASSLOAD_INFO& rSpeedGrassLoadInfo )
{
if( !pInitStruct->strMapFileName.empty() ) // 중앙 세그먼트가 맵이 없는 부분이라면 읽을 필요가 없다.
{
// 맵 파일 열기
KStream* pMapFileStream = KFileManager::Instance().CreateStreamFromResource( pInitStruct->strMapFileName.c_str(), true );
if( NULL != pMapFileStream )
{
// 헤더 읽기 ( 파일 체크는 CTerrainSeamlessWorldInfo::Initialize()에서 이루어졌으므로 다시 체크 안함. )
NFM_HEADER_V22 header;
pMapFileStream->Read( &header, sizeof(header) );
// 맵 속성 정보가 시작하는 파일 위치로 이동
pMapFileStream->Seek( long(header.dwMapPropertiesOffset), KStream::seekSet );
// 맵 속성 정보를 읽는다.
m_MapProperties.LoadProperties( pMapFileStream );
// 세그먼트들의 Prop 인덱스 정보가 시작하는 파일 위치로 이동
pMapFileStream->Seek( long(header.dwPropOffset), KStream::seekSet );
pMapFileStream->Seek( pInitStruct->nSegmentIndexInMap * sizeof(DWORD), KStream::seekCur );
// 해당 세그먼트의 Prop 인덱스 정보가 시작하는 오프셋을 읽고 이동시킨다.
DWORD dwPropIndexOffset;
pMapFileStream->Read( &dwPropIndexOffset, sizeof(dwPropIndexOffset) );
pMapFileStream->Seek( dwPropIndexOffset, KStream::seekSet );
pMapFileStream->Read( &(rPropLoadInfo.nPropLoadCount), sizeof(rPropLoadInfo.nPropLoadCount) );
if( 0 < rPropLoadInfo.nPropLoadCount )
{
rPropLoadInfo.pPropLoadStructs = new NFM_PROPSTRUCT_V15[ rPropLoadInfo.nPropLoadCount ];
pMapFileStream->Read( rPropLoadInfo.pPropLoadStructs, sizeof(NFM_PROPSTRUCT_V15) * rPropLoadInfo.nPropLoadCount );
}
//튕김 방지용
if( header.dwVersion == c_dwNFMCurrentVer )
{
pMapFileStream->Read( &rSpeedGrassLoadInfo.nSpeedGrassGroupCount, sizeof( rSpeedGrassLoadInfo.nSpeedGrassGroupCount ) );
if( rSpeedGrassLoadInfo.nSpeedGrassGroupCount > 0 )
{
rSpeedGrassLoadInfo.pSpeedGrassGroup = new SPEEDGRASSLOAD_INFO::SpeedGrassGroup[rSpeedGrassLoadInfo.nSpeedGrassGroupCount];
for( int nSpeedGrassCnt = 0; nSpeedGrassCnt < rSpeedGrassLoadInfo.nSpeedGrassGroupCount; ++nSpeedGrassCnt )
{
SPEEDGRASSLOAD_INFO::SpeedGrassGroup* pSpeedGrassGroup = &rSpeedGrassLoadInfo.pSpeedGrassGroup[nSpeedGrassCnt];
pMapFileStream->Read( &pSpeedGrassGroup->nSpeedGrassID, sizeof( pSpeedGrassGroup->nSpeedGrassID ) );
pMapFileStream->Read( &pSpeedGrassGroup->nSpeedGrassLoadCount, sizeof( pSpeedGrassGroup->nSpeedGrassLoadCount ) );
if( pSpeedGrassGroup->nSpeedGrassLoadCount > 0 )
{
pSpeedGrassGroup->pSpeedGrassLoadStructs = new NFM_SPEED_GRASS_V1[pSpeedGrassGroup->nSpeedGrassLoadCount];
pMapFileStream->Read( pSpeedGrassGroup->pSpeedGrassLoadStructs, sizeof( NFM_SPEED_GRASS_V1 ) * pSpeedGrassGroup->nSpeedGrassLoadCount );
}
}
}
}
delete pMapFileStream;
return true;
}
}
m_MapProperties.bShowTerrainInGame = false;
return false;
}
void CTerrainInfoForSegment::LoadSegment( KStream* pStream, int nLoadPosX, int nLoadPosY, int nSavePosX, int nSavePosY, int nWidth, int nHeight, bool bHeaderLoad )
{
NFM_VERTEXSTRUCT_V11* pDes = m_pExtendedVertexStruct + (nSavePosY * m_nExtendedTileCount) + nSavePosX;
// 헤더 읽어오기
if( bHeaderLoad )
{
NFM_SEGMENTHEADER_V11* pBuf = new NFM_SEGMENTHEADER_V11;
pStream->Read( pBuf, sizeof(NFM_SEGMENTHEADER_V11) );
::memcpy( m_SegmentHeader.tile, pBuf->tile, sizeof(m_SegmentHeader.tile) );
/// 2012.01.12 왜 하는 것인가? - prodongi
/*
std::list<WORD> rGetList;
for( int i = 0; i < 3; i++ )
rGetList.push_back( m_SegmentHeader.tile[i] );
// 리스트의 중복된 번호를 제거한다.
rGetList.sort();
rGetList.unique();
*/
delete pBuf;
}
else
pStream->Seek( sizeof(NFM_SEGMENTHEADER_V11), KStream::seekCur );
pStream->Seek( sizeof(NFM_VERTEXSTRUCT_V11) * ((nLoadPosY * m_nTileCount) + nLoadPosX), KStream::seekCur );
// 버텍스 데이터 읽어온다.
while( nHeight > 0 )
{
pStream->Read( pDes, sizeof(NFM_VERTEXSTRUCT_V11) * nWidth );
pStream->Seek( sizeof(NFM_VERTEXSTRUCT_V11) * (m_nTileCount - nWidth), KStream::seekCur );
pDes += m_nExtendedTileCount;
--nHeight;
}
}