using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using DigitalOpus.MB.Core;
using UnityEngine.Serialization;
///
/// Used internally during the material baking process
///
[Serializable]
public class MB_AtlasesAndRects{
///
/// One atlas per texture property.
///
public Texture2D[] atlases;
[NonSerialized]
public List mat2rect_map;
public string[] texPropertyNames;
}
public class MB_TextureArrayResultMaterial
{
public MB_AtlasesAndRects[] slices;
}
[System.Serializable]
public class MB_MultiMaterial{
public Material combinedMaterial;
public bool considerMeshUVs;
public List sourceMaterials = new List();
}
[System.Serializable]
public class MB_TexArraySliceRendererMatPair
{
public Material sourceMaterial;
public GameObject renderer;
}
[System.Serializable]
public class MB_TexArraySlice
{
public bool considerMeshUVs;
public List sourceMaterials = new List();
public bool ContainsMaterial(Material mat)
{
for (int i = 0; i < sourceMaterials.Count; i++)
{
if (sourceMaterials[i].sourceMaterial == mat) return true;
}
return false;
}
public HashSet GetDistinctMaterials()
{
HashSet distinctMats = new HashSet();
if (sourceMaterials == null) return distinctMats;
for (int i = 0; i < sourceMaterials.Count; i++)
{
distinctMats.Add(sourceMaterials[i].sourceMaterial);
}
return distinctMats;
}
public bool ContainsMaterialAndMesh(Material mat, Mesh mesh)
{
for (int i = 0; i < sourceMaterials.Count; i++)
{
if (sourceMaterials[i].sourceMaterial == mat &&
MB_Utility.GetMesh(sourceMaterials[i].renderer) == mesh) return true;
}
return false;
}
public List GetAllUsedMaterials(List usedMats)
{
usedMats.Clear();
for (int i = 0; i < sourceMaterials.Count; i++)
{
usedMats.Add(sourceMaterials[i].sourceMaterial);
}
return usedMats;
}
public List GetAllUsedRenderers(List allObjsFromTextureBaker)
{
if (considerMeshUVs)
{
List usedRendererGOs = new List();
for (int i = 0; i < sourceMaterials.Count; i++)
{
usedRendererGOs.Add(sourceMaterials[i].renderer);
}
return usedRendererGOs;
}
else
{
return allObjsFromTextureBaker;
}
}
}
[System.Serializable]
public class MB_TextureArrayReference
{
public string texFromatSetName;
public Texture2DArray texArray;
public MB_TextureArrayReference(string formatSetName, Texture2DArray ta)
{
texFromatSetName = formatSetName;
texArray = ta;
}
}
[System.Serializable]
public class MB_TexArrayForProperty
{
public string texPropertyName;
public MB_TextureArrayReference[] formats = new MB_TextureArrayReference[0];
public MB_TexArrayForProperty(string name, MB_TextureArrayReference[] texRefs)
{
texPropertyName = name;
formats = texRefs;
}
}
[System.Serializable]
public class MB_MultiMaterialTexArray
{
public Material combinedMaterial;
public List slices = new List();
public List textureProperties = new List();
}
[System.Serializable]
public class MB_TextureArrayFormat
{
public string propertyName;
public TextureFormat format;
}
[System.Serializable]
public class MB_TextureArrayFormatSet
{
public string name;
public TextureFormat defaultFormat;
public MB_TextureArrayFormat[] formatOverrides;
public bool ValidateTextureImporterFormatsExistsForTextureFormats(MB2_EditorMethodsInterface editorMethods, int idx)
{
if (editorMethods == null) return true;
if (!editorMethods.TextureImporterFormatExistsForTextureFormat(defaultFormat))
{
Debug.LogError("TextureImporter format does not exist for Texture Array Output Formats: " + idx + " Defaut Format " + defaultFormat);
return false;
}
for (int i = 0; i < formatOverrides.Length; i++)
{
if (!editorMethods.TextureImporterFormatExistsForTextureFormat(formatOverrides[i].format))
{
Debug.LogError("TextureImporter format does not exist for Texture Array Output Formats: " + idx + " Format Overrides: " + i + " (" + formatOverrides[i].format + ")");
return false;
}
}
return true;
}
public TextureFormat GetFormatForProperty(string propName)
{
for (int i = 0; i < formatOverrides.Length; i++)
{
if (formatOverrides.Equals(formatOverrides[i].propertyName))
{
return formatOverrides[i].format;
}
}
return defaultFormat;
}
}
[System.Serializable]
public class MB_MaterialAndUVRect
{
///
/// The source material that was baked into the atlas.
///
public Material material;
///
/// The rectangle in the atlas where the texture (including all tiling) was copied to.
///
public Rect atlasRect;
///
/// For debugging. The name of the first srcObj that uses this MaterialAndUVRect.
///
public string srcObjName;
public int textureArraySliceIdx = -1;
public bool allPropsUseSameTiling = true;
///
/// Only valid if allPropsUseSameTiling = true. Else should be 0,0,0,0
/// The material tiling on the source material
///
[FormerlySerializedAs("sourceMaterialTiling")]
public Rect allPropsUseSameTiling_sourceMaterialTiling;
///
/// Only valid if allPropsUseSameTiling = true. Else should be 0,0,0,0
/// The encapsulating sampling rect that was used to sample for the atlas. Note that the case
/// of dont-considerMeshUVs is the same as do-considerMeshUVs where the uv rect is 0,0,1,1
///
[FormerlySerializedAs("samplingEncapsulatinRect")]
public Rect allPropsUseSameTiling_samplingEncapsulatinRect;
///
/// Only valid if allPropsUseSameTiling = false.
/// The UVrect of the source mesh that was baked. We are using a trick here.
/// Instead of storing the material tiling for each
/// texture property here, we instead bake all those tilings into the atlases and here we pretend
/// that all those tilings were 0,0,1,1. Then all we need is to store is the
/// srcUVsamplingRect
///
public Rect propsUseDifferntTiling_srcUVsamplingRect;
[NonSerialized]
public List objectsThatUse;
///
/// The tilling type for this rectangle in the atlas.
///
public MB_TextureTilingTreatment tilingTreatment = MB_TextureTilingTreatment.unknown;
/// The Material
/// The rect in the atlas this material maps to
/// If true then use sourceMaterialTiling and samplingEncapsulatingRect.
/// if false then use srcUVsamplingRect. None used values should be 0,0,0,0.
/// allPropsUseSameTiling_sourceMaterialTiling
/// allPropsUseSameTiling_samplingEncapsulatinRect
/// propsUseDifferntTiling_srcUVsamplingRect
public MB_MaterialAndUVRect(Material mat,
Rect destRect,
bool allPropsUseSameTiling,
Rect sourceMaterialTiling,
Rect samplingEncapsulatingRect,
Rect srcUVsamplingRect,
MB_TextureTilingTreatment treatment,
string objName)
{
if (allPropsUseSameTiling)
{
Debug.Assert(srcUVsamplingRect == new Rect(0, 0, 0, 0));
}
if (!allPropsUseSameTiling) {
Debug.Assert(samplingEncapsulatingRect == new Rect(0, 0, 0, 0));
Debug.Assert(sourceMaterialTiling == new Rect(0, 0, 0, 0));
}
material = mat;
atlasRect = destRect;
tilingTreatment = treatment;
this.allPropsUseSameTiling = allPropsUseSameTiling;
allPropsUseSameTiling_sourceMaterialTiling = sourceMaterialTiling;
allPropsUseSameTiling_samplingEncapsulatinRect = samplingEncapsulatingRect;
propsUseDifferntTiling_srcUVsamplingRect = srcUVsamplingRect;
srcObjName = objName;
}
public override int GetHashCode()
{
return material.GetInstanceID() ^ allPropsUseSameTiling_samplingEncapsulatinRect.GetHashCode() ^ propsUseDifferntTiling_srcUVsamplingRect.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is MB_MaterialAndUVRect)) return false;
MB_MaterialAndUVRect b = (MB_MaterialAndUVRect)obj;
return material == b.material &&
allPropsUseSameTiling_samplingEncapsulatinRect == b.allPropsUseSameTiling_samplingEncapsulatinRect &&
allPropsUseSameTiling_sourceMaterialTiling == b.allPropsUseSameTiling_sourceMaterialTiling &&
allPropsUseSameTiling == b.allPropsUseSameTiling &&
propsUseDifferntTiling_srcUVsamplingRect == b.propsUseDifferntTiling_srcUVsamplingRect;
}
public Rect GetEncapsulatingRect()
{
if (allPropsUseSameTiling)
{
return allPropsUseSameTiling_samplingEncapsulatinRect;
}
else
{
return propsUseDifferntTiling_srcUVsamplingRect;
}
}
public Rect GetMaterialTilingRect()
{
if (allPropsUseSameTiling)
{
return allPropsUseSameTiling_sourceMaterialTiling;
}
else
{
return new Rect(0, 0, 1, 1);
}
}
}
///
/// 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.
///
public class MB2_TextureBakeResults : ScriptableObject {
public enum ResultType
{
atlas,
textureArray,
}
public static int VERSION { get { return 3252; } }
public int version;
public ResultType resultType = ResultType.atlas;
///
/// Information about the atlas UV rects.
/// IMPORTANT a source material can appear more than once in this list. If we are using considerUVs,
/// then different parts of a source texture could be extracted to different atlas rects.
///
public MB_MaterialAndUVRect[] materialsAndUVRects;
///
/// This is like the combinedMeshRenderer.sharedMaterials. Some materials may be omitted if they have zero submesh triangles.
/// There is one of these per MultiMaterial mapping.
/// Lists source materials that were combined into each result material, and whether considerUVs was used.
/// This is the result materials if building for atlases.
///
public MB_MultiMaterial[] resultMaterials;
///
/// This is like combinedMeshRenderer.sharedMaterials. Each result mat likely uses a different shader.
/// There is one of these per MultiMaterialTexArray mapping.
/// Each of these lists the slices and each slice has list of source mats that are in that slice.
/// This is the result materials if building for texArrays.
///
public MB_MultiMaterialTexArray[] resultMaterialsTexArray;
public bool doMultiMaterial;
public MB2_TextureBakeResults()
{
version = VERSION;
}
private void OnEnable()
{
// backward compatibility copy depricated fixOutOfBounds values to resultMaterials
if (version < 3251)
{
for (int i = 0; i < materialsAndUVRects.Length; i++)
{
materialsAndUVRects[i].allPropsUseSameTiling = true;
}
}
version = VERSION;
}
public int NumResultMaterials()
{
if (resultType == ResultType.atlas) return resultMaterials.Length;
else return resultMaterialsTexArray.Length;
}
public Material GetCombinedMaterialForSubmesh(int idx)
{
if (resultType == ResultType.atlas)
{
return resultMaterials[idx].combinedMaterial;
} else
{
return resultMaterialsTexArray[idx].combinedMaterial;
}
}
public bool GetConsiderMeshUVs(int idxInSrcMats, Material srcMaterial)
{
if (resultType == ResultType.atlas)
{
return resultMaterials[idxInSrcMats].considerMeshUVs;
}
else
{
// TODO do this once and cache.
List slices = resultMaterialsTexArray[idxInSrcMats].slices;
for (int i = 0; i < slices.Count; i++)
{
if (slices[i].ContainsMaterial(srcMaterial)) return slices[i].considerMeshUVs;
}
Debug.LogError("There were no source materials for any slice in this result material.");
return false;
}
}
public List GetSourceMaterialsUsedByResultMaterial(int resultMatIdx)
{
if (resultType == ResultType.atlas)
{
return resultMaterials[resultMatIdx].sourceMaterials;
} else
{
// TODO do this once and cache.
HashSet output = new HashSet();
List slices = resultMaterialsTexArray[resultMatIdx].slices;
for (int i = 0; i < slices.Count; i++)
{
List usedMaterials = new List();
slices[i].GetAllUsedMaterials(usedMaterials);
for (int j = 0; j < usedMaterials.Count; j++)
{
output.Add(usedMaterials[j]);
}
}
List outMats = new List(output);
return outMats;
}
}
///
/// Creates for materials on renderer.
///
/// 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.
public static MB2_TextureBakeResults CreateForMaterialsOnRenderer(GameObject[] gos, List matsOnTargetRenderer)
{
HashSet fullMaterialList = new HashSet(matsOnTargetRenderer);
for (int i = 0; i < gos.Length; i++)
{
if (gos[i] == null)
{
Debug.LogError(string.Format("Game object {0} in list of objects to add was null", i));
return null;
}
Material[] oMats = MB_Utility.GetGOMaterials(gos[i]);
if (oMats.Length == 0)
{
Debug.LogError(string.Format("Game object {0} in list of objects to add no renderer", i));
return null;
}
for (int j = 0; j < oMats.Length; j++)
{
if (!fullMaterialList.Contains(oMats[j])) { fullMaterialList.Add(oMats[j]); }
}
}
Material[] rms = new Material[fullMaterialList.Count];
fullMaterialList.CopyTo(rms);
MB2_TextureBakeResults tbr = (MB2_TextureBakeResults) ScriptableObject.CreateInstance( typeof(MB2_TextureBakeResults) );
List mss = new List();
for (int i = 0; i < rms.Length; i++)
{
if (rms[i] != null)
{
MB_MaterialAndUVRect matAndUVRect = new MB_MaterialAndUVRect(rms[i], new Rect(0f, 0f, 1f, 1f), true, new Rect(0f,0f,1f,1f), new Rect(0f,0f,1f,1f), new Rect(0,0,0,0), MB_TextureTilingTreatment.none, "");
if (!mss.Contains(matAndUVRect))
{
mss.Add(matAndUVRect);
}
}
}
tbr.resultMaterials = new MB_MultiMaterial[mss.Count];
for (int i = 0; i < mss.Count; i++){
tbr.resultMaterials[i] = new MB_MultiMaterial();
List sourceMats = new List();
sourceMats.Add (mss[i].material);
tbr.resultMaterials[i].sourceMaterials = sourceMats;
tbr.resultMaterials[i].combinedMaterial = mss[i].material;
tbr.resultMaterials[i].considerMeshUVs = false;
}
if (rms.Length == 1)
{
tbr.doMultiMaterial = false;
} else
{
tbr.doMultiMaterial = true;
}
tbr.materialsAndUVRects = mss.ToArray();
return tbr;
}
public bool DoAnyResultMatsUseConsiderMeshUVs()
{
if (resultType == ResultType.atlas)
{
if (resultMaterials == null) return false;
for (int i = 0; i < resultMaterials.Length; i++)
{
if (resultMaterials[i].considerMeshUVs) return true;
}
return false;
}
else
{
if (resultMaterialsTexArray == null) return false;
for (int i = 0; i < resultMaterialsTexArray.Length; i++)
{
MB_MultiMaterialTexArray resMat = resultMaterialsTexArray[i];
for (int j = 0; j < resMat.slices.Count; j++)
{
if (resMat.slices[j].considerMeshUVs) return true;
}
}
return false;
}
}
public bool ContainsMaterial(Material m)
{
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 shaders = new HashSet();
if (materialsAndUVRects != null){
for (int i = 0; i < materialsAndUVRects.Length; i++){
if (materialsAndUVRects[i].material != null)
{
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++){
if (materialsAndUVRects[i].material != null)
{
sb.Append(" ").Append(materialsAndUVRects[i].material.name).AppendLine();
}
}
}
return sb.ToString();
}
public void UpgradeToCurrentVersion(MB2_TextureBakeResults tbr)
{
if (tbr.version < 3252)
{
for (int i = 0; i < tbr.materialsAndUVRects.Length; i++)
{
tbr.materialsAndUVRects[i].allPropsUseSameTiling = true;
}
}
}
public static bool IsMeshAndMaterialRectEnclosedByAtlasRect(MB_TextureTilingTreatment tilingTreatment, Rect uvR, Rect sourceMaterialTiling, Rect samplingEncapsulatinRect, MB2_LogLevel logLevel)
{
Rect potentialRect = new Rect();
// test to see if this would fit in what was baked in the atlas
potentialRect = MB3_UVTransformUtility.CombineTransforms(ref uvR, ref sourceMaterialTiling);
if (logLevel >= MB2_LogLevel.trace)
{
if (logLevel >= MB2_LogLevel.trace) Debug.Log("IsMeshAndMaterialRectEnclosedByAtlasRect Rect in atlas uvR=" + uvR.ToString("f5") + " sourceMaterialTiling=" + sourceMaterialTiling.ToString("f5") + "Potential Rect (must fit in encapsulating) " + potentialRect.ToString("f5") + " encapsulating=" + samplingEncapsulatinRect.ToString("f5") + " tilingTreatment=" + tilingTreatment);
}
if (tilingTreatment == MB_TextureTilingTreatment.edgeToEdgeX)
{
if (MB3_UVTransformUtility.LineSegmentContainsShifted(samplingEncapsulatinRect.y, samplingEncapsulatinRect.height, potentialRect.y, potentialRect.height))
{
return true;
}
}
else if (tilingTreatment == MB_TextureTilingTreatment.edgeToEdgeY)
{
if (MB3_UVTransformUtility.LineSegmentContainsShifted(samplingEncapsulatinRect.x, samplingEncapsulatinRect.width, potentialRect.x, potentialRect.width))
{
return true;
}
}
else if (tilingTreatment == MB_TextureTilingTreatment.edgeToEdgeXY)
{
//only one rect in atlas and is edge to edge in both X and Y directions.
return true;
}
else
{
if (MB3_UVTransformUtility.RectContainsShifted(ref samplingEncapsulatinRect, ref potentialRect))
{
return true;
}
}
return false;
}
}