Files
FLESH/Source/FLESHEditor/Private/DismembermentGraph/DismembermentPreviewManager.cpp

788 lines
27 KiB
C++

#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;
}
}