2020-01-08 17:11:23 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-12-19 18:07:47 -05:00
# include "BakeTransformTool.h"
# include "InteractiveToolManager.h"
# include "ToolBuilderUtil.h"
# include "ToolSetupUtil.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/DynamicMesh3.h"
2019-12-19 18:07:47 -05:00
# include "BaseBehaviors/MultiClickSequenceInputBehavior.h"
# include "Selection/SelectClickedAction.h"
2020-01-27 20:11:15 -05:00
# include "MeshAdapterTransforms.h"
# include "MeshDescriptionAdapter.h"
2019-12-19 18:07:47 -05:00
# include "MeshDescriptionToDynamicMesh.h"
# include "DynamicMeshToMeshDescription.h"
2021-03-01 12:11:10 -04:00
# include "Physics/ComponentCollisionUtil.h"
2019-12-19 18:07:47 -05:00
2021-02-23 18:03:26 -04:00
# include "TargetInterfaces/MeshDescriptionCommitter.h"
2021-03-01 12:11:10 -04:00
# include "TargetInterfaces/MeshDescriptionProvider.h"
2021-02-23 18:03:26 -04:00
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
# include "ToolTargetManager.h"
2021-06-22 11:55:16 -04:00
# include "ModelingToolTargetUtil.h"
2021-02-23 18:03:26 -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 "UBakeTransformTool"
/*
* ToolBuilder
*/
2021-11-23 09:42:40 -05:00
UMultiSelectionMeshEditingTool * UBakeTransformToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2021-02-23 18:03:26 -04:00
{
2021-11-23 09:42:40 -05:00
return NewObject < UBakeTransformTool > ( SceneState . ToolManager ) ;
2019-12-19 18:07:47 -05:00
}
/*
* Tool
*/
UBakeTransformToolProperties : : UBakeTransformToolProperties ( )
{
}
UBakeTransformTool : : UBakeTransformTool ( )
{
}
void UBakeTransformTool : : Setup ( )
{
UInteractiveTool : : Setup ( ) ;
2020-01-27 20:11:15 -05:00
BasicProperties = NewObject < UBakeTransformToolProperties > ( this ) ;
2019-12-19 18:07:47 -05:00
AddToolPropertySource ( BasicProperties ) ;
2020-01-27 20:11:15 -05:00
FText AllTheWarnings = LOCTEXT ( " BakeTransformWarning " , " 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-02-23 18:03:26 -04:00
bool bSharesSources = GetMapToSharedSourceData ( MapToFirstOccurrences ) ;
2020-01-27 20:11:15 -05:00
if ( bSharesSources )
{
AllTheWarnings = FText : : Format ( FTextFormat : : FromString ( " {0} \n \n {1} " ) , AllTheWarnings , LOCTEXT ( " BakeTransformSharedAssetsWarning " , " WARNING: Multiple meshes in your selection use the same source asset! This is not supported -- each asset can only have one baked transform. " ) ) ;
}
bool bHasZeroScales = false ;
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
FTransform Transform = ( FTransform ) UE : : ToolTarget : : GetLocalToWorldTransform ( Targets [ ComponentIdx ] ) ;
if ( Transform . GetScale3D ( ) . GetAbsMin ( ) < KINDA_SMALL_NUMBER )
2020-01-27 20:11:15 -05:00
{
bHasZeroScales = true ;
}
}
if ( bHasZeroScales )
{
AllTheWarnings = FText : : Format ( FTextFormat : : FromString ( " {0} \n \n {1} " ) , AllTheWarnings , LOCTEXT ( " BakeTransformWithZeroScale " , " WARNING: Baking a zero scale in any dimension will permanently flatten the asset. " ) ) ;
}
GetToolManager ( ) - > DisplayMessage ( AllTheWarnings , EToolMessageLevel : : UserWarning ) ;
2020-03-10 14:00:36 -04:00
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Bake Transform " ) ) ;
2020-03-10 14:00:36 -04:00
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartTool " , " This Tool applies the current Rotation and/or Scaling of the object's Transform to the underlying mesh Asset. " ) ,
EToolMessageLevel : : UserNotification ) ;
2019-12-19 18:07:47 -05:00
}
void UBakeTransformTool : : Shutdown ( EToolShutdownType ShutdownType )
{
if ( ShutdownType = = EToolShutdownType : : Accept )
{
2020-01-27 20:11:15 -05:00
UpdateAssets ( ) ;
2019-12-19 18:07:47 -05:00
}
}
2020-01-27 20:11:15 -05:00
void UBakeTransformTool : : UpdateAssets ( )
2019-12-19 18:07:47 -05:00
{
2021-03-01 12:11:10 -04:00
// Make sure mesh descriptions are deserialized before we open transaction.
// This is to avoid potential stability issues related to creation/load of
// mesh descriptions inside a transaction.
2021-06-22 11:55:16 -04:00
// TODO: this may not be necessary anymore. Also may not be the most efficient
TArray < FMeshDescription > SourceMeshes ;
2021-03-01 12:11:10 -04:00
for ( int32 ComponentIdx = 0 ; ComponentIdx < Targets . Num ( ) ; ComponentIdx + + )
{
2021-06-22 11:55:16 -04:00
UE : : ToolTarget : : GetMeshDescription ( Targets [ ComponentIdx ] ) ;
2021-03-01 12:11:10 -04:00
}
2019-12-19 18:07:47 -05:00
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " BakeTransformToolTransactionName " , " Bake Transforms " ) ) ;
2021-03-18 12:46:27 -04:00
TArray < UE : : Geometry : : FTransform3d > BakedTransforms ;
2021-02-23 18:03:26 -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
UToolTarget * Target = Targets [ ComponentIdx ] ;
UPrimitiveComponent * Component = UE : : ToolTarget : : GetTargetComponent ( Target ) ;
2021-03-01 12:11:10 -04:00
Component - > Modify ( ) ;
2021-02-23 18:03:26 -04:00
2021-06-22 11:55:16 -04:00
UE : : Geometry : : FTransform3d ComponentToWorld = UE : : ToolTarget : : GetLocalToWorldTransform ( Target ) ;
2021-03-18 12:46:27 -04:00
UE : : Geometry : : FTransform3d ToBakePart = UE : : Geometry : : FTransform3d : : Identity ( ) ;
UE : : Geometry : : FTransform3d NewWorldPart = ComponentToWorld ;
2020-01-27 20:11:15 -05:00
if ( MapToFirstOccurrences [ ComponentIdx ] < ComponentIdx )
2019-12-19 18:07:47 -05:00
{
2020-01-27 20:11:15 -05:00
ToBakePart = BakedTransforms [ MapToFirstOccurrences [ ComponentIdx ] ] ;
BakedTransforms . Add ( ToBakePart ) ;
// try to invert baked transform
2021-03-18 12:46:27 -04:00
NewWorldPart = UE : : Geometry : : FTransform3d (
2020-01-27 20:11:15 -05:00
NewWorldPart . GetRotation ( ) * ToBakePart . GetRotation ( ) . Inverse ( ) ,
NewWorldPart . GetTranslation ( ) ,
2021-03-18 12:46:27 -04:00
NewWorldPart . GetScale ( ) * UE : : Geometry : : FTransform3d : : GetSafeScaleReciprocal ( ToBakePart . GetScale ( ) )
2020-01-27 20:11:15 -05:00
) ;
NewWorldPart . SetTranslation ( NewWorldPart . GetTranslation ( ) - NewWorldPart . TransformVector ( ToBakePart . GetTranslation ( ) ) ) ;
}
else
{
if ( BasicProperties - > bBakeRotation )
{
ToBakePart . SetRotation ( ComponentToWorld . GetRotation ( ) ) ;
NewWorldPart . SetRotation ( FQuaterniond : : Identity ( ) ) ;
}
FVector3d ScaleVec = ComponentToWorld . GetScale ( ) ;
2019-12-19 18:07:47 -05:00
2020-01-27 20:11:15 -05:00
// weird algo to choose what to keep around as uniform scale in the case where we want to bake out the non-uniform scaling
FVector3d AbsScales ( FMathd : : Abs ( ScaleVec . X ) , FMathd : : Abs ( ScaleVec . Y ) , FMathd : : Abs ( ScaleVec . Z ) ) ;
double RemainingUniformScale = AbsScales . X ;
{
FVector3d Dists ;
for ( int SubIdx = 0 ; SubIdx < 3 ; SubIdx + + )
{
int OtherA = ( SubIdx + 1 ) % 3 ;
int OtherB = ( SubIdx + 2 ) % 3 ;
Dists [ SubIdx ] = FMathd : : Abs ( AbsScales [ SubIdx ] - AbsScales [ OtherA ] ) + FMathd : : Abs ( AbsScales [ SubIdx ] - AbsScales [ OtherB ] ) ;
}
int BestSubIdx = 0 ;
for ( int CompareSubIdx = 1 ; CompareSubIdx < 3 ; CompareSubIdx + + )
{
if ( Dists [ CompareSubIdx ] < Dists [ BestSubIdx ] )
{
BestSubIdx = CompareSubIdx ;
}
}
RemainingUniformScale = AbsScales [ BestSubIdx ] ;
if ( RemainingUniformScale < = FLT_MIN )
{
2021-03-17 19:32:44 -04:00
RemainingUniformScale = MaxAbsElement ( AbsScales ) ;
2020-01-27 20:11:15 -05:00
}
}
switch ( BasicProperties - > BakeScale )
{
case EBakeScaleMethod : : BakeFullScale :
ToBakePart . SetScale ( ScaleVec ) ;
NewWorldPart . SetScale ( FVector3d : : One ( ) ) ;
break ;
case EBakeScaleMethod : : BakeNonuniformScale :
check ( RemainingUniformScale > FLT_MIN ) ; // avoid baking a ~zero scale
ToBakePart . SetScale ( ScaleVec / RemainingUniformScale ) ;
NewWorldPart . SetScale ( FVector3d ( RemainingUniformScale , RemainingUniformScale , RemainingUniformScale ) ) ;
break ;
case EBakeScaleMethod : : DoNotBakeScale :
break ;
default :
check ( false ) ; // must explicitly handle all cases
}
2019-12-19 18:07:47 -05:00
2021-06-22 11:55:16 -04:00
// apply edit
FMeshDescription SourceMesh ( UE : : ToolTarget : : GetMeshDescriptionCopy ( Targets [ ComponentIdx ] , false ) ) ;
FMeshDescriptionEditableTriangleMeshAdapter EditableMeshDescAdapter ( & SourceMesh ) ;
// do this part within the commit because we have the MeshDescription already computed
if ( BasicProperties - > bRecenterPivot )
2020-01-27 20:11:15 -05:00
{
2021-06-22 11:55:16 -04:00
FBox BBox = SourceMesh . ComputeBoundingBox ( ) ;
FVector3d Center ( BBox . GetCenter ( ) ) ;
FFrame3d LocalFrame ( Center ) ;
ToBakePart . SetTranslation ( ToBakePart . GetTranslation ( ) - Center ) ;
NewWorldPart . SetTranslation ( NewWorldPart . GetTranslation ( ) + NewWorldPart . TransformVector ( Center ) ) ;
}
2020-01-27 20:11:15 -05:00
2021-06-22 11:55:16 -04:00
MeshAdapterTransforms : : ApplyTransform ( EditableMeshDescAdapter , ToBakePart ) ;
2020-01-27 20:11:15 -05:00
2021-06-22 11:55:16 -04:00
FVector3d BakeScaleVec = ToBakePart . GetScale ( ) ;
if ( BakeScaleVec . X * BakeScaleVec . Y * BakeScaleVec . Z < 0 )
{
SourceMesh . ReverseAllPolygonFacing ( ) ;
}
2020-01-27 20:11:15 -05:00
2021-06-22 11:55:16 -04:00
// todo: support vertex-only update
UE : : ToolTarget : : CommitMeshDescriptionUpdate ( Target , & SourceMesh ) ;
2020-01-27 20:11:15 -05:00
2021-03-01 12:11:10 -04:00
// try to transform simple collision
2021-06-22 11:55:16 -04:00
if ( UE : : Geometry : : ComponentTypeSupportsCollision ( Component ) )
{
UE : : Geometry : : TransformSimpleCollision ( Component , ToBakePart ) ;
}
2021-03-01 12:11:10 -04:00
2020-01-27 20:11:15 -05:00
BakedTransforms . Add ( ToBakePart ) ;
}
2019-12-19 18:07:47 -05:00
2020-01-27 20:11:15 -05:00
Component - > SetWorldTransform ( ( FTransform ) NewWorldPart ) ;
2021-06-22 11:55:16 -04:00
AActor * TargetActor = UE : : ToolTarget : : GetTargetActor ( Target ) ;
if ( TargetActor )
{
TargetActor - > MarkComponentsRenderStateDirty ( ) ;
}
2019-12-19 18:07:47 -05:00
}
2021-11-22 15:45:29 -05:00
if ( BasicProperties - > bRecenterPivot )
{
// hack to ensure user sees the updated pivot immediately: request re-select of the original selection
FSelectedOjectsChangeList NewSelection ;
NewSelection . ModificationType = ESelectedObjectsModificationType : : Replace ;
for ( int OrigMeshIdx = 0 ; OrigMeshIdx < Targets . Num ( ) ; OrigMeshIdx + + )
{
AActor * OwnerActor = UE : : ToolTarget : : GetTargetActor ( Targets [ OrigMeshIdx ] ) ;
if ( OwnerActor )
{
NewSelection . Actors . Add ( OwnerActor ) ;
}
}
GetToolManager ( ) - > RequestSelectionChange ( NewSelection ) ;
}
2019-12-19 18:07:47 -05:00
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
# undef LOCTEXT_NAMESPACE