diff --git a/AssetStudio/AssetStudioForm.Designer.cs b/AssetStudio/AssetStudioForm.Designer.cs index 5c4d575..d485ad1 100644 --- a/AssetStudio/AssetStudioForm.Designer.cs +++ b/AssetStudio/AssetStudioForm.Designer.cs @@ -56,6 +56,8 @@ this.exportAllAssetsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportSelectedAssetsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportFilteredAssetsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.debugMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -111,8 +113,7 @@ this.exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportobjectswithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); - this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exportAllObjectssplitToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -283,6 +284,7 @@ this.exportallobjectssplitToolStripMenuItem, this.exportselectedobjectsMenuItem, this.toolStripSeparator1, + this.exportAllObjectssplitToolStripMenuItem1, this.exportSelectedObjectsToolStripMenuItem, this.exportSelectedObjectsWithAnimationClipToolStripMenuItem}); this.modelToolStripMenuItem.Name = "modelToolStripMenuItem"; @@ -362,6 +364,18 @@ this.exportFilteredAssetsMenuItem.Text = "Filtered assets"; this.exportFilteredAssetsMenuItem.Click += new System.EventHandler(this.ExportAssets_Click); // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(331, 6); + // + // exportAnimatorWithSelectedAnimationClipToolStripMenuItem + // + this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Name = "exportAnimatorWithSelectedAnimationClipToolStripMenuItem"; + this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Size = new System.Drawing.Size(334, 22); + this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Text = "Export Animator with selected AnimationClip"; + this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportAnimatorwithAnimationClipMenuItem_Click); + // // showTypeToolStripMenuItem // this.showTypeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -917,17 +931,12 @@ this.showOriginalFileToolStripMenuItem.Visible = false; this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click); // - // toolStripSeparator3 + // exportAllObjectssplitToolStripMenuItem1 // - this.toolStripSeparator3.Name = "toolStripSeparator3"; - this.toolStripSeparator3.Size = new System.Drawing.Size(331, 6); - // - // exportAnimatorWithSelectedAnimationClipToolStripMenuItem - // - this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Name = "exportAnimatorWithSelectedAnimationClipToolStripMenuItem"; - this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Size = new System.Drawing.Size(334, 22); - this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Text = "Export Animator with selected AnimationClip"; - this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportAnimatorwithAnimationClipMenuItem_Click); + this.exportAllObjectssplitToolStripMenuItem1.Name = "exportAllObjectssplitToolStripMenuItem1"; + this.exportAllObjectssplitToolStripMenuItem1.Size = new System.Drawing.Size(323, 22); + this.exportAllObjectssplitToolStripMenuItem1.Text = "Export all objects (split)"; + this.exportAllObjectssplitToolStripMenuItem1.Click += new System.EventHandler(this.exportAllObjectssplitToolStripMenuItem1_Click); // // AssetStudioForm // @@ -1059,6 +1068,7 @@ private System.Windows.Forms.ToolStripMenuItem exportSelectedObjectsWithAnimationClipToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem exportAnimatorWithSelectedAnimationClipToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem exportAllObjectssplitToolStripMenuItem1; } } diff --git a/AssetStudio/AssetStudioForm.cs b/AssetStudio/AssetStudioForm.cs index 5acfd66..a7820f9 100644 --- a/AssetStudio/AssetStudioForm.cs +++ b/AssetStudio/AssetStudioForm.cs @@ -1392,7 +1392,9 @@ namespace AssetStudio toolStripStatusLabel1.Text = "Nothing exported."; return; } + toolStripStatusLabel1.Text = $"Exporting {Path.GetFileName(saveFileDialog1.FileName)}.fbx"; FBXExporter.WriteFBX(saveFileDialog1.FileName, gameObjects); + toolStripStatusLabel1.Text = $"Finished exporting {Path.GetFileName(saveFileDialog1.FileName)}.fbx"; progressBar1.PerformStep(); if (openAfterExport.Checked && File.Exists(saveFileDialog1.FileName)) { @@ -1816,6 +1818,26 @@ namespace AssetStudio } } + private void exportAllObjectssplitToolStripMenuItem1_Click(object sender, EventArgs e) + { + if (sceneTreeView.Nodes.Count > 0) + { + var saveFolderDialog1 = new OpenFolderDialog(); + if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) + { + var savePath = saveFolderDialog1.Folder + "\\"; + progressBar1.Value = 0; + progressBar1.Maximum = sceneTreeView.Nodes.Count; + + ThreadPool.QueueUserWorkItem(state => ExportSplitObjectsNew(savePath, sceneTreeView.Nodes)); + } + } + else + { + StatusStripUpdate("No Objects available for export"); + } + } + private void exportObjectswithAnimationClipMenuItem_Click(object sender, EventArgs e) { var selectedAssets = GetSelectedAssets(); diff --git a/AssetStudio/Classes/GameObject.cs b/AssetStudio/Classes/GameObject.cs index 0fa3f40..6c5d5c5 100644 --- a/AssetStudio/Classes/GameObject.cs +++ b/AssetStudio/Classes/GameObject.cs @@ -10,10 +10,6 @@ namespace AssetStudio { public AssetPreloadData asset; public List m_Components = new List(); - public PPtr m_Transform; - public PPtr m_MeshRenderer; - public PPtr m_MeshFilter; - public PPtr m_SkinnedMeshRenderer; public int m_Layer; public string m_Name; public ushort m_Tag; @@ -21,6 +17,12 @@ namespace AssetStudio public string uniqueID = "0";//this way file and folder TreeNodes will be treated as FBX scene + public PPtr m_Transform; + public PPtr m_MeshRenderer; + public PPtr m_MeshFilter; + public PPtr m_SkinnedMeshRenderer; + public PPtr m_Animator; + public GameObject(AssetPreloadData preloadData) { if (preloadData != null) diff --git a/AssetStudio/Properties/AssemblyInfo.cs b/AssetStudio/Properties/AssemblyInfo.cs index ac25e77..475b25b 100644 --- a/AssetStudio/Properties/AssemblyInfo.cs +++ b/AssetStudio/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("AssetStudio")] -[assembly: AssemblyDescription("AssetStudio is a tool for exploring, extracting and exporting assets and assetBundles.")] +[assembly: AssemblyDescription("AssetStudio is a tool for exploring, extracting and exporting assets and assetbundles.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AssetStudio")] @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.0.0")] -[assembly: AssemblyFileVersion("0.9.0.0")] +[assembly: AssemblyVersion("0.10.0.0")] +[assembly: AssemblyFileVersion("0.10.0.0")] diff --git a/AssetStudio/StudioClasses/FBXExporter.cs b/AssetStudio/StudioClasses/FBXExporter.cs index dbae84e..9659c73 100644 --- a/AssetStudio/StudioClasses/FBXExporter.cs +++ b/AssetStudio/StudioClasses/FBXExporter.cs @@ -149,7 +149,6 @@ namespace AssetStudio #endregion #region write Materials, collect Texture objects - //StatusStripUpdate("Writing Materials"); foreach (var MaterialPD in Materials) { Material m_Material = new Material(MaterialPD); @@ -381,7 +380,6 @@ namespace AssetStudio #endregion #region write Model nodes and connections - //StatusStripUpdate("Writing Nodes and hierarchy"); foreach (var m_GameObject in GameObjects) { if (m_GameObject.m_MeshFilter == null && m_GameObject.m_SkinnedMeshRenderer == null) @@ -451,7 +449,6 @@ namespace AssetStudio #endregion #region write non-skinnned Geometry - //StatusStripUpdate("Writing Geometry"); foreach (var MeshPD in Meshes) { Mesh m_Mesh = new Mesh(MeshPD, true); @@ -681,8 +678,6 @@ namespace AssetStudio cb.Append("\n}");//Connections end FBXwriter.Write(cb); cb.Clear(); - - StatusStripUpdate("Finished exporting " + Path.GetFileName(FBXfile)); } } @@ -690,8 +685,6 @@ namespace AssetStudio { if (m_Mesh.m_VertexCount > 0)//general failsafe { - //StatusStripUpdate("Writing Geometry: " + m_Mesh.m_Name); - ob.AppendFormat("\n\tGeometry: 3{0}, \"Geometry::\", \"Mesh\" {{", MeshID); ob.Append("\n\t\tProperties70: {"); var randomColor = RandomColorGenerator(m_Mesh.m_Name); diff --git a/AssetStudio/StudioClasses/ModelConverter.cs b/AssetStudio/StudioClasses/ModelConverter.cs index 37c2c64..2ad55cc 100644 --- a/AssetStudio/StudioClasses/ModelConverter.cs +++ b/AssetStudio/StudioClasses/ModelConverter.cs @@ -25,7 +25,15 @@ namespace AssetStudio public ModelConverter(GameObject m_GameObject) { - InitWithGameObject(m_GameObject); + if (assetsfileList.TryGetPD(m_GameObject.m_Animator, out var m_Animator)) + { + var animator = new Animator(m_Animator); + InitWithAnimator(animator); + CollectAnimationClip(animator); + ConvertAnimations(); + } + else + InitWithGameObject(m_GameObject); } public ModelConverter(Animator m_Animator) @@ -65,7 +73,6 @@ namespace AssetStudio InitWithGameObject(m_GameObject); } - private void InitWithGameObject(GameObject m_GameObject) { assetsfileList.TryGetTransform(m_GameObject.m_Transform, out var m_Transform); diff --git a/AssetStudio/StudioClasses/Studio.cs b/AssetStudio/StudioClasses/Studio.cs index c257ab1..85905ad 100644 --- a/AssetStudio/StudioClasses/Studio.cs +++ b/AssetStudio/StudioClasses/Studio.cs @@ -85,12 +85,13 @@ namespace AssetStudio { SetProgressBarValue(0); SetProgressBarMaximum(assetsfileList.Sum(x => x.preloadTable.Values.Count)); + StatusStripUpdate("Building asset list..."); + string fileIDfmt = "D" + assetsfileList.Count.ToString().Length; for (var i = 0; i < assetsfileList.Count; i++) { var assetsFile = assetsfileList[i]; - StatusStripUpdate("Building asset list from " + Path.GetFileName(assetsFile.filePath)); string fileID = i.ToString(fileIDfmt); AssetBundle ab = null; @@ -254,9 +255,10 @@ namespace AssetStudio SetProgressBarMaximum(1); SetProgressBarValue(1); SetProgressBarMaximum(assetsfileList.Sum(x => x.GameObjectList.Values.Count) + 1); + StatusStripUpdate("Building tree structure..."); + foreach (var assetsFile in assetsfileList) { - StatusStripUpdate("Building tree structure from " + Path.GetFileName(assetsFile.filePath)); GameObject fileNode = new GameObject(null); fileNode.Text = Path.GetFileName(assetsFile.filePath); fileNode.m_Name = "RootNode"; @@ -295,7 +297,8 @@ namespace AssetStudio } case ClassIDReference.Animator: { - asset.Text = m_GameObject.asset.Text;//TODO + m_GameObject.m_Animator = m_Component; + asset.Text = m_GameObject.asset.Text; break; } } @@ -520,23 +523,64 @@ namespace AssetStudio continue; //处理非法文件名 var filename = FixFileName(j.Text); - //每个文件单独文件夹 - var saveName = $"{savePath}{filename}\\{filename}.fbx"; + //每个文件存放在单独的文件夹 + var targetPath = $"{savePath}{filename}\\"; //重名文件处理 for (int i = 1; ; i++) { - if (File.Exists(saveName)) + if (Directory.Exists(targetPath)) { - saveName = $"{savePath}{filename} ({i})\\{filename}.fbx"; + targetPath = $"{savePath}{filename} ({i})\\"; } else { break; } } - Directory.CreateDirectory(Path.GetDirectoryName(saveName)); + Directory.CreateDirectory(targetPath); //导出FBX - FBXExporter.WriteFBX(saveName, gameObjects); + StatusStripUpdate($"Exporting {filename}.fbx"); + FBXExporter.WriteFBX($"{targetPath}{filename}.fbx", gameObjects); + StatusStripUpdate($"Finished exporting {filename}.fbx"); + } + ProgressBarPerformStep(); + } + } + + public static void ExportSplitObjectsNew(string savePath, TreeNodeCollection nodes) + { + foreach (TreeNode node in nodes) + { + //遍历一级子节点 + foreach (TreeNode j in node.Nodes) + { + //收集所有子节点 + var gameObjects = new List(); + CollectNode(j, gameObjects); + //跳过一些不需要导出的object + if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) + continue; + //处理非法文件名 + var filename = FixFileName(j.Text); + //每个文件存放在单独的文件夹 + var targetPath = $"{savePath}{filename}\\"; + //重名文件处理 + for (int i = 1; ; i++) + { + if (Directory.Exists(targetPath)) + { + targetPath = $"{savePath}{filename} ({i})\\"; + } + else + { + break; + } + } + Directory.CreateDirectory(targetPath); + //导出FBX + StatusStripUpdate($"Exporting {j.Text}.fbx"); + ExportGameObject((GameObject)j, targetPath); + StatusStripUpdate($"Finished exporting {j.Text}.fbx"); } ProgressBarPerformStep(); }