diff --git a/AssetStudio/AssetStudioForm.cs b/AssetStudio/AssetStudioForm.cs index 64be1b3..f68863c 100644 --- a/AssetStudio/AssetStudioForm.cs +++ b/AssetStudio/AssetStudioForm.cs @@ -14,9 +14,7 @@ using System.Drawing.Text; using OpenTK; using OpenTK.Graphics.OpenGL; using static AssetStudio.Studio; -using static AssetStudio.FBXExporter; using static AssetStudio.Importer; -using static AssetStudio.SpriteHelper; namespace AssetStudio { @@ -996,7 +994,7 @@ namespace AssetStudio case ClassIDReference.Sprite: { imageTexture?.Dispose(); - imageTexture = GetImageFromSprite(asset); + imageTexture = SpriteHelper.GetImageFromSprite(asset); if (imageTexture != null) { previewPanel.BackgroundImage = imageTexture; @@ -1311,64 +1309,18 @@ namespace AssetStudio var saveFolderDialog1 = new OpenFolderDialog(); if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) { - var savePath = saveFolderDialog1.Folder; - savePath = savePath + "\\"; - switch ((bool)Properties.Settings.Default["showExpOpt"]) + var savePath = saveFolderDialog1.Folder + "\\"; + + if ((bool)Properties.Settings.Default["showExpOpt"]) { - case true: - ExportOptions exportOpt = new ExportOptions(); - if (exportOpt.ShowDialog() == DialogResult.OK) { goto case false; } - break; - case false: - { - progressBar1.Value = 0; - progressBar1.Maximum = sceneTreeView.Nodes.Count; - //防止主界面假死 - ThreadPool.QueueUserWorkItem(state => - { - Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); - sceneTreeView.Invoke(new Action(() => - { - //挂起控件防止更新 - sceneTreeView.BeginUpdate(); - //先取消所有Node的选中 - foreach (TreeNode i in sceneTreeView.Nodes) - { - i.Checked = false; - } - })); - //遍历根节点 - foreach (TreeNode i in sceneTreeView.Nodes) - { - if (i.Nodes.Count > 0) - { - //遍历一级子节点 - foreach (TreeNode j in i.Nodes) - { - //加上时间,因为可能有重名的object - var filename = j.Text + DateTime.Now.ToString("_mm_ss_ffff"); - //选中它和它的子节点 - sceneTreeView.Invoke(new Action(() => j.Checked = true)); - //处理非法文件名 - filename = FixFileName(filename); - //导出FBX - WriteFBX(savePath + filename + ".fbx", false); - //取消选中 - sceneTreeView.Invoke(new Action(() => j.Checked = false)); - } - } - ProgressBarPerformStep(); - } - //取消挂起 - sceneTreeView.Invoke(new Action(() => sceneTreeView.EndUpdate())); - if (openAfterExport.Checked) - { - Process.Start(savePath); - } - }); - break; - } + var exportOpt = new ExportOptions(); + exportOpt.ShowDialog(); } + + progressBar1.Value = 0; + progressBar1.Maximum = sceneTreeView.Nodes.Count; + + ThreadPool.QueueUserWorkItem(state => ExportSplitObjects(savePath, sceneTreeView.Nodes)); } } @@ -1382,36 +1334,49 @@ namespace AssetStudio { if (sceneTreeView.Nodes.Count > 0) { - var exportSwitch = ((ToolStripItem)sender).Name == "exportallobjectsMenuItem"; + var exportAll = ((ToolStripItem)sender).Name == "exportallobjectsMenuItem"; saveFileDialog1.FileName = productName + DateTime.Now.ToString("_yy_MM_dd__HH_mm_ss"); if (saveFileDialog1.ShowDialog() == DialogResult.OK) { - switch ((bool)Properties.Settings.Default["showExpOpt"]) + if ((bool)Properties.Settings.Default["showExpOpt"]) { - case true: - ExportOptions exportOpt = new ExportOptions(); - if (exportOpt.ShowDialog() == DialogResult.OK) - { - goto case false; - } - break; - case false: - WriteFBX(saveFileDialog1.FileName, exportSwitch); + var exportOpt = new ExportOptions(); + exportOpt.ShowDialog(); + } - if (openAfterExport.Checked && File.Exists(saveFileDialog1.FileName)) + var gameObjects = new List(); + foreach (var assetsFile in assetsfileList) + { + foreach (var m_GameObject in assetsFile.GameObjectList.Values) + { + if (m_GameObject.Checked || exportAll) { - Process.Start(Path.GetDirectoryName(saveFileDialog1.FileName)); + gameObjects.Add(m_GameObject); } - break; + } + } + + progressBar1.Value = 0; + progressBar1.Maximum = 1; + if (gameObjects.Count == 0) + { + progressBar1.PerformStep(); + toolStripStatusLabel1.Text = "Nothing exported."; + return; + } + FBXExporter.WriteFBX(saveFileDialog1.FileName, gameObjects); + progressBar1.PerformStep(); + if (openAfterExport.Checked && File.Exists(saveFileDialog1.FileName)) + { + Process.Start(Path.GetDirectoryName(saveFileDialog1.FileName)); } } - } else { - StatusStripUpdate("No Objects available for export"); + toolStripStatusLabel1.Text = "No Objects available for export"; } } @@ -1726,7 +1691,7 @@ namespace AssetStudio secondSortColumn = 0; reverseSort = false; enableFiltering = false; - + listSearch.Text = " Filter "; FMODreset(); } diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index 2aacd8a..c9815a5 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -684,6 +684,8 @@ namespace AssetStudio else { m_AnimationType = reader.ReadInt32(); + if (m_AnimationType == 1) + m_Legacy = true; } m_Compressed = reader.ReadBoolean(); m_UseHighQualityCurve = reader.ReadBoolean(); diff --git a/AssetStudio/StudioClasses/Exporter.cs b/AssetStudio/StudioClasses/Exporter.cs index dbbc777..46f97b0 100644 --- a/AssetStudio/StudioClasses/Exporter.cs +++ b/AssetStudio/StudioClasses/Exporter.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; -using static AssetStudio.SpriteHelper; namespace AssetStudio { @@ -13,9 +12,10 @@ namespace AssetStudio { public static bool ExportTexture2D(AssetPreloadData asset, string exportPathName, bool flip) { - var m_Texture2D = new Texture2DConverter(new Texture2D(asset, true)); - if (m_Texture2D.image_data == null) + var texture2D = new Texture2D(asset, true); + if (texture2D.image_data == null || texture2D.image_data.Length == 0) return false; + var m_Texture2D = new Texture2DConverter(texture2D); var convert = (bool)Properties.Settings.Default["convertTexture"]; var bitmap = m_Texture2D.ConvertToBitmap(flip); if (convert && bitmap != null) @@ -287,7 +287,7 @@ namespace AssetStudio var exportFullName = exportPath + asset.Text + "." + type.ToLower(); if (ExportFileExists(exportFullName)) return false; - var bitmap = GetImageFromSprite(asset); + var bitmap = SpriteHelper.GetImageFromSprite(asset); if (bitmap != null) { bitmap.Save(exportFullName, format); @@ -349,7 +349,7 @@ namespace AssetStudio var boneSize = (int)(decimal)Properties.Settings.Default["boneSize"]; var flatInbetween = (bool)Properties.Settings.Default["flatInbetween"]; var compatibility = (bool)Properties.Settings.Default["compatibility"]; - Fbx.Exporter.Export(exportPath, convert, EulerFilter, filterPrecision, ".fbx", allFrames, allBones, skins, boneSize, flatInbetween, compatibility); + Fbx.Exporter.Export(exportPath, convert, EulerFilter, filterPrecision, allFrames, allBones, skins, boneSize, flatInbetween, compatibility); return true; } } diff --git a/AssetStudio/StudioClasses/FBXExporter.cs b/AssetStudio/StudioClasses/FBXExporter.cs index 639d222..dbae84e 100644 --- a/AssetStudio/StudioClasses/FBXExporter.cs +++ b/AssetStudio/StudioClasses/FBXExporter.cs @@ -10,7 +10,7 @@ namespace AssetStudio { static class FBXExporter { - public static void WriteFBX(string FBXfile, bool allNodes) + public static void WriteFBX(string FBXfile, List gameObjects) { var timestamp = DateTime.Now; @@ -47,105 +47,99 @@ namespace AssetStudio */ #region loop nodes and collect objects for export - foreach (var assetsFile in assetsfileList) + foreach (var m_GameObject in gameObjects) { - foreach (var m_GameObject in assetsFile.GameObjectList.Values) + GameObjects.Add(m_GameObject); + + if (assetsfileList.TryGetPD(m_GameObject.m_MeshFilter, out var MeshFilterPD)) { - if (m_GameObject.Checked || allNodes) + //MeshFilters are not unique! + //MeshFilters.Add(MeshFilterPD); + MeshFilter m_MeshFilter = new MeshFilter(MeshFilterPD); + if (assetsfileList.TryGetPD(m_MeshFilter.m_Mesh, out var MeshPD)) { - GameObjects.Add(m_GameObject); + Meshes.Add(MeshPD); - if (assetsfileList.TryGetPD(m_GameObject.m_MeshFilter, out var MeshFilterPD)) - { - //MeshFilters are not unique! - //MeshFilters.Add(MeshFilterPD); - MeshFilter m_MeshFilter = new MeshFilter(MeshFilterPD); - if (assetsfileList.TryGetPD(m_MeshFilter.m_Mesh, out var MeshPD)) - { - Meshes.Add(MeshPD); - - //write connections here and Mesh objects separately without having to backtrack through their MEshFilter to het the GameObject ID - //also note that MeshFilters are not unique, they cannot be used for instancing geometry - cb2.AppendFormat("\n\n\t;Geometry::, Model::{0}", m_GameObject.m_Name); - cb2.AppendFormat("\n\tC: \"OO\",3{0},1{1}", MeshPD.uniqueID, m_GameObject.uniqueID); - } - } - - #region get Renderer - if (assetsfileList.TryGetPD(m_GameObject.m_MeshRenderer, out var RendererPD)) - { - MeshRenderer m_Renderer = new MeshRenderer(RendererPD); - - foreach (var MaterialPPtr in m_Renderer.m_Materials) - { - if (assetsfileList.TryGetPD(MaterialPPtr, out var MaterialPD)) - { - Materials.Add(MaterialPD); - cb2.AppendFormat("\n\n\t;Material::, Model::{0}", m_GameObject.m_Name); - cb2.AppendFormat("\n\tC: \"OO\",6{0},1{1}", MaterialPD.uniqueID, m_GameObject.uniqueID); - } - } - } - - #endregion - - #region get SkinnedMeshRenderer - if (assetsfileList.TryGetPD(m_GameObject.m_SkinnedMeshRenderer, out var SkinnedMeshPD)) - { - Skins.Add(SkinnedMeshPD); - - SkinnedMeshRenderer m_SkinnedMeshRenderer = new SkinnedMeshRenderer(SkinnedMeshPD); - - foreach (var MaterialPPtr in m_SkinnedMeshRenderer.m_Materials) - { - if (assetsfileList.TryGetPD(MaterialPPtr, out var MaterialPD)) - { - Materials.Add(MaterialPD); - cb2.AppendFormat("\n\n\t;Material::, Model::{0}", m_GameObject.m_Name); - cb2.AppendFormat("\n\tC: \"OO\",6{0},1{1}", MaterialPD.uniqueID, m_GameObject.uniqueID); - } - } - - if ((bool)Properties.Settings.Default["exportDeformers"]) - { - DeformerCount += m_SkinnedMeshRenderer.m_Bones.Length; - - //collect skeleton dummies to make sure they are exported - foreach (var bonePPtr in m_SkinnedMeshRenderer.m_Bones) - { - if (assetsfileList.TryGetTransform(bonePPtr, out var b_Transform)) - { - if (assetsfileList.TryGetGameObject(b_Transform.m_GameObject, out var m_Bone)) - { - LimbNodes.Add(m_Bone); - //also collect the root bone - if (m_Bone.Parent.Level > 0) { LimbNodes.Add((GameObject)m_Bone.Parent); } - //should I collect siblings? - } - - #region collect children because m_SkinnedMeshRenderer.m_Bones doesn't contain terminations - foreach (var ChildPPtr in b_Transform.m_Children) - { - if (assetsfileList.TryGetTransform(ChildPPtr, out var ChildTR)) - { - if (assetsfileList.TryGetGameObject(ChildTR.m_GameObject, out var m_Child)) - { - //check that the Model doesn't contain a Mesh, although this won't ensure it's part of the skeleton - if (m_Child.m_MeshFilter == null && m_Child.m_SkinnedMeshRenderer == null) - { - LimbNodes.Add(m_Child); - } - } - } - } - #endregion - } - } - } - } - #endregion + //write connections here and Mesh objects separately without having to backtrack through their MEshFilter to het the GameObject ID + //also note that MeshFilters are not unique, they cannot be used for instancing geometry + cb2.AppendFormat("\n\n\t;Geometry::, Model::{0}", m_GameObject.m_Name); + cb2.AppendFormat("\n\tC: \"OO\",3{0},1{1}", MeshPD.uniqueID, m_GameObject.uniqueID); } } + + #region get Renderer + if (assetsfileList.TryGetPD(m_GameObject.m_MeshRenderer, out var RendererPD)) + { + MeshRenderer m_Renderer = new MeshRenderer(RendererPD); + + foreach (var MaterialPPtr in m_Renderer.m_Materials) + { + if (assetsfileList.TryGetPD(MaterialPPtr, out var MaterialPD)) + { + Materials.Add(MaterialPD); + cb2.AppendFormat("\n\n\t;Material::, Model::{0}", m_GameObject.m_Name); + cb2.AppendFormat("\n\tC: \"OO\",6{0},1{1}", MaterialPD.uniqueID, m_GameObject.uniqueID); + } + } + } + + #endregion + + #region get SkinnedMeshRenderer + if (assetsfileList.TryGetPD(m_GameObject.m_SkinnedMeshRenderer, out var SkinnedMeshPD)) + { + Skins.Add(SkinnedMeshPD); + + SkinnedMeshRenderer m_SkinnedMeshRenderer = new SkinnedMeshRenderer(SkinnedMeshPD); + + foreach (var MaterialPPtr in m_SkinnedMeshRenderer.m_Materials) + { + if (assetsfileList.TryGetPD(MaterialPPtr, out var MaterialPD)) + { + Materials.Add(MaterialPD); + cb2.AppendFormat("\n\n\t;Material::, Model::{0}", m_GameObject.m_Name); + cb2.AppendFormat("\n\tC: \"OO\",6{0},1{1}", MaterialPD.uniqueID, m_GameObject.uniqueID); + } + } + + if ((bool)Properties.Settings.Default["exportDeformers"]) + { + DeformerCount += m_SkinnedMeshRenderer.m_Bones.Length; + + //collect skeleton dummies to make sure they are exported + foreach (var bonePPtr in m_SkinnedMeshRenderer.m_Bones) + { + if (assetsfileList.TryGetTransform(bonePPtr, out var b_Transform)) + { + if (assetsfileList.TryGetGameObject(b_Transform.m_GameObject, out var m_Bone)) + { + LimbNodes.Add(m_Bone); + //also collect the root bone + if (m_Bone.Parent.Level > 0) { LimbNodes.Add((GameObject)m_Bone.Parent); } + //should I collect siblings? + } + + #region collect children because m_SkinnedMeshRenderer.m_Bones doesn't contain terminations + foreach (var ChildPPtr in b_Transform.m_Children) + { + if (assetsfileList.TryGetTransform(ChildPPtr, out var ChildTR)) + { + if (assetsfileList.TryGetGameObject(ChildTR.m_GameObject, out var m_Child)) + { + //check that the Model doesn't contain a Mesh, although this won't ensure it's part of the skeleton + if (m_Child.m_MeshFilter == null && m_Child.m_SkinnedMeshRenderer == null) + { + LimbNodes.Add(m_Child); + } + } + } + } + #endregion + } + } + } + } + #endregion } //if ((bool)Properties.Settings.Default["convertDummies"]) { GameObjects.Except(LimbNodes); } diff --git a/AssetStudio/StudioClasses/ModelConverter.cs b/AssetStudio/StudioClasses/ModelConverter.cs index cb2ebae..37c2c64 100644 --- a/AssetStudio/StudioClasses/ModelConverter.cs +++ b/AssetStudio/StudioClasses/ModelConverter.cs @@ -9,10 +9,6 @@ using static AssetStudio.Studio; namespace AssetStudio { - /* TODO Handle all things in one loop - * Init with GameObject - * Other optimization - */ class ModelConverter : IImported { public List FrameList { get; protected set; } = new List(); @@ -81,7 +77,6 @@ namespace AssetStudio CreateBonePathHash(rootTransform); ConvertFrames(rootTransform, null); - CollectMorphInfo(rootTransform); ConvertMeshRenderer(m_Transform); } @@ -92,21 +87,21 @@ namespace AssetStudio { if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) { - switch (assetPreloadData.Type2) + switch (assetPreloadData.Type) { - case 23: //MeshRenderer + case ClassIDReference.MeshRenderer: { var m_Renderer = new MeshRenderer(assetPreloadData); ConvertMeshRenderer(m_Renderer); break; } - case 137: //SkinnedMeshRenderer + case ClassIDReference.SkinnedMeshRenderer: { var m_SkinnedMeshRenderer = new SkinnedMeshRenderer(assetPreloadData); ConvertMeshRenderer(m_SkinnedMeshRenderer); break; } - case 111: //Animation + case ClassIDReference.Animation: { var m_Animation = new Animation(assetPreloadData); foreach (var animation in m_Animation.m_Animations) @@ -132,7 +127,7 @@ namespace AssetStudio { if (assetsfileList.TryGetPD(m_Animator.m_Controller, out var assetPreloadData)) { - if (assetPreloadData.Type2 == 221)//AnimatorOverrideController + if (assetPreloadData.Type == ClassIDReference.AnimatorOverrideController) { var m_AnimatorOverrideController = new AnimatorOverrideController(assetPreloadData); if (assetsfileList.TryGetPD(m_AnimatorOverrideController.m_Controller, out assetPreloadData)) @@ -154,7 +149,7 @@ namespace AssetStudio } }*/ } - else if (assetPreloadData.Type2 == 91)//AnimatorController + else if (assetPreloadData.Type == ClassIDReference.AnimatorController) { var m_AnimatorController = new AnimatorController(assetPreloadData); foreach (var m_AnimationClip in m_AnimatorController.m_AnimationClips) @@ -196,30 +191,6 @@ namespace AssetStudio } } - private void CollectMorphInfo(Transform m_Transform) - { - assetsfileList.TryGetGameObject(m_Transform.m_GameObject, out var m_GameObject); - if (assetsfileList.TryGetPD(m_GameObject.m_SkinnedMeshRenderer, out var assetPreloadData)) - { - var m_SkinnedMeshRenderer = new SkinnedMeshRenderer(assetPreloadData); - if (assetsfileList.TryGetPD(m_SkinnedMeshRenderer.m_Mesh, out var MeshPD)) - { - var mesh = new Mesh(MeshPD, true); - foreach (var channel in mesh.m_Shapes.channels) - { - morphChannelInfo.Add(channel.nameHash, channel.name); - } - } - } - - foreach (var pptr in m_Transform.m_Children) - { - if (assetsfileList.TryGetTransform(pptr, out var child)) - CollectMorphInfo(child); - } - } - - private void ConvertMeshRenderer(MeshRenderer meshR) { var mesh = GetMesh(meshR); @@ -375,6 +346,10 @@ namespace AssetStudio } //Morphs + foreach (var channel in mesh.m_Shapes.channels) + { + morphChannelInfo.Add(channel.nameHash, channel.name); + } if (mesh.m_Shapes.shapes.Count > 0) { ImportedMorph morph = null; @@ -446,7 +421,7 @@ namespace AssetStudio { if (assetsfileList.TryGetPD(m_Component, out var assetPreloadData)) { - if (assetPreloadData.Type2 == 33) //MeshFilter + if (assetPreloadData.Type == ClassIDReference.MeshFilter) { var m_MeshFilter = new MeshFilter(assetPreloadData); if (assetsfileList.TryGetPD(m_MeshFilter.m_Mesh, out var MeshPD)) @@ -529,7 +504,7 @@ namespace AssetStudio foreach (var texEnv in mat.m_TexEnvs) { Texture2D tex2D = null; - if (assetsfileList.TryGetPD(texEnv.m_Texture, out var TexturePD) && TexturePD.Type2 == 28)//TODO other Texture + if (assetsfileList.TryGetPD(texEnv.m_Texture, out var TexturePD) && TexturePD.Type == ClassIDReference.Texture2D)//TODO other Texture { tex2D = new Texture2D(TexturePD, true); } diff --git a/AssetStudio/StudioClasses/SpriteHelper.cs b/AssetStudio/StudioClasses/SpriteHelper.cs index 4d2c646..166b6e2 100644 --- a/AssetStudio/StudioClasses/SpriteHelper.cs +++ b/AssetStudio/StudioClasses/SpriteHelper.cs @@ -11,12 +11,8 @@ namespace AssetStudio { static class SpriteHelper { - private static Dictionary spriteCache = new Dictionary(); - public static Bitmap GetImageFromSprite(AssetPreloadData asset) { - if (spriteCache.TryGetValue(asset, out var bitmap)) - return (Bitmap)bitmap.Clone(); var m_Sprite = new Sprite(asset, true); if (assetsfileList.TryGetPD(m_Sprite.m_SpriteAtlas, out var assetPreloadData)) { @@ -51,8 +47,7 @@ namespace AssetStudio asset.InfoText = $"Width: {textureRect.Width}\nHeight: {textureRect.Height}\n" + info; var spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb); spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY); - spriteCache.Add(asset, spriteImage); - return (Bitmap)spriteImage.Clone(); + return spriteImage; } } @@ -87,8 +82,7 @@ namespace AssetStudio { graphic.FillPath(brush, path); bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); - spriteCache.Add(asset, bitmap); - return (Bitmap)bitmap.Clone(); + return bitmap; } } } diff --git a/AssetStudio/StudioClasses/Studio.cs b/AssetStudio/StudioClasses/Studio.cs index a96fe78..68cf77b 100644 --- a/AssetStudio/StudioClasses/Studio.cs +++ b/AssetStudio/StudioClasses/Studio.cs @@ -506,6 +506,52 @@ namespace AssetStudio }); } + public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes) + { + foreach (TreeNode node in nodes) + { + //遍历一级子节点 + foreach (TreeNode j in node.Nodes) + { + //收集所有子节点 + var gameObjects = new List(); + CollectNode(j, gameObjects); + //跳过一些不需要导出的object + if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) + continue; + //处理非法文件名 + var filename = FixFileName(j.Text); + //每个文件单独文件夹 + var saveName = $"{savePath}{filename}\\{filename}.fbx"; + //重名文件处理 + for (int i = 1; ; i++) + { + if (File.Exists(saveName)) + { + saveName = $"{savePath}{filename} ({i})\\{filename}.fbx"; + } + else + { + break; + } + } + Directory.CreateDirectory(Path.GetDirectoryName(saveName)); + //导出FBX + FBXExporter.WriteFBX(saveName, gameObjects); + } + ProgressBarPerformStep(); + } + } + + private static void CollectNode(TreeNode node, List gameObjects) + { + gameObjects.Add((GameObject)node); + foreach (TreeNode i in node.Nodes) + { + CollectNode(i, gameObjects); + } + } + public static void ExportAnimatorWithAnimationClip(AssetPreloadData animator, List animationList, string exportPath) { ThreadPool.QueueUserWorkItem(state => diff --git a/AssetStudioFBX/AssetStudioFBX-x86.vcxproj b/AssetStudioFBX/AssetStudioFBX-x86.vcxproj index e558cb3..0f40eab 100644 --- a/AssetStudioFBX/AssetStudioFBX-x86.vcxproj +++ b/AssetStudioFBX/AssetStudioFBX-x86.vcxproj @@ -78,7 +78,6 @@ - diff --git a/AssetStudioFBX/AssetStudioFBX.h b/AssetStudioFBX/AssetStudioFBX.h index 53b3219..95cf7b0 100644 --- a/AssetStudioFBX/AssetStudioFBX.h +++ b/AssetStudioFBX/AssetStudioFBX.h @@ -36,8 +36,8 @@ namespace AssetStudio { ref class Exporter { public: - static void Export(String^ path, IImported^ imported, bool EulerFilter, float filterPrecision, String^ exportFormat, bool allFrames, bool allBones, bool skins, float boneSize, bool flatInbetween, bool compatibility); - static void ExportMorph(String^ path, IImported^ imported, String^ exportFormat, bool morphMask, bool flatInbetween, bool skins, float boneSize, bool compatibility); + static void Export(String^ path, IImported^ imported, bool EulerFilter, float filterPrecision, bool allFrames, bool allBones, bool skins, float boneSize, bool flatInbetween, bool compatibility); + static void ExportMorph(String^ path, IImported^ imported, bool morphMask, bool flatInbetween, bool skins, float boneSize, bool compatibility); private: HashSet^ frameNames; @@ -56,11 +56,10 @@ namespace AssetStudio { FbxArray* pMeshNodes; ~Exporter(); - !Exporter(); void Fbx::Exporter::LinkTexture(ImportedMaterial^ mat, int attIndex, FbxFileTexture* pTexture, FbxProperty& prop); void SetJointsNode(FbxNode* pNode, HashSet^ boneNames, bool allBones); - Exporter(String^ path, IImported^ imported, String^ exportFormat, bool allFrames, bool allBones, bool skins, float boneSize, bool compatibility, bool normals); + Exporter(String^ path, IImported^ imported, bool allFrames, bool allBones, bool skins, float boneSize, bool compatibility, bool normals); HashSet^ SearchHierarchy(); void SearchHierarchy(ImportedFrame^ frame, HashSet^ exportFrames); void SetJointsFromImportedMeshes(bool allBones); diff --git a/AssetStudioFBX/AssetStudioFBX.vcxproj b/AssetStudioFBX/AssetStudioFBX.vcxproj index 663a06a..57f3d6d 100644 --- a/AssetStudioFBX/AssetStudioFBX.vcxproj +++ b/AssetStudioFBX/AssetStudioFBX.vcxproj @@ -78,7 +78,6 @@ - diff --git a/AssetStudioFBX/AssetStudioFBX.vcxproj.filters b/AssetStudioFBX/AssetStudioFBX.vcxproj.filters index 680f753..203b81e 100644 --- a/AssetStudioFBX/AssetStudioFBX.vcxproj.filters +++ b/AssetStudioFBX/AssetStudioFBX.vcxproj.filters @@ -14,9 +14,6 @@ 源文件 - - 源文件 - 源文件 diff --git a/AssetStudioFBX/AssetStudioFBXExporter.cpp b/AssetStudioFBX/AssetStudioFBXExporter.cpp index 81c8081..770af19 100644 --- a/AssetStudioFBX/AssetStudioFBXExporter.cpp +++ b/AssetStudioFBX/AssetStudioFBXExporter.cpp @@ -4,12 +4,143 @@ namespace AssetStudio { - Fbx::Exporter::~Exporter() + void Fbx::Exporter::Export(String^ path, IImported^ imported, bool EulerFilter, float filterPrecision, bool allFrames, bool allBones, bool skins, float boneSize, bool flatInbetween, bool compatibility) { - this->!Exporter(); + FileInfo^ file = gcnew FileInfo(path); + DirectoryInfo^ dir = file->Directory; + if (!dir->Exists) + { + dir->Create(); + } + String^ currentDir = Directory::GetCurrentDirectory(); + Directory::SetCurrentDirectory(dir->FullName); + path = Path::GetFileName(path); + + Exporter^ exporter = gcnew Exporter(path, imported, allFrames, allBones, skins, boneSize, compatibility, true); + exporter->ExportMorphs(imported, false, flatInbetween); + exporter->ExportAnimations(EulerFilter, filterPrecision, flatInbetween); + exporter->pExporter->Export(exporter->pScene); + delete exporter; + + Directory::SetCurrentDirectory(currentDir); } - Fbx::Exporter::!Exporter() + void Fbx::Exporter::ExportMorph(String^ path, IImported^ imported, bool morphMask, bool flatInbetween, bool skins, float boneSize, bool compatibility) + { + FileInfo^ file = gcnew FileInfo(path); + DirectoryInfo^ dir = file->Directory; + if (!dir->Exists) + { + dir->Create(); + } + String^ currentDir = Directory::GetCurrentDirectory(); + Directory::SetCurrentDirectory(dir->FullName); + path = Path::GetFileName(path); + + Exporter^ exporter = gcnew Exporter(path, imported, false, true, skins, boneSize, compatibility, false); + exporter->ExportMorphs(imported, morphMask, flatInbetween); + exporter->pExporter->Export(exporter->pScene); + delete exporter; + + Directory::SetCurrentDirectory(currentDir); + } + + Fbx::Exporter::Exporter(String^ path, IImported^ imported, bool allFrames, bool allBones, bool skins, float boneSize, bool compatibility, bool normals) + { + this->imported = imported; + exportSkins = skins; + this->boneSize = boneSize; + + cDest = NULL; + cFormat = NULL; + pSdkManager = NULL; + pScene = NULL; + pExporter = NULL; + pMaterials = NULL; + pTextures = NULL; + pMeshNodes = NULL; + + pin_ptr pSdkManagerPin = &pSdkManager; + pin_ptr pScenePin = &pScene; + Init(pSdkManagerPin, pScenePin); + + cDest = Fbx::StringToCharArray(path); + pExporter = FbxExporter::Create(pScene, ""); + int lFormatIndex, lFormatCount = pSdkManager->GetIOPluginRegistry()->GetWriterFormatCount(); + for (lFormatIndex = 0; lFormatIndex < lFormatCount; lFormatIndex++) + { + if (pSdkManager->GetIOPluginRegistry()->WriterIsFBX(lFormatIndex)) + { + FbxString lDesc = FbxString(pSdkManager->GetIOPluginRegistry()->GetWriterFormatDescription(lFormatIndex)); + if (lDesc.Find("binary") >= 0) + { + if (!compatibility || lDesc.Find("6.") >= 0) + { + break; + } + } + } + } + + 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(); + FbxTime::EMode pTimeMode = FbxTime::eFrames24; + globalSettings.SetTimeMode(pTimeMode); + + if (!pExporter->Initialize(cDest, lFormatIndex, pSdkManager->GetIOSettings())) + { + throw gcnew Exception(gcnew String("Failed to initialize FbxExporter: ") + gcnew String(pExporter->GetStatus().GetErrorString())); + } + + frameNames = nullptr; + if (!allFrames) + { + frameNames = SearchHierarchy(); + if (!frameNames) + { + return; + } + } + + pMeshNodes = imported->MeshList != nullptr ? new FbxArray(imported->MeshList->Count) : NULL; + ExportFrame(pScene->GetRootNode(), imported->FrameList[0]); + + if (imported->MeshList != nullptr) + { + SetJointsFromImportedMeshes(allBones); + + pMaterials = new FbxArray(); + pTextures = new FbxArray(); + pMaterials->Reserve(imported->MaterialList->Count); + pTextures->Reserve(imported->TextureList->Count); + + for (int i = 0; i < pMeshNodes->GetCount(); i++) + { + FbxNode* meshNode = pMeshNodes->GetAt(i); + String^ meshPath = gcnew String(meshNode->GetName()); + FbxNode* rootNode = meshNode; + while ((rootNode = rootNode->GetParent()) != pScene->GetRootNode()) + { + meshPath = gcnew String(rootNode->GetName()) + "/" + meshPath; + } + ImportedMesh^ mesh = ImportedHelpers::FindMesh(meshPath, imported->MeshList); + ExportMesh(meshNode, mesh, normals); + } + } + else + { + SetJointsNode(pScene->GetRootNode()->GetChild(0), nullptr, true); + } + } + + Fbx::Exporter::~Exporter() { if (pMeshNodes != NULL) { @@ -71,4 +202,1121 @@ namespace AssetStudio SetJointsNode(pNode->GetChild(i), boneNames, allBones); } } + + HashSet^ Fbx::Exporter::SearchHierarchy() + { + if (imported->MeshList == nullptr || imported->MeshList->Count == 0) + { + return nullptr; + } + HashSet^ exportFrames = gcnew HashSet(); + SearchHierarchy(imported->FrameList[0], exportFrames); + return exportFrames; + } + + void Fbx::Exporter::SearchHierarchy(ImportedFrame^ frame, HashSet^ exportFrames) + { + ImportedMesh^ meshListSome = ImportedHelpers::FindMesh(frame, imported->MeshList); + if (meshListSome != nullptr) + { + ImportedFrame^ parent = frame; + while (parent != nullptr) + { + exportFrames->Add(parent->Name); + parent = (ImportedFrame^)parent->Parent; + } + + List^ boneList = meshListSome->BoneList; + if (boneList != nullptr) + { + for (int i = 0; i < boneList->Count; i++) + { + if (!exportFrames->Contains(boneList[i]->Name)) + { + ImportedFrame^ boneParent = ImportedHelpers::FindFrame(boneList[i]->Name, imported->FrameList[0]); + while (boneParent != nullptr) + { + exportFrames->Add(boneParent->Name); + boneParent = (ImportedFrame^)boneParent->Parent; + } + } + } + } + } + + for (int i = 0; i < frame->Count; i++) + { + SearchHierarchy(frame[i], exportFrames); + } + } + + void Fbx::Exporter::SetJointsFromImportedMeshes(bool allBones) + { + if (!exportSkins) + { + return; + } + HashSet^ boneNames = 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]; + boneNames->Add(bone->Name); + } + } + } + + SetJointsNode(pScene->GetRootNode()->GetChild(0), boneNames, allBones); + } + + void Fbx::Exporter::ExportFrame(FbxNode* pParentNode, ImportedFrame^ frame) + { + String^ frameName = frame->Name; + if ((frameNames == nullptr) || frameNames->Contains(frameName)) + { + FbxNode* pFrameNode = NULL; + char* pName = NULL; + try + { + pName = StringToCharArray(frameName); + pFrameNode = FbxNode::Create(pScene, pName); + } + finally + { + Marshal::FreeHGlobal((IntPtr)pName); + } + + Vector3 scale, translate; + Quaternion rotate; + frame->Matrix.Decompose(scale, rotate, translate); + Vector3 rotateVector = Fbx::QuaternionToEuler(rotate); + + pFrameNode->LclScaling.Set(FbxVector4(scale.X, scale.Y, scale.Z)); + pFrameNode->LclRotation.Set(FbxVector4(FbxDouble3(rotateVector.X, rotateVector.Y, rotateVector.Z))); + pFrameNode->LclTranslation.Set(FbxVector4(translate.X, translate.Y, translate.Z)); + pParentNode->AddChild(pFrameNode); + + if (imported->MeshList != nullptr && ImportedHelpers::FindMesh(frame, imported->MeshList) != nullptr) + { + pMeshNodes->Add(pFrameNode); + } + + for (int i = 0; i < frame->Count; i++) + { + ExportFrame(pFrameNode, frame[i]); + } + } + } + + void Fbx::Exporter::ExportMesh(FbxNode* pFrameNode, ImportedMesh^ meshList, bool normals) + { + int lastSlash = meshList->Name->LastIndexOf('/'); + String^ frameName = lastSlash < 0 ? meshList->Name : meshList->Name->Substring(lastSlash + 1); + List^ boneList = meshList->BoneList; + bool hasBones; + if (exportSkins && boneList != nullptr) + { + hasBones = boneList->Count > 0; + } + else + { + hasBones = false; + } + + FbxArray* pBoneNodeList = NULL; + try + { + if (hasBones) + { + pBoneNodeList = new FbxArray(); + pBoneNodeList->Reserve(boneList->Count); + for (int i = 0; i < boneList->Count; i++) + { + ImportedBone^ bone = boneList[i]; + String^ boneName = bone->Name; + char* pBoneName = NULL; + try + { + pBoneName = StringToCharArray(boneName); + FbxNode* foundNode = pScene->GetRootNode()->FindChild(pBoneName); + if (foundNode == NULL) + { + throw gcnew Exception(gcnew String("Couldn't find frame ") + boneName + gcnew String(" used by the bone")); + } + pBoneNodeList->Add(foundNode); + } + finally + { + Marshal::FreeHGlobal((IntPtr)pBoneName); + } + } + } + + for (int i = 0; i < meshList->SubmeshList->Count; i++) + { + char* pName = NULL; + FbxArray* pClusterArray = NULL; + try + { + pName = StringToCharArray(frameName + "_" + i); + FbxMesh* pMesh = FbxMesh::Create(pScene, ""); + + if (hasBones) + { + pClusterArray = new FbxArray(); + pClusterArray->Reserve(boneList->Count); + + for (int i = 0; i < boneList->Count; i++) + { + FbxNode* pNode = pBoneNodeList->GetAt(i); + FbxString lClusterName = pNode->GetNameOnly() + FbxString("Cluster"); + FbxCluster* pCluster = FbxCluster::Create(pSdkManager, lClusterName.Buffer()); + pCluster->SetLink(pNode); + pCluster->SetLinkMode(FbxCluster::eTotalOne); + pClusterArray->Add(pCluster); + } + } + + ImportedSubmesh^ meshObj = meshList->SubmeshList[i]; + List^ faceList = meshObj->FaceList; + List^ vertexList = meshObj->VertexList; + + pMesh->InitControlPoints(vertexList->Count); + FbxVector4* pControlPoints = pMesh->GetControlPoints(); + + FbxGeometryElementNormal* lGeometryElementNormal = NULL; + //if (normals) + { + lGeometryElementNormal = pMesh->GetElementNormal(); + if (!lGeometryElementNormal) + { + lGeometryElementNormal = pMesh->CreateElementNormal(); + } + lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); + lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect); + } + + FbxGeometryElementUV* lGeometryElementUV = pMesh->GetElementUV(); + if (!lGeometryElementUV) + { + lGeometryElementUV = pMesh->CreateElementUV(""); + } + lGeometryElementUV->SetMappingMode(FbxGeometryElement::eByControlPoint); + lGeometryElementUV->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxGeometryElementTangent* lGeometryElementTangent = NULL; + if (normals) + { + lGeometryElementTangent = pMesh->GetElementTangent(); + if (!lGeometryElementTangent) + { + lGeometryElementTangent = pMesh->CreateElementTangent(); + } + lGeometryElementTangent->SetMappingMode(FbxGeometryElement::eByControlPoint); + lGeometryElementTangent->SetReferenceMode(FbxGeometryElement::eDirect); + } + + bool vertexColours = vertexList->Count > 0 && dynamic_cast(vertexList[0]) != nullptr; + if (vertexColours) + { + FbxGeometryElementVertexColor* lGeometryElementVertexColor = pMesh->CreateElementVertexColor(); + lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); + lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); + for (int j = 0; j < vertexList->Count; j++) + { + ImportedVertexWithColour^ vert = (ImportedVertexWithColour^)vertexList[j]; + lGeometryElementVertexColor->GetDirectArray().Add(FbxColor(vert->Colour.Red, vert->Colour.Green, vert->Colour.Blue, vert->Colour.Alpha)); + } + } + + FbxNode* pMeshNode = FbxNode::Create(pScene, pName); + pMeshNode->SetNodeAttribute(pMesh); + pFrameNode->AddChild(pMeshNode); + + ImportedMaterial^ mat = ImportedHelpers::FindMaterial(meshObj->Material, imported->MaterialList); + if (mat != nullptr) + { + FbxGeometryElementMaterial* lGeometryElementMaterial = pMesh->GetElementMaterial(); + if (!lGeometryElementMaterial) + { + lGeometryElementMaterial = pMesh->CreateElementMaterial(); + } + lGeometryElementMaterial->SetMappingMode(FbxGeometryElement::eByPolygon); + lGeometryElementMaterial->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + char* pMatName = NULL; + try + { + pMatName = StringToCharArray(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"; + Color4 diffuse = mat->Diffuse; + Color4 ambient = mat->Ambient; + Color4 emissive = mat->Emissive; + Color4 specular = mat->Specular; + float specularPower = mat->Power; + pMat = FbxSurfacePhong::Create(pScene, pMatName); + pMat->Diffuse.Set(FbxDouble3(diffuse.Red, diffuse.Green, diffuse.Blue)); + pMat->DiffuseFactor.Set(FbxDouble(diffuse.Alpha)); + pMat->Ambient.Set(FbxDouble3(ambient.Red, ambient.Green, ambient.Blue)); + pMat->AmbientFactor.Set(FbxDouble(ambient.Alpha)); + pMat->Emissive.Set(FbxDouble3(emissive.Red, emissive.Green, emissive.Blue)); + pMat->EmissiveFactor.Set(FbxDouble(emissive.Alpha)); + pMat->Specular.Set(FbxDouble3(specular.Red, specular.Green, specular.Blue)); + pMat->SpecularFactor.Set(FbxDouble(specular.Alpha)); + pMat->Shininess.Set(specularPower); + pMat->ShadingModel.Set(lShadingName); + + foundMat = pMaterials->GetCount(); + pMaterials->Add(pMat); + } + pMeshNode->AddMaterial(pMat); + + bool hasTexture = false; + FbxFileTexture* pTextureDiffuse = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[0], imported->TextureList), pMesh); + if (pTextureDiffuse != NULL) + { + LinkTexture(mat, 0, pTextureDiffuse, pMat->Diffuse); + pMat->TransparentColor.ConnectSrcObject(pTextureDiffuse); + hasTexture = true; + } + + FbxFileTexture* pTextureAmbient = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[1], imported->TextureList), pMesh); + if (pTextureAmbient != NULL) + { + LinkTexture(mat, 1, pTextureAmbient, pMat->Ambient); + hasTexture = true; + } + + FbxFileTexture* pTextureEmissive = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[2], imported->TextureList), pMesh); + if (pTextureEmissive != NULL) + { + LinkTexture(mat, 2, pTextureEmissive, pMat->Emissive); + hasTexture = true; + } + + FbxFileTexture* pTextureSpecular = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[3], imported->TextureList), pMesh); + if (pTextureSpecular != NULL) + { + LinkTexture(mat, 3, pTextureSpecular, pMat->Specular); + hasTexture = true; + } + + if (mat->Textures->Length > 4) + { + FbxFileTexture* pTextureBump = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[4], imported->TextureList), pMesh); + if (pTextureBump != NULL) + { + LinkTexture(mat, 4, pTextureBump, pMat->Bump); + hasTexture = true; + } + } + + if (hasTexture) + { + pMeshNode->SetShadingMode(FbxNode::eTextureShading); + } + } + finally + { + Marshal::FreeHGlobal((IntPtr)pMatName); + } + } + + for (int j = 0; j < vertexList->Count; j++) + { + ImportedVertex^ vertex = vertexList[j]; + Vector3 coords = vertex->Position; + pControlPoints[j] = FbxVector4(coords.X, coords.Y, coords.Z, 0); + //if (normals) + { + Vector3 normal = vertex->Normal; + lGeometryElementNormal->GetDirectArray().Add(FbxVector4(normal.X, normal.Y, normal.Z, 0)); + } + array^ uv = vertex->UV; + lGeometryElementUV->GetDirectArray().Add(FbxVector2(uv[0], -uv[1])); + if (normals) + { + Vector4 tangent = vertex->Tangent; + lGeometryElementTangent->GetDirectArray().Add(FbxVector4(tangent.X, tangent.Y, tangent.Z, -tangent.W)); + } + + if (hasBones) + { + array^ boneIndices = vertex->BoneIndices; + array^ weights4 = vertex->Weights; + for (int k = 0; k < weights4->Length; k++) + { + if (boneIndices[k] < boneList->Count && weights4[k] > 0) + { + FbxCluster* pCluster = pClusterArray->GetAt(boneIndices[k]); + pCluster->AddControlPointIndex(j, weights4[k]); + } + } + } + } + + for (int j = 0; j < faceList->Count; j++) + { + ImportedFace^ face = faceList[j]; + unsigned short v1 = (unsigned short)face->VertexIndices[0]; + unsigned short v2 = (unsigned short)face->VertexIndices[1]; + unsigned short v3 = (unsigned short)face->VertexIndices[2]; + pMesh->BeginPolygon(false); + pMesh->AddPolygon(v1); + pMesh->AddPolygon(v2); + pMesh->AddPolygon(v3); + pMesh->EndPolygon(); + } + + if (hasBones) + { + FbxSkin* pSkin = FbxSkin::Create(pScene, ""); + for (int j = 0; j < boneList->Count; j++) + { + FbxCluster* pCluster = pClusterArray->GetAt(j); + if (pCluster->GetControlPointIndicesCount() > 0) + { + FbxNode* pBoneNode = pBoneNodeList->GetAt(j); + Matrix 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]; + } + } + + FbxAMatrix lMeshMatrix = pMeshNode->EvaluateGlobalTransform(); + + pCluster->SetTransformMatrix(lMeshMatrix); + pCluster->SetTransformLinkMatrix(lMeshMatrix * lBoneMatrix.Inverse()); + + pSkin->AddCluster(pCluster); + } + } + + if (pSkin->GetClusterCount() > 0) + { + pMesh->AddDeformer(pSkin); + } + } + } + finally + { + if (pClusterArray != NULL) + { + delete pClusterArray; + } + Marshal::FreeHGlobal((IntPtr)pName); + } + } + } + finally + { + if (pBoneNodeList != NULL) + { + delete pBoneNodeList; + } + } + } + + FbxFileTexture* Fbx::Exporter::ExportTexture(ImportedTexture^ matTex, FbxMesh* pMesh) + { + FbxFileTexture* pTex = NULL; + + if (matTex != nullptr) + { + String^ matTexName = matTex->Name; + char* pTexName = NULL; + try + { + pTexName = StringToCharArray(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); + + String^ path = Path::GetDirectoryName(gcnew String(pExporter->GetFileName().Buffer())); + if (path == String::Empty) + { + path = "."; + } + FileInfo^ file = gcnew FileInfo(path + Path::DirectorySeparatorChar + Path::GetFileName(matTex->Name)); + DirectoryInfo^ dir = file->Directory; + if (!dir->Exists) + { + dir->Create(); + } + BinaryWriter^ writer = gcnew BinaryWriter(file->Create()); + writer->Write(matTex->Data); + writer->Close(); + } + } + finally + { + Marshal::FreeHGlobal((IntPtr)pTexName); + } + } + + return pTex; + } + + void Fbx::Exporter::LinkTexture(ImportedMaterial^ mat, int attIndex, FbxFileTexture* pTexture, FbxProperty& prop) + { + if (mat->TexOffsets != nullptr) + { + pTexture->SetTranslation(mat->TexOffsets[attIndex].X, mat->TexOffsets[attIndex].Y); + } + if (mat->TexScales != nullptr) + { + pTexture->SetScale(mat->TexScales[attIndex].X, mat->TexScales[attIndex].Y); + } + prop.ConnectSrcObject(pTexture); + } + + void Fbx::Exporter::ExportAnimations(bool EulerFilter, float filterPrecision, bool flatInbetween) + { + List^ importedAnimationList = imported->AnimationList; + if (importedAnimationList == nullptr) + { + return; + } + + List^ pNotFound = gcnew List(); + + 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); + } + bool keyframed = dynamic_cast(importedAnimation) != nullptr; + if (keyframed) + { + ImportedKeyframedAnimation^ parser = (ImportedKeyframedAnimation^)importedAnimation; + ExportKeyframedAnimation(parser, kTakeName, lFilter, filterPrecision, pNotFound); + } + else + { + ImportedSampledAnimation^ parser = (ImportedSampledAnimation^)importedAnimation; + ExportSampledAnimation(parser, kTakeName, lFilter, filterPrecision, flatInbetween, pNotFound); + } + } + + /*if (pNotFound->Count > 0) + { + String^ pNotFoundString = gcnew String("Warning: Animations weren't exported for the following missing frames or morphs: "); + for (int i = 0; i < pNotFound->Count; i++) + { + pNotFoundString += pNotFound[i] + ", "; + } + Report::ReportLog(pNotFoundString->Substring(0, pNotFoundString->Length - 2)); + }*/ + } + + void Fbx::Exporter::ExportKeyframedAnimation(ImportedKeyframedAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* EulerFilter, float filterPrecision, List^ pNotFound) + { + List^ pAnimationList = parser->TrackList; + + char* lTakeName = kTakeName.Buffer(); + + FbxTime lTime; + 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]; + String^ name = keyframeList->Name; + int dotPos = name->IndexOf('.'); + if (dotPos >= 0 && !ImportedHelpers::FindFrame(name, imported->FrameList[0])) + { + name = name->Substring(0, dotPos); + } + FbxNode* pNode = NULL; + char* pName = NULL; + try + { + pName = Fbx::StringToCharArray(name); + pNode = pScene->GetRootNode()->FindChild(pName); + } + finally + { + Marshal::FreeHGlobal((IntPtr)pName); + } + + if (pNode == NULL) + { + if (!pNotFound->Contains(name)) + { + pNotFound->Add(name); + } + } + else + { + 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(); + + 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->SetTestForPath(true); + EulerFilter->SetQualityTolerance(filterPrecision); + EulerFilter->Apply(lCurve, 3); + } + } + } + } + + void Fbx::Exporter::ExportSampledAnimation(ImportedSampledAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* EulerFilter, float filterPrecision, bool flatInbetween, List^ pNotFound) + { + List^ pAnimationList = parser->TrackList; + + char* lTakeName = kTakeName.Buffer(); + + FbxTime lTime; + FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, lTakeName); + FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer"); + lAnimStack->AddMember(lAnimLayer); + + const double fps = 1.0 / parser->SampleRate; + + for (int j = 0; j < pAnimationList->Count; j++) + { + ImportedAnimationSampledTrack^ sampleList = pAnimationList[j]; + + int endAt; + if (sampleList->Scalings && sampleList->Scalings->Length > 0) + { + endAt = sampleList->Scalings->Length; + } + else if (sampleList->Rotations && sampleList->Rotations->Length > 0) + { + endAt = sampleList->Rotations->Length; + } + else if (sampleList->Translations && sampleList->Translations->Length > 0) + { + endAt = sampleList->Translations->Length; + } + else if (sampleList->Curve && sampleList->Curve->Length > 0) + { + endAt = sampleList->Curve->Length; + } + + String^ name = sampleList->Name; + int dotPos = name->IndexOf('.'); + if (dotPos >= 0 && !ImportedHelpers::FindFrame(name, imported->FrameList[0])) + { + name = name->Substring(0, dotPos); + } + FbxNode* pNode = NULL; + char* pName = NULL; + try + { + pName = Fbx::StringToCharArray(name); + pNode = pScene->GetRootNode()->FindChild(pName); + } + finally + { + Marshal::FreeHGlobal((IntPtr)pName); + } + + if (pNode == NULL) + { + if (!pNotFound->Contains(name)) + { + pNotFound->Add(name); + } + } + else + { + if (sampleList->Scalings) + { + 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); + lCurveSX->KeyModifyBegin(); + lCurveSY->KeyModifyBegin(); + lCurveSZ->KeyModifyBegin(); + for (int k = 0; k < endAt; k++) + { + if (!sampleList->Scalings[k].HasValue) + continue; + + lTime.SetSecondDouble(fps * k); + + lCurveSX->KeySet(lCurveSX->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.X); + lCurveSY->KeySet(lCurveSY->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.Y); + lCurveSZ->KeySet(lCurveSZ->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.Z); + } + lCurveSX->KeyModifyEnd(); + lCurveSY->KeyModifyEnd(); + lCurveSZ->KeyModifyEnd(); + } + + if (sampleList->Rotations) + { + 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); + lCurveRX->KeyModifyBegin(); + lCurveRY->KeyModifyBegin(); + lCurveRZ->KeyModifyBegin(); + for (int k = 0; k < endAt; k++) + { + if (!sampleList->Rotations[k].HasValue) + continue; + + lTime.SetSecondDouble(fps * k); + + Vector3 rotation = Fbx::QuaternionToEuler(sampleList->Rotations[k].Value); + lCurveRX->KeySet(lCurveRX->KeyAdd(lTime), lTime, rotation.X); + lCurveRY->KeySet(lCurveRY->KeyAdd(lTime), lTime, rotation.Y); + lCurveRZ->KeySet(lCurveRZ->KeyAdd(lTime), lTime, rotation.Z); + } + lCurveRX->KeyModifyEnd(); + lCurveRY->KeyModifyEnd(); + lCurveRZ->KeyModifyEnd(); + + if (EulerFilter) + { + FbxAnimCurve* lCurve[3]; + lCurve[0] = lCurveRX; + lCurve[1] = lCurveRY; + lCurve[2] = lCurveRZ; + EulerFilter->Reset(); + EulerFilter->SetTestForPath(true); + EulerFilter->SetQualityTolerance(filterPrecision); + EulerFilter->Apply(lCurve, 3); + } + } + + if (sampleList->Translations) + { + 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); + lCurveTX->KeyModifyBegin(); + lCurveTY->KeyModifyBegin(); + lCurveTZ->KeyModifyBegin(); + for (int k = 0; k < endAt; k++) + { + if (!sampleList->Translations[k].HasValue) + continue; + + lTime.SetSecondDouble(fps * k); + + lCurveTX->KeySet(lCurveTX->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.X); + lCurveTY->KeySet(lCurveTY->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.Y); + lCurveTZ->KeySet(lCurveTZ->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.Z); + } + lCurveTX->KeyModifyEnd(); + lCurveTY->KeyModifyEnd(); + lCurveTZ->KeyModifyEnd(); + } + + if (sampleList->Curve) + { + FbxNode* pMeshNode = pNode->GetChild(0); + FbxMesh* pMesh = pMeshNode ? pMeshNode->GetMesh() : NULL; + if (pMesh) + { + name = sampleList->Name->Substring(dotPos + 1); + int numBlendShapes = pMesh->GetDeformerCount(FbxDeformer::eBlendShape); + for (int bsIdx = 0; bsIdx < numBlendShapes; bsIdx++) + { + FbxBlendShape* lBlendShape = (FbxBlendShape*)pMesh->GetDeformer(bsIdx, FbxDeformer::eBlendShape); + int numChannels = lBlendShape->GetBlendShapeChannelCount(); + float flatMinStrength = 0, flatMaxStrength; + String^ shapeName = nullptr; + for (int chnIdx = 0; chnIdx < numChannels; chnIdx++) + { + FbxBlendShapeChannel* lChannel = lBlendShape->GetBlendShapeChannel(chnIdx); + String^ keyframeName; + if (!flatInbetween) + { + keyframeName = gcnew String(lChannel->GetName()); + } + else + { + shapeName = gcnew String(lChannel->GetTargetShape(0)->GetName()); + keyframeName = shapeName->Substring(0, shapeName->LastIndexOf("_")); + } + if (keyframeName == name) + { + FbxAnimCurve* lCurve = lChannel->DeformPercent.GetCurve(lAnimLayer, true); + if (flatInbetween) + { + FbxProperty weightProp; + WITH_MARSHALLED_STRING + ( + weightName, + shapeName + ".Weight", + weightProp = pMesh->FindProperty(weightName); + ); + if (weightProp.IsValid()) + { + flatMaxStrength = (float)weightProp.Get(); + } + else + { + flatMaxStrength = 100; + //Report::ReportLog("Error! Weight for flat Blend-Shape " + shapeName + " not found! Using a value of " + flatMaxStrength); + } + } + lCurve->KeyModifyBegin(); + for (int k = 0; k < endAt; k++) + { + if (!sampleList->Curve[k].HasValue) + { + continue; + } + + lTime.SetSecondDouble(fps * k); + + auto keySetIndex = lCurve->KeyAdd(lTime); + + if (!flatInbetween) + { + lCurve->KeySet(keySetIndex, lTime, sampleList->Curve[k].Value); + } + else + { + float val = sampleList->Curve[k].Value; + if (val >= flatMinStrength && val <= flatMaxStrength) + { + val = (val - flatMinStrength) * 100 / (flatMaxStrength - flatMinStrength); + } + else if (val < flatMinStrength) + { + val = 0; + } + else if (val > flatMaxStrength) + { + val = 100; + } + lCurve->KeySet(keySetIndex, lTime, val); + } + } + lCurve->KeyModifyEnd(); + if (!flatInbetween) + { + bsIdx = numBlendShapes; + break; + } + else + { + flatMinStrength = flatMaxStrength; + } + } + } + } + } + else + { + name = sampleList->Name; + if (!pNotFound->Contains(name)) + { + pNotFound->Add(name); + } + } + } + } + } + } + + void Fbx::Exporter::ExportMorphs(IImported^ imported, bool morphMask, bool flatInbetween) + { + if (imported->MeshList == nullptr) + { + return; + } + + for (int meshIdx = 0; meshIdx < imported->MeshList->Count; meshIdx++) + { + ImportedMesh^ meshList = imported->MeshList[meshIdx]; + FbxNode* pBaseNode = NULL; + for (int nodeIdx = 0; nodeIdx < pMeshNodes->GetCount(); nodeIdx++) + { + FbxNode* pMeshNode = pMeshNodes->GetAt(nodeIdx); + String^ framePath = gcnew String(pMeshNode->GetName()); + FbxNode* rootNode = pMeshNode; + while ((rootNode = rootNode->GetParent()) != pScene->GetRootNode()) + { + framePath = gcnew String(rootNode->GetName()) + "/" + framePath; + } + if (framePath == meshList->Name) + { + pBaseNode = pMeshNode; + break; + } + } + if (pBaseNode == NULL) + { + continue; + } + + for each (ImportedMorph^ morph in imported->MorphList) + { + if (morph->Name != meshList->Name) + { + continue; + } + + int meshVertexIndex = 0; + for (int meshObjIdx = 0; meshObjIdx < meshList->SubmeshList->Count; meshObjIdx++) + { + List^ vertList = meshList->SubmeshList[meshObjIdx]->VertexList; + FbxNode* pBaseMeshNode = pBaseNode->GetChild(meshObjIdx); + FbxMesh* pBaseMesh = pBaseMeshNode->GetMesh(); + + FbxBlendShape* lBlendShape; + WITH_MARSHALLED_STRING + ( + pShapeName, + morph->ClipName + (meshList->SubmeshList->Count > 1 ? "_" + meshObjIdx : String::Empty) /*+ "_BlendShape"*/, + lBlendShape = FbxBlendShape::Create(pScene, pShapeName); + ); + pBaseMesh->AddDeformer(lBlendShape); + List^ keyframes = morph->KeyframeList; + for (int i = 0; i < morph->Channels->Count; i++) + { + FbxBlendShapeChannel* lBlendShapeChannel; + if (!flatInbetween) + { + WITH_MARSHALLED_STRING + ( + pChannelName, + gcnew String(lBlendShape->GetName()) + "." + keyframes[morph->Channels[i]->Item2]->Name->Substring(0, keyframes[morph->Channels[i]->Item2]->Name->LastIndexOf("_")), + lBlendShapeChannel = FbxBlendShapeChannel::Create(pScene, pChannelName); + ); + lBlendShapeChannel->DeformPercent = morph->Channels[i]->Item1; + lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); + } + + for (int frameIdx = 0; frameIdx < morph->Channels[i]->Item3; frameIdx++) + { + int shapeIdx = morph->Channels[i]->Item2 + frameIdx; + ImportedMorphKeyframe^ keyframe = keyframes[shapeIdx]; + + FbxShape* pShape; + if (!flatInbetween) + { + WITH_MARSHALLED_STRING + ( + pMorphShapeName, + keyframe->Name, + pShape = FbxShape::Create(pScene, pMorphShapeName); + ); + lBlendShapeChannel->AddTargetShape(pShape, keyframe->Weight); + } + else + { + lBlendShapeChannel = FbxBlendShapeChannel::Create(pScene, ""); + lBlendShapeChannel->DeformPercent = morph->Channels[i]->Item1; + lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); + + WITH_MARSHALLED_STRING + ( + pMorphShapeName, + morph->ClipName + (meshList->SubmeshList->Count > 1 ? "_" + meshObjIdx : String::Empty) + "." + keyframe->Name, + pShape = FbxShape::Create(pScene, pMorphShapeName); + ); + lBlendShapeChannel->AddTargetShape(pShape, 100); + + FbxProperty weightProp; + WITH_MARSHALLED_STRING + ( + pWeightName, + gcnew String(pShape->GetName()) + ".Weight", + weightProp = FbxProperty::Create(pBaseMesh, FbxDoubleDT, pWeightName); + ); + weightProp.ModifyFlag(FbxPropertyFlags::eUserDefined, true); + weightProp.Set(keyframe->Weight); + } + + pShape->InitControlPoints(vertList->Count); + FbxVector4* pControlPoints = pShape->GetControlPoints(); + + for (int j = 0; j < vertList->Count; j++) + { + ImportedVertex^ vertex = vertList[j]; + Vector3 coords = vertex->Position; + pControlPoints[j] = FbxVector4(coords.X, coords.Y, coords.Z, 0); + } + List^ meshIndices = keyframe->MorphedVertexIndices; + for (int j = 0; j < meshIndices->Count; j++) + { + int controlPointIndex = meshIndices[j] - meshVertexIndex; + if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) + { + Vector3 coords = keyframe->VertexList[j]->Position; + pControlPoints[controlPointIndex] = FbxVector4(coords.X, coords.Y, coords.Z, 0); + } + } + + if (flatInbetween && frameIdx > 0) + { + int shapeIdx = morph->Channels[i]->Item2 + frameIdx - 1; + ImportedMorphKeyframe^ keyframe = keyframes[shapeIdx]; + + List^ meshIndices = keyframe->MorphedVertexIndices; + for (int j = 0; j < meshIndices->Count; j++) + { + int controlPointIndex = meshIndices[j] - meshVertexIndex; + if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) + { + Vector3 coords = keyframe->VertexList[j]->Position - vertList[controlPointIndex]->Position; + pControlPoints[controlPointIndex] -= FbxVector4(coords.X, coords.Y, coords.Z, 0); + } + } + } + + if (morphMask) + { + FbxGeometryElementVertexColor* lGeometryElementVertexColor = pBaseMesh->CreateElementVertexColor(); + lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); + lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); + WITH_MARSHALLED_STRING + ( + pColourLayerName, morph->KeyframeList[shapeIdx]->Name, + lGeometryElementVertexColor->SetName(pColourLayerName); + ); + for (int j = 0; j < vertList->Count; j++) + { + lGeometryElementVertexColor->GetDirectArray().Add(FbxColor(1, 1, 1)); + } + for (int j = 0; j < meshIndices->Count; j++) + { + int controlPointIndex = meshIndices[j] - meshVertexIndex; + if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) + { + lGeometryElementVertexColor->GetDirectArray().SetAt(controlPointIndex, FbxColor(0, 0, 1)); + } + } + } + } + } + meshVertexIndex += meshList->SubmeshList[meshObjIdx]->VertexList->Count; + } + } + } + } } \ No newline at end of file diff --git a/AssetStudioFBX/ImportedFBXExporter.cpp b/AssetStudioFBX/ImportedFBXExporter.cpp deleted file mode 100644 index a4a465e..0000000 --- a/AssetStudioFBX/ImportedFBXExporter.cpp +++ /dev/null @@ -1,1266 +0,0 @@ -#include -#include -#include "AssetStudioFBX.h" - -namespace AssetStudio -{ - void Fbx::Exporter::Export(String^ path, IImported^ imported, bool EulerFilter, float filterPrecision, String^ exportFormat, bool allFrames, bool allBones, bool skins, float boneSize, bool flatInbetween, bool compatibility) - { - FileInfo^ file = gcnew FileInfo(path); - DirectoryInfo^ dir = file->Directory; - if (!dir->Exists) - { - dir->Create(); - } - String^ currentDir = Directory::GetCurrentDirectory(); - Directory::SetCurrentDirectory(dir->FullName); - path = Path::GetFileName(path); - - Exporter^ exporter = gcnew Exporter(path, imported, exportFormat, allFrames, allBones, skins, boneSize, compatibility, true); - exporter->ExportMorphs(imported, false, flatInbetween); - exporter->ExportAnimations(EulerFilter, filterPrecision, flatInbetween); - exporter->pExporter->Export(exporter->pScene); - - Directory::SetCurrentDirectory(currentDir); - } - - void Fbx::Exporter::ExportMorph(String^ path, IImported^ imported, String^ exportFormat, bool morphMask, bool flatInbetween, bool skins, float boneSize, bool compatibility) - { - FileInfo^ file = gcnew FileInfo(path); - DirectoryInfo^ dir = file->Directory; - if (!dir->Exists) - { - dir->Create(); - } - String^ currentDir = Directory::GetCurrentDirectory(); - Directory::SetCurrentDirectory(dir->FullName); - path = Path::GetFileName(path); - - Exporter^ exporter = gcnew Exporter(path, imported, exportFormat, false, true, skins, boneSize, compatibility, false); - exporter->ExportMorphs(imported, morphMask, flatInbetween); - exporter->pExporter->Export(exporter->pScene); - delete exporter; - - Directory::SetCurrentDirectory(currentDir); - } - - Fbx::Exporter::Exporter(String^ path, IImported^ imported, String^ exportFormat, bool allFrames, bool allBones, bool skins, float boneSize, bool compatibility, bool normals) - { - this->imported = imported; - exportSkins = skins; - this->boneSize = boneSize; - - cDest = NULL; - cFormat = NULL; - pSdkManager = NULL; - pScene = NULL; - pExporter = NULL; - pMaterials = NULL; - pTextures = NULL; - pMeshNodes = NULL; - - pin_ptr pSdkManagerPin = &pSdkManager; - pin_ptr pScenePin = &pScene; - Init(pSdkManagerPin, pScenePin); - - cDest = Fbx::StringToCharArray(path); - cFormat = Fbx::StringToCharArray(exportFormat); - pExporter = FbxExporter::Create(pScene, ""); - int lFormatIndex, lFormatCount = pSdkManager->GetIOPluginRegistry()->GetWriterFormatCount(); - for (lFormatIndex = 0; lFormatIndex < lFormatCount; lFormatIndex++) - { - FbxString lDesc = FbxString(pSdkManager->GetIOPluginRegistry()->GetWriterFormatDescription(lFormatIndex)); - if (lDesc.Find(cFormat) >= 0) - { - if (pSdkManager->GetIOPluginRegistry()->WriterIsFBX(lFormatIndex)) - { - if (lDesc.Find("binary") >= 0) - { - if (!compatibility || lDesc.Find("6.") >= 0) - { - break; - } - } - } - else - { - break; - } - } - } - - 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(); - FbxTime::EMode pTimeMode = FbxTime::eFrames24; - globalSettings.SetTimeMode(pTimeMode); - - if (!pExporter->Initialize(cDest, lFormatIndex, pSdkManager->GetIOSettings())) - { - throw gcnew Exception(gcnew String("Failed to initialize FbxExporter: ") + gcnew String(pExporter->GetStatus().GetErrorString())); - } - - frameNames = nullptr; - if (!allFrames) - { - frameNames = SearchHierarchy(); - if (!frameNames) - { - return; - } - } - - pMeshNodes = imported->MeshList != nullptr ? new FbxArray(imported->MeshList->Count) : NULL; - ExportFrame(pScene->GetRootNode(), imported->FrameList[0]); - - if (imported->MeshList != nullptr) - { - SetJointsFromImportedMeshes(allBones); - - pMaterials = new FbxArray(); - pTextures = new FbxArray(); - pMaterials->Reserve(imported->MaterialList->Count); - pTextures->Reserve(imported->TextureList->Count); - - for (int i = 0; i < pMeshNodes->GetCount(); i++) - { - FbxNode* meshNode = pMeshNodes->GetAt(i); - String^ meshPath = gcnew String(meshNode->GetName()); - FbxNode* rootNode = meshNode; - while ((rootNode = rootNode->GetParent()) != pScene->GetRootNode()) - { - meshPath = gcnew String(rootNode->GetName()) + "/" + meshPath; - } - ImportedMesh^ mesh = ImportedHelpers::FindMesh(meshPath, imported->MeshList); - ExportMesh(meshNode, mesh, normals); - } - } - else - { - SetJointsNode(pScene->GetRootNode()->GetChild(0), nullptr, true); - } - } - - HashSet^ Fbx::Exporter::SearchHierarchy() - { - if (imported->MeshList == nullptr || imported->MeshList->Count == 0) - { - return nullptr; - } - HashSet^ exportFrames = gcnew HashSet(); - SearchHierarchy(imported->FrameList[0], exportFrames); - return exportFrames; - } - - void Fbx::Exporter::SearchHierarchy(ImportedFrame^ frame, HashSet^ exportFrames) - { - ImportedMesh^ meshListSome = ImportedHelpers::FindMesh(frame, imported->MeshList); - if (meshListSome != nullptr) - { - ImportedFrame^ parent = frame; - while (parent != nullptr) - { - exportFrames->Add(parent->Name); - parent = (ImportedFrame^)parent->Parent; - } - - List^ boneList = meshListSome->BoneList; - if (boneList != nullptr) - { - for (int i = 0; i < boneList->Count; i++) - { - if (!exportFrames->Contains(boneList[i]->Name)) - { - ImportedFrame^ boneParent = ImportedHelpers::FindFrame(boneList[i]->Name, imported->FrameList[0]); - while (boneParent != nullptr) - { - exportFrames->Add(boneParent->Name); - boneParent = (ImportedFrame^)boneParent->Parent; - } - } - } - } - } - - for (int i = 0; i < frame->Count; i++) - { - SearchHierarchy(frame[i], exportFrames); - } - } - - void Fbx::Exporter::SetJointsFromImportedMeshes(bool allBones) - { - if (!exportSkins) - { - return; - } - HashSet^ boneNames = 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]; - boneNames->Add(bone->Name); - } - } - } - - SetJointsNode(pScene->GetRootNode()->GetChild(0), boneNames, allBones); - } - - void Fbx::Exporter::ExportFrame(FbxNode* pParentNode, ImportedFrame^ frame) - { - String^ frameName = frame->Name; - if ((frameNames == nullptr) || frameNames->Contains(frameName)) - { - FbxNode* pFrameNode = NULL; - char* pName = NULL; - try - { - pName = StringToCharArray(frameName); - pFrameNode = FbxNode::Create(pScene, pName); - } - finally - { - Marshal::FreeHGlobal((IntPtr)pName); - } - - Vector3 scale, translate; - Quaternion rotate; - frame->Matrix.Decompose(scale, rotate, translate); - Vector3 rotateVector = Fbx::QuaternionToEuler(rotate); - - pFrameNode->LclScaling.Set(FbxVector4(scale.X, scale.Y, scale.Z)); - pFrameNode->LclRotation.Set(FbxVector4(FbxDouble3(rotateVector.X, rotateVector.Y, rotateVector.Z))); - pFrameNode->LclTranslation.Set(FbxVector4(translate.X, translate.Y, translate.Z)); - pParentNode->AddChild(pFrameNode); - - if (imported->MeshList != nullptr && ImportedHelpers::FindMesh(frame, imported->MeshList) != nullptr) - { - pMeshNodes->Add(pFrameNode); - } - - for (int i = 0; i < frame->Count; i++) - { - ExportFrame(pFrameNode, frame[i]); - } - } - } - - void Fbx::Exporter::ExportMesh(FbxNode* pFrameNode, ImportedMesh^ meshList, bool normals) - { - int lastSlash = meshList->Name->LastIndexOf('/'); - String^ frameName = lastSlash < 0 ? meshList->Name : meshList->Name->Substring(lastSlash + 1); - List^ boneList = meshList->BoneList; - bool hasBones; - if (exportSkins && boneList != nullptr) - { - hasBones = boneList->Count > 0; - } - else - { - hasBones = false; - } - - FbxArray* pBoneNodeList = NULL; - try - { - if (hasBones) - { - pBoneNodeList = new FbxArray(); - pBoneNodeList->Reserve(boneList->Count); - for (int i = 0; i < boneList->Count; i++) - { - ImportedBone^ bone = boneList[i]; - String^ boneName = bone->Name; - char* pBoneName = NULL; - try - { - pBoneName = StringToCharArray(boneName); - FbxNode* foundNode = pScene->GetRootNode()->FindChild(pBoneName); - if (foundNode == NULL) - { - throw gcnew Exception(gcnew String("Couldn't find frame ") + boneName + gcnew String(" used by the bone")); - } - pBoneNodeList->Add(foundNode); - } - finally - { - Marshal::FreeHGlobal((IntPtr)pBoneName); - } - } - } - - for (int i = 0; i < meshList->SubmeshList->Count; i++) - { - char* pName = NULL; - FbxArray* pClusterArray = NULL; - try - { - pName = StringToCharArray(frameName + "_" + i); - FbxMesh* pMesh = FbxMesh::Create(pScene, ""); - - if (hasBones) - { - pClusterArray = new FbxArray(); - pClusterArray->Reserve(boneList->Count); - - for (int i = 0; i < boneList->Count; i++) - { - FbxNode* pNode = pBoneNodeList->GetAt(i); - FbxString lClusterName = pNode->GetNameOnly() + FbxString("Cluster"); - FbxCluster* pCluster = FbxCluster::Create(pSdkManager, lClusterName.Buffer()); - pCluster->SetLink(pNode); - pCluster->SetLinkMode(FbxCluster::eTotalOne); - pClusterArray->Add(pCluster); - } - } - - ImportedSubmesh^ meshObj = meshList->SubmeshList[i]; - List^ faceList = meshObj->FaceList; - List^ vertexList = meshObj->VertexList; - - pMesh->InitControlPoints(vertexList->Count); - FbxVector4* pControlPoints = pMesh->GetControlPoints(); - - FbxGeometryElementNormal* lGeometryElementNormal = NULL; - //if (normals) - { - lGeometryElementNormal = pMesh->GetElementNormal(); - if (!lGeometryElementNormal) - { - lGeometryElementNormal = pMesh->CreateElementNormal(); - } - lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect); - } - - FbxGeometryElementUV* lGeometryElementUV = pMesh->GetElementUV(); - if (!lGeometryElementUV) - { - lGeometryElementUV = pMesh->CreateElementUV(""); - } - lGeometryElementUV->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementUV->SetReferenceMode(FbxGeometryElement::eDirect); - - FbxGeometryElementTangent* lGeometryElementTangent = NULL; - if (normals) - { - lGeometryElementTangent = pMesh->GetElementTangent(); - if (!lGeometryElementTangent) - { - lGeometryElementTangent = pMesh->CreateElementTangent(); - } - lGeometryElementTangent->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementTangent->SetReferenceMode(FbxGeometryElement::eDirect); - } - - bool vertexColours = vertexList->Count > 0 && dynamic_cast(vertexList[0]) != nullptr; - if (vertexColours) - { - FbxGeometryElementVertexColor* lGeometryElementVertexColor = pMesh->CreateElementVertexColor(); - lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); - for (int j = 0; j < vertexList->Count; j++) - { - ImportedVertexWithColour^ vert = (ImportedVertexWithColour^)vertexList[j]; - lGeometryElementVertexColor->GetDirectArray().Add(FbxColor(vert->Colour.Red, vert->Colour.Green, vert->Colour.Blue, vert->Colour.Alpha)); - } - } - - FbxNode* pMeshNode = FbxNode::Create(pScene, pName); - pMeshNode->SetNodeAttribute(pMesh); - pFrameNode->AddChild(pMeshNode); - - ImportedMaterial^ mat = ImportedHelpers::FindMaterial(meshObj->Material, imported->MaterialList); - if (mat != nullptr) - { - FbxGeometryElementMaterial* lGeometryElementMaterial = pMesh->GetElementMaterial(); - if (!lGeometryElementMaterial) - { - lGeometryElementMaterial = pMesh->CreateElementMaterial(); - } - lGeometryElementMaterial->SetMappingMode(FbxGeometryElement::eByPolygon); - lGeometryElementMaterial->SetReferenceMode(FbxGeometryElement::eIndexToDirect); - - char* pMatName = NULL; - try - { - pMatName = StringToCharArray(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"; - Color4 diffuse = mat->Diffuse; - Color4 ambient = mat->Ambient; - Color4 emissive = mat->Emissive; - Color4 specular = mat->Specular; - float specularPower = mat->Power; - pMat = FbxSurfacePhong::Create(pScene, pMatName); - pMat->Diffuse.Set(FbxDouble3(diffuse.Red, diffuse.Green, diffuse.Blue)); - pMat->DiffuseFactor.Set(FbxDouble(diffuse.Alpha)); - pMat->Ambient.Set(FbxDouble3(ambient.Red, ambient.Green, ambient.Blue)); - pMat->AmbientFactor.Set(FbxDouble(ambient.Alpha)); - pMat->Emissive.Set(FbxDouble3(emissive.Red, emissive.Green, emissive.Blue)); - pMat->EmissiveFactor.Set(FbxDouble(emissive.Alpha)); - pMat->Specular.Set(FbxDouble3(specular.Red, specular.Green, specular.Blue)); - pMat->SpecularFactor.Set(FbxDouble(specular.Alpha)); - pMat->Shininess.Set(specularPower); - pMat->ShadingModel.Set(lShadingName); - - foundMat = pMaterials->GetCount(); - pMaterials->Add(pMat); - } - pMeshNode->AddMaterial(pMat); - - bool hasTexture = false; - FbxFileTexture* pTextureDiffuse = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[0], imported->TextureList), pMesh); - if (pTextureDiffuse != NULL) - { - LinkTexture(mat, 0, pTextureDiffuse, pMat->Diffuse); - pMat->TransparentColor.ConnectSrcObject(pTextureDiffuse); - hasTexture = true; - } - - FbxFileTexture* pTextureAmbient = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[1], imported->TextureList), pMesh); - if (pTextureAmbient != NULL) - { - LinkTexture(mat, 1, pTextureAmbient, pMat->Ambient); - hasTexture = true; - } - - FbxFileTexture* pTextureEmissive = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[2], imported->TextureList), pMesh); - if (pTextureEmissive != NULL) - { - LinkTexture(mat, 2, pTextureEmissive, pMat->Emissive); - hasTexture = true; - } - - FbxFileTexture* pTextureSpecular = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[3], imported->TextureList), pMesh); - if (pTextureSpecular != NULL) - { - LinkTexture(mat, 3, pTextureSpecular, pMat->Specular); - hasTexture = true; - } - - if (mat->Textures->Length > 4) - { - FbxFileTexture* pTextureBump = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[4], imported->TextureList), pMesh); - if (pTextureBump != NULL) - { - LinkTexture(mat, 4, pTextureBump, pMat->Bump); - hasTexture = true; - } - } - - if (hasTexture) - { - pMeshNode->SetShadingMode(FbxNode::eTextureShading); - } - } - finally - { - Marshal::FreeHGlobal((IntPtr)pMatName); - } - } - - for (int j = 0; j < vertexList->Count; j++) - { - ImportedVertex^ vertex = vertexList[j]; - Vector3 coords = vertex->Position; - pControlPoints[j] = FbxVector4(coords.X, coords.Y, coords.Z, 0); - //if (normals) - { - Vector3 normal = vertex->Normal; - lGeometryElementNormal->GetDirectArray().Add(FbxVector4(normal.X, normal.Y, normal.Z, 0)); - } - array^ uv = vertex->UV; - lGeometryElementUV->GetDirectArray().Add(FbxVector2(uv[0], -uv[1])); - if (normals) - { - Vector4 tangent = vertex->Tangent; - lGeometryElementTangent->GetDirectArray().Add(FbxVector4(tangent.X, tangent.Y, tangent.Z, -tangent.W)); - } - - if (hasBones) - { - array^ boneIndices = vertex->BoneIndices; - array^ weights4 = vertex->Weights; - for (int k = 0; k < weights4->Length; k++) - { - if (boneIndices[k] < boneList->Count && weights4[k] > 0) - { - FbxCluster* pCluster = pClusterArray->GetAt(boneIndices[k]); - pCluster->AddControlPointIndex(j, weights4[k]); - } - } - } - } - - for (int j = 0; j < faceList->Count; j++) - { - ImportedFace^ face = faceList[j]; - unsigned short v1 = (unsigned short)face->VertexIndices[0]; - unsigned short v2 = (unsigned short)face->VertexIndices[1]; - unsigned short v3 = (unsigned short)face->VertexIndices[2]; - pMesh->BeginPolygon(false); - pMesh->AddPolygon(v1); - pMesh->AddPolygon(v2); - pMesh->AddPolygon(v3); - pMesh->EndPolygon(); - } - - if (hasBones) - { - FbxSkin* pSkin = FbxSkin::Create(pScene, ""); - for (int j = 0; j < boneList->Count; j++) - { - FbxCluster* pCluster = pClusterArray->GetAt(j); - if (pCluster->GetControlPointIndicesCount() > 0) - { - FbxNode* pBoneNode = pBoneNodeList->GetAt(j); - Matrix 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]; - } - } - - FbxAMatrix lMeshMatrix = pMeshNode->EvaluateGlobalTransform(); - - pCluster->SetTransformMatrix(lMeshMatrix); - pCluster->SetTransformLinkMatrix(lMeshMatrix * lBoneMatrix.Inverse()); - - pSkin->AddCluster(pCluster); - } - } - - if (pSkin->GetClusterCount() > 0) - { - pMesh->AddDeformer(pSkin); - } - } - } - finally - { - if (pClusterArray != NULL) - { - delete pClusterArray; - } - Marshal::FreeHGlobal((IntPtr)pName); - } - } - } - finally - { - if (pBoneNodeList != NULL) - { - delete pBoneNodeList; - } - } - } - - FbxFileTexture* Fbx::Exporter::ExportTexture(ImportedTexture^ matTex, FbxMesh* pMesh) - { - FbxFileTexture* pTex = NULL; - - if (matTex != nullptr) - { - String^ matTexName = matTex->Name; - char* pTexName = NULL; - try - { - pTexName = StringToCharArray(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); - - String^ path = Path::GetDirectoryName(gcnew String(pExporter->GetFileName().Buffer())); - if (path == String::Empty) - { - path = "."; - } - FileInfo^ file = gcnew FileInfo(path + Path::DirectorySeparatorChar + Path::GetFileName(matTex->Name)); - DirectoryInfo^ dir = file->Directory; - if (!dir->Exists) - { - dir->Create(); - } - BinaryWriter^ writer = gcnew BinaryWriter(file->Create()); - writer->Write(matTex->Data); - writer->Close(); - } - } - finally - { - Marshal::FreeHGlobal((IntPtr)pTexName); - } - } - - return pTex; - } - - void Fbx::Exporter::LinkTexture(ImportedMaterial^ mat, int attIndex, FbxFileTexture* pTexture, FbxProperty& prop) - { - if (mat->TexOffsets != nullptr) - { - pTexture->SetTranslation(mat->TexOffsets[attIndex].X, mat->TexOffsets[attIndex].Y); - } - if (mat->TexScales != nullptr) - { - pTexture->SetScale(mat->TexScales[attIndex].X, mat->TexScales[attIndex].Y); - } - prop.ConnectSrcObject(pTexture); - } - - void Fbx::Exporter::ExportAnimations(bool EulerFilter, float filterPrecision, bool flatInbetween) - { - List^ importedAnimationList = imported->AnimationList; - if (importedAnimationList == nullptr) - { - return; - } - - List^ pNotFound = gcnew List(); - - 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); - } - bool keyframed = dynamic_cast(importedAnimation) != nullptr; - if (keyframed) - { - ImportedKeyframedAnimation^ parser = (ImportedKeyframedAnimation^)importedAnimation; - ExportKeyframedAnimation(parser, kTakeName, lFilter, filterPrecision, pNotFound); - } - else - { - ImportedSampledAnimation^ parser = (ImportedSampledAnimation^)importedAnimation; - ExportSampledAnimation(parser, kTakeName, lFilter, filterPrecision, flatInbetween, pNotFound); - } - } - - /*if (pNotFound->Count > 0) - { - String^ pNotFoundString = gcnew String("Warning: Animations weren't exported for the following missing frames or morphs: "); - for (int i = 0; i < pNotFound->Count; i++) - { - pNotFoundString += pNotFound[i] + ", "; - } - Report::ReportLog(pNotFoundString->Substring(0, pNotFoundString->Length - 2)); - }*/ - } - - void Fbx::Exporter::ExportKeyframedAnimation(ImportedKeyframedAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* EulerFilter, float filterPrecision, List^ pNotFound) - { - List^ pAnimationList = parser->TrackList; - - char* lTakeName = kTakeName.Buffer(); - - FbxTime lTime; - 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]; - String^ name = keyframeList->Name; - int dotPos = name->IndexOf('.'); - if (dotPos >= 0 && !ImportedHelpers::FindFrame(name, imported->FrameList[0])) - { - name = name->Substring(0, dotPos); - } - FbxNode* pNode = NULL; - char* pName = NULL; - try - { - pName = Fbx::StringToCharArray(name); - pNode = pScene->GetRootNode()->FindChild(pName); - } - finally - { - Marshal::FreeHGlobal((IntPtr)pName); - } - - if (pNode == NULL) - { - if (!pNotFound->Contains(name)) - { - pNotFound->Add(name); - } - } - else - { - 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(); - - 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->SetTestForPath(true); - EulerFilter->SetQualityTolerance(filterPrecision); - EulerFilter->Apply(lCurve, 3); - } - } - } - } - - void Fbx::Exporter::ExportSampledAnimation(ImportedSampledAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* EulerFilter, float filterPrecision, bool flatInbetween, List^ pNotFound) - { - List^ pAnimationList = parser->TrackList; - - char* lTakeName = kTakeName.Buffer(); - - FbxTime lTime; - FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, lTakeName); - FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer"); - lAnimStack->AddMember(lAnimLayer); - - const double fps = 1.0 / parser->SampleRate; - - for (int j = 0; j < pAnimationList->Count; j++) - { - ImportedAnimationSampledTrack^ sampleList = pAnimationList[j]; - - int endAt; - if (sampleList->Scalings && sampleList->Scalings->Length > 0) - { - endAt = sampleList->Scalings->Length; - } - else if (sampleList->Rotations && sampleList->Rotations->Length > 0) - { - endAt = sampleList->Rotations->Length; - } - else if (sampleList->Translations && sampleList->Translations->Length > 0) - { - endAt = sampleList->Translations->Length; - } - else if (sampleList->Curve && sampleList->Curve->Length > 0) - { - endAt = sampleList->Curve->Length; - } - - String^ name = sampleList->Name; - int dotPos = name->IndexOf('.'); - if (dotPos >= 0 && !ImportedHelpers::FindFrame(name, imported->FrameList[0])) - { - name = name->Substring(0, dotPos); - } - FbxNode* pNode = NULL; - char* pName = NULL; - try - { - pName = Fbx::StringToCharArray(name); - pNode = pScene->GetRootNode()->FindChild(pName); - } - finally - { - Marshal::FreeHGlobal((IntPtr)pName); - } - - if (pNode == NULL) - { - if (!pNotFound->Contains(name)) - { - pNotFound->Add(name); - } - } - else - { - if (sampleList->Scalings) - { - 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); - lCurveSX->KeyModifyBegin(); - lCurveSY->KeyModifyBegin(); - lCurveSZ->KeyModifyBegin(); - for (int k = 0; k < endAt; k++) - { - if (!sampleList->Scalings[k].HasValue) - continue; - - lTime.SetSecondDouble(fps * k); - - lCurveSX->KeySet(lCurveSX->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.X); - lCurveSY->KeySet(lCurveSY->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.Y); - lCurveSZ->KeySet(lCurveSZ->KeyAdd(lTime), lTime, sampleList->Scalings[k].Value.Z); - } - lCurveSX->KeyModifyEnd(); - lCurveSY->KeyModifyEnd(); - lCurveSZ->KeyModifyEnd(); - } - - if (sampleList->Rotations) - { - 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); - lCurveRX->KeyModifyBegin(); - lCurveRY->KeyModifyBegin(); - lCurveRZ->KeyModifyBegin(); - for (int k = 0; k < endAt; k++) - { - if (!sampleList->Rotations[k].HasValue) - continue; - - lTime.SetSecondDouble(fps * k); - - Vector3 rotation = Fbx::QuaternionToEuler(sampleList->Rotations[k].Value); - lCurveRX->KeySet(lCurveRX->KeyAdd(lTime), lTime, rotation.X); - lCurveRY->KeySet(lCurveRY->KeyAdd(lTime), lTime, rotation.Y); - lCurveRZ->KeySet(lCurveRZ->KeyAdd(lTime), lTime, rotation.Z); - } - lCurveRX->KeyModifyEnd(); - lCurveRY->KeyModifyEnd(); - lCurveRZ->KeyModifyEnd(); - - if (EulerFilter) - { - FbxAnimCurve* lCurve[3]; - lCurve[0] = lCurveRX; - lCurve[1] = lCurveRY; - lCurve[2] = lCurveRZ; - EulerFilter->Reset(); - EulerFilter->SetTestForPath(true); - EulerFilter->SetQualityTolerance(filterPrecision); - EulerFilter->Apply(lCurve, 3); - } - } - - if (sampleList->Translations) - { - 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); - lCurveTX->KeyModifyBegin(); - lCurveTY->KeyModifyBegin(); - lCurveTZ->KeyModifyBegin(); - for (int k = 0; k < endAt; k++) - { - if (!sampleList->Translations[k].HasValue) - continue; - - lTime.SetSecondDouble(fps * k); - - lCurveTX->KeySet(lCurveTX->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.X); - lCurveTY->KeySet(lCurveTY->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.Y); - lCurveTZ->KeySet(lCurveTZ->KeyAdd(lTime), lTime, sampleList->Translations[k].Value.Z); - } - lCurveTX->KeyModifyEnd(); - lCurveTY->KeyModifyEnd(); - lCurveTZ->KeyModifyEnd(); - } - - if (sampleList->Curve) - { - FbxNode* pMeshNode = pNode->GetChild(0); - FbxMesh* pMesh = pMeshNode ? pMeshNode->GetMesh() : NULL; - if (pMesh) - { - name = sampleList->Name->Substring(dotPos + 1); - int numBlendShapes = pMesh->GetDeformerCount(FbxDeformer::eBlendShape); - for (int bsIdx = 0; bsIdx < numBlendShapes; bsIdx++) - { - FbxBlendShape* lBlendShape = (FbxBlendShape*)pMesh->GetDeformer(bsIdx, FbxDeformer::eBlendShape); - int numChannels = lBlendShape->GetBlendShapeChannelCount(); - float flatMinStrength = 0, flatMaxStrength; - String^ shapeName = nullptr; - for (int chnIdx = 0; chnIdx < numChannels; chnIdx++) - { - FbxBlendShapeChannel* lChannel = lBlendShape->GetBlendShapeChannel(chnIdx); - String^ keyframeName; - if (!flatInbetween) - { - keyframeName = gcnew String(lChannel->GetName()); - } - else - { - shapeName = gcnew String(lChannel->GetTargetShape(0)->GetName()); - keyframeName = shapeName->Substring(0, shapeName->LastIndexOf("_")); - } - if (keyframeName == name) - { - FbxAnimCurve* lCurve = lChannel->DeformPercent.GetCurve(lAnimLayer, true); - if (flatInbetween) - { - FbxProperty weightProp; - WITH_MARSHALLED_STRING - ( - weightName, - shapeName + ".Weight", - weightProp = pMesh->FindProperty(weightName); - ); - if (weightProp.IsValid()) - { - flatMaxStrength = (float)weightProp.Get(); - } - else - { - flatMaxStrength = 100; - //Report::ReportLog("Error! Weight for flat Blend-Shape " + shapeName + " not found! Using a value of " + flatMaxStrength); - } - } - lCurve->KeyModifyBegin(); - for (int k = 0; k < endAt; k++) - { - if (!sampleList->Curve[k].HasValue) - { - continue; - } - - lTime.SetSecondDouble(fps * k); - - auto keySetIndex = lCurve->KeyAdd(lTime); - - if (!flatInbetween) - { - lCurve->KeySet(keySetIndex, lTime, sampleList->Curve[k].Value); - } - else - { - float val = sampleList->Curve[k].Value; - if (val >= flatMinStrength && val <= flatMaxStrength) - { - val = (val - flatMinStrength) * 100 / (flatMaxStrength - flatMinStrength); - } - else if (val < flatMinStrength) - { - val = 0; - } - else if (val > flatMaxStrength) - { - val = 100; - } - lCurve->KeySet(keySetIndex, lTime, val); - } - } - lCurve->KeyModifyEnd(); - if (!flatInbetween) - { - bsIdx = numBlendShapes; - break; - } - else - { - flatMinStrength = flatMaxStrength; - } - } - } - } - } - else - { - name = sampleList->Name; - if (!pNotFound->Contains(name)) - { - pNotFound->Add(name); - } - } - } - } - } - } - - void Fbx::Exporter::ExportMorphs(IImported^ imported, bool morphMask, bool flatInbetween) - { - if (imported->MeshList == nullptr) - { - return; - } - - for (int meshIdx = 0; meshIdx < imported->MeshList->Count; meshIdx++) - { - ImportedMesh^ meshList = imported->MeshList[meshIdx]; - FbxNode* pBaseNode = NULL; - for (int nodeIdx = 0; nodeIdx < pMeshNodes->GetCount(); nodeIdx++) - { - FbxNode* pMeshNode = pMeshNodes->GetAt(nodeIdx); - String^ framePath = gcnew String(pMeshNode->GetName()); - FbxNode* rootNode = pMeshNode; - while ((rootNode = rootNode->GetParent()) != pScene->GetRootNode()) - { - framePath = gcnew String(rootNode->GetName()) + "/" + framePath; - } - if (framePath == meshList->Name) - { - pBaseNode = pMeshNode; - break; - } - } - if (pBaseNode == NULL) - { - continue; - } - - for each (ImportedMorph^ morph in imported->MorphList) - { - if (morph->Name != meshList->Name) - { - continue; - } - - int meshVertexIndex = 0; - for (int meshObjIdx = 0; meshObjIdx < meshList->SubmeshList->Count; meshObjIdx++) - { - List^ vertList = meshList->SubmeshList[meshObjIdx]->VertexList; - FbxNode* pBaseMeshNode = pBaseNode->GetChild(meshObjIdx); - FbxMesh* pBaseMesh = pBaseMeshNode->GetMesh(); - - FbxBlendShape* lBlendShape; - WITH_MARSHALLED_STRING - ( - pShapeName, - morph->ClipName + (meshList->SubmeshList->Count > 1 ? "_" + meshObjIdx : String::Empty) /*+ "_BlendShape"*/, - lBlendShape = FbxBlendShape::Create(pScene, pShapeName); - ); - pBaseMesh->AddDeformer(lBlendShape); - List^ keyframes = morph->KeyframeList; - for (int i = 0; i < morph->Channels->Count; i++) - { - FbxBlendShapeChannel* lBlendShapeChannel; - if (!flatInbetween) - { - WITH_MARSHALLED_STRING - ( - pChannelName, - gcnew String(lBlendShape->GetName()) + "." + keyframes[morph->Channels[i]->Item2]->Name->Substring(0, keyframes[morph->Channels[i]->Item2]->Name->LastIndexOf("_")), - lBlendShapeChannel = FbxBlendShapeChannel::Create(pScene, pChannelName); - ); - lBlendShapeChannel->DeformPercent = morph->Channels[i]->Item1; - lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); - } - - for (int frameIdx = 0; frameIdx < morph->Channels[i]->Item3; frameIdx++) - { - int shapeIdx = morph->Channels[i]->Item2 + frameIdx; - ImportedMorphKeyframe^ keyframe = keyframes[shapeIdx]; - - FbxShape* pShape; - if (!flatInbetween) - { - WITH_MARSHALLED_STRING - ( - pMorphShapeName, - keyframe->Name, - pShape = FbxShape::Create(pScene, pMorphShapeName); - ); - lBlendShapeChannel->AddTargetShape(pShape, keyframe->Weight); - } - else - { - lBlendShapeChannel = FbxBlendShapeChannel::Create(pScene, ""); - lBlendShapeChannel->DeformPercent = morph->Channels[i]->Item1; - lBlendShape->AddBlendShapeChannel(lBlendShapeChannel); - - WITH_MARSHALLED_STRING - ( - pMorphShapeName, - morph->ClipName + (meshList->SubmeshList->Count > 1 ? "_" + meshObjIdx : String::Empty) + "." + keyframe->Name, - pShape = FbxShape::Create(pScene, pMorphShapeName); - ); - lBlendShapeChannel->AddTargetShape(pShape, 100); - - FbxProperty weightProp; - WITH_MARSHALLED_STRING - ( - pWeightName, - gcnew String(pShape->GetName()) + ".Weight", - weightProp = FbxProperty::Create(pBaseMesh, FbxDoubleDT, pWeightName); - ); - weightProp.ModifyFlag(FbxPropertyFlags::eUserDefined, true); - weightProp.Set(keyframe->Weight); - } - - pShape->InitControlPoints(vertList->Count); - FbxVector4* pControlPoints = pShape->GetControlPoints(); - - for (int j = 0; j < vertList->Count; j++) - { - ImportedVertex^ vertex = vertList[j]; - Vector3 coords = vertex->Position; - pControlPoints[j] = FbxVector4(coords.X, coords.Y, coords.Z, 0); - } - List^ meshIndices = keyframe->MorphedVertexIndices; - for (int j = 0; j < meshIndices->Count; j++) - { - int controlPointIndex = meshIndices[j] - meshVertexIndex; - if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) - { - Vector3 coords = keyframe->VertexList[j]->Position; - pControlPoints[controlPointIndex] = FbxVector4(coords.X, coords.Y, coords.Z, 0); - } - } - - if (flatInbetween && frameIdx > 0) - { - int shapeIdx = morph->Channels[i]->Item2 + frameIdx - 1; - ImportedMorphKeyframe^ keyframe = keyframes[shapeIdx]; - - List^ meshIndices = keyframe->MorphedVertexIndices; - for (int j = 0; j < meshIndices->Count; j++) - { - int controlPointIndex = meshIndices[j] - meshVertexIndex; - if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) - { - Vector3 coords = keyframe->VertexList[j]->Position - vertList[controlPointIndex]->Position; - pControlPoints[controlPointIndex] -= FbxVector4(coords.X, coords.Y, coords.Z, 0); - } - } - } - - if (morphMask) - { - FbxGeometryElementVertexColor* lGeometryElementVertexColor = pBaseMesh->CreateElementVertexColor(); - lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint); - lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect); - WITH_MARSHALLED_STRING - ( - pColourLayerName, morph->KeyframeList[shapeIdx]->Name, - lGeometryElementVertexColor->SetName(pColourLayerName); - ); - for (int j = 0; j < vertList->Count; j++) - { - lGeometryElementVertexColor->GetDirectArray().Add(FbxColor(1, 1, 1)); - } - for (int j = 0; j < meshIndices->Count; j++) - { - int controlPointIndex = meshIndices[j] - meshVertexIndex; - if (controlPointIndex >= 0 && controlPointIndex < vertList->Count) - { - lGeometryElementVertexColor->GetDirectArray().SetAt(controlPointIndex, FbxColor(0, 0, 1)); - } - } - } - } - } - meshVertexIndex += meshList->SubmeshList[meshObjIdx]->VertexList->Count; - } - } - } - } -}