Files
BallisticsDocs/Source/EasyBallistics/Private/EBMathematicalBallistics.cpp
T
2025-07-02 22:40:58 -07:00

296 lines
9.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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;
}
}