398 lines
13 KiB
C++
398 lines
13 KiB
C++
#pragma once
|
|
|
|
#include "K3DPCCP.h"
|
|
#include "KRenderDevice.h"
|
|
#include <kfile/KStream.h>
|
|
#include "MapDefine.h"
|
|
|
|
#include "k3dpccp.h"
|
|
#include "K3dTypes.h"
|
|
|
|
// 지형 데이터 관련
|
|
|
|
const K3DVector c_DefaultNormal = K3DVector( 0.f, 0.f, 1.f );
|
|
const int c_nAttrCountPerTile = 8;
|
|
const int c_nEventAreaPerTile = 8;
|
|
const K3DLight c_DefaultLight = K3DLight
|
|
( K3DColor( 1.f, 1.f, 1.f )
|
|
, K3DColor( 1.f, 1.f, 1.f )
|
|
, K3DColor( 0.38f, 0.38f, 0.38f )
|
|
, K3DVector( 0.333333f, 0.666666f, -0.666666f ) );
|
|
|
|
//enum TERRAIN_FILTERING
|
|
//{
|
|
// FILTERING_NONE = 0,
|
|
// FILTERING_3TEXTURE = 1,
|
|
// FILTERING_3TEXSHADOW = 2,
|
|
//};
|
|
|
|
enum TERRAIN_RENDERMODE
|
|
{
|
|
TERRAIN_RENDERMODE_DEFAULT = 0,
|
|
TERRAIN_RENDERMODE_MULTIPASS,
|
|
};
|
|
|
|
enum TERRAIN_LOD_LEVEL
|
|
{
|
|
TERRAIN_LOD_LEVEL_1 = 0,
|
|
TERRAIN_LOD_LEVEL_2,
|
|
TERRAIN_LOD_LEVEL_MAX,
|
|
};
|
|
|
|
enum TERRAIN_LOD_CRACK
|
|
{
|
|
TERRAIN_LOD_CRACK_LEFT = 0,
|
|
TERRAIN_LOD_CRACK_TOP,
|
|
TERRAIN_LOD_CRACK_RIGHT,
|
|
TERRAIN_LOD_CRACK_BOTTOM,
|
|
TERRAIN_LOD_CRACK_MAX,
|
|
};
|
|
|
|
enum TERRAIN_LOD_CRACK_EX
|
|
{
|
|
TERRAIN_LOD_CRACK_TOP_LEFT = 0,
|
|
TERRAIN_LOD_CRACK_BOTTOM_RIGHT,
|
|
TERRAIN_LOD_CRACK_EX_MAX,
|
|
};
|
|
|
|
enum TERRAIN_COMMON_INDEXBUFFER
|
|
{
|
|
TERRAIN_COMMON_IB = 0,
|
|
TERRAIN_COMMON_IB_LOD,
|
|
TERRAIN_COMMON_IB_CRACK,
|
|
TERRAIN_COMMON_IB_MAX,
|
|
};
|
|
|
|
const int c_nSegmentLodTileCount = 2;
|
|
const int c_nCrackBySegmentIndex[TERRAIN_LOD_CRACK_MAX][2] =
|
|
{
|
|
{ 0,-1}, ///< left
|
|
{ -1, 0}, ///< top
|
|
{ 0, 1}, ///< right
|
|
{ 1, 0}, ///< bottom
|
|
};
|
|
|
|
class CTerrainInfo
|
|
{
|
|
public:
|
|
virtual WORD GetTile( int nX, int nY ) const = 0;
|
|
virtual WORD GetFillBit( int nStage, int nX, int nY ) const = 0;
|
|
virtual float GetHeight( int nX, int nY ) const = 0;
|
|
virtual unsigned __int64 GetAttrData( int nX, int nY ) const = 0;
|
|
virtual KTripleColor GetColor( int nX, int nY ) const = 0;
|
|
virtual K3DVertex GetVertex( int nX, int nY ) const = 0;
|
|
virtual K3DVector GetVertexNormalVector( int nX, int nY ) const = 0;
|
|
virtual BYTE GetVertexCoverageFactor( int nStage, int nX, int nY ) const = 0;
|
|
virtual bool GetAttribute( int nAttrX, int nAttrY ) const = 0;
|
|
virtual float GetZ( float x, float y ) const = 0;
|
|
|
|
public:
|
|
static K3DVector BuildNormalVector( K3DVertex v1, K3DVertex v2, K3DVertex v3 )
|
|
{
|
|
float x = float((v2.z - v1.z) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.z - v1.z));
|
|
float y = float((v2.x - v1.x) * (v3.z - v1.z) - (v2.z - v1.z) * (v3.x - v1.x));
|
|
float z = float((v2.y - v1.y) * (v3.x - v1.x) - (v2.x - v1.x) * (v3.y - v1.y));
|
|
float fLength = float(sqrt( (x * x) + (y * y) + (z * z) ));
|
|
|
|
return (fLength != 0.f) ? K3DVector( x / fLength, y / fLength, z / fLength ) : c_DefaultNormal;
|
|
}
|
|
};
|
|
|
|
class CEditableTerrainInfo : public CTerrainInfo
|
|
{
|
|
public:
|
|
virtual void SetTile( int nStage, int nX, int nY, WORD wTileIndex ) = 0;
|
|
virtual void SetFillBit( int nStage, int nX, int nY, DWORD wFillBits ) = 0;
|
|
virtual void AddHeightData( int nX, int nY, float fAdd ) = 0;
|
|
virtual void SetHeightData( int nX, int nY, float fHeight ) = 0;
|
|
virtual void SetAttrData( int nX, int nY, unsigned __int64 wAttribute ) = 0;
|
|
virtual void SetColor( int nX, int nY, const KTripleColor& rColor ) = 0;
|
|
virtual void SetAttribute( int nAttrX, int nAttrY, bool bBlock ) = 0;
|
|
virtual void SetEventArea( int nX, int nY, bool bBlock ) = 0;
|
|
};
|
|
|
|
class CTerrainUtil
|
|
{
|
|
public:
|
|
/*
|
|
공간에 놓인 사각형과 선분의 교점을 구한다.
|
|
|
|
<const K3DVertex* pFourVertices>
|
|
동일평면 상에 놓인 사각형을 이루는 4개의 버텍스. 순서는 다음과 같다.
|
|
|
|
^
|
|
| +-------+
|
|
| |2 |3
|
|
| | |
|
|
| | |
|
|
| +-------+
|
|
| 0 1
|
|
+--------------->
|
|
|
|
0->1->2의 삼각형과 1->3->2의 삼각형 2개를 각각 체크한다.
|
|
*/
|
|
static float GetLineCrossedPointTwoSides( const K3DVertex* pFourVertices, const K3DVector& rNear, const K3DVector& rFar, K3DVector& rIntersect )
|
|
{
|
|
float fT = 1.0f, fSmallestT = 1.0f;
|
|
K3DVector rTempIntersect, rCandidateIntersect;
|
|
|
|
fT = KPCCP::triangle_collide_edge_twosides(pFourVertices[0], pFourVertices[1], pFourVertices[2], rNear, rFar, rTempIntersect);
|
|
if(fSmallestT > fT)
|
|
{
|
|
fSmallestT = fT;
|
|
rCandidateIntersect = rTempIntersect;
|
|
}
|
|
|
|
fT = KPCCP::triangle_collide_edge_twosides(pFourVertices[1], pFourVertices[3], pFourVertices[2], rNear, rFar, rTempIntersect);
|
|
if(fSmallestT > fT)
|
|
{
|
|
fSmallestT = fT;
|
|
rCandidateIntersect = rTempIntersect;
|
|
}
|
|
|
|
if(fSmallestT != 1.0f)
|
|
{
|
|
rIntersect = rCandidateIntersect;
|
|
}
|
|
|
|
return fSmallestT;
|
|
}
|
|
|
|
static bool GetLineCrossedPoint( const K3DVertex* pFourVertices, const K3DVector& rNear, const K3DVector& rFar, K3DVector& rIntersect )
|
|
{
|
|
K3DPlane plane;
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[0], pFourVertices[1], pFourVertices[2], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[0], pFourVertices[1], pFourVertices[2] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[1], pFourVertices[3], pFourVertices[2], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[1], pFourVertices[3], pFourVertices[2] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// 0->1->2의 삼각형과 1->3->2의 삼각형 2개를 각각 양면 모두 체크한다.
|
|
static bool GetLineCrossedPointBothFaces( const K3DVertex* pFourVertices, const K3DVector& rNear, const K3DVector& rFar, K3DVector& rIntersect )
|
|
{
|
|
K3DPlane plane;
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[0], pFourVertices[1], pFourVertices[2], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[0], pFourVertices[1], pFourVertices[2] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[1], pFourVertices[3], pFourVertices[2], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[1], pFourVertices[3], pFourVertices[2] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[2], pFourVertices[1], pFourVertices[0], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[2], pFourVertices[1], pFourVertices[0] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
if( KPCCP::triangle_collide_edge( pFourVertices[2], pFourVertices[3], pFourVertices[1], rNear, rFar ) )
|
|
{
|
|
if( K3DPlaneFromPoints( plane, pFourVertices[2], pFourVertices[3], pFourVertices[1] ) )
|
|
if( K3DPlaneIntersectLine( rIntersect, plane, rNear, rFar ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
|
|
static bool GetEllipsoidCrossedPoint( const K3DVector* pFourVertices, const K3DVector& rBase, const K3DVector& rVelocity, float& fNearestT, K3DVector& rIntersect
|
|
#ifdef _DEBUG
|
|
, K3DVector* pCollidedPolygon
|
|
#endif
|
|
)
|
|
{
|
|
// all vectors passed by argument must be in ellipsoid vector space, so ellipsoid must be unit sphere
|
|
bool bFound = false;
|
|
float fT = 1.0f;
|
|
K3DVector vIntersectionCandidate;
|
|
|
|
if( KPCCP::triangle_collide_swept_unit_sphere( pFourVertices[ 0 ], pFourVertices[ 1 ], pFourVertices[ 2 ], rBase, rVelocity, vIntersectionCandidate, fT ) && ( fNearestT > fT ) )
|
|
{
|
|
bFound = true;
|
|
fNearestT = fT;
|
|
rIntersect = vIntersectionCandidate;
|
|
#ifdef _DEBUG
|
|
pCollidedPolygon[ 0 ] = pFourVertices[ 0 ];
|
|
pCollidedPolygon[ 1 ] = pFourVertices[ 1 ];
|
|
pCollidedPolygon[ 2 ] = pFourVertices[ 2 ];
|
|
#endif
|
|
}
|
|
if( KPCCP::triangle_collide_swept_unit_sphere( pFourVertices[ 1 ], pFourVertices[ 3 ], pFourVertices[ 2 ], rBase, rVelocity, vIntersectionCandidate, fT ) && ( fNearestT > fT ) )
|
|
{
|
|
bFound = true;
|
|
fNearestT = fT;
|
|
rIntersect = vIntersectionCandidate;
|
|
#ifdef _DEBUG
|
|
pCollidedPolygon[ 0 ] = pFourVertices[ 1 ];
|
|
pCollidedPolygon[ 1 ] = pFourVertices[ 3 ];
|
|
pCollidedPolygon[ 2 ] = pFourVertices[ 2 ];
|
|
#endif
|
|
}
|
|
return bFound;
|
|
}
|
|
// }
|
|
|
|
/// 주어진 점이 삼각형( z값을 무시한 xy평면에 투영된 삼각형 ) 안에 있는지 확인
|
|
static bool IsInside2DPolygon( const K3DVertex& rVtx1, const K3DVertex& rVtx2, const K3DVertex& rVtx3, float x, float y )
|
|
{
|
|
float r1 = (rVtx2.y - rVtx1.y) * (x - rVtx1.x) - (rVtx2.x - rVtx1.x) * (y - rVtx1.y);
|
|
float r2 = (rVtx3.y - rVtx2.y) * (x - rVtx2.x) - (rVtx3.x - rVtx2.x) * (y - rVtx2.y);
|
|
float r3 = (rVtx1.y - rVtx3.y) * (x - rVtx3.x) - (rVtx1.x - rVtx3.x) * (y - rVtx3.y);
|
|
|
|
return (r1 >= 0) ? ((r2 >= 0) && (r3 >= 0)) : ((r2 <= 0) && (r3 <= 0));
|
|
}
|
|
/// 주어진 삼각형 내의 특정 (x, y)점의 z값을 구한다.
|
|
static float GetHeightForPolygon( const K3DVertex& rVtx1, const K3DVertex& rVtx2, const K3DVertex& rVtx3, float x, float y )
|
|
{
|
|
float a = (x - rVtx1.x);
|
|
float b = (rVtx3.y - rVtx1.y);
|
|
float c = (rVtx3.x - rVtx1.x);
|
|
float d = (y - rVtx1.y);
|
|
float e = (rVtx2.z - rVtx1.z);
|
|
float f = (rVtx2.x - rVtx1.x);
|
|
float g = (rVtx2.y - rVtx1.y);
|
|
float h = (rVtx3.z - rVtx1.z);
|
|
float i = (f * b) - (c * g);
|
|
|
|
if (i == 0.f)
|
|
{
|
|
if( f != 0.f ) return (rVtx1.z + a * e / f);
|
|
if( b != 0.f ) return (rVtx1.z + d * e / b);
|
|
if( c != 0.f ) return (rVtx1.z + a * h / c);
|
|
if( g != 0.f ) return (rVtx1.z + d * h / g);
|
|
}
|
|
return (rVtx1.z + ( ((a * b) - (c * d)) * e + ((f * d) - (a * g)) * h ) / i);
|
|
}
|
|
};
|
|
|
|
struct TERRAINMAP_PROPERTIES
|
|
{
|
|
struct COLORING
|
|
{
|
|
K3DLight litPrimary; ///< 지형 라이트
|
|
K3DLight litSecondary; ///< 캐랙터, prop용 라이트
|
|
KColor colorSky; ///< 하늘 색상
|
|
KColor colorFog; ///< 포그 색상
|
|
float fFogNear, fFogFar; ///< 포그 거리 설정
|
|
DWORD dwSkyType; ///< 하늘(구름) 타입
|
|
|
|
COLORING()
|
|
: litPrimary( c_DefaultLight )
|
|
, litSecondary( c_DefaultLight )
|
|
, colorSky( KColor( 204, 222, 248, 255 ) )
|
|
, colorFog( KColor( 204, 222, 248, 255 ) )
|
|
, fFogNear( 1024.f )
|
|
, fFogFar( 4096.f )
|
|
, dwSkyType( 0 ) { }
|
|
|
|
void LoadStream( KStream* pStream )
|
|
{
|
|
KTripleColor color;
|
|
LoadColor( pStream, color ); litPrimary.diffuse = K3DColor( color );
|
|
LoadColor( pStream, color ); litPrimary.ambient = K3DColor( color );
|
|
LoadVector( pStream, litPrimary.direction );
|
|
|
|
LoadColor( pStream, color ); litSecondary.diffuse = K3DColor( color );
|
|
LoadColor( pStream, color ); litSecondary.ambient = K3DColor( color );
|
|
LoadVector( pStream, litSecondary.direction );
|
|
|
|
LoadColor( pStream, color ); colorSky = color;
|
|
LoadColor( pStream, color ); colorFog = color;
|
|
|
|
pStream->Read( &fFogNear, sizeof(fFogNear) );
|
|
pStream->Read( &fFogFar, sizeof(fFogFar) );
|
|
pStream->Read( &dwSkyType, sizeof(dwSkyType) );
|
|
}
|
|
|
|
void SaveStream( KStream* pStream ) const
|
|
{
|
|
KTripleColor color;
|
|
color = (KTripleColor)litPrimary.diffuse; SaveColor( pStream, color );
|
|
color = (KTripleColor)litPrimary.ambient; SaveColor( pStream, color );
|
|
SaveVector( pStream, litPrimary.direction );
|
|
|
|
color = (KTripleColor)litSecondary.diffuse; SaveColor( pStream, color );
|
|
color = (KTripleColor)litSecondary.ambient; SaveColor( pStream, color );
|
|
SaveVector( pStream, litSecondary.direction );
|
|
|
|
SaveColor( pStream, KTripleColor( colorSky ) );
|
|
SaveColor( pStream, KTripleColor( colorFog ) );
|
|
|
|
pStream->Write( &fFogNear, sizeof(fFogNear) );
|
|
pStream->Write( &fFogFar, sizeof(fFogFar) );
|
|
pStream->Write( &dwSkyType, sizeof(dwSkyType) );
|
|
}
|
|
|
|
bool operator!=( const COLORING& r ) const
|
|
{
|
|
return
|
|
( litPrimary.diffuse != r.litPrimary.diffuse
|
|
|| litPrimary.ambient != r.litPrimary.ambient
|
|
|| litPrimary.direction != r.litPrimary.direction
|
|
|| litSecondary.diffuse != r.litSecondary.diffuse
|
|
|| litSecondary.ambient != r.litSecondary.ambient
|
|
|| litSecondary.direction != r.litSecondary.direction
|
|
|| colorSky.color != r.colorSky.color
|
|
|| colorFog.color != r.colorFog.color
|
|
|| fFogNear != r.fFogNear
|
|
|| fFogFar != r.fFogFar
|
|
|| dwSkyType != r.dwSkyType );
|
|
}
|
|
|
|
private:
|
|
static void LoadColor( KStream* pStream, KTripleColor& rColor )
|
|
{
|
|
pStream->Read( &(rColor.r), sizeof(rColor.r) );
|
|
pStream->Read( &(rColor.g), sizeof(rColor.g) );
|
|
pStream->Read( &(rColor.b), sizeof(rColor.b) );
|
|
}
|
|
static void SaveColor( KStream* pStream, const KTripleColor& rColor )
|
|
{
|
|
pStream->Write( &(rColor.r), sizeof(rColor.r) );
|
|
pStream->Write( &(rColor.g), sizeof(rColor.g) );
|
|
pStream->Write( &(rColor.b), sizeof(rColor.b) );
|
|
}
|
|
static void LoadVector( KStream* pStream, K3DVector& rVector )
|
|
{
|
|
pStream->Read( &(rVector.x), sizeof(rVector.x) );
|
|
pStream->Read( &(rVector.y), sizeof(rVector.y) );
|
|
pStream->Read( &(rVector.z), sizeof(rVector.z) );
|
|
}
|
|
static void SaveVector( KStream* pStream, const K3DVector& rVector )
|
|
{
|
|
pStream->Write( &(rVector.x), sizeof(rVector.x) );
|
|
pStream->Write( &(rVector.y), sizeof(rVector.y) );
|
|
pStream->Write( &(rVector.z), sizeof(rVector.z) );
|
|
}
|
|
};
|
|
|
|
COLORING Coloring;
|
|
bool bShowTerrainInGame; ///< 게임에서 지형 표시 여부
|
|
|
|
TERRAINMAP_PROPERTIES() : bShowTerrainInGame( true ) { }
|
|
|
|
void LoadProperties( KStream* pStream )
|
|
{
|
|
Coloring.LoadStream( pStream );
|
|
pStream->Read( &bShowTerrainInGame, sizeof(bShowTerrainInGame) );
|
|
}
|
|
void SaveProperties( KStream* pStream ) const
|
|
{
|
|
Coloring.SaveStream( pStream );
|
|
pStream->Write( &bShowTerrainInGame, sizeof(bShowTerrainInGame) );
|
|
}
|
|
};
|