/*	
 *	clipren3.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2008 Naoyuki Sawa
 *
 *	* Wed Feb 13 06:00:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Fri Mar  8 07:00:00 JST 2002 Naoyuki Sawa
 *	- render_line()で終点側がクリッピングされた時に、
 *	  画面端に1ピクセル隙間が出来てしまう問題を修正。
 *	* Wed Apr 24 10:11:00 JST 2002 Naoyuki Sawa
 *	- render_polygon()を追加。
 *	* Wed Jun  5 13:51:00 JST 2002 Naoyuki Sawa
 *	- render_point()を追加。
 *	* Wed Jun 18 21:39:00 JST 2002 Naoyuki Sawa
 *	- render_fill()を追加。
 *	* Sat Jul  6 08:42:00 JST 2002 Naoyuki Sawa
 *	- surface_point()を追加。
 *	* Sat Jul  6 17:51:00 JST 2002 Naoyuki Sawa
 *	- render_fill()の関数名をrender_paint()に変更。
 *	- render_rectangle()を追加。
 *	* Thu Jul 25 04:07:00 JST 2002 Naoyuki Sawa
 *	- render_rectangle_fill()を追加。
 *	* Sun Jul 28 16:56:00 JST 2002 Naoyuki Sawa
 *	- render_paint()に戻り値(塗ったピクセル数)を追加。
 *	* Sat Sep 21 23:02:00 JST 2002 Naoyuki Sawa
 *	- render_dashes()追加。
 *	* Sat Oct 21 15:58:00 JST 2002 Naoyuki Sawa
 *	- render_dashes()に戻り値を追加。
 *	* Sat Mar  8 06:23:00 JST 2003 Naoyuki Sawa
 *	- 描画用ワーク(xformed_vectors[],clip_vertices[]など)を_def_vbuff[]上に確保。
 *	* Wed Mar 26 05:00:00 JST 2003 Naoyuki Sawa
 *	- 浮動小数→固定小数変更につき、ジオメトリステージを大幅に改造。
 *	  浮動小数版の旧ソースは keep/浮動小数版演算描画コード保存 に保存してあります。
 *	  固定小数版もロジックは浮動小数版と同じで、可読性は浮動小数版の方が高いので、
 *	  浮動小数版の旧ソースは破棄しないで下さい。
 *	* Sun Mar 30 19:00:00 JST 2003 Naoyuki Sawa
 *	- スキャンライン描画関数をアセンブラで高速化しました。40%ぐらい速くなりました。
 *	  NOASMシンボルの定義/未定義によって、Cルーチンとアセンブラルーチンを切り替えられます。
 *	- まずは使用頻度の高い、Ｚ比較ありの、color・2bit・2bitmaskだけをアセンブラ化しました。
 *	  その他のスキャンライン描画関数も、今後必要に応じてアセンブラ化する予定です。
 *	  draw_scanline_wz_2bitmaskが全機能バージョンですので、これを基に削ればOKだと思います。
 *	* Tue Apr  1 05:13:00 JST 2003 Naoyuki Sawa
 *	- クリッピングを少し高速化。
 *	  始点・終点座標インデクスの計算に剰余演算が使われていたのを、条件分岐に変更。
 *	* Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa
 *	- スキャンライン描画関数の一部を高速RAM転送候補としてframds??.cへ分離。
 *	  それに伴い、構造体の定義をcliprend.hへ移動。
 *	  また、c/asm切り換えシンボルをNOASM→REND_NOASMに改名し、cliprend.hへ移動。
 *	* Tue Apr  2 08:00:00 JST 2003 Naoyuki Sawa
 *	- clip_z(),clip_xy()にクリッピング不要検査を追加し、高速化。
 *	* Sun Sep 14 18:44:00 JST 2003 Naoyuki Sawa
 *	- cliprend.c→clipren2.c(2D描画)/clipren3.d(3D描画)に分割。
 *	- render_init()をrender_init_2d()/render_init_3d()に分割。
 *	* Thu Jul  8 06:00:00 JST 2004 Naoyuki Sawa
 *	- fcom()とficom()の引数順序変更に追従。
 *	* Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa
 *	- draw_scanline_wz_1bit()をframds12.cへ移動しました。
 *	- draw_scanline_wz_1bitmask()をframds13.cへ移動しました。
 *	- draw_scanline_woz_color()をframds21.cへ移動しました。
 *	- draw_scanline_woz_1bit()をframds22.cへ移動しました。
 *	- draw_scanline_woz_1bitmask()をframds23.cへ移動しました。
 *	- draw_scanline_woz_2bit()をframds24.cへ移動しました。
 *	- draw_scanline_woz_2bitmask()をframds25.cへ移動しました。
 *	* Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
 *	- select_draw_scanline_wz()とselect_draw_scanline_woz()に、以下の変更を行いました。
 *	- colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
 *	  必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
 *	- テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
 *	  前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
 *	* Sun Jan 13 13:34:20 JST 2008 Naoyuki Sawa
 *	- draw_primitive_wz()をframdp1.cへ、draw_primitive_woz()をframdp2.cへ、移動しました。
 *	  draw_primitive_wz()とdraw_primitive_woz()を、今後、アセンブラ化するための準備です。
 *	- この変更に伴い、いくつかの細かな変更を行いました。
 *	  変更箇所は、cliprend.hとclipren3.cを、「2008/01/13」で検索してください。
 *	* Mon Jan 14 23:18:06 JST 2008 Naoyuki Sawa
 *	- draw_primitive_woz()とdraw_primitive_wz()の統合に追従しました。
 *	  変更箇所は、本モジュールを、「2008/01/14」で検索してください。
 */
#include "clip.h"

/*****************************************************************************
 *	RENDER
 *****************************************************************************/

/* 静的なワークエリアを、_def_vbuff[]上に確保するための構造体です。 */
#define N_VERTICES_MAX	8				/* 1ポリゴン当たりの最大頂点数 */
typedef struct _RENDER_WORK {
	vector xformed_vectors[255];			/* 12*255=3060: 変換済み頂点座標バッファ（MESH.nvが符号無し8ビットなので、最大255頂点です） */
	CLIPVERTEX clip_vertices[2][N_VERTICES_MAX];	/* 20*2*8= 320: クリッピング用頂点バッファ         */
	RENDVERTEX rend_vertices[N_VERTICES_MAX];	/* 10*8  =  80: 描画用頂点バッファ                 */
} RENDER_WORK;						/* 合計 = 3460: _def_vbuff[128*88=11264]のサイズを超えないこと！（まだ3倍以上余裕あります） */
#define render_work	((RENDER_WORK*)_def_vbuff)	/* 描画用一時ワークは、_def_vbuff[]上に確保します。 */
							/* これらのワークはローカル関数間の連絡のみに利用され、グローバル関数間で保持する必要はないので、 */
							/* 基本的にユーザーアプリケーションから意識する必要はありません。 */
							/* ただし、直接_def_vbuff[]に描画しようとすると、描画中にワークが破壊され、正しく動作しません。 */
							/* 描画ターゲットに_def_vbuff[]を指定しないよう、ご注意ください。 */

/* クリッピング */
static int  clip_z(RENDER* render, int nsrc);
static int clip_xy(RENDER* render, int nsrc);

/* スキャンライン描画関数の選択と呼び出し */
/* (Ｚコンペアあり) */
//static void draw_primitive_wz(RENDER* render, int n_vertices, int color);		{{2008/01/13:削除->framdp1.cへ移動しました。}}
//static DSLPROC select_draw_scanline_wz(DSLPARAM* param, RENDER* render, int color);	{{2008/01/13:削除->render_mesh()内に処理を含めました。}}
/* (Ｚコンペアなし) */
//static void draw_primitive_woz(RENDER* render, int n_vertices, int color);		{{2008/01/13:削除->framdp2.cへ移動しました。}}
//static DSLPROC select_draw_scanline_woz(DSLPARAM* param, RENDER* render, int color);	{{2008/01/13:削除->render_polygon()内に処理を含めました。}}

void
render_init_3d(RENDER* render, SURFACE* surface, int x, int y, int width, int height, fixed front, fixed back, fixed fovy)
{
	/* まず2D用として初期化します。 */
	render_init_2d(render, surface, x, y, width, height);

	/* クリッピング距離を設定します。 */
	render->front = front;
	render->back = back;

	/* 重要！render_init_2d()での処理内容と重複しますが、こちらでもこの処理が必要です！
	 *       これを忘れると、このあと変換行列の初期化で、ゼロ除算エラーが発生します！！
	 */
	if(width == 0) width = surface->w;
	if(height == 0) height = surface->h;

	/* 変換行列を初期化します。(コンテキストスタックはrender_init_2d()で初期化済み) */
	render->context->matrix = MATRIX_I;
	render->context->matrix = mviewportz(render->context->matrix, x, y, width, height);
	render->context->matrix = mperspect (render->context->matrix, fovy, fidiv(fld(width), height), front);
}

/* * ポリゴン描画は全て整数で処理できますが、現在の実装ではそうなっていません。
 *   メッシュ描画のコードを流用したため、クリッピングに浮動小数を使っています。→2003/03/25 固定小数になりました
 *   この部分を独自の整数処理に書き直せば、大幅に高速化できるはずです。(※TODO)
 * * と思いましたが、クリッピングの改良だけでは、3%程度しか速くならないようです。
 *   ポリゴン描画ではＺコンペアが不要なので、これを省略すると25%程度速くなります。
 *   高速化のために、Ｚコンペア無しのスキャンライン描画を追加するのが良さそうです。
 * * ->やってみました。
 */
void
render_polygon(RENDER* render, const VERTEX* vertices, int n_vertices, int color)
{
	int i;
	const VERTEX* vertex;
	CLIPVERTEX *clip_vertex;
	RENDVERTEX *rend_vertex;

//{{2008/01/13:追加。これまではselect_draw_scanline_woz()で行っていた処理です。
	DSLPARAM param;
	DSLPROC draw_scanline;
	SURFACE* surface = render->surface;
	TEXTURE* texture = render->context->texture;
	param.surface_w = surface->w;
	param.surface_h = surface->h;
	param.surface_vbuff = surface->vbuff;
	if(!texture) {
		draw_scanline = draw_scanline_woz_color;
	} else {
		param.texture_w = texture->header.w;
		param.texture_h = texture->header.h;
		param.texture_buf = texture->buf;
		switch(texture->header.bpp) {
		case 1:
			if(!texture->header.mask) {
				draw_scanline = draw_scanline_woz_1bit;
			} else {
				param.texture_mask = texture->mask;
				draw_scanline = draw_scanline_woz_1bitmask;
			}
			break;
		default/*2*/:
			if(!texture->header.mask) {
				draw_scanline = draw_scanline_woz_2bit;
			} else {
				param.texture_mask = texture->mask;
				draw_scanline = draw_scanline_woz_2bitmask;
			}
			break;
		}
	}
//}}2008/01/13:追加。これまではselect_draw_scanline_woz()で行っていた処理です。

	/* クリッピング用頂点リストを組み立てます。 */
	if(n_vertices > N_VERTICES_MAX) n_vertices = N_VERTICES_MAX;
	for(i = 0, vertex = vertices, clip_vertex = render_work->clip_vertices[0];
	    i < n_vertices;
	    i++, vertex++, clip_vertex++) {
		clip_vertex->x = fild(vertex->x);
		clip_vertex->y = fild(vertex->y);
		clip_vertex->z = fild(1); /* 平面クリッピングのためのダミー。0以外なら何でもOKです。 */
		clip_vertex->s = fadd(fild(vertex->s), fld(0.5)); /* テクセルの中心をポイント */
		clip_vertex->t = fadd(fild(vertex->t), fld(0.5)); /* テクセルの中心をポイント */
	}
	if(n_vertices < 3) return;

	/* 平面クリッピングを行います。 */
	n_vertices = clip_xy(render, n_vertices);
	if(n_vertices < 3) return;

	/* 描画用頂点リストを組み立てます。 */
	for(i = 0, clip_vertex = render_work->clip_vertices[0], rend_vertex = render_work->rend_vertices;
	    i < n_vertices;
	    i++, clip_vertex++, rend_vertex++) {
		rend_vertex->x = fist(clip_vertex->x);
		rend_vertex->y = fist(clip_vertex->y);
//{{2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
//		/* draw_primitive_woz()はrend_vertex->zを使わないので、不定値のままで大丈夫です。 */
//↓ * Mon Jan 14 23:18:06 JST 2008 Naoyuki Sawa
//↓ - draw_primitive()はrend_vertex->zに対してDDAを行うので、z=0を設定しておくことにします。
//↓   不定値でも描画結果には影響しませんが、DDAの条件分岐パスが不定となり、実行速度がばらつくおそれがあります。
//↓   そこで、確実のために、z=0としておくことにしました。
		rend_vertex->z = 0;
//}}2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
		rend_vertex->s = fist(fimul(clip_vertex->s, 1 << 8)); /* 8.8bit */
		rend_vertex->t = fist(fimul(clip_vertex->t, 1 << 8)); /* 8.8bit */
	}

//{{2008/01/13:変更
//	/* プリミティブを描きます。 */
//	draw_primitive_woz(render, n_vertices, color);
//}}2008/01/13:変更{{
	/* プリミティブを描きます。 */
	param.fore_color = color      & 15;
	param.back_color = color >> 4 & 15;
	//draw_primitive_woz(render_work->rend_vertices, n_vertices, &param, draw_scanline);
	//↓2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
	draw_primitive(render_work->rend_vertices, n_vertices, &param, draw_scanline);
//}}2008/01/13:変更
}

void
render_mesh(RENDER* render, const MESH* mesh)
{
	int i, j, n_vectors, n_tcoords, n_faces, n_face_vertices, n_vertices;
	fixed s, xN0, yN0, x01, y01;
	struct _vector *vectors, *vector, *vector_src, *vector_dst; /* 変数名vectorとの衝突回避のため、構造体名にはstruct_vectorを使用 */
	TCOORD *tcoords, *tcoord;
	FACE *face;
	FACEVERTEX *face_vertices, *face_vertex;
	CLIPVERTEX *clip_vertex;
	RENDVERTEX *rend_vertex;

//{{2008/01/13:追加。これまではselect_draw_scanline_wz()で行っていた処理です。
	DSLPARAM param;
	DSLPROC draw_scanline;
	SURFACE* surface = render->surface;
	TEXTURE* texture = render->context->texture;
	param.surface_w = surface->w;
	param.surface_h = surface->h;
	param.surface_vbuff = surface->vbuff;
	param.surface_zbuff = surface->zbuff;
	if(!texture) {
		draw_scanline = draw_scanline_wz_color;
	} else {
		param.texture_w = texture->header.w;
		param.texture_h = texture->header.h;
		param.texture_buf = texture->buf;
		switch(texture->header.bpp) {
		case 1:
			if(!texture->header.mask) {
				draw_scanline = draw_scanline_wz_1bit;
			} else {
				param.texture_mask = texture->mask;
				draw_scanline = draw_scanline_wz_1bitmask;
			}
			break;
		default/*2*/:
			if(!texture->header.mask) {
				draw_scanline = draw_scanline_wz_2bit;
			} else {
				param.texture_mask = texture->mask;
				draw_scanline = draw_scanline_wz_2bitmask;
			}
			break;
		}
	}
//}}2008/01/13:追加。これまではselect_draw_scanline_wz()で行っていた処理です。

	/* リストの先頭アドレスを計算します。 */
	n_vectors = mesh->nv;
	n_tcoords = mesh->nc;
	n_faces = mesh->nf;
	vectors = (struct _vector*)(mesh + 1); /* 変数名vectorとの衝突回避のため、構造体名にはstruct_vectorを使用 */
	tcoords = (TCOORD*)(vectors + n_vectors);
	face = (FACE*)(tcoords + n_tcoords);

	/* 全頂点座標を変換します。 */
	for(i = 0, vector_src = vectors, vector_dst = render_work->xformed_vectors;
	    i < n_vectors;
	    i++, vector_src++, vector_dst++) {
		*vector_dst = vxform(render->context->matrix, *vector_src);
	}

	/* 各フェイスを描きます。 */
	for(i = 0;
	    i < n_faces;
	    i++, face = (FACE*)(face_vertices + n_face_vertices)) {
		n_face_vertices = face->nfv;
		face_vertices = (FACEVERTEX*)(face + 1);

		/* クリッピング用頂点リストを組み立てます。
		 * * テクスチャ無しの場合分けを行っていませんが、動作に問題ないはずです。
		 *   テクスチャ無しならmesh->nc=0なので、tcoordsは最初のフェイスを指しています。
		 *   各フェイス頂点のface_vertex->ic=0なので、テクスチャ座標として最初のフェイスのm,nを取得します。
		 *   mesh->nf=0の場合のみ最初のフェイスが存在しませんが、その場合はこのループ自体が回りません。
		 *   取得したテクスチャ座標は描画時に無視されるので、テクスチャ座標値は関係ありません。
		 *   上記の通り、テクスチャ座標取得時に不正なメモリ領域にアクセスしないことが保証されます。
		 *   従って、テクスチャ無しの場合分けを行わなくても、動作に問題はないと思います。
		 */
		n_vertices = 0;
		for(j = 0, face_vertex = face_vertices, clip_vertex = render_work->clip_vertices[0];
		    j < n_face_vertices;
		    j++, face_vertex++, clip_vertex++) {
			vector = &render_work->xformed_vectors[face_vertex->iv];
			tcoord = &tcoords[face_vertex->ic];
			clip_vertex->x = vector->x;
			clip_vertex->y = vector->y;
			clip_vertex->z = vector->z;
			clip_vertex->s = fadd(fild(tcoord->s), fld(0.5)); /* テクセルの中心をポイント */
			clip_vertex->t = fadd(fild(tcoord->t), fld(0.5)); /* テクセルの中心をポイント */
			if(++n_vertices == N_VERTICES_MAX) break;
		}
		if(n_vertices < 3) continue;

		/* Ｚクリッピングを行います。 */
		n_vertices = clip_z(render, n_vertices);
		if(n_vertices < 3) continue;

		/* 透視変換のための x=x/z,y=y/z を行います。 */
		for(j = 0, clip_vertex = render_work->clip_vertices[0];
		    j < n_vertices;
		    j++, clip_vertex++) {
			clip_vertex->x = fdiv(clip_vertex->x, clip_vertex->z);
			clip_vertex->y = fdiv(clip_vertex->y, clip_vertex->z);
		}

		/* 表裏判定を行います。
		 * 右回り(=表)記述なので、左回り(=裏)なら描きません。
		 */
		xN0 = fsub(render_work->clip_vertices[0][0].x, render_work->clip_vertices[0][n_vertices - 1].x);
		yN0 = fsub(render_work->clip_vertices[0][0].y, render_work->clip_vertices[0][n_vertices - 1].y);
		x01 = fsub(render_work->clip_vertices[0][1].x, render_work->clip_vertices[0][             0].x);
		y01 = fsub(render_work->clip_vertices[0][1].y, render_work->clip_vertices[0][             0].y);
		s = fsub(fmul(xN0, y01), fmul(x01, yN0));
		if(ficom(s, <=, 0)) continue;

		/* 平面クリッピングを行います。 */
		n_vertices = clip_xy(render, n_vertices);
		if(n_vertices < 3) continue;

		/* 描画用頂点リストを組み立てます。 */
		for(j = 0, clip_vertex = render_work->clip_vertices[0], rend_vertex = render_work->rend_vertices;
		    j < n_vertices;
		    j++, clip_vertex++, rend_vertex++) {
			rend_vertex->x = fist(clip_vertex->x);
			rend_vertex->y = fist(clip_vertex->y);
			rend_vertex->z = fist(fimul(fdiv(fmul(fsub(clip_vertex->z, render->front), render->back  ),
							 fmul(fsub(render->back  , render->front), clip_vertex->z)), 0xffff)); /* 8.8bit */
			rend_vertex->s = fist(fimul(clip_vertex->s, 1 << 8)); /* 8.8bit */
			rend_vertex->t = fist(fimul(clip_vertex->t, 1 << 8)); /* 8.8bit */
		}

//{{2008/01/13:変更
//		/* プリミティブを描きます。 */
//		draw_primitive_wz(render, n_vertices, face->c);
//}}2008/01/13:変更{{
		/* プリミティブを描きます。 */
		param.fore_color = face->c      & 15;
		param.back_color = face->c >> 4 & 15;
		//draw_primitive_wz(render_work->rend_vertices, n_vertices, &param, draw_scanline);
		//↓2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
		draw_primitive(render_work->rend_vertices, n_vertices, &param, draw_scanline);
//}}2008/01/13:変更
	}
}

/* Ｚクリッピングを行います。
 * [in]
 *	render				レンダラーへのポインタ。
 *	nsrc				クリッピングする頂点の数。
 *	render_work->clip_vertices[0]	クリッピングする頂点リスト。(グローバル変数)
 * [out]
 *	戻り値				クリッピング後の頂点の数。
 *	render_work->clip_vertices[0]	クリッピング後の頂点リスト。(グローバル変数)
 * [note]
 *	* 作業領域として、render_work->clip_vertices[1]も使います。
 */
static int
clip_z(RENDER* render, int nsrc)
{
#define CLIP(SRC, DST, OP, LIM) \
	src = SRC; \
	dst = DST; \
	ndst = 0; \
	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
		v1 = i < nsrc - 1 ? v0 + 1 : src; \
		if(fcom(v0->z, OP, LIM)) { \
			*dst = *v0; \
			dst++; \
			ndst++; \
			if(ndst == N_VERTICES_MAX) break; \
			if(fcom(v1->z, OP, LIM)) { \
				continue; \
			} else { \
				/* go clip */ \
			} \
		} else { \
			if(fcom(v1->z, OP, LIM)) { \
				/* go clip */ \
			} else { \
				continue; \
			} \
		} \
		/* do clip */ \
		dst->z = LIM; \
		n0 = fsub(LIM, v0->z); \
		n1 = fsub(v1->z, LIM); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->x = fadd(v0->x, fmul(fsub(v1->x, v0->x), n001)); \
		dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001)); \
		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
		dst++; \
		ndst++; \
		if(ndst == N_VERTICES_MAX) break; \
	} \
	if(ndst < 3) return 0; \

	CLIPVERTEX *src, *dst;
	CLIPVERTEX *v0, *v1;
	int i, ndst;
	fixed n0, n1, n01, n001;
	fixed front = render->front;
	fixed back  = render->back ;

	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
	 */
	v0 = render_work->clip_vertices[0];
	v1 = v0 + nsrc;
	while(v0 < v1) {
		if(!fcom(v0->z, >=, front)) break;
		if(!fcom(v0->z, <=, back )) break;
		v0++;
	}
	if(v0 == v1) return nsrc;

	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
	 */

	/* レンジ最大値（back）との比較が「<」ではなく「<=」である理由について説明します。
	 * render_mesh()内で描画用頂点リストを組み立てている処理をご覧いただければわかるように、
	 * z=back の頂点を Zバッファ値=0xff（8.8bitでは0xffff） にマップしています。
	 * z=back の頂点も可視です。従って、z=back もクリッピング内として「<=」で判定しています。
	 */
	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], >=, front); nsrc = ndst;
	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], <=, back );
	return ndst;

#undef CLIP
}

/* 平面クリッピングを行います。
 * [in]
 *	render				レンダラーへのポインタ。
 *	nsrc				クリッピングする頂点の数。
 *	render_work->clip_vertices[0]	クリッピングする頂点リスト。(グローバル変数)
 * [out]
 *	戻り値				クリッピング後の頂点の数。
 *	render_work->clip_vertices[0]	クリッピング後の頂点リスト。(グローバル変数)
 * [note]
 *	* 作業領域として、render_work->clip_vertices[1]も使います。
 */
static int
clip_xy(RENDER* render, int nsrc)
{
#define CLIP(SRC, DST, X, Y, OP, LIM) \
	src = SRC; \
	dst = DST; \
	ndst = 0; \
	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
		v1 = i < nsrc - 1 ? v0 + 1 : src; \
		if(fcom(v0->X, OP, LIM)) { \
			*dst = *v0; \
			dst++; \
			ndst++; \
			if(ndst == N_VERTICES_MAX) break; \
			if(fcom(v1->X, OP, LIM)) { \
				continue; \
			} else { \
				/* go clip */ \
			} \
		} else { \
			if(fcom(v1->X, OP, LIM)) { \
				/* go clip */ \
			} else { \
				continue; \
			} \
		} \
		/* do clip */ \
		dst->X = LIM; \
		n0 = fsub(LIM, v0->X); \
		n1 = fsub(v1->X, LIM); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->Y = fadd(v0->Y, fmul(fsub(v1->Y, v0->Y), n001)); \
		n0 = fmul(n0, v0->z); \
		n1 = fmul(n1, v1->z); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), n001)); \
		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
		dst++; \
		ndst++; \
		if(ndst == N_VERTICES_MAX) break; \
	} \
	if(ndst < 3) return 0; \

	CLIPVERTEX *src, *dst;
	CLIPVERTEX *v0, *v1;
	int i, ndst;
	fixed n0, n1, n01, n001;
	fixed left   = fild(render->left  );
	fixed right  = fild(render->right );
	fixed top    = fild(render->top   );
	fixed bottom = fild(render->bottom);

	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
	 */
	v0 = render_work->clip_vertices[0];
	v1 = v0 + nsrc;
	while(v0 < v1) {
		if(!fcom(v0->x, >=, left  )) break;
		if(!fcom(v0->x, <=, right )) break;
		if(!fcom(v0->y, >=, top   )) break;
		if(!fcom(v0->y, <=, bottom)) break;
		v0++;
	}
	if(v0 == v1) return nsrc;

	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
	 */

	/* レンジ最大値（right,bottom）との比較が「<」ではなく「<=」である理由について説明します。
	 * draw_primitive_*()とdraw_scanline_*_*()内の縦横ループ処理をご覧いただければわかるように、
	 * 下端と右端のピクセルは描きません。
	 * すなわち、画面上のポリゴン頂点座標は、グリッド座標系で表されます。
	 * 従って、下端・右端との比較は「<」ではなく「<=」となります。
	 */
	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], x, y, >=, left  ); nsrc = ndst;
	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], x, y, <=, right ); nsrc = ndst;
	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], y, x, >=, top   ); nsrc = ndst;
	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], y, x, <=, bottom);
	return ndst;

#undef CLIP
}

/****************************************************************************
 *	Ｚコンペアあり
 ****************************************************************************/

//{{2008/01/13:削除->framdp1.cへ移動しました。
///* プリミティブを描きます。
// * [in]
// *	render				レンダラーへのポインタ。
// *	n_vertices			頂点の数。
// *	render_work->rend_vertices	頂点リスト。(グローバル変数)
// *	color				カラー。
// */
//static void
//draw_primitive_wz(RENDER* render, int n_vertices, int color)
//{
//	struct {
//		int iv;		/* 現在の辺の終点インデクス            */
//		int ny;		/* 現在の辺の高さの変位                */
//		int n;		/* 現在の辺の残りライン数              */
//		RENDVERTEX cur;	/* 現在値                              */
//		DDA x, z;	/* 頂点座標DDA(yは左右共通で1づつ進む) */
//		DDA s, t;	/* テクスチャ座標DDA                   */
//	} sides[2], *side;
//
//	DSLPARAM param;
//	int i, itop, y, top, bottom;
//	DSLPROC draw_scanline;
//	RENDVERTEX *v, *v0, *v1;
//
//	/* スキャンライン描画パラメータを初期化します。 */
//	draw_scanline = select_draw_scanline_wz(&param, render, color);
//
//	/* 上下端の位置と、上端の頂点を求めます。 */
//	itop = 0;
//	top = bottom = render_work->rend_vertices[0].y;
//	for(i = 1; i < n_vertices; i++) {
//		v = &render_work->rend_vertices[i];
//		if(v->y < top) {
//			itop = i;
//			top = v->y;
//		}
//		if(v->y > bottom) {
//			bottom = v->y;
//		}
//	}
//	if(top == bottom) return; /* 水平ポリゴンは描きません。 */
//
//	/* 左右の辺を初期化します。 */
//	for(i = 0; i < 2; i++) {
//		side = &sides[i];
//		side->iv = itop;
//		side->n = 0; /* 初回に本当の初期化を行うように */
//	}
//
//	/* 下端を除く、各スキャンラインについて... */
//	for(y = top; y < bottom; y++) {
//		/* 左右の辺について... */
//		for(i = 0; i < 2; i++) {
//			side = &sides[i];
//			/* 次の辺に達したら... */
//			if(side->n == 0) {
//				do {
//					/* 次の辺の両端の頂点を求めます。 */
//					v0 = &render_work->rend_vertices[side->iv];
//					if(i == 0) {
//						side->iv--;
//						if(side->iv == -1) {
//							side->iv = n_vertices - 1;
//						}
//					} else {
//						side->iv++;
//						if(side->iv == n_vertices) {
//							side->iv = 0;
//						}
//					}
//					v1 = &render_work->rend_vertices[side->iv];
//					/* この辺の高さの変位を求めます。 */
//					side->ny = v1->y - v0->y;
//					/* V字ポリゴンを検出したら、残りの描画はキャンセルします。 */
//					if(side->ny < 0) return;
//				} while(side->ny == 0); /* 水平な辺は無視します。 */
//				/* DDAセットアップ。 */
//				DRAW_PRIMITIVE_SETUP(x);
//				DRAW_PRIMITIVE_SETUP(z);
//				DRAW_PRIMITIVE_SETUP(s);
//				DRAW_PRIMITIVE_SETUP(t);
//				/* 次の辺までのカウンタを初期化。 */
//				side->n = side->ny;
//			}
//		}
//		/* 左右の辺の間に、水平線を描きます。 */
//		sides[0].cur.y = sides[1].cur.y = y;
//		draw_scanline(&sides[0].cur, &sides[1].cur, &param);
//		/* 左右の辺について... */
//		for(i = 0; i < 2; i++) {
//			side = &sides[i];
//			/* 次の辺までのライン数を減らします。 */
//			side->n--;
//			/* DDAステップ実行。 */
//			DRAW_PRIMITIVE_STEP(x)
//			DRAW_PRIMITIVE_STEP(z)
//			DRAW_PRIMITIVE_STEP(s)
//			DRAW_PRIMITIVE_STEP(t)
//		}
//	}
//	/* 下端のスキャンラインは描きません。 */
//}
//}}2008/01/13:削除->framdp1.cへ移動しました。

//{{2008/01/13:削除->render_mesh()内に処理を含めました。
///* スキャンライン描画パラメータを初期化します。
// * [in]
// *	param		初期化するパラメータ構造体へのポインタ。
// *	render		レンダラーへのポインタ。
// * [out]
// *	戻り値		スキャンライン描画関数へのポインタ。
// * [note]
// *	* 使わないメンバは初期化しません。（ゼロクリアもしません）
// */
//static DSLPROC
//select_draw_scanline_wz(DSLPARAM* param, RENDER* render, int color)
//{
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	DSLPROC draw_scanline;
//
//	/* サーフェイス */
//	param->surface_w     = surface->w;
//	param->surface_h     = surface->h;
//	param->surface_vbuff = surface->vbuff;
//	param->surface_zbuff = surface->zbuff;
//
//	/* テクスチャ・カラー */
//	if(texture == NULL) {
//		//param->texture_w    = 使わない
//		//param->texture_h    = 使わない
//		//param->texture_buf  = 使わない
//		//param->texture_mask = 使わない
////{{2008/01/01:変更
////		param->back_color     = color & 15;
////		//param->fore_color   = 使わない
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
////↓ - テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
////↓   前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
//		param->fore_color     = color & 15;
//		//param->back_color   = 使わない
////}}2008/01/01:変更
//		draw_scanline = draw_scanline_wz_color;
//	} else {
//		param->texture_w   = texture->header.w;
//		param->texture_h   = texture->header.h;
//		param->texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_wz_1bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_1bitmask;
//			}
////{{2008/01/01:変更
////			param->back_color = color      & 15;
////			param->fore_color = color >> 4 & 15;
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
//			param->fore_color = color      & 15;
//			param->back_color = color >> 4 & 15;
////}}2008/01/01:変更
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_wz_2bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_2bitmask;
//			}
//			//param->fore_color = 使わない
//			//param->back_color = 使わない
//			break;
//		}
//	}
//
//	return draw_scanline;
//}
//}}2008/01/13:削除->render_mesh()内に処理を含めました。

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa            */
/* - draw_scanline_wz_color()はframds11.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa           */
/* - draw_scanline_wz_1bit()はframds12.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa               */
/* - draw_scanline_wz_1bitmask()はframds13.cへ移動しました。 */

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa           */
/* - draw_scanline_wz_2bit()はframds14.cへ移動しました。 */

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa               */
/* - draw_scanline_wz_2bitmask()はframds15.cへ移動しました。 */

/****************************************************************************
 *	Ｚコンペアなし
 ****************************************************************************/

//{{2008/01/13:削除->framdp2.cへ移動しました。
///* プリミティブを描きます。
// * [in]
// *	render				レンダラーへのポインタ。
// *	n_vertices			頂点の数。
// *	render_work->rend_vertices	頂点リスト。(グローバル変数)
// *	color				カラー。
// */
//static void
//draw_primitive_woz(RENDER* render, int n_vertices, int color)
//{
//	struct {
//		int iv;		/* 現在の辺の終点インデクス            */
//		int ny;		/* 現在の辺の高さの変位                */
//		int n;		/* 現在の辺の残りライン数              */
//		RENDVERTEX cur;	/* 現在値                              */
//		DDA x;		/* 頂点座標DDA(yは左右共通で1づつ進む) */
//		DDA s, t;	/* テクスチャ座標DDA                   */
//	} sides[2], *side;
//
//	int i, itop, y, top, bottom;
//	RENDVERTEX *v, *v0, *v1;
//	DSLPROC draw_scanline;
//	DSLPARAM param;
//
//	/* スキャンライン描画パラメータを初期化します。 */
//	draw_scanline = select_draw_scanline_woz(&param, render, color);
//
//	/* 上下端の位置と、上端の頂点を求めます。 */
//	itop = 0;
//	top = bottom = render_work->rend_vertices[0].y;
//	for(i = 1; i < n_vertices; i++) {
//		v = &render_work->rend_vertices[i];
//		if(v->y < top) {
//			itop = i;
//			top = v->y;
//		}
//		if(v->y > bottom) {
//			bottom = v->y;
//		}
//	}
//	if(top == bottom) return; /* 水平ポリゴンは描きません。 */
//
//	/* 左右の辺を初期化します。 */
//	for(i = 0; i < 2; i++) {
//		side = &sides[i];
//		side->iv = itop;
//		side->n = 0; /* 初回に本当の初期化を行うように */
//	}
//
//	/* 下端を除く、各スキャンラインについて... */
//	for(y = top; y < bottom; y++) {
//		/* 左右の辺について... */
//		for(i = 0; i < 2; i++) {
//			side = &sides[i];
//			/* 次の辺に達したら... */
//			if(side->n == 0) {
//				do {
//					/* 次の辺の両端の頂点を求めます。 */
//					v0 = &render_work->rend_vertices[side->iv];
//					if(i == 0) {
//						side->iv--;
//						if(side->iv == -1) {
//							side->iv = n_vertices - 1;
//						}
//					} else {
//						side->iv++;
//						if(side->iv == n_vertices) {
//							side->iv = 0;
//						}
//					}
//					v1 = &render_work->rend_vertices[side->iv];
//					/* この辺の高さの変位を求めます。 */
//					side->ny = v1->y - v0->y;
//					/* V字ポリゴンを検出したら、残りの描画はキャンセルします。 */
//					if(side->ny < 0) return;
//				} while(side->ny == 0); /* 水平な辺は無視します。 */
//				/* DDAセットアップ。 */
//				DRAW_PRIMITIVE_SETUP(x);
//				DRAW_PRIMITIVE_SETUP(s);
//				DRAW_PRIMITIVE_SETUP(t);
//				/* 次の辺までのカウンタを初期化。 */
//				side->n = side->ny;
//			}
//		}
//		/* 左右の辺の間に、水平線を描きます。 */
//		sides[0].cur.y = sides[1].cur.y = y;
//		draw_scanline(&sides[0].cur, &sides[1].cur, &param);
//		/* 左右の辺について... */
//		for(i = 0; i < 2; i++) {
//			side = &sides[i];
//			/* 次の辺までのライン数を減らします。 */
//			side->n--;
//			/* DDAステップ実行。 */
//			DRAW_PRIMITIVE_STEP(x)
//			DRAW_PRIMITIVE_STEP(s)
//			DRAW_PRIMITIVE_STEP(t)
//		}
//	}
//	/* 下端のスキャンラインは描きません。 */
//}
//}}2008/01/13:削除->framdp2.cへ移動しました。

//{{2008/01/13:削除->render_polygon()内に処理を含めました。
///* スキャンライン描画パラメータを初期化します。
// * [in]
// *	param		初期化するパラメータ構造体へのポインタ。
// *	render		レンダラーへのポインタ。
// * [out]
// *	戻り値		スキャンライン描画関数へのポインタ。
// * [note]
// *	* 使わないメンバは初期化しません。（ゼロクリアもしません）
// */
//static DSLPROC
//select_draw_scanline_woz(DSLPARAM* param, RENDER* render, int color)
//{
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	DSLPROC draw_scanline;
//
//	/* サーフェイス */
//	param->surface_w       = surface->w;
//	param->surface_h       = surface->h;
//	param->surface_vbuff   = surface->vbuff;
//	//param->surface_zbuff = 使わない
//
//	/* テクスチャ・カラー */
//	if(texture == NULL) {
//		//param->texture_w    = 使わない
//		//param->texture_h    = 使わない
//		//param->texture_buf  = 使わない
//		//param->texture_mask = 使わない
////{{2008/01/01:変更
////		param->back_color     = color & 15;
////		//param->fore_color   = 使わない
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
////↓ - テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
////↓   前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
//		param->fore_color     = color & 15;
//		//param->back_color   = 使わない
////}}2008/01/01:変更
//		draw_scanline = draw_scanline_woz_color;
//	} else {
//		param->texture_w   = texture->header.w;
//		param->texture_h   = texture->header.h;
//		param->texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_woz_1bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_1bitmask;
//			}
////{{2008/01/01:変更
////			param->back_color = color      & 15;
////			param->fore_color = color >> 4 & 15;
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
//			param->fore_color = color      & 15;
//			param->back_color = color >> 4 & 15;
////}}2008/01/01:変更
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_woz_2bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_2bitmask;
//			}
//			//param->fore_color = 使わない
//			//param->back_color = 使わない
//			break;
//		}
//	}
//
//	return draw_scanline;
//}
//}}2008/01/13:削除->render_polygon()内に処理を含めました。

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa             */
/* - draw_scanline_woz_color()はframds21.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa            */
/* - draw_scanline_woz_1bit()はframds22.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa                */
/* - draw_scanline_woz_1bitmask()はframds23.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa            */
/* - draw_scanline_woz_2bit()はframds24.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa                */
/* - draw_scanline_woz_2bitmask()はframds25.cへ移動しました。 */

