using System; using System.Collections.Generic; using System.Diagnostics; namespace AssetStudio.FbxInterop { internal sealed class FbxExporter : IDisposable { private FbxExporterContext _context; private readonly string _fileName; private readonly IImported _imported; private readonly bool _allNodes; private readonly bool _exportSkins; private readonly bool _castToBone; private readonly float _boneSize; private readonly bool _exportAllUvsAsDiffuseMaps; private readonly bool _exportUV0UV1; private readonly float _scaleFactor; private readonly int _versionIndex; private readonly bool _isAscii; internal FbxExporter(string fileName, IImported imported, bool allNodes, bool exportSkins, bool castToBone, float boneSize, bool exportAllUvsAsDiffuseMaps, bool exportUV0UV1, float scaleFactor, int versionIndex, bool isAscii) { _context = new FbxExporterContext(); _fileName = fileName; _imported = imported; _allNodes = allNodes; _exportSkins = exportSkins; _castToBone = castToBone; _boneSize = boneSize; _exportAllUvsAsDiffuseMaps = exportAllUvsAsDiffuseMaps; _exportUV0UV1 = exportUV0UV1; _scaleFactor = scaleFactor; _versionIndex = versionIndex; _isAscii = isAscii; } ~FbxExporter() { Dispose(false); } public void Dispose() { if (IsDisposed) { return; } Dispose(true); GC.SuppressFinalize(this); } public bool IsDisposed { get; private set; } private void Dispose(bool disposing) { if (disposing) { _context.Dispose(); } IsDisposed = true; } internal void Initialize() { var is60Fps = _imported.AnimationList.Count > 0 && _imported.AnimationList[0].SampleRate.Equals(60.0f); _context.Initialize(_fileName, _scaleFactor, _versionIndex, _isAscii, is60Fps); if (!_allNodes) { var framePaths = SearchHierarchy(); _context.SetFramePaths(framePaths); } } internal void ExportAll(bool blendShape, bool animation, bool eulerFilter, float filterPrecision) { var meshFrames = new List(); ExportRootFrame(meshFrames); if (_imported.MeshList != null) { SetJointsFromImportedMeshes(); PrepareMaterials(); ExportMeshFrames(_imported.RootFrame, meshFrames); } else { SetJointsNode(_imported.RootFrame, null, true); } if (blendShape) { ExportMorphs(); } if (animation) { ExportAnimations(eulerFilter, filterPrecision); } ExportScene(); } private void ExportMorphs() { _context.ExportMorphs(_imported.RootFrame, _imported.MorphList); } private void ExportAnimations(bool eulerFilter, float filterPrecision) { _context.ExportAnimations(_imported.RootFrame, _imported.AnimationList, eulerFilter, filterPrecision); } private void ExportRootFrame(List meshFrames) { _context.ExportFrame(_imported.MeshList, meshFrames, _imported.RootFrame); } private void ExportScene() { _context.ExportScene(); } private void SetJointsFromImportedMeshes() { if (!_exportSkins) { return; } Debug.Assert(_imported.MeshList != null); var bonePaths = new HashSet(); foreach (var mesh in _imported.MeshList) { var boneList = mesh.BoneList; if (boneList != null) { foreach (var bone in boneList) { bonePaths.Add(bone.Path); } } } SetJointsNode(_imported.RootFrame, bonePaths, _castToBone); } private void SetJointsNode(ImportedFrame rootFrame, HashSet bonePaths, bool castToBone) { _context.SetJointsNode(rootFrame, bonePaths, castToBone, _boneSize); } private void PrepareMaterials() { _context.PrepareMaterials(_imported.MaterialList.Count, _imported.TextureList.Count); } private void ExportMeshFrames(ImportedFrame rootFrame, List meshFrames) { foreach (var meshFrame in meshFrames) { _context.ExportMeshFromFrame(rootFrame, meshFrame, _imported.MeshList, _imported.MaterialList, _imported.TextureList, _exportSkins, _exportAllUvsAsDiffuseMaps, _exportUV0UV1); } } private HashSet SearchHierarchy() { if (_imported.MeshList == null || _imported.MeshList.Count == 0) { return null; } var exportFrames = new HashSet(); SearchHierarchy(_imported.RootFrame, _imported.MeshList, exportFrames); return exportFrames; } private static void SearchHierarchy(ImportedFrame rootFrame, List meshList, HashSet exportFrames) { var frameStack = new Stack(); frameStack.Push(rootFrame); while (frameStack.Count > 0) { var frame = frameStack.Pop(); var meshListSome = ImportedHelpers.FindMesh(frame.Path, meshList); if (meshListSome != null) { var parent = frame; while (parent != null) { exportFrames.Add(parent.Path); parent = parent.Parent; } var boneList = meshListSome.BoneList; if (boneList != null) { foreach (var bone in boneList) { if (!exportFrames.Contains(bone.Path)) { var boneParent = rootFrame.FindFrameByPath(bone.Path); while (boneParent != null) { exportFrames.Add(boneParent.Path); boneParent = boneParent.Parent; } } } } } for (var i = frame.Count - 1; i >= 0; i -= 1) { frameStack.Push(frame[i]); } } } } }