Files
Machinist_700km/Plugins/HoudiniEngine/Source/HoudiniEngineEditor/Private/HoudiniSplineComponentVisualizer.cpp
T
Andron666 9c38e93fa4 part7
2022-12-05 20:31:35 +05:00

1022 lines
34 KiB
C++

/*
* Copyright (c) <2021> Side Effects Software Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HoudiniSplineComponentVisualizer.h"
#include "ActorEditorUtils.h"
#include "HoudiniEngineEditor.h"
#include "HoudiniEngineEditorPrivatePCH.h"
#include "HoudiniApi.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniSplineComponent.h"
#include "HoudiniInputObject.h"
#include "HoudiniInput.h"
#include "HoudiniEngineStyle.h"
#include "HoudiniEngineUtils.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#include "ComponentVisualizerManager.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ScopedTransaction.h"
#include "EditorViewportClient.h"
#include "Engine/Selection.h"
#include "HModel.h"
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
IMPLEMENT_HIT_PROXY(HHoudiniSplineVisProxy, HComponentVisProxy);
IMPLEMENT_HIT_PROXY(HHoudiniSplineControlPointVisProxy, HHoudiniSplineVisProxy);
IMPLEMENT_HIT_PROXY(HHoudiniSplineCurveSegmentVisProxy, HHoudiniSplineVisProxy);
FHoudiniSplineComponentVisualizerCommands::FHoudiniSplineComponentVisualizerCommands()
: TCommands< FHoudiniSplineComponentVisualizerCommands >(
"HoudiniSplineComponentVisualizer",
LOCTEXT("HoudiniSplineComponentVisualizer", "Houdini Spline Component Visualizer"),
NAME_None,
FEditorStyle::GetStyleSetName())
{}
void
FHoudiniSplineComponentVisualizerCommands::RegisterCommands()
{
UI_COMMAND(
CommandAddControlPoint, "Add Control Point", "Add control points.",
EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(
CommandDuplicateControlPoint, "Duplicate Control Point", "Duplicate control points.",
EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(
CommandDeleteControlPoint, "Delete Control Point", "delete control points.",
EUserInterfaceActionType::Button, FInputChord(EKeys::Delete));
UI_COMMAND(CommandDeselectAllControlPoints, "Deselect All", "Deselect all control points.",
EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(CommandInsertControlPoint, "Insert Control Point", "Insert a control point on curve.",
EUserInterfaceActionType::Button, FInputChord());
}
FHoudiniSplineComponentVisualizer::FHoudiniSplineComponentVisualizer()
:FComponentVisualizer()
,bAllowDuplication(false)
,EditedCurveSegmentIndex(-1)
,CachedRotation(FQuat::Identity)
,CachedScale3D(FVector::OneVector)
,bMovingPoints(false)
,bInsertingOnCurveControlPoints(false)
,bRecordingMovingPoints(false)
{
FHoudiniSplineComponentVisualizerCommands::Register();
VisualizerActions = MakeShareable(new FUICommandList);
}
void
FHoudiniSplineComponentVisualizer::OnRegister()
{
HOUDINI_LOG_MESSAGE(TEXT("Houdini Spline Component Visualizer Registered!"));
const auto & Commands = FHoudiniSplineComponentVisualizerCommands::Get();
VisualizerActions->MapAction(
Commands.CommandAddControlPoint,
FExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::OnAddControlPoint),
FCanExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::IsAddControlPointValid));
VisualizerActions->MapAction(
Commands.CommandDuplicateControlPoint,
FExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::OnDuplicateControlPoint),
FCanExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::IsDuplicateControlPointValid));
VisualizerActions->MapAction(
Commands.CommandDeleteControlPoint,
FExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::OnDeleteControlPoint),
FCanExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::IsDeleteControlPointValid));
VisualizerActions->MapAction(Commands.CommandDeselectAllControlPoints,
FExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::OnDeselectAllControlPoints),
FCanExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::IsDeselectAllControlPointsValid));
VisualizerActions->MapAction(Commands.CommandInsertControlPoint,
FExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::OnInsertControlPoint),
FCanExecuteAction::CreateSP(this, &FHoudiniSplineComponentVisualizer::IsInsertControlPointValid));
}
void
FHoudiniSplineComponentVisualizer::DrawVisualization(
const UActorComponent * Component,
const FSceneView * View,
FPrimitiveDrawInterface * PDI)
{
const UHoudiniSplineComponent * HoudiniSplineComponent = Cast< const UHoudiniSplineComponent >(Component);
if (!IsValid(HoudiniSplineComponent)
|| !PDI
|| !HoudiniSplineComponent->IsVisible()
|| !HoudiniSplineComponent->IsHoudiniSplineVisible())
return;
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
// Note: Undo a transaction clears the active visualizer in ComponnetVisMangaer, which is private to Visualizer manager.
// HandleProxyForComponentVis() sets the active visualizer. So the selection will be lost after undo.
// A Way to bypass this annoying UE4 implementation:
// If the drawing spline is the one being edited and an undo just happened,
// force to trigger a 'bubble' hit proxy to re-activate the visualizer.
if (HoudiniSplineComponent == EditedHoudiniSplineComponent && EditedHoudiniSplineComponent->bPostUndo)
{
EditedHoudiniSplineComponent->bPostUndo = false;
FEditorViewportClient * FoundViewportClient = FindViewportClient(EditedHoudiniSplineComponent, View);
HComponentVisProxy * BubbleComponentHitProxy = new HComponentVisProxy(EditedHoudiniSplineComponent);
if (FoundViewportClient && BubbleComponentHitProxy)
{
FViewportClick BubbleClick(View, FoundViewportClient, FKey(), EInputEvent::IE_Axis, 0, 0);
GUnrealEd->ComponentVisManager.HandleProxyForComponentVis(FoundViewportClient, BubbleComponentHitProxy, BubbleClick);
}
}
static const FColor ColorNormal = FColor(255.f, 255.f, 255.f);
static const FColor ColorNormalHandleFirst(172.f, 255.f, 172.f);
static const FColor ColorNormalHandleSecond(254.f, 216.f, 177.f);
static const FColor ColorSelectedHandle(255.f, 0.f, 0.f);
static const FColor ColorSelectedHandleFirst(0.f, 192.f, 0.f);
static const FColor ColorSelectedHandleSecond(255.f, 159.f, 0.f);
static const float SizeGrabHandleSelected = 15.f;
static const float SizeGrabHandleNormalLarge = 18.f;
static const float SizeGrabHandleNormalSmall = 12.f;
FVector PreviousPosition;
if (HoudiniSplineComponent)
{
const FTransform & HoudiniSplineComponentTransform = HoudiniSplineComponent->GetComponentTransform();
const TArray< FVector > & DisplayPoints = HoudiniSplineComponent->DisplayPoints; // not used yet
const TArray< FTransform > & CurvePoints = HoudiniSplineComponent->CurvePoints;
// Draw display points (simply linearly connect the control points for temporary)
for (int32 Index = 0; Index < DisplayPoints.Num(); ++Index)
{
const FVector & CurrentPoint = DisplayPoints[Index];
// Fix incorrect scale when actor has been scaled
//FVector CurrentPosition = CurrentPoint + HoudiniSplineComponentTransform.GetLocation();
FVector CurrentPosition = HoudiniSplineComponentTransform.TransformPosition(CurrentPoint);
if (Index > 0)
{
// Add a hitproxy for the line segment
PDI->SetHitProxy(new HHoudiniSplineCurveSegmentVisProxy(HoudiniSplineComponent, Index));
// Draw a line connecting the previous point and the current point
PDI->DrawLine(PreviousPosition, CurrentPosition, ColorNormal, SDPG_Foreground);
PDI->SetHitProxy(nullptr);
}
PreviousPosition = CurrentPosition;
}
// Draw control points (do not draw control points if the curve is an output)
if (!HoudiniSplineComponent->bIsOutputCurve)
{
for (int32 Index = 0; Index < CurvePoints.Num(); ++Index)
{
const FVector & ControlPoint = HoudiniSplineComponentTransform.TransformPosition(CurvePoints[Index].GetLocation());
HHoudiniSplineControlPointVisProxy * HitProxy = new HHoudiniSplineControlPointVisProxy(HoudiniSplineComponent, Index);
PDI->SetHitProxy(HitProxy);
FColor DrawColor = ColorNormal;
float DrawSize = SizeGrabHandleNormalSmall;
if (Index == 0)
{
DrawColor = ColorNormalHandleFirst;
DrawSize = SizeGrabHandleNormalLarge;
}
if (Index == 1)
DrawColor = ColorNormalHandleSecond;
// If this is an point that being editted
if (EditedHoudiniSplineComponent == HoudiniSplineComponent && EditedHoudiniSplineComponent->EditedControlPointsIndexes.Contains(Index))
{
if (Index == 0)
{
DrawColor = ColorSelectedHandleFirst;
}
else if (Index == 1)
{
DrawColor = ColorSelectedHandleSecond;
DrawSize = SizeGrabHandleSelected;
}
else
{
DrawColor = ColorSelectedHandle;
DrawSize = SizeGrabHandleSelected;
}
}
PDI->DrawPoint(ControlPoint, DrawColor, DrawSize, SDPG_Foreground);
PDI->SetHitProxy(nullptr);
}
}
}
}
bool
FHoudiniSplineComponentVisualizer::VisProxyHandleClick(
FEditorViewportClient* InViewportClient,
HComponentVisProxy* VisProxy,
const FViewportClick& Click)
{
if (!InViewportClient || !VisProxy || !VisProxy->Component.IsValid())
return false;
const UHoudiniSplineComponent * HoudiniSplineComponent = CastChecked< const UHoudiniSplineComponent >(VisProxy->Component.Get());
AActor* OldSplineOwningActor = SplinePropertyPath.GetParentOwningActor();
SplinePropertyPath = FComponentPropertyPath(HoudiniSplineComponent);
AActor* NewSplineOwningActor = SplinePropertyPath.GetParentOwningActor();
if (!SplinePropertyPath.IsValid())
{
SplinePropertyPath.Reset();
return false;
}
if (OldSplineOwningActor != NewSplineOwningActor)
{
// Reset selection state if we are selecting a different actor to the one previously selected
EditedCurveSegmentIndex = INDEX_NONE;
}
// Note: This is for re-activating the component visualizer an undo.
// Return true if the hit proxy is a bubble (Neither HHoudiniSplineControlPointVisProxy nor HHoudiniSplineCurveSegmentVisProxy )
//
if (!VisProxy->IsA(HHoudiniSplineControlPointVisProxy::StaticGetType()) && !VisProxy->IsA(HHoudiniSplineCurveSegmentVisProxy::StaticGetType()))
return true;
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
EditedHoudiniSplineComponent = const_cast<UHoudiniSplineComponent *>(HoudiniSplineComponent);
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return false;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
bool editingCurve = false;
// If VisProxy is a HHoudiniSplineControlPointVisProxy
if (VisProxy->IsA(HHoudiniSplineControlPointVisProxy::StaticGetType()))
{
HHoudiniSplineControlPointVisProxy * ControlPointProxy = (HHoudiniSplineControlPointVisProxy*)VisProxy;
if (!ControlPointProxy)
return editingCurve;
editingCurve = true;
// Clear the edited curve segment if a control point is clicked.
EditedCurveSegmentIndex = -1;
if (Click.GetKey() != EKeys::LeftMouseButton)
return editingCurve;
if (InViewportClient->IsCtrlPressed())
{
if (EditedControlPointsIndexes.Contains(ControlPointProxy->ControlPointIndex))
{
EditedControlPointsIndexes.Remove(ControlPointProxy->ControlPointIndex);
}
else
{
EditedControlPointsIndexes.Add(ControlPointProxy->ControlPointIndex);
}
}
else
{
EditedControlPointsIndexes.Empty();
EditedControlPointsIndexes.Add(ControlPointProxy->ControlPointIndex);
}
}
// VisProxy is a HHoudiniSplineCurveSegmentProxy
else if (VisProxy->IsA(HHoudiniSplineCurveSegmentVisProxy::StaticGetType()))
{
//HHoudiniSplineCurveSegmentVisProxy * CurveSegmentProxy = Cast<HHoudiniSplineCurveSegmentVisProxy>(VisProxy);
HHoudiniSplineCurveSegmentVisProxy * CurveSegmentProxy = (HHoudiniSplineCurveSegmentVisProxy*)(VisProxy);
if (!CurveSegmentProxy)
return false;
editingCurve = true;
if (Click.GetKey() == EKeys::LeftMouseButton && InViewportClient->IsAltPressed() && EditedHoudiniSplineComponent)
{
// Continuesly (Alt) inserting on-curve control points is only valid with Breakpoints mode, otherwise it has to be on linear curve type.
if (EditedHoudiniSplineComponent->CurveType != EHoudiniCurveType::Polygon && EditedHoudiniSplineComponent->CurveMethod != EHoudiniCurveMethod::Breakpoints)
return editingCurve;
bInsertingOnCurveControlPoints = true;
editingCurve = true;
EditedControlPointsIndexes.Empty();
EditedCurveSegmentIndex = CurveSegmentProxy->DisplayPointIndex;
int32 InsertedIndex = OnInsertControlPointWithoutUpdate();
if (InsertedIndex < 0) return false;
EditedControlPointsIndexes.Add(InsertedIndex);
EditedCurveSegmentIndex = -1;
bInsertingOnCurveControlPoints = true;
RefreshViewport();
}
// Insert one on-curve control point.
else
{
EditedCurveSegmentIndex = CurveSegmentProxy->DisplayPointIndex;
return editingCurve;
}
}
return editingCurve;
}
bool
FHoudiniSplineComponentVisualizer::HandleInputKey(FEditorViewportClient * ViewportClient, FViewport * Viewport, FKey Key, EInputEvent Event)
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return false;
if (Key == EKeys::Enter)
{
EditedHoudiniSplineComponent->MarkChanged(true);
return true;
}
bool bHandled = false;
if (Key == EKeys::LeftMouseButton)
{
if (Event == IE_Pressed)
{
bMovingPoints = true; // Started moving points when the left mouse button is pressed
bAllowDuplication = true;
bRecordingMovingPoints = false;
}
if (Event == IE_Released)
{
bMovingPoints = false; // Stopped moving points when the left mouse button is released
bAllowDuplication = false;
if (bRecordingMovingPoints)
{
// Only mark the component as changed if a point was actually moved otherwise it will
// cook even if a point was selected.
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
}
bRecordingMovingPoints = false; // allow recording pt moving again
}
}
if (Key == EKeys::Delete)
{
if (Event == IE_Pressed) return true;
if (IsDeleteControlPointValid())
{
OnDeleteControlPoint();
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
return true;
}
}
if (Event == IE_Pressed && VisualizerActions)
{
if (FSlateApplication::IsInitialized())
bHandled = VisualizerActions->ProcessCommandBindings(Key, FSlateApplication::Get().GetModifierKeys(), false);
}
RefreshViewport();
return bHandled;
}
void
FHoudiniSplineComponentVisualizer::EndEditing()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return;
// Clear edited spline if the EndEditing() function is not called from postUndo
if (!EditedHoudiniSplineComponent->bPostUndo)
{
EditedHoudiniSplineComponent->EditedControlPointsIndexes.Empty();
EditedHoudiniSplineComponent = nullptr;
EditedCurveSegmentIndex = -1;
}
//RefreshViewport();
}
bool
FHoudiniSplineComponentVisualizer::GetWidgetLocation(
const FEditorViewportClient* ViewportClient,
FVector& OutLocation) const
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return false;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
if (EditedControlPointsIndexes.Num() <= 0)
return false;
const TArray<FTransform>& CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
// Set the widget location to the center of mass of the selected control points
int32 Sum = 0;
FVector CenterLocation = FVector::ZeroVector;
for (int32 EditedIdx = 0; EditedIdx < EditedControlPointsIndexes.Num(); EditedIdx++)
{
if (!CurvePoints.IsValidIndex(EditedIdx))
continue;
CenterLocation += CurvePoints[EditedControlPointsIndexes[EditedIdx]].GetLocation();
Sum++;
}
if(Sum > 0)
CenterLocation /= Sum;
OutLocation = EditedHoudiniSplineComponent->GetComponentTransform().TransformPosition(CenterLocation);
return true;
}
bool
FHoudiniSplineComponentVisualizer::IsVisualizingArchetype() const
{
UHoudiniSplineComponent* SplineComp = GetEditedHoudiniSplineComponent();
return (SplineComp && SplineComp->GetOwner() && FActorEditorUtils::IsAPreviewOrInactiveActor(SplineComp->GetOwner()));
}
bool
FHoudiniSplineComponentVisualizer::HandleInputDelta(
FEditorViewportClient* ViewportClient,
FViewport* Viewport,
FVector& DeltaTranslate,
FRotator& DeltaRotate,
FVector& DeltaScale)
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!ViewportClient || !EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return false;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
if (EditedControlPointsIndexes.Num() <= 0)
return false;
if (ViewportClient->IsAltPressed() && bAllowDuplication)
{
OnDuplicateControlPoint();
bAllowDuplication = false;
}
else
{
if (!bRecordingMovingPoints)
{
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniSplineComponentMovingPointsTransaction", "Houdini Spline Component: Moving curve points."),
EditedHoudiniSplineComponent->GetOuter(), true);
EditedHoudiniSplineComponent->Modify();
bRecordingMovingPoints = true;
}
}
TArray <FTransform> & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
const FTransform & HoudiniSplineComponentTransform = EditedHoudiniSplineComponent->GetComponentTransform();
for (int i = 0; i < EditedControlPointsIndexes.Num(); ++i )
{
FTransform CurrentPoint = EditedHoudiniSplineComponent->CurvePoints[EditedControlPointsIndexes[i]];
if (!DeltaTranslate.IsZero())
{
FVector OldWorldPosition = HoudiniSplineComponentTransform.TransformPosition(CurrentPoint.GetLocation());
FVector NewWorldPosition = OldWorldPosition + DeltaTranslate;
FVector NewLocalPosition = HoudiniSplineComponentTransform.InverseTransformPosition(NewWorldPosition);
CurrentPoint.SetLocation( NewLocalPosition );
}
if (!DeltaRotate.IsZero())
{
FQuat OldWorldRotation = HoudiniSplineComponentTransform.GetRotation() * CurrentPoint.GetRotation();
FQuat NewWorldRotation = DeltaRotate.Quaternion() * OldWorldRotation;
FQuat NewLocalRotation = HoudiniSplineComponentTransform.GetRotation().Inverse() * NewWorldRotation;
CurrentPoint.SetRotation(NewLocalRotation);
}
if (!DeltaScale.IsZero())
{
FVector NewScale = CurrentPoint.GetScale3D() * (FVector(1.f, 1.f, 1.f) + DeltaScale);
CurrentPoint.SetScale3D(NewScale);
}
EditedHoudiniSplineComponent->EditPointAtindex(CurrentPoint, EditedControlPointsIndexes[i]);
}
RefreshViewport();
return true;
}
TSharedPtr<SWidget>
FHoudiniSplineComponentVisualizer::GenerateContextMenu() const
{
FHoudiniEngineEditor& HoudiniEngineEditor = FHoudiniEngineEditor::Get();
FName StyleSetName = FHoudiniEngineStyle::GetStyleSetName();
FMenuBuilder MenuBuilder(true, VisualizerActions);
MenuBuilder.BeginSection("Houdini Spline actions");
// Create the context menu section
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (EditedHoudiniSplineComponent && !EditedHoudiniSplineComponent->IsPendingKill())
{
MenuBuilder.AddMenuEntry(
FHoudiniSplineComponentVisualizerCommands::Get().CommandAddControlPoint,
NAME_None, TAttribute<FText>(), TAttribute<FText>(),
FSlateIcon(StyleSetName, "HoudiniEngine.HoudiniEngineLogo"));
MenuBuilder.AddMenuEntry(
FHoudiniSplineComponentVisualizerCommands::Get().CommandDuplicateControlPoint,
NAME_None, TAttribute< FText >(), TAttribute< FText >(),
FSlateIcon(StyleSetName, "HoudiniEngine.HoudiniEngineLogo"));
MenuBuilder.AddMenuEntry(
FHoudiniSplineComponentVisualizerCommands::Get().CommandDeleteControlPoint,
NAME_None, TAttribute< FText >(), TAttribute< FText >(),
FSlateIcon(StyleSetName, "HoudiniEngine.HoudiniEngineLogo"));
MenuBuilder.AddMenuEntry(
FHoudiniSplineComponentVisualizerCommands::Get().CommandDeselectAllControlPoints,
NAME_None, TAttribute< FText >(), TAttribute< FText >(),
FSlateIcon(StyleSetName, "HoudiniEngine.HoudiniEngineLogo"));
MenuBuilder.AddMenuEntry(
FHoudiniSplineComponentVisualizerCommands::Get().CommandInsertControlPoint,
NAME_None, TAttribute< FText >(), TAttribute< FText >(),
FSlateIcon(StyleSetName, "HoudiniEngine.HoudiniEngineLogo"));
}
MenuBuilder.EndSection();
TSharedPtr<SWidget> MenuWidget = MenuBuilder.MakeWidget();
return MenuWidget;
}
// Used by alt-pressed on-curve control port insertion.
// We don't want it to be cooked before finishing editing.
// * Need to call WaitForHoudiniInputUpdate() after done.
int32
FHoudiniSplineComponentVisualizer::OnInsertControlPointWithoutUpdate()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return -1;
TArray<FTransform> & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
TArray<FVector> & DisplayPoints = EditedHoudiniSplineComponent->DisplayPoints;
if (EditedCurveSegmentIndex >= DisplayPoints.Num())
return -1;
// ... //
int InsertAfterIndex = 0;
TArray<int32> & DisplayPointIndexDivider = EditedHoudiniSplineComponent->DisplayPointIndexDivider;
for (int itr = 0; itr < DisplayPointIndexDivider.Num(); ++itr)
{
if (DisplayPointIndexDivider[itr] >= EditedCurveSegmentIndex)
{
InsertAfterIndex = itr;
break;
}
}
// ... //
if (InsertAfterIndex >= CurvePoints.Num()) return -1;
FTransform NewPoint = CurvePoints[InsertAfterIndex];
NewPoint.SetLocation(DisplayPoints[EditedCurveSegmentIndex]);
// To Do: Should interpolate the rotation and scale as well here.
// ...
// Insert new control point on curve, and add it to selected CP.
int32 NewPointIndex = AddControlPointAfter(NewPoint, InsertAfterIndex);
// Don't have to reconstruct the index divider each time.
//EditedHoudiniSplineComponent->Construct(EditedHoudiniSplineComponent->DisplayPoints);
EditedHoudiniSplineComponent->DisplayPointIndexDivider.Insert(EditedCurveSegmentIndex, InsertAfterIndex);
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
return NewPointIndex;
}
void
FHoudiniSplineComponentVisualizer::OnInsertControlPoint()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return;
int32 NewPointIndex = OnInsertControlPointWithoutUpdate();
if (NewPointIndex < 0) return;
EditedHoudiniSplineComponent->EditedControlPointsIndexes.Add(NewPointIndex);
RefreshViewport();
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
}
bool
FHoudiniSplineComponentVisualizer::IsInsertControlPointValid() const
{
return EditedCurveSegmentIndex >= 0;
}
void
FHoudiniSplineComponentVisualizer::OnAddControlPoint()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
// Transaction for Undo/Redo
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniSplineComponentInsertingPointsTransaction", "Houdini Spline Component: Inserting curve points."),
EditedHoudiniSplineComponent->GetOuter(), true);
EditedHoudiniSplineComponent->Modify();
EditedControlPointsIndexes.Sort();
const TArray<FTransform> & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
TArray<int32> tNewSelectedPoints;
if (EditedControlPointsIndexes.Num() == 1)
{
FTransform Point = CurvePoints[EditedControlPointsIndexes[0]];
FTransform NewTransform = FTransform::Identity;
FVector Location = Point.GetLocation();
//FQuat Rotation = Point.GetRotation();
//FVector Scale = Point.GetScale3D();
NewTransform.SetLocation(Location + 1.f);
//NewTransform.SetRotation(Rotation);
//NewTransform.SetScale3D(Scale);
int32 NewPointIndex = AddControlPointAfter(NewTransform, EditedControlPointsIndexes[0]);
tNewSelectedPoints.Add(NewPointIndex);
}
else
{
int IndexIncrement = 0;
int CurrentPointIndex, LastPointIndex;
FTransform CurrentPoint, LastPoint;
for (int32 n = 0; n < EditedControlPointsIndexes.Num(); ++n)
{
// Insert a new point between each adjacent pair of points
if (n > 0)
{
CurrentPointIndex = EditedControlPointsIndexes[n];
LastPointIndex = EditedControlPointsIndexes[n - 1];
CurrentPoint = CurvePoints[CurrentPointIndex + IndexIncrement];
LastPoint = CurvePoints[LastPointIndex + IndexIncrement];
// Insert a point in the middle of LastPoint and CurrentPoint
FVector NewPointLocation = LastPoint.GetLocation() + (CurrentPoint.GetLocation() - LastPoint.GetLocation()) / 2.f;
FVector NewPointScale = LastPoint.GetScale3D() + (CurrentPoint.GetScale3D() - LastPoint.GetScale3D()) / 2.f;
FQuat NewPointRotation = FQuat::Slerp(LastPoint.GetRotation(), CurrentPoint.GetRotation(), .5f);
FTransform NewTransform = FTransform::Identity;
NewTransform.SetLocation(NewPointLocation);
NewTransform.SetScale3D(NewPointScale);
NewTransform.SetRotation(NewPointRotation);
int32 NewPointIndex = AddControlPointAfter(NewTransform, EditedControlPointsIndexes[n - 1] + IndexIncrement);
tNewSelectedPoints.Add(NewPointIndex);
IndexIncrement += 1;
}
}
}
EditedControlPointsIndexes.Empty();
EditedControlPointsIndexes = tNewSelectedPoints;
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
RefreshViewport();
}
bool
FHoudiniSplineComponentVisualizer::IsAddControlPointValid() const
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
return EditedHoudiniSplineComponent && !EditedHoudiniSplineComponent->IsPendingKill() &&
EditedHoudiniSplineComponent->EditedControlPointsIndexes.Num() > 0;
}
void
FHoudiniSplineComponentVisualizer::OnDeleteControlPoint()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
if (EditedControlPointsIndexes.Num() <= 0)
return;
// Transaction for Undo/Redo
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniSplineComponentDeletingPointsTransaction", "Houdini Spline Component: Deleting curve points."),
EditedHoudiniSplineComponent->GetOuter(), true);
EditedHoudiniSplineComponent->Modify();
EditedControlPointsIndexes.Sort();
int32 SelectedIndexAfterDelete = EditedControlPointsIndexes[0] - 1;
SelectedIndexAfterDelete = FMath::Max(SelectedIndexAfterDelete, 0);
for (int32 n = EditedControlPointsIndexes.Num() - 1; n >= 0; --n)
{
int32 RemoveIndex = EditedControlPointsIndexes[n];
EditedHoudiniSplineComponent->RemovePointAtIndex(RemoveIndex);
}
EditedControlPointsIndexes.Empty();
OnDeselectAllControlPoints();
EditedControlPointsIndexes.Add(SelectedIndexAfterDelete);
if (IsCookOnCurveChanged(EditedHoudiniSplineComponent))
EditedHoudiniSplineComponent->MarkChanged(true);
// Force refresh the viewport after deleting points to ensure the consistency of HitProxy
RefreshViewport();
}
bool
FHoudiniSplineComponentVisualizer::IsDeleteControlPointValid() const
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return false;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
if (EditedControlPointsIndexes.Num() <= 0)
return false;
// We only allow the number of Control Points is at least 2 after delete
if (EditedHoudiniSplineComponent->GetCurvePointCount() - EditedControlPointsIndexes.Num() < 2)
return false;
return true;
}
void
FHoudiniSplineComponentVisualizer::OnDuplicateControlPoint()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return;
TArray<int32> & EditedControlPointsIndexes = EditedHoudiniSplineComponent->EditedControlPointsIndexes;
if (EditedControlPointsIndexes.Num() <= 0)
return;
// Transaction for Undo/Redo
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniSplineComponentDuplicatingPointsTransaction", "Houdini Spline Component: Duplicating curve points."),
EditedHoudiniSplineComponent->GetOuter(), true);
EditedHoudiniSplineComponent->Modify();
const TArray<FTransform> & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
EditedControlPointsIndexes.Sort();
TArray<int32> tNewSelectedPoints;
int IncrementIndex = 0;
for (int n = 0; n < EditedControlPointsIndexes.Num(); ++n)
{
int32 IndexAfter = EditedControlPointsIndexes[n] + IncrementIndex;
FTransform CurrentPoint = CurvePoints[IndexAfter];
if (IndexAfter == 0)
IndexAfter = -1;
int32 NewPointIndex = AddControlPointAfter(CurrentPoint, IndexAfter);
tNewSelectedPoints.Add(NewPointIndex);
IncrementIndex ++;
}
EditedControlPointsIndexes.Empty();
EditedControlPointsIndexes = tNewSelectedPoints;
EditedHoudiniSplineComponent->MarkModified(true);
RefreshViewport();
}
bool
FHoudiniSplineComponentVisualizer::IsDuplicateControlPointValid() const
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if(!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill()
|| EditedHoudiniSplineComponent->EditedControlPointsIndexes.Num() == 0)
return false;
return true;
}
void
FHoudiniSplineComponentVisualizer::OnDeselectAllControlPoints()
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (EditedHoudiniSplineComponent && !EditedHoudiniSplineComponent->IsPendingKill())
EditedHoudiniSplineComponent->EditedControlPointsIndexes.Empty();
}
bool
FHoudiniSplineComponentVisualizer::IsDeselectAllControlPointsValid() const
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (EditedHoudiniSplineComponent && !EditedHoudiniSplineComponent->IsPendingKill())
return EditedHoudiniSplineComponent->EditedControlPointsIndexes.Num() > 0;
return false;
}
int32
FHoudiniSplineComponentVisualizer::AddControlPointAfter(
const FTransform & NewPoint,
const int32 & nIndex)
{
UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
if (!EditedHoudiniSplineComponent || EditedHoudiniSplineComponent->IsPendingKill())
return nIndex;
const TArray<FTransform> & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
int32 NewControlPointIndex = nIndex + 1;
if (NewControlPointIndex == CurvePoints.Num())
EditedHoudiniSplineComponent->AppendPoint(NewPoint);
else
EditedHoudiniSplineComponent->InsertPointAtIndex(NewPoint, NewControlPointIndex);
// Return the index of the inserted control point
return NewControlPointIndex;
}
void
FHoudiniSplineComponentVisualizer::RefreshViewport()
{
if (GEditor)
GEditor->RedrawLevelEditingViewports(true);
}
// Find the EditorViewportClient of the viewport where the Houdini Spline Component lives in
FEditorViewportClient *
FHoudiniSplineComponentVisualizer::FindViewportClient(
const UHoudiniSplineComponent * InHoudiniSplineComponent,
const FSceneView * View)
{
if (!View || !InHoudiniSplineComponent)
return nullptr;
UWorld * World = InHoudiniSplineComponent->GetWorld();
uint32 ViewKey = View->GetViewKey();
const TArray<FEditorViewportClient*> & AllViewportClients = GUnrealEd->GetAllViewportClients();
for (auto & NextViewportClient : AllViewportClients)
{
if (!NextViewportClient)
continue;
if (NextViewportClient->GetWorld() != World)
continue;
// Found the viewport client which matches the unique key of the current scene view
if (NextViewportClient->ViewState.GetReference()->GetViewKey() == ViewKey)
return NextViewportClient;
}
return nullptr;
}
bool
FHoudiniSplineComponentVisualizer::IsCookOnCurveChanged(UHoudiniSplineComponent * InHoudiniSplineComponent)
{
if (!InHoudiniSplineComponent)
return true;
return InHoudiniSplineComponent->bCookOnCurveChanged;
// UHoudiniSplineComponent* EditedHoudiniSplineComponent = GetEditedHoudiniSplineComponent();
// UHoudiniInputObject * InputObject = Cast<UHoudiniInputObject>(EditedHoudiniSplineComponent->GetOuter());
// if (!InputObject)
// return true;
//
// UHoudiniInput * Input = Cast<UHoudiniInput>(InputObject->GetOuter());
//
// if (!Input)
// return true;
//
// return Input->GetCookOnCurveChange();
};
#undef LOCTEXT_NAMESPACE