diff --git a/FbxExporter/Assets/UTJ/FbxExporter/Editor/FbxExporterWindow.cs b/FbxExporter/Assets/UTJ/FbxExporter/Editor/FbxExporterWindow.cs index c477ee5..005beb4 100644 --- a/FbxExporter/Assets/UTJ/FbxExporter/Editor/FbxExporterWindow.cs +++ b/FbxExporter/Assets/UTJ/FbxExporter/Editor/FbxExporterWindow.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Text.RegularExpressions; using System.Collections.Generic; @@ -18,12 +19,20 @@ public static void Open() window.Show(); } - enum Scope + public enum Scope { Selected, EntireScene, } + public class Record + { + public FbxExporter exporter; + public string path; + public DateTime started = DateTime.Now; + } + static List s_records = new List(); + Scope m_scope = Scope.Selected; bool m_includeChildren = true; FbxExporter.Format m_format = FbxExporter.Format.FbxBinary; @@ -37,9 +46,35 @@ bool DoExport(string path, FbxExporter.Format format, GameObject[] objects) foreach (var obj in objects) exporter.AddNode(obj); - var ret = exporter.Write(path, format); - exporter.Release(); - return ret; + if(exporter.WriteAsync(path, format)) + { + Debug.Log("Export started: " + path); + s_records.Add(new Record {path = path, exporter = exporter}); + return true; + } + else + { + Debug.Log("Export failed: " + path); + return false; + } + } + + void Update() + { + // poll async write + bool finished = false; + foreach (var record in s_records) + { + if (record.exporter.IsFinished()) + { + var elapsed = DateTime.Now - record.started; + record.exporter.Release(); + Debug.Log("Export finished: " + record.path + " (" + elapsed.TotalSeconds + " seconds)"); + finished = true; + } + } + if (finished) + s_records.RemoveAll((a) => { return a.exporter.IsFinished(); }); } void OnGUI() diff --git a/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter.cs b/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter.cs index 359b911..42da417 100644 --- a/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter.cs +++ b/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter.cs @@ -41,9 +41,14 @@ public void AddNode(GameObject go) FindOrCreateNodeTree(go.GetComponent(), ProcessNode); } - public bool Write(string path, Format format) + public bool WriteAsync(string path, Format format) { - return fbxeWrite(m_ctx, path, format); + return fbxeWriteAsync(m_ctx, path, format); + } + + public bool IsFinished() + { + return fbxeIsFinished(m_ctx); } diff --git a/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter_impl.cs b/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter_impl.cs index 1e2afdb..ce13d71 100644 --- a/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter_impl.cs +++ b/FbxExporter/Assets/UTJ/FbxExporter/Scripts/FbxExporter_impl.cs @@ -77,7 +77,8 @@ public static ExportOptions defaultValue [DllImport("FbxExporterCore")] static extern void fbxeReleaseContext(Context ctx); [DllImport("FbxExporterCore")] static extern bool fbxeCreateScene(Context ctx, string name); - [DllImport("FbxExporterCore")] static extern bool fbxeWrite(Context ctx, string path, Format format); + [DllImport("FbxExporterCore")] static extern bool fbxeWriteAsync(Context ctx, string path, Format format); + [DllImport("FbxExporterCore")] static extern bool fbxeIsFinished(Context ctx); [DllImport("FbxExporterCore")] static extern Node fbxeGetRootNode(Context ctx); [DllImport("FbxExporterCore")] static extern Node fbxeFindNodeByName(Context ctx, string name); diff --git a/Plugin/FbxExporter/FbxExporter.cpp b/Plugin/FbxExporter/FbxExporter.cpp index 7d10d03..281b4c4 100644 --- a/Plugin/FbxExporter/FbxExporter.cpp +++ b/Plugin/FbxExporter/FbxExporter.cpp @@ -22,10 +22,16 @@ fbxeAPI int fbxeCreateScene(fbxe::IContext *ctx, const char *name) return ctx->createScene(name); } -fbxeAPI int fbxeWrite(fbxe::IContext *ctx, const char *path, fbxe::Format format) +fbxeAPI int fbxeWriteAsync(fbxe::IContext *ctx, const char *path, fbxe::Format format) { if (!ctx) { return false; } - return ctx->write(path, format); + return ctx->writeAsync(path, format); +} + +fbxeAPI int fbxeIsFinished(fbxe::IContext *ctx) +{ + if (!ctx) { return false; } + return ctx->isFinished(); } fbxeAPI fbxe::Node* fbxeGetRootNode(fbxe::IContext *ctx) diff --git a/Plugin/FbxExporter/FbxExporter.h b/Plugin/FbxExporter/FbxExporter.h index 8979d88..a223a0c 100644 --- a/Plugin/FbxExporter/FbxExporter.h +++ b/Plugin/FbxExporter/FbxExporter.h @@ -70,7 +70,8 @@ fbxeAPI fbxe::IContext* fbxeCreateContext(const fbxe::ExportOptions *opt); fbxeAPI void fbxeReleaseContext(fbxe::IContext *ctx); fbxeAPI int fbxeCreateScene(fbxe::IContext *ctx, const char *name); -fbxeAPI int fbxeWrite(fbxe::IContext *ctx, const char *path, fbxe::Format format); +fbxeAPI int fbxeWriteAsync(fbxe::IContext *ctx, const char *path, fbxe::Format format); +fbxeAPI int fbxeIsFinished(fbxe::IContext *ctx); fbxeAPI fbxe::Node* fbxeGetRootNode(fbxe::IContext *ctx); fbxeAPI fbxe::Node* fbxeFindNodeByName(fbxe::IContext *ctx, const char *name); diff --git a/Plugin/FbxExporter/fbxeContext.cpp b/Plugin/FbxExporter/fbxeContext.cpp index 4792576..774e446 100644 --- a/Plugin/FbxExporter/fbxeContext.cpp +++ b/Plugin/FbxExporter/fbxeContext.cpp @@ -72,7 +72,9 @@ class Context : public IContext void clear() override; bool createScene(const char *name) override; - bool write(const char *path, Format format) override; + bool writeAsync(const char *path, Format format) override; + bool isFinished() override; + void wait() override; Node* getRootNode() override; Node* findNodeByName(const char *name) override; @@ -98,12 +100,12 @@ class Context : public IContext using ContextPtr = std::shared_ptr; -static std::vector g_contexts; +static std::map g_contexts; fbxeAPI IContext* CreateContext(const ExportOptions *opt) { auto ret = new Context(opt); - g_contexts.emplace_back(ret); + g_contexts[ret].reset(ret); return ret; } @@ -127,14 +129,12 @@ Context::~Context() void Context::release() { - // don't delete here to keep proceed async tasks + g_contexts.erase(this); } void Context::clear() { - if (m_task.valid()) { - m_task.wait(); - } + wait(); if (m_scene) { m_scene->Destroy(true); m_scene = nullptr; @@ -160,26 +160,39 @@ bool Context::createScene(const char *name) return m_scene != nullptr; } -bool Context::write(const char *path_, Format format) +bool Context::writeAsync(const char *path_, Format format) { if (!m_scene) { return false; } + wait(); std::string path = path_; m_task = std::async(std::launch::async, [this, path, format]() { - for (auto& p : m_mesh_data) { - for (auto& task : p.second->tasks) { - task(); - } - } - m_mesh_data.clear(); - doWrite(path.c_str(), format); }); return true; } +bool Context::isFinished() +{ + return m_task.valid() && m_task.wait_for(std::chrono::milliseconds(0)) != std::future_status::timeout; +} + +void Context::wait() +{ + if (m_task.valid()) { + m_task.wait(); + } +} + bool Context::doWrite(const char *path, Format format) { + for (auto& p : m_mesh_data) { + for (auto& task : p.second->tasks) { + task(); + } + } + m_mesh_data.clear(); + int file_format = 0; { // search file format index diff --git a/Plugin/FbxExporter/fbxeContext.h b/Plugin/FbxExporter/fbxeContext.h index b14efc7..ece8439 100644 --- a/Plugin/FbxExporter/fbxeContext.h +++ b/Plugin/FbxExporter/fbxeContext.h @@ -11,7 +11,9 @@ class IContext virtual void clear() = 0; virtual bool createScene(const char *name) = 0; - virtual bool write(const char *path, Format format = Format::FbxBinary) = 0; + virtual bool writeAsync(const char *path, Format format = Format::FbxBinary) = 0; + virtual bool isFinished() = 0; + virtual void wait() = 0; virtual Node* getRootNode() = 0; virtual Node* findNodeByName(const char *name) = 0; diff --git a/Plugin/FbxExporter/pch.h b/Plugin/FbxExporter/pch.h index 44af6bc..c8703e0 100644 --- a/Plugin/FbxExporter/pch.h +++ b/Plugin/FbxExporter/pch.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/Plugin/Test/TestFbxExporter.cpp b/Plugin/Test/TestFbxExporter.cpp index 9e79627..755c3ae 100644 --- a/Plugin/Test/TestFbxExporter.cpp +++ b/Plugin/Test/TestFbxExporter.cpp @@ -199,10 +199,10 @@ void TestFbxExportMesh() fbxeAddMeshSubmesh(ctx, mesh, fbxe::Topology::Quads, indices.size(), indices.data(), -1); } - fbxeWrite(ctx, "mesh_binary.fbx", fbxe::Format::FbxBinary); - fbxeWrite(ctx, "mesh_ascii.fbx", fbxe::Format::FbxAscii); - fbxeWrite(ctx, "mesh_encrypted.fbx", fbxe::Format::FbxEncrypted); - fbxeWrite(ctx, "mesh_obj.obj", fbxe::Format::Obj); + fbxeWriteAsync(ctx, "mesh_binary.fbx", fbxe::Format::FbxBinary); + fbxeWriteAsync(ctx, "mesh_ascii.fbx", fbxe::Format::FbxAscii); + fbxeWriteAsync(ctx, "mesh_encrypted.fbx", fbxe::Format::FbxEncrypted); + fbxeWriteAsync(ctx, "mesh_obj.obj", fbxe::Format::Obj); fbxeReleaseContext(ctx); } RegisterTestEntry(TestFbxExportMesh) @@ -241,8 +241,8 @@ void TestFbxExportSkinnedMesh() fbxeAddMeshSubmesh(ctx, mesh, fbxe::Topology::Quads, indices.size(), indices.data(), -1); fbxeAddMeshSkin(ctx, mesh, weights.data(), num_bones, bones, bindposes); - fbxeWrite(ctx, "SkinnedMesh_binary.fbx", fbxe::Format::FbxBinary); - fbxeWrite(ctx, "SkinnedMesh_ascii.fbx", fbxe::Format::FbxAscii); + fbxeWriteAsync(ctx, "SkinnedMesh_binary.fbx", fbxe::Format::FbxBinary); + fbxeWriteAsync(ctx, "SkinnedMesh_ascii.fbx", fbxe::Format::FbxAscii); fbxeReleaseContext(ctx); } RegisterTestEntry(TestFbxExportSkinnedMesh) @@ -299,8 +299,8 @@ void TestFbxExportSkinnedMeshSegmented() fbxeAddMeshSkin(ctx, mesh, weights.data(), num_bones, bones, bindposes); } - fbxeWrite(ctx, "SkinnedMeshSegmented_binary.fbx", fbxe::Format::FbxBinary); - fbxeWrite(ctx, "SkinnedMeshSegmented_ascii.fbx", fbxe::Format::FbxAscii); + fbxeWriteAsync(ctx, "SkinnedMeshSegmented_binary.fbx", fbxe::Format::FbxBinary); + fbxeWriteAsync(ctx, "SkinnedMeshSegmented_ascii.fbx", fbxe::Format::FbxAscii); fbxeReleaseContext(ctx); } RegisterTestEntry(TestFbxExportSkinnedMeshSegmented) @@ -318,7 +318,7 @@ void TestFbxNameConflict() auto cnode11 = fbxeCreateNode(ctx, cnode2, "GrandChild $%&#?*@"); auto cnode12 = fbxeCreateNode(ctx, cnode2, "GrandChild"); - fbxeWrite(ctx, "namesanitize_ascii.fbx", fbxe::Format::FbxAscii); + fbxeWriteAsync(ctx, "namesanitize_ascii.fbx", fbxe::Format::FbxAscii); fbxeReleaseContext(ctx); } RegisterTestEntry(TestFbxNameConflict) \ No newline at end of file