public Segment(SegmentPoint start, SegmentPoint end) { Start = start; End = end; start.Segment = this; end.Segment = this; IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y); }
public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull) { Start = start; End = end; ConvexHull = convexHull; start.ConvexHull = convexHull; end.ConvexHull = convexHull; IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y); }
public void SetVertices(Vector2[] points) { Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported"); LastVertexChangeTime = (float)Timing.TotalTime; for (int i = 0; i < 4; i++) { vertices[i] = new SegmentPoint(points[i], this); losVertices[i] = new SegmentPoint(points[i], this); } for (int i = 0; i < 4; i++) { segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4], this); } int margin = 0; if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[1].Y)) { losVertices[0].Pos = new Vector2(points[0].X + margin, points[0].Y); losVertices[1].Pos = new Vector2(points[1].X + margin, points[1].Y); losVertices[2].Pos = new Vector2(points[2].X - margin, points[2].Y); losVertices[3].Pos = new Vector2(points[3].X - margin, points[3].Y); } else { losVertices[0].Pos = new Vector2(points[0].X, points[0].Y + margin); losVertices[1].Pos = new Vector2(points[1].X, points[1].Y - margin); losVertices[2].Pos = new Vector2(points[2].X, points[2].Y - margin); losVertices[3].Pos = new Vector2(points[3].X, points[3].Y + margin); } CalculateDimensions(); if (parentEntity == null || ignoreEdge == null) { return; } for (int i = 0; i < 4; i++) { ignoreEdge[i] = false; } var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine); if (chList != null) { foreach (ConvexHull ch in chList.List) { UpdateIgnoredEdges(ch); } } }
public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull) { if (start.Pos.Y > end.Pos.Y) { var temp = start; start = end; end = temp; } Start = start; End = end; ConvexHull = convexHull; start.ConvexHull = convexHull; end.ConvexHull = convexHull; IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y); IsAxisAligned = Math.Abs(start.Pos.X - end.Pos.X) < 0.1f || Math.Abs(start.Pos.Y - end.Pos.Y) < 0.001f; }
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); }
public void SetVertices(Vector2[] points, Matrix?rotationMatrix = null) { Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported"); LastVertexChangeTime = (float)Timing.TotalTime; for (int i = 0; i < 4; i++) { vertices[i] = new SegmentPoint(points[i], this); losVertices[i] = new SegmentPoint(points[i], this); losOffsets[i] = null; } for (int i = 0; i < 4; i++) { ignoreEdge[i] = false; } overlappingHulls.Clear(); int margin = 0; if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[2].Y)) { losVertices[0].Pos = new Vector2(points[0].X + margin, points[0].Y); losVertices[1].Pos = new Vector2(points[1].X + margin, points[1].Y); losVertices[2].Pos = new Vector2(points[2].X - margin, points[2].Y); losVertices[3].Pos = new Vector2(points[3].X - margin, points[3].Y); } else { losVertices[0].Pos = new Vector2(points[0].X, points[0].Y + margin); losVertices[1].Pos = new Vector2(points[1].X, points[1].Y - margin); losVertices[2].Pos = new Vector2(points[2].X, points[2].Y - margin); losVertices[3].Pos = new Vector2(points[3].X, points[3].Y + margin); } if (rotationMatrix.HasValue) { for (int i = 0; i < vertices.Length; i++) { vertices[i].Pos = Vector2.Transform(vertices[i].Pos, rotationMatrix.Value); losVertices[i].Pos = Vector2.Transform(losVertices[i].Pos, rotationMatrix.Value); } } for (int i = 0; i < 4; i++) { segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4], this); } CalculateDimensions(); if (ParentEntity == null) { return; } var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine); if (chList != null) { overlappingHulls.Clear(); foreach (ConvexHull ch in chList.List) { MergeOverlappingSegments(ch); } } }