private void CreateShadowPolygonPoints() { ShadowPolygonPoints = ShadowPolygonPoints ?? new List <Vector2>(); ShadowPolygonPoints.Clear(); if (_visiblitySet.Output.Any()) { Vector2 previous = Vector2.one * 1000000; for (int index = 0; index < _visiblitySet.Output.Count; index += 1) { Vector2 point = _visiblitySet.Output[index]; if (!ShadowMathUtils.Approximately(point, previous)) { ShadowPolygonPoints.Add(point); } previous = point; } ShadowPolygonPoints.Add(_visiblitySet.Output.First()); } if (_drawGizmos) { Gizmos.color = Color.yellow; for (int index = 0; index < ShadowPolygonPoints.Count - 1; index++) { Vector2 va = ShadowPolygonPoints[index]; Vector2 vb = ShadowPolygonPoints[index + 1]; Gizmos.DrawLine(va * 1.05f, vb * 1.05f); } Gizmos.DrawLine(ShadowPolygonPoints.Last() * 1.05f, ShadowPolygonPoints.First() * 1.05f); } }
private void AddTriangle(float angle1, float angle2, Segment segment, Segment previous) { Vector2 delta1 = new Vector2(Mathf.Cos(angle1), Mathf.Sin(angle1)); Vector2 delta2 = new Vector2(Mathf.Cos(angle2), Mathf.Sin(angle2)); Vector2 p1 = Center; Vector2 p2 = p1 + delta1; Vector2 p3 = new Vector2(0.0f, 0.0f); Vector2 p4 = new Vector2(0.0f, 0.0f); Gizmos.color = Color.blue; if (segment != null) { // Stop the triangle at the intersecting segment p3 = segment.Start.Point; p4 = segment.End.Point; } else { // Stop the triangle at a fixed distance; this probably is // not what we want, but it never gets used in the demo p3 = p1 + delta1 * 500; p4 = p1 + delta2 * 500; } Vector2 pBegin = ShadowMathUtils.LineIntersection(p3, p4, p1, p2); p2 = p1 + delta2; Vector2 pEnd = ShadowMathUtils.LineIntersection(p3, p4, p1, p2); if (_drawGizmos) { Gizmos.color = Color.green; Gizmos.DrawLine(p1, pBegin); Gizmos.DrawLine(p2, pEnd); //Gizmos.DrawLine(pBegin, pEnd); Gizmos.color = Color.yellow; Gizmos.DrawSphere(pBegin, 0.1f); Gizmos.color = Color.blue; Gizmos.DrawSphere(pEnd, 0.1f); } if (previous != null && ShadowMathUtils.Approximately(segment.Slope, previous.Slope) && ShadowMathUtils.Approximately(Output.Last(), pBegin)) { // It's a continuation of the previous segment! Output.RemoveAt(Output.Count - 1); Output.Add(pEnd); } else { Output.Add(pBegin); Output.Add(pEnd); } }
public bool GetIsPointInLight(Vector2 position) { if (!_bounds.Contains(position)) { return(false); } if (!ShadowPolygonPoints.Any()) { return(false); } return(ShadowMathUtils.GetIsPointInPoly(position, ShadowPolygonPoints)); }
// Helper: do we know that segment a is in front of b? // Implementation not anti-symmetric (that is to say, // _segment_in_front_of(a, b) != (!_segment_in_front_of(b, a)). // Also note that it only has to work in a restricted set of cases // in the visibility algorithm; I don't think it handles all // cases. See http://www.redblobgames.com/articles/visibility/segment-sorting.html public static IntersectionResult IsInFrontOf(Segment a, Segment b, Vector2 relativeTo) { // NOTE: we slightly shorten the segments so that // intersections of the endpoints (common) don't count as // intersections in this algorithm const float epsilon = 0.01f; bool A1 = ShadowMathUtils.GetIsLeft(a, ShadowMathUtils.Interpolate(b.Start.Point, b.End.Point, epsilon)); bool A2 = ShadowMathUtils.GetIsLeft(a, ShadowMathUtils.Interpolate(b.End.Point, b.Start.Point, epsilon)); bool A3 = ShadowMathUtils.GetIsLeft(a, relativeTo); bool B1 = ShadowMathUtils.GetIsLeft(b, ShadowMathUtils.Interpolate(a.Start.Point, a.End.Point, epsilon)); bool B2 = ShadowMathUtils.GetIsLeft(b, ShadowMathUtils.Interpolate(a.End.Point, a.Start.Point, epsilon)); bool B3 = ShadowMathUtils.GetIsLeft(b, relativeTo); // NOTE: this algorithm is probably worthy of a short article // but for now, draw it on paper to see how it works. Consider // the line A1-A2. If both B1 and B2 are on one side and // relativeTo is on the other side, then A is in between the // viewer and B. We can do the same with B1-B2: if A1 and A2 // are on one side, and relativeTo is on the other side, then // B is in between the viewer and A. if (B1 == B2 && B2 != B3) { return(IntersectionResult.InFront); } if (A1 == A2 && A2 == A3) { return(IntersectionResult.InFront); } if (A1 == A2 && A2 != A3) { return(IntersectionResult.Behind); } if (B1 == B2 && B2 == B3) { return(IntersectionResult.Behind); } // If A1 != A2 and B1 != B2 then we have an intersection. // Expose it for the GUI to show a message. A more robust // implementation would split segments at intersections so // that part of the segment is in front and part is behind. //demo_intersectionsDetected.push([a.p1, a.p2, b.p1, b.p2]); return(IntersectionResult.Intersects); // NOTE: previous implementation was a.d < b.d. That's simpler // but trouble when the segments are of dissimilar sizes. If // you're on a grid and the segments are similarly sized, then // using distance will be a simpler and faster implementation. }
public void LateUpdate() { _meshRenderer = _meshRenderer ?? GetComponent <MeshRenderer>(); if (_meshRenderer != null && onlyUpdateWhenVisible && !_meshRenderer.isVisible) { return; } _visiblitySet = _visiblitySet ?? new VisiblitySet(); // Can't rotate the lights at the moment transform.rotation = Quaternion.identity; Vector2 newPosition = transform.position; Vector2 oldScreenSize = _screenSize; _screenSize = new Vector2(Screen.width, Screen.height); bool rebuild = updateOnEachUpdate; rebuild = rebuild || _screenSize != oldScreenSize || !Mathf.Approximately(size, _oldSize); rebuild = rebuild || !Mathf.Approximately(Camera.main.orthographicSize, _oldOrthographicSize); rebuild = rebuild || !Mathf.Approximately(Camera.main.fieldOfView, _oldFieldOfView); rebuild = rebuild || updateOnChangedLocation && !ShadowMathUtils.Approximately(_oldPosition, newPosition); rebuild = rebuild || updateWhenCollidersChange && GetHaveCollidersChanged(); _oldSize = size; if (rebuild) { RebuildShadow(); _oldOrthographicSize = Camera.main.orthographicSize; _oldFieldOfView = Camera.main.fieldOfView; } else { if (updateUvs && !ShadowMathUtils.Approximately(Camera.main.transform.position, _oldCameraPosition)) { _visiblitySet.UpdateUvs(); _oldCameraPosition = Camera.main.transform.position; } } _oldPosition = newPosition; }
private bool InnerUpdateColliders(Vector2 topLeft, Vector2 bottomRight, LayerMask layerMask) { _updateOnFrame = Time.frameCount; Collider2D[] oldColliders = colliders; colliders = Physics2D.OverlapAreaAll(topLeft, bottomRight, layerMask); // Sort them so we get them in the same order as before. This is required for the comparison we do later on. Array.Sort(colliders, (c1, c2) => c1.GetInstanceID() - c2.GetInstanceID()); Vector3[] oldPositions = positions; positions = colliders.Select(c => c.transform.position).ToArray(); Quaternion[] oldRotations = rotations; rotations = colliders.Select(c => c.transform.rotation).ToArray(); // Ease fast checks first if (oldColliders == null || oldColliders.Length != colliders.Length) { return(true); } for (int index = 0; index < colliders.Length; index++) { Collider2D newCollider = colliders[index]; Collider2D oldCollider = oldColliders[index]; if (newCollider != oldCollider) { return(true); } if (!ShadowMathUtils.Approximately(oldPositions[index], newCollider.transform.position)) { return(true); } if (!ShadowMathUtils.Approximately(oldRotations[index], newCollider.transform.rotation)) { return(true); } } return(false); }
private void SplitSegmentsThatOverlap() { // O(N^2) Queue <Segment> open = new Queue <Segment>(Segments); Segments.Clear(); while (open.Any()) { if (Segments.Count > 300) { Debug.Log("Too many segment splits!"); return; } Segment segment = open.Dequeue(); bool splitFree = true; foreach (Segment testing in Segments) { Segment.IntersectionResult intersection = Segment.IsInFrontOf(segment, testing, Center); if (intersection == Segment.IntersectionResult.Intersects) { // Do they *really* overlap, there seems to be some issues with the IsInFrontOf method. if (ShadowMathUtils.Approximately(testing.Start.Point, segment.Start.Point) || ShadowMathUtils.Approximately(testing.End.Point, segment.End.Point) || ShadowMathUtils.Approximately(testing.End.Point, segment.Start.Point) || ShadowMathUtils.Approximately(testing.Start.Point, segment.End.Point)) { //intersection = Segment.IsInFrontOf(segment, testing, Center); //Debug.Log("They're the same point!"+intersection); continue; } // segment && testing overlap, we must split one of them but both must go back on the queue Segments.Remove(testing); open.Enqueue(testing); Vector2 position = ShadowMathUtils.LineIntersection( segment.Start.Point, segment.End.Point, testing.Start.Point, testing.End.Point); if (_drawGizmos) { Gizmos.color = Color.red; Gizmos.DrawSphere(position, 0.2f); } // Split segments and add both parts back to the queue Split(segment, position, open); splitFree = false; break; } } if (splitFree) { Segments.Add(segment); } } }
private void DoMergeCollinearSegments() { if (!MergeCollinearSegments) { return; } //int merges = 0; //int compares = 0; //int before = Segments.Count; //Stopwatch stopwatch = Stopwatch.StartNew(); // Log of performance optimization of this very rouine. // Merged 287 to 149 segments (138 merges, 1710596 compares). 322 ms. // Merged 287 to 149 segments (138 merges, 105329 compares). 14 ms. // Merged 287 to 149 segments (138 merges, 84767 compares, 2 tries). 14 ms. // Merged 287 to 149 segments (138 merges, 84767 compares, 2 tries). 13 ms. // Merged 287 to 149 segments (138 merges, 1968 compares, 6 tries). 5 ms. // Merged 287 to 149 segments (138 merges, 1968 compares, 6 tries). 4 ms. int tries = 0; ILookup <Vector2, Segment> startpointLookup = Segments .ToLookup(s => s.Start.Point, s => s); while (tries++ < 4) { bool merged = false; for (int index = 0; index < Segments.Count; index++) { Segment segment = Segments[index]; if (segment.Merged) { continue; } IEnumerable <Segment> startpoints = startpointLookup[segment.End.Point]; foreach (Segment other in startpoints) { //compares++; if (other.Merged) { continue; } if (ShadowMathUtils.Approximately(segment.Slope, other.Slope)) { other.Merged = true; segment.End.Point = other.End.Point; //merges++; merged = true; } } } Segments.RemoveAll(s => s.Merged); if (!merged) { break; } } //Debug.LogFormat("Merged {0} to {1} segments ({2} merges, {3} compares, {5} tries). {4} ms.", before, Segments.Count, merges, compares, stopwatch.ElapsedMilliseconds, tries); }