Esempio n. 1
0
        IEnumerator _CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods, List <AtlasPackingResult> atlasPackingResult, bool onlyPackRects, bool splitAtlasWhenPackingIfTooBig)
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            try
            {
                _temporaryTextures.Clear();
                MeshBakerMaterialTexture.readyToBuildAtlases = false;

                if (textureEditorMethods != null)
                {
                    textureEditorMethods.Clear();
                    textureEditorMethods.OnPreTextureBake();
                }

                if (objsToMesh == null || objsToMesh.Count == 0)
                {
                    Debug.LogError("No meshes to combine. Please assign some meshes to combine.");
                    result.success = false;
                    yield break;
                }

                if (_atlasPadding < 0)
                {
                    Debug.LogError("Atlas padding must be zero or greater.");
                    result.success = false;
                    yield break;
                }

                if (_maxTilingBakeSize < 2 || _maxTilingBakeSize > 4096)
                {
                    Debug.LogError("Invalid value for max tiling bake size.");
                    result.success = false;
                    yield break;
                }

                for (int i = 0; i < objsToMesh.Count; i++)
                {
                    Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
                    for (int j = 0; j < ms.Length; j++)
                    {
                        Material m = ms[j];
                        if (m == null)
                        {
                            Debug.LogError("Game object " + objsToMesh[i] + " has a null material");
                            result.success = false;
                            yield break;
                        }
                    }
                }

                if (progressInfo != null)
                {
                    progressInfo("Collecting textures for " + objsToMesh.Count + " meshes.", .01f);
                }

                MB3_TextureCombinerPipeline.TexturePipelineData data = LoadPipelineData(resultMaterial, new List <ShaderTextureProperty>(), objsToMesh, allowedMaterialsFilter, new List <MB_TexSet>());
                if (!MB3_TextureCombinerPipeline._CollectPropertyNames(data, LOG_LEVEL))
                {
                    result.success = false;
                    yield break;
                }

                if (_fixOutOfBoundsUVs && (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal ||
                                           _packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical))
                {
                    if (LOG_LEVEL >= MB2_LogLevel.info)
                    {
                        Debug.LogWarning("'Consider Mesh UVs' is enabled but packing algorithm is MeshBakerTexturePacker_Horizontal or MeshBakerTexturePacker_Vertical. It is recommended to use these packers without using 'Consider Mesh UVs'");
                    }
                }

                data.nonTexturePropertyBlender.LoadTextureBlendersIfNeeded(data.resultMaterial);

                if (onlyPackRects)
                {
                    yield return(__RunTexturePackerOnly(result, data, splitAtlasWhenPackingIfTooBig, textureEditorMethods, atlasPackingResult));
                }
                else
                {
                    yield return(__CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, data, splitAtlasWhenPackingIfTooBig, textureEditorMethods));
                }

                /*
                 *      } catch (MissingReferenceException mrex){
                 *              Debug.LogError("Creating atlases failed a MissingReferenceException was thrown. This is normally only happens when trying to create very large atlases and Unity is running out of Memory. Try changing the 'Texture Packer' to a different option, it may work with an alternate packer. This error is sometimes intermittant. Try baking again.");
                 *              Debug.LogError(mrex);
                 *      } catch (Exception ex){
                 *              Debug.LogError(ex);*/
            }
            finally
            {
                _destroyAllTemporaryTextures();
                _restoreProceduralMaterials();
                if (textureEditorMethods != null)
                {
                    textureEditorMethods.RestoreReadFlagsAndFormats(progressInfo);
                    textureEditorMethods.OnPostTextureBake();
                }
                if (LOG_LEVEL >= MB2_LogLevel.debug)
                {
                    Debug.Log("===== Done creating atlases for " + resultMaterial + " Total time to create atlases " + sw.Elapsed.ToString());
                }
            }
            //result.success = success;
        }
Esempio n. 2
0
        public void SuggestTreatment(List <GameObject> objsToMesh, Material[] resultMaterials, List <ShaderTextureProperty> _customShaderPropNames)
        {
            this._customShaderPropNames = _customShaderPropNames;
            StringBuilder sb = new StringBuilder();
            Dictionary <int, MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary <int, MB_Utility.MeshAnalysisResult[]>(); //cache results

            for (int i = 0; i < objsToMesh.Count; i++)
            {
                GameObject obj = objsToMesh[i];
                if (obj == null)
                {
                    continue;
                }
                Material[] ms = MB_Utility.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 = MB_Utility.GetMesh(obj);

                MB_Utility.MeshAnalysisResult[] mar;
                if (!meshAnalysisResultsCache.TryGetValue(m.GetInstanceID(), out mar))
                {
                    mar = new MB_Utility.MeshAnalysisResult[m.subMeshCount];
                    MB_Utility.doSubmeshesShareVertsOrTris(m, ref mar[0]);
                    for (int j = 0; j < m.subMeshCount; j++)
                    {
                        MB_Utility.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 = MB_Utility.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;
                MB3_TextureCombinerPipeline.TexturePipelineData data = LoadPipelineData(resultMaterials[i], new List <ShaderTextureProperty>(), objsToMesh, new List <Material>(), new List <MB_TexSet>());
                MB3_TextureCombinerPipeline._CollectPropertyNames(data, LOG_LEVEL);
                foreach (Material m in m2gos.Keys)
                {
                    for (int j = 0; j < data.texPropertyNames.Count; j++)
                    {
                        if (m.HasProperty(data.texPropertyNames[j].name))
                        {
                            Texture txx = MB3_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);
        }
Esempio n. 3
0
        public static IEnumerator _CreateAtlasesCoroutineSingleResultMaterial(int resMatIdx,
                                                                              MB_TextureArrayResultMaterial bakedMatsAndSlicesResMat,
                                                                              MB_MultiMaterialTexArray resMatConfig,
                                                                              List <GameObject> objsToMesh,
                                                                              MB3_TextureCombiner combiner,
                                                                              MB_TextureArrayFormatSet[] textureArrayOutputFormats,
                                                                              MB_MultiMaterialTexArray[] resultMaterialsTexArray,
                                                                              List <ShaderTextureProperty> customShaderProperties,
                                                                              ProgressUpdateDelegate progressInfo,
                                                                              MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult,
                                                                              bool saveAtlasesAsAssets = false,
                                                                              MB2_EditorMethodsInterface editorMethods = null,
                                                                              float maxTimePerFrame = .01f)
        {
            MB2_LogLevel LOG_LEVEL = combiner.LOG_LEVEL;

            if (LOG_LEVEL >= MB2_LogLevel.debug)
            {
                Debug.Log("Baking atlases for result material " + resMatIdx + " num slices:" + resMatConfig.slices.Count);
            }
            // Each result material can be one set of slices per textureProperty. Each slice can be an atlas.
            // Create atlases for each slice.
            List <MB3_TextureCombiner.TemporaryTexture> generatedTemporaryAtlases = new List <MB3_TextureCombiner.TemporaryTexture>();

            {
                combiner.saveAtlasesAsAssets = false; // Don't want generated atlas slices to be assets
                List <MB_TexArraySlice> slicesConfig = resMatConfig.slices;
                for (int sliceIdx = 0; sliceIdx < slicesConfig.Count; sliceIdx++)
                {
                    Material resMatToPass = null;
                    List <MB_TexArraySliceRendererMatPair> srcMatAndObjPairs = slicesConfig[sliceIdx].sourceMaterials;

                    if (LOG_LEVEL >= MB2_LogLevel.trace)
                    {
                        Debug.Log(" Baking atlases for result material:" + resMatIdx + " slice:" + sliceIdx);
                    }
                    resMatToPass = resMatConfig.combinedMaterial;
                    combiner.fixOutOfBoundsUVs = slicesConfig[sliceIdx].considerMeshUVs;
                    MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult();
                    MB_AtlasesAndRects sliceAtlasesAndRectOutput = bakedMatsAndSlicesResMat.slices[sliceIdx];
                    List <Material>    usedMats = new List <Material>();
                    slicesConfig[sliceIdx].GetAllUsedMaterials(usedMats);

                    yield return(combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, sliceAtlasesAndRectOutput, resMatToPass, slicesConfig[sliceIdx].GetAllUsedRenderers(objsToMesh), usedMats, editorMethods, coroutineResult2, maxTimePerFrame,
                                                                              onlyPackRects: false, splitAtlasWhenPackingIfTooBig: false));

                    coroutineResult.success = coroutineResult2.success;
                    if (!coroutineResult.success)
                    {
                        coroutineResult.isFinished = true;
                        yield break;
                    }

                    // Track which slices are new generated texture instances. Atlases could be original texture assets (one tex per atlas) or temporary texture instances in memory that will need to be destroyed.
                    {
                        for (int texPropIdx = 0; texPropIdx < sliceAtlasesAndRectOutput.atlases.Length; texPropIdx++)
                        {
                            Texture2D atlas = sliceAtlasesAndRectOutput.atlases[texPropIdx];
                            if (atlas != null)
                            {
                                bool atlasWasASourceTexture = false;
                                for (int srcMatIdx = 0; srcMatIdx < srcMatAndObjPairs.Count; srcMatIdx++)
                                {
                                    Material srcMat = srcMatAndObjPairs[srcMatIdx].sourceMaterial;
                                    if (srcMat.HasProperty(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx]) &&
                                        srcMat.GetTexture(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx]) == atlas)
                                    {
                                        atlasWasASourceTexture = true;
                                        break;
                                    }
                                }

                                if (!atlasWasASourceTexture)
                                {
                                    generatedTemporaryAtlases.Add(new MB3_TextureCombiner.TemporaryTexture(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx], atlas));
                                }
                            }
                        }
                    } // end visit slices

                    Debug.Assert(combiner._getNumTemporaryTextures() == 0, "Combiner should have no temporary textures.");
                }

                combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; // Restore original setting.
            }

            // Generated atlas textures are temporary for texture arrays. They exist only in memory. Need to be cleaned up after we create slices.
            for (int i = 0; i < generatedTemporaryAtlases.Count; i++)
            {
                combiner.AddTemporaryTexture(generatedTemporaryAtlases[i]);
            }

            List <ShaderTextureProperty> texPropertyNames = new List <ShaderTextureProperty>();

            MB3_TextureCombinerPipeline._CollectPropertyNames(texPropertyNames, customShaderProperties, resMatConfig.combinedMaterial, LOG_LEVEL);

            // The slices are built from different source-material-lists. Each slice can have different sets of texture properties missing (nulls).
            // Build a master list of texture properties.
            bool[] hasTexForProperty = MB_TextureArrays.DetermineWhichPropertiesHaveTextures(bakedMatsAndSlicesResMat.slices);

            List <Texture2D> temporaryTextureAssets = new List <Texture2D>();

            try
            {
                MB_MultiMaterialTexArray resMaterial = resMatConfig;
                Dictionary <string, MB_TexArrayForProperty> resTexArraysByProperty = new Dictionary <string, MB_TexArrayForProperty>();
                {
                    // Initialize so I don't need to check if properties exist later.
                    for (int propIdx = 0; propIdx < texPropertyNames.Count; propIdx++)
                    {
                        if (hasTexForProperty[propIdx])
                        {
                            resTexArraysByProperty[texPropertyNames[propIdx].name] =
                                new MB_TexArrayForProperty(texPropertyNames[propIdx].name, new MB_TextureArrayReference[textureArrayOutputFormats.Length]);
                        }
                    }
                }


                MB3_TextureCombinerNonTextureProperties textureBlender = null;
                textureBlender = new MB3_TextureCombinerNonTextureProperties(LOG_LEVEL, combiner.considerNonTextureProperties);
                textureBlender.LoadTextureBlendersIfNeeded(resMatConfig.combinedMaterial);
                textureBlender.AdjustNonTextureProperties(resMatConfig.combinedMaterial, texPropertyNames, editorMethods);

                // Vist each TextureFormatSet
                for (int texFormatSetIdx = 0; texFormatSetIdx < textureArrayOutputFormats.Length; texFormatSetIdx++)
                {
                    MB_TextureArrayFormatSet textureArrayFormatSet = textureArrayOutputFormats[texFormatSetIdx];
                    editorMethods.Clear();

                    MB_TextureArrays.TexturePropertyData texPropertyData = new MB_TextureArrays.TexturePropertyData();
                    MB_TextureArrays.FindBestSizeAndMipCountAndFormatForTextureArrays(texPropertyNames, combiner.maxAtlasSize, textureArrayFormatSet, bakedMatsAndSlicesResMat.slices, texPropertyData);

                    // Create textures we might need to create if they don't exist.
                    {
                        for (int propIdx = 0; propIdx < hasTexForProperty.Length; propIdx++)
                        {
                            if (hasTexForProperty[propIdx])
                            {
                                TextureFormat format       = texPropertyData.formats[propIdx];
                                int           numSlices    = bakedMatsAndSlicesResMat.slices.Length;
                                int           targetWidth  = (int)texPropertyData.sizes[propIdx].x;
                                int           targetHeight = (int)texPropertyData.sizes[propIdx].y;
                                for (int sliceIdx = 0; sliceIdx < numSlices; sliceIdx++)
                                {
                                    if (bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx] == null)
                                    {
                                        // Can only setSolidColor on truecolor textures. First create a texture in trucolor format
                                        Texture2D sliceTex = new Texture2D(targetWidth, targetHeight, TextureFormat.ARGB32, texPropertyData.doMips[propIdx]);
                                        Color     col      = textureBlender.GetColorForTemporaryTexture(resMatConfig.slices[sliceIdx].sourceMaterials[0].sourceMaterial, texPropertyNames[propIdx]);
                                        MB_Utility.setSolidColor(sliceTex, col);
                                        // Now create a copy of this texture in target format.
                                        bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx] = editorMethods.CreateTemporaryAssetCopy(texPropertyNames[propIdx], sliceTex, targetWidth, targetHeight, format, LOG_LEVEL);
                                        temporaryTextureAssets.Add(bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx]);
                                        MB_Utility.Destroy(sliceTex);
                                    }
                                }
                            }
                        }
                    }


                    if (LOG_LEVEL >= MB2_LogLevel.debug)
                    {
                        Debug.Log("Converting source textures to readable formats.");
                    }
                    if (MB_TextureArrays.ConvertTexturesToReadableFormat(texPropertyData, bakedMatsAndSlicesResMat.slices, hasTexForProperty, texPropertyNames, combiner, LOG_LEVEL, temporaryTextureAssets, editorMethods))
                    {
                        // We now have a set of slices (one per textureProperty). Build these into Texture2DArray's.
                        if (LOG_LEVEL >= MB2_LogLevel.debug)
                        {
                            Debug.Log("Creating texture arrays");
                        }
                        if (LOG_LEVEL >= MB2_LogLevel.info)
                        {
                            Debug.Log("THERE MAY BE ERRORS IN THE CONSOLE ABOUT 'Rebuilding mipmaps ... not supported'. THESE ARE PROBABLY FALSE POSITIVES AND CAN BE IGNORED.");
                        }
                        Texture2DArray[] textureArrays = MB_TextureArrays.CreateTextureArraysForResultMaterial(texPropertyData, texPropertyNames, bakedMatsAndSlicesResMat.slices, hasTexForProperty, combiner, LOG_LEVEL);


                        // Now have texture arrays for a result material, for all props. Save it.
                        for (int propIdx = 0; propIdx < textureArrays.Length; propIdx++)
                        {
                            if (hasTexForProperty[propIdx])
                            {
                                MB_TextureArrayReference texRef = new MB_TextureArrayReference(textureArrayFormatSet.name, textureArrays[propIdx]);
                                resTexArraysByProperty[texPropertyNames[propIdx].name].formats[texFormatSetIdx] = texRef;
                                if (saveAtlasesAsAssets)
                                {
                                    editorMethods.SaveTextureArrayToAssetDatabase(textureArrays[propIdx],
                                                                                  textureArrayFormatSet.GetFormatForProperty(texPropertyNames[propIdx].name),
                                                                                  bakedMatsAndSlicesResMat.slices[0].texPropertyNames[propIdx],
                                                                                  propIdx, resMaterial.combinedMaterial);
                                }
                            }
                        }
                    }
                } // end vist format set

                resMaterial.textureProperties = new List <MB_TexArrayForProperty>();
                foreach (MB_TexArrayForProperty val in resTexArraysByProperty.Values)
                {
                    resMaterial.textureProperties.Add(val);
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message + "\n" + e.StackTrace.ToString());
                coroutineResult.isFinished = true;
                coroutineResult.success    = false;
            }
            finally
            {
                editorMethods.RestoreReadFlagsAndFormats(progressInfo);
                combiner._destroyAllTemporaryTextures();
                for (int i = 0; i < temporaryTextureAssets.Count; i++)
                {
                    editorMethods.DestroyAsset(temporaryTextureAssets[i]);
                }
                temporaryTextureAssets.Clear();
            }
        }