using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace MTE
{
internal class PaintHeightEditor : IEditor
{
public int Id { get; } = 2;
public bool Enabled { get; set; } = true;
public string Name { get; } = "PaintHeightEditor";
public Texture Icon { get; } =
EditorGUIUtility.IconContent("TerrainInspector.TerrainToolSetHeight").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 MinHeight = -2000;
const float MaxHeight = 2000;
const float MinBrushSize = 0.1f;
const float MaxBrushSize = 10f;
const float MinSpeed = 0.01f;
const float MaxSpeed = 5f;
#endregion
private float brushSize;
public float theHeight;
private float speed;
///
/// Brush size (unit: meter in world space)
///
public float BrushSize
{
get { return brushSize; }
set
{
value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
if (!MathEx.AmostEqual(value, brushSize))
{
brushSize = value;
EditorPrefs.SetFloat("MTE_PaintHeightEditor.brushSize", value);
}
}
}
private float BrushSizeInU3D { get { return BrushSize * Settings.BrushUnit; } }
///
/// Targeting height (unit: meter in local space)
///
public float TheHeight
{
get { return theHeight; }
set { theHeight = value; }
}
///
/// Speed
///
public float Speed
{
get { return speed; }
set
{
value = Mathf.Clamp(value, MinSpeed, MaxSpeed);
if (System.Math.Abs(value - speed) > 0.0001f)
{
speed = value;
EditorPrefs.SetFloat("MTE_PaintHeightEditor.speed", value);
}
}
}
#endregion
public PaintHeightEditor()
{
MTEContext.EnableEvent += (sender, args) =>
{
if (MTEContext.editor == this)
{
LoadSavedParamter();
}
};
MTEContext.EditTypeChangedEvent += (sender, args) =>
{
if (MTEContext.editor == this)
{
LoadSavedParamter();
}
};
// Load default parameters
brushSize = DefaultBrushSize;
speed = DefaultSpeed;
}
private void LoadSavedParamter()
{
// Load parameters from the EditorPrefs
brushSize = EditorPrefs.GetFloat("MTE_PaintHeightEditor.brushSize", DefaultBrushSize);
speed = EditorPrefs.GetFloat("MTE_PaintHeightEditor.speed", DefaultSpeed);
}
public HashSet DefineHotkeys()
{
var hotkeys = new HashSet
{
new Hotkey(this, KeyCode.Minus, () =>
{
Speed -= 0.01f;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.Equals, () =>
{
Speed += 0.01f;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.LeftBracket, () =>
{
BrushSize -= 1;
MTEEditorWindow.Instance.Repaint();
}),
new Hotkey(this, KeyCode.RightBracket, () =>
{
BrushSize += 1;
MTEEditorWindow.Instance.Repaint();
})
};
return hotkeys;
}
public string Header { get { return StringTable.Get(C.PaintHeight_Header); } }
public string Description { get { return StringTable.Get(C.PaintHeight_Description); } }
public void DoArgsGUI()
{
if (!Settings.CompactGUI)
{
GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
}
TheHeight = EditorGUILayoutEx.Slider(StringTable.Get(C.Height), " ", " ", TheHeight, MinHeight, MaxHeight);
BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
Speed = EditorGUILayoutEx.Slider(StringTable.Get(C.Speed), "[", "]", Speed, MinSpeed, MaxSpeed);
}
private readonly List modifyGroups = new List(4);
public void OnSceneGUI()
{
var e = Event.current;
if (!(EditorWindow.mouseOverWindow is SceneView))
{
return;
}
// do nothing when mouse middle/right button, alt key is pressed
if (e.button != 0 || e.alt)
return;
// hold control key to sample height
if (e.control && !e.isKey)
{
float sampledHeight;
if (SampleHeight(e.mousePosition, out sampledHeight))
{
if (Mathf.Abs(TheHeight - sampledHeight) > Mathf.Epsilon)
{
MTEEditorWindow.Instance.Repaint();
}
TheHeight = sampledHeight;
}
}
else
{
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))
{
return;
}
if (Settings.ShowBrushRect)
{
Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
}
// collect modifiy 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);
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("Paint Height", 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;
PaintHeight(mesh, meshCollider, vIndex);
}
}
if (e.type == EventType.MouseUp)
{
MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
MTEEditorWindow.Instance.HandleMeshSave();
}
}
}
SceneView.RepaintAll();
}
///
/// sample height(y)
///
/// screen position of mouse
/// sampled height
/// true: success/false: failed
private static bool SampleHeight(Vector2 mousePosition, out float height)
{
height = 0;
HandleUtility.AddDefaultControl(0);
RaycastHit raycastHit;
Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
if (Physics.Raycast(ray, out raycastHit))
{
if (!MTEContext.IsMatchFilter(raycastHit.transform.gameObject))
{
return false;
}
Handles.color = Color.green;
var size = HandleUtility.GetHandleSize(raycastHit.point) * Settings.PointSize;
Handles.SphereHandleCap(0, raycastHit.point, Quaternion.identity, size, EventType.Repaint);
var hitPointLocal = raycastHit.transform.InverseTransformPoint(raycastHit.point);//convert hitpoint's position to mesh's local coordinate
height = hitPointLocal.y;
return true;
}
return false;
}
///
/// Move height of vertex towards the specified height
///
/// mesh to modify
/// meshcollider related to the mesh
/// indexes of modifying vertexes
private void PaintHeight(Mesh mesh, MeshCollider meshCollider, List vIndex)
{
var vertexes = mesh.vertices;
for (var i = 0; i < vIndex.Count; ++i)
{
var index = vIndex[i];
vertexes[index].y =
Mathf.MoveTowards(vertexes[index].y, TheHeight, Speed);
}
mesh.vertices = vertexes;
MTEEditorWindow.Instance.SetMeshDirty(meshCollider.gameObject);
MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
}
}
}