Static Mesh bounds calculation issue with bDoFastBuild

Hi,

Some of the people on the dev team have been getting an assert lately. The assert in question is this:

Ensure condition failed: false [File:J:\workspace\tl\m\d\UnrealEngine\Engine\Source\Runtime\Core\Public\Math\Matrix.h] [Line: 429] TMatrix<T>::InverseFast(), trying to invert a NIL matrix, this results in NaNs! Use Inverse() instead.

This assert is caused by a static mesh asset having invalid/unset bounds. When comparing a working static mesh with a broken static mesh (a static mesh that will give the assert above), I found out that the difference causing the issue is the value of the bDoFastBuild member variable in UStaticMesh. When this is set to true, the bounds calculation are not updated correctly. An easy fix was to remove the UPROPERTY() above the bDoFastBuild variable, to avoid this being serialized. I feel like this fix is just hiding a bigger issue here and I am wondering if this is known or if there is a work around or a way to get the bounds to recalculate when the bDoFastBuild is set to true.

Hey there, I’m finding the right team to investigate this. In the meantime could you also post the callstack for when you’re hitting that ensure?

Hi Julien,

Can you confirm whether this is happening with all your static meshes, or just a particular one? Would you mind attaching such a broken mesh so I can try to reproduce this myself (and any steps I’d need to take in order for the issue to appear? It looks as if just adding it to the scene would be enough?).

Also, do you have any idea how the broken mesh was imported? Was it via regular FBX import, was it generated by one of our other editor tools, or was it a completely procedurally created mesh?

Thanks for your help. I’ll try to get you a solution for this once I have a bit more information.

Rich

Hi Julien,

I’m going to try to make time to look at this soon. A few things occur to me from your last post.

1) An imported mesh should never have bDoFastBuild set. bDoFastBuild is intended for static meshes which were generated dynamically in the editor or at runtime. Thus generally we shouldn’t expect BuildFromMeshDescription to be called at all when loading an imported static mesh.

2) The content browser approx size is a different value to the actual bounding box. It is an asset registry tag which is filled in during asset scanning. Again we probably wouldn’t expect a bDoFastBuild mesh to have that set up, since it should be a dynamically created asset which doesn’t register with the asset registry.

I would love to know how the problematic meshes came into existence originally. It seems to me that the problems would probably disappear if you were simply to force bDoFastBuild to false on those assets. Perhaps you can try that (and one way would be to expose the bDoFastBuild property in your local StaticMesh.h, by making its `UPROPERTY()` `EditAnywhere` and give it a `Category=Temp`).

Can you let me know if that solves the issue? Meanwhile reimporting it should fix the asset (and I would recommend that you probably do so!).

Thanks,

Rich

Hi,

We are encountering the same bounds issue (with the same ensure failure) and identified the same invalid bounds problem, in both FStaticMeshRenderData::Bounds and FDistanceFieldVolumeData::LocalSpaceMeshBounds (the latter being derived from the former).

Most our static assets are imported with InterchangeFramework (default in UE5.5). Regarding the bDoFastBuild setting, it seems that bFastBuild is always set to true in UInterchangeStaticMeshFactory:

// InterchangeStaticMeshFactory.cpp, from line 1049, in UE5.5.4.
BuildMeshDescriptionsParams.bFastBuild = true;
...
StaticMesh.BuildFromMeshDescriptions(MeshDescriptionPointers, BuildMeshDescriptionsParams);

I hope this information is helpful. We are really looking forward to a fix for this issue.

(We are still figuring out how to fix this issue in our project.)

Best regards,

Lai

I’ve spent a bit more time investigating it. In theory, that Interchange method which calls BuildFromMeshDescriptions should only be getting hit when Interchange is used at runtime to import a mesh, rather than via a regular editor import. [Content removed] can you describe your exact process for importing your static meshes through Interchange please?

To be more specific: can you put a breakpoint at the start of UInterchangeStaticMeshFactory::BuildFromMeshDescriptions and see if bIsAppGame is true, and if so, paste me the callstack to show me how it’s getting there. I am only seeing this being entered with bIsAppGame==false, and thus the “fast build” process doesn’t occur.

Here is a callstack of the assert taken from our Backtrace instance:

KERNELBASE.dll RaiseException

Client.exe ReportEventOnCallingThread

Client.exe ReportEvent

Client.exe FDebug::EnsureFailed

Client.exe FDebug::OptionallyLogFormattedEnsureMessageReturningFalseImpl

Client.exe FDebug::OptionallyLogFormattedEnsureMessageReturningFalse

Client.exe CheckVerifyImpl

Client.exe UE::Assert::Private::EnsureFailed

Client.exe `UE::Math::TMatrix<float>::ErrorEnsure’::`2’::<lambda_1>::operator()

Client.exe UE::Math::TMatrix<float>::ErrorEnsure

Client.exe UE::Math::TMatrix<float>::InverseFast

Client.exe `FDistanceFieldSceneData::UpdateDistanceFieldObjectBuffers’::`46’::<lambda_1>::operator()

Client.exe UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::FFunctionRefStoragePolicy,void __cdecl(int)>::operator()

Client.exe CallBody(TFunctionRef<void __cdecl(int)> const &,TArrayView<std::nullptr_t,int> const &,int,int)

Client.exe ParallelForImpl::ParallelForInternal<TFunctionRef<void (int)>,`ParallelFor’::`2’::<lambda_1>,std::nullptr_t>(wchar_t const *,int,int,TFunctionRef<void __cdecl(int)>,ParallelFor::__l2::<lambda_1>,EParallelForFlags,TArrayView<std::nullptr_t,int> const &)

Client.exe ParallelFor(int,TFunctionRef<void __cdecl(int)>,bool,bool)

Client.exe FDistanceFieldSceneData::UpdateDistanceFieldObjectBuffers

Client.exe FSceneRenderer::PrepareDistanceFieldScene

Client.exe FDeferredShadingSceneRenderer::Render

Client.exe RenderViewFamilies_RenderThread

Client.exe `FRendererModule::BeginRenderingViewFamilies’::`96’::<lambda_3>::operator()

Client.exe UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(FRHICommandListImmediate &)>::operator()

Also one thing to note, when we find the problematic static mesh (the one asserting), if you look in the Content Browser in the Editor, you will see that its Approx Size is garbage/not initialized, something like this:

[Image Removed]But when you open the asset in the editor, in the top left corner the Approx Size shows is correct. This is actually how I tracked down the issue figuring out what was updating the Approx size in the Content Browser vs in the Static Mesh viewer.

Hey!

I confirm that this is NOT happening with all our statics meshes but only a few of them. I manually fix 3 in the past 2 months. I myself do not get the assert like what I linked in the question above but I believe I found a way to “see” the problem. In the static mesh I attached, when you look at it in the Content Browser, the Approx Size has weird values … currently for me, its showing 0 x 3 x 0. When I open this asset (double click), the Approx Size displayed in the preview has a valid value of 4,201x4,201x291. I need to reimport the mesh for the Approx Size in the Content Browser to actually update and show the correct value of 4,201 x 4,201 x 291. I really feel like something broke when this asset was imported the first time around (why and how I do not know) and once its broken, only a reimport will fix it. I believe all our artists import static meshes via regular FBX import.

As an extra note, one of the 3 StaticMesh I fixed was ever weirder than the rest, doing a reimport of the fbx did not end up calling this line in StaticMesh.cpp:

Owner->BuildFromMeshDescription(*Owner->GetMeshDescription(LodIndex), LODResources[LodIndex])

So I hacked the code to force it to call it by changing this line in StaticMesh.cpp:|

if (bTryLoadingFromDDC)

to

if (NaniteResources.RootData.IsEmpty() == false && bTryLoadingFromDDC)

With this the reimport worked and generated the proper data to “fix” the StaticMesh.

I hope this helps a bit, I know this is not much to go on.

Thank you

Julien

Hi Richard,

Sadly I am unsure how the problematic meshes came into existence. We have a lot of them and only a very small amount have the problem and I cannot find anything wrong when looking at the perforce history.

As for your point about setting bDoFastBuild to false and reimporting the asset, this is pretty much what I have been doing like I said in my original post (An easy fix was to remove the UPROPERTY() above the bDoFastBuild variable, to avoid this being serialized).

This indeed fixes the issue but it’s a hack that I keep locally on my machine to fix the issue if it happens again, not a long term solution :smiley:

Thank you,

Julien

Can confirm that our team just started encountering an instance of this as well. We haven’t tried re-exporting yet. I’ll report back if that solves it, but it does seem like a legit bug.

A reimport alone did not solve it in this case. The size did change after re-import, but is still invalid (from large negative values to 0 x 0 x 0).

Similar to Julien above, I forced it to bypass bTryLoadingFromDDC, but the numbers changed again (but still hit an ensure, and sizes are very large positive values).

Bypassing bFastBuild did appear to fix the issue, however. Sizes look normal, and distance field renders correctly.

Hi,

We are checking the same fix of bypassing bDoFastBuild.

In our case, removing the UPROPERTY attached to bDoFastBuild (effectively bypassing it), AND change the StaticMesh DDC key (effectively ​invalidating all the static mesh DDC entries) solves our issue, seemingly.