private void ComputeCachedProperties() { DVector2 difference = GravitationalParent.Position - Position; double totalDistance = difference.Length() - TotalHeight * 0.5; _cachedAltitude = totalDistance - GravitationalParent.SurfaceRadius; double altitude = GetRelativeAltitude(); if (altitude > GravitationalParent.AtmosphereHeight) { _cachedRelativeVelocity = Velocity - GravitationalParent.Velocity; } else { difference.Normalize(); var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + GravitationalParent.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / GravitationalParent.RotationPeriod; _cachedRelativeVelocity = Velocity - (GravitationalParent.Velocity + surfaceNormal * rotationalSpeed); } }
protected virtual void RenderAbove(Graphics graphics, Camera camera) { if (EntryFlame != null) { EntryFlame.Draw(graphics, camera); } // Only show the vectors when it's requested and the craft is not parented if (_showDisplayVectors && Parent == null) { // The length of the vector is based on the width of the camera bounds float lengthFactor = (float)(camera.Bounds.Width * 0.1); PointF start = RenderUtils.WorldToScreen(Position, camera.Bounds); DVector2 relativeVelocity = GetRelativeVelocity(); // Only draw the velocity vector when it can be normalized if (relativeVelocity.Length() > 0) { relativeVelocity.Normalize(); PointF velocityEnd = RenderUtils.WorldToScreen(Position + relativeVelocity * lengthFactor, camera.Bounds); graphics.DrawLine(Pens.White, start, velocityEnd); } DVector2 pitchVector = DVector2.FromAngle(Pitch); pitchVector.Normalize(); PointF pitchEnd = RenderUtils.WorldToScreen(Position + pitchVector * lengthFactor, camera.Bounds); graphics.DrawLine(Pens.Red, start, pitchEnd); } }
public override void Update(double dt) { base.Update(dt); foreach (GridFin gridFin in _gridFins) { gridFin.Update(dt); } foreach (LandingLeg landingLeg in _landingLegs) { landingLeg.Update(dt); } DVector2 velocity = GetRelativeVelocity(); double altitude = GetRelativeAltitude(); DVector2 normalizedVelocity = velocity.Clone(); normalizedVelocity.Normalize(); DVector2 rotation = new DVector2(Math.Cos(Pitch), Math.Sin(Pitch)); // If we are going retro-grade and firing rockets adds soot if (altitude < 70000 && normalizedVelocity.Dot(rotation) < 0 && velocity.Length() > 400) { foreach (IEngine engine in Engines) { if (engine.IsActive && engine.Throttle > 0) { _sootRatio = Math.Min(_sootRatio + 0.015 * dt, 1.0); } } } }
// Interpolate between current and target orientation over the duration public override void Update(double elapsedTime, SpaceCraftBase spaceCraft) { double altitude = spaceCraft.GetRelativeAltitude(); double atmosphereheight = spaceCraft.GravitationalParent.AtmosphereHeight; DVector2 retrograde = spaceCraft.GetRelativeVelocity(); if (altitude > atmosphereheight) { retrograde = spaceCraft.GetInertialVelocity(); } retrograde.Negate(); retrograde.Normalize(); double retrogradeAngle = retrograde.Angle(); double adjustRatio = (elapsedTime - StartTime) / _adjustmentTime; if (adjustRatio > 1) { spaceCraft.SetPitch(retrogradeAngle); } else { double interpolatedAdjust = MathHelper.LerpAngle(_curentOrientation, retrogradeAngle, adjustRatio); spaceCraft.SetPitch(interpolatedAdjust); } }
public void SetRelativePitch(double pitch) { double altitude = GetRelativeAltitude(); if (altitude > GravitationalParent.AtmosphereHeight) { Pitch = GravitationalParent.Pitch + pitch; } else { DVector2 difference = GravitationalParent.Position - Position; difference.Normalize(); var surfaceNormal = new DVector2(difference.Y, difference.X); double normal = surfaceNormal.Angle(); if (double.IsNaN(normal)) { Pitch = GravitationalParent.Pitch + pitch; } else { Pitch = GravitationalParent.Pitch + pitch - normal; } } foreach (ISpaceCraft child in Children) { child.SetRelativePitch(pitch); } }
public override double GetRelativePitch() { double altitude = GetRelativeAltitude(); double relativePitch = Pitch - GravitationalParent.Pitch; if (altitude > GravitationalParent.AtmosphereHeight) { return(relativePitch); } DVector2 difference = GravitationalParent.Position - Position; difference.Normalize(); var surfaceNormal = new DVector2(difference.Y, difference.X); double normal = surfaceNormal.Angle(); if (!double.IsNaN(normal)) { if (altitude > 0.1) { relativePitch += normal; } else { relativePitch = normal; } } return(relativePitch); }
/// <summary> /// Gets the down range distance along the equator of the parent planet. /// </summary> public double GetDownrangeDistance(DVector2 pointOfReference) { if (GravitationalParent == null) { return(0.0); } DVector2 pofOffset = GravitationalParent.Position - pointOfReference; pofOffset.Normalize(); DVector2 spaceCraftOffset = GravitationalParent.Position - Position; spaceCraftOffset.Normalize(); // Find angle between normal vectors double angle = Math.Acos(pofOffset.X * spaceCraftOffset.X + pofOffset.Y * spaceCraftOffset.Y); if (double.IsNaN(angle)) { angle = 0; } // Fixing wrapping around a circle if (angle > Math.PI) { angle -= Math.PI; } // Take the ratio of the angle to the full circumference double arcRatio = angle / (Math.PI * 2); return(arcRatio * (2.0 * GravitationalParent.SurfaceRadius * Math.PI)); }
// Interpolate between current and target orientation over the duration public override void Update(double elapsedTime, SpaceCraftBase spaceCraft) { DVector2 prograde = spaceCraft.GetRelativeVelocity(); prograde.Normalize(); double retrogradeAngle = prograde.Angle(); if (retrogradeAngle > 0) { retrogradeAngle -= Math.PI * 2; } double adjustRatio = (elapsedTime - StartTime) / _adjustmentTime; if (adjustRatio > 1) { spaceCraft.SetPitch(retrogradeAngle); } else { double interpolatedAdjust = MathHelper.LerpAngle(_curentOrientation, retrogradeAngle, adjustRatio); spaceCraft.SetPitch(interpolatedAdjust); } }
public override double GetRelativePitch() { DVector2 difference = GravitationalParent.Position - Position; difference.Normalize(); double surfaceNormal = difference.Angle() - Constants.PiOverTwo; return(Pitch - surfaceNormal); }
public void ResolveAtmopsherics(IMassiveBody body) { DVector2 difference = body.Position - Position; double distance = difference.Length(); difference.Normalize(); double altitude = distance - body.SurfaceRadius; // The object is in the atmosphere of body B if (altitude < body.AtmosphereHeight) { var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + body.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / body.RotationPeriod; // Simple collision detection if (altitude <= 0) { var normal = new DVector2(-difference.X, -difference.Y); Position = body.Position + normal * (body.SurfaceRadius); Velocity = (body.Velocity + surfaceNormal * rotationalSpeed); Rotation = normal.Angle(); AccelerationN.X = -AccelerationG.X; AccelerationN.Y = -AccelerationG.Y; } double atmosphericDensity = body.GetAtmosphericDensity(altitude); DVector2 relativeVelocity = (body.Velocity + surfaceNormal * rotationalSpeed) - Velocity; double velocityMagnitude = relativeVelocity.LengthSquared(); if (velocityMagnitude > 0) { relativeVelocity.Normalize(); // Drag ( Fd = 0.5pv^2dA ) DVector2 dragForce = relativeVelocity * (0.5 * atmosphericDensity * velocityMagnitude * DragCoefficient * CrossSectionalArea); AccelerationD += dragForce / Mass; } } }
public void RenderCl(OpenCLProxy clProxy, Camera camera, IPhysicsBody sun) { RectangleD bounds = ComputeBoundingBox(); // Not in range easy return if (!camera.Intersects(bounds)) { return; } DVector2 sunNormal = DVector2.Zero; if (sun != null) { sunNormal = sun.Position - Position; sunNormal.Normalize(); } if (clProxy.HardwareAccelerationEnabled) { clProxy.UpdateDoubleArgument("cX", camera.Bounds.X); clProxy.UpdateDoubleArgument("cY", camera.Bounds.Y); clProxy.UpdateDoubleArgument("cWidth", camera.Bounds.Width); clProxy.UpdateDoubleArgument("cHeight", camera.Bounds.Height); clProxy.UpdateDoubleArgument("cRot", -camera.Rotation); clProxy.UpdateDoubleArgument("sunNormalX", sunNormal.X); clProxy.UpdateDoubleArgument("sunNormalY", sunNormal.Y); clProxy.UpdateDoubleArgument("bodyX", Position.X); clProxy.UpdateDoubleArgument("bodyY", Position.Y); clProxy.UpdateDoubleArgument("bodyRot", Pitch); clProxy.RunKernel(_computeKernel, RenderUtils.ScreenArea); } else { int totalSize = RenderUtils.ScreenArea; for (int i = 0; i < totalSize; i++) { Kernel.Run(clProxy.ReadIntBuffer("image", totalSize), RenderUtils.ScreenWidth, RenderUtils.ScreenHeight, camera.Bounds.X, camera.Bounds.Y, camera.Bounds.Width, camera.Bounds.Height, camera.Rotation, sunNormal.X, sunNormal.Y, Position.X, Position.Y, Pitch); } Kernel.Finish(); } }
private void SetCameraRotation() { IGravitationalBody target = _gravitationalBodies[_targetIndex]; if (target is ISpaceCraft) { if (_rotateInOrbit) { if (target.InOrbit) { if (!_targetInOrbit) { _camera.SetRotation(0, true); _targetInOrbit = true; } else { _camera.SetRotation(0); } } else { DVector2 craftOffset = target.GravitationalParent.Position - target.Position; craftOffset.Normalize(); if (_targetInOrbit) { _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle(), true); _targetInOrbit = false; } else { _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle()); } } } else { DVector2 craftOffset = target.GravitationalParent.Position - target.Position; craftOffset.Normalize(); _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle()); } } else { _camera.SetRotation(0); } }
public void SetRelativePitch(double pitch) { DVector2 difference = GravitationalParent.Position - Position; difference.Normalize(); double surfaceNormal = difference.Angle() - Constants.PiOverTwo; Pitch = surfaceNormal + pitch; foreach (ISpaceCraft child in Children) { child.SetRelativePitch(pitch); } }
public void RenderCl(OpenCLProxy clProxy, RectangleD cameraBounds, IPhysicsBody sun) { RectangleD bounds = ComputeBoundingBox(); // Not in range easy return if (!cameraBounds.IntersectsWith(bounds)) { return; } DVector2 sunNormal = DVector2.Zero; if (sun != null) { sunNormal = sun.Position - Position; sunNormal.Normalize(); } var normalizedPosition = new DVector2(cameraBounds.X - Position.X, cameraBounds.Y - Position.Y); if (clProxy.HardwareAccelerationEnabled) { clProxy.UpdateDoubleArgument("cameraLeft", normalizedPosition.X); clProxy.UpdateDoubleArgument("cameraTop", normalizedPosition.Y); clProxy.UpdateDoubleArgument("cameraWidth", cameraBounds.Width); clProxy.UpdateDoubleArgument("cameraHeight", cameraBounds.Height); clProxy.UpdateDoubleArgument("sunNormalX", sunNormal.X); clProxy.UpdateDoubleArgument("sunNormalY", sunNormal.Y); clProxy.UpdateDoubleArgument("rotation", Rotation); clProxy.RunKernel(_computeKernel, RenderUtils.ScreenArea); } else { int totalSize = RenderUtils.ScreenArea; for (int i = 0; i < totalSize; i++) { Kernel.Run(clProxy.ReadIntBuffer("image", totalSize), RenderUtils.ScreenWidth, RenderUtils.ScreenHeight, normalizedPosition.X, normalizedPosition.Y, cameraBounds.Width, cameraBounds.Height, sunNormal.X, sunNormal.Y, Rotation); } Kernel.Finish(); } }
public virtual void ResolveGravitation(IPhysicsBody other) { DVector2 difference = other.Position - Position; double r2 = difference.LengthSquared(); double massDistanceRatio = other.Mass / r2; // Ignore the force, the planet is too far away to matter if (massDistanceRatio < 2500) { return; } difference.Normalize(); // Gravitation ( aG = G m1 / r^2 ) AccelerationG += difference * Constants.GravitationConstant * massDistanceRatio; }
// Interpolate between current and target orientation over the duration public override void Update(double elapsedTime, ISpaceCraft spaceCraft) { DVector2 prograde = spaceCraft.GetRelativeVelocity(); prograde.Normalize(); double retrogradeAngle = prograde.Angle(); double adjustRatio = (elapsedTime - StartTime) / _adjustmentTime; if (adjustRatio > 1) { spaceCraft.SetRotation(retrogradeAngle); } else { double interpolatedAdjust = _curentOrientation * (1 - adjustRatio) + retrogradeAngle * adjustRatio; spaceCraft.SetRotation(interpolatedAdjust); } }
public virtual void Update(double dt) { ElapsedTime += dt; if (IsPrograde) { DVector2 prograde = SpaceCraft.GetRelativeVelocity(); prograde.Normalize(); SpaceCraft.SetRotation(prograde.Angle()); } if (IsRetrograde) { DVector2 retrograde = SpaceCraft.GetRelativeVelocity(); retrograde.Negate(); retrograde.Normalize(); SpaceCraft.SetRotation(retrograde.Angle()); } }
// Gets bodies sorted by distance that point the direction of the camera normal private static List <Tuple <double, int> > GetSortedBodyDistances(int currentIndex, IList <IGravitationalBody> bodies, DVector2 cameraNormal) { DVector2 targetCenter = bodies[currentIndex].Position; var bodiesByDistance = new List <Tuple <double, int> >(); for (int i = 0; i < bodies.Count; i++) { if (i == currentIndex) { continue; } var spaceCraft = bodies[i] as ISpaceCraft; // Skip terminated bodies if (spaceCraft != null && spaceCraft.Terminated) { continue; } DVector2 difference = bodies[i].Position - targetCenter; double distance = difference.LengthSquared(); difference.Normalize(); // Only add bodies in the same direction as the camera normal if (difference.Dot(cameraNormal) > 0) { bodiesByDistance.Add(new Tuple <double, int>(distance, i)); } } bodiesByDistance.Sort(Compare); return(bodiesByDistance); }
/// <summary> /// Gets the relative velocity of the spacecraft. If the space craft is within the parent's /// atmosphere than the rotation of the planet is taken in account. Otherwise its a simple difference of velocities. /// </summary> public override DVector2 GetRelativeVelocity() { double altitude = GetRelativeAltitude(); if (altitude > GravitationalParent.AtmosphereHeight) { return(Velocity - GravitationalParent.Velocity); } DVector2 difference = GravitationalParent.Position - Position; difference.Normalize(); var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + GravitationalParent.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / GravitationalParent.RotationPeriod; return(Velocity - (GravitationalParent.Velocity + surfaceNormal * rotationalSpeed)); }
public void ResolveAtmopsherics(IMassiveBody body) { // Don't resolve drag for children if (Parent != null) { return; } DVector2 difference = body.Position - Position; double heightOffset = Children.Count > 0 ? TotalHeight - Height * 0.5 : Height * 0.5; double distance = difference.Length() - heightOffset; difference.Normalize(); double altitude = distance - body.SurfaceRadius; // The spacecraft is in the bodies atmopshere if (altitude < body.AtmosphereHeight) { var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + body.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / body.RotationPeriod; // Rough metric for staying on ground from frame to frame if (altitude <= 0.0001) { _onGroundIterations = Math.Min(_onGroundIterations + 1, 10); } else { _onGroundIterations = Math.Max(_onGroundIterations - 1, 0); } // Simple collision detection if (_onGroundIterations > 5) { OnGround = true; var normal = new DVector2(-difference.X, -difference.Y); Position = body.Position + normal * (body.SurfaceRadius + heightOffset); Velocity = (body.Velocity + surfaceNormal * rotationalSpeed); Pitch = normal.Angle(); AccelerationN.X = -AccelerationG.X; AccelerationN.Y = -AccelerationG.Y; AccelerationY = new DVector2(0, 0); } else { OnGround = false; } double atmosphericDensity = body.GetAtmosphericDensity(altitude); DVector2 relativeVelocity = (body.Velocity + surfaceNormal * rotationalSpeed) - Velocity; double velocityMagnitude = relativeVelocity.LengthSquared(); if (velocityMagnitude > 0) { double speed = relativeVelocity.Length(); // Heating double qConv = 1.83e-4 * Math.Pow(speed, 3) * Math.Sqrt(atmosphericDensity / (Width * 0.5)); double qRad = 3.2e-22 * Math.Pow(speed, 8) * Math.Pow(atmosphericDensity, 1.2) * Math.Sqrt((Width * 0.5)); HeatingRate = qConv + qRad; relativeVelocity.Normalize(); double formDragCoefficient = TotalFormDragCoefficient(); double skinFrictionCoefficient = TotalSkinFrictionCoefficient(); double liftCoefficient = TotalLiftCoefficient(); double formDragTerm = formDragCoefficient * TotalFormDragArea(); double skinFrictionTerm = skinFrictionCoefficient * TotalSkinFrictionArea(); double dragTerm = formDragTerm; if (!double.IsNaN(skinFrictionTerm)) { dragTerm += skinFrictionTerm; } double liftTerm = liftCoefficient * TotalLiftArea(); double turnTerm = 0; if (Settings.Default.UseTheTurnForce) { turnTerm = liftTerm * Math.Sin(Roll); liftTerm *= Math.Cos(Roll); } // Form Drag ( Fd = 0.5pv^2dA ) // Skin friction ( Fs = 0.5CfpV^2S ) DVector2 drag = relativeVelocity * (0.5 * atmosphericDensity * velocityMagnitude * dragTerm); DVector2 lift = relativeVelocity * (0.5 * atmosphericDensity * velocityMagnitude * liftTerm); AccelerationD = drag / Mass; DVector2 accelerationLift = lift / Mass; double alpha = GetAlpha(); double halfPi = Math.PI / 2; bool isRetrograde = alpha > halfPi || alpha < -halfPi; if (Settings.Default.UseTheTurnForce && isRetrograde) { AccelerationL.X -= accelerationLift.Y; AccelerationL.Y += accelerationLift.X; } else { AccelerationL.X += accelerationLift.Y; AccelerationL.Y -= accelerationLift.X; } } } else { HeatingRate = 0; } }
public void ResolveAtmopsherics(IMassiveBody body) { DVector2 difference = body.Position - Position; double distance = difference.Length() - Height * 0.5; difference.Normalize(); Altitude = distance - body.SurfaceRadius; // The object is in the atmosphere of body B if (Altitude < body.AtmosphereHeight) { var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = Altitude + body.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / body.RotationPeriod; // Simple collision detection if (Altitude <= 0) { var normal = new DVector2(-difference.X, -difference.Y); Position = body.Position + normal * (body.SurfaceRadius); Pitch = normal.Angle(); AccelerationN.X = -AccelerationG.X; AccelerationN.Y = -AccelerationG.Y; OnGround = true; } else { OnGround = false; } double atmosphericDensity = body.GetAtmosphericDensity(Altitude); RelativeVelocity = (body.Velocity + surfaceNormal * rotationalSpeed) - Velocity; double velocityMagnitude = RelativeVelocity.LengthSquared(); if (velocityMagnitude > 0) { DVector2 normalizedRelativeVelocity = RelativeVelocity.Clone(); normalizedRelativeVelocity.Normalize(); double formDragTerm = FormDragCoefficient * FrontalArea; double skinFrictionTerm = SkinFrictionCoefficient * ExposedSurfaceArea; double dragTerm = formDragTerm + skinFrictionTerm; // Drag ( Fd = 0.5pv^2dA ) DVector2 dragForce = normalizedRelativeVelocity * (0.5 * atmosphericDensity * velocityMagnitude * dragTerm); // Reject insane forces if (dragForce.Length() < 50000000) { AccelerationD = dragForce / Mass; } } } }
/// <summary> /// Updates the spacecraft and it's children. /// </summary> public override void Update(double dt) { ComputeCachedProperties(); double altitude = GetRelativeAltitude(); if (GravitationalParent == null) { IspMultiplier = 1; } else { IspMultiplier = GravitationalParent.GetIspMultiplier(altitude); } if (Parent == null) { UpdateEngines(dt); if (Thrust > 0 && _isReleased) { var thrustVector = new DVector2(Math.Cos(Pitch) * Math.Cos(Yaw), Math.Sin(Pitch) * Math.Cos(Yaw)); var yawVector = new DVector2(Math.Cos(Pitch) * Math.Sin(Yaw), Math.Sin(Pitch) * Math.Sin(Yaw)); AccelerationN += (thrustVector * Thrust) / Mass; AccelerationY += (yawVector * Thrust) / Mass; } if (_requiresStaging) { // Simulate simple staging mechanism double sAngle = StageOffset.Angle(); DVector2 stagingNormal = DVector2.FromAngle(Pitch + sAngle + Constants.PiOverTwo); AccelerationN += stagingNormal * StagingForce; _requiresStaging = false; } if (_isReleased) { // Integrate acceleration Velocity += AccelerationG * dt; Velocity += AccelerationD * dt; Velocity += AccelerationL * dt; Velocity += AccelerationN * dt; LateralVelocity += AccelerationY * dt; LateralPosition += LateralVelocity * dt; } // Re-normalize FTL scenarios if (Velocity.LengthSquared() > Constants.SpeedLightSquared) { Velocity.Normalize(); Velocity *= Constants.SpeedOfLight; } // If the craft is on the ground with high time warpd don't update the position as normal. // Instead just keep putting the ship at the correct spot on it's gravitational body based on rotation. if (OnGround && dt > 5) { DVector2 parentOffset = GravitationalParent.Position - Position; parentOffset.Normalize(); Position = GravitationalParent.Position + parentOffset * GravitationalParent.SurfaceRadius; } else { // Integrate velocity Position += Velocity * dt; } MachNumber = GetRelativeVelocity().Length() * 0.0029411764; foreach (ISpaceCraft child in Children) { child.UpdateChildren(Position, Velocity); } _trailTimer += dt; // Somewhat arbitrary conditions for launch trails if (dt < 0.1666666666 && !InOrbit && !OnGround && _cachedAltitude > 50 && _cachedAltitude < GravitationalParent.AtmosphereHeight * 2 && _trailTimer > 1) { string parentName = GravitationalParent.ToString(); if (!_launchTrails.ContainsKey(parentName)) { _launchTrails.Add(parentName, new LaunchTrail()); } _launchTrails[parentName].AddPoint(Position, GravitationalParent, Throttle > 0); _trailTimer = 0; } } }
/// <summary> /// Traces a space craft orbit by re-centering the world around the parent. /// </summary> public static void TraceSpaceCraft(SpaceCraftBase satellite, OrbitTrace trace) { IMassiveBody parent = satellite.GravitationalParent; DVector2 initialPosition = satellite.Position - parent.Position; var shipOffset = new DVector2(Math.Cos(satellite.Pitch) * (satellite.TotalWidth - satellite.Width), Math.Sin(satellite.Pitch) * (satellite.TotalHeight - satellite.Height)) * 0.5; initialPosition -= shipOffset; var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent); var proxySatellite = new SpaceCraftProxy(initialPosition, satellite.Velocity - parent.Velocity, satellite); proxySatellite.SetGravitationalParent(proxyParent); int stepCount; double targetDt; double proximityDt; double orbitalDt = 0; bool isOrbiting; double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double proximityAltitude = proxyParent.SurfaceRadius * 0.15; if (altitude < proximityAltitude) { stepCount = 1100; isOrbiting = false; proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = proximityDt; } else { stepCount = 600; isOrbiting = true; orbitalDt = GetOrbitalDt(initialPosition.Length(), parent.Mass, proxySatellite.Velocity.Length()); targetDt = orbitalDt; } // Assumes that the parent is (0,0) which will be true in this case double previousAngle = proxySatellite.Position.Angle(); double totalAngularDisplacement = 0; trace.Reset(satellite.Position - shipOffset); // Update steps based on orbit vs atmosphere for (int step = 0; step < stepCount; step++) { proxySatellite.ResetAccelerations(); proxySatellite.ResolveGravitation(proxyParent); proxySatellite.ResolveAtmopsherics(proxyParent); proxySatellite.Update(targetDt); double currentAngle = proxySatellite.Position.Angle(); totalAngularDisplacement += DeltaAngle(currentAngle, previousAngle); // Made a full orbit if (totalAngularDisplacement > AngularCutoff) { break; } previousAngle = currentAngle; altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double velocity = proxySatellite.GetRelativeVelocity().Length(); double offsetFactor = 0.0; //if (altitude < proxyParent.AtmosphereHeight*2) if (altitude < proxyParent.AtmosphereHeight * 4) { if (velocity < 3000) { offsetFactor = 1.0; } else if (velocity < 4000) { offsetFactor = 1.0 - velocity / 3000.0; } } // Check if reference frame shifting needs to occur in atmosphere if (offsetFactor > 0.0001) { DVector2 difference = proxyParent.Position - proxySatellite.Position; difference.Normalize(); var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + proxyParent.SurfaceRadius; // Distance of circumference at this altitude ( c= r * 2pi ) double pathCirumference = Constants.TwoPi * altitudeFromCenter; double rotationalSpeed = pathCirumference / parent.RotationPeriod; DVector2 atmopshereVelocity = surfaceNormal * rotationalSpeed; proxySatellite.ApplyFrameOffset(atmopshereVelocity * offsetFactor * targetDt); } // Return early if the trace goes into a planet if (altitude <= 0) { trace.AddPoint(proxySatellite.Position + parent.Position, altitude); break; } // Determine the correct change overs from orbital to surface proximity if (isOrbiting) { if (altitude < proximityAltitude) { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); isOrbiting = false; stepCount = 1000; } else { targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); } } else { if (altitude > proximityAltitude) { orbitalDt = GetOrbitalDt(proxySatellite.Position.Length(), parent.Mass, proxySatellite.Velocity.Length()); targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); isOrbiting = true; stepCount = 600; } else { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); } } trace.AddPoint(proxySatellite.Position + parent.Position, altitude); } }
public void ResolveAtmopsherics(IMassiveBody body) { // Don't resolve drag for children if (Parent != null) { return; } DVector2 difference = body.Position - Position; double distance = difference.Length(); difference.Normalize(); double altitude = distance - body.SurfaceRadius; // The spacecraft is in the bodies atmopshere if (altitude < body.AtmosphereHeight) { var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + body.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / body.RotationPeriod; // Simple collision detection if (altitude <= 0) { var normal = new DVector2(-difference.X, -difference.Y); Position = body.Position + normal * (body.SurfaceRadius); Velocity = (body.Velocity + surfaceNormal * rotationalSpeed); Rotation = normal.Angle(); AccelerationN.X = -AccelerationG.X; AccelerationN.Y = -AccelerationG.Y; } double atmosphericDensity = body.GetAtmosphericDensity(altitude); DVector2 relativeVelocity = (body.Velocity + surfaceNormal * rotationalSpeed) - Velocity; double velocity = relativeVelocity.Length(); double velocitySquared = relativeVelocity.LengthSquared(); if (velocitySquared > 0) { double speed = relativeVelocity.Length(); // Heating HeatingRate = 1.83e-4 * Math.Pow(speed, 3) * Math.Sqrt(atmosphericDensity / (Width * 0.5)); relativeVelocity.Normalize(); double dragTerm = TotalDragCoefficient() * TotalDragArea(); // Drag ( Fd = 0.5pv^2dA ) DVector2 dragForce = relativeVelocity * (0.5 * atmosphericDensity * velocitySquared * dragTerm); AccelerationD += dragForce / Mass; double reynoldsNumber = (velocity * Height) / body.GetAtmosphericViscosity(altitude); double frictionCoefficient = 0.455 / Math.Pow(Math.Log10(reynoldsNumber), 2.58); double frictionTerm = frictionCoefficient * TotalSurfaceArea(); // Skin friction ( Fs = 0.5CfpV^2S ) DVector2 skinFriction = relativeVelocity * (0.5 * atmosphericDensity * velocitySquared * frictionTerm); AccelerationD += skinFriction / Mass; } } else { HeatingRate = 0; } }
/// <summary> /// Traces a space craft orbit by re-centering the world around the parent. /// </summary> public static void TraceSpaceCraft(SpaceCraftBase satellite, OrbitTrace trace) { IMassiveBody parent = satellite.GravitationalParent; DVector2 initialPosition = satellite.Position - parent.Position; var shipOffset = new DVector2(Math.Cos(satellite.Pitch) * (satellite.TotalWidth - satellite.Width), Math.Sin(satellite.Pitch) * (satellite.TotalHeight - satellite.Height)) * 0.5; initialPosition -= shipOffset; var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent); var proxySatellite = new SpaceCraftProxy(initialPosition, satellite.Velocity - parent.Velocity, satellite); proxySatellite.SetGravitationalParent(proxyParent); int stepCount; double targetDt; double proximityDt; double orbitalDt = 0; bool isOrbiting; double orbitalTerminationRadius = 0; double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double proximityAltitude = proxyParent.SurfaceRadius * 0.15; if (altitude < proximityAltitude) { stepCount = 1100; isOrbiting = false; proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = proximityDt; } else { stepCount = 600; isOrbiting = true; orbitalDt = GetOrbitalDt(initialPosition, proxySatellite.Velocity, out orbitalTerminationRadius); targetDt = orbitalDt; } trace.Reset(satellite.Position - shipOffset); // Simulate 300 orbital steps, more for proximity for (int step = 0; step < stepCount; step++) { proxySatellite.ResetAccelerations(); proxySatellite.ResolveGravitation(proxyParent); proxySatellite.ResolveAtmopsherics(proxyParent); proxySatellite.Update(targetDt); altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double velocity = proxySatellite.GetRelativeVelocity().Length(); double offsetFactor = 0.0; if (altitude < proxyParent.AtmosphereHeight * 2) { if (velocity < 3000) { offsetFactor = 1.0; } else if (velocity < 4000) { offsetFactor = 1.0 - velocity / 3000.0; } } // Check if reference frame shifting needs to occur in atmosphere if (offsetFactor > 0.0001) { DVector2 difference = proxyParent.Position - proxySatellite.Position; difference.Normalize(); var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + proxyParent.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / parent.RotationPeriod; DVector2 atmopshereVelocity = surfaceNormal * rotationalSpeed; proxySatellite.ApplyFrameOffset(atmopshereVelocity * offsetFactor * targetDt); // Return early if the trace goes into a planet if (altitude <= 0) { trace.AddPoint(proxySatellite.Position + parent.Position, altitude); break; } } // Determine the correct change overs from orbital to surface proximity if (isOrbiting) { if (altitude < proximityAltitude) { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); isOrbiting = false; stepCount = 1000; } else { targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); } } else { if (altitude > proximityAltitude) { orbitalDt = GetOrbitalDt(proxySatellite.Position, proxySatellite.Velocity, out orbitalTerminationRadius); targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); isOrbiting = true; stepCount = 600; } else { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); } } // Check expensive termination conditions after half of the iterations if (isOrbiting && step > 100) { DVector2 offsetVector = proxySatellite.Position - initialPosition; double distanceFromStart = offsetVector.Length(); // Terminate and add the end point if (distanceFromStart < orbitalTerminationRadius) { trace.AddPoint(proxySatellite.Position + parent.Position, altitude); break; } } trace.AddPoint(proxySatellite.Position + parent.Position, altitude); } }