Esempio n. 1
0
        private List <Vector2> FindRaycastHits()
        {
            if (!CastShadows)
            {
                return(null);
            }
            if (range < 1.0f || color.A < 0.01f)
            {
                return(null);
            }

            Vector2 drawPos = position;

            if (ParentSub != null)
            {
                drawPos += ParentSub.DrawPosition;
            }

            var hulls = new List <ConvexHull>();// ConvexHull.GetHullsInRange(position, range, ParentSub);

            foreach (ConvexHullList chList in hullsInRange)
            {
                //hulls.AddRange(chList.List);
                foreach (ConvexHull hull in chList.List)
                {
                    if (!chList.IsHidden.Contains(hull))
                    {
                        hulls.Add(hull);
                    }
                    //hulls.Add(hull);
                }
                foreach (ConvexHull hull in chList.List)
                {
                    chList.IsHidden.Add(hull);
                }
            }

            float bounds = range * 2;
            //find convexhull segments that are close enough and facing towards the light source
            List <Segment>      visibleSegments = new List <Segment>();
            List <SegmentPoint> points          = new List <SegmentPoint>();

            foreach (ConvexHull hull in hulls)
            {
                hull.RefreshWorldPositions();

                var visibleHullSegments = hull.GetVisibleSegments(drawPos);
                visibleSegments.AddRange(visibleHullSegments);
            }

            //Generate new points at the intersections between segments
            //This is necessary for the light volume to generate properly on some subs
            for (int i = 0; i < visibleSegments.Count; i++)
            {
                Vector2 p1a = visibleSegments[i].Start.WorldPos;
                Vector2 p1b = visibleSegments[i].End.WorldPos;

                for (int j = 0; j < visibleSegments.Count; j++)
                {
                    if (j == i)
                    {
                        continue;
                    }
                    Vector2 p2a = visibleSegments[j].Start.WorldPos;
                    Vector2 p2b = visibleSegments[j].End.WorldPos;

                    if (Vector2.DistanceSquared(p1a, p2a) < 25.0f ||
                        Vector2.DistanceSquared(p1a, p2b) < 25.0f ||
                        Vector2.DistanceSquared(p1b, p2a) < 25.0f ||
                        Vector2.DistanceSquared(p1b, p2b) < 25.0f)
                    {
                        continue;
                    }

                    Vector2?intersection = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b);

                    if (intersection != null)
                    {
                        Vector2 intersectionVal = intersection.Value;

                        SegmentPoint start = visibleSegments[i].Start;
                        SegmentPoint end   = visibleSegments[i].End;
                        SegmentPoint mid   = new SegmentPoint(intersectionVal, null);

                        if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 25.0f ||
                            Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 25.0f)
                        {
                            continue;
                        }

                        Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull); seg1.IsHorizontal = visibleSegments[i].IsHorizontal;
                        Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull); seg2.IsHorizontal = visibleSegments[i].IsHorizontal;
                        visibleSegments[i] = seg1;
                        visibleSegments.Insert(i + 1, seg2);
                        i--;
                        break;
                    }
                }
            }

            foreach (Segment s in visibleSegments)
            {
                points.Add(s.Start);
                points.Add(s.End);
                if (Math.Abs(s.Start.WorldPos.X - drawPos.X) > bounds)
                {
                    bounds = Math.Abs(s.Start.WorldPos.X - drawPos.X);
                }
                if (Math.Abs(s.Start.WorldPos.Y - drawPos.Y) > bounds)
                {
                    bounds = Math.Abs(s.Start.WorldPos.Y - drawPos.Y);
                }
                if (Math.Abs(s.End.WorldPos.X - drawPos.X) > bounds)
                {
                    bounds = Math.Abs(s.End.WorldPos.X - drawPos.X);
                }
                if (Math.Abs(s.End.WorldPos.Y - drawPos.Y) > bounds)
                {
                    bounds = Math.Abs(s.End.WorldPos.Y - drawPos.Y);
                }
            }

            //add a square-shaped boundary to make sure we've got something to construct the triangles from
            //even if there aren't enough hull segments around the light source

            //(might be more effective to calculate if we actually need these extra points)
            var boundaryCorners = new List <SegmentPoint> {
                new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y + bounds), null),
                new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y - bounds), null),
                new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y - bounds), null),
                new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y + bounds), null)
            };

            points.AddRange(boundaryCorners);

            for (int i = 0; i < 4; i++)
            {
                visibleSegments.Add(new Segment(boundaryCorners[i], boundaryCorners[(i + 1) % 4], null));
            }

            var compareCCW = new CompareSegmentPointCW(drawPos);

            try
            {
                points.Sort(compareCCW);
            }
            catch (Exception e)
            {
                StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
                foreach (SegmentPoint sp in points)
                {
                    sb.AppendLine(sp.Pos.ToString());
                }
                DebugConsole.ThrowError(sb.ToString(), e);
            }

            List <Vector2> output = new List <Vector2>();

            //List<Pair<int, Vector2>> preOutput = new List<Pair<int, Vector2>>();

            //remove points that are very close to each other
            for (int i = 0; i < points.Count - 1; i++)
            {
                if (Math.Abs(points[i].WorldPos.X - points[i + 1].WorldPos.X) < 6 &&
                    Math.Abs(points[i].WorldPos.Y - points[i + 1].WorldPos.Y) < 6)
                {
                    points.RemoveAt(i + 1);
                    i--;
                }
            }

            foreach (SegmentPoint p in points)
            {
                Vector2 dir       = Vector2.Normalize(p.WorldPos - drawPos);
                Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;

                //do two slightly offset raycasts to hit the segment itself and whatever's behind it
                Pair <int, Vector2> intersection1 = RayCast(drawPos, drawPos + dir * bounds * 2 - dirNormal, visibleSegments);
                Pair <int, Vector2> intersection2 = RayCast(drawPos, drawPos + dir * bounds * 2 + dirNormal, visibleSegments);

                if (intersection1.First < 0)
                {
                    return(new List <Vector2>());
                }
                if (intersection2.First < 0)
                {
                    return(new List <Vector2>());
                }
                Segment seg1 = visibleSegments[intersection1.First];
                Segment seg2 = visibleSegments[intersection2.First];

                bool isPoint1 = MathUtils.LineToPointDistance(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 5.0f;
                bool isPoint2 = MathUtils.LineToPointDistance(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 5.0f;

                if (isPoint1 && isPoint2)
                {
                    //hit at the current segmentpoint -> place the segmentpoint into the list
                    output.Add(p.WorldPos);

                    foreach (ConvexHullList hullList in hullsInRange)
                    {
                        hullList.IsHidden.Remove(p.ConvexHull);
                        hullList.IsHidden.Remove(seg1.ConvexHull);
                        hullList.IsHidden.Remove(seg2.ConvexHull);
                    }
                }
                else if (intersection1.First != intersection2.First)
                {
                    //the raycasts landed on different segments
                    //we definitely want to generate new geometry here
                    output.Add(isPoint1 ? p.WorldPos : intersection1.Second);
                    output.Add(isPoint2 ? p.WorldPos : intersection2.Second);

                    foreach (ConvexHullList hullList in hullsInRange)
                    {
                        hullList.IsHidden.Remove(p.ConvexHull);
                        hullList.IsHidden.Remove(seg1.ConvexHull);
                        hullList.IsHidden.Remove(seg2.ConvexHull);
                    }
                }
                //if neither of the conditions above are met, we just assume
                //that the raycasts both resulted on the same segment
                //and creating geometry here would be wasteful
            }

            //remove points that are very close to each other
            for (int i = 0; i < output.Count - 1; i++)
            {
                if (Math.Abs(output[i].X - output[i + 1].X) < 6 &&
                    Math.Abs(output[i].Y - output[i + 1].Y) < 6)
                {
                    output.RemoveAt(i + 1);
                    i--;
                }
            }

            return(output);
        }
Esempio n. 2
0
        private List <Vector2> FindRaycastHits()
        {
            if (!CastShadows)
            {
                return(null);
            }
            if (range < 1.0f || color.A < 0.01f)
            {
                return(null);
            }

            Vector2 drawPos = position;

            if (ParentSub != null)
            {
                drawPos += ParentSub.DrawPosition;
            }

            var hulls = new List <ConvexHull>();// ConvexHull.GetHullsInRange(position, range, ParentSub);

            foreach (ConvexHullList chList in hullsInRange)
            {
                hulls.AddRange(chList.List);
            }

            //find convexhull segments that are close enough and facing towards the light source
            List <Segment>      visibleSegments = new List <Segment>();
            List <SegmentPoint> points          = new List <SegmentPoint>();

            foreach (ConvexHull hull in hulls)
            {
                hull.RefreshWorldPositions();

                var visibleHullSegments = hull.GetVisibleSegments(drawPos);
                visibleSegments.AddRange(visibleHullSegments);

                foreach (Segment s in visibleHullSegments)
                {
                    points.Add(s.Start);
                    points.Add(s.End);
                }
            }

            //add a square-shaped boundary to make sure we've got something to construct the triangles from
            //even if there aren't enough hull segments around the light source

            //(might be more effective to calculate if we actually need these extra points)
            var boundaryCorners = new List <SegmentPoint> {
                new SegmentPoint(new Vector2(drawPos.X + range * 2, drawPos.Y + range * 2)),
                new SegmentPoint(new Vector2(drawPos.X + range * 2, drawPos.Y - range * 2)),
                new SegmentPoint(new Vector2(drawPos.X - range * 2, drawPos.Y - range * 2)),
                new SegmentPoint(new Vector2(drawPos.X - range * 2, drawPos.Y + range * 2))
            };

            points.AddRange(boundaryCorners);

            var compareCCW = new CompareSegmentPointCW(drawPos);

            try
            {
                points.Sort(compareCCW);
            }
            catch (Exception e)
            {
                StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
                foreach (SegmentPoint sp in points)
                {
                    sb.AppendLine(sp.Pos.ToString());
                }
                DebugConsole.ThrowError(sb.ToString(), e);
            }

            List <Vector2> output = new List <Vector2>();

            //remove points that are very close to each other
            for (int i = 0; i < points.Count - 1; i++)
            {
                if (Math.Abs(points[i].WorldPos.X - points[i + 1].WorldPos.X) < 3 &&
                    Math.Abs(points[i].WorldPos.Y - points[i + 1].WorldPos.Y) < 3)
                {
                    points.RemoveAt(i + 1);
                }
            }

            foreach (SegmentPoint p in points)
            {
                Vector2 dir       = Vector2.Normalize(p.WorldPos - drawPos);
                Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;

                //do two slightly offset raycasts to hit the segment itself and whatever's behind it
                Vector2 intersection1 = RayCast(drawPos, drawPos + dir * range * 2 - dirNormal, visibleSegments);
                Vector2 intersection2 = RayCast(drawPos, drawPos + dir * range * 2 + dirNormal, visibleSegments);

                //hit almost the same position -> only add one vertex to output
                if ((Math.Abs(intersection1.X - intersection2.X) < 5 &&
                     Math.Abs(intersection1.Y - intersection2.Y) < 5))
                {
                    output.Add(intersection1);
                }
                else
                {
                    output.Add(intersection1);
                    output.Add(intersection2);
                }
            }

            return(output);
        }