Files
UnrealEngineUWP/Engine/Plugins/Experimental/MeshModelingToolsetExp/Source/MeshModelingToolsExp/Private/MeshSpaceDeformerTool.cpp

531 lines
19 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshSpaceDeformerTool.h"
#include "InteractiveToolManager.h"
#include "InteractiveGizmoManager.h"
#include "SceneQueries/SceneSnappingManager.h"
#include "ToolBuilderUtil.h"
#include "SegmentTypes.h"
#include "DynamicMesh/DynamicMeshAttributeSet.h"
#include "Mechanics/DragAlignmentMechanic.h"
#include "DynamicMesh/MeshNormals.h"
#include "MeshOpPreviewHelpers.h"
#include "ToolSceneQueriesUtil.h"
#include "ToolSetupUtil.h"
#include "Intersection/IntersectionUtil.h"
#include "PreviewMesh.h"
#include "BaseBehaviors/SingleClickBehavior.h"
#include "Selection/SelectClickedAction.h"
#include "BaseGizmos/GizmoComponents.h"
Gizmos: refactor Modeling Mode gizmo creation out of InteractiveGizmoManager. Editor will use other "default" transform gizmo implementations, and so the UTransformGizmo creation helper functions do not belong in GizmoManager. Instead a UTransformGizmoContextObject now provides this functionality. ModelingToolsEditorMode (and any other modes/systems that want to use these gizmo convenience functions) creates an instance of UTransformGizmoContextObject and registers it with the ContextObjectStore. Calling code can spawn a new UTransformGizmo by looking this object up in the ContextStore and calling it's helper functions. Static versions of the helper functions in the UE::TransformGizmoUtil:: namespace provide a single-line interface that replaces the previous GizmoManager call sites in the MeshModelingTools library. IntervalGizmo is now just registered and unregistered as needed by the MeshSpaceDeformerTool, as this is the only place it is currently used. Previous implementation in InteractiveGizmoManager is left intact as there are a few uses outside of MeshModelingTools that need to be cleaned up before it can be deleted. UTransformGizmo now requires it's Builder to tell it which sub-gizmo identifier strings to pass to the GizmoManager to create axis/plane/rotation sub-gizmos (and the code that registers the Builder must then provide these strings). This cleans up previous explicit references to UInteractiveGizmoManager static strings from UTransformGizmo. #rb Christina.TempelaarL, david.hill #rnx #jira none [CL 16409673 by Ryan Schmidt in ue5-main branch]
2021-05-20 16:39:39 -04:00
#include "BaseGizmos/TransformGizmoUtil.h"
#include "BaseGizmos/IntervalGizmo.h"
#include "MeshDescriptionToDynamicMesh.h"
#include "DynamicMeshToMeshDescription.h"
#include "CoreMinimal.h"
#include "Math/Matrix.h"
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
ModelingTools: Reduce surface area of MeshDescriptionProvider/Committer, replace with UE::ToolTarget:: calls where possible. Add new UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh() function. This is being used for now to avoid potential regressions as UE::ToolTarget::CommitDynamicMeshUpdate will preferentially use DynamicMeshCommitter, and I am not certain it is functionally equivalent in all cases. Add new UE::ToolTarget::CommitDynamicMeshNormalsUpdate(), similar to existing UV version Add new Move-variant of UE::ToolTarget::CommitMeshDescriptionUpdate(), uses new Move-variant of IMeshDescriptionCommitter::CommitMeshDescription. Make existing IMeshDescriptionCommitter::CommitMeshDescription callback interface protected, to prevent usage of this function at public API level (will be removed in future). Tool updates should not change, just using cleaner APIs. EditNormalsTool now uses CommitDynamicMeshNormalsUpdate(), which does go via DynamicMeshCommitter preferentially, where it previously went via MeshDescriptionCommitter. In light testing the results appear equivalent. AttributeEditorTool now operates on MeshDescription copies in various update functions. These are not performance-critical. #rb rinat.abdrashitov #rnx #preflight 61ae45998358693a22c28d1b #ROBOMERGE-AUTHOR: ryan.schmidt #ROBOMERGE-SOURCE: CL 18384350 in //UE5/Release-5.0/... via CL 18384361 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v896-18170469) [CL 18384373 by ryan schmidt in ue5-release-engine-test branch]
2021-12-06 12:42:19 -05:00
#include "ModelingToolTargetUtil.h"
using namespace UE::Geometry;
#define LOCTEXT_NAMESPACE "MeshSpaceDeformerTool"
/*
* ToolBuilder
*/
USingleSelectionMeshEditingTool* UMeshSpaceDeformerToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const
{
return NewObject<UMeshSpaceDeformerTool>(SceneState.ToolManager);
}
TUniquePtr<FDynamicMeshOperator> USpaceDeformerOperatorFactory::MakeNewOperator()
{
check(SpaceDeformerTool);
const ENonlinearOperationType OperationType = SpaceDeformerTool->Settings->SelectedOperationType;
// Create the actual operator type based on the requested operation
TUniquePtr<FMeshSpaceDeformerOp> DeformerOp;
switch (OperationType)
{
case ENonlinearOperationType::Bend:
{
DeformerOp = MakeUnique<FBendMeshOp>();
static_cast<FBendMeshOp*>(DeformerOp.Get())->BendDegrees = SpaceDeformerTool->Settings->BendDegrees;
static_cast<FBendMeshOp*>(DeformerOp.Get())->bLockBottom = SpaceDeformerTool->Settings->bLockBottom;
break;
}
case ENonlinearOperationType::Flare:
{
DeformerOp = MakeUnique<FFlareMeshOp>();
static_cast<FFlareMeshOp*>(DeformerOp.Get())->FlarePercentY = SpaceDeformerTool->Settings->FlarePercentY;
static_cast<FFlareMeshOp*>(DeformerOp.Get())->FlarePercentX = SpaceDeformerTool->Settings->bLockXAndYFlaring ?
SpaceDeformerTool->Settings->FlarePercentY : SpaceDeformerTool->Settings->FlarePercentX;
if (SpaceDeformerTool->Settings->FlareProfileType == EFlareProfileType::SinMode)
{
static_cast<FFlareMeshOp*>(DeformerOp.Get())->FlareType = FFlareMeshOp::EFlareType::SinFlare;
}
else if (SpaceDeformerTool->Settings->FlareProfileType == EFlareProfileType::SinSquaredMode)
{
static_cast<FFlareMeshOp*>(DeformerOp.Get())->FlareType = FFlareMeshOp::EFlareType::SinSqrFlare;
}
else
{
static_cast<FFlareMeshOp*>(DeformerOp.Get())->FlareType = FFlareMeshOp::EFlareType::LinearFlare;
}
break;
}
case ENonlinearOperationType::Twist:
{
DeformerOp = MakeUnique<FTwistMeshOp>();
static_cast<FTwistMeshOp*>(DeformerOp.Get())->TwistDegrees = SpaceDeformerTool->Settings->TwistDegrees;
static_cast<FTwistMeshOp*>(DeformerOp.Get())->bLockBottom = SpaceDeformerTool->Settings->bLockBottom;
break;
}
default:
check(0);
}
// Operator runs on another thread - copy data over that it needs.
SpaceDeformerTool->UpdateOpParameters(*DeformerOp);
// give the operator
return DeformerOp;
}
void UMeshSpaceDeformerToolActionPropertySet::PostAction(EMeshSpaceDeformerToolAction Action)
{
if (ParentTool.IsValid())
{
ParentTool->RequestAction(Action);
}
}
/*
* Tool
*/
UMeshSpaceDeformerTool::UMeshSpaceDeformerTool()
{
}
bool UMeshSpaceDeformerTool::CanAccept() const
{
return Super::CanAccept() && (Preview == nullptr || Preview->HaveValidResult());
}
void UMeshSpaceDeformerTool::Setup()
{
UInteractiveTool::Setup();
Settings = NewObject<UMeshSpaceDeformerToolProperties>(this);
Settings->RestoreProperties(this);
AddToolPropertySource(Settings);
ToolActions = NewObject<UMeshSpaceDeformerToolActionPropertySet>(this);
ToolActions->Initialize(this);
AddToolPropertySource(ToolActions);
// populate the OriginalDynamicMesh with a conversion of the input mesh.
{
OriginalDynamicMesh = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>();
FMeshDescriptionToDynamicMesh Converter;
ModelingTools: Reduce surface area of MeshDescriptionProvider/Committer, replace with UE::ToolTarget:: calls where possible. Add new UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh() function. This is being used for now to avoid potential regressions as UE::ToolTarget::CommitDynamicMeshUpdate will preferentially use DynamicMeshCommitter, and I am not certain it is functionally equivalent in all cases. Add new UE::ToolTarget::CommitDynamicMeshNormalsUpdate(), similar to existing UV version Add new Move-variant of UE::ToolTarget::CommitMeshDescriptionUpdate(), uses new Move-variant of IMeshDescriptionCommitter::CommitMeshDescription. Make existing IMeshDescriptionCommitter::CommitMeshDescription callback interface protected, to prevent usage of this function at public API level (will be removed in future). Tool updates should not change, just using cleaner APIs. EditNormalsTool now uses CommitDynamicMeshNormalsUpdate(), which does go via DynamicMeshCommitter preferentially, where it previously went via MeshDescriptionCommitter. In light testing the results appear equivalent. AttributeEditorTool now operates on MeshDescription copies in various update functions. These are not performance-critical. #rb rinat.abdrashitov #rnx #preflight 61ae45998358693a22c28d1b #ROBOMERGE-AUTHOR: ryan.schmidt #ROBOMERGE-SOURCE: CL 18384350 in //UE5/Release-5.0/... via CL 18384361 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v896-18170469) [CL 18384373 by ryan schmidt in ue5-release-engine-test branch]
2021-12-06 12:42:19 -05:00
Converter.Convert(UE::ToolTarget::GetMeshDescription(Target), *OriginalDynamicMesh);
}
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
FTransform MeshTransform = TargetComponent->GetWorldTransform();
// Hide the mesh, and potentially put a semi-transparent copy in its place. We could
// update the materials and restore them later, but this seems safer.
TargetComponent->SetOwnerVisibility(false);
OriginalMeshPreview = NewObject<UPreviewMesh>();
OriginalMeshPreview->CreateInWorld(GetTargetWorld(), MeshTransform);
ToolSetupUtil::ApplyRenderingConfigurationToPreview(OriginalMeshPreview, Target);
OriginalMeshPreview->UpdatePreview(OriginalDynamicMesh.Get());
OriginalMeshPreview->SetMaterial(0, ToolSetupUtil::GetCustomDepthOffsetMaterial(GetToolManager(), FLinearColor::White,
-0.5, // depth offset, 0.5% inward
0.4)); // opacity
OriginalMeshPreview->SetVisible(Settings->bShowOriginalMesh);
// The gizmo gets initialized to the center point of the object-space bounding box,
// with the Z axis (along which the deformation acts) aligned to the longest of the
// bounding box dimensions, after scaling them with the transform.
FAxisAlignedBox3d BBox = OriginalDynamicMesh->GetBounds();
FVector3d Dimensions = BBox.IsEmpty() ? FVector3d::Zero() : BBox.Max - BBox.Min;
MeshCenter = BBox.IsEmpty() ? FVector3d::Zero() : (FVector3d)MeshTransform.TransformPosition((FVector)BBox.Center());
Dimensions = FVector3d(MeshTransform.GetScale3D().GetAbs()) * Dimensions;
double WorldMajorLength = 0;
// Prefer being aligned with the Z axis.
if (Dimensions.Z >= Dimensions.Y && Dimensions.Z >= Dimensions.X)
{
WorldMajorLength = Dimensions.Z;
GizmoFrame = FFrame3d(MeshCenter, FVector3d::UnitX(), FVector3d::UnitY(), FVector3d::UnitZ());
}
else if (Dimensions.Y >= Dimensions.X)
{
WorldMajorLength = Dimensions.Y;
GizmoFrame = FFrame3d(MeshCenter, FVector3d::UnitZ(), FVector3d::UnitX(), FVector3d::UnitY());
}
else
{
WorldMajorLength = Dimensions.X;
GizmoFrame = FFrame3d(MeshCenter, FVector3d::UnitY(), FVector3d::UnitZ(), FVector3d::UnitX());
}
GizmoFrame.Rotate((FQuaterniond)MeshTransform.GetRotation());
// The scaling of the modifier gizmo is somewhat arbitrary. We choose for it to be
// related to the major axis length.
ModifierGizmoLength = WorldMajorLength;
// add click to set plane behavior
SetPointInWorldConnector = MakePimpl<FSelectClickedAction>();
SetPointInWorldConnector->SnapManager = USceneSnappingManager::Find(GetToolManager());
SetPointInWorldConnector->InvisibleComponentsToHitTest.Add(TargetComponent->GetOwnerComponent());
SetPointInWorldConnector->OnClickedPositionFunc = [this](const FHitResult& Hit)
{
SetGizmoFrameFromWorldPos(Hit.ImpactPoint, Hit.ImpactNormal, Settings->bAlignToNormalOnCtrlClick);
};
USingleClickInputBehavior* ClickToSetPlaneBehavior = NewObject<USingleClickInputBehavior>();
ClickToSetPlaneBehavior->ModifierCheckFunc = FInputDeviceState::IsCtrlKeyDown;
ClickToSetPlaneBehavior->Initialize(SetPointInWorldConnector.Get());
AddInputBehavior(ClickToSetPlaneBehavior);
// Create a new TransformGizmo and associated TransformProxy. The TransformProxy will not be the
// parent of any Components in this case, we just use it's transform and change delegate.
TransformProxy = NewObject<UTransformProxy>(this);
TransformProxy->SetTransform(GizmoFrame.ToFTransform());
Gizmos: refactor Modeling Mode gizmo creation out of InteractiveGizmoManager. Editor will use other "default" transform gizmo implementations, and so the UTransformGizmo creation helper functions do not belong in GizmoManager. Instead a UTransformGizmoContextObject now provides this functionality. ModelingToolsEditorMode (and any other modes/systems that want to use these gizmo convenience functions) creates an instance of UTransformGizmoContextObject and registers it with the ContextObjectStore. Calling code can spawn a new UTransformGizmo by looking this object up in the ContextStore and calling it's helper functions. Static versions of the helper functions in the UE::TransformGizmoUtil:: namespace provide a single-line interface that replaces the previous GizmoManager call sites in the MeshModelingTools library. IntervalGizmo is now just registered and unregistered as needed by the MeshSpaceDeformerTool, as this is the only place it is currently used. Previous implementation in InteractiveGizmoManager is left intact as there are a few uses outside of MeshModelingTools that need to be cleaned up before it can be deleted. UTransformGizmo now requires it's Builder to tell it which sub-gizmo identifier strings to pass to the GizmoManager to create axis/plane/rotation sub-gizmos (and the code that registers the Builder must then provide these strings). This cleans up previous explicit references to UInteractiveGizmoManager static strings from UTransformGizmo. #rb Christina.TempelaarL, david.hill #rnx #jira none [CL 16409673 by Ryan Schmidt in ue5-main branch]
2021-05-20 16:39:39 -04:00
TransformGizmo = UE::TransformGizmoUtil::CreateCustomTransformGizmo(GetToolManager(),
ETransformGizmoSubElements::StandardTranslateRotate, this);
TransformGizmo->SetActiveTarget(TransformProxy, GetToolManager());
// listen for changes to the proxy and update the preview when that happens
TransformProxy->OnTransformChanged.AddUObject(this, &UMeshSpaceDeformerTool::TransformProxyChanged);
// create sources for the interval parameters
UpIntervalSource = NewObject< UGizmoLocalFloatParameterSource >(this);
DownIntervalSource = NewObject< UGizmoLocalFloatParameterSource >(this);
ForwardIntervalSource = NewObject< UGizmoLocalFloatParameterSource >(this);
// Initial Lengths for the interval handle
UpIntervalSource->Value = WorldMajorLength/2;
DownIntervalSource->Value = -WorldMajorLength / 2;
ForwardIntervalSource->Value = GetModifierGizmoValue();
// Sync the properties panel to the interval handles.
Settings->UpperBoundsInterval = UpIntervalSource->Value;
Settings->LowerBoundsInterval = DownIntervalSource->Value;
// Wire up callbacks to update result mesh and the properties panel when these parameters are changed (by gizmo manipulation in viewport). Note this is just a one-way
// coupling (Sources to Properties). The OnPropertyModified() method provides the Properties to Souces coupling
UpIntervalSource->OnParameterChanged.AddLambda([this](IGizmoFloatParameterSource* ParamSource, FGizmoFloatParameterChange Change)->void
{
Settings->UpperBoundsInterval = Change.CurrentValue;
UpdatePreview();
});
DownIntervalSource->OnParameterChanged.AddLambda([this](IGizmoFloatParameterSource* ParamSource, FGizmoFloatParameterChange Change)->void
{
Settings->LowerBoundsInterval = Change.CurrentValue;
UpdatePreview();
});
ForwardIntervalSource->OnParameterChanged.AddLambda([this](IGizmoFloatParameterSource* ParamSource, FGizmoFloatParameterChange Change)->void
{
ApplyModifierGizmoValue(Change.CurrentValue);
UpdatePreview();
});
// add the interval gizmo
Gizmos: refactor Modeling Mode gizmo creation out of InteractiveGizmoManager. Editor will use other "default" transform gizmo implementations, and so the UTransformGizmo creation helper functions do not belong in GizmoManager. Instead a UTransformGizmoContextObject now provides this functionality. ModelingToolsEditorMode (and any other modes/systems that want to use these gizmo convenience functions) creates an instance of UTransformGizmoContextObject and registers it with the ContextObjectStore. Calling code can spawn a new UTransformGizmo by looking this object up in the ContextStore and calling it's helper functions. Static versions of the helper functions in the UE::TransformGizmoUtil:: namespace provide a single-line interface that replaces the previous GizmoManager call sites in the MeshModelingTools library. IntervalGizmo is now just registered and unregistered as needed by the MeshSpaceDeformerTool, as this is the only place it is currently used. Previous implementation in InteractiveGizmoManager is left intact as there are a few uses outside of MeshModelingTools that need to be cleaned up before it can be deleted. UTransformGizmo now requires it's Builder to tell it which sub-gizmo identifier strings to pass to the GizmoManager to create axis/plane/rotation sub-gizmos (and the code that registers the Builder must then provide these strings). This cleans up previous explicit references to UInteractiveGizmoManager static strings from UTransformGizmo. #rb Christina.TempelaarL, david.hill #rnx #jira none [CL 16409673 by Ryan Schmidt in ue5-main branch]
2021-05-20 16:39:39 -04:00
GetToolManager()->GetPairedGizmoManager()->RegisterGizmoType(UIntervalGizmo::GizmoName, NewObject<UIntervalGizmoBuilder>());
IntervalGizmo = GetToolManager()->GetPairedGizmoManager()->CreateGizmo<UIntervalGizmo>(UIntervalGizmo::GizmoName, TEXT("MeshSpaceDefomerInterval"), this);
// wire in the transform and the interval sources.
IntervalGizmo->SetActiveTarget(TransformProxy, UpIntervalSource, DownIntervalSource, ForwardIntervalSource, GetToolManager());
// use the statetarget to track details changes
StateTarget = IntervalGizmo->StateTarget;
// Set up the bent line visualizer
VisualizationRenderer.bDepthTested = false;
VisualizationRenderer.LineColor = FLinearColor::Yellow;
VisualizationRenderer.LineThickness = 4.0;
VisualizationRenderer.SetTransform(GizmoFrame.ToFTransform());
// Set up the preview object
{
// create the operator factory
USpaceDeformerOperatorFactory* DeformerOperatorFactory = NewObject<USpaceDeformerOperatorFactory>(this);
DeformerOperatorFactory->SpaceDeformerTool = this; // set the back pointer
Preview = NewObject<UMeshOpPreviewWithBackgroundCompute>(DeformerOperatorFactory, "Preview");
Preview->Setup(GetTargetWorld(), DeformerOperatorFactory);
ToolSetupUtil::ApplyRenderingConfigurationToPreview(Preview->PreviewMesh, Target);
ModelingComponents: Clean up DynamicMeshComponent API. Update Component and Proxy handling of Tangents to use Attribute Overlay if available. Update affected Tools and also convert most of the affected Tools to use UE::ToolTarget helper functions. - Add UE::ToolTarget::CommitMaterialSetUpdate() and ::CommitDynamicMeshUpdate(). ::GetDynamicMeshCopy() can now return tangents if requested. - Add IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). Default implementation does nothing, UStaticMeshComponentToolTarget implementation initializes auto-generated MeshDescription attributes. Used in ::GetDynamicMeshCopy() to get tangents (but requires a MeshDescription copy). - Clean up handling of Tangents in Simple/OctreeDynamicMeshComponent. Add local MakeTangentsFunc() to generate the Tangents lambda, handle different cases and no-tangents fallbacks consistently. - UDynamicMesh: add optional info arguments to EditMesh() and ChangeInfo struct. Add support for deferring change events from Edit funcs. - Remove UBaseDynamicMeshComponent::InitializeMesh(), ::Bake() APIs, and add ::SetMesh(). Implement in Simple/Octree implementations, update all Tools that used those APIs. - Add USimpleDynamicMeshComponent::ProcessMesh(), EditMesh(). These are now the preferred ways to read/write mesh. - Update USimpleDynamicMeshComponent tangents handling. Externally-computed tangents are now taken directly from the FDynamicMesh3 attribute set. Autogenerated tangents are still computed and stored in an internal FMeshTangentsf, but this is no longer exposed for external updates. - Remove UPreviewMesh pass-through functions for Tangents access, InitializeMesh() and Bake(). Add ProcessMesh() - Update all affected Tools. In most cases these Tools have also been converted to use ModelingToolTargetUtil functions, instead of direct ToolTarget interface casting. #rb none #rnx #jira none #preflight 60c3e71d3e1b3c00015668af #ROBOMERGE-SOURCE: CL 16650666 in //UE5/Main/... #ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v833-16641396) [CL 16650707 by ryan schmidt in ue5-release-engine-test branch]
2021-06-11 22:42:32 -04:00
Preview->PreviewMesh->SetTangentsMode(EDynamicMeshComponentTangentsMode::AutoCalculated);
Preview->SetIsMeshTopologyConstant(true, EMeshRenderAttributeFlags::Positions | EMeshRenderAttributeFlags::VertexNormals);
// Give the preview something to display
Preview->PreviewMesh->UpdatePreview(OriginalDynamicMesh.Get());
Preview->PreviewMesh->SetTransform(MeshTransform);
FComponentMaterialSet MaterialSet;
MaterialSet = UE::ToolTarget::GetMaterialSet(Target);
Preview->ConfigureMaterials(MaterialSet.Materials, ToolSetupUtil::GetDefaultWorkingMaterial(GetToolManager()));
// show the preview mesh
Preview->SetVisibility(true);
// start the compute
UpdatePreview();
}
DragAlignmentMechanic = NewObject<UDragAlignmentMechanic>(this);
DragAlignmentMechanic->Setup(this);
// We want to align to the original mesh, even though it is hidden (our stand-in preview mesh that
// we use to display a transparent version does not get hit tested by our normal raycasts into
// the world).
TArray<const UPrimitiveComponent*> ComponentsToInclude{ TargetComponent->GetOwnerComponent() } ;
DragAlignmentMechanic->AddToGizmo(TransformGizmo, nullptr, &ComponentsToInclude);
DragAlignmentMechanic->AddToGizmo(IntervalGizmo, nullptr, &ComponentsToInclude);
SetToolDisplayName(LOCTEXT("ToolName", "Space Warp"));
GetToolManager()->DisplayMessage(
LOCTEXT("MeshSpaceDeformerToolDescription", "Deform the vertices of the selected Mesh using various spatial deformations. Use the in-viewport Gizmo to control the extents/strength of the deformation. Hold Ctrl while translating/rotating gizmo to align to world."),
EToolMessageLevel::UserNotification);
}
void UMeshSpaceDeformerTool::OnShutdown(EToolShutdownType ShutdownType)
{
Settings->SaveProperties(this);
// Restore source mesh and remove our stand-in
Cast<IPrimitiveComponentBackedTarget>(Target)->SetOwnerVisibility(true);
OriginalMeshPreview->SetVisible(false);
OriginalMeshPreview->Disconnect();
OriginalMeshPreview = nullptr;
if (Preview != nullptr)
{
FDynamicMeshOpResult Result = Preview->Shutdown();
if (ShutdownType == EToolShutdownType::Accept)
{
GetToolManager()->BeginUndoTransaction(LOCTEXT("MeshSpaceDeformer", "Space Deformer"));
FDynamicMesh3* DynamicMeshResult = Result.Mesh.Get();
check(DynamicMeshResult != nullptr);
ModelingTools: Reduce surface area of MeshDescriptionProvider/Committer, replace with UE::ToolTarget:: calls where possible. Add new UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh() function. This is being used for now to avoid potential regressions as UE::ToolTarget::CommitDynamicMeshUpdate will preferentially use DynamicMeshCommitter, and I am not certain it is functionally equivalent in all cases. Add new UE::ToolTarget::CommitDynamicMeshNormalsUpdate(), similar to existing UV version Add new Move-variant of UE::ToolTarget::CommitMeshDescriptionUpdate(), uses new Move-variant of IMeshDescriptionCommitter::CommitMeshDescription. Make existing IMeshDescriptionCommitter::CommitMeshDescription callback interface protected, to prevent usage of this function at public API level (will be removed in future). Tool updates should not change, just using cleaner APIs. EditNormalsTool now uses CommitDynamicMeshNormalsUpdate(), which does go via DynamicMeshCommitter preferentially, where it previously went via MeshDescriptionCommitter. In light testing the results appear equivalent. AttributeEditorTool now operates on MeshDescription copies in various update functions. These are not performance-critical. #rb rinat.abdrashitov #rnx #preflight 61ae45998358693a22c28d1b #ROBOMERGE-AUTHOR: ryan.schmidt #ROBOMERGE-SOURCE: CL 18384350 in //UE5/Release-5.0/... via CL 18384361 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v896-18170469) [CL 18384373 by ryan schmidt in ue5-release-engine-test branch]
2021-12-06 12:42:19 -05:00
UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh(Target, *DynamicMeshResult, true);
GetToolManager()->EndUndoTransaction();
}
}
DragAlignmentMechanic->Shutdown();
UInteractiveGizmoManager* GizmoManager = GetToolManager()->GetPairedGizmoManager();
GizmoManager->DestroyAllGizmosByOwner(this);
Gizmos: refactor Modeling Mode gizmo creation out of InteractiveGizmoManager. Editor will use other "default" transform gizmo implementations, and so the UTransformGizmo creation helper functions do not belong in GizmoManager. Instead a UTransformGizmoContextObject now provides this functionality. ModelingToolsEditorMode (and any other modes/systems that want to use these gizmo convenience functions) creates an instance of UTransformGizmoContextObject and registers it with the ContextObjectStore. Calling code can spawn a new UTransformGizmo by looking this object up in the ContextStore and calling it's helper functions. Static versions of the helper functions in the UE::TransformGizmoUtil:: namespace provide a single-line interface that replaces the previous GizmoManager call sites in the MeshModelingTools library. IntervalGizmo is now just registered and unregistered as needed by the MeshSpaceDeformerTool, as this is the only place it is currently used. Previous implementation in InteractiveGizmoManager is left intact as there are a few uses outside of MeshModelingTools that need to be cleaned up before it can be deleted. UTransformGizmo now requires it's Builder to tell it which sub-gizmo identifier strings to pass to the GizmoManager to create axis/plane/rotation sub-gizmos (and the code that registers the Builder must then provide these strings). This cleans up previous explicit references to UInteractiveGizmoManager static strings from UTransformGizmo. #rb Christina.TempelaarL, david.hill #rnx #jira none [CL 16409673 by Ryan Schmidt in ue5-main branch]
2021-05-20 16:39:39 -04:00
GizmoManager->DeregisterGizmoType(UIntervalGizmo::GizmoName);
}
void UMeshSpaceDeformerTool::TransformProxyChanged(UTransformProxy* Proxy, FTransform Transform)
{
GizmoFrame = FFrame3d(Transform.GetLocation(), Transform.GetRotation());
VisualizationRenderer.SetTransform(GizmoFrame.ToFTransform());
UpdatePreview();
}
void UMeshSpaceDeformerTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
{
UpIntervalSource->Value = Settings->UpperBoundsInterval;
DownIntervalSource->Value = Settings->LowerBoundsInterval;
ForwardIntervalSource->Value = GetModifierGizmoValue();
UpdatePreview();
OriginalMeshPreview->SetVisible(Settings->bShowOriginalMesh);
}
void UMeshSpaceDeformerTool::UpdateOpParameters(FMeshSpaceDeformerOp& MeshSpaceDeformerOp) const
{
MeshSpaceDeformerOp.OriginalMesh = OriginalDynamicMesh;
MeshSpaceDeformerOp.SetTransform(Cast<IPrimitiveComponentBackedTarget>(Target)->GetWorldTransform());
MeshSpaceDeformerOp.GizmoFrame = GizmoFrame;
// set the bound range
MeshSpaceDeformerOp.UpperBoundsInterval = Settings->UpperBoundsInterval;
MeshSpaceDeformerOp.LowerBoundsInterval = Settings->LowerBoundsInterval;
}
void UMeshSpaceDeformerTool::RequestAction(EMeshSpaceDeformerToolAction ActionType)
{
if (PendingAction == EMeshSpaceDeformerToolAction::NoAction)
{
PendingAction = ActionType;
}
}
void UMeshSpaceDeformerTool::ApplyAction(EMeshSpaceDeformerToolAction ActionType)
{
if (PendingAction == EMeshSpaceDeformerToolAction::ShiftToCenter)
{
SetGizmoFrameFromWorldPos((FVector)MeshCenter);
}
}
void UMeshSpaceDeformerTool::OnTick(float DeltaTime)
{
// Deal with clicked button
if (PendingAction != EMeshSpaceDeformerToolAction::NoAction)
{
ApplyAction(PendingAction);
PendingAction = EMeshSpaceDeformerToolAction::NoAction;
}
if (Preview != nullptr)
{
Preview->Tick(DeltaTime);
}
}
void UMeshSpaceDeformerTool::Render(IToolsContextRenderAPI* RenderAPI)
{
DragAlignmentMechanic->Render(RenderAPI);
if (Settings->bDrawVisualization && Settings->SelectedOperationType == ENonlinearOperationType::Bend)
{
VisualizationRenderer.BeginFrame(RenderAPI, RenderAPI->GetCameraState());
for (int32 i = 1; i < VisualizationPoints.Num(); ++i)
{
VisualizationRenderer.DrawLine(VisualizationPoints[i - 1], VisualizationPoints[i]);
}
VisualizationRenderer.EndFrame();
}
else
{
}
}
void UMeshSpaceDeformerTool::UpdatePreview()
{
if (Settings->SelectedOperationType == ENonlinearOperationType::Bend)
{
const int32 NUM_RENDER_POINTS = 30;
VisualizationPoints.SetNumUninitialized(NUM_RENDER_POINTS);
double BentLength = Settings->UpperBoundsInterval - Settings->LowerBoundsInterval;
double ArcAngle = Settings->BendDegrees * PI / 180;
double ArcRadius = BentLength / ArcAngle;
double RotationCenterZ = Settings->bLockBottom ? Settings->LowerBoundsInterval : 0;
FVector2d RotationCenterYZ(ArcRadius, RotationCenterZ);
double PointSpacing = BentLength / (NUM_RENDER_POINTS - 1);
for (int32 i = 0; i < NUM_RENDER_POINTS; ++i)
{
double OriginalZ = Settings->LowerBoundsInterval + PointSpacing * i;
FVector2d YZToRotate(0, RotationCenterZ);
// The negative here is because we are rotating clockwise in the direction of the positive Y axis
double AngleToRotate = -ArcAngle * (OriginalZ - RotationCenterZ) / BentLength;
FMatrix2d RotationMatrix = FMatrix2d::RotationRad(AngleToRotate);
FVector2d RotatedYZ = RotationMatrix * (YZToRotate - RotationCenterYZ) + RotationCenterYZ;
VisualizationPoints[i] = FVector3d(0, RotatedYZ.X, RotatedYZ.Y);
}
}
if (Preview)
{
Preview->InvalidateResult();
}
}
void UMeshSpaceDeformerTool::SetGizmoFrameFromWorldPos(const FVector& Position, const FVector& Normal, bool bAlignNormal)
{
GizmoFrame.Origin = (FVector3d)Position;
if (bAlignNormal)
{
// It's not clear whether aligning the Z axis to the normal is the right idea here. The Z axis
// is the main axis on which we operate. On the one hand, setting it to the normal gives the user
// greater control over its alignment. On the other hand, it seems likely that when clicking the object,
// the user would want the axis to lie along the object on the side they clicked, not pierce inwards.
// Still, it's hard to come up with a clean alternative.
FVector3d FrameZ(Normal);
FVector3d FrameY = UE::Geometry::Normalized(FrameZ.Cross(FVector3d::UnitZ())); // orthogonal to world Z and frame Z
FVector3d FrameX = FrameY.Cross(FrameZ); // safe to not normalize because already orthogonal
GizmoFrame = FFrame3d((FVector3d)Position, FrameX, FrameY, FrameZ);
}
TransformGizmo->ReinitializeGizmoTransform(GizmoFrame.ToFTransform());
UpdatePreview();
}
/**
* These two functions translate to and from the modifier gizmo length
* to the relevant operator parameters. They should be matched to each
* other.
*/
double UMeshSpaceDeformerTool::GetModifierGizmoValue() const
{
switch (Settings->SelectedOperationType)
{
case ENonlinearOperationType::Bend:
return Settings->BendDegrees * ModifierGizmoLength / 180;
case ENonlinearOperationType::Flare:
return Settings->FlarePercentY * ModifierGizmoLength / 100;
case ENonlinearOperationType::Twist:
return Settings->TwistDegrees * ModifierGizmoLength / 360;
}
// Shouldn't get here
check(false);
return 0;
}
void UMeshSpaceDeformerTool::ApplyModifierGizmoValue(double Value)
{
switch (Settings->SelectedOperationType)
{
case ENonlinearOperationType::Bend:
Settings->BendDegrees = 180 * Value / ModifierGizmoLength;
break;
case ENonlinearOperationType::Flare:
Settings->FlarePercentY = 100 * Value / ModifierGizmoLength;
break;
case ENonlinearOperationType::Twist:
Settings->TwistDegrees = 360 * Value/ ModifierGizmoLength;
break;
default:
check(false);
}
}
#undef LOCTEXT_NAMESPACE