123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #define TORQUE_PBR_MATERIALS
- #include "platform/platform.h"
- #include "ts/loader/appSequence.h"
- #include "ts/assimp/assimpAppMaterial.h"
- #include "ts/assimp/assimpAppMesh.h"
- #include "materials/materialManager.h"
- #include "ts/tsMaterialList.h"
- #include "core/stream/fileStream.h"
- // assimp include files.
- #include <assimp/cimport.h>
- #include <assimp/scene.h>
- #include <assimp/postprocess.h>
- #include <assimp/types.h>
- U32 AssimpAppMaterial::sDefaultMatNumber = 0;
- String AppMaterial::cleanString(const String& str)
- {
- String cleanStr(str);
- // Replace invalid characters with underscores
- const String badChars(" -,.+=*/[]%$~;:");
- for (String::SizeType i = 0; i < badChars.length(); i++)
- cleanStr.replace(badChars[i], '_');
- // Prefix with an underscore if string starts with a number
- if ((cleanStr[0] >= '0') && (cleanStr[0] <= '9'))
- cleanStr.insert(0, '_');
- return cleanStr;
- }
- AssimpAppMaterial::AssimpAppMaterial(const char* matName)
- {
- name = ColladaUtils::getOptions().matNamePrefix;
- name += matName;
- mAIMat = NULL;
- // Set some defaults
- flags |= TSMaterialList::S_Wrap;
- flags |= TSMaterialList::T_Wrap;
- }
- AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) :
- mAIMat(mtl)
- {
- aiString matName;
- mtl->Get(AI_MATKEY_NAME, matName);
- name = matName.C_Str();
- if (name.isEmpty())
- {
- name = cleanString(TSShapeLoader::getShapePath().getFileName());
- name += "_defMat";
- name += String::ToString("%d", sDefaultMatNumber);
- sDefaultMatNumber++;
- }
- name = ColladaUtils::getOptions().matNamePrefix + name;
- Con::printf("[ASSIMP] Loading Material: %s", name.c_str());
- #ifdef TORQUE_DEBUG
- enumerateMaterialProperties(mtl);
- #endif
- }
- Material* AssimpAppMaterial::createMaterial(const Torque::Path& path) const
- {
- // The filename and material name are used as TorqueScript identifiers, so
- // clean them up first
- String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
- String cleanName = cleanString(getName());
- // Create the Material definition
- const String oldScriptFile = Con::getVariable("$Con::File");
- Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct
- Material *newMat = MATMGR->allocateAndRegister(cleanName, getName());
- Con::setVariable("$Con::File", oldScriptFile); // restore script path
- initMaterial(path, newMat);
- return newMat;
- }
- void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) const
- {
- String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
- String cleanName = cleanString(getName());
- // Determine the blend mode and transparency for this material
- Material::BlendOp blendOp = Material::None;
- bool translucent = false;
- float opacity = 1.0f;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_OPACITY, opacity))
- {
- if (opacity != 1.0f)
- {
- translucent = true;
- int blendInt;
- blendOp = Material::LerpAlpha;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_BLEND_FUNC, blendInt))
- {
- if (blendInt == aiBlendMode_Additive)
- blendOp = Material::Add;
- }
- }
- }
- else
- { // No opacity key, see if it's defined as a gltf property
- aiString opacityMode;
- if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaMode", 0, 0, opacityMode))
- {
- if (String::compare("MASK", opacityMode.C_Str()) == 0)
- {
- translucent = true;
- blendOp = Material::None;
- float cutoff;
- if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff))
- {
- mat->mAlphaRef = (U32)(cutoff * 255); // alpha ref 0-255
- mat->mAlphaTest = true;
- }
- }
- else if (String::compare("BLEND", opacityMode.C_Str()) == 0)
- {
- translucent = true;
- blendOp = Material::LerpAlpha;
- mat->mAlphaTest = false;
- }
- else
- { // OPAQUE
- translucent = false;
- blendOp = Material::LerpAlpha; // Make default so it doesn't get written to materials.tscript
- }
- }
- }
- mat->mTranslucent = translucent;
- mat->mTranslucentBlendOp = blendOp;
- // Assign color values.
- LinearColorF diffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
- aiColor3D read_color(1.f, 1.f, 1.f);
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color))
- diffuseColor.set(read_color.r, read_color.g, read_color.b, opacity);
- mat->mDiffuse[0] = diffuseColor;
- aiString texName;
- String torquePath;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), texName))
- {
- torquePath = texName.C_Str();
- if (!torquePath.isEmpty())
- mat->mDiffuseMapName[0] = cleanTextureName(torquePath, cleanFile, path, false);
- }
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName))
- {
- torquePath = texName.C_Str();
- if (!torquePath.isEmpty())
- mat->mNormalMapName[0] = cleanTextureName(torquePath, cleanFile, path, false);
- }
- #ifdef TORQUE_PBR_MATERIALS
- float floatVal;
- if (AI_SUCCESS == mAIMat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, floatVal))
- { // The shape has pbr material definitions
- String aoName, rmName; // occlusion and roughness/metalness maps
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0), texName))
- aoName = texName.C_Str();
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName))
- rmName = texName.C_Str();
- if (aoName.isNotEmpty() || rmName.isNotEmpty())
- { // If we have either map, fill all three slots
- if (rmName.isNotEmpty())
- {
- mat->mRoughMapName[0] = cleanTextureName(rmName, cleanFile, path, false); // Roughness
- mat->mRoughnessChan[0] = 1.0f;
- mat->mInvertRoughness[0] = (floatVal == 1.0f);
- mat->mMetalMapName[0] = cleanTextureName(rmName, cleanFile, path, false); // Metallic
- mat->mMetalChan[0] = 2.0f;
- }
- if (aoName.isNotEmpty())
- {
- mat->mAOMapName[0] = cleanTextureName(aoName, cleanFile, path, false); // occlusion
- mat->mAOChan[0] = 0.0f;
- }
- else
- {
- mat->mAOMapName[0] = cleanTextureName(rmName, cleanFile, path, false); // occlusion
- mat->mAOChan[0] = 0.0f;
- }
- }
- }
- #else
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), texName))
- {
- torquePath = texName.C_Str();
- if (!torquePath.isEmpty())
- mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
- }
- /*LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f);
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_SPECULAR, read_color))
- specularColor.set(read_color.r, read_color.g, read_color.b, opacity);
- mat->mMetalness[0] = specularColor;
- // Specular Power
- F32 specularPower = 1.0f;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS_STRENGTH, specularPower))
- mat->mSpecularPower[0] = specularPower;
- // Specular
- F32 specularStrength = 0.0f;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS, specularStrength))
- mat->mSpecularStrength[0] = specularStrength;*/
- #endif
- // Double-Sided
- bool doubleSided = false;
- S32 dbl_sided = 0;
- if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TWOSIDED, dbl_sided))
- doubleSided = (dbl_sided != 0);
- mat->mDoubleSided = doubleSided;
- }
- String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly /*= false*/)
- {
- Torque::Path foundPath;
- String cleanStr;
- if (texName[0] == '*')
- { // It's an embedded texture reference. Make the cached name and return
- cleanStr = shapeName;
- cleanStr += "_cachedTex";
- cleanStr += texName.substr(1);
- return cleanStr;
- }
- // See if the file exists
- bool fileFound = false;
- String testPath = path.getPath();
- testPath += '/';
- testPath += texName;
- testPath.replace('\\', '/');
- fileFound = Torque::FS::IsFile(testPath);
- cleanStr = texName;
- cleanStr.replace('\\', '/');
- if (fileFound)
- {
- if (cleanStr.equal(texName))
- return cleanStr;
- foundPath = testPath;
- }
- else
- {
- // See if the file is in a sub-directory of the shape
- Vector<String> foundFiles;
- Torque::Path inPath(cleanStr);
- String mainDotCsDir = Platform::getMainDotCsDir();
- mainDotCsDir += "/";
- S32 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir + path.getPath() + "/"), inPath.getFullFileName(), true, foundFiles);
- if (results == 0 || foundFiles.size() == 0) // Not under shape directory, try the full tree
- results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir), inPath.getFullFileName(), true, foundFiles);
- if (results > 0 && foundFiles.size() > 0)
- {
- fileFound = true;
- foundPath = foundFiles[0];
- }
- }
- if (fileFound)
- {
- if (nameOnly)
- cleanStr = foundPath.getFullFileName();
- else
- { // Unless the file is in the same directory as the materials.tscript (covered above)
- // we need to set the full path from the root directory. If we use "subdirectory/file.ext",
- // the material manager won't find the image file, but it will be found the next time the
- // material is loaded from file. If we use "./subdirectory/file.ext", the image will be found
- // now, but not the next time it's loaded from file...
- S32 rootLength = dStrlen(Platform::getMainDotCsDir());
- cleanStr = foundPath.getFullPathWithoutRoot().substr(rootLength-1);
- }
- }
- else if (nameOnly)
- cleanStr += " (Not Found)";
-
- return cleanStr;
- }
- #ifdef TORQUE_DEBUG
- void AssimpAppMaterial::enumerateMaterialProperties(aiMaterial* mtl)
- {
- for (U32 i = 0; i < mtl->mNumProperties; ++i)
- {
- aiMaterialProperty* matProp = mtl->mProperties[i];
- String outText;
- if (matProp)
- {
- outText = String::ToString(" Key: %s, Index: %d, Semantic: ", matProp->mKey.C_Str(), matProp->mIndex);
- switch (matProp->mSemantic)
- {
- case aiTextureType_NONE:
- outText += "aiTextureType_NONE";
- break;
- case aiTextureType_DIFFUSE:
- outText += "aiTextureType_DIFFUSE";
- break;
- case aiTextureType_SPECULAR:
- outText += "aiTextureType_SPECULAR";
- break;
- case aiTextureType_AMBIENT:
- outText += "aiTextureType_AMBIENT";
- break;
- case aiTextureType_EMISSIVE:
- outText += "aiTextureType_EMISSIVE";
- break;
- case aiTextureType_HEIGHT:
- outText += "aiTextureType_HEIGHT";
- break;
- case aiTextureType_NORMALS:
- outText += "aiTextureType_NORMALS";
- break;
- case aiTextureType_SHININESS:
- outText += "aiTextureType_SHININESS";
- break;
- case aiTextureType_OPACITY:
- outText += "aiTextureType_OPACITY";
- break;
- case aiTextureType_DISPLACEMENT:
- outText += "aiTextureType_DISPLACEMENT";
- break;
- case aiTextureType_LIGHTMAP:
- outText += "aiTextureType_LIGHTMAP";
- break;
- case aiTextureType_REFLECTION:
- outText += "aiTextureType_REFLECTION";
- break;
- default:
- outText += "aiTextureType_UNKNOWN";
- break;
- }
- aiString stringProp;
- F32* floatProp;
- double* doubleProp;
- S32* intProp;
- switch (matProp->mType)
- {
- case aiPTI_Float:
- floatProp = (F32*)matProp->mData;
- for (U32 j = 0; j < matProp->mDataLength / sizeof(F32); ++j)
- outText += String::ToString(", %0.4f", floatProp[j]);
- break;
- case aiPTI_Double:
- doubleProp = (double*)matProp->mData;
- for (U32 j = 0; j < matProp->mDataLength / sizeof(double); ++j)
- outText += String::ToString(", %0.4lf", doubleProp[j]);
- break;
- case aiPTI_String:
- aiGetMaterialString(mtl, matProp->mKey.C_Str(), matProp->mSemantic, matProp->mIndex, &stringProp);
- outText += String::ToString(", %s", stringProp.C_Str());
- break;
- case aiPTI_Integer:
- intProp = (S32*)matProp->mData;
- for (U32 j = 0; j < matProp->mDataLength / sizeof(S32); ++j)
- outText += String::ToString(", %d", intProp[j]);
- break;
- case aiPTI_Buffer:
- outText += ", aiPTI_Buffer format data";
- break;
- default:
- outText += ", Unknown data type";
- }
- Con::printf("%s", outText.c_str());
- }
- }
- }
- #endif
|