assimpAppMaterial.cpp
Engine/source/ts/assimp/assimpAppMaterial.cpp
Public Defines
define
Detailed Description
Public Defines
TORQUE_PBR_MATERIALS()
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23#define TORQUE_PBR_MATERIALS 24 25#include "platform/platform.h" 26#include "ts/loader/appSequence.h" 27#include "ts/assimp/assimpAppMaterial.h" 28#include "ts/assimp/assimpAppMesh.h" 29#include "materials/materialManager.h" 30#include "ts/tsMaterialList.h" 31#include "core/stream/fileStream.h" 32 33// assimp include files. 34#include <assimp/cimport.h> 35#include <assimp/scene.h> 36#include <assimp/postprocess.h> 37#include <assimp/types.h> 38 39U32 AssimpAppMaterial::sDefaultMatNumber = 0; 40 41String AppMaterial::cleanString(const String& str) 42{ 43 String cleanStr(str); 44 45 // Replace invalid characters with underscores 46 const String badChars(" -,.+=*/[]%$~;:"); 47 for (String::SizeType i = 0; i < badChars.length(); i++) 48 cleanStr.replace(badChars[i], '_'); 49 50 // Prefix with an underscore if string starts with a number 51 if ((cleanStr[0] >= '0') && (cleanStr[0] <= '9')) 52 cleanStr.insert(0, '_'); 53 54 return cleanStr; 55} 56 57AssimpAppMaterial::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 67AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) : 68 mAIMat(mtl) 69{ 70 aiString matName; 71 mtl->Get(AI_MATKEY_NAME, matName); 72 name = matName.C_Str(); 73 if (name.isEmpty()) 74 { 75 name = cleanString(TSShapeLoader::getShapePath().getFileName()); 76 name += "_defMat"; 77 name += String::ToString("%d", sDefaultMatNumber); 78 sDefaultMatNumber++; 79 } 80 name = ColladaUtils::getOptions().matNamePrefix + name; 81 Con::printf("[ASSIMP] Loading Material: %s", name.c_str()); 82#ifdef TORQUE_DEBUG 83 enumerateMaterialProperties(mtl); 84#endif 85} 86 87Material* AssimpAppMaterial::createMaterial(const Torque::Path& path) const 88{ 89 // The filename and material name are used as TorqueScript identifiers, so 90 // clean them up first 91 String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName()); 92 String cleanName = cleanString(getName()); 93 94 // Create the Material definition 95 const String oldScriptFile = Con::getVariable("$Con::File"); 96 Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct 97 Material *newMat = MATMGR->allocateAndRegister(cleanName, getName()); 98 Con::setVariable("$Con::File", oldScriptFile); // restore script path 99 100 initMaterial(path, newMat); 101 102 return newMat; 103} 104 105void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) const 106{ 107 String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName()); 108 String cleanName = cleanString(getName()); 109 110 // Determine the blend mode and transparency for this material 111 Material::BlendOp blendOp = Material::None; 112 bool translucent = false; 113 float opacity = 1.0f; 114 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_OPACITY, opacity)) 115 { 116 if (opacity != 1.0f) 117 { 118 translucent = true; 119 int blendInt; 120 blendOp = Material::LerpAlpha; 121 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_BLEND_FUNC, blendInt)) 122 { 123 if (blendInt == aiBlendMode_Additive) 124 blendOp = Material::Add; 125 } 126 } 127 } 128 else 129 { // No opacity key, see if it's defined as a gltf property 130 aiString opacityMode; 131 if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaMode", 0, 0, opacityMode)) 132 { 133 if (String::compare("MASK", opacityMode.C_Str()) == 0) 134 { 135 translucent = true; 136 blendOp = Material::None; 137 138 float cutoff; 139 if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff)) 140 { 141 mat->mAlphaRef = (U32)(cutoff * 255); // alpha ref 0-255 142 mat->mAlphaTest = true; 143 } 144 } 145 else if (String::compare("BLEND", opacityMode.C_Str()) == 0) 146 { 147 translucent = true; 148 blendOp = Material::LerpAlpha; 149 mat->mAlphaTest = false; 150 } 151 else 152 { // OPAQUE 153 translucent = false; 154 blendOp = Material::LerpAlpha; // Make default so it doesn't get written to materials.tscript 155 } 156 } 157 } 158 mat->mTranslucent = translucent; 159 mat->mTranslucentBlendOp = blendOp; 160 161 // Assign color values. 162 LinearColorF diffuseColor(1.0f, 1.0f, 1.0f, 1.0f); 163 aiColor3D read_color(1.f, 1.f, 1.f); 164 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color)) 165 diffuseColor.set(read_color.r, read_color.g, read_color.b, opacity); 166 mat->mDiffuse[0] = diffuseColor; 167 168 aiString texName; 169 String torquePath; 170 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), texName)) 171 { 172 torquePath = texName.C_Str(); 173 if (!torquePath.isEmpty()) 174 mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); 175 } 176 177 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName)) 178 { 179 torquePath = texName.C_Str(); 180 if (!torquePath.isEmpty()) 181 mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); 182 } 183 184#ifdef TORQUE_PBR_MATERIALS 185 float floatVal; 186 if (AI_SUCCESS == mAIMat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, floatVal)) 187 { // The shape has pbr material definitions 188 String aoName, rmName; // occlusion and roughness/metalness maps 189 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0), texName)) 190 aoName = texName.C_Str(); 191 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName)) 192 rmName = texName.C_Str(); 193 194 if (aoName.isNotEmpty() || rmName.isNotEmpty()) 195 { // If we have either map, fill all three slots 196 if (rmName.isNotEmpty()) 197 { 198 mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Roughness 199 mat->mRoughnessChan[0] = 1.0f; 200 mat->mInvertRoughness[0] = (floatVal == 1.0f); 201 mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Metallic 202 mat->mMetalChan[0] = 2.0f; 203 } 204 if (aoName.isNotEmpty()) 205 { 206 mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile, path, false); // occlusion 207 mat->mAOChan[0] = 0.0f; 208 } 209 else 210 { 211 mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // occlusion 212 mat->mAOChan[0] = 0.0f; 213 } 214 } 215 } 216#else 217 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), texName)) 218 { 219 torquePath = texName.C_Str(); 220 if (!torquePath.isEmpty()) 221 mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); 222 } 223 224 /*LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f); 225 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_SPECULAR, read_color)) 226 specularColor.set(read_color.r, read_color.g, read_color.b, opacity); 227 mat->mMetalness[0] = specularColor; 228 229 // Specular Power 230 F32 specularPower = 1.0f; 231 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS_STRENGTH, specularPower)) 232 mat->mSpecularPower[0] = specularPower; 233 234 // Specular 235 F32 specularStrength = 0.0f; 236 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS, specularStrength)) 237 mat->mSpecularStrength[0] = specularStrength;*/ 238#endif 239 240 // Double-Sided 241 bool doubleSided = false; 242 S32 dbl_sided = 0; 243 if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TWOSIDED, dbl_sided)) 244 doubleSided = (dbl_sided != 0); 245 mat->mDoubleSided = doubleSided; 246} 247 248String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly /*= false*/) 249{ 250 Torque::Path foundPath; 251 String cleanStr; 252 253 if (texName[0] == '*') 254 { // It's an embedded texture reference. Make the cached name and return 255 cleanStr = shapeName; 256 cleanStr += "_cachedTex"; 257 cleanStr += texName.substr(1); 258 return cleanStr; 259 } 260 261 // See if the file exists 262 bool fileFound = false; 263 String testPath = path.getPath(); 264 testPath += '/'; 265 testPath += texName; 266 testPath.replace('\\', '/'); 267 fileFound = Torque::FS::IsFile(testPath); 268 269 cleanStr = texName; 270 cleanStr.replace('\\', '/'); 271 if (fileFound) 272 { 273 if (cleanStr.equal(texName)) 274 return cleanStr; 275 foundPath = testPath; 276 } 277 else 278 { 279 // See if the file is in a sub-directory of the shape 280 Vector<String> foundFiles; 281 Torque::Path inPath(cleanStr); 282 String mainDotCsDir = Platform::getMainDotCsDir(); 283 mainDotCsDir += "/"; 284 S32 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir + path.getPath() + "/"), inPath.getFullFileName(), true, foundFiles); 285 if (results == 0 || foundFiles.size() == 0) // Not under shape directory, try the full tree 286 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir), inPath.getFullFileName(), true, foundFiles); 287 288 if (results > 0 && foundFiles.size() > 0) 289 { 290 fileFound = true; 291 foundPath = foundFiles[0]; 292 } 293 } 294 295 if (fileFound) 296 { 297 if (nameOnly) 298 cleanStr = foundPath.getFullFileName(); 299 else 300 { // Unless the file is in the same directory as the materials.tscript (covered above) 301 // we need to set the full path from the root directory. If we use "subdirectory/file.ext", 302 // the material manager won't find the image file, but it will be found the next time the 303 // material is loaded from file. If we use "./subdirectory/file.ext", the image will be found 304 // now, but not the next time it's loaded from file... 305 S32 rootLength = dStrlen(Platform::getMainDotCsDir()); 306 cleanStr = foundPath.getFullPathWithoutRoot().substr(rootLength-1); 307 } 308 } 309 else if (nameOnly) 310 cleanStr += " (Not Found)"; 311 312 return cleanStr; 313} 314 315#ifdef TORQUE_DEBUG 316void AssimpAppMaterial::enumerateMaterialProperties(aiMaterial* mtl) 317{ 318 for (U32 i = 0; i < mtl->mNumProperties; ++i) 319 { 320 aiMaterialProperty* matProp = mtl->mProperties[i]; 321 String outText; 322 if (matProp) 323 { 324 outText = String::ToString(" Key: %s, Index: %d, Semantic: ", matProp->mKey.C_Str(), matProp->mIndex); 325 switch (matProp->mSemantic) 326 { 327 case aiTextureType_NONE: 328 outText += "aiTextureType_NONE"; 329 break; 330 case aiTextureType_DIFFUSE: 331 outText += "aiTextureType_DIFFUSE"; 332 break; 333 case aiTextureType_SPECULAR: 334 outText += "aiTextureType_SPECULAR"; 335 break; 336 case aiTextureType_AMBIENT: 337 outText += "aiTextureType_AMBIENT"; 338 break; 339 case aiTextureType_EMISSIVE: 340 outText += "aiTextureType_EMISSIVE"; 341 break; 342 case aiTextureType_HEIGHT: 343 outText += "aiTextureType_HEIGHT"; 344 break; 345 case aiTextureType_NORMALS: 346 outText += "aiTextureType_NORMALS"; 347 break; 348 case aiTextureType_SHININESS: 349 outText += "aiTextureType_SHININESS"; 350 break; 351 case aiTextureType_OPACITY: 352 outText += "aiTextureType_OPACITY"; 353 break; 354 case aiTextureType_DISPLACEMENT: 355 outText += "aiTextureType_DISPLACEMENT"; 356 break; 357 case aiTextureType_LIGHTMAP: 358 outText += "aiTextureType_LIGHTMAP"; 359 break; 360 case aiTextureType_REFLECTION: 361 outText += "aiTextureType_REFLECTION"; 362 break; 363 default: 364 outText += "aiTextureType_UNKNOWN"; 365 break; 366 } 367 368 aiString stringProp; 369 F32* floatProp; 370 double* doubleProp; 371 S32* intProp; 372 373 switch (matProp->mType) 374 { 375 case aiPTI_Float: 376 floatProp = (F32*)matProp->mData; 377 for (U32 j = 0; j < matProp->mDataLength / sizeof(F32); ++j) 378 outText += String::ToString(", %0.4f", floatProp[j]); 379 break; 380 case aiPTI_Double: 381 doubleProp = (double*)matProp->mData; 382 for (U32 j = 0; j < matProp->mDataLength / sizeof(double); ++j) 383 outText += String::ToString(", %0.4lf", doubleProp[j]); 384 break; 385 case aiPTI_String: 386 aiGetMaterialString(mtl, matProp->mKey.C_Str(), matProp->mSemantic, matProp->mIndex, &stringProp); 387 outText += String::ToString(", %s", stringProp.C_Str()); 388 break; 389 case aiPTI_Integer: 390 intProp = (S32*)matProp->mData; 391 for (U32 j = 0; j < matProp->mDataLength / sizeof(S32); ++j) 392 outText += String::ToString(", %d", intProp[j]); 393 break; 394 case aiPTI_Buffer: 395 outText += ", aiPTI_Buffer format data"; 396 break; 397 default: 398 outText += ", Unknown data type"; 399 } 400 401 Con::printf("%s", outText.c_str()); 402 } 403 } 404} 405#endif 406