CombineSkinnedMeshes

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(C# Code)
(C# Code)
Line 16: Line 16:
 
@Description Class that Combine Multilple SkinnedMeshes in just one Skinned Mesh Renderer
 
@Description Class that Combine Multilple SkinnedMeshes in just one Skinned Mesh Renderer
 
@Usage: Just AddComponent("CombineSkinnedMeshes") to your root component;
 
@Usage: Just AddComponent("CombineSkinnedMeshes") to your root component;
@IMPORTANT: This script uses the MeshCombineUtility Script provided by Unity
+
@IMPORTANT: This script uses the SkinMeshCombineUtility Script!
  
 
PS: It was tested with FBX files exported from 3D MAX
 
PS: It was tested with FBX files exported from 3D MAX
The Vertex Number and Bone Number must be configured according to your own AVATAR...
+
This Vertex Number and Bone Number must be configured according to your own AVATAR...
 
You can make a Counter to get the Number of Vertices from your imported character, I choice not do it since  
 
You can make a Counter to get the Number of Vertices from your imported character, I choice not do it since  
 
this is script is just executed one time... */
 
this is script is just executed one time... */
Line 32: Line 32:
 
/// However when combining objects with very low triangle counts, it can be faster to use triangles.
 
/// 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.
 
/// Best is to try out which value is faster in practice.
public bool generateTriangleStrips = true;
 
 
public bool castShadows = true;
 
public bool castShadows = true;
 
public bool receiveShadows = true;
 
public bool receiveShadows = true;
 
  
 
/* This is for very particular use, you must set it regarding to your Character */
 
/* This is for very particular use, you must set it regarding to your Character */
public static int VERTEX_NUMBER= YourVerices; //The number of vertices total of the character
+
public static int VERTEX_NUMBER= [Your Vertices Number]; //The number of vertices total of the character
public static int BONE_NUMBER = YourBones; //The number of bones total
+
public static int BONE_NUMBER =[Your Bones Number]; //The number of bones total
 
 
 
// Use this for initialization
 
// Use this for initialization
 
void Start () {
 
void Start () {
+
+
 
//Getting all Skinned Renderer from Children
 
//Getting all Skinned Renderer from Children
 
Component[] allsmr = GetComponentsInChildren(typeof(SkinnedMeshRenderer));
 
Component[] allsmr = GetComponentsInChildren(typeof(SkinnedMeshRenderer));
Matrix4x4 myTransform = transform.worldToLocalMatrix;
+
Matrix4x4 myTransform = transform.worldToLocalMatrix;
Hashtable materialToMesh= new Hashtable();
+
 
 
 
//The hash with the all bones references: it will be used for set up the BoneWeight Indexes
 
//The hash with the all bones references: it will be used for set up the BoneWeight Indexes
Line 61: Line 57:
 
//The Sum of BoneWeights
 
//The Sum of BoneWeights
 
BoneWeight[] totalBoneWeight = new BoneWeight[VERTEX_NUMBER];//total of Vertices for my Example
 
BoneWeight[] totalBoneWeight = new BoneWeight[VERTEX_NUMBER];//total of Vertices for my Example
+
+
 
int offset=0;
 
int offset=0;
 
int b_offset=0;
 
int b_offset=0;
 
Transform[] usedBones= new Transform[totalBones.Length];
 
Transform[] usedBones= new Transform[totalBones.Length];
 
 
 +
ArrayList myInstances = new ArrayList();
 +
 +
//Setting my Arrays for copies
 +
ArrayList myMaterials=new ArrayList();
 
 
 
for(int i=0;i<allsmr.Length;i++)
 
for(int i=0;i<allsmr.Length;i++)
Line 73: Line 72:
 
SkinnedMeshRenderer smrenderer  =  (SkinnedMeshRenderer)allsmr[i];
 
SkinnedMeshRenderer smrenderer  =  (SkinnedMeshRenderer)allsmr[i];
 
//Making changes to the Skinned Renderer
 
//Making changes to the Skinned Renderer
MeshCombineUtility.MeshInstance instance = new MeshCombineUtility.MeshInstance ();
+
SkinMeshCombineUtility.MeshInstance instance = new SkinMeshCombineUtility.MeshInstance ();
 
 
 
//Setting the Mesh for the instance
 
//Setting the Mesh for the instance
 
instance.mesh = smrenderer.sharedMesh;
 
instance.mesh = smrenderer.sharedMesh;
 +
 +
//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) {
 
if (smrenderer != null && smrenderer.enabled && instance.mesh != null) {
Line 82: Line 87:
 
instance.transform = myTransform * smrenderer.transform.localToWorldMatrix;
 
instance.transform = myTransform * smrenderer.transform.localToWorldMatrix;
 
 
//Setting Materials
+
//Material == null
Material[] materials = smrenderer.sharedMaterials;
+
smrenderer.sharedMaterials =new  Material[1];
 +
 
 +
//Getting  subMesh
 +
for(int t=0;t<smrenderer.sharedMesh.subMeshCount;t++)
 +
{
 +
instance.subMeshIndex = t;
 +
myInstances.Add(instance);
 +
}
 
 
for (int m=0;m<materials.Length;m++) {
 
//Getting Minimum of SubMesh
 
instance.subMeshIndex = System.Math.Min(m, instance.mesh.subMeshCount - 1); 
 
 
//Setting Meshed Instances
 
ArrayList objects = (ArrayList)materialToMesh[materials[m]];
 
if (objects != null) {
 
objects.Add(instance);
 
}
 
else
 
{
 
objects = new ArrayList ();
 
objects.Add(instance);
 
materialToMesh.Add(materials[m], objects);
 
}
 
}
 
 
 
//Copying Bones
 
//Copying Bones
 
for(int x=0;x<smrenderer.bones.Length;x++)
 
for(int x=0;x<smrenderer.bones.Length;x++)
Line 139: Line 134:
 
//totalBindPoses[offset] = smrenderer.sharedMesh.bindposes[x] ;
 
//totalBindPoses[offset] = smrenderer.sharedMesh.bindposes[x] ;
 
totalBindPoses[offset] = smrenderer.bones[x].worldToLocalMatrix * transform.localToWorldMatrix ;
 
totalBindPoses[offset] = smrenderer.bones[x].worldToLocalMatrix * transform.localToWorldMatrix ;
offset++;
+
offset++;
+
}
}
+
+
 
}
 
}
 
 
Line 153: Line 146:
 
}
 
}
 
//Disabling current SkinnedMeshRenderer
 
//Disabling current SkinnedMeshRenderer
((SkinnedMeshRenderer)allsmr[i]).enabled = false;
+
((SkinnedMeshRenderer)allsmr[i]).enabled = false;
+
 
}
 
}
 +
}
 
 
+
SkinMeshCombineUtility.MeshInstance[] instances = (SkinMeshCombineUtility.MeshInstance[])myInstances.ToArray(typeof(MeshCombineUtility.MeshInstance));
 +
 +
// Make sure we have a SkinnedMeshRenderer
 +
if (GetComponent(typeof(SkinnedMeshRenderer)) == null)
 +
{
 +
gameObject.AddComponent(typeof(SkinnedMeshRenderer));
 
}
 
}
foreach (DictionaryEntry de  in materialToMesh)
 
{
 
ArrayList elements = (ArrayList)de.Value;
 
MeshCombineUtility.MeshInstance[] instances = (MeshCombineUtility.MeshInstance[])elements.ToArray(typeof(MeshCombineUtility.MeshInstance));
 
 
// We have a maximum of one material, so just attach the mesh to our own game object
 
if (materialToMesh.Count == 1)
 
{
 
 
// Make sure we have a SkinnedMeshRenderer
 
if (GetComponent(typeof(SkinnedMeshRenderer)) == null)
 
{
 
gameObject.AddComponent(typeof(SkinnedMeshRenderer));
 
}
 
 
 
//Setting Skinned Renderer
+
//Setting Skinned Renderer
SkinnedMeshRenderer objRenderer = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer));
+
SkinnedMeshRenderer objRenderer = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer));
objRenderer.sharedMesh = MeshCombineUtility.Combine(instances, generateTriangleStrips);
+
//Setting Mesh
objRenderer.material = (Material)de.Key;
+
objRenderer.sharedMesh = SkinMeshCombineUtility.Combine(instances);
 +
 +
objRenderer.castShadows = castShadows;
 +
objRenderer.receiveShadows = receiveShadows;
 
 
objRenderer.castShadows = castShadows;
+
//Setting Bindposes
objRenderer.receiveShadows = receiveShadows;
+
objRenderer.sharedMesh.bindposes = totalBindPoses;
+
//Setting Bindposes
+
objRenderer.sharedMesh.bindposes = totalBindPoses;
+
+
//Setting BoneWeights
+
objRenderer.sharedMesh.boneWeights = totalBoneWeight;
+
+
//Setting bones
+
objRenderer.bones =totalBones;
+
+
objRenderer.sharedMesh.RecalculateNormals();
+
objRenderer.sharedMesh.RecalculateBounds();
+
//Enable Mesh
+
objRenderer.enabled = true;
+
  
/* Debug.Log("############################################");
+
//Setting BoneWeights
Debug.Log("bindPoses "+objRenderer.sharedMesh.bindposes.Length);
+
objRenderer.sharedMesh.boneWeights = totalBoneWeight;
Debug.Log("boneWeights "+objRenderer.sharedMesh.boneWeights.Length);
+
Debug.Log("Bones "+objRenderer.bones.Length);
+
Debug.Log("Vertices "+objRenderer.sharedMesh.vertices.Length); */
+
}
+
// We have multiple materials to take care of, build one mesh / gameobject for each material
+
// and parent it to this object
+
                        //FOR NOW JUST WORKS WITH ONE MATERIAL !!!
+
                        /*
+
else
+
{
+
GameObject go = new GameObject("CombinedSkinnedMesh");
+
go.transform.parent = transform;
+
go.transform.localScale = Vector3.one;
+
go.transform.localRotation = Quaternion.identity;
+
go.transform.localPosition = Vector3.zero;
+
go.AddComponent(typeof(SkinnedMeshRenderer));
+
((SkinnedMeshRenderer)go.GetComponent(typeof(SkinnedMeshRenderer))).material = (Material)de.Key;
+
 
+
SkinnedMeshRenderer objRenderer = (SkinnedMeshRenderer)go.GetComponent(typeof(SkinnedMeshRenderer));
+
objRenderer.sharedMesh = MeshCombineUtility.Combine(instances, generateTriangleStrips);
+
+
//Setting Bindposes
+
objRenderer.sharedMesh.bindposes = totalBindPoses;
+
 
 
//Setting BoneWeights
+
//Setting bones
objRenderer.sharedMesh.boneWeights = totalBoneWeight;
+
objRenderer.bones =totalBones;
 
 
//Setting bones
+
//Setting Materials
objRenderer.bones =totalBones;
+
objRenderer.sharedMaterials = (Material[])myMaterials.ToArray(typeof(Material));
 
 
objRenderer.sharedMesh.RecalculateNormals();
+
objRenderer.sharedMesh.RecalculateNormals();
objRenderer.sharedMesh.RecalculateBounds();
+
objRenderer.sharedMesh.RecalculateBounds();
 +
 +
//Enable Mesh
 +
objRenderer.enabled = true;
 
 
//Enable Mesh
+
/* Debug.Log("############################################");
objRenderer.enabled = true;
+
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); */
 +
 
 
}
 
}
 
 

Revision as of 19:03, 29 April 2009

Author: Gabriel Santos

Description

Class Model that allows you combine multiple skinned meshes at runtime, useful for Avatar System. IMPORTANT: The number of vertices and bones is very particular, you must set it up according to your own model.

This Class uses the MeshCombineUtility provided by Unity!

Feel free to clean up the code and add to it. :D

C# Code

<csharp> /*IMPORTANT: READ !!!!!! @Autor: Gabriel Santos @Description Class that Combine Multilple SkinnedMeshes in just one Skinned Mesh Renderer @Usage: Just AddComponent("CombineSkinnedMeshes") to your root component; @IMPORTANT: This script uses the SkinMeshCombineUtility Script!

PS: It was tested with FBX files exported from 3D MAX This Vertex Number and Bone Number must be configured according to your own AVATAR... You can make a Counter to get the Number of Vertices from your imported character, I choice not do it since this is script is just executed one time... */

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;

/* This is for very particular use, you must set it regarding to your Character */ public static int VERTEX_NUMBER= [Your Vertices Number]; //The number of vertices total of the character public static int BONE_NUMBER =[Your Bones Number]; //The number of bones total

// Use this for initialization void Start () {

//Getting all Skinned Renderer from Children Component[] allsmr = GetComponentsInChildren(typeof(SkinnedMeshRenderer)); 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();

/* If you want make a counter in order to get the total of Vertices and Bones do it here ... */

//The Sum of All Child Bones Transform[] totalBones = new Transform[BONE_NUMBER];//Total of Bones for my Example //The Sum of the BindPoses Matrix4x4[] totalBindPoses = new Matrix4x4[BONE_NUMBER];//Total of BindPoses //The Sum of BoneWeights BoneWeight[] totalBoneWeight = new BoneWeight[VERTEX_NUMBER];//total of Vertices for my Example

int offset=0; int b_offset=0; Transform[] usedBones= new Transform[totalBones.Length];

ArrayList myInstances = new ArrayList();

//Setting my Arrays for copies ArrayList myMaterials=new ArrayList();

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;

//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; } }

SkinMeshCombineUtility.MeshInstance[] instances = (SkinMeshCombineUtility.MeshInstance[])myInstances.ToArray(typeof(MeshCombineUtility.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 objRenderer.sharedMaterials = (Material[])myMaterials.ToArray(typeof(Material));

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; } } </csharp>

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox