using Pinwheel.Griffin.BackupTool; using System.Collections.Generic; using UnityEditor; using UnityEngine; using Enum = System.Enum; using Type = System.Type; namespace Pinwheel.Griffin.PaintTool { [CustomEditor(typeof(GTerrainTexturePainter))] public class GTerrainPainterInspector : Editor { private GTerrainTexturePainter painter; private Vector3[] worldPoints = new Vector3[4]; private Vector2[] normalizedPoints = new Vector2[4]; private void OnEnable() { Undo.undoRedoPerformed += OnUndoRedo; SceneView.duringSceneGui += DuringSceneGUI; LoadPrefs(); painter = (GTerrainTexturePainter)target; Tools.hidden = true; GCommon.UpdateMaterials(painter.GroupId); GCommon.RegisterBeginRender(OnBeginRender); GCommon.RegisterBeginRenderSRP(OnBeginRenderSRP); } private void OnDisable() { Undo.undoRedoPerformed -= OnUndoRedo; SceneView.duringSceneGui -= DuringSceneGUI; SavePrefs(); Tools.hidden = false; GCommon.UnregisterBeginRender(OnBeginRender); GCommon.UnregisterBeginRenderSRP(OnBeginRenderSRP); GTerrainTexturePainter.Internal_ReleaseRenderTextures(); } private void OnUndoRedo() { if (Selection.activeGameObject != painter.gameObject) return; if (string.IsNullOrEmpty(GUndoCompatibleBuffer.Instance.CurrentBackupName)) return; GBackup.Restore(GUndoCompatibleBuffer.Instance.CurrentBackupName); } private void SavePrefs() { } private void LoadPrefs() { } public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); painter.GroupId = GEditorCommon.ActiveTerrainGroupPopupWithAllOption("Group Id", painter.GroupId); painter.ForceUpdateGeometry = EditorGUILayout.Toggle("Force Update Geometry", painter.ForceUpdateGeometry); painter.Editor_EnableHistory = EditorGUILayout.Toggle("Enable History", painter.Editor_EnableHistory); painter.Editor_EnableLivePreview = EditorGUILayout.Toggle("Enable Live Preview", painter.Editor_EnableLivePreview); bool isMaskPainterInUsed = painter.ActivePainter is GMaskPainter; if (!isMaskPainterInUsed) { painter.EnableTerrainMask = EditorGUILayout.Toggle("Enable Terrain Mask", painter.EnableTerrainMask); } GEditorSettings.Instance.topographic.enable = EditorGUILayout.Toggle("Enable Topographic", GEditorSettings.Instance.topographic.enable); DrawPaintMode(); IGTexturePainter p = painter.ActivePainter; if (p == null) { EditorGUILayout.LabelField("No painter found!", GEditorCommon.WordWrapItalicLabel); } else { DrawInstructionGUI(); DrawBrushMaskGUI(); if (painter.Mode == GTexturePaintingMode.Splat || painter.Mode == GTexturePaintingMode.Custom) DrawSplatGUI(); DrawBrushGUI(); if (painter.ActivePainter != null && painter.ActivePainter is IGTexturePainterWithCustomParams) { IGTexturePainterWithCustomParams activePainter = painter.ActivePainter as IGTexturePainterWithCustomParams; activePainter.Editor_DrawCustomParamsGUI(); } #if GRIFFIN_VEGETATION_STUDIO_PRO GEditorCommon.DrawVspIntegrationGUI(); #endif GEditorCommon.DrawBackupHelpBox(); } if (EditorGUI.EndChangeCheck()) { GUtilities.MarkCurrentSceneDirty(); } } public override bool RequiresConstantRepaint() { return false; } private void DrawInstructionGUI() { string label = "Instruction"; string id = "instruction" + painter.GetInstanceID().ToString(); GEditorCommon.Foldout(label, true, id, () => { string text = null; try { text = painter.ActivePainter.Instruction; } catch (System.Exception e) { text = e.Message; } EditorGUILayout.LabelField(text, GEditorCommon.WordWrapItalicLabel); }); } private void DrawBrushMaskGUI() { string label = "Brush Masks"; string id = "mask" + painter.GetInstanceID().ToString(); GenericMenu menu = new GenericMenu(); menu.AddItem( new GUIContent("New Brush"), false, DisplayNewBrushDialog); menu.AddItem( new GUIContent("Refresh"), false, painter.ReloadBrushMasks); GEditorCommon.Foldout(label, true, id, () => { GSelectionGridArgs args = new GSelectionGridArgs(); args.selectedIndex = painter.SelectedBrushMaskIndex; args.collection = painter.BrushMasks; args.itemSize = GEditorCommon.selectionGridTileSizeSmall; args.itemPerRow = 4; args.drawPreviewFunction = DrawBrushMaskPreview; painter.SelectedBrushMaskIndex = GEditorCommon.SelectionGrid(args); }, menu); } private void DisplayNewBrushDialog() { EditorUtility.DisplayDialog( "Info", "To add a new brush, copy your brush texture to a Resources/PolarisBrushes/ folder, then Refresh.", "OK"); } private void DrawBrushMaskPreview(Rect r, object o) { Texture2D tex = (Texture2D)o; if (tex != null) { EditorGUI.DrawPreviewTexture(r, tex); } else { EditorGUI.DrawRect(r, Color.black); } } private void DrawSplatGUI() { string label = "Splats"; string id = "splats" + painter.GetInstanceID().ToString(); GEditorCommon.Foldout(label, true, id, () => { if (GEditorSettings.Instance.paintTools.useMultiSplatsSelector) { painter.SelectedSplatIndices = GEditorCommon.SplatSetMultiSelectionGrid(painter.GroupId, painter.SelectedSplatIndices); } else { painter.SelectedSplatIndex = GEditorCommon.SplatSetSelectionGrid(painter.GroupId, painter.SelectedSplatIndex); } GEditorSettings.Instance.paintTools.useMultiSplatsSelector = EditorGUILayout.Toggle("Multi Selection", GEditorSettings.Instance.paintTools.useMultiSplatsSelector); EditorUtility.SetDirty(GEditorSettings.Instance); EditorGUILayout.Space(); }); } private void DrawBrushGUI() { string label = "Brush"; string id = "brush" + painter.GetInstanceID().ToString(); GEditorCommon.Foldout(label, true, id, () => { painter.BrushRadius = EditorGUILayout.FloatField("Radius", painter.BrushRadius); painter.BrushRadiusJitter = EditorGUILayout.Slider("Jitter", painter.BrushRadiusJitter, 0f, 1f); GEditorCommon.Separator(); painter.BrushRotation = EditorGUILayout.Slider("Rotation", painter.BrushRotation, -360f, 360f); painter.BrushRotationJitter = EditorGUILayout.Slider("Jitter", painter.BrushRotationJitter, 0f, 1f); GEditorCommon.Separator(); Rect r0 = EditorGUILayout.GetControlRect(); if (painter.BrushOpacity <= 0 || painter.BrushOpacity >= 1) { EditorGUI.DrawRect(r0, GEditorCommon.WarningLabel.normal.textColor); } painter.BrushOpacity = EditorGUI.Slider(r0, "Opacity", painter.BrushOpacity, 0f, 1f); painter.BrushOpacityJitter = EditorGUILayout.Slider("Jitter", painter.BrushOpacityJitter, 0f, 1f); Rect r1 = EditorGUILayout.GetControlRect(); if (painter.BrushTargetStrength <= 0) { EditorGUI.DrawRect(r1, GEditorCommon.WarningLabel.normal.textColor); } painter.BrushTargetStrength = EditorGUI.Slider(r1, "Target Strength", painter.BrushTargetStrength, 0f, 1f); GEditorCommon.Separator(); painter.BrushScatter = EditorGUILayout.Slider("Scatter", painter.BrushScatter, 0f, 1f); painter.BrushScatterJitter = EditorGUILayout.Slider("Jitter", painter.BrushScatterJitter, 0f, 1f); GEditorCommon.Separator(); painter.BrushColor = EditorGUILayout.ColorField("Color", painter.BrushColor); painter.SamplePoint = GEditorCommon.InlineVector3Field("Sample Point", painter.SamplePoint); }); } private void DuringSceneGUI(SceneView sv) { HandleTerrainEditingInSceneView(); HandleBrushSettingsShortcuts(); HandleFunctionKeys(); if (Event.current != null && Event.current.type == EventType.MouseMove) SceneView.RepaintAll(); } private void HandleBrushSettingsShortcuts() { if (Event.current != null && Event.current.isKey) { KeyCode k = Event.current.keyCode; if (k == KeyCode.Equals) { painter.BrushRadius += GEditorSettings.Instance.paintTools.radiusStep; } else if (k == KeyCode.Minus) { painter.BrushRadius -= GEditorSettings.Instance.paintTools.radiusStep; } else if (k == KeyCode.RightBracket) { painter.BrushRotation += GEditorSettings.Instance.paintTools.rotationStep; } else if (k == KeyCode.LeftBracket) { painter.BrushRotation -= GEditorSettings.Instance.paintTools.rotationStep; } else if (k == KeyCode.Quote) { painter.BrushOpacity += GEditorSettings.Instance.paintTools.opacityStep; } else if (k == KeyCode.Semicolon) { painter.BrushOpacity -= GEditorSettings.Instance.paintTools.opacityStep; } } } private void HandleFunctionKeys() { if (Event.current != null && Event.current.type == EventType.KeyDown) { KeyCode k = Event.current.keyCode; if (k >= KeyCode.F1 && k <= KeyCode.F12) { if (painter.Mode != GTexturePaintingMode.Custom && (k - KeyCode.F1) != (int)GTexturePaintingMode.Custom) { painter.Mode = (GTexturePaintingMode)(k - KeyCode.F1); } else { painter.CustomPainterIndex = k - KeyCode.F1; } } } } private void HandleTerrainEditingInSceneView() { if (!painter.enabled) return; if (Event.current == null) return; if (Event.current.alt == true) return; if (Event.current.type != EventType.Repaint && Event.current.type != EventType.MouseDown && Event.current.type != EventType.MouseDrag && Event.current.type != EventType.MouseUp && Event.current.type != EventType.KeyDown) return; int controlId = GUIUtility.GetControlID(this.GetHashCode(), FocusType.Passive); if (Event.current.type == EventType.MouseDown) { if (Event.current.button == 0) { //Set the hot control to this tool, to disable marquee selection tool on mouse dragging GUIUtility.hotControl = controlId; } } else if (Event.current.type == EventType.MouseUp) { GRuntimeSettings.Instance.isEditingGeometry = false; if (GUIUtility.hotControl == controlId) { //Return the hot control back to Unity, use the default GUIUtility.hotControl = 0; } } Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit; if (GStylizedTerrain.Raycast(r, out hit, float.MaxValue, painter.GroupId)) { OnRaycast(true, hit); } else { OnRaycast(false, hit); } if (GGuiEventUtilities.IsLeftMouse) { Event.current.Use(); } } private void OnRaycast(bool isHit, RaycastHit hitInfo) { if (isHit) { if (painter.Editor_EnableLivePreview && painter.ActivePainter != null && painter.ActivePainter is IGTexturePainterWithLivePreview) { //DrawLivePreview(hitInfo); } else { DrawHandleAtCursor(hitInfo); } if (GGuiEventUtilities.IsLeftMouse) { Paint(hitInfo); } } } private GTexturePainterArgs CreateBasicArgs(RaycastHit hit) { GTexturePainterArgs args = new GTexturePainterArgs(); args.HitPoint = hit.point; args.MouseEventType = Event.current.type == EventType.MouseDown ? GPainterMouseEventType.Down : Event.current.type == EventType.MouseDrag ? GPainterMouseEventType.Drag : GPainterMouseEventType.Up; args.ActionType = Event.current.shift ? GPainterActionType.Alternative : Event.current.control ? GPainterActionType.Negative : GPainterActionType.Normal; return args; } private void Paint(RaycastHit hit) { GTexturePainterArgs args = CreateBasicArgs(hit); painter.Paint(args); } private void DrawHandleAtCursor(RaycastHit hit) { Color cursorColor = Event.current.shift ? GEditorSettings.Instance.paintTools.alternativeActionCursorColor : Event.current.control ? GEditorSettings.Instance.paintTools.negativeActionCursorColor : GEditorSettings.Instance.paintTools.normalActionCursorColor; cursorColor.a = cursorColor.a * Mathf.Lerp(0.5f, 1f, painter.BrushOpacity); if (GEditorSettings.Instance.paintTools.useSimpleCursor) { Handles.color = cursorColor; Vector3[] corner = GCommon.GetBrushQuadCorners(hit.point, painter.BrushRadius, painter.BrushRotation); Handles.DrawAAPolyLine(5, corner[0], corner[1], corner[2], corner[3], corner[0]); } else { Matrix4x4 cursorToWorld = Matrix4x4.TRS(hit.point, Quaternion.Euler(0, painter.BrushRotation, 0), 2 * painter.BrushRadius * Vector3.one); worldPoints[0] = cursorToWorld.MultiplyPoint(new Vector3(-0.5f, 0, -0.5f)); worldPoints[1] = cursorToWorld.MultiplyPoint(new Vector3(-0.5f, 0, 0.5f)); worldPoints[2] = cursorToWorld.MultiplyPoint(new Vector3(0.5f, 0, 0.5f)); worldPoints[3] = cursorToWorld.MultiplyPoint(new Vector3(0.5f, 0, -0.5f)); Material mat = GInternalMaterials.PainterCursorProjectorMaterial; mat.SetColor("_Color", cursorColor); mat.SetTexture("_MainTex", painter.BrushMasks[painter.SelectedBrushMaskIndex]); mat.SetMatrix("_WorldToCursorMatrix", cursorToWorld.inverse); mat.SetPass(0); IEnumerator terrains = GStylizedTerrain.ActiveTerrains.GetEnumerator(); while (terrains.MoveNext()) { GStylizedTerrain t = terrains.Current; if (t.TerrainData == null) continue; if (painter.GroupId >= 0 && painter.GroupId != t.GroupId) continue; DrawCursorProjected(t); } } } private void DrawCursorProjected(GStylizedTerrain t) { for (int i = 0; i < normalizedPoints.Length; ++i) { normalizedPoints[i] = t.WorldPointToUV(worldPoints[i]); } Rect dirtyRect = GUtilities.GetRectContainsPoints(normalizedPoints); GTerrainChunk[] chunks = t.GetChunks(); for (int i = 0; i < chunks.Length; ++i) { Rect uvRect = chunks[i].GetUvRange(); if (!uvRect.Overlaps(dirtyRect)) continue; Mesh m = chunks[i].MeshFilterComponent.sharedMesh; if (m == null) continue; Graphics.DrawMeshNow( m, Matrix4x4.TRS(chunks[i].transform.position, chunks[i].transform.rotation, chunks[i].transform.lossyScale)); } } private void DrawPaintMode() { string label = "Mode"; string id = "mode" + painter.GetInstanceID().ToString(); GenericMenu menu = new GenericMenu(); menu.AddItem( new GUIContent("Grid"), GEditorSettings.Instance.paintTools.paintModeSelectorType == GEditorSettings.PaintToolsSettings.GPaintModeSelectorType.Grid, () => { GEditorSettings.Instance.paintTools.paintModeSelectorType = GEditorSettings.PaintToolsSettings.GPaintModeSelectorType.Grid; }); menu.AddItem( new GUIContent("Dropdown"), GEditorSettings.Instance.paintTools.paintModeSelectorType == GEditorSettings.PaintToolsSettings.GPaintModeSelectorType.Dropdown, () => { GEditorSettings.Instance.paintTools.paintModeSelectorType = GEditorSettings.PaintToolsSettings.GPaintModeSelectorType.Dropdown; }); GEditorCommon.Foldout(label, true, id, () => { if (GEditorSettings.Instance.paintTools.paintModeSelectorType == GEditorSettings.PaintToolsSettings.GPaintModeSelectorType.Grid) { ShowPaintModeAsGrid(); } else { ShowPaintModeAsDropdown(); } }, menu); } private void ShowPaintModeAsGrid() { GSelectionGridArgs args0 = new GSelectionGridArgs(); args0.selectedIndex = (int)painter.Mode; args0.collection = Enum.GetValues(typeof(GTexturePaintingMode)); args0.itemSize = GEditorCommon.selectionGridTileSizeWide; args0.itemPerRow = 2; args0.drawPreviewFunction = DrawModePreview; EditorGUI.BeginChangeCheck(); painter.Mode = (GTexturePaintingMode)GEditorCommon.SelectionGrid(args0); if (EditorGUI.EndChangeCheck()) { RecordPaintModeAnalytics(); } if (painter.Mode == GTexturePaintingMode.Custom) { GEditorCommon.Separator(); List customPainterTypes = GTerrainTexturePainter.GetCustomPainterTypes(); if (customPainterTypes.Count == 0) { EditorGUILayout.LabelField("No Custom Painter defined!", GEditorCommon.WordWrapItalicLabel); } else { GSelectionGridArgs args1 = new GSelectionGridArgs(); args1.selectedIndex = painter.CustomPainterIndex; args1.collection = customPainterTypes; args1.itemSize = GEditorCommon.selectionGridTileSizeWide; args1.itemPerRow = 2; args1.drawPreviewFunction = DrawCustomMode; painter.CustomPainterIndex = GEditorCommon.SelectionGrid(args1); GEditorCommon.Separator(); painter.CustomPainterArgs = EditorGUILayout.TextField("Custom Args", painter.CustomPainterArgs); } } } private void DrawModePreview(Rect r, object o) { GTexturePaintingMode mode = (GTexturePaintingMode)o; Texture2D icon = GEditorSkin.Instance.GetTexture(mode.ToString() + "Icon"); if (icon == null) { icon = GEditorSkin.Instance.GetTexture("GearIcon"); } string label = ObjectNames.NicifyVariableName(mode.ToString()); DrawMode(r, label, icon); } private void DrawCustomMode(Rect r, object o) { Type t = (Type)o; if (t != null) { string label = ObjectNames.NicifyVariableName(GEditorCommon.GetClassDisplayName(t)); Texture2D icon = GEditorSkin.Instance.GetTexture("GearIcon"); DrawMode(r, label, icon); } } private void DrawMode(Rect r, string label, Texture icon) { GUIStyle labelStyle = EditorStyles.miniLabel; Rect iconRect = new Rect(r.min.x, r.min.y, r.height, r.height); Rect labelRect = new Rect(r.min.x + r.height + 1, r.min.y, r.width - r.height, r.height); GEditorCommon.DrawBodyBox(r); GEditorCommon.DrawHeaderBox(iconRect); if (icon != null) { GUI.color = labelStyle.normal.textColor; RectOffset offset = new RectOffset(3, 3, 3, 3); GUI.DrawTexture(offset.Remove(iconRect), icon); GUI.color = Color.white; } int indent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; GUI.Label(labelRect, label, labelStyle); EditorGUI.indentLevel = indent; } private void ShowPaintModeAsDropdown() { EditorGUI.BeginChangeCheck(); painter.Mode = (GTexturePaintingMode)EditorGUILayout.EnumPopup("Paint Mode", painter.Mode); if (EditorGUI.EndChangeCheck()) { RecordPaintModeAnalytics(); } if (painter.Mode != GTexturePaintingMode.Custom) return; List customPainterTypes = GTerrainTexturePainter.GetCustomPainterTypes(); string[] labels = new string[customPainterTypes.Count]; for (int i = 0; i < labels.Length; ++i) { labels[i] = ObjectNames.NicifyVariableName(GEditorCommon.GetClassDisplayName(customPainterTypes[i])); } int[] indices = GUtilities.GetIndicesArray(customPainterTypes.Count); painter.CustomPainterIndex = EditorGUILayout.IntPopup("Custom Painter", painter.CustomPainterIndex, labels, indices); painter.CustomPainterArgs = EditorGUILayout.DelayedTextField("Custom Args", painter.CustomPainterArgs); } private void OnBeginRender(Camera cam) { if (cam.cameraType != CameraType.SceneView) return; if (painter.Editor_EnableLivePreview) { bool canDrawLivePreview = painter.ActivePainter != null && painter.ActivePainter is IGTexturePainterWithLivePreview; if (canDrawLivePreview) { Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit; if (GStylizedTerrain.Raycast(r, out hit, float.MaxValue, painter.GroupId)) { DrawLivePreview(cam, hit); } } } if (painter.EnableTerrainMask) { DrawMask(cam); } } private void OnBeginRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam) { OnBeginRender(cam); } private void DrawLivePreview(Camera cam, RaycastHit hitInfo) { bool canDrawLivePreview = painter.ActivePainter != null && painter.ActivePainter is IGTexturePainterWithLivePreview; if (!canDrawLivePreview) return; IGTexturePainterWithLivePreview activePainter = painter.ActivePainter as IGTexturePainterWithLivePreview; GTexturePainterArgs args = CreateBasicArgs(hitInfo); painter.FillArgs(ref args, true); IEnumerator terrains = GStylizedTerrain.ActiveTerrains.GetEnumerator(); while (terrains.MoveNext()) { if (terrains.Current.GroupId != painter.GroupId && painter.GroupId >= 0) continue; activePainter.Editor_DrawLivePreview(terrains.Current, args, cam); } } private void DrawMask(Camera cam) { GCommon.ForEachTerrain(painter.GroupId, (t) => { if (t.TerrainData == null) return; if (painter.ActivePainter is GMaskPainter) { if (GTexturePainterCustomParams.Instance.Mask.Visualize) { GLivePreviewDrawer.DrawMask4ChannelsLivePreview(t, cam, t.TerrainData.Mask.MaskMapOrDefault, GCommon.UnitRect); } } else { GLivePreviewDrawer.DrawTerrainMask(t, cam); } }); } private void RecordPaintModeAnalytics() { GTexturePaintingMode mode = painter.Mode; string url = mode == GTexturePaintingMode.Elevation ? GAnalytics.TPAINTER_ELEVATION : mode == GTexturePaintingMode.HeightSampling ? GAnalytics.TPAINTER_HEIGHT_SAMPLING : mode == GTexturePaintingMode.Terrace ? GAnalytics.TPAINTER_TERRACE : mode == GTexturePaintingMode.Remap ? GAnalytics.TPAINTER_REMAP : mode == GTexturePaintingMode.Noise ? GAnalytics.TPAINTER_NOISE : mode == GTexturePaintingMode.SubDivision ? GAnalytics.TPAINTER_SUBDIV : mode == GTexturePaintingMode.Albedo ? GAnalytics.TPAINTER_ALBEDO : mode == GTexturePaintingMode.Metallic ? GAnalytics.TPAINTER_METALLIC : mode == GTexturePaintingMode.Smoothness ? GAnalytics.TPAINTER_SMOOTHNESS : mode == GTexturePaintingMode.Splat ? GAnalytics.TPAINTER_SPLAT : mode == GTexturePaintingMode.Custom ? GAnalytics.TPAINTER_CUSTOM : string.Empty; GAnalytics.Record(url); } } }