1 //----------------------------------------------------------------------------
2 // EDGE Colour Code
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Based on the DOOM source code, released by Id Software under the
20 // following copyright:
21 //
22 // Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25
26 #include "i_defs.h"
27 #include "i_defs_gl.h"
28
29 #include "ddf/main.h"
30 #include "ddf/colormap.h"
31 #include "ddf/game.h"
32
33 #include "r_colormap.h"
34 #include "dm_defs.h"
35 #include "dm_state.h"
36 #include "e_main.h"
37 #include "g_game.h" // currmap
38 #include "e_player.h"
39 #include "m_argv.h"
40 #include "r_misc.h"
41 #include "r_gldefs.h"
42 #include "r_modes.h"
43 #include "r_image.h"
44 #include "r_shader.h"
45 #include "r_texgl.h"
46 #include "r_units.h"
47 #include "w_wad.h"
48 #include "z_zone.h"
49
50 // -AJA- 1999/06/30: added this
51 byte playpal_data[14][256][3];
52
53 // -AJA- 1999/09/18: fixes problem with black text etc.
54 static bool loaded_playpal = false;
55
56 // -AJA- 1999/07/03: moved these here from st_stuff.c:
57 // Palette indices.
58 // For damage/bonus red-/gold-shifts
59 #define PAIN_PALS 1
60 #define BONUS_PALS 9
61 #define NUM_PAIN_PALS 8
62 #define NUM_BONUS_PALS 4
63 // Radiation suit, green shift.
64 #define RADIATION_PAL 13
65
66 // -AJA- 1999/07/03: moved these here from v_res.c:
67 int var_gamma;
68
69 static bool old_gamma = -1;
70
71
72 // text translation tables
73 const byte *font_whitener = NULL;
74 const colourmap_c *font_whiten_map = NULL;
75
76 const colourmap_c *text_red_map = NULL;
77 const colourmap_c *text_white_map = NULL;
78 const colourmap_c *text_grey_map = NULL;
79 const colourmap_c *text_green_map = NULL;
80 const colourmap_c *text_brown_map = NULL;
81 const colourmap_c *text_blue_map = NULL;
82 const colourmap_c *text_purple_map = NULL;
83 const colourmap_c *text_yellow_map = NULL;
84 const colourmap_c *text_orange_map = NULL;
85
86 // automap translation tables
87 const byte *am_normal_colmap = NULL;
88 const byte *am_overlay_colmap = NULL;
89
90
91 // colour indices from palette
92 int pal_black, pal_white, pal_gray239;
93 int pal_red, pal_green, pal_blue;
94 int pal_yellow, pal_green1, pal_brown1;
95
96 static int V_FindPureColour(int which);
97
98
V_InitPalette(void)99 void V_InitPalette(void)
100 {
101 int t, i, r, g, b, max_file, pal_lump;
102 wadtex_resource_c WT;
103
104 const byte *pal = (const byte*)W_CacheLumpName("PLAYPAL");
105
106 max_file = W_GetNumFiles();
107 pal_lump = -1;
108
109 // find "GLOBAL" palette (the one in the IWAD)
110 for (t = 0; t < max_file; t++)
111 {
112 W_GetTextureLumps(t, &WT);
113
114 if (WT.palette >= 0)
115 {
116 pal_lump = WT.palette;
117 break;
118 }
119 }
120
121 if (pal_lump == -1)
122 I_Error("Missing PLAYPAL palette lump !\n");
123
124 pal = (const byte*)W_CacheLumpNum(pal_lump);
125
126 // read in palette colours
127 for (t = 0; t < 14; t++)
128 {
129 for (i = 0; i < 256; i++)
130 {
131 playpal_data[t][i][0] = pal[(t * 256 + i) * 3 + 0];
132 playpal_data[t][i][1] = pal[(t * 256 + i) * 3 + 1];
133 playpal_data[t][i][2] = pal[(t * 256 + i) * 3 + 2];
134 }
135 }
136
137 for (i = 0; i < 256; i++)
138 {
139 r = playpal_data[0][i][0];
140 g = playpal_data[0][i][1];
141 b = playpal_data[0][i][2];
142 }
143
144 W_DoneWithLump(pal);
145 loaded_playpal = true;
146
147 // lookup useful colours
148 pal_black = V_FindColour(0, 0, 0);
149 pal_white = V_FindColour(255, 255, 255);
150 pal_gray239 = V_FindColour(239, 239, 239);
151
152 pal_red = V_FindPureColour(0);
153 pal_green = V_FindPureColour(1);
154 pal_blue = V_FindPureColour(2);
155
156 pal_yellow = V_FindColour(255, 255, 0);
157 pal_green1 = V_FindColour(64, 128, 48);
158 pal_brown1 = V_FindColour(192, 128, 74);
159
160 I_Printf("Loaded global palette.\n");
161
162 L_WriteDebug("Black:%d White:%d Red:%d Green:%d Blue:%d\n",
163 pal_black, pal_white, pal_red, pal_green, pal_blue);
164 }
165
166
167 //
168 // Reads the translation tables for various things, especially text
169 // colours. The text colourmaps to use are currently hardcoded, when
170 // HUD.DDF (or MENU.DDF) comes along then the colours will be user
171 // configurable.
172 //
173 // -ACB- 1998/09/10 Replaced the old procedure with this.
174 // -AJA- 2000/03/04: Moved here from r_draw1/2.c.
175 // -AJA- 2000/03/05: Uses colmap.ddf instead of PALREMAP lump.
176 //
InitTranslationTables(void)177 static void InitTranslationTables(void)
178 {
179 if (font_whitener)
180 return;
181
182 // look up the general colmaps & coltables
183
184 font_whiten_map = colourmaps.Lookup("FONTWHITEN");
185 font_whitener = V_GetTranslationTable(font_whiten_map);
186
187
188 am_normal_colmap = V_GetTranslationTable(
189 colourmaps.Lookup("AUTOMAP_NORMAL"));
190
191 am_overlay_colmap = V_GetTranslationTable(
192 colourmaps.Lookup("AUTOMAP_OVERLAY"));
193
194 // look up the text maps
195 text_red_map = colourmaps.Lookup("TEXT_RED");
196 text_white_map = colourmaps.Lookup("TEXT_WHITE");
197 text_grey_map = colourmaps.Lookup("TEXT_GREY");
198 text_green_map = colourmaps.Lookup("TEXT_GREEN");
199 text_brown_map = colourmaps.Lookup("TEXT_BROWN");
200 text_blue_map = colourmaps.Lookup("TEXT_BLUE");
201 text_purple_map = colourmaps.Lookup("TEXT_PURPLE");
202 text_yellow_map = colourmaps.Lookup("TEXT_YELLOW");
203 text_orange_map = colourmaps.Lookup("TEXT_ORANGE");
204 }
205
206 static int cur_palette = -1;
207
208
V_InitColour(void)209 void V_InitColour(void)
210 {
211 const char *s = M_GetParm("-gamma");
212 if (s)
213 {
214 var_gamma = MAX(0, MIN(5, atoi(s)));
215 }
216
217 InitTranslationTables();
218 }
219
220 //
221 // Find the closest matching colour in the palette.
222 //
V_FindColour(int r,int g,int b)223 int V_FindColour(int r, int g, int b)
224 {
225 int i;
226
227 int best = 0;
228 int best_dist = 1 << 30;
229
230 for (i=0; i < 256; i++)
231 {
232 int d_r = ABS(r - playpal_data[0][i][0]);
233 int d_g = ABS(g - playpal_data[0][i][1]);
234 int d_b = ABS(b - playpal_data[0][i][2]);
235
236 int dist = d_r * d_r + d_g * d_g + d_b * d_b;
237
238 if (dist == 0)
239 return i;
240
241 if (dist < best_dist)
242 {
243 best = i;
244 best_dist = dist;
245 }
246 }
247
248 return best;
249 }
250
251
252 //
253 // Find the best match for the pure colour. `which' is 0 for red, 1
254 // for green and 2 for blue.
255 //
V_FindPureColour(int which)256 static int V_FindPureColour(int which)
257 {
258 int i;
259
260 int best = 0;
261 int best_dist = 1 << 30;
262
263 for (i=0; i < 256; i++)
264 {
265 int a = playpal_data[0][i][which];
266 int b = playpal_data[0][i][(which+1)%3];
267 int c = playpal_data[0][i][(which+2)%3];
268 int d = MAX(b, c);
269
270 int dist = 255 - (a - d);
271
272 // the pure colour must shine through
273 if (a <= d)
274 continue;
275
276 if (dist < best_dist)
277 {
278 best = i;
279 best_dist = dist;
280 }
281 }
282
283 return best;
284 }
285
286
V_SetPalette(int type,float amount)287 void V_SetPalette(int type, float amount)
288 {
289 int palette = 0;
290
291 // -AJA- 1999/09/17: fixes problems with black text etc.
292 if (!loaded_playpal)
293 return;
294
295 if (amount >= 0.95f)
296 amount = 0.95f;
297
298 switch (type)
299 {
300 case PALETTE_PAIN:
301 palette = (int)(PAIN_PALS + amount * NUM_PAIN_PALS);
302 break;
303
304 case PALETTE_BONUS:
305 palette = (int)(BONUS_PALS + amount * NUM_BONUS_PALS);
306 break;
307
308 case PALETTE_SUIT:
309 palette = RADIATION_PAL;
310 break;
311 }
312
313 if (palette == cur_palette)
314 return;
315
316 cur_palette = palette;
317 }
318
319
320 //
321 // Computes the right "colourmap" (more precisely, coltable) to put into
322 // the dc_colourmap & ds_colourmap variables for use by the column &
323 // span drawers.
324 //
LoadColourmap(const colourmap_c * colm)325 static void LoadColourmap(const colourmap_c * colm)
326 {
327 int lump;
328 int size;
329 const byte *data;
330 const byte *data_in;
331
332 // we are writing to const marked memory here. Here is the only place
333 // the cache struct is touched.
334 colmapcache_t *cache = (colmapcache_t *)&colm->cache; // Intentional Const Override
335
336 lump = W_GetNumForName(colm->lump_name);
337 size = W_LumpLength(lump);
338 data = (const byte*)W_CacheLumpNum(lump);
339
340 if ((colm->start + colm->length) * 256 > size)
341 I_Error("Colourmap [%s] is too small ! (LENGTH too big)\n",
342 colm->name.c_str());
343
344 data_in = data + (colm->start * 256);
345
346 bool whiten = (colm->special & COLSP_Whiten) ? true : false;
347
348 Z_Resize(cache->data, byte, colm->length * 256);
349
350 if (whiten)
351 for (int j = 0; j < colm->length * 256; j++)
352 cache->data[j] = data_in[font_whitener[j]];
353 else
354 for (int j = 0; j < colm->length * 256; j++)
355 cache->data[j] = data_in[j];
356
357 W_DoneWithLump(data);
358 }
359
360
V_GetTranslationTable(const colourmap_c * colmap)361 const byte *V_GetTranslationTable(const colourmap_c * colmap)
362 {
363 // Do we need to load or recompute this colourmap ?
364
365 if (colmap->cache.data == NULL)
366 LoadColourmap(colmap);
367
368 return (const byte*)colmap->cache.data;
369 }
370
371
R_TranslatePalette(byte * new_pal,const byte * old_pal,const colourmap_c * trans)372 void R_TranslatePalette(byte *new_pal, const byte *old_pal,
373 const colourmap_c *trans)
374 {
375 // is the colormap just using GL_COLOUR?
376 if (trans->length == 0)
377 {
378 int r = RGB_RED(trans->gl_colour);
379 int g = RGB_GRN(trans->gl_colour);
380 int b = RGB_BLU(trans->gl_colour);
381
382 for (int j = 0; j < 256; j++)
383 {
384 new_pal[j*3 + 0] = old_pal[j*3+0] * (r+1) / 256;
385 new_pal[j*3 + 1] = old_pal[j*3+1] * (g+1) / 256;
386 new_pal[j*3 + 2] = old_pal[j*3+2] * (b+1) / 256;
387 }
388 }
389 else
390 {
391 // do the actual translation
392 const byte *trans_table = V_GetTranslationTable(trans);
393
394 for (int j = 0; j < 256; j++)
395 {
396 int k = trans_table[j];
397
398 new_pal[j*3 + 0] = old_pal[k*3+0];
399 new_pal[j*3 + 1] = old_pal[k*3+1];
400 new_pal[j*3 + 2] = old_pal[k*3+2];
401 }
402 }
403 }
404
405
AnalyseColourmap(const byte * table,int alpha,int * r,int * g,int * b)406 static int AnalyseColourmap(const byte *table, int alpha,
407 int *r, int *g, int *b)
408 {
409 /* analyse whole colourmap */
410 int r_tot = 0;
411 int g_tot = 0;
412 int b_tot = 0;
413 int total = 0;
414
415 for (int j = 0; j < 256; j++)
416 {
417 int r0 = playpal_data[0][j][0];
418 int g0 = playpal_data[0][j][1];
419 int b0 = playpal_data[0][j][2];
420
421 // give the grey-scales more importance
422 int weight = (r0 == g0 && g0 == b0) ? 3 : 1;
423
424 r0 = (255 * alpha + r0 * (255 - alpha)) / 255;
425 g0 = (255 * alpha + g0 * (255 - alpha)) / 255;
426 b0 = (255 * alpha + b0 * (255 - alpha)) / 255;
427
428 int r1 = playpal_data[0][table[j]][0];
429 int g1 = playpal_data[0][table[j]][1];
430 int b1 = playpal_data[0][table[j]][2];
431
432 int r_div = 255 * MAX(4, r1) / MAX(4, r0);
433 int g_div = 255 * MAX(4, g1) / MAX(4, g0);
434 int b_div = 255 * MAX(4, b1) / MAX(4, b0);
435
436 r_div = MAX(4, MIN(4096, r_div));
437 g_div = MAX(4, MIN(4096, g_div));
438 b_div = MAX(4, MIN(4096, b_div));
439
440 #if 0 // DEBUGGING
441 I_Printf("#%02x%02x%02x / #%02x%02x%02x = (%d,%d,%d)\n",
442 r1, g1, b1, r0, g0, b0, r_div, g_div, b_div);
443 #endif
444 r_tot += r_div * weight;
445 g_tot += g_div * weight;
446 b_tot += b_div * weight;
447 total += weight;
448 }
449
450 (*r) = r_tot / total;
451 (*g) = g_tot / total;
452 (*b) = b_tot / total;
453
454 // scale down when too large to fit
455 int ity = MAX(*r, MAX(*g, *b));
456
457 if (ity > 255)
458 {
459 (*r) = (*r) * 255 / ity;
460 (*g) = (*g) * 255 / ity;
461 (*b) = (*b) * 255 / ity;
462 }
463
464 // compute distance score
465 total = 0;
466
467 for (int k = 0; k < 256; k++)
468 {
469 int r0 = playpal_data[0][k][0];
470 int g0 = playpal_data[0][k][1];
471 int b0 = playpal_data[0][k][2];
472
473 // on-screen colour: c' = c * M * (1 - A) + M * A
474 int sr = (r0 * (*r) / 255 * (255 - alpha) + (*r) * alpha) / 255;
475 int sg = (g0 * (*g) / 255 * (255 - alpha) + (*g) * alpha) / 255;
476 int sb = (b0 * (*b) / 255 * (255 - alpha) + (*b) * alpha) / 255;
477
478 // FIXME: this is the INVULN function
479 #if 0
480 sr = (MAX(0, (*r /2 + 128) - r0) * (255 - alpha) + (*r) * alpha) / 255;
481 sg = (MAX(0, (*g /2 + 128) - g0) * (255 - alpha) + (*g) * alpha) / 255;
482 sb = (MAX(0, (*b /2 + 128) - b0) * (255 - alpha) + (*b) * alpha) / 255;
483 #endif
484
485 int r1 = playpal_data[0][table[k]][0];
486 int g1 = playpal_data[0][table[k]][1];
487 int b1 = playpal_data[0][table[k]][2];
488
489 // FIXME: use weighting (more for greyscale)
490 total += (sr - r1) * (sr - r1);
491 total += (sg - g1) * (sg - g1);
492 total += (sb - b1) * (sb - b1);
493 }
494
495 return total / 256;
496 }
497
498
TransformColourmap(colourmap_c * colmap)499 void TransformColourmap(colourmap_c *colmap)
500 {
501 const byte *table = colmap->cache.data;
502
503 if (table == NULL && ! colmap->lump_name.empty())
504 {
505 LoadColourmap(colmap);
506
507 table = (byte *) colmap->cache.data;
508 }
509
510 if (colmap->font_colour == RGB_NO_VALUE)
511 {
512 if (colmap->gl_colour != RGB_NO_VALUE)
513 colmap->font_colour = colmap->gl_colour;
514 else
515 {
516 SYS_ASSERT(table);
517
518 // for fonts, we only care about the GRAY colour
519 int r = playpal_data[0][table[pal_gray239]][0] * 255 / 239;
520 int g = playpal_data[0][table[pal_gray239]][1] * 255 / 239;
521 int b = playpal_data[0][table[pal_gray239]][2] * 255 / 239;
522
523 r = MIN(255, MAX(0, r));
524 g = MIN(255, MAX(0, g));
525 b = MIN(255, MAX(0, b));
526
527 colmap->font_colour = RGB_MAKE(r, g, b);
528 }
529 }
530
531 if (colmap->gl_colour == RGB_NO_VALUE)
532 {
533 SYS_ASSERT(table);
534
535 int r, g, b;
536
537 // int score =
538 AnalyseColourmap(table, 0, &r, &g, &b);
539
540 #if 0 // DEBUGGING
541 I_Debugf("COLMAP [%s] alpha %d --> (%d %d %d)\n",
542 colmap->name.c_str(), 0, r, g, b);
543 #endif
544
545 r = MIN(255, MAX(0, r));
546 g = MIN(255, MAX(0, g));
547 b = MIN(255, MAX(0, b));
548
549 colmap->gl_colour = RGB_MAKE(r, g, b);
550 }
551
552 L_WriteDebug("TransformColourmap [%s]\n", colmap->name.c_str());
553 L_WriteDebug("- gl_colour = #%06x\n", colmap->gl_colour);
554 }
555
556
V_GetColmapRGB(const colourmap_c * colmap,float * r,float * g,float * b)557 void V_GetColmapRGB(const colourmap_c *colmap, float *r, float *g, float *b)
558 {
559 if (colmap->gl_colour == RGB_NO_VALUE)
560 {
561 // Intention Const Override
562 TransformColourmap((colourmap_c *)colmap);
563 }
564
565 rgbcol_t col = colmap->gl_colour;
566
567 (*r) = GAMMA_CONV((col >> 16) & 0xFF) / 255.0f;
568 (*g) = GAMMA_CONV((col >> 8) & 0xFF) / 255.0f;
569 (*b) = GAMMA_CONV((col ) & 0xFF) / 255.0f;
570 }
571
572
V_GetFontColor(const colourmap_c * colmap)573 rgbcol_t V_GetFontColor(const colourmap_c *colmap)
574 {
575 if (! colmap)
576 return RGB_NO_VALUE;
577
578 if (colmap->font_colour == RGB_NO_VALUE)
579 {
580 // Intention Const Override
581 TransformColourmap((colourmap_c *)colmap);
582 }
583
584 return colmap->font_colour;
585 }
586
587
V_ParseFontColor(const char * name,bool strict)588 rgbcol_t V_ParseFontColor(const char *name, bool strict)
589 {
590 if (! name || ! name[0])
591 return RGB_NO_VALUE;
592
593 rgbcol_t rgb;
594
595 if (name[0] == '#')
596 {
597 rgb = strtol(name+1, NULL, 16);
598 }
599 else
600 {
601 const colourmap_c *colmap = colourmaps.Lookup(name);
602
603 if (! colmap)
604 {
605 if (strict)
606 I_Error("Unknown colormap: '%s'\n", name);
607 else
608 I_Debugf("Unknown colormap: '%s'\n", name);
609
610 return RGB_MAKE(255,0,255);
611 }
612
613 rgb = V_GetFontColor(colmap);
614 }
615
616 if (rgb == RGB_NO_VALUE)
617 rgb ^= 0x000101;
618
619 return rgb;
620 }
621
622
623 //
624 // Call this at the start of each frame (before any rendering or
625 // render-related work has been done). Will update the palette and/or
626 // gamma settings if they have changed since the last call.
627 //
V_ColourNewFrame(void)628 void V_ColourNewFrame(void)
629 {
630 if (var_gamma != old_gamma)
631 {
632 float gamma = 1.0 / (1.0 - var_gamma/8.0);
633
634 I_SetGamma(gamma);
635
636 old_gamma = var_gamma;
637 }
638 }
639
640 //
641 // Returns an RGB value from an index value - used the current
642 // palette. The byte pointer is assumed to point a 3-byte array.
643 //
V_IndexColourToRGB(int indexcol,byte * returncol)644 void V_IndexColourToRGB(int indexcol, byte *returncol)
645 {
646 returncol[0] = playpal_data[cur_palette][indexcol][0];
647 returncol[1] = playpal_data[cur_palette][indexcol][1];
648 returncol[2] = playpal_data[cur_palette][indexcol][2];
649 }
650
V_LookupColour(int col)651 rgbcol_t V_LookupColour(int col)
652 {
653 int r = playpal_data[0][col][0];
654 int g = playpal_data[0][col][1];
655 int b = playpal_data[0][col][2];
656
657 return RGB_MAKE(r,g,b);
658 }
659
660
661 #if 0 // OLD BUT POTENTIALLY USEFUL
662 static void SetupLightMap(lighting_model_e model)
663 {
664 for (i=0; i < 256; i++)
665 {
666 // Approximation of standard Doom lighting:
667 // (based on side-by-side comparison)
668 // [0,72] --> [0,16]
669 // [72,112] --> [16,56]
670 // [112,255] --> [56,255]
671
672 if (i <= 72)
673 rgl_light_map[i] = i * 16 / 72;
674 else if (i <= 112)
675 rgl_light_map[i] = 16 + (i - 72) * 40 / 40;
676 else if (i < 255)
677 rgl_light_map[i] = 56 + (i - 112) * 200 / 144;
678 else
679 rgl_light_map[i] = 255;
680 }
681 }
682 #endif
683
684
685 // -AJA- 1999/07/03: Rewrote this routine, since the palette handling
686 // has been moved to v_colour.c/h (and made more flexible). Later on it
687 // might be good to DDF-ify all this, allowing other palette lumps and
688 // being able to set priorities for the different effects.
689
R_PaletteStuff(void)690 void R_PaletteStuff(void)
691 {
692 int palette = PALETTE_NORMAL;
693 float amount = 0;
694
695 player_t *p = players[displayplayer];
696 SYS_ASSERT(p);
697
698 int cnt = p->damagecount;
699
700 if (p->powers[PW_Berserk] > 0)
701 {
702 int bzc = MIN(20, (int) p->powers[PW_Berserk]); // slowly fade berzerk out
703
704 if (bzc > cnt)
705 cnt = bzc;
706 }
707
708 if (cnt)
709 {
710 palette = PALETTE_PAIN;
711 amount = (cnt + 7) / 64.0f;
712 }
713 else if (p->bonuscount)
714 {
715 palette = PALETTE_BONUS;
716 amount = (p->bonuscount + 7) / 32.0f;
717 }
718 else if (p->powers[PW_AcidSuit] > 4 * 32 ||
719 fmod(p->powers[PW_AcidSuit], 16) >= 8)
720 {
721 palette = PALETTE_SUIT;
722 amount = 1.0f;
723 }
724
725 // This routine will limit `amount' to acceptable values, and will
726 // only update the video palette/colourmaps when the palette actually
727 // changes.
728 V_SetPalette(palette, amount);
729 }
730
731
732 //----------------------------------------------------------------------------
733 // COLORMAP SHADERS
734 //----------------------------------------------------------------------------
735
R_DoomLightingEquation(int L,float dist)736 int R_DoomLightingEquation(int L, float dist)
737 {
738 /* L in the range 0 to 63 */
739
740 int min_L = CLAMP(0, 36 - L, 31);
741
742 int index = (59 - L) - int(1280 / MAX(1, dist));
743
744 /* result is colormap index (0 bright .. 31 dark) */
745 return CLAMP(min_L, index, 31);
746 }
747
748
749 class colormap_shader_c : public abstract_shader_c
750 {
751 private:
752 const colourmap_c *colmap;
753
754 int light_lev;
755
756 GLuint fade_tex;
757
758 bool simple_cmap;
759 lighting_model_e lt_model;
760
761 rgbcol_t whites[32];
762
763 public:
colormap_shader_c(const colourmap_c * CM)764 colormap_shader_c(const colourmap_c *CM) : colmap(CM),
765 light_lev(255), fade_tex(0),
766 simple_cmap(true), lt_model(LMODEL_Doom)
767 { }
768
~colormap_shader_c()769 virtual ~colormap_shader_c()
770 {
771 DeleteTex();
772 }
773
774 private:
DistFromViewplane(float x,float y,float z)775 inline float DistFromViewplane(float x, float y, float z)
776 {
777 float dx = (x - viewx) * viewforward.x;
778 float dy = (y - viewy) * viewforward.y;
779 float dz = (z - viewz) * viewforward.z;
780
781 return dx + dy + dz;
782 }
783
TexCoord(local_gl_vert_t * v,int t,const vec3_t * lit_pos)784 inline void TexCoord(local_gl_vert_t *v, int t, const vec3_t *lit_pos)
785 {
786 float dist = DistFromViewplane(lit_pos->x, lit_pos->y, lit_pos->z);
787
788 int L = light_lev / 4; // need integer range 0-63
789
790 v->texc[t].x = dist / 1600.0;
791 v->texc[t].y = (L + 0.5) / 64.0;
792 }
793
794 public:
Sample(multi_color_c * col,float x,float y,float z)795 virtual void Sample(multi_color_c *col, float x, float y, float z)
796 {
797 // FIXME: assumes standard COLORMAP
798
799 float dist = DistFromViewplane(x, y, z);
800
801 int cmap_idx;
802
803 if (lt_model >= LMODEL_Flat)
804 cmap_idx = CLAMP(0, 42-light_lev/6, 31);
805 else
806 cmap_idx = R_DoomLightingEquation(light_lev/4, dist);
807
808 rgbcol_t WH = whites[cmap_idx];
809
810 col->mod_R += RGB_RED(WH);
811 col->mod_G += RGB_GRN(WH);
812 col->mod_B += RGB_BLU(WH);
813
814 // FIXME: for foggy maps, need to adjust add_R/G/B too
815 }
816
Corner(multi_color_c * col,float nx,float ny,float nz,struct mobj_s * mod_pos,bool is_weapon)817 virtual void Corner(multi_color_c *col, float nx, float ny, float nz,
818 struct mobj_s *mod_pos, bool is_weapon)
819 {
820 // TODO: improve this (normal-ise a little bit)
821
822 float mx = mod_pos->x;
823 float my = mod_pos->y;
824 float mz = mod_pos->z + mod_pos->height/2;
825
826 if (is_weapon)
827 {
828 mx += viewcos * 110;
829 my += viewsin * 110;
830 }
831
832 Sample(col, mx, my, mz);
833 }
834
WorldMix(GLuint shape,int num_vert,GLuint tex,float alpha,int * pass_var,int blending,bool masked,void * data,shader_coord_func_t func)835 virtual void WorldMix(GLuint shape, int num_vert,
836 GLuint tex, float alpha, int *pass_var, int blending,
837 bool masked, void *data, shader_coord_func_t func)
838 {
839 local_gl_vert_t * glvert = RGL_BeginUnit(shape, num_vert,
840 GL_MODULATE, tex,
841 (simple_cmap || r_dumbmulti.d) ? GL_MODULATE : GL_DECAL,
842 fade_tex, *pass_var, blending);
843
844 for (int v_idx=0; v_idx < num_vert; v_idx++)
845 {
846 local_gl_vert_t *dest = glvert + v_idx;
847
848 dest->rgba[3] = alpha;
849
850 vec3_t lit_pos;
851
852 (*func)(data, v_idx, &dest->pos, dest->rgba,
853 &dest->texc[0], &dest->normal, &lit_pos);
854
855 TexCoord(dest, 1, &lit_pos);
856 }
857
858 RGL_EndUnit(num_vert);
859
860 (*pass_var) += 1;
861 }
862
863 private:
MakeColormapTexture(int mode)864 void MakeColormapTexture(int mode)
865 {
866 epi::image_data_c img(256, 64, 4);
867
868 const byte *map = NULL;
869 int length = 32;
870
871 if (colmap && colmap->length > 0)
872 {
873 map = V_GetTranslationTable(colmap);
874 length = colmap->length;
875
876 for (int ci = 0; ci < 32; ci++)
877 {
878 int cmap_idx = length * ci / 32;
879
880 // +4 gets the white pixel -- FIXME: doom specific
881 const byte new_col = map[cmap_idx*256 + 4];
882
883 int r = playpal_data[0][new_col][0];
884 int g = playpal_data[0][new_col][1];
885 int b = playpal_data[0][new_col][2];
886
887 whites[ci] = RGB_MAKE(r, g, b);
888 }
889 }
890 else if (colmap) // GL_COLOUR
891 {
892 for (int ci = 0; ci < 32; ci++)
893 {
894 int r = RGB_RED(colmap->gl_colour) * (31-ci) / 31;
895 int g = RGB_GRN(colmap->gl_colour) * (31-ci) / 31;
896 int b = RGB_BLU(colmap->gl_colour) * (31-ci) / 31;
897
898 whites[ci] = RGB_MAKE(r, g, b);
899 }
900 }
901 else
902 {
903 for (int ci = 0; ci < 32; ci++)
904 {
905 int ity = 255 - ci * 8 - ci / 5;
906
907 whites[ci] = RGB_MAKE(ity, ity, ity);
908 }
909 }
910
911 for (int L = 0; L < 64; L++)
912 {
913 byte *dest = img.PixelAt(0, L);
914
915 for (int x = 0; x < 256; x++, dest += 4)
916 {
917 float dist = 1600.0f * x / 255.0;
918
919 int index;
920
921 if (lt_model >= LMODEL_Flat)
922 {
923 // FLAT lighting
924 index = CLAMP(0, 42 - (L*2/3), 31);
925 }
926 else
927 {
928 // DOOM lighting formula
929 index = R_DoomLightingEquation(L, dist);
930 }
931
932 //WTF index = index * length / 32;
933
934 if (false) //!!!! (mode == 1)
935 {
936 // GL_DECAL mode
937 dest[0] = 0;
938 dest[1] = 0;
939 dest[2] = 0;
940 dest[3] = 0 + index * 8;
941 }
942 else if (mode == 0)
943 {
944 // GL_MODULATE mode
945 if (colmap)
946 {
947 dest[0] = RGB_RED(whites[index]);
948 dest[1] = RGB_GRN(whites[index]);
949 dest[2] = RGB_BLU(whites[index]);
950 dest[3] = 255;
951 }
952 else
953 {
954 dest[0] = 255 - index * 8;
955 dest[1] = dest[0];
956 dest[2] = dest[0];
957 dest[3] = 255;
958 }
959 }
960 else if (mode == 2)
961 {
962 // additive pass (OLD CARDS)
963 dest[0] = index * 8 * 128/256;
964 dest[1] = dest[0];
965 dest[2] = dest[0];
966 dest[3] = 255;
967 }
968 }
969 }
970
971 fade_tex = R_UploadTexture(&img, UPL_Smooth|UPL_Clamp);
972 }
973
974 public:
Update()975 void Update()
976 {
977 if (fade_tex == 0 ||
978 lt_model != currmap->episode->lighting)
979 {
980 if (fade_tex != 0)
981 {
982 glDeleteTextures(1, &fade_tex);
983 }
984
985 lt_model = currmap->episode->lighting;
986
987 MakeColormapTexture(0);
988 }
989 }
990
DeleteTex()991 void DeleteTex()
992 {
993 if (fade_tex != 0)
994 {
995 glDeleteTextures(1, &fade_tex);
996 fade_tex = 0;
997 }
998 }
999
SetLight(int _level)1000 void SetLight(int _level)
1001 {
1002 light_lev = _level;
1003 }
1004 };
1005
1006
1007 static colormap_shader_c *std_cmap_shader;
1008
1009
R_GetColormapShader(const struct region_properties_s * props,int light_add)1010 abstract_shader_c *R_GetColormapShader(const struct region_properties_s *props,
1011 int light_add)
1012 {
1013 if (! std_cmap_shader)
1014 std_cmap_shader = new colormap_shader_c(NULL);
1015
1016 colormap_shader_c *shader = std_cmap_shader;
1017
1018 if (props->colourmap)
1019 {
1020 if (props->colourmap->analysis)
1021 shader = (colormap_shader_c *) props->colourmap->analysis;
1022 else
1023 {
1024 shader = new colormap_shader_c(props->colourmap);
1025
1026 // Intentional Const Override
1027 colourmap_c *CM = (colourmap_c *)props->colourmap;
1028 CM->analysis = shader;
1029 }
1030 }
1031
1032
1033 SYS_ASSERT(shader);
1034
1035 shader->Update();
1036
1037
1038 int lit_Nom = props->lightlevel + light_add;
1039
1040 if (! (props->colourmap &&
1041 (props->colourmap->special & COLSP_NoFlash)) ||
1042 ren_extralight > 250)
1043 {
1044 lit_Nom += ren_extralight;
1045 }
1046
1047 lit_Nom = CLAMP(0, lit_Nom, 255);
1048
1049 shader->SetLight(lit_Nom);
1050
1051 return shader;
1052 }
1053
1054
DeleteColourmapTextures(void)1055 void DeleteColourmapTextures(void)
1056 {
1057 if (std_cmap_shader)
1058 std_cmap_shader->DeleteTex();
1059
1060 std_cmap_shader = NULL;
1061
1062 for (int i = 0; i < colourmaps.GetSize(); i++)
1063 {
1064 colourmap_c *cmap = colourmaps[i];
1065
1066 if (cmap && cmap->analysis)
1067 {
1068 colormap_shader_c * shader = (colormap_shader_c *) cmap->analysis;
1069
1070 shader->DeleteTex();
1071 }
1072 }
1073 }
1074
1075
1076 //--- editor settings ---
1077 // vi:ts=4:sw=4:noexpandtab
1078