public SimCurves(CelestialBody mainBody) { dragCurveMultiplier = new FloatCurve(PhysicsGlobals.DragCurveMultiplier.Curve.keys); dragCurveSurface = new FloatCurve(PhysicsGlobals.DragCurveSurface.Curve.keys); dragCurveTail = new FloatCurve(PhysicsGlobals.DragCurveTail.Curve.keys); dragCurveTip = new FloatCurve(PhysicsGlobals.DragCurveTip.Curve.keys); liftCurve = new FloatCurve(PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftCurve.Curve.keys); liftMachCurve = new FloatCurve(PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Curve.keys); atmospherePressureCurve = new FloatCurve(mainBody.atmospherePressureCurve.Curve.keys); atmosphereTemperatureSunMultCurve = new FloatCurve(mainBody.atmosphereTemperatureSunMultCurve.Curve.keys); latitudeTemperatureBiasCurve = new FloatCurve(mainBody.latitudeTemperatureBiasCurve.Curve.keys); latitudeTemperatureSunMultCurve = new FloatCurve(mainBody.latitudeTemperatureSunMultCurve.Curve.keys); atmosphereTemperatureCurve = new FloatCurve(mainBody.atmosphereTemperatureCurve.Curve.keys); axialTemperatureSunMultCurve = new FloatCurve(mainBody.axialTemperatureSunMultCurve.Curve.keys); }
//******************************************************* public static Vector3 SimAeroForce(Vessel _vessel, Vector3 v_wrld_vel, double altitude, double latitude = 0) { CelestialBody body = _vessel.mainBody; double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. Vector3d total_lift = Vector3d.zero; Vector3d total_drag = Vector3d.zero; // dynamic pressure for standard drag equation double rho = GetDensity(altitude, body); double dyn_pressure = 0.0005 * rho * v_wrld_vel.sqrMagnitude; if (rho <= 0) { return(Vector3.zero); } double soundSpeed = body.GetSpeedOfSound(pressure, rho); double mach = v_wrld_vel.magnitude / soundSpeed; if (mach > 25.0) { mach = 25.0; } // Loop through all parts, accumulating drag and lift. for (int i = 0; i < _vessel.Parts.Count; ++i) { // need checks on shielded components Part p = _vessel.Parts[i]; if (p.ShieldedFromAirstream || p.Rigidbody == null) { continue; } // Get Drag Vector3 sim_dragVectorDir = v_wrld_vel.normalized; Vector3 sim_dragVectorDirLocal = -(p.transform.InverseTransformDirection(sim_dragVectorDir)); Vector3 liftForce = new Vector3(0, 0, 0); Vector3d dragForce; switch (p.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: DragCubeList cubes = p.DragCubes; DragCubeList.CubeData p_drag_data = new DragCubeList.CubeData(); float drag; if (cubes.None) // since 1.0.5, some parts don't have drag cubes (for example fuel lines and struts) { drag = p.maximum_drag; } else { try { cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); } catch (Exception) { cubes.SetDrag(sim_dragVectorDirLocal, (float)mach); cubes.ForceUpdate(true, true); cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); //Debug.Log(String.Format("Trajectories: Caught NRE on Drag Initialization. Should be fixed now. {0}", e)); } float pseudoreynolds = (float)(rho * Mathf.Abs(v_wrld_vel.magnitude)); float pseudoredragmult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(pseudoreynolds); drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoredragmult; liftForce = p_drag_data.liftForce; } double sim_dragScalar = dyn_pressure * (double)drag * PhysicsGlobals.DragMultiplier; dragForce = -(Vector3d)sim_dragVectorDir * sim_dragScalar; break; case Part.DragModel.SPHERICAL: dragForce = -(Vector3d)sim_dragVectorDir * (double)p.maximum_drag; break; case Part.DragModel.CYLINDRICAL: dragForce = -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Mathf.Abs(Vector3.Dot(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir))); break; case Part.DragModel.CONIC: dragForce = -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Vector3.Angle(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir) / 180f); break; default: // no drag to apply dragForce = new Vector3d(); break; } total_drag += dragForce; // If it isn't a wing or lifter, get body lift. if (!p.hasLiftModule) { float simbodyLiftScalar = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * (float)dyn_pressure; simbodyLiftScalar *= PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 bodyLift = p.transform.rotation * (simbodyLiftScalar * liftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, sim_dragVectorDir); // Only accumulate forces for non-LiftModules //Debug.Log("[Trajectories] bodyLift=" + bodyLift); total_lift += bodyLift; } // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass for (int j = 0; j < p.Modules.Count; ++j) { var m = p.Modules[j]; float mcs_mod; if (m is ModuleLiftingSurface) { mcs_mod = 1.0f; double liftQ = dyn_pressure * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(v_wrld_vel, out nVel, out liftVector, out liftdot, out absdot); double prevMach = p.machNumber; p.machNumber = mach; Vector3 local_lift = mcs_mod * wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); Vector3 local_drag = mcs_mod * wing.GetDragVector(nVel, absdot, liftQ); p.machNumber = prevMach; total_lift += local_lift; total_drag += local_drag; } } } // RETURN STUFF //Debug.Log("[Trajectories] total_lift" + total_lift + " total_drag=" + total_drag); Vector3 force = total_lift + total_drag; return(force); }
void Start() { Debug.Log("[CorrectCoL]: Starting!"); if (!far_searched) { foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { if (a.GetName().Name.Equals("FerramAerospaceResearch")) { far_found = true; break; } } far_searched = true; } if (far_found) { Debug.Log("[CorrectCoL]: FAR found, disabling itself!"); GameObject.Destroy(this.gameObject); return; } overlays = EditorVesselOverlays.fetch; if (overlays == null) { Debug.Log("[CorrectCoL]: overlays is null!"); GameObject.Destroy(this.gameObject); return; } old_CoL_marker = overlays.CoLmarker; if (old_CoL_marker == null) { Debug.Log("[CorrectCoL]: CoL_marker is null!"); GameObject.Destroy(this.gameObject); return; } bodylift_curves = PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift"); if (new_CoL_marker == null) { new_CoL_marker = this.gameObject.AddComponent <CoLMarkerFull>(); CoLMarkerFull.lift_curves = bodylift_curves; new_CoL_marker.posMarkerObject = (GameObject)GameObject.Instantiate(old_CoL_marker.dirMarkerObject); new_CoL_marker.posMarkerObject.transform.parent = new_CoL_marker.transform; new_CoL_marker.posMarkerObject.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); new_CoL_marker.posMarkerObject.SetActive(false); new_CoL_marker.posMarkerObject.layer = 2; foreach (Transform child in new_CoL_marker.posMarkerObject.transform) { child.gameObject.layer = 2; } GameEvents.onEditorRestart.Add(new EventVoid.OnEvent(TurnOffCoL)); // should be called once, so let's deserialize graph here too GraphWindow.load_settings(); GraphWindow.init_textures(true); GraphWindow.init_reflections(); clickEvent = new Button.ButtonClickedEvent(); clickEvent.AddListener(ToggleCoL); } GameEvents.onGUIApplicationLauncherReady.Add(onAppLauncherLoad); GameEvents.onGUIApplicationLauncherUnreadifying.Add(onAppLauncherUnload); onAppLauncherLoad(); GraphWindow.shown = false; new_CoL_marker.enabled = false; old_CoL_marker.gameObject.SetActive(false); overlays.toggleCoLbtn.onClick = clickEvent; //overlays.toggleCoLbtn.methodToInvoke = "ToggleCoL"; }
//******************************************************* public static Vector3d SimAeroForce(Vessel vessel, Vector3d v_wrld_vel, double altitude, double latitude = 0.0) { Profiler.Start("SimAeroForce"); CelestialBody body = vessel.mainBody; double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. Vector3d total_lift = Vector3d.zero; Vector3d total_drag = Vector3d.zero; // dynamic pressure for standard drag equation double rho = GetDensity(altitude, body); double dyn_pressure = 0.0005 * rho * v_wrld_vel.sqrMagnitude; if (rho <= 0) { return(Vector3.zero); } double soundSpeed = body.GetSpeedOfSound(pressure, rho); double mach = v_wrld_vel.magnitude / soundSpeed; if (mach > 25.0) { mach = 25.0; } // Loop through all parts, accumulating drag and lift. for (int i = 0; i < vessel.Parts.Count; ++i) { // need checks on shielded components Part p = vessel.Parts[i]; #if DEBUG TrajectoriesDebug partDebug = VesselAerodynamicModel.DebugParts ? p.FindModuleImplementing <TrajectoriesDebug>() : null; if (partDebug != null) { partDebug.Drag = 0; partDebug.Lift = 0; } #endif if (p.ShieldedFromAirstream || p.Rigidbody == null) { continue; } // Get Drag Vector3d sim_dragVectorDir = v_wrld_vel.normalized; Vector3d sim_dragVectorDirLocal = -(p.transform.InverseTransformDirection(sim_dragVectorDir)); Vector3d liftForce = Vector3d.zero; Vector3d dragForce; Profiler.Start("SimAeroForce#drag"); switch (p.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: DragCubeList cubes = p.DragCubes; DragCubeList.CubeData p_drag_data = new DragCubeList.CubeData(); double drag; if (cubes.None) // since 1.0.5, some parts don't have drag cubes (for example fuel lines and struts) { drag = p.maximum_drag; } else { try { cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); } catch (Exception e) { cubes.SetDrag(sim_dragVectorDirLocal, (float)mach); cubes.ForceUpdate(true, true); cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); Util.DebugLogError("Exception {0} on drag initialization", e); } double pseudoreynolds = rho * Math.Abs(v_wrld_vel.magnitude); double pseudoredragmult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)pseudoreynolds); drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoredragmult; liftForce = p_drag_data.liftForce; } double sim_dragScalar = dyn_pressure * drag * PhysicsGlobals.DragMultiplier; dragForce = -sim_dragVectorDir * sim_dragScalar; break; case Part.DragModel.SPHERICAL: dragForce = -sim_dragVectorDir * p.maximum_drag; break; case Part.DragModel.CYLINDRICAL: dragForce = -sim_dragVectorDir *Mathf.Lerp(p.minimum_drag, p.maximum_drag, Mathf.Abs(Vector3.Dot(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir))); break; case Part.DragModel.CONIC: dragForce = -sim_dragVectorDir *Mathf.Lerp(p.minimum_drag, p.maximum_drag, Vector3.Angle(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir) / 180f); break; default: // no drag to apply dragForce = Vector3d.zero; break; } Profiler.Stop("SimAeroForce#drag"); #if DEBUG if (partDebug != null) { partDebug.Drag += (float)dragForce.magnitude; } #endif total_drag += dragForce; // If it isn't a wing or lifter, get body lift. if (!p.hasLiftModule) { Profiler.Start("SimAeroForce#BodyLift"); float simbodyLiftScalar = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * (float)dyn_pressure; simbodyLiftScalar *= PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 bodyLift = p.transform.rotation * (simbodyLiftScalar * liftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, sim_dragVectorDir); // Only accumulate forces for non-LiftModules total_lift += bodyLift; Profiler.Stop("SimAeroForce#BodyLift"); } Profiler.Start("SimAeroForce#LiftingSurface"); // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass for (int j = 0; j < p.Modules.Count; ++j) { var m = p.Modules[j]; if (m is ModuleLiftingSurface wing) { const float mcs_mod = 1.0f; double liftQ = dyn_pressure * 1000; wing.SetupCoefficients(v_wrld_vel, out var nVel, out var liftVector, out var liftdot, out var absdot); double prevMach = p.machNumber; p.machNumber = mach; Vector3 local_lift = mcs_mod * wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); Vector3 local_drag = mcs_mod * wing.GetDragVector(nVel, absdot, liftQ); p.machNumber = prevMach; total_lift += local_lift; total_drag += local_drag; #if DEBUG if (partDebug != null) { partDebug.Lift += local_lift.magnitude; partDebug.Drag += local_drag.magnitude; } #endif } } Profiler.Stop("SimAeroForce#LiftingSurface"); } // RETURN STUFF Vector3 force = total_lift + total_drag; Profiler.Stop("SimAeroForce"); return(force); }
//Retrieve the vector sum of the lift and drag forces on the part when wind is present. public List <Vector3> GetDragLiftForce(ModularFI.ModularFlightIntegrator fi, Part p, Vector3d vel, Vector3d windVec, double mach, bool isgrounded) { //Initialize forces Vector3 total_drag = Vector3d.zero; Vector3 total_lift = Vector3d.zero; List <Vector3> total = new List <Vector3>(); //Set Part drag based on new air velocity p.dragVector = vel; p.dragVectorSqrMag = p.dragVector.sqrMagnitude; //Update dynamic pressure p.dynamicPressurekPa = 0.0005 * p.atmDensity * p.dragVectorSqrMag; //If part darg is near zero or part is shielded do not calculate force sum if (p.dragVectorSqrMag.NearlyEqual(0) || p.ShieldedFromAirstream) { p.dragVectorMag = 0f; p.dragVectorDir = Vector3.zero; p.dragVectorDirLocal = Vector3.zero; p.dragScalar = 0f; } //Update drag vector p.dragVectorMag = (float)Math.Sqrt(p.dragVectorSqrMag); p.dragVectorDir = p.dragVector / p.dragVectorMag; p.dragVectorDirLocal = -p.transform.InverseTransformDirection(p.dragVectorDir); if (((!p.DragCubes.None) && (!p.hasLiftModule)) || (p.name.Contains("kerbalEVA"))) { //Apply drag vector direction to drag cube Vector3 drag_force = Vector3.zero; //Update angular drag (adapated from FAR) p.DragCubes.SetDrag(p.dragVectorDirLocal, (float)mach); //Calculate drag force and set dragScalar (adapated from Trajectories mod) double pseudoreynolds = p.atmDensity * Math.Abs(vel.magnitude); double pseudoReDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)pseudoreynolds); double drag = p.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoReDragMult; p.dragScalar = (float)(p.dynamicPressurekPa * drag * PhysicsGlobals.DragMultiplier); //Add drag force to total drag drag_force = -p.dragVectorDir * p.dragScalar; total_drag += drag_force; //Estimate lift force from body lift p.bodyLiftScalar = (float)(p.dynamicPressurekPa * p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier) * PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 lforce = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); lforce = Vector3.ProjectOnPlane(lforce, -p.dragVectorDir); //Add lift force if vessel is on the ground total_lift += lforce; } for (int j = 0; j < p.Modules.Count; ++j) { var m = p.Modules[j]; if (m is ModuleLiftingSurface) { //Initialize force vectors Vector3 lforce3 = Vector3.zero; Vector3 dforce3 = Vector3.zero; //Convert kPa to Pa double liftQ = p.dynamicPressurekPa * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; //Get wing coefficients Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(vel, out nVel, out liftVector, out liftdot, out absdot); //Get Lift/drag force lforce3 = wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); dforce3 = wing.GetDragVector(nVel, absdot, liftQ); //wing.dragForce = dforce3; if (p.name.Contains("kerbalEVA")) { continue; } total_drag += dforce3; if (isgrounded) { total_lift += lforce3; } } } total.Add(total_drag); total.Add(total_lift); return(total); }
//Update aerodynamics //Adapted from KSP Trajectories, Kerbal Wind Tunnel, FAR, and AeroGUI void UpdateAerodynamics(ModularFI.ModularFlightIntegrator fi, Part part) { Vessel v = FlightGlobals.ActiveVessel; wx_enabled = Util.getWindBool(); //Is weather enabled? use_climo = Util.useCLIM(); //Are we using the climatology use_point = Util.useWX(); //Are we using MPAS point time-series //Get main vessel and get reference to Kerbin (i.e. celestial body) kerbin = Util.getbody(); //Get wind vector (initialize to zero) Vector3d windVec = Vector3d.zero; if (use_climo) { //Retrieve wind vector from climatology windVec = KerbalWxClimo.getWSWind(); // .GetWind(FlightGlobals.currentMainBody, part, rb.position); } else if (use_point) { //Retrieve wind vector from point forecast windVec = KerbalWxPoint.getWSWind(); // .GetWind(FlightGlobals.currentMainBody, part, rb.position); } //Check to see if root part is in list. If not do not perform aero update. //This avoids updating aerodynamics of parts that have been decoupled and are no longer part of the active vessel. bool hasPart = false; for (int i = 0; i < fi.PartThermalDataCount; i++) { PartThermalData pttd = fi.partThermalDataList[i]; Part ppart = pttd.part; if (ppart == v.rootPart) { hasPart = true; } } if (!hasPart) { return; } //Get position of current vessel double vheight = v.altitude; double vlat = v.latitude; double vlng = v.longitude; double air_pressure; double air_density; double soundSpeed; //Define gamma (i.e. ratio between specific heat of dry air at constant pressure and specific heat of dry air at constant volume: cp/cv). double gamma = 1.4; //Util.Log("MFI wx_enabled: " + wx_enabled+", use_point: "+use_point+", use_climo: "+use_climo); bool inatmos = false; if ((FlightGlobals.ActiveVessel.mainBody == kerbin) && (v.altitude <= 70000) && (wx_enabled)) { if (use_climo) { //Retrieve air pressure/density at vessel location climate_api.wx_aero ptd = _clim_api.getPTD(vlat, vlng, vheight); air_density = ptd.density; air_pressure = ptd.pressure; } else { //Retrieve air pressure/density at vessel location weather_api.wx_aero ptd = _wx_api.getPTD(vheight); air_density = ptd.density; air_pressure = ptd.pressure; } //Compute speed of sound based on air pressure and air density. soundSpeed = Math.Sqrt(gamma * (air_pressure / air_density)); inatmos = true; } else { soundSpeed = v.speedOfSound; } //Get height from surface double hsfc = v.heightFromTerrain; //Determine if vessel is grounded (i.e. on the ground) bool isgrounded = true; if (hsfc >= 10) { isgrounded = false; } //If FAR is present FAR will handle aerodynamic updates or if we're not on Kerbin (i.e. in a different atmosphere) if (part.Modules.Contains <ModuleAeroSurface>() || (part.Modules.Contains("MissileLauncher") && part.vessel.rootPart == part) || (haveFAR) || (v.mainBody != kerbin) || (!inatmos)) { fi.BaseFIUpdateAerodynamics(part); if (((!part.DragCubes.None) && (!part.hasLiftModule)) || (part.name.Contains("kerbalEVA"))) { double drag = part.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * fi.pseudoReDragMult; float pscal = (float)(part.dynamicPressurekPa * drag * PhysicsGlobals.DragMultiplier); //Estimate lift force from body lift Vector3 lforce = part.transform.rotation * (part.bodyLiftScalar * part.DragCubes.LiftForce); lforce = Vector3.ProjectOnPlane(lforce, -part.dragVectorDir); } return; } else { fi.BaseFIUpdateAerodynamics(part); //Run base aerodynamic update first (fix bug where parachutes experience multiplicative acceleration) // Get rigid body Rigidbody rb = part.rb; if (rb) { if (part == v.rootPart) { v.mach = 0; } //Retrieve wind vector at vessel location //Vector3d windVec = KWPWind.GetWind(FlightGlobals.currentMainBody, part, rb.position); //Util.Log("MFI windVec: " + windVec + ", use_point: " + use_point + ", use_climo: " + use_climo); //Retrieve world velocity without wind vector Vector3 velocity_nowind = rb.velocity + Krakensbane.GetFrameVelocity(); //Compute mach number double mach_nowind = velocity_nowind.magnitude / soundSpeed; Vector3 drag_sum = Vector3.zero; Vector3 lift_sum = Vector3.zero; if (((!part.DragCubes.None) && (!part.hasLiftModule)) || (part.name.Contains("kerbalEVA"))) { //Estimate Drag force double pseudoreynolds = part.atmDensity * Math.Abs(velocity_nowind.magnitude); double pseudoReDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)pseudoreynolds); double drag = part.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoReDragMult; float pscal = (float)(part.dynamicPressurekPa * drag * PhysicsGlobals.DragMultiplier); drag_sum += -part.dragVectorDir * pscal; //Estimate lift force from body lift float pbodyLiftScalar = (float)(part.dynamicPressurekPa * part.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier) * PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)part.machNumber); Vector3 lforce2 = part.transform.rotation * (pbodyLiftScalar * part.DragCubes.LiftForce); lforce2 = Vector3.ProjectOnPlane(lforce2, -part.dragVectorDir); lift_sum += lforce2; } //Util.Log("BodyLift Default: " + part.bodyLiftScalar + ", Lift Force: " + lift_force); //Loop through each part module to look for lifting surfaces for (int j = 0; j < part.Modules.Count; ++j) { var m = part.Modules[j]; if (m is ModuleLiftingSurface) { //Initialize force vectors Vector3 lforce = Vector3.zero; Vector3 dforce = Vector3.zero; //Convert kPa to Pa double liftQ = part.dynamicPressurekPa * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; //Get wing coefficients Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(velocity_nowind, out nVel, out liftVector, out liftdot, out absdot); //Get Lift/drag force lforce = wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)part.machNumber); dforce = wing.GetDragVector(nVel, absdot, liftQ); if (part.name.Contains("kerbalEVA")) { continue; } drag_sum += dforce; //lift_sum += lforce; if (isgrounded) { lift_sum += lforce; } } } //Retrieve world velocity and subtract off wind vector Vector3 velocity_wind = rb.velocity + Krakensbane.GetFrameVelocity() - windVec; //Compute mach number double mach_wind = velocity_wind.magnitude / soundSpeed; //Set mach number part.machNumber = mach_wind; // *mdiv; if (part == v.rootPart) { fi.mach = mach_wind; } //Get drag and lift forces with wind List <Vector3> total_forces = GetDragLiftForce(fi, part, velocity_wind, windVec, part.machNumber, isgrounded); //Compute difference in drag/lift with and without wind. Vector3 total_drag = total_forces[0] - drag_sum; Vector3 total_lift = total_forces[1] - lift_sum; //Calculate force due to wind Vector3 force = total_lift + total_drag; if (double.IsNaN(force.sqrMagnitude) || (((float)force.magnitude).NearlyEqual(0.0f))) { force = Vector3d.zero; } else { //Adapted from FAR - apply numerical control factor float numericalControlFactor = (float)(part.rb.mass * velocity_wind.magnitude * 0.67 / (force.magnitude * TimeWarp.fixedDeltaTime)); force *= Math.Min(numericalControlFactor, 1); part.AddForce(force); } v.mach = fi.mach; } } }
//******************************************************* public static Vector3 SimLiftForce(CelestialBody body, IShipconstruct vessel, Vector3 v_wrld_vel, double altitude, double latitude = 0.0) { //Profiler.Start("SimLiftForce"); double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. Vector3d total_lift = Vector3d.zero; // dynamic pressure for standard drag equation double rho = GetDensity(altitude, body); double dyn_pressure = 0.0005 * rho * v_wrld_vel.sqrMagnitude; if (rho <= 0) { return(Vector3.zero); } double soundSpeed = body.GetSpeedOfSound(pressure, rho); double mach = v_wrld_vel.magnitude / soundSpeed; if (mach > 25.0) { mach = 25.0; } // Loop through all parts, accumulating drag and lift. for (int i = vessel.Parts.Count - 1; i >= 0; i--) { // need checks on shielded components Part part = vessel.Parts[i]; if (part.ShieldedFromAirstream || part.Rigidbody == null) { continue; } // Get Drag Vector3 sim_dragVectorDir = v_wrld_vel.normalized; Vector3 sim_dragVectorDirLocal = -(part.transform.InverseTransformDirection(sim_dragVectorDir)); Vector3 liftForce = new Vector3(0, 0, 0); //Profiler.Start("SimLiftForce#Body"); switch (part.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: DragCubeList cubes = part.DragCubes; DragCubeList.CubeData p_drag_data = new DragCubeList.CubeData(); if (!cubes.None) // since 1.0.5, some parts don't have drag cubes (for example fuel lines and struts) { try { cubes.SetDragWeights(); cubes.SetPartOcclusion(); cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); } catch (Exception) { cubes.SetDrag(sim_dragVectorDirLocal, (float)mach); cubes.ForceUpdate(true, true); cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach, ref p_drag_data); //Debug.Log(String.Format("Trajectories: Caught NRE on Drag Initialization. Should be fixed now. {0}", e)); } float pseudoreynolds = (float)(rho * Mathf.Abs(v_wrld_vel.magnitude)); float pseudoredragmult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(pseudoreynolds); liftForce = p_drag_data.liftForce; } break; } // If it isn't a wing or lifter, get body lift. if (!part.hasLiftModule) { float simbodyLiftScalar = part.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * (float)dyn_pressure; simbodyLiftScalar *= PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 bodyLift = part.transform.rotation * (simbodyLiftScalar * liftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, sim_dragVectorDir); // Only accumulate forces for non-LiftModules total_lift += bodyLift; } //Profiler.Stop("SimLiftForce#Body"); // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass //Profiler.Start("SimLiftForce#LiftModule"); for (int j = part.Modules.Count - 1; j >= 0; j--) { if (part.Modules[j] is ModuleLiftingSurface) { float mcs_mod = 1.0f; double liftQ = dyn_pressure * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)part.Modules[j]; Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(v_wrld_vel, out nVel, out liftVector, out liftdot, out absdot); double prevMach = part.machNumber; part.machNumber = mach; Vector3 local_lift = mcs_mod * wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); part.machNumber = prevMach; total_lift += local_lift; } } //Profiler.Stop("SimLiftForce#LiftModule"); } // RETURN STUFF //Profiler.Stop("SimLiftForce"); return(total_lift); }
//******************************************************* public static Vector3 SimAeroForce(List <Part> parts, CelestialBody body, Vector3 v_wrld_vel, double altitude, double latitude = 0.0) { double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. Vector3d total_lift = Vector3d.zero; Vector3d total_drag = Vector3d.zero; // dynamic pressure for standard drag equation double rho = GetDensity(altitude, body); double dyn_pressure = 0.0005 * rho * v_wrld_vel.sqrMagnitude; if (rho <= 0) { return(Vector3.zero); } double soundSpeed = body.GetSpeedOfSound(pressure, rho); double mach = v_wrld_vel.magnitude / soundSpeed; if (mach > 25.0) { mach = 25.0; } // Loop through all parts, accumulating drag and lift. foreach (Part p in parts) { // need checks on shielded components if (p.ShieldedFromAirstream || p.Rigidbody == null) { continue; } // Get Drag Vector3 sim_dragVectorDir = v_wrld_vel.normalized; Vector3 sim_dragVectorDirLocal = -(p.transform.InverseTransformDirection(sim_dragVectorDir)); Vector3 liftForce = new Vector3(0, 0, 0); switch (p.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: SmartDragCubeList cubes = new SmartDragCubeList(p, parts); DragCubeList.CubeData p_drag_data; float drag; if (cubes.None) // since 1.0.5, some parts don't have drag cubes (for example fuel lines and struts) { drag = p.maximum_drag; } else { p_drag_data = cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach); drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier; liftForce = p_drag_data.liftForce; } double sim_dragScalar = dyn_pressure * (double)drag * PhysicsGlobals.DragMultiplier; total_drag += -(Vector3d)sim_dragVectorDir * sim_dragScalar; break; case Part.DragModel.SPHERICAL: total_drag += -(Vector3d)sim_dragVectorDir * (double)p.maximum_drag; break; case Part.DragModel.CYLINDRICAL: total_drag += -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Mathf.Abs(Vector3.Dot(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir))); break; case Part.DragModel.CONIC: total_drag += -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Vector3.Angle(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir) / 180f); break; default: // no drag to apply break; } // If it isn't a wing or lifter, get body lift. if (!p.hasLiftModule) { float simbodyLiftScalar = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * (float)dyn_pressure; simbodyLiftScalar *= PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 bodyLift = p.transform.rotation * (simbodyLiftScalar * liftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, sim_dragVectorDir); // Only accumulate forces for non-LiftModules total_lift += bodyLift; } // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass foreach (var m in p.Modules.OfType <ModuleLiftingSurface>()) { float mcs_mod = 1.0f; double liftQ = dyn_pressure * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(v_wrld_vel, out nVel, out liftVector, out liftdot, out absdot); float simLiftScalar = Mathf.Sign(liftdot) * wing.liftCurve.Evaluate(absdot) * wing.liftMachCurve.Evaluate((float)mach); simLiftScalar *= wing.deflectionLiftCoeff; simLiftScalar = (float)(liftQ * (double)(PhysicsGlobals.LiftMultiplier * simLiftScalar)); float simdragScalar = wing.dragCurve.Evaluate(absdot) * wing.dragMachCurve.Evaluate((float)mach); simdragScalar *= wing.deflectionLiftCoeff; simdragScalar = (float)(liftQ * (double)(simdragScalar * PhysicsGlobals.LiftDragMultiplier)); Vector3 local_lift = mcs_mod * wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); Vector3 local_drag = mcs_mod * wing.GetDragVector(nVel, absdot, liftQ); total_lift += local_lift; total_drag += local_drag; } } // RETURN STUFF Vector3 force = total_lift + total_drag; return(1000 * force); }