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

#include "NuMotionMake.h"


#if 0
/*
	_nu_shpinfo_ptr_->int_vlist_func_p			Vlist の補完コア関数のアドレス
*/

/**********************   マクロ関数   **************************/

static void		nuShapeIntLinearVlist( const NUS_MKEY_P *VertexKey, Uint32 VertexNbKeys,
									const NUS_MKEY_P *NormalKey, Uint32 NormalNbKeys,
									Float frame, const Sint32 *SrcVlist, Sint32 **pDstVlist );

/*
	補完基本関数群
*/

/*
	func:	現在の MDATA ポインタのキーに書かれているシェープデータにより補完したデータで VLISTを作成し、
			その VLIST へのポインタを返す。
			・指定された旧 VLIST に基づいて新 VLIST が作成される。
			・補完キーが実質上存在しない場合はどんな場合でも、返されるポインタは旧 VLIST へのポインタとなり、
			  余計なコピーなどは行わない。
			・補完キーが一部存在しない部分に付いては、旧VLISTのデータがそのままコピーされる。
			・新VLIST の中のチャンクの個数とチャンクタイプはすべて、旧VLIST のものと同じに成る。
			・NUS_MDATA(n) 配列への参照ポインタが指すキーフレーム配列が参照される。
			・cnkmodel->vlist	= NULL なら何もせずに、返すポインタを NULL にして戻る。
			・NUS_MDATA(n) 配列への参照ポインタは、呼び出し側で進めること。
			・通常、cnkobj->evalflags の判定を呼び出し側で行い、フラグがセットされている場合はこの関数を呼ばないこと。
	
	in:		cnkmodel	= この中の vlist メンバがデフォルト VLIST として参照される（NULLは使用不可）。
			pVlist		= 補完後の VLIST へのポインタを格納するフォルダ
			
	out:	*pVlist		= キーが１つでもある場合は補完して作成した VLIST へのポインタ。
			*pVlist		= キーが１つもない場合は、cnkmodel->vlist。
*/
void _nuGetShapeVlist( const NJS_CNK_MODEL *cnkmodel, Sint32 **pVlist )
{
	NUS_MKEY_P		*VertexKey;
	NUS_MKEY_P		*NormalKey;
	Uint32			VertexNbKeys;
	Uint32			NormalNbKeys;
	
	if ( cnkmodel->vlist == NULL ) {
		//	VLIST が元々存在しない場合
		*pVlist	= NULL;
		return;
	}
	
	if ( _nu_shpinfo_ptr_->type & NUD_MTYPE_VERT_4 ) {
		//	頂点シェープキーのフォルダがある場合
		if ( ( VertexKey = (NUS_MKEY_P *)_nu_shpinfo_ptr_->key[_nu_shpinfo_ptr_->cnt] ) != NULL ) {
			//	実際にある場合
			VertexNbKeys	= _nu_shpinfo_ptr_->nbkeys[_nu_shpinfo_ptr_->cnt];
		}
		_nu_shpinfo_ptr_->cnt++;
	}
	else {
		VertexKey	= NULL;
	}
	
	if ( _nu_shpinfo_ptr_->type & NUD_MTYPE_NORM_5 ) {
		//	法線シェープキーのフォルダがある場合
		if ( ( NormalKey = (NUS_MKEY_P *)_nu_shpinfo_ptr_->key[_nu_shpinfo_ptr_->cnt] ) != NULL ) {
			//	実際にある場合
			NormalNbKeys	= _nu_shpinfo_ptr_->nbkeys[_nu_shpinfo_ptr_->cnt];
		}
		_nu_shpinfo_ptr_->cnt++;
	}
	else {
		NormalKey	= NULL;
	}
	
	if ( VertexKey != NULL || NormalKey != NULL ) {
		//	補完キーが少なくとも一つは存在する場合
		nuShapeIntLinearVlist( VertexKey, VertexNbKeys, NormalKey, NormalNbKeys,
							_nu_shpinfo_ptr_->frame, cnkmodel->vlist, pVlist );
	}
	else {
		//	補完キーが全く存在しない場合
		*pVlist		= cnkmodel->vlist;
	}
}

/*
	func:	指定された、頂点補完キー列と、法線補完キー列に基づいて補完データを作成し、新しく VLIST を作成する。
			・VLIST は、*SrcVlist に指定された旧 VLIST に基づいて作成される。
			・この関数は、シェープバッファからデータ領域を確保し、その領域に必ず VLIST を作成する。
			・新しく作成した VLIST の先頭アドレスを *pDstVlist に返す。
			・新VLIST の中のチャンクの個数とチャンクタイプはすべて、旧VLIST のものと同じに成る。
	
	in:		VertexKey		= 頂点補完キー列
			VertexNbKeys	= 頂点補完キー列のキーの個数（VertexKey != NULL のときのみ有効）
			NormalKey		= 法線補完キー列
			NormalNbKeys	= 法線補完キー列のキーの個数（NormalKey != NULL のときのみ有効）
			frame			= 補完フレーム番号
			SrcVlist		= 旧VLISTの先頭アドレス（NULL であってはならない）
			pDstVlist		= 新VLISTの先頭アドレスを格納するフォルダ
	
	out:	*pDstVlist		= 新VLISTの先頭アドレス
	
	note:	VertexKey != NULL の場合、VertexKey の１つのキーに存在する頂点の個数が、旧VLIST 中の頂点
			の個数以上でなければ成らない（基本的には一致すべき）。
			NormalKey != NULL の場合、NormalKey の一つのキーに存在する法線の個数は、旧VLIST 中の法線
			の個数以上でなければ成らない（基本的には一致すべき）。
*/
static void nuShapeIntLinearVlist( const NUS_MKEY_P *VertexKey, Uint32 VertexNbKeys,
								   const NUS_MKEY_P *NormalKey, Uint32 NormalNbKeys,
								   Float frame, const Sint32 *SrcVlist, Sint32 **pDstVlist )
{
	Uint32		*SrcChunkTop;
	Uint32		*DstPtr;
	Uint32		ChunkHead;
	Float		VertexCmp1;
	Float		VertexCmp2;
	NUS_MKEY_P	*VertexKey1;
	NUS_MKEY_P	*VertexKey2;
	Float		NormalCmp1;
	Float		NormalCmp2;
	NUS_MKEY_P	*NormalKey1;
	NUS_MKEY_P	*NormalKey2;
	Float		*pKey1Vertex;
	Float		*pKey1Normal;
	Float		*pKey2Vertex;
	Float		*pKey2Normal;
	
	if ( VertexKey != NULL ) {
		_nuGetMotionLinearKeys( (void*)VertexKey, sizeof(NUS_MKEY_P), VertexNbKeys, frame, 
							 	(void**)&VertexKey1, (void**)&VertexKey2, &VertexCmp1 );
		
		VertexCmp2	= 1.0f - VertexCmp1;
		
		pKey1Vertex	= (Float*)VertexKey1->key;
		pKey2Vertex	= (Float*)VertexKey2->key;
	}
	if ( NormalKey != NULL ) {
		_nuGetMotionLinearKeys( (void*)NormalKey, sizeof(NUS_MKEY_P), NormalNbKeys, frame,
							 	(void**)&NormalKey1, (void**)&NormalKey2, &NormalCmp1 );
		
		NormalCmp2	= 1.0f - NormalCmp1;
		
		pKey1Normal	= (Float*)NormalKey1->key;
		pKey2Normal	= (Float*)NormalKey2->key;
	}
	
	SrcChunkTop	= (Uint32 *)SrcVlist;
	DstPtr		= (Uint32 *)_nu_shape_buf_;			//とりあえず先頭を使う（暫定）
	*pDstVlist	= (Sint32 *)DstPtr;
	
	for ( ;; ) {
		ChunkHead	= SrcChunkTop[0] & 0xff;
		
		if ( ChunkHead == NJD_CE ) {
			*DstPtr++	= *SrcChunkTop++;
			break;
		}
		else {
			Uint32		*SrcPtr;
			Uint32		ToNextChunk;
			int			VertexNum;
			int			VertexCnt;
			int			ExistVertex;
			int			ExistNormal;
			int			ExistD8888;
			int			OtherDWORDs;
			
			ToNextChunk	= SrcChunkTop[0] >> 16;
			VertexNum	= SrcChunkTop[1] >> 16;
			
			SrcPtr		= SrcChunkTop;
			
			switch( ChunkHead ) {
			case	NJD_CV:
				ExistVertex		= 1;
				ExistNormal		= 0;
				ExistD8888		= 0;
				OtherDWORDs		= 0;
				break;
			case	NJD_CV_D8:
				ExistVertex		= 1;
				ExistNormal		= 0;
				ExistD8888		= 1;
				OtherDWORDs		= 0;
				break;
			case	NJD_CV_UF:
				ExistVertex		= 1;
				ExistNormal		= 0;
				ExistD8888		= 0;
				OtherDWORDs		= 1;
				break;
			case	NJD_CV_VN:
				ExistVertex		= 1;
				ExistNormal		= 1;
				ExistD8888		= 0;
				OtherDWORDs		= 0;
				break;
			case	NJD_CV_VN_D8:
				ExistVertex		= 1;
				ExistNormal		= 1;
				ExistD8888		= 1;
				OtherDWORDs		= 0;
				break;
			case	NJD_CV_VN_UF:
				ExistVertex		= 1;
				ExistNormal		= 1;
				ExistD8888		= 0;
				OtherDWORDs		= 1;
				break;
			case	NJD_CV_VN_NF:
				ExistVertex		= 1;
				ExistNormal		= 1;
				ExistD8888		= 0;
				OtherDWORDs		= 1;
				break;
			#if 0			//まだ存在しない
			case	NJD_CV_VN_NF_D8:
				ExistVertex		= 1;
				ExistNormal		= 1;
				ExistD8888		= 1;
				OtherDWORDs		= 1;
				break;
			#endif
			default:
				ExistVertex		= 0;
				ExistNormal		= 0;
				ExistD8888		= 0;
				OtherDWORDs		= 0;
				break;
			}
			
			//	チャンクヘッダをコピーする
			DstPtr[0]	= SrcPtr[0];
			DstPtr[1]	= SrcPtr[1];
			SrcPtr	+= 2;
			DstPtr	+= 2;
			
			for ( VertexCnt = 0; VertexCnt < VertexNum; VertexCnt++ ) {
				if ( ExistVertex ) {
					if ( VertexKey != NULL ) {
						//	頂点のシェープキーがある場合はその補完データを、DstVlist に書き込む
						((Float*)DstPtr)[0]	= pKey1Vertex[0] * VertexCmp2 + pKey2Vertex[0] * VertexCmp1;
						((Float*)DstPtr)[1]	= pKey1Vertex[1] * VertexCmp2 + pKey2Vertex[1] * VertexCmp1;
						((Float*)DstPtr)[2]	= pKey1Vertex[2] * VertexCmp2 + pKey2Vertex[2] * VertexCmp1;
						
						pKey1Vertex		+= 3;
						pKey2Vertex		+= 3;
					}
					else {
						//	頂点のシェープキーがない場合は、SrcVlistのデータをそのまま DstVlist にコピーする。
						((Float*)DstPtr)[0]	= ((Float*)SrcPtr)[0];
						((Float*)DstPtr)[1]	= ((Float*)SrcPtr)[1];
						((Float*)DstPtr)[2]	= ((Float*)SrcPtr)[2];
					}
					DstPtr	+= 3;
					SrcPtr	+= 3;
				}
				if ( ExistNormal ) {
					if ( NormalKey != NULL ) {
						//	頂点のシェープキーがある場合はその補完データを、DstVlist に書き込む
						#if		0
						//	正規化する場合
						Float				x;
						Float				y;
						Float				z;
						Float				inv_rr;
						
						x	= pKey1Normal[0] * NormalCmp2 + pKey2Normal[0] * NormalCmp1;
						y	= pKey1Normal[1] * NormalCmp2 + pKey2Normal[1] * NormalCmp1;
						z	= pKey1Normal[2] * NormalCmp2 + pKey2Normal[2] * NormalCmp1;
						
						inv_rr	= fsrra( x * x + y * y + z * z );
						
						((Float*)DstPtr)[0]	= inv_rr * x;
						((Float*)DstPtr)[1]	= inv_rr * y;
						((Float*)DstPtr)[2]	= inv_rr * z;
						#else
						//	正規化しない場合
						((Float*)DstPtr)[0]	= pKey1Normal[0] * NormalCmp2 + pKey2Normal[0] * NormalCmp1;
						((Float*)DstPtr)[1]	= pKey1Normal[1] * NormalCmp2 + pKey2Normal[1] * NormalCmp1;
						((Float*)DstPtr)[2]	= pKey1Normal[2] * NormalCmp2 + pKey2Normal[2] * NormalCmp1;
						#endif
						
						pKey1Normal		+= 3;
						pKey2Normal		+= 3;
					}
					else {
						//	頂点のシェープキーがない場合は、SrcVlistのデータをそのまま DstVlist にコピーする。
						((Float*)DstPtr)[0]	= ((Float*)SrcPtr)[0];
						((Float*)DstPtr)[1]	= ((Float*)SrcPtr)[1];
						((Float*)DstPtr)[2]	= ((Float*)SrcPtr)[2];
					}
					DstPtr	+= 3;
					SrcPtr	+= 3;
				}
				if ( ExistD8888 ) {
					*DstPtr++	= *SrcPtr++;
				}
				
				if ( OtherDWORDs != 0 ) {
					//	現在は有ったとしても 1 DWORD である。
					*DstPtr++	= *SrcPtr++;
				}
			}
			
			//	ミスが有っても伝播しないように CHUNK の長さでスキップする。
			SrcChunkTop		+= 1 + ToNextChunk;
		}
	}
}

#endif



void _nuSetShapeVlistSub( Float *vlist, Float *vlist1, Float *vlist2, Sint32 flag, Float cmp1 );

#define	S_VERTEX		0x0001
#define	S_NORMAL		0x0002
#define	S_D8888			0x0004
#define	S_DWORD			0x0008
#define	S_VERTEX2		0x0010
#define	S_NORMAL2		0x0020
#define	S_D88882		0x0040

void nuSetShapeVlist( Uint32 *vlist, Uint32 vtype )
{
	Sint32	bExist;
	Uint16	*vlist1,*vlist2;
	Sint32	num;
	NUS_SHAPE_GROUP_COEFF *group;

	group = &_nu_shape_buf_[_nu_shpinfo_ptr_->KeyGroupID];
	
	vlist1 = (Uint16*)(_nu_shape_list_->shapes[group->ShapeID1].cvlist[_nu_shpinfo_ptr_->group->entryId].cv);
	vlist2 = (Uint16*)(_nu_shape_list_->shapes[group->ShapeID2].cvlist[_nu_shpinfo_ptr_->group->entryId].cv);
	
	switch( vtype & 0x00ff ) {
	case	NJD_CV:
		bExist = S_VERTEX;
		break;
	case	NJD_CV_D8:
		bExist = S_VERTEX | S_D8888;
		break;
	case	NJD_CV_UF:
		bExist = S_VERTEX | S_DWORD;
		break;
	case	NJD_CV_VN:
		bExist = S_VERTEX | S_NORMAL;
		break;
	case	NJD_CV_VN_D8:
		bExist = S_VERTEX | S_NORMAL | S_D8888;
		break;
	case	NJD_CV_VN_UF:
		bExist = S_VERTEX | S_NORMAL | S_DWORD;
		break;
	case	NJD_CV_VN_NF:
		bExist = S_VERTEX | S_NORMAL | S_DWORD;
		break;
	default:
		return;
		break;
	}

	
	switch( (*vlist1++) & 0x00ff ) {
	case	NJD_CV:
		bExist |= S_VERTEX2;
		num = 3;
		break;
	case	NJD_CV_D8:
		bExist |= S_VERTEX2 | S_D88882;
		num = 4;
		break;
	case	NJD_CV_VN:
		bExist |= S_VERTEX2 | S_NORMAL2;
		num = 6;
		break;
	case	NJD_CV_VN_D8:
		bExist |= S_VERTEX2 | S_NORMAL2 | S_D88882;
		num = 7;
		break;
	case	NJD_D8:
		bExist |= S_D88882;
		num = 1;
		break;
	case	NJD_VN:
		bExist |= S_NORMAL2;
		num = 3;
		break;
	case	NJD_VN_D8:
		bExist |= S_NORMAL2 | S_D88882;
		num = 4;
		break;
	default:
		return;
		break;
	}
	

	//シェープチャンクには頂点数がないのでサイズから換算する

	num  = *vlist1++ / num;
	vlist2 += 2;
	
	_nuSetShapeVlistSub( (Float *)vlist, (Float *)vlist1, (Float *)vlist2, num | (bExist<<16), group->rate );

}



void _nuSetShapeVlistSub( Float *vlist, Float *vlist1, Float *vlist2, Sint32 flag, Float cmp1 )
{
	register	Sint32	num,bExist;
	register	Float	cmp2 = 1.0f - cmp1;
	
	num = flag & 0x0000ffff;
	bExist = flag >> 16;
	
	do {
		if( bExist & S_VERTEX ) {
			if( bExist & S_VERTEX2 ) {
				*vlist     = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				*(vlist+1) = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				*(vlist+2) = *vlist1++ * cmp2 + *vlist2++ * cmp1;
			}
			vlist += 3;
		}
		if( bExist & S_NORMAL ) {
			if( bExist & S_NORMAL2 ) {
#if 0
				Float	x,y,z,l;
				
				x = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				y = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				z = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				l = fsrra( x * x + y * y + z * z );
				*vlist     = x * l;
				*(vlist+1) = y * l;
				*(vlist+2) = z * l;

#else
				*vlist     = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				*(vlist+1) = *vlist1++ * cmp2 + *vlist2++ * cmp1;
				*(vlist+2) = *vlist1++ * cmp2 + *vlist2++ * cmp1;
#endif
			}
			vlist += 3;
		}
		if( bExist & S_D8888 ) {
			if( bExist & S_D88882 ) {
				Uint8	*p,*p1,*p2;

				p  = (Uint8*)vlist;
				p1 = (Uint8*)vlist1;
				p2 = (Uint8*)vlist2;

				*p     = (Uint8)((Float)*p1       * cmp2 + (Float)*p2       * cmp1);
				*(p+1) = (Uint8)((Float)(*(p1+1)) * cmp2 + (Float)(*(p2+1)) * cmp1);
				*(p+2) = (Uint8)((Float)(*(p1+2)) * cmp2 + (Float)(*(p2+2)) * cmp1);
				*(p+3) = (Uint8)((Float)(*(p1+3)) * cmp2 + (Float)(*(p2+3)) * cmp1);

				vlist1 ++;
				vlist2 ++;
			}
			vlist ++;
		}
		if( bExist & S_DWORD ) {
			vlist ++;
		}
	} while( --num );

}
