Files
2025-07-04 02:33:40 -07:00

653 lines
20 KiB
C++

// Copyright 2020 Mookie. All Rights Reserved.
#include "EBBullet.h"
#include "EBBarrel.h"
#include "EBUnitConversions.h"
#include "Engine/World.h"
#include "Engine/Engine.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "DrawDebugHelpers.h"
// Initialize static debug counter
int32 AEBBullet::DebugMessageCounter = 0;
void AEBBullet::CreateDebugImpactWidget(FVector Location, bool bRicochet, bool bPenetration, FVector ImpactVelocity, float PenetrationDepth, UPhysicalMaterial* Material)
{
if (!DebugEnabled || !DebugImpact)
{
return;
}
UEBBarrel* Barrel = GetFiringBarrel();
if (!Barrel || !Barrel->DebugImpactInfo)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Add to debug tracking
DebugImpactLocations.Add(Location);
DebugImpactTimes.Add(World->GetTimeSeconds());
// Clean up old debug data
float CurrentTime = World->GetTimeSeconds();
for (int32 i = DebugImpactLocations.Num() - 1; i >= 0; i--)
{
if (CurrentTime - DebugImpactTimes[i] > ImpactDebugTime)
{
DebugImpactLocations.RemoveAt(i);
DebugImpactTimes.RemoveAt(i);
}
}
// Build comprehensive debug information
FString ImpactType;
FLinearColor ImpactColor;
if (bRicochet)
{
ImpactType = TEXT("RICOCHET");
ImpactColor = ImpactColorRicochet;
}
else if (bPenetration)
{
ImpactType = TEXT("PENETRATION");
ImpactColor = ImpactColorPenetration;
}
else
{
ImpactType = TEXT("STOPPED");
ImpactColor = ImpactColorStopped;
}
FString MaterialName = Material ? Material->GetName() : TEXT("Unknown");
float VelocityCMPS = ImpactVelocity.Size();
float VelocityMPS = FEBUnitConversions::CMPSToMPS(VelocityCMPS);
float VelocityKMH = FEBUnitConversions::CMPSToKMH(VelocityCMPS);
float KineticEnergy = FEBUnitConversions::CalculateKineticEnergyJoules(GetEffectiveMass(), VelocityMPS);
FString DebugInfo = FString::Printf(TEXT(
"Impact: %s | Material: %s | Velocity: %.1f m/s (%.1f km/h) | Mass: %.3f kg | Diameter: %.2f cm | Energy: %.1f J | Penetration: %.2f cm"
),
*ImpactType,
*MaterialName,
VelocityMPS,
VelocityKMH,
GetEffectiveMass(),
GetEffectiveDiameter(),
KineticEnergy,
PenetrationDepth
);
// Fix color conversion for display
uint8 R = FMath::Clamp(FMath::RoundToInt(ImpactColor.R * 255.0f), 0, 255);
uint8 G = FMath::Clamp(FMath::RoundToInt(ImpactColor.G * 255.0f), 0, 255);
uint8 B = FMath::Clamp(FMath::RoundToInt(ImpactColor.B * 255.0f), 0, 255);
FColor DisplayColor(R, G, B);
// Display debug information
if (UseOnScreenMessages && GEngine)
{
// Manage message count
if (DebugMessageCounter > MaxOnScreenMessages)
{
DebugMessageCounter = 0;
}
GEngine->AddOnScreenDebugMessage(DebugMessageCounter++, ImpactDebugTime,
DisplayColor, DebugInfo);
}
if (UseWorldSpaceText)
{
DrawDebugString(World, Location + FVector(0, 0, 50), DebugInfo, nullptr,
DisplayColor, ImpactDebugTime, false, WorldTextScale);
}
}
void AEBBullet::DrawTrajectoryDebug(FVector StartLocation, FVector EndLocation, FVector CurrentVelocity, float DeltaTime)
{
if (!DebugEnabled || !DebugTrajectory)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
float CurrentTime = World->GetTimeSeconds();
// Add trajectory point
if (ShowTrail)
{
DebugTrajectoryPoints.Add(StartLocation);
DebugTrajectoryTimes.Add(CurrentTime);
// Clean up old trajectory points
for (int32 i = DebugTrajectoryPoints.Num() - 1; i >= 0; i--)
{
if (CurrentTime - DebugTrajectoryTimes[i] > DebugTrailTime)
{
DebugTrajectoryPoints.RemoveAt(i);
DebugTrajectoryTimes.RemoveAt(i);
}
}
// Draw trajectory trail
if (DebugTrajectoryPoints.Num() > 1)
{
for (int32 i = 1; i < DebugTrajectoryPoints.Num(); i++)
{
float Alpha = FMath::Clamp((CurrentTime - DebugTrajectoryTimes[i]) / DebugTrailTime, 0.0f, 1.0f);
float VelocityNormalized = FMath::Clamp(CurrentVelocity.Size() / 100000.0f, 0.0f, 1.0f); // Normalize to ~1000m/s
FLinearColor TrailColor = FMath::Lerp(DebugTrailColorSlow, DebugTrailColorFast, VelocityNormalized);
TrailColor.A = 1.0f - Alpha;
// Fix color conversion - ensure valid range [0-255]
uint8 R = FMath::Clamp(FMath::RoundToInt(TrailColor.R * 255.0f), 0, 255);
uint8 G = FMath::Clamp(FMath::RoundToInt(TrailColor.G * 255.0f), 0, 255);
uint8 B = FMath::Clamp(FMath::RoundToInt(TrailColor.B * 255.0f), 0, 255);
uint8 A = FMath::Clamp(FMath::RoundToInt(TrailColor.A * 255.0f), 0, 255);
float LineThickness = DebugTrailWidth > 0 ? DebugTrailWidth : 1.0f;
DrawDebugLine(World, DebugTrajectoryPoints[i-1], DebugTrajectoryPoints[i],
FColor(R, G, B, A), false, -1, 0, LineThickness);
}
}
}
// Show velocity vectors
if (ShowVelocityVectors)
{
FVector VelocityVector = CurrentVelocity * VelocityVectorScale;
DrawDebugDirectionalArrow(World, StartLocation, StartLocation + VelocityVector,
50.0f, FColor::Blue, false, DebugTrailTime, 0, 2.0f);
// Add velocity magnitude text
FString VelocityText = FString::Printf(TEXT("%.1f m/s"),
FEBUnitConversions::CMPSToMPS(CurrentVelocity.Size()));
DrawDebugString(World, StartLocation + FVector(0, 0, 30), VelocityText, nullptr,
FColor::Blue, DebugTrailTime, false, WorldTextScale);
}
// Show path prediction
if (ShowPathPrediction && PathPredictionSteps > 0)
{
FVector PredictLocation = StartLocation;
FVector PredictVelocity = CurrentVelocity;
float PredictDeltaTime = DeltaTime * 5.0f; // Predict further ahead
for (int32 i = 0; i < PathPredictionSteps; i++)
{
FVector NextLocation = PredictLocation + PredictVelocity * PredictDeltaTime;
// Apply gravity and drag prediction (simplified)
FVector GravityForce = OverrideGravity ? Gravity : FVector(0, 0, -980);
PredictVelocity += GravityForce * PredictDeltaTime;
DrawDebugLine(World, PredictLocation, NextLocation, FColor::Yellow, false,
DebugTrailTime, 0, 1.0f);
DrawDebugSphere(World, NextLocation, 2.0f, 8, FColor::Yellow, false, DebugTrailTime);
PredictLocation = NextLocation;
}
}
}
void AEBBullet::DrawXRayTrajectory(FVector EntryPoint, FVector ExitPoint, FVector PenetrationDirection, float PenetrationDepth, UPhysicalMaterial* Material)
{
if (!DebugEnabled || !DebugTrajectory || !ShowXRayTrajectory)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Draw entry point marker
uint8 XR = FMath::Clamp(FMath::RoundToInt(XRayTrajectoryColor.R * 255.0f), 0, 255);
uint8 XG = FMath::Clamp(FMath::RoundToInt(XRayTrajectoryColor.G * 255.0f), 0, 255);
uint8 XB = FMath::Clamp(FMath::RoundToInt(XRayTrajectoryColor.B * 255.0f), 0, 255);
uint8 XA = FMath::Clamp(FMath::RoundToInt(XRayTrajectoryColor.A * 255.0f), 0, 255);
FColor XRayColor(XR, XG, XB, XA);
DrawDebugSphere(World, EntryPoint, 3.0f, 8, XRayColor, false, DebugTrailTime);
// Draw internal trajectory path (X-ray view)
FVector InternalTrajectory = EntryPoint + PenetrationDirection * PenetrationDepth;
// Draw dashed line to represent path through material
int32 DashCount = FMath::Max(1, FMath::RoundToInt(PenetrationDepth / 5.0f)); // Dash every 5cm
for (int32 i = 0; i < DashCount; i++)
{
float StartRatio = (float)i / DashCount;
float EndRatio = FMath::Min(1.0f, (float)(i + 0.7f) / DashCount); // 70% dash, 30% gap
FVector DashStart = FMath::Lerp(EntryPoint, InternalTrajectory, StartRatio);
FVector DashEnd = FMath::Lerp(EntryPoint, InternalTrajectory, EndRatio);
DrawDebugLine(World, DashStart, DashEnd, XRayColor, false, DebugTrailTime, 0, 2.0f);
}
// Draw exit point if it exists
if (!ExitPoint.IsNearlyZero() && !EntryPoint.Equals(ExitPoint, 1.0f))
{
uint8 PR = FMath::Clamp(FMath::RoundToInt(PenetrationTrajectoryColor.R * 255.0f), 0, 255);
uint8 PG = FMath::Clamp(FMath::RoundToInt(PenetrationTrajectoryColor.G * 255.0f), 0, 255);
uint8 PB = FMath::Clamp(FMath::RoundToInt(PenetrationTrajectoryColor.B * 255.0f), 0, 255);
uint8 PA = FMath::Clamp(FMath::RoundToInt(PenetrationTrajectoryColor.A * 255.0f), 0, 255);
FColor PenetrationColor(PR, PG, PB, PA);
DrawDebugSphere(World, ExitPoint, 3.0f, 8, PenetrationColor, false, DebugTrailTime);
// Draw penetration path from calculated internal point to actual exit
DrawDebugLine(World, InternalTrajectory, ExitPoint, PenetrationColor, false, DebugTrailTime, 0, 2.0f);
}
// Add material and penetration info
if (Material && UseWorldSpaceText)
{
FString MaterialInfo = FString::Printf(TEXT("Material: %s\nPenetration: %.1f cm\nPath Length: %.1f cm"),
*Material->GetName(),
PenetrationDepth,
FVector::Dist(EntryPoint, ExitPoint));
DrawDebugString(World, EntryPoint + FVector(0, 0, 25), MaterialInfo, nullptr,
XRayColor, DebugTrailTime, false, WorldTextScale);
}
}
void AEBBullet::DrawImpactDebug(FVector ImpactLocation, FVector ImpactVelocity, FVector ImpactNormal, bool bRicochet, bool bPenetration, float PenetrationDepth, UPhysicalMaterial* Material)
{
if (!DebugEnabled || !DebugImpact)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Choose color based on impact type
FLinearColor ImpactColor;
if (bRicochet)
{
ImpactColor = ImpactColorRicochet;
}
else if (bPenetration)
{
ImpactColor = ImpactColorPenetration;
}
else
{
ImpactColor = ImpactColorStopped;
}
// Fix color conversion
uint8 R = FMath::Clamp(FMath::RoundToInt(ImpactColor.R * 255.0f), 0, 255);
uint8 G = FMath::Clamp(FMath::RoundToInt(ImpactColor.G * 255.0f), 0, 255);
uint8 B = FMath::Clamp(FMath::RoundToInt(ImpactColor.B * 255.0f), 0, 255);
FColor DebugColor(R, G, B);
// Show impact points
if (ShowImpactPoints)
{
DrawDebugSphere(World, ImpactLocation, ImpactPointSize, 12, DebugColor, false, ImpactDebugTime);
}
// Show impact normals
if (ShowImpactNormals)
{
DrawDebugDirectionalArrow(World, ImpactLocation, ImpactLocation + ImpactNormal * 50.0f,
20.0f, DebugColor, false, ImpactDebugTime, 0, 2.0f);
}
// Show penetration depth
if (ShowPenetrationDepth && bPenetration)
{
FVector PenetrationVector = -ImpactNormal * PenetrationDepth;
DrawDebugLine(World, ImpactLocation, ImpactLocation + PenetrationVector,
FColor::Green, false, ImpactDebugTime, 0, 3.0f);
FString PenetrationText = FString::Printf(TEXT("Pen: %.1f cm"), PenetrationDepth);
DrawDebugString(World, ImpactLocation + PenetrationVector + FVector(0, 0, 20),
PenetrationText, nullptr, FColor::Green, ImpactDebugTime, false, WorldTextScale);
}
// Show ricochet angles
if (ShowRicochetAngles && bRicochet)
{
FVector ReflectedVelocity = ImpactVelocity - 2 * FVector::DotProduct(ImpactVelocity, ImpactNormal) * ImpactNormal;
DrawDebugDirectionalArrow(World, ImpactLocation, ImpactLocation + ReflectedVelocity.GetSafeNormal() * 75.0f,
30.0f, FColor::Yellow, false, ImpactDebugTime, 0, 2.0f);
float RicochetAngle = FMath::Acos(FVector::DotProduct(-ImpactVelocity.GetSafeNormal(), ImpactNormal));
FString AngleText = FString::Printf(TEXT("Angle: %.1f°"), FMath::RadiansToDegrees(RicochetAngle));
DrawDebugString(World, ImpactLocation + FVector(0, 0, 40), AngleText, nullptr,
FColor::Yellow, ImpactDebugTime, false, WorldTextScale);
}
// Show material information
if (ShowMaterialInfo && Material)
{
FString MaterialInfo = FString::Printf(TEXT("Material: %s"), *Material->GetName());
DrawDebugString(World, ImpactLocation + FVector(0, 0, 60), MaterialInfo, nullptr,
DebugColor, ImpactDebugTime, false, WorldTextScale);
}
}
void AEBBullet::DrawPhysicsDebug(FVector Location, FVector DragForce, FVector GravityForce, FVector WindForce, float AirDensity, float SpeedOfSound)
{
if (!DebugEnabled || !DebugPhysics)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Show drag forces
if (ShowDragForces && !DragForce.IsNearlyZero())
{
FVector DragVector = DragForce * ForceVectorScale;
DrawDebugDirectionalArrow(World, Location, Location + DragVector,
10.0f, FColor::Red, false, PhysicsDebugTime, 0, 1.5f);
FString DragText = FString::Printf(TEXT("Drag: %.1f N"), DragForce.Size());
DrawDebugString(World, Location + DragVector + FVector(0, 0, 10), DragText, nullptr,
FColor::Red, PhysicsDebugTime, false, WorldTextScale);
}
// Show gravity effect
if (ShowGravityEffect && !GravityForce.IsNearlyZero())
{
FVector GravityVector = GravityForce * ForceVectorScale;
DrawDebugDirectionalArrow(World, Location, Location + GravityVector,
10.0f, FColor::Purple, false, PhysicsDebugTime, 0, 1.5f);
FString GravityText = FString::Printf(TEXT("Gravity: %.1f N"), GravityForce.Size());
DrawDebugString(World, Location + GravityVector + FVector(0, 0, 10), GravityText, nullptr,
FColor::Purple, PhysicsDebugTime, false, WorldTextScale);
}
// Show wind effect
if (ShowWindEffect && !WindForce.IsNearlyZero())
{
FVector WindVector = WindForce * ForceVectorScale;
DrawDebugDirectionalArrow(World, Location, Location + WindVector,
10.0f, FColor::Cyan, false, PhysicsDebugTime, 0, 1.5f);
FString WindText = FString::Printf(TEXT("Wind: %.1f N"), WindForce.Size());
DrawDebugString(World, Location + WindVector + FVector(0, 0, 10), WindText, nullptr,
FColor::Cyan, PhysicsDebugTime, false, WorldTextScale);
}
// Show atmospheric data
if (ShowAtmosphericData)
{
FString AtmosphericInfo = FString::Printf(TEXT(
"Air Density: %.3f kg/m³\nSpeed of Sound: %.1f m/s\nAltitude: %.1f m"
),
AirDensity,
FEBUnitConversions::CMPSToMPS(SpeedOfSound),
FEBUnitConversions::CMToM(GetAltitude(World, Location))
);
DrawDebugString(World, Location + FVector(0, 0, 80), AtmosphericInfo, nullptr,
FColor::White, PhysicsDebugTime, false, WorldTextScale);
}
}
void AEBBullet::DrawBallisticsDebug(FVector Location, FVector BulletVelocity, float KineticEnergy, float DragCoefficient, float MachNumber)
{
if (!DebugEnabled || !DebugBallistics)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Show energy calculations
if (ShowEnergyCalculations)
{
FString EnergyInfo = FString::Printf(TEXT(
"Kinetic Energy: %.1f J\nMomentum: %.3f kg⋅m/s\nPower: %.1f W"
),
KineticEnergy,
GetEffectiveMass() * FEBUnitConversions::CMPSToMPS(BulletVelocity.Size()),
KineticEnergy * BulletVelocity.Size() / 100.0f // Simplified power calculation
);
DrawDebugString(World, Location + FVector(0, 0, 100), EnergyInfo, nullptr,
FColor::Orange, BallisticsDebugTime, false, WorldTextScale);
}
// Show mathematical comparison
if (ShowMathematicalComparison && UseMathematicalPhysics)
{
FString MathInfo = FString::Printf(TEXT(
"Mathematical Mode: ON\nDrag Coefficient: %.3f\nMach Number: %.2f"
),
DragCoefficient,
MachNumber
);
DrawDebugString(World, Location + FVector(0, 0, 120), MathInfo, nullptr,
FColor::Green, BallisticsDebugTime, false, WorldTextScale);
}
// Show ballistic coefficient
if (ShowBallisticCoefficient)
{
float BallisticCoefficient = GetEffectiveMass() / (DragCoefficient * GetEffectiveDiameter() * GetEffectiveDiameter());
FString BCInfo = FString::Printf(TEXT("Ballistic Coefficient: %.3f"), BallisticCoefficient);
DrawDebugString(World, Location + FVector(0, 0, 140), BCInfo, nullptr,
FColor::Magenta, BallisticsDebugTime, false, WorldTextScale);
}
}
void AEBBullet::DrawSpallDebug(FVector ImpactLocation, FVector ImpactVelocity, FVector ImpactNormal, TArray<FVector> FragmentVelocities)
{
if (!DebugEnabled || !DebugSpalling)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
// Fix color conversion for spall colors
uint8 PR = FMath::Clamp(FMath::RoundToInt(SpallColorPrimary.R * 255.0f), 0, 255);
uint8 PG = FMath::Clamp(FMath::RoundToInt(SpallColorPrimary.G * 255.0f), 0, 255);
uint8 PB = FMath::Clamp(FMath::RoundToInt(SpallColorPrimary.B * 255.0f), 0, 255);
FColor PrimaryColor(PR, PG, PB);
uint8 FR = FMath::Clamp(FMath::RoundToInt(SpallColorFragment.R * 255.0f), 0, 255);
uint8 FG = FMath::Clamp(FMath::RoundToInt(SpallColorFragment.G * 255.0f), 0, 255);
uint8 FB = FMath::Clamp(FMath::RoundToInt(SpallColorFragment.B * 255.0f), 0, 255);
FColor FragmentColor(FR, FG, FB);
// Show spall generation
if (ShowSpallGeneration)
{
DrawDebugSphere(World, ImpactLocation, 8.0f, 16, PrimaryColor, false, SpallDebugTime);
FString SpallInfo = FString::Printf(TEXT("Spall Generated: %d fragments"), FragmentVelocities.Num());
DrawDebugString(World, ImpactLocation + FVector(0, 0, 70), SpallInfo, nullptr,
PrimaryColor, SpallDebugTime, false, WorldTextScale);
}
// Show fragment trajectories
if (ShowFragmentTrajectories)
{
for (int32 i = 0; i < FragmentVelocities.Num(); i++)
{
FVector FragmentDirection = FragmentVelocities[i].GetSafeNormal();
FVector FragmentEndpoint = ImpactLocation + FragmentDirection * 100.0f;
DrawDebugDirectionalArrow(World, ImpactLocation, FragmentEndpoint,
15.0f, FragmentColor, false, SpallDebugTime, 0, 1.0f);
}
}
// Show spall cones
if (ShowSpallCones && FragmentVelocities.Num() > 0)
{
// Calculate cone parameters
FVector ConeDirection = ImpactNormal;
float ConeAngle = FMath::DegreesToRadians(45.0f); // 45 degree cone
float ConeLength = 80.0f;
// Draw cone outline
int32 ConeSides = 8;
TArray<FVector> ConePoints;
for (int32 i = 0; i < ConeSides; i++)
{
float Angle = (2.0f * PI * i) / ConeSides;
FVector RadialDirection = FVector(FMath::Cos(Angle), FMath::Sin(Angle), 0);
FVector ConePoint = ImpactLocation + ConeDirection * ConeLength + RadialDirection * FMath::Tan(ConeAngle) * ConeLength;
ConePoints.Add(ConePoint);
// Draw lines from impact point to cone edge
DrawDebugLine(World, ImpactLocation, ConePoint, PrimaryColor, false, SpallDebugTime, 0, 0.5f);
}
// Draw cone base
for (int32 i = 0; i < ConeSides; i++)
{
int32 NextIndex = (i + 1) % ConeSides;
DrawDebugLine(World, ConePoints[i], ConePoints[NextIndex], PrimaryColor, false, SpallDebugTime, 0, 0.5f);
}
}
}
void AEBBullet::DrawPerformanceDebug(FVector Location, int32 TraceCount, float FrameTime, int32 PooledBullets)
{
if (!DebugEnabled || !DebugPerformance)
{
return;
}
UWorld* World = GetWorld();
if (!World)
{
return;
}
FString PerformanceInfo;
// Show trace count
if (ShowTraceCount)
{
PerformanceInfo += FString::Printf(TEXT("Traces: %d\n"), TraceCount);
}
// Show frame timing
if (ShowFrameTiming)
{
float FPS = FrameTime > 0 ? 1.0f / FrameTime : 0.0f;
PerformanceInfo += FString::Printf(TEXT("Frame Time: %.2f ms (%.1f FPS)\n"), FrameTime * 1000.0f, FPS);
}
// Show pooling stats
if (ShowPoolingStats && EnablePooling)
{
PerformanceInfo += FString::Printf(TEXT("Pooled Bullets: %d/%d\n"), PooledBullets, MaxPoolSize);
}
if (!PerformanceInfo.IsEmpty())
{
DrawDebugString(World, Location + FVector(0, 0, 160), PerformanceInfo, nullptr,
FColor::Cyan, PerformanceDebugTime, false, WorldTextScale);
}
}
void AEBBullet::ClearDebugDisplay()
{
DebugTrajectoryPoints.Empty();
DebugTrajectoryTimes.Empty();
DebugImpactLocations.Empty();
DebugImpactTimes.Empty();
DebugTraceCounter = 0;
DebugMessageCounter = 0;
}
void AEBBullet::ToggleDebugCategory(const FString& CategoryName, bool bEnabled)
{
if (CategoryName == TEXT("Trajectory"))
{
DebugTrajectory = bEnabled;
}
else if (CategoryName == TEXT("Impact"))
{
DebugImpact = bEnabled;
}
else if (CategoryName == TEXT("Physics"))
{
DebugPhysics = bEnabled;
}
else if (CategoryName == TEXT("Performance"))
{
DebugPerformance = bEnabled;
}
else if (CategoryName == TEXT("Ballistics"))
{
DebugBallistics = bEnabled;
}
else if (CategoryName == TEXT("Spalling"))
{
DebugSpalling = bEnabled;
}
else if (CategoryName == TEXT("Pooling"))
{
DebugPooling = bEnabled;
}
}
FString AEBBullet::GetDebugInfoString() const
{
return FString::Printf(TEXT(
"Bullet Debug Info:\n"
"Enabled: %s\n"
"Trajectory: %s | Impact: %s | Physics: %s\n"
"Performance: %s | Ballistics: %s | Spalling: %s\n"
"Trace Count: %d | Active Trajectory Points: %d\n"
"Active Impact Points: %d"
),
DebugEnabled ? TEXT("Yes") : TEXT("No"),
DebugTrajectory ? TEXT("On") : TEXT("Off"),
DebugImpact ? TEXT("On") : TEXT("Off"),
DebugPhysics ? TEXT("On") : TEXT("Off"),
DebugPerformance ? TEXT("On") : TEXT("Off"),
DebugBallistics ? TEXT("On") : TEXT("Off"),
DebugSpalling ? TEXT("On") : TEXT("Off"),
DebugTraceCounter,
DebugTrajectoryPoints.Num(),
DebugImpactLocations.Num()
);
}