- [Core] Project restructure
This commit is contained in:
18
AssetStudio.FBXWrapper/AssetStudio.FBXWrapper.csproj
Normal file
18
AssetStudio.FBXWrapper/AssetStudio.FBXWrapper.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>1.00.00</Version>
|
||||
<AssemblyVersion>1.00.00</AssemblyVersion>
|
||||
<FileVersion>1.00.00</FileVersion>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj" />
|
||||
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
16
AssetStudio.FBXWrapper/Fbx.PInvoke.cs
Normal file
16
AssetStudio.FBXWrapper/Fbx.PInvoke.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using AssetStudio.FbxInterop;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
partial class Fbx
|
||||
{
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
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)]
|
||||
private static extern void AsUtilEulerToQuaternion(float vx, float vy, float vz, out float qx, out float qy, out float qz, out float qw);
|
||||
|
||||
}
|
||||
}
|
||||
58
AssetStudio.FBXWrapper/Fbx.cs
Normal file
58
AssetStudio.FBXWrapper/Fbx.cs
Normal file
@@ -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, bool exportAllUvsAsDiffuseMaps, bool exportUV0UV1, 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, exportAllUvsAsDiffuseMaps, exportUV0UV1, scaleFactor, versionIndex, isAscii))
|
||||
{
|
||||
exporter.Initialize();
|
||||
exporter.ExportAll(blendShape, animation, eulerFilter, filterPrecision);
|
||||
}
|
||||
|
||||
Directory.SetCurrentDirectory(currentDir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
10
AssetStudio.FBXWrapper/FbxDll.cs
Normal file
10
AssetStudio.FBXWrapper/FbxDll.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace AssetStudio.FbxInterop
|
||||
{
|
||||
internal static class FbxDll
|
||||
{
|
||||
|
||||
internal const string DllName = "AssetStudioFBXNative";
|
||||
internal const string FbxsdkDllName = "libfbxsdk";
|
||||
|
||||
}
|
||||
}
|
||||
246
AssetStudio.FBXWrapper/FbxExporter.cs
Normal file
246
AssetStudio.FBXWrapper/FbxExporter.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
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 bool _exportAllUvsAsDiffuseMaps;
|
||||
private readonly bool _exportUV0UV1;
|
||||
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, bool exportAllUvsAsDiffuseMaps, bool exportUV0UV1, float scaleFactor, int versionIndex, bool isAscii)
|
||||
{
|
||||
_context = new FbxExporterContext();
|
||||
|
||||
_fileName = fileName;
|
||||
_imported = imported;
|
||||
_allNodes = allNodes;
|
||||
_exportSkins = exportSkins;
|
||||
_castToBone = castToBone;
|
||||
_boneSize = boneSize;
|
||||
_exportAllUvsAsDiffuseMaps = exportAllUvsAsDiffuseMaps;
|
||||
_exportUV0UV1 = exportUV0UV1;
|
||||
_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<ImportedFrame>();
|
||||
|
||||
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<ImportedFrame> 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<string>();
|
||||
|
||||
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<string> 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<ImportedFrame> meshFrames)
|
||||
{
|
||||
foreach (var meshFrame in meshFrames)
|
||||
{
|
||||
_context.ExportMeshFromFrame(rootFrame, meshFrame, _imported.MeshList, _imported.MaterialList, _imported.TextureList, _exportSkins, _exportAllUvsAsDiffuseMaps, _exportUV0UV1);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<string> SearchHierarchy()
|
||||
{
|
||||
if (_imported.MeshList == null || _imported.MeshList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var exportFrames = new HashSet<string>();
|
||||
|
||||
SearchHierarchy(_imported.RootFrame, _imported.MeshList, exportFrames);
|
||||
|
||||
return exportFrames;
|
||||
}
|
||||
|
||||
private static void SearchHierarchy(ImportedFrame rootFrame, List<ImportedMesh> meshList, HashSet<string> exportFrames)
|
||||
{
|
||||
var frameStack = new Stack<ImportedFrame>();
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
224
AssetStudio.FBXWrapper/FbxExporterContext.PInvoke.cs
Normal file
224
AssetStudio.FBXWrapper/FbxExporterContext.PInvoke.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetStudio.FbxInterop
|
||||
{
|
||||
partial class FbxExporterContext
|
||||
{
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxCreateContext();
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool AsFbxInitializeContext(IntPtr context, [MarshalAs(UnmanagedType.LPUTF8Str)] string fileName, float scaleFactor, int versionIndex, [MarshalAs(UnmanagedType.Bool)] bool isAscii, [MarshalAs(UnmanagedType.Bool)] bool is60Fps, out string errorMessage);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxDisposeContext(ref IntPtr ppContext);
|
||||
|
||||
private static void AsFbxSetFramePaths(IntPtr context, string[] framePaths) => AsFbxSetFramePaths(context, framePaths, framePaths.Length);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxSetFramePaths(IntPtr context, [MarshalAs(UnmanagedType.LPUTF8Str)] string[] framePaths, int count);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxExportScene(IntPtr context);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxGetSceneRootNode(IntPtr context);
|
||||
|
||||
private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Quaternion localRotation, in Vector3 localScale)
|
||||
{
|
||||
var localRotationEuler = Fbx.QuaternionToEuler(localRotation);
|
||||
return AsFbxExportSingleFrame(context, parentNode, framePath, frameName, localPosition.X, localPosition.Y, localPosition.Z, localRotationEuler.X, localRotationEuler.Y, localRotationEuler.Z, localScale.X, localScale.Y, localScale.Z);
|
||||
}
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, [MarshalAs(UnmanagedType.LPUTF8Str)] string strFramePath, [MarshalAs(UnmanagedType.LPUTF8Str)] string strFrameName, float localPositionX, float localPositionY, float localPositionZ, float localRotationX, float localRotationY, float localRotationZ, float localScaleX, float localScaleY, float localScaleZ);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxSetJointsNode_CastToBone(IntPtr context, IntPtr node, float boneSize);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxSetJointsNode_BoneInPath(IntPtr context, IntPtr node, float boneSize);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxSetJointsNode_Generic(IntPtr context, IntPtr node);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxPrepareMaterials(IntPtr context, int materialCount, int textureCount);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxCreateTexture(IntPtr context, [MarshalAs(UnmanagedType.LPUTF8Str)] string matTexName);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxLinkTexture(int dest, IntPtr texture, IntPtr material, float offsetX, float offsetY, float scaleX, float scaleY);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxMeshCreateClusterArray(int boneCount);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshDisposeClusterArray(ref IntPtr ppArray);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxMeshCreateCluster(IntPtr context, IntPtr boneNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshAddCluster(IntPtr array, IntPtr cluster);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxMeshCreateMesh(IntPtr context, IntPtr frameNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshInitControlPoints(IntPtr mesh, int vertexCount);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshCreateElementNormal(IntPtr mesh);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshCreateDiffuseUV(IntPtr mesh, int uv);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshCreateNormalMapUV(IntPtr mesh, int uv);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshCreateElementTangent(IntPtr mesh);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshCreateElementVertexColor(IntPtr mesh);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
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)
|
||||
{
|
||||
return AsFbxCreateMaterial(pContext, matName, 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)]
|
||||
private static extern IntPtr AsFbxCreateMaterial(IntPtr pContext, [MarshalAs(UnmanagedType.LPUTF8Str)] string 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)]
|
||||
private static extern int AsFbxAddMaterialToFrame(IntPtr frameNode, IntPtr material);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxSetFrameShadingModeToTextureShading(IntPtr frameNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshSetControlPoint(IntPtr mesh, int index, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshAddPolygon(IntPtr mesh, int materialIndex, int index0, int index1, int index2);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshElementNormalAdd(IntPtr mesh, int elementIndex, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshElementUVAdd(IntPtr mesh, int elementIndex, float u, float v);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshElementTangentAdd(IntPtr mesh, int elementIndex, float x, float y, float z, float w);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshElementVertexColorAdd(IntPtr mesh, int elementIndex, float r, float g, float b, float a);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshSetBoneWeight(IntPtr pClusterArray, int boneIndex, int vertexIndex, float weight);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxMeshCreateSkinContext(IntPtr context, IntPtr frameNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshDisposeSkinContext(ref IntPtr ppSkinContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool FbxClusterArray_HasItemAt(IntPtr pClusterArray, int index);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static unsafe extern void AsFbxMeshSkinAddCluster(IntPtr pSkinContext, IntPtr pClusterArray, int index, float* pBoneMatrix);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMeshAddDeformer(IntPtr pSkinContext, IntPtr pMesh);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxAnimCreateContext([MarshalAs(UnmanagedType.Bool)] bool eulerFilter);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimDisposeContext(ref IntPtr ppAnimContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimPrepareStackAndLayer(IntPtr pContext, IntPtr pAnimContext, [MarshalAs(UnmanagedType.LPUTF8Str)] string takeName);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimLoadCurves(IntPtr pNode, IntPtr pAnimContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimBeginKeyModify(IntPtr pAnimContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimEndKeyModify(IntPtr pAnimContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimAddScalingKey(IntPtr pAnimContext, float time, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimAddRotationKey(IntPtr pAnimContext, float time, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimAddTranslationKey(IntPtr pAnimContext, float time, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimApplyEulerFilter(IntPtr pAnimContext, float filterPrecision);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern int AsFbxAnimGetCurrentBlendShapeChannelCount(IntPtr pAnimContext, IntPtr pNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool AsFbxAnimIsBlendShapeChannelMatch(IntPtr pAnimContext, int channelIndex, [MarshalAs(UnmanagedType.LPUTF8Str)] string channelName);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimBeginBlendShapeAnimCurve(IntPtr pAnimContext, int channelIndex);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimEndBlendShapeAnimCurve(IntPtr pAnimContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxAnimAddBlendShapeKeyframe(IntPtr pAnimContext, float time, float value);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxMorphCreateContext();
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphInitializeContext(IntPtr pContext, IntPtr pMorphContext, IntPtr pNode);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphDisposeContext(ref IntPtr ppMorphContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphAddBlendShapeChannel(IntPtr pContext, IntPtr pMorphContext, [MarshalAs(UnmanagedType.LPUTF8Str)] string channelName);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphAddBlendShapeChannelShape(IntPtr pContext, IntPtr pMorphContext, float weight, [MarshalAs(UnmanagedType.LPUTF8Str)] string shapeName);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphCopyBlendShapeControlPoints(IntPtr pMorphContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphSetBlendShapeVertex(IntPtr pMorphContext, uint index, float x, float y, float z);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphCopyBlendShapeControlPointsNormal(IntPtr pMorphContext);
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern void AsFbxMorphSetBlendShapeVertexNormal(IntPtr pMorphContext, uint index, float x, float y, float z);
|
||||
|
||||
}
|
||||
}
|
||||
666
AssetStudio.FBXWrapper/FbxExporterContext.cs
Normal file
666
AssetStudio.FBXWrapper/FbxExporterContext.cs
Normal file
@@ -0,0 +1,666 @@
|
||||
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<ImportedFrame, IntPtr> _frameToNode;
|
||||
private readonly List<KeyValuePair<string, IntPtr>> _createdMaterials;
|
||||
private readonly Dictionary<string, IntPtr> _createdTextures;
|
||||
|
||||
public FbxExporterContext()
|
||||
{
|
||||
Fbx.QuaternionToEuler(Quaternion.Zero); // workaround to init dll
|
||||
_pContext = AsFbxCreateContext();
|
||||
_frameToNode = new Dictionary<ImportedFrame, IntPtr>();
|
||||
_createdMaterials = new List<KeyValuePair<string, IntPtr>>();
|
||||
_createdTextures = new Dictionary<string, IntPtr>();
|
||||
}
|
||||
|
||||
~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<string> framePaths)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
|
||||
if (framePaths == null || framePaths.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var framePathList = new List<string>(framePaths);
|
||||
var framePathArray = framePathList.ToArray();
|
||||
|
||||
AsFbxSetFramePaths(_pContext, framePathArray);
|
||||
}
|
||||
|
||||
internal void ExportScene()
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
|
||||
AsFbxExportScene(_pContext);
|
||||
}
|
||||
|
||||
internal void ExportFrame(List<ImportedMesh> meshList, List<ImportedFrame> meshFrames, ImportedFrame rootFrame)
|
||||
{
|
||||
var rootNode = AsFbxGetSceneRootNode(_pContext);
|
||||
|
||||
Debug.Assert(rootNode != IntPtr.Zero);
|
||||
|
||||
var nodeStack = new Stack<IntPtr>();
|
||||
var frameStack = new Stack<ImportedFrame>();
|
||||
|
||||
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<string> bonePaths, bool castToBone, float boneSize)
|
||||
{
|
||||
var frameStack = new Stack<ImportedFrame>();
|
||||
|
||||
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<ImportedMesh> meshList, List<ImportedMaterial> materialList, List<ImportedTexture> textureList, bool exportSkins, bool exportAllUvsAsDiffuseMaps, bool exportUV0UV1)
|
||||
{
|
||||
var meshNode = _frameToNode[meshFrame];
|
||||
var mesh = ImportedHelpers.FindMesh(meshFrame.Path, meshList);
|
||||
|
||||
ExportMesh(rootFrame, materialList, textureList, meshNode, mesh, exportSkins, exportAllUvsAsDiffuseMaps, exportUV0UV1);
|
||||
}
|
||||
|
||||
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<ImportedMaterial> materialList, List<ImportedTexture> textureList, IntPtr frameNode, ImportedMesh importedMesh, bool exportSkins, bool exportAllUvsAsDiffuseMaps, bool exportUV0UV1)
|
||||
{
|
||||
var boneList = importedMesh.BoneList;
|
||||
var totalBoneCount = 0;
|
||||
var hasBones = false;
|
||||
if (exportSkins && boneList?.Count > 0)
|
||||
{
|
||||
totalBoneCount = boneList.Count;
|
||||
hasBones = true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
AsFbxMeshInitControlPoints(mesh, importedMesh.VertexList.Count);
|
||||
|
||||
if (importedMesh.hasNormal)
|
||||
{
|
||||
AsFbxMeshCreateElementNormal(mesh);
|
||||
}
|
||||
|
||||
if (exportUV0UV1)
|
||||
{
|
||||
if (importedMesh.hasUV[0])
|
||||
{
|
||||
AsFbxMeshCreateDiffuseUV(mesh, 0);
|
||||
}
|
||||
if (importedMesh.hasUV[1])
|
||||
{
|
||||
if (exportAllUvsAsDiffuseMaps)
|
||||
{
|
||||
AsFbxMeshCreateDiffuseUV(mesh, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsFbxMeshCreateNormalMapUV(mesh, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < importedMesh.hasUV.Length; i++)
|
||||
{
|
||||
if (!importedMesh.hasUV[i]) { continue; }
|
||||
|
||||
if (i == 1 && !exportAllUvsAsDiffuseMaps)
|
||||
{
|
||||
AsFbxMeshCreateNormalMapUV(mesh, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsFbxMeshCreateDiffuseUV(mesh, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (importedMesh.hasTangent)
|
||||
{
|
||||
AsFbxMeshCreateElementTangent(mesh);
|
||||
}
|
||||
|
||||
if (importedMesh.hasColor)
|
||||
{
|
||||
AsFbxMeshCreateElementVertexColor(mesh);
|
||||
}
|
||||
|
||||
AsFbxMeshCreateElementMaterial(mesh);
|
||||
|
||||
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<string, IntPtr>(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);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var face in meshObj.FaceList)
|
||||
{
|
||||
var index0 = face.VertexIndices[0] + meshObj.BaseVertex;
|
||||
var index1 = face.VertexIndices[1] + meshObj.BaseVertex;
|
||||
var index2 = face.VertexIndices[2] + meshObj.BaseVertex;
|
||||
|
||||
AsFbxMeshAddPolygon(mesh, materialIndex, index0, index1, index2);
|
||||
}
|
||||
}
|
||||
|
||||
var vertexList = importedMesh.VertexList;
|
||||
|
||||
var vertexCount = vertexList.Count;
|
||||
|
||||
for (var j = 0; j < vertexCount; j += 1)
|
||||
{
|
||||
var importedVertex = vertexList[j];
|
||||
|
||||
var vertex = importedVertex.Vertex;
|
||||
AsFbxMeshSetControlPoint(mesh, j, vertex.X, vertex.Y, vertex.Z);
|
||||
|
||||
if (importedMesh.hasNormal)
|
||||
{
|
||||
var normal = importedVertex.Normal;
|
||||
AsFbxMeshElementNormalAdd(mesh, 0, normal.X, normal.Y, normal.Z);
|
||||
}
|
||||
|
||||
var uvSize = exportUV0UV1 ? 2 : importedMesh.hasUV.Length;
|
||||
for (var uvIndex = 0; uvIndex < uvSize; 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, boneWeights[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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<ImportedKeyframedAnimation> 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 = Fbx.QuaternionToEuler(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<ImportedMorph> 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);
|
||||
|
||||
for (var i = 0; i < channel.KeyframeList.Count; i++)
|
||||
{
|
||||
var keyframe = channel.KeyframeList[i];
|
||||
|
||||
AsFbxMorphAddBlendShapeChannelShape(_pContext, pMorphContext, keyframe.Weight, i == 0 ? channel.Name : $"{channel.Name}_{i + 1}");
|
||||
|
||||
AsFbxMorphCopyBlendShapeControlPoints(pMorphContext);
|
||||
|
||||
foreach (var vertex in keyframe.VertexList)
|
||||
{
|
||||
var v = vertex.Vertex.Vertex;
|
||||
AsFbxMorphSetBlendShapeVertex(pMorphContext, vertex.Index, v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
if (keyframe.hasNormals)
|
||||
{
|
||||
AsFbxMorphCopyBlendShapeControlPointsNormal(pMorphContext);
|
||||
|
||||
foreach (var vertex in keyframe.VertexList)
|
||||
{
|
||||
var v = vertex.Vertex.Normal;
|
||||
AsFbxMorphSetBlendShapeVertexNormal(pMorphContext, vertex.Index, v.X, v.Y, v.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AsFbxMorphDisposeContext(ref pMorphContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user