1578 lines
45 KiB
C++
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);
|
|
|
|
|
|
} |