UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
r_image.cpp
Go to the documentation of this file.
1 
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
33 
34 #define IMAGE_ARRAY_SIZE 128
35 typedef struct imageArray_s {
37  struct imageArray_s* next;
38 } imageArray_t;
39 
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 */
51 
52 /* lense flares */
54 
55 #define MAX_TEXTURE_SIZE 8192
56 
62 {
63  int i;
64  image_t* image;
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 
85 void R_ImageList_f (void)
86 {
87  int i, cnt;
88  image_t* image;
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 
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 
172 void R_ScaleTexture (const unsigned* in, int inwidth, int inheight, unsigned* out, int outwidth, int outheight)
173 {
174  int i;
175  unsigned p1[MAX_TEXTURE_SIZE], p2[MAX_TEXTURE_SIZE];
176  const unsigned fracstep = inwidth * 0x10000 / outwidth;
177 
178  assert(outwidth <= MAX_TEXTURE_SIZE);
179 
180  unsigned frac = fracstep >> 2;
181  for (i = 0; i < outwidth; i++) {
182  p1[i] = 4 * (frac >> 16);
183  frac += fracstep;
184  }
185  frac = 3 * (fracstep >> 2);
186  for (i = 0; i < outwidth; i++) {
187  p2[i] = 4 * (frac >> 16);
188  frac += fracstep;
189  }
190 
191  for (i = 0; i < outheight; i++, out += outwidth) {
192  const int index = inwidth * (int) ((i + 0.25) * inheight / outheight);
193  const unsigned* inrow = in + index;
194  const int index2 = inwidth * (int) ((i + 0.75) * inheight / outheight);
195  const unsigned* inrow2 = in + index2;
196 
197  assert(index < inwidth * inheight);
198  assert(index2 < inwidth * inheight);
199 
200  for (int j = 0; j < outwidth; j++) {
201  const byte* pix1 = (const byte*) inrow + p1[j];
202  const byte* pix2 = (const byte*) inrow + p2[j];
203  const byte* pix3 = (const byte*) inrow2 + p1[j];
204  const byte* pix4 = (const byte*) inrow2 + p2[j];
205  ((byte*) (out + j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
206  ((byte*) (out + j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
207  ((byte*) (out + j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
208  ((byte*) (out + j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2;
209  }
210  }
211 }
212 
220 void R_GetScaledTextureSize (int width, int height, int* scaledWidth, int* scaledHeight)
221 {
222  for (*scaledWidth = 1; *scaledWidth < width; *scaledWidth <<= 1) {}
223  for (*scaledHeight = 1; *scaledHeight < height; *scaledHeight <<= 1) {}
224 
225  while (*scaledWidth > r_config.maxTextureSize || *scaledHeight > r_config.maxTextureSize) {
226  *scaledWidth >>= 1;
227  *scaledHeight >>= 1;
228  }
229 
230  if (*scaledWidth > MAX_TEXTURE_SIZE)
231  *scaledWidth = MAX_TEXTURE_SIZE;
232  else if (*scaledWidth < 1)
233  *scaledWidth = 1;
234 
235  if (*scaledHeight > MAX_TEXTURE_SIZE)
236  *scaledHeight = MAX_TEXTURE_SIZE;
237  else if (*scaledHeight < 1)
238  *scaledHeight = 1;
239 }
240 
242 {
243  return type == it_pic || type == it_worldrelated;
244 }
245 
246 #ifdef GL_VERSION_ES_CM_1_0
247 inline static void R_StripAlpha (const unsigned* data, unsigned* buffer, const unsigned count)
248 {
249  for (unsigned i = 0; i < count; ++i, ++data) {
250  memmove(((byte*)buffer) + i * 3, (const byte*)data, 3);
251  }
252 }
253 #endif
254 
262 void R_UploadTexture (const unsigned* data, int width, int height, image_t* image)
263 {
264  const bool mipmap = (image->type != it_pic && image->type != it_worldrelated && image->type != it_chars);
265  const bool clamp = R_IsClampedImageType(image->type);
266 #ifdef GL_VERSION_ES_CM_1_0
267  GLint texFormat = GL_RGB;
268 #else
270 #endif
271 
272  /* scan the texture for any non-255 alpha */
273  int i;
274  const byte* scan;
275  const int c = width * height;
276  /* set scan to the first alpha byte */
277  for (i = 0, scan = ((const byte*) data) + 3; i < c; ++i, scan += 4) {
278  if (*scan != 255) {
279 #ifdef GL_VERSION_ES_CM_1_0
280  texFormat = GL_RGBA;
281 #else
283 #endif
284  image->has_alpha = true;
285  break;
286  }
287  }
288 
289  int scaledWidth, scaledHeight;
290  R_GetScaledTextureSize(width, height, &scaledWidth, &scaledHeight);
291 
292  image->upload_width = scaledWidth; /* after power of 2 and scales */
293  image->upload_height = scaledHeight;
294 
295  unsigned* tmpBuff = nullptr;
296  /* some images need very little attention (pics, fonts, etc..) */
297  if (!mipmap && scaledWidth == width && scaledHeight == height) {
298  /* no mipmapping for these images to save memory */
299  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
300  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
301  if (clamp) {
302  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
303  R_CheckError();
304  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305  R_CheckError();
306  }
307 #ifdef GL_VERSION_ES_CM_1_0
308  /* Strip alpha, see comments below */
309  if (texFormat == GL_RGB) {
310  const int count = scaledWidth * scaledHeight;
311  tmpBuff = Mem_PoolAllocTypeN(unsigned, count, vid_imagePool);
312  R_StripAlpha(data, tmpBuff, count);
313  }
314  GLenum bFormat = texFormat;
315 #else
316  GLenum bFormat = GL_RGBA;
317 #endif
318  glTexImage2D(GL_TEXTURE_2D, 0, texFormat, scaledWidth, scaledHeight, 0, bFormat, GL_UNSIGNED_BYTE, tmpBuff ? tmpBuff : data);
319  if (tmpBuff)
320  Mem_Free(tmpBuff);
321  return;
322  }
323 
324  if (scaledWidth != width || scaledHeight != height) { /* whereas others need to be scaled */
325  tmpBuff = Mem_PoolAllocTypeN(unsigned, scaledWidth * scaledHeight, vid_imagePool);
326  R_ScaleTexture(data, width, height, tmpBuff, scaledWidth, scaledHeight);
327  }
328 
329 #ifdef GL_VERSION_ES_CM_1_0
330  /*
331  * According to https://www.khronos.org/opengles/sdk/1.1/docs/man/glTexImage2D.xml
332  * texture format *must* match the data format, which we *always* read as RGBA,
333  * so we need to strip the alpha if we want to upload as RGB, and we want: memory *is*
334  * usually a concern on an ES.
335  */
336  if (texFormat == GL_RGB) {
337  const int count = scaledWidth * scaledHeight;
338  if (!tmpBuff)
339  tmpBuff = Mem_PoolAllocTypeN(unsigned, count, vid_imagePool);
340  R_StripAlpha(c == count ? data : tmpBuff, tmpBuff, count);
341  }
342 #endif
343 
344  /* and mipmapped */
345  if (mipmap) {
346  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_min);
347  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
348  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
349  if (r_config.anisotropic) {
350  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic);
351  R_CheckError();
352  }
353 #ifndef GL_VERSION_ES_CM_1_0
355  glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, r_texture_lod->value);
356  R_CheckError();
357  }
358 #endif
359  } else {
360  if (r_config.anisotropic) {
361  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
362  R_CheckError();
363  }
364  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_max);
365  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
366  }
367  R_CheckError();
368 
369  if (clamp) {
370  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
371  R_CheckError();
372  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
373  R_CheckError();
374  }
375 
376 #ifdef GL_VERSION_ES_CM_1_0
377  const GLenum bFormat = texFormat;
378 #else
379  const GLenum bFormat = GL_RGBA;
380 #endif
381  glTexImage2D(GL_TEXTURE_2D, 0, texFormat, scaledWidth, scaledHeight, 0, bFormat, GL_UNSIGNED_BYTE, tmpBuff ? tmpBuff : data);
382  R_CheckError();
383 
384  if (tmpBuff)
385  Mem_Free(tmpBuff);
386 }
387 
391 void R_SoftenTexture (byte* in, int width, int height, int bpp)
392 {
393  const int size = width * height * bpp;
394 
395  /* soften into a copy of the original image, as in-place would be incorrect */
396  byte* const out = Mem_PoolAllocTypeN(byte, size, vid_imagePool);
397  if (!out)
398  Com_Error(ERR_FATAL, "R_SoftenTexture: failed on allocation of %i bytes", width * height * bpp);
399 
400  memcpy(out, in, size);
401 
402  for (int i = 1; i < height - 1; i++) {
403  for (int j = 1; j < width - 1; j++) {
404  const byte* src = in + ((i * width) + j) * bpp; /* current input pixel */
405 
406  const byte* u = (src - (width * bpp)); /* and it's neighbors */
407  const byte* d = (src + (width * bpp));
408  const byte* l = (src - (1 * bpp));
409  const byte* r = (src + (1 * bpp));
410 
411  byte* dest = out + ((i * width) + j) * bpp; /* current output pixel */
412 
413  for (int k = 0; k < bpp; k++)
414  dest[k] = (u[k] + d[k] + l[k] + r[k]) / 4;
415  }
416  }
417 
418  /* copy the softened image over the input image, and free it */
419  memcpy(in, out, size);
420  Mem_Free(out);
421 }
422 
423 void R_UploadAlpha (const image_t* image, const byte* alphaData)
424 {
425  R_BindTexture(image->texnum);
426 
427  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image->width, image->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, alphaData);
428  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_config.gl_filter_max);
429  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_config.gl_filter_max);
430  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
431  if (image->type == it_wrappic)
432  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
433 }
434 
435 static inline void R_DeleteImage (image_t* image)
436 {
437  const unsigned int hash = Com_HashKey(image->name, MAX_IMAGEHASH);
438  for (image_t** anchor = &imageHash[hash]; *anchor; anchor = &(*anchor)->hash_next) {
439  if (Q_streq((*anchor)->name, image->name)) {
440  HASH_Delete(anchor);
441  break;
442  }
443  }
444 
445  /* free it */
446  glDeleteTextures(1, &image->texnum);
447  R_CheckError();
448 
449  OBJZERO(*image);
450 }
451 
452 image_t* R_GetImage (const char* name)
453 {
454  image_t* image;
455  const unsigned hash = Com_HashKey(name, MAX_IMAGEHASH);
456 
457  /* look for it */
458  for (image = imageHash[hash]; image; image = image->hash_next)
459  if (Q_streq(name, image->name))
460  return image;
461 
462  return nullptr;
463 }
464 
475 image_t* R_LoadImageData (const char* name, const byte* pic, int width, int height, imagetype_t type)
476 {
477  image_t* image;
479  int i;
480  size_t len;
481  unsigned hash;
482 
483  len = strlen(name);
484  if (len >= sizeof(image->name))
485  Com_Error(ERR_DROP, "R_LoadImageData: \"%s\" is too long", name);
486  if (len == 0)
487  Com_Error(ERR_DROP, "R_LoadImageData: name is empty");
488 
489  /* look for it */
490  image = R_GetImage(name);
491  if (image) {
492  assert(image->texnum);
493  Com_Printf("R_LoadImageData: image '%s' is already uploaded\n", name);
494  return image;
495  }
496 
497  /* find a free image_t, using a wicked for()-loop */
498  FOR_EACH_IMAGE(i, image, images) {
499  if (!image->texnum)
500  break;
501  }
502 
503  if (i == r_numImages) {
504  /* Did we run out of space in the current array? Add a new array chunk */
505  if (r_numImages % IMAGE_ARRAY_SIZE == 0) {
506  for (images = &r_images; images->next;)
507  images = images->next;
508  images->next = Mem_AllocType(imageArray_t);
509  image = &images->next->images[0];
510  }
511  r_numImages++;
512  }
513  OBJZERO(*image);
514  image->material = defaultMaterial;
515  image->has_alpha = false;
516  image->type = type;
517  image->width = width;
518  image->height = height;
519 
521 #ifdef COMPILE_UNITTESTS
522  {
523  static int texnum = 0;
524  image->texnum = ++texnum;
525  }
526 #else
527  glGenTextures(1, &image->texnum);
528 #endif
529 
530  Q_strncpyz(image->name, name, sizeof(image->name));
531  /* drop extension */
532  if (len >= 4 && image->name[len - 4] == '.') {
533  image->name[len - 4] = '\0';
534  Com_Printf("Image with extension: '%s'\n", name);
535  }
536 
537  hash = Com_HashKey(image->name, MAX_IMAGEHASH);
538  HASH_Add(imageHash, image, hash);
539 
540  if (pic) {
541  R_BindTexture(image->texnum);
542  R_UploadTexture((const unsigned* ) pic, width, height, image);
543  }
544  return image;
545 }
546 
547 image_t* R_RenderToTexture (const char* name, int x, int y, int w, int h)
548 {
549  image_t* img = R_GetImage(name);
550  const bool dimensionDiffer = img != nullptr && img->width != w && img->height != h;
551  if (img == nullptr || dimensionDiffer) {
552  if (dimensionDiffer) {
553  R_DeleteImage(img);
554  }
555  byte* const buf = Mem_PoolAllocTypeN(byte, w * h * 4, vid_imagePool);
556  img = R_LoadImageData(name, buf, w, h, it_effect);
557  Mem_Free(buf);
558  }
559 
560  glFlush();
561 #ifndef GL_VERSION_ES_CM_1_0
562  glReadBuffer(GL_BACK);
563 #endif
565  R_BindTexture(img->texnum);
566  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, w, h, 0);
567 
568  return img;
569 }
570 
576 {
577  if (!img)
578  return;
579 
580  img->type = type;
581  R_BindTexture(img->texnum);
582 
583  if (R_IsClampedImageType(type)) {
584  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
585  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
586  } else {
587  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
588  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
589  }
590 }
591 
603 image_t* R_FindImage (const char* pname, imagetype_t type)
604 {
605  char lname[MAX_QPATH];
606  image_t* image;
607  SDL_Surface* surf;
608 
609  if (!pname || !pname[0])
610  Com_Error(ERR_FATAL, "R_FindImage: nullptr name");
611 
612  /* drop extension */
613  Com_StripExtension(pname, lname, sizeof(lname));
614 
615  image = R_GetImage(lname);
616  if (image) {
617  /* Warn if game tries to use same image with different texture mapping modes */
618  if (R_IsClampedImageType(image->type) != R_IsClampedImageType(type)) {
619  Com_Printf("Warning: inconsistent usage of image %s (%i,%i)\n", image->name, image->type, type);
620  R_ChangeImageType(image, type);
621  R_ChangeImageType(image->normalmap, type);
622  R_ChangeImageType(image->roughnessmap, type);
623  R_ChangeImageType(image->specularmap, type);
624  R_ChangeImageType(image->glowmap, type);
625  }
626  return image;
627  }
628 
629  if ((surf = Img_LoadImage(lname))) {
630  image = R_LoadImageData(lname, (byte*)surf->pixels, surf->w, surf->h, type);
631  SDL_FreeSurface(surf);
632  if (image->type == it_world) {
633  image->normalmap = R_FindImage(va("%s_nm", image->name), it_normalmap);
634  if (image->normalmap == r_noTexture)
635  image->normalmap = nullptr;
636  }
637  if (image->type != it_glowmap) {
638  image->glowmap = R_FindImage(va("%s_gm", image->name), it_glowmap);
639  if (image->glowmap == r_noTexture)
640  image->glowmap = nullptr;
641  }
642  if (image->type != it_normalmap) {
643  image->normalmap = R_FindImage(va("%s_nm", image->name), it_normalmap);
644  if (image->normalmap == r_noTexture)
645  image->normalmap = nullptr;
646  }
647  if (image->type != it_specularmap) {
648  image->specularmap = R_FindImage(va("%s_sm", image->name), it_specularmap);
649  if (image->specularmap == r_noTexture)
650  image->specularmap = nullptr;
651  }
652  if (image->type != it_roughnessmap) {
653  image->roughnessmap = R_FindImage(va("%s_rm", image->name), it_roughnessmap);
654  if (image->roughnessmap == r_noTexture)
655  image->roughnessmap = nullptr;
656  }
657  }
658 
659  /* no fitting texture found */
660  if (!image)
661  image = r_noTexture;
662 
663  return image;
664 }
665 
673 const image_t* R_FindPics (const char* name)
674 {
675  const image_t* image = R_FindImage(va("pics/%s", name), it_pic);
676  if (image == r_noTexture)
677  return nullptr;
678  return image;
679 }
680 
681 bool R_ImageExists (const char* pname, ...)
682 {
683  char const* const* const types = Img_GetImageTypes();
684  char filename[MAX_QPATH];
685  va_list ap;
686 
687  va_start(ap, pname);
688  Q_vsnprintf(filename, sizeof(filename), pname, ap);
689  va_end(ap);
690 
691  for (int i = 0; types[i]; i++) {
692  if (FS_CheckFile("%s.%s", filename, types[i]) != -1)
693  return true;
694  }
695  return false;
696 }
697 
702 int R_GetImageIndex (image_t* imagePtr)
703 {
705 
706  for (images = &r_images; images; images = images->next) {
707  if (imagePtr >= &images->images[0] && imagePtr <= &images->images[IMAGE_ARRAY_SIZE - 1])
708  return imagePtr - &images->images[0];
709  }
710 
711  return -1;
712 }
713 
719 {
721 
722  if (i >= r_numImages || i < 0)
723  return nullptr;
724 
725  for (images = &r_images; i >= IMAGE_ARRAY_SIZE; i -= IMAGE_ARRAY_SIZE)
726  images = images->next;
727 
728  return &images->images[i];
729 }
730 
735 void R_FreeImage (image_t* image)
736 {
737  /* free image slot */
738  if (!image || !image->texnum)
739  return;
740 
741  /* also free the several maps if they are loaded */
742  if (image->normalmap)
743  R_DeleteImage(image->normalmap);
744  if (image->glowmap)
745  R_DeleteImage(image->glowmap);
746  if (image->roughnessmap)
747  R_DeleteImage(image->roughnessmap);
748  if (image->specularmap)
749  R_DeleteImage(image->specularmap);
750  R_DeleteImage(image);
751 }
752 
757 void R_FreeWorldImages (void)
758 {
759  int i;
760  image_t* image;
762 
763  R_CheckError();
764  /* Wicked for()-loop (tm) */
765  FOR_EACH_IMAGE(i, image, images) {
766  if (image->type < it_world)
767  continue; /* keep them */
768 
769  /* free it */
770  R_FreeImage(image);
771  }
772 }
773 
774 void R_InitImages (void)
775 {
776  int i;
777 
778  r_numImages = 0;
779 
780  for (i = 0; i < MAX_ENVMAPTEXTURES; i++) {
781  r_envmaptextures[i] = R_FindImage(va("pics/envmaps/envmap_%i", i), it_effect);
782  if (r_envmaptextures[i] == r_noTexture)
783  Com_Error(ERR_FATAL, "Could not load environment map %i", i);
784  }
785 
786  for (i = 0; i < NUM_FLARETEXTURES; i++) {
787  r_flaretextures[i] = R_FindImage(va("pics/flares/flare_%i", i), it_effect);
788  if (r_flaretextures[i] == r_noTexture)
789  Com_Error(ERR_FATAL, "Could not load lens flare %i", i);
790  }
791 }
792 
796 void R_ShutdownImages (void)
797 {
798  int i;
799  image_t* image;
801 
802  R_CheckError();
803  /* Wicked for()-loop (tm) */
804  FOR_EACH_IMAGE(i, image, images) {
805  if (!image->texnum)
806  continue; /* free image_t slot */
807  R_DeleteImage(image);
808  }
809  OBJZERO(imageHash);
810  OBJZERO(r_images);
811  r_numImages = 0;
812 }
813 
814 static void R_ReloadImageData (image_t* image)
815 {
816  SDL_Surface* surf;
817  if (image == r_noTexture || !image || !image->texnum)
818  return;
819 
820  surf = Img_LoadImage(image->name);
821  if (!surf) {
822  Com_Printf("R_ReloadImageData: unable to load image %s\n", image->name);
823  surf = SDL_CreateRGBSurface(0, image->width, image->height, 32,
824  0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
825  SDL_FillRect(surf, nullptr, 0x99ff33ff); /* A random color */
826  }
827  glGenTextures(1, &image->texnum);
828  R_BindTexture(image->texnum);
829  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
830  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
831  R_UploadTexture((unsigned* )surf->pixels, surf->w, surf->h, image);
832  SDL_FreeSurface(surf);
833 }
834 
835 void R_ReloadImages (void)
836 {
837  int i;
838  image_t* image;
840 
841  R_CheckError();
842  glEnable(GL_TEXTURE_2D);
843  FOR_EACH_IMAGE(i, image, images) {
844  if (i % 5 == 0) {
845  SCR_DrawLoadingScreen(false, i * 100 / r_numImages);
846  }
847  R_ReloadImageData(image);
849  R_ReloadImageData(image->glowmap);
852  }
853 }
854 
855 typedef struct {
856  const char* name;
857  int minimize, maximize;
859 
861  {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, /* no filtering, no mipmaps */
862  {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, /* bilinear filtering, no mipmaps */
863  {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, /* no filtering, mipmaps */
864  {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, /* bilinear filtering, mipmaps */
865  {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, /* trilinear filtering, mipmaps */
866  {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} /* bilinear filtering, trilinear filtering, mipmaps */
867 };
868 
869 void R_TextureMode (const char* string)
870 {
871  int i;
872  const size_t size = lengthof(gl_texture_modes);
873  const glTextureMode_t* mode = nullptr;
874  for (i = 0; i < size; i++) {
875  mode = &gl_texture_modes[i];
876  if (!Q_strcasecmp(mode->name, string))
877  break;
878  }
879 
880  if (mode == nullptr) {
881  Com_Printf("bad filter name\n");
882  return;
883  }
884 
887 
888  /* Wicked for()-loop (tm) */
889  image_t* image;
890  imageArray_t* images;
891  FOR_EACH_IMAGE(i, image, images) {
892  if (image->type == it_pic || image->type == it_worldrelated || image->type == it_chars)
893  continue; /* no mipmaps */
894 
895  R_BindTexture(image->texnum);
896  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode->minimize);
897  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode->maximize);
898  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic);
899  R_CheckError();
900  }
901 }
902 
903 #ifdef GL_VERSION_ES_CM_1_0
904 void R_TextureSolidMode (const char* string)
905 {
906 }
907 void R_TextureAlphaMode (const char* string)
908 {
909 }
910 #else
911 typedef struct {
912  const char* name;
913  int mode;
914 } gltmode_t;
915 
916 static const gltmode_t gl_alpha_modes[] = {
917  {"GL_RGBA", GL_RGBA},
918  {"GL_RGBA8", GL_RGBA8},
919  {"GL_RGB5_A1", GL_RGB5_A1},
920  {"GL_RGBA4", GL_RGBA4},
921  {"GL_RGBA2", GL_RGBA2},
922  {"GL_LUMINANCE4_ALPHA4", GL_LUMINANCE4_ALPHA4},
923  {"GL_LUMINANCE6_ALPHA2", GL_LUMINANCE6_ALPHA2},
924  {"GL_LUMINANCE8_ALPHA8", GL_LUMINANCE8_ALPHA8},
925  {"GL_LUMINANCE12_ALPHA4", GL_LUMINANCE12_ALPHA4},
926  {"GL_LUMINANCE12_ALPHA12", GL_LUMINANCE12_ALPHA12},
927  {"GL_LUMINANCE16_ALPHA16", GL_LUMINANCE16_ALPHA16}
928 };
929 
930 
931 void R_TextureAlphaMode (const char* string)
932 {
933  const size_t size = lengthof(gl_alpha_modes);
934 
935  for (int i = 0; i < size; i++) {
936  const gltmode_t* mode = &gl_alpha_modes[i];
937  if (!Q_strcasecmp(mode->name, string)) {
938  r_config.gl_alpha_format = mode->mode;
939  return;
940  }
941  }
942 
943  Com_Printf("bad alpha texture mode name (%s)\n", string);
944 }
945 
946 static const gltmode_t gl_solid_modes[] = {
947  {"GL_RGB", GL_RGB},
948  {"GL_RGB8", GL_RGB8},
949  {"GL_RGB5", GL_RGB5},
950  {"GL_RGB4", GL_RGB4},
951  {"GL_R3_G3_B2", GL_R3_G3_B2},
952  {"GL_RGB2", GL_RGB2_EXT},
953  {"GL_RGB4", GL_RGB4_EXT},
954  {"GL_RGB5", GL_RGB5_EXT},
955  {"GL_RGB8", GL_RGB8_EXT},
956  {"GL_RGB10", GL_RGB10_EXT},
957  {"GL_RGB12", GL_RGB12_EXT},
958  {"GL_RGB16", GL_RGB16_EXT},
959  {"GL_LUMINANCE", GL_LUMINANCE},
960  {"GL_LUMINANCE4", GL_LUMINANCE4},
961  {"GL_LUMINANCE8", GL_LUMINANCE8},
962  {"GL_LUMINANCE12", GL_LUMINANCE12},
963  {"GL_LUMINANCE16", GL_LUMINANCE16}
964 };
965 
966 void R_TextureSolidMode (const char* string)
967 {
968  const size_t size = lengthof(gl_solid_modes);
969 
970  for (int i = 0; i < size; i++) {
971  const gltmode_t* mode = &gl_solid_modes[i];
972  if (!Q_strcasecmp(mode->name, string)) {
973  r_config.gl_solid_format = mode->mode;
974  return;
975  }
976  }
977 
978  Com_Printf("bad solid texture mode name (%s)\n", string);
979 }
980 #endif
static void R_ChangeImageType(image_t *img, imagetype_t type)
Set up new image type and change texturemapping paramenters accordingly.
Definition: r_image.cpp:575
static void R_ReloadImageData(image_t *image)
Definition: r_image.cpp:814
bool Q_strnull(const char *string)
Definition: shared.h:138
static const gltmode_t gl_solid_modes[]
Definition: r_image.cpp:946
int gl_solid_format
Definition: r_local.h:189
const char * name
Definition: r_image.cpp:912
int gl_filter_max
Definition: r_local.h:196
image_t * R_RenderToTexture(const char *name, int x, int y, int w, int h)
Definition: r_image.cpp:547
static image_t * imageHash[MAX_IMAGEHASH]
Definition: r_image.cpp:32
struct imageArray_s * next
Definition: r_image.cpp:37
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
Definition: shared.cpp:535
void R_SoftenTexture(byte *in, int width, int height, int bpp)
Applies blurring to a texture.
Definition: r_image.cpp:391
int FS_CheckFile(const char *fmt,...)
Just returns the filelength and -1 if the file wasn't found.
Definition: files.cpp:298
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
cvar_t * r_texture_lod
Definition: r_main.cpp:65
imagetype_t
Definition: r_image.h:41
void R_LoadImage(const char *name, byte **pic, int *width, int *height)
Generic image-data loading fucntion.
Definition: r_image.cpp:152
#define HASH_Delete(anchor)
Definition: common.h:439
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
int r_numImages
Definition: r_image.cpp:41
void SCR_DrawLoadingScreen(bool string, int percent)
Precache and loading screen at startup.
Definition: cl_screen.cpp:100
static bool R_IsClampedImageType(imagetype_t type)
Definition: r_image.cpp:241
void Com_StripExtension(const char *in, char *out, const size_t size)
Removes the file extension from a filename.
Definition: shared.cpp:259
const char * filename
Definition: ioapi.h:41
const GLenum *typedef GLint
Definition: r_gl.h:205
const image_t * R_FindPics(const char *name)
Searches for an image in the image array.
Definition: r_image.cpp:673
void R_ScaleTexture(const unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight)
Definition: r_image.cpp:172
float value
Definition: cvar.h:80
image_t * R_LoadImageData(const char *name, const byte *pic, int width, int height, imagetype_t type)
Creates a new image from RGBA data. Stores it in the gltextures array and also uploads it...
Definition: r_image.cpp:475
local graphics definitions
void R_ShutdownImages(void)
Definition: r_image.cpp:796
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
rconfig_t r_config
Definition: r_main.cpp:47
image_t * r_envmaptextures[MAX_ENVMAPTEXTURES]
Definition: r_image.cpp:50
image_t * r_noTexture
Definition: r_main.cpp:51
Definition: r_image.h:45
int width
Definition: r_image.h:64
int integer
Definition: cvar.h:81
const GLuint *typedef GLuint *typedef GLenum
Definition: r_gl.h:190
voidpf void * buf
Definition: ioapi.h:42
#define ERR_FATAL
Definition: common.h:210
void R_TextureMode(const char *string)
Definition: r_image.cpp:869
#define FOR_EACH_IMAGE(i, image, imageArray)
Definition: r_image.cpp:44
char const *const * Img_GetImageTypes(void)
Definition: images.cpp:46
static void R_DeleteImage(image_t *image)
Definition: r_image.cpp:435
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
void R_ImageList_f(void)
Shows all loaded images.
Definition: r_image.cpp:85
image_t * R_FindImage(const char *pname, imagetype_t type)
Finds or loads the given image.
Definition: r_image.cpp:603
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
imagetype_t type
Definition: r_image.h:63
materialStage_t * stages
Definition: r_material.h:164
image_t images[IMAGE_ARRAY_SIZE]
Definition: r_image.cpp:36
image_t * R_GetImageAtIndex(int i)
Returns an image pointer from the r_images linked list, as if r_images would be a plain contiguous ar...
Definition: r_image.cpp:718
#define ERR_DROP
Definition: common.h:211
GLsizei size
Definition: r_gl.h:152
void R_ImageClearMaterials(void)
Free previously loaded materials and their stages.
Definition: r_image.cpp:61
#define OBJZERO(obj)
Definition: shared.h:178
static const gltmode_t gl_alpha_modes[]
Definition: r_image.cpp:916
struct image_s * roughnessmap
Definition: r_image.h:72
char name[MAX_QPATH]
Definition: r_image.h:62
struct imageArray_s imageArray_t
static wrapCache_t * hash[MAX_WRAP_HASH]
Definition: r_font.cpp:86
int R_GetImageIndex(image_t *imagePtr)
Returns an index of the image pointer in the r_images linked list, as if r_images would be a plain co...
Definition: r_image.cpp:702
void R_UploadAlpha(const image_t *image, const byte *alphaData)
Definition: r_image.cpp:423
#define MAX_ENVMAPTEXTURES
Definition: r_image.h:111
#define NUM_FLARETEXTURES
Definition: r_image.h:114
SDL_Surface * Img_LoadImage(char const *name)
Loads the specified image from the game filesystem and populates the provided SDL_Surface.
Definition: images.cpp:435
int gl_compressed_alpha_format
Definition: r_local.h:193
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texels)
Definition: gldummy.cpp:17
material_t defaultMaterial
Definition: r_material.cpp:34
#define Q_strcasecmp(a, b)
Definition: shared.h:131
int mode
Definition: r_image.cpp:913
void R_TextureSolidMode(const char *string)
Definition: r_image.cpp:966
#define HASH_Add(hash, elem, index)
Definition: common.h:430
struct image_s * glowmap
Definition: r_image.h:70
#define MAX_TEXTURE_SIZE
Definition: r_image.cpp:55
QGL_EXTERN GLenum GLuint * dest
Definition: r_gl.h:101
struct image_s * specularmap
Definition: r_image.h:71
int maxTextureSize
Definition: r_local.h:167
QGL_EXTERN GLuint index
Definition: r_gl.h:110
QGL_EXTERN GLuint count
Definition: r_gl.h:99
#define MAX_GL_TEXTURES
Definition: r_image.h:76
void glTexParameterf(GLenum target, GLenum pname, GLfloat param)
Definition: gldummy.cpp:13
#define Mem_PoolAllocTypeN(type, n, pool)
Definition: mem.h:42
struct image_s * hash_next
Definition: r_image.h:73
bool anisotropic
Definition: r_local.h:185
#define texunit_diffuse
Definition: r_state.h:68
void R_ReloadImages(void)
Definition: r_image.cpp:835
int gl_filter_min
Definition: r_local.h:195
image_t * r_flaretextures[NUM_FLARETEXTURES]
Definition: r_image.cpp:53
#define MAX_QPATH
Definition: filesys.h:40
QGL_EXTERN GLint i
Definition: r_gl.h:113
bool lod_bias
Definition: r_local.h:198
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
static const glTextureMode_t gl_texture_modes[]
Definition: r_image.cpp:860
int upload_width
Definition: r_image.h:65
int gl_alpha_format
Definition: r_local.h:190
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
void R_TextureAlphaMode(const char *string)
Definition: r_image.cpp:931
int height
Definition: r_image.h:64
void R_GetScaledTextureSize(int width, int height, int *scaledWidth, int *scaledHeight)
Calculates the texture size that should be used to upload the texture data.
Definition: r_image.cpp:220
const char * name
Definition: r_image.cpp:856
#define Mem_Free(ptr)
Definition: mem.h:35
const char int mode
Definition: ioapi.h:41
unsigned int Com_HashKey(const char *name, int hashsize)
returns hash key for a string
Definition: shared.cpp:336
Error checking function.
void glGenTextures(GLsizei n, GLuint *textures)
Definition: gldummy.cpp:22
void R_FreeWorldImages(void)
Any image that is a mesh or world texture will be removed here.
Definition: r_image.cpp:757
void R_FreeImage(image_t *image)
Free the image and its assigned maps (roughness, normal, specular, glow - if there are any) ...
Definition: r_image.cpp:735
memPool_t * vid_imagePool
Definition: cl_main.cpp:88
void R_InitImages(void)
Definition: r_image.cpp:774
#define Mem_AllocType(type)
Definition: mem.h:39
int32_t maxAnisotropic
Definition: r_local.h:184
#define lengthof(x)
Definition: shared.h:105
#define R_CheckError()
Definition: r_error.h:30
GLsizei const GLvoid * data
Definition: r_gl.h:152
struct image_s * normalmap
Definition: r_image.h:69
bool has_alpha
Definition: r_image.h:67
#define Q_streq(a, b)
Definition: shared.h:136
imageArray_t r_images
Definition: r_image.cpp:40
image_t * R_GetImage(const char *name)
Definition: r_image.cpp:452
void R_UploadTexture(const unsigned *data, int width, int height, image_t *image)
Uploads the opengl texture to the server.
Definition: r_image.cpp:262
#define MAX_IMAGEHASH
Definition: r_image.cpp:31
uint8_t byte
Definition: ufotypes.h:34
material_t material
Definition: r_image.h:68
static struct mdfour * m
Definition: md4.cpp:35
#define IMAGE_ARRAY_SIZE
Definition: r_image.cpp:34
#define R_BindTexture(tn)
Definition: r_state.h:184
int upload_height
Definition: r_image.h:65
bool R_ImageExists(const char *pname,...)
Definition: r_image.cpp:681
struct materialStage_s * next
Definition: r_material.h:147
int gl_compressed_solid_format
Definition: r_local.h:192
bool R_SelectTexture(gltexunit_t *texunit)
Returns false if the texunit is not supported.
Definition: r_state.cpp:40
GLuint texnum
Definition: r_image.h:66