Image_program.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. /*
  21. all uncompressed
  22. uncompressed normal maps
  23. downsample images
  24. 16 meg Dynamic cache
  25. Anisotropic texturing
  26. Trilinear on all
  27. Trilinear on normal maps, bilinear on others
  28. Bilinear on all
  29. Manager
  30. ->List
  31. ->Print
  32. ->Reload( bool force )
  33. */
  34. #include "../idlib/precompiled.h"
  35. #pragma hdrstop
  36. // tr_imageprogram.c
  37. #include "tr_local.h"
  38. /*
  39. Anywhere that an image name is used (diffusemaps, bumpmaps, specularmaps, lights, etc),
  40. an imageProgram can be specified.
  41. This allows load time operations, like heightmap-to-normalmap conversion and image
  42. composition, to be automatically handled in a way that supports timestamped reloads.
  43. */
  44. /*
  45. =================
  46. R_HeightmapToNormalMap
  47. it is not possible to convert a heightmap into a normal map
  48. properly without knowing the texture coordinate stretching.
  49. We can assume constant and equal ST vectors for walls, but not for characters.
  50. =================
  51. */
  52. static void R_HeightmapToNormalMap( byte *data, int width, int height, float scale ) {
  53. int i, j;
  54. byte *depth;
  55. scale = scale / 256;
  56. // copy and convert to grey scale
  57. j = width * height;
  58. depth = (byte *)R_StaticAlloc( j );
  59. for ( i = 0 ; i < j ; i++ ) {
  60. depth[i] = ( data[i*4] + data[i*4+1] + data[i*4+2] ) / 3;
  61. }
  62. idVec3 dir, dir2;
  63. for ( i = 0 ; i < height ; i++ ) {
  64. for ( j = 0 ; j < width ; j++ ) {
  65. int d1, d2, d3, d4;
  66. int a1, a2, a3, a4;
  67. // FIXME: look at five points?
  68. // look at three points to estimate the gradient
  69. a1 = d1 = depth[ ( i * width + j ) ];
  70. a2 = d2 = depth[ ( i * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
  71. a3 = d3 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + j ) ];
  72. a4 = d4 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
  73. d2 -= d1;
  74. d3 -= d1;
  75. dir[0] = -d2 * scale;
  76. dir[1] = -d3 * scale;
  77. dir[2] = 1;
  78. dir.NormalizeFast();
  79. a1 -= a3;
  80. a4 -= a3;
  81. dir2[0] = -a4 * scale;
  82. dir2[1] = a1 * scale;
  83. dir2[2] = 1;
  84. dir2.NormalizeFast();
  85. dir += dir2;
  86. dir.NormalizeFast();
  87. a1 = ( i * width + j ) * 4;
  88. data[ a1 + 0 ] = (byte)(dir[0] * 127 + 128);
  89. data[ a1 + 1 ] = (byte)(dir[1] * 127 + 128);
  90. data[ a1 + 2 ] = (byte)(dir[2] * 127 + 128);
  91. data[ a1 + 3 ] = 255;
  92. }
  93. }
  94. R_StaticFree( depth );
  95. }
  96. /*
  97. =================
  98. R_ImageScale
  99. =================
  100. */
  101. static void R_ImageScale( byte *data, int width, int height, float scale[4] ) {
  102. int i, j;
  103. int c;
  104. c = width * height * 4;
  105. for ( i = 0 ; i < c ; i++ ) {
  106. j = (byte)(data[i] * scale[i&3]);
  107. if ( j < 0 ) {
  108. j = 0;
  109. } else if ( j > 255 ) {
  110. j = 255;
  111. }
  112. data[i] = j;
  113. }
  114. }
  115. /*
  116. =================
  117. R_InvertAlpha
  118. =================
  119. */
  120. static void R_InvertAlpha( byte *data, int width, int height ) {
  121. int i;
  122. int c;
  123. c = width * height* 4;
  124. for ( i = 0 ; i < c ; i+=4 ) {
  125. data[i+3] = 255 - data[i+3];
  126. }
  127. }
  128. /*
  129. =================
  130. R_InvertColor
  131. =================
  132. */
  133. static void R_InvertColor( byte *data, int width, int height ) {
  134. int i;
  135. int c;
  136. c = width * height* 4;
  137. for ( i = 0 ; i < c ; i+=4 ) {
  138. data[i+0] = 255 - data[i+0];
  139. data[i+1] = 255 - data[i+1];
  140. data[i+2] = 255 - data[i+2];
  141. }
  142. }
  143. /*
  144. ===================
  145. R_AddNormalMaps
  146. ===================
  147. */
  148. static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
  149. int i, j;
  150. byte *newMap;
  151. // resample pic2 to the same size as pic1
  152. if ( width2 != width1 || height2 != height1 ) {
  153. newMap = R_Dropsample( data2, width2, height2, width1, height1 );
  154. data2 = newMap;
  155. } else {
  156. newMap = NULL;
  157. }
  158. // add the normal change from the second and renormalize
  159. for ( i = 0 ; i < height1 ; i++ ) {
  160. for ( j = 0 ; j < width1 ; j++ ) {
  161. byte *d1, *d2;
  162. idVec3 n;
  163. float len;
  164. d1 = data1 + ( i * width1 + j ) * 4;
  165. d2 = data2 + ( i * width1 + j ) * 4;
  166. n[0] = ( d1[0] - 128 ) / 127.0;
  167. n[1] = ( d1[1] - 128 ) / 127.0;
  168. n[2] = ( d1[2] - 128 ) / 127.0;
  169. // There are some normal maps that blend to 0,0,0 at the edges
  170. // this screws up compression, so we try to correct that here by instead fading it to 0,0,1
  171. len = n.LengthFast();
  172. if ( len < 1.0f ) {
  173. n[2] = idMath::Sqrt(1.0 - (n[0]*n[0]) - (n[1]*n[1]));
  174. }
  175. n[0] += ( d2[0] - 128 ) / 127.0;
  176. n[1] += ( d2[1] - 128 ) / 127.0;
  177. n.Normalize();
  178. d1[0] = (byte)(n[0] * 127 + 128);
  179. d1[1] = (byte)(n[1] * 127 + 128);
  180. d1[2] = (byte)(n[2] * 127 + 128);
  181. d1[3] = 255;
  182. }
  183. }
  184. if ( newMap ) {
  185. R_StaticFree( newMap );
  186. }
  187. }
  188. /*
  189. ================
  190. R_SmoothNormalMap
  191. ================
  192. */
  193. static void R_SmoothNormalMap( byte *data, int width, int height ) {
  194. byte *orig;
  195. int i, j, k, l;
  196. idVec3 normal;
  197. byte *out;
  198. static float factors[3][3] = {
  199. { 1, 1, 1 },
  200. { 1, 1, 1 },
  201. { 1, 1, 1 }
  202. };
  203. orig = (byte *)R_StaticAlloc( width * height * 4 );
  204. memcpy( orig, data, width * height * 4 );
  205. for ( i = 0 ; i < width ; i++ ) {
  206. for ( j = 0 ; j < height ; j++ ) {
  207. normal = vec3_origin;
  208. for ( k = -1 ; k < 2 ; k++ ) {
  209. for ( l = -1 ; l < 2 ; l++ ) {
  210. byte *in;
  211. in = orig + ( ((j+l)&(height-1))*width + ((i+k)&(width-1)) ) * 4;
  212. // ignore 000 and -1 -1 -1
  213. if ( in[0] == 0 && in[1] == 0 && in[2] == 0 ) {
  214. continue;
  215. }
  216. if ( in[0] == 128 && in[1] == 128 && in[2] == 128 ) {
  217. continue;
  218. }
  219. normal[0] += factors[k+1][l+1] * ( in[0] - 128 );
  220. normal[1] += factors[k+1][l+1] * ( in[1] - 128 );
  221. normal[2] += factors[k+1][l+1] * ( in[2] - 128 );
  222. }
  223. }
  224. normal.Normalize();
  225. out = data + ( j * width + i ) * 4;
  226. out[0] = (byte)(128 + 127 * normal[0]);
  227. out[1] = (byte)(128 + 127 * normal[1]);
  228. out[2] = (byte)(128 + 127 * normal[2]);
  229. }
  230. }
  231. R_StaticFree( orig );
  232. }
  233. /*
  234. ===================
  235. R_ImageAdd
  236. ===================
  237. */
  238. static void R_ImageAdd( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
  239. int i, j;
  240. int c;
  241. byte *newMap;
  242. // resample pic2 to the same size as pic1
  243. if ( width2 != width1 || height2 != height1 ) {
  244. newMap = R_Dropsample( data2, width2, height2, width1, height1 );
  245. data2 = newMap;
  246. } else {
  247. newMap = NULL;
  248. }
  249. c = width1 * height1 * 4;
  250. for ( i = 0 ; i < c ; i++ ) {
  251. j = data1[i] + data2[i];
  252. if ( j > 255 ) {
  253. j = 255;
  254. }
  255. data1[i] = j;
  256. }
  257. if ( newMap ) {
  258. R_StaticFree( newMap );
  259. }
  260. }
  261. // we build a canonical token form of the image program here
  262. static char parseBuffer[MAX_IMAGE_NAME];
  263. /*
  264. ===================
  265. AppendToken
  266. ===================
  267. */
  268. static void AppendToken( idToken &token ) {
  269. // add a leading space if not at the beginning
  270. if ( parseBuffer[0] ) {
  271. idStr::Append( parseBuffer, MAX_IMAGE_NAME, " " );
  272. }
  273. idStr::Append( parseBuffer, MAX_IMAGE_NAME, token.c_str() );
  274. }
  275. /*
  276. ===================
  277. MatchAndAppendToken
  278. ===================
  279. */
  280. static void MatchAndAppendToken( idLexer &src, const char *match ) {
  281. if ( !src.ExpectTokenString( match ) ) {
  282. return;
  283. }
  284. // a matched token won't need a leading space
  285. idStr::Append( parseBuffer, MAX_IMAGE_NAME, match );
  286. }
  287. /*
  288. ===================
  289. R_ParseImageProgram_r
  290. If pic is NULL, the timestamps will be filled in, but no image will be generated
  291. If both pic and timestamps are NULL, it will just advance past it, which can be
  292. used to parse an image program from a text stream.
  293. ===================
  294. */
  295. static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *height,
  296. ID_TIME_T *timestamps, textureDepth_t *depth ) {
  297. idToken token;
  298. float scale;
  299. ID_TIME_T timestamp;
  300. src.ReadToken( &token );
  301. AppendToken( token );
  302. if ( !token.Icmp( "heightmap" ) ) {
  303. MatchAndAppendToken( src, "(" );
  304. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
  305. return false;
  306. }
  307. MatchAndAppendToken( src, "," );
  308. src.ReadToken( &token );
  309. AppendToken( token );
  310. scale = token.GetFloatValue();
  311. // process it
  312. if ( pic ) {
  313. R_HeightmapToNormalMap( *pic, *width, *height, scale );
  314. if ( depth ) {
  315. *depth = TD_BUMP;
  316. }
  317. }
  318. MatchAndAppendToken( src, ")" );
  319. return true;
  320. }
  321. if ( !token.Icmp( "addnormals" ) ) {
  322. byte *pic2;
  323. int width2, height2;
  324. MatchAndAppendToken( src, "(" );
  325. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
  326. return false;
  327. }
  328. MatchAndAppendToken( src, "," );
  329. if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
  330. if ( pic ) {
  331. R_StaticFree( *pic );
  332. *pic = NULL;
  333. }
  334. return false;
  335. }
  336. // process it
  337. if ( pic ) {
  338. R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 );
  339. R_StaticFree( pic2 );
  340. if ( depth ) {
  341. *depth = TD_BUMP;
  342. }
  343. }
  344. MatchAndAppendToken( src, ")" );
  345. return true;
  346. }
  347. if ( !token.Icmp( "smoothnormals" ) ) {
  348. MatchAndAppendToken( src, "(" );
  349. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
  350. return false;
  351. }
  352. if ( pic ) {
  353. R_SmoothNormalMap( *pic, *width, *height );
  354. if ( depth ) {
  355. *depth = TD_BUMP;
  356. }
  357. }
  358. MatchAndAppendToken( src, ")" );
  359. return true;
  360. }
  361. if ( !token.Icmp( "add" ) ) {
  362. byte *pic2;
  363. int width2, height2;
  364. MatchAndAppendToken( src, "(" );
  365. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
  366. return false;
  367. }
  368. MatchAndAppendToken( src, "," );
  369. if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
  370. if ( pic ) {
  371. R_StaticFree( *pic );
  372. *pic = NULL;
  373. }
  374. return false;
  375. }
  376. // process it
  377. if ( pic ) {
  378. R_ImageAdd( *pic, *width, *height, pic2, width2, height2 );
  379. R_StaticFree( pic2 );
  380. }
  381. MatchAndAppendToken( src, ")" );
  382. return true;
  383. }
  384. if ( !token.Icmp( "scale" ) ) {
  385. float scale[4];
  386. int i;
  387. MatchAndAppendToken( src, "(" );
  388. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  389. for ( i = 0 ; i < 4 ; i++ ) {
  390. MatchAndAppendToken( src, "," );
  391. src.ReadToken( &token );
  392. AppendToken( token );
  393. scale[i] = token.GetFloatValue();
  394. }
  395. // process it
  396. if ( pic ) {
  397. R_ImageScale( *pic, *width, *height, scale );
  398. }
  399. MatchAndAppendToken( src, ")" );
  400. return true;
  401. }
  402. if ( !token.Icmp( "invertAlpha" ) ) {
  403. MatchAndAppendToken( src, "(" );
  404. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  405. // process it
  406. if ( pic ) {
  407. R_InvertAlpha( *pic, *width, *height );
  408. }
  409. MatchAndAppendToken( src, ")" );
  410. return true;
  411. }
  412. if ( !token.Icmp( "invertColor" ) ) {
  413. MatchAndAppendToken( src, "(" );
  414. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  415. // process it
  416. if ( pic ) {
  417. R_InvertColor( *pic, *width, *height );
  418. }
  419. MatchAndAppendToken( src, ")" );
  420. return true;
  421. }
  422. if ( !token.Icmp( "makeIntensity" ) ) {
  423. int i;
  424. MatchAndAppendToken( src, "(" );
  425. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  426. // copy red to green, blue, and alpha
  427. if ( pic ) {
  428. int c;
  429. c = *width * *height * 4;
  430. for ( i = 0 ; i < c ; i+=4 ) {
  431. (*pic)[i+1] =
  432. (*pic)[i+2] =
  433. (*pic)[i+3] = (*pic)[i];
  434. }
  435. }
  436. MatchAndAppendToken( src, ")" );
  437. return true;
  438. }
  439. if ( !token.Icmp( "makeAlpha" ) ) {
  440. int i;
  441. MatchAndAppendToken( src, "(" );
  442. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  443. // average RGB into alpha, then set RGB to white
  444. if ( pic ) {
  445. int c;
  446. c = *width * *height * 4;
  447. for ( i = 0 ; i < c ; i+=4 ) {
  448. (*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3;
  449. (*pic)[i+0] =
  450. (*pic)[i+1] =
  451. (*pic)[i+2] = 255;
  452. }
  453. }
  454. MatchAndAppendToken( src, ")" );
  455. return true;
  456. }
  457. // if we are just parsing instead of loading or checking,
  458. // don't do the R_LoadImage
  459. if ( !timestamps && !pic ) {
  460. return true;
  461. }
  462. // load it as an image
  463. R_LoadImage( token.c_str(), pic, width, height, &timestamp, true );
  464. if ( timestamp == -1 ) {
  465. return false;
  466. }
  467. // add this to the timestamp
  468. if ( timestamps ) {
  469. if ( timestamp > *timestamps ) {
  470. *timestamps = timestamp;
  471. }
  472. }
  473. return true;
  474. }
  475. /*
  476. ===================
  477. R_LoadImageProgram
  478. ===================
  479. */
  480. void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth ) {
  481. idLexer src;
  482. src.LoadMemory( name, strlen(name), name );
  483. src.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
  484. parseBuffer[0] = 0;
  485. if ( timestamps ) {
  486. *timestamps = 0;
  487. }
  488. R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
  489. src.FreeSource();
  490. }
  491. /*
  492. ===================
  493. R_ParsePastImageProgram
  494. ===================
  495. */
  496. const char *R_ParsePastImageProgram( idLexer &src ) {
  497. parseBuffer[0] = 0;
  498. R_ParseImageProgram_r( src, NULL, NULL, NULL, NULL, NULL );
  499. return parseBuffer;
  500. }