1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16
17
18 #include <math.h>
19 #include "m_random.h"
20 #include "h2def.h"
21 #include "m_bbox.h"
22 #include "r_local.h"
23
24 int viewangleoffset;
25
26 // haleyjd: removed WATCOMC
27
28 int validcount = 1; // increment every time a check is made
29
30 lighttable_t *fixedcolormap;
31 extern lighttable_t **walllights;
32
33 int centerx, centery;
34 fixed_t centerxfrac, centeryfrac;
35 fixed_t projection;
36
37 int framecount; // just for profiling purposes
38
39 int sscount, linecount, loopcount;
40
41 fixed_t viewx, viewy, viewz;
42 angle_t viewangle;
43 fixed_t viewcos, viewsin;
44 player_t *viewplayer;
45
46 int detailshift; // 0 = high, 1 = low
47
48 //
49 // precalculated math tables
50 //
51 angle_t clipangle;
52
53 // The viewangletox[viewangle + FINEANGLES/4] lookup maps the visible view
54 // angles to screen X coordinates, flattening the arc to a flat projection
55 // plane. There will be many angles mapped to the same X.
56 int viewangletox[FINEANGLES / 2];
57
58 // The xtoviewangleangle[] table maps a screen pixel to the lowest viewangle
59 // that maps back to x ranges from clipangle to -clipangle
60 angle_t xtoviewangle[SCREENWIDTH + 1];
61
62 lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE];
63 lighttable_t *scalelightfixed[MAXLIGHTSCALE];
64 lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ];
65
66 int extralight; // bumped light from gun blasts
67
68 void (*colfunc) (void);
69 void (*basecolfunc) (void);
70 void (*tlcolfunc) (void);
71 void (*transcolfunc) (void);
72 void (*spanfunc) (void);
73
74 /*
75 ===================
76 =
77 = R_AddPointToBox
78 =
79 ===================
80 */
81
82 /*
83 void R_AddPointToBox (int x, int y, fixed_t *box)
84 {
85 if (x< box[BOXLEFT])
86 box[BOXLEFT] = x;
87 if (x> box[BOXRIGHT])
88 box[BOXRIGHT] = x;
89 if (y< box[BOXBOTTOM])
90 box[BOXBOTTOM] = y;
91 if (y> box[BOXTOP])
92 box[BOXTOP] = y;
93 }
94 */
95
96
97 /*
98 ===============================================================================
99 =
100 = R_PointOnSide
101 =
102 = Returns side 0 (front) or 1 (back)
103 ===============================================================================
104 */
105
R_PointOnSide(fixed_t x,fixed_t y,node_t * node)106 int R_PointOnSide(fixed_t x, fixed_t y, node_t * node)
107 {
108 fixed_t dx, dy;
109 fixed_t left, right;
110
111 if (!node->dx)
112 {
113 if (x <= node->x)
114 return node->dy > 0;
115 return node->dy < 0;
116 }
117 if (!node->dy)
118 {
119 if (y <= node->y)
120 return node->dx < 0;
121 return node->dx > 0;
122 }
123
124 dx = (x - node->x);
125 dy = (y - node->y);
126
127 // try to quickly decide by looking at sign bits
128 if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000)
129 {
130 if ((node->dy ^ dx) & 0x80000000)
131 return 1; // (left is negative)
132 return 0;
133 }
134
135 left = FixedMul(node->dy >> FRACBITS, dx);
136 right = FixedMul(dy, node->dx >> FRACBITS);
137
138 if (right < left)
139 return 0; // front side
140 return 1; // back side
141 }
142
143
R_PointOnSegSide(fixed_t x,fixed_t y,seg_t * line)144 int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line)
145 {
146 fixed_t lx, ly;
147 fixed_t ldx, ldy;
148 fixed_t dx, dy;
149 fixed_t left, right;
150
151 lx = line->v1->x;
152 ly = line->v1->y;
153
154 ldx = line->v2->x - lx;
155 ldy = line->v2->y - ly;
156
157 if (!ldx)
158 {
159 if (x <= lx)
160 return ldy > 0;
161 return ldy < 0;
162 }
163 if (!ldy)
164 {
165 if (y <= ly)
166 return ldx < 0;
167 return ldx > 0;
168 }
169
170 dx = (x - lx);
171 dy = (y - ly);
172
173 // try to quickly decide by looking at sign bits
174 if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000)
175 {
176 if ((ldy ^ dx) & 0x80000000)
177 return 1; // (left is negative)
178 return 0;
179 }
180
181 left = FixedMul(ldy >> FRACBITS, dx);
182 right = FixedMul(dy, ldx >> FRACBITS);
183
184 if (right < left)
185 return 0; // front side
186 return 1; // back side
187 }
188
189
190 /*
191 ===============================================================================
192 =
193 = R_PointToAngle
194 =
195 ===============================================================================
196 */
197
198 #define DBITS (FRACBITS-SLOPEBITS)
199
R_PointToAngle(fixed_t x,fixed_t y)200 angle_t R_PointToAngle(fixed_t x, fixed_t y)
201 {
202 x -= viewx;
203 y -= viewy;
204 if ((!x) && (!y))
205 return 0;
206 if (x >= 0)
207 { // x >=0
208 if (y >= 0)
209 { // y>= 0
210 if (x > y)
211 return tantoangle[SlopeDiv(y, x)]; // octant 0
212 else
213 return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 1
214 }
215 else
216 { // y<0
217 y = -y;
218 if (x > y)
219 return -tantoangle[SlopeDiv(y, x)]; // octant 8
220 else
221 return ANG270 + tantoangle[SlopeDiv(x, y)]; // octant 7
222 }
223 }
224 else
225 { // x<0
226 x = -x;
227 if (y >= 0)
228 { // y>= 0
229 if (x > y)
230 return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; // octant 3
231 else
232 return ANG90 + tantoangle[SlopeDiv(x, y)]; // octant 2
233 }
234 else
235 { // y<0
236 y = -y;
237 if (x > y)
238 return ANG180 + tantoangle[SlopeDiv(y, x)]; // octant 4
239 else
240 return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 5
241 }
242 }
243
244 return 0;
245 }
246
247
R_PointToAngle2(fixed_t x1,fixed_t y1,fixed_t x2,fixed_t y2)248 angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
249 {
250 viewx = x1;
251 viewy = y1;
252 return R_PointToAngle(x2, y2);
253 }
254
255
R_PointToDist(fixed_t x,fixed_t y)256 fixed_t R_PointToDist(fixed_t x, fixed_t y)
257 {
258 int angle;
259 fixed_t dx, dy, temp;
260 fixed_t dist;
261
262 dx = abs(x - viewx);
263 dy = abs(y - viewy);
264
265 if (dy > dx)
266 {
267 temp = dx;
268 dx = dy;
269 dy = temp;
270 }
271
272 angle =
273 (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT;
274
275 dist = FixedDiv(dx, finesine[angle]); // use as cosine
276
277 return dist;
278 }
279
280
281
282 /*
283 =================
284 =
285 = R_InitPointToAngle
286 =
287 =================
288 */
289
R_InitPointToAngle(void)290 void R_InitPointToAngle(void)
291 {
292 // now getting from tables.c
293 #if 0
294 int i;
295 long t;
296 float f;
297 //
298 // slope (tangent) to angle lookup
299 //
300 for (i = 0; i <= SLOPERANGE; i++)
301 {
302 f = atan((float) i / SLOPERANGE) / (3.141592657 * 2);
303 t = 0xffffffff * f;
304 tantoangle[i] = t;
305 }
306 #endif
307 }
308
309 //=============================================================================
310
311 /*
312 ================
313 =
314 = R_ScaleFromGlobalAngle
315 =
316 = Returns the texture mapping scale for the current line at the given angle
317 = rw_distance must be calculated first
318 ================
319 */
320
R_ScaleFromGlobalAngle(angle_t visangle)321 fixed_t R_ScaleFromGlobalAngle(angle_t visangle)
322 {
323 fixed_t scale;
324 int anglea, angleb;
325 int sinea, sineb;
326 fixed_t num, den;
327
328 #if 0
329 {
330 fixed_t dist, z;
331 fixed_t sinv, cosv;
332
333 sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT];
334 dist = FixedDiv(rw_distance, sinv);
335 cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT];
336 z = abs(FixedMul(dist, cosv));
337 scale = FixedDiv(projection, z);
338 return scale;
339 }
340 #endif
341
342 anglea = ANG90 + (visangle - viewangle);
343 angleb = ANG90 + (visangle - rw_normalangle);
344 // bothe sines are allways positive
345 sinea = finesine[anglea >> ANGLETOFINESHIFT];
346 sineb = finesine[angleb >> ANGLETOFINESHIFT];
347 num = FixedMul(projection, sineb) << detailshift;
348 den = FixedMul(rw_distance, sinea);
349 if (den > num >> 16)
350 {
351 scale = FixedDiv(num, den);
352 if (scale > 64 * FRACUNIT)
353 scale = 64 * FRACUNIT;
354 else if (scale < 256)
355 scale = 256;
356 }
357 else
358 scale = 64 * FRACUNIT;
359
360 return scale;
361 }
362
363
364
365 /*
366 =================
367 =
368 = R_InitTables
369 =
370 =================
371 */
372
R_InitTables(void)373 void R_InitTables(void)
374 {
375 // now getting from tables.c
376 #if 0
377 int i;
378 float a, fv;
379 int t;
380
381 //
382 // viewangle tangent table
383 //
384 for (i = 0; i < FINEANGLES / 2; i++)
385 {
386 a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES;
387 fv = FRACUNIT * tan(a);
388 t = fv;
389 finetangent[i] = t;
390 }
391
392 //
393 // finesine table
394 //
395 for (i = 0; i < 5 * FINEANGLES / 4; i++)
396 {
397 // OPTIMIZE: mirror...
398 a = (i + 0.5) * PI * 2 / FINEANGLES;
399 t = FRACUNIT * sin(a);
400 finesine[i] = t;
401 }
402 #endif
403
404 }
405
406
407 /*
408 =================
409 =
410 = R_InitTextureMapping
411 =
412 =================
413 */
414
R_InitTextureMapping(void)415 void R_InitTextureMapping(void)
416 {
417 int i;
418 int x;
419 int t;
420 fixed_t focallength;
421
422
423 //
424 // use tangent table to generate viewangletox
425 // viewangletox will give the next greatest x after the view angle
426 //
427 // calc focallength so FIELDOFVIEW angles covers SCREENWIDTH
428 focallength =
429 FixedDiv(centerxfrac, finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]);
430
431 for (i = 0; i < FINEANGLES / 2; i++)
432 {
433 if (finetangent[i] > FRACUNIT * 2)
434 t = -1;
435 else if (finetangent[i] < -FRACUNIT * 2)
436 t = viewwidth + 1;
437 else
438 {
439 t = FixedMul(finetangent[i], focallength);
440 t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS;
441 if (t < -1)
442 t = -1;
443 else if (t > viewwidth + 1)
444 t = viewwidth + 1;
445 }
446 viewangletox[i] = t;
447 }
448
449 //
450 // scan viewangletox[] to generate xtoviewangleangle[]
451 //
452 // xtoviewangle will give the smallest view angle that maps to x
453 for (x = 0; x <= viewwidth; x++)
454 {
455 i = 0;
456 while (viewangletox[i] > x)
457 i++;
458 xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90;
459 }
460
461 //
462 // take out the fencepost cases from viewangletox
463 //
464 for (i = 0; i < FINEANGLES / 2; i++)
465 {
466 t = FixedMul(finetangent[i], focallength);
467 t = centerx - t;
468 if (viewangletox[i] == -1)
469 viewangletox[i] = 0;
470 else if (viewangletox[i] == viewwidth + 1)
471 viewangletox[i] = viewwidth;
472 }
473
474 clipangle = xtoviewangle[0];
475 }
476
477 //=============================================================================
478
479 /*
480 ====================
481 =
482 = R_InitLightTables
483 =
484 = Only inits the zlight table, because the scalelight table changes
485 = with view size
486 =
487 ====================
488 */
489
490 #define DISTMAP 2
491
R_InitLightTables(void)492 void R_InitLightTables(void)
493 {
494 int i, j, level, startmap;
495 int scale;
496
497 //
498 // Calculate the light levels to use for each level / distance combination
499 //
500 for (i = 0; i < LIGHTLEVELS; i++)
501 {
502 startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
503 for (j = 0; j < MAXLIGHTZ; j++)
504 {
505 scale =
506 FixedDiv((SCREENWIDTH / 2 * FRACUNIT),
507 (j + 1) << LIGHTZSHIFT);
508 scale >>= LIGHTSCALESHIFT;
509 level = startmap - scale / DISTMAP;
510 if (level < 0)
511 level = 0;
512 if (level >= NUMCOLORMAPS)
513 level = NUMCOLORMAPS - 1;
514 zlight[i][j] = colormaps + level * 256;
515 }
516 }
517 }
518
519
520 /*
521 ==============
522 =
523 = R_SetViewSize
524 =
525 = Don't really change anything here, because i might be in the middle of
526 = a refresh. The change will take effect next refresh.
527 =
528 ==============
529 */
530
531 boolean setsizeneeded;
532 int setblocks, setdetail;
533
R_SetViewSize(int blocks,int detail)534 void R_SetViewSize(int blocks, int detail)
535 {
536 setsizeneeded = true;
537 setblocks = blocks;
538 setdetail = detail;
539 }
540
541 /*
542 ==============
543 =
544 = R_ExecuteSetViewSize
545 =
546 ==============
547 */
548
R_ExecuteSetViewSize(void)549 void R_ExecuteSetViewSize(void)
550 {
551 fixed_t cosadj, dy;
552 int i, j, level, startmap;
553
554 setsizeneeded = false;
555
556 if (setblocks == 11)
557 {
558 scaledviewwidth = SCREENWIDTH;
559 viewheight = SCREENHEIGHT;
560 }
561 else
562 {
563 scaledviewwidth = setblocks * 32;
564 viewheight = (setblocks * 161 / 10);
565 }
566
567 detailshift = setdetail;
568 viewwidth = scaledviewwidth >> detailshift;
569
570 centery = viewheight / 2;
571 centerx = viewwidth / 2;
572 centerxfrac = centerx << FRACBITS;
573 centeryfrac = centery << FRACBITS;
574 projection = centerxfrac;
575
576 if (!detailshift)
577 {
578 colfunc = basecolfunc = R_DrawColumn;
579 tlcolfunc = R_DrawTLColumn;
580 transcolfunc = R_DrawTranslatedColumn;
581 spanfunc = R_DrawSpan;
582 }
583 else
584 {
585 colfunc = basecolfunc = R_DrawColumnLow;
586 tlcolfunc = R_DrawTLColumn;
587 transcolfunc = R_DrawTranslatedColumn;
588 spanfunc = R_DrawSpanLow;
589 }
590
591 R_InitBuffer(scaledviewwidth, viewheight);
592
593 R_InitTextureMapping();
594
595 //
596 // psprite scales
597 //
598 pspritescale = FRACUNIT * viewwidth / SCREENWIDTH;
599 pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth;
600
601 //
602 // thing clipping
603 //
604 for (i = 0; i < viewwidth; i++)
605 screenheightarray[i] = viewheight;
606
607 //
608 // planes
609 //
610 for (i = 0; i < viewheight; i++)
611 {
612 dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2;
613 dy = abs(dy);
614 yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy);
615 }
616
617 for (i = 0; i < viewwidth; i++)
618 {
619 cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]);
620 distscale[i] = FixedDiv(FRACUNIT, cosadj);
621 }
622
623 //
624 // Calculate the light levels to use for each level / scale combination
625 //
626 for (i = 0; i < LIGHTLEVELS; i++)
627 {
628 startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
629 for (j = 0; j < MAXLIGHTSCALE; j++)
630 {
631 level =
632 startmap -
633 j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP;
634 if (level < 0)
635 level = 0;
636 if (level >= NUMCOLORMAPS)
637 level = NUMCOLORMAPS - 1;
638 scalelight[i][j] = colormaps + level * 256;
639 }
640 }
641
642 //
643 // draw the border
644 //
645 R_DrawViewBorder(); // erase old menu stuff
646 }
647
648
649 /*
650 ==============
651 =
652 = R_Init
653 =
654 ==============
655 */
656
657 int detailLevel;
658 int screenblocks = 10;
659
R_Init(void)660 void R_Init(void)
661 {
662 R_InitData();
663 R_InitPointToAngle();
664 R_InitTables();
665 // viewwidth / viewheight / detailLevel are set by the defaults
666 R_SetViewSize(screenblocks, detailLevel);
667 R_InitPlanes();
668 R_InitLightTables();
669 R_InitSkyMap();
670 R_InitTranslationTables();
671 framecount = 0;
672 }
673
674 /*
675 ==============
676 =
677 = R_PointInSubsector
678 =
679 ==============
680 */
681
R_PointInSubsector(fixed_t x,fixed_t y)682 subsector_t *R_PointInSubsector(fixed_t x, fixed_t y)
683 {
684 node_t *node;
685 int side, nodenum;
686
687 if (!numnodes) // single subsector is a special case
688 return subsectors;
689
690 nodenum = numnodes - 1;
691
692 while (!(nodenum & NF_SUBSECTOR))
693 {
694 node = &nodes[nodenum];
695 side = R_PointOnSide(x, y, node);
696 nodenum = node->children[side];
697 }
698
699 return &subsectors[nodenum & ~NF_SUBSECTOR];
700
701 }
702
703 //----------------------------------------------------------------------------
704 //
705 // PROC R_SetupFrame
706 //
707 //----------------------------------------------------------------------------
708
R_SetupFrame(player_t * player)709 void R_SetupFrame(player_t * player)
710 {
711 int i;
712 int tableAngle;
713 int tempCentery;
714 int intensity;
715
716 //drawbsp = 1;
717 viewplayer = player;
718 // haleyjd: removed WATCOMC
719 // haleyjd FIXME: viewangleoffset handling?
720 viewangle = player->mo->angle + viewangleoffset;
721 tableAngle = viewangle >> ANGLETOFINESHIFT;
722 viewx = player->mo->x;
723 viewy = player->mo->y;
724
725 if (localQuakeHappening[displayplayer] && !paused)
726 {
727 intensity = localQuakeHappening[displayplayer];
728 viewx += ((M_Random() % (intensity << 2))
729 - (intensity << 1)) << FRACBITS;
730 viewy += ((M_Random() % (intensity << 2))
731 - (intensity << 1)) << FRACBITS;
732 }
733
734 extralight = player->extralight;
735 viewz = player->viewz;
736
737 tempCentery = viewheight / 2 + (player->lookdir) * screenblocks / 10;
738 if (centery != tempCentery)
739 {
740 centery = tempCentery;
741 centeryfrac = centery << FRACBITS;
742 for (i = 0; i < viewheight; i++)
743 {
744 yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT,
745 abs(((i - centery) << FRACBITS) +
746 FRACUNIT / 2));
747 }
748 }
749 viewsin = finesine[tableAngle];
750 viewcos = finecosine[tableAngle];
751 sscount = 0;
752 if (player->fixedcolormap)
753 {
754 fixedcolormap = colormaps + player->fixedcolormap
755 * 256 * sizeof(lighttable_t);
756 walllights = scalelightfixed;
757 for (i = 0; i < MAXLIGHTSCALE; i++)
758 {
759 scalelightfixed[i] = fixedcolormap;
760 }
761 }
762 else
763 {
764 fixedcolormap = 0;
765 }
766 framecount++;
767 validcount++;
768 if (BorderNeedRefresh)
769 {
770 if (setblocks < 10)
771 {
772 R_DrawViewBorder();
773 }
774 BorderNeedRefresh = false;
775 BorderTopRefresh = false;
776 UpdateState |= I_FULLSCRN;
777 }
778 if (BorderTopRefresh)
779 {
780 if (setblocks < 10)
781 {
782 R_DrawTopBorder();
783 }
784 BorderTopRefresh = false;
785 UpdateState |= I_MESSAGES;
786 }
787
788 #if 0
789 {
790 static int frame;
791 memset(screen, frame, SCREENWIDTH * SCREENHEIGHT);
792 frame++;
793 }
794 #endif
795 }
796
797 /*
798 ==============
799 =
800 = R_RenderView
801 =
802 ==============
803 */
804
R_RenderPlayerView(player_t * player)805 void R_RenderPlayerView(player_t * player)
806 {
807 R_SetupFrame(player);
808 R_ClearClipSegs();
809 R_ClearDrawSegs();
810 R_ClearPlanes();
811 R_ClearSprites();
812 NetUpdate(); // check for new console commands
813
814 // Make displayed player invisible locally
815 if (localQuakeHappening[displayplayer] && gamestate == GS_LEVEL)
816 {
817 players[displayplayer].mo->flags2 |= MF2_DONTDRAW;
818 R_RenderBSPNode(numnodes - 1); // head node is the last node output
819 players[displayplayer].mo->flags2 &= ~MF2_DONTDRAW;
820 }
821 else
822 {
823 R_RenderBSPNode(numnodes - 1); // head node is the last node output
824 }
825
826 NetUpdate(); // check for new console commands
827 R_DrawPlanes();
828 NetUpdate(); // check for new console commands
829 R_DrawMasked();
830 NetUpdate(); // check for new console commands
831 }
832