Updated
This commit is contained in:
@@ -1,95 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentCompiler.h"
|
||||
#include "DismembermentGraph/DismembermentGraph.h"
|
||||
|
||||
UDismembermentCompiler::UDismembermentCompiler()
|
||||
{
|
||||
// Initialize default values
|
||||
}
|
||||
|
||||
bool UDismembermentCompiler::CompileGraph(UDismembermentGraph* InGraph)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
|
||||
// Store the graph reference
|
||||
Graph = 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,621 +0,0 @@
|
||||
#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
|
||||
Compiler = nullptr;
|
||||
TargetActor = nullptr;
|
||||
TargetSkeletalMesh = nullptr;
|
||||
|
||||
// Create boolean cut tool
|
||||
CutTool = CreateDefaultSubobject<UBooleanCutTool>(TEXT("CutTool"));
|
||||
}
|
||||
|
||||
void UDismembermentExecutor::Initialize(UDismembermentCompiler* InCompiler)
|
||||
{
|
||||
// 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->SetSkeletalMeshAsset(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->SetSkeletalMeshAsset(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, TEXT("PreviewOrganComponent"));
|
||||
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;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraph.h"
|
||||
#include "DismembermentGraph/DismembermentGraphAsset.h"
|
||||
|
||||
UDismembermentGraph::UDismembermentGraph()
|
||||
{
|
||||
// Initialize default values
|
||||
OwningAsset = nullptr;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
#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"
|
||||
#include "GraphEditorActions.h"
|
||||
#include "AssetTypeActions_Base.h"
|
||||
#include "DismembermentGraph/DismembermentGraphSchema.h"
|
||||
#include "DismembermentGraph/SDismembermentGraphNode.h"
|
||||
#include "DismembermentGraph/DismembermentGraphEditorFactory.h"
|
||||
#include "DismembermentGraph/DismembermentGraphPalette.h"
|
||||
#include "DismembermentGraph/DismembermentGraphNodeFactory.h"
|
||||
#include "Styling/AppStyle.h"
|
||||
#include "GraphEditorSettings.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "DismembermentGraphEditor"
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
TSharedRef<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);
|
||||
}
|
||||
};
|
||||
|
||||
// Comment out this line to avoid module redefinition
|
||||
// IMPLEMENT_MODULE(FDismembermentGraphEditorModule, FLESHEditor)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNode.h"
|
||||
|
||||
UDismembermentGraphNode::UDismembermentGraphNode()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.6f, 0.6f, 0.6f);
|
||||
NodeCategory = FText::FromString("Default");
|
||||
NodeDescription = FText::FromString("Base dismemberment graph node");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNode::AllocateDefaultPins()
|
||||
{
|
||||
// Base implementation does nothing
|
||||
// Derived classes will override this
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
// Default implementation returns the class name
|
||||
return FText::FromString(GetClass()->GetName());
|
||||
}
|
||||
|
||||
FLinearColor UDismembermentGraphNode::GetNodeTitleColor() const
|
||||
{
|
||||
return NodeTitleColor;
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNode::GetTooltipText() const
|
||||
{
|
||||
return NodeDescription;
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNode::GetMenuCategory() const
|
||||
{
|
||||
return NodeCategory;
|
||||
}
|
||||
|
||||
void UDismembermentGraphNode::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Base implementation does nothing
|
||||
// Derived classes will override this
|
||||
}
|
||||
|
||||
void UDismembermentGraphNode::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Base implementation does nothing
|
||||
// Derived classes will override this
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBloodEffect.h"
|
||||
|
||||
UDismembermentGraphNodeBloodEffect::UDismembermentGraphNodeBloodEffect()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.8f, 0.0f, 0.0f); // Red for blood
|
||||
NodeCategory = FText::FromString("Effects");
|
||||
NodeDescription = FText::FromString("Adds blood effect to the dismemberment");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBloodEffect::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodeBloodEffect::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Blood Effect");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBloodEffect::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBloodEffect::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodeBoneSelect.h"
|
||||
|
||||
UDismembermentGraphNodeBoneSelect::UDismembermentGraphNodeBoneSelect()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.8f, 0.8f, 0.2f); // Yellow for bone
|
||||
NodeCategory = FText::FromString("Selection");
|
||||
NodeDescription = FText::FromString("Selects bones for dismemberment");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBoneSelect::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodeBoneSelect::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Bone Selection");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBoneSelect::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeBoneSelect::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodeCut.h"
|
||||
|
||||
UDismembermentGraphNodeCut::UDismembermentGraphNodeCut()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.2f, 0.6f, 0.8f); // Blue for cut
|
||||
NodeCategory = FText::FromString("Operations");
|
||||
NodeDescription = FText::FromString("Performs cutting operation for dismemberment");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeCut::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodeCut::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Cut Operation");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeCut::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeCut::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodeOrgan.h"
|
||||
|
||||
UDismembermentGraphNodeOrgan::UDismembermentGraphNodeOrgan()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.8f, 0.4f, 0.4f); // Pink for organs
|
||||
NodeCategory = FText::FromString("Anatomy");
|
||||
NodeDescription = FText::FromString("Adds organ to the dismemberment system");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeOrgan::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodeOrgan::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Organ");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeOrgan::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeOrgan::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodePhysics.h"
|
||||
|
||||
UDismembermentGraphNodePhysics::UDismembermentGraphNodePhysics()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.4f, 0.8f, 0.4f); // Green for physics
|
||||
NodeCategory = FText::FromString("Physics");
|
||||
NodeDescription = FText::FromString("Configures physics properties for dismembered parts");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodePhysics::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodePhysics::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Physics Properties");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodePhysics::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodePhysics::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "DismembermentGraph/DismembermentGraphNodeWound.h"
|
||||
|
||||
UDismembermentGraphNodeWound::UDismembermentGraphNodeWound()
|
||||
{
|
||||
// Initialize default values
|
||||
NodeTitleColor = FLinearColor(0.8f, 0.2f, 0.2f); // Dark red for wounds
|
||||
NodeCategory = FText::FromString("Effects");
|
||||
NodeDescription = FText::FromString("Adds wound effect to the dismemberment");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeWound::AllocateDefaultPins()
|
||||
{
|
||||
// Create input pin
|
||||
CreatePin(EGPD_Input, TEXT("Exec"), TEXT("In"));
|
||||
|
||||
// Create output pin
|
||||
CreatePin(EGPD_Output, TEXT("Exec"), TEXT("Out"));
|
||||
}
|
||||
|
||||
FText UDismembermentGraphNodeWound::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return FText::FromString("Wound Effect");
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeWound::CompileNode(class FDismembermentCompiler* Compiler)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
|
||||
void UDismembermentGraphNodeWound::ExecuteNode(class FDismembermentExecutor* Executor)
|
||||
{
|
||||
// Implementation will be added in future updates
|
||||
// This is a placeholder to resolve link errors
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
#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 (const 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(FGenericCommands::Get().SelectAll);
|
||||
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
|
||||
@@ -1,787 +0,0 @@
|
||||
#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(FRotationMatrix::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 = UNiagaraFunctionLibrary::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, TEXT("CutPlaneMeshComponent"));
|
||||
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 = UNiagaraFunctionLibrary::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, TEXT("BloodPoolComponent"));
|
||||
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, TEXT("OrganComponent"));
|
||||
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, TEXT("WoundComponent"));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,378 +0,0 @@
|
||||
#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"
|
||||
#include "Styling/AppStyle.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(); // Comment out this line because OutputPinBox is not defined
|
||||
|
||||
// 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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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
|
||||
@@ -1,439 +0,0 @@
|
||||
#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)
|
||||
: AActor(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(FRotator(Settings->Profiles[ProfileIndex].LightingRigRotation, 0, 0));
|
||||
// The following methods may not exist in UE5.5.4, temporarily commented out
|
||||
// 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())
|
||||
{
|
||||
// Cannot remove Actor directly, need to remove the Actor's root component
|
||||
if (PreviewActor && PreviewActor->GetRootComponent())
|
||||
{
|
||||
PreviewScene->RemoveComponent(PreviewActor->GetRootComponent());
|
||||
}
|
||||
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);
|
||||
// The following methods may not exist in UE5.5.4, temporarily commented out
|
||||
// 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);
|
||||
// The following methods may not exist in UE5.5.4, temporarily commented out
|
||||
// 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);
|
||||
|
||||
// In UE5.5.4, these variables may no longer be members of FEditorViewportClient
|
||||
// We only keep the necessary settings, others are commented out
|
||||
bSetListenerPosition = false;
|
||||
bShouldCheckHitProxy = true;
|
||||
|
||||
// The following settings may no longer be supported in UE5.5.4, temporarily commented out
|
||||
/*
|
||||
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;
|
||||
// All bEnableColorize related variables are commented out
|
||||
*/
|
||||
// Continue to comment out more bEnableColorize related variables
|
||||
/*
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensityColor = false;
|
||||
bEnableColorizeVolumetricLightmapIndirectIntensity = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusion = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusionGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusionColor = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusionGrayscale = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusionColor = false;
|
||||
bEnableColorizeVolumetricLightmapAmbientOcclusion = false;
|
||||
*/
|
||||
// Continue to comment out all remaining bEnableColorize related variables
|
||||
/*
|
||||
bEnableColorizeVolumetricLightmapSHBand0 = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Color = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Grayscale = false;
|
||||
bEnableColorizeVolumetricLightmapSHBand0Color = 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
|
||||
void FFLESHEditorCommands::RegisterCommands()
|
||||
{
|
||||
UI_COMMAND(OpenFLESHEditor, "F.L.E.S.H", "Open F.L.E.S.H Dismemberment System Editor", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenFLESHEditor, "F.L.E.S.H Editor", "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());
|
||||
|
||||
@@ -81,54 +81,45 @@ void FFLESHEditorModule::OpenFLESHEditorCommand()
|
||||
OpenFLESHEditor(EToolkitMode::Standalone, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void FFLESHEditorModule::OpenFLESHEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, UObject* ObjectToEdit)
|
||||
void FFLESHEditorModule::OpenFLESHEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, TObjectPtr<UObject> ObjectToEdit)
|
||||
{
|
||||
// Add try-catch block to prevent crashes when opening editor
|
||||
// Add try-catch block to prevent crashes when opening the editor
|
||||
try
|
||||
{
|
||||
// Check if FLESH module is loaded
|
||||
if (!FModuleManager::Get().IsModuleLoaded("FLESH"))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Cannot open FLESH Editor: FLESH module is not loaded"));
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to open FLESH Editor: FLESH module not loaded"));
|
||||
return;
|
||||
}
|
||||
|
||||
// If no object is provided, create a default object to edit
|
||||
// If no object is provided, create a default object
|
||||
if (ObjectToEdit == nullptr)
|
||||
{
|
||||
// Try to find or create a dismemberment graph asset to edit
|
||||
UClass* DismembermentGraphClass = FindObject<UClass>(nullptr, TEXT("/Script/FLESH.DismembermentGraphAsset"));
|
||||
if (DismembermentGraphClass)
|
||||
// Create a basic UObject
|
||||
ObjectToEdit = NewObject<UObject>(GetTransientPackage(), UObject::StaticClass(), TEXT("DefaultFLESHObject"));
|
||||
if (ObjectToEdit == nullptr)
|
||||
{
|
||||
// Try to find an existing asset first
|
||||
ObjectToEdit = FindObject<UObject>(nullptr, TEXT("DefaultDismembermentGraph"));
|
||||
|
||||
// If not found, create a temporary object
|
||||
if (ObjectToEdit == nullptr)
|
||||
{
|
||||
ObjectToEdit = NewObject<UObject>(GetTransientPackage(), DismembermentGraphClass, TEXT("DefaultDismembermentGraph"));
|
||||
ObjectToEdit->AddToRoot(); // Prevent garbage collection
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we can't find the class, create a basic UObject
|
||||
ObjectToEdit = NewObject<UObject>(GetTransientPackage(), UObject::StaticClass(), TEXT("DefaultFLESHObject"));
|
||||
ObjectToEdit->AddToRoot(); // Prevent garbage collection
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to create default object, cannot open FLESH Editor"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new FLESH editor
|
||||
TSharedRef<FFLESHEditor> NewFLESHEditor(new FFLESHEditor());
|
||||
NewFLESHEditor->InitFLESHEditor(Mode, InitToolkitHost, ObjectToEdit);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("FLESH Editor successfully opened"));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to open FLESH Editor: %s"), UTF8_TO_TCHAR(e.what()));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("OpenEditorError", "Failed to open FLESH Editor: {0}"), FText::FromString(UTF8_TO_TCHAR(e.what()))));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to open FLESH Editor with unknown exception"));
|
||||
UE_LOG(LogTemp, Error, TEXT("Unknown exception occurred when opening FLESH Editor"));
|
||||
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("OpenEditorUnknownError", "Unknown exception occurred when opening FLESH Editor"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "SlateOptMacros.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Brushes/SlateImageBrush.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FFLESHEditorStyle::StyleInstance = nullptr;
|
||||
|
||||
|
||||
150
Source/FLESHEditor/Private/FLESHGraph/FLESHCompiler.cpp
Normal file
150
Source/FLESHEditor/Private/FLESHGraph/FLESHCompiler.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "FLESHGraph/FLESHCompiler.h"
|
||||
#include "FLESHGraph/FLESHGraph.h"
|
||||
#include "FLESHGraph/FLESHGraphNode.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFLESHCompiler, Log, All);
|
||||
|
||||
UFLESHCompiler::UFLESHCompiler()
|
||||
{
|
||||
// Initialize default values
|
||||
SourceGraph = nullptr;
|
||||
bCompilationSuccessful = false;
|
||||
ErrorMessage = TEXT("");
|
||||
}
|
||||
|
||||
void UFLESHCompiler::Initialize(UFLESHGraph* InGraph)
|
||||
{
|
||||
// Set source graph
|
||||
SourceGraph = InGraph;
|
||||
|
||||
// Reset compilation status
|
||||
bCompilationSuccessful = false;
|
||||
ErrorMessage = TEXT("");
|
||||
CompiledNodeData.Empty();
|
||||
ExecutionOrder.Empty();
|
||||
|
||||
UE_LOG(LogFLESHCompiler, Log, TEXT("FLESH Compiler initialized with graph: %s"),
|
||||
SourceGraph ? *SourceGraph->GetName() : TEXT("Invalid"));
|
||||
}
|
||||
|
||||
bool UFLESHCompiler::Compile()
|
||||
{
|
||||
// Check if source graph is valid
|
||||
if (!SourceGraph)
|
||||
{
|
||||
ErrorMessage = TEXT("Invalid source graph");
|
||||
UE_LOG(LogFLESHCompiler, Error, TEXT("FLESH Compiler error: %s"), *ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset compilation data
|
||||
CompiledNodeData.Empty();
|
||||
ExecutionOrder.Empty();
|
||||
|
||||
// Validate graph
|
||||
if (!ValidateGraph())
|
||||
{
|
||||
UE_LOG(LogFLESHCompiler, Error, TEXT("FLESH Compiler error: %s"), *ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process all nodes in the graph
|
||||
TArray<UFLESHGraphNode*> AllNodes;
|
||||
// TODO: Get all nodes from graph
|
||||
|
||||
// Process each node
|
||||
for (int32 NodeIndex = 0; NodeIndex < AllNodes.Num(); NodeIndex++)
|
||||
{
|
||||
if (!ProcessNode(AllNodes[NodeIndex], NodeIndex))
|
||||
{
|
||||
UE_LOG(LogFLESHCompiler, Error, TEXT("FLESH Compiler error: %s"), *ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort nodes in execution order
|
||||
SortNodes();
|
||||
|
||||
// Set compilation status
|
||||
bCompilationSuccessful = true;
|
||||
|
||||
UE_LOG(LogFLESHCompiler, Log, TEXT("FLESH Graph compiled successfully. Nodes: %d, Execution order: %d"),
|
||||
CompiledNodeData.Num(), ExecutionOrder.Num());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<FFLESHNodeData> UFLESHCompiler::GetCompiledNodeData() const
|
||||
{
|
||||
return CompiledNodeData;
|
||||
}
|
||||
|
||||
TArray<int32> UFLESHCompiler::GetExecutionOrder() const
|
||||
{
|
||||
return ExecutionOrder;
|
||||
}
|
||||
|
||||
bool UFLESHCompiler::IsCompilationSuccessful() const
|
||||
{
|
||||
return bCompilationSuccessful;
|
||||
}
|
||||
|
||||
FString UFLESHCompiler::GetErrorMessage() const
|
||||
{
|
||||
return ErrorMessage;
|
||||
}
|
||||
|
||||
bool UFLESHCompiler::ProcessNode(UFLESHGraphNode* Node, int32 NodeIndex)
|
||||
{
|
||||
// Check if node is valid
|
||||
if (!Node)
|
||||
{
|
||||
ErrorMessage = FString::Printf(TEXT("Invalid node at index %d"), NodeIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create node data
|
||||
FFLESHNodeData NodeData;
|
||||
NodeData.NodeName = FName(*Node->GetNodeTitle());
|
||||
// TODO: Set node type and parameters
|
||||
|
||||
// Add node data to compiled data
|
||||
CompiledNodeData.Add(NodeData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UFLESHCompiler::SortNodes()
|
||||
{
|
||||
// TODO: Implement topological sort to determine execution order
|
||||
|
||||
// For now, just add all nodes in order
|
||||
for (int32 i = 0; i < CompiledNodeData.Num(); i++)
|
||||
{
|
||||
ExecutionOrder.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool UFLESHCompiler::ValidateGraph()
|
||||
{
|
||||
// TODO: Implement graph validation
|
||||
|
||||
// Check if source graph is valid
|
||||
if (!SourceGraph)
|
||||
{
|
||||
ErrorMessage = TEXT("Source graph is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if graph has at least one node
|
||||
TArray<UFLESHGraphNode*> AllNodes = SourceGraph->GetAllNodes();
|
||||
if (AllNodes.Num() == 0)
|
||||
{
|
||||
ErrorMessage = TEXT("Graph has no nodes");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
195
Source/FLESHEditor/Private/FLESHGraph/FLESHExecutor.cpp
Normal file
195
Source/FLESHEditor/Private/FLESHGraph/FLESHExecutor.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "FLESHGraph/FLESHExecutor.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"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFLESHExecutor, Log, All);
|
||||
|
||||
UFLESHExecutor::UFLESHExecutor()
|
||||
{
|
||||
// Initialize default values
|
||||
Compiler = nullptr;
|
||||
TargetActor = nullptr;
|
||||
TargetSkeletalMesh = nullptr;
|
||||
|
||||
// Create boolean cut tool
|
||||
CutTool = CreateDefaultSubobject<UBooleanCutTool>(TEXT("CutTool"));
|
||||
}
|
||||
|
||||
void UFLESHExecutor::Initialize(UFLESHCompiler* InCompiler)
|
||||
{
|
||||
// Set compiler reference
|
||||
Compiler = InCompiler;
|
||||
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH Executor initialized with compiler: %s"),
|
||||
Compiler ? TEXT("Valid") : TEXT("Invalid"));
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::Execute(AActor* InTargetActor)
|
||||
{
|
||||
// Set target actor
|
||||
TargetActor = InTargetActor;
|
||||
|
||||
// Find skeletal mesh component
|
||||
if (!FindTargetSkeletalMesh())
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Cannot execute FLESH graph, skeletal mesh component not found"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if compiler is valid
|
||||
if (!Compiler)
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Cannot execute FLESH graph, invalid compiler"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if compilation was successful
|
||||
if (!Compiler->IsCompilationSuccessful())
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Cannot execute FLESH graph, compilation failed: %s"),
|
||||
*Compiler->GetErrorMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get execution order from compiler
|
||||
TArray<int32> ExecutionOrder = Compiler->GetExecutionOrder();
|
||||
|
||||
// Get compiled node data
|
||||
TArray<FFLESHNodeData> CompiledNodeData = Compiler->GetCompiledNodeData();
|
||||
|
||||
// Execute nodes in order
|
||||
for (int32 NodeIndex : ExecutionOrder)
|
||||
{
|
||||
// Check if node index is valid
|
||||
if (!CompiledNodeData.IsValidIndex(NodeIndex))
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Invalid node index in execution order: %d"), NodeIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Execute node
|
||||
if (!ExecuteNode(CompiledNodeData[NodeIndex]))
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Failed to execute node: %s"),
|
||||
*CompiledNodeData[NodeIndex].NodeName.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Graph executed successfully on actor: %s"),
|
||||
TargetActor ? *TargetActor->GetName() : TEXT("Invalid"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UFLESHExecutor::SetCutTool(UBooleanCutTool* InCutTool)
|
||||
{
|
||||
CutTool = InCutTool;
|
||||
}
|
||||
|
||||
UBooleanCutTool* UFLESHExecutor::GetCutTool() const
|
||||
{
|
||||
return CutTool;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::FindTargetSkeletalMesh()
|
||||
{
|
||||
// Check if target actor is valid
|
||||
if (!TargetActor)
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Invalid target actor"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find skeletal mesh component
|
||||
TargetSkeletalMesh = TargetActor->FindComponentByClass<USkeletalMeshComponent>();
|
||||
|
||||
// Check if skeletal mesh component is valid
|
||||
if (!TargetSkeletalMesh)
|
||||
{
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Target actor does not have a skeletal mesh component"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// Execute node based on type
|
||||
switch (NodeData.NodeType)
|
||||
{
|
||||
case EFLESHNodeType::Cut:
|
||||
return ExecuteCutNode(NodeData);
|
||||
|
||||
case EFLESHNodeType::BloodEffect:
|
||||
return ExecuteBloodEffectNode(NodeData);
|
||||
|
||||
case EFLESHNodeType::Physics:
|
||||
return ExecutePhysicsNode(NodeData);
|
||||
|
||||
case EFLESHNodeType::Organ:
|
||||
return ExecuteOrganNode(NodeData);
|
||||
|
||||
case EFLESHNodeType::Wound:
|
||||
return ExecuteWoundNode(NodeData);
|
||||
|
||||
case EFLESHNodeType::BoneSelection:
|
||||
return ExecuteBoneSelectionNode(NodeData);
|
||||
|
||||
default:
|
||||
UE_LOG(LogFLESHExecutor, Warning, TEXT("FLESH: Unknown node type for node: %s"),
|
||||
*NodeData.NodeName.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteCutNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement cut node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing cut node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteBloodEffectNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement blood effect node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing blood effect node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecutePhysicsNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement physics node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing physics node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteOrganNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement organ node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing organ node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteWoundNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement wound node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing wound node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHExecutor::ExecuteBoneSelectionNode(const FFLESHNodeData& NodeData)
|
||||
{
|
||||
// TODO: Implement bone selection node execution
|
||||
UE_LOG(LogFLESHExecutor, Log, TEXT("FLESH: Executing bone selection node: %s"), *NodeData.NodeName.ToString());
|
||||
return true;
|
||||
}
|
||||
230
Source/FLESHEditor/Private/FLESHGraph/FLESHGraph.cpp
Normal file
230
Source/FLESHEditor/Private/FLESHGraph/FLESHGraph.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include "FLESHGraph/FLESHGraph.h"
|
||||
#include "FLESHGraph/FLESHGraphNode.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
// Define log category
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFLESHGraph, Log, All);
|
||||
|
||||
UFLESHGraph::UFLESHGraph()
|
||||
{
|
||||
// Initialize default values
|
||||
RootNode = nullptr;
|
||||
}
|
||||
|
||||
void UFLESHGraph::Initialize()
|
||||
{
|
||||
// Clear graph
|
||||
ClearGraph();
|
||||
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH graph initialized"));
|
||||
}
|
||||
|
||||
UFLESHGraphNode* UFLESHGraph::AddNode(TSubclassOf<UFLESHGraphNode> NodeClass, const FVector2D& Position)
|
||||
{
|
||||
// Check if node class is valid
|
||||
if (!NodeClass)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Invalid node class"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create new node
|
||||
UFLESHGraphNode* NewNode = NewObject<UFLESHGraphNode>(this, NodeClass);
|
||||
if (!NewNode)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Failed to create node"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set node position
|
||||
NewNode->SetNodePosition(Position);
|
||||
|
||||
// Add to node list
|
||||
Nodes.Add(NewNode);
|
||||
|
||||
// If it's the first node, set as root node
|
||||
if (Nodes.Num() == 1)
|
||||
{
|
||||
RootNode = NewNode;
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Added new node: %s"), *NewNode->GetNodeTitle());
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
bool UFLESHGraph::RemoveNode(UFLESHGraphNode* Node)
|
||||
{
|
||||
// Check if node is valid
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Trying to remove invalid node"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disconnect from other nodes
|
||||
TArray<UFLESHGraphNode*> InputNodes = Node->GetInputNodes();
|
||||
TArray<UFLESHGraphNode*> OutputNodes = Node->GetOutputNodes();
|
||||
|
||||
for (UFLESHGraphNode* InputNode : InputNodes)
|
||||
{
|
||||
DisconnectNodes(InputNode, Node);
|
||||
}
|
||||
|
||||
for (UFLESHGraphNode* OutputNode : OutputNodes)
|
||||
{
|
||||
DisconnectNodes(Node, OutputNode);
|
||||
}
|
||||
|
||||
// If it's the root node, reset root node
|
||||
if (Node == RootNode)
|
||||
{
|
||||
RootNode = nullptr;
|
||||
|
||||
// If there are other nodes, choose the first one as the new root node
|
||||
if (Nodes.Num() > 1)
|
||||
{
|
||||
for (UFLESHGraphNode* OtherNode : Nodes)
|
||||
{
|
||||
if (OtherNode != Node)
|
||||
{
|
||||
RootNode = OtherNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from node list
|
||||
bool bRemoved = Nodes.Remove(Node) > 0;
|
||||
|
||||
if (bRemoved)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Removed node: %s"), *Node->GetNodeTitle());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Node is not in graph: %s"), *Node->GetNodeTitle());
|
||||
}
|
||||
|
||||
return bRemoved;
|
||||
}
|
||||
|
||||
bool UFLESHGraph::ConnectNodes(UFLESHGraphNode* SourceNode, UFLESHGraphNode* TargetNode)
|
||||
{
|
||||
// Check if nodes are valid
|
||||
if (!SourceNode || !TargetNode)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Trying to connect invalid nodes"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add connection
|
||||
bool bSourceAdded = SourceNode->AddOutputNode(TargetNode);
|
||||
bool bTargetAdded = TargetNode->AddInputNode(SourceNode);
|
||||
|
||||
if (bSourceAdded && bTargetAdded)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Connected nodes: %s -> %s"),
|
||||
*SourceNode->GetNodeTitle(), *TargetNode->GetNodeTitle());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If adding failed, rollback changes
|
||||
if (bSourceAdded)
|
||||
{
|
||||
SourceNode->RemoveOutputNode(TargetNode);
|
||||
}
|
||||
|
||||
if (bTargetAdded)
|
||||
{
|
||||
TargetNode->RemoveInputNode(SourceNode);
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Connecting nodes failed: %s -> %s"),
|
||||
*SourceNode->GetNodeTitle(), *TargetNode->GetNodeTitle());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UFLESHGraph::DisconnectNodes(UFLESHGraphNode* SourceNode, UFLESHGraphNode* TargetNode)
|
||||
{
|
||||
// Check if nodes are valid
|
||||
if (!SourceNode || !TargetNode)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Trying to disconnect invalid nodes"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove connection
|
||||
bool bSourceRemoved = SourceNode->RemoveOutputNode(TargetNode);
|
||||
bool bTargetRemoved = TargetNode->RemoveInputNode(SourceNode);
|
||||
|
||||
if (bSourceRemoved && bTargetRemoved)
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Disconnected nodes: %s -> %s"),
|
||||
*SourceNode->GetNodeTitle(), *TargetNode->GetNodeTitle());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Disconnecting nodes failed: %s -> %s"),
|
||||
*SourceNode->GetNodeTitle(), *TargetNode->GetNodeTitle());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TArray<UFLESHGraphNode*> UFLESHGraph::GetAllNodes() const
|
||||
{
|
||||
return Nodes;
|
||||
}
|
||||
|
||||
UFLESHGraphNode* UFLESHGraph::GetRootNode() const
|
||||
{
|
||||
return RootNode;
|
||||
}
|
||||
|
||||
void UFLESHGraph::SetRootNode(UFLESHGraphNode* InRootNode)
|
||||
{
|
||||
// Check if node is in graph
|
||||
if (InRootNode && Nodes.Contains(InRootNode))
|
||||
{
|
||||
RootNode = InRootNode;
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Set new root node: %s"), *RootNode->GetNodeTitle());
|
||||
}
|
||||
else if (!InRootNode)
|
||||
{
|
||||
RootNode = nullptr;
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Clear root node"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHGraph, Warning, TEXT("FLESH: Trying to set node not in graph as root node"));
|
||||
}
|
||||
}
|
||||
|
||||
void UFLESHGraph::ClearGraph()
|
||||
{
|
||||
// Clear node list
|
||||
Nodes.Empty();
|
||||
|
||||
// Clear root node
|
||||
RootNode = nullptr;
|
||||
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Cleared graph"));
|
||||
}
|
||||
|
||||
bool UFLESHGraph::SaveGraph()
|
||||
{
|
||||
// TODO: implement graph saving feature
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Graph saving feature not implemented"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UFLESHGraph::LoadGraph()
|
||||
{
|
||||
// TODO: implement graph loading feature
|
||||
UE_LOG(LogFLESHGraph, Log, TEXT("FLESH: Graph loading feature not implemented"));
|
||||
return false;
|
||||
}
|
||||
164
Source/FLESHEditor/Private/FLESHGraph/FLESHGraphNode.cpp
Normal file
164
Source/FLESHEditor/Private/FLESHGraph/FLESHGraphNode.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "FLESHGraph/FLESHGraphNode.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
// 定义日志类别
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFLESHGraphNode, Log, All);
|
||||
|
||||
UFLESHGraphNode::UFLESHGraphNode()
|
||||
{
|
||||
// 初始化默认值
|
||||
NodeTitle = TEXT("FLESH节点");
|
||||
NodeType = EFLESHNodeType::None;
|
||||
NodeColor = FLinearColor(0.5f, 0.5f, 0.5f);
|
||||
NodePosition = FVector2D::ZeroVector;
|
||||
}
|
||||
|
||||
FString UFLESHGraphNode::GetNodeTitle() const
|
||||
{
|
||||
return NodeTitle;
|
||||
}
|
||||
|
||||
EFLESHNodeType UFLESHGraphNode::GetNodeType() const
|
||||
{
|
||||
return NodeType;
|
||||
}
|
||||
|
||||
FLinearColor UFLESHGraphNode::GetNodeColor() const
|
||||
{
|
||||
return NodeColor;
|
||||
}
|
||||
|
||||
FVector2D UFLESHGraphNode::GetNodePosition() const
|
||||
{
|
||||
return NodePosition;
|
||||
}
|
||||
|
||||
void UFLESHGraphNode::SetNodePosition(const FVector2D& InPosition)
|
||||
{
|
||||
NodePosition = InPosition;
|
||||
}
|
||||
|
||||
TArray<UFLESHGraphNode*> UFLESHGraphNode::GetInputNodes() const
|
||||
{
|
||||
return InputNodes;
|
||||
}
|
||||
|
||||
TArray<UFLESHGraphNode*> UFLESHGraphNode::GetOutputNodes() const
|
||||
{
|
||||
return OutputNodes;
|
||||
}
|
||||
|
||||
bool UFLESHGraphNode::AddInputNode(UFLESHGraphNode* Node)
|
||||
{
|
||||
// 检查节点是否有效
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 尝试添加无效的输入节点"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查节点是否已经在输入列表中
|
||||
if (InputNodes.Contains(Node))
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 节点已经在输入列表中: %s"), *Node->GetNodeTitle());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加到输入列表
|
||||
InputNodes.Add(Node);
|
||||
|
||||
UE_LOG(LogFLESHGraphNode, Log, TEXT("FLESH: 添加了输入节点: %s -> %s"), *Node->GetNodeTitle(), *GetNodeTitle());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHGraphNode::AddOutputNode(UFLESHGraphNode* Node)
|
||||
{
|
||||
// 检查节点是否有效
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 尝试添加无效的输出节点"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查节点是否已经在输出列表中
|
||||
if (OutputNodes.Contains(Node))
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 节点已经在输出列表中: %s"), *Node->GetNodeTitle());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加到输出列表
|
||||
OutputNodes.Add(Node);
|
||||
|
||||
UE_LOG(LogFLESHGraphNode, Log, TEXT("FLESH: 添加了输出节点: %s -> %s"), *GetNodeTitle(), *Node->GetNodeTitle());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UFLESHGraphNode::RemoveInputNode(UFLESHGraphNode* Node)
|
||||
{
|
||||
// 检查节点是否有效
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 尝试移除无效的输入节点"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从输入列表中移除
|
||||
bool bRemoved = InputNodes.Remove(Node) > 0;
|
||||
|
||||
if (bRemoved)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Log, TEXT("FLESH: 移除了输入节点: %s -> %s"), *Node->GetNodeTitle(), *GetNodeTitle());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 节点不在输入列表中: %s"), *Node->GetNodeTitle());
|
||||
}
|
||||
|
||||
return bRemoved;
|
||||
}
|
||||
|
||||
bool UFLESHGraphNode::RemoveOutputNode(UFLESHGraphNode* Node)
|
||||
{
|
||||
// 检查节点是否有效
|
||||
if (!Node)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 尝试移除无效的输出节点"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从输出列表中移除
|
||||
bool bRemoved = OutputNodes.Remove(Node) > 0;
|
||||
|
||||
if (bRemoved)
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Log, TEXT("FLESH: 移除了输出节点: %s -> %s"), *GetNodeTitle(), *Node->GetNodeTitle());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFLESHGraphNode, Warning, TEXT("FLESH: 节点不在输出列表中: %s"), *Node->GetNodeTitle());
|
||||
}
|
||||
|
||||
return bRemoved;
|
||||
}
|
||||
|
||||
bool UFLESHGraphNode::CompileNode(FFLESHNodeData& OutNodeData)
|
||||
{
|
||||
// 设置基本节点数据
|
||||
OutNodeData.NodeName = FName(*GetNodeTitle());
|
||||
OutNodeData.NodeType = GetNodeType();
|
||||
|
||||
// 设置连接的节点
|
||||
OutNodeData.ConnectedNodes.Empty();
|
||||
for (UFLESHGraphNode* OutputNode : OutputNodes)
|
||||
{
|
||||
// TODO: 获取节点索引
|
||||
// OutNodeData.ConnectedNodes.Add(NodeIndex);
|
||||
}
|
||||
|
||||
UE_LOG(LogFLESHGraphNode, Log, TEXT("FLESH: 编译了节点: %s"), *GetNodeTitle());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -4,7 +4,20 @@
|
||||
#include "CanvasTypes.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Components/DirectionalLightComponent.h"
|
||||
#include "Components/SkyLightComponent.h"
|
||||
#include "Slate/SceneViewport.h"
|
||||
#include "PreviewScene.h"
|
||||
#include "EditorViewportClient.h"
|
||||
#include "EngineGlobals.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine.h"
|
||||
#include "CanvasTypes.h"
|
||||
#include "SceneView.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "Components/SplineComponent.h"
|
||||
#include "EditorModeManager.h"
|
||||
#include "UnrealWidget.h"
|
||||
|
||||
FFLESHViewportClient::FFLESHViewportClient(FFLESHEditor* InEditor)
|
||||
: FEditorViewportClient(nullptr)
|
||||
@@ -13,7 +26,7 @@ FFLESHViewportClient::FFLESHViewportClient(FFLESHEditor* InEditor)
|
||||
, bShowWireframe(false)
|
||||
, bShowBones(false)
|
||||
{
|
||||
// Create a valid viewport scene
|
||||
// Create a valid preview scene
|
||||
PreviewScene = MakeShareable(new FPreviewScene(FPreviewScene::ConstructionValues()));
|
||||
|
||||
// Set the scene for FEditorViewportClient - use constructor instead of SetPreviewScene
|
||||
@@ -41,6 +54,19 @@ FFLESHViewportClient::FFLESHViewportClient(FFLESHEditor* InEditor)
|
||||
|
||||
// Set default render mode
|
||||
SetViewMode(VMI_Lit);
|
||||
|
||||
// Set camera control options similar to asset editor
|
||||
SetRealtime(true);
|
||||
bSetListenerPosition = false;
|
||||
|
||||
// Enable standard editor camera controls
|
||||
if (Widget)
|
||||
{
|
||||
Widget->SetSnapEnabled(true);
|
||||
}
|
||||
|
||||
// Load and display objects from NodeTree
|
||||
LoadNodesFromNodeTree();
|
||||
}
|
||||
|
||||
FFLESHViewportClient::~FFLESHViewportClient()
|
||||
@@ -52,6 +78,23 @@ void FFLESHViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface*
|
||||
{
|
||||
FEditorViewportClient::Draw(View, PDI);
|
||||
|
||||
// Draw navigation widget (coordinate axes)
|
||||
if (PDI)
|
||||
{
|
||||
// Draw coordinate system axes
|
||||
const float AxisLength = 50.0f;
|
||||
const float AxisThickness = 1.0f;
|
||||
|
||||
// Draw X axis (red)
|
||||
PDI->DrawLine(FVector::ZeroVector, FVector(AxisLength, 0.0f, 0.0f), FLinearColor::Red, SDPG_Foreground, AxisThickness);
|
||||
|
||||
// Draw Y axis (green)
|
||||
PDI->DrawLine(FVector::ZeroVector, FVector(0.0f, AxisLength, 0.0f), FLinearColor::Green, SDPG_Foreground, AxisThickness);
|
||||
|
||||
// Draw Z axis (blue)
|
||||
PDI->DrawLine(FVector::ZeroVector, FVector(0.0f, 0.0f, AxisLength), FLinearColor::Blue, SDPG_Foreground, AxisThickness);
|
||||
}
|
||||
|
||||
// Draw custom UI elements
|
||||
if (Viewport)
|
||||
{
|
||||
@@ -111,7 +154,7 @@ bool FFLESHViewportClient::InputKey(const FInputKeyEventArgs& EventArgs)
|
||||
else if (EventArgs.Key == EKeys::F)
|
||||
{
|
||||
// F key focuses on selected object
|
||||
// TODO: Add logic to focus on the selected object
|
||||
FocusOnSelectedNode();
|
||||
bHandled = true;
|
||||
}
|
||||
}
|
||||
@@ -134,6 +177,15 @@ bool FFLESHViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, F
|
||||
return InputKey(Args);
|
||||
}
|
||||
|
||||
// This version of InputAxis is deprecated, but kept for compatibility
|
||||
bool FFLESHViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad)
|
||||
{
|
||||
// Create InputKeyEventArgs for the new API
|
||||
FInputKeyEventArgs Args(InViewport, ControllerId, Key, IE_Axis);
|
||||
Args.AmountDepressed = Delta;
|
||||
return InputKey(Args);
|
||||
}
|
||||
|
||||
void FFLESHViewportClient::ResetCamera()
|
||||
{
|
||||
// Reset camera position and rotation
|
||||
@@ -173,3 +225,164 @@ void FFLESHViewportClient::ToggleBones()
|
||||
// Invalidate view
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void FFLESHViewportClient::LoadNodesFromNodeTree()
|
||||
{
|
||||
if (!PreviewScene.IsValid() || !Editor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear current preview scene components by creating a new one
|
||||
PreviewScene = MakeShareable(new FPreviewScene(FPreviewScene::ConstructionValues()));
|
||||
FEditorViewportClient::PreviewScene = PreviewScene.Get();
|
||||
|
||||
// Add a default light
|
||||
UDirectionalLightComponent* DirectionalLight = NewObject<UDirectionalLightComponent>();
|
||||
DirectionalLight->SetMobility(EComponentMobility::Movable);
|
||||
DirectionalLight->SetIntensity(1.0f);
|
||||
PreviewScene->AddComponent(DirectionalLight, FTransform(FRotator(-45.0f, 45.0f, 0.0f)));
|
||||
|
||||
// Add a skylight for ambient lighting
|
||||
USkyLightComponent* SkyLight = NewObject<USkyLightComponent>();
|
||||
SkyLight->SetMobility(EComponentMobility::Movable);
|
||||
SkyLight->SourceType = ESkyLightSourceType::SLS_CapturedScene;
|
||||
PreviewScene->AddComponent(SkyLight, FTransform::Identity);
|
||||
|
||||
// Add a second directional light from another angle
|
||||
UDirectionalLightComponent* BackLight = NewObject<UDirectionalLightComponent>();
|
||||
BackLight->SetMobility(EComponentMobility::Movable);
|
||||
BackLight->SetIntensity(0.6f);
|
||||
BackLight->SetLightColor(FLinearColor(0.8f, 0.8f, 1.0f)); // Slightly blue backlight
|
||||
PreviewScene->AddComponent(BackLight, FTransform(FRotator(45.0f, -135.0f, 0.0f)));
|
||||
|
||||
// Get all nodes from NodeTree
|
||||
const TArray<TSharedPtr<FVisceraNodeItem>>& NodeRoots = Editor->GetNodeTreeRoots();
|
||||
|
||||
// Recursively load all nodes
|
||||
for (const TSharedPtr<FVisceraNodeItem>& RootNode : NodeRoots)
|
||||
{
|
||||
if (RootNode.IsValid())
|
||||
{
|
||||
LoadNodeRecursive(RootNode, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Update viewport
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void FFLESHViewportClient::LoadNodeRecursive(TSharedPtr<FVisceraNodeItem> Node, USceneComponent* ParentComponent)
|
||||
{
|
||||
if (!Node.IsValid() || !PreviewScene.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USceneComponent* NodeComponent = nullptr;
|
||||
|
||||
// Simplified node loading logic, use static mesh for all node types
|
||||
UStaticMeshComponent* MeshComponent = NewObject<UStaticMeshComponent>();
|
||||
|
||||
// Choose different mesh and color based on node type
|
||||
FString MeshPath;
|
||||
FLinearColor Color;
|
||||
|
||||
if (Node->NodeType.Equals(TEXT("SoftBody")))
|
||||
{
|
||||
MeshPath = TEXT("/Engine/BasicShapes/Sphere.Sphere");
|
||||
Color = FLinearColor(1.0f, 0.5f, 0.5f, 0.7f); // Translucent red
|
||||
}
|
||||
else if (Node->NodeType.Equals(TEXT("LineChain")))
|
||||
{
|
||||
MeshPath = TEXT("/Engine/BasicShapes/Cylinder.Cylinder");
|
||||
Color = FLinearColor(0.5f, 0.5f, 1.0f, 0.7f); // Translucent blue
|
||||
}
|
||||
else if (Node->NodeType.Equals(TEXT("Plane")))
|
||||
{
|
||||
MeshPath = TEXT("/Engine/BasicShapes/Plane.Plane");
|
||||
Color = FLinearColor(0.5f, 0.5f, 1.0f, 0.5f); // Translucent blue
|
||||
}
|
||||
else if (Node->NodeType.Equals(TEXT("Matrix")))
|
||||
{
|
||||
MeshPath = TEXT("/Engine/BasicShapes/Cube.Cube");
|
||||
Color = FLinearColor(0.5f, 1.0f, 0.5f, 0.7f); // Translucent green
|
||||
MeshComponent->SetWorldScale3D(FVector(0.25f, 0.25f, 0.25f)); // Scale down cube
|
||||
}
|
||||
else // Default to sphere
|
||||
{
|
||||
MeshPath = TEXT("/Engine/BasicShapes/Sphere.Sphere");
|
||||
Color = FLinearColor(1.0f, 1.0f, 1.0f, 0.7f); // Translucent white
|
||||
}
|
||||
|
||||
// Load static mesh
|
||||
UStaticMesh* StaticMesh = LoadObject<UStaticMesh>(nullptr, *MeshPath);
|
||||
if (StaticMesh)
|
||||
{
|
||||
MeshComponent->SetStaticMesh(StaticMesh);
|
||||
|
||||
// Set translucent material
|
||||
UMaterial* Material = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));
|
||||
if (Material)
|
||||
{
|
||||
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(Material, MeshComponent);
|
||||
if (DynamicMaterial)
|
||||
{
|
||||
DynamicMaterial->SetVectorParameterValue(TEXT("Color"), Color);
|
||||
MeshComponent->SetMaterial(0, DynamicMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add to preview scene
|
||||
PreviewScene->AddComponent(MeshComponent, FTransform::Identity);
|
||||
NodeComponent = MeshComponent;
|
||||
|
||||
// If parent component exists, set parent-child relationship
|
||||
if (NodeComponent && ParentComponent)
|
||||
{
|
||||
NodeComponent->AttachToComponent(ParentComponent, FAttachmentTransformRules::KeepRelativeTransform);
|
||||
}
|
||||
|
||||
// Recursively load child nodes
|
||||
for (const TSharedPtr<FVisceraNodeItem>& ChildNode : Node->Children)
|
||||
{
|
||||
if (ChildNode.IsValid() && NodeComponent)
|
||||
{
|
||||
LoadNodeRecursive(ChildNode, NodeComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FFLESHViewportClient::UpdateVisibleNodes()
|
||||
{
|
||||
// Reload all nodes
|
||||
LoadNodesFromNodeTree();
|
||||
}
|
||||
|
||||
void FFLESHViewportClient::FocusOnSelectedNode()
|
||||
{
|
||||
if (!Editor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get currently selected node
|
||||
TSharedPtr<FVisceraNodeItem> SelectedNode = Editor->GetSelectedNodeItem();
|
||||
if (!SelectedNode.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Position camera at the selected node's location
|
||||
// Note: Here we use a default position since we don't have actual position information for the node
|
||||
// In a real implementation, we should find the component for this node and get its position
|
||||
FVector FocusLocation = FVector::ZeroVector;
|
||||
|
||||
// Set camera position and rotation
|
||||
SetViewLocation(FocusLocation + FVector(0.0f, 100.0f, 0.0f));
|
||||
SetViewRotation(FRotator(0.0f, -90.0f, 0.0f));
|
||||
|
||||
// Update view
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user