private void GenerateJobs(int count)
        {
            //unpack the geometry from plugin
            jobs.Clear();
            for (int index = 0; index < count; index++)
            {
                MeshingJob job = new MeshingJob
                {
                    id = plugin.CallStatic <string>("OnDataIndex", index)
                };

                //extract vertices
                float[] vertices = plugin.CallStatic <float[]>("OnDataGeomVertex");
                float[] normals  = plugin.CallStatic <float[]>("OnDataGeomNormal");
                job.vertices = new Vector3[vertices.Length / 3];
                job.normals  = new Vector3[normals.Length / 3];
                for (int i = 0; i < vertices.Length / 3; i++)
                {
                    job.vertices[i].x = -vertices[i * 3 + 0];
                    job.vertices[i].y = vertices[i * 3 + 1];
                    job.vertices[i].z = vertices[i * 3 + 2];
                    job.normals[i].x  = -normals[i * 3 + 0];
                    job.normals[i].y  = normals[i * 3 + 1];
                    job.normals[i].z  = normals[i * 3 + 2];
                }

                //extract colors
                ScanSettings scan = arProvider.GetSelectedAR() == ARSDK.ARENGINE ? AREngineSettings : ARCoreSettings;
                if (scan.colorizeMesh)
                {
                    int[] colors = plugin.CallStatic <int[]>("OnDataGeomColor");
                    job.colors = new Color[colors.Length / 3];
                    for (int i = 0; i < colors.Length / 3; i++)
                    {
                        job.colors[i].r = colors[i * 3 + 0] / 255.0f;
                        job.colors[i].g = colors[i * 3 + 1] / 255.0f;
                        job.colors[i].b = colors[i * 3 + 2] / 255.0f;
                        job.colors[i].a = 1.0f;
                    }
                }
                else
                {
                    job.colors = null;
                }

                //extract indices
                int[] indices = plugin.CallStatic <int[]>("OnDataGeomFace");
                job.indices = new int[indices.Length];
                for (int i = 0; i < indices.Length / 3; i++)
                {
                    job.indices[i * 3 + 0] = indices[i * 3 + 1];
                    job.indices[i * 3 + 1] = indices[i * 3 + 0];
                    job.indices[i * 3 + 2] = indices[i * 3 + 2];
                }
                jobs.Add(job);
            }
        }
        private void Update()
        {
            //exporting
            if (threadSaving)
            {
                if (plugin.CallStatic <bool>("IsSaveFinished"))
                {
                    bool success = plugin.CallStatic <bool>("IsSaveSuccessful");
                    Handheld.StopActivityIndicator();
                    arProvider.ResumeSession();
                    Utils.ShowAndroidToastMessage(success ? threadOpSave + " was saved successfully." : "Saving failed!");
                    if (success)
                    {
                        ObjReader.FilePath = threadOpSave.Replace(Application.persistentDataPath + "/", "");
                        SceneManager.LoadScene("ViewObj");
                    }
                    threadOpSave = null;
                    threadSaving = false;
                }
                return;
            }

            //meshing
            ARState state = arProvider.GetState();

            if (!threadRunning && state.tracked)
            {
                ConfigurePlugin();
                if (plugin != null)
                {
                    int count = plugin.CallStatic <int>("OnBegin", state.sessionHandle, state.frameHandle);
                    if (count >= 0)
                    {
                        threadRunning = true;
                        ScanSettings scan = arProvider.GetSelectedAR() == ARSDK.ARENGINE ? AREngineSettings : ARCoreSettings;
                        if (scan.colorizeMesh)
                        {
                            arProvider.GetCamera().targetTexture = cpuRTT;
                            RenderTexture.active = cpuRTT;
                            Camera cam  = arProvider.GetCamera();
                            int    mask = cam.cullingMask;
                            cam.cullingMask = 0;
                            cam.Render();
                            cam.cullingMask = mask;
                            cpuTexture.ReadPixels(new Rect(0, 0, cpuWidth, cpuHeight), 0, 0);
                            RenderTexture.active = null;
                            arProvider.GetCamera().targetTexture = null;
                            cpuPixels = cpuTexture.GetRawTextureData();
                            plugin.CallStatic("OnPixels", cpuWidth, cpuHeight, cpuPixels, cpuScale);
                        }
                        plugin.CallStatic("OnProcess");
                        GenerateJobs(count);
                    }
                    else
                    {
                        plugin.CallStatic("OnEnd");
                    }
                }
            }

            //visualize mesh
            DateTime t = DateTime.UtcNow;

            if (threadRunning)
            {
                while (true)
                {
                    if ((DateTime.UtcNow - t).Milliseconds > 33)
                    {
                        break;
                    }
                    else if (jobs.Count > 0)
                    {
                        MeshingJob job = jobs[jobs.Count - 1];
                        foreach (Vizualisation vizualisation in vizualisations)
                        {
                            GameObject gameObject = vizualisation.OnMeshUpdate(job.id, job.vertices, job.normals, job.colors, job.indices);
                            if (!gamesObjects.ContainsKey(job.id) && gameObject != null)
                            {
                                gamesObjects.Add(job.id, gameObject);
                            }
                        }
                        jobs.RemoveAt(jobs.Count - 1);
                    }
                    else
                    {
                        Finish();
                        break;
                    }
                }
            }

            //request save mesh
            if (!string.IsNullOrEmpty(threadOpSave) && !threadOpPause)
            {
                foreach (Vizualisation vizualisation in vizualisations)
                {
                    vizualisation.OnMeshClear();
                }
                GameObject.Find("UI").SetActive(false);
                GameObject.Find("SavingCanvas").GetComponentInChildren <Text>().text = "儲存中...\n不要關閉應用程式";
                Handheld.StartActivityIndicator();

                plugin.CallStatic("Save", threadOpSave);
                threadSaving = true;
            }
        }