Skip to content

Instantly share code, notes, and snippets.

@zcyemi
Created May 17, 2018 10:50
Show Gist options
  • Save zcyemi/1c9b4da7ff68e48106c30d1aa0151cec to your computer and use it in GitHub Desktop.
Save zcyemi/1c9b4da7ff68e48106c30d1aa0151cec to your computer and use it in GitHub Desktop.
Unity editor script for merging sprite render sprites into a single large mesh.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
public class SpriteMeshMerger
{
[MenuItem("MeshGen/BuildMesh")]
public static void TestGenMesh()
{
var selections = Selection.gameObjects;
CombinedMesh(selections);
}
private static readonly float SORTING_ORDER_SCALING = 0.000001f;
private static readonly string MESH_PATH = "Assets/SpriteMeshGen/";
public static void CombinedMesh(GameObject[] objs)
{
Dictionary<Transform, SpriteRenderer> sprdic = new Dictionary<Transform, SpriteRenderer>();
Vector3 posaccu = Vector3.zero;
string atlasName = null;
foreach (var obj in objs)
{
var sr = obj.GetComponent<SpriteRenderer>();
if (sr == null) continue;
if (sr.sprite == null) continue;
atlasName = sr.sprite.texture.name;
sprdic.Add(obj.transform, sr);
posaccu += obj.transform.position;
}
if (sprdic.Count == 0)
{
Debug.Log("no valid sprite render");
return;
}
Vector3 meshPos = posaccu / sprdic.Count;
meshPos.z = 0;
List<Vector3> vert = new List<Vector3>();
List<Vector2> uv = new List<Vector2>();
List<int> indices = new List<int>();
List<Color> color = new List<Color>();
int totalVertCount = 0;
foreach (var pair in sprdic)
{
var spr = pair.Value.sprite;
Vector2 textureTexel = spr.texture.texelSize;
var sprcenter = spr.bounds.center;
Debug.Log("SpriteBound center:" + FmtVec2(sprcenter));
Debug.Log("SpriteBound size:" + FmtVec2(spr.bounds.size));
var rootpos = pair.Key.position - meshPos + sprcenter;
Color srColor = pair.Value.color;
Vector3 scale = pair.Key.lossyScale;
Quaternion rota = pair.Key.rotation;
Vector2 flip = new Vector2((pair.Value.flipX ? -1f : 1f), (pair.Value.flipY ? -1F : 1F));
int sprVertCount = spr.vertices.Length;
int sprVertCountTiled = 0;
var sr = pair.Value;
Vector2 textureSize = new Vector2(spr.texture.width, spr.texture.height);
List<Vector3> sprVertices = spr.vertices.Select(v => new Vector3(v.x, v.y, 0)).ToList();
if (sr.drawMode == SpriteDrawMode.Simple)
{
Debug.Log("SpriteCenter: " + FmtVec2(sprcenter));
foreach (var v in sprVertices)
{
Vector3 sprv = v;
//F
sprv.x *= flip.x;
sprv.y *= flip.y;
//S
sprv.x *= scale.x;
sprv.y *= scale.y;
//R
sprv = rota * sprv;
//T
sprv.x -= sprcenter.x;
sprv.y -= sprcenter.y;
//Depth;
sprv.z = pair.Value.sortingOrder * SORTING_ORDER_SCALING;
sprv += rootpos;
vert.Add(sprv);
color.Add(srColor);
}
var indlen = spr.triangles.Length;
for (var i = 0; i < indlen; i++)
{
indices.Add(totalVertCount + spr.triangles[i]);
}
uv.AddRange(spr.uv);
totalVertCount += sprVertices.Count;
}
else if (sr.drawMode == SpriteDrawMode.Tiled)
{
Vector2 tileSize = spr.rect.size / spr.pixelsPerUnit;
Debug.Log("unitsize: " + tileSize.x + " " + tileSize.y);
Vector2 itemCount = new Vector2(sr.size.x / tileSize.x, sr.size.y / tileSize.y);
Vector2 rootPosOffset = itemCount - Vector2.one;
rootPosOffset.x *= -.5f * tileSize.x;
rootPosOffset.y *= -.5f * tileSize.y;
int tileCountX = Mathf.CeilToInt(itemCount.x);
int tileCountY = Mathf.CeilToInt(itemCount.y);
Debug.Log(tileCountX + " " + tileCountY);
Debug.Log(itemCount.x + " " + itemCount.y);
for (int tiley = 0; tiley < tileCountY; tiley++)
{
for (int tilex = 0; tilex < tileCountX; tilex++)
{
bool clipx = false;
bool clipy = false;
Vector2 clipMax = Vector2.zero;
if (tilex == tileCountX - 1)
{
float xoff = itemCount.x - tilex;
clipMax.x = (xoff - 0.5f) * tileSize.x;
clipx = true;
}
if (tiley == tileCountY - 1)
{
float yoff = itemCount.y - tiley;
clipMax.y = (yoff - 0.5f) * tileSize.y;
clipy = true;
}
int index = 0;
foreach (var v in sprVertices)
{
Vector3 sprv = v;
Vector2 uvcoord = spr.uv[index];
if (clipx)
{
if (sprv.x > clipMax.x)
{
uvcoord.x -= (sprv.x - clipMax.x) * spr.pixelsPerUnit / textureSize.x;
sprv.x = clipMax.x;
}
}
if (clipy)
{
if (sprv.y > clipMax.y)
{
uvcoord.y -= (sprv.y - clipMax.y) * spr.pixelsPerUnit / textureSize.y;
sprv.y = clipMax.y;
}
}
uv.Add(uvcoord);
color.Add(srColor);
//TileOffset
sprv.x += rootPosOffset.x + tilex * tileSize.x;
sprv.y += rootPosOffset.y + tiley * tileSize.y;
//F
sprv.x *= flip.x;
sprv.y *= flip.y;
//S
sprv.x *= scale.x;
sprv.y *= scale.y;
//R
sprv = rota * sprv;
//T
//Depth;
sprv.z = pair.Value.sortingOrder * SORTING_ORDER_SCALING;
sprv += rootpos;
vert.Add(sprv);
index++;
}
var indlen = spr.triangles.Length;
for (var i = 0; i < indlen; i++)
{
indices.Add(totalVertCount + spr.triangles[i]);
}
totalVertCount += sprVertices.Count;
}
}
}
}
Mesh mesh = new Mesh();
mesh.SetVertices(vert);
mesh.SetUVs(0, uv);
mesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0);
mesh.SetColors(color);
mesh.UploadMeshData(false);
string gname = "SprMesh_" + atlasName + "_mesh";
GameObject g = GameObject.Find(gname) ?? new GameObject("SprMesh_" + atlasName + "_mesh");
if (g.GetComponent<MeshRenderer>() == null) g.AddComponent<MeshRenderer>();
if (g.GetComponent<MeshFilter>() == null) g.AddComponent<MeshFilter>();
g.GetComponent<MeshFilter>().mesh = mesh;
g.transform.position = meshPos;
AssetDatabase.CreateAsset(mesh, MESH_PATH + gname + System.DateTime.UtcNow.ToFileTime() + ".asset");
PrefabUtility.CreatePrefab(MESH_PATH + gname + System.DateTime.UtcNow.ToFileTime() + ".prefab", g, ReplacePrefabOptions.Default);
}
public static string FmtVec3(Vector3 v)
{
return string.Format("<{0},{1},{2}>", v.x, v.y, v.z);
}
public static string FmtVec2(Vector2 v)
{
return string.Format("<{0},{1}>", v.x, v.y);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment