2020-12-04 19:51:25 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MeshGroupPaintTool.h"
# include "InteractiveToolManager.h"
# include "InteractiveGizmoManager.h"
# include "Drawing/MeshElementsVisualizer.h"
# include "Async/ParallelFor.h"
# include "Async/Async.h"
2021-06-11 22:42:32 -04:00
# include "ToolSetupUtil.h"
# include "ModelingToolTargetUtil.h"
2020-12-04 19:51:25 -04:00
# include "MeshWeights.h"
2021-05-22 01:32:46 -04:00
# include "DynamicMesh/MeshNormals.h"
# include "DynamicMesh/MeshIndexUtil.h"
2020-12-04 19:51:25 -04:00
# include "Util/BufferUtil.h"
# include "Util/ColorConstants.h"
# include "Selections/MeshConnectedComponents.h"
# include "Selections/MeshFaceSelection.h"
2020-12-15 12:58:13 -04:00
# include "Selections/MeshVertexSelection.h"
2020-12-04 19:51:25 -04:00
# include "Polygroups/PolygroupUtil.h"
2020-12-15 12:58:13 -04:00
# include "Polygon2.h"
# include "Intersection/IntrLine2Line2.h"
# include "Intersection/IntrSegment2Segment2.h"
2020-12-04 19:51:25 -04:00
# include "Changes/MeshVertexChange.h"
# include "Changes/MeshPolygroupChange.h"
# include "Changes/BasicChanges.h"
# include "Sculpting/MeshGroupPaintBrushOps.h"
# include "Sculpting/StampFalloffs.h"
# include "Sculpting/MeshSculptUtil.h"
2020-12-15 12:58:13 -04:00
# include "CanvasTypes.h"
# include "CanvasItem.h"
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2020-12-04 19:51:25 -04:00
# define LOCTEXT_NAMESPACE "UMeshGroupPaintTool"
namespace
{
// probably should be something defined for the whole tool framework...
# if WITH_EDITOR
static EAsyncExecution GroupPaintToolAsyncExecTarget = EAsyncExecution : : LargeThreadPool ;
# else
static EAsyncExecution GroupPaintToolAsyncExecTarget = EAsyncExecution : : ThreadPool ;
# endif
}
/*
* ToolBuilder
*/
UMeshSurfacePointTool * UMeshGroupPaintToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
{
UMeshGroupPaintTool * SculptTool = NewObject < UMeshGroupPaintTool > ( SceneState . ToolManager ) ;
SculptTool - > SetWorld ( SceneState . World ) ;
return SculptTool ;
}
/*
* Properties
*/
void UMeshGroupPaintToolActionPropertySet : : PostAction ( EMeshGroupPaintToolActions Action )
{
if ( ParentTool . IsValid ( ) )
{
ParentTool - > RequestAction ( Action ) ;
}
}
/*
* Tool
*/
void UMeshGroupPaintTool : : Setup ( )
{
UMeshSculptToolBase : : Setup ( ) ;
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Paint PolyGroups " ) ) ;
2020-12-04 19:51:25 -04:00
// create dynamic mesh component to use for live preview
2021-11-18 14:37:34 -05:00
FActorSpawnParameters SpawnInfo ;
PreviewMeshActor = TargetWorld - > SpawnActor < AInternalToolFrameworkActor > ( FVector : : ZeroVector , FRotator : : ZeroRotator , SpawnInfo ) ;
DynamicMeshComponent = NewObject < UDynamicMeshComponent > ( PreviewMeshActor ) ;
InitializeSculptMeshComponent ( DynamicMeshComponent , PreviewMeshActor ) ;
2020-12-04 19:51:25 -04:00
// assign materials
2021-06-11 22:42:32 -04:00
FComponentMaterialSet MaterialSet = UE : : ToolTarget : : GetMaterialSet ( Target ) ;
2020-12-04 19:51:25 -04:00
for ( int k = 0 ; k < MaterialSet . Materials . Num ( ) ; + + k )
{
DynamicMeshComponent - > SetMaterial ( k , MaterialSet . Materials [ k ] ) ;
}
2021-06-10 18:37:57 -04:00
DynamicMeshComponent - > SetInvalidateProxyOnChangeEnabled ( false ) ;
2020-12-04 19:51:25 -04:00
OnDynamicMeshComponentChangedHandle = DynamicMeshComponent - > OnMeshVerticesChanged . AddUObject ( this , & UMeshGroupPaintTool : : OnDynamicMeshComponentChanged ) ;
FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
Mesh - > EnableVertexColors ( FVector3f : : One ( ) ) ;
2021-08-23 22:08:34 -04:00
FAxisAlignedBox3d Bounds = Mesh - > GetBounds ( true ) ;
2020-12-04 19:51:25 -04:00
TFuture < void > PrecomputeFuture = Async ( GroupPaintToolAsyncExecTarget , [ & ] ( )
{
PrecomputeFilterData ( ) ;
} ) ;
TFuture < void > OctreeFuture = Async ( GroupPaintToolAsyncExecTarget , [ & ] ( )
{
// initialize dynamic octree
if ( Mesh - > TriangleCount ( ) > 100000 )
{
Octree . RootDimension = Bounds . MaxDim ( ) / 10.0 ;
Octree . SetMaxTreeDepth ( 4 ) ;
}
else
{
Octree . RootDimension = Bounds . MaxDim ( ) ;
Octree . SetMaxTreeDepth ( 8 ) ;
}
Octree . Initialize ( Mesh ) ;
//Octree.CheckValidity(EValidityCheckFailMode::Check, true, true);
//FDynamicMeshOctree3::FStatistics Stats;
//Octree.ComputeStatistics(Stats);
//UE_LOG(LogTemp, Warning, TEXT("Octree Stats: %s"), *Stats.ToString());
} ) ;
// initialize render decomposition
TUniquePtr < FMeshRenderDecomposition > Decomp = MakeUnique < FMeshRenderDecomposition > ( ) ;
FMeshRenderDecomposition : : BuildChunkedDecomposition ( Mesh , & MaterialSet , * Decomp ) ;
Decomp - > BuildAssociations ( Mesh ) ;
//UE_LOG(LogTemp, Warning, TEXT("Decomposition has %d groups"), Decomp->Num());
DynamicMeshComponent - > SetExternalDecomposition ( MoveTemp ( Decomp ) ) ;
// initialize brush radius range interval, brush properties
UMeshSculptToolBase : : InitializeBrushSizeRange ( Bounds ) ;
2020-12-15 12:58:13 -04:00
// Set up control points mechanic
PolyLassoMechanic = NewObject < UPolyLassoMarqueeMechanic > ( this ) ;
PolyLassoMechanic - > Setup ( this ) ;
PolyLassoMechanic - > SetIsEnabled ( false ) ;
PolyLassoMechanic - > SpacingTolerance = 10.0f ;
PolyLassoMechanic - > OnDrawPolyLassoFinished . AddUObject ( this , & UMeshGroupPaintTool : : OnPolyLassoFinished ) ;
2020-12-04 19:51:25 -04:00
PolygroupLayerProperties = NewObject < UPolygroupLayersProperties > ( this ) ;
2021-06-20 17:01:49 -04:00
PolygroupLayerProperties - > RestoreProperties ( this , TEXT ( " MeshGroupPaintTool " ) ) ;
2020-12-04 19:51:25 -04:00
PolygroupLayerProperties - > InitializeGroupLayers ( GetSculptMesh ( ) ) ;
PolygroupLayerProperties - > WatchProperty ( PolygroupLayerProperties - > ActiveGroupLayer , [ & ] ( FName ) { OnSelectedGroupLayerChanged ( ) ; } ) ;
UpdateActiveGroupLayer ( ) ;
AddToolPropertySource ( PolygroupLayerProperties ) ;
// initialize other properties
FilterProperties = NewObject < UGroupPaintBrushFilterProperties > ( this ) ;
2020-12-15 12:58:13 -04:00
FilterProperties - > WatchProperty ( FilterProperties - > SubToolType ,
[ this ] ( EMeshGroupPaintInteractionType NewType ) { UpdateSubToolType ( NewType ) ; } ) ;
FilterProperties - > WatchProperty ( FilterProperties - > BrushSize ,
2021-10-28 13:24:28 -04:00
[ this ] ( float NewSize ) { UMeshSculptToolBase : : BrushProperties - > BrushSize . AdaptiveSize = NewSize ; } ) ;
2021-03-02 19:23:29 -04:00
FilterProperties - > WatchProperty ( FilterProperties - > bHitBackFaces ,
[ this ] ( bool bNewValue ) { UMeshSculptToolBase : : BrushProperties - > bHitBackFaces = bNewValue ; } ) ;
2020-12-15 12:58:13 -04:00
FilterProperties - > RestoreProperties ( this ) ;
2021-10-28 13:24:28 -04:00
FilterProperties - > BrushSize = UMeshSculptToolBase : : BrushProperties - > BrushSize . AdaptiveSize ;
2021-03-02 19:23:29 -04:00
FilterProperties - > bHitBackFaces = UMeshSculptToolBase : : BrushProperties - > bHitBackFaces ;
2022-01-14 14:21:26 -05:00
FilterProperties - > SetGroup = ActiveGroupSet - > MaxGroupID ;
2020-12-15 12:58:13 -04:00
AddToolPropertySource ( FilterProperties ) ;
2020-12-04 19:51:25 -04:00
InitializeIndicator ( ) ;
// initialize our properties
AddToolPropertySource ( UMeshSculptToolBase : : BrushProperties ) ;
UMeshSculptToolBase : : BrushProperties - > bShowPerBrushProps = false ;
UMeshSculptToolBase : : BrushProperties - > bShowFalloff = false ;
2020-12-15 12:58:13 -04:00
UMeshSculptToolBase : : BrushProperties - > bShowLazyness = false ;
2020-12-04 19:51:25 -04:00
CalculateBrushRadius ( ) ;
2020-12-15 12:58:13 -04:00
PaintBrushOpProperties = NewObject < UGroupPaintBrushOpProps > ( this ) ;
2021-10-28 13:24:28 -04:00
RegisterBrushType ( ( int32 ) EMeshGroupPaintBrushType : : Paint , LOCTEXT ( " Paint " , " Paint " ) ,
2020-12-04 19:51:25 -04:00
MakeUnique < FLambdaMeshSculptBrushOpFactory > ( [ this ] ( ) { return MakeUnique < FGroupPaintBrushOp > ( ) ; } ) ,
2020-12-15 12:58:13 -04:00
PaintBrushOpProperties ) ;
2020-12-04 19:51:25 -04:00
// secondary brushes
2020-12-15 12:58:13 -04:00
EraseBrushOpProperties = NewObject < UGroupEraseBrushOpProps > ( this ) ;
EraseBrushOpProperties - > GetCurrentGroupLambda = [ this ] ( ) { return PaintBrushOpProperties - > GetGroup ( ) ; } ;
2020-12-04 19:51:25 -04:00
2021-10-28 13:24:28 -04:00
RegisterSecondaryBrushType ( ( int32 ) EMeshGroupPaintBrushType : : Erase , LOCTEXT ( " Erase " , " Erase " ) ,
2020-12-04 19:51:25 -04:00
MakeUnique < TBasicMeshSculptBrushOpFactory < FGroupEraseBrushOp > > ( ) ,
2020-12-15 12:58:13 -04:00
EraseBrushOpProperties ) ;
2020-12-04 19:51:25 -04:00
AddToolPropertySource ( UMeshSculptToolBase : : ViewProperties ) ;
AddToolPropertySource ( UMeshSculptToolBase : : GizmoProperties ) ;
SetToolPropertySourceEnabled ( UMeshSculptToolBase : : GizmoProperties , false ) ;
// register watchers
FilterProperties - > WatchProperty ( FilterProperties - > PrimaryBrushType ,
[ this ] ( EMeshGroupPaintBrushType NewType ) { UpdateBrushType ( NewType ) ; } ) ;
// must call before updating brush type so that we register all brush properties?
UMeshSculptToolBase : : OnCompleteSetup ( ) ;
UpdateBrushType ( FilterProperties - > PrimaryBrushType ) ;
SetActiveSecondaryBrushType ( ( int32 ) EMeshGroupPaintBrushType : : Erase ) ;
FreezeActions = NewObject < UMeshGroupPaintToolFreezeActions > ( this ) ;
FreezeActions - > Initialize ( this ) ;
AddToolPropertySource ( FreezeActions ) ;
MeshElementsDisplay = NewObject < UMeshElementsVisualizer > ( this ) ;
MeshElementsDisplay - > CreateInWorld ( DynamicMeshComponent - > GetWorld ( ) , DynamicMeshComponent - > GetComponentTransform ( ) ) ;
if ( ensure ( MeshElementsDisplay - > Settings ) )
{
2021-06-20 17:01:49 -04:00
MeshElementsDisplay - > Settings - > RestoreProperties ( this , TEXT ( " MeshGroupPaintTool " ) ) ;
2020-12-04 19:51:25 -04:00
AddToolPropertySource ( MeshElementsDisplay - > Settings ) ;
}
2021-06-15 17:05:40 -04:00
MeshElementsDisplay - > SetMeshAccessFunction ( [ this ] ( UMeshElementsVisualizer : : ProcessDynamicMeshFunc ProcessFunc ) {
ProcessFunc ( * GetSculptMesh ( ) ) ;
} ) ;
2020-12-04 19:51:25 -04:00
// force colors update... ?
2021-06-10 18:37:57 -04:00
DynamicMeshComponent - > SetTriangleColorFunction ( [ this ] ( const FDynamicMesh3 * Mesh , int TriangleID )
2020-12-04 19:51:25 -04:00
{
return GetColorForGroup ( ActiveGroupSet - > GetGroup ( TriangleID ) ) ;
2021-06-10 18:37:57 -04:00
} ) ;
2020-12-04 19:51:25 -04:00
// disable view properties
SetViewPropertiesEnabled ( false ) ;
UpdateMaterialMode ( EMeshEditingMaterialModes : : VertexColor ) ;
UpdateWireframeVisibility ( false ) ;
UpdateFlatShadingSetting ( true ) ;
2020-12-15 12:58:13 -04:00
// configure panels
UpdateSubToolType ( FilterProperties - > SubToolType ) ;
2020-12-04 19:51:25 -04:00
PrecomputeFuture . Wait ( ) ;
OctreeFuture . Wait ( ) ;
}
void UMeshGroupPaintTool : : Shutdown ( EToolShutdownType ShutdownType )
{
if ( DynamicMeshComponent ! = nullptr )
{
DynamicMeshComponent - > OnMeshChanged . Remove ( OnDynamicMeshComponentChangedHandle ) ;
}
if ( ensure ( MeshElementsDisplay - > Settings ) )
{
2021-06-20 17:01:49 -04:00
MeshElementsDisplay - > Settings - > SaveProperties ( this , TEXT ( " MeshGroupPaintTool " ) ) ;
2020-12-04 19:51:25 -04:00
}
MeshElementsDisplay - > Disconnect ( ) ;
FilterProperties - > SaveProperties ( this ) ;
2021-06-20 17:01:49 -04:00
PolygroupLayerProperties - > SaveProperties ( this , TEXT ( " MeshGroupPaintTool " ) ) ;
2020-12-04 19:51:25 -04:00
2021-11-18 14:37:34 -05:00
if ( PreviewMeshActor ! = nullptr )
{
PreviewMeshActor - > Destroy ( ) ;
PreviewMeshActor = nullptr ;
}
2021-09-30 23:01:43 -04:00
UMeshSculptToolBase : : Shutdown ( ShutdownType ) ;
}
2020-12-04 19:51:25 -04:00
2021-09-30 23:01:43 -04:00
void UMeshGroupPaintTool : : CommitResult ( UBaseDynamicMeshComponent * Component , bool bModifiedTopology )
{
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " GroupPaintToolTransactionName " , " Paint Groups " ) ) ;
Component - > ProcessMesh ( [ & ] ( const FDynamicMesh3 & CurMesh )
2020-12-04 19:51:25 -04:00
{
2021-09-30 23:01:43 -04:00
UE : : ToolTarget : : CommitDynamicMeshUpdate ( Target , CurMesh , true ) ;
} ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
2020-12-04 19:51:25 -04:00
}
void UMeshGroupPaintTool : : RegisterActions ( FInteractiveToolActionSet & ActionSet )
{
UMeshSculptToolBase : : RegisterActions ( ActionSet ) ;
ActionSet . RegisterAction ( this , ( int32 ) EStandardToolActions : : BaseClientDefinedActionID + 500 ,
TEXT ( " PickGroupColorUnderCursor " ) ,
LOCTEXT ( " PickGroupColorUnderCursor " , " Pick PolyGroup " ) ,
LOCTEXT ( " PickGroupColorUnderCursorTooltip " , " Switch the active PolyGroup to the group currently under the cursor " ) ,
EModifierKey : : Shift , EKeys : : G ,
[ this ] ( ) { bPendingPickGroup = true ; } ) ;
ActionSet . RegisterAction ( this , ( int32 ) EStandardToolActions : : BaseClientDefinedActionID + 501 ,
TEXT ( " ToggleFrozenGroup " ) ,
LOCTEXT ( " ToggleFrozenGroup " , " Toggle Group Frozen State " ) ,
LOCTEXT ( " ToggleFrozenGroupTooltip " , " Toggle Group Frozen State " ) ,
EModifierKey : : Shift , EKeys : : F ,
[ this ] ( ) { bPendingToggleFreezeGroup = true ; } ) ;
ActionSet . RegisterAction ( this , ( int32 ) EStandardToolActions : : BaseClientDefinedActionID + 502 ,
TEXT ( " CreateNewGroup " ) ,
LOCTEXT ( " CreateNewGroup " , " New Group " ) ,
LOCTEXT ( " CreateNewGroupTooltip " , " Allocate a new Polygroup and set as Current " ) ,
EModifierKey : : Shift , EKeys : : Q ,
[ this ] ( ) { AllocateNewGroupAndSetAsCurrentAction ( ) ; } ) ;
} ;
2021-11-07 23:43:01 -05:00
TUniquePtr < FMeshSculptBrushOp > & UMeshGroupPaintTool : : GetActiveBrushOp ( )
{
if ( GetInEraseStroke ( ) )
{
return SecondaryBrushOp ;
}
else
{
return PrimaryBrushOp ;
}
}
2020-12-04 19:51:25 -04:00
void UMeshGroupPaintTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
{
CalculateBrushRadius ( ) ;
}
2021-11-07 23:43:01 -05:00
void UMeshGroupPaintTool : : IncreaseBrushRadiusAction ( )
{
Super : : IncreaseBrushRadiusAction ( ) ;
FilterProperties - > BrushSize = BrushProperties - > BrushSize . AdaptiveSize ;
2021-12-10 17:49:08 -05:00
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2021-11-07 23:43:01 -05:00
}
void UMeshGroupPaintTool : : DecreaseBrushRadiusAction ( )
{
Super : : DecreaseBrushRadiusAction ( ) ;
FilterProperties - > BrushSize = BrushProperties - > BrushSize . AdaptiveSize ;
2021-12-10 17:49:08 -05:00
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2021-11-07 23:43:01 -05:00
}
void UMeshGroupPaintTool : : IncreaseBrushRadiusSmallStepAction ( )
{
Super : : IncreaseBrushRadiusSmallStepAction ( ) ;
FilterProperties - > BrushSize = BrushProperties - > BrushSize . AdaptiveSize ;
2021-12-10 17:49:08 -05:00
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2021-11-07 23:43:01 -05:00
}
void UMeshGroupPaintTool : : DecreaseBrushRadiusSmallStepAction ( )
{
Super : : DecreaseBrushRadiusSmallStepAction ( ) ;
FilterProperties - > BrushSize = BrushProperties - > BrushSize . AdaptiveSize ;
2021-12-10 17:49:08 -05:00
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2021-11-07 23:43:01 -05:00
}
2020-12-15 12:58:13 -04:00
bool UMeshGroupPaintTool : : IsInBrushSubMode ( ) const
{
return FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : Brush
2021-07-26 19:27:37 -04:00
| | FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : Fill
| | FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : GroupFill ;
2020-12-15 12:58:13 -04:00
}
2020-12-04 19:51:25 -04:00
void UMeshGroupPaintTool : : OnBeginStroke ( const FRay & WorldRay )
{
UpdateBrushPosition ( WorldRay ) ;
2020-12-15 12:58:13 -04:00
if ( PaintBrushOpProperties )
{
PaintBrushOpProperties - > Group = FilterProperties - > SetGroup ;
PaintBrushOpProperties - > bOnlyPaintUngrouped = FilterProperties - > bOnlySetUngrouped ;
}
if ( EraseBrushOpProperties )
{
EraseBrushOpProperties - > Group = FilterProperties - > EraseGroup ;
EraseBrushOpProperties - > bOnlyEraseCurrent = FilterProperties - > bOnlyEraseCurrent ;
}
2020-12-04 19:51:25 -04:00
// initialize first "Last Stamp", so that we can assume all stamps in stroke have a valid previous stamp
LastStamp . WorldFrame = GetBrushFrameWorld ( ) ;
LastStamp . LocalFrame = GetBrushFrameLocal ( ) ;
LastStamp . Radius = GetCurrentBrushRadius ( ) ;
LastStamp . Falloff = GetCurrentBrushFalloff ( ) ;
LastStamp . Direction = GetInInvertStroke ( ) ? - 1.0 : 1.0 ;
LastStamp . Depth = GetCurrentBrushDepth ( ) ;
LastStamp . Power = GetActivePressure ( ) * GetCurrentBrushStrength ( ) ;
LastStamp . TimeStamp = FDateTime : : Now ( ) ;
FSculptBrushOptions SculptOptions ;
//SculptOptions.bPreserveUVFlow = false; // FilterProperties->bPreserveUVFlow;
SculptOptions . ConstantReferencePlane = GetCurrentStrokeReferencePlane ( ) ;
TUniquePtr < FMeshSculptBrushOp > & UseBrushOp = GetActiveBrushOp ( ) ;
UseBrushOp - > ConfigureOptions ( SculptOptions ) ;
UseBrushOp - > BeginStroke ( GetSculptMesh ( ) , LastStamp , VertexROI ) ;
AccumulatedTriangleROI . Reset ( ) ;
// begin change here? or wait for first stamp?
BeginChange ( ) ;
}
void UMeshGroupPaintTool : : OnEndStroke ( )
{
GetActiveBrushOp ( ) - > EndStroke ( GetSculptMesh ( ) , LastStamp , VertexROI ) ;
// close change record
EndChange ( ) ;
}
2020-12-15 12:58:13 -04:00
2020-12-04 19:51:25 -04:00
void UMeshGroupPaintTool : : UpdateROI ( const FSculptBrushStamp & BrushStamp )
{
SCOPE_CYCLE_COUNTER ( GroupPaintTool_UpdateROI ) ;
2021-11-07 23:43:01 -05:00
int32 SetGroupID = GetInEraseStroke ( ) ? FilterProperties - > EraseGroup : FilterProperties - > SetGroup ;
2021-07-29 02:15:09 -04:00
2020-12-04 19:51:25 -04:00
const FVector3d & BrushPos = BrushStamp . LocalFrame . Origin ;
const FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
float RadiusSqr = GetCurrentBrushRadius ( ) * GetCurrentBrushRadius ( ) ;
FAxisAlignedBox3d BrushBox (
BrushPos - GetCurrentBrushRadius ( ) * FVector3d : : One ( ) ,
BrushPos + GetCurrentBrushRadius ( ) * FVector3d : : One ( ) ) ;
TriangleROI . Reset ( ) ;
int32 CenterTID = GetBrushTriangleID ( ) ;
if ( Mesh - > IsTriangle ( CenterTID ) )
{
TriangleROI . Add ( CenterTID ) ;
}
2021-07-26 19:27:37 -04:00
FVector3d CenterNormal = Mesh - > IsTriangle ( CenterTID ) ? TriNormals [ CenterTID ] : FVector3d : : One ( ) ; // One so that normal check always passes
2021-07-29 02:15:09 -04:00
bool bVolumetric = ( FilterProperties - > BrushAreaMode = = EMeshGroupPaintBrushAreaType : : Volumetric ) ;
bool bUseAngleThreshold = ( bVolumetric = = false ) & & ( FilterProperties - > AngleThreshold < 180.0f ) ;
2021-07-26 19:27:37 -04:00
double DotAngleThreshold = FMathd : : Cos ( FilterProperties - > AngleThreshold * FMathd : : DegToRad ) ;
bool bStopAtUVSeams = FilterProperties - > bUVSeams ;
bool bStopAtNormalSeams = FilterProperties - > bNormalSeams ;
auto CheckEdgeCriteria = [ & ] ( int32 t1 , int32 t2 ) - > bool
{
if ( bUseAngleThreshold = = false | | CenterNormal . Dot ( TriNormals [ t2 ] ) > DotAngleThreshold )
{
int32 eid = Mesh - > FindEdgeFromTriPair ( t1 , t2 ) ;
if ( bStopAtUVSeams = = false | | UVSeamEdges [ eid ] = = false )
{
if ( bStopAtNormalSeams = = false | | NormalSeamEdges [ eid ] = = false )
{
return true ;
}
}
}
return false ;
} ;
2020-12-15 12:58:13 -04:00
bool bFill = ( FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : Fill ) ;
2021-07-26 19:27:37 -04:00
bool bGroupFill = ( FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : GroupFill ) ;
2020-12-15 12:58:13 -04:00
2021-07-29 02:15:09 -04:00
if ( bVolumetric )
2020-12-04 19:51:25 -04:00
{
Octree . RangeQuery ( BrushBox ,
[ & ] ( int TriIdx ) {
if ( ( Mesh - > GetTriCentroid ( TriIdx ) - BrushPos ) . SquaredLength ( ) < RadiusSqr )
{
TriangleROI . Add ( TriIdx ) ;
}
} ) ;
}
else
{
if ( Mesh - > IsTriangle ( CenterTID ) )
{
TArray < int32 > StartROI ;
StartROI . Add ( CenterTID ) ;
FMeshConnectedComponents : : GrowToConnectedTriangles ( Mesh , StartROI , TriangleROI , & TempROIBuffer ,
[ & ] ( int t1 , int t2 )
{
2021-07-26 19:27:37 -04:00
if ( ( Mesh - > GetTriCentroid ( t2 ) - BrushPos ) . SquaredLength ( ) < RadiusSqr )
2020-12-04 19:51:25 -04:00
{
2021-07-26 19:27:37 -04:00
return CheckEdgeCriteria ( t1 , t2 ) ;
2020-12-04 19:51:25 -04:00
}
return false ;
} ) ;
}
}
2021-07-29 02:15:09 -04:00
if ( bFill )
{
TArray < int32 > StartROI ;
for ( int32 tid : TriangleROI )
{
StartROI . Add ( tid ) ;
}
FMeshConnectedComponents : : GrowToConnectedTriangles ( Mesh , StartROI , TriangleROI , & TempROIBuffer ,
[ & ] ( int t1 , int t2 )
{
return CheckEdgeCriteria ( t1 , t2 ) ;
} ) ;
}
else if ( bGroupFill )
2021-07-26 19:27:37 -04:00
{
TArray < int32 > StartROI ;
TSet < int32 > FillGroups ;
for ( int32 tid : TriangleROI )
{
2021-07-29 02:15:09 -04:00
if ( ActiveGroupSet - > GetGroup ( tid ) ! = SetGroupID )
{
StartROI . Add ( tid ) ;
FillGroups . Add ( ActiveGroupSet - > GetGroup ( tid ) ) ;
}
2021-07-26 19:27:37 -04:00
}
FMeshConnectedComponents : : GrowToConnectedTriangles ( Mesh , StartROI , TriangleROI , & TempROIBuffer ,
[ & ] ( int t1 , int t2 )
{
2021-07-29 02:15:09 -04:00
return ( FillGroups . Contains ( ActiveGroupSet - > GetGroup ( t2 ) ) ) ;
2021-07-26 19:27:37 -04:00
} ) ;
}
2020-12-04 19:51:25 -04:00
// apply visibility filter
if ( FilterProperties - > VisibilityFilter ! = EMeshGroupPaintVisibilityType : : None )
{
2020-12-15 12:58:13 -04:00
TArray < int32 > ResultBuffer ;
ApplyVisibilityFilter ( TriangleROI , TempROIBuffer , ResultBuffer ) ;
2020-12-04 19:51:25 -04:00
}
2020-12-15 12:58:13 -04:00
// construct ROI vertex set
2020-12-04 19:51:25 -04:00
VertexSetBuffer . Reset ( ) ;
for ( int32 tid : TriangleROI )
{
FIndex3i Tri = Mesh - > GetTriangle ( tid ) ;
VertexSetBuffer . Add ( Tri . A ) ; VertexSetBuffer . Add ( Tri . B ) ; VertexSetBuffer . Add ( Tri . C ) ;
}
VertexROI . SetNum ( 0 , false ) ;
BufferUtil : : AppendElements ( VertexROI , VertexSetBuffer ) ;
2020-12-15 12:58:13 -04:00
// construct ROI triangle and group buffers
2020-12-04 19:51:25 -04:00
ROITriangleBuffer . Reserve ( TriangleROI . Num ( ) ) ;
ROITriangleBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : TriangleROI )
{
ROITriangleBuffer . Add ( tid ) ;
}
ROIGroupBuffer . SetNum ( ROITriangleBuffer . Num ( ) , false ) ;
}
bool UMeshGroupPaintTool : : UpdateStampPosition ( const FRay & WorldRay )
{
CalculateBrushRadius ( ) ;
TUniquePtr < FMeshSculptBrushOp > & UseBrushOp = GetActiveBrushOp ( ) ;
ESculptBrushOpTargetType TargetType = UseBrushOp - > GetBrushTargetType ( ) ;
switch ( TargetType )
{
case ESculptBrushOpTargetType : : SculptMesh :
case ESculptBrushOpTargetType : : TargetMesh :
UpdateBrushPositionOnSculptMesh ( WorldRay , true ) ;
break ;
case ESculptBrushOpTargetType : : ActivePlane :
check ( false ) ;
UpdateBrushPositionOnActivePlane ( WorldRay ) ;
break ;
}
if ( UseBrushOp - > GetAlignStampToView ( ) )
{
AlignBrushToView ( ) ;
}
CurrentStamp = LastStamp ;
CurrentStamp . DeltaTime = FMathd : : Min ( ( FDateTime : : Now ( ) - LastStamp . TimeStamp ) . GetTotalSeconds ( ) , 1.0 ) ;
CurrentStamp . WorldFrame = GetBrushFrameWorld ( ) ;
CurrentStamp . LocalFrame = GetBrushFrameLocal ( ) ;
CurrentStamp . Power = GetActivePressure ( ) * GetCurrentBrushStrength ( ) ;
CurrentStamp . PrevLocalFrame = LastStamp . LocalFrame ;
CurrentStamp . PrevWorldFrame = LastStamp . WorldFrame ;
FVector3d MoveDelta = CurrentStamp . LocalFrame . Origin - CurrentStamp . PrevLocalFrame . Origin ;
if ( UseBrushOp - > IgnoreZeroMovements ( ) & & MoveDelta . SquaredLength ( ) < FMathd : : ZeroTolerance )
{
return false ;
}
return true ;
}
2021-07-29 02:15:09 -04:00
bool UMeshGroupPaintTool : : ApplyStamp ( )
2020-12-04 19:51:25 -04:00
{
SCOPE_CYCLE_COUNTER ( GroupPaintToolApplyStamp ) ;
TUniquePtr < FMeshSculptBrushOp > & UseBrushOp = GetActiveBrushOp ( ) ;
// yuck
FMeshTriangleGroupEditBrushOp * GroupBrushOp = ( FMeshTriangleGroupEditBrushOp * ) UseBrushOp . Get ( ) ;
FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
GroupBrushOp - > ApplyStampByTriangles ( Mesh , CurrentStamp , ROITriangleBuffer , ROIGroupBuffer ) ;
2021-07-29 02:15:09 -04:00
bool bUpdated = SyncMeshWithGroupBuffer ( Mesh ) ;
2020-12-04 19:51:25 -04:00
LastStamp = CurrentStamp ;
LastStamp . TimeStamp = FDateTime : : Now ( ) ;
2021-07-29 02:15:09 -04:00
return bUpdated ;
2020-12-04 19:51:25 -04:00
}
2021-07-29 02:15:09 -04:00
bool UMeshGroupPaintTool : : SyncMeshWithGroupBuffer ( FDynamicMesh3 * Mesh )
2020-12-04 19:51:25 -04:00
{
2021-07-29 02:15:09 -04:00
int NumModified = 0 ;
2020-12-04 19:51:25 -04:00
const int32 NumT = ROITriangleBuffer . Num ( ) ;
// change update could be async here if we collected array of <idx,orig,new> and dispatched independenlty
for ( int32 k = 0 ; k < NumT ; + + k )
{
int TriIdx = ROITriangleBuffer [ k ] ;
int32 CurGroupID = ActiveGroupSet - > GetGroup ( TriIdx ) ;
if ( FrozenGroups . Contains ( CurGroupID ) ) // skip frozen groups
{
continue ;
}
2021-07-29 02:15:09 -04:00
if ( ROIGroupBuffer [ k ] ! = CurGroupID )
{
ActiveGroupEditBuilder - > SaveTriangle ( TriIdx , CurGroupID , ROIGroupBuffer [ k ] ) ;
ActiveGroupSet - > SetGroup ( TriIdx , ROIGroupBuffer [ k ] , * Mesh ) ;
//ActiveVertexChange->UpdateVertexColor(VertIdx, OrigColor, NewColor);
NumModified + + ;
}
2020-12-04 19:51:25 -04:00
}
2021-07-29 02:15:09 -04:00
return ( NumModified > 0 ) ;
2020-12-04 19:51:25 -04:00
}
2020-12-15 12:58:13 -04:00
template < typename RealType >
static bool FindPolylineSelfIntersection (
2021-08-30 18:03:07 -04:00
const TArray < UE : : Math : : TVector2 < RealType > > & Polyline ,
UE : : Math : : TVector2 < RealType > & IntersectionPointOut ,
2020-12-15 12:58:13 -04:00
FIndex2i & IntersectionIndexOut ,
bool bParallel = true )
{
int32 N = Polyline . Num ( ) ;
std : : atomic < bool > bSelfIntersects ( false ) ;
ParallelFor ( N - 1 , [ & ] ( int32 i )
{
TSegment2 < RealType > SegA ( Polyline [ i ] , Polyline [ i + 1 ] ) ;
for ( int32 j = i + 2 ; j < N - 1 & & bSelfIntersects = = false ; + + j )
{
TSegment2 < RealType > SegB ( Polyline [ j ] , Polyline [ j + 1 ] ) ;
if ( SegA . Intersects ( SegB ) & & bSelfIntersects = = false )
{
bool ExpectedValue = false ;
if ( std : : atomic_compare_exchange_strong ( & bSelfIntersects , & ExpectedValue , true ) )
{
TIntrSegment2Segment2 < RealType > Intersection ( SegA , SegB ) ;
Intersection . Find ( ) ;
IntersectionPointOut = Intersection . Point0 ;
IntersectionIndexOut = FIndex2i ( i , j ) ;
return ;
}
}
}
} , ( bParallel ) ? EParallelForFlags : : None : EParallelForFlags : : ForceSingleThread ) ;
return bSelfIntersects ;
}
template < typename RealType >
static bool FindPolylineSegmentIntersection (
2021-08-30 18:03:07 -04:00
const TArray < UE : : Math : : TVector2 < RealType > > & Polyline ,
2020-12-15 12:58:13 -04:00
const TSegment2 < RealType > & Segment ,
2021-08-30 18:03:07 -04:00
UE : : Math : : TVector2 < RealType > & IntersectionPointOut ,
2020-12-15 12:58:13 -04:00
int & IntersectionIndexOut )
{
int32 N = Polyline . Num ( ) ;
for ( int32 i = 0 ; i < N - 1 ; + + i )
{
TSegment2 < RealType > PolySeg ( Polyline [ i ] , Polyline [ i + 1 ] ) ;
if ( Segment . Intersects ( PolySeg ) )
{
TIntrSegment2Segment2 < RealType > Intersection ( Segment , PolySeg ) ;
Intersection . Find ( ) ;
IntersectionPointOut = Intersection . Point0 ;
IntersectionIndexOut = i ;
return true ;
}
}
return false ;
}
bool ApproxSelfClipPolyline ( TArray < FVector2f > & Polyline )
{
int32 N = Polyline . Num ( ) ;
// handle already-closed polylines
2021-08-30 18:03:07 -04:00
if ( Distance ( Polyline [ 0 ] , Polyline [ N - 1 ] ) < 0.0001f )
2020-12-15 12:58:13 -04:00
{
return true ;
}
FVector2f IntersectPoint ;
FIndex2i IntersectionIndex ( - 1 , - 1 ) ;
bool bSelfIntersects = FindPolylineSelfIntersection ( Polyline , IntersectPoint , IntersectionIndex ) ;
if ( bSelfIntersects )
{
TArray < FVector2f > NewPolyline ;
NewPolyline . Add ( IntersectPoint ) ;
for ( int32 i = IntersectionIndex . A ; i < = IntersectionIndex . B ; + + i )
{
NewPolyline . Add ( Polyline [ i ] ) ;
}
NewPolyline . Add ( IntersectPoint ) ;
Polyline = MoveTemp ( NewPolyline ) ;
return true ;
}
2021-03-17 19:32:44 -04:00
FVector2f StartDirOut = UE : : Geometry : : Normalized ( Polyline [ 0 ] - Polyline [ 1 ] ) ;
2020-12-15 12:58:13 -04:00
FLine2f StartLine ( Polyline [ 0 ] , StartDirOut ) ;
2021-03-17 19:32:44 -04:00
FVector2f EndDirOut = UE : : Geometry : : Normalized ( Polyline [ N - 1 ] - Polyline [ N - 2 ] ) ;
2020-12-15 12:58:13 -04:00
FLine2f EndLine ( Polyline [ N - 1 ] , EndDirOut ) ;
FIntrLine2Line2f LineIntr ( StartLine , EndLine ) ;
bool bIntersects = false ;
if ( LineIntr . Find ( ) )
{
bIntersects = LineIntr . IsSimpleIntersection ( ) & & ( LineIntr . Segment1Parameter > 0 ) & & ( LineIntr . Segment2Parameter > 0 ) ;
if ( bIntersects )
{
Polyline . Add ( StartLine . PointAt ( LineIntr . Segment1Parameter ) ) ;
Polyline . Add ( StartLine . Origin ) ;
return true ;
}
}
FAxisAlignedBox2f Bounds ;
for ( const FVector2f & P : Polyline )
{
Bounds . Contain ( P ) ;
}
float Size = Bounds . DiagonalLength ( ) ;
2021-08-30 18:03:07 -04:00
FVector2f StartPos = Polyline [ 0 ] + 0.001f * StartDirOut ;
2020-12-15 12:58:13 -04:00
if ( FindPolylineSegmentIntersection ( Polyline , FSegment2f ( StartPos , StartPos + 2 * Size * StartDirOut ) , IntersectPoint , IntersectionIndex . A ) )
{
//TArray<FVector2f> NewPolyline;
//for (int32 i = 0; i <= IntersectionIndex.A; ++i)
//{
// NewPolyline.Add(Polyline[i]);
//}
//NewPolyline.Add(IntersectPoint);
//NewPolyline.Add(Polyline[0]);
//Polyline = MoveTemp(NewPolyline);
return true ;
}
2021-08-30 18:03:07 -04:00
FVector2f EndPos = Polyline [ N - 1 ] + 0.001f * EndDirOut ;
2020-12-15 12:58:13 -04:00
if ( FindPolylineSegmentIntersection ( Polyline , FSegment2f ( EndPos , EndPos + 2 * Size * EndDirOut ) , IntersectPoint , IntersectionIndex . A ) )
{
//TArray<FVector2f> NewPolyline;
//NewPolyline.Add(IntersectPoint);
//for (int32 i = IntersectionIndex.A+1; i < N; ++i)
//{
// NewPolyline.Add(Polyline[i]);
//}
//NewPolyline.Add(Polyline[0]);
//NewPolyline.Add(IntersectPoint);
//Polyline = MoveTemp(NewPolyline);
return true ;
}
return false ;
}
void UMeshGroupPaintTool : : OnPolyLassoFinished ( const FCameraPolyLasso & Lasso , bool bCanceled )
{
// construct polyline
TArray < FVector2f > Polyline ;
for ( FVector2D Pos : Lasso . Polyline )
{
Polyline . Add ( ( FVector2f ) Pos ) ;
}
int32 N = Polyline . Num ( ) ;
if ( N < 2 )
{
return ;
}
// Try to clip polyline to be closed, or closed-enough for winding evaluation to work.
// If that returns false, the polyline is "too open". In that case we will extend
// outwards from the endpoints and then try to create a closed very large polygon
if ( ApproxSelfClipPolyline ( Polyline ) = = false )
{
2021-03-17 19:32:44 -04:00
FVector2f StartDirOut = UE : : Geometry : : Normalized ( Polyline [ 0 ] - Polyline [ 1 ] ) ;
2020-12-15 12:58:13 -04:00
FLine2f StartLine ( Polyline [ 0 ] , StartDirOut ) ;
2021-03-17 19:32:44 -04:00
FVector2f EndDirOut = UE : : Geometry : : Normalized ( Polyline [ N - 1 ] - Polyline [ N - 2 ] ) ;
2020-12-15 12:58:13 -04:00
FLine2f EndLine ( Polyline [ N - 1 ] , EndDirOut ) ;
// if we did not intersect, we are in ambiguous territory. Check if a segment along either end-direction
// intersects the polyline. If it does, we have something like a spiral and will be OK.
// If not, make a closed polygon by interpolating outwards from each endpoint, and then in perp-directions.
FPolygon2f Polygon ( Polyline ) ;
float PerpSign = Polygon . IsClockwise ( ) ? - 1.0 : 1.0 ;
Polyline . Insert ( StartLine . PointAt ( 10000.0f ) , 0 ) ;
2021-03-18 02:31:40 -04:00
Polyline . Insert ( Polyline [ 0 ] + 1000 * PerpSign * UE : : Geometry : : PerpCW ( StartDirOut ) , 0 ) ;
2020-12-15 12:58:13 -04:00
Polyline . Add ( EndLine . PointAt ( 10000.0f ) ) ;
2021-03-18 02:31:40 -04:00
Polyline . Add ( Polyline . Last ( ) + 1000 * PerpSign * UE : : Geometry : : PerpCW ( EndDirOut ) ) ;
2020-12-15 12:58:13 -04:00
FVector2f StartPos = Polyline [ 0 ] ;
Polyline . Add ( StartPos ) ; // close polyline (cannot use Polyline[0] in case Add resizes!)
}
N = Polyline . Num ( ) ;
// project each mesh vertex to view plane and evaluate winding integral of polyline
const FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
TempROIBuffer . SetNum ( Mesh - > MaxVertexID ( ) ) ;
ParallelFor ( Mesh - > MaxVertexID ( ) , [ & ] ( int32 vid )
{
if ( Mesh - > IsVertex ( vid ) )
{
FVector3d WorldPos = CurTargetTransform . TransformPosition ( Mesh - > GetVertex ( vid ) ) ;
FVector2f PlanePos = ( FVector2f ) Lasso . GetProjectedPoint ( ( FVector ) WorldPos ) ;
double WindingSum = 0 ;
FVector2f a = Polyline [ 0 ] - PlanePos , b = FVector2f : : Zero ( ) ;
for ( int32 i = 1 ; i < N ; + + i )
{
b = Polyline [ i ] - PlanePos ;
WindingSum + = ( double ) FMathf : : Atan2 ( a . X * b . Y - a . Y * b . X , a . X * b . X + a . Y * b . Y ) ;
a = b ;
}
WindingSum / = FMathd : : TwoPi ;
bool bInside = FMathd : : Abs ( WindingSum ) > 0.3 ;
TempROIBuffer [ vid ] = bInside ? 1 : 0 ;
}
else
{
TempROIBuffer [ vid ] = - 1 ;
}
} ) ;
// convert to vertex selection, and then select fully-enclosed faces
FMeshVertexSelection VertexSelection ( Mesh ) ;
VertexSelection . SelectByVertexID ( [ & ] ( int32 vid ) { return TempROIBuffer [ vid ] = = 1 ; } ) ;
FMeshFaceSelection FaceSelection ( Mesh , VertexSelection , FilterProperties - > MinTriVertCount ) ;
if ( FaceSelection . Num ( ) = = 0 )
{
return ;
}
2021-11-07 23:43:01 -05:00
int32 SetGroupID = GetInEraseStroke ( ) ? FilterProperties - > EraseGroup : FilterProperties - > SetGroup ;
SetTrianglesToGroupID ( FaceSelection . AsSet ( ) , SetGroupID , GetInEraseStroke ( ) ) ;
2020-12-15 12:58:13 -04:00
}
void UMeshGroupPaintTool : : SetTrianglesToGroupID ( const TSet < int32 > & Triangles , int32 ToGroupID , bool bIsErase )
{
BeginChange ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Triangles )
{
int32 CurGroupID = ActiveGroupSet - > GetGroup ( tid ) ;
if ( CurGroupID = = ToGroupID | | FrozenGroups . Contains ( CurGroupID ) ) // skip frozen groups
{
continue ;
}
if ( bIsErase = = false & & FilterProperties - > bOnlySetUngrouped & & CurGroupID ! = 0 )
{
continue ;
}
if ( bIsErase & & FilterProperties - > bOnlyEraseCurrent & & CurGroupID ! = FilterProperties - > SetGroup )
{
continue ;
}
TempROIBuffer . Add ( tid ) ;
}
if ( HaveVisibilityFilter ( ) )
{
TArray < int32 > VisibleTriangles ;
VisibleTriangles . Reserve ( TempROIBuffer . Num ( ) ) ;
ApplyVisibilityFilter ( TempROIBuffer , VisibleTriangles ) ;
TempROIBuffer = MoveTemp ( VisibleTriangles ) ;
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , ToGroupID , * GetSculptMesh ( ) ) ;
2020-12-15 12:58:13 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
bool UMeshGroupPaintTool : : HaveVisibilityFilter ( ) const
{
return FilterProperties - > VisibilityFilter ! = EMeshGroupPaintVisibilityType : : None ;
}
void UMeshGroupPaintTool : : ApplyVisibilityFilter ( TSet < int32 > & Triangles , TArray < int32 > & ROIBuffer , TArray < int32 > & OutputBuffer )
{
ROIBuffer . SetNum ( 0 , false ) ;
ROIBuffer . Reserve ( Triangles . Num ( ) ) ;
for ( int32 tid : Triangles )
{
ROIBuffer . Add ( tid ) ;
}
OutputBuffer . Reset ( ) ;
ApplyVisibilityFilter ( TempROIBuffer , OutputBuffer ) ;
Triangles . Reset ( ) ;
for ( int32 tid : OutputBuffer )
{
TriangleROI . Add ( tid ) ;
}
}
void UMeshGroupPaintTool : : ApplyVisibilityFilter ( const TArray < int32 > & Triangles , TArray < int32 > & VisibleTriangles )
{
if ( ! HaveVisibilityFilter ( ) )
{
VisibleTriangles = Triangles ;
return ;
}
FViewCameraState StateOut ;
GetToolManager ( ) - > GetContextQueriesAPI ( ) - > GetCurrentViewState ( StateOut ) ;
2021-06-11 22:42:32 -04:00
FTransform3d LocalToWorld = UE : : ToolTarget : : GetLocalToWorldTransform ( Target ) ;
FVector3d LocalEyePosition ( LocalToWorld . InverseTransformPosition ( StateOut . Position ) ) ;
2020-12-15 12:58:13 -04:00
const FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
int32 NumTriangles = Triangles . Num ( ) ;
VisibilityFilterBuffer . SetNum ( NumTriangles , false ) ;
ParallelFor ( NumTriangles , [ & ] ( int32 idx )
{
VisibilityFilterBuffer [ idx ] = true ;
FVector3d Centroid = Mesh - > GetTriCentroid ( Triangles [ idx ] ) ;
FVector3d FaceNormal = Mesh - > GetTriNormal ( Triangles [ idx ] ) ;
if ( FaceNormal . Dot ( ( Centroid - LocalEyePosition ) ) > 0 )
{
VisibilityFilterBuffer [ idx ] = false ;
}
if ( FilterProperties - > VisibilityFilter = = EMeshGroupPaintVisibilityType : : Unoccluded )
{
2021-03-17 19:32:44 -04:00
int32 HitTID = Octree . FindNearestHitObject ( FRay3d ( LocalEyePosition , UE : : Geometry : : Normalized ( Centroid - LocalEyePosition ) ) ) ;
2020-12-15 12:58:13 -04:00
if ( HitTID ! = Triangles [ idx ] )
{
VisibilityFilterBuffer [ idx ] = false ;
}
}
} ) ;
VisibleTriangles . Reset ( ) ;
for ( int32 k = 0 ; k < NumTriangles ; + + k )
{
if ( VisibilityFilterBuffer [ k ] )
{
VisibleTriangles . Add ( Triangles [ k ] ) ;
}
}
}
2020-12-04 19:51:25 -04:00
int32 UMeshGroupPaintTool : : FindHitSculptMeshTriangle ( const FRay3d & LocalRay )
{
2020-12-15 12:58:13 -04:00
if ( ! IsInBrushSubMode ( ) )
{
return IndexConstants : : InvalidID ;
}
2020-12-04 19:51:25 -04:00
if ( GetBrushCanHitBackFaces ( ) )
{
return Octree . FindNearestHitObject ( LocalRay ) ;
}
else
{
FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
FViewCameraState StateOut ;
GetToolManager ( ) - > GetContextQueriesAPI ( ) - > GetCurrentViewState ( StateOut ) ;
2021-03-30 21:25:22 -04:00
FVector3d LocalEyePosition ( CurTargetTransform . InverseTransformPosition ( ( FVector3d ) StateOut . Position ) ) ;
2020-12-04 19:51:25 -04:00
int HitTID = Octree . FindNearestHitObject ( LocalRay ,
[ this , Mesh , & LocalEyePosition ] ( int TriangleID ) {
FVector3d Normal , Centroid ;
double Area ;
Mesh - > GetTriInfo ( TriangleID , Normal , Area , Centroid ) ;
return Normal . Dot ( ( Centroid - LocalEyePosition ) ) < 0 ;
} ) ;
return HitTID ;
}
}
int32 UMeshGroupPaintTool : : FindHitTargetMeshTriangle ( const FRay3d & LocalRay )
{
check ( false ) ;
return IndexConstants : : InvalidID ;
}
bool UMeshGroupPaintTool : : UpdateBrushPosition ( const FRay & WorldRay )
{
TUniquePtr < FMeshSculptBrushOp > & UseBrushOp = GetActiveBrushOp ( ) ;
bool bHit = false ;
ESculptBrushOpTargetType TargetType = UseBrushOp - > GetBrushTargetType ( ) ;
switch ( TargetType )
{
case ESculptBrushOpTargetType : : SculptMesh :
case ESculptBrushOpTargetType : : TargetMesh :
bHit = UpdateBrushPositionOnSculptMesh ( WorldRay , false ) ;
break ;
case ESculptBrushOpTargetType : : ActivePlane :
check ( false ) ;
bHit = UpdateBrushPositionOnSculptMesh ( WorldRay , false ) ;
break ;
}
if ( bHit & & UseBrushOp - > GetAlignStampToView ( ) )
{
AlignBrushToView ( ) ;
}
return bHit ;
}
bool UMeshGroupPaintTool : : OnUpdateHover ( const FInputDeviceRay & DevicePos )
{
PendingStampType = FilterProperties - > PrimaryBrushType ;
if ( ensure ( InStroke ( ) = = false ) )
{
UpdateBrushPosition ( DevicePos . WorldRay ) ;
}
return true ;
}
2020-12-15 12:58:13 -04:00
void UMeshGroupPaintTool : : DrawHUD ( FCanvas * Canvas , IToolsContextRenderAPI * RenderAPI )
{
if ( PolyLassoMechanic )
{
2021-11-07 23:43:01 -05:00
// because the actual group change is deferred until mouse release, color the lasso to let the user know whether it will erase
PolyLassoMechanic - > LineColor = GetInEraseStroke ( ) ? FLinearColor : : Red : FLinearColor : : Green ;
2020-12-15 12:58:13 -04:00
PolyLassoMechanic - > DrawHUD ( Canvas , RenderAPI ) ;
}
}
2020-12-04 19:51:25 -04:00
void UMeshGroupPaintTool : : OnTick ( float DeltaTime )
{
UMeshSculptToolBase : : OnTick ( DeltaTime ) ;
MeshElementsDisplay - > OnTick ( DeltaTime ) ;
2020-12-15 12:58:13 -04:00
bool bIsLasso = ( FilterProperties - > SubToolType = = EMeshGroupPaintInteractionType : : PolyLasso ) ;
PolyLassoMechanic - > SetIsEnabled ( bIsLasso ) ;
ConfigureIndicator ( FilterProperties - > BrushAreaMode = = EMeshGroupPaintBrushAreaType : : Volumetric ) ;
SetIndicatorVisibility ( bIsLasso = = false ) ;
2020-12-04 19:51:25 -04:00
if ( bHavePendingAction )
{
ApplyAction ( PendingAction ) ;
bHavePendingAction = false ;
PendingAction = EMeshGroupPaintToolActions : : NoAction ;
}
SCOPE_CYCLE_COUNTER ( GroupPaintToolTick ) ;
// process the undo update
if ( bUndoUpdatePending )
{
// wait for updates
WaitForPendingUndoRedo ( ) ;
// post rendering update
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( AccumulatedTriangleROI , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
// ignore stamp and wait for next tick to do anything else
bUndoUpdatePending = false ;
return ;
}
if ( bPendingPickGroup | | bPendingToggleFreezeGroup )
{
if ( GetBrushTriangleID ( ) > = 0 & & IsStampPending ( ) = = false )
{
if ( GetSculptMesh ( ) - > IsTriangle ( GetBrushTriangleID ( ) ) )
{
int32 HitGroupID = ActiveGroupSet - > GetGroup ( GetBrushTriangleID ( ) ) ;
if ( bPendingPickGroup )
{
2020-12-15 12:58:13 -04:00
FilterProperties - > SetGroup = HitGroupID ;
2022-01-14 14:21:26 -05:00
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2020-12-04 19:51:25 -04:00
}
else if ( bPendingToggleFreezeGroup )
{
ToggleFrozenGroup ( HitGroupID ) ;
}
}
}
bPendingPickGroup = bPendingToggleFreezeGroup = false ;
}
2020-12-15 12:58:13 -04:00
if ( IsInBrushSubMode ( ) )
2020-12-04 19:51:25 -04:00
{
2021-01-31 20:16:41 -04:00
if ( InStroke ( ) )
2020-12-04 19:51:25 -04:00
{
SCOPE_CYCLE_COUNTER ( GroupPaintTool_Tick_ApplyStampBlock ) ;
// update brush position
if ( UpdateStampPosition ( GetPendingStampRayWorld ( ) ) = = false )
{
return ;
}
2021-01-31 20:16:41 -04:00
UpdateStampPendingState ( ) ;
if ( IsStampPending ( ) = = false )
{
return ;
}
2020-12-04 19:51:25 -04:00
// update sculpt ROI
UpdateROI ( CurrentStamp ) ;
// append updated ROI to modified region (async)
TFuture < void > AccumulateROI = Async ( GroupPaintToolAsyncExecTarget , [ & ] ( )
{
AccumulatedTriangleROI . Append ( TriangleROI ) ;
} ) ;
// apply the stamp
2021-07-29 02:15:09 -04:00
bool bGroupsModified = ApplyStamp ( ) ;
2020-12-04 19:51:25 -04:00
2021-07-29 02:15:09 -04:00
if ( bGroupsModified )
2020-12-04 19:51:25 -04:00
{
SCOPE_CYCLE_COUNTER ( GroupPaintTool_Tick_UpdateMeshBlock ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TriangleROI , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
// we don't really need to wait for these to happen to end Tick()...
AccumulateROI . Wait ( ) ;
}
}
}
void UMeshGroupPaintTool : : AllocateNewGroupAndSetAsCurrentAction ( )
{
2022-01-14 14:21:26 -05:00
FilterProperties - > SetGroup = ActiveGroupSet - > MaxGroupID ;
NotifyOfPropertyChangeByTool ( FilterProperties ) ;
2020-12-04 19:51:25 -04:00
}
FColor UMeshGroupPaintTool : : GetColorForGroup ( int32 GroupID )
{
FColor Color = LinearColors : : SelectFColor ( GroupID ) ;
if ( FrozenGroups . Contains ( GroupID ) )
{
int32 GrayValue = ( Color . R + Color . G + Color . B ) / 3 ;
Color . R = Color . G = Color . B = FMath : : Clamp ( GrayValue , 0 , 255 ) ;
}
return Color ;
}
void UMeshGroupPaintTool : : ToggleFrozenGroup ( int32 FreezeGroupID )
{
if ( FreezeGroupID = = 0 ) return ;
TArray < int32 > InitialFrozenGroups = FrozenGroups ;
if ( FrozenGroups . Contains ( FreezeGroupID ) )
{
FrozenGroups . Remove ( FreezeGroupID ) ;
}
else
{
FrozenGroups . Add ( FreezeGroupID ) ;
}
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
int32 TriGroupID = ActiveGroupSet - > GetGroup ( tid ) ;
if ( TriGroupID = = FreezeGroupID )
{
TempROIBuffer . Add ( tid ) ;
}
}
2022-02-11 18:25:48 -05:00
EmitFrozenGroupsChange ( InitialFrozenGroups , FrozenGroups , LOCTEXT ( " ToggleFrozenGroupChange " , " Toggle Frozen Group " ) ) ;
2020-12-04 19:51:25 -04:00
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
void UMeshGroupPaintTool : : FreezeOtherGroups ( int32 KeepGroupID )
{
TArray < int32 > InitialFrozenGroups = FrozenGroups ;
FrozenGroups . Reset ( ) ;
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
int32 GroupID = ActiveGroupSet - > GetGroup ( tid ) ;
if ( GroupID ! = 0 & & GroupID ! = KeepGroupID )
{
FrozenGroups . AddUnique ( ActiveGroupSet - > GetGroup ( tid ) ) ;
TempROIBuffer . Add ( tid ) ;
}
}
EmitFrozenGroupsChange ( InitialFrozenGroups , FrozenGroups , LOCTEXT ( " FreezeOtherGroups " , " Freeze Other Groups " ) ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
void UMeshGroupPaintTool : : ClearAllFrozenGroups ( )
{
TArray < int32 > InitialFrozenGroups = FrozenGroups ;
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
if ( FrozenGroups . Contains ( ActiveGroupSet - > GetGroup ( tid ) ) )
{
TempROIBuffer . Add ( tid ) ;
}
}
FrozenGroups . Reset ( ) ;
EmitFrozenGroupsChange ( InitialFrozenGroups , FrozenGroups , LOCTEXT ( " ClearAllFrozenGroups " , " Clear Frozen Groups " ) ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
void UMeshGroupPaintTool : : EmitFrozenGroupsChange ( const TArray < int32 > & FromGroups , const TArray < int32 > & ToGroups , const FText & ChangeText )
{
if ( FromGroups ! = ToGroups )
{
TUniquePtr < TSimpleValueLambdaChange < TArray < int32 > > > FrozenGroupsChange = MakeUnique < TSimpleValueLambdaChange < TArray < int32 > > > ( ) ;
FrozenGroupsChange - > FromValue = FromGroups ;
FrozenGroupsChange - > ToValue = ToGroups ;
FrozenGroupsChange - > ValueChangeFunc = [ this ] ( UObject * , const TArray < int32 > & FromGroups , const TArray < int32 > & ToGroups , bool )
{
FrozenGroups = ToGroups ;
DynamicMeshComponent - > FastNotifyVertexAttributesUpdated ( EMeshRenderAttributeFlags : : VertexColors ) ;
} ;
GetToolManager ( ) - > EmitObjectChange ( this , MoveTemp ( FrozenGroupsChange ) , ChangeText ) ;
}
}
void UMeshGroupPaintTool : : GrowCurrentGroupAction ( )
{
BeginChange ( ) ;
2020-12-15 12:58:13 -04:00
int32 CurrentGroupID = FilterProperties - > SetGroup ;
2020-12-04 19:51:25 -04:00
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
FMeshFaceSelection InitialSelection ( Mesh ) ;
InitialSelection . Select ( [ & ] ( int32 tid ) { return ActiveGroupSet - > GetGroup ( tid ) = = CurrentGroupID ; } ) ;
FMeshFaceSelection ExpandSelection ( InitialSelection ) ;
ExpandSelection . ExpandToOneRingNeighbours ( [ & ] ( int32 tid ) { return FrozenGroups . Contains ( ActiveGroupSet - > GetGroup ( tid ) ) = = false ; } ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
ExpandSelection . SetDifference ( InitialSelection , TempROIBuffer ) ;
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , CurrentGroupID , * GetSculptMesh ( ) ) ;
2020-12-04 19:51:25 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
void UMeshGroupPaintTool : : ShrinkCurrentGroupAction ( )
{
BeginChange ( ) ;
2020-12-15 12:58:13 -04:00
int32 CurrentGroupID = FilterProperties - > SetGroup ;
2020-12-04 19:51:25 -04:00
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
FMeshFaceSelection InitialSelection ( Mesh ) ;
InitialSelection . Select ( [ & ] ( int32 tid ) { return ActiveGroupSet - > GetGroup ( tid ) = = CurrentGroupID ; } ) ;
FMeshFaceSelection ContractSelection ( InitialSelection ) ;
ContractSelection . ContractBorderByOneRingNeighbours ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
InitialSelection . SetDifference ( ContractSelection , TempROIBuffer ) ;
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
// todo: could probably guess boundary groups here...
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , 0 , * GetSculptMesh ( ) ) ;
2020-12-04 19:51:25 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
2020-12-15 12:58:13 -04:00
void UMeshGroupPaintTool : : ClearCurrentGroupAction ( )
{
BeginChange ( ) ;
int32 CurrentGroupID = FilterProperties - > SetGroup ;
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
if ( ActiveGroupSet - > GetGroup ( tid ) = = CurrentGroupID )
{
TempROIBuffer . Add ( tid ) ;
}
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , 0 , * GetSculptMesh ( ) ) ;
2020-12-15 12:58:13 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
void UMeshGroupPaintTool : : FloodFillCurrentGroupAction ( )
{
BeginChange ( ) ;
int32 SetGroupID = FilterProperties - > SetGroup ;
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
int32 GroupID = ActiveGroupSet - > GetGroup ( tid ) ;
if ( GroupID = = 0 & & GroupID ! = SetGroupID & & FrozenGroups . Contains ( GroupID ) = = false )
{
TempROIBuffer . Add ( tid ) ;
}
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , SetGroupID , * GetSculptMesh ( ) ) ;
2020-12-15 12:58:13 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
void UMeshGroupPaintTool : : ClearAllGroupsAction ( )
{
BeginChange ( ) ;
const FDynamicMesh3 * Mesh = DynamicMeshComponent - > GetMesh ( ) ;
TempROIBuffer . SetNum ( 0 , false ) ;
for ( int32 tid : Mesh - > TriangleIndicesItr ( ) )
{
if ( ActiveGroupSet - > GetGroup ( tid ) ! = 0 )
{
TempROIBuffer . Add ( tid ) ;
}
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
for ( int32 tid : TempROIBuffer )
{
2021-06-25 01:41:35 -04:00
ActiveGroupSet - > SetGroup ( tid , 0 , * GetSculptMesh ( ) ) ;
2020-12-15 12:58:13 -04:00
}
ActiveGroupEditBuilder - > SaveTriangles ( TempROIBuffer ) ;
DynamicMeshComponent - > FastNotifyTriangleVerticesUpdated ( TempROIBuffer , EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
EndChange ( ) ;
}
2020-12-04 19:51:25 -04:00
//
// Change Tracking
//
void UMeshGroupPaintTool : : BeginChange ( )
{
check ( ActiveGroupEditBuilder = = nullptr ) ;
ActiveGroupEditBuilder = MakeUnique < FDynamicMeshGroupEditBuilder > ( ActiveGroupSet . Get ( ) ) ;
}
void UMeshGroupPaintTool : : EndChange ( )
{
check ( ActiveGroupEditBuilder ) ;
TUniquePtr < FDynamicMeshGroupEdit > EditResult = ActiveGroupEditBuilder - > ExtractResult ( ) ;
ActiveGroupEditBuilder = nullptr ;
TUniquePtr < TWrappedToolCommandChange < FMeshPolygroupChange > > NewChange = MakeUnique < TWrappedToolCommandChange < FMeshPolygroupChange > > ( ) ;
NewChange - > WrappedChange = MakeUnique < FMeshPolygroupChange > ( MoveTemp ( EditResult ) ) ;
NewChange - > BeforeModify = [ this ] ( bool bRevert )
{
this - > WaitForPendingUndoRedo ( ) ;
} ;
GetToolManager ( ) - > EmitObjectChange ( DynamicMeshComponent , MoveTemp ( NewChange ) , LOCTEXT ( " GroupPaintChange " , " Group Stroke " ) ) ;
}
void UMeshGroupPaintTool : : WaitForPendingUndoRedo ( )
{
if ( bUndoUpdatePending )
{
bUndoUpdatePending = false ;
}
}
2021-06-12 14:30:22 -04:00
void UMeshGroupPaintTool : : OnDynamicMeshComponentChanged ( UDynamicMeshComponent * Component , const FMeshVertexChange * Change , bool bRevert )
2020-12-04 19:51:25 -04:00
{
// update octree
FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
// make sure any previous async computations are done, and update the undo ROI
if ( bUndoUpdatePending )
{
// we should never hit this anymore, because of pre-change calling WaitForPendingUndoRedo()
WaitForPendingUndoRedo ( ) ;
// this is not right because now we are going to do extra recomputation, but it's very messy otherwise...
2021-05-22 01:32:46 -04:00
UE : : Geometry : : VertexToTriangleOneRing ( Mesh , Change - > Vertices , AccumulatedTriangleROI ) ;
2020-12-04 19:51:25 -04:00
}
else
{
AccumulatedTriangleROI . Reset ( ) ;
2021-05-22 01:32:46 -04:00
UE : : Geometry : : VertexToTriangleOneRing ( Mesh , Change - > Vertices , AccumulatedTriangleROI ) ;
2020-12-04 19:51:25 -04:00
}
// note that we have a pending update
bUndoUpdatePending = true ;
}
void UMeshGroupPaintTool : : PrecomputeFilterData ( )
{
const FDynamicMesh3 * Mesh = GetSculptMesh ( ) ;
TriNormals . SetNum ( Mesh - > MaxTriangleID ( ) ) ;
ParallelFor ( Mesh - > MaxTriangleID ( ) , [ & ] ( int32 tid )
{
if ( Mesh - > IsTriangle ( tid ) )
{
TriNormals [ tid ] = Mesh - > GetTriNormal ( tid ) ;
}
} ) ;
const FDynamicMeshNormalOverlay * Normals = Mesh - > Attributes ( ) - > PrimaryNormals ( ) ;
const FDynamicMeshUVOverlay * UVs = Mesh - > Attributes ( ) - > PrimaryUV ( ) ;
UVSeamEdges . SetNum ( Mesh - > MaxEdgeID ( ) ) ;
NormalSeamEdges . SetNum ( Mesh - > MaxEdgeID ( ) ) ;
ParallelFor ( Mesh - > MaxEdgeID ( ) , [ & ] ( int32 eid )
{
if ( Mesh - > IsEdge ( eid ) )
{
UVSeamEdges [ eid ] = UVs - > IsSeamEdge ( eid ) ;
NormalSeamEdges [ eid ] = Normals - > IsSeamEdge ( eid ) ;
}
} ) ;
}
void UMeshGroupPaintTool : : OnSelectedGroupLayerChanged ( )
{
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " ChangeActiveGroupLayer " , " Change Polygroup Layer " ) ) ;
TArray < int32 > InitialFrozenGroups = FrozenGroups ;
int32 ActiveLayerIndex = ( ActiveGroupSet ) ? ActiveGroupSet - > GetPolygroupIndex ( ) : - 1 ;
UpdateActiveGroupLayer ( ) ;
int32 NewLayerIndex = ( ActiveGroupSet ) ? ActiveGroupSet - > GetPolygroupIndex ( ) : - 1 ;
if ( ActiveLayerIndex ! = NewLayerIndex )
{
// clear frozen groups
EmitFrozenGroupsChange ( InitialFrozenGroups , FrozenGroups , LOCTEXT ( " ClearAllFrozenGroups " , " Clear Frozen Groups " ) ) ;
TUniquePtr < TSimpleValueLambdaChange < int32 > > GroupLayerChange = MakeUnique < TSimpleValueLambdaChange < int32 > > ( ) ;
GroupLayerChange - > FromValue = ActiveLayerIndex ;
GroupLayerChange - > ToValue = NewLayerIndex ;
GroupLayerChange - > ValueChangeFunc = [ this ] ( UObject * , int32 FromIndex , int32 ToIndex , bool )
{
this - > PolygroupLayerProperties - > SetSelectedFromPolygroupIndex ( ToIndex ) ;
this - > PolygroupLayerProperties - > SilentUpdateWatched ( ) ; // to prevent OnSelectedGroupLayerChanged() from being called immediately
this - > UpdateActiveGroupLayer ( ) ;
} ;
GetToolManager ( ) - > EmitObjectChange ( this , MoveTemp ( GroupLayerChange ) , LOCTEXT ( " ChangeActiveGroupLayer " , " Change Polygroup Layer " ) ) ;
}
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
void UMeshGroupPaintTool : : UpdateActiveGroupLayer ( )
{
if ( PolygroupLayerProperties - > HasSelectedPolygroup ( ) = = false )
{
ActiveGroupSet = MakeUnique < UE : : Geometry : : FPolygroupSet > ( GetSculptMesh ( ) ) ;
}
else
{
FName SelectedName = PolygroupLayerProperties - > ActiveGroupLayer ;
FDynamicMeshPolygroupAttribute * FoundAttrib = UE : : Geometry : : FindPolygroupLayerByName ( * GetSculptMesh ( ) , SelectedName ) ;
ensureMsgf ( FoundAttrib , TEXT ( " Selected Attribute Not Found! Falling back to Default group layer. " ) ) ;
ActiveGroupSet = MakeUnique < UE : : Geometry : : FPolygroupSet > ( GetSculptMesh ( ) , FoundAttrib ) ;
}
// need to reset everything here...
FrozenGroups . Reset ( ) ;
// update colors
DynamicMeshComponent - > FastNotifyVertexAttributesUpdated ( EMeshRenderAttributeFlags : : VertexColors ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
void UMeshGroupPaintTool : : UpdateSubToolType ( EMeshGroupPaintInteractionType NewType )
{
2021-03-02 19:23:29 -04:00
// Currenly we mirror base-brush properties in UGroupPaintBrushFilterProperties, so we never
// want to show both
2020-12-15 12:58:13 -04:00
//bool bSculptPropsVisible = (NewType == EMeshGroupPaintInteractionType::Brush);
//SetToolPropertySourceEnabled(UMeshSculptToolBase::BrushProperties, bSculptPropsVisible);
SetToolPropertySourceEnabled ( UMeshSculptToolBase : : BrushProperties , false ) ;
SetToolPropertySourceEnabled ( FilterProperties , true ) ;
SetBrushOpPropsVisibility ( false ) ;
2020-12-04 19:51:25 -04:00
}
void UMeshGroupPaintTool : : UpdateBrushType ( EMeshGroupPaintBrushType BrushType )
{
static const FText BaseMessage = LOCTEXT ( " OnStartTool " , " Hold Shift to Erase. [/] and S/D change Size (+Shift to small-step). Shift+Q for New Group, Shift+G to pick Group, Shift+F to Freeze Group. " ) ;
FTextBuilder Builder ;
Builder . AppendLine ( BaseMessage ) ;
SetActivePrimaryBrushType ( ( int32 ) BrushType ) ;
SetToolPropertySourceEnabled ( GizmoProperties , false ) ;
GetToolManager ( ) - > DisplayMessage ( Builder . ToText ( ) , EToolMessageLevel : : UserNotification ) ;
}
void UMeshGroupPaintTool : : RequestAction ( EMeshGroupPaintToolActions ActionType )
{
if ( ! bHavePendingAction )
{
PendingAction = ActionType ;
bHavePendingAction = true ;
}
}
void UMeshGroupPaintTool : : ApplyAction ( EMeshGroupPaintToolActions ActionType )
{
switch ( ActionType )
{
case EMeshGroupPaintToolActions : : ClearFrozen :
ClearAllFrozenGroups ( ) ;
break ;
case EMeshGroupPaintToolActions : : FreezeCurrent :
2020-12-15 12:58:13 -04:00
ToggleFrozenGroup ( FilterProperties - > SetGroup ) ;
2020-12-04 19:51:25 -04:00
break ;
case EMeshGroupPaintToolActions : : FreezeOthers :
2020-12-15 12:58:13 -04:00
FreezeOtherGroups ( FilterProperties - > SetGroup ) ;
2020-12-04 19:51:25 -04:00
break ;
case EMeshGroupPaintToolActions : : GrowCurrent :
GrowCurrentGroupAction ( ) ;
break ;
case EMeshGroupPaintToolActions : : ShrinkCurrent :
ShrinkCurrentGroupAction ( ) ;
break ;
2020-12-15 12:58:13 -04:00
case EMeshGroupPaintToolActions : : ClearCurrent :
ClearCurrentGroupAction ( ) ;
break ;
case EMeshGroupPaintToolActions : : FloodFillCurrent :
FloodFillCurrentGroupAction ( ) ;
break ;
case EMeshGroupPaintToolActions : : ClearAll :
ClearAllGroupsAction ( ) ;
break ;
2020-12-04 19:51:25 -04:00
}
}
2020-12-15 12:58:13 -04:00
2020-12-04 19:51:25 -04:00
# undef LOCTEXT_NAMESPACE