#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& 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 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; } }