void Start() { if (waterSlowDragNew < 0) { waterSlowDragNew = PhysicsGlobals.BuoyancyWaterDragSlow; minVelVesselMultNew = (float)PhysicsGlobals.BuoyancyWaterDragPartVelGreaterVesselMult; PhysicsGlobals.BuoyancyWaterDragPartVelGreaterVesselMult = 0; } part.maximum_drag = 0; part.minimum_drag = 0; part.angularDrag = 0; if (HighLogic.LoadedSceneIsFlight) { this.enabled = true; } else if (HighLogic.LoadedSceneIsEditor) { this.enabled = false; } partLocalVel = Vector3.zero; partLocalForce = Vector3.zero; partLocalTorque = Vector3.zero; //if (!part.Modules.Contains("ModuleAeroSurface")) // part.dragModel = Part.DragModel.CYLINDRICAL; if (FARDebugValues.allowStructuralFailures) { FARPartStressTemplate template = FARAeroStress.DetermineStressTemplate(this.part); partStressMaxY = template.YmaxStress; partStressMaxXZ = template.XZmaxStress; } partTransform = part.partTransform; materialColorUpdater = new MaterialColorUpdater(partTransform, PhysicsGlobals.TemperaturePropertyID); if (part.Modules.Contains <FARWingAerodynamicModel>()) { legacyWingModel = part.Modules.GetModule <FARWingAerodynamicModel>(); } else if (part.Modules.Contains <FARControllableSurface>()) { legacyWingModel = part.Modules.GetModule <FARControllableSurface>(); } else { legacyWingModel = null; } // For handling airbrakes aero visualization if (part.Modules.Contains <ModuleAeroSurface>()) { stockAeroSurfaceModule = part.Modules.GetModule <ModuleAeroSurface>(); } else { stockAeroSurfaceModule = null; } }
// Stock aero force calculation by NathanKell private static Vector3 VesselStockAeroForces(Vessel vessel) { Vector3 aeroForce = Vector3.zero; for (int i = 0; i < vessel.parts.Count; i++) { Part part = vessel.parts[i]; aeroForce += -part.dragVectorDir * part.dragScalar; if (!part.hasLiftModule) { Vector3 bodyLift = part.transform.rotation * (part.bodyLiftScalar * part.DragCubes.LiftForce); aeroForce += Vector3.ProjectOnPlane(bodyLift, -part.dragVectorDir); } else { for (int j = 0; j < part.Modules.Count; j++) { ModuleLiftingSurface module = part.Modules[j] as ModuleLiftingSurface; if (module == null) continue; aeroForce += module.liftForce; aeroForce += module.dragForce; } } } return aeroForce; }
private void CountSurfaces() { print("Counting wings for " + vessel.GetName()); liftingSurfaces = new List <ModuleLiftingSurface>(); controlSurfaces = new List <ModuleControlSurface>(); inRange = true; groundPlane = new Plane(); // This will be calculated later wingSpan = 42 / 42; foreach (Part part in vessel.Parts) { ModuleControlSurface thingThatLiftsPartsAndMoves = null; ModuleLiftingSurface thingThatLiftsParts = null; // Make sure we're actually allowed to modify that part if (!part.Modules.Contains("IgnoreGroundEffect")) { // Look through the list of part modules to find anything that inherits ModuleLiftingSurface foreach (PartModule module in part.Modules) { if (typeof(ModuleLiftingSurface).IsAssignableFrom(module.GetType())) { //thingThatLiftsParts = (ModuleLiftingSurface)(module); //initialLift = thingThatLiftsParts.deflectionLiftCoeff; thingThatLiftsParts = (ModuleLiftingSurface)(module); if (module is ModuleControlSurface) { thingThatLiftsPartsAndMoves = (ModuleControlSurface)(module); } } } } if (thingThatLiftsParts != null) { if (thingThatLiftsPartsAndMoves != null) { // It's a control surface, add to control surface arrays controlSurfaces.Add(thingThatLiftsPartsAndMoves); } else { // It's just a lifting surface, add to lifting surface arrays liftingSurfaces.Add(thingThatLiftsParts); } } //thingThatLiftsPartsAndMoves.OnCenterOfLiftQuery(); } print("LiftingSurfaces counted for " + vessel.GetName() + ": " + liftingSurfaces.Count); print("ControlSurfaces counted for " + vessel.GetName() + ": " + controlSurfaces.Count); }
public override void OnStart(StartState state) { if (ferramEnabled) { // Do Ferrem stuff // isn't legacy not even stilln't today but not used? //if (part.Modules.Contains("FARControllableSurface")) { // ferramModule = part.Modules["FARControllableSurface"]; //} else if (part.Modules.Contains("FARWingAerodynamicModel")) { // ferramModule = part.Modules["FARWingAerodynamicModel"]; //} if (part.Modules.Contains("FARAeroPartModule")) { ferramModule = part.Modules ["FARAeroPartModule"]; ferramField = ferramModule.GetType().GetField("worldSpaceAeroForce"); } else { print("FAR module not found"); Destroy(this); return; } //foreach (PartModule pm in part.Modules) { // print ("module: " + pm.ClassName); //} //print ("farrem module: " + ferramModule); print("Ground effect module loaded with FAR"); return; } else { // Look through the list of part modules to find anything that inherits ModuleLiftingSurface foreach (PartModule module in part.Modules) { if (typeof(ModuleLiftingSurface).IsAssignableFrom(module.GetType())) { thingThatLiftsParts = (ModuleLiftingSurface)(module); initialLift = thingThatLiftsParts.deflectionLiftCoeff; if (module is ModuleControlSurface) { //print ("SURFAAAACE!"); thingThatAlsoLiftsPartsButMoves = (ModuleControlSurface)(module); initialLiftCtrl = thingThatAlsoLiftsPartsButMoves.ctrlSurfaceArea; } print("Ground effect module loaded"); return; } } // This means there's a module manager error? } print("Ground effect module loaded for non-aero part?"); Destroy(this); }
public static SimulatedLiftingSurface Borrow(ModuleLiftingSurface module, SimulatedPart part) { SimulatedLiftingSurface surface = pool.Borrow(); surface.vessel = part.vessel; surface.Init(module, part); return(surface); }
public override void OnStart(StartState state) { Debug.Log(string.Format("{0} ModulePebkacLesPitchControl.OnStart", _myModTag)); _pitchEngine = GetPitchEngine(); _deployAnimation = GetDeployAnimation(); _liftingSurface = GetLiftingSurface(); base.OnStart(state); }
private void Start() { shield = new DummyAirstreamShield { part = part }; if (waterSlowDragNew < 0) { waterSlowDragNew = PhysicsGlobals.BuoyancyWaterDragSlow; minVelVesselMultNew = (float)PhysicsGlobals.BuoyancyWaterDragPartVelGreaterVesselMult; PhysicsGlobals.BuoyancyWaterDragPartVelGreaterVesselMult = 0; } part.maximum_drag = 0; part.minimum_drag = 0; part.angularDrag = 0; if (HighLogic.LoadedSceneIsFlight) { enabled = true; } else if (HighLogic.LoadedSceneIsEditor) { enabled = false; } partLocalVel = Vector3.zero; partLocalForce = Vector3.zero; partLocalTorque = Vector3.zero; if (FARDebugValues.allowStructuralFailures && !partStressOverride) { FARPartStressTemplate template = FARAeroStress.DetermineStressTemplate(part); partStressMaxY = template.YMaxStress; partStressMaxXZ = template.XZMaxStress; } partTransform = part.partTransform; materialColorUpdater = new MaterialColorUpdater(partTransform, PhysicsGlobals.TemperaturePropertyID); if (part.Modules.Contains <FARWingAerodynamicModel>()) { LegacyWingModel = part.Modules.GetModule <FARWingAerodynamicModel>(); } else if (part.Modules.Contains <FARControllableSurface>()) { LegacyWingModel = part.Modules.GetModule <FARControllableSurface>(); } else { LegacyWingModel = null; } // For handling airbrakes aero visualization stockAeroSurfaceModule = part.Modules.Contains <ModuleAeroSurface>() ? part.Modules.GetModule <ModuleAeroSurface>() : null; }
private void CountSurfaces() { print("Counting wings for " + vessel.GetName()); liftingSurfaces = new List <WingEntry>(); inRange = true; groundPlane = new Plane(); // This will be calculated later wingSpan = 42 / 42; foreach (Part part in vessel.Parts) { ModuleLiftingSurface thingThatLiftsParts = null; float multiplyingLiftWhatever = DefaultLiftMultiplier; // Look through the list of part modules to find anything that // inherits ModuleLiftingSurface foreach (PartModule module in part.Modules) { if (typeof(ModuleLiftingSurface).IsAssignableFrom(module.GetType())) { thingThatLiftsParts = (ModuleLiftingSurface)(module); //initialLift = thingThatLiftsParts.deflectionLiftCoeff; if (module is ModuleControlSurface) { // maybe do specific for control surfaces } } else if (module is ModuleGroundEffect) { multiplyingLiftWhatever = ((ModuleGroundEffect)module) .groundEffectMultiplier; } } if (thingThatLiftsParts != null) { WingEntry entry = new WingEntry(); entry.groundEffectMultiplier = multiplyingLiftWhatever; entry.surface = thingThatLiftsParts; liftingSurfaces.Add(entry); } //thingThatLiftsPartsAndMoves.OnCenterOfLiftQuery(); } print("LiftingSurfaces counted for " + vessel.GetName() + ": " + liftingSurfaces.Count); }
public override void OnStart(StartState state) { // hook up to the part attach callback /* * if (state == StartState.Editor) * { * part.OnEditorAttach += OnEditorAttach; * } */ List <ModuleEngines> engines = part.FindModulesImplementing <ModuleEngines>(); foreach (ModuleEngines e in engines) { if (e.engineID == escapeEngineID) { escapeEngine = e; } else if (e.engineID == pitchEngineID) { pitchEngine = e; } else if (e.engineID == jettisonEngineID) { jettisonEngine = e; } foreach (BaseAction a in e.Actions) { a.active = false; } foreach (BaseField f in e.Fields) { f.guiActive = false; f.guiActiveEditor = false; } foreach (BaseEvent ev in e.Events) { ev.guiActive = false; ev.guiActiveEditor = false; } } // check for nulls in engines // set up the variables used to shift aerodynamics _maxFuel = part.Resources["SolidFuel"].maxAmount; _origComOffset = part.CoMOffset; _origCopOffset = part.CoPOffset; _origColOffset = part.CoLOffset; // set up the variables used by code for simming the canards _deployAnimation = GetDeployAnimation(); _liftingSurface = GetLiftingSurface(); }
void Start() { part.maximum_drag = 0; part.minimum_drag = 0; part.angularDrag = 0; if (HighLogic.LoadedSceneIsFlight) { this.enabled = true; } else if (HighLogic.LoadedSceneIsEditor) { this.enabled = false; } partLocalVel = Vector3.zero; partLocalForce = Vector3.zero; partLocalTorque = Vector3.zero; if (!part.Modules.Contains("ModuleAeroSurface")) { part.dragModel = Part.DragModel.CYLINDRICAL; } if (FARDebugValues.allowStructuralFailures) { FARPartStressTemplate template = FARAeroStress.DetermineStressTemplate(this.part); partStressMaxY = template.YmaxStress; partStressMaxXZ = template.XZmaxStress; } partTransform = part.partTransform; materialColorUpdater = new MaterialColorUpdater(partTransform, PhysicsGlobals.TemperaturePropertyID); if (part.Modules.Contains("FARWingAerodynamicModel")) { legacyWingModel = part.Modules["FARWingAerodynamicModel"] as FARWingAerodynamicModel; } else if (part.Modules.Contains("FARControllableSurface")) { legacyWingModel = part.Modules["FARControllableSurface"] as FARWingAerodynamicModel; } else { legacyWingModel = null; } // For handling airbrakes aero visualization if (part.Modules.Contains("ModuleAeroSurface")) { stockAeroSurfaceModule = part.Modules["ModuleAeroSurface"] as ModuleAeroSurface; } else { stockAeroSurfaceModule = null; } }
public override void OnStart(StartState state) { List <ModuleEngines> engines = part.FindModulesImplementing <ModuleEngines>(); foreach (ModuleEngines e in engines) { if (e.engineID == _escapeEngineID) { _escapeEngine = e; } else if (e.engineID == _pitchEngineID) { _pitchEngine = e; } else if (e.engineID == _jettisonEngineID) { _jettisonEngine = e; } foreach (BaseAction a in e.Actions) { a.active = false; } foreach (BaseField f in e.Fields) { f.guiActive = false; f.guiActiveEditor = false; } foreach (BaseEvent ev in e.Events) { ev.guiActive = false; ev.guiActiveEditor = false; } } if (hasPitchControl) { // set up the variables used to shift aerodynamics _maxFuel = part.Resources["SolidFuel"].maxAmount; _origComOffset = part.CoMOffset; _origColOffset = part.CoLOffset; // set up the variables used by code for simming the canards _deployAnimation = GetDeployAnimation(); _liftingSurface = GetLiftingSurface(); } // get the decoupler _lesDecoupler = part.FindModuleImplementing <ModuleDecouple>(); if (_lesDecoupler == null) { Debug.LogError(string.Format("{0}: {1}: {2}", _myModTag, part.name, "Did not find a decoupler on the LES!")); } }
private void OnFirstUpdate() // Simulated event { Log.dbg("OnFirtUpdate()"); // Fetch the needed data on craft spawn, when anything that wanted to change something already did it. // Yeah, TweakScale. :) this.module = this.part.Modules.GetModule <ModuleLiftingSurface>(); this.defaultdDeflectionLiftCoeff = this.module.deflectionLiftCoeff; Log.dbg("defaultdDeflectionLiftCoeff = {0}", this.defaultdDeflectionLiftCoeff); }
private double FallbackEvaluateTerminalVelocity() { // Terminal velocity computation based on MechJeb 2.5.1 or one of the later snapshots if (altitudeASL > vessel.mainBody.RealMaxAtmosphereAltitude()) { return(float.PositiveInfinity); } Vector3 pureDragV = Vector3.zero, pureLiftV = Vector3.zero; for (int i = 0; i < vessel.parts.Count; i++) { Part p = vessel.parts[i]; pureDragV += -p.dragVectorDir * p.dragScalar; if (!p.hasLiftModule) { Vector3 bodyLift = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, -p.dragVectorDir); pureLiftV += bodyLift; for (int m = 0; m < p.Modules.Count; m++) { PartModule pm = p.Modules[m]; if (!pm.isEnabled) { continue; } if (pm is ModuleLiftingSurface) { ModuleLiftingSurface liftingSurface = (ModuleLiftingSurface)pm; if (p.ShieldedFromAirstream) { continue; } pureLiftV += liftingSurface.liftForce; pureDragV += liftingSurface.dragForce; } } } } // Why? pureDragV = pureDragV / totalShipWetMass; pureLiftV = pureLiftV / totalShipWetMass; Vector3 force = pureDragV + pureLiftV; double drag = Vector3.Dot(force, -vessel.srf_velocity.normalized); return(Math.Sqrt(localGeeDirect / drag) * vessel.srfSpeed); }
public override void OnStart(StartState state) { try { base.OnStart(state); liftingSurface = part.FindModuleImplementing <ModuleLiftingSurface>(); deflectionLiftCoeff = liftingSurface.deflectionLiftCoeff; liftingSurface.deflectionLiftCoeff = deflectionLiftCoeff * oneOrZero; } catch (Exception ex) { Debug.LogError("PROBLEM.\n" + ex.Message + "\n" + ex.StackTrace); } }
private void GenerateHighlightingData(ShipConstruct ship, CelestialBody body, float altitude, float speed, float aoa) { float mach, atmDensity; lock (body) { atmDensity = (float)Extensions.KSPClassExtensions.GetDensity(body, altitude); mach = speed / (float)body.GetSpeedOfSound(body.GetPressure(altitude), atmDensity); } int count = ship.parts.Count; highlightingData = new PartAeroData[count]; Vector3 inflow = AeroPredictor.InflowVect(aoa); float pseudoReDragMult; lock (PhysicsGlobals.DragCurvePseudoReynolds) pseudoReDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(atmDensity * speed); for (int i = 0; i < count; i++) { if (WindTunnelSettings.HighlightIgnoresLiftingSurfaces && ship.parts[i].HasModuleImplementing <ModuleLiftingSurface>()) { highlightingData[i] = new PartAeroData(0, 0, ship.parts[i].mass); continue; } VesselCache.SimulatedPart simPart = VesselCache.SimulatedPart.Borrow(ship.parts[i], null); Vector3 partForce = simPart.GetAero(inflow, mach, pseudoReDragMult); ModuleLiftingSurface liftingSurface = ship.parts[i].FindModuleImplementing <ModuleLiftingSurface>(); if (liftingSurface != null) { VesselCache.SimulatedLiftingSurface simLiftSurf = VesselCache.SimulatedLiftingSurface.Borrow(liftingSurface, simPart); partForce += simLiftSurf.GetForce(inflow, mach); simLiftSurf.Release(); } simPart.Release(); //Vector3 partForce = highlightingVessel.parts[i].GetAero(inflow, mach, pseudoReDragMult); //Vector3 partForce = StockAeroUtil.SimAeroForce(body, new ShipConstruct("test", "", new List<Part>() { EditorLogic.fetch.ship.parts[i] }), inflow * speed, altitude); partForce = AeroPredictor.ToFlightFrame(partForce, aoa); // (Quaternion.AngleAxis((aoa * 180 / Mathf.PI), Vector3.left) * partForce); highlightingData[i] = new PartAeroData(Math.Abs(partForce.z), Math.Abs(partForce.y), ship.parts[i].mass); } }
private double FallbackEvaluateDragForce() { // Equations based on https://github.com/NathanKell/AeroGUI/blob/master/AeroGUI/AeroGUI.cs and MechJeb. double dragForce = 0.0; if (altitudeASL < vessel.mainBody.RealMaxAtmosphereAltitude()) { Vector3 pureDragV = Vector3.zero, pureLiftV = Vector3.zero; for (int i = 0; i < vessel.parts.Count; i++) { Part p = vessel.parts[i]; pureDragV += -p.dragVectorDir * p.dragScalar; if (!p.hasLiftModule) { Vector3 bodyLift = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, -p.dragVectorDir); pureLiftV += bodyLift; for (int m = 0; m < p.Modules.Count; m++) { PartModule pm = p.Modules[m]; if (pm.isEnabled && pm is ModuleLiftingSurface) { ModuleLiftingSurface liftingSurface = pm as ModuleLiftingSurface; if (!p.ShieldedFromAirstream) { pureLiftV += liftingSurface.liftForce; pureDragV += liftingSurface.dragForce; } } } } } // Per NathanKell here http://forum.kerbalspaceprogram.com/threads/125746-Drag-Api?p=2029514&viewfull=1#post2029514 // drag is in kN. Divide by wet mass to get m/s^2 acceleration Vector3 force = pureDragV + pureLiftV; dragForce = Vector3.Dot(force, -vessel.srf_velocity.normalized); } return(dragForce); }
protected void Init(ModuleLiftingSurface surface, SimulatedPart part) { surface.SetupCoefficients(Vector3.forward, out Vector3 nVel, out this.liftVector, out float liftDot, out float absDot); this.omnidirectional = surface.omnidirectional; this.perpendicularOnly = surface.perpendicularOnly; this.liftCurve = surface.liftCurve.Clone(); this.liftMachCurve = surface.liftMachCurve.Clone(); this.dragCurve = surface.dragCurve.Clone(); this.dragMachCurve = surface.dragMachCurve.Clone(); this.deflectionLiftCoeff = surface.deflectionLiftCoeff; this.useInternalDragModel = surface.useInternalDragModel; this.part = part; if (surface is ModuleControlSurface ctrl) { this.deflectionLiftCoeff *= (1 - ctrl.ctrlSurfaceArea); } }
private double FallbackEvaluateLiftForce() { double liftForce = 0.0; if (altitudeASL < vessel.mainBody.RealMaxAtmosphereAltitude()) { Vector3 pureDragV = Vector3.zero, pureLiftV = Vector3.zero; for (int i = 0; i < vessel.parts.Count; i++) { Part p = vessel.parts[i]; pureDragV += -p.dragVectorDir * p.dragScalar; if (!p.hasLiftModule) { Vector3 bodyLift = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, -p.dragVectorDir); pureLiftV += bodyLift; for (int m = 0; m < p.Modules.Count; m++) { PartModule pm = p.Modules[m]; if (pm.isEnabled && pm is ModuleLiftingSurface) { ModuleLiftingSurface liftingSurface = pm as ModuleLiftingSurface; if (!p.ShieldedFromAirstream) { pureLiftV += liftingSurface.liftForce; pureDragV += liftingSurface.dragForce; } } } } } Vector3 force = pureDragV + pureLiftV; Vector3 liftDir = -Vector3.Cross(vessel.transform.right, vessel.srf_velocity.normalized); liftForce = Vector3.Dot(force, liftDir); } return(liftForce); }
public override void OnUpdate() { base.OnUpdate(); if (HighLogic.LoadedSceneIsFlight == false) { return; } if (watchAnimation == false) { return; } //Are we done deploying/stowing the wing? //If so, update the lift and the GUI. if (anim.aniState == ModuleAnimateGeneric.animationStates.MOVING) { ModuleLiftingSurface liftingSurface = this.part.FindModuleImplementing <ModuleLiftingSurface>(); if (liftingSurface == null) { return; } if (isDeploying) { //Update dragModel & CoLOffset Events["DeployWing"].active = false; Events["StowWing"].active = true; liftingSurface.deflectionLiftCoeff = deflectionLiftCoeffDeployed; watchAnimation = false; } else { //Update dragModel & CoLOffset Events["DeployWing"].active = true; Events["StowWing"].active = false; liftingSurface.deflectionLiftCoeff = deflectionLiftCoeffStowed; watchAnimation = false; } } }
private void OnDestroy() { if (liftArrow != null) { UnityEngine.Object.Destroy(liftArrow); liftArrow = null; } if (dragArrow != null) { UnityEngine.Object.Destroy(dragArrow); dragArrow = null; } if (momentArrow != null) { UnityEngine.Object.Destroy(momentArrow); momentArrow = null; } legacyWingModel = null; stockAeroSurfaceModule = null; }
public override void OnStart(StartState state) { base.OnStart(state); anim = this.part.FindModuleImplementing <ModuleAnimateGeneric>(); if (anim == null) { Debug.Log("[ModuleDeployableWing] ModuleAnimateGeneric not found!"); return; } anim.Events["Toggle"].guiActive = false; anim.Events["Toggle"].guiActiveEditor = false; anim.Events["Toggle"].guiActiveUnfocused = false; ModuleLiftingSurface liftingSurface = this.part.FindModuleImplementing <ModuleLiftingSurface>(); if (liftingSurface == null) { Debug.Log("[ModuleDeployableWing] ModuleLiftingSurface not found!"); return; } //If we're showing the endEventGUIName then the wing is deployed. if (anim.Events["Toggle"].guiName == anim.startEventGUIName) //animation.endEventGUIName) { Events["DeployWing"].active = false; //true; Events["StowWing"].active = true; //false; liftingSurface.deflectionLiftCoeff = deflectionLiftCoeffDeployed; //deflectionLiftCoeffStowed } else { Events["DeployWing"].active = true; //false; Events["StowWing"].active = false; //true; liftingSurface.deflectionLiftCoeff = deflectionLiftCoeffStowed; //deflectionLiftCoeffDeployed } }
private ModuleLiftingSurface GetLiftingSurface() { Debug.Log(string.Format("{0} ModulePebkacLesPitchControl.GetLiftingSurface", _myModTag)); ModuleLiftingSurface myLiftingSurface = null; try { myLiftingSurface = part.FindModulesImplementing <ModuleLiftingSurface>().SingleOrDefault(); } catch (System.Exception x) { Debug.Log(string.Format("{0} ERROR: {1}", _myModTag, x.Message)); } if (!myLiftingSurface) { // this shouldn't happen under normal circumstances Debug.LogError("ERROR: Didn't find ModuleLiftingSurface on LES nosecone!"); } return(myLiftingSurface); }
private void OnDestroy() { if (liftArrow != null) { Destroy(liftArrow); liftArrow = null; } if (dragArrow != null) { Destroy(dragArrow); dragArrow = null; } if (momentArrow != null) { Destroy(momentArrow); momentArrow = null; } LegacyWingModel = null; stockAeroSurfaceModule = null; }
private void FixedUpdateLateLate() { if (!vessel.loaded) { return; } // Checks to see if ground effect would have any significance if (((vessel.situation & LowFlying) == 0) || (vessel.radarAltitude > ActivateAltitude) || !vessel.mainBody.hasSolidSurface || !vessel.mainBody.atmosphere) { if (inRange) { // Previously in ground effect, just exited print(vessel.GetName() + " Exited Ground Effect range"); inRange = false; //ResetLiftValues(); liftingSurfaces = null; } return; } if (liftingSurfaces == null) { // Count the control surfaces if not done so //print(vessel.GetName() + " Entered Ground Effect range"); CountSurfaces(); } groundDir = vessel.gravityForPos.normalized; oceanNormal = -groundDir; // Use raytrace below to set the ground plane if (vessel.radarAltitude < wingSpan * 2.0f) { RaycastHit ray; // 1 << 15 hits anything that isn't a vessel or ocean if (Physics.Raycast(vessel.CoM, groundDir, out ray, wingSpan * 2.0f, 1 << 15)) { groundPlane.SetNormalAndPosition(ray.normal, ray.point); } else { groundPlane.distance = 0.0f; } } else { groundPlane.distance = 0.0f; } float newWingSpan = 1.0f; bool prevInGroundEffect = inGroundEffect; // Set true in AddGroundEffectForce, if any wing is close enough to the // ground inGroundEffect = false; // Loop trough all surfaces and change their lift // Also get the max wingspan for (int i = 0; i < liftingSurfaces.Count; i++) { ModuleLiftingSurface surface = liftingSurfaces[i].surface; float mul = liftingSurfaces[i].groundEffectMultiplier; Part part = surface.part; Vector3 newLift = AddGroundEffectForce(part, surface.liftForce, mul); // set aerodynamic overlay if (surface.liftArrow) { // arrow length is 2x smaller than lift force as of 1.10 surface.liftArrow.Length = newLift.magnitude * 0.5f; surface.liftArrow.Direction = newLift; } newWingSpan = Math.Max(newWingSpan, ApproximateWingSpan(part)); } if (prevInGroundEffect && !inGroundEffect) { print(vessel.GetName() + " Exited Ground Effect"); } else if (!prevInGroundEffect && inGroundEffect) { print(vessel.GetName() + " Entered Ground Effect"); } //print("wingspan: " + newWingSpan); wingSpan = newWingSpan; }
//******************************************************* 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); }
//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; } } }
// This method will calculate various useful stats // That we display in the GUI. public void GetAeroStats(Vector3d nVel) { Vessel v = FlightGlobals.ActiveVessel; Vector3d vLift = Vector3d.zero; // the sum of lift from all parts Vector3d vDrag = Vector3d.zero; // the sum of drag from all parts double sqrMag = v.srf_velocity.sqrMagnitude; Q = 0.5 * v.atmDensity * sqrMag; // dynamic pressure, aka Q eas = Math.Sqrt(sqrMag * v.atmDensity / 1.225); // Equivalent Air Speed // i.e. your airspeed at sea level with the same Q density = v.atmDensity; pressure = v.staticPressurekPa * 1000.0; mach = v.rootPart.machNumber; soundSpeed = v.speedOfSound; double dTime = TimeWarp.fixedDeltaTime; double mass = 0d; // Now we loop through all parts, checking the modules in each part // This way we get all drag, lift, and thrust. for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; mass += p.mass + p.GetResourceMass(); // get part drag (but not wing/surface drag) vDrag += -p.dragVectorDir * p.dragScalar; if (!p.hasLiftModule) { Vector3 bodyLift = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, -p.dragVectorDir); vLift += bodyLift; } // get convection convectiveTotal += p.thermalConvectionFlux * dTime; // now find modules for (int j = 0; j < p.Modules.Count; ++j) { var m = p.Modules[j]; if (m is ModuleLiftingSurface) // control surface derives from this { ModuleLiftingSurface wing = (ModuleLiftingSurface)m; vLift += wing.liftForce; vDrag += wing.dragForce; } // Get thrust if (m is ModuleEngines) // FX derives from this { thrust += ((ModuleEngines)m).finalThrust; } } } // pLift is 'pure' lift, and same for drag, i.e. just the magnitude of each pLift = vLift.magnitude; pDrag = vDrag.magnitude; Vector3d force = vLift + vDrag; // sum of all forces on the craft Vector3d liftDir = -Vector3d.Cross(v.transform.right, nVel); // we need the "lift" direction, which // is "up" from our current velocity vector and roll angle. // Now we can compute the dots. lift = Vector3d.Dot(force, liftDir); // just the force in the 'lift' direction liftUp = Vector3d.Dot(force, v.upAxis); // just the force in the 'up' direction (note, some of it may be drag!) drag = Vector3d.Dot(force, -nVel); // drag force, = pDrag + lift-induced drag lidForce = Vector3d.Dot(vLift, -nVel); // Lift Induced Drag dragUpForce = Vector3d.Dot(vDrag, v.upAxis); // any drag in the opposite direction of gravity ldRatio = lift / drag; // Lift / Drag ratio terminalV = Math.Sqrt(grav / drag) * v.speed; if (double.IsNaN(terminalV)) { terminalV = 0d; } double qRecip = 1000d / Q; // convert kN to Newtons ClS = lift * qRecip; CdS = drag * qRecip; ballisticCoeff = 1000d * mass / CdS; // convert tonnes to kg }
void Start() { part.maximum_drag = 0; part.minimum_drag = 0; part.angularDrag = 0; if (HighLogic.LoadedSceneIsFlight) this.enabled = true; else if (HighLogic.LoadedSceneIsEditor) this.enabled = false; partLocalVel = Vector3.zero; partLocalForce = Vector3.zero; partLocalTorque = Vector3.zero; if (!part.Modules.Contains("ModuleAeroSurface")) part.dragModel = Part.DragModel.CYLINDRICAL; if(FARDebugValues.allowStructuralFailures) { FARPartStressTemplate template = FARAeroStress.DetermineStressTemplate(this.part); partStressMaxY = template.YmaxStress; partStressMaxXZ = template.XZmaxStress; } partTransform = part.partTransform; materialColorUpdater = new MaterialColorUpdater(partTransform, PhysicsGlobals.TemperaturePropertyID); if (part.Modules.Contains("FARWingAerodynamicModel")) legacyWingModel = part.Modules["FARWingAerodynamicModel"] as FARWingAerodynamicModel; else if (part.Modules.Contains("FARControllableSurface")) legacyWingModel = part.Modules["FARControllableSurface"] as FARWingAerodynamicModel; else legacyWingModel = null; // For handling airbrakes aero visualization if (part.Modules.Contains("ModuleAeroSurface")) stockAeroSurfaceModule = part.Modules["ModuleAeroSurface"] as ModuleAeroSurface; else stockAeroSurfaceModule = null; }
private void OnDestroy() { if (liftArrow != null) { UnityEngine.Object.Destroy(liftArrow); liftArrow = null; } if (dragArrow != null) { UnityEngine.Object.Destroy(dragArrow); dragArrow = null; } if (momentArrow != null) { UnityEngine.Object.Destroy(momentArrow); momentArrow = null; } legacyWingModel = null; stockAeroSurfaceModule = null; }
private void UpdateAeroForces() { if (aeroDataValid) { return; } aeroDataValid = true; gravForce = 1000.0 * vessel.GetTotalMass() * FlightGlobals.getGeeForceAtPosition(vessel.CoM).magnitude; // force of gravity // Short-circuit these computations if there's no atmosphere. if (vessel.atmDensity == 0.0) { liftForce = 0.0; dragForce = 0.0; terminalVelocity = 0.0; liftUpForce = 0.0; return; } // Code substantially from NathanKell's AeroGUI mod, // https://github.com/NathanKell/AeroGUI/blob/ccfd5e2e40fdf13e6ce66517ceb1db418689a5f0/AeroGUI/AeroGUI.cs#L301 Vector3d vLift = Vector3d.zero; // the sum of lift from all parts Vector3d vDrag = Vector3d.zero; // the sum of drag from all parts double areaDrag = 0.0; for (int i = vessel.Parts.Count - 1; i >= 0; --i) { Part p = vessel.Parts[i]; // get part drag (but not wing/surface drag) vDrag += -p.dragVectorDir * p.dragScalar; if (!p.hasLiftModule) { Vector3 bodyLift = p.transform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, -p.dragVectorDir); vLift += bodyLift; } ModuleLiftingSurface wing = p.FindModuleImplementing <ModuleLiftingSurface>(); if (wing != null) { vLift += wing.liftForce; vDrag += wing.dragForce; } areaDrag += p.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * PhysicsGlobals.DragMultiplier; } Vector3d force = vLift + vDrag; // sum of all forces on the craft Vector3d nVel = vessel.srf_velocity.normalized; Vector3d liftDir = -Vector3d.Cross(vessel.transform.right, nVel); // we need the "lift" direction, which // is "up" from our current velocity vector and roll angle. // Now we can compute the dots. liftForce = Vector3d.Dot(force, liftDir); // just the force in the 'lift' direction dragForce = Vector3d.Dot(force, -nVel); // drag force, = pDrag + lift-induced drag liftUpForce = Vector3d.Dot(force, up); terminalVelocity = Math.Sqrt(2.0 * gravForce / (areaDrag * vessel.atmDensity)); }
public override void OnStart(StartState state) { // hook up to the part attach callback /* if (state == StartState.Editor) { part.OnEditorAttach += OnEditorAttach; } */ List<ModuleEngines> engines = part.FindModulesImplementing<ModuleEngines>(); foreach (ModuleEngines e in engines) { if (e.engineID == escapeEngineID) { escapeEngine = e; } else if (e.engineID == pitchEngineID) { pitchEngine = e; } else if (e.engineID == jettisonEngineID) { jettisonEngine = e; } foreach (BaseAction a in e.Actions) { a.active = false; } foreach (BaseField f in e.Fields) { f.guiActive = false; f.guiActiveEditor = false; } foreach (BaseEvent ev in e.Events) { ev.guiActive = false; ev.guiActiveEditor = false; } } // check for nulls in engines // set up the variables used to shift aerodynamics _maxFuel = part.Resources["SolidFuel"].maxAmount; _origComOffset = part.CoMOffset; _origCopOffset = part.CoPOffset; _origColOffset = part.CoLOffset; // set up the variables used by code for simming the canards _deployAnimation = GetDeployAnimation(); _liftingSurface = GetLiftingSurface(); }
//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); }
public static Vector3 get_part_torque(CenterOfLiftQuery qry, Part p, Vector3 CoM, ref float lift, ref float drag) { if (p == null || (p.Rigidbody != p.rb) && !PhysicsGlobals.ApplyDragToNonPhysicsParts) { return(Vector3.zero); } Vector3 lift_pos = Vector3.zero; Vector3 drag_pos = Vector3.zero; if (!p.ShieldedFromAirstream) { var providers = p.FindModulesImplementing <ModuleLiftingSurface>(); if ((providers != null) && providers.Count > 0) { p.hasLiftModule = true; } Vector3 res = Vector3.zero; if (p.hasLiftModule && providers[0] is ModuleControlSurface) { p.DragCubes.SetCubeWeight("neutral", 1.5f); p.DragCubes.SetCubeWeight("fullDeflectionPos", 0.0f); p.DragCubes.SetCubeWeight("fullDeflectionNeg", 0.0f); } // drag from drag-cubes if (!p.DragCubes.None) { Vector3 drag_force = Vector3.zero; p.dragVector = qry.refVector; p.dragVectorSqrMag = p.dragVector.sqrMagnitude; p.dragVectorMag = Mathf.Sqrt(p.dragVectorSqrMag); p.dragVectorDir = p.dragVector / p.dragVectorMag; p.dragVectorDirLocal = -p.partTransform.InverseTransformDirection(p.dragVectorDir); p.dynamicPressurekPa = qry.refAirDensity * 0.0005 * p.dragVectorSqrMag; if (p.rb != p.Rigidbody && PhysicsGlobals.ApplyDragToNonPhysicsPartsAtParentCoM) { drag_pos = p.Rigidbody.worldCenterOfMass; lift_pos = drag_pos; } else { lift_pos = p.partTransform.TransformPoint(p.CoLOffset); drag_pos = p.partTransform.TransformPoint(p.CoPOffset); } p.DragCubes.SetDrag(p.dragVectorDirLocal, mach); float pseudoreynolds = (float)(density * Mathf.Abs(speed)); float pseudoredragmult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(pseudoreynolds); float drag_k = p.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoredragmult; p.dragScalar = (float)(p.dynamicPressurekPa * drag_k * PhysicsGlobals.DragMultiplier); drag_force = p.dragScalar * -p.dragVectorDir; res += Vector3.Cross(drag_force, drag_pos - CoM); Vector3 sum_force = drag_force; drag += Vector3.Dot(sum_force, -p.dragVectorDir); } if (!p.hasLiftModule) { // stock aero lift if (!p.DragCubes.None) { p.bodyLiftScalar = (float)(p.dynamicPressurekPa * p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * CorrectCoL.CoLMarkerFull.lift_curves.liftMachCurve.Evaluate(mach)); Vector3 lift_force = p.partTransform.rotation * (p.bodyLiftScalar * p.DragCubes.LiftForce); lift_force = Vector3.ProjectOnPlane(lift_force, -p.dragVectorDir); res += Vector3.Cross(lift_force, lift_pos - CoM); Vector3 sum_force = lift_force; lift += Vector3.Dot(sum_force, Vector3.Cross(p.dragVectorDir, EditorLogic.RootPart.transform.right).normalized); } return(res); } else { double q = 0.5 * qry.refAirDensity * qry.refVector.sqrMagnitude; for (int i = 0; i < providers.Count; i++) { Vector3 dragvect; Vector3 liftvect; Vector3 lift_force = Vector3.zero; Vector3 drag_force = Vector3.zero; float abs; ModuleLiftingSurface lsurf = providers[i]; ModuleControlSurface csurf = lsurf as ModuleControlSurface; lsurf.SetupCoefficients(qry.refVector, out dragvect, out liftvect, out lsurf.liftDot, out abs); lift_pos = p.partTransform.TransformPoint(p.CoLOffset); drag_pos = p.partTransform.TransformPoint(p.CoPOffset); lift_force = lsurf.GetLiftVector(liftvect, lsurf.liftDot, abs, q, mach); if (lsurf.useInternalDragModel) { drag_force = lsurf.GetDragVector(dragvect, abs, q); } if (csurf != null) { float deflection = (float)deflection_field.GetValue(csurf); Quaternion incidence = Quaternion.AngleAxis(csurf.ctrlSurfaceRange * deflection, p.partTransform.rotation * Vector3.right); liftvect = incidence * liftvect; lsurf.liftDot = Vector3.Dot(dragvect, liftvect); abs = Mathf.Abs(lsurf.liftDot); lift_force = lift_force * (1.0f - csurf.ctrlSurfaceArea); lift_force += lsurf.GetLiftVector(liftvect, lsurf.liftDot, abs, q, mach) * csurf.ctrlSurfaceArea; if (csurf.useInternalDragModel) { drag_force = drag_force * (1.0f - csurf.ctrlSurfaceArea); drag_force += csurf.GetDragVector(dragvect, abs, q) * csurf.ctrlSurfaceArea; } } res += Vector3.Cross(lift_force, lift_pos - CoM); res += Vector3.Cross(drag_force, drag_pos - CoM); Vector3 result_force = lift_force + drag_force; lift += Vector3.Dot(result_force, Vector3.Cross(qry.refVector, EditorLogic.RootPart.transform.right).normalized); drag += Vector3.Dot(result_force, -qry.refVector.normalized); } return(res); } } return(Vector3.zero); }