Update
This commit is contained in:
@@ -11,6 +11,7 @@ public class FLESHEditor : ModuleRules
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
"$(PluginDir)/Source/FLESHEditor/Public"
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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"))
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,123 @@
|
||||
class UDismembermentGraphNode;
|
||||
class UDismembermentGraph;
|
||||
|
||||
/**
|
||||
* Dismemberment node type enum
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EDismembermentNodeType : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
Cut UMETA(DisplayName = "Cut"),
|
||||
BloodEffect UMETA(DisplayName = "Blood Effect"),
|
||||
Physics UMETA(DisplayName = "Physics"),
|
||||
Organ UMETA(DisplayName = "Organ"),
|
||||
Wound UMETA(DisplayName = "Wound"),
|
||||
BoneSelection UMETA(DisplayName = "Bone Selection")
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismemberment node data structure
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDismembermentNodeData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Node name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
FName NodeName;
|
||||
|
||||
// Node type
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
EDismembermentNodeType NodeType;
|
||||
|
||||
// Node parameters
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, float> FloatParameters;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, FVector> VectorParameters;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, FRotator> RotatorParameters;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, bool> BoolParameters;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, FName> NameParameters;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dismemberment")
|
||||
TMap<FName, UObject*> ObjectParameters;
|
||||
|
||||
// Constructor
|
||||
FDismembermentNodeData()
|
||||
: NodeType(EDismembermentNodeType::None)
|
||||
{
|
||||
}
|
||||
|
||||
// Get float parameter
|
||||
float GetFloatParameter(const FName& ParamName, float DefaultValue = 0.0f) const
|
||||
{
|
||||
if (const float* Value = FloatParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
// Get vector parameter
|
||||
FVector GetVectorParameter(const FName& ParamName, const FVector& DefaultValue = FVector::ZeroVector) const
|
||||
{
|
||||
if (const FVector* Value = VectorParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
// Get rotator parameter
|
||||
FRotator GetRotatorParameter(const FName& ParamName, const FRotator& DefaultValue = FRotator::ZeroRotator) const
|
||||
{
|
||||
if (const FRotator* Value = RotatorParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
// Get bool parameter
|
||||
bool GetBoolParameter(const FName& ParamName, bool DefaultValue = false) const
|
||||
{
|
||||
if (const bool* Value = BoolParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
// Get name parameter
|
||||
FName GetNameParameter(const FName& ParamName, const FName& DefaultValue = NAME_None) const
|
||||
{
|
||||
if (const FName* Value = NameParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
// Get object parameter
|
||||
UObject* GetObjectParameter(const FName& ParamName, UObject* DefaultValue = nullptr) const
|
||||
{
|
||||
if (UObject* const* Value = ObjectParameters.Find(ParamName))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return DefaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compiled node data structure
|
||||
*/
|
||||
@@ -67,10 +184,28 @@ public:
|
||||
|
||||
/**
|
||||
* Get the execution order
|
||||
* @return Array of node indices in execution order
|
||||
* @param OutExecutionOrder - Array to fill with node indices in execution order
|
||||
* @return True if execution order is valid
|
||||
*/
|
||||
const TArray<int32>& GetExecutionOrder() const { return ExecutionOrder; }
|
||||
|
||||
bool GetExecutionOrder(TArray<int32>& OutExecutionOrder) const
|
||||
{
|
||||
if (ExecutionOrder.Num() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutExecutionOrder = ExecutionOrder;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node data for a specific node index
|
||||
* @param NodeIndex - Index of the node
|
||||
* @param OutNodeData - Node data to fill
|
||||
* @return True if node data is valid
|
||||
*/
|
||||
bool GetNodeData(int32 NodeIndex, FDismembermentNodeData& OutNodeData) const;
|
||||
|
||||
/**
|
||||
* Add a bone selection
|
||||
* @param BoneName - Name of the bone to select
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "DismembermentCompiler.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "BooleanCutTool.h"
|
||||
#include "DismembermentExecutor.generated.h"
|
||||
|
||||
class AActor;
|
||||
@@ -147,6 +148,10 @@ private:
|
||||
// Selected bones
|
||||
UPROPERTY()
|
||||
TArray<FName> SelectedBones;
|
||||
|
||||
// Boolean cut tool for mesh cutting operations
|
||||
UPROPERTY()
|
||||
TObjectPtr<UBooleanCutTool> CutTool;
|
||||
|
||||
// Find the target skeletal mesh component
|
||||
bool FindTargetSkeletalMesh();
|
||||
|
@@ -97,6 +97,12 @@ private:
|
||||
// Command list
|
||||
TSharedPtr<FUICommandList> CommandList;
|
||||
|
||||
// The object being edited
|
||||
UObject* EditingObject;
|
||||
|
||||
// Flag to track if the editor is initialized
|
||||
bool bIsEditorInitialized;
|
||||
|
||||
// Tab IDs
|
||||
static const FName ViewportTabId;
|
||||
static const FName DetailsTabId;
|
||||
|
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
#include "FLESHEditorStyle.h"
|
||||
|
||||
/**
|
||||
* FLESH Editor Commands
|
||||
* Defines all commands for the FLESH editor
|
||||
* F.L.E.S.H Editor Commands
|
||||
* Defines all commands for the F.L.E.S.H editor
|
||||
*/
|
||||
class FFLESHEditorCommands : public TCommands<FFLESHEditorCommands>
|
||||
{
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
FFLESHEditorCommands()
|
||||
: TCommands<FFLESHEditorCommands>(
|
||||
TEXT("FLESHEditor"),
|
||||
NSLOCTEXT("Contexts", "FLESHEditor", "FLESH Editor"),
|
||||
NSLOCTEXT("Contexts", "FLESHEditor", "F.L.E.S.H Editor"),
|
||||
NAME_None,
|
||||
FFLESHEditorStyle::GetStyleSetName())
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
virtual void RegisterCommands() override;
|
||||
// End of TCommands interface
|
||||
|
||||
// Open FLESH Editor command
|
||||
// Open F.L.E.S.H Editor command
|
||||
TSharedPtr<FUICommandInfo> OpenFLESHEditor;
|
||||
|
||||
// Open Dismemberment Graph Editor command
|
||||
|
Reference in New Issue
Block a user