工具向TANotes

🔍TOOL丨批处理1.0:Maskmap&FixSettings&Resize👌

by ERIN.Z, 2022-04-28


前两天写了一个手动拖入通道来生成maskmap的工具MapCreator 在这个的基础上又研究了一下,实现了对于文件夹目录的一键maskmap生成!还做了一键修正贴图导入设置的功能。 TextureTool 留个小尾巴,等有空了可以再写一个自动生成对应的hdrp材质并贴图。不过不清楚第三方资源库的材质和模型的关系..可能还需要从prefab中extract....算了!再说! 更新下载地址👉TextureTool👈

资产相关类

有关Unity内的资源文件,有这样几个比较关键的类:

AssetDatabase

UnityEditor : An Interface for accessing assets and performing operations on assets.

AssetDatabase是对资产操作的主要接口,可以复制、删除、查询创建文件或文件夹。 e.g.

  • 获取资产路径:
    string path = AssetDatabase.GetAssetPath(metallic) ;
  • 查找资产
    var texture2Ds = AssetDatabase.FindAssets ("t:Texture2D", paths);

    第一个字符串是filter,可以使用name/label("l:")/type("t:")检索。其返回值是资产string形式的GUID,需要使用AssetDatabase.GUIDToAssetPath获得其路径,然后使用AssetDatabase.LoadAssetAtPath加载资产。

  • 刷新资源列表
    AssetDatabase.Refresh();

    上一篇中,创建材质的方式是直接在对应路径写入文件,所以需要刷新并导入材质,让引擎识别新加入的文件。

AssetImporter

UnityEditor :Base class from which asset importers for specific asset types derive.

AssetImporter感觉是记录引擎识别资产的方式,可能与.meta文件相关? e.g.

  • 利用userData防止重复编辑
    var im = AssetImporter.GetAtPath (path);
    if (im.userData.Contains ("R2;"))
        continue;
    im.userData += "R2;";
  • 转成对应类型的Importer(如TextureImporter),可对其进行详细设置的修改。 对于材质而言,常用的可以设置Texturetype(转normal),Tetureshapr(转cube),sRGBTexture等。 (靠 这不写个材质一键检查非srgb?) (写了写了)

DirectoryInfo

System.IO : Exposes instance methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.

有关路径的操作,不只限于Editor。 e.g.

  • 查询路径是否存在
    // Check if the target directory exists, if not, create it.
    if (Directory.Exists(target.FullName) == false)
    {
        Directory.CreateDirectory(target.FullName);
    }
  • 获得文件夹中文件的info

    [FileSystemInfo][12][] fileSystemInfos = directoryInfo.GetFileSystemInfos();

    获取路径和资产

    如果已经有某个资产,需要获得它的路径,可以使用AssetDatabase.GetAssetPath()方法,比如上一篇用到的:

    string path = AssetDatabase.GetAssetPath(metallic);

    或者可以让用户选择要操作的文件夹路径:

    private void SelectFolderPath()
    {
        GUILayout.BeginHorizontal();
        {
            FolderPath = EditorGUILayout.TextField("Textures Folder", FolderPath);
            if (GUILayout.Button("...", GUILayout.Width(25))) {
                FolderPath = EditorUtility.OpenFolderPanel("Select a folder", "Assets", "");
            }
            EditorGUIUtility.labelWidth = 0;
        }
        GUILayout.EndHorizontal();
    }

    EditorUtility.OpenFolderPanel()会打开一个选择文件夹的窗口,参数分别为[窗口名],[默认文件夹],[默认名]。上一篇中自定义保存用到了类似的EditorUtility.SaveFilePanel()。 selectfolder 这里学到了一个处理路径的小trick,可以只记录项目文件夹后的路径信息。定义此参数时就可以设定它的get/set方式: folderpath

    private string _folderPath;
    protected string FolderPath {
        get {
            if (string.IsNullOrEmpty(_folderPath))
                _folderPath = "Assets";
            return _folderPath;
        }
    
        set {
            string pstr = Directory.GetParent(Application.dataPath).FullName;
            pstr = pstr.Replace("\\", "/");
            if (value != null && value.Contains(pstr))
                _folderPath = value.Replace(pstr + "/", "");
            else
                _folderPath = value;
        }
    }

    获得文件夹路径后,就可以通过AssetDatabase.FindAsset找到资源:

        var texture2Ds = AssetDatabase.FindAssets ("t:Texture2D", new[]{FolderPath});
        foreach (var textureguid in texture2Ds)
        {
            var texturepath = AssetDatabase.GUIDToAssetPath(textureguid);
            var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            //other operations HERE
        }

    批量更改图片导入设置

    unity不能自动识别从第三方网站下载的贴图类型,根据贴图存储的信息不同,需要进行不同的导入设置: texturesettings 前面提到这些设置信息是存储在TextureImporter中的,只要从路径获取对应的Importer就可以了。注意修改后要SaveandReimport(),相当于setting面板点击apply。 fixsettings 目前还没有用到AssetBundle,之后如果需要就也会写啦! 实现代码如下:

    private void FixSettings()
    {
        //获取文件夹下全部材质
        var texture2Ds = AssetDatabase.FindAssets ("t:Texture2D", new[]{FolderPath});
        foreach (var textureguid in texture2Ds)
        {
            var texturepath = AssetDatabase.GUIDToAssetPath(textureguid);
            //从路径获取importer
            var importer = AssetImporter.GetAtPath(texturepath) as TextureImporter;
            //只处理2D材质
            if (importer.textureShape == TextureImporterShape.Texture2D)
            {
                //法线贴图,type设置为normal
                if (texturepath.Contains("_normal")||texturepath.Contains("_Normal"))
                {
                    if (importer.textureType != TextureImporterType.NormalMap)
                    {
                        importer.textureType = TextureImporterType.NormalMap;
                        importer.SaveAndReimport();
                    }
                    continue;
                }
                //固有色贴图,开启sRGB
                if (texturepath.Contains("_albedo") || texturepath.Contains("_Albedo") ||
                    texturepath.Contains("_basecolor") || texturepath.Contains("_BaseColor") ||
                    texturepath.Contains("_baseColor")|| texturepath.Contains("_color") )
                {
                    if (!importer.sRGBTexture)
                    {
                        importer.sRGBTexture = true;
                        importer.SaveAndReimport();
                    }
                    continue;
                }
                //其他贴图,不开启sRGB
                else
                {
                    if (importer.sRGBTexture)
                    {
                        importer.sRGBTexture = false;
                        importer.SaveAndReimport();
                    }
                }
            }

    批量生成mask map

    利用了之前mapCreator的函数,还写了一个删除不需要的贴图的功能。其实这里还应该有一个弹出窗口警告不可撤销⚠但是我累了,,1.1再说,,, 有感而发:对于批处理工具而言,命名真的好重要哇。

    private void BatchMask()
    {
        var texture2Ds = AssetDatabase.FindAssets ("t:Texture2D", new[]{FolderPath});
        string currentTexture = "";
        maptype = 0;
        bool hasM=false, hasAO=false,hasDM = false, hasR=false;
        foreach (var textureguid in texture2Ds)
        {
            var texturepath = AssetDatabase.GUIDToAssetPath(textureguid);
            //获取前缀前的材质名,用于识别贴图组
            string exactname = Reverse(texturepath);
            exactname = exactname.Split(new char[]{'_'}, 2)[1];
            exactname = Reverse(exactname);
            //当读入新的贴图名时,生成上一贴图组的maskmap
            if (exactname != currentTexture)
            {
                //当至少含有一张生成maskmap所需贴图时
                if (hasM || hasR || hasDM || hasAO)
                {
                    //将没有的贴图改为默认值,以免用到上一贴图组的贴图
                    if(!hasM) metallic = null;
                    if(!hasAO) occlusion = null;
                    if(!hasDM) detailmask = null;
                    if(!hasR) smoothness = null;
                    GenPictureAuto();
                    //如果需要删除hdrp不需要的贴图
                    if (deleteunused)
                    {
                        string deletepath;
                        if (hasM)
                        {
                            deletepath = AssetDatabase.GetAssetPath(metallic);
                            AssetDatabase.DeleteAsset(deletepath);
                        }
                        if (hasAO)
                        {
                            deletepath = AssetDatabase.GetAssetPath(occlusion);
                            AssetDatabase.DeleteAsset(deletepath);
                        }
                        if (hasDM)
                        {
                            deletepath = AssetDatabase.GetAssetPath(detailmask);
                            AssetDatabase.DeleteAsset(deletepath);
                        }
                        if (hasR)
                        {
                            deletepath = AssetDatabase.GetAssetPath(smoothness);
                            AssetDatabase.DeleteAsset(deletepath);
                        }
                        AssetDatabase.Refresh();
                    }
                }
                //重置信息
                hasM = false; hasR = false; hasDM = false; hasAO = false;
                currentTexture = exactname;
            }
            //若为金属度贴图
            if (texturepath.Contains("_metallic") || texturepath.Contains("_Metallic"))
            {
                hasM = true;
                metallic = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            }
            //若为ao
            else if(texturepath.Contains("_ao") || texturepath.Contains("_AO") 
                                               || texturepath.Contains("_occlusion")|| texturepath.Contains("_Occlusion")) 
            {
                hasAO = true;
                occlusion = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            }
            //若为Smoothness
            else if(texturepath.Contains("_smoothness") || texturepath.Contains("_Smoothness"))
            {
                hasR = true;
                useRoughness = false;
                smoothness = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            }
            //若为Roughness
            else if(texturepath.Contains("_Roughness") || texturepath.Contains("_roughness"))
            {
                hasR = true;
                useRoughness = true;
                smoothness = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            }
            //若为DetailMask
            else if(texturepath.Contains("_DetailMask") || texturepath.Contains("_detailmask"))
            {
                hasDM = true;
                detailmask = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            }
        }
    }

改变图片大小

加载图片,将其画在一张RenderTexture中,再新建一张Texture2D,从rt中读出来处理过的数据,再存进原路径中。 目前的这个功能还很不满意!用RT计算应该不是很合理的图像采样方法,放大缩小都会丢失很多细节... 不过还是先放一下~权当复习RT和图像写入了~

private void ResizeTextures()
    {
        var tempTexture = new Texture2D((int) resizeWH.x, (int) resizeWH.y, TextureFormat.ARGB32, false);
        //获取文件夹下全部材质
        var texture2Ds = AssetDatabase.FindAssets ("t:Texture2D", new[]{FolderPath});
        foreach (var textureguid in texture2Ds)
        {
            var texturepath = AssetDatabase.GUIDToAssetPath(textureguid);
            var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(texturepath);
            //若不需要重新调整大小,则跳过
            Vector2 currentsize = new Vector2(texture.width, texture.height);
            if(currentsize == resizeWH) 
                continue;
            var rt = RenderTexture.GetTemporary((int) resizeWH.x, (int) resizeWH.y, 0, RenderTextureFormat.ARGB32);
            rt.DiscardContents();
            Graphics.Blit(texture,rt);
            RenderTexture.active = rt;
            tempTexture.ReadPixels(new Rect(0,0,rt.width,rt.height),0,0);
            tempTexture.Apply();
            if (texturepath.EndsWith(".png"))
            {   //如果该材质含有tif标签,则跳过
                var im = AssetImporter.GetAtPath (texturepath);
                if (im.userData.Contains ("FromTIF;"))
                    continue;
                File.WriteAllBytes(texturepath, tempTexture.EncodeToPNG());
            }
            else if (texturepath.EndsWith(".jpg"))
            {
                File.WriteAllBytes(texturepath, tempTexture.EncodeToJPG());
            }
            else if (texturepath.EndsWith(".tif"))
            {
                Debug.LogWarningFormat(texturepath + "has been saved as PNG.");
                string newname = texturepath.Substring(0, texturepath.Length - 3) + "png";
                File.WriteAllBytes(newname, tempTexture.EncodeToPNG());
                //为tif转换的png图片增加标签
                var im = AssetImporter.GetAtPath(newname);
                im.userData += "FromTIF;";
            }
            RenderTexture.ReleaseTemporary(rt);
        }
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }
参考文章

by ERIN.Z

2024 © typecho & elise