786 lines
27 KiB
C++
786 lines
27 KiB
C++
/*
|
|
* Copyright (c) <2017> Side Effects Software Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Produced by:
|
|
* Mykola Konyk
|
|
* Side Effects Software Inc
|
|
* 123 Front Street West, Suite 1401
|
|
* Toronto, Ontario
|
|
* Canada M5J 2M2
|
|
* 416-504-9876
|
|
*
|
|
*/
|
|
|
|
#include "HoudiniSplineComponentVisualizer.h"
|
|
|
|
#include "HoudiniApi.h"
|
|
#include "HoudiniEngineEditorPrivatePCH.h"
|
|
#include "HoudiniAssetComponent.h"
|
|
#include "HoudiniEngineEditor.h"
|
|
#include "EditorViewportClient.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "EditorStyleSet.h"
|
|
|
|
#include "HoudiniEngineRuntimePrivatePCH.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
|
|
|
|
IMPLEMENT_HIT_PROXY( HHoudiniSplineVisProxy, HComponentVisProxy );
|
|
IMPLEMENT_HIT_PROXY( HHoudiniSplineControlPointVisProxy, HHoudiniSplineVisProxy );
|
|
|
|
HHoudiniSplineVisProxy::HHoudiniSplineVisProxy( const UActorComponent * InComponent )
|
|
: HComponentVisProxy( InComponent, HPP_Wireframe )
|
|
{}
|
|
|
|
HHoudiniSplineControlPointVisProxy::HHoudiniSplineControlPointVisProxy(
|
|
const UActorComponent * InComponent, int32 InControlPointIndex )
|
|
: HHoudiniSplineVisProxy( InComponent )
|
|
, ControlPointIndex( InControlPointIndex )
|
|
{}
|
|
|
|
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 Point.",
|
|
EUserInterfaceActionType::Button, FInputChord() );
|
|
|
|
UI_COMMAND(
|
|
CommandDuplicateControlPoint, "Duplicate Control Point", "Duplicate Control Point.",
|
|
EUserInterfaceActionType::Button, FInputChord());
|
|
|
|
UI_COMMAND(
|
|
CommandDeleteControlPoint, "Delete Control Point", "Delete Control Point.",
|
|
EUserInterfaceActionType::Button, FInputChord(EKeys::Delete) );
|
|
}
|
|
|
|
FHoudiniSplineComponentVisualizer::FHoudiniSplineComponentVisualizer()
|
|
: FComponentVisualizer()
|
|
, EditedHoudiniSplineComponent( nullptr )
|
|
, bCurveEditing( false )
|
|
, bAllowDuplication( true )
|
|
, CachedRotation( FQuat::Identity )
|
|
, bComponentNeedUpdate( false )
|
|
, bCookOnlyOnMouseRelease( false )
|
|
, bRecordTransactionOnMove( true )
|
|
{
|
|
FHoudiniSplineComponentVisualizerCommands::Register();
|
|
VisualizerActions = MakeShareable( new FUICommandList );
|
|
}
|
|
|
|
FHoudiniSplineComponentVisualizer::~FHoudiniSplineComponentVisualizer()
|
|
{
|
|
FHoudiniSplineComponentVisualizerCommands::Unregister();
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::OnRegister()
|
|
{
|
|
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 ) );
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::DrawVisualization(
|
|
const UActorComponent * Component, const FSceneView * View,
|
|
FPrimitiveDrawInterface * PDI )
|
|
{
|
|
const UHoudiniSplineComponent * HoudiniSplineComponent = Cast< const UHoudiniSplineComponent >( Component );
|
|
|
|
if ( HoudiniSplineComponent && HoudiniSplineComponent->IsValidCurve() && HoudiniSplineComponent->IsActive() )
|
|
{
|
|
static const FColor ColorNormal = FColor(255, 255, 255);
|
|
static const FColor ColorFirst(0, 192, 0);
|
|
static const FColor ColorSecond(255, 159, 0);
|
|
static const FColor ColorSelected(255, 0, 0);
|
|
|
|
static const FColor ColorNone = FColor(172, 172, 172);
|
|
static const FColor ColorNoneFirst = FColor(172, 255, 172);
|
|
static const FColor ColorNoneSecond = FColor(254, 216, 177);
|
|
|
|
static const float GrabHandleSize = 12.0f;
|
|
static const float GrabHandleSizeNone = 12.0f;// 8.0f;
|
|
static const float GrabHandleSizeSelected = 13.0f;
|
|
|
|
// Get component transformation.
|
|
const FTransform & HoudiniSplineComponentTransform = HoudiniSplineComponent->GetComponentTransform();
|
|
|
|
// Get curve points.
|
|
const TArray< FTransform > & CurvePoints = HoudiniSplineComponent->CurvePoints;
|
|
const TArray< FVector > & CurveDisplayPoints = HoudiniSplineComponent->CurveDisplayPoints;
|
|
|
|
// Draw the curve.
|
|
FVector DisplayPointFirst;
|
|
FVector DisplayPointPrevious;
|
|
|
|
// Dim the color if no points is selected
|
|
bool bNoPointSelected = EditedControlPointsIndexes.Num() <= 0;
|
|
float GrabHandleCurrentSize = bNoPointSelected ? GrabHandleSizeNone : GrabHandleSize;
|
|
|
|
int32 NumDisplayPoints = CurveDisplayPoints.Num();
|
|
for ( int32 DisplayPointIdx = 0; DisplayPointIdx < NumDisplayPoints; ++DisplayPointIdx )
|
|
{
|
|
// Get point for this index.
|
|
const FVector & DisplayPoint =
|
|
HoudiniSplineComponentTransform.TransformPosition( CurveDisplayPoints[ DisplayPointIdx ] );
|
|
|
|
if ( DisplayPointIdx > 0 )
|
|
{
|
|
// Draw line from previous point to current one.
|
|
PDI->DrawLine( DisplayPointPrevious, DisplayPoint, bNoPointSelected ? ColorNone : ColorNormal, SDPG_Foreground );
|
|
}
|
|
else
|
|
{
|
|
DisplayPointFirst = DisplayPoint;
|
|
}
|
|
|
|
// If this is last point and curve is closed, draw link from last to first.
|
|
if ( HoudiniSplineComponent->IsClosedCurve() && NumDisplayPoints > 1 &&
|
|
DisplayPointIdx + 1 == NumDisplayPoints )
|
|
{
|
|
PDI->DrawLine( DisplayPointFirst, DisplayPoint, bNoPointSelected ? ColorNone : ColorNormal, SDPG_Foreground );
|
|
}
|
|
|
|
DisplayPointPrevious = DisplayPoint;
|
|
}
|
|
|
|
// Draw control points.
|
|
for ( int32 PointIdx = 0; PointIdx < CurvePoints.Num(); ++PointIdx )
|
|
{
|
|
// Get point at this index.
|
|
const FVector & DisplayPoint = HoudiniSplineComponentTransform.TransformPosition( CurvePoints[ PointIdx ].GetLocation() );
|
|
|
|
// Draw point and set hit box for it.
|
|
PDI->SetHitProxy(new HHoudiniSplineControlPointVisProxy(HoudiniSplineComponent, PointIdx));
|
|
|
|
if ( ( bCurveEditing ) && ( EditedControlPointsIndexes.Contains(PointIdx)))
|
|
{
|
|
// If we are editing this control point, change its color
|
|
PDI->DrawPoint(DisplayPoint, ColorSelected, GrabHandleSizeSelected, SDPG_Foreground);
|
|
}
|
|
else
|
|
{
|
|
// Color the first two points differently to show the direction of the spline
|
|
if( PointIdx == 0 )
|
|
PDI->DrawPoint(DisplayPoint, bNoPointSelected ? ColorNoneFirst : ColorFirst, GrabHandleCurrentSize, SDPG_Foreground);
|
|
else if (PointIdx == 1)
|
|
PDI->DrawPoint(DisplayPoint, bNoPointSelected ? ColorNoneSecond : ColorSecond, GrabHandleCurrentSize, SDPG_Foreground);
|
|
else
|
|
PDI->DrawPoint(DisplayPoint, bNoPointSelected ? ColorNone : ColorNormal, GrabHandleCurrentSize, SDPG_Foreground);
|
|
}
|
|
|
|
PDI->SetHitProxy(nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::VisProxyHandleClick(
|
|
FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click )
|
|
{
|
|
bCurveEditing = false;
|
|
if ( !VisProxy || !VisProxy->Component.IsValid() )
|
|
return bCurveEditing;
|
|
|
|
const UHoudiniSplineComponent * HoudiniSplineComponent =
|
|
CastChecked< const UHoudiniSplineComponent >( VisProxy->Component.Get() );
|
|
|
|
EditedHoudiniSplineComponent = const_cast<UHoudiniSplineComponent *>(HoudiniSplineComponent);
|
|
|
|
|
|
if ( !HoudiniSplineComponent )
|
|
return bCurveEditing;
|
|
|
|
if ( !VisProxy->IsA(HHoudiniSplineControlPointVisProxy::StaticGetType()) )
|
|
return bCurveEditing;
|
|
|
|
HHoudiniSplineControlPointVisProxy * ControlPointProxy = (HHoudiniSplineControlPointVisProxy *) VisProxy;
|
|
if ( !ControlPointProxy )
|
|
return bCurveEditing;
|
|
|
|
bCurveEditing = true;
|
|
|
|
// If we are right-clicking, we dont want to select a new point unless the selection is empty/just one point...
|
|
bool bRightClick = Click.GetKey() == EKeys::RightMouseButton;
|
|
if (bRightClick && EditedControlPointsIndexes.Num() > 1)
|
|
return bCurveEditing;
|
|
|
|
bool bIsMultiSelecting = false;
|
|
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
|
|
if(ModifierKeys.IsControlDown())
|
|
bIsMultiSelecting = true;
|
|
|
|
if ( bIsMultiSelecting )
|
|
{
|
|
// Add to multi-selection
|
|
if ( !EditedControlPointsIndexes.Contains(ControlPointProxy->ControlPointIndex) )
|
|
EditedControlPointsIndexes.Add(ControlPointProxy->ControlPointIndex);
|
|
else
|
|
EditedControlPointsIndexes.Remove(ControlPointProxy->ControlPointIndex);
|
|
}
|
|
else
|
|
{
|
|
// Single selection
|
|
EditedControlPointsIndexes.Empty();
|
|
EditedControlPointsIndexes.Add(ControlPointProxy->ControlPointIndex);
|
|
}
|
|
|
|
CacheRotation();
|
|
|
|
return bCurveEditing;
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::HandleInputKey(
|
|
FEditorViewportClient * ViewportClient, FViewport * Viewport, FKey Key, EInputEvent Event )
|
|
{
|
|
bool bHandled = false;
|
|
|
|
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
|
|
bCookOnlyOnMouseRelease = HoudiniRuntimeSettings->bCookCurvesOnMouseRelease;
|
|
|
|
if ( Key == EKeys::LeftMouseButton && Event == IE_Released )
|
|
{
|
|
// Updates the spline
|
|
if ( bComponentNeedUpdate )
|
|
UpdateHoudiniComponents();
|
|
|
|
// Reset duplication flag on LMB release.
|
|
bAllowDuplication = true;
|
|
|
|
// Reset the transaction flag
|
|
bRecordTransactionOnMove = true;
|
|
|
|
// Re-cache the rotation
|
|
CacheRotation();
|
|
}
|
|
|
|
if ( Key == EKeys::Delete && Event == IE_Pressed )
|
|
{
|
|
if (IsDeleteControlPointValid())
|
|
{
|
|
OnDeleteControlPoint();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ( Event == IE_Pressed )
|
|
bHandled = VisualizerActions->ProcessCommandBindings( Key, FSlateApplication::Get().GetModifierKeys(), false );
|
|
|
|
return bHandled;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::EndEditing()
|
|
{
|
|
EditedHoudiniSplineComponent = nullptr;
|
|
EditedControlPointsIndexes.Empty();
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::GetWidgetLocation(
|
|
const FEditorViewportClient * ViewportClient,
|
|
FVector & OutLocation ) const
|
|
{
|
|
if ( !EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0 )
|
|
return false;
|
|
|
|
// Get curve points.
|
|
const TArray< FTransform > & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
|
|
|
|
int32 nCurrentCPIndex = -1;
|
|
FVector LocationSum = FVector::ZeroVector;
|
|
|
|
for (int n = 0; n < EditedControlPointsIndexes.Num(); n++)
|
|
{
|
|
nCurrentCPIndex = EditedControlPointsIndexes[n];
|
|
if( nCurrentCPIndex < 0 || nCurrentCPIndex >= CurvePoints.Num() )
|
|
continue;
|
|
|
|
LocationSum += CurvePoints[nCurrentCPIndex].GetLocation();
|
|
}
|
|
|
|
LocationSum /= EditedControlPointsIndexes.Num();
|
|
OutLocation = EditedHoudiniSplineComponent->GetComponentTransform().TransformPosition(LocationSum);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const
|
|
{
|
|
// Change the system only for the rotation gizmo or if in LocalSpace
|
|
if (ViewportClient->GetWidgetCoordSystemSpace() != COORD_Local && ViewportClient->GetWidgetMode() != FWidget::WM_Rotate)
|
|
return false;
|
|
|
|
// We only orient the widget if we select one point
|
|
if (!EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() != 1)
|
|
return false;
|
|
|
|
// Get the selected curve point
|
|
/*
|
|
int32 nCurrentCPIndex = EditedControlPointsIndexes[0];
|
|
FTransform transf = EditedHoudiniSplineComponent->CurvePoints[nCurrentCPIndex];
|
|
transf.SetLocation(FVector::ZeroVector);
|
|
OutMatrix = transf.ToMatrixNoScale();
|
|
*/
|
|
|
|
OutMatrix = FRotationMatrix::Make(CachedRotation);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::HandleInputDelta(
|
|
FEditorViewportClient * ViewportClient, FViewport * Viewport,
|
|
FVector & DeltaTranslate, FRotator & DeltaRotate, FVector & DeltaScale)
|
|
{
|
|
if (!EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0)
|
|
return false;
|
|
|
|
if ( ViewportClient->IsAltPressed() && bAllowDuplication )
|
|
{
|
|
DuplicateControlPoint();
|
|
|
|
// Don't duplicate again until we release LMB
|
|
bAllowDuplication = false;
|
|
}
|
|
|
|
// Get curve points.
|
|
const TArray< FTransform > & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
|
|
|
|
// Get component transformation.
|
|
const FTransform & HoudiniSplineComponentTransform = EditedHoudiniSplineComponent->GetComponentTransform();
|
|
|
|
|
|
FTransform CurrentPoint = FTransform::Identity;
|
|
for ( int n = 0; n < EditedControlPointsIndexes.Num(); n++ )
|
|
{
|
|
// Get current point from the selected points
|
|
int32 nCurrentCPIndex = EditedControlPointsIndexes[n];
|
|
if (nCurrentCPIndex < 0 || nCurrentCPIndex >= CurvePoints.Num())
|
|
continue;
|
|
|
|
CurrentPoint = CurvePoints[nCurrentCPIndex];
|
|
|
|
// Handle change in translation.
|
|
if ( !DeltaTranslate.IsZero() )
|
|
{
|
|
FVector PointTransformed = HoudiniSplineComponentTransform.TransformPosition(CurrentPoint.GetLocation()); // Get Position in world space
|
|
FVector PointTransformedDelta = PointTransformed + DeltaTranslate; // apply delta in world space
|
|
PointTransformed = HoudiniSplineComponentTransform.InverseTransformPosition(PointTransformedDelta); // convert back to local
|
|
CurrentPoint.SetLocation( PointTransformed );
|
|
}
|
|
|
|
// Handle change in rotation.
|
|
if ( !DeltaRotate.IsZero() )
|
|
{
|
|
FQuat NewRot = HoudiniSplineComponentTransform.GetRotation() * CurrentPoint.GetRotation(); // convert local-space rotation to world-space
|
|
NewRot = DeltaRotate.Quaternion() * NewRot; // apply world-space rotation
|
|
NewRot = HoudiniSplineComponentTransform.GetRotation().Inverse() * NewRot; // convert world-space rotation to local-space
|
|
CurrentPoint.SetRotation( NewRot );
|
|
}
|
|
|
|
// Handle change in scale
|
|
if ( !DeltaScale.IsZero() )
|
|
{
|
|
FVector NewScale = CurrentPoint.GetScale3D() * ( FVector(1,1,1) + DeltaScale );
|
|
CurrentPoint.SetScale3D( NewScale );
|
|
}
|
|
|
|
NotifyComponentModified(nCurrentCPIndex, CurrentPoint);
|
|
}
|
|
|
|
if ( ( bComponentNeedUpdate ) && ( !bCookOnlyOnMouseRelease ) )
|
|
{
|
|
// Update and cook the asset
|
|
UpdateHoudiniComponents();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr< SWidget >
|
|
FHoudiniSplineComponentVisualizer::GenerateContextMenu() const
|
|
{
|
|
FHoudiniEngineEditor& HoudiniEngineEditor = FHoudiniEngineEditor::Get();
|
|
FName StyleSetName = FHoudiniEngineStyle::GetStyleSetName();
|
|
|
|
FMenuBuilder MenuBuilder( true, VisualizerActions );
|
|
{
|
|
MenuBuilder.BeginSection( "CurveKeyEdit" );
|
|
|
|
{
|
|
if ( EditedControlPointsIndexes.Num() > 0 )
|
|
{
|
|
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.EndSection();
|
|
}
|
|
|
|
TSharedPtr< SWidget > MenuWidget = MenuBuilder.MakeWidget();
|
|
return MenuWidget;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::UpdateHoudiniComponents()
|
|
{
|
|
if ( EditedHoudiniSplineComponent )
|
|
EditedHoudiniSplineComponent->UpdateHoudiniComponents();
|
|
|
|
bComponentNeedUpdate = false;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::NotifyComponentModified( int32 PointIndex, const FTransform & Point )
|
|
{
|
|
if ( !EditedHoudiniSplineComponent )
|
|
return;
|
|
|
|
UHoudiniAssetComponent * HoudiniAssetComponent =
|
|
Cast< UHoudiniAssetComponent >( EditedHoudiniSplineComponent->GetAttachParent() );
|
|
|
|
if ( bRecordTransactionOnMove )
|
|
{
|
|
FScopedTransaction Transaction( TEXT(HOUDINI_MODULE_EDITOR),
|
|
LOCTEXT("HoudiniSplineComponentChange", "Houdini Spline Component: Moving a point"),
|
|
HoudiniAssetComponent );
|
|
EditedHoudiniSplineComponent->Modify();
|
|
|
|
// Do not record further transaction until the flag is reset
|
|
bRecordTransactionOnMove = false;
|
|
}
|
|
|
|
// Update given control point.
|
|
EditedHoudiniSplineComponent->UpdatePoint( PointIndex, Point );
|
|
|
|
bComponentNeedUpdate = true;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::OnAddControlPoint()
|
|
{
|
|
if ( !EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() < 0 )
|
|
return;
|
|
|
|
// Transaction for Undo/Redo
|
|
UHoudiniAssetComponent * HoudiniAssetComponent =
|
|
Cast< UHoudiniAssetComponent >(EditedHoudiniSplineComponent->GetAttachParent());
|
|
|
|
if (!HoudiniAssetComponent)
|
|
return;
|
|
|
|
FScopedTransaction Transaction(
|
|
TEXT(HOUDINI_MODULE_EDITOR),
|
|
LOCTEXT("HoudiniSplineComponentChange", "Houdini Spline Component: Adding a control point"),
|
|
HoudiniAssetComponent);
|
|
|
|
EditedHoudiniSplineComponent->Modify();
|
|
|
|
FTransform OtherPoint = FTransform::Identity;
|
|
FTransform CurrentPoint = FTransform::Identity;
|
|
int32 nCurrentCPIndex = -1;
|
|
|
|
// We need to sort the selection to insert the new nodes properly
|
|
EditedControlPointsIndexes.Sort();
|
|
|
|
const TArray< FTransform > & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
|
|
|
|
TArray<int32> tNewSelection;
|
|
for (int32 n = EditedControlPointsIndexes.Num() - 1; n >= 0 ; n--)
|
|
{
|
|
// Get current point from the selected points
|
|
nCurrentCPIndex = EditedControlPointsIndexes[n];
|
|
if (nCurrentCPIndex < 0 || nCurrentCPIndex >= CurvePoints.Num())
|
|
continue;
|
|
|
|
CurrentPoint = CurvePoints[nCurrentCPIndex];
|
|
|
|
// Select the other point
|
|
if (nCurrentCPIndex + 1 != CurvePoints.Num())
|
|
{
|
|
OtherPoint = CurvePoints[nCurrentCPIndex + 1];
|
|
}
|
|
else
|
|
{
|
|
if (EditedHoudiniSplineComponent->bClosedCurve)
|
|
{
|
|
OtherPoint = CurvePoints[0];
|
|
}
|
|
else
|
|
{
|
|
OtherPoint = CurvePoints[nCurrentCPIndex - 1];
|
|
nCurrentCPIndex--;
|
|
}
|
|
}
|
|
|
|
FVector NewPointLocation = CurrentPoint.GetLocation() + (OtherPoint.GetLocation() - CurrentPoint.GetLocation()) / 2.0f;
|
|
FVector NewPointScale = CurrentPoint.GetScale3D() + (OtherPoint.GetScale3D() - CurrentPoint.GetScale3D()) / 2.0f;
|
|
FQuat NewPointRotation = FQuat::Slerp(CurrentPoint.GetRotation(), OtherPoint.GetRotation(), 0.5f);
|
|
|
|
FTransform NewTransform = FTransform::Identity;
|
|
NewTransform.SetLocation(NewPointLocation);
|
|
NewTransform.SetScale3D(NewPointScale);
|
|
NewTransform.SetRotation(NewPointRotation);
|
|
|
|
int32 NewPointIndex = AddControlPointAfter(NewTransform, nCurrentCPIndex);
|
|
|
|
tNewSelection.Add(NewPointIndex);
|
|
}
|
|
|
|
// Update the spline component
|
|
UpdateHoudiniComponents();
|
|
|
|
// Select the new points.
|
|
EditedControlPointsIndexes.Empty();
|
|
EditedControlPointsIndexes = tNewSelection;
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::IsAddControlPointValid() const
|
|
{
|
|
// We can always add points.
|
|
return true;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::OnDeleteControlPoint()
|
|
{
|
|
if (!EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0)
|
|
return;
|
|
|
|
UHoudiniAssetComponent * HoudiniAssetComponent =
|
|
Cast< UHoudiniAssetComponent >(EditedHoudiniSplineComponent->GetAttachParent() );
|
|
|
|
FScopedTransaction Transaction(
|
|
TEXT( HOUDINI_MODULE_EDITOR ),
|
|
LOCTEXT( "HoudiniSplineComponentChange", "Houdini Spline Component: Removing a control point" ),
|
|
HoudiniAssetComponent);
|
|
EditedHoudiniSplineComponent->Modify();
|
|
|
|
// We need to sort the selection to delete the nodes properly
|
|
EditedControlPointsIndexes.Sort();
|
|
|
|
int32 nOffset = 0;
|
|
TArray<int32> tNewSelection;
|
|
for (int32 n = 0; n < EditedControlPointsIndexes.Num(); n++)
|
|
{
|
|
int32 nIndex = EditedControlPointsIndexes[n] - nOffset;
|
|
EditedHoudiniSplineComponent->RemovePoint(nIndex);
|
|
|
|
if (!tNewSelection.Contains(--nIndex))
|
|
tNewSelection.Add(nIndex);
|
|
nOffset++;
|
|
}
|
|
|
|
// Select previous points if possible
|
|
for (int32 n = tNewSelection.Num() - 1; n >= 0; n--)
|
|
{
|
|
// get the previous point
|
|
if (tNewSelection[n] < 0)
|
|
tNewSelection[n] = 0;
|
|
|
|
// if the new index is invalid, or has been removed, unselect it
|
|
if (tNewSelection[n] >= EditedHoudiniSplineComponent->CurvePoints.Num() || EditedControlPointsIndexes.Contains(tNewSelection[n]))
|
|
{
|
|
tNewSelection.RemoveAt(n);
|
|
}
|
|
}
|
|
|
|
if(tNewSelection.Num() > 0)
|
|
EditedControlPointsIndexes = tNewSelection;
|
|
else
|
|
{
|
|
EditedControlPointsIndexes.Empty();
|
|
EditedControlPointsIndexes.Add(0);
|
|
}
|
|
|
|
// cache the rotation
|
|
CacheRotation();
|
|
|
|
// Update the spline object
|
|
UpdateHoudiniComponents();
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::IsDeleteControlPointValid() const
|
|
{
|
|
if ( !EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0 )
|
|
return false;
|
|
|
|
// We can only delete points if we have more than two points.
|
|
if ( EditedHoudiniSplineComponent->GetCurvePointCount() < 2 )
|
|
return false;
|
|
|
|
// We need to leave 2 points after deleting
|
|
if ( EditedHoudiniSplineComponent->GetCurvePointCount() - EditedControlPointsIndexes.Num() < 2 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int32 FHoudiniSplineComponentVisualizer::AddControlPointAfter( const FTransform & NewPoint, const int32& nIndex )
|
|
{
|
|
if ( !EditedHoudiniSplineComponent )
|
|
return nIndex;
|
|
|
|
const TArray< FTransform > & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
|
|
check( nIndex >= 0 && nIndex < CurvePoints.Num() );
|
|
|
|
int32 ControlPointIndex = nIndex + 1;
|
|
if (ControlPointIndex == CurvePoints.Num())
|
|
EditedHoudiniSplineComponent->AddPoint( NewPoint );
|
|
else
|
|
EditedHoudiniSplineComponent->AddPoint( ControlPointIndex, NewPoint);
|
|
|
|
// Return the newly created point index.
|
|
return ControlPointIndex;
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::OnDuplicateControlPoint()
|
|
{
|
|
// Duplicate the selected points
|
|
DuplicateControlPoint();
|
|
|
|
// Update the spline component
|
|
UpdateHoudiniComponents();
|
|
}
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::DuplicateControlPoint()
|
|
{
|
|
if (!EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0)
|
|
return;
|
|
|
|
UHoudiniAssetComponent * HoudiniAssetComponent =
|
|
Cast< UHoudiniAssetComponent >(EditedHoudiniSplineComponent->GetAttachParent());
|
|
|
|
if (!HoudiniAssetComponent)
|
|
return;
|
|
|
|
FScopedTransaction Transaction(
|
|
TEXT(HOUDINI_MODULE_EDITOR),
|
|
LOCTEXT("HoudiniSplineComponentChange", "Houdini Spline Component: Adding a control point"),
|
|
HoudiniAssetComponent);
|
|
|
|
EditedHoudiniSplineComponent->Modify();
|
|
|
|
const TArray< FTransform > & CurvePoints = EditedHoudiniSplineComponent->CurvePoints;
|
|
|
|
//
|
|
EditedControlPointsIndexes.Sort();
|
|
|
|
int32 nCurrentCPIndex = -1;
|
|
int32 nNewCPIndex = -1;
|
|
TArray<int32> tNewSelection;
|
|
|
|
FTransform NewPoint = FTransform::Identity;
|
|
int nOffset = 0;
|
|
for ( int32 n = 0; n < EditedControlPointsIndexes.Num(); n++ )
|
|
{
|
|
// Get current point from the selected points
|
|
nCurrentCPIndex = EditedControlPointsIndexes[n] + nOffset;
|
|
if (nCurrentCPIndex < 0 || nCurrentCPIndex >= CurvePoints.Num())
|
|
continue;
|
|
|
|
// We just add the new point on top of the existing point.
|
|
NewPoint = CurvePoints[nCurrentCPIndex];
|
|
|
|
// Add the new point and select it.
|
|
nNewCPIndex = AddControlPointAfter(NewPoint, nCurrentCPIndex);
|
|
|
|
// Small hack when extending from the first point
|
|
if (nCurrentCPIndex == 0)
|
|
nNewCPIndex = 0;
|
|
|
|
tNewSelection.Add(nNewCPIndex);
|
|
nOffset++;
|
|
}
|
|
|
|
// Select the new points.
|
|
EditedControlPointsIndexes.Empty();
|
|
EditedControlPointsIndexes = tNewSelection;
|
|
}
|
|
|
|
bool
|
|
FHoudiniSplineComponentVisualizer::IsDuplicateControlPointValid() const
|
|
{
|
|
// We can only duplicate points if we have selected a point.
|
|
if (!EditedHoudiniSplineComponent || EditedControlPointsIndexes.Num() <= 0 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
FHoudiniSplineComponentVisualizer::CacheRotation()
|
|
{
|
|
FQuat NewCachedQuat = FQuat::Identity;
|
|
if (EditedHoudiniSplineComponent && EditedControlPointsIndexes.Num() == 1)
|
|
{
|
|
if (EditedHoudiniSplineComponent->CurvePoints.IsValidIndex(EditedControlPointsIndexes[0]))
|
|
NewCachedQuat = (EditedHoudiniSplineComponent->CurvePoints[EditedControlPointsIndexes[0]]).GetRotation();
|
|
}
|
|
|
|
CachedRotation = NewCachedQuat;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |