public static void CopyDragCubesList(DragCubeList source, DragCubeList dest) { dest.ClearCubes(); dest.None = source.None; dest.Procedural = source.Procedural; for (int i = 0; i < source.Cubes.Count; i++) { DragCube c = new DragCube(); CopyDragCube(source.Cubes[i], c); dest.Cubes.Add(c); } dest.SetDragWeights(); for (int i = 0; i < 6; i++) { dest.WeightedArea[i] = source.WeightedArea[i]; dest.WeightedDrag[i] = source.WeightedDrag[i]; dest.AreaOccluded[i] = source.AreaOccluded[i]; dest.WeightedDepth[i] = source.WeightedDepth[i]; } dest.SetDragWeights(); // We are missing PostOcclusionArea but it seems to be used in Thermal only }
//TODO : rewrite the cube calls to only store and update the minimum needed data ( AreaOccluded + WeightedDrag ?) protected static void CopyDragCubesList(DragCubeList source, DragCubeList dest) { dest.ClearCubes(); dest.None = source.None; // Procedural need access to part so things gets bad quick. dest.Procedural = false; for (int i = 0; i < source.Cubes.Count; i++) { DragCube c = DragCubePool.Instance.Borrow(); CopyDragCube(source.Cubes[i], c); dest.Cubes.Add(c); } dest.SetDragWeights(); for (int i = 0; i < 6; i++) { dest.WeightedArea[i] = source.WeightedArea[i]; dest.WeightedDrag[i] = source.WeightedDrag[i]; dest.AreaOccluded[i] = source.AreaOccluded[i]; dest.WeightedDepth[i] = source.WeightedDepth[i]; } dest.SetDragWeights(); // We are missing PostOcclusionArea but it seems to be used in Thermal only }
protected void CopyDragCubesList(DragCubeList source, DragCubeList dest) { source.ForceUpdate(true, true); source.SetDragWeights(); source.SetPartOcclusion(); dest.ClearCubes(); dest.SetPart(source.Part); dest.None = source.None; // Procedural need access to part so things gets bad quick. dest.Procedural = false; for (int i = 0; i < source.Cubes.Count; i++) { DragCube c; lock (DragCubePool.Instance) c = DragCubePool.Instance.Borrow(); CopyDragCube(source.Cubes[i], c); dest.Cubes.Add(c); } dest.SetDragWeights(); for (int i = 0; i < 6; i++) { dest.WeightedArea[i] = source.WeightedArea[i]; dest.WeightedDrag[i] = source.WeightedDrag[i]; dest.AreaOccluded[i] = source.AreaOccluded[i]; dest.WeightedDepth[i] = source.WeightedDepth[i]; } dest.SetDragWeights(); dest.BodyLiftCurve = new PhysicsGlobals.LiftingSurfaceCurve(); dest.SurfaceCurves = new PhysicsGlobals.SurfaceCurvesList(); dest.DragCurveCd = simCurves.DragCurveCd.Clone(); dest.DragCurveCdPower = simCurves.DragCurveCdPower.Clone(); dest.DragCurveMultiplier = simCurves.DragCurveMultiplier.Clone(); dest.BodyLiftCurve.liftCurve = simCurves.LiftCurve.Clone(); dest.BodyLiftCurve.dragCurve = simCurves.DragCurve.Clone(); dest.BodyLiftCurve.dragMachCurve = simCurves.DragMachCurve.Clone(); dest.BodyLiftCurve.liftMachCurve = simCurves.LiftMachCurve.Clone(); dest.SurfaceCurves.dragCurveMultiplier = simCurves.DragCurveMultiplier.Clone(); dest.SurfaceCurves.dragCurveSurface = simCurves.DragCurveSurface.Clone(); dest.SurfaceCurves.dragCurveTail = simCurves.DragCurveTail.Clone(); dest.SurfaceCurves.dragCurveTip = simCurves.DragCurveTip.Clone(); dest.SetPartOcclusion(); }
protected void SetCubeWeight(string name, float newWeight) { int count = cubes.Cubes.Count; if (count == 0) { return; } bool noChange = true; for (int i = count - 1; i >= 0; i--) { if (cubes.Cubes[i].Name == name && cubes.Cubes[i].Weight != newWeight) { cubes.Cubes[i].Weight = newWeight; noChange = false; } } if (noChange) { return; } cubes.SetDragWeights(); }
protected void CopyDragCubesList(DragCubeList source, DragCubeList dest) { dest.ClearCubes(); dest.SetPart(source.Part); dest.None = source.None; // Procedural need access to part so things gets bad quick. dest.Procedural = false; for (int i = 0; i < source.Cubes.Count; i++) { DragCube c = DragCubePool.Instance.Borrow(); CopyDragCube(source.Cubes[i], c); dest.Cubes.Add(c); } dest.SetDragWeights(); for (int i = 0; i < 6; i++) { dest.WeightedArea[i] = source.WeightedArea[i]; dest.WeightedDrag[i] = source.WeightedDrag[i]; dest.AreaOccluded[i] = source.AreaOccluded[i]; dest.WeightedDepth[i] = source.WeightedDepth[i]; } dest.SetDragWeights(); dest.DragCurveCd = simCurves.DragCurveCd; dest.DragCurveCdPower = simCurves.DragCurveCdPower; dest.DragCurveMultiplier = simCurves.DragCurveMultiplier; dest.BodyLiftCurve.liftCurve = simCurves.LiftCurve; dest.BodyLiftCurve.dragCurve = simCurves.DragCurve; dest.BodyLiftCurve.dragMachCurve = simCurves.DragMachCurve; dest.BodyLiftCurve.liftMachCurve = simCurves.LiftMachCurve; dest.SurfaceCurves.dragCurveMultiplier = simCurves.DragCurveMultiplier; dest.SurfaceCurves.dragCurveSurface = simCurves.DragCurveSurface; dest.SurfaceCurves.dragCurveTail = simCurves.DragCurveTail; dest.SurfaceCurves.dragCurveTip = simCurves.DragCurveTip; }
private void Init(IShipconstruct v) { totalMass = 0; dryMass = 0; CoM = Vector3.zero; CoM_dry = Vector3.zero; relativeWingArea = 0; stage = 0; List <Part> oParts = v.Parts; List <SimulatedPart> variableDragParts_ctrls = new List <SimulatedPart>(); count = oParts.Count; if (HighLogic.LoadedSceneIsEditor) { for (int i = 0; i < v.Parts.Count; i++) { Part p = v.Parts[i]; if (p.dragModel == Part.DragModel.CUBE && !p.DragCubes.None) { DragCubeList cubes = p.DragCubes; lock (cubes) { DragCubeList.CubeData p_drag_data = new DragCubeList.CubeData(); try { cubes.SetDragWeights(); cubes.SetPartOcclusion(); cubes.AddSurfaceDragDirection(-Vector3.forward, 0, ref p_drag_data); } catch (Exception) { cubes.SetDrag(Vector3.forward, 0); cubes.ForceUpdate(true, true); cubes.SetDragWeights(); cubes.SetPartOcclusion(); cubes.AddSurfaceDragDirection(-Vector3.forward, 0, ref p_drag_data); //Debug.Log(String.Format("Trajectories: Caught NRE on Drag Initialization. Should be fixed now. {0}", e)); } } } } } bool lgWarning = false; for (int i = 0; i < count; i++) { if (!lgWarning) { ModuleWheels.ModuleWheelDeployment gear = oParts[i].FindModuleImplementing <ModuleWheels.ModuleWheelDeployment>(); bool forcedRetract = !oParts[i].ShieldedFromAirstream && gear != null && gear.Position > 0; if (forcedRetract) { lgWarning = true; } } } // Recursively add all parts to collections // What a crazy change to make just to accomodate rotating parts! partCollection = PartCollection.Borrow(this, oParts[0]); CoM /= totalMass; CoM_dry /= dryMass; partCollection.origin = CoM; if (relativeWingArea == 0) { // I'm not sure what effect calling ScreenMessages from a background thread will be. // Fortunately, anyone using this mod probably has at least one wing. ScreenMessages.PostScreenMessage("No wings found, using a reference area of 1.", 5, ScreenMessageStyle.UPPER_CENTER); relativeWingArea = 1; } //if (lgWarning) //ScreenMessages.PostScreenMessage("Landing gear deployed, results may not be accurate.", 5, ScreenMessageStyle.UPPER_CENTER); }
//TODO : rewrite the cube calls to only store and update the minimum needed data ( AreaOccluded + WeightedDrag ?) protected static void CopyDragCubesList(DragCubeList source, DragCubeList dest) { dest.ClearCubes(); dest.None = source.None; // Procedural need access to part so things gets bad quick. dest.Procedural = false; for (int i = 0; i < source.Cubes.Count; i++) { DragCube c = new DragCube(); CopyDragCube(source.Cubes[i], c); dest.Cubes.Add(c); } dest.SetDragWeights(); for (int i=0; i<6; i++) { dest.WeightedArea[i] = source.WeightedArea[i]; dest.WeightedDrag[i] = source.WeightedDrag[i]; dest.AreaOccluded[i] = source.AreaOccluded[i]; dest.WeightedDepth[i] = source.WeightedDepth[i]; } dest.SetDragWeights(); // We are missing PostOcclusionArea but it seems to be used in Thermal only }
public static double GetChuteArea(List <Part> parts) { double RCParameter = 0; bool realChuteInUse = false; try { foreach (Part p in parts) { //Make a list of all the Module Names for easy checking later. This can be avoided, but is convenient. List <string> ModuleNames = new List <string>(); foreach (PartModule pm in p.Modules) { ModuleNames.Add(pm.moduleName); } if (ModuleNames.Contains("RealChuteModule")) { if (!realChuteInUse) { RCParameter = 0; } //First off, get the PPMS since we'll need that PartModule realChute = p.Modules["RealChuteModule"]; //Assuming that's not somehow null, then we continue if (realChute != null) //Some of this was adopted from DebRefund, as Vendan's method of handling multiple parachutes is better than what I had. { //We make a list of ConfigNodes containing the parachutes (usually 1, but now there can be any number of them) //We get that from the PPMS ConfigNode rcNode = new ConfigNode(); realChute.Save(rcNode); //It's existence means that RealChute is installed and in use on the craft (you could have it installed and use stock chutes, so we only check if it's on the craft) realChuteInUse = true; RCParameter += ProcessRealchute(rcNode); } } else if (ModuleNames.Contains("RealChuteFAR")) //RealChute Lite for FAR { if (!realChuteInUse) { RCParameter = 0; } PartModule realChute = p.Modules["RealChuteFAR"]; float diameter = 0.0F; //realChute.moduleValues.GetValue("deployedDiameter") if (realChute != null) { try { diameter = realChute.Fields.GetValue <float>("deployedDiameter"); Log.Info($"[SR] Diameter is {diameter}."); } catch (Exception e) { Debug.LogError("[SR] Exception while finding deployedDiameter for RealChuteFAR module on module."); Debug.LogException(e); } } else { Log.Info("[SR] moduleRef is null, attempting workaround to find diameter."); object dDefault = p.partInfo.partPrefab.Modules["RealChuteFAR"]?.Fields?.GetValue("deployedDiameter"); //requires C# 6 if (dDefault != null) { diameter = Convert.ToSingle(dDefault); Log.Info($"[SR] Workaround gave a diameter of {diameter}."); } else { Log.Info("[SR] Couldn't get default value, setting to 0 and calling it a day."); diameter = 0.0F; } } float dragC = 1.0f; //float.Parse(realChute.moduleValues.GetValue("staticCd")); RCParameter += (dragC * Mathf.Pow(diameter, 2) * Math.PI / 4.0); realChuteInUse = true; } else if (!realChuteInUse && ModuleNames.Contains("ModuleParachute")) { //Credit to m4v and RCSBuildAid: https://github.com/m4v/RCSBuildAid/blob/master/Plugin/CoDMarker.cs Part part = p ?? p.partInfo.partPrefab; //the part, or the part prefab DragCubeList dragCubes = part.DragCubes; dragCubes.SetCubeWeight("DEPLOYED", 1); dragCubes.SetCubeWeight("SEMIDEPLOYED", 0); dragCubes.SetCubeWeight("PACKED", 0); dragCubes.SetOcclusionMultiplier(0); Quaternion rotation = Quaternion.LookRotation(Vector3d.up); try { rotation = Quaternion.LookRotation(part.partTransform?.InverseTransformDirection(Vector3d.up) ?? Vector3d.up); } catch (Exception e) { Debug.LogException(e); } dragCubes.SetDragVectorRotation(rotation); } if (!realChuteInUse) { Part part = p ?? p.partInfo.partPrefab; //the part reference, or the part prefab DragCubeList dragCubes = part.DragCubes; dragCubes.ForceUpdate(false, true); dragCubes.SetDragWeights(); dragCubes.SetPartOcclusion(); Vector3 dir = Vector3d.up; try { dir = -part.partTransform?.InverseTransformDirection(Vector3d.down) ?? Vector3d.up; } catch (Exception e) { //Debug.LogException(e); Log.Info("[SR] The expected excpetion is still present. " + e.Message); } dragCubes.SetDrag(dir, 0.03f); //mach 0.03, or about 10m/s double dragCoeff = dragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier; RCParameter += (dragCoeff * PhysicsGlobals.DragMultiplier); } } } catch (Exception e) { Debug.LogError("[SR] Error occured while trying to determine total chute area."); Debug.LogException(e); } return(RCParameter); }
//******************************************************* public static Vector3 SimAeroForce(CelestialBody body, IShipconstruct vessel, Vector3 v_wrld_vel, double altitude, double latitude = 0.0, bool verbose = false) { //Profiler.Start("SimAeroForce"); 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 Vector3d part_force = Vector3d.zero; #endif 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; float pseudoreynolds = (float)(rho * v_wrld_vel.magnitude); float pseudoredragmult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(pseudoreynolds); //Profiler.Start("SimAeroForce#Body"); 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.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.SetDragWeights(); cubes.SetPartOcclusion(); 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)); } #if DEBUG if (verbose && i == 0) { Debug.Log(p.name); Debug.Log(string.Format("{0}, {1}, {2}, {3}, {4}, {5}", p.DragCubes.AreaOccluded[0], p.DragCubes.AreaOccluded[1], p.DragCubes.AreaOccluded[2], p.DragCubes.AreaOccluded[3], p.DragCubes.AreaOccluded[4], p.DragCubes.AreaOccluded[5])); Debug.Log(string.Format("{0}, {1}, {2}, {3}, {4}, {5}", p.DragCubes.WeightedDrag[0], p.DragCubes.WeightedDrag[1], p.DragCubes.WeightedDrag[2], p.DragCubes.WeightedDrag[3], p.DragCubes.WeightedDrag[4], p.DragCubes.WeightedDrag[5])); Debug.Log(p_drag_data.area); Debug.Log(p_drag_data.exposedArea); } #endif drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier; liftForce = p_drag_data.liftForce; } dragForce = -(Vector3d)sim_dragVectorDir * drag; 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; } dragForce *= dyn_pressure * pseudoredragmult * PhysicsGlobals.DragMultiplier; total_drag += dragForce; #if DEBUG part_force += dragForce; #endif // 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 #if DEBUG part_force += bodyLift; #endif total_lift += bodyLift; } //Profiler.Stop("SimAeroForce#Body"); // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass //Profiler.Start("SimAeroForce#LiftModule"); 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; #if DEBUG part_force += local_drag + local_lift; #endif } } //Profiler.Stop("SimAeroForce#LiftModule"); #if DEBUG if (verbose) { Debug.Log(p.name + ": " + part_force); } #endif } // RETURN STUFF Vector3 force = total_lift + total_drag; //Profiler.Stop("SimAeroForce"); return(force); }
//******************************************************* public static Vector3 SimDragForce(CelestialBody body, IShipconstruct vessel, Vector3 v_wrld_vel, double altitude, double latitude = 0.0) { double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. 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 = 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)); Vector3d dragForce; switch (part.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: DragCubeList cubes = part.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 = part.maximum_drag; } else { 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); drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoredragmult; } 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)part.maximum_drag; break; case Part.DragModel.CYLINDRICAL: dragForce = -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(part.minimum_drag, part.maximum_drag, Mathf.Abs(Vector3.Dot(part.partTransform.TransformDirection(part.dragReferenceVector), sim_dragVectorDir))); break; case Part.DragModel.CONIC: dragForce = -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(part.minimum_drag, part.maximum_drag, Vector3.Angle(part.partTransform.TransformDirection(part.dragReferenceVector), sim_dragVectorDir) / 180f); break; default: // no drag to apply dragForce = new Vector3d(); break; } total_drag += dragForce; // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass 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_drag = mcs_mod * wing.GetDragVector(nVel, absdot, liftQ); part.machNumber = prevMach; total_drag += local_drag; } } } // RETURN STUFF return(total_drag); }
//******************************************************* 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); }
private void Init(IShipconstruct v, SimCurves _simCurves) { totalMass = 0; dryMass = 0; CoM = Vector3.zero; CoM_dry = Vector3.zero; List<Part> oParts = v.Parts; List<SimulatedPart> variableDragParts_ctrls = new List<SimulatedPart>(); count = oParts.Count; if (HighLogic.LoadedSceneIsEditor) { for (int i = 0; i < v.Parts.Count; i++) { Part p = v.Parts[i]; if (p.dragModel == Part.DragModel.CUBE && !p.DragCubes.None) { DragCubeList cubes = p.DragCubes; DragCubeList.CubeData p_drag_data = new DragCubeList.CubeData(); try { cubes.SetDragWeights(); cubes.SetPartOcclusion(); cubes.AddSurfaceDragDirection(-Vector3.forward, 0, ref p_drag_data); } catch (Exception) { cubes.SetDrag(Vector3.forward, 0); cubes.ForceUpdate(true, true); cubes.SetDragWeights(); cubes.SetPartOcclusion(); cubes.AddSurfaceDragDirection(-Vector3.forward, 0, ref p_drag_data); //Debug.Log(String.Format("Trajectories: Caught NRE on Drag Initialization. Should be fixed now. {0}", e)); } } } } simCurves = _simCurves; if (parts.Capacity < count) parts.Capacity = count; bool lgWarning = false; int stage = 0; for (int i = 0; i < count; i++) { if (!lgWarning) { ModuleWheels.ModuleWheelDeployment gear = oParts[i].FindModuleImplementing<ModuleWheels.ModuleWheelDeployment>(); bool forcedRetract = !oParts[i].ShieldedFromAirstream && gear != null && gear.Position > 0; if (forcedRetract) lgWarning = true; } SimulatedPart simulatedPart = SimulatedPart.Borrow(oParts[i], this); parts.Add(simulatedPart); totalMass += simulatedPart.totalMass; dryMass += simulatedPart.dryMass; CoM += simulatedPart.totalMass * simulatedPart.CoM; CoM_dry += simulatedPart.dryMass * simulatedPart.CoM; ModuleLiftingSurface liftingSurface = oParts[i].FindModuleImplementing<ModuleLiftingSurface>(); if (liftingSurface != null) { parts[i].hasLiftModule = true; if (liftingSurface is ModuleControlSurface ctrlSurface) { ctrls.Add(SimulatedControlSurface.Borrow(ctrlSurface, simulatedPart)); variableDragParts_ctrls.Add(simulatedPart); if (ctrlSurface.ctrlSurfaceArea < 1) surfaces.Add(SimulatedLiftingSurface.Borrow(ctrlSurface, simulatedPart)); } else surfaces.Add(SimulatedLiftingSurface.Borrow(liftingSurface, simulatedPart)); } List<ITorqueProvider> torqueProviders = oParts[i].FindModulesImplementing<ITorqueProvider>(); // TODO: Add them to a list. if(oParts[i].inverseStage > stage) { SimulatedEngine.Release(engines); engines.Clear(); stage = oParts[i].inverseStage; } if (oParts[i].inverseStage >= stage) { MultiModeEngine multiMode = oParts[i].FindModuleImplementing<MultiModeEngine>(); if (multiMode != null) { engines.Add(SimulatedEngine.Borrow(oParts[i].FindModulesImplementing<ModuleEngines>().Find(engine => engine.engineID == multiMode.mode), simulatedPart)); } else { ModuleEngines engine = oParts[i].FindModulesImplementing<ModuleEngines>().FirstOrDefault(); if (engine != null) engines.Add(SimulatedEngine.Borrow(engine, simulatedPart)); } } } CoM /= totalMass; CoM_dry /= dryMass; if (lgWarning) ScreenMessages.PostScreenMessage("Landing gear deployed, results may not be accurate.", 5, ScreenMessageStyle.UPPER_CENTER); /*for (int i = 0; i < count; i++) { parts[i].CoM -= this.CoM; parts[i].CoL -= this.CoM; parts[i].CoP -= this.CoM; }*/ parts.RemoveAll(part => variableDragParts_ctrls.Contains(part)); }
public void update(double curSpeed, double curDensity) { maxSpeed = 0; breakDistance = 0; curDensity = Math.Max(curDensity, 0.2 * vesselState.mainBody.atmDensityASL); double curSpeedOfSound = Math.Max(vesselState.speedOfSound, vesselState.mainBody.GetSpeedOfSound(0.2 * vesselState.mainBody.atmPressureASL, 0.2 * vesselState.mainBody.atmDensityASL)); //create List by descending order of maxSpeed with drag and max chute reaction time SortedList <double, (float, float)> parachuteTypes = new SortedList <double, (float, float)>(Comparer <double> .Create((a, b) => Math.Sign(b - a))); foreach (var p in vesselState.parachutes) { if (p.deploymentState == ModuleParachute.deploymentStates.STOWED) { p.refDensity = curDensity; p.CalcBaseStats(); DragCubeList simParachute = new DragCubeList(); simParachute.LoadCube(p.part.DragCubes, "DEPLOYED"); simParachute.SetPart(p.part); simParachute.SetOcclusionMultiplier(0f); simParachute.ResetCubeWeights(); simParachute.SetDragWeights(); simParachute.SetPartOcclusion(); //Debug.Log(String.Format("ParachuteInfo.update Parchute {0} has up Cube area={1:F1} drag={2:F1} weight={3:F1} AreaOccluded={4:F1}", // p.part.name, simParachute.Cubes[0].Area[(int) DragCube.DragFace.YP], simParachute.Cubes[0].Drag[(int) DragCube.DragFace.YP], // simParachute.Cubes[0].Weight, simParachute.AreaOccluded[(int)DragCube.DragFace.YP])); DragCubeList.CubeData simDrag = new DragCubeList.CubeData(); simParachute.AddSurfaceDragDirection(Vector3.up, (float)(p.maxSafeSpeedAtRef / curSpeedOfSound), ref simDrag); //Debug.Log(String.Format("ParachuteInfo.update Parchute {0} has areaDrag={1:F1}, crossSectionalArea={2:F1}, exposedArea={3:F1}", // p.part.name, simDrag.areaDrag, simDrag.crossSectionalArea, simDrag.exposedArea)); if (parachuteTypes.ContainsKey(p.maxSafeSpeedAtRef)) { parachuteTypes[p.maxSafeSpeedAtRef] = (parachuteTypes[p.maxSafeSpeedAtRef].Item1 + simDrag.areaDrag, Mathf.Max(parachuteTypes[p.maxSafeSpeedAtRef].Item2, p.Anim[p.semiDeployedAnimation].length / p.semiDeploymentSpeed + p.Anim[p.fullyDeployedAnimation].length / p.deploymentSpeed)); } else { parachuteTypes.Add(p.maxSafeSpeedAtRef, (simDrag.areaDrag, p.Anim[p.semiDeployedAnimation].length / p.semiDeploymentSpeed + p.Anim[p.fullyDeployedAnimation].length / p.deploymentSpeed)); } } } float totalFriction = 0; double lastSpeed = 0; foreach (var pt in parachuteTypes) { double effSpeed = Math.Min(curSpeed, pt.Key); // base equation deceleration term + chute reaction term with deceleration taken from moving equations with netwon friction // during reaction time we hardly break, so assume float friction_const = pt.Value.Item1 * 0.0005f * (float)curDensity * PhysicsGlobals.DragMultiplier * PhysicsGlobals.DragCubeMultiplier * PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)(effSpeed * curDensity)); if (breakDistance == 0) // first term for highest speed { breakDistance = (float)vesselState.mass / friction_const * Math.Log(effSpeed) + effSpeed * pt.Value.Item2; undeployedDistance = effSpeed * pt.Value.Item2;// - vesselState.mass / friction_const ; maxSpeed = lastSpeed = effSpeed; totalFriction = friction_const; //Debug.Log(String.Format("ParachuteInfo.update has first chute type with friction={0:F4} and effSpeed={1:F0}, delay={3:F1} results break dist={2:F0}", friction_const, effSpeed, breakDistance, pt.Value.Item2)); } else { // calculate slowdown during parachute opening using old friction effSpeed *= (float)vesselState.mass / (totalFriction * pt.Value.Item2 * effSpeed + vesselState.mass); breakDistance -= (float)vesselState.mass / (totalFriction + friction_const) * friction_const / totalFriction * Math.Log(effSpeed); undeployedDistance += pt.Value.Item2 * effSpeed;// + vesselState.mass * lastSpeed / (totalFriction * effSpeed) - vesselState.mass / (totalFriction + friction_const); lastSpeed = effSpeed; totalFriction += friction_const; //Debug.Log(String.Format("ParachuteInfo.update has next chute type with friction={0:F4} and effSpeed={1:F0}, delay={3:F1} results break dist={2:F0}", friction_const, effSpeed, breakDistance, pt.Value.Item2)); } } if (totalFriction > 0) { double terminalSpeed = Math.Sqrt((float)vesselState.mass / totalFriction * vesselState.mainBody.GeeASL * PhysicsGlobals.GravitationalAcceleration); //we are not getting slower than terminal velocity, so substract this as best guess for integration constant v breakDistance -= (float)vesselState.mass / totalFriction * Math.Log(terminalSpeed); //undeployedDistance += vesselState.mass * lastSpeed / (totalFriction * terminalSpeed); //Debug.Log(String.Format("ParachuteInfo.update has terminal velocity term with friction={0:F4} and terminalSpeed={1:F0} results break dist={2:F0}", totalFriction, terminalSpeed, breakDistance)); } Debug.Log(String.Format("ParachuteInfo.update for speed={0:F0} gives maxSpeed={1:F0} break dist={2:F0} undeployedDistance={3:F0}", curSpeed, maxSpeed, breakDistance, undeployedDistance)); }