2019-12-27 09:26:59 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#include "DynamicMeshSculptTool.h"
|
2020-01-27 20:11:15 -05:00
|
|
|
#include "Containers/Map.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
#include "InteractiveToolManager.h"
|
|
|
|
|
#include "InteractiveGizmoManager.h"
|
|
|
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
|
|
|
|
|
|
#include "SubRegionRemesher.h"
|
|
|
|
|
#include "ProjectionTargets.h"
|
|
|
|
|
#include "MeshConstraints.h"
|
|
|
|
|
#include "MeshConstraintsUtil.h"
|
|
|
|
|
#include "MeshWeights.h"
|
|
|
|
|
#include "MeshNormals.h"
|
|
|
|
|
#include "MeshIndexUtil.h"
|
|
|
|
|
#include "Drawing/MeshDebugDrawing.h"
|
|
|
|
|
#include "PreviewMesh.h"
|
|
|
|
|
#include "ToolSetupUtil.h"
|
2019-11-11 16:57:51 -05:00
|
|
|
#include "ToolSceneQueriesUtil.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#include "Changes/MeshVertexChange.h"
|
|
|
|
|
#include "Changes/MeshChange.h"
|
|
|
|
|
#include "DynamicMeshChangeTracker.h"
|
|
|
|
|
|
2019-10-24 14:46:56 -04:00
|
|
|
#include "ToolDataVisualizer.h"
|
|
|
|
|
#include "Components/PrimitiveComponent.h"
|
|
|
|
|
#include "Generators/SphereGenerator.h"
|
2019-09-12 13:55:17 -04:00
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
#include "InteractiveGizmoManager.h"
|
|
|
|
|
#include "BaseGizmos/GizmoComponents.h"
|
|
|
|
|
#include "BaseGizmos/TransformGizmo.h"
|
2020-01-27 20:11:15 -05:00
|
|
|
#include "UObject/ObjectMacros.h"
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "UDynamicMeshSculptTool"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ToolBuilder
|
|
|
|
|
*/
|
|
|
|
|
UMeshSurfacePointTool* UDynamicMeshSculptToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const
|
|
|
|
|
{
|
|
|
|
|
UDynamicMeshSculptTool* SculptTool = NewObject<UDynamicMeshSculptTool>(SceneState.ToolManager);
|
|
|
|
|
SculptTool->SetEnableRemeshing(this->bEnableRemeshing);
|
|
|
|
|
SculptTool->SetWorld(SceneState.World);
|
|
|
|
|
return SculptTool;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
/*
|
|
|
|
|
* BrushSculptProperties
|
|
|
|
|
*/
|
2019-10-01 20:41:42 -04:00
|
|
|
void UBrushSculptProperties::SaveProperties(UInteractiveTool* SaveFromTool)
|
|
|
|
|
{
|
|
|
|
|
UBrushSculptProperties* PropertyCache = GetPropertyCache<UBrushSculptProperties>();
|
|
|
|
|
PropertyCache->PrimaryBrushType = this->PrimaryBrushType;
|
2020-01-27 20:11:15 -05:00
|
|
|
PropertyCache->PrimaryBrushSpeed = this->PrimaryBrushSpeed;
|
|
|
|
|
PropertyCache->SmoothBrushSpeed = this->SmoothBrushSpeed;
|
2019-10-01 20:41:42 -04:00
|
|
|
PropertyCache->bPreserveUVFlow = this->bPreserveUVFlow;
|
2020-01-27 20:11:15 -05:00
|
|
|
PropertyCache->bSmoothBrushRemeshes = this->bSmoothBrushRemeshes;
|
2019-10-01 20:41:42 -04:00
|
|
|
PropertyCache->BrushDepth = this->BrushDepth;
|
2020-01-27 20:11:15 -05:00
|
|
|
PropertyCache->bFreezeTarget = this->bFreezeTarget;
|
2019-11-03 22:43:57 -05:00
|
|
|
PropertyCache->bHitBackFaces = this->bHitBackFaces;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UBrushSculptProperties::RestoreProperties(UInteractiveTool* RestoreToTool)
|
|
|
|
|
{
|
|
|
|
|
UBrushSculptProperties* PropertyCache = GetPropertyCache<UBrushSculptProperties>();
|
|
|
|
|
this->PrimaryBrushType = PropertyCache->PrimaryBrushType;
|
2020-01-27 20:11:15 -05:00
|
|
|
this->PrimaryBrushSpeed = PropertyCache->PrimaryBrushSpeed;
|
|
|
|
|
this->SmoothBrushSpeed = PropertyCache->SmoothBrushSpeed;
|
2019-10-01 20:41:42 -04:00
|
|
|
this->bPreserveUVFlow = PropertyCache->bPreserveUVFlow;
|
2020-01-27 20:11:15 -05:00
|
|
|
this->bSmoothBrushRemeshes = PropertyCache->bSmoothBrushRemeshes;
|
2019-10-01 20:41:42 -04:00
|
|
|
this->BrushDepth = PropertyCache->BrushDepth;
|
2020-01-27 20:11:15 -05:00
|
|
|
this->bFreezeTarget = PropertyCache->bFreezeTarget;
|
2019-11-03 22:43:57 -05:00
|
|
|
this->bHitBackFaces = PropertyCache->bHitBackFaces;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
/*
|
|
|
|
|
* FixedPlaneBrushProperties
|
|
|
|
|
*/
|
2019-11-05 17:20:52 -05:00
|
|
|
UFixedPlaneBrushProperties::UFixedPlaneBrushProperties()
|
|
|
|
|
{
|
|
|
|
|
bPropertySetEnabled = true;
|
|
|
|
|
bSnapToGrid = true;
|
|
|
|
|
bShowGizmo = true;
|
2019-11-05 20:26:40 -05:00
|
|
|
Position = FVector::ZeroVector;
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UFixedPlaneBrushProperties::SaveProperties(UInteractiveTool* SaveFromTool)
|
|
|
|
|
{
|
|
|
|
|
UFixedPlaneBrushProperties* PropertyCache = GetPropertyCache<UFixedPlaneBrushProperties>();
|
|
|
|
|
PropertyCache->bShowGizmo = this->bShowGizmo;
|
|
|
|
|
PropertyCache->bSnapToGrid = this->bSnapToGrid;
|
2019-11-05 20:26:40 -05:00
|
|
|
PropertyCache->Position = this->Position;
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
void UFixedPlaneBrushProperties::RestoreProperties(UInteractiveTool* RestoreToTool)
|
|
|
|
|
{
|
|
|
|
|
UFixedPlaneBrushProperties* PropertyCache = GetPropertyCache<UFixedPlaneBrushProperties>();
|
|
|
|
|
this->bShowGizmo = PropertyCache->bShowGizmo;
|
|
|
|
|
this->bSnapToGrid = PropertyCache->bSnapToGrid;
|
2019-11-05 20:26:40 -05:00
|
|
|
this->Position = PropertyCache->Position;
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
/*
|
|
|
|
|
* Tool
|
|
|
|
|
*/
|
|
|
|
|
UDynamicMeshSculptTool::UDynamicMeshSculptTool()
|
|
|
|
|
{
|
|
|
|
|
// initialize parameters
|
|
|
|
|
bEnableRemeshing = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::SetWorld(UWorld* World)
|
|
|
|
|
{
|
|
|
|
|
this->TargetWorld = World;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
namespace
|
|
|
|
|
{
|
2019-10-01 20:41:42 -04:00
|
|
|
const FString BrushIndicatorGizmoType = TEXT("BrushIndicatorGizmoType");
|
2020-01-27 20:11:15 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::Setup()
|
|
|
|
|
{
|
|
|
|
|
UMeshSurfacePointTool::Setup();
|
|
|
|
|
|
|
|
|
|
// create dynamic mesh component to use for live preview
|
|
|
|
|
DynamicMeshComponent = NewObject<UOctreeDynamicMeshComponent>(ComponentTarget->GetOwnerActor(), "DynamicMeshSculptToolMesh");
|
|
|
|
|
DynamicMeshComponent->SetupAttachment(ComponentTarget->GetOwnerActor()->GetRootComponent());
|
|
|
|
|
DynamicMeshComponent->RegisterComponent();
|
2019-10-18 13:12:29 -04:00
|
|
|
|
|
|
|
|
// initialize from LOD-0 MeshDescription
|
|
|
|
|
DynamicMeshComponent->InitializeMesh(ComponentTarget->GetMesh());
|
|
|
|
|
|
|
|
|
|
// transform mesh to world space because handling scaling inside brush is a mess
|
|
|
|
|
InitialTargetTransform = FTransform3d(ComponentTarget->GetWorldTransform());
|
|
|
|
|
// clamp scaling because if we allow zero-scale we cannot invert this transform on Accept
|
|
|
|
|
InitialTargetTransform.ClampMinimumScale(0.01);
|
2019-10-31 15:35:28 -04:00
|
|
|
FVector3d Translation = InitialTargetTransform.GetTranslation();
|
|
|
|
|
InitialTargetTransform.SetTranslation(FVector3d::Zero());
|
2019-10-18 13:12:29 -04:00
|
|
|
DynamicMeshComponent->ApplyTransform(InitialTargetTransform, false);
|
|
|
|
|
// since we moved to World coords there is not a current transform anymore.
|
2019-10-31 15:35:28 -04:00
|
|
|
CurTargetTransform = FTransform3d(Translation);
|
2019-10-18 13:12:29 -04:00
|
|
|
DynamicMeshComponent->SetWorldTransform((FTransform)CurTargetTransform);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
// copy material if there is one
|
|
|
|
|
UMaterialInterface* Material = ComponentTarget->GetMaterial(0);
|
|
|
|
|
if (Material != nullptr)
|
|
|
|
|
{
|
|
|
|
|
DynamicMeshComponent->SetMaterial(0, Material);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OnDynamicMeshComponentChangedHandle = DynamicMeshComponent->OnMeshChanged.Add(
|
|
|
|
|
FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &UDynamicMeshSculptTool::OnDynamicMeshComponentChanged));
|
|
|
|
|
|
|
|
|
|
// do we always want to keep vertex normals updated? Perhaps should discard vertex normals before baking?
|
2019-10-18 13:12:29 -04:00
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-01 20:41:42 -04:00
|
|
|
FMeshNormals::QuickComputeVertexNormals(*Mesh);
|
|
|
|
|
|
|
|
|
|
// switch to vertex normals for testing
|
|
|
|
|
//DynamicMeshComponent->GetMesh()->DiscardAttributes();
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
// initialize target mesh
|
2019-10-01 20:41:42 -04:00
|
|
|
UpdateTarget();
|
|
|
|
|
bTargetDirty = false;
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
// initialize brush radius range interval, brush properties
|
2019-10-01 20:41:42 -04:00
|
|
|
double MaxDimension = DynamicMeshComponent->GetMesh()->GetCachedBounds().MaxDim();
|
|
|
|
|
BrushRelativeSizeRange = FInterval1d(MaxDimension*0.01, MaxDimension);
|
|
|
|
|
BrushProperties = NewObject<UBrushBaseProperties>(this, TEXT("Brush"));
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
// initialize other properties
|
2019-10-01 20:41:42 -04:00
|
|
|
SculptProperties = NewObject<UBrushSculptProperties>(this, TEXT("Sculpting"));
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
RemeshProperties = NewObject<UBrushRemeshProperties>(this, TEXT("Remeshing"));
|
|
|
|
|
InitialEdgeLength = EstimateIntialSafeTargetLength(*Mesh, 5000);
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
// hide input Component
|
2019-10-01 20:41:42 -04:00
|
|
|
ComponentTarget->SetOwnerVisibility(false);
|
|
|
|
|
|
|
|
|
|
// init state flags flags
|
|
|
|
|
bInDrag = false;
|
|
|
|
|
bHaveRemeshed = false;
|
|
|
|
|
bRemeshPending = false;
|
|
|
|
|
bStampPending = false;
|
|
|
|
|
ActiveVertexChange = nullptr;
|
|
|
|
|
|
|
|
|
|
// register and spawn brush indicator gizmo
|
|
|
|
|
GetToolManager()->GetPairedGizmoManager()->RegisterGizmoType(BrushIndicatorGizmoType, NewObject<UBrushStampIndicatorBuilder>());
|
|
|
|
|
BrushIndicator = GetToolManager()->GetPairedGizmoManager()->CreateGizmo<UBrushStampIndicator>(BrushIndicatorGizmoType, FString(), this);
|
2019-10-24 14:46:56 -04:00
|
|
|
BrushIndicatorMesh = MakeDefaultSphereMesh(this, TargetWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
BrushIndicator->AttachedComponent = BrushIndicatorMesh->GetRootComponent();
|
|
|
|
|
BrushIndicator->bDrawIndicatorLines = false;
|
|
|
|
|
|
|
|
|
|
// initialize our properties
|
|
|
|
|
AddToolPropertySource(SculptProperties);
|
|
|
|
|
AddToolPropertySource(BrushProperties);
|
|
|
|
|
if (this->bEnableRemeshing)
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
SculptProperties->bIsRemeshingEnabled = true;
|
2019-10-01 20:41:42 -04:00
|
|
|
AddToolPropertySource(RemeshProperties);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BrushProperties->RestoreProperties(this);
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
SculptProperties->RestoreProperties(this);
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
GizmoProperties = NewObject<UFixedPlaneBrushProperties>();
|
|
|
|
|
GizmoProperties->RestoreProperties(this);
|
|
|
|
|
AddToolPropertySource(GizmoProperties);
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
ViewProperties = NewObject<UMeshEditingViewProperties>();
|
|
|
|
|
ViewProperties->RestoreProperties(this);
|
|
|
|
|
AddToolPropertySource(ViewProperties);
|
|
|
|
|
ShowWireframeWatcher.Initialize(
|
|
|
|
|
[this]() { return ViewProperties->bShowWireframe; },
|
|
|
|
|
[this](bool bNewValue) { DynamicMeshComponent->bExplicitShowWireframe = bNewValue; }, false);
|
|
|
|
|
MaterialModeWatcher.Initialize(
|
|
|
|
|
[this]() { return ViewProperties->MaterialMode; },
|
|
|
|
|
[this](EMeshEditingMaterialModes NewMode) { UpdateMaterialMode(NewMode); }, EMeshEditingMaterialModes::ExistingMaterial);
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
// create proxy for plane gizmo, but not gizmo itself, as it only appears in FixedPlane brush mode
|
|
|
|
|
// listen for changes to the proxy and update the plane when that happens
|
|
|
|
|
PlaneTransformProxy = NewObject<UTransformProxy>(this);
|
|
|
|
|
PlaneTransformProxy->OnTransformChanged.AddUObject(this, &UDynamicMeshSculptTool::PlaneTransformChanged);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
GetToolManager()->DisplayMessage(
|
2019-11-08 10:47:08 -05:00
|
|
|
LOCTEXT("OnStartSculptTool", "Hold Shift to Smooth, Ctrl to Invert (where applicable). Shift+Q/A keys cycle through Brush Types. Shift+S/D change Size (Ctrl+Shift to small-step), Shift+W/E change Speed."),
|
2019-10-01 20:41:42 -04:00
|
|
|
EToolMessageLevel::UserNotification);
|
|
|
|
|
|
|
|
|
|
if (bEnableRemeshing)
|
|
|
|
|
{
|
|
|
|
|
PrecomputeRemeshInfo();
|
|
|
|
|
if (bHaveUVSeams)
|
|
|
|
|
{
|
|
|
|
|
GetToolManager()->DisplayMessage(
|
|
|
|
|
LOCTEXT("UVSeamWarning", "This mesh has UV seams which may limit remeshing. Consider clearing the UV layers using the Remesh Tool."),
|
|
|
|
|
EToolMessageLevel::UserWarning);
|
2020-01-27 20:11:15 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
else if (bHaveNormalSeams)
|
|
|
|
|
{
|
|
|
|
|
GetToolManager()->DisplayMessage(
|
|
|
|
|
LOCTEXT("NormalSeamWarning", "This mesh has Hard Normal seams which may limit remeshing. Consider clearing Hard Normals using the Remesh Tool."),
|
|
|
|
|
EToolMessageLevel::UserWarning);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::Shutdown(EToolShutdownType ShutdownType)
|
|
|
|
|
{
|
|
|
|
|
BrushIndicatorMesh->Disconnect();
|
|
|
|
|
BrushIndicatorMesh = nullptr;
|
|
|
|
|
|
|
|
|
|
GetToolManager()->GetPairedGizmoManager()->DestroyAllGizmosByOwner(this);
|
|
|
|
|
BrushIndicator = nullptr;
|
|
|
|
|
GetToolManager()->GetPairedGizmoManager()->DeregisterGizmoType(BrushIndicatorGizmoType);
|
|
|
|
|
|
|
|
|
|
if (DynamicMeshComponent != nullptr)
|
|
|
|
|
{
|
|
|
|
|
DynamicMeshComponent->OnMeshChanged.Remove(OnDynamicMeshComponentChangedHandle);
|
|
|
|
|
|
|
|
|
|
ComponentTarget->SetOwnerVisibility(true);
|
|
|
|
|
|
|
|
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
// safe to do this here because we are about to destroy componeont
|
|
|
|
|
DynamicMeshComponent->ApplyTransform(InitialTargetTransform, true);
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
// this block bakes the modified DynamicMeshComponent back into the StaticMeshComponent inside an undo transaction
|
|
|
|
|
GetToolManager()->BeginUndoTransaction(LOCTEXT("SculptMeshToolTransactionName", "Sculpt Mesh"));
|
2019-11-14 15:31:53 -05:00
|
|
|
ComponentTarget->CommitMesh([=](const FPrimitiveComponentTarget::FCommitParams& CommitParams)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-11-14 15:31:53 -05:00
|
|
|
DynamicMeshComponent->Bake(CommitParams.MeshDescription, bHaveRemeshed);
|
2019-10-01 20:41:42 -04:00
|
|
|
});
|
|
|
|
|
GetToolManager()->EndUndoTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DynamicMeshComponent->UnregisterComponent();
|
|
|
|
|
DynamicMeshComponent->DestroyComponent();
|
|
|
|
|
DynamicMeshComponent = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BrushProperties->SaveProperties(this);
|
|
|
|
|
SculptProperties->SaveProperties(this);
|
|
|
|
|
ViewProperties->SaveProperties(this);
|
2019-11-05 20:26:40 -05:00
|
|
|
GizmoProperties->SaveProperties(this);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::OnDynamicMeshComponentChanged()
|
|
|
|
|
{
|
|
|
|
|
bNormalUpdatePending = true;
|
|
|
|
|
bTargetDirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 15:54:23 -05:00
|
|
|
void UDynamicMeshSculptTool::OnPropertyModified(UObject* PropertySet, FProperty* Property)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDynamicMeshSculptTool::HitTest(const FRay& Ray, FHitResult& OutHit)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
FRay3d LocalRay(CurTargetTransform.InverseTransformPosition(Ray.Origin),
|
|
|
|
|
CurTargetTransform.InverseTransformVector(Ray.Direction));
|
2019-10-01 20:41:42 -04:00
|
|
|
LocalRay.Direction.Normalize();
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2019-11-03 22:43:57 -05:00
|
|
|
int HitTID = FindHitSculptMeshTriangle(LocalRay);
|
2019-10-01 20:41:42 -04:00
|
|
|
if (HitTID != IndexConstants::InvalidID)
|
|
|
|
|
{
|
|
|
|
|
FTriangle3d Triangle;
|
|
|
|
|
Mesh->GetTriVertices(HitTID, Triangle.V[0], Triangle.V[1], Triangle.V[2]);
|
|
|
|
|
FIntrRay3Triangle3d Query(LocalRay, Triangle);
|
|
|
|
|
Query.Find();
|
|
|
|
|
|
|
|
|
|
OutHit.FaceIndex = HitTID;
|
|
|
|
|
OutHit.Distance = Query.RayParameter;
|
2019-10-18 13:12:29 -04:00
|
|
|
OutHit.Normal = (FVector)CurTargetTransform.TransformNormal(Mesh->GetTriNormal(HitTID));
|
|
|
|
|
OutHit.ImpactPoint = (FVector)CurTargetTransform.TransformPosition(LocalRay.PointAt(Query.RayParameter));
|
2019-10-01 20:41:42 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::OnBeginDrag(const FRay& Ray)
|
|
|
|
|
{
|
|
|
|
|
bSmoothing = GetShiftToggle();
|
|
|
|
|
bInvert = GetCtrlToggle();
|
|
|
|
|
|
|
|
|
|
FHitResult OutHit;
|
|
|
|
|
if (HitTest(Ray, OutHit))
|
|
|
|
|
{
|
|
|
|
|
BrushStartCenterWorld = Ray.PointAt(OutHit.Distance) + SculptProperties->BrushDepth*CurrentBrushRadius*Ray.Direction;
|
|
|
|
|
|
|
|
|
|
bInDrag = true;
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
ActiveDragPlane = FFrame3d(BrushStartCenterWorld, -Ray.Direction);
|
|
|
|
|
ActiveDragPlane.RayPlaneIntersection(Ray.Origin, Ray.Direction, 2, LastHitPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
LastBrushPosWorld = LastHitPosWorld;
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosNormalWorld = ActiveDragPlane.Z();
|
|
|
|
|
LastBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastHitPosWorld);
|
2019-10-18 13:04:19 -04:00
|
|
|
LastSmoothBrushPosLocal = LastBrushPosLocal;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
BeginChange(bEnableRemeshing == false);
|
|
|
|
|
|
|
|
|
|
UpdateROI(LastBrushPosLocal);
|
|
|
|
|
|
|
|
|
|
if (SculptProperties->PrimaryBrushType == EDynamicMeshSculptBrushType::Plane)
|
|
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
ActiveFixedBrushPlane = ComputeROIBrushPlane(LastBrushPosLocal, false);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// apply initial stamp
|
|
|
|
|
PendingStampRay = Ray;
|
|
|
|
|
bStampPending = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-19 18:07:47 -05:00
|
|
|
void UDynamicMeshSculptTool::UpdateROI(const FVector3d& BrushPos)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_UpdateROI);
|
|
|
|
|
|
|
|
|
|
// TODO: need dynamic vertex hash table!
|
|
|
|
|
|
|
|
|
|
float RadiusSqr = CurrentBrushRadius * CurrentBrushRadius;
|
|
|
|
|
|
|
|
|
|
FAxisAlignedBox3d BrushBox(
|
|
|
|
|
BrushPos - CurrentBrushRadius * FVector3d::One(),
|
|
|
|
|
BrushPos + CurrentBrushRadius * FVector3d::One());
|
|
|
|
|
|
|
|
|
|
VertexSetBuffer.Reset();
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
|
|
|
|
Octree->RangeQuery(BrushBox,
|
|
|
|
|
[&](int TriIdx) {
|
|
|
|
|
FIndex3i TriV = Mesh->GetTriangle(TriIdx);
|
|
|
|
|
for (int j = 0; j < 3; ++j)
|
|
|
|
|
{
|
|
|
|
|
FVector3d Position = Mesh->GetVertex(TriV[j]);
|
|
|
|
|
if ((Position - BrushPos).SquaredLength() < RadiusSqr)
|
|
|
|
|
{
|
|
|
|
|
VertexSetBuffer.Add(TriV[j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
VertexROI.SetNum(0, false);
|
|
|
|
|
BufferUtil::AppendElements(VertexROI, VertexSetBuffer);
|
|
|
|
|
|
|
|
|
|
TriangleROI.Reset();
|
|
|
|
|
MeshIndexUtil::VertexToTriangleOneRing(Mesh, VertexROI, TriangleROI);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::OnUpdateDrag(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
if (bInDrag)
|
|
|
|
|
{
|
|
|
|
|
PendingStampRay = WorldRay;
|
|
|
|
|
bStampPending = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::CalculateBrushRadius()
|
|
|
|
|
{
|
|
|
|
|
CurrentBrushRadius = 0.5 * BrushRelativeSizeRange.Interpolate(BrushProperties->BrushSize);
|
|
|
|
|
if (BrushProperties->bSpecifyRadius)
|
|
|
|
|
{
|
|
|
|
|
CurrentBrushRadius = BrushProperties->BrushRadius;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BrushProperties->BrushRadius = CurrentBrushRadius;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyStamp(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptToolApplyStamp);
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
// update brush type history. apologies for convoluted logic.
|
2019-10-01 20:41:42 -04:00
|
|
|
StampTimestamp++;
|
|
|
|
|
if (LastStampType != PendingStampType)
|
|
|
|
|
{
|
|
|
|
|
if (BrushTypeHistoryIndex != BrushTypeHistory.Num() - 1)
|
|
|
|
|
{
|
|
|
|
|
if (LastStampType != EDynamicMeshSculptBrushType::LastValue)
|
|
|
|
|
{
|
|
|
|
|
BrushTypeHistory.Add(LastStampType);
|
|
|
|
|
}
|
|
|
|
|
BrushTypeHistoryIndex = BrushTypeHistory.Num()-1;
|
|
|
|
|
}
|
|
|
|
|
LastStampType = PendingStampType;
|
|
|
|
|
if (BrushTypeHistory.Num() == 0 || BrushTypeHistory[BrushTypeHistory.Num()-1] != PendingStampType)
|
|
|
|
|
{
|
|
|
|
|
BrushTypeHistory.Add(PendingStampType);
|
|
|
|
|
BrushTypeHistoryIndex = BrushTypeHistory.Num() - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
|
|
|
|
|
SaveActiveROI();
|
|
|
|
|
|
|
|
|
|
if (bSmoothing)
|
|
|
|
|
{
|
|
|
|
|
ApplySmoothBrush(WorldRay);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (SculptProperties->PrimaryBrushType)
|
|
|
|
|
{
|
|
|
|
|
case EDynamicMeshSculptBrushType::Offset:
|
|
|
|
|
ApplyOffsetBrush(WorldRay);
|
|
|
|
|
break;
|
|
|
|
|
case EDynamicMeshSculptBrushType::SculptMax:
|
|
|
|
|
ApplySculptMaxBrush(WorldRay);
|
|
|
|
|
break;
|
|
|
|
|
case EDynamicMeshSculptBrushType::Move:
|
|
|
|
|
ApplyMoveBrush(WorldRay);
|
|
|
|
|
break;
|
2019-10-18 13:04:19 -04:00
|
|
|
case EDynamicMeshSculptBrushType::Smooth:
|
|
|
|
|
ApplySmoothBrush(WorldRay);
|
|
|
|
|
break;
|
2019-10-01 20:41:42 -04:00
|
|
|
case EDynamicMeshSculptBrushType::Pinch:
|
|
|
|
|
ApplyPinchBrush(WorldRay);
|
|
|
|
|
break;
|
|
|
|
|
case EDynamicMeshSculptBrushType::Inflate:
|
|
|
|
|
ApplyInflateBrush(WorldRay);
|
|
|
|
|
break;
|
|
|
|
|
case EDynamicMeshSculptBrushType::Flatten:
|
|
|
|
|
ApplyFlattenBrush(WorldRay);
|
|
|
|
|
break;
|
|
|
|
|
case EDynamicMeshSculptBrushType::Plane:
|
|
|
|
|
ApplyPlaneBrush(WorldRay);
|
|
|
|
|
break;
|
2019-11-05 17:20:52 -05:00
|
|
|
case EDynamicMeshSculptBrushType::FixedPlane:
|
|
|
|
|
ApplyFixedPlaneBrush(WorldRay);
|
|
|
|
|
break;
|
2020-01-27 20:11:15 -05:00
|
|
|
case EDynamicMeshSculptBrushType::LastValue:
|
|
|
|
|
break;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double UDynamicMeshSculptTool::CalculateBrushFalloff(double Distance)
|
|
|
|
|
{
|
|
|
|
|
double d = Distance / CurrentBrushRadius;
|
|
|
|
|
double w = 1;
|
|
|
|
|
if (d > 0.5)
|
|
|
|
|
{
|
|
|
|
|
d = VectorUtil::Clamp((d - 0.5) / 0.5, 0.0, 1.0);
|
|
|
|
|
w = (1 - d * d);
|
|
|
|
|
w = w * w * w;
|
|
|
|
|
}
|
|
|
|
|
return w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplySmoothBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnSculptMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
|
|
|
|
|
FVector3d SmoothedPos = (SculptProperties->bPreserveUVFlow) ?
|
|
|
|
|
FMeshWeights::MeanValueCentroid(*Mesh, VertIdx) : FMeshWeights::UniformCentroid(*Mesh, VertIdx);
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
FVector3d NewPos = FVector3d::Lerp(OrigPos, SmoothedPos, Falloff*SculptProperties->SmoothBrushSpeed);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyMoveBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnActivePlane(WorldRay);
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d MoveVec = NewBrushPosLocal - LastBrushPosLocal;
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
if (MoveVec.SquaredLength() <= 0)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
return;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, MoveVec](int k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
|
|
|
|
|
double PrevDist = (OrigPos - LastBrushPosLocal).Length();
|
|
|
|
|
double NewDist = (OrigPos - NewBrushPosLocal).Length();
|
|
|
|
|
double UseDist = FMath::Min(PrevDist, NewDist);
|
|
|
|
|
|
2019-12-19 18:07:47 -05:00
|
|
|
double Falloff = CalculateBrushFalloff(UseDist) * ActivePressure;
|
2019-10-18 13:04:19 -04:00
|
|
|
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
|
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
2019-10-01 20:41:42 -04:00
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyOffsetBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnTargetMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
double Direction = (bInvert) ? -1.0 : 1.0;
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = Direction * FMathd::Sqrt(CurrentBrushRadius) * (SculptProperties->PrimaryBrushSpeed) * ActivePressure;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, UseSpeed](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
|
|
|
|
|
FVector3d BasePos, BaseNormal;
|
2019-10-25 13:34:18 -04:00
|
|
|
if (GetTargetMeshNearest(OrigPos, (double)(4 * CurrentBrushRadius), BasePos, BaseNormal) == false)
|
|
|
|
|
{
|
|
|
|
|
ROIPositionBuffer[k] = OrigPos;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FVector3d MoveVec = UseSpeed * BaseNormal;
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
|
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
}
|
2019-10-18 13:04:19 -04:00
|
|
|
});
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplySculptMaxBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnTargetMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
double Direction = (bInvert) ? -1.0 : 1.0;
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = Direction * FMathd::Sqrt(CurrentBrushRadius) * (SculptProperties->PrimaryBrushSpeed) * ActivePressure;
|
2019-10-01 20:41:42 -04:00
|
|
|
double MaxOffset = CurrentBrushRadius * 0.5;
|
|
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, UseSpeed, MaxOffset](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
|
|
|
|
|
FVector3d BasePos, BaseNormal;
|
2019-10-25 13:34:18 -04:00
|
|
|
if (GetTargetMeshNearest(OrigPos, (double)(2 * CurrentBrushRadius), BasePos, BaseNormal) == false)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-25 13:34:18 -04:00
|
|
|
ROIPositionBuffer[k] = OrigPos;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
2019-10-25 13:34:18 -04:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FVector3d MoveVec = UseSpeed * BaseNormal;
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-25 13:34:18 -04:00
|
|
|
FVector3d DeltaPos = NewPos - BasePos;
|
|
|
|
|
if (DeltaPos.SquaredLength() > MaxOffset*MaxOffset)
|
|
|
|
|
{
|
|
|
|
|
DeltaPos.Normalize();
|
|
|
|
|
NewPos = BasePos + MaxOffset * DeltaPos;
|
|
|
|
|
}
|
|
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
}
|
2019-10-18 13:04:19 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyPinchBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnTargetMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
|
|
|
|
FVector3d BrushNormalLocal = CurTargetTransform.InverseTransformNormal(LastBrushPosNormalWorld);
|
|
|
|
|
FVector3d OffsetBrushPosLocal = NewBrushPosLocal - SculptProperties->BrushDepth * CurrentBrushRadius * BrushNormalLocal;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
// hardcoded lazybrush...
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewSmoothBrushPosLocal = (0.75f)*LastSmoothBrushPosLocal + (0.25f)*NewBrushPosLocal;
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
double Direction = (bInvert) ? -1.0 : 1.0;
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = Direction * FMathd::Sqrt(CurrentBrushRadius) * (SculptProperties->PrimaryBrushSpeed*0.05) * ActivePressure;
|
2019-10-18 13:04:19 -04:00
|
|
|
|
|
|
|
|
FVector3d MotionVec = NewSmoothBrushPosLocal - LastSmoothBrushPosLocal;
|
|
|
|
|
bool bHaveMotion = (MotionVec.Length() > FMathf::ZeroTolerance);
|
2019-10-01 20:41:42 -04:00
|
|
|
MotionVec.Normalize();
|
2019-10-18 13:04:19 -04:00
|
|
|
FLine3d MoveLine(LastSmoothBrushPosLocal, MotionVec);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, OffsetBrushPosLocal, bHaveMotion, MotionVec, UseSpeed](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
FVector3d Delta = OffsetBrushPosLocal - OrigPos;
|
2019-10-18 13:04:19 -04:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d MoveVec = UseSpeed * Delta;
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
// pinch uses 1/x falloff, shifted so that
|
2019-10-18 13:04:19 -04:00
|
|
|
double Distance = OrigPos.Distance(NewBrushPosLocal);
|
|
|
|
|
double NormalizedDistance = Distance / CurrentBrushRadius + FMathf::ZeroTolerance;
|
|
|
|
|
double Falloff = (1.0/NormalizedDistance) - 1.0;
|
|
|
|
|
Falloff = FMathd::Clamp(Falloff, 0.0, 1.0);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
if (bHaveMotion && Falloff < 0.8f)
|
|
|
|
|
{
|
|
|
|
|
double AnglePower = 1.0 - FMathd::Abs(MoveVec.Normalized().Dot(MotionVec));
|
|
|
|
|
Falloff *= AnglePower;
|
|
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
2019-10-18 13:04:19 -04:00
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
2019-10-18 13:04:19 -04:00
|
|
|
LastSmoothBrushPosLocal = NewSmoothBrushPosLocal;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
FFrame3d UDynamicMeshSculptTool::ComputeROIBrushPlane(const FVector3d& BrushCenter, bool bIgnoreDepth)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FVector3d AverageNormal(0, 0, 0);
|
|
|
|
|
FVector3d AveragePos(0, 0, 0);
|
|
|
|
|
double WeightSum = 0;
|
|
|
|
|
for (int TriID : TriangleROI)
|
|
|
|
|
{
|
|
|
|
|
FVector3d Centroid = Mesh->GetTriCentroid(TriID);
|
|
|
|
|
double Weight = CalculateBrushFalloff(BrushCenter.Distance(Centroid));
|
|
|
|
|
|
|
|
|
|
AverageNormal += Weight * Mesh->GetTriNormal(TriID);
|
|
|
|
|
AveragePos += Weight * Centroid;
|
|
|
|
|
WeightSum += Weight;
|
|
|
|
|
}
|
|
|
|
|
AverageNormal.Normalize();
|
|
|
|
|
AveragePos /= WeightSum;
|
|
|
|
|
|
|
|
|
|
FFrame3d Result = FFrame3d(AveragePos, AverageNormal);
|
2019-10-18 13:04:19 -04:00
|
|
|
if (bIgnoreDepth == false)
|
|
|
|
|
{
|
|
|
|
|
Result.Origin -= SculptProperties->BrushDepth * CurrentBrushRadius * Result.Z();
|
|
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyPlaneBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnSculptMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
|
|
|
|
FVector3d BrushNormalLocal = CurTargetTransform.InverseTransformNormal(LastBrushPosNormalWorld);
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = FMathd::Sqrt(CurrentBrushRadius) * FMathd::Sqrt(SculptProperties->PrimaryBrushSpeed) * 0.05 * ActivePressure;
|
2019-10-18 13:04:19 -04:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, UseSpeed](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
FVector3d PlanePos = ActiveFixedBrushPlane.ToPlane(OrigPos, 2);
|
|
|
|
|
FVector3d Delta = PlanePos - OrigPos;
|
|
|
|
|
FVector3d MoveVec = UseSpeed * Delta;
|
|
|
|
|
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
2019-10-18 13:04:19 -04:00
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
void UDynamicMeshSculptTool::ApplyFixedPlaneBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnSculptMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
|
|
|
|
FVector3d BrushNormalLocal = CurTargetTransform.InverseTransformNormal(LastBrushPosNormalWorld);
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = CurrentBrushRadius * FMathd::Sqrt(SculptProperties->PrimaryBrushSpeed) * 0.1 * ActivePressure;
|
2019-11-05 17:20:52 -05:00
|
|
|
|
|
|
|
|
FFrame3d FixedPlaneLocal(
|
2019-11-05 20:26:40 -05:00
|
|
|
CurTargetTransform.InverseTransformPosition(GizmoProperties->Position),
|
2019-11-05 17:20:52 -05:00
|
|
|
CurTargetTransform.GetRotation().Inverse() * (FQuaterniond)DrawPlaneOrientation);
|
|
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, UseSpeed, FixedPlaneLocal](int k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
FVector3d PlanePos = FixedPlaneLocal.ToPlane(OrigPos, 2);
|
|
|
|
|
FVector3d Delta = PlanePos - OrigPos;
|
|
|
|
|
double MaxDist = Delta.Normalize();
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
FVector3d MoveVec = Falloff * UseSpeed * Delta;
|
2020-01-27 20:11:15 -05:00
|
|
|
FVector3d NewPos = (MoveVec.SquaredLength() > MaxDist*MaxDist) ?
|
2019-11-05 17:20:52 -05:00
|
|
|
PlanePos : OrigPos + Falloff * MoveVec;
|
|
|
|
|
|
|
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
void UDynamicMeshSculptTool::ApplyFlattenBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnSculptMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
|
|
|
|
FVector3d BrushNormalLocal = CurTargetTransform.InverseTransformNormal(LastBrushPosNormalWorld);
|
2019-10-18 13:04:19 -04:00
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = FMathd::Sqrt(CurrentBrushRadius) * FMathd::Sqrt(SculptProperties->PrimaryBrushSpeed) * 0.05 * ActivePressure;
|
2019-10-18 13:04:19 -04:00
|
|
|
FFrame3d StampFlattenPlane = ComputeROIBrushPlane(NewBrushPosLocal, true);
|
|
|
|
|
//StampFlattenPlane.Origin -= SculptProperties->BrushDepth * CurrentBrushRadius * StampFlattenPlane.Z();
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
ParallelFor(NumV, [this, Mesh, NewBrushPosLocal, UseSpeed, StampFlattenPlane](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
FVector3d PlanePos = StampFlattenPlane.ToPlane(OrigPos, 2);
|
|
|
|
|
FVector3d Delta = PlanePos - OrigPos;
|
|
|
|
|
FVector3d MoveVec = UseSpeed * Delta;
|
|
|
|
|
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff * MoveVec;
|
2019-10-18 13:04:19 -04:00
|
|
|
ROIPositionBuffer[k] = NewPos;
|
|
|
|
|
});
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
2019-10-01 20:41:42 -04:00
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::ApplyInflateBrush(const FRay& WorldRay)
|
|
|
|
|
{
|
|
|
|
|
bool bHit = UpdateBrushPositionOnSculptMesh(WorldRay);
|
|
|
|
|
if (bHit == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewBrushPosLocal = CurTargetTransform.InverseTransformPosition(LastBrushPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
double Direction = (bInvert) ? -1.0 : 1.0;
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSpeed = Direction * CurrentBrushRadius * SculptProperties->PrimaryBrushSpeed * 0.05 * ActivePressure;
|
2019-10-18 13:04:19 -04:00
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
2019-10-18 13:04:19 -04:00
|
|
|
int NumV = VertexROI.Num();
|
|
|
|
|
ROIPositionBuffer.SetNum(NumV, false);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
// calculate vertex normals
|
|
|
|
|
ParallelFor(VertexROI.Num(), [this, Mesh](int Index) {
|
|
|
|
|
int VertIdx = VertexROI[Index];
|
|
|
|
|
FVector3d Normal = FMeshNormals::ComputeVertexNormal(*Mesh, VertIdx);
|
|
|
|
|
Mesh->SetVertexNormal(VertIdx, (FVector3f)Normal);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
ParallelFor(VertexROI.Num(), [this, Mesh, UseSpeed, NewBrushPosLocal](int k)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
int VertIdx = VertexROI[k];
|
2019-10-01 20:41:42 -04:00
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
FVector3d Normal = (FVector3d)Mesh->GetVertexNormal(VertIdx);
|
|
|
|
|
|
|
|
|
|
FVector3d MoveVec = UseSpeed * Normal;
|
|
|
|
|
|
|
|
|
|
double Falloff = CalculateBrushFalloff(OrigPos.Distance(NewBrushPosLocal));
|
|
|
|
|
|
|
|
|
|
FVector3d NewPos = OrigPos + Falloff*MoveVec;
|
2019-10-18 13:04:19 -04:00
|
|
|
ROIPositionBuffer[k] = NewPos;
|
2019-10-01 20:41:42 -04:00
|
|
|
});
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
for (int k = 0; k < NumV; ++k)
|
|
|
|
|
{
|
|
|
|
|
int VertIdx = VertexROI[k];
|
|
|
|
|
const FVector3d& NewPos = ROIPositionBuffer[k];
|
|
|
|
|
FVector3d OrigPos = Mesh->GetVertex(VertIdx);
|
|
|
|
|
Mesh->SetVertex(VertIdx, NewPos);
|
|
|
|
|
UpdateSavedVertex(VertIdx, OrigPos, NewPos);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
bRemeshPending = bEnableRemeshing;
|
|
|
|
|
|
|
|
|
|
LastBrushPosLocal = NewBrushPosLocal;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-03 22:43:57 -05:00
|
|
|
int UDynamicMeshSculptTool::FindHitSculptMeshTriangle(const FRay3d& LocalRay)
|
|
|
|
|
{
|
|
|
|
|
if (SculptProperties->bHitBackFaces)
|
|
|
|
|
{
|
|
|
|
|
return DynamicMeshComponent->GetOctree()->FindNearestHitObject(LocalRay);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
|
|
|
|
|
FViewCameraState StateOut;
|
|
|
|
|
GetToolManager()->GetContextQueriesAPI()->GetCurrentViewState(StateOut);
|
|
|
|
|
FVector3d LocalEyePosition(CurTargetTransform.InverseTransformPosition(StateOut.Position));
|
|
|
|
|
int HitTID = DynamicMeshComponent->GetOctree()->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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UDynamicMeshSculptTool::FindHitTargetMeshTriangle(const FRay3d& LocalRay)
|
|
|
|
|
{
|
|
|
|
|
if (SculptProperties->bHitBackFaces == false)
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
|
|
|
|
|
FViewCameraState StateOut;
|
|
|
|
|
GetToolManager()->GetContextQueriesAPI()->GetCurrentViewState(StateOut);
|
|
|
|
|
FVector3d LocalEyePosition(CurTargetTransform.InverseTransformPosition(StateOut.Position));
|
|
|
|
|
|
2020-02-10 16:10:15 -05:00
|
|
|
BrushTargetMeshSpatial.TriangleFilterF = [this, Mesh, LocalEyePosition](int TriangleID) {
|
2019-11-03 22:43:57 -05:00
|
|
|
FVector3d Normal, Centroid;
|
|
|
|
|
double Area;
|
|
|
|
|
Mesh->GetTriInfo(TriangleID, Normal, Area, Centroid);
|
|
|
|
|
return Normal.Dot((Centroid - LocalEyePosition)) < 0;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HitTID = BrushTargetMeshSpatial.FindNearestHitTriangle(LocalRay);
|
|
|
|
|
|
|
|
|
|
if (SculptProperties->bHitBackFaces == false)
|
|
|
|
|
{
|
|
|
|
|
BrushTargetMeshSpatial.TriangleFilterF = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return HitTID;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
bool UDynamicMeshSculptTool::UpdateBrushPositionOnActivePlane(const FRay& WorldRay)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewHitPosWorld;
|
|
|
|
|
ActiveDragPlane.RayPlaneIntersection(WorldRay.Origin, WorldRay.Direction, 2, NewHitPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
LastBrushPosWorld = NewHitPosWorld;
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosNormalWorld = ActiveDragPlane.Z();
|
2019-10-01 20:41:42 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDynamicMeshSculptTool::UpdateBrushPositionOnTargetMesh(const FRay& WorldRay)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
FRay3d LocalRay(CurTargetTransform.InverseTransformPosition(WorldRay.Origin),
|
|
|
|
|
CurTargetTransform.InverseTransformVector(WorldRay.Direction));
|
2019-10-01 20:41:42 -04:00
|
|
|
LocalRay.Direction.Normalize();
|
|
|
|
|
|
2019-11-03 22:43:57 -05:00
|
|
|
int HitTID = FindHitTargetMeshTriangle(LocalRay);
|
2019-10-01 20:41:42 -04:00
|
|
|
if (HitTID != IndexConstants::InvalidID)
|
|
|
|
|
{
|
|
|
|
|
const FDynamicMesh3* TargetMesh = BrushTargetMeshSpatial.GetMesh();
|
|
|
|
|
|
|
|
|
|
FTriangle3d Triangle;
|
|
|
|
|
TargetMesh->GetTriVertices(HitTID, Triangle.V[0], Triangle.V[1], Triangle.V[2]);
|
|
|
|
|
FIntrRay3Triangle3d Query(LocalRay, Triangle);
|
|
|
|
|
Query.Find();
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosNormalWorld = CurTargetTransform.TransformNormal(TargetMesh->GetTriNormal(HitTID));
|
|
|
|
|
LastBrushPosWorld = CurTargetTransform.TransformPosition(LocalRay.PointAt(Query.RayParameter));
|
2019-10-01 20:41:42 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDynamicMeshSculptTool::UpdateBrushPositionOnSculptMesh(const FRay& WorldRay)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
FRay3d LocalRay(CurTargetTransform.InverseTransformPosition(WorldRay.Origin),
|
|
|
|
|
CurTargetTransform.InverseTransformVector(WorldRay.Direction));
|
2019-10-01 20:41:42 -04:00
|
|
|
LocalRay.Direction.Normalize();
|
|
|
|
|
|
2019-11-03 22:43:57 -05:00
|
|
|
int HitTID = FindHitSculptMeshTriangle(LocalRay);
|
2019-10-01 20:41:42 -04:00
|
|
|
if (HitTID != IndexConstants::InvalidID)
|
|
|
|
|
{
|
|
|
|
|
const FDynamicMesh3* SculptMesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
|
|
|
|
|
FTriangle3d Triangle;
|
|
|
|
|
SculptMesh->GetTriVertices(HitTID, Triangle.V[0], Triangle.V[1], Triangle.V[2]);
|
|
|
|
|
FIntrRay3Triangle3d Query(LocalRay, Triangle);
|
|
|
|
|
Query.Find();
|
|
|
|
|
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosNormalWorld = CurTargetTransform.TransformNormal(SculptMesh->GetTriNormal(HitTID));
|
|
|
|
|
LastBrushPosWorld = CurTargetTransform.TransformPosition(LocalRay.PointAt(Query.RayParameter));
|
2019-10-01 20:41:42 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::OnEndDrag(const FRay& Ray)
|
|
|
|
|
{
|
|
|
|
|
bInDrag = false;
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
// cancel these! otherwise change record could become invalid
|
2019-10-01 20:41:42 -04:00
|
|
|
bStampPending = false;
|
|
|
|
|
bRemeshPending = false;
|
|
|
|
|
|
|
|
|
|
// update spatial
|
|
|
|
|
bTargetDirty = true;
|
|
|
|
|
|
|
|
|
|
// close change record
|
|
|
|
|
EndChange();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDynamicMeshSculptTool::OnUpdateHover(const FInputDeviceRay& DevicePos)
|
|
|
|
|
{
|
|
|
|
|
PendingStampType = SculptProperties->PrimaryBrushType;
|
|
|
|
|
|
|
|
|
|
if (bInDrag)
|
|
|
|
|
{
|
2019-10-18 13:12:29 -04:00
|
|
|
FVector3d NewHitPosWorld;
|
|
|
|
|
ActiveDragPlane.RayPlaneIntersection(DevicePos.WorldRay.Origin, DevicePos.WorldRay.Direction, 2, NewHitPosWorld);
|
2019-10-01 20:41:42 -04:00
|
|
|
LastBrushPosWorld = NewHitPosWorld;
|
2019-10-18 13:12:29 -04:00
|
|
|
LastBrushPosNormalWorld = ActiveDragPlane.Z();
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FHitResult OutHit;
|
|
|
|
|
if (HitTest(DevicePos.WorldRay, OutHit))
|
|
|
|
|
{
|
|
|
|
|
LastBrushPosWorld = DevicePos.WorldRay.PointAt(OutHit.Distance + SculptProperties->BrushDepth*CurrentBrushRadius);
|
|
|
|
|
LastBrushPosNormalWorld = OutHit.Normal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::Render(IToolsContextRenderAPI* RenderAPI)
|
|
|
|
|
{
|
|
|
|
|
UMeshSurfacePointTool::Render(RenderAPI);
|
|
|
|
|
|
2019-12-19 18:07:47 -05:00
|
|
|
BrushIndicator->Update( (float)this->CurrentBrushRadius, (FVector)this->LastBrushPosWorld, (FVector)this->LastBrushPosNormalWorld, 1.0f);
|
2019-11-05 17:20:52 -05:00
|
|
|
|
|
|
|
|
if (SculptProperties->PrimaryBrushType == EDynamicMeshSculptBrushType::FixedPlane)
|
|
|
|
|
{
|
|
|
|
|
FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface();
|
|
|
|
|
FColor GridColor(128, 128, 128, 32);
|
|
|
|
|
float GridThickness = 0.5f;
|
|
|
|
|
float GridLineSpacing = 25.0f; // @todo should be relative to view
|
|
|
|
|
int NumGridLines = 10;
|
2019-11-05 20:26:40 -05:00
|
|
|
FFrame3f DrawFrame(GizmoProperties->Position, DrawPlaneOrientation);
|
2019-11-05 17:20:52 -05:00
|
|
|
MeshDebugDraw::DrawSimpleGrid(DrawFrame, NumGridLines, GridLineSpacing, GridThickness, GridColor, false, PDI, FTransform::Identity);
|
|
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::Tick(float DeltaTime)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptToolTick);
|
|
|
|
|
|
|
|
|
|
UMeshSurfacePointTool::Tick(DeltaTime);
|
|
|
|
|
|
2019-12-19 18:07:47 -05:00
|
|
|
ActivePressure = GetCurrentDevicePressure();
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
// Allow a tick to pass between application of brush stamps. Bizarrely this
|
|
|
|
|
// improves responsiveness in the Editor...
|
|
|
|
|
static int TICK_SKIP_HACK = 0;
|
|
|
|
|
if (TICK_SKIP_HACK++ % 2 == 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
ShowWireframeWatcher.CheckAndUpdate();
|
|
|
|
|
MaterialModeWatcher.CheckAndUpdate();
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
bool bGizmoVisible = (SculptProperties->PrimaryBrushType == EDynamicMeshSculptBrushType::FixedPlane)
|
|
|
|
|
&& (GizmoProperties->bShowGizmo);
|
|
|
|
|
UpdateFixedPlaneGizmoVisibility(bGizmoVisible);
|
|
|
|
|
GizmoProperties->bPropertySetEnabled = (SculptProperties->PrimaryBrushType == EDynamicMeshSculptBrushType::FixedPlane);
|
|
|
|
|
|
|
|
|
|
if (bPendingSetFixedPlanePosition)
|
|
|
|
|
{
|
2019-12-19 18:07:47 -05:00
|
|
|
SetFixedSculptPlaneFromWorldPos((FVector)LastBrushPosWorld);
|
2019-11-05 17:20:52 -05:00
|
|
|
bPendingSetFixedPlanePosition = false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
// if user changed to not-frozen, we need to update the target
|
|
|
|
|
if (bCachedFreezeTarget != SculptProperties->bFreezeTarget)
|
|
|
|
|
{
|
|
|
|
|
UpdateTarget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bMeshModified = false;
|
|
|
|
|
bool bMeshShapeModified = false;
|
|
|
|
|
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
|
|
|
|
|
|
|
|
|
//Octree->CheckValidity(EValidityCheckFailMode::Check, false, true);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Apply stamp
|
2020-01-27 20:11:15 -05:00
|
|
|
//
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
if (bStampPending)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Tick_ApplyStampBlock);
|
|
|
|
|
|
|
|
|
|
// would this ever be true? does stamp require this?
|
|
|
|
|
bool bRemoveTrianglesBeforeStamp = false;
|
|
|
|
|
|
|
|
|
|
if ( bRemoveTrianglesBeforeStamp )
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Tick_ApplyStamp_Remove);
|
|
|
|
|
Octree->RemoveTriangles(TriangleROI);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Tick_ApplyStamp_Remove);
|
|
|
|
|
Octree->NotifyPendingModification(TriangleROI); // to mark initial positions
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyStamp(PendingStampRay);
|
|
|
|
|
bStampPending = false;
|
|
|
|
|
|
|
|
|
|
bNormalUpdatePending = true;
|
|
|
|
|
bMeshModified = true;
|
|
|
|
|
bMeshShapeModified = true;
|
|
|
|
|
|
|
|
|
|
// flow
|
|
|
|
|
if (bInDrag)
|
|
|
|
|
{
|
|
|
|
|
bStampPending = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bRemoveTrianglesBeforeStamp)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Tick_ApplyStamp_Insert);
|
|
|
|
|
Octree->InsertTriangles(TriangleROI);
|
|
|
|
|
}
|
2020-01-27 20:11:15 -05:00
|
|
|
else
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Tick_ApplyStamp_Insert);
|
|
|
|
|
Octree->ReinsertTriangles(TriangleROI);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool bUpdatedROIInRemesh = false;
|
|
|
|
|
if (bRemeshPending)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Tick_RemeshBlock);
|
|
|
|
|
|
|
|
|
|
check(bInDrag == true); // this would break undo otherwise!
|
|
|
|
|
|
|
|
|
|
RemeshROIPass();
|
|
|
|
|
|
|
|
|
|
bMeshModified = true;
|
|
|
|
|
bMeshShapeModified = true;
|
|
|
|
|
bRemeshPending = false;
|
|
|
|
|
bNormalUpdatePending = true;
|
|
|
|
|
bHaveRemeshed = true;
|
|
|
|
|
bUpdatedROIInRemesh = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Octree->CheckValidity(EValidityCheckFailMode::Check, false, true);
|
|
|
|
|
|
|
|
|
|
if (bNormalUpdatePending)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Tick_NormalsBlock);
|
|
|
|
|
|
|
|
|
|
if (Mesh->HasAttributes() && Mesh->Attributes()->PrimaryNormals() != nullptr)
|
|
|
|
|
{
|
|
|
|
|
RecalculateNormals_Overlay();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RecalculateNormals_PerVertex();
|
|
|
|
|
}
|
|
|
|
|
bNormalUpdatePending = false;
|
|
|
|
|
bMeshModified = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bMeshModified)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Tick_UpdateMeshBlock);
|
|
|
|
|
|
|
|
|
|
if (bMeshModified)
|
|
|
|
|
|
|
|
|
|
DynamicMeshComponent->NotifyMeshUpdated();
|
|
|
|
|
GetToolManager()->PostInvalidation();
|
|
|
|
|
|
|
|
|
|
bMeshModified = false;
|
|
|
|
|
|
|
|
|
|
if (bUpdatedROIInRemesh == false)
|
|
|
|
|
{
|
|
|
|
|
UpdateROI(LastBrushPosLocal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bTargetDirty)
|
|
|
|
|
{
|
|
|
|
|
UpdateTarget();
|
|
|
|
|
bTargetDirty = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::PrecomputeRemeshInfo()
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
|
|
|
|
|
// check if we have any open boundary edges
|
|
|
|
|
bHaveMeshBoundaries = false;
|
|
|
|
|
for (int eid : Mesh->EdgeIndicesItr())
|
|
|
|
|
{
|
|
|
|
|
if (Mesh->IsBoundaryEdge(eid))
|
|
|
|
|
{
|
|
|
|
|
bHaveMeshBoundaries = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if we have any UV seams
|
|
|
|
|
bHaveUVSeams = false;
|
|
|
|
|
bHaveNormalSeams = false;
|
|
|
|
|
if (Mesh->HasAttributes())
|
|
|
|
|
{
|
|
|
|
|
FDynamicMeshAttributeSet* Attribs = Mesh->Attributes();
|
|
|
|
|
for (int k = 0; k < Attribs->NumUVLayers(); ++k)
|
|
|
|
|
{
|
|
|
|
|
bHaveUVSeams = bHaveUVSeams || Attribs->GetUVLayer(k)->HasInteriorSeamEdges();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bHaveNormalSeams = Attribs->PrimaryNormals()->HasInteriorSeamEdges();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
/*
|
|
|
|
|
Split Collapse Vertices Pinned Flip
|
|
|
|
|
Fixed FALSE FALSE TRUE FALSE
|
|
|
|
|
Refine TRUE FALSE TRUE FALSE
|
|
|
|
|
Free TRUE TRUE FALSE FALSE
|
|
|
|
|
Ignore TRUE TRUE FALSE TRUE
|
|
|
|
|
*/
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2020-02-22 18:14:58 -05:00
|
|
|
void
|
|
|
|
|
UDynamicMeshSculptTool::ConfigureRemesher(FSubRegionRemesher& Remesher)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-02-22 18:14:58 -05:00
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
|
|
|
|
|
2019-10-18 13:04:19 -04:00
|
|
|
double TargetEdgeLength = RemeshProperties->RelativeSize * InitialEdgeLength;
|
|
|
|
|
Remesher.SetTargetEdgeLength(TargetEdgeLength);
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
double UseSmoothing = RemeshProperties->SmoothingStrength * 0.25;
|
2019-10-18 13:04:19 -04:00
|
|
|
Remesher.SmoothSpeedT = UseSmoothing;
|
|
|
|
|
|
|
|
|
|
// this is a temporary tweak for Pinch brush. Remesh params should be per-brush!
|
2019-10-01 20:41:42 -04:00
|
|
|
if (SculptProperties->PrimaryBrushType == EDynamicMeshSculptBrushType::Pinch && bSmoothing == false)
|
|
|
|
|
{
|
2019-10-18 13:04:19 -04:00
|
|
|
Remesher.MinEdgeLength = TargetEdgeLength * 0.1;
|
|
|
|
|
|
2020-02-22 18:14:58 -05:00
|
|
|
Remesher.CustomSmoothSpeedF = [this, UseSmoothing](const FDynamicMesh3& Mesh, int vID)
|
2019-10-18 13:04:19 -04:00
|
|
|
{
|
|
|
|
|
FVector3d Pos = Mesh.GetVertex(vID);
|
|
|
|
|
double Falloff = CalculateBrushFalloff(Pos.Distance((FVector3d)LastBrushPosLocal));
|
|
|
|
|
return (1.0f - Falloff) * UseSmoothing;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tweak remesh params for Smooth brush
|
2020-01-27 20:11:15 -05:00
|
|
|
if (bSmoothing && SculptProperties->bSmoothBrushRemeshes == false)
|
2019-10-18 13:04:19 -04:00
|
|
|
{
|
|
|
|
|
Remesher.MaxEdgeLength = 2*InitialEdgeLength;
|
|
|
|
|
Remesher.MinEdgeLength = InitialEdgeLength * 0.01;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Remesher.SmoothType = (SculptProperties->bPreserveUVFlow) ?
|
|
|
|
|
FRemesher::ESmoothTypes::MeanValue : FRemesher::ESmoothTypes::Uniform;
|
|
|
|
|
Remesher.bEnableCollapses = RemeshProperties->bCollapses;
|
|
|
|
|
Remesher.bEnableFlips = RemeshProperties->bFlips;
|
|
|
|
|
Remesher.bEnableSplits = RemeshProperties->bSplits;
|
|
|
|
|
Remesher.bPreventNormalFlips = RemeshProperties->bPreventNormalFlips;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_Setup);
|
|
|
|
|
for (int VertIdx : VertexROI)
|
|
|
|
|
{
|
|
|
|
|
Remesher.VertexROI.Add(VertIdx);
|
|
|
|
|
}
|
|
|
|
|
Remesher.InitializeFromVertexROI();
|
|
|
|
|
Remesher.UpdateROI(); // required to use roi in constraints fn below
|
|
|
|
|
Octree->RemoveTriangles(Remesher.GetCurrentTriangleROI());
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
FMeshConstraints Constraints;
|
2019-10-01 20:41:42 -04:00
|
|
|
bool bConstraintAllowSplits = true;
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_Constraints);
|
|
|
|
|
|
|
|
|
|
// TODO: only constrain in ROI. This is quite difficult to do externally because we need to update based on
|
|
|
|
|
// the changing triangle set in Remesher. Perhaps FSubRegionRemesher should update the constraints itself?
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
FMeshConstraintsUtil::ConstrainAllBoundariesAndSeams(Constraints, *Mesh,
|
|
|
|
|
(EEdgeRefineFlags)RemeshProperties->MeshBoundaryConstraint,
|
|
|
|
|
(EEdgeRefineFlags)RemeshProperties->GroupBoundaryConstraint,
|
|
|
|
|
(EEdgeRefineFlags)RemeshProperties->MaterialBoundaryConstraint,
|
|
|
|
|
bConstraintAllowSplits, !RemeshProperties->bPreserveSharpEdges);
|
|
|
|
|
if ( Constraints.HasConstraints() )
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
Remesher.SetExternalConstraints(MoveTemp(Constraints));
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-27 20:11:15 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
void UDynamicMeshSculptTool::RemeshROIPass()
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
2020-02-22 18:14:58 -05:00
|
|
|
FSubRegionRemesher Remesher(Mesh);
|
|
|
|
|
ConfigureRemesher(Remesher);
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
bool bIsUniformSmooth = (Remesher.SmoothType == FRemesher::ESmoothTypes::Uniform);
|
2019-10-01 20:41:42 -04:00
|
|
|
for (int k = 0; k < 5; ++k)
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
if ( ( bIsUniformSmooth == false ) && ( k > 1 ) )
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
Remesher.bEnableFlips = false;
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_RemeshROIUpdate);
|
|
|
|
|
|
|
|
|
|
Remesher.UpdateROI();
|
|
|
|
|
|
|
|
|
|
if (ActiveMeshChange != nullptr)
|
|
|
|
|
{
|
|
|
|
|
Remesher.SaveActiveROI(ActiveMeshChange);
|
|
|
|
|
//ActiveMeshChange->VerifySaveState(); // useful for debugging
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Remesher.BeginTrackRemovedTrisInPass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_RemeshPass);
|
|
|
|
|
Remesher.BasicRemeshPass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_PassOctreeUpdate);
|
|
|
|
|
const TSet<int32>& TrisRemovedInPass = Remesher.EndTrackRemovedTrisInPass();
|
|
|
|
|
Octree->RemoveTriangles(TrisRemovedInPass);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//UE_LOG(LogTemp, Warning, TEXT("Triangle Count %d after update"), Mesh->TriangleCount());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_SculptTool_Remesh_Finish);
|
|
|
|
|
|
|
|
|
|
// reinsert new ROI into octree
|
|
|
|
|
Octree->ReinsertTriangles(Remesher.GetCurrentTriangleROI());
|
|
|
|
|
|
|
|
|
|
//Octree->CheckValidity(EValidityCheckFailMode::Check, false, true);
|
|
|
|
|
|
|
|
|
|
UpdateROI(LastBrushPosLocal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::RecalculateNormals_PerVertex()
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
|
|
|
|
|
|
|
|
|
int MaxVertexID = Mesh->MaxVertexID();
|
|
|
|
|
if (NormalsVertexFlags.Num() < MaxVertexID)
|
|
|
|
|
{
|
|
|
|
|
NormalsVertexFlags.Init(false, MaxVertexID * 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Normals_Collect);
|
|
|
|
|
|
|
|
|
|
TrianglesBuffer.Reset();
|
|
|
|
|
NormalsBuffer.Reset();
|
|
|
|
|
Octree->RangeQuery(Octree->ModifiedBounds, TrianglesBuffer);
|
|
|
|
|
for (int TriangleID : TrianglesBuffer)
|
|
|
|
|
{
|
|
|
|
|
FIndex3i TriV = Mesh->GetTriangle(TriangleID);
|
|
|
|
|
for (int j = 0; j < 3; ++j)
|
|
|
|
|
{
|
|
|
|
|
int vid = TriV[j];
|
|
|
|
|
if (NormalsVertexFlags[vid] == false)
|
|
|
|
|
{
|
|
|
|
|
NormalsBuffer.Add(vid);
|
|
|
|
|
NormalsVertexFlags[vid] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//UE_LOG(LogTemp, Warning, TEXT("Computing %d normals"), NormalsBuffer.Num());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Normals_Compute);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NormalsBuffer.Num(), [&](int k) {
|
|
|
|
|
int vid = NormalsBuffer[k];
|
|
|
|
|
FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid);
|
|
|
|
|
Mesh->SetVertexNormal(vid, (FVector3f)NewNormal);
|
|
|
|
|
NormalsVertexFlags[vid] = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::RecalculateNormals_Overlay()
|
|
|
|
|
{
|
|
|
|
|
FDynamicMesh3* Mesh = DynamicMeshComponent->GetMesh();
|
|
|
|
|
FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr;
|
|
|
|
|
check(Normals != nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FDynamicMeshOctree3* Octree = DynamicMeshComponent->GetOctree();
|
|
|
|
|
|
|
|
|
|
int MaxElementID = Normals->MaxElementID();
|
|
|
|
|
if (NormalsVertexFlags.Num() < MaxElementID)
|
|
|
|
|
{
|
|
|
|
|
NormalsVertexFlags.Init(false, MaxElementID * 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Normals_Collect);
|
|
|
|
|
|
|
|
|
|
TrianglesBuffer.Reset();
|
|
|
|
|
NormalsBuffer.Reset();
|
|
|
|
|
Octree->RangeQuery(Octree->ModifiedBounds, TrianglesBuffer);
|
|
|
|
|
for (int TriangleID : TrianglesBuffer)
|
|
|
|
|
{
|
|
|
|
|
FIndex3i TriElems = Normals->GetTriangle(TriangleID);
|
|
|
|
|
for (int j = 0; j < 3; ++j)
|
|
|
|
|
{
|
|
|
|
|
int elemid = TriElems[j];
|
|
|
|
|
if (NormalsVertexFlags[elemid] == false)
|
|
|
|
|
{
|
|
|
|
|
NormalsBuffer.Add(elemid);
|
|
|
|
|
NormalsVertexFlags[elemid] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//UE_LOG(LogTemp, Warning, TEXT("Computing %d normals"), NormalsBuffer.Num());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER(SculptTool_Normals_Compute);
|
|
|
|
|
|
|
|
|
|
ParallelFor(NormalsBuffer.Num(), [&](int k) {
|
|
|
|
|
int elemid = NormalsBuffer[k];
|
|
|
|
|
FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid);
|
|
|
|
|
Normals->SetElement(elemid, (FVector3f)NewNormal);
|
|
|
|
|
NormalsVertexFlags[elemid] = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::UpdateTarget()
|
|
|
|
|
{
|
|
|
|
|
if (SculptProperties != nullptr )
|
|
|
|
|
{
|
|
|
|
|
bCachedFreezeTarget = SculptProperties->bFreezeTarget;
|
|
|
|
|
if (SculptProperties->bFreezeTarget)
|
|
|
|
|
{
|
|
|
|
|
return; // do not update frozen target
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BrushTargetMesh.Copy(*DynamicMeshComponent->GetMesh(), false, false, false, false);
|
|
|
|
|
BrushTargetMeshSpatial.SetMesh(&BrushTargetMesh, true);
|
|
|
|
|
|
|
|
|
|
BrushTargetNormals.SetMesh(&BrushTargetMesh);
|
|
|
|
|
BrushTargetNormals.ComputeVertexNormals();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UDynamicMeshSculptTool::GetTargetMeshNearest(const FVector3d& Position, double SearchRadius, FVector3d& TargetPosOut, FVector3d& TargetNormalOut)
|
|
|
|
|
{
|
|
|
|
|
double fDistSqr;
|
|
|
|
|
int NearTID = BrushTargetMeshSpatial.FindNearestTriangle(Position, fDistSqr, SearchRadius);
|
|
|
|
|
if (NearTID <= 0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
FTriangle3d Triangle;
|
|
|
|
|
BrushTargetMesh.GetTriVertices(NearTID, Triangle.V[0], Triangle.V[1], Triangle.V[2]);
|
|
|
|
|
FDistPoint3Triangle3d Query(Position, Triangle);
|
|
|
|
|
Query.Get();
|
|
|
|
|
FIndex3i Tri = BrushTargetMesh.GetTriangle(NearTID);
|
|
|
|
|
TargetNormalOut =
|
|
|
|
|
Query.TriangleBaryCoords.X*BrushTargetNormals[Tri.A]
|
|
|
|
|
+ Query.TriangleBaryCoords.Y*BrushTargetNormals[Tri.B]
|
|
|
|
|
+ Query.TriangleBaryCoords.Z*BrushTargetNormals[Tri.C];
|
|
|
|
|
TargetNormalOut.Normalize();
|
|
|
|
|
TargetPosOut = Query.ClosestTrianglePoint;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double UDynamicMeshSculptTool::EstimateIntialSafeTargetLength(const FDynamicMesh3& Mesh, int MinTargetTriCount)
|
|
|
|
|
{
|
|
|
|
|
double AreaSum = 0;
|
|
|
|
|
for (int tid : Mesh.TriangleIndicesItr())
|
|
|
|
|
{
|
|
|
|
|
AreaSum += Mesh.GetTriArea(tid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TriCount = Mesh.TriangleCount();
|
|
|
|
|
double TargetTriArea = 1.0;
|
|
|
|
|
if (TriCount < MinTargetTriCount)
|
|
|
|
|
{
|
|
|
|
|
TargetTriArea = AreaSum / (double)MinTargetTriCount;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetTriArea = AreaSum / (double)TriCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double EdgeLen = TriangleUtil::EquilateralEdgeLengthForArea(TargetTriArea);
|
|
|
|
|
return (double)FMath::RoundToInt(EdgeLen*100.0) / 100.0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 14:46:56 -04:00
|
|
|
UPreviewMesh* UDynamicMeshSculptTool::MakeDefaultSphereMesh(UObject* Parent, UWorld* World, int Resolution /*= 32*/)
|
|
|
|
|
{
|
|
|
|
|
UPreviewMesh* SphereMesh = NewObject<UPreviewMesh>(Parent);
|
|
|
|
|
SphereMesh->CreateInWorld(World, FTransform::Identity);
|
|
|
|
|
FSphereGenerator SphereGen;
|
|
|
|
|
SphereGen.NumPhi = SphereGen.NumTheta = Resolution;
|
|
|
|
|
SphereGen.Generate();
|
|
|
|
|
FDynamicMesh3 Mesh(&SphereGen);
|
|
|
|
|
SphereMesh->UpdatePreview(&Mesh);
|
|
|
|
|
SphereMesh->SetMaterial(ToolSetupUtil::GetDefaultBrushVolumeMaterial(nullptr));
|
|
|
|
|
return SphereMesh;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
void UDynamicMeshSculptTool::IncreaseBrushRadiusAction()
|
|
|
|
|
{
|
|
|
|
|
BrushProperties->BrushSize = FMath::Clamp(BrushProperties->BrushSize + 0.025f, 0.0f, 1.0f);
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::DecreaseBrushRadiusAction()
|
|
|
|
|
{
|
|
|
|
|
BrushProperties->BrushSize = FMath::Clamp(BrushProperties->BrushSize - 0.025f, 0.0f, 1.0f);
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::IncreaseBrushRadiusSmallStepAction()
|
|
|
|
|
{
|
|
|
|
|
BrushProperties->BrushSize = FMath::Clamp(BrushProperties->BrushSize + 0.005f, 0.0f, 1.0f);
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::DecreaseBrushRadiusSmallStepAction()
|
|
|
|
|
{
|
|
|
|
|
BrushProperties->BrushSize = FMath::Clamp(BrushProperties->BrushSize - 0.005f, 0.0f, 1.0f);
|
|
|
|
|
CalculateBrushRadius();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::IncreaseBrushSpeedAction()
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
SculptProperties->PrimaryBrushSpeed = FMath::Clamp(SculptProperties->PrimaryBrushSpeed + 0.05f, 0.0f, 1.0f);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::DecreaseBrushSpeedAction()
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
SculptProperties->PrimaryBrushSpeed = FMath::Clamp(SculptProperties->PrimaryBrushSpeed - 0.05f, 0.0f, 1.0f);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::NextBrushModeAction()
|
|
|
|
|
{
|
|
|
|
|
uint8 LastMode = (uint8)EDynamicMeshSculptBrushType::LastValue;
|
|
|
|
|
SculptProperties->PrimaryBrushType = (EDynamicMeshSculptBrushType)(((uint8)SculptProperties->PrimaryBrushType + 1) % LastMode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::PreviousBrushModeAction()
|
|
|
|
|
{
|
|
|
|
|
uint8 LastMode = (uint8)EDynamicMeshSculptBrushType::LastValue;
|
|
|
|
|
uint8 CurMode = (uint8)SculptProperties->PrimaryBrushType;
|
|
|
|
|
if (CurMode == 0)
|
|
|
|
|
{
|
|
|
|
|
SculptProperties->PrimaryBrushType = (EDynamicMeshSculptBrushType)((uint8)LastMode - 1);
|
2020-01-27 20:11:15 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SculptProperties->PrimaryBrushType = (EDynamicMeshSculptBrushType)((uint8)CurMode - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::NextHistoryBrushModeAction()
|
|
|
|
|
{
|
|
|
|
|
int MaxHistory = BrushTypeHistory.Num() - 1;
|
|
|
|
|
if (BrushTypeHistoryIndex < MaxHistory)
|
|
|
|
|
{
|
|
|
|
|
BrushTypeHistoryIndex++;
|
|
|
|
|
SculptProperties->PrimaryBrushType = BrushTypeHistory[BrushTypeHistoryIndex];
|
|
|
|
|
LastStampType = SculptProperties->PrimaryBrushType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::PreviousHistoryBrushModeAction()
|
|
|
|
|
{
|
|
|
|
|
if (BrushTypeHistoryIndex > 0)
|
|
|
|
|
{
|
|
|
|
|
BrushTypeHistoryIndex--;
|
|
|
|
|
SculptProperties->PrimaryBrushType = BrushTypeHistory[BrushTypeHistoryIndex];
|
|
|
|
|
LastStampType = SculptProperties->PrimaryBrushType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::RegisterActions(FInteractiveToolActionSet& ActionSet)
|
|
|
|
|
{
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::IncreaseBrushSize,
|
2020-01-27 20:11:15 -05:00
|
|
|
TEXT("SculptIncreaseRadius"),
|
2019-10-01 20:41:42 -04:00
|
|
|
LOCTEXT("SculptIncreaseRadius", "Increase Sculpt Radius"),
|
|
|
|
|
LOCTEXT("SculptIncreaseRadiusTooltip", "Increase radius of sculpting brush"),
|
|
|
|
|
EModifierKey::None, EKeys::RightBracket,
|
|
|
|
|
[this]() { IncreaseBrushRadiusAction(); } );
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::DecreaseBrushSize,
|
2020-01-27 20:11:15 -05:00
|
|
|
TEXT("SculptDecreaseRadius"),
|
2019-10-01 20:41:42 -04:00
|
|
|
LOCTEXT("SculptDecreaseRadius", "Decrease Sculpt Radius"),
|
|
|
|
|
LOCTEXT("SculptDecreaseRadiusTooltip", "Decrease radius of sculpting brush"),
|
|
|
|
|
EModifierKey::None, EKeys::LeftBracket,
|
|
|
|
|
[this]() { DecreaseBrushRadiusAction(); } );
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID+1,
|
|
|
|
|
TEXT("NextBrushMode"),
|
|
|
|
|
LOCTEXT("SculptNextBrushMode", "Next Brush Type"),
|
|
|
|
|
LOCTEXT("SculptNextBrushModeTooltip", "Cycle to next Brush Type"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::A,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { NextBrushModeAction(); });
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID+2,
|
|
|
|
|
TEXT("PreviousBrushMode"),
|
|
|
|
|
LOCTEXT("SculptPreviousBrushMode", "Previous Brush Type"),
|
|
|
|
|
LOCTEXT("SculptPreviousBrushModeTooltip", "Cycle to previous Brush Type"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::Q,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { PreviousBrushModeAction(); });
|
|
|
|
|
|
2019-11-08 10:47:08 -05:00
|
|
|
//ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 10,
|
|
|
|
|
// TEXT("NextBrushHistoryState"),
|
|
|
|
|
// LOCTEXT("SculptNextBrushHistoryState", "Next Brush History State"),
|
|
|
|
|
// LOCTEXT("SculptSculptNextBrushHistoryStateTooltip", "Cycle to next Brush History state"),
|
|
|
|
|
// EModifierKey::Shift, EKeys::Q,
|
|
|
|
|
// [this]() { NextHistoryBrushModeAction(); });
|
2019-10-01 20:41:42 -04:00
|
|
|
|
2019-11-08 10:47:08 -05:00
|
|
|
//ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 11,
|
|
|
|
|
// TEXT("PreviousBrushHistoryState"),
|
|
|
|
|
// LOCTEXT("SculptPreviousBrushHistoryState", "Previous Brush History State"),
|
|
|
|
|
// LOCTEXT("SculptPreviousBrushHistoryStateTooltip", "Cycle to previous Brush History state"),
|
|
|
|
|
// EModifierKey::Shift, EKeys::A,
|
|
|
|
|
// [this]() { PreviousHistoryBrushModeAction(); });
|
2019-10-01 20:41:42 -04:00
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 50,
|
|
|
|
|
TEXT("SculptIncreaseSize"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSize", "Increase Size"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSizeTooltip", "Increase Brush Size"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::D,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { IncreaseBrushRadiusAction(); });
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 51,
|
|
|
|
|
TEXT("SculptDecreaseSize"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSize", "Decrease Size"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSizeTooltip", "Decrease Brush Size"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::S,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { DecreaseBrushRadiusAction(); });
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 52,
|
|
|
|
|
TEXT("SculptIncreaseSizeSmallStep"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSize", "Increase Size"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSizeTooltip", "Increase Brush Size"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift | EModifierKey::Control, EKeys::D,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { IncreaseBrushRadiusSmallStepAction(); });
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 53,
|
|
|
|
|
TEXT("SculptDecreaseSizeSmallStemp"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSize", "Decrease Size"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSizeTooltip", "Decrease Brush Size"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift | EModifierKey::Control, EKeys::S,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { DecreaseBrushRadiusSmallStepAction(); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 60,
|
|
|
|
|
TEXT("SculptIncreaseSpeed"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSpeed", "Increase Speed"),
|
|
|
|
|
LOCTEXT("SculptIncreaseSpeedTooltip", "Increase Brush Speed"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::E,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { IncreaseBrushSpeedAction(); });
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 61,
|
|
|
|
|
TEXT("SculptDecreaseSpeed"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSpeed", "Decrease Speed"),
|
|
|
|
|
LOCTEXT("SculptDecreaseSpeedTooltip", "Decrease Brush Speed"),
|
2019-11-08 10:47:08 -05:00
|
|
|
EModifierKey::Shift, EKeys::W,
|
2019-10-01 20:41:42 -04:00
|
|
|
[this]() { DecreaseBrushSpeedAction(); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::ToggleWireframe,
|
|
|
|
|
TEXT("ToggleWireframe"),
|
|
|
|
|
LOCTEXT("ToggleWireframe", "Toggle Wireframe"),
|
|
|
|
|
LOCTEXT("ToggleWireframeTooltip", "Toggle visibility of wireframe overlay"),
|
|
|
|
|
EModifierKey::Alt, EKeys::W,
|
|
|
|
|
[this]() { ViewProperties->bShowWireframe = !ViewProperties->bShowWireframe; });
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
|
|
|
|
|
ActionSet.RegisterAction(this, (int32)EStandardToolActions::BaseClientDefinedActionID + 100,
|
|
|
|
|
TEXT("SetFixedSculptPlane"),
|
|
|
|
|
LOCTEXT("SetFixedSculptPlane", "Set Fixed Sculpt Plane"),
|
|
|
|
|
LOCTEXT("SetFixedSculptPlaneTooltip", "Set position of fixed sculpt plane"),
|
|
|
|
|
EModifierKey::None, EKeys::P,
|
|
|
|
|
[this]() { bPendingSetFixedPlanePosition = true; });
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Change Tracking
|
|
|
|
|
//
|
|
|
|
|
void UDynamicMeshSculptTool::BeginChange(bool bIsVertexChange)
|
|
|
|
|
{
|
|
|
|
|
check(ActiveVertexChange == nullptr);
|
|
|
|
|
check(ActiveMeshChange == nullptr);
|
2020-01-27 20:11:15 -05:00
|
|
|
if (bIsVertexChange)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
|
|
|
|
ActiveVertexChange = new FMeshVertexChangeBuilder();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ActiveMeshChange = new FDynamicMeshChangeTracker(DynamicMeshComponent->GetMesh());
|
|
|
|
|
ActiveMeshChange->BeginChange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::EndChange()
|
|
|
|
|
{
|
|
|
|
|
if (ActiveVertexChange != nullptr)
|
|
|
|
|
{
|
|
|
|
|
GetToolManager()->EmitObjectChange(DynamicMeshComponent, MoveTemp(ActiveVertexChange->Change), LOCTEXT("VertexSculptChange", "Brush Stroke"));
|
|
|
|
|
|
|
|
|
|
delete ActiveVertexChange;
|
|
|
|
|
ActiveVertexChange = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ActiveMeshChange != nullptr)
|
|
|
|
|
{
|
|
|
|
|
FMeshChange* NewMeshChange = new FMeshChange();
|
|
|
|
|
NewMeshChange->DynamicMeshChange = ActiveMeshChange->EndChange();
|
|
|
|
|
//NewMeshChange->DynamicMeshChange->CheckValidity();
|
|
|
|
|
TUniquePtr<FMeshChange> NewChange(NewMeshChange);
|
|
|
|
|
GetToolManager()->EmitObjectChange(DynamicMeshComponent, MoveTemp(NewChange), LOCTEXT("MeshSculptChange", "Brush Stroke"));
|
|
|
|
|
delete ActiveMeshChange;
|
|
|
|
|
ActiveMeshChange = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::SaveActiveROI()
|
|
|
|
|
{
|
|
|
|
|
if (ActiveMeshChange != nullptr)
|
|
|
|
|
{
|
|
|
|
|
for (int vid : VertexROI)
|
|
|
|
|
{
|
|
|
|
|
ActiveMeshChange->SaveVertex(vid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::UpdateSavedVertex(int vid, const FVector3d& OldPosition, const FVector3d& NewPosition)
|
|
|
|
|
{
|
|
|
|
|
if (ActiveVertexChange != nullptr)
|
|
|
|
|
{
|
|
|
|
|
UpdateSavedVertexLock.Lock();
|
|
|
|
|
ActiveVertexChange->UpdateVertex(vid, OldPosition, NewPosition);
|
|
|
|
|
UpdateSavedVertexLock.Unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::UpdateMaterialMode(EMeshEditingMaterialModes MaterialMode)
|
|
|
|
|
{
|
|
|
|
|
if (MaterialMode == EMeshEditingMaterialModes::ExistingMaterial)
|
|
|
|
|
{
|
2019-12-19 18:07:47 -05:00
|
|
|
DynamicMeshComponent->ClearOverrideRenderMaterial();
|
2019-10-01 20:41:42 -04:00
|
|
|
DynamicMeshComponent->bCastDynamicShadow = ComponentTarget->GetOwnerComponent()->bCastDynamicShadow;
|
2020-01-27 20:11:15 -05:00
|
|
|
}
|
2019-10-01 20:41:42 -04:00
|
|
|
else if (MaterialMode == EMeshEditingMaterialModes::MeshFocusMaterial)
|
2020-01-27 20:11:15 -05:00
|
|
|
{
|
2019-12-19 18:07:47 -05:00
|
|
|
UMaterialInterface* SculptMaterial = ToolSetupUtil::GetSculptMaterial1(GetToolManager());
|
|
|
|
|
if (SculptMaterial != nullptr)
|
2019-10-01 20:41:42 -04:00
|
|
|
{
|
2019-12-19 18:07:47 -05:00
|
|
|
DynamicMeshComponent->SetOverrideRenderMaterial(SculptMaterial);
|
2019-10-01 20:41:42 -04:00
|
|
|
}
|
|
|
|
|
DynamicMeshComponent->bCastDynamicShadow = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 17:20:52 -05:00
|
|
|
void UDynamicMeshSculptTool::SetFixedSculptPlaneFromWorldPos(const FVector& Position)
|
|
|
|
|
{
|
|
|
|
|
UpdateFixedSculptPlanePosition(Position);
|
|
|
|
|
if (PlaneTransformGizmo != nullptr)
|
|
|
|
|
{
|
2019-11-05 20:26:40 -05:00
|
|
|
PlaneTransformGizmo->SetNewGizmoTransform(FTransform(DrawPlaneOrientation, GizmoProperties->Position));
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::PlaneTransformChanged(UTransformProxy* Proxy, FTransform Transform)
|
|
|
|
|
{
|
|
|
|
|
DrawPlaneOrientation = Transform.GetRotation();
|
|
|
|
|
UpdateFixedSculptPlanePosition(Transform.GetLocation());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::UpdateFixedSculptPlanePosition(const FVector& Position)
|
|
|
|
|
{
|
2019-11-05 20:26:40 -05:00
|
|
|
GizmoProperties->Position = Position;
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UDynamicMeshSculptTool::UpdateFixedPlaneGizmoVisibility(bool bVisible)
|
|
|
|
|
{
|
|
|
|
|
if (bVisible == false)
|
|
|
|
|
{
|
|
|
|
|
if (PlaneTransformGizmo != nullptr)
|
|
|
|
|
{
|
|
|
|
|
GetToolManager()->GetPairedGizmoManager()->DestroyGizmo(PlaneTransformGizmo);
|
|
|
|
|
PlaneTransformGizmo = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (PlaneTransformGizmo == nullptr)
|
|
|
|
|
{
|
2020-01-27 20:11:15 -05:00
|
|
|
PlaneTransformGizmo = GetToolManager()->GetPairedGizmoManager()->CreateCustomTransformGizmo(
|
|
|
|
|
ETransformGizmoSubElements::StandardTranslateRotate, this);
|
2019-11-05 17:20:52 -05:00
|
|
|
PlaneTransformGizmo->SetActiveTarget(PlaneTransformProxy, GetToolManager());
|
2019-11-05 20:26:40 -05:00
|
|
|
PlaneTransformGizmo->SetNewGizmoTransform(FTransform(DrawPlaneOrientation, GizmoProperties->Position));
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
2019-11-15 12:53:29 -05:00
|
|
|
|
|
|
|
|
PlaneTransformGizmo->bSnapToWorldGrid = GizmoProperties->bSnapToGrid;
|
2019-11-05 17:20:52 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-01 20:41:42 -04:00
|
|
|
#undef LOCTEXT_NAMESPACE
|