Files
UnrealEngine/Engine/Plugins/Runtime/MeshModelingToolset/Source/MeshModelingTools/Public/Sculpting/MeshSculptToolBase.h
2025-05-18 13:04:45 +08:00

700 lines
26 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BaseTools/MeshSurfacePointTool.h"
#include "BaseTools/BaseBrushTool.h"
#include "Sculpting/MeshBrushOpBase.h"
#include "BoxTypes.h"
#include "Properties/MeshMaterialProperties.h"
#include "Changes/ValueWatcher.h"
#include "Components/BaseDynamicMeshComponent.h"
#include "TransactionUtil.h"
#include "MeshSculptToolBase.generated.h"
#define UE_API MESHMODELINGTOOLS_API
class ULocalTwoAxisPropertyEditInputBehavior;
class UMaterialInstanceDynamic;
class UCombinedTransformGizmo;
class UTransformProxy;
class UPreviewMesh;
class UPreviewGeometry;
/**
* Type of Brush Size currently active in FBrushToolRadius
*/
UENUM()
enum class EBrushToolSizeType : uint8
{
/** Brush size is a dimensionless scale relative to the target object size */
Adaptive = 0,
/** Brush size is defined in world dimensions */
World = 1
};
/**
* FBrushToolRadius is used to define the size of 3D "brushes" used in (eg) sculpting tools.
* The brush size can be defined in various ways.
*/
USTRUCT()
struct FBrushToolRadius
{
GENERATED_BODY()
/** Specify the type of brush size currently in use */
UPROPERTY(EditAnywhere, Category = Brush)
EBrushToolSizeType SizeType = EBrushToolSizeType::Adaptive;
/** Adaptive brush size is used to interpolate between an object-specific minimum and maximum brush size */
UPROPERTY(EditAnywhere, Category = Brush, AdvancedDisplay, meta = (EditCondition = "SizeType == EBrushToolSizeType::Adaptive",
DisplayName = "Size", UIMin = "0.0", UIMax = "1.0", ClampMin = "0.0", ClampMax = "10.0"))
float AdaptiveSize = 0.25;
/** World brush size is a dimension in world coordinates */
UPROPERTY(EditAnywhere, Category = Brush, AdvancedDisplay, meta = (EditCondition = "SizeType == EBrushToolSizeType::World",
DisplayName = "World Radius", UIMin = "1.0", UIMax = "1000.0", ClampMin = "0.1", ClampMax = "50000.0"))
float WorldRadius = 100.0;
UPROPERTY(EditAnywhere, Category = Brush)
bool bToolSupportsPressureSensitivity = false;
UPROPERTY(EditAnywhere, Category = Brush, meta = (EditCondition = "bToolSupportsPressureSensitivity"))
bool bEnablePressureSensitivity = false;
/**
* WorldSizeRange defines the min/max dimensions for Adaptive brush size
*/
TInterval<float> WorldSizeRange = TInterval<float>(1.0f, 1000.0f);
//
// util functions
//
/** Set the WorldSizeRange value and optionally clamp the WorldRadius based on this new range */
UE_API void InitializeWorldSizeRange(TInterval<float> Range, bool bValidateWorldRadius = true);
/** Return the set/calculated world-space radius for the current settings */
UE_API float GetWorldRadius() const;
/** Increase the current radius dimension by a fixed step (or a smaller fixed step) */
UE_API void IncreaseRadius(bool bSmallStep);
/** Decrease the current radius dimension by a fixed step (or a smaller fixed step) */
UE_API void DecreaseRadius(bool bSmallStep);
};
/** Mesh Sculpting Brush Types */
UENUM()
enum class EMeshSculptFalloffType : uint8
{
Smooth = 0,
Linear = 1,
Inverse = 2,
Round = 3,
BoxSmooth = 4,
BoxLinear = 5,
BoxInverse = 6,
BoxRound = 7,
LastValue UMETA(Hidden)
};
UCLASS(MinimalAPI)
class USculptBrushProperties : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = Stroke)
EMeshSculptStrokeType StrokeType = EMeshSculptStrokeType::Spacing;
UPROPERTY(EditAnywhere, Category = Brush, meta = (DisplayPriority = 1, ModelingQuickEdit, ModelingQuickSettings = 100))
FBrushToolRadius BrushSize;
/** Amount of falloff to apply (0.0 - 1.0) */
UPROPERTY(EditAnywhere, Category = Brush, meta = (DisplayName = "Falloff", UIMin = "0.0", UIMax = "1.0", ClampMin = "0.0", ClampMax = "1.0", HideEditConditionToggle, EditConditionHides, EditCondition="bShowFalloff", DisplayPriority = 3))
float BrushFalloffAmount;
/** If false, then BrushFalloffAmount will not be shown in DetailsView panels (otherwise no effect) */
UPROPERTY(meta = (TransientToolProperty))
bool bShowFalloff = true;
/** Depth of Brush into surface along view ray or surface normal, depending on the Active Brush Type */
UPROPERTY(EditAnywhere, Category = Brush, meta = (UIMin = "-0.5", UIMax = "0.5", ClampMin = "-1.0", ClampMax = "1.0", DisplayPriority = 5, HideEditConditionToggle, EditConditionHides, EditCondition = "bShowPerBrushProps"))
float Depth = 0;
/** Allow the Brush to hit the back-side of the mesh */
UPROPERTY(EditAnywhere, Category = Brush, meta = (DisplayPriority = 6))
bool bHitBackFaces = true;
/** Brush stamps are applied at this time interval. 0 for a single stamp, 1 for continuous stamps, 0.5 is a stamp every half-second */
UPROPERTY(EditAnywhere, Category = Brush, meta = (DisplayName = "Flow", UIMin = "0.0", UIMax = "1.0", ClampMin = "0.0", ClampMax = "1.0", DisplayPriority = 7, HideEditConditionToggle, EditConditionHides, EditCondition = "bShowFlowRate && StrokeType == EMeshSculptStrokeType::Airbrush"))
float FlowRate = 1.0f;
/** Space out stamp centers at distances Spacing*BrushDiameter along the stroke (so Spacing of 1 means that stamps will be adjacent but non-overlapping). Zero spacing means continuous stamps. */
UPROPERTY(EditAnywhere, Category = Brush, meta = (UIMin = "0.0", UIMax = "4.0", ClampMin = "0.0", ClampMax = "1000.0", DisplayPriority = 8, HideEditConditionToggle, EditConditionHides, EditCondition = "bShowSpacing && StrokeType == EMeshSculptStrokeType::Spacing"))
float Spacing = .05f;
/** Lazy brush smooths out the brush path by averaging the cursor positions */
UPROPERTY(EditAnywhere, Category = Brush, meta = (DisplayName = "Lazy", UIMin = "0", UIMax = "1.0", ClampMin = "0", ClampMax = "1.0", DisplayPriority = 9, HideEditConditionToggle, EditConditionHides, EditCondition = "bShowLazyness"))
float Lazyness = 0;
/** */
UPROPERTY( meta = (TransientToolProperty))
bool bShowPerBrushProps = true;
/** */
UPROPERTY(meta = (TransientToolProperty))
bool bShowLazyness = true;
/** */
UPROPERTY(meta = (TransientToolProperty))
bool bShowFlowRate = true;
UPROPERTY(meta = (TransientToolProperty))
bool bShowSpacing = true;
};
UCLASS(MinimalAPI)
class UKelvinBrushProperties : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
/** Brush Fall off as fraction of brush size*/
UPROPERTY(EditAnywhere, Category = Kelvin, meta = (UIMin = "0.0", ClampMin = "0.0"))
float FallOffDistance = 1.f;
/** How much the mesh resists shear */
UPROPERTY(EditAnywhere, Category = Kelvin, meta = (UIMin = "0.0", ClampMin = "0.0"))
float Stiffness = 1.f;
/** How compressible the spatial region is: 1 - 2 x Poisson ratio */
UPROPERTY(EditAnywhere, Category = Kelvin, meta = (UIMin = "0.0", UIMax = "0.1", ClampMin = "0.0", ClampMax = "1.0"))
float Incompressiblity = 1.f;
/** Integration steps*/
UPROPERTY(EditAnywhere, Category = Kelvin, meta = (UIMin = "0.0", UIMax = "100", ClampMin = "0.0", ClampMax = "100"))
int BrushSteps = 3;
};
UCLASS(MinimalAPI)
class UWorkPlaneProperties : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
UPROPERTY( meta = (TransientToolProperty) )
bool bPropertySetEnabled = true;
/** Toggle whether Work Plane Positioning Gizmo is visible */
UPROPERTY(EditAnywhere, Category = TargetPlane, meta = (HideEditConditionToggle, EditCondition = "bPropertySetEnabled == true"))
bool bShowGizmo = true;
UPROPERTY(EditAnywhere, Category = TargetPlane, AdvancedDisplay, meta = (HideEditConditionToggle, EditCondition = "bPropertySetEnabled == true"))
FVector Position = FVector::ZeroVector;
UPROPERTY(EditAnywhere, Category = TargetPlane, AdvancedDisplay, meta = (HideEditConditionToggle, EditCondition = "bPropertySetEnabled == true"))
FQuat Rotation = FQuat::Identity;
// Recenter the gizmo around the target position (without changing work plane), if it is "too far" (> 10 meters + max bounds dim) from that position currently
void RecenterGizmoIfFar(FVector CenterPosition, double BoundsMaxDim, double TooFarDistance = 1000)
{
double DistanceTolSq = (BoundsMaxDim + TooFarDistance) * (BoundsMaxDim + TooFarDistance);
if (FVector::DistSquared(CenterPosition, Position) > DistanceTolSq)
{
FVector Normal = Rotation.GetAxisZ();
Position = CenterPosition - (CenterPosition - Position).ProjectOnToNormal(Normal);
}
}
};
UCLASS(MinimalAPI)
class USculptMaxBrushProperties : public UInteractiveToolPropertySet
{
GENERATED_BODY()
public:
/** Specify maximum displacement height (relative to brush size) */
UPROPERTY(EditAnywhere, Category = SculptMaxBrush, meta = (UIMin = "0.0", UIMax = "1.0", ClampMin = "0.0", ClampMax = "1.0"))
float MaxHeight = 0.5;
/** Use maximum height from last brush stroke, regardless of brush size. Note that spatial brush falloff still applies. */
UPROPERTY(EditAnywhere, Category = SculptMaxBrush)
bool bFreezeCurrentHeight = false;
};
/**
* Base Tool for mesh sculpting tools, provides some shared functionality
*/
UCLASS(MinimalAPI)
class UMeshSculptToolBase : public UMeshSurfacePointTool
{
GENERATED_BODY()
protected:
using FFrame3d = UE::Geometry::FFrame3d;
public:
UE_API virtual void RegisterActions(FInteractiveToolActionSet& ActionSet) override;
UE_API virtual void SetWorld(UWorld* World);
UE_API virtual void Setup() override;
UE_API virtual void Shutdown(EToolShutdownType ShutdownType) override;
UE_API virtual void Render(IToolsContextRenderAPI* RenderAPI) override;
// UMeshSurfacePointTool API
UE_API virtual bool HitTest(const FRay& Ray, FHitResult& OutHit) override;
UE_API virtual void OnBeginDrag(const FRay& Ray) override;
UE_API virtual void OnUpdateDrag(const FRay& Ray) override;
UE_API virtual void OnEndDrag(const FRay& Ray) override;
UE_API virtual void OnCancelDrag() override;
// end UMeshSurfacePointTool API
protected:
UE_API virtual void OnTick(float DeltaTime) override;
UE_API virtual void OnCompleteSetup();
virtual void OnBeginStroke(const FRay& WorldRay) { check(false); }
virtual void OnEndStroke() { check(false); }
virtual void OnCancelStroke() { check(false); }
public:
/** Properties that control brush size/etc */
UPROPERTY()
TObjectPtr<USculptBrushProperties> BrushProperties;
/** Properties for 3D workplane / gizmo */
UPROPERTY()
TObjectPtr<UWorkPlaneProperties> GizmoProperties;
protected:
UWorld* TargetWorld; // required to spawn UPreviewMesh/etc
FViewCameraState CameraState;
/** Initial transformation on target mesh */
UE::Geometry::FTransformSRT3d InitialTargetTransform;
/** Active transformation on target mesh, includes baked scale */
UE::Geometry::FTransformSRT3d CurTargetTransform;
UE_API FRay3d GetLocalRay(const FRay& WorldRay) const;
/** Returns true if BrushSize Pressure Sensitivity is both supported by the tool and currently enabled. */
UE_API bool GetBrushSizePressureSensitivityEnabled() const;
/** Returns true if Pressure Sensitivity for Brush Strength is both supported by the tool and currently enabled. */
UE_API bool GetBrushStrengthPressureEnabled();
/**
* Subclass must implement this and return relevant rendering component
*/
virtual UBaseDynamicMeshComponent* GetSculptMeshComponent() { check(false); return nullptr; }
virtual FDynamicMesh3* GetSculptMesh() { return GetSculptMeshComponent()->GetMesh(); }
virtual const FDynamicMesh3* GetSculptMesh() const { return const_cast<UMeshSculptToolBase*>(this)->GetSculptMeshComponent()->GetMesh(); }
virtual FDynamicMesh3* GetBaseMesh() { check(false); return nullptr; }
virtual const FDynamicMesh3* GetBaseMesh() const { check(false); return nullptr; }
// For any subclass where this returns false, BrushProperties will not be automatically saved/restored, so the class won't use BrushProperties changes made in other tools.
virtual bool SharesBrushPropertiesChanges() const { return true; }
/**
* Subclass calls this to set up editing component
*/
UE_API void InitializeSculptMeshComponent(UBaseDynamicMeshComponent* Component, AActor* Actor);
/**
* Subclass can override this to change what results are written.
* Default is to apply a default vertex positions update to the target object.
*/
UE_API virtual void CommitResult(UBaseDynamicMeshComponent* Component, bool bModifiedTopology);
//
// Brush Types
//
public:
struct FBrushTypeInfo
{
FText Name;
int32 Identifier;
bool operator==( const FBrushTypeInfo& Other ) const
{
return Other.Identifier == Identifier &&
Other.Name.EqualTo(Name);
}
friend uint32 GetTypeHash(const FBrushTypeInfo& Other)
{
return HashCombine(GetTypeHash(Other.Identifier), GetTypeHash(Other.Name.ToString()));
}
};
const TSet<FBrushTypeInfo>& GetRegisteredPrimaryBrushTypes() const { return RegisteredPrimaryBrushTypes; }
const TSet<FBrushTypeInfo>& GetRegisteredSecondaryBrushTypes() const { return RegisteredSecondaryBrushTypes; }
protected:
TSet<FBrushTypeInfo> RegisteredPrimaryBrushTypes;
TSet<FBrushTypeInfo> RegisteredSecondaryBrushTypes;
UPROPERTY()
TMap<int32, TObjectPtr<UMeshSculptBrushOpProps>> BrushOpPropSets;
TMap<int32, TUniquePtr<FMeshSculptBrushOpFactory>> BrushOpFactories;
UPROPERTY()
TMap<int32, TObjectPtr<UMeshSculptBrushOpProps>> SecondaryBrushOpPropSets;
TMap<int32, TUniquePtr<FMeshSculptBrushOpFactory>> SecondaryBrushOpFactories;
UE_API void RegisterBrushType(int32 Identifier, FText Name, TUniquePtr<FMeshSculptBrushOpFactory> Factory, UMeshSculptBrushOpProps* PropSet);
UE_API void RegisterSecondaryBrushType(int32 Identifier, FText Name, TUniquePtr<FMeshSculptBrushOpFactory> Factory, UMeshSculptBrushOpProps* PropSet);
UE_API virtual void SaveAllBrushTypeProperties(UInteractiveTool* SaveFromTool);
UE_API virtual void RestoreAllBrushTypeProperties(UInteractiveTool* RestoreToTool);
protected:
TUniquePtr<FMeshSculptBrushOp> PrimaryBrushOp;
UMeshSculptBrushOpProps* PrimaryVisiblePropSet = nullptr; // BrushOpPropSets prevents GC of this
TUniquePtr<FMeshSculptBrushOp> SecondaryBrushOp;
UMeshSculptBrushOpProps* SecondaryVisiblePropSet = nullptr;
bool bBrushOpPropsVisible = true;
UE_API void SetActivePrimaryBrushType(int32 Identifier);
UE_API void SetActiveSecondaryBrushType(int32 Identifier);
UE_API virtual TUniquePtr<FMeshSculptBrushOp>& GetActiveBrushOp();
UE_API void SetBrushOpPropsVisibility(bool bVisible);
//
// Falloff types
//
public:
struct FFalloffTypeInfo
{
FText Name;
FString StringIdentifier;
int32 Identifier;
};
const TArray<FFalloffTypeInfo>& GetRegisteredPrimaryFalloffTypes() const { return RegisteredPrimaryFalloffTypes; }
/** Set the active falloff type for the primary brush */
UE_API virtual void SetPrimaryFalloffType(EMeshSculptFalloffType Falloff);
protected:
TSharedPtr<FMeshSculptFallofFunc> PrimaryFalloff;
TArray<FFalloffTypeInfo> RegisteredPrimaryFalloffTypes;
UE_API void RegisterStandardFalloffTypes();
//
// Brush Size
//
protected:
UE::Geometry::FInterval1d BrushRelativeSizeRange;
double CurrentBrushRadius = 1.0;
UE_API virtual void InitializeBrushSizeRange(const UE::Geometry::FAxisAlignedBox3d& TargetBounds);
UE_API virtual void CalculateBrushRadius();
UE_API virtual double GetActiveBrushRadius();
virtual double GetCurrentBrushRadius() const { return CurrentBrushRadius; }
double CurrentBrushFalloff = 0.5;
virtual double GetCurrentBrushFalloff() const { return CurrentBrushFalloff; }
double ActivePressure = 1.0;
virtual double GetActivePressure() const { return ActivePressure; }
// returns brush strength, factoring in pressure sensitivity if applicable
UE_API virtual double GetActiveBrushStrength();
UE_API virtual double GetCurrentBrushStrength();
UE_API virtual double GetCurrentBrushDepth();
public:
UE_API virtual void IncreaseBrushRadiusAction();
UE_API virtual void DecreaseBrushRadiusAction();
UE_API virtual void IncreaseBrushRadiusSmallStepAction();
UE_API virtual void DecreaseBrushRadiusSmallStepAction();
// client currently needs to implement these...
virtual void IncreaseBrushSpeedAction() {}
virtual void DecreaseBrushSpeedAction() {}
virtual void NextBrushModeAction() {}
virtual void PreviousBrushModeAction() {}
public:
// IInteractiveToolCameraFocusAPI override to focus on brush w/ 'F'
UE_API virtual FBox GetWorldSpaceFocusBox() override;
//
// Brush/Stroke stuff
//
protected:
FFrame3d LastBrushFrameWorld;
FFrame3d LastBrushFrameLocal;
int32 LastBrushTriangleID = IndexConstants::InvalidID;
const FFrame3d& GetBrushFrameWorld() const { return LastBrushFrameWorld; }
const FFrame3d& GetBrushFrameLocal() const { return LastBrushFrameLocal; }
int32 GetBrushTriangleID() const { return LastBrushTriangleID; }
UE_API void UpdateBrushFrameWorld(const FVector3d& NewPosition, const FVector3d& NewNormal);
UE_API void AlignBrushToView();
bool GetBrushCanHitBackFaces() const { return BrushProperties->bHitBackFaces; }
// These routines handle the actual ray intersection queries againt the sculpt and target meshes. There
// are two versions for each - a const version and a non-const version. The versions have different names
// to avoid running afoul of deprecation rules. Subclasses may leave the non-const versions unimplemented,
// as the default implementation simply calls the const version. Subclasses should implement the const versions,
// to provide correct ray intersection logic for thier specific sculpt/target situation.
/** @return hit triangle at ray position - subclass *may* implement this */
virtual int32 FindHitSculptMeshTriangle(const FRay3d& LocalRay) { return FindHitSculptMeshTriangleConst(LocalRay); }
/** @return hit triangle at ray position - subclass *must* implement this */
virtual int32 FindHitSculptMeshTriangleConst(const FRay3d& LocalRay) const { check(false); return IndexConstants::InvalidID; }
/** @return hit triangle at ray position - subclass *may* implement this */
virtual int32 FindHitTargetMeshTriangle(const FRay3d& LocalRay) { return FindHitTargetMeshTriangleConst(LocalRay); }
/** @return hit triangle at ray position - subclass *should* implement this for most brushes */
virtual int32 FindHitTargetMeshTriangleConst(const FRay3d& LocalRay) const { check(false); return IndexConstants::InvalidID; }
bool ProjectWorldRayOnActivePlane(const FRay& WorldRay, FVector3d& ProjectedPosition, FVector3d& ProjectedNormal) const;
bool ProjectWorldRayOnTargetMesh(const FRay& WorldRay, bool bFallbackToViewPlane, FVector3d& ProjectedPosition, FVector3d& ProjectedNormal, int32& HitTriangle) const;
bool ProjectWorldRayOnSculptMesh(const FRay& WorldRay, bool bFallbackToViewPlane, FVector3d& ProjectedPosition, FVector3d& ProjectedNormal, int32& HitTriangle) const;
UE_API virtual bool UpdateBrushPositionOnActivePlane(const FRay& WorldRay);
UE_API virtual bool UpdateBrushPositionOnTargetMesh(const FRay& WorldRay, bool bFallbackToViewPlane);
UE_API virtual bool UpdateBrushPositionOnSculptMesh(const FRay& WorldRay, bool bFallbackToViewPlane);
/**
* Provides a hook for informing subclasses which triangle was last hit during a brush update.
* Subclass should implement this if it needs to know which triangle was hit
* @param TriangleID the triangle last hit during brush updates on the Sculpt Mesh
* @param LocalRay the ray in local space which caused the triangle to be hit
*/
virtual void UpdateHitSculptMeshTriangle(int32 TriangleID, const FRay3d& LocalRay) {};
/**
* Provides a hook for informing subclasses which triangle was last hit during a brush update.
* Subclass should implement this if it needs to know which triangle was hit
* @param TriangleID the triangle last hit during brush updates on the Target Mesh
* @param LocalRay the ray in local space which caused the triangle to be hit
*/
virtual void UpdateHitTargetMeshTriangle(int32 TriangleID, const FRay3d& LocalRay) {};
//
// Brush Target Plane is plane that some brushes move on
//
protected:
FFrame3d ActiveBrushTargetPlaneWorld;
UE_API virtual void UpdateBrushTargetPlaneFromHit(const FRay& WorldRay, const FHitResult& Hit);
//
// Stroke Modifiers
//
protected:
bool bInStroke = false;
bool bSmoothing = false;
bool bInvert = false;
UE_API virtual void SaveActiveStrokeModifiers();
virtual bool InStroke() const { return bInStroke; }
virtual bool GetInSmoothingStroke() const { return bSmoothing; }
virtual bool GetInInvertStroke() const { return bInvert; }
struct FTimedStrokePoint
{
double Timestamp;
FVector3d Position;
double Pressure;
};
TArray<FTimedStrokePoint> StrokePath;
UPROPERTY()
TObjectPtr<UPreviewGeometry> StrokeGeometry;
UE_API virtual void ClearStrokePath();
UE_API virtual void AppendStrokePath(const FVector3d& NextPoint);
// when in a stroke, this function determines when a new stamp should be emitted, based on spacing and flow rate settings
UE_API virtual void UpdateStampPendingState();
// for tracking stroke time and length, to apply spacing and flow rate settings
double ActiveStrokeTime = 0.0;
double ActiveStrokePathArcLen = 0.0;
int LastFlowTimeStamp = 0;
int LastSpacingTimestamp = 0;
int StampsDuringStroke = 0;
UE_API virtual void ResetStrokeTime();
UE_API virtual void AccumulateStrokeTime(float DeltaTime);
//
// Stamps
//
protected:
bool bIsStampPending = false;
FRay PendingStampRay;
struct FStampRayData
{
FRay StampRay;
double Pressure;
};
TArray<FStampRayData> PendingStampRays;
FSculptBrushStamp HoverStamp;
FSculptBrushStamp CurrentStamp;
FSculptBrushStamp LastStamp;
UE_API virtual void UpdateHoverStamp(const FFrame3d& StampFrameWorld);
virtual bool IsStampPending() const { return bIsStampPending; }
virtual const FRay& GetPendingStampRayWorld() const { return PendingStampRay; }
// Temporal Flow Rate defines the frequency of stamp placement. 1 is max rate, 0 is no stamps. Defaults to BrushProperties->FlowRate, but subclasses can re-use that setting for other things
UE_API virtual float GetStampTemporalFlowRate() const;
UE_API void ProcessPerTickStamps(TFunction<bool(const FRay& StampRay)> UpdateStampPosition, TFunction<void(int StampIndex, const FRay& StampRay)> ExecuteStampOperation);
UE_API void ProcessPerTickStamps(TFunction<bool(const FRay& StampRay)> UpdateStampPosition, TFunction<void(int StampCount )> PreExecuteStampsOperation, TFunction<void(int StampIndex, const FRay& StampRay)> ExecuteStampOperation, TFunction<void()> PostExecuteStampsOperation );
//
// Stamp ROI Plane is a plane used by some brush ops
//
protected:
FFrame3d StampRegionPlane;
UE_API virtual FFrame3d ComputeStampRegionPlane(const FFrame3d& StampFrame, const TArray<int32>& StampTriangles, bool bIgnoreDepth, bool bViewAligned, bool bInvDistFalloff = true);
UE_API virtual FFrame3d ComputeStampRegionPlane(const FFrame3d& StampFrame, const TSet<int32>& StampTriangles, bool bIgnoreDepth, bool bViewAligned, bool bInvDistFalloff = true);
// Stroke plane is a plane used by some brush ops
//
protected:
FFrame3d StrokePlane;
virtual const FFrame3d& GetCurrentStrokeReferencePlane() const { return StrokePlane; }
UE_API virtual void UpdateStrokeReferencePlaneForROI(const FFrame3d& StampFrame, const TArray<int32>& TriangleROI, bool bViewAligned);
UE_API virtual void UpdateStrokeReferencePlaneFromWorkPlane();
//
// Display / Material
//
public:
UPROPERTY()
TObjectPtr<UMeshEditingViewProperties> ViewProperties;
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> ActiveOverrideMaterial;
protected:
UE_API virtual void SetViewPropertiesEnabled(bool bNewValue);
UE_API virtual void UpdateWireframeVisibility(bool bNewValue);
UE_API virtual void UpdateMaterialMode(EMeshEditingMaterialModes NewMode);
UE_API virtual void UpdateFlatShadingSetting(bool bNewValue);
UE_API virtual void UpdateColorSetting(FLinearColor NewColor);
UE_API virtual void UpdateTransparentColorSetting(FLinearColor NewColor);
UE_API virtual void UpdateOpacitySetting(double Opacity);
UE_API virtual void UpdateTwoSidedSetting(bool bOn);
UE_API virtual void UpdateCustomMaterial(TWeakObjectPtr<UMaterialInterface> NewMaterial);
UE_API virtual void UpdateImageSetting(UTexture2D* NewImage);
//
// brush indicator
//
protected:
// subclasses should call this to create indicator in their ::Setup()
UE_API virtual void InitializeIndicator();
// Called by InitializeIndicator to create a mesh for the brush ROI indicator. Default is sphere.
UE_API virtual UPreviewMesh* MakeBrushIndicatorMesh(UObject* Parent, UWorld* World);
UE_API virtual void ConfigureIndicator(bool bVolumetric);
UE_API virtual bool GetIsVolumetricIndicator();
UE_API virtual void SetIndicatorVisibility(bool bVisible);
UE_API virtual bool GetIndicatorVisibility() const;
protected:
UPROPERTY()
TObjectPtr<UBrushStampIndicator> BrushIndicator;
UPROPERTY()
bool bIsVolumetricIndicator;
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> BrushIndicatorMaterial;
UPROPERTY()
TObjectPtr<UPreviewMesh> BrushIndicatorMesh;
TWeakObjectPtr<ULocalTwoAxisPropertyEditInputBehavior> BrushEditBehavior;
//
// Work Plane
//
public:
// plane gizmo
UPROPERTY()
TObjectPtr<UCombinedTransformGizmo> PlaneTransformGizmo;
UPROPERTY()
TObjectPtr<UTransformProxy> PlaneTransformProxy;
protected:
UE_API virtual void UpdateWorkPlane();
virtual bool ShowWorkPlane() const { return false; };
// Only relevant when ShowWorkPlane() returns true. Determines whether the plane
// grid lines are drawn.
bool bDrawWorkPlaneGridLines = true;
protected:
TValueWatcher<FVector> GizmoPositionWatcher;
TValueWatcher<FQuat> GizmoRotationWatcher;
UE_API virtual void UpdateGizmoFromProperties();
UE_API virtual void PlaneTransformChanged(UTransformProxy* Proxy, FTransform Transform);
enum class EPendingWorkPlaneUpdate
{
NoUpdatePending,
MoveToHitPositionNormal,
MoveToHitPosition,
MoveToHitPositionViewAligned
};
EPendingWorkPlaneUpdate PendingWorkPlaneUpdate;
UE_API virtual void SetFixedSculptPlaneFromWorldPos(const FVector& Position, const FVector& Normal, EPendingWorkPlaneUpdate UpdateType);
UE_API virtual void UpdateFixedSculptPlanePosition(const FVector& Position);
UE_API virtual void UpdateFixedSculptPlaneRotation(const FQuat& Rotation);
UE_API virtual void UpdateFixedPlaneGizmoVisibility(bool bVisible);
protected:
UE::TransactionUtil::FLongTransactionTracker LongTransactions;
};
#undef UE_API