Files
2026-06-01 12:46:52 +02:00

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) );
}
};