Image_program.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition 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 BFG Edition 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. #pragma hdrstop
  35. #include "../idlib/precompiled.h"
  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, TAG_IMAGE );
  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, TAG_IMAGE );
  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, textureUsage_t * usage ) {
  297. idToken token;
  298. float scale;
  299. ID_TIME_T timestamp;
  300. src.ReadToken( &token );
  301. // Since all interaction shaders now assume YCoCG diffuse textures. We replace all entries for the intrinsic
  302. // _black texture to the black texture on disk. Doing this will cause a YCoCG compliant texture to be generated.
  303. // Without a YCoCG compliant black texture we will get color artifacts for any interaction
  304. // material that specifies the _black texture.
  305. if ( token == "_black" ) {
  306. token = "textures\\black";
  307. }
  308. // also check for _white
  309. if ( token == "_white" ) {
  310. token = "guis\\assets\\white";
  311. }
  312. AppendToken( token );
  313. if ( !token.Icmp( "heightmap" ) ) {
  314. MatchAndAppendToken( src, "(" );
  315. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) {
  316. return false;
  317. }
  318. MatchAndAppendToken( src, "," );
  319. src.ReadToken( &token );
  320. AppendToken( token );
  321. scale = token.GetFloatValue();
  322. // process it
  323. if ( pic ) {
  324. R_HeightmapToNormalMap( *pic, *width, *height, scale );
  325. if ( usage ) {
  326. *usage = TD_BUMP;
  327. }
  328. }
  329. MatchAndAppendToken( src, ")" );
  330. return true;
  331. }
  332. if ( !token.Icmp( "addnormals" ) ) {
  333. byte *pic2 = NULL;
  334. int width2, height2;
  335. MatchAndAppendToken( src, "(" );
  336. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) {
  337. return false;
  338. }
  339. MatchAndAppendToken( src, "," );
  340. if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) {
  341. if ( pic ) {
  342. R_StaticFree( *pic );
  343. *pic = NULL;
  344. }
  345. return false;
  346. }
  347. // process it
  348. if ( pic ) {
  349. R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 );
  350. R_StaticFree( pic2 );
  351. if ( usage ) {
  352. *usage = TD_BUMP;
  353. }
  354. }
  355. MatchAndAppendToken( src, ")" );
  356. return true;
  357. }
  358. if ( !token.Icmp( "smoothnormals" ) ) {
  359. MatchAndAppendToken( src, "(" );
  360. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) {
  361. return false;
  362. }
  363. if ( pic ) {
  364. R_SmoothNormalMap( *pic, *width, *height );
  365. if ( usage ) {
  366. *usage = TD_BUMP;
  367. }
  368. }
  369. MatchAndAppendToken( src, ")" );
  370. return true;
  371. }
  372. if ( !token.Icmp( "add" ) ) {
  373. byte *pic2 = NULL;
  374. int width2, height2;
  375. MatchAndAppendToken( src, "(" );
  376. if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) {
  377. return false;
  378. }
  379. MatchAndAppendToken( src, "," );
  380. if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) {
  381. if ( pic ) {
  382. R_StaticFree( *pic );
  383. *pic = NULL;
  384. }
  385. return false;
  386. }
  387. // process it
  388. if ( pic ) {
  389. R_ImageAdd( *pic, *width, *height, pic2, width2, height2 );
  390. R_StaticFree( pic2 );
  391. }
  392. MatchAndAppendToken( src, ")" );
  393. return true;
  394. }
  395. if ( !token.Icmp( "scale" ) ) {
  396. float scale[4];
  397. int i;
  398. MatchAndAppendToken( src, "(" );
  399. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  400. for ( i = 0 ; i < 4 ; i++ ) {
  401. MatchAndAppendToken( src, "," );
  402. src.ReadToken( &token );
  403. AppendToken( token );
  404. scale[i] = token.GetFloatValue();
  405. }
  406. // process it
  407. if ( pic ) {
  408. R_ImageScale( *pic, *width, *height, scale );
  409. }
  410. MatchAndAppendToken( src, ")" );
  411. return true;
  412. }
  413. if ( !token.Icmp( "invertAlpha" ) ) {
  414. MatchAndAppendToken( src, "(" );
  415. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  416. // process it
  417. if ( pic ) {
  418. R_InvertAlpha( *pic, *width, *height );
  419. }
  420. MatchAndAppendToken( src, ")" );
  421. return true;
  422. }
  423. if ( !token.Icmp( "invertColor" ) ) {
  424. MatchAndAppendToken( src, "(" );
  425. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  426. // process it
  427. if ( pic ) {
  428. R_InvertColor( *pic, *width, *height );
  429. }
  430. MatchAndAppendToken( src, ")" );
  431. return true;
  432. }
  433. if ( !token.Icmp( "makeIntensity" ) ) {
  434. int i;
  435. MatchAndAppendToken( src, "(" );
  436. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  437. // copy red to green, blue, and alpha
  438. if ( pic ) {
  439. int c;
  440. c = *width * *height * 4;
  441. for ( i = 0 ; i < c ; i+=4 ) {
  442. (*pic)[i+1] =
  443. (*pic)[i+2] =
  444. (*pic)[i+3] = (*pic)[i];
  445. }
  446. }
  447. MatchAndAppendToken( src, ")" );
  448. return true;
  449. }
  450. if ( !token.Icmp( "makeAlpha" ) ) {
  451. int i;
  452. MatchAndAppendToken( src, "(" );
  453. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  454. // average RGB into alpha, then set RGB to white
  455. if ( pic ) {
  456. int c;
  457. c = *width * *height * 4;
  458. for ( i = 0 ; i < c ; i+=4 ) {
  459. (*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3;
  460. (*pic)[i+0] =
  461. (*pic)[i+1] =
  462. (*pic)[i+2] = 255;
  463. }
  464. }
  465. MatchAndAppendToken( src, ")" );
  466. return true;
  467. }
  468. // if we are just parsing instead of loading or checking,
  469. // don't do the R_LoadImage
  470. if ( !timestamps && !pic ) {
  471. return true;
  472. }
  473. // load it as an image
  474. R_LoadImage( token.c_str(), pic, width, height, &timestamp, true );
  475. if ( timestamp == -1 ) {
  476. return false;
  477. }
  478. // add this to the timestamp
  479. if ( timestamps ) {
  480. if ( timestamp > *timestamps ) {
  481. *timestamps = timestamp;
  482. }
  483. }
  484. return true;
  485. }
  486. /*
  487. ===================
  488. R_LoadImageProgram
  489. ===================
  490. */
  491. void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureUsage_t * usage ) {
  492. idLexer src;
  493. src.LoadMemory( name, strlen(name), name );
  494. src.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
  495. parseBuffer[0] = 0;
  496. if ( timestamps ) {
  497. *timestamps = 0;
  498. }
  499. R_ParseImageProgram_r( src, pic, width, height, timestamps, usage );
  500. src.FreeSource();
  501. }
  502. /*
  503. ===================
  504. R_ParsePastImageProgram
  505. ===================
  506. */
  507. const char *R_ParsePastImageProgram( idLexer &src ) {
  508. parseBuffer[0] = 0;
  509. R_ParseImageProgram_r( src, NULL, NULL, NULL, NULL, NULL );
  510. return parseBuffer;
  511. }