Gravity: one potential per zone owns acceleration and height
GravityField is the law of one well (logarithmic tail, softened core). Zone.GetForce and GetHeight sum it over the bodies; GetNormal derives from the force for ship tilt; Flow and Ship read GetForce. Well strength, core radius and Roche limit are PlanetSettings curves of mass, height exaggeration K one global. Deletes GravityAcceleration, GravityReferenceRate, GravityStrength, the PowerPulse well, GetNormal's finite differences and BodyData.GravityDepthExponent. Settings.asset carries the calibrated curves. Tests: PotentialOwnerTests, the moon's moon placement, retired keys over every BodyData type, a body-derived giant sweep. Not yet built or run (verify job follows); this commit may not build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ff63362f45
commit
021c4cbcbf
16 changed files with 433 additions and 171 deletions
|
|
@ -617,17 +617,12 @@ public class Zone
|
|||
var result = 0f;
|
||||
foreach (var body in PlanetInstances.Values)
|
||||
{
|
||||
var p = position - body.Orbit.Position; //GetOrbitPosition(body.BodyData.Orbit)
|
||||
var distSqr = lengthsq(p);
|
||||
var gravityRadius = body.GravityWellRadius;
|
||||
if (distSqr < gravityRadius*gravityRadius)
|
||||
{
|
||||
var depth = body.GravityWellDepth;
|
||||
result -= PowerPulse(sqrt(distSqr) / gravityRadius, body.BodyData.GravityDepthExponent) * depth;
|
||||
}
|
||||
var offset = position - body.Orbit.Position;
|
||||
result += GravityField.Potential(body.WellStrength, body.CoreRadius, length(offset))
|
||||
- GravityField.Potential(body.WellStrength, body.CoreRadius, Pack.Radius);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result / Settings.HeightExaggeration;
|
||||
}
|
||||
|
||||
public float GetLight(float2 position)
|
||||
|
|
@ -647,33 +642,21 @@ public class Zone
|
|||
internal float SchematicCellSize => _itemManager.GameplaySettings.SchematicCellSize;
|
||||
internal float FieldCrossSection(AsteroidBeltData belt) => _itemManager.ItemData.Get(belt.Kind)?.CrossSection ?? 0f;
|
||||
|
||||
// The one law of how a gravity well pushes anything: ships, floating items and mines all read it. The shape is
|
||||
// the one ships have always felt, normalize(f) * strength * (1 / (1 - |f|^2) - 1), gated off below |f|^2 = .001.
|
||||
// It is an acceleration, per second: the legacy law added that amount once per 60 fps frame with no dt, so
|
||||
// GravityReferenceRate (60) restates it and `acceleration * dt` gives the legacy push at 60 fps.
|
||||
public const float GravityReferenceRate = 60f;
|
||||
|
||||
public static float2 GravityAcceleration(float2 normalXz, float strength)
|
||||
{
|
||||
var magnitude = lengthsq(normalXz);
|
||||
if (magnitude <= .001f) return float2.zero;
|
||||
return normalize(normalXz) * strength * (1 / (1 - magnitude) - 1) * GravityReferenceRate;
|
||||
}
|
||||
|
||||
// The acceleration the well exerts at `position`.
|
||||
// The one source of gravitational acceleration (u/s^2): the sum of every body's well (GravityField). Ships,
|
||||
// floating items, clouds and mines all read it, and so does Flow. It is the gradient of the potential GetHeight
|
||||
// draws, so the two cannot disagree.
|
||||
public float2 GetForce(float2 position)
|
||||
{
|
||||
var normal = GetNormal(position);
|
||||
return GravityAcceleration(new float2(normal.x, normal.z), Settings.GravityStrength);
|
||||
var force = float2.zero;
|
||||
foreach (var body in PlanetInstances.Values)
|
||||
force += GravityField.Acceleration(body.WellStrength, body.CoreRadius, position - body.Orbit.Position);
|
||||
return force;
|
||||
}
|
||||
|
||||
// The global flow velocity at `position` in zone time, in u/s (FlowField is the law). The slope term reads the
|
||||
// downhill gradient, which GetNormal's xz is proportional to.
|
||||
public float3 Flow(float3 position)
|
||||
{
|
||||
var normal = GetNormal(position.xz);
|
||||
return FlowField.At(Settings.Flow, position, FlowScroll, new float2(normal.x, normal.z));
|
||||
}
|
||||
// downhill direction, which is the force.
|
||||
public float3 Flow(float3 position) =>
|
||||
FlowField.At(Settings.Flow, position, FlowScroll, GetForce(position.xz));
|
||||
|
||||
// The flow's noise scroll at zone time (Zone.Flow evaluates at exactly this); lowered to the shaders' _FlowScroll.
|
||||
public float2 FlowScroll => FlowField.Scroll(Settings.Flow, _time);
|
||||
|
|
@ -688,16 +671,12 @@ public class Zone
|
|||
return pow((x + 1) * (1 - x), exponent);
|
||||
}
|
||||
|
||||
public float3 GetNormal(float2 pos, float step = .1f, float mul = 1)
|
||||
// The surface normal of the drawn height (potential / K), derived from the force. Display only (ship tilt): the
|
||||
// sim never reads it as a force.
|
||||
public float3 GetNormal(float2 pos)
|
||||
{
|
||||
float hL = GetHeight(new float2(pos.x - step, pos.y)) * mul;
|
||||
float hR = GetHeight(new float2(pos.x + step, pos.y)) * mul;
|
||||
float hD = GetHeight(new float2(pos.x, pos.y - step)) * mul;
|
||||
float hU = GetHeight(new float2(pos.x, pos.y + step)) * mul;
|
||||
|
||||
// Deduce terrain normal
|
||||
float3 normal = new float3((hL - hR), (hD - hU), step*2);
|
||||
return normalize(normal).xzy;
|
||||
var slope = GetForce(pos) / Settings.HeightExaggeration;
|
||||
return normalize(new float3(slope.x, 1f, slope.y));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -706,10 +685,17 @@ public class Planet
|
|||
public Orbit Orbit;
|
||||
protected readonly PlanetSettings Settings;
|
||||
public BodyData BodyData;
|
||||
// Brush inputs only (ZoneRenderer's Gravity Well brushes and planet icon); no sim reader. grid-lowering deletes them.
|
||||
public float GravityWellDepth;
|
||||
public float GravityWellRadius;
|
||||
public float BodyRadius;
|
||||
|
||||
// The well (GravityField): strength c and softened core radius R by mass, and the Roche limit, the closest a child
|
||||
// may orbit. All three are authored PlanetSettings curves of mass; the core must sit inside the Roche limit.
|
||||
public float WellStrength;
|
||||
public float CoreRadius;
|
||||
public float RocheLimit;
|
||||
|
||||
public Planet(PlanetSettings settings, BodyData data, Orbit orbit)
|
||||
{
|
||||
Settings = settings;
|
||||
|
|
@ -723,6 +709,11 @@ public class Planet
|
|||
BodyRadius = Settings.BodyRadius.Evaluate(BodyData.Mass) * BodyData.BodyRadiusMultiplier;
|
||||
GravityWellRadius = Settings.GravityRadius.Evaluate(BodyData.Mass) * BodyData.GravityRadiusMultiplier;
|
||||
GravityWellDepth = Settings.GravityDepth.Evaluate(BodyData.Mass) * BodyData.GravityDepthMultiplier;
|
||||
WellStrength = Settings.WellStrength.Evaluate(BodyData.Mass);
|
||||
CoreRadius = Settings.CoreRadius.Evaluate(BodyData.Mass);
|
||||
RocheLimit = Settings.RocheLimit.Evaluate(BodyData.Mass);
|
||||
if (WellStrength > 0 && !(0 < CoreRadius && CoreRadius < RocheLimit))
|
||||
throw new InvalidOperationException("A body's core radius must be positive and inside its Roche limit (PlanetSettings.CoreRadius, RocheLimit).");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue