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;
+ }
+
+ }
+}