Description

Geometry Collections (GC) built with "Use Root Proxies" rendering/transform positions ignore the original root node rotation and can lead to unexpected behaviors.

If the root node of the GC was rotated at the moment it was created, some actions will display the odd behavior of discarding the original rotation. I was able to reproduce this by spawning the GC from the Content Browser, duplicating the GCActor in the scene, or by moving the GC to a new location in the scene and undoing the movement. In these cases the rotation of the "root" is set to zero and this gives the impression that the GC was wrongly rotated.

Another licensee joined the conversation and shared his analysis and proposed solution:
------
To fully reproduce the issue, the first static mesh selected in the group during GC creation (i.e. the one that will be used as the root node during root proxy transform calculation in the call to FracturedGeometryCollection->SetRootProxiesFromGeometrySources() on line 1013 of FractureToolGenerators.cpp) must possess a rotation. After creating the GC in this manner, you should see the visual pop.

As for the solution I found, I moved the section of code responsible for the root proxy transform generation starting on line 1010 of FractureToolGenerators.cpp to below the call to FracturedGeometryCollection->InvalidateCollection() on line 1025. This was done as the calls to AddSingleRootNodeIfRequired() and FracturedGeometryCollection->InvalidateCollection() directly above may result in the creation of a new root node, which invalidates the previously generated root proxy transforms as they were generated relative to a stale root node whose transform may differ from the new root node's. Additionally, UGeometryCollectionComponent::RefreshCustomRenderer() uses the new root node's transform as the component space root transform which will cause the root proxy to be rendered incorrectly if the root proxy transforms are generated prior to generating the new root node.

------
During my tests I was able to reproduce this issue in UE versions 5.6, 5.7, and 5.8 (CL45755840).

I found some issues that might be related to this in the Issue Tracker website:
[Link Removed]
[Link Removed]

REPRO PROJECT HERE:

[Link Removed]

Steps to Reproduce

On a blank project:
Spawn 5 cubes in the scene, and arrange them as an L shape
Rotate one of the cubes (Pitch=0.000000,Yaw=-10.000000,Roll=0.000000)
With the rotated cube selected, press and hold SHIFT and click on the other cubes until all are selected
Release SHIFT
Go to Fracture Mode (SHIFT+6)
Press New
Confirm UseRootProxies is enabled (true by default)
Save the Geometry Collection (GC)
Go back to Selection Mode (SHIFT+1)
Select the newly created GC and change it's location
Press CTRL+Z to undo the location change

Expected behavior: the GC should go back to it's original location
Actual behavior: the GC is rotated in relation to the original location by the negative of the angle applied to the root cube.

Other ways to reproduce the issue:
Spawn the GC from the Content Browser
Duplicating the GC in the scene

Note that if you go to Fracture Mode while the rotated GC is selected, the GC is rendered in the correct position. Going back to Selection Mode once again displays a wrong rotation.

Callstack

> UnrealEditor-FractureEditor.dll!UFractureToolGenerateAsset::ConvertActorsToGeometryCollection(const UFractureToolGenerateAsset::FCreationParameters & Params, bool bAddInternalMaterials, TArray<AActor *,TSizedDefaultAllocator<32>> & Actors) Line 1012 C++
UnrealEditor-FractureEditor.dll!UFractureToolGenerateAsset::OnGenerateAssetPathChosen(const UFractureToolGenerateAsset::FCreationParameters & Params, TArray<AActor *,TSizedDefaultAllocator<32>> Actors) Line 798 C++
[Inline Frame] UnrealEditor-FractureEditor.dll!Invoke(void(UFractureToolGenerateAsset::*)(const UFractureToolGenerateAsset::FCreationParameters &, TArray<AActor *,TSizedDefaultAllocator<32>>)) Line 66 C++
UnrealEditor-FractureEditor.dll!??$ApplyAfter@AEBQ8UFractureToolGenerateAsset@@EAAXAEBUFCreationParameters@1@V?$TArray@PEAVAActor@@V?$TSizedDefaultAllocator@$0CA@@@@@@ZAEAPEAV1@AEBU21@@?$TTupleBase@U?$TIntegerSequence@I$0A@@@V?$TArray@PEAVAActor@@V?$TSizedDefaultAllocator@$0CA@@@@@@Tuple@Private@Core@UE@@QEGBA?A_TAEBQ8UFractureToolGenerateAsset@@EAAXAEBUFCreationParameters@5@V?$TArray@PEAVAActor@@V?$TSizedDefaultAllocator@$0CA@@@@@@ZAEAPEAV5@0@Z(void(UFractureToolGenerateAsset::*)(const UFractureToolGenerateAsset::FCreationParameters &, TArray<AActor *,TSizedDefaultAllocator<32>>) & Func, UFractureToolGenerateAsset * & <Args_0>, const UFractureToolGenerateAsset::FCreationParameters & <Args_1>) Line 320 C++
UnrealEditor-FractureEditor.dll!TBaseUObjectMethodDelegateInstance<0,UFractureToolGenerateAsset,void __cdecl(UFractureToolGenerateAsset::FCreationParameters const & __ptr64),FDefaultDelegateUserPolicy,TArray<AActor * __ptr64,TSizedDefaultAllocator<32>>>::ExecuteIfSafe(const UFractureToolGenerateAsset::FCreationParameters & <Params_0>) Line 689 C++
[Inline Frame] UnrealEditor-FractureEditor.dll!TDelegate<void __cdecl(UFractureToolGenerateAsset::FCreationParameters const &),FDefaultDelegateUserPolicy>::ExecuteIfBound(const UFractureToolGenerateAsset::FCreationParameters &) Line 635 C++
UnrealEditor-FractureEditor.dll!SCreateGeometryCollectionFromObject::OnCreateAssetFromActorClicked() Line 563 C++
[Inline Frame] UnrealEditor-FractureEditor.dll!Invoke(FReply(SCreateGeometryCollectionFromObject::*)()) Line 66 C++
[Inline Frame] UnrealEditor-FractureEditor.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(FReply(SCreateGeometryCollectionFromObject::*)() &) Line 320 C++
UnrealEditor-FractureEditor.dll!V::TBaseSPMethodDelegateInstance::Execute() Line 292 C++
[Inline Frame] UnrealEditor-Slate.dll!TDelegate<FReply __cdecl(void),FDefaultDelegateUserPolicy>::Execute() Line 614 C++
UnrealEditor-Slate.dll!SButton::ExecuteOnClick() Line 495 C++
UnrealEditor-Slate.dll!SButton::OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MouseEvent) Line 419 C++
UnrealEditor-Slate.dll!??R<lambda_2>@?7??RoutePointerUpEvent@FSlateApplication@@QEAA?AVFReply@@AEBVFWidgetPath@@AEBUFPointerEvent@@@Z@QEBA@AEBVFArrangedWidget@@1@Z(const FArrangedWidget & TargetWidget, const FPointerEvent & Event) Line 5367 C++
UnrealEditor-Slate.dll!??$Route@VFReply@@VFToLeafmostPolicy@FEventRouter@@UFPointerEvent@@V<lambda_2>@?7??RoutePointerUpEvent@FSlateApplication@@QEAA?AV1@AEBVFWidgetPath@@AEBU4@@Z@@FEventRouter@@SA?AVFReply@@PEAVFSlateApplication@@VFToLeafmostPolicy@0@UFPointerEvent@@AEBV<lambda_2>@?7??RoutePointerUpEvent@2@QEAA?AV1@AEBVFWidgetPath@@AEBU4@@Z@W4ESlateDebuggingInputEvent@@@Z(FSlateApplication * ThisApplication, FEventRouter::FToLeafmostPolicy RoutingPolicy, FPointerEvent EventCopy, const FSlateApplication::RoutePointerUpEvent::__l8::<lambda_2> & Lambda, ESlateDebuggingInputEvent DebuggingInputEvent) Line 456 C++
UnrealEditor-Slate.dll!FSlateApplication::RoutePointerUpEvent(const FWidgetPath & WidgetsUnderPointer, const FPointerEvent & PointerEvent) Line 5353 C++
UnrealEditor-Slate.dll!FSlateApplication::ProcessMouseButtonUpEvent(const FPointerEvent & MouseEvent) Line 5938 C++
UnrealEditor-Slate.dll!FSlateApplication::OnMouseUp(const EMouseButtons::Type Button, const UE::Math::TVector2<double> CursorPos) Line 5894 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage & DeferredMessage) Line 3052 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::DeferMessage(TSharedPtr<FWindowsWindow,1> & NativeWindow, HWND__ * InHWnd, unsigned int InMessage, unsigned __int64 InWParam, __int64 InLParam, int MouseX, int MouseY, unsigned int RawInputFlags) Line 3570 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::ProcessMessage(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 2725 C++
[Inline Frame] UnrealEditor-ApplicationCore.dll!WindowsApplication_WndProc(HWND__ *) Line 1716 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::AppWndProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 1722 C++
[External Code]
[Inline Frame] UnrealEditor-ApplicationCore.dll!WinPumpMessages() Line 116 C++
UnrealEditor-ApplicationCore.dll!FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop) Line 145 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5554 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 60 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 187 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 271 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 339 C++
[External Code]

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-353918 in the post.

0
Login to Vote

Unresolved
CreatedNov 17, 2025
UpdatedFeb 23, 2026
View Jira Issue