Skip to content

Commit

Permalink
Use a Harmony patch of OrbitDriver to set vessel rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanKell committed Nov 5, 2023
1 parent 359eabf commit 3e1e7db
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 61 deletions.
65 changes: 65 additions & 0 deletions Source/Harmony/OrbitDriver.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
1 change: 1 addition & 0 deletions Source/RealismOverhaul.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Compile Include="Harmony\ModuleEngines.cs" />
<Compile Include="Harmony\KSPUtil.cs" />
<Compile Include="Harmony\PartLoader.cs" />
<Compile Include="Harmony\OrbitDriver.cs" />
<Compile Include="Harmony\UIPartActionResourceEditor.cs" />
<Compile Include="Harmony\UIPartActionResource.cs" />
<Compile Include="Harmony\ResourceItem.cs" />
Expand Down
159 changes: 98 additions & 61 deletions Source/VesselModuleRotationRO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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)
Expand All @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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
Expand Down Expand Up @@ -291,7 +328,7 @@ public override void OnUnloadVessel()

private void ApplyAngularVelocity()
{
if (IgnoreRot)
if (IgnoreRot(vessel))
{
return;
}
Expand All @@ -312,9 +349,9 @@ private void ApplyAngularVelocity()
}
}

private void RotateTowardTarget()
private void RotateTowardTarget(Vector3d pos)
{
if (IgnoreRot)
if (IgnoreRot(vessel))
{
return;
}
Expand All @@ -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;
}
Expand All @@ -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;
}

Expand All @@ -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)
Expand Down

0 comments on commit 3e1e7db

Please sign in to comment.