//******************************************************************************
//
// OGL Utility / OGLCamera
//
// カメラクラス
//
// Copyright (C) 2010-2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "OGLCamera.h"
#import "OGLH.h"
#import "YNBaseLib.h"
#import <math.h>


//******************************************************************************
// コンストラクタ
//******************************************************************************
OGLCamera::OGLCamera(void)
{
	_Clear();
}

//******************************************************************************
// デストラクタ
//******************************************************************************
OGLCamera::~OGLCamera(void)
{
}

//******************************************************************************
// 初期化
//******************************************************************************
int OGLCamera::Initialize()
{
	_Clear();
	return 0;
}

//******************************************************************************
// 基本パラメータ設定
//******************************************************************************
void OGLCamera::SetBaseParam(
		float viewAngle,
		float nearPlane,
		float farPlane
	)
{
	m_ViewAngle = viewAngle;
	m_NearPlane = nearPlane;
	m_FarPlane = farPlane;
}

//******************************************************************************
// カメラ位置設定
//******************************************************************************
void OGLCamera::SetPosition(
		OGLVECTOR3 camVector,
		OGLVECTOR3 camLookAtVector,
		OGLVECTOR3 camUpVector
	)
{
	m_CamVector = camVector;
	m_CamLookAtVector = camLookAtVector;
	m_CamUpVector = camUpVector;
}

//******************************************************************************
// 変換
//******************************************************************************
int OGLCamera::Transform(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	OGLVIEWPORT viewPort;
	MTLViewport mtlViewport;
	float aspect = 0.0f;
	matrix_float4x4 viewMatrix;
	matrix_float4x4 projectionMatrix;
	id <MTLRenderCommandEncoder> commandEncoder = nil;
	
	//ビューポート取得
	pOGLDevice->GetViewPort(&viewPort);
	
	//アスペクト比
	aspect = viewPort.width / viewPort.height;
	
	//ビューポート設定
	mtlViewport.originX = viewPort.originx;
	mtlViewport.originY = viewPort.originy;
	mtlViewport.width = viewPort.width;
	mtlViewport.height = viewPort.height;
	mtlViewport.znear = 0.0f;
	mtlViewport.zfar = 1.0f;
	commandEncoder = pOGLDevice->GetCurrentCommandEncoder();
	[commandEncoder setViewport:mtlViewport];
	
	//プロジェクション行列
	projectionMatrix = _gluPerspective(
								m_ViewAngle,	//視覚度
								aspect,			//アスペクト比
								m_NearPlane,	//nearプレーン
								m_FarPlane		//farプレーン
							);
	
	//ビュー行列
	viewMatrix = _gluLookAt(
						m_CamVector.x,			//カメラ位置
						m_CamVector.y,			//
						m_CamVector.z,			//
						m_CamLookAtVector.x,	//注目点
						m_CamLookAtVector.y,	//
						m_CamLookAtVector.z,	//
						m_CamUpVector.x,		//カメラの上方向
						m_CamUpVector.y,		//
						m_CamUpVector.z			//
					);
	
	pOGLDevice->SetProjectionMatrix(projectionMatrix);
	pOGLDevice->SetViewMatrix(viewMatrix);
	
EXIT:;
	return result;
}

//******************************************************************************
// クリア
//******************************************************************************
void OGLCamera::_Clear()
{
	m_ViewAngle = 45.0f;
	m_NearPlane = 1.0f;
	m_FarPlane = 1000.0f;
	m_CamVector = OGLVECTOR3(0.0f, 0.0f, 0.0f);
	m_CamLookAtVector = OGLVECTOR3(0.0f, 0.0f, 1.0f);
	m_CamUpVector = OGLVECTOR3(0.0f, 1.0f, 0.0f);
}

//******************************************************************************
// gluPerspective 代替関数
//******************************************************************************
matrix_float4x4 OGLCamera::_gluPerspective(
		OGLfloat fovy,		//視覚度
		OGLfloat aspect,	//アスペクト比
		OGLfloat znear,		//nearプレーン
		OGLfloat zfar		//farプレーン
	)
{
	OGLfloat f = 1.0 / tanf(OGLH::ToRadian(fovy) / 2.0);
	
	//座標：左手系 NDC[0,1]
	//行列：Metal,OpenGLは列優先(Column major order)
	matrix_float4x4 matrix = {{
		{ f / aspect, 0,     0,                                0  },
		{ 0,          f,     0,	                               0  },
		{ 0,          0,     zfar / (zfar - znear),            1  },
		{ 0,          0,     -(zfar * znear) / (zfar - znear), 0  }
	}};
	
	return matrix;
}

//******************************************************************************
// gluLookAt 代替関数
//******************************************************************************
matrix_float4x4 OGLCamera::_gluLookAt(
		OGLfloat eyeX,		//カメラ位置
		OGLfloat eyeY,		//
		OGLfloat eyeZ,		//
		OGLfloat centerX,	//注目点
		OGLfloat centerY,	//
		OGLfloat centerZ,	//
		OGLfloat upX,		//カメラの上方向
		OGLfloat upY,		//
		OGLfloat upZ		//
	)
{
	OGLVECTOR3 eyev;
	OGLVECTOR3 centerv;
	OGLVECTOR3 upv;
	OGLVECTOR3 f;
	OGLVECTOR3 s;
	OGLVECTOR3 u;
	OGLVECTOR3 t;
	
	eyev    = OGLVECTOR3(eyeX, eyeY, eyeZ);
	centerv = OGLVECTOR3(centerX, centerY, centerZ);
	upv     = OGLVECTOR3(upX, upY, upZ);
	
	f = centerv - eyev;
	f = OGLH::Vec3Normalize(f);
	s = OGLH::Vec3Cross(upv, f);
	s = OGLH::Vec3Normalize(s);
	u = OGLH::Vec3Cross(f, s);
	t = OGLVECTOR3(-OGLH::Vec3Dot(s, eyev), -OGLH::Vec3Dot(u, eyev), -OGLH::Vec3Dot(f, eyev));
	
	//座標：左手系
	//行列：Metal,OpenGLは列優先(Column major order)
	matrix_float4x4 matrix = {{
		{ s.x, u.x, f.x, 0 },
		{ s.y, u.y, f.y, 0 },
		{ s.z, u.z, f.z, 0 },
		{ t.x, t.y, t.z, 1 }
	}};
	
	return matrix;
}

