2020-01-08 17:11:23 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-12-19 18:07:47 -05:00
# include "EditPivotTool.h"
# include "InteractiveToolManager.h"
# include "InteractiveGizmoManager.h"
# include "ToolBuilderUtil.h"
# include "ToolSetupUtil.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/DynamicMesh3.h"
2021-02-25 20:03:29 -04:00
# include "Mechanics/DragAlignmentMechanic.h"
2020-01-27 20:11:15 -05:00
# include "MeshAdapterTransforms.h"
# include "MeshDescriptionAdapter.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshTransforms.h"
2019-12-19 18:07:47 -05:00
# include "BaseBehaviors/ClickDragBehavior.h"
2020-10-22 19:19:16 -04:00
# include "ToolSceneQueriesUtil.h"
2021-02-25 05:39:27 -04:00
# include "Physics/ComponentCollisionUtil.h"
2019-12-19 18:07:47 -05:00
# include "BaseGizmos/GizmoComponents.h"
2021-05-20 16:39:39 -04:00
# include "BaseGizmos/TransformGizmoUtil.h"
2019-12-19 18:07:47 -05:00
# include "Components/PrimitiveComponent.h"
2021-06-15 02:46:37 -04:00
# include "Components/InstancedStaticMeshComponent.h"
2019-12-19 18:07:47 -05:00
# include "Engine/World.h"
2021-03-11 11:40:03 -04:00
# include "TargetInterfaces/MeshDescriptionCommitter.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
# include "ToolTargetManager.h"
2021-06-22 11:55:16 -04:00
# include "ModelingToolTargetUtil.h"
2021-03-11 11:40:03 -04:00
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2019-12-19 18:07:47 -05:00
# define LOCTEXT_NAMESPACE "UEditPivotTool"
/*
* ToolBuilder
*/
2021-11-23 09:42:40 -05:00
UMultiSelectionMeshEditingTool * UEditPivotToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2021-03-11 11:40:03 -04:00
{
2021-11-23 09:42:40 -05:00
return NewObject < UEditPivotTool > ( SceneState . ToolManager ) ;
2019-12-19 18:07:47 -05:00
}
2020-01-27 20:11:15 -05:00
void UEditPivotToolActionPropertySet : : PostAction ( EEditPivotToolActions Action )
{
if ( ParentTool . IsValid ( ) )
{
ParentTool - > RequestAction ( Action ) ;
}
}
2019-12-19 18:07:47 -05:00
/*
* Tool
*/
UEditPivotTool : : UEditPivotTool ( )
{
}
void UEditPivotTool : : Setup ( )
{
UInteractiveTool : : Setup ( ) ;
UClickDragInputBehavior * ClickDragBehavior = NewObject < UClickDragInputBehavior > ( this ) ;
ClickDragBehavior - > Initialize ( this ) ;
AddInputBehavior ( ClickDragBehavior ) ;
TransformProps = NewObject < UEditPivotToolProperties > ( ) ;
AddToolPropertySource ( TransformProps ) ;
2020-01-27 20:11:15 -05:00
EditPivotActions = NewObject < UEditPivotToolActionPropertySet > ( this ) ;
EditPivotActions - > Initialize ( this ) ;
AddToolPropertySource ( EditPivotActions ) ;
2019-12-19 18:07:47 -05:00
ResetActiveGizmos ( ) ;
SetActiveGizmos_Single ( false ) ;
UpdateSetPivotModes ( true ) ;
2021-02-25 20:03:29 -04:00
DragAlignmentMechanic = NewObject < UDragAlignmentMechanic > ( this ) ;
DragAlignmentMechanic - > Setup ( this ) ;
DragAlignmentMechanic - > AddToGizmo ( ActiveGizmos [ 0 ] . TransformGizmo ) ;
2020-01-27 20:11:15 -05:00
Precompute ( ) ;
2019-12-19 18:07:47 -05:00
2020-01-27 20:11:15 -05:00
FText AllTheWarnings = LOCTEXT ( " EditPivotWarning " , " WARNING: This Tool will Modify the selected StaticMesh Assets! If you do not wish to modify the original Assets, please make copies in the Content Browser first! " ) ;
// detect and warn about any meshes in selection that correspond to same source data
2021-03-11 11:40:03 -04:00
bool bSharesSources = GetMapToSharedSourceData ( MapToFirstOccurrences ) ;
2020-01-27 20:11:15 -05:00
if ( bSharesSources )
{
2021-06-15 02:46:37 -04:00
AllTheWarnings = FText : : Format ( FTextFormat : : FromString ( " {0} \n \n {1} " ) , AllTheWarnings , LOCTEXT ( " EditPivotSharedAssetsWarning " , " WARNING: Multiple selected meshes share the same source Asset! Each Asset can only have one baked pivot, some results will be incorrect. " ) ) ;
}
bool bHasISMCs = false ;
for ( int32 k = 0 ; k < Targets . Num ( ) ; + + k )
{
2021-06-22 11:55:16 -04:00
if ( Cast < UInstancedStaticMeshComponent > ( UE : : ToolTarget : : GetTargetComponent ( Targets [ k ] ) ) ! = nullptr )
2021-06-15 02:46:37 -04:00
{
bHasISMCs = true ;
}
}
if ( bHasISMCs )
{
AllTheWarnings = FText : : Format ( FTextFormat : : FromString ( " {0} \n \n {1} " ) , AllTheWarnings , LOCTEXT ( " EditPivotISMCWarning " , " WARNING: Some selected objects are Instanced Components. Pivot of Instances will be modified, instead of Asset. " ) ) ;
2020-01-27 20:11:15 -05:00
}
GetToolManager ( ) - > DisplayMessage ( AllTheWarnings , EToolMessageLevel : : UserWarning ) ;
2020-03-10 14:00:36 -04:00
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Edit Pivot " ) ) ;
2020-03-10 14:00:36 -04:00
GetToolManager ( ) - > DisplayMessage (
2021-02-25 20:03:29 -04:00
LOCTEXT ( " OnStartTool " , " This tool edits the Pivot (Origin) of the input assets. Hold Ctrl while using the gizmo to align to scene. Enable Snap Dragging and click+drag to place gizmo directly into clicked position. " ) ,
2020-03-10 14:00:36 -04:00
EToolMessageLevel : : UserNotification ) ;
2019-12-19 18:07:47 -05:00
}
void UEditPivotTool : : Shutdown ( EToolShutdownType ShutdownType )
{
2021-02-25 20:03:29 -04:00
DragAlignmentMechanic - > Shutdown ( ) ;
2019-12-19 18:07:47 -05:00
FFrame3d CurPivotFrame ( ActiveGizmos [ 0 ] . TransformProxy - > GetTransform ( ) ) ;
2021-11-23 09:42:40 -05:00
GetToolManager ( ) - > GetPairedGizmoManager ( ) - > DestroyAllGizmosByOwner ( this ) ;
2019-12-19 18:07:47 -05:00
if ( ShutdownType = = EToolShutdownType : : Accept )
{
UpdateAssets ( CurPivotFrame ) ;
}
}
2020-07-16 08:23:15 -04:00
void VertexIteration ( const FMeshDescription * Mesh , TFunctionRef < void ( int32 , const FVector & ) > ApplyFunc )
2020-01-27 20:11:15 -05:00
{
2021-05-05 15:07:25 -04:00
TArrayView < const FVector3f > VertexPositions = Mesh - > GetVertexPositions ( ) . GetRawArray ( ) ;
2020-01-27 20:11:15 -05:00
2020-07-16 08:23:15 -04:00
for ( const FVertexID VertexID : Mesh - > Vertices ( ) . GetElementIDs ( ) )
2020-01-27 20:11:15 -05:00
{
2020-07-16 08:23:15 -04:00
const FVector Position = VertexPositions [ VertexID ] ;
2020-01-27 20:11:15 -05:00
ApplyFunc ( VertexID . GetValue ( ) , Position ) ;
}
}
void UEditPivotTool : : Precompute ( )
{
ObjectBounds = FAxisAlignedBox3d : : Empty ( ) ;
WorldBounds = FAxisAlignedBox3d : : Empty ( ) ;
2021-03-11 11:40:03 -04:00
int NumComponents = Targets . Num ( ) ;
2020-01-27 20:11:15 -05:00
if ( NumComponents = = 1 )
{
2021-06-22 11:55:16 -04:00
Transform = UE : : ToolTarget : : GetLocalToWorldTransform ( Targets [ 0 ] ) ;
2020-01-27 20:11:15 -05:00
2021-06-22 11:55:16 -04:00
const FMeshDescription * Mesh = UE : : ToolTarget : : GetMeshDescription ( Targets [ 0 ] ) ;
2020-01-27 20:11:15 -05:00
VertexIteration ( Mesh , [ & ] ( int32 VertexID , const FVector & Position ) {
2021-03-30 21:25:22 -04:00
ObjectBounds . Contain ( ( FVector3d ) Position ) ;
WorldBounds . Contain ( Transform . TransformPosition ( ( FVector3d ) Position ) ) ;
2020-01-27 20:11:15 -05:00
} ) ;
}
else
{
2021-03-18 12:46:27 -04:00
Transform = UE : : Geometry : : FTransform3d : : Identity ( ) ;
2021-03-11 11:40:03 -04:00
for ( int k = 0 ; k < NumComponents ; + + k )
2020-01-27 20:11:15 -05:00
{
2021-06-22 11:55:16 -04:00
UE : : Geometry : : FTransform3d CurTransform = UE : : ToolTarget : : GetLocalToWorldTransform ( Targets [ k ] ) ;
const FMeshDescription * Mesh = UE : : ToolTarget : : GetMeshDescription ( Targets [ k ] ) ;
2020-01-27 20:11:15 -05:00
VertexIteration ( Mesh , [ & ] ( int32 VertexID , const FVector & Position ) {
2021-03-30 21:25:22 -04:00
ObjectBounds . Contain ( CurTransform . TransformPosition ( ( FVector3d ) Position ) ) ;
WorldBounds . Contain ( CurTransform . TransformPosition ( ( FVector3d ) Position ) ) ;
2020-01-27 20:11:15 -05:00
} ) ;
}
}
}
void UEditPivotTool : : RequestAction ( EEditPivotToolActions ActionType )
{
if ( PendingAction = = EEditPivotToolActions : : NoAction )
{
PendingAction = ActionType ;
}
}
2019-12-19 18:07:47 -05:00
2020-04-18 18:42:59 -04:00
void UEditPivotTool : : OnTick ( float DeltaTime )
2019-12-19 18:07:47 -05:00
{
2020-01-27 20:11:15 -05:00
if ( PendingAction ! = EEditPivotToolActions : : NoAction )
{
ApplyAction ( PendingAction ) ;
PendingAction = EEditPivotToolActions : : NoAction ;
}
2019-12-19 18:07:47 -05:00
}
void UEditPivotTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
2021-02-25 20:03:29 -04:00
DragAlignmentMechanic - > Render ( RenderAPI ) ;
2019-12-19 18:07:47 -05:00
}
2020-01-08 13:26:18 -05:00
void UEditPivotTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
2019-12-19 18:07:47 -05:00
{
}
void UEditPivotTool : : UpdateSetPivotModes ( bool bEnableSetPivot )
{
for ( FEditPivotTarget & Target : ActiveGizmos )
{
Target . TransformProxy - > bSetPivotMode = bEnableSetPivot ;
}
}
void UEditPivotTool : : RegisterActions ( FInteractiveToolActionSet & ActionSet )
{
}
2020-01-27 20:11:15 -05:00
void UEditPivotTool : : ApplyAction ( EEditPivotToolActions ActionType )
{
switch ( ActionType )
{
case EEditPivotToolActions : : Center :
case EEditPivotToolActions : : Bottom :
case EEditPivotToolActions : : Top :
case EEditPivotToolActions : : Left :
case EEditPivotToolActions : : Right :
case EEditPivotToolActions : : Front :
case EEditPivotToolActions : : Back :
SetPivotToBoxPoint ( ActionType ) ;
break ;
2021-04-13 10:27:54 -04:00
case EEditPivotToolActions : : WorldOrigin :
SetPivotToWorldOrigin ( ) ;
break ;
2020-01-27 20:11:15 -05:00
}
}
void UEditPivotTool : : SetPivotToBoxPoint ( EEditPivotToolActions ActionPoint )
{
FAxisAlignedBox3d UseBox = ( EditPivotActions - > bUseWorldBox ) ? WorldBounds : ObjectBounds ;
FVector3d Point = UseBox . Center ( ) ;
if ( ActionPoint = = EEditPivotToolActions : : Bottom | | ActionPoint = = EEditPivotToolActions : : Top )
{
Point . Z = ( ActionPoint = = EEditPivotToolActions : : Bottom ) ? UseBox . Min . Z : UseBox . Max . Z ;
}
else if ( ActionPoint = = EEditPivotToolActions : : Left | | ActionPoint = = EEditPivotToolActions : : Right )
{
Point . Y = ( ActionPoint = = EEditPivotToolActions : : Left ) ? UseBox . Min . Y : UseBox . Max . Y ;
}
else if ( ActionPoint = = EEditPivotToolActions : : Back | | ActionPoint = = EEditPivotToolActions : : Front )
{
Point . X = ( ActionPoint = = EEditPivotToolActions : : Front ) ? UseBox . Min . X : UseBox . Max . X ;
}
FTransform NewTransform ;
if ( EditPivotActions - > bUseWorldBox = = false )
{
FFrame3d LocalFrame ( Point ) ;
LocalFrame . Transform ( Transform ) ;
NewTransform = LocalFrame . ToFTransform ( ) ;
}
else
{
NewTransform = FTransform ( ( FVector ) Point ) ;
}
ActiveGizmos [ 0 ] . TransformGizmo - > SetNewGizmoTransform ( NewTransform ) ;
}
2019-12-19 18:07:47 -05:00
2021-04-13 10:27:54 -04:00
void UEditPivotTool : : SetPivotToWorldOrigin ( )
{
ActiveGizmos [ 0 ] . TransformGizmo - > SetNewGizmoTransform ( FTransform ( ) ) ;
}
2019-12-19 18:07:47 -05:00
void UEditPivotTool : : SetActiveGizmos_Single ( bool bLocalRotations )
{
check ( ActiveGizmos . Num ( ) = = 0 ) ;
FEditPivotTarget Transformable ;
Transformable . TransformProxy = NewObject < UTransformProxy > ( this ) ;
Transformable . TransformProxy - > bRotatePerObject = bLocalRotations ;
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2019-12-19 18:07:47 -05:00
{
2021-06-22 11:55:16 -04:00
Transformable . TransformProxy - > AddComponent ( UE : : ToolTarget : : GetTargetComponent ( Targets [ ComponentIdx ] ) ) ;
2019-12-19 18:07:47 -05:00
}
2021-11-23 09:42:40 -05:00
Transformable . TransformGizmo = UE : : TransformGizmoUtil : : CreateCustomTransformGizmo ( GetToolManager ( ) - > GetPairedGizmoManager ( ) ,
2020-01-27 20:11:15 -05:00
ETransformGizmoSubElements : : StandardTranslateRotate , this
) ;
2020-09-24 00:43:27 -04:00
Transformable . TransformGizmo - > SetActiveTarget ( Transformable . TransformProxy , GetToolManager ( ) ) ;
2019-12-19 18:07:47 -05:00
2020-01-27 20:11:15 -05:00
Transformable . TransformGizmo - > bUseContextCoordinateSystem = false ;
Transformable . TransformGizmo - > CurrentCoordinateSystem = EToolContextCoordinateSystem : : Local ;
2019-12-19 18:07:47 -05:00
ActiveGizmos . Add ( Transformable ) ;
}
void UEditPivotTool : : ResetActiveGizmos ( )
{
2021-11-23 09:42:40 -05:00
GetToolManager ( ) - > GetPairedGizmoManager ( ) - > DestroyAllGizmosByOwner ( this ) ;
2019-12-19 18:07:47 -05:00
ActiveGizmos . Reset ( ) ;
}
// does not make sense that CanBeginClickDragSequence() returns a RayHit? Needs to be an out-argument...
FInputRayHit UEditPivotTool : : CanBeginClickDragSequence ( const FInputDeviceRay & PressPos )
{
if ( TransformProps - > bEnableSnapDragging = = false | | ActiveGizmos . Num ( ) = = 0 )
{
return FInputRayHit ( ) ;
}
2021-02-25 20:03:29 -04:00
FHitResult Result ;
2021-12-09 14:46:09 -05:00
bool bWorldHit = ToolSceneQueriesUtil : : FindNearestVisibleObjectHit ( this , Result , PressPos . WorldRay ) ;
2019-12-19 18:07:47 -05:00
2021-02-25 20:03:29 -04:00
if ( ! bWorldHit )
2019-12-19 18:07:47 -05:00
{
2021-02-25 20:03:29 -04:00
return FInputRayHit ( ) ;
2019-12-19 18:07:47 -05:00
}
2021-02-25 20:03:29 -04:00
return FInputRayHit ( Result . Distance , Result . ImpactNormal ) ;
2019-12-19 18:07:47 -05:00
}
void UEditPivotTool : : OnClickPress ( const FInputDeviceRay & PressPos )
{
FInputRayHit HitPos = CanBeginClickDragSequence ( PressPos ) ;
check ( HitPos . bHit ) ;
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " TransformToolTransformTxnName " , " SnapDrag " ) ) ;
FEditPivotTarget & ActiveTarget = ActiveGizmos [ 0 ] ;
USceneComponent * GizmoComponent = ActiveTarget . TransformGizmo - > GetGizmoActor ( ) - > GetRootComponent ( ) ;
StartDragTransform = GizmoComponent - > GetComponentToWorld ( ) ;
}
void UEditPivotTool : : OnClickDrag ( const FInputDeviceRay & DragPos )
{
bool bRotate = ( TransformProps - > RotationMode ! = EEditPivotSnapDragRotationMode : : Ignore ) ;
float NormalSign = ( TransformProps - > RotationMode = = EEditPivotSnapDragRotationMode : : AlignFlipped ) ? - 1.0f : 1.0f ;
FHitResult Result ;
2021-12-09 14:46:09 -05:00
bool bWorldHit = ToolSceneQueriesUtil : : FindNearestVisibleObjectHit ( this , Result , DragPos . WorldRay ) ;
2019-12-19 18:07:47 -05:00
if ( bWorldHit = = false )
{
return ;
}
FVector HitPos = Result . ImpactPoint ;
2021-10-20 22:12:32 -04:00
FVector TargetNormal = ( NormalSign ) * Result . Normal ;
2019-12-19 18:07:47 -05:00
FQuaterniond AlignRotation = ( bRotate ) ?
2021-03-30 21:25:22 -04:00
FQuaterniond ( FVector3d : : UnitZ ( ) , ( FVector3d ) TargetNormal ) : FQuaterniond : : Identity ( ) ;
2019-12-19 18:07:47 -05:00
FTransform NewTransform = StartDragTransform ;
NewTransform . SetRotation ( ( FQuat ) AlignRotation ) ;
NewTransform . SetTranslation ( HitPos ) ;
FEditPivotTarget & ActiveTarget = ActiveGizmos [ 0 ] ;
2021-02-25 20:03:29 -04:00
ActiveTarget . TransformGizmo - > SetNewGizmoTransform ( NewTransform ) ;
2019-12-19 18:07:47 -05:00
}
void UEditPivotTool : : OnClickRelease ( const FInputDeviceRay & ReleasePos )
{
OnTerminateDragSequence ( ) ;
}
void UEditPivotTool : : OnTerminateDragSequence ( )
{
FEditPivotTarget & ActiveTarget = ActiveGizmos [ 0 ] ;
USceneComponent * GizmoComponent = ActiveTarget . TransformGizmo - > GetGizmoActor ( ) - > GetRootComponent ( ) ;
FTransform EndDragtransform = GizmoComponent - > GetComponentToWorld ( ) ;
TUniquePtr < FComponentWorldTransformChange > Change = MakeUnique < FComponentWorldTransformChange > ( StartDragTransform , EndDragtransform ) ;
GetToolManager ( ) - > EmitObjectChange ( GizmoComponent , MoveTemp ( Change ) ,
LOCTEXT ( " TransformToolTransformTxnName " , " SnapDrag " ) ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
void UEditPivotTool : : UpdateAssets ( const FFrame3d & NewPivotWorldFrame )
{
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " EditPivotToolTransactionName " , " Edit Pivot " ) ) ;
FTransform NewWorldTransform = NewPivotWorldFrame . ToFTransform ( ) ;
2020-01-27 20:11:15 -05:00
FTransform NewWorldInverse = NewWorldTransform . Inverse ( ) ;
TArray < FTransform > OriginalTransforms ;
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2019-12-19 18:07:47 -05:00
{
2021-06-22 11:55:16 -04:00
OriginalTransforms . Add ( ( FTransform ) UE : : ToolTarget : : GetLocalToWorldTransform ( Targets [ ComponentIdx ] ) ) ;
2019-12-19 18:07:47 -05:00
}
2021-03-11 11:40:03 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
2020-01-27 20:11:15 -05:00
{
2021-06-22 11:55:16 -04:00
UPrimitiveComponent * Component = UE : : ToolTarget : : GetTargetComponent ( Targets [ ComponentIdx ] ) ;
2021-06-15 02:46:37 -04:00
Component - > Modify ( ) ;
2021-06-22 11:55:16 -04:00
UInstancedStaticMeshComponent * InstancedComponent = Cast < UInstancedStaticMeshComponent > ( Component ) ;
2021-06-15 02:46:37 -04:00
if ( InstancedComponent ! = nullptr )
{
// For ISMC, we will not bake in a mesh transform, instead we will update the instance transforms relative to the new pivot
// TODO: this could be optional, and another alternative would be to bake the pivot to the mesh and then update
// all the instance transforms so they stay in the same position?
// save world transforms
int32 NumInstances = InstancedComponent - > GetInstanceCount ( ) ;
TArray < FTransform > WorldTransforms ;
WorldTransforms . SetNum ( NumInstances ) ;
for ( int32 k = 0 ; k < NumInstances ; + + k )
{
InstancedComponent - > GetInstanceTransform ( k , WorldTransforms [ k ] , true ) ;
}
// update position to new pivot
InstancedComponent - > SetWorldTransform ( NewWorldTransform ) ;
// restore world transforms, which will compute new local transforms such that the instances do not move in the world
for ( int32 k = 0 ; k < NumInstances ; + + k )
{
InstancedComponent - > UpdateInstanceTransform ( k , WorldTransforms [ k ] , true , true , false ) ;
}
}
else if ( MapToFirstOccurrences [ ComponentIdx ] = = ComponentIdx )
2020-01-27 20:11:15 -05:00
{
2021-03-18 12:46:27 -04:00
UE : : Geometry : : FTransform3d ToBake ( OriginalTransforms [ ComponentIdx ] * NewWorldInverse ) ;
2021-10-11 17:00:25 -04:00
// to preserve scale, after baking the first transform, we will also need to bake an inverse scale transform
// this bake will need to be applied separately only in cases where FTransform3d cannot correctly represent the combination of the two transforms
// TODO: we could skip the extra bake step if the bake functions could take a general matrix
UE : : Geometry : : FTransform3d SeparateBakeScale = UE : : Geometry : : FTransform3d : : Identity ( ) ;
bool bNeedSeparateScale = false ;
// ScaledNewWorldTransform is the pivot widget's transform with the scale of the original component transform
FTransform ScaledNewWorldTransform = NewWorldTransform ;
// Note: this section only needed if we want to preserve the original mesh scale
// Basically our goal is, given the original actor transform A = Ta Ra Sa, and gizmo transform G = Tg Rg:
// Try to keep the mesh in the same place w/ new actor transform, Tg Rg Sa (the gizmo transform w/ original actor scale)
// To keep the mesh unmoved, we then have:
// (New actor transform: Tg Rg Sa) * (Baked transform: Sa^-1 Rg^-1 Tg^-1 Ta Ra Sa) = (Original actor transform: Ta Ra Sa)
// This cannot in general be represented as a single transform, so it requires us to bake the Sa^-1 separately ...
// but in special cases where Rg^-1 Ra Sa == Sa Rg^-1 Ra, the baked scales cancel and we can instead bake a single transform with no scale
{
FVector3d Scale = ( FVector3d ) OriginalTransforms [ ComponentIdx ] . GetScale3D ( ) ;
FQuaterniond Rotation = ToBake . GetRotation ( ) ;
bool AxisZero [ 3 ] { FMath : : IsNearlyZero ( Rotation . X , DOUBLE_KINDA_SMALL_NUMBER ) ,
FMath : : IsNearlyZero ( Rotation . Y , DOUBLE_KINDA_SMALL_NUMBER ) ,
FMath : : IsNearlyZero ( Rotation . Z , DOUBLE_KINDA_SMALL_NUMBER ) } ;
int Zeros = ( int ) AxisZero [ 0 ] + ( int ) AxisZero [ 1 ] + ( int ) AxisZero [ 2 ] ;
bool bEqXY = FMath : : IsNearlyEqual ( Scale . X , Scale . Y ) ;
bool bEqYZ = FMath : : IsNearlyEqual ( Scale . Y , Scale . Z ) ;
bool bCanCombinedScales =
Zeros = = 3 | | // no rotation (quaternion x,y,z == axis scaled by sin(angle/2) == 0 when angle is 0)
( bEqXY & & bEqYZ ) | | // uniform scale
( Zeros = = 2 & & ( // rotation is around a major axis &&
( ! AxisZero [ 0 ] & & bEqYZ ) | | // (scales on dimensions-moved-by-rotation are equal)
( ! AxisZero [ 1 ] & & FMath : : IsNearlyEqual ( Scale . X , Scale . Z ) ) | |
( ! AxisZero [ 2 ] & & bEqXY )
) ) ;
bNeedSeparateScale = ! bCanCombinedScales ;
FVector3d InvScale = UE : : Geometry : : FTransform3d : : GetSafeScaleReciprocal ( OriginalTransforms [ ComponentIdx ] . GetScale3D ( ) ) ;
if ( ! bNeedSeparateScale )
{
ToBake . SetScale ( FVector3d : : One ( ) ) ; // clear scale; it will be fully captured by the new transform
ToBake . SetTranslation ( ToBake . GetTranslation ( ) * InvScale ) ;
ScaledNewWorldTransform . SetScale3D ( OriginalTransforms [ ComponentIdx ] . GetScale3D ( ) ) ;
}
else if ( InvScale . X ! = 0 & & InvScale . Y ! = 0 & & InvScale . Z ! = 0 ) // Scale was inverted correctly
{
// non-uniform scale is incompatible with new pivot orientation; need to bake additional counter-scale into the mesh
SeparateBakeScale . SetScale ( InvScale ) ;
ScaledNewWorldTransform . SetScale3D ( OriginalTransforms [ ComponentIdx ] . GetScale3D ( ) ) ;
}
// else do nothing -- scale is not invertible and must be baked
}
2020-01-27 20:11:15 -05:00
2021-02-25 05:39:27 -04:00
// transform simple collision geometry
2021-06-22 11:55:16 -04:00
if ( UE : : Geometry : : ComponentTypeSupportsCollision ( Component ) )
2020-01-27 20:11:15 -05:00
{
2021-06-22 11:55:16 -04:00
UE : : Geometry : : TransformSimpleCollision ( Component , ToBake ) ;
2021-10-11 17:00:25 -04:00
if ( bNeedSeparateScale )
{
UE : : Geometry : : TransformSimpleCollision ( Component , SeparateBakeScale ) ;
}
2021-06-22 11:55:16 -04:00
}
FMeshDescription SourceMesh ( UE : : ToolTarget : : GetMeshDescriptionCopy ( Targets [ ComponentIdx ] , false ) ) ;
FMeshDescriptionEditableTriangleMeshAdapter EditableMeshDescAdapter ( & SourceMesh ) ;
MeshAdapterTransforms : : ApplyTransform ( EditableMeshDescAdapter , ToBake ) ;
2021-10-11 17:00:25 -04:00
if ( bNeedSeparateScale )
{
MeshAdapterTransforms : : ApplyTransform ( EditableMeshDescAdapter , SeparateBakeScale ) ;
}
2021-06-22 11:55:16 -04:00
// todo: support vertex-only update
UE : : ToolTarget : : CommitMeshDescriptionUpdate ( Targets [ ComponentIdx ] , & SourceMesh ) ;
2020-01-27 20:11:15 -05:00
2021-10-11 17:00:25 -04:00
Component - > SetWorldTransform ( ScaledNewWorldTransform ) ;
2020-01-27 20:11:15 -05:00
}
else
{
// try to invert baked transform
FTransform Baked = OriginalTransforms [ MapToFirstOccurrences [ ComponentIdx ] ] * NewWorldInverse ;
Component - > SetWorldTransform ( Baked . Inverse ( ) * OriginalTransforms [ ComponentIdx ] ) ;
}
2021-06-22 11:55:16 -04:00
AActor * OwnerActor = UE : : ToolTarget : : GetTargetActor ( Targets [ ComponentIdx ] ) ;
if ( OwnerActor )
{
OwnerActor - > MarkComponentsRenderStateDirty ( ) ;
OwnerActor - > UpdateComponentTransforms ( ) ;
}
2020-01-27 20:11:15 -05:00
}
// hack to ensure user sees the updated pivot immediately: request re-select of the original selection
FSelectedOjectsChangeList NewSelection ;
NewSelection . ModificationType = ESelectedObjectsModificationType : : Replace ;
2021-03-11 11:40:03 -04:00
for ( int OrigMeshIdx = 0 ; OrigMeshIdx < Targets . Num ( ) ; OrigMeshIdx + + )
2020-01-27 20:11:15 -05:00
{
2021-06-22 11:55:16 -04:00
AActor * OwnerActor = UE : : ToolTarget : : GetTargetActor ( Targets [ OrigMeshIdx ] ) ;
if ( OwnerActor )
{
NewSelection . Actors . Add ( OwnerActor ) ;
}
2020-01-27 20:11:15 -05:00
}
GetToolManager ( ) - > RequestSelectionChange ( NewSelection ) ;
2019-12-19 18:07:47 -05:00
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
# undef LOCTEXT_NAMESPACE