Files
UnrealEngineUWP/Engine/Source/Editor/GeometryMode/Private/GeometryEdMode.cpp
Andrew Rodham ba3528c9d4 Made it possible for asset editors to maintain their own FEditorModeTools lists
Breaking changes include:
    * Rename of GEditorModeTools -> GLevelEditorModeTools to signify that it applies only to the level editor modes
    * Addition of FEditorModeRegistry, responsible for managing and creating new editor modes. Modes are no longer registered with an instance of the mode, instead with a mode factory that is able to create a new mode of that type.
    * Editor modes now operate on FEditorViewportClients rather than FLevelEditorViewportClients
    * Added ability to specify an FEditorModeTools when creating an FEditorViewport

Moved component vizualiser manager handling outside of individual editor modes, and into FLevelEditorViewportClient. This should make it easier to transplant in future.

This work addresses TTP#334640 - EDITOR: Investigate making editor modes a per-'editor' concept

Reviewed by Michael Noland, Matt Kuhlenschmidt

[CL 2109245 by Andrew Rodham in Main branch]
2014-06-18 10:16:16 -04:00

1305 lines
35 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "GeometryModePrivatePCH.h"
#include "DynamicMeshBuilder.h"
IMPLEMENT_MODULE( FGeometryModeModule, GeometryMode );
DEFINE_LOG_CATEGORY_STATIC(LogGeometryMode, Log, All);
void FGeometryModeModule::StartupModule()
{
FEditorModeRegistry::Get().RegisterMode<FEdModeGeometry>(
FBuiltinEditorModes::EM_Geometry,
NSLOCTEXT("EditorModes", "GeometryMode", "Geometry Editing"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.BspMode", "LevelEditor.BspMode.Small"),
true
);
}
void FGeometryModeModule::ShutdownModule()
{
FEditorModeRegistry::Get().UnregisterMode(FBuiltinEditorModes::EM_Geometry);
}
/*------------------------------------------------------------------------------
Geometry Editing.
------------------------------------------------------------------------------*/
FEdModeGeometry::FEdModeGeometry()
{
Tools.Add( new FModeTool_GeometryModify() );
SetCurrentTool( MT_GeometryModify );
}
FEdModeGeometry::~FEdModeGeometry()
{
for( int32 i=0; i<GeomObjects.Num(); i++ )
{
FGeomObject* GeomObject = GeomObjects[i];
delete GeomObject;
}
GeomObjects.Empty();
}
void FEdModeGeometry::Render(const FSceneView* View,FViewport* Viewport,FPrimitiveDrawInterface* PDI)
{
FEdMode::Render(View,Viewport,PDI);
RenderVertex( View, PDI );
RenderEdge( View, PDI );
RenderPoly( View, Viewport, PDI );
}
bool FEdModeGeometry::ShowModeWidgets() const
{
return 1;
}
bool FEdModeGeometry::ShouldDrawBrushWireframe( AActor* InActor ) const
{
checkSlow( InActor );
// If the actor isn't selected, we don't want to interfere with it's rendering.
if( !GEditor->GetSelectedActors()->IsSelected( InActor ) )
{
return true;
}
return true;//false;
}
bool FEdModeGeometry::GetCustomDrawingCoordinateSystem( FMatrix& InMatrix, void* InData )
{
if( GetSelectionState() == GSS_None )
{
return 0;
}
if( InData )
{
InMatrix = FRotationMatrix( ((FGeomBase*)InData)->GetNormal().Rotation() );
}
else
{
// If we don't have a specific geometry object to get the normal from
// use the one that was last selected.
for( int32 o = 0 ; o < GeomObjects.Num() ; ++o )
{
FGeomObject* go = GeomObjects[o];
go->CompileSelectionOrder();
if( go->SelectionOrder.Num() )
{
InMatrix = FRotationMatrix( go->SelectionOrder[ go->SelectionOrder.Num()-1 ]->GetWidgetRotation() );
return 1;
}
}
}
return 0;
}
bool FEdModeGeometry::GetCustomInputCoordinateSystem( FMatrix& InMatrix, void* InData )
{
return GetCustomDrawingCoordinateSystem( InMatrix, InData );
}
bool FEdModeGeometry::UsesToolkits() const
{
return true;
}
void FEdModeGeometry::Enter()
{
FEdMode::Enter();
if (!Toolkit.IsValid())
{
// @todo: Remove this assumption when we make modes per level editor instead of global
auto ToolkitHost = FModuleManager::LoadModuleChecked< FLevelEditorModule >( "LevelEditor" ).GetFirstLevelEditor();
Toolkit = MakeShareable(new FGeometryMode);
Toolkit->Init(ToolkitHost);
}
GetFromSource();
}
void FEdModeGeometry::Exit()
{
FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef());
Toolkit.Reset();
FEdMode::Exit();
for( int32 i=0; i<GeomObjects.Num(); i++ )
{
FGeomObject* GeomObject = GeomObjects[i];
delete GeomObject;
}
GeomObjects.Empty();
}
void FEdModeGeometry::ActorSelectionChangeNotify()
{
GetFromSource();
}
void FEdModeGeometry::MapChangeNotify()
{
// If the map changes in some major way, just refresh all the geometry data.
GetFromSource();
}
void FEdModeGeometry::AddReferencedObjects( FReferenceCollector& Collector )
{
// Call parent implementation
FEdMode::AddReferencedObjects( Collector );
FModeTool_GeometryModify* mtgm = (FModeTool_GeometryModify*)FindTool( MT_GeometryModify );
for( FModeTool_GeometryModify::TModifierIterator Itor( mtgm->ModifierIterator() ) ; Itor ; ++Itor )
{
Collector.AddReferencedObject( *Itor );
}
}
/**
* Returns the number of objects that are selected.
*/
int32 FEdModeGeometry::CountObjectsSelected()
{
return GeomObjects.Num();
}
/**
* Returns the number of polygons that are selected.
*/
int32 FEdModeGeometry::CountSelectedPolygons()
{
int32 Count = 0;
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 P = 0 ; P < GeomObject->PolyPool.Num() ; ++P )
{
if( GeomObject->PolyPool[P].IsSelected() )
{
Count++;
}
}
}
return Count;
}
/**
* Returns the polygons that are selected.
*
* @param InPolygons An array to fill with the selected polygons.
*/
void FEdModeGeometry::GetSelectedPolygons( TArray<FGeomPoly*>& InPolygons )
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 P = 0 ; P < GeomObject->PolyPool.Num() ; ++P )
{
if( GeomObject->PolyPool[P].IsSelected() )
{
InPolygons.Add( &GeomObject->PolyPool[P] );
}
}
}
}
/**
* Returns true if the user has polygons selected.
*/
bool FEdModeGeometry::HavePolygonsSelected()
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 P = 0 ; P < GeomObject->PolyPool.Num() ; ++P )
{
if( GeomObject->PolyPool[P].IsSelected() )
{
return true;
}
}
}
return false;
}
/**
* Returns the number of edges that are selected.
*/
int32 FEdModeGeometry::CountSelectedEdges()
{
int32 Count = 0;
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 E = 0 ; E < GeomObject->EdgePool.Num() ; ++E )
{
if( GeomObject->EdgePool[E].IsSelected() )
{
Count++;
}
}
}
return Count;
}
/**
* Returns true if the user has edges selected.
*/
bool FEdModeGeometry::HaveEdgesSelected()
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 E = 0 ; E < GeomObject->EdgePool.Num() ; ++E )
{
if( GeomObject->EdgePool[E].IsSelected() )
{
return true;
}
}
}
return false;
}
/**
* Returns the edges that are selected.
*
* @param InEdges An array to fill with the selected edges.
*/
void FEdModeGeometry::GetSelectedEdges( TArray<FGeomEdge*>& InEdges )
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 E = 0 ; E < GeomObject->EdgePool.Num() ; ++E )
{
if( GeomObject->EdgePool[E].IsSelected() )
{
InEdges.Add( &GeomObject->EdgePool[E] );
}
}
}
}
/**
* Returns the number of vertices that are selected.
*/
int32 FEdModeGeometry::CountSelectedVertices()
{
int32 Count = 0;
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 V = 0 ; V < GeomObject->VertexPool.Num() ; ++V )
{
if( GeomObject->VertexPool[V].IsSelected() )
{
Count++;
}
}
}
return Count;
}
/**
* Returns true if the user has vertices selected.
*/
bool FEdModeGeometry::HaveVerticesSelected()
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 V = 0 ; V < GeomObject->VertexPool.Num() ; ++V )
{
if( GeomObject->VertexPool[V].IsSelected() )
{
return true;
}
}
}
return false;
}
/**
* Fills an array with all selected vertices.
*
* @param InVerts An array to fill with the unique list of selected vertices.
*/
void FEdModeGeometry::GetSelectedVertices( TArray<FGeomVertex*>& InVerts )
{
InVerts.Empty();
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
for( int32 V = 0 ; V < GeomObject->VertexPool.Num() ; ++V )
{
if( GeomObject->VertexPool[V].IsSelected() )
{
InVerts.Add( &GeomObject->VertexPool[V] );
}
}
}
}
/**
* Utility function that allow you to poll and see if certain sub elements are currently selected.
*
* Returns a combination of the flags in ESelectionStatus.
*/
int32 FEdModeGeometry::GetSelectionState()
{
int32 Status = 0;
if( HavePolygonsSelected() )
{
Status |= GSS_Polygon;
}
if( HaveEdgesSelected() )
{
Status |= GSS_Edge;
}
if( HaveVerticesSelected() )
{
Status |= GSS_Vertex;
}
return Status;
}
FVector FEdModeGeometry::GetWidgetLocation() const
{
return FEdMode::GetWidgetLocation();
}
bool FEdModeGeometry::IsCompatibleWith(FEditorModeID OtherModeID) const
{
return OtherModeID == FBuiltinEditorModes::EM_Bsp;
}
// ------------------------------------------------------------------------------
/**
* Deselects all edges, polygons, and vertices for all selected objects.
*/
void FEdModeGeometry::GeometrySelectNone(bool bStoreSelection, bool bResetPivot)
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
FGeomObject* GeomObject = GeomObjects[ObjectIdx];
GeomObject->Select( 0 );
for( int VertexIdx = 0 ; VertexIdx < GeomObject->EdgePool.Num() ; ++VertexIdx )
{
GeomObject->EdgePool[VertexIdx].Select( 0 );
}
for( int VertexIdx = 0 ; VertexIdx < GeomObject->PolyPool.Num() ; ++VertexIdx )
{
GeomObject->PolyPool[VertexIdx].Select( 0 );
}
for( int VertexIdx = 0 ; VertexIdx < GeomObject->VertexPool.Num() ; ++VertexIdx )
{
GeomObject->VertexPool[VertexIdx].Select( 0 );
}
GeomObject->SelectionOrder.Empty();
}
if (bStoreSelection)
{
FModeTool_GeometryModify* GeometryModifier = (FModeTool_GeometryModify*)FindTool(MT_GeometryModify);
GeometryModifier->StoreAllCurrentGeomSelections();
}
if (bResetPivot && (GeomObjects.Num() > 0))
{
Owner->SetPivotLocation(GeomObjects[0]->GetActualBrush()->GetActorLocation(), false);
}
}
void FEdModeGeometry::SelectionChanged( )
{
StaticCastSharedPtr<FGeometryMode>(Toolkit)->SelectionChanged();
}
// ------------------------------------------------------------------------------
void FEdModeGeometry::RenderPoly( const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI )
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
const FGeomObject* GeomObject = GeomObjects[ObjectIdx];
FLinearColor UnselectedColor = GeomObject->GetActualBrush()->GetWireColor();
UnselectedColor.A = .1f;
FLinearColor SelectedColor = GetDefault<UEditorStyleSettings>()->SelectionColor;
SelectedColor.A = .5f;
// Allocate the material proxy and register it so it can be deleted properly once the rendering is done with it.
FDynamicColoredMaterialRenderProxy* SelectedColorInstance = new FDynamicColoredMaterialRenderProxy(GEngine->GeomMaterial->GetRenderProxy(false),SelectedColor );
PDI->RegisterDynamicResource( SelectedColorInstance );
FDynamicColoredMaterialRenderProxy* UnselectedColorInstance = new FDynamicColoredMaterialRenderProxy(GEngine->GeomMaterial->GetRenderProxy(false),UnselectedColor);
PDI->RegisterDynamicResource( UnselectedColorInstance );
// Render selected filled polygons.
for( int32 PolyIdx = 0 ; PolyIdx < GeomObject->PolyPool.Num() ; ++PolyIdx )
{
const FGeomPoly* GeomPoly = &GeomObject->PolyPool[PolyIdx];
PDI->SetHitProxy( new HGeomPolyProxy(const_cast<FGeomObject*>(GeomPoly->GetParentObject()),PolyIdx) );
{
FDynamicMeshBuilder MeshBuilder;
TArray<FVector> Verts;
// Look at the edge list and create a list of vertices to render from.
FVector LastPos(0);
for( int32 EdgeIdx = 0 ; EdgeIdx < GeomPoly->EdgeIndices.Num() ; ++EdgeIdx )
{
const FGeomEdge* GeomEdge = &GeomPoly->GetParentObject()->EdgePool[ GeomPoly->EdgeIndices[EdgeIdx] ];
if( EdgeIdx == 0 )
{
Verts.Add( GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[0] ] );
LastPos = GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[0] ];
}
else if( GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[0] ].Equals( LastPos ) )
{
Verts.Add( GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[1] ] );
LastPos = GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[1] ];
}
else
{
Verts.Add( GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[0] ] );
LastPos = GeomPoly->GetParentObject()->VertexPool[ GeomEdge->VertexIndices[0] ];
}
}
// Draw Polygon Triangles
const int32 VertexOffset = MeshBuilder.AddVertex(Verts[0], FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor(255,255,255));
MeshBuilder.AddVertex(Verts[1], FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor(255,255,255));
for( int32 VertIdx = 2 ; VertIdx < Verts.Num() ; ++VertIdx )
{
MeshBuilder.AddVertex(Verts[VertIdx], FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor(255,255,255));
MeshBuilder.AddTriangle( VertexOffset + VertIdx - 1, VertexOffset, VertexOffset + VertIdx);
}
if( GeomPoly->IsSelected() )
{
MeshBuilder.Draw(PDI, GeomObject->GetActualBrush()->ActorToWorld().ToMatrixWithScale(), SelectedColorInstance, SDPG_World, false );
}
else
{
// We only draw unselected polygons in the perspective viewport
if( !Viewport->GetClient()->IsOrtho() )
{
// Unselected polygons are drawn at the world level but are bumped slightly forward to avoid z-fighting
MeshBuilder.Draw(PDI, GeomObject->GetActualBrush()->ActorToWorld().ToMatrixWithScale(), UnselectedColorInstance, SDPG_World, false );
}
}
}
PDI->SetHitProxy( NULL );
}
}
}
// ------------------------------------------------------------------------------
void FEdModeGeometry::RenderEdge( const FSceneView* View, FPrimitiveDrawInterface* PDI )
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
const FGeomObject* GeometryObject = GeomObjects[ObjectIdx];
const FColor WireColor = GeometryObject->GetActualBrush()->GetWireColor();
// Edges
for( int32 EdgeIdx = 0 ; EdgeIdx < GeometryObject->EdgePool.Num() ; ++EdgeIdx )
{
const FGeomEdge* GeometryEdge = &GeometryObject->EdgePool[EdgeIdx];
const FColor Color = GeometryEdge->IsSelected() ? FColor(255,128,64) : WireColor;
PDI->SetHitProxy( new HGeomEdgeProxy(const_cast<FGeomObject*>(GeometryObject),EdgeIdx) );
{
FVector V0 = GeometryObject->VertexPool[ GeometryEdge->VertexIndices[0] ];
FVector V1 = GeometryObject->VertexPool[ GeometryEdge->VertexIndices[1] ];
const FTransform ActorToWorld = GeometryObject->GetActualBrush()->ActorToWorld();
V0 = ActorToWorld.TransformPosition( V0 );
V1 = ActorToWorld.TransformPosition( V1 );
PDI->DrawLine( V0, V1, Color, SDPG_Foreground );
}
PDI->SetHitProxy( NULL );
}
}
}
// ------------------------------------------------------------------------------
void FEdModeGeometry::RenderVertex( const FSceneView* View, FPrimitiveDrawInterface* PDI )
{
for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx )
{
const FGeomObject* GeomObject = GeomObjects[ObjectIdx];
check(GeomObject);
// Vertices
FColor Color;
float Scale;
FVector Location;
for( int32 VertIdx = 0 ; VertIdx < GeomObject->VertexPool.Num() ; ++VertIdx )
{
const FGeomVertex* GeomVertex = &GeomObject->VertexPool[VertIdx];
check(GeomVertex);
check(GeomObject->GetActualBrush());
Location = GeomObject->GetActualBrush()->ActorToWorld().TransformPosition( *GeomVertex );
Scale = View->WorldToScreen( Location ).W * ( 4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0] );
Color = GeomVertex->IsSelected() ? FColor(255,128,64) : GeomObject->GetActualBrush()->GetWireColor();
PDI->SetHitProxy( new HGeomVertexProxy( const_cast<FGeomObject*>(GeomObject), VertIdx) );
PDI->DrawSprite( Location, 4.f * Scale, 4.f * Scale, GEngine->DefaultBSPVertexTexture->Resource, Color, SDPG_Foreground, 0.0, 0.0, 0.0, 0.0 );
PDI->SetHitProxy( NULL );
}
}
}
/**
* Cache all the selected geometry on the object, and add to the array if any is found
*
* Return true if new object has been added to the array.
*/
bool FEdModeGeometry::CacheSelectedData( TArray<HGeomMidPoints>& raGeomData, const FGeomObject& rGeomObject ) const
{
// Early out if this object doesn't have a brush
if ( !rGeomObject.ActualBrush )
{
return false;
}
HGeomMidPoints GeomData;
// Loop through all the verts caching their midpoint if they're selected
for ( int32 i=0; i<rGeomObject.VertexPool.Num(); i++ )
{
const FGeomVertex& rGeomVertex = rGeomObject.VertexPool[i];
if(rGeomVertex.IsSelected())
{
GeomData.VertexPool.Add( rGeomVertex.GetMidPoint() );
}
}
// Loop through all the edges caching their midpoint if they're selected
for ( int32 i=0; i<rGeomObject.EdgePool.Num(); i++ )
{
const FGeomEdge& rGeomEdge = rGeomObject.EdgePool[i];
if(rGeomEdge.IsSelected())
{
GeomData.EdgePool.Add( rGeomEdge.GetMidPoint() );
}
}
// Loop through all the polys caching their midpoint if they're selected
for ( int32 i=0; i<rGeomObject.PolyPool.Num(); i++ )
{
const FGeomPoly& rGeomPoly = rGeomObject.PolyPool[i];
if(rGeomPoly.IsSelected())
{
GeomData.PolyPool.Add( rGeomPoly.GetMidPoint() );
}
}
// Only add the data to the array if there was anything that was selected
bool bRet = ( ( GeomData.VertexPool.Num() + GeomData.EdgePool.Num() + GeomData.PolyPool.Num() ) > 0 ? true : false );
if ( bRet )
{
// Make note of the brush this belongs to, then add
GeomData.ActualBrush = rGeomObject.ActualBrush;
raGeomData.Add( GeomData );
}
return bRet;
}
/**
* Attempt to find all the new geometry using the cached data, and cache those new ones out
*
* Return true everything was found (or there was nothing to find)
*/
bool FEdModeGeometry::FindFromCache( TArray<HGeomMidPoints>& raGeomData, FGeomObject& rGeomObject, TArray<FGeomBase*>& raSelectedGeom ) const
{
// Early out if this object doesn't have a brush
if ( !rGeomObject.ActualBrush )
{
return true;
}
// Early out if we don't have anything cached
if ( raGeomData.Num() == 0 )
{
return true;
}
// Loop through all the cached data, seeing if there's a match for the brush
// Note: if GetMidPoint wasn't pure virtual this could be much nicer
bool bRet = false; // True if the brush that was parsed was found and all verts/edges/polys were located
bool bFound = false; // True if the brush that was parsed was found
for( int32 i=0; i<raGeomData.Num(); i++ )
{
// Does this brush match the cached actor?
HGeomMidPoints& rGeomData = raGeomData[i];
if ( rGeomData.ActualBrush == rGeomObject.ActualBrush )
{
// Compare location of new midpoints with cached versions
bFound = true;
bool bSucess = true; // True if all verts/edges/polys were located
for ( int32 j=0; j<rGeomData.VertexPool.Num(); j++ )
{
const FVector& rGeomVector = rGeomData.VertexPool[j];
for ( int32 k=0; k<rGeomObject.VertexPool.Num(); k++ )
{
// If we have a match select it and move on to the next one
FGeomVertex& rGeomVertex = rGeomObject.VertexPool[k];
if ( rGeomVector.Equals( rGeomVertex.GetMidPoint() ) )
{
// Add the new geometry to the to-be-selected pool, and remove from the data pool
raSelectedGeom.Add(&rGeomVertex);
rGeomData.VertexPool.RemoveAt(j--);
break;
}
}
}
// If we didn't locate them all inform the user
if ( rGeomData.VertexPool.Num() != 0 )
{
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to find %d Vertex(s) in new BSP" ), rGeomData.VertexPool.Num() );
bSucess = false;
}
// Compare location of new midpoints with cached versions
for ( int32 j=0; j<rGeomData.EdgePool.Num(); j++ )
{
const FVector& rGeomVector = rGeomData.EdgePool[j];
for ( int32 k=0; k<rGeomObject.EdgePool.Num(); k++ )
{
// If we have a match select it and move on to the next one
FGeomEdge& rGeomEdge = rGeomObject.EdgePool[k];
if ( rGeomVector.Equals( rGeomEdge.GetMidPoint() ) )
{
// Add the new geometry to the to-be-selected pool, and remove from the data pool
raSelectedGeom.Add(&rGeomEdge);
rGeomData.EdgePool.RemoveAt(j--);
break;
}
}
}
// If we didn't locate them all inform the user
if ( rGeomData.EdgePool.Num() != 0 )
{
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to find %d Edge(s) in new BSP" ), rGeomData.EdgePool.Num() );
bSucess = false;
}
// Compare location of new midpoints with cached versions
for ( int32 j=0; j<rGeomData.PolyPool.Num(); j++ )
{
const FVector& rGeomVector = rGeomData.PolyPool[j];
for ( int32 k=0; k<rGeomObject.PolyPool.Num(); k++ )
{
// If we have a match select it and move on to the next one
FGeomPoly& rGeomPoly = rGeomObject.PolyPool[k];
if ( rGeomVector.Equals( rGeomPoly.GetMidPoint() ) )
{
// Add the new geometry to the to-be-selected pool, and remove from the data pool
raSelectedGeom.Add(&rGeomPoly);
rGeomData.PolyPool.RemoveAt(j--);
break;
}
}
}
// If we didn't locate them all inform the user
if ( rGeomData.PolyPool.Num() != 0 )
{
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to find %d Poly(s) in new BSP" ), rGeomData.PolyPool.Num() );
bSucess = false;
}
// If we didn't locate them all inform the user, then remove from the data pool
if ( !bSucess )
{
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to resolve %s Brush in new BSP, see above" ), *rGeomData.ActualBrush->GetName() );
}
bRet = bSucess;
raGeomData.RemoveAt(i--);
break;
}
}
// If we didn't locate the brush inform the user
if ( !bFound )
{
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to find %s Brush(s) in new BSP" ), *rGeomObject.ActualBrush->GetName() );
}
return bRet;
}
/**
* Select all the verts/edges/polys that were found
*
* Return true if successful
*/
bool FEdModeGeometry::SelectCachedData( TArray<FGeomBase*>& raSelectedGeom ) const
{
// Early out if we don't have anything cached
if ( raSelectedGeom.Num() == 0 )
{
return false;
}
check(Owner->IsModeActive(FBuiltinEditorModes::EM_Geometry));
// Backup widget position, we want it to be in the same position as it was previously too
FVector PivLoc = Owner->PivotLocation;
FVector SnapLoc = Owner->SnappedLocation;
// Loop through all the geometry that should be selected
for( int32 i=0; i<raSelectedGeom.Num(); i++ )
{
// Does this brush match the cached actor?
FGeomBase* pGeom = raSelectedGeom[i];
if ( pGeom )
{
pGeom->Select();
}
}
// Restore the widget position
Owner->SetPivotLocation( PivLoc, false );
Owner->SnappedLocation = SnapLoc;
StaticCastSharedPtr<FGeometryMode>(Toolkit)->SelectionChanged();
return true;
}
#define BSP_RESELECT // Attempt to reselect any geometry that was selected prior to the BSP being rebuilt
#define BSP_RESELECT__ALL_OR_NOTHING // If any geometry can't be located, then don't select anything
/**
* Compiles geometry mode information from the selected brushes.
*/
void FEdModeGeometry::GetFromSource()
{
GWarn->BeginSlowTask( NSLOCTEXT("EditorModes", "GeometryMode-BeginRebuildingBSPTask", "Rebuilding BSP"), false);
TArray<HGeomMidPoints> GeomData;
// Go through each brush and update its components before updating below
for( int32 i=0; i<GeomObjects.Num(); i++ )
{
FGeomObject* GeomObject = GeomObjects[i];
if(GeomObject && GeomObject->ActualBrush)
{
#ifdef BSP_RESELECT
// Cache any information that'll help us reselect the object after it's reconstructed
CacheSelectedData( GeomData, *GeomObject );
#endif // BSP_RESELECT
GeomObject->ActualBrush->UnregisterAllComponents();
if (GeomObject->ActualBrush->GetWorld())
{
GeomObject->ActualBrush->RegisterAllComponents();
}
delete GeomObject;
}
}
GeomObjects.Empty();
TArray<FGeomBase*> SelectedGeom;
// Notify the selected actors that they have been moved.
bool bFound = true;
for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It )
{
AActor* Actor = static_cast<AActor*>( *It );
checkSlow( Actor->IsA(AActor::StaticClass()) );
ABrush* BrushActor = Cast< ABrush >( Actor );
if ( BrushActor )
{
if( BrushActor->Brush != NULL )
{
FGeomObject* GeomObject = new FGeomObject();
GeomObject->SetParentObjectIndex( GeomObjects.Add( GeomObject ) );
GeomObject->ActualBrush = BrushActor;
GeomObject->GetFromSource();
#ifdef BSP_RESELECT
// Attempt to find all the previously selected geometry on this object if everything has gone OK so far
if ( bFound && !FindFromCache( GeomData, *GeomObject, SelectedGeom ) )
{
#ifdef BSP_RESELECT__ALL_OR_NOTHING
// If it didn't succeed, don't attempt to reselect anything as the user will only end up moving part of their previous selection
UE_LOG(LogGeometryMode, Warning, TEXT( "Unable to find all previously selected geometry data, resetting selection" ) );
SelectedGeom.Empty();
GeomData.Empty();
bFound = false;
#endif // BSP_RESELECT__ALL_OR_NOTHING
}
#endif // BSP_RESELECT
}
}
}
#ifdef BSP_RESELECT
// Reselect anything that came close to the cached midpoints
SelectCachedData( SelectedGeom );
#endif // BSP_RESELECT
GWarn->EndSlowTask();
}
/**
* Changes the source brushes to match the current geometry data.
*/
void FEdModeGeometry::SendToSource()
{
for( int32 o = 0 ; o < GeomObjects.Num() ; ++o )
{
FGeomObject* go = GeomObjects[o];
go->SendToSource();
}
}
bool FEdModeGeometry::FinalizeSourceData()
{
bool Result = 0;
for( int32 o = 0 ; o < GeomObjects.Num() ; ++o )
{
FGeomObject* go = GeomObjects[o];
if( go->FinalizeSourceData() )
{
Result = 1;
}
}
return Result;
}
void FEdModeGeometry::PostUndo()
{
// Rebuild the geometry data from the current brush state
GetFromSource();
// Restore selection information.
for( int32 o = 0 ; o < GeomObjects.Num() ; ++o )
{
int32 Idx = 0;
FGeomObject* go = GeomObjects[o];
ABrush* Actor = go->GetActualBrush();
// First, clear the current selection
go->SelectNone();
// Next, restore the cached selection
int32 res = go->SetPivotFromSelectionArray( Actor->SavedSelections );
//use the centre of the actor if we didnt find a suitable selection
if( res == INDEX_NONE )
{
Owner->SetPivotLocation( Actor->GetActorLocation() , false );
}
go->ForceLastSelectionIndex( res );
}
}
bool FEdModeGeometry::ExecDelete()
{
check( Owner->IsModeActive( FBuiltinEditorModes::EM_Geometry ) );
// Find the delete modifier and execute it.
FModeTool_GeometryModify* mtgm = (FModeTool_GeometryModify*)FindTool( MT_GeometryModify );
for( FModeTool_GeometryModify::TModifierIterator Itor( mtgm->ModifierIterator() ) ; Itor ; ++Itor )
{
UGeomModifier* gm = *Itor;
if( gm->IsA( UGeomModifier_Delete::StaticClass()) )
{
if( gm->Apply() )
{
return 1;
}
}
}
return 0;
}
void FEdModeGeometry::UpdateInternalData()
{
GetFromSource();
}
/*-----------------------------------------------------------------------------
FModeTool_GeometryModify.
-----------------------------------------------------------------------------*/
void FModeTool_GeometryModify::SetCurrentModifier( UGeomModifier* InModifier )
{
if( CurrentModifier )
{
CurrentModifier->WasDeactivated();
}
CurrentModifier = InModifier;
CurrentModifier->WasActivated();
}
UGeomModifier* FModeTool_GeometryModify::GetCurrentModifier() { return CurrentModifier; }
int32 FModeTool_GeometryModify::GetNumModifiers() { return Modifiers.Num(); }
FModeTool_GeometryModify::FModeTool_GeometryModify()
{
ID = MT_GeometryModify;
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Edit::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Extrude::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Clip::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Pen::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Lathe::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Create::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Delete::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Flip::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Split::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Triangulate::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Optimize::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Turn::StaticClass() ) );
Modifiers.Add( ConstructObject<UGeomModifier>( UGeomModifier_Weld::StaticClass() ) );
CurrentModifier = NULL;
bGeomModified = false;
}
void FModeTool_GeometryModify::SelectNone()
{
FEdModeGeometry* Mode = GLevelEditorModeTools().GetActiveModeTyped<FEdModeGeometry>(FBuiltinEditorModes::EM_Geometry);
check(Mode);
Mode->GeometrySelectNone(false, false);
}
/** @return true if something was selected/deselected, false otherwise. */
bool FModeTool_GeometryModify::BoxSelect( FBox& InBox, bool InSelect )
{
bool bResult = false;
if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_Geometry ) )
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_Geometry );
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObject* go = *Itor;
FTransform ActorToWorld = go->GetActualBrush()->ActorToWorld();
// Only verts for box selection
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex& gv = go->VertexPool[v];
if( FMath::PointBoxIntersection( ActorToWorld.TransformPosition( gv.GetMid() ), InBox ) )
{
gv.Select( InSelect );
bResult = true;
}
}
}
}
return bResult;
}
/** @return true if something was selected/deselected, false otherwise. */
bool FModeTool_GeometryModify::FrustumSelect( const FConvexVolume& InFrustum, bool InSelect /* = true */ )
{
bool bResult = false;
if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_Geometry ) )
{
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_Geometry );
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObject* go = *Itor;
FTransform ActorToWorld = go->GetActualBrush()->ActorToWorld();
// Check each vertex to see if its inside the frustum
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex& gv = go->VertexPool[v];
if( InFrustum.IntersectBox( ActorToWorld.TransformPosition( gv.GetMid() ), FVector::ZeroVector ) )
{
gv.Select( InSelect );
bResult = true;
}
}
}
}
return bResult;
}
void FModeTool_GeometryModify::Tick(FEditorViewportClient* ViewportClient,float DeltaTime)
{
if( CurrentModifier )
{
CurrentModifier->Tick( ViewportClient, DeltaTime );
}
}
/**
* @return true if the delta was handled by this editor mode tool.
*/
bool FModeTool_GeometryModify::InputDelta(FEditorViewportClient* InViewportClient,FViewport* InViewport,FVector& InDrag,FRotator& InRot,FVector& InScale)
{
bool bResult = false;
if( InViewportClient->GetCurrentWidgetAxis() != EAxisList::None )
{
// Geometry mode passes the input on to the current modifier.
if( CurrentModifier )
{
bResult = CurrentModifier->InputDelta( InViewportClient, InViewport, InDrag, InRot, InScale );
}
}
return bResult;
}
bool FModeTool_GeometryModify::StartModify()
{
// Let the modifier do any set up work that it needs to.
if( CurrentModifier != NULL )
{
//Store the current state of the brush so that we can return to upon faulty operation
CurrentModifier->CacheBrushState();
return CurrentModifier->StartModify();
}
// Clear modified flag, so we can track if something actually changes before EndModify
bGeomModified = false;
// No modifier to start
return false;
}
bool FModeTool_GeometryModify::EndModify()
{
// Let the modifier finish up.
if( CurrentModifier != NULL )
{
FEdModeGeometry* mode = ((FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry));
// Update the source data to match the current geometry data.
mode->SendToSource();
// Make sure the source data has remained viable.
if( mode->FinalizeSourceData() )
{
// If the source data was modified, reconstruct the geometry data to reflect that.
mode->GetFromSource();
}
CurrentModifier->EndModify();
// Update internals.
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObject* go = *Itor;
go->ComputeData();
FBSPOps::bspUnlinkPolys( go->GetActualBrush()->Brush );
// If geometry was actually changed, call PostEditBrush
if(bGeomModified)
{
ABrush* Brush = go->GetActualBrush();
if(Brush)
{
if(!Brush->IsStaticBrush())
{
FBSPOps::csgPrepMovingBrush(Brush);
}
}
bGeomModified = false;
}
}
}
return 1;
}
void FModeTool_GeometryModify::StartTrans()
{
if( CurrentModifier != NULL )
{
CurrentModifier->StartTrans();
}
}
void FModeTool_GeometryModify::EndTrans()
{
if( CurrentModifier != NULL )
{
CurrentModifier->EndTrans();
}
}
/**
* @return true if the key was handled by this editor mode tool.
*/
bool FModeTool_GeometryModify::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
check( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_Geometry ) );
// Give the current modifier a chance to handle this first
if( CurrentModifier && CurrentModifier->InputKey( ViewportClient, Viewport, Key, Event ) )
{
return true;
}
if (Key == EKeys::Escape)
{
// Hitting ESC will deselect any subobjects first. If no subobjects are selected, then it will
// deselect the brushes themselves.
FEdModeGeometry* mode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_Geometry );
bool bHadSubObjectSelections = (mode->GetSelectionState() > 0) ? true : false;
for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor )
{
FGeomObject* go = *Itor;
for( int32 p = 0 ; p < go->PolyPool.Num() ; ++p )
{
FGeomPoly& gp = go->PolyPool[p];
if( gp.IsSelected() )
{
gp.Select( false );
bHadSubObjectSelections = true;
}
}
for( int32 e = 0 ; e < go->EdgePool.Num() ; ++e )
{
FGeomEdge& ge = go->EdgePool[e];
if( ge.IsSelected() )
{
ge.Select( false );
bHadSubObjectSelections = true;
}
}
for( int32 v = 0 ; v < go->VertexPool.Num() ; ++v )
{
FGeomVertex& gv = go->VertexPool[v];
if( gv.IsSelected() )
{
gv.Select( false );
bHadSubObjectSelections = true;
}
}
}
if( bHadSubObjectSelections )
{
GEditor->RedrawAllViewports();
return true;
}
}
else
{
return FModeTool::InputKey( ViewportClient, Viewport, Key, Event );
}
return false;
}
void FModeTool_GeometryModify::DrawHUD(FEditorViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View,FCanvas* Canvas)
{
// Give the current modifier a chance to draw a HUD
if( CurrentModifier )
{
CurrentModifier->DrawHUD( ViewportClient, Viewport, View, Canvas );
}
}
void FModeTool_GeometryModify::Render(const FSceneView* View,FViewport* Viewport,FPrimitiveDrawInterface* PDI)
{
// Give the current modifier a chance to render
if( CurrentModifier )
{
CurrentModifier->Render( View, Viewport, PDI );
}
}
void FModeTool_GeometryModify::StoreAllCurrentGeomSelections()
{
if (CurrentModifier)
{
CurrentModifier->StoreAllCurrentGeomSelections();
}
}