private void DrawLightEdge(LightEdge e, Color col) { // debug light edges Vector3 p0 = cam.WorldToScreenPoint(new Vector3(e.x1, e.y1, 0)); Vector3 p1 = cam.WorldToScreenPoint(new Vector3(e.x2, e.y2, 0)); DrawLine(lightTexture, (int)p0.x, (int)p0.y, (int)p1.x, (int)p1.y, col); Debug.DrawLine(new Vector3(e.x1, e.y1, 0), new Vector3(e.x2, e.y2, 0), col); if (e.prev != null) { //DrawLine(lightTexture, (int)p0.x - 5, (int)p0.y - 5, (int)p0.x + 5, (int)p0.y + 5, Color.magenta); } if (e.next != null) { //DrawLine(lightTexture, (int)p1.x + 5, (int)p1.y - 5, (int)p1.x - 5, (int)p1.y + 5, new Color(1f, 1f, 1, 1)); //DrawLine(lightTexture, (int)p1.x - 5, (int)p1.y - 5, (int)p1.x + 5, (int)p1.y + 5, Color.white); } }
private float DistancePointToEdge(float x, float y, LightEdge e) { float x1 = e.x1; float y1 = e.y1; float x2 = e.x2; float y2 = e.y2; float A = x - x1; float B = y - y1; float C = x2 - x1; float D = y2 - y1; float dot = A * C + B * D; float len_sq = C * C + D * D; float param = -1.0f; if (len_sq != 0) { param = dot / len_sq; } float xx, yy; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { xx = x1 + param * C; yy = y1 + param * D; } var dx = x - xx; var dy = y - yy; return(Mathf.Sqrt(dx * dx + dy * dy)); }
private float LightEdgesIntersect(LightEdge a, LightEdge b) { float ua, ub, ud, ux, uy, vx, vy, wx, wy; ua = 0.0f; ux = a.x2 - a.x1; uy = a.y2 - a.y1; vx = b.x2 - b.x1; vy = b.y2 - b.y1; wx = a.x1 - b.x1; wy = a.y1 - b.y1; ud = vy * ux - vx * uy; if (ud != 0) { ua = (vx * wy - vy * wx) / ud; ub = (ux * wy - uy * wx) / ud; if (ua < 0 || ua > 1 || ub < 0 || ub > 1) { ua = 0; } } return(ua); }
public void GetLightEdges(Rect bounds, float x, float y) { PriorityQueue <LightEdge> edges = new PriorityQueue <LightEdge>(); float leftX = Mathf.FloorToInt(bounds.x); float rightX = Mathf.FloorToInt(bounds.x + bounds.width) + 1; float botY = Mathf.FloorToInt(bounds.y); float topY = Mathf.FloorToInt(bounds.y + bounds.height) + 1; // find all edges facing (x, y) in the bounds int leftCol = Mathf.FloorToInt(leftX / CELL_SIZE); leftCol = Mathf.Clamp(leftCol, 0, Cave.grid.GetLength(0) - 1); int rightCol = Mathf.FloorToInt(rightX / CELL_SIZE) - 1; rightCol = Mathf.Clamp(rightCol, 0, Cave.grid.GetLength(0) - 1); int topRow = Mathf.FloorToInt(topY / CELL_SIZE) - 1; topRow = Mathf.Clamp(topRow, -Cave.grid.GetLength(1) + 1, 0); int botRow = Mathf.FloorToInt(botY / CELL_SIZE); botRow = Mathf.Clamp(botRow, -Cave.grid.GetLength(1) + 1, 0); // false means searching for end point // true means searching for start point bool firstIteration = true; for (int r = botRow; r <= topRow; r++) { #region Set Left and Right Boundary Edges Cell leftCell = Cave.grid[leftCol, -r]; if (leftCell == Cell.Empty || leftCell == Cell.SlantSW || leftCell == Cell.SlantNW) { LightEdge e = new LightEdge(leftCol, r, leftCol, r + 1); e.distance = DistancePointToEdge(x, y, e); edges.Enqueue(e); } Cell rightCell = Cave.grid[rightCol, -r]; if (rightCell == Cell.Empty || rightCell == Cell.SlantSE || rightCell == Cell.SlantNE) { LightEdge e = new LightEdge(rightX, r + 1, rightX, r); e.distance = DistancePointToEdge(x, y, e); edges.Enqueue(e); } #endregion for (int c = leftCol; c <= rightCol; c++) { #region Set Top and Bottom Boundary Edges if (firstIteration) { Cell topCell = Cave.grid[c, -topRow]; if (topCell == Cell.Empty || topCell == Cell.SlantNE || topCell == Cell.SlantNW) { LightEdge e = new LightEdge(c, topY, c + 1, topY); e.distance = DistancePointToEdge(x, y, e); edges.Enqueue(e); } Cell botCell = Cave.grid[c, -botRow]; if (botCell == Cell.Empty || botCell == Cell.SlantSE || botCell == Cell.SlantSW) { LightEdge e = new LightEdge(c + 1, botRow, c, botRow); e.distance = DistancePointToEdge(x, y, e); edges.Enqueue(e); } } #endregion LightEdge ee = GetCellEdgeFacingPoint(c, r, x, y); if (ee != null) { ee.distance = DistancePointToEdge(x, y, ee); edges.Enqueue(ee); } } firstIteration = false; } foreach (LightEdge e in edges.getData()) { // set next and previous edge values foreach (LightEdge b in edges.getData()) { if (!e.Equals(b)) { if (e.x1 == b.x2 && e.y1 == b.y2) { e.prev = b; b.next = e; } if (e.x2 == b.x1 && e.y2 == b.y1) { e.next = b; b.prev = e; } } } } List <LightEdge> pedges = new List <LightEdge>(); foreach (LightEdge e in edges.getData()) { // set projections if (e.prev == null) { Vector2 dir = new Vector2(e.x1 - x, e.y1 - y); Vector2 line = dir.normalized * cam.orthographicSize * 10f; LightEdge proj = new LightEdge(e.x1 + line.x, e.y1 + line.y, e.x1, e.y1); proj.next = e; proj.prev = null; e.prev = proj; // intersection logic float pmag = (new Vector2(proj.x2 - proj.x1, proj.y2 - proj.x1)).magnitude; foreach (LightEdge le in edges.getData()) { float t = LightEdgesIntersect(le, proj); Vector2 v = new Vector2(le.x2 - le.x1, le.y2 - le.y1); v = v.normalized * v.magnitude * t; float xi = le.x1 + v.x; float yi = le.y1 + v.y; float mag = Mathf.Sqrt(Mathf.Pow(proj.x2 - xi, 2) + Mathf.Pow(proj.y2 - yi, 2)); if (0 < t && t <= 1 && !le.Equals(e) && mag < pmag) { proj.prev = le; le.next = proj; proj.x1 = xi; proj.y1 = yi; le.x2 = xi; le.y2 = yi; pmag = mag; } } proj.distance = DistancePointToEdge(x, y, proj); pedges.Add(proj); } if (e.next == null) { Vector2 dir = new Vector2(e.x2 - x, e.y2 - y); Vector2 line = dir.normalized * cam.orthographicSize * 10f; LightEdge proj = new LightEdge(e.x2, e.y2, e.x2 + line.x, e.y2 + line.y); proj.prev = e; proj.next = null; e.next = proj; // intersection logic float pmag = (new Vector2(proj.x2 - proj.x1, proj.y2 - proj.x1)).magnitude; foreach (LightEdge le in edges.getData()) { float t = LightEdgesIntersect(proj, le); Vector2 v = new Vector2(proj.x2 - proj.x1, proj.y2 - proj.y1); v = v.normalized * v.magnitude * t; float xi = proj.x1 + v.x; float yi = proj.y1 + v.y; float mag = Mathf.Sqrt(Mathf.Pow(proj.x1 - xi, 2) + Mathf.Pow(proj.y1 - yi, 2)); if (0 < t && t <= 1 && !le.Equals(e) && mag < pmag) { proj.next = le; le.prev = proj; proj.x2 = xi; proj.y2 = yi; le.x1 = xi; le.y1 = yi; pmag = mag; } } proj.distance = DistancePointToEdge(x, y, proj); pedges.Add(proj); } } // add projections to priority queue foreach (LightEdge e in pedges) { edges.Enqueue(e); } // debug next and previous edge links foreach (LightEdge e in edges.getData()) { //DrawLightEdge(e, Color.red); if (e.next != null) { // NEXT DebugDrawSquare(e.x2, e.y2, 0.1f, Color.cyan); } else { DebugDrawSquare(e.x2, e.y2, 0.2f, Color.magenta); DebugDrawSquare(e.x2, e.y2, 0.25f, Color.magenta); } if (e.prev != null) { // PREV DebugDrawSquare(e.x1, e.y1, 0.05f, Color.green); } else { DebugDrawSquare(e.x1, e.y1, 0.2f, Color.magenta); DebugDrawSquare(e.x1, e.y1, 0.25f, Color.magenta); } } Mesh mesh = new Mesh(); MeshFilter filter = GetComponent <MeshFilter>(); int i = 0; LightEdge light = edges.Peek(); List <Vector3> vertices = new List <Vector3>(); vertices.Add(new Vector3(x, y, 0)); do { vertices.Add(new Vector3(light.x1, light.y1, 0)); //DrawLightEdge(light, Color.green); light = light.next; i++; } while (light != null && !light.Equals(edges.Peek()) && i < edges.getData().Count); List <Int32> indices = new List <Int32>(); for (int v = 1; v < vertices.Count; v++) { indices.Add(0); indices.Add(v); int vv = v + 1; if (vv >= vertices.Count) { vv = 1; } indices.Add(vv); } mesh.Clear(); mesh.SetVertices(vertices); mesh.SetTriangles(indices, 0); lightTexture.Apply(); filter.mesh = mesh; }