296 lines
9.9 KiB
C++
296 lines
9.9 KiB
C++
// Copyright 2016 Mookie. All Rights Reserved.
|
||
|
||
#include "EBMathematicalBallistics.h"
|
||
#include "Engine/Engine.h"
|
||
#include "Math/UnrealMathUtility.h"
|
||
|
||
float UEBMathematicalBallistics::CalculatePenetrationDepth(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float VelocityMPS,
|
||
float ImpactAngleDegrees)
|
||
{
|
||
// Calculate kinetic energy in joules
|
||
float KineticEnergy = CalculateKineticEnergy(BulletProps, VelocityMPS);
|
||
|
||
// Calculate sectional density
|
||
float SectionalDensity = BulletProps.GetSectionalDensity();
|
||
|
||
// Calculate hardness ratio
|
||
float HardnessRatio = CalculateHardnessRatio(BulletProps.BulletHardness, MaterialProps.MaterialHardness);
|
||
|
||
// Calculate angle factor (normal impact = 1.0, grazing = 0.0)
|
||
float AngleFactor = CalculateAngleFactor(ImpactAngleDegrees);
|
||
|
||
// Calculate shape factor based on bullet type
|
||
float ShapeFactor = CalculateShapeFactorFromBulletType(BulletProps.BulletType);
|
||
|
||
// Modified Taylor-Hopkinson equation for penetration depth
|
||
// P = (K * E * SD * HF * AF * SF) / (ρ * σ)
|
||
// Where: P = penetration depth, K = constant, E = kinetic energy, SD = sectional density
|
||
// HF = hardness factor, AF = angle factor, SF = shape factor, ρ = density, σ = yield strength
|
||
|
||
float Constant = 0.0012f; // Empirical constant
|
||
float Penetration = (Constant * KineticEnergy * SectionalDensity * HardnessRatio * AngleFactor * ShapeFactor) /
|
||
(MaterialProps.DensityGPerCm3 * MaterialProps.YieldStrengthMPa);
|
||
|
||
return FMath::Max(0.0f, Penetration);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateResidualVelocity(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float VelocityMPS,
|
||
float ThicknessCM,
|
||
float ImpactAngleDegrees)
|
||
{
|
||
// Calculate ballistic limit velocity
|
||
float BallisticLimit = CalculateRechtIpsonVelocity(BulletProps, MaterialProps, ThicknessCM);
|
||
|
||
// Adjust for impact angle
|
||
float AngleFactor = CalculateAngleFactor(ImpactAngleDegrees);
|
||
BallisticLimit = BallisticLimit / AngleFactor;
|
||
|
||
// If impact velocity is below ballistic limit, no penetration
|
||
if (VelocityMPS <= BallisticLimit)
|
||
{
|
||
return 0.0f;
|
||
}
|
||
|
||
// Calculate residual velocity using Recht-Ipson equation
|
||
// Vr = sqrt(V^2 - Vbl^2)
|
||
float ResidualVelocity = FMath::Sqrt(VelocityMPS * VelocityMPS - BallisticLimit * BallisticLimit);
|
||
|
||
// Apply energy absorption factor
|
||
ResidualVelocity *= (1.0f - MaterialProps.EnergyAbsorptionCoefficient);
|
||
|
||
return FMath::Max(0.0f, ResidualVelocity);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateRicochetProbability(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float VelocityMPS,
|
||
float ImpactAngleDegrees)
|
||
{
|
||
// Calculate critical ricochet angle
|
||
float CriticalAngle = CalculateCriticalRicochetAngle(BulletProps, MaterialProps, VelocityMPS);
|
||
|
||
// If impact angle is greater than critical angle, no ricochet
|
||
if (ImpactAngleDegrees > CriticalAngle)
|
||
{
|
||
return 0.0f;
|
||
}
|
||
|
||
// Calculate ricochet probability based on angle and material properties
|
||
float AngleRatio = ImpactAngleDegrees / CriticalAngle;
|
||
float BaseRicochetProbability = 1.0f - FMath::Pow(AngleRatio, 2.0f);
|
||
|
||
// Adjust for hardness ratio
|
||
float HardnessRatio = CalculateHardnessRatio(BulletProps.BulletHardness, MaterialProps.MaterialHardness);
|
||
float HardnessAdjustment = FMath::Clamp(HardnessRatio, 0.1f, 2.0f);
|
||
|
||
// Adjust for velocity (higher velocity reduces ricochet probability)
|
||
float VelocityFactor = CalculateVelocityFactor(VelocityMPS, 500.0f);
|
||
|
||
float RicochetProbability = BaseRicochetProbability * HardnessAdjustment * VelocityFactor;
|
||
|
||
return FMath::Clamp(RicochetProbability, 0.0f, 1.0f);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateBulletExpansion(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float VelocityMPS)
|
||
{
|
||
// Convert velocity to FPS for comparison with expansion threshold
|
||
float VelocityFPS = ConvertMPStoFPS(VelocityMPS);
|
||
|
||
// No expansion if velocity is below threshold
|
||
if (VelocityFPS < BulletProps.ExpansionVelocityThreshold)
|
||
{
|
||
return 1.0f; // No expansion
|
||
}
|
||
|
||
// Calculate expansion based on velocity and bullet type
|
||
float VelocityRatio = VelocityFPS / BulletProps.ExpansionVelocityThreshold;
|
||
float ExpansionFactor = 1.0f;
|
||
|
||
switch (BulletProps.BulletType)
|
||
{
|
||
case EBulletType::BT_HollowPoint:
|
||
ExpansionFactor = FMath::Lerp(1.0f, BulletProps.MaxExpansionMultiplier, FMath::Clamp(VelocityRatio - 1.0f, 0.0f, 1.0f));
|
||
break;
|
||
case EBulletType::BT_SoftPoint:
|
||
ExpansionFactor = FMath::Lerp(1.0f, BulletProps.MaxExpansionMultiplier * 0.8f, FMath::Clamp(VelocityRatio - 1.0f, 0.0f, 1.0f));
|
||
break;
|
||
case EBulletType::BT_Frangible:
|
||
ExpansionFactor = FMath::Lerp(1.0f, BulletProps.MaxExpansionMultiplier * 1.2f, FMath::Clamp(VelocityRatio - 1.0f, 0.0f, 1.0f));
|
||
break;
|
||
case EBulletType::BT_FullMetalJacket:
|
||
case EBulletType::BT_ArmorPiercing:
|
||
ExpansionFactor = 1.0f; // No expansion
|
||
break;
|
||
default:
|
||
ExpansionFactor = FMath::Lerp(1.0f, BulletProps.MaxExpansionMultiplier * 0.6f, FMath::Clamp(VelocityRatio - 1.0f, 0.0f, 1.0f));
|
||
break;
|
||
}
|
||
|
||
return ExpansionFactor;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateKineticEnergy(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
float VelocityMPS)
|
||
{
|
||
// KE = 0.5 * m * v^2
|
||
float MassKg = BulletProps.GetMassKg();
|
||
return 0.5f * MassKg * VelocityMPS * VelocityMPS;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateMomentum(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
float VelocityMPS)
|
||
{
|
||
// p = m * v
|
||
float MassKg = BulletProps.GetMassKg();
|
||
return MassKg * VelocityMPS;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateDragCoefficient(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
float MachNumber)
|
||
{
|
||
// Calculate drag coefficient from ballistic coefficient
|
||
// Standard drag coefficient for G1 projectile at given Mach number
|
||
float StandardDrag = 0.5f; // Simplified - normally would use complex curve
|
||
|
||
// Adjust for actual ballistic coefficient
|
||
float BC = BulletProps.UseG7Model ? BulletProps.BallisticCoefficientG7 : BulletProps.BallisticCoefficientG1;
|
||
|
||
// CD = Standard_CD / BC
|
||
return StandardDrag / BC;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateTaylorHopkinsonLimit(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps)
|
||
{
|
||
// Taylor-Hopkinson limit: t = K * ρ * σ / (SD * V^2)
|
||
// Rearranged to solve for limiting thickness
|
||
float SectionalDensity = BulletProps.GetSectionalDensity();
|
||
float Constant = 0.001f; // Empirical constant
|
||
|
||
return Constant * MaterialProps.DensityGPerCm3 * MaterialProps.YieldStrengthMPa / SectionalDensity;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateRechtIpsonVelocity(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float ThicknessCM)
|
||
{
|
||
// Recht-Ipson equation for ballistic limit velocity
|
||
// Vbl = sqrt(k * σ * t / (ρ * A))
|
||
// Where k is a constant, σ is yield strength, t is thickness, ρ is bullet density, A is cross-sectional area
|
||
|
||
float Constant = 2.0f; // Empirical constant
|
||
float CrossSectionArea = BulletProps.GetCrossSectionCm2();
|
||
float BulletDensity = BulletProps.GetMassKg() / (CrossSectionArea * BulletProps.LengthInches * 2.54f); // Rough approximation
|
||
|
||
float BallisticLimit = FMath::Sqrt(Constant * MaterialProps.YieldStrengthMPa * ThicknessCM /
|
||
(BulletDensity * CrossSectionArea));
|
||
|
||
return BallisticLimit;
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateCriticalRicochetAngle(
|
||
const FMathematicalBulletProperties& BulletProps,
|
||
const FMathematicalMaterialProperties& MaterialProps,
|
||
float VelocityMPS)
|
||
{
|
||
// Calculate critical ricochet angle using empirical formula
|
||
// Critical angle decreases with harder bullets and increases with harder targets
|
||
|
||
float HardnessRatio = CalculateHardnessRatio(BulletProps.BulletHardness, MaterialProps.MaterialHardness);
|
||
float VelocityFactor = CalculateVelocityFactor(VelocityMPS, 300.0f);
|
||
|
||
// Base critical angle (degrees)
|
||
float BaseCriticalAngle = 20.0f;
|
||
|
||
// Adjust for hardness and velocity
|
||
float CriticalAngle = BaseCriticalAngle * HardnessRatio * VelocityFactor;
|
||
|
||
return FMath::Clamp(CriticalAngle, 5.0f, 45.0f);
|
||
}
|
||
|
||
// Helper function implementations
|
||
float UEBMathematicalBallistics::CalculateHardnessRatio(float BulletHardness, float MaterialHardness)
|
||
{
|
||
// Hardness ratio affects penetration and ricochet
|
||
return FMath::Max(0.1f, BulletHardness / MaterialHardness);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateVelocityFactor(float Velocity, float ThresholdVelocity)
|
||
{
|
||
// Velocity factor for various calculations
|
||
return FMath::Clamp(Velocity / ThresholdVelocity, 0.1f, 5.0f);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateAngleFactor(float ImpactAngleDegrees)
|
||
{
|
||
// Convert to radians and calculate cosine (normal impact = 1.0, grazing = 0.0)
|
||
float AngleRadians = FMath::DegreesToRadians(ImpactAngleDegrees);
|
||
return FMath::Cos(AngleRadians);
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateShapeFactorFromBulletType(EBulletType BulletType)
|
||
{
|
||
// Shape factor affects penetration efficiency
|
||
switch (BulletType)
|
||
{
|
||
case EBulletType::BT_ArmorPiercing:
|
||
return 1.3f;
|
||
case EBulletType::BT_FullMetalJacket:
|
||
return 1.0f;
|
||
case EBulletType::BT_SoftPoint:
|
||
return 0.9f;
|
||
case EBulletType::BT_HollowPoint:
|
||
return 0.8f;
|
||
case EBulletType::BT_Frangible:
|
||
return 0.6f;
|
||
case EBulletType::BT_Wadcutter:
|
||
return 0.7f;
|
||
case EBulletType::BT_Match:
|
||
return 1.1f;
|
||
default:
|
||
return 1.0f;
|
||
}
|
||
}
|
||
|
||
float UEBMathematicalBallistics::CalculateMaterialFactor(EBulletMaterial BulletMaterial)
|
||
{
|
||
// Material factor affects penetration and expansion
|
||
switch (BulletMaterial)
|
||
{
|
||
case EBulletMaterial::BM_Steel:
|
||
return 1.4f;
|
||
case EBulletMaterial::BM_Tungsten:
|
||
return 1.8f;
|
||
case EBulletMaterial::BM_Brass:
|
||
return 1.2f;
|
||
case EBulletMaterial::BM_Copper:
|
||
return 1.1f;
|
||
case EBulletMaterial::BM_CopperJacket:
|
||
return 1.0f;
|
||
case EBulletMaterial::BM_Lead:
|
||
return 0.8f;
|
||
case EBulletMaterial::BM_LeadAntimony:
|
||
return 0.9f;
|
||
case EBulletMaterial::BM_Bismuth:
|
||
return 0.7f;
|
||
case EBulletMaterial::BM_Zinc:
|
||
return 0.9f;
|
||
default:
|
||
return 1.0f;
|
||
}
|
||
} |