Added the FScriptTypedElementHandle. These handles are disarmed when the element they pointing to is destroyed instead of crashing the engine. They do have a performance overhead so using these should be restricted to exposing stuff to blueprint/python.
Reworked the TypedElementList to be a template so that we have both a FTypedElementList and a FScriptTypedElementList from the same source code.
Changed the api of the interfaces so that can now accept and use the scripted version of the handle and list instead of the native ones.
#jira UE-133667
#rb Brooke.Hubert
#preflight 61f89bfaf657e25a5908db48
#ROBOMERGE-AUTHOR: julien.stjean
#ROBOMERGE-SOURCE: CL 18816318 in //UE5/Release-5.0/... via CL 18816336 via CL 18822818
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v910-18824042)
[CL 18824371 by julien stjean in ue5-main branch]
Storing typed elements in UObjects can easily lead to reference leaks if the elements aren't cleared prior to the UObject being left pending GC. This actually made UTypedElementList tricky to use, as you had to remember to manually empty it when you'd finished with it to avoid reference leaks, and we've had several cases now where that was done incorrectly.
To address this issue, we've moved element lists back to being a normal C++ class, FTypedElementList. However, unlike the original version of FTypedElementList (which was itself a UStruct), this version is always heap-allocated and referenced via a TSharedPtr/TSharedRef.
This gives us a nice middle-ground of a well defined lifetime (ie, no lingering references prior to GC) while still being efficient to pass around, including for scripting APIs via FTypedElementListProxy (which just wraps the TSharedPtr in a UStruct).
The downside of this approach is that we need to wrap the FTypedElementList functions that we want to expose to the scripting API (see UTypedElementListLibrary), however that is a far more reasonable burden than requring every user of the typed element framework to know and understand that UTypedElementList had to be manually cleared to avoid potentially hard to find reference leaks (especially if via leaked via scripting APIs).
The core of this change is to TypedElementList.h/.cpp, with TypedElementListFwd.h existing to forward declare the pointer types, and TypedElementListProxy.h and TypedElementListLibrary.h existing to declare the proxy type and wrapped functions used for scripting APIs. TypedElementSelectionInterface.h (and its implementations) provide an example of using FTypedElementListProxy within a scripting API, and the rest of the change is mostly just fallout to transform const UTypedElementList* to FTypedElementListConstRef and UTypedElementList* to FTypedElementListRef.
#rb Brooke.Hubert
#preflight 60d2720c634cd100016c804b
#ROBOMERGE-SOURCE: CL 16776547 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v836-16769935)
[CL 16776582 by jamie dale in ue5-release-engine-test branch]
Previously we relied on element implementations to know what their parent or child elements might be, and to deal with them correctly in the following cases:
1) When "normalising" a selection to be safe for operations like a move or delete, eg) removing elements that are children of other selected elements.
2) When deleting an element, also ensuring that any implicitly destroyed child elements were deselected, eg) deleting an ISM component that has selected static mesh instances.
This approach hurts the modularity of the elements themselves, as it requires that the element implementations have intrinsic knowledge of any other child (or parent) element types that may exist, and to deal with them accordingly when needed. As a concrete example, an ISM component may have child elements in the form of static mesh instance elements, however the generic component element type does not (and should not) directly know that static mesh instance elements exist.
To address this issue, we've added a new interface, UTypedElementHierarchyInterface, which can be used to provide information about the logical parent<->child hierarchy information of elements. This is implemented as follows for our current element types:
- Actor:
- GetParentElement returns an invalid element handle.
- GetChildElements returns the element handles of any components on the actor.
- Component:
- GetParentElement returns its owner actor element handle.
- GetChildElements returns nothing by default, but UActorComponent::GetComponentChildElements may be overridden to control this behavior.
- eg) UInstancedStaticMeshComponent overrides it to return its static mesh instance element handles.
- SMInstance:
- GetParentElement returns its owner ISM component element handle.
- GetChildElements returns nothing.
Now the problems listed above can be solved by using this interface instead of relying on the element implementations:
1) Selection normalization is now handled by UTypedElementSelectionSet, replacing the previous duplicate implementations of UTypedElementCommonActions and UTypedElementViewportInteraction.
2) UTypedElementSelectionSet will now walk and also update the selection state of child elements, when asked to via the FTypedElementSelectionOptions.
Breaking Changes:
- UTypedElementCommonActions::GetElementsForAction and UTypedElementViewportInteraction::GetSelectedElementsToMove have been removed, in favor of using the selection normalization functions of UTypedElementSelectionSet.
- Note: You may still favor using FLevelEditorViewportClient::GetElementsToManipulate, as it will have removed normalized elements that also cannot be moved by a gizmo.
- UTypedElementCommonActions::DeleteElements and UTypedElementCommonActions::DuplicateElements have been removed, as these functions operated on an unnormalized selection and could be unsafe. UTypedElementCommonActions::DeleteNormalizedElements and UTypedElementCommonActions::DuplicateNormalizedElements should be used instead.
- AGroupActor::ForEachActorInGroup and AGroupActor::ForEachMovableActorInGroup now take a second AGroupActor* argument in their callback.
#rb Brooke.Hubert
#preflight 60ca33a678c3b00001e8f5df
#ROBOMERGE-SOURCE: CL 16705229 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v835-16672529)
[CL 16705233 by jamie dale in ue5-release-engine-test branch]
Note: This is disabled by default, but can be enabled by setting the "TypedElements.EnableFoliageInstanceElements" CVar to "1" (alongside setting "TypedElements.EnableSMInstanceElements" to "1").
Notable changes:
- AInstancedFoliageActor now implements ISMInstanceManager, and provides an implementation that works for ISMC foliage instances.
- ISMActor foliage is not yet supported, but also not currently used.
- ISMInstanceManager now has functions for NotifySMInstanceMovementX and NotifySMInstanceSelectionChanged, which are called from the SMInstance typed element implementation.
- USMInstanceElementDetailsProxyObject now calls the NotifySMInstanceMovementX functions for transform edits.
- FFoliageStaticMesh::Reapply now re-uses existing instances where possible, to avoid invalidating mapped FSMInstanceElementId values.
- FFoliageInfo now keeps track of all moving instances, rather than if any instances are currently moving.
- Various FFoliageInfo/FFoliageImpl functions now take their indices via TArrayView rather than TArray.
- UEngineElementsLibrary now has public functions for ReplaceEditorXElementHandles, which exposes the previously private re-instancing logic used by UEngineElementsLibrary::OnObjectsReplaced.
- This allows static mesh instance elements to be re-instanced if needed (eg, if moving them to an ISMC in a different world partition).
#rb Brooke.Hubert
#preflight 60b835ef6073fb00015e3cd1
[CL 16552742 by Jamie Dale in ue5-main branch]
The editor viewport would previously skip the GizmoManipulationStopped call if nothing had actually moved, but this causes an imbalance in the APIs which call NotifyMovementStarted and NotifyMovementEnded.
GizmoManipulationStopped is now always called, but with an extra argument saying if anything actually moved.
#rb Brooke.Hubert
#preflight 60aeb2de1db8a70001cbe077
[CL 16537833 by Jamie Dale in ue5-main branch]
The static mesh instance (SMInstance) element type used to directly edit the data on the ISM component, which can cause issues if the ISM component is owned by an actor that has additional bookkeeping data.
To solve this there is now an extra level of indirection that the actor owning a component can use to customize the default per-instance editing behavior, or even to disable per-instance editing entirely:
- FSMInstanceManager is a handle that replaces direct use of FSMInstanceId within the SMInstance element implementation, and promotes performing actions via the instance manager interface rather than on the ISM component directly.
- ISMInstanceManager is an interface that actors owning an ISM component can implement in order to customize the default SMInstance editing behavior. The ISM component also implements this interface for the cases where it manages itself.
- ISMInstanceManagerProvider is an interface that actors owning an ISM component can implement to either redirect the request to get the instance manager instance, or to disable per-instance editing entirely by returning a null instance manager instance.
The instance manager instance used is resolved from the ISM component using the following logic:
- If the owner actor implements the ISMInstanceManagerProvider interface, then that API is used to provide the instance manager. No further fallback happens, and a null instance manager instance will disable per-instance editing.
- If the owner actor implements the ISMInstanceManager interface, then the owner actor is used as the instance manager.
- Otherwise the ISM component is used as the instance manager.
ALightWeightInstanceStaticMeshManager provides an example of using ISMInstanceManager to keep extra bookkeeping data up-to-date, and AISMPartitionActor provides an example of using ISMInstanceManagerProvider to disable per-instance editing.
AISMPartitionActor will be updated to correctly support SMInstance editing in a future change, by forwarding the request on to the client that owns the respective instance.
#rb Brooke.Hubert, Fred.Kimberley
#preflight 60a56a7c7370d300015f23fb
[CL 16422692 by Jamie Dale in ue5-main branch]
These existed as an optimization for USelection to quickly know if an object was selected, without having to test its array. UTypedElementList has that optimization embedded, so at this point the selection annotations are just extra state to keep in-sync, and failure to do so has already lead to bugs.
This change removes them in the simplest way possible, by using a function pointer to callback into editor code from core/engine code (as core/engine cannot directly access the global selection state), though longer-term we probably want to remove IsSelected/IsSelectedInEditor from UObject and let specific systems provide that functionality if they need it (eg, AActor/UActorComponent could provide it by getting the selection set from the owner world, rather than the global state).
#rb Brooke.Hubert
#preflight 607de942e7a5ac0001a6b7d6
[CL 16075785 by Jamie Dale in ue5-main branch]
This was to optimize selecting groups, but we've handled this case elsewhere by just skipping the group logic if the group is already in the list of things that we've processed. This applies that approach to getting the elements to move.
#rb Brooke.Hubert
#ROBOMERGE-SOURCE: CL 15890607 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v786-15839533)
[CL 15890762 by jamie dale in ue5-main branch]
This avoids repeatedly processing each actor in a group during a select/deselect operation, as once the group itself has been selected/deselected, we have by proxy processed its grouped actors too. The only caveat to that is if a selection was previously performed with SetAllowGroups set to false (not the default), but that's advanced usage and could be resolved by toggling the group selection again.
#jira UE-110998
#rb Brooke.Hubert
#ROBOMERGE-SOURCE: CL 15813372 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v783-15756269)
[CL 15814438 by jamie dale in ue5-main branch]
- Moved the logic for managing the offset when pasting/duplicating out of the internal Paste/DuplicateActors implementation, and instead pass the desired offset into the duplicate functions.
- Made the function for getting the list of elements to operate on for a common action more generic, and added helper functions to delete and duplicate the current selection set.
- Added a CanDuplicateElements function to UTypedElementWorldInterface.
- Implemented duplication offset for static mesh instances.
#rb Brooke.Hubert
#ROBOMERGE-SOURCE: CL 15750144 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v781-15675533)
[CL 15753927 by jamie dale in ue5-main branch]
Adds support for selection, gizmo manipulation, property editing, duplication, deletion, and undo/redo for Static Mesh instances within the main Level Editor, without having to enter an alternative editing mode.
Note: This is currently disabled via UE_ENABLE_SMINSTANCE_ELEMENTS
#rb Brooke.Hubert
#ROBOMERGE-SOURCE: CL 15481362 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v771-15082668)
[CL 15481376 by jamie dale in ue5-main branch]
Each group actor is now only enumerated once at the end, rather than once for each selected actor within a group
#jira UE-108343
#rb Brooke.Hubert
#ROBOMERGE-SOURCE: CL 15475127 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v771-15082668)
[CL 15475131 by jamie dale in ue5-main branch]
This lets UTypedElementSelectionSet (which has minimal dependencies) to be used by the Engine and InteractiveToolsFramework modules
#rb Brooke.Hubert
[CL 15219316 by Jamie Dale in ue5-main branch]