From 27c5412768a6a295451313e6681e2c660804c5be Mon Sep 17 00:00:00 2001 From: OTHGMars Date: Tue, 21 May 2019 01:18:27 -0400 Subject: [PATCH] Import settings persistence Adds new settings to ColladaUtils::ImportSettings and TSShapeConstructor::ImportSettings for persistence. Shape will now be re-imported with the original settings if the source art is newer or the cached.dts file has been deleted. Fixes material transparency blend mode assignment. Adds implementation for override scale, material prefix and always/never import options. Reads and applies metadata fields for scale and up axis from formats that provide it. Eliminates the assimp.log file and redirects log messages to console.log. Verbose logging is enabled in debug builds. --- Engine/source/ts/assimp/assimpAppMaterial.cpp | 95 +++- Engine/source/ts/assimp/assimpAppMaterial.h | 4 +- Engine/source/ts/assimp/assimpAppMesh.cpp | 13 +- Engine/source/ts/assimp/assimpAppNode.cpp | 35 +- Engine/source/ts/assimp/assimpAppSequence.cpp | 7 +- Engine/source/ts/assimp/assimpShapeLoader.cpp | 456 ++++++++++++++---- Engine/source/ts/assimp/assimpShapeLoader.h | 15 + Engine/source/ts/collada/colladaUtils.h | 37 ++ Engine/source/ts/tsShapeConstruct.cpp | 52 ++ Engine/source/ts/tsShapeConstruct.h | 2 + 10 files changed, 575 insertions(+), 141 deletions(-) diff --git a/Engine/source/ts/assimp/assimpAppMaterial.cpp b/Engine/source/ts/assimp/assimpAppMaterial.cpp index 713ca9b87..474ea7c3b 100644 --- a/Engine/source/ts/assimp/assimpAppMaterial.cpp +++ b/Engine/source/ts/assimp/assimpAppMaterial.cpp @@ -27,6 +27,7 @@ #include "ts/assimp/assimpAppMesh.h" #include "materials/materialManager.h" #include "ts/tsMaterialList.h" +#include "core/stream/fileStream.h" // assimp include files. #include @@ -34,6 +35,8 @@ #include #include +U32 AssimpAppMaterial::sDefaultMatNumber = 0; + String AppMaterial::cleanString(const String& str) { String cleanStr(str); @@ -52,7 +55,8 @@ String AppMaterial::cleanString(const String& str) AssimpAppMaterial::AssimpAppMaterial(const char* matName) { - name = matName; + name = ColladaUtils::getOptions().matNamePrefix; + name += matName; // Set some defaults flags |= TSMaterialList::S_Wrap; @@ -67,9 +71,12 @@ AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) : name = matName.C_Str(); if (name.isEmpty()) { - name = cleanString(TSShapeLoader::getShapePath().getFileName());; + name = cleanString(TSShapeLoader::getShapePath().getFileName()); name += "_defMat"; + name += String::ToString("%d", sDefaultMatNumber); + sDefaultMatNumber++; } + name = ColladaUtils::getOptions().matNamePrefix + name; Con::printf("[ASSIMP] Loading Material: %s", name.c_str()); #ifdef TORQUE_DEBUG enumerateMaterialProperties(mtl); @@ -125,7 +132,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co if (dStrcmp("MASK", opacityMode.C_Str()) == 0) { translucent = true; - blendOp = Material::LerpAlpha; + blendOp = Material::None; float cutoff; if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff)) @@ -134,10 +141,16 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co mat->mAlphaTest = true; } } - else if (dStrcmp("OPAQUE", opacityMode.C_Str()) != 0) + else if (dStrcmp("BLEND", opacityMode.C_Str()) == 0) { translucent = true; blendOp = Material::LerpAlpha; + mat->mAlphaTest = false; + } + else + { // OPAQUE + translucent = false; + blendOp = Material::LerpAlpha; // Make default so it doesn't get written to materials.cs } } } @@ -157,14 +170,14 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co { torquePath = texName.C_Str(); if (!torquePath.isEmpty()) - mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile); + mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); } if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName)) { torquePath = texName.C_Str(); if (!torquePath.isEmpty()) - mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile); + mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); } #ifdef TORQUE_PBR_MATERIALS @@ -177,27 +190,25 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName)) rmName = texName.C_Str(); - //if (aoName.isNotEmpty() && (aoName == rmName)) - // mat->mOrmMapFilename[0] = cleanTextureName(aoName, cleanFile); // It's an ORM map - //else if (aoName.isNotEmpty() || rmName.isNotEmpty()) + mat->mIsSRGb[0] = true; if (aoName.isNotEmpty() || rmName.isNotEmpty()) { // If we have either map, fill all three slots if (rmName.isNotEmpty()) { - mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile); // Roughness + mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Roughness mat->mSmoothnessChan[0] = 1.0f; mat->mInvertSmoothness = (floatVal == 1.0f); - mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile); // Metallic + mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Metallic mat->mMetalChan[0] = 2.0f; } if (aoName.isNotEmpty()) { - mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile); // occlusion + mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile, path, false); // occlusion mat->mAOChan[0] = 0.0f; } else { - mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile); // occlusion + mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // occlusion mat->mAOChan[0] = 0.0f; } } @@ -207,7 +218,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co { torquePath = texName.C_Str(); if (!torquePath.isEmpty()) - mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile); + mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false); } LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f); @@ -234,22 +245,70 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co mat->mDoubleSided = doubleSided; } -String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName) +String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly /*= false*/) { + Torque::Path foundPath; String cleanStr; if (texName[0] == '*') - { + { // It's an embedded texture reference. Make the cached name and return cleanStr = shapeName; cleanStr += "_cachedTex"; cleanStr += texName.substr(1); + return cleanStr; + } + + // See if the file exists + bool fileFound = false; + String testPath = path.getPath(); + testPath += '/'; + testPath += texName; + testPath.replace('\\', '/'); + fileFound = Torque::FS::IsFile(testPath); + + cleanStr = texName; + cleanStr.replace('\\', '/'); + if (fileFound) + { + if (cleanStr.equal(texName)) + return cleanStr; + foundPath = testPath; } else { - cleanStr = texName; - cleanStr.replace('\\', '/'); + // See if the file is in a sub-directory of the shape + Vector foundFiles; + Torque::Path inPath(cleanStr); + String mainDotCsDir = Platform::getMainDotCsDir(); + mainDotCsDir += "/"; + S32 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir + path.getPath() + "/"), inPath.getFullFileName(), true, foundFiles); + if (results == 0 || foundFiles.size() == 0) // Not under shape directory, try the full tree + results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir), inPath.getFullFileName(), true, foundFiles); + + if (results > 0 && foundFiles.size() > 0) + { + fileFound = true; + foundPath = foundFiles[0]; + } } + if (fileFound) + { + if (nameOnly) + cleanStr = foundPath.getFullFileName(); + else + { // Unless the file is in the same directory as the materials.cs (covered above) + // we need to set the full path from the root directory. If we use "subdirectory/file.ext", + // the material manager won't find the image file, but it will be found the next time the + // material is loaded from file. If we use "./subdirectory/file.ext", the image will be found + // now, but not the next time it's loaded from file... + S32 rootLength = dStrlen(Platform::getMainDotCsDir()); + cleanStr = foundPath.getFullPathWithoutRoot().substr(rootLength-1); + } + } + else if (nameOnly) + cleanStr += " (Not Found)"; + return cleanStr; } diff --git a/Engine/source/ts/assimp/assimpAppMaterial.h b/Engine/source/ts/assimp/assimpAppMaterial.h index 64aaedf28..3c1482fa9 100644 --- a/Engine/source/ts/assimp/assimpAppMaterial.h +++ b/Engine/source/ts/assimp/assimpAppMaterial.h @@ -40,7 +40,6 @@ class AssimpAppMaterial : public AppMaterial #ifdef TORQUE_DEBUG void enumerateMaterialProperties(aiMaterial* mtl); #endif - static String cleanTextureName(String& texName, String& shapeName); public: @@ -51,6 +50,9 @@ public: String getName() const { return name; } Material* createMaterial(const Torque::Path& path) const; void initMaterial(const Torque::Path& path, Material* mat) const; + + static String cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly = false); + static U32 sDefaultMatNumber; }; #endif // _ASSIMP_APPMATERIAL_H_ diff --git a/Engine/source/ts/assimp/assimpAppMesh.cpp b/Engine/source/ts/assimp/assimpAppMesh.cpp index 3ed762e57..053d4b374 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.cpp +++ b/Engine/source/ts/assimp/assimpAppMesh.cpp @@ -79,7 +79,7 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) uvs.reserve(mMeshData->mNumVertices); normals.reserve(mMeshData->mNumVertices); - bool flipNormals = Con::getBoolVariable("$Assimp::FlipNormals", false); + bool flipNormals = ColladaUtils::getOptions().invertNormals; bool noUVFound = false; for (U32 i = 0; imNumVertices; i++) @@ -203,14 +203,17 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) MatrixF boneTransform; AssimpAppNode::assimpToTorqueMat(mMeshData->mBones[b]->mOffsetMatrix, boneTransform); Point3F boneScale = boneTransform.getScale(); - if (boneScale != Point3F::One) + Point3F bonePos = boneTransform.getPosition(); + if (boneScale != Point3F::One && ColladaUtils::getOptions().ignoreNodeScale) { Point3F scaleMult = Point3F::One / boneScale; - Point3F scalePos = boneTransform.getPosition(); boneTransform.scale(scaleMult); - scalePos /= scaleMult; - boneTransform.setPosition(scalePos); + bonePos /= scaleMult; } + + bonePos *= ColladaUtils::getOptions().unit; + boneTransform.setPosition(bonePos); + initialTransforms.push_back(boneTransform); //Weights diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index ca87f9d0c..4f95cefcb 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -91,22 +91,30 @@ MatrixF AssimpAppNode::getTransform(F32 time) else { // no parent (ie. root level) => scale by global shape mLastTransform.identity(); + mLastTransform.scale(ColladaUtils::getOptions().unit); if (!isBounds()) convertMat(mLastTransform); - - //mLastTransform.scale(ColladaUtils::getOptions().unit); } // If this node is animated in the active sequence, fetch the animated transform + MatrixF mat(true); if (sActiveSequence) - { - MatrixF mat(true); getAnimatedTransform(mat, time, sActiveSequence); - mLastTransform.mul(mat); - } else - mLastTransform.mul(mNodeTransform); - + mat = mNodeTransform; + + // Remove node scaling? + Point3F nodeScale = mat.getScale(); + if (nodeScale != Point3F::One && appParent && ColladaUtils::getOptions().ignoreNodeScale) + { + nodeScale.x = nodeScale.x ? (1.0f / nodeScale.x) : 0; + nodeScale.y = nodeScale.y ? (1.0f / nodeScale.y) : 0; + nodeScale.z = nodeScale.z ? (1.0f / nodeScale.z) : 0; + mat.scale(nodeScale); + } + + mLastTransform.mul(mat); + mLastTransformTime = time; return mLastTransform; } @@ -280,12 +288,9 @@ void AssimpAppNode::convertMat(MatrixF& outMat) { MatrixF rot(true); - // This is copied directly from ColladaUtils::convertTransform() - // ColladaUtils::getOptions().upAxis has been temporarily replaced with $Assimp::OverrideUpAxis for testing - // We need a plan for how the full set of assimp import options and settings is going to be managed. - switch (Con::getIntVariable("$Assimp::OverrideUpAxis", 2)) + switch (ColladaUtils::getOptions().upAxis) { - case 0: //UPAXISTYPE_X_UP: + case UPAXISTYPE_X_UP: // rotate 90 around Y-axis, then 90 around Z-axis rot(0, 0) = 0.0f; rot(1, 0) = 1.0f; rot(1, 1) = 0.0f; rot(2, 1) = 1.0f; @@ -295,7 +300,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat) outMat.mulL(rot); break; - case 1: //UPAXISTYPE_Y_UP: + case UPAXISTYPE_Y_UP: // rotate 180 around Y-axis, then 90 around X-axis rot(0, 0) = -1.0f; rot(1, 1) = 0.0f; rot(2, 1) = 1.0f; @@ -305,7 +310,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat) outMat.mulL(rot); break; - case 2: //UPAXISTYPE_Z_UP: + case UPAXISTYPE_Z_UP: default: // nothing to do break; diff --git a/Engine/source/ts/assimp/assimpAppSequence.cpp b/Engine/source/ts/assimp/assimpAppSequence.cpp index 65a1f4b9e..e0a78175e 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.cpp +++ b/Engine/source/ts/assimp/assimpAppSequence.cpp @@ -58,8 +58,8 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) : } } - S32 timeFactor = Con::getIntVariable("$Assimp::AnimTiming", 1); - S32 fpsRequest = Con::getIntVariable("$Assimp::AnimFPS", 30); + S32 timeFactor = ColladaUtils::getOptions().animTiming; + S32 fpsRequest = ColladaUtils::getOptions().animFPS; if (timeFactor == 0) { // Timing specified in frames fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate); @@ -70,10 +70,7 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) : else { // Timing specified in seconds or ms depending on format if (maxEndTime > 1000.0f || mAnim->mDuration > 1000.0f) - { timeFactor = 1000.0f; // If it's more than 1000 seconds, assume it's ms. - Con::setIntVariable("$Assimp::AnimTiming", 1000); - } timeFactor = mClamp(timeFactor, 1, 1000); minFrameTime /= (F32)timeFactor; diff --git a/Engine/source/ts/assimp/assimpShapeLoader.cpp b/Engine/source/ts/assimp/assimpShapeLoader.cpp index 570d4825f..25f8ee088 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.cpp +++ b/Engine/source/ts/assimp/assimpShapeLoader.cpp @@ -39,6 +39,7 @@ #include "core/util/tVector.h" #include "core/strings/findMatch.h" +#include "core/strings/stringUnit.h" #include "core/stream/fileStream.h" #include "core/fileObject.h" #include "ts/tsShape.h" @@ -133,29 +134,16 @@ void AssimpShapeLoader::enumerateScene() // Post-Processing unsigned int ppsteps = - Con::getBoolVariable("$Assimp::ConvertToLeftHanded", false) ? aiProcess_ConvertToLeftHanded : 0 | - Con::getBoolVariable("$Assimp::CalcTangentSpace", false) ? aiProcess_CalcTangentSpace : 0 | - Con::getBoolVariable("$Assimp::JoinIdenticalVertices", false) ? aiProcess_JoinIdenticalVertices : 0 | - Con::getBoolVariable("$Assimp::ValidateDataStructure", false) ? aiProcess_ValidateDataStructure : 0 | - Con::getBoolVariable("$Assimp::ImproveCacheLocality", false) ? aiProcess_ImproveCacheLocality : 0 | - Con::getBoolVariable("$Assimp::RemoveRedundantMaterials", false) ? aiProcess_RemoveRedundantMaterials : 0 | - Con::getBoolVariable("$Assimp::FindDegenerates", false) ? aiProcess_FindDegenerates : 0 | - Con::getBoolVariable("$Assimp::FindInvalidData", false) ? aiProcess_FindInvalidData : 0 | - Con::getBoolVariable("$Assimp::GenUVCoords", false) ? aiProcess_GenUVCoords : 0 | - Con::getBoolVariable("$Assimp::TransformUVCoords", false) ? aiProcess_TransformUVCoords : 0 | - Con::getBoolVariable("$Assimp::FindInstances", false) ? aiProcess_FindInstances : 0 | - Con::getBoolVariable("$Assimp::LimitBoneWeights", false) ? aiProcess_LimitBoneWeights : 0 | - Con::getBoolVariable("$Assimp::OptimizeMeshes", false) ? aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph : 0 | - 0; - - if(Con::getBoolVariable("$Assimp::FlipUVs", true)) - ppsteps |= aiProcess_FlipUVs; - - if(Con::getBoolVariable("$Assimp::FlipWindingOrder", false)) - ppsteps |= aiProcess_FlipWindingOrder; - - if(Con::getBoolVariable("$Assimp::Triangulate", true)) - ppsteps |= aiProcess_Triangulate; + (ColladaUtils::getOptions().convertLeftHanded ? aiProcess_MakeLeftHanded : 0) | + (ColladaUtils::getOptions().reverseWindingOrder ? aiProcess_FlipWindingOrder : 0) | + (ColladaUtils::getOptions().calcTangentSpace ? aiProcess_CalcTangentSpace : 0) | + (ColladaUtils::getOptions().joinIdenticalVerts ? aiProcess_JoinIdenticalVertices : 0) | + (ColladaUtils::getOptions().removeRedundantMats ? aiProcess_RemoveRedundantMaterials : 0) | + (ColladaUtils::getOptions().genUVCoords ? aiProcess_GenUVCoords : 0) | + (ColladaUtils::getOptions().transformUVCoords ? aiProcess_TransformUVCoords : 0) | + (ColladaUtils::getOptions().flipUVCoords ? aiProcess_FlipUVs : 0) | + (ColladaUtils::getOptions().findInstances ? aiProcess_FindInstances : 0) | + (ColladaUtils::getOptions().limitBoneWeights ? aiProcess_LimitBoneWeights : 0); if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false)) ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph; @@ -163,34 +151,21 @@ void AssimpShapeLoader::enumerateScene() if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false)) ppsteps |= aiProcess_SplitLargeMeshes; + // Mandatory options + //ppsteps |= aiProcess_ValidateDataStructure | aiProcess_Triangulate | aiProcess_ImproveCacheLocality; + ppsteps |= aiProcess_Triangulate; //aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives aiPropertyStore* props = aiCreatePropertyStore(); - //aiSetImportPropertyInteger(props, AI_CONFIG_IMPORT_TER_MAKE_UVS, 1); - //aiSetImportPropertyInteger(props, AI_CONFIG_PP_SBP_REMOVE, (aiProcessPreset_TargetRealtime_Quality - // | aiProcess_FlipWindingOrder | aiProcess_FlipUVs - // | aiProcess_CalcTangentSpace - // | aiProcess_FixInfacingNormals) - // & ~aiProcess_RemoveRedundantMaterials); - //aiSetImportPropertyInteger(props, AI_CONFIG_GLOB_MEASURE_TIME, 1); - //aiSetImportPropertyFloat(props, AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.f); - //aiSetImportPropertyInteger(props,AI_CONFIG_PP_PTV_KEEP_HIERARCHY,1); - - struct aiLogStream shapeLog; - shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_FILE, "assimp.log"); + struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL); + shapeLog.callback = assimpLogCallback; + shapeLog.user = 0; aiAttachLogStream(&shapeLog); #ifdef TORQUE_DEBUG aiEnableVerboseLogging(true); #endif - //c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL); - //aiAttachLogStream(&c); - - // Attempt to import with Assimp. - //mScene = importer.ReadFile(shapePath.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_CalcTangentSpace) - // & ~aiProcess_RemoveRedundantMaterials); - mScene = (aiScene*)aiImportFileExWithProperties(shapePath.getFullPath().c_str(), ppsteps, NULL, props); aiReleasePropertyStore(props); @@ -200,11 +175,38 @@ void AssimpShapeLoader::enumerateScene() Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes); Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials); + // Set import options (if they are not set to override) + if (ColladaUtils::getOptions().unit <= 0.0f) + { + F64 unit; + if (!getMetaDouble("UnitScaleFactor", unit)) + { + F32 floatVal; + S32 intVal; + if (getMetaFloat("UnitScaleFactor", floatVal)) + unit = (F64)floatVal; + else if (getMetaInt("UnitScaleFactor", intVal)) + unit = (F64)intVal; + else + unit = 1.0; + } + ColladaUtils::getOptions().unit = (F32)unit; + } + + if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT) + { + S32 upAxis; + if (!getMetaInt("UpAxis", upAxis)) + upAxis = UPAXISTYPE_Z_UP; + ColladaUtils::getOptions().upAxis = (domUpAxisType) upAxis; + } + // Extract embedded textures for (U32 i = 0; i < mScene->mNumTextures; ++i) extractTexture(i, mScene->mTextures[i]); // Load all the materials. + AssimpAppMaterial::sDefaultMatNumber = 0; for ( U32 i = 0; i < mScene->mNumMaterials; i++ ) AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i])); @@ -245,8 +247,8 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds) TSShapeLoader::computeBounds(bounds); // Check if the model origin needs adjusting - bool adjustCenter = Con::getBoolVariable("$Assimp::adjustCenter", false); //ColladaUtils::getOptions().adjustCenter - bool adjustFloor = Con::getBoolVariable("$Assimp::adjustFloor", false); //ColladaUtils::getOptions().adjustFloor + bool adjustCenter = ColladaUtils::getOptions().adjustCenter; + bool adjustFloor = ColladaUtils::getOptions().adjustFloor; if (bounds.isValidBox() && (adjustCenter || adjustFloor)) { // Compute shape offset @@ -289,6 +291,126 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds) } } +bool AssimpShapeLoader::fillGuiTreeView(const char* sourceShapePath, GuiTreeViewCtrl* tree) +{ + Assimp::Importer importer; + Torque::Path path(sourceShapePath); + String cleanFile = AppMaterial::cleanString(path.getFileName()); + + // Attempt to import with Assimp. + const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace) + & ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals); + + if (!shapeScene) + return false; + mScene = shapeScene; + + // Initialize tree + tree->removeItem(0); + S32 meshItem = tree->insertItem(0, "Meshes", String::ToString("%i", shapeScene->mNumMeshes)); + S32 matItem = tree->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials)); + S32 animItem = tree->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations)); + //S32 lightsItem = tree->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights)); + //S32 texturesItem = tree->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures)); + + //Details! + U32 numPolys = 0; + U32 numVerts = 0; + for (U32 i = 0; i < shapeScene->mNumMeshes; i++) + { + tree->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str())); + numPolys += shapeScene->mMeshes[i]->mNumFaces; + numVerts += shapeScene->mMeshes[i]->mNumVertices; + } + + U32 defaultMatNumber = 0; + for (U32 i = 0; i < shapeScene->mNumMaterials; i++) + { + aiMaterial* aiMat = shapeScene->mMaterials[i]; + + aiString matName; + aiMat->Get(AI_MATKEY_NAME, matName); + String name = matName.C_Str(); + if (name.isEmpty()) + { + name = AppMaterial::cleanString(path.getFileName()); + name += "_defMat"; + name += String::ToString("%d", defaultMatNumber); + defaultMatNumber++; + } + + aiString texPath; + aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath); + String texName = texPath.C_Str(); + if (texName.isEmpty()) + { + aiColor3D read_color(1.f, 1.f, 1.f); + if (AI_SUCCESS == aiMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color)) + texName = String::ToString("Color: (%0.3f, %0.3f, %0.3f)", (F32)read_color.r, (F32)read_color.g, (F32)read_color.b); + else + texName = "No Texture"; + } + else + texName = AssimpAppMaterial::cleanTextureName(texName, cleanFile, sourceShapePath, true); + + tree->insertItem(matItem, String::ToString("%s", name.c_str()), String::ToString("%s", texName.c_str())); + } + + for (U32 i = 0; i < shapeScene->mNumAnimations; i++) + { + String sequenceName = shapeScene->mAnimations[i]->mName.C_Str(); + if (sequenceName.isEmpty()) + sequenceName = "ambient"; + tree->insertItem(animItem, sequenceName.c_str()); + } + + U32 numNodes = 0; + if (shapeScene->mRootNode) + { + S32 nodesItem = tree->insertItem(0, "Nodes", ""); + addNodeToTree(nodesItem, shapeScene->mRootNode, tree, numNodes); + tree->setItemValue(nodesItem, String::ToString("%i", numNodes)); + } + + U32 numMetaTags = shapeScene->mMetaData ? shapeScene->mMetaData->mNumProperties : 0; + if (numMetaTags) + addMetaDataToTree(shapeScene->mMetaData, tree); + + F64 unit; + if (!getMetaDouble("UnitScaleFactor", unit)) + unit = 1.0f; + + S32 upAxis; + if (!getMetaInt("UpAxis", upAxis)) + upAxis = UPAXISTYPE_Z_UP; + + /*for (U32 i = 0; i < shapeScene->mNumLights; i++) + { + treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType)); + }*/ + + // Store shape information in the tree control + tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", numNodes)); + tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", shapeScene->mNumMeshes)); + tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", numPolys)); + tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", shapeScene->mNumMaterials)); + tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", shapeScene->mNumLights)); + tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", shapeScene->mNumAnimations)); + tree->setDataField(StringTable->insert("_textureCount"), 0, avar("%d", shapeScene->mNumTextures)); + tree->setDataField(StringTable->insert("_vertCount"), 0, avar("%d", numVerts)); + tree->setDataField(StringTable->insert("_metaTagCount"), 0, avar("%d", numMetaTags)); + tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", (F32)unit)); + + if (upAxis == UPAXISTYPE_X_UP) + tree->setDataField(StringTable->insert("_upAxis"), 0, "X_AXIS"); + else if (upAxis == UPAXISTYPE_Y_UP) + tree->setDataField(StringTable->insert("_upAxis"), 0, "Y_AXIS"); + else + tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS"); + + return true; +} + void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path) { Torque::Path scriptPath(path); @@ -306,7 +428,7 @@ void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path) if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) ) { // Only update existing materials if forced to - if (Con::getBoolVariable("$Assimp::ForceUpdateMats", false)) + if (ColladaUtils::getOptions().forceUpdateMaterials) { mat->initMaterial(scriptPath, mappedMat); persistMgr.setDirty(mappedMat); @@ -351,20 +473,37 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path) return false; } +void AssimpShapeLoader::assimpLogCallback(const char* message, char* user) +{ + Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n")); +} + bool AssimpShapeLoader::ignoreNode(const String& name) { // Do not add AssimpFbx dummy nodes to the TSShape. See: Assimp::FBX::ImportSettings::preservePivots // https://github.com/assimp/assimp/blob/master/code/FBXImportSettings.h#L116-L135 if (name.find("_$AssimpFbx$_") != String::NPos) return true; - return false; + + if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImport, name, false)) + return false; + + return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImport, name, false); +} + +bool AssimpShapeLoader::ignoreMesh(const String& name) +{ + if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImportMesh, name, false)) + return false; + else + return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMesh, name, false); } void AssimpShapeLoader::detectDetails() { // Set LOD option bool singleDetail = true; - switch (Con::getIntVariable("$Assimp::lodType", 0)) + switch (ColladaUtils::getOptions().lodType) { case ColladaUtils::ImportOptions::DetectDTS: // Check for a baseXX->startXX hierarchy at the top-level, if we find @@ -395,7 +534,7 @@ void AssimpShapeLoader::detectDetails() break; } - AssimpAppMesh::fixDetailSize(singleDetail, Con::getIntVariable("$Assimp::singleDetailSize", 2)); + AssimpAppMesh::fixDetailSize(singleDetail, ColladaUtils::getOptions().singleDetailSize); } void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex) @@ -448,6 +587,148 @@ void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex) } } +void AssimpShapeLoader::addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount) +{ + // Add this node + S32 nodeItem = parentItem; + String nodeName = node->mName.C_Str(); + if (!ignoreNode(nodeName)) + { + if (nodeName.isEmpty()) + nodeName = "null"; + nodeItem = tree->insertItem(parentItem, nodeName.c_str(), String::ToString("%i", node->mNumChildren)); + nodeCount++; + } + + // Add any child nodes + for (U32 n = 0; n < node->mNumChildren; ++n) + addNodeToTree(nodeItem, node->mChildren[n], tree, nodeCount); +} + +void AssimpShapeLoader::addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree) +{ + S32 metaItem = tree->insertItem(0, "MetaData", String::ToString("%i", metaData->mNumProperties)); + + aiString valString; + aiVector3D valVec; + + for (U32 n = 0; n < metaData->mNumProperties; ++n) + { + String keyStr = metaData->mKeys[n].C_Str(); + keyStr += ": "; + switch (metaData->mValues[n].mType) + { + case AI_BOOL: + keyStr += ((bool)metaData->mValues[n].mData) ? "true" : "false"; + break; + case AI_INT32: + keyStr += String::ToString(*((S32*)(metaData->mValues[n].mData))); + break; + case AI_UINT64: + keyStr += String::ToString("%I64u", *((U64*)metaData->mValues[n].mData)); + break; + case AI_FLOAT: + keyStr += String::ToString(*((F32*)metaData->mValues[n].mData)); + break; + case AI_DOUBLE: + keyStr += String::ToString(*((F64*)metaData->mValues[n].mData)); + break; + case AI_AISTRING: + metaData->Get(metaData->mKeys[n], valString); + keyStr += valString.C_Str(); + break; + case AI_AIVECTOR3D: + metaData->Get(metaData->mKeys[n], valVec); + keyStr += String::ToString("%f, %f, %f", valVec.x, valVec.y, valVec.z); + break; + default: + break; + } + + tree->insertItem(metaItem, keyStr.c_str(), String::ToString("%i", n)); + } +} + +bool AssimpShapeLoader::getMetabool(const char* key, bool& boolVal) +{ + if (!mScene || !mScene->mMetaData) + return false; + + String keyStr = key; + for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n) + { + if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase)) + { + if (mScene->mMetaData->mValues[n].mType == AI_BOOL) + { + boolVal = (bool)mScene->mMetaData->mValues[n].mData; + return true; + } + } + } + return false; +} + +bool AssimpShapeLoader::getMetaInt(const char* key, S32& intVal) +{ + if (!mScene || !mScene->mMetaData) + return false; + + String keyStr = key; + for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n) + { + if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase)) + { + if (mScene->mMetaData->mValues[n].mType == AI_INT32) + { + intVal = *((S32*)(mScene->mMetaData->mValues[n].mData)); + return true; + } + } + } + return false; +} + +bool AssimpShapeLoader::getMetaFloat(const char* key, F32& floatVal) +{ + if (!mScene || !mScene->mMetaData) + return false; + + String keyStr = key; + for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n) + { + if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase)) + { + if (mScene->mMetaData->mValues[n].mType == AI_FLOAT) + { + floatVal = *((F32*)mScene->mMetaData->mValues[n].mData); + return true; + } + } + } + return false; +} + +bool AssimpShapeLoader::getMetaDouble(const char* key, F64& doubleVal) +{ + if (!mScene || !mScene->mMetaData) + return false; + + String keyStr = key; + for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n) + { + if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase)) + { + if (mScene->mMetaData->mValues[n].mType == AI_DOUBLE) + { + doubleVal = *((F64*)mScene->mMetaData->mValues[n].mData); + return true; + } + } + } + return false; +} + //----------------------------------------------------------------------------- /// This function is invoked by the resource manager based on file extension. TSShape* assimpLoadShape(const Torque::Path &path) @@ -489,6 +770,14 @@ TSShape* assimpLoadShape(const Torque::Path &path) return NULL; } + // Allow TSShapeConstructor object to override properties + ColladaUtils::getOptions().reset(); + TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructor(path.getFullPath()); + if (tscon) + { + ColladaUtils::getOptions() = tscon->mOptions; + } + AssimpShapeLoader loader; TSShape* tss = loader.generateShape(path); if (tss) @@ -510,57 +799,30 @@ TSShape* assimpLoadShape(const Torque::Path &path) return tss; } -DefineEngineFunction(GetShapeInfo, GuiTreeViewCtrl*, (String filePath), , - "Returns a list of supported shape formats in filter form.\n" - "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|") +DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl), , + "(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from " + "a shape file and store it in a GuiTreeView control. This function is " + "used by the assimp import gui to show a preview of the scene contents " + "prior to import, and is probably not much use for anything else.\n" + "@param shapePath shape filename\n" + "@param ctrl GuiTreeView control to add elements to\n" + "@return true if successful, false otherwise\n" + "@ingroup Editors\n" + "@internal") { - Assimp::Importer importer; - - GuiTreeViewCtrl* treeObj = new GuiTreeViewCtrl(); - treeObj->registerObject(); - - Torque::Path path = Torque::Path(filePath); - - // Attempt to import with Assimp. - const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace) - & ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals); - - //Populate info - S32 meshItem = treeObj->insertItem(0, "Shape", String::ToString("%i", shapeScene->mNumMeshes)); - S32 matItem = treeObj->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials)); - S32 animItem = treeObj->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations)); - S32 lightsItem = treeObj->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights)); - S32 texturesItem = treeObj->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures)); - //S32 meshItem = ->insertItem(0, "Cameras", String::ToString("%s", shapeScene->mNumCameras)); - - //Details! - for (U32 i = 0; i < shapeScene->mNumMeshes; i++) + GuiTreeViewCtrl* tree; + if (!Sim::findObject(ctrl, tree)) { - treeObj->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str())); + Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", ctrl); + return false; } - for (U32 i = 0; i < shapeScene->mNumMaterials; i++) - { - aiMaterial* aiMat = shapeScene->mMaterials[i]; + // Check if a cached DTS is available => no need to import the source file + // if we can load the DTS instead + Torque::Path path(shapePath); + if (AssimpShapeLoader::canLoadCachedDTS(path)) + return false; - aiString matName; - aiMat->Get(AI_MATKEY_NAME, matName); - - aiString texPath; - aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath); - - treeObj->insertItem(matItem, String::ToString("%s", matName.C_Str()), String::ToString("%s", texPath.C_Str())); - } - - for (U32 i = 0; i < shapeScene->mNumAnimations; i++) - { - treeObj->insertItem(animItem, String::ToString("%s", shapeScene->mAnimations[i]->mName.C_Str())); - } - - /*for (U32 i = 0; i < shapeScene->mNumLights; i++) - { - treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType)); - }*/ - - return treeObj; -} \ No newline at end of file + AssimpShapeLoader loader; + return loader.fillGuiTreeView(shapePath, tree); +} diff --git a/Engine/source/ts/assimp/assimpShapeLoader.h b/Engine/source/ts/assimp/assimpShapeLoader.h index 848baaaf9..d89eb97d5 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.h +++ b/Engine/source/ts/assimp/assimpShapeLoader.h @@ -28,6 +28,9 @@ #endif #include +class GuiTreeViewCtrl; +struct aiNode; +struct aiMetadata; //----------------------------------------------------------------------------- class AssimpShapeLoader : public TSShapeLoader { @@ -37,9 +40,18 @@ protected: const struct aiScene* mScene; virtual bool ignoreNode(const String& name); + virtual bool ignoreMesh(const String& name); void detectDetails(); void extractTexture(U32 index, aiTexture* pTex); +private: + void addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount); + void addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree); + bool getMetabool(const char* key, bool& boolVal); + bool getMetaInt(const char* key, S32& intVal); + bool getMetaFloat(const char* key, F32& floatVal); + bool getMetaDouble(const char* key, F64& doubleVal); + public: AssimpShapeLoader(); ~AssimpShapeLoader(); @@ -51,7 +63,10 @@ public: void computeBounds(Box3F& bounds); + bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree); + static bool canLoadCachedDTS(const Torque::Path& path); + static void assimpLogCallback(const char* message, char* user); }; #endif // _ASSIMP_SHAPELOADER_H_ diff --git a/Engine/source/ts/collada/colladaUtils.h b/Engine/source/ts/collada/colladaUtils.h index 144a62c7b..245f80161 100644 --- a/Engine/source/ts/collada/colladaUtils.h +++ b/Engine/source/ts/collada/colladaUtils.h @@ -81,6 +81,13 @@ namespace ColladaUtils NumLodTypes }; + enum eAnimTimingType + { + FrameCount = 0, + Seconds = 1, + Milliseconds = 1000 + }; + domUpAxisType upAxis; // Override for the collada element F32 unit; // Override for the collada element eLodType lodType; // LOD type option @@ -96,6 +103,22 @@ namespace ColladaUtils bool forceUpdateMaterials; // Force update of materials.cs bool useDiffuseNames; // Use diffuse texture as the material name + // Assimp specific preprocess import options + bool convertLeftHanded; // Convert to left handed coordinate system. + bool calcTangentSpace; // Calculate tangents and bitangents, if possible. + bool genUVCoords; // Convert spherical, cylindrical, box and planar mapping to proper UVs. + bool transformUVCoords; // Preprocess UV transformations (scaling, translation ...) + bool flipUVCoords; // This step flips all UV coordinates along the y-axis and adjusts material settings + // and bitangents accordingly.\nAssimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0). + bool findInstances; // Search for instanced meshes and remove them by references to one master. + bool limitBoneWeights; // Limit bone weights to 4 per vertex. + bool joinIdenticalVerts; // Identifies and joins identical vertex data sets within all imported meshes. + bool reverseWindingOrder; // This step adjusts the output face winding order to be clockwise. The default face winding order is counter clockwise. + bool invertNormals; // Reverse the normal vector direction for all normals. + bool removeRedundantMats; // Removes redundant materials. + eAnimTimingType animTiming; // How to import timing data as frames, seconds or milliseconds + S32 animFPS; // FPS value to use if timing is set in frames and the animations does not have an fps set + ImportOptions() { reset(); @@ -117,6 +140,20 @@ namespace ColladaUtils adjustFloor = false; forceUpdateMaterials = false; useDiffuseNames = false; + + convertLeftHanded = false; + calcTangentSpace = false; + genUVCoords = false; + transformUVCoords = false; + flipUVCoords = true; + findInstances = false; + limitBoneWeights = false; + joinIdenticalVerts = true; + reverseWindingOrder = true; + invertNormals = false; + removeRedundantMats = true; + animTiming = Seconds; + animFPS = 30; } }; diff --git a/Engine/source/ts/tsShapeConstruct.cpp b/Engine/source/ts/tsShapeConstruct.cpp index c77802895..a35bc3e81 100644 --- a/Engine/source/ts/tsShapeConstruct.cpp +++ b/Engine/source/ts/tsShapeConstruct.cpp @@ -71,6 +71,14 @@ ImplementEnumType( TSShapeConstructorLodType, { ColladaUtils::ImportOptions::TrailingNumber, "TrailingNumber" }, EndImplementEnumType; +ImplementEnumType(TSShapeConstructorAnimType, + "\n\n" + "@ingroup TSShapeConstructor" ) + { ColladaUtils::ImportOptions::FrameCount, "Frames" }, + { ColladaUtils::ImportOptions::Seconds, "Seconds" }, + { ColladaUtils::ImportOptions::Milliseconds, "Milliseconds" }, +EndImplementEnumType; + //----------------------------------------------------------------------------- @@ -149,6 +157,21 @@ TSShapeConstructor::TSShapeConstructor() mOptions.adjustFloor = false; mOptions.forceUpdateMaterials = false; mOptions.useDiffuseNames = false; + + mOptions.convertLeftHanded = false; + mOptions.calcTangentSpace = false; + mOptions.genUVCoords = false; + mOptions.transformUVCoords = false; + mOptions.flipUVCoords = true; + mOptions.findInstances = false; + mOptions.limitBoneWeights = false; + mOptions.joinIdenticalVerts = true; + mOptions.reverseWindingOrder = true; + mOptions.invertNormals = false; + mOptions.removeRedundantMats = true; + mOptions.animTiming = ColladaUtils::ImportOptions::Seconds; + mOptions.animFPS = 30; + mShape = NULL; } @@ -284,6 +307,35 @@ void TSShapeConstructor::initPersistFields() "Forces update of the materials.cs file in the same folder as the COLLADA " "(.dae) file, even if Materials already exist. No effect for DTS files.\n" "Normally only Materials that are not already defined are written to materials.cs." ); + + // Fields added for assimp options + addField( "convertLeftHanded", TypeBool, Offset(mOptions.convertLeftHanded, TSShapeConstructor), + "Convert to left handed coordinate system." ); + addField( "calcTangentSpace", TypeBool, Offset(mOptions.calcTangentSpace, TSShapeConstructor), + "Calculate tangents and bitangents, if possible." ); + addField( "genUVCoords", TypeBool, Offset(mOptions.genUVCoords, TSShapeConstructor), + "Convert spherical, cylindrical, box and planar mapping to proper UVs." ); + addField( "transformUVCoords", TypeBool, Offset(mOptions.transformUVCoords, TSShapeConstructor), + "Preprocess UV transformations (scaling, translation ...)." ); + addField( "flipUVCoords", TypeBool, Offset(mOptions.flipUVCoords, TSShapeConstructor), + "This step flips all UV coordinates along the y-axis and adjusts material settings and bitangents accordingly.\n" + "Assimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0). This will be needed for most textured models." ); + addField( "findInstances", TypeBool, Offset(mOptions.findInstances, TSShapeConstructor), + "Search for instanced meshes and remove them by references to one master." ); + addField( "limitBoneWeights", TypeBool, Offset(mOptions.limitBoneWeights, TSShapeConstructor), + "Limit bone weights to 4 per vertex." ); + addField( "joinIdenticalVerts", TypeBool, Offset(mOptions.joinIdenticalVerts, TSShapeConstructor), + "Identifies and joins identical vertex data sets within all imported meshes." ); + addField( "reverseWindingOrder", TypeBool, Offset(mOptions.reverseWindingOrder, TSShapeConstructor), + "This step adjusts the output face winding order to be clockwise. The default assimp face winding order is counter clockwise." ); + addField( "invertNormals", TypeBool, Offset(mOptions.invertNormals, TSShapeConstructor), + "Reverse the normal vector direction for all normals." ); + addField( "removeRedundantMats", TypeBool, Offset(mOptions.removeRedundantMats, TSShapeConstructor), + "Removes redundant materials." ); + addField( "animTiming", TYPEID< ColladaUtils::ImportOptions::eAnimTimingType >(), Offset(mOptions.animTiming, TSShapeConstructor), + "How to import timing data as frames, seconds or milliseconds." ); + addField("animFPS", TypeS32, Offset(mOptions.animFPS, TSShapeConstructor), + "FPS value to use if timing is set in frames and the animations does not have an fps set."); endGroup( "Collada" ); addGroup( "Sequences" ); diff --git a/Engine/source/ts/tsShapeConstruct.h b/Engine/source/ts/tsShapeConstruct.h index 7826e0507..dba25d0ac 100644 --- a/Engine/source/ts/tsShapeConstruct.h +++ b/Engine/source/ts/tsShapeConstruct.h @@ -328,9 +328,11 @@ public: typedef domUpAxisType TSShapeConstructorUpAxis; typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType; +typedef ColladaUtils::ImportOptions::eAnimTimingType TSShapeConstructorAnimType; DefineEnumType( TSShapeConstructorUpAxis ); DefineEnumType(TSShapeConstructorLodType); +DefineEnumType(TSShapeConstructorAnimType); class TSShapeConstructorMethodActionCallback {