Esempio n. 1
0
        private void Set(Part p, ReentrySimulation.SimCurves _simCurves)
        {
            totalMass             = p.mass + p.GetResourceMass() + p.GetPhysicslessChildMass();
            shieldedFromAirstream = p.ShieldedFromAirstream;

            noDrag             = p.rb == null && !PhysicsGlobals.ApplyDragToNonPhysicsParts;
            hasLiftModule      = p.hasLiftModule;
            bodyLiftMultiplier = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier;

            simCurves = _simCurves;


            // TODO : choose either method :
            // - use the part cube but have the risk that the part change the cubes values (stagging, ...) while we do the sim
            // - use a copy of the cubes but use more mem
            cubes = p.DragCubes;
            //cubes = new DragCubeList();
            //CopyDragCubesList(p.DragCubes, cubes);

            // Rotation to convert the vessel space vesselVelocity to the part space vesselVelocity
            vesselToPart = Quaternion.LookRotation(p.vessel.GetTransform().InverseTransformDirection(p.transform.forward), p.vessel.GetTransform().InverseTransformDirection(p.transform.up)).Inverse();


            //DragCubeMultiplier = PhysicsGlobals.DragCubeMultiplier;
            //DragMultiplier = PhysicsGlobals.DragMultiplier;


            if (p.dragModel != Part.DragModel.CUBE)
            {
                MechJebCore.print(p.name + " " + p.dragModel);
            }

            //oPart = p;
        }
        public double MaxAllowedSpeed(Vector3d pos, Vector3d vel)
        {
            double vSpeed = Vector3d.Dot(vel, pos.normalized);
            double ToF    = (vSpeed + Math.Sqrt(vSpeed * vSpeed + 2 * g * (pos.magnitude - terrainRadius))) / g;

            MechJebCore.print("ToF = " + ToF.ToString("F2"));
            return(0.8 * (thrust - g) * ToF);
        }
Esempio n. 3
0
        IEnumerator Start()
        {
            // We do this in MainMenu because something is going on in that scene that kills anything loaded with a bundle
            if (diffuseAmbient)
            {
                MechJebCore.print("Shaders already loaded");
            }

            MechJebCore.print("Loading Shaders Bundles");

            // Load the font asset bundle
            AssetBundleCreateRequest bundleLoadRequest = AssetBundle.LoadFromFileAsync(shaderPath);

            yield return(bundleLoadRequest);

            AssetBundle assetBundle = bundleLoadRequest.assetBundle;

            if (assetBundle == null)
            {
                MechJebCore.print("Failed to load AssetBundle " + shaderPath);
                yield break;
            }

            AssetBundleRequest assetLoadRequest = assetBundle.LoadAssetAsync <Shader>(diffuseAmbientName);

            yield return(assetLoadRequest);

            diffuseAmbient = assetLoadRequest.asset as Shader;

            assetLoadRequest = assetBundle.LoadAssetAsync <Shader>(diffuseAmbientIgnoreZName);
            yield return(assetLoadRequest);

            diffuseAmbientIgnoreZ = assetLoadRequest.asset as Shader;

            assetBundle.Unload(false);
            MechJebCore.print("Loaded Shaders Bundles");

            comboBoxBackground          = new Texture2D(16, 16, TextureFormat.RGBA32, false);
            comboBoxBackground.wrapMode = TextureWrapMode.Clamp;

            for (int x = 0; x < comboBoxBackground.width; x++)
            {
                for (int y = 0; y < comboBoxBackground.height; y++)
                {
                    if (x == 0 || x == comboBoxBackground.width - 1 || y == 0 || y == comboBoxBackground.height - 1)
                    {
                        comboBoxBackground.SetPixel(x, y, new Color(0, 0, 0, 1));
                    }
                    else
                    {
                        comboBoxBackground.SetPixel(x, y, new Color(0.05f, 0.05f, 0.05f, 0.95f));
                    }
                }
            }

            comboBoxBackground.Apply();
        }
Esempio n. 4
0
        IEnumerator Start()
        {
            // We do this in MainMenu because something is going on in that scene that kills anything loaded with a bundle
            if (diffuseAmbient)
            {
                MechJebCore.print("Shaders already loaded");
            }

            MechJebCore.print("Loading Shaders Bundles");

            // Load the font asset bundle
            AssetBundleCreateRequest bundleLoadRequest = AssetBundle.LoadFromFileAsync(shaderPath);

            yield return(bundleLoadRequest);

            AssetBundle assetBundle = bundleLoadRequest.assetBundle;

            if (assetBundle == null)
            {
                MechJebCore.print("Failed to load AssetBundle " + shaderPath);
                yield break;
            }

            AssetBundleRequest assetLoadRequest = assetBundle.LoadAssetAsync <Shader>(diffuseAmbientName);

            yield return(assetLoadRequest);

            diffuseAmbient = assetLoadRequest.asset as Shader;

            assetLoadRequest = assetBundle.LoadAssetAsync <Shader>(diffuseAmbientIgnoreZName);
            yield return(assetLoadRequest);

            diffuseAmbientIgnoreZ = assetLoadRequest.asset as Shader;

            assetBundle.Unload(false);
            MechJebCore.print("Loaded Shaders Bundles");
        }
Esempio n. 5
0
        public void Update(Vessel vessel)
        {
            if (vessel.rigidbody == null)
            {
                return;                           //if we try to update before rigidbodies exist we spam the console with NullPointerExceptions.
            }
            //if (vessel.packed) return;

            // To investigate some strange error
            if ((vessel.mainBody == null || (object)(vessel.mainBody) == null) && counter == 0)
            {
                if ((object)(vessel.mainBody) == null)
                {
                    MechJebCore.print("vessel.mainBody is proper null");
                }
                else
                {
                    MechJebCore.print("vessel.mainBody is Unity null");
                }

                counter = counter++ % 100;
            }


            time   = Planetarium.GetUniversalTime();
            deltaT = TimeWarp.fixedDeltaTime;

            CoM = vessel.findWorldCenterOfMass();
            up  = (CoM - vessel.mainBody.position).normalized;

            Rigidbody rigidBody = vessel.rootPart.rigidbody;

            if (rigidBody != null)
            {
                rootPartPos = rigidBody.position;
            }

            north                 = Vector3d.Exclude(up, (vessel.mainBody.position + vessel.mainBody.transform.up * (float)vessel.mainBody.Radius) - CoM).normalized;
            east                  = vessel.mainBody.getRFrmVel(CoM).normalized;
            forward               = vessel.GetTransform().up;
            rotationSurface       = Quaternion.LookRotation(north, up);
            rotationVesselSurface = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(vessel.GetTransform().rotation) * rotationSurface);

//            velocityVesselOrbit = vessel.orbit.GetVel();
//            velocityVesselOrbitUnit = velocityVesselOrbit.normalized;
//            velocityVesselSurface = velocityVesselOrbit - vessel.mainBody.getRFrmVel(CoM);
//            velocityVesselSurfaceUnit = velocityVesselSurface.normalized;
            velocityMainBodySurface = rotationSurface * vessel.srf_velocity;

            horizontalOrbit   = Vector3d.Exclude(up, vessel.obt_velocity).normalized;
            horizontalSurface = Vector3d.Exclude(up, vessel.srf_velocity).normalized;

            angularVelocity = Quaternion.Inverse(vessel.GetTransform().rotation) * vessel.rigidbody.angularVelocity;

            radialPlusSurface = Vector3d.Exclude(vessel.srf_velocity, up).normalized;
            radialPlus        = Vector3d.Exclude(vessel.obt_velocity, up).normalized;
            normalPlusSurface = -Vector3d.Cross(radialPlusSurface, vessel.srf_velocity.normalized);
            normalPlus        = -Vector3d.Cross(radialPlus, vessel.obt_velocity.normalized);

            gravityForce = FlightGlobals.getGeeForceAtPosition(CoM);
            localg       = gravityForce.magnitude;

            speedOrbital.value           = vessel.obt_velocity.magnitude;
            speedSurface.value           = vessel.srf_velocity.magnitude;
            speedVertical.value          = Vector3d.Dot(vessel.srf_velocity, up);
            speedSurfaceHorizontal.value = Vector3d.Exclude(up, vessel.srf_velocity).magnitude; //(velocityVesselSurface - (speedVertical * up)).magnitude;
            speedOrbitHorizontal         = (vessel.obt_velocity - (speedVertical * up)).magnitude;

            vesselHeading.value = rotationVesselSurface.eulerAngles.y;
            vesselPitch.value   = (rotationVesselSurface.eulerAngles.x > 180) ? (360.0 - rotationVesselSurface.eulerAngles.x) : -rotationVesselSurface.eulerAngles.x;
            vesselRoll.value    = (rotationVesselSurface.eulerAngles.z > 180) ? (rotationVesselSurface.eulerAngles.z - 360.0) : rotationVesselSurface.eulerAngles.z;

            altitudeASL.value = vessel.mainBody.GetAltitude(CoM);
            //RaycastHit sfc;
            //if (Physics.Raycast(CoM, -up, out sfc, (float)altitudeASL + 10000.0F, 1 << 15))
            //{
            //    altitudeTrue.value = sfc.distance;
            //}
            //else if (vessel.mainBody.pqsController != null)
            //{
            //    // from here: http://kerbalspaceprogram.com/forum/index.php?topic=10324.msg161923#msg161923
            //    altitudeTrue.value = vessel.mainBody.GetAltitude(CoM) - (vessel.mainBody.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(vessel.mainBody.GetLongitude(CoM), Vector3d.down) * QuaternionD.AngleAxis(vessel.mainBody.GetLatitude(CoM), Vector3d.forward) * Vector3d.right) - vessel.mainBody.pqsController.radius);
            //}
            //else
            //{
            //    altitudeTrue.value = vessel.mainBody.GetAltitude(CoM);
            //}

            //double surfaceAltitudeASL = altitudeASL - altitudeTrue;

            double surfaceAltitudeASL = vessel.mainBody.pqsController != null ? vessel.pqsAltitude : 0d;

            altitudeTrue.value = altitudeASL - surfaceAltitudeASL;

            altitudeBottom = altitudeTrue;
            foreach (Part p in vessel.parts)
            {
                if (p.collider != null)
                {
                    Vector3d bottomPoint   = p.collider.ClosestPointOnBounds(vessel.mainBody.position);
                    double   partBottomAlt = vessel.mainBody.GetAltitude(bottomPoint) - surfaceAltitudeASL;
                    altitudeBottom = Math.Max(0, Math.Min(altitudeBottom, partBottomAlt));
                }
            }

            double atmosphericPressure = FlightGlobals.getStaticPressure(altitudeASL, vessel.mainBody);

            if (atmosphericPressure < vessel.mainBody.atmosphereMultiplier * 1e-6)
            {
                atmosphericPressure = 0;
            }
            atmosphericDensity      = FlightGlobals.getAtmDensity(atmosphericPressure);
            atmosphericDensityGrams = atmosphericDensity * 1000;

            orbitApA.value      = vessel.orbit.ApA;
            orbitPeA.value      = vessel.orbit.PeA;
            orbitPeriod.value   = vessel.orbit.period;
            orbitTimeToAp.value = vessel.orbit.timeToAp;
            if (vessel.orbit.eccentricity < 1)
            {
                orbitTimeToPe.value = vessel.orbit.timeToPe;
            }
            else
            {
                orbitTimeToPe.value = -vessel.orbit.meanAnomaly / (2 * Math.PI / vessel.orbit.period);
            }
            orbitLAN.value = vessel.orbit.LAN;
            orbitArgumentOfPeriapsis.value = vessel.orbit.argumentOfPeriapsis;
            orbitInclination.value         = vessel.orbit.inclination;
            orbitEccentricity.value        = vessel.orbit.eccentricity;
            orbitSemiMajorAxis.value       = vessel.orbit.semiMajorAxis;
            latitude.value  = vessel.mainBody.GetLatitude(CoM);
            longitude.value = MuUtils.ClampDegrees180(vessel.mainBody.GetLongitude(CoM));

            if (vessel.mainBody != Planetarium.fetch.Sun)
            {
                Vector3d delta = vessel.mainBody.getPositionAtUT(Planetarium.GetUniversalTime() + 1) - vessel.mainBody.getPositionAtUT(Planetarium.GetUniversalTime() - 1);
                Vector3d plUp  = Vector3d.Cross(vessel.mainBody.getPositionAtUT(Planetarium.GetUniversalTime()) - vessel.mainBody.referenceBody.getPositionAtUT(Planetarium.GetUniversalTime()), vessel.mainBody.getPositionAtUT(Planetarium.GetUniversalTime() + vessel.mainBody.orbit.period / 4) - vessel.mainBody.referenceBody.getPositionAtUT(Planetarium.GetUniversalTime() + vessel.mainBody.orbit.period / 4)).normalized;
                angleToPrograde = MuUtils.ClampDegrees360((((vessel.orbit.inclination > 90) || (vessel.orbit.inclination < -90)) ? 1 : -1) * ((Vector3)up).AngleInPlane(plUp, delta));
            }
            else
            {
                angleToPrograde = 0;
            }

            mainBody = vessel.mainBody;

            radius = (CoM - vessel.mainBody.position).magnitude;

            mass = massDrag = torqueThrustPYAvailable = 0;
            thrustVectorLastFrame   = new Vector3d();
            thrustVectorMaxThrottle = new Vector3d();
            thrustVectorMinThrottle = new Vector3d();
            torqueAvailable         = new Vector3d();
            rcsThrustAvailable      = new Vector6();
            rcsTorqueAvailable      = new Vector6();
            ctrlTorqueAvailable     = new Vector6();

            EngineInfo einfo = new EngineInfo(CoM);
            IntakeInfo iinfo = new IntakeInfo();

            parachutes = new List <ModuleParachute>();

            var rcsbal = vessel.GetMasterMechJeb().rcsbal;

            if (vessel.ActionGroups[KSPActionGroup.RCS] && rcsbal.enabled)
            {
                Vector3d rot = Vector3d.zero;
                foreach (Vector6.Direction dir6 in Enum.GetValues(typeof(Vector6.Direction)))
                {
                    Vector3d dir = Vector6.directions[dir6];
                    double[] throttles;
                    List <RCSSolver.Thruster> thrusters;
                    rcsbal.GetThrottles(dir, out throttles, out thrusters);
                    if (throttles != null)
                    {
                        for (int i = 0; i < throttles.Length; i++)
                        {
                            if (throttles[i] > 0)
                            {
                                Vector3d force = thrusters[i].GetThrust(dir, rot);
                                rcsThrustAvailable.Add(vessel.GetTransform().InverseTransformDirection(dir * Vector3d.Dot(force * throttles[i], dir)));
                            }
                        }
                    }
                }
            }

            hasMFE = false;

            foreach (Part p in vessel.parts)
            {
                if (p.IsPhysicallySignificant())
                {
                    double partMass = p.TotalMass();
                    mass     += partMass;
                    massDrag += partMass * p.maximum_drag;
                }

                if (vessel.ActionGroups[KSPActionGroup.RCS] && !rcsbal.enabled)
                {
                    foreach (ModuleRCS pm in p.Modules.OfType <ModuleRCS>())
                    {
                        double   maxT         = pm.thrusterPower;
                        Vector3d partPosition = p.Rigidbody.worldCenterOfMass - CoM;

                        if ((pm.isEnabled) && (!pm.isJustForShow))
                        {
                            foreach (Transform t in pm.thrusterTransforms)
                            {
                                Vector3d thrusterThrust = vessel.GetTransform().InverseTransformDirection(-t.up.normalized) * pm.thrusterPower;
                                rcsThrustAvailable.Add(thrusterThrust);
                                Vector3d thrusterTorque = Vector3.Cross(vessel.GetTransform().InverseTransformDirection(partPosition), thrusterThrust);
                                rcsTorqueAvailable.Add(thrusterTorque);
                            }
                        }
                    }
                }

                if (p is ControlSurface)
                {
                    Vector3d       partPosition = p.Rigidbody.worldCenterOfMass - CoM;
                    ControlSurface cs           = (p as ControlSurface);
                    Vector3d       airSpeed     = vessel.srf_velocity + Vector3.Cross(cs.Rigidbody.angularVelocity, cs.transform.position - cs.Rigidbody.position);
                    // Air Speed is velocityVesselSurface
                    // AddForceAtPosition seems to need the airspeed vector rotated with the flap rotation x its surface
                    Quaternion airSpeedRot   = Quaternion.AngleAxis(cs.ctrlSurfaceRange * cs.ctrlSurfaceArea, cs.transform.rotation * cs.pivotAxis);
                    Vector3    ctrlTroquePos = vessel.GetTransform().InverseTransformDirection(Vector3.Cross(partPosition, cs.getLiftVector(airSpeedRot * airSpeed)));
                    Vector3    ctrlTroqueNeg = vessel.GetTransform().InverseTransformDirection(Vector3.Cross(partPosition, cs.getLiftVector(Quaternion.Inverse(airSpeedRot) * airSpeed)));
                    ctrlTorqueAvailable.Add(ctrlTroquePos);
                    ctrlTorqueAvailable.Add(ctrlTroqueNeg);
                }

                if (p is CommandPod)
                {
                    torqueAvailable += Vector3d.one * Math.Abs(((CommandPod)p).rotPower);
                }

                foreach (VesselStatePartExtension vspe in vesselStatePartExtensions)
                {
                    vspe(p);
                }

                foreach (PartModule pm in p.Modules)
                {
                    if (!pm.isEnabled)
                    {
                        continue;
                    }

                    if (pm is ModuleReactionWheel)
                    {
                        ModuleReactionWheel rw = (ModuleReactionWheel)pm;
                        // I had to remove the test for active in .23 since the new ressource system reply to the RW that
                        // there is no energy available when the RW do tiny adjustement.
                        // I replaceed it with a test that check if there is electricity anywhere on the ship.
                        // Let's hope we don't get reaction wheel that use something else
                        //if (rw.wheelState == ModuleReactionWheel.WheelState.Active && !rw.stateString.Contains("Not enough"))
                        if (rw.wheelState == ModuleReactionWheel.WheelState.Active && vessel.HasElectricCharge())
                        {
                            torqueAvailable += new Vector3d(rw.PitchTorque, rw.RollTorque, rw.YawTorque);
                        }
                    }
                    else if (pm is ModuleEngines)
                    {
                        einfo.AddNewEngine(pm as ModuleEngines);
                    }
                    else if (pm is ModuleEnginesFX)
                    {
                        einfo.AddNewEngine(pm as ModuleEnginesFX);
                    }
                    else if (pm is ModuleResourceIntake)
                    {
                        iinfo.addIntake(pm as ModuleResourceIntake);
                    }
                    else if (pm is ModuleParachute)
                    {
                        parachutes.Add(pm as ModuleParachute);
                    }
                    else if (pm is ModuleControlSurface)
                    {
                        // TODO : Tweakable for ignorePitch / ignoreYaw  / ignoreRoll
                        ModuleControlSurface cs           = (pm as ModuleControlSurface);
                        Vector3d             partPosition = p.Rigidbody.worldCenterOfMass - CoM;

                        Vector3d airSpeed = vessel.srf_velocity + Vector3.Cross(cs.part.Rigidbody.angularVelocity, cs.transform.position - cs.part.Rigidbody.position);

                        Quaternion airSpeedRot = Quaternion.AngleAxis(cs.ctrlSurfaceRange * cs.ctrlSurfaceArea, cs.transform.rotation * Vector3.right);

                        Vector3 ctrlTroquePos = vessel.GetTransform().InverseTransformDirection(Vector3.Cross(partPosition, cs.getLiftVector(airSpeedRot * airSpeed)));
                        Vector3 ctrlTroqueNeg = vessel.GetTransform().InverseTransformDirection(Vector3.Cross(partPosition, cs.getLiftVector(Quaternion.Inverse(airSpeedRot) * airSpeed)));
                        ctrlTorqueAvailable.Add(ctrlTroquePos);
                        ctrlTorqueAvailable.Add(ctrlTroqueNeg);
                    }

                    if (pm.ClassName == "ModuleEngineConfigs" || pm.ClassName == "ModuleHybridEngine" || pm.ClassName == "ModuleHybridEngines")
                    {
                        hasMFE = true;
                    }

                    foreach (VesselStatePartModuleExtension vspme in vesselStatePartModuleExtensions)
                    {
                        vspme(pm);
                    }
                }
            }

            // Consider all the parachutes
            {
                bool tempParachuteDeployed = false;
                foreach (ModuleParachute p in parachutes)
                {
                    if (p.deploymentState == ModuleParachute.deploymentStates.DEPLOYED || p.deploymentState == ModuleParachute.deploymentStates.SEMIDEPLOYED)
                    {
                        tempParachuteDeployed = true;
                        break;
                    }
                }
                this.parachuteDeployed = tempParachuteDeployed;
            }

            torqueAvailable += Vector3d.Max(rcsTorqueAvailable.positive, rcsTorqueAvailable.negative);   // Should we use Max or Min ?
            torqueAvailable += Vector3d.Max(ctrlTorqueAvailable.positive, ctrlTorqueAvailable.negative); // Should we use Max or Min ?

            thrustVectorMaxThrottle += einfo.thrustMax;
            thrustVectorMinThrottle += einfo.thrustMin;
            thrustVectorLastFrame   += einfo.thrustCurrent;
            torqueThrustPYAvailable += einfo.torqueThrustPYAvailable;

            if (thrustVectorMaxThrottle.magnitude == 0 && vessel.ActionGroups[KSPActionGroup.RCS])
            {
                rcsThrust = true;
                thrustVectorMaxThrottle += (Vector3d)(vessel.transform.up) * rcsThrustAvailable.down;
            }
            else
            {
                rcsThrust = false;
            }

            // Convert the resource information from the einfo and iinfo format
            // to the more useful ResourceInfo format.
            resources = new Dictionary <int, ResourceInfo>();
            foreach (var info in einfo.resourceRequired)
            {
                int id  = info.Key;
                var req = info.Value;
                resources[id] = new ResourceInfo(
                    PartResourceLibrary.Instance.GetDefinition(id),
                    req.requiredLastFrame,
                    req.requiredAtMaxThrottle,
                    iinfo.getIntakes(id));
            }

            int intakeAirId = PartResourceLibrary.Instance.GetDefinition("IntakeAir").id;

            intakeAir           = 0;
            intakeAirNeeded     = 0;
            intakeAirAtMax      = 0;
            intakeAirAllIntakes = 0;
            if (resources.ContainsKey(intakeAirId))
            {
                intakeAir           = resources[intakeAirId].intakeProvided;
                intakeAirAllIntakes = resources[intakeAirId].intakeAvailable;
                intakeAirNeeded     = resources[intakeAirId].required;
                intakeAirAtMax      = resources[intakeAirId].requiredAtMaxThrottle;
            }

            angularMomentum = new Vector3d(angularVelocity.x * MoI.x, angularVelocity.y * MoI.y, angularVelocity.z * MoI.z);

            inertiaTensor = new Matrix3x3();
            foreach (Part p in vessel.parts)
            {
                if (p.Rigidbody == null)
                {
                    continue;
                }

                //Compute the contributions to the vessel inertia tensor due to the part inertia tensor
                Vector3d   principalMoments = p.Rigidbody.inertiaTensor;
                Quaternion princAxesRot     = Quaternion.Inverse(vessel.GetTransform().rotation) * p.transform.rotation * p.Rigidbody.inertiaTensorRotation;
                Quaternion invPrincAxesRot  = Quaternion.Inverse(princAxesRot);

                for (int i = 0; i < 3; i++)
                {
                    Vector3d iHat = Vector3d.zero;
                    iHat[i] = 1;
                    for (int j = 0; j < 3; j++)
                    {
                        Vector3d jHat = Vector3d.zero;
                        jHat[j]              = 1;
                        inertiaTensor[i, j] += Vector3d.Dot(iHat, princAxesRot * Vector3d.Scale(principalMoments, invPrincAxesRot * jHat));
                    }
                }

                //Compute the contributions to the vessel inertia tensor due to the part mass and position
                double  partMass     = p.TotalMass();
                Vector3 partPosition = vessel.GetTransform().InverseTransformDirection(p.Rigidbody.worldCenterOfMass - CoM);

                for (int i = 0; i < 3; i++)
                {
                    inertiaTensor[i, i] += partMass * partPosition.sqrMagnitude;

                    for (int j = 0; j < 3; j++)
                    {
                        inertiaTensor[i, j] += -partMass * partPosition[i] * partPosition[j];
                    }
                }
            }

            MoI             = new Vector3d(inertiaTensor[0, 0], inertiaTensor[1, 1], inertiaTensor[2, 2]);
            angularMomentum = inertiaTensor * angularVelocity;
        }
        protected void StartSimulation(bool addParachuteError)
        {
            double altitudeOfPreviousPrediction         = 0;
            double parachuteMultiplierForThisSimulation = this.parachuteSemiDeployMultiplier;

            if (addParachuteError)
            {
                errorSimulationRunning = true;
                errorStopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            else
            {
                simulationRunning = true;
                stopwatch.Start(); //starts a timer that times how long the simulation takes
            }

            Orbit patch = GetReenteringPatch() ?? orbit;

            // Work out a mass for the total ship, a DragMass for everything except the parachutes that will be used (including the stowed parachutes that will not be used) and list of parchutes that will be used.
            double totalMass = 0;
            double dragMassExcludingUsedParachutes = 0;
            List <SimulatedParachute> usableChutes = new List <SimulatedParachute>();

            for (int index = 0; index < vessel.parts.Count; index++)
            {
                Part p = vessel.parts[index];
                if (p.IsPhysicallySignificant())
                {
                    bool   partIsParachute = false;
                    double partDrag        = 0;
                    double partMass        = p.TotalMass();

                    totalMass += partMass;

                    // Is this part a parachute?
                    for (int i = 0; i < p.Modules.Count; i++)
                    {
                        PartModule pm = p.Modules[i];
                        if (!pm.isEnabled)
                        {
                            continue;
                        }

                        if (pm is ModuleParachute)
                        {
                            ModuleParachute chute = (ModuleParachute)pm;
                            // This is a parachute, but is it one that will be used in the landing / rentry simulation?
                            if (deployChutes && p.inverseStage >= limitChutesStage)
                            {
                                // This chute will be used in the simualtion. Add it to the list of useage parachutes.
                                usableChutes.Add(new SimulatedParachute(chute, patch.StartUT));
                                partIsParachute = true;
                            }
                        }
                    }

                    if (!partIsParachute)
                    {
                        // Part is not a parachute. Just use its drag value.
                        partDrag = p.maximum_drag;
                    }

                    dragMassExcludingUsedParachutes += partDrag * partMass;
                }
            }

            // Work out what the landing altitude was of the last prediction, and use that to pass into the next simulation
            if (null != this.result)
            {
                if (result.outcome == ReentrySimulation.Outcome.LANDED && null != result.body)
                {
                    altitudeOfPreviousPrediction = this.GetResult().endASL; // Note that we are caling GetResult here to force the it to calculate the endASL, if it has not already done this. It is not allowed to do this previously as we are only allowed to do it from this thread, not the reentry simulation thread.
                }
            }

            // Is this a simulation run with errors added? If so then add some error to the parachute multiple
            if (addParachuteError)
            {
                System.Random random = new System.Random();
                parachuteMultiplierForThisSimulation *= (1d + ((random.Next(1000000) - 500000d) / 10000000d));
            }

            // The curves used for the simes are not thread safe so we need a copy used only by the thread
            ReentrySimulation.SimCurves simCurves = new ReentrySimulation.SimCurves(patch.referenceBody);

            SimulatedVessel simVessel = SimulatedVessel.New(vessel, simCurves);

            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, usableChutes, simVessel, simCurves, descentSpeedPolicy, decelEndAltitudeASL, vesselState.limitedMaxThrustAccel, parachuteMultiplierForThisSimulation, altitudeOfPreviousPrediction, addParachuteError, dt, Time.fixedDeltaTime);

            MechJebCore.print("Sim ran with dt=" + dt.ToString("F3"));

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
            //RunSimulation(sim);
        }