//Fills distinctMaterialTextures and usedObjsToMesh
		//If allowedMaterialsFilter is empty then all materials on allObjsToMesh will be collected and usedObjsToMesh will be same as allObjsToMesh
		//else only materials in allowedMaterialsFilter will be included and usedObjsToMesh will be objs that use those materials.
		bool __Step1_CollectDistinctMatTexturesAndUsedObjects(List<GameObject> allObjsToMesh, 
															 List<Material> allowedMaterialsFilter, 
															 List<string> texPropertyNames, 
															 MB2_EditorMethodsInterface textureEditorMethods, 
															 List<MB_TexSet> distinctMaterialTextures, //Will be populated
															 List<GameObject> usedObjsToMesh) //Will be populated, is a subset of allObjsToMesh
		{
			// Collect distinct list of textures to combine from the materials on objsToCombine
			bool outOfBoundsUVs = false;
			for (int i = 0; i < allObjsToMesh.Count; i++){
				GameObject obj = allObjsToMesh[i];
				if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Collecting textures for object " + obj);
				
				if (obj == null){
					Debug.LogError("The list of objects to mesh contained nulls.");
					return false;
				}
				
				Mesh sharedMesh = MB_Utility.GetMesh(obj);
				if (sharedMesh == null){
					Debug.LogError("Object " + obj.name + " in the list of objects to mesh has no mesh.");				
					return false;
				}
	
				Material[] sharedMaterials = MB_Utility.GetGOMaterials(obj);
				if (sharedMaterials == null){
					Debug.LogError("Object " + obj.name + " in the list of objects has no materials.");
					return false;				
				}
				
				for(int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++){
					Material mat = sharedMaterials[matIdx];
					
					//check if this material is in the list of source materaials
					if (allowedMaterialsFilter != null && !allowedMaterialsFilter.Contains(mat)){
						continue;
					}
					
					Rect uvBounds = new Rect();
					bool mcOutOfBoundsUVs = MB_Utility.hasOutOfBoundsUVs(sharedMesh,ref uvBounds,matIdx);
					outOfBoundsUVs = outOfBoundsUVs || mcOutOfBoundsUVs;					
					
					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.");	
						return false;
					}
					
					if (fixOutOfBoundsUVs){
						if (!MB_Utility.validateOBuvsMultiMaterial(sharedMaterials)){
							if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Object " + obj.name + " uses the same material on multiple submeshes. This may generate strange resultAtlasesAndRects especially when used with fix out of bounds uvs. Try duplicating the material.");		
						}
					}
										
					//collect textures scale and offset for each texture in objects material
					MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[texPropertyNames.Count];
					for (int j = 0; j < texPropertyNames.Count; j++){
						Texture2D tx = null;
						Vector2 scale = Vector2.one;
						Vector2 offset = Vector2.zero;
						Vector2 obUVscale = Vector2.one;
						Vector2 obUVoffset = Vector2.zero; 
						if (mat.HasProperty(texPropertyNames[j])){
							Texture txx = mat.GetTexture(texPropertyNames[j]);
							if (txx != null){
								if (txx is Texture2D){
									tx = (Texture2D) txx;
									TextureFormat f = tx.format;
									bool isNormalMap = false;
									if (!Application.isPlaying && textureEditorMethods != null) isNormalMap = textureEditorMethods.IsNormalMap(tx);
									if ((f == TextureFormat.ARGB32 ||
										f == TextureFormat.RGBA32 ||
										f == TextureFormat.BGRA32 ||
										f == TextureFormat.RGB24  ||
										f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work
									{
										//good
									} else {
										//TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewed
										//MB2_Log.Log(MB2_LogLevel.warn,obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These formats cannot be resized. MeshBaker will create duplicates.");
										//tx = createTextureCopy(tx);
										if (!Application.isPlaying){
											//Debug.LogWarning("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'");													
											if (textureEditorMethods != null) textureEditorMethods.AddTextureFormat(tx, isNormalMap);
											tx = (Texture2D) mat.GetTexture(texPropertyNames[j]);
										} else {
											Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'" );																						
											return false;
										}
									}
								} else {
									Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases.");				
									return false;
								}
							}
							offset = mat.GetTextureOffset(texPropertyNames[j]);
							scale = mat.GetTextureScale(texPropertyNames[j]);
						}
						if (tx == null){
							if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("No texture selected for " + texPropertyNames[j] + " in object " + allObjsToMesh[i].name + ". A 2x2 clear texture will be generated and used in the atlas.");
						}
						if (mcOutOfBoundsUVs){
							obUVscale = new Vector2(uvBounds.width,uvBounds.height);
							obUVoffset = new Vector2(uvBounds.x,uvBounds.y);
						}
						mts[j] = new MeshBakerMaterialTexture(tx,offset,scale,obUVoffset,obUVscale);
					}
				
					//Add to distinct set of textures if not already there
					MB_TexSet setOfTexs = new MB_TexSet(mts);
					MB_TexSet setOfTexs2 = distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs,fixOutOfBoundsUVs));
					if (setOfTexs2 != null){
						setOfTexs = setOfTexs2;
					} else {
						distinctMaterialTextures.Add(setOfTexs);	
					}
					if (!setOfTexs.mats.Contains(mat)){
						setOfTexs.mats.Add(mat);
					}
					if (!setOfTexs.gos.Contains(obj)){
						setOfTexs.gos.Add(obj);
						if (!usedObjsToMesh.Contains(obj)) usedObjsToMesh.Add(obj);
					}
				}
			}
			
			return true;
		}
Example #2
0
    bool __combineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects results, List <string> texPropertyNames, List <GameObject> objsToMesh, List <Material> sourceMaterials, int atlasPadding, bool resizePowerOfTwoTextures, bool fixOutOfBoundsUVs, int maxTilingBakeSize)
    {
        int numTextures                 = texPropertyNames.Count;
        List <GameObject> mcs           = objsToMesh;
        bool             outOfBoundsUVs = false;
        List <MB_TexSet> texsAndObjs    = new List <MB_TexSet>();    //one per distinct set of textures

        if (VERBOSE)
        {
            Debug.Log("__combineTexturesIntoAtlases atlases:" + texPropertyNames.Count + " objsToMesh:" + objsToMesh.Count + " fixOutOfBoundsUVs:" + fixOutOfBoundsUVs);
        }
        for (int i = 0; i < mcs.Count; i++)
        {
            GameObject mc = mcs[i];
            if (VERBOSE)
            {
                Debug.Log("Collecting textures for object " + mc);
            }

            if (mc == null)
            {
                Debug.LogError("The list of objects to mesh contained nulls.");
                return(false);
            }

            Mesh sharedMesh = MB_Utility.GetMesh(mc);
            if (sharedMesh == null)
            {
                Debug.LogError("Object " + mc.name + " in the list of objects to mesh has no mesh.");
                return(false);
            }

            Material[] sharedMaterials = MB_Utility.GetGOMaterials(mc);
            if (sharedMaterials == null)
            {
                Debug.LogError("Object " + mc.name + " in the list of objects has no materials.");
                return(false);
            }

            for (int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++)
            {
                Material mat = sharedMaterials[matIdx];

                //check if this material is on the list of source materaials
                if (sourceMaterials != null && !sourceMaterials.Contains(mat))
                {
                    continue;
                }

                Rect uvBounds         = new Rect();
                bool mcOutOfBoundsUVs = MB_Utility.hasOutOfBoundsUVs(sharedMesh, ref uvBounds, matIdx);
                outOfBoundsUVs = outOfBoundsUVs || mcOutOfBoundsUVs;

                if (mat.name.Contains("(Instance)"))
                {
                    Debug.LogError("The sharedMaterial on object " + mc.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.");
                    return(false);
                }

                if (fixOutOfBoundsUVs)
                {
                    if (!MB_Utility.validateOBuvsMultiMaterial(sharedMaterials))
                    {
                        Debug.LogWarning("Object " + mc.name + " uses the same material on multiple submeshes. This may generate strange results especially when used with fix out of bounds uvs. Try duplicating the material.");
                    }
                }

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

                //collect textures scale and offset for each texture in objects material
                MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[texPropertyNames.Count];
                for (int j = 0; j < texPropertyNames.Count; j++)
                {
                    Texture2D tx         = null;
                    Vector2   scale      = Vector2.one;
                    Vector2   offset     = Vector2.zero;
                    Vector2   obUVscale  = Vector2.one;
                    Vector2   obUVoffset = Vector2.zero;
                    if (mat.HasProperty(texPropertyNames[j]))
                    {
                        Texture txx = mat.GetTexture(texPropertyNames[j]);
                        if (txx != null)
                        {
                            if (txx is Texture2D)
                            {
                                tx = (Texture2D)txx;
                                TextureFormat f = tx.format;
                                if (f == TextureFormat.ARGB32 ||
                                    f == TextureFormat.RGBA32 ||
                                    f == TextureFormat.BGRA32 ||
                                    f == TextureFormat.RGB24 ||
                                    f == TextureFormat.Alpha8
                                    )                             //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 skewed
                                    //Debug.LogWarning(mc.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These formats cannot be resized. MeshBaker will create duplicates.");
                                    //tx = createTextureCopy(tx);
#if UNITY_EDITOR
                                    Debug.LogWarning("Object " + mc.name + " in the list of objects to mesh uses Texture " + tx.name + " uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'");
                                    setTextureFormat(tx, TextureImporterFormat.ARGB32, true);
                                    tx = (Texture2D)mat.GetTexture(texPropertyNames[j]);
#else
                                    Debug.LogError("Object " + mc.name + " in the list of objects to mesh uses Texture " + tx.name + " uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'");
                                    return(false);
#endif
                                }
                            }
                            else
                            {
                                Debug.LogError("Object " + mc.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases.");
                                return(false);
                            }
                        }
                        offset = mat.GetTextureOffset(texPropertyNames[j]);
                        scale  = mat.GetTextureScale(texPropertyNames[j]);
                    }
                    if (tx == null)
                    {
                        Debug.LogWarning("No texture selected for " + texPropertyNames[j] + " in object " + mcs[i].name);
                    }
                    if (fixOutOfBoundsUVs && mcOutOfBoundsUVs)
                    {
                        obUVscale  = new Vector2(uvBounds.width, uvBounds.height);
                        obUVoffset = new Vector2(uvBounds.x, uvBounds.y);
                    }
                    mts[j] = new MeshBakerMaterialTexture(tx, offset, scale, obUVoffset, obUVscale);
                }

                //Add to distinct set of textures
                MB_TexSet setOfTexs  = new MB_TexSet(mts);
                MB_TexSet setOfTexs2 = texsAndObjs.Find(x => x.Equals(setOfTexs));
                if (setOfTexs2 != null)
                {
                    setOfTexs = setOfTexs2;
                }
                else
                {
                    texsAndObjs.Add(setOfTexs);
                }
                if (!setOfTexs.mats.Contains(mat))
                {
                    setOfTexs.mats.Add(mat);
                }
            }
        }

//		if (texsAndObjs.Count > 1 && outOfBoundsUVs){
//			Debug.LogWarning("An object in the list of objects to be combined has UVs outside the range (0,1). This object can only be combined with objects like itself that use the exact same set of textures.");
//		}

        int _padding = atlasPadding;
        if (texsAndObjs.Count == 1)
        {
            Debug.Log("All objects use the same textures.");
            _padding = 0;
        }

        for (int i = 0; i < texsAndObjs.Count; i++)
        {
            int       tWidth  = 1;
            int       tHeight = 1;
            MB_TexSet txs     = texsAndObjs[i];
            //get the best size all textures in a TexSet must be the same size.
            for (int j = 0; j < txs.ts.Length; j++)
            {
                MeshBakerMaterialTexture matTex = txs.ts[j];
                if (matTex.t == null)
                {
                    Debug.LogWarning("Creating empty texture for " + texPropertyNames[j]);
                    matTex.t = _createTemporaryTexture(tWidth, tHeight, TextureFormat.ARGB32, true);
                }
                if (!matTex.scale.Equals(Vector2.one))
                {
                    Debug.LogWarning("Texture " + matTex.t + "is tiled by " + matTex.scale + " tiling will be baked into a texture with maxSize:" + maxTilingBakeSize);
                }
                if (!matTex.obUVscale.Equals(Vector2.one))
                {
                    Debug.LogWarning("Texture " + matTex.t + "has out of bounds UVs that effectively tile by " + matTex.obUVscale + " tiling will be baked into a texture with maxSize:" + maxTilingBakeSize);
                }

                if (progressInfo != null)
                {
                    progressInfo("Adjusting for scale and offset " + matTex.t, .01f);
                }
                setReadWriteFlag(matTex.t, true, true);
                matTex.t = getAdjustedForScaleAndOffset2(matTex, fixOutOfBoundsUVs, maxTilingBakeSize);

                if (matTex.t.width * matTex.t.height > tWidth * tHeight)
                {
                    tWidth  = matTex.t.width;
                    tHeight = matTex.t.height;
                }
            }
            if (resizePowerOfTwoTextures)
            {
                if (IsPowerOfTwo(tWidth))
                {
                    tWidth -= _padding * 2;
                }
                if (IsPowerOfTwo(tHeight))
                {
                    tHeight -= _padding * 2;
                }
                if (tWidth < 1)
                {
                    tWidth = 1;
                }
                if (tHeight < 1)
                {
                    tHeight = 1;
                }
            }
            txs.idealWidth  = tWidth;
            txs.idealHeight = tHeight;
        }

        Rect[]      uvRects = null;
        Texture2D[] atlases = new Texture2D[numTextures];
        Rect[]      rs      = null;
        long        estArea = 0;

        StringBuilder report = new StringBuilder();
        if (numTextures > 0)
        {
            report = new StringBuilder();
            report.AppendLine("Report");
            for (int i = 0; i < texsAndObjs.Count; i++)
            {
                MB_TexSet txs = texsAndObjs[i];
                report.AppendLine("----------");
                report.Append("Will be resized to:" + txs.idealWidth + "x" + txs.idealHeight);
                for (int j = 0; j < txs.ts.Length; j++)
                {
                    if (txs.ts[j].t != null)
                    {
                        report.Append(" [" + texPropertyNames[j] + " " + txs.ts[j].t.name + " " + txs.ts[j].t.width + "x" + txs.ts[j].t.height + "]");
                    }
                    else
                    {
                        report.Append(" [" + texPropertyNames[j] + " null]");
                    }
                }
                report.AppendLine("");
                report.Append("Materials using:");
                for (int j = 0; j < txs.mats.Count; j++)
                {
                    report.Append(txs.mats[j].name + ", ");
                }
                report.AppendLine("");
            }
        }

        if (progressInfo != null)
        {
            progressInfo("Creating txture atlases.", .1f);
        }

        //Resize and create null textures
        for (int i = 0; i < numTextures; i++)
        {
            for (int j = 0; j < texsAndObjs.Count; j++)
            {
                MB_TexSet txs = texsAndObjs[j];

                int tWidth  = txs.idealWidth;
                int tHeight = txs.idealHeight;

                Texture2D tx = txs.ts[i].t;
                //create a resized copy if necessary
                if (tx.width != tWidth || tx.height != tHeight)
                {
                    if (progressInfo != null)
                    {
                        progressInfo("Resizing texture '" + tx + "'", .01f);
                    }
                    if (VERBOSE)
                    {
                        Debug.Log("Copying and resizing texture " + texPropertyNames[i] + " from " + tx.width + "x" + tx.height + " to " + tWidth + "x" + tHeight);
                    }
                    setReadWriteFlag((Texture2D)tx, true, true);
                    tx = _resizeTexture((Texture2D)tx, tWidth, tHeight);
                }
                txs.ts[i].t = tx;
            }

            Texture2D[] texToPack = new Texture2D[texsAndObjs.Count];
            for (int j = 0; j < texsAndObjs.Count; j++)
            {
                Texture2D tx = texsAndObjs[j].ts[i].t;
                estArea     += tx.width * tx.height;
                texToPack[j] = tx;
            }
#if UNITY_EDITOR
            if (Math.Sqrt(estArea) > 1000f)
            {
                if (EditorUserBuildSettings.selectedBuildTargetGroup != BuildTargetGroup.Standalone)
                {
                    Debug.LogWarning("If the current selected build target is not standalone then the generated atlases may be capped at size 1024. If build target is Standalone then atlases of 4096 can be built");
                }
            }
#endif
            if (Math.Sqrt(estArea) > 3500f)
            {
                Debug.LogWarning("The maximum possible atlas size is 4096. Textures may be shrunk");
            }
            atlases[i] = new Texture2D(1, 1, TextureFormat.ARGB32, true);
            if (progressInfo != null)
            {
                progressInfo("Packing texture atlas " + texPropertyNames[i], .25f);
            }
            if (i == 0)
            {
                if (progressInfo != null)
                {
                    progressInfo("Estimated min size of atlases: " + Math.Sqrt(estArea), .1f);
                }
                Debug.Log("Estimated texture minimum size:" + Math.Sqrt(estArea));
                uvRects = rs = atlases[i].PackTextures(texToPack, _padding, 4096, false);
                Debug.Log("After pack textures size " + atlases[i].width + " " + atlases[i].height);
                atlases[i].Apply();
            }
            else
            {
                atlases[i] = _copyTexturesIntoAtlas(texToPack, _padding, rs, atlases[0].width, atlases[0].height);
            }
            //_destroyTemporaryTexturesAndSetReadFlags();
        }



        Dictionary <Material, Rect> mat2rect_map = new Dictionary <Material, Rect>();
        for (int i = 0; i < texsAndObjs.Count; i++)
        {
            List <Material> mats = texsAndObjs[i].mats;
            for (int j = 0; j < mats.Count; j++)
            {
                if (!mat2rect_map.ContainsKey(mats[j]))
                {
                    mat2rect_map.Add(mats[j], uvRects[i]);
                }
            }
        }

        results.atlases          = atlases;                            // one per texture on source shader
        results.texPropertyNames = texPropertyNames.ToArray();         // one per texture on source shader
        results.mat2rect_map     = mat2rect_map;
        _destroyTemporaryTexturesAndSetReadFlags();
        if (report != null)
        {
            Debug.Log(report.ToString());
        }
        return(true);
    }
    private void CopyScaledAndTiledToAtlas(MB_TexSet texSet, MeshBakerMaterialTexture source, Vector2 obUVoffset, Vector2 obUVscale, Rect rec, ShaderTextureProperty texturePropertyName, MB3_TextureCombinerNonTextureProperties resultMatTexBlender, bool yIsFlipped)
    {
        Rect r = rec;

        myCamera.backgroundColor = resultMatTexBlender.GetColorForTemporaryTexture(texSet.matsAndGOs.mats[0].mat, texturePropertyName);
        //yIsFlipped = true;
        //if (yIsFlipped)
        //{
        //}
        r.y       = 1f - (r.y + r.height); // DrawTexture uses topLeft 0,0, Texture2D uses bottomLeft 0,0
        r.x      *= _destinationTexture.width;
        r.y      *= _destinationTexture.height;
        r.width  *= _destinationTexture.width;
        r.height *= _destinationTexture.height;

        Rect rPadded = r;

        rPadded.x      -= _padding;
        rPadded.y      -= _padding;
        rPadded.width  += _padding * 2;
        rPadded.height += _padding * 2;

        Rect targPr   = new Rect();
        Rect srcPrTex = texSet.ts[indexOfTexSetToRender].GetEncapsulatingSamplingRect().GetRect();

        if (!_fixOutOfBoundsUVs)
        {
            Debug.Assert(source.matTilingRect.GetRect() == texSet.ts[indexOfTexSetToRender].GetEncapsulatingSamplingRect().GetRect());
        }
        Texture2D tex = source.GetTexture2D();

        /*
         * if (_considerNonTextureProperties && resultMatTexBlender != null)
         * {
         *  if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(string.Format("Blending texture {0} mat {1} with non-texture properties using TextureBlender {2}", tex.name, texSet.mats[0].mat, resultMatTexBlender));
         *
         *  resultMatTexBlender.OnBeforeTintTexture(texSet.mats[0].mat, texturePropertyName.name);
         *  //combine the tintColor with the texture
         *  tex = combiner._createTextureCopy(tex);
         *  for (int i = 0; i < tex.height; i++)
         *  {
         *      Color[] cs = tex.GetPixels(0, i, tex.width, 1);
         *      for (int j = 0; j < cs.Length; j++)
         *      {
         *          cs[j] = resultMatTexBlender.OnBlendTexturePixel(texturePropertyName.name, cs[j]);
         *      }
         *      tex.SetPixels(0, i, tex.width, 1, cs);
         *  }
         *  tex.Apply();
         * }
         */


        //main texture
        TextureWrapMode oldTexWrapMode = tex.wrapMode;

        if (srcPrTex.width == 1f && srcPrTex.height == 1f && srcPrTex.x == 0f && srcPrTex.y == 0f)
        {
            //fixes bug where there is a dark line at the edge of the texture
            tex.wrapMode = TextureWrapMode.Clamp;
        }
        else
        {
            tex.wrapMode = TextureWrapMode.Repeat;
        }
        if (LOG_LEVEL >= MB2_LogLevel.trace)
        {
            Debug.Log("DrawTexture tex=" + tex.name + " destRect=" + r + " srcRect=" + srcPrTex + " Mat=" + mat);
        }
        //fill the padding first
        Rect srcPr = new Rect();

        //top margin
        srcPr.x       = srcPrTex.x;
        srcPr.y       = srcPrTex.y + 1 - 1f / tex.height;
        srcPr.width   = srcPrTex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = r.x;
        targPr.y      = rPadded.y;
        targPr.width  = r.width;
        targPr.height = _padding;
        RenderTexture oldRT = RenderTexture.active;

        RenderTexture.active = _destinationTexture;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //bot margin
        srcPr.x       = srcPrTex.x;
        srcPr.y       = srcPrTex.y;
        srcPr.width   = srcPrTex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = r.x;
        targPr.y      = r.y + r.height;
        targPr.width  = r.width;
        targPr.height = _padding;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);


        //left margin
        srcPr.x       = srcPrTex.x;
        srcPr.y       = srcPrTex.y;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = srcPrTex.height;
        targPr.x      = rPadded.x;
        targPr.y      = r.y;
        targPr.width  = _padding;
        targPr.height = r.height;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //right margin
        srcPr.x       = srcPrTex.x + 1f - 1f / tex.width;
        srcPr.y       = srcPrTex.y;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = srcPrTex.height;
        targPr.x      = r.x + r.width;
        targPr.y      = r.y;
        targPr.width  = _padding;
        targPr.height = r.height;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);


        //top left corner
        srcPr.x       = srcPrTex.x;
        srcPr.y       = srcPrTex.y + 1 - 1f / tex.height;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = rPadded.x;
        targPr.y      = rPadded.y;
        targPr.width  = _padding;
        targPr.height = _padding;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //top right corner
        srcPr.x       = srcPrTex.x + 1f - 1f / tex.width;
        srcPr.y       = srcPrTex.y + 1 - 1f / tex.height;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = r.x + r.width;
        targPr.y      = rPadded.y;
        targPr.width  = _padding;
        targPr.height = _padding;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //bot left corner
        srcPr.x       = srcPrTex.x;
        srcPr.y       = srcPrTex.y;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = rPadded.x;
        targPr.y      = r.y + r.height;
        targPr.width  = _padding;
        targPr.height = _padding;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //bot right corner
        srcPr.x       = srcPrTex.x + 1f - 1f / tex.width;
        srcPr.y       = srcPrTex.y;
        srcPr.width   = 1f / tex.width;
        srcPr.height  = 1f / tex.height;
        targPr.x      = r.x + r.width;
        targPr.y      = r.y + r.height;
        targPr.width  = _padding;
        targPr.height = _padding;
        Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);

        //now the texture
        Graphics.DrawTexture(r, tex, srcPrTex, 0, 0, 0, 0, mat);
        RenderTexture.active = oldRT;
        tex.wrapMode         = oldTexWrapMode;
    }
		//Fills distinctMaterialTextures and usedObjsToMesh
		//If allowedMaterialsFilter is empty then all materials on allObjsToMesh will be collected and usedObjsToMesh will be same as allObjsToMesh
		//else only materials in allowedMaterialsFilter will be included and usedObjsToMesh will be objs that use those materials.
		bool __Step1_CollectDistinctMatTexturesAndUsedObjects(List<GameObject> allObjsToMesh, 
															 List<Material> allowedMaterialsFilter, 
															 List<ShaderTextureProperty> texPropertyNames, 
															 MB2_EditorMethodsInterface textureEditorMethods, 
															 List<MB_TexSet> distinctMaterialTextures, //Will be populated
															 List<GameObject> usedObjsToMesh) //Will be populated, is a subset of allObjsToMesh
		{
			System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
			sw.Start();
			// Collect distinct list of textures to combine from the materials on objsToCombine
			bool outOfBoundsUVs = false;
			Dictionary<int,MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary<int, MB_Utility.MeshAnalysisResult[]>(); //cache results
			for (int i = 0; i < allObjsToMesh.Count; i++){
				GameObject obj = allObjsToMesh[i];
				if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Collecting textures for object " + obj);
				
				if (obj == null){
					Debug.LogError("The list of objects to mesh contained nulls.");
					return false;
				}
				
				Mesh sharedMesh = MB_Utility.GetMesh(obj);
				if (sharedMesh == null){
					Debug.LogError("Object " + obj.name + " in the list of objects to mesh has no mesh.");				
					return false;
				}
	
				Material[] sharedMaterials = MB_Utility.GetGOMaterials(obj);
				if (sharedMaterials == null){
					Debug.LogError("Object " + obj.name + " in the list of objects has no materials.");
					return false;				
				}

				//analyze mesh or grab cached result of previous analysis
				MB_Utility.MeshAnalysisResult[] mar;
				if (!meshAnalysisResultsCache.TryGetValue(sharedMesh.GetInstanceID(),out mar)){
					mar = new MB_Utility.MeshAnalysisResult[sharedMesh.subMeshCount];
					for (int j = 0; j < sharedMesh.subMeshCount; j++){
						Rect outOfBoundsUVRect = new Rect();
						MB_Utility.hasOutOfBoundsUVs(sharedMesh,ref outOfBoundsUVRect,ref mar[j], j);
					}
					meshAnalysisResultsCache.Add(sharedMesh.GetInstanceID(),mar);
				}

				for(int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++){
					Material mat = sharedMaterials[matIdx];
					
					//check if this material is in the list of source materaials
					if (allowedMaterialsFilter != null && !allowedMaterialsFilter.Contains(mat)){
						continue;
					}
					
					//Rect uvBounds = mar[matIdx].uvRect;
					outOfBoundsUVs = outOfBoundsUVs || mar[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.");	
						return false;
					}
					
					if (_fixOutOfBoundsUVs){
						if (!MB_Utility.AreAllSharedMaterialsDistinct(sharedMaterials)){
							if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Object " + obj.name + " uses the same material on multiple submeshes. This may generate strange resultAtlasesAndRects especially when used with fix out of bounds uvs. Try duplicating the material.");		
						}
					}
										
					//collect textures scale and offset for each texture in objects material
					MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[texPropertyNames.Count];
					for (int j = 0; j < texPropertyNames.Count; j++){
						Texture2D tx = null;
						Vector2 scale = Vector2.one;
						Vector2 offset = Vector2.zero;
						Vector2 obUVscale = Vector2.one;
						Vector2 obUVoffset = Vector2.zero;
						Color colorIfNoTexture = Color.clear;
						Color tintColor = GetColorIfNoTexture(mat,texPropertyNames[j]);
						if (mat.HasProperty(texPropertyNames[j].name)){
							Texture txx = mat.GetTexture(texPropertyNames[j].name);
							if (txx != null){
								if (txx is Texture2D){
									tx = (Texture2D) txx;
									TextureFormat f = tx.format;
									bool isNormalMap = false;
									if (!Application.isPlaying && textureEditorMethods != null) isNormalMap = textureEditorMethods.IsNormalMap(tx);
									if ((f == TextureFormat.ARGB32 ||
										f == TextureFormat.RGBA32 ||
										f == TextureFormat.BGRA32 ||
										f == TextureFormat.RGB24  ||
										f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work
									{
										//good
									} else {
										//TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewed
										//MB2_Log.Log(MB2_LogLevel.warn,obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These formats cannot be resized. MeshBaker will create duplicates.");
										//tx = createTextureCopy(tx);
										if (Application.isPlaying && _packingAlgorithm != MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast) {
											Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'" );																						
											return false;
										} else {
                                            //only want to do this if we are saving the atlases to project
											if (textureEditorMethods != null &&
                                                (_packingAlgorithm != MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast ||
                                                isNormalMap)){
												textureEditorMethods.AddTextureFormat(tx, isNormalMap);
											}
											tx = (Texture2D) mat.GetTexture(texPropertyNames[j].name);
										}
									}
								} else {
									Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases.");				
									return false;
								}
							} else { 
								//has texture property but no texture try to set a resonable color from the other texture properties
								colorIfNoTexture = tintColor;
							}
							offset = mat.GetTextureOffset(texPropertyNames[j].name);
							scale = mat.GetTextureScale(texPropertyNames[j].name);
						} else {
                            colorIfNoTexture = tintColor;
                        }
						if (mar[matIdx].hasOutOfBoundsUVs){
							obUVscale = new Vector2(mar[matIdx].uvRect.width,mar[matIdx].uvRect.height);
							obUVoffset = new Vector2(mar[matIdx].uvRect.x,mar[matIdx].uvRect.y);
						}
						mts[j] = new MeshBakerMaterialTexture(tx,offset,scale,obUVoffset,obUVscale,colorIfNoTexture,tintColor);
					}
				
					//Add to distinct set of textures if not already there
					MB_TexSet setOfTexs = new MB_TexSet(mts);
					MB_TexSet setOfTexs2 = distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs,_fixOutOfBoundsUVs));
					if (setOfTexs2 != null){
						setOfTexs = setOfTexs2;
					} else {
						distinctMaterialTextures.Add(setOfTexs);	
					}
					if (!setOfTexs.mats.Contains(mat)){
						setOfTexs.mats.Add(mat);
					}
					if (!setOfTexs.gos.Contains(obj)){
						setOfTexs.gos.Add(obj);
						if (!usedObjsToMesh.Contains(obj)) usedObjsToMesh.Add(obj);
					}
				}
			}
			if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Total time Step1_CollectDistinctTextures " + (sw.ElapsedMilliseconds).ToString("f5"));
			return true;
		}