#include "stdafx.h"

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

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

#ifdef _DEBUG
	#include <crtdbg.h>
#endif //_DEBUG

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768

static const Mix::Vector2 TEXT_PADDING( 16.0f, 16.0f );

static const Mix::Vector3 SOUL_EMIT_RANGE( 10.0f, 4.0f, 10.0f );
static const Float32 SOUL_EMIT_OFFSET_Y = 1.0f;
static const Float32 SOUL_EMIT_INTERVAL = 0.5f;
static const UInt32 SOUL_EMIT_NUM = 40;

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif //_DEBUG

	Mix::ENGINE_CONFIG engineConfig;

	engineConfig.flags = Mix::EC_IO_NARROW | Mix::EC_KEYBOARD | Mix::EC_MOUSE | Mix::EC_GRAPHICS | Mix::EC_SCENE;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Scene\\Widget\\Particle\\Leaving" );
	engineConfig.pCaption = L"Scene - Widget ( Leaving Particle )";
	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::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Scene::IManager* pSceneMgr = Mix::Scene::GetManagerPtr();

		Mix::HID::IMouse* pMouse = NULL;
		Mix::HID::IKeyboard* pKeyboard = NULL;

		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;
		Mix::Graphics::Utility::IFont* pFont = NULL;

		Mix::Scene::IEffectPackage* pSceneEffectPackage = NULL;
		Mix::Scene::IRenderer* pSceneRenderer = NULL;
		Mix::Scene::IViewCamera* pCamera = NULL;

		Mix::Scene::IMaterial* pFireMaterial = NULL;
		Mix::Scene::ILeavingParticle* pFireParticle = NULL;
		Mix::Scene::IBillboardParticleSystem* pFireParticleSystem = NULL;

		Mix::Scene::IMaterial* pSmokeMaterial = NULL;
		Mix::Scene::ILeavingParticle* pSmokeParticle = NULL;
		Mix::Scene::IBillboardParticleSystem* pSmokeParticleSystem = NULL;

		Utility::ViewCameraHelper viewCamHelper;

		Mix::StringW tempStr;

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

		pIOMgr->MountDirectory( L"Data" );

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

		pInputMgr->GetMouse( &pMouse );
		pInputMgr->GetKeyboard( &pKeyboard );

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

		pGraphicsMgr->GetDevice( &pGraphicsDev );

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

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

		if( pSceneMgr->CreateEffectPackage( L"Data\\", L"effects", &pSceneEffectPackage ) == True )
		{
			Mix::Scene::RENDERER_CONFIG config;

			config.caps = Mix::Scene::RCAP_WIDGET | Mix::Scene::RCAP_BLOOM | Mix::Scene::RCAP_LUMINOSITY;

			if( pSceneMgr->CreateRenderer( pSceneEffectPackage, config, &pSceneRenderer, L"Sample" ) == True )
			{
				Mix::Scene::CAMERA_CONFIG camConfig;
				Mix::Scene::IDirectionalLight* pDirLight = NULL;
				Mix::Scene::IPointLight* pPointLight = NULL;
				Mix::Scene::ITerrainModel* pLandModel = NULL;
				Mix::Scene::IActorModel* pWoodModel = NULL;

				/*
					カメラ
				*/

				camConfig.caps = config.caps;
				camConfig.targetSize.Set( SCREEN_WIDTH, SCREEN_HEIGHT );
//				camConfig.colorTexFormat = Mix::Graphics::FMT_R8G8B8A8;
				camConfig.colorTexFormat = Mix::Graphics::FMT_R16G16B16A16F;
//				camConfig.colorTexFormat = Mix::Graphics::FMT_R32G32B32A32F;

				if( pSceneMgr->CreateViewCamera( camConfig, &pCamera ) == True )
				{
					Mix::Scene::ICamera::LUMINOSITY_SETTINGS lumSettings = pCamera->GetLuminositySettings();

					pCamera->SetBackgroundColor( Mix::Vector4( 0.02f, 0.02f, 0.03f, 1.0f ) );
					pCamera->SetProjection( MIX_TO_RAD( 60.0f ), 0.01f, 1000.0f );

					pCamera->SetBloomEnabled( True );

					lumSettings.flags = Mix::Scene::ICamera::LUM_TONE_MAPPING;
					pCamera->SetLuminositySettings( lumSettings );

					pSceneRenderer->AddCamera( pCamera );
				}

				/*
					モデル
				*/

				if( pSceneMgr->CreateTerrainModelFromFile( pSceneEffectPackage, L"Data\\Model\\bonefire\\land.mtm", &pLandModel ) == True )
				{
					pSceneRenderer->AddTerrainModel( pLandModel );

					MIX_RELEASE( pLandModel );
				}

				if( pSceneMgr->CreateActorModelFromFile( pSceneEffectPackage, L"Data\\Model\\bonefire\\woodPile.mam", &pWoodModel ) == True )
				{
					pWoodModel->Update();
					pWoodModel->Refresh();

					pSceneRenderer->AddActorModel( pWoodModel );

					MIX_RELEASE( pWoodModel );
				}

				/*
					ディレクショナルライト
				*/

				if( pSceneMgr->CreateDirectionalLight( &pDirLight ) == True )
				{
					pDirLight->SetEnabled( True );
					pDirLight->SetDirection( Mix::Vector3( +0.7f, -0.4f, 0.6f ).ToNormalize() );
					pDirLight->SetColor( Mix::Vector4( 1.0f, 0.9f, 0.7f, 0.04f ) );

					pSceneRenderer->SetDirectionalLight( pDirLight );

					MIX_RELEASE( pDirLight );
				}

				/*
					ポイントライト( 炎 )
				*/

				if( pSceneMgr->CreatePointLight( &pPointLight ) == True )
				{
					pPointLight->SetPosition( Mix::Vector3( 0.0f, 0.1f, 0.0f ) );
					pPointLight->SetRadius( 0.5f, 4.0f );
					pPointLight->SetColor( Mix::Vector4( 1.0f, 0.6f, 0.3f, 0.3f ) );

					pSceneRenderer->AddPointLight( pPointLight );

					MIX_RELEASE( pPointLight );
				}

				/*
					炎パーティクル
				*/

				if( pSceneMgr->CreateMaterialFromFile( pSceneEffectPackage, L"Data\\Material\\Fire.mtl", &pFireMaterial ) == True )
				{
					Mix::Scene::IDefaultMaterial* pDefMaterial = static_cast<Mix::Scene::IDefaultMaterial*>( pFireMaterial );
					pDefMaterial->SetEmissiveColor( Mix::Vector4( 0.08f, 0.08f, 0.08f, 0.0f ) );
				}

				if( pSceneMgr->CreateBillboardParticleSystem( False, &pFireParticleSystem, MIX_DEBUGNAME ) == True )
				{
					const Float32 FIRE_BRIGHTNESS = 1.0f;
					const Float32 FIRE_OPACITY = 1.0f;
					const Float32 FIRE_COLOR_AMOUNT = 0.8f;

					const Mix::Vector4 FIRE_COLOR0( 0.0f,            0.0f,            FIRE_BRIGHTNESS, 0.8f * FIRE_OPACITY );
					const Mix::Vector4 FIRE_COLOR1( FIRE_BRIGHTNESS, FIRE_BRIGHTNESS, 0.0f,            0.4f * FIRE_OPACITY );
					const Mix::Vector4 FIRE_COLOR2( FIRE_BRIGHTNESS, 0.0f,            0.0f,            0.0f * FIRE_OPACITY );

					pFireParticleSystem->SetEmitRadius( Mix::Vector3( 0.2f, 0.2f, 0.2f ) );
					pFireParticleSystem->SetEmitOffset( 0.0f );
					pFireParticleSystem->SetEmitInterval( 0.1f, 0.15f );
					pFireParticleSystem->SetEmitCount( 2, 4 );

					pFireParticleSystem->SetLife( 0.4f, 0.7f );

					pFireParticleSystem->SetMass( 0.8f, 1.0f );

					pFireParticleSystem->SetGravity( Mix::Vector3( 0.0f, 0.0f, 0.0f ) );

					pFireParticleSystem->SetConstantLinearVelocity( Mix::Vector3( 0.0f, 0.0f, 0.0f ) );
					pFireParticleSystem->SetLinearImpulse( Mix::Vector3( -0.5f, +1.0f, -0.5f ), Mix::Vector3( +0.5f, +1.5f, +0.5f ) );
					pFireParticleSystem->SetLinearAcceleration( Mix::Vector3( 0.0f, 0.0f, 0.0f ), Mix::Vector3( 0.0f, 0.0f, 0.0f ) );
					pFireParticleSystem->SetLinearVelocityDamping( 0.0f, 0.0f );

					pFireParticleSystem->SetAngularImpulse( -4.0f, +4.0f );
					pFireParticleSystem->SetAngularAcceleration( 0.0f, 0.0f );
					pFireParticleSystem->SetAngularVelocityDamping( 0.0f, 0.0f );

					pFireParticleSystem->SetSize( Mix::Vector2( 0.6f, 0.6f ), Mix::Vector2( 0.1f, 0.1f ), 0.5f, 1.0f );

					pFireParticleSystem->SetColor( FIRE_COLOR0, FIRE_COLOR1, FIRE_COLOR2 );
					pFireParticleSystem->SetColorControlPoints( 0.2f, 0.4f );
				}

				if( pSceneMgr->CreateLeavingParticle( pFireMaterial, pFireParticleSystem, &pFireParticle, MIX_DEBUGNAME ) == True )
				{
					Mix::Matrix4x4 mat;

					mat.SetTranslation( 0.0f, 0.2f, 0.0f );
					pFireParticle->SetWorldMatrix( mat );
					pFireParticle->Play( True, pEngine->GetBaseDT(), pEngine->GetBaseFPS() * 1 );

					pSceneRenderer->AddLeavingParticle( pFireParticle );
				}

				/*
					煙パーティクル
				*/

				if( pSceneMgr->CreateMaterialFromFile( pSceneEffectPackage, L"Data\\Material\\Smoke.mtl", &pSmokeMaterial ) == True )
				{
					Mix::Scene::IDefaultMaterial* pDefMaterial = static_cast<Mix::Scene::IDefaultMaterial*>( pSmokeMaterial );
					pDefMaterial->SetBlendType( Mix::Graphics::BLEND_NORMAL );
				}

				if( pSceneMgr->CreateBillboardParticleSystem( False, &pSmokeParticleSystem, MIX_DEBUGNAME ) == True )
				{
					static const Float32 SMOKE_BRIGHTNESS0 = 0.2f;
					static const Float32 SMOKE_BRIGHTNESS1 = 0.4f;
					static const Float32 SMOKE_BRIGHTNESS2 = 0.6f;
					static const Float32 SMOKE_BRIGHTNESS_AMOUNT = 0.5f;

					static const Float32 SMOKE_OPACITY0 = 0.4f;
					static const Float32 SMOKE_OPACITY1 = 0.2f;
					static const Float32 SMOKE_OPACITY2 = 0.0f;
					static const Float32 SMOKE_OPACITY_AMOUNT = 0.5f;

					static const Mix::Vector4 SMOKE_COLOR0( SMOKE_BRIGHTNESS0, SMOKE_BRIGHTNESS0, SMOKE_BRIGHTNESS0, SMOKE_OPACITY0 );
					static const Mix::Vector4 SMOKE_COLOR1( SMOKE_BRIGHTNESS1, SMOKE_BRIGHTNESS1, SMOKE_BRIGHTNESS1, SMOKE_OPACITY1 );
					static const Mix::Vector4 SMOKE_COLOR2( SMOKE_BRIGHTNESS2, SMOKE_BRIGHTNESS2, SMOKE_BRIGHTNESS2, SMOKE_OPACITY2 );

					pSmokeParticleSystem->SetEmitRadius( Mix::Vector3( 0.4f, 0.4f, 0.4f ) );
					pSmokeParticleSystem->SetEmitOffset( 0.0f );
					pSmokeParticleSystem->SetEmitInterval( 0.2f, 0.3f );
					pSmokeParticleSystem->SetEmitCount( 1, 2 );

					pSmokeParticleSystem->SetLife( 4.5f, 5.0f );

					pSmokeParticleSystem->SetMass( 0.8f, 0.9f );

					pSmokeParticleSystem->SetGravity( Mix::Vector3( 0.0f, 0.0f, 0.0f ) );

					pSmokeParticleSystem->SetConstantLinearVelocity( Mix::Vector4( 0.1f, 0.0f, 0.1f ) );

					pSmokeParticleSystem->SetLinearImpulse( Mix::Vector3( -0.2f, +0.8f, -0.2f ), Mix::Vector3( +0.2f, +1.0f, +0.2f ) );
					pSmokeParticleSystem->SetLinearAcceleration( Mix::Vector3( 0.0f, 0.0f, 0.0f ), Mix::Vector3( 0.0f, 0.0f, 0.0f ) );
					pSmokeParticleSystem->SetLinearVelocityDamping( 0.0f, 0.002f );

					pSmokeParticleSystem->SetAngularImpulse( -0.2f, +0.2f );
					pSmokeParticleSystem->SetAngularAcceleration( 0.0f, 0.0f );
					pSmokeParticleSystem->SetAngularVelocityDamping( 0.0f, 0.0f );

					pSmokeParticleSystem->SetSize( Mix::Vector2( 0.3f, 0.3f ), Mix::Vector2( 2.0f, 2.0f ), 0.8f, 1.0f );

					pSmokeParticleSystem->SetColor( SMOKE_COLOR0, SMOKE_COLOR1, SMOKE_COLOR2 );
					pSmokeParticleSystem->SetColorControlPoints( 0.3f, 0.4f );
				}

				if( pSceneMgr->CreateLeavingParticle( pSmokeMaterial, pSmokeParticleSystem, &pSmokeParticle, MIX_DEBUGNAME ) == True )
				{
					Mix::Matrix4x4 mat;

					mat.SetTranslation( 0.0f, 0.4f, 0.0f );
					pSmokeParticle->SetWorldMatrix( mat );
					pSmokeParticle->Play( True, pEngine->GetBaseDT(), pEngine->GetBaseFPS() * 3 );

					pSceneRenderer->AddLeavingParticle( pSmokeParticle );
				}

#ifdef _DEBUG
				Mix::Graphics::Utility::IPerspectiveRenderer* pPersRenderer = NULL;

				if( pGraphicsMgr->CreatePerspectiveRenderer( &pPersRenderer, MIX_DEBUGNAME ) == True )
				{
					pSceneRenderer->Debug_SetPerspectiveRenderer( pPersRenderer );
					MIX_RELEASE( pPersRenderer );
				}
#endif //_DEBUG
			}
		}

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

		viewCamHelper.Initialize(	pMouse,
									pKeyboard,
									Mix::Vector3( 0.0f, 0.5f, 0.0f ),
									Mix::Vector2( MIX_TO_RAD( 10.0f ), MIX_TO_RAD( 10.0f ) ),
									7.0f );

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

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			Float32 dt = pEngine->GetDT();

#ifdef _DEBUG

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// デバッグ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pKeyboard->GetKeyState( Mix::HID::KEY_D ) & Mix::HID::PRESSED )
			{
				UInt32 debDrawFlags = pCamera->Debug_GetDrawFlags();

				if( MIX_TESTBIT( debDrawFlags, Mix::Scene::DDF_PARTICLE ) == 0 )
				{
					debDrawFlags |= Mix::Scene::DDF_PARTICLE;
				}
				else
				{
					debDrawFlags ^= Mix::Scene::DDF_PARTICLE;
				}

				pCamera->Debug_SetDrawFlags( debDrawFlags );
			}

#endif //_DEBUG

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// カメラ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			viewCamHelper.Update();

			if( pCamera != NULL )
			{
				pCamera->SetRotation( viewCamHelper.GetRotation() );
				pCamera->SetAt( viewCamHelper.GetAt() );
				pCamera->SetDistance( viewCamHelper.GetDistance() );
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// キャンバス
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pCanvasRenderer != NULL )
			{
				Mix::Vector2 txtSize;

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

				tempStr.Sprintf( L"FPS %f", pEngine->GetFPS() );
				pCanvasRenderer->AddString( TEXT_PADDING, tempStr.GetConstPtr() );
				tempStr.Sprintf( L"STps %f", pEngine->GetSleepTimePerSec() );
				pCanvasRenderer->AddString( TEXT_PADDING + Mix::Vector2( 0.0f, 16.0f ), tempStr.GetConstPtr() );
				tempStr.Sprintf( L"IFCps %d/%d", pEngine->GetInsomniaFrameCountPerSec(), pEngine->GetInsomniaFrameMax() );
				pCanvasRenderer->AddString( TEXT_PADDING + Mix::Vector2( 0.0f, 32.0f ), tempStr.GetConstPtr() );

#ifdef _DEBUG
				tempStr.Sprintf( L"%s", L"Controls :\n"
										L"  Rotate camera     : Mouse middle button\n"
										L"  Move camera       : Mouse middle button + Shift key\n"
										L"  Distant camera    : Mouse middle button + Ctrl key or Mouse wheel\n"
										L"  Toggle debug draw : D key" );
#else //_DEBUG
				tempStr.Sprintf( L"%s", L"Controls :\n"
										L"  Rotate camera  : Mouse middle button\n"
										L"  Move camera    : Mouse middle button + Shift key\n"
										L"  Distant camera : Mouse middle button + Ctrl key or Mouse wheel\n" );
#endif //_DEBUG

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

				Utility::DrawMouseCursor( pCanvasRenderer, pMouse->GetPos() );
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 更新
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pFireParticle != NULL )
			{
				pFireParticle->Update( dt );
			}

			if( pSmokeParticle != NULL )
			{
				pSmokeParticle->Update( dt );
			}

			if( pCamera != NULL )
			{
				pCamera->Update();
			}

			if( pSceneRenderer != NULL )
			{
				pSceneRenderer->Update( dt, pEngine->GetBaseDT() );
			}

			if( pCanvasRenderer != NULL )
			{
				pCanvasRenderer->Update();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// リフレッシュ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pFireParticle != NULL )
			{
				pFireParticle->Refresh();
			}

			if( pSmokeParticle != NULL )
			{
				pSmokeParticle->Refresh();
			}

			if( pSceneRenderer != NULL )
			{
				pSceneRenderer->Refresh();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 描画
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );

			if( pGraphicsDev->Begin() == True )
			{
				if( pCamera != NULL )
				{
					pCamera->Draw();
				}

				if( pCanvasRenderer != NULL )
				{
					pCanvasRenderer->Draw();
				}

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

		viewCamHelper.Dispose();

		MIX_RELEASE( pSmokeParticle );
		MIX_RELEASE( pSmokeParticleSystem );
		MIX_RELEASE( pSmokeMaterial );

		MIX_RELEASE( pFireParticle );
		MIX_RELEASE( pFireParticleSystem );
		MIX_RELEASE( pFireMaterial );

		MIX_RELEASE( pCamera );
		MIX_RELEASE( pSceneRenderer );
		MIX_RELEASE( pSceneEffectPackage );
		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pKeyboard );
		MIX_RELEASE( pMouse );

		Mix::Finalize();
	}

	return 0;
}
