一,需求
从fbx载入的模型是不可以在unity里编辑的。
我有一人特殊的需求就是想在unity里为mesh的各顶点K动画。
于是需要自己实现一个可编辑(其实只是顶点可以拖动)的mesh。
二,思路
首先由导入的mesh复制一个新mesh,并将原mesh替换掉,这样是为了以后编辑过程不会破坏原mesh,然后就没有原mesh的事儿了。
假设mesh是一个立方体,则其mesh.vertices会有36个元素,mesh.triangles有12个元素。
我们要创建8个gameObject表示立方体的8个顶点gameObject,并根据mesh.vertices的坐标值设定顶点gameObject的localPosition(注意是localPosition而不是position),并且还要找出各顶点gameObject对象mesh.vertcies中的哪些元素。因为一共有8个顶点gameObject,但mesh.vertices有36个元素,所以一个gameObject对应多个元素。
三,unity里实现
1,创建一个gameObject,命句为editableMesh,并添加Mesh Filter和Mesh Renderer,Mesh Filter中添加模型mesh(例如从3dmax或blender中导出的模型)。
2,为editableMesh节点添加子节点vertexObjTemplate并将勾去掉使其变成Active==false状态,其上挂脚本vertexobjControl.cs:
using UnityEngine;using System.Collections;using System.Collections.Generic;public class vertexObjControl : MonoBehaviour { public List<int> m_vIDList=new List<int>();}
3,为editableMesh节点添加脚本convertmeshToEditableMesh.cs
using UnityEngine;using System.Collections;using System.Collections.Generic;using UnityEngine.Assertions.Must;[ExecuteInEditMode]public class convertMeshToEditableMesh : MonoBehaviour { private GameObject m_vertexObjs; private GameObject m_vertexObjTemplate; void Awake(){ m_vertexObjTemplate=transform.FindChild("vertexObjTemplate").gameObject; CreateReplaceMeshFromOriginMesh (); } void Update(){ updateMeshVertex (); } void updateMeshVertex(){ int vertexCount = gameObject.GetComponent<MeshFilter> ().sharedMesh.vertexCount; Vector3[] vertices = new Vector3[vertexCount] ; int vertexObjCount = m_vertexObjs.transform.childCount; for (int i=0; i<vertexObjCount; i++) { GameObject vobj=m_vertexObjs.transform.GetChild(i).gameObject; Vector3 vobjPos=vobj.transform.localPosition; int nVID=vobj.GetComponent<vertexObjControl>().m_vIDList.Count; for(int j=0;j<nVID;j++){ int vID=vobj.GetComponent<vertexObjControl>().m_vIDList[j]; vertices[vID]=vobjPos; } } gameObject.GetComponent<MeshFilter> ().sharedMesh.vertices = vertices;
gameObject.GetComponent<MeshFilter> ().sharedMesh.RecalculateNormals(); gameObject.GetComponent<MeshFilter> ().sharedMesh.RecalculateBounds(); } void CreateReplaceMeshFromOriginMesh() { //if vertexObjs is not created, create it Transform vertexObjsTransform=transform.FindChild("vertexObjs"); if (vertexObjsTransform == null) { m_vertexObjs = new GameObject (); m_vertexObjs.name = "vertexObjs"; m_vertexObjs.transform.SetParent (gameObject.transform); } else { m_vertexObjs=vertexObjsTransform.gameObject; } //if mesh not replaced, replace it if (gameObject.GetComponent<MeshFilter> ().sharedMesh.name != "replaceMesh") { Mesh replaceMesh = Instantiate (gameObject.GetComponent<MeshFilter> ().sharedMesh) as Mesh; replaceMesh.name = "replaceMesh"; gameObject.GetComponent<MeshFilter> ().sharedMesh = replaceMesh; } //if vobjs not generated, generate vobjs from replaved mesh if (m_vertexObjs.transform.childCount == 0) { int vertexCount = gameObject.GetComponent<MeshFilter> ().sharedMesh.vertexCount; List<bool> list_isVIDHasAddedToVObj = new List<bool> (); for (int i=0; i<vertexCount; i++) { list_isVIDHasAddedToVObj.Add (false); } for (int vID=0; vID<vertexCount; vID++) { if (list_isVIDHasAddedToVObj [vID]) { continue; } //add vID of current vertex to existing vertexObj Vector3 vPos = gameObject.GetComponent<MeshFilter> ().sharedMesh.vertices [vID]; int vertexObjCount = m_vertexObjs.transform.childCount; for (int vobjID=0; vobjID<vertexObjCount; vobjID++) { GameObject vobj = m_vertexObjs.transform.GetChild (vobjID).gameObject; Vector3 vobjPos = vobj.transform.localPosition; if (vPos == vobjPos) { //add vID to vobj vobj.GetComponent<vertexObjControl> ().m_vIDList.Add (vID); list_isVIDHasAddedToVObj [vID] = true; } } if (list_isVIDHasAddedToVObj [vID]) {//vID has added to existing vobj //do nothing } else {//no existing vobj for vID to add to //create new vobj, and add vID to it GameObject vobj = Instantiate (m_vertexObjTemplate); vobj.SetActive (true); vobj.name = "vobj_" + m_vertexObjs.transform.childCount; vobj.transform.SetParent (m_vertexObjs.transform); vobj.transform.localPosition = vPos; vobj.GetComponent<vertexObjControl> ().m_vIDList.Add (vID); list_isVIDHasAddedToVObj [vID] = true; } } } gameObject.GetComponent<MeshFilter> ().sharedMesh.RecalculateNormals(); gameObject.GetComponent<MeshFilter> ().sharedMesh.RecalculateBounds(); }}
然后就可以在编辑器里移动mesh的顶点或给顶点K动画了。
----补充:
以上是通用的代码,可将任何mesh转成可编辑(只是移动顶点)的。
如果只想做一个可编辑的四边形面片,可以像下面这样简单实现:
1,创建一个gameObject命名为editableQuad。为editableQuad创建子节点vertexObjs。再为vertexObjs依次创建子节点vertexObj_0,vertexObj_1,vertexObj_2,vertexObj_3(按顺序排列)。
2,再为editableQuad添加脚本editableQuadControl.cs:
using UnityEngine;using System.Collections;using System.Collections.Generic;using UnityEngine.Assertions.Must;[ExecuteInEditMode]public class editableQuadControl : MonoBehaviour { private GameObject vertexObjs; void Awake(){ vertexObjs=transform.FindChild("vertexObjs").gameObject; int childCount = vertexObjs.transform.childCount; int n = 4; for (int i=0; i<n; i++) { if(i>=childCount){ GameObject vertexObj=new GameObject (); vertexObj.name="vertexObj_"+i; vertexObj.transform.SetParent(vertexObjs.transform); vertexObj.transform.localPosition=Random.insideUnitSphere; } } gameObject.GetComponent<MeshFilter> ().mesh = CreateMesh (); } void Update(){ updateMeshVertex (); } void updateMeshVertex(){ Vector3 vLU = vertexObjs.transform.GetChild(0).localPosition; Vector3 vLD = vertexObjs.transform.GetChild(1).localPosition; Vector3 vRU = vertexObjs.transform.GetChild(2).localPosition; Vector3 vRD = vertexObjs.transform.GetChild(3).localPosition; gameObject.GetComponent<MeshFilter> ().sharedMesh.vertices = new Vector3[]{vLD,vLU,vRU,vRD}; } Mesh CreateMesh() { Mesh m = new Mesh(); m.name = "ScriptedMesh"; //note: unity is left-hand system Vector3 vLU = vertexObjs.transform.GetChild(0).localPosition; Vector3 vLD = vertexObjs.transform.GetChild(1).localPosition; Vector3 vRU = vertexObjs.transform.GetChild(2).localPosition; Vector3 vRD = vertexObjs.transform.GetChild(3).localPosition; m.vertices = new Vector3[] { vLD,//LD vLU,//LU vRU,//RU vRD//RD }; m.uv = new Vector2[] { new Vector2 (0, 0), new Vector2 (0, 1), new Vector2 (1, 1), new Vector2 (1, 0) }; m.triangles = new int[] { 0, 1, 2, 0, 2, 3}; m.RecalculateNormals(); m.RecalculateBounds(); return m; }}