unity 编辑mesh顶点位置

在某些特殊情况下,不得不对模型进行一些简单的修改,所以写了个简单的编辑脚本。

思路就是获取mesh上的所有顶点,然后在每个顶点位置创建一个控制点,控制点可以是任意你喜欢的物体,通过判断控制点的位置信息来修改mesh的顶点位置。

在unity中,mesh上的顶点与几个面相交,就会有几个坐标点,估计是把点为每一个三角形面复制了一份,所以这里在生成控制点时要注意剔除重复的点,不要重复生成。

这里我是把每个顶点的坐标转为字符串,使用该坐标的字符串作为key来把控制点与顶点数据联系起来。

下面给出完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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;
        }
    }
}

然后是顶点编辑脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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来修改,修改后再决定是否需要保存。

posted @   多见多闻  阅读(2382)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示