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

#include "NuMotionMake.h"



/*	モーション情報構造体（スロット）		*/
NUS_CUR_OBJMOTION_INFO	_nu_obj_motion_info_[NUD_OBJMOTION_INFO_MAX];

/*	現在処理中のモーション情報		*/
NUS_CUR_OBJMOTION_INFO	*_nu_objmotinfo_ptr_	= &_nu_obj_motion_info_[0];

Sint32 _nu_motion_event_flag_ = OFF;
Uint16 _nu_motion_event_val_ = 0;

/*		プロトタイプ宣言	*/
static int		_nuGetMotionFloat3Dummy( Float pos[3] );
static int		_nuGetMotionAngle3Dummy( Angle ang[3] );

/*
	func:	オブジェクトのモーションパラメータを設定する。
			NUS_MDATA(n) 配列への参照ポインタが、その先頭にセットされる。
			
	in:		motion		= モーション
			frame		= フレーム番号
			
	注意:	モーション情報は０番スロットに格納される。以後のモーション情報は０番スロットが参照される。
			
			nuSetCurMotionInfoSlot() 関数を参照してください。
*/
void nuStartMotionObj( const NUS_MOTION *motion, Float frame )
{
	nuSetCurMotionInfoSlot( 0 );
	nuStartMotionObjEx( motion, frame );
}

/*
	func:	オブジェクトのモーションパラメータを設定する。
			NUS_MDATA(n) 配列への参照ポインタが、その先頭にセットされる。
			
	in:		motion		= モーション
			frame		= フレーム番号
	
	注意:	モーション情報はカレントのモーション情報構造体に出力される。
			
			nuSetCurMotionInfoSlot() 関数を参照してください。
*/
void nuStartMotionObjEx( const NUS_MOTION *motion, Float frame )
{
	Uint32					data_type;
	
	nuStartMotionEx( motion, frame );
	
	data_type				= _nu_motinfo_ptr_->type;
	
	//	pos のモーションが有るかどうかで関数を選択
	if ( data_type & NUD_MTYPE_POS_0 ) {
		_nu_motinfo_ptr_->get_pos_func_p	= _nuGetMotionFloat3;
	}
	else {
		_nu_motinfo_ptr_->get_pos_func_p	= _nuGetMotionFloat3Dummy;
	}
	
	//	ang のモーションが有るかどうかで関数を選択
	if ( data_type & NUD_MTYPE_ANG_1 ) {
		_nu_motinfo_ptr_->get_ang_func_p	= _nuGetMotionAngle3;
		_nu_motinfo_ptr_->get_qua_func_p	= NULL;
		_nu_motinfo_ptr_->get_sang_func_p	= NULL;
	}
	else if( data_type & NUD_MTYPE_SANG_1 ) {
		_nu_motinfo_ptr_->get_ang_func_p	= NULL;
		_nu_motinfo_ptr_->get_qua_func_p	= NULL;
		_nu_motinfo_ptr_->get_sang_func_p	= _nuGetMotionSangle3;
	}
	else if ( data_type & NUD_MTYPE_QUAT_1 ) {
		_nu_motinfo_ptr_->get_ang_func_p	= NULL;
		_nu_motinfo_ptr_->get_qua_func_p	= _nuGetMotionQuaternion;
		_nu_motinfo_ptr_->get_sang_func_p	= NULL;
	}
	else {
		_nu_motinfo_ptr_->get_ang_func_p	= _nuGetMotionAngle3Dummy;
		_nu_motinfo_ptr_->get_qua_func_p	= NULL;
		_nu_motinfo_ptr_->get_sang_func_p	= NULL;
	}
	
	//	scl のモーションが有るかどうかで関数を選択
	if ( data_type & NUD_MTYPE_SCL_2 ) {
		_nu_motinfo_ptr_->get_scl_func_p	= _nuGetMotionFloat3;
	}
	else {
		_nu_motinfo_ptr_->get_scl_func_p	= _nuGetMotionFloat3Dummy;
	}
	
	if (( data_type & NUD_MTYPE_VEC_3 ) || ( data_type & NUD_MTYPE_TARGET_3 )){
		_nu_motinfo_ptr_->cnt++;		//SKIP
	}
	
	if ( data_type & NUD_MTYPE_EVENT_4 ){
		_nu_motion_event_flag_ = ON;
		_nu_motion_event_val_ = 0;
	}
	else{
		_nu_motion_event_flag_ = OFF;
		_nu_motion_event_val_ = 0;
	}
}

/*
	func:	現在のモーションパラメータにおいての、カレントノードのマトリックスを作成し、カレントマトリックスの
			右から乗算する。
			
	in:		cnkobj					= NUS_MDATA(n)への参照ポインタと対応した NJS_CNK_OBJECT 構造体へのポインタ。
									  モーションデータが無い場合にこの構造体のpos,ang,sclメンバが参照される。
	
	out:	cur'		= cur [カレントノードのマトリックス]
	
	expl.:	この関数は、カレントノードのマトリックスを作成する。
			
			引数 cnkobj には、NUS_MDATA(n) への参照ポインタに対応した NJS_CNK_OBJECT 構造体へのポインタを指定する。
			
			指定された cnkobj の evalflags メンバに NJD_EVAL_SKIP が指定されていない場合:
				・NUS_MDATA(n) 配列への参照ポインタが指すキーフレーム配列の補完データが採用される。
				・NUS_MDATA(n) 配列への参照ポインタは１つ進められる。
			
			指定された cnkobj の evalflags メンバに NJD_EVAL_SKIP が指定されている場合:
				・対応するモーションデータが無いとみなされ、モーションデータの、NUS_MDATA(n)配列 は参照されずに
				  cnkobj の中のpos,ang,sclメンバがそのまま採用される。
				・NUS_MDATA(n) 配列への参照ポインタは進められない。
			
*/
void nuMotionTransformEx( const NJS_CNK_OBJECT *cnkobj )
{
	Float				pos[3];
	Float				scl[3];
	Angle				ang[3];
	NJS_QUATERNION		qua;
	int					MotRot;
	
	MotRot	= nuGetMotionNodeData( cnkobj, pos, ang, scl, &qua );
	
	
	njTranslateEx( (NJS_VECTOR *)pos );
	
	switch( MotRot ) {
	case NUD_MROT_ANGLEXYZ :
		njRotateEx( ang, 0 );
		break;
	case NUD_MROT_ANGLEZXY :
		njRotateEx( ang, 1 );
		break;
	default:
		njQuaternionEx( &qua );
		break;
	}
	
	njScaleEx( ( NJS_VECTOR *)scl );
}

/*
	func:	現在のモーションパラメータにおいての、カレントノードの pos, ang（またはqua), scl を取得する。
			対応するモーションデータが無い場合は、指定された cnkobj の固定パラメータが返される。
			角度パラメータについては３通りの返され方があり、戻り値によって区別できる。
			
	in:		cnkobj					= NUS_MDATA(n)への参照ポインタと対応した NJS_CNK_OBJECT 構造体へのポインタ。
									  モーションデータが無い場合にこの構造体のpos,ang,sclメンバが参照される。
			pos						= 位置の格納先
			ang						= 角度の Angle 形式の格納先
			scl						= 拡大率の格納先
			qua						= 角度の NJS_QUATERNION 形式の格納先
			
	out:	pos[0],pos[1],pos[2]	= カレントノードの位置
			ang[0],ang[1],ang[2]	= カレントノードの角度のAngle表現(c.f.戻り値参照）
			scl[0],scl[1],scl[2]	= カレントノードの拡大率
			*qua					= カレントノードの角度のQuaternion表現(c.f.戻り値参照)
			
	return:	返された回転パラメータの種類
			NUD_MROT_ANGLEXYZ	: 回転パラメータは、XYZ-Angle 形式で引数 ang に返される。
			NUD_MROT_ANGLEZXY	: 回転パラメータは、ZXY-Angle 形式で引数 ang に返される。
			NUD_MROT_QUATERNION	: 回転パラメータは、NJS_QUATERNION 形式で引数 qua に返される。
	
	expl.:	この関数を呼ぶ前に、nuStartMotionObj() 関数で、カレントのモーションパラメータを設定しておくこと。
			
			この関数は、カレントノードの角度や位置情報を返す。
			
			指定された cnkobj の evalflags メンバに NJD_EVAL_SKIP が指定されていない場合:
				・NUS_MDATA(n) 配列への参照ポインタが指すキーフレーム配列が参照される。
				・NUS_MDATA(n) 配列への参照ポインタが１つ進められる。
			指定された cnkobj の evalflags メンバに NJD_EVAL_SKIP が指定されている場合:
				・対応するモーションデータが無いとみなされ、モーションデータの、NUS_MDATA(n)配列は参照されず
				  に cnkobj の中のpos,ang,sclメンバがそのまま引数にコピーされる。
				・NUS_MDATA(n) 配列への参照ポインタは進められない。
			
	note:	・以下の説明において、ある項目のキーフレームが存在するとは、motion->type の対応する NUD_MTYPE_???
			  ビットが ON であり、かつ、カレントノードに対応する NUS_MDATA(n)配列中の実際のそのキー配列へのポ
			  インタが NULL ではないことである。
			
			・カレントフレームの pos キーがある場合は、前後の pos キーを補完して引数posに返す。
			  無い場合は、cnkobj->pos を引数posに返す。
			・カレントフレームの scl キーがある場合は、前後の scl キーを補完して引数sclに返す。
			  無い場合は、cnkobj->scl を引数sclに返す。
			・カレントフレームの ang キーがある場合は、前後の ang キーを補完して引数angに返す。
			  無い場合は、cnkobj->ang を引数angに返す。この場合の戻り値は、NUD_MROT_ANGLE??? である。
			・カレントフレームの qua キーがある場合は、前後の qua キーを補完して引数quaに返す。
			  この場合、戻り値は、NUD_MROT_QUATERNION になる。
			  無い場合は、cnkobj->ang を引数angに返す。この場合の戻り値は、NUD_MROT_ANGLE??? である。
			
			・戻り値として、NUD_MROT_ANGLE??? が返されるときは、
			  cnkobj->evalflags に NJD_EVAL_ZXY_ANG が指定されていれば、NUD_MROT_ANGLEZXY、指定されていなければ
			  NUD_MROT_ANGLEXYZ となる。
*/
int nuGetMotionNodeData( const NJS_CNK_OBJECT *cnkobj, Float pos[3], Angle ang[3], Float scl[3], NJS_QUATERNION *qua )
{
	int					rc;
	Sangle 				sang[3];
	
	if ( !( cnkobj->evalflags & NJD_EVAL_SKIP ) ) {
		//	位置成分を処理
		if ( !(*_nu_motinfo_ptr_->get_pos_func_p)( pos ) ) {
			//	位置成分の規定値をコピー
			pos[0]	= cnkobj->pos[0];
			pos[1]	= cnkobj->pos[1];
			pos[2]	= cnkobj->pos[2];
		}
		
		//	回転成分を処理
		if ( _nu_motinfo_ptr_->get_ang_func_p != NULL ) {
			//	モーションが Angle形式の場合
			if ( (*_nu_motinfo_ptr_->get_ang_func_p)( ang ) ) {
				//	Angle キーが実際に存在した場合
				rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
			}
			else {
				//	Angle キーが NULL だった場合
				
				//	回転成分の規定値をコピー
				
				if ( cnkobj->evalflags & NJD_EVAL_QUATERNION ) {
					qua->re		= cnkobj->re_quat;
					qua->im[0]	= *(Float *)&cnkobj->ang[0];
					qua->im[1]	= *(Float *)&cnkobj->ang[1];
					qua->im[2]	= *(Float *)&cnkobj->ang[2];
					
					rc	= NUD_MROT_QUATERNION;
				}
				else {
					ang[0]	= cnkobj->ang[0];
					ang[1]	= cnkobj->ang[1];
					ang[2]	= cnkobj->ang[2];
					
					rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
				}
			
			}
		}
		else if( _nu_motinfo_ptr_->get_sang_func_p != NULL ){
			//	モーションが Angle形式の場合
			if ( (*_nu_motinfo_ptr_->get_sang_func_p)( sang ) ) {
				//	Sangle キーが実際に存在した場合
				ang[0]	= (Angle)sang[0];
				ang[1]	= (Angle)sang[1];
				ang[2]	= (Angle)sang[2];
				rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
			}
			else {
				//	Sangle キーが NULL だった場合
				
				//	回転成分の規定値をコピー
				
				if ( cnkobj->evalflags & NJD_EVAL_QUATERNION ) {
					qua->re		= cnkobj->re_quat;
					qua->im[0]	= *(Float *)&cnkobj->ang[0];
					qua->im[1]	= *(Float *)&cnkobj->ang[1];
					qua->im[2]	= *(Float *)&cnkobj->ang[2];
					
					rc	= NUD_MROT_QUATERNION;
				}
				else {
					ang[0]	= cnkobj->ang[0];
					ang[1]	= cnkobj->ang[1];
					ang[2]	= cnkobj->ang[2];
					
					rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
				}
			
			}
		}
		else {
			//	モーションがコータニオン形式の場合
			if ( (*_nu_motinfo_ptr_->get_qua_func_p)( qua ) ) {
				//	このノードのコータニオンのキーがあった場合
				rc	= NUD_MROT_QUATERNION;
			}
			else {
				//	このノードのコータニオンのキー NULL だった場合
				
				//	回転成分の規定値をコピー
				
				if ( cnkobj->evalflags & NJD_EVAL_QUATERNION ) {
					qua->re		= cnkobj->re_quat;
					qua->im[0]	= *(Float *)&cnkobj->ang[0];
					qua->im[1]	= *(Float *)&cnkobj->ang[1];
					qua->im[2]	= *(Float *)&cnkobj->ang[2];
					
					rc	= NUD_MROT_QUATERNION;
				}
				else {
					ang[0]	= cnkobj->ang[0];
					ang[1]	= cnkobj->ang[1];
					ang[2]	= cnkobj->ang[2];
					
					rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
				}
			}
		}
	
		//	スケーリング成分を処理
		if ( !(*_nu_motinfo_ptr_->get_scl_func_p)( scl ) ) {
			//	スケーリング成分の規定値をコピー
			scl[0]	= cnkobj->scl[0];
			scl[1]	= cnkobj->scl[1];
			scl[2]	= cnkobj->scl[2];
		}

		if ( _nu_motion_event_flag_){
			if(!_nuGetMotionEvent(&_nu_motion_event_val_)){
				if(*(Uint32*)&cnkobj->evalflags & NJD_EVAL_HIDE){
					_nu_motion_event_val_ |= NUD_EVENT_HIDE;
				}
				else{
					_nu_motion_event_val_ &= (~NUD_EVENT_HIDE);
				}
			}
		}
	
		//	次のノードに進む
		nuSetNextMotionNodeEx();
	}
	else {
		//	規定値をコピー
		
		//	位置成分の規定値をコピー
		pos[0]	= cnkobj->pos[0];
		pos[1]	= cnkobj->pos[1];
		pos[2]	= cnkobj->pos[2];
		
		//	スケーリング成分の規定値をコピー
		scl[0]	= cnkobj->scl[0];
		scl[1]	= cnkobj->scl[1];
		scl[2]	= cnkobj->scl[2];
		
		//	回転成分の規定値をコピー
		if ( cnkobj->evalflags & NJD_EVAL_QUATERNION ) {
			qua->re		= cnkobj->re_quat;
			qua->im[0]	= *(Float *)&cnkobj->ang[0];
			qua->im[1]	= *(Float *)&cnkobj->ang[1];
			qua->im[2]	= *(Float *)&cnkobj->ang[2];
			
			rc	= NUD_MROT_QUATERNION;
		}
		else {
			ang[0]	= cnkobj->ang[0];
			ang[1]	= cnkobj->ang[1];
			ang[2]	= cnkobj->ang[2];
			
			rc	= (cnkobj->evalflags & NJD_EVAL_ZXY_ANG) ? NUD_MROT_ANGLEZXY : NUD_MROT_ANGLEXYZ;
		}
	}
	
	return	rc;
}

static int _nuGetMotionFloat3Dummy( Float pos[3] )
{
	return	FALSE;
}

static int _nuGetMotionAngle3Dummy( Angle ang[3] )
{
	return	FALSE;
}


/*
	func:	EVENTモーションがある場合、nuMotionTransformEx関数で取得したEVENTフラグをの内容を
			オブジェクトに反映させる
			
	in:		cnkobj				= NUS_MDATA(n)への参照ポインタと対応した NJS_CNK_OBJECT 構造体へのポインタ。
									オブジェクトデータは書きかえられる
			
	expl.:	この関数を呼ぶ前に、nuMotionTransformEx関数を実行する
			
	note:	オブジェクトの内容を書き換えます。書きかえられては困る場合は、この関数を実行する前に
			オブジェクトを保存しておいてください。現在はEVALFLAGのHIDEしか変更していない。
*/

void nuChangeMotionObject( NJS_CNK_OBJECT *cnkobj)
{

	if ( _nu_motion_event_flag_){
		if(_nu_motion_event_val_ & NUD_EVENT_HIDE){
			*(Uint32*)&cnkobj->evalflags |= NJD_EVAL_HIDE;
		}
		else{
			*(Uint32*)&cnkobj->evalflags &= (~NJD_EVAL_HIDE);
		}
	}
}

/*
	func:	EVENTモーションフラグの結果を返す
	return:	TRUE,FALSE

	expl.:	この関数を呼ぶ前に、nuMotionTransformEx関数を実行する
			
	note:	
*/

Sint32	nuGetMotionEventFlag( void )
{
	return _nu_motion_event_flag_;
}
