using System; using UnityEngine; using UnityEngine.Tilemaps; using UnityEngine.UIElements; namespace UnityEditor.Tilemaps { internal class PaintableSceneViewGrid : PaintableGrid { private Transform gridTransform { get { return grid != null ? grid.transform : null; } } private Grid grid { get { return brushTarget != null ? brushTarget.GetComponentInParent() : (Selection.activeGameObject != null ? Selection.activeGameObject.GetComponentInParent() : null); } } private GridBrushBase gridBrush { get { return GridPaintingState.gridBrush; } } private SceneView activeSceneView; private int sceneViewTransformHash; private GameObject brushTarget { get { return GridPaintingState.scenePaintTarget; } } public Tilemap tilemap { get { if (brushTarget != null) { return brushTarget.GetComponent(); } return null; } } public Action onEdited; protected override void OnEnable() { base.OnEnable(); SceneView.duringSceneGui += OnSceneGUI; Undo.undoRedoPerformed += UndoRedoPerformed; GridSelection.gridSelectionChanged += OnGridSelectionChanged; } protected override void OnDisable() { SceneView.duringSceneGui -= OnSceneGUI; Undo.undoRedoPerformed -= UndoRedoPerformed; GridSelection.gridSelectionChanged -= OnGridSelectionChanged; base.OnDisable(); } private void OnGridSelectionChanged() { SceneView.RepaintAll(); } private Rect GetSceneViewPositionRect(SceneView sceneView) { return new Rect(0, 0, sceneView.position.width, sceneView.position.height); } public void OnSceneGUI(SceneView sceneView) { HandleMouseEnterLeave(sceneView); CallOnSceneGUI(); // Case 1093801: Handle only the currently active scene view if (sceneView != activeSceneView) return; // Case 1077400: SceneView camera transform changes may update the mouse grid position even though the mouse position has not changed var currentSceneViewTransformHash = sceneView.camera.transform.localToWorldMatrix.GetHashCode(); UpdateMouseGridPosition(currentSceneViewTransformHash != sceneViewTransformHash); sceneViewTransformHash = currentSceneViewTransformHash; var dot = 1.0f; var gridView = GetGridView(); if (gridView != null) { dot = Math.Abs(Vector3.Dot(sceneView.camera.transform.forward, Grid.Swizzle(gridView.cellSwizzle, gridView.transform.forward))); } // Case 1021655: Validate that grid is not totally parallel to view (+-5 degrees), otherwise tiles could be accidentally painted on large positions if (dot > 0.1f) { base.OnGUI(); if (InGridEditMode()) { if ((grid != null) && (GridPaintingState.activeGrid == this || GridSelection.active)) { CallOnPaintSceneGUI(); } if (Event.current.type == EventType.Repaint) EditorGUIUtility.AddCursorRect(GetSceneViewPositionRect(sceneView), MouseCursor.CustomCursor); } } } private void HandleMouseEnterLeave(SceneView sceneView) { if (GridPaintingState.isEditing) { if (Event.current.type == EventType.MouseEnterWindow) { OnMouseEnter(sceneView); } else if (Event.current.type == EventType.MouseLeaveWindow) { OnMouseLeave(); } // Case 1043365: When docked, the docking area is considered part of the window and MouseEnter/LeaveWindow events are not considered when entering the docking area else if (sceneView.docked) { var guiPoint = Event.current.mousePosition; var sceneViewPosition = GetSceneViewPositionRect(sceneView); if (sceneViewPosition.Contains(guiPoint)) { if (GridPaintingState.activeGrid != this) { OnMouseEnter(sceneView); } } else if (activeSceneView == sceneView) { if (GridPaintingState.activeGrid == this) { OnMouseLeave(); } } } } } private void OnMouseEnter(SceneView sceneView) { if (GridPaintingState.activeBrushEditor != null) GridPaintingState.activeBrushEditor.OnMouseEnter(); activeSceneView = sceneView; GridPaintingState.AddActiveGrid(this); UpdateMouseGridPosition(true); ResetPreviousMousePositionToCurrentPosition(); } private void OnMouseLeave() { if (GridPaintingState.activeBrushEditor != null) GridPaintingState.activeBrushEditor.OnMouseLeave(); GridPaintingState.RemoveActiveGrid(this); activeSceneView = null; } private void UndoRedoPerformed() { RefreshAllTiles(); } private void RefreshAllTiles() { if (tilemap != null) tilemap.RefreshAllTiles(); } protected override void RegisterUndo() { if (GridPaintingState.activeBrushEditor != null) { GridPaintingState.activeBrushEditor.RegisterUndo(brushTarget, EditTypeToBrushTool(EditorTools.ToolManager.activeToolType)); } } protected override void Paint(Vector3Int position) { if (grid != null) gridBrush.Paint(grid, brushTarget, position); } protected override void Erase(Vector3Int position) { if (grid != null) gridBrush.Erase(grid, brushTarget, position); } protected override void BoxFill(BoundsInt position) { if (grid != null) gridBrush.BoxFill(grid, brushTarget, position); } protected override void BoxErase(BoundsInt position) { if (grid != null) gridBrush.BoxErase(grid, brushTarget, position); } protected override void FloodFill(Vector3Int position) { if (grid != null) gridBrush.FloodFill(grid, brushTarget, position); } protected override void PickBrush(BoundsInt position, Vector3Int pickStart) { if (grid != null) gridBrush.Pick(grid, brushTarget, position, pickStart); } protected override void Select(BoundsInt position) { if (grid != null) { GridSelection.Select(brushTarget, position); gridBrush.Select(grid, brushTarget, position); } } protected override void Move(BoundsInt from, BoundsInt to) { if (grid != null) gridBrush.Move(grid, brushTarget, from, to); } protected override void MoveStart(BoundsInt position) { if (grid != null) gridBrush.MoveStart(grid, brushTarget, position); } protected override void MoveEnd(BoundsInt position) { if (grid != null) gridBrush.MoveEnd(grid, brushTarget, position); } protected override bool CustomTool(bool isToolHotControl, TilemapEditorTool tool, Vector3Int position) { var executed = false; if (grid != null) { executed = tool.HandleTool(isToolHotControl, grid, brushTarget, position); } return executed; } protected override void OnEditStart() { if (GridPaintingState.activeBrushEditor != null && grid != null) GridPaintingState.activeBrushEditor.OnEditStart(grid, brushTarget); onEdited?.Invoke(brushTarget); } protected override void OnEditEnd() { if (GridPaintingState.activeBrushEditor != null && grid != null) GridPaintingState.activeBrushEditor.OnEditEnd(grid, brushTarget); } protected override void ClearGridSelection() { GridSelection.Clear(); } public override bool isActive => grid != null; public override Rect rectPosition => activeSceneView != null ? activeSceneView.rootVisualElement.worldBound : Rect.zero; public override VisualElement windowRoot => activeSceneView != null ? activeSceneView.rootVisualElement.GetRoot() : null; public override void Repaint() { SceneView.RepaintAll(); } protected override bool ValidateFloodFillPosition(Vector3Int position) { return true; } protected override Vector2Int ScreenToGrid(Vector2 screenPosition, float zPosition) { if (tilemap != null) { var transform = tilemap.transform; var plane = new Plane(GetGridForward(tilemap), transform.position); var screenLocal = GridEditorUtility.ScreenToLocal(transform, screenPosition, plane); if (GridPaintingState.gridBrushMousePositionAtZ) screenLocal.z = zPosition; var cell = LocalToGrid(tilemap, screenLocal); return new Vector2Int(cell.x, cell.y); } if (grid != null) { var screenLocal = GridEditorUtility.ScreenToLocal(gridTransform, screenPosition, GetGridPlane(grid)); if (GridPaintingState.gridBrushMousePositionAtZ) screenLocal.z = zPosition; var cell = LocalToGrid(grid, screenLocal); return new Vector2Int(cell.x, cell.y); } return Vector2Int.zero; } protected override bool PickingIsDefaultTool() { return false; } protected override bool CanPickOutsideEditMode() { return false; } protected override GridLayout.CellLayout CellLayout() { return grid.cellLayout; } Vector3Int LocalToGrid(GridLayout gridLayout, Vector3 local) { return gridLayout.LocalToCell(local); } private Vector3 GetGridForward(GridLayout gridLayout) { switch (gridLayout.cellSwizzle) { case GridLayout.CellSwizzle.XYZ: return gridLayout.transform.forward * -1f; case GridLayout.CellSwizzle.XZY: return gridLayout.transform.up * -1f; case GridLayout.CellSwizzle.YXZ: return gridLayout.transform.forward; case GridLayout.CellSwizzle.YZX: return gridLayout.transform.up; case GridLayout.CellSwizzle.ZXY: return gridLayout.transform.right; case GridLayout.CellSwizzle.ZYX: return gridLayout.transform.right * -1f; } return gridLayout.transform.forward * -1f; } private Plane GetGridPlane(Grid planeForGrid) { return new Plane(GetGridForward(planeForGrid), planeForGrid.transform.position); } private GridLayout GetGridView() { if (tilemap != null) return tilemap; if (grid != null) return grid; return null; } void CallOnPaintSceneGUI() { bool hasSelection = GridSelection.active && GridSelection.target == brushTarget; if (!hasSelection && GridPaintingState.activeGrid != this) return; RectInt rect = new RectInt(mouseGridPosition, new Vector2Int(1, 1)); if (m_MarqueeStart.HasValue) rect = GridEditorUtility.GetMarqueeRect(mouseGridPosition, m_MarqueeStart.Value); else if (hasSelection) rect = new RectInt(GridSelection.position.xMin, GridSelection.position.yMin, GridSelection.position.size.x, GridSelection.position.size.y); var layoutGrid = tilemap != null ? tilemap.layoutGrid : grid as GridLayout; BoundsInt brushBounds = new BoundsInt(new Vector3Int(rect.x, rect.y, zPosition), new Vector3Int(rect.width, rect.height, 1)); if (GridPaintingState.activeBrushEditor != null) { GridPaintingState.activeBrushEditor.OnPaintSceneGUI(layoutGrid, brushTarget, brushBounds , EditTypeToBrushTool(EditorTools.ToolManager.activeToolType), m_MarqueeStart.HasValue || executing); } else // Fallback when user hasn't defined custom editor { GridBrushEditorBase.OnPaintSceneGUIInternal(layoutGrid, brushTarget, brushBounds , EditTypeToBrushTool(EditorTools.ToolManager.activeToolType), m_MarqueeStart.HasValue || executing); } } void CallOnSceneGUI() { var gridLayout = tilemap != null ? tilemap : grid as GridLayout; bool hasSelection = GridSelection.active && GridSelection.target == brushTarget; if (GridPaintingState.activeBrushEditor != null) { GridPaintingState.activeBrushEditor.OnSceneGUI(gridLayout, brushTarget); if (hasSelection) { GridPaintingState.activeBrushEditor.OnSelectionSceneGUI(gridLayout, brushTarget); } } if (hasSelection) { RectInt rect = new RectInt(GridSelection.position.xMin, GridSelection.position.yMin, GridSelection.position.size.x, GridSelection.position.size.y); BoundsInt brushBounds = new BoundsInt(new Vector3Int(rect.x, rect.y, zPosition), new Vector3Int(rect.width, rect.height, 1)); GridBrushEditorBase.OnSceneGUIInternal(gridLayout, brushTarget, brushBounds , EditTypeToBrushTool(EditorTools.ToolManager.activeToolType), m_MarqueeStart.HasValue || executing); } } } }