Files

3418 lines
98 KiB
C++
Raw Permalink Normal View History

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "CoreMinimal.h"
#include "EngineDefines.h"
#include "Misc/MessageDialog.h"
#include "InputCoreTypes.h"
#include "GameFramework/Actor.h"
#include "Engine/World.h"
#include "SceneView.h"
#include "Model.h"
#include "Settings/LevelEditorViewportSettings.h"
#include "Engine/Brush.h"
#include "Settings/LevelEditorMiscSettings.h"
#include "Engine/Polys.h"
#include "Engine/Selection.h"
#include "EditorModeManager.h"
#include "EditorModes.h"
#include "Dialogs/Dialogs.h"
#include "BSPOps.h"
#include "GeometryEdMode.h"
#include "EditorGeometry.h"
#include "Engine/BrushShape.h"
#include "EditorSupportDelegates.h"
#include "ScopedTransaction.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "LevelEditorViewport.h"
#include "Layers/ILayers.h"
#include "ActorEditorUtils.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Components/BrushComponent.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "GeomModifier.h"
#include "GeomModifier_Edit.h"
#include "GeomModifier_Clip.h"
#include "GeomModifier_Create.h"
#include "GeomModifier_Delete.h"
#include "GeomModifier_Extrude.h"
#include "GeomModifier_Flip.h"
#include "GeomModifier_Lathe.h"
#include "GeomModifier_Pen.h"
#include "GeomModifier_Split.h"
#include "GeomModifier_Triangulate.h"
#include "GeomModifier_Optimize.h"
#include "GeomModifier_Turn.h"
#include "GeomModifier_Weld.h"
DEFINE_LOG_CATEGORY_STATIC(LogGeomModifier, Log, All);
#define LOCTEXT_NAMESPACE "UnrealEd.GeomModifier"
static FVector ComputeWorldSpaceMousePos( FEditorViewportClient* ViewportClient )
{
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
ViewportClient->Viewport,
ViewportClient->GetScene(),
ViewportClient->EngineShowFlags)
.SetRealtimeUpdate( ViewportClient->IsRealtime() ));
FSceneView* View = ViewportClient->CalcSceneView(&ViewFamily);
// Note only works for ortho viewports
return View->PixelToWorld(ViewportClient->Viewport->GetMouseX(),ViewportClient->Viewport->GetMouseY(),0.5f);
}
UGeomModifier::UGeomModifier(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bPushButton = false;
bInitialized = false;
bPendingPivotOffsetUpdate = false;
CachedPolys = NULL;
}
const FText& UGeomModifier::GetModifierDescription() const
{
return Description;
}
const FText& UGeomModifier::GetModifierTooltip() const
{
return Tooltip;
}
void UGeomModifier::Initialize()
{
}
bool UGeomModifier::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
return false;
}
bool UGeomModifier::InputDelta(FEditorViewportClient* InViewportClient,FViewport* InViewport,FVector& InDrag,FRotator& InRot,FVector& InScale)
{
if( GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
if( !bInitialized )
{
Initialize();
bInitialized = true;
}
}
return false;
}
bool UGeomModifier::Apply()
{
bool bResult = false;
if( GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
StartTrans();
bResult = OnApply();
EndTrans();
EndModify();
}
return bResult;
}
bool UGeomModifier::OnApply()
{
return false;
}
bool UGeomModifier::Supports()
{
return true;
}
void UGeomModifier::GeomError(const FString& InErrorMsg)
{
FMessageDialog::Open( EAppMsgType::Ok, FText::Format( NSLOCTEXT("UnrealEd", "Error_Modifier", "Modifier ({0}) : {1}"), GetModifierDescription(), FText::FromString(InErrorMsg) ) );
}
bool UGeomModifier::StartModify()
{
bInitialized = false;
bPendingPivotOffsetUpdate = false;
return false;
}
bool UGeomModifier::EndModify()
{
if (bPendingPivotOffsetUpdate)
{
UpdatePivotOffset();
}
StoreAllCurrentGeomSelections();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
return true;
}
void UGeomModifier::Render(const FSceneView* View,FViewport* Viewport,FPrimitiveDrawInterface* PDI)
{
}
void UGeomModifier::DrawHUD(FEditorViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View,FCanvas* Canvas)
{
}
void UGeomModifier::CacheBrushState()
{
FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();
if( !CachedPolys )
{
//Create the list of polys
CachedPolys = NewObject<UPolys>(this);
}
CachedPolys->Element.Empty();
//Create duplicates of all of the polys in the brush
for( int32 polyIndex = 0 ; polyIndex < BuilderBrush->Brush->Polys->Element.Num() ; ++polyIndex )
{
FPoly currentPoly = BuilderBrush->Brush->Polys->Element[polyIndex];
FPoly newPoly;
newPoly.Init();
newPoly.Base = currentPoly.Base;
//Add all of the verts to the new poly
for( int32 vertIndex = 0; vertIndex < currentPoly.Vertices.Num(); ++vertIndex )
{
FVector newVertex = currentPoly.Vertices[vertIndex];
newPoly.Vertices.Add( newVertex );
}
CachedPolys->Element.Add(newPoly);
}
}
void UGeomModifier::RestoreBrushState()
{
FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();
//Remove all of the current polys
BuilderBrush->Brush->Polys->Element.Empty();
//Add all of the cached polys
for( int32 polyIndex = 0 ; polyIndex < CachedPolys->Element.Num() ; polyIndex++ )
{
BuilderBrush->Brush->Polys->Element.Push(CachedPolys->Element[polyIndex]);
}
BuilderBrush->Brush->BuildBound();
BuilderBrush->ReregisterAllComponents();
GeomMode->FinalizeSourceData();
GeomMode->GetFromSource();
GEditor->SelectNone( true, true );
GEditor->RedrawLevelEditingViewports(true);
//Tell the user what just happened
FMessageDialog::Debugf(LOCTEXT("InvalidBrushState", "Invalid brush state could fail to triangulate. Reverting to previous state."));
}
bool UGeomModifier::DoEdgesOverlap()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
//Loop through all of the geometry objects
for( FEdModeGeometry::TGeomObjectIterator itor( mode->GeomObjectItor() ) ; itor ; ++itor )
{
FGeomObjectPtr geomObject = *itor;
//Loop through all of the edges
for( int32 edgeIndex1 = 0 ; edgeIndex1 < geomObject->EdgePool.Num() ; ++edgeIndex1 )
{
FGeomEdge* edge1 = &geomObject->EdgePool[edgeIndex1];
for( int32 edgeIndex2 = 0 ; edgeIndex2 < geomObject->EdgePool.Num() ; ++edgeIndex2 )
{
FGeomEdge* edge2 = &geomObject->EdgePool[edgeIndex2];
//Don't compare an edge with itself
if( !(edge1->IsSameEdge(*edge2)) )
{
FVector closestPoint1, closestPoint2;
FVector edge1Vert1 = geomObject->VertexPool[ edge1->VertexIndices[0] ];
FVector edge2Vert1 = geomObject->VertexPool[ edge2->VertexIndices[0] ];
FVector edge1Vert2 = geomObject->VertexPool[ edge1->VertexIndices[1] ];
FVector edge2Vert2 = geomObject->VertexPool[ edge2->VertexIndices[1] ];
//Find the distance between the two segments
FMath::SegmentDistToSegment( edge1Vert1, edge1Vert2, edge2Vert1, edge2Vert2, closestPoint1, closestPoint2 );
if ( (closestPoint1.Equals(closestPoint2)) )
{
//Identical closest points indicates that lines cross
bool bSharedVertex = ((edge1Vert1.Equals(edge2Vert1)) || (edge1Vert1.Equals(edge2Vert2))
|| (edge1Vert2.Equals(edge2Vert1)) || (edge1Vert2.Equals(edge2Vert2)));
// Edges along the same line are exempt
if ( !bSharedVertex )
{
bool bIntersectionIsVert = ((edge1Vert1.Equals(closestPoint2, THRESH_POINTS_ARE_SAME)) || (edge1Vert2.Equals(closestPoint2, THRESH_POINTS_ARE_SAME))
|| (edge2Vert1.Equals(closestPoint2, THRESH_POINTS_ARE_SAME )) || (edge2Vert2.Equals(closestPoint2, THRESH_POINTS_ARE_SAME)) );
// Edges intersecting at a vertex are exempt
if ( !bIntersectionIsVert )
{
// Edges cross. The shape drawn with this brush will likely be undesireable
return true;
}
}
}
}
}
}
}
return false;
}
void UGeomModifier::UpdatePivotOffset()
{
if (!GetDefault<ULevelEditorMiscSettings>()->bAutoMoveBSPPivotOffset)
{
return;
}
FEdModeGeometry* Mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
for (FEdModeGeometry::TGeomObjectIterator It(Mode->GeomObjectItor()); It; ++It)
{
FGeomObjectPtr GeomObject = *It;
ABrush* Brush = GeomObject->GetActualBrush();
TSet<FVector> UniqueVertices;
FVector VertexCenter = FVector::ZeroVector;
if (Brush->Brush && Brush->Brush->Polys)
{
for (const auto& Element : Brush->Brush->Polys->Element)
{
for (const auto& Vertex : Element.Vertices)
{
UniqueVertices.Add(Vertex);
}
}
for (const auto& Vertex : UniqueVertices)
{
VertexCenter += Vertex;
}
if (UniqueVertices.Num() > 0)
{
VertexCenter /= UniqueVertices.Num();
}
}
Brush->SetPivotOffset(VertexCenter);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Transaction tracking.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace {
/**
* @return The shared transaction object used by
*/
static FScopedTransaction*& StaticTransaction()
{
static FScopedTransaction* STransaction = NULL;
return STransaction;
}
/**
* Ends the outstanding transaction, if one exists.
*/
static void EndTransaction()
{
delete StaticTransaction();
StaticTransaction() = NULL;
}
/**
* Begins a new transaction, if no outstanding transaction exists.
*/
static void BeginTransaction(const FText& Description)
{
if ( !StaticTransaction() )
{
StaticTransaction() = new FScopedTransaction( Description );
}
}
} // namespace
void UGeomModifier::StartTrans()
{
if( !GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
return;
}
StoreAllCurrentGeomSelections();
// Start the transaction.
BeginTransaction( FText::Format( NSLOCTEXT("UnrealEd", "Modifier_F", "Modifier [{0}]"), GetModifierDescription() ) );
// Mark all selected brushes as modified.
FEdModeGeometry* CurMode = static_cast<FEdModeGeometry*>( GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry) );
for( FEdModeGeometry::TGeomObjectIterator Itor( CurMode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
ABrush* Actor = go->GetActualBrush();
Actor->Modify();
}
}
void UGeomModifier::EndTrans()
{
EndTransaction();
}
void UGeomModifier::StoreCurrentGeomSelections( TArray<struct FGeomSelection>& SelectionArray, FGeomObjectPtr go )
{
SelectionArray.Empty();
FGeomSelection* gs = NULL;
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex* gv = &go->VertexPool[v];
if( gv->IsSelected() )
{
gs = new( SelectionArray )FGeomSelection;
gs->Type = GS_Vertex;
gs->Index = v;
gs->SelectionIndex = gv->GetSelectionIndex();
}
}
for( int32 e = 0 ; e < go->EdgePool.Num() ; ++e )
{
FGeomEdge* ge = &go->EdgePool[e];
if( ge->IsSelected() )
{
gs = new( SelectionArray )FGeomSelection;
gs->Type = GS_Edge;
gs->Index = e;
gs->SelectionIndex = ge->GetSelectionIndex();
}
}
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() )
{
gs = new( SelectionArray )FGeomSelection;
gs->Type = GS_Poly;
gs->Index = p;
gs->SelectionIndex = gp->GetSelectionIndex();
}
}
}
void UGeomModifier::StoreAllCurrentGeomSelections()
{
if( !GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
return;
}
FEdModeGeometry* CurMode = static_cast<FEdModeGeometry*>( GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry) );
// Record the current selection list into the selected brushes.
for( FEdModeGeometry::TGeomObjectIterator Itor( CurMode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
go->CompileSelectionOrder();
ABrush* Actor = go->GetActualBrush();
StoreCurrentGeomSelections( Actor->SavedSelections , go );
}
}
UGeomModifier_Edit::UGeomModifier_Edit(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Edit", "Edit");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Edit", "Tooltip", "Translate, rotate or scale existing geometry.");
}
bool UGeomModifier_Edit::InputDelta(FEditorViewportClient* InViewportClient,FViewport* InViewport,FVector& InDrag,FRotator& InRot,FVector& InScale)
{
if( UGeomModifier::InputDelta( InViewportClient, InViewport, InDrag, InRot, InScale ) )
{
return true;
}
if( !GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
return false;
}
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
FModeTool_GeometryModify* tool = (FModeTool_GeometryModify*)mode->GetCurrentTool();
TArray<FGeomVertex*> UniqueVertexList;
/**
* All geometry objects can be manipulated by transforming the vertices that make
* them up. So based on the type of thing we're editing, we need to dig for those
* vertices a little differently.
*/
// Only permit scaling if there is exactly one selected poly. Make a note of it here
FGeomPoly* SelectedPoly = nullptr;
int32 NumSelectedPolys = 0;
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() )
{
SelectedPoly = gp;
NumSelectedPolys++;
for( int32 e = 0 ; e < gp->EdgeIndices.Num() ; ++e )
{
FGeomEdge* ge = &go->EdgePool[ gp->EdgeIndices[e] ];
UniqueVertexList.AddUnique( &go->VertexPool[ ge->VertexIndices[0] ] );
UniqueVertexList.AddUnique( &go->VertexPool[ ge->VertexIndices[1] ] );
}
}
}
for( int32 e = 0 ; e < go->EdgePool.Num() ; ++e )
{
FGeomEdge* ge = &go->EdgePool[e];
if( ge->IsSelected() )
{
UniqueVertexList.AddUnique( &go->VertexPool[ ge->VertexIndices[0] ] );
UniqueVertexList.AddUnique( &go->VertexPool[ ge->VertexIndices[1] ] );
}
}
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex* gv = &go->VertexPool[v];
if( gv->IsSelected() )
{
UniqueVertexList.AddUnique( gv );
}
}
}
// If we didn't move any vertices, then tell the caller that we didn't handle the input.
// This allows LDs to drag brushes around in geometry mode as long as no geometry
// objects are selected.
if( !UniqueVertexList.Num() )
{
return false;
}
const bool bShiftPressed = InViewportClient->IsShiftPressed();
// If we're trying to rotate vertices, only allow that if Shift is held down. This just makes it easier
// to rotate brushes around while working in geometry mode, since you rarely ever want to rotate vertices
FRotator FinalRot = InRot;
if( !bShiftPressed )
{
FinalRot = FRotator::ZeroRotator;
}
if( InDrag.IsZero() && FinalRot.IsZero() && InScale.IsZero() )
{
// No change, but handled
return true;
}
StartTrans();
// Let tool know that some modification has actually taken place
tool->bGeomModified = true;
/**
* Scaling needs to know the bounding box for the selected verts, so generate that before looping.
*/
Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 3293188) #rb none #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 3203880 on 2016/11/18 by Ori.Cohen Copying //UE4/Dev-Physics-Upgrade to Dev-Framework (//UE4/Dev-Framework) Change 3207429 on 2016/11/22 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3207285 Change 3252627 on 2017/01/10 by Lukasz.Furman removed duplicated entries from visual logger shape rendering #ue4 Change 3252675 on 2017/01/10 by Ori.Cohen Add support for tagged memory regions (Copying //Tasks/UE4/Dev-ImmediateModePhysics to Dev-Framework (//UE4/Dev-Framework)) Change 3252686 on 2017/01/10 by Ori.Cohen Refactor BodySetup to make it easier to reuse shape creation (Copying //Tasks/UE4/Dev-ImmediateModePhysics to Dev-Framework (//UE4/Dev-Framework)) Change 3252833 on 2017/01/10 by Ori.Cohen Refactor constraint so that it can be used for external solvers. (Copying //Tasks/UE4/Dev-ImmediateModePhysics to Dev-Framework (//UE4/Dev-Framework)) Change 3252887 on 2017/01/10 by Dan.Reynolds Increased modes to include: Harmonic minor Melodic minor (going up) Pentatonic (Major) Pentatonic (minor) Whole Tone Diminished (WH) and Blues Change 3252895 on 2017/01/10 by Aaron.McLeran update to music utilities. Change 3253060 on 2017/01/10 by Aaron.McLeran Updates to synthesis plugin and some new features to DSP objects Change 3253061 on 2017/01/10 by Aaron.McLeran Updates to music maps Change 3253078 on 2017/01/10 by Aaron.McLeran Removing pragma optimization code accidentally checked in Change 3253110 on 2017/01/10 by Ori.Cohen First iteration of immediate mode ragdoll node (Copying //Tasks/UE4/Dev-ImmediateModePhysics to Dev-Framework (//UE4/Dev-Framework)) Change 3253315 on 2017/01/10 by Aaron.McLeran Fixing a few bugs in DSP objects - Added a new types file EpicSynth1 and EpicSynth1 component can share enums Change 3253577 on 2017/01/11 by Aaron.McLeran Checking in updates to assets for music -- celestial manager for rotating objects like planets, new ambient map Change 3254052 on 2017/01/11 by Ori.Cohen Fix build. Change 3254059 on 2017/01/11 by Ori.Cohen Turn off html5 trying to build apex. Change 3254095 on 2017/01/11 by Ori.Cohen Fix build Change 3254200 on 2017/01/11 by Jon.Nabozny Make vectorized FTransform Accumulate (with blend) and AccumulateWithAdditive (with blend) consistent with the non-vectorized version and comments. #JIRA UE-40469 Change 3254334 on 2017/01/11 by Marc.Audy Put in missing virtual Change 3254397 on 2017/01/11 by dan.reynolds Updates to OtonOkeMap Change 3254410 on 2017/01/11 by Marc.Audy Cleanup autos Change 3254420 on 2017/01/11 by Marc.Audy PR #3110: Add missing IsInAudioThread check (Contributed by projectgheist) Modified somewhat, but based on what PR indicated as a problem. #jira UE-40369 Change 3254423 on 2017/01/11 by Marc.Audy Optimize GetDefaultSubobjectByName and GetDefaultSubobjects Remove autos Change 3254826 on 2017/01/11 by Aaron.McLeran Bringing optimizations to dev-framework Change 3254831 on 2017/01/11 by dan.reynolds Modified MidiSynthTestBP to use Program Change events to pull a Preset from a Preset Bank--added a Data Blueprint Object ES1Bank_Default (containing Preset arrays) with children classes for different classifications of Presets. Change 3254833 on 2017/01/11 by dan.reynolds Updating MidiSynthTestBP's default SynthPreset pan value. Change 3254851 on 2017/01/11 by dan.reynolds Updating ES1Bank_Bass Updating OtonOkeMap Change 3254854 on 2017/01/11 by Aaron.McLeran Some fixups for pan modulation Change 3255682 on 2017/01/12 by aaron.mcleran Turning the bass down a bit on OtonOkeMap Change 3255721 on 2017/01/12 by Marc.Audy Fix spelling error Change 3255790 on 2017/01/12 by Marc.Audy Copying //UE4/Dev-Physics-Upgrade to Dev-Framework (//UE4/Dev-Framework) Change 3256263 on 2017/01/12 by Ori.Cohen Refactor immediate mode api to take PxD6Joint and PxRigidActor instead. Change 3256288 on 2017/01/12 by Ori.Cohen Undo constraint refactor as we found a way around it and it made the code much harder to read/debug Change 3256360 on 2017/01/12 by Ori.Cohen Make sure physx actors passed into immediate mode are done so with proper locks (can probably improve this in the case where the actor is not even in the scene) Change 3256846 on 2017/01/13 by Marc.Audy Deprecate FBox/FBox2D int32 constructor because it makes no sense if you pass in a non 0 value. Use ForceInit instead. Change 3256954 on 2017/01/13 by Marc.Audy Fix missed fixup of deprecated constructor use Change 3257167 on 2017/01/13 by Jon.Nabozny Fix check in FBodyInstance::SetCollisionEnabled. Create convenience methods for HasPhysics and HasQuery. #jira UE-39633 Change 3257181 on 2017/01/13 by Zak.Parrish Adding input map and some testing content to Xenakis Change 3257183 on 2017/01/13 by Mieszko.Zielinski Implemented an improved navigation projection BP function that retrieves both projected locaiton as well as a boolean indicating if the projection succeeded #UE4 Also, did similar changes to GetRandomReachablePointInRadius and GetRandomPointInNavigableRadius #jira UE-40368 Change 3257211 on 2017/01/13 by Jon.Nabozny Fix CIS issue caused by 3257167. Change 3257220 on 2017/01/13 by Marc.Audy Additional FBox constructor deprecation fixups Change 3257236 on 2017/01/13 by zak.parrish Fixed error on Xenakis input pawn Change 3257242 on 2017/01/13 by zak.parrish Update to InputListener Change 3257273 on 2017/01/13 by Marc.Audy No reason to pass simple types by reference Change 3257418 on 2017/01/13 by Ori.Cohen Attempt to turn android physx libs back to static libs. Change 3257445 on 2017/01/13 by Ori.Cohen Turn android libs back to OBJ and removed unreal side linking as it seems we are now just merging into a single physx lib Change 3257903 on 2017/01/14 by Aaron.McLeran Additions to synth module and updates to dsp objects - Adding ability to create arbitrary modular patches from modulating sources to modulation destinations - DSP objects define their default depths but patches can override - Creating new SynthesisEditor module for synthesis plugin so we can create synthesis preset assets - Adding a preset bank type so we can store a bank of presets (aka factory presets) Change 3258179 on 2017/01/15 by Seth.Weedin Duplicating input test map for some FX work Change 3258181 on 2017/01/15 by Seth.Weedin Modify skybox in test map to be dark and spooky Change 3258183 on 2017/01/15 by aaron.johnson substituted classes, changed wind speed and adjusted level lighting Change 3258190 on 2017/01/15 by aaron.johnson substituted triplet pawn and motion controller classes, enabled grabbing animations Change 3258191 on 2017/01/15 by Aaron.McLeran Getting source effects working for GDC demo - Added new synthesis editor module to create instances of user-created source effects - Added code to do source effects - Modified old design to a newer, more simpler design for calling into client code to set parameters. No longer using the complex struct reflection design and instead just pass in the uobject preset the user created. They'll then cast it to the type that has the actual settings. - Tweaks and fixes to existing dsp objects to get source effects working - Modified existing engine code to allow for playing out source effect tails - Only supporting mono and stereo assets for source effect processing. Multi-channel effect processing is overly complex for this feature though we may extend the capabilities in the future. - Fixed issue of pitching with stereo delay effect on setting first interpolated param - Moving synth/dsp stuff in synthesis plugins into appropriate public/private folders in plugin/module - Deleting some cruft files no longer needed Change 3258201 on 2017/01/15 by Seth.Weedin C++ and BP classes for managing grid cells. Initial grid mapping tests. #rb none Change 3258206 on 2017/01/15 by aaron.johnson map push, triplets interface created, debug widget placed in level Change 3258222 on 2017/01/15 by Aaron.McLeran Fixing crash when there's a null entry in the source effect chain Fixed some zippering introduced by applying volume twice. Change 3258225 on 2017/01/15 by aaron.johnson Interface changes, pawn output values wip Change 3258228 on 2017/01/15 by aaron.johnson Pawn should be outputting all correct values for Tripletsinterface Change 3258242 on 2017/01/15 by Stanley.Hayes Edge lights and Spherical Density Materials Change 3258251 on 2017/01/16 by Seth.Weedin More progress on grid FX. Add curve strength modifiers, begin hooking up interaction. #rb none Change 3258284 on 2017/01/16 by Aaron.McLeran Fixing CIS build error Surprised that MSVC allows that... Change 3258525 on 2017/01/16 by Mieszko.Zielinski Made UGameplayTask::ResourceOverlapPolicy configurable via ini files #UE4 Change 3258537 on 2017/01/16 by Lukasz.Furman fixed duplicated & undo operations not updating navigation area in nav link proxy and nav link component #ue4 Change 3258595 on 2017/01/16 by Marc.Audy Fix static analysis warning Change 3259364 on 2017/01/16 by Mieszko.Zielinski BTTask_RotateToFaceBBEntry comment spelling fix #UE4 #jira UE-40669 Change 3259683 on 2017/01/16 by dan.reynolds Updated Preset Bank System implemented in MidiSynthTestBP and 4 Preset Banks have been started Change 3260244 on 2017/01/17 by Lina.Halper #anim - optimize layer blend node to not create mask weights in run-time but in compile time. #code review: Martin.Wilson Change 3260617 on 2017/01/17 by Ori.Cohen Immediate mode spawns its own actors. Change 3260701 on 2017/01/17 by Ori.Cohen Don't bother blending physics with animation when physics is QueryOnly Change 3260796 on 2017/01/17 by Ori.Cohen EndPhysics tick will no longer be scheduled if QueryOnly is used on a ragdoll. Change 3261207 on 2017/01/17 by Ori.Cohen First iteration of contact enabling/disabling for immediate mode. Change 3262010 on 2017/01/18 by Marc.Audy Remove some autos Change 3262525 on 2017/01/18 by Lina.Halper Fix crash with required bones index not using property indexing #jira: UE-40786 Change 3263658 on 2017/01/19 by Martin.Wilson Add AnimTechDemo to dev-framework (base third person + feng mao) Change 3263684 on 2017/01/19 by Lina.Halper #anim : layer node - fix allocation change I made by mistake Change 3264523 on 2017/01/19 by Ori.Cohen Immediate mode can now add static geometry it finds in the world. Also improve contact gen by caching iteration order Change 3264701 on 2017/01/19 by Ori.Cohen Make it so that immediate mode ragdolls collide with the ground in persona.This is a bit of an editor only hack which allows immediate mode to find non-static actors Change 3264980 on 2017/01/19 by Ori.Cohen Make sure physics asset collision disabled works in immediate mode. Change 3265011 on 2017/01/19 by Ori.Cohen Added the ability to override physics asset for immediate mode Change 3265030 on 2017/01/19 by Ori.Cohen Added override gravity for immediate mode. Change 3265650 on 2017/01/20 by Benn.Gallagher NvCloth Source Change 3265652 on 2017/01/20 by Benn.Gallagher NvCloth Lib #rnx Change 3265653 on 2017/01/20 by Benn.Gallagher NvCloth Bin #rnx Change 3266195 on 2017/01/20 by Danny.Bouimad Initial ClothTest Assets for NCloth Before and after comparison TM-MultiClothTest (Under Maps>Framework>Cloth) Change 3266377 on 2017/01/20 by Marc.Audy Ensure that OrphanedDataOnly and TrashClass blueprint generated classes are correctly considered a blueprint class for disregard for GC purposes. Change 3267873 on 2017/01/23 by Jon.Nabozny Fix SceneProxy shadowing in UGeometryCacheComponent. Change 3268025 on 2017/01/23 by Benn.Gallagher IWYU change, platform PCH generation seemed to hide this one. Change 3268026 on 2017/01/23 by Benn.Gallagher Fixed LOCTEXT_NAMESPACE being inconsistently scoped in an #if block #rnx Change 3268630 on 2017/01/23 by Zak.Parrish Updating to add MIGS shooter content, as well as audio interaction Blueprints Change 3268663 on 2017/01/23 by Ori.Cohen Ragdoll animnode uses raw physics asset pointer to ensure it makes a hard reference. Change 3268811 on 2017/01/23 by Ori.Cohen Added component space sim for immediate mode Change 3269369 on 2017/01/24 by Benn.Gallagher Copying //Tasks/UE4/Dev-UEFW-11-NewClothingPipeline to Dev-Framework (//UE4/Dev-Framework) Replaced clothing with new simulation framework Change 3269417 on 2017/01/24 by danny.bouimad Minor Update to cloth map for test Change 3269420 on 2017/01/24 by Benn.Gallagher Removed APEX simulation from clothing framework (used in testing, not fully complete) Change 3269421 on 2017/01/24 by danny.bouimad Small tweaks Change 3269515 on 2017/01/24 by Lukasz.Furman enabled gameplay debugger's OnSelectionChanged event support for both PIE and SIE modes fixed GameplayAbility debugger's category not using IAbilitySystemInterface #ue4 Change 3269595 on 2017/01/24 by mason.seay Break apart physics asset for crash bug Change 3269819 on 2017/01/24 by Ori.Cohen Make the possibly kinematic actor the first actor in the immediate mode joint. This is consistent with physx vanilla solver. Change 3270364 on 2017/01/24 by Josh.Stoddard upgrade to the latest version of v-HACD: https://github.com/kmammou/v-hacd/tree/master/src/VHACD_Lib commit: 7a09f9d NOTE: only updated windows binaries mac and linux still using old binaries until they can be tested #jira UE-40124 #rb josh.stoddard Change 3271188 on 2017/01/25 by Jurre.deBaare Post-import script support #jira UEFW-80 Change 3271249 on 2017/01/25 by Thomas.Sarkanen Move soundwave-internal curve tables to advanced display Exposing it was confusing to audio people Change 3271586 on 2017/01/25 by Marc.Audy Don't rerun construction scripts twice on a level that has been hidden and reshown #jira UE-40306 Change 3272048 on 2017/01/25 by Ori.Cohen Fix for immediate mode sim when root body is the same as the root bone. Change 3272083 on 2017/01/25 by Ori.Cohen Make sure to warn when component space sim and collision are used together. Also handle it gracefully. Change 3272300 on 2017/01/25 by Ori.Cohen Fix incorrect collision generation when a shape's local pose is not identity. Change 3273195 on 2017/01/26 by Jurre.deBaare Fix for Anim import script crash in GetBonePosesForTime Change 3273204 on 2017/01/26 by Ben.Marsh Ignore PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS and PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS macros between include directives. Fixes CIS warning with IncludeTool. Change 3273378 on 2017/01/26 by James.Golding In AnimBP editor, call CopyNodeDataToPreviewNode when properties are edited, not just pin defaults changed Change 3273381 on 2017/01/26 by James.Golding Big refactor to PoseDriver - RBF logic now moved into its own class/file - Allow editing of transform and radial scaling per-target - Add support for different falloff functions (not just Gaussian) - Allow driving curves directly, rather than always poses - Add details customization for pose driver node - Edits to PoseDriver settings now take immediate effect, don't need to recompile Change 3273826 on 2017/01/26 by Josh.Stoddard modify VHACD to improve quality of hulls generated by convex decomposition NOTE: mac libs not included - mac editor will use legacy libs for now Change 3273902 on 2017/01/26 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3273433 Change 3274018 on 2017/01/26 by Ori.Cohen Added immediate physics preview in phat. Change 3274165 on 2017/01/26 by Ori.Cohen PhAT now depends on immediate mode plugin. Fix build #JIRA UE-41179 Change 3275001 on 2017/01/27 by Jurre.deBaare Fix for crash in Persona with Anim Modifiers Change 3275297 on 2017/01/27 by Ori.Cohen Big refactor to iterate over shapes instead of bodies (allows multiple shape per body collision) Change 3275340 on 2017/01/27 by Benn.Gallagher Fixed Paragon clothing crashes during clothing upgrade step, fixed bone mapping not getting updated on reimport with different hierarchy #jira UE-41025 #jira UE-41039 Change 3275383 on 2017/01/27 by Benn.Gallagher Blacklisted double promotion warning on ps4 NvCloth build #rnx Change 3275426 on 2017/01/27 by Benn.Gallagher Removed CUDA dependencies from NvCloth cmake files Change 3275670 on 2017/01/27 by Ori.Cohen Fix phat ragdoll in immediate mode updating sketal mesh component transform Change 3275673 on 2017/01/27 by Ori.Cohen Add position/velocity iteration to immediate mode Change 3276001 on 2017/01/27 by Alan.Noon Migrated Immediate Mode Minion Ragdoll Content to GDC AnimTech Project. Updated DefaultInput.ini none Change 3276596 on 2017/01/28 by Aaron.McLeran Removing unused #ifdef Change 3276597 on 2017/01/28 by Aaron.McLeran Getting rid of static analysis warning Change 3277354 on 2017/01/30 by Lukasz.Furman fixed custom navlink Id collisions #ue4 Change 3277356 on 2017/01/30 by Lukasz.Furman fixed comments in GameplayDebugger.h #jira UE-41103 Change 3277371 on 2017/01/30 by mason.seay Test map for spawn sound/force feedback bug. Change 3277445 on 2017/01/30 by Lukasz.Furman fixed compilation warning #ue4 Change 3277560 on 2017/01/30 by Danny.Bouimad Made checkin to Fix Crash that occured due to bad content. Change 3277567 on 2017/01/30 by Ori.Cohen Fix immediate mode crashing when joint is empty. #JIRA UE-41026 Change 3277928 on 2017/01/30 by Ori.Cohen Turn on immediate mode plugin by default Change 3278433 on 2017/01/30 by Ori.Cohen Immediate mode supports heightfield collision. Change 3278449 on 2017/01/30 by Ori.Cohen Fix immediate mode cache not being initialized properly. Change 3278787 on 2017/01/31 by James.Golding Fix CIS error in ImmediatePhysicsSimulation.cpp Change 3279303 on 2017/01/31 by mason.seay Assets for RigidBody node bug Change 3279352 on 2017/01/31 by Benn.Gallagher Fixed inertia blends on self collision cloth assets as we now only have local space simulation and these values weren't used before Change 3279377 on 2017/01/31 by Alan.Noon GDC AnimTech Demo: adjusted minion physics assets none Change 3279425 on 2017/01/31 by james.cobbett Updating QA-Physics map. Made one of the simulated physics objects more user-friendly, able to enable/disable physics on key-press now. Change 3279436 on 2017/01/31 by Benn.Gallagher Fixed inertia scales on Owen mesh Change 3279480 on 2017/01/31 by Benn.Gallagher Fixes for clothing behavior changes #jira UE-41092 Change 3279495 on 2017/01/31 by Ori.Cohen Remove unneeded cache clearing when contact pairs are not skipped, but there is no collision. Change 3279579 on 2017/01/31 by james.cobbett Added new scenario to QA-Physics map. Moving platforms (up/down, left/right) with physics objects on them. Change 3279695 on 2017/01/31 by mason.seay RigidBody node test asset Change 3280105 on 2017/01/31 by Ori.Cohen Prevent query only ragdolls from simulating if their bodysetup is marked as simulated. Also remove slow check in term body for owning components. This is not true for destructibles or immediate mode Change 3280148 on 2017/01/31 by mason.seay First round of assets for force feedback testing Change 3280860 on 2017/02/01 by James.Golding Merge CL 3280853 to Dev-Framework Fix crash with null CurrentSkeleton on AnimInstance when using Re-import button in SkelMesh Editor Change 3281172 on 2017/02/01 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3281156 Change 3281210 on 2017/02/01 by james.cobbett Updated QA-Physics map Added cube that starts off with physics enabled, then disables. Made physics toggleable on that and another cube. Change 3281211 on 2017/02/01 by James.Golding Details customization for editing PoseDriver targets list Change 3281332 on 2017/02/01 by Marc.Audy Fix bad merge Fix file types Change 3281388 on 2017/02/01 by mason.seay Updated Force Feedback asset Change 3281396 on 2017/02/01 by mason.seay moving asset Change 3281987 on 2017/02/01 by Benn.Gallagher Fixed project generation failing after main merge Change 3282047 on 2017/02/01 by Marc.Audy Fix up Target and build cs files after changes from Dev-Build Change 3282214 on 2017/02/01 by Ori.Cohen Expose radial forces to immediate mode Change 3282221 on 2017/02/01 by Alan.Noon Immediate Mode GDC demo content: development on minion anim B, refined Orbital Laser Pawn controls, tweaked laser parameters none Change 3282273 on 2017/02/01 by Ori.Cohen Fix crash when recompiling animbp of immediate mode due to null pointer. Change 3282368 on 2017/02/01 by Ori.Cohen Quick iteration on minion demo Change 3282824 on 2017/02/02 by James.Golding Fix for CIS in RBFSolver.h Change 3282829 on 2017/02/02 by James.Golding Fix CIS in PoseDriverDetails.cpp Fix list UI not refreshing after copying targets from PoseAsset Change 3282834 on 2017/02/02 by Danny.Bouimad Adding Pose driver additive assets Change 3282863 on 2017/02/02 by James.Golding Add Mambo mesh and Skeleton Change 3282892 on 2017/02/02 by James.Golding Copy Aurora (Ice) and Mambo meshes/materials/some anims from Dev-General to AnimTechDemo project in Dev-Framework Change 3283157 on 2017/02/02 by Mieszko.Zielinski Cook Orion Win64 fix #UE4 Had to change the Extent param of K2_ProjectPointToNavigation. Updated the error causing Orion BP Change 3283159 on 2017/02/02 by Marc.Audy Additional CIS fixes Change 3283179 on 2017/02/02 by Marc.Audy More CIS fixes Change 3283197 on 2017/02/02 by Jurre.deBaare Fix for issues importing Fornite geometry cache assets #fix Use actual import number of frames instead of total number of frames in the Alembic Cache Change 3283201 on 2017/02/02 by Marc.Audy Keep fixing CIS Change 3283270 on 2017/02/02 by James.Golding Merging CL 3276013 to Dev-Framework - fix issue with additive pose preview applying twice Change 3283499 on 2017/02/02 by Marc.Audy More CIS fixes Change 3283543 on 2017/02/02 by Jon.Nabozny Update comment on AActor::GetActorBounds to properly reflect ChildActorComponents aren't included in the calculation. Change 3283663 on 2017/02/02 by Ori.Cohen Fix potential null dereference in ragdoll node Change 3283757 on 2017/02/02 by Marc.Audy May fix remaining CIS issues Change 3283984 on 2017/02/02 by Marc.Audy Fix linux CIS Change 3284039 on 2017/02/02 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3283913 Change 3284067 on 2017/02/02 by Marc.Audy Fixup mistakes in converting redirects Change 3284187 on 2017/02/02 by Ori.Cohen Immediate mode works with radial force (not just radial impulse) Change 3284358 on 2017/02/02 by Ori.Cohen Update arcblade phys asset for immediate mode Change 3284667 on 2017/02/02 by Marc.Audy Arguments is an array not a string now. Fixing commented out code. Change 3284684 on 2017/02/02 by Marc.Audy Move AVIWriter out in to its own module to avoid any possible unity build issues where xwindows.h got indirectly included through the DirectShow third party library and caused FGenericWindow::IsMaximized and IsMinimized to conflict with a macro. Change 3284707 on 2017/02/02 by Marc.Audy Fix AVIWriter module compilation on Mac Change 3285012 on 2017/02/03 by Benn.Gallagher Fixes for Dx NvCloth shader warnings Change 3285016 on 2017/02/03 by Marc.Audy Fix missing include Change 3285048 on 2017/02/03 by Benn.Gallagher Fixed Persona needing a restart when changing number of clothing assets (import/delete) #jira UE-41323 Change 3285325 on 2017/02/03 by Marc.Audy Properly implement AVIWriter module Change 3285538 on 2017/02/03 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3285499 Change 3285735 on 2017/02/03 by Jon.Nabozny Add IsInAir method to UVehicleWheel. #jira UE-38369 Change 3285862 on 2017/02/03 by Aaron.McLeran UE-41435 Fixing PIE audio - Fixing PIE audio. Recent change to editor preferences from Dev-Editor branch (CL 3234495) caused all audio to be muted in PIE. Change 3285914 on 2017/02/03 by danny.bouimad RecomputeTangents Test Assets Change 3286246 on 2017/02/03 by Mieszko.Zielinski Changes to game-specific BPs containing calls to deprecated NavigationSystem functions #UE4 #jira UE-41527 #jira UE-41518 Change 3286308 on 2017/02/03 by Ori.Cohen Make sure physx trimesh scale is never too small. Fix box clamping being ignored. Fixes cook warnings for Odin. #JIRA UE-41529 Change 3286396 on 2017/02/03 by Ori.Cohen Fix CIS Change 3286479 on 2017/02/03 by Ori.Cohen Copying //UE4/Dev-Physics-Upgrade to Dev-Framework (//UE4/Dev-Framework) Change 3287421 on 2017/02/06 by James.Golding Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3286819 Change 3287427 on 2017/02/06 by James.Golding Fix PoseBlendNode to 'pass through' if no poses are activated Change 3287430 on 2017/02/06 by James.Golding - Add support to PoseDriver for evaluating source bone in the space of a different bone - Fix driven bone adding a scale of 1 - Fix posedriver values 'sticking' (reset all weights to zero each frame) - Move CopyTargetsFromPoseAsset and AutoSetTargetScales from FAnimNode_PoseDriver to UAnimGraphNode_PoseDriver (not required outside editor) - Tranlsation targets now draw larger when selected - 'Copy from pose asset' now also auto-sets radius for you - Remove spammy warnings for missing poses/curves - Add UPoseAsset::GetNumTracks and ::GetFullPose - Remove unused ExtractionContext from UPoseAsset::GetBaseAnimationPose - Remove bIncludeRefPoseAsNeutralPose option (not really useful since we no longer always normalize weights to 1.0) Change 3287496 on 2017/02/06 by Chad.Garyet fixing busted quotes around defaultvalues Change 3287569 on 2017/02/06 by Mieszko.Zielinski Orion BP fixed after deprecating NavigationSystem's BP API #Orion Change 3287595 on 2017/02/06 by Benn.Gallagher BuildPhysX.Automation: Deploying PhysX & NvCloth Win64 Win32 PS4 libs. Built for new NvCloth upgrade Change 3287598 on 2017/02/06 by Benn.Gallagher NvCloth Upgrade to 21604115 Added Linux+Mac support Change 3287710 on 2017/02/06 by Lukasz.Furman added option to disable navlink polys at the end of generated paths #ue4 Change 3287857 on 2017/02/06 by Benn.Gallagher Fixed NvCloth module files to correctly set up linux and mac hopefully Change 3287894 on 2017/02/06 by Benn.Gallagher Another fix to NvCloth build files, didn't get picked up in VS for some reason. Change 3287917 on 2017/02/06 by Lina.Halper Copy from CharacterRigging to Dev-Framework #code review:Thomas.Sarkanen, Martin.Wilson, James.Golding, Andrew.Rodham Change 3287938 on 2017/02/06 by Thomas.Sarkanen Fix crash opening a media sound wave #jira UE-41582 - Editor crashes when running Automation test Change 3287942 on 2017/02/06 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3287682 Change 3288035 on 2017/02/06 by James.Golding Remove C++ GameMode and pawn classes (replace with floating BP instead) Resave anims to remove Orion refs Add simple AnimBP and map for Mambo testing Change 3288036 on 2017/02/06 by Benn.Gallagher Fix to BuildPhysX task to trigger Mac and Linux builds properly Change 3288125 on 2017/02/06 by Ori.Cohen Change PhysXCommon back to dylib Change 3288127 on 2017/02/06 by Benn.Gallagher Fixed project file identification not working for NvCloth under XCode Change 3288156 on 2017/02/06 by Benn.Gallagher Disable "expansion-to-defined" warning in Linux NvCloth builds Change 3288159 on 2017/02/06 by Lina.Halper potential compile fix for Ocean Editor #code review:Thomas.Sarkanen Change 3288190 on 2017/02/06 by Ori.Cohen Link against static PhysXCommon for mac Change 3288200 on 2017/02/06 by Marc.Audy Fix CIS Change 3288270 on 2017/02/06 by Lina.Halper fix compile error #code review:Thomas.Sarkanen, Marc.Audy Change 3288302 on 2017/02/06 by Thomas.Sarkanen Fixed ensure when deselecting bones in anim BP editor #jira UE-41274 - Ensure when clicking in the viewport of an animation blueprint Change 3288348 on 2017/02/06 by Lina.Halper - Enabled control rig - Changed plugin name to be Control Rig Change 3288490 on 2017/02/06 by Benn.Gallagher Fixes for Mac attempting static links against NvCloth and failing to load dynamic libraries. Worked with MasonS to get Mac editor up and running. Change 3288511 on 2017/02/06 by Lina.Halper compile fix Change 3288513 on 2017/02/06 by Lina.Halper Check in content to work with Change 3288615 on 2017/02/06 by Ori.Cohen Fix skeletal mesh not simulating when using an aggregate. #JIRA UE-41593 Change 3288791 on 2017/02/06 by thomas.sarkanen Exposed transforms to cinematics so they can be animated Change 3288795 on 2017/02/06 by Ori.Cohen Fix lock warnings for physx #JIRA UE-41591 Change 3288817 on 2017/02/06 by Charles.Anderson GDC Arcblade setup tests. Change 3288825 on 2017/02/06 by Lina.Halper Fix build issue of shadow variable Change 3289058 on 2017/02/06 by Ori.Cohen Fix crash when immediate mode constraint generates 0 rows. This is a potentially temporary fix until NVIDIA replies with a better solution. #JIRA UE-41026 Change 3289348 on 2017/02/06 by Lina.Halper fix compile issue Change 3289369 on 2017/02/06 by Lina.Halper Renamed leg control to limb control and will be used for arm/feet. - changed vars. - has unused variables that will be used soon but want to check in so that i don't block content change on BaseHuman. #code review:Thomas.Sakanen Change 3289422 on 2017/02/06 by Lina.Halper Fixed IK sinking issue - or moving #code review:Thomas.Sarkanen Change 3289433 on 2017/02/06 by Lina.Halper Fixed real shadow error Change 3289485 on 2017/02/06 by Lina.Halper fixed build issue Change 3289657 on 2017/02/07 by thomas.sarkanen Added rig bone mapping to Ice's skeletal mesh Change 3289658 on 2017/02/07 by thomas.sarkanen Added ControlRig map with Ice setup to pose Change 3289662 on 2017/02/07 by Thomas.Sarkanen Fixed up static analysis warning Change 3289663 on 2017/02/07 by Thomas.Sarkanen Fixed crash when attempting to bind to skeletal mesh with already-set anim BP Anim instance may not have actually been created when binding, so dont dereference it Change 3289717 on 2017/02/07 by Benn.Gallagher Switch Linux NvCloth to static for Linux builds. Adjust lib directory to match actual directory Change 3289718 on 2017/02/07 by Benn.Gallagher BuildPhysX.Automation: Deploying NvCloth Linux_x86_64-unknown-linux-gnu libs. Change 3289744 on 2017/02/07 by Benn.Gallagher Fixed missing masses causing crash initialising clothing actors #jira UE-41599 Change 3289746 on 2017/02/07 by Danny.Bouimad Adding Some Content for JamesG he wanted some nicer looking Pose driver test files. Change 3289756 on 2017/02/07 by danny.bouimad Changing the asset for JamesG. Change 3289785 on 2017/02/07 by James.Golding Replace old PoseDrive test with Danny's new one Change 3289858 on 2017/02/07 by Lina.Halper fixed issue with undo transaction buffer Change 3289860 on 2017/02/07 by Benn.Gallagher Fixed crash after reimporting a clothing asset with the clothing config open and then changing the confg #jira UE-41655 Change 3289912 on 2017/02/07 by Thomas.Sarkanen Merging using Raven_To_Dev-Framework Originally from CLs 3249471, 3258522, 3260271, 3273791: Sequencer: More work supporting array properties more generically + fixes Change 3289962 on 2017/02/07 by James.Golding Add thickness option to DrawWireDiamond Change 3289963 on 2017/02/07 by James.Golding Add spin option to VectorInputBox Change 3289966 on 2017/02/07 by James.Golding Add weight bar chart to PoseDriver details Stop drawing pose weight text in viewport Fix position targets not drawing larger when selected Change 3290094 on 2017/02/07 by Thomas.Sarkanen Fixed typo in filename (fallout from search and replace) Change 3290119 on 2017/02/07 by Thomas.Sarkanen Manipulators can now have their IK/FK space set on them They are not drawn when the space for the chain that they control is not the same as their setting Also fixed a crash with invalid objects when reloading maps. Change 3290145 on 2017/02/07 by Thomas.Sarkanen CIS fix for fallout from Raven changes #jira UE-41670 - Mac editor fails to compile with PropertyTrackEditor errors Change 3290319 on 2017/02/07 by Marc.Audy Make sound player nodes hard reference the assets unless they are in a chain below a quality node. Change 3290484 on 2017/02/07 by Richard.Hinckley Fixing grammar in popup messages. Change 3290533 on 2017/02/07 by Marc.Audy Make GetAIController BlueprintPure #jira UE-41654 Change 3290624 on 2017/02/07 by Marc.Audy Reorder header to avoid include tool warnings Change 3290697 on 2017/02/07 by Lina.Halper - support FK manipulator being in local space - fixed FK key spamming issue for making blend weight to be not keyable - this creates conflicts with enum #code review: Thomas.Sarkanen Change 3290748 on 2017/02/07 by Ori.Cohen Touch immediate mode file to force physx re-link Change 3290807 on 2017/02/07 by Richard.Hinckley #jira UE-39891 Updates to assist in automatic documentation generation. Change 3290946 on 2017/02/07 by Lina.Halper Fix issue of notify looping. #jira: UE-31463 #Code review:Martin.Wilson Change 3291553 on 2017/02/07 by Lina.Halper Rename/move file(s) - modified mesh mapping controller window to be Control Rig Change 3291571 on 2017/02/07 by Lina.Halper added set up spine option #code review:Thomas.Sarkanen Change 3291581 on 2017/02/07 by Ori.Cohen Temporarily turn off phat immediate mode preview which crashes. Change 3291949 on 2017/02/08 by James.Golding Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3291819 Change 3291966 on 2017/02/08 by Lina.Halper Fix issue with notify looping bug #jira: UE-31463 Change 3292247 on 2017/02/08 by Marc.Audy Clean up bad merge caused by Fortnite integration to main Change 3292326 on 2017/02/08 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3292313 Change 3292409 on 2017/02/08 by Marc.Audy Resubmit FortPawn.cpp with proper code even though perforce doesn't think there is a difference since when you sync it, the contents are wrong. Change 3292481 on 2017/02/08 by Ori.Cohen Fix for convex hull cooking (from Josh.S) #JIRA UE-41656 Change 3292492 on 2017/02/08 by Mieszko.Zielinski Redone replacement of deprecated navigation system's BP functions in Fortnite BPs #Fortnite Change 3292778 on 2017/02/08 by Ori.Cohen Touch physx DDC key for new cooking. #JIRA UE-41656 [CL 3293329 by Marc Audy in Main branch]
2017-02-08 17:53:41 -05:00
FBox VertBBox(ForceInit);
for( int32 x = 0 ; x < UniqueVertexList.Num() ; ++x )
{
VertBBox += *UniqueVertexList[x];
}
FVector BBoxExtent = VertBBox.GetExtent();
/**
* We first generate a list of unique vertices and then transform that list
* in one shot. This prevents vertices from being touched more than once (which
* would result in them transforming x times as fast as others).
*/
for( int32 x = 0 ; x < UniqueVertexList.Num() ; ++x )
{
FGeomVertex* vtx = UniqueVertexList[x];
const ABrush* Brush = vtx->GetParentObject()->GetActualBrush();
// Translate
if (!InDrag.IsZero())
{
*vtx += Brush->ActorToWorld().InverseTransformVector(InDrag);
}
// Rotate
if( !FinalRot.IsZero() )
{
const FRotationMatrix Matrix( FinalRot );
FVector Wk( vtx->X, vtx->Y, vtx->Z );
Wk = Brush->ActorToWorld().TransformPosition(Wk);
Wk -= GLevelEditorModeTools().PivotLocation;
Wk = Matrix.TransformPosition( Wk );
Wk += GLevelEditorModeTools().PivotLocation;
*vtx = Brush->ActorToWorld().InverseTransformPosition(Wk);
}
// Scale
if( !InScale.IsZero() && NumSelectedPolys == 1)
{
// This is a quick fix for now.
// Scale needs to know the surface normal of the polys selected (and scale only makes sense on polys, not edges or verts),
// so remember one selected poly and use that transform.
// Since scaling is relative to the pivot, it would actually be horrible scaling multiple polys at once anyway.
const FScaleMatrix Matrix(InScale + 1.0f);
const FVector PivotInModelSpace = Brush->ActorToWorld().InverseTransformPosition(GLevelEditorModeTools().PivotLocation);
const FRotationMatrix GeomBaseTransform = FRotationMatrix(SelectedPoly->GetWidgetRotation());
FVector Wk(vtx->X, vtx->Y, vtx->Z);
Wk -= PivotInModelSpace;
Wk = GeomBaseTransform.TransformPosition(Wk);
Wk = Matrix.TransformPosition(Wk);
Wk = GeomBaseTransform.InverseTransformPosition(Wk);
Wk += PivotInModelSpace;
*vtx = Wk;
}
}
if( DoEdgesOverlap() )
{
//Two edges overlap, which causes triangulation problems, so move the vertices back to their previous location
for( int32 x = 0 ; x < UniqueVertexList.Num() ; ++x )
{
FGeomVertex* vtx = UniqueVertexList[x];
const ABrush* Brush = vtx->GetParentObject()->GetActualBrush();
*vtx -= Brush->ActorToWorld().InverseTransformVector(InDrag);
}
GLevelEditorModeTools().PivotLocation -= InDrag;
GLevelEditorModeTools().SnappedLocation -= InDrag;
}
const bool bIsCtrlPressed = InViewportClient->IsCtrlPressed();
const bool bIsAltPressed = InViewportClient->IsAltPressed();
if(!InDrag.IsZero() && bShiftPressed && bIsCtrlPressed && !bIsAltPressed)
{
FVector CameraDelta(InDrag);
// Only apply camera speed modifiers to the drag if we aren't zooming in an ortho viewport.
if( !InViewportClient->IsOrtho() || !(InViewport->KeyState(EKeys::LeftMouseButton) && InViewport->KeyState(EKeys::RightMouseButton)) )
{
const float CameraSpeed = InViewportClient->GetCameraSpeed();
CameraDelta *= CameraSpeed;
}
InViewportClient->MoveViewportCamera( CameraDelta, InRot );
}
EndTrans();
bPendingPivotOffsetUpdate = true;
GEditor->RedrawLevelEditingViewports(true);
return true;
}
/*------------------------------------------------------------------------------
UGeomModifier_Extrude
------------------------------------------------------------------------------*/
UGeomModifier_Extrude::UGeomModifier_Extrude(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Extrude", "Extrude");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Extrude", "Tooltip", "Moves the selected geometry element forward, creating new geometry behind it if necessary.");
Length = 16;
Segments = 1;
}
Copying //UE4/Dev-Editor to Dev-Main (//UE4/Dev-Main) #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2771249 on 2015/11/18 by Joe.Tidmarsh Ensure that UCircularThrobber's Radius determines the widget's desired size when a child of UCanvasPanelSlot. #jira UE-23186 Change 2794402 on 2015/12/08 by Joe.Tidmarsh Reverting recent changes to Circular throbber. It's unintuative to enforce Size To Content. Will find some other solution. Change 2803507 on 2015/12/15 by Richard.TalbotWatkin BSP poly extrusion can now only be done in the normal direction of the poly. #jira UE-24168 - BSP face breaks off when extruding on Y or Z axes Change 2803510 on 2015/12/15 by Richard.TalbotWatkin Building new static mesh LODs now initializes override vertex colors based on LOD0. #jira UE-23747 - CLONE - if LODs are generated for meshes with vertex colors in a level the vertex colors dont propagate to the LOD in the level Change 2808877 on 2015/12/18 by Alexis.Matte Make sure the delta scale sign is swap when we have multiple axis with different sign current axis value #jira UE-21574 #codereview nick.darnell Change 2810114 on 2015/12/21 by Alexis.Matte #jira UE-23769 We now expose a message telling the user that we found some mesh that are not reference by any scene node in the fbx file. #codereview nick.darnell Change 2810211 on 2015/12/21 by Richard.TalbotWatkin Fixed issue with Show Only Selected not showing members of actor groups. #jira UE-24453 - CLONE - Show Selected is broken for certain Orion meshes Change 2811035 on 2015/12/22 by Alexis.Matte #jira UE-24671 Polish UI #codereview nick.darnell Change 2811123 on 2015/12/22 by Alexis.Matte #jira UE-21936 We now can decide which fbx sdk compatibility version we can use when exportting to a fbx file. #codereview nick.darnell Change 2812830 on 2015/12/28 by Richard.TalbotWatkin Prevent engine assets' properties from having project assets assigned to them. #jira UE-18215 - Details panel: prevent engine content from referencing game content Change 2812854 on 2015/12/28 by Richard.TalbotWatkin Fixed issue where floating windows were having their border size erroneously added again and again. Allowed PIE windows to not respect work area bounds if they are created centered, so that they can overlap off the edge of the screen. #jira UE-24465 - 10 Pixels Added to Width & Height of Floating Editor Windows Each Time Project is Reopened #jira UE-24364 - "Always Center Window to Screen" No Longer Functioning in New Editor Window (PIE) Change 2812875 on 2015/12/28 by Alexis.Matte #jira ue-22237 first implementation for skeletal mesh scene import and reimport. Small refator to remove duplicate code in different fbx list ui. #codereview nick.darnell Change 2813172 on 2015/12/29 by Alexis.Matte #jira ue-21656 Partial submit, the base code is there to add all light type with there properties. #codereview nick.darnell Change 2813403 on 2015/12/30 by Richard.TalbotWatkin PIE in New Editor Window now respects the Game Gets Mouse Control setting. This provides a workaround for UE-24824 where attempting to drag a PIE window fails due to the viewport capturing and locking the mouse to itself in FSceneViewport::OnFocusReceived. Change 2813429 on 2015/12/30 by Alexis.Matte #jira ue-21656 -spotlight and point light support fbx attenuation -fix the light orientation so now directional and spotlight point to the same direction of the fbx #codereview nick.darnell Change 2813456 on 2015/12/30 by Alexis.Matte #jira ue-21656 -Import the camera from fbx #codereview nick.darnell Change 2813457 on 2015/12/30 by Richard.TalbotWatkin Fixed issues with the code which determines whether the user is attempting to assign a game asset/class to an engine asset's property. #jira UE-18215 - Details panel: prevent engine content from referencing game content Change 2813475 on 2015/12/30 by Richard.TalbotWatkin Removed erroneous debug code. Change 2814451 on 2016/01/04 by Joe.Tidmarsh Fixed Tint colour for circular throbber. #jira UE-24445 Change 2814546 on 2016/01/04 by Richard.TalbotWatkin Force Message Log to update its category list if a new category is added while it is open. #jira UE-24266 - Message Log not updating Categories in Real-Time Change 2814613 on 2016/01/04 by Alexis.Matte [CL 2851481 by Nick Darnell in Main branch]
2016-02-01 14:57:29 -05:00
bool UGeomModifier_Extrude::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale)
{
FEdModeGeometry* Mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
const bool bGetRawValue = true;
const bool bIsLocalCoords = GLevelEditorModeTools().GetCoordSystem(bGetRawValue) == COORD_Local;
if (!bIsLocalCoords)
{
// Before the modal dialog has popped up, force tracking to stop and reset the focus
InViewportClient->LostFocus(InViewport);
InViewportClient->ReceivedFocus(InViewport);
CheckCoordinatesMode();
}
if (!bIsLocalCoords || Mode->GetCurrentWidgetAxis() != EAxisList::X)
{
InDrag = FVector::ZeroVector;
InRot = FRotator::ZeroRotator;
InScale = FVector::ZeroVector;
return false;
}
return Super::InputDelta(InViewportClient, InViewport, InDrag, InRot, InScale);
}
bool UGeomModifier_Extrude::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return mode->HavePolygonsSelected();
}
void UGeomModifier_Extrude::WasActivated()
{
// Extrude requires a local coordinate system to work properly so automatically enable
// that here while saving the current coordinate system for restoration later.
const bool bGetRawValue = true;
SaveCoordSystem = GLevelEditorModeTools().GetCoordSystem(bGetRawValue);
CheckCoordinatesMode();
GEditor->RedrawLevelEditingViewports(true);
}
void UGeomModifier_Extrude::WasDeactivated()
{
// When the user leaves this modifier, restore their old coordinate system.
GLevelEditorModeTools().SetCoordSystem((ECoordSystem)SaveCoordSystem);
GEditor->RedrawLevelEditingViewports(true);
}
void UGeomModifier_Extrude::CheckCoordinatesMode()
{
const bool bGetRawValue = true;
if( GLevelEditorModeTools().GetCoordSystem(bGetRawValue) != COORD_Local )
{
FSuppressableWarningDialog::FSetupInfo Info( LOCTEXT("ExtrudeCoordinateWarningBody","Extrude only works with Local Coordinates System"), LOCTEXT("ExtrudeCoordinateWarningTitle","Extrude Coordinates Mode Warning"), "ExtrudeCoordsWarning" );
Info.ConfirmText = LOCTEXT( "Close", "Close");
FSuppressableWarningDialog WarnAboutCoordinatesSystem( Info );
WarnAboutCoordinatesSystem.ShowModal();
GLevelEditorModeTools().SetCoordSystem(COORD_Local);
}
}
void UGeomModifier_Extrude::Initialize()
{
//the user may have changed the mode AFTER going into extrude - double check its LOCAL not WORLD
CheckCoordinatesMode();
Apply( GEditor->GetGridSize(), 1 );
}
bool UGeomModifier_Extrude::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// When applying via the keyboard, we force the local coordinate system.
const bool bGetRawValue = true;
const ECoordSystem SaveCS = GLevelEditorModeTools().GetCoordSystem(bGetRawValue);
GLevelEditorModeTools().SetCoordSystem(COORD_Local);
//GApp->DlgGeometryTools->PropertyWindow->FinalizeValues();
Apply( Length, Segments );
// Restore the coordinate system.
GLevelEditorModeTools().SetCoordSystem(SaveCS);
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
void ExtrudePolygonGroup( ABrush* InBrush, FVector InGroupNormal, int32 InStartOffset, int32 InLength, TArray<FPoly>& InPolygonGroup )
{
TArray< TArray<FVector> > Windings;
FPoly::GetOutsideWindings( InBrush, InPolygonGroup, Windings );
for( int32 w = 0 ; w < Windings.Num() ; ++w )
{
TArray<FVector>* WindingVerts = &Windings[w];
FVector Offset = InGroupNormal * InLength;
FVector StartOffset = InGroupNormal * InStartOffset;
for( int32 v = 0 ; v < WindingVerts->Num() ; ++v )
{
FVector vtx0 = StartOffset + (*WindingVerts)[ v ];
FVector vtx1 = StartOffset + (*WindingVerts)[ v ] + Offset;
FVector vtx2 = StartOffset + (*WindingVerts)[ (v + 1) % WindingVerts->Num() ] + Offset;
FVector vtx3 = StartOffset + (*WindingVerts)[ (v + 1) % WindingVerts->Num() ];
FPoly NewPoly;
NewPoly.Init();
NewPoly.Base = InBrush->GetActorLocation();
NewPoly.Vertices.Add( vtx1 );
NewPoly.Vertices.Add( vtx0 );
NewPoly.Vertices.Add( vtx3 );
NewPoly.Vertices.Add( vtx2 );
if( NewPoly.Finalize( InBrush, 1 ) == 0 )
{
InBrush->Brush->Polys->Element.Add( NewPoly );
}
}
}
}
void UGeomModifier_Extrude::Apply(int32 InLength, int32 InSegments)
{
if( !GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
return;
}
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// Force user input to be valid
InLength = FMath::Max( 1, InLength );
InSegments = FMath::Max( 1, InSegments );
//
TArray<int32> SavedSelectionIndices;
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
ABrush* Brush = go->GetActualBrush();
go->SendToSource();
TArray<FPoly> Polygons;
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
FVector Normal = Brush->GetActorQuat().Inverse().RotateVector( mode->GetWidgetNormalFromCurrentAxis( gp ) );
if( gp->IsSelected() )
{
SavedSelectionIndices.Add( p );
FPoly* Poly = gp->GetActualPoly();
Polygons.Add( *Poly );
// Move the existing poly along the normal by InLength units.
for( int32 v = 0 ; v < Poly->Vertices.Num() ; ++v )
{
FVector* vtx = &Poly->Vertices[v];
*vtx += Normal * (InLength * InSegments);
}
Poly->Base += Normal * (InLength * InSegments);
}
}
if( Polygons.Num() )
{
struct FCompareFPolyNormal
{
FORCEINLINE bool operator()( const FPoly& A, const FPoly& B ) const
{
return (B.Normal - A.Normal).SizeSquared() < 0.f;
}
};
Polygons.Sort( FCompareFPolyNormal() );
FVector NormalCompare;
TArray<FPoly> PolygonGroup;
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
FPoly* Poly = &Polygons[p];
if( p == 0 )
{
NormalCompare = Poly->Normal;
}
if( NormalCompare.Equals( Poly->Normal ) )
{
PolygonGroup.Add( *Poly );
}
else
{
if( PolygonGroup.Num() )
{
for( int32 s = 0 ; s < InSegments ; ++s )
{
ExtrudePolygonGroup( Brush, NormalCompare, InLength * s, InLength, PolygonGroup );
}
}
NormalCompare = Poly->Normal;
PolygonGroup.Empty();
PolygonGroup.Add( *Poly );
}
}
if( PolygonGroup.Num() )
{
for( int32 s = 0 ; s < InSegments ; ++s )
{
ExtrudePolygonGroup( Brush, NormalCompare, InLength * s, InLength, PolygonGroup );
}
}
}
go->FinalizeSourceData();
go->GetFromSource();
for( int32 x = 0 ; x < SavedSelectionIndices.Num() ; ++x )
{
FGeomPoly* Poly = &go->PolyPool[ SavedSelectionIndices[x] ];
Poly->Select(1);
}
}
}
UGeomModifier_Lathe::UGeomModifier_Lathe(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Lathe", "Lathe");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Lathe", "Tooltip", "Create new geometry by rotating the selected brush shape about the current pivot point.");
Axis = EAxis::Y;
TotalSegments = 16;
Segments = 4;
AlignToSide = false;
}
bool UGeomModifier_Lathe::Supports()
{
// Lathe mode requires ABrushShape actors to be selected.
for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It )
{
ABrush* Brush = Cast<ABrush>( *It );
if( Brush && Brush->IsBrushShape() )
{
return true;
}
}
return false;
}
void UGeomModifier_Lathe::Initialize()
{
}
bool UGeomModifier_Lathe::OnApply()
{
//FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetCurrentMode();
//GApp->DlgGeometryTools->PropertyWindow->FinalizeValues();
Apply( TotalSegments, Segments, Axis );
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
void UGeomModifier_Lathe::Apply( int32 InTotalSegments, int32 InSegments, EAxis::Type InAxis )
{
if( !GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) )
{
return;
}
// Determine the axis from the active ortho viewport
if ( !GLastKeyLevelEditingViewportClient || !GLastKeyLevelEditingViewportClient->IsOrtho() )
{
return;
}
//Save the brush state in case a bogus shape is generated
CacheBrushState();
switch( GLastKeyLevelEditingViewportClient->ViewportType )
{
case LVT_OrthoXZ:
Axis = EAxis::X;
break;
case LVT_OrthoXY:
Axis = EAxis::Y;
break;
case LVT_OrthoYZ:
Axis = EAxis::Z;
break;
}
FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
InTotalSegments = FMath::Max( 3, InTotalSegments );
InSegments = FMath::Max( 1, InSegments );
if( InSegments > InTotalSegments )
{
InTotalSegments = InSegments;
}
// We will be replacing the builder brush, so get it prepped.
ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();
BuilderBrush->SetActorLocation(GeomMode->GetWidgetLocation(), false);
BuilderBrush->SetPivotOffset(FVector::ZeroVector);
BuilderBrush->SetFlags( RF_Transactional );
BuilderBrush->Brush->Polys->Element.Empty();
// Ensure the builder brush is unhidden.
BuilderBrush->bHidden = false;
BuilderBrush->bHiddenEdLayer = false;
BuilderBrush->SetIsTemporarilyHiddenInEditor( false );
// Some convenience flags
bool bNeedCaps = (InSegments < InTotalSegments);
// Lathe every selected ABrushShape actor into the builder brush
for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It )
{
TArray<FEdge> EdgePool;
ABrushShape* BrushShape = Cast<ABrushShape>( *It );
if( BrushShape )
{
if( BrushShape->Brush->Polys->Element.Num() < 1 )
{
continue;
}
TArray< TArray<FVector> > Windings;
FPoly::GetOutsideWindings( BrushShape, BrushShape->Brush->Polys->Element, Windings );
FVector delta = GeomMode->GetWidgetLocation() - BrushShape->GetActorLocation();
//
// Let's lathe...
//
// Build up an array of vertices that represents the entire lathe.
float AngleStep = 360.f / (float)InTotalSegments;
float Angle = 0;
for( int32 w = 0 ; w < Windings.Num() ; ++w )
{
TArray<FVector>* WindingVerts = &Windings[w];
TArray<FVector> ShapeVertices;
for( int32 s = 0 ; s < (InSegments + 1 + (AlignToSide?1:0)) ; ++s )
{
FRotator rot = FRotator( 0, Angle, 0 );
if( Axis == EAxis::X )
{
rot = FRotator( Angle, 0, 0 );
}
else if( Axis == EAxis::Z )
{
rot = FRotator( 0, 0, Angle );
}
FRotationMatrix RotationMatrix( rot );
for( int32 e = 0 ; e < WindingVerts->Num() ; ++e )
{
FVector vtx = (*WindingVerts)[e] - delta - BrushShape->GetPivotOffset();
vtx = RotationMatrix.TransformPosition( vtx );
ShapeVertices.Add( vtx );
}
if( AlignToSide && (s == 0 || s == InSegments) )
{
Angle += AngleStep / 2.0f;
}
else
{
Angle += AngleStep;
}
}
int32 NumVertsInShape = WindingVerts->Num();
for( int32 s = 0 ; s < InSegments + (AlignToSide?1:0) ; ++s )
{
int32 BaseIdx = s * WindingVerts->Num();
for( int32 v = 0 ; v < NumVertsInShape ; ++v )
{
FVector vtx0 = ShapeVertices[ BaseIdx + v ];
FVector vtx1 = ShapeVertices[ BaseIdx + NumVertsInShape + v ];
FVector vtx2 = ShapeVertices[ BaseIdx + NumVertsInShape + ((v + 1) % NumVertsInShape) ];
FVector vtx3 = ShapeVertices[ BaseIdx + ((v + 1) % NumVertsInShape) ];
FPoly NewPoly;
NewPoly.Init();
NewPoly.Base = BuilderBrush->GetActorLocation();
NewPoly.Vertices.Add( vtx0 );
NewPoly.Vertices.Add( vtx1 );
NewPoly.Vertices.Add( vtx2 );
NewPoly.Vertices.Add( vtx3 );
if( NewPoly.Finalize( BuilderBrush, 1 ) == 0 )
{
BuilderBrush->Brush->Polys->Element.Add( NewPoly );
}
}
}
}
// Create start/end capping polygons if they are necessary
if( bNeedCaps )
{
for( int32 w = 0 ; w < Windings.Num() ; ++w )
{
TArray<FVector>* WindingVerts = &Windings[w];
//
// Create the start cap
//
FPoly Poly;
Poly.Init();
Poly.Base = BrushShape->GetActorLocation();
// Add the verts from the shape
for( int32 v = 0 ; v < WindingVerts->Num() ; ++v )
{
Poly.Vertices.Add((*WindingVerts)[v] - delta - BrushShape->GetPivotOffset());
}
Poly.Finalize( BuilderBrush, 1 );
// Break the shape down into convex shapes.
TArray<FPoly> Polygons;
Poly.Triangulate( BuilderBrush, Polygons );
FPoly::OptimizeIntoConvexPolys( BuilderBrush, Polygons );
// Add the resulting convex polygons into the brush
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
FPoly Polygon = Polygons[p];
if (Polygon.Finalize(BuilderBrush, 1) == 0)
{
BuilderBrush->Brush->Polys->Element.Add(Polygon);
}
}
//
// Create the end cap
//
Poly.Init();
Poly.Base = BrushShape->GetActorLocation();
// Add the verts from the shape
FRotator rot = FRotator( 0, AngleStep * InSegments, 0 );
if( Axis == EAxis::X )
{
rot = FRotator( AngleStep * InSegments, 0, 0 );
}
else if( Axis == EAxis::Z )
{
rot = FRotator( 0, 0, AngleStep * InSegments );
}
FRotationMatrix RotationMatrix( rot );
for( int32 v = 0 ; v < WindingVerts->Num() ; ++v )
{
Poly.Vertices.Add(RotationMatrix.TransformPosition((*WindingVerts)[v] - delta - BrushShape->GetPivotOffset()));
}
Poly.Finalize( BuilderBrush, 1 );
// Break the shape down into convex shapes.
Polygons.Empty();
Poly.Triangulate( BuilderBrush, Polygons );
FPoly::OptimizeIntoConvexPolys( BuilderBrush, Polygons );
// Add the resulting convex polygons into the brush
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
FPoly Polygon = Polygons[p];
Polygon.Reverse();
if (Polygon.Finalize(BuilderBrush, 1) == 0)
{
BuilderBrush->Brush->Polys->Element.Add(Polygon);
}
}
}
}
}
}
// Finalize the builder brush
BuilderBrush->Brush->BuildBound();
BuilderBrush->ReregisterAllComponents();
GeomMode->FinalizeSourceData();
GeomMode->GetFromSource();
GEditor->SelectNone( true, true );
GEditor->SelectActor( BuilderBrush, true, true );
if( DoEdgesOverlap() )
{//Overlapping edges yielded an invalid brush state
RestoreBrushState();
}
else
{
GEditor->RedrawLevelEditingViewports(true);
}
// Create additive brush from builder brush
GEditor->Exec(GeomMode->GetWorld(), TEXT("BRUSH ADD SELECTNEWBRUSH"));
// Deselect & hide builder brush
BuilderBrush->SetIsTemporarilyHiddenInEditor(true);
GEditor->SelectActor(BuilderBrush, false, false);
}
/*------------------------------------------------------------------------------
UGeomModifier_Pen
------------------------------------------------------------------------------*/
UGeomModifier_Pen::UGeomModifier_Pen(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Pen", "Pen");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Pen", "Tooltip", "Create new geometry by drawing the vertices directly into an orthographic viewport. Press space bar to place a vertex, and Enter to close the polygon.");
bCreateBrushShape = false;
bAutoExtrude = true;
ExtrudeDepth = 256;
bCreateConvexPolygons = true;
}
/**
* Gives the modifier a chance to initialize it's internal state when activated.
*/
void UGeomModifier_Pen::WasActivated()
{
ShapeVertices.Empty();
}
/**
* Implements the modifier application.
*/
bool UGeomModifier_Pen::OnApply()
{
Apply();
bPendingPivotOffsetUpdate = true;
return true;
}
void UGeomModifier_Pen::Apply()
{
if( ShapeVertices.Num() > 2 )
{
FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
ABrush* ResultingBrush = GeomMode->GetWorld()->GetDefaultBrush();
ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();
// Move all the vertices that the user placed to the same "height" as the builder brush, based on
// viewport orientation. This is preferable to always creating the new builder brush at height zero.
for( int32 v = 0 ; v < ShapeVertices.Num() ; ++v )
{
FVector* vtx = &ShapeVertices[v];
switch( GLastKeyLevelEditingViewportClient->ViewportType )
{
case LVT_OrthoXY:
vtx->Z = BuilderBrush->GetActorLocation().Z;
break;
case LVT_OrthoXZ:
vtx->Y = BuilderBrush->GetActorLocation().Y;
break;
case LVT_OrthoYZ:
vtx->X = BuilderBrush->GetActorLocation().X;
break;
}
}
// Generate center location from the shape's center
FBox WorldBounds(ShapeVertices.GetData(), ShapeVertices.Num());
FVector BaseLocation = WorldBounds.GetCenter();
//create a scoped transaction so that we can undo the creation/modification
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "BrushSet", "Brush Set") );
//if we are creating a brush we need to first create an actor for it
if( bCreateBrushShape )
{
// Create a shape brush instead of modifying the builder brush
ResultingBrush = BuilderBrush->GetWorld()->SpawnActor<ABrushShape>(BaseLocation, FRotator::ZeroRotator);
ResultingBrush->PreEditChange(NULL);
// It's OK to create an empty brush here as we are going to re-create the polys anyway.
FBSPOps::csgCopyBrush( ResultingBrush, BuilderBrush, PF_DefaultFlags, BuilderBrush->GetFlags(), true, true, true );
ResultingBrush->PostEditChange();
}
else
{
ResultingBrush = FBSPOps::csgAddOperation( BuilderBrush, PF_DefaultFlags, Brush_Add );
}
// Make sure the graphics engine isn't busy rendering this geometry before we go and modify it
FlushRenderingCommands();
ResultingBrush->SetActorLocation(BaseLocation, false);
ResultingBrush->SetPivotOffset(FVector::ZeroVector);
ResultingBrush->SetFlags( RF_Transactional );
ResultingBrush->Brush->Polys->Element.Empty();
// Ensure the brush is unhidden.
ResultingBrush->bHidden = false;
ResultingBrush->bHiddenEdLayer = false;
ResultingBrush->SetIsTemporarilyHiddenInEditor( false );
FPoly Poly;
Poly.Init();
Poly.Base = BaseLocation;
for( int32 v = 0 ; v < ShapeVertices.Num() ; ++v )
{
new(Poly.Vertices) FVector( ShapeVertices[v] - BaseLocation );
}
if( Poly.Finalize( ResultingBrush, 1 ) == 0 )
{
// Break the shape down into triangles.
TArray<FPoly> Triangles;
Poly.Triangulate( ResultingBrush, Triangles );
TArray<FPoly> Polygons = Triangles;
// Optionally, optimize the resulting triangles into convex polygons.
if( bCreateConvexPolygons )
{
FPoly::OptimizeIntoConvexPolys( ResultingBrush, Polygons );
}
// If the user isn't creating an ABrushShape, then carry on adding the sides and bottom face
// If the user wants a full brush created, add the rest of the polys
if( !bCreateBrushShape && bAutoExtrude && ExtrudeDepth > 0 )
{
// Extruding along delta
FVector HalfDelta;
// Create another set of polygons that will represent the bottom face
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
FPoly poly = Polygons[p];
if (p == 0)
{
HalfDelta = 0.5f * poly.Normal * ExtrudeDepth;
}
if( poly.Finalize( ResultingBrush, 0 ) == 0 )
{
for( int32 v = 0 ; v < poly.Vertices.Num() ; ++v )
{
FVector* vtx = &poly.Vertices[v];
*vtx += HalfDelta;
}
new(ResultingBrush->Brush->Polys->Element)FPoly( poly );
}
poly.Reverse();
if( poly.Finalize( ResultingBrush, 0 ) == 0 )
{
for( int32 v = 0 ; v < poly.Vertices.Num() ; ++v )
{
FVector* vtx = &poly.Vertices[v];
*vtx -= 2.0f * HalfDelta;
}
new(ResultingBrush->Brush->Polys->Element)FPoly( poly );
}
}
// Create the polygons that make up the sides of the brush
if( Polygons.Num() > 0 )
{
for( int32 v = 0 ; v < ShapeVertices.Num() ; ++v )
{
FVector vtx0 = ShapeVertices[v] + HalfDelta;
FVector vtx1 = ShapeVertices[(v+1)%ShapeVertices.Num()] + HalfDelta;
FVector vtx2 = vtx1 - 2.0f * HalfDelta;
FVector vtx3 = vtx0 - 2.0f * HalfDelta;
FPoly SidePoly;
SidePoly.Init();
SidePoly.Vertices.Add( vtx1 - BaseLocation );
SidePoly.Vertices.Add( vtx0 - BaseLocation );
SidePoly.Vertices.Add( vtx3 - BaseLocation );
SidePoly.Vertices.Add( vtx2 - BaseLocation );
if( SidePoly.Finalize( ResultingBrush, 1 ) == 0 )
{
new(ResultingBrush->Brush->Polys->Element)FPoly( SidePoly );
}
}
}
}
else // not extruding a solid brush
{
// Now that we have a set of convex polygons, add them all to the brush. These will form the top face.
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
if( Polygons[p].Finalize( ResultingBrush, 0 ) == 0 )
{
new(ResultingBrush->Brush->Polys->Element)FPoly(Polygons[p]);
}
}
}
}
// Finish up
ResultingBrush->Brush->BuildBound();
ResultingBrush->ReregisterAllComponents();
ShapeVertices.Empty();
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->SelectNone( true, true );
GEditor->SelectActor( ResultingBrush, true, true );
// Switch back to edit mode
//FModeTool_GeometryModify* tool = (FModeTool_GeometryModify*)mode->GetCurrentTool();
//tool->SetCurrentModifier( tool->GetModifier(0) );
//force a rebuild of the brush (otherwise the auto-update will do it and this will result in the undo buffer being incorrect)
ABrush::SetNeedRebuild(ResultingBrush->GetLevel());
FBSPOps::RebuildBrush( ResultingBrush->Brush );
GEditor->RebuildAlteredBSP();
GEditor->RedrawLevelEditingViewports(true);
}
}
#if 0
static bool DoesFinalLineIntersectWithShape(const TArray<FVector>& Vertices, const FVector& FinalVertex)
{
// TODO: improve this, it often fails.
// Determine if the next line segment would intersect with any of the previous ones in the shape
for (int32 Index = 0; Index < Vertices.Num() - 1; Index++)
{
const FVector& Segment1Start = Vertices[Index];
const FVector& Segment1End = Vertices[Index + 1];
const FVector& Segment2Start = Vertices[Vertices.Num() - 1];
const FVector& Segment2End = FinalVertex;
// Check that the two line segments are coplanar
check(FMath::IsNearlyZero(FVector::DotProduct(Segment2Start - Segment1Start, FVector::CrossProduct(Segment1End - Segment1Start, Segment2End - Segment2Start))));
FVector Segment1Result;
FVector Segment2Result;
FMath::SegmentDistToSegmentSafe(Segment1Start, Segment1End, Segment2Start, Segment2End, Segment1Result, Segment2Result);
if (Segment1Result.Equals(Segment2Result) && !Segment1Result.Equals(Segment1Start) && !Segment1Result.Equals(Segment1End))
{
return true;
}
}
return false;
}
#endif
static bool DoLineSegmentsIntersect(const FVector2D& Segment1Start, const FVector2D& Segment1End, const FVector2D& Segment2Start, const FVector2D& Segment2End)
{
const FVector2D Segment1Dir = Segment1End - Segment1Start;
const FVector2D Segment2Dir = Segment2End - Segment2Start;
const float Determinant = FVector2D::CrossProduct(Segment1Dir, Segment2Dir);
if (!FMath::IsNearlyZero(Determinant))
{
const FVector2D SegmentStartDelta = Segment2Start - Segment1Start;
const float OneOverDet = 1.0f / Determinant;
const float Seg1Intersection = FVector2D::CrossProduct(SegmentStartDelta, Segment2Dir) * OneOverDet;
const float Seg2Intersection = FVector2D::CrossProduct(SegmentStartDelta, Segment1Dir) * OneOverDet;
const float Epsilon = 1/128.0f;
return (Seg1Intersection > Epsilon && Seg1Intersection < 1.0f - Epsilon && Seg2Intersection > Epsilon && Seg2Intersection < 1.0f - Epsilon);
}
return false;
}
/**
* Given an array of points forming an unclosed polygon, determines whether a line segment formed from the final polygon vertex and a given endpoint
* intersects with any edge of the polygon in the 2D plane in which both segments lie.
*
* @param Vertices Array of vertices forming unclosed polygon
* @param EndVertex Endpoint of line segment starting from final point of polygon
*/
static bool DoesFinalLineIntersectWithShape(const TArray<FVector>& Vertices, const FVector& EndVertex)
{
// Can't intersect if there are fewer than 2 vertices
if (Vertices.Num() < 2)
{
return false;
}
// All line segments in the polygon ought to be coplanar. Hence the problem can be reduced to detecting intersections of line segments
// projected onto their common plane, using 2D coordinates.
// Line segment 1 is the line to test against the rest of the shape
const FVector& Segment1Start = Vertices[Vertices.Num() - 1];
const FVector& Segment1End = EndVertex;
const FVector Segment1Dir = Segment1End - Segment1Start;
const float Segment1Len = Segment1Dir.Size();
if (FMath::IsNearlyZero(Segment1Len))
{
// Treat zero length line segments as non-intersecting
return false;
}
// The direction of segment 1 on the plane will provide the X axis of the 2D basis on the plane
const FVector ProjectedXAxis = Segment1Dir / Segment1Len;
for (int32 Index = 0; Index < Vertices.Num() - 1; Index++)
{
// Line segment 2 is each edge of the shape
const FVector& Segment2Start = Vertices[Index];
const FVector& Segment2End = Vertices[Index + 1];
const FVector Segment2Dir = Segment2End - Segment2Start;
const FVector SegmentStartDelta = Segment2Start - Segment1Start;
// If line segments 1 and 2 are coplanar, the plane normal will be shared, and be calculated from a cross product of their two directions
FVector PlaneNormal = FVector::CrossProduct(Segment1Dir, Segment2Dir);
// Check that they are indeed coplanar
const bool bIsCoplanar = FMath::IsNearlyZero(FVector::DotProduct(SegmentStartDelta, PlaneNormal));
if (!bIsCoplanar)
{
// Non-coplanar line segments can't possibly intersect (ignoring the case of coincident start/end points)
return false;
}
// Parallel line segments will have yielded a zero normal. Attempt to calculate it from the segment start delta.
// If the lines are coincident, this will also yield a zero normal, resulting in a 1D basis (which is still sufficient to detect segment overlaps in projection space).
const bool bParallel = (FMath::IsNearlyZero(PlaneNormal.SizeSquared()));
if (bParallel)
{
PlaneNormal = FVector::CrossProduct(Segment1Dir, SegmentStartDelta);
}
// Get the Y axis of the 2D basis from the X axis and the plane normal
const FVector ProjectedYAxis = FVector::CrossProduct(PlaneNormal.GetSafeNormal(), ProjectedXAxis);
// Project 3d points onto plane
const FVector2D ProjectedSegment1Start(0.0f, 0.0f);
const FVector2D ProjectedSegment1End(Segment1Len, 0.0f);
const FVector2D ProjectedSegment2Start(FVector::DotProduct(ProjectedXAxis, SegmentStartDelta), FVector::DotProduct(ProjectedYAxis, SegmentStartDelta));
const FVector2D ProjectedSegment2End(FVector::DotProduct(ProjectedXAxis, Segment2End - Segment1Start), FVector::DotProduct(ProjectedYAxis, Segment2End - Segment1Start));
// Now check intersection of 2d segments.
if (DoLineSegmentsIntersect(ProjectedSegment1Start, ProjectedSegment1End, ProjectedSegment2Start, ProjectedSegment2End))
{
return true;
}
}
return false;
}
/**
* @return true if the key was handled by this editor mode tool.
*/
bool UGeomModifier_Pen::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
bool bResult = false;
#if WITH_EDITORONLY_DATA
if( ViewportClient->IsOrtho() && Event == IE_Pressed )
{
const bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl);
const bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift);
const bool bAltDown = Viewport->KeyState(EKeys::LeftAlt) || Viewport->KeyState(EKeys::RightAlt);
// CTRL+RightClick (or SPACE bar) adds a vertex to the world
if( (bCtrlDown && !bShiftDown && !bAltDown && Key == EKeys::RightMouseButton) || Key == EKeys::SpaceBar )
{
// if we're trying to edit vertices in a different viewport to the one we started in then popup a warning
if( ShapeVertices.Num() && ViewportClient != UsingViewportClient )
{
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "GeomModifierPen_Warning_AddingVertexInWrongViewport", "Vertices can only be added to one viewport at a time." ) );
return true;
}
if( ShapeVertices.Num() && MouseWorldSpacePos.Equals( ShapeVertices[0] ) )
{
if (!DoesFinalLineIntersectWithShape(ShapeVertices, ShapeVertices[0]))
{
Apply();
bResult = true;
}
}
else
{
if (!DoesFinalLineIntersectWithShape(ShapeVertices, MouseWorldSpacePos))
{
UsingViewportClient = ViewportClient;
ShapeVertices.Add( MouseWorldSpacePos );
bResult = true;
}
}
}
else if( Key == EKeys::Escape || Key == EKeys::BackSpace )
{
if( ShapeVertices.Num() )
{
ShapeVertices.RemoveAt( ShapeVertices.Num() - 1 );
}
bResult = true;
}
else if( Key == EKeys::Enter )
{
if (ShapeVertices.Num() > 0 && !DoesFinalLineIntersectWithShape(ShapeVertices, ShapeVertices[0]))
{
Apply();
bResult = true;
}
}
}
if( bResult )
{
GEditor->RedrawLevelEditingViewports( true );
}
#endif // WITH_EDITORONLY_DATA
return bResult;
}
void UGeomModifier_Pen::Render(const FSceneView* View,FViewport* Viewport,FPrimitiveDrawInterface* PDI)
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
FModeTool_GeometryModify* tool = (FModeTool_GeometryModify*)mode->GetCurrentTool();
if( tool->GetCurrentModifier() != this )
{
return;
}
// Only draw in ortho viewports
if( !((FEditorViewportClient*)(Viewport->GetClient()))->IsOrtho() )
{
return;
}
FLinearColor Color = bCreateBrushShape ? GEngine->C_BrushShape : GEngine->C_BrushWire;
// If we have more than 2 vertices placed, connect them with lines
if( ShapeVertices.Num() > 1 )
{
for( int32 v = 0 ; v < ShapeVertices.Num() - 1 ; ++v )
{
PDI->DrawLine( ShapeVertices[v], ShapeVertices[ v+1 ], Color, SDPG_Foreground );
}
}
// Draw vertices for each point the user has put down
for( int32 v = 0 ; v < ShapeVertices.Num() ; ++v )
{
PDI->DrawPoint( ShapeVertices[v], Color, 6.f, SDPG_Foreground );
}
if( ShapeVertices.Num() )
{
if (!DoesFinalLineIntersectWithShape(ShapeVertices, MouseWorldSpacePos))
{
// Draw a dashed line from the last placed vertex to the current mouse position
DrawDashedLine(PDI, ShapeVertices[ShapeVertices.Num() - 1], MouseWorldSpacePos, FLinearColor(1, 0.5f, 0), GEditor->GetGridSize(), SDPG_Foreground);
}
}
if( ShapeVertices.Num() > 2 )
{
if (!DoesFinalLineIntersectWithShape(ShapeVertices, ShapeVertices[0]))
{
// Draw a darkened dashed line to show what the completed shape will look like
DrawDashedLine(PDI, ShapeVertices[ShapeVertices.Num() - 1], ShapeVertices[0], FLinearColor(.5, 0, 0), GEditor->GetGridSize(), SDPG_Foreground);
}
}
// Draw a box where the next vertex will be placed
int32 BoxSz = FMath::Max( GEditor->GetGridSize() / 2, 1.f );
DrawWireBox(PDI, FBox::BuildAABB( MouseWorldSpacePos, FVector(BoxSz,BoxSz,BoxSz) ), FLinearColor(1,1,1), SDPG_Foreground);
}
void UGeomModifier_Pen::DrawHUD(FEditorViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View,FCanvas* Canvas)
{
}
void UGeomModifier_Pen::Tick(FEditorViewportClient* ViewportClient,float DeltaTime)
{
if( GCurrentLevelEditingViewportClient == ViewportClient )
{
FVector NewMouseWorldSpacePos = ComputeWorldSpaceMousePos(ViewportClient);
// If the grid is enabled, figure out where the nearest grid location is to the mouse cursor
if( GetDefault<ULevelEditorViewportSettings>()->GridEnabled )
{
NewMouseWorldSpacePos = NewMouseWorldSpacePos.GridSnap( GEditor->GetGridSize() );
}
// If the mouse position has moved, update the viewport
if( NewMouseWorldSpacePos != MouseWorldSpacePos )
{
MouseWorldSpacePos = NewMouseWorldSpacePos;
GEditor->RedrawLevelEditingViewports( true );
}
}
}
/*------------------------------------------------------------------------------
UGeomModifier_Clip
------------------------------------------------------------------------------*/
namespace GeometryClipping {
/**
* Creates a giant brush aligned with this plane.
*
* @param OutGiantBrush [out] The new brush.
* @param InPlane Plane with which to align the brush.
*
* NOTE: it is up to the caller to set up the new brush upon return in regards to it's CSG operation and flags.
*/
static void BuildGiantAlignedBrush( ABrush& OutGiantBrush, const FPlane& InPlane )
{
OutGiantBrush.SetActorLocation(FVector::ZeroVector, false);
OutGiantBrush.SetPivotOffset(FVector::ZeroVector);
verify( OutGiantBrush.Brush );
verify( OutGiantBrush.Brush->Polys );
OutGiantBrush.Brush->Polys->Element.Empty();
// Create a list of vertices that can be used for the new brush
FVector vtxs[8];
FPlane FlippedPlane = InPlane.Flip();
FPoly TempPoly = FPoly::BuildInfiniteFPoly( FlippedPlane );
TempPoly.Finalize(&OutGiantBrush,0);
vtxs[0] = TempPoly.Vertices[0];
vtxs[1] = TempPoly.Vertices[1];
vtxs[2] = TempPoly.Vertices[2];
vtxs[3] = TempPoly.Vertices[3];
FlippedPlane = FlippedPlane.Flip();
FPoly TempPoly2 = FPoly::BuildInfiniteFPoly( FlippedPlane );
vtxs[4] = TempPoly2.Vertices[0] + (TempPoly2.Normal * -(WORLD_MAX)); vtxs[5] = TempPoly2.Vertices[1] + (TempPoly2.Normal * -(WORLD_MAX));
vtxs[6] = TempPoly2.Vertices[2] + (TempPoly2.Normal * -(WORLD_MAX)); vtxs[7] = TempPoly2.Vertices[3] + (TempPoly2.Normal * -(WORLD_MAX));
// Create the polys for the new brush.
FPoly newPoly;
// TOP
newPoly.Init();
newPoly.Base = vtxs[0];
newPoly.Vertices.Add( vtxs[0] );
newPoly.Vertices.Add( vtxs[1] );
newPoly.Vertices.Add( vtxs[2] );
newPoly.Vertices.Add( vtxs[3] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// BOTTOM
newPoly.Init();
newPoly.Base = vtxs[4];
newPoly.Vertices.Add( vtxs[4] );
newPoly.Vertices.Add( vtxs[5] );
newPoly.Vertices.Add( vtxs[6] );
newPoly.Vertices.Add( vtxs[7] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// SIDES
// 1
newPoly.Init();
newPoly.Base = vtxs[1];
newPoly.Vertices.Add( vtxs[1] );
newPoly.Vertices.Add( vtxs[0] );
newPoly.Vertices.Add( vtxs[7] );
newPoly.Vertices.Add( vtxs[6] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// 2
newPoly.Init();
newPoly.Base = vtxs[2];
newPoly.Vertices.Add( vtxs[2] );
newPoly.Vertices.Add( vtxs[1] );
newPoly.Vertices.Add( vtxs[6] );
newPoly.Vertices.Add( vtxs[5] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// 3
newPoly.Init();
newPoly.Base = vtxs[3];
newPoly.Vertices.Add( vtxs[3] );
newPoly.Vertices.Add( vtxs[2] );
newPoly.Vertices.Add( vtxs[5] );
newPoly.Vertices.Add( vtxs[4] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// 4
newPoly.Init();
newPoly.Base = vtxs[0];
newPoly.Vertices.Add( vtxs[0] );
newPoly.Vertices.Add( vtxs[3] );
newPoly.Vertices.Add( vtxs[4] );
newPoly.Vertices.Add( vtxs[7] );
newPoly.Finalize(&OutGiantBrush,0);
new(OutGiantBrush.Brush->Polys->Element)FPoly(newPoly);
// Finish creating the new brush.
OutGiantBrush.Brush->BuildBound();
}
/**
* Clips the specified brush against the specified plane.
*
* @param InWorld World context
* @param InPlane The plane to clip against.
* @param InBrush The brush to clip.
* @return The newly created brush representing the portion of the brush in the plane's positive halfspace.
*/
static ABrush* ClipBrushAgainstPlane( const FPlane& InPlane, ABrush* InBrush)
{
UWorld* World = InBrush->GetWorld();
ULevel* BrushLevel = InBrush->GetLevel();
// Create a giant brush in the level of the source brush to use in the intersection process.
ABrush* ClippedBrush = NULL;
// When clipping non-builder brushes, create a duplicate of the brush
// to clip. This duplicate will replace the existing brush.
if( !FActorEditorUtils::IsABuilderBrush(InBrush) )
{
// Select only the original brush to prevent other actors from being duplicated.
GEditor->SelectNone( false, true );
GEditor->SelectActor( InBrush, true, false, false );
// Duplicate the original brush. This will serve as our clipped brush.
GEditor->edactDuplicateSelected( BrushLevel, false );
// Clipped brush should be the only selected
// actor if the duplication didn't fail.
ClippedBrush = GEditor->GetSelectedActors()->GetTop<ABrush>();
}
// To clip the builder brush, instead of replacing it, spawn a
// temporary brush to clip. Then, copy that to the builder brush.
else
{
// NOTE: This brush is discarded later on after copying the values to the builder brush.
FActorSpawnParameters SpawnInfo;
SpawnInfo.OverrideLevel = BrushLevel;
SpawnInfo.Template = InBrush;
ClippedBrush = World->SpawnActor<ABrush>( InBrush->GetClass(), SpawnInfo );
check( ClippedBrush );
}
// It's possible that the duplication failed.
if( !ClippedBrush )
{
return NULL;
}
// The brushes should have the same class otherwise
// perhaps there were additional brushes were selected.
check( ClippedBrush->GetClass() == InBrush->GetClass() );
ClippedBrush->Brush = NewObject<UModel>(ClippedBrush, NAME_None, RF_Transactional);
ClippedBrush->Brush->Initialize(nullptr);
ClippedBrush->GetBrushComponent()->Brush = ClippedBrush->Brush;
GeometryClipping::BuildGiantAlignedBrush( *ClippedBrush, InPlane );
ClippedBrush->BrushType = InBrush->BrushType;
ClippedBrush->SetFlags( InBrush->GetFlags() );
ClippedBrush->PolyFlags = InBrush->PolyFlags;
// Create a BSP for the brush that is being clipped.
FBSPOps::bspBuild( InBrush->Brush, FBSPOps::BSP_Optimal, 15, 70, 1, 0 );
FBSPOps::bspRefresh( InBrush->Brush, true );
FBSPOps::bspBuildBounds( InBrush->Brush );
// Intersect the giant brush with the source brush's BSP. This will give us the finished, clipping brush
// contained inside of the giant brush.
ClippedBrush->Modify();
InBrush->Brush->Modify();
GEditor->bspBrushCSG( ClippedBrush, InBrush->Brush, 0, Brush_MAX, CSG_Intersect, false, false, true );
FBSPOps::bspUnlinkPolys( ClippedBrush->Brush );
// Remove all polygons on the giant brush that don't match the normal of the clipping plane
for( int32 p = 0 ; p < ClippedBrush->Brush->Polys->Element.Num() ; ++p )
{
FPoly* P = &ClippedBrush->Brush->Polys->Element[p];
if( P->Finalize( ClippedBrush, 1 ) == 0 )
{
if( !FPlane( P->Vertices[0], P->Normal ).Equals( InPlane, 0.01f ) )
{
ClippedBrush->Brush->Polys->Element.RemoveAt( p );
p = -1;
}
}
}
// The BSP "CSG_Intersect" code sometimes creates some nasty polygon fragments so clean those up here before going further.
FPoly::OptimizeIntoConvexPolys( ClippedBrush, ClippedBrush->Brush->Polys->Element );
// Clip each polygon in the original brush against the clipping plane. For every polygon that is behind the plane or split by it, keep the back portion.
FVector PlaneBase = FVector( InPlane.X, InPlane.Y, InPlane.Z ) * InPlane.W;
for( int32 p = 0 ; p < InBrush->Brush->Polys->Element.Num() ; ++p )
{
FPoly Poly = InBrush->Brush->Polys->Element[p];
FPoly front, back;
int32 res = Poly.SplitWithPlane( PlaneBase, InPlane.GetSafeNormal(), &front, &back, true );
switch( res )
{
case SP_Back:
ClippedBrush->Brush->Polys->Element.Add( Poly );
break;
case SP_Split:
ClippedBrush->Brush->Polys->Element.Add( back );
break;
}
}
// At this point we have a clipped brush with optimized capping polygons so we can finish up by fixing it's ordering in the actor array and other misc things.
ClippedBrush->CopyPosRotScaleFrom( InBrush );
ClippedBrush->PolyFlags = InBrush->PolyFlags;
// Clean the brush up.
for( int32 poly = 0 ; poly < ClippedBrush->Brush->Polys->Element.Num() ; poly++ )
{
FPoly* Poly = &(ClippedBrush->Brush->Polys->Element[poly]);
Poly->iLink = poly;
Poly->Normal = FVector::ZeroVector;
Poly->Finalize(ClippedBrush,0);
}
// One final pass to clean the polyflags of all temporary settings.
for( int32 poly = 0 ; poly < ClippedBrush->Brush->Polys->Element.Num() ; poly++ )
{
FPoly* Poly = &(ClippedBrush->Brush->Polys->Element[poly]);
Poly->PolyFlags &= ~PF_EdCut;
Poly->PolyFlags &= ~PF_EdProcessed;
}
// Move the new brush to where the new brush was to preserve brush ordering.
ABrush* BuilderBrush = World->GetDefaultBrush();
if( InBrush == BuilderBrush )
{
// Special-case behavior for the builder brush.
// Copy the temporary brush back over onto the builder brush (keeping object flags)
BuilderBrush->Modify();
Copying //UE4/Dev-Core to //UE4/Dev-Main (Source: //UE4/Dev-Core @ 3620134) #lockdown Nick.Penwarden #rb none ============================ MAJOR FEATURES & CHANGES ============================ Change 3550452 by Ben.Marsh UAT: Improve readability of error message when an editor commandlet fails with an error code. Change 3551179 by Ben.Marsh Add methods for reading text files into an array of strings. Change 3551260 by Ben.Marsh Core: Change FFileHelper routines to use enum classes for flags. Change 3555697 by Gil.Gribb Fixed a rare crash when the asset registry scanner found old cooked files with package level compression. #jira UE-47668 Change 3556464 by Ben.Marsh UGS: If working in a virtual stream, use the name of the first non-virtual ancestor for writing version files. Change 3557630 by Ben.Marsh Allow the network version to be set via Build.version if it's not overriden from Version.h. Change 3561357 by Gil.Gribb Fixed crashes related to loading old unversioned files in the editor. #jira UE-47806 Change 3565711 by Graeme.Thornton PR #3839: Make non-encoding specific Base64 functions accessible (Contributed by stfx) Change 3565864 by Robert.Manuszewski Temp fix for a race condition with the async loading thread enabled - caching the linker in case it gets removed (but not deleted) from super class object. Change 3569022 by Ben.Marsh PR #3849: Update gitignore (Contributed by mhutch) Change 3569113 by Ben.Marsh Fix Japanese errors not displaying correctly in the cook output log. #jira UE-47746 Change 3569486 by Ben.Marsh UGS: Always sync the Enterprise folder if the selected .uproject file has the "Enterprise" flag set. Change 3570483 by Graeme.Thornton Minor C# cleanups. Removing some redundant "using" calls which also cause dotnetcore compile errors Change 3570513 by Robert.Manuszewski Fix for a race condition with async loading thread enabled. Change 3570664 by Ben.Marsh UBT: Use P/Invoke to determine number of physical processors on Windows rather than using WMI. Starting up WMIC adds 2.5 seconds to build times, and is not compatible with .NET core. Change 3570708 by Robert.Manuszewski Added ENABLE_GC_OBJECT_CHECKS macro to be able to quickly toggle UObject pointer checks in shipping builds when the garbage collector is running. Change 3571592 by Ben.Marsh UBT: Allow running with -installed without creating [InstalledPlatforms] entries in BaseEngine.ini. If there is no HasInstalledPlatformInfo=true setting, assume that all platforms are still available. Change 3572215 by Graeme.Thornton UBT - Remove some unnecessary using directives - Point SN-DBS code at the new Utils.GetPhysicalProcessorCount call, rather than trying to calculate it itself Change 3572437 by Robert.Manuszewski Game-specific fix for lazy object pointer issues in one of the test levels. The previous fix had to be partially reverted due to side-effects. #jira UE-44996 Change 3572480 by Robert.Manuszewski MaterialInstanceCollections will no longer be added to GC clusters to prevent materials staying around in memory for too long Change 3573547 by Ben.Marsh Add support for displaying log timestamps in local time. Set LogTimes=Local in *Engine.ini, or pass -LocalLogTimes on the command line. Change 3574562 by Robert.Manuszewski PR #3847: Add GC callbacks for script integrations (Contributed by mhutch) Change 3575017 by Ben.Marsh Move some functions related to generating window resolutions out of Core (FParse::Resolution, GenerateConvenientWindowedResolutions). Also remove a few headers from shared PCHs prior to splitting application functionality out of Core. Change 3575689 by Ben.Marsh Add a fixed URL for opening the API documentation, so it works correctly in "internal" and "perforce" builds. Change 3575934 by Steve.Robb Fix for nested preprocessor definitions. Change 3575961 by Steve.Robb Fix for nested zeros. Change 3576297 by Robert.Manuszewski Material resources will now be discarded in PostLoad (Game Thread) instead of in Serialize (potentially Async Loading Thread) so that shader deregistration doesn't assert when done from a different thread than the game thread. #jira FORT-38977 Change 3576366 by Ben.Marsh Add shim functions to allow redirecting FPlatformMisc::ClipboardCopy()/ClipboardPaste() to FPlatformApplicationMisc::ClipboardCopy()/ClipboardPaste() while they are deprecated. Change 3578290 by Graeme.Thornton Changes to Ionic zip library to allow building on dot net core Change 3578291 by Graeme.Thornton Ionic zip library binaries built for .NET Core Change 3578354 by Graeme.Thornton Added FBase64::GetDecodedDataSize() to determine the size of bytes of a decoded base64 string Change 3578674 by Robert.Manuszewski After loading packages flush linker cache on uncooked platforms to free precache memory Change 3579068 by Steve.Robb Fix for CLASS_Intrinsic getting stomped. Fix to EClassFlags so that they are visible in the debugger. Re-added mysteriously-removed comments. Change 3579228 by Steve.Robb BOM removed. Change 3579297 by Ben.Marsh Fix exception if a plugin lists the same module twice. #jira UE-48232 Change 3579898 by Robert.Manuszewski When creating GC clusters and asserting due to objects still being pending load, the object name and cluster name will now be logged with the assert. Change 3579983 by Robert.Manuszewski More fixes for freeing linker cache memory in the editor. Change 3580012 by Graeme.Thornton Remove redundant copy of FileReference.cs Change 3580408 by Ben.Marsh Validate that arguments passed to the checkf macro are valid sprintf types, and fix up a few places which are currently incorrect. Change 3582104 by Graeme.Thornton Added a dynamic compilation path that uses the latest roslyn apis. Currently only used by the .NET Core path. Change 3582131 by Graeme.Thornton #define out some PerformanceCounter calls that don't exist in .NET Core. They're only used by mono-specific calls anyway. Change 3582645 by Ben.Marsh PR #3879: fix bug when creating a new VS2017 C++ project (Contributed by mnannola) #jira UE-48192 Change 3583955 by Robert.Manuszewski Support for EDL cooked packages in the editor Change 3584035 by Graeme.Thornton Split RunExternalExecutable into RunExternaNativelExecutable and RunExternalDotNETExecutable. When running under .NET Core, externally launched DotNET utilities must be launched via the 'dotnet' proxy to work correctly. Change 3584177 by Robert.Manuszewski Removed unused member variable (FArchiveAsync2::bKeepRestOfFilePrecached) Change 3584315 by Ben.Marsh Move Android JNI accessor functions into separate header, to decouple it from the FAndroidApplication class. Change 3584370 by Ben.Marsh Move hooks which allow platforms to load any modules into the FPlatformApplicationMisc classes. Change 3584498 by Ben.Marsh Move functions for getting and setting the hardware window pointer onto the appropriate platform window classes. Change 3585003 by Steve.Robb Fix for TChunkedArray ranged-for iteration. #jira UE-48297 Change 3585235 by Ben.Marsh Remove LogEngine extern from Core; use the platform log channels instead. Change 3585942 by Ben.Marsh Move MessageBoxExt() implementation into application layer for platforms that require it. Change 3587071 by Ben.Marsh Move Linux's UngrabAllInput() function into a callback, so DebugBreak still works without SDL. Change 3587161 by Ben.Marsh Remove headers which will be stripped out of the Core module from Core.h and PlatformIncludes.h. Change 3587579 by Steve.Robb Fix for Children list not being rebuilt after hot reload. Change 3587584 by Graeme.Thornton Logging improvements for pak signature check failures - Added "PakCorrupt" console command which corrupts the master signature table - Added some extra log information about which block failed - Re-hash the master signature table and to make sure that it hasn't changed since startup - Moved the ensure around so that some extra logging messages can make it out before the ensure is hit - Added PAK_SIGNATURE_CHECK_FAILS_ARE_FATAL to IPlatformFilePak.h so we have a single place to make signature check failures fatal again Change 3587586 by Graeme.Thornton Changes to make UBT build and run on .NET Core - Added *_DNC csproj files for DotNETUtilities and UnrealBuildTool projects which contain the .NET Core build setups - VCSharpProjectFile can no be asked for the CsProjectInfo for a particular configuration, which is cached for future use - After loading VCSharpProjectFiles, .NET Core based projects will be excluded unless generating VSCode projects Change 3587953 by Steve.Robb Allow arbitrary UENUM initializers for enumerators. Editor-only data UENUM support. Enumerators named MAX are now treated as the UENUM's maximum, and will not cause a MAX+1 value to be generated. #jira UE-46274 Change 3589827 by Graeme.Thornton More fixes for VSCode project generation and for UBT running on .NET Core - Use a different file extension for rules assemblies when build on .NET Core, so they never get used by their counterparts - UEConsoleTraceListener supports stdout/stderror constructor parameter and outputs to the appropriate channel - Added documentation for UEConsoleTraceListener - All platforms .NET project compilation tasks/launch configs now use "dotnet" and not the normal batch files - Restored the default UBT log verbosity to "Log" rather than "VeryVeryVerbose" - Renamed assemblies for .NETCore versions of DotNETUtilities and UnrealBuildTool so they don't conflict with the output of the existing .NET Desktop Framework stuff Change 3589868 by Graeme.Thornton Separate .NET Core projects for UBT and DotNETCommon out into their own directories so that their intermediates don't overlap with the standard .NET builds, causing failures. UBT registers ONLY .NET Core C# projects when generating VSCode solutions, and ONLY standard C# projects in all other cases Change 3589919 by Robert.Manuszewski Fixing crash when cooking textures that have already been cooked for EDL (support for cooked content in the editor) Change 3589940 by Graeme.Thornton Force UBT to think it's running on mono when actually running on .NET Core. Disables a lot of windows specific code paths. Change 3590078 by Graeme.Thornton Fully disable automatic assembly info generation in .NET Core projects Change 3590534 by Robert.Manuszewski Marking UObject as intrinsic clas to fix a crash on UFE startup. Change 3591498 by Gil.Gribb UE4 - Fixed several edge cases in the low level async loading code, especially around cancellation. Also PakFileTest is a console command which can be used to stress test pak file loading. Change 3591605 by Gil.Gribb UE4 - Follow up to fixing several edge cases in the low level async loading code. Change 3592577 by Graeme.Thornton .NET Core C# projects now reference source files explicitly, to stop it accidentally compiling various intermediates Change 3592684 by Steve.Robb Fix for EObjectFlags being passed as the wrong argument to csgCopyBrush. Change 3592710 by Steve.Robb Fix for invalid casts in ListProps command. Some name changes in command output. Change 3592715 by Ben.Marsh Move Windows event log code into cpp file, and expose it to other modules even if it's not enabled by default. Change 3592767 by Gil.Gribb UE4 - Changed the logic so that engine UObjects boot before anything else. The engine classes are known to be cycle-free, so we will get them done before moving onto game modules. Change 3592770 by Gil.Gribb UE4 - Fixed a race condition with async read completion in the prescence of cancels. Change 3593090 by Steve.Robb Better error message when there two clashing type names are found. Change 3593697 by Steve.Robb VisitTupleElements function, which calls a functor for each element in the tuple. Change 3595206 by Ben.Marsh Include additional diagnostics for missing imports when a module load fails. Change 3596140 by Graeme.Thornton Batch file for running MSBuild Change 3596267 by Steve.Robb Thread safety fix to FPaths::GetProjectFilePath(). Change 3596271 by Robert.Manuszewski Added code to verify compression flags in package file summary to avoid cases where corrupt packages are crashing the editor #jira UE-47535 Change 3596283 by Steve.Robb Redundant casts removed from UHT. Change 3596303 by Ben.Marsh EC: Improve parsing of Android Clang errors and warnings, which are formatted as MSVC diagnostics to allow go-to-line clicking in the Output Window. Change 3596337 by Ben.Marsh UBT: Format messages about incorrect headers in a way that makes them clickable from Visual Studio. Change 3596367 by Steve.Robb Iterator checks in ranged-for on TMap, TSet and TSparseArray. Change 3596410 by Gil.Gribb UE4 - Improved some error messages on runtime failures in the EDL. Change 3596532 by Ben.Marsh UnrealVS: Fix setting command line to empty not affecting property sheet. Also remove support for VS2013. #jira UE-48119 Change 3596631 by Steve.Robb Tool which takes a .map file and a .objmap file (from UBT) and creates a report which shows the size of all the symbols contributed by the source code per-folder. Change 3596807 by Ben.Marsh Improve Intellisense when generated headers are missing or out of date (eg. line numbers changed, etc...). These errors seem to be masked by VAX, but are present when using the default Visual Studio Intellisense. * UCLASS macro is defined to empty when __INTELLISENSE__ is defined. Previous macro was preventing any following class declaration being parsed correctly if generated code was out of date, causing squiggles over all class methods/variables. * Insert a semicolon after each expanded GENERATED_BODY macro, so that if it parses incorrectly, the compiler can still continue parsing the next declaration. Change 3596957 by Steve.Robb UBT can be used to write out an .objsrcmap file for use with the MapFileParser. Renaming of ObjMap to ObjSrcMap in MapFileParser. Change 3597213 by Ben.Marsh Remove AutoReporter. We don't support this any more. Change 3597558 by Ben.Marsh UGS: Allow adding custom actions to the context menu for right clicking on a changelist. Actions are specified in the project's UnrealEngine.ini file, with the following syntax: +ContextMenu=(Label="This is the menu item", Execute="foo.exe", Arguments="bar") The standard set of variables for custom tools is expanded in each parameter (eg. $(ProjectDir), $(EditorConfig), etc...), plus the $(Change) variable. Change 3597982 by Ben.Marsh Add an option to allow overriding the local DDC path from the editor (under Editor Preferences > Global > Local Derived Data Cache). #jira UE-47173 Change 3598045 by Ben.Marsh UGS: Add variables for stream and client name, and the ability to escape any variables for URIs using the syntax $(VariableName:URI). Change 3599214 by Ben.Marsh Avoid string duplication when comparing extensions. Change 3600038 by Steve.Robb Fix for maps being modified during iteration in cache compaction. Change 3600136 by Steve.Robb GitHub #3538 : Fixed a bug with the handling of 'TMap' key/value types in the UnrealHeaderTool Change 3600214 by Steve.Robb More accurate error message when unsupported template parameters are provided in a TSet property. Change 3600232 by Ben.Marsh UBT: Force UHT to run again if the .build.cs file for a module has changed. #jira UE-46119 Change 3600246 by Steve.Robb GitHub #3045 : allow multiple interface definition in a file Change 3600645 by Ben.Marsh Convert QAGame to Include-What-You-Use. Change 3600897 by Ben.Marsh Fix invalid path (multiple slashes) in LibCurl.build.cs. Causes exception when scanning for includes. Change 3601558 by Graeme.Thornton Simple first pass VSCode editor integration plugin Change 3601658 by Graeme.Thornton Enable intellisense generation for VS Code project files and setup include paths properly Change 3601762 by Ben.Marsh UBT: Add support for adaptive non-unity builds when working from a Git repository. The ISourceFileWorkingSet interface is now used to query files belonging to the working set, and has separate implementations for Perforce (PerforceSourceFileWorkingSet) and Git (GitSourceFileWorkingSet). The Git implementation is used if a .git directory is found in the directory containing the Engine folder, the directory containing the project file, or the parent directory of the project file, and spawns a "git status" process in the background to determine which files are untracked or staged. Several new settings are supported in BuildConfiguration.xml to allow modifying default behavior: <SourceFileWorkingSet> <Provider>Default</Provider> <!-- May be None, Default, Git or Perforce --> <RepositoryPath></RepositoryPath> <!-- Specifies the path to the repository, relative to the directory containing the Engine folder. If not set, tries to find a .git directory in the locations listed above. --> <GitPath>git</GitPath> <!-- Specifies the path to the Git executable. Defaults to "git", which assumes that it will be on the PATH --> </SourceFileWorkingSet> Change 3604032 by Graeme.Thornton First attempt at automatically detecting the existance and location of visual studio code in the source code accessor module. Only works for windows. Change 3604038 by Graeme.Thornton Added FSourceCodeNavigation::GetSelectedSourceCodeIDE() which returns the name of the selected source code accessor. Replaced all usages of FSourceCodeNavigation::GetSuggestedSourceCodeIDE() with GetSelectedSourceCodeIDE(), where the message is referring to the opening or editing of code. Change 3604106 by Steve.Robb GitHub #3561 : UE-44950: Don't see all caps struct constructor as macro Change 3604192 by Steve.Robb GitHub #3911 : Improving ToUpper/ToLower efficiency Change 3604273 by Graeme.Thornton IWYU build fixes when malloc profiler is enabled Change 3605457 by Ben.Marsh Fix race for intiialization of ThreadID variable on FRunnableThreadWin, and restore a previous check that was working around it. Change 3606720 by James.Hopkin Dave Ratti's fix to character base recursion protection code - was missing a GetOwner call, instead attempting to cast a component to a pawn. Change 3606807 by Graeme.Thornton Disabled optimizations around FShooterStyle::Create(), which was crashing in Win64 shipping game builds due to some known compiler issue. Same variety of fix as BenZ did in CL 3567741. Change 3607026 by James.Hopkin Fixed incorrect ABrush cast - was attempting to cast a UModel to ABrush, which can never succeed Change 3607142 by Graeme.Thornton UBT - Minor refactor of BackgroundProcess shutdown in SourceFileWorkingSet. Check whether the process has already exited before trying to kill it during Dispose. Change 3607146 by Ben.Marsh UGS: Fix exception due to formatting string when Perforce throws an error. Change 3607147 by Steve.Robb Efficiency fix for integer properties, which were causing a property mismatch and thus a tag lookup every time. Float and double conversion support added to int properties. NAME_DoubleProperty added. Fix for converting enum class enumerators > 255 to int properties. Change 3607516 by Ben.Marsh PR #3935: Fix DECLARE_DELEGATE_NineParams, DECLARE_MULTICAST_DELEGATE_NineParams. (Contributed by enginevividgames) Change 3610421 by Ben.Marsh UAT: Move help for RebuildLightMapsCommand into attributes, so they display when running with -help. Change 3610657 by Ben.Marsh UAT: Unify initialization of command environment for build machines and local execution. Always derive parameters which aren't manually set via environment variables. Change 3611000 by Ben.Marsh UAT: Remove the -ForceLocal command line option. Settings are now determined automatically, independently of the -Buildmachine argument. Change 3612471 by Ben.Marsh UBT: Move FastJSON into DotNETUtilities. Change 3613479 by Ben.Marsh UBT: Remove the bIsCodeProject flag from UProjectInfo. This was only really being used to determine which projects to generate an IDE project for, so it is now checked in the project file generator. Change 3613910 by Ben.Marsh UBT: Remove unnecessary code to guess a project from the target name; doesn't work due to init order, actual project is determined later. Change 3614075 by Ben.Marsh UBT: Remove hacks for testing project file attributes by name. Change 3614090 by Ben.Marsh UBT: Remove global lookup of project by name. Projects should be explicitly specified by path when necessary. Change 3614488 by Ben.Marsh UBT: Prevent annoying (but handled) exception when constructing SQLiteModuleSupport objects with -precompile enabled. Change 3614490 by Ben.Marsh UBT: Simplify generation of arguments for building intellisense; determine the platform/configuration to build from the project file generation code, rather than inside the target itself. Change 3614962 by Ben.Marsh UBT: Move the VS2017 strict conformance mode (/permissive-) behind a command line option (-Strict), and disable it by default. Building with this mode is not guaranteed to work correctly without updated Windows headers. Change 3615416 by Ben.Marsh EC: Include an icon showing the overall status of a build in the grid view. Change 3615713 by Ben.Marsh UBT: Delete any files in output directories which match output files in other directories. Allows automatically deleting build products which are moved into another folder. #jira UE-48987 Change 3616652 by Ben.Marsh Plugins: Fix incorrect dialog when binaries for a plugin are missing. Should only prompt to disable if starting a content-only project. #jira UE-49007 Change 3616680 by Ben.Marsh Add the CodeAPI-HTML.tgz file into the installed engine build. Change 3616767 by Ben.Marsh Plugins: Tweak error message if the FModuleManager::IsUpToDate() function returns false for a plugin module; the module may be missing, not just incompatible. Change 3616864 by Ben.Marsh Cap the length of the temporary package name during save, to prevent excessively long filenames going over the limit once a GUID is appended. #jira UE-48711 Change 3619964 by Ben.Marsh UnrealVS: Fix single file compile for foreign projects, where the command line contains $(SolutionDir) and $(ProjectName) variables. Change 3548930 by Ben.Marsh UBT: Remove UEBuildModuleCSDLL; there is no codepath that still supports creating them. Remove the remaining UEBuildModule/UEBuildModuleCPP abstraction. Change 3558056 by Ben.Marsh Deprecate FString::Trim() and FString::TrimTrailing(), and replace them with separate versions to mutate (TrimStartInline(), TrimEndInline()) or return by copy (TrimStart(), TrimEnd()). Also add a functions to trim whitespace from both ends of a string (TrimStartAndEnd(), TrimStartAndEndInline()). Change 3563309 by Graeme.Thornton Moved some common C# classes into the DotNETCommon assembly Change 3570283 by Graeme.Thornton Move some code out of RPCUtility and into DotNETCommon, removing the dependency between the two projects Added UEConsoleTraceListener to replace ConsoleTraceListener, which doesn't exist in DotNetCore Change 3572811 by Ben.Marsh UBT: Add -enableasan / -enabletsan command line options and bEnableAddressSanitizer / bEnableThreadSanitizer settings in BuildConfiguration.xml (and remove environment variables). Change 3573397 by Ben.Marsh UBT: Create a <ExeName>.version file for every target built by UBT, in the same JSON format as Engine/Build/Build.version. This allows monolithic targets to read a version number at runtime, unlike when it's embedded in a modules file, and allows creating versioned client executables that will work with versioned servers when syncing through UGS. Change 3575659 by Ben.Marsh Remove CHM API documentation. Change 3582103 by Graeme.Thornton Simple ResX writer implemetation that the xbox deloyment code can use instead of the one from the windows forms assembly, which isn't supported on .NET Core Removed reference to System.Windows.Form from UBT. Change 3584113 by Ben.Marsh Move key-mapping functionality into the InputCore module. Change 3584278 by Ben.Marsh Move FPlatformMisc::RequestMinimize() into FPlatformApplicationMisc. Change 3584453 by Ben.Marsh Move functionality for querying device display density to FApplicationMisc, due to dependence on application-level functionality on mobile platforms. Change 3585301 by Ben.Marsh Move PlatformPostInit() into an FPlatformApplicationMisc function. Change 3587050 by Ben.Marsh Move IsThisApplicationForeground() into FPlatformApplicationMisc. Change 3587059 by Ben.Marsh Move RequiresVirtualKeyboard() into FPlatformApplicationMisc. Change 3587119 by Ben.Marsh Move GetAbsoluteLogFilename() into FPlatformMisc. Change 3587800 by Steve.Robb Fixes to container visualizers for types whose pointer type isn't simply Type*. Change 3588393 by Ben.Marsh Move platform output devices into their own headers. Change 3588868 by Ben.Marsh Move creation of console, error and warning output devices int PlatformApplicationMisc. Change 3589879 by Graeme.Thornton All automation projects now have a reference to DotNETUtilities Fixed a build error in the WEX automation library Change 3590034 by Ben.Marsh Move functionality related to windowing and input out of the Core module and into an ApplicationCore module, so it is possible to build utilities with Core without adding dependencies on XInput (Windows), SDL (Linux), and OpenGL (Mac). Change 3593754 by Steve.Robb Fix for tuple debugger visualization. Change 3597208 by Ben.Marsh Move CrashReporter out of a public folder; it's not in a form that is usable by subscribers and licensees. Change 3600163 by Ben.Marsh UBT: Simplify how targets are cleaned. Delete all intermediate folders for a platform/configuration, and delete any build products matching the UE4 naming convention for that target, rather than relying on the current build configuration or list of previous build products. This will ensure that build products which are no longer being generated will also be cleaned. #jira UE-46725 Change 3604279 by Graeme.Thornton Move pre/post garbage collection delegates into accessor functions so they can be used by globally constructed objects Change 3606685 by James.Hopkin Removed redundant 'Cast's (casting to either the same type or a base). In SClassViewer, replaced cast with TAssetPtr::operator* call to get the wrapped UClass. Also removed redundant 'IsA's from AnimationRetargetContent::AddRemappedAsset in EditorAnimUtils.cpp. Change 3610950 by Ben.Marsh UAT: Simplify logic for detecting Perforce settings, using environment variables if they are set, otherwise falling back to detecting them. Removes special cases for build machines, and makes it simpler to set up UAT commands on builders outside Epic. Change 3610991 by Ben.Marsh UAT: Use the correct P4 settings to detect settings if only some parameters are specified on the command line. Change 3612342 by Ben.Marsh UBT: Change JsonObject.Read() to take a FileReference parameter. Change 3612362 by Ben.Marsh UBT: Remove some more cases of paths being passed as strings rather than using FileReference objects. Change 3619128 by Ben.Marsh Include builder warnings and errors in the notification emails for automated tests, otherwise it's difficult to track down non-test failures. [CL 3620189 by Ben Marsh in Main branch]
2017-08-31 12:08:38 -04:00
FBSPOps::csgCopyBrush( BuilderBrush, ClippedBrush, BuilderBrush->PolyFlags, BuilderBrush->GetFlags(), 0, true );
GEditor->Layers->DisassociateActorFromLayers( ClippedBrush );
World->EditorDestroyActor( ClippedBrush, false );
// Note that we're purposefully returning non-NULL here to report that the clip was successful,
// even though the ClippedBrush has been destroyed!
}
else
{
// Remove the old brush.
const int32 ClippedBrushIndex = BrushLevel->Actors.Num() - 1;
check( BrushLevel->Actors[ClippedBrushIndex] == ClippedBrush );
BrushLevel->Actors.RemoveAt(ClippedBrushIndex);
// Add the new brush right after the old brush.
const int32 OldBrushIndex = BrushLevel->Actors.Find( InBrush );
check( OldBrushIndex != INDEX_NONE );
BrushLevel->Actors.Insert( ClippedBrush, OldBrushIndex+1 );
}
return ClippedBrush;
}
} // namespace GeometryClipping
UGeomModifier_Clip::UGeomModifier_Clip(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "BrushClip", "Brush Clip");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Clip", "Tooltip", "Given a dividing plane, cut the geometry into two pieces, optionally discarding one of them. This operation only works in an orthographic viewport. Define the vertices of the dividing plane with the space bar, and press Enter to apply.");
bFlipNormal = false;
bSplit = false;
}
void UGeomModifier_Clip::WasActivated()
{
ClipMarkers.Empty();
}
bool UGeomModifier_Clip::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return mode->GetSelectionState() ? false : true;
}
bool UGeomModifier_Clip::OnApply()
{
ApplyClip( bSplit, bFlipNormal );
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
void UGeomModifier_Clip::ApplyClip( bool InSplit, bool InFlipNormal )
{
if ( !GLastKeyLevelEditingViewportClient )
{
return;
}
// Assemble the set of selected brushes.
TArray<ABrush*> Brushes;
for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It )
{
AActor* Actor = static_cast<AActor*>( *It );
checkSlow( Actor->IsA(AActor::StaticClass()) );
ABrush* Brush = Cast< ABrush >( Actor );
if( Brush )
{
Brushes.Add( Brush );
}
}
// Do nothing if no brushes are selected.
if ( Brushes.Num() == 0 )
{
return;
}
// Make sure enough clip markers have been placed.
if( ClipMarkers.Num() != 2 )
{
GeomError( NSLOCTEXT("UnrealEd", "Error_NotEnoughClipMarkers", "You haven't placed enough clip markers to perform this operation.").ToString() );
return;
}
// Focus has to be in an orthographic viewport so the editor can determine where the third point on the plane is
if( !GLastKeyLevelEditingViewportClient->IsOrtho() )
{
GeomError( NSLOCTEXT("UnrealEd", "Error_BrushClipViewportNotOrthographic", "The focus needs to be in an orthographic viewport for brush clipping to work.").ToString() );
return;
}
// Create a clipping plane based on ClipMarkers present in the level.
const FVector vtx1 = ClipMarkers[0];
const FVector vtx2 = ClipMarkers[1];
FVector vtx3;
// Compute the third vertex based on the viewport orientation.
vtx3 = vtx1;
switch( GLastKeyLevelEditingViewportClient->ViewportType )
{
case LVT_OrthoXY:
vtx3.Z -= 64;
break;
case LVT_OrthoXZ:
vtx3.Y -= 64;
break;
case LVT_OrthoYZ:
vtx3.X -= 64;
break;
}
// Perform the clip.
{
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "BrushClip", "Brush Clip") );
GEditor->SelectNone( false, true );
// Clip the brush list.
TArray<ABrush*> NewBrushes;
TArray<ABrush*> OldBrushes;
for ( int32 BrushIndex = 0 ; BrushIndex < Brushes.Num() ; ++BrushIndex )
{
ABrush* SrcBrush = Brushes[ BrushIndex ];
// Compute a clipping plane in the local frame of the brush.
const FTransform ToBrushWorld( SrcBrush->ActorToWorld() );
const FVector LocalVtx1( ToBrushWorld.InverseTransformPosition( vtx1 ) );
const FVector LocalVtx2( ToBrushWorld.InverseTransformPosition( vtx2 ) );
const FVector LocalVtx3( ToBrushWorld.InverseTransformPosition( vtx3 ) );
FVector PlaneNormal( (LocalVtx2 - LocalVtx1) ^ (LocalVtx3 - LocalVtx1) );
if( PlaneNormal.SizeSquared() < THRESH_ZERO_NORM_SQUARED )
{
GeomError( NSLOCTEXT("UnrealEd", "Error_ClipUnableToComputeNormal", "Unable to compute normal for brush clip!").ToString() );
continue;
}
PlaneNormal.Normalize();
FPlane ClippingPlane( LocalVtx1, PlaneNormal );
if ( InFlipNormal )
{
ClippingPlane = ClippingPlane.Flip();
}
// Is the brush a builder brush?
const bool bIsBuilderBrush = FActorEditorUtils::IsABuilderBrush(SrcBrush);
// Perform the clip.
bool bCreatedBrush = false;
ABrush* NewBrush = GeometryClipping::ClipBrushAgainstPlane( ClippingPlane, SrcBrush );
if ( NewBrush )
{
// Select the src brush for builders, or the returned brush for non-builders.
if ( !bIsBuilderBrush )
{
NewBrushes.Add( NewBrush );
}
else
{
NewBrushes.Add( SrcBrush );
}
bCreatedBrush = true;
}
// If we're doing a split instead of just a plain clip . . .
if( InSplit )
{
// Don't perform a second clip if the builder brush was already split.
if ( !bIsBuilderBrush || !bCreatedBrush )
{
// Clip the brush against the flipped clipping plane.
ABrush* NewBrush2 = GeometryClipping::ClipBrushAgainstPlane( ClippingPlane.Flip(), SrcBrush );
if ( NewBrush2 )
{
// We don't add the brush to the list of new brushes, so that only new brushes
// in the non-cleaved halfspace of the clipping plane will be selected.
bCreatedBrush = true;
}
}
}
// Destroy source brushes that aren't builders.
if ( !bIsBuilderBrush )
{
OldBrushes.Add( SrcBrush );
}
}
// Clear selection to prevent the second clipped brush from being selected.
// When both are selected, it's hard to tell that the brush is clipped.
GEditor->SelectNone( false, true );
// Delete old brushes.
for ( int32 BrushIndex = 0 ; BrushIndex < OldBrushes.Num() ; ++BrushIndex )
{
ABrush* OldBrush = OldBrushes[ BrushIndex ];
GEditor->Layers->DisassociateActorFromLayers( OldBrush );
OldBrush->GetWorld()->EditorDestroyActor( OldBrush, true );
}
// Select new brushes.
for ( int32 BrushIndex = 0 ; BrushIndex < NewBrushes.Num() ; ++BrushIndex )
{
ABrush* NewBrush = NewBrushes[ BrushIndex ];
GEditor->SelectActor( NewBrush, true, false );
}
// Notify editor of new selection state.
GEditor->NoteSelectionChange();
}
FEdModeGeometry* Mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
Mode->FinalizeSourceData();
Mode->GetFromSource();
}
bool UGeomModifier_Clip::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
bool bResult = false;
if( ViewportClient->IsOrtho() && Event == IE_Pressed )
{
const bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl);
const bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift);
const bool bAltDown = Viewport->KeyState(EKeys::LeftAlt) || Viewport->KeyState(EKeys::RightAlt);
if( (bCtrlDown && !bShiftDown && !bAltDown && Key == EKeys::RightMouseButton) || Key == EKeys::SpaceBar )
{
// if the user has 2 markers placed and the click location is on top of the second point, perform the cllck. This is a shortcut the LDs wanted.
if( ClipMarkers.Num() == 2 )
{
const FVector* Pos = &ClipMarkers[1];
if( Pos->Equals( SnappedMouseWorldSpacePos ) )
{
OnApply();
return true;
}
}
// If there are already 2 clip markers in the world, clear them out.
if( ClipMarkers.Num() > 1 )
{
ClipMarkers.Empty();
}
ClipMarkers.Add( SnappedMouseWorldSpacePos );
bResult = true;
}
else if( Key == EKeys::Escape || Key == EKeys::BackSpace )
{
if( ClipMarkers.Num() )
{
ClipMarkers.RemoveAt( ClipMarkers.Num() - 1 );
}
bResult = true;
}
else if( Key == EKeys::Enter )
{
// If the user has 1 marker placed when they press ENTER, go ahead and place a second one at the current mouse location.
// This allows LDs to place one point, move to a good spot and press ENTER for a quick clip.
if( ClipMarkers.Num() == 1 )
{
ClipMarkers.Add( SnappedMouseWorldSpacePos );
}
ApplyClip( bAltDown, bShiftDown );
bResult = true;
}
}
if( bResult )
{
GEditor->RedrawLevelEditingViewports( true );
}
return bResult;
}
void UGeomModifier_Clip::Render(const FSceneView* View,FViewport* Viewport,FPrimitiveDrawInterface* PDI)
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
FModeTool_GeometryModify* tool = (FModeTool_GeometryModify*)mode->GetCurrentTool();
if( tool->GetCurrentModifier() != this )
{
return;
}
// Only draw in ortho viewports
if( !((FEditorViewportClient*)(Viewport->GetClient()))->IsOrtho() )
{
return;
}
// Draw a yellow box on each clip marker
for( int32 x = 0 ; x < ClipMarkers.Num() ; ++x )
{
FVector* vtx = &ClipMarkers[x];
PDI->DrawPoint( *vtx, FLinearColor(1,0,0), 6.f, SDPG_Foreground );
}
// If 2 markers are placed, draw a line connecting them and a line showing the clip normal.
// If 1 marker is placed, draw a dashed line and normal to show where the clip plane will appear if the user commits.
if( ClipMarkers.Num() )
{
FVector LineStart = ClipMarkers[0];
FVector LineEnd = (ClipMarkers.Num() == 2) ? ClipMarkers[1] : SnappedMouseWorldSpacePos;
if( ClipMarkers.Num() == 1 )
{
DrawDashedLine( PDI, LineStart, LineEnd, FLinearColor(1,.5,0), GEditor->GetGridSize(), SDPG_Foreground );
}
else
{
PDI->DrawLine( LineStart, LineEnd, FLinearColor(1,0,0), SDPG_Foreground );
}
FVector vtx1, vtx2, vtx3;
FPoly NormalPoly;
vtx1 = LineStart;
vtx2 = LineEnd;
vtx3 = vtx1;
const FEditorViewportClient* ViewportClient = static_cast<FEditorViewportClient*>( Viewport->GetClient() );
switch( ViewportClient->ViewportType )
{
case LVT_OrthoXY:
vtx3.Z -= 64;
break;
case LVT_OrthoXZ:
vtx3.Y -= 64;
break;
case LVT_OrthoYZ:
vtx3.X -= 64;
break;
}
NormalPoly.Vertices.Add( vtx1 );
NormalPoly.Vertices.Add( vtx2 );
NormalPoly.Vertices.Add( vtx3 );
if( !NormalPoly.CalcNormal(1) )
{
FVector Start = ( vtx1 + vtx2 ) / 2.f;
float NormalLength = (vtx2 - vtx1).Size() / 2.f;
if( ClipMarkers.Num() == 1 )
{
DrawDashedLine( PDI, Start, Start + NormalPoly.Normal * NormalLength, FLinearColor(1,.5,0), GEditor->GetGridSize(), SDPG_Foreground );
}
else
{
PDI->DrawLine( Start, Start + NormalPoly.Normal * NormalLength, FLinearColor(1,0,0), SDPG_Foreground );
}
}
}
// Draw a box at the cursor location
int32 BoxSz = FMath::Max( GEditor->GetGridSize() / 2, 1.f );
DrawWireBox(PDI, FBox::BuildAABB( SnappedMouseWorldSpacePos, FVector(BoxSz,BoxSz,BoxSz) ), FLinearColor(1,1,1), SDPG_Foreground);
}
void UGeomModifier_Clip::DrawHUD(FEditorViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View,FCanvas* Canvas)
{
}
void UGeomModifier_Clip::Tick(FEditorViewportClient* ViewportClient,float DeltaTime)
{
if( GCurrentLevelEditingViewportClient == ViewportClient )
{
// Figure out where the nearest grid location is to the mouse cursor
FVector NewSnappedMouseWorldSpacePos = ComputeWorldSpaceMousePos(ViewportClient).GridSnap( GEditor->GetGridSize() );
// If the snapped mouse position has moved, update the viewport
if( NewSnappedMouseWorldSpacePos != SnappedMouseWorldSpacePos )
{
SnappedMouseWorldSpacePos = NewSnappedMouseWorldSpacePos;
GEditor->RedrawLevelEditingViewports( true );
}
}
}
UGeomModifier_Delete::UGeomModifier_Delete(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Delete", "Delete");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Delete", "Tooltip", "Deletes the selected geometry elements (vertices, edges or polygons).");
bPushButton = true;
}
bool UGeomModifier_Delete::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return (mode->HavePolygonsSelected() || mode->HaveVerticesSelected());
}
bool UGeomModifier_Delete::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
bool bHandled = false;
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
// Polys
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() )
{
gp->GetParentObject()->GetActualBrush()->Brush->Polys->Element[ gp->ActualPolyIndex ].PolyFlags |= PF_GeomMarked;
bHandled = 1;
}
}
for( int32 p = 0 ; p < go->GetActualBrush()->Brush->Polys->Element.Num() ; ++p )
{
if( (go->GetActualBrush()->Brush->Polys->Element[ p ].PolyFlags&PF_GeomMarked) > 0 )
{
go->GetActualBrush()->Brush->Polys->Element.RemoveAt( p );
p = -1;
}
}
// Verts
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex* gv = &go->VertexPool[v];
if( gv->IsSelected() )
{
for( int32 x = 0 ; x < gv->GetParentObject()->GetActualBrush()->Brush->Polys->Element.Num() ; ++x )
{
FPoly* Poly = &gv->GetParentObject()->GetActualBrush()->Brush->Polys->Element[x];
Poly->RemoveVertex( *gv );
bHandled = 1;
}
}
}
go->GetActualBrush()->SavedSelections.Empty();
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
// Reset the pivot point to the newest selected object.
AActor* SelectedActor = Cast<AActor>(GEditor->GetSelectedActors()->GetBottom(AActor::StaticClass()));
GEditor->GetSelectedActors()->Modify();
if(SelectedActor)
{
FEditorModeTools& Tools = GLevelEditorModeTools();
Tools.SetPivotLocation( SelectedActor->GetActorLocation() , false );
}
bPendingPivotOffsetUpdate = true;
return bHandled;
}
UGeomModifier_Create::UGeomModifier_Create(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Create", "Create");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Create", "Tooltip", "Creates a new polygon from the selected vertices. The vertices must be selected in clockwise order to create a poly with an outward facing normal.");
bPushButton = true;
}
bool UGeomModifier_Create::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return mode->HaveVerticesSelected();
}
bool UGeomModifier_Create::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
go->CompileSelectionOrder();
// Create an ordered list of vertices based on the selection order.
TArray<FGeomVertex*> Verts;
for( int32 x = 0 ; x < go->SelectionOrder.Num() ; ++x )
{
FGeomBase* obj = go->SelectionOrder[x];
if( obj->IsVertex() )
{
Verts.Add( (FGeomVertex*)obj );
}
}
if( Verts.Num() > 2 )
{
// Create new geometry based on the selected vertices
FPoly* NewPoly = new( go->GetActualBrush()->Brush->Polys->Element )FPoly();
NewPoly->Init();
for( int32 x = 0 ; x < Verts.Num() ; ++x )
{
FGeomVertex* gv = Verts[x];
new(NewPoly->Vertices) FVector(*gv);
}
NewPoly->Normal = FVector::ZeroVector;
NewPoly->Base = *Verts[0];
NewPoly->PolyFlags = PF_DefaultFlags;
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
UGeomModifier_Flip::UGeomModifier_Flip(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Flip", "Flip");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Flip", "Tooltip", "Flips the normal of the selected polygon so that it faces the other way.");
bPushButton = true;
}
bool UGeomModifier_Flip::Supports()
{
// Supports polygons selected and objects selected
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return (!mode->HaveEdgesSelected() && !mode->HaveVerticesSelected());
}
bool UGeomModifier_Flip::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
bool bHavePolygonsSelected = mode->HavePolygonsSelected();
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() || !bHavePolygonsSelected )
{
FPoly* Poly = &go->GetActualBrush()->Brush->Polys->Element[ gp->ActualPolyIndex ];
Poly->Reverse();
}
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
UGeomModifier_Split::UGeomModifier_Split(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Split", "Split");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Pen", "Split_Tooltip", "Split a brush in half, the exact operation depending on which geometry elements are selected.");
bPushButton = true;
}
bool UGeomModifier_Split::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// This modifier assumes that a single geometry object is selected
if( mode->CountObjectsSelected() != 1 )
{
return false;
}
int32 NumPolygonsSelected = mode->CountSelectedPolygons();
int32 NumEdgesSelected = mode->CountSelectedEdges();
int32 NumVerticesSelected = mode->CountSelectedVertices();
if( (NumPolygonsSelected == 1 && NumEdgesSelected == 1 && NumVerticesSelected == 0) // Splitting a face at an edge mid point (scalpel)
|| (NumPolygonsSelected == 0 && NumEdgesSelected > 0 && NumVerticesSelected == 0) // Splitting a brush at an edge mid point (ring cut)
|| (NumPolygonsSelected == 1 && NumEdgesSelected == 0 && NumVerticesSelected == 2) // Splitting a polygon across 2 vertices
|| (NumPolygonsSelected == 0 && NumEdgesSelected == 0 && NumVerticesSelected == 2) // Splitting a brush across 2 vertices
)
{
return true;
}
return false;
}
bool UGeomModifier_Split::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// Get a pointer to the selected geom object
FGeomObjectPtr GeomObject = NULL;
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
GeomObject = *Itor;
break;
}
if( !GeomObject.IsValid() )
{
return false;
}
// Count up how many of each subobject are selected so we can determine what the user is trying to split
int32 NumPolygonsSelected = mode->CountSelectedPolygons();
int32 NumEdgesSelected = mode->CountSelectedEdges();
int32 NumVerticesSelected = mode->CountSelectedVertices();
if( NumPolygonsSelected == 1 && NumEdgesSelected == 1 && NumVerticesSelected == 0 )
{
//
// Splitting a face at an edge mid point (scalpel)
//
// Get the selected edge
TArray<FGeomEdge*> Edges;
mode->GetSelectedEdges( Edges );
check( Edges.Num() == 1 );
FGeomEdge* SelectedEdge = Edges[0];
// Figure out the verts that are part of that edge
FGeomVertex* Vertex0 = &GeomObject->VertexPool[ SelectedEdge->VertexIndices[0] ];
FGeomVertex* Vertex1 = &GeomObject->VertexPool[ SelectedEdge->VertexIndices[1] ];
const FVector Vtx0 = *Vertex0->GetActualVertex( Vertex0->ActualVertexIndices[0] );
const FVector Vtx1 = *Vertex1->GetActualVertex( Vertex1->ActualVertexIndices[0] );
// Get the selected polygon
TArray<FGeomPoly*> Polygons;
mode->GetSelectedPolygons( Polygons );
check( Polygons.Num() == 1 );
FGeomPoly* Polygon = Polygons[0];
FPoly* SelectedPoly = Polygon->GetActualPoly();
// Get the selected brush
ABrush* Brush = GeomObject->GetActualBrush();
//
// Sanity checking
//
{
// 1. Make sure that the selected edge is part of the selected polygon
if( !SelectedPoly->Vertices.Contains( Vtx0 ) || !SelectedPoly->Vertices.Contains( Vtx1 ) )
{
GeomError( NSLOCTEXT("UnrealEd", "Error_SelectedEdgeMustBelongToSelectedPoly", "The edge used for splitting must be part of the selected polygon.").ToString() );
return false;
}
}
// Generate a base and a normal for the cutting plane
const FVector PlaneNormal( (Vtx1 - Vtx0).GetSafeNormal() );
const FVector PlaneBase = 0.5f*(Vtx1 + Vtx0);
// Clip the selected polygon against the cutting plane
FPoly Front, Back;
Front.Init();
Back.Init();
int32 Res = SelectedPoly->SplitWithPlane( PlaneBase, PlaneNormal, &Front, &Back, 1 );
if( Res == SP_Split )
{
TArray<FPoly> NewPolygons;
NewPolygons.Add( Front );
NewPolygons.Add( Back );
// At this point, see if any other polygons in the brush need to have a vertex added to an edge
FPlane CuttingPlane( PlaneBase, PlaneNormal );
for( int32 p = 0 ; p < Brush->Brush->Polys->Element.Num() ; ++p )
{
FPoly* P = &Brush->Brush->Polys->Element[p];
if( P != SelectedPoly )
{
for( int32 v = 0 ; v < P->Vertices.Num() ; ++v )
{
FVector* v0 = &P->Vertices[v];
FVector* v1 = &P->Vertices[ (v + 1) % P->Vertices.Num() ];
// Make sure the line formed by the edge actually crosses the plane before checking for the intersection point.
if( FMath::IsNegativeFloat( CuttingPlane.PlaneDot( *v0 ) ) != FMath::IsNegativeFloat( CuttingPlane.PlaneDot( *v1 ) ) )
{
FVector Intersection = FMath::LinePlaneIntersection( *v0, *v1, CuttingPlane );
// Make sure that the intersection point lies on the same plane as the selected polygon as we only need to add it there and not
// to any other edge that might intersect the cutting plane.
if( SelectedPoly->OnPlane( Intersection ) )
{
P->Vertices.Insert( Intersection, (v+1) % P->Vertices.Num() );
break;
}
}
}
NewPolygons.Add( *P );
}
}
// Replace the old polygon list with the new one
Brush->Brush->Polys->Element = NewPolygons;
}
}
else if( NumPolygonsSelected == 0 && NumEdgesSelected > 0 && NumVerticesSelected == 0 )
{
//
// Splitting a brush at an edge mid point (ring cut)
//
// Get the selected edge
TArray<FGeomEdge*> Edges;
mode->GetSelectedEdges( Edges );
check( Edges.Num() > 0 );
FGeomEdge* Edge = Edges[0];
// Generate a base and a normal for the cutting plane
FGeomVertex* Vertex0 = &GeomObject->VertexPool[ Edge->VertexIndices[0] ];
FGeomVertex* Vertex1 = &GeomObject->VertexPool[ Edge->VertexIndices[1] ];
const FVector v0 = *Vertex0->GetActualVertex( Vertex0->ActualVertexIndices[0] );
const FVector v1 = *Vertex1->GetActualVertex( Vertex1->ActualVertexIndices[0] );
const FVector PlaneNormal( (v1 - v0).GetSafeNormal() );
const FVector PlaneBase = 0.5f*(v1 + v0);
ABrush* Brush = GeomObject->GetActualBrush();
// The polygons for the new brush are stored in here and the polys inside of the original brush are replaced at the end of the loop
TArray<FPoly> NewPolygons;
// Clip each polygon against the cutting plane
for( int32 p = 0 ; p < Brush->Brush->Polys->Element.Num() ; ++p )
{
FPoly* Poly = &Brush->Brush->Polys->Element[p];
FPoly Front, Back;
Front.Init();
Back.Init();
int32 Res = Poly->SplitWithPlane( PlaneBase, PlaneNormal, &Front, &Back, 1 );
switch( Res )
{
case SP_Split:
NewPolygons.Add( Front );
NewPolygons.Add( Back );
break;
default:
NewPolygons.Add( *Poly );
break;
}
}
// Replace the old polygon list with the new one
Brush->Brush->Polys->Element = NewPolygons;
}
else if( NumPolygonsSelected == 1 && NumEdgesSelected == 0 && NumVerticesSelected == 2 )
{
//
// Splitting a polygon across 2 vertices
//
// Get the selected verts
TArray<FGeomVertex*> Verts;
mode->GetSelectedVertices( Verts );
check( Verts.Num() == 2 );
FGeomVertex* Vertex0 = Verts[0];
FGeomVertex* Vertex1 = Verts[1];
const FVector v0 = *Vertex0->GetActualVertex( Vertex0->ActualVertexIndices[0] );
const FVector v1 = *Vertex1->GetActualVertex( Vertex1->ActualVertexIndices[0] );
// Get the selected polygon
TArray<FGeomPoly*> Polys;
mode->GetSelectedPolygons( Polys );
check( Polys.Num() == 1 );
FGeomPoly* SelectedPoly = Polys[0];
FPoly* Poly = SelectedPoly->GetActualPoly();
//
// Sanity checking
//
{
// 1. Make sure that the selected vertices are part of the selected polygon
if( !SelectedPoly->GetActualPoly()->Vertices.Contains( v0 ) || !SelectedPoly->GetActualPoly()->Vertices.Contains( v1 ) )
{
GeomError( NSLOCTEXT("UnrealEd", "Error_SelectedVerticesMustBelongToSelectedPoly", "The vertices used for splitting must be part of the selected polygon.").ToString() );
return false;
}
}
// Generate a base and a normal for the cutting plane
FVector v2 = v0 + (SelectedPoly->GetNormal() * 64.0f);
const FPlane PlaneNormal( v0, v1, v2 );
const FVector PlaneBase = 0.5f*(v1 + v0);
ABrush* Brush = GeomObject->GetActualBrush();
// The polygons for the new brush are stored in here and the polys inside of the original brush are replaced at the end of the loop
TArray<FPoly> NewPolygons;
// Clip the selected polygon against the cutting plane.
for( int32 p = 0 ; p < Brush->Brush->Polys->Element.Num() ; ++p )
{
FPoly* P = &Brush->Brush->Polys->Element[p];
if( P == Poly )
{
FPoly Front, Back;
Front.Init();
Back.Init();
int32 Res = P->SplitWithPlane( PlaneBase, PlaneNormal, &Front, &Back, 1 );
switch( Res )
{
case SP_Split:
NewPolygons.Add( Front );
NewPolygons.Add( Back );
break;
default:
NewPolygons.Add( *P );
break;
}
}
else
{
NewPolygons.Add( *P );
}
}
// Replace the old polygon list with the new one
Brush->Brush->Polys->Element = NewPolygons;
}
else if( NumPolygonsSelected == 0 && NumEdgesSelected == 0 && NumVerticesSelected == 2 )
{
//
// Splitting a brush across 2 vertices
//
// Get the selected verts
TArray<FGeomVertex*> Verts;
mode->GetSelectedVertices( Verts );
check( Verts.Num() == 2 );
// Generate a base and a normal for the cutting plane
FGeomVertex* Vertex0 = Verts[0];
FGeomVertex* Vertex1 = Verts[1];
const FVector v0 = *Vertex0->GetActualVertex( Vertex0->ActualVertexIndices[0] );
const FVector v1 = *Vertex1->GetActualVertex( Vertex1->ActualVertexIndices[0] );
FVector v2 = ((Vertex0->GetNormal() + Vertex1->GetNormal()) / 2.0f) * 64.f;
const FPlane PlaneNormal( v0, v1, v2 );
const FVector PlaneBase = 0.5f*(v1 + v0);
ABrush* Brush = GeomObject->GetActualBrush();
// The polygons for the new brush are stored in here and the polys inside of the original brush are replaced at the end of the loop
TArray<FPoly> NewPolygons;
// Clip each polygon against the cutting plane
for( int32 p = 0 ; p < Brush->Brush->Polys->Element.Num() ; ++p )
{
FPoly* Poly = &Brush->Brush->Polys->Element[p];
FPoly Front, Back;
Front.Init();
Back.Init();
int32 Res = Poly->SplitWithPlane( PlaneBase, PlaneNormal, &Front, &Back, 1 );
switch( Res )
{
case SP_Split:
NewPolygons.Add( Front );
NewPolygons.Add( Back );
break;
default:
NewPolygons.Add( *Poly );
break;
}
}
// Replace the old polygon list with the new one
Brush->Brush->Polys->Element = NewPolygons;
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
bPendingPivotOffsetUpdate = true;
return true;
}
UGeomModifier_Triangulate::UGeomModifier_Triangulate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Triangulate", "Triangulate");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Triangulate", "Tooltip", "Break the selected polygons down into triangles.");
bPushButton = true;
}
bool UGeomModifier_Triangulate::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return (!mode->HaveEdgesSelected() && !mode->HaveVerticesSelected());
}
bool UGeomModifier_Triangulate::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
bool bHavePolygonsSelected = mode->HavePolygonsSelected();
// Mark the selected polygons so we can find them in the next loop, and create
// a local list of FPolys to triangulate later.
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
TArray<FPoly> PolyList;
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() || !bHavePolygonsSelected )
{
gp->GetParentObject()->GetActualBrush()->Brush->Polys->Element[ gp->ActualPolyIndex ].PolyFlags |= PF_GeomMarked;
PolyList.Add( gp->GetParentObject()->GetActualBrush()->Brush->Polys->Element[ gp->ActualPolyIndex ] );
}
}
// Delete existing polygons
for( int32 p = 0 ; p < go->GetActualBrush()->Brush->Polys->Element.Num() ; ++p )
{
if( (go->GetActualBrush()->Brush->Polys->Element[ p ].PolyFlags&PF_GeomMarked) > 0 )
{
go->GetActualBrush()->Brush->Polys->Element.RemoveAt( p );
p = -1;
}
}
// Triangulate the old polygons into the brush
for( int32 p = 0 ; p < PolyList.Num() ; ++p )
{
TArray<FPoly> Triangles;
PolyList[p].Triangulate( go->GetActualBrush(), Triangles );
for( int32 t = 0 ; t < Triangles.Num() ; ++t )
{
go->GetActualBrush()->Brush->Polys->Element.Add( Triangles[t] );
}
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
return true;
}
UGeomModifier_Optimize::UGeomModifier_Optimize(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Optimize", "Optimize");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Optimize", "Tooltip", "Optimizes the selected geometry by merging together any polygons which can be formed into a single convex polygon.");
bPushButton = true;
}
bool UGeomModifier_Optimize::OnApply()
{
// First triangulate before performing optimize
Super::OnApply();
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
TArray<FPoly> Polygons;
if( mode->HavePolygonsSelected() )
{
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
ABrush* ActualBrush = go->GetActualBrush();
// Gather a list of polygons that are
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[p];
if( gp->IsSelected() )
{
ActualBrush->Brush->Polys->Element[ gp->ActualPolyIndex ].PolyFlags |= PF_GeomMarked;
Polygons.Add( ActualBrush->Brush->Polys->Element[ gp->ActualPolyIndex ] );
}
}
// Delete existing polygons
for( int32 p = 0 ; p < go->GetActualBrush()->Brush->Polys->Element.Num() ; ++p )
{
if( (ActualBrush->Brush->Polys->Element[ p ].PolyFlags&PF_GeomMarked) > 0 )
{
ActualBrush->Brush->Polys->Element.RemoveAt( p );
p = -1;
}
}
// Optimize the polygons in the list
FPoly::OptimizeIntoConvexPolys( ActualBrush, Polygons );
// Copy the new polygons into the brush
for( int32 p = 0 ; p < Polygons.Num() ; ++p )
{
FPoly Poly = Polygons[p];
Poly.PolyFlags &= ~PF_GeomMarked;
ActualBrush->Brush->Polys->Element.Add( Poly );
}
}
}
else
{
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
ABrush* ActualBrush = go->GetActualBrush();
// Optimize the polygons
FPoly::OptimizeIntoConvexPolys( ActualBrush, ActualBrush->Brush->Polys->Element );
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
return true;
}
UGeomModifier_Turn::UGeomModifier_Turn(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Turn", "Turn");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Turn", "Tooltip", "Given a selected edge common to two triangles, turn the edge so that it is connected to the previously unconnected vertices.");
bPushButton = true;
}
bool UGeomModifier_Turn::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return mode->HaveEdgesSelected();
}
bool UGeomModifier_Turn::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// Edges
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
TArray<FGeomEdge> Edges;
go->CompileUniqueEdgeArray( &Edges );
// Make sure that all polygons involved are triangles
for( int32 e = 0 ; e < Edges.Num() ; ++e )
{
FGeomEdge* ge = &Edges[e];
for( int32 p = 0 ; p < ge->ParentPolyIndices.Num() ; ++p )
{
FGeomPoly* gp = &go->PolyPool[ ge->ParentPolyIndices[p] ];
FPoly* Poly = gp->GetActualPoly();
if( Poly->Vertices.Num() != 3 )
{
FNotificationInfo NotificationInfo(LOCTEXT("Error_PolygonsOnEdgeToTurnMustBeTriangles", "The polygons on each side of the edge you want to turn must be triangles."));
NotificationInfo.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(NotificationInfo);
EndTrans();
return 0;
}
}
}
// Turn the edges, one by one
for( int32 e = 0 ; e < Edges.Num() ; ++e )
{
FGeomEdge* ge = &Edges[e];
TArray<FVector> Quad;
// Since we're doing each edge individually, they should each have exactly 2 polygon
// parents (and each one is a triangle (verified above))
if( ge->ParentPolyIndices.Num() == 2 )
{
FGeomPoly* gp = &go->PolyPool[ ge->ParentPolyIndices[0] ];
FPoly* Poly = gp->GetActualPoly();
FPoly SavePoly0 = *Poly;
int32 idx0 = Poly->GetVertexIndex( go->VertexPool[ ge->VertexIndices[0] ] );
int32 idx1 = Poly->GetVertexIndex( go->VertexPool[ ge->VertexIndices[1] ] );
int32 idx2 = INDEX_NONE;
if( idx0 + idx1 == 1 )
{
idx2 = 2;
}
else if( idx0 + idx1 == 3 )
{
idx2 = 0;
}
else
{
idx2 = 1;
}
Quad.Add( Poly->Vertices[idx0] );
Quad.Add( Poly->Vertices[idx2] );
Quad.Add( Poly->Vertices[idx1] );
gp = &go->PolyPool[ ge->ParentPolyIndices[1] ];
Poly = gp->GetActualPoly();
FPoly SavePoly1 = *Poly;
for( int32 v = 0 ; v < Poly->Vertices.Num() ; ++v )
{
Quad.AddUnique( Poly->Vertices[v] );
}
// If the adjoining polys were coincident, don't try to turn the edge
if (Quad.Num() == 3)
{
continue;
}
// Create new polygons
FPoly* NewPoly;
NewPoly = new( gp->GetParentObject()->GetActualBrush()->Brush->Polys->Element )FPoly();
NewPoly->Init();
new(NewPoly->Vertices) FVector(Quad[2]);
new(NewPoly->Vertices) FVector(Quad[1]);
new(NewPoly->Vertices) FVector(Quad[3]);
NewPoly->Base = SavePoly0.Base;
NewPoly->Material = SavePoly0.Material;
NewPoly->PolyFlags = SavePoly0.PolyFlags;
NewPoly->TextureU = SavePoly0.TextureU;
NewPoly->TextureV = SavePoly0.TextureV;
NewPoly->Normal = FVector::ZeroVector;
NewPoly->Finalize(go->GetActualBrush(),1);
NewPoly = new( gp->GetParentObject()->GetActualBrush()->Brush->Polys->Element )FPoly();
NewPoly->Init();
new(NewPoly->Vertices) FVector(Quad[3]);
new(NewPoly->Vertices) FVector(Quad[1]);
new(NewPoly->Vertices) FVector(Quad[0]);
NewPoly->Base = SavePoly1.Base;
NewPoly->Material = SavePoly1.Material;
NewPoly->PolyFlags = SavePoly1.PolyFlags;
NewPoly->TextureU = SavePoly1.TextureU;
NewPoly->TextureV = SavePoly1.TextureV;
NewPoly->Normal = FVector::ZeroVector;
NewPoly->Finalize(go->GetActualBrush(),1);
// Tag the old polygons
for( int32 p = 0 ; p < ge->ParentPolyIndices.Num() ; ++p )
{
FGeomPoly* GeomPoly = &go->PolyPool[ ge->ParentPolyIndices[p] ];
go->GetActualBrush()->Brush->Polys->Element[GeomPoly->ActualPolyIndex].PolyFlags |= PF_GeomMarked;
}
}
}
// Delete the old polygons
for( int32 p = 0 ; p < go->GetActualBrush()->Brush->Polys->Element.Num() ; ++p )
{
if( (go->GetActualBrush()->Brush->Polys->Element[ p ].PolyFlags&PF_GeomMarked) > 0 )
{
go->GetActualBrush()->Brush->Polys->Element.RemoveAt( p );
p = -1;
}
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
return true;
}
UGeomModifier_Weld::UGeomModifier_Weld(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Description = NSLOCTEXT("UnrealEd", "Weld", "Weld");
Tooltip = NSLOCTEXT("UnrealEd.GeomModifier_Weld", "Tooltip", "Merge all selected vertices to the first selected vertex.");
bPushButton = true;
}
bool UGeomModifier_Weld::Supports()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
return (mode->HaveVerticesSelected() && !mode->HaveEdgesSelected() && !mode->HavePolygonsSelected());
}
bool UGeomModifier_Weld::OnApply()
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry);
// Verts
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
go->CompileSelectionOrder();
if( go->SelectionOrder.Num() > 1 )
{
//NOTE: function assumes ONLY vertices are selected, UGeomModifier_Weld::Supports must ensure this.
FGeomVertex* FirstSel = (FGeomVertex*)go->SelectionOrder[0];
// Move all selected vertices to the location of the first vertex that was selected.
for( int32 v = 1 ; v < go->SelectionOrder.Num() ; ++v )
{
FGeomVertex* gv = (FGeomVertex*)go->SelectionOrder[v];
if( gv->IsSelected() )
{
gv->X = FirstSel->X;
gv->Y = FirstSel->Y;
gv->Z = FirstSel->Z;
}
}
go->SendToSource();
}
}
mode->FinalizeSourceData();
mode->GetFromSource();
GEditor->RebuildAlteredBSP(); // Brush has been altered, update the Bsp
//finally, cache the selections AFTER the weld and set the widget to the appropriate selection
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObjectPtr go = *Itor;
go->CompileSelectionOrder();
ABrush* Actor = go->GetActualBrush();
StoreCurrentGeomSelections( Actor->SavedSelections , go );
go->SelectNone();
int32 res = go->SetPivotFromSelectionArray( Actor->SavedSelections );
if( res == INDEX_NONE )
{
FEditorModeTools& Tools = GLevelEditorModeTools();
Tools.SetPivotLocation( Actor->GetActorLocation() , false );
}
go->ForceLastSelectionIndex( res );
}
return true;
}
#undef LOCTEXT_NAMESPACE