2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include "d_local.h" // FIXME: shouldn't need to include this
27 #define MAXLEFTCLIPEDGES 100
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
33 unsigned int cacheoffset
;
35 int c_faceclip
; // number of faces clipped
37 zpointdesc_t r_zpointdesc
;
39 polydesc_t r_polydesc
;
43 clipplane_t
*entity_clipplanes
;
44 clipplane_t view_clipplanes
[4];
45 clipplane_t world_clipplanes
[16];
49 qboolean r_leftclipped
, r_rightclipped
;
50 static qboolean makeleftedge
, makerightedge
;
51 qboolean r_nearzionly
;
53 int sintable
[SIN_BUFFER_SIZE
];
54 int intsintable
[SIN_BUFFER_SIZE
];
56 mvertex_t r_leftenter
, r_leftexit
;
57 mvertex_t r_rightenter
, r_rightexit
;
67 float r_u1
, r_v1
, r_lzi1
;
70 qboolean r_lastvertvalid
;
80 void R_EmitEdge (mvertex_t
*pv0
, mvertex_t
*pv1
)
82 edge_t
*edge
, *pcheck
;
85 vec3_t local
, transformed
;
88 float scale
, lzi0
, u0
, v0
;
100 world
= &pv0
->position
[0];
102 // transform and project
103 VectorSubtract (world
, modelorg
, local
);
104 TransformVector (local
, transformed
);
106 if (transformed
[2] < NEAR_CLIP
)
107 transformed
[2] = NEAR_CLIP
;
109 lzi0
= 1.0 / transformed
[2];
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
;
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
;
126 ceilv0
= (int) ceilf(v0
);
129 world
= &pv1
->position
[0];
131 // transform and project
132 VectorSubtract (world
, modelorg
, local
);
133 TransformVector (local
, transformed
);
135 if (transformed
[2] < NEAR_CLIP
)
136 transformed
[2] = NEAR_CLIP
;
138 r_lzi1
= 1.0 / transformed
[2];
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
;
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
;
157 if (lzi0
> r_nearzi
) // for mipmap finding
160 // for right edges, all we want is the effect on 1/z
166 r_ceilv1
= (int) ceilf(r_v1
);
170 if (ceilv0
== r_ceilv1
)
172 // we cache unclipped horizontal edges as fully clipped
173 if (cacheoffset
!= 0x7FFFFFFF)
175 cacheoffset
= FULLY_CLIPPED_CACHED
|
176 (r_framecount
& FRAMECOUNT_MASK
);
179 return; // horizontal edge
182 side
= ceilv0
> r_ceilv1
;
186 edge
->owner
= r_pedge
;
192 // trailing edge (go from p1 to p2)
196 edge
->surfs
[0] = surface_p
- surfaces
;
199 u_step
= ((r_u1
- u0
) / (r_v1
- v0
));
200 u
= u0
+ ((float)v
- v0
) * u_step
;
204 // leading edge (go from p2 to p1)
209 edge
->surfs
[1] = surface_p
- surfaces
;
211 u_step
= ((u0
- r_u1
) / (v0
- r_v1
));
212 u
= r_u1
+ ((float)v
- r_v1
) * u_step
;
215 edge
->u_step
= u_step
*0x100000;
216 edge
->u
= u
*0x100000 + 0xFFFFF;
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
;
229 // sort the edge in normally
233 u_check
++; // sort trailers after leaders
235 if (!newedges
[v
] || newedges
[v
]->u
>= u_check
)
237 edge
->next
= newedges
[v
];
242 pcheck
= newedges
[v
];
243 while (pcheck
->next
&& pcheck
->next
->u
< u_check
)
244 pcheck
= pcheck
->next
;
245 edge
->next
= pcheck
->next
;
249 edge
->nextremove
= removeedges
[v2
];
250 removeedges
[v2
] = edge
;
259 void R_ClipEdge (mvertex_t
*pv0
, mvertex_t
*pv1
, clipplane_t
*clip
)
268 d0
= DotProduct (pv0
->position
, clip
->normal
) - clip
->dist
;
269 d1
= DotProduct (pv1
->position
, clip
->normal
) - clip
->dist
;
273 // point 0 is unclipped
276 // both points are unclipped
280 // only point 1 is clipped
282 // we don't cache clipped edges
283 cacheoffset
= 0x7FFFFFFF;
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]);
295 r_leftclipped
= true;
296 r_leftexit
= clipvert
;
298 else if (clip
->rightedge
)
300 r_rightclipped
= true;
301 r_rightexit
= clipvert
;
304 R_ClipEdge (pv0
, &clipvert
, clip
->next
);
309 // point 0 is clipped
312 // both points are clipped
313 // we do cache fully clipped edges
315 cacheoffset
= FULLY_CLIPPED_CACHED
|
316 (r_framecount
& FRAMECOUNT_MASK
);
320 // only point 0 is clipped
321 r_lastvertvalid
= false;
323 // we don't cache partially clipped edges
324 cacheoffset
= 0x7FFFFFFF;
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]);
336 r_leftclipped
= true;
337 r_leftenter
= clipvert
;
339 else if (clip
->rightedge
)
341 r_rightclipped
= true;
342 r_rightenter
= clipvert
;
345 R_ClipEdge (&clipvert
, pv1
, clip
->next
);
348 } while ((clip
= clip
->next
) != NULL
);
352 R_EmitEdge (pv0
, pv1
);
363 void R_EmitCachedEdge (void)
367 pedge_t
= (edge_t
*)((unsigned long)r_edges
+ r_pedge
->cachededgeoffset
);
369 if (!pedge_t
->surfs
[0])
370 pedge_t
->surfs
[0] = surface_p
- surfaces
;
372 pedge_t
->surfs
[1] = surface_p
- surfaces
;
374 if (pedge_t
->nearzi
> r_nearzi
) // for mipmap finding
375 r_nearzi
= pedge_t
->nearzi
;
386 void R_RenderFace (msurface_t
*fa
, int clipflags
)
393 medge_t
*pedges
, tedge
;
396 // skip out if no more surfs
397 if ((surface_p
) >= surf_max
)
403 // ditto if not enough edges left, or switch to auxedges if possible
404 if ((edge_p
+ fa
->numedges
+ 4) >= edge_max
)
406 r_outofedges
+= fa
->numedges
;
412 // set up clip planes
415 for (i
=3, mask
= 0x08 ; i
>=0 ; i
--, mask
>>= 1)
417 if (clipflags
& mask
)
419 view_clipplanes
[i
].next
= pclip
;
420 pclip
= &view_clipplanes
[i
];
424 // push the edges through
427 r_nearzionly
= false;
428 makeleftedge
= makerightedge
= false;
429 pedges
= currententity
->model
->edges
;
430 r_lastvertvalid
= false;
432 for (i
=0 ; i
<fa
->numedges
; i
++)
434 lindex
= currententity
->model
->surfedges
[fa
->firstedge
+ i
];
438 r_pedge
= &pedges
[lindex
];
440 // if the edge is cached, we can just reuse the edge
443 if (r_pedge
->cachededgeoffset
& FULLY_CLIPPED_CACHED
)
445 if ((r_pedge
->cachededgeoffset
& FRAMECOUNT_MASK
) ==
448 r_lastvertvalid
= false;
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
))
460 r_lastvertvalid
= false;
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]],
472 r_pedge
->cachededgeoffset
= cacheoffset
;
477 makerightedge
= true;
478 r_lastvertvalid
= true;
483 r_pedge
= &pedges
[lindex
];
484 // if the edge is cached, we can just reuse the edge
487 if (r_pedge
->cachededgeoffset
& FULLY_CLIPPED_CACHED
)
489 if ((r_pedge
->cachededgeoffset
& FRAMECOUNT_MASK
) ==
492 r_lastvertvalid
= false;
498 // it's cached if the cached edge is valid and is owned
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
))
506 r_lastvertvalid
= false;
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]],
518 r_pedge
->cachededgeoffset
= cacheoffset
;
523 makerightedge
= true;
524 r_lastvertvalid
= true;
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?
534 r_lastvertvalid
= false;
535 R_ClipEdge (&r_leftexit
, &r_leftenter
, pclip
->next
);
538 // if there was a clip off the right edge, get the right r_nearzi
542 r_lastvertvalid
= false;
544 R_ClipEdge (&r_rightexit
, &r_rightenter
, view_clipplanes
[1].next
);
547 // if no edges made it out, return without posting the surface
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
;
563 // FIXME: cache this?
564 TransformVector (pplane
->normal
, p_normal
);
565 // FIXME: cache this?
566 distinv
= 1.0 / (pplane
->dist
- DotProduct (modelorg
, pplane
->normal
));
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
;
574 //JDC VectorCopy (r_worldmodelorg, surface_p->modelorg);
584 void R_RenderBmodelFace (bedge_t
*pedges
, msurface_t
*psurf
)
594 // skip out if no more surfs
595 if (surface_p
>= surf_max
)
601 // ditto if not enough edges left, or switch to auxedges if possible
602 if ((edge_p
+ psurf
->numedges
+ 4) >= edge_max
)
604 r_outofedges
+= psurf
->numedges
;
610 // this is a dummy to give the caching mechanism someplace to write to
613 // set up clip planes
616 for (i
=3, mask
= 0x08 ; i
>=0 ; i
--, mask
>>= 1)
618 if (r_clipflags
& mask
)
620 view_clipplanes
[i
].next
= pclip
;
621 pclip
= &view_clipplanes
[i
];
625 // push the edges through
628 r_nearzionly
= false;
629 makeleftedge
= makerightedge
= false;
630 // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching
632 r_lastvertvalid
= false;
634 for ( ; pedges
; pedges
= pedges
->pnext
)
636 r_leftclipped
= r_rightclipped
= false;
637 R_ClipEdge (pedges
->v
[0], pedges
->v
[1], pclip
);
642 makerightedge
= true;
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?
651 R_ClipEdge (&r_leftexit
, &r_leftenter
, pclip
->next
);
654 // if there was a clip off the right edge, get the right r_nearzi
659 R_ClipEdge (&r_rightexit
, &r_rightenter
, view_clipplanes
[1].next
);
662 // if no edges made it out, return without posting the surface
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
;
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
));
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
;
689 //JDC VectorCopy (r_worldmodelorg, surface_p->modelorg);
699 void R_RenderPoly (msurface_t
*fa
, int clipflags
)
701 int i
, lindex
, lnumverts
, s_axis
, t_axis
;
702 float dist
, lastdist
, lzi
, scale
, u
, v
, frac
;
704 vec3_t local
, transformed
;
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
;
713 // FIXME: clean this up and make it faster
714 // FIXME: guard against running out of vertices
716 s_axis
= t_axis
= 0; // keep compiler happy
718 // set up clip planes
721 for (i
=3, mask
= 0x08 ; i
>=0 ; i
--, mask
>>= 1)
723 if (clipflags
& mask
)
725 view_clipplanes
[i
].next
= pclip
;
726 pclip
= &view_clipplanes
[i
];
730 // reconstruct the polygon
731 // FIXME: these should be precalculated and loaded off disk
732 pedges
= currententity
->model
->edges
;
733 lnumverts
= fa
->numedges
;
736 for (i
=0 ; i
<lnumverts
; i
++)
738 lindex
= currententity
->model
->surfedges
[fa
->firstedge
+ i
];
742 r_pedge
= &pedges
[lindex
];
743 verts
[0][i
] = r_pcurrentvertbase
[r_pedge
->v
[0]];
747 r_pedge
= &pedges
[-lindex
];
748 verts
[0][i
] = r_pcurrentvertbase
[r_pedge
->v
[1]];
752 // clip the polygon, done if not visible
755 lastvert
= lnumverts
- 1;
756 lastdist
= DotProduct (verts
[vertpage
][lastvert
].position
,
757 pclip
->normal
) - pclip
->dist
;
761 newpage
= vertpage
^ 1;
763 for (i
=0 ; i
<lnumverts
; i
++)
765 dist
= DotProduct (verts
[vertpage
][i
].position
, pclip
->normal
) -
768 if ((lastdist
> 0) != (dist
> 0))
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
);
788 verts
[newpage
][newverts
] = verts
[vertpage
][i
];
797 if (!visible
|| (newverts
< 3))
800 lnumverts
= newverts
;
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
808 switch (pplane
->type
)
829 for (i
=0 ; i
<lnumverts
; i
++)
831 // transform and project
832 VectorSubtract (verts
[vertpage
][i
].position
, modelorg
, local
);
833 TransformVector (local
, transformed
);
835 if (transformed
[2] < NEAR_CLIP
)
836 transformed
[2] = NEAR_CLIP
;
838 lzi
= 1.0 / transformed
[2];
840 if (lzi
> r_nearzi
) // for mipmap finding
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
;
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
;
861 pverts
[i
].s
= verts
[vertpage
][i
].position
[s_axis
];
862 pverts
[i
].t
= verts
[vertpage
][i
].position
[t_axis
];
865 // build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z
867 r_polydesc
.numverts
= lnumverts
;
868 r_polydesc
.nearzi
= r_nearzi
;
869 r_polydesc
.pcurrentface
= fa
;
870 r_polydesc
.pverts
= pverts
;
882 void R_ZDrawSubmodelPolys (model_t
*pmodel
)
889 psurf
= &pmodel
->surfaces
[pmodel
->firstmodelsurface
];
890 numsurfaces
= pmodel
->nummodelsurfaces
;
892 for (i
=0 ; i
<numsurfaces
; i
++, psurf
++)
894 // find which side of the node we are on
895 pplane
= psurf
->plane
;
897 dot
= DotProduct (modelorg
, pplane
->normal
) - pplane
->dist
;
900 if (((psurf
->flags
& SURF_PLANEBACK
) && (dot
< -BACKFACE_EPSILON
)) ||
901 (!(psurf
->flags
& SURF_PLANEBACK
) && (dot
> BACKFACE_EPSILON
)))
903 // FIXME: use bounding-box-based frustum clipping info?
904 R_RenderPoly (psurf
, 15);