public IEnumerator GetTextureAtlas(Texture2D[] textures, Action <TextureAtlas> callback) { TextureAtlas atlas = null; // Clear out any atlases that were removed m_Atlases.RemoveAll(a => a == null); yield return(null); foreach (var a in m_Atlases) { // At a minimum the atlas should have all of the textures requested, but can be a superset if (!textures.Except(a.textures).Any()) { atlas = a; break; } yield return(null); } if (!atlas) { atlas = ScriptableObject.CreateInstance <TextureAtlas>(); foreach (var t in textures) { var assetImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); var textureImporter = assetImporter as TextureImporter; if (textureImporter && !textureImporter.isReadable) { textureImporter.isReadable = true; textureImporter.SaveAndReimport(); } else if (!assetImporter) { // In-memory textures need to be saved to disk in order to be referenced by the texture atlas SaveUniqueAtlasAsset(t); } yield return(null); } var textureAtlas = new Texture2D(0, 0, TextureFormat.RGB24, true, PlayerSettings.colorSpace == ColorSpace.Linear); var uvs = textureAtlas.PackTextures(textures.ToArray(), 0, 1024, true); if (uvs != null) { atlas.textureAtlas = textureAtlas; atlas.uvs = uvs; atlas.textures = textures; SaveUniqueAtlasAsset(textureAtlas); SaveUniqueAtlasAsset(atlas); m_Atlases.Add(atlas); } yield return(null); } if (callback != null) { callback(atlas); } }
public IEnumerator Batch(GameObject go) { var renderers = go.GetComponentsInChildren <Renderer>(); var materials = new HashSet <Material>(renderers.SelectMany(r => r.sharedMaterials)); var textures = new HashSet <Texture2D>(materials.Select(m => { if (m) { return(m.mainTexture as Texture2D); } return(null); }).Where(t => t != null)).ToList(); textures.Add(whiteTexture); TextureAtlas atlas = null; yield return(TextureAtlasModule.instance.GetTextureAtlas(textures.ToArray(), a => atlas = a)); var atlasLookup = new Dictionary <Texture2D, Rect>(); var atlasTextures = atlas.textures; for (int i = 0; i < atlasTextures.Length; i++) { atlasLookup[atlasTextures[i]] = atlas.uvs[i]; } MeshFilter[] meshFilters = go.GetComponentsInChildren <MeshFilter>(); var combine = new List <CombineInstance>(); for (int i = 0; i < meshFilters.Length; i++) { var mf = meshFilters[i]; var sharedMesh = mf.sharedMesh; if (!sharedMesh) { continue; } if (!sharedMesh.isReadable) { var assetPath = AssetDatabase.GetAssetPath(sharedMesh); if (!string.IsNullOrEmpty(assetPath)) { var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter; if (importer) { importer.isReadable = true; importer.SaveAndReimport(); } } } var ci = new CombineInstance(); var mesh = Object.Instantiate(sharedMesh); var mr = mf.GetComponent <MeshRenderer>(); var sharedMaterials = mr.sharedMaterials; var uv = mesh.uv; var colors = mesh.colors; if (colors == null || colors.Length == 0) { colors = new Color[uv.Length]; } var updated = new bool[uv.Length]; var triangles = new List <int>(); // Some meshes have submeshes that either aren't expected to render or are missing a material, so go ahead and skip var subMeshCount = Mathf.Min(mesh.subMeshCount, sharedMaterials.Length); for (int j = 0; j < subMeshCount; j++) { var sharedMaterial = sharedMaterials[Mathf.Min(j, sharedMaterials.Length - 1)]; var mainTexture = whiteTexture; var materialColor = Color.white; if (sharedMaterial) { var texture = sharedMaterial.mainTexture as Texture2D; if (texture) { mainTexture = texture; } if (sharedMaterial.HasProperty("_Color")) { materialColor = sharedMaterial.color; } } if (mesh.GetTopology(j) != MeshTopology.Triangles) { Debug.LogWarning("Mesh must have triangles", mf); continue; } triangles.Clear(); mesh.GetTriangles(triangles, j); var uvOffset = atlasLookup[mainTexture]; foreach (var t in triangles) { if (!updated[t]) { var uvCoord = uv[t]; if (mainTexture == whiteTexture) { // Sample at center of white texture to avoid sampling edge colors incorrectly uvCoord.x = 0.5f; uvCoord.y = 0.5f; } uvCoord.x = Mathf.Lerp(uvOffset.xMin, uvOffset.xMax, uvCoord.x); uvCoord.y = Mathf.Lerp(uvOffset.yMin, uvOffset.yMax, uvCoord.y); uv[t] = uvCoord; if (mainTexture == whiteTexture) { colors[t] = materialColor; } else { colors[t] = Color.white; } updated[t] = true; } } yield return(null); } mesh.uv = uv; mesh.uv2 = null; mesh.colors = colors; ci.mesh = mesh; ci.transform = mf.transform.localToWorldMatrix; combine.Add(ci); mf.gameObject.SetActive(false); yield return(null); } var combinedMesh = new Mesh(); combinedMesh.indexFormat = IndexFormat.UInt32; combinedMesh.CombineMeshes(combine.ToArray(), true, true); combinedMesh.RecalculateBounds(); var meshFilter = go.AddComponent <MeshFilter>(); meshFilter.sharedMesh = combinedMesh; for (int i = 0; i < meshFilters.Length; i++) { Object.DestroyImmediate(meshFilters[i].gameObject); } var meshRenderer = go.AddComponent <MeshRenderer>(); var material = new Material(Shader.Find("Custom/AutoLOD/SimpleBatcher")); material.mainTexture = atlas.textureAtlas; meshRenderer.sharedMaterial = material; }
public IEnumerator Batch(GameObject hlodRoot, System.Action <float> progress) { yield return(PackTextures(hlodRoot)); Dictionary <Texture2D, Material> createdMaterials = new Dictionary <Texture2D, Material>(); for (int childIndex = 0; childIndex < hlodRoot.transform.childCount; ++childIndex) { var child = hlodRoot.transform.GetChild(childIndex); var go = child.gameObject; var renderers = go.GetComponentsInChildren <Renderer>(); var materials = new HashSet <Material>(renderers.SelectMany(r => r.sharedMaterials)); TextureAtlas atlas = packer.GetAtlas(go); var atlasLookup = new Dictionary <Texture2D, Rect>(); var atlasTextures = atlas.textures; for (int i = 0; i < atlasTextures.Length; i++) { atlasLookup[atlasTextures[i]] = atlas.uvs[i]; } MeshFilter[] meshFilters = go.GetComponentsInChildren <MeshFilter>(); var combine = new List <CombineInstance>(); for (int i = 0; i < meshFilters.Length; i++) { var mf = meshFilters[i]; var sharedMesh = mf.sharedMesh; if (!sharedMesh) { continue; } if (!sharedMesh.isReadable) { var assetPath = AssetDatabase.GetAssetPath(sharedMesh); if (!string.IsNullOrEmpty(assetPath)) { var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter; if (importer) { importer.isReadable = true; importer.SaveAndReimport(); } } } var mesh = Object.Instantiate(sharedMesh); var mr = mf.GetComponent <MeshRenderer>(); var sharedMaterials = mr.sharedMaterials; var uv = mesh.uv; var colors = mesh.colors; if (colors == null || colors.Length == 0) { colors = new Color[uv.Length]; } var updated = new bool[uv.Length]; var triangles = new List <int>(); // Some meshes have submeshes that either aren't expected to render or are missing a material, so go ahead and skip var subMeshCount = Mathf.Min(mesh.subMeshCount, sharedMaterials.Length); for (int j = 0; j < subMeshCount; j++) { var sharedMaterial = sharedMaterials[Mathf.Min(j, sharedMaterials.Length - 1)]; var mainTexture = whiteTexture; var materialColor = Color.white; if (sharedMaterial) { var texture = GetTexture(sharedMaterial); if (texture) { mainTexture = texture; } if (sharedMaterial.HasProperty("_Color")) { materialColor = sharedMaterial.color; } } if (mesh.GetTopology(j) != MeshTopology.Triangles) { Debug.LogWarning("Mesh must have triangles", mf); continue; } triangles.Clear(); mesh.GetTriangles(triangles, j); var uvOffset = atlasLookup[mainTexture]; foreach (var t in triangles) { if (!updated[t]) { var uvCoord = uv[t]; if (mainTexture == whiteTexture) { // Sample at center of white texture to avoid sampling edge colors incorrectly uvCoord.x = 0.5f; uvCoord.y = 0.5f; } uvCoord.x = Mathf.Lerp(uvOffset.xMin, uvOffset.xMax, uvCoord.x); uvCoord.y = Mathf.Lerp(uvOffset.yMin, uvOffset.yMax, uvCoord.y); uv[t] = uvCoord; if (mainTexture == whiteTexture) { colors[t] = materialColor; } else { colors[t] = Color.white; } updated[t] = true; } } } mesh.uv = uv; mesh.uv2 = null; mesh.colors = colors; for (int j = 0; j < subMeshCount; ++j) { var ci = new CombineInstance(); ci.mesh = mesh; ci.subMeshIndex = j; ci.transform = mf.transform.localToWorldMatrix; combine.Add(ci); } mf.gameObject.SetActive(false); } var combinedMesh = new Mesh(); #if UNITY_2017_3_OR_NEWER combinedMesh.indexFormat = IndexFormat.UInt32; #endif combinedMesh.CombineMeshes(combine.ToArray(), true, true); combinedMesh.RecalculateBounds(); var meshFilter = go.AddComponent <MeshFilter>(); meshFilter.sharedMesh = combinedMesh; for (int i = 0; i < meshFilters.Length; i++) { Object.DestroyImmediate(meshFilters[i].gameObject); } var meshRenderer = go.AddComponent <MeshRenderer>(); Material material = null; if (createdMaterials.ContainsKey(atlas.textureAtlas) == false) { if (option.BatchMaterial == null) { material = new Material(Shader.Find("Custom/AutoLOD/SimpleBatcher")); } else { material = new Material(option.BatchMaterial); } material.mainTexture = atlas.textureAtlas; string matName = hlodRoot.name + "_" + createdMaterials.Count; AssetDatabase.CreateAsset(material, "Assets/" + SceneLOD.GetSceneLODPath() + matName + ".mat"); createdMaterials.Add(atlas.textureAtlas, material); } else { material = createdMaterials[atlas.textureAtlas]; } meshRenderer.sharedMaterial = material; string assetName = hlodRoot.name + "_" + go.name; AssetDatabase.CreateAsset(combinedMesh, "Assets/" + SceneLOD.GetSceneLODPath() + assetName + ".asset"); AssetDatabase.SaveAssets(); if (progress != null) { progress((float)childIndex / (float)hlodRoot.transform.childCount); } yield return(null); } }