diff --git a/AssetStudio/AssetStudio-x86.csproj b/AssetStudio/AssetStudio-x86.csproj index 0677ef5..3840483 100644 --- a/AssetStudio/AssetStudio-x86.csproj +++ b/AssetStudio/AssetStudio-x86.csproj @@ -211,6 +211,7 @@ + AssetStudioForm.cs Designer diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index 6f1cfca..c566505 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -211,6 +211,7 @@ AssetStudioForm.cs + ExportOptions.cs diff --git a/AssetStudio/AssetStudioForm.cs b/AssetStudio/AssetStudioForm.cs index 3a0960c..ae99a71 100644 --- a/AssetStudio/AssetStudioForm.cs +++ b/AssetStudio/AssetStudioForm.cs @@ -30,34 +30,27 @@ namespace AssetStudio private FMOD.MODE loopMode = FMOD.MODE.LOOP_OFF; private uint FMODlenms; private float FMODVolume = 0.8f; - private float FMODfrequency; private Bitmap imageTexture; - #region OpenTK variables - int pgmID, pgmColorID, pgmBlackID; - int attributeVertexPosition; - int attributeNormalDirection; - int attributeVertexColor; - int uniformModelMatrix; - int uniformViewMatrix; - int vao; - int vboPositions; - int vboNormals; - int vboColors; - int vboModelMatrix; - int vboViewMatrix; - int eboElements; - Vector3[] vertexData; - Vector3[] normalData; - Vector3[] normal2Data; - Vector4[] colorData; - Matrix4 modelMatrixData; - Matrix4 viewMatrixData; - int[] indiceData; - int wireFrameMode; - int shadeMode; - int normalMode; + #region OpenTK + private int pgmID, pgmColorID, pgmBlackID; + private int attributeVertexPosition; + private int attributeNormalDirection; + private int attributeVertexColor; + private int uniformModelMatrix; + private int uniformViewMatrix; + private int vao; + private Vector3[] vertexData; + private Vector3[] normalData; + private Vector3[] normal2Data; + private Vector4[] colorData; + private Matrix4 modelMatrixData; + private Matrix4 viewMatrixData; + private int[] indiceData; + private int wireFrameMode; + private int shadeMode; + private int normalMode; #endregion //asset list sorting helpers @@ -70,8 +63,6 @@ namespace AssetStudio private int nextGObject; private List treeSrcResults = new List(); - private PrivateFontCollection pfc = new PrivateFontCollection(); - [DllImport("gdi32.dll")] private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts); @@ -176,16 +167,12 @@ namespace AssetStudio return; } - bool optionLoadAssetsMenuItem = !dontLoadAssetsMenuItem.Checked; - bool optionDisplayAll = displayAll.Checked; - bool optionBuildHierarchyMenuItem = !dontBuildHierarchyMenuItem.Checked; - bool optionBuildClassStructuresMenuItem = buildClassStructuresMenuItem.Checked; - - BuildAssetStructures(optionLoadAssetsMenuItem, optionDisplayAll, optionBuildHierarchyMenuItem, optionBuildClassStructuresMenuItem, displayOriginalName.Checked); + BuildAssetStructures(!dontLoadAssetsMenuItem.Checked, displayAll.Checked, !dontBuildHierarchyMenuItem.Checked, buildClassStructuresMenuItem.Checked, + displayOriginalName.Checked, out var fileNodes); BeginInvoke(new Action(() => { - if (productName != "") + if (!string.IsNullOrEmpty(productName)) { Text = $"AssetStudio - {productName} - {assetsfileList[0].m_Version} - {assetsfileList[0].platformStr}"; } @@ -203,7 +190,10 @@ namespace AssetStudio { sceneTreeView.BeginUpdate(); sceneTreeView.Nodes.AddRange(fileNodes.ToArray()); - fileNodes.Clear(); + foreach (TreeNode node in sceneTreeView.Nodes) + { + node.HideCheckBox(); + } sceneTreeView.EndUpdate(); } if (buildClassStructuresMenuItem.Checked) @@ -676,7 +666,6 @@ namespace AssetStudio assetInfoLabel.Text = null; textPreviewBox.Visible = false; fontPreviewBox.Visible = false; - pfc.Dispose(); FMODpanel.Visible = false; glControl1.Visible = false; lastLoadedAsset = null; @@ -759,10 +748,10 @@ namespace AssetStudio FMODpanel.Visible = true; - result = channel.getFrequency(out FMODfrequency); + result = channel.getFrequency(out var frequency); if (ERRCHECK(result)) { break; } - FMODinfoLabel.Text = FMODfrequency + " Hz"; + FMODinfoLabel.Text = frequency + " Hz"; FMODtimerLabel.Text = $"0:0.0 / {FMODlenms / 1000 / 60}:{FMODlenms / 1000 % 60}.{FMODlenms / 10 % 100}"; break; } @@ -808,38 +797,38 @@ namespace AssetStudio var re = AddFontMemResourceEx(data, (uint)m_Font.m_FontData.Length, IntPtr.Zero, ref cFonts); if (re != IntPtr.Zero) { - pfc = new PrivateFontCollection(); - pfc.AddMemoryFont(data, m_Font.m_FontData.Length); - Marshal.FreeCoTaskMem(data); - if (pfc.Families.Length > 0) + using (var pfc = new PrivateFontCollection()) { - //textPreviewBox.Font = new Font(pfc.Families[0], 16, FontStyle.Regular); - //textPreviewBox.Text = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ\r\n1234567890.:,;'\"(!?)+-*/=\r\nThe quick brown fox jumps over the lazy dog. 1234567890"; - fontPreviewBox.SelectionStart = 0; - fontPreviewBox.SelectionLength = 80; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 16, FontStyle.Regular); - fontPreviewBox.SelectionStart = 81; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 12, FontStyle.Regular); - fontPreviewBox.SelectionStart = 138; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 18, FontStyle.Regular); - fontPreviewBox.SelectionStart = 195; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 24, FontStyle.Regular); - fontPreviewBox.SelectionStart = 252; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 36, FontStyle.Regular); - fontPreviewBox.SelectionStart = 309; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 48, FontStyle.Regular); - fontPreviewBox.SelectionStart = 366; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 60, FontStyle.Regular); - fontPreviewBox.SelectionStart = 423; - fontPreviewBox.SelectionLength = 55; - fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 72, FontStyle.Regular); - fontPreviewBox.Visible = true; + pfc.AddMemoryFont(data, m_Font.m_FontData.Length); + Marshal.FreeCoTaskMem(data); + if (pfc.Families.Length > 0) + { + fontPreviewBox.SelectionStart = 0; + fontPreviewBox.SelectionLength = 80; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 16, FontStyle.Regular); + fontPreviewBox.SelectionStart = 81; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 12, FontStyle.Regular); + fontPreviewBox.SelectionStart = 138; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 18, FontStyle.Regular); + fontPreviewBox.SelectionStart = 195; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 24, FontStyle.Regular); + fontPreviewBox.SelectionStart = 252; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 36, FontStyle.Regular); + fontPreviewBox.SelectionStart = 309; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 48, FontStyle.Regular); + fontPreviewBox.SelectionStart = 366; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 60, FontStyle.Regular); + fontPreviewBox.SelectionStart = 423; + fontPreviewBox.SelectionLength = 55; + fontPreviewBox.SelectionFont = new Font(pfc.Families[0], 72, FontStyle.Regular); + fontPreviewBox.Visible = true; + } } break; } @@ -1576,20 +1565,20 @@ namespace AssetStudio GL.DeleteVertexArray(vao); GL.GenVertexArrays(1, out vao); GL.BindVertexArray(vao); - createVBO(out vboPositions, vertexData, attributeVertexPosition); + createVBO(out var vboPositions, vertexData, attributeVertexPosition); if (normalMode == 0) { - createVBO(out vboNormals, normal2Data, attributeNormalDirection); + createVBO(out var vboNormals, normal2Data, attributeNormalDirection); } else { if (normalData != null) - createVBO(out vboNormals, normalData, attributeNormalDirection); + createVBO(out var vboNormals, normalData, attributeNormalDirection); } - createVBO(out vboColors, colorData, attributeVertexColor); - createVBO(out vboModelMatrix, modelMatrixData, uniformModelMatrix); - createVBO(out vboViewMatrix, viewMatrixData, uniformViewMatrix); - createEBO(out eboElements, indiceData); + createVBO(out var vboColors, colorData, attributeVertexColor); + createVBO(out var vboModelMatrix, modelMatrixData, uniformModelMatrix); + createVBO(out var vboViewMatrix, viewMatrixData, uniformViewMatrix); + createEBO(out var eboElements, indiceData); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindVertexArray(0); } @@ -1826,7 +1815,7 @@ namespace AssetStudio var savePath = saveFolderDialog1.Folder + "\\"; progressBar1.Value = 0; progressBar1.Maximum = sceneTreeView.Nodes.Count; - ExportSplitObjectsNew(savePath, sceneTreeView.Nodes); + ExportSplitObjects(savePath, sceneTreeView.Nodes, true); } } else diff --git a/AssetStudio/StudioClasses/FBXExporter.cs b/AssetStudio/StudioClasses/FBXExporter.cs index 66677b2..a98a203 100644 --- a/AssetStudio/StudioClasses/FBXExporter.cs +++ b/AssetStudio/StudioClasses/FBXExporter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Web.Script.Serialization; using static AssetStudio.Studio; using static AssetStudio.Exporter; @@ -13,6 +14,15 @@ namespace AssetStudio public static void WriteFBX(string FBXfile, List gameObjects) { var timestamp = DateTime.Now; + Dictionary> jsonMats = null; + if (File.Exists(mainPath + "\\materials.json")) + { + using (var reader = File.OpenText(mainPath + "\\materials.json")) + { + var matLine = reader.ReadToEnd(); + jsonMats = new JavaScriptSerializer().Deserialize>>(matLine); + } + } using (StreamWriter FBXwriter = new StreamWriter(FBXfile)) { diff --git a/AssetStudio/StudioClasses/Studio.cs b/AssetStudio/StudioClasses/Studio.cs index 2590f66..e5355ad 100644 --- a/AssetStudio/StudioClasses/Studio.cs +++ b/AssetStudio/StudioClasses/Studio.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Threading; -using System.Web.Script.Serialization; using System.Windows.Forms; using static AssetStudio.Exporter; @@ -19,13 +18,9 @@ namespace AssetStudio public static List exportableAssets = new List(); //used to hold all assets while the ListView is filtered private static HashSet exportableAssetsHash = new HashSet(); //avoid the same name asset public static List visibleAssets = new List(); //used to build the ListView from all or filtered assets - - public static string productName = ""; - public static string mainPath = ""; - public static List fileNodes = new List(); - - public static Dictionary> jsonMats; public static Dictionary> AllClassStructures = new Dictionary>(); + public static string mainPath; + public static string productName = ""; //UI public static Action SetProgressBarValue; @@ -154,10 +149,12 @@ namespace AssetStudio return extractedCount; } - public static void BuildAssetStructures(bool loadAssetsMenuItem, bool displayAll, bool buildHierarchyMenuItem, bool buildClassStructuresMenuItem, bool displayOriginalName) + public static void BuildAssetStructures(bool loadAssets, bool displayAll, bool buildHierarchy, bool buildClassStructures, bool displayOriginalName, out List fileNodes) { + fileNodes = null; + #region first loop - read asset data & create list - if (loadAssetsMenuItem) + if (loadAssets) { SetProgressBarValue(0); SetProgressBarMaximum(assetsfileList.Sum(x => x.preloadTable.Values.Count)); @@ -325,115 +322,107 @@ namespace AssetStudio #endregion #region second loop - build tree structure - fileNodes = new List(); - if (buildHierarchyMenuItem) + if (buildHierarchy) { - SetProgressBarValue(0); - SetProgressBarMaximum(assetsfileList.Sum(x => x.GameObjectList.Values.Count)); - StatusStripUpdate("Building tree structure..."); - - foreach (var assetsFile in assetsfileList) + fileNodes = new List(); + var gameObjectCount = assetsfileList.Sum(x => x.GameObjectList.Values.Count); + if (gameObjectCount > 0) { - GameObject fileNode = new GameObject(null); - fileNode.Text = Path.GetFileName(assetsFile.filePath); - fileNode.m_Name = "RootNode"; + SetProgressBarValue(0); + SetProgressBarMaximum(gameObjectCount); + StatusStripUpdate("Building tree structure..."); - foreach (var m_GameObject in assetsFile.GameObjectList.Values) + foreach (var assetsFile in assetsfileList) { - foreach (var m_Component in m_GameObject.m_Components) + GameObject fileNode = new GameObject(null); + fileNode.Text = Path.GetFileName(assetsFile.filePath); + fileNode.m_Name = "RootNode"; + + foreach (var m_GameObject in assetsFile.GameObjectList.Values) { - if (m_Component.m_FileID >= 0 && m_Component.m_FileID < assetsfileList.Count) + foreach (var m_Component in m_GameObject.m_Components) { - var sourceFile = assetsfileList[m_Component.m_FileID]; - if (sourceFile.preloadTable.TryGetValue(m_Component.m_PathID, out var asset)) + if (m_Component.m_FileID >= 0 && m_Component.m_FileID < assetsfileList.Count) { - switch (asset.Type) + var sourceFile = assetsfileList[m_Component.m_FileID]; + if (sourceFile.preloadTable.TryGetValue(m_Component.m_PathID, out var asset)) { - case ClassIDReference.Transform: - { - m_GameObject.m_Transform = m_Component; - break; - } - case ClassIDReference.MeshRenderer: - { - m_GameObject.m_MeshRenderer = m_Component; - break; - } - case ClassIDReference.MeshFilter: - { - m_GameObject.m_MeshFilter = m_Component; - if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) + switch (asset.Type) + { + case ClassIDReference.Transform: { - var m_MeshFilter = new MeshFilter(assetPreloadData); - if (assetsfileList.TryGetPD(m_MeshFilter.m_Mesh, out assetPreloadData)) - { - assetPreloadData.gameObject = m_GameObject; - } + m_GameObject.m_Transform = m_Component; + break; } - break; - } - case ClassIDReference.SkinnedMeshRenderer: - { - m_GameObject.m_SkinnedMeshRenderer = m_Component; - if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) + case ClassIDReference.MeshRenderer: { - var m_SkinnedMeshRenderer = new SkinnedMeshRenderer(assetPreloadData); - if (assetsfileList.TryGetPD(m_SkinnedMeshRenderer.m_Mesh, out assetPreloadData)) - { - assetPreloadData.gameObject = m_GameObject; - } + m_GameObject.m_MeshRenderer = m_Component; + break; } - break; - } - case ClassIDReference.Animator: - { - m_GameObject.m_Animator = m_Component; - asset.Text = m_GameObject.asset.Text; - break; - } + case ClassIDReference.MeshFilter: + { + m_GameObject.m_MeshFilter = m_Component; + if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) + { + var m_MeshFilter = new MeshFilter(assetPreloadData); + if (assetsfileList.TryGetPD(m_MeshFilter.m_Mesh, out assetPreloadData)) + { + assetPreloadData.gameObject = m_GameObject; + } + } + break; + } + case ClassIDReference.SkinnedMeshRenderer: + { + m_GameObject.m_SkinnedMeshRenderer = m_Component; + if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) + { + var m_SkinnedMeshRenderer = new SkinnedMeshRenderer(assetPreloadData); + if (assetsfileList.TryGetPD(m_SkinnedMeshRenderer.m_Mesh, out assetPreloadData)) + { + assetPreloadData.gameObject = m_GameObject; + } + } + break; + } + case ClassIDReference.Animator: + { + m_GameObject.m_Animator = m_Component; + asset.Text = m_GameObject.asset.Text; + break; + } + } } } } - } - var parentNode = fileNode; + var parentNode = fileNode; - if (assetsfileList.TryGetTransform(m_GameObject.m_Transform, out var m_Transform)) - { - if (assetsfileList.TryGetTransform(m_Transform.m_Father, out var m_Father)) + if (assetsfileList.TryGetTransform(m_GameObject.m_Transform, out var m_Transform)) { - //GameObject Parent; - if (assetsfileList.TryGetGameObject(m_Father.m_GameObject, out parentNode)) + if (assetsfileList.TryGetTransform(m_Transform.m_Father, out var m_Father)) { - //parentNode = Parent; + if (assetsfileList.TryGetGameObject(m_Father.m_GameObject, out parentNode)) + { + } } } + + parentNode.Nodes.Add(m_GameObject); + ProgressBarPerformStep(); } - parentNode.Nodes.Add(m_GameObject); - ProgressBarPerformStep(); + if (fileNode.Nodes.Count > 0) + { + fileNodes.Add(fileNode); + } } - - if (fileNode.Nodes.Count > 0) - { - fileNodes.Add(fileNode); - } - } - - if (File.Exists(mainPath + "\\materials.json")) - { - string matLine; - using (StreamReader reader = File.OpenText(mainPath + "\\materials.json")) - { - matLine = reader.ReadToEnd(); - } - jsonMats = new JavaScriptSerializer().Deserialize>>(matLine); } } #endregion #region build list of class strucutres - if (buildClassStructuresMenuItem) + if (buildClassStructures) { //group class structures by versionv foreach (var assetsFile in assetsfileList) @@ -595,7 +584,7 @@ namespace AssetStudio }); } - public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes) + public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes, bool isNew = false) { ThreadPool.QueueUserWorkItem(state => { @@ -629,7 +618,10 @@ namespace AssetStudio Directory.CreateDirectory(targetPath); //导出FBX StatusStripUpdate($"Exporting {filename}.fbx"); - FBXExporter.WriteFBX($"{targetPath}{filename}.fbx", gameObjects); + if (isNew) + ExportGameObject((GameObject)j, targetPath); + else + FBXExporter.WriteFBX($"{targetPath}{filename}.fbx", gameObjects); StatusStripUpdate($"Finished exporting {filename}.fbx"); } ProgressBarPerformStep(); @@ -637,48 +629,6 @@ namespace AssetStudio }); } - public static void ExportSplitObjectsNew(string savePath, TreeNodeCollection nodes) - { - ThreadPool.QueueUserWorkItem(state => - { - 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(); - } - }); - } - private static void CollectNode(TreeNode node, List gameObjects) { gameObjects.Add((GameObject)node); diff --git a/AssetStudio/TreeViewExtensions.cs b/AssetStudio/TreeViewExtensions.cs new file mode 100644 index 0000000..4eedecd --- /dev/null +++ b/AssetStudio/TreeViewExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; + +namespace AssetStudio +{ + public static class TreeViewExtensions + { + private const int TVIF_STATE = 0x8; + private const int TVIS_STATEIMAGEMASK = 0xF000; + private const int TV_FIRST = 0x1100; + private const int TVM_SETITEM = TV_FIRST + 63; + + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)] + private struct TVITEM + { + public int mask; + public IntPtr hItem; + public int state; + public int stateMask; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpszText; + public int cchTextMax; + public int iImage; + public int iSelectedImage; + public int cChildren; + public IntPtr lParam; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref TVITEM lParam); + + /// + /// Hides the checkbox for the specified node on a TreeView control. + /// + public static void HideCheckBox(this TreeNode node) + { + var tvi = new TVITEM + { + hItem = node.Handle, + mask = TVIF_STATE, + stateMask = TVIS_STATEIMAGEMASK, + state = 0 + }; + SendMessage(node.TreeView.Handle, TVM_SETITEM, IntPtr.Zero, ref tvi); + } + } +}