Script to combine Animated Mesh and Textures

Discussion in 'External Tools' started by duhprey, Feb 16, 2012.

  1. duhprey

    duhprey

    Member

    Joined:
    Nov 13, 2009
    Messages:
    145
    Hi,

    Here's a script (based on some others I found here and on unifycommunity) that you can place on the root and will combine all the children Skinned meshes into a single one as well as combining all their textures into an atlas. Turning it into one draw call. It does it once while in the editor, creating new assets for the combination so that you can save them into your project for later.

    Add it under an Editor/ folder. It'll create Assets->CombineSkinnedMeshes. Highlight the object you want to combine in the Hiearchy view and then click that menu item. All the generated combined assets will go in the top level of your asset folder.

    Good luck!

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5. using System.IO;
    6.  
    7. public class EditorCombine : MonoBehaviour {
    8.  
    9.     static public int maxAtlasSize = 2048;
    10.  
    11.     [MenuItem ("Assets/CombineSkinnedMeshes")]
    12.     static void CombineTheMeshes(){
    13.         if (Selection.activeGameObject == null) return;
    14.  
    15.         SkinnedMeshRenderer[] SMRs;
    16.  
    17.         int vertCount = 0;
    18.         int normCount = 0;
    19.         int tanCount = 0;
    20.         int triCount = 0;
    21.         int uvCount = 0;
    22.         int boneCount = 0;
    23.         int bpCount = 0;
    24.         int bwCount = 0;
    25.  
    26.         Transform[] bones;
    27.         Matrix4x4[] bindPoses;
    28.         BoneWeight[] weights;
    29.  
    30.         Vector3[] verts;
    31.         Vector3[] norms;
    32.         Vector4[] tans;
    33.         int[] tris;
    34.         Vector2[] uvs;
    35.         Texture2D[] textures;
    36.  
    37.         int vertOffset = 0;
    38.         int normOffset = 0;
    39.         int tanOffset = 0;
    40.         int triOffset = 0;
    41.         int uvOffset = 0;
    42.         int meshOffset = 0;
    43.    
    44.         int  boneSplit = 0;
    45.         int bNum = 0;
    46.  
    47.         int[] bCount;
    48.  
    49.         SMRs = Selection.activeGameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
    50.         foreach (SkinnedMeshRenderer smr in SMRs){
    51.             vertCount += smr.sharedMesh.vertices.Length;
    52.             normCount += smr.sharedMesh.normals.Length;
    53.             tanCount += smr.sharedMesh.tangents.Length;
    54.             triCount += smr.sharedMesh.triangles.Length;
    55.             uvCount += smr.sharedMesh.uv.Length;
    56.             boneCount += smr.bones.Length;
    57.             bpCount += smr.sharedMesh.bindposes.Length;
    58.             bwCount += smr.sharedMesh.boneWeights.Length;
    59.             bNum++;
    60.         }
    61.         bCount = new int[3];
    62.         bones = new Transform[boneCount];
    63.         weights = new BoneWeight[bwCount];
    64.         bindPoses = new Matrix4x4[bpCount];
    65.         textures = new Texture2D[bNum];
    66.        
    67.         foreach (SkinnedMeshRenderer smr in SMRs){
    68.             for(int b1 = 0; b1 < smr.bones.Length; b1++){
    69.                 bones[bCount[0]] = smr.bones[b1];
    70.                 bCount[0]++;
    71.             }
    72.             for(int b2 = 0; b2 < smr.sharedMesh.boneWeights.Length; b2++){
    73.                 weights[bCount[1]] = smr.sharedMesh.boneWeights[b2];
    74.                 weights[bCount[1]].boneIndex0 += boneSplit;
    75.                 weights[bCount[1]].boneIndex1 += boneSplit;
    76.                 weights[bCount[1]].boneIndex2 += boneSplit;
    77.                 weights[bCount[1]].boneIndex3 += boneSplit;
    78.                 bCount[1]++;
    79.             }
    80.             for(int b3 = 0; b3 < smr.sharedMesh.bindposes.Length; b3++){
    81.                 bindPoses[bCount[2]] = smr.sharedMesh.bindposes[b3];
    82.                 bCount[2]++;
    83.             }
    84.             boneSplit += smr.bones.Length;
    85.         }
    86.         verts = new Vector3[vertCount];
    87.         norms = new Vector3[normCount];
    88.         tans = new Vector4[tanCount];
    89.         tris = new int[triCount];
    90.         uvs = new Vector2[uvCount];
    91.        
    92.         foreach (SkinnedMeshRenderer smr in SMRs){
    93.             foreach (int i in smr.sharedMesh.triangles){
    94.                 tris[triOffset++] = i + vertOffset;
    95.             }
    96.             foreach (Vector3 v in smr.sharedMesh.vertices){
    97.                 verts[vertOffset++] = v;
    98.             }
    99.             foreach (Vector3 n in smr.sharedMesh.normals){
    100.                 norms[normOffset++] = n;
    101.             }
    102.             foreach (Vector4 t in smr.sharedMesh.tangents){
    103.                 tans[tanOffset++] = t;
    104.             }
    105.             foreach (Vector2 uv in smr.sharedMesh.uv){
    106.                 uvs[uvOffset++] = uv;
    107.             }
    108.             textures[meshOffset] = (Texture2D) smr.sharedMaterial.mainTexture;
    109.             string path = AssetDatabase.GetAssetPath (smr.sharedMaterial.mainTexture);
    110.             TextureImporter imp = (TextureImporter) AssetImporter.GetAtPath (path);
    111.             if (!imp.isReadable) {
    112.                 imp.isReadable = true;
    113.                 AssetDatabase.Refresh ();
    114.                 AssetDatabase.ImportAsset (path);
    115.             }
    116.             meshOffset++;
    117.             smr.enabled = false;
    118.         }
    119.  
    120.         Texture2D tx = new Texture2D (1,1);
    121.         Rect[] r = tx.PackTextures (textures, 0, maxAtlasSize);
    122.         File.WriteAllBytes (Application.dataPath + "/" + Selection.activeGameObject.name + ".png", tx.EncodeToPNG());
    123.         AssetDatabase.Refresh ();
    124.         tx = (Texture2D) AssetDatabase.LoadAssetAtPath ("Assets/" + Selection.activeGameObject.name + ".png", typeof(Texture2D));
    125.  
    126.         uvOffset = 0;
    127.         meshOffset = 0;
    128.         foreach (SkinnedMeshRenderer smr in SMRs) {
    129.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    130.                 uvs[uvOffset].x = Mathf.Lerp (r[meshOffset].xMin, r[meshOffset].xMax, uv.x);
    131.                 uvs[uvOffset].y = Mathf.Lerp (r[meshOffset].yMin, r[meshOffset].yMax, uv.y);
    132.                 uvOffset ++;
    133.             }
    134.             meshOffset ++;
    135.         }
    136.  
    137.         Material mat = new Material (Shader.Find("Diffuse"));
    138.         mat.mainTexture = tx;
    139.         AssetDatabase.CreateAsset(mat, "Assets/" + Selection.activeGameObject.name + ".mat");
    140.  
    141.         //New Mesh
    142.         Mesh me = new Mesh();
    143.         me.vertices = verts;
    144.         me.normals = norms;
    145.         me.tangents = tans;
    146.         me.boneWeights = weights;
    147.         me.uv = uvs;
    148.         me.triangles = tris;
    149.         AssetDatabase.CreateAsset(me, "Assets/" + Selection.activeGameObject.name + "mesh.asset");
    150.         me.bindposes = bindPoses;
    151.    
    152.         SkinnedMeshRenderer newSMR = Selection.activeGameObject.AddComponent<SkinnedMeshRenderer>();
    153.    
    154.         newSMR.sharedMesh = me;
    155.         newSMR.bones = bones;
    156.         newSMR.updateWhenOffscreen = true;
    157.         Selection.activeGameObject.renderer.material = mat;
    158.     }
    159. }
    160.  
  2. Morning

    Morning

    Member

    Joined:
    Feb 4, 2012
    Messages:
    1,138
    Most definitely going to help. Thanks a lot.
  3. Joel Santos

    Joel Santos

    Member

    Joined:
    Feb 23, 2010
    Messages:
    116
    Hi there.
    I was really hopping for something like this and your script looks like a life saver...
    ...but, it's not working for me right now. It gives this message when I try to use it..
    Code (csharp):
    1. InvalidCastException: Cannot cast from source type to destination type.
    2. EditorCombine.CombineTheMeshes () (at Assets/Editor/EditorCombine .cs:213)
    Am I doing something wrong... or...

    Edit: Hm I think it's probably because the material as no texture associated. Will change the materials and try again
    Last edited: Feb 16, 2012
  4. duhprey

    duhprey

    Member

    Joined:
    Nov 13, 2009
    Messages:
    145
    You're right. Its because there aren't textures. I forgot that I meant to write a workaround to that... its a little more complicated since once its combined it will get a texture. So its still probably better if you decide a texture yourself, even if it's a solid white or something.
  5. Joel Santos

    Joel Santos

    Member

    Joined:
    Feb 23, 2010
    Messages:
    116
    Hm I've tried with different models and it generates 3 files in the root as you said but the model it generates it's empty.. Again.. Am I doing something wrong?
  6. duhprey

    duhprey

    Member

    Joined:
    Nov 13, 2009
    Messages:
    145
    are there any errors? You could perhaps try adding the skinmeshrenderer component manually and add in the generated mesh and material. I haven't tried this in very many cases... so there could be something else I missed.
  7. Joel Santos

    Joel Santos

    Member

    Joined:
    Feb 23, 2010
    Messages:
    116
    Anyone succeeded with this script?
  8. duhprey

    duhprey

    Member

    Joined:
    Nov 13, 2009
    Messages:
    145
    If you feel like sending a sample model, id be happy to try it and see what's wrong.
  9. sanpats

    sanpats

    Member

    Joined:
    Aug 24, 2011
    Messages:
    104
    I tried to use your script with Arteria's Medieval Base Model Pack, but when combine the UV of newly generated Skinned Mesh Renderer is mess up. Any solution?
  10. sanpats

    sanpats

    Member

    Joined:
    Aug 24, 2011
    Messages:
    104
    I tested using Debug.Log and UV coordinates of old Skinned Meshes are outside the range of 0 to 1, weird.
  11. duhprey

    duhprey

    Member

    Joined:
    Nov 13, 2009
    Messages:
    145
    Normally you put the ranges outside 0 to 1 when repeating a texture multiple times along an object. I'm not sure if that's really what they are doing, but that would create a problem for texture combining. I'm not sure if there's a solution...
  12. sanpats

    sanpats

    Member

    Joined:
    Aug 24, 2011
    Messages:
    104
    I solved the problem just add lines checking if the values are outside of [0,1]. If so, just add or subtract 1 until it is in range. And this script can be change to make it combine script on the fly during runtime easily. I have to untick "Play automatically" or something on animation component to make the animation work though.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5. using System.IO;
    6.  
    7. public class CombineSkinnedMeshesWithAtlas : MonoBehaviour {
    8.  
    9.     public int maxAtlasSize = 2048;
    10.     private bool initFlag;
    11.    
    12.     void Start () {
    13.         initFlag = true;   
    14.     }
    15.    
    16.     void Update () {
    17.         if (initFlag) {
    18.             CombineTheMeshes(gameObject);
    19.             initFlag = false;
    20.         }
    21.         else animation.CrossFade("Run");
    22.     }
    23.    
    24.     void CombineTheMeshes(GameObject source){
    25.         if (source == null) return;
    26.  
    27.         SkinnedMeshRenderer[] SMRs;
    28.  
    29.         int vertCount = 0;
    30.         int normCount = 0;
    31.         int tanCount = 0;
    32.         int triCount = 0;
    33.         int uvCount = 0;
    34.         int boneCount = 0;
    35.         int bpCount = 0;
    36.         int bwCount = 0;
    37.  
    38.         Transform[] bones;
    39.         Matrix4x4[] bindPoses;
    40.         BoneWeight[] weights;
    41.  
    42.         Vector3[] verts;
    43.         Vector3[] norms;
    44.         Vector4[] tans;
    45.         int[] tris;
    46.         Vector2[] uvs;
    47.         Texture2D[] textures;
    48.  
    49.         int vertOffset = 0;
    50.         int normOffset = 0;
    51.         int tanOffset = 0;
    52.         int triOffset = 0;
    53.         int uvOffset = 0;
    54.         int meshOffset = 0;
    55.  
    56.         int  boneSplit = 0;
    57.         int bNum = 0;
    58.  
    59.         int[] bCount;
    60.  
    61.         SMRs = source.GetComponentsInChildren<SkinnedMeshRenderer>();
    62.         foreach (SkinnedMeshRenderer smr in SMRs){
    63.             vertCount += smr.sharedMesh.vertices.Length;
    64.             normCount += smr.sharedMesh.normals.Length;
    65.             tanCount += smr.sharedMesh.tangents.Length;
    66.             triCount += smr.sharedMesh.triangles.Length;
    67.             uvCount += smr.sharedMesh.uv.Length;
    68.             boneCount += smr.bones.Length;
    69.             bpCount += smr.sharedMesh.bindposes.Length;
    70.             bwCount += smr.sharedMesh.boneWeights.Length;
    71.             bNum++;
    72.         }
    73.         bCount = new int[3];
    74.         bones = new Transform[boneCount];
    75.         weights = new BoneWeight[bwCount];
    76.         bindPoses = new Matrix4x4[bpCount];
    77.         textures = new Texture2D[bNum];
    78.  
    79.         foreach (SkinnedMeshRenderer smr in SMRs){
    80.             for(int b1 = 0; b1 < smr.bones.Length; b1++){
    81.                 bones[bCount[0]] = smr.bones[b1];
    82.                 bCount[0]++;
    83.             }
    84.             for(int b2 = 0; b2 < smr.sharedMesh.boneWeights.Length; b2++){
    85.                 weights[bCount[1]] = smr.sharedMesh.boneWeights[b2];
    86.                 weights[bCount[1]].boneIndex0 += boneSplit;
    87.                 weights[bCount[1]].boneIndex1 += boneSplit;
    88.                 weights[bCount[1]].boneIndex2 += boneSplit;
    89.                 weights[bCount[1]].boneIndex3 += boneSplit;
    90.                 bCount[1]++;
    91.             }
    92.             for(int b3 = 0; b3 < smr.sharedMesh.bindposes.Length; b3++){
    93.                 bindPoses[bCount[2]] = smr.sharedMesh.bindposes[b3];
    94.                 bCount[2]++;
    95.             }
    96.             boneSplit += smr.bones.Length;
    97.         }
    98.         verts = new Vector3[vertCount];
    99.         norms = new Vector3[normCount];
    100.         tans = new Vector4[tanCount];
    101.         tris = new int[triCount];
    102.         uvs = new Vector2[uvCount];
    103.  
    104.         foreach (SkinnedMeshRenderer smr in SMRs){
    105.             foreach (int i in smr.sharedMesh.triangles){
    106.                 tris[triOffset++] = i + vertOffset;
    107.             }
    108.             foreach (Vector3 v in smr.sharedMesh.vertices){
    109.                 verts[vertOffset++] = v;
    110.             }
    111.             foreach (Vector3 n in smr.sharedMesh.normals){
    112.                 norms[normOffset++] = n;
    113.             }
    114.             foreach (Vector4 t in smr.sharedMesh.tangents){
    115.                 tans[tanOffset++] = t;
    116.             }
    117.             foreach (Vector2 uv in smr.sharedMesh.uv){
    118.                 uvs[uvOffset++] = uv;              
    119.             }
    120.             textures[meshOffset] = (Texture2D) smr.sharedMaterial.mainTexture;
    121.             string path = AssetDatabase.GetAssetPath (smr.sharedMaterial.mainTexture);
    122.             TextureImporter imp = (TextureImporter) AssetImporter.GetAtPath (path);
    123.             if (!imp.isReadable) {
    124.                 imp.isReadable = true;
    125.                 AssetDatabase.Refresh ();
    126.                 AssetDatabase.ImportAsset (path);
    127.             }
    128.             meshOffset++;
    129.             smr.enabled = false;
    130.         }
    131.  
    132.         Texture2D tx = new Texture2D (1,1);
    133.         Rect[] r = tx.PackTextures (textures, 0, maxAtlasSize);
    134.         File.WriteAllBytes (Application.dataPath + "/" + source.name + ".png", tx.EncodeToPNG());
    135.         AssetDatabase.Refresh ();
    136.         tx = (Texture2D) AssetDatabase.LoadAssetAtPath ("Assets/" + source.name + ".png", typeof(Texture2D));
    137.  
    138.         uvOffset = 0;
    139.         meshOffset = 0;
    140.         foreach (SkinnedMeshRenderer smr in SMRs) {
    141.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    142.                 Vector2 uvClamped = new Vector2();
    143.                
    144.                 uvClamped = uv;
    145.                
    146.                 while (uvClamped.x > 1)
    147.                     uvClamped.x = uvClamped.x - 1;
    148.                
    149.                 while (uvClamped.x < 0)
    150.                     uvClamped.x = uvClamped.x + 1;
    151.                
    152.                 while (uvClamped.y > 1)
    153.                     uvClamped.y = uvClamped.y - 1;
    154.                
    155.                 while (uvClamped.x < 0)
    156.                     uvClamped.y = uvClamped.y + 1;
    157.                
    158.                 uvs[uvOffset].x = Mathf.Lerp (r[meshOffset].xMin, r[meshOffset].xMax, uvClamped.x);            
    159.                 uvs[uvOffset].y = Mathf.Lerp (r[meshOffset].yMin, r[meshOffset].yMax, uvClamped.y);            
    160.                 uvOffset ++;
    161.             }
    162.             meshOffset ++;
    163.         }
    164.  
    165.         Material mat = new Material (Shader.Find("Diffuse"));
    166.         mat.mainTexture = tx;
    167.         AssetDatabase.CreateAsset(mat, "Assets/" + source.name + ".mat");
    168.  
    169.         //New Mesh
    170.         Mesh me = new Mesh();
    171.         me.name = source.name;
    172.         me.vertices = verts;
    173.         me.normals = norms;
    174.         me.tangents = tans;
    175.         me.boneWeights = weights;
    176.         me.uv = uvs;
    177.         me.triangles = tris;
    178.         AssetDatabase.CreateAsset(me, "Assets/" + source.name + "mesh.asset");
    179.         me.bindposes = bindPoses;
    180.         SkinnedMeshRenderer newSMR = source.AddComponent<SkinnedMeshRenderer>();
    181.  
    182.         newSMR.sharedMesh = me;
    183.         newSMR.bones = bones;
    184.         newSMR.updateWhenOffscreen = true;
    185.         source.renderer.material = mat;
    186.     }
    187. }
    188.  
    Last edited: Jun 5, 2012
  13. Marble

    Marble

    Member

    Joined:
    Aug 29, 2005
    Messages:
    938
    Here is another adaptation of duhprey's adaptation. This version of the script doesn't do any atlasing; it just puts all original materials under the same combined renderer, which is useful if you want to be able to swap the texture of, say, a shirt or skin color without changing the rest. The atlasing code is just commented in case someone wants to add it back in.

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.IO;
    6.  
    7. public class EditorCombine : MonoBehaviour {
    8.  
    9. //    static public int maxAtlasSize = 2048;
    10.  
    11.     [MenuItem ("Assets/CombineSkinnedMeshes")]
    12.  
    13.     static void CombineTheMeshes(){
    14.  
    15.         if (Selection.activeGameObject == null) return;
    16.  
    17.         SkinnedMeshRenderer[] SMRs;
    18.  
    19.         int vertCount = 0;
    20.         int normCount = 0;
    21.         int tanCount = 0;
    22.         int triCount = 0;
    23.         int uvCount = 0;
    24.         int boneCount = 0;
    25.         int bpCount = 0;
    26.         int bwCount = 0;
    27.  
    28.         Transform[] bones;
    29.         Matrix4x4[] bindPoses;
    30.         BoneWeight[] weights;
    31.  
    32.         Vector3[] verts;
    33.         Vector3[] norms;
    34.         Vector4[] tans;
    35.         Vector2[] uvs;
    36. //      Texture2D[] textures;
    37.         List<int[]> subMeshes;
    38.         Material[] mats;
    39.  
    40.         int vertOffset = 0;
    41.         int normOffset = 0;
    42.         int tanOffset = 0;
    43.         int triOffset = 0;
    44.         int uvOffset = 0;
    45.         int meshOffset = 0;
    46.  
    47.         int boneSplit = 0;
    48.         int bNum = 0;
    49.  
    50.         int[] bCount;
    51.  
    52.         SMRs = Selection.activeGameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
    53.  
    54.         foreach (SkinnedMeshRenderer smr in SMRs) {
    55.             vertCount += smr.sharedMesh.vertices.Length;
    56.             normCount += smr.sharedMesh.normals.Length;
    57.             tanCount += smr.sharedMesh.tangents.Length;
    58.             triCount += smr.sharedMesh.triangles.Length;
    59.             uvCount += smr.sharedMesh.uv.Length;
    60.             boneCount += smr.bones.Length;
    61.             bpCount += smr.sharedMesh.bindposes.Length;
    62.             bwCount += smr.sharedMesh.boneWeights.Length;
    63.             bNum++;
    64.         }
    65.  
    66.         bCount = new int[3];
    67.        
    68.         bones = new Transform[boneCount];
    69.         weights = new BoneWeight[bwCount];
    70.         bindPoses = new Matrix4x4[bpCount];
    71. //      textures = new Texture2D[bNum];
    72.         mats = new Material[bNum];
    73.  
    74.         foreach (SkinnedMeshRenderer smr in SMRs) {
    75.            
    76.             // Load bone transforms
    77.             for(int b1 = 0; b1 < smr.bones.Length; b1++) {
    78.                
    79.                 bones[bCount[0]] = smr.bones[b1];
    80.                 bCount[0]++;
    81.             }
    82.  
    83.             // Load bone weights
    84.             for(int b2 = 0; b2 < smr.sharedMesh.boneWeights.Length; b2++){
    85.  
    86.                 weights[bCount[1]] = smr.sharedMesh.boneWeights[b2];
    87.                 weights[bCount[1]].boneIndex0 += boneSplit;
    88.                 weights[bCount[1]].boneIndex1 += boneSplit;
    89.                 weights[bCount[1]].boneIndex2 += boneSplit;
    90.                 weights[bCount[1]].boneIndex3 += boneSplit;
    91.  
    92.                 bCount[1]++;
    93.             }
    94.  
    95.             // Load bone bindposes
    96.             for(int b3 = 0; b3 < smr.sharedMesh.bindposes.Length; b3++){
    97.  
    98.                 bindPoses[bCount[2]] = smr.sharedMesh.bindposes[b3];
    99.                 bCount[2]++;
    100.             }
    101.  
    102.             boneSplit += smr.bones.Length;
    103.         }
    104.  
    105.         verts = new Vector3[vertCount];
    106.         norms = new Vector3[normCount];
    107.         tans = new Vector4[tanCount];
    108.         subMeshes = new List<int[]>();
    109.         uvs = new Vector2[uvCount];
    110.  
    111.         foreach (SkinnedMeshRenderer smr in SMRs){
    112.            
    113.             int[] theseTris = new int[smr.sharedMesh.triangles.Length];
    114.             foreach (int i in smr.sharedMesh.triangles){
    115.                 theseTris[triOffset++] = i + vertOffset;
    116.             }
    117.            
    118.             subMeshes.Add(theseTris);
    119.             triOffset = 0;
    120.  
    121.             foreach (Vector3 v in smr.sharedMesh.vertices){
    122.                 verts[vertOffset++] = v;
    123.             }
    124.  
    125.             foreach (Vector3 n in smr.sharedMesh.normals){
    126.                 norms[normOffset++] = n;
    127.             }
    128.  
    129.             foreach (Vector4 t in smr.sharedMesh.tangents){
    130.                 tans[tanOffset++] = t;
    131.             }
    132.  
    133.             foreach (Vector2 uv in smr.sharedMesh.uv){
    134.                 uvs[uvOffset++] = uv;
    135.             }
    136.            
    137.             mats[meshOffset] = smr.sharedMaterial;
    138.            
    139.  
    140. //            textures[meshOffset] = (Texture2D) smr.sharedMaterial.mainTexture;
    141. //            string path = AssetDatabase.GetAssetPath (smr.sharedMaterial.mainTexture);
    142. //            TextureImporter imp = (TextureImporter) AssetImporter.GetAtPath (path);
    143. //
    144. //            if (!imp.isReadable) {
    145. //                imp.isReadable = true;
    146. //                AssetDatabase.Refresh ();
    147. //                AssetDatabase.ImportAsset (path);
    148. //            }
    149.            
    150.             meshOffset++;
    151.  
    152.             smr.enabled = false;
    153.         }
    154.  
    155.  
    156.  
    157. //        Texture2D tx = new Texture2D (1,1);
    158. //        Rect[] r = tx.PackTextures (textures, 0, maxAtlasSize);
    159. //        File.WriteAllBytes (Application.dataPath + "/" + Selection.activeGameObject.name + ".png", tx.EncodeToPNG());
    160. //        AssetDatabase.Refresh ();
    161. //        tx = (Texture2D) AssetDatabase.LoadAssetAtPath ("Assets/" + Selection.activeGameObject.name + ".png", typeof(Texture2D));
    162.  
    163. //        uvOffset = 0;
    164. //        meshOffset = 0;
    165. //        foreach (SkinnedMeshRenderer smr in SMRs) {
    166. //
    167. //            foreach (Vector2 uv in smr.sharedMesh.uv) {
    168. //             
    169. //                uvs[uvOffset].x = Mathf.Lerp (r[meshOffset].xMin, r[meshOffset].xMax, uv.x);
    170. //                uvs[uvOffset].y = Mathf.Lerp (r[meshOffset].yMin, r[meshOffset].yMax, uv.y);
    171. //
    172. //                uvOffset ++;
    173. //            }
    174. //
    175. //            meshOffset ++;
    176. //        }
    177.  
    178.  
    179.  
    180. //        Material mat = new Material (Shader.Find("Diffuse"));
    181. //        mat.mainTexture = tx;
    182. //
    183. //        AssetDatabase.CreateAsset(mat, "Assets/" + Selection.activeGameObject.name + ".mat");
    184.  
    185.  
    186.  
    187.         //New Mesh
    188.  
    189.         Mesh me = new Mesh();
    190.         me.vertices = verts;
    191.         me.normals = norms;
    192.         me.tangents = tans;
    193.         me.boneWeights = weights;
    194.         me.uv = uvs;
    195.         me.subMeshCount = subMeshes.Count;
    196.        
    197.         for( int subMesh = 0; subMesh < subMeshes.Count; subMesh++ ) {
    198.             me.SetTriangles( subMeshes[subMesh], subMesh );
    199.         }
    200.  
    201.         AssetDatabase.CreateAsset(me, "Assets/" + Selection.activeGameObject.name + "mesh.asset");
    202.  
    203.         me.bindposes = bindPoses;
    204.  
    205.         SkinnedMeshRenderer newSMR = Selection.activeGameObject.AddComponent<SkinnedMeshRenderer>();
    206.  
    207.         newSMR.sharedMesh = me;
    208.         newSMR.bones = bones;
    209.         newSMR.updateWhenOffscreen = true;
    210.        
    211.         Selection.activeGameObject.renderer.sharedMaterials = mats;
    212.  
    213. //        Selection.activeGameObject.renderer.material = mat;
    214.     }
    215.  
    216. }
    Last edited: Mar 23, 2013
  14. Marble

    Marble

    Member

    Joined:
    Aug 29, 2005
    Messages:
    938
    ... and another. This version atlases at runtime, incorporates sunpats' fix (with a somewhat more graceful modulus), and can accommodate normal maps. Also, it reminds any Mecanim animator that it has a new renderer bounds; otherwise, animation stops working.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO;
    4.  
    5. public class CombineAndAtlas : MonoBehaviour {
    6.    
    7.     public bool hasNormalMaps = true;
    8.    
    9.     int maxAtlasSize = 2048;
    10.  
    11.     void Start() {
    12.  
    13.         SkinnedMeshRenderer[] SMRs;
    14.        
    15.         int vertCount = 0;
    16.         int normCount = 0;
    17.         int tanCount = 0;
    18.         int triCount = 0;
    19.         int uvCount = 0;
    20.         int boneCount = 0;
    21.         int bpCount = 0;
    22.         int bwCount = 0;
    23.  
    24.         Transform[] bones;
    25.         Matrix4x4[] bindPoses;
    26.         BoneWeight[] weights;
    27.  
    28.         Vector3[] verts;
    29.         Vector3[] norms;
    30.         Vector4[] tans;
    31.         int[] tris;
    32.         Vector2[] uvs;
    33.         Texture2D[] textures;
    34.         Texture2D[] normalmaps;
    35.  
    36.         int vertOffset = 0;
    37.         int normOffset = 0;
    38.         int tanOffset = 0;
    39.         int triOffset = 0;
    40.         int uvOffset = 0;
    41.         int meshOffset = 0;
    42.  
    43.         int  boneSplit = 0;
    44.         int bNum = 0;
    45.  
    46.         int[] bCount;
    47.  
    48.         SMRs = GetComponentsInChildren<SkinnedMeshRenderer>();
    49.  
    50.         foreach (SkinnedMeshRenderer smr in SMRs) {
    51.             vertCount += smr.sharedMesh.vertices.Length;
    52.             normCount += smr.sharedMesh.normals.Length;
    53.             tanCount += smr.sharedMesh.tangents.Length;
    54.             triCount += smr.sharedMesh.triangles.Length;
    55.             uvCount += smr.sharedMesh.uv.Length;
    56.             boneCount += smr.bones.Length;
    57.             bpCount += smr.sharedMesh.bindposes.Length;
    58.             bwCount += smr.sharedMesh.boneWeights.Length;
    59.             bNum++;
    60.         }
    61.  
    62.         bCount = new int[3];
    63.         bones = new Transform[boneCount];
    64.         weights = new BoneWeight[bwCount];
    65.         bindPoses = new Matrix4x4[bpCount];
    66.         textures = new Texture2D[bNum];
    67.         normalmaps = new Texture2D[bNum];
    68.        
    69.         foreach (SkinnedMeshRenderer smr in SMRs) {
    70.  
    71.             for(int b1 = 0; b1 < smr.bones.Length; b1++) {
    72.                 bones[bCount[0]] = smr.bones[b1];
    73.                
    74.                 bCount[0]++;
    75.             }
    76.  
    77.             for(int b2 = 0; b2 < smr.sharedMesh.boneWeights.Length; b2++) {
    78.                 weights[bCount[1]] = smr.sharedMesh.boneWeights[b2];
    79.                 weights[bCount[1]].boneIndex0 += boneSplit;
    80.                 weights[bCount[1]].boneIndex1 += boneSplit;
    81.                 weights[bCount[1]].boneIndex2 += boneSplit;
    82.                 weights[bCount[1]].boneIndex3 += boneSplit;
    83.  
    84.                 bCount[1]++;
    85.             }
    86.  
    87.             for(int b3 = 0; b3 < smr.sharedMesh.bindposes.Length; b3++) {
    88.                 bindPoses[bCount[2]] = smr.sharedMesh.bindposes[b3];
    89.  
    90.                 bCount[2]++;
    91.             }
    92.  
    93.             boneSplit += smr.bones.Length;
    94.         }
    95.  
    96.         verts = new Vector3[vertCount];
    97.         norms = new Vector3[normCount];
    98.         tans = new Vector4[tanCount];
    99.         tris = new int[triCount];
    100.         uvs = new Vector2[uvCount];
    101.  
    102.         foreach (SkinnedMeshRenderer smr in SMRs) {
    103.            
    104.             foreach (int i in smr.sharedMesh.triangles) {
    105.                 tris[triOffset++] = i + vertOffset;
    106.             }
    107.  
    108.             foreach (Vector3 v in smr.sharedMesh.vertices) {
    109.                 verts[vertOffset++] = v;
    110.             }
    111.  
    112.             foreach (Vector3 n in smr.sharedMesh.normals) {
    113.                 norms[normOffset++] = n;
    114.             }
    115.  
    116.             foreach (Vector4 t in smr.sharedMesh.tangents) {
    117.                 tans[tanOffset++] = t;
    118.             }
    119.  
    120.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    121.                 uvs[uvOffset++] = uv;
    122.             }
    123.  
    124.             textures[meshOffset] = (Texture2D) smr.sharedMaterial.GetTexture("_MainTex");
    125.             if( hasNormalMaps ) normalmaps[meshOffset] = (Texture2D) smr.sharedMaterial.GetTexture("_BumpMap");
    126.  
    127.             meshOffset++;
    128.  
    129.             Destroy( smr.gameObject );
    130.         }
    131.  
    132.         Texture2D tx = new Texture2D (1,1);
    133.         Rect[] r = tx.PackTextures (textures, 0, maxAtlasSize);
    134.        
    135.         Texture2D nm = new Texture2D (1,1);
    136.        
    137.         if( hasNormalMaps ) {
    138.             nm.PackTextures (normalmaps, 0, maxAtlasSize);
    139.         }
    140.  
    141.         uvOffset = 0;
    142.         meshOffset = 0;
    143.  
    144.         foreach (SkinnedMeshRenderer smr in SMRs) {
    145.  
    146.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    147.                 uvs[uvOffset].x = Mathf.Lerp (r[meshOffset].xMin, r[meshOffset].xMax, uv.x % 1);
    148.                 uvs[uvOffset].y = Mathf.Lerp (r[meshOffset].yMin, r[meshOffset].yMax, uv.y % 1);
    149.  
    150.                 uvOffset ++;
    151.             }
    152.  
    153.             meshOffset ++;
    154.         }
    155.  
    156.         Material mat;
    157.         if( hasNormalMaps ) mat = new Material( Shader.Find("Bumped Diffuse") );
    158.         else mat = new Material( Shader.Find("Diffuse") );
    159.        
    160.         mat.SetTexture("_MainTex", tx);
    161.         if( hasNormalMaps ) mat.SetTexture("_BumpMap", nm);
    162.  
    163.         //New Mesh
    164.         Mesh me = new Mesh();
    165.         me.name = gameObject.name;
    166.         me.vertices = verts;
    167.         me.normals = norms;
    168.         me.tangents = tans;
    169.         me.boneWeights = weights;
    170.         me.uv = uvs;
    171.         me.triangles = tris;
    172.  
    173.         me.bindposes = bindPoses;
    174.  
    175.         SkinnedMeshRenderer newSMR = gameObject.AddComponent<SkinnedMeshRenderer>();
    176.  
    177.         newSMR.sharedMesh = me;
    178.         newSMR.bones = bones;
    179.         newSMR.updateWhenOffscreen = true;
    180.  
    181.         renderer.material = mat;
    182.        
    183.         // reset animator culling mode so that Mecanim recognizes renderer bounds again
    184.         Animator animator = GetComponent<Animator>();
    185.         if( animator ) {
    186.             animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
    187.             animator.cullingMode = AnimatorCullingMode.BasedOnRenderers;
    188.         }
    189.     }
    190. }
    Last edited: Mar 26, 2013
  15. KirbyRawr

    KirbyRawr

    Member

    Joined:
    Jul 23, 2012
    Messages:
    646
    I modified a little your code because it gave me problems with normals, also i added multiple options, thanks for your script sir ^^.
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. using System.Collections;
    4.  
    5. using System.IO;
    6.  
    7.  
    8.  
    9. public class CombineAndAtlas : MonoBehaviour {
    10.  
    11.    
    12.    
    13.     public bool hasNormalMaps = true;
    14.     public bool inverseNormals = true;
    15.     public bool destroy = false;
    16.     public bool enable = true;
    17.    
    18.  
    19.     int maxAtlasSize = 2048;
    20.  
    21.  
    22.  
    23.     void Start() {
    24.  
    25.  
    26.  
    27.         SkinnedMeshRenderer[] SMRs;
    28.  
    29.        
    30.  
    31.         int vertCount = 0;
    32.  
    33.         int normCount = 0;
    34.  
    35.         int tanCount = 0;
    36.  
    37.         int triCount = 0;
    38.  
    39.         int uvCount = 0;
    40.  
    41.         int boneCount = 0;
    42.  
    43.         int bpCount = 0;
    44.  
    45.         int bwCount = 0;
    46.  
    47.  
    48.  
    49.         Transform[] bones;
    50.  
    51.         Matrix4x4[] bindPoses;
    52.  
    53.         BoneWeight[] weights;
    54.  
    55.  
    56.  
    57.         Vector3[] verts;
    58.  
    59.         Vector3[] norms;
    60.  
    61.         Vector4[] tans;
    62.  
    63.         int[] tris;
    64.  
    65.         Vector2[] uvs;
    66.  
    67.         Texture2D[] textures;
    68.  
    69.         Texture2D[] normalmaps;
    70.  
    71.  
    72.  
    73.         int vertOffset = 0;
    74.  
    75.         int normOffset = 0;
    76.  
    77.         int tanOffset = 0;
    78.  
    79.         int triOffset = 0;
    80.  
    81.         int uvOffset = 0;
    82.  
    83.         int meshOffset = 0;
    84.  
    85.  
    86.  
    87.         int  boneSplit = 0;
    88.  
    89.         int bNum = 0;
    90.  
    91.  
    92.  
    93.         int[] bCount;
    94.  
    95.  
    96.  
    97.         SMRs = GetComponentsInChildren<SkinnedMeshRenderer>();
    98.  
    99.  
    100.  
    101.         foreach (SkinnedMeshRenderer smr in SMRs) {
    102.  
    103.             vertCount += smr.sharedMesh.vertices.Length;
    104.  
    105.             normCount += smr.sharedMesh.normals.Length;
    106.  
    107.             tanCount += smr.sharedMesh.tangents.Length;
    108.  
    109.             triCount += smr.sharedMesh.triangles.Length;
    110.  
    111.             uvCount += smr.sharedMesh.uv.Length;
    112.  
    113.             boneCount += smr.bones.Length;
    114.  
    115.             bpCount += smr.sharedMesh.bindposes.Length;
    116.  
    117.             bwCount += smr.sharedMesh.boneWeights.Length;
    118.  
    119.             bNum++;
    120.  
    121.         }
    122.  
    123.  
    124.  
    125.         bCount = new int[3];
    126.  
    127.         bones = new Transform[boneCount];
    128.  
    129.         weights = new BoneWeight[bwCount];
    130.  
    131.         bindPoses = new Matrix4x4[bpCount];
    132.  
    133.         textures = new Texture2D[bNum];
    134.  
    135.         normalmaps = new Texture2D[bNum];
    136.  
    137.        
    138.  
    139.         foreach (SkinnedMeshRenderer smr in SMRs) {
    140.  
    141.  
    142.  
    143.             for(int b1 = 0; b1 < smr.bones.Length; b1++) {
    144.  
    145.                 bones[bCount[0]] = smr.bones[b1];
    146.  
    147.                
    148.  
    149.                 bCount[0]++;
    150.  
    151.             }
    152.  
    153.  
    154.  
    155.             for(int b2 = 0; b2 < smr.sharedMesh.boneWeights.Length; b2++) {
    156.  
    157.                 weights[bCount[1]] = smr.sharedMesh.boneWeights[b2];
    158.  
    159.                 weights[bCount[1]].boneIndex0 += boneSplit;
    160.  
    161.                 weights[bCount[1]].boneIndex1 += boneSplit;
    162.  
    163.                 weights[bCount[1]].boneIndex2 += boneSplit;
    164.  
    165.                 weights[bCount[1]].boneIndex3 += boneSplit;
    166.  
    167.  
    168.  
    169.                 bCount[1]++;
    170.  
    171.             }
    172.  
    173.  
    174.  
    175.             for(int b3 = 0; b3 < smr.sharedMesh.bindposes.Length; b3++) {
    176.  
    177.                 bindPoses[bCount[2]] = smr.sharedMesh.bindposes[b3];
    178.  
    179.  
    180.  
    181.                 bCount[2]++;
    182.  
    183.             }
    184.  
    185.  
    186.  
    187.             boneSplit += smr.bones.Length;
    188.  
    189.         }
    190.  
    191.  
    192.  
    193.         verts = new Vector3[vertCount];
    194.  
    195.         norms = new Vector3[normCount];
    196.  
    197.         tans = new Vector4[tanCount];
    198.  
    199.         tris = new int[triCount];
    200.  
    201.         uvs = new Vector2[uvCount];
    202.  
    203.  
    204.  
    205.         foreach (SkinnedMeshRenderer smr in SMRs) {
    206.  
    207.            
    208.  
    209.             foreach (int i in smr.sharedMesh.triangles) {
    210.  
    211.                 tris[triOffset++] = i + vertOffset;
    212.  
    213.             }
    214.  
    215.  
    216.  
    217.             foreach (Vector3 v in smr.sharedMesh.vertices) {
    218.  
    219.                 verts[vertOffset++] = v;
    220.  
    221.             }
    222.  
    223.  
    224.  
    225.             foreach (Vector3 n in smr.sharedMesh.normals) {
    226.  
    227.                 norms[normOffset++] = n;
    228.  
    229.             }
    230.  
    231.  
    232.  
    233.             foreach (Vector4 t in smr.sharedMesh.tangents) {
    234.  
    235.                 tans[tanOffset++] = t;
    236.  
    237.             }
    238.  
    239.  
    240.  
    241.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    242.  
    243.                 uvs[uvOffset++] = uv;
    244.  
    245.             }
    246.  
    247.  
    248.  
    249.             textures[meshOffset] = (Texture2D) smr.sharedMaterial.GetTexture("_MainTex");
    250.  
    251.             if( hasNormalMaps ) normalmaps[meshOffset] = (Texture2D) smr.sharedMaterial.GetTexture("_BumpMap");
    252.  
    253.  
    254.  
    255.             meshOffset++;
    256.        
    257.             if(destroy  !enable){
    258.             Destroy(smr);  
    259.             }
    260.            
    261.             if(enable  !destroy){
    262.             smr.enabled = false;   
    263.             }
    264.            
    265.  
    266.          
    267.  
    268.         }
    269.  
    270.  
    271.  
    272.         Texture2D tx = new Texture2D (1,1);
    273.  
    274.         Rect[] r = tx.PackTextures (textures, 0, maxAtlasSize);
    275.  
    276.        
    277.  
    278.         Texture2D nm = new Texture2D (1,1);
    279.  
    280.        
    281.  
    282.         if( hasNormalMaps ) {
    283.  
    284.             nm.PackTextures (normalmaps, 0, maxAtlasSize);
    285.  
    286.         }
    287.  
    288.  
    289.  
    290.         uvOffset = 0;
    291.  
    292.         meshOffset = 0;
    293.  
    294.  
    295.  
    296.         foreach (SkinnedMeshRenderer smr in SMRs) {
    297.  
    298.  
    299.  
    300.             foreach (Vector2 uv in smr.sharedMesh.uv) {
    301.  
    302.                 uvs[uvOffset].x = Mathf.Lerp (r[meshOffset].xMin, r[meshOffset].xMax, uv.x % 1);
    303.  
    304.                 uvs[uvOffset].y = Mathf.Lerp (r[meshOffset].yMin, r[meshOffset].yMax, uv.y % 1);
    305.  
    306.  
    307.  
    308.                 uvOffset ++;
    309.  
    310.             }
    311.  
    312.  
    313.  
    314.             meshOffset ++;
    315.  
    316.         }
    317.  
    318.  
    319.  
    320.         Material mat;
    321.  
    322.         if( hasNormalMaps ) mat = new Material( Shader.Find("Bumped Diffuse") );
    323.  
    324.         else mat = new Material( Shader.Find("Diffuse") );
    325.  
    326.        
    327.  
    328.         mat.SetTexture("_MainTex", tx);
    329.  
    330.         if( hasNormalMaps ) mat.SetTexture("_BumpMap", nm);
    331.  
    332.  
    333.  
    334.         //New Mesh
    335.  
    336.         Mesh me = new Mesh();
    337.  
    338.         me.name = gameObject.name;
    339.  
    340.         me.vertices = verts;
    341.  
    342.         me.normals = norms;
    343.  
    344.         me.tangents = tans;
    345.  
    346.         me.boneWeights = weights;
    347.  
    348.         me.uv = uvs;
    349.  
    350.         me.triangles = tris;
    351.  
    352.  
    353.  
    354.         me.bindposes = bindPoses;
    355.  
    356.  
    357.  
    358.         SkinnedMeshRenderer newSMR = gameObject.AddComponent<SkinnedMeshRenderer>();
    359.  
    360.  
    361.  
    362.         newSMR.sharedMesh = me;
    363.  
    364.         newSMR.bones = bones;
    365.  
    366.         newSMR.updateWhenOffscreen = true;
    367.  
    368.  
    369.  
    370.         renderer.material = mat;
    371.  
    372.      if(inverseNormals){
    373.  
    374.             Vector3[] normals = me.normals;
    375.             for (int i=0;i<normals.Length;i++)
    376.                 normals[i] = -normals[i];
    377.             me.normals = normals;
    378.  
    379.             for (int m=0;m<me.subMeshCount;m++)
    380.             {
    381.                 int[] triangles = me.GetTriangles(m);
    382.                 for (int i=0;i<triangles.Length;i+=3)
    383.                 {
    384.                     int temp = triangles[i + 0];
    385.                     triangles[i + 0] = triangles[i + 1];
    386.                     triangles[i + 1] = temp;
    387.                 }
    388.                 me.SetTriangles(triangles, m);
    389.             }
    390.                
    391.         }
    392.        
    393.        
    394.        
    395.    
    396.  
    397.         // reset animator culling mode so that Mecanim recognizes renderer bounds again
    398.  
    399.         Animator animator = GetComponent<Animator>();
    400.  
    401.         if( animator ) {
    402.  
    403.             animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
    404.  
    405.             animator.cullingMode = AnimatorCullingMode.BasedOnRenderers;
    406.  
    407.         }
    408.  
    409.     }
    410.  
    411. }
  16. payalsharma9988

    payalsharma9988

    New Member

    Joined:
    Oct 31, 2013
    Messages:
    25
    Great this all scripts doing wrk well properly.
  17. O'parallel

    O'parallel

    New Member

    Joined:
    Dec 9, 2012
    Messages:
    81
    Forgive my ignorance but how do I apply this? Can't drop the script on the character and can't see it in the menu's.
  18. Marble

    Marble

    Member

    Joined:
    Aug 29, 2005
    Messages:
    938
    Why can't you drop the script on your character? You need copy and paste the code into a C# script called CombineAndAtlas first, of course.
  19. O'parallel

    O'parallel

    New Member

    Joined:
    Dec 9, 2012
    Messages:
    81
    I missed the public class naming, and got Sanpaths version working through the asset menu, and now seems like I can get your versions too, Thanks for replying.
  20. konsnos

    konsnos

    Member

    Joined:
    Feb 13, 2012
    Messages:
    60
    CombineAndAtlas works pretty well :) Thank you people! I'm dropping it to a container with lots of the same SkinnedMeshes and it combines them to one reducing a lot of draw calls.