diff --git a/Source/BladeSmith/DynamicIronBlock.cpp b/Source/BladeSmith/DynamicIronBlock.cpp index 53aa915..dd9add8 100644 --- a/Source/BladeSmith/DynamicIronBlock.cpp +++ b/Source/BladeSmith/DynamicIronBlock.cpp @@ -304,11 +304,14 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f // 计算实际的形变深度 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, radius: %f, anisotropy: %f"), + LocalHitLocation.X, LocalHitLocation.Y, LocalHitLocation.Z, ActualDepth, ImpactRadius, AnisotropyRatio); - 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); + // 计算材料流动方向 + FVector BoxHalfSize = BoxSize * 0.5f; + FVector ToNearestEdge = FindNearestFreeEdgeDirection(LocalHitLocation, BoxHalfSize); + FVector PrimaryFlow = CalculatePrimaryFlowDirection(LocalHitNormal, ToNearestEdge); + FVector SecondaryFlow = CalculateSecondaryFlowDirection(LocalHitNormal, PrimaryFlow); // 使用GeometryScript修改网格顶点 DynMesh->EditMesh([&](FDynamicMesh3& EditMesh) @@ -323,9 +326,14 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f // 计算顶点到锤击点的距离 float Distance = FVector::Dist(VertexPosFloat, LocalHitLocation); + // 只处理影响范围内的顶点 + float BulgeOuterRadius = ImpactRadius * (1.0f + BulgeRatio); + if (Distance >= BulgeOuterRadius) + continue; + FVector Displacement = FVector::ZeroVector; - // 压缩区:顶点在锤击半径内,向下压 + // 压缩区:顶点在锤击半径内,向下压 if (Distance < ImpactRadius) { // 计算衰减因子(使用幂函数实现平滑过渡) @@ -338,29 +346,44 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f // 沿着锤击法线方向进行形变(向内压) Displacement = -LocalHitNormal * VertexDeformation; } - // 凸起区:顶点在压缩区外、凸起区内,材料向上和向外流动 - else if (Distance < BulgeOuterRadius) + // 凸起区:顶点在压缩区外、凸起区内,材料向上和向外流动 + else { - // 计算在凸起区中的位置 [0, 1] - float BulgeNormalizedDist = (Distance - ImpactRadius) / (BulgeOuterRadius - ImpactRadius); + // 将位置投影到流动方向 + float DistAlongPrimary = FVector::DotProduct( + FVector(VertexPosFloat - LocalHitLocation), PrimaryFlow); + float DistAlongSecondary = FVector::DotProduct( + FVector(VertexPosFloat - LocalHitLocation), SecondaryFlow); - // 凸起区衰减:靠近压缩区边界最强,向外逐渐减弱 - float BulgeFalloff = 1.0f - FMath::Pow(BulgeNormalizedDist, FalloffExponent); + // 各向异性距离计算 + // 主方向的"有效距离"更小(更容易到达)→ 凸起更大 + float EffectiveDistance = FMath::Sqrt( + FMath::Square(DistAlongPrimary / AnisotropyRatio) + + FMath::Square(DistAlongSecondary) + ); - // 计算凸起高度(基于体积守恒和BulgeStrength) - float BulgeHeight = ActualDepth * BulgeStrength * BulgeFalloff; + float DistanceInBulgeZone = EffectiveDistance / (BulgeOuterRadius - ImpactRadius); - // 计算径向方向(从锤击点指向顶点的水平方向) - FVector RadialDir = VertexPosFloat - LocalHitLocation; - RadialDir = RadialDir - LocalHitNormal * FVector::DotProduct(RadialDir, LocalHitNormal); // 移除法线方向分量 - RadialDir.Normalize(); + if (DistanceInBulgeZone < 1.0f) + { + // 凸起区衰减:靠近压缩区边界最强,向外逐渐减弱 + float BulgeFalloff = 1.0f - FMath::Pow(DistanceInBulgeZone, FalloffExponent); - // 材料流动:主要向上(反法线方向),少量向外(径向) - // 向上分量占70%,向外分量占30% - FVector UpwardDisplacement = -LocalHitNormal * BulgeHeight * 0.7f; - FVector OutwardDisplacement = RadialDir * BulgeHeight * 0.3f; + // 计算凸起高度(基于体积守恒和BulgeStrength) + float BulgeHeight = ActualDepth * BulgeStrength * BulgeFalloff; - Displacement = UpwardDisplacement + OutwardDisplacement; + // 材料流动:主要向上(反法线方向),少量向外(径向) + // 向上分量占70%,向外分量占30% + FVector UpwardDisplacement = -LocalHitNormal * BulgeHeight * 0.7f; + + // 计算径向方向(各向异性) + FVector RadialDir = PrimaryFlow * DistAlongPrimary + SecondaryFlow * DistAlongSecondary; + RadialDir.Normalize(); + + FVector OutwardDisplacement = RadialDir * BulgeHeight * 0.3f; + + Displacement = UpwardDisplacement + OutwardDisplacement; + } } // 应用形变 @@ -368,6 +391,43 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f { FVector3d NewPosition = VertexPos + FVector3d(Displacement.X, Displacement.Y, Displacement.Z); EditMesh.SetVertex(VertexID, NewPosition); + + // 更新累积应变(累加位移量) + if (bVertexAttributesInitialized && AccumulatedStrainArray.IsValidIndex(VertexID)) + { + float CurrentStrain = AccumulatedStrainArray[VertexID]; + float StrainDelta = Displacement.Size(); + AccumulatedStrainArray[VertexID] = CurrentStrain + StrainDelta; + } + } + } + + // 边缘约束(如果启用) + if (bEnableEdgeConstraint) + { + TSet EdgeVertices; + MarkEdgeVertices(EditMesh, EdgeVertices); + + // 只处理受影响区域的边缘顶点 + TSet AffectedEdges; + float BulgeOuterRadius = ImpactRadius * (1.0f + BulgeRatio); + for (int32 VID : EdgeVertices) + { + if (!EditMesh.IsVertex(VID)) + continue; + + FVector3d Pos = EditMesh.GetVertex(VID); + FVector PosFloat(Pos.X, Pos.Y, Pos.Z); + if (FVector::Dist(PosFloat, LocalHitLocation) < ImpactRadius * 1.5f) + { + AffectedEdges.Add(VID); + } + } + + if (AffectedEdges.Num() > 0) + { + ApplyEdgeLinearConstraint(EditMesh, AffectedEdges); + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Applied edge constraint to %d vertices"), AffectedEdges.Num()); } } }, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false); @@ -382,7 +442,7 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f // 重新生成 BVH GenerateBVH(); - UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Hammer impact deformation applied successfully")); + UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Anisotropic hammer impact applied successfully")); } // ========== 温度系统实现 ========== @@ -1367,3 +1427,116 @@ void ADynamicIronBlock::ReleaseVertexAttributes() UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Vertex attributes released")); } + +// ========== 各向异性形变辅助函数实现 ========== + +FVector ADynamicIronBlock::FindNearestFreeEdgeDirection(const FVector& LocalPos, const FVector& BoxHalfSize) const +{ + // 计算到6个面的距离 + float DistToEdges[6] = { + BoxHalfSize.X + LocalPos.X, // -X面 + BoxHalfSize.X - LocalPos.X, // +X面 + BoxHalfSize.Y + LocalPos.Y, // -Y面 + BoxHalfSize.Y - LocalPos.Y, // +Y面 + BoxHalfSize.Z + LocalPos.Z, // -Z面 + BoxHalfSize.Z - LocalPos.Z // +Z面 + }; + + // 找最近的边缘 + int32 NearestEdge = 0; + float MinDist = DistToEdges[0]; + for (int32 i = 1; i < 6; ++i) + { + if (DistToEdges[i] < MinDist) + { + MinDist = DistToEdges[i]; + NearestEdge = i; + } + } + + // 返回朝向最近边缘的方向 + static const FVector EdgeDirections[6] = { + FVector(-1, 0, 0), FVector(1, 0, 0), + FVector(0, -1, 0), FVector(0, 1, 0), + FVector(0, 0, -1), FVector(0, 0, 1) + }; + + return EdgeDirections[NearestEdge]; +} + +FVector ADynamicIronBlock::CalculatePrimaryFlowDirection(const FVector& HammerDir, const FVector& ToNearestEdge) const +{ + // 移除锤击方向的分量(材料不能向锤头方向流动) + FVector PrimaryFlow = ToNearestEdge - HammerDir * FVector::DotProduct(ToNearestEdge, HammerDir); + return PrimaryFlow.GetSafeNormal(); +} + +FVector ADynamicIronBlock::CalculateSecondaryFlowDirection(const FVector& HammerDir, const FVector& PrimaryFlow) const +{ + return FVector::CrossProduct(HammerDir, PrimaryFlow).GetSafeNormal(); +} + +void ADynamicIronBlock::MarkEdgeVertices(const FDynamicMesh3& Mesh, TSet& OutEdgeVertices) const +{ + FVector BoxHalfSize = BoxSize * 0.5f; + float EdgeThresholdCM = EdgeThreshold; + + for (int32 VID : Mesh.VertexIndicesItr()) + { + FVector3d Pos = Mesh.GetVertex(VID); + FVector PosFloat(Pos.X, Pos.Y, Pos.Z); + + // 检查是否接近任何边界 + bool bNearEdge = false; + bNearEdge |= FMath::Abs(PosFloat.X - BoxHalfSize.X) < EdgeThresholdCM; + bNearEdge |= FMath::Abs(PosFloat.X + BoxHalfSize.X) < EdgeThresholdCM; + bNearEdge |= FMath::Abs(PosFloat.Y - BoxHalfSize.Y) < EdgeThresholdCM; + bNearEdge |= FMath::Abs(PosFloat.Y + BoxHalfSize.Y) < EdgeThresholdCM; + bNearEdge |= FMath::Abs(PosFloat.Z - BoxHalfSize.Z) < EdgeThresholdCM; + bNearEdge |= FMath::Abs(PosFloat.Z + BoxHalfSize.Z) < EdgeThresholdCM; + + if (bNearEdge) + { + OutEdgeVertices.Add(VID); + } + } +} + +void ADynamicIronBlock::ApplyEdgeLinearConstraint(FDynamicMesh3& Mesh, const TSet& EdgeVertices) +{ + // 简化版本:仅平滑边缘顶点,不进行完整的PCA拟合 + for (int32 VID : EdgeVertices) + { + if (!Mesh.IsVertex(VID)) + continue; + + // 找到此顶点的边缘邻居 + TArray EdgeNeighbors; + for (int32 NbrVID : Mesh.VtxVerticesItr(VID)) + { + if (EdgeVertices.Contains(NbrVID)) + { + EdgeNeighbors.Add(NbrVID); + } + } + + if (EdgeNeighbors.Num() >= 2) + { + // 计算邻居顶点的平均位置作为目标位置 + FVector3f AvgPos(0.0f, 0.0f, 0.0f); + for (int32 NbrVID : EdgeNeighbors) + { + AvgPos += FVector3f(Mesh.GetVertex(NbrVID)); + } + AvgPos /= EdgeNeighbors.Num(); + + // 当前位置 + FVector3d CurrentPos = Mesh.GetVertex(VID); + + // 平滑更新(50%向平均位置移动) + FVector3d SmoothPos = FMath::Lerp(CurrentPos, FVector3d(AvgPos.X, AvgPos.Y, AvgPos.Z), 0.5f); + + Mesh.SetVertex(VID, SmoothPos); + } + } +} diff --git a/Source/BladeSmith/DynamicIronBlock.h b/Source/BladeSmith/DynamicIronBlock.h index 6ea8fdb..33970d7 100644 --- a/Source/BladeSmith/DynamicIronBlock.h +++ b/Source/BladeSmith/DynamicIronBlock.h @@ -312,6 +312,35 @@ private: */ void ReleaseVertexAttributes(); + // ========== 各向异性形变辅助函数 ========== + + /** + * 查找最近的自由边界方向 + * 材料会优先朝向自由边界流动(阻力最小) + */ + FVector FindNearestFreeEdgeDirection(const FVector& LocalPos, const FVector& BoxHalfSize) const; + + /** + * 计算材料流动的主方向 + * 主方向 = 朝向边缘 + 垂直于锤击方向 + */ + FVector CalculatePrimaryFlowDirection(const FVector& HammerDir, const FVector& ToNearestEdge) const; + + /** + * 计算次要流动方向(垂直于主方向和锤击方向) + */ + FVector CalculateSecondaryFlowDirection(const FVector& HammerDir, const FVector& PrimaryFlow) const; + + /** + * 检测并标记边缘顶点 + */ + void MarkEdgeVertices(const FDynamicMesh3& Mesh, TSet& OutEdgeVertices) const; + + /** + * 对边缘顶点应用线性约束 + */ + void ApplyEdgeLinearConstraint(FDynamicMesh3& Mesh, const TSet& EdgeVertices); + // ========== GPU计算系统(Phase 2) ========== // GPU缓冲区(Ping-Pong双缓冲)