DragonNest/Common/EternityEngineSrv/EtTerrainArea.cpp
2024-12-19 09:48:26 +08:00

368 lines
9 KiB
C++

#include "StdAfx.h"
#include "EtConvexVolume.h"
#include "EtCollisionFunc.h"
#include "EtLoader.h"
#include "EtTerrainArea.h"
using namespace EternityEngine;
DECL_MULTISMART_PTR_STATIC( CEtTerrainArea, MAX_SESSION_COUNT, 20 )
CEtTerrainArea::CEtTerrainArea( CMultiRoom *pRoom )
: CMultiSmartPtrBase< CEtTerrainArea, MAX_SESSION_COUNT >(pRoom)
{
m_BoundingBox.Reset();
m_nBlockCountX = 0;
m_nBlockCountY = 0;
m_nBlockSizeX = 0;
m_nBlockSizeY = 0;
}
CEtTerrainArea::~CEtTerrainArea(void)
{
Clear();
}
void CEtTerrainArea::Clear()
{
SAFE_DELETE_PVEC( m_vecTerrain );
}
void CEtTerrainArea::CalcSelectCount( int nBlockIndex, int &nStart, int &nEnd )
{
if( nBlockIndex == -1 )
{
nStart = 0;
nEnd = ( int )m_vecTerrain.size();
}
else
{
nStart = nBlockIndex;
nEnd = nStart + 1;
}
}
void CEtTerrainArea::Initialize( STerrainInfo *pInfo )
{
CEtTerrain *pTerrain;
int i, j;
Clear();
m_TerrainInfo = *pInfo;
m_nBlockCountX = pInfo->nSizeX / DEFAULT_TERRAIN_SIZE;
if( pInfo->nSizeX % DEFAULT_TERRAIN_SIZE )
{
m_nBlockCountX++;
}
m_nBlockSizeX = DEFAULT_TERRAIN_SIZE;
m_nBlockCountY = pInfo->nSizeY / DEFAULT_TERRAIN_SIZE;
if( pInfo->nSizeY % DEFAULT_TERRAIN_SIZE )
{
m_nBlockCountY++;
}
m_nBlockSizeY = DEFAULT_TERRAIN_SIZE;
STerrainInfo TerrainInfo;
memcpy( &TerrainInfo, pInfo, sizeof( STerrainInfo ) );
TerrainInfo.nSizeX = m_nBlockSizeX;
TerrainInfo.nSizeY = m_nBlockSizeY;
for( i = 0; i < m_nBlockCountY; i++ )
{
for( j = 0; j < m_nBlockCountX; j++ )
{
TerrainInfo.pHeight = pInfo->pHeight + ( i * m_nBlockSizeY ) * ( pInfo->nSizeX + 1 ) + j * m_nBlockSizeX;
TerrainInfo.pLayerDensity = pInfo->pLayerDensity + ( i * m_nBlockSizeY ) * ( pInfo->nSizeX + 1 ) + j * m_nBlockSizeX;
TerrainInfo.TerrainOffset = pInfo->TerrainOffset
+ EtVector3( pInfo->fTileSize * j * m_nBlockSizeX, 0.0f, pInfo->fTileSize * i * m_nBlockSizeY );
pTerrain = new CEtTerrain();
pTerrain->SetTerrainInfo( &TerrainInfo );
pTerrain->SetBlockOffset( j * m_nBlockSizeX, i * m_nBlockSizeY );
pTerrain->SetStride( pInfo->nSizeX + 1, pInfo->nSizeY + 1 );
m_vecTerrain.push_back( pTerrain );
}
}
float fWorldSizeX, fWorldSizeY, fWorldSizeZ;
fWorldSizeX = m_TerrainInfo.nSizeX * m_TerrainInfo.fTileSize;
fWorldSizeY = m_TerrainInfo.nSizeY * m_TerrainInfo.fTileSize;
fWorldSizeZ = pInfo->fHeightMultiply * 65535.f;
CEtObject::SetWorldSize( GetRoom(), EtVector3( fWorldSizeX * 0.5f, 0.0f, fWorldSizeY * 0.5f ), max( max( fWorldSizeX, fWorldSizeY ), fWorldSizeZ ) );
CalcBoundingBox();
}
void CEtTerrainArea::InitializeBlock( int nBlockIndex )
{
int i, nStart, nEnd;
CalcSelectCount( nBlockIndex, nStart, nEnd );
for( i = nStart; i < nEnd; i++ )
{
m_vecTerrain[ i ]->Initialize();
}
CalcBoundingBox();
}
void CEtTerrainArea::ChangeBlockType( TerrainType Type, int nBlockIndex )
{
}
TerrainType CEtTerrainArea::GetBlockType( int nBlockIndex )
{
return TT_NORMAL;
}
void CEtTerrainArea::GetExtent( EtVector3 &Origin, EtVector3 &Extent )
{
Origin = ( m_BoundingBox.Max + m_BoundingBox.Min ) / 2;
Extent = ( m_BoundingBox.Max - m_BoundingBox.Min ) / 2;
}
void CEtTerrainArea::CalcBoundingBox()
{
int i;
SAABox *pBoundingBox;
for( i = 0; i < ( int )m_vecTerrain.size(); i++ )
{
pBoundingBox = m_vecTerrain[ i ]->GetBoundingBox();
if( m_BoundingBox.Min.y > pBoundingBox->Min.y )
{
m_BoundingBox.Min.y = pBoundingBox->Min.y;
}
if( m_BoundingBox.Max.y < pBoundingBox->Max.y )
{
m_BoundingBox.Max.y = pBoundingBox->Max.y;
}
}
m_BoundingBox.Max.x = m_TerrainInfo.TerrainOffset.x + m_TerrainInfo.nSizeX * m_TerrainInfo.fTileSize;
m_BoundingBox.Max.z = m_TerrainInfo.TerrainOffset.z + m_TerrainInfo.nSizeY * m_TerrainInfo.fTileSize;
m_BoundingBox.Min.x = m_TerrainInfo.TerrainOffset.x;
m_BoundingBox.Min.z = m_TerrainInfo.TerrainOffset.z;
}
bool CEtTerrainArea::Pick( EtVector3 &Origin, EtVector3 &Direction, EtVector3 &PickPos )
{
int i;
float fMinDist, fFindDist, fDistToBox;
EtVector3 FindPos, ModifyOrigin;
fMinDist = FLT_MAX;
for( i = 0; i < ( int )m_vecTerrain.size(); i++ )
{
SAABox *pBoundingBox;
pBoundingBox = m_vecTerrain[ i ]->GetBoundingBox();
if( !TestLineToBox( Origin, Direction, *pBoundingBox, fDistToBox ) )
{
continue;
}
if( fDistToBox == 0.0f )
{
ModifyOrigin = Origin;
}
else
{
ModifyOrigin = Origin + Direction * fDistToBox;
}
fFindDist = m_vecTerrain[ i ]->Pick( ModifyOrigin, Direction, FindPos ) + fDistToBox;
if(fFindDist < fMinDist )
{
fMinDist = fFindDist;
PickPos = FindPos;
}
}
if( fMinDist != FLT_MAX )
{
return true;
}
return false;
}
bool CEtTerrainArea::IsInside( float fX, float fZ )
{
if( fX < m_TerrainInfo.TerrainOffset.x )
{
return false;
}
if( fX > m_TerrainInfo.TerrainOffset.x + m_TerrainInfo.nSizeX * m_TerrainInfo.fTileSize )
{
return false;
}
if( fZ < m_TerrainInfo.TerrainOffset.z )
{
return false;
}
if( fZ > m_TerrainInfo.TerrainOffset.z + m_TerrainInfo.nSizeY * m_TerrainInfo.fTileSize )
{
return false;
}
return true;
}
void CEtTerrainArea::CalcCellPosition( float fX, float fZ, int &nCellX, int &nCellZ, float *pWeight )
{
float fModX, fModZ;
fX -= m_TerrainInfo.TerrainOffset.x;
fZ -= m_TerrainInfo.TerrainOffset.z;
if( fX < 0.0f )
{
fX = 0.f;
}
else if( fX >= m_TerrainInfo.nSizeX * m_TerrainInfo.fTileSize )
{
fX = m_TerrainInfo.nSizeX * m_TerrainInfo.fTileSize - 0.1f;
}
if( fZ < 0.f )
{
fZ = 0.f;
}
else if( fZ >= m_TerrainInfo.nSizeY * m_TerrainInfo.fTileSize )
{
fZ = m_TerrainInfo.nSizeY * m_TerrainInfo.fTileSize - 0.1f;
}
nCellX = ( int )( fX / m_TerrainInfo.fTileSize );
nCellZ = ( int )( fZ / m_TerrainInfo.fTileSize );
fModX = fX / m_TerrainInfo.fTileSize - nCellX;
fModZ = fZ / m_TerrainInfo.fTileSize - nCellZ;
if( fModX > fModZ )
{
pWeight[ 0 ] = 1.0f - fModX;
pWeight[ 1 ] = fModX - fModZ;
pWeight[ 2 ] = 0.0f;
pWeight[ 3 ] = fModZ;
}
else if( fModX < fModZ )
{
pWeight[ 0 ] = 1.0f - fModZ;
pWeight[ 1 ] = 0.0f;
pWeight[ 2 ] = fModZ - fModX;
pWeight[ 3 ] = fModX;
}
else
{
pWeight[ 0 ] = 1.0f - fModZ;
pWeight[ 1 ] = 0.0f;
pWeight[ 2 ] = 0.0f;
pWeight[ 3 ] = fModZ;
}
}
float CEtTerrainArea::GetLandHeight( float fX, float fZ, EtVector3 *pNormal )
{
int i;
float fRet;
int nCellX, nCellZ;
float fHeight[ 4 ], fWeight[ 4 ];
DWORD dwSectorWidth = m_TerrainInfo.nSizeX + 1;
CalcCellPosition( fX, fZ, nCellX, nCellZ, fWeight );
fHeight[ 0 ] = m_TerrainInfo.pHeight[ nCellZ * dwSectorWidth + nCellX ];
fHeight[ 1 ] = m_TerrainInfo.pHeight[ nCellZ * dwSectorWidth + nCellX + 1 ];
fHeight[ 2 ] = m_TerrainInfo.pHeight[ ( nCellZ + 1 ) * dwSectorWidth + nCellX ];
fHeight[ 3 ] = m_TerrainInfo.pHeight[ ( nCellZ + 1 ) * dwSectorWidth + nCellX + 1 ];
fRet = 0.0f;
for( i = 0; i < 4; i++ )
{
fRet += fHeight[ i ] * fWeight[ i ];
}
if( pNormal )
{
GetVertexNormal( *pNormal, nCellX, nCellZ );
}
return fRet * m_TerrainInfo.fHeightMultiply;
}
void CEtTerrainArea::GetLandNormal( EtVector3 &Normal, float fX, float fZ )
{
int i;
EtVector3 ReturnVec, VertexNormal[ 4 ];
int nCellX, nCellZ;
float fWeight[ 4 ];
CalcCellPosition( fX, fZ, nCellX, nCellZ, fWeight );
GetVertexNormal( VertexNormal[ 0 ], nCellX, nCellZ );
GetVertexNormal( VertexNormal[ 1 ], nCellX + 1, nCellZ );
GetVertexNormal( VertexNormal[ 2 ], nCellX, nCellZ + 1 );
GetVertexNormal( VertexNormal[ 3 ], nCellX + 1, nCellZ + 1 );
ReturnVec = EtVector3( 0.0f, 0.0f, 0.0f );;
for( i = 0; i < 4; i++ )
{
ReturnVec += VertexNormal[ i ] * fWeight[ i ];
}
EtVec3Normalize( &Normal, &ReturnVec );
}
void CEtTerrainArea::GetVertexNormal( EtVector3 &Normal, int nCellX, int nCellZ )
{
int nHeightIndex;
float fDX, fDZ;
EtVector3 Return;
DWORD dwTileWidth = m_TerrainInfo.nSizeX + 1;
DWORD dwTileHeight = m_TerrainInfo.nSizeY + 1;
nHeightIndex = dwTileWidth * nCellZ + nCellX;
if( nCellX == 0 )
{
fDX = ( m_TerrainInfo.pHeight[ nHeightIndex ] - m_TerrainInfo.pHeight[ nHeightIndex + 1 ] ) / m_TerrainInfo.fTileSize;
}
else if( nCellX == dwTileWidth - 1 )
{
fDX = ( m_TerrainInfo.pHeight[ nHeightIndex - 1 ] - m_TerrainInfo.pHeight[ nHeightIndex ] ) / m_TerrainInfo.fTileSize;
}
else
{
fDX = ( m_TerrainInfo.pHeight[ nHeightIndex - 1 ] - m_TerrainInfo.pHeight[ nHeightIndex + 1 ] ) / ( m_TerrainInfo.fTileSize * 2 );
}
if( nCellZ == 0 )
{
fDZ = ( m_TerrainInfo.pHeight[ nHeightIndex ] - m_TerrainInfo.pHeight[ nHeightIndex + dwTileWidth ] ) / m_TerrainInfo.fTileSize;
}
else if( nCellZ == dwTileHeight - 1 )
{
fDZ = ( m_TerrainInfo.pHeight[ nHeightIndex - dwTileWidth ] - m_TerrainInfo.pHeight[ nHeightIndex ] ) / m_TerrainInfo.fTileSize;
}
else
{
fDZ = ( m_TerrainInfo.pHeight[ nHeightIndex - dwTileWidth ] - m_TerrainInfo.pHeight[ nHeightIndex + dwTileWidth ] ) / ( m_TerrainInfo.fTileSize * 2 );
}
Return.x = fDX;
Return.y = 1.414f;
Return.z = fDZ;
EtVec3Normalize( &Normal, &Return );
}
EtTerrainHandle CEtTerrainArea::GetTerrainArea( CMultiRoom *pRoom, float fX, float fZ )
{
int i;
for( i = 0; i < GetItemCount(pRoom); i++ )
{
EtTerrainHandle hTerrainArea;
hTerrainArea = GetItem( pRoom, i );
if( hTerrainArea->IsInside( fX, fZ ) )
{
return hTerrainArea;
}
}
return CEtTerrainArea::Identity();
}