/*
 *	clipobj3.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2006 Naoyuki Sawa
 *
 *	* Wed Jun 28 00:42:05 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	  Expatライブラリをリンクするので、実行バイナリが大きくなります。
 *	  mesh_load()を使わないバイナリが、無駄に大きくなるのを避けるため、
 *	  通常の描画モジュールclipren2.c,clipren3.cとは別ファイルにしました。
 *	- 2006/06/28現在、テクスチャ座標の読み込みの動作確認が未だです。
 *	  ExportPieceClipプラグイン側で、テクスチャ座標出力が未実装だからです。
 *	  一応、本モジュール側のテクスチャ座標読み込み処理は、全部書いてあるので、
 *	  バグが無ければこのままで大丈夫なはずです。
 *	  ↓2006/06/29追記 
 *	  ExportPieceClipプラグインに、テクスチャ座標出力を追加しました。
 *	  本モジュールの読み込み処理も、問題なく動作しているようです。
 *	* Wed Jun 28 04:51:07 JST 2006 Naoyuki Sawa
 *	- "XML_Parser parser"を、mesh_load()のローカル変数から、MESHLOAD構\造体のメンバへ移動しました。
 *	  現時点では特に意味はありませんが、今後、エレメントハンドラ中でエラーを表\示する場合に備えて、
 *	  "XML_GetCurrentLineNumber(ml->parser)"などの呼び出しを可能\とするためです。
 *	* Thu Jun 29 14:44:46 JST 2006 Naoyuki Sawa
 *	- ファイル名をclipmesh.c→clipobj3.cに変更しました。
 *	- wire_load()を追加しました。
 *	* Sun Aug 10 21:42:25 JST 2008 Naoyuki Sawa
 *	- tool/ExportPieceClipが、<vector>要素を固定小数点でなく浮動小数点で出力するよう変更したことに追従し、
 *	  mesh_load()とwire_load()に、浮動小数点のまま読み込むか、固定小数点に変換するかの選択を追加しました。
 */
#include "clip.h"

/*****************************************************************************
 *	mesh_load
 *****************************************************************************/

#define MESHLOAD_STAT_MESH_START	0
#define MESHLOAD_STAT_MESH_END		1
#define MESHLOAD_STAT_VECTOR_START	2
#define MESHLOAD_STAT_VECTOR_END	3
#define MESHLOAD_STAT_TCOORD_START	4
#define MESHLOAD_STAT_TCOORD_END	5
#define MESHLOAD_STAT_FACE_START	6
#define MESHLOAD_STAT_FACE_END		7
#define MESHLOAD_STAT_FACEVERTEX_START	8
#define MESHLOAD_STAT_FACEVERTEX_END	9
#define MESHLOAD_STAT_EOF		-1

#define MESHLOAD_BUFLEN	64	/* 読み込みバッファ長 (調整可) */

/*{{clipren3.cより*/
#define N_VERTICES_MAX	8	/* 1ポリゴン当たりの最大頂点数 */
/*}}clipren3.cより*/

typedef struct _MESHLOAD {
	XML_Parser parser;
	//
//{{2008/08/10:追加
	int convert_fixed;	/* 0 = <vector>要素を浮動小数点のままで、浮動小数点MESHとする。 */
	//			/* 1 = <vector>要素を固定小数点に変換し、固定小数点MESHとする。 */
//}}2008/08/10:追加
	int stat;		/* MESHLOAD_STAT_* */
	int iv;
	int ic;
	int if_;
	int ifv;
	//
	MESH m;
	vector v[255/*nv*/];
	TCOORD c[255/*nc*/];
	struct {
		FACE f;
		FACEVERTEX fv[N_VERTICES_MAX/*nfv*/];
	} f[255/*nf*/];
	//
	unsigned char buf[MESHLOAD_BUFLEN];
} MESHLOAD;

static void
mesh_load_start_element(void* user_data, const char* name, const char* atts[])
{
	MESHLOAD* ml = user_data;

	switch(ml->stat) {
	case MESHLOAD_STAT_MESH_START:
		if(strcasecmp(name, "mesh") ||
		   !atts[0] || strcasecmp(atts[0], "nv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "nc") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "nf") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
		ml->m.nv = atoi(atts[1]);
		ml->m.nc = atoi(atts[3]);
		ml->m.nf = atoi(atts[5]);
		if(ml->m.nv) {        /* 頂点座標有り */
			ml->stat = MESHLOAD_STAT_VECTOR_START;
			ml->iv   = 0;
		} else if(ml->m.nc) { /* テクスチャ座標有り */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
			ml->ic   = 0;
		} else if(ml->m.nf) { /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_  = 0;
		} else {              /* 空メッシュ */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_VECTOR_START:
		if(strcasecmp(name, "vector") ||
		   !atts[0] || strcasecmp(atts[0], "x") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "y") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "z") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
//{{2008/08/10:変更
//		ml->v[ml->iv].x = (fixed)atoi(atts[1]);
//		ml->v[ml->iv].y = (fixed)atoi(atts[3]);
//		ml->v[ml->iv].z = (fixed)atoi(atts[5]);
//↓2008/08/10:変更
		if(ml->convert_fixed) {
			ml->v[ml->iv].x = fld(atof(atts[1]));
			ml->v[ml->iv].y = fld(atof(atts[3]));
			ml->v[ml->iv].z = fld(atof(atts[5]));
		} else {
			float x = atof(atts[1]);
			float y = atof(atts[3]);
			float z = atof(atts[5]);
			ml->v[ml->iv].x = *(fixed*)&x;
			ml->v[ml->iv].y = *(fixed*)&y;
			ml->v[ml->iv].z = *(fixed*)&z;
		}
//}}2008/08/10:変更
		ml->stat = MESHLOAD_STAT_VECTOR_END;
		break;

	case MESHLOAD_STAT_TCOORD_START:
		if(strcasecmp(name, "tcoord") ||
		   !atts[0] || strcasecmp(atts[0], "s") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "t") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->c[ml->ic].s = atoi(atts[1]);
		ml->c[ml->ic].t = atoi(atts[3]);
		ml->stat = MESHLOAD_STAT_TCOORD_END;
		break;

	case MESHLOAD_STAT_FACE_START:
		if(strcasecmp(name, "face") ||
		   !atts[0] || strcasecmp(atts[0], "nfv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "c"  ) || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->f[ml->if_].f.nfv = atoi(atts[1]);
		ml->f[ml->if_].f.c   = atoi(atts[3]);
		if(ml->f[ml->if_].f.nfv) { /* フェイス頂点有り */
			if(ml->f[ml->if_].f.nfv > N_VERTICES_MAX) {
				DIE();
			}
			ml->stat = MESHLOAD_STAT_FACEVERTEX_START;
			ml->ifv  = 0;
		} else {                   /* 空フェイス */
			ml->stat = MESHLOAD_STAT_FACE_END;
		}
		break;

	case MESHLOAD_STAT_FACEVERTEX_START:
		if(strcasecmp(name, "facevertex") ||
		   !atts[0] || strcasecmp(atts[0], "iv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "ic") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->f[ml->if_].fv[ml->ifv].iv = atoi(atts[1]);
		ml->f[ml->if_].fv[ml->ifv].ic = atoi(atts[3]);
		ml->stat = MESHLOAD_STAT_FACEVERTEX_END;
		break;

	default:
		DIE();
	}
}

static void
mesh_load_end_element(void* user_data, const char* name)
{
	MESHLOAD* ml = user_data;

	switch(ml->stat) {
	case MESHLOAD_STAT_MESH_END:
		if(strcasecmp(name, "mesh")) {
			DIE();
		}
		ml->stat = MESHLOAD_STAT_EOF;
		break;

	case MESHLOAD_STAT_VECTOR_END:
		if(strcasecmp(name, "vector")) {
			DIE();
		}
		if(++ml->iv < ml->m.nv) { /* 頂点座標継続 */
			ml->stat = MESHLOAD_STAT_VECTOR_START;
		} else if(ml->m.nc) {     /* テクスチャ座標有り */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
			ml->ic   = 0;
		} else if(ml->m.nf) {     /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_  = 0;
		} else {                  /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_TCOORD_END:
		if(strcasecmp(name, "tcoord")) {
			DIE();
		}
		if(++ml->ic < ml->m.nc) { /* テクスチャ座標継続 */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
		} else if(ml->m.nf) {     /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_ = 0;
		} else {                  /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_FACE_END:
		if(strcasecmp(name, "face")) {
			DIE();
		}
		if(++ml->if_ < ml->m.nf) { /* フェイス継続 */
			ml->stat = MESHLOAD_STAT_FACE_START;
		} else {                   /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_FACEVERTEX_END:
		if(strcasecmp(name, "facevertex")) {
			DIE();
		}
		if(++ml->ifv < ml->f[ml->if_].f.nfv) { /* フェイス頂点継続 */
			ml->stat = MESHLOAD_STAT_FACEVERTEX_START;
		} else {                               /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_FACE_END;
		}
		break;

	default:
		DIE();
	}
}

MESH*
//{{2008/08/10:変更
//mesh_load(const char* filename)
//↓2008/08/10:変更
mesh_load(const char* filename, int convert_fixed)
//}}2008/08/10:変更
{
	int result;
	int size;
	int done;
	FILE* fp;
	MESHLOAD* ml;
	MESH* m;
	unsigned char* p;

	/* ファイルを開きます。 */
	fp = fopen(filename, "rt");
	if(!fp) {
		DIE();
	}

	/* MESHLOAD構\造体のメモリを確保します。 */
	ml = calloc(1, sizeof(MESHLOAD));
	if(!ml) {
		DIE();
	}

//{{2008/08/10:追加
	ml->convert_fixed = convert_fixed;
//}}2008/08/10:追加

	/* XMLパーサを作成します。 */
	ml->parser = XML_ParserCreate(NULL);
	if(!ml->parser) {
		DIE();
	}

	/* ユーザデータを設定します。 */
	XML_SetUserData(ml->parser, ml);

	/* 要素ハンドラを設定します。 */
	XML_SetElementHandler(ml->parser, mesh_load_start_element, mesh_load_end_element);

	do {
		/* ファイルを読み込みます。 */
		size = fread(ml->buf, 1, MESHLOAD_BUFLEN, fp);

		/* ファイル終端に達したか調べます。 */
		done = feof(fp);

		/* 読み込んだ文字列をパースします。 */
		result = XML_Parse(ml->parser, ml->buf, size, done);
		if(result == XML_STATUS_ERROR) {
			DIE();
		}
	} while(!done); /* ファイル終端に達するまで */

	/* メッシュ全体のサイズを求めます。 */
	size =  sizeof(MESH);
	size += sizeof(vector) * ml->m.nv;
	size += sizeof(TCOORD) * ml->m.nc;
	for(ml->if_ = 0; ml->if_ < ml->m.nf; ml->if_++) {
		size += sizeof(FACE);
		size += sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv;
	}

	/* メッシュ全体のメモリを確保します。 */
	m = calloc(size, 1);
	if(!m) {
		DIE();
	}

	/* メッシュ全体を組み立てます。 */
	p = (unsigned char*)m;
	memcpy(p, &ml->m, sizeof(MESH));
	       p +=       sizeof(MESH);
	memcpy(p, ml->v, sizeof(vector) * ml->m.nv);
	       p +=      sizeof(vector) * ml->m.nv;
	memcpy(p, ml->c, sizeof(TCOORD) * ml->m.nc);
	       p +=      sizeof(TCOORD) * ml->m.nc;
	for(ml->if_ = 0; ml->if_ < ml->m.nf; ml->if_++) {
		memcpy(p, &ml->f[ml->if_].f, sizeof(FACE));
		       p +=                  sizeof(FACE);
		memcpy(p, ml->f[ml->if_].fv, sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv);
		       p +=                  sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv;
	}

	/* XMLパーサを削除します。 */
	XML_ParserFree(ml->parser);

	/* MESHLOAD構\造体のメモリを開放します。 */
	free(ml);

	/* ファイルを閉じます。 */
	fclose(fp);

	return m;
}

/*****************************************************************************
 *	wire_load
 *****************************************************************************/

#define WIRELOAD_STAT_WIRE_START	0
#define WIRELOAD_STAT_WIRE_END		1
#define WIRELOAD_STAT_VECTOR_START	2
#define WIRELOAD_STAT_VECTOR_END	3
#define WIRELOAD_STAT_WSEG_START	4
#define WIRELOAD_STAT_WSEG_END		5
#define WIRELOAD_STAT_EOF		-1

#define WIRELOAD_BUFLEN	64	/* 読み込みバッファ長 (調整可) */

typedef struct _WIRELOAD {
	XML_Parser parser;
	//
//{{2008/08/10:追加
	int convert_fixed;	/* 0 = <vector>要素を浮動小数点のままで、浮動小数点WIREとする。 */
	//			/* 1 = <vector>要素を固定小数点に変換し、固定小数点WIREとする。 */
//}}2008/08/10:追加
	int stat;	/* WIRELOAD_STAT_* */
	int iv;
	int is;
	//
	WIRE* w;	/* <mesh>と違って、先頭の<wire>タグだけでサイズが決定できます。 */
	//		/* 従って、MESHLOAD構\造体にあるような中間バッファは不要であり、 */
	//		/* <wire>タグ読み込み時に、全体のメモリブロックを確保できます。 */
	//
	vector* v;	/* MESHLOAD.wに確保したメモリブロックの中をポイントしています。 */
	WSEG* s;	/* MESHLOAD.wに確保したメモリブロックの中をポイントしています。 */
	//
	unsigned char buf[WIRELOAD_BUFLEN];
} WIRELOAD;

static void
wire_load_start_element(void* user_data, const char* name, const char* atts[])
{
	WIRELOAD* wl = user_data;
	int nv;
	int ns;

	switch(wl->stat) {
	case WIRELOAD_STAT_WIRE_START:
		if(strcasecmp(name, "wire") ||
		   !atts[0] || strcasecmp(atts[0], "nv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "ns") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		nv = atoi(atts[1]);
		ns = atoi(atts[3]);
		wl->w = calloc(sizeof(WIRE) + sizeof(vector) * nv + sizeof(WSEG) * ns, 1);
		if(!wl->w) {
			DIE();
		}
		wl->w->nv = nv;
		wl->w->ns = ns;
		wl->v = (vector*)(wl->w + 1 );
		wl->s = (  WSEG*)(wl->v + nv);
		if(wl->w->nv) {        /* 頂点座標有り */
			wl->stat = WIRELOAD_STAT_VECTOR_START;
			wl->iv   = 0;
		} else if(wl->w->ns) { /* セグメント有り */
			wl->stat = WIRELOAD_STAT_WSEG_START;
			wl->is   = 0;
		} else {               /* 空ワイヤフレーム */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	case WIRELOAD_STAT_VECTOR_START:
		if(strcasecmp(name, "vector") ||
		   !atts[0] || strcasecmp(atts[0], "x") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "y") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "z") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
//{{2008/08/10:変更
//		wl->v[wl->iv].x = (fixed)atoi(atts[1]);
//		wl->v[wl->iv].y = (fixed)atoi(atts[3]);
//		wl->v[wl->iv].z = (fixed)atoi(atts[5]);
//↓2008/08/10:変更
		if(wl->convert_fixed) {
			wl->v[wl->iv].x = fld(atof(atts[1]));
			wl->v[wl->iv].y = fld(atof(atts[3]));
			wl->v[wl->iv].z = fld(atof(atts[5]));
		} else {
			float x = atof(atts[1]);
			float y = atof(atts[3]);
			float z = atof(atts[5]);
			wl->v[wl->iv].x = *(fixed*)&x;
			wl->v[wl->iv].y = *(fixed*)&y;
			wl->v[wl->iv].z = *(fixed*)&z;
		}
//}}2008/08/10:変更
		wl->stat = WIRELOAD_STAT_VECTOR_END;
		break;

	case WIRELOAD_STAT_WSEG_START:
		if(strcasecmp(name, "wseg") ||
		   !atts[0] || strcasecmp(atts[0], "iv0") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "iv1") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		wl->s[wl->is].iv0 = atoi(atts[1]);
		wl->s[wl->is].iv1 = atoi(atts[3]);
		wl->stat = WIRELOAD_STAT_WSEG_END;
		break;

	default:
		DIE();
	}
}

static void
wire_load_end_element(void* user_data, const char* name)
{
	WIRELOAD* wl = user_data;

	switch(wl->stat) {
	case WIRELOAD_STAT_WIRE_END:
		if(strcasecmp(name, "wire")) {
			DIE();
		}
		wl->stat = WIRELOAD_STAT_EOF;
		break;

	case WIRELOAD_STAT_VECTOR_END:
		if(strcasecmp(name, "vector")) {
			DIE();
		}
		if(++wl->iv < wl->w->nv) { /* 頂点座標継続 */
			wl->stat = WIRELOAD_STAT_VECTOR_START;
		} else if(wl->w->ns) {     /* セグメント有り */
			wl->stat = WIRELOAD_STAT_WSEG_START;
			wl->is   = 0;
		} else {                   /* ワイヤフレーム終了 */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	case WIRELOAD_STAT_WSEG_END:
		if(strcasecmp(name, "wseg")) {
			DIE();
		}
		if(++wl->is < wl->w->ns) { /* セグメント継続 */
			wl->stat = WIRELOAD_STAT_WSEG_START;
		} else {                   /* ワイヤフレーム終了 */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	default:
		DIE();
	}
}

WIRE*
//{{2008/08/10:変更
//wire_load(const char* filename)
//↓2008/08/10:変更
wire_load(const char* filename, int convert_fixed)
//}}2008/08/10:変更
{
	int result;
	int size;
	int done;
	FILE* fp;
	WIRELOAD* wl;
	WIRE* w;

	/* ファイルを開きます。 */
	fp = fopen(filename, "rt");
	if(!fp) {
		DIE();
	}

	/* WIRELOAD構\造体のメモリを確保します。 */
	wl = calloc(1, sizeof(WIRELOAD));
	if(!wl) {
		DIE();
	}

//{{2008/08/10:追加
	wl->convert_fixed = convert_fixed;
//}}2008/08/10:追加

	/* XMLパーサを作成します。 */
	wl->parser = XML_ParserCreate(NULL);
	if(!wl->parser) {
		DIE();
	}

	/* ユーザデータを設定します。 */
	XML_SetUserData(wl->parser, wl);

	/* 要素ハンドラを設定します。 */
	XML_SetElementHandler(wl->parser, wire_load_start_element, wire_load_end_element);

	do {
		/* ファイルを読み込みます。 */
		size = fread(wl->buf, 1, WIRELOAD_BUFLEN, fp);

		/* ファイル終端に達したか調べます。 */
		done = feof(fp);

		/* 読み込んだ文字列をパースします。 */
		result = XML_Parse(wl->parser, wl->buf, size, done);
		if(result == XML_STATUS_ERROR) {
			DIE();
		}
	} while(!done); /* ファイル終端に達するまで */

	/* ワイヤフレーム全体のメモリを取得します。 */
	w = wl->w;

	/* XMLパーサを削除します。 */
	XML_ParserFree(wl->parser);

	/* WIRELOAD構\造体のメモリを開放します。 */
	free(wl);

	/* ファイルを閉じます。 */
	fclose(fp);

	return w;
}

