//******************************************************************************
//
// MIDITrail / MTFirstPersonCam
//
// 一人称カメラクラス
//
// Copyright (C) 2010-2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

// MEMO:
// Windows版のソースを移植しているため、座標は左手系(DirectX)で処理している。
// 左手系(DirectX)=>右手系(OpenGL)への変換は LH2RH マクロで実現する。

#import "YNBaseLib.h"
#import "OGLUtil.h"
#import "MTParam.h"
#import "MTConfFile.h"
#import "MTFirstPersonCam.h"


//******************************************************************************
// コンストラクタ
//******************************************************************************
MTFirstPersonCam::MTFirstPersonCam(void)
{
	//カメラ制御系
	m_CamVector = OGLVECTOR3(0.0f, 0.0f, 0.0f);
	m_CamVectorRightEye = OGLVECTOR3(0.0f, 0.0f, 0.0f);
	m_CamUpVector = OGLVECTOR3(0.0f, 1.0f, 0.0f);
	m_CamDirPhi = 0.0f;
	m_CamDirTheta = 0.0f;
	m_VRViewEyeSeparation = 0.5f;
	m_ProgressDirection = DirX;
	
	//ユーザ操作制御系
	m_IsMouseCamMode = false;
	m_IsAutoRollMode = false;
	m_pView = nil;
	m_pTouchCtrl = NULL;
	m_ScreenType = MTScene::StandardView;
	m_DevDirPhiOffset = 0.0f;
	m_TouchInvertXYAxisFactor = 1.0f;
	
	//移動速度
	m_VelocityFB = 15.0f; // m/sec.
	m_VelocityLR = 15.0f; // m/sec.
	m_VelocityUD = 10.0f; // m/sec.
	m_VelocityPT =  6.0f; // degrees/sec.
	m_AcceleRate =  2.0f; // 加速倍率
	
	//回転制御系
	m_RollAngle = 0.0f;
	m_VelocityAutoRoll = 6.0f;
	m_VelocityManualRoll = 1.0f;

	//時間制御系
	m_PrevTime = 0;
	m_DeltaTime = 0;
	m_PrevTickTime = 0;
	m_CurTickTime = 0;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
MTFirstPersonCam::~MTFirstPersonCam(void)
{
	m_DIKeyCtrl.Terminate();
}

//******************************************************************************
// 初期化処理
//******************************************************************************
int MTFirstPersonCam::Initialize(
		UIView* pView,
		NSString* pSceneName,
		SMSeqData* pSeqData
	)
{
	int result = 0;
	
	m_pView = pView;
	
	//パラメータ設定ファイル読み込み
	result = _LoadConfFile(pSceneName);
	if (result != 0) goto EXIT;
	
	//ユーザ設定読み込み
	result = _LoadUserConf();
	if (result != 0) goto EXIT;
	
	//ノートデザインオブジェクト初期化
	result = m_NoteDesign.Initialize(pSceneName, pSeqData);
	if (result != 0) goto EXIT;
	
	//キーボードデバイス制御初期化
	result = m_DIKeyCtrl.Initialize(pView);
	if (result != 0) goto EXIT;
	
	//デバイスアクセス権取得
	m_DIKeyCtrl.Acquire();
	
	//ゲームパッド初期化：ユーザインデックス0固定
	result = m_GamePadCtrl.Initialize(0);
	if (result != 0) goto EXIT;
	
	//カメラ初期化
	result = m_Camera.Initialize();
	if (result != 0) goto EXIT;
	
	//基本パラメータ設定
	m_Camera.SetBaseParam(
			45.0f,		//画角
			1.0f,		//Nearプレーン：0だとZ軸順制御がおかしくなる
			1000.0f		//Farプレーン
		);
	
	//カメラ位置設定
	m_Camera.SetPosition(
			OGLVECTOR3(0.0f, 0.0f, LH2RH(0.0f)),	//カメラ位置
			OGLVECTOR3(0.0f, 0.0f, LH2RH(1.0f)),	//注目点
			OGLVECTOR3(0.0f, 1.0f, LH2RH(0.0f))		//カメラ上方向
		);
	
	//Mach時間初期化
	result = m_MachTime.Initialize();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// カメラ位置設定
//******************************************************************************
void MTFirstPersonCam::SetPosition(
		OGLVECTOR3 camVector
	)
{
	m_CamVector = camVector;
}

//******************************************************************************
// カメラ方向設定
//******************************************************************************
void MTFirstPersonCam::SetDirection(
		float phi,
		float theta
	)
{
	m_CamDirPhi = phi;
	m_CamDirTheta = theta;
}

//******************************************************************************
// カメラ位置取得
//******************************************************************************
void MTFirstPersonCam::GetPosition(
		OGLVECTOR3* pCamVector
	)
{
	*pCamVector = m_CamVector;
}

//******************************************************************************
// カメラ位置取得：右目用
//******************************************************************************
void MTFirstPersonCam::GetPositionRightEye(
		OGLVECTOR3* pCamVector
	)
{
	*pCamVector = m_CamVectorRightEye;
}

//******************************************************************************
// カメラ方向取得
//******************************************************************************
void MTFirstPersonCam::GetDirection(
		float* pPhi,
		float* pTheta
	)
{
	*pPhi = m_CamDirPhi;
	*pTheta = m_CamDirTheta;
}

//******************************************************************************
// マウス視線移動モード登録
//******************************************************************************
void MTFirstPersonCam::SetMouseCamMode(
		bool isEnable
	)
{
	m_IsMouseCamMode = isEnable;
}

//******************************************************************************
// 自動回転モード登録
//******************************************************************************
void MTFirstPersonCam::SetAutoRollMode(
		bool isEnable
	)
{
	m_IsAutoRollMode = isEnable;
}

//******************************************************************************
// 自動回転方向切り替え
//******************************************************************************
void MTFirstPersonCam::SwitchAutoRllDirecton()
{
	//回転方向を逆にする
	m_VelocityAutoRoll *= -1.0f;
}

//******************************************************************************
// 変換処理
//******************************************************************************
int MTFirstPersonCam::Transform(
		OGLDevice* pOGLDevice,
		bool isActive
	)
{
	int result = 0;
	float dt = 0.0f;
	float dX = 0.0f;
	float dY = 0.0f;
	float dW = 0.0f;
	
	//デルタタイム
	dt = (float)m_DeltaTime / 1000.0f;
	
	//TODO: ここじゃないどこかへ移す
	m_DIKeyCtrl.Acquire();
	
	//アプリケーションのアクティブ状態を設定
	m_DIKeyCtrl.SetActiveState(isActive);
	
	//現在のキーボード状態を取得
	//result = m_DIKeyCtrl.GetKeyStatus();
	//if (result != 0) goto EXIT;
	
	//スワイプ移動距離デルタ値
	//  MIDITrail Ver.1.3.6からタッチ操作による視線の移動方向を逆転させる（一般的なゲーム操作に合わせる）
	if (m_pTouchCtrl != NULL) {
		dX = m_pTouchCtrl->GetDeltaEyeDirX() * (2.0f) * m_TouchInvertXYAxisFactor;
		dY = m_pTouchCtrl->GetDeltaEyeDirY() * (2.0f) * m_TouchInvertXYAxisFactor;
		dW = m_pTouchCtrl->GetDeltaRT() * (-1.0f);
		//NSLog(@"TouchCtrl dX:%f dY:%f dW:%f", dX, dY, dW);
	}
	
	//ゲームパッド状態更新
	result = m_GamePadCtrl.UpdateState();
	if (result != 0) goto EXIT;
	
	//スクリーンゲームパッド状態更新
	if (m_pScreenGamePadCtrl == NULL) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	result = m_pScreenGamePadCtrl->UpdateState();
	if (result != 0) goto EXIT;
	
	//ゲームパッド操作：右スティック
	//スティック値は-1.0から1.0の範囲
	dX += m_VelocityPT * dt * m_GamePadCtrl.GetState_ThumbRX() * (100.0f);
	dY += m_VelocityPT * dt * m_GamePadCtrl.GetState_ThumbRY() * (-100.0f);
	//NSLog(@"ExGamepad dX:%d dY:%d", dX, dY);
	
	//マウス視線移動モードOFFなら移動量を無視する
	if (!m_IsMouseCamMode) {
		dX = 0;
		dY = 0;
	}
	
	//CTRL+移動キーで視線方向を変化させる
	if (m_DIKeyCtrl.IsKeyDown(DIK_CONTROL)) {
		if (m_DIKeyCtrl.IsKeyDown(DIK_W) || m_DIKeyCtrl.IsKeyDown(DIK_UP)) {
			dY -= m_VelocityPT;
		}
		if (m_DIKeyCtrl.IsKeyDown(DIK_S) || m_DIKeyCtrl.IsKeyDown(DIK_DOWN)) {
			dY += m_VelocityPT;
		}
		if (m_DIKeyCtrl.IsKeyDown(DIK_A) || m_DIKeyCtrl.IsKeyDown(DIK_LEFT)) {
			dX -= m_VelocityPT;
		}
		if (m_DIKeyCtrl.IsKeyDown(DIK_D) || m_DIKeyCtrl.IsKeyDown(DIK_RIGHT)) {
			dX += m_VelocityPT;
		}
	}
	
	//デルタタイム算出
	_CalcDeltaTime();
	
	//視線方向の更新
	result = _TransformEyeDirection(dX, dY);
	if (result != 0) goto EXIT;
	
	//カメラ位置の更新
	result = _TransformCamPosition();
	if (result != 0) goto EXIT;
	
	//カメラ位置設定
	result = _SetCamPosition(m_CamVector, m_CamDirPhi, m_CamDirTheta, m_CamUpVector);
	if (result != 0) goto EXIT;
	
	//カメラ更新
	result = m_Camera.Transform(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//回転対応
	result = _TransformRolling(dW);
	if (result != 0) goto EXIT;
	
	//デルタ値クリア
	m_pTouchCtrl->ClearDelta();
	
EXIT:;
	return result;
}

//******************************************************************************
// 変換処理：左目用
//******************************************************************************
int MTFirstPersonCam::TransformLeftEye(
		OGLDevice* pOGLDevice,
		bool isActive
	)
{
	return Transform(pOGLDevice, isActive);
}

//******************************************************************************
// 変換処理：右目用
//******************************************************************************
int MTFirstPersonCam::TransformRightEye(
		OGLDevice* pOGLDevice,
		bool isActive
	)
{
	int result = 0;
	float phi = 0.0f;
	float phiRad = 0.0f;
	OGLVECTOR3 camVector;
	OGLVECTOR3 moveVector;
	
	//Off-Axis法を用いる
	//  左目と右目の視線方向：並行
	//  左目と右目の間隔：m_VRViewEyeSeparation
	
	//移動方向は左目から右目
	phi = m_CamDirPhi + LH2RH(-90.0f);
	
	//クリッピング
	if (phi >= 360.0f) {
		phi -= 360.0f;
	}
	else if (phi <= -360.0f) {
		phi += 360.0f;
	}
	
	//移動ベクトル作成（極座標から直行座標へ変換）
	phiRad = OGLH::ToRadian(phi);
	moveVector.x = m_VRViewEyeSeparation * cos(phiRad);  // r * sin(90) * cos(phi)
	moveVector.y = 0.0f;                                 // r * cos(90)
	moveVector.z = m_VRViewEyeSeparation * sin(phiRad);  // r * sin(90) * cos(phi)
	
	//カメラ位置を移動
	camVector = m_CamVector;
	camVector.x += moveVector.x;
	camVector.y += moveVector.y;
	camVector.z += moveVector.z;
	
	//カメラ位置設定
	result = _SetCamPosition(camVector, m_CamDirPhi, m_CamDirTheta, m_CamUpVector);
	if (result != 0) goto EXIT;
	
	//カメラ更新
	result = m_Camera.Transform(pOGLDevice);
	if (result != 0) goto EXIT;
	
	m_CamVectorRightEye = camVector;
	
EXIT:;
	return result;
}

//******************************************************************************
// 視線方向更新
//******************************************************************************
int MTFirstPersonCam::_TransformEyeDirection(
		float dX,
		float dY
	)
{
	int result = 0;
	float dt = 0.0f;
	float dPhi = 0.0f;
	float dTheta = 0.0f;
	
	//デルタタイム
	dt = (float)m_DeltaTime / 1000.0f;
	
	//マウス移動量から方位角と天頂角の増加量を算出
	dPhi   = (float)-dX * m_VelocityPT * dt;
	dTheta = (float) dY * m_VelocityPT * dt;
	
	//極端な角度の変化を抑止する
	//  画面描画が引っかかった場合にマウス移動量が蓄積され
	//  突然あらぬ方向を向いてしまうことを避けたい
    if (fabsf(dPhi) > 45.0f) {
		dPhi = 0.0f;
	}
	if (fabsf(dTheta) > 45.0f) {
		dTheta = 0.0f;
	}
	
	//マウス移動量を方位角と天頂角に反映する
	m_CamDirPhi += LH2RH(dPhi);
	m_CamDirTheta += dTheta;
	
	//クリッピング処理
	if (m_CamDirPhi >= 360.0f) {
		m_CamDirPhi -= 360.0f;
	}
	else if (m_CamDirPhi <= -360.0f) {
		m_CamDirPhi += 360.0f;
	}
	if (m_CamDirTheta <= 1.0f) {
		m_CamDirTheta = 1.0f;
	}
	else if (m_CamDirTheta >= 179.0f) {
		m_CamDirTheta = 179.0f;
	}
	//↑天頂角が0度または180度になると描画がおかしくなる・・・
	
//EXIT:;
	return result;
}

//******************************************************************************
// カメラ位置更新
//******************************************************************************
int MTFirstPersonCam::_TransformCamPosition()
{
	int result = 0;
	float dFB = 0.0f;
	float dLR = 0.0f;
	float phi = 0.0f;
	float phiRad = 0.0f;
	float distance = 0.0f;
	float dt = 0.0f;
	float rate = 0.0f;
	float progress = 0.0f;
	OGLVECTOR3 moveVector;
	
	//デルタタイム
	dt = (float)m_DeltaTime / 1000.0f;
	
	//移動方向の方位角
	phi = m_CamDirPhi;
	
	if (m_DIKeyCtrl.IsKeyDown(DIK_COMMAND) || m_DIKeyCtrl.IsKeyDown(DIK_CONTROL)) {
		//コマンドキーまたはCTRLキーが押されている場合はキー入力を無視する
	}
	else {
		//移動速度の加速倍率
		rate = 1.0f;
		if (m_DIKeyCtrl.IsKeyDown(DIK_SHIFT)) {
			rate = m_AcceleRate;
		}
		//前移動
		if (m_pTouchCtrl->GetDeltaFB() > 0.0f) {
			rate = m_pTouchCtrl->GetDeltaFB() * (0.1f);
			distance = m_VelocityFB * dt * rate;
			phi += 0.0f;
		}
		//後ろ移動：視線は前を向いたまま
		if (m_pTouchCtrl->GetDeltaFB() < 0.0f) {
			rate = m_pTouchCtrl->GetDeltaFB() * (-0.1f);
			distance = m_VelocityFB * dt * rate;
			phi += 180.0f;
		}
		//左移動：視線は前を向いたまま
		if (m_pTouchCtrl->GetDeltaLR() > 0.0f) {
			rate = m_pTouchCtrl->GetDeltaLR() * (0.1f);
			distance = m_VelocityLR * dt * rate;
			phi += LH2RH(90.0f);
		}
		//右移動：視線は前を向いたまま
		if (m_pTouchCtrl->GetDeltaLR() < 0.0f) {
			rate = m_pTouchCtrl->GetDeltaLR() * (-0.1f);
			distance = m_VelocityLR * dt * rate;
			phi += LH2RH(-90.0f);
		}
		//上昇：視線変更なし
		if (m_pTouchCtrl->GetDeltaUD() > 0.0f) {
			rate = m_pTouchCtrl->GetDeltaUD() * (0.1f);
			m_CamVector.y += +(m_VelocityUD * dt * rate);
		}
		//下降：視線変更なし
		if (m_pTouchCtrl->GetDeltaUD() < 0.0f) {
			rate = m_pTouchCtrl->GetDeltaUD() * (-0.1f);
			m_CamVector.y += -(m_VelocityUD * dt * rate);
		}
		//-X軸方向（曲再生逆方向）に移動：視線変更なし
		if (m_DIKeyCtrl.IsKeyDown(DIK_Z) || m_DIKeyCtrl.IsKeyDown(DIK_COMMA)) {
			m_CamVector.x +=  -(m_VelocityFB * dt * rate);
		}
		//+X軸方向（曲再生方向）に移動：視線変更なし
		if (m_DIKeyCtrl.IsKeyDown(DIK_C) || m_DIKeyCtrl.IsKeyDown(DIK_PERIOD)) {
			m_CamVector.x +=  +(m_VelocityFB * dt * rate);
		}
	}
	
	//ゲームパッド操作：十字キー＞前後左右移動
	if (distance == 0.0f) {
		if (m_GamePadCtrl.GetState_DPadUp() || m_pScreenGamePadCtrl->GetState_DPadUp()) {
			dFB = m_VelocityFB * dt * (1.0f);
		}
		if (m_GamePadCtrl.GetState_DPadDown() || m_pScreenGamePadCtrl->GetState_DPadDown()) {
			dFB = m_VelocityFB * dt * (-1.0f);
		}
		if (m_GamePadCtrl.GetState_DPadRight() || m_pScreenGamePadCtrl->GetState_DPadRight()) {
			dLR = m_VelocityLR * dt * (1.0f);
		}
		if (m_GamePadCtrl.GetState_DPadLeft() || m_pScreenGamePadCtrl->GetState_DPadLeft()) {
			dLR = m_VelocityLR * dt * (-1.0f);
		}
		distance = sqrt((dFB * dFB) + (dLR * dLR));
		phi += OGLH::ToDegree(atan2(dLR, dFB));
	}
	//ゲームパッド操作：左スティック＞前後左右移動
	if (distance == 0.0f) {
		//スティック値は-1.0から1.0の範囲
		dFB += m_VelocityFB * dt * m_GamePadCtrl.GetState_ThumbLX();
		dLR += m_VelocityLR * dt * m_GamePadCtrl.GetState_ThumbLY();
		distance = sqrt((dFB * dFB) + (dLR * dLR));
		phi += OGLH::ToDegree(atan2(dFB, dLR));
	}
	//ゲームパッド操作：X,Yボタン＞下降,上昇移動
	if (m_GamePadCtrl.GetState_X() || m_pScreenGamePadCtrl->GetState_X()) {
		m_CamVector.y += -(m_VelocityUD * dt);
	}
	if (m_GamePadCtrl.GetState_Y() || m_pScreenGamePadCtrl->GetState_Y()) {
		m_CamVector.y += +(m_VelocityUD * dt);
	}
	
	//クリッピング
	if (phi >= 360.0f) {
		phi -= 360.0f;
	}
	else if (phi <= -360.0f) {
		phi += 360.0f;
	}
	
	//移動ベクトル作成（極座標から直行座標へ変換）
	phiRad = OGLH::ToRadian(phi);
	moveVector.x = distance * cos(phiRad);  // r * sin(90) * cos(phi)
	moveVector.y = 0.0f;                    // r * cos(90)
	moveVector.z = distance * sin(phiRad);  // r * sin(90) * cos(phi)
	
	//カメラ位置を移動
	m_CamVector.x += moveVector.x;
	m_CamVector.y += moveVector.y;
	m_CamVector.z += moveVector.z;
	
	//演奏追跡
	progress = m_NoteDesign.GetPlayPosX(m_CurTickTime) - m_NoteDesign.GetPlayPosX(m_PrevTickTime);
	switch (m_ProgressDirection) {
		case DirX:
			m_CamVector.x += progress;
			break;
		case DirY:
			m_CamVector.y += progress;
			break;
		case DirZ:
			m_CamVector.z += progress;
			break;
	}
	
	//カメラ位置クリッピング
	_ClipCamVector(&m_CamVector);
	
	m_PrevTickTime = m_CurTickTime;
	
//EXIT:;
	return result;
}

//******************************************************************************
// 回転対応
//******************************************************************************
int MTFirstPersonCam::_TransformRolling(
		float dW
	)
{
	int result = 0;
	float dt = 0.0f;
	float domega = 0.0f;
	
	//デルタタイム
	dt = (float)m_DeltaTime / 1000.0f;
	
	//ホイール移動量から角度を算出
	domega = (float)dW * m_VelocityManualRoll * dt;
	
	//極端な角度の変化を抑止する
	//  画面描画が引っかかった場合にマウス移動量が蓄積され
	//  突然あらぬ方向を向いてしまうことを避けたい
	if (fabsf(domega) > 45.0f) {
		domega = 0.0f;
	}
	
	//自動回転
	if (m_IsAutoRollMode) {
		domega += m_VelocityAutoRoll * dt;
	}
	
	//回転角度更新
	m_RollAngle += domega;
	
	//回転角度のクリップ
	if (m_RollAngle >= 360.0f) {
		m_RollAngle -= 360.0f;
	}
	else if (m_RollAngle <= -360.0f) {
		m_RollAngle += 360.0f;
	}
	
//EXIT:;
	return result;
}

//******************************************************************************
// 手動回転角度取得
//******************************************************************************
float MTFirstPersonCam::GetManualRollAngle()
{
	return m_RollAngle;
}

//******************************************************************************
// 手動回転角度設定
//******************************************************************************
void MTFirstPersonCam::SetManualRollAngle(
		float rollAngle
	)
{
	m_RollAngle = rollAngle;
}

//******************************************************************************
// 自動回転速度取得
//******************************************************************************
float MTFirstPersonCam::GetAutoRollVelocity()
{
	return m_VelocityAutoRoll;
}

//******************************************************************************
// 自動回転速度設定
//******************************************************************************
void MTFirstPersonCam::SetAutoRollVelocity(
		float rollVelocity
	)
{
	m_VelocityAutoRoll = rollVelocity;
}

//******************************************************************************
// カメラ位置設定
//******************************************************************************
int MTFirstPersonCam::_SetCamPosition(
		OGLVECTOR3 camVector,
		float camDirPhi,
		float camDirTheta,
		OGLVECTOR3 camUpVector
	)
{
	int result = 0;
	float phiRad = 0.0f;
	float thetaRad = 0.0f;
	OGLVECTOR3 lookVector;
	OGLVECTOR3 camLookAtVector;
	
	//視線ベクトル（極座標から直交座標へ変換）
	phiRad    = OGLH::ToRadian(camDirPhi);
	thetaRad  = OGLH::ToRadian(camDirTheta);
	lookVector.x = 10.0f * sin(thetaRad) * cos(phiRad);
	lookVector.y = 10.0f * cos(thetaRad);
	lookVector.z = 10.0f * sin(thetaRad) * sin(phiRad);
	
	//カメラ位置に視線ベクトルを足して注目点を算出
	camLookAtVector = m_CamVector;
	camLookAtVector.x += lookVector.x;
	camLookAtVector.y += lookVector.y;
	camLookAtVector.z += lookVector.z;
	
	//カメラ位置登録
	m_Camera.SetPosition(
			camVector,			//カメラ位置
			camLookAtVector, 	//注目点
			camUpVector			//カメラ上方向
		);

	return result;
}

//******************************************************************************
// デルタタイム取得
//******************************************************************************
void MTFirstPersonCam::_CalcDeltaTime()
{
	uint64_t curTime = 0;
	
	curTime = m_MachTime.GetCurTimeInMsec();
	
	if (m_PrevTime == 0) {
		//初回測定時は変化なしとする
		m_DeltaTime = 0;
	}
	else {
		//デルタタイム
		m_DeltaTime = curTime - m_PrevTime;
	}
	
	m_PrevTime = curTime;
	
	return;
}

//******************************************************************************
// チックタイム設定
//******************************************************************************
void MTFirstPersonCam::SetCurTickTime(
		unsigned int curTickTime
	)
{
	m_CurTickTime = curTickTime;
}

//******************************************************************************
// リセット
//******************************************************************************
void MTFirstPersonCam::Reset()
{
	m_PrevTime = 0;
	m_DeltaTime = 0;
	m_PrevTickTime = 0;
	m_CurTickTime = 0;
}

//******************************************************************************
// 設定ファイル読み込み
//******************************************************************************
int MTFirstPersonCam::_LoadConfFile(
		NSString* pSceneName
	)
{
	int result = 0;
	MTConfFile confFile;
	
	result = confFile.Initialize(pSceneName);
	if (result != 0) goto EXIT;
	
	//カメラ移動速度情報取得
	result = confFile.SetCurSection(@"FirstPersonCam");
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityFB", &m_VelocityFB, 15.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityLR", &m_VelocityLR, 15.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityUD", &m_VelocityUD, 10.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityPT", &m_VelocityPT, 6.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"AcceleRate", &m_AcceleRate, 2.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityAutoRoll", &m_VelocityAutoRoll, 6.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VelocityManualRoll", &m_VelocityManualRoll, 1.0f);
	if (result != 0) goto EXIT;
	result = confFile.GetFloat(@"VRViewEyeSeparation", &m_VRViewEyeSeparation, 0.5f);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 設定ファイル読み込み
//******************************************************************************
int MTFirstPersonCam::_LoadUserConf()
{
	int result = 0;
	YNUserConf* pUserConf = nil;
	int invertXYAxis = 0;
	
	//ユーザ設定初期化
	pUserConf = [[YNUserConf alloc] init];
	if (pUserConf == nil) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//タッチ操作X軸Y軸反転の設定を取得
	[pUserConf setCategory:MT_CONF_CATEGORY_CONTROLLER];
	[pUserConf setSection:MT_CONF_SECTION_TOUCH];
	invertXYAxis = [pUserConf intValueForKey:@"InvertXYAxis" defaultValue:0];
	
	m_TouchInvertXYAxisFactor = 1.0f;
	if (invertXYAxis > 0) {
		m_TouchInvertXYAxisFactor = -1.0f;
	}
	
EXIT:;
	[pUserConf release];
	return result;
}

//******************************************************************************
// カメラ位置クリッピング
//******************************************************************************
void MTFirstPersonCam::_ClipCamVector(
		OGLVECTOR3* pVector
	)
{
	if (pVector->x < -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT)) {
		pVector->x = -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT);
	}
	if (pVector->x > MTFIRSTPERSONCAM_CAMVECTOR_LIMIT) {
		pVector->x = MTFIRSTPERSONCAM_CAMVECTOR_LIMIT;
	}
	if (pVector->y < -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT)) {
		pVector->y = -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT);
	}
	if (pVector->y > MTFIRSTPERSONCAM_CAMVECTOR_LIMIT) {
		pVector->y = MTFIRSTPERSONCAM_CAMVECTOR_LIMIT;
	}
	if (pVector->z < -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT)) {
		pVector->z = -(MTFIRSTPERSONCAM_CAMVECTOR_LIMIT);
	}
	if (pVector->z > MTFIRSTPERSONCAM_CAMVECTOR_LIMIT) {
		pVector->z = MTFIRSTPERSONCAM_CAMVECTOR_LIMIT;
	}
}

//******************************************************************************
// 進行方向設定
//******************************************************************************
void MTFirstPersonCam::SetProgressDirection(
		MTProgressDirection dir
	)
{
	m_ProgressDirection = dir;
}

//******************************************************************************
// マウスホイールイベント受信
//******************************************************************************
void MTFirstPersonCam::OnScrollWheel(
		float deltaWheelX,
		float deltaWheelY,
		float deltaWheelZ
	)
{
	return;
}

//******************************************************************************
// タッチイベント制御登録
//******************************************************************************
void MTFirstPersonCam::SetTouchCtrl(MTTouchCtrl* pTouchCtrl)
{
	m_pTouchCtrl = pTouchCtrl;
}

//******************************************************************************
// スクリーン種別設定
//******************************************************************************
void MTFirstPersonCam::SetScreenType(MTScene::ScreenType type)
{
	switch (type) {
		case MTScene::StandardView:
			//通常ビュー開始により方向のオフセットをクリアする
			m_DevDirPhiOffset = 0.0f;
			break;
		case MTScene::VRViewSingleLens:
		case MTScene::VRViewDualLens:
			//VRビュー開始によりデバイス方向がリセットされるため現時点の方向をオフセットとして保持する
			if (m_ScreenType == MTScene::StandardView) {
				m_DevDirPhiOffset = m_CamDirPhi;
			}
			break;
	}
	
	m_ScreenType = type;
	m_DevDirPhiOffset = m_CamDirPhi;
	m_CamUpVector = OGLVECTOR3(0.0f, 1.0f, 0.0f);
	
	return;
}

//******************************************************************************
// デバイス方向設定
//******************************************************************************
void MTFirstPersonCam::SetDeviceDirection(OGLQuaternion quaternion)
{
	OGLVECTOR3 v;
	OGLVECTOR3 u;
	OGLVECTOR3 p;
	GLfloat s = 0.0f;
	GLfloat len = 0.0f;
	GLfloat devDirPhi = 0.0f;
	GLfloat devDirTheta = 0.0f;
	
	//デバイスのクォータニオンを用いて単位ベクトルを回転
	v = OGLVECTOR3(0, 0, -1);
	u = OGLVECTOR3(quaternion.x, quaternion.y, quaternion.z);
	s = quaternion.w;
	p =   (u * (2.0f * OGLH::Vec3Dot(u, v)))
	    + (v * (s * s - OGLH::Vec3Dot(u, u)))
	    + (OGLH::Vec3Cross(u, v) * (2.0f * s));
	//NSLog(@"x:%.3f y:%.3f z:%.3f", p.x, p.y, p.z);
	
	//回転後の単位ベクトルの方位角と仰角を算出
	len = sqrt(p.x * p.x + p.y * p.y);
	len = MAX(len, (1e-6));
	devDirPhi = OGLH::ToDegree(LH2RH(OGLH::Sign(p.y) * acos(p.x / len)));
	devDirTheta = OGLH::ToDegree(acos(p.z));
	//NSLog(@"phi:%.3f theta:%.3f", devDirPhi, devDirTheta);
	
	//TODO: カメラ上方向ベクトル更新 m_CamUpVector
	//  現状は上方向ベクトルを(0,1,0)で固定しているため
	//  頭を左右に傾けたときにカメラが傾かない
	
	//クリッピング処理
	if (devDirTheta <= 1.0f) {
		devDirTheta = 1.0f;
	}
	else if (devDirTheta >= 179.0f) {
		devDirTheta = 179.0f;
	}
	
	//カメラ方向に反映
	if ((m_ScreenType == MTScene::VRViewSingleLens)
	 || (m_ScreenType == MTScene::VRViewDualLens)) {
		m_CamDirPhi = m_DevDirPhiOffset + devDirPhi;
		m_CamDirTheta = devDirTheta;
	}
	
	return;
}

//******************************************************************************
// ゲームコントローラー更新
//******************************************************************************
int MTFirstPersonCam::OnGameControllerChanged()
{
	int result = 0;
	
	//ゲームパッド初期化：ユーザインデックス0固定
	result = m_GamePadCtrl.Initialize(0);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// スクリーンゲームパッド登録
//******************************************************************************
void MTFirstPersonCam::SetScreenGamePadCtrl(MTScreenGamePadCtrl* pScreenGamePadCtrl)
{
	m_pScreenGamePadCtrl = pScreenGamePadCtrl;
}


