1、对于网格贴图坐标在【0,1】范围内的游戏物体,可以合并贴图。
2、对应网格贴图坐标超出【0,1】范围的游戏物体,合并网格,不合并贴图,需要编写定制纹理数的shader。
如果也用合并贴图的方式,会在贴图重复使用的接缝处出现斑马线
(需要在片元着色器将uv转换到【0,1】范围(
uv= uv-floor(uv);这个操作会导致
斑马线),再用rect转换到合并后的贴图)。
方案2:对应网格贴图坐标超出【0,1】范围的游戏物体,合并网格,不合并贴图,需要编写定制纹理数的shader。
如果也用合并贴图的方式,会在贴图重复使用的接缝处出现斑马线
(需要在片元着色器将uv转换到【0,1】范围(
uv= uv-floor(uv);这个操作会导致
斑马线),再用rect转换到合并后的贴图)。
斑马线效果:
1、合并网格
2、添加网格渲染器,编辑定制贴图数量的shader,创建材质球,指定多个对应贴图。
代码:
using
System.Collections;
using
System.Collections.Generic;
using
UnityEngine;
using
UnityEditor;
public
class
CombineSubMeshToPrefab
:
MonoBehaviour
{
[
MenuItem
(
"Tools/Combine SubMesh And Create Prefab & C"
)]
public
static
void
CombineSubMesh()
{
GameObject
go =
Selection
.activeGameObject;
if
(go)
{
MeshFilter
mf = go.GetComponent<
MeshFilter
>();
if
(mf)
{
Mesh
mesh = mf.sharedMesh;
if
(mesh)
{
Mesh
cMesh =
MeshUtil
.MergeSubMesh(mesh);
if
(cMesh)
{
string
meshPath =
"Assets/__source/Model/Combined/"
+cMesh.name+
".asset"
;
AssetDatabase
.DeleteAsset(meshPath);
AssetDatabase
.CreateAsset(cMesh, meshPath);
GameObject
cGo =
new
GameObject
(
"CSM_"
+ go.name);
cGo.transform.parent = go.transform;
MeshFilter
cMf=cGo.AddComponent<
MeshFilter
>();
cMf.sharedMesh = cMesh;
string
prefabPath =
"Assets/__source/Prefab/Combined/"
+ cGo.name+
".prefab"
;
Object
tempPrefab =
PrefabUtility
.CreateEmptyPrefab(prefabPath);
tempPrefab =
PrefabUtility
.ReplacePrefab(cGo, tempPrefab);
}
}
}
}
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
public class MeshUtil{
[MenuItem("Tools/Combine SubMesh & M")]
public static void CombineSubMesh()
{
GameObject go = Selection.activeGameObject;
if (go)
{
MeshFilter mf = go.GetComponent<MeshFilter>();
if (mf)
{
Mesh mesh = mf.sharedMesh;
if (mesh)
{
Mesh cMesh = MeshUtil.MergeSubMesh(mesh);
if (cMesh)
{
string meshPath = "Assets/__source/Model/Combined/" + cMesh.name + ".asset";
AssetDatabase.DeleteAsset(meshPath);
AssetDatabase.CreateAsset(cMesh, meshPath);
}
}
}
}
}
/// <summary>
/// 深度复制网格
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public static Mesh MeshTrueCopy(Mesh i)
{
Mesh o = new Mesh();
o.name = i.name;
o.hideFlags = i.hideFlags;
//顶点 位置 方向
o.vertices = i.vertices;
o.normals = i.normals;
o.tangents = i.tangents;
//三角面
o.triangles = i.triangles;
o.subMeshCount = i.subMeshCount;
//颜色
o.uv = i.uv;
int dCount = 50;
for(int j=0;j<o.vertexCount;j++)
{
if (dCount > 0&&(Mathf.Abs(o.uv[j].x)>1|| Mathf.Abs(o.uv[j].y) > 1))
{
dCount--;
Debug.Log("uv=" + o.uv[j].x+" "+o.uv[j].y);
}
if (dCount == 0)
{
break;
}
}
o.uv2 = i.uv2;
o.uv3 = i.uv3;
o.uv4 = i.uv4;
o.colors = i.colors;
o.colors32 = i.colors32;
//边缘
o.bounds = i.bounds;
//骨骼
o.boneWeights = i.boneWeights;
o.bindposes = i.bindposes;
return o;
}
/// <summary>
/// 合并包含多个子网格的网格为只有一个子网格的网格
/// 同时更新UV
/// </summary>
/// <param name="srcMesh"></param>
/// <returns></returns>
public static Mesh MergeSubMesh(Mesh srcMesh,Rect[] texRects)
{
Mesh tarMesh = new Mesh();
List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
List<Vector3> tarNormalList = new List<Vector3>();
List<Vector2> tarUVList = new List<Vector2>();
List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表
List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表
Vector3[] srcVertexArray = srcMesh.vertices;
Vector3[] srcNormalArray = srcMesh.normals;
Vector2[] srcUvArray = srcMesh.uv;
int[] srcTrianges = srcMesh.triangles;
int srcSubmeshCount = srcMesh.subMeshCount;
//初始化
tarVertexList = initVertexOrNormalList(srcVertexArray);
tarNormalList = initVertexOrNormalList(srcNormalArray);
tarUVList = initUvList(srcUvArray);
List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
for (int i = 0; i < srcSubmeshCount; i++)
{
int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);
Debug.Log("子网格【" + i + "】拥有三角形数量为【" + (srcSubmeshTriangeArray.Length / 3) + "】顶点索引为【" + ShowArrayElements(srcSubmeshTriangeArray) + "】");
//找出当前子网格与前面的所有子网格共用的顶点列表。
int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
Debug.Log("已共用顶点索引【" + ShowArrayElements(hadSharedVertexIndexArr) + "】");
//复制共享顶点列表,添加顶点
int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
int[] newSubmeshTrianges = oldSubmeshTrianges;
if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
{
Debug.Log("添加顶点前:拥有顶点数【" + tarVertexList.Count + "】");
Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();
CopyAndAddVertexs(hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList, ref vertexIndexRef);
Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【" + ShowDictionary(vertexIndexRef) + "】");
//通过旧新顶点索引对应关系更新三角形数组
newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【" + ShowArrayElements(newSubmeshTrianges) + "】");
}
Debug.Log("更新UV前此子网格各顶点的UV值【"+ ShowUVsi(newSubmeshTrianges, tarUVList) + "】");
UpdateUv(newSubmeshTrianges, texRects[i],ref tarUVList);
Debug.Log("Rect【"+texRects[i].ToString()+"】更新UV后此子网格各顶点的UV值【" + ShowUVsi(newSubmeshTrianges, tarUVList) + "】");
//存储本此循环数据,为下一次循环作准备
srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
Debug.Log("本次【" + i + "】循环结束,未更新三角形顶点索引为【" + ShowListElements(srcBeforCurrentSubmeshTrianges) + "】");
Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
}
//组装新的网格
tarMesh.name = "Msm_" + srcMesh.name;
tarMesh.vertices = tarVertexList.ToArray();
tarMesh.normals = tarNormalList.ToArray();
tarMesh.uv = tarUVList.ToArray();
tarMesh.triangles = tarTrianges.ToArray();
return tarMesh;
}
public static string ShowUVsi(int[] newSubmeshTriange, List<Vector2> tarUVList)
{
string rs = "[";
foreach (int idx in newSubmeshTriange)
{
rs+= idx+":(" +tarUVList[idx].x+","+ tarUVList[idx].y+")|";
}
return rs+"]";
}
public static void UpdateUv(int[] newSubmeshTrianges,Rect texRect,ref List<Vector2> tarUVList)
{
List<int> vertexIndexs_noRepeat = ArrayRemoveDuplication(newSubmeshTrianges);
foreach (int idx in vertexIndexs_noRepeat)
{
float x = texRect.x + UVToZeroOne(tarUVList[idx].x) * texRect.width;
float y = texRect.y + UVToZeroOne(tarUVList[idx].y) * texRect.height;
if (idx == 0)
{
Debug.Log("idx="+idx+" x="+x+" old x="+ tarUVList[idx].x+ " UVToZeroOne=" + UVToZeroOne(tarUVList[idx].x) + " texRect="+texRect.ToString());
}
tarUVList[idx] = new Vector2(x,y);
}
}
public static float UVToZeroOne(float i)
{
float o = 0;
o = i - Mathf.Floor(i);
return o;
}
/// <summary>
/// 合并包含多个子网格的网格为只有一个子网格的网格
/// </summary>
/// <param name="srcMesh"></param>
/// <returns></returns>
public static Mesh MergeSubMesh(Mesh srcMesh)
{
Mesh tarMesh = new Mesh();
List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
List<Vector3> tarNormalList = new List<Vector3>();
List<Vector2> tarUVList = new List<Vector2>();
List<Vector2> tarUV2List = new List<Vector2>();
List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表
List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表
Vector3[] srcVertexArray = srcMesh.vertices;
Vector3[] srcNormalArray = srcMesh.normals;
Vector2[] srcUvArray = srcMesh.uv;
int[] srcTrianges = srcMesh.triangles;
int srcSubmeshCount = srcMesh.subMeshCount;
//初始化
tarVertexList = initVertexOrNormalList(srcVertexArray);
tarNormalList = initVertexOrNormalList(srcNormalArray);
tarUVList = initUvList(srcUvArray);
tarUV2List = initUvList(tarUVList.ToArray());
List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
for (int i = 0; i < srcSubmeshCount; i++)
{
int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);
Debug.Log("子网格【"+i+"】拥有三角形数量为【"+(srcSubmeshTriangeArray.Length/3)+"】顶点索引为【"+ ShowArrayElements(srcSubmeshTriangeArray) + "】");
//找出当前子网格与前面的所有子网格共用的顶点列表。
int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
Debug.Log("已共用顶点索引【"+ ShowArrayElements(hadSharedVertexIndexArr) +"】");
//复制共享顶点列表,添加顶点
int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
int[] newSubmeshTrianges = oldSubmeshTrianges;
if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
{
Debug.Log("添加顶点前:拥有顶点数【"+ tarVertexList.Count+ "】");
Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();
CopyAndAddVertexs(i,hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList,ref tarUV2List, ref vertexIndexRef);
Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【"+ ShowDictionary(vertexIndexRef)+ "】");
//通过旧新顶点索引对应关系更新三角形数组
newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【"+ ShowArrayElements(newSubmeshTrianges)+ "】");
}
//根据三角形顶点列表更新UV2,标识顶点来源自哪个子网格
UdateUv2(i, newSubmeshTrianges, ref tarUV2List);
//存储本此循环数据,为下一次循环作准备
srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
Debug.Log("本次【"+i+"】循环结束,未更新三角形顶点索引为【"+ShowListElements(srcBeforCurrentSubmeshTrianges)+"】");
Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
}
//组装新的网格
tarMesh.name = "Msm_"+srcMesh.name;
tarMesh.vertices = tarVertexList.ToArray();
tarMesh.normals = tarNormalList.ToArray();
tarMesh.uv = tarUVList.ToArray();
tarMesh.uv2 = tarUV2List.ToArray();
tarMesh.triangles = tarTrianges.ToArray();
return tarMesh;
}
/// <summary>
/// 合并包含多个子网格的网格为只有一个子网格的网格
/// </summary>
/// <param name="srcMesh"></param>
/// <returns></returns>
public static Mesh MergeSubMeshStoreRect(Mesh srcMesh,Rect[] rects)
{
Mesh tarMesh = new Mesh();
List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
List<Vector3> tarNormalList = new List<Vector3>();
List<Vector2> tarUVList = new List<Vector2>();
List<Vector2> tarUV2List = new List<Vector2>();
List<Vector2> tarUV3List = new List<Vector2>();
List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表
List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表
Vector3[] srcVertexArray = srcMesh.vertices;
Vector3[] srcNormalArray = srcMesh.normals;
Vector2[] srcUvArray = srcMesh.uv;
int[] srcTrianges = srcMesh.triangles;
int srcSubmeshCount = srcMesh.subMeshCount;
//初始化
tarVertexList = initVertexOrNormalList(srcVertexArray);
tarNormalList = initVertexOrNormalList(srcNormalArray);
tarUVList = initUvList(srcUvArray);
tarUV2List = initUvList(tarUVList.ToArray());
tarUV3List = initUvList(tarUVList.ToArray());
List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
for (int i = 0; i < srcSubmeshCount; i++)
{
int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);
Debug.Log("子网格【" + i + "】拥有三角形数量为【" + (srcSubmeshTriangeArray.Length / 3) + "】顶点索引为【" + ShowArrayElements(srcSubmeshTriangeArray) + "】");
//找出当前子网格与前面的所有子网格共用的顶点列表。
int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
Debug.Log("已共用顶点索引【" + ShowArrayElements(hadSharedVertexIndexArr) + "】");
//复制共享顶点列表,添加顶点
int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
int[] newSubmeshTrianges = oldSubmeshTrianges;
if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
{
Debug.Log("添加顶点前:拥有顶点数【" + tarVertexList.Count + "】");
Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();
CopyAndAddVertexs(i, hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList, ref tarUV2List, ref vertexIndexRef);
Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【" + ShowDictionary(vertexIndexRef) + "】");
//通过旧新顶点索引对应关系更新三角形数组
newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【" + ShowArrayElements(newSubmeshTrianges) + "】");
}
//根据三角形顶点列表更新UV2,标识顶点来源自哪个子网格
//UdateUv2(i, newSubmeshTrianges, ref tarUV2List);
StoreRectToUV(newSubmeshTrianges, rects[i], ref tarUV2List, ref tarUV3List);
//存储本此循环数据,为下一次循环作准备
srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
Debug.Log("本次【" + i + "】循环结束,未更新三角形顶点索引为【" + ShowListElements(srcBeforCurrentSubmeshTrianges) + "】");
Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
}
//组装新的网格
tarMesh.name = "Msm_" + srcMesh.name;
tarMesh.vertices = tarVertexList.ToArray();
tarMesh.normals = tarNormalList.ToArray();
tarMesh.uv = tarUVList.ToArray();
tarMesh.uv2 = tarUV2List.ToArray();
tarMesh.uv3 = tarUV3List.ToArray();
tarMesh.triangles = tarTrianges.ToArray();
return tarMesh;
}
public static void StoreRectToUV(int[] newSubmeshTrianges,Rect rect,ref List<Vector2> uv2,ref List<Vector2> uv3)
{
if (newSubmeshTrianges != null && newSubmeshTrianges.Length > 0)
{
foreach (int vIdx in newSubmeshTrianges)
{
uv2[vIdx] = new Vector2(rect.x, rect.y);
uv3[vIdx] = new Vector2(rect.width, rect.height);
}
}
}
public static void UdateUv2(int currSubmeshId, int[] newSubmeshTrianges, ref List<Vector2> uv2)
{
if (newSubmeshTrianges != null && newSubmeshTrianges.Length > 0)
{
foreach (int vIdx in newSubmeshTrianges)
{
uv2[vIdx] = new Vector2((float)currSubmeshId,0.0f);
}
}
//Debug.Log("UV2="+ ShowUVs(uv2));
}
public static string ShowUVs(List<Vector2> uv2)
{
string rs = "";
for(int i=0;i<uv2.Count; i++)
{
rs += i+"["+uv2[i].x+","+uv2[i].y+"] ";
}
return rs;
}
public static int[] UpdateTrianges(int[] oldTrianges,Dictionary<int,int> idxRef)
{
int[] newTrianges = new int[oldTrianges.Length];
for (int i = 0; i < oldTrianges.Length; i++)
{
if (idxRef.ContainsKey(oldTrianges[i]))
{
newTrianges[i] = idxRef[oldTrianges[i]];
}
else
{
newTrianges[i] = oldTrianges[i];
}
}
return newTrianges;
}
public static int[] CopyArray(int[] arr)
{
int[] o = new int[arr.Length];