
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

	FILE	: RayTrace.cpp

+ ------ Explanation of file --------------------------------------------------------------------------

	ゲームプログラミングwikiより引用、改変
	http://www.c3.club.kyutech.ac.jp/gamewiki/index.php?%BB%B0%BC%A1%B8%B5%A4%CE%A5%DE%A5%A6%A5%B9%BA%C2%C9%B8%BC%E8%C6%C0#content_1_1

+ _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

#include "Common.h"

#include "Renderer.h"

#include "RayTrace.h"

//==============================================
//
//コンストラクタ
//
//==============================================
RayTrace::RayTrace()
{


}

//==============================================
//
//デストラクタ
//
//==============================================
RayTrace::~RayTrace()
{


}

//==============================================
//
//レイ判定をする前に初期化
//
//==============================================
void RayTrace::Init(float fWidth,float fHeight,float offX,float offY)
{
	m_fWidth = fWidth / 2;
	m_fHeight = fHeight / 2;
	m_offX = offX;
	m_offY = offY;
}

//==============================================
//
//ここからマウスとオブジェクトの衝突座標を取得
//
//	inMousePos	: マウス座標
//	inVertex	: ローカル頂点座標配列のポインタ(四角形の[4]でOK)
//	inWorld		: ワールド変換した行列
//	outPos		: ヒット座標を入れる変数
//	inSize		: 頂点数、defaultは四角形の6
//
//==============================================
bool RayTrace::GetPosFromScreen(VEC2 *inMousePos,VEC3 *inVertex,D3DXMATRIX *inWorld,VEC3 *outPos, int inSize)
{
	// 使う時に自動更新する
	RayTrace::SetMatrix(renderer.m_view, renderer.m_proj);

	// 変換しておく
	VEC3 v6[6];
	V4toV6(inVertex, v6);

	VEC2 out;
	D3DXVECTOR4 tmp[3];
	D3DXVECTOR4 ray_start(0,0,0,0),ray_dir(0,0,0,0);
	RayTrace::ScreenToProj(&out,inMousePos);
	RayTrace::ProjToWorld(&out,&ray_start,&ray_dir);
	for(int i = 0; i < inSize; i += 3){
		tmp[0].x = v6[i].x;
		tmp[0].y = v6[i].y;
		tmp[0].z = v6[i].z;
		tmp[0].w = 1.0f;
		D3DXVec4Transform(&tmp[0], &tmp[0],inWorld);

		tmp[1].x = v6[i + 1].x;
		tmp[1].y = v6[i + 1].y;
		tmp[1].z = v6[i + 1].z;
		tmp[1].w = 1.0f;
		D3DXVec4Transform(&tmp[1], &tmp[1],inWorld);

		tmp[2].x = v6[i + 2].x;
		tmp[2].y = v6[i + 2].y;
		tmp[2].z = v6[i + 2].z;
		tmp[2].w = 1.0f;
		D3DXVec4Transform(&tmp[2], &tmp[2],inWorld);
		if(RayTrace::GetHitPos(&ray_start,&ray_dir,tmp,outPos)){
			return true;
		}
	}
	return false;
}


//==============================================
//
//マウスを射影座標からワールド座標に変換します。
//
//==============================================
void RayTrace::ProjToWorld(VEC2 *in,D3DXVECTOR4 *ray_start,D3DXVECTOR4 *ray_dir)
{
	D3DXVECTOR4 ray_start_proj(in->x, in->y, 0, 1);// 前方クリップ面
	D3DXVECTOR4 ray_eye(0, 0, 0, 1);			 // ビュー空間でのカメラの位置
	D3DXVECTOR4 ray_to;
	// 視点を射影空間からワールド座標に変換する
	D3DXVec4Transform(ray_start, &ray_eye,&m_matInvView);
	D3DXVec4Transform(&ray_to, &ray_start_proj,&m_matInvViewToProj);
	D3DXVec4Scale(&ray_to, &ray_to, 1.0f/ray_to.w);// w=1 の射影空間に落とす
	// 向きの計算
	D3DXVec4Subtract(ray_dir,&ray_to,ray_start);
	D3DXVec4Normalize(ray_dir, ray_dir);
}

//==============================================
//
//スクリーン座標から射影座標に変換します。
//
//==============================================
void RayTrace::ScreenToProj(VEC2 *out,VEC2 *in)
{
	out->x = (in->x - m_offX) / m_fWidth - 1.0f;
	out->y = -(in->y - m_offY) / m_fHeight + 1.0f;
}

//==============================================
//
//座標変換するための行列を設定します。
//
//==============================================
void RayTrace::SetMatrix(D3DXMATRIX &matView,D3DXMATRIX &matProj)
{
	D3DXMatrixMultiply(&m_matInvViewToProj,&matView,&matProj);
	D3DXMatrixInverse(&m_matInvView,NULL,&matView);
    D3DXMatrixInverse(&m_matInvViewToProj,NULL,&m_matInvViewToProj);
}


//==============================================
//
//一平面に対して頂点が衝突しているかを判定します。
//
//==============================================
bool RayTrace::GetHitPos(D3DXVECTOR4 *x, D3DXVECTOR4 *v,D3DXVECTOR4 *tmp,VEC3 *outvec)
{
	VEC3 c;
	D3DXVECTOR4 d0, d1;
	D3DXVECTOR4 normal;
	D3DXVECTOR4 p;

	D3DXVec4Subtract(&d0,&tmp[1],&tmp[0]);
	D3DXVec4Subtract(&d1,&tmp[2],&tmp[0]);
	D3DXVec3Cross((VEC3*)&normal, (VEC3*)&d1, (VEC3*)&d0);
	D3DXVec3Normalize((VEC3*)&normal, (VEC3*)&normal);
	D3DXVec4Subtract(&p,&tmp[0],x);
	FLOAT xpn = D3DXVec3Dot((VEC3 *)&p, (VEC3 *)&normal);
	FLOAT vn  = D3DXVec3Dot((VEC3 *)v,   (VEC3 *)&normal);
	float t = xpn / vn;

	// ３角形の平面への射影
	D3DXVec4Scale(&p,v,t);
	D3DXVec4Add(&p,&p,x);

	// 射影した点が３角形の内側にあるかテスト
	D3DXVec4Subtract(&d0,&p,&tmp[0]);		//d0 = p - tmp[0];
	D3DXVec4Subtract(&d1,&tmp[1],&tmp[0]);	//d1 = tmp[1]-tmp[0];
	D3DXVec3Cross(&c, (VEC3*)&d0, (VEC3*)&d1);
	if(D3DXVec3Dot(&c, (VEC3 *)&normal) < 0){
		return false;
	}

	D3DXVec4Subtract(&d0,&p,&tmp[1]);		//d0 = p-tmp[1];
	D3DXVec4Subtract(&d1,&tmp[2],&tmp[1]);	//d1 = tmp[2]-tmp[1];
	D3DXVec3Cross(&c, (VEC3*)&d0, (VEC3*)&d1);
	if(D3DXVec3Dot(&c, (VEC3 *)&normal) < 0){
		return false;
	}
	D3DXVec4Subtract(&d0,&p,&tmp[2]);		//d0 = p-tmp[2];
	D3DXVec4Subtract(&d1,&tmp[0],&tmp[2]);	//d1 =tmp[0]-tmp[2];
	D3DXVec3Cross(&c, (VEC3*)&d0, (VEC3*)&d1);
	if(D3DXVec3Dot(&c, (VEC3 *)&normal) < 0) {
		return false;
	}
	if(outvec){
		outvec->x = p.x / p.w;
		outvec->y = p.y / p.w;
		outvec->z = p.z / p.w;
	}
	return true;
}

