Search Unity

Script to combine Animated Mesh and Textures

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

  1. duhprey

    duhprey

    Joined:
    Nov 13, 2009
    Posts:
    166
    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.name = Selection.activeGameObject.name;
    144.         me.vertices = verts;
    145.         me.normals = norms;
    146.         me.tangents = tans;
    147.         me.boneWeights = weights;
    148.         me.uv = uvs;
    149.         me.triangles = tris;
    150.         AssetDatabase.CreateAsset(me, "Assets/" + Selection.activeGameObject.name + "mesh.asset");
    151.         me.bindposes = bindPoses;
    152.    
    153.         SkinnedMeshRenderer newSMR = Selection.activeGameObject.AddComponent<SkinnedMeshRenderer>();
    154.    
    155.         newSMR.sharedMesh = me;
    156.         newSMR.bones = bones;
    157.         newSMR.updateWhenOffscreen = true;
    158.         Selection.activeGameObject.renderer.material = mat;
    159.     }
    160. }
    161.  
     
  2. Morning

    Morning

    Joined:
    Feb 4, 2012
    Posts:
    1,141
    Most definitely going to help. Thanks a lot.
     
  3. Joel-Santos

    Joel-Santos

    Joined:
    Feb 23, 2010
    Posts:
    121
    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

    Joined:
    Nov 13, 2009
    Posts:
    166
    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

    Joined:
    Feb 23, 2010
    Posts:
    121
    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

    Joined:
    Nov 13, 2009
    Posts:
    166
    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

    Joined:
    Feb 23, 2010
    Posts:
    121
    Anyone succeeded with this script?
     
  8. duhprey

    duhprey

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

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
    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

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

    duhprey

    Joined:
    Nov 13, 2009
    Posts:
    166
    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

    Joined:
    Aug 24, 2011
    Posts:
    343
    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

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    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.name = Selection.activeGameObject.name;
    191.         me.vertices = verts;
    192.         me.normals = norms;
    193.         me.tangents = tans;
    194.         me.boneWeights = weights;
    195.         me.uv = uvs;
    196.         me.subMeshCount = subMeshes.Count;
    197.        
    198.         for( int subMesh = 0; subMesh < subMeshes.Count; subMesh++ ) {
    199.             me.SetTriangles( subMeshes[subMesh], subMesh );
    200.         }
    201.  
    202.         AssetDatabase.CreateAsset(me, "Assets/" + Selection.activeGameObject.name + "mesh.asset");
    203.  
    204.         me.bindposes = bindPoses;
    205.  
    206.         SkinnedMeshRenderer newSMR = Selection.activeGameObject.AddComponent<SkinnedMeshRenderer>();
    207.  
    208.         newSMR.sharedMesh = me;
    209.         newSMR.bones = bones;
    210.         newSMR.updateWhenOffscreen = true;
    211.        
    212.         Selection.activeGameObject.renderer.sharedMaterials = mats;
    213.  
    214. //        Selection.activeGameObject.renderer.material = mat;
    215.     }
    216.  
    217. }
     
    Last edited: Mar 23, 2013
  14. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    ... 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

    Joined:
    Jul 23, 2012
    Posts:
    945
    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

    Joined:
    Oct 31, 2013
    Posts:
    24
    Great this all scripts doing wrk well properly.
     
  17. Parallel_

    Parallel_

    Joined:
    Dec 9, 2012
    Posts:
    90
    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

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    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. Parallel_

    Parallel_

    Joined:
    Dec 9, 2012
    Posts:
    90
    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

    Joined:
    Feb 13, 2012
    Posts:
    121
    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.
     
  21. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36


    Tweaked a bit for my taste : goes from 20 to 2 batches, about 0.04 ms gained in rendering for this character.
    Added remove unused SMR, fixed formattings and stuff.
    Could be good for horde of characters or creating optimized prefab. Maybe runtime optimization for the main character.

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

    Riderfan

    Joined:
    Jan 10, 2013
    Posts:
    514
    Hi there. I know this is a really old thread but I'm running into an issue that I hope you (or someone) could lend a hand with. I'm using your tweaked script above to combine a collection of characters into a single skinned mesh. What I'm running into though is the following issue;

    After the combine process, only one of the meshes in the collection animates. All others are static even though they're all the same skinned mesh (just duplicates of the first one in the first row).


    Untitled.png

    I was wondering if you had any suggestions as to where I could look to figure out why only one of the characters of the final combined mesh is animating.

    thanks
     
  23. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    @Riderfan
    I'm not sure I understand correctly.
    If you duplicated all your characters and then applied for each one CombineAndAtlas I can't see where there could be a problem.
    But if you applied only once CombineAndAtlas for all the characters then I'm supposing that Unity skip duplicated bones and only animate the first one that it found. Maybe there is a way to bypass that if that's your problem ?
    Try to figure out how this guy does it : https://assetstore.unity.com/packages/tools/animation/mesh-animator-26009
    It may be bad, but I would try to only have one character animating, then use BakeMesh() on in and finally draw all of my characters in one draw call with this mesh. Might be enough for you.

    Edit: From mesh animator documentation, it uses the same method that I wanted you to try. It also pre-saves each frame so that each character can be played at a different time or even a different animation, that also mean more draw calls and more memory.
     
    Last edited: Nov 26, 2017
  24. Riderfan

    Riderfan

    Joined:
    Jan 10, 2013
    Posts:
    514
    Thanks for the tip about MeshAnimator.. I'll look into that, it looks quite promising.
     
  25. MP-ul

    MP-ul

    Joined:
    Jan 25, 2014
    Posts:
    230
    Well if you animate the bones of a model the skinned mesh moves with them no?
     
  26. MP-ul

    MP-ul

    Joined:
    Jan 25, 2014
    Posts:
    230
    And i think that if you need animate 5 objects that are combined you still need 5 animators to animate every bone Structure no?