From 83391470e5484b60b2635e209aa7d6d96a35cf4b Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Mon, 2 May 2022 15:29:19 +0200 Subject: [PATCH 1/8] Fix various NREs. Calculate vessel orientation without using the NavBall (which doesn't exist in IVA mode). --- .../FARGUI/FARFlightGUI/PhysicsCalcs.cs | 58 ++++++++++++++----- .../FARPartGeometry/VehicleVoxel.cs | 3 +- .../LEGACYferram4/FARControllableSurface.cs | 2 + .../RealChuteLite/ChuteCalculator.cs | 3 + .../RealChuteLite/RealChuteFAR.cs | 16 ++--- 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/FerramAerospaceResearch/FARGUI/FARFlightGUI/PhysicsCalcs.cs b/FerramAerospaceResearch/FARGUI/FARFlightGUI/PhysicsCalcs.cs index 4fa61a2b0..f58fcd926 100644 --- a/FerramAerospaceResearch/FARGUI/FARFlightGUI/PhysicsCalcs.cs +++ b/FerramAerospaceResearch/FARGUI/FARFlightGUI/PhysicsCalcs.cs @@ -60,7 +60,7 @@ internal class PhysicsCalcs private readonly FARCenterQuery aeroForces = new FARCenterQuery(); private readonly int intakeAirId; private readonly double intakeAirDensity = 1; - private NavBall _navball; + // private NavBall _navball; private List _currentAeroModules; private List _LEGACY_currentWingAeroModel = new List(); @@ -188,11 +188,11 @@ private void CalculateForceBreakdown(Vector3d velVectorNorm) vesselInfo.liftToDragRatio = vesselInfo.liftForce / vesselInfo.dragForce; } - private void GetNavball() - { - if (HighLogic.LoadedSceneIsFlight) - _navball = Object.FindObjectOfType(); - } + // private void GetNavball() + // { + // if (HighLogic.LoadedSceneIsFlight) + // _navball = Object.FindObjectOfType(); + // } private void CalculateVesselOrientation(Vector3d velVectorNorm) { @@ -215,17 +215,43 @@ private void CalculateVesselOrientation(Vector3d velVectorNorm) if (double.IsNaN(vesselInfo.sideslipAngle)) vesselInfo.sideslipAngle = 0; - if (_navball == null) - GetNavball(); - if (!_navball) - return; - Quaternion vesselRot = Quaternion.Inverse(_navball.relativeGymbal); + // if (_navball == null) + // GetNavball(); + // if (!_navball) + // return; + // Quaternion vesselRot = Quaternion.Inverse(_navball.relativeGymbal); + + // vesselInfo.headingAngle = vesselRot.eulerAngles.y; + // vesselInfo.pitchAngle = + // vesselRot.eulerAngles.x > 180 ? 360 - vesselRot.eulerAngles.x : -vesselRot.eulerAngles.x; + // vesselInfo.rollAngle = + // vesselRot.eulerAngles.z > 180 ? 360 - vesselRot.eulerAngles.z : -vesselRot.eulerAngles.z; + + // Gives the same numbers as the above, but also works in IVA mode which doesn't have a NavBall instance. + var localUp = (_vessel.transform.position - _vessel.mainBody.transform.position).normalized; + var east = Vector3.Cross(localUp, _vessel.mainBody.RotationAxis).normalized; + var north = Vector3.Cross(east, localUp).normalized; + vesselInfo.headingAngle = (SignedAngle(north, Vector3.ProjectOnPlane(up, localUp).normalized, localUp) + 360f) % 360f; + vesselInfo.pitchAngle = 90f - SignedAngle(localUp, up, Vector3.Cross(localUp, up).normalized); + vesselInfo.rollAngle = SignedAngle(Vector3.Cross(localUp, up).normalized, right, -up); + } - vesselInfo.headingAngle = vesselRot.eulerAngles.y; - vesselInfo.pitchAngle = - vesselRot.eulerAngles.x > 180 ? 360 - vesselRot.eulerAngles.x : -vesselRot.eulerAngles.x; - vesselInfo.rollAngle = - vesselRot.eulerAngles.z > 180 ? 360 - vesselRot.eulerAngles.z : -vesselRot.eulerAngles.z; + /// + /// A more precise version of Vector3.SignedAngle at low angles. + /// From https://www.reddit.com/r/Unity3D/comments/aruqz9/if_vector3angle_is_too_imprecise_or_jittery_at/ + /// + /// + /// + /// + /// + float SignedAngle(Vector3 from, Vector3 to, Vector3 axis) + { + var cross = Vector3.Cross(from, to); + var dot = Vector3.Dot(from, to); + var angle = Mathf.Atan2(cross.magnitude, dot) * Mathf.Rad2Deg; + if (Vector3.Dot(axis, cross) < 0.0f) + angle = -angle; + return angle; } private void CalculateEngineAndIntakeBasedParameters(double vesselSpeed) diff --git a/FerramAerospaceResearch/FARPartGeometry/VehicleVoxel.cs b/FerramAerospaceResearch/FARPartGeometry/VehicleVoxel.cs index 1d501ceaa..395d8b1ad 100644 --- a/FerramAerospaceResearch/FARPartGeometry/VehicleVoxel.cs +++ b/FerramAerospaceResearch/FARPartGeometry/VehicleVoxel.cs @@ -295,7 +295,7 @@ private void BuildVoxel(List geoModules, bool multiThreaded, threadsQueued = Environment.ProcessorCount - 1; if (!multiThreaded) - //Go through it backwards; this ensures that children (and so interior to cargo bay parts) are handled first + //Go through it backwards; this ensures that children (and so interior to cargo bay parts) are handled first { foreach (GeometryPartModule m in geoModules) { @@ -1354,6 +1354,7 @@ public void ClearVisualVoxels() return; VoxelizationThreadpool.Instance.RunOnMainThread(() => { + if (voxelMesh == null) { FARLogger.Debug($"Visual voxel has disappeared!"); return; } FARLogger.Debug("Clearing visual voxels"); voxelMesh.gameObject.SetActive(false); voxelMesh.Clear(); diff --git a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs index 9e0878210..73d792032 100644 --- a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs +++ b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs @@ -547,6 +547,8 @@ public void CalculateSurfaceFunctions() float mass = 0; foreach (Part p in VesselPartList) { + if (p == null) + continue; CoM += p.transform.position * p.mass; mass += p.mass; } diff --git a/FerramAerospaceResearch/RealChuteLite/ChuteCalculator.cs b/FerramAerospaceResearch/RealChuteLite/ChuteCalculator.cs index 431c6f3e5..4f595612c 100644 --- a/FerramAerospaceResearch/RealChuteLite/ChuteCalculator.cs +++ b/FerramAerospaceResearch/RealChuteLite/ChuteCalculator.cs @@ -24,7 +24,10 @@ private void Start() DragCube semi = prefab.DragCubes.Cubes.Find(c => c.Name == "SEMIDEPLOYED"), deployed = prefab.DragCubes.Cubes.Find(c => c.Name == "DEPLOYED"); if (semi == null || deployed == null) + { FARLogger.Info("" + part.title + " cannot find drag cube for RealChuteLite"); + continue; + } module.preDeployedDiameter = GetApparentDiameter(semi); module.deployedDiameter = GetApparentDiameter(deployed); part.moduleInfos.Find(m => m.moduleName == "RealChute").info = module.GetInfo(); diff --git a/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs b/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs index 4445bb15b..18523c8a6 100644 --- a/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs +++ b/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs @@ -443,21 +443,21 @@ public void AssumeDragCubePosition(string cubeName) case "STOWED": { parachute.gameObject.SetActive(false); - cap.gameObject.SetActive(true); + if (cap != null) cap.gameObject.SetActive(true); break; } case "RCDEPLOYED": //This is not a predeployed state, no touchy { parachute.gameObject.SetActive(false); - cap.gameObject.SetActive(false); + if (cap != null) cap.gameObject.SetActive(false); break; } case "SEMIDEPLOYED": // stock { parachute.gameObject.SetActive(true); - cap.gameObject.SetActive(false); + if (cap != null) cap.gameObject.SetActive(false); // to the end of the animation part.SkipToAnimationTime(semiDeployedAnimation, 0, 1); break; @@ -466,7 +466,7 @@ public void AssumeDragCubePosition(string cubeName) case "DEPLOYED": // stock { parachute.gameObject.SetActive(true); - cap.gameObject.SetActive(false); + if (cap != null) cap.gameObject.SetActive(false); // to the end of the animation part.SkipToAnimationTime(fullyDeployedAnimation, 0, 1); break; @@ -580,7 +580,7 @@ public void GUIRepack() DeploymentState = DeploymentStates.STOWED; randomTimer.Reset(); time = 0; - cap.gameObject.SetActive(true); + if (cap != null) cap.gameObject.SetActive(true); part.DragCubes.SetCubeWeight("PACKED", 1); part.DragCubes.SetCubeWeight("RCDEPLOYED", 0); } @@ -727,7 +727,7 @@ public void PreDeploy() part.Effect("rcpredeploy"); DeploymentState = DeploymentStates.PREDEPLOYED; parachute.gameObject.SetActive(true); - cap.gameObject.SetActive(false); + if (cap != null) cap.gameObject.SetActive(false); part.PlayAnimation(semiDeployedAnimation, semiDeploymentSpeed); dragTimer.Start(); part.DragCubes.SetCubeWeight("PACKED", 0); @@ -1184,7 +1184,7 @@ public override void OnStart(StartState startState) initiated = true; armed = false; chuteCount = maxSpares; - cap.gameObject.SetActive(true); + if (cap != null) cap.gameObject.SetActive(true); } float tmpPartMass = TotalMass; @@ -1213,7 +1213,7 @@ public override void OnStart(StartState startState) if (DeploymentState != DeploymentStates.STOWED) { part.stackIcon.SetIconColor(XKCDColors.Red); - cap.gameObject.SetActive(false); + if (cap != null) cap.gameObject.SetActive(false); } if (staged && IsDeployed) From b47026d8c585662c818b682c844ec5d725d8ad75 Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Mon, 2 May 2022 19:50:41 +0200 Subject: [PATCH 2/8] Use the more elegant 'cap?.' instead of 'if (cap != null)'. --- .../RealChuteLite/RealChuteFAR.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs b/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs index 18523c8a6..97fa38469 100644 --- a/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs +++ b/FerramAerospaceResearch/RealChuteLite/RealChuteFAR.cs @@ -443,21 +443,21 @@ public void AssumeDragCubePosition(string cubeName) case "STOWED": { parachute.gameObject.SetActive(false); - if (cap != null) cap.gameObject.SetActive(true); + cap?.gameObject.SetActive(true); break; } case "RCDEPLOYED": //This is not a predeployed state, no touchy { parachute.gameObject.SetActive(false); - if (cap != null) cap.gameObject.SetActive(false); + cap?.gameObject.SetActive(false); break; } case "SEMIDEPLOYED": // stock { parachute.gameObject.SetActive(true); - if (cap != null) cap.gameObject.SetActive(false); + cap?.gameObject.SetActive(false); // to the end of the animation part.SkipToAnimationTime(semiDeployedAnimation, 0, 1); break; @@ -466,7 +466,7 @@ public void AssumeDragCubePosition(string cubeName) case "DEPLOYED": // stock { parachute.gameObject.SetActive(true); - if (cap != null) cap.gameObject.SetActive(false); + cap?.gameObject.SetActive(false); // to the end of the animation part.SkipToAnimationTime(fullyDeployedAnimation, 0, 1); break; @@ -580,7 +580,7 @@ public void GUIRepack() DeploymentState = DeploymentStates.STOWED; randomTimer.Reset(); time = 0; - if (cap != null) cap.gameObject.SetActive(true); + cap?.gameObject.SetActive(true); part.DragCubes.SetCubeWeight("PACKED", 1); part.DragCubes.SetCubeWeight("RCDEPLOYED", 0); } @@ -727,7 +727,7 @@ public void PreDeploy() part.Effect("rcpredeploy"); DeploymentState = DeploymentStates.PREDEPLOYED; parachute.gameObject.SetActive(true); - if (cap != null) cap.gameObject.SetActive(false); + cap?.gameObject.SetActive(false); part.PlayAnimation(semiDeployedAnimation, semiDeploymentSpeed); dragTimer.Start(); part.DragCubes.SetCubeWeight("PACKED", 0); @@ -1184,7 +1184,7 @@ public override void OnStart(StartState startState) initiated = true; armed = false; chuteCount = maxSpares; - if (cap != null) cap.gameObject.SetActive(true); + cap?.gameObject.SetActive(true); } float tmpPartMass = TotalMass; @@ -1213,7 +1213,7 @@ public override void OnStart(StartState startState) if (DeploymentState != DeploymentStates.STOWED) { part.stackIcon.SetIconColor(XKCDColors.Red); - if (cap != null) cap.gameObject.SetActive(false); + cap?.gameObject.SetActive(false); } if (staged && IsDeployed) From df35aa75f61bf799e3cc8bb6e4f1216d2f98363a Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Mon, 2 May 2022 23:10:00 +0200 Subject: [PATCH 3/8] Fix KeyNotFoundException in FARAeroSection.UpdateAeroSection using TryGetValue. --- .../FARAeroComponents/FARAeroSection.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/FerramAerospaceResearch/FARAeroComponents/FARAeroSection.cs b/FerramAerospaceResearch/FARAeroComponents/FARAeroSection.cs index ac84700d2..18c8044bd 100644 --- a/FerramAerospaceResearch/FARAeroComponents/FARAeroSection.cs +++ b/FerramAerospaceResearch/FARAeroComponents/FARAeroSection.cs @@ -143,12 +143,13 @@ Dictionary partWorldToLocalMatrixDict Vector3 worldSpaceAvgPos = Vector3.zero; float totalDragFactor = 0; + PartTransformInfo partTransformInfo; for (int i = 0; i < moduleList.Count; i++) { Part p = moduleList[i].part; - if (!partWorldToLocalMatrixDict.ContainsKey(p)) + if (!partWorldToLocalMatrixDict.TryGetValue(p, out partTransformInfo)) continue; - worldSpaceAvgPos += partWorldToLocalMatrixDict[p].worldPosition * dragFactor[i]; + worldSpaceAvgPos += partTransformInfo.worldPosition * dragFactor[i]; totalDragFactor += dragFactor[i]; } @@ -160,8 +161,10 @@ Dictionary partWorldToLocalMatrixDict for (int i = 0; i < moduleList.Count; i++) { - var data = new PartData {aeroModule = moduleList[i]}; - Matrix4x4 transformMatrix = partWorldToLocalMatrixDict[data.aeroModule.part].worldToLocalMatrix; + var data = new PartData { aeroModule = moduleList[i] }; + if (!partWorldToLocalMatrixDict.TryGetValue(data.aeroModule.part, out partTransformInfo)) + continue; + Matrix4x4 transformMatrix = partTransformInfo.worldToLocalMatrix; Vector3 forceCenterWorldSpace = centroidLocationAlongxRef + Vector3.ProjectOnPlane(partWorldToLocalMatrixDict[data.aeroModule.part] From a5619dce8b0696aaffab7af77cef729467c60f61 Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Mon, 2 May 2022 23:57:29 +0200 Subject: [PATCH 4/8] Initialise _vehicleCrossSection as an empty array to avoid an NRE. --- .../FARAeroComponents/VehicleAerodynamics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs b/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs index 6089e71dd..c09f3e99c 100644 --- a/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs +++ b/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs @@ -71,7 +71,7 @@ internal class VehicleAerodynamics private readonly List weighting = new List(); private VehicleVoxel _voxel; - private VoxelCrossSection[] _vehicleCrossSection = new VoxelCrossSection[1]; + private VoxelCrossSection[] _vehicleCrossSection = Array.Empty(); private double[] _ductedAreaAdjustment = new double[1]; private int _voxelCount; From 24747427e32e7b98abdb4a743e683ddd1ca07d50 Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Tue, 3 May 2022 01:34:57 +0200 Subject: [PATCH 5/8] Update the ship parts list before iterating through it (avoids null parts). --- FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs index 73d792032..4cef6f475 100644 --- a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs +++ b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs @@ -545,6 +545,7 @@ public void CalculateSurfaceFunctions() Vector3 CoM = Vector3.zero; float mass = 0; + UpdateShipPartsList(); foreach (Part p in VesselPartList) { if (p == null) From 5fc2b1d054e5f4187424c23bd9f9fd0215dad7a5 Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Tue, 3 May 2022 01:49:47 +0200 Subject: [PATCH 6/8] Remove the now unneeded 'p == null' check. --- FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs index 4cef6f475..546a5b2ef 100644 --- a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs +++ b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs @@ -548,8 +548,6 @@ public void CalculateSurfaceFunctions() UpdateShipPartsList(); foreach (Part p in VesselPartList) { - if (p == null) - continue; CoM += p.transform.position * p.mass; mass += p.mass; } From e45e3b43b819229a75f16ca1ccbabf44b0f6ed6f Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Tue, 3 May 2022 16:09:17 +0200 Subject: [PATCH 7/8] Call UpdateShipPartsList before invoking OnVesselPartsChange and before the initial CalculateSurfaceFunctions. --- .../LEGACYferram4/FARControllableSurface.cs | 4 +++- FerramAerospaceResearch/LEGACYferram4/FARPartModule.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs index 546a5b2ef..426316c81 100644 --- a/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs +++ b/FerramAerospaceResearch/LEGACYferram4/FARControllableSurface.cs @@ -467,7 +467,10 @@ public override void Initialization() public override void FixedUpdate() { if (justStarted) + { + UpdateShipPartsList(); CalculateSurfaceFunctions(); + } if (HighLogic.LoadedSceneIsFlight && !(part is null) && !(vessel is null)) { @@ -545,7 +548,6 @@ public void CalculateSurfaceFunctions() Vector3 CoM = Vector3.zero; float mass = 0; - UpdateShipPartsList(); foreach (Part p in VesselPartList) { CoM += p.transform.position * p.mass; diff --git a/FerramAerospaceResearch/LEGACYferram4/FARPartModule.cs b/FerramAerospaceResearch/LEGACYferram4/FARPartModule.cs index 2e67dbf4e..fb3d38142 100644 --- a/FerramAerospaceResearch/LEGACYferram4/FARPartModule.cs +++ b/FerramAerospaceResearch/LEGACYferram4/FARPartModule.cs @@ -68,6 +68,7 @@ public Collider[] PartColliders public void ForceOnVesselPartsChange() { + UpdateShipPartsList(); OnVesselPartsChange?.Invoke(); } @@ -78,7 +79,6 @@ public void Start() public virtual void Initialization() { - OnVesselPartsChange = UpdateShipPartsList; UpdateShipPartsList(); } From 0c923d5d96aee8721d7607fe8bc9047869ae6f15 Mon Sep 17 00:00:00 2001 From: Brett Ryland Date: Tue, 3 May 2022 16:17:45 +0200 Subject: [PATCH 8/8] Use backward finite difference for the 'back' index of the first derivative calculation in CalculateSonicPressure. --- .../FARAeroComponents/VehicleAerodynamics.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs b/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs index c09f3e99c..4d357746c 100644 --- a/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs +++ b/FerramAerospaceResearch/FARAeroComponents/VehicleAerodynamics.cs @@ -1651,7 +1651,7 @@ double sectionThickness } else if (i == back) { - firstDerivArea = vehicleCrossSection[i].area - vehicleCrossSection[i + 1].area; + firstDerivArea = vehicleCrossSection[i - 1].area - vehicleCrossSection[i].area; firstDerivArea /= sectionThickness; } else @@ -1692,7 +1692,7 @@ double sectionThickness } else if (i == back) { - firstDerivArea = vehicleCrossSection[i].area - vehicleCrossSection[i + 1].area; + firstDerivArea = vehicleCrossSection[i - 1].area - vehicleCrossSection[i].area; firstDerivArea /= -sectionThickness; } else