/// <summary> /// Export to a file asynchronously. /// </summary> /// <remarks> /// This method must be used as a coroutine. Failure to do so whould result in no export whatsoever. In particular, /// for successful export, the returned iterator must be itered over until it's end. /// </remarks> /// <param name="root">The root gameobject of the hierarchy to export.</param> /// <param name="filename">The file name and path to export to.</param> /// <param name="return_callback">The callback that will be called on export completion.</param> /// <param name="progress_callback">The callback that will be called periodically during export to notify current progress.</param> /// <returns>An iterator to use in a coroutine.</returns> public IEnumerator Export(GameObject root, string filename, Module.ExporterSuccessCallback return_callback, Module.ProgressCallback progress_callback = null) { if (isDisposed) { throw new ObjectDisposedException(GetType().FullName); } Module.ProgressCallback progress1 = null; Module.ProgressCallback progress2 = null; if (progress_callback != null) { progress1 = p => progress_callback(p * Importer.unityLoadingPercentage); progress2 = p => progress_callback(p * (1f - Importer.unityLoadingPercentage) + Importer.unityLoadingPercentage); } string extension = Path.GetExtension(filename).Remove(0, 1).ToLower(); Module.IExporter module; if (extensionHandler.TryGetValue(extension, out module)) { return(Export((scene, success) => module.ExportToFile(scene, filename, success, progress2), root, filename, return_callback, progress1)); } else { Debug.LogErrorFormat("Unsupported format with extension '{0}'. No exporter is registered for this format.", extension); } return(null); }
private IEnumerator Export(Func <Type.Scene, Module.ExporterSuccessCallback, IEnumerator> exporter, GameObject root, string filename, Module.ExporterSuccessCallback return_callback, Module.ProgressCallback progress_callback = null) { bool success = false; bool waiting; do { lock (this) { if (exporting) { waiting = true; } else { exporting = true; waiting = false; } } if (waiting) { yield return(null); } }while(waiting); if (exporter != null) { int refresh_rate = Screen.currentResolution.refreshRate; float max_frame_duration = 1000.0f * 0.75f * (1.0f / (float)(refresh_rate >= 20 ? refresh_rate : 60)); // In milliseconds. Use only 75% of the available time to avoid missing vsync events // Create timer to break the code that must be executed in unity thread into chunks that will fit into the required target framerate System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); DateTime start = DateTime.Now; Type.Scene scene = null; // Get data from Unity IEnumerator it = Type.Scene.FromUnity(root, s => scene = s, progress_callback); timer.Start(); // Split code executed in unity thread into chunks that allow to maintain targeted framerate, // without loosing unnecessary time by yielding every time possible (because 1 yield <=> 1 frame) while (it.MoveNext()) { if (timer.ElapsedMilliseconds >= max_frame_duration) { yield return(null); timer.Reset(); timer.Start(); } } if (scene != null) { // Export data to final format it = exporter(scene, s => success = s); while (it.MoveNext()) { yield return(it.Current); } DateTime end = DateTime.Now; if (success) { Debug.LogFormat("Export successful: {0}.", end.Subtract(start)); } else { Debug.LogErrorFormat("Export to '{0}' failed.", filename); } } else { Debug.LogErrorFormat("Export to '{0}' failed.", filename); } } else { Debug.LogError("Invalid null exporter."); } // Ready to accept new exports lock (this) { exporting = false; } if (return_callback != null) { return_callback(success); } }
public IEnumerator ToAssimp(Module.Export.Assimp.Context context, string filename, aiPostProcessSteps steps, Module.ExporterSuccessCallback return_callback, Module.ProgressCallback progress_callback) { bool success = false; string extension = System.IO.Path.GetExtension(filename).Remove(0, 1).ToLower(); uint export_format_count = context.exporter.GetExportFormatCount(); bool found_exporter = false; for (uint i = 0; i < export_format_count; i++) { using (aiExportFormatDesc desc = context.exporter.GetExportFormatDescription(i)) { if (extension == desc.fileExtension.ToLower()) { using (aiScene scene = new aiScene()) { InitProgress(context, progress_callback, this); context.scene = scene; // Export nodes IResult nodes_result = context.threads.AddTask(() => { using (aiNode root = root_node.ToAssimp(context, this, null)) { scene.mRootNode = root.Unmanaged(); } }); // Export materials. context.threads.AddTask(() => Material.ToAssimp(context, this)); // We must wait for all the nodes to be processed before exporting meshes because indexes are computed during parsing. while (!nodes_result.Done) { context.progress.Display(); yield return(null); } // Export meshes context.threads.AddTask(() => Mesh.ToAssimp(context, this)); // Wait for all tasks to be completed IEnumerator it = context.threads.WaitForTasksCompletion(); while (it.MoveNext()) { context.progress.Display(); yield return(it.Current); } // Do the final export using Assimp now that we created the complete structure in the C++ DLL. Result <aiReturn> status = context.threads.AddTask(() => context.exporter.Export(scene, desc.id, filename, steps)); // Wait for export to complete while (!status.Done) { context.progress.Display(); yield return(null); } if (progress_callback != null) { progress_callback(1f); } context.Clean(); // Check export status if (status.Success && status.Value == aiReturn.aiReturn_SUCCESS) { success = true; } else { Debug.LogErrorFormat("Failed to export to: {0}. \nThe exporter reported the following error: {1}", filename, context.exporter.GetErrorString()); } } found_exporter = true; break; } } } if (!found_exporter) { Debug.LogErrorFormat("No exporter for format '{0}' was found in Assimp.", extension); } if (return_callback != null) { return_callback(success); } }