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); }
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); }