Skip to content

Commit 251440d

Browse files
authored
Merge pull request #1817 from Areloch/assimpShapeLoadMiscFixes
Fixes issues with node rotations as well as groundFrames processing for assimp shape imports
2 parents 79cf805 + c38c019 commit 251440d

3 files changed

Lines changed: 42 additions & 42 deletions

File tree

Engine/source/ts/assimp/assimpAppNode.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ MatrixF AssimpAppNode::getTransform(F32 time)
8484
// no parent (ie. root level) => scale by global shape <unit>
8585
mLastTransform.identity();
8686
mLastTransform.scale(ColladaUtils::getOptions().unit * ColladaUtils::getOptions().formatScaleFactor);
87-
if (!isBounds())
88-
{
89-
MatrixF axisFix = ColladaUtils::getOptions().axisCorrectionMat;
90-
mLastTransform.mulL(axisFix);
91-
}
87+
88+
MatrixF axisFix = ColladaUtils::getOptions().axisCorrectionMat;
89+
mLastTransform.mulL(axisFix);
9290
}
9391

9492
// If this node is animated in the active sequence, fetch the animated transform

Engine/source/ts/assimp/assimpAppNode.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,7 @@ class AssimpAppNode : public AppNode
123123

124124
MatrixF getNodeTransform(F32 time) override;
125125
bool animatesTransform(const AppSequence* appSeq) override;
126-
bool isParentRoot() override
127-
{
128-
if (!appParent)
129-
return false; // the scene root itself has no parent — not a content root
130-
131-
// True when this node's immediate parent is the scene root node.
132-
// mParentName is stored at construction from the AppNode's normalised name
133-
// (empty names become "null"), so apply the same normalisation to the raw
134-
// aiScene root name before comparing.
135-
const char* rootName = mScene->mRootNode->mName.C_Str();
136-
if (dStrlen(rootName) == 0)
137-
rootName = "null";
138-
139-
return dStrcmp(mParentName, rootName) == 0;
140-
}
126+
bool isParentRoot() override { return (appParent == NULL); }
141127

142128
static void assimpToTorqueMat(const aiMatrix4x4& inAssimpMat, MatrixF& outMat);
143129
static aiNode* findChildNodeByName(const char* nodeName, aiNode* rootNode);

Engine/source/ts/assimp/assimpShapeLoader.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ void AssimpShapeLoader::enumerateScene()
244244
aiEnableVerboseLogging(true);
245245
#endif
246246

247+
//We already do transform stuff on our end, so we don't need assimp doing it as well and potentially causing a double-up
248+
mImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_IGNORE_UP_DIRECTION, true);
249+
247250
// Read the file
248251
mScene = mImporter.ReadFile(shapePath.getFullPath().c_str(), flags);
249252

@@ -310,20 +313,19 @@ void AssimpShapeLoader::enumerateScene()
310313
// Setup LOD checks
311314
detectDetails();
312315

313-
// Process mRootNode as an AppNode directly.
314-
//
315-
// Making it the single parentless node means the !appParent branch in
316-
// AssimpAppNode::getTransform() fires exactly once — for this node only.
317-
// Scale + axisCorrectionMat are therefore applied in exactly one place.
318-
// Every child (bones, mesh nodes, bounds) inherits the correction naturally
319-
// through the parent chain; no per-node special-casing is needed.
320-
AssimpAppNode* sceneRootAppNode = new AssimpAppNode(mScene, mScene->mRootNode, nullptr);
321-
if (!processNode(sceneRootAppNode))
316+
// Register each direct child of mRootNode as its own parentless AppNode
317+
// (mirrors ColladaShapeLoader::enumerateScene()) instead of one wrapper
318+
// node.
319+
for (U32 iNode = 0; iNode < mScene->mRootNode->mNumChildren; iNode++)
320+
{
321+
aiNode* childNode = mScene->mRootNode->mChildren[iNode];
322+
AssimpAppNode* appNode = new AssimpAppNode(mScene, childNode, nullptr);
323+
if (!processNode(appNode))
322324
{
323-
Con::errorf("[ASSIMP] Failed to process scene root node '%s'.",
324-
mScene->mRootNode->mName.C_Str());
325-
delete sceneRootAppNode;
326-
sceneRootAppNode = nullptr;
325+
Con::errorf("[ASSIMP] Failed to process root-level node '%s'.",
326+
childNode->mName.C_Str());
327+
delete appNode;
328+
}
327329
}
328330

329331
// Bounds check — every Torque shape needs a bounds node.
@@ -399,7 +401,8 @@ void AssimpShapeLoader::configureImportUnits() {
399401
}
400402

401403
F32 fps;
402-
if(getMetaFloat("CustomFrameRate", fps))
404+
// FBX uses -1 to indicate "no custom frame rate", so only use it if it's positive
405+
if (getMetaFloat("CustomFrameRate", fps) && fps > 0.0f)
403406
opts.animFPS = fps;
404407
}
405408
}
@@ -452,7 +455,7 @@ void AssimpShapeLoader::getRootAxisTransform()
452455
return v;
453456
};
454457

455-
Point3F forward = axisToVector(frontAxis, frontSign == 1 ? -frontSign : frontSign);
458+
Point3F forward = axisToVector(frontAxis, -frontSign);
456459
Point3F up = axisToVector(upAxis, upSign);
457460
Point3F right = mCross(forward, up);
458461

@@ -478,7 +481,7 @@ void AssimpShapeLoader::getRootAxisTransform()
478481

479482
void AssimpShapeLoader::processAnimations()
480483
{
481-
if (mScene->mNumAnimations == 0)
484+
if (mScene->mNumAnimations == 0 || mScene->mAnimations[0] == NULL)
482485
return;
483486

484487
// Multiple animations = multiple actions; single animation = flat timeline.
@@ -505,10 +508,14 @@ void AssimpShapeLoader::processAnimations()
505508

506509
Vector<aiNodeAnim*> ambientChannels;
507510
F32 maxKeyTime = 0.0f;
511+
F32 maxSourceDuration = 0.0f;
508512

513+
// mScene outlives this call, so reference its channels directly - no per-channel copy needed.
509514
for (U32 i = 0; i < mScene->mNumAnimations; ++i)
510515
{
511516
aiAnimation* anim = mScene->mAnimations[i];
517+
maxSourceDuration = getMax(maxSourceDuration, (F32)anim->mDuration);
518+
512519
for (U32 j = 0; j < anim->mNumChannels; j++)
513520
{
514521
aiNodeAnim* nodeAnim = anim->mChannels[j];
@@ -522,12 +529,21 @@ void AssimpShapeLoader::processAnimations()
522529
}
523530
}
524531

525-
ambientSeq->mNumChannels = ambientChannels.size();
526-
ambientSeq->mChannels = ambientChannels.address();
527-
ambientSeq->mDuration = maxKeyTime;
528-
ambientSeq->mTicksPerSecond = targetTPS;
532+
// no point in creating a NULL anim sequence...
533+
if (ambientChannels.size() > 0)
534+
{
535+
ambientSeq->mNumChannels = ambientChannels.size();
536+
ambientSeq->mChannels = ambientChannels.address();
537+
// if we somehow dont have keys, just use the max source duration
538+
ambientSeq->mDuration = (maxKeyTime > 0.0f) ? maxKeyTime : maxSourceDuration;
539+
ambientSeq->mTicksPerSecond = targetTPS;
529540

530-
appSequences.push_back(new AssimpAppSequence(ambientSeq));
541+
appSequences.push_back(new AssimpAppSequence(ambientSeq));
542+
}
543+
else
544+
{
545+
delete ambientSeq;
546+
}
531547
return;
532548
}
533549

@@ -1135,7 +1151,7 @@ bool AssimpShapeLoader::getMetabool(const char* key, bool& boolVal)
11351151
{
11361152
if (mScene->mMetaData->mValues[n].mType == AI_BOOL)
11371153
{
1138-
boolVal = (bool)mScene->mMetaData->mValues[n].mData;
1154+
boolVal = *(bool*)mScene->mMetaData->mValues[n].mData;
11391155
return true;
11401156
}
11411157
}

0 commit comments

Comments
 (0)