////////////////////////////////////////////////////////////////////////////////////////////////////
// インクルード
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "Mix/IO.h"
#include "Mix/HID.h"
#include "Mix/Dynamics.h"
#include "Mix/Graphics.h"

#include "Utility/Common.h"
#include "Utility/FollowCameraHelper.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
// 定数
////////////////////////////////////////////////////////////////////////////////////////////////////

#define SCREEN_WIDTH ( 1024 )
#define SCREEN_HEIGHT ( 768 )

static const Float32 KC_HEIGHT = 1.7f;
static const Float32 KC_RADIUS = 0.3f;
static const Float32 KC_STEP_HEIGHT = 0.35f;
static const Float32 KC_SLOPE_LIMIT = MIX_TO_RAD( 45.0f );
static const Mix::Vector3 KC_INIT_POS( 0.0f, 0.85f, 0.0f );
static const Mix::Vector3 KC_TARGET_OFFSET( 0.0f, 0.85f, 0.0f );
static const Mix::Vector3 KC_UP_AXIS( 0.0f, 1.0f, 0.0f );
static const Float32 KC_ROT_COIFFE = MIX_TO_RAD( 4.0f );
static const Float32 KC_MOV_COIFFE = 0.1f;

static const Float32 CAM_ELEV_COIFFE = MIX_TO_RAD( 1.0f );
static const Float32 CAM_ELEV_MIN = MIX_TO_RAD( 1.0f );
static const Float32 CAM_ELEV_MAX = MIX_TO_RAD( 80.0f );

////////////////////////////////////////////////////////////////////////////////////////////////////
// 関数
////////////////////////////////////////////////////////////////////////////////////////////////////

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
	Mix::ENGINE_CONFIG engineConfig;

	engineConfig.flags = Mix::EC_IO_NARROW | Mix::EC_KEYBOARD | Mix::EC_GRAPHICS | Mix::EC_DYNAMICS;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Dynamics\\Controller\\KinematicCharacter" );
	engineConfig.pCaption = L"Dynamics - KinematicCharacter";
	engineConfig.targetSize = Mix::Point( SCREEN_WIDTH, SCREEN_HEIGHT );
	engineConfig.shaderModel = Mix::Graphics::SHADER_MODEL_3;

	if( Mix::Initialize( engineConfig ) == True )
	{
		Mix::IEngine* pEngine = Mix::GetEnginePtr();

		Mix::IO::IManager* pIOMgr = Mix::IO::GetManagerPtr();

		Mix::HID::IManager* pInputMgr = Mix::HID::GetManagerPtr();
		Mix::HID::IKeyboard* pKeyboard = NULL;

		Mix::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();
		Mix::Dynamics::IWorld* pDynamicsWorld = NULL;
		Mix::Dynamics::IKinematicCharacter* pDynamicsKChar = NULL;

		Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::IPerspectiveRenderer* pPersRenderer = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;
		Mix::Graphics::Utility::IFont* pFont = NULL;

		Utility::FollowCameraHelper fwCamHelper;
		Mix::Matrix4x4 viewMat;
		Mix::Matrix4x4 projMat;

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pIOMgr->MountDirectory( L"Data" );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pInputMgr->GetKeyboard( &pKeyboard );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		if( pDynamicsMgr->CreateWorld( Mix::Dynamics::WORLD_CONFIG(), &pDynamicsWorld, L"Sample" ) == True )
		{
			Mix::Dynamics::IStaticPlane* pStaticPlane = NULL;
			Mix::Dynamics::IBoxShape* pBoxShape = NULL;
			Mix::Dynamics::ICompoundShape* pCompShape = NULL;
			Mix::Dynamics::IRigidBody* pRigidBody = NULL;

			/*
				床
			*/

			if( pDynamicsMgr->CreateStaticPlane( Mix::Vector3( 0.0f, 1.0f, 0.0f ), 0.0f, &pStaticPlane, L"Sample" ) == True )
			{
				pStaticPlane->Debug_SetDrawExtent( 100 );
				pDynamicsWorld->AddObject( pStaticPlane );
			}

			/*
				壁
			*/

			static const Float32 WALL_WX = 5.0f;
			static const Float32 WALL_WY = 2.0f;
			static const Float32 WALL_WZ = 0.25f;

			if( pDynamicsMgr->CreateBoxShape( WALL_WX, WALL_WY, WALL_WZ, &pBoxShape ) == True )
			{
				if( pDynamicsMgr->CreateRigidBody( 0.0f, pBoxShape, &pRigidBody ) == True )
				{
					Mix::Quaternion worldRot;
					Mix::Vector3 worldPos;

					worldRot.SetRotationAxis( Mix::Vector3( 0.0f, 1.0f, 0.0f ), MIX_TO_RAD( 45.0f ) );
					worldPos.Set( 7.0f, WALL_WY, 7.0f );

					pRigidBody->SetWorldTransform( worldRot, worldPos );
					pDynamicsWorld->AddObject( pRigidBody );

					MIX_RELEASE( pRigidBody );
				}

				MIX_RELEASE( pBoxShape );
			}

			/*
				段差
			*/

			static const Float32 ST_WX = 1.0f;
			static const Float32 ST_WZ = 0.6f;
			static const Float32 ST_WZ2 = ST_WZ * 2.0f;
			static const Float32 ST_H = 0.15f;
			static const Float32 ST_H2 = ST_H * 2.0f;
			static const Int32 ST_MAX = 10;
			static const Float32 ST_START_Y = ST_H2 * static_cast<Float32>( ST_MAX );

			if( pDynamicsMgr->CreateCompoundShape( &pCompShape ) == True )
			{
				for( Int32 i = 0; i < ST_MAX; i++ )
				{
					Float32 indexF = static_cast<Float32>( i );
					Mix::Vector3 pos;

					pos.x = 0.0f;
					pos.y = ST_START_Y - ST_H2 * indexF;
					pos.z = ST_WZ * indexF;

					if( pDynamicsMgr->CreateBoxShape( ST_WX, ST_H, ST_WZ * indexF, &pBoxShape ) == True )
					{
						pCompShape->AddChild( pBoxShape, Mix::Quaternion::Identity(), pos );
					}

					MIX_RELEASE( pBoxShape );
				}

				if( pDynamicsMgr->CreateRigidBody( 0.0f, pCompShape, &pRigidBody ) == True )
				{
					Mix::Quaternion worldRot;
					Mix::Vector3 worldPos;

					worldRot.SetRotationAxis( Mix::Vector3( 0.0f, 1.0f, 0.0f ), MIX_TO_RAD( 180.0f ) );
					worldPos.Set( 0.0f, 0.0f, 15.0f );

					pRigidBody->SetWorldTransform( worldRot, worldPos );
					pDynamicsWorld->AddObject( pRigidBody );

					MIX_RELEASE( pRigidBody );
				}

				MIX_RELEASE( pCompShape );
			}

			/*
				勾配
			*/

			const Float32 SL_WX = 2.0f;
			const Float32 SL_WZ = 4.0f;
			const Float32 SL_H = 0.5f;

			if( pDynamicsMgr->CreateCompoundShape( &pCompShape ) == True )
			{
				Mix::Quaternion worldRot;
				Mix::Vector3 worldPos;

				if( pDynamicsMgr->CreateBoxShape( SL_WX, SL_H, SL_WZ, &pBoxShape ) == True )
				{
					worldRot.SetRotationAxis( Mix::Vector3( 1.0f, 0.0f, 0.0f ), MIX_TO_RAD( -40.0f ) );
					worldPos.Set( 0.0f, 2.0f, 0.0f );

					pCompShape->AddChild( pBoxShape, worldRot, worldPos );
					MIX_RELEASE( pBoxShape );
				}

				if( pDynamicsMgr->CreateBoxShape( SL_WX, SL_H, SL_WZ, &pBoxShape ) == True )
				{
					worldRot.SetRotationAxis( Mix::Vector3( 1.0f, 0.0f, 0.0f ), MIX_TO_RAD( -50.0f ) );
					worldPos.Set( 0.0f, 7.5f, 5.5f );

					pCompShape->AddChild( pBoxShape, worldRot, worldPos );
					MIX_RELEASE( pBoxShape );
				}

				if( pDynamicsMgr->CreateRigidBody( 0.0f, pCompShape, &pRigidBody ) == True )
				{
					Mix::Quaternion worldRot;
					Mix::Vector3 worldPos;

					worldRot.SetRotationAxis( Mix::Vector3( 0.0f, 1.0f, 0.0f ), MIX_TO_RAD( -45.0f ) );
					worldPos.Set( -10.0f, 0.0f, 15.0f );

					pRigidBody->SetWorldTransform( worldRot, worldPos );
					pRigidBody->UpdateLocalInertia();
					pDynamicsWorld->AddObject( pRigidBody );

					MIX_RELEASE( pRigidBody );
				}

				MIX_RELEASE( pCompShape );
			}

			/*
				キネマティックキャラクター
			*/

			if( pDynamicsMgr->CreateKinematicCharacter( KC_HEIGHT, KC_RADIUS, KC_STEP_HEIGHT, &pDynamicsKChar, L"Sample" ) == True )
			{
				pDynamicsKChar->SetSlopeLimit( KC_SLOPE_LIMIT );
				pDynamicsKChar->SetWorldPosition( KC_INIT_POS );

				pDynamicsWorld->AddObject( pDynamicsKChar );
			}

			MIX_RELEASE( pStaticPlane );
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		pGraphicsMgr->CreatePerspectiveRenderer( &pPersRenderer, L"Sample" );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, L"Sample" ) == True )
		{
			if( pGraphicsMgr->CreateFontFromFile( L"Data\\Font\\mspg_16_400.fnt", &pFont ) == True )
			{
				pCanvasRenderer->SetFont( pFont );
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		Float32 camAspect = static_cast<Float32>( SCREEN_WIDTH ) / static_cast<Float32>( SCREEN_HEIGHT );

		fwCamHelper.Initialize( pDynamicsKChar->GetWorldRotation(), pDynamicsKChar->GetWorldPosition(), -8.0f, MIX_TO_RAD( 30.0f ) );

		Mix::Matrix4x4::PerspectiveFovLH( MIX_TO_RAD( 60.0f ), camAspect, 0.1f, 1000.0f, projMat );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////

			/*
				Rotate
			*/

			if( pKeyboard->GetKeyState( Mix::HID::KEY_LEFT ) & Mix::HID::DOWN )
			{
				Mix::Quaternion rot;

				rot.SetRotationAxis( KC_UP_AXIS, -KC_ROT_COIFFE );
				rot = pDynamicsKChar->GetWorldRotation() * rot;
				rot.Normalize();

				pDynamicsKChar->SetWorldRotation( rot );
			}
			else if( pKeyboard->GetKeyState( Mix::HID::KEY_RIGHT ) & Mix::HID::DOWN )
			{
				Mix::Quaternion rot;

				rot.SetRotationAxis( KC_UP_AXIS, +KC_ROT_COIFFE );
				rot = pDynamicsKChar->GetWorldRotation() * rot;
				rot.Normalize();

				pDynamicsKChar->SetWorldRotation( rot );
			}

			if( pDynamicsKChar->OnGround() == True )
			{
				/*
					Move
				*/

				Mix::Matrix4x4 kcMat = Mix::Matrix4x4( pDynamicsKChar->GetWorldRotation() );
				Mix::Vector3 movVel( 0.0f, 0.0f, 0.0f );

				if( pKeyboard->GetKeyState( Mix::HID::KEY_W ) & Mix::HID::DOWN )
				{
					movVel += kcMat * Mix::Vector3( 0.0f, 0.0f, +KC_MOV_COIFFE );
				}
				else if( pKeyboard->GetKeyState( Mix::HID::KEY_S ) & Mix::HID::DOWN )
				{
					movVel += kcMat * Mix::Vector3( 0.0f, 0.0f, -KC_MOV_COIFFE );
				}

				if( pKeyboard->GetKeyState( Mix::HID::KEY_A ) & Mix::HID::DOWN )
				{
					movVel += kcMat * Mix::Vector3( -KC_MOV_COIFFE, 0.0f, 0.0f );
				}
				else if( pKeyboard->GetKeyState( Mix::HID::KEY_D ) & Mix::HID::DOWN )
				{
					movVel += kcMat * Mix::Vector3( +KC_MOV_COIFFE, 0.0f, 0.0f );
				}

				pDynamicsKChar->SetLinearVelocity( movVel );

				/*
					Jump
				*/

				if( pKeyboard->GetKeyState( Mix::HID::KEY_SPACE ) & Mix::HID::PRESSED )
				{
					pDynamicsKChar->Jump();
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////

			pDynamicsWorld->Update( pEngine->GetDT() );

			////////////////////////////////////////////////////////////////////////////////////////////////////

			Float32 camElev = fwCamHelper.GetElevation();

			if( pKeyboard->GetKeyState( Mix::HID::KEY_UP ) & Mix::HID::DOWN )
			{
				camElev += CAM_ELEV_COIFFE;
			}
			else if( pKeyboard->GetKeyState( Mix::HID::KEY_DOWN ) & Mix::HID::DOWN )
			{
				camElev -= CAM_ELEV_COIFFE;
			}

			fwCamHelper.SetElevation( MIX_CLAMP( camElev, CAM_ELEV_MIN, CAM_ELEV_MAX ) );
			fwCamHelper.Update( pDynamicsKChar->GetWorldRotation(), pDynamicsKChar->GetWorldPosition() );

			Mix::Matrix4x4::LookAtLH( fwCamHelper.GetEye(), fwCamHelper.GetAt(), fwCamHelper.GetUp(), viewMat );

			////////////////////////////////////////////////////////////////////////////////////////////////////

			pDynamicsWorld->Debug_Draw( pPersRenderer );

			pPersRenderer->Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////

			Mix::String tempStr;
			Mix::Vector2 txtSize;
			Mix::Vector2 txtPos;

			/*
				FPS
			*/

			tempStr.Sprintf( L"FPS %f", pEngine->GetFPS() );
			pCanvasRenderer->AddString( 16.0f, 16.0f, tempStr.GetConstPtr() );

			/*
				Desription
			*/

			tempStr.Sprintf( L"%s",	L"Controls :\n"
									L"  Rotate : Left or Right key\n"
									L"  Move   : Up or Down key\n"
									L"  Jump   : Space key" );

			txtSize = Mix::Graphics::Utility::MeasureString( pFont, tempStr.GetConstPtr() );
			txtPos.x = 16.0f;
			txtPos.y = static_cast<Float32>( SCREEN_HEIGHT ) - txtSize.y - 16.0f;
			pCanvasRenderer->AddString( txtPos, tempStr.GetConstPtr() );

			/*
				Update
			*/

			pCanvasRenderer->Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			pGraphicsDev->Clear( True, False, Mix::Vector4( 0.1f, 0.1f, 0.1f, 1.0f ) );

			if( pGraphicsDev->Begin() == True )
			{
				pPersRenderer->Draw( viewMat * projMat );
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pPersRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pDynamicsKChar );
		MIX_RELEASE( pDynamicsWorld );
		MIX_RELEASE( pKeyboard );

		Mix::Finalize();
	}

	return 0;
}
