Files
Leviathan/Client/Game/game/Main/SGameCameraAnimator.cpp
2026-06-01 12:46:52 +02:00

661 lines
18 KiB
C++

#include "stdafx.h"
#include "SGameCameraAnimator.h"
#include "SGameWorld.h"
#include "TerrainMapEngine.h"
#include "SGameLowQualityWater.h"
#include "KViewport.h"
//=======================================================================================
//
// SGameCameraTerrainHeightAnimator
//
SGameCameraTerrainHeightAnimator::SGameCameraTerrainHeightAnimator
( CTerrainMapEngine& terrain
, SGameLowQualityWater& water )
: m_terrain ( terrain )
, m_water ( water )
{
}
SGameCameraTerrainHeightAnimator::~SGameCameraTerrainHeightAnimator()
{
}
void SGameCameraTerrainHeightAnimator::Animate( DWORD time, SGameCamera& camera )
{
if( camera.GetCameraMode() != SGameCamera::CAMERA_GAME_MODE ) return;
// collide with terrain & water
K3DVector vCameraPosition = camera.GetCameraPosition();
K3DVector vProjXYCameraPosition( vCameraPosition.x, vCameraPosition.y, 0.f );
// 땅/물 높이 가져오기
float fTerrainHeight;
float fWaterHeight = -3.0000000e+038f;
//아래 부분을 없애면, 물 속으로 카메라 들어 가짐.
if( m_water.GetWaterHeight( vProjXYCameraPosition, fWaterHeight ) )
fWaterHeight += 5.0f;
float fHeight = 0.0f;
bool bOnProp = false;
WORD wTile = 0;
m_terrain.GetTerrainHeight(
vProjXYCameraPosition.x, vProjXYCameraPosition.y, fTerrainHeight, wTile, &bOnProp );
if ( fTerrainHeight < fWaterHeight )
{
bOnProp = true;
fHeight = fWaterHeight;
}
else
{
bOnProp = false;
fHeight = fTerrainHeight;
}
if ( !bOnProp )
m_terrain.GetTerrainHeight( vProjXYCameraPosition.x, vProjXYCameraPosition.y, fHeight, wTile );
else
fHeight = fWaterHeight;
K3DVector vRelativePos = camera.GetRelativePosition();
K3DVector vProjXYRelativePos( vRelativePos.x, vRelativePos.y, 0.f );
float fProjXYLength = K3DVectorLength( vProjXYRelativePos );
float fBaseHeight = fHeight + ( SGameCamera::CAMERA_ELEVATION_BASE * fProjXYLength );
if( vCameraPosition.z < fBaseHeight )
{
if( camera.IsDistanceChanged() )
camera.Elevate( camera.GetDistanceDelta() * SGameCamera::CAMERA_ROTATION_CONSTANT );
vRelativePos.z += fBaseHeight - vCameraPosition.z;
camera.SetRelativePosition( vRelativePos );
}
}
//=======================================================================================
//
// SGameCameraTerrainCollisionAnimator
//
SGameCameraTerrainCollisionAnimator::SGameCameraTerrainCollisionAnimator
( CTerrainMapEngine& terrain
, SGameLowQualityWater& water )
: m_terrain ( terrain )
, m_water ( water )
, m_vPrevCameraPosition ( 0, 0, 0 )
, m_vPrevTargetPosition ( 0, 0, 0 )
{
}
SGameCameraTerrainCollisionAnimator::~SGameCameraTerrainCollisionAnimator()
{
}
void SGameCameraTerrainCollisionAnimator::Animate( DWORD time, SGameCamera& camera )
{
if( camera.GetCameraMode() != SGameCamera::CAMERA_GAME_MODE ) return;
// collide with terrain & water
CollisionArgument colarg;
if( _HasMoved( camera ) )
{
colarg.rayPosition = camera.GetCameraTargetPosition();
colarg.rayDirection = camera.GetCameraPosition() - camera.GetCameraTargetPosition();
colarg.collidePoint = colarg.rayPosition + colarg.rayDirection;
colarg.collideDirection = colarg.rayDirection;
_ExecuteCollisionResponseWithTerrain( colarg );
// backup collision argument for next step
m_backupCollisionArgument = colarg;
}
else
{
colarg = m_backupCollisionArgument;
}
K3DVector vFound = colarg.collidePoint;
K3DVector vRelFound = colarg.collidePoint - colarg.rayPosition;
K3DVector vProjXYRelPos( vRelFound.x, vRelFound.y, 0.f );
float fProjXYLength = K3DVectorLength( vProjXYRelPos );
if( colarg.found )
{
vFound.z += SGameCamera::CAMERA_ELEVATION_BASE * fProjXYLength;
}
else
{
float fBaseHeight
= _GetTerrainHeight( vFound )
+ ( SGameCamera::CAMERA_ELEVATION_BASE * fProjXYLength );
if( vFound.z < fBaseHeight )
{
if( camera.IsDistanceChanged() )
camera.Elevate( camera.GetDistanceDelta() * SGameCamera::CAMERA_ROTATION_CONSTANT );
vFound.z = fBaseHeight;
}
}
camera.SetRelativePosition( vFound - colarg.rayPosition );
}
bool SGameCameraTerrainCollisionAnimator::_HasMoved( const SGameCamera& camera )
{
K3DVector vCameraPosition = camera.GetCameraPosition();
K3DVector vTargetPosition = camera.GetCameraTargetPosition();
bool bHasMoved = ( m_vPrevCameraPosition != vCameraPosition ||
m_vPrevTargetPosition != vTargetPosition ) ? true : false;
// backup for next process
m_vPrevCameraPosition = vCameraPosition;
m_vPrevTargetPosition = vTargetPosition;
return bHasMoved;
}
float SGameCameraTerrainCollisionAnimator::_GetTerrainHeight( const K3DVector& position )
{
K3DVector vProjXYCameraPosition( position.x, position.y, 0.f );
// 땅/물 높이 가져오기
float fTerrainHeight;
float fWaterHeight = -3.0000000e+038f;
//아래 부분을 없애면, 물 속으로 카메라 들어 가짐.
if( m_water.GetWaterHeight( vProjXYCameraPosition, fWaterHeight ) )
fWaterHeight += 5.0f;
float fHeight = 0.0f;
bool bOnProp = false;
WORD wTile = 0;
m_terrain.GetTerrainHeight(
vProjXYCameraPosition.x, vProjXYCameraPosition.y, fTerrainHeight, wTile, &bOnProp );
if ( fTerrainHeight < fWaterHeight )
{
bOnProp = true;
fHeight = fWaterHeight;
}
else
{
bOnProp = false;
fHeight = fTerrainHeight;
}
if ( !bOnProp )
m_terrain.GetTerrainHeight( vProjXYCameraPosition.x, vProjXYCameraPosition.y, fHeight, wTile );
else
fHeight = fWaterHeight;
return fHeight;
}
void SGameCameraTerrainCollisionAnimator::
_ExecuteCollisionResponseWithTerrain( CollisionArgument& argument )
{
K3DVector targetPos = argument.rayPosition + argument.rayDirection;
K3DCamera camera;
camera.SetTargetPos( targetPos.x, targetPos.y, targetPos.z );
TERRAIN_PICK_RESULT result = m_terrain.GetLineCrossedPoint(
&camera, argument.rayPosition, targetPos, argument.collidePoint );
if( result == TERRAIN_PICK_RESULT::TERRAIN_PICK_SUCCECED ||
result == TERRAIN_PICK_RESULT::TERRAIN_STOOL_PICK_SUCCECED )
{
argument.found = true;
}
else
{
argument.found = false;
}
}
//=======================================================================================
//
// SGameCameraPropCollisionAnimator
//
SGameCameraPropCollisionAnimator::SGameCameraPropCollisionAnimator
( CTerrainMapEngine& terrain
, const K3DVector& vCollisionRadius
, DWORD dwInterpolationTime )
: m_terrain ( terrain )
, m_vCollidableSphereRadius ( vCollisionRadius )
, m_vPrevCameraPosition ( 0, 0, 0 )
, m_vPrevTargetPosition ( 0, 0, 0 )
, m_dwTime ( 0 )
, m_dwInterpolationTime ( dwInterpolationTime )
, m_bInterpolated ( false )
, m_fCurrRadius ( 0 )
, m_fGoalRadius ( 0 )
#ifdef _DEBUG
, m_bDebugRender ( true )
#endif
{
}
SGameCameraPropCollisionAnimator::~SGameCameraPropCollisionAnimator()
{
}
void SGameCameraPropCollisionAnimator::Animate( DWORD time, SGameCamera& camera )
{
if( camera.GetCameraMode() != SGameCamera::CAMERA_GAME_MODE ) return;
m_dwTimeDiff = time - m_dwTime;
m_dwTime = time;
CollisionArgument arg;
if( _HasMoved( camera ) )
{
// prepare argument for responding by collision detection
arg.radius = m_vCollidableSphereRadius;
arg.rayPosition = camera.GetCameraTargetPosition();
arg.rayDirection = camera.GetRelativePosition();
// excute collision response
_ExecuteCollisionResponseWithProp( arg );
_BackupCollisionArgument( arg );
}
else
{
arg = m_backupCollidionArgument;
}
// soften collision response
_SoftenResponse( arg );
// locate camera on responsed position
camera.SetRelativePosition( arg.collidePoint - arg.rayPosition );
}
void SGameCameraPropCollisionAnimator::Render( KViewportObject* pViewport )
{
#ifdef _DEBUG
pViewport->RegisterWire( &m_primitiveWire );
#endif
}
bool SGameCameraPropCollisionAnimator::_HasMoved( SGameCamera& camera )
{
K3DVector vCameraPosition = camera.GetCameraPosition();
K3DVector vTargetPosition = camera.GetCameraTargetPosition();
bool bHasMoved = ( m_vPrevCameraPosition != vCameraPosition ||
m_vPrevTargetPosition != vTargetPosition ) ? true : false;
// backup for next process
m_vPrevCameraPosition = vCameraPosition;
m_vPrevTargetPosition = vTargetPosition;
return bHasMoved;
}
void SGameCameraPropCollisionAnimator::_ExecuteCollisionResponseWithProp(
CollisionArgument& argument )
{
// debug rendering
#ifdef _DEBUG
if( m_bDebugRender )
{
m_primitiveWire.Clear();
m_primitiveWire.AddLine(
argument.rayPosition,
argument.rayPosition + argument.rayDirection * 0.9f,
KColor( 0xffff0000 ) );
m_primitiveWire.AddLine(
argument.rayPosition + argument.rayDirection * 0.9f,
argument.rayPosition + argument.rayDirection,
KColor( 0xffffffff ) );
}
#endif
// detect collision point
if( m_terrain.GetPropLineCrossedPointTwoSidesWithCollisionMesh
( argument.rayPosition
, argument.rayPosition + argument.rayDirection
, argument.nearestT
, argument.collidePoint
#ifdef _DEBUG
, argument.foundPolygon
#endif
) )
{
argument.found = true;
argument.collideDirection = argument.nearestT * argument.rayDirection;
#ifdef _DEBUG
if( m_bDebugRender )
{
m_primitiveWire.AddLine(
argument.foundPolygon[ 0 ], argument.foundPolygon[ 1 ], KColor( 0xffffffff ) );
m_primitiveWire.AddLine(
argument.foundPolygon[ 1 ], argument.foundPolygon[ 2 ], KColor( 0xffffffff ) );
m_primitiveWire.AddLine(
argument.foundPolygon[ 2 ], argument.foundPolygon[ 0 ], KColor( 0xffffffff ) );
}
#endif
}
else
{
argument.found = false;
argument.collidePoint = argument.rayPosition + argument.rayDirection;
argument.collideDirection = argument.rayDirection;
}
}
void SGameCameraPropCollisionAnimator::_SoftenResponse( CollisionArgument& argument )
{
m_fGoalRadius = K3DVectorLength( argument.rayDirection );
m_fCurrRadius = _GetInterpolatedRadius();
if( argument.found )
{
float fCollidedRadius = K3DVectorLength( argument.collideDirection );
if( m_fCurrRadius > fCollidedRadius )
m_fCurrRadius = fCollidedRadius;
m_bInterpolated = true;
}
else
{
if( m_fCurrRadius > m_fGoalRadius )
{
m_fCurrRadius = m_fGoalRadius;
m_bInterpolated = false;
}
}
K3DVector vInterpolatedPosition = argument.rayDirection;
vInterpolatedPosition.Normalize() *= m_fCurrRadius;
argument.collidePoint = argument.rayPosition + vInterpolatedPosition;
}
float SGameCameraPropCollisionAnimator::_GetInterpolatedRadius()
{
return _HasInterpolated()
? m_fCurrRadius + m_fGoalRadius * ( (float)m_dwTimeDiff / m_dwInterpolationTime )
: m_fGoalRadius;
}
//=======================================================================================
//
// SGameCameraTerrainAndPropCollisionAnimator
//
SGameCameraTerrainAndPropCollisionAnimator::SGameCameraTerrainAndPropCollisionAnimator
( CTerrainMapEngine& terrain
, SGameLowQualityWater& water
, const K3DVector& vCollisionRadius
, DWORD dwInterpolationTime )
: m_terrain ( terrain )
, m_water ( water )
, m_vCollidableSphereRadius ( vCollisionRadius )
, m_vPrevCameraPosition ( 0, 0, 0 )
, m_vPrevTargetPosition ( 0, 0, 0 )
, m_dwTime ( 0 )
, m_dwInterpolationTime ( dwInterpolationTime )
, m_bInterpolated ( false )
, m_fCurrRadius ( 0 )
, m_fGoalRadius ( 0 )
#ifdef _DEBUG
, m_bDebugRender ( true )
#endif
{
}
SGameCameraTerrainAndPropCollisionAnimator::~SGameCameraTerrainAndPropCollisionAnimator()
{
}
void SGameCameraTerrainAndPropCollisionAnimator::Animate( DWORD time, SGameCamera& camera )
{
if( camera.GetCameraMode() != SGameCamera::CAMERA_GAME_MODE ) return;
m_dwTimeDiff = time - m_dwTime;
m_dwTime = time;
CollisionArgument arg;
if( _HasMoved( camera ) )
{
// prepare argument for responding by collision detection
arg.radius = m_vCollidableSphereRadius;
arg.rayPosition = camera.GetCameraTargetPosition();
arg.rayDirection = camera.GetRelativePosition();
_ExecuteCollisionResponseWithWater( arg, camera );
// excute collision response
_ExecuteCollisionResponseWithTerrain( arg );
// adjust ray direction
arg.rayDirection = arg.collideDirection;
arg.nearestT = 1.0f; // { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
_ExecuteCollisionResponseWithProp( arg );
// backup collision result
m_backupCollidionArgument = arg;
}
else
{
arg = m_backupCollidionArgument;
}
m_fGoalRadius = K3DVectorLength( camera.GetRelativePosition() );
m_fCurrRadius = _GetInterpolatedRadius();
// soften collision response
_SoftenResponse( arg );
// locate camera on responsed position
camera.SetRelativePosition( arg.collidePoint - arg.rayPosition );
// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
#ifdef _DEBUG
if( m_bDebugRender )
{
m_primitiveWire.Clear();
m_primitiveWire.AddLine( arg.rayPosition, arg.rayPosition + arg.rayDirection * 0.9f, KColor( 0xffff0000 ) );
m_primitiveWire.AddLine( arg.rayPosition + arg.rayDirection * 0.9f, arg.rayPosition + arg.rayDirection, KColor( 0xffffffff ) );
m_primitiveWire.AddSphere( arg.collidePoint, arg.radius.x, KColor( 0xffffffff ), 32 );
if( arg.found )
{
m_primitiveWire.AddLine( arg.foundPolygon[ 0 ], arg.foundPolygon[ 1 ], KColor( 0xffffffff ) );
m_primitiveWire.AddLine( arg.foundPolygon[ 1 ], arg.foundPolygon[ 2 ], KColor( 0xffffffff ) );
m_primitiveWire.AddLine( arg.foundPolygon[ 2 ], arg.foundPolygon[ 0 ], KColor( 0xffffffff ) );
}
if( !arg.debugLines.empty( ) )
{
CollisionArgument::DEBUG_LINE_CONTAINER::iterator it = arg.debugLines.begin( ), itend = arg.debugLines.end( );
for( ; it != itend; )
{
CollisionArgument::DEBUG_LINE_CONTAINER::reference lineBegin = *it++;
CollisionArgument::DEBUG_LINE_CONTAINER::reference lineEnd = *it++;
m_primitiveWire.AddLine( lineBegin.first, lineEnd.first, lineBegin.second );
}
}
}
#endif
// }
}
void SGameCameraTerrainAndPropCollisionAnimator::Render( KViewportObject* pViewport )
{
#ifdef _DEBUG
pViewport->RegisterWire( &m_primitiveWire );
#endif
}
// { sonador 7.1.9 성능 개선
void SGameCameraTerrainAndPropCollisionAnimator::SetRadius( const K3DVector& vRadius )
{
m_vCollidableSphereRadius.Set( vRadius.x, vRadius.y, vRadius.z );
}
// }
bool SGameCameraTerrainAndPropCollisionAnimator::_HasMoved( SGameCamera& camera )
{
K3DVector vCameraPosition = camera.GetCameraPosition();
K3DVector vTargetPosition = camera.GetCameraTargetPosition();
bool bHasMoved = ( m_vPrevCameraPosition != vCameraPosition ||
m_vPrevTargetPosition != vTargetPosition ) ? true : false;
// backup for next process
m_vPrevCameraPosition = vCameraPosition;
m_vPrevTargetPosition = vTargetPosition;
return bHasMoved;
}
void SGameCameraTerrainAndPropCollisionAnimator::_ExecuteCollisionResponseWithWater( CollisionArgument& argument, SGameCamera& camera )
{
// pick water
/*if( m_water.GetEllipsoidCrossedPoint(
argument.rayPosition
, argument.rayDirection
, argument.radius
, argument.nearestT
, vWaterCandidate
#ifdef _DEBUG
, argument.foundPolygon
#endif
) )
{
if( argument.nearestT > 0.0f )
argument.found = true;
else
argument.nearestT = 1.0f;
}*/
float fWaterHeight = -3.0000000e+038f;
K3DVector vCamera = argument.rayPosition + argument.rayDirection;
K3DVector vProjXYCamera( vCamera.x, vCamera.y, 0.0f );
//아래 부분을 없애면, 물 속으로 카메라 들어 가짐.
if( m_water.GetWaterHeight( vProjXYCamera, fWaterHeight ) )
fWaterHeight += argument.radius.z;
// 카메라가 물 위에 있다.
if( fWaterHeight > vCamera.z )
{
argument.found = true;
vCamera.z = fWaterHeight;
// record collision results
argument.collidePoint = vCamera;
argument.collideDirection = vCamera - argument.rayPosition;
// reset collision argument
argument.rayDirection = argument.collideDirection;
argument.nearestT = 1.0f;
// reset camera && deactive interpolation
camera.SetRelativePosition( argument.collideDirection );
m_bInterpolated = false;
}
}
void SGameCameraTerrainAndPropCollisionAnimator::_ExecuteCollisionResponseWithTerrain( CollisionArgument& argument )
{
// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
K3DVector vTerrainCandidate( 0, 0, 0 );
// pick terrain
if( m_terrain.GetTerrainEllipsoidCrossedPoint(
argument.rayPosition
, argument.rayDirection
, argument.radius
, argument.nearestT
, vTerrainCandidate
#ifdef _DEBUG
, argument.foundPolygon
, argument.debugLines
#endif
) )
{
argument.found = true;
if( argument.nearestT > 0.001f )
{
argument.collideDirection = argument.rayDirection * argument.nearestT;
}
else
{
argument.collideDirection = argument.rayDirection * 0.001f;
}
argument.collidePoint = argument.rayPosition + argument.collideDirection;
}
else
{
argument.collideDirection = argument.rayDirection;
argument.collidePoint = argument.rayPosition + argument.rayDirection;
}
// }
}
void SGameCameraTerrainAndPropCollisionAnimator::_ExecuteCollisionResponseWithProp( CollisionArgument& argument )
{
// { [sonador][7.1.7]충돌 처리 개선(Swept Shpere Collision Detection)
// detect collision point
K3DVector vCollidePoint;
if( m_terrain.GetPropEllipsoidCrossedPointTwoSides
( argument.rayPosition
, argument.rayPosition + argument.rayDirection
, argument.radius
, argument.nearestT
, vCollidePoint
#ifdef _DEBUG
, argument.foundPolygon
#endif
) )
{
argument.found = true;
if( argument.nearestT > 0.001f )
{
argument.collideDirection = argument.rayDirection * argument.nearestT;
}
else
{
argument.collideDirection = argument.rayDirection * 0.001f;
}
argument.collidePoint = argument.rayPosition + argument.collideDirection;
}
// }
}
void SGameCameraTerrainAndPropCollisionAnimator::_SoftenResponse( CollisionArgument& argument )
{
if( argument.found )
{
float fCollidedRadius = K3DVectorLength( argument.collideDirection );
if( m_fCurrRadius > fCollidedRadius )
m_fCurrRadius = fCollidedRadius;
m_bInterpolated = true;
}
else
{
if( m_fCurrRadius > m_fGoalRadius )
{
m_fCurrRadius = m_fGoalRadius;
m_bInterpolated = false;
}
}
K3DVector vInterpolatedPosition = argument.collideDirection;
if( !vInterpolatedPosition.IsZero( ) )
{
vInterpolatedPosition.Normalize() *= m_fCurrRadius;
}
argument.collidePoint = argument.rayPosition + vInterpolatedPosition;
}
float SGameCameraTerrainAndPropCollisionAnimator::_GetInterpolatedRadius()
{
return _HasInterpolated()
? m_fCurrRadius + m_fGoalRadius * ( (float)m_dwTimeDiff / m_dwInterpolationTime )
: m_fGoalRadius;
}