// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "GeometryEdMode.h" #include "EditorViewportClient.h" #include "Misc/FeedbackContext.h" #include "Modules/ModuleManager.h" #include "EditorStyleSet.h" #include "Classes/EditorStyleSettings.h" #include "Materials/Material.h" #include "Engine/Selection.h" #include "EditorModeManager.h" #include "EditorModes.h" #include "Toolkits/ToolkitManager.h" #include "BSPOps.h" #include "GeometryMode.h" #include "EditorGeometry.h" #include "DynamicMeshBuilder.h" #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" IMPLEMENT_MODULE( FGeometryModeModule, GeometryMode ); DEFINE_LOG_CATEGORY_STATIC(LogGeometryMode, Log, All); void FGeometryModeModule::StartupModule() { FEditorModeRegistry::Get().RegisterMode( FBuiltinEditorModes::EM_Geometry, NSLOCTEXT("EditorModes", "GeometryMode", "Geometry Editing"), FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.BspMode", "LevelEditor.BspMode.Small"), true, 500 ); } void FGeometryModeModule::ShutdownModule() { FEditorModeRegistry::Get().UnregisterMode(FBuiltinEditorModes::EM_Geometry); } /*------------------------------------------------------------------------------ Geometry Editing. ------------------------------------------------------------------------------*/ FEdModeGeometry::FEdModeGeometry() { Tools.Add( new FModeTool_GeometryModify() ); SetCurrentTool( MT_GeometryModify ); } FEdModeGeometry::~FEdModeGeometry() { 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 ) { FGeomBase* GeomBase = static_cast(InData); FGeomObjectPtr GeomObject = GeomBase->GetParentObject(); check(GeomObject.IsValid()); ABrush* Brush = GeomObject->GetActualBrush(); InMatrix = FRotationMatrix(GeomBase->GetNormal().Rotation()) * FQuatRotationMatrix(Brush->GetActorQuat()); } 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 ) { FGeomObjectPtr go = GeomObjects[o]; go->CompileSelectionOrder(); if( go->SelectionOrder.Num() ) { FGeomBase* GeomBase = go->SelectionOrder[go->SelectionOrder.Num() - 1]; check(GeomBase != nullptr); FGeomObjectPtr GeomObject = GeomBase->GetParentObject(); check(GeomObject.IsValid()); ABrush* Brush = GeomObject->GetActualBrush(); InMatrix = FRotationMatrix( go->SelectionOrder[ go->SelectionOrder.Num()-1 ]->GetWidgetRotation() ) * FQuatRotationMatrix(Brush->GetActorQuat()); 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()) { Toolkit = MakeShareable(new FGeometryMode); Toolkit->Init(Owner->GetToolkitHost()); } GetFromSource(); } void FEdModeGeometry::Exit() { FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef()); Toolkit.Reset(); FEdMode::Exit(); 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 ) { FGeomObjectPtr 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& InPolygons ) { for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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& InEdges ) { for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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& InVerts ) { InVerts.Empty(); for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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(Toolkit)->SelectionChanged(); } // ------------------------------------------------------------------------------ void FEdModeGeometry::RenderPoly( const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI ) { for( int32 ObjectIdx = 0 ; ObjectIdx < GeomObjects.Num() ; ++ObjectIdx ) { FGeomObjectPtr GeomObject = GeomObjects[ObjectIdx]; FLinearColor UnselectedColor = GeomObject->GetActualBrush()->GetWireColor(); UnselectedColor.A = .1f; FLinearColor SelectedColor = GetDefault()->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(),SelectedColor ); PDI->RegisterDynamicResource( SelectedColorInstance ); FDynamicColoredMaterialRenderProxy* UnselectedColorInstance = new FDynamicColoredMaterialRenderProxy(GEngine->GeomMaterial->GetRenderProxy(),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(GeomPoly->GetParentObject(),PolyIdx) ); { FDynamicMeshBuilder MeshBuilder(View->GetFeatureLevel()); TArray 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::White); MeshBuilder.AddVertex(Verts[1], FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor::White); 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::White); 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 ) { FGeomObjectPtr 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(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 ) { FGeomObjectPtr GeomObject = GeomObjects[ObjectIdx]; check(GeomObject.IsValid()); // 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->UnscaledViewRect.Width() / View->ViewMatrices.GetProjectionMatrix().M[0][0] ); Color = GeomVertex->IsSelected() ? FColor(255,128,64) : GeomObject->GetActualBrush()->GetWireColor(); PDI->SetHitProxy( new HGeomVertexProxy( 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& 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 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& raGeomData, FGeomObject& rGeomObject, TArray& 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; iGetName() ); } 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& 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; iSelect(); } } // Restore the widget position Owner->SetPivotLocation( PivLoc, false ); Owner->SnappedLocation = SnapLoc; StaticCastSharedPtr(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 GeomData; // Go through each brush and update its components before updating below for( int32 i=0; iActualBrush) { #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(); } } } GeomObjects.Empty(); TArray SelectedGeom; // Notify the selected actors that they have been moved. bool bFound = true; for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = static_cast( *It ); checkSlow( Actor->IsA(AActor::StaticClass()) ); ABrush* BrushActor = Cast< ABrush >( Actor ); if ( BrushActor ) { if( BrushActor->Brush != NULL ) { FGeomObjectPtr GeomObject = MakeShareable( 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 ) { FGeomObjectPtr go = GeomObjects[o]; go->SendToSource(); } } bool FEdModeGeometry::FinalizeSourceData() { bool Result = 0; for( int32 o = 0 ; o < GeomObjects.Num() ; ++o ) { FGeomObjectPtr 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; FGeomObjectPtr go = GeomObjects[o]; ABrush* Actor = go->GetActualBrush(); // First, clear the current selection go->SelectNone(); // Next, restore the cached selection go->UpdateFromSelectionArray( Actor->SavedSelections ); 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( NewObject( GetTransientPackage(), UGeomModifier_Edit::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Extrude::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Clip::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Pen::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Lathe::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Create::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Delete::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Flip::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Split::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Triangulate::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Optimize::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Turn::StaticClass() ) ); Modifiers.Add( NewObject( GetTransientPackage(), UGeomModifier_Weld::StaticClass() ) ); CurrentModifier = NULL; bGeomModified = false; } void FModeTool_GeometryModify::SelectNone() { FEdModeGeometry* Mode = GLevelEditorModeTools().GetActiveModeTyped(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 ) { FGeomObjectPtr 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, FEditorViewportClient* InViewportClient, 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 ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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 ) { FGeomObjectPtr 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(); } }