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