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

1578 lines
45 KiB
C++

#pragma once
#include "stdafx.h"
#include "TerrainSegment.h"
#include "KViewport.h"
#include "TerrainInfoForSegment.h"
#include "TerrainPropInfo.h"
#include "TerrainPrimitiveFor1Stage.h"
#include "TerrainPrimitiveFor3Stage.h"
#include "TerrainPrimitiveFor4Stage.h"
//#include "K3DFrustum.h"
#include "gamedefine.h"
#include "SRenderFlag.h"
#include "KSeqSpeedTree.h"
#include "KResourceManager.h"
#include "k3dpccp.h"
#include "PVSObject.h"
#include <toolkit/SafeTickCount.h>
//#include "SDebug_Util.h"
class CTerrainPrimitiveInvisible : public CTerrainPrimitive
{
public:
CTerrainPrimitiveInvisible( int nVertexCount, const CTerrainInfo* pTerrainInfo, bool& rResult )
: CTerrainPrimitive( nVertexCount, NULL, pTerrainInfo ) { rResult = true; }
virtual void Render( KViewportObject *viewport, class K3DRenderDevice *dev, bool bUseAccum = true ) { }
public: // for Editor
bool RefreshHeight( const CTerrainInfo* pTerrainInfo, K3DRenderDevice* pDev ) { return true; }
bool RefreshTile( const CTerrainInfo* pTerrainInfo, K3DRenderDevice* pDev ) { return true; }
bool RefreshColor( const CTerrainInfo* pTerrainInfo, K3DRenderDevice* pDev ) { return true; }
};
namespace
{
K3DTexture* getLightmapTex(int segmentX, int segmentY)
{
int newSegmentIndex = 8 * (segmentY / 8) * 896 + 8 * (segmentX / 8 );
char lightMapName[_MAX_PATH];
_snprintf_s(lightMapName, _MAX_PATH, "_light_%d.tga", newSegmentIndex);
NX3LoadPack nx3Pack;
return KTextureManager::GetManager()->GetTexture(lightMapName, &nx3Pack);
}
// sonador #2.1.7.1 스피드 트리 퍼포먼스 증가
// 카메라와의 거리가 PropProcessSkipDist 이상이 되면 프랍의 process 가 호출되지 않도록 한다.
const float PropProcessSkipDist = 1800.f;
// prop의 가시거리에서 어느정도 이상 떨어졌을때 반투명하게 처리할지를 지정하는 값.
// ( 예> 0.7f일 경우 보이는 거리의 70%이상 멀어지면 투명해지기 시작한다. )
const float PropOpaqueDistanceRatio = 0.9f;
}
#ifdef _DEBUG
bool CTerrainSegment::s_bCubeRender = false;
#endif
bool CTerrainSegment::s_bGlassRender = true;
CTerrainSegment::CTerrainSegment
( CTerrainInfoForSegment* pTerrainInfo
// , TERRAIN_FILTERING FilteringType
, TERRAIN_RENDERMODE RenderMode
, float fTileLength
, int nTileCount
, K3DIndexBuffer** ppCommonIndexBuffer
, CTerrainTextureInfo* pTextureInfo
, const CTerrainPropInfo* pPropInfo
, K3DRenderDevice* pDevice
, int nSegmentIndex
, bool& rResult
, DWORD dwRenderTime
, const K3DVertex* pOriginCorrection
, int segmentIndex
, int segmentX
, int segmentY
, bool bEnableLightmap)
//: m_nFilteringType( FilteringType )
//: m_nFilteringType( FilteringType )
: m_nRenderMode(RenderMode)
, m_bVisible( false )
, m_nTileCount( nTileCount )
, m_dwRenderTime( dwRenderTime )
, m_unLastRenderedTick( 0 )
, m_pTerrainInfo( pTerrainInfo )
, m_pTerrainPrimitive( NULL )
, m_pTerrainShadowPrimitive( NULL )
, m_pPropInfo( pPropInfo )
, m_pPropVisibleFlag( NULL )
, m_nPropVisibleFlagCount( 0 )
{
int nVertexCount = (m_nTileCount + 1);
m_nSegmentX = segmentX;
m_nSegmentY = segmentY;
int lightmap_offset_x = m_nSegmentX % 8;
int lightmap_offset_y = m_nSegmentY % 8;
m_nSegmentIndex = segmentIndex;
m_bEnableLightmap = bEnableLightmap;
K3DTexture* pLightmapTex = NULL;
if(m_bEnableLightmap)
pLightmapTex = getLightmapTex(m_nSegmentX, m_nSegmentY);
// 지형 primitive 생성
if( m_pTerrainInfo->NeedPrimitive() )
{
std::list<WORD> UsingTileList;
m_pTerrainInfo->GetUsingTileList( UsingTileList );
//switch( FilteringType )
//{
//case FILTERING_NONE:
// m_pTerrainPrimitive = new CTerrainPrimitiveFor1Stage(
// fTileLength,
// nVertexCount,
// pCommonIndexBuffer,
// UsingTileList,
// pTextureInfo,
// m_pTerrainInfo,
// pDevice,
// rResult );
// break;
//case FILTERING_3TEXTURE:
// m_pTerrainPrimitive = new CTerrainPrimitiveFor3Stage(
// fTileLength,
// nVertexCount,
// pCommonIndexBuffer,
// UsingTileList,
// pTextureInfo,
// m_pTerrainInfo,
// pDevice,
// rResult );
// break;
//case FILTERING_3TEXSHADOW:
// m_pTerrainPrimitive = new CTerrainPrimitiveFor4Stage(
// fTileLength,
// nVertexCount,
// pCommonIndexBuffer,
// UsingTileList,
// pTextureInfo,
// m_pTerrainInfo,
// pDevice,
// rResult );
// break;
//default:
// rResult = false;
// break;
//}
switch( RenderMode )
{
case TERRAIN_RENDERMODE_DEFAULT:
case TERRAIN_RENDERMODE_MULTIPASS:
m_pTerrainPrimitive = new CTerrainPrimitiveFor4Stage(
fTileLength,
nVertexCount,
ppCommonIndexBuffer,
UsingTileList,
pTextureInfo,
m_pTerrainInfo,
pDevice,
RenderMode,
rResult,
pLightmapTex,
lightmap_offset_x,
lightmap_offset_y);
break;
default:
rResult = false;
break;
}
if( NULL != m_pTerrainPrimitive )
{
m_pTerrainPrimitive->SetOriginCorrection( pOriginCorrection );
m_pTerrainShadowPrimitive = m_pTerrainPrimitive->CreateCustomTexturePrimitive();
}
}
else
{
m_pTerrainPrimitive = new CTerrainPrimitiveInvisible(
nVertexCount,
m_pTerrainInfo,
rResult );
}
#ifdef _DEBUG
float minx,miny,minz,maxx,maxy,maxz;
m_pTerrainPrimitive->GetSegmentMinMaxValue( minx,miny,minz,maxx,maxy,maxz );
m_segment_cube = K3DBoundRotCube( minx,maxx, miny,maxy, minz,maxz ); //only segment
m_segment_cube.SetWireColor( KColor(0,0,0,255) );
m_segment_cube.SetColorGradation( true );
#endif
}
CTerrainSegment::~CTerrainSegment()
{
SAFE_DELETE( m_pTerrainShadowPrimitive );
SAFE_DELETE( m_pTerrainPrimitive );
if( m_pPropVisibleFlag ) delete [] m_pPropVisibleFlag;
delete m_pTerrainInfo;
TERRAINSPEEDGRASS_VECTOR::iterator iter = m_vSpeedGrass.begin();
for( ; iter != m_vSpeedGrass.end(); )
{
SAFE_DELETE( (*iter) );
iter = m_vSpeedGrass.erase( iter );
}
m_vSpeedGrass.clear();
}
const TERRAINMAP_PROPERTIES::COLORING& CTerrainSegment::GetMapColoring() const
{
return m_pTerrainInfo->GetMapColoring();
}
void CTerrainSegment::SetTerrainLight( const K3DLight& rLight, K3DRenderDevice* pDevice )
{
m_pTerrainInfo->SetTerrainLight( rLight );
m_pTerrainPrimitive->RefreshColor( m_pTerrainInfo, pDevice );
}
void CTerrainSegment::SetCustomValid( bool bIsValid )
{
if( NULL != m_pTerrainShadowPrimitive )
{
m_pTerrainShadowPrimitive->SetValid( bIsValid );
}
}
WORD CTerrainSegment::GetTile( float x, float y ) const
{
return m_pTerrainInfo->GetGameTile( x, y );
}
float CTerrainSegment::GetSegmentHeight( float x, float y ) const
{
return m_pTerrainInfo->GetZ( x, y );
}
bool CTerrainSegment::GetSegmentColor( float x, float y, KTripleColor &out ) const
{
if( !m_pTerrainInfo->NeedPrimitive() )
return false;
// Read color from terrain info <- NOT WORKING!!
// return m_pTerrainInfo->GetColorByWorldCoordinate( nX, nY );
//
// Read color from vertex buffer
//
float baseX, baseY;
K3DTERRAINVERTEX *p, *q;
K3DMatrix *transform;
// Get reference point
transform=m_pTerrainPrimitive->GetRootMat();
baseX=transform->m30;
baseY=transform->m31;
x-=baseX;
y-=baseY;
// Get vertex buffer
K3DVertexBufferSPtr *vb=m_pTerrainPrimitive->GetVertexBuffer();
if( !vb ) return false;
int vcount=m_pTerrainPrimitive->GetVertexCount();
int count;
(*vb)->Lock( (void**)&p, count );
try {
if (p==NULL) throw 0;
if (x < p->pos.x) throw 0; // nX < GRIDS
if (y < p->pos.y) throw 0; // nY < GRIDS
int i;
// Y 거르기
q=p+vcount; // 한 줄 다음
for (i=0; i<vcount-1; i++) { // '마지막-1' 까지
if (y < q->pos.y) break;
p=q;
q+=vcount;
}
if (i==vcount-1) throw 0; // nY >= GRIDS
// X거르기
q=p+1; // 한 칸 다음
for (i=0; i<vcount-1; i++) {
if (x < q->pos.x) break;
p=q;
q++;
}
if (i==vcount-1) throw 0; // nX >= GRIDS
// 색 blend용 좌표
float dx, dy, dxi, dyi;
dx=(x - p->pos.x) / ((p+1)->pos.x - p->pos.x); if (dx<0.) dx=0; else if (dx>1.) dx=1.;
dy=(y - p->pos.y) / ((p+vcount)->pos.y - p->pos.y); if (dy<0.) dy=0; else if (dy>1.) dy=1.;
dxi=1.f-dx;
dyi=1.f-dy;
// Get 4 grid corner colors
const KColor& rColor00 = p->diffuse;
const KColor& rColor10 = q->diffuse;
p+=vcount;
q+=vcount;
const KColor& rColor01 = p->diffuse;
const KColor& rColor11 = q->diffuse;
// Blend color
K3DVector cl, cr, c;
cl.x=rColor00.r*dyi + rColor01.r*dy;
cl.y=rColor00.g*dyi + rColor01.g*dy;
cl.z=rColor00.b*dyi + rColor01.b*dy;
cr.x=rColor10.r*dyi + rColor11.r*dy;
cr.y=rColor10.g*dyi + rColor11.g*dy;
cr.z=rColor10.b*dyi + rColor11.b*dy;
c.x=cl.x*dxi + cr.x*dx;
c.y=cl.y*dxi + cr.y*dx;
c.z=cl.z*dxi + cr.z*dx;
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
out.r=(unsigned char)c.x;
out.g=(unsigned char)c.y;
out.b=(unsigned char)c.z;
} catch (int) {
out=KTripleColor(0, 0, 0);
}
if (p!=NULL) (*vb)->Unlock();
return true;
}
void CTerrainSegment::GetSegmentTriangle(float x, float y, K3DVector& v1, K3DVector& v2, K3DVector& v3) const
{
m_pTerrainInfo->GetTriangle(x, y, v1, v2, v3);
}
bool CTerrainSegment::GetAttribute( int nAttrX, int nAttrY ) const
{
return m_pTerrainInfo->GetAttribute( nAttrX, nAttrY );
}
K3DVertex CTerrainSegment::GetVertex( int nX, int nY ) const
{
return m_pTerrainInfo->GetVertex( nX, nY );
}
//void CTerrainSegment::ResetStoolColor() const
//{
// for( TERRAINPROP_VECTOR::const_iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
// {
// const TERRAINSTOOL_VECTOR& rStoolVector = (*itProp)->GetStoolVector();
// for( TERRAINSTOOL_VECTOR::const_iterator itStool = rStoolVector.begin(); itStool != rStoolVector.end(); itStool++ )
// (*itStool).BuildWire( KColor( 255, 255, 0, 255 ) );
// }
//}
float CTerrainSegment::GetLineCrossedPointTwoSides( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) const
{
K3DVertex vertices[4];
float fT = 1.0f, fSmallestT = 1.0f;
K3DVector rCurrentPickedPoint;
for( int nY( 0 ); nY < m_nTileCount; ++nY )
{
for( int nX( 0 ); nX < m_nTileCount; ++nX )
{
vertices[0] = m_pTerrainInfo->GetVertex( nX , nY );
vertices[1] = m_pTerrainInfo->GetVertex( nX + 1, nY );
vertices[2] = m_pTerrainInfo->GetVertex( nX , nY + 1 );
vertices[3] = m_pTerrainInfo->GetVertex( nX + 1, nY + 1 );
fT = CTerrainUtil::GetLineCrossedPointTwoSides( vertices, rNear, rFar, rCurrentPickedPoint );
if( fSmallestT > fT )
{
fSmallestT = fT;
rPickedPoint = rCurrentPickedPoint;
return fSmallestT; // 임시 코드 by blackfish
}
}
}
return fSmallestT;
}
static inline float point_dist_point( float px, float py, float qx, float qy )
{
qx -= px, qy -= py;
return sqrt( qx*qx + qy*qy );
}
static inline float point_dist_linesegment( float px, float py, float sx, float sy, float tx, float ty )
{
tx -= sx, ty -= sy;
if ( tx == 0 && ty == 0 )
return point_dist_point( px, py, sx, sy );
float len_st = sqrt( tx*tx + ty*ty );
return abs( (px-sx) * ty - (py-sy) * tx ) / len_st;
}
bool CTerrainSegment::GetLineCrossedPoint( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint ) const
{
float d = point_dist_linesegment( m_pTerrainInfo->GetCenterX(), m_pTerrainInfo->GetCenterY(), rNear.x, rNear.y, rFar.x, rFar.y );
if ( d > m_pTerrainInfo->GetSegmentLength() * 1.414213f )
return false;
const K3DVertex* pCube = m_pTerrainPrimitive->GetTerrainCube();
K3DVertex pLine[ 2 ];
pLine[ 0 ] = rNear;
pLine[ 1 ] = rFar;
if( !KPCCP::edge_collide_rotcube( pLine, pCube ) ) return false;
K3DVertex vertices[4];
for( int nY( 0 ); nY < m_nTileCount; ++nY )
{
for( int nX( 0 ); nX < m_nTileCount; ++nX )
{
vertices[0] = m_pTerrainInfo->GetVertex( nX , nY );
vertices[1] = m_pTerrainInfo->GetVertex( nX + 1, nY );
vertices[2] = m_pTerrainInfo->GetVertex( nX , nY + 1 );
vertices[3] = m_pTerrainInfo->GetVertex( nX + 1, nY + 1 );
if( CTerrainUtil::GetLineCrossedPoint( vertices, rNear, rFar, rPickedPoint ) )
{
return true;
}
}
}
return false;
}
// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
bool CTerrainSegment::GetEllipsoidCrossedPoint
( const K3DVector& rNear
, const K3DVector& rFar
, const K3DVector& rRadius
, const K3DVector& rBaseInESpace
, const K3DVector& rVelocityInESpace
, const K3DMatrix& rCBM
, float& fNearestT
, K3DVector& rPickedPoint
#ifdef _DEBUG
, K3DVector* pCollidedPolygon
, std::vector< std::pair< K3DVector, KColor > >& vecDebugLine
#endif
) const
{
float d = point_dist_linesegment( m_pTerrainInfo->GetCenterX( ), m_pTerrainInfo->GetCenterY( ), rNear.x, rNear.y, rFar.x, rFar.y );
if ( d > m_pTerrainInfo->GetSegmentLength( ) * 1.414213f )
return false;
//const K3DVertex* pCube = m_pTerrainPrimitive->GetTerrainCube( );
//K3DVertex pLine[ 2 ];
//pLine[ 0 ] = rNear;
//pLine[ 1 ] = rFar;
//if( !KPCCP::edge_collide_rotcube( pLine, pCube ) ) return false;
K3DVector min, max;
m_pTerrainPrimitive->GetSegmentMinMaxValue( min.x, min.y, min.z, max.x, max.y, max.z );
min -= rRadius;
max += rRadius;
if( ( rNear.x < min.x && rFar.x < min.x ) || ( rNear.x > max.x && rFar.x > max.x ) ||
( rNear.y < min.y && rFar.y < min.y ) || ( rNear.y > max.y && rFar.y > max.y ) ||
( rNear.z < min.z && rFar.z < min.z ) || ( rNear.z > max.z && rFar.z > max.z ) )
return false;
bool bFound = false;
K3DVector vertices[ 4 ];
for( int nY( 0 ); nY < m_nTileCount; ++nY )
{
for( int nX( 0 ); nX < m_nTileCount; ++nX )
{
vertices[ 0 ] = m_pTerrainInfo->GetVertex( nX , nY );
vertices[ 1 ] = m_pTerrainInfo->GetVertex( nX + 1, nY );
vertices[ 2 ] = m_pTerrainInfo->GetVertex( nX , nY + 1 );
vertices[ 3 ] = m_pTerrainInfo->GetVertex( nX + 1, nY + 1 );
#ifdef _DEBUG
vecDebugLine.push_back( std::make_pair( vertices[ 0 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 1 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 1 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 2 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 2 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 0 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 1 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 3 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 3 ], KColor( 0xff00ff00 ) ) );
vecDebugLine.push_back( std::make_pair( vertices[ 2 ], KColor( 0xff00ff00 ) ) );
#endif
// transform to ellipsoid vector space
for( int n = 0; n < 4; ++n )
K3DVectorTransform( vertices[ n ], vertices[ n ], rCBM );
if( CTerrainUtil::GetEllipsoidCrossedPoint( vertices, rBaseInESpace, rVelocityInESpace, fNearestT, rPickedPoint
#ifdef _DEBUG
, pCollidedPolygon
#endif
) )
{
bFound = true;
}
}
}
return bFound;
}
// }
const int LOAD_PROP_MAX_SIZE = 1;
void CTerrainSegment::ProcessLoadedProps()
{
THREAD_SYNCRONIZE( m_PropLock );
int nLoadedCnt = 0;
int nLoadingCnt = 0;
// 로딩 끝난 녀석을 m_NotLoadedPropVector 에 넣어준다.
for( TERRAINPROP_VECTOR::iterator itProp = m_NotLoadedPropVector.begin(); itProp != m_NotLoadedPropVector.end(); )
{
const CTerrainPropForGameSPtr& spProp = (*itProp);
// 로딩 끝났으면...
if( (*itProp)->IsRealLoad() )
{
++nLoadedCnt;
m_Loaded_PropVector.push_back( spProp );
itProp = m_NotLoadedPropVector.erase( itProp );
continue;
}
// 이미 로딩 걸려 있으면 패스
if( (*itProp)->IsPendLoading() )
{
++nLoadingCnt;
++itProp;
continue;
}
++itProp;
}
if( !nLoadingCnt || nLoadedCnt > 10 )
{
if( nLoadedCnt )
{
applyProps();
}
}
if( nLoadedCnt && m_nPropVisibleFlagCount < (int)m_Loaded_PropVector.size() )
{
if( m_pPropVisibleFlag ) delete [] m_pPropVisibleFlag;
m_nPropVisibleFlagCount = (int)m_Loaded_PropVector.size();
m_pPropVisibleFlag = new char[ m_nPropVisibleFlagCount/8 + 1 ];
}
}
void CTerrainSegment::LoadAllProps( const K3DVector* pFrustum /*= NULL*/ )
{
THREAD_SYNCRONIZE( m_PropLock );
for( TERRAINPROP_VECTOR::iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
{
CTerrainPropForGameSPtr pProp = (*itProp);
if( pProp->GetParentSegment() != this ) continue;
if( pFrustum )
{
pProp->ClipTest( pFrustum );
if( pProp->GetIsClip() )
continue;
}
pProp->Load_Real();
m_Loaded_PropVector.push_back( pProp );
}
applyProps();
}
void CTerrainSegment::SetWindStrength( float fStrength )
{
for(TERRAINPROP_VECTOR::iterator itProp = m_Loaded_PropVector.begin(); itProp != m_Loaded_PropVector.end(); itProp++)
{
if((*itProp)->GetPropType() == GCLAN_PROP_SPEED)
{
KSeqSpeedTree* pTree = (KSeqSpeedTree*) (*itProp)->GetModel();
pTree->SetWindStrength( fStrength );
}
}
}
void CTerrainSegment::GetStoolLineCrossedPoint( const K3DVector& rNear, const K3DVector& rFar, K3DVector& rPickedPoint, TERRAIN_PICK_RESULT & nStoolPickResult, float & fStoolDistance, K3DVector & vtCurrentPoint, std::vector< CTerrainProp * > & check_list )
{
THREAD_SYNCRONIZE( m_PropLock );
// 프랍들에 대해 체크
TERRAINPROP_VECTOR& rPropVector = m_Loaded_PropVector;
float fHeight = 0.f;
for( TERRAINPROP_VECTOR::iterator itProp = rPropVector.begin(); itProp != rPropVector.end(); itProp++ )
{
bool IsCheck = false;
for( unsigned int i(0); check_list.size()>i; i++ )
{ //TODO : 최적화의 여지가 남아 있다.
if( check_list[i] == (*itProp) )
{
IsCheck = true;
break;
}
}
if( IsCheck ) continue; //이미 체크 되어 있음
if( (*itProp)->GetBoundCube()->CheckCollision( rNear, rFar ) )
{
// _oprint( "Click Prop : %s \n", (*itProp)->m_strFileName.c_str() );
check_list.push_back( (*itProp) );
if( !(*itProp)->GetProcessFlagAtThisTick() ) continue;
if( (*itProp)->IsHeight() )
{
float fCurrentDistance = (*itProp)->GetPropHeight( rNear, rFar, vtCurrentPoint );
if( fStoolDistance > fCurrentDistance )
{
fStoolDistance = fCurrentDistance;
rPickedPoint = vtCurrentPoint;
nStoolPickResult = TERRAIN_STOOL_PICK_SUCCECED;
}
}
}
}
}
// { [sonador][COLLIDABLE_CAMERA]
bool CTerrainSegment::GetPropLineCrossedPointTwoSidesWithCollisionMesh
( const K3DVector& rayStart
, const K3DVector& rayEnd
, float& fNearestT
, K3DVector& vCandidateCollidedPoint
#ifdef _DEBUG
, K3DVector* pCollidedPolygon
#endif
)
{
THREAD_SYNCRONIZE( m_PropLock );
#ifdef _DEBUG
K3DVector collidedPolygon[ 3 ];
#endif
bool bFound = false;
TERRAINPROP_VECTOR& propVector = m_Loaded_PropVector;
TERRAINPROP_VECTOR::iterator itPropEnd = propVector.end();
for( TERRAINPROP_VECTOR::iterator itProp = propVector.begin();
itProp != itPropEnd; ++itProp )
{
if( !(*itProp)->GetProcessFlagAtThisTick() ) continue;
if( (*itProp)->GetBoundCube()->CheckCollision( rayStart, rayEnd ) )
{
K3DVector vCollisionPoint;
float fT = (*itProp)->CheckPolygonCollisionWithCollisionMesh(
rayStart, rayEnd, fNearestT, vCollisionPoint
#ifdef _DEBUG
, collidedPolygon
#endif
);
if( fNearestT > fT )
{
bFound = true;
fNearestT = fT;
vCandidateCollidedPoint = vCollisionPoint;
#ifdef _DEBUG
pCollidedPolygon[ 0 ] = collidedPolygon[ 0 ];
pCollidedPolygon[ 1 ] = collidedPolygon[ 1 ];
pCollidedPolygon[ 2 ] = collidedPolygon[ 2 ];
#endif
}
}
}
return bFound;
}
// 2010.07.22 - prodongi
//extern std::vector<string> collidedMesh;
extern std::vector<DWORD> collidedMesh;
// }
// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
bool CTerrainSegment::GetPropEllipsoidCrossedPointTwoSidesWithCollisionMesh
( const K3DVector& rayStart
, const K3DVector& rayEnd
, const K3DVector& rayStartInESpace
, const K3DVector& rayEndInESpace
, const K3DMatrix& matCBM
, float& fNearestT
, K3DVector& vCandidateCollidedPoint
#ifdef _DEBUG
, K3DVector* pCollidedPolygon
#endif
)
{
THREAD_SYNCRONIZE( m_PropLock );
bool bFound = false;
TERRAINPROP_VECTOR& propVector = m_Loaded_PropVector;
TERRAINPROP_VECTOR::iterator itPropEnd = propVector.end();
for( TERRAINPROP_VECTOR::iterator itProp = propVector.begin();
itProp != itPropEnd; ++itProp )
{
if( !(*itProp)->GetProcessFlagAtThisTick() ) continue;
// 2010.07.22 m_strFileName은 고유 번호가 될 수 없다- prodongi
/*
if ( find(collidedMesh.begin(), collidedMesh.end(), (*itProp)->m_strFileName) == collidedMesh.end() )
collidedMesh.push_back((*itProp)->m_strFileName);
else
continue;
*/
if ( find(collidedMesh.begin(), collidedMesh.end(), (*itProp)->GetID()) == collidedMesh.end() )
collidedMesh.push_back((*itProp)->GetID());
else
continue;
if( (*itProp)->GetBoundCube()->CheckCollision( rayStart, rayEnd ) )
{
if( (*itProp)->CheckPolygonEllipsoidCollisionWithCollisionMesh( rayStartInESpace, rayEndInESpace, matCBM, fNearestT, vCandidateCollidedPoint
#ifdef _DEBUG
, pCollidedPolygon
#endif
) )
{
bFound = true;
}
}
}
return bFound;
}
// }
void CTerrainSegment::GetPropLineCrossedPointTwoSides( const K3DVector& rNear, const K3DVector& rFar, float & fSmallestT, K3DVector& rCandidatePickedPoint )
{
THREAD_SYNCRONIZE( m_PropLock );
TERRAINPROP_VECTOR& rPropVector = m_Loaded_PropVector;
float fHeight = 0.f;
for( TERRAINPROP_VECTOR::iterator itProp = rPropVector.begin(); itProp != rPropVector.end(); itProp++ )
{
if( !(*itProp)->GetProcessFlagAtThisTick() ) continue;
if( (*itProp)->GetBoundCube()->CheckCollision( rNear, rFar ) )
{
K3DVector rTempPickedPoint;
float fT = (*itProp)->CheckPolygonCollision( rNear, rFar, rTempPickedPoint );
if( fSmallestT > fT )
{
fSmallestT = fT;
rCandidatePickedPoint = rTempPickedPoint;
}
}
}
return;
}
bool CTerrainSegment::GetStoolTriangle( float x, float y, K3DVector& v1, K3DVector& v2, K3DVector& v3, float fTerrainHeight ) const
{
const CTerrainPropForGame* pSelectProp = NULL;
K3DVector pos1 = K3DVector( x, y, 50000.f);
K3DVector pos2 = K3DVector( x, y, -50000.f);
K3DVector vResult;
float fResult = 1.f;
for( TERRAINPROP_VECTOR::const_iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
{
if( (*itProp)->IsHeight() && (*itProp)->IsInRectArea(x,y) )
{
float resultCur = (*itProp)->GetPropHeightByTriangleIndex(x,y);
if( fTerrainHeight < resultCur )
{
fTerrainHeight = resultCur;
pSelectProp = (*itProp);
}
}
}
if( pSelectProp )
{
pSelectProp->GetPropTriangle( x, y, v1, v2, v3 );
return true;
}
return false;
}
bool CTerrainSegment::GetStoolHeight( float x, float y, float& rGetHeight )
{
THREAD_SYNCRONIZE( m_PropLock );
#ifndef NDEBUG
DWORD dwStartTime = GetSafeTickCount();
#endif
bool bGetStoolHeight = false;
TERRAIN_PICK_RESULT nStoolPickResult = TERRAIN_STOOL_PICK_FAILED;
K3DVector pos1 = K3DVector( x, y, 50000.f);
K3DVector pos2 = K3DVector( x, y, -50000.f);
K3DVector vResult;
for( TERRAINPROP_VECTOR::const_iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
{
if( (*itProp)->IsHeight() && (*itProp)->IsInRectArea(x,y) )
{
float resultCur = (*itProp)->GetPropHeightByTriangleIndex(x,y);
if( rGetHeight < resultCur )
{
rGetHeight = resultCur;
bGetStoolHeight = true;
}
}
}
#ifndef NDEBUG
// _oprint( "[시간 검사 GetStoolHeight : %d]\n", GetSafeTickCount()-dwStartTime );
#endif
return bGetStoolHeight;
}
CTerrainProp* CTerrainSegment::GetTerrainProp( float x, float y, float& rGetHeight )
{
THREAD_SYNCRONIZE( m_PropLock );
TERRAIN_PICK_RESULT nStoolPickResult = TERRAIN_STOOL_PICK_FAILED;
K3DVector pos1 = K3DVector( x, y, 50000.f);
K3DVector pos2 = K3DVector( x, y, -50000.f);
K3DVector vResult;
CTerrainProp* pTerrainProp = NULL;
for( TERRAINPROP_VECTOR::iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
{
if( (*itProp)->IsHeight() && (*itProp)->IsInRectArea(x,y) )
{
float resultCur = (*itProp)->GetPropHeightByTriangleIndex(x,y);
if( rGetHeight < resultCur )
{
rGetHeight = resultCur;
pTerrainProp = (*itProp)->GetTerrainProp();
}
}
}
return pTerrainProp;
}
void CTerrainSegment::applyProps()
{
if( m_nRenderMode == TERRAIN_RENDERMODE_DEFAULT)
{
m_pTerrainPrimitive->ReloadProp( m_PropVector );
}
if( m_nPropVisibleFlagCount < (int)m_Loaded_PropVector.size() )
{
if( m_pPropVisibleFlag ) delete [] m_pPropVisibleFlag;
m_nPropVisibleFlagCount = (int)m_Loaded_PropVector.size();
m_pPropVisibleFlag = new char[ m_nPropVisibleFlagCount/8 + 1 ];
}
}
bool CTerrainSegment::IsFrustumCollide( const K3DVector* pFrustum )
{
return KPCCP::nonuniformcube_collide_nonuniformcube(pFrustum, m_pTerrainPrimitive->GetVisibleCube());
}
bool CTerrainSegment::PVSDrawSegment( const unsigned long uRenderBitVector, KViewportObject** ppViewportList, int nViewportCount, const K3DVector* pFrustum, const K3DVector& rCameraPos, float fVisibleDistance, float fLodDistance, float fDistanceRatio, DWORD dwCurrentTime, unsigned unCurrentRenderTick, int & nRenderPropCnt, PVSObjectManager* pPVSObjManager )
{
const int fixFps = 24;
bool bPropProcess = true;
float msFps = 1000.f / fixFps;
if ( (dwCurrentTime - m_dwRenderTime) < msFps )
{
bPropProcess = false;
}
else
{
m_dwRenderTime = dwCurrentTime;
}
m_bVisible = pPVSObjManager->CheckSegment( m_nSegmentX, m_nSegmentY );
m_unLastRenderedTick = unCurrentRenderTick;
bool bAnyVisible = false, bVPVisible[8] = { false, };
if( m_bVisible )
{ //카메라 Frustum Check
for(int vit = 0; vit < nViewportCount; ++vit) {
m_pTerrainPrimitive->TestRenderObject();
bVPVisible[vit] = KPCCP::nonuniformcube_collide_nonuniformcube(ppViewportList[vit]->GetFrustum(), m_pTerrainPrimitive->GetVisibleCube());
// bAnyVisible은 하나의 viewport에서라도 보이면 체크된다. - 01
if(bVPVisible[vit]) bAnyVisible = true;
if(m_pTerrainInfo->NeedPrimitive() == false || bVPVisible[vit] == false) continue;
if( uRenderBitVector & Game_RenderSegment )
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) != 0 ||
(ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_WATER) != 0 )
{
#ifdef _DEBUG
if( s_bCubeRender )
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) != 0 )
{
m_visible_cube.Render( ppViewportList[vit] );
m_segment_cube.Render( ppViewportList[vit] );
}
}
#endif
m_pTerrainPrimitive->CalcLength( rCameraPos );
ppViewportList[vit]->Register( m_pTerrainPrimitive, KRenderObject::RENDEREFX_TERRAIN);
if( uRenderBitVector & Game_RenderSegShadow01 )
{
if( NULL != m_pTerrainShadowPrimitive && m_pTerrainShadowPrimitive->IsValid() )
{
ppViewportList[vit]->Register( m_pTerrainShadowPrimitive, KRenderObject::RENDEREFX_SHADOW_TERRAIN);
}
}
}
}
}
}
m_bVisible = bVPVisible[0];
//프랍은 세그먼트와 상관 없이 보일 수 있음.
if( true )
{
if( bAnyVisible )
{
THREAD_SYNCRONIZE( m_PropLock );
bool bPropLoadCompleteFlag = false;
for( TERRAINPROP_VECTOR::iterator itLoadingProp = m_NotLoadedPropVector.begin(); itLoadingProp != m_NotLoadedPropVector.end(); )
{
CTerrainPropForGame* pProp = (*itLoadingProp);
if( pProp->IsRealLoad() )
{
bPropLoadCompleteFlag = true;
++itLoadingProp;
continue;
}
if( pProp->IsPendLoading() )
{
++itLoadingProp;
continue;
}
float fPropAlphaRatio = PropOpaqueDistanceRatio;
// 카메라와의 거리 체크
float fGapX = rCameraPos.x - pProp->GetPos().x;
float fGapY = rCameraPos.y - pProp->GetPos().y;
float fCameraDist = sqrtf((fGapX * fGapX) + (fGapY * fGapY));
// 프랍의 크기도 고려
float fPropWidth = pProp->GetBoundCube()->GetWidth();
float fPropLength = pProp->GetBoundCube()->GetLength();
float fRadius = sqrtf((fPropWidth * fPropWidth) + (fPropLength * fPropLength)) / 2.0f;
// 시야에 보일만한 거리에 있는 놈들만 로딩건다.
if( fVisibleDistance + fRadius*2 > fCameraDist )
{
pProp->PendLoading();
}
++itLoadingProp;
}
if( bPropLoadCompleteFlag )
{
ProcessLoadedProps();
}
}//if( bAnyVisible )
if( !m_Loaded_PropVector.empty() )
{
int prop_visible_count = 0;
memset( m_pPropVisibleFlag, 0, m_nPropVisibleFlagCount/8 + 1 );
if( uRenderBitVector & Game_RenderPropProc )
{
THREAD_SYNCRONIZE( m_PropLock );
for( TERRAINPROP_VECTOR::iterator itProp = m_Loaded_PropVector.begin(); itProp != m_Loaded_PropVector.end(); itProp++, prop_visible_count++ )
{
CTerrainPropForGame* pProp = (*itProp);
if( !pPVSObjManager->CheckProp( pProp->GetSegmentIdx(), pProp->GetPropIdx() ) )
continue;
if( pProp->GetProcessFlagAtThisTick() )
continue;
pProp->SetProcessFlagAtThisTick();
float fBoundary, fPropAlphaRatio = PropOpaqueDistanceRatio;
// 카메라와의 거리 체크
float fGapX = rCameraPos.x - pProp->GetPos().x;
float fGapY = rCameraPos.y - pProp->GetPos().y;
float fCameraDist = sqrtf((fGapX * fGapX) + (fGapY * fGapY));
// 프랍의 크기도 고려
float fPropWidth = pProp->GetBoundCube()->GetWidth();
float fPropLength = pProp->GetBoundCube()->GetLength();
float fRadius = sqrtf((fPropWidth * fPropWidth) + (fPropLength * fPropLength)) / 2.0f;
float fDist = fCameraDist - fRadius*2;
if(fDist < 0.0f) fDist = 0.0f;
////프랍이 일정거리 이상이면, Process Skip
//if( fDist >= PropProcessSkipDist )
// bPropProcess = false;
float fPropVisibleRatio = m_pPropInfo->GetPropVisibleRatio( pProp->GetPropNum() );
// 알파가 fBoundary 이하인것은 그리지 않는다.
// fBoundary & fPropAlphaRatio 조절
if( fPropVisibleRatio < 2.0f ) { fBoundary = 0.0f; fPropAlphaRatio = 0.6f; }
else if( fPropVisibleRatio < 3.0f ) { fBoundary = 0.1f; fPropAlphaRatio = 0.7f; }
else if( fPropVisibleRatio < 4.0f ) { fBoundary = 0.2f; fPropAlphaRatio = 0.8f; }
else { fBoundary = 0.3f; }
float fPropVisibleDistance = fVisibleDistance * fPropVisibleRatio;
float fPropAlphaDistance = (fPropVisibleDistance * fPropAlphaRatio);
float fPropVisibility = GetPropVisibility( fDist, fPropVisibleDistance * pProp->GetVisRatio(), fPropAlphaDistance * pProp->GetVisRatio() );
// 알파 테스트
if( fBoundary > fPropVisibility ) continue;
pProp->SetLodLevel( (fDist < fLodDistance) ? 0 : 1 );
m_pPropVisibleFlag[ prop_visible_count / 8 ] |= ( 1 << ( prop_visible_count % 8 ) );
if( true /*bPropProcess*/ )
{
pProp->SetVisibility( fPropVisibility );
//pProp->SetVisible(fCameraDist, fPropVisibleDistance);
}
pProp->SetFloatLod(fCameraDist, fPropVisibleDistance);
}
}
if( uRenderBitVector & Game_RenderProp )
{
THREAD_SYNCRONIZE( m_PropLock );
for(int vit = 0; vit < nViewportCount; ++vit)
{
if(vit != 0 && bVPVisible[vit] == false) continue;
K3DVector *npFrustum = ppViewportList[vit]->GetFrustum();
prop_visible_count = 0;
for( TERRAINPROP_VECTOR::iterator itProp = m_Loaded_PropVector.begin(); itProp != m_Loaded_PropVector.end(); itProp++, prop_visible_count++ )
{
CTerrainPropForGame* pProp = (*itProp);
if( ( 1 << (prop_visible_count % 8) ) & m_pPropVisibleFlag[ ( prop_visible_count / 8 ) ] )
{
pProp->ClipTest( npFrustum );
if( !pProp->GetIsClip() )
{
pProp->Process( dwCurrentTime );
//현재 랜더링 되는 객체 수, 기본 뷰포트에서 그리는 것만 Count
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) ) nRenderPropCnt++;
pProp->Render( ppViewportList[vit] );
}
}
}
}
}
}
if( s_bGlassRender && !m_vSpeedGrass.empty() )
{//==========================
for(int vit = 0; vit < nViewportCount; ++vit)
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) )
{
TERRAINSPEEDGRASS_VECTOR::iterator iter = m_vSpeedGrass.begin();
for( ; iter != m_vSpeedGrass.end(); ++iter )
{
(*iter)->Process( dwCurrentTime );
(*iter)->Render( ppViewportList[vit] );
}
}
}
}//==========================
}
if( m_pTerrainInfo->NeedPrimitive() )
return m_bVisible;
else
return false;
return m_bVisible;
}
bool CTerrainSegment::DrawSegment( const unsigned long uRenderBitVector, KViewportObject** ppViewportList, int nViewportCount, const K3DVector* pFrustum,
const K3DVector& rCameraPos, float fVisibleDistance, float fLodDistance, float fDistanceRatio,
DWORD dwCurrentTime, unsigned unCurrentRenderTick, int & nRenderPropCnt, int nLimitDistance/*=0*/ )
{
const int fixFps = 24;
bool bPropProcess = true;
float msFps = 1000.f / fixFps;
if ( (dwCurrentTime - m_dwRenderTime) < msFps )
{
bPropProcess = false;
}
else
{
m_dwRenderTime = dwCurrentTime;
}
m_unLastRenderedTick = unCurrentRenderTick;
bool bAnyVisible = false, bVPVisible[8] = { false, };
//하늘이 아닌 경우만, 랜더 한다.
{
for(int vit = 0; vit < nViewportCount; ++vit) {
m_pTerrainPrimitive->TestRenderObject();
bVPVisible[vit] = KPCCP::nonuniformcube_collide_nonuniformcube(ppViewportList[vit]->GetFrustum(), m_pTerrainPrimitive->GetVisibleCube());
// bAnyVisible은 하나의 viewport에서라도 보이면 체크된다. - 01
if(bVPVisible[vit]) bAnyVisible = true;
if(m_pTerrainInfo->NeedPrimitive() == false || bVPVisible[vit] == false) continue;
//강제로 마을 같은 경우는 거리 제한이 생긴다.
if( nLimitDistance > 0 )
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) != 0 )
{
#ifdef _DEBUG
K3DVertex tpos = ppViewportList[vit]->GetCameraTargetPos();
K3DVertex spos = m_pTerrainPrimitive->GetWorldTransform();
#endif
float fD1 = K3DVectorLength( ppViewportList[vit]->GetCameraTargetPos() - m_pTerrainPrimitive->GetWorldTransform() );
int nLimit = 3000;
if ((m_nMapX == 12) && (m_nMapY == 7)) nLimit = 3500; // 엘카시아 던전에서만 약간 늘림
if( fD1 > nLimit ) continue;
}
}
if( uRenderBitVector & Game_RenderSegment )
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) != 0 ||
(ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_WATER) != 0 )
{
#ifdef _DEBUG
if( s_bCubeRender )
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) != 0 )
{
m_visible_cube.Render( ppViewportList[vit] );
m_segment_cube.Render( ppViewportList[vit] );
}
}
#endif
m_pTerrainPrimitive->CalcLength( rCameraPos );
ppViewportList[vit]->Register( m_pTerrainPrimitive, KRenderObject::RENDEREFX_TERRAIN);
if( uRenderBitVector & Game_RenderSegShadow01 )
{
if( NULL != m_pTerrainShadowPrimitive && m_pTerrainShadowPrimitive->IsValid() ) {
ppViewportList[vit]->Register( m_pTerrainShadowPrimitive, KRenderObject::RENDEREFX_SHADOW_TERRAIN);
}
}
}
}
}
}
// m_bVisibe은 GameViewport에서 보일때만 체크된다. - 01
m_bVisible = bVPVisible[0];
if( bAnyVisible )
{
THREAD_SYNCRONIZE( m_PropLock );
bool bPropLoadCompleteFlag = false;
for( TERRAINPROP_VECTOR::iterator itLoadingProp = m_NotLoadedPropVector.begin(); itLoadingProp != m_NotLoadedPropVector.end(); )
{
CTerrainPropForGame* pProp = (*itLoadingProp);
if( pProp->IsRealLoad() )
{
bPropLoadCompleteFlag = true;
++itLoadingProp;
continue;
}
if( pProp->IsPendLoading() )
{
++itLoadingProp;
continue;
}
//float fPropAlphaRatio = PropOpaqueDistanceRatio;
// 카메라와의 거리 체크
float fGapX = rCameraPos.x - pProp->GetPos().x;
float fGapY = rCameraPos.y - pProp->GetPos().y;
float fCameraDist = sqrtf((fGapX * fGapX) + (fGapY * fGapY));
// 프랍의 크기도 고려
float fPropWidth = pProp->GetBoundCube()->GetWidth();
float fPropLength = pProp->GetBoundCube()->GetLength();
float fRadius = sqrtf((fPropWidth * fPropWidth) + (fPropLength * fPropLength)) / 2.0f;
// 시야에 보일만한 거리에 있는 놈들만 로딩건다.
if( fVisibleDistance + fRadius*2 > fCameraDist )
{
pProp->PendLoading();
}
++itLoadingProp;
}
if( bPropLoadCompleteFlag )
{
ProcessLoadedProps();
}
if( !m_Loaded_PropVector.empty() )
{
int prop_visible_count = 0;
memset( m_pPropVisibleFlag, 0, m_nPropVisibleFlagCount/8 + 1 );
if( uRenderBitVector & Game_RenderPropProc )
{
THREAD_SYNCRONIZE( m_PropLock );
for( TERRAINPROP_VECTOR::iterator itProp = m_Loaded_PropVector.begin(); itProp != m_Loaded_PropVector.end(); itProp++, prop_visible_count++ )
{
CTerrainPropForGame* pProp = (*itProp);
if( pProp->GetProcessFlagAtThisTick() )
continue;
pProp->SetProcessFlagAtThisTick();
float fPropAlphaRatio = PropOpaqueDistanceRatio;
// 카메라와의 거리 체크
float fGapX = rCameraPos.x - pProp->GetPos().x;
float fGapY = rCameraPos.y - pProp->GetPos().y;
float fCameraDist = sqrtf((fGapX * fGapX) + (fGapY * fGapY));
// 프랍의 크기도 고려
float fPropWidth = pProp->GetBoundCube()->GetWidth();
float fPropLength = pProp->GetBoundCube()->GetLength();
float fRadius = sqrtf((fPropWidth * fPropWidth) + (fPropLength * fPropLength)) / 2.0f;
float fDist = fCameraDist - fRadius;
if(fDist < 0.0f) fDist = 0.0f;
////프랍이 일정거리 이상이면, Process Skip
//if( fDist >= PropProcessSkipDist )
// bPropProcess = false;
float fPropVisibleRatio = m_pPropInfo->GetPropVisibleRatio( pProp->GetPropNum() );
//// 알파가 fBoundary 이하인것은 그리지 않는다.
//// fBoundary & fPropAlphaRatio 조절
//if( fPropVisibleRatio < 0.2f ) { fBoundary = 0.0f; fPropAlphaRatio = 0.6f; }
//else if( fPropVisibleRatio < 0.3f ) { fBoundary = 0.1f; fPropAlphaRatio = 0.7f; }
//else if( fPropVisibleRatio < 0.4f ) { fBoundary = 0.2f; fPropAlphaRatio = 0.8f; }
//else { fBoundary = 0.3f; }
float fPropVisibleDistance = fVisibleDistance * fPropVisibleRatio;
float fPropAlphaDistance = (fPropVisibleDistance * fPropAlphaRatio);
float fPropLodDistance = fLodDistance * fPropVisibleRatio * pProp->GetVisRatio();
float fPropVisibility = GetPropVisibility( fDist, fPropVisibleDistance * pProp->GetVisRatio(), fPropAlphaDistance * pProp->GetVisRatio() );
// 알파 테스트
// if( fBoundary > fPropVisibility ) continue;
// if( fPropVisibility <= 0.1f ) continue;
pProp->SetLodLevel( ( fDist < fPropLodDistance ) ? 0 : 1 );
m_pPropVisibleFlag[ prop_visible_count / 8 ] |= ( 1 << ( prop_visible_count % 8 ) );
if( bPropProcess )
{
pProp->SetVisibility( fPropVisibility );
//pProp->SetVisible(fCameraDist, fPropVisibleDistance);
}
pProp->SetFloatLod( fCameraDist, fPropLodDistance );
}
}
if( uRenderBitVector & Game_RenderProp )
{
THREAD_SYNCRONIZE( m_PropLock );
for(int vit = 0; vit < nViewportCount; ++vit)
{
if(bVPVisible[vit] == false) continue;
K3DVector *npFrustum = ppViewportList[vit]->GetFrustum();
prop_visible_count = 0;
for( TERRAINPROP_VECTOR::iterator itProp = m_Loaded_PropVector.begin(); itProp != m_Loaded_PropVector.end(); itProp++, prop_visible_count++ )
{
CTerrainPropForGame* pProp = (*itProp);
if( ( 1 << (prop_visible_count % 8) ) & m_pPropVisibleFlag[ ( prop_visible_count / 8 ) ] )
{
pProp->ClipTest( npFrustum );
if( !pProp->GetIsClip() )
{
pProp->Process( dwCurrentTime );
//강제로 마을 같은 경우는 거리 제한이 생긴다.
if( nLimitDistance > 0 )
{
#ifdef _DEBUG
K3DVertex tpos = ppViewportList[vit]->GetCameraTargetPos();
K3DVertex spos = m_pTerrainPrimitive->GetWorldTransform();
#endif
float fD1 = K3DVectorLength( ppViewportList[vit]->GetCameraTargetPos() - pProp->GetTransform().GetPosVector() );
if( fD1 > nLimitDistance )
{
//크기가 작은 것들을 우선으로 걸른다.
if( pProp->IsSizeCull() ) continue;
}
}
//현재 랜더링 되는 객체 수, 기본 뷰포트에서 그리는 것만 Count
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) ) nRenderPropCnt++;
pProp->Render( ppViewportList[vit] );
}
}
}
}
}
}
if( s_bGlassRender && !m_vSpeedGrass.empty() )
{//==========================
for(int vit = 0; vit < nViewportCount; ++vit)
{
if( (ppViewportList[vit]->GetAttributes() & KViewportObject::VIEWPORT_GAME) )
{
TERRAINSPEEDGRASS_VECTOR::iterator iter = m_vSpeedGrass.begin();
for( ; iter != m_vSpeedGrass.end(); ++iter )
{
(*iter)->Process( dwCurrentTime );
(*iter)->Render( ppViewportList[vit] );
}
}
}
}//==========================
}
if( m_pTerrainInfo->NeedPrimitive() )
return m_bVisible;
else
return false;
return m_bVisible;
}
TERRAIN_LOD_LEVEL CTerrainSegment::CalcLength( const K3DVector& rCameraPos )
{
if( m_pTerrainPrimitive == NULL )
return TERRAIN_LOD_LEVEL_MAX;
else
{
if( m_pTerrainInfo->NeedPrimitive() == false )
return TERRAIN_LOD_LEVEL_MAX;
}
m_pTerrainPrimitive->CalcLength( rCameraPos );
return m_pTerrainPrimitive->GetTerrainLodLevel();
}
void CTerrainSegment::SetNeighborsTerrainLoadLevel( TERRAIN_LOD_LEVEL nLevel, TERRAIN_LOD_CRACK nLodCrack )
{
if( m_pTerrainPrimitive )
m_pTerrainPrimitive->SetNeighborsTerrainLoadLevel( nLevel, nLodCrack );
}
namespace
{
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 ) );
}
inline void GetPropMinMaxVertices( const CTerrainPropForGame* pProp, K3DVertex& rMin, K3DVertex& rMax )
{
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 );
}
}
}
void CTerrainSegment::TakeNeighborsPropInfo( CTerrainSegment *pNeighbor, K3DPoint & ptMinSegment, K3DPoint & ptMaxSegment )
{
THREAD_SYNCRONIZE( pNeighbor->m_PropLock );
TERRAINPROP_VECTOR & rPropVector = pNeighbor->m_PropVector;
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 ) )
{
AddProp( *itProp );
}
}
#ifdef _DEBUG
float minx,miny,minz,maxx,maxy,maxz;
m_pTerrainPrimitive->GetVisibleMinMaxValue( minx,miny,minz,maxx,maxy,maxz );
m_visible_cube = K3DBoundRotCube( minx,maxx, miny,maxy, minz,maxz ); //include prop
m_visible_cube.SetWireColor( KColor(0,0,255,255) );
m_visible_cube.SetColorGradation( true );
#endif
}
void CTerrainSegment::AddProp( CTerrainPropForGame* pProp )
{
THREAD_SYNCRONIZE( m_PropLock );
if( std::find( m_PropVector.begin(), m_PropVector.end(), pProp ) == m_PropVector.end() )
{
m_PropVector.push_back( CTerrainPropForGameSPtr(pProp) );
if( !pProp->IsRealLoad() )
{
m_NotLoadedPropVector.push_back( CTerrainPropForGameSPtr(pProp) );
}
//applyProps();
}
m_pTerrainPrimitive->SetPropBound( pProp->GetBoundCube()->GetMinVertex(), pProp->GetBoundCube()->GetMaxVertex() );
}
bool CTerrainSegment::IsPropLoadCompleted()
{
THREAD_SYNCRONIZE( &m_PropLock );
for( TERRAINPROP_VECTOR::const_iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
if( !(*itProp)->IsLoadCompleted() ) return false;
return true;
}
float CTerrainSegment::GetPropVisibility( float fCameraDist, float fPropVisibleDist, float fPropAlphaDist ) const
{
if( fCameraDist > fPropVisibleDist )
return 0.f;
if( fCameraDist > fPropAlphaDist )
return 1.f - ( (fCameraDist - fPropAlphaDist) / (fPropVisibleDist - fPropAlphaDist) );
return 1.f;
}
void CTerrainSegment::AddSpeedGrass( CTerrainSpeedGrassForGame* pSpeedGrass )
{
m_vSpeedGrass.push_back( pSpeedGrass );
}
void CTerrainSegment::RefreshTerrainLightmap(bool bEnableLightmap)
{
m_bEnableLightmap = bEnableLightmap;
K3DTexture* pLightmapTex = NULL;
if(m_bEnableLightmap)
pLightmapTex = getLightmapTex(m_nSegmentX, m_nSegmentY);
m_pTerrainPrimitive->SetLightMapTex(pLightmapTex);
THREAD_SYNCRONIZE( &m_PropLock );
for( TERRAINPROP_VECTOR::iterator itProp = m_PropVector.begin(); itProp != m_PropVector.end(); itProp++ )
(*itProp)->RefreshLightmap(m_bEnableLightmap);
}