using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using System.Text;
namespace MTE
{
internal class RaiseLowerEditor : IEditor
{
public int Id { get; } = 1;
public bool Enabled { get; set; } = true;
public string Name { get; } = "RaiseLowerEditor";
public Texture Icon { get; } =
EditorGUIUtility.IconContent("TerrainInspector.TerrainToolRaise").image;
public bool WillEditMesh { get; } = true;
public bool WantMouseMove { get; } = false;
#region Parameters
#region Constant
// default
const float DefaultBrushSize = 4;
const float DefaultSpeed = 1f;
// min/max
const float MinBrushSize = 0.1f;
const float MaxBrushSize = 10f;
const float MinStrength = 0.01f;
const float MaxStrength = 1f;
#endregion
///
/// Current brush index (not used)
///
public int brushIndex = 0;
///
/// Going up/down
///
public bool GoingUp = true;
///
/// Brush size (cell count)
///
private float brushSize;
///
/// Brush strength
///
private float strength;
///
/// Falloff curve
///
private AnimationCurve falloffCurve;
///
/// Brush size (unit: brush unit)
///
public float BrushSize
{
get { return brushSize; }
set
{
value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
if (!MathEx.AmostEqual(value, brushSize))
{
brushSize = value;
EditorPrefs.SetFloat("MTE_RaiseLowerEditor.brushSize", value);
}
}
}
private float BrushSizeInU3D
{
[System.Diagnostics.DebuggerStepThrough]
get { return BrushSize * Settings.BrushUnit; }
}
///
/// Brush strength
///
public float Strength
{
get { return strength; }
set
{
value = Mathf.Clamp(value, MinStrength, MaxStrength);
if (!MathEx.AmostEqual(value, strength))
{
strength = value;
EditorPrefs.SetFloat("MTE_RaiseLowerEditor.strength", value);
}
}
}
#endregion
public RaiseLowerEditor()
{
MTEContext.EnableEvent += (sender, args) =>
{
if (MTEContext.editor == this)
{
LoadSavedParamter();
}
};
// Load default parameters
brushSize = DefaultBrushSize;
strength = DefaultSpeed;
falloffCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
}
private void LoadSavedParamter()
{
// Load parameters from the EditorPrefs
brushSize = EditorPrefs.GetFloat("MTE_RaiseLowerEditor.brushSize", DefaultBrushSize);
strength = EditorPrefs.GetFloat("MTE_RaiseLowerEditor.strength", DefaultSpeed);
}
public HashSet DefineHotkeys()
{
return new HashSet
{
new Hotkey(this, KeyCode.LeftBracket, () =>
{
BrushSize -= 1;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.RightBracket, () =>
{
BrushSize += 1;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.Minus, () =>
{
Strength -= 0.01f;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.Equals, () =>
{
Strength += 0.01f;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.Space, () =>
{
//toggle push up/down
GoingUp = !GoingUp;
MTEEditorWindow.Instance.Repaint();
})
};
}
public string Header { get { return StringTable.Get(C.RaiseLower_Header); } }
public string Description { get { return StringTable.Get(C.RaiseLower_Description); } }
public void DoArgsGUI()
{
var defaultGUIColor = GUI.contentColor;
var defaultFontSize = GUI.skin.toggle.fontSize;
GUI.color = GoingUp ? Color.green : Color.green*0.8f;
GUI.skin.button.fontSize = 40;
GUI.enabled = false;
GUILayout.Toggle(GoingUp, GoingUp ? @"⇧" : @"⇩", "Button");
GUI.color = defaultGUIColor;
GUI.skin.button.fontSize = defaultFontSize;
GUI.enabled = true;
//Settings
if (!Settings.CompactGUI)
{
GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
}
BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
Strength = EditorGUILayoutEx.Slider(StringTable.Get(C.Strength), "[", "]", Strength, MinStrength, MaxStrength);
EditorGUILayout.BeginHorizontal();
{
var label = new GUIContent(StringTable.Get(C.Falloff));
var size = GUIStyle.none.CalcSize(label);
GUILayout.Label(label, GUILayout.Width(size.x + 10), GUILayout.MinWidth(60));
EditorGUILayout.LabelField("", GUILayout.Width(15));
var rect = GUILayoutUtility.GetRect(128, 128, GUILayout.ExpandWidth(false));
falloffCurve = EditorGUI.CurveField(rect, falloffCurve, Color.green, new Rect(0, 0, 1, 1));
EditorGUILayout.LabelField("", GUILayout.Width(15));
}
EditorGUILayout.EndHorizontal();
}
private readonly List modifyGroups = new List(4);
private readonly StringBuilder notice = new StringBuilder();
public void OnSceneGUI()
{
notice.Length = 0;
do
{
var e = Event.current;
if (e.commandName == "UndoRedoPerformed")
{
SceneView.RepaintAll();
break;
}
if (!(EditorWindow.mouseOverWindow is SceneView))
{
notice.AppendLine("Mouse not in SceneView.");
break;
}
// do nothing when mouse middle/right button, control/alt key is pressed
if (e.button != 0 || e.control || e.alt)
{
break;
}
HandleUtility.AddDefaultControl(0);
RaycastHit raycastHit;
Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
//Debug.Log(string.Format("mouse at ({0}, {1})", e.mousePosition.x, e.mousePosition.y));
if (Physics.Raycast(ray, out raycastHit,
Mathf.Infinity,
1 << MTEContext.TargetLayer//only hit target layer
))
{
//check tag
if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
{
notice.AppendLine("No target was hit.");
break;
}
if (Settings.ShowBrushRect)
{
Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
if (Settings.DebugMode)
{
var oldZTest = Handles.zTest;
Handles.color = Color.red;
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
Handles.ArrowHandleCap(0, raycastHit.point + new Vector3(0, 200, 0), Quaternion.LookRotation(Vector3.down), 1000f, EventType.Repaint);
Handles.zTest = oldZTest;
}
}
if (!MTEContext.Targets.Any())
{
notice.AppendLine("No targets. Check if editing mesh-terrain(s) match the tag and layer of MTE editor.");
}
else
{
// collect modify group
modifyGroups.Clear();
foreach (var target in MTEContext.Targets)
{
var meshFilter = target.GetComponent();
var meshCollider = target.GetComponent();
var mesh = meshFilter.sharedMesh;
var hitPointLocal = target.transform.InverseTransformPoint(raycastHit.point);
List vIndex;
List vDistance;
VertexMap.GetAffectedVertex(target, hitPointLocal, BrushSizeInU3D,
out vIndex, out vDistance);
notice.AppendFormat("About to modify {0} vertices in {1}.\n", vIndex.Count, target.name);
if (Settings.ShowAffectedVertex)
{
Utility.ShowAffectedVertices(target, mesh, vIndex);
}
if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
{
if (vIndex.Count != 0)
{
modifyGroups.Add(new MeshModifyGroup(target, mesh, meshCollider, vIndex, vDistance));
}
}
}
//record undo operation for targets that to be modified
if (e.type == EventType.MouseDown)
{
Utility.Record("Raise/Lower", raycastHit.point, this.BrushSizeInU3D);
}
// execute the modification
if (modifyGroups.Count != 0)
{
for (int i = 0; i < modifyGroups.Count; i++)
{
var modifyGroup = modifyGroups[i];
var mesh = modifyGroup.mesh;
var meshCollider = modifyGroup.meshCollider;
var vIndex = modifyGroup.vIndex;
var vDistance = modifyGroup.vDistance;
RaiseLower(mesh, meshCollider, vIndex, vDistance);
notice.AppendFormat("Modified {0} vertices in {1}.\n", vIndex.Count, modifyGroup.gameObject.name);
}
}
if (e.type == EventType.MouseUp)
{
MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
MTEEditorWindow.Instance.HandleMeshSave();
}
}
}
SceneView.RepaintAll();
} while (false);
if (Settings.DebugMode)
{
Handles.BeginGUI();
GUILayout.Label(this.notice.ToString());
Handles.EndGUI();
}
}
///
/// Do raise/lower height for selecting vertexes
///
/// mesh to modify
/// meshcollider related to the mesh
/// indexes of modifying vertexes
/// distances of modifying vertexes to the center, corresponding to vVertexIndex
public void RaiseLower(Mesh mesh, MeshCollider meshCollider, List vIndex, List vDistance)
{
var vertexes = mesh.vertices;
var brushSizeInU3D = this.BrushSizeInU3D;
var brushStrengthInU3D = Strength * Settings.BrushUnit;
for (var i = 0; i < vIndex.Count; ++i)
{
var index = vIndex[i];
var distance = vDistance[i];
var x = Mathf.Clamp01(1 - distance / brushSizeInU3D);
var falloffFactor = falloffCurve.Evaluate(x);//get falloff factor from the curve
var offset = brushStrengthInU3D * falloffFactor;
if (GoingUp)
{
vertexes[index].y += offset;
}
else
{
vertexes[index].y -= offset;
}
}
mesh.vertices = vertexes;
MTEEditorWindow.Instance.SetMeshDirty(meshCollider.gameObject);
MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
}
}
}