void templog(string msg)
        {
            MaterialFactoryItem log = new MaterialFactoryItem();

            log.Name = msg;
            Items.Add(log);
        }
        async Task <int> parseCategoryDir(string dir, CategoryModel cate)
        {
            if (cate == null)
            {
                //templog("category is null");
                return(0);
            }

            var files = Directory.GetFiles(dir);

            Dictionary <string, List <MaterialTextureInfo> > textureMap = new Dictionary <string, List <MaterialTextureInfo> >();

            MaterialModel matTemplate = null;

            Logic.CategoryCustomData_Material matTemplateInfo = null;
            if (string.IsNullOrEmpty(cate.customData) == false)
            {
                try
                {
                    matTemplateInfo = Newtonsoft.Json.JsonConvert.DeserializeObject <Logic.CategoryCustomData_Material>(cate.customData);
                }
                catch { }
            }
            if (matTemplateInfo?.TemplateId != null)
            {
                var getres = await Logic.BambooEditor.Instance.api.GetAsync <MaterialModel>("/material/" + matTemplateInfo.TemplateId);

                matTemplate = getres.Content;
                categoryTemplateMap[cate.id] = matTemplate;
            }

            foreach (var file in files)
            {
                MaterialTextureInfo info = new MaterialTextureInfo();
                info.Path = Path.GetDirectoryName(file);
                string filename = Path.GetFileNameWithoutExtension(file);
                info.Name      = "";
                info.ParamName = "";
                int tagIndex = filename.IndexOf('_');
                if (tagIndex > 0)
                {
                    info.Name = filename.Substring(0, tagIndex);
                    if (tagIndex < filename.Length - 1)
                    {
                        info.ParamName = filename.Substring(tagIndex + 1);
                    }
                }
                else
                {
                    info.Name = filename;
                }

                List <MaterialTextureInfo> textures = null;
                if (textureMap.ContainsKey(info.Name))
                {
                    textures = textureMap[info.Name];
                }
                else
                {
                    textures = new List <MaterialTextureInfo>();
                    textureMap[info.Name] = textures;
                }
                textures.Add(info);
            }

            //templog($"{dir} find {files.Length} files, get {textureMap.Count} materials");

            foreach (var item in textureMap)
            {
                MaterialFactoryItem mat = new MaterialFactoryItem();
                mat.TextureList         = item.Value;
                mat.Name                = item.Key;
                mat.CategoryId          = cate.id;
                mat.CategoryPath        = getCategoryPathName(cate);
                mat.MaterialTempateName = matTemplate?.name;

                mat.PropObjToString();
                Items.Add(mat);
            }

            var dirs = Directory.GetDirectories(dir);

            foreach (var d in dirs)
            {
                string        dirname   = Path.GetFileName(d);
                CategoryModel childcate = cate.children.Find(t => t.name == dirname);
                await parseCategoryDir(d, childcate);
            }

            return(textureMap.Count);
        }