在某些特殊情况下,不得不对模型进行一些简单的修改,所以写了个简单的编辑脚本。
思路就是获取mesh上的所有顶点,然后在每个顶点位置创建一个控制点,控制点可以是任意你喜欢的物体,通过判断控制点的位置信息来修改mesh的顶点位置。
在unity中,mesh上的顶点与几个面相交,就会有几个坐标点,估计是把点为每一个三角形面复制了一份,所以这里在生成控制点时要注意剔除重复的点,不要重复生成。
这里我是把每个顶点的坐标转为字符串,使用该坐标的字符串作为key来把控制点与顶点数据联系起来。
下面给出完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MeshEditorPoint : MonoBehaviour { //顶点id,(顶点初始位置转字符串) [HideInInspector] public string pointid; //记录坐标点上一次移动的位置,用于判断控制点是否移动 [HideInInspector] private Vector3 lastPosition; public delegate void MoveDelegate(string pid,Vector3 pos); //控制点移动时的回调 public MoveDelegate onMove = null; // Use this for initialization void Start () { lastPosition = transform.position; } // Update is called once per frame void Update () { if(transform.position != lastPosition){ if(onMove != null) onMove(pointid, transform.localPosition); lastPosition = transform.position; } } }
然后是顶点编辑脚本:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System; public class ModelMeshEditor : MonoBehaviour { //控制点的大小 public float pointScale = 1.0f; private float lastPointScale = 1.0f; Mesh mesh; //顶点列表 List<Vector3> positionList = new List<Vector3>(); //顶点控制物体列表 List<GameObject> positionObjList = new List<GameObject>(); /// <summary> /// key:顶点字符串 /// value:顶点在列表中的位置 /// </summary> Dictionary<string, List<int>> pointmap = new Dictionary<string, List<int>>(); // Use this for initialization void Start () { lastPointScale = pointScale; mesh = GetComponent<MeshFilter>().sharedMesh; CreateEditorPoint(); } //创建控制点 public void CreateEditorPoint(){ positionList = new List<Vector3>(mesh.vertices); for (int i = 0; i < mesh.vertices.Length; i++) { string vstr = Vector2String(mesh.vertices[i]); if(!pointmap.ContainsKey(vstr)){ pointmap.Add(vstr,new List<int>()); } pointmap[vstr].Add(i); } foreach (string key in pointmap.Keys) { GameObject editorpoint = (GameObject)Resources.Load("Prefabs/MeshEditor/MeshEditorPoint"); editorpoint = Instantiate(editorpoint); editorpoint.transform.parent = transform; editorpoint.transform.localPosition = String2Vector(key); editorpoint.transform.localScale = new Vector3(1f, 1f, 1f); MeshEditorPoint editorPoint = editorpoint.GetComponent<MeshEditorPoint>(); editorPoint.onMove = PointMove; editorPoint.pointid = key; positionObjList.Add(editorpoint); } } //顶点物体被移动时调用此方法 public void PointMove(string pointid,Vector3 position){ if(!pointmap.ContainsKey(pointid)){ return; } List<int> _list = pointmap[pointid]; for (int i = 0; i < _list.Count; i ++){ positionList[_list[i]] = position; } mesh.vertices = positionList.ToArray(); mesh.RecalculateNormals(); } // Update is called once per frame void Update () { //检测控制点尺寸是否改变 if (Math.Abs(lastPointScale - pointScale) > 0.1f){ lastPointScale = pointScale; for (int i = 0; i < positionObjList.Count; i ++){ positionObjList[i].transform.localScale = new Vector3(pointScale, pointScale, pointScale); } } } string Vector2String(Vector3 v){ StringBuilder str = new StringBuilder(); str.Append(v.x).Append(",").Append(v.y).Append(",").Append(v.z); return str.ToString(); } Vector3 String2Vector(string vstr) { try{ string[] strings = vstr.Split(','); return new Vector3(float.Parse(strings[0]), float.Parse(strings[1]), float.Parse(strings[2])); }catch(Exception e){ Debug.LogError(e.ToString()); return Vector3.zero; } } }
使用:
首先制作控制点的prefab,然后挂载上MeshEditorPoint脚本。
在想要编辑的mesh的GameObject上挂载ModelMeshEditor,不要忘记修改ModelMeshEditor中加载prefab的路径。
然后运行场景可就可以看到模型上的控制点了:
然后就可以拖动这些控制点来改变mesh的顶点数据了:
***注意:这里对mesh的修改是即时保存的,并且不能恢复,修改之前请务必做好备份!!!也可以修改上面的脚本,在开始时复制一个mesh来修改,修改后再决定是否需要保存。