diff --git a/Engine/source/ts/assimp/assimpAppMesh.cpp b/Engine/source/ts/assimp/assimpAppMesh.cpp index 9f7ac8860..8270ce1f3 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.cpp +++ b/Engine/source/ts/assimp/assimpAppMesh.cpp @@ -47,106 +47,6 @@ S32 AssimpAppMesh::fixedSize = 2; //------------------------------------------------------------------------------ -void AssimpAppMesh::computeBounds(Box3F& bounds) -{ - bounds = Box3F::Invalid; - - if (isSkin()) - { - // Compute bounds for skinned mesh - Vector boneTransforms; - boneTransforms.setSize(nodeIndex.size()); - - // Calculate bone transformations - for (S32 iBone = 0; iBone < boneTransforms.size(); iBone++) { - MatrixF nodeMat = bones[iBone]->getNodeTransform(TSShapeLoader::DefaultTime); - TSShapeLoader::zapScale(nodeMat); // Remove scaling to ensure uniform transformation - boneTransforms[iBone].mul(nodeMat, initialTransforms[iBone]); - } - - // Transform vertices using weighted bone transformations - Vector transformedVerts; - transformedVerts.setSize(initialVerts.size()); - transformedVerts.fill(Point3F::Zero); - - for (S32 iWeight = 0; iWeight < vertexIndex.size(); iWeight++) { - const S32 vertIndex = vertexIndex[iWeight]; - const MatrixF& deltaTransform = boneTransforms[boneIndex[iWeight]]; - - Point3F weightedVert; - deltaTransform.mulP(initialVerts[vertIndex], &weightedVert); - weightedVert *= weight[iWeight]; - - transformedVerts[vertIndex] += weightedVert; - } - - // Extend bounds using the transformed vertices - for (const auto& vert : transformedVerts) { - bounds.extend(vert); - } - } - else - { - MatrixF transform = getMeshTransform(TSShapeLoader::DefaultTime); - TSShapeLoader::zapScale(transform); - - for (S32 iVert = 0; iVert < points.size(); iVert++) - { - Point3F p; - transform.mulP(points[iVert], &p); - bounds.extend(p); - } - } -} - -TSMesh* AssimpAppMesh::constructTSMesh() -{ - if (points.empty() || normals.empty() || primitives.empty() || indices.empty()) - return NULL; - - TSMesh* tsmesh; - if (isSkin()) - { - TSSkinMesh* tsskin = new TSSkinMesh(); - tsmesh = tsskin; - - // Copy skin elements - tsskin->weight = weight; - tsskin->boneIndex = boneIndex; - tsskin->vertexIndex = vertexIndex; - tsskin->batchData.nodeIndex = nodeIndex; - tsskin->batchData.initialTransforms = initialTransforms; - tsskin->batchData.initialVerts = initialVerts; - tsskin->batchData.initialNorms = initialNorms; - } - else - { - tsmesh = new TSMesh(); - } - - // Copy mesh elements - tsmesh->mVerts = points; - tsmesh->mNorms = normals; - tsmesh->mTverts = uvs; - tsmesh->mPrimitives = primitives; - tsmesh->mIndices = indices; - tsmesh->mColors = colors; - tsmesh->mTverts2 = uv2s; - - // Finish initializing the shape - computeBounds(tsmesh->mBounds); - tsmesh->setFlags(flags); - tsmesh->updateMeshFlags(); - //tsmesh->computeBounds(); - tsmesh->numFrames = numFrames; - tsmesh->numMatFrames = numMatFrames; - tsmesh->vertsPerFrame = vertsPerFrame; - tsmesh->createTangents(tsmesh->mVerts, tsmesh->mNorms); - tsmesh->mEncodedNorms.set(NULL, 0); - - return tsmesh; -} - AssimpAppMesh::AssimpAppMesh(const struct aiMesh* mesh, AssimpAppNode* node) : mMeshData(mesh), appNode(node) { diff --git a/Engine/source/ts/assimp/assimpAppMesh.h b/Engine/source/ts/assimp/assimpAppMesh.h index 951327f67..81f0b251d 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.h +++ b/Engine/source/ts/assimp/assimpAppMesh.h @@ -46,9 +46,6 @@ protected: static S32 fixedSize; ///< The fixed detail size value for all geometry public: - void computeBounds(Box3F& bounds) override; - TSMesh* constructTSMesh() override; - AssimpAppMesh(const struct aiMesh* mesh, AssimpAppNode* node); ~AssimpAppMesh() { diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index e8b4cbd4a..88d5af975 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -84,6 +84,8 @@ MatrixF AssimpAppNode::getTransform(F32 time) // no parent (ie. root level) => scale by global shape mLastTransform.identity(); mLastTransform.scale(ColladaUtils::getOptions().unit * ColladaUtils::getOptions().formatScaleFactor); + if (!isBounds()) + ColladaUtils::convertTransform(mLastTransform); } // If this node is animated in the active sequence, fetch the animated transform diff --git a/Engine/source/ts/assimp/assimpShapeLoader.cpp b/Engine/source/ts/assimp/assimpShapeLoader.cpp index 8fd0785c9..1f05f0fab 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.cpp +++ b/Engine/source/ts/assimp/assimpShapeLoader.cpp @@ -192,26 +192,35 @@ void AssimpShapeLoader::enumerateScene() TSShapeLoader::updateProgress(TSShapeLoader::Load_ReadFile, "Reading File"); Con::printf("[ASSIMP] Attempting to load file: %s", shapePath.getFullPath().c_str()); - // Define post-processing steps - U32 ppsteps = aiProcess_Triangulate | /*aiProcess_PreTransformVertices |*/ aiProcess_ConvertToLeftHanded & ~aiProcess_MakeLeftHanded; + const ColladaUtils::ImportOptions& opts = ColladaUtils::getOptions(); + + // Define post-processing steps + unsigned flags = + aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | + aiProcess_ValidateDataStructure | + aiProcess_ConvertToLeftHanded & ~aiProcess_MakeLeftHanded; + + if (opts.convertLeftHanded) flags |= aiProcess_MakeLeftHanded; + if (opts.reverseWindingOrder) flags |= aiProcess_FlipWindingOrder; + if (opts.genUVCoords) flags |= aiProcess_GenUVCoords; + if (opts.transformUVCoords) flags |= aiProcess_TransformUVCoords; + if (opts.limitBoneWeights) flags |= aiProcess_LimitBoneWeights; + if (opts.calcTangentSpace) flags |= aiProcess_CalcTangentSpace; + if (opts.findInstances) flags |= aiProcess_FindInstances; + if (opts.removeRedundantMats) flags |= aiProcess_RemoveRedundantMaterials; + if (opts.joinIdenticalVerts) flags |= aiProcess_JoinIdenticalVertices; + if (opts.invertNormals) flags |= aiProcess_FixInfacingNormals; + if (opts.flipUVCoords) flags |= aiProcess_FlipUVs; - const auto& options = ColladaUtils::getOptions(); - if (options.calcTangentSpace) ppsteps |= aiProcess_CalcTangentSpace; - if (options.joinIdenticalVerts) ppsteps |= aiProcess_JoinIdenticalVertices; - if (options.removeRedundantMats) ppsteps |= aiProcess_RemoveRedundantMaterials; - if (options.genUVCoords) ppsteps |= aiProcess_GenUVCoords; - if (options.transformUVCoords) ppsteps |= aiProcess_TransformUVCoords; - if (options.findInstances) ppsteps |= aiProcess_FindInstances; - if (options.limitBoneWeights) ppsteps |= aiProcess_LimitBoneWeights; if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false)) { - ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph; + flags |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph; } if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false)) { - ppsteps |= aiProcess_SplitLargeMeshes; + flags |= aiProcess_SplitLargeMeshes; } - ppsteps |= aiProcess_ValidateDataStructure; struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL); shapeLog.callback = assimpLogCallback; @@ -221,15 +230,8 @@ void AssimpShapeLoader::enumerateScene() aiEnableVerboseLogging(true); #endif - /*mImporter.SetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 1); - mImporter.SetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 1); - mImporter.SetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4(1, 0, 0, 0, - 0, 0, -1, 0, - 0, 1, 0, 0, - 0, 0, 0, 1));*/ - // Read the file - mScene = mImporter.ReadFile(shapePath.getFullPath().c_str(), ppsteps); + mScene = mImporter.ReadFile(shapePath.getFullPath().c_str(), flags); if (!mScene || !mScene->mRootNode) { Con::errorf("[ASSIMP] ERROR: Could not load file: %s", shapePath.getFullPath().c_str()); @@ -245,26 +247,24 @@ void AssimpShapeLoader::enumerateScene() debugSceneMetaData(mScene); #endif - ColladaUtils::getOptions().upAxis = UPAXISTYPE_Y_UP; // default to Y up for assimp. // Handle scaling configureImportUnits(); - // Format-specific adjustments - String fileExt = String::ToLower(shapePath.getExtension()); - const aiImporterDesc* importerDescription = aiGetImporterDesc(fileExt.c_str()); - if (importerDescription && dStrcmp(importerDescription->mName, "Autodesk FBX Importer") == 0) { - Con::printf("[ASSIMP] Detected FBX format, checking unit scale..."); - F32 scaleFactor = ColladaUtils::getOptions().unit; - if (scaleFactor != 1.0f) { - Con::printf("[ASSIMP] Applying FBX scale factor: %f", scaleFactor); - scaleScene(mScene, scaleFactor); - } - else - { - scaleScene(mScene, 0.01f); + if (mScene->mMetaData) { + aiString fmt; + if (mScene->mMetaData->Get("SourceAsset_Format", fmt)) { + if (dStrstr(fmt.C_Str(), "FBX") != NULL) { + // FBX is always centimeters. Convert to meters. + ColladaUtils::getOptions().formatScaleFactor = 0.0100f; + Con::printf("[ASSIMP] FBX detected: applying 0.01 scale (cm -> m)."); + } } } + ColladaUtils::getOptions().upAxis = UPAXISTYPE_Z_UP; + // Compute & apply axis conversion matrix + getRootAxisTransform(); + for (U32 i = 0; i < mScene->mNumTextures; ++i) { extractTexture(i, mScene->mTextures[i]); } @@ -278,25 +278,21 @@ void AssimpShapeLoader::enumerateScene() // Setup LOD checks detectDetails(); - aiMatrix4x4 sceneRoot = aiMatrix4x4(1, 0, 0, 0, - 0, 0, -1, 0, - 0, 1, 0, 0, - 0, 0, 0, 1); + aiNode* root = mScene->mRootNode; - applyTransformation(mScene->mRootNode, sceneRoot); - - // Process the scene graph - AssimpAppNode* rootNode = new AssimpAppNode(mScene, mScene->mRootNode, 0); - if (!processNode(rootNode)) { - delete rootNode; + for (S32 iNode = 0; iNode < root->mNumChildren; iNode++) + { + aiNode* child = root->mChildren[iNode]; + AssimpAppNode* node = new AssimpAppNode(mScene, child); + if (!processNode(node)) { + delete node; + } } - processAssimpNode(mScene->mRootNode, mScene, rootNode); - // Add a bounds node if none exists if (!boundsNode) { + aiNode* reqNode = new aiNode("bounds"); - mScene->mRootNode->addChildren(1, &reqNode); reqNode->mTransformation = aiMatrix4x4();// *sceneRoot; AssimpAppNode* appBoundsNode = new AssimpAppNode(mScene, reqNode); if (!processNode(appBoundsNode)) { @@ -312,10 +308,16 @@ void AssimpShapeLoader::enumerateScene() } void AssimpShapeLoader::configureImportUnits() { - auto& options = ColladaUtils::getOptions(); + auto& opts = ColladaUtils::getOptions(); // Configure unit scaling - if (options.unit <= 0.0f) { + if (opts.unit > 0.0f) + return; + + + // Try metadata for some formats + if (mScene->mMetaData) + { F64 unitScaleFactor = 1.0; if (!getMetaDouble("UnitScaleFactor", unitScaleFactor)) { F32 floatVal; @@ -327,22 +329,55 @@ void AssimpShapeLoader::configureImportUnits() { unitScaleFactor = static_cast(intVal); } } - options.unit = static_cast(unitScaleFactor); + + opts.formatScaleFactor = unitScaleFactor; + + unitScaleFactor = 1.0; + if (!getMetaDouble("OriginalUnitScaleFactor", unitScaleFactor)) { + F32 floatVal; + S32 intVal; + if (getMetaFloat("OriginalUnitScaleFactor", floatVal)) { + unitScaleFactor = static_cast(floatVal); + } + else if (getMetaInt("OriginalUnitScaleFactor", intVal)) { + unitScaleFactor = static_cast(intVal); + } + } + + opts.unit = unitScaleFactor; + + // FBX may use another property name + U32 unit = 0; + if (mScene->mMetaData->Get("Unit", unit)) + { + opts.unit = (F32)unit; + } } } -void AssimpShapeLoader::processAssimpNode(const aiNode* node, const aiScene* scene, AssimpAppNode* parentNode) +void AssimpShapeLoader::getRootAxisTransform() { - AssimpAppNode* currNode; - if (node == scene->mRootNode) + aiMetadata* meta = mScene->mMetaData; + if (!meta) { - currNode = parentNode; - } - else - { - currNode = new AssimpAppNode(scene, node, parentNode); - processNode(currNode); + // assume y up + ColladaUtils::getOptions().upAxis = UPAXISTYPE_Y_UP; + return; } + + // Fetch metadata values + int upAxis = 1, upSign = 1; + int frontAxis = 2, frontSign = -1; + int coordAxis = 0, coordSign = 1; + + meta->Get("UpAxis", upAxis); + meta->Get("UpAxisSign", upSign); + meta->Get("FrontAxis", frontAxis); + meta->Get("FrontAxisSign", frontSign); + meta->Get("CoordAxis", coordAxis); + meta->Get("CoordAxisSign", coordSign); + + ColladaUtils::getOptions().upAxis = (domUpAxisType)upAxis; } void AssimpShapeLoader::processAnimations() @@ -570,46 +605,6 @@ bool AssimpShapeLoader::fillGuiTreeView(const char* sourceShapePath, GuiTreeView return true; } -void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path) -{ - return; - /* - Torque::Path scriptPath(path); - scriptPath.setFileName("materials"); - scriptPath.setExtension(TORQUE_SCRIPT_EXTENSION); - - // First see what materials we need to update - PersistenceManager persistMgr; - for ( U32 iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++ ) - { - AssimpAppMaterial *mat = dynamic_cast( AppMesh::appMaterials[iMat] ); - if ( mat ) - { - Material *mappedMat; - if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) ) - { - // Only update existing materials if forced to - if (ColladaUtils::getOptions().forceUpdateMaterials) - { - mat->initMaterial(scriptPath, mappedMat); - persistMgr.setDirty(mappedMat); - } - } - else - { - // Create a new material definition - persistMgr.setDirty( mat->createMaterial( scriptPath ), scriptPath.getFullPath() ); - } - } - } - - if ( persistMgr.getDirtyList().empty() ) - return; - - persistMgr.saveDirty(); - */ -} - /// Check if an up-to-date cached DTS is available for this DAE file bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path) { @@ -1004,8 +999,6 @@ TSShape* assimpLoadShape(const Torque::Path &path) tss->write(&dtsStream); } } - - loader.updateMaterialsScript(path); } loader.releaseImport(); return tss; diff --git a/Engine/source/ts/assimp/assimpShapeLoader.h b/Engine/source/ts/assimp/assimpShapeLoader.h index d5daff3af..086e4969f 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.h +++ b/Engine/source/ts/assimp/assimpShapeLoader.h @@ -47,6 +47,9 @@ protected: Assimp::Importer mImporter; const aiScene* mScene; + // internal helpers + void getRootAxisTransform(); + //bool processNode(AppNode* node) override; bool ignoreNode(const String& name) override; bool ignoreMesh(const String& name) override; @@ -54,7 +57,6 @@ protected: void extractTexture(U32 index, aiTexture* pTex); private: - void processAssimpNode(const aiNode* node, const aiScene* scene, AssimpAppNode* parentNode = nullptr); void addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount); void addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree); bool getMetabool(const char* key, bool& boolVal); @@ -70,7 +72,6 @@ public: void releaseImport(); void enumerateScene() override; void configureImportUnits(); - void updateMaterialsScript(const Torque::Path &path); void processAnimations(); void computeBounds(Box3F& bounds) override;