- Home /
Combining SkinnedMesh's with the same material and animations
This script works for reducing draw calls down to 1 by combining skinned meshes, but, it doesn't work for animation (even though it says it does), the one I talk about next is more robust regarding bones etc.
This script works for combining the meshes and keeping the animations, but, it still creates x different materials, x = the quantify of skinned meshes that get combined into the final mesh. I need it to use just 1 material....like the script above, and I dont even need the material to be dynamically created as I already have the 1 material that everything uses. I have tried modifying this (see code below) with some code from the one above but only the first skinned mesh that this one finds gets the material rendered to it.
The end result should be a single combined mesh all using a single material resulting in a single draw call with animations working. Any suggestions would be great.
FYI - this calls some methods in this script here which I have not modified.
using UnityEngine;
using System.Collections;
public class CombineSkinnedMeshes : MonoBehaviour
{
/// Usually rendering with triangle strips is faster.
/// However when combining objects with very low triangle counts, it can be faster to use triangles.
/// Best is to try out which value is faster in practice.
public bool castShadows = true;
public bool receiveShadows = true;
public int BoneCount =33; //The number of bones total
public Material MatForCombine;
public Texture2D SingleTexture;
void Start ()
{
//Getting all Skinned Renderer from Children
Component[] allsmr = GetComponentsInChildren(typeof(SkinnedMeshRenderer));
var vertCount = 0;
int offset = 0;
int b_offset = 0;
foreach (SkinnedMeshRenderer render in allsmr)
vertCount += render.sharedMesh.vertexCount;
Matrix4x4 myTransform = transform.worldToLocalMatrix;
//The hash with the all bones references: it will be used for set up the BoneWeight Indexes
Hashtable boneHash =new Hashtable();
//The Sum of All Child Bones
Transform[] totalBones = new Transform[BoneCount];//Total of Bones for my Example
//The Sum of the BindPoses
Matrix4x4[] totalBindPoses = new Matrix4x4[BoneCount];//Total of BindPoses
//The Sum of BoneWeights
BoneWeight[] totalBoneWeight = new BoneWeight[vertCount];//total of Vertices for my Example
Transform[] usedBones= new Transform[totalBones.Length];
ArrayList myInstances = new ArrayList();
//Setting my Arrays for copies
ArrayList myMaterials=new ArrayList();
int[] meshIndex = new int[allsmr.Length];
for(int i=0;i<allsmr.Length;i++)
{
//Getting one by one
SkinnedMeshRenderer smrenderer = (SkinnedMeshRenderer)allsmr[i];
//Making changes to the Skinned Renderer
SkinMeshCombineUtility.MeshInstance instance = new SkinMeshCombineUtility.MeshInstance ();
//Setting the Mesh for the instance
instance.mesh = smrenderer.sharedMesh;
meshIndex[i] = instance.mesh.vertexCount;
//Getting All Materials
for(int t=0;t<smrenderer.sharedMaterials.Length;t++)
{
myMaterials.Add(smrenderer.sharedMaterials[t]);
}
if (smrenderer != null && smrenderer.enabled && instance.mesh != null) {
instance.transform = myTransform * smrenderer.transform.localToWorldMatrix;
//Material == null
smrenderer.sharedMaterials =new Material[1];
//Getting subMesh
for(int t=0;t<smrenderer.sharedMesh.subMeshCount;t++)
{
instance.subMeshIndex = t;
myInstances.Add(instance);
}
//Copying Bones
for(int x=0;x<smrenderer.bones.Length;x++)
{
bool flag = false;
for(int j=0;j<totalBones.Length;j++)
{
if(usedBones[j]!=null)
//If the bone was already inserted
if((smrenderer.bones[x]==usedBones[j]))
{
flag = true;
break;
}
}
//If Bone is New ...
if(!flag)
{
//Debug.Log("Inserted bone:"+smrenderer.bones[x].name);
for(int f=0;f<totalBones.Length;f++)
{
//Insert bone at the firs free position
if(usedBones[f]==null)
{
usedBones[f] = smrenderer.bones[x];
break;
}
}
//inserting bones in totalBones
totalBones[offset]=smrenderer.bones[x];
//Reference HashTable
boneHash.Add(smrenderer.bones[x].name,offset);
//Recalculating BindPoses
//totalBindPoses[offset] = smrenderer.sharedMesh.bindposes[x] ;
totalBindPoses[offset] = smrenderer.bones[x].worldToLocalMatrix * transform.localToWorldMatrix ;
offset++;
}
}
//RecalculateBoneWeights
for(int x=0;x<smrenderer.sharedMesh.boneWeights.Length ;x++)
{
//Just Copying and changing the Bones Indexes !!
totalBoneWeight[b_offset] = recalculateIndexes(smrenderer.sharedMesh.boneWeights[x],boneHash,smrenderer.bones);
b_offset++;
}
//Disabling current SkinnedMeshRenderer
//((SkinnedMeshRenderer)allsmr[i]).enabled = false;
((SkinnedMeshRenderer)allsmr[i]).gameObject.active = false;
}
}
SkinMeshCombineUtility.MeshInstance[] instances = (SkinMeshCombineUtility.MeshInstance[])myInstances.ToArray(typeof(SkinMeshCombineUtility.MeshInstance));
// Make sure we have a SkinnedMeshRenderer
if (GetComponent(typeof(SkinnedMeshRenderer)) == null)
{
gameObject.AddComponent(typeof(SkinnedMeshRenderer));
}
//Setting Skinned Renderer
SkinnedMeshRenderer objRenderer = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer));
//Setting Mesh
objRenderer.sharedMesh = SkinMeshCombineUtility.Combine(instances);
objRenderer.castShadows = castShadows;
objRenderer.receiveShadows = receiveShadows;
//Setting Bindposes
objRenderer.sharedMesh.bindposes = totalBindPoses;
//Setting BoneWeights
objRenderer.sharedMesh.boneWeights = totalBoneWeight;
//Setting bones
objRenderer.bones =totalBones;
///Setting Materials ----- THIS ONLY WORKS FOR THE FIRST MESH IN THE TREE...
///I THINK UV's ARENT CORRECT FOR OTHER MESHES???
Texture2D skinnedMeshAtlas = new Texture2D(2048, 2048);
var texturesArrary = new Texture2D[1] {SingleTexture};
Rect[] packingResult = skinnedMeshAtlas.PackTextures(texturesArrary, 0);
Vector2[] originalUVs = objRenderer.sharedMesh.uv;
Vector2[] atlasUVs = new Vector2[originalUVs.Length];
int rectIndex = 0;
int vertTracker = 0;
for (int i = 0; i < atlasUVs.Length; i++)
{
atlasUVs[i].x = Mathf.Lerp(packingResult[rectIndex].xMin, packingResult[rectIndex].xMax, originalUVs[i].x);
atlasUVs[i].y = Mathf.Lerp(packingResult[rectIndex].yMin, packingResult[rectIndex].yMax, originalUVs[i].y);
if (i >= meshIndex[rectIndex] + vertTracker)
{
vertTracker += meshIndex[rectIndex];
//rectIndex++;
}
}
Material combinedMat = new Material(Shader.Find("Diffuse"));
combinedMat.mainTexture = skinnedMeshAtlas;
objRenderer.sharedMesh.uv = atlasUVs;
objRenderer.sharedMaterial = combinedMat;
objRenderer.sharedMesh.RecalculateNormals();
objRenderer.sharedMesh.RecalculateBounds();
//Enable Mesh
objRenderer.enabled = true;
/* Debug.Log("############################################");
Debug.Log("M Materials "+myMaterials.Count);
Debug.Log("bindPoses "+objRenderer.sharedMesh.bindposes.Length);
Debug.Log("boneWeights "+objRenderer.sharedMesh.boneWeights.Length);
Debug.Log("Bones "+objRenderer.bones.Length);
Debug.Log("Vertices "+objRenderer.sharedMesh.vertices.Length); */
}
/*
@Description: Revert the order of an array of components
(NOT USED)
*/
static Component[] revertComponent(Component[] comp )
{
Component[] result = new Component[comp.Length];
int x=0;
for(int i=comp.Length-1;i>=0;i--)
{
result[x++]=comp[i];
}
return result;
}
/*
@Description: Setting the Indexes for the new bones
*/
static BoneWeight recalculateIndexes(BoneWeight bw,Hashtable boneHash,Transform[] meshBones )
{
BoneWeight retBw = bw;
retBw.boneIndex0 = (int)boneHash[meshBones[bw.boneIndex0].name];
retBw.boneIndex1 = (int)boneHash[meshBones[bw.boneIndex1].name];
retBw.boneIndex2 = (int)boneHash[meshBones[bw.boneIndex2].name];
retBw.boneIndex3 = (int)boneHash[meshBones[bw.boneIndex3].name];
return retBw;
}
}
I just re-wrote the question. Looking to figure out how to get the texture mapping from the first script to work in the second script, Im close.
Hey I'm looking for an in editor script that does this. Any chance you have cracked it?