assimpAppMaterial.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #define TORQUE_PBR_MATERIALS
  23. #include "platform/platform.h"
  24. #include "ts/loader/appSequence.h"
  25. #include "ts/assimp/assimpAppMaterial.h"
  26. #include "ts/assimp/assimpAppMesh.h"
  27. #include "materials/materialManager.h"
  28. #include "ts/tsMaterialList.h"
  29. #include "core/stream/fileStream.h"
  30. #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
  31. #ifdef new
  32. #undef new
  33. #endif
  34. #endif
  35. // assimp include files.
  36. #include <assimp/cimport.h>
  37. #include <assimp/scene.h>
  38. #include <assimp/postprocess.h>
  39. #include <assimp/types.h>
  40. #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
  41. # define _new new(__FILE__, __LINE__)
  42. # define new _new
  43. #endif
  44. U32 AssimpAppMaterial::sDefaultMatNumber = 0;
  45. String AppMaterial::cleanString(const String& str)
  46. {
  47. String cleanStr(str);
  48. // Replace invalid characters with underscores
  49. const String badChars(" -,.+=*/[]%$~;:");
  50. for (String::SizeType i = 0; i < badChars.length(); i++)
  51. cleanStr.replace(badChars[i], '_');
  52. // Prefix with an underscore if string starts with a number
  53. if ((cleanStr[0] >= '0') && (cleanStr[0] <= '9'))
  54. cleanStr.insert(0, '_');
  55. return cleanStr;
  56. }
  57. AssimpAppMaterial::AssimpAppMaterial(const char* matName)
  58. {
  59. name = ColladaUtils::getOptions().matNamePrefix;
  60. name += matName;
  61. mAIMat = NULL;
  62. // Set some defaults
  63. flags |= TSMaterialList::S_Wrap;
  64. flags |= TSMaterialList::T_Wrap;
  65. }
  66. AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) :
  67. mAIMat(mtl)
  68. {
  69. aiString matName;
  70. mtl->Get(AI_MATKEY_NAME, matName);
  71. name = matName.C_Str();
  72. if (name.isEmpty())
  73. {
  74. name = cleanString(TSShapeLoader::getShapePath().getFileName());
  75. name += "_defMat";
  76. name += String::ToString("%d", sDefaultMatNumber);
  77. sDefaultMatNumber++;
  78. }
  79. name = ColladaUtils::getOptions().matNamePrefix + name;
  80. Con::printf("[ASSIMP] Loading Material: %s", name.c_str());
  81. #ifdef TORQUE_DEBUG
  82. enumerateMaterialProperties(mtl);
  83. #endif
  84. }
  85. Material* AssimpAppMaterial::createMaterial(const Torque::Path& path) const
  86. {
  87. // The filename and material name are used as TorqueScript identifiers, so
  88. // clean them up first
  89. String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
  90. String cleanName = cleanString(getName());
  91. // Create the Material definition
  92. const String oldScriptFile = Con::getVariable("$Con::File");
  93. Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct
  94. Material *newMat = MATMGR->allocateAndRegister(cleanName, getName());
  95. Con::setVariable("$Con::File", oldScriptFile); // restore script path
  96. initMaterial(path, newMat);
  97. return newMat;
  98. }
  99. void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) const
  100. {
  101. String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
  102. String cleanName = cleanString(getName());
  103. // Determine the blend mode and transparency for this material
  104. Material::BlendOp blendOp = Material::None;
  105. bool translucent = false;
  106. float opacity = 1.0f;
  107. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_OPACITY, opacity))
  108. {
  109. if (opacity != 1.0f)
  110. {
  111. translucent = true;
  112. int blendInt;
  113. blendOp = Material::LerpAlpha;
  114. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_BLEND_FUNC, blendInt))
  115. {
  116. if (blendInt == aiBlendMode_Additive)
  117. blendOp = Material::Add;
  118. }
  119. }
  120. }
  121. else
  122. { // No opacity key, see if it's defined as a gltf property
  123. aiString opacityMode;
  124. if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaMode", 0, 0, opacityMode))
  125. {
  126. if (String::compare("MASK", opacityMode.C_Str()) == 0)
  127. {
  128. translucent = true;
  129. blendOp = Material::None;
  130. float cutoff;
  131. if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff))
  132. {
  133. mat->mAlphaRef = (U32)(cutoff * 255); // alpha ref 0-255
  134. mat->mAlphaTest = true;
  135. }
  136. }
  137. else if (String::compare("BLEND", opacityMode.C_Str()) == 0)
  138. {
  139. translucent = true;
  140. blendOp = Material::LerpAlpha;
  141. mat->mAlphaTest = false;
  142. }
  143. else
  144. { // OPAQUE
  145. translucent = false;
  146. blendOp = Material::LerpAlpha; // Make default so it doesn't get written to materials.tscript
  147. }
  148. }
  149. }
  150. mat->mTranslucent = translucent;
  151. mat->mTranslucentBlendOp = blendOp;
  152. // Assign color values.
  153. LinearColorF diffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
  154. aiColor3D read_color(1.f, 1.f, 1.f);
  155. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color))
  156. diffuseColor.set(read_color.r, read_color.g, read_color.b, opacity);
  157. mat->mDiffuse[0] = diffuseColor;
  158. aiString texName;
  159. String torquePath;
  160. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), texName))
  161. {
  162. torquePath = texName.C_Str();
  163. if (!torquePath.isEmpty())
  164. mat->_setDiffuseMap(cleanTextureName(torquePath, cleanFile, path, false), 0);
  165. }
  166. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName))
  167. {
  168. torquePath = texName.C_Str();
  169. if (!torquePath.isEmpty())
  170. mat->_setNormalMap(cleanTextureName(torquePath, cleanFile, path, false), 0);
  171. }
  172. #ifdef TORQUE_PBR_MATERIALS
  173. float floatVal;
  174. if (AI_SUCCESS == mAIMat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, floatVal))
  175. { // The shape has pbr material definitions
  176. String aoName, rmName; // occlusion and roughness/metalness maps
  177. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0), texName))
  178. aoName = texName.C_Str();
  179. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName))
  180. rmName = texName.C_Str();
  181. if (aoName.isNotEmpty() || rmName.isNotEmpty())
  182. { // If we have either map, fill all three slots
  183. if (rmName.isNotEmpty())
  184. {
  185. mat->_setRoughMap(cleanTextureName(rmName, cleanFile, path, false), 0); // Roughness
  186. mat->mRoughnessChan[0] = 1.0f;
  187. mat->mInvertRoughness[0] = (floatVal == 1.0f);
  188. mat->_setMetalMap(cleanTextureName(rmName, cleanFile, path, false), 0); // Metallic
  189. mat->mMetalChan[0] = 2.0f;
  190. }
  191. if (aoName.isNotEmpty())
  192. {
  193. mat->_setAOMap(cleanTextureName(aoName, cleanFile, path, false), 0); // occlusion
  194. mat->mAOChan[0] = 0.0f;
  195. }
  196. else
  197. {
  198. mat->_setAOMap(cleanTextureName(aoName, cleanFile, path, false), 0); // occlusion
  199. mat->mAOChan[0] = 0.0f;
  200. }
  201. }
  202. }
  203. #else
  204. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), texName))
  205. {
  206. torquePath = texName.C_Str();
  207. if (!torquePath.isEmpty())
  208. mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
  209. }
  210. /*LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f);
  211. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_SPECULAR, read_color))
  212. specularColor.set(read_color.r, read_color.g, read_color.b, opacity);
  213. mat->mMetalness[0] = specularColor;
  214. // Specular Power
  215. F32 specularPower = 1.0f;
  216. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS_STRENGTH, specularPower))
  217. mat->mSpecularPower[0] = specularPower;
  218. // Specular
  219. F32 specularStrength = 0.0f;
  220. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS, specularStrength))
  221. mat->mSpecularStrength[0] = specularStrength;*/
  222. #endif
  223. // Double-Sided
  224. bool doubleSided = false;
  225. S32 dbl_sided = 0;
  226. if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TWOSIDED, dbl_sided))
  227. doubleSided = (dbl_sided != 0);
  228. mat->mDoubleSided = doubleSided;
  229. }
  230. String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly /*= false*/)
  231. {
  232. Torque::Path foundPath;
  233. String cleanStr;
  234. if (texName[0] == '*')
  235. { // It's an embedded texture reference. Make the cached name and return
  236. cleanStr = shapeName;
  237. cleanStr += "_cachedTex";
  238. cleanStr += texName.substr(1);
  239. return cleanStr;
  240. }
  241. // See if the file exists
  242. bool fileFound = false;
  243. String testPath = path.getPath();
  244. testPath += '/';
  245. testPath += texName;
  246. testPath.replace('\\', '/');
  247. fileFound = Torque::FS::IsFile(testPath);
  248. cleanStr = texName;
  249. cleanStr.replace('\\', '/');
  250. if (fileFound)
  251. {
  252. if (cleanStr.equal(texName))
  253. return cleanStr;
  254. foundPath = testPath;
  255. }
  256. else
  257. {
  258. // See if the file is in a sub-directory of the shape
  259. Vector<String> foundFiles;
  260. Torque::Path inPath(cleanStr);
  261. String mainDotCsDir = Platform::getMainDotCsDir();
  262. mainDotCsDir += "/";
  263. S32 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir + path.getPath() + "/"), inPath.getFullFileName(), true, foundFiles);
  264. if (results == 0 || foundFiles.size() == 0) // Not under shape directory, try the full tree
  265. results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir), inPath.getFullFileName(), true, foundFiles);
  266. if (results > 0 && foundFiles.size() > 0)
  267. {
  268. fileFound = true;
  269. foundPath = foundFiles[0];
  270. }
  271. }
  272. if (fileFound)
  273. {
  274. if (nameOnly)
  275. cleanStr = foundPath.getFullFileName();
  276. else
  277. { // Unless the file is in the same directory as the materials.tscript (covered above)
  278. // we need to set the full path from the root directory. If we use "subdirectory/file.ext",
  279. // the material manager won't find the image file, but it will be found the next time the
  280. // material is loaded from file. If we use "./subdirectory/file.ext", the image will be found
  281. // now, but not the next time it's loaded from file...
  282. S32 rootLength = dStrlen(Platform::getMainDotCsDir());
  283. cleanStr = foundPath.getFullPathWithoutRoot().substr(rootLength-1);
  284. }
  285. }
  286. else if (nameOnly)
  287. cleanStr += " (Not Found)";
  288. return cleanStr;
  289. }
  290. #ifdef TORQUE_DEBUG
  291. void AssimpAppMaterial::enumerateMaterialProperties(aiMaterial* mtl)
  292. {
  293. for (U32 i = 0; i < mtl->mNumProperties; ++i)
  294. {
  295. aiMaterialProperty* matProp = mtl->mProperties[i];
  296. String outText;
  297. if (matProp)
  298. {
  299. outText = String::ToString(" Key: %s, Index: %d, Semantic: ", matProp->mKey.C_Str(), matProp->mIndex);
  300. switch (matProp->mSemantic)
  301. {
  302. case aiTextureType_NONE:
  303. outText += "aiTextureType_NONE";
  304. break;
  305. case aiTextureType_DIFFUSE:
  306. outText += "aiTextureType_DIFFUSE";
  307. break;
  308. case aiTextureType_SPECULAR:
  309. outText += "aiTextureType_SPECULAR";
  310. break;
  311. case aiTextureType_AMBIENT:
  312. outText += "aiTextureType_AMBIENT";
  313. break;
  314. case aiTextureType_EMISSIVE:
  315. outText += "aiTextureType_EMISSIVE";
  316. break;
  317. case aiTextureType_HEIGHT:
  318. outText += "aiTextureType_HEIGHT";
  319. break;
  320. case aiTextureType_NORMALS:
  321. outText += "aiTextureType_NORMALS";
  322. break;
  323. case aiTextureType_SHININESS:
  324. outText += "aiTextureType_SHININESS";
  325. break;
  326. case aiTextureType_OPACITY:
  327. outText += "aiTextureType_OPACITY";
  328. break;
  329. case aiTextureType_DISPLACEMENT:
  330. outText += "aiTextureType_DISPLACEMENT";
  331. break;
  332. case aiTextureType_LIGHTMAP:
  333. outText += "aiTextureType_LIGHTMAP";
  334. break;
  335. case aiTextureType_REFLECTION:
  336. outText += "aiTextureType_REFLECTION";
  337. break;
  338. default:
  339. outText += "aiTextureType_UNKNOWN";
  340. break;
  341. }
  342. aiString stringProp;
  343. F32* floatProp;
  344. double* doubleProp;
  345. S32* intProp;
  346. switch (matProp->mType)
  347. {
  348. case aiPTI_Float:
  349. floatProp = (F32*)matProp->mData;
  350. for (U32 j = 0; j < matProp->mDataLength / sizeof(F32); ++j)
  351. outText += String::ToString(", %0.4f", floatProp[j]);
  352. break;
  353. case aiPTI_Double:
  354. doubleProp = (double*)matProp->mData;
  355. for (U32 j = 0; j < matProp->mDataLength / sizeof(double); ++j)
  356. outText += String::ToString(", %0.4lf", doubleProp[j]);
  357. break;
  358. case aiPTI_String:
  359. aiGetMaterialString(mtl, matProp->mKey.C_Str(), matProp->mSemantic, matProp->mIndex, &stringProp);
  360. outText += String::ToString(", %s", stringProp.C_Str());
  361. break;
  362. case aiPTI_Integer:
  363. intProp = (S32*)matProp->mData;
  364. for (U32 j = 0; j < matProp->mDataLength / sizeof(S32); ++j)
  365. outText += String::ToString(", %d", intProp[j]);
  366. break;
  367. case aiPTI_Buffer:
  368. outText += ", aiPTI_Buffer format data";
  369. break;
  370. default:
  371. outText += ", Unknown data type";
  372. }
  373. Con::printf("%s", outText.c_str());
  374. }
  375. }
  376. }
  377. #endif