1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: r_draw.cpp 4715 2014-03-29 15:58:53Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
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 #include <stddef.h>
27 #include <assert.h>
28 #include <algorithm>
29
30 #include <SDL.h>
31 #if (SDL_VERSION > SDL_VERSIONNUM(1, 2, 7))
32 #include "SDL_cpuinfo.h"
33 #endif
34 #include "r_intrin.h"
35
36 #include "m_alloc.h"
37 #include "doomdef.h"
38 #include "i_system.h"
39 #include "z_zone.h"
40 #include "w_wad.h"
41 #include "r_local.h"
42 #include "v_video.h"
43 #include "doomstat.h"
44 #include "st_stuff.h"
45
46 #include "gi.h"
47
48 #undef RANGECHECK
49
50 // status bar height at bottom of screen
51 // [RH] status bar position at bottom of screen
52 extern int ST_Y;
53
54 //
55 // All drawing to the view buffer is accomplished in this file.
56 // The other refresh files only know about ccordinates,
57 // not the architecture of the frame buffer.
58 // Conveniently, the frame buffer is a linear one,
59 // and we need only the base address,
60 // and the total size == width*height*depth/8.,
61 //
62
63 extern "C" {
64 drawcolumn_t dcol;
65 drawspan_t dspan;
66 }
67
68
69
70 byte* viewimage;
71 extern "C" {
72 int viewwidth;
73 int viewheight;
74 }
75 int scaledviewwidth;
76 int viewwindowx;
77 int viewwindowy;
78 byte** ylookup;
79 int* columnofs;
80
81 extern "C" {
82 int realviewwidth; // [RH] Physical width of view window
83 int realviewheight; // [RH] Physical height of view window
84 int detailxshift; // [RH] X shift for horizontal detail level
85 int detailyshift; // [RH] Y shift for vertical detail level
86 }
87
88 // [RH] Pointers to the different column drawers.
89 // These get changed depending on the current
90 // screen depth.
91 void (*R_DrawColumn)(void);
92 void (*R_DrawColumnHoriz)(void);
93 void (*R_DrawFuzzColumn)(void);
94 void (*R_DrawTranslucentColumn)(void);
95 void (*R_DrawTranslatedColumn)(void);
96 void (*R_DrawSpan)(void);
97 void (*R_DrawSlopeSpan)(void);
98 void (*R_FillColumn)(void);
99 void (*R_FillSpan)(void);
100 void (*R_FillTranslucentSpan)(void);
101 void (*rt_copy1col) (int hx, int sx, int yl, int yh);
102 void (*rt_copy4cols) (int sx, int yl, int yh);
103 void (*rt_map1col) (int hx, int sx, int yl, int yh);
104 void (*rt_map4cols) (int sx, int yl, int yh);
105 void (*rt_lucent1col) (int hx, int sx, int yl, int yh);
106 void (*rt_lucent4cols) (int sx, int yl, int yh);
107 void (*rt_tlate1col) (int hx, int sx, int yl, int yh);
108 void (*rt_tlate4cols) (int sx, int yl, int yh);
109 void (*rt_tlatelucent1col) (int hx, int sx, int yl, int yh);
110 void (*rt_tlatelucent4cols) (int sx, int yl, int yh);
111
112 // Possibly vectorized functions:
113 void (*R_DrawSpanD)(void);
114 void (*R_DrawSlopeSpanD)(void);
115 void (*r_dimpatchD)(const DCanvas *const cvs, argb_t color, int alpha, int x1, int y1, int w, int h);
116
117 // ============================================================================
118 //
119 // Fuzz Table
120 //
121 // Framebuffer postprocessing.
122 // Creates a fuzzy image by copying pixels
123 // from adjacent ones to left and right.
124 // Used with an all black colormap, this
125 // could create the SHADOW effect,
126 // i.e. spectres and invisible players.
127 //
128 // ============================================================================
129
130 #define FUZZTABLE 64 // [RH] FUZZTABLE changed from 50 to 64
131 #define FUZZOFF (screen->pitch)
132
133 static int fuzzoffset[FUZZTABLE];
134 static int fuzzpos = 0;
135
136 static const signed char fuzzinit[FUZZTABLE] = {
137 1,-1, 1,-1, 1, 1,-1, 1,
138 1,-1, 1, 1, 1,-1, 1, 1,
139 1,-1,-1,-1,-1, 1,-1,-1,
140 1, 1, 1, 1,-1, 1,-1, 1,
141 1,-1,-1, 1, 1,-1,-1,-1,
142 -1, 1, 1, 1, 1,-1, 1, 1,
143 -1, 1, 1, 1,-1, 1, 1, 1,
144 -1, 1, 1,-1, 1, 1,-1, 1
145 };
146
R_InitFuzzTable(void)147 void R_InitFuzzTable (void)
148 {
149 int i;
150 int fuzzoff;
151
152 screen->Lock ();
153 fuzzoff = FUZZOFF << detailyshift;
154 screen->Unlock ();
155
156 for (i = 0; i < FUZZTABLE; i++)
157 fuzzoffset[i] = fuzzinit[i] * fuzzoff;
158 }
159
160
161 // ============================================================================
162 //
163 // Translucency Table
164 //
165 // ============================================================================
166
167 /*
168 [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses
169 a 32k RGB table instead of an 8k one. At least on my machine, it's
170 slightly faster (probably because it uses only one shift instead of
171 two), and it looks considerably less green at the ends of the
172 translucency range. The extra size doesn't appear to be an issue.
173
174 The following note is from DOSDoom 0.65:
175
176 New translucency algorithm, by Erik Sandberg:
177
178 Basically, we compute the red, green and blue values for each pixel, and
179 then use a RGB table to check which one of the palette colours that best
180 represents those RGB values. The RGB table is 8k big, with 4 R-bits,
181 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k
182 table takes up more memory and results in more cache misses, so an 8k
183 table seemed to be quite ultimate.
184
185 The computation of the RGB for each pixel is accelerated by using two
186 1k tables for each translucency level.
187 The xth element of one of these tables contains the r, g and b values for
188 the colour x, weighted for the current translucency level (for example,
189 the weighted rgb values for background colour at 75% translucency are 1/4
190 of the original rgb values). The rgb values are stored as three
191 low-precision fixed point values, packed into one long per colour:
192 Bit 0-4: Frac part of blue (5 bits)
193 Bit 5-8: Int part of blue (4 bits)
194 Bit 9-13: Frac part of red (5 bits)
195 Bit 14-17: Int part of red (4 bits)
196 Bit 18-22: Frac part of green (5 bits)
197 Bit 23-27: Int part of green (5 bits)
198 Bit 28-31: All zeros (4 bits)
199
200 The point of this format is that the two colours now can be added, and
201 then be converted to a RGB table index very easily: First, we just set
202 all the frac bits and the four upper zero bits to 1. It's now possible
203 to get the RGB table index by anding the current value >> 5 with the
204 current value >> 19. When asm-optimised, this should be the fastest
205 algorithm that uses RGB tables.
206 */
207
208
209 // ============================================================================
210 //
211 // Indexed-color Translation Table
212 //
213 // Used to draw player sprites with the green colorramp mapped to others.
214 // Could be used with different translation tables, e.g. the lighter colored
215 // version of the BaronOfHell, the HellKnight, uses identical sprites, kinda
216 // brightened up.
217 //
218 // ============================================================================
219
220 byte* translationtables;
221 argb_t translationRGB[MAXPLAYERS+1][16];
222 byte *Ranges;
223 static byte *translationtablesmem = NULL;
224
225 //
226 // R_InitTranslationTables
227 //
228 // Creates the translation tables to map
229 // the green color ramp to gray, brown, red.
230 // Assumes a given structure of the PLAYPAL.
231 // Could be read from a lump instead.
232 //
R_InitTranslationTables(void)233 void R_InitTranslationTables (void)
234 {
235 static const char ranges[23][8] = {
236 "CRBRICK",
237 "CRTAN",
238 "CRGRAY",
239 "CRGREEN",
240 "CRBROWN",
241 "CRGOLD",
242 "CRRED",
243 "CRBLUE2",
244 { 'C','R','O','R','A','N','G','E' },
245 "CRGRAY", // "White"
246 { 'C','R','Y','E','L','L','O','W' },
247 "CRRED", // "Untranslated"
248 "CRGRAY", // "Black"
249 "CRBLUE",
250 "CRTAN", // "Cream"
251 "CRGREEN", // "Olive"
252 "CRGREEN", // "Dark Green"
253 "CRRED", // "Dark Red"
254 "CRBROWN", // "Dark Brown"
255 "CRRED", // "Purple"
256 "CRGRAY", // "Dark Gray"
257 "CRBLUE" // "Cyan"
258 };
259
260 R_FreeTranslationTables();
261
262 translationtablesmem = new byte[256*(MAXPLAYERS+3+22)+255]; // denis - fixme - magic numbers?
263
264 // [Toke - fix13]
265 // denis - cleaned this up somewhat
266 translationtables = (byte *)(((ptrdiff_t)translationtablesmem + 255) & ~255);
267
268 // [RH] Each player now gets their own translation table
269 // (soon to be palettes). These are set up during
270 // netgame arbitration and as-needed rather than
271 // in here. We do, however load some text translation
272 // tables from our PWAD (ala BOOM).
273
274 for (int i = 0; i < 256; i++)
275 translationtables[i] = i;
276
277 // Set up default translationRGB tables:
278 palette_t *pal = GetDefaultPalette();
279 for (int i = 0; i < MAXPLAYERS; ++i)
280 {
281 for (int j = 0x70; j < 0x80; ++j)
282 translationRGB[i][j - 0x70] = pal->basecolors[j];
283 }
284
285 for (int i = 1; i < MAXPLAYERS+3; i++)
286 memcpy (translationtables + i*256, translationtables, 256);
287
288 // create translation tables for dehacked patches that expect them
289 for (int i = 0x70; i < 0x80; i++) {
290 // map green ramp to gray, brown, red
291 translationtables[i+(MAXPLAYERS+0)*256] = 0x60 + (i&0xf);
292 translationtables[i+(MAXPLAYERS+1)*256] = 0x40 + (i&0xf);
293 translationtables[i+(MAXPLAYERS+2)*256] = 0x20 + (i&0xf);
294 }
295
296 Ranges = translationtables + (MAXPLAYERS+3)*256;
297 for (int i = 0; i < 22; i++)
298 W_ReadLump (W_GetNumForName (ranges[i]), Ranges + 256 * i);
299
300 }
301
R_FreeTranslationTables(void)302 void R_FreeTranslationTables (void)
303 {
304 delete[] translationtablesmem;
305 translationtablesmem = NULL;
306 }
307
308 // [Nes] Vanilla player translation table.
R_BuildClassicPlayerTranslation(int player,int color)309 void R_BuildClassicPlayerTranslation (int player, int color)
310 {
311 palette_t *pal = GetDefaultPalette();
312 int i;
313
314 if (color == 1) // Indigo
315 for (i = 0x70; i < 0x80; i++)
316 {
317 translationtables[i+(player * 256)] = 0x60 + (i&0xf);
318 translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
319 }
320 else if (color == 2) // Brown
321 for (i = 0x70; i < 0x80; i++)
322 {
323 translationtables[i+(player * 256)] = 0x40 + (i&0xf);
324 translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
325 }
326 else if (color == 3) // Red
327 for (i = 0x70; i < 0x80; i++)
328 {
329 translationtables[i+(player * 256)] = 0x20 + (i&0xf);
330 translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
331 }
332 }
333
R_CopyTranslationRGB(int fromplayer,int toplayer)334 void R_CopyTranslationRGB (int fromplayer, int toplayer)
335 {
336 for (int i = 0x70; i < 0x80; ++i)
337 {
338 translationRGB[toplayer][i - 0x70] = translationRGB[fromplayer][i - 0x70];
339 translationtables[i+(toplayer * 256)] = translationtables[i+(fromplayer * 256)];
340 }
341 }
342
343 // [RH] Create a player's translation table based on
344 // a given mid-range color.
R_BuildPlayerTranslation(int player,int color)345 void R_BuildPlayerTranslation (int player, int color)
346 {
347 palette_t *pal = GetDefaultPalette();
348 byte *table = &translationtables[player * 256];
349 int i;
350 float r = (float)RPART(color) / 255.0f;
351 float g = (float)GPART(color) / 255.0f;
352 float b = (float)BPART(color) / 255.0f;
353 float h, s, v;
354 float sdelta, vdelta;
355
356 RGBtoHSV (r, g, b, &h, &s, &v);
357
358 s -= 0.23f;
359 if (s < 0.0f)
360 s = 0.0f;
361 sdelta = 0.014375f;
362
363 v += 0.1f;
364 if (v > 1.0f)
365 v = 1.0f;
366 vdelta = -0.05882f;
367
368 for (i = 0x70; i < 0x80; i++) {
369 HSVtoRGB (&r, &g, &b, h, s, v);
370
371 // Set up RGB values for 32bpp translation:
372 translationRGB[player][i - 0x70] = MAKERGB(
373 (int)(r * 255.0f),
374 (int)(g * 255.0f),
375 (int)(b * 255.0f)
376 );
377
378 table[i] = BestColor (pal->basecolors,
379 (int)(r * 255.0f),
380 (int)(g * 255.0f),
381 (int)(b * 255.0f),
382 pal->numcolors);
383 s += sdelta;
384 if (s > 1.0f) {
385 s = 1.0f;
386 sdelta = 0.0f;
387 }
388
389 v += vdelta;
390 if (v < 0.0f) {
391 v = 0.0f;
392 vdelta = 0.0f;
393 }
394 }
395 }
396
397
398 // ============================================================================
399 //
400 // Spans
401 //
402 // With DOOM style restrictions on view orientation,
403 // the floors and ceilings consist of horizontal slices
404 // or spans with constant z depth.
405 // However, rotation around the world z axis is possible,
406 // thus this mapping, while simpler and faster than
407 // perspective correct texture mapping, has to traverse
408 // the texture at an angle in all but a few cases.
409 // In consequence, flats are not stored by column (like walls),
410 // and the inner loop has to step in texture space u and v.
411 //
412 // ============================================================================
413
414
415 // ============================================================================
416 //
417 // Generic Drawers
418 //
419 // Templated versions of column and span drawing functions
420 //
421 // ============================================================================
422
423 //
424 // R_BlankColumn
425 //
426 // [SL] - Does nothing (obviously). Used when a column drawing function
427 // pointer should not draw anything.
428 //
R_BlankColumn()429 void R_BlankColumn()
430 {
431 }
432
433 //
434 // R_BlankSpan
435 //
436 // [SL] - Does nothing (obviously). Used when a span drawing function
437 // pointer should not draw anything.
438 //
R_BlankSpan()439 void R_BlankSpan()
440 {
441 }
442
443 //
444 // R_FillColumnGeneric
445 //
446 // Templated version of a function to fill a column with a solid color.
447 // The data type of the destination pixels and a color-remapping functor
448 // are passed as template parameters.
449 //
450 template<typename PIXEL_T, typename COLORFUNC>
R_FillColumnGeneric(PIXEL_T * dest,const drawcolumn_t & drawcolumn)451 static forceinline void R_FillColumnGeneric(PIXEL_T* dest, const drawcolumn_t& drawcolumn)
452 {
453 #ifdef RANGECHECK
454 if (drawcolumn.x >= screen->width || drawcolumn.yl < 0 || drawcolumn.yh >= screen->height)
455 {
456 Printf (PRINT_HIGH, "R_FillColumn: %i to %i at %i\n", drawcolumn.yl, drawcolumn.yh, drawcolumn.x);
457 return;
458 }
459 #endif
460
461 int color = drawcolumn.color;
462 int pitch = drawcolumn.pitch / sizeof(PIXEL_T);
463 int count = drawcolumn.yh - drawcolumn.yl + 1;
464 if (count <= 0)
465 return;
466
467 COLORFUNC colorfunc(drawcolumn);
468
469 do {
470 colorfunc(color, dest);
471 dest += pitch;
472 } while (--count);
473 }
474
475
476 //
477 // R_DrawColumnGeneric
478 //
479 // A column is a vertical slice/span from a wall texture that,
480 // given the DOOM style restrictions on the view orientation,
481 // will always have constant z depth.
482 // Thus a special case loop for very fast rendering can
483 // be used. It has also been used with Wolfenstein 3D.
484 //
485 // Templated version of a column mapping function.
486 // The data type of the destination pixels and a color-remapping functor
487 // are passed as template parameters.
488 //
489 template<typename PIXEL_T, typename COLORFUNC>
R_DrawColumnGeneric(PIXEL_T * dest,const drawcolumn_t & drawcolumn)490 static forceinline void R_DrawColumnGeneric(PIXEL_T* dest, const drawcolumn_t& drawcolumn)
491 {
492 #ifdef RANGECHECK
493 if (drawcolumn.x >= screen->width || drawcolumn.yl < 0 || drawcolumn.yh >= screen->height)
494 {
495 Printf (PRINT_HIGH, "R_DrawColumn: %i to %i at %i\n", drawcolumn.yl, drawcolumn.yh, drawcolumn.x);
496 return;
497 }
498 #endif
499
500 palindex_t* source = drawcolumn.source;
501 int pitch = drawcolumn.pitch / sizeof(PIXEL_T);
502 int count = drawcolumn.yh - drawcolumn.yl + 1;
503 if (count <= 0)
504 return;
505
506 const fixed_t fracstep = drawcolumn.iscale;
507 fixed_t frac = drawcolumn.texturefrac;
508
509 const int texheight = drawcolumn.textureheight;
510 const int mask = (texheight >> FRACBITS) - 1;
511
512 COLORFUNC colorfunc(drawcolumn);
513
514 // [SL] Properly tile textures whose heights are not a power-of-2,
515 // avoiding a tutti-frutti effect. From Eternity Engine.
516 if (texheight & (texheight - 1))
517 {
518 // texture height is NOT a power-of-2
519 // just do a simple blit to the dest buffer (I'm lazy)
520
521 if (frac < 0)
522 while ((frac += texheight) < 0);
523 else
524 while (frac >= texheight)
525 frac -= texheight;
526
527 while (count--)
528 {
529 colorfunc(source[frac >> FRACBITS], dest);
530 dest += pitch;
531 if ((frac += fracstep) >= texheight)
532 frac -= texheight;
533 }
534 }
535 else
536 {
537 // texture height is a power-of-2
538 // do some loop unrolling
539 while (count >= 8)
540 {
541 colorfunc(source[(frac >> FRACBITS) & mask], dest);
542 dest += pitch; frac += fracstep;
543 colorfunc(source[(frac >> FRACBITS) & mask], dest);
544 dest += pitch; frac += fracstep;
545 colorfunc(source[(frac >> FRACBITS) & mask], dest);
546 dest += pitch; frac += fracstep;
547 colorfunc(source[(frac >> FRACBITS) & mask], dest);
548 dest += pitch; frac += fracstep;
549 colorfunc(source[(frac >> FRACBITS) & mask], dest);
550 dest += pitch; frac += fracstep;
551 colorfunc(source[(frac >> FRACBITS) & mask], dest);
552 dest += pitch; frac += fracstep;
553 colorfunc(source[(frac >> FRACBITS) & mask], dest);
554 dest += pitch; frac += fracstep;
555 colorfunc(source[(frac >> FRACBITS) & mask], dest);
556 dest += pitch; frac += fracstep;
557 count -= 8;
558 }
559
560 if (count & 1)
561 {
562 colorfunc(source[(frac >> FRACBITS) & mask], dest);
563 dest += pitch; frac += fracstep;
564 }
565
566 if (count & 2)
567 {
568 colorfunc(source[(frac >> FRACBITS) & mask], dest);
569 dest += pitch; frac += fracstep;
570 colorfunc(source[(frac >> FRACBITS) & mask], dest);
571 dest += pitch; frac += fracstep;
572 }
573
574 if (count & 4)
575 {
576 colorfunc(source[(frac >> FRACBITS) & mask], dest);
577 dest += pitch; frac += fracstep;
578 colorfunc(source[(frac >> FRACBITS) & mask], dest);
579 dest += pitch; frac += fracstep;
580 colorfunc(source[(frac >> FRACBITS) & mask], dest);
581 dest += pitch; frac += fracstep;
582 colorfunc(source[(frac >> FRACBITS) & mask], dest);
583 dest += pitch; frac += fracstep;
584 }
585 }
586 }
587
588
589 //
590 // R_FillSpanGeneric
591 //
592 // Templated version of a function to fill a span with a solid color.
593 // The data type of the destination pixels and a color-remapping functor
594 // are passed as template parameters.
595 //
596 template<typename PIXEL_T, typename COLORFUNC>
R_FillSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)597 static forceinline void R_FillSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
598 {
599 #ifdef RANGECHECK
600 if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
601 drawspan.y >= viewheight || drawspan.y < 0)
602 {
603 Printf(PRINT_HIGH, "R_FillSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
604 return;
605 }
606 #endif
607
608 int color = drawspan.color;
609 int colsize = drawspan.colsize;
610 int count = drawspan.x2 - drawspan.x1 + 1;
611 if (count <= 0)
612 return;
613
614 COLORFUNC colorfunc(drawspan);
615
616 do {
617 colorfunc(color, dest);
618 dest += colsize;
619 } while (--count);
620 }
621
622
623 //
624 // R_DrawLevelSpanGeneric
625 //
626 // Templated version of a function to fill a horizontal span with a texture map.
627 // The data type of the destination pixels and a color-remapping functor
628 // are passed as template parameters.
629 //
630 template<typename PIXEL_T, typename COLORFUNC>
R_DrawLevelSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)631 static forceinline void R_DrawLevelSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
632 {
633 #ifdef RANGECHECK
634 if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
635 drawspan.y >= viewheight || drawspan.y < 0)
636 {
637 Printf(PRINT_HIGH, "R_DrawLevelSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
638 return;
639 }
640 #endif
641
642 palindex_t* source = drawspan.source;
643 int colsize = drawspan.colsize;
644 int count = drawspan.x2 - drawspan.x1 + 1;
645 if (count <= 0)
646 return;
647
648 dsfixed_t xfrac = drawspan.xfrac;
649 dsfixed_t yfrac = drawspan.yfrac;
650 const dsfixed_t xstep = drawspan.xstep;
651 const dsfixed_t ystep = drawspan.ystep;
652
653 COLORFUNC colorfunc(drawspan);
654
655 do {
656 // Current texture index in u,v.
657 const int spot = ((yfrac >> (32-6-6)) & (63*64)) + (xfrac >> (32-6));
658
659 // Lookup pixel from flat texture tile,
660 // re-index using light/colormap.
661
662 colorfunc(source[spot], dest);
663 dest += colsize;
664
665 // Next step in u,v.
666 xfrac += xstep;
667 yfrac += ystep;
668 } while (--count);
669 }
670
671
672 //
673 // R_DrawSlopedSpanGeneric
674 //
675 // Texture maps a sloped surface using affine texturemapping for each row of
676 // the span. Not as pretty as a perfect texturemapping but should be much
677 // faster.
678 //
679 // Based on R_DrawSlope_8_64 from Eternity Engine, written by SoM/Quasar
680 //
681 // The data type of the destination pixels and a color-remapping functor
682 // are passed as template parameters.
683 //
684 template<typename PIXEL_T, typename COLORFUNC>
R_DrawSlopedSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)685 static forceinline void R_DrawSlopedSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
686 {
687 #ifdef RANGECHECK
688 if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
689 drawspan.y >= viewheight || drawspan.y < 0)
690 {
691 Printf(PRINT_HIGH, "R_DrawSlopedSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
692 return;
693 }
694 #endif
695
696 palindex_t* source = drawspan.source;
697 int colsize = drawspan.colsize;
698 int count = drawspan.x2 - drawspan.x1 + 1;
699 if (count <= 0)
700 return;
701
702 float iu = drawspan.iu, iv = drawspan.iv;
703 const float ius = drawspan.iustep, ivs = drawspan.ivstep;
704 float id = drawspan.id, ids = drawspan.idstep;
705
706 int ltindex = 0;
707
708 shaderef_t colormap;
709 COLORFUNC colorfunc(drawspan);
710
711 while (count >= SPANJUMP)
712 {
713 const float mulstart = 65536.0f / id;
714 id += ids * SPANJUMP;
715 const float mulend = 65536.0f / id;
716
717 const float ustart = iu * mulstart;
718 const float vstart = iv * mulstart;
719
720 fixed_t ufrac = (fixed_t)ustart;
721 fixed_t vfrac = (fixed_t)vstart;
722
723 iu += ius * SPANJUMP;
724 iv += ivs * SPANJUMP;
725
726 const float uend = iu * mulend;
727 const float vend = iv * mulend;
728
729 fixed_t ustep = (fixed_t)((uend - ustart) * INTERPSTEP);
730 fixed_t vstep = (fixed_t)((vend - vstart) * INTERPSTEP);
731
732 int incount = SPANJUMP;
733 while (incount--)
734 {
735 colormap = drawspan.slopelighting[ltindex++];
736
737 const int spot = ((vfrac >> 10) & 0xFC0) | ((ufrac >> 16) & 63);
738 colorfunc(source[spot], dest);
739 dest += colsize;
740 ufrac += ustep;
741 vfrac += vstep;
742 }
743
744 count -= SPANJUMP;
745 }
746
747 if (count > 0)
748 {
749 const float mulstart = 65536.0f / id;
750 id += ids * count;
751 const float mulend = 65536.0f / id;
752
753 const float ustart = iu * mulstart;
754 const float vstart = iv * mulstart;
755
756 fixed_t ufrac = (fixed_t)ustart;
757 fixed_t vfrac = (fixed_t)vstart;
758
759 iu += ius * count;
760 iv += ivs * count;
761
762 const float uend = iu * mulend;
763 const float vend = iv * mulend;
764
765 fixed_t ustep = (fixed_t)((uend - ustart) / count);
766 fixed_t vstep = (fixed_t)((vend - vstart) / count);
767
768 int incount = count;
769 while (incount--)
770 {
771 colormap = drawspan.slopelighting[ltindex++];
772
773 const int spot = ((vfrac >> 10) & 0xFC0) | ((ufrac >> 16) & 63);
774 colorfunc(source[spot], dest);
775 dest += colsize;
776 ufrac += ustep;
777 vfrac += vstep;
778 }
779 }
780 }
781
782
783 /************************************/
784 /* */
785 /* Palettized drawers (C versions) */
786 /* */
787 /************************************/
788
789 // ----------------------------------------------------------------------------
790 //
791 // 8bpp color remapping functors
792 //
793 // These functors provide a variety of ways to manipulate a source pixel
794 // color (given by 8bpp palette index) and write the result to the destination
795 // buffer.
796 //
797 // The functors are instantiated with a shaderef_t* parameter (typically
798 // dcol.colormap or ds_colormap) that will be used to shade the pixel.
799 //
800 // ----------------------------------------------------------------------------
801
802 class PaletteFunc
803 {
804 public:
PaletteFunc(const drawcolumn_t & drawcolumn)805 PaletteFunc(const drawcolumn_t& drawcolumn) { }
PaletteFunc(const drawspan_t & drawspan)806 PaletteFunc(const drawspan_t& drawspan) { }
807
operator ()(byte c,palindex_t * dest) const808 forceinline void operator()(byte c, palindex_t* dest) const
809 {
810 *dest = c;
811 }
812 };
813
814 class PaletteColormapFunc
815 {
816 public:
PaletteColormapFunc(const drawcolumn_t & drawcolumn)817 PaletteColormapFunc(const drawcolumn_t& drawcolumn) :
818 colormap(drawcolumn.colormap) { }
PaletteColormapFunc(const drawspan_t & drawspan)819 PaletteColormapFunc(const drawspan_t& drawspan) :
820 colormap(drawspan.colormap) { }
821
operator ()(byte c,palindex_t * dest) const822 forceinline void operator()(byte c, palindex_t* dest) const
823 {
824 *dest = colormap.index(c);
825 }
826
827 private:
828 const shaderef_t& colormap;
829 };
830
831 class PaletteFuzzyFunc
832 {
833 public:
PaletteFuzzyFunc(const drawcolumn_t & drawcolum)834 PaletteFuzzyFunc(const drawcolumn_t& drawcolum) :
835 colormap(&GetDefaultPalette()->maps, 6) { }
836
operator ()(byte c,palindex_t * dest) const837 forceinline void operator()(byte c, palindex_t* dest) const
838 {
839 *dest = colormap.index(dest[fuzzoffset[fuzzpos]]);
840 fuzzpos = (fuzzpos + 1) & (FUZZTABLE - 1);
841 }
842
843 private:
844 shaderef_t colormap;
845 };
846
847 class PaletteTranslucentColormapFunc
848 {
849 public:
PaletteTranslucentColormapFunc(const drawcolumn_t & drawcolumn)850 PaletteTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
851 colormap(drawcolumn.colormap)
852 {
853 calculate_alpha(drawcolumn.translevel);
854 }
855
PaletteTranslucentColormapFunc(const drawspan_t & drawspan)856 PaletteTranslucentColormapFunc(const drawspan_t& drawspan) :
857 colormap(drawspan.colormap)
858 {
859 calculate_alpha(drawspan.translevel);
860 }
861
operator ()(byte c,palindex_t * dest) const862 forceinline void operator()(byte c, palindex_t* dest) const
863 {
864 const palindex_t fg = colormap.index(c);
865 const palindex_t bg = *dest;
866
867 *dest = rt_blend2<palindex_t>(bg, bga, fg, fga);
868 }
869
870 private:
calculate_alpha(fixed_t translevel)871 void calculate_alpha(fixed_t translevel)
872 {
873 fga = (translevel & ~0x03FF) >> 8;
874 bga = 255 - fga;
875 }
876
877 const shaderef_t& colormap;
878 int fga, bga;
879 };
880
881 class PaletteTranslatedColormapFunc
882 {
883 public:
PaletteTranslatedColormapFunc(const drawcolumn_t & drawcolumn)884 PaletteTranslatedColormapFunc(const drawcolumn_t& drawcolumn) :
885 colormap(drawcolumn.colormap), translation(drawcolumn.translation) { }
886
operator ()(byte c,palindex_t * dest) const887 forceinline void operator()(byte c, palindex_t* dest) const
888 {
889 *dest = colormap.index(translation.tlate(c));
890 }
891
892 private:
893 const shaderef_t& colormap;
894 const translationref_t& translation;
895 };
896
897 class PaletteTranslatedTranslucentColormapFunc
898 {
899 public:
PaletteTranslatedTranslucentColormapFunc(const drawcolumn_t & drawcolumn)900 PaletteTranslatedTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
901 tlatefunc(drawcolumn), translation(drawcolumn.translation) { }
902
operator ()(byte c,palindex_t * dest) const903 forceinline void operator()(byte c, palindex_t* dest) const
904 {
905 tlatefunc(translation.tlate(c), dest);
906 }
907
908 private:
909 PaletteTranslucentColormapFunc tlatefunc;
910 const translationref_t& translation;
911 };
912
913 class PaletteSlopeColormapFunc
914 {
915 public:
PaletteSlopeColormapFunc(const drawspan_t & drawspan)916 PaletteSlopeColormapFunc(const drawspan_t& drawspan) :
917 colormap(drawspan.slopelighting) { }
918
operator ()(byte c,palindex_t * dest)919 forceinline void operator()(byte c, palindex_t* dest)
920 {
921 *dest = colormap->index(c);
922 colormap++;
923 }
924
925 private:
926 const shaderef_t* colormap;
927 };
928
929
930 // ----------------------------------------------------------------------------
931 //
932 // 8bpp color column drawing wrappers
933 //
934 // ----------------------------------------------------------------------------
935
936 #define FB_COLDEST_P ((palindex_t*)(ylookup[dcol.yl] + columnofs[dcol.x]))
937
938 //
939 // R_FillColumnP
940 //
941 // Fills a column in the 8bpp palettized screen buffer with a solid color,
942 // determined by dcol.color. Performs no shading.
943 //
R_FillColumnP()944 void R_FillColumnP()
945 {
946 R_FillColumnGeneric<palindex_t, PaletteFunc>(FB_COLDEST_P, dcol);
947 }
948
949 //
950 // R_DrawColumnP
951 //
952 // Renders a column to the 8bpp palettized screen buffer from the source buffer
953 // dcol.source and scaled by dcol.iscale. Shading is performed using dcol.colormap.
954 //
R_DrawColumnP()955 void R_DrawColumnP()
956 {
957 R_DrawColumnGeneric<palindex_t, PaletteColormapFunc>(FB_COLDEST_P, dcol);
958 }
959
960 //
961 // R_StretchColumnP
962 //
963 // Renders a column to the 8bpp palettized screen buffer from the source buffer
964 // dcol.source and scaled by dcol.iscale. Performs no shading.
965 //
R_StretchColumnP()966 void R_StretchColumnP()
967 {
968 R_DrawColumnGeneric<palindex_t, PaletteFunc>(FB_COLDEST_P, dcol);
969 }
970
971 //
972 // R_DrawFuzzColumnP
973 //
974 // Alters a column in the 8bpp palettized screen buffer using Doom's partial
975 // invisibility effect, which shades the column and rearranges the ordering
976 // the pixels to create distortion. Shading is performed using colormap 6.
977 //
R_DrawFuzzColumnP()978 void R_DrawFuzzColumnP()
979 {
980 // adjust the borders (prevent buffer over/under-reads)
981 if (dcol.yl <= 0)
982 dcol.yl = 1;
983 if (dcol.yh >= viewheight - 1)
984 dcol.yh = viewheight - 2;
985
986 R_FillColumnGeneric<palindex_t, PaletteFuzzyFunc>(FB_COLDEST_P, dcol);
987
988 fuzzpos = (fuzzpos + 3) & (FUZZTABLE - 1);
989 }
990
991 //
992 // R_DrawTranslucentColumnP
993 //
994 // Renders a translucent column to the 8bpp palettized screen buffer from the
995 // source buffer dcol.source and scaled by dcol.iscale. The amount of
996 // translucency is controlled by dcol.translevel. Shading is performed using
997 // dcol.colormap.
998 //
R_DrawTranslucentColumnP()999 void R_DrawTranslucentColumnP()
1000 {
1001 R_DrawColumnGeneric<palindex_t, PaletteTranslucentColormapFunc>(FB_COLDEST_P, dcol);
1002 }
1003
1004 //
1005 // R_DrawTranslatedColumnP
1006 //
1007 // Renders a column to the 8bpp palettized screen buffer with color-remapping
1008 // from the source buffer dcol.source and scaled by dcol.iscale. The translation
1009 // table is supplied by dcol.translation. Shading is performed using dcol.colormap.
1010 //
R_DrawTranslatedColumnP()1011 void R_DrawTranslatedColumnP()
1012 {
1013 R_DrawColumnGeneric<palindex_t, PaletteTranslatedColormapFunc>(FB_COLDEST_P, dcol);
1014 }
1015
1016 //
1017 // R_DrawTlatedLucentColumnP
1018 //
1019 // Renders a translucent column to the 8bpp palettized screen buffer with
1020 // color-remapping from the source buffer dcol.source and scaled by dcol.iscale.
1021 // The translation table is supplied by dcol.translation and the amount of
1022 // translucency is controlled by dcol.translevel. Shading is performed using
1023 // dcol.colormap.
1024 //
R_DrawTlatedLucentColumnP()1025 void R_DrawTlatedLucentColumnP()
1026 {
1027 R_DrawColumnGeneric<palindex_t, PaletteTranslatedTranslucentColormapFunc>(FB_COLDEST_P, dcol);
1028 }
1029
1030 //
1031 // R_FillColumnHorizP
1032 //
1033 // Fills a column in an 8bpp palettized buffer dcol.temp with a solid color,
1034 // determined by dcol.color. Performs no shading.
1035 //
R_FillColumnHorizP()1036 void R_FillColumnHorizP()
1037 {
1038 if (dcol.yl > dcol.yh)
1039 return;
1040
1041 const int x = dcol.x & 3;
1042 unsigned int **span = &dc_ctspan[x];
1043
1044 (*span)[0] = dcol.yl;
1045 (*span)[1] = dcol.yh;
1046 *span += 2;
1047 palindex_t* dest = &dc_temp[x + 4*dcol.yl];
1048
1049 int oldpitch = dcol.pitch;
1050 dcol.pitch = 4;
1051
1052 R_FillColumnGeneric<palindex_t, PaletteFunc>(dest, dcol);
1053
1054 dcol.pitch = oldpitch;
1055 }
1056
1057 //
1058 // R_DrawColumnHorizP
1059 //
1060 // Renders a column to an 8bpp palettized buffer dcol.temp from the source buffer
1061 // dcol.source and scaled by dcol.iscale. The column is rendered to the buffer in
1062 // an interleaved format, writing to every 4th byte of the buffer. Performs
1063 // no shading.
1064 //
R_DrawColumnHorizP()1065 void R_DrawColumnHorizP()
1066 {
1067 if (dcol.yl > dcol.yh)
1068 return;
1069
1070 const int x = dcol.x & 3;
1071 unsigned int **span = &dc_ctspan[x];
1072
1073 (*span)[0] = dcol.yl;
1074 (*span)[1] = dcol.yh;
1075 *span += 2;
1076 palindex_t* dest = &dc_temp[x + 4*dcol.yl];
1077
1078 int oldpitch = dcol.pitch;
1079 dcol.pitch = 4;
1080
1081 R_DrawColumnGeneric<palindex_t, PaletteFunc>(dest, dcol);
1082
1083 dcol.pitch = oldpitch;
1084 }
1085
1086
1087 // ----------------------------------------------------------------------------
1088 //
1089 // 8bpp color span drawing wrappers
1090 //
1091 // ----------------------------------------------------------------------------
1092
1093 #define FB_SPANDEST_P ((palindex_t*)(ylookup[dspan.y] + columnofs[dspan.x1]))
1094
1095 //
1096 // R_FillSpanP
1097 //
1098 // Fills a span in the 8bpp palettized screen buffer with a solid color,
1099 // determined by ds_color. Performs no shading.
1100 //
R_FillSpanP()1101 void R_FillSpanP()
1102 {
1103 R_FillSpanGeneric<palindex_t, PaletteFunc>(FB_SPANDEST_P, dspan);
1104 }
1105
1106 //
1107 // R_FillTranslucentSpanP
1108 //
1109 // Fills a span in the 8bpp palettized screen buffer with a solid color,
1110 // determined by ds_color using translucency. Shading is performed
1111 // using ds_colormap.
1112 //
R_FillTranslucentSpanP()1113 void R_FillTranslucentSpanP()
1114 {
1115 R_FillSpanGeneric<palindex_t, PaletteTranslucentColormapFunc>(FB_SPANDEST_P, dspan);
1116 }
1117
1118 //
1119 // R_DrawSpanP
1120 //
1121 // Renders a span for a level plane to the 8bpp palettized screen buffer from
1122 // the source buffer ds_source. Shading is performed using ds_colormap.
1123 //
R_DrawSpanP()1124 void R_DrawSpanP()
1125 {
1126 R_DrawLevelSpanGeneric<palindex_t, PaletteColormapFunc>(FB_SPANDEST_P, dspan);
1127 }
1128
1129 //
1130 // R_DrawSlopeSpanP
1131 //
1132 // Renders a span for a sloped plane to the 8bpp palettized screen buffer from
1133 // the source buffer ds_source. Shading is performed using ds_colormap.
1134 //
R_DrawSlopeSpanP()1135 void R_DrawSlopeSpanP()
1136 {
1137 R_DrawSlopedSpanGeneric<palindex_t, PaletteSlopeColormapFunc>(FB_SPANDEST_P, dspan);
1138 }
1139
1140
1141 /****************************************/
1142 /* */
1143 /* [RH] ARGB8888 drawers (C versions) */
1144 /* */
1145 /****************************************/
1146
1147 // ----------------------------------------------------------------------------
1148 //
1149 // 32bpp color remapping functors
1150 //
1151 // These functors provide a variety of ways to manipulate a source pixel
1152 // color (given by 8bpp palette index) and write the result to the destination
1153 // buffer.
1154 //
1155 // The functors are instantiated with a shaderef_t* parameter (typically
1156 // dcol.colormap or ds_colormap) that will be used to shade the pixel.
1157 //
1158 // ----------------------------------------------------------------------------
1159
1160 class DirectFunc
1161 {
1162 public:
DirectFunc(const drawcolumn_t & drawcolumn)1163 DirectFunc(const drawcolumn_t& drawcolumn) { }
DirectFunc(const drawspan_t & drawspan)1164 DirectFunc(const drawspan_t& drawspan) { }
1165
operator ()(byte c,argb_t * dest) const1166 forceinline void operator()(byte c, argb_t* dest) const
1167 {
1168 *dest = basecolormap.shade(c);
1169 }
1170 };
1171
1172 class DirectColormapFunc
1173 {
1174 public:
DirectColormapFunc(const drawcolumn_t & drawcolumn)1175 DirectColormapFunc(const drawcolumn_t& drawcolumn) :
1176 colormap(drawcolumn.colormap) { }
DirectColormapFunc(const drawspan_t & drawspan)1177 DirectColormapFunc(const drawspan_t& drawspan) :
1178 colormap(drawspan.colormap) { }
1179
operator ()(byte c,argb_t * dest) const1180 forceinline void operator()(byte c, argb_t* dest) const
1181 {
1182 *dest = colormap.shade(c);
1183 }
1184
1185 private:
1186 const shaderef_t& colormap;
1187 };
1188
1189 class DirectFuzzyFunc
1190 {
1191 public:
DirectFuzzyFunc(const drawcolumn_t & drawcolumn)1192 DirectFuzzyFunc(const drawcolumn_t& drawcolumn) { }
1193
operator ()(byte c,argb_t * dest) const1194 forceinline void operator()(byte c, argb_t* dest) const
1195 {
1196 argb_t work = dest[fuzzoffset[fuzzpos] >> 2];
1197 *dest = work - ((work >> 2) & 0x3f3f3f);
1198 fuzzpos = (fuzzpos + 1) & (FUZZTABLE - 1);
1199 }
1200 };
1201
1202 class DirectTranslucentColormapFunc
1203 {
1204 public:
DirectTranslucentColormapFunc(const drawcolumn_t & drawcolumn)1205 DirectTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
1206 colormap(drawcolumn.colormap)
1207 {
1208 calculate_alpha(drawcolumn.translevel);
1209 }
1210
DirectTranslucentColormapFunc(const drawspan_t & drawspan)1211 DirectTranslucentColormapFunc(const drawspan_t& drawspan) :
1212 colormap(drawspan.colormap)
1213 {
1214 calculate_alpha(drawspan.translevel);
1215 }
1216
operator ()(byte c,argb_t * dest) const1217 forceinline void operator()(byte c, argb_t* dest) const
1218 {
1219 argb_t fg = colormap.shade(c);
1220 argb_t bg = *dest;
1221 *dest = alphablend2a(bg, bga, fg, fga);
1222 }
1223
1224 private:
calculate_alpha(fixed_t translevel)1225 void calculate_alpha(fixed_t translevel)
1226 {
1227 fga = (translevel & ~0x03FF) >> 8;
1228 bga = 255 - fga;
1229 }
1230
1231 const shaderef_t& colormap;
1232 int fga, bga;
1233 };
1234
1235 class DirectTranslatedColormapFunc
1236 {
1237 public:
DirectTranslatedColormapFunc(const drawcolumn_t & drawcolumn)1238 DirectTranslatedColormapFunc(const drawcolumn_t& drawcolumn) :
1239 colormap(drawcolumn.colormap), translation(drawcolumn.translation) { }
1240
operator ()(byte c,argb_t * dest) const1241 forceinline void operator()(byte c, argb_t* dest) const
1242 {
1243 *dest = colormap.tlate(translation, c);
1244 }
1245
1246 private:
1247 const shaderef_t& colormap;
1248 const translationref_t& translation;
1249 };
1250
1251 class DirectTranslatedTranslucentColormapFunc
1252 {
1253 public:
DirectTranslatedTranslucentColormapFunc(const drawcolumn_t & drawcolumn)1254 DirectTranslatedTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
1255 tlatefunc(drawcolumn), translation(drawcolumn.translation) { }
1256
operator ()(byte c,argb_t * dest) const1257 forceinline void operator()(byte c, argb_t* dest) const
1258 {
1259 tlatefunc(translation.tlate(c), dest);
1260 }
1261
1262 private:
1263 DirectTranslucentColormapFunc tlatefunc;
1264 const translationref_t& translation;
1265 };
1266
1267 class DirectSlopeColormapFunc
1268 {
1269 public:
DirectSlopeColormapFunc(const drawspan_t & drawspan)1270 DirectSlopeColormapFunc(const drawspan_t& drawspan) :
1271 colormap(drawspan.slopelighting) { }
1272
operator ()(byte c,argb_t * dest)1273 forceinline void operator()(byte c, argb_t* dest)
1274 {
1275 *dest = colormap->shade(c);
1276 colormap++;
1277 }
1278
1279 private:
1280 const shaderef_t* colormap;
1281 };
1282
1283
1284 // ----------------------------------------------------------------------------
1285 //
1286 // 32bpp color drawing wrappers
1287 //
1288 // ----------------------------------------------------------------------------
1289
1290 #define FB_COLDEST_D ((argb_t*)(ylookup[dcol.yl] + columnofs[dcol.x]))
1291
1292 //
1293 // R_FillColumnD
1294 //
1295 // Fills a column in the 32bpp ARGB8888 screen buffer with a solid color,
1296 // determined by dcol.color. Performs no shading.
1297 //
R_FillColumnD()1298 void R_FillColumnD()
1299 {
1300 R_FillColumnGeneric<argb_t, DirectFunc>(FB_COLDEST_D, dcol);
1301 }
1302
1303 //
1304 // R_DrawColumnD
1305 //
1306 // Renders a column to the 32bpp ARGB8888 screen buffer from the source buffer
1307 // dcol.source and scaled by dcol.iscale. Shading is performed using dcol.colormap.
1308 //
R_DrawColumnD()1309 void R_DrawColumnD()
1310 {
1311 R_DrawColumnGeneric<argb_t, DirectColormapFunc>(FB_COLDEST_D, dcol);
1312 }
1313
1314 //
1315 // R_DrawFuzzColumnD
1316 //
1317 // Alters a column in the 32bpp ARGB8888 screen buffer using Doom's partial
1318 // invisibility effect, which shades the column and rearranges the ordering
1319 // the pixels to create distortion. Shading is performed using colormap 6.
1320 //
R_DrawFuzzColumnD()1321 void R_DrawFuzzColumnD()
1322 {
1323 // adjust the borders (prevent buffer over/under-reads)
1324 if (dcol.yl <= 0)
1325 dcol.yl = 1;
1326 if (dcol.yh >= viewheight - 1)
1327 dcol.yh = viewheight - 2;
1328
1329 R_FillColumnGeneric<argb_t, DirectFuzzyFunc>(FB_COLDEST_D, dcol);
1330
1331 fuzzpos = (fuzzpos + 3) & (FUZZTABLE - 1);
1332 }
1333
1334 //
1335 // R_DrawTranslucentColumnD
1336 //
1337 // Renders a translucent column to the 32bpp ARGB8888 screen buffer from the
1338 // source buffer dcol.source and scaled by dcol.iscale. The amount of
1339 // translucency is controlled by dcol.translevel. Shading is performed using
1340 // dcol.colormap.
1341 //
R_DrawTranslucentColumnD()1342 void R_DrawTranslucentColumnD()
1343 {
1344 R_DrawColumnGeneric<argb_t, DirectTranslucentColormapFunc>(FB_COLDEST_D, dcol);
1345 }
1346
1347 //
1348 // R_DrawTranslatedColumnD
1349 //
1350 // Renders a column to the 32bpp ARGB8888 screen buffer with color-remapping
1351 // from the source buffer dcol.source and scaled by dcol.iscale. The translation
1352 // table is supplied by dcol.translation. Shading is performed using dcol.colormap.
1353 //
R_DrawTranslatedColumnD()1354 void R_DrawTranslatedColumnD()
1355 {
1356 R_DrawColumnGeneric<argb_t, DirectTranslatedColormapFunc>(FB_COLDEST_D, dcol);
1357 }
1358
1359 //
1360 // R_DrawTlatedLucentColumnD
1361 //
1362 // Renders a translucent column to the 32bpp ARGB8888 screen buffer with
1363 // color-remapping from the source buffer dcol.source and scaled by dcol.iscale.
1364 // The translation table is supplied by dcol.translation and the amount of
1365 // translucency is controlled by dcol.translevel. Shading is performed using
1366 // dcol.colormap.
1367 //
R_DrawTlatedLucentColumnD()1368 void R_DrawTlatedLucentColumnD()
1369 {
1370 R_DrawColumnGeneric<argb_t, DirectTranslatedTranslucentColormapFunc>(FB_COLDEST_D, dcol);
1371 }
1372
1373
1374 // ----------------------------------------------------------------------------
1375 //
1376 // 32bpp color span drawing wrappers
1377 //
1378 // ----------------------------------------------------------------------------
1379
1380 #define FB_SPANDEST_D ((argb_t*)(ylookup[dspan.y] + columnofs[dspan.x1]))
1381
1382 //
1383 // R_FillSpanD
1384 //
1385 // Fills a span in the 32bpp ARGB8888 screen buffer with a solid color,
1386 // determined by ds_color. Performs no shading.
1387 //
R_FillSpanD()1388 void R_FillSpanD()
1389 {
1390 R_FillSpanGeneric<argb_t, DirectFunc>(FB_SPANDEST_D, dspan);
1391 }
1392
1393 //
1394 // R_FillTranslucentSpanD
1395 //
1396 // Fills a span in the 32bpp ARGB8888 screen buffer with a solid color,
1397 // determined by ds_color using translucency. Shading is performed
1398 // using ds_colormap.
1399 //
R_FillTranslucentSpanD()1400 void R_FillTranslucentSpanD()
1401 {
1402 R_FillSpanGeneric<argb_t, DirectTranslucentColormapFunc>(FB_SPANDEST_D, dspan);
1403 }
1404
1405 //
1406 // R_DrawSpanD
1407 //
1408 // Renders a span for a level plane to the 32bpp ARGB8888 screen buffer from
1409 // the source buffer ds_source. Shading is performed using ds_colormap.
1410 //
R_DrawSpanD_c()1411 void R_DrawSpanD_c()
1412 {
1413 R_DrawLevelSpanGeneric<argb_t, DirectColormapFunc>(FB_SPANDEST_D, dspan);
1414 }
1415
1416 //
1417 // R_DrawSlopeSpanD
1418 //
1419 // Renders a span for a sloped plane to the 32bpp ARGB8888 screen buffer from
1420 // the source buffer ds_source. Shading is performed using ds_colormap.
1421 //
R_DrawSlopeSpanD_c()1422 void R_DrawSlopeSpanD_c()
1423 {
1424 R_DrawSlopedSpanGeneric<argb_t, DirectSlopeColormapFunc>(FB_SPANDEST_D, dspan);
1425 }
1426
1427
1428 /****************************************************/
1429
1430 //
1431 // R_InitBuffer
1432 // Creats lookup tables that avoid
1433 // multiplies and other hazzles
1434 // for getting the framebuffer address
1435 // of a pixel to draw.
1436 //
R_InitBuffer(int width,int height)1437 void R_InitBuffer(int width, int height)
1438 {
1439 int i;
1440 byte *buffer;
1441 int pitch;
1442 int xshift;
1443
1444 int windowwidth = width << detailxshift;
1445 int windowheight = height << detailyshift;
1446
1447 // Handle resize,
1448 // e.g. smaller view windows
1449 // with border and/or status bar.
1450 viewwindowx = (screen->width - windowwidth) >> 1;
1451
1452 // [RH] Adjust column offset according to bytes per pixel
1453 // and detail mode
1454 xshift = detailxshift + (screen->is8bit() ? 0 : 2);
1455
1456 // Column offset. For windows
1457 for (i = 0; i < width; i++)
1458 columnofs[i] = (viewwindowx + i) << xshift;
1459
1460 // Same with base row offset.
1461 if (windowwidth == screen->width)
1462 viewwindowy = 0;
1463 else
1464 viewwindowy = (ST_Y - windowheight) >> 1;
1465
1466 screen->Lock();
1467 buffer = screen->buffer;
1468 pitch = screen->pitch;
1469 screen->Unlock();
1470
1471 // Precalculate all row offsets.
1472 for (i = 0; i < height; i++)
1473 ylookup[i] = buffer + ((i << detailyshift) + viewwindowy) * pitch;
1474 }
1475
1476
R_DrawBorder(int x1,int y1,int x2,int y2)1477 void R_DrawBorder (int x1, int y1, int x2, int y2)
1478 {
1479 int lump;
1480
1481 lump = W_CheckNumForName (gameinfo.borderFlat, ns_flats);
1482 if (lump >= 0)
1483 {
1484 screen->FlatFill (x1 & ~63, y1, x2, y2,
1485 (byte *)W_CacheLumpNum (lump, PU_CACHE));
1486 }
1487 else
1488 {
1489 screen->Clear (x1, y1, x2, y2, 0);
1490 }
1491 }
1492
1493
1494 //
1495 // R_DrawViewBorder
1496 // Draws the border around the view
1497 // for different size windows?
1498 //
1499 void V_MarkRect (int x, int y, int width, int height);
1500
R_DrawViewBorder(void)1501 void R_DrawViewBorder (void)
1502 {
1503 int x, y;
1504 int offset, size;
1505 gameborder_t *border;
1506
1507 if (realviewwidth == screen->width) {
1508 return;
1509 }
1510
1511 border = gameinfo.border;
1512 offset = border->offset;
1513 size = border->size;
1514
1515 R_DrawBorder (0, 0, screen->width, viewwindowy);
1516 R_DrawBorder (0, viewwindowy, viewwindowx, realviewheight + viewwindowy);
1517 R_DrawBorder (viewwindowx + realviewwidth, viewwindowy, screen->width, realviewheight + viewwindowy);
1518 R_DrawBorder (0, viewwindowy + realviewheight, screen->width, ST_Y);
1519
1520 for (x = viewwindowx; x < viewwindowx + realviewwidth; x += size)
1521 {
1522 screen->DrawPatch (W_CachePatch (border->t),
1523 x, viewwindowy - offset);
1524 screen->DrawPatch (W_CachePatch (border->b),
1525 x, viewwindowy + realviewheight);
1526 }
1527 for (y = viewwindowy; y < viewwindowy + realviewheight; y += size)
1528 {
1529 screen->DrawPatch (W_CachePatch (border->l),
1530 viewwindowx - offset, y);
1531 screen->DrawPatch (W_CachePatch (border->r),
1532 viewwindowx + realviewwidth, y);
1533 }
1534 // Draw beveled edge.
1535 screen->DrawPatch (W_CachePatch (border->tl),
1536 viewwindowx-offset, viewwindowy-offset);
1537
1538 screen->DrawPatch (W_CachePatch (border->tr),
1539 viewwindowx+realviewwidth, viewwindowy-offset);
1540
1541 screen->DrawPatch (W_CachePatch (border->bl),
1542 viewwindowx-offset, viewwindowy+realviewheight);
1543
1544 screen->DrawPatch (W_CachePatch (border->br),
1545 viewwindowx+realviewwidth, viewwindowy+realviewheight);
1546
1547 V_MarkRect (0, 0, screen->width, ST_Y);
1548 }
1549
1550 // ============================================================================
1551 //
1552 // Horizontal and vertical pixel doubling functions
1553 //
1554 // ============================================================================
1555
R_DoubleX8()1556 static void R_DoubleX8()
1557 {
1558 int rowsize = realviewwidth >> 2;
1559 int pitch = screen->pitch >> (2 - detailyshift);
1560
1561 unsigned int* line = (unsigned int*)(screen->buffer + viewwindowy * screen->pitch + viewwindowx);
1562 for (int y = 0; y < viewheight; y++, line += pitch)
1563 {
1564 for (int x = 0; x < rowsize; x += 2)
1565 {
1566 unsigned int a = line[x + 0];
1567 unsigned int b = line[x + 1];
1568 a &= 0x00ff00ff;
1569 b &= 0x00ff00ff;
1570 line[x + 0] = a | (a << 8);
1571 line[x + 1] = b | (b << 8);
1572 }
1573 }
1574 }
1575
R_DoubleX32()1576 static void R_DoubleX32()
1577 {
1578 int rowsize = realviewwidth;
1579 int pitch = screen->pitch >> (2 - detailyshift);
1580
1581 argb_t* line = (argb_t*)(screen->buffer + viewwindowy * screen->pitch + viewwindowx);
1582 for (int y = 0; y < viewheight; y++, line += pitch)
1583 {
1584 for (int x = 0; x < rowsize; x += 2)
1585 line[x + 1] = line[x];
1586 }
1587 }
1588
R_DoubleY8()1589 static void R_DoubleY8()
1590 {
1591 int rowsize = realviewwidth;
1592 int pitch = screen->pitch;
1593
1594 byte* line = screen->buffer + viewwindowy * pitch + viewwindowx;
1595
1596 for (int y = 0; y < viewheight; y++, line += pitch << 1)
1597 memcpy(line + pitch, line, rowsize);
1598 }
1599
R_DoubleY32()1600 static void R_DoubleY32()
1601 {
1602 int rowsize = realviewwidth << 2;
1603 int pitch = screen->pitch;
1604
1605 byte* line = screen->buffer + viewwindowy * pitch + viewwindowx;
1606
1607 for (int y = 0; y < viewheight; y++, line += pitch << 1)
1608 memcpy(line + pitch, line, rowsize);
1609 }
1610
1611
1612 // [RH] Double pixels in the view window horizontally
1613 // and/or vertically (or not at all).
R_DetailDouble(void)1614 void R_DetailDouble (void)
1615 {
1616 if (screen->is8bit())
1617 {
1618 if (detailxshift)
1619 R_DoubleX8();
1620 if (detailyshift)
1621 R_DoubleY8();
1622 }
1623 else
1624 {
1625 if (detailxshift)
1626 R_DoubleX32();
1627 if (detailyshift)
1628 R_DoubleY32();
1629 }
1630 }
1631
1632
1633 enum r_optimize_kind {
1634 OPTIMIZE_NONE,
1635 OPTIMIZE_SSE2,
1636 OPTIMIZE_MMX,
1637 OPTIMIZE_ALTIVEC
1638 };
1639
1640 static r_optimize_kind optimize_kind = OPTIMIZE_NONE;
1641 static std::vector<r_optimize_kind> optimizations_available;
1642
get_optimization_name(r_optimize_kind kind)1643 static const char *get_optimization_name(r_optimize_kind kind)
1644 {
1645 switch (kind)
1646 {
1647 case OPTIMIZE_SSE2: return "sse2";
1648 case OPTIMIZE_MMX: return "mmx";
1649 case OPTIMIZE_ALTIVEC: return "altivec";
1650 case OPTIMIZE_NONE:
1651 default:
1652 return "none";
1653 }
1654 }
1655
get_optimization_name_list(const bool includeNone)1656 static std::string get_optimization_name_list(const bool includeNone)
1657 {
1658 std::string str;
1659 std::vector<r_optimize_kind>::const_iterator it = optimizations_available.begin();
1660 if (!includeNone)
1661 ++it;
1662
1663 for (; it != optimizations_available.end(); ++it)
1664 {
1665 str.append(get_optimization_name(*it));
1666 if (it+1 != optimizations_available.end())
1667 str.append(", ");
1668 }
1669 return str;
1670 }
1671
print_optimizations()1672 static void print_optimizations()
1673 {
1674 Printf(PRINT_HIGH, "r_optimize detected \"%s\"\n", get_optimization_name_list(false).c_str());
1675 }
1676
detect_optimizations()1677 static bool detect_optimizations()
1678 {
1679 if (!optimizations_available.empty())
1680 return false;
1681
1682 optimizations_available.clear();
1683
1684 // Start with default non-optimized:
1685 optimizations_available.push_back(OPTIMIZE_NONE);
1686
1687 // Detect CPU features in ascending order of preference:
1688 #ifdef __MMX__
1689 #ifndef _XBOX // Until SDLx is updated
1690 if (SDL_HasMMX())
1691 #endif
1692 optimizations_available.push_back(OPTIMIZE_MMX);
1693 #endif
1694 #ifdef __SSE2__
1695 if (SDL_HasSSE2())
1696 optimizations_available.push_back(OPTIMIZE_SSE2);
1697 #endif
1698 #ifdef __ALTIVEC__
1699 if (SDL_HasAltiVec())
1700 optimizations_available.push_back(OPTIMIZE_ALTIVEC);
1701 #endif
1702
1703 return true;
1704 }
1705
1706 //
1707 // R_IsOptimizationAvailable
1708 //
1709 // Returns true if Odamex was compiled with support for the optimization
1710 // and the current CPU also supports it.
1711 //
R_IsOptimizationAvailable(r_optimize_kind kind)1712 static bool R_IsOptimizationAvailable(r_optimize_kind kind)
1713 {
1714 return std::find(optimizations_available.begin(), optimizations_available.end(), kind)
1715 != optimizations_available.end();
1716 }
1717
1718
CVAR_FUNC_IMPL(r_optimize)1719 CVAR_FUNC_IMPL(r_optimize)
1720 {
1721 const char* val = var.cstring();
1722
1723 // Only print the detected list the first time:
1724 if (detect_optimizations())
1725 print_optimizations();
1726
1727 // Set the optimization based on availability:
1728 if (stricmp(val, "none") == 0)
1729 optimize_kind = OPTIMIZE_NONE;
1730 else if (stricmp(val, "sse2") == 0 && R_IsOptimizationAvailable(OPTIMIZE_SSE2))
1731 optimize_kind = OPTIMIZE_SSE2;
1732 else if (stricmp(val, "mmx") == 0 && R_IsOptimizationAvailable(OPTIMIZE_MMX))
1733 optimize_kind = OPTIMIZE_MMX;
1734 else if (stricmp(val, "altivec") == 0 && R_IsOptimizationAvailable(OPTIMIZE_ALTIVEC))
1735 optimize_kind = OPTIMIZE_ALTIVEC;
1736 else if (stricmp(val, "detect") == 0)
1737 // Default to the most preferred:
1738 optimize_kind = optimizations_available.back();
1739 else
1740 {
1741 Printf(PRINT_HIGH, "Invalid value for r_optimize. Availible options are \"%s, detect\"\n",
1742 get_optimization_name_list(true).c_str());
1743
1744 // Restore the original setting:
1745 var.Set(get_optimization_name(optimize_kind));
1746 return;
1747 }
1748
1749 const char* optimize_name = get_optimization_name(optimize_kind);
1750 if (stricmp(val, optimize_name) != 0)
1751 {
1752 // update the cvar string
1753 // this will trigger the callback to run a second time
1754 Printf(PRINT_HIGH, "r_optimize set to \"%s\" based on availability\n", optimize_name);
1755 var.Set(optimize_name);
1756 }
1757 else
1758 {
1759 // cvar string is current, now intialize the drawing function pointers
1760 R_InitVectorizedDrawers();
1761 R_InitColumnDrawers();
1762 }
1763 }
1764
1765
1766 //
1767 // R_InitVectorizedDrawers
1768 //
1769 // Sets up the function pointers based on CPU optimization selected.
1770 //
R_InitVectorizedDrawers()1771 void R_InitVectorizedDrawers()
1772 {
1773 if (optimize_kind == OPTIMIZE_NONE)
1774 {
1775 // [SL] set defaults to non-vectorized drawers
1776 rtv_lucent4colsP = rtv_lucent4cols_c;
1777 rtv_lucent4colsD = rtv_lucent4cols_c;
1778 R_DrawSpanD = R_DrawSpanD_c;
1779 R_DrawSlopeSpanD = R_DrawSlopeSpanD_c;
1780 r_dimpatchD = r_dimpatchD_c;
1781 }
1782 #ifdef __SSE2__
1783 if (optimize_kind == OPTIMIZE_SSE2)
1784 {
1785 rtv_lucent4colsP = rtv_lucent4cols_SSE2;
1786 rtv_lucent4colsD = rtv_lucent4cols_SSE2;
1787 R_DrawSpanD = R_DrawSpanD_SSE2;
1788 R_DrawSlopeSpanD = R_DrawSlopeSpanD_SSE2;
1789 r_dimpatchD = r_dimpatchD_SSE2;
1790 }
1791 #endif
1792 #ifdef __MMX__
1793 else if (optimize_kind == OPTIMIZE_MMX)
1794 {
1795 rtv_lucent4colsP = rtv_lucent4cols_MMX;
1796 rtv_lucent4colsD = rtv_lucent4cols_MMX;
1797 R_DrawSpanD = R_DrawSpanD_c; // TODO
1798 R_DrawSlopeSpanD = R_DrawSlopeSpanD_c; // TODO
1799 r_dimpatchD = r_dimpatchD_MMX;
1800 }
1801 #endif
1802 #ifdef __ALTIVEC__
1803 else if (optimize_kind == OPTIMIZE_ALTIVEC)
1804 {
1805 rtv_lucent4colsP = rtv_lucent4cols_c; // TODO
1806 rtv_lucent4colsD = rtv_lucent4cols_c; // TODO
1807 R_DrawSpanD = R_DrawSpanD_c; // TODO
1808 R_DrawSlopeSpanD = R_DrawSlopeSpanD_c; // TODO
1809 r_dimpatchD = r_dimpatchD_ALTIVEC;
1810 }
1811 #endif
1812
1813 // Check that all pointers are definitely assigned!
1814 assert(rtv_lucent4colsP != NULL);
1815 assert(rtv_lucent4colsD != NULL);
1816 assert(R_DrawSpanD != NULL);
1817 assert(R_DrawSlopeSpanD != NULL);
1818 assert(r_dimpatchD != NULL);
1819 }
1820
1821 // [RH] Initialize the column drawer pointers
R_InitColumnDrawers()1822 void R_InitColumnDrawers ()
1823 {
1824 if (!screen)
1825 return;
1826
1827 // NOTE(jsd): It's okay to use R_DrawColumnHorizP because it renders to a temp buffer first.
1828 R_DrawColumnHoriz = R_DrawColumnHorizP;
1829
1830 if (screen->is8bit())
1831 {
1832 R_DrawColumn = R_DrawColumnP;
1833 R_DrawFuzzColumn = R_DrawFuzzColumnP;
1834 R_DrawTranslucentColumn = R_DrawTranslucentColumnP;
1835 R_DrawTranslatedColumn = R_DrawTranslatedColumnP;
1836 R_DrawSlopeSpan = R_DrawSlopeSpanP;
1837 R_DrawSpan = R_DrawSpanP;
1838 R_FillColumn = R_FillColumnP;
1839 R_FillSpan = R_FillSpanP;
1840 R_FillTranslucentSpan = R_FillTranslucentSpanP;
1841
1842 rt_copy1col = rt_copy1colP;
1843 rt_copy4cols = rt_copy4colsP;
1844 rt_map1col = rt_map1colP;
1845 rt_map4cols = rt_map4colsP;
1846 rt_lucent1col = rt_lucent1colP;
1847 rt_lucent4cols = rt_lucent4colsP;
1848 rt_tlate1col = rt_tlate1colP;
1849 rt_tlate4cols = rt_tlate4colsP;
1850 rt_tlatelucent1col = rt_tlatelucent1colP;
1851 rt_tlatelucent4cols = rt_tlatelucent4colsP;
1852 }
1853 else
1854 {
1855 // 32bpp rendering functions:
1856 R_DrawColumn = R_DrawColumnD;
1857 R_DrawFuzzColumn = R_DrawFuzzColumnD;
1858 R_DrawTranslucentColumn = R_DrawTranslucentColumnD;
1859 R_DrawTranslatedColumn = R_DrawTranslatedColumnD;
1860 R_DrawSlopeSpan = R_DrawSlopeSpanD;
1861 R_DrawSpan = R_DrawSpanD;
1862 R_FillColumn = R_FillColumnD;
1863 R_FillSpan = R_FillSpanD;
1864 R_FillTranslucentSpan = R_FillTranslucentSpanD;
1865
1866 rt_copy1col = rt_copy1colD;
1867 rt_copy4cols = rt_copy4colsD;
1868 rt_map1col = rt_map1colD;
1869 rt_map4cols = rt_map4colsD;
1870 rt_lucent1col = rt_lucent1colD;
1871 rt_lucent4cols = rt_lucent4colsD;
1872 rt_tlate1col = rt_tlate1colD;
1873 rt_tlate4cols = rt_tlate4colsD;
1874 rt_tlatelucent1col = rt_tlatelucent1colD;
1875 rt_tlatelucent4cols = rt_tlatelucent4colsD;
1876 }
1877 }
1878
1879 VERSION_CONTROL (r_draw_cpp, "$Id: r_draw.cpp 4715 2014-03-29 15:58:53Z dr_sean $")
1880
1881