This commit is contained in:
2025-04-18 18:17:02 +08:00
parent 80fdce96b2
commit e15eafe5d4
18 changed files with 259 additions and 493 deletions

View File

@@ -11,8 +11,8 @@ public class FLESH : ModuleRules
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
"$(EngineDir)/Plugins/Experimental/GeometryScripting/Source/GeometryScriptingCore/Public",
"$(EngineDir)/Plugins/Experimental/GeometryScripting/Source/GeometryScriptingEditor/Public"
// "$(EngineDir)/Plugins/Experimental/GeometryScripting/Source/GeometryScriptingCore/Public",
// "$(EngineDir)/Plugins/Experimental/GeometryScripting/Source/GeometryScriptingEditor/Public"
}
);

View File

@@ -1,258 +0,0 @@
#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"
// 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;
// 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)
{
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;
}
// 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)
{
// 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;
}
// 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
}

View File

@@ -3,8 +3,8 @@
#include "Engine/SkeletalMesh.h"
#include "ProceduralMeshComponent.h"
#include "Materials/MaterialInterface.h"
// 暂时注释掉 GeometryScripting 相关头文件
// 当我们解决了基础模块的编译问题后再恢复
// 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"
@@ -125,11 +125,11 @@ TArray<UProceduralMeshComponent*> UBooleanCutTool::CutProceduralMesh(UProcedural
TArray<FColor> VertexColors;
TArray<FProcMeshTangent> Tangents;
// UE5.5.4 中,GetSectionMeshData 方法已不存在,改为使用 GetProcMeshSection 方法
// In UE5.5.4, GetSectionMeshData method has been removed, replaced with GetProcMeshSection
FProcMeshSection* MeshSection = TargetMesh->GetProcMeshSection(0);
if (MeshSection)
{
// MeshSection 中提取数据
// Extract data from MeshSection
for (const FProcMeshVertex& Vertex : MeshSection->ProcVertexBuffer)
{
Vertices.Add(Vertex.Position);
@@ -139,7 +139,7 @@ TArray<UProceduralMeshComponent*> UBooleanCutTool::CutProceduralMesh(UProcedural
Tangents.Add(Vertex.Tangent);
}
// 转换索引
// Convert indices
for (uint32 Index : MeshSection->ProcIndexBuffer)
{
Triangles.Add(static_cast<int32>(Index));
@@ -231,10 +231,10 @@ UMaterialInterface* UBooleanCutTool::GetCutMaterial() const
// 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: 当我们解决了 GeometryScripting 相关的问题后再恢复完整实现
// TODO: Restore the complete implementation after resolving GeometryScripting issues
return IntersectionPoints;
}

View File

@@ -1,92 +0,0 @@
#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 InExpansionRate - Rate of expansion
* @param MaxSize - Maximum size
*/
UFUNCTION(BlueprintCallable, Category = "FLESH|Blood")
void StartExpansion(float InExpansionRate = 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();
};

View File

@@ -6,6 +6,8 @@
// Forward declaration
class UDismembermentGraphBase;
#include "DismembermentGraph/DismembermentGraphBase.h"
#include "DismembermentGraphAsset.generated.h"
/**
@@ -22,7 +24,7 @@ public:
// The graph owned by this asset
UPROPERTY()
class UDismembermentGraphBase* Graph;
TObjectPtr<class UDismembermentGraphBase> Graph;
// Compile the graph into executable logic
UFUNCTION(BlueprintCallable, Category = "FLESH|Dismemberment")

View File

@@ -5,10 +5,10 @@
#include "DismembermentGraphBase.generated.h"
/**
* Dismemberment graph base class
* Used for visual programming of dismemberment logic
* Dismemberment graph base class (Rewritten)
* Provides extensible, multi-layer, and serializable node graph for procedural dismemberment logic
*/
UCLASS()
UCLASS(BlueprintType)
class FLESH_API UDismembermentGraphBase : public UObject
{
GENERATED_BODY()
@@ -16,23 +16,35 @@ class FLESH_API UDismembermentGraphBase : public UObject
public:
UDismembermentGraphBase();
// Graph nodes
UPROPERTY()
TArray<class UEdGraphNode*> Nodes;
// All nodes in the graph (supports multiple node types)
UPROPERTY(VisibleAnywhere, Instanced, Category = "Graph")
TArray<TObjectPtr<class UEdGraphNode>> Nodes;
// Compilation status
UPROPERTY()
UPROPERTY(VisibleAnywhere, Category = "Graph")
bool bCompiled;
// Clear graph
// Multi-layer support: each layer can represent bone, organ, skin, etc.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Graph|Layers")
TArray<FName> Layers;
// Patch data for each layer (future extensibility)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Graph|Patch")
TMap<FName, FString> LayerPatchData;
// Clear all nodes and reset graph
UFUNCTION(BlueprintCallable, Category = "Graph")
void ClearGraph();
// Add node
// Add a node to the graph
UFUNCTION(BlueprintCallable, Category = "Graph")
class UEdGraphNode* AddNode(TSubclassOf<class UEdGraphNode> NodeClass, const FVector2D& Position);
// Remove node
// Remove a node from the graph
UFUNCTION(BlueprintCallable, Category = "Graph")
void RemoveNode(class UEdGraphNode* Node);
// Create connection
void CreateConnection(class UEdGraphPin* A, class UEdGraphPin* B);
// Serialize graph to string (for asset versioning, debugging, etc.)
UFUNCTION(BlueprintCallable, Category = "Graph")
FString SerializeGraph() const;
};

View File

@@ -0,0 +1,84 @@
// @ 2025, Copyright Virtuos Games. All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "NiagaraComponent.h"
#include "NiagaraSystem.h"
#include "BloodPool.generated.h"
class UNiagaraSystem;
class UNiagaraComponent;
/**
* Blood pool actor that creates a decal and blood burst effect
* Used to simulate blood pooling under dismembered body parts
*/
UCLASS(Blueprintable)
class FLESH_API ABloodPool : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABloodPool();
virtual void BeginPlay() override;
/**
* Create blood pool
* @param World - World object
* @param Location - Blood pool location
* @param Scale - Blood pool size
* @param BloodPoolTemplate - Blood pool template class
* @return Created blood pool Actor
*/
UFUNCTION(BlueprintCallable, Category = "Dismemberment|Gore", meta = (WorldContext = "World"))
static ABloodPool* CreateBloodPool(UWorld* World, const FVector& Location, float Scale = 1.0f, TSubclassOf<ABloodPool> BloodPoolTemplate = nullptr);
// Components
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
TObjectPtr<class UBoxComponent> Collision;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
TObjectPtr<class UDecalComponent> Decal;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
TObjectPtr<UNiagaraComponent> BloodBurstEffect;
protected:
FVector RemapSizeForDecal(const FVector In) const;
public:
// Visual Effects
/** The blood decal material to use */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Visual Effects", meta=(DisplayPriority=1))
TObjectPtr<UMaterialInterface> DecalMaterial = nullptr;
/** The blood burst Niagara system to use */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Visual Effects", meta=(DisplayPriority=2))
TObjectPtr<UNiagaraSystem> BloodBurstSystem;
// Timing Parameters
/** Adds a delay before the blood decal appears */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Timing", meta=(ExposeOnSpawn="true", ClampMin="0.0", UIMin="0.0", DisplayPriority=1))
float StartDelay = 0.f;
/** The lifetime of the blood before it dries up and fades away */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Timing", meta=(ExposeOnSpawn="true", ClampMin="1.0", UIMin="1.0", DisplayPriority=2))
float MaxLifetime = 60.f;
/** Time it takes to interpolate to the new size and location */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Timing", meta=(ExposeOnSpawn="true", ClampMin="0.0", UIMin="0.0", DisplayPriority=3))
float InterpTime = 0.3f;
// Appearance Parameters
/** Size of the blood decal */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Appearance", meta=(ExposeOnSpawn="true", DisplayPriority=1))
FVector DecalSize = FVector(100);
/** Relative rotation of the decal */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Dismemberment|Appearance", meta=(ExposeOnSpawn="true", DisplayPriority=2))
FRotator DecalRotation = FRotator(0);
};