
#define __CAMERACTRL_C__

/*--------------------
   組み込みファイル名
  --------------------*/
#include "smpWin.h"
#include "cameractrl.h"

#define MAX_FLOAT	((Float)3.40282346638528860e+38)

#define LEFT_BUTTON		1
#define MIDDLE_BUTTON	2
#define RIGHT_BUTTON	3
#define DOWN			4
#define UP				5

/*--------------
   内部変数宣言
  --------------*/
static CAMERA		*camera = NULL;

static Sint32		nbModel=0;
static MODEL		*model=NULL;

static Sint32		pressButton;	/* 押したボタンの番号 */

static Sint32		width;
static Sint32		height;
static Sint32		centerX;		/* ウインドウの中心Ｘ */
static Sint32		centerY;		/* ウインドウの中心Ｙ */
static Sint32		mouseX;			/* マウスのＸ */
static Sint32		mouseY;			/* マウスのＹ */
static Sint32		mouseOldX;
static Sint32		mouseOldY;

static Float		length;			/* カメラとターゲットとの距離 */
static Float		controlR;		/* カメラコントロールの球の半径 */
static NNS_VECTOR	controlP0;		/* カメラコントロールの球上の任意座標 */
static NNS_VECTOR	controlP1;

/*--------------------------
   現在の回転角度を返します
  --------------------------*/
void GetRotation(NNS_MATRIX *mtx, NNS_VECTOR *rot)
{
	NNS_MATRIX m;
	NNS_VECTOR v;
	Angle32    ay, az;

	v.x = NNM_MTX(*mtx, 0, 0);
	v.y = NNM_MTX(*mtx, 1, 0);
	v.z = NNM_MTX(*mtx, 2, 0);
	ay = nnArcTan2(-v.z, nnSqrt((v.x * v.x) + (v.y * v.y)));
	az = nnArcTan2(v.y, v.x);

	nnMakeRotateYMatrix( &m, -ay);
	nnRotateZMatrix(&m, &m, -az);
	v.x = -NNM_MTX(*mtx, 0, 2);
	v.y = -NNM_MTX(*mtx, 1, 2);
	v.z = -NNM_MTX(*mtx, 2, 2);
	nnTransformVector( &v, &m, &v );

	rot->x = NNM_A32toDEG( nnArcTan2(v.y, -v.z) );
	rot->y = NNM_A32toDEG( ay );
	rot->z = NNM_A32toDEG( az );
}

/*----------------------------------
   コントロールベクトルを計算します
  ----------------------------------*/
static void CalcContVector(NNS_VECTOR *p)
{
	Float r2, r, len;

	r2 = p->x*p->x + p->y*p->y;
	r = nnSqrt(r2);
	if(controlR > r){
		p->z = nnSqrt(controlR*controlR - r2);
		len = nnSqrt(r2 + p->z*p->z);
		p->x = p->x / len;
		p->y = p->y / len;
		p->z = p->z / len;
	} else{
		p->x = p->x / r;
		p->y = p->y / r;
		p->z = 0.0f;
	}
}

/*----------------------
   カメラの回転をします
  ----------------------*/
static void OribitCamera(void)
{
	Float      cosQ,
			   speed;
	NNS_VECTOR sp0, sp1,
			   axis, tmp,
			  *rot;
	NNS_MATRIX m;

	controlP0.x = controlP1.x;
	controlP0.y = controlP1.y;
	controlP0.z = controlP1.z;
	controlP1.x =(Float)(mouseX - centerX);
	controlP1.y =(Float)(mouseY - centerY);
	/* コントロールベクトルを計算します */
	CalcContVector(&controlP1);

	sp0 = controlP0;
	sp1 = controlP1;

	cosQ = sp0.x*sp1.x + sp0.y*sp1.y + sp0.z*sp1.z;
	if(cosQ > 0.999999f){
		return;
	}
	/* 任意軸を求める */
	nnCrossProductVector( &axis, &sp0, &sp1 );
	nnNormalizeVector(&axis, &axis);

	/* カメラのマトリクス */
	speed = nnSqrt( (Float)((mouseX-mouseOldX)*(mouseX-mouseOldX) +
							(mouseY-mouseOldY)*(mouseY-mouseOldY)));

	rot = &camera->Rotation;
	nnMakeRotateXYZMatrix( &m, NNM_DEGtoA32(rot->x), NNM_DEGtoA32(rot->y), NNM_DEGtoA32(rot->z));
	{
		NNS_QUATERNION q;
		/*nnMakeRotateAxisQuaternion( &q, axis.x, -axis.y, axis.z, NNM_DEGtoA32(-speed*0.5f));*/
		/*nnMakeRotateAxisQuaternion( &q, axis.x, -axis.y, axis.z, NNM_DEGtoA32(speed*0.5f));*/
		nnMakeRotateAxisQuaternion( &q, axis.x, -axis.y, axis.z, NNM_DEGtoA32(speed*0.5f*0.5f));
		nnQuaternionMatrix( &m, &m, &q );
	}
	/* カメラの角度を求める */
	GetRotation(&m, rot);
	/* カメラの座標を求める */
	tmp.x = 0.0f;
	tmp.y = 0.0f;
	tmp.z = length;
	nnTransformVector( &camera->Position, &m, &tmp);
	camera->Position.x += camera->Interest.x;
	camera->Position.y += camera->Interest.y;
	camera->Position.z += camera->Interest.z;
}

/*--------------------------
   カメラを前後に移動します
  --------------------------*/
static void DollyCamera(void)
{
	NNS_MATRIX  m;
	Float       speed;
	NNS_VECTOR *rot, tmp;

	speed = (Float)(mouseY - mouseOldY)*0.5f;

	length += -speed;
	if(length < 0.0f){
		/*length = -length;*/
		length = 0.01f;
	}
	rot = &camera->Rotation;
	nnMakeTranslateMatrix( &m, camera->Interest.x, camera->Interest.y, camera->Interest.z );
	nnRotateXYZMatrix( &m, &m, NNM_DEGtoA32(rot->x), NNM_DEGtoA32(rot->y), NNM_DEGtoA32(rot->z));
	tmp.x = 0.0f;
	tmp.y = 0.0f;
	tmp.z = length;
	nnTransformVector( &camera->Position, &m, &tmp );
}

/*--------------------------
   カメラを左右に移動します
  --------------------------*/
static void DragCamera(void)
{
	Float      scale;
	NNS_VECTOR p, *rot;
	NNS_MATRIX m;
	CAMERA    *cam;
	cam = camera;

	scale = nnTan(NNM_DEGtoA32(cam->FieldOfView*0.5f)) * length / ((Float)height*0.5f);

	p.x = (Float)-(mouseX-mouseOldX) * scale;
	p.y = (Float) (mouseY-mouseOldY) * scale;
	p.z = 0.0f;

	rot = &cam->Rotation;
	nnMakeRotateXYZMatrix( &m, NNM_DEGtoA32(rot->x), NNM_DEGtoA32(rot->y), NNM_DEGtoA32(rot->z));
	nnTransformVector( &p, &m, &p);
	cam->Interest.x += p.x;
	cam->Interest.y += p.y;
	cam->Interest.z += p.z;
	cam->Position.x += p.x;
	cam->Position.y += p.y;
	cam->Position.z += p.z;
}

/*-
  -*/
static void ModelGetMinMax(MODEL *mdl, NNS_VECTOR *min, NNS_VECTOR *max)
{
	NNS_OBJECT *pObj;
	NNS_NODE   *pNd;
	NNS_MATRIX *pMtx;
	NNS_VECTOR  p[2];
	Sint32      i, j;

	pObj = mdl->pObject;
	pNd  = pObj->pNodeList;
	pMtx = mdl->pMtxList;
	for(i=0; i<pObj->nNode; i++, pNd++, pMtx++){
		p[0].x = pNd->Center.x + pNd->BoundingBoxX;
		p[0].y = pNd->Center.y + pNd->BoundingBoxY;
		p[0].z = pNd->Center.z + pNd->BoundingBoxZ;
		p[1].x = pNd->Center.x - pNd->BoundingBoxX;
		p[1].y = pNd->Center.y - pNd->BoundingBoxY;
		p[1].z = pNd->Center.z - pNd->BoundingBoxZ;
		nnTransformVector( &p[0], pMtx, &p[0]);
		nnTransformVector( &p[1], pMtx, &p[1]);
		for(j=0; j<2; j++){
			if(min->x > p[j].x) min->x = p[j].x;
			if(max->x < p[j].x) max->x = p[j].x;

			if(min->y > p[j].y) min->y = p[j].y;
			if(max->y < p[j].y) max->y = p[j].y;

			if(min->z > p[j].z) min->z = p[j].z;
			if(max->z < p[j].z) max->z = p[j].z;
		}
	}
}

static void FrameInCamera(void)
{
	CAMERA    *cam;
	NNS_MATRIX m;
	NNS_VECTOR mdlMin, mdlMax,
		      *rot, tmp;
	Float      r;
	Sint32     i;

	if(nbModel == 0){
		return;
	}

	cam = camera;

	mdlMax.x = -MAX_FLOAT;
	mdlMax.y = -MAX_FLOAT;
	mdlMax.z = -MAX_FLOAT;
	mdlMin.x =  MAX_FLOAT;
	mdlMin.y =  MAX_FLOAT;
	mdlMin.z =  MAX_FLOAT;

	for(i=0; i<nbModel; i++){
		ModelGetMinMax(&model[i], &mdlMin, &mdlMax);
	}

	cam->Interest.x = (mdlMax.x + mdlMin.x) * 0.5f;
	cam->Interest.y = (mdlMax.y + mdlMin.y) * 0.5f;
	cam->Interest.z = (mdlMax.z + mdlMin.z) * 0.5f;

	r = nnDistanceVector(&cam->Interest, &mdlMax);
	length = r / nnSin(NNM_DEGtoA32(cam->FieldOfView*0.5f));

	rot = &cam->Rotation;
	nnMakeTranslateMatrix( &m, cam->Interest.x, cam->Interest.y, cam->Interest.z );
	nnRotateXYZMatrix( &m, &m, NNM_DEGtoA32(rot->x), NNM_DEGtoA32(rot->y), NNM_DEGtoA32(rot->z));
	tmp.x = 0.0f;
	tmp.y = 0.0f;
	tmp.z = length;
	nnTransformVector( &camera->Position, &m, &tmp );
}

/*-
  -*/
static void ReshapeWindow(int w, int h)
{
	Float wf, hf;
	wf = (Float) w;
	hf = (Float) h;
	width	= w;
	height	= h;
	if(w >= h){
		controlR = hf * 0.5f;
	} else{
		controlR = wf * 0.5f;
	}
	centerX		= w/2;
	centerY		= h/2;
}
static void Reshape(int w, int h)
{
	Float wf, hf;
	wf = (Float) w;
	hf = (Float) h;
	ReshapeWindow(w, h);
	/*
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	nnMakePerspectiveMatrix( &gProjmtx, NNM_DEGtoA32( camera->FieldOfView ),
							 wf/hf, camera->NearPlane, camera->FarPlane );
	 */
}

/*-
  -*/
static void Mouse(int button, int state, int x, int y)
{
	Sint32 tmp;
	switch(button){
		case LEFT_BUTTON:	tmp	= LEFT_BUTTON;		break;
		case MIDDLE_BUTTON:	tmp	= MIDDLE_BUTTON;	break;
		case RIGHT_BUTTON:	tmp	= RIGHT_BUTTON;		break;
		default:			tmp = -1;				break;
	}
	if(state == 0){
		/* ボタンを押した */
		if(tmp == -1){
			/* 押されてない */
			return;
		}
		pressButton = tmp;
		/* イベントの発生 */
		/* 初期化 */
		mouseOldX = x;
		mouseOldY = y;
		controlP1.x =(Float)(mouseOldX - centerX);
		controlP1.y =(Float)(mouseOldY - centerY);
		/* コントロールベクトルを計算します */
		CalcContVector(&controlP1);
	} else{
		/* ボタンを離した */
		if(tmp == pressButton){
			/* イベントの終了 */
			pressButton = -1;
		}
	}
	/*printf("%d %d %d %d\n", button, state, x, y);*/

	{
		static Sint32 BT = 0;
		if(state == 0)	BT |= (1<<tmp);
		else			BT ^= (1<<tmp);
		if(BT == ((1<<LEFT_BUTTON) | (1<<MIDDLE_BUTTON))){
			FrameInCamera();
		}
	}
}

/*-
  -*/
static void Motion(int x, int y)
{
	if(pressButton == -1){
		return;
	}

	mouseX = x;
	mouseY = y;

	switch(pressButton){
		case LEFT_BUTTON:
			/* カメラの回転をします */
			OribitCamera();
			break;
		case MIDDLE_BUTTON:
			/* カメラを左右に移動します */
			DragCamera();
			break;
		case RIGHT_BUTTON:
			/* カメラを前後に移動します */
			DollyCamera();
			break;
		default:
			break;
	}

	mouseOldX = mouseX;
	mouseOldY = mouseY;

	/*printf("%d %d\n", x, y);*/
}

/*----------------------------
   カメラコントロールの初期化
  ----------------------------*/
static void CameraCtrlInit(CAMERA *cam, Sint32 w, Sint32 h)
{
	camera		= cam;

	pressButton	= -1;

	width		= 0;
	height		= 0;
	centerX		= 0;
	centerY		= 0;
	mouseX		= 0;
	mouseY		= 0;
	mouseOldX	= 0;
	mouseOldY	= 0;

	length		= 0.0f;
	controlR	= 0.0f;
	controlP0.x	= 0.0f;
	controlP0.y	= 0.0f;
	controlP0.z	= 0.0f;
	controlP1.x	= 0.0f;
	controlP1.y	= 0.0f;
	controlP1.z	= 0.0f;

//	{
//		GLint tmpview[4];
//		glGetIntegerv( GL_VIEWPORT, tmpview );
//		ReshapeWindow(tmpview[2], tmpview[3]);
//	}
	ReshapeWindow(w, h);
}

/*----------------------
   カメラをセットします
  ----------------------*/
void CameraCtrlSetCamera(CAMERA *cam, Sint32 w, Sint32 h)
{
	if(cam != NULL){
		CameraCtrlInit(cam, w, h);
		//ReshapeFunc(Reshape);
		//MouseFunc(Mouse);
		//MotionFunc(Motion);
		/* カメラとターゲットとの距離 */
		length = nnDistanceVector(&camera->Position, &camera->Interest);
	} else{
		//ReshapeFunc(NULL);
		//MouseFunc(NULL);
		//MotionFunc(NULL);
	}
}

/*------------------------------------------
   カメラコントロールにモデルをセットします
  ------------------------------------------*/
void CameraCtrlSetModel(Sint32 nbMdl, MODEL *mdl)
{
	nbModel = nbMdl;
	model = mdl;
}

/*-
  -*/
void CameraCtrlMain(unsigned long evtType, void *ptr)
{
	SMPWINEvent *evt = (SMPWINEvent*)ptr;

	if(camera == NULL) return;

	switch(evtType){
		case SMPWIN_TYPE_SIZE:				Reshape(evt->m_width, evt->m_height);					break;

		case SMPWIN_TYPE_LEFTBUTTON_DOWN:	Mouse(LEFT_BUTTON, 0, evt->m_mouseX, evt->m_mouseY);	break;
		case SMPWIN_TYPE_LEFTBUTTON_UP:		Mouse(LEFT_BUTTON, 1, evt->m_mouseX, evt->m_mouseY);	break;

		case SMPWIN_TYPE_MIDDLEBUTTON_DOWN:	Mouse(MIDDLE_BUTTON, 0, evt->m_mouseX, evt->m_mouseY);	break;
		case SMPWIN_TYPE_MIDDLEBUTTON_UP:	Mouse(MIDDLE_BUTTON, 1, evt->m_mouseX, evt->m_mouseY);	break;

		case SMPWIN_TYPE_RIGHTBUTTON_DOWN:	Mouse(RIGHT_BUTTON, 0, evt->m_mouseX, evt->m_mouseY);	break;
		case SMPWIN_TYPE_RIGHTBUTTON_UP:	Mouse(RIGHT_BUTTON, 1, evt->m_mouseX, evt->m_mouseY);	break;

		case SMPWIN_TYPE_MOUSE_MOVE:		Motion(evt->m_mouseX, evt->m_mouseY);					break;
		default:
			break;
	}
}
