#include <ninja.h>
#include "flatconv.h"
/*
	チャンクデータフラットコンバータ

	ハードウェアのフラット機能を生かすために頂点チャンクの法線データを作り直します。
	それによって頂点数が増えます。
	頂点チャンクを新たに生成しなおしますのでそのポインタとモデルデータ、および面の
	法線がどの程度まで離れていても同じとするか、をチェックするためのスレッショルド値
	を指定します。
	法線のチェックはそれぞれの内積を求めて、その値の差がスレッショルド値以下なら同じ
	法線とみなします。
	この値を小さくすると頂点の複製がたくさんでき、大きくするとフラットにしたとき汚く
	なります。だいたい、０．０００１ぐらいにしてください。
*/


Int	njCnkFlatConvert( NJS_CNK_MODEL *model, void *vertex, Float d )
{
	Sint32		vtype,off,vnum,pnum,i,strip;
	Uint32		u32,size,vsize,uoff;
	NJS_VERTEX_CHUNK	*vp0,*vp1;
	Sint16		*vlist0,*plist,*vlist1;
	Sint16		data,chunk,p0,p1,p2;
	NJS_VECTOR	vector;
	Float		fwork,fwork2;
	
	vlist0 = model->vlist;
	plist = model->plist;
	
	vtype = *vlist0++;
	vsize = *vlist0++;
	off  = *vlist0++;
	vnum = *vlist0++;

	vlist1 = vertex;
	vp0 = (NJS_VERTEX_CHUNK*)vlist0;
	vp1 = (NJS_VERTEX_CHUNK*)(vlist1+4);
	
	for( i=0; i<vnum; i++ ) {
		(vp1+i)->p.x = 0.f;
		(vp1+i)->p.y = 0.f;
		(vp1+i)->p.z = 0.f;
		(vp1+i)->v.x = 0.f;
		(vp1+i)->v.y = 0.f;
		(vp1+i)->v.z = 0.f;
	}
	
	while( (data = *plist++) != NJD_CE ) {
		chunk = data & 0x00ff;
		if( chunk < NJD_TINYOFF ) continue;		/* 16 bits size */
		if( chunk < NJD_MATOFF  ) {				/* Tex ID		*/
			plist++;
			continue;
		}
		if( chunk < NJD_STRIPOFF  ) {			/* Material		*/
			size = *plist ++;
			plist += size;
			continue;
		}

		size = *(Uint16*)plist++;
		u32 = *plist++;		/* strip num, user off */
		uoff = (u32 >> NJD_UFO_SHIFT) & 0x03;
		strip = u32 & 0x3fff;
		
		switch( chunk ) {
		case NJD_CS:
			do {
				pnum = *plist++;
				fwork2 = 1.0f;
				if( pnum < 0 ) {
					pnum = -pnum;
					fwork2 = -fwork2;
				}
				pnum-=2;
				/* 最初の２点は頂点をコピーだけする	*/
				p0 = *plist++;
				(vp1+p0)->p = (vp0+p0)->p;
				p1 = *plist++;
				(vp1+p1)->p = (vp0+p1)->p;
				do {
					p2 = *plist;

					/* 面の法線を求める	*/
					njGetPlaneNormal2( &(vp0+p0)->p, &(vp0+p1)->p, &(vp0+p2)->p, &vector );
					njUnitVector( &vector );
					vector.x *= fwork2;
					vector.y *= fwork2;
					vector.z *= fwork2;

					if( ((vp1+p2)->v.x == 0.f) && ((vp1+p2)->v.y == 0.f) && ((vp1+p2)->v.z == 0.f) ) {
						/* 法線が設定されていなければそのままセット	*/
						(vp1+p2)->p = (vp0+p2)->p;
						(vp1+p2)->v = vector;
					}
					else {
						/* すでにセットされていたらそれとの内積をとって、角度を調べる	*/
						fwork = (vp1+p2)->v.x * vector.x + (vp1+p2)->v.y * vector.y + (vp1+p2)->v.z * vector.z;
						if( fabsf(fwork-1.0f) > d ) {
							/* 規定角度以上なら新たな頂点を生成する	*/
							(vp1+vnum)->p = (vp0+p2)->p;
							(vp1+vnum)->v = vector;
							*plist = vnum;				/* 頂点インデックスを書き換える	*/
							vnum++;
						}
					}
					plist += (uoff+1);
					fwork2 = -fwork2;
					p0 = p1;
					p1 = p2;
				} while(--pnum);
			} while(--strip);
			break;
		case NJD_CS_UVN:
		case NJD_CS_UVH:
			do {
				pnum = *plist++;
				fwork2 = 1.0f;
				if( pnum < 0 ) {
					pnum = -pnum;
					fwork2 = -fwork2;
				}
				pnum-=2;
				/* 最初の２点は頂点をコピーだけする	*/
				p0 = *plist++;
				plist += 2;
				(vp1+p0)->p = (vp0+p0)->p;
				p1 = *plist++;
				plist += 2;
				(vp1+p1)->p = (vp0+p1)->p;
				do {
					p2 = *plist;

					/* 面の法線を求める	*/
					njGetPlaneNormal2( &(vp0+p0)->p, &(vp0+p1)->p, &(vp0+p2)->p, &vector );
					njUnitVector( &vector );
					vector.x *= fwork2;
					vector.y *= fwork2;
					vector.z *= fwork2;

					if( ((vp1+p2)->v.x == 0.f) && ((vp1+p2)->v.y == 0.f) && ((vp1+p2)->v.z == 0.f) ) {
						/* 法線が設定されていなければそのままセット	*/
						(vp1+p2)->p = (vp0+p2)->p;
						(vp1+p2)->v = vector;
					}
					else {
						/* すでにセットされていたらそれとの内積をとって、角度を調べる	*/
						fwork = (vp1+p2)->v.x * vector.x + (vp1+p2)->v.y * vector.y + (vp1+p2)->v.z * vector.z;
						if( fabsf(fwork-1.0f) > d ) {
							/* 規定角度以上なら新たな頂点を生成する	*/
							(vp1+vnum)->p = (vp0+p2)->p;
							(vp1+vnum)->v = vector;
							*plist = vnum;				/* 頂点インデックスを書き換える	*/
							vnum++;
						}
					}
					plist += (uoff+3);
					fwork2 = -fwork2;
					p0 = p1;
					p1 = p2;
				} while(--pnum);
			} while(--strip);
			break;
		}
	}
	
	*vlist1++ = vtype;
	*vlist1++ = vsize;
	*vlist1++ = off;
	*vlist1++ = vnum;

	vlist1 += vsize * 2 - 2;
	*vlist1 = NJD_CE;

	model->vlist = vertex;
	
	return vnum;
}
