Browse Source

Added physical balloon (sphere with collider that expand when filled with gas, receive buoyancy force, and is linked to the vessel with a spring)

master
Youen Toupin 9 years ago
parent
commit
0306486fb0
  1. 102
      Plugin/ModuleAerostat.cs
  2. BIN
      doc/references/balloon_almost_empty.jpg
  3. BIN
      doc/references/balloon_full.jpg

102
Plugin/ModuleAerostat.cs

@ -16,6 +16,8 @@ namespace Aerostats
// In this class, amount of gas Q is specified in m^3 at stp (standard pressure of 100kPa and temperature of 0°C = 273K). Ideal gas equation P.V = n.R.T gives us the equivalent number of moles: n = P.V/R/T = 100000.Q/8.314/273 = 44.Q mol
private static readonly float R = 8.314f;
private static readonly float ZeroCelsius = 273.15f;
private static readonly float StandardPressure = 100000.0f;
private static readonly float StpVolumeToMoles = 100000.0f / R / 273.15f;
/// <summary>
@ -60,9 +62,34 @@ namespace Aerostats
/// If the balloon can not store the target amount of gas (because the maximum volume has been reached), the system won't try to inject more gas, to avoid venting through the security valve of the balloon.
/// </summary>
[KSPField(isPersistant = true, guiActive = true)]
[UI_FloatRange(minValue = 0.0f, maxValue = 20000.0f, stepIncrement = 1.0f)]
[UI_FloatRange(minValue = 0, maxValue = 20000.0f, stepIncrement = 1.0f)]
public float LiftingGasTargetQuantity;
/// <summary>
/// Mass of the empty balloon, in kg
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float BalloonEmptyMass = 50.0f;
/// <summary>
/// Minimum amount of gas that will be use to inflate the balloon at the beginning.
/// This is needed if you want the balloon to lift itself right after staging.
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float MinimumFillQuantity = 75.0f;
/// <summary>
/// Drag coefficient of the balloon
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float BalloonDragCoeff = 0.3f;
/// <summary>
/// Force applied by the spring between the balloon and the vessel, proportional to spring extension, in newton per meter
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float SpringHardness = 20000.0f;
private bool Staged;
/// <summary>
@ -76,6 +103,8 @@ namespace Aerostats
/// </summary>
private float Inflation = 0;
private GameObject Balloon;
private void PlayAnimation(string animationName, float animationSpeed)
{
foreach (Animation animation in part.FindModelAnimators(animationName))
@ -117,12 +146,22 @@ namespace Aerostats
{
UnityEngine.Debug.Log("Aerostats: staged");
ScreenMessages.PostScreenMessage("staged");
PlayAnimation(DeployAnimation, 1.0f);
//PlayAnimation(DeployAnimation, 1.0f);
Balloon = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Balloon.transform.position = part.Rigidbody.position + part.Rigidbody.transform.up;
Balloon.AddComponent<Rigidbody>();
Balloon.rigidbody.mass = BalloonEmptyMass;
Balloon.rigidbody.angularDrag = 10.0f;
LiftingGasQuantity = Math.Min(MinimumFillQuantity, RemainingCompressedGas);
RemainingCompressedGas -= LiftingGasQuantity;
}
private void FixedUpdate()
{
if (!Staged && part.inverseStage >= Staging.CurrentStage)
if (!Staged && GameSettings.LAUNCH_STAGES.GetKeyDown() && vessel.isActiveVessel && (part.inverseStage == Staging.CurrentStage - 1 || Staging.CurrentStage == 0))
{
Staged = true;
OnStaged();
@ -130,21 +169,25 @@ namespace Aerostats
if (Staged)
{
if (!IsAnimationPlaying(DeployAnimation))
//if (!IsAnimationPlaying(DeployAnimation))
{
float externalTemperature = (float)FlightGlobals.getExternalTemperature();
float balloonInternalTemperature = externalTemperature;
float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000, 0.00001f);
float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000.0f, 0.00001f);
Util.PostSingleScreenMessage("external atmo", "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa");
float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles;
LiftingGasTargetQuantity = Math.Max(MinimumFillQuantity, LiftingGasTargetQuantity);
if(LiftingGasTargetQuantity > LiftingGasQuantity)
{
// infalting balloon
float stepFinalQuantity = Math.Min(LiftingGasQuantity + MaxGasFillRate * Time.fixedDeltaTime, Math.Min(LiftingGasTargetQuantity, currentMaxQuantity));
float step = Math.Max(stepFinalQuantity - LiftingGasQuantity, 0.0f);
LiftingGasQuantity += step;
RemainingCompressedGas -= step;
// gas exhausted
if(RemainingCompressedGas < 0)
{
Util.PostSingleScreenMessage("out of gas", "Lifting gas reserve exhausted");
@ -154,6 +197,7 @@ namespace Aerostats
}
else
{
// deflating balloon
LiftingGasQuantity = Math.Max(LiftingGasQuantity - MaxGasVentRate * Time.fixedDeltaTime, LiftingGasTargetQuantity);
}
@ -170,16 +214,56 @@ namespace Aerostats
Inflation = currentGasVolume / MaxBalloonVolume;
Util.PostSingleScreenMessage("inflation", "Inflation = " + (Inflation * 100.0f).ToString("0.00") + "%");
SetInflation(Inflation);
//SetInflation(Inflation);
float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, vessel.mainBody);
float balloonLift = currentGasVolume * (airDensity - GasDensity);
Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + balloonLift + "kg");
float currentGasDensity = GasDensity * balloonInternalTemperature / ZeroCelsius / externalPressure * StandardPressure;
float balloonLift = currentGasVolume * airDensity;
float balloonGasMass = currentGasDensity * currentGasVolume;
Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + (balloonLift - balloonGasMass) + "kg");
var gravityAccel = FlightGlobals.getGeeForceAtPosition(vessel.GetWorldPos3D());
//Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")");
part.Rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force);
// V = 4/3*pi*r^3
float radius = Mathf.Pow(currentGasVolume * 0.75f / Mathf.PI, 0.333f);
float scale = radius + 0.1f;
Balloon.transform.localScale = new Vector3(scale,scale,scale);
Balloon.rigidbody.mass = (BalloonEmptyMass + balloonGasMass) * 0.001f;
Balloon.rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force);
// balloon drag
var airVelocity = Balloon.rigidbody.velocity + Krakensbane.GetFrameVelocity() /*- vessel.mainBody.getRFrmVel(vessel.GetWorldPos3D())*/;
float sqVel = (float)airVelocity.magnitude;
sqVel *= sqVel;
float dragForce = 0.5f * airDensity * sqVel * BalloonDragCoeff * (Mathf.PI * radius * radius);
Util.PostSingleScreenMessage("balloon drag", "Drag = " + dragForce + "N");
Balloon.rigidbody.AddForce(-airVelocity.normalized * dragForce / 1000.0f, ForceMode.Force);
// spring between balloon and base
float restLength = 2.0f + radius;
var springVec = Balloon.rigidbody.position - part.rigidbody.position;
float springLength = springVec.magnitude;
float springForceMag = 0;
if(springLength > restLength)
{
float tensingLength = springLength - restLength;
springForceMag = tensingLength * SpringHardness;
var springForce = springVec * (springForceMag / springLength * 0.001f);
part.rigidbody.AddForce(springForce, ForceMode.Force);
Balloon.rigidbody.AddForceAtPosition(-springForce, Balloon.rigidbody.position - Balloon.transform.up * radius, ForceMode.Force);
}
Util.PostSingleScreenMessage("spring force", "Spring force = " + springForceMag + "N");
}
}
}
public void Update()
{
if (Balloon != null)
{
Gizmos.DrawLine(part.transform.position, Balloon.transform.position - Balloon.transform.up * Balloon.transform.localScale.x);
}
}
}
}

BIN
doc/references/balloon_almost_empty.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
doc/references/balloon_full.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Loading…
Cancel
Save