1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 
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.
8 
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.
12 
13 See the GNU General Public License for more details.
14 
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.
18 
19 */
20 // r_main.c
21 
22 #include <stdint.h>
23 
24 #include "cmd.h"
25 #include "console.h"
26 #include "quakedef.h"
27 #include "r_local.h"
28 #include "screen.h"
29 #include "sound.h"
30 #include "sys.h"
31 #include "view.h"
32 
33 void *colormap;
34 float r_time1;
35 int r_numallocatededges;
36 
37 qboolean r_recursiveaffinetriangles = true;
38 
39 int r_pixbytes = 1;
40 float r_aliasuvscale = 1.0;
41 int r_outofsurfaces;
42 int r_outofedges;
43 
44 static vec3_t viewlightvec;
45 static alight_t r_viewlighting = { 128, 192, viewlightvec };
46 
47 qboolean r_dowarp, r_dowarpold, r_viewchanged;
48 
49 mvertex_t *r_pcurrentvertbase;
50 
51 int c_surf;
52 int r_maxsurfsseen, r_maxedgesseen;
53 
54 static int r_cnumsurfs;
55 static qboolean r_surfsonstack;
56 
57 byte *r_warpbuffer;
58 
59 static byte *r_stack_start;
60 
61 entity_t r_worldentity;
62 
63 //
64 // view origin
65 //
66 vec3_t vup, base_vup;
67 vec3_t vpn, base_vpn;
68 vec3_t vright, base_vright;
69 vec3_t r_origin;
70 
71 //
72 // screen size info
73 //
74 refdef_t r_refdef;
75 float xcenter, ycenter;
76 float xscale, yscale;
77 float xscaleinv, yscaleinv;
78 float xscaleshrink, yscaleshrink;
79 float aliasxscale, aliasyscale, aliasxcenter, aliasycenter;
80 
81 int screenwidth;
82 
83 float pixelAspect;
84 static float screenAspect;
85 static float verticalFieldOfView;
86 static float xOrigin, yOrigin;
87 
88 mplane_t screenedge[4];
89 
90 //
91 // refresh flags
92 //
93 int r_framecount = 1;		// so frame counts initialized to 0 don't match
94 int r_visframecount;
95 int r_polycount;
96 int r_drawnpolycount;
97 
98 mleaf_t *r_viewleaf, *r_oldviewleaf;
99 
100 texture_t *r_notexture_mip;
101 
102 float r_aliastransition, r_resfudge;
103 
104 int d_lightstylevalue[256];	// 8.8 fraction of base light value
105 
106 cvar_t r_draworder = { "r_draworder", "0" };
107 cvar_t r_speeds = { "r_speeds", "0" };
108 cvar_t r_graphheight = { "r_graphheight", "15" };
109 cvar_t r_clearcolor = { "r_clearcolor", "2" };
110 cvar_t r_waterwarp = { "r_waterwarp", "1" };
111 cvar_t r_drawentities = { "r_drawentities", "1" };
112 cvar_t r_drawviewmodel = { "r_drawviewmodel", "1" };
113 cvar_t r_ambient = { "r_ambient", "0" };
114 cvar_t r_numsurfs = { "r_numsurfs", "0" };
115 cvar_t r_numedges = { "r_numedges", "0" };
116 
117 cvar_t r_lockpvs = { "r_lockpvs", "0" };
118 cvar_t r_lockfrustum = { "r_lockfrustum", "0" };
119 
120 cvar_t r_fullbright = { "r_fullbright", "0" };
121 
122 #ifdef QW_HACK
123 cvar_t r_netgraph = { "r_netgraph", "0" };
124 static cvar_t r_zgraph = { "r_zgraph", "0" };
125 #endif
126 
127 static cvar_t r_timegraph = { "r_timegraph", "0" };
128 static cvar_t r_aliasstats = { "r_polymodelstats", "0" };
129 static cvar_t r_dspeeds = { "r_dspeeds", "0" };
130 static cvar_t r_reportsurfout = { "r_reportsurfout", "0" };
131 static cvar_t r_maxsurfs = { "r_maxsurfs", "0" };
132 static cvar_t r_reportedgeout = { "r_reportedgeout", "0" };
133 static cvar_t r_maxedges = { "r_maxedges", "0" };
134 static cvar_t r_aliastransbase = { "r_aliastransbase", "200" };
135 static cvar_t r_aliastransadj = { "r_aliastransadj", "100" };
136 
137 /*
138 ==================
139 R_InitTextures
140 ==================
141 */
142 void
R_InitTextures(void)143 R_InitTextures(void)
144 {
145     int x, y, m;
146     byte *dest;
147 
148 // create a simple checkerboard texture for the default
149     r_notexture_mip = (texture_t*)Hunk_AllocName(sizeof(texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2,
150 		       "notexture");
151 
152     r_notexture_mip->width = r_notexture_mip->height = 16;
153     r_notexture_mip->offsets[0] = sizeof(texture_t);
154     r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16;
155     r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8;
156     r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4;
157 
158     for (m = 0; m < 4; m++) {
159 	dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
160 	for (y = 0; y < (16 >> m); y++) {
161 	    for (x = 0; x < (16 >> m); x++) {
162 		if ((y < (8 >> m)) ^ (x < (8 >> m)))
163 		    *dest++ = 0;
164 		else
165 		    *dest++ = 0xff;
166 	    }
167 	}
168     }
169 }
170 
171 
172 /*
173 ================
174 R_InitTurb
175 ================
176 */
177 static void
R_InitTurb(void)178 R_InitTurb(void)
179 {
180     int i;
181 
182     for (i = 0; i < TURB_TABLE_SIZE; ++i) {
183 	sintable[i] = TURB_SURF_AMP
184 	    + sin(i * 3.14159 * 2 / TURB_CYCLE) * TURB_SURF_AMP;
185 	intsintable[i] = TURB_SCREEN_AMP
186 	    + sin(i * 3.14159 * 2 / TURB_CYCLE) * TURB_SCREEN_AMP;
187     }
188 }
189 
190 
191 /*
192 ===============
193 R_Init
194 ===============
195 */
196 void
R_Init(void)197 R_Init(void)
198 {
199     int dummy;
200 
201     // get stack position so we can guess if we are going to overflow
202     r_stack_start = (byte *)&dummy;
203 
204     R_InitTurb();
205 
206     Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
207     Cmd_AddCommand("pointfile", R_ReadPointFile_f);
208 
209     Cvar_RegisterVariable(&r_draworder);
210     Cvar_RegisterVariable(&r_speeds);
211     Cvar_RegisterVariable(&r_graphheight);
212     Cvar_RegisterVariable(&r_clearcolor);
213     Cvar_RegisterVariable(&r_waterwarp);
214     Cvar_RegisterVariable(&r_drawentities);
215     Cvar_RegisterVariable(&r_drawviewmodel);
216     Cvar_RegisterVariable(&r_ambient);
217     Cvar_RegisterVariable(&r_numsurfs);
218     Cvar_RegisterVariable(&r_numedges);
219 #ifdef NQ_HACK
220     Cvar_RegisterVariable(&r_lerpmodels);
221     Cvar_RegisterVariable(&r_lerpmove);
222 #endif
223     Cvar_RegisterVariable(&r_lockpvs);
224     Cvar_RegisterVariable(&r_lockfrustum);
225 
226     Cvar_RegisterVariable(&r_fullbright);
227 
228     Cvar_RegisterVariable(&r_timegraph);
229     Cvar_RegisterVariable(&r_aliasstats);
230     Cvar_RegisterVariable(&r_dspeeds);
231     Cvar_RegisterVariable(&r_reportsurfout);
232     Cvar_RegisterVariable(&r_maxsurfs);
233     Cvar_RegisterVariable(&r_reportedgeout);
234     Cvar_RegisterVariable(&r_maxedges);
235     Cvar_RegisterVariable(&r_aliastransbase);
236     Cvar_RegisterVariable(&r_aliastransadj);
237 
238 #ifdef QW_HACK
239     Cvar_RegisterVariable(&r_netgraph);
240     Cvar_RegisterVariable(&r_zgraph);
241 #endif
242 
243     Cvar_SetValue("r_maxedges", (float)NUMSTACKEDGES);
244     Cvar_SetValue("r_maxsurfs", (float)NUMSTACKSURFACES);
245 
246     view_clipplanes[0].leftedge = true;
247     view_clipplanes[1].rightedge = true;
248     view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =
249 	view_clipplanes[3].leftedge = false;
250     view_clipplanes[0].rightedge = view_clipplanes[2].rightedge =
251 	view_clipplanes[3].rightedge = false;
252 
253     r_refdef.xOrigin = XCENTERING;
254     r_refdef.yOrigin = YCENTERING;
255 
256     R_InitParticles();
257 
258     D_Init();
259 }
260 
261 extern void V_NewMap (void);
262 
263 /*
264 ===============
265 R_NewMap
266 ===============
267 */
268 void
R_NewMap(void)269 R_NewMap(void)
270 {
271     int i;
272 
273     memset(&r_worldentity, 0, sizeof(r_worldentity));
274     r_worldentity.model = cl.worldmodel;
275 
276 // clear out efrags in case the level hasn't been reloaded
277 // FIXME: is this one short?
278     for (i = 0; i < cl.worldmodel->numleafs; i++)
279 	cl.worldmodel->leafs[i].efrags = NULL;
280 
281     r_viewleaf = NULL;
282     R_ClearParticles();
283 
284     r_cnumsurfs = qclamp((int)r_maxsurfs.value, MINSURFACES, MAXSURFACES);
285     if (r_cnumsurfs > NUMSTACKSURFACES) {
286 	surfaces = (surf_t*)Hunk_AllocName(r_cnumsurfs * sizeof(surf_t), "surfaces");
287 	surface_p = surfaces;
288 	surf_max = &surfaces[r_cnumsurfs];
289 	r_surfsonstack = false;
290 	// surface 0 doesn't really exist; it's just a dummy because index 0
291 	// is used to indicate no edge attached to surface
292 	surfaces--;
293     } else {
294 	r_surfsonstack = true;
295     }
296 
297     r_maxedgesseen = 0;
298     r_maxsurfsseen = 0;
299 
300     r_numallocatededges = qclamp((int)r_maxedges.value, MINEDGES, MAXEDGES);
301     if (r_numallocatededges <= NUMSTACKEDGES) {
302 	auxedges = NULL;
303     } else {
304 	auxedges = (edge_t*)Hunk_AllocName(r_numallocatededges * sizeof(edge_t),
305 				  "edges");
306     }
307 
308     r_dowarpold = false;
309     r_viewchanged = false;
310 
311     V_NewMap();
312 }
313 
314 
315 /*
316 ===============
317 R_SetVrect
318 ===============
319 */
320 void
R_SetVrect(const vrect_t * pvrectin,vrect_t * pvrect,int lineadj)321 R_SetVrect(const vrect_t *pvrectin, vrect_t *pvrect, int lineadj)
322 {
323     int h;
324     float size;
325     qboolean full;
326 
327 #ifdef NQ_HACK
328     full = (scr_viewsize.value >= 120.0f);
329 #endif
330 #ifdef QW_HACK
331     full = (!cl_sbar.value && scr_viewsize.value >= 100.0f);
332 #endif
333     size = qmin(scr_viewsize.value, 100.0f);
334 
335     /* Hide the status bar during intermission */
336     if (cl.intermission) {
337 	full = true;
338 	size = 100.0;
339 	lineadj = 0;
340     }
341     size /= 100.0;
342 
343     if (full)
344 	h = pvrectin->height;
345     else
346 	h = pvrectin->height - lineadj;
347 
348     pvrect->width = pvrectin->width * size;
349     if (pvrect->width < 96) {
350 	size = 96.0 / pvrectin->width;
351 	pvrect->width = 96;	// min for icons
352     }
353     pvrect->width &= ~7;
354 
355     pvrect->height = pvrectin->height * size;
356     if (!full) {
357 	if (pvrect->height > pvrectin->height - lineadj)
358 	    pvrect->height = pvrectin->height - lineadj;
359     } else if (pvrect->height > pvrectin->height)
360 	pvrect->height = pvrectin->height;
361     pvrect->height &= ~1;
362 
363     pvrect->x = (pvrectin->width - pvrect->width) / 2;
364     if (full)
365 	pvrect->y = 0;
366     else
367 	pvrect->y = (h - pvrect->height) / 2;
368 }
369 
370 
371 /*
372 ===============
373 R_ViewChanged
374 
375 Called every time the vid structure or r_refdef changes.
376 Guaranteed to be called before the first refresh
377 ===============
378 */
379 void
R_ViewChanged(vrect_t * pvrect,int lineadj,float aspect)380 R_ViewChanged(vrect_t *pvrect, int lineadj, float aspect)
381 {
382     int i;
383     float res_scale;
384 
385     r_viewchanged = true;
386 
387     R_SetVrect(pvrect, &r_refdef.vrect, lineadj);
388 
389     r_refdef.horizontalFieldOfView = 2.0 * tan(r_refdef.fov_x / 360 * M_PI);
390     r_refdef.fvrectx = (float)r_refdef.vrect.x;
391     r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5;
392     r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x << 20) + (1 << 19) - 1;
393     r_refdef.fvrecty = (float)r_refdef.vrect.y;
394     r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5;
395     r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width;
396     r_refdef.vrectright_adj_shift20 =
397 	(r_refdef.vrectright << 20) + (1 << 19) - 1;
398     r_refdef.fvrectright = (float)r_refdef.vrectright;
399     r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5;
400     r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99;
401     r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height;
402     r_refdef.fvrectbottom = (float)r_refdef.vrectbottom;
403     r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5;
404 
405     r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale);
406     r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale);
407     r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale);
408     r_refdef.aliasvrect.height =
409 	(int)(r_refdef.vrect.height * r_aliasuvscale);
410     r_refdef.aliasvrectright =
411 	r_refdef.aliasvrect.x + r_refdef.aliasvrect.width;
412     r_refdef.aliasvrectbottom =
413 	r_refdef.aliasvrect.y + r_refdef.aliasvrect.height;
414 
415     pixelAspect = aspect;
416     xOrigin = r_refdef.xOrigin;
417     yOrigin = r_refdef.yOrigin;
418 
419     screenAspect = r_refdef.vrect.width * pixelAspect / r_refdef.vrect.height;
420 // 320*200 1.0 pixelAspect = 1.6 screenAspect
421 // 320*240 1.0 pixelAspect = 1.3333 screenAspect
422 // proper 320*200 pixelAspect = 0.8333333
423 
424     verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect;
425 
426 // values for perspective projection
427 // if math were exact, the values would range from 0.5 to to range+0.5
428 // hopefully they wll be in the 0.000001 to range+.999999 and truncate
429 // the polygon rasterization will never render in the first row or column
430 // but will definately render in the [range] row and column, so adjust the
431 // buffer origin to get an exact edge to edge fill
432     xcenter = ((float)r_refdef.vrect.width * XCENTERING) +
433 	r_refdef.vrect.x - 0.5;
434     aliasxcenter = xcenter * r_aliasuvscale;
435     ycenter = ((float)r_refdef.vrect.height * YCENTERING) +
436 	r_refdef.vrect.y - 0.5;
437     aliasycenter = ycenter * r_aliasuvscale;
438 
439     xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView;
440     aliasxscale = xscale * r_aliasuvscale;
441     xscaleinv = 1.0 / xscale;
442     yscale = xscale * pixelAspect;
443     aliasyscale = yscale * r_aliasuvscale;
444     yscaleinv = 1.0 / yscale;
445     xscaleshrink =
446 	(r_refdef.vrect.width - 6) / r_refdef.horizontalFieldOfView;
447     yscaleshrink = xscaleshrink * pixelAspect;
448 
449 // left side clip
450     screenedge[0].normal[0] =
451 	-1.0 / (xOrigin * r_refdef.horizontalFieldOfView);
452     screenedge[0].normal[1] = 0;
453     screenedge[0].normal[2] = 1;
454     screenedge[0].type = PLANE_ANYZ;
455 
456 // right side clip
457     screenedge[1].normal[0] =
458 	1.0 / ((1.0 - xOrigin) * r_refdef.horizontalFieldOfView);
459     screenedge[1].normal[1] = 0;
460     screenedge[1].normal[2] = 1;
461     screenedge[1].type = PLANE_ANYZ;
462 
463 // top side clip
464     screenedge[2].normal[0] = 0;
465     screenedge[2].normal[1] = -1.0 / (yOrigin * verticalFieldOfView);
466     screenedge[2].normal[2] = 1;
467     screenedge[2].type = PLANE_ANYZ;
468 
469 // bottom side clip
470     screenedge[3].normal[0] = 0;
471     screenedge[3].normal[1] = 1.0 / ((1.0 - yOrigin) * verticalFieldOfView);
472     screenedge[3].normal[2] = 1;
473     screenedge[3].type = PLANE_ANYZ;
474 
475     for (i = 0; i < 4; i++)
476 	VectorNormalize(screenedge[i].normal);
477 
478     res_scale =
479 	sqrt((double)(r_refdef.vrect.width * r_refdef.vrect.height) /
480 	     (320.0 * 152.0)) * (2.0 / r_refdef.horizontalFieldOfView);
481     r_aliastransition = r_aliastransbase.value * res_scale;
482     r_resfudge = r_aliastransadj.value * res_scale;
483 
484     D_ViewChanged();
485 }
486 
487 
488 /*
489 ===============
490 R_MarkSurfaces
491 ===============
492 */
493 static void
R_MarkSurfaces(void)494 R_MarkSurfaces(void)
495 {
496     const leafbits_t *pvs;
497     leafblock_t check;
498     int leafnum, i;
499     mleaf_t *leaf;
500     mnode_t *node;
501     msurface_t **mark;
502     qboolean pvs_changed;
503 
504     /*
505      * If the PVS hasn't changed, no need to update bsp visframes,
506      * just store the efrags.
507      */
508     pvs_changed = (r_viewleaf != r_oldviewleaf && !r_lockpvs.value);
509     if (pvs_changed) {
510 	r_visframecount++;
511 	r_oldviewleaf = r_viewleaf;
512     }
513 
514     pvs = Mod_LeafPVS(cl.worldmodel, r_viewleaf);
515     foreach_leafbit(pvs, leafnum, check) {
516 	leaf = &cl.worldmodel->leafs[leafnum + 1];
517 	if (leaf->efrags)
518 	    R_StoreEfrags(&leaf->efrags);
519 	if (!pvs_changed)
520 	    continue;
521 
522 	/* Mark the surfaces */
523 	mark = leaf->firstmarksurface;
524 	for (i = 0; i < leaf->nummarksurfaces; i++) {
525 	    (*mark)->visframe = r_visframecount;
526 	    mark++;
527 	}
528 
529 	/* Mark the leaf and all parent nodes */
530 	node = (mnode_t *)leaf;
531 	do {
532 	    if (node->visframe == r_visframecount)
533 		break;
534 	    node->visframe = r_visframecount;
535 	    node = node->parent;
536 	} while (node);
537     }
538 }
539 
540 /*
541 =============
542 R_CullSurfaces
543 =============
544 */
545 static void
R_CullSurfaces(model_t * model,vec3_t vieworg)546 R_CullSurfaces(model_t *model, vec3_t vieworg)
547 {
548     int i, j;
549     int side;
550     mnode_t *node;
551     msurface_t *surf;
552     mplane_t *plane;
553     vec_t dist;
554 
555     node = model->nodes;
556     node->clipflags = 15;
557 
558     for (;;) {
559 	if (node->visframe != r_visframecount)
560 	    goto NodeUp;
561 
562 	if (node->clipflags) {
563 	    /* Clip the node against the frustum */
564 	    for (i = 0; i < 4; i++) {
565 		if (!(node->clipflags & (1 << i)))
566 		    continue;
567 		plane = &view_clipplanes[i].plane;
568 		side = BoxOnPlaneSide(node->mins, node->maxs, plane);
569 		if (side == PSIDE_BACK) {
570 		    node->clipflags = BMODEL_FULLY_CLIPPED;
571 		    goto NodeUp;
572 		}
573 		if (side == PSIDE_FRONT)
574 		    node->clipflags &= ~(1 << i);
575 	    }
576 	}
577 
578 	if (node->contents < 0)
579 	    goto NodeUp;
580 
581 	surf = model->surfaces + node->firstsurface;
582 	for (i = 0; i < node->numsurfaces; i++, surf++) {
583 	    /* Clip the surfaces against the frustum */
584 	    surf->clipflags = node->clipflags;
585 	    for (j = 0; j < 4; j++) {
586 		if (!(node->clipflags & (1 << j)))
587 		    continue;
588 		plane = &view_clipplanes[j].plane;
589 		side = BoxOnPlaneSide(surf->mins, surf->maxs, plane);
590 		if (side == PSIDE_BACK) {
591 		    surf->clipflags = BMODEL_FULLY_CLIPPED;
592 		    break;
593 		}
594 		if (side == PSIDE_FRONT)
595 		    surf->clipflags &= ~(1 << j);
596 	    }
597 	    if (j < 4)
598 		continue;
599 
600 	    /* Cull backward facing surfs */
601 	    if (surf->plane->type < 3) {
602 		dist = vieworg[surf->plane->type] - surf->plane->dist;
603 	    } else {
604 		dist = DotProduct(vieworg, surf->plane->normal);
605 		dist -= surf->plane->dist;
606 	    }
607 	    if (surf->flags & SURF_PLANEBACK) {
608 		if (dist > -BACKFACE_EPSILON)
609 		    surf->clipflags = BMODEL_FULLY_CLIPPED;
610 	    } else {
611 		if (dist < BACKFACE_EPSILON)
612 		    surf->clipflags = BMODEL_FULLY_CLIPPED;
613 	    }
614 	}
615 
616 //  DownLeft:
617 	/* Don't descend into solid leafs because parent links are broken */
618 	if (node->children[0]->contents == CONTENTS_SOLID)
619 	    goto DownRight;
620 	node->children[0]->clipflags = node->clipflags;
621 	node = node->children[0];
622 	continue;
623 
624     DownRight:
625 	/* Don't descent into solid leafs because parent links are broken */
626 	if (node->children[1]->contents == CONTENTS_SOLID)
627 	    goto NodeUp;
628 	node->children[1]->clipflags = node->clipflags;
629 	node = node->children[1];
630 	continue;
631 
632     NodeUp:
633 	/* If we just processed the left node, go right */
634 	if (node->parent && node == node->parent->children[0]) {
635 	    node = node->parent;
636 	    goto DownRight;
637 	}
638 
639 	/* If we were on a right branch, backtrack */
640 	while (node->parent && node == node->parent->children[1])
641 	    node = node->parent;
642 
643 	/* If we still have a parent, we need to cross to the right */
644 	if (node->parent) {
645 	    node = node->parent;
646 	    goto DownRight;
647 	}
648 
649 	/* If no more parents, we are done */
650 	break;
651     }
652 }
653 
654 /*
655 =============
656 R_CullSubmodelSurfaces
657 =============
658 */
659 static void
R_CullSubmodelSurfaces(const model_t * submodel,const vec3_t vieworg,int clipflags)660 R_CullSubmodelSurfaces(const model_t *submodel, const vec3_t vieworg,
661 		       int clipflags)
662 {
663     int i, j, side;
664     msurface_t *surf;
665     mplane_t *plane;
666     vec_t dist;
667 
668     surf = submodel->surfaces + submodel->firstmodelsurface;
669     for (i = 0; i < submodel->nummodelsurfaces; i++, surf++) {
670 	/* Clip the surface against the frustum */
671 	surf->clipflags = clipflags;
672 	for (j = 0; j < 4; j++) {
673 	    if (!(surf->clipflags & (1 << j)))
674 		continue;
675 	    plane = &view_clipplanes[j].plane;
676 	    side = BoxOnPlaneSide(surf->mins, surf->maxs, plane);
677 	    if (side == PSIDE_BACK) {
678 		surf->clipflags = BMODEL_FULLY_CLIPPED;
679 		break;
680 	    }
681 	    if (side == PSIDE_FRONT)
682 		surf->clipflags &= ~(1 << j);
683 	}
684 	if (j < 4)
685 	    continue;
686 
687 	/* Cull backward facing surfs */
688 	if (surf->plane->type < 3) {
689 	    dist = vieworg[surf->plane->type] - surf->plane->dist;
690 	} else {
691 	    dist = DotProduct(vieworg, surf->plane->normal);
692 	    dist -= surf->plane->dist;
693 	}
694 	if (surf->flags & SURF_PLANEBACK) {
695 	    if (dist > -BACKFACE_EPSILON)
696 		surf->clipflags = BMODEL_FULLY_CLIPPED;
697 	} else {
698 	    if (dist < BACKFACE_EPSILON)
699 		surf->clipflags = BMODEL_FULLY_CLIPPED;
700 	}
701     }
702 }
703 
704 /*
705 =============
706 R_DrawEntitiesOnList
707 =============
708 */
709 static void
R_DrawEntitiesOnList(void)710 R_DrawEntitiesOnList(void)
711 {
712     entity_t *e;
713     int i, j;
714     int lnum;
715     alight_t lighting;
716 
717 // FIXME: remove and do real lighting
718     float lightvec[3] = { -1, 0, 0 };
719     vec3_t dist;
720     float add;
721 
722     if (!r_drawentities.value)
723 	return;
724 
725     for (i = 0; i < cl_numvisedicts; i++) {
726 	e = &cl_visedicts[i];
727 #ifdef NQ_HACK
728 	if (e == &cl_entities[cl.viewentity])
729 	    continue;		// don't draw the player
730 #endif
731 	switch (e->model->type) {
732 	case mod_sprite:
733 	    VectorCopy(e->origin, r_entorigin);
734 	    VectorSubtract(r_origin, r_entorigin, modelorg);
735 	    R_DrawSprite(e);
736 	    break;
737 
738 	case mod_alias:
739 #ifdef NQ_HACK
740 	    if (r_lerpmove.value) {
741 		float delta = e->currentorigintime - e->previousorigintime;
742 		float frac = qclamp((cl.time - e->currentorigintime) / delta, 0.0, 1.0);
743 		vec3_t lerpvec;
744 
745 		/* FIXME - hack to skip the viewent (weapon) */
746 		if (e == &cl.viewent)
747 		    goto nolerp;
748 
749 		VectorSubtract(e->currentorigin, e->previousorigin, lerpvec);
750 		VectorMA(e->previousorigin, frac, lerpvec, r_entorigin);
751 	    } else
752 	    nolerp:
753 #endif
754 	    {
755 		VectorCopy(e->origin, r_entorigin);
756 	    }
757 	    VectorSubtract(r_origin, r_entorigin, modelorg);
758 
759 	    // see if the bounding box lets us trivially reject, also sets
760 	    // trivial accept status
761 	    if (R_AliasCheckBBox(e)) {
762 		j = R_LightPoint(e->origin);
763 
764 		lighting.ambientlight = j;
765 		lighting.shadelight = j;
766 
767 		lighting.plightvec = lightvec;
768 
769 		for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) {
770 		    if (cl_dlights[lnum].die >= cl.time) {
771 			VectorSubtract(e->origin, cl_dlights[lnum].origin,
772 				       dist);
773 			add = cl_dlights[lnum].radius - Length(dist);
774 
775 			if (add > 0)
776 			    lighting.ambientlight += add;
777 		    }
778 		}
779 
780 		// clamp lighting so it doesn't overbright as much
781 		if (lighting.ambientlight > 128)
782 		    lighting.ambientlight = 128;
783 		if (lighting.ambientlight + lighting.shadelight > 192)
784 		    lighting.shadelight = 192 - lighting.ambientlight;
785 
786 		R_AliasDrawModel(e, &lighting);
787 	    }
788 	    break;
789 
790 	default:
791 	    break;
792 	}
793     }
794 }
795 
796 /*
797 =============
798 R_DrawViewModel
799 =============
800 */
801 static void
R_DrawViewModel(void)802 R_DrawViewModel(void)
803 {
804     entity_t *e;
805 // FIXME: remove and do real lighting
806     float lightvec[3] = { -1, 0, 0 };
807     int j;
808     int lnum;
809     vec3_t dist;
810     float add;
811     dlight_t *dl;
812 
813 #ifdef NQ_HACK
814     if (!r_drawviewmodel.value || chase_active.value)
815 	return;
816 #endif
817 #ifdef QW_HACK
818     if (!r_drawviewmodel.value || !Cam_DrawViewModel())
819 	return;
820 #endif
821 
822     if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
823 	return;
824 
825     if (cl.stats[STAT_HEALTH] <= 0)
826 	return;
827 
828     e = &cl.viewent;
829     if (!e->model)
830 	return;
831 
832     VectorCopy(e->origin, r_entorigin);
833     VectorSubtract(r_origin, r_entorigin, modelorg);
834 
835     VectorCopy(vup, viewlightvec);
836     VectorInverse(viewlightvec);
837 
838     j = R_LightPoint(e->origin);
839 
840     if (j < 24)
841 	j = 24;			// allways give some light on gun
842     r_viewlighting.ambientlight = j;
843     r_viewlighting.shadelight = j;
844 
845 // add dynamic lights
846     for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) {
847 	dl = &cl_dlights[lnum];
848 	if (!dl->radius)
849 	    continue;
850 	if (!dl->radius)
851 	    continue;
852 	if (dl->die < cl.time)
853 	    continue;
854 
855 	VectorSubtract(e->origin, dl->origin, dist);
856 	add = dl->radius - Length(dist);
857 	if (add > 0)
858 	    r_viewlighting.ambientlight += add;
859     }
860 
861 // clamp lighting so it doesn't overbright as much
862     if (r_viewlighting.ambientlight > 128)
863 	r_viewlighting.ambientlight = 128;
864     if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192)
865 	r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight;
866 
867     r_viewlighting.plightvec = lightvec;
868 
869     R_AliasDrawModel(e, &r_viewlighting);
870 }
871 
872 
873 /*
874 =============
875 R_BmodelCheckBBox
876 =============
877 */
878 static int
R_BmodelCheckBBox(const entity_t * e,model_t * clmodel,const vec3_t mins,const vec3_t maxs)879 R_BmodelCheckBBox(const entity_t *e, model_t *clmodel,
880 		  const vec3_t mins, const vec3_t maxs)
881 {
882     int i, side, clipflags;
883     vec_t d;
884 
885     clipflags = 0;
886 
887     if (e->angles[0] || e->angles[1] || e->angles[2]) {
888 	for (i = 0; i < 4; i++) {
889 	    d = DotProduct(e->origin, view_clipplanes[i].plane.normal);
890 	    d -= view_clipplanes[i].plane.dist;
891 
892 	    if (d <= -clmodel->radius)
893 		return BMODEL_FULLY_CLIPPED;
894 
895 	    if (d <= clmodel->radius)
896 		clipflags |= (1 << i);
897 	}
898     } else {
899 	for (i = 0; i < 4; i++) {
900 	    side = BoxOnPlaneSide(mins, maxs, &view_clipplanes[i].plane);
901 	    if (side == PSIDE_BACK)
902 		return BMODEL_FULLY_CLIPPED;
903 	    if (side == PSIDE_BOTH)
904 		clipflags |= (1 << i);
905 	}
906     }
907 
908     return clipflags;
909 }
910 
911 
912 /*
913 =============
914 R_DrawBEntitiesOnList
915 =============
916 */
R_DrawBEntitiesOnList(void)917 static void R_DrawBEntitiesOnList(void)
918 {
919     entity_t *e;
920     int i, clipflags;
921     vec3_t oldorigin;
922     model_t *model;
923     vec3_t mins, maxs;
924 
925     if (!r_drawentities.value)
926 	return;
927 
928     VectorCopy(modelorg, oldorigin);
929     insubmodel = true;
930 
931     for (i = 0; i < cl_numvisedicts; i++) {
932 	e = &cl_visedicts[i];
933 	if (e->model->type != mod_brush)
934 	    continue;
935 
936 	model = e->model;
937 
938 	// see if the bounding box lets us trivially reject, also sets
939 	// trivial accept status
940 	VectorAdd(e->origin, model->mins, mins);
941 	VectorAdd(e->origin, model->maxs, maxs);
942 	clipflags = R_BmodelCheckBBox(e, model, mins, maxs);
943 
944 	if (clipflags == BMODEL_FULLY_CLIPPED)
945 	    continue;
946 
947 	VectorCopy(e->origin, r_entorigin);
948 	VectorSubtract(r_origin, r_entorigin, modelorg);
949 	r_pcurrentvertbase = model->vertexes;
950 
951 	// FIXME: stop transforming twice
952 	R_RotateBmodel(e);
953 
954 	// calculate dynamic lighting for bmodel if it's not an
955 	// instanced model
956 	if (model->firstmodelsurface != 0)
957        R_PushDlights (model->nodes + model->hulls[0].firstclipnode);  /*qbism - from MH */
958 
959 	r_pefragtopnode = NULL;
960 	VectorCopy(mins, r_emins);
961 	VectorCopy(maxs, r_emaxs);
962 	R_SplitEntityOnNode2(cl.worldmodel->nodes);
963 	R_CullSubmodelSurfaces(model, modelorg, clipflags);
964 
965 	if (r_pefragtopnode) {
966 	    e->topnode = r_pefragtopnode;
967 
968 	    if (r_pefragtopnode->contents >= 0) {
969 		// not a leaf; has to be clipped to the world BSP
970 		R_DrawSolidClippedSubmodelPolygons(e, model);
971 	    } else {
972 		// falls entirely in one leaf, so we just put all
973 		// the edges in the edge list and let 1/z sorting
974 		// handle drawing order
975 		R_DrawSubmodelPolygons(e, model, clipflags);
976 	    }
977 	    e->topnode = NULL;
978 	}
979 
980 	// put back world rotation and frustum clipping
981 	// FIXME: R_RotateBmodel should just work off base_vxx
982 	VectorCopy(base_vpn, vpn);
983 	VectorCopy(base_vup, vup);
984 	VectorCopy(base_vright, vright);
985 	VectorCopy(base_modelorg, modelorg);
986 	VectorCopy(oldorigin, modelorg);
987 	R_TransformFrustum();
988     }
989 
990     insubmodel = false;
991 }
992 
993 
994 /*
995 ================
996 R_EdgeDrawing
997 ================
998 */
R_EdgeDrawing(void)999 static void R_EdgeDrawing(void)
1000 {
1001    edge_t * ledges = malloc(sizeof(edge_t)*CACHE_PAD_ARRAY(NUMSTACKEDGES, edge_t));
1002    surf_t * lsurfs = malloc(sizeof(surf_t)*CACHE_PAD_ARRAY(NUMSTACKSURFACES, surf_t));
1003 
1004    if (auxedges) {
1005       r_edges = auxedges;
1006    } else {
1007       r_edges =  (edge_t *)
1008 				(((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
1009    }
1010 
1011    if (r_surfsonstack) {
1012       surfaces =  (surf_t *)
1013 				(((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
1014       surf_max = &surfaces[r_cnumsurfs];
1015       // surface 0 doesn't really exist; it's just a dummy because index 0
1016       // is used to indicate no edge attached to surface
1017       surfaces--;
1018    }
1019 
1020    R_BeginEdgeFrame();
1021 
1022    R_RenderWorld();
1023 
1024    // only the world can be drawn back to front with no z reads or compares,
1025    // just z writes, so have the driver turn z compares on now
1026    D_TurnZOn();
1027 
1028    R_DrawBEntitiesOnList();
1029 
1030    R_ScanEdges();
1031 
1032    free(lsurfs);
1033    free(ledges);
1034 
1035 }
1036 
1037 
1038 /*
1039 ================
1040 R_RenderView
1041 
1042 r_refdef must be set before the first call
1043 ================
1044 */
1045 static void
R_RenderView_(void)1046 R_RenderView_(void)
1047 {
1048     byte warpbuffer[WARP_WIDTH * WARP_HEIGHT];
1049 
1050     r_warpbuffer = warpbuffer;
1051 
1052     R_SetupFrame();
1053     R_PushDlights (cl.worldmodel->nodes);  /* qbism - moved here from view.c */
1054     R_MarkSurfaces();		// done here so we know if we're in water
1055     R_CullSurfaces(r_worldentity.model, r_refdef.vieworg);
1056 
1057     // make FDIV fast. This reduces timing precision after we've been running
1058     // for a while, so we don't do it globally.  This also sets chop mode, and
1059     // we do it here so that setup stuff like the refresh area calculations
1060     // match what's done in screen.c
1061     Sys_LowFPPrecision();
1062 
1063     if (!r_worldentity.model || !cl.worldmodel)
1064 	Sys_Error("%s: NULL worldmodel", __func__);
1065 
1066     R_EdgeDrawing();
1067 
1068     R_DrawEntitiesOnList();
1069 
1070     R_DrawViewModel();
1071 
1072     R_DrawParticles();
1073 
1074     if (r_dowarp)
1075 	D_WarpScreen();
1076 
1077     V_SetContentsColor(r_viewleaf->contents);
1078 
1079     if (r_aliasstats.value)
1080 	R_PrintAliasStats();
1081 
1082     if (r_reportsurfout.value && r_outofsurfaces)
1083 	Con_Printf("Short %d surfaces\n", r_outofsurfaces);
1084 
1085     if (r_reportedgeout.value && r_outofedges)
1086 	Con_Printf("Short roughly %d edges\n", r_outofedges * 2 / 3);
1087 
1088     // back to high floating-point precision
1089     Sys_HighFPPrecision();
1090 }
1091 
1092 void
R_RenderView(void)1093 R_RenderView(void)
1094 {
1095     int dummy;
1096 
1097     if (Hunk_LowMark() & 3)
1098 	Sys_Error("Hunk is missaligned");
1099 
1100     if ((intptr_t)(&dummy) & 3)
1101 	Sys_Error("Stack is missaligned");
1102 
1103     if ((intptr_t)(&r_warpbuffer) & 3)
1104 	Sys_Error("Globals are missaligned");
1105 
1106     R_RenderView_();
1107 }
1108