feat(flight): add aspect ratio lift and altitude-hold trim

- Derive lift slope and induced drag from each wing's aspect ratio, found by grouping connected wing blocks across short span gaps.
- Give stabilizers and vertical wings their own parasitic drag from the stabilizerDrag config.
- Replace designSpeed with a fixed lift constant and a liftScale multiplier.
- Make auto trim hold the altitude the craft has when the pilot releases pitch, using nested altitude, climb and attitude loops.
- Add /improvedaerodynamics (alias /aero) to list, read and set the main config values in game.
This commit is contained in:
richard committed 2026-10-28 22:06:50 +01:00
1 parent 4b7fb0e1e8
commit 599ecae21f
10 files changed
+329 -48

No files matched your search

@@ -57,6 +57,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
// Torque (ship axes) one unit of pitch input on this node's pitch surfaces makes, measured every substep
final Vector3d pitchResponse = new Vector3d();
final Vector3d pitchResponseBuilding = new Vector3d();
final AutoTrim.Targets trimTargets = new AutoTrim.Targets();
volatile double savedTrim;
NodeState(NodeData data) {
@@ -102,7 +103,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
@Override
public void publish(long tick) {
published = new Frame(tick, buildingSurfaces.values().toArray(new AeroSurface[0]), buildingNodes);
published = new Frame(tick, WingShape.withAspectRatios(buildingSurfaces.values()), buildingNodes);
buildingSurfaces = new Long2ObjectOpenHashMap<>();
buildingNodes = new Long2ObjectOpenHashMap<>();
}
@@ -131,7 +132,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
boolean improved = Config.IMPROVED_FLIGHT_MODEL.get();
Matrix3dc inertia = ship.getMomentOfInertia();
updateNodes(frame, worldVelocity, rotation, inertia, w, dt, improved);
updateNodes(frame, worldVelocity, transform.getPositionInWorld().y(), rotation, inertia, w, dt, improved);
evaluate(frame, centerOfMass, v, w, improved, true, wrench);
if (Config.IMPLICIT_DAMPING.get()) {
@@ -204,8 +205,10 @@ public final class AeroState implements PhysicsBridge.Publishable {
private void evaluate(Frame frame, Vector3dc centerOfMass, double[] v, double[] w, boolean improved, boolean commit, Wrench out) {
out.clear();
double weight = clock.weight();
double liftConstant = ImprovedAero.liftConstant(Config.DESIGN_SPEED.get());
double liftConstant = ImprovedAero.LIFT_CONSTANT;
double stallAngleScale = Config.STALL_ANGLE_SCALE.get();
double liftScale = Config.LIFT_SCALE.get();
double thinDrag = Config.STABILIZER_DRAG.get();
double[] r = new double[3];
if (commit) {
wingStalled = false;
@@ -224,7 +227,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
double input = s.type().controllable ? input(s, node, state, 0.0) : 0.0;
if (improved) {
boolean nowStalled = ImprovedAero.evaluate(s, v, w, r, input, weight, liftConstant, stallAngleScale, stalled[i], out);
boolean nowStalled = ImprovedAero.evaluate(s, v, w, r, input, weight, liftConstant, liftScale, stallAngleScale, thinDrag, stalled[i], out);
if (commit) {
stalled[i] = nowStalled;
wingStalled |= nowStalled && s.type() == SurfaceType.WING;
@@ -239,7 +242,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
probe.clear();
double base = 0.0;
if (improved) {
ImprovedAero.evaluate(s, v, w, r, input, weight, liftConstant, stallAngleScale, stalled[i], probe);
ImprovedAero.evaluate(s, v, w, r, input, weight, liftConstant, liftScale, stallAngleScale, thinDrag, stalled[i], probe);
} else {
WariumAero.evaluate(s, v, w, r, input, weight, probe);
}
@@ -247,7 +250,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
probe.clear();
double nudged = input(s, node, state, delta);
if (improved) {
ImprovedAero.evaluate(s, v, w, r, nudged, weight, liftConstant, stallAngleScale, stalled[i], probe);
ImprovedAero.evaluate(s, v, w, r, nudged, weight, liftConstant, liftScale, stallAngleScale, thinDrag, stalled[i], probe);
} else {
WariumAero.evaluate(s, v, w, r, nudged, weight, probe);
}
@@ -285,7 +288,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
};
}
private void updateNodes(Frame frame, Vector3dc worldVelocity, Quaterniondc rotation, Matrix3dc inertia,
private void updateNodes(Frame frame, Vector3dc worldVelocity, double altitude, Quaterniondc rotation, Matrix3dc inertia,
double[] w, double dt, boolean improved) {
double step = Config.CONTROL_SMOOTHING.get() ? INPUT_RATE_PER_TICK * clock.weight() : Double.POSITIVE_INFINITY;
for (NodeData node : frame.nodes.values()) {
@@ -296,7 +299,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
state.pitch = moveTowards(state.pitch, node.pitch(), step);
state.yaw = moveTowards(state.yaw, node.yaw(), step);
state.roll = moveTowards(state.roll, node.roll(), step);
AutoTrim.update(state, node, worldVelocity, rotation, inertia, w, dt, improved && wingStalled);
AutoTrim.update(state, node, worldVelocity, altitude, rotation, inertia, w, dt, improved && wingStalled);
}
}