diff --git a/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj new file mode 100644 index 0000000..e3571ff --- /dev/null +++ b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj @@ -0,0 +1,51 @@ + + + + + Debug + AnyCPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136} + Library + Properties + AssetStudio.PInvoke + AssetStudio.PInvoke + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AssetStudio.PInvoke/DllLoader.cs b/AssetStudio.PInvoke/DllLoader.cs new file mode 100644 index 0000000..a912231 --- /dev/null +++ b/AssetStudio.PInvoke/DllLoader.cs @@ -0,0 +1,123 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; + +namespace AssetStudio.PInvoke +{ + public static class DllLoader + { + + public static void PreloadDll(string dllName) + { + var dllDir = GetDirectedDllDirectory(); + + // Not using OperatingSystem.Platform. + // See: https://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Win32.LoadDll(dllDir, dllName); + } + else + { + Posix.LoadDll(dllDir, dllName); + } + } + + private static string GetDirectedDllDirectory() + { + var localPath = new Uri(typeof(DllLoader).Assembly.CodeBase).LocalPath; + var localDir = Path.GetDirectoryName(localPath); + + var subDir = Environment.Is64BitProcess ? "x64" : "x86"; + + var directedDllDir = Path.Combine(localDir, subDir); + + return directedDllDir; + } + + private static class Win32 + { + + internal static void LoadDll(string dllDir, string dllName) + { + var dllFileName = $"{dllName}.dll"; + var directedDllPath = Path.Combine(dllDir, dllFileName); + + // Specify SEARCH_DLL_LOAD_DIR to load dependent libraries located in the same platform-specific directory. + var hLibrary = LoadLibraryEx(directedDllPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + + if (hLibrary == IntPtr.Zero) + { + var errorCode = Marshal.GetLastWin32Error(); + var exception = new Win32Exception(errorCode); + + throw new DllNotFoundException(exception.Message, exception); + } + } + + // HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + // HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags); + + private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000; + private const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100; + + } + + private static class Posix + { + + internal static void LoadDll(string dllDir, string dllName) + { + string dllExtension; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + dllExtension = ".so"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + dllExtension = ".dylib"; + } + else + { + throw new NotSupportedException(); + } + + var dllFileName = $"lib{dllName}{dllExtension}"; + var directedDllPath = Path.Combine(dllDir, dllFileName); + + const int ldFlags = RTLD_NOW | RTLD_GLOBAL; + var hLibrary = DlOpen(directedDllPath, ldFlags); + + if (hLibrary == IntPtr.Zero) + { + var pErrStr = DlError(); + // `PtrToStringAnsi` always uses the specific constructor of `String` (see dotnet/core#2325), + // which in turn interprets the byte sequence with system default codepage. On OSX and Linux + // the codepage is UTF-8 so the error message should be handled correctly. + var errorMessage = Marshal.PtrToStringAnsi(pErrStr); + + throw new DllNotFoundException(errorMessage); + } + } + + // OSX and most Linux OS use LP64 so `int` is still 32-bit even on 64-bit platforms. + // void *dlopen(const char *filename, int flag); + [DllImport("libdl", EntryPoint = "dlopen")] + private static extern IntPtr DlOpen([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags); + + // char *dlerror(void); + [DllImport("libdl", EntryPoint = "dlerror")] + private static extern IntPtr DlError(); + + private const int RTLD_LAZY = 0x1; + private const int RTLD_NOW = 0x2; + private const int RTLD_GLOBAL = 0x100; + + } + + } +} diff --git a/AssetStudio.PInvoke/Properties/AssemblyInfo.cs b/AssetStudio.PInvoke/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a148c5d --- /dev/null +++ b/AssetStudio.PInvoke/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("AssetStudio.PInvoke")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AssetStudio.PInvoke")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("40c796b5-88ce-4adc-acd6-2f4862b7f136")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AssetStudio.PInvoke/Utf8StringHandle.cs b/AssetStudio.PInvoke/Utf8StringHandle.cs new file mode 100644 index 0000000..96a7719 --- /dev/null +++ b/AssetStudio.PInvoke/Utf8StringHandle.cs @@ -0,0 +1,100 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace AssetStudio.PInvoke +{ + // Generally the technique from Steamworks.NET + public class Utf8StringHandle : SafeHandleZeroOrMinusOneIsInvalid + { + + static Utf8StringHandle() + { + Utf8 = new UTF8Encoding(false); + } + + public Utf8StringHandle(string str) + : base(true) + { + IntPtr buffer; + + if (str == null) + { + buffer = IntPtr.Zero; + } + else + { + if (str.Length == 0) + { + buffer = Marshal.AllocHGlobal(1); + + unsafe + { + *(byte*)buffer = 0; + } + } + else + { + var strlen = Utf8.GetByteCount(str); + var strBuffer = new byte[strlen + 1]; + + Utf8.GetBytes(str, 0, str.Length, strBuffer, 0); + + buffer = Marshal.AllocHGlobal(strBuffer.Length); + + Marshal.Copy(strBuffer, 0, buffer, strBuffer.Length); + } + } + + SetHandle(buffer); + } + + public static string ReadUtf8StringFromPointer(IntPtr lpstr) + { + if (lpstr == IntPtr.Zero || lpstr == new IntPtr(-1)) + { + return null; + } + + var byteCount = 0; + + unsafe + { + var p = (byte*)lpstr.ToPointer(); + + while (*p != 0) + { + byteCount += 1; + p += 1; + } + } + + if (byteCount == 0) + { + return string.Empty; + } + + var strBuffer = new byte[byteCount]; + + Marshal.Copy(lpstr, strBuffer, 0, byteCount); + + var str = Utf8.GetString(strBuffer); + + return str; + } + + protected override bool ReleaseHandle() + { + if (!IsInvalid) + { + Marshal.FreeHGlobal(handle); + } + + return true; + } + + private static readonly UTF8Encoding Utf8; + + } +} diff --git a/AssetStudio.sln b/AssetStudio.sln index 8a212a9..6d799b0 100644 --- a/AssetStudio.sln +++ b/AssetStudio.sln @@ -5,62 +5,122 @@ VisualStudioVersion = 16.0.29920.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBX", "AssetStudioFBX\AssetStudioFBX.vcxproj", "{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoder", "Texture2DDecoder\Texture2DDecoder.vcxproj", "{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{80AEC261-21EE-4E4F-A93B-7A744DC84888}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioGUI", "AssetStudioGUI\AssetStudioGUI.csproj", "{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "AssetStudioFBXNative\AssetStudioFBXNative.vcxproj", "{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioFBXWrapper", "AssetStudioFBXWrapper\AssetStudioFBXWrapper.csproj", "{BD76E63F-1517-47FA-8233-33E853A3ACEE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Texture2DDecoderWrapper", "Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj", "{2AFCE830-B463-49B3-A026-877E5EAFC0A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio.PInvoke", "AssetStudio.PInvoke\AssetStudio.PInvoke.csproj", "{40C796B5-88CE-4ADC-ACD6-2F4862B7F136}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|Any CPU.Build.0 = Debug|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x64.ActiveCfg = Debug|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x64.Build.0 = Debug|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x86.ActiveCfg = Debug|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x86.Build.0 = Debug|Any CPU + {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|Any CPU.Build.0 = Release|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x64.ActiveCfg = Release|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x64.Build.0 = Release|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x86.ActiveCfg = Release|Any CPU {7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x86.Build.0 = Release|Any CPU - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x64.ActiveCfg = Debug|x64 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x64.Build.0 = Debug|x64 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x86.ActiveCfg = Debug|Win32 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x86.Build.0 = Debug|Win32 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x64.ActiveCfg = Release|x64 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x64.Build.0 = Release|x64 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x86.ActiveCfg = Release|Win32 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x86.Build.0 = Release|Win32 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x64.ActiveCfg = Debug|x64 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x64.Build.0 = Debug|x64 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x86.ActiveCfg = Debug|Win32 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x86.Build.0 = Debug|Win32 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x64.ActiveCfg = Release|x64 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x64.Build.0 = Release|x64 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x86.ActiveCfg = Release|Win32 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x86.Build.0 = Release|Win32 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.ActiveCfg = Debug|x64 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.Build.0 = Debug|x64 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.ActiveCfg = Debug|x86 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.Build.0 = Debug|x86 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.ActiveCfg = Release|x64 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.Build.0 = Release|x64 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.ActiveCfg = Release|x86 - {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.Build.0 = Release|x86 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.ActiveCfg = Debug|x64 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.Build.0 = Debug|x64 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.ActiveCfg = Debug|x86 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.Build.0 = Debug|x86 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.ActiveCfg = Release|x64 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.Build.0 = Release|x64 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.ActiveCfg = Release|x86 - {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.Build.0 = Release|x86 + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.ActiveCfg = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.Build.0 = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.ActiveCfg = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.Build.0 = Debug|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|Any CPU.Build.0 = Release|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.ActiveCfg = Release|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.Build.0 = Release|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.ActiveCfg = Release|Any CPU + {80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.Build.0 = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.ActiveCfg = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.Build.0 = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.ActiveCfg = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.Build.0 = Debug|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|Any CPU.Build.0 = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.ActiveCfg = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.Build.0 = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.ActiveCfg = Release|Any CPU + {52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.Build.0 = Release|Any CPU + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.ActiveCfg = Debug|x64 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.Build.0 = Debug|x64 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x86.ActiveCfg = Debug|Win32 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x86.Build.0 = Debug|Win32 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|Any CPU.ActiveCfg = Release|Win32 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.ActiveCfg = Release|x64 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.Build.0 = Release|x64 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.ActiveCfg = Release|Win32 + {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.Build.0 = Release|Win32 + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|x64.ActiveCfg = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|x64.Build.0 = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|x86.ActiveCfg = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Debug|x86.Build.0 = Debug|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|Any CPU.Build.0 = Release|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|x64.ActiveCfg = Release|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|x64.Build.0 = Release|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|x86.ActiveCfg = Release|Any CPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE}.Release|x86.Build.0 = Release|Any CPU + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.ActiveCfg = Debug|x64 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.Build.0 = Debug|x64 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.ActiveCfg = Debug|Win32 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.Build.0 = Debug|Win32 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.ActiveCfg = Release|Win32 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.ActiveCfg = Release|x64 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32 + {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32 + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|x64.Build.0 = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Debug|x86.Build.0 = Debug|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|Any CPU.Build.0 = Release|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|x64.ActiveCfg = Release|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|x64.Build.0 = Release|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|x86.ActiveCfg = Release|Any CPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4}.Release|x86.Build.0 = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|x64.ActiveCfg = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|x64.Build.0 = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|x86.ActiveCfg = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Debug|x86.Build.0 = Debug|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|Any CPU.Build.0 = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x64.ActiveCfg = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x64.Build.0 = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x86.ActiveCfg = Release|Any CPU + {40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AssetStudioFBX/AssemblyInfo.cpp b/AssetStudioFBX/AssemblyInfo.cpp deleted file mode 100644 index acdec3e..0000000 --- a/AssetStudioFBX/AssemblyInfo.cpp +++ /dev/null @@ -1,20 +0,0 @@ -using namespace System; -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; -using namespace System::Security::Permissions; - -[assembly:AssemblyTitleAttribute(L"AssetStudioFBX")]; -[assembly:AssemblyDescriptionAttribute(L"")]; -[assembly:AssemblyConfigurationAttribute(L"")]; -[assembly:AssemblyCompanyAttribute(L"")]; -[assembly:AssemblyProductAttribute(L"AssetStudioFBX")]; -[assembly:AssemblyCopyrightAttribute(L"Copyright © Perfare 2018-2020")]; -[assembly:AssemblyTrademarkAttribute(L"")]; -[assembly:AssemblyCultureAttribute(L"")]; - -[assembly:AssemblyVersionAttribute("1.0.*")]; - -[assembly:ComVisible(false)]; - -[assembly:CLSCompliantAttribute(true)]; \ No newline at end of file diff --git a/AssetStudioFBX/AssetStudioFBX.cpp b/AssetStudioFBX/AssetStudioFBX.cpp deleted file mode 100644 index 2d40576..0000000 --- a/AssetStudioFBX/AssetStudioFBX.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "AssetStudioFBX.h" - -namespace AssetStudio -{ - char* Fbx::StringToUTF8(String^ s) - { - auto bytes = Text::Encoding::UTF8->GetBytes(s); - auto chars = new char[bytes->Length + 1]; - pin_ptr ptr = &bytes[0]; - memcpy(chars, ptr, bytes->Length); - chars[bytes->Length] = '\0'; - return chars; - } - - void Fbx::Init(FbxManager** pSdkManager, FbxScene** pScene) - { - *pSdkManager = FbxManager::Create(); - if (!pSdkManager) - { - throw gcnew Exception(gcnew String("Unable to create the FBX SDK manager")); - } - - FbxIOSettings* ios = FbxIOSettings::Create(*pSdkManager, IOSROOT); - (*pSdkManager)->SetIOSettings(ios); - *pScene = FbxScene::Create(*pSdkManager, ""); - } - - Vector3 Fbx::QuaternionToEuler(Quaternion q) - { - FbxAMatrix lMatrixRot; - lMatrixRot.SetQ(FbxQuaternion(q.X, q.Y, q.Z, q.W)); - FbxVector4 lEuler = lMatrixRot.GetR(); - return Vector3((float)lEuler[0], (float)lEuler[1], (float)lEuler[2]); - } - - Quaternion Fbx::EulerToQuaternion(Vector3 v) - { - FbxAMatrix lMatrixRot; - lMatrixRot.SetR(FbxVector4(v.X, v.Y, v.Z)); - FbxQuaternion lQuaternion = lMatrixRot.GetQ(); - return Quaternion((float)lQuaternion[0], (float)lQuaternion[1], (float)lQuaternion[2], (float)lQuaternion[3]); - } -} \ No newline at end of file diff --git a/AssetStudioFBX/AssetStudioFBX.h b/AssetStudioFBX/AssetStudioFBX.h deleted file mode 100644 index 793da5d..0000000 --- a/AssetStudioFBX/AssetStudioFBX.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include - -#ifdef IOS_REF -#undef IOS_REF -#define IOS_REF (*(pSdkManager->GetIOSettings())) -#endif - -using namespace System; -using namespace System::Collections::Generic; -using namespace System::IO; - -#define WITH_MARSHALLED_STRING(name,str,block)\ - { \ - char* name; \ - try \ - { \ - name = StringToUTF8(str); \ - block \ - } \ - finally \ - { \ - delete name; \ - } \ - } - -static char* FBXVersion[] = -{ - FBX_2010_00_COMPATIBLE, - FBX_2011_00_COMPATIBLE, - FBX_2012_00_COMPATIBLE, - FBX_2013_00_COMPATIBLE, - FBX_2014_00_COMPATIBLE, - FBX_2016_00_COMPATIBLE -}; - -namespace AssetStudio { - - public ref class Fbx - { - public: - static Vector3 QuaternionToEuler(Quaternion q); - static Quaternion EulerToQuaternion(Vector3 v); - static char* StringToUTF8(String^ s); - static void Init(FbxManager** pSdkManager, FbxScene** pScene); - - ref class Exporter - { - public: - static void Export(String^ path, IImported^ imported, bool eulerFilter, float filterPrecision, - bool allNodes, bool skins, bool animation, bool blendShape, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii); - - private: - bool exportSkins; - float boneSize; - IImported^ imported; - HashSet^ framePaths; - Dictionary^ frameToNode; - List^ meshFrames; - - char* cDest; - FbxManager* pSdkManager; - FbxScene* pScene; - FbxExporter* pExporter; - FbxArray* pMaterials; - FbxArray* pTextures; - FbxPose* pBindPose; - - Exporter(String^ name, IImported^ imported, bool allNodes, bool skins, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii); - ~Exporter(); - - void Exporter::LinkTexture(ImportedMaterialTexture^ texture, FbxFileTexture* pTexture, FbxProperty& prop); - void SetJointsNode(ImportedFrame^ frame, HashSet^ bonePaths, bool allBones); - HashSet^ SearchHierarchy(); - void SearchHierarchy(ImportedFrame^ frame, HashSet^ exportFrames); - void SetJointsFromImportedMeshes(bool allBones); - void ExportFrame(FbxNode* pParentNode, ImportedFrame^ frame); - void ExportMesh(FbxNode* pFrameNode, ImportedMesh^ iMesh); - FbxFileTexture* ExportTexture(ImportedTexture^ matTex); - void ExportAnimations(bool eulerFilter, float filterValue); - void ExportKeyframedAnimation(ImportedKeyframedAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* eulerFilter, float filterPrecision); - void ExportMorphs(); - }; - }; -} diff --git a/AssetStudioFBX/AssetStudioFBX.vcxproj.filters b/AssetStudioFBX/AssetStudioFBX.vcxproj.filters deleted file mode 100644 index c2abcdc..0000000 --- a/AssetStudioFBX/AssetStudioFBX.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - 头文件 - - - - - 源文件 - - - 源文件 - - - 源文件 - - - \ No newline at end of file diff --git a/AssetStudioFBX/AssetStudioFBXExporter.cpp b/AssetStudioFBX/AssetStudioFBXExporter.cpp deleted file mode 100644 index 951fd2b..0000000 --- a/AssetStudioFBX/AssetStudioFBXExporter.cpp +++ /dev/null @@ -1,915 +0,0 @@ -#include "AssetStudioFBX.h" - -namespace AssetStudio -{ - void Fbx::Exporter::Export(String^ path, IImported^ imported, bool eulerFilter, float filterPrecision, - bool allNodes, bool skins, bool animation, bool blendShape, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii) - { - FileInfo^ file = gcnew FileInfo(path); - DirectoryInfo^ dir = file->Directory; - if (!dir->Exists) - { - dir->Create(); - } - String^ currentDir = Directory::GetCurrentDirectory(); - Directory::SetCurrentDirectory(dir->FullName); - auto name = Path::GetFileName(path); - Exporter^ exporter = gcnew Exporter(name, imported, allNodes, skins, castToBone, boneSize, scaleFactor, versionIndex, isAscii); - if (blendShape) - { - exporter->ExportMorphs(); - } - if (animation) - { - exporter->ExportAnimations(eulerFilter, filterPrecision); - } - exporter->pExporter->Export(exporter->pScene); - delete exporter; - - Directory::SetCurrentDirectory(currentDir); - } - - Fbx::Exporter::Exporter(String^ name, IImported^ imported, bool allNodes, bool skins, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii) - { - this->imported = imported; - exportSkins = skins; - this->boneSize = boneSize; - - cDest = NULL; - pSdkManager = NULL; - pScene = NULL; - pExporter = NULL; - pMaterials = NULL; - pTextures = NULL; - - pin_ptr pSdkManagerPin = &pSdkManager; - pin_ptr pScenePin = &pScene; - Init(pSdkManagerPin, pScenePin); - - IOS_REF.SetBoolProp(EXP_FBX_MATERIAL, true); - IOS_REF.SetBoolProp(EXP_FBX_TEXTURE, true); - IOS_REF.SetBoolProp(EXP_FBX_EMBEDDED, false); - IOS_REF.SetBoolProp(EXP_FBX_SHAPE, true); - IOS_REF.SetBoolProp(EXP_FBX_GOBO, true); - IOS_REF.SetBoolProp(EXP_FBX_ANIMATION, true); - IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true); - - FbxGlobalSettings& globalSettings = pScene->GetGlobalSettings(); - globalSettings.SetSystemUnit(FbxSystemUnit(scaleFactor)); - - if (imported->AnimationList->Count > 0) - { - auto ani = imported->AnimationList[0]; - if (ani->SampleRate == 60.0f) - { - globalSettings.SetTimeMode(FbxTime::eFrames60); - } - } - - cDest = StringToUTF8(name); - pExporter = FbxExporter::Create(pScene, ""); - - int pFileFormat = 0; - if (versionIndex == 0) - { - pFileFormat = 3; - if (isAscii) - { - pFileFormat = 4; - } - } - else - { - pExporter->SetFileExportVersion(FBXVersion[versionIndex]); - if (isAscii) - { - pFileFormat = 1; - } - } - - if (!pExporter->Initialize(cDest, pFileFormat, pSdkManager->GetIOSettings())) - { - throw gcnew Exception(gcnew String("Failed to initialize FbxExporter: ") + gcnew String(pExporter->GetStatus().GetErrorString())); - } - - framePaths = nullptr; - if (!allNodes) - { - framePaths = SearchHierarchy(); - if (!framePaths) - { - return; - } - } - - pBindPose = FbxPose::Create(pScene, "BindPose"); - pScene->AddPose(pBindPose); - - frameToNode = gcnew Dictionary(); - meshFrames = imported->MeshList != nullptr ? gcnew List() : nullptr; - ExportFrame(pScene->GetRootNode(), imported->RootFrame); - - if (imported->MeshList != nullptr) - { - SetJointsFromImportedMeshes(castToBone); - - pMaterials = new FbxArray(); - pTextures = new FbxArray(); - pMaterials->Reserve(imported->MaterialList->Count); - pTextures->Reserve(imported->TextureList->Count); - - for (int i = 0; i < meshFrames->Count; i++) - { - auto meshFram = meshFrames[i]; - FbxNode* meshNode = (FbxNode*)frameToNode[meshFram]; - ImportedMesh^ mesh = ImportedHelpers::FindMesh(meshFram->Path, imported->MeshList); - ExportMesh(meshNode, mesh); - } - } - else - { - SetJointsNode(imported->RootFrame, nullptr, true); - } - } - - Fbx::Exporter::~Exporter() - { - imported = nullptr; - if (framePaths != nullptr) - { - framePaths->Clear(); - } - if (frameToNode != nullptr) - { - frameToNode->Clear(); - } - if (meshFrames != nullptr) - { - meshFrames->Clear(); - } - - if (pMaterials != NULL) - { - delete pMaterials; - } - if (pTextures != NULL) - { - delete pTextures; - } - if (pExporter != NULL) - { - pExporter->Destroy(); - } - if (pScene != NULL) - { - pScene->Destroy(); - } - if (pSdkManager != NULL) - { - pSdkManager->Destroy(); - } - if (cDest != NULL) - { - delete cDest; - } - } - - void Fbx::Exporter::SetJointsNode(ImportedFrame^ frame, HashSet^ bonePaths, bool castToBone) - { - size_t pointer; - if (frameToNode->TryGetValue(frame, pointer)) - { - auto pNode = (FbxNode*)pointer; - if (castToBone) - { - FbxSkeleton* pJoint = FbxSkeleton::Create(pScene, ""); - pJoint->Size.Set(FbxDouble(boneSize)); - pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); - pNode->SetNodeAttribute(pJoint); - } - else if (bonePaths->Contains(frame->Path)) - { - FbxSkeleton* pJoint = FbxSkeleton::Create(pScene, ""); - pJoint->Size.Set(FbxDouble(boneSize)); - pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); - pNode->SetNodeAttribute(pJoint); - - pJoint = FbxSkeleton::Create(pScene, ""); - pJoint->Size.Set(FbxDouble(boneSize)); - pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); - pNode->GetParent()->SetNodeAttribute(pJoint); - } - else - { - FbxNull* pNull = FbxNull::Create(pScene, ""); - if (pNode->GetChildCount() > 0) - { - pNull->Look.Set(FbxNull::eNone); - } - - pNode->SetNodeAttribute(pNull); - } - } - for (int i = 0; i < frame->Count; i++) - { - SetJointsNode(frame[i], bonePaths, castToBone); - } - } - - HashSet^ Fbx::Exporter::SearchHierarchy() - { - if (imported->MeshList == nullptr || imported->MeshList->Count == 0) - { - return nullptr; - } - HashSet^ exportFrames = gcnew HashSet(); - SearchHierarchy(imported->RootFrame, exportFrames); - return exportFrames; - } - - void Fbx::Exporter::SearchHierarchy(ImportedFrame^ frame, HashSet^ exportFrames) - { - ImportedMesh^ meshListSome = ImportedHelpers::FindMesh(frame->Path, imported->MeshList); - if (meshListSome != nullptr) - { - ImportedFrame^ parent = frame; - while (parent != nullptr) - { - exportFrames->Add(parent->Path); - parent = parent->Parent; - } - - List^ boneList = meshListSome->BoneList; - if (boneList != nullptr) - { - for (int i = 0; i < boneList->Count; i++) - { - if (!exportFrames->Contains(boneList[i]->Path)) - { - ImportedFrame^ boneParent = imported->RootFrame->FindFrameByPath(boneList[i]->Path); - while (boneParent != nullptr) - { - exportFrames->Add(boneParent->Path); - boneParent = boneParent->Parent; - } - } - } - } - } - - for (int i = 0; i < frame->Count; i++) - { - SearchHierarchy(frame[i], exportFrames); - } - } - - void Fbx::Exporter::SetJointsFromImportedMeshes(bool castToBone) - { - if (!exportSkins) - { - return; - } - HashSet^ bonePaths = gcnew HashSet(); - for (int i = 0; i < imported->MeshList->Count; i++) - { - ImportedMesh^ meshList = imported->MeshList[i]; - List^ boneList = meshList->BoneList; - if (boneList != nullptr) - { - for (int j = 0; j < boneList->Count; j++) - { - ImportedBone^ bone = boneList[j]; - bonePaths->Add(bone->Path); - } - } - } - - SetJointsNode(imported->RootFrame, bonePaths, castToBone); - } - - void Fbx::Exporter::ExportFrame(FbxNode* pParentNode, ImportedFrame^ frame) - { - if (framePaths == nullptr || framePaths->Contains(frame->Path)) - { - FbxNode* pFrameNode; - WITH_MARSHALLED_STRING - ( - pName, - frame->Name, - pFrameNode = FbxNode::Create(pScene, pName); - ); - - pFrameNode->LclScaling.Set(FbxDouble3(frame->LocalScale.X, frame->LocalScale.Y, frame->LocalScale.Z)); - pFrameNode->LclRotation.Set(FbxDouble3(frame->LocalRotation.X, frame->LocalRotation.Y, frame->LocalRotation.Z)); - pFrameNode->LclTranslation.Set(FbxDouble3(frame->LocalPosition.X, frame->LocalPosition.Y, frame->LocalPosition.Z)); - pFrameNode->SetPreferedAngle(pFrameNode->LclRotation.Get()); - pParentNode->AddChild(pFrameNode); - pBindPose->Add(pFrameNode, pFrameNode->EvaluateGlobalTransform()); - - if (imported->MeshList != nullptr && ImportedHelpers::FindMesh(frame->Path, imported->MeshList) != nullptr) - { - meshFrames->Add(frame); - } - - frameToNode->Add(frame, (size_t)pFrameNode); - - for (int i = 0; i < frame->Count; i++) - { - ExportFrame(pFrameNode, frame[i]); - } - } - } - - void Fbx::Exporter::ExportMesh(FbxNode* pFrameNode, ImportedMesh^ iMesh) - { - List^ boneList = iMesh->BoneList; - bool hasBones; - if (exportSkins && boneList != nullptr) - { - hasBones = boneList->Count > 0; - } - else - { - hasBones = false; - } - - FbxArray* pClusterArray = nullptr; - - try - { - if (hasBones) - { - pClusterArray = new FbxArray(boneList->Count); - - for (int i = 0; i < boneList->Count; i++) - { - auto bone = boneList[i]; - if (bone->Path != nullptr) - { - auto frame = imported->RootFrame->FindFrameByPath(bone->Path); - auto boneNode = (FbxNode*)frameToNode[frame]; - FbxString lClusterName = boneNode->GetNameOnly() + FbxString("Cluster"); - FbxCluster* pCluster = FbxCluster::Create(pScene, lClusterName.Buffer()); - pCluster->SetLink(boneNode); - pCluster->SetLinkMode(FbxCluster::eTotalOne); - pClusterArray->Add(pCluster); - } - else - { - pClusterArray->Add(NULL); - } - } - } - - FbxMesh* pMesh = FbxMesh::Create(pScene, pFrameNode->GetName()); - pFrameNode->SetNodeAttribute(pMesh); - - int vertexCount = 0; - for (int i = 0; i < iMesh->SubmeshList->Count; i++) - { - vertexCount += iMesh->SubmeshList[i]->VertexList->Count; - } - - pMesh->InitControlPoints(vertexCount); - FbxVector4* pControlPoints = pMesh->GetControlPoints(); - - FbxGeometryElementNormal* lGeometryElementNormal = NULL; - if (iMesh->hasNormal) - { - lGeometryElementNormal = pMesh->CreateElementNormal(); - lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect); - } - - if (iMesh->hasUV[0]) - { - auto lGeometryElementUV = pMesh->CreateElementUV("UV0", FbxLayerElement::eTextureDiffuse); - lGeometryElementUV->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementUV->SetReferenceMode(FbxGeometryElement::eDirect); - } - - if (iMesh->hasUV[1]) - { - auto lGeometryElementUV = pMesh->CreateElementUV("UV1", FbxLayerElement::eTextureNormalMap); - lGeometryElementUV->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementUV->SetReferenceMode(FbxGeometryElement::eDirect); - } - - /*for (int uv = 0; uv < 8; uv++) - { - if (iMesh->hasUV[uv]) - { - auto lGeometryElementUV = pMesh->CreateElementUV(FbxString("UV") + FbxString(uv)); - lGeometryElementUV->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementUV->SetReferenceMode(FbxGeometryElement::eDirect); - } - }*/ - - FbxGeometryElementTangent* lGeometryElementTangent = NULL; - if (iMesh->hasTangent) - { - lGeometryElementTangent = pMesh->CreateElementTangent(); - lGeometryElementTangent->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementTangent->SetReferenceMode(FbxGeometryElement::eDirect); - } - - FbxGeometryElementVertexColor* lGeometryElementVertexColor = NULL; - if (iMesh->hasColor) - { - lGeometryElementVertexColor = pMesh->CreateElementVertexColor(); - lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); - } - - FbxGeometryElementMaterial* lGeometryElementMaterial = pMesh->CreateElementMaterial(); - lGeometryElementMaterial->SetMappingMode(FbxGeometryElement::eByPolygon); - lGeometryElementMaterial->SetReferenceMode(FbxGeometryElement::eIndexToDirect); - - int firstVertex = 0; - for (int i = 0; i < iMesh->SubmeshList->Count; i++) - { - ImportedSubmesh^ meshObj = iMesh->SubmeshList[i]; - List^ vertexList = meshObj->VertexList; - List^ faceList = meshObj->FaceList; - - int materialIndex = 0; - ImportedMaterial^ mat = ImportedHelpers::FindMaterial(meshObj->Material, imported->MaterialList); - if (mat != nullptr) - { - char* pMatName = NULL; - try - { - pMatName = StringToUTF8(mat->Name); - int foundMat = -1; - for (int j = 0; j < pMaterials->GetCount(); j++) - { - FbxSurfacePhong* pMatTemp = pMaterials->GetAt(j); - if (strcmp(pMatTemp->GetName(), pMatName) == 0) - { - foundMat = j; - break; - } - } - - FbxSurfacePhong* pMat; - if (foundMat >= 0) - { - pMat = pMaterials->GetAt(foundMat); - } - else - { - FbxString lShadingName = "Phong"; - Color diffuse = mat->Diffuse; - Color ambient = mat->Ambient; - Color emissive = mat->Emissive; - Color specular = mat->Specular; - Color reflection = mat->Reflection; - pMat = FbxSurfacePhong::Create(pScene, pMatName); - pMat->Diffuse.Set(FbxDouble3(diffuse.R, diffuse.G, diffuse.B)); - //pMat->DiffuseFactor.Set(FbxDouble(diffuse.A)); - pMat->Ambient.Set(FbxDouble3(ambient.R, ambient.G, ambient.B)); - //pMat->AmbientFactor.Set(FbxDouble(ambient.A)); - pMat->Emissive.Set(FbxDouble3(emissive.R, emissive.G, emissive.B)); - //pMat->EmissiveFactor.Set(FbxDouble(emissive.A)); - pMat->Specular.Set(FbxDouble3(specular.R, specular.G, specular.B)); - //pMat->SpecularFactor.Set(FbxDouble(specular.A)); - pMat->Reflection.Set(FbxDouble3(reflection.R, reflection.G, reflection.B)); - //pMat->ReflectionFactor.Set(FbxDouble(reflection.A)); - pMat->Shininess.Set(FbxDouble(mat->Shininess)); - pMat->TransparencyFactor.Set(FbxDouble(mat->Transparency)); - pMat->ShadingModel.Set(lShadingName); - pMaterials->Add(pMat); - } - materialIndex = pFrameNode->AddMaterial(pMat); - - bool hasTexture = false; - - for each (ImportedMaterialTexture ^ texture in mat->Textures) - { - auto pTexture = ExportTexture(ImportedHelpers::FindTexture(texture->Name, imported->TextureList)); - if (pTexture != NULL) - { - if (texture->Dest == 0) - { - LinkTexture(texture, pTexture, pMat->Diffuse); - hasTexture = true; - } - else if (texture->Dest == 1) - { - LinkTexture(texture, pTexture, pMat->NormalMap); - hasTexture = true; - } - else if (texture->Dest == 2) - { - LinkTexture(texture, pTexture, pMat->Specular); - hasTexture = true; - } - else if (texture->Dest == 3) - { - LinkTexture(texture, pTexture, pMat->Bump); - hasTexture = true; - } - } - } - - if (hasTexture) - { - pFrameNode->SetShadingMode(FbxNode::eTextureShading); - } - } - finally - { - delete pMatName; - } - } - - for (int j = 0; j < vertexList->Count; j++) - { - ImportedVertex^ iVertex = vertexList[j]; - - Vector3 vertex = iVertex->Vertex; - pControlPoints[j + firstVertex] = FbxVector4(vertex.X, vertex.Y, vertex.Z, 0); - - if (iMesh->hasNormal) - { - Vector3 normal = iVertex->Normal; - lGeometryElementNormal->GetDirectArray().Add(FbxVector4(normal.X, normal.Y, normal.Z, 0)); - } - - //for (int uv = 0; uv < 8; uv++) - for (int uv = 0; uv < 2; uv++) - { - if (iMesh->hasUV[uv]) - { - auto m_UV = iVertex->UV[uv]; - auto lGeometryElementUV = pMesh->GetElementUV(uv); - lGeometryElementUV->GetDirectArray().Add(FbxVector2(m_UV[0], m_UV[1])); - } - } - - if (iMesh->hasTangent) - { - Vector4 tangent = iVertex->Tangent; - lGeometryElementTangent->GetDirectArray().Add(FbxVector4(tangent.X, tangent.Y, tangent.Z, tangent.W)); - } - - if (iMesh->hasColor) - { - auto color = iVertex->Color; - lGeometryElementVertexColor->GetDirectArray().Add(FbxColor(color.R, color.G, color.B, color.A)); - } - - if (hasBones && iVertex->BoneIndices != nullptr) - { - auto boneIndices = iVertex->BoneIndices; - auto weights4 = iVertex->Weights; - for (int k = 0; k < 4; k++) - { - if (boneIndices[k] < boneList->Count && weights4[k] > 0) - { - FbxCluster* pCluster = pClusterArray->GetAt(boneIndices[k]); - if (pCluster) - { - pCluster->AddControlPointIndex(j + firstVertex, weights4[k]); - } - } - } - } - } - - for (int j = 0; j < faceList->Count; j++) - { - ImportedFace^ face = faceList[j]; - pMesh->BeginPolygon(materialIndex); - pMesh->AddPolygon(face->VertexIndices[0] + firstVertex); - pMesh->AddPolygon(face->VertexIndices[1] + firstVertex); - pMesh->AddPolygon(face->VertexIndices[2] + firstVertex); - pMesh->EndPolygon(); - } - - firstVertex += vertexList->Count; - } - - if (hasBones) - { - FbxSkin* pSkin = FbxSkin::Create(pScene, ""); - FbxAMatrix lMeshMatrix = pFrameNode->EvaluateGlobalTransform(); - for (int j = 0; j < boneList->Count; j++) - { - FbxCluster* pCluster = pClusterArray->GetAt(j); - if (pCluster) - { - auto boneMatrix = boneList[j]->Matrix; - FbxAMatrix lBoneMatrix; - for (int m = 0; m < 4; m++) - { - for (int n = 0; n < 4; n++) - { - lBoneMatrix.mData[m][n] = boneMatrix[m, n]; - } - } - - pCluster->SetTransformMatrix(lMeshMatrix); - pCluster->SetTransformLinkMatrix(lMeshMatrix * lBoneMatrix.Inverse()); - - pSkin->AddCluster(pCluster); - } - } - - if (pSkin->GetClusterCount() > 0) - { - pMesh->AddDeformer(pSkin); - } - } - } - finally - { - if (pClusterArray != NULL) - { - delete pClusterArray; - } - } - } - - FbxFileTexture* Fbx::Exporter::ExportTexture(ImportedTexture^ matTex) - { - FbxFileTexture* pTex = NULL; - - if (matTex != nullptr) - { - String^ matTexName = matTex->Name; - char* pTexName = NULL; - try - { - pTexName = StringToUTF8(matTexName); - int foundTex = -1; - for (int i = 0; i < pTextures->GetCount(); i++) - { - FbxFileTexture* pTexTemp = pTextures->GetAt(i); - if (strcmp(pTexTemp->GetName(), pTexName) == 0) - { - foundTex = i; - break; - } - } - - if (foundTex >= 0) - { - pTex = pTextures->GetAt(foundTex); - } - else - { - pTex = FbxFileTexture::Create(pScene, pTexName); - pTex->SetFileName(pTexName); - pTex->SetTextureUse(FbxTexture::eStandard); - pTex->SetMappingType(FbxTexture::eUV); - pTex->SetMaterialUse(FbxFileTexture::eModelMaterial); - pTex->SetSwapUV(false); - pTex->SetTranslation(0.0, 0.0); - pTex->SetScale(1.0, 1.0); - pTex->SetRotation(0.0, 0.0); - pTextures->Add(pTex); - - FileInfo^ file = gcnew FileInfo(matTex->Name); - BinaryWriter^ writer = gcnew BinaryWriter(file->Create()); - writer->Write(matTex->Data); - writer->Close(); - } - } - finally - { - delete pTexName; - } - } - - return pTex; - } - - void Fbx::Exporter::LinkTexture(ImportedMaterialTexture^ texture, FbxFileTexture* pTexture, FbxProperty& prop) - { - pTexture->SetTranslation(texture->Offset.X, texture->Offset.Y); - pTexture->SetScale(texture->Scale.X, texture->Scale.Y); - prop.ConnectSrcObject(pTexture); - } - - void Fbx::Exporter::ExportAnimations(bool eulerFilter, float filterPrecision) - { - auto importedAnimationList = imported->AnimationList; - if (importedAnimationList == nullptr) - { - return; - } - - FbxAnimCurveFilterUnroll* lFilter = eulerFilter ? new FbxAnimCurveFilterUnroll() : NULL; - - for (int i = 0; i < importedAnimationList->Count; i++) - { - auto importedAnimation = importedAnimationList[i]; - FbxString kTakeName; - if (importedAnimation->Name) - { - WITH_MARSHALLED_STRING - ( - pClipName, - importedAnimation->Name, - kTakeName = FbxString(pClipName); - ); - } - else - { - kTakeName = FbxString("Take") + FbxString(i); - } - ExportKeyframedAnimation(importedAnimation, kTakeName, lFilter, filterPrecision); - } - } - - void Fbx::Exporter::ExportKeyframedAnimation(ImportedKeyframedAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* eulerFilter, float filterPrecision) - { - List^ pAnimationList = parser->TrackList; - - char* lTakeName = kTakeName.Buffer(); - - FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, lTakeName); - FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer"); - lAnimStack->AddMember(lAnimLayer); - - for (int j = 0; j < pAnimationList->Count; j++) - { - ImportedAnimationKeyframedTrack^ keyframeList = pAnimationList[j]; - if (keyframeList->Path == nullptr) - { - continue; - } - auto frame = imported->RootFrame->FindFrameByPath(keyframeList->Path); - if (frame != nullptr) - { - FbxNode* pNode = (FbxNode*)frameToNode[frame]; - - FbxAnimCurve* lCurveSX = pNode->LclScaling.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); - FbxAnimCurve* lCurveSY = pNode->LclScaling.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); - FbxAnimCurve* lCurveSZ = pNode->LclScaling.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); - FbxAnimCurve* lCurveRX = pNode->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); - FbxAnimCurve* lCurveRY = pNode->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); - FbxAnimCurve* lCurveRZ = pNode->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); - FbxAnimCurve* lCurveTX = pNode->LclTranslation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); - FbxAnimCurve* lCurveTY = pNode->LclTranslation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); - FbxAnimCurve* lCurveTZ = pNode->LclTranslation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); - - lCurveSX->KeyModifyBegin(); - lCurveSY->KeyModifyBegin(); - lCurveSZ->KeyModifyBegin(); - lCurveRX->KeyModifyBegin(); - lCurveRY->KeyModifyBegin(); - lCurveRZ->KeyModifyBegin(); - lCurveTX->KeyModifyBegin(); - lCurveTY->KeyModifyBegin(); - lCurveTZ->KeyModifyBegin(); - - FbxTime lTime; - - for each (auto Scaling in keyframeList->Scalings) - { - lTime.SetSecondDouble(Scaling->time); - - lCurveSX->KeySet(lCurveSX->KeyAdd(lTime), lTime, Scaling->value.X); - lCurveSY->KeySet(lCurveSY->KeyAdd(lTime), lTime, Scaling->value.Y); - lCurveSZ->KeySet(lCurveSZ->KeyAdd(lTime), lTime, Scaling->value.Z); - } - for each (auto Rotation in keyframeList->Rotations) - { - lTime.SetSecondDouble(Rotation->time); - - lCurveRX->KeySet(lCurveRX->KeyAdd(lTime), lTime, Rotation->value.X); - lCurveRY->KeySet(lCurveRY->KeyAdd(lTime), lTime, Rotation->value.Y); - lCurveRZ->KeySet(lCurveRZ->KeyAdd(lTime), lTime, Rotation->value.Z); - } - for each (auto Translation in keyframeList->Translations) - { - lTime.SetSecondDouble(Translation->time); - - lCurveTX->KeySet(lCurveTX->KeyAdd(lTime), lTime, Translation->value.X); - lCurveTY->KeySet(lCurveTY->KeyAdd(lTime), lTime, Translation->value.Y); - lCurveTZ->KeySet(lCurveTZ->KeyAdd(lTime), lTime, Translation->value.Z); - } - - lCurveSX->KeyModifyEnd(); - lCurveSY->KeyModifyEnd(); - lCurveSZ->KeyModifyEnd(); - lCurveRX->KeyModifyEnd(); - lCurveRY->KeyModifyEnd(); - lCurveRZ->KeyModifyEnd(); - lCurveTX->KeyModifyEnd(); - lCurveTY->KeyModifyEnd(); - lCurveTZ->KeyModifyEnd(); - - if (eulerFilter) - { - FbxAnimCurve* lCurve[3]; - lCurve[0] = lCurveRX; - lCurve[1] = lCurveRY; - lCurve[2] = lCurveRZ; - eulerFilter->Reset(); - eulerFilter->SetQualityTolerance(filterPrecision); - eulerFilter->Apply(lCurve, 3); - } - - //BlendShape - if (keyframeList->BlendShape != nullptr) - { - FbxString channelName; - WITH_MARSHALLED_STRING - ( - pClipName, - keyframeList->BlendShape->ChannelName, - channelName = FbxString(pClipName); - ); - - auto lGeometry = (FbxGeometry*)pNode->GetNodeAttribute(); - int lBlendShapeDeformerCount = lGeometry->GetDeformerCount(FbxDeformer::eBlendShape); - if (lBlendShapeDeformerCount > 0) - { - FbxBlendShape* lBlendShape = (FbxBlendShape*)lGeometry->GetDeformer(0, FbxDeformer::eBlendShape); - int lBlendShapeChannelCount = lBlendShape->GetBlendShapeChannelCount(); - for (int lChannelIndex = 0; lChannelIndex < lBlendShapeChannelCount; ++lChannelIndex) - { - FbxBlendShapeChannel* lChannel = lBlendShape->GetBlendShapeChannel(lChannelIndex); - FbxString lChannelName = lChannel->GetNameOnly(); - if (lChannelName == channelName) - { - FbxAnimCurve* lAnimCurve = lGeometry->GetShapeChannel(0, lChannelIndex, lAnimLayer, true); - lAnimCurve->KeyModifyBegin(); - - for each (auto keyframe in keyframeList->BlendShape->Keyframes) - { - lTime.SetSecondDouble(keyframe->time); - int lKeyIndex = lAnimCurve->KeyAdd(lTime); - lAnimCurve->KeySetValue(lKeyIndex, keyframe->value); - lAnimCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic); - } - - lAnimCurve->KeyModifyEnd(); - } - } - } - } - } - } - } - - void Fbx::Exporter::ExportMorphs() - { - if (imported->MeshList == nullptr) - { - return; - } - for each (ImportedMorph ^ morph in imported->MorphList) - { - auto frame = imported->RootFrame->FindFrameByPath(morph->Path); - if (frame != nullptr) - { - FbxNode* pNode = (FbxNode*)frameToNode[frame]; - FbxMesh* pMesh = pNode->GetMesh(); - - FbxBlendShape* lBlendShape = FbxBlendShape::Create(pScene, pMesh->GetNameOnly() + FbxString("BlendShape")); - pMesh->AddDeformer(lBlendShape); - - for (int i = 0; i < morph->Channels->Count; i++) - { - auto channel = morph->Channels[i]; - - FbxBlendShapeChannel* lBlendShapeChannel; - WITH_MARSHALLED_STRING - ( - pChannelName, - channel->Name, - lBlendShapeChannel = FbxBlendShapeChannel::Create(pScene, pChannelName); - ); - lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); - - for each (ImportedMorphKeyframe ^ keyframe in channel->KeyframeList) - { - FbxShape* lShape = FbxShape::Create(pScene, FbxString(keyframe->Weight)); - lBlendShapeChannel->AddTargetShape(lShape, keyframe->Weight); - - auto vectorCount = pMesh->GetControlPointsCount(); - FbxVector4* orilVector4 = pMesh->GetControlPoints(); - lShape->InitControlPoints(vectorCount); - FbxVector4* lVector4 = lShape->GetControlPoints(); - - for (int j = 0; j < vectorCount; j++) - { - auto vertex = orilVector4[j]; - lVector4[j] = FbxVector4(vertex); - } - for (int j = 0; j < keyframe->VertexList->Count; j++) - { - auto index = keyframe->VertexList[j]->Index; - auto vertex = keyframe->VertexList[j]->Vertex->Vertex; - lVector4[index] = FbxVector4(vertex.X, vertex.Y, vertex.Z, 0); - } - } - } - } - } - } -} \ No newline at end of file diff --git a/AssetStudioFBXNative/AssetStudioFBXNative.rc b/AssetStudioFBXNative/AssetStudioFBXNative.rc new file mode 100644 index 0000000..4e5e5b3 --- /dev/null +++ b/AssetStudioFBXNative/AssetStudioFBXNative.rc @@ -0,0 +1,99 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Language neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL +#pragma code_page(65001) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "FileDescription", "AssetStudioFBXNative" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "AssetStudioFBXNative.dll" + VALUE "LegalCopyright", "Copyright (C) Perfare 2018-2020; Copyright (C) hozuki 2020" + VALUE "OriginalFilename", "AssetStudioFBXNative.dll" + VALUE "ProductName", "AssetStudioFBXNative" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // Language neutral resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/AssetStudioFBX/AssetStudioFBX.vcxproj b/AssetStudioFBXNative/AssetStudioFBXNative.vcxproj similarity index 62% rename from AssetStudioFBX/AssetStudioFBX.vcxproj rename to AssetStudioFBXNative/AssetStudioFBXNative.vcxproj index 7248d4d..41f7e2c 100644 --- a/AssetStudioFBX/AssetStudioFBX.vcxproj +++ b/AssetStudioFBXNative/AssetStudioFBXNative.vcxproj @@ -20,10 +20,9 @@ 16.0 - {B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8} - v4.7.2 - ManagedCProj - AssetStudioFBX + Win32Proj + {11ea25a3-ed68-40ee-a9d0-7fde3b583027} + AssetStudioFBXNative 10.0.18362.0 @@ -31,28 +30,26 @@ DynamicLibrary true v141 - true Unicode DynamicLibrary false v141 - true + true Unicode DynamicLibrary true v141 - true Unicode DynamicLibrary false v141 - true + true Unicode @@ -73,14 +70,60 @@ - + + true + .dll + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + .dll + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + true + .dll + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + .dll + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + Level3 - FBXSDK_SHARED;WIN32;_DEBUG;%(PreprocessorDefinitions) + true + _AS_DLL;FBXSDK_SHARED;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories) + Console + true + libfbxsdk.lib;%(AdditionalDependencies) + C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x86\release;%(AdditionalLibraryDirectories) + + + + + Level3 + true + true + true + _AS_DLL;FBXSDK_SHARED;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true libfbxsdk.lib;%(AdditionalDependencies) C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x86\release;%(AdditionalLibraryDirectories) @@ -88,52 +131,61 @@ Level3 - FBXSDK_SHARED;_DEBUG;%(PreprocessorDefinitions) + true + _AS_DLL;FBXSDK_SHARED;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories) + Console + true libfbxsdk.lib;%(AdditionalDependencies) C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x64\release;%(AdditionalLibraryDirectories) - - - Level3 - FBXSDK_SHARED;WIN32;NDEBUG;%(PreprocessorDefinitions) - C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories) - - - libfbxsdk.lib;%(AdditionalDependencies) - C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x86\release;%(AdditionalLibraryDirectories) - - Level3 - FBXSDK_SHARED;NDEBUG;%(PreprocessorDefinitions) + true + true + true + _AS_DLL;FBXSDK_SHARED;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories) + Console + true + true + true libfbxsdk.lib;%(AdditionalDependencies) C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x64\release;%(AdditionalLibraryDirectories) - + + + + + + - - - + + + + + + + + + - - + - - {7662f8c2-7bfd-442e-a948-a43b4f7eb06e} - + diff --git a/AssetStudioFBXNative/AssetStudioFBXNative.vcxproj.filters b/AssetStudioFBXNative/AssetStudioFBXNative.vcxproj.filters new file mode 100644 index 0000000..5c05942 --- /dev/null +++ b/AssetStudioFBXNative/AssetStudioFBXNative.vcxproj.filters @@ -0,0 +1,74 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + + + + 资源文件 + + + \ No newline at end of file diff --git a/AssetStudioFBXNative/api.cpp b/AssetStudioFBXNative/api.cpp new file mode 100644 index 0000000..3fe775e --- /dev/null +++ b/AssetStudioFBXNative/api.cpp @@ -0,0 +1,1136 @@ +#include + +#include "dllexport.h" +#include "bool32_t.h" +#include "asfbx_context.h" +#include "asfbx_skin_context.h" +#include "asfbx_anim_context.h" +#include "asfbx_morph_context.h" +#include "utils.h" + +using namespace fbxsdk; + +AS_API(void) AsUtilQuaternionToEuler(float qx, float qy, float qz, float qw, float* vx, float* vy, float* vz) +{ + Quaternion q(qx, qy, qz, qw); + + auto v = QuaternionToEuler(q); + + if (vx) + { + *vx = v.X; + } + + if (vy) + { + *vy = v.Y; + } + + if (vz) + { + *vz = v.Z; + } +} + +AS_API(void) AsUtilEulerToQuaternion(float vx, float vy, float vz, float* qx, float* qy, float* qz, float* qw) +{ + Vector3 v(vx, vy, vz); + + auto q = EulerToQuaternion(v); + + if (qx) + { + *qx = q.X; + } + + if (qy) + { + *qy = q.Y; + } + + if (qz) + { + *qz = q.Z; + } + + if (qw) + { + *qw = q.W; + } +} + +#define MGR_IOS_REF (*(pSdkManager->GetIOSettings())) + +static const char* FBXVersion[] = +{ + FBX_2010_00_COMPATIBLE, + FBX_2011_00_COMPATIBLE, + FBX_2012_00_COMPATIBLE, + FBX_2013_00_COMPATIBLE, + FBX_2014_00_COMPATIBLE, + FBX_2016_00_COMPATIBLE +}; + +AS_API(AsFbxContext*) AsFbxCreateContext() +{ + return new AsFbxContext(); +} + +AS_API(bool32_t) AsFbxInitializeContext(AsFbxContext* pContext, const char* pFileName, float scaleFactor, int32_t versionIndex, bool32_t isAscii, bool32_t is60Fps, const char** pErrMsg) { + if (pContext == nullptr) + { + if (pErrMsg != nullptr) + { + *pErrMsg = "null pointer for pContext"; + } + + return false; + } + + auto pSdkManager = FbxManager::Create(); + pContext->pSdkManager = pSdkManager; + + FbxIOSettings* ios = FbxIOSettings::Create(pSdkManager, IOSROOT); + pSdkManager->SetIOSettings(ios); + + auto pScene = FbxScene::Create(pSdkManager, ""); + pContext->pScene = pScene; + + MGR_IOS_REF.SetBoolProp(EXP_FBX_MATERIAL, true); + MGR_IOS_REF.SetBoolProp(EXP_FBX_TEXTURE, true); + MGR_IOS_REF.SetBoolProp(EXP_FBX_EMBEDDED, false); + MGR_IOS_REF.SetBoolProp(EXP_FBX_SHAPE, true); + MGR_IOS_REF.SetBoolProp(EXP_FBX_GOBO, true); + MGR_IOS_REF.SetBoolProp(EXP_FBX_ANIMATION, true); + MGR_IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true); + + FbxGlobalSettings& globalSettings = pScene->GetGlobalSettings(); + globalSettings.SetSystemUnit(FbxSystemUnit(scaleFactor)); + + if (is60Fps) + { + globalSettings.SetTimeMode(FbxTime::eFrames60); + } + + auto pExporter = FbxExporter::Create(pScene, ""); + pContext->pExporter = pExporter; + + int pFileFormat = 0; + + if (versionIndex == 0) + { + pFileFormat = 3; + + if (isAscii) + { + pFileFormat = 4; + } + } + else + { + pExporter->SetFileExportVersion(FBXVersion[versionIndex]); + + if (isAscii) + { + pFileFormat = 1; + } + } + + if (!pExporter->Initialize(pFileName, pFileFormat, pSdkManager->GetIOSettings())) + { + if (pErrMsg != nullptr) + { + auto errStr = pExporter->GetStatus().GetErrorString(); + *pErrMsg = errStr; + } + + return false; + } + + auto pBindPose = FbxPose::Create(pScene, "BindPose"); + pContext->pBindPose = pBindPose; + + pScene->AddPose(pBindPose); + + return true; +} + +AS_API(void) AsFbxDisposeContext(AsFbxContext** ppContext) +{ + if (ppContext == nullptr) { + return; + } + + delete (*ppContext); + *ppContext = nullptr; +} + +AS_API(void) AsFbxSetFramePaths(AsFbxContext* pContext, const char* ppPaths[], int32_t count) +{ + if (pContext == nullptr) { + return; + } + + auto& framePaths = pContext->framePaths; + + for (auto i = 0; i < count; i += 1) + { + const char* path = ppPaths[i]; + framePaths.insert(std::string(path)); + } +} + +AS_API(void) AsFbxExportScene(AsFbxContext* pContext) +{ + if (pContext == nullptr) + { + return; + } + + auto pScene = pContext->pScene; + auto pExporter = pContext->pExporter; + + if (pExporter != nullptr && pScene != nullptr) + { + pExporter->Export(pScene); + } +} + +AS_API(FbxNode*) AsFbxGetSceneRootNode(AsFbxContext* pContext) +{ + if (pContext == nullptr) + { + return nullptr; + } + + if (pContext->pScene == nullptr) + { + return nullptr; + } + + return pContext->pScene->GetRootNode(); +} + +AS_API(FbxNode*) AsFbxExportSingleFrame(AsFbxContext* pContext, FbxNode* pParentNode, const char* pFramePath, const char* pFrameName, float localPositionX, float localPositionY, float localPositionZ, float localRotationX, float localRotationY, float localRotationZ, float localScaleX, float localScaleY, float localScaleZ) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return nullptr; + } + + const auto& framePaths = pContext->framePaths; + + if (!(framePaths.empty() || framePaths.find(pFramePath) != framePaths.end())) + { + return nullptr; + } + + auto pFrameNode = FbxNode::Create(pContext->pScene, pFrameName); + + pFrameNode->LclScaling.Set(FbxDouble3(localScaleX, localScaleY, localScaleZ)); + pFrameNode->LclRotation.Set(FbxDouble3(localRotationX, localRotationY, localRotationZ)); + pFrameNode->LclTranslation.Set(FbxDouble3(localPositionX, localPositionY, localPositionZ)); + pFrameNode->SetPreferedAngle(pFrameNode->LclRotation.Get()); + + pParentNode->AddChild(pFrameNode); + + if (pContext->pBindPose != nullptr) + { + pContext->pBindPose->Add(pFrameNode, pFrameNode->EvaluateGlobalTransform()); + } + + return pFrameNode; +} + +AS_API(void) AsFbxSetJointsNode_CastToBone(AsFbxContext* pContext, FbxNode* pNode, float boneSize) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pNode == nullptr) + { + return; + } + + FbxSkeleton* pJoint = FbxSkeleton::Create(pContext->pScene, ""); + pJoint->Size.Set(FbxDouble(boneSize)); + pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); + pNode->SetNodeAttribute(pJoint); +} + +AS_API(void) AsFbxSetJointsNode_BoneInPath(AsFbxContext* pContext, FbxNode* pNode, float boneSize) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pNode == nullptr) + { + return; + } + + FbxSkeleton* pJoint = FbxSkeleton::Create(pContext->pScene, ""); + pJoint->Size.Set(FbxDouble(boneSize)); + pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); + pNode->SetNodeAttribute(pJoint); + + pJoint = FbxSkeleton::Create(pContext->pScene, ""); + pJoint->Size.Set(FbxDouble(boneSize)); + pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); + pNode->GetParent()->SetNodeAttribute(pJoint); +} + +AS_API(void) AsFbxSetJointsNode_Generic(AsFbxContext* pContext, FbxNode* pNode) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pNode == nullptr) + { + return; + } + + FbxNull* pNull = FbxNull::Create(pContext->pScene, ""); + + if (pNode->GetChildCount() > 0) + { + pNull->Look.Set(FbxNull::eNone); + } + + pNode->SetNodeAttribute(pNull); +} + +AS_API(void) AsFbxPrepareMaterials(AsFbxContext* pContext, int32_t materialCount, int32_t textureCount) +{ + if (pContext == nullptr) + { + return; + } + + pContext->pMaterials = new FbxArray(); + pContext->pTextures = new FbxArray(); + + pContext->pMaterials->Reserve(materialCount); + pContext->pTextures->Reserve(textureCount); +} + +AS_API(FbxFileTexture*) AsFbxCreateTexture(AsFbxContext* pContext, const char* pMatTexName) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return nullptr; + } + + auto pTex = FbxFileTexture::Create(pContext->pScene, pMatTexName); + pTex->SetFileName(pMatTexName); + pTex->SetTextureUse(FbxTexture::eStandard); + pTex->SetMappingType(FbxTexture::eUV); + pTex->SetMaterialUse(FbxFileTexture::eModelMaterial); + pTex->SetSwapUV(false); + pTex->SetTranslation(0.0, 0.0); + pTex->SetScale(1.0, 1.0); + pTex->SetRotation(0.0, 0.0); + + if (pContext->pTextures != nullptr) + { + pContext->pTextures->Add(pTex); + } + + return pTex; +} + +AS_API(void) AsFbxLinkTexture(int32_t dest, FbxFileTexture* pTexture, FbxSurfacePhong* pMaterial, float offsetX, float offsetY, float scaleX, float scaleY) +{ + if (pTexture == nullptr || pMaterial == nullptr) + { + return; + } + + pTexture->SetTranslation(offsetX, offsetY); + pTexture->SetScale(scaleX, scaleY); + + FbxProperty* pProp; + + switch (dest) + { + case 0: + pProp = &pMaterial->Diffuse; + break; + case 1: + pProp = &pMaterial->NormalMap; + break; + case 2: + pProp = &pMaterial->Specular; + break; + case 3: + pProp = &pMaterial->Bump; + break; + default: + pProp = nullptr; + break; + } + + if (pProp != nullptr) { + pProp->ConnectSrcObject(pTexture); + } +} + +AS_API(FbxArray*) AsFbxMeshCreateClusterArray(int32_t boneCount) +{ + return new FbxArray(boneCount); +} + +AS_API(void) AsFbxMeshDisposeClusterArray(FbxArray** ppArray) +{ + if (ppArray == nullptr) { + return; + } + + delete (*ppArray); + *ppArray = nullptr; +} + +AS_API(FbxCluster*) AsFbxMeshCreateCluster(AsFbxContext* pContext, FbxNode* pBoneNode) +{ + if (pContext == nullptr || pContext->pScene == nullptr) { + return nullptr; + } + + if (pBoneNode == nullptr) { + return nullptr; + } + + FbxString lClusterName = pBoneNode->GetNameOnly() + FbxString("Cluster"); + FbxCluster* pCluster = FbxCluster::Create(pContext->pScene, lClusterName.Buffer()); + pCluster->SetLink(pBoneNode); + pCluster->SetLinkMode(FbxCluster::eTotalOne); + + return pCluster; +} + +AS_API(void) AsFbxMeshAddCluster(FbxArray* pArray, FbxCluster* pCluster) +{ + if (pArray == nullptr) { + return; + } + + pArray->Add(pCluster); +} + +AS_API(FbxMesh*) AsFbxMeshCreateMesh(AsFbxContext* pContext, FbxNode* pFrameNode) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return nullptr; + } + + if (pFrameNode == nullptr) + { + return nullptr; + } + + FbxMesh* pMesh = FbxMesh::Create(pContext->pScene, pFrameNode->GetName()); + pFrameNode->SetNodeAttribute(pMesh); + + return pMesh; +} + +AS_API(void) AsFbxMeshInitControlPoints(FbxMesh* pMesh, int32_t vertexCount) +{ + if (pMesh == nullptr) + { + return; + } + + pMesh->InitControlPoints(vertexCount); +} + +AS_API(void) AsFbxMeshCreateElementNormal(FbxMesh* pMesh) +{ + if (pMesh == nullptr) + { + return; + } + + auto pNormal = pMesh->CreateElementNormal(); + pNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); + pNormal->SetReferenceMode(FbxGeometryElement::eDirect); +} + +AS_API(void) AsFbxMeshCreateElementUV(FbxMesh* pMesh, int32_t uv) +{ + if (pMesh == nullptr) + { + return; + } + + auto pUV = pMesh->CreateElementUV(FbxString("UV") + FbxString(uv), FbxLayerElement::eTextureDiffuse); + pUV->SetMappingMode(FbxGeometryElement::eByControlPoint); + pUV->SetReferenceMode(FbxGeometryElement::eDirect); +} + +AS_API(void) AsFbxMeshCreateElementTangent(FbxMesh* pMesh) +{ + if (pMesh == nullptr) + { + return; + } + + auto pTangent = pMesh->CreateElementTangent(); + pTangent->SetMappingMode(FbxGeometryElement::eByControlPoint); + pTangent->SetReferenceMode(FbxGeometryElement::eDirect); +} + +AS_API(void) AsFbxMeshCreateElementVertexColor(FbxMesh* pMesh) +{ + if (pMesh == nullptr) + { + return; + } + + auto pVertexColor = pMesh->CreateElementVertexColor(); + pVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); + pVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); +} + +AS_API(void) AsFbxMeshCreateElementMaterial(FbxMesh* pMesh) +{ + if (pMesh == nullptr) + { + return; + } + + auto pMaterial = pMesh->CreateElementMaterial(); + pMaterial->SetMappingMode(FbxGeometryElement::eByPolygon); + pMaterial->SetReferenceMode(FbxGeometryElement::eIndexToDirect); +} + +AS_API(FbxSurfacePhong*) AsFbxCreateMaterial(AsFbxContext* pContext, const char* pMatName, + float diffuseR, float diffuseG, float diffuseB, + float ambientR, float ambientG, float ambientB, + float emissiveR, float emissiveG, float emissiveB, + float specularR, float specularG, float specularB, + float reflectR, float reflectG, float reflectB, + float shininess, float transparency) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return nullptr; + } + + if (pMatName == nullptr) + { + return nullptr; + } + + auto pMat = FbxSurfacePhong::Create(pContext->pScene, pMatName); + + pMat->Diffuse.Set(FbxDouble3(diffuseR, diffuseG, diffuseB)); + pMat->Ambient.Set(FbxDouble3(ambientR, ambientG, ambientB)); + pMat->Emissive.Set(FbxDouble3(emissiveR, emissiveG, emissiveB)); + pMat->Specular.Set(FbxDouble3(specularR, specularG, specularB)); + pMat->Reflection.Set(FbxDouble3(reflectR, reflectG, reflectB)); + pMat->Shininess.Set(FbxDouble(shininess)); + pMat->TransparencyFactor.Set(FbxDouble(transparency)); + pMat->ShadingModel.Set("Phong"); + + if (pContext->pMaterials) + { + pContext->pMaterials->Add(pMat); + } + + return pMat; +} + +AS_API(int32_t) AsFbxAddMaterialToFrame(FbxNode* pFrameNode, FbxSurfacePhong* pMaterial) +{ + if (pFrameNode == nullptr || pMaterial == nullptr) + { + return 0; + } + + return pFrameNode->AddMaterial(pMaterial); +} + +AS_API(void) AsFbxSetFrameShadingModeToTextureShading(FbxNode* pFrameNode) +{ + if (pFrameNode == nullptr) + { + return; + } + + pFrameNode->SetShadingMode(FbxNode::eTextureShading); +} + +AS_API(void) AsFbxMeshSetControlPoint(FbxMesh* pMesh, int32_t index, float x, float y, float z) +{ + if (pMesh == nullptr) + { + return; + } + + auto pControlPoints = pMesh->GetControlPoints(); + + pControlPoints[index] = FbxVector4(x, y, z, 0); +} + +AS_API(void) AsFbxMeshAddPolygon(FbxMesh* pMesh, int32_t materialIndex, int32_t index0, int32_t index1, int32_t index2) +{ + if (pMesh == nullptr) + { + return; + } + + pMesh->BeginPolygon(materialIndex); + pMesh->AddPolygon(index0); + pMesh->AddPolygon(index1); + pMesh->AddPolygon(index2); + pMesh->EndPolygon(); +} + +AS_API(void) AsFbxMeshElementNormalAdd(FbxMesh* pMesh, int32_t elementIndex, float x, float y, float z) +{ + if (pMesh == nullptr) + { + return; + } + + auto pElem = pMesh->GetElementNormal(elementIndex); + auto& array = pElem->GetDirectArray(); + + array.Add(FbxVector4(x, y, z, 0)); +} + +AS_API(void) AsFbxMeshElementUVAdd(FbxMesh* pMesh, int32_t elementIndex, float u, float v) +{ + if (pMesh == nullptr) + { + return; + } + + auto pElem = pMesh->GetElementUV(elementIndex); + auto& array = pElem->GetDirectArray(); + + array.Add(FbxVector2(u, v)); +} + +AS_API(void) AsFbxMeshElementTangentAdd(FbxMesh* pMesh, int32_t elementIndex, float x, float y, float z, float w) +{ + if (pMesh == nullptr) + { + return; + } + + auto pElem = pMesh->GetElementTangent(elementIndex); + auto& array = pElem->GetDirectArray(); + + array.Add(FbxVector4(x, y, z, w)); +} + +AS_API(void) AsFbxMeshElementVertexColorAdd(FbxMesh* pMesh, int32_t elementIndex, float r, float g, float b, float a) +{ + if (pMesh == nullptr) + { + return; + } + + auto pElem = pMesh->GetElementVertexColor(elementIndex); + auto& array = pElem->GetDirectArray(); + + array.Add(FbxVector4(r, g, b, a)); +} + +AS_API(void) AsFbxMeshSetBoneWeight(FbxArray* pClusterArray, int32_t boneIndex, int32_t vertexIndex, float weight) +{ + if (pClusterArray == nullptr) + { + return; + } + + auto pCluster = pClusterArray->GetAt(boneIndex); + + if (pCluster != nullptr) + { + pCluster->AddControlPointIndex(vertexIndex, weight); + } +} + +AS_API(AsFbxSkinContext*) AsFbxMeshCreateSkinContext(AsFbxContext* pContext, FbxNode* pFrameNode) +{ + return new AsFbxSkinContext(pContext, pFrameNode); +} + +AS_API(void) AsFbxMeshDisposeSkinContext(AsFbxSkinContext** ppSkinContext) +{ + if (ppSkinContext == nullptr) + { + return; + } + + delete (*ppSkinContext); + *ppSkinContext = nullptr; +} + +AS_API(bool32_t) FbxClusterArray_HasItemAt(FbxArray* pClusterArray, int32_t index) +{ + if (pClusterArray == nullptr) + { + return false; + } + + auto pCluster = pClusterArray->GetAt(index); + + return pCluster != nullptr; +} + +static inline int32_t IndexFrom4x4(int32_t m, int32_t n) +{ + return m * 4 + n; +} + +AS_API(void) AsFbxMeshSkinAddCluster(AsFbxSkinContext* pSkinContext, FbxArray* pClusterArray, int32_t index, float pBoneMatrix[16]) +{ + if (pSkinContext == nullptr) + { + return; + } + + if (pClusterArray == nullptr) + { + return; + } + + if (pBoneMatrix == nullptr) + { + return; + } + + auto pCluster = pClusterArray->GetAt(index); + + if (pCluster == nullptr) + { + return; + } + + FbxAMatrix boneMatrix; + + for (int m = 0; m < 4; m += 1) + { + for (int n = 0; n < 4; n += 1) + { + auto index = IndexFrom4x4(m, n); + boneMatrix.mData[m][n] = pBoneMatrix[index]; + } + } + + pCluster->SetTransformMatrix(pSkinContext->lMeshMatrix); + pCluster->SetTransformLinkMatrix(pSkinContext->lMeshMatrix * boneMatrix.Inverse()); + + if (pSkinContext->pSkin) + { + pSkinContext->pSkin->AddCluster(pCluster); + } +} + +AS_API(void) AsFbxMeshAddDeformer(AsFbxSkinContext* pSkinContext, FbxMesh* pMesh) +{ + if (pSkinContext == nullptr || pSkinContext->pSkin == nullptr) + { + return; + } + + if (pMesh == nullptr) + { + return; + } + + if (pSkinContext->pSkin->GetClusterCount() > 0) + { + pMesh->AddDeformer(pSkinContext->pSkin); + } +} + +AS_API(AsFbxAnimContext*) AsFbxAnimCreateContext(bool32_t eulerFilter) +{ + return new AsFbxAnimContext(eulerFilter); +} + +AS_API(void) AsFbxAnimDisposeContext(AsFbxAnimContext** ppAnimContext) +{ + if (ppAnimContext == nullptr) + { + return; + } + + delete (*ppAnimContext); + *ppAnimContext = nullptr; +} + +AS_API(void) AsFbxAnimPrepareStackAndLayer(AsFbxContext* pContext, AsFbxAnimContext* pAnimContext, const char* pTakeName) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pAnimContext == nullptr) + { + return; + } + + if (pTakeName == nullptr) + { + return; + } + + pAnimContext->lAnimStack = FbxAnimStack::Create(pContext->pScene, pTakeName); + pAnimContext->lAnimLayer = FbxAnimLayer::Create(pContext->pScene, "Base Layer"); + + pAnimContext->lAnimStack->AddMember(pAnimContext->lAnimLayer); +} + +AS_API(void) AsFbxAnimLoadCurves(FbxNode* pNode, AsFbxAnimContext* pAnimContext) +{ + if (pNode == nullptr) + { + return; + } + + if (pAnimContext == nullptr) + { + return; + } + + pAnimContext->lCurveSX = pNode->LclScaling.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); + pAnimContext->lCurveSY = pNode->LclScaling.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); + pAnimContext->lCurveSZ = pNode->LclScaling.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); + pAnimContext->lCurveRX = pNode->LclRotation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); + pAnimContext->lCurveRY = pNode->LclRotation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); + pAnimContext->lCurveRZ = pNode->LclRotation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); + pAnimContext->lCurveTX = pNode->LclTranslation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); + pAnimContext->lCurveTY = pNode->LclTranslation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); + pAnimContext->lCurveTZ = pNode->LclTranslation.GetCurve(pAnimContext->lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); +} + +AS_API(void) AsFbxAnimBeginKeyModify(AsFbxAnimContext* pAnimContext) +{ + if (pAnimContext == nullptr) + { + return; + } + + pAnimContext->lCurveSX->KeyModifyBegin(); + pAnimContext->lCurveSY->KeyModifyBegin(); + pAnimContext->lCurveSZ->KeyModifyBegin(); + pAnimContext->lCurveRX->KeyModifyBegin(); + pAnimContext->lCurveRY->KeyModifyBegin(); + pAnimContext->lCurveRZ->KeyModifyBegin(); + pAnimContext->lCurveTX->KeyModifyBegin(); + pAnimContext->lCurveTY->KeyModifyBegin(); + pAnimContext->lCurveTZ->KeyModifyBegin(); +} + +AS_API(void) AsFbxAnimEndKeyModify(AsFbxAnimContext* pAnimContext) +{ + if (pAnimContext == nullptr) + { + return; + } + + pAnimContext->lCurveSX->KeyModifyEnd(); + pAnimContext->lCurveSY->KeyModifyEnd(); + pAnimContext->lCurveSZ->KeyModifyEnd(); + pAnimContext->lCurveRX->KeyModifyEnd(); + pAnimContext->lCurveRY->KeyModifyEnd(); + pAnimContext->lCurveRZ->KeyModifyEnd(); + pAnimContext->lCurveTX->KeyModifyEnd(); + pAnimContext->lCurveTY->KeyModifyEnd(); + pAnimContext->lCurveTZ->KeyModifyEnd(); +} + +AS_API(void) AsFbxAnimAddScalingKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z) +{ + if (pAnimContext == nullptr) + { + return; + } + + FbxTime lTime; + lTime.SetSecondDouble(time); + + pAnimContext->lCurveSX->KeySet(pAnimContext->lCurveSX->KeyAdd(lTime), lTime, x); + pAnimContext->lCurveSY->KeySet(pAnimContext->lCurveSY->KeyAdd(lTime), lTime, y); + pAnimContext->lCurveSZ->KeySet(pAnimContext->lCurveSZ->KeyAdd(lTime), lTime, z); +} + +AS_API(void) AsFbxAnimAddRotationKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z) +{ + if (pAnimContext == nullptr) + { + return; + } + + FbxTime lTime; + lTime.SetSecondDouble(time); + + pAnimContext->lCurveRX->KeySet(pAnimContext->lCurveRX->KeyAdd(lTime), lTime, x); + pAnimContext->lCurveRY->KeySet(pAnimContext->lCurveRY->KeyAdd(lTime), lTime, y); + pAnimContext->lCurveRZ->KeySet(pAnimContext->lCurveRZ->KeyAdd(lTime), lTime, z); +} + +AS_API(void) AsFbxAnimAddTranslationKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z) +{ + if (pAnimContext == nullptr) + { + return; + } + + FbxTime lTime; + lTime.SetSecondDouble(time); + + pAnimContext->lCurveTX->KeySet(pAnimContext->lCurveTX->KeyAdd(lTime), lTime, x); + pAnimContext->lCurveTY->KeySet(pAnimContext->lCurveTY->KeyAdd(lTime), lTime, y); + pAnimContext->lCurveTZ->KeySet(pAnimContext->lCurveTZ->KeyAdd(lTime), lTime, z); +} + +AS_API(void) AsFbxAnimApplyEulerFilter(AsFbxAnimContext* pAnimContext, float filterPrecision) +{ + if (pAnimContext == nullptr || pAnimContext->lFilter == nullptr) + { + return; + } + + FbxAnimCurve* lCurve[3]; + lCurve[0] = pAnimContext->lCurveRX; + lCurve[1] = pAnimContext->lCurveRY; + lCurve[2] = pAnimContext->lCurveRZ; + + auto eulerFilter = pAnimContext->lFilter; + + eulerFilter->Reset(); + eulerFilter->SetQualityTolerance(filterPrecision); + eulerFilter->Apply(lCurve, 3); +} + +AS_API(int32_t) AsFbxAnimGetCurrentBlendShapeChannelCount(AsFbxAnimContext* pAnimContext, fbxsdk::FbxNode* pNode) +{ + if (pAnimContext == nullptr) + { + return 0; + } + + if (pNode == nullptr) + { + return 0; + } + + auto lGeometry = dynamic_cast(pNode->GetNodeAttribute()); + pAnimContext->lGeometry = lGeometry; + + if (lGeometry == nullptr) + { + return 0; + } + + auto blendShapeDeformerCount = lGeometry->GetDeformerCount(FbxDeformer::eBlendShape); + + if (blendShapeDeformerCount <= 0) + { + return 0; + } + + auto lBlendShape = dynamic_cast(lGeometry->GetDeformer(0, FbxDeformer::eBlendShape)); + pAnimContext->lBlendShape = lBlendShape; + + if (lBlendShape == nullptr) + { + return 0; + } + + auto lBlendShapeChannelCount = lBlendShape->GetBlendShapeChannelCount(); + + return lBlendShapeChannelCount; +} + +AS_API(bool32_t) AsFbxAnimIsBlendShapeChannelMatch(AsFbxAnimContext* pAnimContext, int32_t channelIndex, const char* channelName) +{ + if (pAnimContext == nullptr || pAnimContext->lBlendShape == nullptr) + { + return false; + } + + if (channelName == nullptr) + { + return false; + } + + FbxBlendShapeChannel* lChannel = pAnimContext->lBlendShape->GetBlendShapeChannel(channelIndex); + auto lChannelName = lChannel->GetNameOnly(); + + FbxString chanName(channelName); + + return lChannelName == chanName; +} + +AS_API(void) AsFbxAnimBeginBlendShapeAnimCurve(AsFbxAnimContext* pAnimContext, int32_t channelIndex) +{ + if (pAnimContext == nullptr || pAnimContext->lGeometry == nullptr || pAnimContext->lAnimLayer == nullptr) + { + return; + } + + pAnimContext->lAnimCurve = pAnimContext->lGeometry->GetShapeChannel(0, channelIndex, pAnimContext->lAnimLayer, true); + pAnimContext->lAnimCurve->KeyModifyBegin(); +} + +AS_API(void) AsFbxAnimEndBlendShapeAnimCurve(AsFbxAnimContext* pAnimContext) +{ + if (pAnimContext == nullptr || pAnimContext->lAnimCurve == nullptr) + { + return; + } + + pAnimContext->lAnimCurve->KeyModifyEnd(); +} + +AS_API(void) AsFbxAnimAddBlendShapeKeyframe(AsFbxAnimContext* pAnimContext, float time, float value) +{ + if (pAnimContext == nullptr || pAnimContext->lAnimCurve == nullptr) + { + return; + } + + FbxTime lTime; + lTime.SetSecondDouble(time); + + auto keyIndex = pAnimContext->lAnimCurve->KeyAdd(lTime); + pAnimContext->lAnimCurve->KeySetValue(keyIndex, value); + pAnimContext->lAnimCurve->KeySetInterpolation(keyIndex, FbxAnimCurveDef::eInterpolationCubic); +} + +AS_API(AsFbxMorphContext*) AsFbxMorphCreateContext() +{ + return new AsFbxMorphContext(); +} + +AS_API(void) AsFbxMorphInitializeContext(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, fbxsdk::FbxNode* pNode) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pMorphContext == nullptr) + { + return; + } + + if (pNode == nullptr) + { + return; + } + + auto pMesh = pNode->GetMesh(); + pMorphContext->pMesh = pMesh; + + auto lBlendShape = FbxBlendShape::Create(pContext->pScene, pMesh->GetNameOnly() + FbxString("BlendShape")); + pMorphContext->lBlendShape = lBlendShape; + + pMesh->AddDeformer(lBlendShape); +} + +AS_API(void) AsFbxMorphDisposeContext(AsFbxMorphContext** ppMorphContext) +{ + if (ppMorphContext == nullptr) + { + return; + } + + delete (*ppMorphContext); + *ppMorphContext = nullptr; +} + +AS_API(void) AsFbxMorphAddBlendShapeChannel(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, const char* channelName) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pMorphContext == nullptr) + { + return; + } + + if (channelName == nullptr) + { + return; + } + + auto lBlendShapeChannel = FbxBlendShapeChannel::Create(pContext->pScene, channelName); + pMorphContext->lBlendShapeChannel = lBlendShapeChannel; + + if (pMorphContext->lBlendShape != nullptr) + { + pMorphContext->lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); + } +} + +AS_API(void) AsFbxMorphAddBlendShapeChannelShape(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, float weight) +{ + if (pContext == nullptr || pContext->pScene == nullptr) + { + return; + } + + if (pMorphContext == nullptr) + { + return; + } + + auto lShape = FbxShape::Create(pContext->pScene, FbxString(weight)); + pMorphContext->lShape = lShape; + + if (pMorphContext->lBlendShapeChannel != nullptr) { + pMorphContext->lBlendShapeChannel->AddTargetShape(lShape, weight); + } +} + +AS_API(void) AsFbxMorphCopyBlendShapeControlPoints(AsFbxMorphContext* pMorphContext) +{ + if (pMorphContext == nullptr || pMorphContext->pMesh == nullptr || pMorphContext->lShape == nullptr) + { + return; + } + + auto vectorCount = pMorphContext->pMesh->GetControlPointsCount(); + + auto srcControlPoints = pMorphContext->pMesh->GetControlPoints(); + + pMorphContext->lShape->InitControlPoints(vectorCount); + + auto dstControlPoints = pMorphContext->lShape->GetControlPoints(); + + for (int j = 0; j < vectorCount; j++) + { + auto vertex = srcControlPoints[j]; + dstControlPoints[j] = FbxVector4(vertex); + } +} + +AS_API(void) AsFbxMorphSetBlendShapeVertex(AsFbxMorphContext* pMorphContext, uint32_t index, float x, float y, float z) +{ + if (pMorphContext == nullptr || pMorphContext->lShape == nullptr) + { + return; + } + + auto pControlPoints = pMorphContext->lShape->GetControlPoints(); + + pControlPoints[index] = FbxVector4(x, y, z, 0); +} diff --git a/AssetStudioFBXNative/api.h b/AssetStudioFBXNative/api.h new file mode 100644 index 0000000..1422136 --- /dev/null +++ b/AssetStudioFBXNative/api.h @@ -0,0 +1,155 @@ +#pragma once + +#include "dllexport.h" +#include "bool32_t.h" + +namespace fbxsdk +{ + class FbxNode; + class FbxFileTexture; + template + class FbxArray; + class FbxCluster; + class FbxMesh; + class FbxSurfacePhong; +} + +struct AsFbxContext; +struct AsFbxSkinContext; +struct AsFbxAnimContext; +struct AsFbxMorphContext; + +AS_API(void) AsUtilQuaternionToEuler(float qx, float qy, float qz, float qw, float* vx, float* vy, float* vz); + +AS_API(void) AsUtilEulerToQuaternion(float vx, float vy, float vz, float* qx, float* qy, float* qz, float* qw); + +// All strings ([const] char *) in this header are UTF-8 strings. + +AS_API(AsFbxContext*) AsFbxCreateContext(); + +// Do not free pErrMsg +AS_API(bool32_t) AsFbxInitializeContext(AsFbxContext* pContext, const char* pFileName, float scaleFactor, int32_t versionIndex, bool32_t isAscii, bool32_t is60Fps, const char** pErrMsg); + +AS_API(void) AsFbxDisposeContext(AsFbxContext** ppContext); + +AS_API(void) AsFbxSetFramePaths(AsFbxContext* pContext, const char* ppPaths[], int32_t count); + +AS_API(void) AsFbxExportScene(AsFbxContext* pContext); + +AS_API(fbxsdk::FbxNode*) AsFbxGetSceneRootNode(AsFbxContext* pContext); + +AS_API(fbxsdk::FbxNode*) AsFbxExportSingleFrame(AsFbxContext* pContext, fbxsdk::FbxNode* pParentNode, const char* pFramePath, const char* pFrameName, float localPositionX, float localPositionY, float localPositionZ, float localRotationX, float localRotationY, float localRotationZ, float localScaleX, float localScaleY, float localScaleZ); + +AS_API(void) AsFbxSetJointsNode_CastToBone(AsFbxContext* pContext, fbxsdk::FbxNode* pNode, float boneSize); + +AS_API(void) AsFbxSetJointsNode_BoneInPath(AsFbxContext* pContext, fbxsdk::FbxNode* pNode, float boneSize); + +AS_API(void) AsFbxSetJointsNode_Generic(AsFbxContext* pContext, fbxsdk::FbxNode* pNode); + +AS_API(void) AsFbxPrepareMaterials(AsFbxContext* pContext, int32_t materialCount, int32_t textureCount); + +AS_API(fbxsdk::FbxFileTexture*) AsFbxCreateTexture(AsFbxContext* pContext, const char* pMatTexName); + +AS_API(void) AsFbxLinkTexture(int32_t dest, fbxsdk::FbxFileTexture* pTexture, fbxsdk::FbxSurfacePhong* pMaterial, float offsetX, float offsetY, float scaleX, float scaleY); + +AS_API(fbxsdk::FbxArray*) AsFbxMeshCreateClusterArray(int32_t boneCount); + +AS_API(void) AsFbxMeshDisposeClusterArray(fbxsdk::FbxArray** ppArray); + +AS_API(fbxsdk::FbxCluster*) AsFbxMeshCreateCluster(AsFbxContext* pContext, fbxsdk::FbxNode* pBoneNode); + +AS_API(void) AsFbxMeshAddCluster(fbxsdk::FbxArray* pArray, /* CanBeNull */ fbxsdk::FbxCluster* pCluster); + +AS_API(fbxsdk::FbxMesh*) AsFbxMeshCreateMesh(AsFbxContext* pContext, fbxsdk::FbxNode* pFrameNode); + +AS_API(void) AsFbxMeshInitControlPoints(fbxsdk::FbxMesh* pMesh, int32_t vertexCount); + +AS_API(void) AsFbxMeshCreateElementNormal(fbxsdk::FbxMesh* pMesh); + +AS_API(void) AsFbxMeshCreateElementUV(fbxsdk::FbxMesh* pMesh, int32_t uv); + +AS_API(void) AsFbxMeshCreateElementTangent(fbxsdk::FbxMesh* pMesh); + +AS_API(void) AsFbxMeshCreateElementVertexColor(fbxsdk::FbxMesh* pMesh); + +AS_API(void) AsFbxMeshCreateElementMaterial(fbxsdk::FbxMesh* pMesh); + +AS_API(fbxsdk::FbxSurfacePhong*) AsFbxCreateMaterial(AsFbxContext* pContext, const char* pMatName, + float diffuseR, float diffuseG, float diffuseB, + float ambientR, float ambientG, float ambientB, + float emissiveR, float emissiveG, float emissiveB, + float specularR, float specularG, float specularB, + float reflectR, float reflectG, float reflectB, + float shininess, float transparency); + +AS_API(int32_t) AsFbxAddMaterialToFrame(fbxsdk::FbxNode* pFrameNode, fbxsdk::FbxSurfacePhong* pMaterial); + +AS_API(void) AsFbxSetFrameShadingModeToTextureShading(fbxsdk::FbxNode* pFrameNode); + +AS_API(void) AsFbxMeshSetControlPoint(fbxsdk::FbxMesh* pMesh, int32_t index, float x, float y, float z); + +AS_API(void) AsFbxMeshAddPolygon(fbxsdk::FbxMesh* pMesh, int32_t materialIndex, int32_t index0, int32_t index1, int32_t index2); + +AS_API(void) AsFbxMeshElementNormalAdd(fbxsdk::FbxMesh* pMesh, int32_t elementIndex, float x, float y, float z); + +AS_API(void) AsFbxMeshElementUVAdd(fbxsdk::FbxMesh* pMesh, int32_t elementIndex, float u, float v); + +AS_API(void) AsFbxMeshElementTangentAdd(fbxsdk::FbxMesh* pMesh, int32_t elementIndex, float x, float y, float z, float w); + +AS_API(void) AsFbxMeshElementVertexColorAdd(fbxsdk::FbxMesh* pMesh, int32_t elementIndex, float r, float g, float b, float a); + +AS_API(void) AsFbxMeshSetBoneWeight(fbxsdk::FbxArray* pClusterArray, int32_t boneIndex, int32_t vertexIndex, float weight); + +AS_API(AsFbxSkinContext*) AsFbxMeshCreateSkinContext(AsFbxContext* pContext, fbxsdk::FbxNode* pFrameNode); + +AS_API(void) AsFbxMeshDisposeSkinContext(AsFbxSkinContext** ppSkinContext); + +AS_API(bool32_t) FbxClusterArray_HasItemAt(fbxsdk::FbxArray* pClusterArray, int32_t index); + +AS_API(void) AsFbxMeshSkinAddCluster(AsFbxSkinContext* pSkinContext, fbxsdk::FbxArray* pClusterArray, int32_t index, float pBoneMatrix[16]); + +AS_API(void) AsFbxMeshAddDeformer(AsFbxSkinContext* pSkinContext, fbxsdk::FbxMesh* pMesh); + +AS_API(AsFbxAnimContext*) AsFbxAnimCreateContext(bool32_t eulerFilter); + +AS_API(void) AsFbxAnimDisposeContext(AsFbxAnimContext** ppAnimContext); + +AS_API(void) AsFbxAnimPrepareStackAndLayer(AsFbxContext* pContext, AsFbxAnimContext* pAnimContext, const char* pTakeName); + +AS_API(void) AsFbxAnimLoadCurves(fbxsdk::FbxNode* pNode, AsFbxAnimContext* pAnimContext); + +AS_API(void) AsFbxAnimBeginKeyModify(AsFbxAnimContext* pAnimContext); + +AS_API(void) AsFbxAnimEndKeyModify(AsFbxAnimContext* pAnimContext); + +AS_API(void) AsFbxAnimAddScalingKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z); + +AS_API(void) AsFbxAnimAddRotationKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z); + +AS_API(void) AsFbxAnimAddTranslationKey(AsFbxAnimContext* pAnimContext, float time, float x, float y, float z); + +AS_API(void) AsFbxAnimApplyEulerFilter(AsFbxAnimContext* pAnimContext, float filterPrecision); + +AS_API(int32_t) AsFbxAnimGetCurrentBlendShapeChannelCount(AsFbxAnimContext* pAnimContext, fbxsdk::FbxNode* pNode); + +AS_API(bool32_t) AsFbxAnimIsBlendShapeChannelMatch(AsFbxAnimContext* pAnimContext, int32_t channelIndex, const char* channelName); + +AS_API(void) AsFbxAnimBeginBlendShapeAnimCurve(AsFbxAnimContext* pAnimContext, int32_t channelIndex); + +AS_API(void) AsFbxAnimEndBlendShapeAnimCurve(AsFbxAnimContext* pAnimContext); + +AS_API(void) AsFbxAnimAddBlendShapeKeyframe(AsFbxAnimContext* pAnimContext, float time, float value); + +AS_API(AsFbxMorphContext*) AsFbxMorphCreateContext(); + +AS_API(void) AsFbxMorphInitializeContext(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, fbxsdk::FbxNode* pNode); + +AS_API(void) AsFbxMorphDisposeContext(AsFbxMorphContext** ppMorphContext); + +AS_API(void) AsFbxMorphAddBlendShapeChannel(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, const char* channelName); + +AS_API(void) AsFbxMorphAddBlendShapeChannelShape(AsFbxContext* pContext, AsFbxMorphContext* pMorphContext, float weight); + +AS_API(void) AsFbxMorphCopyBlendShapeControlPoints(AsFbxMorphContext* pMorphContext); + +AS_API(void) AsFbxMorphSetBlendShapeVertex(AsFbxMorphContext* pMorphContext, uint32_t index, float x, float y, float z); diff --git a/AssetStudioFBXNative/asfbx_anim_context.cpp b/AssetStudioFBXNative/asfbx_anim_context.cpp new file mode 100644 index 0000000..a425178 --- /dev/null +++ b/AssetStudioFBXNative/asfbx_anim_context.cpp @@ -0,0 +1,27 @@ +#include "asfbx_anim_context.h" + +AsFbxAnimContext::AsFbxAnimContext(bool32_t eulerFilter) + : lFilter(nullptr) +{ + if (eulerFilter) + { + lFilter = new FbxAnimCurveFilterUnroll(); + } + + lAnimStack = nullptr; + lAnimLayer = nullptr; + + lCurveSX = nullptr; + lCurveSY = nullptr; + lCurveSZ = nullptr; + lCurveRX = nullptr; + lCurveRY = nullptr; + lCurveRZ = nullptr; + lCurveTX = nullptr; + lCurveTY = nullptr; + lCurveTZ = nullptr; + + lGeometry = nullptr; + lBlendShape = nullptr; + lAnimCurve = nullptr; +} diff --git a/AssetStudioFBXNative/asfbx_anim_context.h b/AssetStudioFBXNative/asfbx_anim_context.h new file mode 100644 index 0000000..94241db --- /dev/null +++ b/AssetStudioFBXNative/asfbx_anim_context.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "bool32_t.h" + +struct AsFbxAnimContext +{ + + FbxAnimCurveFilterUnroll* lFilter; + + FbxAnimStack* lAnimStack; + FbxAnimLayer* lAnimLayer; + + FbxAnimCurve* lCurveSX; + FbxAnimCurve* lCurveSY; + FbxAnimCurve* lCurveSZ; + FbxAnimCurve* lCurveRX; + FbxAnimCurve* lCurveRY; + FbxAnimCurve* lCurveRZ; + FbxAnimCurve* lCurveTX; + FbxAnimCurve* lCurveTY; + FbxAnimCurve* lCurveTZ; + + FbxGeometry* lGeometry; + FbxBlendShape* lBlendShape; + FbxAnimCurve* lAnimCurve; + + AsFbxAnimContext(bool32_t eulerFilter); + ~AsFbxAnimContext() = default; + +}; diff --git a/AssetStudioFBXNative/asfbx_context.cpp b/AssetStudioFBXNative/asfbx_context.cpp new file mode 100644 index 0000000..d510fab --- /dev/null +++ b/AssetStudioFBXNative/asfbx_context.cpp @@ -0,0 +1,33 @@ +#include + +#include "asfbx_context.h" + +AsFbxContext::AsFbxContext() +{ + pSdkManager = nullptr; + pScene = nullptr; + pTextures = nullptr; + pMaterials = nullptr; + pExporter = nullptr; + pBindPose = nullptr; +} + +AsFbxContext::~AsFbxContext() +{ + framePaths.clear(); + + delete pMaterials; + delete pTextures; + + if (pExporter != nullptr) { + pExporter->Destroy(); + } + + if (pScene != nullptr) { + pScene->Destroy(); + } + + if (pSdkManager != nullptr) { + pSdkManager->Destroy(); + } +} diff --git a/AssetStudioFBXNative/asfbx_context.h b/AssetStudioFBXNative/asfbx_context.h new file mode 100644 index 0000000..20f98fd --- /dev/null +++ b/AssetStudioFBXNative/asfbx_context.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace fbxsdk +{ + class FbxManager; + class FbxScene; + class FbxExporter; + template + class FbxArray; + class FbxFileTexture; + class FbxSurfacePhong; + class FbxPose; +} + +struct AsFbxContext +{ + + fbxsdk::FbxManager* pSdkManager; + fbxsdk::FbxScene* pScene; + fbxsdk::FbxArray* pTextures; + fbxsdk::FbxArray* pMaterials; + fbxsdk::FbxExporter* pExporter; + fbxsdk::FbxPose* pBindPose; + + std::unordered_set framePaths; + + AsFbxContext(); + ~AsFbxContext(); +}; diff --git a/AssetStudioFBXNative/asfbx_morph_context.cpp b/AssetStudioFBXNative/asfbx_morph_context.cpp new file mode 100644 index 0000000..4d9fd12 --- /dev/null +++ b/AssetStudioFBXNative/asfbx_morph_context.cpp @@ -0,0 +1,9 @@ +#include "asfbx_morph_context.h" + +AsFbxMorphContext::AsFbxMorphContext() +{ + pMesh = nullptr; + lBlendShape = nullptr; + lBlendShapeChannel = nullptr; + lShape = nullptr; +} diff --git a/AssetStudioFBXNative/asfbx_morph_context.h b/AssetStudioFBXNative/asfbx_morph_context.h new file mode 100644 index 0000000..ff2b4e0 --- /dev/null +++ b/AssetStudioFBXNative/asfbx_morph_context.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +struct AsFbxMorphContext +{ + + FbxMesh* pMesh; + FbxBlendShape* lBlendShape; + FbxBlendShapeChannel* lBlendShapeChannel; + FbxShape* lShape; + + AsFbxMorphContext(); + ~AsFbxMorphContext() = default; + +}; diff --git a/AssetStudioFBXNative/asfbx_skin_context.cpp b/AssetStudioFBXNative/asfbx_skin_context.cpp new file mode 100644 index 0000000..c637ff1 --- /dev/null +++ b/AssetStudioFBXNative/asfbx_skin_context.cpp @@ -0,0 +1,16 @@ +#include "asfbx_skin_context.h" +#include "asfbx_context.h" + +AsFbxSkinContext::AsFbxSkinContext(AsFbxContext* pContext, FbxNode* pFrameNode) + : pSkin(nullptr) +{ + if (pContext != nullptr && pContext->pScene != nullptr) + { + pSkin = FbxSkin::Create(pContext->pScene, ""); + } + + if (pFrameNode != nullptr) + { + lMeshMatrix = pFrameNode->EvaluateGlobalTransform(); + } +} diff --git a/AssetStudioFBXNative/asfbx_skin_context.h b/AssetStudioFBXNative/asfbx_skin_context.h new file mode 100644 index 0000000..73810e7 --- /dev/null +++ b/AssetStudioFBXNative/asfbx_skin_context.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +struct AsFbxContext; + +struct AsFbxSkinContext +{ + + FbxSkin* pSkin; + FbxAMatrix lMeshMatrix; + + AsFbxSkinContext(AsFbxContext* pContext, FbxNode* pFrameNode); + ~AsFbxSkinContext() = default; + +}; diff --git a/AssetStudioFBXNative/bool32_t.h b/AssetStudioFBXNative/bool32_t.h new file mode 100644 index 0000000..2b2d6b2 --- /dev/null +++ b/AssetStudioFBXNative/bool32_t.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +typedef uint32_t bool32_t; diff --git a/AssetStudioFBXNative/cpp.hint b/AssetStudioFBXNative/cpp.hint new file mode 100644 index 0000000..020cc47 --- /dev/null +++ b/AssetStudioFBXNative/cpp.hint @@ -0,0 +1 @@ +#define AS_API(ret_type) diff --git a/AssetStudioFBXNative/dllexport.h b/AssetStudioFBXNative/dllexport.h new file mode 100644 index 0000000..9e9c0b7 --- /dev/null +++ b/AssetStudioFBXNative/dllexport.h @@ -0,0 +1,56 @@ +#pragma once + +#if defined(_MSC_VER) +#if _MSC_VER < 1910 // MSVC 2017- +#error MSVC 2017 or later is required. +#endif +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW__) +#ifdef _AS_DLL +#ifdef __GNUC__ +#define _AS_EXPORT __attribute__ ((dllexport)) +#else +#define _AS_EXPORT __declspec(dllexport) +#endif +#else +#ifdef __GNUC__ +#define _AS_EXPORT __attribute__ ((dllimport)) +#else +#define _AS_EXPORT __declspec(dllimport) +#endif +#endif +#define _AS_LOCAL +#else +#if __GNUC__ >= 4 +#define _AS_EXPORT __attribute__ ((visibility ("default"))) +#define _AS_LOCAL __attribute__ ((visibility ("hidden"))) +#else +#define _AS_EXPORT +#define _AS_LOCAL +#endif +#endif + +#ifdef __cplusplus +#ifndef _EXTERN_C_STMT +#define _EXTERN_C_STMT extern "C" +#endif +#else +#ifndef _EXTERN_C_STMT +#define _EXTERN_C_STMT +#endif +#endif + +#ifndef _AS_CALL +#if defined(WIN32) || defined(_WIN32) +#define _AS_CALL __stdcall +#else +#define _AS_CALL /* __cdecl */ +#endif +#endif + +#if defined(_MSC_VER) +#define AS_API(ret_type) _EXTERN_C_STMT _AS_EXPORT ret_type _AS_CALL +#else +#define AS_API(ret_type) _EXTERN_C_STMT _AS_EXPORT _AS_CALL ret_type +#endif diff --git a/AssetStudioFBXNative/resource.h b/AssetStudioFBXNative/resource.h new file mode 100644 index 0000000..557566e --- /dev/null +++ b/AssetStudioFBXNative/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AssetStudioFBXNative.rc + +// ¶һĬֵ +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/AssetStudioFBXNative/utils.cpp b/AssetStudioFBXNative/utils.cpp new file mode 100644 index 0000000..b111d55 --- /dev/null +++ b/AssetStudioFBXNative/utils.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include "utils.h" + +Vector3::Vector3() + : X(0), Y(0), Z(0) +{ +} + +Vector3::Vector3(float x, float y, float z) + : X(x), Y(y), Z(z) +{ +} + +Quaternion::Quaternion() + : X(0), Y(0), Z(0), W(1) +{ +} + +Quaternion::Quaternion(float x, float y, float z) + : X(x), Y(y), Z(z), W(1) +{ +} + +Quaternion::Quaternion(float x, float y, float z, float w) + : X(x), Y(y), Z(z), W(w) +{ +} + +Vector3 QuaternionToEuler(Quaternion q) { + FbxAMatrix lMatrixRot; + lMatrixRot.SetQ(FbxQuaternion(q.X, q.Y, q.Z, q.W)); + FbxVector4 lEuler = lMatrixRot.GetR(); + return Vector3((float)lEuler[0], (float)lEuler[1], (float)lEuler[2]); +} + +Quaternion EulerToQuaternion(Vector3 v) { + FbxAMatrix lMatrixRot; + lMatrixRot.SetR(FbxVector4(v.X, v.Y, v.Z)); + FbxQuaternion lQuaternion = lMatrixRot.GetQ(); + return Quaternion((float)lQuaternion[0], (float)lQuaternion[1], (float)lQuaternion[2], (float)lQuaternion[3]); +} diff --git a/AssetStudioFBXNative/utils.h b/AssetStudioFBXNative/utils.h new file mode 100644 index 0000000..b1e2c7b --- /dev/null +++ b/AssetStudioFBXNative/utils.h @@ -0,0 +1,29 @@ +#pragma once + +struct Vector3 { + + float X; + float Y; + float Z; + + Vector3(); + Vector3(float x, float y, float z); + +}; + +struct Quaternion { + + float X; + float Y; + float Z; + float W; + + Quaternion(); + Quaternion(float x, float y, float z); + Quaternion(float x, float y, float z, float w); + +}; + +Vector3 QuaternionToEuler(Quaternion q); + +Quaternion EulerToQuaternion(Vector3 v); diff --git a/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj new file mode 100644 index 0000000..f62c396 --- /dev/null +++ b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {BD76E63F-1517-47FA-8233-33E853A3ACEE} + Library + Properties + AssetStudio.FbxInterop + AssetStudioFBXWrapper + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + {40c796b5-88ce-4adc-acd6-2f4862b7f136} + AssetStudio.PInvoke + + + {7662f8c2-7bfd-442e-a948-a43b4f7eb06e} + AssetStudio + + + + \ No newline at end of file diff --git a/AssetStudioFBXWrapper/Fbx.PInvoke.cs b/AssetStudioFBXWrapper/Fbx.PInvoke.cs new file mode 100644 index 0000000..11d6c5f --- /dev/null +++ b/AssetStudioFBXWrapper/Fbx.PInvoke.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; +using AssetStudio.FbxInterop; + +namespace AssetStudio +{ + partial class Fbx + { + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsUtilQuaternionToEuler(float qx, float qy, float qz, float qw, out float vx, out float vy, out float vz); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsUtilEulerToQuaternion(float vx, float vy, float vz, out float qx, out float qy, out float qz, out float qw); + + } +} diff --git a/AssetStudioFBXWrapper/Fbx.cs b/AssetStudioFBXWrapper/Fbx.cs new file mode 100644 index 0000000..3f38884 --- /dev/null +++ b/AssetStudioFBXWrapper/Fbx.cs @@ -0,0 +1,58 @@ +using AssetStudio.FbxInterop; +using AssetStudio.PInvoke; +using System.IO; + +namespace AssetStudio +{ + public static partial class Fbx + { + + static Fbx() + { + DllLoader.PreloadDll(FbxDll.DllName); + } + + public static Vector3 QuaternionToEuler(Quaternion q) + { + AsUtilQuaternionToEuler(q.X, q.Y, q.Z, q.W, out var x, out var y, out var z); + return new Vector3(x, y, z); + } + + public static Quaternion EulerToQuaternion(Vector3 v) + { + AsUtilEulerToQuaternion(v.X, v.Y, v.Z, out var x, out var y, out var z, out var w); + return new Quaternion(x, y, z, w); + } + + public static class Exporter + { + + public static void Export(string path, IImported imported, bool eulerFilter, float filterPrecision, + bool allNodes, bool skins, bool animation, bool blendShape, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii) + { + var file = new FileInfo(path); + var dir = file.Directory; + + if (!dir.Exists) + { + dir.Create(); + } + + var currentDir = Directory.GetCurrentDirectory(); + Directory.SetCurrentDirectory(dir.FullName); + + var name = Path.GetFileName(path); + + using (var exporter = new FbxExporter(name, imported, allNodes, skins, castToBone, boneSize, scaleFactor, versionIndex, isAscii)) + { + exporter.Initialize(); + exporter.ExportAll(blendShape, animation, eulerFilter, filterPrecision); + } + + Directory.SetCurrentDirectory(currentDir); + } + + } + + } +} diff --git a/AssetStudioFBXWrapper/FbxDll.cs b/AssetStudioFBXWrapper/FbxDll.cs new file mode 100644 index 0000000..292ce57 --- /dev/null +++ b/AssetStudioFBXWrapper/FbxDll.cs @@ -0,0 +1,10 @@ +namespace AssetStudio.FbxInterop +{ + internal static class FbxDll + { + + internal const string DllName = "AssetStudioFBXNative"; + internal const string FbxsdkDllName = "libfbxsdk"; + + } +} diff --git a/AssetStudioFBXWrapper/FbxExporter.cs b/AssetStudioFBXWrapper/FbxExporter.cs new file mode 100644 index 0000000..ef008b7 --- /dev/null +++ b/AssetStudioFBXWrapper/FbxExporter.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace AssetStudio.FbxInterop +{ + internal sealed class FbxExporter : IDisposable + { + + private FbxExporterContext _context; + + private readonly string _fileName; + private readonly IImported _imported; + private readonly bool _allNodes; + private readonly bool _exportSkins; + private readonly bool _castToBone; + private readonly float _boneSize; + private readonly float _scaleFactor; + private readonly int _versionIndex; + private readonly bool _isAscii; + + internal FbxExporter(string fileName, IImported imported, bool allNodes, bool exportSkins, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii) + { + _context = new FbxExporterContext(); + + _fileName = fileName; + _imported = imported; + _allNodes = allNodes; + _exportSkins = exportSkins; + _castToBone = castToBone; + _boneSize = boneSize; + _scaleFactor = scaleFactor; + _versionIndex = versionIndex; + _isAscii = isAscii; + } + + ~FbxExporter() + { + Dispose(false); + } + + public void Dispose() + { + if (IsDisposed) + { + return; + } + + Dispose(true); + GC.SuppressFinalize(this); + } + + public bool IsDisposed { get; private set; } + + private void Dispose(bool disposing) + { + if (disposing) + { + _context.Dispose(); + } + + IsDisposed = true; + } + + internal void Initialize() + { + var is60Fps = _imported.AnimationList.Count > 0 && _imported.AnimationList[0].SampleRate.Equals(60.0f); + + _context.Initialize(_fileName, _scaleFactor, _versionIndex, _isAscii, is60Fps); + + if (!_allNodes) + { + var framePaths = SearchHierarchy(); + + _context.SetFramePaths(framePaths); + } + } + + internal void ExportAll(bool blendShape, bool animation, bool eulerFilter, float filterPrecision) + { + var meshFrames = new List(); + + ExportRootFrame(meshFrames); + + if (_imported.MeshList != null) + { + SetJointsFromImportedMeshes(); + + PrepareMaterials(); + + ExportMeshFrames(_imported.RootFrame, meshFrames); + } + else + { + SetJointsNode(_imported.RootFrame, null, true); + } + + + + if (blendShape) + { + ExportMorphs(); + } + + if (animation) + { + ExportAnimations(eulerFilter, filterPrecision); + } + + ExportScene(); + } + + private void ExportMorphs() + { + _context.ExportMorphs(_imported.RootFrame, _imported.MorphList); + } + + private void ExportAnimations(bool eulerFilter, float filterPrecision) + { + _context.ExportAnimations(_imported.RootFrame, _imported.AnimationList, eulerFilter, filterPrecision); + } + + private void ExportRootFrame(List meshFrames) + { + _context.ExportFrame(_imported.MeshList, meshFrames, _imported.RootFrame); + } + + private void ExportScene() + { + _context.ExportScene(); + } + + private void SetJointsFromImportedMeshes() + { + if (!_exportSkins) + { + return; + } + + Debug.Assert(_imported.MeshList != null); + + var bonePaths = new HashSet(); + + foreach (var mesh in _imported.MeshList) + { + var boneList = mesh.BoneList; + + if (boneList != null) + { + foreach (var bone in boneList) + { + bonePaths.Add(bone.Path); + } + } + } + + SetJointsNode(_imported.RootFrame, bonePaths, _castToBone); + } + + private void SetJointsNode(ImportedFrame rootFrame, HashSet bonePaths, bool castToBone) + { + _context.SetJointsNode(rootFrame, bonePaths, castToBone, _boneSize); + } + + private void PrepareMaterials() + { + _context.PrepareMaterials(_imported.MaterialList.Count, _imported.TextureList.Count); + } + + private void ExportMeshFrames(ImportedFrame rootFrame, List meshFrames) + { + foreach (var meshFrame in meshFrames) + { + _context.ExportMeshFromFrame(rootFrame, meshFrame, _imported.MeshList, _imported.MaterialList, _imported.TextureList, _exportSkins); + } + } + + private HashSet SearchHierarchy() + { + if (_imported.MeshList == null || _imported.MeshList.Count == 0) + { + return null; + } + + var exportFrames = new HashSet(); + + SearchHierarchy(_imported.RootFrame, _imported.MeshList, exportFrames); + + return exportFrames; + } + + private static void SearchHierarchy(ImportedFrame rootFrame, List meshList, HashSet exportFrames) + { + var frameStack = new Stack(); + + frameStack.Push(rootFrame); + + while (frameStack.Count > 0) + { + var frame = frameStack.Pop(); + + var meshListSome = ImportedHelpers.FindMesh(frame.Path, meshList); + + if (meshListSome != null) + { + var parent = frame; + + while (parent != null) + { + exportFrames.Add(parent.Path); + parent = parent.Parent; + } + + var boneList = meshListSome.BoneList; + + if (boneList != null) + { + foreach (var bone in boneList) + { + if (!exportFrames.Contains(bone.Path)) + { + var boneParent = rootFrame.FindFrameByPath(bone.Path); + + while (boneParent != null) + { + exportFrames.Add(boneParent.Path); + boneParent = boneParent.Parent; + } + } + } + } + } + + for (var i = frame.Count - 1; i >= 0; i -= 1) + { + frameStack.Push(frame[i]); + } + } + } + + } +} diff --git a/AssetStudioFBXWrapper/FbxExporterContext.PInvoke.cs b/AssetStudioFBXWrapper/FbxExporterContext.PInvoke.cs new file mode 100644 index 0000000..e97271a --- /dev/null +++ b/AssetStudioFBXWrapper/FbxExporterContext.PInvoke.cs @@ -0,0 +1,308 @@ +using System; +using System.Runtime.InteropServices; +using AssetStudio.PInvoke; + +namespace AssetStudio.FbxInterop +{ + partial class FbxExporterContext + { + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxCreateContext(); + + private static bool AsFbxInitializeContext(IntPtr context, string fileName, float scaleFactor, int versionIndex, bool isAscii, bool is60Fps, out string errorMessage) + { + bool b; + IntPtr pErrMsg; + + using (var fileNameUtf8 = new Utf8StringHandle(fileName)) + { + b = AsFbxInitializeContext(context, fileNameUtf8.DangerousGetHandle(), scaleFactor, versionIndex, isAscii, is60Fps, out pErrMsg); + } + + errorMessage = Utf8StringHandle.ReadUtf8StringFromPointer(pErrMsg); + + return b; + } + + // Do not free the pointer strErrorMessage + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool AsFbxInitializeContext(IntPtr context, IntPtr strFileName, float scaleFactor, int versionIndex, [MarshalAs(UnmanagedType.Bool)] bool isAscii, [MarshalAs(UnmanagedType.Bool)] bool is60Fps, out IntPtr strErrorMessage); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxDisposeContext(ref IntPtr ppContext); + + private static void AsFbxSetFramePaths(IntPtr context, string[] framePaths) + { + var framePathCount = framePaths.Length; + + if (framePathCount == 0) + { + AsFbxSetFramePaths(context, Array.Empty(), 0); + } + else + { + var utf8Paths = new Utf8StringHandle[framePathCount]; + + try + { + for (var i = 0; i < framePathCount; i += 1) + { + utf8Paths[i] = new Utf8StringHandle(framePaths[i]); + } + + var pathPointers = new IntPtr[framePathCount]; + + for (var i = 0; i < framePathCount; i += 1) + { + pathPointers[i] = utf8Paths[i].DangerousGetHandle(); + } + + AsFbxSetFramePaths(context, pathPointers, framePathCount); + } + finally + { + foreach (var path in utf8Paths) + { + path?.Dispose(); + } + } + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxSetFramePaths(IntPtr context, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] strFramePaths, int count); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxExportScene(IntPtr context); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxGetSceneRootNode(IntPtr context); + + private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Vector3 localRotation, in Vector3 localScale) + { + using (var framePathUtf8 = new Utf8StringHandle(framePath)) + { + using (var frameNameUtf8 = new Utf8StringHandle(frameName)) + { + return AsFbxExportSingleFrame(context, parentNode, framePathUtf8.DangerousGetHandle(), frameNameUtf8.DangerousGetHandle(), localPosition.X, localPosition.Y, localPosition.Z, localRotation.X, localRotation.Y, localRotation.Z, localScale.X, localScale.Y, localScale.Z); + } + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, IntPtr strFramePath, IntPtr strFrameName, float localPositionX, float localPositionY, float localPositionZ, float localRotationX, float localRotationY, float localRotationZ, float localScaleX, float localScaleY, float localScaleZ); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxSetJointsNode_CastToBone(IntPtr context, IntPtr node, float boneSize); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxSetJointsNode_BoneInPath(IntPtr context, IntPtr node, float boneSize); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxSetJointsNode_Generic(IntPtr context, IntPtr node); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxPrepareMaterials(IntPtr context, int materialCount, int textureCount); + + private static IntPtr AsFbxCreateTexture(IntPtr context, string matTexName) + { + using (var matTexNameUtf8 = new Utf8StringHandle(matTexName)) + { + return AsFbxCreateTexture(context, matTexNameUtf8.DangerousGetHandle()); + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxCreateTexture(IntPtr context, IntPtr strMatTexName); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxLinkTexture(int dest, IntPtr texture, IntPtr material, float offsetX, float offsetY, float scaleX, float scaleY); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxMeshCreateClusterArray(int boneCount); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshDisposeClusterArray(ref IntPtr ppArray); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxMeshCreateCluster(IntPtr context, IntPtr boneNode); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshAddCluster(IntPtr array, IntPtr cluster); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxMeshCreateMesh(IntPtr context, IntPtr frameNode); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshInitControlPoints(IntPtr mesh, int vertexCount); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshCreateElementNormal(IntPtr mesh); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshCreateElementUV(IntPtr mesh, int uv); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshCreateElementTangent(IntPtr mesh); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshCreateElementVertexColor(IntPtr mesh); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshCreateElementMaterial(IntPtr mesh); + + private static IntPtr AsFbxCreateMaterial(IntPtr pContext, string matName, in Color diffuse, in Color ambient, in Color emissive, in Color specular, in Color reflection, float shininess, float transparency) + { + using (var matNameUtf8 = new Utf8StringHandle(matName)) + { + return AsFbxCreateMaterial(pContext, matNameUtf8.DangerousGetHandle(), diffuse.R, diffuse.G, diffuse.B, ambient.R, ambient.G, ambient.B, emissive.R, emissive.G, emissive.B, specular.R, specular.G, specular.B, reflection.R, reflection.G, reflection.B, shininess, transparency); + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxCreateMaterial(IntPtr pContext, IntPtr pMatName, + float diffuseR, float diffuseG, float diffuseB, + float ambientR, float ambientG, float ambientB, + float emissiveR, float emissiveG, float emissiveB, + float specularR, float specularG, float specularB, + float reflectR, float reflectG, float reflectB, + float shininess, float transparency); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern int AsFbxAddMaterialToFrame(IntPtr frameNode, IntPtr material); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxSetFrameShadingModeToTextureShading(IntPtr frameNode); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshSetControlPoint(IntPtr mesh, int index, float x, float y, float z); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshAddPolygon(IntPtr mesh, int materialIndex, int index0, int index1, int index2); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshElementNormalAdd(IntPtr mesh, int elementIndex, float x, float y, float z); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshElementUVAdd(IntPtr mesh, int elementIndex, float u, float v); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshElementTangentAdd(IntPtr mesh, int elementIndex, float x, float y, float z, float w); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshElementVertexColorAdd(IntPtr mesh, int elementIndex, float r, float g, float b, float a); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshSetBoneWeight(IntPtr pClusterArray, int boneIndex, int vertexIndex, float weight); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxMeshCreateSkinContext(IntPtr context, IntPtr frameNode); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshDisposeSkinContext(ref IntPtr ppSkinContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool FbxClusterArray_HasItemAt(IntPtr pClusterArray, int index); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private unsafe static extern void AsFbxMeshSkinAddCluster(IntPtr pSkinContext, IntPtr pClusterArray, int index, float* pBoneMatrix); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMeshAddDeformer(IntPtr pSkinContext, IntPtr pMesh); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxAnimCreateContext([MarshalAs(UnmanagedType.Bool)] bool eulerFilter); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimDisposeContext(ref IntPtr ppAnimContext); + + private static void AsFbxAnimPrepareStackAndLayer(IntPtr pContext, IntPtr pAnimContext, string takeName) + { + using (var takeNameUtf8 = new Utf8StringHandle(takeName)) + { + AsFbxAnimPrepareStackAndLayer(pContext, pAnimContext, takeNameUtf8.DangerousGetHandle()); + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimPrepareStackAndLayer(IntPtr pContext, IntPtr pAnimContext, IntPtr strTakeName); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimLoadCurves(IntPtr pNode, IntPtr pAnimContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimBeginKeyModify(IntPtr pAnimContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimEndKeyModify(IntPtr pAnimContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimAddScalingKey(IntPtr pAnimContext, float time, float x, float y, float z); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimAddRotationKey(IntPtr pAnimContext, float time, float x, float y, float z); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimAddTranslationKey(IntPtr pAnimContext, float time, float x, float y, float z); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimApplyEulerFilter(IntPtr pAnimContext, float filterPrecision); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern int AsFbxAnimGetCurrentBlendShapeChannelCount(IntPtr pAnimContext, IntPtr pNode); + + private static bool AsFbxAnimIsBlendShapeChannelMatch(IntPtr pAnimContext, int channelIndex, string channelName) + { + using (var channelNameUtf8 = new Utf8StringHandle(channelName)) + { + return AsFbxAnimIsBlendShapeChannelMatch(pAnimContext, channelIndex, channelNameUtf8.DangerousGetHandle()); + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool AsFbxAnimIsBlendShapeChannelMatch(IntPtr pAnimContext, int channelIndex, IntPtr strChannelName); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimBeginBlendShapeAnimCurve(IntPtr pAnimContext, int channelIndex); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimEndBlendShapeAnimCurve(IntPtr pAnimContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxAnimAddBlendShapeKeyframe(IntPtr pAnimContext, float time, float value); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern IntPtr AsFbxMorphCreateContext(); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphInitializeContext(IntPtr pContext, IntPtr pMorphContext, IntPtr pNode); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphDisposeContext(ref IntPtr ppMorphContext); + + private static void AsFbxMorphAddBlendShapeChannel(IntPtr pContext, IntPtr pMorphContext, string channelName) + { + using (var channelNameUtf8 = new Utf8StringHandle(channelName)) + { + AsFbxMorphAddBlendShapeChannel(pContext, pMorphContext, channelNameUtf8.DangerousGetHandle()); + } + } + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphAddBlendShapeChannel(IntPtr pContext, IntPtr pMorphContext, IntPtr strChannelName); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphAddBlendShapeChannelShape(IntPtr pContext, IntPtr pMorphContext, float weight); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphCopyBlendShapeControlPoints(IntPtr pMorphContext); + + [DllImport(FbxDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void AsFbxMorphSetBlendShapeVertex(IntPtr pMorphContext, uint index, float x, float y, float z); + + } +} diff --git a/AssetStudioFBXWrapper/FbxExporterContext.cs b/AssetStudioFBXWrapper/FbxExporterContext.cs new file mode 100644 index 0000000..b5762f6 --- /dev/null +++ b/AssetStudioFBXWrapper/FbxExporterContext.cs @@ -0,0 +1,629 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; + +namespace AssetStudio.FbxInterop +{ + internal sealed partial class FbxExporterContext : IDisposable + { + + private IntPtr _pContext; + private readonly Dictionary _frameToNode; + private readonly List> _createdMaterials; + private readonly Dictionary _createdTextures; + + public FbxExporterContext() + { + _pContext = AsFbxCreateContext(); + _frameToNode = new Dictionary(); + _createdMaterials = new List>(); + _createdTextures = new Dictionary(); + } + + ~FbxExporterContext() + { + Dispose(false); + } + + public void Dispose() + { + if (IsDisposed) + { + return; + } + + Dispose(true); + GC.SuppressFinalize(this); + } + + public bool IsDisposed { get; private set; } + + private void Dispose(bool disposing) + { + IsDisposed = true; + + _frameToNode.Clear(); + _createdMaterials.Clear(); + _createdTextures.Clear(); + + AsFbxDisposeContext(ref _pContext); + } + + private void EnsureNotDisposed() + { + if (IsDisposed) + { + throw new ObjectDisposedException(nameof(FbxExporterContext)); + } + } + + internal void Initialize(string fileName, float scaleFactor, int versionIndex, bool isAscii, bool is60Fps) + { + EnsureNotDisposed(); + + var b = AsFbxInitializeContext(_pContext, fileName, scaleFactor, versionIndex, isAscii, is60Fps, out var errorMessage); + + if (!b) + { + var fullMessage = $"Failed to initialize FbxExporter: {errorMessage}"; + throw new ApplicationException(fullMessage); + } + } + + internal void SetFramePaths(HashSet framePaths) + { + EnsureNotDisposed(); + + if (framePaths == null || framePaths.Count == 0) + { + return; + } + + var framePathList = new List(framePaths); + var framePathArray = framePathList.ToArray(); + + AsFbxSetFramePaths(_pContext, framePathArray); + } + + internal void ExportScene() + { + EnsureNotDisposed(); + + AsFbxExportScene(_pContext); + } + + internal void ExportFrame(List meshList, List meshFrames, ImportedFrame rootFrame) + { + var rootNode = AsFbxGetSceneRootNode(_pContext); + + Debug.Assert(rootNode != IntPtr.Zero); + + var nodeStack = new Stack(); + var frameStack = new Stack(); + + nodeStack.Push(rootNode); + frameStack.Push(rootFrame); + + while (nodeStack.Count > 0) + { + var parentNode = nodeStack.Pop(); + var frame = frameStack.Pop(); + + var childNode = AsFbxExportSingleFrame(_pContext, parentNode, frame.Path, frame.Name, frame.LocalPosition, frame.LocalRotation, frame.LocalScale); + + if (meshList != null && ImportedHelpers.FindMesh(frame.Path, meshList) != null) + { + meshFrames.Add(frame); + } + + _frameToNode.Add(frame, childNode); + + for (var i = frame.Count - 1; i >= 0; i -= 1) + { + nodeStack.Push(childNode); + frameStack.Push(frame[i]); + } + } + } + + internal void SetJointsNode(ImportedFrame rootFrame, HashSet bonePaths, bool castToBone, float boneSize) + { + var frameStack = new Stack(); + + frameStack.Push(rootFrame); + + while (frameStack.Count > 0) + { + var frame = frameStack.Pop(); + + if (_frameToNode.TryGetValue(frame, out var node)) + { + Debug.Assert(node != IntPtr.Zero); + + if (castToBone) + { + AsFbxSetJointsNode_CastToBone(_pContext, node, boneSize); + } + else + { + Debug.Assert(bonePaths != null); + + if (bonePaths.Contains(frame.Path)) + { + AsFbxSetJointsNode_BoneInPath(_pContext, node, boneSize); + } + else + { + AsFbxSetJointsNode_Generic(_pContext, node); + } + } + } + + for (var i = frame.Count - 1; i >= 0; i -= 1) + { + frameStack.Push(frame[i]); + } + } + } + + internal void PrepareMaterials(int materialCount, int textureCount) + { + AsFbxPrepareMaterials(_pContext, materialCount, textureCount); + } + + internal void ExportMeshFromFrame(ImportedFrame rootFrame, ImportedFrame meshFrame, List meshList, List materialList, List textureList, bool exportSkins) + { + var meshNode = _frameToNode[meshFrame]; + var mesh = ImportedHelpers.FindMesh(meshFrame.Path, meshList); + + ExportMesh(rootFrame, materialList, textureList, meshNode, mesh, exportSkins); + } + + private IntPtr ExportTexture(ImportedTexture texture) + { + if (texture == null) + { + return IntPtr.Zero; + } + + if (_createdTextures.ContainsKey(texture.Name)) + { + return _createdTextures[texture.Name]; + } + + var pTex = AsFbxCreateTexture(_pContext, texture.Name); + + _createdTextures.Add(texture.Name, pTex); + + var file = new FileInfo(texture.Name); + + using (var writer = new BinaryWriter(file.Create())) + { + writer.Write(texture.Data); + } + + return pTex; + } + + private void ExportMesh(ImportedFrame rootFrame, List materialList, List textureList, IntPtr frameNode, ImportedMesh importedMesh, bool exportSkins) + { + var boneList = importedMesh.BoneList; + var totalBoneCount = boneList.Count; + var hasBones = exportSkins && boneList != null && totalBoneCount > 0; + + var pClusterArray = IntPtr.Zero; + + try + { + if (hasBones) + { + pClusterArray = AsFbxMeshCreateClusterArray(totalBoneCount); + + foreach (var bone in boneList) + { + if (bone.Path != null) + { + var frame = rootFrame.FindFrameByPath(bone.Path); + var boneNode = _frameToNode[frame]; + + var cluster = AsFbxMeshCreateCluster(_pContext, boneNode); + + AsFbxMeshAddCluster(pClusterArray, cluster); + } + else + { + AsFbxMeshAddCluster(pClusterArray, IntPtr.Zero); + } + } + } + + var mesh = AsFbxMeshCreateMesh(_pContext, frameNode); + + var totalVertexCount = 0; + + foreach (var m in importedMesh.SubmeshList) + { + totalVertexCount += m.VertexList.Count; + } + + AsFbxMeshInitControlPoints(mesh, totalVertexCount); + + if (importedMesh.hasNormal) + { + AsFbxMeshCreateElementNormal(mesh); + } + + for (var i = 0; i < 2; i += 1) + { + if (importedMesh.hasUV[i]) + { + AsFbxMeshCreateElementUV(mesh, i); + } + } + + if (importedMesh.hasTangent) + { + AsFbxMeshCreateElementTangent(mesh); + } + + if (importedMesh.hasColor) + { + AsFbxMeshCreateElementVertexColor(mesh); + } + + AsFbxMeshCreateElementMaterial(mesh); + + var firstVertex = 0; + + foreach (var meshObj in importedMesh.SubmeshList) + { + var materialIndex = 0; + var mat = ImportedHelpers.FindMaterial(meshObj.Material, materialList); + + if (mat != null) + { + var foundMat = _createdMaterials.FindIndex(kv => kv.Key == mat.Name); + IntPtr pMat; + + if (foundMat >= 0) + { + pMat = _createdMaterials[foundMat].Value; + } + else + { + var diffuse = mat.Diffuse; + var ambient = mat.Ambient; + var emissive = mat.Emissive; + var specular = mat.Specular; + var reflection = mat.Reflection; + + pMat = AsFbxCreateMaterial(_pContext, mat.Name, in diffuse, in ambient, in emissive, in specular, in reflection, mat.Shininess, mat.Transparency); + + _createdMaterials.Add(new KeyValuePair(mat.Name, pMat)); + } + + materialIndex = AsFbxAddMaterialToFrame(frameNode, pMat); + + var hasTexture = false; + + foreach (var texture in mat.Textures) + { + var tex = ImportedHelpers.FindTexture(texture.Name, textureList); + var pTexture = ExportTexture(tex); + + if (pTexture != IntPtr.Zero) + { + switch (texture.Dest) + { + case 0: + case 1: + case 2: + case 3: + { + AsFbxLinkTexture(texture.Dest, pTexture, pMat, texture.Offset.X, texture.Offset.Y, texture.Scale.X, texture.Scale.Y); + hasTexture = true; + break; + } + default: + break; + } + } + } + + if (hasTexture) + { + AsFbxSetFrameShadingModeToTextureShading(frameNode); + } + } + + var vertexList = meshObj.VertexList; + + var vertexCount = vertexList.Count; + + for (var j = 0; j < vertexCount; j += 1) + { + var importedVertex = vertexList[j]; + + var vertex = importedVertex.Vertex; + AsFbxMeshSetControlPoint(mesh, j + firstVertex, vertex.X, vertex.Y, vertex.Z); + + if (importedMesh.hasNormal) + { + var normal = importedVertex.Normal; + AsFbxMeshElementNormalAdd(mesh, 0, normal.X, normal.Y, normal.Z); + } + + for (var uvIndex = 0; uvIndex < 2; uvIndex += 1) + { + if (importedMesh.hasUV[uvIndex]) + { + var uv = importedVertex.UV[uvIndex]; + AsFbxMeshElementUVAdd(mesh, uvIndex, uv[0], uv[1]); + } + } + + if (importedMesh.hasTangent) + { + var tangent = importedVertex.Tangent; + AsFbxMeshElementTangentAdd(mesh, 0, tangent.X, tangent.Y, tangent.Z, tangent.W); + } + + if (importedMesh.hasColor) + { + var color = importedVertex.Color; + AsFbxMeshElementVertexColorAdd(mesh, 0, color.R, color.G, color.B, color.A); + } + + if (hasBones && importedVertex.BoneIndices != null) + { + var boneIndices = importedVertex.BoneIndices; + var boneWeights = importedVertex.Weights; + + for (var k = 0; k < 4; k += 1) + { + if (boneIndices[k] < totalBoneCount && boneWeights[k] > 0) + { + AsFbxMeshSetBoneWeight(pClusterArray, boneIndices[k], j + firstVertex, boneWeights[k]); + } + } + } + } + + foreach (var face in meshObj.FaceList) + { + var index0 = face.VertexIndices[0] + firstVertex; + var index1 = face.VertexIndices[1] + firstVertex; + var index2 = face.VertexIndices[2] + firstVertex; + + AsFbxMeshAddPolygon(mesh, materialIndex, index0, index1, index2); + } + + firstVertex += vertexCount; + } + + if (hasBones) + { + IntPtr pSkinContext = IntPtr.Zero; + + try + { + pSkinContext = AsFbxMeshCreateSkinContext(_pContext, frameNode); + + unsafe + { + var boneMatrix = stackalloc float[16]; + + for (var j = 0; j < totalBoneCount; j += 1) + { + if (!FbxClusterArray_HasItemAt(pClusterArray, j)) + { + continue; + } + + var m = boneList[j].Matrix; + + CopyMatrix4x4(in m, boneMatrix); + + AsFbxMeshSkinAddCluster(pSkinContext, pClusterArray, j, boneMatrix); + } + } + + AsFbxMeshAddDeformer(pSkinContext, mesh); + } + finally + { + AsFbxMeshDisposeSkinContext(ref pSkinContext); + } + } + } + finally + { + AsFbxMeshDisposeClusterArray(ref pClusterArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void CopyMatrix4x4(in Matrix4x4 matrix, float* buffer) + { + for (var m = 0; m < 4; m += 1) + { + for (var n = 0; n < 4; n += 1) + { + var index = IndexFrom4x4(m, n); + buffer[index] = matrix[m, n]; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int IndexFrom4x4(int m, int n) + { + return 4 * m + n; + } + + internal void ExportAnimations(ImportedFrame rootFrame, List animationList, bool eulerFilter, float filterPrecision) + { + if (animationList == null || animationList.Count == 0) + { + return; + } + + var pAnimContext = IntPtr.Zero; + + try + { + pAnimContext = AsFbxAnimCreateContext(eulerFilter); + + for (int i = 0; i < animationList.Count; i++) + { + var importedAnimation = animationList[i]; + string takeName; + + if (importedAnimation.Name != null) + { + takeName = importedAnimation.Name; + } + else + { + takeName = $"Take{i.ToString()}"; + } + + AsFbxAnimPrepareStackAndLayer(_pContext, pAnimContext, takeName); + + ExportKeyframedAnimation(rootFrame, importedAnimation, pAnimContext, filterPrecision); + } + } + finally + { + AsFbxAnimDisposeContext(ref pAnimContext); + } + } + + private void ExportKeyframedAnimation(ImportedFrame rootFrame, ImportedKeyframedAnimation parser, IntPtr pAnimContext, float filterPrecision) + { + foreach (var track in parser.TrackList) + { + if (track.Path == null) + { + continue; + } + + var frame = rootFrame.FindFrameByPath(track.Path); + + if (frame == null) + { + continue; + } + + var pNode = _frameToNode[frame]; + + AsFbxAnimLoadCurves(pNode, pAnimContext); + + AsFbxAnimBeginKeyModify(pAnimContext); + + foreach (var scaling in track.Scalings) + { + var value = scaling.value; + AsFbxAnimAddScalingKey(pAnimContext, scaling.time, value.X, value.Y, value.Z); + } + + foreach (var rotation in track.Rotations) + { + var value = rotation.value; + AsFbxAnimAddRotationKey(pAnimContext, rotation.time, value.X, value.Y, value.Z); + } + + foreach (var translation in track.Translations) + { + var value = translation.value; + AsFbxAnimAddTranslationKey(pAnimContext, translation.time, value.X, value.Y, value.Z); + } + + AsFbxAnimEndKeyModify(pAnimContext); + + AsFbxAnimApplyEulerFilter(pAnimContext, filterPrecision); + + var blendShape = track.BlendShape; + + if (blendShape != null) + { + var channelCount = AsFbxAnimGetCurrentBlendShapeChannelCount(pAnimContext, pNode); + + if (channelCount > 0) + { + for (var channelIndex = 0; channelIndex < channelCount; channelIndex += 1) + { + if (!AsFbxAnimIsBlendShapeChannelMatch(pAnimContext, channelIndex, blendShape.ChannelName)) + { + continue; + } + + AsFbxAnimBeginBlendShapeAnimCurve(pAnimContext, channelIndex); + + foreach (var keyframe in blendShape.Keyframes) + { + AsFbxAnimAddBlendShapeKeyframe(pAnimContext, keyframe.time, keyframe.value); + } + + AsFbxAnimEndBlendShapeAnimCurve(pAnimContext); + } + } + } + } + } + + internal void ExportMorphs(ImportedFrame rootFrame, List morphList) + { + if (morphList == null || morphList.Count == 0) + { + return; + } + + foreach (var morph in morphList) + { + var frame = rootFrame.FindFrameByPath(morph.Path); + + if (frame == null) + { + continue; + } + + var pNode = _frameToNode[frame]; + + var pMorphContext = IntPtr.Zero; + + try + { + pMorphContext = AsFbxMorphCreateContext(); + + AsFbxMorphInitializeContext(_pContext, pMorphContext, pNode); + + foreach (var channel in morph.Channels) + { + AsFbxMorphAddBlendShapeChannel(_pContext, pMorphContext, channel.Name); + + foreach (var keyframe in channel.KeyframeList) + { + AsFbxMorphAddBlendShapeChannelShape(_pContext, pMorphContext, keyframe.Weight); + + AsFbxMorphCopyBlendShapeControlPoints(pMorphContext); + + foreach (var vertex in keyframe.VertexList) + { + var v = vertex.Vertex.Vertex; + AsFbxMorphSetBlendShapeVertex(pMorphContext, vertex.Index, v.X, v.Y, v.Z); + } + } + } + } + finally + { + AsFbxMorphDisposeContext(ref pMorphContext); + } + } + } + + } +} \ No newline at end of file diff --git a/AssetStudioFBXWrapper/Properties/AssemblyInfo.cs b/AssetStudioFBXWrapper/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c7e3ed8 --- /dev/null +++ b/AssetStudioFBXWrapper/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AssetStudioFBXWrapper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AssetStudioFBXWrapper")] +[assembly: AssemblyCopyright("Copyright © Perfare 2018-2020; Copyright © hozuki 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("bd76e63f-1517-47fa-8233-33e853a3acee")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AssetStudioGUI/AssetStudioGUI.csproj b/AssetStudioGUI/AssetStudioGUI.csproj index 134cd58..aa436bb 100644 --- a/AssetStudioGUI/AssetStudioGUI.csproj +++ b/AssetStudioGUI/AssetStudioGUI.csproj @@ -13,53 +13,31 @@ true true - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - true - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - true - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - 7.3 - prompt - MinimumRecommendedRules.ruleset - true - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - 7.3 - prompt - MinimumRecommendedRules.ruleset - true - Resources\as.ico + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + 7.3 + prompt + MinimumRecommendedRules.ruleset + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + 7.3 + prompt + MinimumRecommendedRules.ruleset + false + ..\packages\OpenTK.3.1.0\lib\net20\OpenTK.dll @@ -144,24 +122,22 @@ - + PreserveNewest - fmod.dll + x86\fmod.dll PreserveNewest - libfbxsdk.dll + x86\libfbxsdk.dll - - PreserveNewest - fmod.dll + x64\fmod.dll PreserveNewest - libfbxsdk.dll + x64\libfbxsdk.dll diff --git a/AssetStudioUtility/AssetStudioUtility.csproj b/AssetStudioUtility/AssetStudioUtility.csproj index e0acd12..0e1b2fc 100644 --- a/AssetStudioUtility/AssetStudioUtility.csproj +++ b/AssetStudioUtility/AssetStudioUtility.csproj @@ -13,42 +13,22 @@ 512 true - + true - bin\x64\Debug\ + bin\Debug\ DEBUG;TRACE full - x64 + AnyCPU 7.3 prompt MinimumRecommendedRules.ruleset - - bin\x64\Release\ + + bin\Release\ TRACE true pdbonly - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - 7.3 - prompt - MinimumRecommendedRules.ruleset - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 + AnyCPU 7.3 prompt MinimumRecommendedRules.ruleset @@ -82,17 +62,21 @@ - - {b82dd1ba-4eec-4f29-a686-03d7f0df39b8} - AssetStudioFBX + + {40c796b5-88ce-4adc-acd6-2f4862b7f136} + AssetStudio.PInvoke + + + {bd76e63f-1517-47fa-8233-33e853a3acee} + AssetStudioFBXWrapper {7662f8c2-7bfd-442e-a948-a43b4f7eb06e} AssetStudio - - {bec7b5e6-0a7b-4824-97a7-eea04d9eba29} - Texture2DDecoder + + {2afce830-b463-49b3-a026-877e5eafc0a4} + Texture2DDecoderWrapper diff --git a/AssetStudioUtility/FMOD Studio API/fmod.cs b/AssetStudioUtility/FMOD Studio API/fmod.cs index 75439d2..b82c8a9 100644 --- a/AssetStudioUtility/FMOD Studio API/fmod.cs +++ b/AssetStudioUtility/FMOD Studio API/fmod.cs @@ -7,6 +7,7 @@ using System; using System.Text; using System.Runtime.InteropServices; +using AssetStudio.PInvoke; namespace FMOD { @@ -1587,6 +1588,12 @@ namespace FMOD */ public class Factory { + + static Factory() + { + DllLoader.PreloadDll(VERSION.dll); + } + public static RESULT System_Create(out System system) { system = null; diff --git a/Texture2DDecoder/AssemblyInfo.cpp b/Texture2DDecoder/AssemblyInfo.cpp deleted file mode 100644 index 8a147b6..0000000 --- a/Texture2DDecoder/AssemblyInfo.cpp +++ /dev/null @@ -1,20 +0,0 @@ -using namespace System; -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; -using namespace System::Security::Permissions; - -[assembly:AssemblyTitleAttribute(L"Texture2DDecoder")]; -[assembly:AssemblyDescriptionAttribute(L"")]; -[assembly:AssemblyConfigurationAttribute(L"")]; -[assembly:AssemblyCompanyAttribute(L"")]; -[assembly:AssemblyProductAttribute(L"Texture2DDecoder")]; -[assembly:AssemblyCopyrightAttribute(L"Copyright © Perfare 2020")]; -[assembly:AssemblyTrademarkAttribute(L"")]; -[assembly:AssemblyCultureAttribute(L"")]; - -[assembly:AssemblyVersionAttribute("1.0.*")]; - -[assembly:ComVisible(false)]; - -[assembly:CLSCompliantAttribute(true)]; diff --git a/Texture2DDecoder/Texture2DDecoder.cpp b/Texture2DDecoder/Texture2DDecoder.cpp deleted file mode 100644 index 02a41aa..0000000 --- a/Texture2DDecoder/Texture2DDecoder.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include "Texture2DDecoder.h" -#include "bcn.h" -#include "pvrtc.h" -#include "etc.h" -#include "atc.h" -#include "astc.h" -#include "crunch.h" -#include "unitycrunch.h" - -namespace Texture2DDecoder { - bool TextureDecoder::DecodeDXT1(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc1(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeDXT5(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc3(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodePVRTC(array^ data, long w, long h, array^ image, bool is2bpp) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_pvrtc(dataPin, w, h, reinterpret_cast(imagePin), is2bpp ? 1 : 0); - } - - bool TextureDecoder::DecodeETC1(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_etc1(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeETC2(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_etc2(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeETC2A1(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_etc2a1(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeETC2A8(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_etc2a8(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeEACR(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_eacr(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeEACRSigned(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_eacr_signed(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeEACRG(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_eacrg(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeEACRGSigned(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_eacrg_signed(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeBC4(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc4(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeBC5(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc5(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeBC6(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc6(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeBC7(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_bc7(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeATCRGB4(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_atc_rgb4(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeATCRGBA8(array^ data, long w, long h, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_atc_rgba8(dataPin, w, h, reinterpret_cast(imagePin)); - } - - bool TextureDecoder::DecodeASTC(array^ data, long w, long h, int bw, int bh, array^ image) { - pin_ptr dataPin = &data[0]; - pin_ptr imagePin = &image[0]; - return decode_astc(dataPin, w, h, bw, bh, reinterpret_cast(imagePin)); - } - - array^ TextureDecoder::UnpackCrunch(array^ data) { - pin_ptr dataPin = &data[0]; - void* ret; - uint32_t retSize; - if (!crunch_unpack_level(dataPin, data->Length, 0, &ret, &retSize)) { - return nullptr; - } - auto buff = gcnew array(retSize); - pin_ptr buffPin = &buff[0]; - memcpy(buffPin, ret, retSize); - delete ret; - return buff; - } - - array^ TextureDecoder::UnpackUnityCrunch(array^ data) { - pin_ptr dataPin = &data[0]; - void* ret; - uint32_t retSize; - if (!unity_crunch_unpack_level(dataPin, data->Length, 0, &ret, &retSize)) { - return nullptr; - } - auto buff = gcnew array(retSize); - pin_ptr buffPin = &buff[0]; - memcpy(buffPin, ret, retSize); - delete ret; - return buff; - } -} - diff --git a/Texture2DDecoder/Texture2DDecoder.h b/Texture2DDecoder/Texture2DDecoder.h deleted file mode 100644 index 030d2e6..0000000 --- a/Texture2DDecoder/Texture2DDecoder.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -using namespace System; - -namespace Texture2DDecoder { - public ref class TextureDecoder - { - public: - static bool DecodeDXT1(array^ data, long w, long h, array^ image); - static bool DecodeDXT5(array^ data, long w, long h, array^ image); - static bool DecodePVRTC(array^ data, long w, long h, array^ image, bool is2bpp); - static bool DecodeETC1(array^ data, long w, long h, array^ image); - static bool DecodeETC2(array^ data, long w, long h, array^ image); - static bool DecodeETC2A1(array^ data, long w, long h, array^ image); - static bool DecodeETC2A8(array^ data, long w, long h, array^ image); - static bool DecodeEACR(array^ data, long w, long h, array^ image); - static bool DecodeEACRSigned(array^ data, long w, long h, array^ image); - static bool DecodeEACRG(array^ data, long w, long h, array^ image); - static bool DecodeEACRGSigned(array^ data, long w, long h, array^ image); - static bool DecodeBC4(array^ data, long w, long h, array^ image); - static bool DecodeBC5(array^ data, long w, long h, array^ image); - static bool DecodeBC6(array^ data, long w, long h, array^ image); - static bool DecodeBC7(array^ data, long w, long h, array^ image); - static bool DecodeATCRGB4(array^ data, long w, long h, array^ image); - static bool DecodeATCRGBA8(array^ data, long w, long h, array^ image); - static bool DecodeASTC(array^ data, long w, long h, int bw, int bh, array^ image); - static array^ UnpackCrunch(array^ data); - static array^ UnpackUnityCrunch(array^ data); - }; -} \ No newline at end of file diff --git a/Texture2DDecoderNative/Texture2DDecoderNative.rc b/Texture2DDecoderNative/Texture2DDecoderNative.rc new file mode 100644 index 0000000..fa24168 --- /dev/null +++ b/Texture2DDecoderNative/Texture2DDecoderNative.rc @@ -0,0 +1,99 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Language neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL +#pragma code_page(65001) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "FileDescription", "Texture2DDecoderNative" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "Texture2DDecoderNative.dll" + VALUE "LegalCopyright", "Copyright (C) Perfare 2020; Copyright (C) hozuki 2020" + VALUE "OriginalFilename", "Texture2DDecoderNative.dll" + VALUE "ProductName", "Texture2DDecoderNative" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // Language neutral resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Texture2DDecoder/Texture2DDecoder.vcxproj b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj similarity index 58% rename from Texture2DDecoder/Texture2DDecoder.vcxproj rename to Texture2DDecoderNative/Texture2DDecoderNative.vcxproj index 8d6df7d..5e9b01a 100644 --- a/Texture2DDecoder/Texture2DDecoder.vcxproj +++ b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj @@ -18,12 +18,49 @@ x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 - {BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29} - v4.7.2 - ManagedCProj - Texture2DDecoder + Win32Proj + {29356642-c46e-4144-83d8-22dc09d0d7fd} + Texture2DDecoderNative 10.0.18362.0 @@ -31,28 +68,26 @@ DynamicLibrary true v141 - true Unicode DynamicLibrary false v141 - true + true Unicode DynamicLibrary true v141 - true Unicode DynamicLibrary false v141 - true + true Unicode @@ -73,74 +108,94 @@ - + + true + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + true + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + Level3 - WIN32;_DEBUG;%(PreprocessorDefinitions) + true + _T2D_DLL;WIN32;_DEBUG;TEXTURE2DDECODERNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + stdafx.h - - - - - - Level3 - _DEBUG;%(PreprocessorDefinitions) - - - + Windows + true + false Level3 - WIN32;NDEBUG;%(PreprocessorDefinitions) + true + true + true + _T2D_DLL;WIN32;NDEBUG;TEXTURE2DDECODERNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + stdafx.h - + Windows + true + true + true + false + + + + + Level3 + true + _T2D_DLL;_DEBUG;TEXTURE2DDECODERNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + stdafx.h + + + Windows + true + false Level3 - NDEBUG;%(PreprocessorDefinitions) + true + true + true + _T2D_DLL;NDEBUG;TEXTURE2DDECODERNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + stdafx.h - + Windows + true + true + true + false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Texture2DDecoder/Texture2DDecoder.vcxproj.filters b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj.filters similarity index 86% rename from Texture2DDecoder/Texture2DDecoder.vcxproj.filters rename to Texture2DDecoderNative/Texture2DDecoderNative.vcxproj.filters index b62082a..0d2ff69 100644 --- a/Texture2DDecoder/Texture2DDecoder.vcxproj.filters +++ b/Texture2DDecoderNative/Texture2DDecoderNative.vcxproj.filters @@ -15,6 +15,33 @@ + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + 头文件 @@ -39,39 +66,18 @@ 头文件 - + 头文件 - + 头文件 - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - + 头文件 - + 源文件 @@ -92,11 +98,16 @@ 源文件 - - 源文件 - - + 源文件 + + + + + + 资源文件 + + \ No newline at end of file diff --git a/Texture2DDecoder/astc.cpp b/Texture2DDecoderNative/astc.cpp similarity index 100% rename from Texture2DDecoder/astc.cpp rename to Texture2DDecoderNative/astc.cpp diff --git a/Texture2DDecoder/astc.h b/Texture2DDecoderNative/astc.h similarity index 100% rename from Texture2DDecoder/astc.h rename to Texture2DDecoderNative/astc.h diff --git a/Texture2DDecoder/atc.cpp b/Texture2DDecoderNative/atc.cpp similarity index 100% rename from Texture2DDecoder/atc.cpp rename to Texture2DDecoderNative/atc.cpp diff --git a/Texture2DDecoder/atc.h b/Texture2DDecoderNative/atc.h similarity index 100% rename from Texture2DDecoder/atc.h rename to Texture2DDecoderNative/atc.h diff --git a/Texture2DDecoder/bcn.cpp b/Texture2DDecoderNative/bcn.cpp similarity index 100% rename from Texture2DDecoder/bcn.cpp rename to Texture2DDecoderNative/bcn.cpp diff --git a/Texture2DDecoder/bcn.h b/Texture2DDecoderNative/bcn.h similarity index 100% rename from Texture2DDecoder/bcn.h rename to Texture2DDecoderNative/bcn.h diff --git a/Texture2DDecoderNative/bool32_t.h b/Texture2DDecoderNative/bool32_t.h new file mode 100644 index 0000000..2b2d6b2 --- /dev/null +++ b/Texture2DDecoderNative/bool32_t.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +typedef uint32_t bool32_t; diff --git a/Texture2DDecoder/color.h b/Texture2DDecoderNative/color.h similarity index 100% rename from Texture2DDecoder/color.h rename to Texture2DDecoderNative/color.h diff --git a/Texture2DDecoderNative/cpp.hint b/Texture2DDecoderNative/cpp.hint new file mode 100644 index 0000000..63084fd --- /dev/null +++ b/Texture2DDecoderNative/cpp.hint @@ -0,0 +1 @@ +#define T2D_API(ret_type) diff --git a/Texture2DDecoder/crunch.cpp b/Texture2DDecoderNative/crunch.cpp similarity index 100% rename from Texture2DDecoder/crunch.cpp rename to Texture2DDecoderNative/crunch.cpp diff --git a/Texture2DDecoder/crunch.h b/Texture2DDecoderNative/crunch.h similarity index 100% rename from Texture2DDecoder/crunch.h rename to Texture2DDecoderNative/crunch.h diff --git a/Texture2DDecoder/crunch/crn_decomp.h b/Texture2DDecoderNative/crunch/crn_decomp.h similarity index 100% rename from Texture2DDecoder/crunch/crn_decomp.h rename to Texture2DDecoderNative/crunch/crn_decomp.h diff --git a/Texture2DDecoder/crunch/crnlib.h b/Texture2DDecoderNative/crunch/crnlib.h similarity index 100% rename from Texture2DDecoder/crunch/crnlib.h rename to Texture2DDecoderNative/crunch/crnlib.h diff --git a/Texture2DDecoderNative/dllexport.h b/Texture2DDecoderNative/dllexport.h new file mode 100644 index 0000000..8a54c4a --- /dev/null +++ b/Texture2DDecoderNative/dllexport.h @@ -0,0 +1,56 @@ +#pragma once + +#if defined(_MSC_VER) +#if _MSC_VER < 1910 // MSVC 2017- +#error MSVC 2017 or later is required. +#endif +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW__) +#ifdef _T2D_DLL +#ifdef __GNUC__ +#define _T2D_EXPORT __attribute__ ((dllexport)) +#else +#define _T2D_EXPORT __declspec(dllexport) +#endif +#else +#ifdef __GNUC__ +#define _T2D_EXPORT __attribute__ ((dllimport)) +#else +#define _T2D_EXPORT __declspec(dllimport) +#endif +#endif +#define _T2D_LOCAL +#else +#if __GNUC__ >= 4 +#define _T2D_EXPORT __attribute__ ((visibility ("default"))) +#define _T2D_LOCAL __attribute__ ((visibility ("hidden"))) +#else +#define _T2D_EXPORT +#define _T2D_LOCAL +#endif +#endif + +#ifdef __cplusplus +#ifndef _EXTERN_C_STMT +#define _EXTERN_C_STMT extern "C" +#endif +#else +#ifndef _EXTERN_C_STMT +#define _EXTERN_C_STMT +#endif +#endif + +#ifndef _T2D_CALL +#if defined(WIN32) || defined(_WIN32) +#define _T2D_CALL __stdcall +#else +#define _T2D_CALL /* __cdecl */ +#endif +#endif + +#if defined(_MSC_VER) +#define T2D_API(ret_type) _EXTERN_C_STMT _T2D_EXPORT ret_type _T2D_CALL +#else +#define T2D_API(ret_type) _EXTERN_C_STMT _T2D_EXPORT _T2D_CALL ret_type +#endif diff --git a/Texture2DDecoderNative/dllmain.cpp b/Texture2DDecoderNative/dllmain.cpp new file mode 100644 index 0000000..086fe7b --- /dev/null +++ b/Texture2DDecoderNative/dllmain.cpp @@ -0,0 +1,174 @@ +#include "dllexport.h" +#include "bool32_t.h" + +#include "bcn.h" +#include "pvrtc.h" +#include "etc.h" +#include "atc.h" +#include "astc.h" +#include "crunch.h" +#include "unitycrunch.h" + +T2D_API(bool32_t) DecodeDXT1(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc1(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeDXT5(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc3(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodePVRTC(const void* data, int32_t width, int32_t height, void* image, bool32_t is2bpp) +{ + return decode_pvrtc(static_cast(data), width, height, static_cast(image), is2bpp ? 1 : 0); +} + +T2D_API(bool32_t) DecodeETC1(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_etc1(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeETC2(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_etc2(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeETC2A1(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_etc2a1(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeETC2A8(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_etc2a8(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeEACR(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_eacr(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeEACRSigned(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_eacr_signed(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeEACRG(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_eacrg(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeEACRGSigned(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_eacrg_signed(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeBC4(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc4(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeBC5(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc5(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeBC6(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc6(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeBC7(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_bc7(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeATCRGB4(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_atc_rgb4(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeATCRGBA8(const void* data, int32_t width, int32_t height, void* image) +{ + return decode_atc_rgba8(static_cast(data), width, height, static_cast(image)); +} + +T2D_API(bool32_t) DecodeASTC(const void* data, int32_t width, int32_t height, int32_t blockWidth, int32_t blockHeight, void* image) +{ + return decode_astc(static_cast(data), width, height, blockWidth, blockHeight, static_cast(image)); +} + +T2D_API(void) DisposeBuffer(void** ppBuffer) +{ + if (ppBuffer == nullptr) + { + return; + } + + auto ppTypedBuffer = reinterpret_cast(ppBuffer); + + delete[](*ppTypedBuffer); + + *ppBuffer = nullptr; +} + +T2D_API(void) UnpackCrunch(const void* data, uint32_t dataSize, void** ppResult, uint32_t* pResultSize) +{ + void* result; + uint32_t resultSize; + + if (ppResult != nullptr) + { + *ppResult = nullptr; + } + + if (pResultSize != nullptr) + { + *pResultSize = 0; + } + + if (!crunch_unpack_level(static_cast(data), dataSize, 0, &result, &resultSize)) { + return; + } + + if (ppResult != nullptr) + { + *ppResult = result; + } + + if (pResultSize != nullptr) + { + *pResultSize = resultSize; + } +} + +T2D_API(void) UnpackUnityCrunch(const void* data, uint32_t dataSize, void** ppResult, uint32_t* pResultSize) +{ + void* result; + uint32_t resultSize; + + if (ppResult != nullptr) + { + *ppResult = nullptr; + } + + if (pResultSize != nullptr) + { + *pResultSize = 0; + } + + if (!unity_crunch_unpack_level(static_cast(data), dataSize, 0, &result, &resultSize)) { + return; + } + + if (ppResult != nullptr) + { + *ppResult = result; + } + + if (pResultSize != nullptr) + { + *pResultSize = resultSize; + } +} diff --git a/Texture2DDecoder/endianness.h b/Texture2DDecoderNative/endianness.h similarity index 100% rename from Texture2DDecoder/endianness.h rename to Texture2DDecoderNative/endianness.h diff --git a/Texture2DDecoder/etc.cpp b/Texture2DDecoderNative/etc.cpp similarity index 100% rename from Texture2DDecoder/etc.cpp rename to Texture2DDecoderNative/etc.cpp diff --git a/Texture2DDecoder/etc.h b/Texture2DDecoderNative/etc.h similarity index 100% rename from Texture2DDecoder/etc.h rename to Texture2DDecoderNative/etc.h diff --git a/Texture2DDecoder/fp16.h b/Texture2DDecoderNative/fp16.h similarity index 100% rename from Texture2DDecoder/fp16.h rename to Texture2DDecoderNative/fp16.h diff --git a/Texture2DDecoder/fp16/bitcasts.h b/Texture2DDecoderNative/fp16/bitcasts.h similarity index 100% rename from Texture2DDecoder/fp16/bitcasts.h rename to Texture2DDecoderNative/fp16/bitcasts.h diff --git a/Texture2DDecoder/fp16/fp16.h b/Texture2DDecoderNative/fp16/fp16.h similarity index 100% rename from Texture2DDecoder/fp16/fp16.h rename to Texture2DDecoderNative/fp16/fp16.h diff --git a/Texture2DDecoder/pvrtc.cpp b/Texture2DDecoderNative/pvrtc.cpp similarity index 100% rename from Texture2DDecoder/pvrtc.cpp rename to Texture2DDecoderNative/pvrtc.cpp diff --git a/Texture2DDecoder/pvrtc.h b/Texture2DDecoderNative/pvrtc.h similarity index 100% rename from Texture2DDecoder/pvrtc.h rename to Texture2DDecoderNative/pvrtc.h diff --git a/Texture2DDecoderNative/resource.h b/Texture2DDecoderNative/resource.h new file mode 100644 index 0000000..9345558 --- /dev/null +++ b/Texture2DDecoderNative/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Texture2DDecoderNative.rc + +// ¶һĬֵ +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Texture2DDecoder/unitycrunch.cpp b/Texture2DDecoderNative/unitycrunch.cpp similarity index 100% rename from Texture2DDecoder/unitycrunch.cpp rename to Texture2DDecoderNative/unitycrunch.cpp diff --git a/Texture2DDecoder/unitycrunch.h b/Texture2DDecoderNative/unitycrunch.h similarity index 100% rename from Texture2DDecoder/unitycrunch.h rename to Texture2DDecoderNative/unitycrunch.h diff --git a/Texture2DDecoder/unitycrunch/crn_decomp.h b/Texture2DDecoderNative/unitycrunch/crn_decomp.h similarity index 100% rename from Texture2DDecoder/unitycrunch/crn_decomp.h rename to Texture2DDecoderNative/unitycrunch/crn_decomp.h diff --git a/Texture2DDecoder/unitycrunch/crn_defs.h b/Texture2DDecoderNative/unitycrunch/crn_defs.h similarity index 100% rename from Texture2DDecoder/unitycrunch/crn_defs.h rename to Texture2DDecoderNative/unitycrunch/crn_defs.h diff --git a/Texture2DDecoder/unitycrunch/crnlib.h b/Texture2DDecoderNative/unitycrunch/crnlib.h similarity index 100% rename from Texture2DDecoder/unitycrunch/crnlib.h rename to Texture2DDecoderNative/unitycrunch/crnlib.h diff --git a/Texture2DDecoderWrapper/Properties/AssemblyInfo.cs b/Texture2DDecoderWrapper/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f26409d --- /dev/null +++ b/Texture2DDecoderWrapper/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Texture2DDecoder")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Texture2DDecoder")] +[assembly: AssemblyCopyright("Copyright © Perfare 2020; Copyright © hozuki 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("2afce830-b463-49b3-a026-877e5eafc0a4")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Texture2DDecoderWrapper/T2DDll.cs b/Texture2DDecoderWrapper/T2DDll.cs new file mode 100644 index 0000000..834d490 --- /dev/null +++ b/Texture2DDecoderWrapper/T2DDll.cs @@ -0,0 +1,9 @@ +namespace Texture2DDecoder +{ + internal static class T2DDll + { + + internal const string DllName = "Texture2DDecoderNative"; + + } +} diff --git a/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj b/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj new file mode 100644 index 0000000..3f4016d --- /dev/null +++ b/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {2AFCE830-B463-49B3-A026-877E5EAFC0A4} + Library + Properties + Texture2DDecoder + Texture2DDecoderWrapper + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + {40c796b5-88ce-4adc-acd6-2f4862b7f136} + AssetStudio.PInvoke + + + + \ No newline at end of file diff --git a/Texture2DDecoderWrapper/TextureDecoder.PInvoke.cs b/Texture2DDecoderWrapper/TextureDecoder.PInvoke.cs new file mode 100644 index 0000000..65c7572 --- /dev/null +++ b/Texture2DDecoderWrapper/TextureDecoder.PInvoke.cs @@ -0,0 +1,90 @@ +using System.Runtime.InteropServices; + +namespace Texture2DDecoder +{ + unsafe partial class TextureDecoder + { + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeDXT1(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeDXT5(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodePVRTC(void* data, int width, int height, void* image, [MarshalAs(UnmanagedType.Bool)] bool is2bpp); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeETC1(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeETC2(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeETC2A1(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeETC2A8(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeEACR(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeEACRSigned(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeEACRG(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeEACRGSigned(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeBC4(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeBC5(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeBC6(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeBC7(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeATCRGB4(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeATCRGBA8(void* data, int width, int height, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DecodeASTC(void* data, int width, int height, int blockWidth, int blockHeight, void* image); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void DisposeBuffer(ref void* ppBuffer); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void UnpackCrunch(void* data, uint dataSize, out void* result, out uint resultSize); + + [DllImport(T2DDll.DllName, CallingConvention = CallingConvention.Winapi)] + private static extern void UnpackUnityCrunch(void* data, uint dataSize, out void* result, out uint resultSize); + + } +} diff --git a/Texture2DDecoderWrapper/TextureDecoder.cs b/Texture2DDecoderWrapper/TextureDecoder.cs new file mode 100644 index 0000000..797b78f --- /dev/null +++ b/Texture2DDecoderWrapper/TextureDecoder.cs @@ -0,0 +1,262 @@ +using System; +using System.Runtime.InteropServices; +using AssetStudio.PInvoke; + +namespace Texture2DDecoder +{ + public static unsafe partial class TextureDecoder + { + + static TextureDecoder() + { + DllLoader.PreloadDll(T2DDll.DllName); + } + + public static bool DecodeDXT1(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeDXT1(pData, width, height, pImage); + } + } + } + + public static bool DecodeDXT5(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeDXT5(pData, width, height, pImage); + } + } + } + + public static bool DecodePVRTC(byte[] data, int width, int height, byte[] image, bool is2bpp) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodePVRTC(pData, width, height, pImage, is2bpp); + } + } + } + + public static bool DecodeETC1(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeETC1(pData, width, height, pImage); + } + } + } + + public static bool DecodeETC2(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeETC2(pData, width, height, pImage); + } + } + } + + public static bool DecodeETC2A1(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeETC2A1(pData, width, height, pImage); + } + } + } + + public static bool DecodeETC2A8(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeETC2A8(pData, width, height, pImage); + } + } + } + + public static bool DecodeEACR(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeEACR(pData, width, height, pImage); + } + } + } + + public static bool DecodeEACRSigned(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeEACRSigned(pData, width, height, pImage); + } + } + } + + public static bool DecodeEACRG(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeEACRG(pData, width, height, pImage); + } + } + } + + public static bool DecodeEACRGSigned(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeEACRGSigned(pData, width, height, pImage); + } + } + } + + public static bool DecodeBC4(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeBC4(pData, width, height, pImage); + } + } + } + + public static bool DecodeBC5(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeBC5(pData, width, height, pImage); + } + } + } + + public static bool DecodeBC6(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeBC6(pData, width, height, pImage); + } + } + } + + public static bool DecodeBC7(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeBC7(pData, width, height, pImage); + } + } + } + + public static bool DecodeATCRGB4(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeATCRGB4(pData, width, height, pImage); + } + } + } + + public static bool DecodeATCRGBA8(byte[] data, int width, int height, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeATCRGBA8(pData, width, height, pImage); + } + } + } + + public static bool DecodeASTC(byte[] data, int width, int height, int blockWidth, int blockHeight, byte[] image) + { + fixed (byte* pData = data) + { + fixed (byte* pImage = image) + { + return DecodeASTC(pData, width, height, blockWidth, blockHeight, pImage); + } + } + } + + public static byte[] UnpackCrunch(byte[] data) + { + void* pBuffer; + uint bufferSize; + + fixed (byte* pData = data) + { + UnpackCrunch(pData, (uint)data.Length, out pBuffer, out bufferSize); + } + + if (pBuffer == null) + { + return null; + } + + var result = new byte[bufferSize]; + + Marshal.Copy(new IntPtr(pBuffer), result, 0, (int)bufferSize); + + DisposeBuffer(ref pBuffer); + + return result; + } + + public static byte[] UnpackUnityCrunch(byte[] data) + { + void* pBuffer; + uint bufferSize; + + fixed (byte* pData = data) + { + UnpackUnityCrunch(pData, (uint)data.Length, out pBuffer, out bufferSize); + } + + if (pBuffer == null) + { + return null; + } + + var result = new byte[bufferSize]; + + Marshal.Copy(new IntPtr(pBuffer), result, 0, (int)bufferSize); + + DisposeBuffer(ref pBuffer); + + return result; + } + + } +}