Переглянути джерело

Implements the Asset Importer integrated into the engine, avoiding the need for calldowns into the tool suite to import assets

Areloch 5 роки тому
батько
коміт
fb31f9d1e5

+ 1912 - 0
Engine/source/T3D/assets/assetImporter.cpp

@@ -0,0 +1,1912 @@
+#include "assetImporter.h"
+#include "assetImporter_ScriptBinding.h"
+#include "core/strings/findMatch.h"
+#include "ImageAsset.h"
+#include "ShapeAsset.h"
+#include "SoundAsset.h"
+#include "MaterialAsset.h"
+#include "ShapeAnimationAsset.h"
+
+#include "ts/collada/colladaUtils.h"
+#include "ts/collada/colladaAppNode.h"
+#include "ts/collada/colladaShapeLoader.h"
+
+#include "ts/assimp/assimpShapeLoader.h"
+#include <ts\tsShapeConstruct.h>
+
+
+ConsoleDocClass(AssetImportConfig,
+   "@brief Defines properties for an AssetImprotConfig object.\n"
+   "@AssetImportConfig is a SimObject derived object intended to act as a container for all the necessary configuration data when running the Asset Importer.\n"
+   "@It dictates if and how any given asset type will be processed when running an import action. This is because the Asset Importer utilizes a lot of informed logic\n"
+   "@to try and automate as much of the import process as possible. In theory, you would run the import on a given file, and based on your config the importer will do\n"
+   "@everything from importing the designated file, as well as finding and importing any associated files such as images or materials, and prepping the objects at time\n"
+   "@of import to avoid as much manual post-processing as possible.\n\n"
+   "@ingroup Assets\n"
+);
+
+IMPLEMENT_CONOBJECT(AssetImportConfig);
+
+AssetImportConfig::AssetImportConfig() :
+   DuplicatAutoResolution("AutoRename"),
+   WarningsAsErrors(false),
+   PreventImportWithErrors(true),
+   AutomaticallyPromptMissingFiles(false),
+   ImportMesh(true),
+   DoUpAxisOverride(false),
+   UpAxisOverride("Z_AXIS"),
+   DoScaleOverride(false),
+   ScaleOverride(false),
+   IgnoreNodeScale(false),
+   AdjustCenter(false),
+   AdjustFloor(false),
+   CollapseSubmeshes(false),
+   LODType("TrailingNumber"),
+   ImportedNodes(""),
+   IgnoreNodes(""),
+   ImportMeshes(""),
+   IgnoreMeshes(""),
+   convertLeftHanded(false),
+   calcTangentSpace(false),
+   removeRedundantMats(false),
+   genUVCoords(false),
+   TransformUVs(false),
+   flipUVCoords(false),
+   findInstances(false),
+   limitBoneWeights(false),
+   JoinIdenticalVerts(false),
+   reverseWindingOrder(false),
+   invertNormals(false),
+   ImportMaterials(true),
+   CreatePBRConfig(true),
+   UseDiffuseSuffixOnOriginImage(false),
+   UseExistingMaterials(false),
+   IgnoreMaterials(""),
+   PopulateMaterialMaps(false),
+   ImportAnimations(true),
+   SeparateAnimations(false),
+   SeparateAnimationPrefix(""),
+   animTiming("FrameCount"),
+   animFPS(false),
+   GenerateCollisions(false),
+   GenCollisionType(""),
+   CollisionMeshPrefix(""),
+   GenerateLOSCollisions(false),
+   GenLOSCollisionType(""),
+   LOSCollisionMeshPrefix(""),
+   importImages(true),
+   ImageType("GUI"),
+   DiffuseTypeSuffixes("_ALBEDO,_DIFFUSE,_ALB,_DIF,_COLOR,_COL,_A,_C,-ALBEDO,-DIFFUSE,-ALB,-DIF,-COLOR,-COL,-A,-C"),
+   NormalTypeSuffixes("_NORMAL,_NORM,_N,-NORMAL,-NORM,-N"),
+   MetalnessTypeSuffixes("_METAL,_MET,_METALNESS,_METALLIC,_M,-METAL, -MET, -METALNESS, -METALLIC, -M"),
+   RoughnessTypeSuffixes("_ROUGH,_ROUGHNESS,_R,-ROUGH,-ROUGHNESS,-R"),
+   SmoothnessTypeSuffixes("_SMOOTH,_SMOOTHNESS,_S,-SMOOTH,-SMOOTHNESS,-S"),
+   AOTypeSuffixes("_AO,_AMBIENT,_AMBIENTOCCLUSION,-AO,-AMBIENT,-AMBIENTOCCLUSION"),
+   PBRTypeSuffixes("_COMP,_COMPOSITE,_PBR,-COMP,-COMPOSITE,-PBR"),
+   TextureFilteringMode("Bilinear"),
+   UseMips(true),
+   IsHDR(false),
+   Scaling(false),
+   Compresse(false),
+   GenerateMaterialOnImport(true),
+   importSounds(true),
+   VolumeAdjust(false),
+   PitchAdjust(false),
+   Compressed(false)
+{
+}
+
+AssetImportConfig::~AssetImportConfig()
+{
+
+}
+
+/// Engine.
+void AssetImportConfig::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void AssetImportConfig::loadImportConfig(Settings* configSettings, String configName)
+{
+   //General
+   DuplicatAutoResolution = configSettings->value(String(configName + "/General/DuplicatAutoResolution").c_str());
+   WarningsAsErrors = dAtob(configSettings->value(String(configName + "/General/WarningsAsErrors").c_str()));
+   PreventImportWithErrors = dAtob(configSettings->value(String(configName + "/General/PreventImportWithErrors").c_str()));
+   AutomaticallyPromptMissingFiles = dAtob(configSettings->value(String(configName + "/General/AutomaticallyPromptMissingFiles").c_str()));
+
+   //Meshes
+   ImportMesh = dAtob(configSettings->value(String(configName + "/Meshes/ImportMesh").c_str()));
+   DoUpAxisOverride = dAtob(configSettings->value(String(configName + "/Meshes/DoUpAxisOverride").c_str()));
+   UpAxisOverride = configSettings->value(String(configName + "/Meshes/UpAxisOverride").c_str());
+   DoScaleOverride = dAtob(configSettings->value(String(configName + "/Meshes/DoScaleOverride").c_str()));
+   ScaleOverride = dAtof(configSettings->value(String(configName + "/Meshes/ScaleOverride").c_str()));
+   IgnoreNodeScale = dAtob(configSettings->value(String(configName + "/Meshes/IgnoreNodeScale").c_str()));
+   AdjustCenter = dAtob(configSettings->value(String(configName + "/Meshes/AdjustCenter").c_str()));
+   AdjustFloor = dAtob(configSettings->value(String(configName + "/Meshes/AdjustFloor").c_str()));
+   CollapseSubmeshes = dAtob(configSettings->value(String(configName + "/Meshes/CollapseSubmeshes").c_str()));
+   LODType = configSettings->value(String(configName + "/Meshes/LODType").c_str());
+   ImportedNodes = configSettings->value(String(configName + "/Meshes/ImportedNodes").c_str());
+   IgnoreNodes = configSettings->value(String(configName + "/Meshes/IgnoreNodes").c_str());
+   ImportMeshes = configSettings->value(String(configName + "/Meshes/ImportMeshes").c_str());
+   IgnoreMeshes = configSettings->value(String(configName + "/Meshes/IgnoreMeshes").c_str());
+
+   //Assimp/Collada
+   convertLeftHanded = dAtob(configSettings->value(String(configName + "/Meshes/convertLeftHanded").c_str()));
+   calcTangentSpace = dAtob(configSettings->value(String(configName + "/Meshes/calcTangentSpace").c_str()));
+   removeRedundantMats = dAtob(configSettings->value(String(configName + "/Meshes/removeRedundantMats").c_str()));
+   genUVCoords = dAtob(configSettings->value(String(configName + "/Meshes/genUVCoords").c_str()));
+   TransformUVs = dAtob(configSettings->value(String(configName + "/Meshes/TransformUVs").c_str()));
+   flipUVCoords = dAtob(configSettings->value(String(configName + "/Meshes/flipUVCoords").c_str()));
+   findInstances = dAtob(configSettings->value(String(configName + "/Meshes/findInstances").c_str()));
+   limitBoneWeights = dAtob(configSettings->value(String(configName + "/Meshes/limitBoneWeights").c_str()));
+   JoinIdenticalVerts = dAtob(configSettings->value(String(configName + "/Meshes/JoinIdenticalVerts").c_str()));
+   reverseWindingOrder = dAtob(configSettings->value(String(configName + "/Meshes/reverseWindingOrder").c_str()));
+   invertNormals = dAtob(configSettings->value(String(configName + "/Meshes/invertNormals").c_str()));
+
+   //Materials
+   ImportMaterials = dAtob(configSettings->value(String(configName + "/Materials/ImportMaterials").c_str()));
+   CreatePBRConfig = dAtob(configSettings->value(String(configName + "/Materials/CreatePBRConfig").c_str()));
+   UseDiffuseSuffixOnOriginImage = dAtob(configSettings->value(String(configName + "/Materials/UseDiffuseSuffixOnOriginImage").c_str()));
+   UseExistingMaterials = dAtob(configSettings->value(String(configName + "/Materials/UseExistingMaterials").c_str()));
+   IgnoreMaterials = configSettings->value(String(configName + "/Materials/IgnoreMaterials").c_str());
+   PopulateMaterialMaps = dAtob(configSettings->value(String(configName + "/Materials/invertPopulateMaterialMapsNormals").c_str()));
+
+   //Animations
+   ImportAnimations = dAtob(configSettings->value(String(configName + "/Animations/ImportAnimations").c_str()));
+   SeparateAnimations = dAtob(configSettings->value(String(configName + "/Animations/SeparateAnimations").c_str()));
+   SeparateAnimationPrefix = configSettings->value(String(configName + "/Animations/SeparateAnimationPrefix").c_str());
+   animTiming = configSettings->value(String(configName + "/Animations/animTiming").c_str());
+   animFPS = dAtof(configSettings->value(String(configName + "/Animations/animFPS").c_str()));
+
+   //Collisions
+   GenerateCollisions = dAtob(configSettings->value(String(configName + "/Collision/GenerateCollisions").c_str()));
+   GenCollisionType = configSettings->value(String(configName + "/Collision/GenCollisionType").c_str());
+   CollisionMeshPrefix = configSettings->value(String(configName + "/Collision/CollisionMeshPrefix").c_str());
+   GenerateLOSCollisions = dAtob(configSettings->value(String(configName + "/Collision/GenerateLOSCollisions").c_str()));
+   GenLOSCollisionType = configSettings->value(String(configName + "/Collision/GenLOSCollisionType").c_str());
+   LOSCollisionMeshPrefix = configSettings->value(String(configName + "/Collision/LOSCollisionMeshPrefix").c_str());
+
+   //Images
+   importImages = dAtob(configSettings->value(String(configName + "/Images/importImages").c_str()));
+   ImageType = configSettings->value(String(configName + "/Images/ImageType").c_str());
+   DiffuseTypeSuffixes = configSettings->value(String(configName + "/Images/DiffuseTypeSuffixes").c_str());
+   NormalTypeSuffixes = configSettings->value(String(configName + "/Images/NormalTypeSuffixes").c_str());
+   MetalnessTypeSuffixes = configSettings->value(String(configName + "/Images/MetalnessTypeSuffixes").c_str());
+   RoughnessTypeSuffixes = configSettings->value(String(configName + "/Images/RoughnessTypeSuffixes").c_str());
+   SmoothnessTypeSuffixes = configSettings->value(String(configName + "/Images/SmoothnessTypeSuffixes").c_str());
+   AOTypeSuffixes = configSettings->value(String(configName + "/Images/AOTypeSuffixes").c_str());
+   PBRTypeSuffixes = configSettings->value(String(configName + "/Images/PBRTypeSuffixes").c_str());
+   TextureFilteringMode = configSettings->value(String(configName + "/Images/TextureFilteringMode").c_str());
+   UseMips = dAtob(configSettings->value(String(configName + "/Images/UseMips").c_str()));
+   IsHDR = dAtob(configSettings->value(String(configName + "/Images/IsHDR").c_str()));
+   Scaling = dAtof(configSettings->value(String(configName + "/Images/Scaling").c_str()));
+   Compressed = dAtob(configSettings->value(String(configName + "/Images/Compressed").c_str()));
+   GenerateMaterialOnImport = dAtob(configSettings->value(String(configName + "/Images/GenerateMaterialOnImport").c_str()));
+
+   //Sounds
+   VolumeAdjust = dAtof(configSettings->value(String(configName + "/Sounds/VolumeAdjust").c_str()));
+   PitchAdjust = dAtof(configSettings->value(String(configName + "/Sounds/PitchAdjust").c_str()));
+   Compressed = dAtob(configSettings->value(String(configName + "/Sounds/Compressed").c_str()));
+}
+
+ConsoleDocClass(AssetImportObject,
+   "@brief Defines properties for an AssetImportObject object.\n"
+   "@AssetImportObject is a SimObject derived object intended to act as a stand-in for the to-be imported objects.\n"
+   "@It contains important info such as dependencies, if it's been processed, any error/status issues and the originating file\n"
+   "@or if it's been programmatically generated as part of the import process.\n\n"
+   "@ingroup Assets\n"
+);
+
+IMPLEMENT_CONOBJECT(AssetImportObject);
+
+AssetImportObject::AssetImportObject() :
+   dirty(false),
+   skip(false),
+   processed(false),
+   generatedAsset(false),
+   parentAssetItem(nullptr),
+   tamlFilePath(""),
+   imageSuffixType(""),
+   shapeInfo(nullptr)
+{
+
+}
+
+AssetImportObject::~AssetImportObject()
+{
+
+}
+
+void AssetImportObject::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+ConsoleDocClass(AssetImporter,
+   "@brief Defines properties for an AssetImportObject object.\n"
+   "@AssetImportObject is a SimObject derived object intended to act as a stand-in for the to-be imported objects.\n"
+   "@It contains important info such as dependencies, if it's been processed, any error/status issues and the originating file\n"
+   "@or if it's been programmatically generated as part of the import process.\n\n"
+   "@ingroup Assets\n"
+);
+
+IMPLEMENT_CONOBJECT(AssetImporter);
+
+AssetImporter::AssetImporter() :
+   importIssues(false),
+   isReimport(false),
+   assetHeirarchyChanged(false)
+{
+}
+
+AssetImporter::~AssetImporter()
+{
+
+}
+
+void AssetImporter::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+//
+// Utility Functions
+//
+
+AssetImportObject* AssetImporter::addImportingFile(Torque::Path filePath)
+{
+   String assetType = getAssetTypeByFile(filePath);
+
+   if (assetType.isEmpty())
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is of an unrecognized/unsupported type.", filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+      return nullptr;
+   }
+
+   AssetImportObject* newAssetItem = addImportingAsset(assetType, filePath, nullptr, "");
+
+   originalImportingAssets.push_back(filePath);
+
+   return newAssetItem;
+}
+
+AssetImportObject* AssetImporter::addImportingAsset(String assetType, Torque::Path filePath, AssetImportObject* parentItem, String assetNameOverride)
+{
+   String assetName;
+
+   //In some cases(usually generated assets on import, like materials) we'll want to specifically define the asset name instead of peeled from the filePath
+   if (assetNameOverride.isNotEmpty())
+      assetName = assetNameOverride;
+   else
+      assetName = filePath.getFileName();
+
+   AssetImportObject* assetImportObj = new AssetImportObject();
+   assetImportObj->registerObject();
+
+   assetImportObj->assetType = assetType;
+   assetImportObj->filePath = filePath;
+   assetImportObj->assetName = assetName;
+   assetImportObj->cleanAssetName = assetName;
+   assetImportObj->moduleName = targetModuleId;
+   assetImportObj->status = "";
+   assetImportObj->statusType = "";
+   assetImportObj->statusInfo = "";
+
+   assetImportObj->dirty = false;
+   assetImportObj->skip = false;
+   assetImportObj->processed = false;
+   assetImportObj->generatedAsset = false;
+
+   if (parentItem != nullptr)
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Added Child Importing Asset to %s", parentItem->assetName.c_str());
+      activityLog.push_back(importLogBuffer);
+
+      parentItem->childAssetItems.push_back(assetImportObj);
+      assetImportObj->parentAssetItem = parentItem;
+   }
+   else
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Added Importing Asset");
+      activityLog.push_back(importLogBuffer);
+   }
+
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "   Asset Info: Name: %s | Type: %s", assetImportObj->assetName.c_str(), assetImportObj->assetType.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   if (!filePath.isEmpty())
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "   File: %s", filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+   }
+
+   return assetImportObj;
+}
+
+void AssetImporter::deleteImportingAsset(AssetImportObject* assetItem)
+{
+   assetItem->skip = true;
+
+   //log it
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Deleting Importing Asset %s and all it's child items", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+}
+
+AssetImportObject* AssetImporter::findImportingAssetByName(String assetName, AssetImportObject* assetItem)
+{
+   if (assetItem == nullptr)
+   {
+      for (U32 i = 0; i < importingAssets.size(); i++)
+      {
+         if (importingAssets[i]->cleanAssetName == assetName)
+         {
+            return importingAssets[i];
+         }
+
+         //If it wasn't a match, try recusing on the children(if any)
+         AssetImportObject* retItem = findImportingAssetByName(assetName, importingAssets[i]);
+         if (retItem != nullptr)
+            return retItem;
+      }
+   }
+   else
+   {
+      //this is the child recursing section
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         if (assetItem->childAssetItems[i]->cleanAssetName == assetName)
+         {
+            return assetItem->childAssetItems[i];
+         }
+
+         //If it wasn't a match, try recusing on the children(if any)
+         AssetImportObject* retItem = findImportingAssetByName(assetName, assetItem->childAssetItems[i]);
+         if (retItem != nullptr)
+            return retItem;
+      }
+   }
+
+   return nullptr;
+}
+
+ModuleDefinition* AssetImporter::getModuleFromPath(Torque::Path filePath)
+{
+   U32 folderCount = StringUnit::getUnitCount(filePath.getPath().c_str(), "/");
+
+   for (U32 i = 0; i < folderCount; i++)
+   {
+      String folderName = StringUnit::getUnit(filePath.getPath().c_str(), i, "/");
+
+      ModuleDefinition* moduleDef = ModuleDatabase.findModule(folderName.c_str(), 1);
+      if (moduleDef != nullptr)
+         return moduleDef;
+   }
+
+   return nullptr;
+}
+
+String AssetImporter::parseImageSuffixes(String assetName, String* suffixType)
+{
+   U32 suffixTypeIdx = 0;
+   while (suffixTypeIdx < 6)
+   {
+      String suffixList;
+      switch (suffixTypeIdx)
+      {
+         case 0:
+            suffixList = activeImportConfig.DiffuseTypeSuffixes;
+            suffixType->insert(0, "Albedo", 10);
+            break;
+         case 1:
+            suffixList = activeImportConfig.NormalTypeSuffixes;
+            suffixType->insert(0, "Normal", 10);
+            break;
+         case 2:
+            suffixList = activeImportConfig.RoughnessTypeSuffixes;
+            suffixType->insert(0, "Roughness", 10);
+            break;
+         case 3:
+            suffixList = activeImportConfig.AOTypeSuffixes;
+            suffixType->insert(0, "AO", 10);
+            break;
+         case 4:
+            suffixList = activeImportConfig.MetalnessTypeSuffixes;
+            suffixType->insert(0, "Metalness", 10);
+            break;
+         case 5:
+            suffixList = activeImportConfig.PBRTypeSuffixes;
+            suffixType->insert(0, "PBRConfig", 10);
+            break;
+         default:
+            suffixList = "";
+      }
+
+      suffixTypeIdx++;
+
+      U32 suffixCount = StringUnit::getUnitCount(suffixList, ",;");
+      for (U32 i = 0; i < suffixCount; i++)
+      {
+         String suffix = StringUnit::getUnit(suffixList, i, ",;");
+         String searchSuffix = String("*") + suffix;
+         if (FindMatch::isMatch(searchSuffix.c_str(), assetName.c_str(), false))
+         {
+            //We have a match, so indicate as such
+            return suffix;
+         }
+      }
+   }
+
+   return "";
+}
+
+String AssetImporter::getAssetTypeByFile(Torque::Path filePath)
+{
+   String fileExt = filePath.getExtension();
+
+   if (fileExt == String("png") || fileExt == String("jpg") || fileExt == String("jpeg") || fileExt == String("dds"))
+      return "ImageAsset";
+   else if (fileExt == String("dae") || fileExt == String("fbx") || fileExt == String("blend") || fileExt == String("obj") || fileExt == String("dts") || fileExt == String("gltf") || fileExt == String("gltb"))
+      return "ShapeAsset";
+   else if (fileExt == String("dsq"))
+      return "ShapeAnimationAsset";
+   else if (fileExt == String("ogg") || fileExt == String("wav") || fileExt == String("mp3"))
+      return "SoundAsset";
+   else if (fileExt == String("zip"))
+      return "Zip";
+   else if (fileExt.isEmpty())
+      return "Folder";
+
+   return "";
+}
+
+void AssetImporter::resetImportSession()
+{
+   importingAssets.clear();
+   activityLog.clear();
+
+   for (U32 i = 0; i < originalImportingAssets.size(); i++)
+   {
+      addImportingFile(originalImportingAssets[i]);
+   }
+}
+
+void AssetImporter::resetImportAsset(AssetImportObject* assetItem)
+{
+}
+
+S32 AssetImporter::getActivityLogLineCount()
+{
+   return activityLog.size();
+}
+
+String AssetImporter::getActivityLogLine(U32 line)
+{
+   if (line >= activityLog.size())
+      return "";
+
+   return activityLog[line];
+}
+
+void AssetImporter::dumpActivityLog()
+{
+   for (U32 i = 0; i < activityLog.size(); i++)
+   {
+      Con::printf(activityLog[i].c_str());
+   }
+}
+
+S32 AssetImporter::getAssetItemCount()
+{
+   return importingAssets.size();
+}
+
+AssetImportObject* AssetImporter::getAssetItem(U32 index)
+{
+   if (index >= importingAssets.size())
+      return nullptr;
+
+   return importingAssets[index];
+}
+
+S32 AssetImporter::getAssetItemChildCount(AssetImportObject* assetItem)
+{
+   return assetItem->childAssetItems.size();
+}
+
+AssetImportObject* AssetImporter::getAssetItemChild(AssetImportObject* assetItem, U32 index)
+{
+   if (index >= assetItem->childAssetItems.size())
+      return nullptr;
+
+   return assetItem->childAssetItems[index];
+}
+//
+// Processing
+//
+// Helper struct for counting nodes, meshes and polygons down through the scene
+// hierarchy
+struct SceneStats
+{
+   S32 numNodes;
+   S32 numMeshes;
+   S32 numPolygons;
+   S32 numMaterials;
+   S32 numLights;
+   S32 numClips;
+
+   SceneStats() : numNodes(0), numMeshes(0), numPolygons(0), numMaterials(0), numLights(0), numClips(0) { }
+};
+
+// Recurse through the <visual_scene> adding nodes and geometry to the GuiTreeView control
+static void processNode(GuiTreeViewCtrl* tree, domNode* node, S32 parentID, SceneStats& stats)
+{
+   stats.numNodes++;
+   S32 nodeID = tree->insertItem(parentID, _GetNameOrId(node), "node", "", 0, 0);
+
+   // Update mesh and poly counts
+   for (S32 i = 0; i < node->getContents().getCount(); i++)
+   {
+      domGeometry* geom = 0;
+      const char* elemName = "";
+
+      daeElement* child = node->getContents()[i];
+      switch (child->getElementType())
+      {
+      case COLLADA_TYPE::INSTANCE_GEOMETRY:
+      {
+         domInstance_geometry* instgeom = daeSafeCast<domInstance_geometry>(child);
+         if (instgeom)
+         {
+            geom = daeSafeCast<domGeometry>(instgeom->getUrl().getElement());
+            elemName = _GetNameOrId(geom);
+         }
+         break;
+      }
+
+      case COLLADA_TYPE::INSTANCE_CONTROLLER:
+      {
+         domInstance_controller* instctrl = daeSafeCast<domInstance_controller>(child);
+         if (instctrl)
+         {
+            domController* ctrl = daeSafeCast<domController>(instctrl->getUrl().getElement());
+            elemName = _GetNameOrId(ctrl);
+            if (ctrl && ctrl->getSkin())
+               geom = daeSafeCast<domGeometry>(ctrl->getSkin()->getSource().getElement());
+            else if (ctrl && ctrl->getMorph())
+               geom = daeSafeCast<domGeometry>(ctrl->getMorph()->getSource().getElement());
+         }
+         break;
+      }
+
+      case COLLADA_TYPE::INSTANCE_LIGHT:
+         stats.numLights++;
+         tree->insertItem(nodeID, _GetNameOrId(node), "light", "", 0, 0);
+         break;
+      }
+
+      if (geom && geom->getMesh())
+      {
+         const char* name = _GetNameOrId(node);
+         if (dStrEqual(name, "null") || dStrEndsWith(name, "PIVOT"))
+            name = _GetNameOrId(daeSafeCast<domNode>(node->getParent()));
+
+         stats.numMeshes++;
+         tree->insertItem(nodeID, name, "mesh", "", 0, 0);
+
+         for (S32 j = 0; j < geom->getMesh()->getTriangles_array().getCount(); j++)
+            stats.numPolygons += geom->getMesh()->getTriangles_array()[j]->getCount();
+         for (S32 j = 0; j < geom->getMesh()->getTristrips_array().getCount(); j++)
+            stats.numPolygons += geom->getMesh()->getTristrips_array()[j]->getCount();
+         for (S32 j = 0; j < geom->getMesh()->getTrifans_array().getCount(); j++)
+            stats.numPolygons += geom->getMesh()->getTrifans_array()[j]->getCount();
+         for (S32 j = 0; j < geom->getMesh()->getPolygons_array().getCount(); j++)
+            stats.numPolygons += geom->getMesh()->getPolygons_array()[j]->getCount();
+         for (S32 j = 0; j < geom->getMesh()->getPolylist_array().getCount(); j++)
+            stats.numPolygons += geom->getMesh()->getPolylist_array()[j]->getCount();
+      }
+   }
+
+   // Recurse into child nodes
+   for (S32 i = 0; i < node->getNode_array().getCount(); i++)
+      processNode(tree, node->getNode_array()[i], nodeID, stats);
+
+   for (S32 i = 0; i < node->getInstance_node_array().getCount(); i++)
+   {
+      domInstance_node* instnode = node->getInstance_node_array()[i];
+      domNode* dNode = daeSafeCast<domNode>(instnode->getUrl().getElement());
+      if (dNode)
+         processNode(tree, dNode, nodeID, stats);
+   }
+}
+
+static bool enumColladaForImport(const char* shapePath, GuiTreeViewCtrl* tree, bool loadCachedDts)
+{
+   // Check if a cached DTS is available => no need to import the collada file
+   // if we can load the DTS instead
+   Torque::Path path(shapePath);
+   if (loadCachedDts && ColladaShapeLoader::canLoadCachedDTS(path))
+      return false;
+
+   // Check if this is a Sketchup file (.kmz) and if so, mount the zip filesystem
+   // and get the path to the DAE file.
+   String mountPoint;
+   Torque::Path daePath;
+   bool isSketchup = ColladaShapeLoader::checkAndMountSketchup(path, mountPoint, daePath);
+
+   // Load the Collada file into memory
+   domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(daePath);
+   if (!root)
+   {
+      TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
+      return false;
+   }
+
+   if (isSketchup)
+   {
+      // Unmount the zip if we mounted it
+      Torque::FS::Unmount(mountPoint);
+   }
+
+   // Initialize tree
+   tree->removeItem(0);
+   S32 nodesID = tree->insertItem(0, "Shape", "", "", 0, 0);
+   S32 matsID = tree->insertItem(0, "Materials", "", "", 0, 0);
+   S32 animsID = tree->insertItem(0, "Animations", "", "", 0, 0);
+
+   SceneStats stats;
+
+   // Query DOM for shape summary details
+   for (S32 i = 0; i < root->getLibrary_visual_scenes_array().getCount(); i++)
+   {
+      const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[i];
+      for (S32 j = 0; j < libScenes->getVisual_scene_array().getCount(); j++)
+      {
+         const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[j];
+         for (S32 k = 0; k < visualScene->getNode_array().getCount(); k++)
+            processNode(tree, visualScene->getNode_array()[k], nodesID, stats);
+      }
+   }
+
+   // Get material count
+   for (S32 i = 0; i < root->getLibrary_materials_array().getCount(); i++)
+   {
+      const domLibrary_materials* libraryMats = root->getLibrary_materials_array()[i];
+      stats.numMaterials += libraryMats->getMaterial_array().getCount();
+      for (S32 j = 0; j < libraryMats->getMaterial_array().getCount(); j++)
+      {
+         domMaterial* mat = libraryMats->getMaterial_array()[j];
+         tree->insertItem(matsID, _GetNameOrId(mat), "", "", 0, 0);
+      }
+   }
+
+   // Get images count
+   for (S32 i = 0; i < root->getLibrary_images_array().getCount(); i++)
+   {
+      const domLibrary_images* libraryImages = root->getLibrary_images_array()[i];
+
+      for (S32 j = 0; j < libraryImages->getImage_array().getCount(); j++)
+      {
+         domImage* img = libraryImages->getImage_array()[j];
+
+         S32 materialID = tree->findItemByName(_GetNameOrId(img));
+
+         if (materialID == 0)
+            continue;
+
+         tree->setItemValue(materialID, img->getInit_from()->getValue().str().c_str());
+      }
+   }
+
+   // Get animation count
+   for (S32 i = 0; i < root->getLibrary_animation_clips_array().getCount(); i++)
+   {
+      const domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[i];
+      stats.numClips += libraryClips->getAnimation_clip_array().getCount();
+      for (S32 j = 0; j < libraryClips->getAnimation_clip_array().getCount(); j++)
+      {
+         domAnimation_clip* clip = libraryClips->getAnimation_clip_array()[j];
+         tree->insertItem(animsID, _GetNameOrId(clip), "animation", "", 0, 0);
+      }
+   }
+   if (stats.numClips == 0)
+   {
+      // No clips => check if there are any animations (these will be added to a default clip)
+      for (S32 i = 0; i < root->getLibrary_animations_array().getCount(); i++)
+      {
+         const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[i];
+         if (libraryAnims->getAnimation_array().getCount())
+         {
+            stats.numClips = 1;
+            tree->insertItem(animsID, "ambient", "animation", "", 0, 0);
+            break;
+         }
+      }
+   }
+
+   // Extract the global scale and up_axis from the top level <asset> element,
+   F32 unit = 1.0f;
+   domUpAxisType upAxis = UPAXISTYPE_Z_UP;
+   if (root->getAsset()) {
+      if (root->getAsset()->getUnit())
+         unit = root->getAsset()->getUnit()->getMeter();
+      if (root->getAsset()->getUp_axis())
+         upAxis = root->getAsset()->getUp_axis()->getValue();
+   }
+
+   TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
+
+   // Store shape information in the tree control
+   tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", stats.numNodes));
+   tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", stats.numMeshes));
+   tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", stats.numPolygons));
+   tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", stats.numMaterials));
+   tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", stats.numLights));
+   tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", stats.numClips));
+   tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", 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");
+
+   char shapesStr[16];
+   dSprintf(shapesStr, 16, "%i", stats.numMeshes);
+   char materialsStr[16];
+   dSprintf(materialsStr, 16, "%i", stats.numMaterials);
+   char animationsStr[16];
+   dSprintf(animationsStr, 16, "%i", stats.numClips);
+
+   tree->setItemValue(nodesID, StringTable->insert(shapesStr));
+   tree->setItemValue(matsID, StringTable->insert(materialsStr));
+   tree->setItemValue(animsID, StringTable->insert(animationsStr));
+
+   return true;
+}
+
+void AssetImporter::processImportAssets(AssetImportObject* assetItem)
+{
+   if (assetItem == nullptr)
+   {
+      assetHeirarchyChanged = false;
+
+      for (U32 i = 0; i < importingAssets.size(); i++)
+      {
+         AssetImportObject* item = importingAssets[i];
+         if (item->skip)
+            continue;
+
+         if (!item->processed)
+         {
+            //Sanitize before modifying our asset name(suffix additions, etc)
+            if (item->assetName != item->cleanAssetName)
+               item->assetName = item->cleanAssetName;
+
+            //handle special pre-processing here for any types that need it
+
+            //process the asset items
+            if (item->assetType == String("ImageAsset"))
+               processImageAsset(item);
+            else if (item->assetType == String("ShapeAsset"))
+               processShapeAsset(item);
+            else if (item->assetType == String("SoundAsset"))
+               SoundAsset::prepareAssetForImport(this, item);
+            else if (item->assetType == String("MaterialAsset"))
+               processMaterialAsset(item);
+            else if (item->assetType == String("ShapeAnimationAsset"))
+               ShapeAnimationAsset::prepareAssetForImport(this, item);
+
+            item->processed = true;
+         }
+
+         //try recusing on the children(if any)
+         processImportAssets(item);
+      }
+   }
+   else
+   {
+      //this is the child recursing section
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+         if (childItem->skip)
+            continue;
+
+         if (!childItem->processed)
+         {
+            //Sanitize before modifying our asset name(suffix additions, etc)
+            if (childItem->assetName != childItem->cleanAssetName)
+               childItem->assetName = childItem->cleanAssetName;
+
+            //handle special pre-processing here for any types that need it
+
+            //process the asset items
+            if (childItem->assetType == String("ImageAsset"))
+               processImageAsset(childItem);
+            else if (childItem->assetType == String("ShapeAsset"))
+               processShapeAsset(childItem);
+            else if (childItem->assetType == String("SoundAsset"))
+               SoundAsset::prepareAssetForImport(this, childItem);
+            else if (childItem->assetType == String("MaterialAsset"))
+               processMaterialAsset(childItem);
+            else if (childItem->assetType == String("ShapeAnimationAsset"))
+               ShapeAnimationAsset::prepareAssetForImport(this, childItem);
+
+            childItem->processed = true;
+         }
+
+         //try recusing on the children(if any)
+         processImportAssets(childItem);
+      }
+   }
+
+   //If our hierarchy changed, it's because we did so during processing
+   //so we'll loop back through again until everything has been processed
+   if (assetHeirarchyChanged)
+      processImportAssets();
+}
+
+void AssetImporter::processImageAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Preparing Image for Import: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   if ((activeImportConfig.GenerateMaterialOnImport && assetItem->parentAssetItem == nullptr) || assetItem->parentAssetItem != nullptr)
+   {
+      //find our suffix match, if any
+      String noSuffixName = assetItem->assetName;
+      String suffixType;
+      String suffix = parseImageSuffixes(assetItem->assetName, &suffixType);
+      if (suffix.isNotEmpty())
+      {
+         assetItem->imageSuffixType = suffixType;
+         S32 suffixPos =assetItem->assetName.find(suffix, 0, String::NoCase|String::Left);
+         noSuffixName = assetItem->assetName.substr(0, suffixPos);
+      }
+
+      //We try to automatically populate materials under the naming convention: materialName: Rock, image maps: Rock_Albedo, Rock_Normal, etc
+
+      AssetImportObject* materialAsset = findImportingAssetByName(noSuffixName);
+      if (materialAsset != nullptr && materialAsset->assetType != String("MaterialAsset"))
+      {
+         //We may have a situation where an asset matches the no-suffix name, but it's not a material asset. Ignore this
+         //asset item for now
+         materialAsset = nullptr;
+      }
+
+      //If we didn't find a matching material asset in our current items, we'll make one now
+      if (materialAsset == nullptr)
+      {
+         if (!assetItem->filePath.isEmpty())
+         {
+            materialAsset = addImportingAsset("MaterialAsset", "", nullptr, noSuffixName);
+            //Add the material into the primary list of importing assets
+            importingAssets.push_back(materialAsset);
+         }
+      }
+
+      //Not that, one way or another, we have the generated material asset, lets move on to associating our image with it
+      if (materialAsset != nullptr && materialAsset != assetItem->parentAssetItem)
+      {
+         if (assetItem->parentAssetItem != nullptr)
+         {
+            //If the image had an existing parent, it gets removed from that parent's child item list
+            assetItem->parentAssetItem->childAssetItems.remove(assetItem);
+         }
+         else
+         {
+            //If it didn't have one, we're going to pull it from the importingAssets list
+            importingAssets.remove(assetItem);
+         }
+
+         //Now we can add it to the correct material asset
+         materialAsset->childAssetItems.push_back(assetItem);
+         assetItem->parentAssetItem = materialAsset;
+
+         assetHeirarchyChanged = true;
+      }
+
+      //Now to do some cleverness. If we're generating a material, we can parse like assets being imported(similar filenames) but different suffixes
+      //If we find these, we'll just populate into the original's material
+
+      //if we need to append the diffuse suffix and indeed didn't find a suffix on the name, do that here
+      if (suffixType.isEmpty())
+      {
+         if (activeImportConfig.UseDiffuseSuffixOnOriginImage)
+         {
+            String diffuseToken = StringUnit::getUnit(activeImportConfig.DiffuseTypeSuffixes, 0, ",;");
+            assetItem->assetName = assetItem->assetName + diffuseToken;
+         }
+         else
+         {
+            //We need to ensure that our image asset doesn't match the same name as the material asset, so if we're not trying to force the diffuse suffix
+            //we'll give it a generic one
+            if (materialAsset->assetName.compare(assetItem->assetName) == 0)
+            {
+               assetItem->assetName = assetItem->assetName + "_image";
+            }
+         }
+
+         suffixType = "Albedo";
+      }
+      
+      if (suffixType.isNotEmpty())
+      {
+         assetItem->imageSuffixType = suffixType;
+
+         //otherwise, if we have some sort of suffix, we'll want to figure out if we've already got an existing material, and should append to it  
+         if (activeImportConfig.PopulateMaterialMaps)
+         {
+            
+         }
+      }
+   }
+
+   assetItem->processed = true;
+}
+
+void AssetImporter::processMaterialAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Preparing Material for Import: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   String filePath = assetItem->filePath.getFullPath();
+   String fileName = assetItem->filePath.getFileName();
+   String fileExt = assetItem->filePath.getExtension();
+   const char* assetName = assetItem->assetName.c_str();
+
+   assetItem->generatedAsset = true;
+
+   if (activeImportConfig.IgnoreMaterials.isNotEmpty())
+   {
+      U32 ignoredMatNameCount = StringUnit::getUnitCount(activeImportConfig.IgnoreMaterials, ".;");
+      for (U32 i = 0; i < ignoredMatNameCount; i++)
+      {
+         String ignoredName = StringUnit::getUnit(activeImportConfig.IgnoreMaterials, i, ".;");
+         if (FindMatch::isMatch(ignoredName.c_str(), assetName, false))
+         {
+            assetItem->skip = true;
+
+            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Material %s has been ignored due to it's name being listed in the IgnoreMaterials list in the Import Config.", assetItem->assetName.c_str());
+            activityLog.push_back(importLogBuffer);
+            return;
+         }
+      }
+   }
+
+   if (activeImportConfig.PopulateMaterialMaps)
+   {
+      //If we're trying to populate the rest of our material maps, we need to go looking
+   }
+   
+   assetItem->processed = true;
+}
+
+void AssetImporter::processShapeAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Preparing Shape for Import: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   String filePath = assetItem->filePath.getFullPath();
+   String fileName = assetItem->filePath.getFileName();
+   String fileExt = assetItem->filePath.getExtension();
+   const char* assetName = assetItem->assetName.c_str();
+
+   if (assetItem->shapeInfo == nullptr)
+   {
+      GuiTreeViewCtrl* shapeInfo = new GuiTreeViewCtrl();
+      shapeInfo->registerObject();
+
+      if (fileExt.compare("dae") == 0)
+      {
+         enumColladaForImport(filePath, shapeInfo, false);
+      }
+      else
+      {
+         // Check if a cached DTS is available => no need to import the source file
+         // if we can load the DTS instead
+
+         AssimpShapeLoader loader;
+         loader.fillGuiTreeView(filePath.c_str(), shapeInfo);
+      }
+
+      assetItem->shapeInfo = shapeInfo;
+   }
+
+   S32 meshCount = dAtoi(assetItem->shapeInfo->getDataField(StringTable->insert("_meshCount"), nullptr));
+   S32 shapeItem = assetItem->shapeInfo->findItemByName("Meshes");
+
+   S32 animCount = dAtoi(assetItem->shapeInfo->getDataField(StringTable->insert("_animCount"), nullptr));
+   S32 animItem = assetItem->shapeInfo->findItemByName("Animations");
+
+   S32 materialCount = dAtoi(assetItem->shapeInfo->getDataField(StringTable->insert("_materialCount"), nullptr));
+   S32 matItem = assetItem->shapeInfo->findItemByName("Materials");
+
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "   Shape Info: Mesh Count: %i | Material Count: %i | Anim Count: %i", meshCount, animCount, materialCount);
+   activityLog.push_back(importLogBuffer);
+
+   if (activeImportConfig.ImportMesh && meshCount > 0)
+   {
+
+   }
+
+   if (activeImportConfig.ImportAnimations && animCount > 0)
+   {
+      //If we have animations but no meshes, then this is a pure animation file so we can swap the asset type here
+      if (meshCount == 0)
+      {
+         //assetItem->assetType = "ShapeAnimation";
+      }
+   }
+
+   if (activeImportConfig.ImportMaterials && materialCount > 0)
+   {
+      S32 materialId = assetItem->shapeInfo->getChildItem(matItem);
+      processShapeMaterialInfo(assetItem, materialId);
+
+      materialId = assetItem->shapeInfo->getNextSiblingItem(materialId);
+      while (materialId != 0)
+      {
+         processShapeMaterialInfo(assetItem, materialId);
+         materialId = assetItem->shapeInfo->getNextSiblingItem(materialId);
+      }
+   }
+
+   assetItem->processed = true;
+}
+
+void AssetImporter::processShapeMaterialInfo(AssetImportObject* assetItem, S32 materialItemId)
+{
+   String matName = assetItem->shapeInfo->getItemText(materialItemId);
+
+   Torque::Path filePath = assetItem->shapeInfo->getItemValue(materialItemId);
+   if (filePath.getFullFileName().isNotEmpty())
+   {
+      if (!Platform::isFile(filePath.getFullFileName()))
+      {
+         //could be a stale path reference, such as if it was downloaded elsewhere. Trim to just the filename and see
+         //if we can find it there
+         String shapePathBase = assetItem->filePath.getPath();
+
+         String matFilePath = filePath.getFileName() + "." + filePath.getExtension();
+         //trim (not found) if needbe
+         /*
+         %suffixPos = strpos(strlwr(%filename), " (not found)", 0);
+         %filename = getSubStr(%filename, 0, %suffixPos);
+         */
+
+         String testFileName = shapePathBase + "/" + matFilePath;
+         if (Platform::isFile(testFileName))
+         {
+            filePath = testFileName;
+         }
+      }
+
+      AssetImportObject* matAssetItem = addImportingAsset("MaterialAsset", "", assetItem, matName);
+      addImportingAsset("ImageAsset", filePath, matAssetItem, "");
+   }
+   else
+   {
+      /*
+      //check to see if it's actually just a flat color
+      if(getWordCount(%filePath) == 4 && getWord(%filePath, 0) $= "Color:")
+      {
+         AssetBrowser.addImportingAsset("MaterialAsset", %matName, %assetItem);
+      }
+      else
+      {
+         //we need to try and find our material, since the shapeInfo wasn't able to find it automatically
+         %filePath = findImageFile(filePath(%assetItem.filePath), %matName);
+         if(%filePath !$= "" && isFile(%filePath))
+            AssetBrowser.addImportingAsset("MaterialAsset", %filePath, %assetItem);
+         else
+            AssetBrowser.addImportingAsset("MaterialAsset", filePath(%assetItem.filePath) @ "/" @ %matName, %assetItem);
+      }
+      */
+   }
+}
+
+//
+// Validation
+//
+
+bool AssetImporter::validateAssets()
+{
+   importIssues = false;
+
+   resetAssetValidationStatus();
+
+   for (U32 i = 0; i < importingAssets.size(); i++)
+   {
+      validateAsset(importingAssets[i]);
+      resolveAssetItemIssues(importingAssets[i]);
+   }
+
+   return importIssues;
+}
+
+void AssetImporter::validateAsset(AssetImportObject* assetItem)
+{
+   if (assetItem->skip)
+      return;
+
+   bool hasCollision = checkAssetForCollision(assetItem);
+
+   if (hasCollision)
+   {
+      importIssues = true;
+      return;
+   }
+
+   if (!isReimport)
+   {
+      AssetQuery aQuery;
+      U32 numAssetsFound = AssetDatabase.findAllAssets(&aQuery);
+
+      bool hasCollision = false;
+      for (U32 i = 0; i < numAssetsFound; i++)
+      {
+         StringTableEntry assetId = aQuery.mAssetList[i];
+
+         ModuleDefinition* moduleDef = AssetDatabase.getAssetModuleDefinition(assetId);
+
+         if (moduleDef->getModuleId() != StringTable->insert(targetModuleId.c_str()))
+            continue;
+
+         StringTableEntry assetName = AssetDatabase.getAssetName(assetId);
+
+         if (assetName == StringTable->insert(assetItem->assetName.c_str()))
+         {
+            hasCollision = true;
+            assetItem->status = "Error";
+            assetItem->statusType = "DuplicateAsset";
+            assetItem->statusInfo = "Duplicate asset names found within the target module!\nAsset \"" + assetItem->assetName + "\" of type \"" + assetItem->assetType + "\" has a matching name.\nPlease rename it and try again!";
+
+            //log it
+            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Asset %s has an identically named asset in the target module.", assetItem->assetName.c_str());
+            activityLog.push_back(importLogBuffer);
+            break;
+         }
+      }
+   }
+
+   if (!assetItem->filePath.isEmpty() && !assetItem->generatedAsset && !Platform::isFile(assetItem->filePath.getFullPath().c_str()))
+   {
+      assetItem->status = "Error";
+      assetItem->statusType = "MissingFile";
+      assetItem->statusInfo = "Unable to find file to be imported with provided path: " + assetItem->filePath + "\n Please select a valid file.";
+
+      //log it
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Asset %s's file at %s was not found.", assetItem->assetName.c_str(), assetItem->filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+   }
+
+   if (assetItem->status == String("Warning"))
+   {
+      if (activeImportConfig.WarningsAsErrors)
+      {
+         assetItem->status = "Error";
+
+         //log it
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Import configuration has treated an import warning as an error.", assetItem->assetName.c_str());
+         activityLog.push_back(importLogBuffer);
+      }
+   }
+
+   if (assetItem->status == String("Error"))
+      importIssues = true;
+
+   for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+   {
+      validateAsset(assetItem->childAssetItems[i]);
+      resolveAssetItemIssues(assetItem->childAssetItems[i]);
+   }
+
+   return;
+}
+
+void AssetImporter::resetAssetValidationStatus(AssetImportObject* assetItem)
+{
+   if (assetItem == nullptr)
+   {
+      for (U32 i = 0; i < importingAssets.size(); i++)
+      {
+         if (importingAssets[i]->skip)
+            continue;
+
+         importingAssets[i]->status = "";
+         importingAssets[i]->statusType = "";
+         importingAssets[i]->statusInfo = "";
+
+         //If it wasn't a match, try recusing on the children(if any)
+         resetAssetValidationStatus(importingAssets[i]);
+      }
+   }
+   else
+   {
+      //this is the child recursing section
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         if (assetItem->childAssetItems[i]->skip)
+            continue;
+
+         assetItem->childAssetItems[i]->status = "";
+         assetItem->childAssetItems[i]->statusType = "";
+         assetItem->childAssetItems[i]->statusInfo = "";
+
+         //If it wasn't a match, try recusing on the children(if any)
+         resetAssetValidationStatus(assetItem->childAssetItems[i]);
+      }
+   }
+}
+
+bool AssetImporter::checkAssetForCollision(AssetImportObject* assetItemToCheck, AssetImportObject* assetItem)
+{
+   bool results = false;
+
+   if (assetItem == nullptr)
+   {
+      for (U32 i = 0; i < importingAssets.size(); i++)
+      {
+         if (importingAssets[i]->skip)
+            continue;
+
+         if ((assetItemToCheck->assetName.compare(importingAssets[i]->assetName) == 0) && (assetItemToCheck->getId() != importingAssets[i]->getId()))
+         {
+            //we do have a collision, note the collsion and bail out
+            assetItemToCheck->status = "Warning";
+            assetItemToCheck->statusType = "DuplicateImportAsset";
+            assetItemToCheck->statusInfo = "Duplicate asset names found with importing assets!\nAsset \"" + importingAssets[i]->assetName + "\" of the type \"" + importingAssets[i]->assetType + "\" and \"" +
+                                             assetItemToCheck->assetName + "\" of the type \"" + assetItemToCheck->assetType + "\" have matching names.\nPlease rename one of them.";
+
+             dSprintf(importLogBuffer, sizeof(importLogBuffer), "Warning! Asset %s, type %s has a naming collision with another importing asset: %s, type %s",
+                assetItemToCheck->assetName.c_str(), assetItemToCheck->assetType.c_str(),
+                importingAssets[i]->assetName.c_str(), importingAssets[i]->assetType.c_str());
+             activityLog.push_back(importLogBuffer);
+
+            return true;
+         }
+
+         //If it wasn't a match, try recusing on the children(if any)
+         results = checkAssetForCollision(assetItemToCheck, importingAssets[i]);
+         if (results)
+            return results;
+      }
+   }
+   else
+   {
+      //this is the child recursing section
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         if (assetItem->childAssetItems[i]->skip)
+            continue;
+
+         if ((assetItemToCheck->assetName.compare(assetItem->childAssetItems[i]->assetName) == 0) && (assetItemToCheck->getId() != assetItem->childAssetItems[i]->getId()))
+         {
+            //we do have a collision, note the collsion and bail out
+            assetItemToCheck->status = "Warning";
+            assetItemToCheck->statusType = "DuplicateImportAsset";
+            assetItemToCheck->statusInfo = "Duplicate asset names found with importing assets!\nAsset \"" + assetItem->assetName + "\" of the type \"" + assetItem->assetType + "\" and \"" +
+               assetItemToCheck->assetName + "\" of the type \"" + assetItemToCheck->assetType + "\" have matching names.\nPlease rename one of them.";
+
+            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Warning! Asset %s, type %s has a naming collision with another importing asset: %s, type %s",
+               assetItemToCheck->assetName.c_str(), assetItemToCheck->assetType.c_str(),
+               importingAssets[i]->assetName.c_str(), importingAssets[i]->assetType.c_str());
+            activityLog.push_back(importLogBuffer);
+
+            return true;
+         }
+
+         //If it wasn't a match, try recusing on the children(if any)
+         results = checkAssetForCollision(assetItemToCheck, assetItem->childAssetItems[i]);
+         if (results)
+            return results;
+      }
+   }
+
+   return results;
+}
+
+void AssetImporter::resolveAssetItemIssues(AssetImportObject* assetItem)
+{
+   if (assetItem->statusType == String("DuplicateImportAsset") || assetItem->statusType == String("DuplicateAsset"))
+   {
+      String humanReadableReason = assetItem->statusType == String("DuplicateImportAsset") ? "Importing asset was duplicate of another importing asset" : "Importing asset was duplicate of an existing asset";
+
+      //get the config value for duplicateAutoResolution
+      if (activeImportConfig.DuplicatAutoResolution == String("AutoPrune"))
+      {
+         //delete the item
+         deleteImportingAsset(assetItem);
+
+         //log it's deletion
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was autoprined due to %s as part of the Import Configuration", assetItem->assetName.c_str(), humanReadableReason.c_str());
+         activityLog.push_back(importLogBuffer);
+
+         importIssues = false;
+      }
+      else if (activeImportConfig.DuplicatAutoResolution == String("AutoRename"))
+      {
+         //Set trailing number
+         String renamedAssetName = assetItem->assetName;
+         renamedAssetName = Sim::getUniqueName(renamedAssetName.c_str());
+
+         //Log it's renaming
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was renamed due to %s as part of the Import Configuration", assetItem->assetName.c_str(), humanReadableReason.c_str());
+         activityLog.push_back(importLogBuffer);
+
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was renamed to %s", assetItem->assetName.c_str(), renamedAssetName.c_str());
+         activityLog.push_back(importLogBuffer);
+
+         assetItem->assetName = renamedAssetName;
+
+         //Whatever status we had prior is no longer relevent, so reset the status
+         resetAssetValidationStatus(assetItem);
+         importIssues = false;
+      }
+   }
+   else if (assetItem->statusType == String("MissingFile"))
+   {
+      //Trigger callback to look?
+   }
+}
+
+//
+// Importing
+//
+
+//new AssetImporter(AsImport);
+//AsImport.autoImportFile("data/pbr/images/greasy-pan-2-albedo.png");
+//AsImport.autoImportFile("data/pbr/shapeTest/Blockout_Primitive_Cube.fbx");
+
+
+//AsImport.setTargetPath("data/pbr/importTest/");
+//AsImport.addImportingFile("D:/Gamedev/art/Blockout/Blockout_Primitive_Cube.fbx");
+//AsImport.processImportingAssets();
+//AsImport.validateImportingAssets();
+//AsImport.importAssets();
+StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
+{
+   String assetType = getAssetTypeByFile(filePath);
+
+   if (assetType == String("Folder") || assetType == String("Zip"))
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is a folder or zip.", filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+      return StringTable->EmptyString();
+   }
+
+   if (assetType.isEmpty())
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is of an unrecognized/unsupported type.", filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+      return StringTable->EmptyString();
+   }
+
+   //Find out if the filepath has an associated module to it. If we're importing in-place, it needs to be within a module's directory
+   ModuleDefinition* targetModuleDef = getModuleFromPath(filePath);
+
+   if (targetModuleDef == nullptr)
+   {
+      //log it
+      return StringTable->EmptyString();
+   }
+   else
+   {
+      targetModuleId = targetModuleDef->getModuleId();
+   }
+
+   //set our path
+   targetPath = filePath.getPath();
+
+   //use a default import config
+   activeImportConfig = AssetImportConfig();
+
+   AssetImportObject* assetItem = addImportingAsset(assetType, filePath, nullptr, "");
+
+   importingAssets.push_back(assetItem);
+
+   processImportAssets();
+
+   bool hasIssues = validateAssets();
+
+   if (hasIssues)
+   {
+      //log it
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Import process has failed due to issues discovered during validation!");
+      activityLog.push_back(importLogBuffer);
+   }
+   else
+   {
+      importAssets();
+   }
+
+#if TORQUE_DEBUG
+   for (U32 i = 0; i < activityLog.size(); i++)
+   {
+      Con::printf(activityLog[i].c_str());
+   }
+#endif
+}
+
+void AssetImporter::importAssets(AssetImportObject* assetItem)
+{
+   ModuleDefinition* moduleDef = ModuleDatabase.findModule(targetModuleId.c_str(), 1);
+
+   if (moduleDef == nullptr)
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "AssetImporter::importAssets - Unable to find moduleId %s", targetModuleId.c_str());
+      activityLog.push_back(importLogBuffer);
+      return;
+   }
+
+   if (assetItem == nullptr)
+   {
+      for (U32 i = 0; i < importingAssets.size(); i++)
+      {
+         if (importingAssets[i]->skip)
+            continue;
+
+         Torque::Path assetPath;
+         if (importingAssets[i]->assetType == String("ImageAsset"))
+            assetPath = importImageAsset(importingAssets[i]);
+         else if (importingAssets[i]->assetType == String("ShapeAsset"))
+            assetPath = importShapeAsset(importingAssets[i]);
+         else if (importingAssets[i]->assetType == String("SoundAsset"))
+            assetPath = SoundAsset::importAsset(importingAssets[i]);
+         else if (importingAssets[i]->assetType == String("MaterialAsset"))
+            assetPath = importMaterialAsset(importingAssets[i]);
+         else if (importingAssets[i]->assetType == String("ShapeAnimationAsset"))
+            assetPath = ShapeAnimationAsset::importAsset(importingAssets[i]);
+
+         //If we got a valid filepath back from the import action, then we know we're good to go and we can go ahead and register the asset!
+         if (!assetPath.isEmpty() && !isReimport)
+         {
+            bool registerSuccess = AssetDatabase.addDeclaredAsset(moduleDef, assetPath.getFullPath().c_str());
+
+            if (!registerSuccess)
+            {
+               dSprintf(importLogBuffer, sizeof(importLogBuffer), "AssetImporter::importAssets - Failed to successfully register new asset at path %s to moduleId %s", assetPath.getFullPath().c_str(), targetModuleId.c_str());
+               activityLog.push_back(importLogBuffer);
+            }
+         }
+
+         //recurse if needed
+         importAssets(importingAssets[i]);
+      }
+   }
+   else
+   {
+      //this is the child recursing section
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+         if (childItem->skip)
+            continue;
+
+         Torque::Path assetPath;
+         if (childItem->assetType == String("ImageAsset"))
+            assetPath = importImageAsset(childItem);
+         else if (childItem->assetType == String("ShapeAsset"))
+            assetPath = importShapeAsset(childItem);
+         else if (childItem->assetType == String("SoundAsset"))
+            assetPath = SoundAsset::importAsset(childItem);
+         else if (childItem->assetType == String("MaterialAsset"))
+            assetPath = importMaterialAsset(childItem);
+         else if (childItem->assetType == String("ShapeAnimationAsset"))
+            assetPath = ShapeAnimationAsset::importAsset(childItem);
+
+         //If we got a valid filepath back from the import action, then we know we're good to go and we can go ahead and register the asset!
+         if (!assetPath.isEmpty() && !isReimport)
+         {
+            bool registerSuccess = AssetDatabase.addDeclaredAsset(moduleDef, assetPath.getFullPath().c_str());
+
+            if (!registerSuccess)
+            {
+               dSprintf(importLogBuffer, sizeof(importLogBuffer), "AssetImporter::importAssets - Failed to successfully register new asset at path %s to moduleId %s", assetPath.getFullPath().c_str(), targetModuleId.c_str());
+               activityLog.push_back(importLogBuffer);
+            }
+         }
+
+         //recurse if needed
+         importAssets(childItem);
+      }
+   }
+}
+
+//
+// Type-specific import logic
+//
+
+Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Beginning importing of Image Asset: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   ImageAsset* newAsset = new ImageAsset();
+   newAsset->registerObject();
+
+   StringTableEntry assetName = StringTable->insert(assetItem->assetName.c_str());
+
+   String imageFileName = assetItem->filePath.getFileName() + "." + assetItem->filePath.getExtension();
+   String assetPath = targetPath + "/" + imageFileName;
+   String tamlPath = targetPath + "/" + assetName + ".asset.taml";
+   String originalPath = assetItem->filePath.getFullPath().c_str();
+
+   char qualifiedFromFile[2048];
+   char qualifiedToFile[2048];
+
+   Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
+   Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
+   
+   newAsset->setAssetName(assetName);
+   newAsset->setImageFileName(imageFileName.c_str());
+   newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
+
+   ImageAsset::ImageTypes imageType = ImageAsset::getImageTypeFromName(assetItem->imageSuffixType.c_str());
+   newAsset->setImageType(imageType);
+
+   Taml tamlWriter;
+   bool importSuccessful = tamlWriter.write(newAsset, tamlPath.c_str());
+
+   if (!importSuccessful)
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to write asset taml file %s", tamlPath.c_str());
+      activityLog.push_back(importLogBuffer);
+      return "";
+   }
+
+   if (!isReimport)
+   {
+      bool isInPlace = !dStrcmp(qualifiedFromFile, qualifiedToFile);
+
+      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      {
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
+         activityLog.push_back(importLogBuffer);
+         return "";
+      }
+   }
+
+   return tamlPath;
+}
+
+Torque::Path AssetImporter::importMaterialAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Beginning importing of Material Asset: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   MaterialAsset* newAsset = new MaterialAsset();
+   newAsset->registerObject();
+
+   StringTableEntry assetName = StringTable->insert(assetItem->assetName.c_str());
+
+   String tamlPath = targetPath + "/" + assetName + ".asset.taml";
+   String scriptName = assetItem->assetName + ".cs";
+   String scriptPath = targetPath + "/" + scriptName;
+   String originalPath = assetItem->filePath.getFullPath().c_str();
+
+   char qualifiedFromFile[2048];
+
+   Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
+
+   newAsset->setAssetName(assetName);
+   newAsset->setScriptFile(scriptName.c_str());
+   newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
+
+   //iterate through and write out the material maps dependencies
+   S32 dependencySlotId = 0;
+   for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+   {
+      AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+      if (childItem->skip || !childItem->processed || childItem->assetType.compare("ImageAsset") != 0)
+         continue;
+
+      char dependencyFieldName[64];
+      dSprintf(dependencyFieldName, 64, "imageMap%i", dependencySlotId);
+
+      char dependencyFieldDef[512];
+      dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+
+      newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
+
+      dependencySlotId++;
+   }
+
+   Taml tamlWriter;
+   bool importSuccessful = tamlWriter.write(newAsset, tamlPath.c_str());
+
+   if (!importSuccessful)
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to write asset taml file %s", tamlPath.c_str());
+      activityLog.push_back(importLogBuffer);
+      return "";
+   }
+
+   //build the PBRConfig file if we're flagged to and have valid image maps
+   if (activeImportConfig.CreatePBRConfig)
+   {
+      AssetImportObject* pbrConfigMap = nullptr;
+      AssetImportObject* roughnessMap = nullptr;
+      AssetImportObject* metalnessMap = nullptr;
+      AssetImportObject* aoMap = nullptr;
+
+      //We need to find any/all respective image maps for the given channels
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+         if (childItem->skip || childItem->assetType.compare("ImageAsset") != 0)
+            continue;
+
+         if (childItem->imageSuffixType.compare("PBRConfig") == 0)
+            pbrConfigMap = childItem;
+         else if(childItem->imageSuffixType.compare("Roughness") == 0)
+            roughnessMap = childItem;
+         else if (childItem->imageSuffixType.compare("Metalness") == 0)
+            metalnessMap = childItem;
+         else if (childItem->imageSuffixType.compare("AO") == 0)
+            aoMap = childItem;
+      }
+
+      if (pbrConfigMap != nullptr && pbrConfigMap->generatedAsset)
+      {
+         if (roughnessMap != nullptr || metalnessMap != nullptr || aoMap != nullptr)
+         {
+            U32 channelKey[4] = { 0,1,2,3 };
+
+            GFX->getTextureManager()->saveCompositeTexture(aoMap->filePath.getFullPath(), roughnessMap->filePath.getFullPath(), metalnessMap->filePath.getFullPath(), "",
+               channelKey, pbrConfigMap->filePath.getFullPath(), &GFXTexturePersistentProfile);
+         }
+      }
+   }
+
+   FileObject* file = new FileObject();
+   file->registerObject();
+
+   //Now write the script file containing our material out
+   if (file->openForWrite(scriptPath.c_str()))
+   {
+      file->writeLine((U8*)"//--- OBJECT WRITE BEGIN ---");
+
+      char lineBuffer[1024];
+      dSprintf(lineBuffer, 1024, "singleton Material(%s) {", assetName);
+      file->writeLine((U8*)lineBuffer);
+
+      dSprintf(lineBuffer, 1024, "   mapTo=\"%s\";", assetName);
+      file->writeLine((U8*)lineBuffer);
+
+      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      {
+         AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+         if (childItem->skip || !childItem->processed || childItem->assetType.compare("ImageAsset") != 0)
+            continue;
+
+         String mapFieldName = "";
+
+         if (childItem->imageSuffixType.compare("Albedo") == 0)
+         {
+            mapFieldName = "DiffuseMap";
+         }
+
+         String path = childItem->filePath.getFullFileName();
+         dSprintf(lineBuffer, 1024, "   %s[0] = \"%s\";", mapFieldName.c_str(), path.c_str());
+         file->writeLine((U8*)lineBuffer);
+
+         dSprintf(lineBuffer, 1024, "   %sAsset[0] = \"%s:%s\";", mapFieldName.c_str(), targetModuleId.c_str(), childItem->assetName.c_str());
+         file->writeLine((U8*)lineBuffer);
+      }
+
+      file->writeLine((U8*)"};");
+      file->writeLine((U8*)"//--- OBJECT WRITE END ---");
+
+      file->close();
+   }
+   else
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to write asset script file %s", scriptPath.c_str());
+      activityLog.push_back(importLogBuffer);
+      return "";
+   }
+
+   return tamlPath;
+}
+
+Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
+{
+   dSprintf(importLogBuffer, sizeof(importLogBuffer), "Beginning importing of Shape Asset: %s", assetItem->assetName.c_str());
+   activityLog.push_back(importLogBuffer);
+
+   ShapeAsset* newAsset = new ShapeAsset();
+   newAsset->registerObject();
+
+   StringTableEntry assetName = StringTable->insert(assetItem->assetName.c_str());
+
+   String shapeFileName = assetItem->filePath.getFileName() + "." + assetItem->filePath.getExtension();
+   String assetPath = targetPath + "/" + shapeFileName;
+   String constructorPath = targetPath + "/" + assetItem->filePath.getFileName() + ".cs";
+   String tamlPath = targetPath + "/" + assetName + ".asset.taml";
+   String originalPath = assetItem->filePath.getFullPath().c_str();
+
+   char qualifiedFromFile[2048];
+   char qualifiedToFile[2048];
+
+   Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
+   Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
+
+   newAsset->setAssetName(assetName);
+   newAsset->setShapeFile(shapeFileName.c_str());
+   newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
+
+   //iterate through and write out the material maps dependencies
+   S32 dependencySlotId = 0;
+   for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+   {
+      AssetImportObject* childItem = assetItem->childAssetItems[i];
+
+      if (childItem->skip || !childItem->processed)
+         continue;
+
+      if (childItem->assetType.compare("MaterialAsset") == 0)
+      {
+         char dependencyFieldName[64];
+         dSprintf(dependencyFieldName, 64, "materialSlot%i", dependencySlotId);
+
+         char dependencyFieldDef[512];
+         dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+  
+         newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
+
+         dependencySlotId++;
+      }
+      else if (childItem->assetType.compare("ShapeAnimationAsset") == 0)
+      {
+         char dependencyFieldName[64];
+         dSprintf(dependencyFieldName, 64, "animationSequence%i", dependencySlotId);
+
+         char dependencyFieldDef[512];
+         dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+
+         newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
+
+         dependencySlotId++;
+      }
+   }
+
+   Taml tamlWriter;
+   bool importSuccessful = tamlWriter.write(newAsset, tamlPath.c_str());
+
+   if (!importSuccessful)
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to write asset taml file %s", tamlPath.c_str());
+      activityLog.push_back(importLogBuffer);
+      return "";
+   }
+
+   if (!isReimport)
+   {
+      bool isInPlace = !dStrcmp(qualifiedFromFile, qualifiedToFile);
+
+      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      {
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
+         activityLog.push_back(importLogBuffer);
+         return "";
+      }
+   }
+
+   //find/create shape constructor
+   TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructor(Torque::Path(qualifiedToFile).getFullPath());
+   if (constructor == nullptr)
+   {
+      constructor = new TSShapeConstructor(qualifiedToFile);
+
+      String constructorName = assetItem->filePath.getFileName() + "_" + assetItem->filePath.getExtension().substr(0, 3);
+      constructorName.replace("-", "_");
+      constructorName.replace(".", "_");
+      constructorName = Sim::getUniqueName(constructorName.c_str());
+      constructor->registerObject(constructorName.c_str());
+   }
+
+   //now we write the import config logic into the constructor itself to ensure we load like we wanted it to
+   String neverImportMats;
+
+   if (activeImportConfig.IgnoreMaterials.isNotEmpty())
+   {
+      U32 ignoredMatNamesCount = StringUnit::getUnitCount(activeImportConfig.IgnoreMaterials, ",;");
+      for (U32 i = 0; i < ignoredMatNamesCount;  i++)
+      {
+         if (i == 0)
+            neverImportMats = StringUnit::getUnit(activeImportConfig.IgnoreMaterials, i, ",;");
+         else
+            neverImportMats += String("\t") + StringUnit::getUnit(activeImportConfig.IgnoreMaterials, i, ",;");
+      }
+   }
+
+   if (activeImportConfig.DoUpAxisOverride)
+   {
+      S32 upAxis;
+      if (activeImportConfig.UpAxisOverride.compare("X_AXIS") == 0)
+      {
+         upAxis = domUpAxisType::UPAXISTYPE_X_UP;
+      }
+      else if(activeImportConfig.UpAxisOverride.compare("Y_AXIS") == 0)
+      {
+         upAxis = domUpAxisType::UPAXISTYPE_Y_UP;
+      }
+      else if(activeImportConfig.UpAxisOverride.compare("Z_AXIS") == 0)
+      {
+         upAxis = domUpAxisType::UPAXISTYPE_Z_UP;
+      }
+      constructor->mOptions.upAxis = (domUpAxisType)upAxis;
+   }
+
+   if (activeImportConfig.DoScaleOverride)
+      constructor->mOptions.unit = activeImportConfig.ScaleOverride;
+   else
+      constructor->mOptions.unit = -1;
+
+   enum eAnimTimingType
+   {
+      FrameCount = 0,
+      Seconds = 1,
+      Milliseconds = 1000
+   };
+
+   S32 lodType;
+   if (activeImportConfig.LODType.compare("TrailingNumber") == 0)
+      lodType = ColladaUtils::ImportOptions::eLodType::TrailingNumber;
+   else if (activeImportConfig.LODType.compare("SingleSize") == 0)
+      lodType = ColladaUtils::ImportOptions::eLodType::SingleSize;
+   else if (activeImportConfig.LODType.compare("DetectDTS") == 0)
+      lodType = ColladaUtils::ImportOptions::eLodType::DetectDTS;
+   constructor->mOptions.lodType = (ColladaUtils::ImportOptions::eLodType)lodType;
+
+   constructor->mOptions.singleDetailSize = activeImportConfig.convertLeftHanded;
+   constructor->mOptions.alwaysImport = activeImportConfig.ImportedNodes;
+   constructor->mOptions.neverImport = activeImportConfig.IgnoreNodes;
+   constructor->mOptions.alwaysImportMesh = activeImportConfig.ImportMeshes;
+   constructor->mOptions.neverImportMesh = activeImportConfig.IgnoreMeshes;
+   constructor->mOptions.ignoreNodeScale = activeImportConfig.IgnoreNodeScale;
+   constructor->mOptions.adjustCenter = activeImportConfig.AdjustCenter;
+   constructor->mOptions.adjustFloor = activeImportConfig.AdjustFloor;
+
+   constructor->mOptions.convertLeftHanded = activeImportConfig.convertLeftHanded;
+   constructor->mOptions.calcTangentSpace = activeImportConfig.calcTangentSpace;
+   constructor->mOptions.genUVCoords = activeImportConfig.genUVCoords;
+   constructor->mOptions.flipUVCoords = activeImportConfig.flipUVCoords;
+   constructor->mOptions.findInstances = activeImportConfig.findInstances;
+   constructor->mOptions.limitBoneWeights = activeImportConfig.limitBoneWeights;
+   constructor->mOptions.joinIdenticalVerts = activeImportConfig.JoinIdenticalVerts;
+   constructor->mOptions.reverseWindingOrder = activeImportConfig.reverseWindingOrder;
+   constructor->mOptions.invertNormals = activeImportConfig.invertNormals;
+   constructor->mOptions.removeRedundantMats = activeImportConfig.removeRedundantMats;
+
+   S32 animTimingType;
+   if (activeImportConfig.animTiming.compare("FrameCount") == 0)
+      animTimingType = ColladaUtils::ImportOptions::eAnimTimingType::FrameCount;
+   else if (activeImportConfig.animTiming.compare("Seconds") == 0)
+      animTimingType = ColladaUtils::ImportOptions::eAnimTimingType::Seconds;
+   else if (activeImportConfig.animTiming.compare("Milliseconds") == 0)
+      animTimingType = ColladaUtils::ImportOptions::eAnimTimingType::Milliseconds;
+   constructor->mOptions.animTiming = (ColladaUtils::ImportOptions::eAnimTimingType)animTimingType;
+
+   constructor->mOptions.animFPS = activeImportConfig.animFPS;
+
+   constructor->mOptions.neverImportMat = neverImportMats;
+
+   if (!constructor->save(constructorPath.c_str()))
+   {
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Failed to save shape constructor file to %s", constructorPath.c_str());
+      activityLog.push_back(importLogBuffer);
+   }
+
+   return tamlPath;
+}

+ 680 - 0
Engine/source/T3D/assets/assetImporter.h

@@ -0,0 +1,680 @@
+#pragma once
+
+#include "assets/assetPtr.h"
+#include "assets/assetManager.h"
+#include "module/moduleManager.h"
+#include "util/settings.h"
+#include "gui\controls\guiTreeViewCtrl.h"
+
+/// <summary>
+/// AssetImportConfig is a SimObject derived object intended to act as a container for all the necessary configuration data when running the Asset Importer.
+/// It dictates if and how any given asset type will be processed when running an import action. This is because the Asset Importer utilizes a lot of informed logic
+/// to try and automate as much of the import process as possible. In theory, you would run the import on a given file, and based on your config the importer will do
+/// everything from importing the designated file, as well as finding and importing any associated files such as images or materials, and prepping the objects at time
+/// of import to avoid as much manual post-processing as possible.
+/// </summary>
+class AssetImportConfig : public SimObject
+{
+   //General Settings
+public:
+   /// <summary>
+   /// Duplicate Asset Auto-Resolution Action. Options are None, AutoPrune, AutoRename
+   /// </summary>
+   String DuplicatAutoResolution;
+
+   /// <summary>
+   /// Indicates if warnings should be treated as errors.
+   /// </summary>
+   bool WarningsAsErrors;
+
+   /// <summary>
+   /// Indicates if importing should be prevented from completing if any errors are detected at all
+   /// </summary>
+   bool PreventImportWithErrors;
+
+   /// <summary>
+   /// Should the importer automatically prompt to find missing files if they are not detected automatically by the importer
+   /// </summary>
+   bool AutomaticallyPromptMissingFiles;
+   //
+
+   //
+   //Mesh Settings
+   /// <summary>
+   /// Indicates if this config supports importing meshes
+   /// </summary>
+   bool ImportMesh;
+
+   /// <summary>
+   /// Indicates if the up axis in the model file should be overridden 
+   /// </summary>
+   bool DoUpAxisOverride;
+
+   /// <summary>
+   /// If overriding, what axis should be used as up. Options are X_AXIS, Y_AXIS, Z_AXIS
+   /// </summary>
+   String UpAxisOverride;
+
+   /// <summary>
+   /// Indicates if the scale in the model file should be overridden 
+   /// </summary>
+   bool DoScaleOverride;
+
+   /// <summary>
+   /// If overriding, what scale should be used
+   /// </summary>
+   F32 ScaleOverride;
+
+   /// <summary>
+   /// Indicates if scale of nodes should be ignored
+   /// </summary>
+   bool IgnoreNodeScale;
+
+   /// <summary>
+   /// Indicates if the center of the model file should be automatically recentered
+   /// </summary>
+   bool AdjustCenter;
+
+   /// <summary>
+   /// Indicates if the floor height of the model file should be automatically zero'd
+   /// </summary>
+   bool AdjustFloor;
+
+   /// <summary>
+   /// Indicates if submeshes should be collapsed down into a single main mesh
+   /// </summary>
+   bool CollapseSubmeshes;
+
+   /// <summary>
+   /// Indicates what LOD mode the model file should utilize to process out LODs. Options are TrailingNumber, DetectDTS, SingleSize
+   /// </summary>
+   String LODType;
+
+   //ImportAssetConfigSettingsList.addNewConfigSetting("TrailingNumber", "Trailing Number", "float", "", "2", "", "Mesh");
+   /// <summary>
+   /// A list of what nodes should be guaranteed to be imported if found in the model file. Separated by either , or ;
+   /// </summary>
+   String ImportedNodes;
+
+   /// <summary>
+   /// A list of what nodes should be guaranteed to not be imported if found in the model file. Separated by either , or ;
+   /// </summary>
+   String IgnoreNodes;
+
+   /// <summary>
+   /// A list of what mesh objects should be guaranteed to be imported if found in the model file. Separated by either , or ;
+   /// </summary>
+   String ImportMeshes;
+
+   /// <summary>
+   /// A list of what mesh objects should be guaranteed to not be imported if found in the model file. Separated by either , or ;
+   /// </summary>
+   String IgnoreMeshes;
+
+   //Assimp/Collada params
+   /// <summary>
+   /// Flag to indicate the shape loader should convert to a left-handed coordinate system
+   /// </summary>
+   bool convertLeftHanded;
+
+   /// <summary>
+   /// Should the shape loader calculate tangent space values
+   /// </summary>
+   bool calcTangentSpace;
+
+   /// <summary>
+   /// Should the shape loader automatically prune redundant/duplicate materials
+   /// </summary>
+   bool removeRedundantMats;
+
+   /// <summary>
+   /// Should the shape loader auto-generate UV Coordinates for the mesh.
+   /// </summary>
+   bool genUVCoords;
+
+   /// <summary>
+   /// Should the UV coordinates be transformed.
+   /// </summary>
+   bool TransformUVs;
+
+   /// <summary>
+   /// Should the UV coordinates be flipped
+   /// </summary>
+   bool flipUVCoords;
+
+   /// <summary>
+   /// Should the shape loader automatically look for instanced submeshes in the model file
+   /// </summary>
+   bool findInstances;
+
+   /// <summary>
+   /// Should the shape loader limit the bone weights
+   /// </summary>
+   bool limitBoneWeights;
+
+   /// <summary>
+   /// Should the shape loader automatically merge identical/duplicate verts
+   /// </summary>
+   bool JoinIdenticalVerts;
+
+   /// <summary>
+   /// Should the shape loader reverse the winding order of the mesh's face indicies
+   /// </summary>
+   bool reverseWindingOrder;
+
+   /// <summary>
+   /// Should the normals on the model be inverted
+   /// </summary>
+   bool invertNormals;
+   //
+
+   //
+   //Materials
+   /// <summary>
+   /// Does this config allow for importing of materials
+   /// </summary>
+   bool ImportMaterials;
+
+   /// <summary>
+   /// When importing a material, should it automatically attempt to merge Roughness, AO and Metalness maps into a single, composited PBR Configuration map
+   /// </summary>
+   bool CreatePBRConfig;
+
+   /// <summary>
+   /// When generating a material off of an importing image, should the importer force appending a diffusemap suffix onto the end to avoid potential naming confusion.
+   /// e.g. MyCoolStuff.png is imported, generating MyCoolStuff material asset and MyCoolStuff_Diffuse image asset
+   /// </summary>
+   bool UseDiffuseSuffixOnOriginImage;
+
+   /// <summary>
+   /// Should the importer try and use existing material assets in the game directory if at all possible. (Not currently utilized)
+   /// </summary>
+   bool UseExistingMaterials;
+
+   /// <summary>
+   /// A list of material names that should not be imported. Separated by either , or ;
+   /// </summary>
+   String IgnoreMaterials;
+
+   /// <summary>
+   /// When processing a material asset, should the importer attempt to populate the various material maps on it by looking up common naming conventions for potentially relevent image files
+   /// e.g. If MyCoolStuff_Diffuse.png is imported, generating MyCoolStuff material, it would also find MyCoolStuff_Normal and MyCoolStuff_PBR images and map them to the normal and PBRConfig maps respectively automatically
+   /// </summary>
+   bool PopulateMaterialMaps;
+
+   //
+   //Animations
+   /// <summary>
+   /// Does this config allow for importing Shape Animations
+   /// </summary>
+   bool ImportAnimations;
+
+   /// <summary>
+   /// When importing a shape file, should the animations within be separated out into unique files
+   /// </summary>
+   bool SeparateAnimations;
+
+   /// <summary>
+   /// If separating animations out from a source file, what prefix should be added to the names for grouping association
+   /// </summary>
+   String SeparateAnimationPrefix;
+
+   /// <summary>
+   /// Defines the animation timing for the given animation sequence. Options are FrameTime, Seconds, Milliseconds
+   /// </summary>
+   String animTiming;
+
+   /// <summary>
+   /// The FPS of the animation sequence
+   /// </summary>
+   F32 animFPS;
+
+   //
+   //Collision
+   /// <summary>
+   /// Does this configuration generate collision geometry when importing. (Not currently enabled)
+   /// </summary>
+   bool GenerateCollisions;
+
+   /// <summary>
+   /// What sort of collision geometry is generated. (Not currently enabled)
+   /// </summary>
+   String GenCollisionType;
+
+   /// <summary>
+   /// What prefix is added to the collision geometry generated. (Not currently enabled)
+   /// </summary>
+   String CollisionMeshPrefix;
+
+   /// <summary>
+   /// Does this configuration generate Line of Sight collision geometry. (Not currently enabled)
+   /// </summary>
+   bool  GenerateLOSCollisions;
+
+   /// <summary>
+   /// What sort of Line of Sight collision geometry is generated. (Not currently enabled)
+   /// </summary>
+   String GenLOSCollisionType;
+
+   /// <summary>
+   /// What prefix is added to the Line of Sight collision geometry generated. (Not currently enabled)
+   /// </summary>
+   String LOSCollisionMeshPrefix;
+
+   //
+   //Images
+   /// <summary>
+   /// Does this configuration support importing images.
+   /// </summary>
+   bool importImages;
+
+   /// <summary>
+   /// What is the default ImageType images are imported as. Options are: N/A, Diffuse, Normal, Metalness, Roughness, AO, PBRConfig, GUI, Cubemap
+   /// </summary>
+   String ImageType;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a diffuse map.
+   /// e.g. _Albedo or _Color
+   /// </summary>
+   String DiffuseTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a normal map.
+   /// e.g. _Normal or _Norm
+   /// </summary>
+   String NormalTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a metalness map.
+   /// e.g. _Metalness or _Metal
+   /// </summary>
+   String MetalnessTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a roughness map.
+   /// e.g. _roughness or _rough
+   /// </summary>
+   String RoughnessTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a smoothness map.
+   /// e.g. _smoothness or _smooth
+   /// </summary>
+   String SmoothnessTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a ambient occlusion map.
+   /// e.g. _ambient or _ao
+   /// </summary>
+   String AOTypeSuffixes;
+
+   /// <summary>
+   /// What type of suffixes are scanned to detect if an importing image is a PBRConfig map.
+   /// e.g. _Composite or _PBR
+   /// </summary>
+   String PBRTypeSuffixes;
+
+   /// <summary>
+   /// Indicates what filter mode images imported with this configuration utilizes. Options are Linear, Bilinear, Trilinear
+   /// </summary>
+   String TextureFilteringMode;
+
+   /// <summary>
+   /// Indicates if images imported with this configuration utilize mipmaps
+   /// </summary>
+   bool UseMips;
+
+   /// <summary>
+   /// Indicates if images imported with this configuration are in an HDR format
+   /// </summary>
+   bool IsHDR;
+
+   /// <summary>
+   /// Indicates what amount of scaling images imported with this configuration use
+   /// </summary>
+   F32 Scaling;
+
+   /// <summary>
+   /// Indicates if images imported with this configuration are compressed
+   /// </summary>
+   bool Compressed;
+
+   /// <summary>
+   /// Indicates if images imported with this configuration generate a parent material for it as well
+   /// </summary>
+   bool GenerateMaterialOnImport;
+
+   //
+   //Sounds
+   /// <summary>
+   /// Indicates if sounds are imported with this configuration
+   /// </summary>
+   bool importSounds;
+
+   /// <summary>
+   /// Indicates what amount the volume is adjusted on sounds imported with this configuration
+   /// </summary>
+   F32 VolumeAdjust;
+
+   /// <summary>
+   /// Indicates what amount the pitch is adjusted on sounds imported with this configuration
+   /// </summary>
+   F32 PitchAdjust;
+
+   /// <summary>
+   /// Indicates if sounds imported with this configuration are compressed
+   /// </summary>
+   bool Compressed;
+
+public:
+   AssetImportConfig();
+   virtual ~AssetImportConfig();
+
+   /// Engine.
+   static void initPersistFields();
+
+   /// <summary>
+   /// Loads a configuration from a Settings object
+   /// @param configSettings, The Settings object to load from
+   /// @param configName, The name of the configuration setting to load from the setting object
+   /// </summary>
+   void loadImportConfig(Settings* configSettings, String configName);
+
+   /// Declare Console Object.
+   DECLARE_CONOBJECT(AssetImportConfig);
+};
+
+/// <summary>
+/// AssetImportConfig is a SimObject derived object that represents and holds information for an importing asset. They are generated and processed by the AssetImporter
+/// </summary>
+class AssetImportObject : public SimObject
+{
+   typedef SimObject Parent;
+
+public:
+   /// <summary>
+   /// What type is the importing asset
+   /// </summary>
+   String assetType;
+
+   /// <summary>
+   /// What is the source file path of the importing asset
+   /// </summary>
+   Torque::Path filePath;
+
+   /// <summary>
+   /// What is the asset's name
+   /// </summary>
+   String assetName;
+
+   /// <summary>
+   /// What is the original, unmodified by processing, asset name
+   /// </summary>
+   String cleanAssetName;
+
+   /// <summary>
+   /// What is the name of the module this asset will be importing into
+   /// </summary>
+   String moduleName;
+
+   /// <summary>
+   /// What is the current status of this asset item in it's import process
+   /// </summary>
+   String status;
+
+   /// <summary>
+   /// If there is a warning or error status, what type is the condition for this asset item
+   /// </summary>
+   String statusType;
+
+   /// <summary>
+   /// What is the articulated information of the status of the asset. Contains the error or warning log data.
+   /// </summary>
+   String statusInfo;
+
+   /// <summary>
+   /// Is the asset item currently flagged as dirty
+   /// </summary>
+   bool dirty;
+
+   /// <summary>
+   /// Is this asset item marked to be skipped. If it is, it's usually due to being marked as deleted
+   /// </summary>
+   bool skip;
+
+   /// <summary>
+   /// Has the asset item been processed
+   /// </summary>
+   bool processed;
+
+   /// <summary>
+   /// Is this specific asset item generated as part of the import process of another item
+   /// </summary>
+   bool generatedAsset;
+
+   /// <summary>
+   /// What, if any, importing asset item is this item's parent
+   /// </summary>
+   AssetImportObject* parentAssetItem;
+
+   /// <summary>
+   /// What, if any, importing asset item are children of this item
+   /// </summary>
+   Vector< AssetImportObject*> childAssetItems;
+
+   /// <summary>
+   /// What is the ultimate asset taml file path for this import item
+   /// </summary>
+   String tamlFilePath;
+
+   //
+   /// <summary>
+   /// Specific to ImageAsset type
+   /// What is the image asset's suffix type. Options are: Albedo, Normal, Roughness, AO, Metalness, PBRConfig
+   /// </summary>
+   String imageSuffixType;
+
+   //
+   /// <summary>
+   /// Specific to ShapeAsset type
+   /// Processed information about the shape file. Contains numbers and lists of meshes, materials and animations
+   /// </summary>
+   GuiTreeViewCtrl* shapeInfo;
+
+public:
+   AssetImportObject();
+   virtual ~AssetImportObject();
+
+   /// Engine.
+   static void initPersistFields();
+
+   /// Declare Console Object.
+   DECLARE_CONOBJECT(AssetImportObject);
+};
+
+/// <summary>
+/// AssetImporter is a SimObject derived object that processed and imports files and turns them into assets if they are of valid types.
+/// Utilizes an AssetImportConfig to inform the importing process's behavior.
+/// </summary>
+class AssetImporter : public SimObject
+{
+   typedef SimObject Parent;
+
+   /// <summary>
+   /// The import configuration that is currently being utilized
+   /// </summary>
+   AssetImportConfig activeImportConfig;
+
+   /// <summary>
+   /// A log of all the actions that have been performed by the importer
+   /// </summary>
+   Vector<String> activityLog;
+
+   /// <summary>
+   /// A list of AssetImportObjects that are to be imported
+   /// </summary>
+   Vector<AssetImportObject*> importingAssets;
+
+   /// <summary>
+   /// A list of AssetImportObjects that are to be imported. These are unmodified by anything in the importing session, and are only used for resetting purposes;
+   /// </summary>
+   Vector<Torque::Path> originalImportingAssets;
+
+   /// <summary>
+   /// The Id of the module the assets are to be imported into
+   /// </summary>
+   String targetModuleId;
+
+   /// <summary>
+   /// The path any imported assets are placed in as their destination
+   /// </summary>
+   String targetPath;
+
+   /// <summary>
+   /// Are there any issues with any of the current import asset items
+   /// </summary>
+   bool importIssues;
+
+   /// <summary>
+   /// Is this import action a reimport of an existing asset
+   /// </summary>
+   bool isReimport;
+
+   /// <summary>
+   /// Has the heirarchy of asset import items changed due to processing
+   /// </summary>
+   bool assetHeirarchyChanged;
+
+   /// <summary>
+   /// A string used for writing into the importLog
+   /// </summary>
+   char importLogBuffer[1024];
+
+public:
+   AssetImporter();
+   virtual ~AssetImporter();
+
+   /// Engine.
+   static void initPersistFields();
+
+   /// Declare Console Object.
+   DECLARE_CONOBJECT(AssetImporter);
+
+   /// <summary>
+   /// Sets the target path for the assets being imported to be deposited into
+   /// <para>@param pTargetPath, The filePath of the destination point assets are imported into</para>
+   /// </summary>
+   void setTargetPath(Torque::Path pTargetPath) { targetPath = pTargetPath; }
+
+   /// <summary>
+   /// Processes a file into an AssetImportObject and adds it to the session for importing
+   /// <para>@param filePath, The filePath of the file to be imported in as an asset</para>
+   /// <para>@return AssetImportObject that was created</para>
+   /// </summary>
+   AssetImportObject* addImportingFile(Torque::Path filePath);
+
+   /// <summary>
+   /// Adds an importing asset to the current session
+   /// <para>@param assetType, Type of the asset being imported</para>
+   /// <para>@param filePath, path of the file to be imported</para>
+   /// <para>@param parentItem, AssetImportObject that the new item is a child of. null if no parent</para>
+   /// <para>@param assetNameOverride, If not blank, will be the new item's assetName instead of being created off of the filePath</para>
+   /// <para>@return AssetImportObject that was created</para>
+   /// </summary>
+   AssetImportObject* addImportingAsset(String assetType, Torque::Path filePath, AssetImportObject* parentItem, String assetNameOverride);
+
+   /// <summary>
+   /// Deletes the asset item from the import session. Affects the item's children as well
+   /// <para>@param assetItem, asset item to be marked as deleted</para>
+   /// </summary>
+   void deleteImportingAsset(AssetImportObject* assetItem);
+
+   /// <summary>
+   /// Finds an asset item in the session if it exists, by name
+   /// <para>@param assetName, Asset name to find</para>
+   /// <para>@param assetItem, if null, will loop over and recurse the main import asset items, if a specific AssetImportObject is passed in, it will recurse it's children</para>
+   /// <para>@return AssetImportObject that was found</para>
+   /// </summary>
+   AssetImportObject* findImportingAssetByName(String assetName, AssetImportObject* assetItem = nullptr);
+
+   /// <summary>
+   /// Finds the module associated with a given file path
+   /// <para>@param filePath, File path to parse the the module from</para>
+   /// <para>@return ModuleDefinition that was found</para>
+   /// </summary>
+   ModuleDefinition* getModuleFromPath(Torque::Path filePath);
+
+   /// <summary>
+   /// Parses an asset's name to try and find if any of the import config's suffix lists match to it
+   /// <para>@param assetName, Asset name to parse any image suffix out of</para>
+   /// <para>@param suffixType, output, The suffix type that was matched to the asset name</para>
+   /// <para>@return suffix that matched to the asset name</para>
+   /// </summary>
+   String parseImageSuffixes(String assetName, String* suffixType);
+   String getAssetTypeByFile(Torque::Path filePath);
+   void resetImportSession();
+   void resetImportAsset(AssetImportObject* assetItem);
+   S32 getActivityLogLineCount();
+   String getActivityLogLine(U32 line);
+   void dumpActivityLog();
+
+   S32 getAssetItemCount();
+   AssetImportObject* getAssetItem(U32 index);
+   S32 getAssetItemChildCount(AssetImportObject* assetItem);
+   AssetImportObject* getAssetItemChild(AssetImportObject* assetItem, U32 index);
+
+   /// <summary>
+   /// Finds an asset item in the session if it exists
+   /// @param assetName, Asset name to find
+   /// @param assetItem, if null, will loop over and recurse the main import asset items, if a specific AssetImportObject is passed in, it will recurse it's children
+   /// @return AssetImportObject that was found
+   /// </summary>
+   void processImportAssets(AssetImportObject* assetItem = nullptr);
+   void processImageAsset(AssetImportObject* assetItem);
+   void processMaterialAsset(AssetImportObject* assetItem);
+   void processShapeAsset(AssetImportObject* assetItem);
+   void processShapeMaterialInfo(AssetImportObject* assetItem, S32 materialItemId);
+
+   bool validateAssets();
+   void validateAsset(AssetImportObject* assetItem);
+
+   /// <summary>
+   /// Finds an asset item in the session if it exists
+   /// @param assetName, Asset name to find
+   /// @param assetItem, if null, will loop over and recurse the main import asset items, if a specific AssetImportObject is passed in, it will recurse it's children
+   /// @return AssetImportObject that was found
+   /// </summary>
+   void resetAssetValidationStatus(AssetImportObject* assetItem = nullptr);
+
+   /// <summary>
+   /// Finds an asset item in the session if it exists
+   /// @param assetName, Asset name to find
+   /// @param assetItem, if null, will loop over and recurse the main import asset items, if a specific AssetImportObject is passed in, it will recurse it's children
+   /// @return AssetImportObject that was found
+   /// </summary>
+   bool checkAssetForCollision(AssetImportObject* assetItemToCheckFor, AssetImportObject* assetItem = nullptr);
+
+   void resolveAssetItemIssues(AssetImportObject* assetItem);
+
+   /// <summary>
+   /// Runs the import process on a single file in-place. Intended primarily for autoimporting a loose file that's in the game directory.
+   /// <para>@param filePath, The filePath of the file to be imported in as an asset</para>
+   /// <para>@return AssetId of the asset that was imported. If import failed, it will be empty.</para>
+   /// </summary>
+   StringTableEntry autoImportFile(Torque::Path filePath);
+
+   /// <summary>
+   /// Finds an asset item in the session if it exists
+   /// <para>@param assetName, Asset name to find</para>
+   /// <para>@param assetItem, if null, will loop over and recurse the main import asset items, if a specific AssetImportObject is passed in, it will recurse it's children</para>
+   /// <para>@return AssetImportObject that was found</para>
+   /// </summary>
+   void importAssets(AssetImportObject* assetItem = nullptr);
+   Torque::Path importImageAsset(AssetImportObject* assetItem);
+   Torque::Path importMaterialAsset(AssetImportObject* assetItem);
+   Torque::Path importShapeAsset(AssetImportObject* assetItem);   
+
+   //
+   AssetImportConfig* getImportConfig() { return &activeImportConfig; }
+};

+ 140 - 0
Engine/source/T3D/assets/assetImporter_ScriptBinding.h

@@ -0,0 +1,140 @@
+#pragma once
+
+#include "console/engineAPI.h"
+#include "assetImporter.h"
+
+//Console Functions
+
+DefineEngineMethod(AssetImportConfig, loadImportConfig, void, (Settings* configSettings, String configName), (nullAsType<Settings*>(), ""),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->loadImportConfig(configSettings, configName);
+}
+
+DefineEngineMethod(AssetImporter, setTargetPath, void, (String path), (""),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->setTargetPath(path);
+}
+
+DefineEngineMethod(AssetImporter, resetImportSession, void, (), ,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->resetImportSession();
+}
+
+DefineEngineMethod(AssetImporter, dumpActivityLog, void, (), ,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->dumpActivityLog();
+}
+
+DefineEngineMethod(AssetImporter, getActivityLogLineCount, S32, (),,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->getActivityLogLineCount();
+}
+
+DefineEngineMethod(AssetImporter, getActivityLogLine, String, (S32 i), (0),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->getActivityLogLine(0);
+}
+
+DefineEngineMethod(AssetImporter, autoImportFile, String, (String path), (""),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->autoImportFile(path);
+}
+
+DefineEngineMethod(AssetImporter, addImportingFile, AssetImportObject*, (String path), (""),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->addImportingFile(path);
+}
+
+DefineEngineMethod(AssetImporter, processImportingAssets, void, (), ,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->processImportAssets();
+}
+
+DefineEngineMethod(AssetImporter, validateImportingAssets, bool, (), ,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->validateAssets();
+}
+
+DefineEngineMethod(AssetImporter, resolveAssetItemIssues, void, (AssetImportObject* assetItem), (nullAsType< AssetImportObject*>()),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   object->resolveAssetItemIssues(assetItem);
+}
+
+DefineEngineMethod(AssetImporter, importAssets, void, (),,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->importAssets();
+}
+
+DefineEngineMethod(AssetImporter, getAssetItemCount, S32, (),,
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->getAssetItemCount();
+}
+
+DefineEngineMethod(AssetImporter, getAssetItem, AssetImportObject*, (S32 index), (0),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   return object->getAssetItem(index);
+}
+
+DefineEngineMethod(AssetImporter, getAssetItemChildCount, S32, (AssetImportObject* assetItem), (nullAsType< AssetImportObject*>()),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   if (assetItem == nullptr)
+      return 0;
+
+   return object->getAssetItemChildCount(assetItem);
+}
+
+DefineEngineMethod(AssetImporter, getAssetItemChild, AssetImportObject*, (AssetImportObject* assetItem, S32 index), (nullAsType< AssetImportObject*>(), 0),
+   "Creates a new script asset using the targetFilePath.\n"
+   "@return The bool result of calling exec")
+{
+   if (assetItem == nullptr)
+      return nullptr;
+
+   return object->getAssetItemChild(assetItem, index);
+}
+
+
+/*DefineEngineFunction(enumColladaForImport, bool, (const char* shapePath, const char* ctrl, bool loadCachedDts), ("", "", true),
+   "(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from "
+   "a COLLADA file and store it in a GuiTreeView control. This function is "
+   "used by the COLLADA 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 COLLADA filename\n"
+   "@param ctrl GuiTreeView control to add elements to\n"
+   "@param loadCachedDts dictates if it should try and load the cached dts file if it exists"
+   "@return true if successful, false otherwise\n"
+   "@ingroup Editors\n"
+   "@internal")
+{
+   return enumColladaForImport(shapePath, ctrl, loadCachedDts);
+}*/