Update
This commit is contained in:
61
Source/FLESH/FLESH.Build.cs
Normal file
61
Source/FLESH/FLESH.Build.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class FLESH : ModuleRules
|
||||
{
|
||||
public FLESH(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"Niagara",
|
||||
"GeometryCore",
|
||||
"GeometryFramework",
|
||||
"GeometryScriptingCore",
|
||||
"ProceduralMeshComponent",
|
||||
"PhysicsCore",
|
||||
"ChaosCloth",
|
||||
"ChaosNiagara",
|
||||
"PhysicsControl",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
136
Source/FLESH/Private/AnatomicalLayerSystem.cpp
Normal file
136
Source/FLESH/Private/AnatomicalLayerSystem.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "AnatomicalLayerSystem.h"
|
||||
|
||||
// Constructor
|
||||
UAnatomicalLayerSystem::UAnatomicalLayerSystem()
|
||||
{
|
||||
}
|
||||
|
||||
// Add a new anatomical layer
|
||||
int32 UAnatomicalLayerSystem::AddLayer(const FName& LayerName, EAnatomicalLayerType LayerType)
|
||||
{
|
||||
// Create new layer
|
||||
FAnatomicalLayer NewLayer;
|
||||
NewLayer.LayerName = LayerName;
|
||||
NewLayer.LayerType = LayerType;
|
||||
|
||||
// Set default properties based on type
|
||||
switch (LayerType)
|
||||
{
|
||||
case EAnatomicalLayerType::Skin:
|
||||
NewLayer.Thickness = 0.5f;
|
||||
NewLayer.Density = 1.0f;
|
||||
NewLayer.Elasticity = 0.7f;
|
||||
NewLayer.FractureThreshold = 50.0f;
|
||||
break;
|
||||
case EAnatomicalLayerType::Muscle:
|
||||
NewLayer.Thickness = 1.0f;
|
||||
NewLayer.Density = 1.2f;
|
||||
NewLayer.Elasticity = 0.6f;
|
||||
NewLayer.FractureThreshold = 80.0f;
|
||||
break;
|
||||
case EAnatomicalLayerType::Bone:
|
||||
NewLayer.Thickness = 2.0f;
|
||||
NewLayer.Density = 1.5f;
|
||||
NewLayer.Elasticity = 0.3f;
|
||||
NewLayer.FractureThreshold = 150.0f;
|
||||
break;
|
||||
case EAnatomicalLayerType::Organ:
|
||||
NewLayer.Thickness = 1.5f;
|
||||
NewLayer.Density = 1.1f;
|
||||
NewLayer.Elasticity = 0.8f;
|
||||
NewLayer.FractureThreshold = 30.0f;
|
||||
break;
|
||||
case EAnatomicalLayerType::Blood:
|
||||
NewLayer.Thickness = 0.1f;
|
||||
NewLayer.Density = 1.0f;
|
||||
NewLayer.Elasticity = 0.9f;
|
||||
NewLayer.FractureThreshold = 10.0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Add to array
|
||||
Layers.Add(NewLayer);
|
||||
|
||||
// Return index
|
||||
return Layers.Num() - 1;
|
||||
}
|
||||
|
||||
// Remove anatomical layer at specified index
|
||||
bool UAnatomicalLayerSystem::RemoveLayer(int32 LayerIndex)
|
||||
{
|
||||
// Check if index is valid
|
||||
if (Layers.IsValidIndex(LayerIndex))
|
||||
{
|
||||
Layers.RemoveAt(LayerIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get anatomical layer at specified index
|
||||
FAnatomicalLayer UAnatomicalLayerSystem::GetLayer(int32 LayerIndex) const
|
||||
{
|
||||
// Check if index is valid
|
||||
if (Layers.IsValidIndex(LayerIndex))
|
||||
{
|
||||
return Layers[LayerIndex];
|
||||
}
|
||||
|
||||
// Return empty layer
|
||||
return FAnatomicalLayer();
|
||||
}
|
||||
|
||||
// Get all anatomical layers
|
||||
TArray<FAnatomicalLayer> UAnatomicalLayerSystem::GetAllLayers() const
|
||||
{
|
||||
return Layers;
|
||||
}
|
||||
|
||||
// Set anatomical layer data at specified index
|
||||
bool UAnatomicalLayerSystem::SetLayer(int32 LayerIndex, const FAnatomicalLayer& Layer)
|
||||
{
|
||||
// Check if index is valid
|
||||
if (Layers.IsValidIndex(LayerIndex))
|
||||
{
|
||||
Layers[LayerIndex] = Layer;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get layer count
|
||||
int32 UAnatomicalLayerSystem::GetLayerCount() const
|
||||
{
|
||||
return Layers.Num();
|
||||
}
|
||||
|
||||
// Get layers by type
|
||||
TArray<FAnatomicalLayer> UAnatomicalLayerSystem::GetLayersByType(EAnatomicalLayerType LayerType) const
|
||||
{
|
||||
TArray<FAnatomicalLayer> Result;
|
||||
|
||||
// Iterate through all layers, find layers matching the type
|
||||
for (const FAnatomicalLayer& Layer : Layers)
|
||||
{
|
||||
if (Layer.LayerType == LayerType)
|
||||
{
|
||||
Result.Add(Layer);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Set physics for all layers
|
||||
void UAnatomicalLayerSystem::SetPhysicsForAllLayers(bool bEnable)
|
||||
{
|
||||
// Iterate through all layers, set physics property
|
||||
for (FAnatomicalLayer& Layer : Layers)
|
||||
{
|
||||
Layer.bEnablePhysics = bEnable;
|
||||
}
|
||||
}
|
114
Source/FLESH/Private/BloodPool.cpp
Normal file
114
Source/FLESH/Private/BloodPool.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "BloodPool.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/DecalComponent.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
// Sets default values
|
||||
ABloodPool::ABloodPool()
|
||||
{
|
||||
// Set this actor to call Tick() every frame
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
// Create and set up the collision component
|
||||
Collision = CreateDefaultSubobject<UBoxComponent>(TEXT("Collision"));
|
||||
RootComponent = Collision;
|
||||
Collision->SetCollisionProfileName(TEXT("NoCollision"));
|
||||
Collision->SetBoxExtent(FVector(1.0f, 100.0f, 100.0f));
|
||||
|
||||
// Create and set up the decal component
|
||||
Decal = CreateDefaultSubobject<UDecalComponent>(TEXT("Decal"));
|
||||
Decal->SetupAttachment(RootComponent);
|
||||
Decal->SetRelativeRotation(FRotator(90.0f, 0.0f, 0.0f));
|
||||
Decal->DecalSize = FVector(10.0f, 100.0f, 100.0f);
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ABloodPool::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Set the initial decal material
|
||||
if (DecalMaterial)
|
||||
{
|
||||
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(DecalMaterial, this);
|
||||
if (DynamicMaterial)
|
||||
{
|
||||
// Set the color parameter if it exists
|
||||
DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor);
|
||||
Decal->SetMaterial(0, DynamicMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the pool size
|
||||
UpdatePoolSize();
|
||||
}
|
||||
|
||||
void ABloodPool::SetPoolSize(float NewSize)
|
||||
{
|
||||
PoolSize = FMath::Max(0.1f, NewSize);
|
||||
UpdatePoolSize();
|
||||
}
|
||||
|
||||
void ABloodPool::SetPoolColor(const FLinearColor& NewColor)
|
||||
{
|
||||
PoolColor = NewColor;
|
||||
|
||||
// Update the material color
|
||||
if (Decal && Decal->GetMaterial(0))
|
||||
{
|
||||
UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Decal->GetMaterial(0));
|
||||
if (DynamicMaterial)
|
||||
{
|
||||
DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ABloodPool::StartExpansion(float ExpansionRate, float MaxSize)
|
||||
{
|
||||
this->ExpansionRate = ExpansionRate;
|
||||
MaxPoolSize = MaxSize;
|
||||
bIsExpanding = true;
|
||||
|
||||
// Start the expansion timer
|
||||
GetWorldTimerManager().SetTimer(ExpansionTimerHandle, this, &ABloodPool::ExpandPool, 0.1f, true);
|
||||
}
|
||||
|
||||
void ABloodPool::UpdatePoolSize()
|
||||
{
|
||||
// Update the decal size
|
||||
if (Decal)
|
||||
{
|
||||
Decal->DecalSize = FVector(10.0f, 100.0f * PoolSize, 100.0f * PoolSize);
|
||||
}
|
||||
|
||||
// Update the collision box size
|
||||
if (Collision)
|
||||
{
|
||||
Collision->SetBoxExtent(FVector(1.0f, 100.0f * PoolSize, 100.0f * PoolSize));
|
||||
}
|
||||
}
|
||||
|
||||
void ABloodPool::ExpandPool()
|
||||
{
|
||||
if (!bIsExpanding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase the pool size
|
||||
PoolSize += ExpansionRate * 0.1f;
|
||||
|
||||
// Check if we've reached the maximum size
|
||||
if (PoolSize >= MaxPoolSize)
|
||||
{
|
||||
PoolSize = MaxPoolSize;
|
||||
bIsExpanding = false;
|
||||
GetWorldTimerManager().ClearTimer(ExpansionTimerHandle);
|
||||
}
|
||||
|
||||
// Update the pool size
|
||||
UpdatePoolSize();
|
||||
}
|
166
Source/FLESH/Private/BloodSystem.cpp
Normal file
166
Source/FLESH/Private/BloodSystem.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "BloodSystem.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "Components/DecalComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UBloodSystem::UBloodSystem()
|
||||
{
|
||||
// Set this component to be initialized when the game starts, and to be ticked every frame
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
// Called when the game starts
|
||||
void UBloodSystem::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void UBloodSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
// Clean up finished blood effects
|
||||
for (int32 i = ActiveBloodEffects.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
if (!ActiveBloodEffects[i] || !ActiveBloodEffects[i]->IsActive())
|
||||
{
|
||||
ActiveBloodEffects.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UBloodSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity)
|
||||
{
|
||||
// Ensure we have a valid blood spray system
|
||||
if (!BloodSpraySystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Manage the number of active blood effects
|
||||
if (ActiveBloodEffects.Num() >= MaxBloodEffects)
|
||||
{
|
||||
// Remove the oldest blood effect
|
||||
if (ActiveBloodEffects[0])
|
||||
{
|
||||
ActiveBloodEffects[0]->DestroyComponent();
|
||||
}
|
||||
ActiveBloodEffects.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Create a rotation from the direction
|
||||
FRotator Rotation = Direction.Rotation();
|
||||
|
||||
// Spawn the blood effect
|
||||
UNiagaraComponent* BloodEffect = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
|
||||
GetWorld(),
|
||||
BloodSpraySystem,
|
||||
Location,
|
||||
Rotation,
|
||||
FVector(Intensity),
|
||||
true,
|
||||
true,
|
||||
ENCPoolMethod::AutoRelease
|
||||
);
|
||||
|
||||
if (BloodEffect)
|
||||
{
|
||||
// Set the intensity parameter if it exists
|
||||
BloodEffect->SetFloatParameter(FName("Intensity"), Intensity);
|
||||
|
||||
// Add to active blood effects
|
||||
ActiveBloodEffects.Add(BloodEffect);
|
||||
}
|
||||
}
|
||||
|
||||
AActor* UBloodSystem::CreateBloodPool(const FVector& Location, float Size)
|
||||
{
|
||||
// Ensure we have a valid blood pool class
|
||||
if (!BloodPoolClass)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Manage the number of active blood pools
|
||||
if (ActiveBloodPools.Num() >= MaxBloodPools)
|
||||
{
|
||||
// Remove the oldest blood pool
|
||||
if (ActiveBloodPools[0])
|
||||
{
|
||||
ActiveBloodPools[0]->Destroy();
|
||||
}
|
||||
ActiveBloodPools.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Spawn the blood pool
|
||||
FActorSpawnParameters SpawnParams;
|
||||
SpawnParams.Owner = GetOwner();
|
||||
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
||||
|
||||
// Trace downward to find the floor
|
||||
FHitResult HitResult;
|
||||
FVector TraceStart = Location;
|
||||
FVector TraceEnd = Location - FVector(0, 0, 200.0f);
|
||||
FCollisionQueryParams QueryParams;
|
||||
QueryParams.AddIgnoredActor(GetOwner());
|
||||
|
||||
if (GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECC_Visibility, QueryParams))
|
||||
{
|
||||
// Spawn at the hit location
|
||||
AActor* BloodPool = GetWorld()->SpawnActor<AActor>(BloodPoolClass, HitResult.Location, FRotator::ZeroRotator, SpawnParams);
|
||||
|
||||
if (BloodPool)
|
||||
{
|
||||
// Set the size of the blood pool
|
||||
BloodPool->SetActorScale3D(FVector(Size));
|
||||
|
||||
// Add to active blood pools
|
||||
ActiveBloodPools.Add(BloodPool);
|
||||
|
||||
return BloodPool;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UDecalComponent* UBloodSystem::ApplyBloodDecal(UPrimitiveComponent* Component, const FVector& Location, const FVector& Direction, float Size)
|
||||
{
|
||||
// Ensure we have a valid blood decal material
|
||||
if (!BloodDecalMaterial || !Component)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Manage the number of active blood decals
|
||||
if (ActiveBloodDecals.Num() >= MaxBloodDecals)
|
||||
{
|
||||
// Remove the oldest blood decal
|
||||
if (ActiveBloodDecals[0])
|
||||
{
|
||||
ActiveBloodDecals[0]->DestroyComponent();
|
||||
}
|
||||
ActiveBloodDecals.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Create a rotation from the direction (opposite to the direction for the decal to face outward)
|
||||
FRotator Rotation = (-Direction).Rotation();
|
||||
|
||||
// Create the decal component
|
||||
UDecalComponent* DecalComponent = NewObject<UDecalComponent>(Component);
|
||||
DecalComponent->SetupAttachment(Component);
|
||||
DecalComponent->SetRelativeLocation(Component->GetComponentTransform().InverseTransformPosition(Location));
|
||||
DecalComponent->SetRelativeRotation(Component->GetComponentTransform().InverseTransformRotation(Rotation.Quaternion()));
|
||||
DecalComponent->SetDecalMaterial(BloodDecalMaterial);
|
||||
DecalComponent->DecalSize = FVector(10.0f, Size * 100.0f, Size * 100.0f);
|
||||
DecalComponent->RegisterComponent();
|
||||
|
||||
// Add to active blood decals
|
||||
ActiveBloodDecals.Add(DecalComponent);
|
||||
|
||||
return DecalComponent;
|
||||
}
|
283
Source/FLESH/Private/BooleanCutTool.cpp
Normal file
283
Source/FLESH/Private/BooleanCutTool.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "BooleanCutTool.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "GeometryScriptingCore.h"
|
||||
#include "GeometryScript/MeshBooleanFunctions.h"
|
||||
#include "GeometryScript/MeshPrimitiveFunctions.h"
|
||||
#include "DynamicMesh/DynamicMesh3.h"
|
||||
#include "GeometryScript/MeshTransformFunctions.h"
|
||||
|
||||
// Constructor
|
||||
UBooleanCutTool::UBooleanCutTool()
|
||||
{
|
||||
}
|
||||
|
||||
// Perform boolean cut on static mesh
|
||||
TArray<UStaticMesh*> UBooleanCutTool::CutStaticMesh(UStaticMesh* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<UStaticMesh*> Result;
|
||||
|
||||
// Check if target mesh is valid
|
||||
if (!TargetMesh)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Create cut plane mesh
|
||||
UStaticMesh* CutPlaneMesh = CreateCutPlaneMesh(CutPlane);
|
||||
if (!CutPlaneMesh)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Use GeometryScript to perform boolean cut
|
||||
// Note: This is just a framework, actual implementation requires GeometryScript API
|
||||
|
||||
// Create positive part mesh
|
||||
UStaticMesh* PositiveMesh = NewObject<UStaticMesh>();
|
||||
// TODO: Use GeometryScript to perform boolean difference operation for positive part
|
||||
|
||||
// Create negative part mesh
|
||||
UStaticMesh* NegativeMesh = NewObject<UStaticMesh>();
|
||||
// TODO: Use GeometryScript to perform boolean difference operation for negative part
|
||||
|
||||
// If cap creation is needed
|
||||
if (bCreateCap)
|
||||
{
|
||||
// TODO: Create cap for positive and negative parts
|
||||
}
|
||||
|
||||
// Add to result array
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform boolean cut on skeletal mesh
|
||||
TArray<USkeletalMesh*> UBooleanCutTool::CutSkeletalMesh(USkeletalMesh* TargetMesh, const FCutPlane& CutPlane, FName BoneName, bool bCreateCap)
|
||||
{
|
||||
TArray<USkeletalMesh*> Result;
|
||||
|
||||
// Check if target mesh is valid
|
||||
if (!TargetMesh)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Create cut plane mesh
|
||||
UStaticMesh* CutPlaneMesh = CreateCutPlaneMesh(CutPlane);
|
||||
if (!CutPlaneMesh)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Use GeometryScript to perform boolean cut
|
||||
// Note: This is just a framework, actual implementation requires GeometryScript API
|
||||
|
||||
// If bone name is specified, only cut the part influenced by the bone
|
||||
if (BoneName != NAME_None)
|
||||
{
|
||||
// TODO: Get vertices influenced by the bone, only cut these vertices
|
||||
}
|
||||
|
||||
// Create positive part skeletal mesh
|
||||
USkeletalMesh* PositiveMesh = NewObject<USkeletalMesh>();
|
||||
// TODO: Use GeometryScript to perform boolean difference operation for positive part
|
||||
|
||||
// Create negative part skeletal mesh
|
||||
USkeletalMesh* NegativeMesh = NewObject<USkeletalMesh>();
|
||||
// TODO: Use GeometryScript to perform boolean difference operation for negative part
|
||||
|
||||
// If cap creation is needed
|
||||
if (bCreateCap)
|
||||
{
|
||||
// TODO: Create cap for positive and negative parts
|
||||
}
|
||||
|
||||
// Add to result array
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Perform boolean cut on procedural mesh
|
||||
TArray<UProceduralMeshComponent*> UBooleanCutTool::CutProceduralMesh(UProceduralMeshComponent* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
TArray<UProceduralMeshComponent*> Result;
|
||||
|
||||
// Check if target mesh is valid
|
||||
if (!TargetMesh)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Get mesh data
|
||||
TArray<FVector> Vertices;
|
||||
TArray<int32> Triangles;
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector2D> UVs;
|
||||
TArray<FColor> VertexColors;
|
||||
TArray<FProcMeshTangent> Tangents;
|
||||
|
||||
TargetMesh->GetSectionMeshData(0, Vertices, Triangles, Normals, UVs, VertexColors, Tangents);
|
||||
|
||||
// Calculate intersection points between cut plane and mesh
|
||||
TArray<FVector> IntersectionPoints = CalculateIntersectionPoints(Vertices, Triangles, CutPlane);
|
||||
|
||||
// Create positive part procedural mesh
|
||||
UProceduralMeshComponent* PositiveMesh = NewObject<UProceduralMeshComponent>(TargetMesh->GetOwner());
|
||||
PositiveMesh->RegisterComponent();
|
||||
|
||||
// Create negative part procedural mesh
|
||||
UProceduralMeshComponent* NegativeMesh = NewObject<UProceduralMeshComponent>(TargetMesh->GetOwner());
|
||||
NegativeMesh->RegisterComponent();
|
||||
|
||||
// Split mesh
|
||||
TArray<FVector> PositiveVertices;
|
||||
TArray<int32> PositiveTriangles;
|
||||
TArray<FVector> PositiveNormals;
|
||||
TArray<FVector2D> PositiveUVs;
|
||||
TArray<FColor> PositiveVertexColors;
|
||||
TArray<FProcMeshTangent> PositiveTangents;
|
||||
|
||||
TArray<FVector> NegativeVertices;
|
||||
TArray<int32> NegativeTriangles;
|
||||
TArray<FVector> NegativeNormals;
|
||||
TArray<FVector2D> NegativeUVs;
|
||||
TArray<FColor> NegativeVertexColors;
|
||||
TArray<FProcMeshTangent> NegativeTangents;
|
||||
|
||||
// TODO: Split mesh data based on cut plane
|
||||
|
||||
// Create positive part mesh
|
||||
PositiveMesh->CreateMeshSection(0, PositiveVertices, PositiveTriangles, PositiveNormals, PositiveUVs, PositiveVertexColors, PositiveTangents, true);
|
||||
|
||||
// Create negative part mesh
|
||||
NegativeMesh->CreateMeshSection(0, NegativeVertices, NegativeTriangles, NegativeNormals, NegativeUVs, NegativeVertexColors, NegativeTangents, true);
|
||||
|
||||
// If cap creation is needed
|
||||
if (bCreateCap)
|
||||
{
|
||||
CreateCapMesh(IntersectionPoints, CutPlane, PositiveMesh);
|
||||
CreateCapMesh(IntersectionPoints, CutPlane, NegativeMesh);
|
||||
}
|
||||
|
||||
// Set material
|
||||
if (CutMaterial)
|
||||
{
|
||||
PositiveMesh->SetMaterial(0, TargetMesh->GetMaterial(0));
|
||||
PositiveMesh->SetMaterial(1, CutMaterial);
|
||||
|
||||
NegativeMesh->SetMaterial(0, TargetMesh->GetMaterial(0));
|
||||
NegativeMesh->SetMaterial(1, CutMaterial);
|
||||
}
|
||||
|
||||
// Add to result array
|
||||
Result.Add(PositiveMesh);
|
||||
Result.Add(NegativeMesh);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Create cut plane mesh
|
||||
UStaticMesh* UBooleanCutTool::CreateCutPlaneMesh(const FCutPlane& CutPlane)
|
||||
{
|
||||
// Create a plane mesh
|
||||
UStaticMesh* PlaneMesh = NewObject<UStaticMesh>();
|
||||
|
||||
// TODO: Use GeometryScript to create plane mesh
|
||||
// Note: This is just a framework, actual implementation requires GeometryScript API
|
||||
|
||||
return PlaneMesh;
|
||||
}
|
||||
|
||||
// Set cut material
|
||||
void UBooleanCutTool::SetCutMaterial(UMaterialInterface* Material)
|
||||
{
|
||||
CutMaterial = Material;
|
||||
}
|
||||
|
||||
// Get cut material
|
||||
UMaterialInterface* UBooleanCutTool::GetCutMaterial() const
|
||||
{
|
||||
return CutMaterial;
|
||||
}
|
||||
|
||||
// Calculate intersection points between cut plane and mesh
|
||||
TArray<FVector> UBooleanCutTool::CalculateIntersectionPoints(const TArray<FVector>& Vertices, const TArray<int32>& Indices, const FCutPlane& CutPlane)
|
||||
{
|
||||
TArray<FVector> IntersectionPoints;
|
||||
|
||||
// Check if index array is for triangles
|
||||
if (Indices.Num() % 3 != 0)
|
||||
{
|
||||
return IntersectionPoints;
|
||||
}
|
||||
|
||||
// Iterate through all triangles
|
||||
for (int32 i = 0; i < Indices.Num(); i += 3)
|
||||
{
|
||||
// Get three vertices of the triangle
|
||||
FVector V0 = Vertices[Indices[i]];
|
||||
FVector V1 = Vertices[Indices[i + 1]];
|
||||
FVector V2 = Vertices[Indices[i + 2]];
|
||||
|
||||
// Calculate signed distance from each vertex to the cut plane
|
||||
float D0 = FVector::DotProduct(V0 - CutPlane.Location, CutPlane.Normal);
|
||||
float D1 = FVector::DotProduct(V1 - CutPlane.Location, CutPlane.Normal);
|
||||
float D2 = FVector::DotProduct(V2 - CutPlane.Location, CutPlane.Normal);
|
||||
|
||||
// Check if triangle intersects with cut plane
|
||||
if ((D0 * D1 <= 0.0f) || (D0 * D2 <= 0.0f) || (D1 * D2 <= 0.0f))
|
||||
{
|
||||
// Calculate intersection points of edges with cut plane
|
||||
if (D0 * D1 <= 0.0f)
|
||||
{
|
||||
// Edge V0-V1 intersects with cut plane
|
||||
float T = D0 / (D0 - D1);
|
||||
FVector IntersectionPoint = V0 + T * (V1 - V0);
|
||||
IntersectionPoints.Add(IntersectionPoint);
|
||||
}
|
||||
|
||||
if (D0 * D2 <= 0.0f)
|
||||
{
|
||||
// Edge V0-V2 intersects with cut plane
|
||||
float T = D0 / (D0 - D2);
|
||||
FVector IntersectionPoint = V0 + T * (V2 - V0);
|
||||
IntersectionPoints.Add(IntersectionPoint);
|
||||
}
|
||||
|
||||
if (D1 * D2 <= 0.0f)
|
||||
{
|
||||
// Edge V1-V2 intersects with cut plane
|
||||
float T = D1 / (D1 - D2);
|
||||
FVector IntersectionPoint = V1 + T * (V2 - V1);
|
||||
IntersectionPoints.Add(IntersectionPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IntersectionPoints;
|
||||
}
|
||||
|
||||
// Create cap mesh
|
||||
void UBooleanCutTool::CreateCapMesh(const TArray<FVector>& IntersectionPoints, const FCutPlane& CutPlane, UProceduralMeshComponent* TargetMesh)
|
||||
{
|
||||
// Check if there are enough intersection points
|
||||
if (IntersectionPoints.Num() < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Create cap mesh using intersection points
|
||||
// Note: This is just a framework, actual implementation requires more complex algorithm
|
||||
|
||||
// 1. Project intersection points onto cut plane
|
||||
// 2. Triangulate projected points
|
||||
// 3. Create cap mesh
|
||||
// 4. Add to target mesh
|
||||
}
|
276
Source/FLESH/Private/DismembermentSystem.cpp
Normal file
276
Source/FLESH/Private/DismembermentSystem.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
#include "DismembermentSystem.h"
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "PhysicsEngine/PhysicsAsset.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UDismembermentSystem::UDismembermentSystem()
|
||||
{
|
||||
// Set this component to be initialized when the game starts, and to be ticked every frame
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
// Called when the game starts
|
||||
void UDismembermentSystem::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Get the skeletal mesh component from the owner
|
||||
AActor* Owner = GetOwner();
|
||||
if (Owner)
|
||||
{
|
||||
TargetMesh = Owner->FindComponentByClass<USkeletalMeshComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void UDismembermentSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
}
|
||||
|
||||
bool UDismembermentSystem::PerformCut(const FVector& CutLocation, const FVector& CutDirection, float CutWidth, float CutDepth)
|
||||
{
|
||||
// Ensure we have a valid target mesh
|
||||
if (!TargetMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation will use Geometry Script to perform boolean cutting operations
|
||||
// This is a placeholder for the actual implementation
|
||||
|
||||
// Convert world location to local space
|
||||
FVector LocalCutLocation = TargetMesh->GetComponentTransform().InverseTransformPosition(CutLocation);
|
||||
FVector LocalCutDirection = TargetMesh->GetComponentTransform().InverseTransformVector(CutDirection);
|
||||
|
||||
// TODO: Implement real-time boolean cutting using Geometry Script
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentSystem::DismemberAtBone(const FName& BoneName, const FVector& CutDirection, EDismembermentType DismembermentType)
|
||||
{
|
||||
// Ensure we have a valid target mesh
|
||||
if (!TargetMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the bone exists
|
||||
int32 BoneIndex = TargetMesh->GetBoneIndex(BoneName);
|
||||
if (BoneIndex == INDEX_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the bone data
|
||||
int32 RegisteredBoneIndex = FindBoneIndex(BoneName);
|
||||
if (RegisteredBoneIndex == INDEX_NONE)
|
||||
{
|
||||
// Bone not registered for dismemberment
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the bone is already dismembered
|
||||
if (RegisteredBones[RegisteredBoneIndex].bIsDismembered)
|
||||
{
|
||||
// Already dismembered
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the transform of the bone
|
||||
FTransform BoneTransform = TargetMesh->GetBoneTransform(BoneIndex);
|
||||
|
||||
// Create a procedural mesh for the dismembered part
|
||||
UProceduralMeshComponent* DismemberedPart = CreateProceduralMeshFromSkeletalMesh(TargetMesh, 0, 0);
|
||||
if (!DismemberedPart)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the dismembered part to the map
|
||||
DismemberedParts.Add(BoneName, DismemberedPart);
|
||||
|
||||
// Mark the bone as dismembered
|
||||
RegisteredBones[RegisteredBoneIndex].bIsDismembered = true;
|
||||
|
||||
// Spawn blood effect at the cut location
|
||||
SpawnBloodEffect(BoneTransform.GetLocation(), CutDirection);
|
||||
|
||||
// Hide the bone in the original skeletal mesh
|
||||
// This requires modifying the skeletal mesh's vertex weights or using a material to hide the bone
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentSystem::ApplyBoneDamage(const FName& BoneName, float Damage, const FVector& DamageLocation, const FVector& DamageDirection, EDismembermentType DismembermentType)
|
||||
{
|
||||
// Find the bone data
|
||||
int32 RegisteredBoneIndex = FindBoneIndex(BoneName);
|
||||
if (RegisteredBoneIndex == INDEX_NONE)
|
||||
{
|
||||
// Bone not registered for dismemberment
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the bone is already dismembered
|
||||
if (RegisteredBones[RegisteredBoneIndex].bIsDismembered)
|
||||
{
|
||||
// Already dismembered
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply damage to the bone
|
||||
RegisteredBones[RegisteredBoneIndex].Health -= Damage;
|
||||
|
||||
// Check if the bone should be dismembered
|
||||
if (RegisteredBones[RegisteredBoneIndex].Health <= RegisteredBones[RegisteredBoneIndex].DismembermentThreshold)
|
||||
{
|
||||
// Dismember the bone
|
||||
return DismemberAtBone(BoneName, DamageDirection, DismembermentType);
|
||||
}
|
||||
|
||||
// Spawn a smaller blood effect to indicate damage
|
||||
if (Damage > 0)
|
||||
{
|
||||
SpawnBloodEffect(DamageLocation, DamageDirection, FMath::Clamp(Damage / 100.0f, 0.1f, 1.0f));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDismembermentSystem::RegisterBone(const FDismembermentBoneData& BoneData)
|
||||
{
|
||||
// Check if the bone already exists
|
||||
for (int32 i = 0; i < RegisteredBones.Num(); ++i)
|
||||
{
|
||||
if (RegisteredBones[i].BoneName == BoneData.BoneName)
|
||||
{
|
||||
// Update existing bone data
|
||||
RegisteredBones[i] = BoneData;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new bone data
|
||||
RegisteredBones.Add(BoneData);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDismembermentSystem::SetBloodEffect(UNiagaraSystem* BloodEffect)
|
||||
{
|
||||
BloodEffectSystem = BloodEffect;
|
||||
}
|
||||
|
||||
void UDismembermentSystem::SetCutMaterial(UMaterialInterface* CutMaterial)
|
||||
{
|
||||
CutSurfaceMaterial = CutMaterial;
|
||||
}
|
||||
|
||||
bool UDismembermentSystem::GetBoneData(const FName& BoneName, FDismembermentBoneData& OutBoneData) const
|
||||
{
|
||||
int32 BoneIndex = FindBoneIndex(BoneName);
|
||||
if (BoneIndex != INDEX_NONE)
|
||||
{
|
||||
OutBoneData = RegisteredBones[BoneIndex];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FDismembermentBoneData> UDismembermentSystem::GetAllBones() const
|
||||
{
|
||||
return RegisteredBones;
|
||||
}
|
||||
|
||||
void UDismembermentSystem::ResetDismemberment()
|
||||
{
|
||||
// Reset all bones
|
||||
for (int32 i = 0; i < RegisteredBones.Num(); ++i)
|
||||
{
|
||||
RegisteredBones[i].bIsDismembered = false;
|
||||
RegisteredBones[i].Health = 100.0f;
|
||||
}
|
||||
|
||||
// Destroy all dismembered parts
|
||||
for (auto& Part : DismemberedParts)
|
||||
{
|
||||
if (Part.Value)
|
||||
{
|
||||
Part.Value->DestroyComponent();
|
||||
}
|
||||
}
|
||||
DismemberedParts.Empty();
|
||||
|
||||
// Show all bones in the original skeletal mesh
|
||||
// This requires restoring the skeletal mesh's vertex weights or material
|
||||
}
|
||||
|
||||
UProceduralMeshComponent* UDismembermentSystem::CreateProceduralMeshFromSkeletalMesh(USkeletalMeshComponent* SkelMesh, int32 LODIndex, int32 SectionIndex)
|
||||
{
|
||||
// Ensure we have a valid skeletal mesh
|
||||
if (!SkelMesh)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new procedural mesh component
|
||||
UProceduralMeshComponent* ProcMesh = NewObject<UProceduralMeshComponent>(GetOwner());
|
||||
ProcMesh->RegisterComponent();
|
||||
|
||||
// TODO: Extract mesh data from the skeletal mesh and create a procedural mesh
|
||||
// This will involve:
|
||||
// 1. Getting vertex positions, normals, UVs, etc. from the skeletal mesh
|
||||
// 2. Creating triangles for the procedural mesh
|
||||
// 3. Setting up materials
|
||||
// 4. Setting up collision
|
||||
|
||||
return ProcMesh;
|
||||
}
|
||||
|
||||
void UDismembermentSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity)
|
||||
{
|
||||
// Ensure we have a valid blood effect system
|
||||
if (!BloodEffectSystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a rotation from the direction
|
||||
FRotator Rotation = Direction.Rotation();
|
||||
|
||||
// Spawn the blood effect
|
||||
UNiagaraComponent* BloodEffect = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
|
||||
GetWorld(),
|
||||
BloodEffectSystem,
|
||||
Location,
|
||||
Rotation,
|
||||
FVector(Intensity),
|
||||
true,
|
||||
true,
|
||||
ENCPoolMethod::AutoRelease
|
||||
);
|
||||
|
||||
if (BloodEffect)
|
||||
{
|
||||
// Set the intensity parameter if it exists
|
||||
BloodEffect->SetFloatParameter(FName("Intensity"), Intensity);
|
||||
}
|
||||
}
|
||||
|
||||
int32 UDismembermentSystem::FindBoneIndex(const FName& BoneName) const
|
||||
{
|
||||
for (int32 i = 0; i < RegisteredBones.Num(); ++i)
|
||||
{
|
||||
if (RegisteredBones[i].BoneName == BoneName)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
20
Source/FLESH/Private/FLESHModule.cpp
Normal file
20
Source/FLESH/Private/FLESHModule.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "FLESHModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FFLESHModule"
|
||||
|
||||
void FFLESHModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory
|
||||
// The exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FFLESHModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module
|
||||
// For modules that support dynamic reloading, we call this function before unloading the module
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FFLESHModule, FLESH)
|
159
Source/FLESH/Public/AnatomicalLayerSystem.h
Normal file
159
Source/FLESH/Public/AnatomicalLayerSystem.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "AnatomicalLayerSystem.generated.h"
|
||||
|
||||
/**
|
||||
* Anatomical layer type enumeration
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EAnatomicalLayerType : uint8
|
||||
{
|
||||
Skin,
|
||||
Muscle,
|
||||
Bone,
|
||||
Organ,
|
||||
Blood,
|
||||
Custom
|
||||
};
|
||||
|
||||
/**
|
||||
* Anatomical layer data structure
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FAnatomicalLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Layer name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
FName LayerName;
|
||||
|
||||
// Layer type
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
EAnatomicalLayerType LayerType;
|
||||
|
||||
// Layer mesh
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
TObjectPtr<UStaticMesh> LayerMesh;
|
||||
|
||||
// Layer material
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
TObjectPtr<UMaterialInterface> LayerMaterial;
|
||||
|
||||
// Layer thickness
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
float Thickness = 1.0f;
|
||||
|
||||
// Layer density
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
float Density = 1.0f;
|
||||
|
||||
// Layer elasticity
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
float Elasticity = 0.5f;
|
||||
|
||||
// Layer fracture threshold
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
float FractureThreshold = 100.0f;
|
||||
|
||||
// Enable physics
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
bool bEnablePhysics = true;
|
||||
|
||||
// Is visible
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Anatomy")
|
||||
bool bVisible = true;
|
||||
|
||||
// Constructor
|
||||
FAnatomicalLayer()
|
||||
: LayerName(NAME_None)
|
||||
, LayerType(EAnatomicalLayerType::Skin)
|
||||
, LayerMesh(nullptr)
|
||||
, LayerMaterial(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Anatomical layer system class
|
||||
* Manages multi-layer system for bones, organs and physics properties
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class FLESH_API UAnatomicalLayerSystem : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UAnatomicalLayerSystem();
|
||||
|
||||
/**
|
||||
* Add a new anatomical layer
|
||||
* @param LayerName - Layer name
|
||||
* @param LayerType - Layer type
|
||||
* @return Index of the newly added layer
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
int32 AddLayer(const FName& LayerName, EAnatomicalLayerType LayerType);
|
||||
|
||||
/**
|
||||
* Remove an anatomical layer by index
|
||||
* @param LayerIndex - Layer index
|
||||
* @return Whether the removal was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
bool RemoveLayer(int32 LayerIndex);
|
||||
|
||||
/**
|
||||
* Get an anatomical layer by index
|
||||
* @param LayerIndex - Layer index
|
||||
* @return Anatomical layer data
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
FAnatomicalLayer GetLayer(int32 LayerIndex) const;
|
||||
|
||||
/**
|
||||
* Get all anatomical layers
|
||||
* @return Array of anatomical layers
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
TArray<FAnatomicalLayer> GetAllLayers() const;
|
||||
|
||||
/**
|
||||
* Set an anatomical layer by index
|
||||
* @param LayerIndex - Layer index
|
||||
* @param Layer - New layer data
|
||||
* @return Whether the setting was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
bool SetLayer(int32 LayerIndex, const FAnatomicalLayer& Layer);
|
||||
|
||||
/**
|
||||
* Get the number of layers
|
||||
* @return Number of layers
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
int32 GetLayerCount() const;
|
||||
|
||||
/**
|
||||
* Get layers by type
|
||||
* @param LayerType - Layer type
|
||||
* @return Array of layers matching the type
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
TArray<FAnatomicalLayer> GetLayersByType(EAnatomicalLayerType LayerType) const;
|
||||
|
||||
/**
|
||||
* Set physics for all layers
|
||||
* @param bEnable - Whether to enable physics
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Anatomy")
|
||||
void SetPhysicsForAllLayers(bool bEnable);
|
||||
|
||||
private:
|
||||
// Array of anatomical layers
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Anatomy")
|
||||
TArray<FAnatomicalLayer> Layers;
|
||||
};
|
92
Source/FLESH/Public/BloodPool.h
Normal file
92
Source/FLESH/Public/BloodPool.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "BloodPool.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class UBoxComponent;
|
||||
class UDecalComponent;
|
||||
class UMaterialInterface;
|
||||
|
||||
/**
|
||||
* Blood pool actor class, used to create blood pools on surfaces
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESH_API ABloodPool : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
ABloodPool();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set the blood pool size
|
||||
* @param NewSize - New size for the blood pool
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
void SetPoolSize(float NewSize);
|
||||
|
||||
/**
|
||||
* Set the blood pool color
|
||||
* @param NewColor - New color for the blood pool
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
void SetPoolColor(const FLinearColor& NewColor);
|
||||
|
||||
/**
|
||||
* Start blood pool expansion
|
||||
* @param ExpansionRate - Rate of expansion
|
||||
* @param MaxSize - Maximum size
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
void StartExpansion(float ExpansionRate = 1.0f, float MaxSize = 3.0f);
|
||||
|
||||
private:
|
||||
// Collision component for surface detection
|
||||
UPROPERTY(VisibleAnywhere, Category = "Components")
|
||||
TObjectPtr<UBoxComponent> Collision;
|
||||
|
||||
// Decal component for rendering the blood pool
|
||||
UPROPERTY(VisibleAnywhere, Category = "Components")
|
||||
TObjectPtr<UDecalComponent> Decal;
|
||||
|
||||
// Blood pool decal material
|
||||
UPROPERTY(EditAnywhere, Category = "Appearance")
|
||||
TObjectPtr<UMaterialInterface> DecalMaterial;
|
||||
|
||||
// Current blood pool size
|
||||
UPROPERTY(EditAnywhere, Category = "Appearance")
|
||||
float PoolSize = 1.0f;
|
||||
|
||||
// Blood pool color
|
||||
UPROPERTY(EditAnywhere, Category = "Appearance")
|
||||
FLinearColor PoolColor = FLinearColor::Red;
|
||||
|
||||
// Expansion rate
|
||||
UPROPERTY(EditAnywhere, Category = "Expansion")
|
||||
float ExpansionRate = 0.5f;
|
||||
|
||||
// Maximum blood pool size
|
||||
UPROPERTY(EditAnywhere, Category = "Expansion")
|
||||
float MaxPoolSize = 2.0f;
|
||||
|
||||
// Whether the pool is expanding
|
||||
UPROPERTY()
|
||||
bool bIsExpanding = false;
|
||||
|
||||
// Blood pool expansion timer handle
|
||||
FTimerHandle ExpansionTimerHandle;
|
||||
|
||||
// Internal function to update the blood pool size
|
||||
void UpdatePoolSize();
|
||||
|
||||
// Internal function to expand the blood pool
|
||||
void ExpandPool();
|
||||
};
|
98
Source/FLESH/Public/BloodSystem.h
Normal file
98
Source/FLESH/Public/BloodSystem.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "BloodSystem.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class UNiagaraComponent;
|
||||
class UDecalComponent;
|
||||
|
||||
/**
|
||||
* Blood system component for the FLESH plugin
|
||||
* Handles blood effects, pools, and decals
|
||||
*/
|
||||
UCLASS(ClassGroup=(FLESH), meta=(BlueprintSpawnableComponent))
|
||||
class FLESH_API UBloodSystem : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UBloodSystem();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/**
|
||||
* Spawn blood effect at the specified location
|
||||
* @param Location - World location to spawn the blood effect
|
||||
* @param Direction - Direction of the blood spray
|
||||
* @param Intensity - Intensity of the blood effect (0.0-1.0)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
void SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity = 1.0f);
|
||||
|
||||
/**
|
||||
* Create a blood pool at the specified location
|
||||
* @param Location - World location to create the blood pool
|
||||
* @param Size - Size of the blood pool
|
||||
* @return The created blood pool actor
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
AActor* CreateBloodPool(const FVector& Location, float Size = 1.0f);
|
||||
|
||||
/**
|
||||
* Apply a blood decal to the specified component
|
||||
* @param Component - Component to apply the blood decal to
|
||||
* @param Location - World location of the decal
|
||||
* @param Direction - Direction the decal should face
|
||||
* @param Size - Size of the decal
|
||||
* @return The created decal component
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
|
||||
UDecalComponent* ApplyBloodDecal(UPrimitiveComponent* Component, const FVector& Location, const FVector& Direction, float Size = 1.0f);
|
||||
|
||||
private:
|
||||
// Niagara system for blood spray effects
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UNiagaraSystem> BloodSpraySystem;
|
||||
|
||||
// Material for blood decals
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UMaterialInterface> BloodDecalMaterial;
|
||||
|
||||
// Blood pool class to spawn
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood", meta = (AllowPrivateAccess = "true"))
|
||||
TSubclassOf<AActor> BloodPoolClass;
|
||||
|
||||
// Active blood effects
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UNiagaraComponent>> ActiveBloodEffects;
|
||||
|
||||
// Active blood decals
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UDecalComponent>> ActiveBloodDecals;
|
||||
|
||||
// Active blood pools
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<AActor>> ActiveBloodPools;
|
||||
|
||||
// Maximum number of active blood effects
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood|Limits", meta = (AllowPrivateAccess = "true"))
|
||||
int32 MaxBloodEffects = 10;
|
||||
|
||||
// Maximum number of active blood decals
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood|Limits", meta = (AllowPrivateAccess = "true"))
|
||||
int32 MaxBloodDecals = 30;
|
||||
|
||||
// Maximum number of active blood pools
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Blood|Limits", meta = (AllowPrivateAccess = "true"))
|
||||
int32 MaxBloodPools = 5;
|
||||
};
|
129
Source/FLESH/Public/BooleanCutTool.h
Normal file
129
Source/FLESH/Public/BooleanCutTool.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "BooleanCutTool.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class UStaticMesh;
|
||||
class USkeletalMesh;
|
||||
class UProceduralMeshComponent;
|
||||
|
||||
/**
|
||||
* Cut plane definition
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCutPlane
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Cut plane location
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Cutting")
|
||||
FVector Location;
|
||||
|
||||
// Cut plane normal
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Cutting")
|
||||
FVector Normal;
|
||||
|
||||
// Cut plane width
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Cutting")
|
||||
float Width = 100.0f;
|
||||
|
||||
// Cut plane height
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Cutting")
|
||||
float Height = 100.0f;
|
||||
|
||||
// Constructor
|
||||
FCutPlane()
|
||||
: Location(FVector::ZeroVector)
|
||||
, Normal(FVector::UpVector)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor with parameters
|
||||
FCutPlane(const FVector& InLocation, const FVector& InNormal, float InWidth = 100.0f, float InHeight = 100.0f)
|
||||
: Location(InLocation)
|
||||
, Normal(InNormal)
|
||||
, Width(InWidth)
|
||||
, Height(InHeight)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Boolean cut tool class
|
||||
* Provides real-time boolean cutting functionality
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class FLESH_API UBooleanCutTool : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UBooleanCutTool();
|
||||
|
||||
/**
|
||||
* Perform boolean cut on static mesh
|
||||
* @param TargetMesh - Target static mesh
|
||||
* @param CutPlane - Cut plane
|
||||
* @param bCreateCap - Whether to create a cap
|
||||
* @return Array of cut meshes [0] is positive side, [1] is negative side
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
TArray<UStaticMesh*> CutStaticMesh(UStaticMesh* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap = true);
|
||||
|
||||
/**
|
||||
* Perform boolean cut on skeletal mesh
|
||||
* @param TargetMesh - Target skeletal mesh
|
||||
* @param CutPlane - Cut plane
|
||||
* @param BoneName - Bone name, if specified only cuts the part influenced by this bone
|
||||
* @param bCreateCap - Whether to create a cap
|
||||
* @return Array of cut skeletal meshes [0] is positive side, [1] is negative side
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
TArray<USkeletalMesh*> CutSkeletalMesh(USkeletalMesh* TargetMesh, const FCutPlane& CutPlane, FName BoneName = NAME_None, bool bCreateCap = true);
|
||||
|
||||
/**
|
||||
* Perform boolean cut on procedural mesh
|
||||
* @param TargetMesh - Target procedural mesh
|
||||
* @param CutPlane - Cut plane
|
||||
* @param bCreateCap - Whether to create a cap
|
||||
* @return Array of cut procedural meshes [0] is positive side, [1] is negative side
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
TArray<UProceduralMeshComponent*> CutProceduralMesh(UProceduralMeshComponent* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap = true);
|
||||
|
||||
/**
|
||||
* Create cut plane mesh
|
||||
* @param CutPlane - Cut plane
|
||||
* @return Cut plane mesh
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
UStaticMesh* CreateCutPlaneMesh(const FCutPlane& CutPlane);
|
||||
|
||||
/**
|
||||
* Set cut material
|
||||
* @param Material - Cut material
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
void SetCutMaterial(UMaterialInterface* Material);
|
||||
|
||||
/**
|
||||
* Get cut material
|
||||
* @return Current cut material
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Cutting")
|
||||
UMaterialInterface* GetCutMaterial() const;
|
||||
|
||||
private:
|
||||
// Cut material
|
||||
UPROPERTY()
|
||||
TObjectPtr<UMaterialInterface> CutMaterial;
|
||||
|
||||
// Internal function to calculate intersection points between cut plane and mesh
|
||||
TArray<FVector> CalculateIntersectionPoints(const TArray<FVector>& Vertices, const TArray<int32>& Indices, const FCutPlane& CutPlane);
|
||||
|
||||
// Internal function to create cap mesh
|
||||
void CreateCapMesh(const TArray<FVector>& IntersectionPoints, const FCutPlane& CutPlane, UProceduralMeshComponent* TargetMesh);
|
||||
};
|
87
Source/FLESH/Public/DismemberedAnimInstance.h
Normal file
87
Source/FLESH/Public/DismemberedAnimInstance.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "DismemberedAnimInstance.generated.h"
|
||||
|
||||
/**
|
||||
* Animation instance class for dismembered body parts
|
||||
* Handles animation of separated limbs and body parts
|
||||
*/
|
||||
UCLASS()
|
||||
class FLESH_API UDismemberedAnimInstance : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
UDismemberedAnimInstance();
|
||||
|
||||
// Called when animation updates
|
||||
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
// Called to initialize animation
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
|
||||
/**
|
||||
* Set the source bone name this dismembered part was cut from
|
||||
* @param BoneName - Name of the source bone
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Animation")
|
||||
void SetSourceBone(const FName& BoneName);
|
||||
|
||||
/**
|
||||
* Set the cut type for this dismembered part
|
||||
* @param CutType - Type of cut that created this part
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Animation")
|
||||
void SetCutType(int32 CutType);
|
||||
|
||||
/**
|
||||
* Apply an impulse to the dismembered part
|
||||
* @param Impulse - Impulse vector to apply
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Animation")
|
||||
void ApplyImpulse(const FVector& Impulse);
|
||||
|
||||
private:
|
||||
// Name of the bone this part was cut from
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName SourceBoneName;
|
||||
|
||||
// Type of cut that created this part
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
int32 CutType;
|
||||
|
||||
// Root bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName RootBoneName;
|
||||
|
||||
// Head bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName HeadBoneName;
|
||||
|
||||
// Left arm bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName LeftArmBoneName;
|
||||
|
||||
// Right arm bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName RightArmBoneName;
|
||||
|
||||
// Left leg bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName LeftLegBoneName;
|
||||
|
||||
// Right leg bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FName RightLegBoneName;
|
||||
|
||||
// Current angular velocity
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FVector AngularVelocity;
|
||||
|
||||
// Current linear velocity
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FLESH|Animation", meta = (AllowPrivateAccess = "true"))
|
||||
FVector LinearVelocity;
|
||||
};
|
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "DismembermentGraphAsset.generated.h"
|
||||
|
||||
/**
|
||||
* Asset that contains a dismemberment graph
|
||||
* Used for visual programming of dismemberment logic
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class FLESH_API UDismembermentGraphAsset : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UDismembermentGraphAsset();
|
||||
|
||||
// The graph owned by this asset
|
||||
UPROPERTY()
|
||||
class UDismembermentGraph* Graph;
|
||||
|
||||
// Compile the graph into executable logic
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool CompileGraph();
|
||||
|
||||
// Execute the compiled graph
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool ExecuteGraph(class AActor* TargetActor);
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Called when the asset is created
|
||||
virtual void PostInitProperties() override;
|
||||
|
||||
// Called when the asset is duplicated
|
||||
virtual void PostDuplicate(bool bDuplicateForPIE) override;
|
||||
|
||||
// Called when the asset is loaded
|
||||
virtual void PostLoad() override;
|
||||
#endif
|
||||
};
|
204
Source/FLESH/Public/DismembermentSystem.h
Normal file
204
Source/FLESH/Public/DismembermentSystem.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "DismembermentSystem.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class USkeletalMeshComponent;
|
||||
class UProceduralMeshComponent;
|
||||
class UNiagaraSystem;
|
||||
class UMaterialInterface;
|
||||
|
||||
/**
|
||||
* Bone dismemberment type enumeration
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EDismembermentType : uint8
|
||||
{
|
||||
None,
|
||||
Cut, // Clean cut
|
||||
Tear, // Torn flesh
|
||||
Crush, // Crushed bone
|
||||
Blast, // Explosion damage
|
||||
Burn, // Burn damage
|
||||
Custom // Custom dismemberment type
|
||||
};
|
||||
|
||||
/**
|
||||
* Bone data structure for dismemberment
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDismembermentBoneData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
FName BoneName;
|
||||
|
||||
// Parent bone name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
FName ParentBoneName;
|
||||
|
||||
// Bone health
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
float Health = 100.0f;
|
||||
|
||||
// Damage threshold before dismemberment
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
float DismembermentThreshold = 50.0f;
|
||||
|
||||
// Blood effect socket name
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
FName BloodSocketName;
|
||||
|
||||
// Whether this bone can be dismembered
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
bool bCanBeDismembered = true;
|
||||
|
||||
// Whether this bone is critical (death if dismembered)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
bool bIsCritical = false;
|
||||
|
||||
// Whether this bone is already dismembered
|
||||
UPROPERTY(BlueprintReadOnly, Category = "FLESH|Dismemberment")
|
||||
bool bIsDismembered = false;
|
||||
|
||||
// Constructor
|
||||
FDismembermentBoneData()
|
||||
: BoneName(NAME_None)
|
||||
, ParentBoneName(NAME_None)
|
||||
, BloodSocketName(NAME_None)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main component for the FLESH dismemberment system
|
||||
* Handles real-time boolean cutting, multi-layer cutting, and physics interactions
|
||||
*/
|
||||
UCLASS(ClassGroup=(FLESH), meta=(BlueprintSpawnableComponent))
|
||||
class FLESH_API UDismembermentSystem : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UDismembermentSystem();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/**
|
||||
* Perform a cut on the owner's mesh at the specified location and direction
|
||||
* @param CutLocation - World location of the cut
|
||||
* @param CutDirection - Direction of the cut
|
||||
* @param CutWidth - Width of the cut
|
||||
* @param CutDepth - Depth of the cut
|
||||
* @return True if the cut was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool PerformCut(const FVector& CutLocation, const FVector& CutDirection, float CutWidth = 1.0f, float CutDepth = 10.0f);
|
||||
|
||||
/**
|
||||
* Perform a dismemberment at the specified bone
|
||||
* @param BoneName - Name of the bone to dismember
|
||||
* @param CutDirection - Direction of the cut
|
||||
* @param DismembermentType - Type of dismemberment to perform
|
||||
* @return True if the dismemberment was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool DismemberAtBone(const FName& BoneName, const FVector& CutDirection, EDismembermentType DismembermentType = EDismembermentType::Cut);
|
||||
|
||||
/**
|
||||
* Apply damage to a specific bone
|
||||
* @param BoneName - Name of the bone to damage
|
||||
* @param Damage - Amount of damage to apply
|
||||
* @param DamageLocation - World location of the damage
|
||||
* @param DamageDirection - Direction of the damage
|
||||
* @param DismembermentType - Type of damage for potential dismemberment
|
||||
* @return True if damage was applied
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool ApplyBoneDamage(const FName& BoneName, float Damage, const FVector& DamageLocation, const FVector& DamageDirection, EDismembermentType DismembermentType = EDismembermentType::Cut);
|
||||
|
||||
/**
|
||||
* Register a bone for dismemberment
|
||||
* @param BoneData - Bone data to register
|
||||
* @return True if the bone was registered
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool RegisterBone(const FDismembermentBoneData& BoneData);
|
||||
|
||||
/**
|
||||
* Set the blood effect for dismemberment
|
||||
* @param BloodEffect - Niagara system for blood effects
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetBloodEffect(UNiagaraSystem* BloodEffect);
|
||||
|
||||
/**
|
||||
* Set the cut material for dismemberment
|
||||
* @param CutMaterial - Material to use for cut surfaces
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetCutMaterial(UMaterialInterface* CutMaterial);
|
||||
|
||||
/**
|
||||
* Get the bone data for a specific bone
|
||||
* @param BoneName - Name of the bone
|
||||
* @param OutBoneData - Output bone data
|
||||
* @return True if the bone data was found
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool GetBoneData(const FName& BoneName, FDismembermentBoneData& OutBoneData) const;
|
||||
|
||||
/**
|
||||
* Get all registered bones
|
||||
* @return Array of all registered bone data
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
TArray<FDismembermentBoneData> GetAllBones() const;
|
||||
|
||||
/**
|
||||
* Reset all dismemberment (restore all bones)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void ResetDismemberment();
|
||||
|
||||
private:
|
||||
// The skeletal mesh to perform dismemberment on
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> TargetMesh;
|
||||
|
||||
// Map of bone names to their corresponding procedural mesh components
|
||||
UPROPERTY()
|
||||
TMap<FName, TObjectPtr<UProceduralMeshComponent>> DismemberedParts;
|
||||
|
||||
// Registered bones for dismemberment
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment")
|
||||
TArray<FDismembermentBoneData> RegisteredBones;
|
||||
|
||||
// Niagara system for blood effects
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment")
|
||||
TObjectPtr<UNiagaraSystem> BloodEffectSystem;
|
||||
|
||||
// Material for cut surfaces
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment")
|
||||
TObjectPtr<UMaterialInterface> CutSurfaceMaterial;
|
||||
|
||||
// Internal function to create a procedural mesh from a skeletal mesh section
|
||||
UProceduralMeshComponent* CreateProceduralMeshFromSkeletalMesh(USkeletalMeshComponent* SkelMesh, int32 LODIndex, int32 SectionIndex);
|
||||
|
||||
// Internal function to spawn blood effects
|
||||
void SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity = 1.0f);
|
||||
|
||||
// Internal function to find bone index by name
|
||||
int32 FindBoneIndex(const FName& BoneName) const;
|
||||
};
|
245
Source/FLESH/Public/FLESHDismembermentComponent.h
Normal file
245
Source/FLESH/Public/FLESHDismembermentComponent.h
Normal file
@@ -0,0 +1,245 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "DismembermentSystem.h"
|
||||
#include "FLESHDismembermentComponent.generated.h"
|
||||
|
||||
// Forward declarations
|
||||
class USkeletalMeshComponent;
|
||||
class UNiagaraSystem;
|
||||
class UMaterialInterface;
|
||||
|
||||
/**
|
||||
* Bone patch type for customizing dismemberment effects
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EBonePatchType : uint8
|
||||
{
|
||||
Default, // Default patch
|
||||
Head, // Head specific patch
|
||||
Arm, // Arm specific patch
|
||||
Leg, // Leg specific patch
|
||||
Torso, // Torso specific patch
|
||||
Custom // Custom patch
|
||||
};
|
||||
|
||||
/**
|
||||
* Bone patch data structure for customizing dismemberment effects
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FBonePatchData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Patch type
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
EBonePatchType PatchType = EBonePatchType::Default;
|
||||
|
||||
// Bone names affected by this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
TArray<FName> BoneNames;
|
||||
|
||||
// Custom cut material for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
TObjectPtr<UMaterialInterface> CustomCutMaterial = nullptr;
|
||||
|
||||
// Custom blood effect for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
TObjectPtr<UNiagaraSystem> CustomBloodEffect = nullptr;
|
||||
|
||||
// Damage multiplier for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment", meta = (ClampMin = "0.0", ClampMax = "10.0"))
|
||||
float DamageMultiplier = 1.0f;
|
||||
|
||||
// Whether this patch has organs
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
bool bHasOrgans = false;
|
||||
|
||||
// Organ mesh for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment", meta = (EditCondition = "bHasOrgans"))
|
||||
TObjectPtr<UStaticMesh> OrganMesh = nullptr;
|
||||
|
||||
// Organ material for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment", meta = (EditCondition = "bHasOrgans"))
|
||||
TObjectPtr<UMaterialInterface> OrganMaterial = nullptr;
|
||||
|
||||
// Whether this patch has custom physics
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment")
|
||||
bool bHasCustomPhysics = false;
|
||||
|
||||
// Physics asset for this patch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FLESH|Dismemberment", meta = (EditCondition = "bHasCustomPhysics"))
|
||||
TObjectPtr<UPhysicsAsset> CustomPhysicsAsset = nullptr;
|
||||
|
||||
// Constructor
|
||||
FBonePatchData()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main component for the FLESH dismemberment system with blueprint support
|
||||
* Provides easy-to-use blueprint interface for the dismemberment system
|
||||
*/
|
||||
UCLASS(ClassGroup=(FLESH), meta=(BlueprintSpawnableComponent))
|
||||
class FLESH_API UFLESHDismembermentComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this component's properties
|
||||
UFLESHDismembermentComponent();
|
||||
|
||||
protected:
|
||||
// Called when the game starts
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/**
|
||||
* Perform a cut at the specified location and direction
|
||||
* @param CutLocation - World location of the cut
|
||||
* @param CutDirection - Direction of the cut
|
||||
* @param CutWidth - Width of the cut
|
||||
* @param CutDepth - Depth of the cut
|
||||
* @return True if the cut was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool PerformCut(const FVector& CutLocation, const FVector& CutDirection, float CutWidth = 1.0f, float CutDepth = 10.0f);
|
||||
|
||||
/**
|
||||
* Perform a cut using a transformation matrix
|
||||
* @param CutTransform - Transformation matrix for the cut
|
||||
* @param CutWidth - Width of the cut
|
||||
* @param CutDepth - Depth of the cut
|
||||
* @return True if the cut was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool PerformCutWithMatrix(const FMatrix& CutTransform, float CutWidth = 1.0f, float CutDepth = 10.0f);
|
||||
|
||||
/**
|
||||
* Dismember a specific bone
|
||||
* @param BoneName - Name of the bone to dismember
|
||||
* @param DismembermentType - Type of dismemberment to perform
|
||||
* @return True if the dismemberment was successful
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool DismemberBone(const FName& BoneName, EDismembermentType DismembermentType = EDismembermentType::Cut);
|
||||
|
||||
/**
|
||||
* Apply damage to a specific bone
|
||||
* @param BoneName - Name of the bone to damage
|
||||
* @param Damage - Amount of damage to apply
|
||||
* @param DamageLocation - World location of the damage
|
||||
* @param DamageDirection - Direction of the damage
|
||||
* @param DismembermentType - Type of damage for potential dismemberment
|
||||
* @return True if damage was applied
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool ApplyBoneDamage(const FName& BoneName, float Damage, const FVector& DamageLocation, const FVector& DamageDirection, EDismembermentType DismembermentType = EDismembermentType::Cut);
|
||||
|
||||
/**
|
||||
* Add a bone patch
|
||||
* @param PatchData - Patch data to add
|
||||
* @return True if the patch was added
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool AddBonePatch(const FBonePatchData& PatchData);
|
||||
|
||||
/**
|
||||
* Remove a bone patch
|
||||
* @param PatchType - Type of patch to remove
|
||||
* @return True if the patch was removed
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool RemoveBonePatch(EBonePatchType PatchType);
|
||||
|
||||
/**
|
||||
* Get a bone patch by type
|
||||
* @param PatchType - Type of patch to get
|
||||
* @param OutPatchData - Output patch data
|
||||
* @return True if the patch was found
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
bool GetBonePatch(EBonePatchType PatchType, FBonePatchData& OutPatchData) const;
|
||||
|
||||
/**
|
||||
* Get all bone patches
|
||||
* @return Array of all bone patches
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
TArray<FBonePatchData> GetAllBonePatches() const;
|
||||
|
||||
/**
|
||||
* Reset all dismemberment (restore all bones)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void ResetDismemberment();
|
||||
|
||||
/**
|
||||
* Set the blood effect for dismemberment
|
||||
* @param BloodEffect - Niagara system for blood effects
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetBloodEffect(UNiagaraSystem* BloodEffect);
|
||||
|
||||
/**
|
||||
* Set the cut material for dismemberment
|
||||
* @param CutMaterial - Material to use for cut surfaces
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetCutMaterial(UMaterialInterface* CutMaterial);
|
||||
|
||||
/**
|
||||
* Set whether to show organs
|
||||
* @param bShow - Whether to show organs
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetShowOrgans(bool bShow);
|
||||
|
||||
/**
|
||||
* Set whether to enable physics
|
||||
* @param bEnable - Whether to enable physics
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")
|
||||
void SetEnablePhysics(bool bEnable);
|
||||
|
||||
private:
|
||||
// The dismemberment system
|
||||
UPROPERTY()
|
||||
TObjectPtr<UDismembermentSystem> DismembermentSystem;
|
||||
|
||||
// The target skeletal mesh component
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> TargetMesh;
|
||||
|
||||
// Default blood effect
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UNiagaraSystem> DefaultBloodEffect;
|
||||
|
||||
// Default cut material
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UMaterialInterface> DefaultCutMaterial;
|
||||
|
||||
// Whether to show organs
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment", meta = (AllowPrivateAccess = "true"))
|
||||
bool bShowOrgans = true;
|
||||
|
||||
// Whether to enable physics
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment", meta = (AllowPrivateAccess = "true"))
|
||||
bool bEnablePhysics = true;
|
||||
|
||||
// Bone patches
|
||||
UPROPERTY(EditAnywhere, Category = "FLESH|Dismemberment", meta = (AllowPrivateAccess = "true"))
|
||||
TArray<FBonePatchData> BonePatches;
|
||||
|
||||
// Internal function to find a bone patch by type
|
||||
int32 FindBonePatchIndex(EBonePatchType PatchType) const;
|
||||
|
||||
// Internal function to apply a bone patch
|
||||
void ApplyBonePatch(const FBonePatchData& PatchData);
|
||||
};
|
21
Source/FLESH/Public/FLESHModule.h
Normal file
21
Source/FLESH/Public/FLESHModule.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
/**
|
||||
* FLESH Module - Fully Locational Evisceration System for Humanoids
|
||||
*/
|
||||
class FLESH_API FFLESHModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** Singleton getter */
|
||||
static FFLESHModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FFLESHModule>("FLESH");
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user