BsFreeImgImporter.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsFreeImgImporter.h"
  4. #include "Resources/BsResource.h"
  5. #include "Debug/BsDebug.h"
  6. #include "FileSystem/BsDataStream.h"
  7. #include "Managers/BsTextureManager.h"
  8. #include "Image/BsTexture.h"
  9. #include "Importer/BsTextureImportOptions.h"
  10. #include "FileSystem/BsFileSystem.h"
  11. #include "BsCoreApplication.h"
  12. #include "CoreThread/BsCoreThread.h"
  13. #include "Math/BsMath.h"
  14. #include "Math/BsVector2.h"
  15. #include "Math/BsVector3.h"
  16. #include "FreeImage.h"
  17. #include "Utility/BsBitwise.h"
  18. #include "Renderer/BsRenderer.h"
  19. using namespace std::placeholders;
  20. namespace bs
  21. {
  22. void FreeImageLoadErrorHandler(FREE_IMAGE_FORMAT fif, const char *message)
  23. {
  24. // Callback method as required by FreeImage to report problems
  25. const char* typeName = FreeImage_GetFormatFromFIF(fif);
  26. if (typeName)
  27. {
  28. gDebug().logError("FreeImage error: '" + String(message) + "' when loading format " + typeName);
  29. }
  30. else
  31. {
  32. gDebug().logError("FreeImage error: '" + String(message) + "'");
  33. }
  34. }
  35. FreeImgImporter::FreeImgImporter()
  36. :SpecificImporter()
  37. {
  38. FreeImage_Initialise(false);
  39. // Register codecs
  40. WStringStream strExt;
  41. strExt << "Supported formats: ";
  42. bool first = true;
  43. for (int i = 0; i < FreeImage_GetFIFCount(); ++i)
  44. {
  45. // Skip DDS codec since FreeImage does not have the option
  46. // to keep DXT data compressed, we'll use our own codec
  47. if ((FREE_IMAGE_FORMAT)i == FIF_DDS)
  48. continue;
  49. WString exts = toWString(String(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)));
  50. if (!first)
  51. {
  52. strExt << ",";
  53. }
  54. first = false;
  55. strExt << exts;
  56. // Pull off individual formats (separated by comma by FI)
  57. Vector<WString> extsVector = StringUtil::split(exts, L",");
  58. for (auto v = extsVector.begin(); v != extsVector.end(); ++v)
  59. {
  60. auto findIter = std::find(mExtensions.begin(), mExtensions.end(), *v);
  61. if(findIter == mExtensions.end())
  62. {
  63. WString ext = *v;
  64. StringUtil::toLowerCase(ext);
  65. mExtensionToFID.insert(std::make_pair(ext, i));
  66. mExtensions.push_back(ext);
  67. }
  68. }
  69. }
  70. // Set error handler
  71. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  72. }
  73. FreeImgImporter::~FreeImgImporter()
  74. {
  75. FreeImage_DeInitialise();
  76. }
  77. bool FreeImgImporter::isExtensionSupported(const WString& ext) const
  78. {
  79. WString lowerCaseExt = ext;
  80. StringUtil::toLowerCase(lowerCaseExt);
  81. return find(mExtensions.begin(), mExtensions.end(), lowerCaseExt) != mExtensions.end();
  82. }
  83. bool FreeImgImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
  84. {
  85. WString ext = magicNumToExtension(magicNumPtr, numBytes);
  86. return isExtensionSupported(ext);
  87. }
  88. WString FreeImgImporter::magicNumToExtension(const UINT8* magic, UINT32 maxBytes) const
  89. {
  90. // Set error handler
  91. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  92. FIMEMORY* fiMem =
  93. FreeImage_OpenMemory((BYTE*)magic, static_cast<DWORD>(maxBytes));
  94. FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(fiMem, (int)maxBytes);
  95. FreeImage_CloseMemory(fiMem);
  96. if (fif != FIF_UNKNOWN)
  97. {
  98. WString ext = toWString(String(FreeImage_GetFormatFromFIF(fif)));
  99. StringUtil::toLowerCase(ext);
  100. return ext;
  101. }
  102. else
  103. {
  104. return StringUtil::WBLANK;
  105. }
  106. }
  107. SPtr<ImportOptions> FreeImgImporter::createImportOptions() const
  108. {
  109. return bs_shared_ptr_new<TextureImportOptions>();
  110. }
  111. SPtr<Resource> FreeImgImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
  112. {
  113. const TextureImportOptions* textureImportOptions = static_cast<const TextureImportOptions*>(importOptions.get());
  114. SPtr<DataStream> fileData = FileSystem::openFile(filePath, true);
  115. SPtr<PixelData> imgData = importRawImage(fileData);
  116. if(imgData == nullptr || imgData->getData() == nullptr)
  117. return nullptr;
  118. Vector<SPtr<PixelData>> faceData;
  119. TextureType texType;
  120. if(textureImportOptions->getIsCubemap())
  121. {
  122. texType = TEX_TYPE_CUBE_MAP;
  123. std::array<SPtr<PixelData>, 6> cubemapFaces;
  124. if (generateCubemap(imgData, textureImportOptions->getCubemapSourceType(), cubemapFaces))
  125. {
  126. faceData.insert(faceData.begin(), cubemapFaces.begin(), cubemapFaces.end());
  127. }
  128. else // Fall-back to 2D texture
  129. {
  130. texType = TEX_TYPE_2D;
  131. faceData.push_back(imgData);
  132. }
  133. }
  134. else
  135. {
  136. texType = TEX_TYPE_2D;
  137. faceData.push_back(imgData);
  138. }
  139. UINT32 numMips = 0;
  140. if (textureImportOptions->getGenerateMipmaps() &&
  141. Bitwise::isPow2(faceData[0]->getWidth()) && Bitwise::isPow2(faceData[0]->getHeight()))
  142. {
  143. UINT32 maxPossibleMip = PixelUtil::getMaxMipmaps(faceData[0]->getWidth(), faceData[0]->getHeight(),
  144. faceData[0]->getDepth(), faceData[0]->getFormat());
  145. if (textureImportOptions->getMaxMip() == 0)
  146. numMips = maxPossibleMip;
  147. else
  148. numMips = std::min(maxPossibleMip, textureImportOptions->getMaxMip());
  149. }
  150. int usage = TU_DEFAULT;
  151. if (textureImportOptions->getCPUCached())
  152. usage |= TU_CPUCACHED;
  153. bool sRGB = textureImportOptions->getSRGB();
  154. TEXTURE_DESC texDesc;
  155. texDesc.type = texType;
  156. texDesc.width = faceData[0]->getWidth();
  157. texDesc.height = faceData[0]->getHeight();
  158. texDesc.numMips = numMips;
  159. texDesc.format = textureImportOptions->getFormat();
  160. texDesc.usage = usage;
  161. texDesc.hwGamma = sRGB;
  162. SPtr<Texture> newTexture = Texture::_createPtr(texDesc);
  163. UINT32 numFaces = (UINT32)faceData.size();
  164. for (UINT32 i = 0; i < numFaces; i++)
  165. {
  166. Vector<SPtr<PixelData>> mipLevels;
  167. if (numMips > 0)
  168. {
  169. MipMapGenOptions mipOptions;
  170. mipOptions.isSRGB = sRGB;
  171. mipLevels = PixelUtil::genMipmaps(*faceData[i], mipOptions);
  172. }
  173. else
  174. mipLevels.push_back(faceData[i]);
  175. for (UINT32 mip = 0; mip < (UINT32)mipLevels.size(); ++mip)
  176. {
  177. SPtr<PixelData> dst = newTexture->getProperties().allocBuffer(0, mip);
  178. PixelUtil::bulkPixelConversion(*mipLevels[mip], *dst);
  179. newTexture->writeData(dst, i, mip);
  180. }
  181. }
  182. fileData->close();
  183. WString fileName = filePath.getWFilename(false);
  184. newTexture->setName(fileName);
  185. return newTexture;
  186. }
  187. SPtr<PixelData> FreeImgImporter::importRawImage(const SPtr<DataStream>& fileData)
  188. {
  189. if(fileData->size() > std::numeric_limits<UINT32>::max())
  190. {
  191. BS_EXCEPT(InternalErrorException, "File size larger than supported!");
  192. }
  193. UINT32 magicLen = std::min((UINT32)fileData->size(), 32u);
  194. UINT8 magicBuf[32];
  195. fileData->read(magicBuf, magicLen);
  196. fileData->seek(0);
  197. WString fileExtension = magicNumToExtension(magicBuf, magicLen);
  198. auto findFormat = mExtensionToFID.find(fileExtension);
  199. if(findFormat == mExtensionToFID.end())
  200. {
  201. BS_EXCEPT(InvalidParametersException, "Type of the file provided is not supported by this importer. File type: " + toString(fileExtension));
  202. }
  203. FREE_IMAGE_FORMAT imageFormat = (FREE_IMAGE_FORMAT)findFormat->second;
  204. // Set error handler
  205. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  206. // Buffer stream into memory (TODO: override IO functions instead?)
  207. MemoryDataStream memStream(fileData);
  208. fileData->close();
  209. FIMEMORY* fiMem = FreeImage_OpenMemory(memStream.getPtr(), static_cast<DWORD>(memStream.size()));
  210. FIBITMAP* fiBitmap = FreeImage_LoadFromMemory(
  211. (FREE_IMAGE_FORMAT)imageFormat, fiMem);
  212. if (!fiBitmap)
  213. {
  214. BS_EXCEPT(InternalErrorException, "Error decoding image");
  215. }
  216. UINT32 width = FreeImage_GetWidth(fiBitmap);
  217. UINT32 height = FreeImage_GetHeight(fiBitmap);
  218. PixelFormat format = PF_UNKNOWN;
  219. // Must derive format first, this may perform conversions
  220. FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(fiBitmap);
  221. FREE_IMAGE_COLOR_TYPE colourType = FreeImage_GetColorType(fiBitmap);
  222. unsigned bpp = FreeImage_GetBPP(fiBitmap);
  223. unsigned srcElemSize = 0;
  224. switch(imageType)
  225. {
  226. case FIT_UNKNOWN:
  227. case FIT_COMPLEX:
  228. case FIT_UINT32:
  229. case FIT_INT32:
  230. case FIT_DOUBLE:
  231. default:
  232. BS_EXCEPT(InternalErrorException, "Unknown or unsupported image format");
  233. break;
  234. case FIT_BITMAP:
  235. // Standard image type
  236. // Perform any colour conversions for greyscale
  237. if (colourType == FIC_MINISWHITE || colourType == FIC_MINISBLACK)
  238. {
  239. FIBITMAP* newBitmap = FreeImage_ConvertToGreyscale(fiBitmap);
  240. // free old bitmap and replace
  241. FreeImage_Unload(fiBitmap);
  242. fiBitmap = newBitmap;
  243. // get new formats
  244. bpp = FreeImage_GetBPP(fiBitmap);
  245. colourType = FreeImage_GetColorType(fiBitmap);
  246. }
  247. // Perform any colour conversions for RGB
  248. else if (bpp < 8 || colourType == FIC_PALETTE || colourType == FIC_CMYK)
  249. {
  250. FIBITMAP* newBitmap = FreeImage_ConvertTo24Bits(fiBitmap);
  251. // free old bitmap and replace
  252. FreeImage_Unload(fiBitmap);
  253. fiBitmap = newBitmap;
  254. // get new formats
  255. bpp = FreeImage_GetBPP(fiBitmap);
  256. colourType = FreeImage_GetColorType(fiBitmap);
  257. }
  258. // by this stage, 8-bit is greyscale, 16/24/32 bit are RGB[A]
  259. switch(bpp)
  260. {
  261. case 8:
  262. format = PF_R8;
  263. srcElemSize = 1;
  264. break;
  265. case 16:
  266. // Determine 555 or 565 from green mask
  267. // cannot be 16-bit greyscale since that's FIT_UINT16
  268. if(FreeImage_GetGreenMask(fiBitmap) == FI16_565_GREEN_MASK)
  269. {
  270. assert(false && "Format not supported by the engine. TODO.");
  271. return nullptr;
  272. }
  273. else
  274. {
  275. assert(false && "Format not supported by the engine. TODO.");
  276. return nullptr;
  277. // FreeImage doesn't support 4444 format so must be 1555
  278. }
  279. srcElemSize = 2;
  280. break;
  281. case 24:
  282. // FreeImage differs per platform
  283. // PF_BYTE_BGR[A] for little endian (== PF_ARGB native)
  284. // PF_BYTE_RGB[A] for big endian (== PF_RGBA native)
  285. #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
  286. format = PF_RGB8;
  287. #else
  288. format = PF_BGR8;
  289. #endif
  290. srcElemSize = 3;
  291. break;
  292. case 32:
  293. #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
  294. format = PF_RGBA8;
  295. #else
  296. format = PF_BGRA8;
  297. #endif
  298. srcElemSize = 4;
  299. break;
  300. };
  301. break;
  302. case FIT_UINT16:
  303. case FIT_INT16:
  304. // 16-bit greyscale
  305. assert(false && "No INT pixel formats supported currently. TODO.");
  306. return nullptr;
  307. break;
  308. case FIT_FLOAT:
  309. // Single-component floating point data
  310. format = PF_R32F;
  311. srcElemSize = 4;
  312. break;
  313. case FIT_RGB16:
  314. format = PF_RGBA16F;
  315. srcElemSize = 2 * 3;
  316. break;
  317. case FIT_RGBA16:
  318. format = PF_RGBA16F;
  319. srcElemSize = 2 * 4;
  320. break;
  321. case FIT_RGBF:
  322. format = PF_RGB32F;
  323. srcElemSize = 4 * 3;
  324. break;
  325. case FIT_RGBAF:
  326. format = PF_RGBA32F;
  327. srcElemSize = 4 * 4;
  328. break;
  329. };
  330. unsigned char* srcData = FreeImage_GetBits(fiBitmap);
  331. unsigned srcPitch = FreeImage_GetPitch(fiBitmap);
  332. // Final data - invert image and trim pitch at the same time
  333. UINT32 dstElemSize = PixelUtil::getNumElemBytes(format);
  334. UINT32 dstPitch = width * PixelUtil::getNumElemBytes(format);
  335. // Bind output buffer
  336. SPtr<PixelData> texData = bs_shared_ptr_new<PixelData>(width, height, 1, format);
  337. texData->allocateInternalBuffer();
  338. UINT8* output = texData->getData();
  339. UINT8* pSrc;
  340. UINT8* pDst = output;
  341. // Copy row by row, which is faster
  342. if (srcElemSize == dstElemSize)
  343. {
  344. for (UINT32 y = 0; y < height; ++y)
  345. {
  346. pSrc = srcData + (height - y - 1) * srcPitch;
  347. memcpy(pDst, pSrc, dstPitch);
  348. pDst += dstPitch;
  349. }
  350. }
  351. else
  352. {
  353. for (UINT32 y = 0; y < height; ++y)
  354. {
  355. pSrc = srcData + (height - y - 1) * srcPitch;
  356. for(UINT32 x = 0; x < width; ++x)
  357. memcpy(pDst + x * dstElemSize, pSrc + x * srcElemSize, srcElemSize);
  358. pDst += dstPitch;
  359. }
  360. }
  361. FreeImage_Unload(fiBitmap);
  362. FreeImage_CloseMemory(fiMem);
  363. return texData;
  364. }
  365. /**
  366. * Reads the source texture as a horizontal or vertical list of 6 cubemap faces.
  367. *
  368. * @param[in] source Source texture to read.
  369. * @param[out] output Output array that will contain individual cubemap faces.
  370. * @param[in] faceSize Size of a single face, in pixels. Both width & height must match.
  371. * @param[in] vertical True if the faces are laid out vertically, false if horizontally.
  372. */
  373. void readCubemapList(const SPtr<PixelData>& source, std::array<SPtr<PixelData>, 6>& output, UINT32 faceSize, bool vertical)
  374. {
  375. Vector2I faceStart;
  376. for(UINT32 i = 0; i < 6; i++)
  377. {
  378. output[i] = PixelData::create(faceSize, faceSize, 1, source->getFormat());
  379. if (vertical)
  380. faceStart.y += faceSize;
  381. else
  382. faceStart.x += faceSize;
  383. PixelVolume volume(faceStart.x, faceStart.y, faceStart.x + faceSize, faceStart.y + faceSize);
  384. PixelUtil::copy(*source, *output[i], faceStart.x, faceStart.y);
  385. }
  386. }
  387. /**
  388. * Reads the source texture as a horizontal or vertical "cross" of 6 cubemap faces.
  389. *
  390. * Vertical layout:
  391. * +Y
  392. * -X +Z +X
  393. * -Y
  394. * -Z
  395. *
  396. * Horizontal layout:
  397. * +Y
  398. * -X +Z +X -Z
  399. * -Y
  400. *
  401. * @param[in] source Source texture to read.
  402. * @param[out] output Output array that will contain individual cubemap faces.
  403. * @param[in] faceSize Size of a single face, in pixels. Both width & height must match.
  404. * @param[in] vertical True if the faces are laid out vertically, false if horizontally.
  405. */
  406. void readCubemapCross(const SPtr<PixelData>& source, std::array<SPtr<PixelData>, 6>& output, UINT32 faceSize,
  407. bool vertical)
  408. {
  409. const static UINT32 vertFaceIndices[] = { 5, 3, 1, 7, 4, 10 };
  410. const static UINT32 horzFaceIndices[] = { 6, 4, 1, 9, 5, 7 };
  411. const UINT32* faceIndices = vertical ? vertFaceIndices : horzFaceIndices;
  412. UINT32 numFacesInRow = vertical ? 3 : 4;
  413. for (UINT32 i = 0; i < 6; i++)
  414. {
  415. output[i] = PixelData::create(faceSize, faceSize, 1, source->getFormat());
  416. UINT32 faceX = (faceIndices[i] % numFacesInRow) * faceSize;
  417. UINT32 faceY = (faceIndices[i] / numFacesInRow) * faceSize;
  418. PixelVolume volume(faceX, faceY, faceX + faceSize, faceY + faceSize);
  419. PixelUtil::copy(*source, *output[i], faceX, faceY);
  420. }
  421. // Flip -Z as it's upside down
  422. if (vertical)
  423. PixelUtil::mirror(*output[5], MirrorModeBits::X | MirrorModeBits::Y);
  424. }
  425. /** Method that maps a direction to a point on a plane in range [0, 1] using spherical mapping. */
  426. Vector2 mapCubemapDirToSpherical(const Vector3& dir)
  427. {
  428. // Using the OpenGL spherical mapping formula
  429. Vector3 nrmDir = Vector3::normalize(dir);
  430. nrmDir.z += 1.0f;
  431. float m = 2 * nrmDir.length();
  432. float u = nrmDir.x / m + 0.5f;
  433. float v = nrmDir.y / m + 0.5f;
  434. return Vector2(u, v);
  435. }
  436. /**
  437. * Method that maps a direction to a point on a plane in range [0, 1] using cylindrical mapping. This mapping is also
  438. * know as longitude-latitude mapping, Blinn/Newell mapping or equirectangular cylindrical mapping.
  439. */
  440. Vector2 mapCubemapDirToCylindrical(const Vector3& dir)
  441. {
  442. Vector3 nrmDir = Vector3::normalize(dir);
  443. float u = (atan2(nrmDir.z, nrmDir.x) + Math::PI) / Math::TWO_PI;
  444. float v = acos(nrmDir.y) / Math::PI;
  445. return Vector2(u, v);
  446. }
  447. /** Resizes the provided cubemap faces and outputs a new set of resized faces. */
  448. void downsampleCubemap(const std::array<SPtr<PixelData>, 6>& input, std::array<SPtr<PixelData>, 6>& output, UINT32 size)
  449. {
  450. for(UINT32 i = 0; i < 6; i++)
  451. {
  452. output[i] = PixelData::create(size, size, 1, input[i]->getFormat());
  453. PixelUtil::scale(*input[i], *output[i]);
  454. }
  455. }
  456. /**
  457. * Reads the source texture and remaps its data into six faces of a cubemap.
  458. *
  459. * @param[in] source Source texture to remap.
  460. * @param[out] output Remapped faces of the cubemap.
  461. * @param[in] faceSize Width/height of each individual face, in pixels.
  462. * @param[in] remap Function to use for remapping the cubemap direction to UV.
  463. */
  464. void readCubemapUVRemap(const SPtr<PixelData>& source, std::array<SPtr<PixelData>, 6>& output, UINT32 faceSize,
  465. const std::function<Vector2(Vector3)>& remap)
  466. {
  467. struct RemapInfo
  468. {
  469. int idx[3];
  470. Vector3 sign;
  471. };
  472. // Mapping from default (X, Y, 1.0f) plane to relevant cube face. Also flipping Y so it corresponds to how pixel
  473. // coordinates are mapped.
  474. static const RemapInfo remapLookup[] =
  475. {
  476. { 2, 1, 0, { 1.0f, -1.0f, 1.0f }}, // X+
  477. { 2, 1, 0, { -1.0f, -1.0f, -1.0f }}, // X-
  478. { 0, 2, 1, { 1.0f, -1.0f, 1.0f }}, // Y+
  479. { 0, 2, 1, { 1.0f, 1.0f, -1.0f }}, // Y-
  480. { 0, 1, 2, { 1.0f, -1.0f, -1.0f }}, // Z+
  481. { 0, 1, 2, { -1.0f, -1.0f, 1.0f }} // Z-
  482. };
  483. float invSize = 1.0f / faceSize;
  484. for (UINT32 faceIdx = 0; faceIdx < 6; faceIdx++)
  485. {
  486. output[faceIdx] = PixelData::create(faceSize, faceSize, 1, source->getFormat());
  487. const RemapInfo& remapInfo = remapLookup[faceIdx];
  488. for (UINT32 y = 0; y < faceSize; y++)
  489. {
  490. for (UINT32 x = 0; x < faceSize; x++)
  491. {
  492. // Map from pixel coordinates to [-1, 1] range.
  493. Vector2 sourceCoord = (Vector2((float)x, (float)y) * invSize) * 2.0f - Vector2(1.0f, 1.0f);
  494. Vector3 direction = Vector3(sourceCoord.x, sourceCoord.y, 1.0f);
  495. direction *= remapInfo.sign;
  496. // Rotate towards current face
  497. Vector3 rotatedDir;
  498. rotatedDir[remapInfo.idx[0]] = direction.x;
  499. rotatedDir[remapInfo.idx[1]] = direction.y;
  500. rotatedDir[remapInfo.idx[2]] = direction.z;
  501. // Find location in the source to sample from
  502. Vector2 sourceUV = remap(rotatedDir);
  503. Color color = source->sampleColorAt(sourceUV);
  504. // Write the sampled color
  505. output[faceIdx]->setColorAt(color, x, y);
  506. }
  507. }
  508. }
  509. }
  510. bool FreeImgImporter::generateCubemap(const SPtr<PixelData>& source, CubemapSourceType sourceType,
  511. std::array<SPtr<PixelData>, 6>& output)
  512. {
  513. // Note: Expose this as a parameter if needed:
  514. UINT32 cubemapSupersampling = 1; // Set to amount of samples
  515. Vector2I faceSize;
  516. bool cross = false;
  517. bool vertical = false;
  518. switch(sourceType)
  519. {
  520. case CubemapSourceType::Faces:
  521. {
  522. float aspect = source->getWidth() / (float)source->getHeight();
  523. if(Math::approxEquals(aspect, 6.0f)) // Horizontal list
  524. {
  525. faceSize.x = source->getWidth() / 6;
  526. faceSize.y = source->getHeight();
  527. }
  528. else if(Math::approxEquals(aspect, 1.0f / 6.0f)) // Vertical list
  529. {
  530. faceSize.x = source->getWidth();
  531. faceSize.y = source->getHeight() / 6;
  532. vertical = true;
  533. }
  534. else if(Math::approxEquals(aspect, 4.0f / 3.0f)) // Horizontal cross
  535. {
  536. faceSize.x = source->getWidth() / 4;
  537. faceSize.y = source->getHeight() / 3;
  538. cross = true;
  539. }
  540. else if(Math::approxEquals(aspect, 3.0f / 4.0f)) // Vertical cross
  541. {
  542. faceSize.x = source->getWidth() / 3;
  543. faceSize.y = source->getHeight() / 4;
  544. cross = true;
  545. vertical = true;
  546. }
  547. else
  548. {
  549. LOGWRN("Unable to generate a cubemap: unrecognized face configuration.");
  550. return false;
  551. }
  552. }
  553. break;
  554. case CubemapSourceType::Cylindrical:
  555. case CubemapSourceType::Spherical:
  556. // Half of the source size will be used for each cube face, which should yield good enough quality
  557. faceSize.x = std::max(source->getWidth(), source->getHeight()) / 2;
  558. faceSize.x = Bitwise::closestPow2(faceSize.x);
  559. // Don't allow sizes larger than 4096
  560. faceSize.x = std::min(faceSize.x, 4096);
  561. faceSize.y = faceSize.x;
  562. break;
  563. default: // Assuming single-image
  564. faceSize.x = source->getWidth();
  565. faceSize.y = source->getHeight();
  566. break;
  567. }
  568. if (faceSize.x != faceSize.y)
  569. {
  570. LOGWRN("Unable to generate a cubemap: width & height must match.");
  571. return false;
  572. }
  573. if (!Bitwise::isPow2(faceSize.x))
  574. {
  575. LOGWRN("Unable to generate a cubemap: width & height must be powers of 2.");
  576. return false;
  577. }
  578. switch (sourceType)
  579. {
  580. case CubemapSourceType::Faces:
  581. {
  582. if (cross)
  583. readCubemapCross(source, output, faceSize.x, vertical);
  584. else
  585. readCubemapList(source, output, faceSize.x, vertical);
  586. }
  587. break;
  588. case CubemapSourceType::Cylindrical:
  589. {
  590. UINT32 superSampledFaceSize = faceSize.x * cubemapSupersampling;
  591. std::array<SPtr<PixelData>, 6> superSampledOutput;
  592. readCubemapUVRemap(source, superSampledOutput, superSampledFaceSize, &mapCubemapDirToCylindrical);
  593. if (faceSize.x != (INT32)superSampledFaceSize)
  594. downsampleCubemap(superSampledOutput, output, faceSize.x);
  595. else
  596. output = superSampledOutput;
  597. }
  598. break;
  599. case CubemapSourceType::Spherical:
  600. {
  601. UINT32 superSampledFaceSize = faceSize.x * cubemapSupersampling;
  602. std::array<SPtr<PixelData>, 6> superSampledOutput;
  603. readCubemapUVRemap(source, superSampledOutput, superSampledFaceSize, &mapCubemapDirToSpherical);
  604. if (faceSize.x != (INT32)superSampledFaceSize)
  605. downsampleCubemap(superSampledOutput, output, faceSize.x);
  606. else
  607. output = superSampledOutput;
  608. }
  609. break;
  610. default: // Single-image
  611. for (UINT32 i = 0; i < 6; i++)
  612. output[i] = source;
  613. break;
  614. }
  615. return true;
  616. }
  617. }