From 80fdce96b22ffb444752ec0fd3a3103fc8d454da Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 18 Apr 2025 18:14:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20Source/FLESH/Private/Blood?= =?UTF-8?q?Pool.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/FLESH/Private/BloodPool.cpp | 392 ++++++++++++++++++----------- 1 file changed, 242 insertions(+), 150 deletions(-) diff --git a/Source/FLESH/Private/BloodPool.cpp b/Source/FLESH/Private/BloodPool.cpp index cbb9ff4..4210b02 100644 --- a/Source/FLESH/Private/BloodPool.cpp +++ b/Source/FLESH/Private/BloodPool.cpp @@ -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 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 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(); + // TODO: Use GeometryScript to perform boolean difference operation for positive part + + // Create negative part mesh + UStaticMesh* NegativeMesh = NewObject(); + // 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 UBooleanCutTool::CutSkeletalMesh(USkeletalMesh* TargetMesh, const FCutPlane& CutPlane, FName BoneName, bool bCreateCap) +{ + TArray 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(); + // TODO: Use GeometryScript to perform boolean difference operation for positive part + + // Create negative part skeletal mesh + USkeletalMesh* NegativeMesh = NewObject(); + // 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 UBooleanCutTool::CutProceduralMesh(UProceduralMeshComponent* TargetMesh, const FCutPlane& CutPlane, bool bCreateCap) +{ + TArray Result; + + // Check if target mesh is valid + if (!TargetMesh) + { + return Result; + } + + // Get mesh data + TArray Vertices; + TArray Triangles; + TArray Normals; + TArray UVs; + TArray VertexColors; + TArray 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(Index)); } } + + // Calculate intersection points between cut plane and mesh + TArray IntersectionPoints = CalculateIntersectionPoints(Vertices, Triangles, CutPlane); + + // Create positive part procedural mesh + UProceduralMeshComponent* PositiveMesh = NewObject(TargetMesh->GetOwner()); + PositiveMesh->RegisterComponent(); + + // Create negative part procedural mesh + UProceduralMeshComponent* NegativeMesh = NewObject(TargetMesh->GetOwner()); + NegativeMesh->RegisterComponent(); + + // Split mesh + TArray PositiveVertices; + TArray PositiveTriangles; + TArray PositiveNormals; + TArray PositiveUVs; + TArray PositiveVertexColors; + TArray PositiveTangents; + + TArray NegativeVertices; + TArray NegativeTriangles; + TArray NegativeNormals; + TArray NegativeUVs; + TArray NegativeVertexColors; + TArray 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(); + + // 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 UBooleanCutTool::CalculateIntersectionPoints(const TArray& Vertices, const TArray& Indices, const FCutPlane& CutPlane) +{ + // Simplify the implementation of this method to resolve compilation issues + TArray IntersectionPoints; + + // TODO: Restore the complete implementation after resolving GeometryScripting issues + + return IntersectionPoints; +} + +// Create cap mesh +void UBooleanCutTool::CreateCapMesh(const TArray& 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(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(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 }