#include "stdafx.h"
#include "CDirect3DNative.h"
#include "DrawTexture.h"

namespace FDK
{
	// static メンバの実体。

	HWND CDirect3DNative::hWindow;
	Atom<bool> CDirect3DNative::b全画面モード;
	bool CDirect3DNative::b垂直帰線同期を待つ;
	SIZE CDirect3DNative::sz描画サイズ原寸px;
	SIZE CDirect3DNative::sz表示サイズpx;
	IDirect3D9Ex *CDirect3DNative::pDirect3D9Ex;
	UINT CDirect3DNative::nDeviceResetToken;
	IDirect3DDeviceManager9 *CDirect3DNative::pD3DDeviceManager9;
	IDirect3DDevice9Ex *CDirect3DNative::pD3DDevice9Ex;
	D3DDISPLAYMODE *CDirect3DNative::pDisplayMode;
	DWORD CDirect3DNative::dwデバイスを作成したスレッドのID;
	D3DVIEWPORT9 CDirect3DNative::viewPort;
	UINT CDirect3DNative::uアダプタID = 0;

	// 移行、t初期化() を除いてすべてスレッドセーフ。（デバイスは必ずロックが必要。その他のDirect3Dオブジェクトは、マルチスレッドでデバイスを作成する前提なので自動排他。）

	void CDirect3DNative::t初期化( SIZE sz描画サイズ原寸px )
	{
		HRESULT hr = S_OK;

		CDirect3DNative::hWindow = NULL;
		CDirect3DNative::b全画面モード = false;
		CDirect3DNative::b垂直帰線同期を待つ = true;
		CDirect3DNative::sz描画サイズ原寸px = sz描画サイズ原寸px;
		CDirect3DNative::sz表示サイズpx = sz描画サイズ原寸px;
		CDirect3DNative::dwデバイスを作成したスレッドのID = 0xFFFFFFFF;
		
		CDirect3DNative::pDirect3D9Ex = NULL;
		CDirect3DNative::pD3DDeviceManager9 = NULL;
		CDirect3DNative::nDeviceResetToken = 0;
		CDirect3DNative::pD3DDevice9Ex = NULL;

		try
		{
			#pragma region [ Direct3D9Ex の作成 ]
			//-----------------
			{
				IDirect3D9Ex *pD3D;
				tFAILEDなら例外発生( L"Direct3D9Ex の作成",
					hr = ::Direct3DCreate9Ex( D3D_SDK_VERSION, &pD3D ) );
				
				CDirect3DNative::pDirect3D9Ex = pD3D;
			}
			//-----------------
			#pragma endregion

			#pragma region [ Direct3DDeviceManager9 の作成 ]
			//-----------------
			{
				UINT token;
				IDirect3DDeviceManager9 *pMan;

				tFAILEDなら例外発生( L"D3Dデバイスマネージャの作成",
					hr = ::DXVA2CreateDirect3DDeviceManager9( &token, &pMan ) );

				CDirect3DNative::pD3DDeviceManager9 = pMan;
				CDirect3DNative::nDeviceResetToken = token;
			}
			//-----------------
			#pragma endregion

			#pragma region [ Direct3DDevice9Ex の作成…… は行わない。アプリから tデバイスを作成する() を呼び出すこと。]
			//-----------------
			CDirect3DNative::pD3DDevice9Ex = NULL;
			//-----------------
			#pragma endregion

			#pragma region [ pDisplayMode の初期化 ]
			//-----------------
			{
				D3DDISPLAYMODE *pMode = new D3DDISPLAYMODE();
				::ZeroMemory( pMode, sizeof( D3DDISPLAYMODE ) );
				CDirect3DNative::pDisplayMode = pMode;
			}
			//-----------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_COM_RELEASE( CDirect3DNative::pD3DDevice9Ex );
			SAFE_COM_RELEASE( CDirect3DNative::pD3DDeviceManager9 );
			SAFE_COM_RELEASE( CDirect3DNative::pDirect3D9Ex );

			throw gcnew Exception( L"CDirect3Dクラスの初期化に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::t終了()
	{
		SAFE_DELETE( CDirect3DNative::pDisplayMode );
		SAFE_COM_RELEASE( CDirect3DNative::pD3DDevice9Ex );
		SAFE_COM_RELEASE( CDirect3DNative::pD3DDeviceManager9 );
		SAFE_COM_RELEASE( CDirect3DNative::pDirect3D9Ex );
	}

	void CDirect3DNative::tデバイスを作成する( HWND hWindow, bool b全画面モード, int nWidth, int nHeight, bool b垂直帰線同期を待つ )
	{
		Debug::Assert( NULL != hWindow );
		Debug::Assert( NULL != CDirect3DNative::pDirect3D9Ex );
		Debug::Assert( NULL != CDirect3DNative::pD3DDeviceManager9 );
		Debug::Assert( 0xFFFFFFFF == CDirect3DNative::dwデバイスを作成したスレッドのID || CDirect3DNative::dwデバイスを作成したスレッドのID == ::GetCurrentThreadId() );

		HRESULT hr = S_OK;

		D3DPRESENT_PARAMETERS pp;
		D3DDISPLAYMODEEX displayModeEx;
		IDirect3DDevice9Ex *p新デバイス = NULL;

		try
		{
			// パラメータを生成・保存。

			CDirect3DNative::b全画面モード = b全画面モード;
			CDirect3DNative::b垂直帰線同期を待つ = b垂直帰線同期を待つ;
			CDirect3DNative::hWindow = hWindow;
			CDirect3DNative::sz表示サイズpx.cx = nWidth;
			CDirect3DNative::sz表示サイズpx.cy = nHeight;

			CDirect3DNative::uアダプタID = CDirect3DNative::tモニタに対応するアダプタを取得する( CDirect3DNative::pDirect3D9Ex, ::MonitorFromWindow( CDirect3DNative::hWindow, MONITOR_DEFAULTTONEAREST ) );

			#pragma region [ 新デバイスを作成する。]
			//-----------------
			CDirect3DNative::tデバイス作成パラメータを取得する( b全画面モード, nWidth, nHeight, &pp, &displayModeEx );

			hr = CDirect3DNative::pDirect3D9Ex->CreateDeviceEx(
					CDirect3DNative::uアダプタID,
					D3DDEVTYPE_HAL,
					pp.hDeviceWindow,
					//                          ↓ MULTITHREADED は、MediaFoundation を使い場合は必ず指定。
					D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE | D3DCREATE_HARDWARE_VERTEXPROCESSING,
					&pp,
					( CDirect3DNative::b全画面モード.Get() ) ? &displayModeEx : NULL,
					&p新デバイス );

			tFAILEDなら例外発生( L"Direct3Dデバイスの作成", hr );
			//-----------------
			#pragma endregion

			#pragma region [ ビューポートを取得。]
			//-----------------
			D3DVIEWPORT9 view;
			tFAILEDなら例外発生( L"デバイスのビューポートの取得", hr = p新デバイス->GetViewport( &view ) );
			CDirect3DNative::viewPort = view;
			//-----------------
			#pragma endregion

			#pragma region [ 物理縮小率を計算し、DrawTextureに格納。]
			//-----------------
			RECT rc;
			::GetClientRect( hWindow, &rc );
			DrawTexture::f物理縮小倍率 = (float) ( rc.right - rc.left ) / (float) CDirect3DNative::sz描画サイズ原寸px.cx;
			//-----------------
			#pragma endregion

			#pragma region [ デフォルトのレンダリングステータスを設定する。]
			//-----------------
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(Lightning)",
				hr = p新デバイス->SetRenderState( D3DRS_LIGHTING, FALSE ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(ZEnable)",
				hr = p新デバイス->SetRenderState( D3DRS_ZENABLE, FALSE ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(AntiAliasedLineEnable)",
				hr = p新デバイス->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, FALSE ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(AlphaTestEnable)",
				hr = p新デバイス->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(MultiSampleAntiAlias)",
				hr = p新デバイス->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, FALSE ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(SrcBlend)",
				hr = p新デバイス->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ) );
			
			tFAILEDなら例外発生( L"デフォルトのレンダリングステータスの設定(InvSrcAlpha)",
				hr = p新デバイス->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ) );
			//-----------------
			#pragma endregion
			
			#pragma region [ フレーム遅延を１に固定。]
			//-------------------------
			p新デバイス->SetMaximumFrameLatency( 1 );
			//-------------------------
			#pragma endregion

			#pragma region [ 新デバイスをデバイスマネージャに登録し、既存のデバイスを破棄する。]
			//-----------------
			CDirect3DNative::tデバイスをデバイスマネージャーに登録する( p新デバイス );

			// 既存のデバイスを破棄。
			SAFE_COM_RELEASE( CDirect3DNative::pD3DDevice9Ex );

			// 新デバイスを保存。
			CDirect3DNative::pD3DDevice9Ex = p新デバイス;
			CDirect3DNative::pD3DDevice9Ex->AddRef();
			p新デバイス->Release();
			p新デバイス = NULL;

			// 作成したスレッドのIDを保存。
			CDirect3DNative::dwデバイスを作成したスレッドのID = ::GetCurrentThreadId( );
			//-----------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_COM_RELEASE( p新デバイス );
			SAFE_DELETE( CDirect3DNative::pDisplayMode );
			SAFE_COM_RELEASE( CDirect3DNative::pDirect3D9Ex );

			throw gcnew Exception( L"Direct3D9 デバイスの作成または登録に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::tデバイスを再構築する()
	{
		try
		{
			// 以前のパラメータを使って再作成。

			CDirect3DNative::tデバイスを作成する( CDirect3DNative::hWindow, CDirect3DNative::b全画面モード.Get(), CDirect3DNative::sz表示サイズpx.cx, CDirect3DNative::sz表示サイズpx.cy, CDirect3DNative::b垂直帰線同期を待つ );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスの再構築に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::tデバイス作成パラメータを取得する( _In_ bool b全画面モード, _In_ int nWidth, _In_ int nHeight, _Out_ D3DPRESENT_PARAMETERS* pPP, _Out_ D3DDISPLAYMODEEX* pDM )
	{
		HRESULT hr = D3D_OK;

		CDirect3DNative::GetDirect3DDisplayMode();		// CDirect3DNative::pDisplayMode を設定。


		// D3DPRESENT_PARAMETERS *pp の作成

		if( CDirect3DNative::b全画面モード.Get() )
		{
			#pragma region [ (a) 全画面モード ]
			//-------------------------
			::ZeroMemory( pPP, sizeof( D3DPRESENT_PARAMETERS ) );
			pPP->AutoDepthStencilFormat = D3DFORMAT::D3DFMT_UNKNOWN;
			pPP->BackBufferCount = 1;
			pPP->BackBufferFormat = CDirect3DNative::pDisplayMode->Format;					// 常にデスクトップフォーマットに合わせる。
			pPP->BackBufferWidth = nWidth;
			pPP->BackBufferHeight = nHeight;
			pPP->hDeviceWindow = CDirect3DNative::hWindow;
			pPP->Flags = D3DPRESENTFLAG_VIDEO | D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;			// GUI を使う場合、LockableBackbuffer でなければならない。
			pPP->FullScreen_RefreshRateInHz = CDirect3DNative::pDisplayMode->RefreshRate;
			pPP->PresentationInterval = ( CDirect3DNative::b垂直帰線同期を待つ ) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
			pPP->MultiSampleQuality = 0;
			pPP->MultiSampleType = D3DMULTISAMPLE_NONE;										// GUI を使う場合、None でなければならない。
			pPP->SwapEffect = D3DSWAPEFFECT_DISCARD;										// GUI を使う場合、Discard でなければならない。
			pPP->Windowed = false;
			//-------------------------
			#pragma endregion
		}
		else
		{
			#pragma region [ (b) ウィンドウモード ]
			//-------------------------
			::ZeroMemory( pPP, sizeof( D3DPRESENT_PARAMETERS ) );
			pPP->AutoDepthStencilFormat = D3DFORMAT::D3DFMT_UNKNOWN;
			pPP->BackBufferCount = 1;
			pPP->BackBufferFormat = CDirect3DNative::pDisplayMode->Format;					// 常にデスクトップフォーマットに合わせる。
			pPP->BackBufferWidth = nWidth;
			pPP->BackBufferHeight = nHeight;
			pPP->hDeviceWindow = CDirect3DNative::hWindow;
			pPP->Flags = D3DPRESENTFLAG_VIDEO | D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;			// GUI を使う場合、LockableBackbuffer でなければならない。
			pPP->FullScreen_RefreshRateInHz = 0;											// Windowモードでは必ず 0 を指定する。
			pPP->PresentationInterval = ( CDirect3DNative::b垂直帰線同期を待つ ) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
			pPP->MultiSampleQuality = 0;
			pPP->MultiSampleType = D3DMULTISAMPLE_NONE;										// GUI を使う場合、None でなければならない。
			pPP->SwapEffect = D3DSWAPEFFECT_DISCARD;										// GUI を使う場合、Discard でなければならない。
			pPP->Windowed = true;
			//-------------------------
			#pragma endregion
		}


		// D3DISPLAYMODEEX *pDM の作成

		::ZeroMemory( pDM, sizeof( D3DDISPLAYMODEEX ) );
		pDM->Size = sizeof( D3DDISPLAYMODEEX );
		pDM->Format = pPP->BackBufferFormat;
		pDM->Width = pPP->BackBufferWidth;
		pDM->Height = pPP->BackBufferHeight;
		pDM->RefreshRate = pPP->FullScreen_RefreshRateInHz;
		pDM->ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;
	}

	void CDirect3DNative::tデバイスをロックする( bool bBlock, [Out] HANDLE *phDevice, [Out] IDirect3DDevice9Ex **ppDeviceEx )
	{
		Debug::Assert( NULL != CDirect3DNative::pD3DDeviceManager9 );
		Debug::Assert( NULL != phDevice );
		//Debug::Assert( NULL != ppDeviceEx );	NULL OK

		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;
		IDirect3DDevice9 *pDevice = NULL;
		
		try
		{
			tFAILEDなら例外発生( L"デバイスハンドルのオープン",
				hr = CDirect3DNative::pD3DDeviceManager9->OpenDeviceHandle( &hDevice ) );					// ハンドルを開いて、

			hr = CDirect3DNative::pD3DDeviceManager9->LockDevice( hDevice, &pDevice, bBlock );				// デバイスをロックする。先にメソッドロックを行っているため、ここでブロックされることはないだろう。

			if( hr == DXVA2_E_NEW_VIDEO_DEVICE )															// もしデバイスが無効だったら、
			{
				tFAILEDなら例外発生( L"デバイスハンドルのクローズ",
					hr = CDirect3DNative::pD3DDeviceManager9->CloseDeviceHandle( hDevice ) );				// いったんハンドルを閉じて
				hDevice = NULL;

				tFAILEDなら例外発生( L"デバイスハンドルの再オープン",
					hr = CDirect3DNative::pD3DDeviceManager9->OpenDeviceHandle( &hDevice ) );				// 開き直し、

				tFAILEDなら例外発生( L"デバイスのロックの取得",
					hr = CDirect3DNative::pD3DDeviceManager9->LockDevice( hDevice, &pDevice, bBlock ) );	// もう一度デバイスをロックする。
			}
			else
			{
				tFAILEDなら例外発生( L"デバイスのロックの取得", hr );										// 無効以外のエラーは失敗。
			}

			
			// ハンドルとポインタを戻り値として返す。
			
			*phDevice = hDevice;
			if( ppDeviceEx != NULL )
				*ppDeviceEx = CDirect3DNative::GetDirect3DDevice9Ex();

		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			if( hDevice != NULL )
			{
				#pragma region [ オープン中ならクローズする。]
				//-----------------
				try
				{
					CDirect3DNative::pD3DDeviceManager9->CloseDeviceHandle( hDevice );
				}
				catch( Exception^ )
				{
					// ここでのエラーは無視。
				}
				//-----------------
				#pragma endregion
			}

			throw gcnew Exception( L"デバイスのロックに失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::tデバイスのロックを解除する( HANDLE &hLockedDevice )
	{
		Debug::Assert( NULL != hLockedDevice );
		Debug::Assert( NULL != CDirect3DNative::pD3DDeviceManager9 );

		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"デバイスのロックの解放",
				hr = CDirect3DNative::pD3DDeviceManager9->UnlockDevice( hLockedDevice, TRUE ) );

			tFAILEDなら例外発生( L"デバイスハンドルのクローズ",
				hr = CDirect3DNative::pD3DDeviceManager9->CloseDeviceHandle( hLockedDevice ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスのロックの解除に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			hLockedDevice = NULL;
		}
	}

	HRESULT CDirect3DNative::tデバイスの状態を取得する()
	{
		Debug::Assert( NULL != CDirect3DNative::hWindow );
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, NULL );
			hr = CDirect3DNative::tデバイスの状態を取得する( hDevice );	// hr が FAILED でも意味があるので、例外は発生させない。
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスの状態の取得に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}

		return hr;
	}
	HRESULT CDirect3DNative::tデバイスの状態を取得する( HANDLE hLockedDevice )
	{
		Debug::Assert( NULL != hLockedDevice );
		Debug::Assert( NULL != CDirect3DNative::hWindow );
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		// hr が FAILED でも意味があるので、例外は発生させない。
		return CDirect3DNative::pD3DDevice9Ex->CheckDeviceState( CDirect3DNative::hWindow );
	}

	void CDirect3DNative::tデバイスをクリアする()
	{
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, NULL );
			CDirect3DNative::tデバイスをクリアする( hDevice );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスのクリアに失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::tデバイスをクリアする( HANDLE hLockedDevice )
	{
		Debug::Assert( NULL != hLockedDevice );
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"デバイスのクリア",
				hr = CDirect3DNative::pD3DDevice9Ex->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB( 255, 0, 0, 0 ), 0.0f, 0 ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスのクリア（ロック済み）に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

	void CDirect3DNative::tデバイスをリセットする()
	{
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, NULL );
			CDirect3DNative::tデバイスをリセットする( hDevice );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスのリセットに失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::tデバイスをリセットする( HANDLE hLockedDevice )
	{
		Debug::Assert( NULL != hLockedDevice );
		Debug::Assert( NULL != CDirect3DNative::pD3DDevice9Ex );

		HRESULT hr = D3D_OK;

		try
		{
			D3DPRESENT_PARAMETERS pp;
			D3DDISPLAYMODEEX dm;

			CDirect3DNative::tデバイス作成パラメータを取得する( CDirect3DNative::b全画面モード.Get(), CDirect3DNative::sz表示サイズpx.cx, CDirect3DNative::sz表示サイズpx.cy, &pp, &dm );

			tFAILEDなら例外発生( L"デバイスのリセット",
				hr = CDirect3DNative::pD3DDevice9Ex->ResetEx( &pp, ( CDirect3DNative::b全画面モード.Get() ) ? &dm : NULL ) );

			#pragma region [ 物理縮小率を計算し、DrawTextureに格納。]
			//-----------------
			RECT rc;
			::GetClientRect( hWindow, &rc );
			DrawTexture::f物理縮小倍率 = (float) ( rc.right - rc.left ) / (float) CDirect3DNative::sz描画サイズ原寸px.cx;
			//-----------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"デバイスのリセット（ロック済み）に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

	List<DisplayMode>^ CDirect3DNative::tフォーマットに対応するディスプレイモードを取得する( HWND hWindow, D3DFormat format )
	{
		Debug::Assert( hWindow != NULL );

		HRESULT hr = S_OK;
		HMONITOR hMonitor = NULL;
		List<DisplayMode>^ displayModes = nullptr;

		try
		{
			#pragma region [ ウィンドウに最も近いデフォルトモニタのハンドルを取得する。]
			//-----------------
			// エラーの場合の戻り値は不明（MSDNに記載なし）。
			hMonitor = ::MonitorFromWindow( hWindow, MONITOR_DEFAULTTONEAREST );
			//-----------------
			#pragma endregion
			#pragma region [ 取得したモニタに対応するアダプタIDを取得する。]
			//-----------------
			UINT uADID = CDirect3DNative::tモニタに対応するアダプタを取得する( CDirect3DNative::pDirect3D9Ex, hMonitor );
			//-----------------
			#pragma endregion
			
			#pragma region [ アダプタとフォーマットから、利用可能なモードを列挙する。]
			//-----------------
			int nMode数 = CDirect3DNative::pDirect3D9Ex->GetAdapterModeCount( uADID, static_cast<D3DFORMAT>( format ) );
			if( nMode数 == 0 )
				throw gcnew Exception( L"アダプタのモード数として 0 が返されました。" );

			displayModes = gcnew List<DisplayMode>( nMode数 );

			for( int i = 0; i < nMode数; i++ )
			{
				D3DDISPLAYMODE d3dmode;

				if( CDirect3DNative::pDirect3D9Ex->EnumAdapterModes( uADID, static_cast<D3DFORMAT>( format ), i, &d3dmode ) != D3D_OK )
					continue;	// 単に使えないモードと見なしてこのモードはスキップする。

				DisplayMode mode;
				mode.Width = d3dmode.Width;
				mode.Height = d3dmode.Height;
				mode.Format = format;
				mode.RefreshRate = d3dmode.RefreshRate;

				displayModes->Add( mode );
			}
			//-----------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_REF_DELETE( displayModes );

			throw gcnew Exception( L"フォーマットに対応するディスプレイモードの取得に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}

		return displayModes;
	}

	void CDirect3DNative::tデバイスをデバイスマネージャーに登録する()
	{
		CDirect3DNative::tデバイスをデバイスマネージャーに登録する( CDirect3DNative::pD3DDevice9Ex );
	}
	void CDirect3DNative::tデバイスをデバイスマネージャーに登録する( IDirect3DDevice9Ex* pDeviceEx )
	{
		Debug::Assert( NULL != CDirect3DNative::pD3DDeviceManager9 );

		HRESULT hr = D3D_OK;

		tFAILEDなら例外発生( L"デバイスマネージャへのデバイスの登録",
			hr = CDirect3DNative::pD3DDeviceManager9->ResetDevice( pDeviceEx, CDirect3DNative::nDeviceResetToken ) );
	}

	IDirect3DDeviceManager9 *CDirect3DNative::GetDirect3DDeviceManager()
	{
		// NULL でもエラーにしない。そのまま返す。

		if( CDirect3DNative::pD3DDeviceManager9 != NULL )
			CDirect3DNative::pD3DDeviceManager9->AddRef();

		return CDirect3DNative::pD3DDeviceManager9;
	}
	IDirect3D9Ex *CDirect3DNative::GetDirect3DEx()
	{
		// NULL でもエラーにしない。そのまま返す。

		if( CDirect3DNative::pDirect3D9Ex != NULL )
			CDirect3DNative::pDirect3D9Ex->AddRef();

		return CDirect3DNative::pDirect3D9Ex;
	}
	IDirect3DDevice9Ex *CDirect3DNative::GetDirect3DDevice9Ex()
	{
		// NULL でもエラーにしない。そのまま返す。

		if( CDirect3DNative::pD3DDevice9Ex != NULL )
			CDirect3DNative::pD3DDevice9Ex->AddRef();

		return CDirect3DNative::pD3DDevice9Ex;
	}
	D3DDISPLAYMODE CDirect3DNative::GetDirect3DDisplayMode()
	{
		#pragma region [ 取得がまだなら取得する。]
		//-----------------
		if( CDirect3DNative::pDisplayMode->Width == 0 &&
			CDirect3DNative::pDisplayMode->Height == 0 &&
			CDirect3DNative::pDisplayMode->RefreshRate == 0 &&
			CDirect3DNative::pDisplayMode->Format == 0 )
		{
			HRESULT hr = D3D_OK;

			tFAILEDなら例外発生( L"アダプタのディスプレイモードの取得",
				hr = CDirect3DNative::pDirect3D9Ex->GetAdapterDisplayMode( CDirect3DNative::uアダプタID, CDirect3DNative::pDisplayMode ) );
		}
		//-----------------
		#pragma endregion

		return *( CDirect3DNative::pDisplayMode );
	}
	D3DVIEWPORT9 CDirect3DNative::GetViewPort()
	{
		// ノーチェックリターン。
		return CDirect3DNative::viewPort;
	}

	void CDirect3DNative::SetTransform( TransformState state, Matrix value )
	{
		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;
		IDirect3DDevice9Ex *pDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, &pDevice );

			tFAILEDなら例外発生( L"TransformState の設定",
				hr = pDevice->SetTransform( (D3DTRANSFORMSTATETYPE) state, &Matrix::ToD3DXMATRIX( value ) ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"TransformState の設定に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}
	}

	void CDirect3DNative::SetRenderState( RenderState state, int value )
	{
		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;
		IDirect3DDevice9Ex *pDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, &pDevice );

			tFAILEDなら例外発生( L"RenderState の設定(int)",
				hr = pDevice->SetRenderState( static_cast<D3DRENDERSTATETYPE>( state ), value ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"RenderState の設定に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::SetRenderState( RenderState state, bool value )
	{
		CDirect3DNative::SetRenderState( state, ( value ) ? ( (int) TRUE ) : ( (int) FALSE ) );
	}
	void CDirect3DNative::SetRenderState( RenderState state, float value )
	{
		CDirect3DNative::SetRenderState( state, *( (int*) ( &value ) ) );
	}
	generic< typename T >
	void CDirect3DNative::SetRenderState( RenderState state, T value )
	{
		CDirect3DNative::SetRenderState( state, static_cast<int>( value ) );
	}

	void CDirect3DNative::SetTextureStageState( int stage, TextureStageState state, int value )
	{
		HRESULT hr = S_OK;
		HANDLE hDevice = NULL;
		IDirect3DDevice9Ex *pDevice = NULL;

		try
		{
			CDirect3DNative::tデバイスをロックする( true, &hDevice, &pDevice );
			
			tFAILEDなら例外発生( L"TextureStageState の設定(int)",
				hr = pDevice->SetTextureStageState( stage, static_cast<D3DTEXTURESTAGESTATETYPE>( state ), (DWORD) value ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"テクスチャステージステートの設定に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			#pragma region [ ロック中の場合は解除する。]
			//-----------------
			if( hDevice != NULL )
				CDirect3DNative::tデバイスのロックを解除する( hDevice );
			//-----------------
			#pragma endregion
		}
	}
	void CDirect3DNative::SetTextureStageState( int stage, TextureStageState state, bool value )
	{
		CDirect3DNative::SetTextureStageState( stage, state, ( value ) ? ( (int) TRUE ) : ( (int) FALSE ) );
	}
	void CDirect3DNative::SetTextureStageState( int stage, TextureStageState state, float value )
	{
		CDirect3DNative::SetTextureStageState( stage, state, *( (int*) ( &value ) ) );
	}
	generic< typename T >
	void CDirect3DNative::SetTextureStageState( int stage, TextureStageState state, T value )
	{
		CDirect3DNative::SetTextureStageState( stage, state, static_cast<int>( value ) );
	}

// private:
	UINT CDirect3DNative::tモニタに対応するアダプタを取得する( IDirect3D9Ex *pD3D9, HMONITOR hMonitor )
	{
		Debug::Assert( NULL != pD3D9 );
		Debug::Assert( NULL != hMonitor );


		// hMonitor に対応するアダプタインデックス uアダプタID を見つける。

		UINT nAdapter数 = pD3D9->GetAdapterCount();

		for( UINT i = 0; i < nAdapter数; i++ )
		{
			HMONITOR hMonitorTmp = pD3D9->GetAdapterMonitor( i );

			if( hMonitorTmp == NULL )
				continue;	// 無視する。

			if( hMonitorTmp == hMonitor )
				return i;	// 見つけた。
		}

		// 見つからなかった。

		throw gcnew Exception( L"モニタに対応するアダプタの取得に失敗しました。" );
	}
}