float3 FutureLocalPosition(Orbiter o, float t)
    {
        // TODO: Mass stubbed out here... should come from the body?
        const float MASS           = 1;
        float       period         = Orbiter.Period(o, MASS, G);
        float       radians        = (o.Radians + (float)o.Direction * TWO_PI / period * t) % (TWO_PI);
        float3      futurePosition = o.Radius * UnitCircle(radians);

        return(futurePosition);
    }
    public void FixedUpdate()
    {
        float dt = Time.fixedDeltaTime;

        foreach (var emitter in Emitters)
        {
            emitter.TimeTillEmission -= dt;

            if (emitter.TimeTillEmission <= 0)
            {
                GameObject emittee = Instantiate(emitter.Emittee, emitter.transform.position, emitter.transform.rotation);

                Register(emittee);
                if (emittee.TryGetComponent <Velocity>(out Velocity v))
                {
                    v.Value = emitter.transform.forward * emitter.EmissionSpeed;
                }
                // TODO: note... this is not totally correct as we could slightly overshoot...
                // TODO: consider fixing this AND the initial position based on this slight overshoot to get perfect behavior
                emitter.TimeTillEmission = emitter.EmissionPeriod;
            }
        }

        // Update deathtimers: modifies component arrays
        for (int i = 0; i < DeathTimers.Count; i++)
        {
            DeathTimer deathTimer = DeathTimers[i];

            deathTimer.Value -= dt;
            if (deathTimer.Value <= 0)
            {
                UnRegister(deathTimer.gameObject);
                Destroy(deathTimer.gameObject);
                i--;
            }
        }

        // Update all free bodies
        foreach (var freebody in FreeBodies)
        {
            Velocity v = freebody.GetComponent <Velocity>();

            foreach (var normalizedMass in NormalizedMasses)
            {
                float3 delta     = normalizedMass.transform.position - freebody.transform.position;
                float3 direction = normalize(delta);
                float  d         = length(delta);

                v.Value += direction * dt * normalizedMass.Value * MassFactor * G / (d * d);
            }
        }

        // There are several versions of this system that will offer better and better behavior
        // The stupid chaser with fixed speed
        //      Constantly tries to move towards the CURRENT location of their target
        foreach (var chaser in FindObjectsOfType <Chaser>())
        {
            float3 delta             = chaser.Target.position - chaser.transform.position;
            float3 direction         = normalize(chaser.Target.position - chaser.transform.position);
            float  remainingDistance = min(length(delta), chaser.MaxSpeed * dt);

            chaser.transform.position = chaser.transform.position + remainingDistance * (Vector3)direction;
            Debug.DrawLine(chaser.transform.position, chaser.Target.position, Color.red);
        }

        // The predictive chaser
        //      Predicts the location of the target various times in the future
        //      Chooses the first time where the distance to the object is within its range for that given time based on its max speed
        //      Moves towards that future location at the required speed
        var predictiveChasers = FindObjectsOfType <PredictiveChaser>();

        foreach (var predictiveChaser in predictiveChasers)
        {
            predictiveChaser.TimeElapsed       += dt;
            predictiveChaser.transform.position = PathSystem.PositionFromTrajectoryAtTime(predictiveChaser.Trajectory, predictiveChaser.TimeElapsed);
        }
        foreach (var predictiveChaser in predictiveChasers)
        {
            if (predictiveChaser.TimeElapsed >= predictiveChaser.Trajectory.duration)
            {
                predictiveChaser.transform.SetParent(predictiveChaser.Target, true);
                predictiveChaser.transform.localPosition = Vector3.zero;
                Destroy(predictiveChaser);
            }
        }

        foreach (var traveler in Travelers)
        {
            // every traveler needs to calculate the position of their target
        }

        // Set the required radius of all orbiters that are geosynchronous
        foreach (var geosynchronous in Geosynchronouses)
        {
            if (geosynchronous.TryGetComponent <Orbiter>(out Orbiter orbiter))
            {
                const float MASS = 1;

                orbiter.Radius = Orbiter.RequiredRadiusForPeriod(MASS, G, geosynchronous.Rotator.Period);
            }
        }

        // Update all orbiters
        foreach (var orbiter in Orbiters)
        {
            // TODO: This mass value is stubbed out here but probably should be taken from the body itself?
            const float MASS   = 1;
            float       period = Orbiter.Period(orbiter, MASS, G);

            orbiter.Radians = (orbiter.Radians + (float)orbiter.Direction * TWO_PI / period * dt) % (TWO_PI);
            orbiter.transform.localPosition = orbiter.Radius * UnitCircle(orbiter.Radians);
        }

        // Update all rotators
        foreach (var rotator in Rotators)
        {
            rotator.Radians = (rotator.Radians + (float)rotator.Direction * TWO_PI / rotator.Period * dt) % (TWO_PI);
            rotator.transform.localRotation = Quaternion.AngleAxis(-rotator.Radians * Mathf.Rad2Deg, Vector3.up);
        }

        // Update all target reflectors
        foreach (var targetreflector in TargetReflectors)
        {
            float3 toSource = targetreflector.Source.transform.position - targetreflector.transform.position;
            float3 toTarget = targetreflector.Target.transform.position - targetreflector.transform.position;
            float3 halfway  = normalize((normalize(toSource) + normalize(toTarget)) / 2);

            targetreflector.transform.forward = halfway;
        }

        // Update Skyhook Rockets
        foreach (var skyhookRocket in SkyHookRockets)
        {
            switch (skyhookRocket.CurrentPlan)
            {
            case SkyHookRocket.Plan.EnterOrbit:
                skyhookRocket.TimeRemaining -= dt;

                if (skyhookRocket.TimeRemaining <= 0)
                {
                    Orbiter o = (Orbiter)skyhookRocket.gameObject.AddComponent(typeof(Orbiter));

                    o.Radians   = 0;
                    o.Radius    = skyhookRocket.orbitRadius;
                    o.Direction = Orbiter.RotationalDirection.CounterClockwise;
                    Orbiters.Add(o);
                    skyhookRocket.TimeRemaining = 0;
                    skyhookRocket.transform.SetParent(skyhookRocket.Origin, true);
                    skyhookRocket.CurrentPlan = SkyHookRocket.Plan.RideTheHook;
                }
                else
                {
                    float3 currentPosition = skyhookRocket.transform.position;
                    float3 futurePosition  = FuturePosition(skyhookRocket.Origin, skyhookRocket.TimeRemaining);
                    float  fraction        = skyhookRocket.TimeRemaining / skyhookRocket.TransitionTime;

                    Debug.DrawLine(currentPosition, futurePosition, Color.red);
                    skyhookRocket.TimeRemaining     -= dt;
                    skyhookRocket.transform.position = lerp(futurePosition, currentPosition, fraction);
                }

                break;

            case SkyHookRocket.Plan.RideTheHook:
                // Check if we should release
                if (Input.GetKeyDown(KeyCode.Space))
                {
                    float3   trajectory = FutureVelocity(skyhookRocket.transform, 0);
                    Orbiter  orbiter    = skyhookRocket.GetComponent <Orbiter>();
                    Velocity velocity   = (Velocity)skyhookRocket.gameObject.AddComponent(typeof(Velocity));

                    Orbiters.Remove(orbiter);
                    Velocities.Add(velocity);
                    Destroy(orbiter);
                    velocity.Value = trajectory;
                    skyhookRocket.transform.SetParent(null, true);
                    skyhookRocket.transform.forward = normalize(trajectory);
                    skyhookRocket.CurrentPlan       = SkyHookRocket.Plan.Free;
                }
                break;
            }
        }

        foreach (var velocity in Velocities)
        {
            velocity.transform.position += dt * (Vector3)velocity.Value;
        }

        foreach (var orbitPredictor in OrbitPredictors)
        {
            int count = orbitPredictor.LineRenderer.positionCount;
            for (int i = 0; i < count; i++)
            {
                float  time          = (float)i / (float)(count - 1) * OrbitPredictorTimeInTheFuture;
                float3 velocity      = FutureVelocity(orbitPredictor.transform, 0);
                float3 trajectory    = normalize(velocity);
                float3 toTarget      = new float3(0, 0, 0) - (float3)orbitPredictor.transform.position;
                float3 towardsTarget = normalize(toTarget);
                float  trajectoryDotTowardsTarget = dot(trajectory, towardsTarget);

                orbitPredictor.LineRenderer.SetPosition(i, FuturePosition(orbitPredictor.transform, time));
            }
        }

        // Raytracing for light sources
        RaytracingSystem.LightSources     = LightSources;
        RaytracingSystem.LightSourceCount = RaytracingSystem.LightSources.Length;
        RaytracingSystem.Schedule();

        // Raytrace rendering
        for (int i = 0; i < RaytracingSystem.Traces.Count; i++)
        {
            var trace         = RaytracingSystem.Traces[i];
            var lr            = LineRenderers[i];
            var logEnergy     = pow(trace.Energy, ScalePower);
            var width         = EnergyToWidthCurve.Evaluate(logEnergy);
            var intensity     = EnergyToIntensityCurve.Evaluate(logEnergy);
            var emissionColor = intensity * BeamColor;

            lr.positionCount = 2;
            lr.SetPosition(0, trace.From);
            lr.SetPosition(1, trace.To);
            lr.startWidth = width;
            lr.endWidth   = width;
            lr.material.SetColor("Color", BeamColor);
            lr.material.SetColor("_EmissionColor", emissionColor);
            lr.gameObject.SetActive(true);
        }
        for (int i = RaytracingSystem.Traces.Count; i < LineRenderers.Length; i++)
        {
            LineRenderers[i].gameObject.SetActive(false);
        }

        // Render orbits
        OrbitRenderingSystem.Orbiters = Orbiters;
        OrbitRenderingSystem.Count    = OrbitRenderingSystem.Orbiters.Count;
        OrbitRenderingSystem.Schedule();

        // Update the spacefield
        SpaceField.NormalizedMasses = NormalizedMasses;
        SpaceField.Count            = SpaceField.NormalizedMasses.Length;
        SpaceField.Schedule();
    }