feat: 实现各向异性形变系统和边缘约束
- Task 4: 实现方向检测辅助函数 - FindNearestFreeEdgeDirection() - 查找最近的自由边界方向 - CalculatePrimaryFlowDirection() - 计算材料流动的主方向 - CalculateSecondaryFlowDirection() - 计算次要流动方向 - Task 5: 重构OnHammerImpact以支持各向异性形变 - 添加材料流动方向计算 - 在凸起区使用各向异性距离计算 - 更新累积应变数组 - Task 6: 实现边缘顶点检测和约束 - MarkEdgeVertices() - 检测并标记边缘顶点 - ApplyEdgeLinearConstraint() - 对边缘顶点应用线性约束 - 在OnHammerImpact中集成边缘约束逻辑 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<int32> EdgeVertices;
|
||||
MarkEdgeVertices(EditMesh, EdgeVertices);
|
||||
|
||||
// 只处理受影响区域的边缘顶点
|
||||
TSet<int32> 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<int32>& 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<int32>& EdgeVertices)
|
||||
{
|
||||
// 简化版本:仅平滑边缘顶点,不进行完整的PCA拟合
|
||||
for (int32 VID : EdgeVertices)
|
||||
{
|
||||
if (!Mesh.IsVertex(VID))
|
||||
continue;
|
||||
|
||||
// 找到此顶点的边缘邻居
|
||||
TArray<int32> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int32>& OutEdgeVertices) const;
|
||||
|
||||
/**
|
||||
* 对边缘顶点应用线性约束
|
||||
*/
|
||||
void ApplyEdgeLinearConstraint(FDynamicMesh3& Mesh, const TSet<int32>& EdgeVertices);
|
||||
|
||||
// ========== GPU计算系统(Phase 2) ==========
|
||||
|
||||
// GPU缓冲区(Ping-Pong双缓冲)
|
||||
|
||||
Reference in New Issue
Block a user