Torque3D Documentation / _generateds / assimpAppMaterial.cpp

assimpAppMaterial.cpp

Engine/source/ts/assimp/assimpAppMaterial.cpp

More...

Public Defines

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