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