1 //----------------------------------------------------------------------------
2 //  EDGE OpenGL Rendering (Skies)
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 #include "i_defs.h"
20 #include "i_defs_gl.h"
21 
22 #include <math.h>
23 
24 #include "epi/image_data.h"
25 
26 #include "dm_state.h"
27 #include "m_math.h"
28 #include "r_misc.h"
29 #include "w_flat.h"
30 #include "r_sky.h"
31 #include "r_gldefs.h"
32 #include "r_sky.h"
33 #include "r_units.h"
34 #include "r_colormap.h"
35 #include "r_modes.h"
36 #include "r_image.h"
37 #include "r_texgl.h"
38 #include "w_wad.h"
39 #include "z_zone.h"
40 
41 #define DEBUG  0
42 
43 
44 const image_c *sky_image;
45 
46 bool custom_sky_box;
47 
48 // needed for SKY
49 extern epi::image_data_c *ReadAsEpiBlock(image_c *rim);
50 
51 
52 typedef struct sec_sky_ring_s
53 {
54 	// which group of connected skies (0 if none)
55 	int group;
56 
57 	// link of sector in RING
58 	struct sec_sky_ring_s *next;
59 	struct sec_sky_ring_s *prev;
60 
61 	// maximal sky height of group
62 	float max_h;
63 }
64 sec_sky_ring_t;
65 
66 //
67 // R_ComputeSkyHeights
68 //
69 // This routine computes the sky height field in sector_t, which is
70 // the maximal sky height over all sky sectors (ceiling only) which
71 // are joined by 2S linedefs.
72 //
73 // Algorithm: Initially all sky sectors are in individual groups.  Now
74 // we scan the linedef list.  For each 2-sectored line with sky on
75 // both sides, merge the two groups into one.  Simple :).  We can
76 // compute the maximal height of the group as we go.
77 //
R_ComputeSkyHeights(void)78 void R_ComputeSkyHeights(void)
79 {
80 	int i;
81 	line_t *ld;
82 	sector_t *sec;
83 
84 	// --- initialise ---
85 
86 	sec_sky_ring_t *rings = new sec_sky_ring_t[numsectors];
87 
88 	memset(rings, 0, numsectors * sizeof(sec_sky_ring_t));
89 
90 	for (i=0, sec=sectors; i < numsectors; i++, sec++)
91 	{
92 		if (! IS_SKY(sec->ceil))
93 			continue;
94 
95 		rings[i].group = (i + 1);
96 		rings[i].next = rings[i].prev = rings + i;
97 		rings[i].max_h = sec->c_h;
98 
99 		// leave some room for tall sprites
100 		static const float SPR_H_MAX = 256.0f;
101 
102 		if (sec->c_h < 30000.0f && (sec->c_h > sec->f_h) &&
103 			(sec->c_h < sec->f_h + SPR_H_MAX))
104 		{
105 			rings[i].max_h = sec->f_h + SPR_H_MAX;
106 		}
107 	}
108 
109 	// --- make the pass over linedefs ---
110 
111 	for (i=0, ld=lines; i < numlines; i++, ld++)
112 	{
113 		sector_t *sec1, *sec2;
114 		sec_sky_ring_t *ring1, *ring2, *tmp_R;
115 
116 		if (! ld->side[0] || ! ld->side[1])
117 			continue;
118 
119 		sec1 = ld->frontsector;
120 		sec2 = ld->backsector;
121 
122 		SYS_ASSERT(sec1 && sec2);
123 
124 		if (sec1 == sec2)
125 			continue;
126 
127 		ring1 = rings + (sec1 - sectors);
128 		ring2 = rings + (sec2 - sectors);
129 
130 		// we require sky on both sides
131 		if (ring1->group == 0 || ring2->group == 0)
132 			continue;
133 
134 		// already in the same group ?
135 		if (ring1->group == ring2->group)
136 			continue;
137 
138 		// swap sectors to ensure the lower group is added to the higher
139 		// group, since we don't need to update the `max_h' fields of the
140 		// highest group.
141 
142 		if (ring1->max_h < ring2->max_h)
143 		{
144 			tmp_R = ring1;  ring1 = ring2;  ring2 = tmp_R;
145 		}
146 
147 		// update the group numbers in the second group
148 
149 		ring2->group = ring1->group;
150 		ring2->max_h = ring1->max_h;
151 
152 		for (tmp_R=ring2->next; tmp_R != ring2; tmp_R=tmp_R->next)
153 		{
154 			tmp_R->group = ring1->group;
155 			tmp_R->max_h = ring1->max_h;
156 		}
157 
158 		// merge 'em baby...
159 
160 		ring1->next->prev = ring2;
161 		ring2->next->prev = ring1;
162 
163 		tmp_R = ring1->next;
164 		ring1->next = ring2->next;
165 		ring2->next = tmp_R;
166 	}
167 
168 	// --- now store the results, and free up ---
169 
170 	for (i=0, sec=sectors; i < numsectors; i++, sec++)
171 	{
172 		if (rings[i].group > 0)
173 			sec->sky_h = rings[i].max_h;
174 
175 #if 0   // DEBUG CODE
176 		L_WriteDebug("SKY: sec %d  group %d  max_h %1.1f\n", i,
177 				rings[i].group, rings[i].max_h);
178 #endif
179 	}
180 
181 	delete[] rings;
182 }
183 
184 //----------------------------------------------------------------------------
185 
186 
187 static bool need_to_draw_sky = false;
188 
189 
190 typedef struct
191 {
192 	const image_c *base_sky;
193 
194 	const colourmap_c *fx_colmap;
195 
196 	int face_size;
197 
198 	GLuint tex[6];
199 
200 	// face images are only present for custom skyboxes.
201 	// pseudo skyboxes are generated outside of the image system.
202 	const image_c *face[6];
203 }
204 fake_skybox_t;
205 
206 static fake_skybox_t fake_box[2] =
207 {
208 	{
209 		NULL, NULL, 1,
210 		{ 0,0,0,0,0,0 },
211 		{ NULL, NULL, NULL, NULL, NULL, NULL }
212 	},
213 	{
214 		NULL, NULL, 1,
215 		{ 0,0,0,0,0,0 },
216 		{ NULL, NULL, NULL, NULL, NULL, NULL }
217 	}
218 };
219 
220 
DeleteSkyTexGroup(int SK)221 static void DeleteSkyTexGroup(int SK)
222 {
223 	for (int i = 0; i < 6; i++)
224 	{
225 		if (fake_box[SK].tex[i] != 0)
226 		{
227 			glDeleteTextures(1, &fake_box[SK].tex[i]);
228 			fake_box[SK].tex[i] = 0;
229 		}
230 	}
231 }
232 
DeleteSkyTextures(void)233 void DeleteSkyTextures(void)
234 {
235 	for (int SK = 0; SK < 2; SK++)
236 	{
237 		fake_box[SK].base_sky  = NULL;
238 		fake_box[SK].fx_colmap = NULL;
239 
240 		DeleteSkyTexGroup(SK);
241 	}
242 }
243 
244 
RGL_SetupSkyMatrices(float dist)245 static void RGL_SetupSkyMatrices(float dist)
246 {
247 	glMatrixMode(GL_PROJECTION);
248 	glPushMatrix();
249 
250 	glLoadIdentity();
251 	glFrustum(-view_x_slope * r_nearclip.f, view_x_slope * r_nearclip.f,
252 			  -view_y_slope * r_nearclip.f, view_y_slope * r_nearclip.f,
253 			  r_nearclip.f, r_farclip.f);
254 
255 	glMatrixMode(GL_MODELVIEW);
256 	glPushMatrix();
257 
258 	glLoadIdentity();
259 
260 	glRotatef(270.0f - ANG_2_FLOAT(viewvertangle), 1.0f, 0.0f, 0.0f);
261 	glRotatef(90.0f  - ANG_2_FLOAT(viewangle), 0.0f, 0.0f, 1.0f);
262 }
263 
264 
RGL_SetupSkyMatrices2D(void)265 static void RGL_SetupSkyMatrices2D(void)
266 {
267 	glMatrixMode(GL_PROJECTION);
268 	glPushMatrix();
269 
270 	glLoadIdentity();
271 	glOrtho(0.0f, (float)SCREENWIDTH,
272 			0.0f, (float)SCREENHEIGHT, -1.0f, 1.0f);
273 
274 	glMatrixMode(GL_MODELVIEW);
275 	glPushMatrix();
276 
277 	glLoadIdentity();
278 }
279 
280 
RGL_RevertSkyMatrices(void)281 void RGL_RevertSkyMatrices(void)
282 {
283 	glMatrixMode(GL_PROJECTION);
284 	glPopMatrix();
285 
286 	glMatrixMode(GL_MODELVIEW);
287 	glPopMatrix();
288 }
289 
290 
RGL_BeginSky(void)291 void RGL_BeginSky(void)
292 {
293 	need_to_draw_sky = false;
294 
295 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
296 	glDisable(GL_TEXTURE_2D);
297 
298 	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
299 	glEdgeFlag(GL_TRUE);
300 }
301 
302 
RGL_FinishSky(void)303 void RGL_FinishSky(void)
304 {
305 	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
306 
307 	if (! need_to_draw_sky)
308 		return;
309 
310 	// draw sky picture, but DON'T affect the depth buffering
311 
312 	glEnable(GL_TEXTURE_2D);
313 
314 	glDepthMask(GL_FALSE);
315 
316 	if (level_flags.mlook || custom_sky_box)
317 	{
318 		if (! r_dumbsky.d)
319 			glDepthFunc(GL_GREATER);
320 
321 		RGL_DrawSkyBox();
322 	}
323 	else
324 	{
325 		RGL_DrawSkyOriginal();
326 	}
327 
328 	glDepthFunc(GL_LEQUAL);
329 	glDepthMask(GL_TRUE);
330 
331 	glDisable(GL_TEXTURE_2D);
332 
333 #if 0
334 	// clear buffer (EXPERIMENTAL) -- causes render problems: ceilings
335 	// you shouldn't be able to see (MAP05, MAP12).
336 	glClear(GL_DEPTH_BUFFER_BIT);
337 #endif
338 }
339 
340 
RGL_DrawSkyBox(void)341 void RGL_DrawSkyBox(void)
342 {
343 	float dist = r_farclip.f / 2.0f;
344 
345 	int SK = RGL_UpdateSkyBoxTextures();
346 
347 	RGL_SetupSkyMatrices(dist);
348 
349 	float v0 = 0.0f;
350 	float v1 = 1.0f;
351 
352 	if (r_dumbclamp.d)
353 	{
354 		float size = fake_box[SK].face_size;
355 
356 		v0 = 0.5f / size;
357 		v1 = 1.0f - v0;
358 	}
359 
360 	glEnable(GL_TEXTURE_2D);
361 
362 	float col[4];
363 
364 	col[0] = LT_RED(255);
365 	col[1] = LT_GRN(255);
366 	col[2] = LT_BLU(255);
367 	col[3] = 1.0f;
368 
369 	if (r_colormaterial.d || ! r_colorlighting.d)
370 		glColor4fv(col);
371 	else
372 	{
373 		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, col);
374 		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
375 	}
376 
377 	// top
378 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_Top]);
379         glNormal3i(0, 0, -1);
380 
381 	glBegin(GL_QUADS);
382 	glTexCoord2f(v0, v0); glVertex3f(-dist,  dist, +dist);
383 	glTexCoord2f(v0, v1); glVertex3f(-dist, -dist, +dist);
384 	glTexCoord2f(v1, v1); glVertex3f( dist, -dist, +dist);
385 	glTexCoord2f(v1, v0); glVertex3f( dist,  dist, +dist);
386 	glEnd();
387 
388 	// bottom
389 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_Bottom]);
390         glNormal3i(0, 0, +1);
391 
392 	glBegin(GL_QUADS);
393 	glTexCoord2f(v0, v0); glVertex3f(-dist, -dist, -dist);
394 	glTexCoord2f(v0, v1); glVertex3f(-dist,  dist, -dist);
395 	glTexCoord2f(v1, v1); glVertex3f( dist,  dist, -dist);
396 	glTexCoord2f(v1, v0); glVertex3f( dist, -dist, -dist);
397 	glEnd();
398 
399 	// north
400 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_North]);
401         glNormal3i(0, -1, 0);
402 
403 	glBegin(GL_QUADS);
404 	glTexCoord2f(v0, v0); glVertex3f(-dist,  dist, -dist);
405 	glTexCoord2f(v0, v1); glVertex3f(-dist,  dist, +dist);
406 	glTexCoord2f(v1, v1); glVertex3f( dist,  dist, +dist);
407 	glTexCoord2f(v1, v0); glVertex3f( dist,  dist, -dist);
408 	glEnd();
409 
410 	// east
411 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_East]);
412         glNormal3i(-1, 0, 0);
413 
414 	glBegin(GL_QUADS);
415 	glTexCoord2f(v0, v0); glVertex3f( dist,  dist, -dist);
416 	glTexCoord2f(v0, v1); glVertex3f( dist,  dist, +dist);
417 	glTexCoord2f(v1, v1); glVertex3f( dist, -dist, +dist);
418 	glTexCoord2f(v1, v0); glVertex3f( dist, -dist, -dist);
419 	glEnd();
420 
421 	// south
422 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_South]);
423         glNormal3i(0, +1, 0);
424 
425 	glBegin(GL_QUADS);
426 	glTexCoord2f(v0, v0); glVertex3f( dist, -dist, -dist);
427 	glTexCoord2f(v0, v1); glVertex3f( dist, -dist, +dist);
428 	glTexCoord2f(v1, v1); glVertex3f(-dist, -dist, +dist);
429 	glTexCoord2f(v1, v0); glVertex3f(-dist, -dist, -dist);
430 	glEnd();
431 
432 	// west
433 	glBindTexture(GL_TEXTURE_2D, fake_box[SK].tex[WSKY_West]);
434         glNormal3i(+1, 0, 0);
435 
436 	glBegin(GL_QUADS);
437 	glTexCoord2f(v0, v0); glVertex3f(-dist, -dist, -dist);
438 	glTexCoord2f(v0, v1); glVertex3f(-dist, -dist, +dist);
439 	glTexCoord2f(v1, v1); glVertex3f(-dist,  dist, +dist);
440 	glTexCoord2f(v1, v0); glVertex3f(-dist,  dist, -dist);
441 	glEnd();
442 
443 	glDisable(GL_TEXTURE_2D);
444 
445 	RGL_RevertSkyMatrices();
446 }
447 
448 
RGL_DrawSkyOriginal(void)449 void RGL_DrawSkyOriginal(void)
450 {
451 	RGL_SetupSkyMatrices2D();
452 
453 	float white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
454 
455 	if (r_colormaterial.d || ! r_colorlighting.d)
456 		glColor4fv(white);
457 	else
458 	{
459 		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, white);
460 		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white);
461 	}
462 
463 	GLuint tex_id = W_ImageCache(sky_image, false, ren_fx_colmap);
464 
465 	glEnable(GL_TEXTURE_2D);
466 	glBindTexture(GL_TEXTURE_2D, tex_id);
467 
468 	// divide screen into 32 vertical strips, since mapping is non-linear
469 	glBegin(GL_QUAD_STRIP);
470 
471 	// FIXME for widescreen
472 	float FIELDOFVIEW = CLAMP(5, r_fov.f, 175);
473 
474 	float focal_len = tan(FIELDOFVIEW * M_PI / 360.0);
475 	float centerxfrac = SCREENWIDTH / 2.0f;
476 
477 	float ty1 = 200.0f / 128.0f;
478 	float ty2 = 0;
479 
480 	for (int i = 0; i <= 32; i++)
481 	{
482 		int sx = i * SCREENWIDTH / 32;
483 
484 		// use formula from original Doom code
485 		angle_t ang = ANG180 + M_ATan((1.0f - sx / centerxfrac) * focal_len);
486 
487 		// some mucking about here to prevent wrap-around
488 		float tx = (((viewangle >> 2) + (ang >> 2) + (ANG180 >> 2)) >> 20);
489 
490 		if ((IM_WIDTH(sky_image) / IM_HEIGHT(sky_image)) < 2.28f)
491 			tx = tx / 256.0f;
492 		else
493 			tx = tx / 1024.0f;
494 
495 #if 0  // DEBUGGING
496 I_Printf("[%i] --> %1.2f  tx %1.4f\n", i, ANG_2_FLOAT(ang), tx);
497 #endif
498 		glTexCoord2f(tx, 1.0f - ty1);
499 		glVertex2i(sx, 0);
500 
501 		glTexCoord2f(tx, 1.0f - ty2);
502 		glVertex2i(sx, SCREENHEIGHT);
503  	}
504 
505 	glEnd();
506 
507 	glDisable(GL_TEXTURE_2D);
508 
509 	RGL_RevertSkyMatrices();
510 }
511 
512 
RGL_DrawSkyPlane(subsector_t * sub,float h)513 void RGL_DrawSkyPlane(subsector_t *sub, float h)
514 {
515 	need_to_draw_sky = true;
516 
517 	if (r_dumbsky.d)
518 		return;
519 
520 	MIR_Height(h);
521 
522 	glNormal3f(0, 0, (viewz > h) ? 1.0f : -1.0f);
523 
524 	glBegin(GL_POLYGON);
525 
526 	for (seg_t *seg=sub->segs; seg; seg=seg->sub_next)
527 	{
528 		float x = seg->v1->x;
529 		float y = seg->v1->y;
530 
531 		MIR_Coordinate(x, y);
532 
533 		glVertex3f(x, y, h);
534 	}
535 
536 	glEnd();
537 }
538 
539 
RGL_DrawSkyWall(seg_t * seg,float h1,float h2)540 void RGL_DrawSkyWall(seg_t *seg, float h1, float h2)
541 {
542 	need_to_draw_sky = true;
543 
544 	if (r_dumbsky.d)
545 		return;
546 
547 	float x1 = seg->v1->x;
548 	float y1 = seg->v1->y;
549 	float x2 = seg->v2->x;
550 	float y2 = seg->v2->y;
551 
552 	MIR_Coordinate(x1, y1);
553 	MIR_Coordinate(x2, y2);
554 
555 	MIR_Height(h1);
556 	MIR_Height(h2);
557 
558 	glNormal3f(y2 - y1, x1 - x2, 0);
559 
560 	glBegin(GL_QUADS);
561 
562 	glVertex3f(x1, y1, h1);
563 	glVertex3f(x1, y1, h2);
564 	glVertex3f(x2, y2, h2);
565 	glVertex3f(x2, y2, h1);
566 
567 	glEnd();
568 }
569 
570 
571 //----------------------------------------------------------------------------
572 
573 
574 #define PIXEL_RED(pix)  (what_palette[pix*3 + 0])
575 #define PIXEL_GRN(pix)  (what_palette[pix*3 + 1])
576 #define PIXEL_BLU(pix)  (what_palette[pix*3 + 2])
577 
578 
SkyIsNarrow(const image_c * sky)579 static bool SkyIsNarrow(const image_c *sky)
580 {
581 	// check the aspect of the image
582 	return (IM_WIDTH(sky) / IM_HEIGHT(sky)) < 2.28f;
583 }
584 
585 
CalcSkyCoord(int px,int py,int pw,int ph,int face,bool narrow,float * tx,float * ty)586 static void CalcSkyCoord(int px, int py, int pw, int ph, int face,
587 		bool narrow, float *tx, float *ty)
588 {
589 	// the 0.5 here ensures we never hit exactly zero
590 	float ax = ((float)px + 0.5f) / (float)pw * 2.0f - 1.0f;
591 	float ay = ((float)py + 0.5f) / (float)ph * 2.0f - 1.0f;
592 
593 	float sx, sy, sz;
594 
595 	switch (face)
596 	{
597 		case WSKY_North:
598 			sx = ax; sy = 1.0f; sz = ay; break;
599 
600 		case WSKY_South:
601 			sx = -ax; sy = -1.0f; sz = ay; break;
602 
603 		case WSKY_East:
604 			sx = 1.0f; sy = -ax; sz = ay; break;
605 
606 		case WSKY_West:
607 			sx = -1.0f; sy = ax; sz = ay; break;
608 
609 		case WSKY_Top:
610 			sx = ax; sy = -ay; sz = 1.0f; break;
611 
612 		case WSKY_Bottom:
613 			sx = ax; sy = ay; sz = -1.0f; break;
614 
615 		default:
616 			I_Error("CalcSkyCoord: INTERNAL ERROR (lost face)\n");
617 			sx = sy = sz = 0;
618 			break; /* NOT REACHED */
619 	}
620 
621 	float len2 = sqrt((sx) * (sx) + (sy) * (sy));
622 
623 	angle_t H = ANG0  + R_PointToAngle(0, 0, sx, sy);
624 	angle_t V = ANG90 - R_PointToAngle(0, 0, len2, sz);
625 
626 	if (narrow)
627 		*tx = (float)(H >> 1) / (float)(1 << 30);
628 	else
629 		*tx = (float)(H >> 2) / (float)(1 << 30);
630 
631 	// want yy to range from 0.0 (top) to 2.0 (bottom)
632 	float yy = (float)(V) / (float)ANG90;
633 
634 	// this lowers the effective centre of the pseudo skybox to
635 	// match the DOOM sky, which is 128 pixels on a 200 pixel high
636 	// screen (so it dips 28 pixels below the horizon).
637 	yy = yy / 1.15f;
638 
639 	// mirror it (vertically)
640 	if (yy > 1.0f) yy = 2.0f - yy;
641 
642 	*ty = 1.0f - pow(yy, 2.2);
643 }
644 
645 
BlurCentre(epi::image_data_c & img)646 static void BlurCentre(epi::image_data_c& img)
647 {
648 	// Blurs the center of the image (the top face of the
649 	// pseudo sky box).  The amount of blur is different at
650 	// different places: from none at all at the edges upto
651 	// maximum blur in the middle.
652 
653 	SYS_ASSERT(img.bpp == 3);
654 
655 	// create a copy to work from (as we cannot blur in-place)
656 	epi::image_data_c orig(img.width, img.height, 3);
657 
658 	memcpy(orig.pixels, img.pixels, orig.width*orig.height*3);
659 
660 	for (int y = 1+img.height/4; y < img.height*3/4; y++)
661 	for (int x = 1+img.width /4; x < img.width *3/4; x++)
662 	{
663 		int x_pos = 31 - ABS(x - img.width /2) * 127 / img.width;
664 		int y_pos = 31 - ABS(y - img.height/2) * 127 / img.height;
665 
666 		// SYS_ASSERT(0 <= x_pos && x_pos <= 31);
667 		// SYS_ASSERT(0 <= y_pos && y_pos <= 31);
668 
669 		int min_pos = MIN(x_pos, y_pos);
670 
671 		int size = 16 + min_pos*2;  // range: 1.00 to 4.99 (times 16)
672 
673 		int d_size = (size | 15) / 16;
674 
675 		// compute average over the box
676 		int r = 0;
677 		int g = 0;
678 		int b = 0;
679 		int total = 0;
680 
681 		for (int dy = -d_size; dy <= +d_size; dy++)
682 		for (int dx = -d_size; dx <= +d_size; dx++)
683 		{
684 			u8_t *src = orig.PixelAt(x+dx, y+dy);
685 
686 			int qty = ( (ABS(dx) < d_size) ? 16 : (size & 15) ) *
687 			          ( (ABS(dy) < d_size) ? 16 : (size & 15) );
688 
689 			total += qty;
690 
691 			r += src[0] * qty;
692 			g += src[1] * qty;
693 			b += src[2] * qty;
694 		}
695 
696 		SYS_ASSERT(total > 0);
697 
698 		u8_t *dest = img.PixelAt(x, y);
699 
700 		dest[0] = r / total;
701 		dest[1] = g / total;
702 		dest[2] = b / total;
703 	}
704 }
705 
BuildFace(const epi::image_data_c * sky,int face,fake_skybox_t * info,const byte * what_palette)706 static GLuint BuildFace(const epi::image_data_c *sky, int face,
707 				  	    fake_skybox_t *info, const byte *what_palette)
708 
709 {
710 	int img_size = info->face_size;
711 
712 	epi::image_data_c img(img_size, img_size, 3);
713 
714 
715 	bool narrow = SkyIsNarrow(info->base_sky);
716 
717 	const byte *src = sky->pixels;
718 
719 	int sky_w = sky->width;
720 	int sky_h = sky->height;
721 
722 	for (int y=0; y < img_size; y++)
723 	{
724 		u8_t *dest = img.PixelAt(0, y);
725 
726 		for (int x=0; x < img_size; x++, dest += 3)
727 		{
728 			float tx, ty;
729 
730 			CalcSkyCoord(x, y, img_size, img_size, face, narrow, &tx, &ty);
731 
732 			// Bilinear Filtering
733 
734 			int TX = (int)(tx * sky_w * 16);
735 			int TY = (int)(ty * sky_h * 16);
736 
737 			// negative values shouldn't occur, but just in case...
738 			TX = (TX + sky_w * 64) % (sky_w * 16);
739 			TY = (TY + sky_h * 64) % (sky_h * 16);
740 
741 			// SYS_ASSERT(TX >= 0 && TY >= 0);
742 
743 			int FX = TX % 16; TX >>= 4;
744 			int FY = TY % 16; TY >>= 4;
745 
746 			// SYS_ASSERT(TX < sky_w && TY < sky_h);
747 
748 
749 			int TX2 = (TX + 1) % sky_w;
750 			int TY2 = (TY < sky_h-1) ? (TY+1) : TY;
751 
752 			byte rA, rB, rC, rD;
753 			byte gA, gB, gC, gD;
754 			byte bA, bB, bC, bD;
755 
756 			switch (sky->bpp)
757 			{
758 				case 1:
759 				{
760 					byte src_A = src[TY  * sky_w + TX];
761 					byte src_B = src[TY  * sky_w + TX2];
762 					byte src_C = src[TY2 * sky_w + TX];
763 					byte src_D = src[TY2 * sky_w + TX2];
764 
765 					rA = PIXEL_RED(src_A); rB = PIXEL_RED(src_B);
766 					rC = PIXEL_RED(src_C); rD = PIXEL_RED(src_D);
767 
768 					gA = PIXEL_GRN(src_A); gB = PIXEL_GRN(src_B);
769 					gC = PIXEL_GRN(src_C); gD = PIXEL_GRN(src_D);
770 
771 					bA = PIXEL_BLU(src_A); bB = PIXEL_BLU(src_B);
772 					bC = PIXEL_BLU(src_C); bD = PIXEL_BLU(src_D);
773 				}
774 				break;
775 
776 				case 3:
777 				{
778 					rA = src[(TY * sky_w + TX) * 3 + 0];
779 					gA = src[(TY * sky_w + TX) * 3 + 1];
780 					bA = src[(TY * sky_w + TX) * 3 + 2];
781 
782 					rB = src[(TY * sky_w + TX2) * 3 + 0];
783 					gB = src[(TY * sky_w + TX2) * 3 + 1];
784 					bB = src[(TY * sky_w + TX2) * 3 + 2];
785 
786 					rC = src[(TY2 * sky_w + TX) * 3 + 0];
787 					gC = src[(TY2 * sky_w + TX) * 3 + 1];
788 					bC = src[(TY2 * sky_w + TX) * 3 + 2];
789 
790 					rD = src[(TY2 * sky_w + TX2) * 3 + 0];
791 					gD = src[(TY2 * sky_w + TX2) * 3 + 1];
792 					bD = src[(TY2 * sky_w + TX2) * 3 + 2];
793 				}
794 				break;
795 
796 				case 4:
797 				{
798 					rA = src[(TY * sky_w + TX) * 4 + 0];
799 					gA = src[(TY * sky_w + TX) * 4 + 1];
800 					bA = src[(TY * sky_w + TX) * 4 + 2];
801 
802 					rB = src[(TY * sky_w + TX2) * 4 + 0];
803 					gB = src[(TY * sky_w + TX2) * 4 + 1];
804 					bB = src[(TY * sky_w + TX2) * 4 + 2];
805 
806 					rC = src[(TY2 * sky_w + TX) * 4 + 0];
807 					gC = src[(TY2 * sky_w + TX) * 4 + 1];
808 					bC = src[(TY2 * sky_w + TX) * 4 + 2];
809 
810 					rD = src[(TY2 * sky_w + TX2) * 4 + 0];
811 					gD = src[(TY2 * sky_w + TX2) * 4 + 1];
812 					bD = src[(TY2 * sky_w + TX2) * 4 + 2];
813 				}
814 				break;
815 
816 				default:  // remove compiler warning
817 					rA = rB = rC = rD = 0;
818 					gA = gB = gC = gD = 0;
819 					bA = bB = bC = bD = 0;
820 					break;
821 			}
822 
823 			int r = (int)rA * (FX^15) * (FY^15) +
824 					(int)rB * (FX   ) * (FY^15) +
825 					(int)rC * (FX^15) * (FY   ) +
826 					(int)rD * (FX   ) * (FY   );
827 
828 			int g = (int)gA * (FX^15) * (FY^15) +
829 					(int)gB * (FX   ) * (FY^15) +
830 					(int)gC * (FX^15) * (FY   ) +
831 					(int)gD * (FX   ) * (FY   );
832 
833 			int b = (int)bA * (FX^15) * (FY^15) +
834 					(int)bB * (FX   ) * (FY^15) +
835 					(int)bC * (FX^15) * (FY   ) +
836 					(int)bD * (FX   ) * (FY   );
837 
838 			dest[0] = r / 225;
839 			dest[1] = g / 225;
840 			dest[2] = b / 225;
841 		}
842 	}
843 
844 	// make the top surface look less bad
845 	if (face == WSKY_Top)
846 	{
847 		BlurCentre(img);
848 	}
849 
850 	return R_UploadTexture(&img, UPL_Smooth|UPL_Clamp);
851 }
852 
853 
UserSkyFaceName(const char * base,int face)854 static const char *UserSkyFaceName(const char *base, int face)
855 {
856 	static char buffer[64];
857 	static const char letters[] = "NESWTB";
858 
859 	sprintf(buffer, "%s_%c", base, letters[face]);
860 	return buffer;
861 }
862 
863 
RGL_UpdateSkyBoxTextures(void)864 int RGL_UpdateSkyBoxTextures(void)
865 {
866 	int SK = ren_fx_colmap ? 1 : 0;
867 
868 	fake_skybox_t *info = &fake_box[SK];
869 
870 	if (info->base_sky  == sky_image &&
871 		info->fx_colmap == ren_fx_colmap)
872 	{
873 		return SK;
874 	}
875 
876 	info->base_sky  = sky_image;
877 	info->fx_colmap = ren_fx_colmap;
878 
879 
880 	// check for custom sky boxes
881 	info->face[WSKY_North] = W_ImageLookup(
882 			UserSkyFaceName(sky_image->name, WSKY_North), INS_Texture, ILF_Null);
883 
884 	if (info->face[WSKY_North])
885 	{
886 		custom_sky_box = true;
887 
888 		info->face_size = info->face[WSKY_North]->total_w;
889 
890 		for (int i = WSKY_East; i < 6; i++)
891 			info->face[i] = W_ImageLookup(
892 					UserSkyFaceName(sky_image->name, i), INS_Texture);
893 
894 		for (int k = 0; k < 6; k++)
895 			info->tex[k] = W_ImageCache(info->face[k], false, ren_fx_colmap);
896 
897 		return SK;
898 	}
899 
900 
901 	// Create pseudo sky box
902 
903 	info->face_size = 256;
904 
905 	custom_sky_box = false;
906 
907 
908 	// Intentional Const Override
909 	const epi::image_data_c *block = ReadAsEpiBlock((image_c*)sky_image);
910 	SYS_ASSERT(block);
911 
912 	// get correct palette
913 	const byte *what_pal = (const byte *) &playpal_data[0];
914 	bool what_pal_cached = false;
915 
916 	static byte trans_pal[256*3];
917 
918 	if (ren_fx_colmap)
919 	{
920 		R_TranslatePalette(trans_pal, what_pal, ren_fx_colmap);
921 		what_pal = trans_pal;
922 	}
923 	else if (sky_image->source_palette >= 0)
924 	{
925 		what_pal = (const byte *) W_CacheLumpNum(sky_image->source_palette);
926 		what_pal_cached = true;
927 	}
928 
929 	DeleteSkyTexGroup(SK);
930 
931 	info->tex[WSKY_North]  = BuildFace(block, WSKY_North,  info, what_pal);
932 	info->tex[WSKY_East]   = BuildFace(block, WSKY_East,   info, what_pal);
933 	info->tex[WSKY_Top]    = BuildFace(block, WSKY_Top,    info, what_pal);
934 	info->tex[WSKY_Bottom] = BuildFace(block, WSKY_Bottom, info, what_pal);
935 
936 	// optimisation: can share side textures when narrow
937 
938 	info->tex[WSKY_South] = SkyIsNarrow(sky_image) ? info->tex[WSKY_North] :
939 						 BuildFace(block, WSKY_South, info, what_pal );
940 
941 	info->tex[WSKY_West]  = SkyIsNarrow(sky_image) ? info->tex[WSKY_East] :
942 						 BuildFace(block, WSKY_West, info, what_pal );
943 
944 	delete block;
945 
946 	if (what_pal_cached)
947 		W_DoneWithLump(what_pal);
948 
949 	return SK;
950 }
951 
952 
RGL_PreCacheSky(void)953 void RGL_PreCacheSky(void)
954 {
955 	// TODO
956 }
957 
958 //--- editor settings ---
959 // vi:ts=4:sw=4:noexpandtab
960