public MyDetectedEntityInfo Raycast(double distance, Vector3D direction)
        {
            if (Vector3D.IsZero(direction))
            {
                throw new ArgumentOutOfRangeException("direction", "Direction cannot be 0,0,0");
            }

            //mods can disable raycast on a block by setting the distance limit to 0 (-1 means infinite)
            if (distance <= 0 || (BlockDefinition.RaycastDistanceLimit > -1 && distance > BlockDefinition.RaycastDistanceLimit))
            {
                return(new MyDetectedEntityInfo());
            }

            if (AvailableScanRange < distance || !this.CheckIsWorking())
            {
                return(new MyDetectedEntityInfo());
            }

            AvailableScanRange -= distance;

            var startPos  = this.WorldMatrix.Translation;
            var targetPos = startPos + direction * distance;

            //try a physics raycast first
            //very accurate, but very slow
            List <MyPhysics.HitInfo> hits = new List <MyPhysics.HitInfo>();

            MyPhysics.CastRay(startPos, targetPos, hits);

            foreach (var hit in hits)
            {
                var entity = (MyEntity)hit.HkHitInfo.GetHitEntity();
                if (entity == this)
                {
                    continue;
                }

                m_lastRay = new RaycastInfo()
                {
                    Distance = distance, Start = startPos, End = targetPos, Hit = hit.Position
                };
                return(MyDetectedEntityInfoHelper.Create(entity, this.OwnerId, hit.Position));
            }

            //long-distance planet scanning
            //fastest way is to intersect planet bounding boxes then treat the planet as a sphere
            LineD line   = new LineD(startPos, targetPos);
            var   voxels = new List <MyLineSegmentOverlapResult <MyVoxelBase> >();

            MyGamePruningStructure.GetVoxelMapsOverlappingRay(ref line, voxels);

            foreach (var result in voxels)
            {
                var planet = result.Element as MyPlanet;
                if (planet == null)
                {
                    continue;
                }

                double distCenter = Vector3D.DistanceSquared(this.PositionComp.GetPosition(), planet.PositionComp.GetPosition());
                var    gravComp   = planet.Components.Get <MyGravityProviderComponent>();
                if (gravComp == null)
                {
                    continue;
                }

                if (!gravComp.IsPositionInRange(startPos) && distCenter > planet.MaximumRadius * planet.MaximumRadius)
                {
                    var boundingSphere = new BoundingSphereD(planet.PositionComp.GetPosition(), planet.MaximumRadius);
                    var rayd           = new RayD(startPos, direction);
                    var intersection   = boundingSphere.Intersects(rayd);

                    if (!intersection.HasValue)
                    {
                        continue;
                    }

                    if (distance < intersection.Value)
                    {
                        continue;
                    }

                    var hitPos = startPos + direction * intersection.Value;
                    m_lastRay = new RaycastInfo()
                    {
                        Distance = distance, Start = startPos, End = targetPos, Hit = hitPos
                    };
                    return(MyDetectedEntityInfoHelper.Create(result.Element, this.OwnerId, hitPos));
                }

                //if the camera is inside gravity, query voxel storage
                if (planet.RootVoxel.Storage == null)
                {
                    continue;
                }
                var start = Vector3D.Transform(line.From, planet.PositionComp.WorldMatrixInvScaled);
                start += planet.SizeInMetresHalf;
                var end = Vector3D.Transform(line.To, planet.PositionComp.WorldMatrixInvScaled);
                end += planet.SizeInMetresHalf;

                var voxRay = new LineD(start, end);

                double startOffset;
                double endOffset;
                if (!planet.RootVoxel.Storage.DataProvider.Intersect(ref voxRay, out startOffset, out endOffset))
                {
                    continue;
                }

                var from = voxRay.From;
                voxRay.From = from + voxRay.Direction * voxRay.Length * startOffset;
                voxRay.To   = from + voxRay.Direction * voxRay.Length * endOffset;

                start = voxRay.From - planet.SizeInMetresHalf;
                start = Vector3D.Transform(start, planet.PositionComp.WorldMatrix);

                m_lastRay = new RaycastInfo()
                {
                    Distance = distance, Start = startPos, End = targetPos, Hit = start
                };
                return(MyDetectedEntityInfoHelper.Create(result.Element, this.OwnerId, start));
            }

            m_lastRay = new RaycastInfo()
            {
                Distance = distance, Start = startPos, End = targetPos, Hit = null
            };
            return(new MyDetectedEntityInfo());
        }