using UnityEngine;
|
|
using System;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using DigitalOpus.MB.Core;
|
|
using LuaFramework;
|
|
/// <summary>
|
|
/// Used internally during the material baking process
|
|
/// </summary>
|
|
[Serializable]
|
|
public class MB_AtlasesAndRects{
|
|
public Texture2D[] atlases;
|
|
[NonSerialized]
|
|
public List<MB_MaterialAndUVRect> mat2rect_map;
|
|
public string[] texPropertyNames;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class MB_MultiMaterial{
|
|
public Material combinedMaterial;
|
|
public List<Material> sourceMaterials = new List<Material>();
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class MB_MaterialAndUVRect
|
|
{
|
|
public Material material;
|
|
|
|
//The UV rect in the source texture that was used when copying from source texture to destinationAtlas.
|
|
//If _fixOutOfBoundsUVs it may be smaller than, same size, or larger than 0..1. larger than means that these meshes have out of bounds UVs
|
|
//If !_fixOutOfBoundsUVs it will be 0..1
|
|
public Rect atlasRect;
|
|
public Rect atlasSubrectMaterialOnly; //the subrectangle of atlasRect assuming only material tiling
|
|
|
|
public MB_MaterialAndUVRect(Material m, Rect destRect, Rect matTilingTransformRect)
|
|
{
|
|
material = m;
|
|
atlasRect = destRect;
|
|
atlasSubrectMaterialOnly = matTilingTransformRect;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return material.GetInstanceID() ^ atlasSubrectMaterialOnly.GetHashCode();
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!(obj is MB_MaterialAndUVRect)) return false;
|
|
return (material == ((MB_MaterialAndUVRect)obj).material && atlasSubrectMaterialOnly == ((MB_MaterialAndUVRect)obj).atlasSubrectMaterialOnly);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class stores the results from an MB2_TextureBaker when materials are combined into atlases. It stores
|
|
/// a list of materials and the corresponding UV rectangles in the atlases. It also stores the configuration
|
|
/// options that were used to generate the combined material.
|
|
///
|
|
/// It can be saved as an asset in the project so that textures can be baked in one scene and used in another.
|
|
///
|
|
/// </summary>
|
|
|
|
public class MB2_TextureBakeResults : ScriptableObject {
|
|
public MB_AtlasesAndRects[] combinedMaterialInfo;
|
|
|
|
//Depricated
|
|
public Material[] materials;
|
|
public Rect[] prefabUVRects;
|
|
|
|
public MB_MaterialAndUVRect[] materialsAndUVRects;
|
|
|
|
|
|
public Material resultMaterial;
|
|
public MB_MultiMaterial[] resultMaterials;
|
|
public bool doMultiMaterial;
|
|
public bool fixOutOfBoundsUVs;
|
|
|
|
/// <summary>
|
|
/// Creates for materials on renderer.
|
|
/// </summary>
|
|
/// <returns>Generates an MB2_TextureBakeResult that can be used if all objects to be combined use the same material.
|
|
/// Returns a MB2_TextureBakeResults that will map all materials used by renderer r to
|
|
/// the rectangle 0,0..1,1 in the atlas.</returns>
|
|
/// <param name="r">The red component.</param>
|
|
public static MB2_TextureBakeResults CreateForMaterialsOnRenderer(Renderer r){
|
|
MB2_TextureBakeResults tbr = (MB2_TextureBakeResults) ScriptableObject.CreateInstance( typeof(MB2_TextureBakeResults) );
|
|
//Material[] ms = r.sharedMaterials;
|
|
//MB_MaterialAndUVRect[] mss = new MB_MaterialAndUVRect[r.sharedMaterials.Length];
|
|
List<MB_MaterialAndUVRect> mss = new List<MB_MaterialAndUVRect>();
|
|
Material[] ms;
|
|
for (int i = 0; i < r.sharedMaterials.Length; i++)
|
|
{
|
|
if (r.sharedMaterials[i] != null)
|
|
{
|
|
MB_MaterialAndUVRect matAndUVRect = new MB_MaterialAndUVRect(r.sharedMaterials[i], new Rect(0f, 0f, 1f, 1f), new Rect(0f,0f,1f,1f));
|
|
if (!mss.Contains(matAndUVRect))
|
|
{
|
|
mss.Add(matAndUVRect);
|
|
}
|
|
}
|
|
}
|
|
if (r.sharedMaterials.Length > 1) {
|
|
tbr.prefabUVRects = new Rect[mss.Count];
|
|
tbr.materials = ms = new Material[mss.Count];
|
|
tbr.resultMaterials = new MB_MultiMaterial[mss.Count];
|
|
for (int i = 0; i < mss.Count; i++){
|
|
ms[i] = mss[i].material;
|
|
tbr.prefabUVRects[i] = new Rect(0f,0f,1f,1f);
|
|
tbr.resultMaterials[i] = new MB_MultiMaterial();
|
|
List<Material> sourceMats = new List<Material>();
|
|
sourceMats.Add (ms[i]);
|
|
tbr.resultMaterials[i].sourceMaterials = sourceMats;
|
|
tbr.resultMaterials[i].combinedMaterial = ms[i];
|
|
}
|
|
tbr.doMultiMaterial = true;
|
|
} else {
|
|
tbr.doMultiMaterial = false;
|
|
tbr.prefabUVRects = new Rect[]{new Rect(0f,0f,1f,1f)};
|
|
tbr.materials = ms = new Material[] { mss[0].material };
|
|
tbr.resultMaterial = mss[0].material;
|
|
tbr.resultMaterials = new MB_MultiMaterial[] { new MB_MultiMaterial() };
|
|
List<Material> sourceMats = new List<Material>();
|
|
sourceMats.Add(ms[0]);
|
|
tbr.resultMaterials[0].sourceMaterials = sourceMats;
|
|
tbr.resultMaterials[0].combinedMaterial = mss[0].material;
|
|
}
|
|
tbr.materialsAndUVRects = mss.ToArray();
|
|
tbr.fixOutOfBoundsUVs = false;
|
|
return tbr;
|
|
}
|
|
|
|
public bool ContainsMaterial(Material m)
|
|
{
|
|
if (materialsAndUVRects.Length == 0) {
|
|
materialsAndUVRects = new MB_MaterialAndUVRect[materials.Length];
|
|
for (int i = 0; i < materialsAndUVRects.Length; i++){
|
|
materialsAndUVRects[i] = new MB_MaterialAndUVRect(materials[i], prefabUVRects[i],new Rect(0f,0f,1f,1f));
|
|
}
|
|
}
|
|
for (int i = 0; i < materialsAndUVRects.Length; i++)
|
|
{
|
|
if (materialsAndUVRects[i].material == m){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
public string GetDescription(){
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append("Shaders:\n");
|
|
HashSet<Shader> shaders = new HashSet<Shader>();
|
|
if (materialsAndUVRects != null){
|
|
for (int i = 0; i < materialsAndUVRects.Length; i++){
|
|
shaders.Add(materialsAndUVRects[i].material.shader);
|
|
}
|
|
}
|
|
|
|
foreach(Shader m in shaders){
|
|
sb.Append(" ").Append(m.name).AppendLine();
|
|
}
|
|
sb.Append("Materials:\n");
|
|
if (materialsAndUVRects != null){
|
|
for (int i = 0; i < materialsAndUVRects.Length; i++){
|
|
sb.Append(" ").Append(materialsAndUVRects[i].material.name).AppendLine();
|
|
}
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
public class Material2AtlasRectangleMapper
|
|
{
|
|
MB2_TextureBakeResults tbr;
|
|
//bool allMatsAreUnique;
|
|
int[] numTimesMatAppearsInAtlas;
|
|
MB_MaterialAndUVRect[] matsAndSrcUVRect;
|
|
//Rect[] uvRectInAtlas;
|
|
|
|
public Material2AtlasRectangleMapper(MB2_TextureBakeResults res)
|
|
{
|
|
tbr = res;
|
|
matsAndSrcUVRect = res.materialsAndUVRects;
|
|
//uvRectInAtlas = res.prefabUVRects;
|
|
//allMatsAreUnique = true;
|
|
|
|
//backward compatibility. this may be an old TextureBakeResult which has no materialsAndUVRects if so then build it now
|
|
if (matsAndSrcUVRect == null || matsAndSrcUVRect.Length == 0)
|
|
{
|
|
matsAndSrcUVRect = new MB_MaterialAndUVRect[res.materials.Length];
|
|
for (int i = 0; i < res.materials.Length; i++)
|
|
{
|
|
matsAndSrcUVRect[i] = new MB_MaterialAndUVRect(res.materials[i], res.prefabUVRects[i],new Rect(0f,0f,1f,1f));
|
|
}
|
|
res.materialsAndUVRects = matsAndSrcUVRect;
|
|
}
|
|
|
|
//count the number of times a material appears in the atlas. used for fast lookup
|
|
numTimesMatAppearsInAtlas = new int[matsAndSrcUVRect.Length];
|
|
for (int i = 0; i < matsAndSrcUVRect.Length; i++)
|
|
{
|
|
if (numTimesMatAppearsInAtlas[i] > 1)
|
|
{
|
|
continue;
|
|
}
|
|
int count = 1;
|
|
for (int j = i + 1; j < matsAndSrcUVRect.Length; j++)
|
|
{
|
|
if (matsAndSrcUVRect[i].material == matsAndSrcUVRect[j].material)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
numTimesMatAppearsInAtlas[i] = count;
|
|
if (count > 1)
|
|
{
|
|
//allMatsAreUnique = false;
|
|
for (int j = i + 1; j < matsAndSrcUVRect.Length; j++)
|
|
{
|
|
if (matsAndSrcUVRect[i].material == matsAndSrcUVRect[j].material)
|
|
{
|
|
numTimesMatAppearsInAtlas[j] = count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//a material can appear more than once in an atlas if using fixOutOfBoundsUVs.
|
|
//in this case need to use the UV rect of the mesh to find the correct rectangle.
|
|
public bool TryMapMaterialToUVRect(Material mat, Mesh m, int submeshIdx, MB3_MeshCombinerSingle.MeshChannelsCache meshChannelCache, Dictionary<int, MB_Utility.MeshAnalysisResult[]> meshAnalysisCache,
|
|
out Rect rectInAtlas, out Rect subrectInAtlasMatTiling, ref String errorMsg)
|
|
{
|
|
if (tbr.materialsAndUVRects.Length == 0 && tbr.materials.Length > 0) {
|
|
errorMsg = "The 'Material Bake Result' needs to be re-baked to be compatible with this version of Mesh Baker. Please re-bake using the MB3_TextureBaker.";
|
|
rectInAtlas = new Rect();
|
|
subrectInAtlasMatTiling = new Rect();
|
|
return false;
|
|
}
|
|
if (mat == null)
|
|
{
|
|
rectInAtlas = new Rect();
|
|
subrectInAtlasMatTiling = new Rect();
|
|
errorMsg = String.Format("Mesh {0} Had no material on submesh {1} cannot map to a material in the atlas", m.name, submeshIdx);
|
|
return false;
|
|
}
|
|
if (submeshIdx >= m.subMeshCount)
|
|
{
|
|
errorMsg = "Submesh index is greater than the number of submeshes";
|
|
rectInAtlas = new Rect();
|
|
subrectInAtlasMatTiling = new Rect();
|
|
return false;
|
|
}
|
|
|
|
//find the first index of this material
|
|
int idx = -1;
|
|
for (int i = 0; i < matsAndSrcUVRect.Length; i++)
|
|
{
|
|
if (mat == matsAndSrcUVRect[i].material)
|
|
{
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
// if couldn't find material
|
|
if (idx == -1)
|
|
{
|
|
rectInAtlas = new Rect();
|
|
subrectInAtlasMatTiling = new Rect();
|
|
errorMsg = String.Format("Material {0} could not be found in the Material Bake Result", mat.name);
|
|
return false;
|
|
}
|
|
|
|
if (!tbr.fixOutOfBoundsUVs)
|
|
{
|
|
if (numTimesMatAppearsInAtlas[idx] != 1)
|
|
{
|
|
LogManager.LogError("There is a problem with this TextureBakeResults. FixOutOfBoundsUVs is false and a material appears more than once.");
|
|
}
|
|
rectInAtlas = matsAndSrcUVRect[idx].atlasRect;
|
|
subrectInAtlasMatTiling = matsAndSrcUVRect[idx].atlasSubrectMaterialOnly;
|
|
return true;
|
|
} else {
|
|
//todo what if no UVs
|
|
//Find UV rect in source mesh
|
|
|
|
MB_Utility.MeshAnalysisResult[] mar;
|
|
if (!meshAnalysisCache.TryGetValue(m.GetInstanceID(), out mar))
|
|
{
|
|
mar = new MB_Utility.MeshAnalysisResult[m.subMeshCount];
|
|
for (int j = 0; j < m.subMeshCount; j++)
|
|
{
|
|
Vector2[] uvss = meshChannelCache.GetUv0Raw(m);
|
|
MB_Utility.hasOutOfBoundsUVs(uvss, m, ref mar[j], j);
|
|
}
|
|
meshAnalysisCache.Add(m.GetInstanceID(), mar);
|
|
}
|
|
|
|
//this could be a mesh that was not used in the texture baking that has huge UV tiling too big for the rect that was baked
|
|
//find a record that has an atlas uvRect capable of containing this
|
|
bool found = false;
|
|
for (int i = idx; i < matsAndSrcUVRect.Length; i++)
|
|
{
|
|
if (matsAndSrcUVRect[i].material == mat)
|
|
{
|
|
//calculate what the UV rect in the atlas would be if we combined the material tiling of this rect with the UV tiling of this submesh
|
|
Rect potentialRect = new Rect();
|
|
Rect uvR = mar[submeshIdx].uvRect;
|
|
Rect matR = matsAndSrcUVRect[i].atlasSubrectMaterialOnly;
|
|
potentialRect = MB3_UVTransformUtility.CombineTransforms(ref matR,ref uvR);
|
|
//MB3_UVTransformUtility.Canonicalize(ref potentialRect);
|
|
// test to see if this would fit in what was baked in the atlas
|
|
Rect rr = new Rect(0f, 0f, 1f, 1f);
|
|
if (MB3_UVTransformUtility.RectContains(ref rr, ref potentialRect)) {
|
|
idx = i;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
rectInAtlas = matsAndSrcUVRect[idx].atlasRect;
|
|
subrectInAtlasMatTiling = matsAndSrcUVRect[idx].atlasSubrectMaterialOnly;
|
|
return true;
|
|
} else
|
|
{
|
|
rectInAtlas = new Rect();
|
|
subrectInAtlasMatTiling = new Rect();
|
|
errorMsg = String.Format("Could not find a tiled rectangle in the atlas capable of containing the uv and material tiling on mesh {0}", m.name);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|