using System; using System.Collections.Generic; using System.Linq; using Unity.Burst.CompilerServices; using UnityEditor; using UnityEngine; using UnityEngine.EventSystems; using WI; using XED.Interfaces; using XED.Manage; using XED.RuntimeGizmo; namespace XED { public class WallBuilder : MonoBehaviour, ISingle, IInputHandler, IStatusController { public enum VirtualPoint { LeftTop, LeftBottom, RightTop, RightBottom, LeftCenter, RightCenter, } public enum BuilderState { None, PointModfiy, PointRemove, Drawing } public float thickness; private GameObject renderParent; private Material lineMat; public Action onRemoveWallGroupEvent; //wall private HashSet walls = new(); private List removeLines = new(); private List cPoints = new(); private HashSet linePoints = new(); private List wallgroups = new(); Dictionary> groupTable = new(); private HashSet<(Wall, Wall)> edgeTable = new(); public Dictionary> connectLines = new(); private Wall tempWall; private LinePoint clickedPoint; private LinePoint handlePoint; private LinePoint pilePoint; private HashSet pointAddWalls = new(); private HashSet addPoints = new(); public LinePoint prf_LinePoint; public Wall prf_Wall; public WallGroup prf_WallGroup; private FloorCreateManager fm; private int cc; public Action> onCreateWallMesh; int index; public Dictionary> pointToWall = new(); internal event Action onDrawStart; private MeshCreator meshCreator; public class Group { public List points = new(); public List wallLines = new(); public void AddWallLine(Wall wallLine) { if (wallLines.Contains(wallLine)) return; wallLines.Add(wallLine); } } private RTGController rtgController; public override void AfterAwake() { renderParent = new GameObject("LineRenderParent"); lineMat = Resources.Load("Materials/Mat_LineRender"); fm = FindSingle(); prf_LinePoint = Resources.Load("Prefabs/PRF_LinePoint"); prf_Wall = Resources.Load("Prefabs/PRF_Wall"); prf_WallGroup = Resources.Load("Prefabs/PRF_WallGroup"); meshCreator = FindSingle(); rtgController = new(); handler = GetInputHandler(); } public void SetWallViewMode(ViewMode mode) { switch (mode) { case ViewMode.TopView: //2D View �϶� break; case ViewMode.PerspectiveView: MeshCreate(); break; } } public void SetDrawState(BuilderState state) { drawState = state; //GizmoController 수정해야함. switch (drawState) { case BuilderState.Drawing: DrawStart(); break; case BuilderState.PointModfiy: //기지모 생성? break; case BuilderState.PointRemove: //나중에 팝업?? break; default: MeshCreate(); break; } } public void RemoveSelectedPoint(LinePoint lp) { if (lp == null) return; //해당 포인트 제거 var removeWalls = new List(); foreach (var removeWall in pointToWall[lp]) { removeWalls.Add(removeWall); } for (int i= 0; i dividePoints = new(); public WallDivider(Wall target) { this.target = target; } public void AddDividePoint(LinePoint point) { dividePoints.Add(point); } public List<(LinePoint, LinePoint)> Divide() { dividePoints.Add(target.leftCenterPoint); dividePoints.Add(target.rightCenterPoint); dividePoints = dividePoints.OrderBy(x => Vector3.Distance(target.leftCenterPoint.position, x.position)).ToList(); List<(LinePoint, LinePoint)> results = new(); for(int i =0;i dividers = new(); foreach (var wall in walls) { var lps2 = wall.GetCenterPointPositions(); Vector3 cp = Vector3.zero; // continue; if (fixWall.isConnected(wall)) continue; if (!Wathf.GetCrossVector(lps, lps2, ref cp)) continue; if(!TryGetNearPoint(cp, out LinePoint clp)) clp = CreateLinePoint(cp); dividers.TryAdd(wall, new WallDivider(wall)); dividers[wall].AddDividePoint(clp); dividers.TryAdd(fixWall, new WallDivider(fixWall)); dividers[fixWall].AddDividePoint(clp); } foreach(var w in dividers) { var dividedLines = w.Value.Divide(); foreach(var dl in dividedLines) { CreateWall(dl.Item1, dl.Item2); } Break(w.Key); } } } void Break(Wall wall) { walls.Remove(wall); var lcp = wall.leftCenterPoint; var rcp = wall.rightCenterPoint; pointToWall[lcp].Remove(wall); pointToWall[rcp].Remove(wall); rcp.Disconnect(lcp); lcp.Disconnect(rcp); if (lcp.connectPoints.Count == 0) { linePoints.Remove(lcp); pointToWall.Remove(lcp); } if(rcp.connectPoints.Count == 0) { linePoints.Remove(rcp); pointToWall.Remove(rcp); } wall.Deselect(); Destroy(wall.gameObject); } Wall BuildContinuesWall(Vector3 p) { var endPoint = handlePoint; endPoint.transform.position = p; pilePoint = endPoint; handlePoint = CreateLinePoint(p); var fixWall = tempWall; tempWall = CreateWall(pilePoint, handlePoint); return fixWall; } void BuildFirstWall(Vector3 p) { if (TryGetNearPoint(p, out LinePoint point)) { pilePoint = point; handlePoint = CreateLinePoint(p); } else { pilePoint = CreateLinePoint(p); } tempWall = CreateWall(pilePoint, handlePoint); } bool TryGetNearPoint(Vector3 pos, out LinePoint point) { point = linePoints .Where(x => Vector3.Distance(x.position, pos) < 0.05f) .FirstOrDefault(); return point != null && point != handlePoint; } #region wall private bool OverlapLine(Wall tWall) { var crashPoints = tWall.crashLinePoints .OrderByDescending(x => Vector3.Distance(tWall.rightCenterPoint.position, x.position)).ToList(); if (crashPoints.Count < 2) { return false; } var crashWalls = tWall.crashWalls.ToList(); if (crashPoints.Count == 2) { if (!RemoveCrashWall(tWall, crashWalls)) { WallConnecting(crashPoints[0], crashPoints[1], tWall); return false; } return true; } DisconnectLine(tWall); walls.Remove(tWall); tWall.Release(); var newWalls = new List(); for (int i = 0; i < crashPoints.Count - 1; i++) { var newLine = CreateWall(crashPoints[i], crashPoints[i + 1]); WallConnecting(newLine); newWalls.Add(newLine); } foreach (var newWall in newWalls) { RemoveCrashWall(newWall, crashWalls); WallConnecting(newWall); tWall = newWall; } return false; } private bool RemoveCrashWall(Wall newWall, List crashWalls) { var newLcp = newWall.leftCenterPoint; var newRcp = newWall.rightCenterPoint; foreach (var crashWall in crashWalls) { if (newWall == crashWall) continue; var crashLcp = crashWall.leftCenterPoint; var crashRcp = crashWall.rightCenterPoint; if (newLcp.Equals(crashLcp) && newRcp.Equals(crashRcp)) { DisconnectLine(crashWall); walls.Remove(crashWall); crashWall.Release(); return false; } else if (newLcp.Equals(crashRcp) && newRcp.Equals(crashLcp)) { DisconnectLine(crashWall); walls.Remove(crashWall); crashWall.Release(); return false; } } return true; } void CalculateLineCrossing(Wall target) { var lps = target.GetCenterPointPositions(); foreach (var wall2 in walls) { if (target == wall2) continue; var lps2 = wall2.GetCenterPointPositions(); Vector3 cp = Vector3.zero; if (!Wathf.GetCrossVector(lps, lps2, ref cp)) continue; var clp = CreateLinePoint(cp); if (target.includedPoints.Add(clp)) { pointAddWalls.Add(target); } if (wall2.includedPoints.Add(clp)) { pointAddWalls.Add(wall2); } pointToWall[clp].Add(target); pointToWall[clp].Add(wall2); //connectLines[clp].Add(target); //connectLines[clp].Add(wall2); addPoints.Add(clp); } } private void CrossLineDivide() { //ũ�ν��� ���� ������ var newLines = new List(); foreach (var wall in pointAddWalls) { wall.Clear(); var lcp = wall.leftCenterPoint; var rcp = wall.rightCenterPoint; LinePoint curr; LinePoint prev; var crossPoints = wall.includedPoints. OrderBy(l => Vector3.Distance(l.position, lcp.position)).ToList(); DisconnectLine(wall); for (int i = 0; i < crossPoints.Count - 1; i++) { prev = crossPoints[i]; curr = crossPoints[i + 1]; Wall target = null; if (i == 0) { target = wall; addPoints.Add(prev); } else { target = CreateWall(prev, curr); wall.includedPoints.Remove(curr); //connectLines[curr].Remove(wall); pointToWall[curr].Remove(wall); } newLines.Add(target); addPoints.Add(curr); WallConnecting(prev, curr, target); } } foreach (var addWall in newLines) { walls.Add(addWall); } //crossPointTable.Clear(); pointAddWalls.Clear(); } #endregion #region Group private void Grouping() { cPoints = new(linePoints); if (cPoints.Count == 0) { foreach (var removeGroup in wallgroups) { removeGroup.Release(); } groupTable.Clear(); wallgroups.Clear(); return; } var groupList = new List(); var removeGroups = new List(); PointsGroup(ref groupList); int count = wallgroups.Count - groupList.Count; if (count > 0) { for (int i = wallgroups.Count - 1; i == groupList.Count; i--) { removeGroups.Add(wallgroups[i]); groupTable.Remove(wallgroups[i]); wallgroups.Remove(wallgroups[i]); } } else if (count < 0) { for (int i = 0; i < Mathf.Abs(count); i++) { CreateWallGroup(); } } int index = 0; foreach (var group in wallgroups) { foreach (var linepoint in groupList[index].points) { linepoint.transform.SetParent(group.linepoints); } group.groupWalls = groupList[index].wallLines.ToHashSet(); foreach (var wall in groupList[index].wallLines) { wall.transform.SetParent(group.walls); } group.groupPoints = groupList[index].points.ToHashSet(); groupTable[group] = groupList[index].points; index++; } foreach (var removeGroup in removeGroups) { removeGroup.Release(); } } private WallGroup CreateWallGroup() { var group = Instantiate(prf_WallGroup); groupTable.Add(group, new()); wallgroups.Add(group); group.onRemove = onRemoveWallGroupEvent; return group; } private void ContainGroup(LinePoint point, Group wallgroup) { if (wallgroup.points.Contains(point)) return; wallgroup.points.Add(point); foreach (var line in connectLines[point]) { wallgroup.AddWallLine(line); } } private void AddPointGroup(LinePoint point, Group wallgroup) { if (wallgroup.points.Contains(point)) { cPoints.Remove(point); return; } wallgroup.points.Add(point); foreach (var line in connectLines[point]) { wallgroup.AddWallLine(line); } cPoints.Remove(point); var points = point.connectPoints; foreach (var cp in points) { if (cp.Equals(point)) { ContainGroup(cp, wallgroup); continue; } AddPointGroup(cp, wallgroup); } } void PointsGroup(ref List groupList) { var wallGroup = new Group(); AddPointGroup(cPoints[0], wallGroup); groupList.Add(wallGroup); if (cPoints.Count != 0) { PointsGroup(ref groupList); } } #endregion #region Ray cast Events bool onStayMap; bool onClickMap; bool onClickPoint; Vector3 hitPos; void UpdateLoop() { //DrawWall끝나고 Mesh 계산하는 부분이 빠져서 수정해야함. if (drawState == BuilderState.Drawing) { if (onStayMap) { if (!onClickMap && onClickPoint) { } else if (onClickMap && !onClickPoint) { //OverLap 검사? BuildWall(hitPos); PointsConnectLineCalculate(); } } } } internal void OnStayMap(RaycastHit hit, Component component) { Debug.Log($"OnStayMap"); if (!drawState.Equals(BuilderState.Drawing)) return; onStayMap = true; hitPos = new Vector3(hit.point.x, hit.point.y, hit.point.z); handlePoint.transform.position = hitPos; if (tempWall != null) { tempWall.PreviewLineUpdate(); } } internal void OnClickLinePoint(RaycastHit hit, Component component) { if (drawState.Equals(BuilderState.Drawing)) return; var tempPoint = component as LinePoint; onClickPoint = true; clickedPoint = tempPoint; switch (drawState) { case BuilderState.PointModfiy: break; case BuilderState.PointRemove: RemoveSelectedPoint(clickedPoint); break; } } internal void OnClickMap(RaycastHit hit, Component component) { if (EventSystem.current.currentSelectedGameObject != null) return; Debug.Log("OnClickMap"); onClickMap = true; } #endregion private void DisconnectLine(Wall removeWall) { var p1 = removeWall.leftCenterPoint; var p2 = removeWall.rightCenterPoint; pointToWall[p1].Remove(removeWall); pointToWall[p2].Remove(removeWall); // connectLines[p1].Remove(removeWall); // connectLines[p2].Remove(removeWall); } private void WallConnecting(Wall newWall) { newWall.PreviewLineUpdate(); //connectLines[newWall.leftCenterPoint].Add(newWall); //connectLines[newWall.rightCenterPoint].Add(newWall); pointToWall[newWall.leftCenterPoint].Add(newWall); pointToWall[newWall.rightCenterPoint].Add(newWall); foreach (var wallgroup in wallgroups) { if (wallgroup.ContainPointInWallGroup(newWall)) { wallgroup.AddWall(newWall); break; } } } private void WallConnecting(LinePoint p1, LinePoint p2, Wall newWall) { newWall.PreviewLineUpdate(); pointToWall[p1].Add(newWall); pointToWall[p2].Add(newWall); //connectLines[p1].Add(newWall); //connectLines[p2].Add(newWall); foreach (var wallgroup in wallgroups) { if (wallgroup.ContainPointInWallGroup(newWall)) { wallgroup.AddWall(newWall); break; } } } private void ConnectWallReSetting(LinePoint point) { foreach (var reviseWall in connectLines[point]) { reviseWall.EdgeRecalculate(); var centerPoints = reviseWall.GetCenterPoints(); foreach (var pp in centerPoints) { foreach (var wall in connectLines[pp]) { wall.Clear(); foreach (var subLine in connectLines[pp]) { if (wall == subLine) continue; wall.CalculateCrossing(subLine); subLine.CalculateCrossing(wall); } } edgeTable.Clear(); foreach (var wall in connectLines[pp]) { foreach (var crossLine in connectLines[pp]) { if (wall == crossLine) continue; VirtualEdgePoint(wall, crossLine); } } } } addPoints.Clear(); } private void PointsConnectLineCalculate() { foreach (var pp in addPoints) { ConnectLineVertexCalculate(pp); } addPoints.Clear(); } private void ConnectLineVertexCalculate(LinePoint pp) { foreach (var wall in pointToWall[pp]) //connectLines[pp] { wall.Clear(); foreach (var subLine in pointToWall[pp]) //connectLines[pp] { if (wall == subLine) continue; wall.CalculateCrossing(subLine); subLine.CalculateCrossing(wall); } } edgeTable.Clear(); foreach (var wall in pointToWall[pp]) //connectLines[pp] { foreach (var crossLine in pointToWall[pp]) //connectLines[pp] { if (wall == crossLine) continue; VirtualEdgePoint(wall, crossLine); } } } void VirtualEdgePoint(Wall wall1, Wall wall2) { if (wall2 == wall1) return; if (edgeTable.Contains((wall1, wall2))) return; if (edgeTable.Contains((wall2, wall1))) return; VirtualPoint[] newLineVP = new VirtualPoint[2]; VirtualPoint[] crossLineVP = new VirtualPoint[2]; if (!wall1.RefreshVirtualPoint(VirtualPoint.LeftTop, VirtualPoint.LeftBottom, ref newLineVP)) { return; } wall2.RefreshVirtualPoint(VirtualPoint.LeftTop, VirtualPoint.LeftBottom, ref crossLineVP); var crossPoint = Vector3.zero; var mVL = wall1.GetApplyPointPositions(newLineVP); var sVR = wall2.GetApplyPointPositions(crossLineVP); if (Wathf.GetCrossVector(mVL, sVR, ref crossPoint)) { if (wall1.NearVritualEdgePoint(crossPoint, newLineVP[0])) { edgeTable.Add((wall1, wall2)); } if (wall2.NearVritualEdgePoint(crossPoint, crossLineVP[0])) { edgeTable.Add((wall2, wall1)); } } } InputHandler handler; void MouseUp() { onStayMap = false; onClickMap = false; onClickPoint = false; Debug.Log("Mouse Up"); } public InputHandler GetInputHandler() { Dictionary downKeyActions = new(); downKeyActions.Add(KeyCode.Q, DrawEnd); downKeyActions.Add(KeyCode.Mouse0, Pointing); Dictionary upKeyActions = new(); upKeyActions.Add(KeyCode.Mouse0, MouseUp); InputHandler result = new InputHandler(null, downKeyActions, upKeyActions,null, UpdateLoop); return result; } public void CurrentStatusEvent() { var raycaster = FindSingle(); raycaster.AddEvent(Raycaster.EventType.FirstStay, typeof (Map), OnStayMap); raycaster.AddEvent(Raycaster.EventType.FirstClick, typeof(Map), OnClickMap); raycaster.AddEvent(Raycaster.EventType.FirstClick, typeof(LinePoint), OnClickLinePoint); FindSingle().SetHandler(handler); } public void PrevStatusEvent() { var raycaster = FindSingle(); raycaster.RemoveEvent(Raycaster.EventType.FirstStay, typeof(Map), OnStayMap); raycaster.RemoveEvent(Raycaster.EventType.FirstClick, typeof(Map), OnClickMap); raycaster.RemoveEvent(Raycaster.EventType.FirstClick, typeof(LinePoint), OnClickLinePoint); FindSingle().RemoveHandler(handler); } void Pointing() { } void DrawEnd() { Debug.Log($"DrawEnd"); if (handlePoint != null) { linePoints.Remove(handlePoint); pointToWall.Remove(handlePoint); handlePoint.Release(); Destroy(handlePoint.gameObject); } if (pilePoint != null) { if (pilePoint.connectPoints.Count == 0) { linePoints.Remove(pilePoint); pointToWall.Remove(pilePoint); pilePoint.Release(); } pilePoint = null; } if (tempWall != null) { walls.Remove(tempWall); tempWall.Release(); Destroy(tempWall.gameObject); } tempWall = null; handlePoint = null; clickedPoint = null; SetDrawState(BuilderState.None); } #if UNITY_EDITOR public float gizmoTh; public float gizmosp; private void OnDrawGizmos() { foreach (var wall in walls) { var lfps = wall.GetLeftLinePointPositions(); var rfps = wall.GetRightLinePointPositions(); var cfps = wall.GetCenterPointPositions(); Handles.color = Color.yellow; Handles.DrawLine(lfps[0], lfps[1], gizmoTh); Handles.color = Color.red; Handles.DrawLine(rfps[0], rfps[1], gizmoTh); Handles.color = Color.white; Handles.DrawLine(cfps[0], cfps[1], gizmoTh); Gizmos.color = Color.blue; Gizmos.DrawSphere(lfps[0], gizmosp); Gizmos.DrawSphere(lfps[1], gizmosp); Gizmos.color = Color.green; Gizmos.DrawSphere(rfps[0], gizmosp); Gizmos.DrawSphere(rfps[1], gizmosp); } if (pilePoint != null) { Gizmos.color = Color.cyan; Gizmos.DrawSphere(pilePoint.position, 0.1f); } if (clickedPoint != null) { Gizmos.color = Color.white; Gizmos.DrawSphere(clickedPoint.position, 0.1f); } Handles.color = Color.white; GUIStyle gs = new GUIStyle(); gs.fontStyle = FontStyle.Bold; gs.fontSize = 20; int i = 0; foreach(var l in linePoints) { Handles.Label(l.position, $"{i++}", gs); } } #endif } }