2020-05-04 17:46:02 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "BaseTools/BaseMeshProcessingTool.h"
# include "InteractiveToolManager.h"
# include "ToolBuilderUtil.h"
# include "ToolSetupUtil.h"
2021-06-12 14:30:22 -04:00
# include "Components/DynamicMeshComponent.h"
2020-06-23 18:40:00 -04:00
# include "Async/Async.h"
2020-05-04 17:46:02 -04:00
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshNormals.h"
2020-06-23 18:40:00 -04:00
# include "MeshBoundaryLoops.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshTransforms.h"
2020-06-23 18:40:00 -04:00
# include "WeightMapUtil.h"
2020-05-04 17:46:02 -04:00
# include "DynamicMeshToMeshDescription.h"
# include "MeshDescriptionToDynamicMesh.h"
2021-03-24 11:11:02 -04:00
# include "TargetInterfaces/MaterialProvider.h"
# include "TargetInterfaces/MeshDescriptionCommitter.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
# include "ToolTargetManager.h"
2021-12-06 12:42:19 -05:00
# include "ModelingToolTargetUtil.h"
2020-05-04 17:46:02 -04:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(BaseMeshProcessingTool)
2020-05-04 17:46:02 -04:00
# define LOCTEXT_NAMESPACE "UBaseMeshProcessingTool"
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2020-05-04 17:46:02 -04:00
/*
* ToolBuilder
*/
2021-03-24 11:11:02 -04:00
const FToolTargetTypeRequirements & UBaseMeshProcessingToolBuilder : : GetTargetRequirements ( ) const
{
static FToolTargetTypeRequirements TypeRequirements ( {
UMaterialProvider : : StaticClass ( ) ,
UMeshDescriptionCommitter : : StaticClass ( ) ,
UMeshDescriptionProvider : : StaticClass ( ) ,
UPrimitiveComponentBackedTarget : : StaticClass ( )
} ) ;
return TypeRequirements ;
}
2020-05-04 17:46:02 -04:00
bool UBaseMeshProcessingToolBuilder : : CanBuildTool ( const FToolBuilderState & SceneState ) const
{
2021-03-24 11:11:02 -04:00
return SceneState . TargetManager - > CountSelectedAndTargetable ( SceneState , GetTargetRequirements ( ) ) = = 1 ;
2020-05-04 17:46:02 -04:00
}
UInteractiveTool * UBaseMeshProcessingToolBuilder : : BuildTool ( const FToolBuilderState & SceneState ) const
{
UBaseMeshProcessingTool * NewTool = MakeNewToolInstance ( SceneState . ToolManager ) ;
2021-03-24 11:11:02 -04:00
UToolTarget * Target = SceneState . TargetManager - > BuildFirstSelectedTargetable ( SceneState , GetTargetRequirements ( ) ) ;
check ( Target ) ;
NewTool - > SetTarget ( Target ) ;
2020-05-04 17:46:02 -04:00
NewTool - > SetWorld ( SceneState . World ) ;
return NewTool ;
}
/*
* Tool
*/
void UBaseMeshProcessingTool : : SetWorld ( UWorld * World )
{
this - > TargetWorld = World ;
}
void UBaseMeshProcessingTool : : Setup ( )
{
UInteractiveTool : : Setup ( ) ;
// hide input StaticMeshComponent
2021-03-24 11:11:02 -04:00
IPrimitiveComponentBackedTarget * TargetComponent = Cast < IPrimitiveComponentBackedTarget > ( Target ) ;
TargetComponent - > SetOwnerVisibility ( false ) ;
2020-05-04 17:46:02 -04:00
// initialize our properties
ToolPropertyObjects . Add ( this ) ;
// populate the BaseMesh with a conversion of the input mesh.
FMeshDescriptionToDynamicMesh Converter ;
2021-12-06 12:42:19 -05:00
Converter . Convert ( UE : : ToolTarget : : GetMeshDescription ( Target ) , InitialMesh ) ;
2020-05-04 17:46:02 -04:00
if ( RequiresScaleNormalization ( ) )
{
// compute area of the input mesh and compute normalization scaling factor
FVector2d VolArea = TMeshQueries < FDynamicMesh3 > : : GetVolumeArea ( InitialMesh ) ;
double UnitScalingMeasure = FMathd : : Max ( 0.01 , FMathd : : Sqrt ( VolArea . Y / 6.0 ) ) ; // 6.0 is a bit arbitrary here...surface area of unit box
// translate to origin and then apply inverse of scale
2021-08-23 22:08:34 -04:00
FAxisAlignedBox3d Bounds = InitialMesh . GetBounds ( ) ;
2020-05-04 17:46:02 -04:00
SrcTranslate = Bounds . Center ( ) ;
MeshTransforms : : Translate ( InitialMesh , - SrcTranslate ) ;
SrcScale = UnitScalingMeasure ;
MeshTransforms : : Scale ( InitialMesh , ( 1.0 / SrcScale ) * FVector3d : : One ( ) , FVector3d : : Zero ( ) ) ;
// apply that transform to target transform so that visible mesh stays in the same spot
2021-03-24 11:11:02 -04:00
OverrideTransform = TargetComponent - > GetWorldTransform ( ) ;
2020-05-04 17:46:02 -04:00
FVector TranslateDelta = OverrideTransform . TransformVector ( ( FVector ) SrcTranslate ) ;
FVector CurScale = OverrideTransform . GetScale3D ( ) ;
OverrideTransform . AddToTranslation ( TranslateDelta ) ;
CurScale . X * = ( float ) SrcScale ;
CurScale . Y * = ( float ) SrcScale ;
CurScale . Z * = ( float ) SrcScale ;
OverrideTransform . SetScale3D ( CurScale ) ;
bIsScaleNormalizationApplied = true ;
}
else
{
SrcTranslate = FVector3d : : Zero ( ) ;
SrcScale = 1.0 ;
2021-03-24 11:11:02 -04:00
OverrideTransform = TargetComponent - > GetWorldTransform ( ) ;
2020-05-04 17:46:02 -04:00
bIsScaleNormalizationApplied = false ;
}
2020-06-23 18:40:00 -04:00
// pending startup computations
TArray < TFuture < void > > PendingComputes ;
// calculate base mesh vertex normals if necessary normals
if ( RequiresInitialVtxNormals ( ) )
{
TFuture < void > NormalsCompute = Async ( EAsyncExecution : : ThreadPool , [ & ] ( ) {
InitialVtxNormals = MakeShared < FMeshNormals > ( & InitialMesh ) ;
InitialVtxNormals - > ComputeVertexNormals ( ) ;
} ) ;
PendingComputes . Add ( MoveTemp ( NormalsCompute ) ) ;
}
// calculate base mesh boundary loops if necessary
if ( RequiresInitialBoundaryLoops ( ) )
{
TFuture < void > LoopsCompute = Async ( EAsyncExecution : : ThreadPool , [ & ] ( ) {
InitialBoundaryLoops = MakeShared < FMeshBoundaryLoops > ( & InitialMesh ) ;
} ) ;
PendingComputes . Add ( MoveTemp ( LoopsCompute ) ) ;
}
2020-05-04 17:46:02 -04:00
// Construct the preview object and set the material on it.
Preview = NewObject < UMeshOpPreviewWithBackgroundCompute > ( this , " Preview " ) ;
Preview - > Setup ( this - > TargetWorld , this ) ; // Adds the actual functional tool in the Preview object
2021-06-11 22:42:32 -04:00
Preview - > PreviewMesh - > SetTangentsMode ( EDynamicMeshComponentTangentsMode : : AutoCalculated ) ;
2021-10-07 22:25:54 -04:00
ToolSetupUtil : : ApplyRenderingConfigurationToPreview ( Preview - > PreviewMesh , Target ) ;
2020-05-04 17:46:02 -04:00
FComponentMaterialSet MaterialSet ;
2021-03-24 11:11:02 -04:00
Cast < IMaterialProvider > ( Target ) - > GetMaterialSet ( MaterialSet ) ;
2020-05-04 17:46:02 -04:00
Preview - > ConfigureMaterials ( MaterialSet . Materials ,
ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) )
) ;
Preview - > SetWorkingMaterialDelay ( 0.75 ) ;
Preview - > PreviewMesh - > SetTransform ( OverrideTransform ) ;
Preview - > PreviewMesh - > UpdatePreview ( & InitialMesh ) ;
// show the preview mesh
Preview - > SetVisibility ( true ) ;
InitializeProperties ( ) ;
UpdateOptionalPropertyVisibility ( ) ;
2020-06-23 18:40:00 -04:00
for ( TFuture < void > & Future : PendingComputes )
{
Future . Wait ( ) ;
}
2020-05-04 17:46:02 -04:00
// start the compute
InvalidateResult ( ) ;
GetToolManager ( ) - > DisplayMessage ( GetToolMessageString ( ) , EToolMessageLevel : : UserNotification ) ;
}
FText UBaseMeshProcessingTool : : GetToolMessageString ( ) const
{
return FText : : GetEmpty ( ) ;
}
FText UBaseMeshProcessingTool : : GetAcceptTransactionName ( ) const
{
return LOCTEXT ( " BaseMeshProcessingToolTransactionName " , " Update Mesh " ) ;
}
void UBaseMeshProcessingTool : : SavePropertySets ( )
{
for ( FOptionalPropertySet & PropStruct : OptionalProperties )
{
if ( PropStruct . PropertySet . IsValid ( ) )
{
PropStruct . PropertySet - > SaveProperties ( this ) ;
}
}
2020-06-23 18:40:00 -04:00
if ( WeightMapPropertySet . IsValid ( ) )
{
WeightMapPropertySet - > SaveProperties ( this ) ;
}
2020-05-04 17:46:02 -04:00
}
void UBaseMeshProcessingTool : : Shutdown ( EToolShutdownType ShutdownType )
{
2020-11-24 18:42:39 -04:00
if ( ShutdownType = = EToolShutdownType : : Accept & & AreAllTargetsValid ( ) = = false )
{
UE_LOG ( LogTemp , Error , TEXT ( " Tool Target has become Invalid (possibly it has been Force Deleted). Aborting Tool. " ) ) ;
ShutdownType = EToolShutdownType : : Cancel ;
}
2020-05-04 17:46:02 -04:00
OnShutdown ( ShutdownType ) ;
SavePropertySets ( ) ;
// Restore (unhide) the source meshes
2021-03-24 11:11:02 -04:00
Cast < IPrimitiveComponentBackedTarget > ( Target ) - > SetOwnerVisibility ( true ) ;
2020-05-04 17:46:02 -04:00
if ( Preview ! = nullptr )
{
FDynamicMeshOpResult Result = Preview - > Shutdown ( ) ;
2020-11-24 18:42:39 -04:00
2020-05-04 17:46:02 -04:00
if ( ShutdownType = = EToolShutdownType : : Accept )
{
GetToolManager ( ) - > BeginUndoTransaction ( GetAcceptTransactionName ( ) ) ;
FDynamicMesh3 * DynamicMeshResult = Result . Mesh . Get ( ) ;
check ( DynamicMeshResult ! = nullptr ) ;
// un-apply scale normalization if it was applied
if ( bIsScaleNormalizationApplied )
{
MeshTransforms : : Scale ( * DynamicMeshResult , FVector3d ( SrcScale , SrcScale , SrcScale ) , FVector3d : : Zero ( ) ) ;
MeshTransforms : : Translate ( * DynamicMeshResult , SrcTranslate ) ;
}
bool bTopologyChanged = HasMeshTopologyChanged ( ) ;
2021-12-06 12:42:19 -05:00
UE : : ToolTarget : : CommitMeshDescriptionUpdateViaDynamicMesh ( Target , * DynamicMeshResult , bTopologyChanged ) ;
2020-05-04 17:46:02 -04:00
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
}
}
void UBaseMeshProcessingTool : : Render ( IToolsContextRenderAPI * RenderAPI )
{
UpdateResult ( ) ;
}
void UBaseMeshProcessingTool : : OnTick ( float DeltaTime )
{
Preview - > Tick ( DeltaTime ) ;
}
void UBaseMeshProcessingTool : : InvalidateResult ( )
{
Preview - > InvalidateResult ( ) ;
bResultValid = false ;
}
void UBaseMeshProcessingTool : : UpdateResult ( )
{
if ( bResultValid )
{
return ;
}
bResultValid = Preview - > HaveValidResult ( ) ;
}
bool UBaseMeshProcessingTool : : HasAccept ( ) const
{
return true ;
}
bool UBaseMeshProcessingTool : : CanAccept ( ) const
{
2021-02-05 16:33:02 -04:00
return Super : : CanAccept ( ) & & bResultValid & & Preview - > HaveValidNonEmptyResult ( ) ;
2020-05-04 17:46:02 -04:00
}
void UBaseMeshProcessingTool : : AddOptionalPropertySet (
UInteractiveToolPropertySet * PropSet ,
TUniqueFunction < bool ( ) > VisibilityFunc ,
TUniqueFunction < void ( ) > OnModifiedFunc ,
bool bChangeInvalidatesResult )
{
AddToolPropertySource ( PropSet ) ;
PropSet - > RestoreProperties ( this ) ;
SetToolPropertySourceEnabled ( PropSet , false ) ;
FOptionalPropertySet PropSetStruct ;
PropSetStruct . PropertySet = PropSet ;
PropSetStruct . IsVisible = MoveTemp ( VisibilityFunc ) ;
PropSetStruct . OnModifiedFunc = MoveTemp ( OnModifiedFunc ) ;
PropSetStruct . bInvalidateOnModify = bChangeInvalidatesResult ;
int32 Index = OptionalProperties . Num ( ) ;
OptionalProperties . Add ( MoveTemp ( PropSetStruct ) ) ;
PropSet - > GetOnModified ( ) . AddLambda ( [ Index , this ] ( UObject * , FProperty * ) { OnOptionalPropSetModified ( Index ) ; } ) ;
}
void UBaseMeshProcessingTool : : OnOptionalPropSetModified ( int32 Index )
{
const FOptionalPropertySet & PropSetStruct = OptionalProperties [ Index ] ;
PropSetStruct . OnModifiedFunc ( ) ;
if ( PropSetStruct . bInvalidateOnModify )
{
InvalidateResult ( ) ;
}
}
void UBaseMeshProcessingTool : : UpdateOptionalPropertyVisibility ( )
{
for ( FOptionalPropertySet & PropStruct : OptionalProperties )
{
if ( PropStruct . PropertySet . IsValid ( ) )
{
bool bVisible = PropStruct . IsVisible ( ) ;
SetToolPropertySourceEnabled ( PropStruct . PropertySet . Get ( ) , bVisible ) ;
}
}
2020-06-23 18:40:00 -04:00
if ( WeightMapPropertySet . IsValid ( ) )
{
bool bVisible = WeightMapPropertySetVisibleFunc ( ) ;
SetToolPropertySourceEnabled ( WeightMapPropertySet . Get ( ) , bVisible ) ;
}
2020-05-04 17:46:02 -04:00
}
2020-06-23 18:40:00 -04:00
TSharedPtr < FMeshNormals > & UBaseMeshProcessingTool : : GetInitialVtxNormals ( )
2020-05-04 17:46:02 -04:00
{
2020-06-23 18:40:00 -04:00
checkf ( InitialVtxNormals . IsValid ( ) , TEXT ( " Initial Vertex Normals have not been computed - must return true from RequiresInitialVtxNormals() " ) ) ;
return InitialVtxNormals ;
2020-05-04 17:46:02 -04:00
}
2020-06-23 18:40:00 -04:00
TSharedPtr < FMeshBoundaryLoops > & UBaseMeshProcessingTool : : GetInitialBoundaryLoops ( )
{
checkf ( InitialBoundaryLoops . IsValid ( ) , TEXT ( " Initial Boundary Loops have not been computed - must return true from RequiresInitialBoundaryLoops() " ) ) ;
return InitialBoundaryLoops ;
}
void UBaseMeshProcessingTool : : SetupWeightMapPropertySet ( UWeightMapSetProperties * Properties )
{
AddToolPropertySource ( Properties ) ;
Properties - > RestoreProperties ( this ) ;
WeightMapPropertySet = Properties ;
// initialize property list
2021-12-06 12:42:19 -05:00
Properties - > InitializeFromMesh ( UE : : ToolTarget : : GetMeshDescription ( Target ) ) ;
2020-06-23 18:40:00 -04:00
Properties - > WatchProperty ( Properties - > WeightMap ,
[ & ] ( FName ) { OnSelectedWeightMapChanged ( true ) ; } ) ;
Properties - > WatchProperty ( Properties - > bInvertWeightMap ,
[ & ] ( bool ) { OnSelectedWeightMapChanged ( true ) ; } ) ;
OnSelectedWeightMapChanged ( false ) ;
}
void UBaseMeshProcessingTool : : OnSelectedWeightMapChanged ( bool bInvalidate )
{
TSharedPtr < FIndexedWeightMap1f > NewWeightMap = MakeShared < FIndexedWeightMap1f > ( ) ;
// this will return all-ones weight map if None is selected
2021-12-06 12:42:19 -05:00
bool bFound = UE : : WeightMaps : : GetVertexWeightMap ( UE : : ToolTarget : : GetMeshDescription ( Target ) , WeightMapPropertySet - > WeightMap , * NewWeightMap , 1.0f ) ;
2020-06-23 18:40:00 -04:00
if ( bFound & & WeightMapPropertySet - > bInvertWeightMap )
{
NewWeightMap - > InvertWeightMap ( ) ;
}
2023-07-25 17:45:47 -04:00
// This will automatically reset the weightmap to None if the new value doesn't exist. This could easily occur during a preset load, for instance, and we
// don't want to pollute the value with garbage from a preset value that doesn't apply to the active target.
if ( ! bFound )
{
WeightMapPropertySet - > SetSelectedFromWeightMapIndex ( - 1 ) ;
}
2020-06-23 18:40:00 -04:00
ActiveWeightMap = NewWeightMap ;
if ( bInvalidate )
{
InvalidateResult ( ) ;
}
}
bool UBaseMeshProcessingTool : : HasActiveWeightMap ( ) const
{
return WeightMapPropertySet . IsValid ( ) & & WeightMapPropertySet - > HasSelectedWeightMap ( ) ;
}
TSharedPtr < FIndexedWeightMap1f > & UBaseMeshProcessingTool : : GetActiveWeightMap ( )
{
checkf ( ActiveWeightMap . IsValid ( ) , TEXT ( " Weight Map has not been initialized - must call SetupWeightMapPropertySet() in property set " ) ) ;
return ActiveWeightMap ;
}
2020-05-04 17:46:02 -04:00
# undef LOCTEXT_NAMESPACE
2022-09-28 01:06:15 -04:00