doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Image_program.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 /*
30 
31 all uncompressed
32 uncompressed normal maps
33 
34 downsample images
35 
36 16 meg Dynamic cache
37 
38 Anisotropic texturing
39 
40 Trilinear on all
41 Trilinear on normal maps, bilinear on others
42 Bilinear on all
43 
44 
45 Manager
46 
47 ->List
48 ->Print
49 ->Reload( bool force )
50 
51 */
52 
53 #include "../idlib/precompiled.h"
54 #pragma hdrstop
55 
56 // tr_imageprogram.c
57 
58 #include "tr_local.h"
59 
60 /*
61 
62 Anywhere that an image name is used (diffusemaps, bumpmaps, specularmaps, lights, etc),
63 an imageProgram can be specified.
64 
65 This allows load time operations, like heightmap-to-normalmap conversion and image
66 composition, to be automatically handled in a way that supports timestamped reloads.
67 
68 */
69 
70 /*
71 =================
72 R_HeightmapToNormalMap
73 
74 it is not possible to convert a heightmap into a normal map
75 properly without knowing the texture coordinate stretching.
76 We can assume constant and equal ST vectors for walls, but not for characters.
77 =================
78 */
79 static void R_HeightmapToNormalMap( byte *data, int width, int height, float scale ) {
80  int i, j;
81  byte *depth;
82 
83  scale = scale / 256;
84 
85  // copy and convert to grey scale
86  j = width * height;
87  depth = (byte *)R_StaticAlloc( j );
88  for ( i = 0 ; i < j ; i++ ) {
89  depth[i] = ( data[i*4] + data[i*4+1] + data[i*4+2] ) / 3;
90  }
91 
92  idVec3 dir, dir2;
93  for ( i = 0 ; i < height ; i++ ) {
94  for ( j = 0 ; j < width ; j++ ) {
95  int d1, d2, d3, d4;
96  int a1, a2, a3, a4;
97 
98  // FIXME: look at five points?
99 
100  // look at three points to estimate the gradient
101  a1 = d1 = depth[ ( i * width + j ) ];
102  a2 = d2 = depth[ ( i * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
103  a3 = d3 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + j ) ];
104  a4 = d4 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
105 
106  d2 -= d1;
107  d3 -= d1;
108 
109  dir[0] = -d2 * scale;
110  dir[1] = -d3 * scale;
111  dir[2] = 1;
112  dir.NormalizeFast();
113 
114  a1 -= a3;
115  a4 -= a3;
116 
117  dir2[0] = -a4 * scale;
118  dir2[1] = a1 * scale;
119  dir2[2] = 1;
120  dir2.NormalizeFast();
121 
122  dir += dir2;
123  dir.NormalizeFast();
124 
125  a1 = ( i * width + j ) * 4;
126  data[ a1 + 0 ] = (byte)(dir[0] * 127 + 128);
127  data[ a1 + 1 ] = (byte)(dir[1] * 127 + 128);
128  data[ a1 + 2 ] = (byte)(dir[2] * 127 + 128);
129  data[ a1 + 3 ] = 255;
130  }
131  }
132 
133 
134  R_StaticFree( depth );
135 }
136 
137 
138 /*
139 =================
140 R_ImageScale
141 =================
142 */
143 static void R_ImageScale( byte *data, int width, int height, float scale[4] ) {
144  int i, j;
145  int c;
146 
147  c = width * height * 4;
148 
149  for ( i = 0 ; i < c ; i++ ) {
150  j = (byte)(data[i] * scale[i&3]);
151  if ( j < 0 ) {
152  j = 0;
153  } else if ( j > 255 ) {
154  j = 255;
155  }
156  data[i] = j;
157  }
158 }
159 
160 /*
161 =================
162 R_InvertAlpha
163 =================
164 */
165 static void R_InvertAlpha( byte *data, int width, int height ) {
166  int i;
167  int c;
168 
169  c = width * height* 4;
170 
171  for ( i = 0 ; i < c ; i+=4 ) {
172  data[i+3] = 255 - data[i+3];
173  }
174 }
175 
176 /*
177 =================
178 R_InvertColor
179 =================
180 */
181 static void R_InvertColor( byte *data, int width, int height ) {
182  int i;
183  int c;
184 
185  c = width * height* 4;
186 
187  for ( i = 0 ; i < c ; i+=4 ) {
188  data[i+0] = 255 - data[i+0];
189  data[i+1] = 255 - data[i+1];
190  data[i+2] = 255 - data[i+2];
191  }
192 }
193 
194 
195 /*
196 ===================
197 R_AddNormalMaps
198 
199 ===================
200 */
201 static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
202  int i, j;
203  byte *newMap;
204 
205  // resample pic2 to the same size as pic1
206  if ( width2 != width1 || height2 != height1 ) {
207  newMap = R_Dropsample( data2, width2, height2, width1, height1 );
208  data2 = newMap;
209  } else {
210  newMap = NULL;
211  }
212 
213  // add the normal change from the second and renormalize
214  for ( i = 0 ; i < height1 ; i++ ) {
215  for ( j = 0 ; j < width1 ; j++ ) {
216  byte *d1, *d2;
217  idVec3 n;
218  float len;
219 
220  d1 = data1 + ( i * width1 + j ) * 4;
221  d2 = data2 + ( i * width1 + j ) * 4;
222 
223  n[0] = ( d1[0] - 128 ) / 127.0;
224  n[1] = ( d1[1] - 128 ) / 127.0;
225  n[2] = ( d1[2] - 128 ) / 127.0;
226 
227  // There are some normal maps that blend to 0,0,0 at the edges
228  // this screws up compression, so we try to correct that here by instead fading it to 0,0,1
229  len = n.LengthFast();
230  if ( len < 1.0f ) {
231  n[2] = idMath::Sqrt(1.0 - (n[0]*n[0]) - (n[1]*n[1]));
232  }
233 
234  n[0] += ( d2[0] - 128 ) / 127.0;
235  n[1] += ( d2[1] - 128 ) / 127.0;
236  n.Normalize();
237 
238  d1[0] = (byte)(n[0] * 127 + 128);
239  d1[1] = (byte)(n[1] * 127 + 128);
240  d1[2] = (byte)(n[2] * 127 + 128);
241  d1[3] = 255;
242  }
243  }
244 
245  if ( newMap ) {
246  R_StaticFree( newMap );
247  }
248 }
249 
250 /*
251 ================
252 R_SmoothNormalMap
253 ================
254 */
255 static void R_SmoothNormalMap( byte *data, int width, int height ) {
256  byte *orig;
257  int i, j, k, l;
258  idVec3 normal;
259  byte *out;
260  static float factors[3][3] = {
261  { 1, 1, 1 },
262  { 1, 1, 1 },
263  { 1, 1, 1 }
264  };
265 
266  orig = (byte *)R_StaticAlloc( width * height * 4 );
267  memcpy( orig, data, width * height * 4 );
268 
269  for ( i = 0 ; i < width ; i++ ) {
270  for ( j = 0 ; j < height ; j++ ) {
271  normal = vec3_origin;
272  for ( k = -1 ; k < 2 ; k++ ) {
273  for ( l = -1 ; l < 2 ; l++ ) {
274  byte *in;
275 
276  in = orig + ( ((j+l)&(height-1))*width + ((i+k)&(width-1)) ) * 4;
277 
278  // ignore 000 and -1 -1 -1
279  if ( in[0] == 0 && in[1] == 0 && in[2] == 0 ) {
280  continue;
281  }
282  if ( in[0] == 128 && in[1] == 128 && in[2] == 128 ) {
283  continue;
284  }
285 
286  normal[0] += factors[k+1][l+1] * ( in[0] - 128 );
287  normal[1] += factors[k+1][l+1] * ( in[1] - 128 );
288  normal[2] += factors[k+1][l+1] * ( in[2] - 128 );
289  }
290  }
291  normal.Normalize();
292  out = data + ( j * width + i ) * 4;
293  out[0] = (byte)(128 + 127 * normal[0]);
294  out[1] = (byte)(128 + 127 * normal[1]);
295  out[2] = (byte)(128 + 127 * normal[2]);
296  }
297  }
298 
299  R_StaticFree( orig );
300 }
301 
302 
303 /*
304 ===================
305 R_ImageAdd
306 
307 ===================
308 */
309 static void R_ImageAdd( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
310  int i, j;
311  int c;
312  byte *newMap;
313 
314  // resample pic2 to the same size as pic1
315  if ( width2 != width1 || height2 != height1 ) {
316  newMap = R_Dropsample( data2, width2, height2, width1, height1 );
317  data2 = newMap;
318  } else {
319  newMap = NULL;
320  }
321 
322 
323  c = width1 * height1 * 4;
324 
325  for ( i = 0 ; i < c ; i++ ) {
326  j = data1[i] + data2[i];
327  if ( j > 255 ) {
328  j = 255;
329  }
330  data1[i] = j;
331  }
332 
333  if ( newMap ) {
334  R_StaticFree( newMap );
335  }
336 }
337 
338 
339 // we build a canonical token form of the image program here
340 static char parseBuffer[MAX_IMAGE_NAME];
341 
342 /*
343 ===================
344 AppendToken
345 ===================
346 */
347 static void AppendToken( idToken &token ) {
348  // add a leading space if not at the beginning
349  if ( parseBuffer[0] ) {
350  idStr::Append( parseBuffer, MAX_IMAGE_NAME, " " );
351  }
352  idStr::Append( parseBuffer, MAX_IMAGE_NAME, token.c_str() );
353 }
354 
355 /*
356 ===================
357 MatchAndAppendToken
358 ===================
359 */
360 static void MatchAndAppendToken( idLexer &src, const char *match ) {
361  if ( !src.ExpectTokenString( match ) ) {
362  return;
363  }
364  // a matched token won't need a leading space
365  idStr::Append( parseBuffer, MAX_IMAGE_NAME, match );
366 }
367 
368 /*
369 ===================
370 R_ParseImageProgram_r
371 
372 If pic is NULL, the timestamps will be filled in, but no image will be generated
373 If both pic and timestamps are NULL, it will just advance past it, which can be
374 used to parse an image program from a text stream.
375 ===================
376 */
377 static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *height,
378  ID_TIME_T *timestamps, textureDepth_t *depth ) {
379  idToken token;
380  float scale;
381  ID_TIME_T timestamp;
382 
383  src.ReadToken( &token );
384  AppendToken( token );
385 
386  if ( !token.Icmp( "heightmap" ) ) {
387  MatchAndAppendToken( src, "(" );
388 
389  if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
390  return false;
391  }
392 
393  MatchAndAppendToken( src, "," );
394 
395  src.ReadToken( &token );
396  AppendToken( token );
397  scale = token.GetFloatValue();
398 
399  // process it
400  if ( pic ) {
401  R_HeightmapToNormalMap( *pic, *width, *height, scale );
402  if ( depth ) {
403  *depth = TD_BUMP;
404  }
405  }
406 
407  MatchAndAppendToken( src, ")" );
408  return true;
409  }
410 
411  if ( !token.Icmp( "addnormals" ) ) {
412  byte *pic2;
413  int width2, height2;
414 
415  MatchAndAppendToken( src, "(" );
416 
417  if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
418  return false;
419  }
420 
421  MatchAndAppendToken( src, "," );
422 
423  if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
424  if ( pic ) {
425  R_StaticFree( *pic );
426  *pic = NULL;
427  }
428  return false;
429  }
430 
431  // process it
432  if ( pic ) {
433  R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 );
434  R_StaticFree( pic2 );
435  if ( depth ) {
436  *depth = TD_BUMP;
437  }
438  }
439 
440  MatchAndAppendToken( src, ")" );
441  return true;
442  }
443 
444  if ( !token.Icmp( "smoothnormals" ) ) {
445  MatchAndAppendToken( src, "(" );
446 
447  if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
448  return false;
449  }
450 
451  if ( pic ) {
452  R_SmoothNormalMap( *pic, *width, *height );
453  if ( depth ) {
454  *depth = TD_BUMP;
455  }
456  }
457 
458  MatchAndAppendToken( src, ")" );
459  return true;
460  }
461 
462  if ( !token.Icmp( "add" ) ) {
463  byte *pic2;
464  int width2, height2;
465 
466  MatchAndAppendToken( src, "(" );
467 
468  if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
469  return false;
470  }
471 
472  MatchAndAppendToken( src, "," );
473 
474  if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
475  if ( pic ) {
476  R_StaticFree( *pic );
477  *pic = NULL;
478  }
479  return false;
480  }
481 
482  // process it
483  if ( pic ) {
484  R_ImageAdd( *pic, *width, *height, pic2, width2, height2 );
485  R_StaticFree( pic2 );
486  }
487 
488  MatchAndAppendToken( src, ")" );
489  return true;
490  }
491 
492  if ( !token.Icmp( "scale" ) ) {
493  float scale[4];
494  int i;
495 
496  MatchAndAppendToken( src, "(" );
497 
498  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
499 
500  for ( i = 0 ; i < 4 ; i++ ) {
501  MatchAndAppendToken( src, "," );
502  src.ReadToken( &token );
503  AppendToken( token );
504  scale[i] = token.GetFloatValue();
505  }
506 
507  // process it
508  if ( pic ) {
509  R_ImageScale( *pic, *width, *height, scale );
510  }
511 
512  MatchAndAppendToken( src, ")" );
513  return true;
514  }
515 
516  if ( !token.Icmp( "invertAlpha" ) ) {
517  MatchAndAppendToken( src, "(" );
518 
519  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
520 
521  // process it
522  if ( pic ) {
523  R_InvertAlpha( *pic, *width, *height );
524  }
525 
526  MatchAndAppendToken( src, ")" );
527  return true;
528  }
529 
530  if ( !token.Icmp( "invertColor" ) ) {
531  MatchAndAppendToken( src, "(" );
532 
533  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
534 
535  // process it
536  if ( pic ) {
537  R_InvertColor( *pic, *width, *height );
538  }
539 
540  MatchAndAppendToken( src, ")" );
541  return true;
542  }
543 
544  if ( !token.Icmp( "makeIntensity" ) ) {
545  int i;
546 
547  MatchAndAppendToken( src, "(" );
548 
549  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
550 
551  // copy red to green, blue, and alpha
552  if ( pic ) {
553  int c;
554  c = *width * *height * 4;
555  for ( i = 0 ; i < c ; i+=4 ) {
556  (*pic)[i+1] =
557  (*pic)[i+2] =
558  (*pic)[i+3] = (*pic)[i];
559  }
560  }
561 
562  MatchAndAppendToken( src, ")" );
563  return true;
564  }
565 
566  if ( !token.Icmp( "makeAlpha" ) ) {
567  int i;
568 
569  MatchAndAppendToken( src, "(" );
570 
571  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
572 
573  // average RGB into alpha, then set RGB to white
574  if ( pic ) {
575  int c;
576  c = *width * *height * 4;
577  for ( i = 0 ; i < c ; i+=4 ) {
578  (*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3;
579  (*pic)[i+0] =
580  (*pic)[i+1] =
581  (*pic)[i+2] = 255;
582  }
583  }
584 
585  MatchAndAppendToken( src, ")" );
586  return true;
587  }
588 
589  // if we are just parsing instead of loading or checking,
590  // don't do the R_LoadImage
591  if ( !timestamps && !pic ) {
592  return true;
593  }
594 
595  // load it as an image
596  R_LoadImage( token.c_str(), pic, width, height, &timestamp, true );
597 
598  if ( timestamp == -1 ) {
599  return false;
600  }
601 
602  // add this to the timestamp
603  if ( timestamps ) {
604  if ( timestamp > *timestamps ) {
605  *timestamps = timestamp;
606  }
607  }
608 
609  return true;
610 }
611 
612 
613 /*
614 ===================
615 R_LoadImageProgram
616 ===================
617 */
618 void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth ) {
619  idLexer src;
620 
621  src.LoadMemory( name, strlen(name), name );
623 
624  parseBuffer[0] = 0;
625  if ( timestamps ) {
626  *timestamps = 0;
627  }
628 
629  R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
630 
631  src.FreeSource();
632 }
633 
634 /*
635 ===================
636 R_ParsePastImageProgram
637 ===================
638 */
639 const char *R_ParsePastImageProgram( idLexer &src ) {
640  parseBuffer[0] = 0;
641  R_ParseImageProgram_r( src, NULL, NULL, NULL, NULL, NULL );
642  return parseBuffer;
643 }
644 
byte * R_Dropsample(const byte *in, int inwidth, int inheight, int outwidth, int outheight)
float Normalize(void)
Definition: Vector.h:646
void FreeSource(void)
Definition: Lexer.cpp:1676
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:4804
GLenum GLsizei n
Definition: glext.h:3705
#define MAX_IMAGE_NAME
Definition: Image.h:144
void R_LoadImage(const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, bool makePowerOf2)
float GetFloatValue(void)
Definition: Token.h:138
Definition: Vector.h:316
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glext.h:2878
static float Sqrt(float x)
Definition: Math.h:302
Definition: Token.h:71
void SetFlags(int flags)
Definition: Lexer.h:298
GLuint src
Definition: glext.h:5390
GLenum GLsizei len
Definition: glext.h:3472
int i
Definition: process.py:33
int Icmp(const char *text) const
Definition: Str.h:667
void R_LoadImageProgram(const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth)
list l
Definition: prepare.py:17
Definition: Lexer.h:137
const GLubyte * c
Definition: glext.h:4677
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
#define NULL
Definition: Lib.h:88
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
float NormalizeFast(void)
Definition: Vector.h:524
GLenum GLsizei width
Definition: glext.h:2846
int LoadMemory(const char *ptr, int length, const char *name, int startLine=1)
Definition: Lexer.cpp:1646
GLenum GLsizei GLsizei height
Definition: glext.h:2856
const char * R_ParsePastImageProgram(idLexer &src)
int ExpectTokenString(const char *string)
Definition: Lexer.cpp:919
Definition: Image.h:126
tuple f
Definition: idal.py:89
GLuint in
Definition: glext.h:5388
void Append(const char a)
Definition: Str.h:729
unsigned char byte
Definition: Lib.h:75
void * R_StaticAlloc(int bytes)
Definition: tr_main.cpp:301
const GLcharARB * name
Definition: glext.h:3629
const char * c_str(void) const
Definition: Str.h:487
float LengthFast(void) const
Definition: Vector.h:639
GLint j
Definition: qgl.h:264
void R_StaticFree(void *data)
Definition: tr_main.cpp:335
int ReadToken(idToken *token)
Definition: Lexer.cpp:820
textureDepth_t
Definition: Image.h:122