unity 編輯mesh頂點位置


在某些特殊情況下,不得不對模型進行一些簡單的修改,所以寫了個簡單的編輯腳本。

思路就是獲取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來修改,修改后再決定是否需要保存。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM