diff --git a/Source/Harmony/OrbitDriver.cs b/Source/Harmony/OrbitDriver.cs new file mode 100644 index 0000000000..300cf359f3 --- /dev/null +++ b/Source/Harmony/OrbitDriver.cs @@ -0,0 +1,65 @@ +using HarmonyLib; +using UnityEngine; + +namespace RealismOverhaul.Harmony +{ + [HarmonyPatch(typeof(OrbitDriver))] + internal class PatchOrbitDriver + { + [HarmonyPrefix] + [HarmonyPatch(nameof(OrbitDriver.updateFromParameters), typeof(bool))] + internal static bool Prefix_updateFromParameters(OrbitDriver __instance, bool setPosition) + { + if (!setPosition + || !VesselModuleRotationRO.IsEnabled + || __instance.vessel == null + || (__instance.vessel.loaded && !__instance.vessel.packed) + || (VesselModuleRotationRO.IgnoreRot(__instance.vessel))) + return true; + + __instance.updateUT = Planetarium.GetUniversalTime(); + __instance.orbit.UpdateFromUT(__instance.updateUT); + __instance.pos = __instance.orbit.pos; + __instance.vel = __instance.orbit.vel; + __instance.pos.Swizzle(); + __instance.vel.Swizzle(); + if (double.IsNaN(__instance.pos.x)) + { + MonoBehaviour.print("ObT : " + __instance.orbit.ObT + "\nM : " + __instance.orbit.meanAnomaly + "\nE : " + __instance.orbit.eccentricAnomaly + "\nV : " + + __instance.orbit.trueAnomaly + "\nRadius: " + __instance.orbit.radius + "\nvel: " + __instance.vel.ToString() + "\nAN: " + __instance.orbit.an.ToString() + + "\nperiod: " + __instance.orbit.period + "\n"); + + Debug.LogWarning("[OrbitDriver Warning!]: " + __instance.vessel.GetDisplayName() + " had a NaN Orbit and was removed."); + __instance.vessel.Unload(); + UnityEngine.Object.Destroy(__instance.vessel.gameObject); + return false; + } + VesselModuleRotationRO mod = null; + foreach (var vm in __instance.vessel.vesselModules) + { + if (vm is VesselModuleRotationRO vmr) + { + mod = vmr; + break; + } + } + if (!__instance.reverse) + { + Vector3d offset = (QuaternionD)__instance.driverTransform.rotation * (Vector3d)__instance.vessel.localCoM; + Vector3d pos = __instance.referenceBody.position + __instance.pos - offset; + if (mod != null) + mod.RailsUpdate(pos); + else + __instance.vessel.SetPosition(pos); + } + else + { + __instance.referenceBody.position = ((Vector3d)__instance.driverTransform.position) - __instance.pos; + if (mod != null) + mod.RailsUpdate(__instance.vessel.vesselTransform.position); + } + + return false; + } + } +} diff --git a/Source/RealismOverhaul.csproj b/Source/RealismOverhaul.csproj index 230ed9806b..586e1aed86 100644 --- a/Source/RealismOverhaul.csproj +++ b/Source/RealismOverhaul.csproj @@ -40,6 +40,7 @@ + diff --git a/Source/VesselModuleRotationRO.cs b/Source/VesselModuleRotationRO.cs index 0ff6e89b5e..79752080c8 100644 --- a/Source/VesselModuleRotationRO.cs +++ b/Source/VesselModuleRotationRO.cs @@ -95,7 +95,7 @@ private static bool CheckEnabled() return _isEnabled; } - private static bool IsEnabled => _shouldCheckEnabled ? CheckEnabled() : _isEnabled; + public static bool IsEnabled => _shouldCheckEnabled ? CheckEnabled() : _isEnabled; private bool IsOverThreshold(Vector3 rot) { @@ -116,13 +116,31 @@ private Quaternion UnityRot() private void SetRot() { - if (IgnoreRot) + if (IgnoreRot(vessel)) return; vessel.SetRotation(UnityRot(), true); } - private bool IgnoreRot => vessel.situation == Vessel.Situations.PRELAUNCH || vessel.situation == Vessel.Situations.LANDED || vessel.situation == Vessel.Situations.SPLASHED; + private void SetPosRot(Quaternion rotation, Vector3d position) + { + if (!vessel.loaded) + { + vessel.vesselTransform.rotation = rotation; + vessel.vesselTransform.position = position; + return; + } + int count = vessel.parts.Count; + QuaternionD rotD = rotation; + for (int i = 0; i < count; i++) + { + Part part = vessel.parts[i]; + part.partTransform.rotation = rotation * part.orgRot; + part.partTransform.position = position + rotD * part.orgPos; + } + } + + public static bool IgnoreRot(Vessel vessel) => vessel.situation == Vessel.Situations.PRELAUNCH || vessel.situation == Vessel.Situations.LANDED || vessel.situation == Vessel.Situations.SPLASHED; protected override void OnLoad(ConfigNode node) { @@ -148,15 +166,11 @@ protected override void OnLoad(ConfigNode node) SetRot(); } - private void FixedUpdate() + public void RestoreState() { - if (!IsEnabled) - return; - if (_restoreManeuverHash) StoreManeuverHash(); - bool packRotate = false; if (vessel.loaded) { if (_restoreTarget) @@ -165,18 +179,8 @@ private void FixedUpdate() _lastTarget = vessel.targetObject; } - // Vessel is loaded but not in physics, either because - // - It is in the physics bubble but in non-physics timewarp - // - It has gone outside of the physics bubble - // - It was just loaded, is in the physics bubble and will be unpacked in a few frames - if (vessel.packed) - { - packRotate = true; - } - else if (FlightGlobals.ready) // The vessel is in physics simulation and fully loaded + if (!vessel.packed && FlightGlobals.ready) { - bool okToSaveAngularVelocity = true; - // Restoring previous SAS selection if (_restoreSAS) { @@ -188,18 +192,9 @@ private void FixedUpdate() _retrySASCount = 10; } } - - if (_restoreAngularVelocity) // Restoring saved rotation if it was above the threshold - { - // Debug.Log("[US] " + vessel.vesselName + " going OFF rails : restoring angular velocity, angvel=" + angularVelocity.magnitude); - ApplyAngularVelocity(); - okToSaveAngularVelocity = false; - _restoreAngularVelocity = false; - } - // When a vessel loads, VesselAutopilot gets enabled in Update based on SAS actiongroup being on // (the actiongroup is what's persisted). So we're going to run (in FixedUpdate) before that happens. - if (_retrySAS) + else if (_retrySAS) { if (_retrySASCount > 0) { @@ -216,51 +211,93 @@ private void FixedUpdate() // Debug.Log("[US] can't set autopilot mode."); } } - - // Saving angular velocity (if we can), SAS mode, and check target hold status - SaveOffRailsStatus(okToSaveAngularVelocity); } } - else if (FlightGlobals.ready) + else { - packRotate = true; if (_restoreTarget) { _restoreTarget = false; _lastTarget = vessel.protoVessel.targetInfo?.FindTarget(); } } + } - if (packRotate) - { - // Check if target / maneuver is modified/deleted during timewarp - bool holdValid = false; - if (autopilotTargetHold) - { - holdValid = TargetHoldValidity(); - if (TimeWarp.WarpMode == TimeWarp.Modes.HIGH && TimeWarp.CurrentRateIndex > 0) - autopilotTargetHold = holdValid; - } + private void FixedUpdate() + { + if (!IsEnabled) + return; + + RestoreState(); - // if we don't have over-thresh angular velocity and we do - // have a target, orient to the target - if (angularVelocity == Vector3.zero && autopilotTargetHold && holdValid) + //bool packRotate = false; + if (vessel.loaded) + { + // Vessel is loaded but not in physics, either because + // - It is in the physics bubble but in non-physics timewarp + // - It has gone outside of the physics bubble + // - It was just loaded, is in the physics bubble and will be unpacked in a few frames + if (vessel.packed) { - RotateTowardTarget(); - StoreRot(); + //packRotate = true; } - else + else if (FlightGlobals.ready) // The vessel is in physics simulation and fully loaded { - RotatePacked(); - // We don't store rot here, it relies on lastUT - // and original orientation + bool okToSaveAngularVelocity = true; + + if (_restoreAngularVelocity) // Restoring saved rotation if it was above the threshold + { + // Debug.Log("[US] " + vessel.vesselName + " going OFF rails : restoring angular velocity, angvel=" + angularVelocity.magnitude); + ApplyAngularVelocity(); + okToSaveAngularVelocity = false; + _restoreAngularVelocity = false; + } + + // Saving angular velocity (if we can), SAS mode, and check target hold status + SaveOffRailsStatus(okToSaveAngularVelocity); } } + //else + //{ + // packRotate = true; + + //} + + //if (packRotate) + //{ + // RailsUpdate(vessel.vesselTransform.position); + //} // Saving this FixedUpdate target, autopilot context and maneuver node, to check if they have changed in the next FixedUpdate SaveLastUpdateStatus(); } + public void RailsUpdate(Vector3d pos) + { + // Check if target / maneuver is modified/deleted during timewarp + bool holdValid = false; + if (autopilotTargetHold) + { + holdValid = TargetHoldValidity(); + if (TimeWarp.WarpMode == TimeWarp.Modes.HIGH && TimeWarp.CurrentRateIndex > 0) + autopilotTargetHold = holdValid; + } + + // if we don't have over-thresh angular velocity and we do + // have a target, orient to the target + if (angularVelocity == Vector3.zero && autopilotTargetHold && holdValid) + { + RotateTowardTarget(pos); + StoreRot(); + } + else + { + RotatePacked(pos); + // We don't store rot here, it relies on lastUT + // and original orientation + } + } + // Vessel is entering physics simulation, either by being loaded or getting out of timewarp // Don't restore rotation/angular velocity here because the vessel/scene isn't fully loaded // Mark it to be done in a latter FixedUpdate, where we can check for FlightGlobals.ready @@ -291,7 +328,7 @@ public override void OnUnloadVessel() private void ApplyAngularVelocity() { - if (IgnoreRot) + if (IgnoreRot(vessel)) { return; } @@ -312,9 +349,9 @@ private void ApplyAngularVelocity() } } - private void RotateTowardTarget() + private void RotateTowardTarget(Vector3d pos) { - if (IgnoreRot) + if (IgnoreRot(vessel)) { return; } @@ -323,12 +360,12 @@ private void RotateTowardTarget() Vector3 up = vessel.transform.TransformDirection(referenceUpLocal); float dot = Vector3.Dot(tgt, up); if (!vessel.loaded || dot < 0.99999f) - vessel.SetRotation(FromToRotation(up, tgt) * vessel.transform.rotation, true); // SetPos=false seems to break the game on some occasions... + SetPosRot(FromToRotation(up, tgt) * vessel.transform.rotation, pos); } - private void RotatePacked() + private void RotatePacked(Vector3d pos) { - if (IgnoreRot) + if (IgnoreRot(vessel)) { return; } @@ -337,7 +374,7 @@ private void RotatePacked() // If we don't have angular velocity, just update our rotation if (angularVelocity == Vector3.zero) { - vessel.SetRotation(unityRot, true); + SetPosRot(unityRot, pos); return; } @@ -347,7 +384,7 @@ private void RotatePacked() double fullRotations = Math.Floor(rotAngle * (1d / 360d)); rotAngle -= fullRotations * 360d; - vessel.SetRotation(Quaternion.AngleAxis((float)rotAngle, ToUnity(angularVelocity)) * UnityRot(), true); // false seems to fix the "infinite roll bug" + SetPosRot(Quaternion.AngleAxis((float)rotAngle, ToUnity(angularVelocity)) * UnityRot(), pos); } private bool RestoreSASMode(int mode)