/************************************************************
  Ninja Sample
  COPYRIGHT (C) SEGA ENTERPRISES, LTD.

  タイトル ： ポリゴンモデルに当り判定をつける
  編集履歴 ： 1998-05-13 製作開始
			  1998-06-19 SET5対応
			  2000-03-15 Ninja2と新コリジョンに対応
************************************************************/

#include <shinobi.h>
#include "Nj.h"

/* グローバルセクション */



/* 物体の個数 */
#define OBJECT_NUM			40
#define DEFAULT_OBJECT_NUM	18

/* 物体の並進速度の大きさ（固定値） */
#define	VELOCITY			0.15f
/* 物体の回転速度の大きさ */
#define	ANG_VEL				0x300

/* コリジョン用定義 */
#define COLLISION_WEIGHT    40

/* コリジョン押し出し量 */
#define	COLLISION_PUSH_STEP	0.005f
/* コリジョン押し出し回数 */
#define	COLLISION_PUSH_CNT	10

/* 境界枠用定義 */
#define	BOUNDARY_SX			30.0f
#define	BOUNDARY_SY			30.0f
#define	BOUNDARY_SZ			8.0f
#define	BOUNDARY_HX			(BOUNDARY_SX*0.5f)
#define	BOUNDARY_HY			(BOUNDARY_SY*0.5f)
#define	BOUNDARY_HZ			(BOUNDARY_SZ*0.5f)
#define BOUNDARY_COLOR		0xffffffff

/* 境界での押し出し量 */
#define	BOUNDARY_PUSH		0.01f


/* カメラの初期位置 */
#define CAMERA_POS_Z		35.0f
#define	CAMERA_MOVE_SPEED	0.5f
#define	CAMERA_ROT_SPEED	0x180


/* 外部定義のモデルデータ */
#define	MODEL_CUBE		model_col_cube_cube
#define	MODEL_SPHERE	model_col_sphere_sphere
#define	MODEL_CAPSULE1	model_col_capsule1_capsule1
#define	MODEL_CAPSULE2	model_col_capsule2_capsule2
#define	MODEL_CAPSULE3	model_col_capsule3_capsule3

extern NJS_CNK_MODEL	MODEL_CUBE[];
extern NJS_CNK_MODEL	MODEL_SPHERE[];
extern NJS_CNK_MODEL	MODEL_CAPSULE1[];
extern NJS_CNK_MODEL	MODEL_CAPSULE2[];
extern NJS_CNK_MODEL	MODEL_CAPSULE3[];



/* モデル用構造体 */

/* 球  */
typedef struct {

	Float			r;
	NJS_SPHERE		coli;

} TMODEL_SPHERE;

/*	旧ボックス	*/
typedef struct {

	Float			size1;
	Float			size2;
	Float			size3;
	NJS_BOX			coli;

} TMODEL_BOX;

/*	新ボックス	*/
typedef struct {

	Float			size1;
	Float			size2;
	Float			size3;
	NJS_PARAHEXA	coli;

} TMODEL_BOX2;

/*	カプセル	*/
typedef struct {

	Float		r;
	Float		l;
	NJS_CAPSULE coli;

} TMODEL_CAPSULE;

/* モデル用共用体 */
typedef union {

	TMODEL_SPHERE  sphere;
	TMODEL_BOX     box;
	TMODEL_BOX2    box2;
	TMODEL_CAPSULE capsule;

} TMY_MODEL;

/* オブジェクト用構造体 */
enum TYPE {
	TYPE_SPHERE,
	TYPE_BOX,
	TYPE_CAPSULE,
	TYPE_END
};

typedef struct {
	enum TYPE	type;
	
	NJS_POINT3	pos;
	NJS_POINT3	v;
	
	Angle		angx;
	Angle		angy;
	Angle		angz;
	
	Angle		angvx;
	Angle		angvy;
	Angle		angvz;
	
	Float		weight;
	
	Bool		collided;
	
	TMY_MODEL	model;
} TMY_OBJECT;



static TMY_OBJECT	G_Object[OBJECT_NUM];


/* ポーズ判定用フラグ */
Bool	g_bPause;

/* 六面体コリジョン使用フラグ */
Bool	g_bOld;

/* Z 方向無視フラグ	*/
Bool	g_bIgnoreZ;

/* 描画オブジェクト数	*/
int		g_nbObject;


/*
	カメラが回転するときに動いて見えない点の世界座標:
	見たい対象の中心に設定してください。
*/
static const NJS_POINT3		FixPoint = { 0.0f, 0.0f, 0.0f };


/*	関数のプロトタイプ宣言	*/
void	ActivePhase( void );
void	PausePhase( void );

void	ExecObjects( void );
void	CollisionPhase( void );

void	CreateObject( int obj_num );

void	DrawWorld( void );
void	DrawObject( void );
void	DrawBoundary( void );
void	PrintInformation( void );
void	DrawLine( Float x1, Float y1, Float z1, Float x2, Float y2, Float z2, Uint32 color );

void	SetACollision( TMY_OBJECT *obj );
void 	ClearCollidedFlags( void );
void	CollisionInteraction( void );
void	CollisionWithBoundary( void );
int		CollisionObject( TMY_OBJECT *obj1, TMY_OBJECT *obj2 );
void	CollisionEffect( TMY_OBJECT *obj1, TMY_OBJECT *obj2 );

void	ProcessController( void );


/*
	キーリピート処理関数群
*/
#define	KEY_FIRST_INTERVAL	30
#define	KEY_REPEAT_INTERVAL	4
int		g_nKeyTimer;
Bool	g_bKeyFirst;
Bool	g_bKeyTouch;

void	InitKeyRepeat( void );
void	ProcessKeyRepeat( void );
Bool	IsKeyRepeat( void );

/**********************************************************************

	UserInit --- 初期化関数

**********************************************************************/

Sint32 UserInit(void)
{
	g_bPause	= FALSE;
	g_bOld		= FALSE;
	g_bIgnoreZ	= FALSE;
	g_nbObject	= DEFAULT_OBJECT_NUM;
	
	InitKeyRepeat();
	
	njPrintSize( 10 );
	
	/* オブジェクトの生成 */
	CreateObject(OBJECT_NUM);
	
	/* テキストの色を指定 */
	njPrintColor( 0xffc0c0c0 );
	
	/* カメラを移動 */
	njTranslateCameraEx( 0.0f, 0.0f, CAMERA_POS_Z );
	
	return USER_CONTINUE;
}


/**********************************************************************

	CreateObject --- オブジェクトを生成する

**********************************************************************/

void CreateObject(int obj_num)
{
	NJS_POINT3		pos;
	int				counter;
	TMY_OBJECT		*obj;
	
	/* オブジェクトデータの初期化 */
	obj		= &G_Object[0];
	pos.x	= -BOUNDARY_HX * 0.8f;
	pos.y	=  BOUNDARY_HY * 0.8f;
	for (counter = 0; counter < obj_num; counter++) {
		Float				f;
		
		obj->type		= counter % TYPE_END;
		
		
		obj->weight		= COLLISION_WEIGHT;
		obj->collided	= FALSE;
		
		obj->pos.x		= pos.x;
		obj->pos.y		= pos.y;
		obj->pos.z		=  0.0f;
		
		obj->angx		= (Angle)((njRandom()*2.0f-1.0f) * 0x10000);
		obj->angy		= (Angle)((njRandom()*2.0f-1.0f) * 0x10000);
		obj->angz		= (Angle)((njRandom()*2.0f-1.0f) * 0x10000);
		
		obj->v.x		= njRandom() - 0.5f;
		obj->v.y		= njRandom() - 0.5f;
		obj->v.z		= njRandom() - 0.5f;
		f				= VELOCITY / sqrtf( obj->v.x*obj->v.x + obj->v.y*obj->v.y + obj->v.z*obj->v.z );
		obj->v.x		*= f;
		obj->v.y		*= f;
		obj->v.z		*= f;
		
		obj->angvx		= (Angle)((njRandom()*2.0f-1.0f) * ANG_VEL);
		obj->angvy		= (Angle)((njRandom()*2.0f-1.0f) * ANG_VEL);
		obj->angvz		= (Angle)((njRandom()*2.0f-1.0f) * ANG_VEL);
		
		switch(obj->type) {
		case TYPE_SPHERE:
			/* 球データの初期化 */
			obj->model.sphere.r		= 1.5F;
			break;

		case TYPE_BOX:
			/* ボックスデータの初期化 */
			obj->model.box.size1	= 6.0F;
			obj->model.box.size2	= 4.0F;
			obj->model.box.size3	= 1.4F;
			break;

		case TYPE_CAPSULE:
			/* カプセルデータの初期化 */
			obj->model.capsule.r	= 1.3F;
			obj->model.capsule.l	= 5.0F;
			break;
		
		default:
			break;
		}
		pos.x	+= BOUNDARY_HX * 0.4f;
		if (pos.x > BOUNDARY_HX * 0.8f) {
			pos.x	= -BOUNDARY_HX * 0.8f;
			pos.y	-= BOUNDARY_HY * 0.4f;
		}
		
		obj++;
	}
}


/**********************************************************************

	UserMain --- メイン関数

**********************************************************************/

Sint32 UserMain(void)
{
	/* コントローラ入力処理 */
	ProcessController();
	
	/* ライトを設定 */
	njCnkSetEasyLight( 0.0f, 0.0f, -1.0f );
	njCnkSetEasyLightIntensity( 0.8f, 0.2f );
	njCnkSetEasyLightColor( 1.0f, 1.0f, 1.0f );
	
	/* 情報を表示 */
	PrintInformation();
	
	/* ポーズ状態によって処理を切り替える */
	if (!g_bPause) {
		ActivePhase();
	}
	else {
		PausePhase();
	}
	
	return USER_CONTINUE;
}

/**********************************************************************

	PrintInformation --- 情報を表示する

**********************************************************************/

void PrintInformation( void )
{
#if DEBUG
	/* タイトルの表示 */
	njPrintC(NJM_LOCATION(18, 4), "Ninja Sample COLLISION");
	
	/* オブジェクト数の表示 */
	njPrint(NJM_LOCATION(2, 19), "%d objects", g_nbObject);
	
	/* BOXコリジョンの種類を表示 */
	if ( g_bOld ) {
		njPrintC(NJM_LOCATION(2, 20), "OLD BOX  (A BUTTON)");
	}
	else {
		njPrintC(NJM_LOCATION(2, 20), "NEW BOX  (A BUTTON)");
	}
	
	/* Z 方向を有効にするかどうかを表示する */
	if ( g_bIgnoreZ ) {
		njPrintC(NJM_LOCATION(2, 21), "IGNORE Z (Y BUTTON)");
	}
	else {
		njPrintC(NJM_LOCATION(2, 21), "USE Z    (Y BUTTON)" );
	}
	
	njPrintC(NJM_LOCATION(2, 23), "X+UDLR:ROTATE" );
	njPrintC(NJM_LOCATION(2, 24), "ST:RESET POS B:PAUSE" );
	njPrintC(NJM_LOCATION(2, 25), "BL:DEC OBJ  BR:INC OBJ" );
#endif
}

/**********************************************************************

	ProcessController --- コントローラを処理する

**********************************************************************/

void ProcessController( void )
{
	if (Per->press & PDD_DGT_ST) {
		njUnitMatrix(NULL);
		njTranslateCameraEx( 0.0f, 0.0f, CAMERA_POS_Z );
	}
	if (Per->press & PDD_DGT_TA) {
		g_bOld = !g_bOld;
	}
	if (Per->press & PDD_DGT_TB) {
		g_bPause = !g_bPause;
	}
	if (Per->press & PDD_DGT_TY) {
		g_bIgnoreZ = !g_bIgnoreZ;
	}
	
	if (Per->on & PDD_DGT_TL) {
		if ( IsKeyRepeat() ) {
			if ( g_nbObject > 1 ) {
				g_nbObject--;
			}
		}
	}
	if (Per->on & PDD_DGT_TR) {
		if ( IsKeyRepeat() ) {
			if ( g_nbObject < OBJECT_NUM ) {
				g_nbObject++;
			}
		}
	}
	
	ProcessKeyRepeat();
	
	if ( Per->on & PDD_DGT_TX ) {
		NJS_POINT3					pos1;
		NJS_POINT3					pos2;
		
		// 回転
		
		njCalcPoint(NULL, &FixPoint, &pos1);
		
		if ( Per->on & PDD_DGT_KL ) {
			njRotateYCameraEx( CAMERA_ROT_SPEED );
		}
		if ( Per->on & PDD_DGT_KR ) {
			njRotateYCameraEx( -CAMERA_ROT_SPEED );
		}
		if ( Per->on & PDD_DGT_KU ) {
			njRotateXCameraEx(  CAMERA_ROT_SPEED );
		}
		if ( Per->on & PDD_DGT_KD ) {
			njRotateXCameraEx( -CAMERA_ROT_SPEED );
		}
		if ( Per->x1 > 64 ) {
			njRotateZCameraEx( CAMERA_ROT_SPEED );
		}
		if ( Per->x1 < -64 ) {
			njRotateZCameraEx( -CAMERA_ROT_SPEED );
		}
		njCalcPoint(NULL, &FixPoint, &pos2);
		
		/*
			回転によって、世界座標表現で FixPoint の点のカメラから
			見た位置を動かさないようにカメラを平行移動する。
		*/
		njTranslateCameraEx( pos2.x-pos1.x, pos2.y-pos1.y, pos2.z-pos1.z );
	}
	else {
		// 平行移動
		Float			x=0.f;
		Float			y=0.f;
		Float			z=0.f;
		
		if ( Per->on & PDD_DGT_KL ) {
			x	= -CAMERA_MOVE_SPEED;
		}
		if ( Per->on & PDD_DGT_KR ) {
			x	=  CAMERA_MOVE_SPEED;
		}
		if ( Per->on & PDD_DGT_KU ) {
			y	= -CAMERA_MOVE_SPEED;
		}
		if ( Per->on & PDD_DGT_KD ) {
			y	=  CAMERA_MOVE_SPEED;
		}
		if ( Per->y1 > 64 ) {
			z	=  CAMERA_MOVE_SPEED;
		}
		if ( Per->y1 < -64 ) {
			z	= -CAMERA_MOVE_SPEED;
		}
		njTranslateCameraEx( x, y, z );
	}
}

void InitKeyRepeat( void )
{
	g_nKeyTimer		= 0;
	g_bKeyFirst		= TRUE;
	g_bKeyTouch		= FALSE;
}

void ProcessKeyRepeat( void )
{
	if ( !g_bKeyTouch ) {
		g_nKeyTimer		= 0;
		g_bKeyFirst		= TRUE;
	}
	else {
		if ( g_nKeyTimer != 0 ) {
			g_nKeyTimer--;
			g_bKeyFirst		= FALSE;
		}
		else {
			g_bKeyFirst		= TRUE;
		}
		
		g_bKeyTouch		= FALSE;
	}
}

Bool IsKeyRepeat( void )
{
	g_bKeyTouch		= TRUE;
	
	if ( g_nKeyTimer == 0 ) {
		if ( g_bKeyFirst ) {
			g_nKeyTimer	= KEY_FIRST_INTERVAL;
		}
		else {
			g_nKeyTimer	= KEY_REPEAT_INTERVAL;
		}
		
		return	TRUE;
	}
	else {
		return	FALSE;
	}
}

/**********************************************************************

	ActivePhase --- アクティブ時処理関数

**********************************************************************/

void ActivePhase(void)
{
	/* オブジェクトのデータ処理 */
	ExecObjects();

	/* コリジョン処理 */
	CollisionPhase();

	/* 描画処理 */
	DrawWorld();
}


/**********************************************************************

	PausePhase --- ポーズ時処理関数

**********************************************************************/

void PausePhase(void) 
{
	/* 描画処理 */
	DrawWorld();
	
	/* ポーズのお知らせ */
#if DEBUG
	njPrintC(NJM_LOCATION(25, 30), "PAUSE");
#endif
}


/**********************************************************************

	DrawWorld --- 世界全体を表示

	argument: なし

**********************************************************************/

void DrawWorld(void)
{
	njPushMatrixEx();
	
	/* 枠の描画 */
	DrawBoundary();
	
	/* オブジェクトの描画 */
	DrawObject();
	
	njPopMatrixEx();
}

/**********************************************************************

	DrawObject --- オブジェクトの描画処理

	argument: なし

**********************************************************************/

void DrawObject(void)
{
	TMY_OBJECT		*obj = G_Object;
	int				counter;
	Float			inten			= 1.0f;

	for (counter = 0; counter < g_nbObject; counter++) {
		switch(obj->type) {
		case TYPE_SPHERE:
			njCnkSetEasyLightColor( inten, 0.0f, 0.0f );
			njPushMatrixEx();
				njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
				njScale(NULL, obj->model.sphere.r, obj->model.sphere.r, obj->model.sphere.r);
				njCnkEasyDrawModel(MODEL_SPHERE);
			njPopMatrixEx();
			break;

		case TYPE_BOX:
			njCnkSetEasyLightColor( 0.0f, inten, 0.0f );
			njPushMatrixEx();
				njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
				njRotateX(NULL, obj->angx);
				njRotateY(NULL, obj->angy);
				njRotateZ(NULL, obj->angz);
				njScale(NULL, obj->model.box.size1, obj->model.box.size2, obj->model.box.size3);
				njTranslate(NULL, -0.5f, -0.5f, -0.5f);
				njCnkEasyDrawModel(MODEL_CUBE);
			njPopMatrixEx();
			break;
		
		case TYPE_CAPSULE: {
			Float			r;
			Float			l;
			
			r	= obj->model.capsule.r;
			l	= obj->model.capsule.l;
			
			njCnkSetEasyLightColor( 0.0f, 0.0f, inten );
			njPushMatrixEx();
				njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
				
				njRotateX(NULL, obj->angx);
				njRotateY(NULL, obj->angy);
				njRotateZ(NULL, obj->angz);
				
				njRotateX(NULL, 0x4000);
				
				njPushMatrixEx();
					njTranslate(NULL, 0.0f, 0.0f, l*0.5f);
					njScale(NULL, r, r, r);
					njCnkEasyDrawModel(MODEL_CAPSULE1);
				njPopMatrixEx();
				
				njPushMatrixEx();
					njScale(NULL, r, r, l);
					njTranslate(NULL, 0.0f, 0.0f, 0.5f);
					njCnkEasyDrawModel(MODEL_CAPSULE2);
				njPopMatrixEx();
				
				njPushMatrixEx();
					njTranslate(NULL, 0.0f, 0.0f, -l*0.5f);
					njScale(NULL, r, r, r);
					njCnkEasyDrawModel(MODEL_CAPSULE3);
				njPopMatrixEx();
				
			njPopMatrixEx();
			break;
		}
		}
		obj++;
	}
}

/**********************************************************************

	DrawBoundary --- 枠の描画処理

	argument: なし

**********************************************************************/

void DrawBoundary(void)
{
	/* 上の面 */
	DrawLine( -BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ,  BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ,  BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ, -BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine( -BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ, -BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	
	/* 下の面 */
	DrawLine( -BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ,  BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ,  BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, -BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine( -BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, -BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	
	/* 周囲 */
	DrawLine( -BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ, -BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX,  BOUNDARY_HY,  BOUNDARY_HZ,  BOUNDARY_HX, -BOUNDARY_HY,  BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine(  BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ,  BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
	DrawLine( -BOUNDARY_HX,  BOUNDARY_HY, -BOUNDARY_HZ, -BOUNDARY_HX, -BOUNDARY_HY, -BOUNDARY_HZ, BOUNDARY_COLOR );
}

/**********************************************************************

	ExecObjects --- オブジェクトのデータ処理

	argument: なし

**********************************************************************/

void ExecObjects( void )
{
	TMY_OBJECT		*obj = G_Object;
	int				counter;

	for (counter = 0; counter < g_nbObject; counter++) {
		/* オブジェクトの移動処理 */
		obj->pos.x += obj->v.x;
		obj->pos.y += obj->v.y;
		if ( !g_bIgnoreZ ) {
			obj->pos.z		+= obj->v.z;
		}
		else {
			obj->pos.z		= 0.0f;
		}
		
		obj->angx		+= obj->angvx;
		obj->angy		+= obj->angvy;
		obj->angz		+= obj->angvz;
		
		/* コリジョン用データの作成 */
		SetACollision( obj );
		
		obj++;
	}
}


/**********************************************************************

	SetACollision --- 指定されたオブジェクトのコリジョンを設定する

	argument:	TMY_OBJECT	*obj	コリジョンを設定するオブジェクト

**********************************************************************/

void SetACollision( TMY_OBJECT *obj )
{
	njPushMatrixEx();
	
	switch(obj->type) {

	case TYPE_SPHERE: {
		obj->model.sphere.coli.c = obj->pos;
		obj->model.sphere.coli.r = obj->model.sphere.r;
		break;
	}
	
	case TYPE_CAPSULE: {
		static const NJS_POINT3		c1	= { 0.0f, 0.5f, 0.0f };
		static const NJS_POINT3		c2	= { 0.0f, -0.5f, 0.0f };
		
		njUnitMatrix(NULL);
		njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
		njRotateX(NULL, obj->angx);
		njRotateY(NULL, obj->angy);
		njRotateZ(NULL, obj->angz);
		njScale(NULL, 1.0f, obj->model.capsule.l, 1.0f );
		
		njCalcPoint(NULL, &c1, &obj->model.capsule.coli.c1 );
		njCalcPoint(NULL, &c2, &obj->model.capsule.coli.c2 );
		
		obj->model.capsule.coli.r  = obj->model.capsule.r;
		break;
	}
	
	case TYPE_BOX: {
		if ( g_bOld ) {
			int								i;
			static const NJS_POINT3			pos[8]	= {
				{-0.5f,  0.5f,  0.5f},
				{-0.5f,  0.5f, -0.5f},
				{ 0.5f,  0.5f, -0.5f},
				{ 0.5f,  0.5f,  0.5f},
				{-0.5f, -0.5f,  0.5f},
				{-0.5f, -0.5f, -0.5f},
				{ 0.5f, -0.5f, -0.5f},
				{ 0.5f, -0.5f,  0.5f}
			};
			
			njUnitMatrix(NULL);
			njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
			njRotateX(NULL, obj->angx);
			njRotateY(NULL, obj->angy);
			njRotateZ(NULL, obj->angz);
			
			njScale(NULL, obj->model.box.size1, obj->model.box.size2, obj->model.box.size3);
			
			for ( i = 0; i < 8; i++ ) {
				njCalcPoint(NULL, &pos[i], &obj->model.box.coli.v[i] );
			}
		}
		else {
			static const NJS_POINT3			pos0	= {-0.5f, -0.5f, -0.5f};
			static const NJS_POINT3			pos1	= {1.0f, 0.0f, 0.0f};
			static const NJS_POINT3			pos2	= {0.0f, 1.0f, 0.0f};
			static const NJS_POINT3			pos3	= {0.0f, 0.0f, 1.0f};
			
			njUnitMatrix(NULL);
			njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
			njRotateX(NULL, obj->angx);
			njRotateY(NULL, obj->angy);
			njRotateZ(NULL, obj->angz);
			
			njScale(NULL, obj->model.box.size1, obj->model.box.size2, obj->model.box.size3);
			
			njCalcPoint(NULL, &pos0, &obj->model.box2.coli.p );
			njCalcVector(NULL, &pos1, &obj->model.box2.coli.v[0] );
			njCalcVector(NULL, &pos2, &obj->model.box2.coli.v[1] );
			njCalcVector(NULL, &pos3, &obj->model.box2.coli.v[2] );
		}
		break;
	}
	
	default:
		break;
	}
	
	njPopMatrixEx();
}

/**********************************************************************

	CollisionPhase --- オブジェクトの当たり判定処理

	argument: なし

**********************************************************************/

void CollisionPhase(void)
{
	/* 衝突フラグをクリア */
	ClearCollidedFlags();
	
	/*	壁とオブジェクトの衝突判定	*/
	CollisionWithBoundary();
	
	/*	オブジェクト同士の当たり判定処理	*/
	CollisionInteraction();
}

/**********************************************************************

	ClearCollidedFlags --- 全てのオブジェクトの衝突フラグをクリアする。

	argument: なし

**********************************************************************/

void ClearCollidedFlags( void )
{
	int				cnt_src;
	TMY_OBJECT		*obj;
	
	obj		= &G_Object[0];
	for (cnt_src = 0; cnt_src < g_nbObject; cnt_src++) {
		obj->collided	= FALSE;
		obj++;
	}
}

/**********************************************************************

	CollisionInteraction --- オブジェクト同士の当たり判定処理

	argument: なし

**********************************************************************/

void CollisionInteraction( void )
{
	int				cnt_src;
	int				cnt_dest;
	TMY_OBJECT		*obj;
	
	
	/* 全てのオブジェクトに対して当たり判定を行う */
	for (cnt_src = 0; cnt_src < g_nbObject; cnt_src++) {
		for (cnt_dest = cnt_src + 1; cnt_dest < g_nbObject; cnt_dest++) {
			if (CollisionObject(&G_Object[cnt_src], &G_Object[cnt_dest])) {
				CollisionEffect(&G_Object[cnt_src], &G_Object[cnt_dest]);
			}
		}
	}
}

/**********************************************************************

	CollisionWithBoundary --- オブジェクトと境界壁の当たり判定処理

	argument: なし

**********************************************************************/

void CollisionWithBoundary( void )
{
	int				cnt_src;
	TMY_OBJECT		*obj;
	NJS_POINT3		minP;
	NJS_POINT3		maxP;
	
	/*	壁とオブジェクトの衝突判定	*/
	obj		= &G_Object[0];
	for (cnt_src = 0; cnt_src < g_nbObject; cnt_src++) {
		switch(obj->type) {
		case TYPE_SPHERE:
			njGetSphereMinMax( &obj->model.sphere.coli, &minP, &maxP );
			break;
		
		case TYPE_BOX:
			if ( g_bOld ) {
				njGetBoxMinMax( &obj->model.box.coli, &minP, &maxP );
			}
			else {
				njGetBox2MinMax( &obj->model.box2.coli, &minP, &maxP );
			}
			break;
		
		case TYPE_CAPSULE:
			njGetCapsuleMinMax( &obj->model.capsule.coli, &minP, &maxP );
			break;
			
		default:
			break;
		}
		
		
		if ( minP.x < -BOUNDARY_HX ) {
			if ( obj->v.x < 0.0f ) {
				obj->v.x = -obj->v.x;
			}
			obj->pos.x		+= -BOUNDARY_HX - minP.x + BOUNDARY_PUSH;
			obj->collided	= TRUE;
		}
		else if ( maxP.x > BOUNDARY_HX ) {
			if ( obj->v.x > 0.0f ) {
				obj->v.x = -obj->v.x;
			}
			obj->pos.x		+= BOUNDARY_HX - maxP.x - BOUNDARY_PUSH;
			obj->collided	= TRUE;
		}
		
		if ( minP.y < -BOUNDARY_HY ) {
			if ( obj->v.y < 0.0f ) {
				obj->v.y = -obj->v.y;
			}
			obj->pos.y		+= -BOUNDARY_HY - minP.y + BOUNDARY_PUSH;
			obj->collided	= TRUE;
		}
		else if ( maxP.y > BOUNDARY_HY ) {
			if ( obj->v.y > 0.0f ) {
				obj->v.y = -obj->v.y;
			}
			obj->pos.y		+= BOUNDARY_HY - maxP.y - BOUNDARY_PUSH;
			obj->collided	= TRUE;
		}
		if ( !g_bIgnoreZ ) {
			if ( minP.z < -BOUNDARY_HZ ) {
				if ( obj->v.z < 0.0f ) {
					obj->v.z = -obj->v.z;
				}
				obj->pos.z		+= -BOUNDARY_HZ - minP.z + BOUNDARY_PUSH;
				obj->collided	= TRUE;
			}
			else if ( maxP.z > BOUNDARY_HZ ) {
				if ( obj->v.z > 0.0f ) {
					obj->v.z = -obj->v.z;
				}
				obj->pos.z		+= BOUNDARY_HZ - maxP.z - BOUNDARY_PUSH;
				obj->collided	= TRUE;
			}
		}
		obj++;
	}
}


/**********************************************************************

	CollisionObject --- オブジェクトの当たり判定処理

	argument: TMY_OBJECT *obj1（オブジェクト１）
			  TMY_OBJECT *obj2（オブジェクト２）

**********************************************************************/

int CollisionObject(TMY_OBJECT *obj1, TMY_OBJECT *obj2)
{
	if ( g_bOld ) {
		switch(obj1->type) {
		case TYPE_SPHERE:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckSS(&(obj1->model.sphere.coli),
									   &(obj2->model.sphere.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckBS(&(obj2->model.box.coli),
									   &(obj1->model.sphere.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckSC(&(obj1->model.sphere.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
	
		case TYPE_BOX:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckBS(&(obj1->model.box.coli),
									   &(obj2->model.sphere.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckBB(&(obj1->model.box.coli),
									   &(obj2->model.box.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckBC(&(obj1->model.box.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
	
		case TYPE_CAPSULE:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckSC(&(obj2->model.sphere.coli),
									   &(obj1->model.capsule.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckBC(&(obj2->model.box.coli),
									   &(obj1->model.capsule.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckCC(&(obj1->model.capsule.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
		}
	}
	else {
		switch(obj1->type) {
		case TYPE_SPHERE:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckSS(&(obj1->model.sphere.coli),
									   &(obj2->model.sphere.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckB2S(&(obj2->model.box2.coli),
									   &(obj1->model.sphere.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckSC(&(obj1->model.sphere.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
	
		case TYPE_BOX:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckB2S(&(obj1->model.box2.coli),
									   &(obj2->model.sphere.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckB2B2(&(obj1->model.box2.coli),
									   &(obj2->model.box2.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckB2C(&(obj1->model.box2.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
	
		case TYPE_CAPSULE:
			if (obj2->type == TYPE_SPHERE) {
				return njCollisionCheckSC(&(obj2->model.sphere.coli),
									   &(obj1->model.capsule.coli));
			} else if (obj2->type == TYPE_BOX) {
				return njCollisionCheckB2C(&(obj2->model.box2.coli),
									   &(obj1->model.capsule.coli));
			} else if (obj2->type == TYPE_CAPSULE) {
				return njCollisionCheckCC(&(obj1->model.capsule.coli),
									   &(obj2->model.capsule.coli));
			}
			break;
		}
	}
}


/**********************************************************************

	CollisionEffect --- 衝突したときの処理

	argument: TMY_OBJECT *obj（オブジェクトへのポインタ）

**********************************************************************/

void CollisionEffect(TMY_OBJECT *obj1, TMY_OBJECT *obj2)
{
	int				i;
	Float			ux;			//力の加わる方向ベクトル
	Float			uy;
	Float			uz;
	Float			dvx;
	Float			dvy;
	Float			dvz;
	Float			oou;
	
	obj1->collided	= TRUE;
	obj2->collided	= TRUE;
	
	ux		= obj1->pos.x - obj2->pos.x;
	uy		= obj1->pos.y - obj2->pos.y;
	uz		= obj1->pos.z - obj2->pos.z;
	oou		= 1.0f / sqrtf( ux * ux + uy * uy + uz * uz );
	ux		*= oou;
	uy		*= oou;
	uz		*= oou;
	
	dvx		= obj2->v.x - obj1->v.x;
	dvy		= obj2->v.y - obj1->v.y;
	dvz		= obj2->v.z - obj1->v.z;
	
	if ( ux * dvx + uy * dvy + uz * dvz > 0.0f ) {
		Float			sumw;
		Float			k1;
		Float			k2;
		
		
		sumw	= obj1->weight + obj2->weight;
		
		k1	=  2.0f * obj2->weight / sumw * ( ux * dvx + uy * dvy + uz * dvz );
		k2	= -2.0f * obj1->weight / sumw * ( ux * dvx + uy * dvy + uz * dvz );
		
		obj1->v.x	+= k1 * ux;
		obj1->v.y	+= k1 * uy;
		obj1->v.z	+= k1 * uz;
		
		obj2->v.x	+= k2 * ux;
		obj2->v.y	+= k2 * uy;
		obj2->v.z	+= k2 * uz;
	}
	
	//	少し押し出しておく
	for ( i = 0; i < COLLISION_PUSH_CNT; i++ ) {
		obj1->pos.x += COLLISION_PUSH_STEP * ux;
		obj1->pos.y += COLLISION_PUSH_STEP * uy;
		obj1->pos.z += COLLISION_PUSH_STEP * uz;
		
		obj2->pos.x -= COLLISION_PUSH_STEP * ux;
		obj2->pos.y -= COLLISION_PUSH_STEP * uy;
		obj2->pos.z -= COLLISION_PUSH_STEP * uz;
		
		if ( !CollisionObject( obj1, obj2 ) ) {
			break;
		}
	}
}

/**********************************************************************

	DrawLine --- 直線を描画

**********************************************************************/

void DrawLine( Float x1, Float y1, Float z1, Float x2, Float y2, Float z2, Uint32 color )
{
	NJS_POINT3			points[2];
	
	points[0].x		= x1;
	points[0].y		= y1;
	points[0].z		= z1;
	points[1].x		= x2;
	points[1].y		= y2;
	points[1].z		= z2;
	
	njDrawLineExStart( 0.5f, color, 0 );
	njDrawLine3DExSetList( points, 2 );
	njDrawLineExEnd();
}

/**********************************************************************

	UserExit --- 終了関数

**********************************************************************/

Sint32 UserExit(void)
{
	return USER_CONTINUE;
}
