更新 Source/FLESH/Private/BloodPool.cpp
This commit is contained in:
@@ -1,166 +1,258 @@
|
||||
#include "BloodSystem.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "Components/DecalComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Engine/World.h"
|
||||
#include "BooleanCutTool.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
// Temporarily commented out GeometryScripting related headers
|
||||
// Will restore them after we resolve the basic module compilation issues
|
||||
//#include "GeometryScriptingCore.h"
|
||||
//#include "GeometryScript/MeshBooleanFunctions.h"
|
||||
//#include "GeometryScript/MeshPrimitiveFunctions.h"
|
||||
//#include "DynamicMesh/DynamicMesh3.h"
|
||||
//#include "GeometryScript/MeshTransformFunctions.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UBloodSystem::UBloodSystem()
|
||||
// Constructor
|
||||
UBooleanCutTool::UBooleanCutTool()
|
||||
{
|
||||
// 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()
|
||||
// Perform boolean cut on static mesh
|
||||
TArray<UStaticMesh*> UBooleanCutTool::CutStaticMesh(UStaticMesh* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap)
|
||||
{
|
||||
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)
|
||||
TArray<UStaticMesh*> Result;
|
||||
|
||||
// Check if target mesh is valid
|
||||
if (!TargetMesh)
|
||||
{
|
||||
if (!ActiveBloodEffects[i] || !ActiveBloodEffects[i]->IsActive())
|
||||
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;
|
||||
|
||||
// In UE5.5.4, GetSectionMeshData method has been removed, replaced with GetProcMeshSection
|
||||
FProcMeshSection* MeshSection = TargetMesh->GetProcMeshSection(0);
|
||||
if (MeshSection)
|
||||
{
|
||||
// Extract data from MeshSection
|
||||
for (const FProcMeshVertex& Vertex : MeshSection->ProcVertexBuffer)
|
||||
{
|
||||
ActiveBloodEffects.RemoveAt(i);
|
||||
Vertices.Add(Vertex.Position);
|
||||
Normals.Add(Vertex.Normal);
|
||||
UVs.Add(Vertex.UV0);
|
||||
VertexColors.Add(Vertex.Color);
|
||||
Tangents.Add(Vertex.Tangent);
|
||||
}
|
||||
|
||||
// Convert indices
|
||||
for (uint32 Index : MeshSection->ProcIndexBuffer)
|
||||
{
|
||||
Triangles.Add(static_cast<int32>(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void UBloodSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity)
|
||||
// Create cut plane mesh
|
||||
UStaticMesh* UBooleanCutTool::CreateCutPlaneMesh(const FCutPlane& CutPlane)
|
||||
{
|
||||
// Ensure we have a valid blood spray system
|
||||
if (!BloodSpraySystem)
|
||||
// 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)
|
||||
{
|
||||
// Simplify the implementation of this method to resolve compilation issues
|
||||
TArray<FVector> IntersectionPoints;
|
||||
|
||||
// TODO: Restore the complete implementation after resolving GeometryScripting issues
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
}
|
||||
|
Reference in New Issue
Block a user