]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/r_draw.c
games/doom: #A/audio -> /dev/audio
[plan9front.git] / sys / src / games / doom / r_draw.c
1 // Emacs style mode select   -*- C++ -*- 
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //      The actual span/column drawing functions.
21 //      Here find the main potential for optimization,
22 //       e.g. inline assembly, different algorithms.
23 //
24 //-----------------------------------------------------------------------------
25
26
27 static const char
28 rcsid[] = "$Id: r_draw.c,v 1.4 1997/02/03 16:47:55 b1 Exp $";
29
30
31 #include "doomdef.h"
32
33 #include "i_system.h"
34 #include "z_zone.h"
35 #include "w_wad.h"
36
37 #include "r_local.h"
38
39 // Needs access to LFB (guess what).
40 #include "v_video.h"
41
42 // State.
43 #include "doomstat.h"
44
45
46 // ?
47 #define MAXWIDTH                        1120
48 #define MAXHEIGHT                       832
49
50 // status bar height at bottom of screen
51 #define SBARHEIGHT              32
52
53 //
54 // All drawing to the view buffer is accomplished in this file.
55 // The other refresh files only know about ccordinates,
56 //  not the architecture of the frame buffer.
57 // Conveniently, the frame buffer is a linear one,
58 //  and we need only the base address,
59 //  and the total size == width*height*depth/8.,
60 //
61
62
63 byte*           viewimage; 
64 int             viewwidth;
65 int             scaledviewwidth;
66 int             viewheight;
67 int             viewwindowx;
68 int             viewwindowy; 
69 byte*           ylookup[MAXHEIGHT]; 
70 int             columnofs[MAXWIDTH]; 
71
72 // Color tables for different players,
73 //  translate a limited part to another
74 //  (color ramps used for  suit colors).
75 //
76 byte            translations[3][256];   
77  
78  
79
80
81 //
82 // R_DrawColumn
83 // Source is the top of the column to scale.
84 //
85 lighttable_t*           dc_colormap; 
86 int                     dc_x; 
87 int                     dc_yl; 
88 int                     dc_yh; 
89 fixed_t                 dc_iscale; 
90 fixed_t                 dc_texturemid;
91
92 // first pixel in a column (possibly virtual) 
93 byte*                   dc_source;              
94
95 // just for profiling 
96 int                     dccount;
97
98 //
99 // A column is a vertical slice/span from a wall texture that,
100 //  given the DOOM style restrictions on the view orientation,
101 //  will always have constant z depth.
102 // Thus a special case loop for very fast rendering can
103 //  be used. It has also been used with Wolfenstein 3D.
104 // 
105 void R_DrawColumn (void) 
106
107     int                 count; 
108     byte*               dest; 
109     fixed_t             frac;
110     fixed_t             fracstep;        
111  
112     count = dc_yh - dc_yl; 
113
114     // Zero length, column does not exceed a pixel.
115     if (count < 0) 
116         return; 
117                                  
118 #ifdef RANGECHECK 
119     if ((unsigned)dc_x >= SCREENWIDTH
120         || dc_yl < 0
121         || dc_yh >= SCREENHEIGHT) 
122         I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); 
123 #endif 
124
125     // Framebuffer destination address.
126     // Use ylookup LUT to avoid multiply with ScreenWidth.
127     // Use columnofs LUT for subwindows? 
128     dest = ylookup[dc_yl] + columnofs[dc_x];  
129
130     // Determine scaling,
131     //  which is the only mapping to be done.
132     fracstep = dc_iscale; 
133     frac = dc_texturemid + (dc_yl-centery)*fracstep; 
134
135     // Inner loop that does the actual texture mapping,
136     //  e.g. a DDA-lile scaling.
137     // This is as fast as it gets.
138     do 
139     {
140         // Re-map color indices from wall texture column
141         //  using a lighting/special effects LUT.
142         *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]];
143         
144         dest += SCREENWIDTH; 
145         frac += fracstep;
146         
147     } while (count--); 
148
149
150
151 void R_DrawColumnLow (void) 
152
153     int                 count; 
154     byte*               dest; 
155     byte*               dest2;
156     fixed_t             frac;
157     fixed_t             fracstep;        
158  
159     count = dc_yh - dc_yl; 
160
161     // Zero length.
162     if (count < 0) 
163         return; 
164                                  
165 #ifdef RANGECHECK 
166     if ((unsigned)dc_x >= SCREENWIDTH
167         || dc_yl < 0
168         || dc_yh >= SCREENHEIGHT)
169     {
170         
171         I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
172     }
173     //  dccount++; 
174 #endif 
175     // Blocky mode, need to multiply by 2.
176     dc_x <<= 1;
177     
178     dest = ylookup[dc_yl] + columnofs[dc_x];
179     dest2 = ylookup[dc_yl] + columnofs[dc_x+1];
180     
181     fracstep = dc_iscale; 
182     frac = dc_texturemid + (dc_yl-centery)*fracstep;
183     
184     do 
185     {
186         // Hack. Does not work corretly.
187         *dest2 = *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]];
188         dest += SCREENWIDTH;
189         dest2 += SCREENWIDTH;
190         frac += fracstep; 
191
192     } while (count--);
193 }
194
195
196 //
197 // Spectre/Invisibility.
198 //
199 #define FUZZTABLE               50 
200 #define FUZZOFF (SCREENWIDTH)
201
202
203 int     fuzzoffset[FUZZTABLE] =
204 {
205     FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
206     FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
207     FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,
208     FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
209     FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,
210     FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,
211     FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF 
212 }; 
213
214 int     fuzzpos = 0; 
215
216
217 //
218 // Framebuffer postprocessing.
219 // Creates a fuzzy image by copying pixels
220 //  from adjacent ones to left and right.
221 // Used with an all black colormap, this
222 //  could create the SHADOW effect,
223 //  i.e. spectres and invisible players.
224 //
225 void R_DrawFuzzColumn (void) 
226
227     int                 count; 
228     byte*               dest; 
229     fixed_t             frac;
230     fixed_t             fracstep;        
231
232     // Adjust borders. Low... 
233     if (!dc_yl) 
234         dc_yl = 1;
235
236     // .. and high.
237     if (dc_yh == viewheight-1) 
238         dc_yh = viewheight - 2; 
239                  
240     count = dc_yh - dc_yl; 
241
242     // Zero length.
243     if (count < 0) 
244         return; 
245
246     
247 #ifdef RANGECHECK 
248     if ((unsigned)dc_x >= SCREENWIDTH
249         || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
250     {
251         I_Error ("R_DrawFuzzColumn: %i to %i at %i",
252                  dc_yl, dc_yh, dc_x);
253     }
254 #endif
255
256
257     // Keep till detailshift bug in blocky mode fixed,
258     //  or blocky mode removed.
259     /* WATCOM code 
260     if (detailshift)
261     {
262         if (dc_x & 1)
263         {
264             outpw (GC_INDEX,GC_READMAP+(2<<8) ); 
265             outp (SC_INDEX+1,12); 
266         }
267         else
268         {
269             outpw (GC_INDEX,GC_READMAP); 
270             outp (SC_INDEX+1,3); 
271         }
272         dest = destview + dc_yl*80 + (dc_x>>1); 
273     }
274     else
275     {
276         outpw (GC_INDEX,GC_READMAP+((dc_x&3)<<8) ); 
277         outp (SC_INDEX+1,1<<(dc_x&3)); 
278         dest = destview + dc_yl*80 + (dc_x>>2); 
279     }*/
280
281     
282     // Does not work with blocky mode.
283     dest = ylookup[dc_yl] + columnofs[dc_x];
284
285     // Looks familiar.
286     fracstep = dc_iscale; 
287     frac = dc_texturemid + (dc_yl-centery)*fracstep; 
288
289     // Looks like an attempt at dithering,
290     //  using the colormap #6 (of 0-31, a bit
291     //  brighter than average).
292     do 
293     {
294         // Lookup framebuffer, and retrieve
295         //  a pixel that is either one column
296         //  left or right of the current one.
297         // Add index from colormap to index.
298         *dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; 
299
300         // Clamp table lookup index.
301         if (++fuzzpos == FUZZTABLE) 
302             fuzzpos = 0;
303         
304         dest += SCREENWIDTH;
305
306         frac += fracstep; 
307     } while (count--); 
308
309  
310   
311  
312
313 //
314 // R_DrawTranslatedColumn
315 // Used to draw player sprites
316 //  with the green colorramp mapped to others.
317 // Could be used with different translation
318 //  tables, e.g. the lighter colored version
319 //  of the BaronOfHell, the HellKnight, uses
320 //  identical sprites, kinda brightened up.
321 //
322 byte*   dc_translation;
323 byte*   translationtables;
324
325 void R_DrawTranslatedColumn (void) 
326
327     int                 count; 
328     byte*               dest; 
329     fixed_t             frac;
330     fixed_t             fracstep;        
331  
332     count = dc_yh - dc_yl; 
333     if (count < 0) 
334         return; 
335                                  
336 #ifdef RANGECHECK 
337     if ((unsigned)dc_x >= SCREENWIDTH
338         || dc_yl < 0
339         || dc_yh >= SCREENHEIGHT)
340     {
341         I_Error ( "R_DrawColumn: %i to %i at %i",
342                   dc_yl, dc_yh, dc_x);
343     }
344     
345 #endif 
346
347
348     // WATCOM VGA specific.
349     /* Keep for fixing.
350     if (detailshift)
351     {
352         if (dc_x & 1)
353             outp (SC_INDEX+1,12); 
354         else
355             outp (SC_INDEX+1,3);
356         
357         dest = destview + dc_yl*80 + (dc_x>>1); 
358     }
359     else
360     {
361         outp (SC_INDEX+1,1<<(dc_x&3)); 
362
363         dest = destview + dc_yl*80 + (dc_x>>2); 
364     }*/
365
366     
367     // FIXME. As above.
368     dest = ylookup[dc_yl] + columnofs[dc_x]; 
369
370     // Looks familiar.
371     fracstep = dc_iscale; 
372     frac = dc_texturemid + (dc_yl-centery)*fracstep; 
373
374     // Here we do an additional index re-mapping.
375     do 
376     {
377         // Translation tables are used
378         //  to map certain colorramps to other ones,
379         //  used with PLAY sprites.
380         // Thus the "green" ramp of the player 0 sprite
381         //  is mapped to gray, red, black/indigo. 
382         *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]];
383         dest += SCREENWIDTH;
384         
385         frac += fracstep; 
386     } while (count--); 
387
388
389
390
391
392 //
393 // R_InitTranslationTables
394 // Creates the translation tables to map
395 //  the green color ramp to gray, brown, red.
396 // Assumes a given structure of the PLAYPAL.
397 // Could be read from a lump instead.
398 //
399 void R_InitTranslationTables (void)
400 {
401     int         i;
402         
403     translationtables = Z_Malloc (256*3+255, PU_STATIC, 0);
404     translationtables = (byte *)(( (int)translationtables + 255 )& ~255);
405     
406     // translate just the 16 green colors
407     for (i=0 ; i<256 ; i++)
408     {
409         if (i >= 0x70 && i<= 0x7f)
410         {
411             // map green ramp to gray, brown, red
412             translationtables[i] = 0x60 + (i&0xf);
413             translationtables [i+256] = 0x40 + (i&0xf);
414             translationtables [i+512] = 0x20 + (i&0xf);
415         }
416         else
417         {
418             // Keep all other colors as is.
419             translationtables[i] = translationtables[i+256] 
420                 = translationtables[i+512] = i;
421         }
422     }
423 }
424
425
426
427
428 //
429 // R_DrawSpan 
430 // With DOOM style restrictions on view orientation,
431 //  the floors and ceilings consist of horizontal slices
432 //  or spans with constant z depth.
433 // However, rotation around the world z axis is possible,
434 //  thus this mapping, while simpler and faster than
435 //  perspective correct texture mapping, has to traverse
436 //  the texture at an angle in all but a few cases.
437 // In consequence, flats are not stored by column (like walls),
438 //  and the inner loop has to step in texture space u and v.
439 //
440 int                     ds_y; 
441 int                     ds_x1; 
442 int                     ds_x2;
443
444 lighttable_t*           ds_colormap; 
445
446 fixed_t                 ds_xfrac; 
447 fixed_t                 ds_yfrac; 
448 fixed_t                 ds_xstep; 
449 fixed_t                 ds_ystep;
450
451 // start of a 64*64 tile image 
452 byte*                   ds_source;      
453
454 // just for profiling
455 int                     dscount;
456
457
458 //
459 // Draws the actual span.
460 void R_DrawSpan (void) 
461
462     fixed_t             xfrac;
463     fixed_t             yfrac; 
464     byte*               dest; 
465     int                 count;
466     int                 spot; 
467          
468 #ifdef RANGECHECK 
469     if (ds_x2 < ds_x1
470         || ds_x1<0
471         || ds_x2>=SCREENWIDTH  
472         || (unsigned)ds_y>SCREENHEIGHT)
473     {
474         I_Error( "R_DrawSpan: %i to %i at %i",
475                  ds_x1,ds_x2,ds_y);
476     }
477 //      dscount++; 
478 #endif 
479
480     
481     xfrac = ds_xfrac; 
482     yfrac = ds_yfrac; 
483          
484     dest = ylookup[ds_y] + columnofs[ds_x1];
485
486     // We do not check for zero spans here?
487     count = ds_x2 - ds_x1; 
488
489     do 
490     {
491         // Current texture index in u,v.
492         spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
493
494         // Lookup pixel from flat texture tile,
495         //  re-index using light/colormap.
496         *dest++ = ds_colormap[ds_source[spot]];
497
498         // Next step in u,v.
499         xfrac += ds_xstep; 
500         yfrac += ds_ystep;
501         
502     } while (count--); 
503
504
505
506 //
507 // Again..
508 //
509 void R_DrawSpanLow (void) 
510
511     fixed_t             xfrac;
512     fixed_t             yfrac; 
513     byte*               dest; 
514     int                 count;
515     int                 spot; 
516          
517 #ifdef RANGECHECK 
518     if (ds_x2 < ds_x1
519         || ds_x1<0
520         || ds_x2>=SCREENWIDTH  
521         || (unsigned)ds_y>SCREENHEIGHT)
522     {
523         I_Error( "R_DrawSpan: %i to %i at %i",
524                  ds_x1,ds_x2,ds_y);
525     }
526 //      dscount++; 
527 #endif 
528          
529     xfrac = ds_xfrac; 
530     yfrac = ds_yfrac; 
531
532     // Blocky mode, need to multiply by 2.
533     ds_x1 <<= 1;
534     ds_x2 <<= 1;
535     
536     dest = ylookup[ds_y] + columnofs[ds_x1];
537   
538     
539     count = ds_x2 - ds_x1; 
540     do 
541     { 
542         spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
543         // Lowres/blocky mode does it twice,
544         //  while scale is adjusted appropriately.
545         *dest++ = ds_colormap[ds_source[spot]]; 
546         *dest++ = ds_colormap[ds_source[spot]];
547         
548         xfrac += ds_xstep; 
549         yfrac += ds_ystep; 
550
551     } while (count--); 
552 }
553
554 //
555 // R_InitBuffer 
556 // Creats lookup tables that avoid
557 //  multiplies and other hazzles
558 //  for getting the framebuffer address
559 //  of a pixel to draw.
560 //
561 void
562 R_InitBuffer
563 ( int           width,
564   int           height ) 
565
566     int         i; 
567
568     // Handle resize,
569     //  e.g. smaller view windows
570     //  with border and/or status bar.
571     viewwindowx = (SCREENWIDTH-width) >> 1; 
572
573     // Column offset. For windows.
574     for (i=0 ; i<width ; i++) 
575         columnofs[i] = viewwindowx + i;
576
577     // Samw with base row offset.
578     if (width == SCREENWIDTH) 
579         viewwindowy = 0; 
580     else 
581         viewwindowy = (SCREENHEIGHT-SBARHEIGHT-height) >> 1; 
582
583     // Preclaculate all row offsets.
584     for (i=0 ; i<height ; i++) 
585         ylookup[i] = screens[0] + (i+viewwindowy)*SCREENWIDTH; 
586
587  
588  
589
590
591 //
592 // R_FillBackScreen
593 // Fills the back screen with a pattern
594 //  for variable screen sizes
595 // Also draws a beveled edge.
596 //
597 void R_FillBackScreen (void) 
598
599     byte*       src;
600     byte*       dest; 
601     int         x;
602     int         y; 
603     patch_t*    patch;
604
605     // DOOM border patch.
606     char        name1[] = "FLOOR7_2";
607
608     // DOOM II border patch.
609     char        name2[] = "GRNROCK";    
610
611     char*       name;
612         
613     if (scaledviewwidth == 320)
614         return;
615         
616     if ( gamemode == commercial)
617         name = name2;
618     else
619         name = name1;
620     
621     src = W_CacheLumpName (name, PU_CACHE); 
622     dest = screens[1]; 
623          
624     for (y=0 ; y<SCREENHEIGHT-SBARHEIGHT ; y++) 
625     { 
626         for (x=0 ; x<SCREENWIDTH/64 ; x++) 
627         { 
628             memcpy (dest, src+((y&63)<<6), 64); 
629             dest += 64; 
630         } 
631
632         if (SCREENWIDTH&63) 
633         { 
634             memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63); 
635             dest += (SCREENWIDTH&63); 
636         } 
637     } 
638         
639     patch = W_CacheLumpName ("brdr_t",PU_CACHE);
640
641     for (x=0 ; x<scaledviewwidth ; x+=8)
642         V_DrawPatch (viewwindowx+x,viewwindowy-8,1,patch);
643     patch = W_CacheLumpName ("brdr_b",PU_CACHE);
644
645     for (x=0 ; x<scaledviewwidth ; x+=8)
646         V_DrawPatch (viewwindowx+x,viewwindowy+viewheight,1,patch);
647     patch = W_CacheLumpName ("brdr_l",PU_CACHE);
648
649     for (y=0 ; y<viewheight ; y+=8)
650         V_DrawPatch (viewwindowx-8,viewwindowy+y,1,patch);
651     patch = W_CacheLumpName ("brdr_r",PU_CACHE);
652
653     for (y=0 ; y<viewheight ; y+=8)
654         V_DrawPatch (viewwindowx+scaledviewwidth,viewwindowy+y,1,patch);
655
656
657     // Draw beveled edge. 
658     V_DrawPatch (viewwindowx-8,
659                  viewwindowy-8,
660                  1,
661                  W_CacheLumpName ("brdr_tl",PU_CACHE));
662     
663     V_DrawPatch (viewwindowx+scaledviewwidth,
664                  viewwindowy-8,
665                  1,
666                  W_CacheLumpName ("brdr_tr",PU_CACHE));
667     
668     V_DrawPatch (viewwindowx-8,
669                  viewwindowy+viewheight,
670                  1,
671                  W_CacheLumpName ("brdr_bl",PU_CACHE));
672     
673     V_DrawPatch (viewwindowx+scaledviewwidth,
674                  viewwindowy+viewheight,
675                  1,
676                  W_CacheLumpName ("brdr_br",PU_CACHE));
677
678  
679
680 //
681 // Copy a screen buffer.
682 //
683 void
684 R_VideoErase
685 ( unsigned      ofs,
686   int           count ) 
687
688   // LFB copy.
689   // This might not be a good idea if memcpy
690   //  is not optiomal, e.g. byte by byte on
691   //  a 32bit CPU, as GNU GCC/Linux libc did
692   //  at one point.
693     memcpy (screens[0]+ofs, screens[1]+ofs, count); 
694
695
696
697 //
698 // R_DrawViewBorder
699 // Draws the border around the view
700 //  for different size windows?
701 //
702 void
703 V_MarkRect
704 ( int           x,
705   int           y,
706   int           width,
707   int           height ); 
708  
709 void R_DrawViewBorder (void) 
710
711     int         top;
712     int         side;
713     int         ofs;
714     int         i; 
715  
716     if (scaledviewwidth == SCREENWIDTH) 
717         return; 
718   
719     top = ((SCREENHEIGHT-SBARHEIGHT)-viewheight)/2; 
720     side = (SCREENWIDTH-scaledviewwidth)/2; 
721  
722     // copy top and one line of left side 
723     R_VideoErase (0, top*SCREENWIDTH+side); 
724  
725     // copy one line of right side and bottom 
726     ofs = (viewheight+top)*SCREENWIDTH-side; 
727     R_VideoErase (ofs, top*SCREENWIDTH+side); 
728  
729     // copy sides using wraparound 
730     ofs = top*SCREENWIDTH + SCREENWIDTH-side; 
731     side <<= 1;
732     
733     for (i=1 ; i<viewheight ; i++) 
734     { 
735         R_VideoErase (ofs, side); 
736         ofs += SCREENWIDTH; 
737     } 
738
739     // ? 
740     V_MarkRect (0,0,SCREENWIDTH, SCREENHEIGHT-SBARHEIGHT); 
741
742  
743