在某些特殊情況下,不得不對模型進行一些簡單的修改,所以寫了個簡單的編輯腳本。
思路就是獲取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來修改,修改后再決定是否需要保存。