Пример #1
0
        /// <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);
        }
Пример #2
0
        /// <summary>
        /// Import a file asynchronously from a given byte array as source.
        /// </summary>
        /// <remarks>
        /// This method must be used as a coroutine. Failure to do so whould result in no import whatsoever. In particular,
        /// for successful import, the returned iterator must be itered over until it's end.
        ///
        /// The file name is used mainly to determine wich importer to use based on the extension,
        /// as well as for logging and error reporting purposes.
        /// </remarks>
        /// <param name="filename">The name of the file to import.</param>
        /// <param name="data">The array containg the data to import.</param>
        /// <param name="return_callback">The callback that will be called on import completion.</param>
        /// <param name="progress_callback">The callback that will be called periodically during import to notify current progress.</param>
        /// <returns>An iterator to use in a coroutine.</returns>
        public IEnumerator Import(string filename, byte[] data, ReturnCallback 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 * (1f - unityLoadingPercentage));
                progress2 = p => progress_callback(p * unityLoadingPercentage + (1f - unityLoadingPercentage));
            }

            string extension = Path.GetExtension(filename).Remove(0, 1).ToLower();

            Module.IImporter module;

            if (extensionHandler.TryGetValue(extension, out module))
            {
                return(Import(result => module.ImportFromBytes(filename, data, result, progress1), filename, return_callback, progress2));
            }
            else
            {
                Debug.LogErrorFormat("Unsupported format with extension '{0}'. No importer is registered for this format.", extension);
            }

            return(null);
        }
Пример #3
0
        private static void InitProgress(Module.Import.Assimp.Context context, Module.ProgressCallback callback, aiScene scene)
        {
            uint nb_textures = 0;

            using (aiMaterialArray assimp_materials = scene.Materials)
            {
                uint nb_materials = assimp_materials.Size();

                for (uint i = 0; i < nb_materials; i++)
                {
                    using (aiMaterial material = assimp_materials.Get(i))
                    {
                        foreach (KeyValuePair <string, aiTextureType> pair in Assimp.Convert.textureTypes)
                        {
                            using (aiString texture_name = new aiString())
                            {
                                if (material.GetTexturePath(pair.Value, 0, texture_name))
                                {
                                    nb_textures++;
                                }
                            }
                        }
                    }
                }
            }

            uint nb_steps = Node.ASSIMP_PROGRESS_FACTOR * CountNodes(scene.mRootNode);

            nb_steps += Mesh.ASSIMP_PROGRESS_FACTOR * scene.Meshes.Size();
            nb_steps += Material.ASSIMP_PROGRESS_FACTOR * scene.Materials.Size();
            nb_steps += Texture.ASSIMP_PROGRESS_FACTOR * nb_textures;
            nb_steps  = (uint)(nb_steps / (1f - assimpNativeLoadingPrecentage));

            context.progress.Init(nb_steps, callback);
        }
Пример #4
0
        private static void InitProgress(Module.Export.Assimp.Context context, Module.ProgressCallback callback, Scene scene)
        {
            uint nb_steps = Node.ASSIMP_PROGRESS_FACTOR * CountNodes(scene.root_node);

            nb_steps += Mesh.ASSIMP_PROGRESS_FACTOR * (uint)(scene.meshes != null ? scene.meshes.Length : 0);
            nb_steps += Material.ASSIMP_PROGRESS_FACTOR * (uint)(scene.materials != null ? scene.materials.Length : 0);
            nb_steps += Texture.ASSIMP_PROGRESS_FACTOR * (uint)(scene.textures != null ? scene.textures.Length : 0);

            context.progress.Init(nb_steps, callback);
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        public static IEnumerator FromAssimp(Module.Import.Assimp.Context context, global::Assimp.Importer importer, Func <aiScene> loader, Module.ImporterReturnCallback return_callback, Module.ProgressCallback progress_callback)
        {
            if (progress_callback != null)
            {
                progress_callback(0.01f);
            }

            aiScene scene = null;

            // Async loading does not work in editor when not in play mode
            context.threads.AddTask(() => scene = loader());

            IEnumerator it = context.threads.WaitForTasksCompletion();

            while (it.MoveNext())
            {
                context.progress.Display();

                yield return(it.Current);
            }

            if (scene != null)
            {
                InitProgress(context, progress_callback, scene);

                context.progress.Update((uint)(assimpNativeLoadingPrecentage * context.progress.TotalSteps));

                context.scene = new Scene();

                context.scene.assimpTextures = new Dictionary <string, CLARTE.Backport.Tuple <Texture, uint> >();

                context.threads.AddTask(() => Mesh.FromAssimp(context, scene));
                context.threads.AddTask(() => Material.FromAssimp(context, scene));
                context.threads.AddTask(() => context.scene.root_node = Node.FromAssimp(context, scene, scene.mRootNode));

                it = context.threads.WaitForTasksCompletion();
                while (it.MoveNext())
                {
                    context.progress.Display();

                    yield return(it.Current);
                }

                // Assign materials to meshes now that all data is available
                context.threads.AddTask(() => Node.SetAssimpMeshesMaterials(context.scene, context.scene.root_node));
                // Assign textures to final array
                context.threads.AddTask(() =>
                {
                    context.scene.textures = new Texture[context.scene.assimpTextures.Count];

                    foreach (KeyValuePair <string, CLARTE.Backport.Tuple <Texture, uint> > pair in context.scene.assimpTextures)
                    {
                        context.scene.textures[pair.Value.Item2] = pair.Value.Item1;
                    }
                });

                // We can safelly dispose of the scene because all tasks must have been completed to reach this point, therefore we do not risque deallocating data used in another thread.
                // this method can take a few hundred milliseconds, therefore, we do it async and wait for it to complete before allowing new imports.
                context.threads.AddTask(scene.Dispose);
                context.threads.AddTask(importer.FreeScene);

                it = context.threads.WaitForTasksCompletion();
                while (it.MoveNext())
                {
                    context.progress.Display();

                    yield return(it.Current);
                }

                // Clean up
                context.scene.assimpTextures = null;

                if (return_callback != null)
                {
                    return_callback(context.scene);
                }

                context.Clean();
            }
            else
            {
                Debug.LogErrorFormat("Failed to open file: {0}{1}{2}.\nThe importer reported the following error: {3}", context.path, System.IO.Path.PathSeparator, context.filename, importer.GetErrorString());
            }
        }
Пример #7
0
        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);
            }
        }
Пример #8
0
        public IEnumerator ToUnity(Module.ProgressCallback progress_callback = null)
        {
            unityRoot = null;

            if (root_node != null)
            {
                GameObject node_template = new GameObject("Node");
                GameObject mesh_template = new GameObject("Mesh");
                mesh_template.AddComponent <MeshFilter>();
                mesh_template.AddComponent <MeshRenderer>();

                unityMapping    = new Mapping();
                unityReferences = new HashSet <UnityReference>();

                uint nb_steps = CountNodes(root_node);
                nb_steps += (uint)(meshes != null ? meshes.Length : 0);
                nb_steps += (uint)(materials != null ? materials.Length : 0);
                nb_steps += (uint)(textures != null ? textures.Length : 0);

                Utils.Progress progress = new Utils.Progress();
                progress.Init(nb_steps, progress_callback);

                IEnumerator it = root_node.ToUnity(this, null, node_template, mesh_template, progress);

                while (it.MoveNext())
                {
                    progress.Display();

                    yield return(it.Current);
                }

                // Destroy templates
                if (Application.isPlaying)
                {
                    UnityEngine.Object.Destroy(node_template);
                    UnityEngine.Object.Destroy(mesh_template);
                }
                else
                {
                    UnityEngine.Object.DestroyImmediate(node_template);
                    UnityEngine.Object.DestroyImmediate(mesh_template);
                }

                if (root_node.UnityNodes != null && root_node.UnityNodes.Length > 0)
                {
                    GameObject[] nodes = root_node.UnityNodes;

                    // This can happen if we have a single original mesh splitted into multiple unity meshes
                    if (nodes.Length > 1)
                    {
                        unityRoot = new GameObject(nodes[0].name);

                        // Regiter an ID for this object
                        unityMapping.Add(unityRoot, unityMapping.GetNewId());

                        unityRoot.SetActive(false);

                        unityRoot.transform.position   = nodes[0].transform.position;
                        unityRoot.transform.rotation   = nodes[0].transform.rotation;
                        unityRoot.transform.localScale = nodes[0].transform.localScale;

                        foreach (GameObject go in nodes)
                        {
                            go.transform.parent = unityRoot.transform;

                            go.SetActive(root_node.Active);
                        }
                    }
                    else
                    {
                        unityRoot = nodes[0];
                    }
                }

                // Resolve references
                foreach (UnityReference reference in unityReferences)
                {
                    reference.ToUnity(this);
                }

                if (unityRoot != null)
                {
                    unityRoot.SetActive(root_node.Active);
                }

                // Clean up
                unityReferences = null;
            }
        }
Пример #9
0
        public static IEnumerator FromUnity(GameObject root, Action <Scene> callback, Module.ProgressCallback progress_callback = null)
        {
            if (root != null && callback != null)
            {
                Scene scene = new Scene();

                scene.unityMapping    = new Mapping();
                scene.unityMeshes     = new Dictionary <UnityEngine.Mesh, int>();
                scene.unityMaterials  = new Dictionary <UnityEngine.Material, int>();
                scene.unityTextures   = new Dictionary <Texture2D, int>();
                scene.unityComponents = new Dictionary <Component, UnityComponent>();
                scene.unityReferences = new HashSet <UnityReference>();

                uint nb_nodes = CountNodes(root.transform);

                Utils.Progress progress = new Utils.Progress();
                progress.Init(nb_nodes, progress_callback);

                // Parse the node hierarchy
                IEnumerator it = Node.FromUnity(scene, root.transform, n => scene.root_node = n, progress);

                while (it.MoveNext())
                {
                    progress.Display();

                    yield return(it.Current);
                }

                int meshes_count    = scene.unityMeshes.Count;
                int materials_count = scene.unityMaterials.Count;
                int textures_count  = scene.unityTextures.Count;

                progress.Init((uint)(nb_nodes + meshes_count + materials_count + textures_count), progress_callback);
                progress.Update(nb_nodes);

                // Convert the quick lookup meshes dictionary to the final destination array
                if (meshes_count > 0)
                {
                    Mesh[] meshes = new Mesh[meshes_count];

                    foreach (KeyValuePair <UnityEngine.Mesh, int> mesh_pair in scene.unityMeshes)
                    {
                        meshes[mesh_pair.Value] = Mesh.FromUnity(mesh_pair.Key);

                        progress.Update(1);
                        progress.Display();

                        yield return(null);
                    }

                    scene.meshes = meshes;
                }

                // Convert the quick lookup materials dictionary to the final destination array
                if (materials_count > 0)
                {
                    Material[] materials = new Material[materials_count];

                    foreach (KeyValuePair <UnityEngine.Material, int> mat_pair in scene.unityMaterials)
                    {
                        materials[mat_pair.Value] = Material.FromUnity(scene, mat_pair.Key);

                        progress.Update(1);
                        progress.Display();

                        yield return(null);
                    }

                    scene.materials = materials;
                }

                // Convert the quick lookup materials dictionary to the final destination array
                if (textures_count > 0)
                {
                    Texture[] textures = new Texture[textures_count];

                    foreach (KeyValuePair <Texture2D, int> tex_pair in scene.unityTextures)
                    {
                        textures[tex_pair.Value] = Texture.FromUnity(tex_pair.Key);

                        progress.Update(1);
                        progress.Display();

                        yield return(null);
                    }

                    scene.textures = textures;
                }

                // Resolve references
                foreach (UnityReference reference in scene.unityReferences)
                {
                    reference.ResolveReference(scene.unityMapping, scene.unityMeshes, scene.unityMaterials, scene.unityTextures);
                }

                // Clean up
                scene.unityMapping    = null;
                scene.unityMeshes     = null;
                scene.unityMaterials  = null;
                scene.unityTextures   = null;
                scene.unityComponents = null;
                scene.unityReferences = null;

                // Return the result
                callback(scene);
            }
        }
Пример #10
0
        private IEnumerator Import(Func <Module.ImporterReturnCallback, IEnumerator> importer, string filename, ReturnCallback return_callback, Module.ProgressCallback progress_callback = null)
        {
            bool waiting;

            do
            {
                lock (this)
                {
                    if (importing)
                    {
                        waiting = true;
                    }
                    else
                    {
                        importing = true;

                        waiting = false;
                    }
                }

                if (waiting)
                {
                    yield return(null);
                }
            }while(waiting);

            if (importer != 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;

                // Import data
                IEnumerator it = importer(s => scene = s);

                while (it.MoveNext())
                {
                    yield return(it.Current);
                }

                if (scene != null)
                {
                    // Convert data to Unity format
                    it = scene.ToUnity(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();
                        }
                    }

                    DateTime end = DateTime.Now;

                    // Add diagnostic info
                    if (scene.UnityRoot != null)
                    {
                        int vertices_loaded = 0;
                        int faces_loaded    = 0;

                        foreach (Type.Mesh mesh in scene.meshes)
                        {
                            vertices_loaded += mesh.VerticesCount;
                            faces_loaded    += mesh.FacesCount;
                        }

                        scene.UnityRoot.AddComponent <Info>().Init(filename, end.Subtract(start), vertices_loaded, faces_loaded, scene.IdMapping.Id2Go);
                    }

                    if (return_callback != null)
                    {
                        return_callback(scene.UnityRoot);
                    }
                }
                else
                {
                    Debug.LogErrorFormat("Import of '{0}' failed.", filename);
                }
            }
            else
            {
                Debug.LogError("Invalid null importer.");
            }

            // Ready to accept new imports
            lock (this)
            {
                importing = false;
            }
        }