Files
UnrealEngine/Engine/Plugins/Runtime/MeshModelingToolset/Source/ModelingComponents/Public/BaseTools/BaseMeshProcessingTool.h
2025-05-18 13:04:45 +08:00

311 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "SingleTargetWithSelectionTool.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "WeightMapTypes.h"
#include "MeshOpPreviewHelpers.h"
#include "PropertySets/WeightMapSetProperties.h"
#include "BaseMeshProcessingTool.generated.h"
#define UE_API MODELINGCOMPONENTS_API
// predeclarations
struct FMeshDescription;
class UDynamicMeshComponent;
class UPreviewMesh;
class UBaseMeshProcessingTool;
PREDECLARE_USE_GEOMETRY_CLASS(FMeshBoundaryLoops);
PREDECLARE_USE_GEOMETRY_CLASS(FMeshNormals);
using UE::Geometry::FDynamicMesh3;
using UE::Geometry::FIndexedWeightMap1f;
/**
* ToolBuilder for UBaseMeshProcessingTool
*/
UCLASS(MinimalAPI)
class UBaseMeshProcessingToolBuilder : public USingleTargetWithSelectionToolBuilder
{
GENERATED_BODY()
public:
UE_API virtual bool CanBuildTool(const FToolBuilderState& SceneState) const override;
virtual bool RequiresInputSelection() const override { return false; }
UE_API virtual USingleTargetWithSelectionTool* CreateNewTool(const FToolBuilderState& SceneState) const override;
public:
// subclass must override!
UE_DEPRECATED(5.5, "MakeNewToolInstance is deprecated, please use CreateNewTool instead")
virtual UBaseMeshProcessingTool* MakeNewToolInstance(UObject* Outer) const { check(false); return nullptr; }
protected:
UE_API virtual const FToolTargetTypeRequirements& GetTargetRequirements() const override;
};
/**
* UBaseMeshProcessingTool is a base Tool (ie has no functionality of it's own and must be subclassed)
* that provides the following structure:
* - a Background-Compute-With-Preview Temp Actor/Component is created based on the input mesh
* - The Subclass provides FDynamicMeshOperator instances (via IDynamicMeshOperatorFactory) that process/modify and update this Preview
* - PropertySets with custom visibility can be registered, and on change will invalidate the current computation
*
* Optional support for a WeightMap property set and tracking of active weight map can be enabled by calling
* AddWeightMapPropertySet(), GetActiveWeightMap() will then return the active WeightMap, and changes to the
* WeightMap selection will invalidate the computation.
*
* Most subclasses will only need to define their PropertySets and implement MakeNewOperator(), see eg SmoothMeshTool for a minimal example
*
* Other functions:
* - GetInitialMesh() : return reference to copy of initial mesh, used to initialize FDynamicMeshOperator
* - GetUPreviewMesh() : return the UPreviewMesh inside the background compute (for configuration/etc - should not directly touch the mesh!)
* - GetPreviewTransform() : return active FTransform on the Preview mesh, should be passed to FDynamicMeshOperator unless it is outputting world position
* - InvalidateResult() : subclasses call this to notify the base class that current result/computation has been invalidated
*
* The Base tool will do various optional precomputations or changes to the input mesh, which can be configured by
* overriding various functions below.
*
* RequiresInitialVtxNormals() : return true (default=false) to calculate per-vertex normals on the input mesh, returned by GetInitialVtxNormals()
*
* RequiresInitialBoundaryLoops() : return true (default=false) to calculate boundary loops on the input mesh, returned by GetInitialBoundaryLoops()
*
* RequiresScaleNormalization() : return true (default=true) to apply an initial scale to the input mesh so that it has consistent size
* before being sent into the computation. Scaling factor (eg for scaling UI constants) can be accessed via GetScaleNormalizationFactor()
*
*/
UCLASS(MinimalAPI)
class UBaseMeshProcessingTool : public USingleTargetWithSelectionTool, public UE::Geometry::IDynamicMeshOperatorFactory
{
GENERATED_BODY()
protected:
using FFrame3d = UE::Geometry::FFrame3d;
public:
UBaseMeshProcessingTool() = default;
//
// InteractiveTool API - generally does not need to be modified by subclasses
//
UE_API virtual void Setup() override;
UE_API virtual void Shutdown(EToolShutdownType ShutdownType) override;
UE_API virtual void OnTick(float DeltaTime) override;
UE_API virtual void Render(IToolsContextRenderAPI* RenderAPI) override;
virtual bool HasCancel() const override { return true; }
UE_API virtual bool HasAccept() const override;
UE_API virtual bool CanAccept() const override;
protected:
//
// UBaseMeshProcessingTool REQUIRED API - subclasses must implement these functions
//
/**
* IDynamicMeshOperatorFactory implementation that subclass must override and implement
*/
virtual TUniquePtr<UE::Geometry::FDynamicMeshOperator> MakeNewOperator() override
{
check(false);
return TUniquePtr<UE::Geometry::FDynamicMeshOperator>();
}
/**
* called when Tool is Accepted to determine whether it is safe to only update vertex positions, or if entire target mesh must be replaced.
*/
virtual bool HasMeshTopologyChanged() const
{
unimplemented();
return true;
}
//
// UBaseMeshProcessingTool OPTIONAL API - subclasses may implement these functions
//
/** @return text string shown to user for Accept Transaction that updates input mesh. Subclass should override. */
UE_API virtual FText GetToolMessageString() const;
/** @return text string shown to user for Accept Transaction that updates input mesh. Subclass should override. */
UE_API virtual FText GetAcceptTransactionName() const;
/** This function is called during ::Setup() to allow subclass to register property sets, before kicking off initial computation */
virtual void InitializeProperties() {}
/** This function is called during ::Shutdown so that subclass may perform final processing and save property sets */
virtual void OnShutdown(EToolShutdownType ShutdownType) {}
//
// Optional Property Set API - subclasses can use this to manage property sets with configurable visibility that invalidate the precompute
//
/**
* Register an optional property set with the given VisibilityFunc
*/
template<class PropSetType>
PropSetType* AddOptionalPropertySet(TUniqueFunction<bool()> VisibilityFunc, bool bChangeInvalidatesResult = true)
{
return AddOptionalPropertySet<PropSetType>(MoveTemp(VisibilityFunc), []() {}, bChangeInvalidatesResult);
}
/**
* Register an optional property set with the given VisibilityFunc, and call OnModifiedFunc if any of the properties change
*/
template<class PropSetType>
PropSetType* AddOptionalPropertySet( TUniqueFunction<bool()> VisibilityFunc, TUniqueFunction<void()> OnModifiedFunc, bool bChangeInvalidatesResult)
{
PropSetType* PropSet = NewObject<PropSetType>(this);
AddOptionalPropertySet(PropSet, MoveTemp(VisibilityFunc), MoveTemp(OnModifiedFunc), bChangeInvalidatesResult);
return PropSet;
}
/** Call this function to update optional property sets visibility. Should call base implementation */
UE_API virtual void UpdateOptionalPropertyVisibility();
UE_API virtual void AddOptionalPropertySet(UInteractiveToolPropertySet* PropSet,
TUniqueFunction<bool()> VisibilityFunc,
TUniqueFunction<void()> OnModifiedFunc,
bool bChangeInvalidatesResult);
struct FOptionalPropertySet
{
TUniqueFunction<bool()> IsVisible;
TUniqueFunction<void()> OnModifiedFunc;
bool bInvalidateOnModify;
TWeakObjectPtr<UInteractiveToolPropertySet> PropertySet;
};
TArray<FOptionalPropertySet> OptionalProperties;
UE_API virtual void OnOptionalPropSetModified(int32 Index);
UE_API virtual void SavePropertySets();
// Preview object holds temporary Actor with preview mesh component
UPROPERTY()
TObjectPtr<UMeshOpPreviewWithBackgroundCompute> Preview = nullptr;
UPreviewMesh* GetUPreviewMesh() const { return Preview->PreviewMesh; }
const FTransform& GetPreviewTransform() const { return OverrideTransform; }
private:
bool bResultValid = false;
protected:
UE_API virtual void InvalidateResult();
UE_API virtual void UpdateResult();
//
// Initial data, used to initialize background compute / etc
//
private:
FDynamicMesh3 InitialMesh;
protected:
/** @return duplciate of initial mesh (possibly with optional size normalization) */
const FDynamicMesh3& GetInitialMesh() const { return InitialMesh; }
/** @return duplciate of initial mesh (possibly with optional size normalization) */
FDynamicMesh3& GetInitialMesh() { return InitialMesh; }
//
// Optional base mesh per-vertex normals. Default enabled.
// These are computed at Tool startup if required, and then not modified, so can be passed to multithreaded operators/etc
//
protected:
/**
* If this function returns true, BaseNormals will be initialized in Tool ::Setup().
* This has some cost and should be disabled if not necessary.
*/
virtual bool RequiresInitialVtxNormals() const { return false; }
/** @return calculated base normals. This pointer does not change for the lifetime of the Tool. */
UE_API TSharedPtr<FMeshNormals>& GetInitialVtxNormals();
private:
TSharedPtr<FMeshNormals> InitialVtxNormals;
//
// Optional base mesh boundary loops. Default enabled.
// These are computed at Tool startup if required, and then not modified, so can be passed to multithreaded operators/etc
//
protected:
/**
* If this function returns true, InitialBoundaryLoops will be initialized in Tool ::Setup().
* This has some cost and should be disabled if not necessary.
*/
virtual bool RequiresInitialBoundaryLoops() const { return false; }
/** @return calculated base normals. This pointer does not change for the lifetime of the Tool. */
UE_API TSharedPtr<FMeshBoundaryLoops>& GetInitialBoundaryLoops();
private:
TSharedPtr<FMeshBoundaryLoops> InitialBoundaryLoops;
//
// Optional weight map support
// Weight map will only be enabled if base class registers a weight map property set
//
protected:
template<class PropSetType>
PropSetType* AddWeightMapPropertySet(TUniqueFunction<bool()> VisibilityFunc = []() {return true; } )
{
PropSetType* PropSet = NewObject<PropSetType>(this);
SetupWeightMapPropertySet(PropSet);
WeightMapPropertySetVisibleFunc = MoveTemp(VisibilityFunc);
return PropSet;
}
UE_API virtual void SetupWeightMapPropertySet(UWeightMapSetProperties* Properties);
UE_API bool HasActiveWeightMap() const;
UE_API TSharedPtr<FIndexedWeightMap1f>& GetActiveWeightMap();
private:
TWeakObjectPtr<UWeightMapSetProperties> WeightMapPropertySet;
TUniqueFunction<bool()> WeightMapPropertySetVisibleFunc;
TSharedPtr<FIndexedWeightMap1f> ActiveWeightMap;
UE_API void OnSelectedWeightMapChanged(bool bInvalidate);
//
// Optional uniform scale applied to mesh. Enabled by default
//
protected:
/**
* If this function returns true, input mesh will be scaled to normalized dimension in ::Setup() before any processing begins.
* This scaling will be undone on Accept()
*/
virtual bool RequiresScaleNormalization() const { return true; }
double GetScaleNormalizationFactor() const { return 1.0 / SrcScale; };
private:
// scale/translate applied to input mesh to regularize it
bool bIsScaleNormalizationApplied = false;
FVector3d SrcTranslate;
double SrcScale;
// transform that does the opposite of scale/translate so that mesh stays in the right spot on screen
FTransform OverrideTransform;
};
#undef UE_API