feat(flight): enhance flight model with transonic drag and vortex lift features

This commit is contained in:
richard committed 2026-10-28 23:14:37 +01:00
1 parent 599ecae21f
commit 44a5f7f9bc
12 files changed
+177 -26

No files matched your search

@@ -1,5 +1,7 @@
package dev.lumareth.improvedAerodynamics.flight;
import dev.lumareth.improvedAerodynamics.Config;
/**
* Flight model based on each surface's angle of attack, used instead of {@link WariumAero} when enabled.
* <p>
@@ -41,6 +43,18 @@ final class ImprovedAero {
*/
static final double LIFT_CONSTANT = 160.0 / (REFERENCE_SPEED * LIFT_SLOPE * REFERENCE_ANGLE);
/** The config values the model reads, taken once per evaluation. */
record Tuning(double liftScale, double stallAngleScale, double thinDrag,
boolean transonic, double speedOfSound, double transonicPeakDrag, double supersonicDrag,
boolean vortex, double vortexStallBonus, double vortexLiftStrength) {
static Tuning read() {
return new Tuning(Config.LIFT_SCALE.get(), Config.STALL_ANGLE_SCALE.get(), Config.STABILIZER_DRAG.get(),
Config.TRANSONIC_DRAG.get(), Config.SPEED_OF_SOUND.get(), Config.TRANSONIC_PEAK_DRAG.get(),
Config.SUPERSONIC_DRAG.get(),
Config.VORTEX_LIFT.get(), Config.VORTEX_STALL_BONUS.get(), Config.VORTEX_LIFT_STRENGTH.get());
}
}
/**
* Adds the surface's forces to out.
*
@@ -49,12 +63,10 @@ final class ImprovedAero {
* @param r block center relative to the center of mass, ship axes
* @param input control input of the surface, -1 to 1
* @param stalled whether the surface was stalled in the previous substep
* @param liftScale multiplier for the lift only, not the drag
* @param thinDrag parasitic drag coefficient of stabilizers and vertical wings
* @return whether the surface is stalled now
*/
static boolean evaluate(AeroSurface s, double[] v, double[] w, double[] r, double input, double weight,
double liftConstant, double liftScale, double stallAngleScale, double thinDrag, boolean stalled, Wrench out) {
Tuning tuning, boolean stalled, Wrench out) {
// Airflow at the block: ship velocity plus w x r
double vx = v[0] + w[1] * r[2] - w[2] * r[1];
double vy = v[1] + w[2] * r[0] - w[0] * r[2];
@@ -71,8 +83,13 @@ final class ImprovedAero {
return false;
}
// Warium's own per-block drag, lower for thin surfaces since they stabilize with lift, not drag
double parasitic = -localSpeed * (s.type().thin ? thinDrag : s.type().parasiticDrag) * p / m;
// Warium's own per-block drag, lower for thin surfaces since they stabilize with lift, not drag. Shock waves
// multiply it near the speed of sound; this is the only zero-lift drag a craft has, induced and stall drag
// below are not affected
double shockDrag = tuning.transonic()
? TransonicDrag.multiplier(localSpeed / tuning.speedOfSound(), tuning.transonicPeakDrag(), tuning.supersonicDrag())
: 1.0;
double parasitic = -localSpeed * (s.type().thin ? tuning.thinDrag() : s.type().parasiticDrag) * shockDrag * p / m;
out.addForce(r, vx * parasitic, vy * parasitic, vz * parasitic);
// Chord c along the facing, normal n up for horizontal surfaces and to the side for vertical ones
@@ -99,7 +116,13 @@ final class ImprovedAero {
}
alpha = Math.max(-Math.PI / 2.0, Math.min(Math.PI / 2.0, alpha));
double critical = Math.toRadians(s.slab() ? SurfaceType.SLAB_STALL_ANGLE : s.type().stallAngle) * stallAngleScale;
// Low aspect ratio wings keep their flow attached longer thanks to leading-edge vortices. Control surfaces
// in a wing's group stall with the wing instead of before it
double vortexFactor = tuning.vortex() && (s.type() == SurfaceType.WING || s.wingGroup() && s.type().controllable)
? VortexLift.factor(s.aspectRatio()) : 0.0;
double baseStall = s.slab() ? SurfaceType.SLAB_STALL_ANGLE
: s.wingGroup() && s.type().controllable ? SurfaceType.WING.stallAngle : s.type().stallAngle;
double critical = Math.toRadians(baseStall) * (1.0 + tuning.vortexStallBonus() * vortexFactor) * tuning.stallAngleScale();
double absAlpha = Math.abs(alpha);
// Flips only where both blends agree, so the hysteresis never makes the force jump
if (!stalled && absAlpha > critical + STALL_BLEND) {
@@ -115,11 +138,14 @@ final class ImprovedAero {
double slope = aspectRatio > 0.0 ? 2.0 * Math.PI / (1.0 + 2.0 / aspectRatio) : LIFT_SLOPE;
double inducedDrag = aspectRatio > 0.0 ? 1.0 / (Math.PI * EFFICIENCY * aspectRatio) : INDUCED_DRAG;
double attachedLift = slope * alpha;
if (s.type() == SurfaceType.WING) {
attachedLift += VortexLift.liftCoefficient(alpha, vortexFactor, tuning.vortexLiftStrength());
}
double lift = attached * attachedLift + (1.0 - attached) * SEPARATED_LIFT * Math.sin(2.0 * alpha);
double drag = attached * inducedDrag * attachedLift * attachedLift
+ (1.0 - attached) * SEPARATED_DRAG * Math.sin(alpha) * Math.sin(alpha);
double q = liftConstant * p * fd * planeSpeedSq;
double q = LIFT_CONSTANT * p * fd * planeSpeedSq;
// Lift is perpendicular to the airflow in the chord/normal plane, on the side of n
double side = u < 0.0 ? -1.0 : 1.0;
double lx = (Math.abs(u) * nx - side * vn * cx) / planeSpeed;
@@ -129,13 +155,13 @@ final class ImprovedAero {
double dx = -(u * cx + vn * nx) / planeSpeed;
double dy = -(vn * ny) / planeSpeed;
double dz = -(u * cz + vn * nz) / planeSpeed;
double liftForce = q * lift * liftScale;
double liftForce = q * lift * tuning.liftScale();
double dragForce = q * drag / m;
out.addForce(r, lx * liftForce + dx * dragForce, ly * liftForce + dy * dragForce, lz * liftForce + dz * dragForce);
return stalled;
}
private static double smoothstep(double edge0, double edge1, double x) {
static double smoothstep(double edge0, double edge1, double x) {
double t = Math.max(0.0, Math.min(1.0, (x - edge0) / (edge1 - edge0)));
return t * t * (3.0 - 2.0 * t);
}