using System.Text; using UnityEngine; using UnityEngine.TerrainTools; namespace UnityEditor.TerrainTools { /// /// Represents an base terrain tools variator class. /// public abstract class BaseBrushVariator : IBrushController, IBrushTerrainCache { private readonly string m_NamePrefix; private readonly IBrushEventHandler m_EventHandler; private readonly IBrushTerrainCache m_TerrainCache; private static GUIStyle _s_SceneLabelStyle; /// /// Gets the the GUIStyle of the scene's label. /// protected static GUIStyle s_SceneLabelStyle { get { if (_s_SceneLabelStyle != null) { return _s_SceneLabelStyle; } _s_SceneLabelStyle = new GUIStyle { normal = new GUIStyleState() { background = Texture2D.whiteTexture }, fontSize = 12 }; return _s_SceneLabelStyle; } } /// /// Checks if the brush is in use. /// public virtual bool isInUse => m_ModifierActive; /// /// Initializes and returns an instance of BaseBrushVariator. /// /// The tool's name which the variator is used with. /// The brush event handler. /// The cache reference of the terrain. protected BaseBrushVariator(string toolName, IBrushEventHandler eventHandler, IBrushTerrainCache terrainCache) { m_NamePrefix = toolName; m_EventHandler = eventHandler; m_TerrainCache = terrainCache; } /// /// Sets the repaint request to true. /// protected void RequestRepaint() { m_EventHandler.RequestRepaint(); } private void OnModifierKeyPressed() { m_ModifierActive = true; HandleBeginModifier(); } private void OnModifierKeyReleased() { HandleEndModifier(); m_ModifierActive = false; } /// /// Gets the editor preferences for boolean values. /// /// The name of the preference. /// The default value of the preference. /// Returns the editor preference value corresponding to the name. /// protected bool GetEditorPrefs(string name, bool defaultValue) { return EditorPrefs.GetBool($"{m_NamePrefix}.{name}", defaultValue); } /// /// Sets the editor preferences for boolean values. /// /// The name of the preference. /// The prefence value to set. /// protected void SetEditorPrefs(string name, bool currentValue) { EditorPrefs.SetBool($"{m_NamePrefix}.{name}", currentValue); } /// /// Gets the editor preferences for float values. /// /// The name of the preference. /// The default value of the preference. /// Returns the editor preference value corresponding to the name. /// protected float GetEditorPrefs(string name, float defaultValue) { return EditorPrefs.GetFloat($"{m_NamePrefix}.{name}", defaultValue); } /// /// Sets the editor preferences for float values. /// /// The name of the preference. /// The prefence value to set. /// protected void SetEditorPrefs(string name, float currentValue) { EditorPrefs.SetFloat($"{m_NamePrefix}.{name}", currentValue); } private bool m_ModifierActive; private Vector2 m_InitialMousePosition; /// /// Calculates the difference between the mouses initial and current position. /// /// The current mouse event. /// The scale to multiply the delta by. /// Returns the difference between the initial and current position of the mouse. /// protected Vector2 CalculateMouseDeltaFromInitialPosition(Event mouseEvent, float scale = 1.0f) { Vector2 mousePosition = mouseEvent.mousePosition; Vector2 delta = m_InitialMousePosition - mousePosition; Vector2 scaledDelta = delta * scale; return scaledDelta; } /// /// Gets the mouses delta from the mouse event. /// /// The current mouse event. /// The scale to multiply the delta by. /// Returns the mouses delta. /// protected static Vector2 CalculateMouseDelta(Event mouseEvent, float scale = 1.0f) { Vector2 delta = mouseEvent.delta; Vector2 scaledDelta = delta * scale; return scaledDelta; } /// /// Called when the Variator is initially modified. /// /// This method is to be overriden when creating custom variators. /// Returns true when the modification is successful. Otherwise, returns false. protected virtual bool OnBeginModifier() { return false; } /// /// Called when a mouse drag is used when modifying a Variator. /// /// The current mouse event. /// The terrain in focus. /// The editcontext to reference. /// This method is to be overriden when creating custom variators. /// Returns true when the modification is successful. Otherwise, returns false. protected virtual bool OnModifierUsingMouseMove(Event mouseEvent, Terrain terrain, IOnSceneGUI editContext) { if (!m_ModifierActive) { m_InitialMousePosition = mouseEvent.mousePosition; } return false; } /// /// Called when a mouse wheel is used when modifying a Variator. /// /// The current mouse event. /// The terrain in focus. /// The editcontext to reference. /// This method is to be overriden when creating custom variators. /// Returns true when the modification is successful. Otherwise, returns false. protected virtual bool OnModifierUsingMouseWheel(Event mouseEvent, Terrain terrain, IOnSceneGUI editContext) { return false; } /// /// Called when the Variator's modification has finished. /// /// This method is to be overriden when creating custom variators. /// Returns true when the modification is successful. Otherwise, returns false. protected virtual bool OnEndModifier() { return false; } private bool HandleBeginModifier() { bool consumeEvent = OnBeginModifier(); return consumeEvent; } private bool HandleModifierUsingMouseMove(Event mouseEvent, Terrain terrain, IOnSceneGUI editContext) { bool consumeEvent = OnModifierUsingMouseMove(mouseEvent, terrain, editContext); return consumeEvent; } private bool HandleModifierUsingMouseWheel(Event mouseEvent, Terrain terrain, IOnSceneGUI editContext) { bool consumeEvent = OnModifierUsingMouseWheel(mouseEvent, terrain, editContext); return consumeEvent; } private bool HandleEndModifier() { bool consumeEvent = OnEndModifier(); return consumeEvent; } private bool ProcessMouseEvent(Event mouseEvent, int controlId, Terrain terrain, IOnSceneGUI editContext) { bool consumeEvent = false; if (m_ModifierActive) { EventType eventType = mouseEvent.GetTypeForControl(controlId); switch (eventType) { case EventType.MouseMove: { consumeEvent |= HandleModifierUsingMouseMove(mouseEvent, terrain, editContext); break; } case EventType.ScrollWheel: { consumeEvent |= HandleModifierUsingMouseWheel(mouseEvent, terrain, editContext); break; } } // End of switch. } if (consumeEvent) { // We changed something - time to repaint... RequestRepaint(); return true; } else { return false; } } /// /// Defines data when the brush is selected. /// /// The shortcut handler to subscribe brush shortcuts. /// /// public virtual void OnEnterToolMode(BrushShortcutHandler shortcutHandler) { shortcutHandler.AddActions(BrushShortcutType.RotationSizeStrength, OnModifierKeyPressed, OnModifierKeyReleased); } /// /// Defines data when the brush is deselected. /// /// The shortcut handler to unsubscribe brush shortcuts. /// /// public virtual void OnExitToolMode(BrushShortcutHandler shortcutHandler) { shortcutHandler.RemoveActions(BrushShortcutType.RotationSizeStrength); } /// /// Triggers events to render objects and displays within Scene view. /// /// The event to check whether the mouse is in use. /// The control ID of the GUI. /// The terrain in focus. /// The editcontext to reference public virtual void OnSceneGUI(Event currentEvent, int controlId, Terrain terrain, IOnSceneGUI editContext) { if (currentEvent.isMouse || currentEvent.isScrollWheel) { if (ProcessMouseEvent(currentEvent, controlId, terrain, editContext)) { m_EventHandler.RegisterEvent(currentEvent); } } } /// /// Renders the brush's GUI within the inspector view. /// /// The terrain in focus. /// The editcontext used to show the brush GUI. public virtual void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext) { } /// /// Triggers events when painting on a terrain. /// /// The terrain in focus. /// The editcontext to reference. /// Returns true if the paint opertation is succesful. Otherise, returns false. public virtual bool OnPaint(Terrain terrain, IOnPaint editContext) { return true; } /// /// Adds basic information to the selected brush. /// /// The Terrain in focus. /// The IOnSceneGUI to reference. /// The StringBuilder containing the brush information. public virtual void AppendBrushInfo(Terrain terrain, IOnSceneGUI editContext, StringBuilder builder) { } /// /// Gets and sets the terrain in focus. /// public Terrain terrainUnderCursor => m_TerrainCache.terrainUnderCursor; /// /// Gets and sets the value associated to whether there is a raycast hit detecting a terrain under the cursor. /// public bool isRaycastHitUnderCursorValid => m_TerrainCache.isRaycastHitUnderCursorValid; /// /// Gets and sets the raycast hit that was under the cursor's position. /// public RaycastHit raycastHitUnderCursor => m_TerrainCache.raycastHitUnderCursor; /// /// Checks if the cursor is currently locked and can not be updated. /// public bool canUpdateTerrainUnderCursor => m_TerrainCache.canUpdateTerrainUnderCursor; /// /// Handles the locking of the terrain cursor in it's current position. /// /// This method is commonly used when utilizing shortcuts. /// Whether the cursor is visible within the scene. When the value is true the cursor is visible. /// public void LockTerrainUnderCursor(bool cursorVisible) { m_TerrainCache.LockTerrainUnderCursor(cursorVisible); } /// /// Handles unlocking of the terrain cursor. /// /// public void UnlockTerrainUnderCursor() { m_TerrainCache.UnlockTerrainUnderCursor(); } } }