Beispiel #1
0
        /// <summary>Gets the input induced camera offset, based on mouse position or game pad state.</summary>
        /// <returns>The offset based on player input.</returns>
        private Vector2 GetInputInducedOffset()
        {
            Vector2 offset;

            offset.X = 0;
            offset.Y = 0;

            // Get viewport, for mouse position scaling and offset scaling.
            var viewport    = _graphics.Viewport;
            var offsetScale = new Vector2(viewport.Width, viewport.Height).Length() / 6f;

            var inputManager = (InputManager)_services.GetService(typeof(InputManager));
            var mouse        = inputManager.GetMouse();

            // If we have a game pad attached, get the stick tilt.
            if (Settings.Instance.EnableGamepad)
            {
                foreach (var gamepad in inputManager.GamePads)
                {
                    if (gamepad.IsAttached)
                    {
                        offset = GamePadHelper.GetLook(gamepad);

                        // Only use the first gamepad we can find.
                        break;
                    }
                }
            }
            else if (mouse != null)
            {
                // Otherwise use the mouse.
                var state = mouse.GetState();

                // Get the relative position of the mouse to the ship and
                // apply some factoring to it (so that the maximum distance
                // of cursor to ship is not half the screen size).
                if (state.X >= 0 && state.X < viewport.Width)
                {
                    offset.X = ((state.X / (float)viewport.Width) - 0.5f) * 2;
                }
                if (state.Y >= 0 && state.Y < viewport.Height)
                {
                    offset.Y = ((state.Y / (float)viewport.Height) - 0.5f) * 2;
                }
            }

            // Normalize the vector. This way we get some 'dead area' when controlling with the mouse
            // in the corners of the screen (where the offset length won't change), but we get the same
            // effect as we'd get it with the game pad, keeping it fair in how far players can look.
            if (offset.LengthSquared() > 1)
            {
                offset.Normalize();
            }
            return(XnaUnitConversion.ToSimulationUnits(offset * offsetScale));
        }
Beispiel #2
0
        // TODO in case we need this somewhere else it might be a good idea to move this to the EntityFactory
        private void CreateAsteroid(FarPosition position, IUniformRandom random, ContentManager content)
        {
            // Randomly scale and rotate it.
            var scale = (float)random.NextDouble(0.5f, 1f);
            var angle = (float)random.NextDouble() * MathHelper.TwoPi;

            // Determine shape for physics system.
            var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14);
            var texture     = content.Load <Texture2D>(textureName);
            var hull        = new List <Vector2>(TextureConverter.DetectVertices(texture, 8f, textureName: textureName)[0]);

            for (var k = 0; k < hull.Count; ++k)
            {
                hull[k] -= new Vector2(texture.Width / 2f, texture.Height / 2f);
                hull[k]  = XnaUnitConversion.ToSimulationUnits(hull[k]) * scale;
            }
            var polygons = EarClipDecomposer.ConvexPartition(hull);

            // Create physical representation.
            var entity = Manager.AddEntity();
            var body   = Manager.AddBody(entity, position, angle, Body.BodyType.Dynamic);

            foreach (var polygon in polygons)
            {
                Manager.AttachPolygon(body, polygon, density: 1000f, restitution: 0.2f);
            }
            // Slow down to allow reaching sleep state again.
            body.LinearDamping  = 0.05f * Space.Util.Settings.TicksPerSecond;
            body.AngularDamping = 0.025f * Space.Util.Settings.TicksPerSecond;

            // Bounds of the asteroid for rendering culling. We use the diagonal for a loose fit that
            // contains every possible rotated state of the texture.
            var width    = UnitConversion.ToSimulationUnits(texture.Width);
            var height   = UnitConversion.ToSimulationUnits(texture.Height);
            var diagonal = (float)Math.Sqrt(width * width + height * height);
            var bounds   = new FarRectangle(-diagonal / 2, -diagonal / 2, diagonal, diagonal);

            // Rendering stuff.
            Manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId);
            Manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId);
            Manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(textureName, scale);

            // Auto removal.
            Manager.AddComponent <CellDeath>(entity).Initialize(true);
            Manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId);

            // Make it destructible.
            var health = Manager.AddComponent <Health>(entity);

            health.Value        = health.MaxValue = 200 * scale;
            health.Regeneration = 0f;

            // As they don't move on their own, start asteroids as sleeping to save performance.
            body.IsAwake = false;
        }
        /// <summary>Converts a coordinate in screen space to simulation space.</summary>
        /// <param name="point">The point in screen space.</param>
        /// <returns>The projected point.</returns>
        public Vector2 SimulationToScreen(WorldPoint point)
        {
            var viewport   = _graphicsDevice.Viewport;
            var projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, 0, viewport.Height, 0, 1);
            var view       = ComputeViewTransform();

#if FARMATH
            var projected = _graphicsDevice.Viewport.Project(new Vector3(XnaUnitConversion.ToScreenUnits((Vector2)(point + view.Translation)), 0), projection, view.Matrix, Matrix.Identity);
#else
            var projected = _graphicsDevice.Viewport.Project(
                new Vector3(XnaUnitConversion.ToScreenUnits(point), 0), projection, view, Matrix.Identity);
#endif
            return(new Vector2(projected.X, projected.Y));
        }
        /// <summary>Converts a coordinate in simulation space to screen space.</summary>
        /// <param name="point">The point in simulation space.</param>
        /// <returns>The unprojected point.</returns>
        public WorldPoint ScreenToSimulation(Vector2 point)
        {
            var viewport   = _graphicsDevice.Viewport;
            var projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, 0, viewport.Height, 0, 1);
            var view       = ComputeViewTransform();

#if FARMATH
            var unprojected = _graphicsDevice.Viewport.Unproject(new Vector3(point, 0), projection, view.Matrix, Matrix.Identity);
            return(XnaUnitConversion.ToSimulationUnits(new Vector2(unprojected.X, unprojected.Y)) - view.Translation);
#else
            var unprojected = _graphicsDevice.Viewport.Unproject(
                new Vector3(point, 0), projection, view, Matrix.Identity);
            return(XnaUnitConversion.ToSimulationUnits(new Vector2(unprojected.X, unprojected.Y)));
#endif
        }
Beispiel #5
0
 private void DrawArrow(Vector2 start, Vector2 toEnd, Color color)
 {
     start = XnaUnitConversion.ToScreenUnits(start);
     toEnd = XnaUnitConversion.ToScreenUnits(toEnd);
     // Don't draw tiny arrows...
     if (toEnd.LengthSquared() < 1f)
     {
         return;
     }
     _spriteBatch.Draw(
         _arrow,
         start,
         null,
         color,
         (float)Math.Atan2(toEnd.Y, toEnd.X),
         new Vector2(0, _arrow.Height / 2f),
         new Vector2(toEnd.Length() / _arrow.Width, 1),
         SpriteEffects.None,
         0);
 }
        private void DrawContact(Contact contact, Func <WorldPoint, Vector2> toScreen)
        {
            Vector2            normal;
            IList <WorldPoint> points;

            contact.ComputeWorldManifold(out normal, out points);

            for (var i = 0; i < points.Count; ++i)
            {
                var point = toScreen(points[i]);

                if (RenderContactPoints)
                {
                    _primitiveBatch.DrawFilledRectangle(
                        point, UnitConversion.ToScreenUnits(0.1f), UnitConversion.ToScreenUnits(0.1f), ContactColor);
                }

                if (RenderContactNormals)
                {
                    // We want to use the normal (untransformed in FarValue case) method
                    // for mapping to screen space when computing our axis length.
                    _primitiveBatch.DrawLine(
                        point, point + XnaUnitConversion.ToScreenUnits(normal * NormalScale), ContactNormalColor);
                }

                if (RenderContactPointNormalImpulse)
                {
                    // We want to use the normal (untransformed in FarValue case) method
                    // for mapping to screen space when computing our axis length.
                    _primitiveBatch.DrawLine(
                        point,
                        point + XnaUnitConversion.ToScreenUnits(normal * contact.GetNormalImpulse(i) * ImpulseScale),
                        ContactNormalImpulseColor);
                }
            }
        }
Beispiel #7
0
        private CacheEntry GetCacheEntry(int entity)
        {
            var equipment = (SpaceItemSlot)Manager.GetComponent(entity, ItemSlot.TypeId);
            var hash      = HashEquipment(equipment);

            // Create cache texture if necessary.
            if (!ModelCache.ContainsKey(hash))
            {
                // Simulations may run multi-threaded, so we need to lock out static table here.
                lock (ModelCache)
                {
                    // Maybe we got our entry while waiting for our lock?
                    if (!ModelCache.ContainsKey(hash))
                    {
                        // No cache entry yet, create it. Determine the needed size of the texture.
                        var size = ComputeModelSize(equipment, Vector2.Zero);
                        // Then create it and push it as our current render target.
                        var target = new RenderTarget2D(
                            _spriteBatch.GraphicsDevice,
                            (int)System.Math.Ceiling(size.Width),
                            (int)System.Math.Ceiling(size.Height));

                        var previousRenderTargets = _spriteBatch.GraphicsDevice.GetRenderTargets();
                        _spriteBatch.GraphicsDevice.SetRenderTarget(target);
                        _spriteBatch.GraphicsDevice.Clear(Color.Transparent);
                        _spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);

                        // Render the actual equipment into our texture.
                        RenderEquipment(equipment, new Vector2(-size.X, -size.Y));

                        // Restore the old render state.
                        _spriteBatch.End();
                        _spriteBatch.GraphicsDevice.SetRenderTargets(previousRenderTargets);

                        // Build polygon hull.
                        var abstraction = Manager.GetComponent(entity, Avatar.TypeId) == null
                                              ? NPCModelTolerance
                                              : PlayerModelTolerance;
                        var hull = new List <Vector2>(TextureConverter.DetectVertices(target, abstraction)[0]);
                        for (var i = 0; i < hull.Count; ++i)
                        {
                            // Center at origin.
                            hull[i] += new Vector2(size.X, size.Y);
                            // Scale to simulation units.
                            hull[i] = XnaUnitConversion.ToSimulationUnits(hull[i]);
                        }

                        // Create a new cache entry for this equipment combination.
                        ModelCache[hash] = new CacheEntry
                        {
                            Texture      = target,
                            Offset       = new Vector2(-size.X, -size.Y),
                            PolygonHulls = EarClipDecomposer.ConvexPartition(hull)
                        };
                    }
                }
            }

            // Update last access time.
            ModelCache[hash].Age = 0;

            // Then return the entry!
            return(ModelCache[hash]);
        }
Beispiel #8
0
        /// <summary>Updates the component.</summary>
        /// <param name="frame">The frame.</param>
        /// <param name="component">The component.</param>
        protected override void UpdateComponent(long frame, WeaponControl component)
        {
            // Nothing to do if we're not shooting.
            if (!component.Shooting)
            {
                return;
            }

            // Get components.
            var attributes = (Attributes <AttributeType>)Manager.GetComponent(component.Entity, Attributes <AttributeType> .TypeId);
            var equipment  = (ItemSlot)Manager.GetComponent(component.Entity, ItemSlot.TypeId);
            var energy     = (Energy)Manager.GetComponent(component.Entity, Energy.TypeId);
            var faction    = (Faction)Manager.GetComponent(component.Entity, Faction.TypeId);

            // Check all equipped weapon.
            foreach (var slot in equipment.AllSlots)
            {
                // Skip empty slots.
                if (slot.Item <= 0)
                {
                    continue;
                }

                // Skip if it's not a weapon.
                var weapon = (Weapon)Manager.GetComponent(slot.Item, Weapon.TypeId);
                if (weapon == null)
                {
                    continue;
                }

                // Test if this weapon is on cooldown.
                if (_cooldowns[weapon.Entity] > 0)
                {
                    continue;
                }

                // Get the energy consumption, skip if we don't have enough.
                var energyConsumption = 0f;
                if (weapon.Attributes.ContainsKey(AttributeType.WeaponEnergyConsumption))
                {
                    energyConsumption = attributes.GetValue(
                        AttributeType.WeaponEnergyConsumption,
                        weapon.Attributes[AttributeType.WeaponEnergyConsumption]);
                }
                if (energy.Value < energyConsumption)
                {
                    continue;
                }

                // Set cooldown.
                var cooldown = 0f;
                if (weapon.Attributes.ContainsKey(AttributeType.WeaponCooldown))
                {
                    cooldown = attributes.GetValue(
                        AttributeType.WeaponCooldown,
                        weapon.Attributes[AttributeType.WeaponCooldown]);
                }
                _cooldowns[weapon.Entity] = (int)(cooldown * Settings.TicksPerSecond);

                // Consume our energy.
                energy.SetValue(energy.Value - energyConsumption);

                // Compute spawn offset.
                var offset   = Vector2.Zero;
                var rotation = 0f;
                ((SpaceItemSlot)slot).Accumulate(ref offset, ref rotation);

                // Generate projectiles.
                foreach (var projectile in weapon.Projectiles)
                {
                    _projectilesToCreate.Add(
                        new PendingProjectile
                    {
                        Factory  = projectile,
                        Entity   = component.Entity,
                        Offset   = XnaUnitConversion.ToSimulationUnits(offset),
                        Rotation = rotation,
                        Weapon   = weapon,
                        Faction  = faction.Value
                    });
                }

                // Play sound.
                var soundSystem = (SoundSystem)Manager.GetSystem(SoundSystem.TypeId);
                if (soundSystem != null)
                {
                    soundSystem.Play(weapon.Sound, component.Entity);
                }
            }
        }
        private void DrawBody(Body body, Func <Vector2, Vector2> toScreen)
        {
            // Draw the fixtures attached to this body.
            if (RenderFixtures)
            {
                // Get color to draw primitives in based on body state.
                Color color;
                if (!body.Enabled)
                {
                    color = DisabledShapeColor;
                }
                else if (body.Type == Body.BodyType.Static)
                {
                    color = StaticShapeColor;
                }
                else if (body.Type == Body.BodyType.Kinematic)
                {
                    color = KinematicShapeColor;
                }
                else if (!body.IsAwake)
                {
                    color = SleepingShapeColor;
                }
                else
                {
                    color = DefaultShapeColor;
                }

                // Get all fixtures attached to this body.
                foreach (Fixture fixture in body.Fixtures)
                {
                    switch (fixture.Type)
                    {
                    case Fixture.FixtureType.Circle:
                    {
                        var circle = fixture as CircleFixture;
                        System.Diagnostics.Debug.Assert(circle != null);
                        _primitiveBatch.DrawSolidCircle(
                            toScreen(circle.Center),
                            UnitConversion.ToScreenUnits(circle.Radius),
                            color);
                    }
                    break;

                    case Fixture.FixtureType.Edge:
                    {
                        var edge = fixture as EdgeFixture;
                        System.Diagnostics.Debug.Assert(edge != null);
                        _primitiveBatch.DrawLine(
                            toScreen(edge.Vertex1),
                            toScreen(edge.Vertex2),
                            color);
                    }
                    break;

                    case Fixture.FixtureType.Polygon:
                    {
                        var polygon = fixture as PolygonFixture;
                        System.Diagnostics.Debug.Assert(polygon != null);
                        _primitiveBatch.DrawFilledPolygon(
                            polygon.Vertices
                            .Take(polygon.Count)
                            .Select(toScreen).ToList(),
                            color);
                    }
                    break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }

            // Draw the transform at the center of mass.
            if (RenderCenterOfMass)
            {
                var p1 = toScreen(body.Sweep.LocalCenter);
                // We want to use the normal (untransformed in FarValue case) method
                // for mapping to screen space when computing our axis length.
                _primitiveBatch.DrawLine(p1, p1 + XnaUnitConversion.ToScreenUnits(Vector2.UnitX * AxisScale), Color.Red);
                _primitiveBatch.DrawLine(p1, p1 + XnaUnitConversion.ToScreenUnits(Vector2.UnitY * AxisScale), Color.Blue);
            }
        }
        public void OnDraw(Draw message)
        {
            // Skip if we have nothing to draw.
            if (!Enabled ||
                !(RenderFixtureBounds || RenderFixtures || RenderCenterOfMass || RenderContactPoints ||
                  RenderContactNormals || RenderContactPointNormalImpulse))
            {
                return;
            }

            // Get physics system from which to get bodies and contacts.
            var physics = Manager.GetSystem(PhysicsSystem.TypeId) as PhysicsSystem;

            if (physics == null)
            {
                return;
            }

            // Compute view matrix.
            var view = ComputeViewTransform();

            // Render fixture bounds.
            if (RenderFixtureBounds)
            {
#if FARMATH
                _primitiveBatch.Begin(view.Matrix);
#else
                _primitiveBatch.Begin(view);
#endif
                foreach (var aabb in physics.FixtureBounds)
                {
#if FARMATH
                    var center = (Vector2)(aabb.Center + view.Translation);
#else
                    var center = aabb.Center;
#endif
                    _primitiveBatch.DrawRectangle(
                        XnaUnitConversion.ToScreenUnits(center),
                        UnitConversion.ToScreenUnits(aabb.Width),
                        UnitConversion.ToScreenUnits(aabb.Height),
                        FixtureBoundsColor);
                }
                _primitiveBatch.End();
            }

            // Render fixtures.
            if (RenderFixtures || RenderCenterOfMass)
            {
                foreach (var bodyInfo in GetVisibleBodies())
                {
                    // Get model transform based on body transform.
                    var body         = bodyInfo.Item1;
                    var bodyPosition = bodyInfo.Item2;
#if FARMATH
                    var bodyAngle       = bodyInfo.Item3;
                    var bodyTranslation = (Vector2)(bodyPosition + view.Translation);
                    var model           = Matrix.CreateRotationZ(bodyAngle) *
                                          Matrix.CreateTranslation(UnitConversion.ToScreenUnits(bodyTranslation.X),
                                                                   UnitConversion.ToScreenUnits(bodyTranslation.Y), 0);
                    _primitiveBatch.Begin(model * view.Matrix);
                    DrawBody(body, XnaUnitConversion.ToScreenUnits);
#else
                    var model = Matrix.CreateRotationZ(body.Sweep.Angle) *
                                Matrix.CreateTranslation(
                        UnitConversion.ToScreenUnits(bodyPosition.X),
                        UnitConversion.ToScreenUnits(bodyPosition.Y),
                        0);
                    _primitiveBatch.Begin(model * view);

                    DrawBody(body, XnaUnitConversion.ToScreenUnits);
#endif

                    _primitiveBatch.End();
                }
            }

            // Render contacts.
            if (RenderContactPoints || RenderContactNormals || RenderContactPointNormalImpulse)
            {
#if FARMATH
                _primitiveBatch.Begin(view.Matrix);
#else
                _primitiveBatch.Begin(view);
#endif
                foreach (var contact in GetVisibleContacts())
                {
#if FARMATH
                    DrawContact(contact, v => XnaUnitConversion.ToScreenUnits((Vector2)(v + view.Translation)));
#else
                    DrawContact(contact, XnaUnitConversion.ToScreenUnits);
#endif
                }
                _primitiveBatch.End();
            }

            // Render joints.
            if (RenderJoints)
            {
#if FARMATH
                _primitiveBatch.Begin(view.Matrix);
#else
                _primitiveBatch.Begin(view);
#endif

                foreach (var joint in GetVisibleJoints())
                {
#if FARMATH
                    DrawJoint(joint, v => XnaUnitConversion.ToScreenUnits((Vector2)(v + view.Translation)));
#else
                    DrawJoint(joint, XnaUnitConversion.ToScreenUnits);
#endif
                }

                _primitiveBatch.End();
            }
        }
Beispiel #11
0
        public void OnDraw(Draw message)
        {
            if (!Enabled)
            {
                return;
            }

            // Get local player's avatar.
            var avatar = ((LocalPlayerSystem)Manager.GetSystem(LocalPlayerSystem.TypeId)).LocalPlayerAvatar;

            if (avatar <= 0)
            {
                return;
            }

            // Get info on the local player's ship.
            var info = (ShipInfo)Manager.GetComponent(avatar, ShipInfo.TypeId);

            // Get the index we use for looking up nearby objects.
            var index = (IndexSystem)Manager.GetSystem(IndexSystem.TypeId);

            // Get camera information.
            var camera = (CameraSystem)Manager.GetSystem(CameraSystem.TypeId);

            // Get camera position.
            var position = camera.CameraPosition;

            // Get zoom from camera.
            var zoom = camera.Zoom;

            // Scale ellipse based on camera zoom.
            _filledEllipse.Scale = zoom;
            _ellipse.Scale       = zoom;

            // Figure out the overall range of our radar system.
            var radarRange = UnitConversion.ToSimulationUnits(info.RadarRange);

            // Our mass.
            var mass = info.Mass;

            // Get our viewport.
            var viewport = _spriteBatch.GraphicsDevice.Viewport;

            // Get the screen's center, used for diverse computations, and as
            // a center for relative computations (because the player's always
            // rendered in the center of the screen).
            Vector2 center;

            center.X = viewport.Width / 2f;
            center.Y = viewport.Height / 2f;

            // Precomputed for the loop.
            var radarRangeSquared = radarRange * radarRange;

            // Get the radius of the minimal bounding sphere of our viewport.
            var radius = UnitConversion.ToSimulationUnits((float)Math.Sqrt(center.X * center.X + center.Y * center.Y));

            // Increase radius accordingly, to include stuff possibly further away.
            radius /= zoom;

            // Loop through all our neighbors.
            index[Detectable.IndexId].Find(position, radarRange, _reusableNeighborList);

            // Begin drawing.
            _spriteBatch.Begin();
            foreach (IIndexable neighbor in _reusableNeighborList.Select(Manager.GetComponentById))
            {
                // Get the components we need.
                var neighborTransform  = Manager.GetComponent(neighbor.Entity, TransformTypeId) as ITransform;
                var neighborDetectable = Manager.GetComponent(neighbor.Entity, Detectable.TypeId) as Detectable;

                // Bail if we're missing something.
                if (neighborTransform == null || neighborDetectable == null || neighborDetectable.Texture == null)
                {
                    continue;
                }

                // We don't show the icons for anything that's inside our
                // viewport. Get the position of the detectable inside our
                // viewport. This will also serve as our direction vector.
                var direction = (Vector2)(neighborTransform.Position - position);

                // We'll make stuff far away a little less opaque. First get
                // the linear relative distance.
                var ld = direction.LengthSquared() / radarRangeSquared;
                // Then apply a exponential fall-off, and make it cap a little
                // early to get the 100% alpha when nearby, not only when
                // exactly on top of the object ;)
                ld = Math.Min(1, (1.1f - ld * ld * ld) * 1.1f);

                // If it's an astronomical object, check if its orbit is
                // potentially in our screen space, if so draw it.
                var ellipse = ((EllipsePath)Manager.GetComponent(neighbor.Entity, EllipsePath.TypeId));
                if (ellipse != null)
                {
                    // The entity we're orbiting around is at one of the two
                    // foci of the ellipse. We want the center, though.

                    // Get the current position of the entity we're orbiting.
                    var focusTransform = ((ITransform)Manager.GetComponent(ellipse.CenterEntityId, TransformTypeId)).Position;

                    // Compute the distance of the ellipse's foci to the center
                    // of the ellipse.
                    var     ellipseFocusDistance = (float)Math.Sqrt(ellipse.MajorRadius * ellipse.MajorRadius - ellipse.MinorRadius * ellipse.MinorRadius);
                    Vector2 ellipseCenter;
                    ellipseCenter.X = ellipseFocusDistance;
                    ellipseCenter.Y = 0;
                    var rotation = Matrix.CreateRotationZ(ellipse.Angle);
                    Vector2.Transform(ref ellipseCenter, ref rotation, out ellipseCenter);
                    focusTransform += ellipseCenter;

                    // Get relative vector from position to ellipse center.
                    var toCenter = (Vector2)(focusTransform - position);

                    // Far clipping, i.e. don't render if we're outside and
                    // not seeing the ellipse.
                    var distanceToCenterSquared = toCenter.LengthSquared();
                    var farClipDistance         = ellipse.MajorRadius + radius;
                    farClipDistance *= farClipDistance;

                    // Near clipping, i.e. don't render if we're inside the
                    // ellipse, but not seeing its border.
                    var nearClipDistance = Math.Max(0, ellipse.MinorRadius - radius);
                    nearClipDistance *= nearClipDistance;

                    // Check if we're cutting (potentially seeing) the orbit
                    // ellipse of the neighbor.
                    if (farClipDistance > distanceToCenterSquared &&
                        nearClipDistance <= distanceToCenterSquared)
                    {
                        // Yes, set the properties for our ellipse renderer.
                        _ellipse.Center      = XnaUnitConversion.ToScreenUnits(toCenter) + center;
                        _ellipse.MajorRadius = UnitConversion.ToScreenUnits(ellipse.MajorRadius);
                        _ellipse.MinorRadius = UnitConversion.ToScreenUnits(ellipse.MinorRadius);
                        _ellipse.Rotation    = ellipse.Angle;

                        // Diameter the opacity based on our distance to the
                        // actual object. Apply a exponential fall-off, and
                        // make it cap a little early to get the 100% alpha
                        // when nearby, not only when exactly on top of the
                        // object ;)
                        _ellipse.Color = OrbitColor * ld;
                        // And draw it!
                        _ellipse.Draw();
                    }
                }

                // If the neighbor does collision damage and is an attractor,
                // show the "dead zone" (i.e. the area beyond the point of no
                // return).
                var neighborGravitation     = Manager.GetComponent(neighbor.Entity, Gravitation.TypeId) as Gravitation;
                var neighborCollisionDamage = Manager.GetComponent(neighbor.Entity, CollisionDamage.TypeId) as CollisionDamage;
                if (neighborCollisionDamage == null || neighborGravitation == null ||
                    (neighborGravitation.GravitationType & Gravitation.GravitationTypes.Attractor) == 0)
                {
                    continue;
                }

                // The point of no return is the distance at which the
                // gravitation is stronger than our maximum thruster
                // output.
                var maxAcceleration = info.MaxAcceleration;
                var masses          = mass * neighborGravitation.Mass / Settings.TicksPerSecond;
                var dangerPoint     = (float)Math.Sqrt(masses / (maxAcceleration * 0.5f)) + DeadZoneDiffuseWidth;
                _filledEllipse.Center   = XnaUnitConversion.ToScreenUnits(direction) + center;
                _filledEllipse.Gradient = UnitConversion.ToScreenUnits(DeadZoneDiffuseWidth);
                _ellipse.Center         = _filledEllipse.Center;
                _ellipse.Rotation       = 0;
                var distToCenter = direction.Length();
                // Check if we're potentially seeing the marker.
                if (radius >= distToCenter - dangerPoint)
                {
                    var dangerRadius = UnitConversion.ToScreenUnits(dangerPoint);
                    _filledEllipse.Radius = dangerRadius;
                    _filledEllipse.Draw();

                    // Make the lines pulsate a bit.
                    var phase = (0.6f + (float)(Math.Sin(MathHelper.ToRadians(message.Frame * 6)) + 1) * 0.125f);

                    _ellipse.Radius = dangerRadius - UnitConversion.ToScreenUnits(DeadZoneDiffuseWidth) * 0.5f;
                    _ellipse.Color  = Color.Red * phase * 0.7f;
                    _ellipse.Draw();

                    var pointOfNoReturn = (float)Math.Sqrt(masses / maxAcceleration) + DeadZoneDiffuseWidth;
                    if (radius >= distToCenter - pointOfNoReturn)
                    {
                        var deadRadius = UnitConversion.ToScreenUnits(pointOfNoReturn);
                        _filledEllipse.Radius = deadRadius;
                        _filledEllipse.Draw();

                        _ellipse.Radius = deadRadius - UnitConversion.ToScreenUnits(DeadZoneDiffuseWidth) * 0.5f;
                        _ellipse.Color  = Color.Red * phase;
                        _ellipse.Draw();
                    }
                }
            }
            // Done drawing.
            _spriteBatch.End();

            // Clear the list for the next run.
            _reusableNeighborList.Clear();
        }