//******************************************************************************
//
// MIDITrail / MTLogo
//
// MIDITrail ロゴ描画クラス
//
// Copyright (C) 2010-2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

// MEMO:
// ポリゴンをタイル状に並べてロゴのテクスチャを貼り付ける。
// ポリゴンの色をタイルごとに更新することでロゴをグラデーションさせる。
//
//   +-++-++-++-+
//   |/||/||/||/|...
//   +-++-++-++-+
//
// Windows版のソースを移植しているため、座標は左手系(DirectX)で処理している。
// 左手系(DirectX,Metal)=>右手系(OpenGL)への変換は LH2RH マクロで実現する。

#import "YNBaseLib.h"
#import "MTLogo.h"


//******************************************************************************
// コンストラクタ
//******************************************************************************
MTLogo::MTLogo(void)
{
	m_pVertex = NULL;
	m_StartTime = 0;
	m_TileHeight = 0.0f;
	m_TileWidth = 0.0f;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
MTLogo::~MTLogo(void)
{
	Release();
}

//******************************************************************************
// シーン生成
//******************************************************************************
int MTLogo::Create(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	
	Release();
	
	//テクスチャ生成
	result = _CreateTexture(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//頂点生成
	result = _CreateVertex(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//Mach時間初期化
	result = m_MachTime.Initialize();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 変換処理
//******************************************************************************
int MTLogo::Transform(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	OGLTransMatrix transMatrix;
	
	//タイトルグラデーション設定
	result = _SetGradationColor();
	if (result != 0) goto EXIT;
	
	//頂点更新（アニメーション）
	result = _UpdateVertexPosition();
	if (result != 0) goto EXIT;

	//変換行列設定
	m_Primitive.Transform(&transMatrix);
	
EXIT:;
	return result;
}

//******************************************************************************
// 描画
//******************************************************************************
int MTLogo::Draw(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	
	//タイトル文字描画
	result = m_Primitive.Draw(pOGLDevice, m_FontTexture.GetTexture());
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 破棄
//******************************************************************************
void MTLogo::Release()
{
	m_Primitive.Release();
	m_FontTexture.Clear();
}

//******************************************************************************
// テクスチャ生成
//******************************************************************************
int MTLogo::_CreateTexture(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	OGLCOLOR color = OGLCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	bool isForceFixedPitch = false;
	
	//フォント設定
	result = m_FontTexture.SetFont(
					MTLOGO_FONTNAME,	//フォント名称
					MTLOGO_FONTSIZE,	//フォントサイズ
					color,				//色
					isForceFixedPitch	//固定ピッチ強制
				);
	if (result != 0) goto EXIT;
	
	//タイル文字一覧テクスチャ作成
	result = m_FontTexture.CreateTexture(pOGLDevice, MTLOGO_TITLE);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// フォントタイル頂点生成
//******************************************************************************
int MTLogo::_CreateVertex(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	unsigned int i = 0;
	MTLOGO_VERTEX* pVertex = NULL;
	
	//プリミティブ初期化
	result = m_Primitive.Initialize(
					pOGLDevice,				//描画デバイス
					sizeof(MTLOGO_VERTEX),	//頂点サイズ
					_GetFVFFormat(),		//頂点FVFフォーマット
					OGL_TRIANGLES			//プリミティブ種別
				);
	if (result != 0) goto EXIT;
	
	//頂点バッファ生成
	result = m_Primitive.CreateVertexBuffer(pOGLDevice, 6*MTLOGO_TILE_NUM);
	if (result != 0) goto EXIT;
	
	//バッファのロック
	result = m_Primitive.LockVertex((void**)&pVertex);
	if (result != 0) goto EXIT;
	
	//頂点座標設定
	_SetVertexPosition(
			pVertex,		//頂点座標配列
			MTLOGO_POS_X,	//描画位置x
			MTLOGO_POS_Y,	//描画位置y
			MTLOGO_MAG		//拡大率
		);
	
	for (i = 0; i < 6*MTLOGO_TILE_NUM; i++) {
		//各頂点のディフューズ色
		pVertex[i].c = OGLCOLOR(0.0f, 0.0f, 0.0f, 1.0f); //R,G,B,A
	}
	
	//バッファのロック解除
	result = m_Primitive.UnlockVertex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 頂点位置設定
//******************************************************************************
void MTLogo::_SetVertexPosition(
		MTLOGO_VERTEX* pVertex,
		float x,
		float y,
		float magRate
	)
{
	unsigned int i = 0;
	unsigned int texHeight = 0;
	unsigned int texWidth = 0;
	float height = 0.0f;
	float width = 0.0f;
	float tileNo = 0.0f;
	
	//タイルサイズ
	m_FontTexture.GetTextureSize(&texHeight, &texWidth);
	height = texHeight * magRate;
	width  = ((float)texWidth / (float)MTLOGO_TILE_NUM) * magRate;
	m_TileHeight = height;
	m_TileWidth = width;
	
	//頂点座標：XY平面の(0, 0)を左上とする
	for (i = 0; i < MTLOGO_TILE_NUM; i++) {
		//頂点座標
		pVertex[i*6+0].p = OGLVECTOR3(width * (i     ),  0.0f,   0.0f);
		pVertex[i*6+1].p = OGLVECTOR3(width * (i+1.0f),  0.0f,   0.0f);
		pVertex[i*6+2].p = OGLVECTOR3(width * (i     ), -height, 0.0f);
		pVertex[i*6+3].p = pVertex[i*6+2].p;
		pVertex[i*6+4].p = pVertex[i*6+1].p;
		pVertex[i*6+5].p = OGLVECTOR3(width * (i+1.0f), -height, 0.0f);
		
		//テクスチャ座標
		//左上
		pVertex[i*6+0].t.x = 1.0f * tileNo / (float)MTLOGO_TILE_NUM;
		pVertex[i*6+0].t.y = 0.0f;
		//右上
		pVertex[i*6+1].t.x = 1.0f * (tileNo + 1.0f) / (float)MTLOGO_TILE_NUM;
		pVertex[i*6+1].t.y = 0.0f;
		//左下
		pVertex[i*6+2].t.x = 1.0f * tileNo / (float)MTLOGO_TILE_NUM;
		pVertex[i*6+2].t.y = 1.0f;
		//左下
		pVertex[i*6+3].t = pVertex[i*6+2].t;
		//右上
		pVertex[i*6+4].t = pVertex[i*6+1].t;
		//右下
		pVertex[i*6+5].t.x = 1.0f * (tileNo + 1.0f) / (float)MTLOGO_TILE_NUM;
		pVertex[i*6+5].t.y = 1.0f;
		
		tileNo += 1.0f;
	}
	
	//法線
	for (i = 0; i < 6*MTLOGO_TILE_NUM; i++) {
		pVertex[i].n = OGLVECTOR3(0.0f, 0.0f, -1.0f);
	}
	
	//指定位置に移動
	for (i = 0; i < 6*MTLOGO_TILE_NUM; i++) {
		pVertex[i].p.x += x;
		pVertex[i].p.y += y;
	}
	
	return;
}

//******************************************************************************
// グラデーション設定
//******************************************************************************
int MTLogo::_SetGradationColor()
{
	int result = 0;
	unsigned int i = 0;
	unsigned int sceneTime = 0;
	unsigned int delay = 0;
	unsigned int tileTime = 0;
	float color = 0.0f;
	MTLOGO_VERTEX* pVertex = NULL;
	MTLOGO_VERTEX* pVertexTarget = NULL;
	
	//シーン経過時間
	if (m_StartTime == 0) {
		m_StartTime = m_MachTime.GetCurTimeInMsec();
	}
	sceneTime = (unsigned int)(m_MachTime.GetCurTimeInMsec() - m_StartTime);
	
	//バッファのロック
	result = m_Primitive.LockVertex((void**)&pVertex);
	if (result != 0) goto EXIT;
	
	//グラデーション処理
	for (i = 0; i < MTLOGO_TILE_NUM; i++) {
		
		//タイルごとの遅延時間
		delay = i * (MTLOGO_GRADATION_TIME / MTLOGO_TILE_NUM);
		
		//タイルにとっての経過時間
		if (sceneTime < delay) {
			tileTime = 0;
		}
		else {
			tileTime = sceneTime - delay;
		}
		
		//タイルの色
		if (tileTime < MTLOGO_GRADATION_TIME) {
			//黒→白
			color = (float)tileTime / (float)MTLOGO_GRADATION_TIME;
		}
		else if (tileTime < (MTLOGO_GRADATION_TIME*2)) {
			//白→黒
			color = 1.0f - ((float)(tileTime - MTLOGO_GRADATION_TIME) / (float)MTLOGO_GRADATION_TIME);
		}
		else {
			//黒
			color = 0.0f;
		}
		
		//タイルの色を頂点に設定
		pVertexTarget = pVertex + (6*i);
		_SetTileColor(pVertexTarget, color);
	}
	
	//バッファのロック解除
	result = m_Primitive.UnlockVertex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// タイル色設定
//******************************************************************************
void MTLogo::_SetTileColor(
		MTLOGO_VERTEX* pVertex,
		float color
	)
{
	unsigned int i = 0;
	
	for (i = 0; i < 6; i++) {
		pVertex[i].c = OGLCOLOR(color, color, color, 1.0f); //R,G,B,A
	}
}

//******************************************************************************
// 頂点更新（アニメーション）
//******************************************************************************
int MTLogo::_UpdateVertexPosition()
{
	int result = 0;
	MTLOGO_VERTEX* pVertex = NULL;
	unsigned int i = 0;
	float height = 0.0f;
	float width = 0.0f;
	float ratio = 0.0f;
	float angle = 0.0f;
	float prevAngle = 0.0f;
	unsigned int sceneTime = 0;
	unsigned int delay = 0;
	unsigned int tileTime = 0;
	
	//バッファのロック
	result = m_Primitive.LockVertex((void**)&pVertex);
	if (result != 0) goto EXIT;
	
	//シーン経過時間
	if (m_StartTime == 0) {
		m_StartTime = m_MachTime.GetCurTimeInMsec();
	}
	sceneTime = (unsigned int)(m_MachTime.GetCurTimeInMsec() - m_StartTime);
	
	//タイルサイズ
	height = m_TileHeight;
	width = m_TileWidth;
	
	//頂点座標：XY平面の(0, 0)を左上とする
	for (i = 0; i < MTLOGO_TILE_NUM; i++) {
		//タイルごとの経過時間
		delay = (float)i * ((float)MTLOGO_GRADATION_TIME / MTLOGO_TILE_NUM);
		if (sceneTime < delay) {
			tileTime = 0;
		}
		else {
			tileTime = sceneTime - delay;
		}
		
		//タイルごとの回転角度
		ratio = (float)tileTime / MTLOGO_GRADATION_TIME;
		angle = 0.0f;
		if (ratio < 1.0f) {
			angle = 180.0f - (ratio * 180.0f);
		}
		if (i == 0) {
			prevAngle = angle;
		}
		
		//頂点座標：タイル左辺は前回角度、タイル右辺は今回角度を適用
		pVertex[i*6+0].p = OGLH::RotateYZ(-height/2, 0, OGLVECTOR3(width * (i     ),  0.0f,   0.0f), prevAngle);
		pVertex[i*6+1].p = OGLH::RotateYZ(-height/2, 0, OGLVECTOR3(width * (i+1.0f),  0.0f,   0.0f), angle);
		pVertex[i*6+2].p = OGLH::RotateYZ(-height/2, 0, OGLVECTOR3(width * (i     ), -height, 0.0f), prevAngle);
		pVertex[i*6+3].p = pVertex[i*6+2].p;
		pVertex[i*6+4].p = pVertex[i*6+1].p;
		pVertex[i*6+5].p = OGLH::RotateYZ(-height/2, 0, OGLVECTOR3(width * (i+1.0f), -height, 0.0f), angle);
		//法線
		pVertex[i*6+0].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), prevAngle);
		pVertex[i*6+1].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), angle);
		pVertex[i*6+2].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), prevAngle);
		pVertex[i*6+3].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), prevAngle);
		pVertex[i*6+4].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), angle);
		pVertex[i*6+5].n = OGLH::RotateYZ(0, 0, OGLVECTOR3(0.0f, 0.0f, -1.0f), angle);
		
		prevAngle = angle;
	}
	
	//指定位置に移動
	for (i = 0; i < (6 * MTLOGO_TILE_NUM); i++) {
		pVertex[i].p.x += MTLOGO_POS_X;
		pVertex[i].p.y += MTLOGO_POS_Y;
	}
	
	//バッファのロック解除
	result = m_Primitive.UnlockVertex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

