Skeletal Mesh Ref Skeleton still not properly initializing virtual bones

This question was created in reference to: [Skeletal Mesh RefSkeleton not restoring virtual [Content removed]

We recently upgraded our project from 5.4.2 -> 5.5.4, and we noticed that our virtual bones on our character meshes were not animating or able to be manipulated in our animation graphs. Digging through the engine code and stepping through to understand what was happening, and it appears that the SkeletalMesh was calling Serialize before the Skeleton was being fully loaded into memory. This was causing the Reference Skeleton on the skeletal mesh to not know about the virtual bones that existed on the skeleton.

During editor load, we would get a call to `USkeletalMesh::Serialize` that would call `FReferenceSkeleton::RebuildRefSkeleton`. However, the passed in Skeleton asset would still not have any data initialized, including the virtual bones. (Object Flags ReachabilityFlag0 | LoaderImport | AsyncLoadingPhase1 | Async). The SkeletalMesh would have the same flags. Shortly after, we’d see `USkeleton::Serialize` called on the skeleton with the data now populated, but the SkeletalMesh never queries for the virtual bones after the first serialize call.

It’s worth mentioning this is not the case with all of our skeletal meshes. Stepping through, most skeletons are serialized before their meshes, but we have a case where the first skeletal mesh that’s loaded is loaded before its skeleton, which happens to be our character mesh. I linked the referenced question as it appears to be exactly what we’re observing, but unfortunately the fix that was submitted (I think from Unreal Engine Issues and Bug Tracker (UE\-66234\)) does not appear to fix our case. In the meantime the user from the referenced question suggested adding `GetRefSkeleton().RebuildRefSkeleton(GetSkeleton(), false)` in `USkeletalMesh::PostLoad`, which appears to fix the issue. But I don’t know if this is an appropriate fix or if there is a better fix to use.

Hi, thanks for reporting this issue and including all that info. We’ve had various reports for a while about virtual bones being lost and it sounds like this could be the cause.

From those flags, it looks like the Skeleton is being loaded at the same time that the mesh is being loaded. There’s a couple of things you could try with this:

  • In USkeletalMesh::PostLoad call ConditionalPostLoad on the skeleton
  • In USkeletalMesh::Serialize call Preload on the skeleton prior to the call to RebuildRefSkeleton (as an example, we do this at the end of the Serialize method for the cloth assets)

Let me know if either of those fix the behaviour for you.

I think it makes sense that ConditionalPostLoad wouldn’t make a difference in this case since it’s too late in the serialization process. And, from my understanding, the order of conditional post load should be deterministic based on dependencies, whereas serialize isn’t. Preloading the dependent asset is usually the way around this kind of problem, so I would recommend going with that. It also means that you should avoid any other potential issues with the skeleton not having been deserialized.

I think you should be able to simplify the code a little to this:

`if (Ar.IsLoading())
{
USkeleton* MeshSkeleton = GetSkeleton();
if (MeshSkeleton)
{
Ar.Preload(MeshSkeleton);
}

const bool bRebuildNameMap = false;
GetRefSkeleton().RebuildRefSkeleton(GetSkeleton(), bRebuildNameMap);
}`I’ll talk to the dev team about getting this change integrated.

Also, I’m curious what the flags look like on the skeleton after you’ve called preload?

Hi Euan,

The ConditionalPostLoad did not end up working for us, as nothing seemed to tell the skeletal mesh to refresh its reference skeleton with the loaded virtual bones on the skeleton (unless you mean in addition to the lines we added from the user in the referenced question.

The Preload seemed to work for the Skeletal Mesh Serialize if we called it before RebuildRefSkeleton. We made the following changes, which appeared to work for any skeletal meshes loaded before their skeleton (SkeletalMesh.cpp, line 1810-1826)

if (Ar.IsLoading()) { #if WITH_ENGINE_CHANGES { FLinkerLoad* l_Linker = GetLinker(); USkeleton* l_Skeleton = GetSkeleton(); if (l_Linker != nullptr && l_Skeleton != nullptr) { l_Linker->Preload(l_Skeleton); } } #endif const bool bRebuildNameMap = false; GetRefSkeleton().RebuildRefSkeleton(GetSkeleton(), bRebuildNameMap); }Is this a better solution than the one proposed by the user? We’ve been using the RebuildRefSkeleton call in PostLoad for about 2 weeks now with no issue, but I’m more than happy to use this solution if it’s more appropriate. Thanks!

Went ahead and simplified the code to what you provided.

For the skeleton, the flags on the Object and Package were the following:

Skeleton Object (Before): RF_Public | RF_Standalone | RF_Transactional | RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WasLoaded

Skeleton Package (Before): RF_Public | RF_WillBeLoaded

Skeleton Object (After): RF_Public | RF_Standalone | RF_Transactional | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WasLoaded | RF_LoadCompleted

Skeleton Package (After): RF_Public | RF_WillBeLoaded

The 4 flags I mentioned before (ReachabilityFlag0 | LoaderImport | AsyncLoadingPhase1 | Async) don’t seem to change, but I only seem to get these from Rider’s natvis. I’m not 100% sure where it’s pulling these flags from.

Hi, the object flags look good based on the changes since previously the skeleton had RF_NeedLoad whereas now it has RF_NeedPostLoad set. I have a CL to get this change integrated into 5.7 but the owner of the serialization code is out of office at the moment so it’ll be a few weeks until I can get it integrated. So I’ve created a JIRA that you’ll be able to track the status from.

Since we have that, and things are working for you, I’m going to close out this thread. But you can always reopen it if you want to discuss things further, or if any other issues come up around those changes.