Created
December 8, 2015 15:30
-
-
Save romainPechot/168249eff6dbfac22b9a to your computer and use it in GitHub Desktop.
Quick tool for batching a lot of small meshes inside a SkinnedMesh. The genuine meshes have to use the same Material because of the SkinnedMeshRenderer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.Rendering; | |
using System.Collections.Generic; | |
[System.Serializable] | |
public class RendererShadowLightSetup | |
{ | |
[SerializeField] | |
private ShadowCastingMode shadowCastingMode = ShadowCastingMode.TwoSided; | |
public ShadowCastingMode SHADOW_CASTIN_MODE { get { return shadowCastingMode; } } | |
[SerializeField] | |
private bool receiveShadows = true; | |
public bool RECEIVE_SHADOWS { get { return receiveShadows; } } | |
[SerializeField] | |
private bool useLightProbes = false; | |
public bool USE_LIGHT_PROBES { get { return useLightProbes; } } | |
[SerializeField] | |
private ReflectionProbeUsage reflectionProbeUsage = ReflectionProbeUsage.Off; | |
public ReflectionProbeUsage REFLECTION_PROBE_USAGE { get { return reflectionProbeUsage; } } | |
public void Apply(Renderer[] renderers) | |
{ | |
if(renderers != null && renderers.Length > 0) | |
{ | |
for(int i = 0; i < renderers.Length; i++) | |
{ | |
Apply(renderers[i]); | |
} | |
} | |
else Debug.LogWarning("Missing Renderer[] ref, or array is empty!"); | |
}// Apply() | |
public void Apply(Renderer renderer) | |
{ | |
if(renderer) | |
{ | |
// shadow casting | |
renderer.shadowCastingMode = shadowCastingMode; | |
// receive shadow | |
renderer.receiveShadows = receiveShadows; | |
// use light probe | |
renderer.useLightProbes = useLightProbes; | |
// reflection probe usage | |
renderer.reflectionProbeUsage = reflectionProbeUsage; | |
} | |
else Debug.LogWarning("Missing Renderer ref!"); | |
}// Apply() | |
}// RendererShadowLightSetup | |
public static class SkinnedMeshRendererMerger | |
{ | |
public const int MAX_VERTICES_PER_MESH = 65536; | |
private static int sub_meshes_index_start = 0; | |
private static int sub_meshes_index_end = 0; | |
public static SkinnedMeshRenderer[] Generate(MeshFilter[] genuine_mesh_filters, Material material = null, Transform root_skinned_meshes = null, SkinQuality skin_quality = SkinQuality.Bone1, bool destroy_genuine = true, RendererShadowLightSetup renderer_shadow_ligth_setup = null) | |
{ | |
// temp list skinned meshrenderer | |
List<SkinnedMeshRenderer> list_skinned_mesh_merges = new List<SkinnedMeshRenderer>(); | |
while(sub_meshes_index_start < genuine_mesh_filters.Length) | |
{ | |
// new skin mesh | |
SkinnedMeshRenderer skinned_mesh_renderer = new GameObject("SkinnedMeshRenderer " + list_skinned_mesh_merges.Count.ToString(), typeof(SkinnedMeshRenderer)).GetComponent<SkinnedMeshRenderer>(); | |
Transform transform_skinned_mesh_renderer = skinned_mesh_renderer.transform; | |
// reset transform | |
transform_skinned_mesh_renderer.SetParent(root_skinned_meshes); | |
transform_skinned_mesh_renderer.localPosition = Vector3.zero; | |
transform_skinned_mesh_renderer.localRotation = Quaternion.identity; | |
transform_skinned_mesh_renderer.localScale = Vector3.one; | |
// skinmesh values | |
int vert_count = 0; | |
int norm_count = 0; | |
int tri_count = 0; | |
int uv_count = 0; | |
sub_meshes_index_end = sub_meshes_index_start; | |
int sub_meshes_count = 0; | |
bool count_done = false; | |
while(!count_done) | |
{ | |
// NOT out of bounds ? | |
if(sub_meshes_index_end < genuine_mesh_filters.Length) | |
{ | |
// NOT null ? | |
if(genuine_mesh_filters[sub_meshes_index_end]) | |
{ | |
// fetch new submesh | |
Mesh mesh = genuine_mesh_filters[sub_meshes_index_end].mesh; | |
// mesh is readable ? | |
if(mesh.isReadable) | |
{ | |
// still inside vertices limit ? | |
if(vert_count + mesh.vertexCount < MAX_VERTICES_PER_MESH) | |
{ | |
// add to count | |
vert_count += mesh.vertices.Length; | |
norm_count += mesh.normals.Length; | |
tri_count += mesh.triangles.Length; | |
uv_count += mesh.uv.Length; | |
// sub index up | |
sub_meshes_index_end++; | |
// update count | |
sub_meshes_count++; | |
} | |
else | |
{ | |
count_done = true; | |
} | |
} | |
else | |
{ | |
Debug.LogWarning("Mesh \"" + mesh.name + "\" is marked as not readable!"); | |
// sub index up | |
sub_meshes_index_end++; | |
} | |
} | |
else | |
{ | |
Debug.LogWarning("Missing MeshFilter ref at index " + sub_meshes_index_end); | |
// sub index up | |
sub_meshes_index_end++; | |
} | |
} | |
else | |
{ | |
count_done = true; | |
} | |
} | |
if(sub_meshes_count > 0) | |
{ | |
// allocate arrays | |
Vector3[] verts = new Vector3[vert_count]; | |
Vector3[] norms = new Vector3[norm_count]; | |
Transform[] tBones = new Transform[sub_meshes_count]; | |
Matrix4x4[] bindPoses = new Matrix4x4[sub_meshes_count]; | |
BoneWeight[] weights = new BoneWeight[vert_count]; | |
int[] tris = new int[tri_count]; | |
Vector2[] uvs = new Vector2[uv_count]; | |
int vertOffset = 0; | |
int normOffset = 0; | |
int triOffset = 0; | |
int uvOffset = 0; | |
int meshOffset = 0; | |
// merge the meshes and set up bones | |
for(int i = sub_meshes_index_start; i < sub_meshes_index_end; i++) | |
{ | |
MeshFilter meshFilter = genuine_mesh_filters[i]; | |
Mesh mesh = meshFilter.mesh; | |
int[] tri = mesh.triangles; | |
for(int j = 0; j < tri.Length; j++) | |
{ | |
tris[triOffset++] = tri[j] + vertOffset; | |
} | |
tBones[meshOffset] = meshFilter.transform; | |
bindPoses[meshOffset] = Matrix4x4.identity; | |
Vector3[] vtr3 = mesh.vertices; | |
for(int j = 0; j < vtr3.Length; j++) | |
{ | |
weights[vertOffset].weight0 = 1f; | |
weights[vertOffset].boneIndex0 = meshOffset; | |
verts[vertOffset++] = vtr3[j]; | |
} | |
Vector3[] nrm = mesh.normals; | |
for(int j = 0; j < nrm.Length; j++) | |
{ | |
norms[normOffset++] = nrm[j]; | |
} | |
Vector2[] uv = mesh.uv; | |
for(int j = 0; j < uv.Length; j++) | |
{ | |
uvs[uvOffset++] = uv[j]; | |
} | |
meshOffset++; | |
} | |
// generate skinned mesh | |
Mesh skinned_mesh = new Mesh(); | |
// setup mesh | |
skinned_mesh.name = transform_skinned_mesh_renderer.name; | |
skinned_mesh.vertices = verts; | |
skinned_mesh.normals = norms; | |
skinned_mesh.boneWeights = weights; | |
skinned_mesh.uv = uvs; | |
skinned_mesh.triangles = tris; | |
skinned_mesh.bindposes = bindPoses; | |
// hook up to the skin mesh renderer | |
skinned_mesh_renderer.quality = skin_quality; | |
skinned_mesh_renderer.sharedMesh = skinned_mesh; | |
skinned_mesh_renderer.bones = tBones; | |
skinned_mesh_renderer.material = material; | |
// setup ligth/shadow ? | |
if(renderer_shadow_ligth_setup != null) | |
{ | |
renderer_shadow_ligth_setup.Apply(skinned_mesh_renderer); | |
} | |
// recalculate bounds | |
skinned_mesh_renderer.sharedMesh.RecalculateBounds(); | |
// add to temp list | |
list_skinned_mesh_merges.Add(skinned_mesh_renderer); | |
// update main index | |
sub_meshes_index_start = sub_meshes_index_end; | |
} | |
else | |
{ | |
sub_meshes_index_start = sub_meshes_index_end = int.MaxValue; | |
} | |
}// while() | |
// destroy sub mesh filter/renderer ? | |
if(destroy_genuine) | |
{ | |
for(int i = 0; i < genuine_mesh_filters.Length; i++) | |
{ | |
if(genuine_mesh_filters[i]) | |
{ | |
// mesh renderer ? | |
MeshRenderer meshRenderer = genuine_mesh_filters[i].GetComponent<MeshRenderer>(); | |
if(meshRenderer) | |
{ | |
GameObject.Destroy(meshRenderer); | |
} | |
// mesh | |
Mesh mesh = genuine_mesh_filters[i].mesh; | |
if(mesh) | |
{ | |
GameObject.Destroy(mesh); | |
} | |
GameObject.Destroy(genuine_mesh_filters[i]); | |
} | |
} | |
} | |
return list_skinned_mesh_merges.ToArray(); | |
}// Generate() | |
}// SkinnedMeshMerger |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment