/// <summary>
 /// 判断网格超出 UV
 /// </summary>
 /// <param name="m"></param>
 /// <param name="putResultHere"></param>
 /// <param name="submeshIndex"></param>
 /// <param name="uvChannel"></param>
 /// <returns></returns>
 public static bool hasOutOfBoundsUVs(Mesh m, ref MeshAnalysisResult putResultHere, int submeshIndex = -1, int uvChannel = 0)
 {
     if (m == null)
     {
         putResultHere.hasOutOfBoundsUVs = false;
         return(putResultHere.hasOutOfBoundsUVs);
     }
     Vector2[] uvs;
     if (uvChannel == 0)
     {
         uvs = m.uv;
     }
     else if (uvChannel == 1)
     {
         uvs = m.uv2;
     }
     else if (uvChannel == 2)
     {
         uvs = m.uv3;
     }
     else
     {
         uvs = m.uv4;
     }
     return(hasOutOfBoundsUVs(uvs, m, ref putResultHere, submeshIndex));
 }
        public static int doSubmeshesShareVertsOrTris(Mesh m, ref MeshAnalysisResult mar)
        {
            MB_Triangle consider = new MB_Triangle();
            MB_Triangle other    = new MB_Triangle();

            //cache all triangles
            int[][] tris = new int[m.subMeshCount][];
            for (int i = 0; i < m.subMeshCount; i++)
            {
                tris[i] = m.GetTriangles(i);
            }
            bool sharesVerts = false;
            bool sharesTris  = false;

            for (int i = 0; i < m.subMeshCount; i++)
            {
                int[] smA = tris[i];
                for (int j = i + 1; j < m.subMeshCount; j++)
                {
                    int[] smB = tris[j];
                    for (int k = 0; k < smA.Length; k += 3)
                    {
                        consider.Initialize(smA, k, i);
                        for (int l = 0; l < smB.Length; l += 3)
                        {
                            other.Initialize(smB, l, j);
                            if (consider.isSame(other))
                            {
                                sharesTris = true;
                                break;
                            }
                            if (consider.sharesVerts(other))
                            {
                                sharesVerts = true;
                                break;
                            }
                        }
                    }
                }
            }
            if (sharesTris)
            {
                mar.hasOverlappingSubmeshVerts = true;
                mar.hasOverlappingSubmeshTris  = true;
                return(2);
            }
            else if (sharesVerts)
            {
                mar.hasOverlappingSubmeshVerts = true;
                mar.hasOverlappingSubmeshTris  = false;
                return(1);
            }
            else
            {
                mar.hasOverlappingSubmeshTris  = false;
                mar.hasOverlappingSubmeshVerts = false;
                return(0);
            }
        }
        public static bool hasOutOfBoundsUVs(Mesh m, ref Rect uvBounds)
        {
            MeshAnalysisResult mar = new MeshAnalysisResult();
            bool outVal            = hasOutOfBoundsUVs(m, ref mar);

            uvBounds = mar.uvRect;
            return(outVal);
        }
        /// <summary>
        /// A material can appear more than once in an atlas if using fixOutOfBoundsUVs.
        /// in this case you need to use the UV rect of the mesh to find the correct rectangle.
        /// If the all properties on the mat use the same tiling then
        /// encapsulatingRect can be larger and will include baked UV and material tiling
        /// If mat uses different tiling for different maps then encapsulatingRect is the uvs of
        /// source mesh used to bake atlas and sourceMaterialTilingOut is 0,0,1,1. This works because
        /// material tiling was baked into the atlas.
        /// 尝试获取源物体材质在合并材质中的映射信息
        /// 如果使用fixOutOfBoundsUVs,一个材质可以在图集中出现多次。在这种情况下,您需要使用网格的UV矩形来找到正确的矩形。
        /// 如果材质上的所有属性都使用相同的拼贴,则 encapsulatingRect 可以更大,并将包含烘焙的UV和材质平铺
        /// 如果mat 对不同映射使用不同的tiling,则 encapsulatingRect 是用于烘焙图集的 uvs 且 sourceMaterialTilingOut 为0,0,1,1。
        /// 材质 tiling 烘焙到 atlas 中。
        /// </summary>
        public bool TryGetMaterialToUVRectMap(Material sourceMat,
                                              Mesh sourceMesh,
                                              int submeshIdx,
                                              int idxInResultMats,
                                              MeshChannelsCache meshChannelCache,
                                              Dictionary <int, MeshAnalysisResult[]> meshAnalysisCache,
                                              out TextureTilingTreatment tilingTreatment,
                                              out Rect rectInAtlas,
                                              out Rect encapsulatingRectOut,
                                              out Rect sourceMaterialTilingOut,
                                              ref string errorMsg)
        {
            for (int i = 0; i < resultAsset.materialsAndUVRects.Length; i++)
            {
                resultAsset.materialsAndUVRects[i].allPropsUseSameTiling = true;
            }

            tilingTreatment = TextureTilingTreatment.unknown;
            if (resultAsset.materialsAndUVRects.Length == 0)
            {
                errorMsg                = "Texture Bake Result 资源中的 材质UVRect 映射信息为空,需重新合并";
                rectInAtlas             = new Rect();
                encapsulatingRectOut    = new Rect();
                sourceMaterialTilingOut = new Rect();
                return(false);
            }
            if (sourceMat == null)
            {
                rectInAtlas             = new Rect();
                encapsulatingRectOut    = new Rect();
                sourceMaterialTilingOut = new Rect();
                errorMsg = string.Format("网格 {0} 的子网格 {1} 缺少材质,无法获取映射关系", sourceMesh.name, submeshIdx);
                return(false);
            }
            if (submeshIdx >= sourceMesh.subMeshCount)
            {
                errorMsg                = "参数错误 :Submesh index 大于子网格数量";
                rectInAtlas             = new Rect();
                encapsulatingRectOut    = new Rect();
                sourceMaterialTilingOut = new Rect();
                return(false);
            }

            //源材质在 matsAndSrcUVRect 的 ID
            int idx = -1;

            for (int i = 0; i < matsAndSrcUVRect.Length; i++)
            {
                if (sourceMat == matsAndSrcUVRect[i].material)
                {
                    idx = i;
                    break;
                }
            }
            if (idx == -1)
            {
                rectInAtlas             = new Rect();
                encapsulatingRectOut    = new Rect();
                sourceMaterialTilingOut = new Rect();
                errorMsg = string.Format("Material {0} 在 Texture Bake Result 中无法找到", sourceMat.name);
                return(false);
            }

            //不处理网格 UVs
            if (!resultAsset.resultMaterials[idxInResultMats].considerMeshUVs)
            {
                if (numTimesMatAppearsInAtlas[idx] != 1)
                {
                    Debug.LogError("TextureBakeResults 资源错误,FixOutOfBoundsUVs is false and a material appears more than once.");
                }
                MaterialAndUVRect mr = matsAndSrcUVRect[idx];
                rectInAtlas             = mr.atlasRect;
                tilingTreatment         = mr.tilingTreatment;
                encapsulatingRectOut    = mr.GetEncapsulatingRect();
                sourceMaterialTilingOut = mr.GetMaterialTilingRect();
                return(true);
            }
            else
            {
                //todo what if no UVs
                //Find UV rect in source mesh
                //源网格分析,并缓存
                MeshAnalysisResult[] meshAnalysisInfo;
                if (!meshAnalysisCache.TryGetValue(sourceMesh.GetInstanceID(), out meshAnalysisInfo))
                {
                    meshAnalysisInfo = new MeshAnalysisResult[sourceMesh.subMeshCount];
                    for (int j = 0; j < sourceMesh.subMeshCount; j++)
                    {
                        Vector2[] uvss = meshChannelCache.GetUv0Raw(sourceMesh);
                        MeshBakerUtility.hasOutOfBoundsUVs(uvss, sourceMesh, ref meshAnalysisInfo[j], j);
                    }
                    meshAnalysisCache.Add(sourceMesh.GetInstanceID(), meshAnalysisInfo);
                }

                //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
                //这可能是未在纹理烘焙中使用的网格,该网格的UV贴图对于烘焙的rect而言太大
                //找到一条记录,该记录具有能够包含此图集的uvRect
                bool found                = false;
                Rect encapsulatingRect    = new Rect(0, 0, 0, 0);
                Rect sourceMaterialTiling = new Rect(0, 0, 0, 0);

                //Debug.Log(string.Format("尝试在图集中查找能够使用材质{1}保持网格{0}的平铺采样rect的矩形",
                //    m, sourceMat, meshAnalysisInfo[submeshIdx].uvRect.ToString("f5")));

                for (int i = idx; i < matsAndSrcUVRect.Length; i++)
                {
                    MaterialAndUVRect matAndUVrect = matsAndSrcUVRect[i];
                    if (matAndUVrect.material == sourceMat)
                    {
                        if (matAndUVrect.allPropsUseSameTiling)
                        {
                            encapsulatingRect    = matAndUVrect.allPropsUseSameTiling_samplingEncapsulatinRect;
                            sourceMaterialTiling = matAndUVrect.allPropsUseSameTiling_sourceMaterialTiling;
                        }
                        else
                        {
                            encapsulatingRect    = matAndUVrect.propsUseDifferntTiling_srcUVsamplingRect;
                            sourceMaterialTiling = new Rect(0, 0, 1, 1);
                        }

                        if (UVRectUtility.IsMeshAndMaterialRectEnclosedByAtlasRect(
                                matAndUVrect.tilingTreatment,
                                meshAnalysisInfo[submeshIdx].uvRect,
                                sourceMaterialTiling,
                                encapsulatingRect))
                        {
                            Debug.Log("在图集中找到" + "ID 为 " + i + "包含" + sourceMesh + "  的 tiled sampling 的 Rect");
                            idx   = i;
                            found = true;
                            break;
                        }
                    }
                }
                if (found)
                {
                    MaterialAndUVRect mr = matsAndSrcUVRect[idx];
                    rectInAtlas             = mr.atlasRect;
                    tilingTreatment         = mr.tilingTreatment;
                    encapsulatingRectOut    = mr.GetEncapsulatingRect();
                    sourceMaterialTilingOut = mr.GetMaterialTilingRect();
                    return(true);
                }
                else
                {
                    rectInAtlas             = new Rect();
                    encapsulatingRectOut    = new Rect();
                    sourceMaterialTilingOut = 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} for material {1}. " +
                                             "Was this mesh included when atlases were baked?", sourceMesh.name, sourceMat);
                    return(false);
                }
            }
        }
Exemplo n.º 5
0
        public void SuggestTreatment(List <GameObject> objsToMesh, Material[] resultMaterials, List <ShaderTextureProperty> _customShaderPropNames)
        {
            this._customShaderPropNames = _customShaderPropNames;
            StringBuilder sb = new StringBuilder();
            Dictionary <int, MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary <int, MeshAnalysisResult[]>(); //cache results

            for (int i = 0; i < objsToMesh.Count; i++)
            {
                GameObject obj = objsToMesh[i];
                if (obj == null)
                {
                    continue;
                }
                Material[] ms = MeshBakerUtility.GetGOMaterials(objsToMesh[i]);
                if (ms.Length > 1)
                { // and each material is not mapped to its own layer
                    sb.AppendFormat("\nObject {0} uses {1} materials. Possible treatments:\n", objsToMesh[i].name, ms.Length);
                    sb.AppendFormat("  1) Collapse the submeshes together into one submesh in the combined mesh. Each of the original submesh materials will map to a different UV rectangle in the atlas(es) used by the combined material.\n");
                    sb.AppendFormat("  2) Use the multiple materials feature to map submeshes in the source mesh to submeshes in the combined mesh.\n");
                }
                Mesh m = MeshBakerUtility.GetMesh(obj);

                MeshAnalysisResult[] mar;
                if (!meshAnalysisResultsCache.TryGetValue(m.GetInstanceID(), out mar))
                {
                    mar = new MeshAnalysisResult[m.subMeshCount];
                    MeshBakerUtility.doSubmeshesShareVertsOrTris(m, ref mar[0]);
                    for (int j = 0; j < m.subMeshCount; j++)
                    {
                        MeshBakerUtility.hasOutOfBoundsUVs(m, ref mar[j], j);
                        //DRect outOfBoundsUVRect = new DRect(mar[j].uvRect);
                        mar[j].hasOverlappingSubmeshTris  = mar[0].hasOverlappingSubmeshTris;
                        mar[j].hasOverlappingSubmeshVerts = mar[0].hasOverlappingSubmeshVerts;
                    }
                    meshAnalysisResultsCache.Add(m.GetInstanceID(), mar);
                }

                for (int j = 0; j < ms.Length; j++)
                {
                    if (mar[j].hasOutOfBoundsUVs)
                    {
                        DRect r = new DRect(mar[j].uvRect);
                        sb.AppendFormat("\nObject {0} submesh={1} material={2} uses UVs outside the range 0,0 .. 1,1 to create tiling that tiles the box {3},{4} .. {5},{6}. This is a problem because the UVs outside the 0,0 .. 1,1 " +
                                        "rectangle will pick up neighboring textures in the atlas. Possible Treatments:\n", obj, j, ms[j], r.x.ToString("G4"), r.y.ToString("G4"), (r.x + r.width).ToString("G4"), (r.y + r.height).ToString("G4"));
                        sb.AppendFormat("    1) Ignore the problem. The tiling may not affect result significantly.\n");
                        sb.AppendFormat("    2) Use the 'fix out of bounds UVs' feature to bake the tiling and scale the UVs to fit in the 0,0 .. 1,1 rectangle.\n");
                        sb.AppendFormat("    3) Use the Multiple Materials feature to map the material on this submesh to its own submesh in the combined mesh. No other materials should map to this submesh. This will result in only one texture in the atlas(es) and the UVs should tile correctly.\n");
                        sb.AppendFormat("    4) Combine only meshes that use the same (or subset of) the set of materials on this mesh. The original material(s) can be applied to the result\n");
                    }
                }
                if (mar[0].hasOverlappingSubmeshVerts)
                {
                    sb.AppendFormat("\nObject {0} has submeshes that share vertices. This is a problem because each vertex can have only one UV coordinate and may be required to map to different positions in the various atlases that are generated. Possible treatments:\n", objsToMesh[i]);
                    sb.AppendFormat(" 1) Ignore the problem. The vertices may not affect the result.\n");
                    sb.AppendFormat(" 2) Use the Multiple Materials feature to map the submeshs that overlap to their own submeshs in the combined mesh. No other materials should map to this submesh. This will result in only one texture in the atlas(es) and the UVs should tile correctly.\n");
                    sb.AppendFormat(" 3) Combine only meshes that use the same (or subset of) the set of materials on this mesh. The original material(s) can be applied to the result\n");
                }
            }
            Dictionary <Material, List <GameObject> > m2gos = new Dictionary <Material, List <GameObject> >();

            for (int i = 0; i < objsToMesh.Count; i++)
            {
                if (objsToMesh[i] != null)
                {
                    Material[] ms = MeshBakerUtility.GetGOMaterials(objsToMesh[i]);
                    for (int j = 0; j < ms.Length; j++)
                    {
                        if (ms[j] != null)
                        {
                            List <GameObject> lgo;
                            if (!m2gos.TryGetValue(ms[j], out lgo))
                            {
                                lgo = new List <GameObject>();
                                m2gos.Add(ms[j], lgo);
                            }
                            if (!lgo.Contains(objsToMesh[i]))
                            {
                                lgo.Add(objsToMesh[i]);
                            }
                        }
                    }
                }
            }

            for (int i = 0; i < resultMaterials.Length; i++)
            {
                string resultMatShaderName = resultMaterials[i] != null ? "None" : resultMaterials[i].shader.name;
                TexturePipelineData data   = CreatePipelineData(resultMaterials[i], new List <ShaderTextureProperty>(), objsToMesh, new List <Material>(), new List <MaterialPropTexturesSet>());
                TextureCombinerPipeline._CollectPropertyNames(data);
                foreach (Material m in m2gos.Keys)
                {
                    for (int j = 0; j < data.texPropertyNames.Count; j++)
                    {
                        if (m.HasProperty(data.texPropertyNames[j].name))
                        {
                            Texture txx = TextureCombinerPipeline.GetTextureConsideringStandardShaderKeywords(resultMatShaderName, m, data.texPropertyNames[j].name);
                            if (txx != null)
                            {
                                Vector2 o = m.GetTextureOffset(data.texPropertyNames[j].name);
                                Vector3 s = m.GetTextureScale(data.texPropertyNames[j].name);
                                if (o.x < 0f || o.x + s.x > 1f ||
                                    o.y < 0f || o.y + s.y > 1f)
                                {
                                    sb.AppendFormat("\nMaterial {0} used by objects {1} uses texture {2} that is tiled (scale={3} offset={4}). If there is more than one texture in the atlas " +
                                                    " then Mesh Baker will bake the tiling into the atlas. If the baked tiling is large then quality can be lost. Possible treatments:\n", m, PrintList(m2gos[m]), txx, s, o);
                                    sb.AppendFormat("  1) Use the baked tiling.\n");
                                    sb.AppendFormat("  2) Use the Multiple Materials feature to map the material on this object/submesh to its own submesh in the combined mesh. No other materials should map to this submesh. The original material can be applied to this submesh.\n");
                                    sb.AppendFormat("  3) Combine only meshes that use the same (or subset of) the set of textures on this mesh. The original material can be applied to the result.\n");
                                }
                            }
                        }
                    }
                }
            }
            string outstr = "";

            if (sb.Length == 0)
            {
                outstr = "====== No problems detected. These meshes should combine well ====\n  If there are problems with the combined meshes please report the problem to digitalOpus.ca so we can improve Mesh Baker.";
            }
            else
            {
                outstr = "====== There are possible problems with these meshes that may prevent them from combining well. TREATMENT SUGGESTIONS (copy and paste to text editor if too big) =====\n" + sb.ToString();
            }
            Debug.Log(outstr);
        }
        /// <summary>
        /// 第一步:
        ///     写入 TexturePipelineData 的 MaterialPropTexturesSet 列表,和 usedObjsToMesh 列表
        /// 每个TexSet在 Atlas 中都是一个矩形。
        /// 如果 allowedMaterialsFilter (过滤器)为空,那么将收集 allObjsToMesh 上的所有材质,usedObjsToMesh 将与allObjsToMesh相同
        /// 否则,将仅包括allowedMaterialsFilter中的材料,而usedObjsToMesh将是使用这些材料的objs。
        /// </summary>
        internal static IEnumerator __Step1_CollectDistinctMatTexturesAndUsedObjects(ProgressUpdateDelegate progressInfo,
                                                                                     CombineTexturesIntoAtlasesCoroutineResult result,
                                                                                     TexturePipelineData data,
                                                                                     EditorMethodsInterface textureEditorMethods,
                                                                                     List <GameObject> usedObjsToMesh)
        {
            // Collect distinct list of textures to combine from the materials on objsToCombine
            // 收集UsedObjects上不同的材质纹理
            bool outOfBoundsUVs = false;
            Dictionary <int, MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary <int, MeshAnalysisResult[]>(); //cache results

            for (int i = 0; i < data.allObjsToMesh.Count; i++)
            {
                GameObject obj = data.allObjsToMesh[i];
                //报道进度
                if (progressInfo != null)
                {
                    progressInfo("Collecting textures for " + obj, ((float)i) / data.allObjsToMesh.Count / 2f);
                }

                if (obj == null)
                {
                    Debug.LogError("合并游戏物体列表中包含空物体");
                    result.success = false;
                    yield break;
                }

                Mesh sharedMesh = MeshBakerUtility.GetMesh(obj);
                if (sharedMesh == null)
                {
                    Debug.LogError("游戏物体 " + obj.name + " 网格为空");
                    result.success = false;
                    yield break;
                }

                Material[] sharedMaterials = MeshBakerUtility.GetGOMaterials(obj);
                if (sharedMaterials.Length == 0)
                {
                    Debug.LogError("游戏物体 " + obj.name + " 材质为空.");
                    result.success = false;
                    yield break;
                }

                //analyze mesh or grab cached result of previous analysis, stores one result for each submesh
                //处理网格数据
                MeshAnalysisResult[] meshAnalysisResults;//每个游戏物体的主网格子网格数据数组
                if (!meshAnalysisResultsCache.TryGetValue(sharedMesh.GetInstanceID(), out meshAnalysisResults))
                {
                    meshAnalysisResults = new MeshAnalysisResult[sharedMesh.subMeshCount];
                    for (int j = 0; j < sharedMesh.subMeshCount; j++)
                    {
                        MeshBakerUtility.hasOutOfBoundsUVs(sharedMesh, ref meshAnalysisResults[j], j);
                        if (data._normalizeTexelDensity)
                        {
                            meshAnalysisResults[j].submeshArea = GetSubmeshArea(sharedMesh, j);
                        }

                        if (data._fixOutOfBoundsUVs && !meshAnalysisResults[j].hasUVs)
                        {
                            meshAnalysisResults[j].uvRect = new Rect(0, 0, 1, 1);
                            Debug.LogWarning("Mesh for object " + obj + " has no UV channel but 'consider UVs' is enabled." +
                                             " Assuming UVs will be generated filling 0,0,1,1 rectangle.");
                        }
                    }
                    meshAnalysisResultsCache.Add(sharedMesh.GetInstanceID(), meshAnalysisResults);
                }

                if (data._fixOutOfBoundsUVs)
                {
                    Debug.Log("Mesh Analysis for object " + obj +
                              " numSubmesh=" + meshAnalysisResults.Length +
                              " HasOBUV=" + meshAnalysisResults[0].hasOutOfBoundsUVs +
                              " UVrectSubmesh0=" + meshAnalysisResults[0].uvRect);
                }


                //处理材质数据
                for (int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++)
                {
                    ////for each submesh
                    //if (progressInfo != null)
                    //{
                    //    progressInfo(string.Format("Collecting textures for {0} submesh {1}", obj, matIdx),
                    //        ((float)i) / data.allObjsToMesh.Count / 2f);
                    //}
                    Material mat = sharedMaterials[matIdx];

                    // 材质过滤器
                    if (data.allowedMaterialsFilter != null && !data.allowedMaterialsFilter.Contains(mat))
                    {
                        continue;
                    }

                    outOfBoundsUVs = outOfBoundsUVs || meshAnalysisResults[matIdx].hasOutOfBoundsUVs;

                    if (mat.name.Contains("(Instance)"))
                    {
                        Debug.LogError("The sharedMaterial on object " + obj.name + " has been 'Instanced'." +
                                       " This was probably caused by a script accessing the meshRender.material property in the editor. " +
                                       " The material to UV Rectangle mapping will be incorrect. " +
                                       "To fix this recreate the object from its prefab or re-assign its material from the correct asset.");
                        result.success = false;
                        yield break;
                    }

                    if (data._fixOutOfBoundsUVs)
                    {
                        if (!MeshBakerUtility.AreAllSharedMaterialsDistinct(sharedMaterials))
                        {
                            Debug.LogWarning("游戏物体 " + obj.name + " 使用相同的材质在多个子网格. " +
                                             "可能生成奇怪的 resultAtlasesAndRects,尤其是与 _fixOutOfBoundsUVs 为 true 时");
                        }
                    }

                    //材质属性所用到的 Texutre
                    MaterialPropTexture[] mts = new MaterialPropTexture[data.texPropertyNames.Count];
                    for (int propIdx = 0; propIdx < data.texPropertyNames.Count; propIdx++)
                    {
                        Texture tx           = null;
                        Vector2 scale        = Vector2.one;
                        Vector2 offset       = Vector2.zero;
                        float   texelDensity = 0f;
                        if (mat.HasProperty(data.texPropertyNames[propIdx].name))
                        {
                            Texture txx = GetTextureConsideringStandardShaderKeywords(data.resultMaterial.shader.name, mat, data.texPropertyNames[propIdx].name);
                            if (txx != null)
                            {
                                if (txx is Texture2D)
                                {
                                    //TextureFormat 验证
                                    tx = txx;
                                    TextureFormat f           = ((Texture2D)tx).format;
                                    bool          isNormalMap = false;
                                    if (!Application.isPlaying && textureEditorMethods != null)
                                    {
                                        isNormalMap = textureEditorMethods.IsNormalMap((Texture2D)tx);
                                    }
                                    if ((f == TextureFormat.ARGB32 ||
                                         f == TextureFormat.RGBA32 ||
                                         f == TextureFormat.BGRA32 ||
                                         f == TextureFormat.RGB24 ||
                                         f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work
                                    {
                                        //可使用
                                    }
                                    else
                                    {
                                        //TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewe
                                        //尝试使用tex2.SetPixels(tex1.GetPixels())复制纹理,但是3.5中的bug意味着DTX1和5压缩纹理出现扭曲
                                        if (Application.isPlaying && data._packingAlgorithm != PackingAlgorithmEnum.MeshBakerTexturePacker_Fast)
                                        {
                                            Debug.LogWarning("合并列表中,游戏物体 " + obj.name + " 所使用的 Texture " +
                                                             tx.name + " 使用的格式 " + f +
                                                             "不是: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 或 DXT. " +
                                                             "无法在运行时重新设置尺寸" +
                                                             "If format says 'compressed' try changing it to 'truecolor'");
                                            result.success = false;
                                            yield break;
                                        }
                                        else
                                        {
                                            tx = (Texture2D)mat.GetTexture(data.texPropertyNames[propIdx].name);
                                        }
                                    }
                                }
                                else
                                {
                                    Debug.LogError("合并列表中,游戏物体 " + obj.name + " 渲染网格使用的 Texture 不是 Texture2D. ");
                                    result.success = false;
                                    yield break;
                                }
                            }
                            //像素密度
                            if (tx != null && data._normalizeTexelDensity)
                            {
                                //不考虑平铺和UV采样超出范围
                                if (meshAnalysisResults[propIdx].submeshArea == 0)
                                {
                                    texelDensity = 0f;
                                }
                                else
                                {
                                    texelDensity = (tx.width * tx.height) / (meshAnalysisResults[propIdx].submeshArea);
                                }
                            }
                            //规格,偏移
                            GetMaterialScaleAndOffset(mat, data.texPropertyNames[propIdx].name, out offset, out scale);
                        }

                        mts[propIdx] = new MaterialPropTexture(tx, offset, scale, texelDensity);
                    }

                    // 收集材质参数值的平均值
                    data.nonTexturePropertyBlender.CollectAverageValuesOfNonTextureProperties(data.resultMaterial, mat);

                    Vector2 obUVscale  = new Vector2(meshAnalysisResults[matIdx].uvRect.width, meshAnalysisResults[matIdx].uvRect.height);
                    Vector2 obUVoffset = new Vector2(meshAnalysisResults[matIdx].uvRect.x, meshAnalysisResults[matIdx].uvRect.y);

                    //Add to distinct set of textures if not already there
                    TextureTilingTreatment tilingTreatment = TextureTilingTreatment.none;
                    if (data._fixOutOfBoundsUVs)
                    {
                        tilingTreatment = TextureTilingTreatment.considerUVs;
                    }

                    //合并信息 distinctMaterialTextures 数据设置

                    //材质各参数 Texture,及 UV 偏移数据映射
                    MaterialPropTexturesSet setOfTexs = new MaterialPropTexturesSet(mts, obUVoffset, obUVscale, tilingTreatment);  //one of these per submesh
                    //材质及各变化参数Rect 数据
                    MatAndTransformToMerged matt = new MatAndTransformToMerged(new DRect(obUVoffset, obUVscale), data._fixOutOfBoundsUVs, mat);

                    setOfTexs.matsAndGOs.mats.Add(matt);

                    MaterialPropTexturesSet setOfTexs2 = data.distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs, data._fixOutOfBoundsUVs, data.nonTexturePropertyBlender));
                    if (setOfTexs2 != null)
                    {
                        setOfTexs = setOfTexs2;
                    }
                    else
                    {
                        data.distinctMaterialTextures.Add(setOfTexs);
                    }

                    if (!setOfTexs.matsAndGOs.mats.Contains(matt))
                    {
                        setOfTexs.matsAndGOs.mats.Add(matt);
                    }

                    if (!setOfTexs.matsAndGOs.gos.Contains(obj))
                    {
                        setOfTexs.matsAndGOs.gos.Add(obj);
                        //已使用 游戏物体
                        if (!usedObjsToMesh.Contains(obj))
                        {
                            usedObjsToMesh.Add(obj);
                        }
                    }
                }
            }

            Debug.Log(string.Format("第一阶段完成;" +
                                    "参与合并的游戏物体的不同材质,各自包含与shader属性对应的不同的纹理,收集到 {0} 组 textures,即 {0} 个不同的材质," +
                                    "fixOutOfBoundsUV:{1} " +
                                    "considerNonTextureProperties:{2}",
                                    data.distinctMaterialTextures.Count, data._fixOutOfBoundsUVs, data._considerNonTextureProperties));

            if (data.distinctMaterialTextures.Count == 0)
            {
                Debug.LogError("None of the source object materials matched any of the allowed materials for submesh with result material: " + data.resultMaterial);
                result.success = false;
                yield break;
            }

            TextureCombinerMerging merger = new TextureCombinerMerging(data._considerNonTextureProperties,
                                                                       data.nonTexturePropertyBlender, data._fixOutOfBoundsUVs);

            merger.MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects(data.distinctMaterialTextures);

            yield break;
        }
        public static bool hasOutOfBoundsUVs(Vector2[] uvs, Mesh m, ref MeshAnalysisResult putResultHere, int submeshIndex = -1)
        {
            putResultHere.hasUVs = true;
            if (uvs.Length == 0)
            {
                putResultHere.hasUVs            = false;
                putResultHere.hasOutOfBoundsUVs = false;
                putResultHere.uvRect            = new Rect();
                return(putResultHere.hasOutOfBoundsUVs);
            }
            float minx, miny, maxx, maxy;

            if (submeshIndex >= m.subMeshCount)
            {
                putResultHere.hasOutOfBoundsUVs = false;
                putResultHere.uvRect            = new Rect();
                return(putResultHere.hasOutOfBoundsUVs);
            }
            else if (submeshIndex >= 0)
            {
                //checking specific submesh
                int[] tris = m.GetTriangles(submeshIndex);
                if (tris.Length == 0)
                {
                    putResultHere.hasOutOfBoundsUVs = false;
                    putResultHere.uvRect            = new Rect();
                    return(putResultHere.hasOutOfBoundsUVs);
                }
                minx = maxx = uvs[tris[0]].x;
                miny = maxy = uvs[tris[0]].y;
                for (int idx = 0; idx < tris.Length; idx++)
                {
                    int i = tris[idx];
                    if (uvs[i].x < minx)
                    {
                        minx = uvs[i].x;
                    }
                    if (uvs[i].x > maxx)
                    {
                        maxx = uvs[i].x;
                    }
                    if (uvs[i].y < miny)
                    {
                        miny = uvs[i].y;
                    }
                    if (uvs[i].y > maxy)
                    {
                        maxy = uvs[i].y;
                    }
                }
            }
            else
            {
                //checking all UVs
                minx = maxx = uvs[0].x;
                miny = maxy = uvs[0].y;
                for (int i = 0; i < uvs.Length; i++)
                {
                    if (uvs[i].x < minx)
                    {
                        minx = uvs[i].x;
                    }
                    if (uvs[i].x > maxx)
                    {
                        maxx = uvs[i].x;
                    }
                    if (uvs[i].y < miny)
                    {
                        miny = uvs[i].y;
                    }
                    if (uvs[i].y > maxy)
                    {
                        maxy = uvs[i].y;
                    }
                }
            }
            Rect uvBounds = new Rect();

            uvBounds.x      = minx;
            uvBounds.y      = miny;
            uvBounds.width  = maxx - minx;
            uvBounds.height = maxy - miny;
            if (maxx > 1f || minx < 0f || maxy > 1f || miny < 0f)
            {
                putResultHere.hasOutOfBoundsUVs = true;
            }
            else
            {
                putResultHere.hasOutOfBoundsUVs = false;
            }
            putResultHere.uvRect = uvBounds;
            return(putResultHere.hasOutOfBoundsUVs);
        }