This commit is contained in:
2025-04-21 08:17:52 +08:00
parent 5bf88e162c
commit 8c6c85f5be
109 changed files with 39272 additions and 284 deletions

View File

@@ -16,6 +16,80 @@ bool UDismembermentCompiler::CompileGraph(UDismembermentGraph* InGraph)
// Clear compiled data
CompiledNodeData.Empty();
ExecutionOrder.Empty();
// Perform topological sort to determine execution order
if (Graph)
{
// TODO: Implement actual graph compilation
// For now, just add a placeholder node for testing
FCompiledNodeData NodeData;
CompiledNodeData.Add(NodeData);
ExecutionOrder.Add(0);
}
return true;
}
bool UDismembermentCompiler::GetNodeData(int32 NodeIndex, FDismembermentNodeData& OutNodeData) const
{
// Check if node index is valid
if (!CompiledNodeData.IsValidIndex(NodeIndex))
{
return false;
}
// Get compiled node data
const FCompiledNodeData& CompiledData = CompiledNodeData[NodeIndex];
// Create a placeholder node data for now
// In a real implementation, this would extract data from the compiled node
OutNodeData = FDismembermentNodeData();
// Set node name
if (CompiledData.Node)
{
OutNodeData.NodeName = CompiledData.Node->GetFName();
}
else
{
OutNodeData.NodeName = FName(TEXT("Node") + FString::FromInt(NodeIndex));
}
// Set node type based on node index (just for testing)
// In a real implementation, this would be determined by the node type
switch (NodeIndex % 6)
{
case 0:
OutNodeData.NodeType = EDismembermentNodeType::Cut;
break;
case 1:
OutNodeData.NodeType = EDismembermentNodeType::BloodEffect;
break;
case 2:
OutNodeData.NodeType = EDismembermentNodeType::Physics;
break;
case 3:
OutNodeData.NodeType = EDismembermentNodeType::Organ;
break;
case 4:
OutNodeData.NodeType = EDismembermentNodeType::Wound;
break;
case 5:
OutNodeData.NodeType = EDismembermentNodeType::BoneSelection;
break;
default:
OutNodeData.NodeType = EDismembermentNodeType::None;
break;
}
// Add some placeholder parameters for testing
OutNodeData.FloatParameters.Add(TEXT("Width"), 10.0f);
OutNodeData.FloatParameters.Add(TEXT("Depth"), 5.0f);
OutNodeData.VectorParameters.Add(TEXT("Location"), FVector(0.0f, 0.0f, 0.0f));
OutNodeData.VectorParameters.Add(TEXT("Direction"), FVector(0.0f, 0.0f, 1.0f));
OutNodeData.BoolParameters.Add(TEXT("CreateDecal"), true);
OutNodeData.BoolParameters.Add(TEXT("SimulatePhysics"), true);
return true;
}

View File

@@ -97,18 +97,13 @@ void FDismembermentEditor::PostRedo(bool bSuccess)
// Create the editor layout
void FDismembermentEditor::CreateEditorLayout()
{
// Register tab spawners
// Create the layout
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)
)
// In UE5.5.4, toolbar is no longer a separate tab
// Skip the toolbar tab section and directly create the main content area
->Split
(
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)

View File

@@ -1,16 +1,621 @@
#include "DismembermentExecutor.h"
#include "DismembermentGraph/DismembermentExecutor.h"
#include "Engine/SkeletalMesh.h"
#include "Components/SkeletalMeshComponent.h"
#include "NiagaraSystem.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/DecalComponent.h"
#include "Kismet/GameplayStatics.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
UDismembermentExecutor::UDismembermentExecutor()
{
// Initialize default values
ExecutionState = 0;
Compiler = nullptr;
TargetActor = nullptr;
TargetSkeletalMesh = nullptr;
// Create boolean cut tool
CutTool = CreateDefaultSubobject<UBooleanCutTool>(TEXT("CutTool"));
}
void UDismembermentExecutor::ExecuteGraph(const TArray<uint8>& CompiledData)
void UDismembermentExecutor::Initialize(UDismembermentCompiler* InCompiler)
{
// Implementation will be added in future updates
// This is a placeholder to resolve link errors
// Reset execution state
ExecutionState = 0;
// Set compiler reference
Compiler = InCompiler;
}
bool UDismembermentExecutor::Execute(AActor* InTargetActor)
{
// Set target actor
TargetActor = InTargetActor;
// Find skeletal mesh component
if (!FindTargetSkeletalMesh())
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, skeletal mesh component not found"));
return false;
}
// Check if compiler is valid
if (!Compiler)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, invalid compiler"));
return false;
}
// Get execution order from compiler
TArray<int32> ExecutionOrder;
if (!Compiler->GetExecutionOrder(ExecutionOrder))
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, invalid execution order"));
return false;
}
// Clear selected bones
SelectedBones.Empty();
// Execute nodes in order
bool bSuccess = true;
for (int32 NodeIndex : ExecutionOrder)
{
// Execute node
bool bNodeSuccess = ExecuteNode(NodeIndex);
// Log node execution result
FDismembermentNodeData NodeData;
if (Compiler->GetNodeData(NodeIndex, NodeData))
{
FString NodeName = NodeData.NodeName.ToString();
if (bNodeSuccess)
{
UE_LOG(LogTemp, Display, TEXT("FLESH: Successfully executed node %s"), *NodeName);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Failed to execute node %s"), *NodeName);
bSuccess = false;
}
}
}
return bSuccess;
}
bool UDismembermentExecutor::FindTargetSkeletalMesh()
{
// Check if target actor is valid
if (!TargetActor)
{
return false;
}
// Get skeletal mesh component
TargetSkeletalMesh = TargetActor->FindComponentByClass<USkeletalMeshComponent>();
return TargetSkeletalMesh != nullptr;
}
void UDismembermentExecutor::AddSelectedBone(const FName& BoneName)
{
// Add bone to selection list
if (!SelectedBones.Contains(BoneName))
{
SelectedBones.Add(BoneName);
}
}
bool UDismembermentExecutor::ApplyCut(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material)
{
// Check if target skeletal mesh is valid
if (!TargetSkeletalMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot perform cut, target skeletal mesh is invalid"));
return false;
}
// Check if cut tool is valid
if (!CutTool)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot perform cut, cutting tool is invalid"));
return false;
}
// Set cut material
CutTool->SetCutMaterial(Material);
// Create cut plane
FCutPlane CutPlane;
CutPlane.Location = Location;
CutPlane.Normal = Direction.GetSafeNormal();
// Apply cut to selected bones or entire mesh
bool bSuccess = false;
if (SelectedBones.Num() > 0)
{
// Cut only selected bones
for (const FName& BoneName : SelectedBones)
{
// Cut skeletal mesh at the specified bone
TArray<USkeletalMesh*> CutResults = CutTool->CutSkeletalMesh(
TargetSkeletalMesh->GetSkeletalMeshAsset(),
CutPlane,
BoneName,
true // Create cap
);
if (CutResults.Num() >= 2)
{
// Replace the original skeletal mesh with the first cut result
TargetSkeletalMesh->SetSkeletalMesh(CutResults[0]);
// TODO: Handle the second cut result (detached part)
// This would involve creating a new actor with the second mesh
// and applying physics to it
bSuccess = true;
}
}
}
else
{
// Cut the entire mesh
TArray<USkeletalMesh*> CutResults = CutTool->CutSkeletalMesh(
TargetSkeletalMesh->GetSkeletalMeshAsset(),
CutPlane,
NAME_None, // No specific bone
true // Create cap
);
if (CutResults.Num() >= 2)
{
// Replace the original skeletal mesh with the first cut result
TargetSkeletalMesh->SetSkeletalMesh(CutResults[0]);
// TODO: Handle the second cut result (detached part)
// This would involve creating a new actor with the second mesh
// and applying physics to it
bSuccess = true;
}
}
return bSuccess;
}
bool UDismembermentExecutor::SpawnBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure, bool CreateBloodPool, float BloodPoolSize, UMaterialInterface* BloodPoolMaterial)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, target actor is invalid"));
return false;
}
// Check if blood effect is valid
if (!BloodEffect)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, blood particle system is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Spawn blood effect
UNiagaraComponent* BloodComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
World,
BloodEffect,
Location,
FRotator::ZeroRotator,
FVector(1.0f, 1.0f, 1.0f),
true,
true,
ENCPoolMethod::AutoRelease,
true
);
if (!BloodComponent)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, failed to create particle system component"));
return false;
}
// Set blood parameters
BloodComponent->SetVariableFloat(FName("BloodAmount"), BloodAmount);
BloodComponent->SetVariableFloat(FName("BloodPressure"), BloodPressure);
// Create blood pool if needed
if (CreateBloodPool && BloodPoolMaterial)
{
// Create a decal component for the blood pool
UDecalComponent* BloodPoolDecal = UGameplayStatics::SpawnDecalAtLocation(
World,
BloodPoolMaterial,
FVector(BloodPoolSize, BloodPoolSize, 10.0f), // Size of the decal
Location,
FRotator(-90.0f, 0.0f, 0.0f), // Rotate to face the ground
10.0f // Lifetime
);
if (BloodPoolDecal)
{
// Set fade parameters
BloodPoolDecal->SetFadeOut(5.0f, 5.0f, false);
}
}
return true;
}
bool UDismembermentExecutor::ApplyPhysics(float Mass, float LinearDamping, float AngularDamping, bool EnableGravity, bool SimulatePhysics, bool GenerateOverlapEvents, UPhysicalMaterial* PhysicalMaterial, float ImpulseForce, float ImpulseRadius)
{
// Check if target skeletal mesh is valid
if (!TargetSkeletalMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot apply physics, target skeletal mesh is invalid"));
return false;
}
// Apply physics settings to the skeletal mesh component
TargetSkeletalMesh->SetSimulatePhysics(SimulatePhysics);
TargetSkeletalMesh->SetEnableGravity(EnableGravity);
TargetSkeletalMesh->SetGenerateOverlapEvents(GenerateOverlapEvents);
// Set mass properties
if (Mass > 0.0f)
{
TargetSkeletalMesh->SetMassOverrideInKg(NAME_None, Mass, true);
}
// Set damping
TargetSkeletalMesh->SetLinearDamping(LinearDamping);
TargetSkeletalMesh->SetAngularDamping(AngularDamping);
// Set physical material if provided
if (PhysicalMaterial)
{
TargetSkeletalMesh->SetPhysMaterialOverride(PhysicalMaterial);
}
// Apply impulse if force is greater than zero
if (ImpulseForce > 0.0f)
{
// Get the component's location
FVector ComponentLocation = TargetSkeletalMesh->GetComponentLocation();
// Apply radial impulse
TargetSkeletalMesh->AddRadialImpulse(
ComponentLocation,
ImpulseRadius,
ImpulseForce,
ERadialImpulseFalloff::RIF_Linear,
true // Apply impulse to all bodies
);
}
return true;
}
bool UDismembermentExecutor::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)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, target actor is invalid"));
return false;
}
// Check if organ mesh is valid
if (!OrganMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, organ mesh is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Create a static mesh component for the organ
UStaticMeshComponent* OrganComponent = NewObject<UStaticMeshComponent>(TargetActor);
if (!OrganComponent)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, failed to create static mesh component"));
return false;
}
// Register the component
OrganComponent->RegisterComponent();
// Set the mesh and material
OrganComponent->SetStaticMesh(OrganMesh);
if (OrganMaterial)
{
OrganComponent->SetMaterial(0, OrganMaterial);
}
// Set transform
if (TargetSkeletalMesh && !AttachBoneName.IsNone())
{
// Attach to bone if specified
OrganComponent->AttachToComponent(
TargetSkeletalMesh,
FAttachmentTransformRules::KeepRelativeTransform,
AttachBoneName
);
}
else
{
// Attach to actor root
OrganComponent->AttachToComponent(
TargetActor->GetRootComponent(),
FAttachmentTransformRules::KeepRelativeTransform
);
}
// Set relative transform
OrganComponent->SetRelativeLocation(RelativeLocation);
OrganComponent->SetRelativeRotation(RelativeRotation);
OrganComponent->SetRelativeScale3D(RelativeScale);
// Apply physics if needed
if (SimulatePhysics)
{
OrganComponent->SetSimulatePhysics(true);
OrganComponent->SetEnableGravity(true);
OrganComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
// Add a tag to identify this as an organ
OrganComponent->ComponentTags.Add(FName("Organ"));
// Add custom tags for organ properties
if (IsCriticalOrgan)
{
OrganComponent->ComponentTags.Add(FName("CriticalOrgan"));
}
// Store damage multiplier as a custom float
OrganComponent->SetCustomPrimitiveDataFloat(0, DamageMultiplier);
OrganComponent->SetCustomPrimitiveDataFloat(1, BloodAmount);
}
return true;
}
bool UDismembermentExecutor::ApplyWoundEffect(float WoundSize, float WoundDepth, UMaterialInterface* WoundMaterial, UNiagaraSystem* WoundEffect, bool CreateDecal, UMaterialInterface* DecalMaterial, float DecalSize, float DecalLifetime, bool AffectBoneHealth, float BoneDamage)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot apply wound effect, target actor is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Get the location from the selected bone or actor location
FVector WoundLocation;
FRotator WoundRotation;
if (TargetSkeletalMesh && SelectedBones.Num() > 0)
{
// Use the first selected bone's location
WoundLocation = TargetSkeletalMesh->GetBoneLocation(SelectedBones[0]);
WoundRotation = TargetSkeletalMesh->GetBoneQuaternion(SelectedBones[0]).Rotator();
}
else if (TargetSkeletalMesh)
{
// Use the skeletal mesh component's location
WoundLocation = TargetSkeletalMesh->GetComponentLocation();
WoundRotation = TargetSkeletalMesh->GetComponentRotation();
}
else
{
// Use the actor's location
WoundLocation = TargetActor->GetActorLocation();
WoundRotation = TargetActor->GetActorRotation();
}
// Spawn wound effect if provided
if (WoundEffect)
{
UNiagaraComponent* WoundEffectComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
World,
WoundEffect,
WoundLocation,
WoundRotation,
FVector(WoundSize, WoundSize, WoundSize),
true,
true,
ENCPoolMethod::AutoRelease,
true
);
if (WoundEffectComponent)
{
// Set wound parameters
WoundEffectComponent->SetVariableFloat(FName("WoundSize"), WoundSize);
WoundEffectComponent->SetVariableFloat(FName("WoundDepth"), WoundDepth);
}
}
// Create decal if needed
if (CreateDecal && DecalMaterial)
{
// Create a decal component for the wound
UDecalComponent* WoundDecal = UGameplayStatics::SpawnDecalAttached(
DecalMaterial,
FVector(DecalSize, DecalSize, DecalSize),
TargetSkeletalMesh ? TargetSkeletalMesh : TargetActor->GetRootComponent(),
NAME_None,
WoundLocation,
WoundRotation,
EAttachLocation::KeepWorldPosition,
DecalLifetime
);
if (WoundDecal)
{
// Set fade parameters
WoundDecal->SetFadeOut(DecalLifetime * 0.8f, DecalLifetime * 0.2f, false);
}
}
// Apply bone damage if needed
if (AffectBoneHealth && TargetSkeletalMesh && SelectedBones.Num() > 0)
{
// This would typically involve a custom bone health system
// For now, we'll just log that bone damage was applied
for (const FName& BoneName : SelectedBones)
{
UE_LOG(LogTemp, Display, TEXT("FLESH: Applied %.2f damage to bone %s"), BoneDamage, *BoneName.ToString());
}
}
return true;
}
bool UDismembermentExecutor::ExecuteNode(int32 NodeIndex)
{
// Check if compiler is valid
if (!Compiler)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute node, compiler is invalid"));
return false;
}
// Get node data from compiler
FDismembermentNodeData NodeData;
if (!Compiler->GetNodeData(NodeIndex, NodeData))
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute node, node data is invalid"));
return false;
}
// Execute node based on its type
bool bSuccess = false;
switch (NodeData.NodeType)
{
case EDismembermentNodeType::Cut:
{
// Get cut parameters
FVector Location = NodeData.GetVectorParameter("Location", FVector::ZeroVector);
FVector Direction = NodeData.GetVectorParameter("Direction", FVector::UpVector);
float Width = NodeData.GetFloatParameter("Width", 1.0f);
float Depth = NodeData.GetFloatParameter("Depth", 10.0f);
UMaterialInterface* Material = Cast<UMaterialInterface>(NodeData.GetObjectParameter("Material"));
// Apply cut
bSuccess = ApplyCut(Location, Direction, Width, Depth, Material);
break;
}
case EDismembermentNodeType::BloodEffect:
{
// Get blood effect parameters
FVector Location = NodeData.GetVectorParameter("Location", FVector::ZeroVector);
UNiagaraSystem* BloodEffect = Cast<UNiagaraSystem>(NodeData.GetObjectParameter("BloodEffect"));
float BloodAmount = NodeData.GetFloatParameter("BloodAmount", 1.0f);
float BloodPressure = NodeData.GetFloatParameter("BloodPressure", 1.0f);
bool CreateBloodPool = NodeData.GetBoolParameter("CreateBloodPool", true);
float BloodPoolSize = NodeData.GetFloatParameter("BloodPoolSize", 100.0f);
UMaterialInterface* BloodPoolMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("BloodPoolMaterial"));
// Spawn blood effect
bSuccess = SpawnBloodEffect(Location, BloodEffect, BloodAmount, BloodPressure, CreateBloodPool, BloodPoolSize, BloodPoolMaterial);
break;
}
case EDismembermentNodeType::Physics:
{
// Get physics parameters
float Mass = NodeData.GetFloatParameter("Mass", 10.0f);
float LinearDamping = NodeData.GetFloatParameter("LinearDamping", 0.01f);
float AngularDamping = NodeData.GetFloatParameter("AngularDamping", 0.01f);
bool EnableGravity = NodeData.GetBoolParameter("EnableGravity", true);
bool SimulatePhysics = NodeData.GetBoolParameter("SimulatePhysics", true);
bool GenerateOverlapEvents = NodeData.GetBoolParameter("GenerateOverlapEvents", true);
UPhysicalMaterial* PhysicalMaterial = Cast<UPhysicalMaterial>(NodeData.GetObjectParameter("PhysicalMaterial"));
float ImpulseForce = NodeData.GetFloatParameter("ImpulseForce", 1000.0f);
float ImpulseRadius = NodeData.GetFloatParameter("ImpulseRadius", 100.0f);
// Apply physics
bSuccess = ApplyPhysics(Mass, LinearDamping, AngularDamping, EnableGravity, SimulatePhysics, GenerateOverlapEvents, PhysicalMaterial, ImpulseForce, ImpulseRadius);
break;
}
case EDismembermentNodeType::Organ:
{
// Get organ parameters
UStaticMesh* OrganMesh = Cast<UStaticMesh>(NodeData.GetObjectParameter("OrganMesh"));
UMaterialInterface* OrganMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("OrganMaterial"));
FName AttachBoneName = NodeData.GetNameParameter("AttachBoneName", NAME_None);
FVector RelativeLocation = NodeData.GetVectorParameter("RelativeLocation", FVector::ZeroVector);
FRotator RelativeRotation = NodeData.GetRotatorParameter("RelativeRotation", FRotator::ZeroRotator);
FVector RelativeScale = NodeData.GetVectorParameter("RelativeScale", FVector::OneVector);
bool SimulatePhysics = NodeData.GetBoolParameter("SimulatePhysics", true);
float DamageMultiplier = NodeData.GetFloatParameter("DamageMultiplier", 1.0f);
bool IsCriticalOrgan = NodeData.GetBoolParameter("IsCriticalOrgan", false);
float BloodAmount = NodeData.GetFloatParameter("BloodAmount", 1.0f);
// Spawn organ
bSuccess = SpawnOrgan(OrganMesh, OrganMaterial, AttachBoneName, RelativeLocation, RelativeRotation, RelativeScale, SimulatePhysics, DamageMultiplier, IsCriticalOrgan, BloodAmount);
break;
}
case EDismembermentNodeType::Wound:
{
// Get wound parameters
float WoundSize = NodeData.GetFloatParameter("WoundSize", 10.0f);
float WoundDepth = NodeData.GetFloatParameter("WoundDepth", 5.0f);
UMaterialInterface* WoundMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("WoundMaterial"));
UNiagaraSystem* WoundEffect = Cast<UNiagaraSystem>(NodeData.GetObjectParameter("WoundEffect"));
bool CreateDecal = NodeData.GetBoolParameter("CreateDecal", true);
UMaterialInterface* DecalMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("DecalMaterial"));
float DecalSize = NodeData.GetFloatParameter("DecalSize", 50.0f);
float DecalLifetime = NodeData.GetFloatParameter("DecalLifetime", 10.0f);
bool AffectBoneHealth = NodeData.GetBoolParameter("AffectBoneHealth", false);
float BoneDamage = NodeData.GetFloatParameter("BoneDamage", 10.0f);
// Apply wound effect
bSuccess = ApplyWoundEffect(WoundSize, WoundDepth, WoundMaterial, WoundEffect, CreateDecal, DecalMaterial, DecalSize, DecalLifetime, AffectBoneHealth, BoneDamage);
break;
}
case EDismembermentNodeType::BoneSelection:
{
// Get bone selection parameters
FName BoneName = NodeData.GetNameParameter("BoneName", NAME_None);
// Add bone to selection
if (!BoneName.IsNone())
{
AddSelectedBone(BoneName);
bSuccess = true;
}
break;
}
default:
UE_LOG(LogTemp, Warning, TEXT("FLESH: Unknown node type"));
break;
}
return bSuccess;
}

View File

@@ -3,6 +3,7 @@
#include "FLESHEditorStyle.h"
#include "MatrixInputWidget.h"
#include "DismembermentGraph/DismembermentGraphEditor.h"
#include "DismembermentGraph/DismembermentGraphAsset.h"
#include "Framework/Docking/TabManager.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SBox.h"
@@ -23,15 +24,32 @@ const FName FFLESHEditor::GraphEditorTabId(TEXT("FLESHEditor_GraphEditor"));
const FName FFLESHEditor::ToolbarTabId(TEXT("FLESHEditor_Toolbar"));
FFLESHEditor::FFLESHEditor()
: EditingObject(nullptr)
, CommandList(MakeShareable(new FUICommandList))
{
// Initialize member variables
bIsEditorInitialized = false;
}
FFLESHEditor::~FFLESHEditor()
{
// Unregister from any events
// 注释掉有问题的代码因为FFLESHEditor没有继承FEditorUndoClient
// if (GEditor)
// {
// GEditor->UnregisterForUndo(this);
// }
// Clear references
DetailsWidget = nullptr;
EditingObject = nullptr;
}
void FFLESHEditor::InitFLESHEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, UObject* InObject)
{
// Store the object we're editing
EditingObject = InObject;
// Create command list
this->CreateCommandList();
@@ -52,29 +70,37 @@ void FFLESHEditor::InitFLESHEditor(const EToolkitMode::Type Mode, const TSharedP
->Split
(
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
->SetSizeCoefficient(0.7f)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.7f)
->AddTab(ViewportTabId, ETabState::OpenedTab)
->SetSizeCoefficient(0.2f)
->AddTab(DetailsTabId, ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.3f)
->AddTab(MatrixEditorTabId, ETabState::OpenedTab)
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.6f)
->AddTab(ViewportTabId, ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.4f)
->AddTab(GraphEditorTabId, ETabState::OpenedTab)
)
)
)
->Split
(
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
->SetSizeCoefficient(0.3f)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.6f)
->AddTab(DetailsTabId, ETabState::OpenedTab)
->AddTab(MatrixEditorTabId, ETabState::OpenedTab)
)
->Split
(
@@ -86,10 +112,50 @@ void FFLESHEditor::InitFLESHEditor(const EToolkitMode::Type Mode, const TSharedP
)
);
// Initialize toolkit
const bool bCreateDefaultStandaloneMenu = true;
const bool bCreateDefaultToolbar = true;
this->InitAssetEditor(Mode, InitToolkitHost, FName("FLESHEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, InObject, false);
// Create a standalone toolkit
FAssetEditorToolkit::InitAssetEditor(
Mode,
InitToolkitHost,
FName("FLESHEditorApp"),
StandaloneDefaultLayout,
true, // bCreateDefaultStandaloneMenu
true, // bCreateDefaultToolbar
InObject);
// Register tab spawners
TabManager = GetTabManager();
if (TabManager.IsValid())
{
TabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Viewport))
.SetDisplayName(LOCTEXT("ViewportTab", "Viewport"));
TabManager->RegisterTabSpawner(DetailsTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Details))
.SetDisplayName(LOCTEXT("DetailsTab", "Details"));
TabManager->RegisterTabSpawner(AssetBrowserTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_AssetBrowser))
.SetDisplayName(LOCTEXT("AssetBrowserTab", "Asset Browser"));
TabManager->RegisterTabSpawner(MatrixEditorTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_MatrixEditor))
.SetDisplayName(LOCTEXT("MatrixEditorTab", "Matrix Editor"));
TabManager->RegisterTabSpawner(GraphEditorTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_GraphEditor))
.SetDisplayName(LOCTEXT("GraphEditorTab", "Graph Editor"));
TabManager->RegisterTabSpawner(ToolbarTabId, FOnSpawnTab::CreateSP(this, &FFLESHEditor::SpawnTab_Toolbar))
.SetDisplayName(LOCTEXT("ToolbarTab", "Toolbar"));
}
// Initialize the editor using the base class method, so it won't crash even if an empty object is passed in.
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);
DetailsWidget->SetObject(EditingObject);
}
void FFLESHEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
@@ -163,9 +229,12 @@ FLinearColor FFLESHEditor::GetWorldCentricTabColorScale() const
void FFLESHEditor::OpenEditor()
{
// Create a DismembermentGraphAsset to avoid assertion failure and provide meaningful editing
UDismembermentGraphAsset* GraphAsset = NewObject<UDismembermentGraphAsset>();
// Create new editor instance
TSharedRef<FFLESHEditor> NewEditor = MakeShareable(new FFLESHEditor());
NewEditor->InitFLESHEditor(EToolkitMode::Standalone, nullptr, nullptr);
NewEditor->InitFLESHEditor(EToolkitMode::Standalone, nullptr, GraphAsset);
}
TSharedRef<SDockTab> FFLESHEditor::SpawnTab_Viewport(const FSpawnTabArgs& Args)
@@ -253,21 +322,32 @@ TSharedRef<SWidget> FFLESHEditor::CreateViewportWidget()
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);
// Use the details widget that was created in InitFLESHEditor
// If it doesn't exist yet, create it
if (!DetailsWidget.IsValid())
{
// 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);
// Set the object to edit
if (EditingObject != nullptr)
{
DetailsWidget->SetObject(EditingObject);
}
}
return SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))

View File

@@ -4,7 +4,7 @@
void FFLESHEditorCommands::RegisterCommands()
{
UI_COMMAND(OpenFLESHEditor, "FLESH", "Open FLESH Dismemberment System Editor", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(OpenFLESHEditor, "F.L.E.S.H", "Open F.L.E.S.H 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());

View File

@@ -35,8 +35,8 @@ void FFLESHEditorModule::StartupModule()
// Add to main menu
{
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools");
FToolMenuSection& Section = Menu->FindOrAddSection("Tools");
Section.AddMenuEntryWithCommandList(FFLESHEditorCommands::Get().OpenFLESHEditor, PluginCommands);
}