From 887f239a204561f4f21dd3e40874bcda3ff459c3 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 20 Jun 2026 17:09:43 +0100 Subject: [PATCH 1/2] Assimp fixes Should now apply the same corrective fix to modelled bounds --- Engine/source/ts/assimp/assimpAppMesh.cpp | 46 +++++++++++++++---- Engine/source/ts/assimp/assimpAppMesh.h | 2 +- Engine/source/ts/assimp/assimpAppNode.cpp | 20 +++++--- Engine/source/ts/assimp/assimpShapeLoader.cpp | 10 ++++ 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/Engine/source/ts/assimp/assimpAppMesh.cpp b/Engine/source/ts/assimp/assimpAppMesh.cpp index 60cee3a2e..cad5f5f55 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.cpp +++ b/Engine/source/ts/assimp/assimpAppMesh.cpp @@ -69,7 +69,7 @@ const char* AssimpAppMesh::getName(bool allowFixed) // actual object node. Detect this and return the object node name instead // of the pivot node. const char* nodeName = appNode->getName(); - if ( dStrEqual(nodeName, "null") || dStrEndsWith(nodeName, "PIVOT") ) + if (dStrEqual(nodeName, "null") || dStrEndsWith(nodeName, "PIVOT")) nodeName = appNode->getParentName(); // If all geometry is being fixed to the same size, append the size @@ -79,7 +79,37 @@ const char* AssimpAppMesh::getName(bool allowFixed) MatrixF AssimpAppMesh::getMeshTransform(F32 time) { - return appNode->getNodeTransform(time); + MatrixF transform = appNode->getNodeTransform(time); + + // AssimpAppNode::getTransform() deliberately skips axis correction for the + // bounds node itself, since its (uncorrected) transform is used elsewhere + // as the reference frame the rest of the shape gets normalized against + // (see TSShapeLoader::getLocalNodeMatrix). But if this mesh's geometry was + // hand-modeled as part of the source scene (as opposed to the empty, + // auto-generated bounds node added when none exists), it lives in the same + // source up-axis space as every other mesh and needs the same correction + // baked into its locked vertex data - otherwise it ends up sitting in the + // model's original, unrotated space instead of Torque's Z-up space. + if (appNode->isBounds()) + { + MatrixF axisFix = ColladaUtils::getOptions().axisCorrectionMat; + transform.mulL(axisFix); + } + + return transform; +} + +void AssimpAppMesh::computeBounds(Box3F& bounds) +{ + if (appNode->isBounds()) + { + bounds = Box3F::Invalid; + for (S32 iVert = 0; iVert < points.size(); iVert++) + bounds.extend(points[iVert]); + return; + } + + Parent::computeBounds(bounds); } void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) @@ -94,7 +124,7 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) bool flipNormals = ColladaUtils::getOptions().invertNormals; bool noUVFound = false; - for (U32 i = 0; imNumVertices; i++) + for (U32 i = 0; i < mMeshData->mNumVertices; i++) { // Points and Normals aiVector3D pt = mMeshData->mVertices[i]; @@ -169,10 +199,10 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) | mappedMat; primitive.numElements = indicesCount; - for ( U32 n = 0; n < mMeshData->mNumFaces; ++n) + for (U32 n = 0; n < mMeshData->mNumFaces; ++n) { const struct aiFace* face = &mMeshData->mFaces[n]; - if ( face->mNumIndices == 3 ) + if (face->mNumIndices == 3) { U32 indexCount = face->mNumIndices; for (U32 ind = 0; ind < indexCount; ind++) @@ -180,8 +210,8 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) U32 index = face->mIndices[ind]; indices.push_back(index); } - } - else + } + else { Con::printf("[ASSIMP] Non-Triangle Face Found. Indices: %d", face->mNumIndices); } @@ -281,7 +311,7 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset) } } - if ( noUVFound ) + if (noUVFound) Con::warnf("[ASSIMP] No UV Data for mesh."); } diff --git a/Engine/source/ts/assimp/assimpAppMesh.h b/Engine/source/ts/assimp/assimpAppMesh.h index 48c1cfc34..e0e87add3 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.h +++ b/Engine/source/ts/assimp/assimpAppMesh.h @@ -119,7 +119,7 @@ public: /// @return The mesh transform at the specified time MatrixF getMeshTransform(F32 time) override; F32 getVisValue(F32 t) override; - + void computeBounds(Box3F& bounds) override; static Vector sMaterialRemap; }; diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index af448f940..4fbd7779e 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -48,16 +48,16 @@ aiAnimation* AssimpAppNode::sActiveSequence = NULL; F32 AssimpAppNode::sTimeMultiplier = 1.0f; AssimpAppNode::AssimpAppNode(const aiScene* scene, const aiNode* node, AssimpAppNode* parentNode) - : mScene(scene), - mNode(node ? node : scene->mRootNode), - mInvertMeshes(false), - mLastTransformTime(TSShapeLoader::DefaultTime - 1), - mDefaultTransformValid(false) + : mScene(scene), + mNode(node ? node : scene->mRootNode), + mInvertMeshes(false), + mLastTransformTime(TSShapeLoader::DefaultTime - 1), + mDefaultTransformValid(false) { appParent = parentNode; // Initialize node and parent names. mName = dStrdup(mNode->mName.C_Str()); - if ( dStrlen(mName) == 0 ) + if (dStrlen(mName) == 0) { const char* defaultName = "null"; mName = dStrdup(defaultName); @@ -84,6 +84,14 @@ 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 (mScene && mScene->mRootNode) + { + MatrixF sceneRootMat(true); + assimpToTorqueMat(mScene->mRootNode->mTransformation, sceneRootMat); + mLastTransform.mulL(sceneRootMat); + } + if (!isBounds()) { MatrixF axisFix = ColladaUtils::getOptions().axisCorrectionMat; diff --git a/Engine/source/ts/assimp/assimpShapeLoader.cpp b/Engine/source/ts/assimp/assimpShapeLoader.cpp index 794eb6aa2..4c2b62134 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.cpp +++ b/Engine/source/ts/assimp/assimpShapeLoader.cpp @@ -981,9 +981,19 @@ static bool sReadAssimp(const Torque::Path &path, TSShape*& res_shape) // Allow TSShapeConstructor object to override properties ColladaUtils::getOptions().reset(); TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructorByFilename(path.getFullPath()); + bool autoDetectUpAxis = true; if (tscon) { ColladaUtils::getOptions() = tscon->mOptions; + autoDetectUpAxis = (tscon->mOptions.upAxis == UPAXISTYPE_COUNT); + } + + AssimpShapeLoader loader; + TSShape* tss = loader.generateShape(path); + + if (tscon && autoDetectUpAxis) + { + tscon->mOptions = ColladaUtils::getOptions(); } AssimpShapeLoader loader; From 97f3485a76c91cf267ba0c35cf70ef48de8b0589 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 20 Jun 2026 18:14:00 +0100 Subject: [PATCH 2/2] Update assimpShapeLoader.cpp --- Engine/source/ts/assimp/assimpShapeLoader.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Engine/source/ts/assimp/assimpShapeLoader.cpp b/Engine/source/ts/assimp/assimpShapeLoader.cpp index 4c2b62134..b8e42e7ec 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.cpp +++ b/Engine/source/ts/assimp/assimpShapeLoader.cpp @@ -996,8 +996,6 @@ static bool sReadAssimp(const Torque::Path &path, TSShape*& res_shape) tscon->mOptions = ColladaUtils::getOptions(); } - AssimpShapeLoader loader; - TSShape* tss = loader.generateShape(path); if (tss) { TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");