1 /**
2  * @file
3  */
4 
5 /*
6 Copyright (C) 1997-2001 Id Software, Inc.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 
23 */
24 
25 #include "r_local.h"
26 #include "r_error.h"
27 #include "r_geoscape.h"
28 #include "../../shared/images.h"
29 #include "../cl_screen.h"
30 
31 #define MAX_IMAGEHASH 256
32 static image_t* imageHash[MAX_IMAGEHASH];
33 
34 #define IMAGE_ARRAY_SIZE 128
35 typedef struct imageArray_s {
36 	image_t images[IMAGE_ARRAY_SIZE];
37 	struct imageArray_s* next;
38 } imageArray_t;
39 
40 imageArray_t r_images;
41 int r_numImages;
42 
43 /* Wicked for()-loop to go through all images in the r_images linked list. Parameters are: (int i, image_t* image, imageArray_t* imageArray) */
44 #define FOR_EACH_IMAGE(i, image, imageArray) \
45 	for (i = 0, imageArray = &r_images, image = &imageArray->images[0]; i < r_numImages; i++, image++, \
46 			(i % IMAGE_ARRAY_SIZE) ? 0 : (image = (imageArray = imageArray->next) ? &imageArray->images[0] : nullptr))
47 
48 
49 /* generic environment map */
50 image_t* r_envmaptextures[MAX_ENVMAPTEXTURES];
51 
52 /* lense flares */
53 image_t* r_flaretextures[NUM_FLARETEXTURES];
54 
55 #define MAX_TEXTURE_SIZE 8192
56 
57 /**
58  * @brief Free previously loaded materials and their stages
59  * @sa R_LoadMaterials
60  */
R_ImageClearMaterials(void)61 void R_ImageClearMaterials (void)
62 {
63 	int i;
64 	image_t* image;
65 	imageArray_t* images;
66 
67 	/* clear previously loaded materials */
68 	FOR_EACH_IMAGE(i, image, images) {
69 		material_t* m = &image->material;
70 		materialStage_t* s = m->stages;
71 
72 		while (s) {  /* free the stages chain */
73 			materialStage_t* ss = s->next;
74 			Mem_Free(s);
75 			s = ss;
76 		}
77 
78 		*m = defaultMaterial;
79 	}
80 }
81 
82 /**
83  * @brief Shows all loaded images
84  */
R_ImageList_f(void)85 void R_ImageList_f (void)
86 {
87 	int i, cnt;
88 	image_t* image;
89 	imageArray_t* images;
90 	int texels;
91 
92 	Com_Printf("------------------\n");
93 	texels = 0;
94 	cnt = 0;
95 
96 	FOR_EACH_IMAGE(i, image, images) {
97 		const char* type;
98 		if (!image->texnum)
99 			continue;
100 		cnt++;
101 		texels += image->upload_width * image->upload_height;
102 		switch (image->type) {
103 		case it_effect:
104 			type = "EF";
105 			break;
106 		case it_skin:
107 			type = "SK";
108 			break;
109 		case it_wrappic:
110 			type = "WR";
111 			break;
112 		case it_chars:
113 			type = "CH";
114 			break;
115 		case it_static:
116 			type = "ST";
117 			break;
118 		case it_normalmap:
119 			type = "NM";
120 			break;
121 		case it_material:
122 			type = "MA";
123 			break;
124 		case it_lightmap:
125 			type = "LM";
126 			break;
127 		case it_world:
128 			type = "WO";
129 			break;
130 		case it_pic:
131 			type = "PI";
132 			break;
133 		default:
134 			type = "  ";
135 			break;
136 		}
137 
138 		Com_Printf("%s %4i %4i RGB: %5i idx: %s\n", type, image->upload_width, image->upload_height, image->texnum, image->name);
139 	}
140 	Com_Printf("Total textures: %i/%i (max textures: %i)\n", cnt, r_numImages, MAX_GL_TEXTURES);
141 	Com_Printf("Total texel count (not counting mipmaps): %i\n", texels);
142 }
143 
144 /**
145  * @brief Generic image-data loading fucntion.
146  * @param[in] name (Full) pathname to the image to load. Extension (if given) will be ignored.
147  * @param[out] pic Image data.
148  * @param[out] width Width of the loaded image.
149  * @param[out] height Height of the loaded image.
150  * @sa R_FindImage
151  */
R_LoadImage(const char * name,byte ** pic,int * width,int * height)152 void R_LoadImage (const char* name, byte** pic, int* width, int* height)
153 {
154 	char filenameTemp[MAX_QPATH];
155 	SDL_Surface* surf;
156 
157 	if (Q_strnull(name))
158 		Com_Error(ERR_FATAL, "R_LoadImage: nullptr name");
159 
160 	Com_StripExtension(name, filenameTemp, sizeof(filenameTemp));
161 
162 	if ((surf = Img_LoadImage(filenameTemp))) {
163 		const size_t size = (surf->w * surf->h) * 4;
164 		*width = surf->w;
165 		*height = surf->h;
166 		*pic = Mem_PoolAllocTypeN(byte, size, vid_imagePool);
167 		memcpy(*pic, surf->pixels, size);
168 		SDL_FreeSurface(surf);
169 	}
170 }
171 
R_ScaleTexture(const unsigned * in,int inwidth,int inheight,unsigned * out,int outwidth,int outheight)172 void R_ScaleTexture (const unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight)
173 {
174 	int i, j;
175 	unsigned frac;
176 	unsigned p1[MAX_TEXTURE_SIZE], p2[MAX_TEXTURE_SIZE];
177 	const unsigned fracstep = inwidth * 0x10000 / outwidth;
178 
179 	assert(outwidth <= MAX_TEXTURE_SIZE);
180 
181 	frac = fracstep >> 2;
182 	for (i = 0; i < outwidth; i++) {
183 		p1[i] = 4 * (frac >> 16);
184 		frac += fracstep;
185 	}
186 	frac = 3 * (fracstep >> 2);
187 	for (i = 0; i < outwidth; i++) {
188 		p2[i] = 4 * (frac >> 16);
189 		frac += fracstep;
190 	}
191 
192 	for (i = 0; i < outheight; i++, out += outwidth) {
193 		const int index = inwidth * (int) ((i + 0.25) * inheight / outheight);
194 		const unsigned *inrow = in + index;
195 		const int index2 = inwidth * (int) ((i + 0.75) * inheight / outheight);
196 		const unsigned *inrow2 = in + index2;
197 
198 		assert(index < inwidth * inheight);
199 		assert(index2 < inwidth * inheight);
200 
201 		for (j = 0; j < outwidth; j++) {
202 			const byte* pix1 = (const byte*) inrow + p1[j];
203 			const byte* pix2 = (const byte*) inrow + p2[j];
204 			const byte* pix3 = (const byte*) inrow2 + p1[j];
205 			const byte* pix4 = (const byte*) inrow2 + p2[j];
206 			((byte*) (out + j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
207 			((byte*) (out + j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
208 			((byte*) (out + j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
209 			((byte*) (out + j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2;
210 		}
211 	}
212 }
213 
214 /**
215  * @brief Calculates the texture size that should be used to upload the texture data
216  * @param[in] width The width of the source texture data
217  * @param[in] height The heigt of the source texture data
218  * @param[out] scaledWidth The resulting width - can be the same as the given @c width
219  * @param[out] scaledHeight The resulting height - can be the same as the given @c height
220  */
R_GetScaledTextureSize(int width,int height,int * scaledWidth,int * scaledHeight)221 void R_GetScaledTextureSize (int width, int height, int* scaledWidth, int* scaledHeight)
222 {
223 	for (*scaledWidth = 1; *scaledWidth < width; *scaledWidth <<= 1) {}
224 	for (*scaledHeight = 1; *scaledHeight < height; *scaledHeight <<= 1) {}
225 
226 	while (*scaledWidth > r_config.maxTextureSize || *scaledHeight > r_config.maxTextureSize) {
227 		*scaledWidth >>= 1;
228 		*scaledHeight >>= 1;
229 	}
230 
231 	if (*scaledWidth > MAX_TEXTURE_SIZE)
232 		*scaledWidth = MAX_TEXTURE_SIZE;
233 	else if (*scaledWidth < 1)
234 		*scaledWidth = 1;
235 
236 	if (*scaledHeight > MAX_TEXTURE_SIZE)
237 		*scaledHeight = MAX_TEXTURE_SIZE;
238 	else if (*scaledHeight < 1)
239 		*scaledHeight = 1;
240 }
241 
R_IsClampedImageType(imagetype_t type)242 inline static bool R_IsClampedImageType (imagetype_t type)
243 {
244 	return type == it_pic || type == it_worldrelated;
245 }
246 
247 /**
248  * @brief Uploads the opengl texture to the server
249  * @param[in] data Must be in RGBA format
250  * @param width Width of the image
251  * @param height Height of the image
252  * @param[in,out] image Pointer to the image structure to initialize
253  */
R_UploadTexture(const unsigned * data,int width,int height,image_t * image)254 void R_UploadTexture (const unsigned *data, int width, int height, image_t* image)
255 {
256 	unsigned *scaled = nullptr;
257 	int scaledWidth, scaledHeight;
258 #ifdef GL_VERSION_ES_CM_1_0
259 	GLint texFormat = GL_RGB;
260 #else
261 	GLint texFormat = r_config.gl_compressed_solid_format ? r_config.gl_compressed_solid_format : r_config.gl_solid_format;
262 #endif
263 	int i, c;
264 	const byte* scan;
265 	const bool mipmap = (image->type != it_pic && image->type != it_worldrelated && image->type != it_chars);
266 	const bool clamp = R_IsClampedImageType(image->type);
267 
268 	/* scan the texture for any non-255 alpha */
269 	c = width * height;
270 	/* set scan to the first alpha byte */
271 	for (i = 0, scan = ((const byte*) data) + 3; i < c; i++, scan += 4) {
272 		if (*scan != 255) {
273 #ifdef GL_VERSION_ES_CM_1_0
274 			texFormat = GL_RGBA;
275 #else
276 			texFormat = r_config.gl_compressed_alpha_format ? r_config.gl_compressed_alpha_format : r_config.gl_alpha_format;
277 #endif
278 			image->has_alpha = true;
279 			break;
280 		}
281 	}
282 
283 	R_GetScaledTextureSize(width, height, &scaledWidth, &scaledHeight);
284 
285 	image->upload_width = scaledWidth;	/* after power of 2 and scales */
286 	image->upload_height = scaledHeight;
287 
288 	/* some images need very little attention (pics, fonts, etc..) */
289 	if (!mipmap && scaledWidth == width && scaledHeight == height) {
290 		/* no mipmapping for these images to save memory */
291 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
292 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
293 		if (clamp) {
294 			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
295 			R_CheckError();
296 			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
297 			R_CheckError();
298 		}
299 		glTexImage2D(GL_TEXTURE_2D, 0, texFormat, scaledWidth, scaledHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
300 		return;
301 	}
302 
303 	if (scaledWidth != width || scaledHeight != height) {  /* whereas others need to be scaled */
304 		scaled = Mem_PoolAllocTypeN(unsigned, scaledWidth * scaledHeight, vid_imagePool);
305 		R_ScaleTexture(data, width, height, scaled, scaledWidth, scaledHeight);
306 	}
307 
308 	/* and mipmapped */
309 	if (mipmap) {
310 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_min);
311 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
312 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
313 		if (r_config.anisotropic) {
314 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic);
315 			R_CheckError();
316 		}
317 #ifndef GL_VERSION_ES_CM_1_0
318 		if (r_texture_lod->integer && r_config.lod_bias) {
319 			glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, r_texture_lod->value);
320 			R_CheckError();
321 		}
322 #endif
323 	} else {
324 		if (r_config.anisotropic) {
325 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
326 			R_CheckError();
327 		}
328 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_max);
329 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
330 	}
331 	R_CheckError();
332 
333 	if (clamp) {
334 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
335 		R_CheckError();
336 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
337 		R_CheckError();
338 	}
339 
340 	glTexImage2D(GL_TEXTURE_2D, 0, texFormat, scaledWidth, scaledHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled ? scaled : data);
341 	R_CheckError();
342 
343 	if (scaled)
344 		Mem_Free(scaled);
345 }
346 
347 /**
348  * @brief Applies blurring to a texture
349  */
R_SoftenTexture(byte * in,int width,int height,int bpp)350 void R_SoftenTexture (byte* in, int width, int height, int bpp)
351 {
352 	int i, j, k;
353 	const int size = width * height * bpp;
354 
355 	/* soften into a copy of the original image, as in-place would be incorrect */
356 	byte* const out = Mem_PoolAllocTypeN(byte, size, vid_imagePool);
357 	if (!out)
358 		Com_Error(ERR_FATAL, "R_SoftenTexture: failed on allocation of %i bytes", width * height * bpp);
359 
360 	memcpy(out, in, size);
361 
362 	for (i = 1; i < height - 1; i++) {
363 		for (j = 1; j < width - 1; j++) {
364 			const byte* src = in + ((i * width) + j) * bpp;  /* current input pixel */
365 
366 			const byte* u = (src - (width * bpp));  /* and it's neighbors */
367 			const byte* d = (src + (width * bpp));
368 			const byte* l = (src - (1 * bpp));
369 			const byte* r = (src + (1 * bpp));
370 
371 			byte* dest = out + ((i * width) + j) * bpp;  /* current output pixel */
372 
373 			for (k = 0; k < bpp; k++)
374 				dest[k] = (u[k] + d[k] + l[k] + r[k]) / 4;
375 		}
376 	}
377 
378 	/* copy the softened image over the input image, and free it */
379 	memcpy(in, out, size);
380 	Mem_Free(out);
381 }
382 
R_UploadAlpha(const image_t * image,const byte * alphaData)383 void R_UploadAlpha (const image_t* image, const byte* alphaData)
384 {
385 	R_BindTexture(image->texnum);
386 
387 	glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image->width, image->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, alphaData);
388 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_max);
389 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
390 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
391 	if (image->type == it_wrappic)
392 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
393 }
394 
R_DeleteImage(image_t * image)395 static inline void R_DeleteImage (image_t* image)
396 {
397 	const unsigned int hash = Com_HashKey(image->name, MAX_IMAGEHASH);
398 	for (image_t** anchor = &imageHash[hash]; *anchor; anchor = &(*anchor)->hash_next) {
399 		if (Q_streq((*anchor)->name, image->name)) {
400 			HASH_Delete(anchor);
401 			break;
402 		}
403 	}
404 
405 	/* free it */
406 	glDeleteTextures(1, &image->texnum);
407 	R_CheckError();
408 
409 	OBJZERO(*image);
410 }
411 
R_GetImage(const char * name)412 image_t* R_GetImage (const char* name)
413 {
414 	image_t* image;
415 	const unsigned hash = Com_HashKey(name, MAX_IMAGEHASH);
416 
417 	/* look for it */
418 	for (image = imageHash[hash]; image; image = image->hash_next)
419 		if (Q_streq(name, image->name))
420 			return image;
421 
422 	return nullptr;
423 }
424 
425 /**
426  * @brief Creates a new image from RGBA data. Stores it in the gltextures array
427  * and also uploads it.
428  * @note This is also used as an entry point for the generated r_noTexture
429  * @param[in] name The name of the newly created image
430  * @param[in] pic The RGBA data of the image
431  * @param[in] width The width of the image (power of two, please)
432  * @param[in] height The height of the image (power of two, please)
433  * @param[in] type The image type @sa imagetype_t
434  */
R_LoadImageData(const char * name,const byte * pic,int width,int height,imagetype_t type)435 image_t* R_LoadImageData (const char* name, const byte* pic, int width, int height, imagetype_t type)
436 {
437 	image_t* image;
438 	imageArray_t* images;
439 	int i;
440 	size_t len;
441 	unsigned hash;
442 
443 	len = strlen(name);
444 	if (len >= sizeof(image->name))
445 		Com_Error(ERR_DROP, "R_LoadImageData: \"%s\" is too long", name);
446 	if (len == 0)
447 		Com_Error(ERR_DROP, "R_LoadImageData: name is empty");
448 
449 	/* look for it */
450 	image = R_GetImage(name);
451 	if (image) {
452 		assert(image->texnum);
453 		Com_Printf("R_LoadImageData: image '%s' is already uploaded\n", name);
454 		return image;
455 	}
456 
457 	/* find a free image_t, using a wicked for()-loop */
458 	FOR_EACH_IMAGE(i, image, images) {
459 		if (!image->texnum)
460 			break;
461 	}
462 
463 	if (i == r_numImages) {
464 		/* Did we run out of space in the current array? Add a new array chunk */
465 		if (r_numImages % IMAGE_ARRAY_SIZE == 0) {
466 			for (images = &r_images; images->next;)
467 				images = images->next;
468 			images->next = Mem_AllocType(imageArray_t);
469 			image = &images->next->images[0];
470 		}
471 		r_numImages++;
472 	}
473 	OBJZERO(*image);
474 	image->material = defaultMaterial;
475 	image->has_alpha = false;
476 	image->type = type;
477 	image->width = width;
478 	image->height = height;
479 
480 	/** @todo Instead of this hack, unit tests' build should link to the dummy GL driver */
481 #ifdef COMPILE_UNITTESTS
482 	{
483 		static int texnum = 0;
484 		image->texnum = ++texnum;
485 	}
486 #else
487 	glGenTextures(1, &image->texnum);
488 #endif
489 
490 	Q_strncpyz(image->name, name, sizeof(image->name));
491 	/* drop extension */
492 	if (len >= 4 && image->name[len - 4] == '.') {
493 		image->name[len - 4] = '\0';
494 		Com_Printf("Image with extension: '%s'\n", name);
495 	}
496 
497 	hash = Com_HashKey(image->name, MAX_IMAGEHASH);
498 	HASH_Add(imageHash, image, hash);
499 
500 	if (pic) {
501 		R_BindTexture(image->texnum);
502 		R_UploadTexture((const unsigned *) pic, width, height, image);
503 	}
504 	return image;
505 }
506 
R_RenderToTexture(const char * name,int x,int y,int w,int h)507 image_t* R_RenderToTexture (const char* name, int x, int y, int w, int h)
508 {
509 	image_t* img = R_GetImage(name);
510 	const bool dimensionDiffer = img != nullptr && img->width != w && img->height != h;
511 	if (img == nullptr || dimensionDiffer) {
512 		if (dimensionDiffer) {
513 			R_DeleteImage(img);
514 		}
515 		byte* const buf = Mem_PoolAllocTypeN(byte, w * h * 4, vid_imagePool);
516 		img = R_LoadImageData(name, buf, w, h, it_effect);
517 		Mem_Free(buf);
518 	}
519 
520 	glFlush();
521 #ifndef GL_VERSION_ES_CM_1_0
522 	glReadBuffer(GL_BACK);
523 #endif
524 	R_SelectTexture(&texunit_diffuse);
525 	R_BindTexture(img->texnum);
526 	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, w, h, 0);
527 
528 	return img;
529 }
530 
531 /**
532  * @brief Set up new image type and change texturemapping paramenters accordingly
533  * @note Mipmapping mode is not updated
534  */
R_ChangeImageType(image_t * img,imagetype_t type)535 static void R_ChangeImageType(image_t* img, imagetype_t type)
536 {
537 	if (!img)
538 		return;
539 
540 	img->type = type;
541 	R_BindTexture(img->texnum);
542 
543 	if (R_IsClampedImageType(type)) {
544 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
545 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
546 	} else {
547 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
548 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
549 	}
550 }
551 
552 /**
553  * @brief Finds or loads the given image
554  * @sa R_RegisterImage
555  * @param[in] pname Image name Path relative to the game dir (e.g. textures/tex_common/nodraw)
556  * @param[in] type The type of the image. This has influence on image filters and texture
557  * parameters when uploading the image data
558  * @note the image name has to be at least 5 chars long
559  * @sa R_LoadTGA
560  * @sa R_LoadJPG
561  * @sa R_LoadPNG
562  */
R_FindImage(const char * pname,imagetype_t type)563 image_t* R_FindImage (const char* pname, imagetype_t type)
564 {
565 	char lname[MAX_QPATH];
566 	image_t* image;
567 	SDL_Surface* surf;
568 
569 	if (!pname || !pname[0])
570 		Com_Error(ERR_FATAL, "R_FindImage: nullptr name");
571 
572 	/* drop extension */
573 	Com_StripExtension(pname, lname, sizeof(lname));
574 
575 	image = R_GetImage(lname);
576 	if (image) {
577 		/* Warn if game tries to use same image with different texture mapping modes */
578 		if (R_IsClampedImageType(image->type) != R_IsClampedImageType(type)) { /** @todo should also check the mipmapping */
579 			Com_Printf("Warning: inconsistent usage of image %s (%i,%i)\n", image->name, image->type, type);
580 			R_ChangeImageType(image, type);
581 			R_ChangeImageType(image->normalmap, type);
582 			R_ChangeImageType(image->roughnessmap, type);
583 			R_ChangeImageType(image->specularmap, type);
584 			R_ChangeImageType(image->glowmap, type);
585 		}
586 		return image;
587 	}
588 
589 	if ((surf = Img_LoadImage(lname))) {
590 		image = R_LoadImageData(lname, (byte*)surf->pixels, surf->w, surf->h, type);
591 		SDL_FreeSurface(surf);
592 		if (image->type == it_world) {
593 			image->normalmap = R_FindImage(va("%s_nm", image->name), it_normalmap);
594 			if (image->normalmap == r_noTexture)
595 				image->normalmap = nullptr;
596 		}
597 		if (image->type != it_glowmap) {
598 			image->glowmap = R_FindImage(va("%s_gm", image->name), it_glowmap);
599 			if (image->glowmap == r_noTexture)
600 				image->glowmap = nullptr;
601 		}
602 		if (image->type != it_normalmap) {
603 			image->normalmap = R_FindImage(va("%s_nm", image->name), it_normalmap);
604 			if (image->normalmap == r_noTexture)
605 				image->normalmap = nullptr;
606 		}
607 		if (image->type != it_specularmap) {
608 			image->specularmap = R_FindImage(va("%s_sm", image->name), it_specularmap);
609 			if (image->specularmap == r_noTexture)
610 				image->specularmap = nullptr;
611 		}
612 		if (image->type != it_roughnessmap) {
613 			image->roughnessmap = R_FindImage(va("%s_rm", image->name), it_roughnessmap);
614 			if (image->roughnessmap == r_noTexture)
615 				image->roughnessmap = nullptr;
616 		}
617 	}
618 
619 	/* no fitting texture found */
620 	if (!image)
621 		image = r_noTexture;
622 
623 	return image;
624 }
625 
626 /**
627  * @brief Searches for an image in the image array
628  * @param[in] name The name of the image relative to pics/
629  * @note name may not be null and has to be longer than 4 chars
630  * @return nullptr on error or image_t pointer on success
631  * @sa R_FindImage
632  */
R_FindPics(const char * name)633 const image_t* R_FindPics (const char* name)
634 {
635 	const image_t* image = R_FindImage(va("pics/%s", name), it_pic);
636 	if (image == r_noTexture)
637 		return nullptr;
638 	return image;
639 }
640 
R_ImageExists(const char * pname,...)641 bool R_ImageExists (const char* pname, ...)
642 {
643 	char const* const* const types = Img_GetImageTypes();
644 	int i;
645 	char filename[MAX_QPATH];
646 	va_list ap;
647 
648 	va_start(ap, pname);
649 	Q_vsnprintf(filename, sizeof(filename), pname, ap);
650 	va_end(ap);
651 
652 	for (i = 0; types[i]; i++) {
653 		if (FS_CheckFile("%s.%s", filename, types[i]) != -1)
654 			return true;
655 	}
656 	return false;
657 }
658 
659 /**
660  * @brief Returns an index of the image pointer in the r_images linked list, as if r_images would be a plain contiguous array
661  * @param imagePtr The image pointer
662  */
R_GetImageIndex(image_t * imagePtr)663 int R_GetImageIndex (image_t* imagePtr)
664 {
665 	imageArray_t* images;
666 
667 	for (images = &r_images; images; images = images->next) {
668 		if (imagePtr >= &images->images[0] && imagePtr <= &images->images[IMAGE_ARRAY_SIZE - 1])
669 			return imagePtr - &images->images[0];
670 	}
671 
672 	return -1;
673 }
674 
675 /**
676  * @brief Returns an image pointer from the r_images linked list, as if r_images would be a plain contiguous array
677  * @param i The image index inside r_images
678  */
R_GetImageAtIndex(int i)679 image_t* R_GetImageAtIndex (int i)
680 {
681 	imageArray_t* images;
682 
683 	if (i >= r_numImages || i < 0)
684 		return nullptr;
685 
686 	for (images = &r_images; i >= IMAGE_ARRAY_SIZE; i -= IMAGE_ARRAY_SIZE)
687 		images = images->next;
688 
689 	return &images->images[i];
690 }
691 
692 /**
693  * @brief Free the image and its assigned maps (roughness, normal, specular, glow - if there are any)
694  * @param image The image that should be freed
695  */
R_FreeImage(image_t * image)696 void R_FreeImage (image_t* image)
697 {
698 	/* free image slot */
699 	if (!image || !image->texnum)
700 		return;
701 
702 	/* also free the several maps if they are loaded */
703 	if (image->normalmap)
704 		R_DeleteImage(image->normalmap);
705 	if (image->glowmap)
706 		R_DeleteImage(image->glowmap);
707 	if (image->roughnessmap)
708 		R_DeleteImage(image->roughnessmap);
709 	if (image->specularmap)
710 		R_DeleteImage(image->specularmap);
711 	R_DeleteImage(image);
712 }
713 
714 /**
715  * @brief Any image that is a mesh or world texture will be removed here
716  * @sa R_ShutdownImages
717  */
R_FreeWorldImages(void)718 void R_FreeWorldImages (void)
719 {
720 	int i;
721 	image_t* image;
722 	imageArray_t* images;
723 
724 	R_CheckError();
725 	/* Wicked for()-loop (tm) */
726 	FOR_EACH_IMAGE(i, image, images) {
727 		if (image->type < it_world)
728 			continue;			/* keep them */
729 
730 		/* free it */
731 		R_FreeImage(image);
732 	}
733 }
734 
R_InitImages(void)735 void R_InitImages (void)
736 {
737 	int i;
738 
739 	r_numImages = 0;
740 
741 	for (i = 0; i < MAX_ENVMAPTEXTURES; i++) {
742 		r_envmaptextures[i] = R_FindImage(va("pics/envmaps/envmap_%i", i), it_effect);
743 		if (r_envmaptextures[i] == r_noTexture)
744 			Com_Error(ERR_FATAL, "Could not load environment map %i", i);
745 	}
746 
747 	for (i = 0; i < NUM_FLARETEXTURES; i++) {
748 		r_flaretextures[i] = R_FindImage(va("pics/flares/flare_%i", i), it_effect);
749 		if (r_flaretextures[i] == r_noTexture)
750 			Com_Error(ERR_FATAL, "Could not load lens flare %i", i);
751 	}
752 }
753 
754 /**
755  * @sa R_FreeWorldImages
756  */
R_ShutdownImages(void)757 void R_ShutdownImages (void)
758 {
759 	int i;
760 	image_t* image;
761 	imageArray_t* images;
762 
763 	R_CheckError();
764 	/* Wicked for()-loop (tm) */
765 	FOR_EACH_IMAGE(i, image, images) {
766 		if (!image->texnum)
767 			continue;			/* free image_t slot */
768 		R_DeleteImage(image);
769 	}
770 	OBJZERO(imageHash);
771 	OBJZERO(r_images);
772 	r_numImages = 0;
773 }
774 
R_ReloadImageData(image_t * image)775 static void R_ReloadImageData (image_t* image)
776 {
777 	SDL_Surface* surf;
778 	if (image == r_noTexture || !image || !image->texnum)
779 		return;
780 
781 	surf = Img_LoadImage(image->name);
782 	if (!surf) {
783 		Com_Printf("R_ReloadImageData: unable to load image %s\n", image->name);
784 		surf = SDL_CreateRGBSurface(0, image->width, image->height, 32,
785 					0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
786 		SDL_FillRect(surf, nullptr, 0x99ff33ff); /* A random color */
787 	}
788 	glGenTextures(1, &image->texnum);
789 	R_BindTexture(image->texnum);
790 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
791 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
792 	R_UploadTexture((unsigned *)surf->pixels, surf->w, surf->h, image);
793 	SDL_FreeSurface(surf);
794 }
795 
R_ReloadImages(void)796 void R_ReloadImages (void)
797 {
798 	int i;
799 	image_t* image;
800 	imageArray_t* images;
801 
802 	R_CheckError();
803 	glEnable(GL_TEXTURE_2D);
804 	FOR_EACH_IMAGE(i, image, images) {
805 		if (i % 5 == 0) {
806 			SCR_DrawLoadingScreen(false, i * 100 / r_numImages);
807 		}
808 		R_ReloadImageData(image);
809 		R_ReloadImageData(image->normalmap);
810 		R_ReloadImageData(image->glowmap);
811 		R_ReloadImageData(image->specularmap);
812 		R_ReloadImageData(image->roughnessmap);
813 	}
814 }
815 
816 typedef struct {
817 	const char* name;
818 	int minimize, maximize;
819 } glTextureMode_t;
820 
821 static const glTextureMode_t gl_texture_modes[] = {
822 	{"GL_NEAREST", GL_NEAREST, GL_NEAREST}, /* no filtering, no mipmaps */
823 	{"GL_LINEAR", GL_LINEAR, GL_LINEAR}, /* bilinear filtering, no mipmaps */
824 	{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, /* no filtering, mipmaps */
825 	{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, /* bilinear filtering, mipmaps */
826 	{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, /* trilinear filtering, mipmaps */
827 	{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} /* bilinear filtering, trilinear filtering, mipmaps */
828 };
829 
R_TextureMode(const char * string)830 void R_TextureMode (const char* string)
831 {
832 	int i;
833 	image_t* image;
834 	imageArray_t* images;
835 	const size_t size = lengthof(gl_texture_modes);
836 	const glTextureMode_t* mode;
837 
838 	mode = nullptr;
839 	for (i = 0; i < size; i++) {
840 		mode = &gl_texture_modes[i];
841 		if (!Q_strcasecmp(mode->name, string))
842 			break;
843 	}
844 
845 	if (mode == nullptr) {
846 		Com_Printf("bad filter name\n");
847 		return;
848 	}
849 
850 	r_config.gl_filter_min = mode->minimize;
851 	r_config.gl_filter_max = mode->maximize;
852 
853 	/* Wicked for()-loop (tm) */
854 	FOR_EACH_IMAGE(i, image, images) {
855 		if (image->type == it_pic || image->type == it_worldrelated || image->type == it_chars)
856 			continue; /* no mipmaps */
857 
858 		R_BindTexture(image->texnum);
859 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode->minimize);
860 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode->maximize);
861 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic);
862 		R_CheckError();
863 	}
864 }
865 
866 #ifdef GL_VERSION_ES_CM_1_0
R_TextureSolidMode(const char * string)867 void R_TextureSolidMode (const char* string)
868 {
869 }
R_TextureAlphaMode(const char * string)870 void R_TextureAlphaMode (const char* string)
871 {
872 }
873 #else
874 typedef struct {
875 	const char* name;
876 	int mode;
877 } gltmode_t;
878 
879 static const gltmode_t gl_alpha_modes[] = {
880 	{"GL_RGBA", GL_RGBA},
881 	{"GL_RGBA8", GL_RGBA8},
882 	{"GL_RGB5_A1", GL_RGB5_A1},
883 	{"GL_RGBA4", GL_RGBA4},
884 	{"GL_RGBA2", GL_RGBA2},
885 	{"GL_LUMINANCE4_ALPHA4", GL_LUMINANCE4_ALPHA4},
886 	{"GL_LUMINANCE6_ALPHA2", GL_LUMINANCE6_ALPHA2},
887 	{"GL_LUMINANCE8_ALPHA8", GL_LUMINANCE8_ALPHA8},
888 	{"GL_LUMINANCE12_ALPHA4", GL_LUMINANCE12_ALPHA4},
889 	{"GL_LUMINANCE12_ALPHA12", GL_LUMINANCE12_ALPHA12},
890 	{"GL_LUMINANCE16_ALPHA16", GL_LUMINANCE16_ALPHA16}
891 };
892 
893 
R_TextureAlphaMode(const char * string)894 void R_TextureAlphaMode (const char* string)
895 {
896 	int i;
897 	const size_t size = lengthof(gl_alpha_modes);
898 
899 	for (i = 0; i < size; i++) {
900 		const gltmode_t* mode = &gl_alpha_modes[i];
901 		if (!Q_strcasecmp(mode->name, string)) {
902 			r_config.gl_alpha_format = mode->mode;
903 			return;
904 		}
905 	}
906 
907 	Com_Printf("bad alpha texture mode name (%s)\n", string);
908 }
909 
910 static const gltmode_t gl_solid_modes[] = {
911 	{"GL_RGB", GL_RGB},
912 	{"GL_RGB8", GL_RGB8},
913 	{"GL_RGB5", GL_RGB5},
914 	{"GL_RGB4", GL_RGB4},
915 	{"GL_R3_G3_B2", GL_R3_G3_B2},
916 	{"GL_RGB2", GL_RGB2_EXT},
917 	{"GL_RGB4", GL_RGB4_EXT},
918 	{"GL_RGB5", GL_RGB5_EXT},
919 	{"GL_RGB8", GL_RGB8_EXT},
920 	{"GL_RGB10", GL_RGB10_EXT},
921 	{"GL_RGB12", GL_RGB12_EXT},
922 	{"GL_RGB16", GL_RGB16_EXT},
923 	{"GL_LUMINANCE", GL_LUMINANCE},
924 	{"GL_LUMINANCE4", GL_LUMINANCE4},
925 	{"GL_LUMINANCE8", GL_LUMINANCE8},
926 	{"GL_LUMINANCE12", GL_LUMINANCE12},
927 	{"GL_LUMINANCE16", GL_LUMINANCE16}
928 };
929 
R_TextureSolidMode(const char * string)930 void R_TextureSolidMode (const char* string)
931 {
932 	int i;
933 	const size_t size = lengthof(gl_solid_modes);
934 
935 	for (i = 0; i < size; i++) {
936 		const gltmode_t* mode = &gl_solid_modes[i];
937 		if (!Q_strcasecmp(mode->name, string)) {
938 			r_config.gl_solid_format = mode->mode;
939 			return;
940 		}
941 	}
942 
943 	Com_Printf("bad solid texture mode name (%s)\n", string);
944 }
945 #endif
946