Update
This commit is contained in:
67
Source/FLESHEditor/FLESHEditor.Build.cs
Normal file
67
Source/FLESHEditor/FLESHEditor.Build.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class FLESHEditor : ModuleRules
|
||||
{
|
||||
public FLESHEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"FLESH"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"UnrealEd",
|
||||
"PropertyEditor",
|
||||
"EditorStyle",
|
||||
"LevelEditor",
|
||||
"GeometryCore",
|
||||
"GeometryFramework",
|
||||
"GeometryScriptingEditor",
|
||||
"ToolMenus",
|
||||
"GraphEditor",
|
||||
"BlueprintGraph",
|
||||
"KismetWidgets",
|
||||
"ApplicationCore",
|
||||
"InputCore",
|
||||
"AssetTools",
|
||||
"EditorWidgets",
|
||||
"Kismet",
|
||||
"AdvancedPreviewScene",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
367
Source/FLESHEditor/Private/DismembermentEditor.cpp
Normal file
367
Source/FLESHEditor/Private/DismembermentEditor.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
#include "DismembermentEditor.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
#include "PropertyEditorModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "IDetailsView.h"
|
||||
#include "Widgets/Layout/SBorder.h"
|
||||
#include "EditorStyleSet.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Framework/Commands/UICommandList.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
#include "ToolMenus.h"
|
||||
|
||||
// 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"));
|
||||
|
||||
// Constructor
|
||||
FDismembermentEditor::FDismembermentEditor()
|
||||
: SkeletalMesh(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor
|
||||
FDismembermentEditor::~FDismembermentEditor()
|
||||
{
|
||||
}
|
||||
|
||||
// Initialize the editor
|
||||
void FDismembermentEditor::InitDismembermentEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeletalMesh* InSkeletalMesh)
|
||||
{
|
||||
// Set the skeletal mesh being edited
|
||||
SkeletalMesh = InSkeletalMesh;
|
||||
|
||||
// Create the layout
|
||||
CreateEditorLayout();
|
||||
|
||||
// Create the toolbar
|
||||
CreateEditorToolbar();
|
||||
|
||||
// Initialize the asset editor
|
||||
InitAssetEditor(Mode, InitToolkitHost, FName("DismembermentEditorApp"),
|
||||
FTabManager::FLayout::NullLayout,
|
||||
/*bCreateDefaultStandaloneMenu=*/ true,
|
||||
/*bCreateDefaultToolbar=*/ true,
|
||||
InSkeletalMesh);
|
||||
|
||||
// Register for undo/redo
|
||||
GEditor->RegisterForUndo(this);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Refresh the views
|
||||
if (DetailsWidget.IsValid())
|
||||
{
|
||||
DetailsWidget->ForceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
// Post redo handler
|
||||
void FDismembermentEditor::PostRedo(bool bSuccess)
|
||||
{
|
||||
// Refresh the views
|
||||
if (DetailsWidget.IsValid())
|
||||
{
|
||||
DetailsWidget->ForceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
// Create the editor layout
|
||||
void FDismembermentEditor::CreateEditorLayout()
|
||||
{
|
||||
// Register tab spawners
|
||||
TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_DismembermentEditor_Layout_v1")
|
||||
->AddArea
|
||||
(
|
||||
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.1f)
|
||||
->AddTab(GetToolbarTabId(), ETabState::OpenedTab)
|
||||
->SetHideTabWell(true)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.7f)
|
||||
->AddTab(ViewportTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
|
||||
->SetSizeCoefficient(0.3f)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.4f)
|
||||
->AddTab(DetailsTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.3f)
|
||||
->AddTab(LayerSystemTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.3f)
|
||||
->AddTab(PhysicsSettingsTabId, ETabState::OpenedTab)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Set the layout
|
||||
FAssetEditorToolkit::InitAssetEditor(
|
||||
EToolkitMode::Standalone,
|
||||
TSharedPtr<IToolkitHost>(),
|
||||
FName("DismembermentEditorApp"),
|
||||
StandaloneDefaultLayout,
|
||||
true,
|
||||
true,
|
||||
SkeletalMesh
|
||||
);
|
||||
}
|
||||
|
||||
// Create the editor toolbar
|
||||
void FDismembermentEditor::CreateEditorToolbar()
|
||||
{
|
||||
// Create command list
|
||||
TSharedPtr<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");
|
||||
{
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PerformBooleanCut)),
|
||||
NAME_None,
|
||||
FText::FromString("Boolean Cut"),
|
||||
FText::FromString("Perform a boolean cut operation"),
|
||||
FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.CurveBase"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::AddNewLayer)),
|
||||
NAME_None,
|
||||
FText::FromString("Add Layer"),
|
||||
FText::FromString("Add a new anatomical layer"),
|
||||
FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.Layer"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::PreviewEffects)),
|
||||
NAME_None,
|
||||
FText::FromString("Preview"),
|
||||
FText::FromString("Preview the dismemberment effects"),
|
||||
FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.ParticleSystem"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
|
||||
ToolbarBuilder.AddToolBarButton(
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FDismembermentEditor::SaveEdits)),
|
||||
NAME_None,
|
||||
FText::FromString("Save"),
|
||||
FText::FromString("Save the dismemberment setup"),
|
||||
FSlateIcon(FEditorStyle::GetStyleSetName(), "AssetEditor.SaveAsset"),
|
||||
EUserInterfaceActionType::Button
|
||||
);
|
||||
}
|
||||
ToolbarBuilder.EndSection();
|
||||
})
|
||||
);
|
||||
|
||||
// Add the extender
|
||||
AddToolbarExtender(ToolbarExtender);
|
||||
}
|
||||
|
||||
// Register tab spawners
|
||||
void FDismembermentEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
// Call the base implementation
|
||||
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
|
||||
|
||||
// Register the tab spawners
|
||||
InTabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_Viewport))
|
||||
.SetDisplayName(FText::FromString("Viewport"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(DetailsTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_Details))
|
||||
.SetDisplayName(FText::FromString("Details"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(LayerSystemTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_LayerSystem))
|
||||
.SetDisplayName(FText::FromString("Layer System"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.Layer"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(PhysicsSettingsTabId, FOnSpawnTab::CreateSP(this, &FDismembermentEditor::SpawnTab_PhysicsSettings))
|
||||
.SetDisplayName(FText::FromString("Physics Settings"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.PhysicsAsset"));
|
||||
}
|
||||
|
||||
// Unregister tab spawners
|
||||
void FDismembermentEditor::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
// Call the base implementation
|
||||
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
||||
|
||||
// Unregister the tab spawners
|
||||
InTabManager->UnregisterTabSpawner(ViewportTabId);
|
||||
InTabManager->UnregisterTabSpawner(DetailsTabId);
|
||||
InTabManager->UnregisterTabSpawner(LayerSystemTabId);
|
||||
InTabManager->UnregisterTabSpawner(PhysicsSettingsTabId);
|
||||
}
|
||||
|
||||
// Spawn the viewport tab
|
||||
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_Viewport(const FSpawnTabArgs& Args)
|
||||
{
|
||||
// Create the viewport widget
|
||||
ViewportWidget = SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(FMargin(4.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Viewport will be implemented here"))
|
||||
];
|
||||
|
||||
// Create the tab
|
||||
return SNew(SDockTab)
|
||||
.Label(FText::FromString("Viewport"))
|
||||
[
|
||||
ViewportWidget.ToSharedRef()
|
||||
];
|
||||
}
|
||||
|
||||
// Spawn the details tab
|
||||
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_Details(const FSpawnTabArgs& Args)
|
||||
{
|
||||
// Create the details view
|
||||
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
|
||||
FDetailsViewArgs DetailsViewArgs;
|
||||
DetailsViewArgs.bUpdatesFromSelection = true;
|
||||
DetailsViewArgs.bLockable = false;
|
||||
DetailsViewArgs.bAllowSearch = true;
|
||||
DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea;
|
||||
DetailsViewArgs.bHideSelectionTip = true;
|
||||
|
||||
DetailsWidget = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
|
||||
DetailsWidget->SetObject(SkeletalMesh);
|
||||
|
||||
// Create the tab
|
||||
return SNew(SDockTab)
|
||||
.Label(FText::FromString("Details"))
|
||||
[
|
||||
DetailsWidget.ToSharedRef()
|
||||
];
|
||||
}
|
||||
|
||||
// Spawn the layer system tab
|
||||
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_LayerSystem(const FSpawnTabArgs& Args)
|
||||
{
|
||||
// Create the layer system widget
|
||||
LayerSystemWidget = SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(FMargin(4.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Layer System will be implemented here"))
|
||||
];
|
||||
|
||||
// Create the tab
|
||||
return SNew(SDockTab)
|
||||
.Label(FText::FromString("Layer System"))
|
||||
[
|
||||
LayerSystemWidget.ToSharedRef()
|
||||
];
|
||||
}
|
||||
|
||||
// Spawn the physics settings tab
|
||||
TSharedRef<SDockTab> FDismembermentEditor::SpawnTab_PhysicsSettings(const FSpawnTabArgs& Args)
|
||||
{
|
||||
// Create the physics settings widget
|
||||
PhysicsSettingsWidget = SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(FMargin(4.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Physics Settings will be implemented here"))
|
||||
];
|
||||
|
||||
// Create the tab
|
||||
return SNew(SDockTab)
|
||||
.Label(FText::FromString("Physics Settings"))
|
||||
[
|
||||
PhysicsSettingsWidget.ToSharedRef()
|
||||
];
|
||||
}
|
||||
|
||||
// Perform a boolean cut operation
|
||||
void FDismembermentEditor::PerformBooleanCut()
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
// Add a new layer
|
||||
void FDismembermentEditor::AddNewLayer()
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
// Save the edits
|
||||
void FDismembermentEditor::SaveEdits()
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
// Preview the effects
|
||||
void FDismembermentEditor::PreviewEffects()
|
||||
{
|
||||
// To be implemented
|
||||
}
|
61
Source/FLESHEditor/Private/DismembermentEditor.h
Normal file
61
Source/FLESHEditor/Private/DismembermentEditor.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "EditorUndoClient.h"
|
||||
|
||||
class USkeletalMesh;
|
||||
class SDockTab;
|
||||
class IDetailsView;
|
||||
class SBorder;
|
||||
|
||||
/**
|
||||
* Dismemberment System Editor
|
||||
* Provides real-time boolean cutting and multi-layer system editing functionality
|
||||
*/
|
||||
class FDismembermentEditor : public FAssetEditorToolkit, public FEditorUndoClient
|
||||
{
|
||||
public:
|
||||
FDismembermentEditor();
|
||||
virtual ~FDismembermentEditor();
|
||||
|
||||
void InitDismembermentEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeletalMesh* InSkeletalMesh);
|
||||
|
||||
virtual FName GetToolkitFName() const override;
|
||||
virtual FText GetBaseToolkitName() const override;
|
||||
virtual FString GetWorldCentricTabPrefix() const override;
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
|
||||
virtual void PostUndo(bool bSuccess) override;
|
||||
virtual void PostRedo(bool bSuccess) override;
|
||||
|
||||
private:
|
||||
void CreateEditorLayout();
|
||||
void CreateEditorToolbar();
|
||||
|
||||
void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager);
|
||||
void UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager);
|
||||
|
||||
TSharedRef<SDockTab> SpawnTab_Viewport(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_Details(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_LayerSystem(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_PhysicsSettings(const FSpawnTabArgs& Args);
|
||||
|
||||
void PerformBooleanCut();
|
||||
void AddNewLayer();
|
||||
void SaveEdits();
|
||||
void PreviewEffects();
|
||||
|
||||
private:
|
||||
USkeletalMesh* SkeletalMesh;
|
||||
|
||||
TSharedPtr<SBorder> ViewportWidget;
|
||||
TSharedPtr<IDetailsView> DetailsWidget;
|
||||
TSharedPtr<SBorder> LayerSystemWidget;
|
||||
TSharedPtr<SBorder> PhysicsSettingsWidget;
|
||||
|
||||
static const FName ViewportTabId;
|
||||
static const FName DetailsTabId;
|
||||
static const FName LayerSystemTabId;
|
||||
static const FName PhysicsSettingsTabId;
|
||||
};
|
@@ -0,0 +1,22 @@
|
||||
#include "DismembermentGraph/DismembermentGraphEditorFactory.h"
|
||||
#include "DismembermentGraph/DismembermentGraphAsset.h"
|
||||
|
||||
UDismembermentGraphEditorFactory::UDismembermentGraphEditorFactory()
|
||||
{
|
||||
// Factory configuration
|
||||
SupportedClass = UDismembermentGraphAsset::StaticClass();
|
||||
bCreateNew = true;
|
||||
bEditAfterNew = true;
|
||||
}
|
||||
|
||||
UObject* UDismembermentGraphEditorFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
// Create a new dismemberment graph asset
|
||||
UDismembermentGraphAsset* NewAsset = NewObject<UDismembermentGraphAsset>(InParent, InClass, InName, Flags);
|
||||
return NewAsset;
|
||||
}
|
||||
|
||||
bool UDismembermentGraphEditorFactory::ShouldShowInNewMenu() const
|
||||
{
|
||||
return true;
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
#include "DismembermentGraph/DismembermentGraphEditor.h"
|
||||
#include "DismembermentGraph/DismembermentGraphAsset.h"
|
||||
#include "DismembermentGraph/DismembermentGraph.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNode.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeCut.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBoneSelect.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBloodEffect.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "EdGraphUtilities.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "DismembermentGraphEditor"
|
||||
|
||||
/**
|
||||
* Dismemberment graph schema actions factory
|
||||
*/
|
||||
class FDismembermentGraphSchemaActionMenuBuilder : public FGraphSchemaActionMenuBuilder
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
FDismembermentGraphSchemaActionMenuBuilder(const UEdGraphSchema* InSchema)
|
||||
: FGraphSchemaActionMenuBuilder(InSchema)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment graph editor module
|
||||
*/
|
||||
class FDismembermentGraphEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
// IModuleInterface interface
|
||||
virtual void StartupModule() override
|
||||
{
|
||||
// Register asset type actions
|
||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
|
||||
// Register asset category
|
||||
DismembermentGraphAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Dismemberment")), LOCTEXT("DismembermentGraphAssetCategory", "Dismemberment"));
|
||||
|
||||
// Register asset type actions
|
||||
TSharedPtr<IAssetTypeActions> Action = MakeShareable(new FDismembermentGraphAssetTypeActions(DismembermentGraphAssetCategoryBit));
|
||||
AssetTools.RegisterAssetTypeActions(Action);
|
||||
RegisteredAssetTypeActions.Add(Action);
|
||||
|
||||
// Register graph editor factory
|
||||
TSharedPtr<FGraphPanelNodeFactory> NodeFactory = MakeShareable(new FDismembermentGraphNodeFactory);
|
||||
FEdGraphUtilities::RegisterVisualNodeFactory(NodeFactory);
|
||||
|
||||
// Register node factories
|
||||
RegisterNodeFactory(UDismembermentGraphNodeCut::StaticClass(), LOCTEXT("CutNode", "Cut Node"), LOCTEXT("CutNodeTooltip", "Performs a cut operation"));
|
||||
RegisterNodeFactory(UDismembermentGraphNodeBoneSelect::StaticClass(), LOCTEXT("BoneSelectNode", "Bone Select Node"), LOCTEXT("BoneSelectNodeTooltip", "Selects bones for operations"));
|
||||
RegisterNodeFactory(UDismembermentGraphNodeBloodEffect::StaticClass(), LOCTEXT("BloodEffectNode", "Blood Effect Node"), LOCTEXT("BloodEffectNodeTooltip", "Creates blood effects"));
|
||||
}
|
||||
|
||||
virtual void ShutdownModule() override
|
||||
{
|
||||
// Unregister asset type actions
|
||||
if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
|
||||
{
|
||||
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
for (TSharedPtr<IAssetTypeActions> Action : RegisteredAssetTypeActions)
|
||||
{
|
||||
AssetTools.UnregisterAssetTypeActions(Action.ToSharedRef());
|
||||
}
|
||||
}
|
||||
|
||||
RegisteredAssetTypeActions.Empty();
|
||||
|
||||
// Unregister node factories
|
||||
for (const auto& Factory : RegisteredNodeFactories)
|
||||
{
|
||||
FEdGraphUtilities::UnregisterVisualNodeFactory(Factory);
|
||||
}
|
||||
|
||||
RegisteredNodeFactories.Empty();
|
||||
}
|
||||
// End of IModuleInterface interface
|
||||
|
||||
private:
|
||||
// Asset category bit
|
||||
EAssetTypeCategories::Type DismembermentGraphAssetCategoryBit;
|
||||
|
||||
// Registered asset type actions
|
||||
TArray<TSharedPtr<IAssetTypeActions>> RegisteredAssetTypeActions;
|
||||
|
||||
// Registered node factories
|
||||
TArray<TSharedPtr<FGraphPanelNodeFactory>> RegisteredNodeFactories;
|
||||
|
||||
// Register a node factory
|
||||
void RegisterNodeFactory(UClass* NodeClass, const FText& DisplayName, const FText& Tooltip)
|
||||
{
|
||||
TSharedPtr<FDismembermentGraphNodeFactory> NodeFactory = MakeShareable(new FDismembermentGraphNodeFactory(NodeClass, DisplayName, Tooltip));
|
||||
FEdGraphUtilities::RegisterVisualNodeFactory(NodeFactory);
|
||||
RegisteredNodeFactories.Add(NodeFactory);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment graph asset type actions
|
||||
*/
|
||||
class FDismembermentGraphAssetTypeActions : public FAssetTypeActions_Base
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
FDismembermentGraphAssetTypeActions(EAssetTypeCategories::Type InAssetCategory)
|
||||
: AssetCategory(InAssetCategory)
|
||||
{
|
||||
}
|
||||
|
||||
// FAssetTypeActions_Base interface
|
||||
virtual FText GetName() const override { return LOCTEXT("DismembermentGraphAssetName", "Dismemberment Graph"); }
|
||||
virtual FColor GetTypeColor() const override { return FColor(255, 64, 64); }
|
||||
virtual UClass* GetSupportedClass() const override { return UDismembermentGraphAsset::StaticClass(); }
|
||||
virtual uint32 GetCategories() override { return AssetCategory; }
|
||||
virtual bool HasActions(const TArray<UObject*>& InObjects) const override { return false; }
|
||||
virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor) override
|
||||
{
|
||||
for (UObject* Object : InObjects)
|
||||
{
|
||||
if (UDismembermentGraphAsset* GraphAsset = Cast<UDismembermentGraphAsset>(Object))
|
||||
{
|
||||
TSharedRef<FDismembermentGraphEditor> NewEditor = MakeShareable(new FDismembermentGraphEditor());
|
||||
NewEditor->InitDismembermentGraphEditor(EToolkitMode::Standalone, EditWithinLevelEditor, GraphAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of FAssetTypeActions_Base interface
|
||||
|
||||
private:
|
||||
// Asset category
|
||||
EAssetTypeCategories::Type AssetCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment graph node factory
|
||||
*/
|
||||
class FDismembermentGraphNodeFactory : public FGraphPanelNodeFactory
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
FDismembermentGraphNodeFactory()
|
||||
: NodeClass(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
FDismembermentGraphNodeFactory(UClass* InNodeClass, const FText& InDisplayName, const FText& InTooltip)
|
||||
: NodeClass(InNodeClass)
|
||||
, DisplayName(InDisplayName)
|
||||
, Tooltip(InTooltip)
|
||||
{
|
||||
}
|
||||
|
||||
// FGraphPanelNodeFactory interface
|
||||
virtual TSharedPtr<SGraphNode> CreateNode(UEdGraphNode* Node) const override
|
||||
{
|
||||
if (NodeClass && Node->IsA(NodeClass))
|
||||
{
|
||||
return SNew(SDismembermentGraphNode, Node);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
// End of FGraphPanelNodeFactory interface
|
||||
|
||||
private:
|
||||
// Node class
|
||||
UClass* NodeClass;
|
||||
|
||||
// Display name
|
||||
FText DisplayName;
|
||||
|
||||
// Tooltip
|
||||
FText Tooltip;
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE(FDismembermentGraphEditorModule, FLESHEditor)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@@ -0,0 +1,459 @@
|
||||
#include "DismembermentGraph/DismembermentGraphSchema.h"
|
||||
#include "DismembermentGraph/DismembermentGraph.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNode.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeCut.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBoneSelect.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBloodEffect.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodePhysics.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeOrgan.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeWound.h"
|
||||
#include "EdGraphNode_Comment.h"
|
||||
#include "Framework/Commands/GenericCommands.h"
|
||||
#include "GraphEditorActions.h"
|
||||
#include "ToolMenus.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "DismembermentGraphSchema"
|
||||
|
||||
// Pin categories
|
||||
const FName UDismembermentGraphSchema::PC_Exec("Exec");
|
||||
const FName UDismembermentGraphSchema::PC_Bone("Bone");
|
||||
const FName UDismembermentGraphSchema::PC_Cut("Cut");
|
||||
const FName UDismembermentGraphSchema::PC_Blood("Blood");
|
||||
const FName UDismembermentGraphSchema::PC_Physics("Physics");
|
||||
const FName UDismembermentGraphSchema::PC_Organ("Organ");
|
||||
const FName UDismembermentGraphSchema::PC_Wound("Wound");
|
||||
|
||||
void UDismembermentGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const
|
||||
{
|
||||
// Add node actions
|
||||
const FText ToolTip = LOCTEXT("NewDismembermentNodeTooltip", "Add node here");
|
||||
const FText MenuDesc = LOCTEXT("NewDismembermentNodeMenuDesc", "Add Node");
|
||||
|
||||
TArray<TSubclassOf<UDismembermentGraphNode>> NodeClasses;
|
||||
NodeClasses.Add(UDismembermentGraphNodeCut::StaticClass());
|
||||
NodeClasses.Add(UDismembermentGraphNodeBoneSelect::StaticClass());
|
||||
NodeClasses.Add(UDismembermentGraphNodeBloodEffect::StaticClass());
|
||||
NodeClasses.Add(UDismembermentGraphNodePhysics::StaticClass());
|
||||
NodeClasses.Add(UDismembermentGraphNodeOrgan::StaticClass());
|
||||
NodeClasses.Add(UDismembermentGraphNodeWound::StaticClass());
|
||||
|
||||
for (TSubclassOf<UDismembermentGraphNode> NodeClass : NodeClasses)
|
||||
{
|
||||
UDismembermentGraphNode* NodeTemplate = NodeClass->GetDefaultObject<UDismembermentGraphNode>();
|
||||
FText NodeCategory = NodeTemplate->NodeCategory;
|
||||
FText NodeTitle = NodeTemplate->GetNodeTitle(ENodeTitleType::ListView);
|
||||
FText NodeTooltip = NodeTemplate->GetTooltipText();
|
||||
|
||||
TSharedPtr<FDismembermentSchemaAction_NewNode> NewNodeAction(new FDismembermentSchemaAction_NewNode(
|
||||
NodeCategory,
|
||||
NodeTitle,
|
||||
NodeTooltip,
|
||||
0));
|
||||
|
||||
NewNodeAction->NodeClass = NodeClass;
|
||||
ContextMenuBuilder.AddAction(NewNodeAction);
|
||||
}
|
||||
|
||||
// Add comment node
|
||||
TSharedPtr<FDismembermentSchemaAction_NewNode> NewCommentAction(new FDismembermentSchemaAction_NewNode(
|
||||
FText::GetEmpty(),
|
||||
LOCTEXT("NewComment", "Comment"),
|
||||
LOCTEXT("NewCommentTooltip", "Add a comment node"),
|
||||
0));
|
||||
|
||||
NewCommentAction->NodeClass = UEdGraphNode_Comment::StaticClass();
|
||||
ContextMenuBuilder.AddAction(NewCommentAction);
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
|
||||
{
|
||||
if (Context && Context->Node)
|
||||
{
|
||||
FToolMenuSection& Section = Menu->AddSection("DismembermentGraphNodeActions", LOCTEXT("NodeActionsMenuHeader", "Node Actions"));
|
||||
Section.AddMenuEntry(FGenericCommands::Get().Delete);
|
||||
Section.AddMenuEntry(FGenericCommands::Get().Cut);
|
||||
Section.AddMenuEntry(FGenericCommands::Get().Copy);
|
||||
Section.AddMenuEntry(FGenericCommands::Get().Duplicate);
|
||||
|
||||
// Add preview action
|
||||
Section.AddMenuEntry(
|
||||
"PreviewNode",
|
||||
LOCTEXT("PreviewNode", "Preview Node"),
|
||||
LOCTEXT("PreviewNodeTooltip", "Preview the effect of this node"),
|
||||
FSlateIcon(),
|
||||
FUIAction(
|
||||
FExecuteAction::CreateLambda([Context]()
|
||||
{
|
||||
if (UDismembermentGraphNode* Node = Cast<UDismembermentGraphNode>(Context->Node))
|
||||
{
|
||||
// TODO: Implement node preview
|
||||
}
|
||||
}),
|
||||
FCanExecuteAction::CreateLambda([Context]()
|
||||
{
|
||||
return Context->Node != nullptr;
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Add general graph actions
|
||||
{
|
||||
FToolMenuSection& Section = Menu->AddSection("DismembermentGraphActions", LOCTEXT("GraphActionsMenuHeader", "Graph Actions"));
|
||||
Section.AddMenuEntry(FGraphEditorCommands::Get().SelectAllNodes);
|
||||
Section.AddMenuEntry(
|
||||
"ArrangeNodes",
|
||||
LOCTEXT("ArrangeNodes", "Arrange Nodes"),
|
||||
LOCTEXT("ArrangeNodesTooltip", "Arrange the nodes in the graph"),
|
||||
FSlateIcon(),
|
||||
FUIAction(
|
||||
FExecuteAction::CreateLambda([Context]()
|
||||
{
|
||||
if (Context && Context->Graph)
|
||||
{
|
||||
// TODO: Implement node arrangement
|
||||
}
|
||||
}),
|
||||
FCanExecuteAction::CreateLambda([Context]()
|
||||
{
|
||||
return Context && Context->Graph;
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const FPinConnectionResponse UDismembermentGraphSchema::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const
|
||||
{
|
||||
// Make sure the pins are valid
|
||||
if (!A || !B)
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Invalid pins"));
|
||||
}
|
||||
|
||||
// Make sure the pins are not on the same node
|
||||
if (A->GetOwningNode() == B->GetOwningNode())
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("SameNode", "Can't connect pins on the same node"));
|
||||
}
|
||||
|
||||
// Check pin directions
|
||||
const UEdGraphPin* InputPin = nullptr;
|
||||
const UEdGraphPin* OutputPin = nullptr;
|
||||
|
||||
if (A->Direction == EGPD_Input && B->Direction == EGPD_Output)
|
||||
{
|
||||
InputPin = A;
|
||||
OutputPin = B;
|
||||
}
|
||||
else if (A->Direction == EGPD_Output && B->Direction == EGPD_Input)
|
||||
{
|
||||
InputPin = B;
|
||||
OutputPin = A;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("IncompatibleDirections", "Incompatible pin directions"));
|
||||
}
|
||||
|
||||
// Check pin types
|
||||
FDismembermentGraphPinType InputType = GetPinType(InputPin);
|
||||
FDismembermentGraphPinType OutputType = GetPinType(OutputPin);
|
||||
|
||||
if (InputType != OutputType)
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("IncompatibleTypes", "Incompatible pin types"));
|
||||
}
|
||||
|
||||
// Check for cycles
|
||||
if (WouldCreateCycle(OutputPin, InputPin))
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("CycleDetected", "Connection would create a cycle"));
|
||||
}
|
||||
|
||||
// Check if the pins are already connected
|
||||
for (int32 i = 0; i < InputPin->LinkedTo.Num(); ++i)
|
||||
{
|
||||
if (InputPin->LinkedTo[i] == OutputPin)
|
||||
{
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("AlreadyConnected", "Pins are already connected"));
|
||||
}
|
||||
}
|
||||
|
||||
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("CreateConnection", "Create Connection"));
|
||||
}
|
||||
|
||||
bool UDismembermentGraphSchema::TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const
|
||||
{
|
||||
// Check if the connection is valid
|
||||
const FPinConnectionResponse Response = CanCreateConnection(A, B);
|
||||
if (Response.Response != CONNECT_RESPONSE_MAKE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Break existing connections on input pins
|
||||
if (A->Direction == EGPD_Input)
|
||||
{
|
||||
A->BreakAllPinLinks();
|
||||
}
|
||||
else if (B->Direction == EGPD_Input)
|
||||
{
|
||||
B->BreakAllPinLinks();
|
||||
}
|
||||
|
||||
// Make the connection
|
||||
A->MakeLinkTo(B);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const
|
||||
{
|
||||
return Pin && Pin->LinkedTo.Num() > 0;
|
||||
}
|
||||
|
||||
FLinearColor UDismembermentGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const
|
||||
{
|
||||
FDismembermentGraphPinType DismembermentPinType;
|
||||
DismembermentPinType.PinCategory = PinType.PinCategory;
|
||||
return GetPinTypeColor(DismembermentPinType);
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const
|
||||
{
|
||||
Super::BreakNodeLinks(TargetNode);
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const
|
||||
{
|
||||
Super::BreakPinLinks(TargetPin, bSendsNodeNotification);
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const
|
||||
{
|
||||
Super::BreakSinglePinLink(SourcePin, TargetPin);
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const
|
||||
{
|
||||
// TODO: Implement asset dropping
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const
|
||||
{
|
||||
// TODO: Implement asset dropping on nodes
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const
|
||||
{
|
||||
// TODO: Implement asset dropping on pins
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const
|
||||
{
|
||||
// TODO: Implement asset hover message
|
||||
OutTooltipText = TEXT("Drop to create a reference");
|
||||
OutOkIcon = true;
|
||||
}
|
||||
|
||||
void UDismembermentGraphSchema::GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const
|
||||
{
|
||||
// TODO: Implement asset hover message
|
||||
OutTooltipText = TEXT("Drop to create a reference");
|
||||
OutOkIcon = true;
|
||||
}
|
||||
|
||||
bool UDismembermentGraphSchema::CanConnectPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, FDismembermentGraphConnectionResponse& OutResponse) const
|
||||
{
|
||||
// Check if the pins are valid
|
||||
if (!PinA || !PinB)
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_INCOMPATIBLE;
|
||||
OutResponse.Message = LOCTEXT("PinError", "Invalid pins");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the pins are on the same node
|
||||
if (PinA->GetOwningNode() == PinB->GetOwningNode())
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_SELF_CONNECTION;
|
||||
OutResponse.Message = LOCTEXT("SameNode", "Can't connect pins on the same node");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check pin directions
|
||||
const UEdGraphPin* InputPin = nullptr;
|
||||
const UEdGraphPin* OutputPin = nullptr;
|
||||
|
||||
if (PinA->Direction == EGPD_Input && PinB->Direction == EGPD_Output)
|
||||
{
|
||||
InputPin = PinA;
|
||||
OutputPin = PinB;
|
||||
}
|
||||
else if (PinA->Direction == EGPD_Output && PinB->Direction == EGPD_Input)
|
||||
{
|
||||
InputPin = PinB;
|
||||
OutputPin = PinA;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_INCOMPATIBLE;
|
||||
OutResponse.Message = LOCTEXT("IncompatibleDirections", "Incompatible pin directions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check pin types
|
||||
FDismembermentGraphPinType InputType = GetPinType(InputPin);
|
||||
FDismembermentGraphPinType OutputType = GetPinType(OutputPin);
|
||||
|
||||
if (InputType != OutputType)
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_INCOMPATIBLE;
|
||||
OutResponse.Message = LOCTEXT("IncompatibleTypes", "Incompatible pin types");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for cycles
|
||||
if (WouldCreateCycle(OutputPin, InputPin))
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_CYCLE;
|
||||
OutResponse.Message = LOCTEXT("CycleDetected", "Connection would create a cycle");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the pins are already connected
|
||||
for (int32 i = 0; i < InputPin->LinkedTo.Num(); ++i)
|
||||
{
|
||||
if (InputPin->LinkedTo[i] == OutputPin)
|
||||
{
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::ERROR_DISALLOWED;
|
||||
OutResponse.Message = LOCTEXT("AlreadyConnected", "Pins are already connected");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OutResponse.Response = FDismembermentGraphConnectionResponse_K2::OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentGraphSchema::WouldCreateCycle(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const
|
||||
{
|
||||
// Check if connecting these pins would create a cycle
|
||||
const UEdGraphNode* NodeA = PinA->GetOwningNode();
|
||||
const UEdGraphNode* NodeB = PinB->GetOwningNode();
|
||||
|
||||
// If the nodes are the same, there's a cycle
|
||||
if (NodeA == NodeB)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Depth-first search to check for cycles
|
||||
TSet<const UEdGraphNode*> VisitedNodes;
|
||||
TArray<const UEdGraphNode*> NodesToVisit;
|
||||
NodesToVisit.Push(NodeB);
|
||||
|
||||
while (NodesToVisit.Num() > 0)
|
||||
{
|
||||
const UEdGraphNode* CurrentNode = NodesToVisit.Pop();
|
||||
if (VisitedNodes.Contains(CurrentNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VisitedNodes.Add(CurrentNode);
|
||||
|
||||
// Check all output pins
|
||||
for (int32 PinIndex = 0; PinIndex < CurrentNode->Pins.Num(); ++PinIndex)
|
||||
{
|
||||
const UEdGraphPin* Pin = CurrentNode->Pins[PinIndex];
|
||||
if (Pin->Direction == EGPD_Output)
|
||||
{
|
||||
// Check all connections
|
||||
for (int32 LinkIndex = 0; LinkIndex < Pin->LinkedTo.Num(); ++LinkIndex)
|
||||
{
|
||||
const UEdGraphPin* LinkedPin = Pin->LinkedTo[LinkIndex];
|
||||
const UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode();
|
||||
|
||||
// If we found NodeA, there's a cycle
|
||||
if (LinkedNode == NodeA)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add the linked node to the nodes to visit
|
||||
NodesToVisit.Push(LinkedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FDismembermentGraphPinType UDismembermentGraphSchema::GetPinType(const UEdGraphPin* Pin)
|
||||
{
|
||||
return FDismembermentGraphPinType(Pin->PinType.PinCategory);
|
||||
}
|
||||
|
||||
FLinearColor UDismembermentGraphSchema::GetPinTypeColor(const FDismembermentGraphPinType& PinType)
|
||||
{
|
||||
if (PinType.PinCategory == PC_Exec)
|
||||
{
|
||||
return FLinearColor::White;
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Bone)
|
||||
{
|
||||
return FLinearColor(0.9f, 0.9f, 0.2f);
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Cut)
|
||||
{
|
||||
return FLinearColor(1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Blood)
|
||||
{
|
||||
return FLinearColor(0.8f, 0.0f, 0.0f);
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Physics)
|
||||
{
|
||||
return FLinearColor(0.0f, 0.8f, 0.8f);
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Organ)
|
||||
{
|
||||
return FLinearColor(0.8f, 0.4f, 0.4f);
|
||||
}
|
||||
else if (PinType.PinCategory == PC_Wound)
|
||||
{
|
||||
return FLinearColor(0.6f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
return FLinearColor::Black;
|
||||
}
|
||||
|
||||
UDismembermentGraphNode* UDismembermentGraphSchema::CreateNode(TSubclassOf<UDismembermentGraphNode> NodeClass, UEdGraph* ParentGraph, float NodePosX, float NodePosY, bool bSelectNewNode)
|
||||
{
|
||||
UDismembermentGraphNode* NewNode = NewObject<UDismembermentGraphNode>(ParentGraph, NodeClass);
|
||||
NewNode->CreateNewGuid();
|
||||
NewNode->PostPlacedNewNode();
|
||||
NewNode->NodePosX = NodePosX;
|
||||
NewNode->NodePosY = NodePosY;
|
||||
NewNode->AllocateDefaultPins();
|
||||
|
||||
ParentGraph->AddNode(NewNode, true, bSelectNewNode);
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// Schema action for creating a new node
|
||||
FDismembermentSchemaAction_NewNode::FDismembermentSchemaAction_NewNode(const FText& InNodeCategory, const FText& InMenuDesc, const FText& InToolTip, const int32 InGrouping)
|
||||
: FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping)
|
||||
{
|
||||
}
|
||||
|
||||
UEdGraphNode* FDismembermentSchemaAction_NewNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode)
|
||||
{
|
||||
UDismembermentGraphNode* NewNode = UDismembermentGraphSchema::CreateNode(NodeClass, ParentGraph, Location.X, Location.Y, bSelectNewNode);
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@@ -0,0 +1,787 @@
|
||||
#include "DismembermentGraph/DismembermentPreviewManager.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNode.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeCut.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBoneSelect.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBloodEffect.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodePhysics.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeOrgan.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeWound.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Components/DecalComponent.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY(LogFLESHPreview);
|
||||
|
||||
UDismembermentPreviewManager::UDismembermentPreviewManager()
|
||||
: World(nullptr)
|
||||
, TargetActor(nullptr)
|
||||
, TargetSkeletalMesh(nullptr)
|
||||
, PreviewedNode(nullptr)
|
||||
, PreviewCutPlaneMesh(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::Initialize(TObjectPtr<UWorld> InWorld)
|
||||
{
|
||||
World = InWorld;
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Preview manager initialized"));
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::Cleanup()
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Cleaning up preview manager"));
|
||||
ClearPreview();
|
||||
World = nullptr;
|
||||
TargetActor = nullptr;
|
||||
TargetSkeletalMesh = nullptr;
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::SetTargetActor(TObjectPtr<AActor> InActor)
|
||||
{
|
||||
// Clear any existing preview
|
||||
ClearPreview();
|
||||
|
||||
// Set the new target actor
|
||||
TargetActor = InActor;
|
||||
|
||||
if (TargetActor)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Set target actor: %s"), *TargetActor->GetName());
|
||||
// Find the skeletal mesh component
|
||||
FindTargetSkeletalMesh();
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Set empty target actor"));
|
||||
}
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewNode(TObjectPtr<UDismembermentGraphNode> Node)
|
||||
{
|
||||
// Clear any existing preview
|
||||
ClearPreview();
|
||||
|
||||
// Set the new previewed node
|
||||
PreviewedNode = Node;
|
||||
|
||||
// Check if we have a valid target
|
||||
if (!TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview node: Invalid target actor or skeletal mesh"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview node: Node is null"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Previewing node: %s"), *Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
|
||||
|
||||
// Preview based on node type
|
||||
if (TObjectPtr<UDismembermentGraphNodeCut> CutNode = Cast<UDismembermentGraphNodeCut>(Node))
|
||||
{
|
||||
return PreviewCutNode(CutNode);
|
||||
}
|
||||
else if (TObjectPtr<UDismembermentGraphNodeBoneSelect> BoneSelectNode = Cast<UDismembermentGraphNodeBoneSelect>(Node))
|
||||
{
|
||||
return PreviewBoneSelectNode(BoneSelectNode);
|
||||
}
|
||||
else if (TObjectPtr<UDismembermentGraphNodeBloodEffect> BloodEffectNode = Cast<UDismembermentGraphNodeBloodEffect>(Node))
|
||||
{
|
||||
return PreviewBloodEffectNode(BloodEffectNode);
|
||||
}
|
||||
else if (TObjectPtr<UDismembermentGraphNodePhysics> PhysicsNode = Cast<UDismembermentGraphNodePhysics>(Node))
|
||||
{
|
||||
return PreviewPhysicsNode(PhysicsNode);
|
||||
}
|
||||
else if (TObjectPtr<UDismembermentGraphNodeOrgan> OrganNode = Cast<UDismembermentGraphNodeOrgan>(Node))
|
||||
{
|
||||
return PreviewOrganNode(OrganNode);
|
||||
}
|
||||
else if (TObjectPtr<UDismembermentGraphNodeWound> WoundNode = Cast<UDismembermentGraphNodeWound>(Node))
|
||||
{
|
||||
return PreviewWoundNode(WoundNode);
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Unknown node type"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::ClearPreview()
|
||||
{
|
||||
// Clear the previewed node
|
||||
PreviewedNode = nullptr;
|
||||
|
||||
// Clear all preview components
|
||||
ClearPreviewComponents();
|
||||
|
||||
// Clear preview data
|
||||
PreviewBoneSelections.Empty();
|
||||
PreviewCutTransforms.Empty();
|
||||
PreviewBloodEffectTransforms.Empty();
|
||||
PreviewOrganTransforms.Empty();
|
||||
PreviewWoundTransforms.Empty();
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Preview cleared"));
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::Tick(float DeltaTime)
|
||||
{
|
||||
// Update preview effects
|
||||
if (PreviewedNode && TargetActor && TargetSkeletalMesh)
|
||||
{
|
||||
// Update based on node type
|
||||
if (UDismembermentGraphNodeCut* CutNode = Cast<UDismembermentGraphNodeCut>(PreviewedNode))
|
||||
{
|
||||
// Update cut preview
|
||||
}
|
||||
else if (UDismembermentGraphNodeBloodEffect* BloodEffectNode = Cast<UDismembermentGraphNodeBloodEffect>(PreviewedNode))
|
||||
{
|
||||
// Update blood effect preview
|
||||
}
|
||||
else if (UDismembermentGraphNodePhysics* PhysicsNode = Cast<UDismembermentGraphNodePhysics>(PreviewedNode))
|
||||
{
|
||||
// Update physics preview
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::FindTargetSkeletalMesh()
|
||||
{
|
||||
TargetSkeletalMesh = nullptr;
|
||||
|
||||
if (TargetActor)
|
||||
{
|
||||
// Find the first skeletal mesh component
|
||||
TargetSkeletalMesh = TargetActor->FindComponentByClass<USkeletalMeshComponent>();
|
||||
|
||||
if (TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Found target skeletal mesh: %s"), *TargetSkeletalMesh->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("No skeletal mesh component found on actor %s"), *TargetActor->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot find skeletal mesh: Target actor is null"));
|
||||
}
|
||||
|
||||
return TargetSkeletalMesh != nullptr;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewCutNode(TObjectPtr<UDismembermentGraphNodeCut> CutNode)
|
||||
{
|
||||
if (!CutNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview cut node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the cut parameters
|
||||
float CutWidth = CutNode->CutWidth;
|
||||
float CutDepth = CutNode->CutDepth;
|
||||
UMaterialInterface* CutMaterial = CutNode->bUseCustomMaterial ? CutNode->CustomCutMaterial : nullptr;
|
||||
|
||||
// Get the actor's transform
|
||||
FTransform ActorTransform = TargetActor->GetActorTransform();
|
||||
|
||||
// Create a default cut direction if not connected to an input
|
||||
FVector CutLocation = ActorTransform.GetLocation() + FVector(0.0f, 0.0f, 100.0f);
|
||||
FVector CutDirection = FVector(1.0f, 0.0f, 0.0f);
|
||||
|
||||
// Create the preview cut plane mesh
|
||||
PreviewCutPlaneMesh = CreatePreviewCutPlaneMesh(CutLocation, CutDirection, CutWidth, CutDepth, CutMaterial);
|
||||
|
||||
if (!PreviewCutPlaneMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create cut plane mesh"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the cut transform for later use
|
||||
FTransform CutTransform;
|
||||
CutTransform.SetLocation(CutLocation);
|
||||
CutTransform.SetRotation(FQuat::MakeFromX(CutDirection));
|
||||
CutTransform.SetScale3D(FVector(CutWidth, CutDepth, 1.0f));
|
||||
PreviewCutTransforms.Add(CutTransform);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Cut node preview created successfully"));
|
||||
|
||||
return PreviewCutPlaneMesh != nullptr;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewBoneSelectNode(TObjectPtr<UDismembermentGraphNodeBoneSelect> BoneSelectNode)
|
||||
{
|
||||
if (!BoneSelectNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview bone select node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the bone selection parameters
|
||||
TArray<FName> BoneNames = BoneSelectNode->BoneNames;
|
||||
bool bUseRegex = BoneSelectNode->bUseRegex;
|
||||
FString BoneNamePattern = BoneSelectNode->BoneNamePattern;
|
||||
bool bIncludeChildren = BoneSelectNode->bIncludeChildren;
|
||||
|
||||
// Store the bone selections for later use
|
||||
PreviewBoneSelections.Append(BoneNames);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Bone select node preview created successfully, selected %d bones"), BoneNames.Num());
|
||||
|
||||
// TODO: Handle regex and child bones
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewBloodEffectNode(TObjectPtr<UDismembermentGraphNodeBloodEffect> BloodEffectNode)
|
||||
{
|
||||
if (!BloodEffectNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview blood effect node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the blood effect parameters
|
||||
UNiagaraSystem* BloodEffect = BloodEffectNode->BloodEffect;
|
||||
float BloodAmount = BloodEffectNode->BloodAmount;
|
||||
float BloodPressure = BloodEffectNode->BloodPressure;
|
||||
bool bCreateBloodPool = BloodEffectNode->bCreateBloodPool;
|
||||
float BloodPoolSize = BloodEffectNode->BloodPoolSize;
|
||||
UMaterialInterface* BloodPoolMaterial = BloodEffectNode->BloodPoolMaterial;
|
||||
|
||||
if (!BloodEffect)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview blood effect: No blood effect system specified"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the actor's transform
|
||||
FTransform ActorTransform = TargetActor->GetActorTransform();
|
||||
|
||||
// Create a default blood effect location if not connected to an input
|
||||
FVector BloodLocation = ActorTransform.GetLocation() + FVector(0.0f, 0.0f, 100.0f);
|
||||
|
||||
// Create the preview blood effect
|
||||
TObjectPtr<UNiagaraComponent> BloodEffectComponent = CreatePreviewBloodEffect(BloodLocation, BloodEffect, BloodAmount, BloodPressure);
|
||||
|
||||
if (!BloodEffectComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create blood effect component"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the preview blood pool if needed
|
||||
TObjectPtr<UDecalComponent> BloodPoolComponent = nullptr;
|
||||
if (bCreateBloodPool)
|
||||
{
|
||||
BloodPoolComponent = CreatePreviewBloodPool(BloodLocation, BloodPoolSize, BloodPoolMaterial);
|
||||
|
||||
if (!BloodPoolComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to create blood pool component"));
|
||||
}
|
||||
}
|
||||
|
||||
// Store the blood effect transform for later use
|
||||
FTransform BloodEffectTransform;
|
||||
BloodEffectTransform.SetLocation(BloodLocation);
|
||||
PreviewBloodEffectTransforms.Add(BloodEffectTransform);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Blood effect node preview created successfully"));
|
||||
|
||||
return BloodEffectComponent != nullptr;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewPhysicsNode(TObjectPtr<UDismembermentGraphNodePhysics> PhysicsNode)
|
||||
{
|
||||
if (!PhysicsNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview physics node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the physics parameters
|
||||
float Mass = PhysicsNode->Mass;
|
||||
float LinearDamping = PhysicsNode->LinearDamping;
|
||||
float AngularDamping = PhysicsNode->AngularDamping;
|
||||
bool bEnableGravity = PhysicsNode->bEnableGravity;
|
||||
bool bSimulatePhysics = PhysicsNode->bSimulatePhysics;
|
||||
bool bGenerateOverlapEvents = PhysicsNode->bGenerateOverlapEvents;
|
||||
UPhysicalMaterial* PhysicalMaterial = PhysicsNode->PhysicalMaterial;
|
||||
float ImpulseForce = PhysicsNode->ImpulseForce;
|
||||
float ImpulseRadius = PhysicsNode->ImpulseRadius;
|
||||
|
||||
// Apply physics settings to the target skeletal mesh
|
||||
if (TargetSkeletalMesh)
|
||||
{
|
||||
// Store original values to restore later
|
||||
TargetSkeletalMesh->SetMassOverrideInKg(NAME_None, Mass, true);
|
||||
TargetSkeletalMesh->SetLinearDamping(LinearDamping);
|
||||
TargetSkeletalMesh->SetAngularDamping(AngularDamping);
|
||||
TargetSkeletalMesh->SetEnableGravity(bEnableGravity);
|
||||
TargetSkeletalMesh->SetSimulatePhysics(bSimulatePhysics);
|
||||
TargetSkeletalMesh->SetGenerateOverlapEvents(bGenerateOverlapEvents);
|
||||
|
||||
if (PhysicalMaterial)
|
||||
{
|
||||
TargetSkeletalMesh->SetPhysMaterialOverride(PhysicalMaterial);
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Physics node preview applied successfully, mass: %.2f, linear damping: %.2f, angular damping: %.2f"),
|
||||
Mass, LinearDamping, AngularDamping);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewOrganNode(TObjectPtr<UDismembermentGraphNodeOrgan> OrganNode)
|
||||
{
|
||||
if (!OrganNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview organ node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the organ parameters
|
||||
UStaticMesh* OrganMesh = OrganNode->OrganMesh;
|
||||
UMaterialInterface* OrganMaterial = OrganNode->OrganMaterial;
|
||||
FName AttachBoneName = OrganNode->AttachBoneName;
|
||||
FVector RelativeLocation = OrganNode->RelativeLocation;
|
||||
FRotator RelativeRotation = OrganNode->RelativeRotation;
|
||||
FVector RelativeScale = OrganNode->RelativeScale;
|
||||
bool bSimulatePhysics = OrganNode->bSimulatePhysics;
|
||||
float DamageMultiplier = OrganNode->DamageMultiplier;
|
||||
bool bIsCriticalOrgan = OrganNode->bIsCriticalOrgan;
|
||||
float BloodAmount = OrganNode->BloodAmount;
|
||||
|
||||
if (!OrganMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview organ: No organ mesh specified"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the preview organ
|
||||
TObjectPtr<UStaticMeshComponent> OrganComponent = CreatePreviewOrgan(OrganMesh, OrganMaterial, AttachBoneName, RelativeLocation, RelativeRotation, RelativeScale);
|
||||
|
||||
if (!OrganComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create organ component"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the organ transform for later use
|
||||
FTransform OrganTransform;
|
||||
if (TargetSkeletalMesh && !AttachBoneName.IsNone())
|
||||
{
|
||||
FTransform BoneTransform = TargetSkeletalMesh->GetBoneTransform(TargetSkeletalMesh->GetBoneIndex(AttachBoneName));
|
||||
OrganTransform = FTransform(RelativeRotation, RelativeLocation, RelativeScale) * BoneTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
OrganTransform = FTransform(RelativeRotation, RelativeLocation, RelativeScale) * TargetActor->GetActorTransform();
|
||||
}
|
||||
PreviewOrganTransforms.Add(OrganTransform);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Organ node preview created successfully, attached to bone: %s"),
|
||||
AttachBoneName.IsNone() ? TEXT("None") : *AttachBoneName.ToString());
|
||||
|
||||
return OrganComponent != nullptr;
|
||||
}
|
||||
|
||||
bool UDismembermentPreviewManager::PreviewWoundNode(TObjectPtr<UDismembermentGraphNodeWound> WoundNode)
|
||||
{
|
||||
if (!WoundNode || !TargetActor || !TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot preview wound node: Invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the wound parameters
|
||||
float WoundSize = WoundNode->WoundSize;
|
||||
float WoundDepth = WoundNode->WoundDepth;
|
||||
UMaterialInterface* WoundMaterial = WoundNode->WoundMaterial;
|
||||
UNiagaraSystem* WoundEffect = WoundNode->WoundEffect;
|
||||
bool bCreateDecal = WoundNode->bCreateDecal;
|
||||
UMaterialInterface* DecalMaterial = WoundNode->DecalMaterial;
|
||||
float DecalSize = WoundNode->DecalSize;
|
||||
float DecalLifetime = WoundNode->DecalLifetime;
|
||||
bool bAffectBoneHealth = WoundNode->bAffectBoneHealth;
|
||||
float BoneDamage = WoundNode->BoneDamage;
|
||||
|
||||
// Get the actor's transform
|
||||
FTransform ActorTransform = TargetActor->GetActorTransform();
|
||||
|
||||
// Create a default wound location if not connected to an input
|
||||
FVector WoundLocation = ActorTransform.GetLocation() + FVector(0.0f, 0.0f, 100.0f);
|
||||
|
||||
// Create the preview wound effect
|
||||
TObjectPtr<UNiagaraComponent> WoundEffectComponent = nullptr;
|
||||
if (WoundEffect)
|
||||
{
|
||||
WoundEffectComponent = UNiagaraComponent::SpawnSystemAttached(
|
||||
WoundEffect,
|
||||
TargetSkeletalMesh,
|
||||
NAME_None,
|
||||
WoundLocation,
|
||||
FRotator::ZeroRotator,
|
||||
EAttachLocation::KeepWorldPosition,
|
||||
false);
|
||||
|
||||
if (WoundEffectComponent)
|
||||
{
|
||||
PreviewNiagaraComponents.Add(WoundEffectComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to create wound effect component"));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the preview wound decal if needed
|
||||
TObjectPtr<UDecalComponent> WoundDecalComponent = nullptr;
|
||||
if (bCreateDecal && DecalMaterial)
|
||||
{
|
||||
WoundDecalComponent = CreatePreviewWound(WoundLocation, DecalSize, DecalMaterial);
|
||||
|
||||
if (!WoundDecalComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to create wound decal component"));
|
||||
}
|
||||
}
|
||||
|
||||
// Store the wound transform for later use
|
||||
FTransform WoundTransform;
|
||||
WoundTransform.SetLocation(WoundLocation);
|
||||
PreviewWoundTransforms.Add(WoundTransform);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Log, TEXT("Wound node preview created successfully, size: %.2f, depth: %.2f"), WoundSize, WoundDepth);
|
||||
|
||||
return WoundEffectComponent != nullptr || WoundDecalComponent != nullptr;
|
||||
}
|
||||
|
||||
TObjectPtr<UStaticMeshComponent> UDismembermentPreviewManager::CreatePreviewCutPlaneMesh(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material)
|
||||
{
|
||||
if (!World || !TargetActor)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot create cut plane: Invalid world or target actor"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a static mesh component for the cut plane
|
||||
TObjectPtr<UStaticMeshComponent> CutPlaneMeshComponent = NewObject<UStaticMeshComponent>(TargetActor);
|
||||
if (!CutPlaneMeshComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create cut plane component"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register the component
|
||||
CutPlaneMeshComponent->RegisterComponent();
|
||||
|
||||
// Set the mesh to a plane
|
||||
UStaticMesh* PlaneMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Plane.Plane"));
|
||||
if (PlaneMesh)
|
||||
{
|
||||
CutPlaneMeshComponent->SetStaticMesh(PlaneMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to load plane mesh"));
|
||||
}
|
||||
|
||||
// Set the material
|
||||
if (Material)
|
||||
{
|
||||
CutPlaneMeshComponent->SetMaterial(0, Material);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use a default material
|
||||
UMaterialInterface* DefaultMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));
|
||||
if (DefaultMaterial)
|
||||
{
|
||||
CutPlaneMeshComponent->SetMaterial(0, DefaultMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to load default material"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the transform
|
||||
FRotator Rotation = FRotationMatrix::MakeFromX(Direction).Rotator();
|
||||
CutPlaneMeshComponent->SetWorldLocationAndRotation(Location, Rotation);
|
||||
CutPlaneMeshComponent->SetWorldScale3D(FVector(Width, Depth, 1.0f));
|
||||
|
||||
// Add to the preview components
|
||||
PreviewStaticMeshComponents.Add(CutPlaneMeshComponent);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Created cut plane mesh, location: %s, direction: %s"),
|
||||
*Location.ToString(), *Direction.ToString());
|
||||
|
||||
return CutPlaneMeshComponent;
|
||||
}
|
||||
|
||||
TObjectPtr<UNiagaraComponent> UDismembermentPreviewManager::CreatePreviewBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure)
|
||||
{
|
||||
if (!World || !TargetActor || !BloodEffect)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot create blood effect: Invalid parameters"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a Niagara component for the blood effect
|
||||
TObjectPtr<UNiagaraComponent> BloodEffectComponent = UNiagaraComponent::SpawnSystemAttached(
|
||||
BloodEffect,
|
||||
TargetSkeletalMesh,
|
||||
NAME_None,
|
||||
Location,
|
||||
FRotator::ZeroRotator,
|
||||
EAttachLocation::KeepWorldPosition,
|
||||
false);
|
||||
|
||||
if (!BloodEffectComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create blood effect component"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set parameters
|
||||
BloodEffectComponent->SetFloatParameter(TEXT("BloodAmount"), BloodAmount);
|
||||
BloodEffectComponent->SetFloatParameter(TEXT("BloodPressure"), BloodPressure);
|
||||
|
||||
// Add to the preview components
|
||||
PreviewNiagaraComponents.Add(BloodEffectComponent);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Created blood effect, location: %s, blood amount: %.2f, blood pressure: %.2f"),
|
||||
*Location.ToString(), BloodAmount, BloodPressure);
|
||||
|
||||
return BloodEffectComponent;
|
||||
}
|
||||
|
||||
TObjectPtr<UDecalComponent> UDismembermentPreviewManager::CreatePreviewBloodPool(const FVector& Location, float Size, UMaterialInterface* Material)
|
||||
{
|
||||
if (!World || !TargetActor)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot create blood pool: Invalid world or target actor"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a decal component for the blood pool
|
||||
TObjectPtr<UDecalComponent> BloodPoolComponent = NewObject<UDecalComponent>(TargetActor);
|
||||
if (!BloodPoolComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create blood pool component"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register the component
|
||||
BloodPoolComponent->RegisterComponent();
|
||||
|
||||
// Set the material
|
||||
if (Material)
|
||||
{
|
||||
BloodPoolComponent->SetMaterial(0, Material);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use a default material
|
||||
UMaterialInterface* DefaultMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));
|
||||
if (DefaultMaterial)
|
||||
{
|
||||
BloodPoolComponent->SetMaterial(0, DefaultMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to load default material"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the transform
|
||||
BloodPoolComponent->SetWorldLocation(Location);
|
||||
BloodPoolComponent->SetWorldRotation(FRotator(-90.0f, 0.0f, 0.0f));
|
||||
BloodPoolComponent->DecalSize = FVector(Size, Size, 10.0f);
|
||||
|
||||
// Add to the preview components
|
||||
PreviewDecalComponents.Add(BloodPoolComponent);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Created blood pool decal, location: %s, size: %.2f"),
|
||||
*Location.ToString(), Size);
|
||||
|
||||
return BloodPoolComponent;
|
||||
}
|
||||
|
||||
TObjectPtr<UStaticMeshComponent> UDismembermentPreviewManager::CreatePreviewOrgan(UStaticMesh* OrganMesh, UMaterialInterface* OrganMaterial, const FName& AttachBoneName, const FVector& RelativeLocation, const FRotator& RelativeRotation, const FVector& RelativeScale)
|
||||
{
|
||||
if (!World || !TargetActor || !OrganMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot create organ: Invalid parameters"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a static mesh component for the organ
|
||||
TObjectPtr<UStaticMeshComponent> OrganComponent = NewObject<UStaticMeshComponent>(TargetActor);
|
||||
if (!OrganComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create organ component"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register the component
|
||||
OrganComponent->RegisterComponent();
|
||||
|
||||
// Set the mesh
|
||||
OrganComponent->SetStaticMesh(OrganMesh);
|
||||
|
||||
// Set the material
|
||||
if (OrganMaterial)
|
||||
{
|
||||
OrganComponent->SetMaterial(0, OrganMaterial);
|
||||
}
|
||||
|
||||
// Attach to bone if specified
|
||||
if (TargetSkeletalMesh && !AttachBoneName.IsNone())
|
||||
{
|
||||
if (TargetSkeletalMesh->GetBoneIndex(AttachBoneName) != INDEX_NONE)
|
||||
{
|
||||
OrganComponent->AttachToComponent(TargetSkeletalMesh, FAttachmentTransformRules::KeepRelativeTransform, AttachBoneName);
|
||||
OrganComponent->SetRelativeLocationAndRotation(RelativeLocation, RelativeRotation);
|
||||
OrganComponent->SetRelativeScale3D(RelativeScale);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Organ attached to bone: %s"), *AttachBoneName.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Bone not found: %s, attaching to actor root component"), *AttachBoneName.ToString());
|
||||
// Set the transform
|
||||
OrganComponent->SetWorldLocationAndRotation(
|
||||
TargetActor->GetActorLocation() + RelativeLocation,
|
||||
TargetActor->GetActorRotation() + RelativeRotation);
|
||||
OrganComponent->SetWorldScale3D(RelativeScale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the transform
|
||||
OrganComponent->SetWorldLocationAndRotation(
|
||||
TargetActor->GetActorLocation() + RelativeLocation,
|
||||
TargetActor->GetActorRotation() + RelativeRotation);
|
||||
OrganComponent->SetWorldScale3D(RelativeScale);
|
||||
}
|
||||
|
||||
// Add to the preview components
|
||||
PreviewStaticMeshComponents.Add(OrganComponent);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Created organ component, relative location: %s"), *RelativeLocation.ToString());
|
||||
|
||||
return OrganComponent;
|
||||
}
|
||||
|
||||
TObjectPtr<UDecalComponent> UDismembermentPreviewManager::CreatePreviewWound(const FVector& Location, float Size, UMaterialInterface* Material)
|
||||
{
|
||||
if (!World || !TargetActor)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Cannot create wound: Invalid world or target actor"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a decal component for the wound
|
||||
TObjectPtr<UDecalComponent> WoundComponent = NewObject<UDecalComponent>(TargetActor);
|
||||
if (!WoundComponent)
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Error, TEXT("Failed to create wound component"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register the component
|
||||
WoundComponent->RegisterComponent();
|
||||
|
||||
// Set the material
|
||||
if (Material)
|
||||
{
|
||||
WoundComponent->SetMaterial(0, Material);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use a default material
|
||||
UMaterialInterface* DefaultMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));
|
||||
if (DefaultMaterial)
|
||||
{
|
||||
WoundComponent->SetMaterial(0, DefaultMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHPreview, Warning, TEXT("Failed to load default material"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the transform
|
||||
WoundComponent->SetWorldLocation(Location);
|
||||
WoundComponent->SetWorldRotation(FRotator(-90.0f, 0.0f, 0.0f));
|
||||
WoundComponent->DecalSize = FVector(Size, Size, 10.0f);
|
||||
|
||||
// Add to the preview components
|
||||
PreviewDecalComponents.Add(WoundComponent);
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Created wound decal, location: %s, size: %.2f"),
|
||||
*Location.ToString(), Size);
|
||||
|
||||
return WoundComponent;
|
||||
}
|
||||
|
||||
void UDismembermentPreviewManager::ClearPreviewComponents()
|
||||
{
|
||||
// Destroy all preview components
|
||||
for (TObjectPtr<UNiagaraComponent> Component : PreviewNiagaraComponents)
|
||||
{
|
||||
if (Component)
|
||||
{
|
||||
Component->DestroyComponent();
|
||||
}
|
||||
}
|
||||
PreviewNiagaraComponents.Empty();
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Cleared %d Niagara components"), PreviewNiagaraComponents.Num());
|
||||
|
||||
for (TObjectPtr<UDecalComponent> Component : PreviewDecalComponents)
|
||||
{
|
||||
if (Component)
|
||||
{
|
||||
Component->DestroyComponent();
|
||||
}
|
||||
}
|
||||
PreviewDecalComponents.Empty();
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Cleared %d decal components"), PreviewDecalComponents.Num());
|
||||
|
||||
for (TObjectPtr<UStaticMeshComponent> Component : PreviewStaticMeshComponents)
|
||||
{
|
||||
if (Component)
|
||||
{
|
||||
Component->DestroyComponent();
|
||||
}
|
||||
}
|
||||
PreviewStaticMeshComponents.Empty();
|
||||
|
||||
UE_LOG(LogFLESHPreview, Verbose, TEXT("Cleared %d static mesh components"), PreviewStaticMeshComponents.Num());
|
||||
|
||||
// Clear the cut plane mesh
|
||||
if (PreviewCutPlaneMesh)
|
||||
{
|
||||
PreviewCutPlaneMesh->DestroyComponent();
|
||||
PreviewCutPlaneMesh = nullptr;
|
||||
}
|
||||
}
|
@@ -0,0 +1,377 @@
|
||||
#include "DismembermentGraph/SDismembermentGraphNode.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNode.h"
|
||||
#include "GraphEditorSettings.h"
|
||||
#include "SGraphPin.h"
|
||||
#include "SlateOptMacros.h"
|
||||
#include "Widgets/SBoxPanel.h"
|
||||
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
||||
#include "Widgets/Images/SImage.h"
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
void SDismembermentGraphNode::Construct(const FArguments& InArgs, UEdGraphNode* InNode)
|
||||
{
|
||||
GraphNode = InNode;
|
||||
UpdateGraphNode();
|
||||
bIsHovered = false;
|
||||
}
|
||||
|
||||
void SDismembermentGraphNode::UpdateGraphNode()
|
||||
{
|
||||
// Clear the widget
|
||||
ContentScale.Bind(this, &SGraphNode::GetContentScale);
|
||||
LeftNodeBox.Reset();
|
||||
RightNodeBox.Reset();
|
||||
OutputPinBox.Reset();
|
||||
|
||||
// Setup the node title
|
||||
TSharedPtr<SNodeTitle> NodeTitle = SNew(SNodeTitle, GraphNode);
|
||||
|
||||
// Create the node body
|
||||
this->ContentScale.Bind(this, &SGraphNode::GetContentScale);
|
||||
this->GetOrAddSlot(ENodeZone::Center)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("Graph.StateNode.Body"))
|
||||
.BorderBackgroundColor(this, &SDismembermentGraphNode::GetNodeColor)
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Fill)
|
||||
.Padding(0.0f)
|
||||
[
|
||||
SNew(SOverlay)
|
||||
// Main node body
|
||||
+ SOverlay::Slot()
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Fill)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
// Title bar
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("Graph.StateNode.ColorSpill"))
|
||||
.BorderBackgroundColor(this, &SDismembermentGraphNode::GetNodeTitleColor)
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(FMargin(10.0f, 5.0f))
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
// Node title
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(this, &SDismembermentGraphNode::GetNodeTitle)
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.NodeTitle")
|
||||
.Margin(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
|
||||
]
|
||||
// Node category
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(this, &SDismembermentGraphNode::GetNodeCategory)
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.NodeTitle")
|
||||
.ColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f))
|
||||
.Margin(FMargin(4.0f, 0.0f, 0.0f, 0.0f))
|
||||
]
|
||||
]
|
||||
]
|
||||
// Node content
|
||||
+ SVerticalBox::Slot()
|
||||
.Padding(0.0f)
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("NoBorder"))
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Fill)
|
||||
.Padding(FMargin(10.0f, 0.0f))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
// Node description
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(FMargin(0.0f, 4.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(this, &SDismembermentGraphNode::GetNodeDescription)
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.Description")
|
||||
.WrapTextAt(200.0f)
|
||||
]
|
||||
// Node preview
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(FMargin(0.0f, 4.0f))
|
||||
[
|
||||
GetNodePreviewWidget()
|
||||
]
|
||||
]
|
||||
]
|
||||
// Input pins
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SAssignNew(LeftNodeBox, SVerticalBox)
|
||||
]
|
||||
// Output pins
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SAssignNew(RightNodeBox, SVerticalBox)
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Create all the pins
|
||||
CreatePinWidgets();
|
||||
}
|
||||
|
||||
void SDismembermentGraphNode::CreatePinWidgets()
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the input pins
|
||||
for (int32 PinIndex = 0; PinIndex < DismembermentNode->Pins.Num(); PinIndex++)
|
||||
{
|
||||
UEdGraphPin* Pin = DismembermentNode->Pins[PinIndex];
|
||||
if (!Pin->bHidden)
|
||||
{
|
||||
TSharedPtr<SGraphPin> NewPin = SNew(SGraphPin, Pin);
|
||||
AddPin(NewPin.ToSharedRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentGraphNode::AddPin(const TSharedRef<SGraphPin>& PinToAdd)
|
||||
{
|
||||
PinToAdd->SetOwner(SharedThis(this));
|
||||
|
||||
const UEdGraphPin* PinObj = PinToAdd->GetPinObj();
|
||||
const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView;
|
||||
if (bAdvancedParameter)
|
||||
{
|
||||
PinToAdd->SetVisibility(TAttribute<EVisibility>(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced));
|
||||
}
|
||||
|
||||
if (PinToAdd->GetDirection() == EGPD_Input)
|
||||
{
|
||||
LeftNodeBox->AddSlot()
|
||||
.AutoHeight()
|
||||
.HAlign(HAlign_Left)
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(10.0f, 4.0f)
|
||||
[
|
||||
PinToAdd
|
||||
];
|
||||
InputPins.Add(PinToAdd);
|
||||
}
|
||||
else // EGPD_Output
|
||||
{
|
||||
RightNodeBox->AddSlot()
|
||||
.AutoHeight()
|
||||
.HAlign(HAlign_Right)
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(10.0f, 4.0f)
|
||||
[
|
||||
PinToAdd
|
||||
];
|
||||
OutputPins.Add(PinToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
TSharedPtr<SToolTip> SDismembermentGraphNode::GetComplexTooltip()
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SNew(SToolTip)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(FMargin(0.0f, 2.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(FMargin(0.0f, 2.0f))
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(DismembermentNode->GetTooltipText())
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
FReply SDismembermentGraphNode::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return FReply::Handled().DetectDrag(SharedThis(this), EKeys::LeftMouseButton);
|
||||
}
|
||||
|
||||
FReply SDismembermentGraphNode::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SDismembermentGraphNode::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
void SDismembermentGraphNode::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
bIsHovered = true;
|
||||
SGraphNode::OnMouseEnter(MyGeometry, MouseEvent);
|
||||
}
|
||||
|
||||
void SDismembermentGraphNode::OnMouseLeave(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
bIsHovered = false;
|
||||
SGraphNode::OnMouseLeave(MouseEvent);
|
||||
}
|
||||
|
||||
UDismembermentGraphNode* SDismembermentGraphNode::GetDismembermentGraphNode() const
|
||||
{
|
||||
return Cast<UDismembermentGraphNode>(GraphNode);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SDismembermentGraphNode::GetNodeTitleWidget()
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return SNew(STextBlock).Text(NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node"));
|
||||
}
|
||||
|
||||
return SNew(STextBlock)
|
||||
.Text(DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle))
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.NodeTitle");
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SDismembermentGraphNode::GetNodeBodyWidget()
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return SNew(STextBlock).Text(NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node"));
|
||||
}
|
||||
|
||||
return SNew(STextBlock)
|
||||
.Text(DismembermentNode->GetTooltipText())
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.Description")
|
||||
.WrapTextAt(200.0f);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SDismembermentGraphNode::GetNodePreviewWidget()
|
||||
{
|
||||
// This can be customized for different node types to show a preview
|
||||
return SNew(SBox)
|
||||
.WidthOverride(100.0f)
|
||||
.HeightOverride(100.0f)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("Graph.StateNode.Body"))
|
||||
.BorderBackgroundColor(FLinearColor(0.1f, 0.1f, 0.1f, 0.5f))
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(NSLOCTEXT("SDismembermentGraphNode", "Preview", "Preview"))
|
||||
.TextStyle(FEditorStyle::Get(), "Graph.StateNode.Description")
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
FSlateColor SDismembermentGraphNode::GetNodeColor() const
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return FLinearColor::Black;
|
||||
}
|
||||
|
||||
FLinearColor NodeColor = DismembermentNode->NodeTitleColor;
|
||||
if (IsNodeSelected())
|
||||
{
|
||||
NodeColor = FLinearColor(1.0f, 0.5f, 0.0f);
|
||||
}
|
||||
else if (IsNodeHovered())
|
||||
{
|
||||
NodeColor = NodeColor * 1.2f;
|
||||
}
|
||||
|
||||
return NodeColor;
|
||||
}
|
||||
|
||||
FSlateColor SDismembermentGraphNode::GetNodeTitleColor() const
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return FLinearColor::Black;
|
||||
}
|
||||
|
||||
return DismembermentNode->NodeTitleColor.LinearRGBToHSV();
|
||||
}
|
||||
|
||||
FText SDismembermentGraphNode::GetNodeTitle() const
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node");
|
||||
}
|
||||
|
||||
return DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle);
|
||||
}
|
||||
|
||||
FText SDismembermentGraphNode::GetNodeCategory() const
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
return DismembermentNode->NodeCategory;
|
||||
}
|
||||
|
||||
FText SDismembermentGraphNode::GetNodeDescription() const
|
||||
{
|
||||
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
|
||||
if (!DismembermentNode)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
return DismembermentNode->NodeDescription;
|
||||
}
|
||||
|
||||
bool SDismembermentGraphNode::IsNodeSelected() const
|
||||
{
|
||||
return GraphNode && GraphNode->IsSelected();
|
||||
}
|
||||
|
||||
bool SDismembermentGraphNode::IsNodeHovered() const
|
||||
{
|
||||
return bIsHovered;
|
||||
}
|
||||
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
@@ -0,0 +1,477 @@
|
||||
#include "DismembermentGraph/SDismembermentPreviewViewport.h"
|
||||
#include "DismembermentGraph/DismembermentPreviewManager.h"
|
||||
#include "EditorViewportClient.h"
|
||||
#include "SEditorViewport.h"
|
||||
#include "PreviewScene.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "AdvancedPreviewScene.h"
|
||||
#include "AssetViewerSettings.h"
|
||||
#include "EditorModeManager.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
// Preview actor class
|
||||
class ADismembermentPreviewActor : public AActor
|
||||
{
|
||||
public:
|
||||
ADismembermentPreviewActor(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
// Create a skeletal mesh component
|
||||
SkeletalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMeshComponent"));
|
||||
RootComponent = SkeletalMeshComponent;
|
||||
}
|
||||
|
||||
// Set the skeletal mesh
|
||||
void SetSkeletalMesh(USkeletalMesh* InSkeletalMesh)
|
||||
{
|
||||
if (SkeletalMeshComponent)
|
||||
{
|
||||
SkeletalMeshComponent->SetSkeletalMesh(InSkeletalMesh);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the skeletal mesh component
|
||||
USkeletalMeshComponent* GetSkeletalMeshComponent() const
|
||||
{
|
||||
return SkeletalMeshComponent;
|
||||
}
|
||||
|
||||
private:
|
||||
// The skeletal mesh component
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> SkeletalMeshComponent;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SDismembermentPreviewViewport
|
||||
|
||||
void SDismembermentPreviewViewport::Construct(const FArguments& InArgs)
|
||||
{
|
||||
// Create the preview scene
|
||||
PreviewScene = MakeShareable(new FAdvancedPreviewScene(FPreviewScene::ConstructionValues()));
|
||||
|
||||
// Set up the preview scene
|
||||
UAssetViewerSettings* Settings = UAssetViewerSettings::Get();
|
||||
const int32 ProfileIndex = Settings->Profiles.IsValidIndex(0) ? 0 : INDEX_NONE;
|
||||
if (ProfileIndex != INDEX_NONE)
|
||||
{
|
||||
Settings->Profiles[ProfileIndex].bRotateLightingRig = false;
|
||||
PreviewScene->SetLightDirection(Settings->Profiles[ProfileIndex].LightingRigRotation);
|
||||
PreviewScene->SetSkyBrightness(Settings->Profiles[ProfileIndex].EnvironmentCubeMapBrightness);
|
||||
PreviewScene->SetLightColor(Settings->Profiles[ProfileIndex].LightColor);
|
||||
PreviewScene->SetLightIntensity(Settings->Profiles[ProfileIndex].LightBrightness);
|
||||
PreviewScene->SetEnvironmentCubeMap(Settings->Profiles[ProfileIndex].EnvironmentCubeMap);
|
||||
}
|
||||
|
||||
// Create the preview actor
|
||||
CreatePreviewActor();
|
||||
|
||||
// Call parent constructor
|
||||
SEditorViewport::Construct(SEditorViewport::FArguments());
|
||||
}
|
||||
|
||||
SDismembermentPreviewViewport::~SDismembermentPreviewViewport()
|
||||
{
|
||||
// Clean up the preview scene
|
||||
if (PreviewScene.IsValid())
|
||||
{
|
||||
PreviewScene->RemoveComponent(PreviewActor);
|
||||
PreviewScene.Reset();
|
||||
}
|
||||
|
||||
// Clean up the preview actor
|
||||
if (PreviewActor)
|
||||
{
|
||||
if (PreviewActor->GetWorld())
|
||||
{
|
||||
PreviewActor->Destroy();
|
||||
}
|
||||
PreviewActor = nullptr;
|
||||
}
|
||||
|
||||
// Clean up the viewport client
|
||||
ViewportClient.Reset();
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::SetPreviewManager(UDismembermentPreviewManager* InPreviewManager)
|
||||
{
|
||||
PreviewManager = InPreviewManager;
|
||||
|
||||
// Update the viewport client
|
||||
if (ViewportClient.IsValid())
|
||||
{
|
||||
ViewportClient->SetPreviewManager(PreviewManager);
|
||||
}
|
||||
|
||||
// Update the preview actor
|
||||
if (PreviewManager && PreviewActor)
|
||||
{
|
||||
PreviewManager->SetTargetActor(PreviewActor);
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::SetPreviewSkeletalMesh(USkeletalMesh* InSkeletalMesh)
|
||||
{
|
||||
// Set the skeletal mesh on the preview actor
|
||||
if (PreviewActor)
|
||||
{
|
||||
ADismembermentPreviewActor* PreviewActorCasted = Cast<ADismembermentPreviewActor>(PreviewActor);
|
||||
if (PreviewActorCasted)
|
||||
{
|
||||
PreviewActorCasted->SetSkeletalMesh(InSkeletalMesh);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the preview actor
|
||||
UpdatePreviewActor();
|
||||
}
|
||||
|
||||
AActor* SDismembermentPreviewViewport::GetPreviewActor() const
|
||||
{
|
||||
return PreviewActor;
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::RefreshViewport()
|
||||
{
|
||||
// Invalidate the viewport
|
||||
if (ViewportClient.IsValid())
|
||||
{
|
||||
ViewportClient->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FEditorViewportClient> SDismembermentPreviewViewport::MakeEditorViewportClient()
|
||||
{
|
||||
// Create the viewport client
|
||||
ViewportClient = MakeShareable(new SDismembermentPreviewViewportClient(PreviewScene.Get(), SharedThis(this)));
|
||||
|
||||
// Set the preview manager
|
||||
ViewportClient->SetPreviewManager(PreviewManager);
|
||||
|
||||
// Set up the viewport client
|
||||
ViewportClient->SetViewLocation(FVector(0.0f, 0.0f, 200.0f));
|
||||
ViewportClient->SetViewRotation(FRotator(-20.0f, 0.0f, 0.0f));
|
||||
ViewportClient->SetViewLocationForOrbiting(FVector(0.0f, 0.0f, 0.0f));
|
||||
ViewportClient->bSetListenerPosition = false;
|
||||
ViewportClient->EngineShowFlags.SetPostProcessing(true);
|
||||
ViewportClient->EngineShowFlags.SetLighting(true);
|
||||
ViewportClient->EngineShowFlags.SetIndirectLightingCache(true);
|
||||
ViewportClient->EngineShowFlags.SetSeparateTranslucency(true);
|
||||
ViewportClient->EngineShowFlags.SetTemporalAA(true);
|
||||
ViewportClient->EngineShowFlags.SetGrid(false);
|
||||
ViewportClient->EngineShowFlags.SetAtmosphere(true);
|
||||
ViewportClient->EngineShowFlags.SetSkeletalMeshes(true);
|
||||
ViewportClient->EngineShowFlags.SetDecals(true);
|
||||
ViewportClient->EngineShowFlags.SetParticles(true);
|
||||
ViewportClient->EngineShowFlags.SetVolumetricFog(true);
|
||||
ViewportClient->EngineShowFlags.SetDynamicShadows(true);
|
||||
ViewportClient->EngineShowFlags.SetSkyLighting(true);
|
||||
ViewportClient->EngineShowFlags.SetAmbientOcclusion(true);
|
||||
ViewportClient->EngineShowFlags.SetScreenSpaceReflections(true);
|
||||
ViewportClient->EngineShowFlags.SetAntiAliasing(true);
|
||||
ViewportClient->EngineShowFlags.SetMotionBlur(false);
|
||||
ViewportClient->EngineShowFlags.SetBounds(false);
|
||||
ViewportClient->EngineShowFlags.SetCollision(false);
|
||||
ViewportClient->EngineShowFlags.SetBSP(false);
|
||||
ViewportClient->EngineShowFlags.SetFog(true);
|
||||
ViewportClient->EngineShowFlags.SetStaticMeshes(true);
|
||||
ViewportClient->EngineShowFlags.SetLandscape(true);
|
||||
ViewportClient->EngineShowFlags.SetTranslucency(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedFoliage(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedGrass(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedStaticMeshes(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedFoliage(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedGrass(true);
|
||||
ViewportClient->EngineShowFlags.SetInstancedStaticMeshes(true);
|
||||
ViewportClient->EngineShowFlags.SetSplines(true);
|
||||
ViewportClient->EngineShowFlags.SetSelectionOutline(true);
|
||||
ViewportClient->EngineShowFlags.SetMeshEdges(false);
|
||||
ViewportClient->EngineShowFlags.SetVertexColors(false);
|
||||
ViewportClient->EngineShowFlags.SetLightComplexity(false);
|
||||
ViewportClient->EngineShowFlags.SetShaderComplexity(false);
|
||||
ViewportClient->EngineShowFlags.SetStaticMeshLODColoration(false);
|
||||
ViewportClient->EngineShowFlags.SetLightMapDensity(false);
|
||||
ViewportClient->EngineShowFlags.SetLightInfluences(false);
|
||||
ViewportClient->EngineShowFlags.SetOptimizeVizibility(false);
|
||||
ViewportClient->EngineShowFlags.SetTextRender(true);
|
||||
ViewportClient->EngineShowFlags.SetTestImage(false);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeLightCulling(false);
|
||||
ViewportClient->EngineShowFlags.SetPrecomputedVisibility(true);
|
||||
ViewportClient->EngineShowFlags.SetPrecomputedVisibilityCells(false);
|
||||
ViewportClient->EngineShowFlags.SetVolumes(false);
|
||||
ViewportClient->EngineShowFlags.SetGame(false);
|
||||
ViewportClient->EngineShowFlags.SetBSPTriangles(false);
|
||||
ViewportClient->EngineShowFlags.SetHitProxies(false);
|
||||
ViewportClient->EngineShowFlags.SetGBufferHints(false);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeShadingModels(false);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeAdaptiveDOF(false);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeSSR(false);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeSSS(false);
|
||||
ViewportClient->EngineShowFlags.SetVolumetricLightmap(true);
|
||||
ViewportClient->EngineShowFlags.SetVisualizeOutOfBoundsPixels(false);
|
||||
ViewportClient->EngineShowFlags.SetHighResScreenshotMask(false);
|
||||
ViewportClient->EngineShowFlags.SetHMDDistortion(false);
|
||||
ViewportClient->EngineShowFlags.SetStereoRendering(false);
|
||||
ViewportClient->EngineShowFlags.SetTonemapper(true);
|
||||
ViewportClient->EngineShowFlags.SetLumenReflections(true);
|
||||
ViewportClient->EngineShowFlags.SetLumenGlobalIllumination(true);
|
||||
ViewportClient->EngineShowFlags.SetVirtualShadowMaps(true);
|
||||
ViewportClient->EngineShowFlags.SetNanite(true);
|
||||
|
||||
return ViewportClient.ToSharedRef();
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::OnFocusViewportToSelection()
|
||||
{
|
||||
// Focus the viewport on the preview actor
|
||||
if (ViewportClient.IsValid() && PreviewActor)
|
||||
{
|
||||
ViewportClient->FocusViewportOnBox(PreviewActor->GetComponentsBoundingBox());
|
||||
}
|
||||
}
|
||||
|
||||
bool SDismembermentPreviewViewport::IsVisible() const
|
||||
{
|
||||
return SEditorViewport::IsVisible();
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::CreatePreviewActor()
|
||||
{
|
||||
// Clean up any existing preview actor
|
||||
if (PreviewActor)
|
||||
{
|
||||
if (PreviewActor->GetWorld())
|
||||
{
|
||||
PreviewActor->Destroy();
|
||||
}
|
||||
PreviewActor = nullptr;
|
||||
}
|
||||
|
||||
// Create a new preview actor
|
||||
if (PreviewScene.IsValid())
|
||||
{
|
||||
UWorld* World = PreviewScene->GetWorld();
|
||||
if (World)
|
||||
{
|
||||
PreviewActor = World->SpawnActor<ADismembermentPreviewActor>();
|
||||
if (PreviewActor)
|
||||
{
|
||||
// Add the actor to the preview scene
|
||||
PreviewScene->AddComponent(PreviewActor->GetRootComponent(), FTransform::Identity);
|
||||
|
||||
// Update the preview manager
|
||||
if (PreviewManager)
|
||||
{
|
||||
PreviewManager->SetTargetActor(PreviewActor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewport::UpdatePreviewActor()
|
||||
{
|
||||
// Update the preview manager
|
||||
if (PreviewManager && PreviewActor)
|
||||
{
|
||||
PreviewManager->SetTargetActor(PreviewActor);
|
||||
}
|
||||
|
||||
// Refresh the viewport
|
||||
RefreshViewport();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SDismembermentPreviewViewportClient
|
||||
|
||||
SDismembermentPreviewViewportClient::SDismembermentPreviewViewportClient(FPreviewScene* InPreviewScene, const TWeakPtr<SDismembermentPreviewViewport>& InViewportWidget)
|
||||
: FEditorViewportClient(nullptr, InPreviewScene, InViewportWidget)
|
||||
, ViewportWidget(InViewportWidget)
|
||||
, PreviewManager(nullptr)
|
||||
{
|
||||
// Set up the viewport client
|
||||
SetRealtime(true);
|
||||
bSetListenerPosition = false;
|
||||
bShouldCheckHitProxy = true;
|
||||
bShowGrid = false;
|
||||
bDisableInput = false;
|
||||
bAllowMatineePreview = false;
|
||||
bUsingOrbitCamera = true;
|
||||
bForceInitialFocus = true;
|
||||
bShowBounds = false;
|
||||
bShowBoundsActors = false;
|
||||
bShowFloor = true;
|
||||
bShowBoxes = false;
|
||||
bShowWireframe = false;
|
||||
bShowCollision = false;
|
||||
bShowSockets = true;
|
||||
bDrawAxes = false;
|
||||
bShowNormals = false;
|
||||
bShowTangents = false;
|
||||
bShowBinormals = false;
|
||||
bShowConstraints = false;
|
||||
bShowCameras = false;
|
||||
bShowLightRadius = false;
|
||||
bShowLightVolumes = false;
|
||||
bShowLightInfluences = false;
|
||||
bShowLightFrustums = false;
|
||||
bShowShadowFrustums = false;
|
||||
bShowLightingOnlyOverlap = false;
|
||||
bShowLightingVisualization = false;
|
||||
bShowLightingStats = false;
|
||||
bShowShadowDensity = false;
|
||||
bShowPhysicalMaterialMasks = false;
|
||||
bShowSpriteSockets = false;
|
||||
bShowParticleSystemComponents = true;
|
||||
bShowParticleSystems = true;
|
||||
bShowLOD = false;
|
||||
bShowHUD = false;
|
||||
bShowDebugInfo = false;
|
||||
bDrawPreviewShadowsInGame = false;
|
||||
bEnableDirectLightMap = true;
|
||||
bEnableIndirectLightMap = true;
|
||||
bEnableColorizeDistancefieldLightingMask = false;
|
||||
bEnableColorizeDistanceFieldLightingMaskGrayscale = false;
|
||||
bEnableColorizeDistanceFieldLightingMaskColor = false;
|
||||
bEnableColorizeDistanceFieldOcclusion = false;
|
||||
bEnableColorizeDistanceFieldOcclusionGrayscale = false;
|
||||
bEnableColorizeDistanceFieldOcclusionColor = false;
|
||||
bEnableColorizeDistanceFieldLightingMaskGrayscale = false;
|
||||
bEnableColorizeDistanceFieldLightingMaskColor = false;
|
||||
bEnableColorizeDistanceFieldOcclusionGrayscale = false;
|
||||
bEnableColorizeDistanceFieldOcclusionColor = false;
|
||||
bEnableColorizeDistanceFieldLightingMask = false;
|
||||
bEnableColorizeDistanceFieldOcclusion = false;
|
||||
bEnableColorizeDistanceFieldGradient = false;
|
||||
bEnableColorizeDistanceFieldGradientGrayscale = false;
|
||||
bEnableColorizeDistanceFieldGradientColor = false;
|
||||
bEnableColorizeDistanceFieldGradientGrayscale = false;
|
||||
bEnableColorizeDistanceFieldGradientColor = false;
|
||||
bEnableColorizeDistanceFieldGradient = false;
|
||||
bEnableColorizeDistanceField = false;
|
||||
bEnableColorizeDistanceFieldGrayscale = false;
|
||||
bEnableColorizeDistanceFieldColor = false;
|
||||
bEnableColorizeDistanceFieldGrayscale = false;
|
||||
bEnableColorizeDistanceFieldColor = false;
|
||||
bEnableColorizeDistanceField = false;
|
||||
bEnableColorizeDistanceFieldMeshSDF = false;
|
||||
bEnableColorizeDistanceFieldMeshSDFGrayscale = false;
|
||||
bEnableColorizeDistanceFieldMeshSDFColor = false;
|
||||
bEnableColorizeDistanceFieldMeshSDFGrayscale = false;
|
||||
bEnableColorizeDistanceFieldMeshSDFColor = false;
|
||||
bEnableColorizeDistanceFieldMeshSDF = false;
|
||||
bEnableColorizeVolumetricLightmap = false;
|
||||
bEnableColorizeVolumetricLightmapDensity = false;
|
||||
bEnableColorizeVolumetricLightmapSamples = false;
|
||||
bEnableColorizeGlobalDistanceField = false;
|
||||
bEnableColorizeGlobalDistanceFieldGrayscale = false;
|
||||
bEnableColorizeGlobalDistanceFieldColor = false;
|
||||
bEnableColorizeGlobalDistanceFieldGrayscale = false;
|
||||
bEnableColorizeGlobalDistanceFieldColor = false;
|
||||
bEnableColorizeGlobalDistanceField = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDF = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDFGrayscale = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDFColor = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDFGrayscale = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDFColor = false;
|
||||
bEnableColorizeGlobalDistanceFieldMeshSDF = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusion = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusionGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusionColor = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusionGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusionColor = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalOcclusion = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapDirectionalIndirectIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapEnvironmentIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand1 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand2 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand3 = false;
|
||||
}
|
||||
|
||||
SDismembermentPreviewViewportClient::~SDismembermentPreviewViewportClient()
|
||||
{
|
||||
// Clean up
|
||||
PreviewManager = nullptr;
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewportClient::Tick(float DeltaSeconds)
|
||||
{
|
||||
// Call parent tick
|
||||
FEditorViewportClient::Tick(DeltaSeconds);
|
||||
|
||||
// Tick the preview manager
|
||||
if (PreviewManager)
|
||||
{
|
||||
PreviewManager->Tick(DeltaSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI)
|
||||
{
|
||||
// Call parent draw
|
||||
FEditorViewportClient::Draw(View, PDI);
|
||||
|
||||
// Draw preview elements
|
||||
if (PreviewManager)
|
||||
{
|
||||
// TODO: Draw preview elements
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewportClient::DrawCanvas(FViewport& InViewport, FSceneView& View, FCanvas& Canvas)
|
||||
{
|
||||
// Call parent draw canvas
|
||||
FEditorViewportClient::DrawCanvas(InViewport, View, Canvas);
|
||||
|
||||
// Draw preview elements
|
||||
if (PreviewManager)
|
||||
{
|
||||
// TODO: Draw preview elements
|
||||
}
|
||||
}
|
||||
|
||||
void SDismembermentPreviewViewportClient::SetPreviewManager(UDismembermentPreviewManager* InPreviewManager)
|
||||
{
|
||||
PreviewManager = InPreviewManager;
|
||||
}
|
546
Source/FLESHEditor/Private/FLESHEditor.cpp
Normal file
546
Source/FLESHEditor/Private/FLESHEditor.cpp
Normal file
@@ -0,0 +1,546 @@
|
||||
#include "FLESHEditor.h"
|
||||
#include "FLESHEditorCommands.h"
|
||||
#include "FLESHEditorStyle.h"
|
||||
#include "MatrixInputWidget.h"
|
||||
#include "DismembermentGraph/DismembermentGraphEditor.h"
|
||||
#include "Framework/Docking/TabManager.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Widgets/Layout/SBorder.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "PropertyEditorModule.h"
|
||||
#include "EditorStyleSet.h"
|
||||
#include "LevelEditor.h"
|
||||
#include "ToolMenus.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FLESHEditor"
|
||||
|
||||
const FName FFLESHEditor::ViewportTabId(TEXT("FLESHEditor_Viewport"));
|
||||
const FName FFLESHEditor::DetailsTabId(TEXT("FLESHEditor_Details"));
|
||||
const FName FFLESHEditor::AssetBrowserTabId(TEXT("FLESHEditor_AssetBrowser"));
|
||||
const FName FFLESHEditor::MatrixEditorTabId(TEXT("FLESHEditor_MatrixEditor"));
|
||||
const FName FFLESHEditor::GraphEditorTabId(TEXT("FLESHEditor_GraphEditor"));
|
||||
const FName FFLESHEditor::ToolbarTabId(TEXT("FLESHEditor_Toolbar"));
|
||||
|
||||
FFLESHEditor::FFLESHEditor()
|
||||
{
|
||||
}
|
||||
|
||||
FFLESHEditor::~FFLESHEditor()
|
||||
{
|
||||
}
|
||||
|
||||
void FFLESHEditor::InitFLESHEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost)
|
||||
{
|
||||
// Create command list
|
||||
CreateCommandList();
|
||||
|
||||
// Create tab layout
|
||||
const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("FLESHEditorLayout_v1.0")
|
||||
->AddArea
|
||||
(
|
||||
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.1f)
|
||||
->AddTab(ToolbarTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
|
||||
->SetSizeCoefficient(0.7f)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.7f)
|
||||
->AddTab(ViewportTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.3f)
|
||||
->AddTab(MatrixEditorTabId, ETabState::OpenedTab)
|
||||
)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
|
||||
->SetSizeCoefficient(0.3f)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.6f)
|
||||
->AddTab(DetailsTabId, ETabState::OpenedTab)
|
||||
)
|
||||
->Split
|
||||
(
|
||||
FTabManager::NewStack()
|
||||
->SetSizeCoefficient(0.4f)
|
||||
->AddTab(AssetBrowserTabId, ETabState::OpenedTab)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Initialize toolkit
|
||||
const bool bCreateDefaultStandaloneMenu = true;
|
||||
const bool bCreateDefaultToolbar = true;
|
||||
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, FName("FLESHEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar);
|
||||
}
|
||||
|
||||
void FFLESHEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
|
||||
|
||||
// Register tab spawners
|
||||
InTabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Viewport))
|
||||
.SetDisplayName(LOCTEXT("ViewportTab", "Viewport"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(DetailsTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Details))
|
||||
.SetDisplayName(LOCTEXT("DetailsTab", "Details"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(AssetBrowserTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_AssetBrowser))
|
||||
.SetDisplayName(LOCTEXT("AssetBrowserTab", "Asset Browser"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.TabIcon"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(MatrixEditorTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_MatrixEditor))
|
||||
.SetDisplayName(LOCTEXT("MatrixEditorTab", "Matrix Editor"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "PropertyEditor.Grid.TabIcon"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(GraphEditorTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_GraphEditor))
|
||||
.SetDisplayName(LOCTEXT("GraphEditorTab", "Graph Editor"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x"));
|
||||
|
||||
InTabManager->RegisterTabSpawner(ToolbarTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Toolbar))
|
||||
.SetDisplayName(LOCTEXT("ToolbarTab", "Toolbar"))
|
||||
.SetGroup(WorkspaceMenuCategory.ToSharedRef())
|
||||
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Toolbar"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
||||
{
|
||||
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
||||
|
||||
// Unregister tab spawners
|
||||
InTabManager->UnregisterTabSpawner(ViewportTabId);
|
||||
InTabManager->UnregisterTabSpawner(DetailsTabId);
|
||||
InTabManager->UnregisterTabSpawner(AssetBrowserTabId);
|
||||
InTabManager->UnregisterTabSpawner(MatrixEditorTabId);
|
||||
InTabManager->UnregisterTabSpawner(GraphEditorTabId);
|
||||
InTabManager->UnregisterTabSpawner(ToolbarTabId);
|
||||
}
|
||||
|
||||
FName FFLESHEditor::GetToolkitFName() const
|
||||
{
|
||||
return FName("FLESHEditor");
|
||||
}
|
||||
|
||||
FText FFLESHEditor::GetBaseToolkitName() const
|
||||
{
|
||||
return LOCTEXT("FLESHEditorAppLabel", "FLESH Editor");
|
||||
}
|
||||
|
||||
FString FFLESHEditor::GetWorldCentricTabPrefix() const
|
||||
{
|
||||
return TEXT("FLESH ");
|
||||
}
|
||||
|
||||
FLinearColor FFLESHEditor::GetWorldCentricTabColorScale() const
|
||||
{
|
||||
return FLinearColor(0.7f, 0.0f, 0.0f, 0.5f);
|
||||
}
|
||||
|
||||
void FFLESHEditor::OpenEditor()
|
||||
{
|
||||
// Create new editor instance
|
||||
TSharedRef<FFLESHEditor> NewEditor = MakeShareable(new FFLESHEditor());
|
||||
NewEditor->InitFLESHEditor(EToolkitMode::Standalone, nullptr);
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_Viewport(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("ViewportTitle", "Viewport"))
|
||||
[
|
||||
CreateViewportWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_Details(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("DetailsTitle", "Details"))
|
||||
[
|
||||
CreateDetailsWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_AssetBrowser(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("AssetBrowserTitle", "Asset Browser"))
|
||||
[
|
||||
CreateAssetBrowserWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_MatrixEditor(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("MatrixEditorTitle", "Matrix Editor"))
|
||||
[
|
||||
CreateMatrixEditorWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_GraphEditor(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("GraphEditorTitle", "Graph Editor"))
|
||||
[
|
||||
CreateGraphEditorWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_Toolbar(const FSpawnTabArgs& Args)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.Label(LOCTEXT("ToolbarTitle", "Toolbar"))
|
||||
[
|
||||
CreateToolbarWidget()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateViewportWidget()
|
||||
{
|
||||
// Create viewport widget
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("ViewportHeader", "FLESH Viewport"))
|
||||
.Font(FEditorStyle::GetFontStyle("HeadingFont"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("ViewportPlaceholder", "Character model and dismemberment effects will be displayed here"))
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateDetailsWidget()
|
||||
{
|
||||
// Create details panel
|
||||
FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
FDetailsViewArgs DetailsViewArgs;
|
||||
DetailsViewArgs.bUpdatesFromSelection = true;
|
||||
DetailsViewArgs.bLockable = false;
|
||||
DetailsViewArgs.bAllowSearch = true;
|
||||
DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea;
|
||||
DetailsViewArgs.bHideSelectionTip = true;
|
||||
DetailsViewArgs.NotifyHook = nullptr;
|
||||
DetailsViewArgs.bSearchInitialKeyFocus = false;
|
||||
DetailsViewArgs.ViewIdentifier = NAME_None;
|
||||
DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic;
|
||||
DetailsViewArgs.bShowOptions = true;
|
||||
DetailsViewArgs.bAllowMultipleTopLevelObjects = true;
|
||||
DetailsWidget = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
|
||||
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("DetailsHeader", "Property Editor"))
|
||||
.Font(FEditorStyle::GetFontStyle("HeadingFont"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
DetailsWidget.ToSharedRef()
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateAssetBrowserWidget()
|
||||
{
|
||||
// Create asset browser
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("AssetBrowserHeader", "Asset Browser"))
|
||||
.Font(FEditorStyle::GetFontStyle("HeadingFont"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("ImportCharacterModel", "Import Character Model"))
|
||||
.OnClicked_Lambda([this]() { OnImportCharacterModel(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("ImportOrganModel", "Import Organ Model"))
|
||||
.OnClicked_Lambda([this]() { OnImportOrganModel(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("ImportSkeletonModel", "Import Skeleton Model"))
|
||||
.OnClicked_Lambda([this]() { OnImportSkeletonModel(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("ImportPhysicsAsset", "Import Physics Asset"))
|
||||
.OnClicked_Lambda([this]() { OnImportPhysicsAsset(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateMatrixEditorWidget()
|
||||
{
|
||||
// Create matrix editor
|
||||
MatrixEditorWidget = SNew(SMatrixInputWidget)
|
||||
.Matrix(FMatrix::Identity)
|
||||
.OnMatrixChanged_Lambda([this](const FMatrix& NewMatrix) {
|
||||
// Handle matrix change
|
||||
});
|
||||
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("MatrixEditorHeader", "Matrix Editor"))
|
||||
.Font(FEditorStyle::GetFontStyle("HeadingFont"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
MatrixEditorWidget.ToSharedRef()
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("TestMatrix", "Test Matrix"))
|
||||
.OnClicked_Lambda([this]() { OnTestMatrix(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateGraphEditorWidget()
|
||||
{
|
||||
// Create graph editor
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("GraphEditorHeader", "Graph Editor"))
|
||||
.Font(FEditorStyle::GetFontStyle("HeadingFont"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("GraphEditorPlaceholder", "Dismemberment system logic graph will be displayed here"))
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFLESHEditor::CreateToolbarWidget()
|
||||
{
|
||||
// Create toolbar
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(4.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("DismembermentGraphEditor", "Dismemberment Graph Editor"))
|
||||
.OnClicked_Lambda([this]() { OnOpenDismembermentGraphEditor(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("AnatomicalLayerEditor", "Anatomical Layer Editor"))
|
||||
.OnClicked_Lambda([this]() { OnOpenAnatomicalLayerEditor(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("BooleanCutTool", "Boolean Cut Tool"))
|
||||
.OnClicked_Lambda([this]() { OnOpenBooleanCutTool(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("BloodSystemEditor", "Blood System Editor"))
|
||||
.OnClicked_Lambda([this]() { OnOpenBloodSystemEditor(); return FReply::Handled(); })
|
||||
.ButtonStyle(FFLESHEditorStyle::Get(), "FLESHEditor.RoundedButton")
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
void FFLESHEditor::CreateCommandList()
|
||||
{
|
||||
// Create command list
|
||||
CommandList = MakeShareable(new FUICommandList);
|
||||
|
||||
// Bind commands
|
||||
CommandList->MapAction(
|
||||
FFLESHEditorCommands::Get().OpenDismembermentGraphEditor,
|
||||
FExecuteAction::CreateSP(this, &FFLESHEditor::OnOpenDismembermentGraphEditor),
|
||||
FCanExecuteAction());
|
||||
|
||||
CommandList->MapAction(
|
||||
FFLESHEditorCommands::Get().OpenAnatomicalLayerEditor,
|
||||
FExecuteAction::CreateSP(this, &FFLESHEditor::OnOpenAnatomicalLayerEditor),
|
||||
FCanExecuteAction());
|
||||
|
||||
CommandList->MapAction(
|
||||
FFLESHEditorCommands::Get().OpenBooleanCutTool,
|
||||
FExecuteAction::CreateSP(this, &FFLESHEditor::OnOpenBooleanCutTool),
|
||||
FCanExecuteAction());
|
||||
|
||||
CommandList->MapAction(
|
||||
FFLESHEditorCommands::Get().OpenBloodSystemEditor,
|
||||
FExecuteAction::CreateSP(this, &FFLESHEditor::OnOpenBloodSystemEditor),
|
||||
FCanExecuteAction());
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnOpenDismembermentGraphEditor()
|
||||
{
|
||||
// Open dismemberment graph editor
|
||||
FGlobalTabmanager::Get()->TryInvokeTab(GraphEditorTabId);
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnOpenAnatomicalLayerEditor()
|
||||
{
|
||||
// Open anatomical layer editor
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("AnatomicalLayerEditorNotImplemented", "Anatomical Layer Editor is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnOpenBooleanCutTool()
|
||||
{
|
||||
// Open boolean cut tool
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("BooleanCutToolNotImplemented", "Boolean Cut Tool is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnOpenBloodSystemEditor()
|
||||
{
|
||||
// Open blood system editor
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("BloodSystemEditorNotImplemented", "Blood System Editor is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnImportCharacterModel()
|
||||
{
|
||||
// Import character model
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ImportCharacterModelNotImplemented", "Import Character Model feature is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnImportOrganModel()
|
||||
{
|
||||
// Import organ model
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ImportOrganModelNotImplemented", "Import Organ Model feature is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnImportSkeletonModel()
|
||||
{
|
||||
// Import skeleton model
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ImportSkeletonModelNotImplemented", "Import Skeleton Model feature is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnImportPhysicsAsset()
|
||||
{
|
||||
// Import physics asset
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ImportPhysicsAssetNotImplemented", "Import Physics Asset feature is not implemented yet"));
|
||||
}
|
||||
|
||||
void FFLESHEditor::OnTestMatrix()
|
||||
{
|
||||
// Test matrix
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("TestMatrixNotImplemented", "Test Matrix feature is not implemented yet"));
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
14
Source/FLESHEditor/Private/FLESHEditorCommands.cpp
Normal file
14
Source/FLESHEditor/Private/FLESHEditorCommands.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "FLESHEditorCommands.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FLESHEditorCommands"
|
||||
|
||||
void FFLESHEditorCommands::RegisterCommands()
|
||||
{
|
||||
UI_COMMAND(OpenFLESHEditor, "FLESH", "Open FLESH Dismemberment System Editor", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenDismembermentGraphEditor, "Dismemberment Graph", "Open Dismemberment System Graph Editor", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenAnatomicalLayerEditor, "Anatomical Layer", "Open Anatomical Layer Editor", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenBooleanCutTool, "Boolean Cut", "Open Boolean Cut Tool", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenBloodSystemEditor, "Blood System", "Open Blood System Editor", EUserInterfaceActionType::Button, FInputChord());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
74
Source/FLESHEditor/Private/FLESHEditorModule.cpp
Normal file
74
Source/FLESHEditor/Private/FLESHEditorModule.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "FLESHEditorModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "FLESHEditorStyle.h"
|
||||
#include "FLESHEditorCommands.h"
|
||||
#include "FLESHEditor.h"
|
||||
#include "LevelEditor.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FFLESHEditorModule"
|
||||
|
||||
void FFLESHEditorModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory
|
||||
// The exact timing is specified in the .uplugin file per-module
|
||||
|
||||
// Initialize style
|
||||
FFLESHEditorStyle::Initialize();
|
||||
FFLESHEditorStyle::ReloadTextures();
|
||||
|
||||
// Register commands
|
||||
FFLESHEditorCommands::Register();
|
||||
|
||||
// Map commands
|
||||
PluginCommands = MakeShareable(new FUICommandList);
|
||||
PluginCommands->MapAction(
|
||||
FFLESHEditorCommands::Get().OpenFLESHEditor,
|
||||
FExecuteAction::CreateRaw(this, &FFLESHEditorModule::OpenFLESHEditor),
|
||||
FCanExecuteAction());
|
||||
|
||||
// Register editor menus
|
||||
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateLambda([this]()
|
||||
{
|
||||
// Add menu items
|
||||
FToolMenuOwnerScoped OwnerScoped(this);
|
||||
|
||||
// Add to main menu
|
||||
{
|
||||
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
|
||||
FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
|
||||
Section.AddMenuEntryWithCommandList(FFLESHEditorCommands::Get().OpenFLESHEditor, PluginCommands);
|
||||
}
|
||||
|
||||
// Add to toolbar
|
||||
{
|
||||
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar");
|
||||
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
|
||||
FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFLESHEditorCommands::Get().OpenFLESHEditor));
|
||||
Entry.SetCommandList(PluginCommands);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void FFLESHEditorModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module
|
||||
// For modules that support dynamic reloading, we call this function before unloading the module
|
||||
|
||||
// Unregister style
|
||||
FFLESHEditorStyle::Shutdown();
|
||||
|
||||
// Unregister editor menus
|
||||
UToolMenus::UnRegisterStartupCallback(this);
|
||||
UToolMenus::UnregisterOwner(this);
|
||||
}
|
||||
|
||||
void FFLESHEditorModule::OpenFLESHEditor()
|
||||
{
|
||||
// Open FLESH editor
|
||||
FFLESHEditor::OpenEditor();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FFLESHEditorModule, FLESHEditor)
|
92
Source/FLESHEditor/Private/FLESHEditorStyle.cpp
Normal file
92
Source/FLESHEditor/Private/FLESHEditorStyle.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "FLESHEditorStyle.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "Styling/SlateTypes.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "SlateOptMacros.h"
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FFLESHEditorStyle::StyleInstance = nullptr;
|
||||
|
||||
void FFLESHEditorStyle::Initialize()
|
||||
{
|
||||
if (!StyleInstance.IsValid())
|
||||
{
|
||||
StyleInstance = Create();
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void FFLESHEditorStyle::Shutdown()
|
||||
{
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
|
||||
ensure(StyleInstance.IsUnique());
|
||||
StyleInstance.Reset();
|
||||
}
|
||||
|
||||
void FFLESHEditorStyle::ReloadTextures()
|
||||
{
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
|
||||
}
|
||||
}
|
||||
|
||||
FName FFLESHEditorStyle::GetStyleSetName()
|
||||
{
|
||||
static FName StyleSetName(TEXT("FLESHEditorStyle"));
|
||||
return StyleSetName;
|
||||
}
|
||||
|
||||
const ISlateStyle& FFLESHEditorStyle::Get()
|
||||
{
|
||||
return *StyleInstance;
|
||||
}
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
TSharedRef<FSlateStyleSet> FFLESHEditorStyle::Create()
|
||||
{
|
||||
TSharedRef<FSlateStyleSet> StyleRef = MakeShareable(new FSlateStyleSet(GetStyleSetName()));
|
||||
StyleRef->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("FLESH"))->GetBaseDir() / TEXT("Resources"));
|
||||
|
||||
// Button styles
|
||||
const FVector2D Icon16x16(16.0f, 16.0f);
|
||||
const FVector2D Icon20x20(20.0f, 20.0f);
|
||||
const FVector2D Icon40x40(40.0f, 40.0f);
|
||||
const FVector2D Icon64x64(64.0f, 64.0f);
|
||||
const FVector2D Icon128x128(128.0f, 128.0f);
|
||||
|
||||
// Main FLESH editor button
|
||||
StyleRef->Set("FLESHEditor.OpenFLESHEditor", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon40x40));
|
||||
StyleRef->Set("FLESHEditor.OpenFLESHEditor.Small", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon20x20));
|
||||
|
||||
// Dismemberment graph editor button
|
||||
StyleRef->Set("FLESHEditor.OpenDismembermentGraphEditor", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon40x40));
|
||||
StyleRef->Set("FLESHEditor.OpenDismembermentGraphEditor.Small", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon20x20));
|
||||
|
||||
// Anatomical layer editor button
|
||||
StyleRef->Set("FLESHEditor.OpenAnatomicalLayerEditor", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon40x40));
|
||||
StyleRef->Set("FLESHEditor.OpenAnatomicalLayerEditor.Small", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon20x20));
|
||||
|
||||
// Boolean cut tool button
|
||||
StyleRef->Set("FLESHEditor.OpenBooleanCutTool", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon40x40));
|
||||
StyleRef->Set("FLESHEditor.OpenBooleanCutTool.Small", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon20x20));
|
||||
|
||||
// Blood system editor button
|
||||
StyleRef->Set("FLESHEditor.OpenBloodSystemEditor", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon40x40));
|
||||
StyleRef->Set("FLESHEditor.OpenBloodSystemEditor.Small", new FSlateImageBrush(StyleRef->RootToContentDir(TEXT("Icon128.png")), Icon20x20));
|
||||
|
||||
// Button style - rounded corners
|
||||
const FButtonStyle RoundedButtonStyle = FButtonStyle()
|
||||
.SetNormal(FSlateRoundedBoxBrush(FLinearColor(0.05f, 0.05f, 0.05f, 1.0f), 8.0f))
|
||||
.SetHovered(FSlateRoundedBoxBrush(FLinearColor(0.1f, 0.1f, 0.1f, 1.0f), 8.0f))
|
||||
.SetPressed(FSlateRoundedBoxBrush(FLinearColor(0.2f, 0.2f, 0.2f, 1.0f), 8.0f))
|
||||
.SetNormalPadding(FMargin(4, 2, 4, 2))
|
||||
.SetPressedPadding(FMargin(4, 3, 4, 1));
|
||||
|
||||
StyleRef->Set("FLESHEditor.RoundedButton", RoundedButtonStyle);
|
||||
|
||||
return StyleRef;
|
||||
}
|
||||
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
223
Source/FLESHEditor/Private/MatrixInputWidget.cpp
Normal file
223
Source/FLESHEditor/Private/MatrixInputWidget.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#include "MatrixInputWidget.h"
|
||||
#include "SlateOptMacros.h"
|
||||
#include "Widgets/Layout/SUniformGridPanel.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "EditorStyleSet.h"
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
void SMatrixInputWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
Matrix = InArgs._Matrix;
|
||||
OnMatrixChanged = InArgs._OnMatrixChanged;
|
||||
|
||||
// Create a grid panel for the matrix elements
|
||||
GridPanel = SNew(SGridPanel);
|
||||
|
||||
// Create numeric entry boxes for each matrix element
|
||||
NumericEntryBoxes.SetNum(16); // 4x4 matrix
|
||||
|
||||
// Add labels for rows and columns
|
||||
for (int32 Col = 0; Col < 4; ++Col)
|
||||
{
|
||||
GridPanel->AddSlot(Col + 1, 0)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::Format(FText::FromString("Column {0}"), FText::AsNumber(Col)))
|
||||
.Margin(FMargin(5.0f))
|
||||
];
|
||||
}
|
||||
|
||||
for (int32 Row = 0; Row < 4; ++Row)
|
||||
{
|
||||
GridPanel->AddSlot(0, Row + 1)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::Format(FText::FromString("Row {0}"), FText::AsNumber(Row)))
|
||||
.Margin(FMargin(5.0f))
|
||||
];
|
||||
}
|
||||
|
||||
// Add numeric entry boxes for each matrix element
|
||||
for (int32 Row = 0; Row < 4; ++Row)
|
||||
{
|
||||
for (int32 Col = 0; Col < 4; ++Col)
|
||||
{
|
||||
TSharedPtr<SNumericEntryBox<float>> NumericEntryBox = CreateMatrixElementWidget(Row, Col);
|
||||
NumericEntryBoxes[Row * 4 + Col] = NumericEntryBox;
|
||||
|
||||
GridPanel->AddSlot(Col + 1, Row + 1)
|
||||
[
|
||||
SNew(SBox)
|
||||
.WidthOverride(80.0f)
|
||||
.Padding(FMargin(2.0f))
|
||||
[
|
||||
NumericEntryBox.ToSharedRef()
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Create buttons for common operations
|
||||
TSharedPtr<SUniformGridPanel> ButtonPanel = SNew(SUniformGridPanel)
|
||||
.SlotPadding(FMargin(2.0f));
|
||||
|
||||
ButtonPanel->AddSlot(0, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Reset to Identity"))
|
||||
.ToolTipText(FText::FromString("Reset the matrix to identity"))
|
||||
.OnClicked(FOnClicked::CreateLambda([this]()
|
||||
{
|
||||
ResetToIdentity();
|
||||
return FReply::Handled();
|
||||
}))
|
||||
];
|
||||
|
||||
ButtonPanel->AddSlot(1, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Set from Rotation"))
|
||||
.ToolTipText(FText::FromString("Set the matrix from rotation values (degrees)"))
|
||||
.OnClicked(FOnClicked::CreateLambda([this]()
|
||||
{
|
||||
SetFromEulerAngles(0.0f, 0.0f, 0.0f);
|
||||
return FReply::Handled();
|
||||
}))
|
||||
];
|
||||
|
||||
ButtonPanel->AddSlot(2, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Set from Translation"))
|
||||
.ToolTipText(FText::FromString("Set the matrix from translation values"))
|
||||
.OnClicked(FOnClicked::CreateLambda([this]()
|
||||
{
|
||||
SetFromTranslation(FVector::ZeroVector);
|
||||
return FReply::Handled();
|
||||
}))
|
||||
];
|
||||
|
||||
// Main widget layout
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5.0f)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Matrix Input"))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 14))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
.Padding(5.0f)
|
||||
[
|
||||
SNew(SScrollBox)
|
||||
+ SScrollBox::Slot()
|
||||
[
|
||||
GridPanel.ToSharedRef()
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5.0f)
|
||||
[
|
||||
ButtonPanel.ToSharedRef()
|
||||
]
|
||||
];
|
||||
|
||||
// Update the UI with the initial matrix value
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::SetMatrix(const FMatrix& InMatrix)
|
||||
{
|
||||
Matrix = InMatrix;
|
||||
UpdateUI();
|
||||
|
||||
// Notify listeners of the change
|
||||
if (OnMatrixChanged.IsBound())
|
||||
{
|
||||
OnMatrixChanged.Execute(Matrix);
|
||||
}
|
||||
}
|
||||
|
||||
FMatrix SMatrixInputWidget::GetMatrix() const
|
||||
{
|
||||
return Matrix;
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::ResetToIdentity()
|
||||
{
|
||||
SetMatrix(FMatrix::Identity);
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::SetFromEulerAngles(float Roll, float Pitch, float Yaw)
|
||||
{
|
||||
FRotator Rotator(Pitch, Yaw, Roll);
|
||||
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
|
||||
SetMatrix(RotationMatrix);
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::SetFromTranslation(const FVector& Translation)
|
||||
{
|
||||
FMatrix TranslationMatrix = FMatrix::Identity;
|
||||
TranslationMatrix.SetOrigin(Translation);
|
||||
SetMatrix(TranslationMatrix);
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::SetFromTransform(const FTransform& Transform)
|
||||
{
|
||||
SetMatrix(Transform.ToMatrixWithScale());
|
||||
}
|
||||
|
||||
TSharedPtr<SNumericEntryBox<float>> SMatrixInputWidget::CreateMatrixElementWidget(int32 Row, int32 Col)
|
||||
{
|
||||
return SNew(SNumericEntryBox<float>)
|
||||
.Value_Lambda([this, Row, Col]() -> float
|
||||
{
|
||||
return Matrix.M[Row][Col];
|
||||
})
|
||||
.OnValueChanged(SNumericEntryBox<float>::FOnValueChanged::CreateSP(this, &SMatrixInputWidget::OnMatrixElementChanged, Row, Col))
|
||||
.AllowSpin(true)
|
||||
.Delta(0.1f)
|
||||
.MinValue(-10000.0f)
|
||||
.MaxValue(10000.0f)
|
||||
.MinSliderValue(-10.0f)
|
||||
.MaxSliderValue(10.0f);
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::OnMatrixElementChanged(float NewValue, int32 Row, int32 Col)
|
||||
{
|
||||
// Update the matrix element
|
||||
Matrix.M[Row][Col] = NewValue;
|
||||
|
||||
// Notify listeners of the change
|
||||
if (OnMatrixChanged.IsBound())
|
||||
{
|
||||
OnMatrixChanged.Execute(Matrix);
|
||||
}
|
||||
}
|
||||
|
||||
void SMatrixInputWidget::UpdateUI()
|
||||
{
|
||||
// Update each numeric entry box with the current matrix values
|
||||
for (int32 Row = 0; Row < 4; ++Row)
|
||||
{
|
||||
for (int32 Col = 0; Col < 4; ++Col)
|
||||
{
|
||||
int32 Index = Row * 4 + Col;
|
||||
if (NumericEntryBoxes.IsValidIndex(Index) && NumericEntryBoxes[Index].IsValid())
|
||||
{
|
||||
NumericEntryBoxes[Index]->SetValue(Matrix.M[Row][Col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
153
Source/FLESHEditor/Public/AnatomicalStructureBrush.h
Normal file
153
Source/FLESHEditor/Public/AnatomicalStructureBrush.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "AnatomicalStructureBrush.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class USkeletalMesh;
|
||||
class UStaticMesh;
|
||||
|
||||
/**
|
||||
* Anatomical brush type enumeration
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EAnatomicalBrushType : uint8
|
||||
{
|
||||
Bone, // Bone brush
|
||||
Muscle, // Muscle brush
|
||||
Organ, // Organ brush
|
||||
Vessel, // Blood vessel brush
|
||||
Nerve, // Nerve brush
|
||||
Custom // Custom brush
|
||||
};
|
||||
|
||||
/**
|
||||
* Anatomical brush settings
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FAnatomicalBrushSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Brush type
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush")
|
||||
EAnatomicalBrushType BrushType = EAnatomicalBrushType::Bone;
|
||||
|
||||
// Brush size
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (ClampMin = "0.1", ClampMax = "100.0"))
|
||||
float BrushSize = 10.0f;
|
||||
|
||||
// Brush strength
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float BrushStrength = 0.5f;
|
||||
|
||||
// Brush falloff
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float BrushFalloff = 0.5f;
|
||||
|
||||
// Brush material
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush")
|
||||
TObjectPtr<UMaterialInterface> BrushMaterial = nullptr;
|
||||
|
||||
// Brush mesh
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush")
|
||||
TObjectPtr<UStaticMesh> BrushMesh = nullptr;
|
||||
|
||||
// Enable physics
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush")
|
||||
bool bEnablePhysics = true;
|
||||
|
||||
// Physics density
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (EditCondition = "bEnablePhysics"))
|
||||
float PhysicsDensity = 1.0f;
|
||||
|
||||
// Physics elasticity
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (EditCondition = "bEnablePhysics"))
|
||||
float PhysicsElasticity = 0.5f;
|
||||
|
||||
// Fracture threshold
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Brush", meta = (EditCondition = "bEnablePhysics"))
|
||||
float FractureThreshold = 100.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Anatomical structure brush tool class
|
||||
* Used for creating and editing anatomical structures in the editor
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class FLESHEDITOR_API UAnatomicalStructureBrush : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UAnatomicalStructureBrush();
|
||||
|
||||
/**
|
||||
* Initialize the brush
|
||||
* @param InSettings - Brush settings
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
void Initialize(const FAnatomicalBrushSettings& InSettings);
|
||||
|
||||
/**
|
||||
* Apply brush to skeletal mesh
|
||||
* @param TargetMesh - Target skeletal mesh
|
||||
* @param Location - Application location
|
||||
* @param Direction - Application direction
|
||||
* @param BoneName - Bone name, if specified, only apply to this bone
|
||||
* @return Whether the application was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
bool ApplyToSkeletalMesh(USkeletalMesh* TargetMesh, const FVector& Location, const FVector& Direction, FName BoneName = NAME_None);
|
||||
|
||||
/**
|
||||
* Apply brush to static mesh
|
||||
* @param TargetMesh - Target static mesh
|
||||
* @param Location - Application location
|
||||
* @param Direction - Application direction
|
||||
* @return Whether the application was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
bool ApplyToStaticMesh(UStaticMesh* TargetMesh, const FVector& Location, const FVector& Direction);
|
||||
|
||||
/**
|
||||
* Set brush type
|
||||
* @param BrushType - Brush type
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
void SetBrushType(EAnatomicalBrushType BrushType);
|
||||
|
||||
/**
|
||||
* Set brush size
|
||||
* @param Size - Brush size
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
void SetBrushSize(float Size);
|
||||
|
||||
/**
|
||||
* Set brush strength
|
||||
* @param Strength - Brush strength
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
void SetBrushStrength(float Strength);
|
||||
|
||||
/**
|
||||
* Get brush settings
|
||||
* @return Current brush settings
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Brush")
|
||||
FAnatomicalBrushSettings GetBrushSettings() const;
|
||||
|
||||
private:
|
||||
// Brush settings
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Brush")
|
||||
FAnatomicalBrushSettings BrushSettings;
|
||||
|
||||
// Internal function for creating anatomical structure
|
||||
UStaticMesh* CreateAnatomicalStructure(const FVector& Location, const FVector& Direction, float Size);
|
||||
|
||||
// Internal function for applying physics properties
|
||||
void ApplyPhysicsProperties(UStaticMesh* Mesh);
|
||||
};
|
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "DismembermentCompiler.generated.h"
|
||||
|
||||
class UDismembermentGraphNode;
|
||||
class UDismembermentGraph;
|
||||
|
||||
/**
|
||||
* Compiled node data structure
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FCompiledNodeData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Node reference
|
||||
UPROPERTY()
|
||||
TObjectPtr<UDismembermentGraphNode> Node;
|
||||
|
||||
// Input nodes
|
||||
UPROPERTY()
|
||||
TArray<int32> InputNodeIndices;
|
||||
|
||||
// Output nodes
|
||||
UPROPERTY()
|
||||
TArray<int32> OutputNodeIndices;
|
||||
|
||||
// Execution order index
|
||||
int32 ExecutionOrder;
|
||||
|
||||
// Constructor
|
||||
FCompiledNodeData()
|
||||
: Node(nullptr)
|
||||
, ExecutionOrder(-1)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment compiler class
|
||||
* Compiles a dismemberment graph into executable logic
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentCompiler : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UDismembermentCompiler();
|
||||
|
||||
/**
|
||||
* Compile a dismemberment graph
|
||||
* @param InGraph - The graph to compile
|
||||
* @return True if compilation was successful
|
||||
*/
|
||||
bool CompileGraph(UDismembermentGraph* InGraph);
|
||||
|
||||
/**
|
||||
* Get the compiled node data
|
||||
* @return Array of compiled node data
|
||||
*/
|
||||
const TArray<FCompiledNodeData>& GetCompiledNodeData() const { return CompiledNodeData; }
|
||||
|
||||
/**
|
||||
* Get the execution order
|
||||
* @return Array of node indices in execution order
|
||||
*/
|
||||
const TArray<int32>& GetExecutionOrder() const { return ExecutionOrder; }
|
||||
|
||||
/**
|
||||
* Add a bone selection
|
||||
* @param BoneName - Name of the bone to select
|
||||
*/
|
||||
void AddBoneSelection(const FName& BoneName);
|
||||
|
||||
/**
|
||||
* Add a cut operation
|
||||
* @param Location - Location of the cut
|
||||
* @param Direction - Direction of the cut
|
||||
* @param Width - Width of the cut
|
||||
* @param Depth - Depth of the cut
|
||||
* @param Material - Material to use for the cut surface
|
||||
*/
|
||||
void AddCutOperation(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material);
|
||||
|
||||
/**
|
||||
* Add a blood effect
|
||||
* @param Location - Location of the blood effect
|
||||
* @param BloodEffect - Niagara system for the blood effect
|
||||
* @param BloodAmount - Amount of blood
|
||||
* @param BloodPressure - Blood pressure
|
||||
* @param CreateBloodPool - Whether to create a blood pool
|
||||
* @param BloodPoolSize - Size of the blood pool
|
||||
* @param BloodPoolMaterial - Material for the blood pool
|
||||
*/
|
||||
void AddBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure, bool CreateBloodPool, float BloodPoolSize, UMaterialInterface* BloodPoolMaterial);
|
||||
|
||||
/**
|
||||
* Add a physics simulation
|
||||
* @param Mass - Mass of the object
|
||||
* @param LinearDamping - Linear damping
|
||||
* @param AngularDamping - Angular damping
|
||||
* @param EnableGravity - Whether to enable gravity
|
||||
* @param SimulatePhysics - Whether to simulate physics
|
||||
* @param GenerateOverlapEvents - Whether to generate overlap events
|
||||
* @param PhysicalMaterial - Physical material to use
|
||||
* @param ImpulseForce - Force of the impulse
|
||||
* @param ImpulseRadius - Radius of the impulse
|
||||
*/
|
||||
void AddPhysicsSimulation(float Mass, float LinearDamping, float AngularDamping, bool EnableGravity, bool SimulatePhysics, bool GenerateOverlapEvents, UPhysicalMaterial* PhysicalMaterial, float ImpulseForce, float ImpulseRadius);
|
||||
|
||||
/**
|
||||
* Add an organ
|
||||
* @param OrganMesh - Mesh for the organ
|
||||
* @param OrganMaterial - Material for the organ
|
||||
* @param AttachBoneName - Name of the bone to attach to
|
||||
* @param RelativeLocation - Relative location
|
||||
* @param RelativeRotation - Relative rotation
|
||||
* @param RelativeScale - Relative scale
|
||||
* @param SimulatePhysics - Whether to simulate physics
|
||||
* @param DamageMultiplier - Damage multiplier
|
||||
* @param IsCriticalOrgan - Whether this is a critical organ
|
||||
* @param BloodAmount - Amount of blood
|
||||
*/
|
||||
void AddOrgan(UStaticMesh* OrganMesh, UMaterialInterface* OrganMaterial, const FName& AttachBoneName, const FVector& RelativeLocation, const FRotator& RelativeRotation, const FVector& RelativeScale, bool SimulatePhysics, float DamageMultiplier, bool IsCriticalOrgan, float BloodAmount);
|
||||
|
||||
/**
|
||||
* Add a wound effect
|
||||
* @param WoundSize - Size of the wound
|
||||
* @param WoundDepth - Depth of the wound
|
||||
* @param WoundMaterial - Material for the wound
|
||||
* @param WoundEffect - Effect for the wound
|
||||
* @param CreateDecal - Whether to create a decal
|
||||
* @param DecalMaterial - Material for the decal
|
||||
* @param DecalSize - Size of the decal
|
||||
* @param DecalLifetime - Lifetime of the decal
|
||||
* @param AffectBoneHealth - Whether to affect bone health
|
||||
* @param BoneDamage - Amount of bone damage
|
||||
*/
|
||||
void AddWoundEffect(float WoundSize, float WoundDepth, UMaterialInterface* WoundMaterial, UNiagaraSystem* WoundEffect, bool CreateDecal, UMaterialInterface* DecalMaterial, float DecalSize, float DecalLifetime, bool AffectBoneHealth, float BoneDamage);
|
||||
|
||||
private:
|
||||
// The graph being compiled
|
||||
UPROPERTY()
|
||||
TObjectPtr<UDismembermentGraph> Graph;
|
||||
|
||||
// Compiled node data
|
||||
UPROPERTY()
|
||||
TArray<FCompiledNodeData> CompiledNodeData;
|
||||
|
||||
// Execution order
|
||||
UPROPERTY()
|
||||
TArray<int32> ExecutionOrder;
|
||||
|
||||
// Bone selections
|
||||
UPROPERTY()
|
||||
TArray<FName> BoneSelections;
|
||||
|
||||
// Cut operations
|
||||
UPROPERTY()
|
||||
TArray<FTransform> CutOperations;
|
||||
|
||||
// Cut materials
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UMaterialInterface>> CutMaterials;
|
||||
|
||||
// Cut widths
|
||||
UPROPERTY()
|
||||
TArray<float> CutWidths;
|
||||
|
||||
// Cut depths
|
||||
UPROPERTY()
|
||||
TArray<float> CutDepths;
|
||||
|
||||
// Blood effects
|
||||
UPROPERTY()
|
||||
TArray<FTransform> BloodEffectTransforms;
|
||||
|
||||
// Blood effect systems
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UNiagaraSystem>> BloodEffectSystems;
|
||||
|
||||
// Blood amounts
|
||||
UPROPERTY()
|
||||
TArray<float> BloodAmounts;
|
||||
|
||||
// Blood pressures
|
||||
UPROPERTY()
|
||||
TArray<float> BloodPressures;
|
||||
|
||||
// Create blood pools
|
||||
UPROPERTY()
|
||||
TArray<bool> CreateBloodPools;
|
||||
|
||||
// Blood pool sizes
|
||||
UPROPERTY()
|
||||
TArray<float> BloodPoolSizes;
|
||||
|
||||
// Blood pool materials
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UMaterialInterface>> BloodPoolMaterials;
|
||||
|
||||
// Topological sort the nodes
|
||||
bool TopologicalSort();
|
||||
|
||||
// Visit node for topological sort
|
||||
void VisitNode(int32 NodeIndex, TArray<bool>& Visited, TArray<bool>& TempMark, TArray<int32>& SortedNodes, bool& bHasCycle);
|
||||
};
|
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "DismembermentCompiler.h"
|
||||
#include "DismembermentExecutor.generated.h"
|
||||
|
||||
class AActor;
|
||||
class USkeletalMeshComponent;
|
||||
class UDismembermentCompiler;
|
||||
|
||||
/**
|
||||
* Dismemberment executor class
|
||||
* Executes a compiled dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentExecutor : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UDismembermentExecutor();
|
||||
|
||||
/**
|
||||
* Initialize the executor with a compiler
|
||||
* @param InCompiler - The compiler containing the compiled graph
|
||||
*/
|
||||
void Initialize(UDismembermentCompiler* InCompiler);
|
||||
|
||||
/**
|
||||
* Execute the compiled graph on a target actor
|
||||
* @param TargetActor - The actor to apply the dismemberment effects to
|
||||
* @return True if execution was successful
|
||||
*/
|
||||
bool Execute(AActor* TargetActor);
|
||||
|
||||
/**
|
||||
* Get the target actor
|
||||
* @return The target actor
|
||||
*/
|
||||
AActor* GetTargetActor() const { return TargetActor; }
|
||||
|
||||
/**
|
||||
* Get the target skeletal mesh component
|
||||
* @return The target skeletal mesh component
|
||||
*/
|
||||
USkeletalMeshComponent* GetTargetSkeletalMesh() const { return TargetSkeletalMesh; }
|
||||
|
||||
/**
|
||||
* Get the selected bones
|
||||
* @return Array of selected bone names
|
||||
*/
|
||||
const TArray<FName>& GetSelectedBones() const { return SelectedBones; }
|
||||
|
||||
/**
|
||||
* Add a bone to the selection
|
||||
* @param BoneName - Name of the bone to add
|
||||
*/
|
||||
void AddSelectedBone(const FName& BoneName);
|
||||
|
||||
/**
|
||||
* Apply a cut to the target
|
||||
* @param Location - Location of the cut
|
||||
* @param Direction - Direction of the cut
|
||||
* @param Width - Width of the cut
|
||||
* @param Depth - Depth of the cut
|
||||
* @param Material - Material to use for the cut surface
|
||||
* @return True if the cut was successful
|
||||
*/
|
||||
bool ApplyCut(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material);
|
||||
|
||||
/**
|
||||
* Spawn a blood effect
|
||||
* @param Location - Location of the blood effect
|
||||
* @param BloodEffect - Niagara system for the blood effect
|
||||
* @param BloodAmount - Amount of blood
|
||||
* @param BloodPressure - Blood pressure
|
||||
* @param CreateBloodPool - Whether to create a blood pool
|
||||
* @param BloodPoolSize - Size of the blood pool
|
||||
* @param BloodPoolMaterial - Material for the blood pool
|
||||
* @return True if the blood effect was created successfully
|
||||
*/
|
||||
bool SpawnBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure, bool CreateBloodPool, float BloodPoolSize, UMaterialInterface* BloodPoolMaterial);
|
||||
|
||||
/**
|
||||
* Apply physics simulation
|
||||
* @param Mass - Mass of the object
|
||||
* @param LinearDamping - Linear damping
|
||||
* @param AngularDamping - Angular damping
|
||||
* @param EnableGravity - Whether to enable gravity
|
||||
* @param SimulatePhysics - Whether to simulate physics
|
||||
* @param GenerateOverlapEvents - Whether to generate overlap events
|
||||
* @param PhysicalMaterial - Physical material to use
|
||||
* @param ImpulseForce - Force of the impulse
|
||||
* @param ImpulseRadius - Radius of the impulse
|
||||
* @return True if the physics simulation was applied successfully
|
||||
*/
|
||||
bool ApplyPhysics(float Mass, float LinearDamping, float AngularDamping, bool EnableGravity, bool SimulatePhysics, bool GenerateOverlapEvents, UPhysicalMaterial* PhysicalMaterial, float ImpulseForce, float ImpulseRadius);
|
||||
|
||||
/**
|
||||
* Spawn an organ
|
||||
* @param OrganMesh - Mesh for the organ
|
||||
* @param OrganMaterial - Material for the organ
|
||||
* @param AttachBoneName - Name of the bone to attach to
|
||||
* @param RelativeLocation - Relative location
|
||||
* @param RelativeRotation - Relative rotation
|
||||
* @param RelativeScale - Relative scale
|
||||
* @param SimulatePhysics - Whether to simulate physics
|
||||
* @param DamageMultiplier - Damage multiplier
|
||||
* @param IsCriticalOrgan - Whether this is a critical organ
|
||||
* @param BloodAmount - Amount of blood
|
||||
* @return True if the organ was spawned successfully
|
||||
*/
|
||||
bool SpawnOrgan(UStaticMesh* OrganMesh, UMaterialInterface* OrganMaterial, const FName& AttachBoneName, const FVector& RelativeLocation, const FRotator& RelativeRotation, const FVector& RelativeScale, bool SimulatePhysics, float DamageMultiplier, bool IsCriticalOrgan, float BloodAmount);
|
||||
|
||||
/**
|
||||
* Apply a wound effect
|
||||
* @param WoundSize - Size of the wound
|
||||
* @param WoundDepth - Depth of the wound
|
||||
* @param WoundMaterial - Material for the wound
|
||||
* @param WoundEffect - Effect for the wound
|
||||
* @param CreateDecal - Whether to create a decal
|
||||
* @param DecalMaterial - Material for the decal
|
||||
* @param DecalSize - Size of the decal
|
||||
* @param DecalLifetime - Lifetime of the decal
|
||||
* @param AffectBoneHealth - Whether to affect bone health
|
||||
* @param BoneDamage - Amount of bone damage
|
||||
* @return True if the wound effect was applied successfully
|
||||
*/
|
||||
bool ApplyWoundEffect(float WoundSize, float WoundDepth, UMaterialInterface* WoundMaterial, UNiagaraSystem* WoundEffect, bool CreateDecal, UMaterialInterface* DecalMaterial, float DecalSize, float DecalLifetime, bool AffectBoneHealth, float BoneDamage);
|
||||
|
||||
private:
|
||||
// The compiler containing the compiled graph
|
||||
UPROPERTY()
|
||||
TObjectPtr<UDismembermentCompiler> Compiler;
|
||||
|
||||
// The target actor
|
||||
UPROPERTY()
|
||||
TObjectPtr<AActor> TargetActor;
|
||||
|
||||
// The target skeletal mesh component
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> TargetSkeletalMesh;
|
||||
|
||||
// Selected bones
|
||||
UPROPERTY()
|
||||
TArray<FName> SelectedBones;
|
||||
|
||||
// Find the target skeletal mesh component
|
||||
bool FindTargetSkeletalMesh();
|
||||
|
||||
// Execute a node
|
||||
bool ExecuteNode(int32 NodeIndex);
|
||||
};
|
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "DismembermentGraph.generated.h"
|
||||
|
||||
/**
|
||||
* Dismemberment graph for visual logic design
|
||||
* Allows for node-based editing of dismemberment system logic
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraph : public UEdGraph
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraph();
|
||||
|
||||
// The asset that owns this graph
|
||||
UPROPERTY()
|
||||
class UDismembermentGraphAsset* OwningAsset;
|
||||
};
|
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "GraphEditor.h"
|
||||
|
||||
class UDismembermentGraphAsset;
|
||||
class UDismembermentGraph;
|
||||
class SDockTab;
|
||||
|
||||
/**
|
||||
* Dismemberment graph editor
|
||||
* Provides a Mutable-like node editor for dismemberment system logic
|
||||
*/
|
||||
class FLESHEDITOR_API FDismembermentGraphEditor : public FAssetEditorToolkit
|
||||
{
|
||||
public:
|
||||
FDismembermentGraphEditor();
|
||||
virtual ~FDismembermentGraphEditor();
|
||||
|
||||
// Initialize the editor
|
||||
void InitDismembermentGraphEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, UDismembermentGraphAsset* InAsset);
|
||||
|
||||
// FAssetEditorToolkit interface
|
||||
virtual void RegisterTabSpawners(const TSharedRef<FTabManager>& TabManager) override;
|
||||
virtual void UnregisterTabSpawners(const TSharedRef<FTabManager>& TabManager) override;
|
||||
virtual FName GetToolkitFName() const override;
|
||||
virtual FText GetBaseToolkitName() const override;
|
||||
virtual FString GetWorldCentricTabPrefix() const override;
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
// End of FAssetEditorToolkit interface
|
||||
|
||||
// Get the edited asset
|
||||
UDismembermentGraphAsset* GetEditedAsset() const { return EditedAsset; }
|
||||
|
||||
// Get the graph editor widget
|
||||
TSharedRef<SGraphEditor> GetGraphEditor() const { return GraphEditorWidget.ToSharedRef(); }
|
||||
|
||||
private:
|
||||
// The asset being edited
|
||||
UDismembermentGraphAsset* EditedAsset;
|
||||
|
||||
// The graph editor widget
|
||||
TSharedPtr<SGraphEditor> GraphEditorWidget;
|
||||
|
||||
// Tab spawners
|
||||
TSharedRef<SDockTab> SpawnTab_GraphCanvas(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_Properties(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_Palette(const FSpawnTabArgs& Args);
|
||||
|
||||
// Create graph editor widget
|
||||
TSharedRef<SGraphEditor> CreateGraphEditorWidget();
|
||||
|
||||
// Graph editor commands
|
||||
void CreateCommandList();
|
||||
TSharedPtr<FUICommandList> GraphEditorCommands;
|
||||
|
||||
// Command handlers
|
||||
void SelectAllNodes();
|
||||
void DeleteSelectedNodes();
|
||||
void CutSelectedNodes();
|
||||
void CopySelectedNodes();
|
||||
void PasteNodes();
|
||||
void DuplicateSelectedNodes();
|
||||
|
||||
// Graph changed handler
|
||||
void OnGraphChanged(const FEdGraphEditAction& Action);
|
||||
|
||||
// Node selection changed handler
|
||||
void OnSelectedNodesChanged(const TSet<UObject*>& NewSelection);
|
||||
|
||||
// Compile the graph
|
||||
void CompileGraph();
|
||||
|
||||
// Properties panel
|
||||
TSharedPtr<class IDetailsView> PropertiesWidget;
|
||||
|
||||
// Node palette
|
||||
TSharedPtr<class SDismembermentGraphPalette> PaletteWidget;
|
||||
};
|
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "DismembermentGraphEditorFactory.generated.h"
|
||||
|
||||
/**
|
||||
* Factory for creating dismemberment graph assets
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphEditorFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphEditorFactory();
|
||||
|
||||
// UFactory interface
|
||||
virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
virtual bool ShouldShowInNewMenu() const override;
|
||||
// End of UFactory interface
|
||||
};
|
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "DismembermentGraphNode.generated.h"
|
||||
|
||||
/**
|
||||
* Base class for all dismemberment graph nodes
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNode : public UEdGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNode();
|
||||
|
||||
// Node title color
|
||||
UPROPERTY(EditAnywhere, Category = "Appearance")
|
||||
FLinearColor NodeTitleColor;
|
||||
|
||||
// Node category
|
||||
UPROPERTY(EditAnywhere, Category = "Category")
|
||||
FText NodeCategory;
|
||||
|
||||
// Node description
|
||||
UPROPERTY(EditAnywhere, Category = "Description")
|
||||
FText NodeDescription;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
virtual FLinearColor GetNodeTitleColor() const override;
|
||||
virtual FText GetTooltipText() const override;
|
||||
virtual FText GetMenuCategory() const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// Compile this node into executable logic
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler);
|
||||
|
||||
// Execute this node
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor);
|
||||
};
|
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodeBloodEffect.generated.h"
|
||||
|
||||
/**
|
||||
* Node for creating blood effects in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodeBloodEffect : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodeBloodEffect();
|
||||
|
||||
// Blood effect parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect")
|
||||
TObjectPtr<UNiagaraSystem> BloodEffect;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect")
|
||||
float BloodAmount;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect")
|
||||
float BloodPressure;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect")
|
||||
bool bCreateBloodPool;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect", meta = (EditCondition = "bCreateBloodPool"))
|
||||
float BloodPoolSize;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Blood Effect", meta = (EditCondition = "bCreateBloodPool"))
|
||||
TObjectPtr<UMaterialInterface> BloodPoolMaterial;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodeBoneSelect.generated.h"
|
||||
|
||||
/**
|
||||
* Node for selecting bones in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodeBoneSelect : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodeBoneSelect();
|
||||
|
||||
// Bone selection parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Bone Selection")
|
||||
TArray<FName> BoneNames;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Bone Selection")
|
||||
bool bUseRegex;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Bone Selection", meta = (EditCondition = "bUseRegex"))
|
||||
FString BoneNamePattern;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Bone Selection")
|
||||
bool bIncludeChildren;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodeCut.generated.h"
|
||||
|
||||
/**
|
||||
* Node for performing a cut operation in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodeCut : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodeCut();
|
||||
|
||||
// Cut parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Cut Parameters")
|
||||
float CutWidth;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Cut Parameters")
|
||||
float CutDepth;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Cut Parameters")
|
||||
bool bUseCustomMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Cut Parameters", meta = (EditCondition = "bUseCustomMaterial"))
|
||||
TObjectPtr<UMaterialInterface> CustomCutMaterial;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodeOrgan.generated.h"
|
||||
|
||||
/**
|
||||
* Node for organ simulation in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodeOrgan : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodeOrgan();
|
||||
|
||||
// Organ parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
TObjectPtr<UStaticMesh> OrganMesh;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
TObjectPtr<UMaterialInterface> OrganMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
FName AttachBoneName;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
FVector RelativeLocation;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
FRotator RelativeRotation;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
FVector RelativeScale;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
bool bSimulatePhysics;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
float DamageMultiplier;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
bool bIsCriticalOrgan;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Organ Parameters")
|
||||
float BloodAmount;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodePhysics.generated.h"
|
||||
|
||||
/**
|
||||
* Node for physics simulation in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodePhysics : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodePhysics();
|
||||
|
||||
// Physics parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
float Mass;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
float LinearDamping;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
float AngularDamping;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
bool bEnableGravity;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
bool bSimulatePhysics;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
bool bGenerateOverlapEvents;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
TObjectPtr<UPhysicalMaterial> PhysicalMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
float ImpulseForce;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Physics Parameters")
|
||||
float ImpulseRadius;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DismembermentGraphNode.h"
|
||||
#include "DismembermentGraphNodeWound.generated.h"
|
||||
|
||||
/**
|
||||
* Node for wound effects in the dismemberment graph
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphNodeWound : public UDismembermentGraphNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphNodeWound();
|
||||
|
||||
// Wound parameters
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
float WoundSize;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
float WoundDepth;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
TObjectPtr<UMaterialInterface> WoundMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
TObjectPtr<UNiagaraSystem> WoundEffect;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
bool bCreateDecal;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters", meta = (EditCondition = "bCreateDecal"))
|
||||
TObjectPtr<UMaterialInterface> DecalMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters", meta = (EditCondition = "bCreateDecal"))
|
||||
float DecalSize;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters", meta = (EditCondition = "bCreateDecal"))
|
||||
float DecalLifetime;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters")
|
||||
bool bAffectBoneHealth;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Wound Parameters", meta = (EditCondition = "bAffectBoneHealth"))
|
||||
float BoneDamage;
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UDismembermentGraphNode interface
|
||||
virtual void CompileNode(class FDismembermentCompiler* Compiler) override;
|
||||
virtual void ExecuteNode(class FDismembermentExecutor* Executor) override;
|
||||
// End of UDismembermentGraphNode interface
|
||||
};
|
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "Widgets/Views/STableRow.h"
|
||||
#include "Widgets/Views/STreeView.h"
|
||||
|
||||
class FDismembermentGraphEditor;
|
||||
|
||||
/**
|
||||
* Node category structure for the palette
|
||||
*/
|
||||
struct FDismembermentGraphNodeCategory
|
||||
{
|
||||
// Category name
|
||||
FText CategoryName;
|
||||
|
||||
// Child nodes
|
||||
TArray<TSharedPtr<FDismembermentGraphNodeCategory>> Children;
|
||||
|
||||
// Node classes in this category
|
||||
TArray<UClass*> NodeClasses;
|
||||
|
||||
// Constructor
|
||||
FDismembermentGraphNodeCategory(const FText& InCategoryName)
|
||||
: CategoryName(InCategoryName)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Node palette widget for the dismemberment graph editor
|
||||
*/
|
||||
class FLESHEDITOR_API SDismembermentGraphPalette : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SDismembermentGraphPalette) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs, TSharedPtr<FDismembermentGraphEditor> InGraphEditor);
|
||||
|
||||
private:
|
||||
// The graph editor that owns this palette
|
||||
TWeakPtr<FDismembermentGraphEditor> GraphEditor;
|
||||
|
||||
// Root categories
|
||||
TArray<TSharedPtr<FDismembermentGraphNodeCategory>> RootCategories;
|
||||
|
||||
// Tree view widget
|
||||
TSharedPtr<STreeView<TSharedPtr<FDismembermentGraphNodeCategory>>> CategoriesTreeView;
|
||||
|
||||
// Search box widget
|
||||
TSharedPtr<class SSearchBox> SearchBox;
|
||||
|
||||
// Search filter
|
||||
TSharedPtr<class FDismembermentGraphNodeSearchFilter> SearchFilter;
|
||||
|
||||
// Initialize the palette
|
||||
void InitializePalette();
|
||||
|
||||
// Create a category tree item
|
||||
TSharedRef<ITableRow> OnGenerateCategoryRow(TSharedPtr<FDismembermentGraphNodeCategory> Category, const TSharedRef<STableViewBase>& OwnerTable);
|
||||
|
||||
// Get child categories
|
||||
void OnGetCategoryChildren(TSharedPtr<FDismembermentGraphNodeCategory> Category, TArray<TSharedPtr<FDismembermentGraphNodeCategory>>& OutChildren);
|
||||
|
||||
// Category is expanded
|
||||
void OnCategoryExpansionChanged(TSharedPtr<FDismembermentGraphNodeCategory> Category, bool bExpanded);
|
||||
|
||||
// Search text changed
|
||||
void OnSearchTextChanged(const FText& InFilterText);
|
||||
|
||||
// Create a node from the palette
|
||||
FReply OnCreateNode(UClass* NodeClass, const FVector2D& ScreenPosition, const FVector2D& GraphPosition);
|
||||
|
||||
// Handle drag detected
|
||||
FReply OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, UClass* NodeClass);
|
||||
|
||||
// Create default categories
|
||||
void CreateDefaultCategories();
|
||||
|
||||
// Add a node class to the palette
|
||||
void AddNodeClassToCategory(UClass* NodeClass, const FText& CategoryName);
|
||||
};
|
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "DismembermentGraphSchema.generated.h"
|
||||
|
||||
class UDismembermentGraphNode;
|
||||
|
||||
/**
|
||||
* Connection response
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FDismembermentGraphConnectionResponse
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Response type
|
||||
UPROPERTY()
|
||||
int32 Response;
|
||||
|
||||
// Response text
|
||||
UPROPERTY()
|
||||
FText Message;
|
||||
|
||||
// Pin names that would be broken
|
||||
UPROPERTY()
|
||||
TArray<FText> BreakingPins;
|
||||
|
||||
// Constructor
|
||||
FDismembermentGraphConnectionResponse()
|
||||
: Response(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection response types
|
||||
*/
|
||||
struct FDismembermentGraphConnectionResponse_K2
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
// No error
|
||||
OK = 0,
|
||||
// Generic error
|
||||
ERROR_INCOMPATIBLE = 1,
|
||||
// Disallowed pin connection
|
||||
ERROR_DISALLOWED = 2,
|
||||
// Self-connection not allowed
|
||||
ERROR_SELF_CONNECTION = 3,
|
||||
// Cycle not allowed
|
||||
ERROR_CYCLE = 4
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Pin type
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FDismembermentGraphPinType
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Pin category
|
||||
UPROPERTY()
|
||||
FName PinCategory;
|
||||
|
||||
// Constructor
|
||||
FDismembermentGraphPinType()
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor with category
|
||||
FDismembermentGraphPinType(const FName& InPinCategory)
|
||||
: PinCategory(InPinCategory)
|
||||
{
|
||||
}
|
||||
|
||||
// Equality operator
|
||||
bool operator==(const FDismembermentGraphPinType& Other) const
|
||||
{
|
||||
return PinCategory == Other.PinCategory;
|
||||
}
|
||||
|
||||
// Inequality operator
|
||||
bool operator!=(const FDismembermentGraphPinType& Other) const
|
||||
{
|
||||
return !(*this == Other);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment graph schema
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentGraphSchema : public UEdGraphSchema
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Pin categories
|
||||
static const FName PC_Exec;
|
||||
static const FName PC_Bone;
|
||||
static const FName PC_Cut;
|
||||
static const FName PC_Blood;
|
||||
static const FName PC_Physics;
|
||||
static const FName PC_Organ;
|
||||
static const FName PC_Wound;
|
||||
|
||||
// UEdGraphSchema interface
|
||||
virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override;
|
||||
virtual void GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const override;
|
||||
virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override;
|
||||
virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override;
|
||||
virtual bool ShouldHidePinDefaultValue(UEdGraphPin* Pin) const override;
|
||||
virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override;
|
||||
virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override;
|
||||
virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const override;
|
||||
virtual void BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const override;
|
||||
virtual void DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const override;
|
||||
virtual void DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const override;
|
||||
virtual void DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const override;
|
||||
virtual void GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const override;
|
||||
virtual void GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const override;
|
||||
// End of UEdGraphSchema interface
|
||||
|
||||
/**
|
||||
* Check if two pins can be connected
|
||||
* @param PinA - First pin
|
||||
* @param PinB - Second pin
|
||||
* @param OutResponse - Connection response
|
||||
* @return True if the pins can be connected
|
||||
*/
|
||||
bool CanConnectPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, FDismembermentGraphConnectionResponse& OutResponse) const;
|
||||
|
||||
/**
|
||||
* Check if connecting two pins would create a cycle
|
||||
* @param PinA - First pin
|
||||
* @param PinB - Second pin
|
||||
* @return True if connecting the pins would create a cycle
|
||||
*/
|
||||
bool WouldCreateCycle(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const;
|
||||
|
||||
/**
|
||||
* Get the pin type from a pin
|
||||
* @param Pin - The pin to get the type from
|
||||
* @return The pin type
|
||||
*/
|
||||
static FDismembermentGraphPinType GetPinType(const UEdGraphPin* Pin);
|
||||
|
||||
/**
|
||||
* Get the pin type color
|
||||
* @param PinType - The pin type
|
||||
* @return The pin color
|
||||
*/
|
||||
static FLinearColor GetPinTypeColor(const FDismembermentGraphPinType& PinType);
|
||||
|
||||
/**
|
||||
* Create a new node
|
||||
* @param NodeClass - Class of the node to create
|
||||
* @param ParentGraph - Graph to create the node in
|
||||
* @param NodePosX - X position of the node
|
||||
* @param NodePosY - Y position of the node
|
||||
* @param bSelectNewNode - Whether to select the new node
|
||||
* @return The created node
|
||||
*/
|
||||
static UDismembermentGraphNode* CreateNode(TSubclassOf<UDismembermentGraphNode> NodeClass, UEdGraph* ParentGraph, float NodePosX, float NodePosY, bool bSelectNewNode = true);
|
||||
};
|
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "DismembermentPreviewManager.generated.h"
|
||||
|
||||
// Add a log category declaration
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogFLESHPreview, Log, All);
|
||||
|
||||
class UDismembermentGraphNode;
|
||||
class USkeletalMeshComponent;
|
||||
class AActor;
|
||||
class UWorld;
|
||||
class UNiagaraComponent;
|
||||
class UDecalComponent;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
/**
|
||||
* Preview manager for dismemberment effects
|
||||
* Handles real-time preview of dismemberment nodes
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESHEDITOR_API UDismembermentPreviewManager : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UDismembermentPreviewManager();
|
||||
|
||||
/**
|
||||
* Initialize the preview manager
|
||||
* @param InWorld - The world to create the preview in
|
||||
*/
|
||||
void Initialize(TObjectPtr<UWorld> InWorld);
|
||||
|
||||
/**
|
||||
* Clean up the preview manager
|
||||
*/
|
||||
void Cleanup();
|
||||
|
||||
/**
|
||||
* Set the target actor for preview
|
||||
* @param InActor - The actor to preview on
|
||||
*/
|
||||
void SetTargetActor(TObjectPtr<AActor> InActor);
|
||||
|
||||
/**
|
||||
* Preview a node
|
||||
* @param Node - The node to preview
|
||||
* @return True if the preview was successful
|
||||
*/
|
||||
bool PreviewNode(TObjectPtr<UDismembermentGraphNode> Node);
|
||||
|
||||
/**
|
||||
* Clear the current preview
|
||||
*/
|
||||
void ClearPreview();
|
||||
|
||||
/**
|
||||
* Update the preview
|
||||
* @param DeltaTime - Time since last update
|
||||
*/
|
||||
void Tick(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Get the target actor
|
||||
* @return The target actor
|
||||
*/
|
||||
TObjectPtr<AActor> GetTargetActor() const { return TargetActor; }
|
||||
|
||||
/**
|
||||
* Get the target skeletal mesh component
|
||||
* @return The target skeletal mesh component
|
||||
*/
|
||||
TObjectPtr<USkeletalMeshComponent> GetTargetSkeletalMesh() const { return TargetSkeletalMesh; }
|
||||
|
||||
private:
|
||||
// The world to create the preview in
|
||||
UPROPERTY()
|
||||
TObjectPtr<UWorld> World;
|
||||
|
||||
// The target actor
|
||||
UPROPERTY()
|
||||
TObjectPtr<AActor> TargetActor;
|
||||
|
||||
// The target skeletal mesh component
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> TargetSkeletalMesh;
|
||||
|
||||
// The currently previewed node
|
||||
UPROPERTY()
|
||||
TObjectPtr<UDismembermentGraphNode> PreviewedNode;
|
||||
|
||||
// Preview components
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UNiagaraComponent>> PreviewNiagaraComponents;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UDecalComponent>> PreviewDecalComponents;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UStaticMeshComponent>> PreviewStaticMeshComponents;
|
||||
|
||||
// Preview cut plane mesh
|
||||
UPROPERTY()
|
||||
TObjectPtr<UStaticMeshComponent> PreviewCutPlaneMesh;
|
||||
|
||||
// Preview bone selections
|
||||
UPROPERTY()
|
||||
TArray<FName> PreviewBoneSelections;
|
||||
|
||||
// Preview cut locations
|
||||
UPROPERTY()
|
||||
TArray<FTransform> PreviewCutTransforms;
|
||||
|
||||
// Preview blood effect locations
|
||||
UPROPERTY()
|
||||
TArray<FTransform> PreviewBloodEffectTransforms;
|
||||
|
||||
// Preview organ locations
|
||||
UPROPERTY()
|
||||
TArray<FTransform> PreviewOrganTransforms;
|
||||
|
||||
// Preview wound locations
|
||||
UPROPERTY()
|
||||
TArray<FTransform> PreviewWoundTransforms;
|
||||
|
||||
// Find the target skeletal mesh component
|
||||
bool FindTargetSkeletalMesh();
|
||||
|
||||
// Preview a cut node
|
||||
bool PreviewCutNode(TObjectPtr<class UDismembermentGraphNodeCut> CutNode);
|
||||
|
||||
// Preview a bone select node
|
||||
bool PreviewBoneSelectNode(TObjectPtr<class UDismembermentGraphNodeBoneSelect> BoneSelectNode);
|
||||
|
||||
// Preview a blood effect node
|
||||
bool PreviewBloodEffectNode(TObjectPtr<class UDismembermentGraphNodeBloodEffect> BloodEffectNode);
|
||||
|
||||
// Preview a physics node
|
||||
bool PreviewPhysicsNode(TObjectPtr<class UDismembermentGraphNodePhysics> PhysicsNode);
|
||||
|
||||
// Preview an organ node
|
||||
bool PreviewOrganNode(TObjectPtr<class UDismembermentGraphNodeOrgan> OrganNode);
|
||||
|
||||
// Preview a wound node
|
||||
bool PreviewWoundNode(TObjectPtr<class UDismembermentGraphNodeWound> WoundNode);
|
||||
|
||||
// Create a preview cut plane mesh
|
||||
TObjectPtr<UStaticMeshComponent> CreatePreviewCutPlaneMesh(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material);
|
||||
|
||||
// Create a preview blood effect
|
||||
TObjectPtr<UNiagaraComponent> CreatePreviewBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure);
|
||||
|
||||
// Create a preview blood pool
|
||||
TObjectPtr<UDecalComponent> CreatePreviewBloodPool(const FVector& Location, float Size, UMaterialInterface* Material);
|
||||
|
||||
// Create a preview organ
|
||||
TObjectPtr<UStaticMeshComponent> CreatePreviewOrgan(UStaticMesh* OrganMesh, UMaterialInterface* OrganMaterial, const FName& AttachBoneName, const FVector& RelativeLocation, const FRotator& RelativeRotation, const FVector& RelativeScale);
|
||||
|
||||
// Create a preview wound
|
||||
TObjectPtr<UDecalComponent> CreatePreviewWound(const FVector& Location, float Size, UMaterialInterface* Material);
|
||||
|
||||
// Clear all preview components
|
||||
void ClearPreviewComponents();
|
||||
};
|
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SGraphNode.h"
|
||||
|
||||
class UDismembermentGraphNode;
|
||||
|
||||
/**
|
||||
* Visual representation of a dismemberment graph node
|
||||
*/
|
||||
class FLESHEDITOR_API SDismembermentGraphNode : public SGraphNode
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SDismembermentGraphNode) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs, UEdGraphNode* InNode);
|
||||
|
||||
// SGraphNode interface
|
||||
virtual void UpdateGraphNode() override;
|
||||
virtual void CreatePinWidgets() override;
|
||||
virtual void AddPin(const TSharedRef<SGraphPin>& PinToAdd) override;
|
||||
virtual TSharedPtr<SToolTip> GetComplexTooltip() override;
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
|
||||
// End of SGraphNode interface
|
||||
|
||||
protected:
|
||||
// Get the dismemberment graph node
|
||||
UDismembermentGraphNode* GetDismembermentGraphNode() const;
|
||||
|
||||
// Get the node title widget
|
||||
TSharedRef<SWidget> GetNodeTitleWidget();
|
||||
|
||||
// Get the node body widget
|
||||
TSharedRef<SWidget> GetNodeBodyWidget();
|
||||
|
||||
// Get the node preview widget
|
||||
TSharedRef<SWidget> GetNodePreviewWidget();
|
||||
|
||||
// Node color
|
||||
FSlateColor GetNodeColor() const;
|
||||
|
||||
// Node title color
|
||||
FSlateColor GetNodeTitleColor() const;
|
||||
|
||||
// Node title text
|
||||
FText GetNodeTitle() const;
|
||||
|
||||
// Node category text
|
||||
FText GetNodeCategory() const;
|
||||
|
||||
// Node description text
|
||||
FText GetNodeDescription() const;
|
||||
|
||||
// Is the node selected
|
||||
bool IsNodeSelected() const;
|
||||
|
||||
// Is the node hovered
|
||||
bool IsNodeHovered() const;
|
||||
|
||||
private:
|
||||
// Is the node hovered
|
||||
bool bIsHovered;
|
||||
};
|
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "EditorViewportClient.h"
|
||||
#include "SEditorViewport.h"
|
||||
|
||||
class UDismembermentPreviewManager;
|
||||
class USkeletalMesh;
|
||||
class AActor;
|
||||
class FPreviewScene;
|
||||
class SDismembermentPreviewViewportClient;
|
||||
|
||||
/**
|
||||
* Viewport for previewing dismemberment effects
|
||||
*/
|
||||
class FLESHEDITOR_API SDismembermentPreviewViewport : public SEditorViewport
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SDismembermentPreviewViewport)
|
||||
{}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/**
|
||||
* Constructs the viewport widget
|
||||
*/
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~SDismembermentPreviewViewport();
|
||||
|
||||
/**
|
||||
* Set the preview manager
|
||||
* @param InPreviewManager - The preview manager to use
|
||||
*/
|
||||
void SetPreviewManager(UDismembermentPreviewManager* InPreviewManager);
|
||||
|
||||
/**
|
||||
* Set the preview skeletal mesh
|
||||
* @param InSkeletalMesh - The skeletal mesh to preview
|
||||
*/
|
||||
void SetPreviewSkeletalMesh(USkeletalMesh* InSkeletalMesh);
|
||||
|
||||
/**
|
||||
* Get the preview actor
|
||||
* @return The preview actor
|
||||
*/
|
||||
AActor* GetPreviewActor() const;
|
||||
|
||||
/**
|
||||
* Refresh the viewport
|
||||
*/
|
||||
void RefreshViewport();
|
||||
|
||||
protected:
|
||||
// SEditorViewport interface
|
||||
virtual TSharedRef<FEditorViewportClient> MakeEditorViewportClient() override;
|
||||
virtual void OnFocusViewportToSelection() override;
|
||||
virtual bool IsVisible() const override;
|
||||
// End of SEditorViewport interface
|
||||
|
||||
private:
|
||||
// The preview scene
|
||||
TSharedPtr<FPreviewScene> PreviewScene;
|
||||
|
||||
// The viewport client
|
||||
TSharedPtr<SDismembermentPreviewViewportClient> ViewportClient;
|
||||
|
||||
// The preview manager
|
||||
TObjectPtr<UDismembermentPreviewManager> PreviewManager;
|
||||
|
||||
// The preview actor
|
||||
TObjectPtr<AActor> PreviewActor;
|
||||
|
||||
// Create the preview actor
|
||||
void CreatePreviewActor();
|
||||
|
||||
// Update the preview actor
|
||||
void UpdatePreviewActor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Viewport client for previewing dismemberment effects
|
||||
*/
|
||||
class SDismembermentPreviewViewportClient : public FEditorViewportClient
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param InPreviewScene - The preview scene
|
||||
* @param InViewportWidget - The viewport widget
|
||||
*/
|
||||
SDismembermentPreviewViewportClient(FPreviewScene* InPreviewScene, const TWeakPtr<SDismembermentPreviewViewport>& InViewportWidget);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~SDismembermentPreviewViewportClient();
|
||||
|
||||
// FEditorViewportClient interface
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
virtual void Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) override;
|
||||
virtual void DrawCanvas(FViewport& InViewport, FSceneView& View, FCanvas& Canvas) override;
|
||||
// End of FEditorViewportClient interface
|
||||
|
||||
/**
|
||||
* Set the preview manager
|
||||
* @param InPreviewManager - The preview manager to use
|
||||
*/
|
||||
void SetPreviewManager(UDismembermentPreviewManager* InPreviewManager);
|
||||
|
||||
private:
|
||||
// The viewport widget
|
||||
TWeakPtr<SDismembermentPreviewViewport> ViewportWidget;
|
||||
|
||||
// The preview manager
|
||||
TObjectPtr<UDismembermentPreviewManager> PreviewManager;
|
||||
};
|
107
Source/FLESHEditor/Public/FLESHEditor.h
Normal file
107
Source/FLESHEditor/Public/FLESHEditor.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
|
||||
class SDockTab;
|
||||
class SGraphEditor;
|
||||
class SPropertyTreeView;
|
||||
class SAssetBrowser;
|
||||
class SMatrixInputWidget;
|
||||
|
||||
/**
|
||||
* FLESH Main Editor
|
||||
* Provides the main editing functionality for the dismemberment system
|
||||
*/
|
||||
class FLESHEDITOR_API FFLESHEditor : public FAssetEditorToolkit
|
||||
{
|
||||
public:
|
||||
FFLESHEditor();
|
||||
virtual ~FFLESHEditor();
|
||||
|
||||
// Initialize the editor
|
||||
void InitFLESHEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost);
|
||||
|
||||
// FAssetEditorToolkit interface
|
||||
virtual void RegisterTabSpawners(const TSharedRef<FTabManager>& TabManager) override;
|
||||
virtual void UnregisterTabSpawners(const TSharedRef<FTabManager>& TabManager) override;
|
||||
virtual FName GetToolkitFName() const override;
|
||||
virtual FText GetBaseToolkitName() const override;
|
||||
virtual FString GetWorldCentricTabPrefix() const override;
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
// End of FAssetEditorToolkit interface
|
||||
|
||||
// Open the editor
|
||||
static void OpenEditor();
|
||||
|
||||
private:
|
||||
// Tab spawners
|
||||
TSharedRef<SDockTab> SpawnTab_Viewport(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_Details(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_AssetBrowser(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_MatrixEditor(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_GraphEditor(const FSpawnTabArgs& Args);
|
||||
TSharedRef<SDockTab> SpawnTab_Toolbar(const FSpawnTabArgs& Args);
|
||||
|
||||
// Create viewport widget
|
||||
TSharedRef<SWidget> CreateViewportWidget();
|
||||
|
||||
// Create details panel
|
||||
TSharedRef<SWidget> CreateDetailsWidget();
|
||||
|
||||
// Create asset browser
|
||||
TSharedRef<SWidget> CreateAssetBrowserWidget();
|
||||
|
||||
// Create matrix editor
|
||||
TSharedRef<SWidget> CreateMatrixEditorWidget();
|
||||
|
||||
// Create graph editor
|
||||
TSharedRef<SWidget> CreateGraphEditorWidget();
|
||||
|
||||
// Create toolbar
|
||||
TSharedRef<SWidget> CreateToolbarWidget();
|
||||
|
||||
// Create command list
|
||||
void CreateCommandList();
|
||||
|
||||
// Command handlers
|
||||
void OnOpenDismembermentGraphEditor();
|
||||
void OnOpenAnatomicalLayerEditor();
|
||||
void OnOpenBooleanCutTool();
|
||||
void OnOpenBloodSystemEditor();
|
||||
void OnImportCharacterModel();
|
||||
void OnImportOrganModel();
|
||||
void OnImportSkeletonModel();
|
||||
void OnImportPhysicsAsset();
|
||||
void OnTestMatrix();
|
||||
|
||||
// Viewport widget
|
||||
TSharedPtr<class SViewport> ViewportWidget;
|
||||
|
||||
// Details panel
|
||||
TSharedPtr<class IDetailsView> DetailsWidget;
|
||||
|
||||
// Asset browser
|
||||
TSharedPtr<SAssetBrowser> AssetBrowserWidget;
|
||||
|
||||
// Matrix editor
|
||||
TSharedPtr<SMatrixInputWidget> MatrixEditorWidget;
|
||||
|
||||
// Graph editor
|
||||
TSharedPtr<SGraphEditor> GraphEditorWidget;
|
||||
|
||||
// Toolbar
|
||||
TSharedPtr<class SBorder> ToolbarWidget;
|
||||
|
||||
// Command list
|
||||
TSharedPtr<FUICommandList> CommandList;
|
||||
|
||||
// Tab IDs
|
||||
static const FName ViewportTabId;
|
||||
static const FName DetailsTabId;
|
||||
static const FName AssetBrowserTabId;
|
||||
static const FName MatrixEditorTabId;
|
||||
static const FName GraphEditorTabId;
|
||||
static const FName ToolbarTabId;
|
||||
};
|
41
Source/FLESHEditor/Public/FLESHEditorCommands.h
Normal file
41
Source/FLESHEditor/Public/FLESHEditorCommands.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
#include "FLESHEditorStyle.h"
|
||||
|
||||
/**
|
||||
* FLESH Editor Commands
|
||||
* Defines all commands for the FLESH editor
|
||||
*/
|
||||
class FFLESHEditorCommands : public TCommands<FFLESHEditorCommands>
|
||||
{
|
||||
public:
|
||||
FFLESHEditorCommands()
|
||||
: TCommands<FFLESHEditorCommands>(
|
||||
TEXT("FLESHEditor"),
|
||||
NSLOCTEXT("Contexts", "FLESHEditor", "FLESH Editor"),
|
||||
NAME_None,
|
||||
FFLESHEditorStyle::GetStyleSetName())
|
||||
{
|
||||
}
|
||||
|
||||
// TCommands interface
|
||||
virtual void RegisterCommands() override;
|
||||
// End of TCommands interface
|
||||
|
||||
// Open FLESH Editor command
|
||||
TSharedPtr<FUICommandInfo> OpenFLESHEditor;
|
||||
|
||||
// Open Dismemberment Graph Editor command
|
||||
TSharedPtr<FUICommandInfo> OpenDismembermentGraphEditor;
|
||||
|
||||
// Open Anatomical Layer Editor command
|
||||
TSharedPtr<FUICommandInfo> OpenAnatomicalLayerEditor;
|
||||
|
||||
// Open Boolean Cut Tool command
|
||||
TSharedPtr<FUICommandInfo> OpenBooleanCutTool;
|
||||
|
||||
// Open Blood System Editor command
|
||||
TSharedPtr<FUICommandInfo> OpenBloodSystemEditor;
|
||||
};
|
28
Source/FLESHEditor/Public/FLESHEditorModule.h
Normal file
28
Source/FLESHEditor/Public/FLESHEditorModule.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
/**
|
||||
* FLESH Editor Module - Editor tools for the Fully Locational Evisceration System for Humanoids
|
||||
*/
|
||||
class FFLESHEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** Singleton getter */
|
||||
static FFLESHEditorModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FFLESHEditorModule>("FLESHEditor");
|
||||
}
|
||||
|
||||
/** Open FLESH Editor */
|
||||
void OpenFLESHEditor();
|
||||
|
||||
private:
|
||||
/** Plugin command list */
|
||||
TSharedPtr<class FUICommandList> PluginCommands;
|
||||
};
|
34
Source/FLESHEditor/Public/FLESHEditorStyle.h
Normal file
34
Source/FLESHEditor/Public/FLESHEditorStyle.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
|
||||
/**
|
||||
* FLESH Editor Style
|
||||
* Defines the visual style for the FLESH editor
|
||||
*/
|
||||
class FFLESHEditorStyle
|
||||
{
|
||||
public:
|
||||
// Initialize the style
|
||||
static void Initialize();
|
||||
|
||||
// Shutdown the style
|
||||
static void Shutdown();
|
||||
|
||||
// Reload textures
|
||||
static void ReloadTextures();
|
||||
|
||||
// Get the style set name
|
||||
static FName GetStyleSetName();
|
||||
|
||||
// Get the instance
|
||||
static const ISlateStyle& Get();
|
||||
|
||||
private:
|
||||
// Create the style
|
||||
static TSharedRef<FSlateStyleSet> Create();
|
||||
|
||||
// The instance
|
||||
static TSharedPtr<FSlateStyleSet> StyleInstance;
|
||||
};
|
72
Source/FLESHEditor/Public/MatrixInputWidget.h
Normal file
72
Source/FLESHEditor/Public/MatrixInputWidget.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "Widgets/Input/SNumericEntryBox.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Widgets/Layout/SGridPanel.h"
|
||||
|
||||
/**
|
||||
* Matrix input widget for the FLESH editor
|
||||
* Allows inputting transformation matrices for precise cutting operations
|
||||
*/
|
||||
class FLESHEDITOR_API SMatrixInputWidget : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SMatrixInputWidget)
|
||||
: _Matrix(FMatrix::Identity)
|
||||
, _OnMatrixChanged()
|
||||
{}
|
||||
/** The initial matrix value */
|
||||
SLATE_ARGUMENT(FMatrix, Matrix)
|
||||
/** Called when the matrix value changes */
|
||||
SLATE_EVENT(FOnMatrixChanged, OnMatrixChanged)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Constructs this widget */
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
/** Sets the matrix value */
|
||||
void SetMatrix(const FMatrix& InMatrix);
|
||||
|
||||
/** Gets the current matrix value */
|
||||
FMatrix GetMatrix() const;
|
||||
|
||||
/** Resets the matrix to identity */
|
||||
void ResetToIdentity();
|
||||
|
||||
/** Creates a rotation matrix from Euler angles */
|
||||
void SetFromEulerAngles(float Roll, float Pitch, float Yaw);
|
||||
|
||||
/** Creates a translation matrix */
|
||||
void SetFromTranslation(const FVector& Translation);
|
||||
|
||||
/** Creates a combined transformation matrix */
|
||||
void SetFromTransform(const FTransform& Transform);
|
||||
|
||||
private:
|
||||
/** The current matrix value */
|
||||
FMatrix Matrix;
|
||||
|
||||
/** Called when the matrix value changes */
|
||||
FOnMatrixChanged OnMatrixChanged;
|
||||
|
||||
/** The grid panel containing the matrix elements */
|
||||
TSharedPtr<SGridPanel> GridPanel;
|
||||
|
||||
/** Array of numeric entry boxes for matrix elements */
|
||||
TArray<TSharedPtr<SNumericEntryBox<float>>> NumericEntryBoxes;
|
||||
|
||||
/** Creates a numeric entry box for a matrix element */
|
||||
TSharedPtr<SNumericEntryBox<float>> CreateMatrixElementWidget(int32 Row, int32 Col);
|
||||
|
||||
/** Called when a matrix element changes */
|
||||
void OnMatrixElementChanged(float NewValue, int32 Row, int32 Col);
|
||||
|
||||
/** Updates the UI from the matrix value */
|
||||
void UpdateUI();
|
||||
};
|
||||
|
||||
/** Delegate for matrix value changes */
|
||||
DECLARE_DELEGATE_OneParam(FOnMatrixChanged, const FMatrix&);
|
Reference in New Issue
Block a user