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