2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "StaticMeshEditorModule.h"
# include "AssetRegistryModule.h"
# include "StaticMeshEditor.h"
# include "SStaticMeshEditorViewport.h"
# include "StaticMeshEditorViewportClient.h"
# include "StaticMeshEditorTools.h"
# include "StaticMeshEditorActions.h"
# include "UnrealEd.h"
Remove more headers from Engine.h (StaticMeshResources.h, AnimTree.h, SkeletalMeshTypes.h, SkeletalMeshActor.h, LightingBuildOptions.h, PixelFormat.h, WorldComposition.h, VisualLog.h, StaticLighting.h, Lightmap.h, ShadowMap.h, Model.h)
[CL 2086772 by James Golding in Main branch]
2014-05-29 17:21:47 -04:00
# include "StaticMeshResources.h"
2014-03-14 14:13:41 -04:00
# include "ISocketManager.h"
# include "PreviewScene.h"
# include "ScopedTransaction.h"
# include "BusyCursor.h"
# include "FbxMeshUtils.h"
# include "../Private/GeomFitUtils.h"
# include "EditorViewportCommands.h"
# include "Editor/UnrealEd/Private/ConvexDecompTool.h"
# include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
# include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructureModule.h"
2014-07-24 04:28:41 -04:00
# include "Runtime/Analytics/Analytics/Public/Interfaces/IAnalyticsProvider.h"
# include "EngineAnalytics.h"
2014-10-14 22:50:06 -04:00
# include "SDockTab.h"
# include "GenericCommands.h"
# include "STextComboBox.h"
# include "SNotificationList.h"
# include "NotificationManager.h"
2014-11-12 04:43:54 -05:00
# include "Engine/Selection.h"
2014-07-24 04:28:41 -04:00
2014-03-14 14:13:41 -04:00
# define LOCTEXT_NAMESPACE "StaticMeshEditor"
DEFINE_LOG_CATEGORY_STATIC ( LogStaticMeshEditor , Log , All ) ;
2015-02-21 13:20:31 -05:00
class FStaticMeshStatusMessageContext : public FScopedSlowTask
{
public :
explicit FStaticMeshStatusMessageContext ( const FText & InMessage )
: FScopedSlowTask ( 0 , InMessage )
{
UE_LOG ( LogStaticMesh , Log , TEXT ( " %s " ) , * InMessage . ToString ( ) ) ;
MakeDialog ( ) ;
}
} ;
2014-03-14 14:13:41 -04:00
const FName FStaticMeshEditor : : ViewportTabId ( TEXT ( " StaticMeshEditor_Viewport " ) ) ;
const FName FStaticMeshEditor : : PropertiesTabId ( TEXT ( " StaticMeshEditor_Properties " ) ) ;
const FName FStaticMeshEditor : : SocketManagerTabId ( TEXT ( " StaticMeshEditor_SocketManager " ) ) ;
const FName FStaticMeshEditor : : CollisionTabId ( TEXT ( " StaticMeshEditor_Collision " ) ) ;
void FStaticMeshEditor : : RegisterTabSpawners ( const TSharedRef < class FTabManager > & TabManager )
{
2014-10-23 15:11:28 -04:00
WorkspaceMenuCategory = TabManager - > AddLocalWorkspaceMenuCategory ( LOCTEXT ( " WorkspaceMenu_StaticMeshEditor " , " Static Mesh Editor " ) ) ;
2014-10-09 12:34:55 -04:00
auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory . ToSharedRef ( ) ;
2014-03-14 14:13:41 -04:00
2014-10-09 12:34:55 -04:00
FAssetEditorToolkit : : RegisterTabSpawners ( TabManager ) ;
2014-03-14 14:13:41 -04:00
TabManager - > RegisterTabSpawner ( ViewportTabId , FOnSpawnTab : : CreateSP ( this , & FStaticMeshEditor : : SpawnTab_Viewport ) )
. SetDisplayName ( LOCTEXT ( " ViewportTab " , " Viewport " ) )
2014-10-09 12:34:55 -04:00
. SetGroup ( WorkspaceMenuCategoryRef )
. SetIcon ( FSlateIcon ( FEditorStyle : : GetStyleSetName ( ) , " LevelEditor.Tabs.Viewports " ) ) ;
2014-03-14 14:13:41 -04:00
TabManager - > RegisterTabSpawner ( PropertiesTabId , FOnSpawnTab : : CreateSP ( this , & FStaticMeshEditor : : SpawnTab_Properties ) )
. SetDisplayName ( LOCTEXT ( " PropertiesTab " , " Details " ) )
2014-10-09 12:34:55 -04:00
. SetGroup ( WorkspaceMenuCategoryRef )
. SetIcon ( FSlateIcon ( FEditorStyle : : GetStyleSetName ( ) , " LevelEditor.Tabs.Details " ) ) ;
2014-03-14 14:13:41 -04:00
TabManager - > RegisterTabSpawner ( SocketManagerTabId , FOnSpawnTab : : CreateSP ( this , & FStaticMeshEditor : : SpawnTab_SocketManager ) )
. SetDisplayName ( LOCTEXT ( " SocketManagerTab " , " Socket Manager " ) )
2014-10-09 12:34:55 -04:00
. SetGroup ( WorkspaceMenuCategoryRef ) ;
2014-03-14 14:13:41 -04:00
TabManager - > RegisterTabSpawner ( CollisionTabId , FOnSpawnTab : : CreateSP ( this , & FStaticMeshEditor : : SpawnTab_Collision ) )
. SetDisplayName ( LOCTEXT ( " CollisionTab " , " Convex Decomposition " ) )
2014-10-09 12:34:55 -04:00
. SetGroup ( WorkspaceMenuCategoryRef ) ;
2014-03-14 14:13:41 -04:00
}
void FStaticMeshEditor : : UnregisterTabSpawners ( const TSharedRef < class FTabManager > & TabManager )
{
FAssetEditorToolkit : : UnregisterTabSpawners ( TabManager ) ;
TabManager - > UnregisterTabSpawner ( ViewportTabId ) ;
TabManager - > UnregisterTabSpawner ( PropertiesTabId ) ;
TabManager - > UnregisterTabSpawner ( SocketManagerTabId ) ;
TabManager - > UnregisterTabSpawner ( CollisionTabId ) ;
}
FStaticMeshEditor : : ~ FStaticMeshEditor ( )
{
2014-04-25 06:23:34 -04:00
FReimportManager : : Instance ( ) - > OnPostReimport ( ) . RemoveAll ( this ) ;
2014-03-14 14:13:41 -04:00
GEditor - > UnregisterForUndo ( this ) ;
GEditor - > OnObjectReimported ( ) . RemoveAll ( this ) ;
}
void FStaticMeshEditor : : InitStaticMeshEditor ( const EToolkitMode : : Type Mode , const TSharedPtr < class IToolkitHost > & InitToolkitHost , UStaticMesh * ObjectToEdit )
{
2014-04-25 06:23:34 -04:00
FReimportManager : : Instance ( ) - > OnPostReimport ( ) . AddRaw ( this , & FStaticMeshEditor : : OnPostReimport ) ;
2014-03-14 14:13:41 -04:00
// Support undo/redo
ObjectToEdit - > SetFlags ( RF_Transactional ) ;
GEditor - > RegisterForUndo ( this ) ;
// Register our commands. This will only register them if not previously registered
FStaticMeshEditorCommands : : Register ( ) ;
// Register to be notified when an object is reimported.
GEditor - > OnObjectReimported ( ) . AddSP ( this , & FStaticMeshEditor : : OnObjectReimported ) ;
BindCommands ( ) ;
Viewport = SNew ( SStaticMeshEditorViewport )
. StaticMeshEditor ( SharedThis ( this ) )
. ObjectToEdit ( ObjectToEdit ) ;
FPropertyEditorModule & PropertyEditorModule = FModuleManager : : LoadModuleChecked < FPropertyEditorModule > ( TEXT ( " PropertyEditor " ) ) ;
FDetailsViewArgs DetailsViewArgs ;
DetailsViewArgs . bAllowSearch = true ;
DetailsViewArgs . bLockable = false ;
DetailsViewArgs . bUpdatesFromSelection = false ;
2015-01-26 17:14:50 -05:00
DetailsViewArgs . NameAreaSettings = FDetailsViewArgs : : HideNameArea ;
2014-03-14 14:13:41 -04:00
DetailsViewArgs . NotifyHook = this ;
StaticMeshDetailsView = PropertyEditorModule . CreateDetailView ( DetailsViewArgs ) ;
FOnGetDetailCustomizationInstance LayoutCustomStaticMeshProperties = FOnGetDetailCustomizationInstance : : CreateSP ( this , & FStaticMeshEditor : : MakeStaticMeshDetails ) ;
StaticMeshDetailsView - > RegisterInstancedCustomPropertyLayout ( UStaticMesh : : StaticClass ( ) , LayoutCustomStaticMeshProperties ) ;
SetEditorMesh ( ObjectToEdit ) ;
BuildSubTools ( ) ;
2015-03-09 17:10:09 -04:00
const TSharedRef < FTabManager : : FLayout > StandaloneDefaultLayout = FTabManager : : NewLayout ( " Standalone_StaticMeshEditor_Layout_v4 " )
2014-03-14 14:13:41 -04:00
- > AddArea
(
FTabManager : : NewPrimaryArea ( ) - > SetOrientation ( Orient_Vertical )
- > Split
(
FTabManager : : NewStack ( )
- > SetSizeCoefficient ( 0.1f )
- > SetHideTabWell ( true )
- > AddTab ( GetToolbarTabId ( ) , ETabState : : OpenedTab )
)
- > Split
(
2015-03-09 17:10:09 -04:00
FTabManager : : NewSplitter ( ) - > SetOrientation ( Orient_Horizontal )
- > SetSizeCoefficient ( 0.9f )
2014-03-14 14:13:41 -04:00
- > Split
(
FTabManager : : NewStack ( )
- > SetSizeCoefficient ( 0.6f )
2015-03-09 17:10:09 -04:00
- > AddTab ( ViewportTabId , ETabState : : OpenedTab )
- > SetHideTabWell ( true )
2014-03-14 14:13:41 -04:00
)
- > Split
(
2015-03-09 17:10:09 -04:00
FTabManager : : NewSplitter ( ) - > SetOrientation ( Orient_Vertical )
- > SetSizeCoefficient ( 0.2f )
2014-03-14 14:13:41 -04:00
- > Split
(
FTabManager : : NewStack ( )
2015-03-09 17:10:09 -04:00
- > SetSizeCoefficient ( 0.7f )
2014-03-14 14:13:41 -04:00
- > AddTab ( PropertiesTabId , ETabState : : OpenedTab )
)
- > Split
(
FTabManager : : NewStack ( )
2015-03-09 17:10:09 -04:00
- > SetSizeCoefficient ( 0.3f )
- > AddTab ( SocketManagerTabId , ETabState : : OpenedTab )
2014-03-14 14:13:41 -04:00
- > AddTab ( CollisionTabId , ETabState : : ClosedTab )
)
)
)
) ;
const bool bCreateDefaultStandaloneMenu = true ;
const bool bCreateDefaultToolbar = true ;
FAssetEditorToolkit : : InitAssetEditor ( Mode , InitToolkitHost , StaticMeshEditorAppIdentifier , StandaloneDefaultLayout , bCreateDefaultToolbar , bCreateDefaultStandaloneMenu , ObjectToEdit ) ;
ExtendMenu ( ) ;
ExtendToolBar ( ) ;
RegenerateMenusAndToolbars ( ) ;
}
TSharedRef < IDetailCustomization > FStaticMeshEditor : : MakeStaticMeshDetails ( )
{
TSharedRef < FStaticMeshDetails > NewDetails = MakeShareable ( new FStaticMeshDetails ( * this ) ) ;
StaticMeshDetails = NewDetails ;
return NewDetails ;
}
void FStaticMeshEditor : : ExtendMenu ( )
{
struct Local
{
static void FillEditMenu ( FMenuBuilder & InMenuBuilder )
{
InMenuBuilder . BeginSection ( " Sockets " , LOCTEXT ( " EditStaticMeshSockets " , " Sockets " ) ) ;
{
InMenuBuilder . AddMenuEntry ( FGenericCommands : : Get ( ) . Delete , " DeleteSocket " , LOCTEXT ( " DeleteSocket " , " Delete Socket " ) , LOCTEXT ( " DeleteSocketToolTip " , " Deletes the selected socket from the mesh. " ) ) ;
InMenuBuilder . AddMenuEntry ( FGenericCommands : : Get ( ) . Duplicate , " DuplicateSocket " , LOCTEXT ( " DuplicateSocket " , " Duplicate Socket " ) , LOCTEXT ( " DuplicateSocketToolTip " , " Duplicates the selected socket. " ) ) ;
}
InMenuBuilder . EndSection ( ) ;
}
static void FillMeshMenu ( FMenuBuilder & InMenuBuilder )
{
// @todo mainframe: These menus, and indeed all menus like them, should be updated with extension points, plus expose public module
// access to extending the menus. They may also need to extend the command list, or be able to PUSH a command list of their own.
// If we decide to only allow PUSHING, then nothing else should be needed (happens by extender automatically). But if we want to
// augment the asset editor's existing command list, then we need to think about how to expose support for that.
InMenuBuilder . BeginSection ( " MeshFindSource " ) ;
{
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . FindSource ) ;
}
InMenuBuilder . EndSection ( ) ;
InMenuBuilder . BeginSection ( " MeshChange " ) ;
{
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . ChangeMesh ) ;
2015-02-21 13:20:31 -05:00
static auto * CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.StaticMesh.EnableSaveGeneratedLODsInPackage " ) ) ;
if ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 )
{
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . SaveGeneratedLODs ) ;
}
2014-03-14 14:13:41 -04:00
}
InMenuBuilder . EndSection ( ) ;
}
static void FillCollisionMenu ( FMenuBuilder & InMenuBuilder )
{
InMenuBuilder . BeginSection ( " CollisionEditCollision " ) ;
{
2014-06-13 05:03:24 -04:00
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateSphereCollision ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateSphylCollision ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateBoxCollision ) ;
2014-03-14 14:13:41 -04:00
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateDOP10X ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateDOP10Y ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateDOP10Z ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateDOP18 ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateDOP26 ) ;
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . ConvertBoxesToConvex ) ;
2014-06-13 05:03:24 -04:00
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . RemoveCollision ) ;
InMenuBuilder . AddMenuEntry ( FGenericCommands : : Get ( ) . Delete , " DeleteCollision " , LOCTEXT ( " DeleteCollision " , " Delete Selected Collision " ) , LOCTEXT ( " DeleteCollisionToolTip " , " Deletes the selected Collision from the mesh. " ) ) ;
InMenuBuilder . AddMenuEntry ( FGenericCommands : : Get ( ) . Duplicate , " DuplicateCollision " , LOCTEXT ( " DuplicateCollision " , " Duplicate Selected Collision " ) , LOCTEXT ( " DuplicateCollisionToolTip " , " Duplicates the selected Collision. " ) ) ;
2014-03-14 14:13:41 -04:00
}
InMenuBuilder . EndSection ( ) ;
InMenuBuilder . BeginSection ( " CollisionAutoConvexCollision " ) ;
{
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CreateAutoConvexCollision ) ;
}
InMenuBuilder . EndSection ( ) ;
InMenuBuilder . BeginSection ( " CollisionCopy " ) ;
{
InMenuBuilder . AddMenuEntry ( FStaticMeshEditorCommands : : Get ( ) . CopyCollisionFromSelectedMesh ) ;
}
InMenuBuilder . EndSection ( ) ;
}
static void GenerateMeshAndCollisionMenuBars ( FMenuBarBuilder & InMenuBarBuilder )
{
InMenuBarBuilder . AddPullDownMenu (
LOCTEXT ( " StaticMeshEditorMeshMenu " , " Mesh " ) ,
LOCTEXT ( " StaticMeshEditorMeshMenu_ToolTip " , " Opens a menu with commands for altering this mesh " ) ,
FNewMenuDelegate : : CreateStatic ( & Local : : FillMeshMenu ) ,
" Mesh " ) ;
InMenuBarBuilder . AddPullDownMenu (
LOCTEXT ( " StaticMeshEditorCollisionMenu " , " Collision " ) ,
LOCTEXT ( " StaticMeshEditorCollisionMenu_ToolTip " , " Opens a menu with commands for editing this mesh's collision " ) ,
FNewMenuDelegate : : CreateStatic ( & Local : : FillCollisionMenu ) ,
" Collision " ) ;
}
} ;
TSharedPtr < FExtender > MenuExtender = MakeShareable ( new FExtender ) ;
MenuExtender - > AddMenuExtension (
" EditHistory " ,
EExtensionHook : : After ,
GetToolkitCommands ( ) ,
FMenuExtensionDelegate : : CreateStatic ( & Local : : FillEditMenu ) ) ;
MenuExtender - > AddMenuBarExtension (
" Asset " ,
EExtensionHook : : After ,
GetToolkitCommands ( ) ,
FMenuBarExtensionDelegate : : CreateStatic ( & Local : : GenerateMeshAndCollisionMenuBars )
) ;
AddMenuExtender ( MenuExtender ) ;
IStaticMeshEditorModule * StaticMeshEditorModule = & FModuleManager : : LoadModuleChecked < IStaticMeshEditorModule > ( " StaticMeshEditor " ) ;
AddMenuExtender ( StaticMeshEditorModule - > GetMenuExtensibilityManager ( ) - > GetAllExtenders ( GetToolkitCommands ( ) , GetEditingObjects ( ) ) ) ;
}
void FStaticMeshEditor : : AddReferencedObjects ( FReferenceCollector & Collector )
{
Collector . AddReferencedObject ( StaticMesh ) ;
}
TSharedRef < SDockTab > FStaticMeshEditor : : SpawnTab_Viewport ( const FSpawnTabArgs & Args )
{
check ( Args . GetTabId ( ) = = ViewportTabId ) ;
TSharedRef < SDockTab > SpawnedTab =
SNew ( SDockTab )
. Label ( LOCTEXT ( " StaticMeshViewport_TabTitle " , " Viewport " ) )
[
Viewport . ToSharedRef ( )
] ;
Viewport - > SetParentTab ( SpawnedTab ) ;
return SpawnedTab ;
}
TSharedRef < SDockTab > FStaticMeshEditor : : SpawnTab_Properties ( const FSpawnTabArgs & Args )
{
check ( Args . GetTabId ( ) = = PropertiesTabId ) ;
return SNew ( SDockTab )
. Icon ( FEditorStyle : : GetBrush ( " StaticMeshEditor.Tabs.Properties " ) )
. Label ( LOCTEXT ( " StaticMeshProperties_TabTitle " , " Details " ) )
[
StaticMeshDetailsView . ToSharedRef ( )
] ;
}
TSharedRef < SDockTab > FStaticMeshEditor : : SpawnTab_SocketManager ( const FSpawnTabArgs & Args )
{
check ( Args . GetTabId ( ) = = SocketManagerTabId ) ;
return SNew ( SDockTab )
. Label ( LOCTEXT ( " StaticMeshSocketManager_TabTitle " , " Socket Manager " ) )
[
SocketManager . ToSharedRef ( )
] ;
}
TSharedRef < SDockTab > FStaticMeshEditor : : SpawnTab_Collision ( const FSpawnTabArgs & Args )
{
check ( Args . GetTabId ( ) = = CollisionTabId ) ;
return SNew ( SDockTab )
. Label ( LOCTEXT ( " StaticMeshConvexDecomp_TabTitle " , " Convex Decomposition " ) )
[
ConvexDecomposition . ToSharedRef ( )
] ;
}
void FStaticMeshEditor : : BindCommands ( )
{
const FStaticMeshEditorCommands & Commands = FStaticMeshEditorCommands : : Get ( ) ;
const TSharedRef < FUICommandList > & UICommandList = GetToolkitCommands ( ) ;
UICommandList - > MapAction ( FGenericCommands : : Get ( ) . Delete ,
2014-06-13 05:03:24 -04:00
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : DeleteSelected ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanDeleteSelected ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction ( FGenericCommands : : Get ( ) . Undo ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : UndoAction ) ) ;
UICommandList - > MapAction ( FGenericCommands : : Get ( ) . Redo ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : RedoAction ) ) ;
UICommandList - > MapAction (
FGenericCommands : : Get ( ) . Duplicate ,
2014-06-13 05:03:24 -04:00
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : DuplicateSelected ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanDuplicateSelected ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction (
FGenericCommands : : Get ( ) . Rename ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : RequestRenameSelectedSocket ) ,
2014-06-13 05:03:24 -04:00
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanRenameSelected ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction (
Commands . CreateDOP10X ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : GenerateKDop , KDopDir10X , ( uint32 ) 10 ) ) ;
UICommandList - > MapAction (
Commands . CreateDOP10Y ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : GenerateKDop , KDopDir10Y , ( uint32 ) 10 ) ) ;
UICommandList - > MapAction (
Commands . CreateDOP10Z ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : GenerateKDop , KDopDir10Z , ( uint32 ) 10 ) ) ;
UICommandList - > MapAction (
Commands . CreateDOP18 ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : GenerateKDop , KDopDir18 , ( uint32 ) 18 ) ) ;
UICommandList - > MapAction (
Commands . CreateDOP26 ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : GenerateKDop , KDopDir26 , ( uint32 ) 26 ) ) ;
2014-06-13 05:03:24 -04:00
UICommandList - > MapAction (
Commands . CreateBoxCollision ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnCollisionBox ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction (
Commands . CreateSphereCollision ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnCollisionSphere ) ) ;
2014-06-13 05:03:24 -04:00
UICommandList - > MapAction (
Commands . CreateSphylCollision ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnCollisionSphyl ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction (
Commands . RemoveCollision ,
2015-01-06 14:54:09 -05:00
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnRemoveCollision ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanRemoveCollision ) ) ;
2014-03-14 14:13:41 -04:00
UICommandList - > MapAction (
Commands . ConvertBoxesToConvex ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnConvertBoxToConvexCollision ) ) ;
UICommandList - > MapAction (
Commands . CopyCollisionFromSelectedMesh ,
2014-12-19 12:47:41 -05:00
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnCopyCollisionFromSelectedStaticMesh ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanCopyCollisionFromSelectedStaticMesh ) ) ;
2014-03-14 14:13:41 -04:00
// Mesh menu
UICommandList - > MapAction (
Commands . FindSource ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : ExecuteFindInExplorer ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanExecuteSourceCommands ) ) ;
UICommandList - > MapAction (
Commands . ChangeMesh ,
2014-12-19 12:47:41 -05:00
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnChangeMesh ) ,
FCanExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : CanChangeMesh ) ) ;
2014-03-14 14:13:41 -04:00
2015-02-21 13:20:31 -05:00
UICommandList - > MapAction (
Commands . SaveGeneratedLODs ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnSaveGeneratedLODs ) ) ;
2014-03-14 14:13:41 -04:00
// Collision Menu
UICommandList - > MapAction (
Commands . CreateAutoConvexCollision ,
FExecuteAction : : CreateSP ( this , & FStaticMeshEditor : : OnConvexDecomposition ) ) ;
}
void FStaticMeshEditor : : ExtendToolBar ( )
{
struct Local
{
static void FillToolbar ( FToolBarBuilder & ToolbarBuilder , TSharedPtr < class STextComboBox > UVChannelCombo , TSharedPtr < class STextComboBox > LODLevelCombo )
{
ToolbarBuilder . BeginSection ( " Realtime " ) ;
{
ToolbarBuilder . AddToolBarButton ( FEditorViewportCommands : : Get ( ) . ToggleRealTime ) ;
}
ToolbarBuilder . EndSection ( ) ;
ToolbarBuilder . BeginSection ( " Command " ) ;
{
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowSockets ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowWireframe ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowVertexColor ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowGrid ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowBounds ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowCollision ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowPivot ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowNormals ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowTangents ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowBinormals ) ;
2015-01-06 04:14:48 -05:00
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetShowVertices ) ;
2014-03-14 14:13:41 -04:00
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetDrawUVs ) ;
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . SetDrawAdditionalData ) ;
}
ToolbarBuilder . EndSection ( ) ;
ToolbarBuilder . BeginSection ( " UV " ) ;
{
ToolbarBuilder . AddWidget ( UVChannelCombo . ToSharedRef ( ) ) ;
}
ToolbarBuilder . EndSection ( ) ;
ToolbarBuilder . BeginSection ( " Camera " ) ;
{
ToolbarBuilder . AddToolBarButton ( FStaticMeshEditorCommands : : Get ( ) . ResetCamera ) ;
}
ToolbarBuilder . EndSection ( ) ;
ToolbarBuilder . BeginSection ( " LOD " ) ;
{
ToolbarBuilder . AddWidget ( LODLevelCombo . ToSharedRef ( ) ) ;
}
ToolbarBuilder . EndSection ( ) ;
}
} ;
TSharedPtr < FExtender > ToolbarExtender = MakeShareable ( new FExtender ) ;
ToolbarExtender - > AddToolBarExtension (
" Asset " ,
EExtensionHook : : After ,
Viewport - > GetCommandList ( ) ,
FToolBarExtensionDelegate : : CreateStatic ( & Local : : FillToolbar , UVChannelCombo , LODLevelCombo )
) ;
AddToolbarExtender ( ToolbarExtender ) ;
IStaticMeshEditorModule * StaticMeshEditorModule = & FModuleManager : : LoadModuleChecked < IStaticMeshEditorModule > ( " StaticMeshEditor " ) ;
AddToolbarExtender ( StaticMeshEditorModule - > GetToolBarExtensibilityManager ( ) - > GetAllExtenders ( GetToolkitCommands ( ) , GetEditingObjects ( ) ) ) ;
}
void FStaticMeshEditor : : BuildSubTools ( )
{
FSimpleDelegate OnSocketSelectionChanged = FSimpleDelegate : : CreateSP ( SharedThis ( this ) , & FStaticMeshEditor : : OnSocketSelectionChanged ) ;
SocketManager = ISocketManager : : CreateSocketManager ( SharedThis ( this ) , OnSocketSelectionChanged ) ;
SAssignNew ( ConvexDecomposition , SConvexDecomposition )
. StaticMeshEditorPtr ( SharedThis ( this ) ) ;
// Build toolbar widgets
UVChannelCombo = SNew ( STextComboBox )
. OptionsSource ( & UVChannels )
. OnSelectionChanged ( this , & FStaticMeshEditor : : ComboBoxSelectionChanged )
. IsEnabled ( FSlateApplication : : Get ( ) . GetNormalExecutionAttribute ( ) ) ;
if ( UVChannels . IsValidIndex ( 0 ) )
{
UVChannelCombo - > SetSelectedItem ( UVChannels [ 0 ] ) ;
}
LODLevelCombo = SNew ( STextComboBox )
. OptionsSource ( & LODLevels )
. OnSelectionChanged ( this , & FStaticMeshEditor : : LODLevelsSelectionChanged )
. IsEnabled ( FSlateApplication : : Get ( ) . GetNormalExecutionAttribute ( ) ) ;
if ( LODLevels . IsValidIndex ( 0 ) )
{
LODLevelCombo - > SetSelectedItem ( LODLevels [ 0 ] ) ;
}
}
FName FStaticMeshEditor : : GetToolkitFName ( ) const
{
return FName ( " StaticMeshEditor " ) ;
}
FText FStaticMeshEditor : : GetBaseToolkitName ( ) const
{
return LOCTEXT ( " AppLabel " , " StaticMesh Editor " ) ;
}
FString FStaticMeshEditor : : GetWorldCentricTabPrefix ( ) const
{
return LOCTEXT ( " WorldCentricTabPrefix " , " StaticMesh " ) . ToString ( ) ;
}
FLinearColor FStaticMeshEditor : : GetWorldCentricTabColorScale ( ) const
{
return FLinearColor ( 0.3f , 0.2f , 0.5f , 0.5f ) ;
}
UStaticMeshComponent * FStaticMeshEditor : : GetStaticMeshComponent ( ) const
{
return Viewport - > GetStaticMeshComponent ( ) ;
}
void FStaticMeshEditor : : SetSelectedSocket ( UStaticMeshSocket * InSelectedSocket )
{
SocketManager - > SetSelectedSocket ( InSelectedSocket ) ;
}
UStaticMeshSocket * FStaticMeshEditor : : GetSelectedSocket ( ) const
{
check ( SocketManager . IsValid ( ) ) ;
return SocketManager - > GetSelectedSocket ( ) ;
}
void FStaticMeshEditor : : DuplicateSelectedSocket ( )
{
SocketManager - > DuplicateSelectedSocket ( ) ;
}
void FStaticMeshEditor : : RequestRenameSelectedSocket ( )
{
SocketManager - > RequestRenameSelectedSocket ( ) ;
}
2014-06-13 05:03:24 -04:00
bool FStaticMeshEditor : : IsPrimValid ( const FPrimData & InPrimData ) const
{
if ( StaticMesh - > BodySetup )
{
const FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
switch ( InPrimData . PrimType )
{
case KPT_Sphere :
return AggGeom - > SphereElems . IsValidIndex ( InPrimData . PrimIndex ) ;
case KPT_Box :
return AggGeom - > BoxElems . IsValidIndex ( InPrimData . PrimIndex ) ;
case KPT_Sphyl :
return AggGeom - > SphylElems . IsValidIndex ( InPrimData . PrimIndex ) ;
case KPT_Convex :
return AggGeom - > ConvexElems . IsValidIndex ( InPrimData . PrimIndex ) ;
}
}
return false ;
}
bool FStaticMeshEditor : : HasSelectedPrims ( ) const
{
return ( SelectedPrims . Num ( ) > 0 ? true : false ) ;
}
2014-09-22 10:43:45 -04:00
void FStaticMeshEditor : : AddSelectedPrim ( const FPrimData & InPrimData , bool bClearSelection )
2014-06-13 05:03:24 -04:00
{
check ( IsPrimValid ( InPrimData ) ) ;
2014-09-16 04:20:39 -04:00
// Enable collision, if not already
if ( ! Viewport - > GetViewportClient ( ) . IsSetShowWireframeCollisionChecked ( ) )
{
Viewport - > GetViewportClient ( ) . SetShowWireframeCollision ( ) ;
}
2014-09-22 10:43:45 -04:00
if ( bClearSelection )
{
ClearSelectedPrims ( ) ;
}
2014-06-13 05:03:24 -04:00
SelectedPrims . Add ( InPrimData ) ;
}
void FStaticMeshEditor : : RemoveSelectedPrim ( const FPrimData & InPrimData )
{
SelectedPrims . Remove ( InPrimData ) ;
}
void FStaticMeshEditor : : RemoveInvalidPrims ( )
{
for ( int32 PrimIdx = SelectedPrims . Num ( ) - 1 ; PrimIdx > = 0 ; PrimIdx - - )
{
FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
if ( ! IsPrimValid ( PrimData ) )
{
SelectedPrims . RemoveAt ( PrimIdx ) ;
}
}
}
bool FStaticMeshEditor : : IsSelectedPrim ( const FPrimData & InPrimData ) const
{
return SelectedPrims . Contains ( InPrimData ) ;
}
void FStaticMeshEditor : : ClearSelectedPrims ( )
{
SelectedPrims . Empty ( ) ;
}
void FStaticMeshEditor : : DuplicateSelectedPrims ( const FVector * InOffset )
{
if ( SelectedPrims . Num ( ) > 0 )
{
check ( StaticMesh - > BodySetup ) ;
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_DuplicateSelectedPrims " , " Duplicate Collision " ) ) ;
StaticMesh - > BodySetup - > Modify ( ) ;
//Clear the cache (PIE may have created some data), create new GUID
StaticMesh - > BodySetup - > InvalidatePhysicsData ( ) ;
for ( int32 PrimIdx = 0 ; PrimIdx < SelectedPrims . Num ( ) ; PrimIdx + + )
{
FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
check ( IsPrimValid ( PrimData ) ) ;
switch ( PrimData . PrimType )
{
case KPT_Sphere :
{
const FKSphereElem SphereElem = AggGeom - > SphereElems [ PrimData . PrimIndex ] ;
PrimData . PrimIndex = AggGeom - > SphereElems . Add ( SphereElem ) ;
}
break ;
case KPT_Box :
{
const FKBoxElem BoxElem = AggGeom - > BoxElems [ PrimData . PrimIndex ] ;
PrimData . PrimIndex = AggGeom - > BoxElems . Add ( BoxElem ) ;
}
break ;
case KPT_Sphyl :
{
const FKSphylElem SphylElem = AggGeom - > SphylElems [ PrimData . PrimIndex ] ;
PrimData . PrimIndex = AggGeom - > SphylElems . Add ( SphylElem ) ;
}
break ;
case KPT_Convex :
{
const FKConvexElem ConvexElem = AggGeom - > ConvexElems [ PrimData . PrimIndex ] ;
PrimData . PrimIndex = AggGeom - > ConvexElems . Add ( ConvexElem ) ;
}
break ;
}
// If specified, offset the duplicate by a specific amount
if ( InOffset )
{
FTransform PrimTransform = GetPrimTransform ( PrimData ) ;
FVector PrimLocation = PrimTransform . GetLocation ( ) ;
PrimLocation + = * InOffset ;
PrimTransform . SetLocation ( PrimLocation ) ;
SetPrimTransform ( PrimData , PrimTransform ) ;
}
}
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
GEditor - > EndTransaction ( ) ;
// Mark staticmesh as dirty, to help make sure it gets saved.
StaticMesh - > MarkPackageDirty ( ) ;
// Update views/property windows
Viewport - > RefreshViewport ( ) ;
2014-09-10 17:19:28 -04:00
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-06-13 05:03:24 -04:00
}
}
void FStaticMeshEditor : : TranslateSelectedPrims ( const FVector & InDrag )
{
2014-12-01 11:19:18 -05:00
check ( StaticMesh - > BodySetup ) ;
StaticMesh - > BodySetup - > InvalidatePhysicsData ( ) ;
2014-06-13 05:03:24 -04:00
for ( int32 PrimIdx = 0 ; PrimIdx < SelectedPrims . Num ( ) ; PrimIdx + + )
{
const FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
FTransform PrimTransform = GetPrimTransform ( PrimData ) ;
FVector PrimLocation = PrimTransform . GetLocation ( ) ;
PrimLocation + = InDrag ;
PrimTransform . SetLocation ( PrimLocation ) ;
SetPrimTransform ( PrimData , PrimTransform ) ;
}
2014-12-01 11:19:18 -05:00
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
2014-06-13 05:03:24 -04:00
}
void FStaticMeshEditor : : RotateSelectedPrims ( const FRotator & InRot )
{
2014-12-01 11:19:18 -05:00
check ( StaticMesh - > BodySetup ) ;
StaticMesh - > BodySetup - > InvalidatePhysicsData ( ) ;
2014-06-13 05:03:24 -04:00
const FQuat DeltaQ = InRot . Quaternion ( ) ;
for ( int32 PrimIdx = 0 ; PrimIdx < SelectedPrims . Num ( ) ; PrimIdx + + )
{
const FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
FTransform PrimTransform = GetPrimTransform ( PrimData ) ;
FRotator ActorRotWind , ActorRotRem ;
PrimTransform . Rotator ( ) . GetWindingAndRemainder ( ActorRotWind , ActorRotRem ) ;
const FQuat ActorQ = ActorRotRem . Quaternion ( ) ;
FRotator NewActorRotRem = FRotator ( DeltaQ * ActorQ ) ;
NewActorRotRem . Normalize ( ) ;
PrimTransform . SetRotation ( NewActorRotRem . Quaternion ( ) ) ;
SetPrimTransform ( PrimData , PrimTransform ) ;
}
2014-12-01 11:19:18 -05:00
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
2014-06-13 05:03:24 -04:00
}
void FStaticMeshEditor : : ScaleSelectedPrims ( const FVector & InScale )
{
check ( StaticMesh - > BodySetup ) ;
2014-12-01 11:19:18 -05:00
StaticMesh - > BodySetup - > InvalidatePhysicsData ( ) ;
2014-06-13 05:03:24 -04:00
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
FVector ModifiedScale = InScale ;
if ( GEditor - > UsePercentageBasedScaling ( ) )
{
ModifiedScale = InScale * ( ( GEditor - > GetScaleGridSize ( ) / 100.0f ) / GEditor - > GetGridSize ( ) ) ;
}
2015-01-06 14:54:09 -05:00
//Multiply in estimated size of the mesh so scaling of sphere, box and sphyl is similar speed to other scaling
float SimplePrimitiveScaleSpeedFactor = StaticMesh - > GetBounds ( ) . SphereRadius ;
2014-06-13 05:03:24 -04:00
for ( int32 PrimIdx = 0 ; PrimIdx < SelectedPrims . Num ( ) ; PrimIdx + + )
{
const FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
check ( IsPrimValid ( PrimData ) ) ;
switch ( PrimData . PrimType )
{
case KPT_Sphere :
2015-01-06 14:54:09 -05:00
AggGeom - > SphereElems [ PrimData . PrimIndex ] . ScaleElem ( SimplePrimitiveScaleSpeedFactor * ModifiedScale , MinPrimSize ) ;
2014-06-13 05:03:24 -04:00
break ;
case KPT_Box :
2015-01-06 14:54:09 -05:00
AggGeom - > BoxElems [ PrimData . PrimIndex ] . ScaleElem ( SimplePrimitiveScaleSpeedFactor * ModifiedScale , MinPrimSize ) ;
2014-06-13 05:03:24 -04:00
break ;
case KPT_Sphyl :
2015-01-06 14:54:09 -05:00
AggGeom - > SphylElems [ PrimData . PrimIndex ] . ScaleElem ( SimplePrimitiveScaleSpeedFactor * ModifiedScale , MinPrimSize ) ;
2014-06-13 05:03:24 -04:00
break ;
case KPT_Convex :
AggGeom - > ConvexElems [ PrimData . PrimIndex ] . ScaleElem ( ModifiedScale , MinPrimSize ) ;
break ;
}
2014-09-10 17:19:28 -04:00
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-06-13 05:03:24 -04:00
}
2014-12-01 11:19:18 -05:00
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
2014-06-13 05:03:24 -04:00
}
bool FStaticMeshEditor : : CalcSelectedPrimsAABB ( FBox & OutBox ) const
{
check ( StaticMesh - > BodySetup ) ;
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
for ( int32 PrimIdx = 0 ; PrimIdx < SelectedPrims . Num ( ) ; PrimIdx + + )
{
const FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
check ( IsPrimValid ( PrimData ) ) ;
switch ( PrimData . PrimType )
{
case KPT_Sphere :
OutBox + = AggGeom - > SphereElems [ PrimData . PrimIndex ] . CalcAABB ( FTransform : : Identity , 1.f ) ;
break ;
case KPT_Box :
OutBox + = AggGeom - > BoxElems [ PrimData . PrimIndex ] . CalcAABB ( FTransform : : Identity , 1.f ) ;
break ;
case KPT_Sphyl :
OutBox + = AggGeom - > SphylElems [ PrimData . PrimIndex ] . CalcAABB ( FTransform : : Identity , 1.f ) ;
break ;
case KPT_Convex :
OutBox + = AggGeom - > ConvexElems [ PrimData . PrimIndex ] . CalcAABB ( FTransform : : Identity , FVector ( 1.f ) ) ;
break ;
}
}
return HasSelectedPrims ( ) ;
}
bool FStaticMeshEditor : : GetLastSelectedPrimTransform ( FTransform & OutTransform ) const
{
if ( SelectedPrims . Num ( ) > 0 )
{
check ( StaticMesh - > BodySetup ) ;
const FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
const FPrimData & PrimData = SelectedPrims . Last ( ) ;
check ( IsPrimValid ( PrimData ) ) ;
switch ( PrimData . PrimType )
{
case KPT_Sphere :
OutTransform = AggGeom - > SphereElems [ PrimData . PrimIndex ] . GetTransform ( ) ;
break ;
case KPT_Box :
OutTransform = AggGeom - > BoxElems [ PrimData . PrimIndex ] . GetTransform ( ) ;
break ;
case KPT_Sphyl :
OutTransform = AggGeom - > SphylElems [ PrimData . PrimIndex ] . GetTransform ( ) ;
break ;
case KPT_Convex :
OutTransform = AggGeom - > ConvexElems [ PrimData . PrimIndex ] . GetTransform ( ) ;
break ;
}
}
return HasSelectedPrims ( ) ;
}
FTransform FStaticMeshEditor : : GetPrimTransform ( const FPrimData & InPrimData ) const
{
check ( StaticMesh - > BodySetup ) ;
const FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
check ( IsPrimValid ( InPrimData ) ) ;
switch ( InPrimData . PrimType )
{
case KPT_Sphere :
return AggGeom - > SphereElems [ InPrimData . PrimIndex ] . GetTransform ( ) ;
case KPT_Box :
return AggGeom - > BoxElems [ InPrimData . PrimIndex ] . GetTransform ( ) ;
case KPT_Sphyl :
return AggGeom - > SphylElems [ InPrimData . PrimIndex ] . GetTransform ( ) ;
case KPT_Convex :
return AggGeom - > ConvexElems [ InPrimData . PrimIndex ] . GetTransform ( ) ;
}
return FTransform : : Identity ;
}
void FStaticMeshEditor : : SetPrimTransform ( const FPrimData & InPrimData , const FTransform & InPrimTransform ) const
{
check ( StaticMesh - > BodySetup ) ;
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
check ( IsPrimValid ( InPrimData ) ) ;
switch ( InPrimData . PrimType )
{
case KPT_Sphere :
AggGeom - > SphereElems [ InPrimData . PrimIndex ] . SetTransform ( InPrimTransform ) ;
break ;
case KPT_Box :
AggGeom - > BoxElems [ InPrimData . PrimIndex ] . SetTransform ( InPrimTransform ) ;
break ;
case KPT_Sphyl :
AggGeom - > SphylElems [ InPrimData . PrimIndex ] . SetTransform ( InPrimTransform ) ;
break ;
case KPT_Convex :
AggGeom - > ConvexElems [ InPrimData . PrimIndex ] . SetTransform ( InPrimTransform ) ;
break ;
}
2014-09-10 17:19:28 -04:00
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-06-13 05:03:24 -04:00
}
2014-09-16 04:20:39 -04:00
bool FStaticMeshEditor : : OverlapsExistingPrim ( const FPrimData & InPrimData ) const
{
check ( StaticMesh - > BodySetup ) ;
const FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
// Assume that if the transform of the prim is the same, then it overlaps (FKConvexElem doesn't have an operator==, and no shape takes tolerances into account)
check ( IsPrimValid ( InPrimData ) ) ;
switch ( InPrimData . PrimType )
{
case KPT_Sphere :
{
const FKSphereElem InSphereElem = AggGeom - > SphereElems [ InPrimData . PrimIndex ] ;
const FTransform InElemTM = InSphereElem . GetTransform ( ) ;
for ( int32 i = 0 ; i < AggGeom - > SphereElems . Num ( ) ; + + i )
{
if ( i = = InPrimData . PrimIndex )
{
continue ;
}
const FKSphereElem & SphereElem = AggGeom - > SphereElems [ i ] ;
const FTransform ElemTM = SphereElem . GetTransform ( ) ;
if ( InElemTM . Equals ( ElemTM ) )
{
return true ;
}
}
}
break ;
case KPT_Box :
{
const FKBoxElem InBoxElem = AggGeom - > BoxElems [ InPrimData . PrimIndex ] ;
const FTransform InElemTM = InBoxElem . GetTransform ( ) ;
for ( int32 i = 0 ; i < AggGeom - > BoxElems . Num ( ) ; + + i )
{
if ( i = = InPrimData . PrimIndex )
{
continue ;
}
const FKBoxElem & BoxElem = AggGeom - > BoxElems [ i ] ;
const FTransform ElemTM = BoxElem . GetTransform ( ) ;
if ( InElemTM . Equals ( ElemTM ) )
{
return true ;
}
}
}
break ;
case KPT_Sphyl :
{
const FKSphylElem InSphylElem = AggGeom - > SphylElems [ InPrimData . PrimIndex ] ;
const FTransform InElemTM = InSphylElem . GetTransform ( ) ;
for ( int32 i = 0 ; i < AggGeom - > SphylElems . Num ( ) ; + + i )
{
if ( i = = InPrimData . PrimIndex )
{
continue ;
}
const FKSphylElem & SphylElem = AggGeom - > SphylElems [ i ] ;
const FTransform ElemTM = SphylElem . GetTransform ( ) ;
if ( InElemTM . Equals ( ElemTM ) )
{
return true ;
}
}
}
break ;
case KPT_Convex :
{
const FKConvexElem InConvexElem = AggGeom - > ConvexElems [ InPrimData . PrimIndex ] ;
const FTransform InElemTM = InConvexElem . GetTransform ( ) ;
for ( int32 i = 0 ; i < AggGeom - > ConvexElems . Num ( ) ; + + i )
{
if ( i = = InPrimData . PrimIndex )
{
continue ;
}
const FKConvexElem & ConvexElem = AggGeom - > ConvexElems [ i ] ;
const FTransform ElemTM = ConvexElem . GetTransform ( ) ;
if ( InElemTM . Equals ( ElemTM ) )
{
return true ;
}
}
}
break ;
}
return false ;
}
2014-03-14 14:13:41 -04:00
void FStaticMeshEditor : : RefreshTool ( )
{
int32 NumLODs = StaticMesh - > GetNumLODs ( ) ;
for ( int32 LODIndex = 0 ; LODIndex < NumLODs ; + + LODIndex )
{
UpdateLODStats ( LODIndex ) ;
}
bool bForceRefresh = true ;
StaticMeshDetailsView - > SetObject ( StaticMesh , bForceRefresh ) ;
RegenerateLODComboList ( ) ;
RegenerateUVChannelComboList ( ) ;
RefreshViewport ( ) ;
}
void FStaticMeshEditor : : RefreshViewport ( )
{
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : RegenerateLODComboList ( )
{
if ( StaticMesh - > RenderData )
{
int32 OldLOD = GetCurrentLODLevel ( ) ;
NumLODLevels = StaticMesh - > RenderData - > LODResources . Num ( ) ;
// Fill out the LOD level combo.
LODLevels . Empty ( ) ;
LODLevels . Add ( MakeShareable ( new FString ( LOCTEXT ( " AutoLOD " , " Auto LOD " ) . ToString ( ) ) ) ) ;
LODLevels . Add ( MakeShareable ( new FString ( LOCTEXT ( " BaseLOD " , " Base LOD " ) . ToString ( ) ) ) ) ;
for ( int32 LODLevelID = 1 ; LODLevelID < NumLODLevels ; + + LODLevelID )
{
LODLevels . Add ( MakeShareable ( new FString ( FString : : Printf ( * LOCTEXT ( " LODLevel_ID " , " LOD Level %d " ) . ToString ( ) , LODLevelID ) ) ) ) ;
}
if ( LODLevelCombo . IsValid ( ) )
{
LODLevelCombo - > RefreshOptions ( ) ;
if ( OldLOD < LODLevels . Num ( ) )
{
LODLevelCombo - > SetSelectedItem ( LODLevels [ OldLOD ] ) ;
}
else
{
LODLevelCombo - > SetSelectedItem ( LODLevels [ 0 ] ) ;
}
}
}
else
{
NumLODLevels = 0 ;
LODLevels . Empty ( ) ;
LODLevels . Add ( MakeShareable ( new FString ( LOCTEXT ( " AutoLOD " , " Auto LOD " ) . ToString ( ) ) ) ) ;
}
}
void FStaticMeshEditor : : RegenerateUVChannelComboList ( )
{
int32 OldUVChannel = GetCurrentUVChannel ( ) ;
// Fill out the UV channels combo.
UVChannels . Empty ( ) ;
int32 MaxUVChannels = FMath : : Max < int32 > ( GetNumUVChannels ( ) , 1 ) ;
for ( int32 UVChannelID = 0 ; UVChannelID < MaxUVChannels ; + + UVChannelID )
{
UVChannels . Add ( MakeShareable ( new FString ( FText : : Format ( LOCTEXT ( " UVChannel_ID " , " UV Channel {0} " ) , FText : : AsNumber ( UVChannelID ) ) . ToString ( ) ) ) ) ;
}
if ( UVChannelCombo . IsValid ( ) )
{
UVChannelCombo - > RefreshOptions ( ) ;
if ( OldUVChannel > = 0 & & OldUVChannel < GetNumUVChannels ( ) )
{
UVChannelCombo - > SetSelectedItem ( UVChannels [ OldUVChannel ] ) ;
}
else
{
UVChannelCombo - > SetSelectedItem ( UVChannels [ 0 ] ) ;
}
}
}
void FStaticMeshEditor : : UpdateLODStats ( int32 CurrentLOD )
{
NumTriangles [ CurrentLOD ] = 0 ;
NumVertices [ CurrentLOD ] = 0 ;
NumUVChannels [ CurrentLOD ] = 0 ;
NumLODLevels = 0 ;
if ( StaticMesh - > RenderData )
{
NumLODLevels = StaticMesh - > RenderData - > LODResources . Num ( ) ;
if ( CurrentLOD > = 0 & & CurrentLOD < NumLODLevels )
{
FStaticMeshLODResources & LODModel = StaticMesh - > RenderData - > LODResources [ CurrentLOD ] ;
NumTriangles [ CurrentLOD ] = LODModel . GetNumTriangles ( ) ;
NumVertices [ CurrentLOD ] = LODModel . GetNumVertices ( ) ;
NumUVChannels [ CurrentLOD ] = LODModel . VertexBuffer . GetNumTexCoords ( ) ;
}
}
}
void FStaticMeshEditor : : ComboBoxSelectionChanged ( TSharedPtr < FString > NewSelection , ESelectInfo : : Type /*SelectInfo*/ )
{
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : LODLevelsSelectionChanged ( TSharedPtr < FString > NewSelection , ESelectInfo : : Type /*SelectInfo*/ )
{
int32 CurrentLOD = GetCurrentLODLevel ( ) ;
UpdateLODStats ( CurrentLOD > 0 ? CurrentLOD - 1 : 0 ) ;
Viewport - > ForceLODLevel ( CurrentLOD ) ;
}
int32 FStaticMeshEditor : : GetCurrentUVChannel ( )
{
int32 Index = 0 ;
UVChannels . Find ( UVChannelCombo - > GetSelectedItem ( ) , Index ) ;
return Index ;
}
int32 FStaticMeshEditor : : GetCurrentLODLevel ( )
{
int32 Index = 0 ;
LODLevels . Find ( LODLevelCombo - > GetSelectedItem ( ) , Index ) ;
return Index ;
}
int32 FStaticMeshEditor : : GetCurrentLODIndex ( )
{
int32 Index = LODLevels . Find ( LODLevelCombo - > GetSelectedItem ( ) ) ;
return Index = = 0 ? 0 : Index - 1 ;
}
void FStaticMeshEditor : : GenerateKDop ( const FVector * Directions , uint32 NumDirections )
{
TArray < FVector > DirArray ;
for ( uint32 DirectionIndex = 0 ; DirectionIndex < NumDirections ; DirectionIndex + + )
{
DirArray . Add ( Directions [ DirectionIndex ] ) ;
}
2014-06-13 05:03:24 -04:00
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_GenerateKDop " , " Create Convex Collision " ) ) ;
const int32 PrimIndex = GenerateKDopAsSimpleCollision ( StaticMesh , DirArray ) ;
GEditor - > EndTransaction ( ) ;
if ( PrimIndex ! = INDEX_NONE )
{
2014-07-24 04:28:41 -04:00
if ( FEngineAnalytics : : IsAvailable ( ) )
{
2014-08-01 05:31:22 -04:00
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( TEXT ( " Editor.Usage.StaticMesh.Collision " ) , TEXT ( " Type " ) , TEXT ( " KDop Collision " ) ) ;
2014-07-24 04:28:41 -04:00
}
2014-09-16 04:20:39 -04:00
const FPrimData PrimData = FPrimData ( KPT_Convex , PrimIndex ) ;
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-09-22 10:43:45 -04:00
AddSelectedPrim ( PrimData , true ) ;
2014-09-16 04:20:39 -04:00
while ( OverlapsExistingPrim ( PrimData ) )
{
TranslateSelectedPrims ( OverlapNudge ) ;
}
2014-06-13 05:03:24 -04:00
}
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : OnCollisionBox ( )
{
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_OnCollisionBox " , " Create Box Collision " ) ) ;
const int32 PrimIndex = GenerateBoxAsSimpleCollision ( StaticMesh ) ;
GEditor - > EndTransaction ( ) ;
if ( PrimIndex ! = INDEX_NONE )
{
2014-07-24 04:28:41 -04:00
if ( FEngineAnalytics : : IsAvailable ( ) )
{
2014-08-01 05:31:22 -04:00
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( TEXT ( " Editor.Usage.StaticMesh.Collision " ) , TEXT ( " Type " ) , TEXT ( " Box Collision " ) ) ;
2014-07-24 04:28:41 -04:00
}
2014-09-16 04:20:39 -04:00
const FPrimData PrimData = FPrimData ( KPT_Box , PrimIndex ) ;
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-09-22 10:43:45 -04:00
AddSelectedPrim ( PrimData , true ) ;
2014-09-16 04:20:39 -04:00
while ( OverlapsExistingPrim ( PrimData ) )
{
TranslateSelectedPrims ( OverlapNudge ) ;
}
2014-06-13 05:03:24 -04:00
}
2014-03-14 14:13:41 -04:00
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : OnCollisionSphere ( )
{
2014-06-13 05:03:24 -04:00
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_OnCollisionSphere " , " Create Sphere Collision " ) ) ;
const int32 PrimIndex = GenerateSphereAsSimpleCollision ( StaticMesh ) ;
GEditor - > EndTransaction ( ) ;
if ( PrimIndex ! = INDEX_NONE )
{
2014-07-24 04:28:41 -04:00
if ( FEngineAnalytics : : IsAvailable ( ) )
{
2014-08-01 05:31:22 -04:00
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( TEXT ( " Editor.Usage.StaticMesh.Collision " ) , TEXT ( " Type " ) , TEXT ( " Sphere Collision " ) ) ;
2014-07-24 04:28:41 -04:00
}
2014-09-16 04:20:39 -04:00
const FPrimData PrimData = FPrimData ( KPT_Sphere , PrimIndex ) ;
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-09-22 10:43:45 -04:00
AddSelectedPrim ( PrimData , true ) ;
2014-09-16 04:20:39 -04:00
while ( OverlapsExistingPrim ( PrimData ) )
{
TranslateSelectedPrims ( OverlapNudge ) ;
}
2014-06-13 05:03:24 -04:00
}
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : OnCollisionSphyl ( )
{
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_OnCollisionSphyl " , " Create Capsule Collision " ) ) ;
const int32 PrimIndex = GenerateSphylAsSimpleCollision ( StaticMesh ) ;
GEditor - > EndTransaction ( ) ;
if ( PrimIndex ! = INDEX_NONE )
{
2014-07-24 04:28:41 -04:00
if ( FEngineAnalytics : : IsAvailable ( ) )
{
2014-08-01 05:31:22 -04:00
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( TEXT ( " Editor.Usage.StaticMesh.Collision " ) , TEXT ( " Type " ) , TEXT ( " Capsule Collision " ) ) ;
2014-07-24 04:28:41 -04:00
}
2014-09-16 04:20:39 -04:00
const FPrimData PrimData = FPrimData ( KPT_Sphyl , PrimIndex ) ;
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-09-22 10:43:45 -04:00
AddSelectedPrim ( PrimData , true ) ;
2014-09-16 04:20:39 -04:00
while ( OverlapsExistingPrim ( PrimData ) )
{
TranslateSelectedPrims ( OverlapNudge ) ;
}
2014-06-13 05:03:24 -04:00
}
2014-03-14 14:13:41 -04:00
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : OnRemoveCollision ( void )
{
UBodySetup * BS = StaticMesh - > BodySetup ;
2015-01-06 14:54:09 -05:00
check ( BS ! = NULL & & BS - > AggGeom . GetElementCount ( ) > 0 ) ;
2014-06-13 05:03:24 -04:00
2015-01-06 14:54:09 -05:00
ClearSelectedPrims ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-06 14:54:09 -05:00
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-06 14:54:09 -05:00
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_RemoveCollision " , " Remove Collision " ) ) ;
StaticMesh - > BodySetup - > Modify ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-06 14:54:09 -05:00
StaticMesh - > BodySetup - > RemoveSimpleCollision ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-06 14:54:09 -05:00
GEditor - > EndTransaction ( ) ;
2014-09-10 17:19:28 -04:00
2015-01-06 14:54:09 -05:00
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
// Mark staticmesh as dirty, to help make sure it gets saved.
StaticMesh - > MarkPackageDirty ( ) ;
// Update views/property windows
Viewport - > RefreshViewport ( ) ;
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
}
bool FStaticMeshEditor : : CanRemoveCollision ( )
{
UBodySetup * BS = StaticMesh - > BodySetup ;
return ( BS ! = NULL & & BS - > AggGeom . GetElementCount ( ) > 0 ) ;
2014-03-14 14:13:41 -04:00
}
/** Util for adding vertex to an array if it is not already present. */
static void AddVertexIfNotPresent ( TArray < FVector > & Vertices , const FVector & NewVertex )
{
bool bIsPresent = false ;
for ( int32 i = 0 ; i < Vertices . Num ( ) ; i + + )
{
float diffSqr = ( NewVertex - Vertices [ i ] ) . SizeSquared ( ) ;
if ( diffSqr < 0.01f * 0.01f )
{
bIsPresent = 1 ;
break ;
}
}
if ( ! bIsPresent )
{
Vertices . Add ( NewVertex ) ;
}
}
void FStaticMeshEditor : : CreateBoxVertsFromBoxCollision ( const FKBoxElem & BoxElem , TArray < FVector > & Verts , float Scale )
{
FVector B [ 2 ] , P , Q , Radii ;
// X,Y,Z member variables are LENGTH not RADIUS
Radii . X = Scale * 0.5f * BoxElem . X ;
Radii . Y = Scale * 0.5f * BoxElem . Y ;
Radii . Z = Scale * 0.5f * BoxElem . Z ;
B [ 0 ] = Radii ; // max
B [ 1 ] = - 1.0f * Radii ; // min
FTransform BoxElemTM = BoxElem . GetTransform ( ) ;
for ( int32 i = 0 ; i < 2 ; i + + )
{
for ( int32 j = 0 ; j < 2 ; j + + )
{
P . X = B [ i ] . X ; Q . X = B [ i ] . X ;
P . Y = B [ j ] . Y ; Q . Y = B [ j ] . Y ;
P . Z = B [ 0 ] . Z ; Q . Z = B [ 1 ] . Z ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( P ) ) ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( Q ) ) ;
P . Y = B [ i ] . Y ; Q . Y = B [ i ] . Y ;
P . Z = B [ j ] . Z ; Q . Z = B [ j ] . Z ;
P . X = B [ 0 ] . X ; Q . X = B [ 1 ] . X ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( P ) ) ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( Q ) ) ;
P . Z = B [ i ] . Z ; Q . Z = B [ i ] . Z ;
P . X = B [ j ] . X ; Q . X = B [ j ] . X ;
P . Y = B [ 0 ] . Y ; Q . Y = B [ 1 ] . Y ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( P ) ) ;
AddVertexIfNotPresent ( Verts , BoxElemTM . TransformPosition ( Q ) ) ;
}
}
}
void FStaticMeshEditor : : OnConvertBoxToConvexCollision ( )
{
// If we have a collision model for this staticmesh, ask if we want to replace it.
if ( StaticMesh - > BodySetup ! = NULL )
{
int32 ShouldReplace = FMessageDialog : : Open ( EAppMsgType : : YesNo , LOCTEXT ( " ConvertBoxCollisionPrompt " , " Are you sure you want to convert all box collision? " ) ) ;
if ( ShouldReplace = = EAppReturnType : : Yes )
{
UBodySetup * BodySetup = StaticMesh - > BodySetup ;
int32 NumBoxElems = BodySetup - > AggGeom . BoxElems . Num ( ) ;
if ( NumBoxElems > 0 )
{
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-03-14 14:13:41 -04:00
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands ( ) ;
FKConvexElem * NewConvexColl = NULL ;
//For each box elem, calculate the new convex collision representation
//Stored in a temp array so we can undo on failure.
TArray < FKConvexElem > TempArray ;
for ( int32 i = 0 ; i < NumBoxElems ; i + + )
{
const FKBoxElem & BoxColl = BodySetup - > AggGeom . BoxElems [ i ] ;
//Create a new convex collision element
NewConvexColl = new ( TempArray ) FKConvexElem ( ) ;
NewConvexColl - > Reset ( ) ;
//Fill the convex verts from the box elem collision and generate the convex hull
CreateBoxVertsFromBoxCollision ( BoxColl , NewConvexColl - > VertexData , 1.0f ) ;
NewConvexColl - > UpdateElemBox ( ) ;
}
//Clear the cache (PIE may have created some data), create new GUID
BodySetup - > InvalidatePhysicsData ( ) ;
//Copy the new data into the static mesh
BodySetup - > AggGeom . ConvexElems . Append ( TempArray ) ;
//Clear out what we just replaced
BodySetup - > AggGeom . BoxElems . Empty ( ) ;
BodySetup - > CreatePhysicsMeshes ( ) ;
2014-09-16 04:20:39 -04:00
// Select the new prims
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
for ( int32 i = 0 ; i < NumBoxElems ; + + i )
{
2014-09-22 10:43:45 -04:00
AddSelectedPrim ( FPrimData ( KPT_Convex , ( AggGeom - > ConvexElems . Num ( ) - ( i + 1 ) ) ) , false ) ;
2014-09-16 04:20:39 -04:00
}
2014-03-14 14:13:41 -04:00
// Mark static mesh as dirty, to help make sure it gets saved.
StaticMesh - > MarkPackageDirty ( ) ;
// Update views/property windows
Viewport - > RefreshViewport ( ) ;
2014-09-10 17:19:28 -04:00
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-03-14 14:13:41 -04:00
}
}
}
}
void FStaticMeshEditor : : OnCopyCollisionFromSelectedStaticMesh ( )
{
2014-12-19 12:47:41 -05:00
UStaticMesh * SelectedMesh = GetFirstSelectedStaticMeshInContentBrowser ( ) ;
check ( SelectedMesh & & SelectedMesh ! = StaticMesh & & SelectedMesh - > BodySetup ! = NULL ) ;
UBodySetup * BodySetup = StaticMesh - > BodySetup ;
ClearSelectedPrims ( ) ;
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands ( ) ;
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_CopyCollisionFromSelectedStaticMesh " , " Copy Collision from Selected Static Mesh " ) ) ;
BodySetup - > Modify ( ) ;
// Copy body properties from
BodySetup - > CopyBodyPropertiesFrom ( SelectedMesh - > BodySetup ) ;
// Enable collision, if not already
if ( ! Viewport - > GetViewportClient ( ) . IsSetShowWireframeCollisionChecked ( ) )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
Viewport - > GetViewportClient ( ) . SetShowWireframeCollision ( ) ;
}
// Invalidate physics data and create new meshes
BodySetup - > InvalidatePhysicsData ( ) ;
BodySetup - > CreatePhysicsMeshes ( ) ;
GEditor - > EndTransaction ( ) ;
// Mark static mesh as dirty, to help make sure it gets saved.
StaticMesh - > MarkPackageDirty ( ) ;
// Redraw level editor viewports, in case the asset's collision is visible in a viewport and the viewport isn't set to realtime.
// Note: This could be more intelligent and only trigger a redraw if the asset is referenced in the world.
GUnrealEd - > RedrawLevelEditingViewports ( ) ;
// Update views/property windows
Viewport - > RefreshViewport ( ) ;
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
}
bool FStaticMeshEditor : : CanCopyCollisionFromSelectedStaticMesh ( ) const
{
bool CanCopy = false ;
TArray < FAssetData > SelectedAssets ;
GEditor - > GetContentBrowserSelections ( SelectedAssets ) ;
if ( SelectedAssets . Num ( ) = = 1 )
{
FAssetData & Asset = SelectedAssets [ 0 ] ;
if ( Asset . GetClass ( ) = = UStaticMesh : : StaticClass ( ) )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
UStaticMesh * SelectedMesh = Cast < UStaticMesh > ( Asset . GetAsset ( ) ) ;
if ( SelectedMesh & & SelectedMesh ! = StaticMesh & & SelectedMesh - > BodySetup ! = NULL )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
CanCopy = true ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-12-19 12:47:41 -05:00
return CanCopy ;
}
UStaticMesh * FStaticMeshEditor : : GetFirstSelectedStaticMeshInContentBrowser ( ) const
{
TArray < FAssetData > SelectedAssets ;
GEditor - > GetContentBrowserSelections ( SelectedAssets ) ;
for ( auto & Asset : SelectedAssets )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
UStaticMesh * SelectedMesh = Cast < UStaticMesh > ( Asset . GetAsset ( ) ) ;
if ( SelectedMesh )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
return SelectedMesh ;
2014-03-14 14:13:41 -04:00
}
}
2014-12-19 12:47:41 -05:00
return NULL ;
2014-03-14 14:13:41 -04:00
}
void FStaticMeshEditor : : SetEditorMesh ( UStaticMesh * InStaticMesh )
{
StaticMesh = InStaticMesh ;
//Init stat arrays. A static mesh can have up to three level of details beyond the base mesh.
const int32 ArraySize = 4 ;
NumVertices . Empty ( ArraySize ) ;
NumVertices . AddZeroed ( ArraySize ) ;
NumTriangles . Empty ( ArraySize ) ;
NumTriangles . AddZeroed ( ArraySize ) ;
NumUVChannels . Empty ( ArraySize ) ;
NumUVChannels . AddZeroed ( ArraySize ) ;
// Always default the LOD to 0 when setting the mesh.
UpdateLODStats ( 0 ) ;
// Fill out the LOD level combo.
LODLevels . Empty ( ) ;
LODLevels . Add ( MakeShareable ( new FString ( LOCTEXT ( " AutoLOD " , " Auto LOD " ) . ToString ( ) ) ) ) ;
LODLevels . Add ( MakeShareable ( new FString ( LOCTEXT ( " BaseLOD " , " Base LOD " ) . ToString ( ) ) ) ) ;
for ( int32 LODLevelID = 1 ; LODLevelID < NumLODLevels ; + + LODLevelID )
{
LODLevels . Add ( MakeShareable ( new FString ( FString : : Printf ( * LOCTEXT ( " LODLevel_ID " , " LOD Level %d " ) . ToString ( ) , LODLevelID ) ) ) ) ;
//Update LOD stats for each level
UpdateLODStats ( LODLevelID ) ;
}
// Fill out the UV channels combo.
UVChannels . Empty ( ) ;
for ( int32 UVChannelID = 0 ; UVChannelID < GetNumUVChannels ( 0 ) ; + + UVChannelID )
{
UVChannels . Add ( MakeShareable ( new FString ( FText : : Format ( LOCTEXT ( " UVChannel_ID " , " UV Channel {0} " ) , FText : : AsNumber ( UVChannelID ) ) . ToString ( ) ) ) ) ;
}
if ( UVChannelCombo . IsValid ( ) )
{
UVChannelCombo - > RefreshOptions ( ) ;
if ( UVChannels . Num ( ) )
{
UVChannelCombo - > SetSelectedItem ( UVChannels [ 0 ] ) ;
}
}
if ( LODLevelCombo . IsValid ( ) )
{
LODLevelCombo - > RefreshOptions ( ) ;
if ( LODLevels . Num ( ) )
{
LODLevelCombo - > SetSelectedItem ( LODLevels [ 0 ] ) ;
}
}
// Set the details view.
StaticMeshDetailsView - > SetObject ( StaticMesh ) ;
Viewport - > UpdatePreviewMesh ( StaticMesh ) ;
Viewport - > RefreshViewport ( ) ;
}
void FStaticMeshEditor : : OnChangeMesh ( )
{
2014-12-19 12:47:41 -05:00
UStaticMesh * SelectedMesh = GetFirstSelectedStaticMeshInContentBrowser ( ) ;
check ( SelectedMesh ! = NULL & & SelectedMesh ! = StaticMesh ) ;
RemoveEditingObject ( StaticMesh ) ;
AddEditingObject ( SelectedMesh ) ;
SetEditorMesh ( SelectedMesh ) ;
// Clear selections made on previous mesh
ClearSelectedPrims ( ) ;
GetSelectedEdges ( ) . Empty ( ) ;
if ( SocketManager . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
SocketManager - > UpdateStaticMesh ( ) ;
}
}
2014-03-14 14:13:41 -04:00
2014-12-19 12:47:41 -05:00
bool FStaticMeshEditor : : CanChangeMesh ( ) const
{
bool CanChange = false ;
2014-03-14 14:13:41 -04:00
2014-12-19 12:47:41 -05:00
TArray < FAssetData > SelectedAssets ;
GEditor - > GetContentBrowserSelections ( SelectedAssets ) ;
if ( SelectedAssets . Num ( ) = = 1 )
{
FAssetData & Asset = SelectedAssets [ 0 ] ;
if ( Asset . GetClass ( ) = = UStaticMesh : : StaticClass ( ) )
2014-03-14 14:13:41 -04:00
{
2014-12-19 12:47:41 -05:00
UStaticMesh * SelectedMesh = Cast < UStaticMesh > ( Asset . GetAsset ( ) ) ;
if ( SelectedMesh & & SelectedMesh ! = StaticMesh )
{
CanChange = true ;
}
2014-03-14 14:13:41 -04:00
}
}
2014-12-19 12:47:41 -05:00
return CanChange ;
2014-03-14 14:13:41 -04:00
}
2015-02-21 13:20:31 -05:00
void FStaticMeshEditor : : OnSaveGeneratedLODs ( )
{
if ( StaticMesh )
{
StaticMesh - > GenerateLodsInPackage ( ) ;
// Update editor UI as we modified LOD groups
auto Selected = StaticMeshDetailsView - > GetSelectedObjects ( ) ;
StaticMeshDetailsView - > SetObjects ( Selected , true ) ;
// Update screen
Viewport - > RefreshViewport ( ) ;
}
}
2015-03-30 07:26:43 -04:00
void FStaticMeshEditor : : DoDecomp ( float InAccuracy , int32 InMaxHullVerts )
2014-03-14 14:13:41 -04:00
{
// Check we have a selected StaticMesh
if ( StaticMesh & & StaticMesh - > RenderData )
{
FStaticMeshLODResources & LODModel = StaticMesh - > RenderData - > LODResources [ 0 ] ;
// Start a busy cursor so the user has feedback while waiting
const FScopedBusyCursor BusyCursor ;
// Make vertex buffer
int32 NumVerts = LODModel . VertexBuffer . GetNumVertices ( ) ;
TArray < FVector > Verts ;
for ( int32 i = 0 ; i < NumVerts ; i + + )
{
FVector Vert = LODModel . PositionVertexBuffer . VertexPosition ( i ) ;
Verts . Add ( Vert ) ;
}
// Make index buffer
TArray < uint32 > Indices ;
LODModel . IndexBuffer . GetCopy ( Indices ) ;
2014-06-13 05:03:24 -04:00
ClearSelectedPrims ( ) ;
2014-03-14 14:13:41 -04:00
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands ( ) ;
// Get the BodySetup we are going to put the collision into
UBodySetup * bs = StaticMesh - > BodySetup ;
if ( bs )
{
bs - > RemoveSimpleCollision ( ) ;
}
else
{
// Otherwise, create one here.
StaticMesh - > CreateBodySetup ( ) ;
bs = StaticMesh - > BodySetup ;
}
// Run actual util to do the work
2015-03-30 07:26:43 -04:00
DecomposeMeshToHulls ( bs , Verts , Indices , InAccuracy , InMaxHullVerts ) ;
2014-03-14 14:13:41 -04:00
2014-09-16 04:20:39 -04:00
// Enable collision, if not already
if ( ! Viewport - > GetViewportClient ( ) . IsSetShowWireframeCollisionChecked ( ) )
{
Viewport - > GetViewportClient ( ) . SetShowWireframeCollision ( ) ;
}
2014-03-14 14:13:41 -04:00
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
// Mark mesh as dirty
StaticMesh - > MarkPackageDirty ( ) ;
// Update screen.
Viewport - > RefreshViewport ( ) ;
2014-09-10 17:19:28 -04:00
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-03-14 14:13:41 -04:00
}
}
TSet < int32 > & FStaticMeshEditor : : GetSelectedEdges ( )
{
return Viewport - > GetSelectedEdges ( ) ;
}
int32 FStaticMeshEditor : : GetNumTriangles ( int32 LODLevel ) const
{
return NumTriangles . IsValidIndex ( LODLevel ) ? NumTriangles [ LODLevel ] : 0 ;
}
int32 FStaticMeshEditor : : GetNumVertices ( int32 LODLevel ) const
{
return NumVertices . IsValidIndex ( LODLevel ) ? NumVertices [ LODLevel ] : 0 ;
}
int32 FStaticMeshEditor : : GetNumUVChannels ( int32 LODLevel ) const
{
return NumUVChannels . IsValidIndex ( LODLevel ) ? NumUVChannels [ LODLevel ] : 0 ;
}
2014-06-13 05:03:24 -04:00
void FStaticMeshEditor : : DeleteSelected ( )
{
if ( GetSelectedSocket ( ) )
{
DeleteSelectedSockets ( ) ;
}
if ( HasSelectedPrims ( ) )
{
DeleteSelectedPrims ( ) ;
}
}
bool FStaticMeshEditor : : CanDeleteSelected ( ) const
{
return ( GetSelectedSocket ( ) ! = NULL | | HasSelectedPrims ( ) ) ;
}
2014-03-14 14:13:41 -04:00
void FStaticMeshEditor : : DeleteSelectedSockets ( )
{
check ( SocketManager . IsValid ( ) ) ;
SocketManager - > DeleteSelectedSocket ( ) ;
}
2014-06-13 05:03:24 -04:00
void FStaticMeshEditor : : DeleteSelectedPrims ( )
2014-03-14 14:13:41 -04:00
{
2014-06-13 05:03:24 -04:00
if ( SelectedPrims . Num ( ) > 0 )
{
2015-01-06 14:54:09 -05:00
// Sort the selected prims by PrimIndex so when we're deleting them we don't mess up other prims indicies
struct FCompareFPrimDataPrimIndex
2014-06-13 05:03:24 -04:00
{
2015-01-06 14:54:09 -05:00
FORCEINLINE bool operator ( ) ( const FPrimData & A , const FPrimData & B ) const
2014-06-13 05:03:24 -04:00
{
2015-01-06 14:54:09 -05:00
return A . PrimIndex < B . PrimIndex ;
2014-06-13 05:03:24 -04:00
}
2015-01-06 14:54:09 -05:00
} ;
SelectedPrims . Sort ( FCompareFPrimDataPrimIndex ( ) ) ;
2014-06-13 05:03:24 -04:00
2015-01-06 14:54:09 -05:00
check ( StaticMesh - > BodySetup ) ;
2014-06-13 05:03:24 -04:00
2015-01-06 14:54:09 -05:00
FKAggregateGeom * AggGeom = & StaticMesh - > BodySetup - > AggGeom ;
2014-06-13 05:03:24 -04:00
2015-01-06 14:54:09 -05:00
GEditor - > BeginTransaction ( LOCTEXT ( " FStaticMeshEditor_DeleteSelectedPrims " , " Delete Collision " ) ) ;
StaticMesh - > BodySetup - > Modify ( ) ;
2014-06-13 05:03:24 -04:00
2015-01-06 14:54:09 -05:00
for ( int32 PrimIdx = SelectedPrims . Num ( ) - 1 ; PrimIdx > = 0 ; PrimIdx - - )
{
const FPrimData & PrimData = SelectedPrims [ PrimIdx ] ;
2014-09-09 12:34:40 -04:00
2015-01-06 14:54:09 -05:00
check ( IsPrimValid ( PrimData ) ) ;
switch ( PrimData . PrimType )
{
case KPT_Sphere :
AggGeom - > SphereElems . RemoveAt ( PrimData . PrimIndex ) ;
break ;
case KPT_Box :
AggGeom - > BoxElems . RemoveAt ( PrimData . PrimIndex ) ;
break ;
case KPT_Sphyl :
AggGeom - > SphylElems . RemoveAt ( PrimData . PrimIndex ) ;
break ;
case KPT_Convex :
AggGeom - > ConvexElems . RemoveAt ( PrimData . PrimIndex ) ;
break ;
}
2014-06-13 05:03:24 -04:00
}
2015-01-06 14:54:09 -05:00
GEditor - > EndTransaction ( ) ;
ClearSelectedPrims ( ) ;
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands ( ) ;
// Make sure to invalidate cooked data
StaticMesh - > BodySetup - > InvalidatePhysicsData ( ) ;
// refresh collision change back to staticmesh components
RefreshCollisionChange ( StaticMesh ) ;
// Mark staticmesh as dirty, to help make sure it gets saved.
StaticMesh - > MarkPackageDirty ( ) ;
// Update views/property windows
Viewport - > RefreshViewport ( ) ;
StaticMesh - > bCustomizedCollision = true ; //mark the static mesh for collision customization
2014-06-13 05:03:24 -04:00
}
}
void FStaticMeshEditor : : DuplicateSelected ( )
{
DuplicateSelectedSocket ( ) ;
const FVector InitialOffset ( 20.f ) ;
DuplicateSelectedPrims ( & InitialOffset ) ;
}
bool FStaticMeshEditor : : CanDuplicateSelected ( ) const
{
return ( GetSelectedSocket ( ) ! = NULL | | HasSelectedPrims ( ) ) ;
}
bool FStaticMeshEditor : : CanRenameSelected ( ) const
{
return ( GetSelectedSocket ( ) ! = NULL ) ;
2014-03-14 14:13:41 -04:00
}
void FStaticMeshEditor : : ExecuteFindInExplorer ( )
{
if ( ensure ( StaticMesh - > AssetImportData ) )
{
const FString SourceFilePath = FReimportManager : : ResolveImportFilename ( StaticMesh - > AssetImportData - > SourceFilePath , StaticMesh ) ;
if ( SourceFilePath . Len ( ) & & IFileManager : : Get ( ) . FileSize ( * SourceFilePath ) ! = INDEX_NONE )
{
FPlatformProcess : : ExploreFolder ( * FPaths : : GetPath ( SourceFilePath ) ) ;
}
}
}
bool FStaticMeshEditor : : CanExecuteSourceCommands ( ) const
{
if ( ! StaticMesh - > AssetImportData )
{
return false ;
}
const FString & SourceFilePath = FReimportManager : : ResolveImportFilename ( StaticMesh - > AssetImportData - > SourceFilePath , StaticMesh ) ;
return SourceFilePath . Len ( ) & & IFileManager : : Get ( ) . FileSize ( * SourceFilePath ) ! = INDEX_NONE ;
}
void FStaticMeshEditor : : OnObjectReimported ( UObject * InObject )
{
// Make sure we are using the object that is being reimported, otherwise a lot of needless work could occur.
if ( StaticMesh = = InObject )
{
SetEditorMesh ( Cast < UStaticMesh > ( InObject ) ) ;
}
}
void FStaticMeshEditor : : OnConvexDecomposition ( )
{
TabManager - > InvokeTab ( CollisionTabId ) ;
}
bool FStaticMeshEditor : : OnRequestClose ( )
{
bool bAllowClose = true ;
if ( StaticMeshDetails . IsValid ( ) & & StaticMeshDetails . Pin ( ) - > IsApplyNeeded ( ) )
{
// find out the user wants to do with this dirty material
EAppReturnType : : Type YesNoCancelReply = FMessageDialog : : Open (
EAppMsgType : : YesNoCancel ,
FText : : Format ( LOCTEXT ( " ShouldApplyLODChanges " , " Would you like to apply level of detail changes to {0}? \n \n (No will lose all changes!) " ) , FText : : FromString ( StaticMesh - > GetName ( ) ) )
) ;
switch ( YesNoCancelReply )
{
case EAppReturnType : : Yes :
StaticMeshDetails . Pin ( ) - > ApplyChanges ( ) ;
bAllowClose = true ;
break ;
case EAppReturnType : : No :
// Do nothing, changes will be abandoned.
bAllowClose = true ;
break ;
case EAppReturnType : : Cancel :
// Don't exit.
bAllowClose = false ;
break ;
}
}
return bAllowClose ;
}
void FStaticMeshEditor : : RegisterOnPostUndo ( const FOnPostUndo & Delegate )
{
OnPostUndo . Add ( Delegate ) ;
}
void FStaticMeshEditor : : UnregisterOnPostUndo ( SWidget * Widget )
{
OnPostUndo . RemoveAll ( Widget ) ;
}
void FStaticMeshEditor : : NotifyPostChange ( const FPropertyChangedEvent & PropertyChangedEvent , UProperty * PropertyThatChanged )
{
if ( StaticMesh & & StaticMesh - > BodySetup )
{
StaticMesh - > BodySetup - > CreatePhysicsMeshes ( ) ;
}
}
void FStaticMeshEditor : : UndoAction ( )
{
GEditor - > UndoTransaction ( ) ;
}
void FStaticMeshEditor : : RedoAction ( )
{
GEditor - > RedoTransaction ( ) ;
}
void FStaticMeshEditor : : PostUndo ( bool bSuccess )
{
2014-06-13 05:03:24 -04:00
RemoveInvalidPrims ( ) ;
2014-03-14 14:13:41 -04:00
OnPostUndo . Broadcast ( ) ;
}
void FStaticMeshEditor : : PostRedo ( bool bSuccess )
{
2014-06-13 05:03:24 -04:00
RemoveInvalidPrims ( ) ;
2014-03-14 14:13:41 -04:00
OnPostUndo . Broadcast ( ) ;
}
void FStaticMeshEditor : : OnSocketSelectionChanged ( )
{
UStaticMeshSocket * SelectedSocket = GetSelectedSocket ( ) ;
2014-06-13 05:03:24 -04:00
if ( SelectedSocket )
{
ClearSelectedPrims ( ) ;
}
2014-03-14 14:13:41 -04:00
Viewport - > GetViewportClient ( ) . OnSocketSelectionChanged ( SelectedSocket ) ;
}
void FStaticMeshEditor : : OnPostReimport ( UObject * InObject , bool bSuccess )
{
// Ignore if this is regarding a different object
if ( InObject ! = StaticMesh )
{
return ;
}
2014-07-24 04:28:41 -04:00
if ( bSuccess )
{
RefreshTool ( ) ;
}
2014-03-14 14:13:41 -04:00
}
# undef LOCTEXT_NAMESPACE