1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21// r_draw.c
22
23#include "quakedef.h"
24#include "r_local.h"
25#include "d_local.h"	// FIXME: shouldn't need to include this
26
27#define MAXLEFTCLIPEDGES		100
28
29// !!! if these are changed, they must be changed in asm_draw.h too !!!
30#define FULLY_CLIPPED_CACHED	0x80000000
31#define FRAMECOUNT_MASK			0x7FFFFFFF
32
33unsigned int	cacheoffset;
34
35int			c_faceclip;					// number of faces clipped
36
37zpointdesc_t	r_zpointdesc;
38
39polydesc_t		r_polydesc;
40
41
42
43clipplane_t	*entity_clipplanes;
44clipplane_t	view_clipplanes[4];
45clipplane_t	world_clipplanes[16];
46
47medge_t			*r_pedge;
48
49qboolean		r_leftclipped, r_rightclipped;
50static qboolean	makeleftedge, makerightedge;
51qboolean		r_nearzionly;
52
53int		sintable[1280];
54int		intsintable[1280];
55
56mvertex_t	r_leftenter, r_leftexit;
57mvertex_t	r_rightenter, r_rightexit;
58
59typedef struct
60{
61	float	u,v;
62	int		ceilv;
63} evert_t;
64
65int				r_emitted;
66float			r_nearzi;
67float			r_u1, r_v1, r_lzi1;
68int				r_ceilv1;
69
70qboolean	r_lastvertvalid;
71
72
73#if	!id386
74
75/*
76================
77R_EmitEdge
78================
79*/
80void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1)
81{
82	edge_t	*edge, *pcheck;
83	int		u_check;
84	float	u, u_step;
85	vec3_t	local, transformed;
86	float	*world;
87	int		v, v2, ceilv0;
88	float	scale, lzi0, u0, v0;
89	int		side;
90
91	if (r_lastvertvalid)
92	{
93		u0 = r_u1;
94		v0 = r_v1;
95		lzi0 = r_lzi1;
96		ceilv0 = r_ceilv1;
97	}
98	else
99	{
100		world = &pv0->position[0];
101
102	// transform and project
103		VectorSubtract (world, modelorg, local);
104		TransformVector (local, transformed);
105
106		if (transformed[2] < NEAR_CLIP)
107			transformed[2] = NEAR_CLIP;
108
109		lzi0 = 1.0 / transformed[2];
110
111	// FIXME: build x/yscale into transform?
112		scale = xscale * lzi0;
113		u0 = (xcenter + scale*transformed[0]);
114		if (u0 < r_refdef.fvrectx_adj)
115			u0 = r_refdef.fvrectx_adj;
116		if (u0 > r_refdef.fvrectright_adj)
117			u0 = r_refdef.fvrectright_adj;
118
119		scale = yscale * lzi0;
120		v0 = (ycenter - scale*transformed[1]);
121		if (v0 < r_refdef.fvrecty_adj)
122			v0 = r_refdef.fvrecty_adj;
123		if (v0 > r_refdef.fvrectbottom_adj)
124			v0 = r_refdef.fvrectbottom_adj;
125
126		ceilv0 = (int) ceil(v0);
127	}
128
129	world = &pv1->position[0];
130
131// transform and project
132	VectorSubtract (world, modelorg, local);
133	TransformVector (local, transformed);
134
135	if (transformed[2] < NEAR_CLIP)
136		transformed[2] = NEAR_CLIP;
137
138	r_lzi1 = 1.0 / transformed[2];
139
140	scale = xscale * r_lzi1;
141	r_u1 = (xcenter + scale*transformed[0]);
142	if (r_u1 < r_refdef.fvrectx_adj)
143		r_u1 = r_refdef.fvrectx_adj;
144	if (r_u1 > r_refdef.fvrectright_adj)
145		r_u1 = r_refdef.fvrectright_adj;
146
147	scale = yscale * r_lzi1;
148	r_v1 = (ycenter - scale*transformed[1]);
149	if (r_v1 < r_refdef.fvrecty_adj)
150		r_v1 = r_refdef.fvrecty_adj;
151	if (r_v1 > r_refdef.fvrectbottom_adj)
152		r_v1 = r_refdef.fvrectbottom_adj;
153
154	if (r_lzi1 > lzi0)
155		lzi0 = r_lzi1;
156
157	if (lzi0 > r_nearzi)	// for mipmap finding
158		r_nearzi = lzi0;
159
160// for right edges, all we want is the effect on 1/z
161	if (r_nearzionly)
162		return;
163
164	r_emitted = 1;
165
166	r_ceilv1 = (int) ceil(r_v1);
167
168
169// create the edge
170	if (ceilv0 == r_ceilv1)
171	{
172	// we cache unclipped horizontal edges as fully clipped
173		if (cacheoffset != 0x7FFFFFFF)
174		{
175			cacheoffset = FULLY_CLIPPED_CACHED |
176					(r_framecount & FRAMECOUNT_MASK);
177		}
178
179		return;		// horizontal edge
180	}
181
182	side = ceilv0 > r_ceilv1;
183
184	edge = edge_p++;
185
186	edge->owner = r_pedge;
187
188	edge->nearzi = lzi0;
189
190	if (side == 0)
191	{
192	// trailing edge (go from p1 to p2)
193		v = ceilv0;
194		v2 = r_ceilv1 - 1;
195
196		edge->surfs[0] = surface_p - surfaces;
197		edge->surfs[1] = 0;
198
199		u_step = ((r_u1 - u0) / (r_v1 - v0));
200		u = u0 + ((float)v - v0) * u_step;
201	}
202	else
203	{
204	// leading edge (go from p2 to p1)
205		v2 = ceilv0 - 1;
206		v = r_ceilv1;
207
208		edge->surfs[0] = 0;
209		edge->surfs[1] = surface_p - surfaces;
210
211		u_step = ((u0 - r_u1) / (v0 - r_v1));
212		u = r_u1 + ((float)v - r_v1) * u_step;
213	}
214
215	edge->u_step = u_step*0x100000;
216	edge->u = u*0x100000 + 0xFFFFF;
217
218// we need to do this to avoid stepping off the edges if a very nearly
219// horizontal edge is less than epsilon above a scan, and numeric error causes
220// it to incorrectly extend to the scan, and the extension of the line goes off
221// the edge of the screen
222// FIXME: is this actually needed?
223	if (edge->u < r_refdef.vrect_x_adj_shift20)
224		edge->u = r_refdef.vrect_x_adj_shift20;
225	if (edge->u > r_refdef.vrectright_adj_shift20)
226		edge->u = r_refdef.vrectright_adj_shift20;
227
228//
229// sort the edge in normally
230//
231	u_check = edge->u;
232	if (edge->surfs[0])
233		u_check++;	// sort trailers after leaders
234
235	if (!newedges[v] || newedges[v]->u >= u_check)
236	{
237		edge->next = newedges[v];
238		newedges[v] = edge;
239	}
240	else
241	{
242		pcheck = newedges[v];
243		while (pcheck->next && pcheck->next->u < u_check)
244			pcheck = pcheck->next;
245		edge->next = pcheck->next;
246		pcheck->next = edge;
247	}
248
249	edge->nextremove = removeedges[v2];
250	removeedges[v2] = edge;
251}
252
253
254/*
255================
256R_ClipEdge
257================
258*/
259void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
260{
261	float		d0, d1, f;
262	mvertex_t	clipvert;
263
264	if (clip)
265	{
266		do
267		{
268			d0 = DotProduct (pv0->position, clip->normal) - clip->dist;
269			d1 = DotProduct (pv1->position, clip->normal) - clip->dist;
270
271			if (d0 >= 0)
272			{
273			// point 0 is unclipped
274				if (d1 >= 0)
275				{
276				// both points are unclipped
277					continue;
278				}
279
280			// only point 1 is clipped
281
282			// we don't cache clipped edges
283				cacheoffset = 0x7FFFFFFF;
284
285				f = d0 / (d0 - d1);
286				clipvert.position[0] = pv0->position[0] +
287						f * (pv1->position[0] - pv0->position[0]);
288				clipvert.position[1] = pv0->position[1] +
289						f * (pv1->position[1] - pv0->position[1]);
290				clipvert.position[2] = pv0->position[2] +
291						f * (pv1->position[2] - pv0->position[2]);
292
293				if (clip->leftedge)
294				{
295					r_leftclipped = true;
296					r_leftexit = clipvert;
297				}
298				else if (clip->rightedge)
299				{
300					r_rightclipped = true;
301					r_rightexit = clipvert;
302				}
303
304				R_ClipEdge (pv0, &clipvert, clip->next);
305				return;
306			}
307			else
308			{
309			// point 0 is clipped
310				if (d1 < 0)
311				{
312				// both points are clipped
313				// we do cache fully clipped edges
314					if (!r_leftclipped)
315						cacheoffset = FULLY_CLIPPED_CACHED |
316								(r_framecount & FRAMECOUNT_MASK);
317					return;
318				}
319
320			// only point 0 is clipped
321				r_lastvertvalid = false;
322
323			// we don't cache partially clipped edges
324				cacheoffset = 0x7FFFFFFF;
325
326				f = d0 / (d0 - d1);
327				clipvert.position[0] = pv0->position[0] +
328						f * (pv1->position[0] - pv0->position[0]);
329				clipvert.position[1] = pv0->position[1] +
330						f * (pv1->position[1] - pv0->position[1]);
331				clipvert.position[2] = pv0->position[2] +
332						f * (pv1->position[2] - pv0->position[2]);
333
334				if (clip->leftedge)
335				{
336					r_leftclipped = true;
337					r_leftenter = clipvert;
338				}
339				else if (clip->rightedge)
340				{
341					r_rightclipped = true;
342					r_rightenter = clipvert;
343				}
344
345				R_ClipEdge (&clipvert, pv1, clip->next);
346				return;
347			}
348		} while ((clip = clip->next) != NULL);
349	}
350
351// add the edge
352	R_EmitEdge (pv0, pv1);
353}
354
355#endif	// !id386
356
357
358/*
359================
360R_EmitCachedEdge
361================
362*/
363void R_EmitCachedEdge (void)
364{
365	edge_t		*pedge_t;
366
367	pedge_t = (edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset);
368
369	if (!pedge_t->surfs[0])
370		pedge_t->surfs[0] = surface_p - surfaces;
371	else
372		pedge_t->surfs[1] = surface_p - surfaces;
373
374	if (pedge_t->nearzi > r_nearzi)	// for mipmap finding
375		r_nearzi = pedge_t->nearzi;
376
377	r_emitted = 1;
378}
379
380
381/*
382================
383R_RenderFace
384================
385*/
386void R_RenderFace (msurface_t *fa, int clipflags)
387{
388	int			i, lindex;
389	unsigned	mask;
390	mplane_t	*pplane;
391	float		distinv;
392	vec3_t		p_normal;
393	medge_t		*pedges, tedge;
394	clipplane_t	*pclip;
395
396// skip out if no more surfs
397	if ((surface_p) >= surf_max)
398	{
399		r_outofsurfaces++;
400		return;
401	}
402
403// ditto if not enough edges left, or switch to auxedges if possible
404	if ((edge_p + fa->numedges + 4) >= edge_max)
405	{
406		r_outofedges += fa->numedges;
407		return;
408	}
409
410	c_faceclip++;
411
412// set up clip planes
413	pclip = NULL;
414
415	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
416	{
417		if (clipflags & mask)
418		{
419			view_clipplanes[i].next = pclip;
420			pclip = &view_clipplanes[i];
421		}
422	}
423
424// push the edges through
425	r_emitted = 0;
426	r_nearzi = 0;
427	r_nearzionly = false;
428	makeleftedge = makerightedge = false;
429	pedges = currententity->model->edges;
430	r_lastvertvalid = false;
431
432	for (i=0 ; i<fa->numedges ; i++)
433	{
434		lindex = currententity->model->surfedges[fa->firstedge + i];
435
436		if (lindex > 0)
437		{
438			r_pedge = &pedges[lindex];
439
440		// if the edge is cached, we can just reuse the edge
441			if (!insubmodel)
442			{
443				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
444				{
445					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
446						r_framecount)
447					{
448						r_lastvertvalid = false;
449						continue;
450					}
451				}
452				else
453				{
454					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
455						 r_pedge->cachededgeoffset) &&
456						(((edge_t *)((unsigned long)r_edges +
457						 r_pedge->cachededgeoffset))->owner == r_pedge))
458					{
459						R_EmitCachedEdge ();
460						r_lastvertvalid = false;
461						continue;
462					}
463				}
464			}
465
466		// assume it's cacheable
467			cacheoffset = (byte *)edge_p - (byte *)r_edges;
468			r_leftclipped = r_rightclipped = false;
469			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]],
470						&r_pcurrentvertbase[r_pedge->v[1]],
471						pclip);
472			r_pedge->cachededgeoffset = cacheoffset;
473
474			if (r_leftclipped)
475				makeleftedge = true;
476			if (r_rightclipped)
477				makerightedge = true;
478			r_lastvertvalid = true;
479		}
480		else
481		{
482			lindex = -lindex;
483			r_pedge = &pedges[lindex];
484		// if the edge is cached, we can just reuse the edge
485			if (!insubmodel)
486			{
487				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
488				{
489					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
490						r_framecount)
491					{
492						r_lastvertvalid = false;
493						continue;
494					}
495				}
496				else
497				{
498				// it's cached if the cached edge is valid and is owned
499				// by this medge_t
500					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
501						 r_pedge->cachededgeoffset) &&
502						(((edge_t *)((unsigned long)r_edges +
503						 r_pedge->cachededgeoffset))->owner == r_pedge))
504					{
505						R_EmitCachedEdge ();
506						r_lastvertvalid = false;
507						continue;
508					}
509				}
510			}
511
512		// assume it's cacheable
513			cacheoffset = (byte *)edge_p - (byte *)r_edges;
514			r_leftclipped = r_rightclipped = false;
515			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]],
516						&r_pcurrentvertbase[r_pedge->v[0]],
517						pclip);
518			r_pedge->cachededgeoffset = cacheoffset;
519
520			if (r_leftclipped)
521				makeleftedge = true;
522			if (r_rightclipped)
523				makerightedge = true;
524			r_lastvertvalid = true;
525		}
526	}
527
528// if there was a clip off the left edge, add that edge too
529// FIXME: faster to do in screen space?
530// FIXME: share clipped edges?
531	if (makeleftedge)
532	{
533		r_pedge = &tedge;
534		r_lastvertvalid = false;
535		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
536	}
537
538// if there was a clip off the right edge, get the right r_nearzi
539	if (makerightedge)
540	{
541		r_pedge = &tedge;
542		r_lastvertvalid = false;
543		r_nearzionly = true;
544		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
545	}
546
547// if no edges made it out, return without posting the surface
548	if (!r_emitted)
549		return;
550
551	r_polycount++;
552
553	surface_p->data = (void *)fa;
554	surface_p->nearzi = r_nearzi;
555	surface_p->flags = fa->flags;
556	surface_p->insubmodel = insubmodel;
557	surface_p->spanstate = 0;
558	surface_p->entity = currententity;
559	surface_p->key = r_currentkey++;
560	surface_p->spans = NULL;
561
562	pplane = fa->plane;
563// FIXME: cache this?
564	TransformVector (pplane->normal, p_normal);
565// FIXME: cache this?
566	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
567
568	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
569	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
570	surface_p->d_ziorigin = p_normal[2] * distinv -
571			xcenter * surface_p->d_zistepu -
572			ycenter * surface_p->d_zistepv;
573
574//JDC	VectorCopy (r_worldmodelorg, surface_p->modelorg);
575	surface_p++;
576}
577
578
579/*
580================
581R_RenderBmodelFace
582================
583*/
584void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf)
585{
586	int			i;
587	unsigned	mask;
588	mplane_t	*pplane;
589	float		distinv;
590	vec3_t		p_normal;
591	medge_t		tedge;
592	clipplane_t	*pclip;
593
594// skip out if no more surfs
595	if (surface_p >= surf_max)
596	{
597		r_outofsurfaces++;
598		return;
599	}
600
601// ditto if not enough edges left, or switch to auxedges if possible
602	if ((edge_p + psurf->numedges + 4) >= edge_max)
603	{
604		r_outofedges += psurf->numedges;
605		return;
606	}
607
608	c_faceclip++;
609
610// this is a dummy to give the caching mechanism someplace to write to
611	r_pedge = &tedge;
612
613// set up clip planes
614	pclip = NULL;
615
616	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
617	{
618		if (r_clipflags & mask)
619		{
620			view_clipplanes[i].next = pclip;
621			pclip = &view_clipplanes[i];
622		}
623	}
624
625// push the edges through
626	r_emitted = 0;
627	r_nearzi = 0;
628	r_nearzionly = false;
629	makeleftedge = makerightedge = false;
630// FIXME: keep clipped bmodel edges in clockwise order so last vertex caching
631// can be used?
632	r_lastvertvalid = false;
633
634	for ( ; pedges ; pedges = pedges->pnext)
635	{
636		r_leftclipped = r_rightclipped = false;
637		R_ClipEdge (pedges->v[0], pedges->v[1], pclip);
638
639		if (r_leftclipped)
640			makeleftedge = true;
641		if (r_rightclipped)
642			makerightedge = true;
643	}
644
645// if there was a clip off the left edge, add that edge too
646// FIXME: faster to do in screen space?
647// FIXME: share clipped edges?
648	if (makeleftedge)
649	{
650		r_pedge = &tedge;
651		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
652	}
653
654// if there was a clip off the right edge, get the right r_nearzi
655	if (makerightedge)
656	{
657		r_pedge = &tedge;
658		r_nearzionly = true;
659		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
660	}
661
662// if no edges made it out, return without posting the surface
663	if (!r_emitted)
664		return;
665
666	r_polycount++;
667
668	surface_p->data = (void *)psurf;
669	surface_p->nearzi = r_nearzi;
670	surface_p->flags = psurf->flags;
671	surface_p->insubmodel = true;
672	surface_p->spanstate = 0;
673	surface_p->entity = currententity;
674	surface_p->key = r_currentbkey;
675	surface_p->spans = NULL;
676
677	pplane = psurf->plane;
678// FIXME: cache this?
679	TransformVector (pplane->normal, p_normal);
680// FIXME: cache this?
681	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
682
683	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
684	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
685	surface_p->d_ziorigin = p_normal[2] * distinv -
686			xcenter * surface_p->d_zistepu -
687			ycenter * surface_p->d_zistepv;
688
689//JDC	VectorCopy (r_worldmodelorg, surface_p->modelorg);
690	surface_p++;
691}
692
693
694/*
695================
696R_RenderPoly
697================
698*/
699void R_RenderPoly (msurface_t *fa, int clipflags)
700{
701	int			i, lindex, lnumverts, s_axis, t_axis;
702	float		dist, lastdist, lzi, scale, u, v, frac;
703	unsigned	mask;
704	vec3_t		local, transformed;
705	clipplane_t	*pclip;
706	medge_t		*pedges;
707	mplane_t	*pplane;
708	mvertex_t	verts[2][100];	//FIXME: do real number
709	polyvert_t	pverts[100];	//FIXME: do real number, safely
710	int			vertpage, newverts, newpage, lastvert;
711	qboolean	visible;
712
713// FIXME: clean this up and make it faster
714// FIXME: guard against running out of vertices
715
716	s_axis = t_axis = 0;	// keep compiler happy
717
718// set up clip planes
719	pclip = NULL;
720
721	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
722	{
723		if (clipflags & mask)
724		{
725			view_clipplanes[i].next = pclip;
726			pclip = &view_clipplanes[i];
727		}
728	}
729
730// reconstruct the polygon
731// FIXME: these should be precalculated and loaded off disk
732	pedges = currententity->model->edges;
733	lnumverts = fa->numedges;
734	vertpage = 0;
735
736	for (i=0 ; i<lnumverts ; i++)
737	{
738		lindex = currententity->model->surfedges[fa->firstedge + i];
739
740		if (lindex > 0)
741		{
742			r_pedge = &pedges[lindex];
743			verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]];
744		}
745		else
746		{
747			r_pedge = &pedges[-lindex];
748			verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]];
749		}
750	}
751
752// clip the polygon, done if not visible
753	while (pclip)
754	{
755		lastvert = lnumverts - 1;
756		lastdist = DotProduct (verts[vertpage][lastvert].position,
757							   pclip->normal) - pclip->dist;
758
759		visible = false;
760		newverts = 0;
761		newpage = vertpage ^ 1;
762
763		for (i=0 ; i<lnumverts ; i++)
764		{
765			dist = DotProduct (verts[vertpage][i].position, pclip->normal) -
766					pclip->dist;
767
768			if ((lastdist > 0) != (dist > 0))
769			{
770				frac = dist / (dist - lastdist);
771				verts[newpage][newverts].position[0] =
772						verts[vertpage][i].position[0] +
773						((verts[vertpage][lastvert].position[0] -
774						  verts[vertpage][i].position[0]) * frac);
775				verts[newpage][newverts].position[1] =
776						verts[vertpage][i].position[1] +
777						((verts[vertpage][lastvert].position[1] -
778						  verts[vertpage][i].position[1]) * frac);
779				verts[newpage][newverts].position[2] =
780						verts[vertpage][i].position[2] +
781						((verts[vertpage][lastvert].position[2] -
782						  verts[vertpage][i].position[2]) * frac);
783				newverts++;
784			}
785
786			if (dist >= 0)
787			{
788				verts[newpage][newverts] = verts[vertpage][i];
789				newverts++;
790				visible = true;
791			}
792
793			lastvert = i;
794			lastdist = dist;
795		}
796
797		if (!visible || (newverts < 3))
798			return;
799
800		lnumverts = newverts;
801		vertpage ^= 1;
802		pclip = pclip->next;
803	}
804
805// transform and project, remembering the z values at the vertices and
806// r_nearzi, and extract the s and t coordinates at the vertices
807	pplane = fa->plane;
808	switch (pplane->type)
809	{
810	case PLANE_X:
811	case PLANE_ANYX:
812		s_axis = 1;
813		t_axis = 2;
814		break;
815	case PLANE_Y:
816	case PLANE_ANYY:
817		s_axis = 0;
818		t_axis = 2;
819		break;
820	case PLANE_Z:
821	case PLANE_ANYZ:
822		s_axis = 0;
823		t_axis = 1;
824		break;
825	}
826
827	r_nearzi = 0;
828
829	for (i=0 ; i<lnumverts ; i++)
830	{
831	// transform and project
832		VectorSubtract (verts[vertpage][i].position, modelorg, local);
833		TransformVector (local, transformed);
834
835		if (transformed[2] < NEAR_CLIP)
836			transformed[2] = NEAR_CLIP;
837
838		lzi = 1.0 / transformed[2];
839
840		if (lzi > r_nearzi)	// for mipmap finding
841			r_nearzi = lzi;
842
843	// FIXME: build x/yscale into transform?
844		scale = xscale * lzi;
845		u = (xcenter + scale*transformed[0]);
846		if (u < r_refdef.fvrectx_adj)
847			u = r_refdef.fvrectx_adj;
848		if (u > r_refdef.fvrectright_adj)
849			u = r_refdef.fvrectright_adj;
850
851		scale = yscale * lzi;
852		v = (ycenter - scale*transformed[1]);
853		if (v < r_refdef.fvrecty_adj)
854			v = r_refdef.fvrecty_adj;
855		if (v > r_refdef.fvrectbottom_adj)
856			v = r_refdef.fvrectbottom_adj;
857
858		pverts[i].u = u;
859		pverts[i].v = v;
860		pverts[i].zi = lzi;
861		pverts[i].s = verts[vertpage][i].position[s_axis];
862		pverts[i].t = verts[vertpage][i].position[t_axis];
863	}
864
865// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z
866// for each vertex
867	r_polydesc.numverts = lnumverts;
868	r_polydesc.nearzi = r_nearzi;
869	r_polydesc.pcurrentface = fa;
870	r_polydesc.pverts = pverts;
871
872// draw the polygon
873	D_DrawPoly ();
874}
875
876
877/*
878================
879R_ZDrawSubmodelPolys
880================
881*/
882void R_ZDrawSubmodelPolys (model_t *pmodel)
883{
884	int			i, numsurfaces;
885	msurface_t	*psurf;
886	float		dot;
887	mplane_t	*pplane;
888
889	psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
890	numsurfaces = pmodel->nummodelsurfaces;
891
892	for (i=0 ; i<numsurfaces ; i++, psurf++)
893	{
894	// find which side of the node we are on
895		pplane = psurf->plane;
896
897		dot = DotProduct (modelorg, pplane->normal) - pplane->dist;
898
899	// draw the polygon
900		if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
901			(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
902		{
903		// FIXME: use bounding-box-based frustum clipping info?
904			R_RenderPoly (psurf, 15);
905		}
906	}
907}
908
909