/*	
 *	clipwire.c
 *
 *	ワイヤーフレーム描画を定義します。
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Tue Dec 21 04:30:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Thu Dec 23 09:08:00 JST 2004 Naoyuki Sawa
 *	- Zクリッピングのバグを修正しました。
 *	  (fcomを忘れてそのまま比較していて、符号無し比較になってしまっていました)
 *	* Sun Dec 26 16:10:00 JST 2004 Naoyuki Sawa
 *	- render_line_3d()を追加しました。
 *	* Wed Jul 05 03:16:43 JST 2006 Naoyuki Sawa
 *	- render_line_3d()の引数型を変更しました。
 *	  これまで、始点・終点の(x,y,z)座標をすべてバラで引き渡していたのを変更し、
 *	  始点ベクトルと終点ベクトルへのconstポインタにて引き渡すよう変更しました。
 *	  この変更により、引数の数が4つに収まり、すべてレジスタ渡しとなって、高速化されます。
 *	- 2006/07/05現在、本ライブラリにおいて、(const vector*)型の引数を使うのは初めてです。
 *	  これまでは、変更前のように、ベクトルの(x,y,z)座標をすべてバラで引き渡すか、
 *	  または、ベクトルの値渡しとしていました。(主に、clipfixモジュールを参照してください)
 *	  今回の変更は、ライブラリ全体から見るとやや統一性に欠けますが、前述した高速化の利点と、
 *	  変更前の仕様では引数が多すぎて見た目にも判り辛かったことを考慮し、変更を決定しました。
 *	  本関数を利用しているプログラムは、まだほとんど無いので、変更の影響は小さいと思います。
 */
#include "clip.h"

void
render_wire(RENDER* render, const WIRE* wire, int color)
{
	/* 変換済み頂点座標バッファを_def_vbuff[]上に確保します。 */
#define NV_MAX (sizeof _def_vbuff / sizeof(vector)) /* 128×88÷12=938 */
	vector* xv = (vector*)_def_vbuff/*[NV_MAX]*/;
	//
	int i;
	int nv;
	int ns;
	int iv;
	fixed f;
	fixed b;
	const vector* v;
	const WSEG* s;
	vector v0;
	vector v1;
	matrix m;

	/* 頂点座標リスト、セグメントリストを取得します。 */
	nv = wire->nv;
	if((nv < 0) || (nv > NV_MAX)) {
		DIE(); /* 頂点座標数不正 */
	}
	ns = wire->ns;
	if(ns < 0) {
		DIE(); /* セグメント数不正 */
	}
	v = (const vector*)(wire + 1);
	s = (const WSEG*)(v + nv);

	/* あらかじめ、全ての頂点座標を変換しておきます。 */
	m = render->context->matrix;
	for(i = 0;
	    i < nv;
	    i++, v++, xv++) {
		*xv = vxform(m, *v);
	}
	xv -= nv;  /* 変換済み頂点座標へのポインタを、バッファの先頭アドレスへ戻します */
	//v -= nv; /* 頂点座標リストはもう不要なので、こちらは先頭へ戻す必要ありません */

	/* 各セグメントを描きます。 */
	f = render->front;
	b = render->back;
	for(i = 0;
	    i < ns;
	    i++, s++) {

		/* 始点、終点を取得します。 */
		iv = s->iv0;
		if((iv < 0) || (iv > nv)) {
			DIE(); /* 始点インデクス不正 */
		}
		v0 = xv[iv];
		iv = s->iv1;
		if((iv < 0) || (iv > nv)) {
			DIE(); /* 終点インデクス不正 */
		}
		v1 = xv[iv];

		/* Zクリッピングを行います。 */
#define CLIP(A, B, OP, LIM) 								\
		if(!fcom(v##A.z, OP, LIM)) {						\
			if(!fcom(v##B.z, OP, LIM)) {					\
				continue;						\
			} else {							\
				fixed xAB = fsub(v##B.x, v##A.x);			\
				fixed yAB = fsub(v##B.y, v##A.y);			\
				fixed zAB = fsub(v##B.z, v##A.z);			\
				fixed zAL = fsub(LIM   , v##A.z);			\
				v##A.x = fadd(v##A.x, fdiv(fmul(xAB, zAL), zAB));	\
				v##A.y = fadd(v##A.y, fdiv(fmul(yAB, zAL), zAB));	\
				v##A.z = LIM;						\
			}								\
		}
		CLIP(0, 1, >=, f);
		CLIP(1, 0, >=, f);
		CLIP(0, 1, <=, b);
		CLIP(1, 0, <=, b);
#undef CLIP

		/* 透視変換を行います。
		 * * render->context->matrixにビューポート変換行列が掛かっていること前提です。
		 *   ビューポート変換行列は、mviewport()ではなくmviewportz()を使ってください。
		 *   以上の条件が満たされない場合、描画結果は予測できません。
		 *   (cliprend.c render_mesh()にはこの件に関するコメントを書きませんでしたが、
		 *    変換行列の前提に関しては、render_mesh()もrender_wire()と同し条件です。)
		 */
		v0.x = fdiv(v0.x, v0.z);
		v0.y = fdiv(v0.y, v0.z);
		v1.x = fdiv(v1.x, v1.z);
		v1.y = fdiv(v1.y, v1.z);

		/* セグメントを描きます。 */
		render_line(render, fist(v0.x), fist(v0.y), fist(v1.x), fist(v1.y), color);
	}
}

void
//render_line_3d(RENDER* render, fixed x0, fixed y0, fixed z0, fixed x1, fixed y1, fixed z1, int color)
//↓Wed Jul 05 03:16:43 JST 2006 Naoyuki Sawa
render_line_3d(RENDER* render, const vector* v0, const vector* v1, int color)
{
	struct {
		WIRE w;
		vector v[2];
		WSEG s[1];
	} wire;
	/* 構造体の初期化構文を使うのは危険です。下のコメントを参照してください。 */
	wire.w.nv = 2;
	wire.w.ns = 1;
	//wire.v[0].x = x0;
	//wire.v[0].y = y0;
	//wire.v[0].z = z0;
	//↓Wed Jul 05 03:16:43 JST 2006 Naoyuki Sawa
#if 0
	wire.v[0] = *v0;	/* こう書くと、memcpy()が呼び出されてしまいます */
#else
	wire.v[0].x = v0->x;	/* こう書けば直接代入になり、memcpy()よりも高速 */
	wire.v[0].y = v0->y;
	wire.v[0].z = v0->z;
#endif
	//wire.v[1].x = x1;
	//wire.v[1].y = y1;
	//wire.v[1].z = z1;
	//↓Wed Jul 05 03:16:43 JST 2006 Naoyuki Sawa
#if 0
	wire.v[1] = *v1;	/* こう書くと、memcpy()が呼び出されてしまいます */
#else
	wire.v[1].x = v1->x;	/* こう書けば直接代入になり、memcpy()よりも高速 */
	wire.v[1].y = v1->y;
	wire.v[1].z = v1->z;
#endif
	wire.s[0].iv0 = 0;
	wire.s[0].iv1 = 1;
	render_wire(render, &wire.w, color);
}
//	* Sun Dec 26 16:10:00 JST 2004 Naoyuki Sawa
//	- 以下のコードは、"WSEG s[1]"で充分なところをたまたま間違って"WSEG s[2]"とした例です。
//	  少しメモリが無駄ですが、動作上は問題ないはずです。
//
//		void
//		render_line_3d(RENDER* render, vector v0, vector v1, int color)
//		{
//			struct {
//				WIRE w;
//				vector v[2];
//				WSEG s[2];	/* ★無駄★ */
//			} wire = {
//				{ 2, 1 },	/* wire */
//				{ v0, v1 },	/* v[0..1] */
//				{ { 0, 1 } },	/* s[0] */
//			};
//			render_wire(render, &wire.w, color);
//		}
//
//	  ところが、このコードをpcc33.exeでコンパイルすると、s[]の初期化が行われません!!
//	  次のように修正すると、正しくs[]の初期化が行われます。
//
//		void
//		render_line_3d(RENDER* render, vector v0, vector v1, int color)
//		{
//			struct {
//				WIRE w;
//				vector v[2];
//				WSEG s[1];	/* ★修正★ */
//			} wire = {
//				{ 2, 1 },	/* wire */
//				{ v0, v1 },	/* v[0..1] */
//				{ { 0, 1 } },	/* s[0] */
//			};
//			render_wire(render, &wire.w, color);
//		}
//
//	なぜこのような振る舞いになるのか不明です。
//	そもそも定数以外で構造体を初期化するのはC言語使用違反なのですが、GCCやVC++拡張ではOKのはずです。
//	前者の例でs[]の初期化が行われない部分は定数で初期化しているわけですし。。。
//	複雑にネストした構造体を初期化する場合は、初期化構文を使わず明示的に代入する方が安全みたいです。
