Skip to content

Instantly share code, notes, and snippets.

@empika
Created November 17, 2017 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save empika/f1ec6735b1959738e79c30c6d88088ae to your computer and use it in GitHub Desktop.
Save empika/f1ec6735b1959738e79c30c6d88088ae to your computer and use it in GitHub Desktop.
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Rendering;
namespace Code.Utils
{
public class MeshUtils
{
public static GameObject SpriteToMesh(GameObject spriteObject)
{
SpriteRenderer spriteRenderer = spriteObject.GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
{
return null;
}
GameObject content = new GameObject(spriteObject.name);
content.isStatic = true;
Transform holderTransform = content.transform;
holderTransform.SetParent(spriteObject.transform);
holderTransform.localPosition = Vector3.zero;
holderTransform.localScale = Vector3.one;
holderTransform.localRotation = Quaternion.identity;
Mesh mesh = new Mesh();
mesh.vertices = Array.ConvertAll(spriteRenderer.sprite.vertices, i => (Vector3)i);
mesh.uv = spriteRenderer.sprite.uv;
mesh.triangles = Array.ConvertAll(spriteRenderer.sprite.triangles, i => (int)i);
mesh.RecalculateBounds();
mesh.RecalculateNormals();
mesh.RecalculateTangents();
MeshRenderer meshRenderer = content.AddComponent<MeshRenderer>();
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetTexture("_MainTex", spriteRenderer.sprite.texture);
block.SetColor("_Color", spriteRenderer.color);
meshRenderer.SetPropertyBlock(block);
meshRenderer.sharedMaterial = spriteRenderer.sharedMaterial;
meshRenderer.sortingLayerID = spriteRenderer.sortingLayerID;
meshRenderer.sortingOrder = spriteRenderer.sortingOrder;
meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
meshRenderer.receiveShadows = false;
spriteRenderer.enabled = false;
MeshFilter meshFilter = content.AddComponent<MeshFilter>();
meshFilter.sharedMesh = mesh;
return content;
}
public static GameObject CombineMeshes(GameObject parentObject, GameObject[] objectsToCombine, string name,
Vector3 position, Material material, Texture2D texture)
{
if (objectsToCombine.Length > 0)
{
GameObject combinedObject = new GameObject();
combinedObject.transform.SetParent(parentObject.transform);
combinedObject.transform.localPosition = Vector3.zero;
combinedObject.transform.localScale = Vector3.one;
combinedObject.name = name;
CombineInstance[] combine = new CombineInstance[objectsToCombine.Length];
for (int i = 0; i < objectsToCombine.Length; i++)
{
combine[i].mesh = objectsToCombine[i].GetComponent<MeshFilter>().sharedMesh;
Matrix4x4 parentTransform = parentObject.transform.worldToLocalMatrix;
combine[i].transform = parentTransform * objectsToCombine[i].transform.localToWorldMatrix;
}
combinedObject.isStatic = true;
MeshFilter filter = combinedObject.AddComponent<MeshFilter>();
MeshRenderer renderer = combinedObject.AddComponent<MeshRenderer>();
filter.sharedMesh = new Mesh();
filter.sharedMesh.name = "Tiles";
filter.sharedMesh.CombineMeshes(combine, true);
filter.sharedMesh.RecalculateNormals();
filter.sharedMesh.RecalculateBounds();
filter.sharedMesh.RecalculateTangents();
MeshRenderer currentMeshRenderer = objectsToCombine[0].GetComponent<MeshRenderer>();
MaterialPropertyBlock currentPropertyBlock = new MaterialPropertyBlock();
currentMeshRenderer.GetPropertyBlock(currentPropertyBlock);
renderer.sharedMaterial = material;
renderer.sortingLayerID = currentMeshRenderer.sortingLayerID;
renderer.sortingOrder = currentMeshRenderer.sortingOrder;
renderer.shadowCastingMode = ShadowCastingMode.Off;
renderer.receiveShadows = false;
MaterialPropertyBlock block = new MaterialPropertyBlock();
renderer.GetPropertyBlock(block);
block.SetTexture("_MainTex", texture);
block.SetColor("_Color", currentPropertyBlock.GetVector("_Color"));
renderer.SetPropertyBlock(block);
#if UNITY_EDITOR
UnityEditor.Unwrapping.GenerateSecondaryUVSet(filter.sharedMesh);
UnityEditor.EditorUtility.SetDirty(combinedObject);
#endif
return combinedObject;
}
return null;
}
}
}
using UnityEngine;
namespace Code.Game.Behaviours.Sprites
{
[ExecuteInEditMode]
public class SpriteBaker : MonoBehaviour
{
public GameObject CombinedSprites;
public Material Material;
public Texture2D Texture;
public void Start()
{
if (CombinedSprites)
{
MeshRenderer renderer = CombinedSprites.GetComponentInChildren<MeshRenderer>();
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetTexture("_MainTex", Texture);
renderer.SetPropertyBlock(block);
renderer.sharedMaterial = Material;
}
}
}
}
using System.Collections.Generic;
using Code.Game.Behaviours.Sprites;
using Code.Game.World.Environment;
using Code.Utils;
using UnityEditor;
using UnityEngine;
namespace Editor.Game.Behaviours.Sprites
{
[CustomEditor(typeof(SpriteBaker))]
public class SpriteBakerEditor : UnityEditor.Editor
{
private SpriteBaker spriteBaker;
private SerializedProperty materialProperty;
private SerializedProperty textureProperty;
private SerializedProperty combinedSpritesProperty;
private void OnEnable()
{
spriteBaker = (SpriteBaker) target;
materialProperty = serializedObject.FindProperty("Material");
textureProperty = serializedObject.FindProperty("Texture");
combinedSpritesProperty = serializedObject.FindProperty("CombinedSprites");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(materialProperty);
EditorGUILayout.PropertyField(textureProperty);
EditorGUILayout.PropertyField(combinedSpritesProperty);
if (GUILayout.Button("Create combined mesh"))
{
if (spriteBaker.CombinedSprites != null)
{
DestroyImmediate(spriteBaker.CombinedSprites);
spriteBaker.CombinedSprites = null;
}
createCombinedMesh();
}
if (GUILayout.Button("Save mesh"))
{
if (spriteBaker.CombinedSprites != null)
{
MeshFilter meshFilter = spriteBaker.CombinedSprites.GetComponent<MeshFilter>();
if (meshFilter && meshFilter.sharedMesh)
{
if (PrefabUtility.GetPrefabType(meshFilter.sharedMesh) == PrefabType.None)
{
ProjectWindowUtil.CreateAsset(meshFilter.sharedMesh,
string.Format("{0}_{1}_mesh.asset", spriteBaker.gameObject.name,
spriteBaker.Material.name));
}
// TODO, what do we do if the mesh already exists?
}
else
{
Debug.Log("No mesh filter or mesh");
}
}
}
if (GUILayout.Button("Revert to sprites"))
{
if (spriteBaker.CombinedSprites != null)
{
DestroyImmediate(spriteBaker.CombinedSprites);
spriteBaker.CombinedSprites = null;
foreach (SpriteRenderer spriteRenderer in spriteBaker.GetComponentsInChildren<SpriteRenderer>())
{
spriteRenderer.enabled = true;
}
}
}
serializedObject.ApplyModifiedProperties();
}
private void createCombinedMesh()
{
List<GameObject> gameObjectsToCombine = new List<GameObject>();
SpriteRenderer[] spriteRenderers = spriteBaker.GetComponentsInChildren<SpriteRenderer>();
foreach (SpriteRenderer spriteRenderer in spriteRenderers)
{
if (spriteRenderer == null) continue;
Animator animator = spriteRenderer.gameObject.GetComponent<Animator>();
if (animator) continue;
string materialCompareName = spriteRenderer.sharedMaterial.name.Replace(" (Instance)", "");
if (materialCompareName == spriteBaker.Material.name)
{
GameObject meshGameObject = MeshUtils.SpriteToMesh(spriteRenderer.gameObject);
if (meshGameObject != null)
{
gameObjectsToCombine.Add(meshGameObject);
}
}
}
Debug.Log("Combining " + gameObjectsToCombine.Count + " meshes for: " + spriteBaker.Material.name);
spriteBaker.CombinedSprites = MeshUtils.CombineMeshes(
spriteBaker.gameObject,
gameObjectsToCombine.ToArray(),
string.Format("Mesh_{0}", spriteBaker.Material.name),
Vector3.zero,
spriteBaker.Material,
spriteBaker.Texture
);
Debug.Log("Destroying individual sprite meshes");
for (int i = 0; i < gameObjectsToCombine.Count; i++)
{
DestroyImmediate(gameObjectsToCombine[i]);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment