Jelajahi Sumber

Merge branch 'Assimp_Settings' of https://github.com/OTHGMars/Torque3D into Preview4_0

Areloch 6 tahun lalu
induk
melakukan
ef226f6a65

+ 4 - 1
Engine/lib/assimp/code/glTF2Asset.h

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
@@ -1047,6 +1047,9 @@ namespace glTF2
         {
             bool KHR_materials_pbrSpecularGlossiness;
             bool KHR_materials_unlit;
+            //T3D_CHANGE_BEGIN
+            bool KHR_draco_mesh_compression;
+            //T3D_CHANGE_END
 
         } extensionsUsed;
 

+ 11 - 0
Engine/lib/assimp/code/glTF2Asset.inl

@@ -1360,6 +1360,14 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
     // Load the metadata
     asset.Read(doc);
     ReadExtensionsUsed(doc);
+    
+    //T3D_CHANGE_BEGIN
+    if (extensionsUsed.KHR_draco_mesh_compression)
+    {
+       ASSIMP_LOG_ERROR("GLTF: Draco mesh compression is not supported by Torque3D.");
+       throw DeadlyImportError("GLTF: Draco mesh compression is not supported by Torque3D.");
+    }
+    //T3D_CHANGE_END
 
     // Prepare the dictionaries
     for (size_t i = 0; i < mDicts.size(); ++i) {
@@ -1425,6 +1433,9 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
 
     CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
     CHECK_EXT(KHR_materials_unlit);
+    //T3D_CHANGE_BEGIN
+    CHECK_EXT(KHR_draco_mesh_compression);
+    //T3D_CHANGE_END
 
     #undef CHECK_EXT
 }

+ 5 - 0
Engine/lib/assimp/t3d_usage.md

@@ -0,0 +1,5 @@
+### Asset Import Library Usage Notes
+
+No single set of default values will be appropriate for all import formats and many do not provide any metadata to draw the information from, so Shape Import Settings (.sis) files have been added as a way to save and reuse import settings. You can create default settings for each shape file type that you will be importing by setting all of the values in the import options gui and selecting `Save Settings`. Save the file in the default settings location (game/tools, editable in EditorSettings) using the shape file extension as the file name i.e. fbx.sis, blend.sis, obj.sis. Default settings files have been provided for gltf files.
+
+Shapes can be re-imported via the Shape Editor by checking the `Source Art` box at the top of the inspector pane. Note: If the shape is currently loaded in the level, or is preloaded in any active datablock, the source art cannot be reloaded because the existing shape reference will not be released.

+ 77 - 18
Engine/source/ts/assimp/assimpAppMaterial.cpp

@@ -27,6 +27,7 @@
 #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>
@@ -34,6 +35,8 @@
 #include <assimp/postprocess.h>
 #include <assimp/types.h>
 
+U32 AssimpAppMaterial::sDefaultMatNumber = 0;
+
 String AppMaterial::cleanString(const String& str)
 {
    String cleanStr(str);
@@ -52,7 +55,8 @@ String AppMaterial::cleanString(const String& str)
 
 AssimpAppMaterial::AssimpAppMaterial(const char* matName)
 {
-   name = matName;
+   name = ColladaUtils::getOptions().matNamePrefix;
+   name += matName;
 
    // Set some defaults
    flags |= TSMaterialList::S_Wrap;
@@ -67,9 +71,12 @@ AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) :
    name = matName.C_Str();
    if (name.isEmpty())
    {
-      name = cleanString(TSShapeLoader::getShapePath().getFileName());;
+      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);
@@ -125,7 +132,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
          if (dStrcmp("MASK", opacityMode.C_Str()) == 0)
          {
             translucent = true;
-            blendOp = Material::LerpAlpha;
+            blendOp = Material::None;
 
             float cutoff;
             if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff))
@@ -134,10 +141,16 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
                mat->mAlphaTest = true;
             }
          }
-         else if (dStrcmp("OPAQUE", opacityMode.C_Str()) != 0)
+         else if (dStrcmp("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.cs
          }
       }
    }
@@ -157,14 +170,14 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
    {
       torquePath = texName.C_Str();
       if (!torquePath.isEmpty())
-         mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile);
+         mat->mDiffuseMapFilename[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->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile);
+         mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
    }
 
 #ifdef TORQUE_PBR_MATERIALS
@@ -177,27 +190,25 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
       if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName))
          rmName = texName.C_Str();
 
-      //if (aoName.isNotEmpty() && (aoName == rmName))
-      //   mat->mOrmMapFilename[0] = cleanTextureName(aoName, cleanFile); // It's an ORM map
-      //else if (aoName.isNotEmpty() || rmName.isNotEmpty())
+      mat->mIsSRGb[0] = true;
       if (aoName.isNotEmpty() || rmName.isNotEmpty())
       {  // If we have either map, fill all three slots
          if (rmName.isNotEmpty())
          {
-            mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile); // Roughness
+            mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Roughness
             mat->mSmoothnessChan[0] = 1.0f;
             mat->mInvertSmoothness = (floatVal == 1.0f);
-            mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile); // Metallic
+            mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Metallic
             mat->mMetalChan[0] = 2.0f;
          }
          if (aoName.isNotEmpty())
          {
-            mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile); // occlusion
+            mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile, path, false); // occlusion
             mat->mAOChan[0] = 0.0f;
          }
          else
          {
-            mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile); // occlusion
+            mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // occlusion
             mat->mAOChan[0] = 0.0f;
          }
       }
@@ -207,7 +218,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
    {
       torquePath = texName.C_Str();
       if (!torquePath.isEmpty())
-         mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile);
+         mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
    }
 
    /*LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f);
@@ -234,22 +245,70 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
    mat->mDoubleSided = doubleSided;
 }
 
-String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName)
+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
    {
-      cleanStr = texName;
-      cleanStr.replace('\\', '/');
+      // 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.cs (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;
 }
 

+ 3 - 1
Engine/source/ts/assimp/assimpAppMaterial.h

@@ -40,7 +40,6 @@ class AssimpAppMaterial : public AppMaterial
 #ifdef TORQUE_DEBUG
    void enumerateMaterialProperties(aiMaterial* mtl);
 #endif
-   static String cleanTextureName(String& texName, String& shapeName);
 
 public:
 
@@ -51,6 +50,9 @@ public:
    String getName() const { return name; }
    Material* createMaterial(const Torque::Path& path) const;
    void initMaterial(const Torque::Path& path, Material* mat) const;
+
+   static String cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly = false);
+   static U32 sDefaultMatNumber;
 };
 
 #endif // _ASSIMP_APPMATERIAL_H_

+ 8 - 5
Engine/source/ts/assimp/assimpAppMesh.cpp

@@ -79,7 +79,7 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
    uvs.reserve(mMeshData->mNumVertices);
    normals.reserve(mMeshData->mNumVertices);
 
-   bool flipNormals = Con::getBoolVariable("$Assimp::FlipNormals", false);
+   bool flipNormals = ColladaUtils::getOptions().invertNormals;
 
    bool noUVFound = false;
    for (U32 i = 0; i<mMeshData->mNumVertices; i++)
@@ -203,14 +203,17 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
       MatrixF boneTransform;
       AssimpAppNode::assimpToTorqueMat(mMeshData->mBones[b]->mOffsetMatrix, boneTransform);
       Point3F boneScale = boneTransform.getScale();
-      if (boneScale != Point3F::One)
+      Point3F bonePos = boneTransform.getPosition();
+      if (boneScale != Point3F::One && ColladaUtils::getOptions().ignoreNodeScale)
       {
          Point3F scaleMult = Point3F::One / boneScale;
-         Point3F scalePos = boneTransform.getPosition();
          boneTransform.scale(scaleMult);
-         scalePos /= scaleMult;
-         boneTransform.setPosition(scalePos);
+         bonePos /= scaleMult;
       }
+
+      bonePos *= ColladaUtils::getOptions().unit;
+      boneTransform.setPosition(bonePos);
+
       initialTransforms.push_back(boneTransform);
 
       //Weights

+ 20 - 15
Engine/source/ts/assimp/assimpAppNode.cpp

@@ -91,22 +91,30 @@ MatrixF AssimpAppNode::getTransform(F32 time)
    else {
       // no parent (ie. root level) => scale by global shape <unit>
       mLastTransform.identity();
+      mLastTransform.scale(ColladaUtils::getOptions().unit);
       if (!isBounds())
          convertMat(mLastTransform);
-
-      //mLastTransform.scale(ColladaUtils::getOptions().unit);
    }
 
    // If this node is animated in the active sequence, fetch the animated transform
+   MatrixF mat(true);
    if (sActiveSequence)
-   {
-      MatrixF mat(true);
       getAnimatedTransform(mat, time, sActiveSequence);
-      mLastTransform.mul(mat);
-   }
    else
-      mLastTransform.mul(mNodeTransform);
-   
+      mat = mNodeTransform;
+
+   // Remove node scaling?
+   Point3F nodeScale = mat.getScale();
+   if (nodeScale != Point3F::One && appParent && ColladaUtils::getOptions().ignoreNodeScale)
+   {
+      nodeScale.x = nodeScale.x ? (1.0f / nodeScale.x) : 0;
+      nodeScale.y = nodeScale.y ? (1.0f / nodeScale.y) : 0;
+      nodeScale.z = nodeScale.z ? (1.0f / nodeScale.z) : 0;
+      mat.scale(nodeScale);
+   }
+
+   mLastTransform.mul(mat);
+
    mLastTransformTime = time;
    return mLastTransform;
 }
@@ -280,12 +288,9 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
 {
    MatrixF rot(true);
 
-   // This is copied directly from ColladaUtils::convertTransform()
-   // ColladaUtils::getOptions().upAxis has been temporarily replaced with $Assimp::OverrideUpAxis for testing
-   // We need a plan for how the full set of assimp import options and settings is going to be managed.
-   switch (Con::getIntVariable("$Assimp::OverrideUpAxis", 2))
+   switch (ColladaUtils::getOptions().upAxis)
    {
-   case 0: //UPAXISTYPE_X_UP:
+   case UPAXISTYPE_X_UP:
       // rotate 90 around Y-axis, then 90 around Z-axis
       rot(0, 0) = 0.0f;  rot(1, 0) = 1.0f;
       rot(1, 1) = 0.0f;	rot(2, 1) = 1.0f;
@@ -295,7 +300,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
       outMat.mulL(rot);
       break;
 
-   case 1: //UPAXISTYPE_Y_UP:
+   case UPAXISTYPE_Y_UP:
       // rotate 180 around Y-axis, then 90 around X-axis
       rot(0, 0) = -1.0f;
       rot(1, 1) = 0.0f;	rot(2, 1) = 1.0f;
@@ -305,7 +310,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
       outMat.mulL(rot);
       break;
 
-   case 2: //UPAXISTYPE_Z_UP:
+   case UPAXISTYPE_Z_UP:
    default:
       // nothing to do
       break;

+ 2 - 5
Engine/source/ts/assimp/assimpAppSequence.cpp

@@ -58,8 +58,8 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
       }
    }
 
-   S32 timeFactor = Con::getIntVariable("$Assimp::AnimTiming", 1);
-   S32 fpsRequest = Con::getIntVariable("$Assimp::AnimFPS", 30);
+   S32 timeFactor = ColladaUtils::getOptions().animTiming;
+   S32 fpsRequest = ColladaUtils::getOptions().animFPS;
    if (timeFactor == 0)
    {  // Timing specified in frames
       fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
@@ -70,10 +70,7 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
    else
    {  // Timing specified in seconds or ms depending on format
       if (maxEndTime > 1000.0f || mAnim->mDuration > 1000.0f)
-      {
          timeFactor = 1000.0f;   // If it's more than 1000 seconds, assume it's ms.
-         Con::setIntVariable("$Assimp::AnimTiming", 1000);
-      }
 
       timeFactor = mClamp(timeFactor, 1, 1000);
       minFrameTime /= (F32)timeFactor;

+ 359 - 97
Engine/source/ts/assimp/assimpShapeLoader.cpp

@@ -39,6 +39,7 @@
 
 #include "core/util/tVector.h"
 #include "core/strings/findMatch.h"
+#include "core/strings/stringUnit.h"
 #include "core/stream/fileStream.h"
 #include "core/fileObject.h"
 #include "ts/tsShape.h"
@@ -133,29 +134,16 @@ void AssimpShapeLoader::enumerateScene()
 
    // Post-Processing
    unsigned int ppsteps = 
-       Con::getBoolVariable("$Assimp::ConvertToLeftHanded", false) ? aiProcess_ConvertToLeftHanded : 0 |
-       Con::getBoolVariable("$Assimp::CalcTangentSpace", false) ? aiProcess_CalcTangentSpace : 0 |
-       Con::getBoolVariable("$Assimp::JoinIdenticalVertices", false) ? aiProcess_JoinIdenticalVertices : 0 |
-       Con::getBoolVariable("$Assimp::ValidateDataStructure", false) ? aiProcess_ValidateDataStructure : 0 |
-       Con::getBoolVariable("$Assimp::ImproveCacheLocality", false) ? aiProcess_ImproveCacheLocality : 0 |
-       Con::getBoolVariable("$Assimp::RemoveRedundantMaterials", false) ? aiProcess_RemoveRedundantMaterials : 0 |
-       Con::getBoolVariable("$Assimp::FindDegenerates", false) ? aiProcess_FindDegenerates : 0 |
-       Con::getBoolVariable("$Assimp::FindInvalidData", false) ? aiProcess_FindInvalidData : 0 |
-       Con::getBoolVariable("$Assimp::GenUVCoords", false) ? aiProcess_GenUVCoords : 0 |
-       Con::getBoolVariable("$Assimp::TransformUVCoords", false) ? aiProcess_TransformUVCoords : 0 |
-       Con::getBoolVariable("$Assimp::FindInstances", false) ? aiProcess_FindInstances : 0 |
-       Con::getBoolVariable("$Assimp::LimitBoneWeights", false) ? aiProcess_LimitBoneWeights : 0 |
-       Con::getBoolVariable("$Assimp::OptimizeMeshes", false) ? aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph : 0 |
-       0;
-
-   if(Con::getBoolVariable("$Assimp::FlipUVs", true))
-      ppsteps |= aiProcess_FlipUVs;
-
-   if(Con::getBoolVariable("$Assimp::FlipWindingOrder", false))
-      ppsteps |= aiProcess_FlipWindingOrder;
-
-   if(Con::getBoolVariable("$Assimp::Triangulate", true))
-      ppsteps |= aiProcess_Triangulate;
+      (ColladaUtils::getOptions().convertLeftHanded ? aiProcess_MakeLeftHanded : 0) |
+      (ColladaUtils::getOptions().reverseWindingOrder ? aiProcess_FlipWindingOrder : 0) |
+      (ColladaUtils::getOptions().calcTangentSpace ? aiProcess_CalcTangentSpace : 0) |
+      (ColladaUtils::getOptions().joinIdenticalVerts ? aiProcess_JoinIdenticalVertices : 0) |
+      (ColladaUtils::getOptions().removeRedundantMats ? aiProcess_RemoveRedundantMaterials : 0) |
+      (ColladaUtils::getOptions().genUVCoords ? aiProcess_GenUVCoords : 0) |
+      (ColladaUtils::getOptions().transformUVCoords ? aiProcess_TransformUVCoords : 0) |
+      (ColladaUtils::getOptions().flipUVCoords ? aiProcess_FlipUVs : 0) |
+      (ColladaUtils::getOptions().findInstances ? aiProcess_FindInstances : 0) |
+      (ColladaUtils::getOptions().limitBoneWeights ? aiProcess_LimitBoneWeights : 0);
 
    if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false))
       ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph;
@@ -163,34 +151,21 @@ void AssimpShapeLoader::enumerateScene()
    if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false))
       ppsteps |= aiProcess_SplitLargeMeshes;
 
+   // Mandatory options
+   //ppsteps |= aiProcess_ValidateDataStructure | aiProcess_Triangulate | aiProcess_ImproveCacheLocality;
+   ppsteps |= aiProcess_Triangulate;
    //aiProcess_SortByPType              | // make 'clean' meshes which consist of a single typ of primitives
 
    aiPropertyStore* props = aiCreatePropertyStore();
 
-   //aiSetImportPropertyInteger(props,   AI_CONFIG_IMPORT_TER_MAKE_UVS,         1);
-   //aiSetImportPropertyInteger(props,   AI_CONFIG_PP_SBP_REMOVE,               (aiProcessPreset_TargetRealtime_Quality 
-   //                                                                                 | aiProcess_FlipWindingOrder | aiProcess_FlipUVs 
-   //                                                                                 | aiProcess_CalcTangentSpace
-   //                                                                                 | aiProcess_FixInfacingNormals) 
-   //                                                                                    & ~aiProcess_RemoveRedundantMaterials);
-   //aiSetImportPropertyInteger(props,   AI_CONFIG_GLOB_MEASURE_TIME,           1);
-   //aiSetImportPropertyFloat(props,     AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,  80.f);
-   //aiSetImportPropertyInteger(props,AI_CONFIG_PP_PTV_KEEP_HIERARCHY,1);
-
-   struct aiLogStream shapeLog;
-   shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_FILE, "assimp.log");
+   struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
+   shapeLog.callback = assimpLogCallback;
+   shapeLog.user = 0;
    aiAttachLogStream(&shapeLog);
 #ifdef TORQUE_DEBUG
    aiEnableVerboseLogging(true);
 #endif
 
-   //c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
-   //aiAttachLogStream(&c);
-
-   // Attempt to import with Assimp.
-   //mScene = importer.ReadFile(shapePath.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_CalcTangentSpace)
-   //   & ~aiProcess_RemoveRedundantMaterials);
-
    mScene = (aiScene*)aiImportFileExWithProperties(shapePath.getFullPath().c_str(), ppsteps, NULL, props);
 
    aiReleasePropertyStore(props);
@@ -200,11 +175,38 @@ void AssimpShapeLoader::enumerateScene()
       Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
       Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
 
+      // Set import options (if they are not set to override)
+      if (ColladaUtils::getOptions().unit <= 0.0f)
+      {
+         F64 unit;
+         if (!getMetaDouble("UnitScaleFactor", unit))
+         {
+            F32 floatVal;
+            S32 intVal;
+            if (getMetaFloat("UnitScaleFactor", floatVal))
+               unit = (F64)floatVal;
+            else if (getMetaInt("UnitScaleFactor", intVal))
+               unit = (F64)intVal;
+            else
+               unit = 1.0;
+         }
+         ColladaUtils::getOptions().unit = (F32)unit;
+      }
+
+      if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT)
+      {
+         S32 upAxis;
+         if (!getMetaInt("UpAxis", upAxis))
+            upAxis = UPAXISTYPE_Z_UP;
+         ColladaUtils::getOptions().upAxis = (domUpAxisType) upAxis;
+      }
+
       // Extract embedded textures
       for (U32 i = 0; i < mScene->mNumTextures; ++i)
          extractTexture(i, mScene->mTextures[i]);
 
       // Load all the materials.
+      AssimpAppMaterial::sDefaultMatNumber = 0;
       for ( U32 i = 0; i < mScene->mNumMaterials; i++ )
          AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
 
@@ -245,8 +247,8 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds)
    TSShapeLoader::computeBounds(bounds);
 
    // Check if the model origin needs adjusting
-   bool adjustCenter = Con::getBoolVariable("$Assimp::adjustCenter", false); //ColladaUtils::getOptions().adjustCenter
-   bool adjustFloor = Con::getBoolVariable("$Assimp::adjustFloor", false); //ColladaUtils::getOptions().adjustFloor
+   bool adjustCenter = ColladaUtils::getOptions().adjustCenter;
+   bool adjustFloor = ColladaUtils::getOptions().adjustFloor;
    if (bounds.isValidBox() && (adjustCenter || adjustFloor))
    {
       // Compute shape offset
@@ -289,6 +291,126 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds)
    }
 }
 
+bool AssimpShapeLoader::fillGuiTreeView(const char* sourceShapePath, GuiTreeViewCtrl* tree)
+{
+   Assimp::Importer importer;
+   Torque::Path path(sourceShapePath);
+   String cleanFile = AppMaterial::cleanString(path.getFileName());
+
+   // Attempt to import with Assimp.
+   const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace)
+      & ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals);
+
+   if (!shapeScene)
+      return false;
+   mScene = shapeScene;
+
+   // Initialize tree
+   tree->removeItem(0);
+   S32 meshItem = tree->insertItem(0, "Meshes", String::ToString("%i", shapeScene->mNumMeshes));
+   S32 matItem = tree->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials));
+   S32 animItem = tree->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations));
+   //S32 lightsItem = tree->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights));
+   //S32 texturesItem = tree->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures));
+
+   //Details!
+   U32 numPolys = 0;
+   U32 numVerts = 0;
+   for (U32 i = 0; i < shapeScene->mNumMeshes; i++)
+   {
+      tree->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str()));
+      numPolys += shapeScene->mMeshes[i]->mNumFaces;
+      numVerts += shapeScene->mMeshes[i]->mNumVertices;
+   }
+
+   U32 defaultMatNumber = 0;
+   for (U32 i = 0; i < shapeScene->mNumMaterials; i++)
+   {
+      aiMaterial* aiMat = shapeScene->mMaterials[i];
+
+      aiString matName;
+      aiMat->Get(AI_MATKEY_NAME, matName);
+      String name = matName.C_Str();
+      if (name.isEmpty())
+      {
+         name = AppMaterial::cleanString(path.getFileName());
+         name += "_defMat";
+         name += String::ToString("%d", defaultMatNumber);
+         defaultMatNumber++;
+      }
+
+      aiString texPath;
+      aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath);
+      String texName = texPath.C_Str();
+      if (texName.isEmpty())
+      {
+         aiColor3D read_color(1.f, 1.f, 1.f);
+         if (AI_SUCCESS == aiMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color))
+            texName = String::ToString("Color: (%0.3f, %0.3f, %0.3f)", (F32)read_color.r, (F32)read_color.g, (F32)read_color.b);
+         else
+            texName = "No Texture";
+      }
+      else
+         texName = AssimpAppMaterial::cleanTextureName(texName, cleanFile, sourceShapePath, true);
+
+      tree->insertItem(matItem, String::ToString("%s", name.c_str()), String::ToString("%s", texName.c_str()));
+   }
+
+   for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
+   {
+      String sequenceName = shapeScene->mAnimations[i]->mName.C_Str();
+      if (sequenceName.isEmpty())
+         sequenceName = "ambient";
+      tree->insertItem(animItem, sequenceName.c_str());
+   }
+
+   U32 numNodes = 0;
+   if (shapeScene->mRootNode)
+   {
+      S32 nodesItem = tree->insertItem(0, "Nodes", "");
+      addNodeToTree(nodesItem, shapeScene->mRootNode, tree, numNodes);
+      tree->setItemValue(nodesItem, String::ToString("%i", numNodes));
+   }
+
+   U32 numMetaTags = shapeScene->mMetaData ? shapeScene->mMetaData->mNumProperties : 0;
+   if (numMetaTags)
+      addMetaDataToTree(shapeScene->mMetaData, tree);
+
+   F64 unit;
+   if (!getMetaDouble("UnitScaleFactor", unit))
+      unit = 1.0f;
+
+   S32 upAxis;
+   if (!getMetaInt("UpAxis", upAxis))
+      upAxis = UPAXISTYPE_Z_UP;
+
+   /*for (U32 i = 0; i < shapeScene->mNumLights; i++)
+   {
+      treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType));
+   }*/
+
+   // Store shape information in the tree control
+   tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", numNodes));
+   tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", shapeScene->mNumMeshes));
+   tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", numPolys));
+   tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", shapeScene->mNumMaterials));
+   tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", shapeScene->mNumLights));
+   tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", shapeScene->mNumAnimations));
+   tree->setDataField(StringTable->insert("_textureCount"), 0, avar("%d", shapeScene->mNumTextures));
+   tree->setDataField(StringTable->insert("_vertCount"), 0, avar("%d", numVerts));
+   tree->setDataField(StringTable->insert("_metaTagCount"), 0, avar("%d", numMetaTags));
+   tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", (F32)unit));
+
+   if (upAxis == UPAXISTYPE_X_UP)
+      tree->setDataField(StringTable->insert("_upAxis"), 0, "X_AXIS");
+   else if (upAxis == UPAXISTYPE_Y_UP)
+      tree->setDataField(StringTable->insert("_upAxis"), 0, "Y_AXIS");
+   else
+      tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS");
+
+   return true;
+}
+
 void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
 {
    Torque::Path scriptPath(path);
@@ -306,7 +428,7 @@ void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
          if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) )
          {
             // Only update existing materials if forced to
-            if (Con::getBoolVariable("$Assimp::ForceUpdateMats", false))
+            if (ColladaUtils::getOptions().forceUpdateMaterials)
             {
                mat->initMaterial(scriptPath, mappedMat);
                persistMgr.setDirty(mappedMat);
@@ -351,20 +473,37 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
    return false;
 }
 
+void AssimpShapeLoader::assimpLogCallback(const char* message, char* user)
+{
+   Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n"));
+}
+
 bool AssimpShapeLoader::ignoreNode(const String& name)
 {
    // Do not add AssimpFbx dummy nodes to the TSShape. See: Assimp::FBX::ImportSettings::preservePivots
    // https://github.com/assimp/assimp/blob/master/code/FBXImportSettings.h#L116-L135
    if (name.find("_$AssimpFbx$_") != String::NPos)
       return true;
-   return false;
+
+   if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImport, name, false))
+      return false;
+
+   return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImport, name, false);
+}
+
+bool AssimpShapeLoader::ignoreMesh(const String& name)
+{
+   if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImportMesh, name, false))
+      return false;
+   else
+      return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMesh, name, false);
 }
 
 void AssimpShapeLoader::detectDetails()
 {
    // Set LOD option
    bool singleDetail = true;
-   switch (Con::getIntVariable("$Assimp::lodType", 0))
+   switch (ColladaUtils::getOptions().lodType)
    {
    case ColladaUtils::ImportOptions::DetectDTS:
       // Check for a baseXX->startXX hierarchy at the top-level, if we find
@@ -395,7 +534,7 @@ void AssimpShapeLoader::detectDetails()
       break;
    }
 
-   AssimpAppMesh::fixDetailSize(singleDetail, Con::getIntVariable("$Assimp::singleDetailSize", 2));
+   AssimpAppMesh::fixDetailSize(singleDetail, ColladaUtils::getOptions().singleDetailSize);
 }
 
 void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex)
@@ -448,6 +587,148 @@ void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex)
    }
 }
 
+void AssimpShapeLoader::addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount)
+{
+   // Add this node
+   S32 nodeItem = parentItem;
+   String nodeName = node->mName.C_Str();
+   if (!ignoreNode(nodeName))
+   {
+      if (nodeName.isEmpty())
+         nodeName = "null";
+      nodeItem = tree->insertItem(parentItem, nodeName.c_str(), String::ToString("%i", node->mNumChildren));
+      nodeCount++;
+   }
+
+   // Add any child nodes
+   for (U32 n = 0; n < node->mNumChildren; ++n)
+      addNodeToTree(nodeItem, node->mChildren[n], tree, nodeCount);
+}
+
+void AssimpShapeLoader::addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree)
+{
+   S32 metaItem = tree->insertItem(0, "MetaData", String::ToString("%i", metaData->mNumProperties));
+
+   aiString valString;
+   aiVector3D valVec;
+
+   for (U32 n = 0; n < metaData->mNumProperties; ++n)
+   {
+      String keyStr = metaData->mKeys[n].C_Str();
+      keyStr += ": ";
+      switch (metaData->mValues[n].mType)
+      {
+      case AI_BOOL:
+         keyStr += ((bool)metaData->mValues[n].mData) ? "true" : "false";
+         break;
+      case AI_INT32:
+         keyStr += String::ToString(*((S32*)(metaData->mValues[n].mData)));
+         break;
+      case AI_UINT64:
+         keyStr += String::ToString("%I64u", *((U64*)metaData->mValues[n].mData));
+         break;
+      case AI_FLOAT:
+         keyStr += String::ToString(*((F32*)metaData->mValues[n].mData));
+         break;
+      case AI_DOUBLE:
+         keyStr += String::ToString(*((F64*)metaData->mValues[n].mData));
+         break;
+      case AI_AISTRING:
+         metaData->Get<aiString>(metaData->mKeys[n], valString);
+         keyStr += valString.C_Str();
+         break;
+      case AI_AIVECTOR3D:
+         metaData->Get<aiVector3D>(metaData->mKeys[n], valVec);
+         keyStr += String::ToString("%f, %f, %f", valVec.x, valVec.y, valVec.z);
+         break;
+      default:
+         break;
+      }
+
+      tree->insertItem(metaItem, keyStr.c_str(), String::ToString("%i", n));
+   }
+}
+
+bool AssimpShapeLoader::getMetabool(const char* key, bool& boolVal)
+{
+   if (!mScene || !mScene->mMetaData)
+      return false;
+
+   String keyStr = key;
+   for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
+   {
+      if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
+      {
+         if (mScene->mMetaData->mValues[n].mType == AI_BOOL)
+         {
+            boolVal = (bool)mScene->mMetaData->mValues[n].mData;
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+bool AssimpShapeLoader::getMetaInt(const char* key, S32& intVal)
+{
+   if (!mScene || !mScene->mMetaData)
+      return false;
+
+   String keyStr = key;
+   for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
+   {
+      if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
+      {
+         if (mScene->mMetaData->mValues[n].mType == AI_INT32)
+         {
+            intVal = *((S32*)(mScene->mMetaData->mValues[n].mData));
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+bool AssimpShapeLoader::getMetaFloat(const char* key, F32& floatVal)
+{
+   if (!mScene || !mScene->mMetaData)
+      return false;
+
+   String keyStr = key;
+   for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
+   {
+      if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
+      {
+         if (mScene->mMetaData->mValues[n].mType == AI_FLOAT)
+         {
+            floatVal = *((F32*)mScene->mMetaData->mValues[n].mData);
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+bool AssimpShapeLoader::getMetaDouble(const char* key, F64& doubleVal)
+{
+   if (!mScene || !mScene->mMetaData)
+      return false;
+
+   String keyStr = key;
+   for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
+   {
+      if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
+      {
+         if (mScene->mMetaData->mValues[n].mType == AI_DOUBLE)
+         {
+            doubleVal = *((F64*)mScene->mMetaData->mValues[n].mData);
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
 //-----------------------------------------------------------------------------
 /// This function is invoked by the resource manager based on file extension.
 TSShape* assimpLoadShape(const Torque::Path &path)
@@ -489,6 +770,14 @@ TSShape* assimpLoadShape(const Torque::Path &path)
       return NULL;
    }
 
+   // Allow TSShapeConstructor object to override properties
+   ColladaUtils::getOptions().reset();
+   TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructor(path.getFullPath());
+   if (tscon)
+   {
+      ColladaUtils::getOptions() = tscon->mOptions;
+   }
+
    AssimpShapeLoader loader;
    TSShape* tss = loader.generateShape(path);
    if (tss)
@@ -510,57 +799,30 @@ TSShape* assimpLoadShape(const Torque::Path &path)
    return tss;
 }
 
-DefineEngineFunction(GetShapeInfo, GuiTreeViewCtrl*, (String filePath), ,
-   "Returns a list of supported shape formats in filter form.\n"
-   "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|")
+DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl), ,
+   "(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from "
+   "a shape file and store it in a GuiTreeView control. This function is "
+   "used by the assimp import gui to show a preview of the scene contents "
+   "prior to import, and is probably not much use for anything else.\n"
+   "@param shapePath shape filename\n"
+   "@param ctrl GuiTreeView control to add elements to\n"
+   "@return true if successful, false otherwise\n"
+   "@ingroup Editors\n"
+   "@internal")
 {
-   Assimp::Importer importer;
-
-   GuiTreeViewCtrl* treeObj = new GuiTreeViewCtrl();
-   treeObj->registerObject();
-
-   Torque::Path path = Torque::Path(filePath);
-
-   // Attempt to import with Assimp.
-   const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace)
-      & ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals);
-
-   //Populate info
-   S32 meshItem = treeObj->insertItem(0, "Shape", String::ToString("%i", shapeScene->mNumMeshes));
-   S32 matItem = treeObj->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials));
-   S32 animItem = treeObj->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations));
-   S32 lightsItem = treeObj->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights));
-   S32 texturesItem = treeObj->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures));
-   //S32 meshItem = ->insertItem(0, "Cameras", String::ToString("%s", shapeScene->mNumCameras));
-
-   //Details!
-   for (U32 i = 0; i < shapeScene->mNumMeshes; i++)
-   {
-      treeObj->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str()));
-   }
-
-   for (U32 i = 0; i < shapeScene->mNumMaterials; i++)
+   GuiTreeViewCtrl* tree;
+   if (!Sim::findObject(ctrl, tree))
    {
-      aiMaterial* aiMat = shapeScene->mMaterials[i];
-
-      aiString matName;
-      aiMat->Get(AI_MATKEY_NAME, matName);
-
-      aiString texPath;
-      aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath);
-
-      treeObj->insertItem(matItem, String::ToString("%s", matName.C_Str()), String::ToString("%s", texPath.C_Str()));
+      Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", ctrl);
+      return false;
    }
 
-   for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
-   {
-      treeObj->insertItem(animItem, String::ToString("%s", shapeScene->mAnimations[i]->mName.C_Str()));
-   }
-
-   /*for (U32 i = 0; i < shapeScene->mNumLights; i++)
-   {
-      treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType));
-   }*/
+   // Check if a cached DTS is available => no need to import the source file
+   // if we can load the DTS instead
+   Torque::Path path(shapePath);
+   if (AssimpShapeLoader::canLoadCachedDTS(path))
+      return false;
 
-   return treeObj;
-}
+   AssimpShapeLoader loader;
+   return loader.fillGuiTreeView(shapePath, tree);
+}

+ 15 - 0
Engine/source/ts/assimp/assimpShapeLoader.h

@@ -28,6 +28,9 @@
 #endif
 #include <assimp/texture.h>
 
+class GuiTreeViewCtrl;
+struct aiNode;
+struct aiMetadata;
 //-----------------------------------------------------------------------------
 class AssimpShapeLoader : public TSShapeLoader
 {
@@ -37,9 +40,18 @@ protected:
    const struct aiScene* mScene;
 
    virtual bool ignoreNode(const String& name);
+   virtual bool ignoreMesh(const String& name);
    void detectDetails();
    void extractTexture(U32 index, aiTexture* pTex);
 
+private:
+   void addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount);
+   void addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree);
+   bool getMetabool(const char* key, bool& boolVal);
+   bool getMetaInt(const char* key, S32& intVal);
+   bool getMetaFloat(const char* key, F32& floatVal);
+   bool getMetaDouble(const char* key, F64& doubleVal);
+
 public:
    AssimpShapeLoader();
    ~AssimpShapeLoader();
@@ -51,7 +63,10 @@ public:
 
    void computeBounds(Box3F& bounds);
 
+   bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree);
+
    static bool canLoadCachedDTS(const Torque::Path& path);
+   static void assimpLogCallback(const char* message, char* user);
 };
 
 #endif // _ASSIMP_SHAPELOADER_H_

+ 37 - 0
Engine/source/ts/collada/colladaUtils.h

@@ -81,6 +81,13 @@ namespace ColladaUtils
          NumLodTypes
       };
 
+      enum eAnimTimingType
+      {
+         FrameCount = 0,
+         Seconds = 1,
+         Milliseconds = 1000
+      };
+
       domUpAxisType  upAxis;           // Override for the collada <up_axis> element
       F32            unit;             // Override for the collada <unit> element
       eLodType       lodType;          // LOD type option
@@ -97,6 +104,22 @@ namespace ColladaUtils
       bool           forceUpdateMaterials;   // Force update of materials.cs
       bool           useDiffuseNames;  // Use diffuse texture as the material name
 
+      // Assimp specific preprocess import options
+      bool           convertLeftHanded;   // Convert to left handed coordinate system.
+      bool           calcTangentSpace;    // Calculate tangents and bitangents, if possible.
+      bool           genUVCoords;         // Convert spherical, cylindrical, box and planar mapping to proper UVs.
+      bool           transformUVCoords;   // Preprocess UV transformations (scaling, translation ...)
+      bool           flipUVCoords;        // This step flips all UV coordinates along the y-axis and adjusts material settings
+                                          // and bitangents accordingly.\nAssimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0).
+      bool           findInstances;       // Search for instanced meshes and remove them by references to one master.
+      bool           limitBoneWeights;    // Limit bone weights to 4 per vertex.
+      bool           joinIdenticalVerts;  // Identifies and joins identical vertex data sets within all imported meshes.
+      bool           reverseWindingOrder; // This step adjusts the output face winding order to be clockwise. The default face winding order is counter clockwise.
+      bool           invertNormals;       // Reverse the normal vector direction for all normals.
+      bool           removeRedundantMats; // Removes redundant materials.
+      eAnimTimingType animTiming;         // How to import timing data as frames, seconds or milliseconds
+      S32            animFPS;             // FPS value to use if timing is set in frames and the animations does not have an fps set
+
       ImportOptions()
       {
          reset();
@@ -119,6 +142,20 @@ namespace ColladaUtils
          adjustFloor = false;
          forceUpdateMaterials = false;
          useDiffuseNames = false;
+
+         convertLeftHanded = false;
+         calcTangentSpace = false;
+         genUVCoords = false;
+         transformUVCoords = false;
+         flipUVCoords = true;
+         findInstances = false;
+         limitBoneWeights = false;
+         joinIdenticalVerts = true;
+         reverseWindingOrder = true;
+         invertNormals = false;
+         removeRedundantMats = true;
+         animTiming = Seconds;
+         animFPS = 30;
       }
    };
 

+ 52 - 0
Engine/source/ts/tsShapeConstruct.cpp

@@ -71,6 +71,14 @@ ImplementEnumType( TSShapeConstructorLodType,
    { ColladaUtils::ImportOptions::TrailingNumber,  "TrailingNumber" },
 EndImplementEnumType;
 
+ImplementEnumType(TSShapeConstructorAnimType,
+   "\n\n"
+   "@ingroup TSShapeConstructor" )
+   { ColladaUtils::ImportOptions::FrameCount,      "Frames" },
+   { ColladaUtils::ImportOptions::Seconds,         "Seconds" },
+   { ColladaUtils::ImportOptions::Milliseconds,    "Milliseconds" },
+EndImplementEnumType;
+
 
 //-----------------------------------------------------------------------------
 
@@ -150,6 +158,21 @@ TSShapeConstructor::TSShapeConstructor()
    mOptions.adjustFloor = false;
    mOptions.forceUpdateMaterials = false;
    mOptions.useDiffuseNames = false;
+
+   mOptions.convertLeftHanded = false;
+   mOptions.calcTangentSpace = false;
+   mOptions.genUVCoords = false;
+   mOptions.transformUVCoords = false;
+   mOptions.flipUVCoords = true;
+   mOptions.findInstances = false;
+   mOptions.limitBoneWeights = false;
+   mOptions.joinIdenticalVerts = true;
+   mOptions.reverseWindingOrder = true;
+   mOptions.invertNormals = false;
+   mOptions.removeRedundantMats = true;
+   mOptions.animTiming = ColladaUtils::ImportOptions::Seconds;
+   mOptions.animFPS = 30;
+
    mShape = NULL;
 }
 
@@ -292,6 +315,35 @@ void TSShapeConstructor::initPersistFields()
       "Forces update of the materials.cs file in the same folder as the COLLADA "
       "(.dae) file, even if Materials already exist. No effect for DTS files.\n"
       "Normally only Materials that are not already defined are written to materials.cs." );
+
+   // Fields added for assimp options
+   addField( "convertLeftHanded", TypeBool, Offset(mOptions.convertLeftHanded, TSShapeConstructor),
+      "Convert to left handed coordinate system." );
+   addField( "calcTangentSpace", TypeBool, Offset(mOptions.calcTangentSpace, TSShapeConstructor),
+      "Calculate tangents and bitangents, if possible." );
+   addField( "genUVCoords", TypeBool, Offset(mOptions.genUVCoords, TSShapeConstructor),
+      "Convert spherical, cylindrical, box and planar mapping to proper UVs." );
+   addField( "transformUVCoords", TypeBool, Offset(mOptions.transformUVCoords, TSShapeConstructor),
+      "Preprocess UV transformations (scaling, translation ...)." );
+   addField( "flipUVCoords", TypeBool, Offset(mOptions.flipUVCoords, TSShapeConstructor),
+      "This step flips all UV coordinates along the y-axis and adjusts material settings and bitangents accordingly.\n"
+      "Assimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0). This will be needed for most textured models." );
+   addField( "findInstances", TypeBool, Offset(mOptions.findInstances, TSShapeConstructor),
+      "Search for instanced meshes and remove them by references to one master." );
+   addField( "limitBoneWeights", TypeBool, Offset(mOptions.limitBoneWeights, TSShapeConstructor),
+      "Limit bone weights to 4 per vertex." );
+   addField( "joinIdenticalVerts", TypeBool, Offset(mOptions.joinIdenticalVerts, TSShapeConstructor),
+      "Identifies and joins identical vertex data sets within all imported meshes." );
+   addField( "reverseWindingOrder", TypeBool, Offset(mOptions.reverseWindingOrder, TSShapeConstructor),
+      "This step adjusts the output face winding order to be clockwise. The default assimp face winding order is counter clockwise." );
+   addField( "invertNormals", TypeBool, Offset(mOptions.invertNormals, TSShapeConstructor),
+      "Reverse the normal vector direction for all normals." );
+   addField( "removeRedundantMats", TypeBool, Offset(mOptions.removeRedundantMats, TSShapeConstructor),
+      "Removes redundant materials." );
+   addField( "animTiming", TYPEID< ColladaUtils::ImportOptions::eAnimTimingType >(), Offset(mOptions.animTiming, TSShapeConstructor),
+      "How to import timing data as frames, seconds or milliseconds." );
+   addField("animFPS", TypeS32, Offset(mOptions.animFPS, TSShapeConstructor),
+      "FPS value to use if timing is set in frames and the animations does not have an fps set.");
    endGroup( "Collada" );
 
    addGroup( "Sequences" );

+ 2 - 0
Engine/source/ts/tsShapeConstruct.h

@@ -328,9 +328,11 @@ public:
 
 typedef domUpAxisType TSShapeConstructorUpAxis;
 typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType;
+typedef ColladaUtils::ImportOptions::eAnimTimingType TSShapeConstructorAnimType;
 
 DefineEnumType( TSShapeConstructorUpAxis );
 DefineEnumType(TSShapeConstructorLodType);
+DefineEnumType(TSShapeConstructorAnimType);
 
 class TSShapeConstructorMethodActionCallback
 {

+ 29 - 0
Templates/BaseGame/game/tools/glb.sis

@@ -0,0 +1,29 @@
+SISV1
+lodType	SingleSize
+singleDetailSize	2
+materialPrefix	
+alwaysImport	
+neverImport	
+alwaysImportMesh	
+neverImportMesh	
+upAxis	Y_AXIS
+scale	1
+animTiming	Milliseconds
+animFPS	30
+overrideUpAxis	1
+overrideScale	0
+ignoreNodeScale	0
+adjustCenter	0
+adjustFloor	0
+forceUpdateMaterials	0
+convertLeftHanded	0
+calcTangentSpace	0
+removeRedundantMats	1
+genUVCoords	0
+transformUVCoords	0
+flipUVCoords	1
+findInstances	0
+limitBoneWeights	0
+joinIdenticalVerts	1
+reverseWindingOrder	1
+invertNormals	0

+ 29 - 0
Templates/BaseGame/game/tools/gltf.sis

@@ -0,0 +1,29 @@
+SISV1
+lodType	SingleSize
+singleDetailSize	2
+materialPrefix	
+alwaysImport	
+neverImport	
+alwaysImportMesh	
+neverImportMesh	
+upAxis	Y_AXIS
+scale	1
+animTiming	Milliseconds
+animFPS	30
+overrideUpAxis	1
+overrideScale	0
+ignoreNodeScale	0
+adjustCenter	0
+adjustFloor	0
+forceUpdateMaterials	0
+convertLeftHanded	0
+calcTangentSpace	0
+removeRedundantMats	1
+genUVCoords	0
+transformUVCoords	0
+flipUVCoords	1
+findInstances	0
+limitBoneWeights	0
+joinIdenticalVerts	1
+reverseWindingOrder	1
+invertNormals	0

+ 540 - 0
Templates/BaseGame/game/tools/gui/assimpImport.ed.cs

@@ -0,0 +1,540 @@
+//------------------------------------------------------------------------------
+// Fields that will be saved to settings
+$Assimp::textFields = "lodType" TAB "singleDetailSize" TAB "materialPrefix" TAB
+   "alwaysImport" TAB "neverImport" TAB "alwaysImportMesh" TAB "neverImportMesh"
+   TAB "upAxis" TAB "scale" TAB "animTiming" TAB "animFPS";
+
+$Assimp::checkboxFields = "overrideUpAxis" TAB "overrideScale" TAB "ignoreNodeScale"
+   TAB "adjustCenter" TAB "adjustFloor" TAB "forceUpdateMaterials" TAB "convertLeftHanded"
+   TAB "calcTangentSpace" TAB "removeRedundantMats" TAB "genUVCoords" TAB
+   "transformUVCoords" TAB "flipUVCoords" TAB "findInstances" TAB "limitBoneWeights"
+   TAB "joinIdenticalVerts" TAB "reverseWindingOrder" TAB "invertNormals";
+//------------------------------------------------------------------------------
+
+function ShapeImportTreeView::refresh(%this, %what)
+{
+   %shapeRoot = %this.getFirstRootItem();
+   %materialsRoot = %this.getNextSibling(%shapeRoot);
+   %animRoot = %this.getNextSibling(%materialsRoot);
+
+   // Refresh nodes
+   if ((%what $= "all") || (%what $= "nodes"))
+   {
+      // Indicate whether nodes will be ignored on import
+      %this._alwaysImport = strreplace(AssimpImportDlg-->alwaysImport.getText(), ";", "\t");
+      %this._neverImport = strreplace(AssimpImportDlg-->neverImport.getText(), ";", "\t");
+      %this._alwaysImportMesh = strreplace(AssimpImportDlg-->alwaysImportMesh.getText(), ";", "\t");
+      %this._neverImportMesh = strreplace(AssimpImportDlg-->neverImportMesh.getText(), ";", "\t");
+      %this.refreshNode(%this.getChild(%shapeRoot));
+   }
+
+   // Refresh materials
+   if ((%what $= "all") || (%what $= "materials"))
+   {
+      %matPrefix = AssimpImportDlg-->materialPrefix.getText();
+      %id = %this.getChild(%materialsRoot);
+      while (%id > 0)
+      {
+         %baseName = %this.getItemValue(%id);
+         %name = %matPrefix @ %baseName;
+
+         // Indicate whether material name is already mapped
+         %this.editItem(%id, %name, %baseName);
+         %mapped = getMaterialMapping(%name);
+         if (%mapped $= "")
+         {
+            %this.setItemTooltip(%id, "A new material will be mapped to this name");
+            %this.setItemImages(%id, %this._imageMaterial, %this._imageMaterial);
+         }
+         else
+         {
+            %this.setItemTooltip(%id, %mapped SPC "is already mapped to this material name");
+            %this.setItemImages(%id, %this._imageExMaterial, %this._imageExMaterial);
+         }
+
+         %id = %this.getNextSibling(%id);
+      }
+   }
+
+   // Refresh animations
+   if ((%what $= "all") || (%what $= "animations"))
+   {
+      %id = %this.getChild(%animRoot);
+      while (%id > 0)
+      {
+         %this.setItemImages(%id, %this._imageAnim, %this._imageAnim);
+         %id = %this.getNextSibling(%id);
+      }
+   }
+}
+
+function ShapeImportTreeView::refreshNode(%this, %id)
+{
+   while (%id > 0)
+   {
+      switch$ (%this.getItemValue(%id))
+      {
+         case "mesh":
+            // Check if this mesh will be ignored on import
+            if (strIsMatchMultipleExpr(%this._alwaysImportMesh, %this.getItemText(%id)) ||
+               !strIsMatchMultipleExpr(%this._neverImportMesh, %this.getItemText(%id)) )
+            {
+               %this.setItemTooltip(%id, "");
+               %this.setItemImages(%id, %this._imageMesh, %this._imageMesh);
+            }
+            else
+            {
+               %this.setItemTooltip(%id, "This mesh will be ignored on import");
+               %this.setItemImages(%id, %this._imageExNode, %this._imageExNode);
+            }
+
+         case "light":
+            %this.setItemImages(%id, %this._imageLight, %this._imageLight);
+
+         case "node":
+            // Check if this node will be ignored on import
+            if (strIsMatchMultipleExpr(%this._alwaysImport, %this.getItemText(%id)) ||
+               !strIsMatchMultipleExpr(%this._neverImport, %this.getItemText(%id)) )
+            {
+               %this.setItemTooltip(%id, "");
+               %this.setItemImages(%id, %this._imageNode, %this._imageNode);
+            }
+            else
+            {
+               %this.setItemTooltip(%id, "This node will be ignored on import");
+               %this.setItemImages(%id, %this._imageExNode, %this._imageExNode);
+            }
+      }
+
+      // recurse through children and siblings
+      %this.refreshNode(%this.getChild(%id));
+      %id = %this.getNextSibling(%id);
+   }
+}
+
+function ShapeImportTreeView::onDefineIcons(%this)
+{
+   // Set the tree view icon indices and texture paths
+   %this._imageNone = 0;
+   %this._imageNode = 1;
+   %this._imageMesh = 2;
+   %this._imageMaterial = 3;
+   %this._imageLight = 4;
+   %this._imageAnimation = 5;
+   %this._imageExNode = 6;
+   %this._imageExMaterial = 7;
+
+   %icons = ":" @                                                    // no icon
+            "tools/gui/images/ColladaImport/iconNode:" @             // normal node
+            "tools/gui/images/ColladaImport/iconMesh:" @             // mesh
+            "tools/gui/images/ColladaImport/iconMaterial:" @         // new material
+            "tools/gui/images/ColladaImport/iconLight:" @            // light
+            "tools/gui/images/ColladaImport/iconAnimation:" @        // sequence
+            "tools/gui/images/ColladaImport/iconIgnoreNode:" @       // ignored node
+            "tools/gui/images/ColladaImport/iconExistingMaterial";   // existing material
+
+   %this.buildIconTable( %icons );
+}
+
+function AssimpImportDlg::updateOverrideUpAxis(%this, %override)
+{
+   %this-->overrideUpAxis.setStateOn(%override);
+   %this-->upAxis.setActive(%override);
+   if (!%override)
+      %this-->upAxis.setText(ShapeImportTreeView._upAxis);
+}
+
+function AssimpImportDlg::updateOverrideScale(%this, %override)
+{
+   %this-->overrideScale.setStateOn(%override);
+   %this-->scale.setActive(%override);
+   if (!%override)
+      %this-->scale.setText(ShapeImportTreeView._unit);
+}
+
+function AssimpImportDlg::initFromConstructor(%this)
+{
+   if (%this.constructor.upAxis !$= "DEFAULT")
+   {
+      %this-->upAxis.setText(%this.constructor.upAxis);
+      %this.updateOverrideUpAxis(true);
+   }
+   else
+      %this.updateOverrideUpAxis(false);
+
+   if (%this.constructor.unit > 0)
+   {
+      %this-->scale.setText(%this.constructor.unit);
+      %this.updateOverrideScale(true);
+   }
+   else
+      %this.updateOverrideScale(false);
+
+   %this-->lodType.setText(%this.constructor.lodType);
+   %this-->singleDetailSize.setText(%this.constructor.singleDetailSize);
+   %this-->materialPrefix.setText(%this.constructor.matNamePrefix);
+   %this-->alwaysImport.setText(strreplace(%this.constructor.alwaysImport, "\t", ";"));
+   %this-->neverImport.setText(strreplace(%this.constructor.neverImport, "\t", ";"));
+   %this-->alwaysImportMesh.setText(strreplace(%this.constructor.alwaysImportMesh, "\t", ";"));
+   %this-->neverImportMesh.setText(strreplace(%this.constructor.neverImportMesh, "\t", ";"));
+   %this-->ignoreNodeScale.setStateOn(%this.constructor.ignoreNodeScale);
+   %this-->adjustCenter.setStateOn(%this.constructor.adjustCenter);
+   %this-->adjustFloor.setStateOn(%this.constructor.adjustFloor);
+   %this-->forceUpdateMaterials.setStateOn(%this.constructor.forceUpdateMaterials);
+
+   %this-->animTiming.setText(%this.constructor.animTiming);
+   %this-->animFPS.setText(%this.constructor.animFPS);
+
+   %this-->convertLeftHanded.setStateOn(%this.constructor.convertLeftHanded);
+   %this-->calcTangentSpace.setStateOn(%this.constructor.calcTangentSpace);
+   %this-->genUVCoords.setStateOn(%this.constructor.genUVCoords);
+   %this-->transformUVCoords.setStateOn(%this.constructor.transformUVCoords);
+   %this-->flipUVCoords.setStateOn(%this.constructor.flipUVCoords);
+   %this-->findInstances.setStateOn(%this.constructor.findInstances);
+   %this-->limitBoneWeights.setStateOn(%this.constructor.limitBoneWeights);
+   %this-->joinIdenticalVerts.setStateOn(%this.constructor.joinIdenticalVerts);
+   %this-->reverseWindingOrder.setStateOn(%this.constructor.reverseWindingOrder);
+   %this-->invertNormals.setStateOn(%this.constructor.invertNormals);
+   %this-->removeRedundantMats.setStateOn(%this.constructor.removeRedundantMats);
+}
+
+function AssimpImportDlg::initFromDefaults(%this)
+{
+   %this.updateOverrideUpAxis(false);
+   %this.updateOverrideScale(false);
+
+   %this-->lodType.setText("TrailingNumber");
+   %this-->singleDetailSize.setText("2");
+   %this-->materialPrefix.setText("");
+   %this-->alwaysImport.setText("");
+   %this-->neverImport.setText("");
+   %this-->alwaysImportMesh.setText("");
+   %this-->neverImportMesh.setText("");
+   %this-->ignoreNodeScale.setStateOn(0);
+   %this-->adjustCenter.setStateOn(0);
+   %this-->adjustFloor.setStateOn(0);
+   %this-->forceUpdateMaterials.setStateOn(0);
+
+   %this-->animTiming.setText("Seconds");
+   %this-->animFPS.setText("30");
+
+   %this-->convertLeftHanded.setStateOn(0);
+   %this-->calcTangentSpace.setStateOn(0);
+   %this-->genUVCoords.setStateOn(0);
+   %this-->transformUVCoords.setStateOn(0);
+   %this-->flipUVCoords.setStateOn(1);
+   %this-->findInstances.setStateOn(0);
+   %this-->limitBoneWeights.setStateOn(0);
+   %this-->joinIdenticalVerts.setStateOn(1);
+   %this-->reverseWindingOrder.setStateOn(1);
+   %this-->invertNormals.setStateOn(0);
+   %this-->removeRedundantMats.setStateOn(1);
+}
+
+function AssimpImportDlg::saveToConstructor(%this)
+{
+   // Store values from GUI
+   if (%this-->overrideUpAxis.getValue())
+      %this.constructor.upAxis = %this-->upAxis.getText();
+   else
+      %this.constructor.upAxis = "DEFAULT";
+
+   if (%this-->overrideScale.getValue())
+      %this.constructor.unit = %this-->scale.getText();
+   else
+      %this.constructor.unit = -1;
+
+   %this.constructor.lodType = %this-->lodType.getText();
+   %this.constructor.singleDetailSize = %this-->singleDetailSize.getText();
+   %this.constructor.matNamePrefix = %this-->materialPrefix.getText();
+   %this.constructor.alwaysImport = strreplace(%this-->alwaysImport.getText(), ";", "\t");
+   %this.constructor.neverImport = strreplace(%this-->neverImport.getText(), ";", "\t");
+   %this.constructor.alwaysImportMesh = strreplace(%this-->alwaysImportMesh.getText(), ";", "\t");
+   %this.constructor.neverImportMesh = strreplace(%this-->neverImportMesh.getText(), ";", "\t");
+   %this.constructor.ignoreNodeScale = %this-->ignoreNodeScale.getValue();
+   %this.constructor.adjustCenter = %this-->adjustCenter.getValue();
+   %this.constructor.adjustFloor = %this-->adjustFloor.getValue();
+   %this.constructor.forceUpdateMaterials = %this-->forceUpdateMaterials.getValue();
+
+   %this.constructor.animTiming = %this-->animTiming.getText();
+   %this.constructor.animFPS = %this-->animFPS.getText();
+
+   %this.constructor.convertLeftHanded = %this-->convertLeftHanded.getValue();
+   %this.constructor.calcTangentSpace = %this-->calcTangentSpace.getValue();
+   %this.constructor.genUVCoords = %this-->genUVCoords.getValue();
+   %this.constructor.transformUVCoords = %this-->transformUVCoords.getValue();
+   %this.constructor.flipUVCoords = %this-->flipUVCoords.getValue();
+   %this.constructor.findInstances = %this-->findInstances.getValue();
+   %this.constructor.limitBoneWeights = %this-->limitBoneWeights.getValue();
+   %this.constructor.joinIdenticalVerts = %this-->joinIdenticalVerts.getValue();
+   %this.constructor.reverseWindingOrder = %this-->reverseWindingOrder.getValue();
+   %this.constructor.invertNormals = %this-->invertNormals.getValue();
+   %this.constructor.removeRedundantMats = %this-->removeRedundantMats.getValue();
+}
+
+function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
+{
+   %this.settingVersion = 1.0;
+   %this.path = %shapePath;
+   %this.cmd = %cmd;
+
+   // Check for an existing TSShapeConstructor object. Need to exec the script
+   // manually as the resource may not have been loaded yet
+   %csPath = filePath(%this.path) @ "/" @ fileBase(%this.path) @ ".cs";
+   if (isFile(%csPath))
+      exec(%csPath);
+
+   %this.constructor = ShapeEditor.findConstructor(%this.path);
+
+   // Only show the import dialog if required. Note that 'GetShapeInfo' will
+   // fail if the source file is missing, or a cached.dts is available.
+   $assimp::forceLoad = EditorSettings.value("forceLoadDAE");
+   if (!GetShapeInfo(%shapePath, "ShapeImportTreeView"))
+   {
+      eval(%cmd);
+      $assimp::forceLoad = false;
+      return;
+   }
+   $assimp::forceLoad = false;
+
+   // Initialise GUI
+   ShapeImportTreeView.onDefineIcons();
+
+   // Window Title
+   %this-->window.text = "Shape Import:" SPC %this.path;
+
+   // Fill Popups
+   %this-->upAxis.clear();
+   %this-->upAxis.add("X_AXIS", 1);
+   %this-->upAxis.add("Y_AXIS", 2);
+   %this-->upAxis.add("Z_AXIS", 3);
+
+   %this-->lodType.clear();
+   %this-->lodType.add("DetectDTS", 1);
+   %this-->lodType.add("SingleSize", 2);
+   %this-->lodType.add("TrailingNumber", 3);
+
+   %this-->animTiming.clear();
+   %this-->animTiming.add("Frames", 0);
+   %this-->animTiming.add("Seconds", 1);
+   %this-->animTiming.add("Milliseconds", 1000);
+
+   // Set model details
+   %this-->nodes.setText(ShapeImportTreeView._nodeCount);
+   %this-->meshes.setText(ShapeImportTreeView._meshCount);
+   %this-->lights.setText(ShapeImportTreeView._lightCount);
+   %this-->materials.setText(ShapeImportTreeView._materialCount);
+   %this-->polygons.setText(ShapeImportTreeView._polygonCount);
+   %this-->animations.setText(ShapeImportTreeView._animCount);
+   %this-->textures.setText(ShapeImportTreeView._textureCount);
+   %this-->vertices.setText(ShapeImportTreeView._vertCount);
+   %metaText = (ShapeImportTreeView._metaTagCount == 0) ? "No Records" : ShapeImportTreeView._metaTagCount;
+   %this-->metadata.setText(%metaText);
+
+   if (%this.constructor > 0)
+      %this.initFromConstructor();
+   else
+   {
+      %this.initFromDefaults();
+
+      // If there's a default settings file for the file type, load it
+      %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+      %settingsFile = %defaultSISPath @ "/" @ getSubStr(fileExt(%shapePath),1) @ ".sis";
+      if (isFile(%settingsFile))
+         %this.loadSettingsFrom(%settingsFile);
+   }
+
+   Canvas.pushDialog(%this);
+
+   ShapeImportTreeView.refresh("all");
+}
+
+function AssimpImportDlg::onLoadSettings(%this)
+{
+   %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+   %dlg = new OpenFileDialog()
+   {
+      Filters        = "Shape Import Settings (*.sis)|*.sis|";
+      DefaultPath    = %defaultSISPath;
+      DefaultFile    = "";
+      ChangePath     = false;
+      MustExist      = true;
+   };
+            
+   %ret = %dlg.Execute();
+   if ( %ret )
+      %fileName = %dlg.FileName;
+      
+   %dlg.delete();
+   if ( !%ret )
+      return; 
+
+   %this.loadSettingsFrom(%fileName);
+}
+
+function AssimpImportDlg::onSaveSettings(%this)
+{
+   %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+   %dlg = new SaveFileDialog()
+   {
+      Filters        = "Shape Import Settings (*.sis)|*.sis|";
+      DefaultPath    = %defaultSISPath;
+      DefaultFile    = "";
+      ChangePath     = false;
+      MustExist      = true;
+   };
+         
+   %ret = %dlg.Execute();
+   if ( %ret )
+   {
+      %saveFile = %dlg.FileName;
+      if(fileExt( %saveFile ) !$= ".sis")
+         %saveFile = %saveFile @ ".sis";
+   }
+   
+   %dlg.delete();
+   
+   if (!%ret)
+      return;
+
+   %this.saveSettingsTo(%saveFile);
+}
+
+function AssimpImportDlg::onCancel(%this)
+{
+   Canvas.popDialog(%this);
+   ShapeImportTreeView.clear();
+}
+
+function AssimpImportDlg::onOK(%this)
+{
+   Canvas.popDialog(%this);
+   ShapeImportTreeView.clear();
+
+   if (%this.needsSave())
+   {
+      if (!isObject(%this.constructor))
+      {  // Create a new TSShapeConstructor object
+         %this.constructor = ShapeEditor.createConstructor(%this.path);
+      }
+   }
+   
+   if (%this.constructor > 0)
+   {
+      %this.saveToConstructor();
+      // Save new settings to file
+      ShapeEditor.saveConstructor(%this.constructor);
+   }
+
+   // Load the shape (always from the DAE)
+   $assimp::forceLoad = true;
+   eval(%this.cmd);
+   $assimp::forceLoad = false;
+}
+
+function AssimpImportDlg::needsSave(%this)
+{
+   if ((%this-->overrideUpAxis.getValue() != 0)      ||
+      (%this-->overrideScale.getValue() != 0)        ||
+      (%this-->lodType.getText() !$= "DetectDTS")    ||
+      (%this-->singleDetailSize.getText() !$= "2")   ||
+      (%this-->materialPrefix.getText() !$= "")      ||
+      (%this-->alwaysImport.getText() !$= "")        ||
+      (%this-->neverImport.getText() !$= "")         ||
+      (%this-->alwaysImportMesh.getText() !$= "")    ||
+      (%this-->neverImportMesh.getText() !$= "")     ||
+      (%this-->ignoreNodeScale.getValue() != 0)      ||
+      (%this-->adjustCenter.getValue() != 0)         ||
+      (%this-->adjustFloor.getValue() != 0)          ||
+      (%this-->forceUpdateMaterials.getValue() != 0))
+      return true;
+
+   if ((%this-->animTiming.getText() !$= "Seconds")  ||
+      (%this-->animFPS.getText() !$= "30")           ||
+      (%this-->convertLeftHanded.getValue() != 0)    ||
+      (%this-->calcTangentSpace.getValue() != 0)     ||
+      (%this-->genUVCoords.getValue() != 0)          ||
+      (%this-->transformUVCoords.getValue() != 0)    ||
+      (%this-->flipUVCoords.getValue() != 1)         ||
+      (%this-->findInstances.getValue() != 0)        ||
+      (%this-->limitBoneWeights.getValue() != 0)     ||
+      (%this-->joinIdenticalVerts.getValue() != 1)   ||
+      (%this-->reverseWindingOrder.getValue() != 1)  ||
+      (%this-->invertNormals.getValue() != 0)        ||
+      (%this-->removeRedundantMats.getValue() != 1))
+      return true;
+
+   return false;
+}
+
+function AssimpImportDlg::loadSettingsFrom(%this, %filename)
+{
+   %fileObj = new FileObject();
+   if (!%fileObj.OpenForRead(%fileName))
+   {
+      error("Error opening file to write settings (" @ %fileName @ ").");
+      %fileObj.delete();
+      return;
+   }
+
+   %line = %fileObj.ReadLine();
+   if (getSubStr(%line, 0, 4) !$= "SISV")
+      return; // ? not sis file
+
+   while( !%fileObj.isEOF() )
+   {
+      %line = %fileObj.ReadLine();
+      %key = getField(%line, 0);
+      %data = getFields(%line, 1);
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %name = %ctrl.getClassName();
+         if (%ctrl.getClassName() $= "GuiCheckBoxCtrl")
+            %ctrl.setStateOn(%data);
+         else
+            %ctrl.setText(%data);
+      }
+   }
+
+   %this.updateOverrideUpAxis(%this-->overrideUpAxis.getValue());
+   %this.updateOverrideScale(%this-->overrideScale.getValue());
+
+   %fileObj.close();
+   %fileObj.delete();
+}
+
+function AssimpImportDlg::saveSettingsTo(%this, %filename)
+{
+   %fileObj = new FileObject();
+   if (!%fileObj.openForWrite(%fileName))
+   {
+      error("Error opening file to write settings (" @ %fileName @ ").");
+      %fileObj.delete();
+      return;
+   }
+   %fileObj.writeLine("SISV" @ %this.settingVersion);
+
+   foreach$ (%key in $Assimp::textFields)
+   {
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %data = %ctrl.getText();
+         %fileObj.writeLine(%key TAB %data);
+      }
+   }
+
+   foreach$ (%key in $Assimp::checkboxFields)
+   {
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %data = %ctrl.getValue();
+         %fileObj.writeLine(%key TAB %data);
+      }
+   }
+
+   %fileObj.close();
+   %fileObj.delete();
+}

File diff ditekan karena terlalu besar
+ 1345 - 584
Templates/BaseGame/game/tools/gui/assimpImport.ed.gui


+ 2 - 2
Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui

@@ -489,14 +489,14 @@
          Profile = "ToolsGuiCheckBoxProfile";
          HorizSizing = "left";
          VertSizing = "bottom";
-         Position = "135 27";
+         Position = "133 27";
          Extent = "72 13";
          Variable = "EWorldEditor.forceLoadDAE";
          Command = "EWorldEditor.forceLoadDAE = $ThisControl.getValue(); EditorSettings.setValue(\"forceLoadDAE\", EWorldEditor.forceLoadDAE);";
          tooltipprofile = "ToolsGuiToolTipProfile";
          ToolTip = "Forces loading of DAE files (ignores cached.dts if present)";
          hovertime = "1000";
-         text = " Force DAE";
+         text = " Source Art";
       };
 
    };

+ 3 - 0
Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditor.ed.cs

@@ -227,6 +227,9 @@ function ShapeEditor::saveChanges( %this )
 
 function ShapeEditor::findConstructor( %this, %path )
 {
+   if (!isObject(TSShapeConstructorGroup))
+      return -1;
+
    %count = TSShapeConstructorGroup.getCount();
    for ( %i = 0; %i < %count; %i++ )
    {

+ 29 - 0
Templates/Full/game/tools/glb.sis

@@ -0,0 +1,29 @@
+SISV1
+lodType	SingleSize
+singleDetailSize	2
+materialPrefix	
+alwaysImport	
+neverImport	
+alwaysImportMesh	
+neverImportMesh	
+upAxis	Y_AXIS
+scale	1
+animTiming	Milliseconds
+animFPS	30
+overrideUpAxis	1
+overrideScale	0
+ignoreNodeScale	0
+adjustCenter	0
+adjustFloor	0
+forceUpdateMaterials	0
+convertLeftHanded	0
+calcTangentSpace	0
+removeRedundantMats	1
+genUVCoords	0
+transformUVCoords	0
+flipUVCoords	1
+findInstances	0
+limitBoneWeights	0
+joinIdenticalVerts	1
+reverseWindingOrder	1
+invertNormals	0

+ 29 - 0
Templates/Full/game/tools/gltf.sis

@@ -0,0 +1,29 @@
+SISV1
+lodType	SingleSize
+singleDetailSize	2
+materialPrefix	
+alwaysImport	
+neverImport	
+alwaysImportMesh	
+neverImportMesh	
+upAxis	Y_AXIS
+scale	1
+animTiming	Milliseconds
+animFPS	30
+overrideUpAxis	1
+overrideScale	0
+ignoreNodeScale	0
+adjustCenter	0
+adjustFloor	0
+forceUpdateMaterials	0
+convertLeftHanded	0
+calcTangentSpace	0
+removeRedundantMats	1
+genUVCoords	0
+transformUVCoords	0
+flipUVCoords	1
+findInstances	0
+limitBoneWeights	0
+joinIdenticalVerts	1
+reverseWindingOrder	1
+invertNormals	0

+ 540 - 0
Templates/Full/game/tools/gui/assimpImport.ed.cs

@@ -0,0 +1,540 @@
+//------------------------------------------------------------------------------
+// Fields that will be saved to settings
+$Assimp::textFields = "lodType" TAB "singleDetailSize" TAB "materialPrefix" TAB
+   "alwaysImport" TAB "neverImport" TAB "alwaysImportMesh" TAB "neverImportMesh"
+   TAB "upAxis" TAB "scale" TAB "animTiming" TAB "animFPS";
+
+$Assimp::checkboxFields = "overrideUpAxis" TAB "overrideScale" TAB "ignoreNodeScale"
+   TAB "adjustCenter" TAB "adjustFloor" TAB "forceUpdateMaterials" TAB "convertLeftHanded"
+   TAB "calcTangentSpace" TAB "removeRedundantMats" TAB "genUVCoords" TAB
+   "transformUVCoords" TAB "flipUVCoords" TAB "findInstances" TAB "limitBoneWeights"
+   TAB "joinIdenticalVerts" TAB "reverseWindingOrder" TAB "invertNormals";
+//------------------------------------------------------------------------------
+
+function ShapeImportTreeView::refresh(%this, %what)
+{
+   %shapeRoot = %this.getFirstRootItem();
+   %materialsRoot = %this.getNextSibling(%shapeRoot);
+   %animRoot = %this.getNextSibling(%materialsRoot);
+
+   // Refresh nodes
+   if ((%what $= "all") || (%what $= "nodes"))
+   {
+      // Indicate whether nodes will be ignored on import
+      %this._alwaysImport = strreplace(AssimpImportDlg-->alwaysImport.getText(), ";", "\t");
+      %this._neverImport = strreplace(AssimpImportDlg-->neverImport.getText(), ";", "\t");
+      %this._alwaysImportMesh = strreplace(AssimpImportDlg-->alwaysImportMesh.getText(), ";", "\t");
+      %this._neverImportMesh = strreplace(AssimpImportDlg-->neverImportMesh.getText(), ";", "\t");
+      %this.refreshNode(%this.getChild(%shapeRoot));
+   }
+
+   // Refresh materials
+   if ((%what $= "all") || (%what $= "materials"))
+   {
+      %matPrefix = AssimpImportDlg-->materialPrefix.getText();
+      %id = %this.getChild(%materialsRoot);
+      while (%id > 0)
+      {
+         %baseName = %this.getItemValue(%id);
+         %name = %matPrefix @ %baseName;
+
+         // Indicate whether material name is already mapped
+         %this.editItem(%id, %name, %baseName);
+         %mapped = getMaterialMapping(%name);
+         if (%mapped $= "")
+         {
+            %this.setItemTooltip(%id, "A new material will be mapped to this name");
+            %this.setItemImages(%id, %this._imageMaterial, %this._imageMaterial);
+         }
+         else
+         {
+            %this.setItemTooltip(%id, %mapped SPC "is already mapped to this material name");
+            %this.setItemImages(%id, %this._imageExMaterial, %this._imageExMaterial);
+         }
+
+         %id = %this.getNextSibling(%id);
+      }
+   }
+
+   // Refresh animations
+   if ((%what $= "all") || (%what $= "animations"))
+   {
+      %id = %this.getChild(%animRoot);
+      while (%id > 0)
+      {
+         %this.setItemImages(%id, %this._imageAnim, %this._imageAnim);
+         %id = %this.getNextSibling(%id);
+      }
+   }
+}
+
+function ShapeImportTreeView::refreshNode(%this, %id)
+{
+   while (%id > 0)
+   {
+      switch$ (%this.getItemValue(%id))
+      {
+         case "mesh":
+            // Check if this mesh will be ignored on import
+            if (strIsMatchMultipleExpr(%this._alwaysImportMesh, %this.getItemText(%id)) ||
+               !strIsMatchMultipleExpr(%this._neverImportMesh, %this.getItemText(%id)) )
+            {
+               %this.setItemTooltip(%id, "");
+               %this.setItemImages(%id, %this._imageMesh, %this._imageMesh);
+            }
+            else
+            {
+               %this.setItemTooltip(%id, "This mesh will be ignored on import");
+               %this.setItemImages(%id, %this._imageExNode, %this._imageExNode);
+            }
+
+         case "light":
+            %this.setItemImages(%id, %this._imageLight, %this._imageLight);
+
+         case "node":
+            // Check if this node will be ignored on import
+            if (strIsMatchMultipleExpr(%this._alwaysImport, %this.getItemText(%id)) ||
+               !strIsMatchMultipleExpr(%this._neverImport, %this.getItemText(%id)) )
+            {
+               %this.setItemTooltip(%id, "");
+               %this.setItemImages(%id, %this._imageNode, %this._imageNode);
+            }
+            else
+            {
+               %this.setItemTooltip(%id, "This node will be ignored on import");
+               %this.setItemImages(%id, %this._imageExNode, %this._imageExNode);
+            }
+      }
+
+      // recurse through children and siblings
+      %this.refreshNode(%this.getChild(%id));
+      %id = %this.getNextSibling(%id);
+   }
+}
+
+function ShapeImportTreeView::onDefineIcons(%this)
+{
+   // Set the tree view icon indices and texture paths
+   %this._imageNone = 0;
+   %this._imageNode = 1;
+   %this._imageMesh = 2;
+   %this._imageMaterial = 3;
+   %this._imageLight = 4;
+   %this._imageAnimation = 5;
+   %this._imageExNode = 6;
+   %this._imageExMaterial = 7;
+
+   %icons = ":" @                                                    // no icon
+            "tools/gui/images/ColladaImport/iconNode:" @             // normal node
+            "tools/gui/images/ColladaImport/iconMesh:" @             // mesh
+            "tools/gui/images/ColladaImport/iconMaterial:" @         // new material
+            "tools/gui/images/ColladaImport/iconLight:" @            // light
+            "tools/gui/images/ColladaImport/iconAnimation:" @        // sequence
+            "tools/gui/images/ColladaImport/iconIgnoreNode:" @       // ignored node
+            "tools/gui/images/ColladaImport/iconExistingMaterial";   // existing material
+
+   %this.buildIconTable( %icons );
+}
+
+function AssimpImportDlg::updateOverrideUpAxis(%this, %override)
+{
+   %this-->overrideUpAxis.setStateOn(%override);
+   %this-->upAxis.setActive(%override);
+   if (!%override)
+      %this-->upAxis.setText(ShapeImportTreeView._upAxis);
+}
+
+function AssimpImportDlg::updateOverrideScale(%this, %override)
+{
+   %this-->overrideScale.setStateOn(%override);
+   %this-->scale.setActive(%override);
+   if (!%override)
+      %this-->scale.setText(ShapeImportTreeView._unit);
+}
+
+function AssimpImportDlg::initFromConstructor(%this)
+{
+   if (%this.constructor.upAxis !$= "DEFAULT")
+   {
+      %this-->upAxis.setText(%this.constructor.upAxis);
+      %this.updateOverrideUpAxis(true);
+   }
+   else
+      %this.updateOverrideUpAxis(false);
+
+   if (%this.constructor.unit > 0)
+   {
+      %this-->scale.setText(%this.constructor.unit);
+      %this.updateOverrideScale(true);
+   }
+   else
+      %this.updateOverrideScale(false);
+
+   %this-->lodType.setText(%this.constructor.lodType);
+   %this-->singleDetailSize.setText(%this.constructor.singleDetailSize);
+   %this-->materialPrefix.setText(%this.constructor.matNamePrefix);
+   %this-->alwaysImport.setText(strreplace(%this.constructor.alwaysImport, "\t", ";"));
+   %this-->neverImport.setText(strreplace(%this.constructor.neverImport, "\t", ";"));
+   %this-->alwaysImportMesh.setText(strreplace(%this.constructor.alwaysImportMesh, "\t", ";"));
+   %this-->neverImportMesh.setText(strreplace(%this.constructor.neverImportMesh, "\t", ";"));
+   %this-->ignoreNodeScale.setStateOn(%this.constructor.ignoreNodeScale);
+   %this-->adjustCenter.setStateOn(%this.constructor.adjustCenter);
+   %this-->adjustFloor.setStateOn(%this.constructor.adjustFloor);
+   %this-->forceUpdateMaterials.setStateOn(%this.constructor.forceUpdateMaterials);
+
+   %this-->animTiming.setText(%this.constructor.animTiming);
+   %this-->animFPS.setText(%this.constructor.animFPS);
+
+   %this-->convertLeftHanded.setStateOn(%this.constructor.convertLeftHanded);
+   %this-->calcTangentSpace.setStateOn(%this.constructor.calcTangentSpace);
+   %this-->genUVCoords.setStateOn(%this.constructor.genUVCoords);
+   %this-->transformUVCoords.setStateOn(%this.constructor.transformUVCoords);
+   %this-->flipUVCoords.setStateOn(%this.constructor.flipUVCoords);
+   %this-->findInstances.setStateOn(%this.constructor.findInstances);
+   %this-->limitBoneWeights.setStateOn(%this.constructor.limitBoneWeights);
+   %this-->joinIdenticalVerts.setStateOn(%this.constructor.joinIdenticalVerts);
+   %this-->reverseWindingOrder.setStateOn(%this.constructor.reverseWindingOrder);
+   %this-->invertNormals.setStateOn(%this.constructor.invertNormals);
+   %this-->removeRedundantMats.setStateOn(%this.constructor.removeRedundantMats);
+}
+
+function AssimpImportDlg::initFromDefaults(%this)
+{
+   %this.updateOverrideUpAxis(false);
+   %this.updateOverrideScale(false);
+
+   %this-->lodType.setText("TrailingNumber");
+   %this-->singleDetailSize.setText("2");
+   %this-->materialPrefix.setText("");
+   %this-->alwaysImport.setText("");
+   %this-->neverImport.setText("");
+   %this-->alwaysImportMesh.setText("");
+   %this-->neverImportMesh.setText("");
+   %this-->ignoreNodeScale.setStateOn(0);
+   %this-->adjustCenter.setStateOn(0);
+   %this-->adjustFloor.setStateOn(0);
+   %this-->forceUpdateMaterials.setStateOn(0);
+
+   %this-->animTiming.setText("Seconds");
+   %this-->animFPS.setText("30");
+
+   %this-->convertLeftHanded.setStateOn(0);
+   %this-->calcTangentSpace.setStateOn(0);
+   %this-->genUVCoords.setStateOn(0);
+   %this-->transformUVCoords.setStateOn(0);
+   %this-->flipUVCoords.setStateOn(1);
+   %this-->findInstances.setStateOn(0);
+   %this-->limitBoneWeights.setStateOn(0);
+   %this-->joinIdenticalVerts.setStateOn(1);
+   %this-->reverseWindingOrder.setStateOn(1);
+   %this-->invertNormals.setStateOn(0);
+   %this-->removeRedundantMats.setStateOn(1);
+}
+
+function AssimpImportDlg::saveToConstructor(%this)
+{
+   // Store values from GUI
+   if (%this-->overrideUpAxis.getValue())
+      %this.constructor.upAxis = %this-->upAxis.getText();
+   else
+      %this.constructor.upAxis = "DEFAULT";
+
+   if (%this-->overrideScale.getValue())
+      %this.constructor.unit = %this-->scale.getText();
+   else
+      %this.constructor.unit = -1;
+
+   %this.constructor.lodType = %this-->lodType.getText();
+   %this.constructor.singleDetailSize = %this-->singleDetailSize.getText();
+   %this.constructor.matNamePrefix = %this-->materialPrefix.getText();
+   %this.constructor.alwaysImport = strreplace(%this-->alwaysImport.getText(), ";", "\t");
+   %this.constructor.neverImport = strreplace(%this-->neverImport.getText(), ";", "\t");
+   %this.constructor.alwaysImportMesh = strreplace(%this-->alwaysImportMesh.getText(), ";", "\t");
+   %this.constructor.neverImportMesh = strreplace(%this-->neverImportMesh.getText(), ";", "\t");
+   %this.constructor.ignoreNodeScale = %this-->ignoreNodeScale.getValue();
+   %this.constructor.adjustCenter = %this-->adjustCenter.getValue();
+   %this.constructor.adjustFloor = %this-->adjustFloor.getValue();
+   %this.constructor.forceUpdateMaterials = %this-->forceUpdateMaterials.getValue();
+
+   %this.constructor.animTiming = %this-->animTiming.getText();
+   %this.constructor.animFPS = %this-->animFPS.getText();
+
+   %this.constructor.convertLeftHanded = %this-->convertLeftHanded.getValue();
+   %this.constructor.calcTangentSpace = %this-->calcTangentSpace.getValue();
+   %this.constructor.genUVCoords = %this-->genUVCoords.getValue();
+   %this.constructor.transformUVCoords = %this-->transformUVCoords.getValue();
+   %this.constructor.flipUVCoords = %this-->flipUVCoords.getValue();
+   %this.constructor.findInstances = %this-->findInstances.getValue();
+   %this.constructor.limitBoneWeights = %this-->limitBoneWeights.getValue();
+   %this.constructor.joinIdenticalVerts = %this-->joinIdenticalVerts.getValue();
+   %this.constructor.reverseWindingOrder = %this-->reverseWindingOrder.getValue();
+   %this.constructor.invertNormals = %this-->invertNormals.getValue();
+   %this.constructor.removeRedundantMats = %this-->removeRedundantMats.getValue();
+}
+
+function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
+{
+   %this.settingVersion = 1.0;
+   %this.path = %shapePath;
+   %this.cmd = %cmd;
+
+   // Check for an existing TSShapeConstructor object. Need to exec the script
+   // manually as the resource may not have been loaded yet
+   %csPath = filePath(%this.path) @ "/" @ fileBase(%this.path) @ ".cs";
+   if (isFile(%csPath))
+      exec(%csPath);
+
+   %this.constructor = ShapeEditor.findConstructor(%this.path);
+
+   // Only show the import dialog if required. Note that 'GetShapeInfo' will
+   // fail if the source file is missing, or a cached.dts is available.
+   $assimp::forceLoad = EditorSettings.value("forceLoadDAE");
+   if (!GetShapeInfo(%shapePath, "ShapeImportTreeView"))
+   {
+      eval(%cmd);
+      $assimp::forceLoad = false;
+      return;
+   }
+   $assimp::forceLoad = false;
+
+   // Initialise GUI
+   ShapeImportTreeView.onDefineIcons();
+
+   // Window Title
+   %this-->window.text = "Shape Import:" SPC %this.path;
+
+   // Fill Popups
+   %this-->upAxis.clear();
+   %this-->upAxis.add("X_AXIS", 1);
+   %this-->upAxis.add("Y_AXIS", 2);
+   %this-->upAxis.add("Z_AXIS", 3);
+
+   %this-->lodType.clear();
+   %this-->lodType.add("DetectDTS", 1);
+   %this-->lodType.add("SingleSize", 2);
+   %this-->lodType.add("TrailingNumber", 3);
+
+   %this-->animTiming.clear();
+   %this-->animTiming.add("Frames", 0);
+   %this-->animTiming.add("Seconds", 1);
+   %this-->animTiming.add("Milliseconds", 1000);
+
+   // Set model details
+   %this-->nodes.setText(ShapeImportTreeView._nodeCount);
+   %this-->meshes.setText(ShapeImportTreeView._meshCount);
+   %this-->lights.setText(ShapeImportTreeView._lightCount);
+   %this-->materials.setText(ShapeImportTreeView._materialCount);
+   %this-->polygons.setText(ShapeImportTreeView._polygonCount);
+   %this-->animations.setText(ShapeImportTreeView._animCount);
+   %this-->textures.setText(ShapeImportTreeView._textureCount);
+   %this-->vertices.setText(ShapeImportTreeView._vertCount);
+   %metaText = (ShapeImportTreeView._metaTagCount == 0) ? "No Records" : ShapeImportTreeView._metaTagCount;
+   %this-->metadata.setText(%metaText);
+
+   if (%this.constructor > 0)
+      %this.initFromConstructor();
+   else
+   {
+      %this.initFromDefaults();
+
+      // If there's a default settings file for the file type, load it
+      %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+      %settingsFile = %defaultSISPath @ "/" @ getSubStr(fileExt(%shapePath),1) @ ".sis";
+      if (isFile(%settingsFile))
+         %this.loadSettingsFrom(%settingsFile);
+   }
+
+   Canvas.pushDialog(%this);
+
+   ShapeImportTreeView.refresh("all");
+}
+
+function AssimpImportDlg::onLoadSettings(%this)
+{
+   %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+   %dlg = new OpenFileDialog()
+   {
+      Filters        = "Shape Import Settings (*.sis)|*.sis|";
+      DefaultPath    = %defaultSISPath;
+      DefaultFile    = "";
+      ChangePath     = false;
+      MustExist      = true;
+   };
+            
+   %ret = %dlg.Execute();
+   if ( %ret )
+      %fileName = %dlg.FileName;
+      
+   %dlg.delete();
+   if ( !%ret )
+      return; 
+
+   %this.loadSettingsFrom(%fileName);
+}
+
+function AssimpImportDlg::onSaveSettings(%this)
+{
+   %defaultSISPath = EditorSettings.value("defaultSettingsPath", "Tools");
+   %dlg = new SaveFileDialog()
+   {
+      Filters        = "Shape Import Settings (*.sis)|*.sis|";
+      DefaultPath    = %defaultSISPath;
+      DefaultFile    = "";
+      ChangePath     = false;
+      MustExist      = true;
+   };
+         
+   %ret = %dlg.Execute();
+   if ( %ret )
+   {
+      %saveFile = %dlg.FileName;
+      if(fileExt( %saveFile ) !$= ".sis")
+         %saveFile = %saveFile @ ".sis";
+   }
+   
+   %dlg.delete();
+   
+   if (!%ret)
+      return;
+
+   %this.saveSettingsTo(%saveFile);
+}
+
+function AssimpImportDlg::onCancel(%this)
+{
+   Canvas.popDialog(%this);
+   ShapeImportTreeView.clear();
+}
+
+function AssimpImportDlg::onOK(%this)
+{
+   Canvas.popDialog(%this);
+   ShapeImportTreeView.clear();
+
+   if (%this.needsSave())
+   {
+      if (!isObject(%this.constructor))
+      {  // Create a new TSShapeConstructor object
+         %this.constructor = ShapeEditor.createConstructor(%this.path);
+      }
+   }
+   
+   if (%this.constructor > 0)
+   {
+      %this.saveToConstructor();
+      // Save new settings to file
+      ShapeEditor.saveConstructor(%this.constructor);
+   }
+
+   // Load the shape (always from the DAE)
+   $assimp::forceLoad = true;
+   eval(%this.cmd);
+   $assimp::forceLoad = false;
+}
+
+function AssimpImportDlg::needsSave(%this)
+{
+   if ((%this-->overrideUpAxis.getValue() != 0)      ||
+      (%this-->overrideScale.getValue() != 0)        ||
+      (%this-->lodType.getText() !$= "DetectDTS")    ||
+      (%this-->singleDetailSize.getText() !$= "2")   ||
+      (%this-->materialPrefix.getText() !$= "")      ||
+      (%this-->alwaysImport.getText() !$= "")        ||
+      (%this-->neverImport.getText() !$= "")         ||
+      (%this-->alwaysImportMesh.getText() !$= "")    ||
+      (%this-->neverImportMesh.getText() !$= "")     ||
+      (%this-->ignoreNodeScale.getValue() != 0)      ||
+      (%this-->adjustCenter.getValue() != 0)         ||
+      (%this-->adjustFloor.getValue() != 0)          ||
+      (%this-->forceUpdateMaterials.getValue() != 0))
+      return true;
+
+   if ((%this-->animTiming.getText() !$= "Seconds")  ||
+      (%this-->animFPS.getText() !$= "30")           ||
+      (%this-->convertLeftHanded.getValue() != 0)    ||
+      (%this-->calcTangentSpace.getValue() != 0)     ||
+      (%this-->genUVCoords.getValue() != 0)          ||
+      (%this-->transformUVCoords.getValue() != 0)    ||
+      (%this-->flipUVCoords.getValue() != 1)         ||
+      (%this-->findInstances.getValue() != 0)        ||
+      (%this-->limitBoneWeights.getValue() != 0)     ||
+      (%this-->joinIdenticalVerts.getValue() != 1)   ||
+      (%this-->reverseWindingOrder.getValue() != 1)  ||
+      (%this-->invertNormals.getValue() != 0)        ||
+      (%this-->removeRedundantMats.getValue() != 1))
+      return true;
+
+   return false;
+}
+
+function AssimpImportDlg::loadSettingsFrom(%this, %filename)
+{
+   %fileObj = new FileObject();
+   if (!%fileObj.OpenForRead(%fileName))
+   {
+      error("Error opening file to write settings (" @ %fileName @ ").");
+      %fileObj.delete();
+      return;
+   }
+
+   %line = %fileObj.ReadLine();
+   if (getSubStr(%line, 0, 4) !$= "SISV")
+      return; // ? not sis file
+
+   while( !%fileObj.isEOF() )
+   {
+      %line = %fileObj.ReadLine();
+      %key = getField(%line, 0);
+      %data = getFields(%line, 1);
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %name = %ctrl.getClassName();
+         if (%ctrl.getClassName() $= "GuiCheckBoxCtrl")
+            %ctrl.setStateOn(%data);
+         else
+            %ctrl.setText(%data);
+      }
+   }
+
+   %this.updateOverrideUpAxis(%this-->overrideUpAxis.getValue());
+   %this.updateOverrideScale(%this-->overrideScale.getValue());
+
+   %fileObj.close();
+   %fileObj.delete();
+}
+
+function AssimpImportDlg::saveSettingsTo(%this, %filename)
+{
+   %fileObj = new FileObject();
+   if (!%fileObj.openForWrite(%fileName))
+   {
+      error("Error opening file to write settings (" @ %fileName @ ").");
+      %fileObj.delete();
+      return;
+   }
+   %fileObj.writeLine("SISV" @ %this.settingVersion);
+
+   foreach$ (%key in $Assimp::textFields)
+   {
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %data = %ctrl.getText();
+         %fileObj.writeLine(%key TAB %data);
+      }
+   }
+
+   foreach$ (%key in $Assimp::checkboxFields)
+   {
+      %ctrl = %this.findObjectByInternalName(%key, true);
+      if (isObject(%ctrl))
+      {
+         %data = %ctrl.getValue();
+         %fileObj.writeLine(%key TAB %data);
+      }
+   }
+
+   %fileObj.close();
+   %fileObj.delete();
+}

File diff ditekan karena terlalu besar
+ 1345 - 584
Templates/Full/game/tools/gui/assimpImport.ed.gui


+ 2 - 2
Templates/Full/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui

@@ -489,14 +489,14 @@
          Profile = "ToolsGuiCheckBoxProfile";
          HorizSizing = "left";
          VertSizing = "bottom";
-         Position = "135 27";
+         Position = "133 27";
          Extent = "72 13";
          Variable = "EWorldEditor.forceLoadDAE";
          Command = "EWorldEditor.forceLoadDAE = $ThisControl.getValue(); EditorSettings.setValue(\"forceLoadDAE\", EWorldEditor.forceLoadDAE);";
          tooltipprofile = "ToolsGuiToolTipProfile";
          ToolTip = "Forces loading of DAE files (ignores cached.dts if present)";
          hovertime = "1000";
-         text = " Force DAE";
+         text = " Source Art";
       };
 
    };

+ 3 - 0
Templates/Full/game/tools/shapeEditor/scripts/shapeEditor.ed.cs

@@ -227,6 +227,9 @@ function ShapeEditor::saveChanges( %this )
 
 function ShapeEditor::findConstructor( %this, %path )
 {
+   if (!isObject(TSShapeConstructorGroup))
+      return -1;
+
    %count = TSShapeConstructorGroup.getCount();
    for ( %i = 0; %i < %count; %i++ )
    {

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini