Unreal Engine exports wrong/incorrect framerate Animation Sequences to FBX!

Super, super confused here…

I have an animation created in sequencer running at 24fps.

I bake the animation into an asset at 24fps. (The information panel on hover confirms this).

I right click → Asset Actions → Export, and boom - a 30FPS fbx.

How and why?

Is there a way to fix this, or is it a bug?

EDIT: SOLUTION FOUND!

I’ve done some more digging.

In the Unreal source code, FbxAnimationExport.cpp appears to be responsible for setting the framerate in the FBX file.

# https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxAnimationExport.cpp
# Line 36

		const double FrameRate = AnimSeq->GetDataModel()->GetFrameRate().AsDecimal();
		//Configure the scene time line
		{
			FbxGlobalSettings& SceneGlobalSettings = Scene->GetGlobalSettings();
			double CurrentSceneFrameRate = FbxTime::GetFrameRate(SceneGlobalSettings.GetTimeMode());
			if (!bSceneGlobalTimeLineSet || FrameRate > CurrentSceneFrameRate)
			{
				FbxTime::EMode ComputeTimeMode = FbxTime::ConvertFrameRateToTimeMode(FrameRate);
				FbxTime::SetGlobalTimeMode(ComputeTimeMode, ComputeTimeMode == FbxTime::eCustom ? FrameRate : 0.0);
				SceneGlobalSettings.SetTimeMode(ComputeTimeMode);
				if (ComputeTimeMode == FbxTime::eCustom)
				{
					SceneGlobalSettings.SetCustomFrameRate(FrameRate);
				}
				bSceneGlobalTimeLineSet = true;
			}
		}

Line 36 should get the framerate from the sequence and and then the lines following should interpret that framerate and correctly set it inside the FBX GlobalSettings.

The FBX GlobalSettings can be found in the Autodesk FBX documentation, here:
FBX SDK Documentation: C++: FbxGlobalSettings Class Reference (autodesk.com)

In there, you’ll find the two functions that FbxAnimationExport.cpp calls on, listed under Time Settings:

# FBX C++ API Reference > Class Hierarchy >  FbxEmitter > FbxObject > FbxGlobalSettings
void SetTimeMode	(	FbxTime::EMode 	pTimeMode	)	
    Sets the time mode.
    Parameters:
    pTimeMode	One of the defined modes in class FbxTime.

FBX Time has supported FPS presets:

# FBX C++ API Reference > Class Hierarchy > FbxTime
eMode:
    eDefaultMode 	
    eFrames120 	
    eFrames100 	
    eFrames60 	
    eFrames50 	
    eFrames48 	
    eFrames30 	
    eFrames30Drop 	
    eNTSCDropFrame 	
    eNTSCFullFrame 	
    ePAL 	
    eFrames24 	
    eFrames1000 	
    eFilmFullFrame 	
    eCustom 	
    eFrames96 	
    eFrames72 	
    eFrames59dot94 	
    eModesCount 	

eFrames24 exists as an enum, so in theory the code inside FbxAnimationExport.cpp should be working. The difficulty for me is I don’t have the skills to actually debug what the engine itself is doing at runtime while running this code, so I don’t know what values it’s outputting and working with!

# Step 1:
# FbxAnimationExport.cpp, line 43
FbxTime::EMode ComputeTimeMode = FbxTime::ConvertFrameRateToTimeMode(FrameRate);

# Which runs: FBX C++ API Reference > Class Hierarchy > FbxTime
    static EMode ConvertFrameRateToTimeMode( double pFrameRate, double pPrecision = 0.00000001)
        # "Get time mode associated with frame rate."
        # "The corresponding time mode identifier or eDefaultMode if no time mode associated to the given frame rate is found."

# Step 2: FbxAnimationExport.cpp, line 44
FbxTime::SetGlobalTimeMode(ComputeTimeMode, ComputeTimeMode == FbxTime::eCustom ? FrameRate : 0.0);

# Which runs: FBX C++ API Reference > Class Hierarchy > FbxTime
    static void SetGlobalTimeMode( EMode pTimeMode, double pFrameRate = 0.0 )
        # "Set default time mode."
        # Parameters:
            # pTimeMode: Time mode identifier.
            # pFrameRate: Custom framerate, only have effect in case of pTimeMode = FbxTime::eCustom
        #Remarks:
            #It is meaningless to set default time mode to eDefaultMode.

# So if Step 1 produces a eDefaultMode EMode enum, then Step 2 does nothing.

# Step 3: FbxAnimationExport.cpp, line 45
SceneGlobalSettings.SetTimeMode(ComputeTimeMode);
if (ComputeTimeMode == FbxTime::eCustom)
{
 	SceneGlobalSettings.SetCustomFrameRate(FrameRate);
}
bSceneGlobalTimeLineSet = true;
# If the eMode returned from Step 1 is eCustom (because the framerate assessed in step one is not an existing EMode enum) then set a totally custom framerate.

And we should be good.

But just thinking through the logic, looking at line 41:

# FbxAnimationExport.cpp, line 41
if (!bSceneGlobalTimeLineSet || FrameRate > CurrentSceneFrameRate)

If we’ve already set the value, skip…
OR
if FrameRate is greater than CurrentSceneFrameRate, skip…

# FbxAnimationExport.cpp, line 41
const double FrameRate = AnimSeq->GetDataModel()->GetFrameRate().AsDecimal();
# Therefore:
FrameRate = 24.0

So what is the FbxTime default value?

# FbxAnimationExport.cpp, line 41
double CurrentSceneFrameRate = FbxTime::GetFrameRate(SceneGlobalSettings.GetTimeMode());
# Therefore:
FrameRate = [unknown value]

Going back to our conditional:

# FbxAnimationExport.cpp, line 41
if (!bSceneGlobalTimeLineSet || 24 > [unknown])
    # There's the possibility setting the framerate won't work because the unknown value is larger than 24.

Based on the code, I figure the assumption is that if the FbxScene hasn’t been setup yet, that line 41 FbxTime::GetFrameRate(SceneGlobalSettings.GetTimeMode()); is likely feeding through an EMode value of eDefaultMode, which when assessed by GetFrameRate(), resolves into 0.

Instead, I took a different approach. I manually inspected the FBX export using the autodesk fbx converter tool, and found something interesting:

TimeMode is being exported as 6, instead of what it shouold be, 11!

# fbxsdk/core/base/fbxtime.h Source File
enum EMode
{
        eDefaultMode,    #  0
        eFrames120,      #  1
        eFrames100,      #  2
        eFrames60,       #  3
        eFrames50,       #  4
        eFrames48,       #  5
        eFrames30,       #  6 <- !!!!!!!!
        eFrames30Drop,   #  7
        eNTSCDropFrame,  #  8
        eNTSCFullFrame,  #  9
        ePAL,            # 10
        eFrames24,       # 11 <- What it should be
        eFrames1000,     # 12
        eFilmFullFrame,  # 13
        eCustom,         # 14
        eFrames96,       # 15
        eFrames72,       # 16
        eFrames59dot94,  # 17
        eModesCount      # 18
};              

And from there, I know exactly what the solution is. I’ll explain it in the next post for those with the same problem.

In my next post I’ll explain the actual issue causing this.

EXPLANATION:

I noticed this happens when using different menus to export.

When exporting via Asset Actions -> Export the EMode enum is correctly written into the FBX settings.

However, when I:
Open the Animation Sequence -> Top Bar -> Export Asset -> Preview Mesh

Unreal Engine fails to setup the TimeMode setting.

I have submitted a bug report and will update the post if a ticket is created for it.

Hope this helps.