diff --git a/Source/BladeSmith/DynamicIronBlock.cpp b/Source/BladeSmith/DynamicIronBlock.cpp index f6b9db3..601e882 100644 --- a/Source/BladeSmith/DynamicIronBlock.cpp +++ b/Source/BladeSmith/DynamicIronBlock.cpp @@ -7,6 +7,11 @@ #include "GeometryScript/MeshPrimitiveFunctions.h" #include "GeometryScript/MeshSpatialFunctions.h" #include "GeometryScript/MeshBasicEditFunctions.h" +#include "GeometryScript/MeshUVFunctions.h" +#include "GeometryScript/MeshQueryFunctions.h" +#include "GeometryScript/MeshDeformFunctions.h" +#include "GeometryScript/MeshNormalsFunctions.h" +#include "Materials/MaterialInstanceDynamic.h" ADynamicIronBlock::ADynamicIronBlock() { @@ -73,12 +78,36 @@ void ADynamicIronBlock::InitializeBoxMesh() NumSubDivide.X, NumSubDivide.Y, NumSubDivide.Z // X, Y, Z 轴的细分数 ); + // 为网格生成唯一的 UV 坐标(使用 XAtlas 自动展开) + FGeometryScriptXAtlasOptions XAtlasOptions; + XAtlasOptions.MaxIterations = 1; + + UGeometryScriptLibrary_MeshUVFunctions::AutoGenerateXAtlasMeshUVs( + DynMesh, + 0, // UV 通道 0 + XAtlasOptions + ); + + // 如果设置了材质,创建动态材质实例并应用 + if (IronMaterial) + { + UMaterialInstanceDynamic* DynamicMaterialInstance = UMaterialInstanceDynamic::Create(IronMaterial, this); + if (DynamicMaterialInstance) + { + DynMeshComp->SetMaterial(0, DynamicMaterialInstance); + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Dynamic material instance created and applied")); + } + } + // 通知组件网格已更新 DynMeshComp->NotifyMeshUpdated(); // 生成 BVH GenerateBVH(); + // 初始化温度网格 + InitializeTemperatureGrid(); + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Box mesh initialized with size (%f, %f, %f)"), BoxSize.X, BoxSize.Y, BoxSize.Z); } @@ -112,4 +141,474 @@ void ADynamicIronBlock::GenerateBVH() DynMeshComp->UpdateCollision(); UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: BVH generated successfully")); -} \ No newline at end of file +} + +void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, float ImpactRadius, float ImpactDepth, float ImpactForce) +{ + // 获取 DynamicMeshComponent + UDynamicMeshComponent* DynMeshComp = GetDynamicMeshComponent(); + if (!DynMeshComp) + { + UE_LOG(LogTemp, Error, TEXT("DynamicIronBlock: Failed to get DynamicMeshComponent for hammer impact")); + return; + } + + // 获取 DynamicMesh + UDynamicMesh* DynMesh = DynMeshComp->GetDynamicMesh(); + if (!DynMesh) + { + UE_LOG(LogTemp, Error, TEXT("DynamicIronBlock: Failed to get DynamicMesh for hammer impact")); + return; + } + + // 限制力度范围在0-1之间 + ImpactForce = FMath::Clamp(ImpactForce, 0.0f, 1.0f); + + // 将世界坐标转换为局部坐标 + FTransform ActorTransform = GetActorTransform(); + FVector LocalHitLocation = ActorTransform.InverseTransformPosition(HitLocation); + FVector LocalHitNormal = ActorTransform.InverseTransformVector(HitNormal).GetSafeNormal(); + + // 计算实际的形变深度 + float ActualDepth = ImpactDepth * ImpactForce; + + // 计算凸起区域的外半径 + float BulgeOuterRadius = ImpactRadius * (1.0f + BulgeRatio); + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Hammer impact at local position (%f, %f, %f), depth: %f, impact radius: %f, bulge radius: %f"), + LocalHitLocation.X, LocalHitLocation.Y, LocalHitLocation.Z, ActualDepth, ImpactRadius, BulgeOuterRadius); + + // 使用GeometryScript修改网格顶点 + DynMesh->EditMesh([&](FDynamicMesh3& EditMesh) + { + // 遍历所有顶点 + for (int32 VertexID : EditMesh.VertexIndicesItr()) + { + // 获取顶点位置 + FVector3d VertexPos = EditMesh.GetVertex(VertexID); + FVector VertexPosFloat(VertexPos.X, VertexPos.Y, VertexPos.Z); + + // 计算顶点到锤击点的距离 + float Distance = FVector::Dist(VertexPosFloat, LocalHitLocation); + + FVector Displacement = FVector::ZeroVector; + + // 压缩区:顶点在锤击半径内,向下压 + if (Distance < ImpactRadius) + { + // 计算衰减因子(使用幂函数实现平滑过渡) + float NormalizedDist = Distance / ImpactRadius; // 归一化距离 [0, 1] + float Falloff = 1.0f - FMath::Pow(NormalizedDist, FalloffExponent); // 衰减系数 + + // 计算该顶点的形变偏移量 + float VertexDeformation = ActualDepth * Falloff; + + // 沿着锤击法线方向进行形变(向内压) + Displacement = -LocalHitNormal * VertexDeformation; + } + // 凸起区:顶点在压缩区外、凸起区内,材料向上和向外流动 + else if (Distance < BulgeOuterRadius) + { + // 计算在凸起区中的位置 [0, 1] + float BulgeNormalizedDist = (Distance - ImpactRadius) / (BulgeOuterRadius - ImpactRadius); + + // 凸起区衰减:靠近压缩区边界最强,向外逐渐减弱 + float BulgeFalloff = 1.0f - FMath::Pow(BulgeNormalizedDist, FalloffExponent); + + // 计算凸起高度(基于体积守恒和BulgeStrength) + float BulgeHeight = ActualDepth * BulgeStrength * BulgeFalloff; + + // 计算径向方向(从锤击点指向顶点的水平方向) + FVector RadialDir = VertexPosFloat - LocalHitLocation; + RadialDir = RadialDir - LocalHitNormal * FVector::DotProduct(RadialDir, LocalHitNormal); // 移除法线方向分量 + RadialDir.Normalize(); + + // 材料流动:主要向上(反法线方向),少量向外(径向) + // 向上分量占70%,向外分量占30% + FVector UpwardDisplacement = -LocalHitNormal * BulgeHeight * 0.7f; + FVector OutwardDisplacement = RadialDir * BulgeHeight * 0.3f; + + Displacement = UpwardDisplacement + OutwardDisplacement; + } + + // 应用形变 + if (!Displacement.IsNearlyZero()) + { + FVector3d NewPosition = VertexPos + FVector3d(Displacement.X, Displacement.Y, Displacement.Z); + EditMesh.SetVertex(VertexID, NewPosition); + } + } + }, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false); + + // 重新计算法线 + FGeometryScriptCalculateNormalsOptions NormalOp; + UGeometryScriptLibrary_MeshNormalsFunctions::RecomputeNormals(DynMesh,NormalOp); + + // 通知组件网格已更新 + DynMeshComp->NotifyMeshUpdated(); + + // 重新生成 BVH + GenerateBVH(); + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Hammer impact deformation applied successfully")); +} + +// ========== 温度系统实现 ========== + +void ADynamicIronBlock::CalculateTemperatureResolution() +{ + // 将BoxSize从厘米转换为毫米 + FVector BoxSizeMM = BoxSize * 10.0f; + + // 计算每个轴的体素数量(向上取整) + TemperatureResolution.X = FMath::Max(1, FMath::CeilToInt(BoxSizeMM.X / TemperatureVoxelSize)); + TemperatureResolution.Y = FMath::Max(1, FMath::CeilToInt(BoxSizeMM.Y / TemperatureVoxelSize)); + TemperatureResolution.Z = FMath::Max(1, FMath::CeilToInt(BoxSizeMM.Z / TemperatureVoxelSize)); + + // 限制最大分辨率(防止内存爆炸) + const int32 MaxResolutionPerAxis = 200; + TemperatureResolution.X = FMath::Min(TemperatureResolution.X, MaxResolutionPerAxis); + TemperatureResolution.Y = FMath::Min(TemperatureResolution.Y, MaxResolutionPerAxis); + TemperatureResolution.Z = FMath::Min(TemperatureResolution.Z, MaxResolutionPerAxis); + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Temperature resolution calculated as (%d, %d, %d)"), + TemperatureResolution.X, TemperatureResolution.Y, TemperatureResolution.Z); +} + +void ADynamicIronBlock::InitializeTemperatureGrid() +{ + if (!bEnableTemperatureSystem) + { + UE_LOG(LogTemp, Warning, TEXT("DynamicIronBlock: Temperature system is disabled")); + return; + } + + // 计算分辨率 + CalculateTemperatureResolution(); + + // 分配体素数组 + const int32 TotalVoxels = TemperatureResolution.X * TemperatureResolution.Y * TemperatureResolution.Z; + TemperatureGrid.SetNum(TotalVoxels); + + // 计算体素网格的起始位置(相对于Actor中心,单位:厘米) + FVector GridOrigin = -BoxSize * 0.5f; + + // 计算实际体素尺寸(毫米转厘米) + FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f); + + // 初始化每个体素 + for (int32 Z = 0; Z < TemperatureResolution.Z; ++Z) + { + for (int32 Y = 0; Y < TemperatureResolution.Y; ++Y) + { + for (int32 X = 0; X < TemperatureResolution.X; ++X) + { + int32 Index = GetTemperatureIndex(X, Y, Z); + FTemperatureVoxel& Voxel = TemperatureGrid[Index]; + + // 初始温度(室温) + Voxel.Temperature = AmbientTemperature; + + // 检测是否为表面体素 + if (IsSurfaceTemperatureVoxel(X, Y, Z)) + { + Voxel.Flags |= 1; // 设置bit 0为1 + } + else + { + Voxel.Flags &= ~1; // 设置bit 0为0 + } + + // 更新塑性系数 + UpdatePlasticityCoefficient(Voxel); + } + } + } + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Temperature grid initialized with %d voxels (%.2f MB)"), + TotalVoxels, (TotalVoxels * sizeof(FTemperatureVoxel)) / (1024.0f * 1024.0f)); +} + +bool ADynamicIronBlock::IsSurfaceTemperatureVoxel(int32 X, int32 Y, int32 Z) const +{ + return X == 0 || X == TemperatureResolution.X - 1 || + Y == 0 || Y == TemperatureResolution.Y - 1 || + Z == 0 || Z == TemperatureResolution.Z - 1; +} + +float ADynamicIronBlock::CalculatePlasticityFromTemperature(float Temperature) const +{ + // 归一化温度到 [0, 1] 范围 + float NormalizedTemp = FMath::Clamp( + (Temperature - ForgeTemperatureMin) / (ForgeTemperatureMax - ForgeTemperatureMin), + 0.0f, 1.0f + ); + + // 使用平滑步函数(Smoothstep) + return NormalizedTemp * NormalizedTemp * (3.0f - 2.0f * NormalizedTemp); +} + +void ADynamicIronBlock::UpdatePlasticityCoefficient(FTemperatureVoxel& Voxel) +{ + Voxel.PlasticityCoefficient = CalculatePlasticityFromTemperature(Voxel.Temperature); +} + +void ADynamicIronBlock::HeatRegion(FVector WorldCenter, float Radius, float TemperatureIncrease) +{ + if (!bEnableTemperatureSystem || TemperatureGrid.Num() == 0) + { + UE_LOG(LogTemp, Warning, TEXT("DynamicIronBlock: Temperature system not initialized")); + return; + } + + // 将世界坐标转换为局部坐标 + FTransform ActorTransform = GetActorTransform(); + FVector LocalCenter = ActorTransform.InverseTransformPosition(WorldCenter); + + // 计算体素网格的起始位置 + FVector GridOrigin = -BoxSize * 0.5f; + FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f); + + // 将影响范围转换为体素坐标 + FVector LocalCenterVoxel = (LocalCenter - GridOrigin) / VoxelSizeCM; + int32 RadiusVoxels = FMath::CeilToInt(Radius / VoxelSizeCM.X); + + int32 MinX = FMath::Max(0, FMath::FloorToInt(LocalCenterVoxel.X) - RadiusVoxels); + int32 MaxX = FMath::Min(TemperatureResolution.X - 1, FMath::CeilToInt(LocalCenterVoxel.X) + RadiusVoxels); + int32 MinY = FMath::Max(0, FMath::FloorToInt(LocalCenterVoxel.Y) - RadiusVoxels); + int32 MaxY = FMath::Min(TemperatureResolution.Y - 1, FMath::CeilToInt(LocalCenterVoxel.Y) + RadiusVoxels); + int32 MinZ = FMath::Max(0, FMath::FloorToInt(LocalCenterVoxel.Z) - RadiusVoxels); + int32 MaxZ = FMath::Min(TemperatureResolution.Z - 1, FMath::CeilToInt(LocalCenterVoxel.Z) + RadiusVoxels); + + int32 AffectedVoxels = 0; + + // 遍历影响范围内的体素 + for (int32 Z = MinZ; Z <= MaxZ; ++Z) + { + for (int32 Y = MinY; Y <= MaxY; ++Y) + { + for (int32 X = MinX; X <= MaxX; ++X) + { + // 计算体素中心在局部坐标中的位置 + FVector VoxelCenter = GridOrigin + FVector( + (X + 0.5f) * VoxelSizeCM.X, + (Y + 0.5f) * VoxelSizeCM.Y, + (Z + 0.5f) * VoxelSizeCM.Z + ); + + // 计算距离 + float Distance = FVector::Dist(VoxelCenter, LocalCenter); + + if (Distance < Radius) + { + int32 Index = GetTemperatureIndex(X, Y, Z); + FTemperatureVoxel& Voxel = TemperatureGrid[Index]; + + // 距离衰减 + float Falloff = 1.0f - (Distance / Radius); + float TempDelta = TemperatureIncrease * Falloff; + + Voxel.Temperature += TempDelta; + UpdatePlasticityCoefficient(Voxel); + ++AffectedVoxels; + } + } + } + } + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Heated %d voxels in region (center: %s, radius: %.2f)"), + AffectedVoxels, *LocalCenter.ToString(), Radius); + + // 更新顶点颜色以显示温度变化 + UpdateVertexColors(); +} + +float ADynamicIronBlock::SampleTemperatureAtPosition(const FVector& LocalPosition) const +{ + if (!bEnableTemperatureSystem || TemperatureGrid.Num() == 0) + { + return AmbientTemperature; + } + + // 将位置转换为体素坐标系 + FVector GridOrigin = -BoxSize * 0.5f; + FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f); + FVector VoxelCoord = (LocalPosition - GridOrigin) / VoxelSizeCM; + + // 获取相邻的8个体素的索引 + int32 X0 = FMath::FloorToInt(VoxelCoord.X); + int32 Y0 = FMath::FloorToInt(VoxelCoord.Y); + int32 Z0 = FMath::FloorToInt(VoxelCoord.Z); + int32 X1 = X0 + 1; + int32 Y1 = Y0 + 1; + int32 Z1 = Z0 + 1; + + // 边界检查 + if (!IsValidTemperatureCoord(X0, Y0, Z0) || !IsValidTemperatureCoord(X1, Y1, Z1)) + { + return AmbientTemperature; + } + + // 插值权重 + float Fx = VoxelCoord.X - X0; + float Fy = VoxelCoord.Y - Y0; + float Fz = VoxelCoord.Z - Z0; + + // 获取8个角点的温度 + float T000 = TemperatureGrid[GetTemperatureIndex(X0, Y0, Z0)].Temperature; + float T100 = TemperatureGrid[GetTemperatureIndex(X1, Y0, Z0)].Temperature; + float T010 = TemperatureGrid[GetTemperatureIndex(X0, Y1, Z0)].Temperature; + float T110 = TemperatureGrid[GetTemperatureIndex(X1, Y1, Z0)].Temperature; + float T001 = TemperatureGrid[GetTemperatureIndex(X0, Y0, Z1)].Temperature; + float T101 = TemperatureGrid[GetTemperatureIndex(X1, Y0, Z1)].Temperature; + float T011 = TemperatureGrid[GetTemperatureIndex(X0, Y1, Z1)].Temperature; + float T111 = TemperatureGrid[GetTemperatureIndex(X1, Y1, Z1)].Temperature; + + // 三线性插值 + float T00 = FMath::Lerp(T000, T100, Fx); + float T10 = FMath::Lerp(T010, T110, Fx); + float T01 = FMath::Lerp(T001, T101, Fx); + float T11 = FMath::Lerp(T011, T111, Fx); + float T0 = FMath::Lerp(T00, T10, Fy); + float T1 = FMath::Lerp(T01, T11, Fy); + return FMath::Lerp(T0, T1, Fz); +} + +float ADynamicIronBlock::SamplePlasticityAtPosition(const FVector& LocalPosition) const +{ + if (!bEnableTemperatureSystem || TemperatureGrid.Num() == 0) + { + return 0.0f; + } + + // 将位置转换为体素坐标系 + FVector GridOrigin = -BoxSize * 0.5f; + FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f); + FVector VoxelCoord = (LocalPosition - GridOrigin) / VoxelSizeCM; + + // 获取相邻的8个体素的索引 + int32 X0 = FMath::FloorToInt(VoxelCoord.X); + int32 Y0 = FMath::FloorToInt(VoxelCoord.Y); + int32 Z0 = FMath::FloorToInt(VoxelCoord.Z); + int32 X1 = X0 + 1; + int32 Y1 = Y0 + 1; + int32 Z1 = Z0 + 1; + + // 边界检查 + if (!IsValidTemperatureCoord(X0, Y0, Z0) || !IsValidTemperatureCoord(X1, Y1, Z1)) + { + return 0.0f; + } + + // 插值权重 + float Fx = VoxelCoord.X - X0; + float Fy = VoxelCoord.Y - Y0; + float Fz = VoxelCoord.Z - Z0; + + // 获取8个角点的塑性系数 + float P000 = TemperatureGrid[GetTemperatureIndex(X0, Y0, Z0)].PlasticityCoefficient; + float P100 = TemperatureGrid[GetTemperatureIndex(X1, Y0, Z0)].PlasticityCoefficient; + float P010 = TemperatureGrid[GetTemperatureIndex(X0, Y1, Z0)].PlasticityCoefficient; + float P110 = TemperatureGrid[GetTemperatureIndex(X1, Y1, Z0)].PlasticityCoefficient; + float P001 = TemperatureGrid[GetTemperatureIndex(X0, Y0, Z1)].PlasticityCoefficient; + float P101 = TemperatureGrid[GetTemperatureIndex(X1, Y0, Z1)].PlasticityCoefficient; + float P011 = TemperatureGrid[GetTemperatureIndex(X0, Y1, Z1)].PlasticityCoefficient; + float P111 = TemperatureGrid[GetTemperatureIndex(X1, Y1, Z1)].PlasticityCoefficient; + + // 三线性插值 + float P00 = FMath::Lerp(P000, P100, Fx); + float P10 = FMath::Lerp(P010, P110, Fx); + float P01 = FMath::Lerp(P001, P101, Fx); + float P11 = FMath::Lerp(P011, P111, Fx); + float P0 = FMath::Lerp(P00, P10, Fy); + float P1 = FMath::Lerp(P01, P11, Fy); + return FMath::Lerp(P0, P1, Fz); +} + +FLinearColor ADynamicIronBlock::TemperatureToColor(float Temperature) const +{ + // 归一化温度到 [0, 1] 范围 + float NormalizedTemp = FMath::Clamp( + (Temperature - AmbientTemperature) / (ForgeTemperatureMax - AmbientTemperature), + 0.0f, 1.0f + ); + + // 黑体辐射近似颜色映射 + if (NormalizedTemp < 0.33f) + { + // 冷:深灰 → 暗红 + return FMath::Lerp( + FLinearColor(0.2f, 0.2f, 0.2f), + FLinearColor(0.5f, 0.1f, 0.05f), + NormalizedTemp * 3.0f + ); + } + else if (NormalizedTemp < 0.66f) + { + // 中:暗红 → 亮红 + return FMath::Lerp( + FLinearColor(0.5f, 0.1f, 0.05f), + FLinearColor(1.0f, 0.3f, 0.1f), + (NormalizedTemp - 0.33f) * 3.0f + ); + } + else + { + // 热:亮红 → 橙黄白 + return FMath::Lerp( + FLinearColor(1.0f, 0.3f, 0.1f), + FLinearColor(1.0f, 1.0f, 0.9f), + (NormalizedTemp - 0.66f) * 3.0f + ); + } +} + +void ADynamicIronBlock::UpdateVertexColors() +{ + if (!bEnableTemperatureSystem || TemperatureGrid.Num() == 0) + { + return; + } + + UDynamicMeshComponent* DynMeshComp = GetDynamicMeshComponent(); + if (!DynMeshComp) + { + return; + } + + UDynamicMesh* DynMesh = DynMeshComp->GetDynamicMesh(); + if (!DynMesh) + { + return; + } + + DynMesh->EditMesh([&](FDynamicMesh3& EditMesh) + { + // 启用顶点颜色(如果尚未启用) + if (!EditMesh.HasVertexColors()) + { + EditMesh.EnableVertexColors(FVector3f(0.2f, 0.2f, 0.2f)); + } + + // 遍历所有顶点 + for (int32 VertexID : EditMesh.VertexIndicesItr()) + { + // 获取顶点位置(局部坐标) + FVector3d VertexPos = EditMesh.GetVertex(VertexID); + FVector VertexPosFloat(VertexPos.X, VertexPos.Y, VertexPos.Z); + + // 通过三线性插值获取该顶点的温度 + float Temperature = SampleTemperatureAtPosition(VertexPosFloat); + + // 将温度映射到颜色 + FLinearColor VertexColor = TemperatureToColor(Temperature); + + // 设置顶点颜色 + EditMesh.SetVertexColor(VertexID, FVector3f(VertexColor.R, VertexColor.G, VertexColor.B)); + } + }, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false); + + DynMeshComp->NotifyMeshUpdated(); + + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Vertex colors updated based on temperature")); +} diff --git a/Source/BladeSmith/DynamicIronBlock.h b/Source/BladeSmith/DynamicIronBlock.h index d36e9e6..7b51c8b 100644 --- a/Source/BladeSmith/DynamicIronBlock.h +++ b/Source/BladeSmith/DynamicIronBlock.h @@ -6,9 +6,37 @@ #include "DynamicMeshActor.h" #include "DynamicIronBlock.generated.h" +/** + * 温度体素结构 - 存储单个体素的温度和塑性数据 + */ +USTRUCT(BlueprintType) +struct FTemperatureVoxel +{ + GENERATED_BODY() + + // 温度值(摄氏度) + UPROPERTY() + float Temperature; + + // 塑性系数(0-1),由温度计算得出 + UPROPERTY() + float PlasticityCoefficient; + + // 标志位(bit 0: 是否为表面体素) + UPROPERTY() + uint8 Flags; + + FTemperatureVoxel() + : Temperature(20.0f) + , PlasticityCoefficient(0.0f) + , Flags(0) + {} +}; + /** * 动态铁块Actor,继承自ADynamicMeshActor * 用于模拟铁匠打铁游戏中的铁块,支持动态变形 + * 包含基于体素的温度模拟系统 */ UCLASS() class BLADESMITH_API ADynamicIronBlock : public ADynamicMeshActor @@ -29,6 +57,72 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Iron Block") bool bEnableFastBuild; + // 铁块材质 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Iron Block") + UMaterialInterface* IronMaterial; + + // 形变衰减指数(控制边缘过渡的平滑度)- 材质物理特性 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Iron Block|Material Properties") + float FalloffExponent = 2.0f; + + // 材料凸起比例(凸起区域半径相对于压缩区域的倍数) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Iron Block|Material Properties") + float BulgeRatio = 0.8f; + + // 材料凸起强度(控制凸起高度,基于体积守恒) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Iron Block|Material Properties") + float BulgeStrength = 0.5f; + + // ========== 温度系统参数 ========== + + // 温度体素大小(毫米) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System", meta = (ClampMin = "3.0", ClampMax = "10.0")) + float TemperatureVoxelSize = 5.0f; + + // 热传导系数 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System", meta = (ClampMin = "0.0", ClampMax = "1.0")) + float ThermalConductivity = 0.1f; + + // 表面冷却速率 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System", meta = (ClampMin = "0.0", ClampMax = "1.0")) + float CoolingRate = 0.05f; + + // 环境温度(摄氏度) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System") + float AmbientTemperature = 20.0f; + + // 锻造温度下限(摄氏度)- 开始软化 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System") + float ForgeTemperatureMin = 800.0f; + + // 锻造温度上限(摄氏度)- 完全软化 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System") + float ForgeTemperatureMax = 1200.0f; + + // 是否启用温度系统 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Temperature System") + bool bEnableTemperatureSystem = true; + + /** + * 测试函数 - 加热指定区域 + * @param WorldCenter 加热中心位置(世界坐标) + * @param Radius 加热半径 + * @param TemperatureIncrease 温度增量 + */ + UFUNCTION(BlueprintCallable, Category = "Temperature System") + void HeatRegion(FVector WorldCenter, float Radius, float TemperatureIncrease); + + /** + * 受击函数 - 模拟圆形铁锤锤击金属表面产生塑性形变 + * @param HitLocation 锤击位置(世界坐标) + * @param HitNormal 锤击法线方向 + * @param ImpactRadius 锤击影响半径 + * @param ImpactDepth 锤击形变深度 + * @param ImpactForce 锤击力度(0-1),影响形变深度 + */ + UFUNCTION(BlueprintCallable, Category = "Hammer Impact") + void OnHammerImpact(FVector HitLocation, FVector HitNormal, float ImpactRadius, float ImpactDepth, float ImpactForce = 1.0f); + protected: // 当游戏开始或生成时调用 virtual void BeginPlay() override; @@ -46,4 +140,95 @@ private: * 生成BVH加速结构 */ void GenerateBVH(); + + // ========== 温度系统数据 ========== + + // 温度体素网格(CPU侧) + TArray TemperatureGrid; + + // 温度网格分辨率 + FIntVector TemperatureResolution; + + // ========== 温度系统方法 ========== + + /** + * 计算温度网格分辨率(基于BoxSize和TemperatureVoxelSize) + */ + void CalculateTemperatureResolution(); + + /** + * 初始化温度体素网格 + */ + void InitializeTemperatureGrid(); + + /** + * 获取体素索引 + */ + FORCEINLINE int32 GetTemperatureIndex(int32 X, int32 Y, int32 Z) const + { + return X + Y * TemperatureResolution.X + Z * TemperatureResolution.X * TemperatureResolution.Y; + } + + FORCEINLINE int32 GetTemperatureIndex(const FIntVector& Coord) const + { + return GetTemperatureIndex(Coord.X, Coord.Y, Coord.Z); + } + + /** + * 检查体素坐标是否有效 + */ + FORCEINLINE bool IsValidTemperatureCoord(int32 X, int32 Y, int32 Z) const + { + return X >= 0 && X < TemperatureResolution.X && + Y >= 0 && Y < TemperatureResolution.Y && + Z >= 0 && Z < TemperatureResolution.Z; + } + + FORCEINLINE bool IsValidTemperatureCoord(const FIntVector& Coord) const + { + return IsValidTemperatureCoord(Coord.X, Coord.Y, Coord.Z); + } + + /** + * 判断是否为表面体素 + */ + bool IsSurfaceTemperatureVoxel(int32 X, int32 Y, int32 Z) const; + + /** + * 从温度网格采样温度(三线性插值) + * @param LocalPosition 局部坐标位置 + * @return 插值后的温度值 + */ + float SampleTemperatureAtPosition(const FVector& LocalPosition) const; + + /** + * 从温度网格采样塑性系数(三线性插值) + * @param LocalPosition 局部坐标位置 + * @return 插值后的塑性系数 + */ + float SamplePlasticityAtPosition(const FVector& LocalPosition) const; + + /** + * 根据温度计算塑性系数 + * @param Temperature 温度值 + * @return 塑性系数(0-1) + */ + float CalculatePlasticityFromTemperature(float Temperature) const; + + /** + * 更新体素的塑性系数(基于当前温度) + */ + void UpdatePlasticityCoefficient(FTemperatureVoxel& Voxel); + + /** + * 温度到颜色的映射(黑体辐射近似) + * @param Temperature 温度值 + * @return 颜色 + */ + FLinearColor TemperatureToColor(float Temperature) const; + + /** + * 更新网格顶点颜色以显示温度 + */ + void UpdateVertexColors(); }; \ No newline at end of file