Files
FLESH/Source/FLESHEditor/Private/DismembermentEditor.cpp
2025-04-21 20:25:53 +08:00

1250 lines
49 KiB
C++

#include "DismembermentEditor.h"
#include "Widgets/Docking/SDockTab.h"
#include "PropertyEditorModule.h"
#include "Modules/ModuleManager.h"
#include "IDetailsView.h"
#include "Widgets/Layout/SBorder.h"
#include "Styling/AppStyle.h"
#include "Engine/SkeletalMesh.h"
#include "Framework/Commands/UICommandList.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ToolMenus.h"
#include "Logging/LogMacros.h"
#include "Misc/MessageDialog.h"
// Define log category
DEFINE_LOG_CATEGORY_STATIC(LogDismembermentEditor, Log, All);
// Define tab names
const FName FDismembermentEditor::ViewportTabId(TEXT("DismembermentEditor_Viewport"));
const FName FDismembermentEditor::DetailsTabId(TEXT("DismembermentEditor_Details"));
const FName FDismembermentEditor::LayerSystemTabId(TEXT("DismembermentEditor_LayerSystem"));
const FName FDismembermentEditor::PhysicsSettingsTabId(TEXT("DismembermentEditor_PhysicsSettings"));
const FName FDismembermentEditor::NodeTreeTabId(TEXT("DismembermentEditor_NodeTree"));
// Constructor
FDismembermentEditor::FDismembermentEditor()
: SkeletalMesh(nullptr)
, BooleanCutTool(nullptr)
, LayerSystem(nullptr)
, SelectedBoneName(NAME_None)
, bCreateCapMesh(true)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("DismembermentEditor constructor called"));
// Create boolean cut tool
BooleanCutTool = NewObject<UBooleanCutTool>();
if (BooleanCutTool)
{
// Initialize default settings
BooleanCutTool->SetCapMeshMethod(ECapMeshMethod::Simple);
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut tool created successfully"));
}
else
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to create Boolean cut tool"));
}
// Create anatomical layer system
LayerSystem = NewObject<UAnatomicalLayerSystem>();
if (LayerSystem)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Anatomical layer system created successfully"));
}
else
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to create Anatomical layer system"));
}
// Initialize default cut plane
CurrentCutPlane.Location = FVector(0, 0, 0);
CurrentCutPlane.Normal = FVector(0, 0, 1); // Default to Z-up
}
// Destructor
FDismembermentEditor::~FDismembermentEditor()
{
// Unregister from any events
if (GEditor)
{
GEditor->UnregisterForUndo(this);
}
UE_LOG(LogDismembermentEditor, Log, TEXT("DismembermentEditor destructor called"));
}
// Initialize the editor
void FDismembermentEditor::InitDismembermentEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeletalMesh* InSkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Initializing Dismemberment Editor"));
try
{
// Set the skeletal mesh being edited
SkeletalMesh = InSkeletalMesh;
if (!SkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to initialize Dismemberment Editor: Skeletal mesh is null"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to initialize Dismemberment Editor: Skeletal mesh is null"));
return;
}
// Initialize the layer system with the target mesh
if (LayerSystem)
{
// We can't directly set TargetMesh, as UAnatomicalLayerSystem doesn't have this member
// But we can store a reference to the skeletal mesh here
UE_LOG(LogDismembermentEditor, Log, TEXT("Layer system initialized, target mesh: %s"), *SkeletalMesh->GetName());
}
// Create the layout
CreateEditorLayout();
// Create the toolbar
CreateEditorToolbar();
// Initialize the asset editor
FAssetEditorToolkit::InitAssetEditor(
Mode,
InitToolkitHost,
FName("DismembermentEditorApp"),
FTabManager::FLayout::NullLayout,
/*bCreateDefaultStandaloneMenu=*/ true,
/*bCreateDefaultToolbar=*/ true,
InSkeletalMesh
);
// Register for undo/redo
GEditor->RegisterForUndo(this);
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment Editor initialized successfully"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception during Dismemberment Editor initialization: %s"), UTF8_TO_TCHAR(e.what()));
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception during Dismemberment Editor initialization: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception during Dismemberment Editor initialization"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception during Dismemberment Editor initialization"));
}
}
// Get the toolkit name
FName FDismembermentEditor::GetToolkitFName() const
{
return FName("DismembermentEditor");
}
// Get the base toolkit name
FText FDismembermentEditor::GetBaseToolkitName() const
{
return FText::FromString("Dismemberment Editor");
}
// Get the world centric tab prefix
FString FDismembermentEditor::GetWorldCentricTabPrefix() const
{
return "DismembermentEditor";
}
// Get the world centric tab color scale
FLinearColor FDismembermentEditor::GetWorldCentricTabColorScale() const
{
return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f);
}
// Post undo handler
void FDismembermentEditor::PostUndo(bool bSuccess)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("PostUndo called, success: %d"), bSuccess);
// Refresh the views
if (bSuccess)
{
if (DetailsWidget.IsValid())
{
DetailsWidget->ForceRefresh();
}
// Refresh other widgets as needed
if (ViewportWidget.IsValid())
{
// Refresh viewport if needed
}
if (LayerSystemWidget.IsValid())
{
// Refresh layer system widget if needed
}
if (PhysicsSettingsWidget.IsValid())
{
// Refresh physics settings widget if needed
}
}
}
// Post redo handler
void FDismembermentEditor::PostRedo(bool bSuccess)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("PostRedo called, success: %d"), bSuccess);
// Refresh the views
if (bSuccess)
{
if (DetailsWidget.IsValid())
{
DetailsWidget->ForceRefresh();
}
// Refresh other widgets as needed
if (ViewportWidget.IsValid())
{
// Refresh viewport if needed
}
if (LayerSystemWidget.IsValid())
{
// Refresh layer system widget if needed
}
if (PhysicsSettingsWidget.IsValid())
{
// Refresh physics settings widget if needed
}
}
}
// Create the editor layout
void FDismembermentEditor::CreateEditorLayout()
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Creating editor layout"));
try
{
// Create the layout
TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_DismembermentEditor_Layout_v1")
->AddArea
(
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.2f)
->AddTab(NodeTreeTabId, ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.6f)
->AddTab(ViewportTabId, ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.2f)
->AddTab(DetailsTabId, ETabState::OpenedTab)
->AddTab(LayerSystemTabId, ETabState::OpenedTab)
->AddTab(PhysicsSettingsTabId, ETabState::OpenedTab)
)
)
);
// Set the layout
FAssetEditorToolkit::InitAssetEditor(
EToolkitMode::Standalone,
TSharedPtr<IToolkitHost>(),
FName("DismembermentEditorApp"),
StandaloneDefaultLayout,
true,
true,
SkeletalMesh
);
// Register tab spawners
RegisterTabSpawners(GetTabManager().ToSharedRef());
UE_LOG(LogDismembermentEditor, Log, TEXT("Editor layout created successfully"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception creating editor layout: %s"), UTF8_TO_TCHAR(e.what()));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception creating editor layout"));
}
}
// Register tab spawners
void FDismembermentEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Registering tab spawners"));
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
try
{
InTabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_Viewport))
.SetDisplayName(FText::FromString("Viewport"))
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Viewports"));
InTabManager->RegisterTabSpawner(NodeTreeTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_NodeTree))
.SetDisplayName(FText::FromString("Node Tree"))
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.BlueprintCore"));
InTabManager->RegisterTabSpawner(DetailsTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_Details))
.SetDisplayName(FText::FromString("Details"))
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details"));
InTabManager->RegisterTabSpawner(LayerSystemTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_LayerSystem))
.SetDisplayName(FText::FromString("Anatomical Layer System"))
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.Layer"));
InTabManager->RegisterTabSpawner(PhysicsSettingsTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_PhysicsSettings))
.SetDisplayName(FText::FromString("Physics Settings"))
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.PhysicsAsset"));
UE_LOG(LogDismembermentEditor, Log, TEXT("Tab spawners registered successfully"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception registering tab spawners: %s"), UTF8_TO_TCHAR(e.what()));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception registering tab spawners"));
}
}
// Unregister tab spawners
void FDismembermentEditor::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Unregistering tab spawners"));
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
InTabManager->UnregisterTabSpawner(ViewportTabId);
InTabManager->UnregisterTabSpawner(NodeTreeTabId);
InTabManager->UnregisterTabSpawner(DetailsTabId);
InTabManager->UnregisterTabSpawner(LayerSystemTabId);
InTabManager->UnregisterTabSpawner(PhysicsSettingsTabId);
}
// Spawn the viewport tab
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_Viewport(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.Label(FText::FromString("FLESH Viewport"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(STextBlock)
.Text(FText::FromString("Viewport implementation will be added here"))
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(FText::FromString("Boolean Cut"))
.ToolTipText(FText::FromString("Perform Boolean Cut Operation"))
.OnClicked_Lambda([this]() {
PerformBooleanCut();
return FReply::Handled();
})
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(FText::FromString("Preview"))
.ToolTipText(FText::FromString("Preview Dismemberment Effects"))
.OnClicked_Lambda([this]() {
PreviewEffects();
return FReply::Handled();
})
]
]
];
}
// Spawn the node tree tab
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_NodeTree(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.Label(FText::FromString("Node Tree"))
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Node Tree"))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.DarkGroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(STextBlock)
.Text(FText::FromString("Node tree implementation will be added here"))
]
]
]
];
}
// Spawn the details tab
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_Details(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.Label(FText::FromString("Details"))
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Details"))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SExpandableArea)
.HeaderContent()
[
SNew(STextBlock)
.Text(FText::FromString("Display Settings"))
]
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Draw when Selected"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Checked)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Draw when Parent Set"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Checked)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Draw when Children Set"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Checked)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Draw Always"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Unchecked)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SExpandableArea)
.HeaderContent()
[
SNew(STextBlock)
.Text(FText::FromString("Editor Node"))
]
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Enable Simulation"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Checked)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SExpandableArea)
.HeaderContent()
[
SNew(STextBlock)
.Text(FText::FromString("Skinning Names"))
]
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(STextBlock)
.Text(FText::FromString("Skinning names will be added here"))
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SExpandableArea)
.HeaderContent()
[
SNew(STextBlock)
.Text(FText::FromString("Debug"))
]
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Debug Fixed Movement"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SCheckBox)
.IsChecked(ECheckBoxState::Unchecked)
]
]
]
]
]
];
}
// Spawn the layer system tab
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_LayerSystem(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.Label(FText::FromString("Anatomical Layer System"))
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Anatomical Layer System"))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Configure anatomical layers for the mesh"))
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.DarkGroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(STextBlock)
.Text(FText::FromString("Layer System implementation will be added here"))
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(FText::FromString("Add Layer"))
.OnClicked_Lambda([this]() {
AddNewLayer();
return FReply::Handled();
})
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(FText::FromString("Remove Layer"))
.OnClicked_Lambda([this]() {
// TODO: Implement RemoveLayer
return FReply::Handled();
})
]
]
]
];
}
// Spawn the physics settings tab
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_PhysicsSettings(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.Label(FText::FromString("Physics Settings"))
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Physics Settings"))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Configure physics properties for dismemberment"))
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.DarkGroupBorder"))
.Padding(FMargin(4.0f))
[
SNew(STextBlock)
.Text(FText::FromString("Physics Settings implementation will be added here"))
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4, 0, 0)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(FText::FromString("Apply Physics"))
.OnClicked_Lambda([this]() {
// TODO: Implement ApplyPhysics
return FReply::Handled();
})
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(FText::FromString("Reset Physics"))
.OnClicked_Lambda([this]() {
// TODO: Implement ResetPhysics
return FReply::Handled();
})
]
]
]
];
}
// Create the editor toolbar
void FDismembermentEditor::CreateEditorToolbar()
{
// Create command list
TSharedRef<FUICommandList> CommandList = MakeShareable(new FUICommandList);
// Get the toolbar builder
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
ToolbarExtender->AddToolBarExtension(
"Asset",
EExtensionHook::After,
CommandList,
FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ToolbarBuilder)
{
ToolbarBuilder.BeginSection("Dismemberment");
{
// Save button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::SaveEdits)),
NAME_None,
FText::FromString("Save"),
FText::FromString("Save Dismemberment Settings"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "AssetEditor.SaveAsset"),
EUserInterfaceActionType::Button
);
// Browse button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Browse functionality
})),
NAME_None,
FText::FromString("Browse"),
FText::FromString("Browse Assets"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "ContentBrowser.AssetActions.Browse"),
EUserInterfaceActionType::Button
);
// Hide Mesh button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Hide Mesh functionality
})),
NAME_None,
FText::FromString("Hide Mesh"),
FText::FromString("Toggle Mesh Visibility"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.ToggleRealTime"),
EUserInterfaceActionType::Button
);
// Preview Animation button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Preview Animation functionality
})),
NAME_None,
FText::FromString("Preview Animation"),
FText::FromString("Preview Animation"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Persona.PreviewMesh"),
EUserInterfaceActionType::Button
);
// Play button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Play functionality
})),
NAME_None,
FText::FromString("Play"),
FText::FromString("Play Animation"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "MediaAsset.AssetActions.Play"),
EUserInterfaceActionType::Button
);
// Pause button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Pause functionality
})),
NAME_None,
FText::FromString("Pause"),
FText::FromString("Pause Animation"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "MediaAsset.AssetActions.Pause"),
EUserInterfaceActionType::Button
);
// Recompile button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Recompile functionality
})),
NAME_None,
FText::FromString("Recompile"),
FText::FromString("Recompile Dismemberment System"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "AssetEditor.ReimportAsset"),
EUserInterfaceActionType::Button
);
// Draw Flags button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Draw Flags functionality
})),
NAME_None,
FText::FromString("Draw Flags"),
FText::FromString("Toggle Draw Flags"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.VisualizeBufferVisualizationMenu"),
EUserInterfaceActionType::Button
);
// Selection Flags button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Selection Flags functionality
})),
NAME_None,
FText::FromString("Selection Flags"),
FText::FromString("Toggle Selection Flags"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.VisualizeNaniteVisualizationMenu"),
EUserInterfaceActionType::Button
);
// Draw Skinning button
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateLambda([this]() {
// TODO: Implement Draw Skinning functionality
})),
NAME_None,
FText::FromString("Draw Skinning"),
FText::FromString("Toggle Skinning Visualization"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.VisualizeHLODColoration"),
EUserInterfaceActionType::Button
);
}
ToolbarBuilder.EndSection();
})
);
// Add the toolbar extender
AddToolbarExtender(ToolbarExtender);
}
// Perform a boolean cut operation
void FDismembermentEditor::PerformBooleanCut()
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Performing boolean cut operation"));
try
{
if (!SkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot perform boolean cut: No skeletal mesh selected"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot perform boolean cut: No skeletal mesh selected"));
return;
}
if (!BooleanCutTool)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot perform boolean cut: Boolean cut tool not initialized"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot perform boolean cut: Boolean cut tool not initialized"));
return;
}
// Perform the actual boolean cut operation
UE_LOG(LogDismembermentEditor, Log, TEXT("Using boolean cut tool to perform cut operation, Bone: %s, Create Cap: %s"),
*SelectedBoneName.ToString(), bCreateCapMesh ? TEXT("Yes") : TEXT("No"));
// If a bone is selected, use bone-guided cutting
TArray<USkeletalMesh*> ResultMeshes;
if (SelectedBoneName != NAME_None)
{
ResultMeshes = BooleanCutTool->CutWithBoneAxis(SkeletalMesh, SelectedBoneName, CurrentCutPlane, bCreateCapMesh);
}
else
{
// Otherwise use regular skeletal mesh cutting
ResultMeshes = BooleanCutTool->CutSkeletalMesh(SkeletalMesh, CurrentCutPlane, NAME_None, bCreateCapMesh);
}
// Check results
if (ResultMeshes.Num() > 0)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut completed successfully, generated %d meshes"), ResultMeshes.Num());
// Show success message
FText SuccessMessage = FText::Format(
FText::FromString("Boolean cut completed, generated {0} meshes"),
FText::AsNumber(ResultMeshes.Num())
);
FMessageDialog::Open(EAppMsgType::Ok, SuccessMessage);
// TODO: Display result meshes in viewport
}
else
{
UE_LOG(LogDismembermentEditor, Warning, TEXT("Boolean cut operation did not generate any meshes"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Boolean cut operation did not generate any meshes"));
}
UE_LOG(LogDismembermentEditor, Log, TEXT("Boolean cut operation completed"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception during boolean cut operation: %s"), UTF8_TO_TCHAR(e.what()));
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception during boolean cut operation: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception during boolean cut operation"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception during boolean cut operation"));
}
}
// Add a new layer
void FDismembermentEditor::AddNewLayer()
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Adding new anatomical layer"));
try
{
if (!SkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot add new layer: No skeletal mesh selected"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot add new layer: No skeletal mesh selected"));
return;
}
if (!LayerSystem)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot add new layer: Layer system not initialized"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot add new layer: Layer system not initialized"));
return;
}
// Show a dialog to get the layer type and name
TSharedRef<SWindow> Window = SNew(SWindow)
.Title(FText::FromString("Add New Anatomical Layer"))
.SizingRule(ESizingRule::Autosized)
.SupportsMaximize(false)
.SupportsMinimize(false);
TSharedPtr<SComboBox<TSharedPtr<EAnatomicalLayerType>>> LayerTypeComboBox;
TSharedPtr<SEditableTextBox> LayerNameTextBox;
// Create array of layer types
TArray<TSharedPtr<EAnatomicalLayerType>> LayerTypes;
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Skin)));
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Muscle)));
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Bone)));
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Organ)));
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Blood)));
LayerTypes.Add(MakeShareable(new EAnatomicalLayerType(EAnatomicalLayerType::Custom)));
// Selected layer type
TSharedPtr<EAnatomicalLayerType> SelectedLayerType = LayerTypes[0];
// Layer name
FString NewLayerName = TEXT("New Layer");
// Dialog result
bool bDialogResult = false;
Window->SetContent(
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(8.0f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Layer Type:"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 8)
[
SAssignNew(LayerTypeComboBox, SComboBox<TSharedPtr<EAnatomicalLayerType>>)
.OptionsSource(&LayerTypes)
.InitiallySelectedItem(SelectedLayerType)
.OnSelectionChanged_Lambda([&SelectedLayerType](TSharedPtr<EAnatomicalLayerType> NewValue, ESelectInfo::Type SelectType) {
SelectedLayerType = NewValue;
})
.OnGenerateWidget_Lambda([](TSharedPtr<EAnatomicalLayerType> InLayerType) {
FString TypeName;
switch (*InLayerType)
{
case EAnatomicalLayerType::Skin: TypeName = TEXT("Skin"); break;
case EAnatomicalLayerType::Muscle: TypeName = TEXT("Muscle"); break;
case EAnatomicalLayerType::Bone: TypeName = TEXT("Bone"); break;
case EAnatomicalLayerType::Organ: TypeName = TEXT("Organ"); break;
case EAnatomicalLayerType::Blood: TypeName = TEXT("Blood"); break;
case EAnatomicalLayerType::Custom: TypeName = TEXT("Custom"); break;
default: TypeName = TEXT("Unknown"); break;
}
return SNew(STextBlock).Text(FText::FromString(TypeName));
})
[
SNew(STextBlock)
.Text_Lambda([&SelectedLayerType]() {
FString TypeName;
switch (*SelectedLayerType)
{
case EAnatomicalLayerType::Skin: TypeName = TEXT("Skin"); break;
case EAnatomicalLayerType::Muscle: TypeName = TEXT("Muscle"); break;
case EAnatomicalLayerType::Bone: TypeName = TEXT("Bone"); break;
case EAnatomicalLayerType::Organ: TypeName = TEXT("Organ"); break;
case EAnatomicalLayerType::Blood: TypeName = TEXT("Blood"); break;
case EAnatomicalLayerType::Custom: TypeName = TEXT("Custom"); break;
default: TypeName = TEXT("Unknown"); break;
}
return FText::FromString(TypeName);
})
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 4)
[
SNew(STextBlock)
.Text(FText::FromString("Layer Name:"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 0, 0, 8)
[
SAssignNew(LayerNameTextBox, SEditableTextBox)
.Text(FText::FromString(NewLayerName))
.OnTextChanged_Lambda([&NewLayerName](const FText& NewText) {
NewLayerName = NewText.ToString();
})
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 8, 0, 0)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpacer)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(FText::FromString("Cancel"))
.OnClicked_Lambda([&Window, &bDialogResult]() {
bDialogResult = false;
Window->RequestDestroyWindow();
return FReply::Handled();
})
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(FText::FromString("Add"))
.OnClicked_Lambda([&Window, &bDialogResult]() {
bDialogResult = true;
Window->RequestDestroyWindow();
return FReply::Handled();
})
]
]
]
);
// Show the dialog
GEditor->EditorAddModalWindow(Window);
// If the user clicked Add
if (bDialogResult && SelectedLayerType.IsValid())
{
// Add the new layer
int32 NewLayerIndex = LayerSystem->AddLayer(FName(*NewLayerName), *SelectedLayerType);
if (NewLayerIndex != INDEX_NONE)
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Added new layer: %s (Type: %d)"), *NewLayerName, (int32)*SelectedLayerType);
// Show success message
FText SuccessMessage = FText::Format(
FText::FromString("Successfully added new layer: {0}"),
FText::FromString(NewLayerName)
);
FMessageDialog::Open(EAppMsgType::Ok, SuccessMessage);
// TODO: Update the UI to show the new layer
}
else
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Failed to add new layer"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to add new layer"));
}
}
UE_LOG(LogDismembermentEditor, Log, TEXT("Add new layer operation completed"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception adding new layer: %s"), UTF8_TO_TCHAR(e.what()));
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while adding new layer: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception adding new layer"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while adding new layer"));
}
}
// Save the edits
void FDismembermentEditor::SaveEdits()
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Saving dismemberment setup"));
try
{
if (!SkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot save edits: No skeletal mesh selected"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot save edits: No skeletal mesh selected"));
return;
}
// TODO: Implement the actual save functionality
// This will involve:
// 1. Saving the current state of the mesh
// 2. Saving the layer system configuration
// 3. Saving the physics settings
// For now, just show a message
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Save functionality will be implemented in a future update"));
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment setup saved"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception saving edits: %s"), UTF8_TO_TCHAR(e.what()));
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while saving edits: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception saving edits"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while saving edits"));
}
}
// Preview the effects
void FDismembermentEditor::PreviewEffects()
{
UE_LOG(LogDismembermentEditor, Log, TEXT("Previewing dismemberment effects"));
try
{
if (!SkeletalMesh)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Cannot preview effects: No skeletal mesh selected"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot preview effects: No skeletal mesh selected"));
return;
}
// TODO: Implement the actual preview functionality
// This will involve:
// 1. Creating a temporary instance of the mesh
// 2. Applying the current dismemberment settings
// 3. Showing the effects in the viewport
// For now, just show a message
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Preview functionality will be implemented in a future update"));
UE_LOG(LogDismembermentEditor, Log, TEXT("Dismemberment effects previewed"));
}
catch (const std::exception& e)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Exception previewing effects: %s"), UTF8_TO_TCHAR(e.what()));
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(FText::FromString("Exception occurred while previewing effects: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
}
catch (...)
{
UE_LOG(LogDismembermentEditor, Error, TEXT("Unknown exception previewing effects"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Unknown exception occurred while previewing effects"));
}
}