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