using NewHorizons.External; using OWML.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using Logger = NewHorizons.Utility.Logger; using System.Reflection; using NewHorizons.Utility; using NewHorizons.Components.Orbital; namespace NewHorizons.Builder.Orbital { static class InitialMotionBuilder { public static InitialMotion Make(GameObject body, AstroObject primaryBody, AstroObject secondaryBody, OWRigidbody OWRB, OrbitModule orbit) { // Doing it like this so the planet orbit updater can just use an existing initial motion with the other method InitialMotion initialMotion = body.AddComponent(); return SetInitialMotionFromConfig(initialMotion, primaryBody, secondaryBody, orbit); } public static InitialMotion SetInitialMotionFromConfig(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody, OrbitModule orbit) { // This bit makes the initial motion not try to calculate the orbit velocity itself for reasons initialMotion._orbitImpulseScalar = 0f; // Rotation initialMotion._initAngularSpeed = orbit.SiderealPeriod == 0 ? 0f : 2f * Mathf.PI / (orbit.SiderealPeriod * 60f); var rotationAxis = Quaternion.AngleAxis(orbit.AxialTilt, Vector3.right) * Vector3.up; secondaryBody.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); if (!orbit.IsStatic && primaryBody != null) { SetInitialMotion(initialMotion, primaryBody, secondaryBody); } else { initialMotion._initLinearDirection = Vector3.forward; initialMotion._initLinearSpeed = 0f; } return initialMotion; } public static void SetInitialMotion(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody) { var focalPoint = primaryBody.GetComponent(); if (focalPoint) { // Focal stuff var name = secondaryBody.GetCustomName(); if (name == focalPoint.PrimaryName || name == focalPoint.SecondaryName) { // The one we're currently looking at is always null var otherBody = focalPoint.Primary ?? focalPoint.Secondary; if (otherBody != null) { // We set the positions and velocities of both right now if (name == focalPoint.PrimaryName) { SetBinaryInitialMotion(primaryBody, secondaryBody as NHAstroObject, otherBody as NHAstroObject); } else { SetBinaryInitialMotion(primaryBody, otherBody as NHAstroObject, secondaryBody as NHAstroObject); } } } else { // It's a circumbinary moon/planet var fakePrimaryBody = focalPoint.FakeMassBody.GetComponent(); SetMotionFromPrimary(fakePrimaryBody, secondaryBody as NHAstroObject, initialMotion); } } else if (primaryBody.GetGravityVolume()) { SetMotionFromPrimary(primaryBody, secondaryBody as NHAstroObject, initialMotion); } else { Logger.Log($"No primary gravity or focal point for {primaryBody}"); } } private static void SetMotionFromPrimary(AstroObject primaryBody, NHAstroObject secondaryBody, InitialMotion initialMotion) { initialMotion.SetPrimaryBody(primaryBody.GetAttachedOWRigidbody()); var primaryGravity = new Gravity(primaryBody.GetGravityVolume()); var secondaryGravity = new Gravity(secondaryBody.GetGravityVolume()); var velocity = secondaryBody.GetOrbitalParameters(primaryGravity, secondaryGravity).InitialVelocity; var parentVelocity = primaryBody?.GetComponent()?.GetInitVelocity() ?? Vector3.zero; initialMotion._cachedInitVelocity = velocity + parentVelocity; initialMotion._isInitVelocityDirty = false; } private static void SetBinaryInitialMotion(AstroObject baryCenter, NHAstroObject primaryBody, NHAstroObject secondaryBody) { Logger.Log($"Setting binary initial motion [{primaryBody.name}] [{secondaryBody.name}]"); var primaryGravity = new Gravity(primaryBody._gravityVolume); var secondaryGravity = new Gravity(secondaryBody._gravityVolume); // Might make binaries with binaries with binaries work if (primaryBody.GetGravityVolume() == null) { primaryGravity = new Gravity(primaryBody.GetComponent()?.FakeMassBody?.GetComponent()?.GetGravityVolume()); } if (secondaryBody.GetGravityVolume() == null) { secondaryGravity = new Gravity(secondaryBody.GetComponent()?.FakeMassBody?.GetComponent()?.GetGravityVolume()); } // Update the positions var distance = secondaryBody.SemiMajorAxis + primaryBody.SemiMajorAxis; var m1 = primaryGravity.Mass; var m2 = secondaryGravity.Mass; var r1 = distance * m2 / (m1 + m2); var r2 = distance * m1 / (m1 + m2); var ecc = secondaryBody.Eccentricity; var inc = secondaryBody.Inclination; var arg = secondaryBody.ArgumentOfPeriapsis; var lon = secondaryBody.LongitudeOfAscendingNode; var tru = secondaryBody.TrueAnomaly; // Update their astro objects primaryBody.SetOrbitalParametersFromTrueAnomaly(ecc, r1, inc, arg, lon, tru - 180); secondaryBody.SetOrbitalParametersFromTrueAnomaly(ecc, r2, inc, arg, lon, tru); primaryBody.transform.position = baryCenter.transform.position + primaryBody.GetOrbitalParameters(primaryGravity, secondaryGravity).InitialPosition; secondaryBody.transform.position = baryCenter.transform.position + secondaryBody.GetOrbitalParameters(secondaryGravity, primaryGravity).InitialPosition; // Update the velocities var reducedMass = 1f / ((1f / m1) + (1f / m2)); var reducedMassGravity = new Gravity(reducedMass, primaryGravity.Power); var baryCenterVelocity = baryCenter?.GetComponent()?.GetInitVelocity() ?? Vector3.zero; // Doing the two body problem as a single one body problem gives the relative velocity of secondary to primary var reducedOrbit = OrbitalParameters.FromTrueAnomaly( reducedMassGravity, secondaryGravity, ecc, distance, inc, arg, lon, tru ); // We know their velocities sum up to the total relative velocity and are related to the masses / distances // Idk where the times 2 comes from but it makes it work? Idk theres 2 planets? Makes no sense var relativeVelocity = 2f * reducedOrbit.InitialVelocity; var secondaryVelocity = relativeVelocity / (1f + (r1 / r2)); var primaryVelocity = relativeVelocity - secondaryVelocity; // Now we just set things var primaryInitialMotion = primaryBody.GetComponent(); primaryInitialMotion._cachedInitVelocity = baryCenterVelocity + primaryVelocity; primaryInitialMotion._isInitVelocityDirty = false; var secondaryInitialMotion = secondaryBody.GetComponent(); secondaryInitialMotion._cachedInitVelocity = baryCenterVelocity - secondaryVelocity; secondaryInitialMotion._isInitVelocityDirty = false; Logger.Log($"Binary Initial Motion: {m1}, {m2}, {r1}, {r2}, {primaryVelocity}, {secondaryVelocity}"); } } }