IPS_Editor_SplineEditable interface, spline visibility, snap-to-ground, Play without BaseLevel
Refactor spline system to use IPS_Editor_SplineEditable interface: - New interface in Spline/PS_Editor_SplineEditable.h with all editing methods - APS_Editor_SplineActor and APS_BehaviorEditor_AISpline both implement it - All ~40 Cast<APS_Editor_SplineActor> replaced with Cast<IPS_Editor_SplineEditable> - UndoManager SplinePointAction uses TWeakObjectPtr<AActor> + interface cast - Any actor implementing the interface works with full editor spline editing Spline visibility: - SetVisualizationVisible() hides tube/handles/cube in gameplay (data stays for AI) - SceneLoader calls it when stripping editor components - SplineMatOccluded material added (unused for now, PP occlusion planned) Other improvements: - Snap spline CP to ground on placement (downward raytrace) - Play button works without BaseLevel (uses current editor map) - AISpline triggers SplineNetwork::RebuildNetwork on BeginPlay (next tick) - PS_BehaviorEditor marked EnabledByDefault: false (only active in PROSERVE) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
"Description": "Bridge plugin: AI Behavior splines editable via PS_Editor runtime editor.",
|
"Description": "Bridge plugin: AI Behavior splines editable via PS_Editor runtime editor.",
|
||||||
"Category": "PS Tools",
|
"Category": "PS Tools",
|
||||||
"CreatedBy": "Asterion",
|
"CreatedBy": "Asterion",
|
||||||
|
"EnabledByDefault": false,
|
||||||
"Modules": [
|
"Modules": [
|
||||||
{
|
{
|
||||||
"Name": "PS_BehaviorEditor",
|
"Name": "PS_BehaviorEditor",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Components/StaticMeshComponent.h"
|
#include "Components/StaticMeshComponent.h"
|
||||||
#include "Engine/StaticMesh.h"
|
#include "Engine/StaticMesh.h"
|
||||||
#include "Materials/MaterialInstanceDynamic.h"
|
#include "Materials/MaterialInstanceDynamic.h"
|
||||||
|
#include "PS_AI_Behavior_SplineNetwork.h"
|
||||||
|
|
||||||
APS_BehaviorEditor_AISpline::APS_BehaviorEditor_AISpline()
|
APS_BehaviorEditor_AISpline::APS_BehaviorEditor_AISpline()
|
||||||
{
|
{
|
||||||
@@ -82,6 +83,9 @@ void APS_BehaviorEditor_AISpline::BeginPlay()
|
|||||||
|
|
||||||
CenterCubeMat = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
CenterCubeMat = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
||||||
CenterCubeMat->SetVectorParameterValue(TEXT("Color"), SplineColor);
|
CenterCubeMat->SetVectorParameterValue(TEXT("Color"), SplineColor);
|
||||||
|
|
||||||
|
SplineMatOccluded = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
||||||
|
SplineMatOccluded->SetVectorParameterValue(TEXT("Color"), FLinearColor(0.3f, 0.3f, 0.3f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center cube (selection handle)
|
// Center cube (selection handle)
|
||||||
@@ -102,6 +106,18 @@ void APS_BehaviorEditor_AISpline::BeginPlay()
|
|||||||
{
|
{
|
||||||
CenterCube->SetMaterial(0, CenterCubeMat);
|
CenterCube->SetMaterial(0, CenterCubeMat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rebuild SplineNetwork next tick (all splines from scene load will be ready)
|
||||||
|
GetWorld()->GetTimerManager().SetTimerForNextTick([WeakWorld = TWeakObjectPtr<UWorld>(GetWorld())]()
|
||||||
|
{
|
||||||
|
if (UWorld* World = WeakWorld.Get())
|
||||||
|
{
|
||||||
|
if (UPS_AI_Behavior_SplineNetwork* Network = World->GetSubsystem<UPS_AI_Behavior_SplineNetwork>())
|
||||||
|
{
|
||||||
|
Network->RebuildNetwork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Point manipulation ----
|
// ---- Point manipulation ----
|
||||||
@@ -406,6 +422,23 @@ void APS_BehaviorEditor_AISpline::ClearHandleHighlight()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Visibility ----
|
||||||
|
|
||||||
|
void APS_BehaviorEditor_AISpline::SetVisualizationVisible(bool bVisible)
|
||||||
|
{
|
||||||
|
if (SplineMeshComp) SplineMeshComp->SetVisibility(bVisible);
|
||||||
|
if (CenterCube) CenterCube->SetVisibility(bVisible);
|
||||||
|
if (HandleRoot) HandleRoot->SetVisibility(bVisible, true);
|
||||||
|
for (UStaticMeshComponent* Handle : PointHandles)
|
||||||
|
{
|
||||||
|
if (Handle)
|
||||||
|
{
|
||||||
|
Handle->SetVisibility(bVisible);
|
||||||
|
Handle->SetCollisionEnabled(bVisible ? ECollisionEnabled::QueryOnly : ECollisionEnabled::NoCollision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Serialization ----
|
// ---- Serialization ----
|
||||||
|
|
||||||
void APS_BehaviorEditor_AISpline::ExportToCustomProperties(TMap<FString, FString>& OutProperties) const
|
void APS_BehaviorEditor_AISpline::ExportToCustomProperties(TMap<FString, FString>& OutProperties) const
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "PS_AI_Behavior_SplinePath.h"
|
#include "PS_AI_Behavior_SplinePath.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "PS_BehaviorEditor_AISpline.generated.h"
|
#include "PS_BehaviorEditor_AISpline.generated.h"
|
||||||
|
|
||||||
class UProceduralMeshComponent;
|
class UProceduralMeshComponent;
|
||||||
@@ -19,7 +20,7 @@ class UPS_Editor_EditableComponent;
|
|||||||
* Includes tube visualization + handles from PS_Editor's spline system.
|
* Includes tube visualization + handles from PS_Editor's spline system.
|
||||||
*/
|
*/
|
||||||
UCLASS(BlueprintType, Blueprintable, meta = (DisplayName = "AI Spline (Editor)"))
|
UCLASS(BlueprintType, Blueprintable, meta = (DisplayName = "AI Spline (Editor)"))
|
||||||
class PS_BEHAVIOREDITOR_API APS_BehaviorEditor_AISpline : public APS_AI_Behavior_SplinePath, public IPS_Editor_PointPlaceable
|
class PS_BEHAVIOREDITOR_API APS_BehaviorEditor_AISpline : public APS_AI_Behavior_SplinePath, public IPS_Editor_PointPlaceable, public IPS_Editor_SplineEditable
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@@ -39,40 +40,46 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spline|Visual")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spline|Visual")
|
||||||
FLinearColor SplineSelectedColor = FLinearColor(0.4f, 0.8f, 1.0f);
|
FLinearColor SplineSelectedColor = FLinearColor(0.4f, 0.8f, 1.0f);
|
||||||
|
|
||||||
// ---- Point manipulation ----
|
// ---- Point manipulation (IPS_Editor_SplineEditable) ----
|
||||||
|
|
||||||
void AddPoint(const FVector& WorldPosition);
|
virtual void AddPoint(const FVector& WorldPosition) override;
|
||||||
void MovePoint(int32 Index, const FVector& NewWorldPosition);
|
virtual void MovePoint(int32 Index, const FVector& NewWorldPosition) override;
|
||||||
void RemovePoint(int32 Index);
|
virtual void RemovePoint(int32 Index) override;
|
||||||
int32 GetNumPoints() const;
|
virtual int32 GetNumPoints() const override;
|
||||||
FVector GetPointLocation(int32 Index) const;
|
virtual FVector GetPointLocation(int32 Index) const override;
|
||||||
TArray<FVector> GetAllPointLocations() const;
|
virtual TArray<FVector> GetAllPointLocations() const override;
|
||||||
void SetAllPointLocations(const TArray<FVector>& Points);
|
virtual void SetAllPointLocations(const TArray<FVector>& Points) override;
|
||||||
void InsertPoint(int32 Index, const FVector& WorldPosition);
|
virtual void InsertPoint(int32 Index, const FVector& WorldPosition) override;
|
||||||
int32 FindInsertIndex(const FVector& WorldLocation) const;
|
virtual int32 FindInsertIndex(const FVector& WorldLocation) const override;
|
||||||
FVector GetCentroid() const;
|
virtual FVector GetCentroid() const override;
|
||||||
|
|
||||||
// ---- Visualization ----
|
// ---- Visualization (IPS_Editor_SplineEditable) ----
|
||||||
|
|
||||||
void UpdateVisualization();
|
virtual void UpdateVisualization() override;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS Editor|Spline")
|
UFUNCTION(BlueprintCallable, Category = "PS Editor|Spline")
|
||||||
void UpdateSplineMaterials();
|
virtual void UpdateSplineMaterials() override;
|
||||||
|
|
||||||
// ---- Handle interaction ----
|
// ---- Handle interaction (IPS_Editor_SplineEditable) ----
|
||||||
|
|
||||||
int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const;
|
virtual int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const override;
|
||||||
void SetHandlesVisible(bool bVisible);
|
virtual void SetHandlesVisible(bool bVisible) override;
|
||||||
void SetSelected(bool bSelected);
|
virtual void SetSelected(bool bSelected) override;
|
||||||
bool IsCenterCube(const UPrimitiveComponent* Comp) const;
|
virtual bool IsCenterCube(const UPrimitiveComponent* Comp) const override;
|
||||||
bool IsSplineMesh(const UPrimitiveComponent* Comp) const;
|
virtual bool IsSplineMesh(const UPrimitiveComponent* Comp) const override;
|
||||||
void HighlightHandle(int32 Index);
|
virtual void HighlightHandle(int32 Index) override;
|
||||||
void ClearHandleHighlight();
|
virtual void ClearHandleHighlight() override;
|
||||||
|
|
||||||
// ---- Serialization ----
|
// ---- Visibility (IPS_Editor_SplineEditable) ----
|
||||||
|
virtual void SetVisualizationVisible(bool bVisible) override;
|
||||||
|
|
||||||
void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const;
|
// ---- Serialization (IPS_Editor_SplineEditable) ----
|
||||||
void ImportFromCustomProperties(const TMap<FString, FString>& Properties);
|
|
||||||
|
virtual void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const override;
|
||||||
|
virtual void ImportFromCustomProperties(const TMap<FString, FString>& Properties) override;
|
||||||
|
|
||||||
|
// ---- Access (IPS_Editor_SplineEditable) ----
|
||||||
|
virtual USplineComponent* GetSplineComponent() const override { return SplineComp; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
@@ -95,6 +102,8 @@ private:
|
|||||||
TObjectPtr<UMaterialInstanceDynamic> HandleMat;
|
TObjectPtr<UMaterialInstanceDynamic> HandleMat;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UMaterialInstanceDynamic> HandleHighlightMat;
|
TObjectPtr<UMaterialInstanceDynamic> HandleHighlightMat;
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<UMaterialInstanceDynamic> SplineMatOccluded;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UStaticMeshComponent> CenterCube;
|
TObjectPtr<UStaticMeshComponent> CenterCube;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "PS_Editor_Gizmo.h"
|
#include "PS_Editor_Gizmo.h"
|
||||||
#include "PS_Editor_UndoManager.h"
|
#include "PS_Editor_UndoManager.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "Components/SplineComponent.h"
|
#include "Components/SplineComponent.h"
|
||||||
#include "DrawDebugHelpers.h"
|
#include "DrawDebugHelpers.h"
|
||||||
#include "PS_Editor_ConfirmDialog.h"
|
#include "PS_Editor_ConfirmDialog.h"
|
||||||
@@ -379,7 +380,7 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
// Spline point mode: move the single point instead of actors
|
// Spline point mode: move the single point instead of actors
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* SplineActor = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
if (SplinePointDragInitialState.IsValidIndex(ActiveSplinePointIndex))
|
if (SplinePointDragInitialState.IsValidIndex(ActiveSplinePointIndex))
|
||||||
{
|
{
|
||||||
@@ -472,7 +473,7 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
// Following a single spline point
|
// Following a single spline point
|
||||||
if (APS_Editor_SplineActor* SplineActor = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
Gizmo->SetActorLocation(SplineActor->GetPointLocation(ActiveSplinePointIndex));
|
Gizmo->SetActorLocation(SplineActor->GetPointLocation(ActiveSplinePointIndex));
|
||||||
}
|
}
|
||||||
@@ -480,7 +481,7 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
else if (Selected.Num() == 1)
|
else if (Selected.Num() == 1)
|
||||||
{
|
{
|
||||||
// Single actor: use centroid for splines, location for others
|
// Single actor: use centroid for splines, location for others
|
||||||
if (APS_Editor_SplineActor* SplineActor = Cast<APS_Editor_SplineActor>(Selected[0].Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(Selected[0].Get()))
|
||||||
{
|
{
|
||||||
Gizmo->SetActorLocation(SplineActor->GetCentroid());
|
Gizmo->SetActorLocation(SplineActor->GetCentroid());
|
||||||
}
|
}
|
||||||
@@ -507,7 +508,7 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
else if (CameraMode == EPS_Editor_CameraMode::SplinePointDrag)
|
else if (CameraMode == EPS_Editor_CameraMode::SplinePointDrag)
|
||||||
{
|
{
|
||||||
// Drag spline point along horizontal plane
|
// Drag spline point along horizontal plane
|
||||||
APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get());
|
IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get());
|
||||||
if (!Spline || ActiveSplinePointIndex < 0) return;
|
if (!Spline || ActiveSplinePointIndex < 0) return;
|
||||||
|
|
||||||
FVector RayOrigin, RayDir;
|
FVector RayOrigin, RayDir;
|
||||||
@@ -585,23 +586,23 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
if (Mode == EPS_Editor_EditorMode::SplinePlacement || Mode == EPS_Editor_EditorMode::SplinePlacement)
|
if (Mode == EPS_Editor_EditorMode::SplinePlacement || Mode == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
{
|
{
|
||||||
// Collect candidate splines: selected actors + active placement actor
|
// Collect candidate splines: selected actors + active placement actor
|
||||||
TArray<APS_Editor_SplineActor*> CandidateSplines;
|
TArray<IPS_Editor_SplineEditable*> CandidateSplines;
|
||||||
if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager())
|
||||||
{
|
{
|
||||||
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* S = Cast<APS_Editor_SplineActor>(Weak.Get()))
|
if (IPS_Editor_SplineEditable* S = Cast<IPS_Editor_SplineEditable>(Weak.Get()))
|
||||||
{
|
{
|
||||||
CandidateSplines.AddUnique(S);
|
CandidateSplines.AddUnique(S);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (APS_Editor_SplineActor* ActiveSpline = Cast<APS_Editor_SplineActor>(EdPC->GetActivePlacementActor()))
|
if (IPS_Editor_SplineEditable* ActiveSpline = Cast<IPS_Editor_SplineEditable>(EdPC->GetActivePlacementActor()))
|
||||||
{
|
{
|
||||||
CandidateSplines.AddUnique(ActiveSpline);
|
CandidateSplines.AddUnique(ActiveSpline);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (APS_Editor_SplineActor* Spline : CandidateSplines)
|
for (IPS_Editor_SplineEditable* Spline : CandidateSplines)
|
||||||
{
|
{
|
||||||
// Center cube check (skip in SplinePlacement — no center cube interaction during creation)
|
// Center cube check (skip in SplinePlacement — no center cube interaction during creation)
|
||||||
if (Mode == EPS_Editor_EditorMode::SplinePlacement)
|
if (Mode == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
@@ -626,7 +627,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
if (HandleIdx >= 0)
|
if (HandleIdx >= 0)
|
||||||
{
|
{
|
||||||
ActiveSplinePointIndex = HandleIdx;
|
ActiveSplinePointIndex = HandleIdx;
|
||||||
DraggedSplineActor = Spline;
|
DraggedSplineActor = Cast<AActor>(Spline);
|
||||||
Spline->HighlightHandle(HandleIdx);
|
Spline->HighlightHandle(HandleIdx);
|
||||||
|
|
||||||
if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager())
|
||||||
@@ -650,16 +651,16 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
if (EdPC2->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
if (EdPC2->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
{
|
{
|
||||||
// Collect candidate splines: selected + active placement actor
|
// Collect candidate splines: selected + active placement actor
|
||||||
TArray<APS_Editor_SplineActor*> TubeCandidates;
|
TArray<IPS_Editor_SplineEditable*> TubeCandidates;
|
||||||
if (UPS_Editor_SelectionManager* SM2 = EdPC2->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SM2 = EdPC2->GetSelectionManager())
|
||||||
{
|
{
|
||||||
for (const TWeakObjectPtr<AActor>& Weak : SM2->GetSelectedActors())
|
for (const TWeakObjectPtr<AActor>& Weak : SM2->GetSelectedActors())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* S = Cast<APS_Editor_SplineActor>(Weak.Get()))
|
if (IPS_Editor_SplineEditable* S = Cast<IPS_Editor_SplineEditable>(Weak.Get()))
|
||||||
TubeCandidates.AddUnique(S);
|
TubeCandidates.AddUnique(S);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (APS_Editor_SplineActor* ActiveSpline = Cast<APS_Editor_SplineActor>(EdPC2->GetActivePlacementActor()))
|
if (IPS_Editor_SplineEditable* ActiveSpline = Cast<IPS_Editor_SplineEditable>(EdPC2->GetActivePlacementActor()))
|
||||||
{
|
{
|
||||||
TubeCandidates.AddUnique(ActiveSpline);
|
TubeCandidates.AddUnique(ActiveSpline);
|
||||||
}
|
}
|
||||||
@@ -673,7 +674,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
|
|
||||||
if (GetWorld()->LineTraceSingleByChannel(LineHit, RayOrigin, TraceEnd, ECC_Camera, LineParams))
|
if (GetWorld()->LineTraceSingleByChannel(LineHit, RayOrigin, TraceEnd, ECC_Camera, LineParams))
|
||||||
{
|
{
|
||||||
for (APS_Editor_SplineActor* Spline : TubeCandidates)
|
for (IPS_Editor_SplineEditable* Spline : TubeCandidates)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (Spline->IsSplineMesh(LineHit.GetComponent()))
|
if (Spline->IsSplineMesh(LineHit.GetComponent()))
|
||||||
@@ -687,7 +688,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
if (UPS_Editor_UndoManager* UM = EdPC2->GetUndoManager())
|
if (UPS_Editor_UndoManager* UM = EdPC2->GetUndoManager())
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = OldPoints;
|
Action->OldPoints = OldPoints;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Insert spline point");
|
Action->Description = TEXT("Insert spline point");
|
||||||
@@ -695,7 +696,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ActiveSplinePointIndex = InsertIdx;
|
ActiveSplinePointIndex = InsertIdx;
|
||||||
DraggedSplineActor = Spline;
|
DraggedSplineActor = Cast<AActor>(Spline);
|
||||||
Spline->HighlightHandle(InsertIdx);
|
Spline->HighlightHandle(InsertIdx);
|
||||||
|
|
||||||
if (UPS_Editor_SelectionManager* InsertSM = EdPC2->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* InsertSM = EdPC2->GetSelectionManager())
|
||||||
@@ -721,13 +722,13 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
{
|
{
|
||||||
if (EdPC3->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
if (EdPC3->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(EdPC3->GetActivePlacementActor()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(EdPC3->GetActivePlacementActor()))
|
||||||
{
|
{
|
||||||
// Raycast to get world click position
|
// Raycast to get world click position
|
||||||
FHitResult AddHit;
|
FHitResult AddHit;
|
||||||
FCollisionQueryParams AddParams;
|
FCollisionQueryParams AddParams;
|
||||||
AddParams.AddIgnoredActor(this);
|
AddParams.AddIgnoredActor(this);
|
||||||
AddParams.AddIgnoredActor(Spline);
|
AddParams.AddIgnoredActor(Cast<AActor>(Spline));
|
||||||
const FVector AddEnd = RayOrigin + RayDir * 100000.0f;
|
const FVector AddEnd = RayOrigin + RayDir * 100000.0f;
|
||||||
|
|
||||||
FVector ClickPos;
|
FVector ClickPos;
|
||||||
@@ -746,7 +747,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
if (UPS_Editor_UndoManager* UM = EdPC3->GetUndoManager())
|
if (UPS_Editor_UndoManager* UM = EdPC3->GetUndoManager())
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = OldPoints;
|
Action->OldPoints = OldPoints;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Add spline point");
|
Action->Description = TEXT("Add spline point");
|
||||||
@@ -756,7 +757,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
// Select the new point
|
// Select the new point
|
||||||
const int32 NewIdx = Spline->GetNumPoints() - 1;
|
const int32 NewIdx = Spline->GetNumPoints() - 1;
|
||||||
ActiveSplinePointIndex = NewIdx;
|
ActiveSplinePointIndex = NewIdx;
|
||||||
DraggedSplineActor = Spline;
|
DraggedSplineActor = Cast<AActor>(Spline);
|
||||||
Spline->HighlightHandle(NewIdx);
|
Spline->HighlightHandle(NewIdx);
|
||||||
|
|
||||||
if (UPS_Editor_SelectionManager* SM3 = EdPC3->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SM3 = EdPC3->GetSelectionManager())
|
||||||
@@ -800,10 +801,10 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
// Spline point drag: record SplinePointAction
|
// Spline point drag: record SplinePointAction
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* SplineActor = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = SplineActor;
|
Action->SplineActor = Cast<AActor>(SplineActor);
|
||||||
Action->OldPoints = SplinePointDragInitialState;
|
Action->OldPoints = SplinePointDragInitialState;
|
||||||
Action->NewPoints = SplineActor->GetAllPointLocations();
|
Action->NewPoints = SplineActor->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Move spline point");
|
Action->Description = TEXT("Move spline point");
|
||||||
@@ -852,7 +853,7 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
// Keep gizmo at the active spline point
|
// Keep gizmo at the active spline point
|
||||||
if (APS_Editor_SplineActor* SplineActor = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
if (APS_Editor_Gizmo* G = SM2->GetGizmo())
|
if (APS_Editor_Gizmo* G = SM2->GetGizmo())
|
||||||
{
|
{
|
||||||
@@ -872,14 +873,14 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
else if (CameraMode == EPS_Editor_CameraMode::SplinePointDrag)
|
else if (CameraMode == EPS_Editor_CameraMode::SplinePointDrag)
|
||||||
{
|
{
|
||||||
// Record undo for spline point move
|
// Record undo for spline point move
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetController()))
|
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetController()))
|
||||||
{
|
{
|
||||||
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = SplinePointDragInitialState;
|
Action->OldPoints = SplinePointDragInitialState;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Move spline point");
|
Action->Description = TEXT("Move spline point");
|
||||||
@@ -973,7 +974,7 @@ void APS_Editor_Pawn::HandleDelete(const FInputActionValue& Value)
|
|||||||
if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement
|
if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement
|
||||||
&& ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
&& ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get());
|
IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get());
|
||||||
if (Spline && Spline->GetNumPoints() > 2)
|
if (Spline && Spline->GetNumPoints() > 2)
|
||||||
{
|
{
|
||||||
TArray<FVector> OldPoints = Spline->GetAllPointLocations();
|
TArray<FVector> OldPoints = Spline->GetAllPointLocations();
|
||||||
@@ -982,7 +983,7 @@ void APS_Editor_Pawn::HandleDelete(const FInputActionValue& Value)
|
|||||||
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = OldPoints;
|
Action->OldPoints = OldPoints;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Delete spline point");
|
Action->Description = TEXT("Delete spline point");
|
||||||
@@ -1089,7 +1090,7 @@ void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value)
|
|||||||
// If a spline point is selected (in edit or placement mode), snap that point to ground
|
// If a spline point is selected (in edit or placement mode), snap that point to ground
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
const FVector PointLoc = Spline->GetPointLocation(ActiveSplinePointIndex);
|
const FVector PointLoc = Spline->GetPointLocation(ActiveSplinePointIndex);
|
||||||
const FVector TraceStart = PointLoc + FVector(0.0f, 0.0f, 1.0f); // Just above current position
|
const FVector TraceStart = PointLoc + FVector(0.0f, 0.0f, 1.0f); // Just above current position
|
||||||
@@ -1097,7 +1098,7 @@ void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value)
|
|||||||
|
|
||||||
FHitResult Hit;
|
FHitResult Hit;
|
||||||
FCollisionQueryParams Params;
|
FCollisionQueryParams Params;
|
||||||
Params.AddIgnoredActor(Spline);
|
Params.AddIgnoredActor(Cast<AActor>(Spline));
|
||||||
Params.AddIgnoredActor(this);
|
Params.AddIgnoredActor(this);
|
||||||
if (UPS_Editor_SelectionManager* SnapSM = EditorPC->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SnapSM = EditorPC->GetSelectionManager())
|
||||||
{
|
{
|
||||||
@@ -1132,7 +1133,7 @@ void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value)
|
|||||||
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
if (UPS_Editor_UndoManager* UM = EditorPC->GetUndoManager())
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = OldPoints;
|
Action->OldPoints = OldPoints;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Snap spline point to ground");
|
Action->Description = TEXT("Snap spline point to ground");
|
||||||
@@ -1438,7 +1439,7 @@ void APS_Editor_Pawn::BeginGizmoDrag(EPS_Editor_GizmoAxis Axis)
|
|||||||
// Store initial spline state if editing a point
|
// Store initial spline state if editing a point
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
SplinePointDragInitialState = Spline->GetAllPointLocations();
|
SplinePointDragInitialState = Spline->GetAllPointLocations();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "PS_Editor_Gizmo.h"
|
#include "PS_Editor_Gizmo.h"
|
||||||
#include "PS_Editor_Pawn.h"
|
#include "PS_Editor_Pawn.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
#include "Engine/LevelStreamingDynamic.h"
|
#include "Engine/LevelStreamingDynamic.h"
|
||||||
#include "Engine/PostProcessVolume.h"
|
#include "Engine/PostProcessVolume.h"
|
||||||
@@ -164,9 +165,9 @@ void APS_Editor_PlayerController::OnPossess(APawn* InPawn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APS_Editor_SplineActor* APS_Editor_PlayerController::GetActiveSplineActor() const
|
IPS_Editor_SplineEditable* APS_Editor_PlayerController::GetActiveSplineActor() const
|
||||||
{
|
{
|
||||||
return Cast<APS_Editor_SplineActor>(ActivePlacementActor);
|
return Cast<IPS_Editor_SplineEditable>(ActivePlacementActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APS_Editor_PlayerController::CloseEditor()
|
void APS_Editor_PlayerController::CloseEditor()
|
||||||
@@ -223,7 +224,7 @@ void APS_Editor_PlayerController::BeginPointPlacementAppend(AActor* ExistingActo
|
|||||||
EditorMode = EPS_Editor_EditorMode::SplinePlacement;
|
EditorMode = EPS_Editor_EditorMode::SplinePlacement;
|
||||||
|
|
||||||
// Store initial state for undo (spline-specific)
|
// Store initial state for undo (spline-specific)
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(ExistingActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(ExistingActor))
|
||||||
{
|
{
|
||||||
AppendInitialPoints = Spline->GetAllPointLocations();
|
AppendInitialPoints = Spline->GetAllPointLocations();
|
||||||
Spline->SetHandlesVisible(true);
|
Spline->SetHandlesVisible(true);
|
||||||
@@ -240,7 +241,7 @@ void APS_Editor_PlayerController::FinishPointPlacement()
|
|||||||
AppendInitialPoints.Empty();
|
AppendInitialPoints.Empty();
|
||||||
EditorMode = EPS_Editor_EditorMode::Normal;
|
EditorMode = EPS_Editor_EditorMode::Normal;
|
||||||
|
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(PlacementActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(PlacementActor))
|
||||||
{
|
{
|
||||||
Spline->SetHandlesVisible(false);
|
Spline->SetHandlesVisible(false);
|
||||||
Spline->ClearHandleHighlight();
|
Spline->ClearHandleHighlight();
|
||||||
@@ -439,6 +440,22 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA
|
|||||||
ClickWorldPos = WorldOrigin + WorldDirection * 1000.0f;
|
ClickWorldPos = WorldOrigin + WorldDirection * 1000.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snap spline CP to ground: trace downward from click point
|
||||||
|
{
|
||||||
|
FHitResult GroundHit;
|
||||||
|
FCollisionQueryParams GroundParams;
|
||||||
|
GroundParams.AddIgnoredActor(GetPawn());
|
||||||
|
if (ActivePlacementActor) GroundParams.AddIgnoredActor(ActivePlacementActor);
|
||||||
|
|
||||||
|
const FVector GroundStart = ClickWorldPos + FVector(0.0f, 0.0f, 50.0f);
|
||||||
|
const FVector GroundEnd = ClickWorldPos - FVector(0.0f, 0.0f, 10000.0f);
|
||||||
|
|
||||||
|
if (GetWorld()->LineTraceSingleByChannel(GroundHit, GroundStart, GroundEnd, ECC_Visibility, GroundParams))
|
||||||
|
{
|
||||||
|
ClickWorldPos = GroundHit.ImpactPoint + FVector(0.0f, 0.0f, 5.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ActivePlacementActor)
|
if (!ActivePlacementActor)
|
||||||
{
|
{
|
||||||
// First click: spawn the actor, track immediately, record undo
|
// First click: spawn the actor, track immediately, record undo
|
||||||
@@ -470,7 +487,7 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we clicked on the existing spline tube → insert instead of append
|
// Check if we clicked on the existing spline tube → insert instead of append
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(ActivePlacementActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(ActivePlacementActor))
|
||||||
{
|
{
|
||||||
if (Spline->GetNumPoints() >= 2)
|
if (Spline->GetNumPoints() >= 2)
|
||||||
{
|
{
|
||||||
@@ -494,7 +511,7 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default: add point at end via the interface (with undo)
|
// Default: add point at end via the interface (with undo)
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(ActivePlacementActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(ActivePlacementActor))
|
||||||
{
|
{
|
||||||
TArray<FVector> OldPoints = Spline->GetAllPointLocations();
|
TArray<FVector> OldPoints = Spline->GetAllPointLocations();
|
||||||
Spline->AddPoint(ClickWorldPos);
|
Spline->AddPoint(ClickWorldPos);
|
||||||
@@ -502,7 +519,7 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA
|
|||||||
if (UndoManager)
|
if (UndoManager)
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Spline;
|
Action->SplineActor = Cast<AActor>(Spline);
|
||||||
Action->OldPoints = OldPoints;
|
Action->OldPoints = OldPoints;
|
||||||
Action->NewPoints = Spline->GetAllPointLocations();
|
Action->NewPoints = Spline->GetAllPointLocations();
|
||||||
Action->Description = TEXT("Add spline point");
|
Action->Description = TEXT("Add spline point");
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class UPS_Editor_SpawnCatalog;
|
|||||||
class UPS_Editor_MasterCatalog;
|
class UPS_Editor_MasterCatalog;
|
||||||
class UPS_Editor_SceneSerializer;
|
class UPS_Editor_SceneSerializer;
|
||||||
class APS_Editor_SplineActor;
|
class APS_Editor_SplineActor;
|
||||||
|
class IPS_Editor_SplineEditable;
|
||||||
class IPS_Editor_PointPlaceable;
|
class IPS_Editor_PointPlaceable;
|
||||||
class ULevelStreamingDynamic;
|
class ULevelStreamingDynamic;
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ public:
|
|||||||
bool IsAppendingToActor() const { return bAppendingToActor; }
|
bool IsAppendingToActor() const { return bAppendingToActor; }
|
||||||
|
|
||||||
// Legacy convenience (for spline-specific code like point editing)
|
// Legacy convenience (for spline-specific code like point editing)
|
||||||
APS_Editor_SplineActor* GetActiveSplineActor() const;
|
IPS_Editor_SplineEditable* GetActiveSplineActor() const;
|
||||||
|
|
||||||
/** Convenience: enter point placement for an existing spline. */
|
/** Convenience: enter point placement for an existing spline. */
|
||||||
void BeginSplineEditing(AActor* Spline) { BeginPointPlacementAppend(Spline); }
|
void BeginSplineEditing(AActor* Spline) { BeginPointPlacementAppend(Spline); }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "PS_Editor_EditableComponent.h"
|
#include "PS_Editor_EditableComponent.h"
|
||||||
#include "PS_Editor_SpawnableComponent.h"
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "GameFramework/PlayerController.h"
|
#include "GameFramework/PlayerController.h"
|
||||||
#include "Engine/World.h"
|
#include "Engine/World.h"
|
||||||
#include "GameFramework/Pawn.h"
|
#include "GameFramework/Pawn.h"
|
||||||
@@ -188,7 +189,7 @@ void UPS_Editor_SelectionManager::SelectActor(AActor* Actor)
|
|||||||
OnSelectionChanged.Broadcast();
|
OnSelectionChanged.Broadcast();
|
||||||
|
|
||||||
// Mark spline as selected (handles shown only in SplineEditing mode, not here)
|
// Mark spline as selected (handles shown only in SplineEditing mode, not here)
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
Spline->SetSelected(true);
|
Spline->SetSelected(true);
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,7 @@ void UPS_Editor_SelectionManager::DeselectActor(AActor* Actor)
|
|||||||
OnSelectionChanged.Broadcast();
|
OnSelectionChanged.Broadcast();
|
||||||
|
|
||||||
// Unmark spline
|
// Unmark spline
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
Spline->SetHandlesVisible(false);
|
Spline->SetHandlesVisible(false);
|
||||||
Spline->SetSelected(false);
|
Spline->SetSelected(false);
|
||||||
@@ -258,7 +259,7 @@ void UPS_Editor_SelectionManager::ClearSelection()
|
|||||||
SetActorHighlight(Actor, false);
|
SetActorHighlight(Actor, false);
|
||||||
|
|
||||||
// Spline-specific cleanup
|
// Spline-specific cleanup
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
Spline->SetHandlesVisible(false);
|
Spline->SetHandlesVisible(false);
|
||||||
Spline->SetSelected(false);
|
Spline->SetSelected(false);
|
||||||
@@ -316,7 +317,7 @@ FVector UPS_Editor_SelectionManager::GetSelectionPivot() const
|
|||||||
if (AActor* Actor = Weak.Get())
|
if (AActor* Actor = Weak.Get())
|
||||||
{
|
{
|
||||||
// For SplineActors, use the centroid of all points as pivot
|
// For SplineActors, use the centroid of all points as pivot
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
Sum += Spline->GetCentroid();
|
Sum += Spline->GetCentroid();
|
||||||
}
|
}
|
||||||
@@ -523,7 +524,7 @@ void UPS_Editor_SelectionManager::UpdateGizmo()
|
|||||||
// Override pivot for SplineActors: use centroid so gizmo aligns with center cube
|
// Override pivot for SplineActors: use centroid so gizmo aligns with center cube
|
||||||
if (SelectedActors.Num() == 1)
|
if (SelectedActors.Num() == 1)
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(SelectedActors[0].Get()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SelectedActors[0].Get()))
|
||||||
{
|
{
|
||||||
Pivot = Spline->GetCentroid();
|
Pivot = Spline->GetCentroid();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "PS_Editor_UndoManager.h"
|
#include "PS_Editor_UndoManager.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "UObject/UnrealType.h"
|
#include "UObject/UnrealType.h"
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
|
|
||||||
void FPS_Editor_SplinePointAction::Execute()
|
void FPS_Editor_SplinePointAction::Execute()
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = SplineActor.Get())
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SplineActor.Get()))
|
||||||
{
|
{
|
||||||
Spline->SetAllPointLocations(NewPoints);
|
Spline->SetAllPointLocations(NewPoints);
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ void FPS_Editor_SplinePointAction::Execute()
|
|||||||
|
|
||||||
void FPS_Editor_SplinePointAction::Undo()
|
void FPS_Editor_SplinePointAction::Undo()
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = SplineActor.Get())
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SplineActor.Get()))
|
||||||
{
|
{
|
||||||
Spline->SetAllPointLocations(OldPoints);
|
Spline->SetAllPointLocations(OldPoints);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,17 +158,16 @@ struct FPS_Editor_SpawnAction : public FPS_Editor_Action
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class APS_Editor_SplineActor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records a spline point state change (move, add, remove).
|
* Records a spline point state change (move, add, remove).
|
||||||
* Stores full state snapshots (all points before/after) for simplicity.
|
* Stores full state snapshots (all points before/after) for simplicity.
|
||||||
|
* SplineActor must implement IPS_Editor_SplineEditable.
|
||||||
*/
|
*/
|
||||||
struct FPS_Editor_SplinePointAction : public FPS_Editor_Action
|
struct FPS_Editor_SplinePointAction : public FPS_Editor_Action
|
||||||
{
|
{
|
||||||
FPS_Editor_SplinePointAction() : FPS_Editor_Action(EPS_Editor_ActionType::SplinePointChange) {}
|
FPS_Editor_SplinePointAction() : FPS_Editor_Action(EPS_Editor_ActionType::SplinePointChange) {}
|
||||||
|
|
||||||
TWeakObjectPtr<APS_Editor_SplineActor> SplineActor;
|
TWeakObjectPtr<AActor> SplineActor;
|
||||||
TArray<FVector> OldPoints;
|
TArray<FVector> OldPoints;
|
||||||
TArray<FVector> NewPoints;
|
TArray<FVector> NewPoints;
|
||||||
FString Description;
|
FString Description;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "PS_Editor_EditableInterface.h"
|
#include "PS_Editor_EditableInterface.h"
|
||||||
#include "PS_Editor_SpawnableComponent.h"
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
#include "JsonObjectConverter.h"
|
#include "JsonObjectConverter.h"
|
||||||
#include "Misc/FileHelper.h"
|
#include "Misc/FileHelper.h"
|
||||||
@@ -74,6 +75,12 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
|||||||
{
|
{
|
||||||
Comp->DestroyComponent();
|
Comp->DestroyComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide spline visualization in gameplay (tube, handles) — data stays for AI
|
||||||
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
|
{
|
||||||
|
Spline->SetVisualizationVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +151,7 @@ AActor* UPS_Editor_SceneLoader::SpawnActorFromData(UWorld* World, const FPS_Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import spline data if applicable
|
// Import spline data if applicable
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(SpawnedActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SpawnedActor))
|
||||||
{
|
{
|
||||||
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "PS_Editor_PlayerController.h"
|
#include "PS_Editor_PlayerController.h"
|
||||||
#include "PS_Editor_SceneLoader.h"
|
#include "PS_Editor_SceneLoader.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "JsonObjectConverter.h"
|
#include "JsonObjectConverter.h"
|
||||||
#include "Misc/FileHelper.h"
|
#include "Misc/FileHelper.h"
|
||||||
#include "Misc/Paths.h"
|
#include "Misc/Paths.h"
|
||||||
@@ -47,7 +48,7 @@ bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_
|
|||||||
ActorData.Scale = Actor->GetActorScale3D();
|
ActorData.Scale = Actor->GetActorScale3D();
|
||||||
|
|
||||||
// Export spline data
|
// Export spline data
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
Spline->ExportToCustomProperties(ActorData.CustomProperties);
|
Spline->ExportToCustomProperties(ActorData.CustomProperties);
|
||||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Exported spline with %d points, class=%s"),
|
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Exported spline with %d points, class=%s"),
|
||||||
@@ -214,7 +215,7 @@ bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import spline data
|
// Import spline data
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(SpawnedActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SpawnedActor))
|
||||||
{
|
{
|
||||||
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ void APS_Editor_SplineActor::BeginPlay()
|
|||||||
|
|
||||||
CenterCubeMat = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
CenterCubeMat = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
||||||
CenterCubeMat->SetVectorParameterValue(TEXT("Color"), SplineColor);
|
CenterCubeMat->SetVectorParameterValue(TEXT("Color"), SplineColor);
|
||||||
|
|
||||||
|
// Occluded pass: gray, no depth test — visible through walls
|
||||||
|
SplineMatOccluded = UMaterialInstanceDynamic::Create(GizmoMat, this);
|
||||||
|
SplineMatOccluded->SetVectorParameterValue(TEXT("Color"), FLinearColor(0.3f, 0.3f, 0.3f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create center cube (selection handle for whole spline)
|
// Create center cube (selection handle for whole spline)
|
||||||
@@ -255,14 +259,12 @@ void APS_Editor_SplineActor::UpdateVisualization()
|
|||||||
}
|
}
|
||||||
|
|
||||||
SplineMeshComp->ClearAllMeshSections();
|
SplineMeshComp->ClearAllMeshSections();
|
||||||
|
|
||||||
SplineMeshComp->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray<FColor>(), TArray<FProcMeshTangent>(), true);
|
SplineMeshComp->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray<FColor>(), TArray<FProcMeshTangent>(), true);
|
||||||
|
|
||||||
// Force collision and bounds update immediately
|
|
||||||
SplineMeshComp->UpdateBounds();
|
SplineMeshComp->UpdateBounds();
|
||||||
SplineMeshComp->MarkRenderStateDirty();
|
SplineMeshComp->MarkRenderStateDirty();
|
||||||
|
|
||||||
// Apply the current material (normal or selected)
|
|
||||||
// Check if handles are visible as a proxy for "selected" state
|
|
||||||
const bool bCurrentlySelected = (PointHandles.Num() > 0 && PointHandles[0] && PointHandles[0]->IsVisible());
|
const bool bCurrentlySelected = (PointHandles.Num() > 0 && PointHandles[0] && PointHandles[0]->IsVisible());
|
||||||
UMaterialInstanceDynamic* LineMat = (bCurrentlySelected && SplineMatSelected) ? SplineMatSelected : SplineMat;
|
UMaterialInstanceDynamic* LineMat = (bCurrentlySelected && SplineMatSelected) ? SplineMatSelected : SplineMat;
|
||||||
if (LineMat)
|
if (LineMat)
|
||||||
@@ -458,6 +460,21 @@ void APS_Editor_SplineActor::UpdateSplineMaterials()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APS_Editor_SplineActor::SetVisualizationVisible(bool bVisible)
|
||||||
|
{
|
||||||
|
if (SplineMeshComp) SplineMeshComp->SetVisibility(bVisible);
|
||||||
|
if (CenterCube) CenterCube->SetVisibility(bVisible);
|
||||||
|
if (HandleRoot) HandleRoot->SetVisibility(bVisible, true);
|
||||||
|
for (UStaticMeshComponent* Handle : PointHandles)
|
||||||
|
{
|
||||||
|
if (Handle)
|
||||||
|
{
|
||||||
|
Handle->SetVisibility(bVisible);
|
||||||
|
Handle->SetCollisionEnabled(bVisible ? ECollisionEnabled::QueryOnly : ECollisionEnabled::NoCollision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool APS_Editor_SplineActor::IsSplineMesh(const UPrimitiveComponent* Comp) const
|
bool APS_Editor_SplineActor::IsSplineMesh(const UPrimitiveComponent* Comp) const
|
||||||
{
|
{
|
||||||
return Comp && Comp == SplineMeshComp;
|
return Comp && Comp == SplineMeshComp;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/Actor.h"
|
#include "GameFramework/Actor.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "PS_Editor_SplineActor.generated.h"
|
#include "PS_Editor_SplineActor.generated.h"
|
||||||
|
|
||||||
class USplineComponent;
|
class USplineComponent;
|
||||||
@@ -15,7 +16,7 @@ class UPS_Editor_EditableComponent;
|
|||||||
* Renders as a visible quad strip with clickable point handles.
|
* Renders as a visible quad strip with clickable point handles.
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class PS_EDITOR_API APS_Editor_SplineActor : public AActor, public IPS_Editor_PointPlaceable
|
class PS_EDITOR_API APS_Editor_SplineActor : public AActor, public IPS_Editor_PointPlaceable, public IPS_Editor_SplineEditable
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@@ -46,74 +47,77 @@ public:
|
|||||||
// ---- Point manipulation ----
|
// ---- Point manipulation ----
|
||||||
|
|
||||||
/** Add a point at the end of the spline (world space). */
|
/** Add a point at the end of the spline (world space). */
|
||||||
void AddPoint(const FVector& WorldPosition);
|
virtual void AddPoint(const FVector& WorldPosition) override;
|
||||||
|
|
||||||
/** Move an existing point to a new world position. */
|
/** Move an existing point to a new world position. */
|
||||||
void MovePoint(int32 Index, const FVector& NewWorldPosition);
|
virtual void MovePoint(int32 Index, const FVector& NewWorldPosition) override;
|
||||||
|
|
||||||
/** Remove a point by index. */
|
/** Remove a point by index. */
|
||||||
void RemovePoint(int32 Index);
|
virtual void RemovePoint(int32 Index) override;
|
||||||
|
|
||||||
/** Get the number of spline points. */
|
/** Get the number of spline points. */
|
||||||
int32 GetNumPoints() const;
|
virtual int32 GetNumPoints() const override;
|
||||||
|
|
||||||
/** Get the world position of a spline point. */
|
/** Get the world position of a spline point. */
|
||||||
FVector GetPointLocation(int32 Index) const;
|
virtual FVector GetPointLocation(int32 Index) const override;
|
||||||
|
|
||||||
/** Get all point world positions (for undo snapshots). */
|
/** Get all point world positions (for undo snapshots). */
|
||||||
TArray<FVector> GetAllPointLocations() const;
|
virtual TArray<FVector> GetAllPointLocations() const override;
|
||||||
|
|
||||||
/** Set all point positions at once (for undo restore). Rebuilds handles and visualization. */
|
/** Set all point positions at once (for undo restore). Rebuilds handles and visualization. */
|
||||||
void SetAllPointLocations(const TArray<FVector>& Points);
|
virtual void SetAllPointLocations(const TArray<FVector>& Points) override;
|
||||||
|
|
||||||
// ---- Visualization ----
|
// ---- Visualization ----
|
||||||
|
|
||||||
/** Regenerate the line mesh and reposition all handles. */
|
/** Regenerate the line mesh and reposition all handles. */
|
||||||
void UpdateVisualization();
|
virtual void UpdateVisualization() override;
|
||||||
|
|
||||||
/** Re-apply SplineColor to materials (call after changing SplineColor at runtime). */
|
/** Re-apply SplineColor to materials (call after changing SplineColor at runtime). */
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS Editor|Spline")
|
UFUNCTION(BlueprintCallable, Category = "PS Editor|Spline")
|
||||||
void UpdateSplineMaterials();
|
virtual void UpdateSplineMaterials() override;
|
||||||
|
|
||||||
// ---- Handle interaction ----
|
// ---- Handle interaction ----
|
||||||
|
|
||||||
/** Find which point handle is under the cursor ray. Returns -1 if none. */
|
/** Find which point handle is under the cursor ray. Returns -1 if none. */
|
||||||
int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const;
|
virtual int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const override;
|
||||||
|
|
||||||
/** Show or hide all point handles and center cube. */
|
/** Show or hide all point handles and center cube. */
|
||||||
void SetHandlesVisible(bool bVisible);
|
virtual void SetHandlesVisible(bool bVisible) override;
|
||||||
|
|
||||||
/** Set the spline's selected state (changes line + cube color). */
|
/** Set the spline's selected state (changes line + cube color). */
|
||||||
void SetSelected(bool bSelected);
|
virtual void SetSelected(bool bSelected) override;
|
||||||
|
|
||||||
/** Returns true if the given component is the center cube. */
|
/** Returns true if the given component is the center cube. */
|
||||||
bool IsCenterCube(const UPrimitiveComponent* Comp) const;
|
virtual bool IsCenterCube(const UPrimitiveComponent* Comp) const override;
|
||||||
|
|
||||||
/** Highlight a specific handle (yellow), others go back to normal (white). */
|
/** Highlight a specific handle (yellow), others go back to normal (white). */
|
||||||
void HighlightHandle(int32 Index);
|
virtual void HighlightHandle(int32 Index) override;
|
||||||
|
|
||||||
/** Clear all handle highlights. */
|
/** Clear all handle highlights. */
|
||||||
void ClearHandleHighlight();
|
virtual void ClearHandleHighlight() override;
|
||||||
|
|
||||||
// ---- Serialization ----
|
// ---- Serialization ----
|
||||||
|
|
||||||
/** Returns true if the given component is the spline line mesh. */
|
/** Returns true if the given component is the spline line mesh. */
|
||||||
bool IsSplineMesh(const UPrimitiveComponent* Comp) const;
|
virtual bool IsSplineMesh(const UPrimitiveComponent* Comp) const override;
|
||||||
|
|
||||||
/** Find the best insertion index for a new point at the given world location. */
|
/** Find the best insertion index for a new point at the given world location. */
|
||||||
int32 FindInsertIndex(const FVector& WorldLocation) const;
|
virtual int32 FindInsertIndex(const FVector& WorldLocation) const override;
|
||||||
|
|
||||||
/** Insert a point at a specific index. */
|
/** Insert a point at a specific index. */
|
||||||
void InsertPoint(int32 Index, const FVector& WorldPosition);
|
virtual void InsertPoint(int32 Index, const FVector& WorldPosition) override;
|
||||||
|
|
||||||
/** Get the centroid of all spline points (world space). Used as pivot for gizmo. */
|
/** Get the centroid of all spline points (world space). Used as pivot for gizmo. */
|
||||||
FVector GetCentroid() const;
|
virtual FVector GetCentroid() const override;
|
||||||
|
|
||||||
|
/** Hide/show the spline visualization. */
|
||||||
|
virtual void SetVisualizationVisible(bool bVisible) override;
|
||||||
|
|
||||||
/** Export spline data to a key-value map (for JSON serialization). */
|
/** Export spline data to a key-value map (for JSON serialization). */
|
||||||
void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const;
|
virtual void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const override;
|
||||||
|
|
||||||
/** Import spline data from a key-value map (for scene loading). */
|
/** Import spline data from a key-value map (for scene loading). */
|
||||||
void ImportFromCustomProperties(const TMap<FString, FString>& Properties);
|
virtual void ImportFromCustomProperties(const TMap<FString, FString>& Properties) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
@@ -144,6 +148,9 @@ private:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UMaterialInstanceDynamic> HandleHighlightMat;
|
TObjectPtr<UMaterialInstanceDynamic> HandleHighlightMat;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<UMaterialInstanceDynamic> SplineMatOccluded; // Gray, no depth test — visible through walls
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UStaticMeshComponent> CenterCube;
|
TObjectPtr<UStaticMeshComponent> CenterCube;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UObject/Interface.h"
|
||||||
|
#include "PS_Editor_SplineEditable.generated.h"
|
||||||
|
|
||||||
|
class USplineComponent;
|
||||||
|
|
||||||
|
UINTERFACE(BlueprintType, meta = (DisplayName = "PS Editor Spline Editable"))
|
||||||
|
class PS_EDITOR_API UPS_Editor_SplineEditable : public UInterface
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for any actor that provides spline editing in PS_Editor.
|
||||||
|
* Implemented by APS_Editor_SplineActor and APS_BehaviorEditor_AISpline.
|
||||||
|
*
|
||||||
|
* The editor (Pawn, SelectionManager, Serializer, etc.) casts to this interface
|
||||||
|
* instead of a concrete class, so any spline-like actor works with the full
|
||||||
|
* editing flow: point placement, handle drag, insert, delete, undo, serialization.
|
||||||
|
*/
|
||||||
|
class PS_EDITOR_API IPS_Editor_SplineEditable
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ---- Point manipulation ----
|
||||||
|
virtual void AddPoint(const FVector& WorldPosition) = 0;
|
||||||
|
virtual void MovePoint(int32 Index, const FVector& NewWorldPosition) = 0;
|
||||||
|
virtual void RemovePoint(int32 Index) = 0;
|
||||||
|
virtual int32 GetNumPoints() const = 0;
|
||||||
|
virtual FVector GetPointLocation(int32 Index) const = 0;
|
||||||
|
virtual TArray<FVector> GetAllPointLocations() const = 0;
|
||||||
|
virtual void SetAllPointLocations(const TArray<FVector>& Points) = 0;
|
||||||
|
virtual void InsertPoint(int32 Index, const FVector& WorldPosition) = 0;
|
||||||
|
virtual int32 FindInsertIndex(const FVector& WorldLocation) const = 0;
|
||||||
|
virtual FVector GetCentroid() const = 0;
|
||||||
|
|
||||||
|
// ---- Visualization ----
|
||||||
|
virtual void UpdateVisualization() = 0;
|
||||||
|
virtual void UpdateSplineMaterials() = 0;
|
||||||
|
|
||||||
|
// ---- Handle interaction ----
|
||||||
|
virtual int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const = 0;
|
||||||
|
virtual void SetHandlesVisible(bool bVisible) = 0;
|
||||||
|
virtual void SetSelected(bool bSelected) = 0;
|
||||||
|
virtual bool IsCenterCube(const UPrimitiveComponent* Comp) const = 0;
|
||||||
|
virtual bool IsSplineMesh(const UPrimitiveComponent* Comp) const = 0;
|
||||||
|
virtual void HighlightHandle(int32 Index) = 0;
|
||||||
|
virtual void ClearHandleHighlight() = 0;
|
||||||
|
|
||||||
|
// ---- Serialization ----
|
||||||
|
virtual void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const = 0;
|
||||||
|
virtual void ImportFromCustomProperties(const TMap<FString, FString>& Properties) = 0;
|
||||||
|
|
||||||
|
// ---- Visibility ----
|
||||||
|
/** Hide/show the spline visualization (tube, handles, center cube). Data remains for AI. */
|
||||||
|
virtual void SetVisualizationVisible(bool bVisible) = 0;
|
||||||
|
|
||||||
|
// ---- Access ----
|
||||||
|
virtual USplineComponent* GetSplineComponent() const = 0;
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "PS_Editor_EditableInterface.h"
|
#include "PS_Editor_EditableInterface.h"
|
||||||
#include "PS_Editor_SpawnableComponent.h"
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "PS_Editor_SplineEditable.h"
|
||||||
#include "PS_Editor_Pawn.h"
|
#include "PS_Editor_Pawn.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
#include "Widgets/SBoxPanel.h"
|
#include "Widgets/SBoxPanel.h"
|
||||||
@@ -204,9 +205,9 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
|||||||
{
|
{
|
||||||
for (const TWeakObjectPtr<AActor>& Weak : SelMgr->GetSelectedActors())
|
for (const TWeakObjectPtr<AActor>& Weak : SelMgr->GetSelectedActors())
|
||||||
{
|
{
|
||||||
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(Weak.Get()))
|
if (Cast<IPS_Editor_SplineEditable>(Weak.Get()))
|
||||||
{
|
{
|
||||||
EditorPC->BeginSplineEditing(Spline);
|
EditorPC->BeginSplineEditing(Weak.Get());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,26 +714,42 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
|
|
||||||
// Store scene name in GameInstance subsystem (survives OpenLevel)
|
// Store scene name in GameInstance subsystem (survives OpenLevel)
|
||||||
FString BaseLevel = EditorPC->CurrentBaseLevel;
|
FString BaseLevel = EditorPC->CurrentBaseLevel;
|
||||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor Play: BaseLevel='%s'"), *BaseLevel);
|
FString SceneName = SaveNameField.IsValid() ? SaveNameField->GetText().ToString() : TEXT("");
|
||||||
if (BaseLevel.IsEmpty())
|
|
||||||
|
// Resolve map path: BaseLevel from catalog, or current map if none selected
|
||||||
|
FString MapPath;
|
||||||
|
if (!BaseLevel.IsEmpty())
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: No BaseLevel set, cannot Play."));
|
if (FString* Found = BaseLevelPathMap.Find(BaseLevel))
|
||||||
return FReply::Handled();
|
{
|
||||||
|
MapPath = *Found;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MapPath = FString::Printf(TEXT("/Game/%s"), *BaseLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No BaseLevel = play on the current editor map
|
||||||
|
MapPath = EditorPC->GetWorld()->GetMapName();
|
||||||
|
MapPath.RemoveFromStart(EditorPC->GetWorld()->StreamingLevelsPrefix);
|
||||||
|
BaseLevel = MapPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString SceneName = SaveNameField.IsValid() ? SaveNameField->GetText().ToString() : TEXT("");
|
UE_LOG(LogTemp, Log, TEXT("PS_Editor Play: BaseLevel='%s', Map='%s', Scenario='%s'"), *BaseLevel, *MapPath, *SceneName);
|
||||||
|
|
||||||
if (UGameInstance* GI = EditorPC->GetGameInstance())
|
if (UGameInstance* GI = EditorPC->GetGameInstance())
|
||||||
{
|
{
|
||||||
if (UPS_Editor_GameInstanceSubsystem* Sub = GI->GetSubsystem<UPS_Editor_GameInstanceSubsystem>())
|
if (UPS_Editor_GameInstanceSubsystem* Sub = GI->GetSubsystem<UPS_Editor_GameInstanceSubsystem>())
|
||||||
{
|
{
|
||||||
Sub->SetPendingScenario(SceneName, BaseLevel);
|
Sub->SetPendingScenario(SceneName, BaseLevel);
|
||||||
Sub->bIsPlayMode = true;
|
Sub->bIsPlayMode = true;
|
||||||
// Remember editor map for ReturnToEditor
|
|
||||||
if (Sub->EditorMapName.IsEmpty())
|
if (Sub->EditorMapName.IsEmpty())
|
||||||
{
|
{
|
||||||
FString MapName = EditorPC->GetWorld()->GetMapName();
|
FString EdMapName = EditorPC->GetWorld()->GetMapName();
|
||||||
MapName.RemoveFromStart(EditorPC->GetWorld()->StreamingLevelsPrefix);
|
EdMapName.RemoveFromStart(EditorPC->GetWorld()->StreamingLevelsPrefix);
|
||||||
Sub->EditorMapName = MapName;
|
Sub->EditorMapName = EdMapName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -750,16 +767,6 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
EditorPC->bShowMouseCursor = false;
|
EditorPC->bShowMouseCursor = false;
|
||||||
EditorPC->SetInputMode(FInputModeGameOnly());
|
EditorPC->SetInputMode(FInputModeGameOnly());
|
||||||
|
|
||||||
// Use full path from MasterCatalog, fallback to /Game/{name}
|
|
||||||
FString MapPath;
|
|
||||||
if (FString* Found = BaseLevelPathMap.Find(BaseLevel))
|
|
||||||
{
|
|
||||||
MapPath = *Found;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MapPath = FString::Printf(TEXT("/Game/%s"), *BaseLevel);
|
|
||||||
}
|
|
||||||
UGameplayStatics::OpenLevel(EditorPC, FName(*MapPath), true, TEXT(""));
|
UGameplayStatics::OpenLevel(EditorPC, FName(*MapPath), true, TEXT(""));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
})
|
})
|
||||||
@@ -1353,7 +1360,7 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
{
|
{
|
||||||
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
||||||
{
|
{
|
||||||
if (Cast<APS_Editor_SplineActor>(Weak.Get()))
|
if (Cast<IPS_Editor_SplineEditable>(Weak.Get()))
|
||||||
{
|
{
|
||||||
bSplineSelected = true;
|
bSplineSelected = true;
|
||||||
break;
|
break;
|
||||||
@@ -1598,11 +1605,7 @@ void UPS_Editor_MainWidget::RefreshOutliner()
|
|||||||
|
|
||||||
// Build display name
|
// Build display name
|
||||||
FString DisplayName;
|
FString DisplayName;
|
||||||
if (Cast<APS_Editor_SplineActor>(Actor))
|
if (UPS_Editor_SpawnableComponent* SC = Actor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
||||||
{
|
|
||||||
DisplayName = TEXT("Spline");
|
|
||||||
}
|
|
||||||
else if (UPS_Editor_SpawnableComponent* SC = Actor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
|
||||||
{
|
{
|
||||||
if (!SC->DisplayName.IsEmpty())
|
if (!SC->DisplayName.IsEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user