You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
788 lines
23 KiB
C++
788 lines
23 KiB
C++
|
|
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||
|
|
|
||
|
|
|
||
|
|
#include "EdModeInteractiveToolsContext.h"
|
||
|
|
#include "Editor.h"
|
||
|
|
#include "EditorViewportClient.h"
|
||
|
|
#include "EditorModeManager.h"
|
||
|
|
#include "LevelEditorViewport.h" // for GCurrentLevelEditingViewportClient
|
||
|
|
#include "Engine/Selection.h"
|
||
|
|
#include "Misc/ITransaction.h"
|
||
|
|
#include "ScopedTransaction.h"
|
||
|
|
#include "Materials/Material.h"
|
||
|
|
#include "Engine/StaticMesh.h"
|
||
|
|
#include "Components/StaticMeshComponent.h"
|
||
|
|
|
||
|
|
#include "EditorToolAssetAPI.h"
|
||
|
|
#include "EditorComponentSourceFactory.h"
|
||
|
|
|
||
|
|
//#include "PhysicsEngine/BodySetup.h"
|
||
|
|
//#include "Interfaces/Interface_CollisionDataProvider.h"
|
||
|
|
|
||
|
|
//#define ENABLE_DEBUG_PRINTING
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class FEdModeToolsContextQueriesImpl : public IToolsContextQueriesAPI
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
UEdModeInteractiveToolsContext* ToolsContext;
|
||
|
|
FEdMode* EditorMode;
|
||
|
|
|
||
|
|
FViewCameraState CachedViewState;
|
||
|
|
|
||
|
|
FEdModeToolsContextQueriesImpl(UEdModeInteractiveToolsContext* Context, FEdMode* EditorModeIn)
|
||
|
|
{
|
||
|
|
ToolsContext = Context;
|
||
|
|
EditorMode = EditorModeIn;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CacheCurrentViewState(FEditorViewportClient* ViewportClient)
|
||
|
|
{
|
||
|
|
FViewportCameraTransform ViewTransform = ViewportClient->GetViewTransform();
|
||
|
|
CachedViewState.Position = ViewTransform.GetLocation();
|
||
|
|
CachedViewState.Orientation = ViewTransform.GetRotation().Quaternion();
|
||
|
|
CachedViewState.bIsOrthographic = ViewportClient->IsOrtho();
|
||
|
|
CachedViewState.bIsVR = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void GetCurrentSelectionState(FToolBuilderState& StateOut) const override
|
||
|
|
{
|
||
|
|
StateOut.ToolManager = ToolsContext->ToolManager;
|
||
|
|
StateOut.GizmoManager = ToolsContext->GizmoManager;
|
||
|
|
StateOut.World = EditorMode->GetWorld();
|
||
|
|
StateOut.SelectedActors = EditorMode->GetModeManager()->GetSelectedActors();
|
||
|
|
StateOut.SelectedComponents = EditorMode->GetModeManager()->GetSelectedComponents();
|
||
|
|
StateOut.SourceBuilder = ToolsContext->GetComponentSourceFactory();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
virtual void GetCurrentViewState(FViewCameraState& StateOut) const override
|
||
|
|
{
|
||
|
|
StateOut = CachedViewState;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
virtual bool ExecuteSceneSnapQuery(const FSceneSnapQueryRequest& Request, TArray<FSceneSnapQueryResult>& Results) const override
|
||
|
|
{
|
||
|
|
if (Request.RequestType != ESceneSnapQueryType::Position)
|
||
|
|
{
|
||
|
|
return false; // not supported yet
|
||
|
|
}
|
||
|
|
|
||
|
|
int FoundResultCount = 0;
|
||
|
|
|
||
|
|
//
|
||
|
|
// Run a snap query by casting ray into the world.
|
||
|
|
// If a hit is found, we look up what triangle was hit, and then test its vertices and edges
|
||
|
|
//
|
||
|
|
|
||
|
|
// cast ray into world
|
||
|
|
FVector RayStart = CachedViewState.Position;
|
||
|
|
FVector RayDirection = Request.Position - RayStart; RayDirection.Normalize();
|
||
|
|
FVector RayEnd = RayStart + 9999999 * RayDirection;
|
||
|
|
FCollisionObjectQueryParams ObjectQueryParams(FCollisionObjectQueryParams::AllObjects);
|
||
|
|
FCollisionQueryParams QueryParams = FCollisionQueryParams::DefaultQueryParam;
|
||
|
|
QueryParams.bTraceComplex = true;
|
||
|
|
QueryParams.bReturnFaceIndex = true;
|
||
|
|
FHitResult HitResult;
|
||
|
|
bool bHitWorld = EditorMode->GetWorld()->LineTraceSingleByObjectType(HitResult, RayStart, RayEnd, ObjectQueryParams, QueryParams);
|
||
|
|
if (bHitWorld && HitResult.FaceIndex >= 0)
|
||
|
|
{
|
||
|
|
float VisualAngle = OpeningAngleDeg(Request.Position, HitResult.ImpactPoint, RayStart);
|
||
|
|
//UE_LOG(LogTemp, Warning, TEXT("[HIT] visualangle %f faceindex %d"), VisualAngle, HitResult.FaceIndex);
|
||
|
|
if (VisualAngle < Request.VisualAngleThresholdDegrees)
|
||
|
|
{
|
||
|
|
UPrimitiveComponent* Component = HitResult.Component.Get();
|
||
|
|
if (Cast<UStaticMeshComponent>(Component) != nullptr)
|
||
|
|
{
|
||
|
|
// HitResult.FaceIndex is apparently an index into the TriMeshCollisionData, not sure how
|
||
|
|
// to directly access it. Calling GetPhysicsTriMeshData is expensive!
|
||
|
|
//UBodySetup* BodySetup = Cast<UStaticMeshComponent>(Component)->GetBodySetup();
|
||
|
|
//UObject* CDPObj = BodySetup->GetOuter();
|
||
|
|
//IInterface_CollisionDataProvider* CDP = Cast<IInterface_CollisionDataProvider>(CDPObj);
|
||
|
|
//FTriMeshCollisionData TriMesh;
|
||
|
|
//CDP->GetPhysicsTriMeshData(&TriMesh, true);
|
||
|
|
//FTriIndices Triangle = TriMesh.Indices[HitResult.FaceIndex];
|
||
|
|
//FVector Positions[3] = { TriMesh.Vertices[Triangle.v0], TriMesh.Vertices[Triangle.v1], TriMesh.Vertices[Triangle.v2] };
|
||
|
|
|
||
|
|
// physics collision data is created from StaticMesh RenderData
|
||
|
|
// so use HitResult.FaceIndex to extract triangle from the LOD0 mesh
|
||
|
|
// (note: this may be incorrect if there are multiple sections...in that case I think we have to
|
||
|
|
// first find section whose accumulated index range would contain .FaceIndexX)
|
||
|
|
UStaticMesh* StaticMesh = Cast<UStaticMeshComponent>(Component)->GetStaticMesh();
|
||
|
|
FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[0];
|
||
|
|
FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();
|
||
|
|
int32 TriIdx = 3 * HitResult.FaceIndex;
|
||
|
|
FVector Positions[3];
|
||
|
|
Positions[0] = LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(Indices[TriIdx]);
|
||
|
|
Positions[1] = LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(Indices[TriIdx+1]);
|
||
|
|
Positions[2] = LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(Indices[TriIdx+2]);
|
||
|
|
|
||
|
|
// transform to world space
|
||
|
|
FTransform ComponentTransform = Component->GetComponentTransform();
|
||
|
|
Positions[0] = ComponentTransform.TransformPosition(Positions[0]);
|
||
|
|
Positions[1] = ComponentTransform.TransformPosition(Positions[1]);
|
||
|
|
Positions[2] = ComponentTransform.TransformPosition(Positions[2]);
|
||
|
|
|
||
|
|
FSceneSnapQueryResult SnapResult;
|
||
|
|
SnapResult.TriVertices[0] = Positions[0];
|
||
|
|
SnapResult.TriVertices[1] = Positions[1];
|
||
|
|
SnapResult.TriVertices[2] = Positions[2];
|
||
|
|
|
||
|
|
// try snapping to vertices
|
||
|
|
float SmallestAngle = Request.VisualAngleThresholdDegrees;
|
||
|
|
if ( (Request.TargetTypes & ESceneSnapQueryTargetType::MeshVertex) != ESceneSnapQueryTargetType::None)
|
||
|
|
{
|
||
|
|
for (int j = 0; j < 3; ++j)
|
||
|
|
{
|
||
|
|
VisualAngle = OpeningAngleDeg(Request.Position, Positions[j], RayStart);
|
||
|
|
if (VisualAngle < SmallestAngle)
|
||
|
|
{
|
||
|
|
SmallestAngle = VisualAngle;
|
||
|
|
SnapResult.Position = Positions[j];
|
||
|
|
SnapResult.TargetType = ESceneSnapQueryTargetType::MeshVertex;
|
||
|
|
SnapResult.TriSnapIndex = j;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// try snapping to nearest points on edges
|
||
|
|
if ( ((Request.TargetTypes & ESceneSnapQueryTargetType::MeshEdge) != ESceneSnapQueryTargetType::None) &&
|
||
|
|
(SnapResult.TargetType != ESceneSnapQueryTargetType::MeshVertex) )
|
||
|
|
{
|
||
|
|
for (int j = 0; j < 3; ++j)
|
||
|
|
{
|
||
|
|
FVector EdgeNearestPt = NearestSegmentPt(Positions[j], Positions[(j+1)%3], Request.Position);
|
||
|
|
VisualAngle = OpeningAngleDeg(Request.Position, EdgeNearestPt, RayStart);
|
||
|
|
if (VisualAngle < SmallestAngle )
|
||
|
|
{
|
||
|
|
SmallestAngle = VisualAngle;
|
||
|
|
SnapResult.Position = EdgeNearestPt;
|
||
|
|
SnapResult.TargetType = ESceneSnapQueryTargetType::MeshEdge;
|
||
|
|
SnapResult.TriSnapIndex = j;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// if we found a valid snap, return it
|
||
|
|
if (SmallestAngle < Request.VisualAngleThresholdDegrees)
|
||
|
|
{
|
||
|
|
SnapResult.TargetActor = HitResult.Actor.Get();
|
||
|
|
SnapResult.TargetComponent = HitResult.Component.Get();
|
||
|
|
Results.Add(SnapResult);
|
||
|
|
FoundResultCount++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return (FoundResultCount > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//@ todo this are mirrored from GeometryProcessing, which is still experimental...replace w/ direct calls once GP component is standardized
|
||
|
|
static float OpeningAngleDeg(FVector A, FVector B, const FVector& P)
|
||
|
|
{
|
||
|
|
A -= P;
|
||
|
|
A.Normalize();
|
||
|
|
B -= P;
|
||
|
|
B.Normalize();
|
||
|
|
float Dot = FMath::Clamp(FVector::DotProduct(A,B), -1.0f, 1.0f);
|
||
|
|
return acos(Dot) * (180.0f / 3.141592653589f);
|
||
|
|
}
|
||
|
|
static FVector NearestSegmentPt(FVector A, FVector B, const FVector& P)
|
||
|
|
{
|
||
|
|
FVector Direction = (B - A);
|
||
|
|
float Length = Direction.Size();
|
||
|
|
Direction /= Length;
|
||
|
|
float t = FVector::DotProduct( (P - A), Direction);
|
||
|
|
if (t >= Length)
|
||
|
|
{
|
||
|
|
return B;
|
||
|
|
}
|
||
|
|
if (t <= 0)
|
||
|
|
{
|
||
|
|
return A;
|
||
|
|
}
|
||
|
|
return A + t * Direction;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
virtual UMaterialInterface* GetStandardMaterial(EStandardToolContextMaterials MaterialType) const
|
||
|
|
{
|
||
|
|
if (MaterialType == EStandardToolContextMaterials::VertexColorMaterial)
|
||
|
|
{
|
||
|
|
return ToolsContext->StandardVertexColorMaterial;
|
||
|
|
}
|
||
|
|
check(false);
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class FEdModeToolsContextTransactionImpl : public IToolsContextTransactionsAPI
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
UEdModeInteractiveToolsContext* ToolsContext;
|
||
|
|
FEdMode* EditorMode;
|
||
|
|
|
||
|
|
FEdModeToolsContextTransactionImpl(UEdModeInteractiveToolsContext* Context, FEdMode* EditorModeIn)
|
||
|
|
{
|
||
|
|
ToolsContext = Context;
|
||
|
|
EditorMode = EditorModeIn;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
virtual void PostMessage(const TCHAR* Message, EToolMessageLevel Level) override
|
||
|
|
{
|
||
|
|
UE_LOG(LogTemp, Warning, TEXT("[ToolsContext] %s"), Message);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
virtual void PostInvalidation() override
|
||
|
|
{
|
||
|
|
ToolsContext->PostInvalidation();
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void BeginUndoTransaction(const FText& Description) override
|
||
|
|
{
|
||
|
|
GEditor->BeginTransaction(Description);
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void EndUndoTransaction() override
|
||
|
|
{
|
||
|
|
GEditor->EndTransaction();
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void AppendChange(UObject* TargetObject, TUniquePtr<FChange> Change, const FText& Description) override
|
||
|
|
{
|
||
|
|
FScopedTransaction Transaction(Description);
|
||
|
|
check(GUndo != nullptr);
|
||
|
|
GUndo->StoreUndo(TargetObject, MoveTemp(Change));
|
||
|
|
// end transaction
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
virtual bool RequestSelectionChange(const FSelectedOjectsChangeList& SelectionChange) override
|
||
|
|
{
|
||
|
|
checkf(SelectionChange.Components.Num() == 0, TEXT("FEdModeToolsContextTransactionImpl::RequestSelectionChange - Component selection not supported yet"));
|
||
|
|
|
||
|
|
if (SelectionChange.ModificationType == ESelectedObjectsModificationType::Clear)
|
||
|
|
{
|
||
|
|
GEditor->SelectNone(true, true, false);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SelectionChange.ModificationType == ESelectedObjectsModificationType::Replace )
|
||
|
|
{
|
||
|
|
GEditor->SelectNone(false, true, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool bAdd = (SelectionChange.ModificationType != ESelectedObjectsModificationType::Remove);
|
||
|
|
int NumActors = SelectionChange.Actors.Num();
|
||
|
|
for (int k = 0; k < NumActors; ++k)
|
||
|
|
{
|
||
|
|
GEditor->SelectActor(SelectionChange.Actors[k], bAdd, false, true, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
GEditor->NoteSelectionChange(true);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
UEdModeInteractiveToolsContext::UEdModeInteractiveToolsContext()
|
||
|
|
{
|
||
|
|
EditorMode = nullptr;
|
||
|
|
QueriesAPI = nullptr;
|
||
|
|
TransactionAPI = nullptr;
|
||
|
|
AssetAPI = nullptr;
|
||
|
|
SourceFactory = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::Initialize(IToolsContextQueriesAPI* QueriesAPIIn, IToolsContextTransactionsAPI* TransactionsAPIIn)
|
||
|
|
{
|
||
|
|
UInteractiveToolsContext::Initialize(QueriesAPIIn, TransactionsAPIIn);
|
||
|
|
|
||
|
|
BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddLambda([this](bool bSimulating)
|
||
|
|
{
|
||
|
|
TerminateActiveToolsOnPIEStart();
|
||
|
|
});
|
||
|
|
PreSaveWorldDelegateHandle = FEditorDelegates::PreSaveWorld.AddLambda([this](uint32 SaveFlags, UWorld* World)
|
||
|
|
{
|
||
|
|
TerminateActiveToolsOnSaveWorld();
|
||
|
|
});
|
||
|
|
|
||
|
|
bInvalidationPending = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::Shutdown()
|
||
|
|
{
|
||
|
|
FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle);
|
||
|
|
FEditorDelegates::PreSaveWorld.Remove(PreSaveWorldDelegateHandle);
|
||
|
|
|
||
|
|
UInteractiveToolsContext::Shutdown();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::InitializeContextFromEdMode(FEdMode* EditorModeIn)
|
||
|
|
{
|
||
|
|
this->EditorMode = EditorModeIn;
|
||
|
|
|
||
|
|
this->TransactionAPI = new FEdModeToolsContextTransactionImpl(this, EditorModeIn);
|
||
|
|
this->QueriesAPI = new FEdModeToolsContextQueriesImpl(this, EditorModeIn);
|
||
|
|
this->AssetAPI = new FEditorToolAssetAPI();
|
||
|
|
this->SourceFactory = new FEditorComponentSourceFactory();
|
||
|
|
|
||
|
|
Initialize(QueriesAPI, TransactionAPI);
|
||
|
|
|
||
|
|
// enable auto invalidation in Editor, because invalidating for all hover and capture events is unpleasant
|
||
|
|
this->InputRouter->bAutoInvalidateOnHover = true;
|
||
|
|
this->InputRouter->bAutoInvalidateOnCapture = true;
|
||
|
|
|
||
|
|
|
||
|
|
// set up standard materials
|
||
|
|
StandardVertexColorMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Game/Materials/VertexColor"));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::ShutdownContext()
|
||
|
|
{
|
||
|
|
Shutdown();
|
||
|
|
|
||
|
|
if (QueriesAPI != nullptr)
|
||
|
|
{
|
||
|
|
delete QueriesAPI;
|
||
|
|
QueriesAPI = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (TransactionAPI != nullptr)
|
||
|
|
{
|
||
|
|
delete TransactionAPI;
|
||
|
|
TransactionAPI = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (AssetAPI != nullptr)
|
||
|
|
{
|
||
|
|
delete AssetAPI;
|
||
|
|
AssetAPI = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SourceFactory != nullptr)
|
||
|
|
{
|
||
|
|
delete SourceFactory;
|
||
|
|
SourceFactory = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
this->EditorMode = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::TerminateActiveToolsOnPIEStart()
|
||
|
|
{
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Left))
|
||
|
|
{
|
||
|
|
EToolShutdownType ShutdownType = ToolManager->CanAcceptActiveTool(EToolSide::Left) ?
|
||
|
|
EToolShutdownType::Accept : EToolShutdownType::Cancel;
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Left, ShutdownType);
|
||
|
|
}
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Right))
|
||
|
|
{
|
||
|
|
EToolShutdownType ShutdownType = ToolManager->CanAcceptActiveTool(EToolSide::Right) ?
|
||
|
|
EToolShutdownType::Accept : EToolShutdownType::Cancel;
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Right, ShutdownType);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void UEdModeInteractiveToolsContext::TerminateActiveToolsOnSaveWorld()
|
||
|
|
{
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Left))
|
||
|
|
{
|
||
|
|
EToolShutdownType ShutdownType = ToolManager->CanAcceptActiveTool(EToolSide::Left) ?
|
||
|
|
EToolShutdownType::Accept : EToolShutdownType::Cancel;
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Left, ShutdownType);
|
||
|
|
}
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Right))
|
||
|
|
{
|
||
|
|
EToolShutdownType ShutdownType = ToolManager->CanAcceptActiveTool(EToolSide::Right) ?
|
||
|
|
EToolShutdownType::Accept : EToolShutdownType::Cancel;
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Right, ShutdownType);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::PostInvalidation()
|
||
|
|
{
|
||
|
|
bInvalidationPending = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::Tick(FEditorViewportClient* ViewportClient, float DeltaTime)
|
||
|
|
{
|
||
|
|
ToolManager->Tick(DeltaTime);
|
||
|
|
GizmoManager->Tick(DeltaTime);
|
||
|
|
|
||
|
|
if (bInvalidationPending)
|
||
|
|
{
|
||
|
|
ViewportClient->Invalidate();
|
||
|
|
bInvalidationPending = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// save this view
|
||
|
|
// Check against GCurrentLevelEditingViewportClient is temporary and should be removed in future.
|
||
|
|
// Current issue is that this ::Tick() is called *per viewport*, so once for each view in a 4-up view.
|
||
|
|
if (ViewportClient == GCurrentLevelEditingViewportClient)
|
||
|
|
{
|
||
|
|
((FEdModeToolsContextQueriesImpl*)this->QueriesAPI)->CacheCurrentViewState(ViewportClient);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class TempRenderContext : public IToolsContextRenderAPI
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
FPrimitiveDrawInterface* PDI;
|
||
|
|
|
||
|
|
virtual FPrimitiveDrawInterface* GetPrimitiveDrawInterface() override
|
||
|
|
{
|
||
|
|
return PDI;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
void UEdModeInteractiveToolsContext::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI)
|
||
|
|
{
|
||
|
|
TempRenderContext RenderContext;
|
||
|
|
RenderContext.PDI = PDI;
|
||
|
|
ToolManager->Render(&RenderContext);
|
||
|
|
GizmoManager->Render(&RenderContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
if (Event == IE_Pressed) { UE_LOG(LogTemp, Warning, TEXT("PRESSED EVENT")); }
|
||
|
|
else if (Event == IE_Released) { UE_LOG(LogTemp, Warning, TEXT("RELEASED EVENT")); }
|
||
|
|
else if (Event == IE_Repeat) { UE_LOG(LogTemp, Warning, TEXT("REPEAT EVENT")); }
|
||
|
|
else if (Event == IE_Axis) { UE_LOG(LogTemp, Warning, TEXT("AXIS EVENT")); }
|
||
|
|
else if (Event == IE_DoubleClick) { UE_LOG(LogTemp, Warning, TEXT("DOUBLECLICK EVENT")); }
|
||
|
|
#endif
|
||
|
|
|
||
|
|
bool bHandled = false;
|
||
|
|
|
||
|
|
|
||
|
|
// escape key cancels current tool
|
||
|
|
if (Key == EKeys::Escape && Event == IE_Released )
|
||
|
|
{
|
||
|
|
if (ToolManager->HasAnyActiveTool())
|
||
|
|
{
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Mouse))
|
||
|
|
{
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Mouse, EToolShutdownType::Cancel);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// enter key accepts current tool, or ends tool if it does not have accept state
|
||
|
|
if (Key == EKeys::Enter && Event == IE_Released && ToolManager->HasAnyActiveTool())
|
||
|
|
{
|
||
|
|
if (ToolManager->HasActiveTool(EToolSide::Mouse))
|
||
|
|
{
|
||
|
|
if (ToolManager->GetActiveTool(EToolSide::Mouse)->HasAccept())
|
||
|
|
{
|
||
|
|
if (ToolManager->CanAcceptActiveTool(EToolSide::Mouse))
|
||
|
|
{
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Mouse, EToolShutdownType::Accept);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Mouse, EToolShutdownType::Completed);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// if alt is down we do not process mouse event
|
||
|
|
if (ViewportClient->IsAltPressed())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Event == IE_Pressed || Event == IE_Released)
|
||
|
|
{
|
||
|
|
if (Key.IsMouseButton())
|
||
|
|
{
|
||
|
|
bool bIsLeftMouse = (Key == EKeys::LeftMouseButton);
|
||
|
|
bool bIsMiddleMouse = (Key == EKeys::MiddleMouseButton);
|
||
|
|
bool bIsRightMouse = (Key == EKeys::RightMouseButton);
|
||
|
|
|
||
|
|
if (bIsLeftMouse || bIsMiddleMouse || bIsRightMouse)
|
||
|
|
{
|
||
|
|
|
||
|
|
// early-out here if we are going to do camera manipulation
|
||
|
|
if (ViewportClient->IsAltPressed())
|
||
|
|
{
|
||
|
|
return bHandled;
|
||
|
|
}
|
||
|
|
|
||
|
|
FInputDeviceState InputState = CurrentMouseState;
|
||
|
|
InputState.InputDevice = EInputDevices::Mouse;
|
||
|
|
InputState.SetModifierKeyStates(
|
||
|
|
ViewportClient->IsShiftPressed(), ViewportClient->IsAltPressed(),
|
||
|
|
ViewportClient->IsCtrlPressed(), ViewportClient->IsCmdPressed());
|
||
|
|
|
||
|
|
if (bIsLeftMouse)
|
||
|
|
{
|
||
|
|
InputState.Mouse.Left.SetStates(
|
||
|
|
(Event == IE_Pressed), (Event == IE_Pressed), (Event == IE_Released));
|
||
|
|
CurrentMouseState.Mouse.Left.bDown = (Event == IE_Pressed);
|
||
|
|
}
|
||
|
|
else if (bIsMiddleMouse)
|
||
|
|
{
|
||
|
|
InputState.Mouse.Middle.SetStates(
|
||
|
|
(Event == IE_Pressed), (Event == IE_Pressed), (Event == IE_Released));
|
||
|
|
CurrentMouseState.Mouse.Middle.bDown = (Event == IE_Pressed);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
InputState.Mouse.Right.SetStates(
|
||
|
|
(Event == IE_Pressed), (Event == IE_Pressed), (Event == IE_Released));
|
||
|
|
CurrentMouseState.Mouse.Right.bDown = (Event == IE_Pressed);
|
||
|
|
}
|
||
|
|
|
||
|
|
InputRouter->PostInputEvent(InputState);
|
||
|
|
|
||
|
|
if (InputRouter->HasActiveMouseCapture())
|
||
|
|
{
|
||
|
|
// what is this about? MeshPaintMode has it...
|
||
|
|
ViewportClient->bLockFlightCamera = true;
|
||
|
|
bHandled = true; // indicate that we handled this event,
|
||
|
|
// which will disable camera movement/etc ?
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
//ViewportClient->bLockFlightCamera = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (Key.IsGamepadKey())
|
||
|
|
{
|
||
|
|
// not supported yet
|
||
|
|
}
|
||
|
|
else if (Key.IsTouch())
|
||
|
|
{
|
||
|
|
// not supported yet
|
||
|
|
}
|
||
|
|
else if (Key.IsFloatAxis() || Key.IsVectorAxis())
|
||
|
|
{
|
||
|
|
// not supported yet
|
||
|
|
}
|
||
|
|
else // is this definitely a keyboard key?
|
||
|
|
{
|
||
|
|
FInputDeviceState InputState;
|
||
|
|
InputState.InputDevice = EInputDevices::Keyboard;
|
||
|
|
InputState.SetModifierKeyStates(
|
||
|
|
ViewportClient->IsShiftPressed(), ViewportClient->IsAltPressed(),
|
||
|
|
ViewportClient->IsCtrlPressed(), ViewportClient->IsCmdPressed());
|
||
|
|
InputState.Keyboard.ActiveKey.Button = Key;
|
||
|
|
bool bPressed = (Event == IE_Pressed);
|
||
|
|
InputState.Keyboard.ActiveKey.SetStates(bPressed, bPressed, !bPressed);
|
||
|
|
InputRouter->PostInputEvent(InputState);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return bHandled;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::MouseEnter(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
UE_LOG(LogTemp, Warning, TEXT("MOUSE ENTER"));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
CurrentMouseState.Mouse.Position2D = FVector2D(x, y);
|
||
|
|
CurrentMouseState.Mouse.WorldRay = GetRayFromMousePos(ViewportClient, Viewport, x, y);
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
//UE_LOG(LogTemp, Warning, TEXT("MOUSE MOVE"));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
CurrentMouseState.Mouse.Position2D = FVector2D(x, y);
|
||
|
|
CurrentMouseState.Mouse.WorldRay = GetRayFromMousePos(ViewportClient, Viewport, x, y);
|
||
|
|
FInputDeviceState InputState = CurrentMouseState;
|
||
|
|
InputState.InputDevice = EInputDevices::Mouse;
|
||
|
|
|
||
|
|
InputState.SetModifierKeyStates(
|
||
|
|
ViewportClient->IsShiftPressed(), ViewportClient->IsAltPressed(),
|
||
|
|
ViewportClient->IsCtrlPressed(), ViewportClient->IsCmdPressed());
|
||
|
|
|
||
|
|
if (InputRouter->HasActiveMouseCapture())
|
||
|
|
{
|
||
|
|
// This state occurs if InputBehavior did not release capture on mouse release.
|
||
|
|
// UMultiClickSequenceInputBehavior does this, eg for multi-click draw-polygon sequences.
|
||
|
|
// It's not ideal though and maybe would be better done via multiple captures + hover...?
|
||
|
|
InputRouter->PostInputEvent(InputState);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
InputRouter->PostHoverInputEvent(InputState);
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::MouseLeave(FEditorViewportClient* ViewportClient, FViewport* Viewport)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
UE_LOG(LogTemp, Warning, TEXT("MOUSE LEAVE"));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::CapturedMouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InMouseX, int32 InMouseY)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
//UE_LOG(LogTemp, Warning, TEXT("CAPTURED MOUSE MOVE"));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// if alt is down we will not allow client to see this event
|
||
|
|
if (InViewportClient->IsAltPressed())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
FVector2D OldPosition = CurrentMouseState.Mouse.Position2D;
|
||
|
|
CurrentMouseState.Mouse.Position2D = FVector2D(InMouseX, InMouseY);
|
||
|
|
CurrentMouseState.Mouse.WorldRay = GetRayFromMousePos(InViewportClient, InViewport, InMouseX, InMouseY);
|
||
|
|
|
||
|
|
if (InputRouter->HasActiveMouseCapture())
|
||
|
|
{
|
||
|
|
FInputDeviceState InputState = CurrentMouseState;
|
||
|
|
InputState.InputDevice = EInputDevices::Mouse;
|
||
|
|
InputState.SetModifierKeyStates(
|
||
|
|
InViewportClient->IsShiftPressed(), InViewportClient->IsAltPressed(),
|
||
|
|
InViewportClient->IsCtrlPressed(), InViewportClient->IsCmdPressed());
|
||
|
|
InputState.Mouse.Delta2D = CurrentMouseState.Mouse.Position2D - OldPosition;
|
||
|
|
InputRouter->PostInputEvent(InputState);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::EndTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport)
|
||
|
|
{
|
||
|
|
#ifdef ENABLE_DEBUG_PRINTING
|
||
|
|
UE_LOG(LogTemp, Warning, TEXT("END TRACKING"));
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
FRay UEdModeInteractiveToolsContext::GetRayFromMousePos(FEditorViewportClient* ViewportClient, FViewport* Viewport, int MouseX, int MouseY)
|
||
|
|
{
|
||
|
|
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
|
||
|
|
ViewportClient->Viewport,
|
||
|
|
ViewportClient->GetScene(),
|
||
|
|
ViewportClient->EngineShowFlags)
|
||
|
|
.SetRealtimeUpdate(ViewportClient->IsRealtime()));
|
||
|
|
FSceneView* View = ViewportClient->CalcSceneView(&ViewFamily);
|
||
|
|
FViewportCursorLocation MouseViewportRay(View, (FEditorViewportClient*)Viewport->GetClient(), MouseX, MouseY);
|
||
|
|
|
||
|
|
return FRay(MouseViewportRay.GetOrigin(), MouseViewportRay.GetDirection(), true);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::CanStartTool(const FString& ToolTypeIdentifier) const
|
||
|
|
{
|
||
|
|
return (ToolManager->HasActiveTool(EToolSide::Mouse) == false) &&
|
||
|
|
(ToolManager->CanActivateTool(EToolSide::Mouse, ToolTypeIdentifier) == true);
|
||
|
|
}
|
||
|
|
bool UEdModeInteractiveToolsContext::ActiveToolHasAccept() const
|
||
|
|
{
|
||
|
|
return ToolManager->HasActiveTool(EToolSide::Mouse) &&
|
||
|
|
ToolManager->GetActiveTool(EToolSide::Mouse)->HasAccept();
|
||
|
|
}
|
||
|
|
bool UEdModeInteractiveToolsContext::CanAcceptActiveTool() const
|
||
|
|
{
|
||
|
|
return ToolManager->CanAcceptActiveTool(EToolSide::Mouse);
|
||
|
|
}
|
||
|
|
bool UEdModeInteractiveToolsContext::CanCancelActiveTool() const
|
||
|
|
{
|
||
|
|
return ToolManager->CanCancelActiveTool(EToolSide::Mouse);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UEdModeInteractiveToolsContext::CanCompleteActiveTool() const
|
||
|
|
{
|
||
|
|
return ToolManager->HasActiveTool(EToolSide::Mouse) && CanCancelActiveTool() == false;
|
||
|
|
}
|
||
|
|
void UEdModeInteractiveToolsContext::StartTool(const FString& ToolTypeIdentifier)
|
||
|
|
{
|
||
|
|
if (ToolManager->SelectActiveToolType(EToolSide::Mouse, ToolTypeIdentifier) == false)
|
||
|
|
{
|
||
|
|
UE_LOG(LogTemp, Warning, TEXT("ToolManager: Unknown Tool Type %s"), *ToolTypeIdentifier);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ToolManager->ActivateTool(EToolSide::Mouse);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void UEdModeInteractiveToolsContext::EndTool(EToolShutdownType ShutdownType)
|
||
|
|
{
|
||
|
|
ToolManager->DeactivateTool(EToolSide::Mouse, ShutdownType);
|
||
|
|
}
|