ddsLoader.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gfx/bitmap/ddsFile.h"
  24. #include "gfx/gfxDevice.h"
  25. #include "core/util/fourcc.h"
  26. #include "console/console.h"
  27. #include "core/resourceManager.h"
  28. #include "core/stream/fileStream.h"
  29. #include "gfx/bitmap/gBitmap.h"
  30. #include "console/engineAPI.h"
  31. S32 DDSFile::smActiveCopies = 0;
  32. U32 DDSFile::smDropMipCount = 0;
  33. // These were copied from the DX9 docs. The names are changed
  34. // from the "real" defines since not all platforms have them.
  35. enum DDSSurfaceDescFlags
  36. {
  37. DDSDCaps = 0x00000001l,
  38. DDSDHeight = 0x00000002l,
  39. DDSDWidth = 0x00000004l,
  40. DDSDPitch = 0x00000008l,
  41. DDSDPixelFormat = 0x00001000l,
  42. DDSDMipMapCount = 0x00020000l,
  43. DDSDLinearSize = 0x00080000l,
  44. DDSDDepth = 0x00800000l,
  45. };
  46. enum DDSPixelFormatFlags
  47. {
  48. DDPFAlphaPixels = 0x00000001,
  49. DDPFFourCC = 0x00000004,
  50. DDPFRGB = 0x00000040,
  51. DDPFLUMINANCE = 0x00020000
  52. };
  53. enum DDSCapFlags
  54. {
  55. DDSCAPSComplex = 0x00000008,
  56. DDSCAPSTexture = 0x00001000,
  57. DDSCAPSMipMap = 0x00400000,
  58. DDSCAPS2Cubemap = 0x00000200,
  59. DDSCAPS2Cubemap_POSITIVEX = 0x00000400,
  60. DDSCAPS2Cubemap_NEGATIVEX = 0x00000800,
  61. DDSCAPS2Cubemap_POSITIVEY = 0x00001000,
  62. DDSCAPS2Cubemap_NEGATIVEY = 0x00002000,
  63. DDSCAPS2Cubemap_POSITIVEZ = 0x00004000,
  64. DDSCAPS2Cubemap_NEGATIVEZ = 0x00008000,
  65. DDSCAPS2Volume = 0x00200000,
  66. };
  67. #define FOURCC_DXT1 (MakeFourCC('D','X','T','1'))
  68. #define FOURCC_DXT2 (MakeFourCC('D','X','T','2'))
  69. #define FOURCC_DXT3 (MakeFourCC('D','X','T','3'))
  70. #define FOURCC_DXT4 (MakeFourCC('D','X','T','4'))
  71. #define FOURCC_DXT5 (MakeFourCC('D','X','T','5'))
  72. DDSFile::DDSFile( const DDSFile &dds )
  73. : mFlags( dds.mFlags ),
  74. mHeight( dds.mHeight ),
  75. mWidth( dds.mWidth ),
  76. mDepth( dds.mDepth ),
  77. mPitchOrLinearSize( dds.mPitchOrLinearSize ),
  78. mMipMapCount( dds.mMipMapCount ),
  79. mFormat( dds.mFormat ),
  80. mBytesPerPixel( dds.mBytesPerPixel ),
  81. mFourCC( dds.mFourCC ),
  82. mCacheString( dds.mCacheString ),
  83. mSourcePath( dds.mSourcePath ),
  84. mHasTransparency( dds.mHasTransparency )
  85. {
  86. VECTOR_SET_ASSOCIATION( mSurfaces );
  87. smActiveCopies++;
  88. for ( U32 i=0; i < dds.mSurfaces.size(); i++ )
  89. {
  90. SurfaceData *surface = NULL;
  91. if ( dds.mSurfaces[i] )
  92. surface = new SurfaceData;
  93. mSurfaces.push_back( surface );
  94. if ( !surface )
  95. continue;
  96. for ( U32 m=0; m < dds.mSurfaces[i]->mMips.size(); m++ )
  97. {
  98. U32 size = dds.getSurfaceSize( m );
  99. surface->mMips.push_back(new U8[size]);
  100. dMemcpy( surface->mMips.last(), dds.mSurfaces[i]->mMips[m], size );
  101. }
  102. }
  103. }
  104. void DDSFile::clear()
  105. {
  106. mFlags = 0;
  107. mHeight = mWidth = mDepth = mPitchOrLinearSize = mMipMapCount = 0;
  108. mFormat = GFXFormatR8G8B8;
  109. }
  110. U32 DDSFile::getSurfacePitch( U32 mipLevel ) const
  111. {
  112. if(mFlags.test(CompressedData))
  113. {
  114. U32 sizeMultiple = 0;
  115. switch(mFormat)
  116. {
  117. case GFXFormatDXT1:
  118. sizeMultiple = 8;
  119. break;
  120. case GFXFormatDXT2:
  121. case GFXFormatDXT3:
  122. case GFXFormatDXT4:
  123. case GFXFormatDXT5:
  124. sizeMultiple = 16;
  125. break;
  126. default:
  127. AssertISV(false, "DDSFile::getPitch - invalid compressed texture format, we only support DXT1-5 right now.");
  128. break;
  129. }
  130. // Maybe need to be DWORD aligned?
  131. U32 align = getMax(U32(1), getWidth(mipLevel)/4) * sizeMultiple;
  132. align += 3; align >>=2; align <<=2;
  133. return align;
  134. }
  135. else
  136. return getWidth(mipLevel) * mBytesPerPixel;
  137. }
  138. U32 DDSFile::getSurfaceSize( U32 height, U32 width, U32 mipLevel ) const
  139. {
  140. // Bump by the mip level.
  141. height = getMax(U32(1), height >> mipLevel);
  142. width = getMax(U32(1), width >> mipLevel);
  143. if(mFlags.test(CompressedData))
  144. {
  145. // From the directX docs:
  146. // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
  147. U32 sizeMultiple = 0;
  148. switch(mFormat)
  149. {
  150. case GFXFormatDXT1:
  151. sizeMultiple = 8;
  152. break;
  153. case GFXFormatDXT2:
  154. case GFXFormatDXT3:
  155. case GFXFormatDXT4:
  156. case GFXFormatDXT5:
  157. sizeMultiple = 16;
  158. break;
  159. default:
  160. AssertISV(false, "DDSFile::getSurfaceSize - invalid compressed texture format, we only support DXT1-5 right now.");
  161. break;
  162. }
  163. return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple;
  164. }
  165. else
  166. {
  167. return height * width* mBytesPerPixel;
  168. }
  169. }
  170. U32 DDSFile::getSizeInBytes() const
  171. {
  172. // TODO: This doesn't take mDepth into account, so
  173. // it doesn't work right for volume or cubemap textures!
  174. U32 bytes = 0;
  175. for ( U32 i=0; i < mMipMapCount; i++ )
  176. bytes += getSurfaceSize( mHeight, mWidth, i );
  177. return bytes;
  178. }
  179. U32 DDSFile::getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels )
  180. {
  181. AssertFatal( format >= GFXFormatDXT1 && format <= GFXFormatDXT5,
  182. "DDSFile::getSizeInBytes - Must be a DXT format!" );
  183. // From the directX docs:
  184. // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5)
  185. U32 sizeMultiple = 0;
  186. if ( format == GFXFormatDXT1 )
  187. sizeMultiple = 8;
  188. else
  189. sizeMultiple = 16;
  190. U32 mipHeight, mipWidth;
  191. U32 bytes = 0;
  192. for ( U32 m=0; m < mipLevels; m++ )
  193. {
  194. mipHeight = getMax( U32(1), height >> m );
  195. mipWidth = getMax( U32(1), width >> m );
  196. bytes += getMax( U32(1), mipWidth / 4 ) *
  197. getMax( U32(1), mipHeight / 4 ) * sizeMultiple;
  198. }
  199. return bytes;
  200. }
  201. bool DDSFile::readHeader(Stream &s)
  202. {
  203. U32 tmp;
  204. // Read the FOURCC
  205. s.read(&tmp);
  206. if(tmp != MakeFourCC('D', 'D', 'S', ' '))
  207. {
  208. Con::errorf("DDSFile::readHeader - unexpected magic number, wanted 'DDS '!");
  209. return false;
  210. }
  211. // Read the size of the header.
  212. s.read(&tmp);
  213. if(tmp != 124)
  214. {
  215. Con::errorf("DDSFile::readHeader - incorrect header size. Expected 124 bytes.");
  216. return false;
  217. }
  218. // Read some flags...
  219. U32 ddsdFlags;
  220. s.read(&ddsdFlags);
  221. // "Always include DDSD_CAPS, DDSD_PIXELFORMAT, DDSD_WIDTH, DDSD_HEIGHT."
  222. if(!(ddsdFlags & (DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight)))
  223. {
  224. Con::errorf("DDSFile::readHeader - incorrect surface description flags.");
  225. return false;
  226. }
  227. // Read height and width (always present)
  228. s.read(&mHeight);
  229. s.read(&mWidth);
  230. // Read pitch or linear size.
  231. // First make sure we have valid flags (either linear size or pitch).
  232. if((ddsdFlags & (DDSDLinearSize | DDSDPitch)) == (DDSDLinearSize | DDSDPitch))
  233. {
  234. // Both are invalid!
  235. Con::errorf("DDSFile::readHeader - encountered both DDSD_LINEARSIZE and DDSD_PITCH!");
  236. return false;
  237. }
  238. // Ok, some flags are set, so let's do some reading.
  239. s.read(&mPitchOrLinearSize);
  240. if(ddsdFlags & DDSDLinearSize)
  241. {
  242. mFlags.set(LinearSizeFlag); // ( mHeight / 4 ) * ( mWidth / 4 ) * DDSSIZE
  243. }
  244. else if (ddsdFlags & DDSDPitch)
  245. {
  246. mFlags.set(PitchSizeFlag); // ( mWidth / 4 ) * DDSSIZE ???
  247. }
  248. else
  249. {
  250. // Neither set! This appears to be depressingly common.
  251. // Con::warnf("DDSFile::readHeader - encountered neither DDSD_LINEARSIZE nor DDSD_PITCH!");
  252. }
  253. // Do we need to read depth? If so, we are a volume texture!
  254. s.read(&mDepth);
  255. if(ddsdFlags & DDSDDepth)
  256. {
  257. mFlags.set(VolumeFlag);
  258. }
  259. else
  260. {
  261. // Wipe it if the flag wasn't set!
  262. mDepth = 0;
  263. }
  264. // Deal with mips!
  265. s.read(&mMipMapCount);
  266. if(ddsdFlags & DDSDMipMapCount)
  267. {
  268. mFlags.set(MipMapsFlag);
  269. }
  270. else
  271. {
  272. // Wipe it if the flag wasn't set!
  273. mMipMapCount = 1;
  274. }
  275. // Deal with 11 DWORDS of reserved space (this reserved space brought to
  276. // you by DirectDraw and the letters F and U).
  277. for(U32 i=0; i<11; i++)
  278. s.read(&tmp);
  279. // Now we're onto the pixel format!
  280. s.read(&tmp);
  281. if(tmp != 32)
  282. {
  283. Con::errorf("DDSFile::readHeader - pixel format chunk has unexpected size!");
  284. return false;
  285. }
  286. U32 ddpfFlags;
  287. s.read(&ddpfFlags);
  288. // Read the next few values so we can deal with them all in one go.
  289. U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask;
  290. s.read(&pfFourCC);
  291. s.read(&pfBitCount);
  292. s.read(&pfRMask);
  293. s.read(&pfGMask);
  294. s.read(&pfBMask);
  295. s.read(&pfAlphaMask);
  296. // Sanity check flags...
  297. if(!(ddpfFlags & (DDPFRGB | DDPFFourCC | DDPFLUMINANCE)))
  298. {
  299. Con::errorf("DDSFile::readHeader - incoherent pixel flags, neither RGB, FourCC, or Luminance!");
  300. return false;
  301. }
  302. // For now let's just dump the header info.
  303. if(ddpfFlags & DDPFLUMINANCE)
  304. {
  305. mFlags.set(RGBData);
  306. mBytesPerPixel = pfBitCount / 8;
  307. bool hasAlpha = ddpfFlags & DDPFAlphaPixels;
  308. mHasTransparency = hasAlpha;
  309. // Try to match a format.
  310. if(hasAlpha)
  311. {
  312. // If it has alpha it is one of...
  313. // GFXFormatA8L8
  314. // GFXFormatA4L4
  315. if(pfBitCount == 16)
  316. mFormat = GFXFormatA8L8;
  317. else if(pfBitCount == 8)
  318. mFormat = GFXFormatA4L4;
  319. else
  320. {
  321. Con::errorf("DDSFile::readHeader - unable to match alpha Luminance format!");
  322. return false;
  323. }
  324. }
  325. else
  326. {
  327. // Otherwise it is one of...
  328. // GFXFormatL16
  329. // GFXFormatL8
  330. if(pfBitCount == 16)
  331. mFormat = GFXFormatL16;
  332. else if(pfBitCount == 8)
  333. mFormat = GFXFormatL8;
  334. else
  335. {
  336. Con::errorf("DDSFile::readHeader - unable to match non-alpha Luminance format!");
  337. return false;
  338. }
  339. }
  340. }
  341. else if(ddpfFlags & DDPFRGB)
  342. {
  343. mFlags.set(RGBData);
  344. //Con::printf("RGB Pixel format of DDS:");
  345. //Con::printf(" bitcount = %d (16, 24, 32)", pfBitCount);
  346. mBytesPerPixel = pfBitCount / 8;
  347. //Con::printf(" red mask = %x", pfRMask);
  348. //Con::printf(" green mask = %x", pfGMask);
  349. //Con::printf(" blue mask = %x", pfBMask);
  350. bool hasAlpha = false;
  351. if(ddpfFlags & DDPFAlphaPixels)
  352. {
  353. hasAlpha = true;
  354. //Con::printf(" alpha mask = %x", pfAlphaMask);
  355. }
  356. else
  357. {
  358. //Con::printf(" no alpha.");
  359. }
  360. mHasTransparency = hasAlpha;
  361. // Try to match a format.
  362. if(hasAlpha)
  363. {
  364. // If it has alpha it is one of...
  365. // GFXFormatR8G8B8A8
  366. // GFXFormatR5G5B5A1
  367. // GFXFormatA8
  368. if(pfBitCount == 32)
  369. mFormat = GFXFormatR8G8B8A8;
  370. else if(pfBitCount == 16)
  371. mFormat = GFXFormatR5G5B5A1;
  372. else if(pfBitCount == 8)
  373. mFormat = GFXFormatA8;
  374. else
  375. {
  376. Con::errorf("DDSFile::readHeader - unable to match alpha RGB format!");
  377. return false;
  378. }
  379. }
  380. else
  381. {
  382. // Otherwise it is one of...
  383. // GFXFormatR8G8B8
  384. // GFXFormatR8G8B8X8
  385. // GFXFormatR5G6B5
  386. // GFXFormatL8
  387. if(pfBitCount == 24)
  388. mFormat = GFXFormatR8G8B8;
  389. else if(pfBitCount == 32)
  390. mFormat = GFXFormatR8G8B8X8;
  391. else if(pfBitCount == 16)
  392. mFormat = GFXFormatR5G6B5;
  393. else if(pfBitCount == 8)
  394. {
  395. // luminance
  396. mFormat = GFXFormatL8;
  397. }
  398. else
  399. {
  400. Con::errorf("DDSFile::readHeader - unable to match non-alpha RGB format!");
  401. return false;
  402. }
  403. }
  404. // Sweet, all done.
  405. }
  406. else if (ddpfFlags & DDPFFourCC)
  407. {
  408. mHasTransparency = (ddpfFlags & DDPFAlphaPixels);
  409. mFlags.set(CompressedData);
  410. /* Con::printf("FourCC Pixel format of DDS:");
  411. Con::printf(" fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]); */
  412. // Ok, make a format determination.
  413. switch(pfFourCC)
  414. {
  415. case FOURCC_DXT1:
  416. mFormat = GFXFormatDXT1;
  417. break;
  418. case FOURCC_DXT2:
  419. mFormat = GFXFormatDXT2;
  420. break;
  421. case FOURCC_DXT3:
  422. mFormat = GFXFormatDXT3;
  423. break;
  424. case FOURCC_DXT4:
  425. mFormat = GFXFormatDXT4;
  426. break;
  427. case FOURCC_DXT5:
  428. mFormat = GFXFormatDXT5;
  429. break;
  430. default:
  431. Con::errorf("DDSFile::readHeader - unknown fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]);
  432. break;
  433. }
  434. }
  435. // Deal with final caps bits... Is this really necessary?
  436. U32 caps1, caps2;
  437. s.read(&caps1);
  438. s.read(&caps2);
  439. s.read(&tmp);
  440. s.read(&tmp); // More icky reserved space.
  441. // Screw caps1.
  442. // if(!(caps1 & DDSCAPS_TEXTURE)))
  443. // {
  444. // }
  445. // Caps2 has cubemap/volume info. Care about that.
  446. if(caps2 & DDSCAPS2Cubemap)
  447. {
  448. mFlags.set(CubeMapFlag);
  449. // Store the face flags too.
  450. if ( caps2 & DDSCAPS2Cubemap_POSITIVEX ) mFlags.set( CubeMap_PosX_Flag );
  451. if ( caps2 & DDSCAPS2Cubemap_NEGATIVEX ) mFlags.set( CubeMap_NegX_Flag );
  452. if ( caps2 & DDSCAPS2Cubemap_POSITIVEY ) mFlags.set( CubeMap_PosY_Flag );
  453. if ( caps2 & DDSCAPS2Cubemap_NEGATIVEY ) mFlags.set( CubeMap_NegY_Flag );
  454. if ( caps2 & DDSCAPS2Cubemap_POSITIVEZ ) mFlags.set( CubeMap_PosZ_Flag );
  455. if ( caps2 & DDSCAPS2Cubemap_NEGATIVEZ ) mFlags.set( CubeMap_NegZ_Flag );
  456. }
  457. // MS has ANOTHER reserved word here. This one particularly sucks.
  458. s.read(&tmp);
  459. return true;
  460. }
  461. bool DDSFile::read(Stream &s, U32 dropMipCount)
  462. {
  463. if( !readHeader(s) || mMipMapCount == 0 )
  464. {
  465. Con::errorf("DDSFile::read - error reading header!");
  466. return false;
  467. }
  468. // If we're droping mips then make sure we have enough.
  469. dropMipCount = getMin( dropMipCount, mMipMapCount - 1 );
  470. // At this point we know what sort of image we contain. So we should
  471. // allocate some buffers, and read it in.
  472. // How many surfaces are we talking about?
  473. if(mFlags.test(CubeMapFlag))
  474. {
  475. mSurfaces.setSize( Cubemap_Surface_Count );
  476. for ( U32 i=0; i < Cubemap_Surface_Count; i++ )
  477. {
  478. // Does the cubemap contain this surface?
  479. if ( mFlags.test( CubeMap_PosX_Flag + ( i << 1 ) ) )
  480. mSurfaces[i] = new SurfaceData();
  481. else
  482. {
  483. mSurfaces[i] = NULL;
  484. continue;
  485. }
  486. // Load all the mips.
  487. for(S32 l=0; l<mMipMapCount; l++)
  488. mSurfaces[i]->readNextMip(this, s, mHeight, mWidth, l, l < dropMipCount );
  489. }
  490. }
  491. else if (mFlags.test(VolumeFlag))
  492. {
  493. // Do something with volume
  494. }
  495. else
  496. {
  497. // It's a plain old texture.
  498. // First allocate a SurfaceData to stick this in.
  499. mSurfaces.push_back(new SurfaceData());
  500. // Load however many mips there are.
  501. for(S32 i=0; i<mMipMapCount; i++)
  502. mSurfaces.last()->readNextMip(this, s, mHeight, mWidth, i, i < dropMipCount);
  503. // Ok, we're done.
  504. }
  505. // If we're dropping mips then fix up the stats.
  506. if ( dropMipCount > 0 )
  507. {
  508. // Fix up the pitch and/or linear size.
  509. if( mFlags.test( LinearSizeFlag ) )
  510. mPitchOrLinearSize = getSurfaceSize( dropMipCount );
  511. else if ( mFlags.test( PitchSizeFlag ) )
  512. mPitchOrLinearSize = getSurfacePitch( dropMipCount );
  513. // Now fix up the rest of the
  514. mMipMapCount = getMax( (U32)1, mMipMapCount - dropMipCount );
  515. mHeight = getHeight( dropMipCount );
  516. mWidth = getWidth( dropMipCount );
  517. }
  518. return true;
  519. }
  520. bool DDSFile::writeHeader( Stream &s )
  521. {
  522. // Read the FOURCC
  523. s.write( 4, "DDS " );
  524. U32 tmp = 0;
  525. // Read the size of the header.
  526. s.write( 124 );
  527. // Read some flags...
  528. U32 ddsdFlags = DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight;
  529. if ( mFlags.test( CompressedData ) )
  530. ddsdFlags |= DDSDLinearSize;
  531. else
  532. ddsdFlags |= DDSDPitch;
  533. if ( mMipMapCount > 0 )
  534. ddsdFlags |= DDSDMipMapCount;
  535. s.write( ddsdFlags );
  536. // Read height and width (always present)
  537. s.write( mHeight );
  538. s.write( mWidth );
  539. // Ok, some flags are set, so let's do some reading.
  540. s.write( mPitchOrLinearSize );
  541. // Do we need to read depth? If so, we are a volume texture!
  542. s.write( mDepth );
  543. // Deal with mips!
  544. s.write( mMipMapCount );
  545. // Deal with 11 DWORDS of reserved space (this reserved space brought to
  546. // you by DirectDraw and the letters F and U).
  547. for(U32 i=0; i<11; i++)
  548. s.write( tmp ); // is this right?
  549. // Now we're onto the pixel format!
  550. // This is the size, in bits,
  551. // of the pixel format data.
  552. tmp = 32;
  553. s.write( tmp );
  554. U32 ddpfFlags;
  555. U32 fourCC = 0;
  556. if ( mFlags.test( CompressedData ) )
  557. {
  558. ddpfFlags = DDPFFourCC;
  559. if (mFormat == GFXFormatDXT1)
  560. fourCC = FOURCC_DXT1;
  561. if (mFormat == GFXFormatDXT3)
  562. fourCC = FOURCC_DXT3;
  563. if (mFormat == GFXFormatDXT5)
  564. fourCC = FOURCC_DXT5;
  565. }
  566. else
  567. ddpfFlags = mBytesPerPixel == 4 ? DDPFRGB | DDPFAlphaPixels : DDPFRGB;
  568. s.write( ddpfFlags );
  569. // Read the next few values so we can deal with them all in one go.
  570. //U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask;
  571. s.write( fourCC );
  572. s.write( mBytesPerPixel * 8 );
  573. s.write( 0x000000FF );
  574. s.write( 0x00FF0000 );
  575. s.write( 0x0000FF00 );
  576. s.write( 0xFF000000 );
  577. // Deal with final caps bits... Is this really necessary?
  578. U32 caps1 = DDSCAPSTexture;
  579. if ( mMipMapCount > 0 )
  580. caps1 |= DDSCAPSComplex | DDSCAPSMipMap;
  581. tmp = 0;
  582. s.write( caps1 );
  583. s.write( tmp );
  584. s.write( tmp );
  585. s.write( tmp );// More icky reserved space.
  586. // MS has ANOTHER reserved word here. This one particularly sucks.
  587. s.write( tmp );
  588. return true;
  589. }
  590. bool DDSFile::write( Stream &s )
  591. {
  592. if(!writeHeader(s))
  593. {
  594. Con::errorf("DDSFile::write - error writing header!");
  595. return false;
  596. }
  597. // At this point we know what sort of image we contain. So we should
  598. // allocate some buffers, and read it in.
  599. // How many surfaces are we talking about?
  600. if(mFlags.test(CubeMapFlag))
  601. {
  602. // Do something with cubemaps.
  603. }
  604. else if (mFlags.test(VolumeFlag))
  605. {
  606. // Do something with volume
  607. }
  608. else
  609. {
  610. // It's a plain old texture.
  611. // Load however many mips there are.
  612. for ( S32 i = 0; i < mMipMapCount; i++ )
  613. mSurfaces.last()->writeNextMip(this, s, mHeight, mWidth, i);
  614. // Ok, we're done.
  615. }
  616. return true;
  617. }
  618. void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file)
  619. {
  620. GBitmap *foo = new GBitmap(dds->mWidth >> mip, dds->mHeight >> mip, false, dds->mFormat);
  621. // Copy our data in.
  622. dMemcpy(foo->getWritableBits(), mMips[mip], dds->getSurfaceSize(dds->mHeight, dds->mWidth, mip) );
  623. FileStream stream;
  624. stream.open( file, Torque::FS::File::Write );
  625. if ( stream.getStatus() == Stream::Ok )
  626. {
  627. // Write it out.
  628. foo->writeBitmap("png", stream);
  629. }
  630. // Clean up.
  631. delete foo;
  632. }
  633. void DDSFile::SurfaceData::readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip)
  634. {
  635. U32 size = dds->getSurfaceSize(height, width, mipLevel);
  636. // If we're skipping this mip then seek forward.
  637. if ( skip )
  638. s.setPosition( s.getPosition() + size );
  639. else
  640. {
  641. mMips.push_back(new U8[size]);
  642. if(!s.read(size, mMips.last()))
  643. Con::errorf("DDSFile::SurfaceData::addNextMip - failed to read mip!");
  644. }
  645. }
  646. void DDSFile::SurfaceData::writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel)
  647. {
  648. U32 size = dds->getSurfaceSize(height, width, mipLevel);
  649. if(!s.write(size, mMips[mipLevel]))
  650. Con::errorf("DDSFile::SurfaceData::writeNextMip - failed to write mip!");
  651. }
  652. //------------------------------------------------------------------------------
  653. template<> void *Resource<DDSFile>::create( const Torque::Path &path )
  654. {
  655. #ifdef TORQUE_DEBUG_RES_MANAGER
  656. Con::printf( "Resource<DDSFile>::create - [%s]", path.getFullPath().c_str() );
  657. #endif
  658. FileStream stream;
  659. stream.open( path.getFullPath(), Torque::FS::File::Read );
  660. if ( stream.getStatus() != Stream::Ok )
  661. return NULL;
  662. DDSFile *retDDS = new DDSFile;
  663. if( !retDDS->read( stream, DDSFile::smDropMipCount ) )
  664. {
  665. delete retDDS;
  666. return NULL;
  667. }
  668. else
  669. {
  670. // Set source file name
  671. retDDS->mSourcePath = path;
  672. retDDS->mCacheString = Torque::Path::Join( path.getRoot(), ':', path.getPath() );
  673. retDDS->mCacheString = Torque::Path::Join( retDDS->mCacheString, '/', path.getFileName() );
  674. }
  675. return retDDS;
  676. }
  677. template<> ResourceBase::Signature Resource<DDSFile>::signature()
  678. {
  679. return MakeFourCC('D','D','S',' '); // Direct Draw Surface
  680. }
  681. Resource<DDSFile> DDSFile::load( const Torque::Path &path, U32 dropMipCount )
  682. {
  683. PROFILE_SCOPE( DDSFile_load );
  684. // HACK: It sucks that we cannot pass parameters into
  685. // the resource manager loading system.
  686. DDSFile::smDropMipCount = dropMipCount;
  687. Resource<DDSFile> ret = ResourceManager::get().load( path );
  688. DDSFile::smDropMipCount = 0;
  689. // Any kind of error checking or path stepping can happen here
  690. return ret;
  691. }
  692. //------------------------------------------------------------------------------
  693. DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
  694. {
  695. if( gbmp == NULL )
  696. return NULL;
  697. DDSFile *ret = new DDSFile;
  698. // Set up the DDSFile properties that matter. Since this is a GBitmap, there
  699. // are assumptions that can be made
  700. ret->mHeight = gbmp->getHeight();
  701. ret->mWidth = gbmp->getWidth();
  702. ret->mDepth = 0;
  703. ret->mFormat = gbmp->getFormat();
  704. ret->mFlags.set(RGBData);
  705. ret->mBytesPerPixel = gbmp->getBytesPerPixel();
  706. ret->mMipMapCount = gbmp->getNumMipLevels();
  707. ret->mHasTransparency = gbmp->getHasTransparency();
  708. // ASSUMPTION!!!
  709. // This _most likely_ does not belong here, but it is safe to assume that if
  710. // a GBitmap is 24-bit, and it's being converted to a DDS, it is most likely
  711. // going to be either:
  712. // a) Uploaded as a 32-bit texture, and just needs to be padded to RGBX
  713. // b) Uploaded as a compressed format, and needs to be padded to 32-bits anyway
  714. if( ret->mFormat == GFXFormatR8G8B8 )
  715. {
  716. ret->mFormat = GFXFormatR8G8B8X8;
  717. ret->mBytesPerPixel = 4;
  718. }
  719. if( ret->mMipMapCount > 1 )
  720. ret->mFlags.set(MipMapsFlag);
  721. // One surface per GBitmap
  722. ret->mSurfaces.push_back( new SurfaceData() );
  723. // Load the mips
  724. for( S32 i = 0; i < ret->mMipMapCount; i++ )
  725. {
  726. const U32 mipSz = ret->getSurfaceSize(i);
  727. ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] );
  728. U8 *mipMem = ret->mSurfaces.last()->mMips.last();
  729. // If this is a straight copy, just do it, otherwise (ugh)
  730. if( ret->mFormat == gbmp->getFormat() )
  731. dMemcpy( mipMem, gbmp->getBits(i), mipSz );
  732. else
  733. {
  734. // Assumption:
  735. AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." );
  736. for( S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ )
  737. {
  738. U8 *dst = &mipMem[pxl * ret->mBytesPerPixel];
  739. const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()];
  740. dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) );
  741. dst[ret->mBytesPerPixel - 1] = 255;
  742. }
  743. }
  744. // Uncomment to debug-dump each mip level
  745. //ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
  746. }
  747. return ret;
  748. }
  749. DefineEngineFunction( getActiveDDSFiles, S32, (),,
  750. "Returns the count of active DDSs files in memory.\n"
  751. "@ingroup Rendering\n" )
  752. {
  753. return DDSFile::smActiveCopies;
  754. }