protected virtual void Set(Part p, ReentrySimulation.SimCurves _simCurves) { Rigidbody rigidbody = p.rb; totalMass = rigidbody == null ? 0 : rigidbody.mass; // TODO : check if we need to use this or the one without the childMass shieldedFromAirstream = p.ShieldedFromAirstream; noDrag = rigidbody == null && !PhysicsGlobals.ApplyDragToNonPhysicsParts; hasLiftModule = p.hasLiftModule; bodyLiftMultiplier = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier; simCurves = _simCurves; cubes = new DragCubeList(); CopyDragCubesList(p.DragCubes, cubes); // Rotation to convert the vessel space vesselVelocity to the part space vesselVelocity vesselToPart = Quaternion.LookRotation(p.vessel.GetTransform().InverseTransformDirection(p.transform.forward), p.vessel.GetTransform().InverseTransformDirection(p.transform.up)).Inverse(); //DragCubeMultiplier = PhysicsGlobals.DragCubeMultiplier; //DragMultiplier = PhysicsGlobals.DragMultiplier; //if (p.dragModel != Part.DragModel.CUBE) // MechJebCore.print(p.name + " " + p.dragModel); //oPart = p; }
//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 }
// Unfortunately the DragCubeList SetDrag method is not thread safe // so here is a thread safe version protected void SetDrag(Vector3d dragVector, float machNumber) { areaDrag = 0; liftForce = Vector3d.zero; if (cubes.None) { return; } for (int i = 0; i < 6; i++) { Vector3d faceDirection = DragCubeList.GetFaceDirection((DragCube.DragFace)i); float dragDot = (float)Vector3d.Dot(dragVector, faceDirection); float dragValue = DragCurveValue((dragDot + 1f) * 0.5f, machNumber); float faceAreaDrag = cubes.AreaOccluded[i] * dragValue; areaDrag = areaDrag + faceAreaDrag * cubes.WeightedDrag[i]; if (dragDot > 0f) { float lift = simCurves.LiftCurve.Evaluate(dragDot); if (!double.IsNaN(lift)) { liftForce = liftForce - faceDirection * (dragDot * cubes.AreaOccluded[i] * cubes.WeightedDrag[i] * lift); } } } }
private void Set(Part p, ReentrySimulation.SimCurves _simCurves) { totalMass = p.mass + p.GetResourceMass() + p.GetPhysicslessChildMass(); shieldedFromAirstream = p.ShieldedFromAirstream; noDrag = p.rb == null && !PhysicsGlobals.ApplyDragToNonPhysicsParts; hasLiftModule = p.hasLiftModule; bodyLiftMultiplier = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier; simCurves = _simCurves; // TODO : choose either method : // - use the part cube but have the risk that the part change the cubes values (stagging, ...) while we do the sim // - use a copy of the cubes but use more mem cubes = p.DragCubes; //cubes = new DragCubeList(); //CopyDragCubesList(p.DragCubes, cubes); // Rotation to convert the vessel space vesselVelocity to the part space vesselVelocity vesselToPart = Quaternion.LookRotation(p.vessel.GetTransform().InverseTransformDirection(p.transform.forward), p.vessel.GetTransform().InverseTransformDirection(p.transform.up)).Inverse(); //DragCubeMultiplier = PhysicsGlobals.DragCubeMultiplier; //DragMultiplier = PhysicsGlobals.DragMultiplier; if (p.dragModel != Part.DragModel.CUBE) { MechJebCore.print(p.name + " " + p.dragModel); } //oPart = p; }
public 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 }
private void BuildCubeData() { DragCubeList cubes = part.DragCubes; XP = $"{cubes.WeightedArea[0]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.XP)):F2})"; XN = $"{cubes.WeightedArea[1]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.XN)):F2})"; YP = $"{cubes.WeightedArea[2]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.YP)):F2})"; YN = $"{cubes.WeightedArea[3]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.YN)):F2})"; ZP = $"{cubes.WeightedArea[4]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.ZP)):F2})"; ZN = $"{cubes.WeightedArea[5]:F2} ({cubes.GetCubeAreaDir(DragCubeList.GetFaceDirection(DragCube.DragFace.ZN)):F2})"; }
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(); }
public void setup() { //Get rid of decoupler shroud module if no top node found if (destroyShroudIfNoTopNode()) { return; } starDragCubes = part.DragCubes; getTextureNames(); //Remove copied decoupler shroud when copied Transform copiedDecouplerShroud = transform.FindChild("DecouplerShroud"); if (copiedDecouplerShroud != null) { Destroy(copiedDecouplerShroud.gameObject); shroudCylinders = null; } //Set up events part.OnEditorAttach += partReattached; part.OnEditorDetach += partDetached; Fields[nameof(shroudEnabled)].OnValueModified += activeToggled; Fields[nameof(autoDetectSize)].OnValueModified += setButtonActive; Fields[nameof(autoDetectSize)].OnValueModified += detectSize; Fields[nameof(topWidth)].OnValueModified += updateShroud; Fields[nameof(botWidth)].OnValueModified += updateShroud; Fields[nameof(height)].OnValueModified += updateShroud; Fields[nameof(thickness)].OnValueModified += updateShroud; Fields[nameof(vertOffset)].OnValueModified += updateShroud; Fields[nameof(textureIndex)].OnValueModified += updateTexture; setButtonActive(); if (HighLogic.LoadedSceneIsFlight) { createShroudGO(); if (GetShroudedPart() != null && shroudEnabled) { GetShroudedPart().AddShield(this); } } else { if (part.isAttached) { createShroudGO(); } } detectSize(); setupFinished = true; }
static float GetFacingAreaSum(Vector3 direction, float[] faceAreaArray) { float num = 0; for (int i = 0; i < 6; i++) { float num2 = Mathf.Clamp01(Vector3.Dot(direction, DragCubeList.GetFaceDirection((DragCube.DragFace)i))); num += faceAreaArray [i] * num2; } return(num); }
//Retreives an "apparent" diameter from a DragCube private float GetApparentDiameter(DragCube cube) { float area = 0; for (int i = 0; i < 6; i++) { area += cube.Area[i] * cube.Drag[i] * PhysicsGlobals.DragCurveValue((Vector3.Dot(Vector3.up, DragCubeList.GetFaceDirection((DragCube.DragFace)i)) + 1f) * 0.5f, 0); } return((float)(Math.Max(Math.Round(Math.Sqrt((area * PhysicsGlobals.DragCubeMultiplier * PhysicsGlobals.DragMultiplier) / Math.PI) * 2d, 1, MidpointRounding.AwayFromZero), 0.1))); }
public DragCubeList.CubeData AddSurfaceDragDirection(Vector3 direction, float machNumber) { part.DragCubes.SetDrag(direction, machNumber); //var liftCurves = GetPrivate<PhysicsGlobals.LiftingSurfaceCurve>("liftCurves", part.DragCubes); var liftCurves = part.DragCubes.BodyLiftCurve; float num = 0; DragCubeList.CubeData result = default(DragCubeList.CubeData); result.dragVector = direction; for (int i = 0; i < 6; i++) { Vector3 faceDirection = DragCubeList.GetFaceDirection((DragCube.DragFace)i); float num2 = Vector3.Dot(direction, faceDirection); float dotNormalized = (num2 + 1) * 0.5f; float num3 = PhysicsGlobals.DragCurveValue(PhysicsGlobals.SurfaceCurves, dotNormalized, machNumber); float num4 = this.areaOccluded [i] * num3; float num5 = this.weightedDrag [i]; float num6 = num5; if (num6 < 1) { num6 = PhysicsGlobals.DragCurveCd.Evaluate(num6); } result.area += num4; result.areaDrag += num4 * num6; result.crossSectionalArea += this.areaOccluded [i] * Mathf.Clamp01(num2); if (num5 < 0.01) { num5 = 1; } if (num5 < 1) { num5 = 1 / num5; } result.exposedArea += num4 / PhysicsGlobals.DragCurveMultiplier.Evaluate(machNumber) * num5; if (num2 > 0) { num += num2; double num7 = (double)liftCurves.liftCurve.Evaluate(num2); if (!double.IsNaN(num7)) { result.liftForce += -faceDirection * (num2 * this.areaOccluded [i] * this.weightedDrag [i] * (float)num7); } result.depth += num2 * this.weightedDepth [i]; result.dragCoeff += num2 * num6; } } float num8 = 1 / num; result.depth *= num8; result.dragCoeff *= num8; return(result); }
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; }
void AreaToCubeOperation(Vector3 direction, float area, Func <float, float, float> operation) { for (int i = 0; i < 6; i++) { float num = Mathf.Clamp01(Vector3.Dot(direction, DragCubeList.GetFaceDirection((DragCube.DragFace)i))); if (num > 0) { this.areaOccluded[i] = operation(area * num, this.areaOccluded[i]); float num2 = this.weightedArea [i] - this.areaOccluded[i]; if (this.areaOccluded[i] > 0) { this.weightedDrag[i] = Mathf.Max(0, (this.weightedDragOrig[i] * this.weightedArea[i] - num2) / this.areaOccluded[i]); } else { this.weightedDrag[i] = 1E-05f; } } } }
//Retrieves an "apparent" diameter from a DragCube private static float GetApparentDiameter(DragCube cube) { float area = 0; for (int i = 0; i < 6; i++) { // TODO 1.2: according to API docs this method should have only 2 arguments but it has 3 area += cube.Area[i] * cube.Drag[i] * PhysicsGlobals.DragCurveValue(PhysicsGlobals.SurfaceCurves, (Vector3.Dot(Vector3.up, DragCubeList .GetFaceDirection((DragCube.DragFace)i)) + 1) * 0.5f, 0); } return((float)Math.Max(Math.Round(Math.Sqrt(area * 0.1f * PhysicsGlobals.DragMultiplier / Math.PI) * 2, 1, MidpointRounding.AwayFromZero), 0.1)); }
public void setup() { starDragCubes = part.DragCubes; getTextures(); //Set up events part.OnEditorAttach += partReattached; part.OnEditorDetach += partDetached; Fields[nameof(shroudEnabled)].OnValueModified += activeToggled; Fields[nameof(autoDetectSize)].OnValueModified += setButtonActive; Fields[nameof(autoDetectSize)].OnValueModified += detectSize; Fields[nameof(topWidth)].OnValueModified += updateShroud; Fields[nameof(botWidth)].OnValueModified += updateShroud; Fields[nameof(height)].OnValueModified += updateShroud; Fields[nameof(thickness)].OnValueModified += updateShroud; Fields[nameof(vertOffset)].OnValueModified += updateShroud; Fields[nameof(textureIndex)].OnValueModified += updateTexture; setButtonActive(); if (HighLogic.LoadedSceneIsFlight) { createShroudGO(); if (GetShroudedPart() != null && shroudEnabled) { GetShroudedPart().AddShield(this); } } else { if (part.isAttached) { createShroudGO(); } } detectSize(); }
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)); }
public override void OnStart(PartModule.StartState state) { if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) { return; } if (!CompatibilityChecker.IsAllCompatible()) { Actions.ForEach(a => a.active = false); Events.ForEach(e => { e.active = false; e.guiActive = false; e.guiActiveEditor = false; }); Fields["chuteCount"].guiActive = false; return; } //Staging icon this.part.stagingIcon = "PARACHUTES"; InitializeAnimationSystem(); //First initiation of the part if (!this.initiated) { this.initiated = true; this.armed = false; this.chuteCount = maxSpares; this.cap.gameObject.SetActive(true); } this.part.mass = this.totalMass; this.massDelta = 0f; if ((object)(this.part.partInfo) != null && (object)(this.part.partInfo.partPrefab) != null) { this.massDelta = this.part.mass - this.part.partInfo.partPrefab.mass; } //Flight loading if (HighLogic.LoadedSceneIsFlight) { Random random = new Random(); this.randomTime = (float)random.NextDouble(); this.randomX = (float)(random.NextDouble() * 100); this.randomY = (float)(random.NextDouble() * 100); //Hide/show UI event addition GameEvents.onHideUI.Add(HideUI); GameEvents.onShowUI.Add(ShowUI); if (this.canRepack) { SetRepack(); } if (this.time != 0) { this.dragTimer = new PhysicsWatch(this.time); } if (this.deploymentState != DeploymentStates.STOWED) { this.part.stackIcon.SetIconColor(XKCDColors.Red); this.cap.gameObject.SetActive(false); } if (this.staged && this.isDeployed) { this.parachute.gameObject.SetActive(true); switch (this.deploymentState) { case DeploymentStates.PREDEPLOYED: this.part.SkipToAnimationTime(this.semiDeployedAnimation, this.semiDeploymentSpeed, Mathf.Clamp01(this.time)); break; case DeploymentStates.DEPLOYED: this.part.SkipToAnimationTime(this.fullyDeployedAnimation, this.deploymentSpeed, Mathf.Clamp01(this.time)); break; default: break; } } DragCubeList cubes = this.part.DragCubes; //Set stock cubes to 0 cubes.SetCubeWeight("PACKED", 0); cubes.SetCubeWeight("SEMIDEPLOYED", 0); cubes.SetCubeWeight("DEPLOYED", 0); //Sets RC cubes if (this.deploymentState == DeploymentStates.STOWED) { cubes.SetCubeWeight("PACKED", 1); cubes.SetCubeWeight("RCDEPLOYED", 0); } else { cubes.SetCubeWeight("PACKED", 0); cubes.SetCubeWeight("RCDEPLOYED", 1); } } //GUI this.window = new Rect(200, 100, 350, 400); this.drag = new Rect(0, 0, 350, 30); }
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 override void OnStart(StartState startState) { if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) { return; } if (!CompatibilityChecker.IsAllCompatible()) { Actions.ForEach(a => a.active = false); Events.ForEach(e => { e.active = false; e.guiActive = false; e.guiActiveEditor = false; }); Fields["chuteCount"].guiActive = false; return; } //Staging icon part.stagingIcon = "PARACHUTES"; InitializeAnimationSystem(); //First initiation of the part if (!initiated) { initiated = true; armed = false; chuteCount = maxSpares; cap.gameObject.SetActive(true); } float tmpPartMass = TotalMass; massDelta = 0; if (!(part.partInfo?.partPrefab is null)) { massDelta = tmpPartMass - part.partInfo.partPrefab.mass; } //Flight loading if (HighLogic.LoadedSceneIsFlight) { var random = new Random(); randomTime = (float)random.NextDouble(); randomX = (float)(random.NextDouble() * 100); randomY = (float)(random.NextDouble() * 100); //Hide/show UI event addition GameEvents.onHideUI.Add(HideUI); GameEvents.onShowUI.Add(ShowUI); GameEvents.onStageActivate.Add(OnStageActivate); if (CanRepack) { SetRepack(); } if (!time.NearlyEqual(0)) { dragTimer = new PhysicsWatch(time); } if (DeploymentState != DeploymentStates.STOWED) { part.stackIcon.SetIconColor(XKCDColors.Red); cap.gameObject.SetActive(false); } if (staged && IsDeployed) { parachute.gameObject.SetActive(true); switch (DeploymentState) { case DeploymentStates.PREDEPLOYED: part.SkipToAnimationTime(semiDeployedAnimation, semiDeploymentSpeed, Mathf.Clamp01(time)); break; case DeploymentStates.DEPLOYED: part.SkipToAnimationTime(fullyDeployedAnimation, deploymentSpeed, Mathf.Clamp01(time)); break; } } DragCubeList cubes = part.DragCubes; //Set stock cubes to 0 cubes.SetCubeWeight("PACKED", 0); cubes.SetCubeWeight("SEMIDEPLOYED", 0); cubes.SetCubeWeight("DEPLOYED", 0); //Sets RC cubes if (DeploymentState == DeploymentStates.STOWED) { cubes.SetCubeWeight("PACKED", 1); cubes.SetCubeWeight("RCDEPLOYED", 0); } else { cubes.SetCubeWeight("PACKED", 0); cubes.SetCubeWeight("RCDEPLOYED", 1); } } //GUI window = new Rect(200, 100, 350, 400); drag = new Rect(0, 0, 350, 30); }
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); }
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 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); }
//******************************************************* 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 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); }
//******************************************************* 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); }