lltinygltfhelper.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /**
  2. * @file lltinygltfhelper.cpp
  3. * @brief The LLTinyGLTFHelper class implementation
  4. *
  5. * $LicenseInfo:firstyear=2022&license=viewergpl$
  6. *
  7. * Copyright (c) 2022, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "lltinygltfhelper.h"
  34. #include "lldir.h"
  35. #include "llimage.h"
  36. #include "llviewertexture.h"
  37. #include "llviewertexturelist.h"
  38. static void strip_alpha_channel(LLPointer<LLImageRaw>& img)
  39. {
  40. if (img->getComponents() == 4)
  41. {
  42. LLImageRaw* tmp = new LLImageRaw(img->getWidth(), img->getHeight(), 3);
  43. tmp->copyUnscaled4onto3(img);
  44. img = tmp;
  45. }
  46. }
  47. // Copy red channel from src_img to dst_img. Preconditions: dst_img must have 3
  48. // components, src_img and dst_image must have the same dimensions.
  49. static void copy_red_channel(LLPointer<LLImageRaw>& src_img,
  50. LLPointer<LLImageRaw>& dst_img)
  51. {
  52. llassert(src_img->getWidth() == dst_img->getWidth() &&
  53. src_img->getHeight() == dst_img->getHeight() &&
  54. dst_img->getComponents() == 3);
  55. U32 pixel_count = dst_img->getWidth() * dst_img->getHeight();
  56. U8* src = src_img->getData();
  57. U8* dst = dst_img->getData();
  58. S8 src_components = src_img->getComponents();
  59. for (U32 i = 0; i < pixel_count; ++i)
  60. {
  61. dst[i * 3] = src[i * src_components];
  62. }
  63. }
  64. //static
  65. void LLTinyGLTFHelper::initFetchedTextures(tinygltf::Material& material,
  66. LLPointer<LLImageRaw>& basecol_imgp,
  67. LLPointer<LLImageRaw>& normal_imgp,
  68. LLPointer<LLImageRaw>& mr_imgp,
  69. LLPointer<LLImageRaw>& emissive_imgp,
  70. LLPointer<LLImageRaw>& occl_imgp,
  71. LLPointer<LLViewerFetchedTexture>& basecolp,
  72. LLPointer<LLViewerFetchedTexture>& normalp,
  73. LLPointer<LLViewerFetchedTexture>& mrp,
  74. LLPointer<LLViewerFetchedTexture>& emissivep)
  75. {
  76. if (basecol_imgp)
  77. {
  78. basecolp = LLViewerTextureManager::getFetchedTexture(basecol_imgp);
  79. }
  80. if (normal_imgp)
  81. {
  82. strip_alpha_channel(normal_imgp);
  83. normalp = LLViewerTextureManager::getFetchedTexture(normal_imgp);
  84. }
  85. if (mr_imgp)
  86. {
  87. strip_alpha_channel(mr_imgp);
  88. if (occl_imgp)
  89. {
  90. if (material.pbrMetallicRoughness.metallicRoughnessTexture.index !=
  91. material.occlusionTexture.index)
  92. {
  93. // Occlusion is a distinct texture from pbrMetallicRoughness;
  94. // pack into MR red channel.
  95. S32 occlusion_idx = material.occlusionTexture.index;
  96. S32 mr_idx =
  97. material.pbrMetallicRoughness.metallicRoughnessTexture.index;
  98. if (occlusion_idx != mr_idx)
  99. {
  100. // Scale occlusion image to match resolution of mr image
  101. occl_imgp->scale(mr_imgp->getWidth(),
  102. mr_imgp->getHeight());
  103. copy_red_channel(occl_imgp, mr_imgp);
  104. }
  105. }
  106. }
  107. else if (material.occlusionTexture.index == -1)
  108. {
  109. // No occlusion, make sure red channel of ORM is all 255
  110. occl_imgp = new LLImageRaw(mr_imgp->getWidth(),
  111. mr_imgp->getHeight(), 3);
  112. occl_imgp->clear(255, 255, 255);
  113. copy_red_channel(occl_imgp, mr_imgp);
  114. }
  115. }
  116. else if (occl_imgp)
  117. {
  118. // No mr but occlusion exists, make a white mr_imgp and copy occlusion
  119. // red channel over.
  120. mr_imgp = new LLImageRaw(occl_imgp->getWidth(), occl_imgp->getHeight(),
  121. 3);
  122. mr_imgp->clear(255, 255, 255);
  123. copy_red_channel(occl_imgp, mr_imgp);
  124. }
  125. if (mr_imgp)
  126. {
  127. mrp = LLViewerTextureManager::getFetchedTexture(mr_imgp);
  128. }
  129. if (emissive_imgp)
  130. {
  131. strip_alpha_channel(emissive_imgp);
  132. emissivep = LLViewerTextureManager::getFetchedTexture(emissive_imgp);
  133. }
  134. }
  135. //static
  136. LLColor4 LLTinyGLTFHelper::getColor(const std::vector<double>& in)
  137. {
  138. LLColor4 out;
  139. for (S32 i = 0, count = llmin((S32)in.size(), 4); i < count; ++i)
  140. {
  141. out.mV[i] = in[i];
  142. }
  143. return out;
  144. }
  145. //static
  146. const tinygltf::Image* LLTinyGLTFHelper::getImageFromTextureIndex(const tinygltf::Model& m,
  147. S32 tex_idx)
  148. {
  149. if (tex_idx >= 0)
  150. {
  151. S32 src_idx = m.textures[tex_idx].source;
  152. if (src_idx >= 0)
  153. {
  154. return &(m.images[src_idx]);
  155. }
  156. }
  157. return NULL;
  158. }
  159. //static
  160. LLImageRaw* LLTinyGLTFHelper::getTexture(const std::string& folder,
  161. const tinygltf::Model& model,
  162. S32 tex_idx, std::string& name,
  163. bool flip)
  164. {
  165. const tinygltf::Image* imgp = getImageFromTextureIndex(model, tex_idx);
  166. LLImageRaw* rawp = NULL;
  167. if (imgp && imgp->bits == 8 && !imgp->image.empty() &&
  168. imgp->component <= 4)
  169. {
  170. name = imgp->name;
  171. rawp = new LLImageRaw(&imgp->image[0], imgp->width, imgp->height,
  172. imgp->component);
  173. if (flip)
  174. {
  175. rawp->verticalFlip();
  176. }
  177. rawp->optimizeAwayAlpha();
  178. }
  179. return rawp;
  180. }
  181. //static
  182. LLImageRaw* LLTinyGLTFHelper::getTexture(const std::string& folder,
  183. const tinygltf::Model& model,
  184. S32 tex_idx, bool flip)
  185. {
  186. const tinygltf::Image* imgp = getImageFromTextureIndex(model, tex_idx);
  187. LLImageRaw* rawp = NULL;
  188. if (imgp && imgp->bits == 8 && !imgp->image.empty() &&
  189. imgp->component <= 4)
  190. {
  191. rawp = new LLImageRaw(&imgp->image[0], imgp->width, imgp->height,
  192. imgp->component);
  193. if (flip)
  194. {
  195. rawp->verticalFlip();
  196. }
  197. rawp->optimizeAwayAlpha();
  198. }
  199. return rawp;
  200. }
  201. //static
  202. bool LLTinyGLTFHelper::loadModel(const std::string& filename,
  203. tinygltf::Model& model_in)
  204. {
  205. std::string exten = gDirUtil.getExtension(filename);
  206. LLStringUtil::toLower(exten);
  207. if (exten != "gltf" && exten != "glb")
  208. {
  209. llwarns << "Invalid extension for a GLTF model file: " << filename
  210. << llendl;
  211. return false;
  212. }
  213. tinygltf::TinyGLTF loader;
  214. std::string error_msg, warn_msg;
  215. bool success;
  216. if (exten == "gltf")
  217. {
  218. // File is ASCII
  219. success = loader.LoadASCIIFromFile(&model_in, &error_msg, &warn_msg,
  220. filename);
  221. }
  222. else
  223. {
  224. // File is binary
  225. success = loader.LoadBinaryFromFile(&model_in, &error_msg, &warn_msg,
  226. filename);
  227. }
  228. if (!success)
  229. {
  230. llwarns << "Failed to load file: " << filename << " - Error: "
  231. << error_msg << " - Warning: " << warn_msg << llendl;
  232. return false;
  233. }
  234. if (model_in.materials.empty())
  235. {
  236. // Materials are missing
  237. llwarns << "Load failed. No material found in file: " << filename
  238. << llendl;
  239. return false;
  240. }
  241. return true;
  242. }
  243. //static
  244. bool LLTinyGLTFHelper::getMaterialFromModel(const std::string& filename,
  245. const tinygltf::Model& model_in,
  246. S32 mat_idx,
  247. LLFetchedGLTFMaterial* matp,
  248. std::string& mat_name, bool flip)
  249. {
  250. if (!matp)
  251. {
  252. llwarns << "NULL material passed for " << filename << llendl;
  253. llassert(false);
  254. return false;
  255. }
  256. if (!matp || (S32)model_in.materials.size() <= mat_idx)
  257. {
  258. // Materials are missing
  259. llwarns << "Cannot load. No material at index " << mat_idx
  260. << " in file " << filename << llendl;
  261. return false;
  262. }
  263. matp->setFromModel(model_in, mat_idx);
  264. std::string folder = gDirUtil.getDirName(filename);
  265. tinygltf::Material mat_in = model_in.materials[mat_idx];
  266. mat_name = mat_in.name;
  267. // Get base color texture
  268. LLPointer<LLImageRaw> base_img =
  269. getTexture(folder, model_in,
  270. mat_in.pbrMetallicRoughness.baseColorTexture.index, flip);
  271. // Get normal map
  272. LLPointer<LLImageRaw> normal_imgp = getTexture(folder, model_in,
  273. mat_in.normalTexture.index,
  274. flip);
  275. // Get metallic-roughness texture
  276. LLPointer<LLImageRaw> mr_imgp =
  277. getTexture(folder, model_in,
  278. mat_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
  279. // Get emissive texture
  280. LLPointer<LLImageRaw> emissive_imgp =
  281. getTexture(folder, model_in, mat_in.emissiveTexture.index, flip);
  282. // Get occlusion map if needed
  283. LLPointer<LLImageRaw> occl_imgp;
  284. if (mat_in.occlusionTexture.index !=
  285. mat_in.pbrMetallicRoughness.metallicRoughnessTexture.index)
  286. {
  287. occl_imgp = getTexture(folder, model_in,
  288. mat_in.occlusionTexture.index, flip);
  289. }
  290. LLPointer<LLViewerFetchedTexture> basecolp;
  291. LLPointer<LLViewerFetchedTexture> normalp;
  292. LLPointer<LLViewerFetchedTexture> mrp;
  293. LLPointer<LLViewerFetchedTexture> emissivep;
  294. // *TODO: pass it into local bitmaps ?
  295. initFetchedTextures(mat_in, base_img, normal_imgp, mr_imgp, emissive_imgp,
  296. occl_imgp, basecolp, normalp, mrp, emissivep);
  297. constexpr F32 VIRTUAL_SIZE = 64.f * 64.f;
  298. if (basecolp)
  299. {
  300. basecolp->addTextureStats(VIRTUAL_SIZE);
  301. matp->mTextureId[BASECOLIDX] = basecolp->getID();
  302. }
  303. else
  304. {
  305. matp->mTextureId[BASECOLIDX].setNull();
  306. }
  307. matp->mBaseColorTexture = basecolp;
  308. if (normalp)
  309. {
  310. normalp->addTextureStats(VIRTUAL_SIZE);
  311. matp->mTextureId[NORMALIDX] = normalp->getID();
  312. }
  313. else
  314. {
  315. matp->mTextureId[NORMALIDX].setNull();
  316. }
  317. matp->mNormalTexture = normalp;
  318. if (mrp)
  319. {
  320. mrp->addTextureStats(VIRTUAL_SIZE);
  321. matp->mTextureId[MROUGHIDX] = mrp->getID();
  322. }
  323. else
  324. {
  325. matp->mTextureId[MROUGHIDX].setNull();
  326. }
  327. matp->mMetallicRoughnessTexture = mrp;
  328. if (emissivep)
  329. {
  330. emissivep->addTextureStats(VIRTUAL_SIZE);
  331. matp->mTextureId[EMISSIVEIDX] = emissivep->getID();
  332. }
  333. else
  334. {
  335. matp->mTextureId[EMISSIVEIDX].setNull();
  336. }
  337. matp->mEmissiveTexture = emissivep;
  338. return true;
  339. }
  340. // ----------------------------------------------------------------------------
  341. // This function is used in the llgltf library to use LLTinyGLTFHelper's
  342. // services without knowing anything about it. It also takes a LLGLTFMaterial
  343. // pointer instead of a LLFetchedGLTFMaterial one, because the llgltf library
  344. // knows nothing about LLFetchedGLTFMaterial excepted it derives from the
  345. // parent LLGLTFMaterial class... HB
  346. void get_material_from_model(const std::string& filename,
  347. const tinygltf::Model& model, S32 mat_idx,
  348. LLGLTFMaterial* matp,
  349. std::string& mat_name, bool flip)
  350. {
  351. LLFetchedGLTFMaterial* fmatp = dynamic_cast<LLFetchedGLTFMaterial*>(matp);
  352. if (!fmatp)
  353. {
  354. llwarns_once << "Not a fetched material !" << llendl;
  355. llassert(false);
  356. return;
  357. }
  358. LLTinyGLTFHelper::getMaterialFromModel(filename, model, mat_idx, fmatp,
  359. mat_name, flip);
  360. }