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 ActualDepth = ImpactDepth * ImpactForce;
|
||||||
|
|
||||||
// 计算凸起区域的外半径
|
UE_LOG(LogTemp, Log, TEXT("DynamicIronBlock: Hammer impact at local position (%f, %f, %f), depth: %f, radius: %f, anisotropy: %f"),
|
||||||
float BulgeOuterRadius = ImpactRadius * (1.0f + BulgeRatio);
|
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修改网格顶点
|
// 使用GeometryScript修改网格顶点
|
||||||
DynMesh->EditMesh([&](FDynamicMesh3& EditMesh)
|
DynMesh->EditMesh([&](FDynamicMesh3& EditMesh)
|
||||||
@@ -323,9 +326,14 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f
|
|||||||
// 计算顶点到锤击点的距离
|
// 计算顶点到锤击点的距离
|
||||||
float Distance = FVector::Dist(VertexPosFloat, LocalHitLocation);
|
float Distance = FVector::Dist(VertexPosFloat, LocalHitLocation);
|
||||||
|
|
||||||
|
// 只处理影响范围内的顶点
|
||||||
|
float BulgeOuterRadius = ImpactRadius * (1.0f + BulgeRatio);
|
||||||
|
if (Distance >= BulgeOuterRadius)
|
||||||
|
continue;
|
||||||
|
|
||||||
FVector Displacement = FVector::ZeroVector;
|
FVector Displacement = FVector::ZeroVector;
|
||||||
|
|
||||||
// 压缩区:顶点在锤击半径内,向下压
|
// 压缩区:顶点在锤击半径内,向下压
|
||||||
if (Distance < ImpactRadius)
|
if (Distance < ImpactRadius)
|
||||||
{
|
{
|
||||||
// 计算衰减因子(使用幂函数实现平滑过渡)
|
// 计算衰减因子(使用幂函数实现平滑过渡)
|
||||||
@@ -338,29 +346,44 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f
|
|||||||
// 沿着锤击法线方向进行形变(向内压)
|
// 沿着锤击法线方向进行形变(向内压)
|
||||||
Displacement = -LocalHitNormal * VertexDeformation;
|
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 DistanceInBulgeZone = EffectiveDistance / (BulgeOuterRadius - ImpactRadius);
|
||||||
float BulgeHeight = ActualDepth * BulgeStrength * BulgeFalloff;
|
|
||||||
|
|
||||||
// 计算径向方向(从锤击点指向顶点的水平方向)
|
if (DistanceInBulgeZone < 1.0f)
|
||||||
FVector RadialDir = VertexPosFloat - LocalHitLocation;
|
{
|
||||||
RadialDir = RadialDir - LocalHitNormal * FVector::DotProduct(RadialDir, LocalHitNormal); // 移除法线方向分量
|
// 凸起区衰减:靠近压缩区边界最强,向外逐渐减弱
|
||||||
RadialDir.Normalize();
|
float BulgeFalloff = 1.0f - FMath::Pow(DistanceInBulgeZone, FalloffExponent);
|
||||||
|
|
||||||
// 材料流动:主要向上(反法线方向),少量向外(径向)
|
// 计算凸起高度(基于体积守恒和BulgeStrength)
|
||||||
// 向上分量占70%,向外分量占30%
|
float BulgeHeight = ActualDepth * BulgeStrength * BulgeFalloff;
|
||||||
FVector UpwardDisplacement = -LocalHitNormal * BulgeHeight * 0.7f;
|
|
||||||
FVector OutwardDisplacement = RadialDir * BulgeHeight * 0.3f;
|
|
||||||
|
|
||||||
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);
|
FVector3d NewPosition = VertexPos + FVector3d(Displacement.X, Displacement.Y, Displacement.Z);
|
||||||
EditMesh.SetVertex(VertexID, NewPosition);
|
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);
|
}, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false);
|
||||||
@@ -382,7 +442,7 @@ void ADynamicIronBlock::OnHammerImpact(FVector HitLocation, FVector HitNormal, f
|
|||||||
// 重新生成 BVH
|
// 重新生成 BVH
|
||||||
GenerateBVH();
|
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"));
|
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();
|
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计算系统(Phase 2) ==========
|
||||||
|
|
||||||
// GPU缓冲区(Ping-Pong双缓冲)
|
// GPU缓冲区(Ping-Pong双缓冲)
|
||||||
|
|||||||
Reference in New Issue
Block a user