Exemple #1
0
        public static void LoadFromBytes(VRMImporterContext context)
        {
            context.CreateMaterial = VRMImporter.GetMaterialFunc(glTF_VRM_Material.Parse(context.Json));

            gltfImporter.Import <glTF_VRM>(context);
            if (string.IsNullOrEmpty(context.Path))
            {
                if (string.IsNullOrEmpty(context.VRM.extensions.VRM.meta.title))
                {
                    context.Root.name = "VRM_LOADED";
                }
                else
                {
                    context.Root.name = context.VRM.extensions.VRM.meta.title;
                }
            }
            else
            {
                context.Root.name = Path.GetFileNameWithoutExtension(context.Path);
            }

            OnLoadModel(context);

            context.ShowMeshes();
        }
Exemple #2
0
        private static Schedulable <GameObject> LoadVrmAsyncInternal(VRMImporterContext ctx, bool show)
        {
            var schedulable = Schedulable.Create();

            return(schedulable
                   .AddTask(Scheduler.ThreadPool, () =>
            {
                return glTF_VRM_Material.Parse(ctx.Json);
            })
                   .ContinueWith(Scheduler.MainThread, x =>
            {
                // material function
                ctx.CreateMaterial = VRMImporter.GetMaterialFunc(x);
            })
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // textures
                for (int i = 0; i < ctx.GLTF.textures.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.MainThread,
                                   () =>
                    {
                        var texture = new TextureItem(ctx.GLTF, index);
                        texture.Process(ctx.GLTF, ctx.Storage);
                        return texture;
                    })
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.Textures.Add(x));
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () => LoadMaterials(ctx))
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // meshes
                for (int i = 0; i < ctx.GLTF.meshes.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.ThreadPool,
                                   () => gltfImporter.ReadMesh(ctx, index))
                    .ContinueWith(Scheduler.MainThread, x => gltfImporter.BuildMesh(ctx, x))
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.Meshes.Add(x))
                    ;
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () => LoadNodes(ctx))
                   .ContinueWithCoroutine(Scheduler.MainThread, () => BuildHierarchy(ctx))
                   .ContinueWith(Scheduler.CurrentThread, _ => VRMImporter.OnLoadModel(ctx))
                   .ContinueWith(Scheduler.CurrentThread,
                                 _ =>
            {
                ctx.Root.name = "VRM";

                if (show)
                {
                    ctx.ShowMeshes();
                }

                return ctx.Root;
            }));
        }
Exemple #3
0
        /// <summary>
        /// Taskで非同期にロードする例
        /// </summary>
        async void LoadVRMClicked()
        {
#if UNITY_STANDALONE_WIN
            var path = FileDialogForWindows.FileDialog("open VRM", ".vrm");
#else
            var path = Application.dataPath + "/default.vrm";
#endif
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            var context = new VRMImporterContext(path);

            var bytes = await ReadBytesAsync(path);

            // GLB形式でJSONを取得しParseします
            context.ParseVrm(bytes);

            // metaを取得(todo: thumbnailテクスチャのロード)
            var meta = context.ReadMeta();
            Debug.LogFormat("meta: title:{0}", meta.Title);

            // ParseしたJSONをシーンオブジェクトに変換していく
            var now = Time.time;
            var go  = await VRMImporter.LoadVrmAsync(context);

            var delta = Time.time - now;
            Debug.LogFormat("LoadVrmAsync {0:0.0} seconds", delta);
            OnLoaded(go);
        }
        public void ImportExportTest()
        {
            var path    = UniGLTF.UnityPath.FromUnityPath("Models/Alicia_vrm-0.40/AliciaSolid_vrm-0.40.vrm");
            var context = new VRMImporterContext(path);

            context.ParseGlb(File.ReadAllBytes(path.FullPath));
            VRMImporter.LoadFromBytes(context);

            using (new ActionDisposer(() => { GameObject.DestroyImmediate(context.Root); }))
            {
                var importJson = JsonParser.Parse(context.Json);
                importJson.SetValue("/extensions/VRM/exporterVersion", VRMVersion.VRM_VERSION);
                importJson.SetValue("/asset/generator", UniGLTF.UniGLTFVersion.UNIGLTF_VERSION);
                importJson.SetValue("/scene", 0);
                importJson.SetValue("/materials/*/doubleSided", false);
                //importJson.SetValue("/materials/*/pbrMetallicRoughness/roughnessFactor", 0);
                //importJson.SetValue("/materials/*/pbrMetallicRoughness/baseColorFactor", new float[] { 1, 1, 1, 1 });
                importJson.SetValue("/accessors/*/normalized", false);
                importJson.RemoveValue("/nodes/*/extras");
                importJson.SetValue("/bufferViews/12/byteStride", 4);
                importJson.SetValue("/bufferViews/13/byteStride", 4);
                importJson.SetValue("/bufferViews/14/byteStride", 4);
                importJson.SetValue("/bufferViews/15/byteStride", 4);
                importJson.SetValue("/bufferViews/22/byteStride", 4);
                importJson.SetValue("/bufferViews/29/byteStride", 4);
                importJson.SetValue("/bufferViews/45/byteStride", 4);
                importJson.SetValue("/bufferViews/46/byteStride", 4);
                importJson.SetValue("/bufferViews/47/byteStride", 4);
                importJson.SetValue("/bufferViews/201/byteStride", 4);
                importJson.SetValue("/bufferViews/202/byteStride", 4);
                importJson.SetValue("/bufferViews/203/byteStride", 4);
                importJson.SetValue("/bufferViews/204/byteStride", 4);
                importJson.SetValue("/bufferViews/211/byteStride", 4);
                importJson.SetValue("/bufferViews/212/byteStride", 4);
                importJson.SetValue("/bufferViews/213/byteStride", 4);
                importJson.SetValue("/bufferViews/214/byteStride", 4);
                importJson.SetValue("/bufferViews/215/byteStride", 4);
                importJson.SetValue("/bufferViews/243/byteStride", 4);
                importJson.SetValue("/bufferViews/247/byteStride", 64);
                importJson.SetValue("/bufferViews/248/byteStride", 64);
                importJson.SetValue("/bufferViews/249/byteStride", 64);
                importJson.SetValue("/bufferViews/250/byteStride", 64);
                importJson.SetValue("/bufferViews/251/byteStride", 64);
                importJson.SetValue("/bufferViews/252/byteStride", 64);
                importJson.SetValue("/bufferViews/253/byteStride", 64);

                var vrm        = VRMExporter.Export(context.Root);
                var exportJson = JsonParser.Parse(vrm.ToJson());

                foreach (var kv in importJson.Diff(exportJson))
                {
                    Debug.Log(kv);
                }

                Assert.AreEqual(importJson, exportJson);
            }
        }
Exemple #5
0
        public static void LoadFromBytes(VRMImporterContext context)
        {
            context.CreateMaterial = VRMImporter.GetMaterialFunc(glTF_VRM_Material.Parse(context.Json));

            gltfImporter.Load(context);

            OnLoadModel(context);

            context.ShowMeshes();
        }
Exemple #6
0
        static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
        {
            foreach (string path in importedAssets)
            {
                var ext = Path.GetExtension(path).ToLower();
                if (ext == ".vrm")
                {
                    var context = new VRMImporterContext(path);

                    context.ParseVrm(File.ReadAllBytes(context.Path));

                    //
                    // https://answers.unity.com/questions/647615/how-to-update-import-settings-for-newly-created-as.html
                    //
                    for (int i = 0; i < context.GLTF.textures.Count; ++i)
                    {
                        var x     = context.GLTF.textures[i];
                        var image = context.GLTF.images[x.source];
                        if (string.IsNullOrEmpty(image.uri))
                        {
                            // glb buffer
                            var folder = context.GetAssetFolder(".Textures").AssetPathToFullPath();
                            if (!Directory.Exists(folder))
                            {
                                UnityEditor.AssetDatabase.CreateFolder(context.GLTF.baseDir, Path.GetFileNameWithoutExtension(context.Path) + ".Textures");
                                //Directory.CreateDirectory(folder);
                            }

                            var textureName = !string.IsNullOrEmpty(image.name) ? image.name: string.Format("buffer#{0:00}", i);
                            var png         = Path.Combine(folder, textureName + ".png");
                            var byteSegment = context.GLTF.GetViewBytes(image.bufferView);
                            File.WriteAllBytes(png, byteSegment.ToArray());
                            var assetPath = png.ToUnityRelativePath();
                            //Debug.LogFormat("import asset {0}", assetPath);
                            UnityEditor.AssetDatabase.ImportAsset(assetPath, UnityEditor.ImportAssetOptions.ForceUpdate);
                            UnityEditor.AssetDatabase.Refresh();
                            image.uri = assetPath.Substring(context.GLTF.baseDir.Length + 1);
                        }
                    }

                    EditorApplication.delayCall += () =>
                    {
                        // delay and can import png texture
                        VRMImporter.LoadFromBytes(context);
                        context.SaveAsAsset();
                        context.Destroy(false);
                    };
                }
            }
        }
Exemple #7
0
        void LoadAsync(VRMImporterContext context)
        {
#if true
            var now = Time.time;
            VRMImporter.LoadVrmAsync(context, go => {
                var delta = Time.time - now;
                Debug.LogFormat("LoadVrmAsync {0:0.0} seconds", delta);
                OnLoaded(go);
            });
#else
            // ローカルファイルシステムからロードします
            VRMImporter.LoadVrmAsync(path, OnLoaded);
#endif
        }
        public void MeshCoyTest()
        {
            var path    = UniGLTF.UnityPath.FromUnityPath("Models/Alicia_vrm-0.40/AliciaSolid_vrm-0.40.vrm");
            var context = new VRMImporterContext();

            context.ParseGlb(File.ReadAllBytes(path.FullPath));
            VRMImporter.LoadFromBytes(context);

            foreach (var mesh in context.Meshes)
            {
                var src = mesh.Mesh;
                var dst = src.Copy(true);
                MeshTests.MeshEquals(src, dst);
            }
        }
        static void Import(string readPath, UnityPath prefabPath)
        {
            var bytes   = File.ReadAllBytes(readPath);
            var context = new VRMImporterContext(UnityPath.FromFullpath(readPath));

            context.ParseGlb(File.ReadAllBytes(readPath));
            context.SaveTexturesAsPng(prefabPath);

            EditorApplication.delayCall += () =>
            {
                // delay and can import png texture
                VRMImporter.LoadFromBytes(context);
                context.SaveAsAsset(prefabPath);
                context.Destroy(false);
            };
        }
Exemple #10
0
        void LoadModel(string path)
        {
            if (!File.Exists(path))
            {
                return;
            }

            Debug.LogFormat("{0}", path);
            var ext = Path.GetExtension(path).ToLower();

            switch (ext)
            {
            case ".vrm":
            {
                var context = new VRMImporterContext();

                var file = File.ReadAllBytes(path);
                context.ParseGlb(file);

                m_texts.UpdateMeta(context);
                //UniJSON.JsonParser.Parse(context.Json);

                VRMImporter.LoadVrmAsync(context, SetModel);

                break;
            }

            case ".glb":
            {
                var context = new UniGLTF.ImporterContext();

                var file = File.ReadAllBytes(path);
                context.ParseGlb(file);

                UniGLTF.gltfImporter.Load(context);
                context.ShowMeshes();
                SetModel(context.Root);

                break;
            }

            default:
                Debug.LogWarningFormat("unknown file type: {0}", path);
                break;
            }
        }
Exemple #11
0
        /// <summary>
        /// メタが不要な場合のローダー
        /// </summary>
        void LoadVRMClicked_without_meta()
        {
#if UNITY_STANDALONE_WIN
            var path = FileDialogForWindows.FileDialog("open VRM", ".vrm");
#else
            var path = Application.dataPath + "/default.vrm";
#endif
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

#if true
            var bytes = File.ReadAllBytes(path);
            // なんらかの方法でByte列を得た

            var context = new VRMImporterContext();

            // GLB形式でJSONを取得しParseします
            context.ParseGlb(bytes);

            if (m_loadAsync)
            {
                // ローカルファイルシステムからロードします
                LoadAsync(context);
            }
            else
            {
                context.Load();
                OnLoaded(context);
            }
#else
            // ParseしたJSONをシーンオブジェクトに変換していく
            if (m_loadAsync)
            {
                // ローカルファイルシステムからロードします
                VRMImporter.LoadVrmAsync(path, OnLoaded);
            }
            else
            {
                var root = VRMImporter.LoadFromPath(path);
                OnLoaded(root);
            }
#endif
        }
Exemple #12
0
        static void ImportMenu()
        {
            var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");

            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            var ext = Path.GetExtension(path).ToLower();

            if (ext != ".vrm")
            {
                return;
            }

            if (path.StartsWithUnityAssetPath())
            {
                Debug.LogWarning("vrm in AssetFolder is imported automatically");
                Selection.activeObject = AssetDatabase.LoadAssetAtPath <GameObject>(path.Replace(".vrm", ".prefab").ToUnityRelativePath());
                return;
            }

            var root = VRMImporter.LoadFromPath(path);

            if (!EditorApplication.isPlaying)
            {
                // save root as Asset
                var prefabPath = EditorUtility.SaveFilePanel("save vrm prefab",
                                                             "Assets",
                                                             Path.GetFileNameWithoutExtension(path) + ".prefab",
                                                             "prefab"
                                                             );

                if (!string.IsNullOrEmpty(prefabPath))
                {
                    VRMAssetWriter.SaveAsPrefab(root, prefabPath);

                    var prefab = AssetDatabase.LoadAssetAtPath <GameObject>(prefabPath.ToUnityRelativePath());
                    Selection.activeObject = prefab;
                }
            }
        }
Exemple #13
0
        void OnOpenClicked()
        {
            var path = FileDialogForWindows.FileDialog("open vrm", "vrm");

            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            Debug.LogFormat("{0}", path);

            var go = VRMImporter.LoadFromPath(path);

            if (go == null)
            {
                return;
            }

            SetModel(go);
        }
        static void ImportVrm(UnityPath path)
        {
            if (!path.IsUnderAssetsFolder)
            {
                throw new Exception();
            }
            var context = new VRMImporterContext(path);

            context.ParseGlb(File.ReadAllBytes(path.FullPath));

            var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");

            context.SaveTexturesAsPng(prefabPath);

            EditorApplication.delayCall += () =>
            {
                // delay and can import png texture
                VRMImporter.LoadFromBytes(context);
                context.SaveAsAsset(prefabPath);
                context.Destroy(false);
            };
        }
Exemple #15
0
        static void ImportMenu()
        {
            var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");

            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            if (Application.isPlaying)
            {
                // load into scene
                Selection.activeGameObject = VRMImporter.LoadFromPath(path);
            }
            else
            {
                if (path.StartsWithUnityAssetPath())
                {
                    Debug.LogWarningFormat("disallow import from folder under the Assets");
                    return;
                }

                var assetPath = EditorUtility.SaveFilePanel("save prefab", "Assets", Path.GetFileNameWithoutExtension(path), "prefab");
                if (string.IsNullOrEmpty(path))
                {
                    return;
                }

                if (!assetPath.StartsWithUnityAssetPath())
                {
                    Debug.LogWarningFormat("out of asset path: {0}", assetPath);
                    return;
                }

                // import as asset
                Import(path, UnityPath.FromUnityPath(assetPath));
            }
        }
Exemple #16
0
        void LoadVRMClicked()
        {
#if UNITY_STANDALONE_WIN
            var path = FileDialogForWindows.FileDialog("open VRM", ".vrm");
#else
            var path = Application.dataPath + "/default.vrm";
#endif
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            var bytes = File.ReadAllBytes(path);
            // なんらかの方法でByte列を得た

            var context = new VRMImporterContext(path);

            // GLB形式でJSONを取得しParseします
            context.ParseVrm(bytes);


            // metaを取得(todo: thumbnailテクスチャのロード)
            var meta = context.ReadMeta();
            Debug.LogFormat("meta: title:{0}", meta.Title);


            // ParseしたJSONをシーンオブジェクトに変換していく
            if (m_loadAsync)
            {
                LoadAsync(context);
            }
            else
            {
                VRMImporter.LoadFromBytes(context);
                OnLoaded(context.Root);
            }
        }
Exemple #17
0
        static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
        {
            foreach (string path in importedAssets)
            {
                var ext = Path.GetExtension(path).ToLower();
                if (ext == ".vrm")
                {
                    var context = new VRMImporterContext(path);
                    try
                    {
                        VRMImporter.LoadFromPath(context);

                        /*
                         * var prefabPath = String.Format("{0}/{1}.prefab",
                         *  Path.GetDirectoryName(path),
                         *  Path.GetFileNameWithoutExtension(path));
                         *
                         * VRMAssetWriter.SaveAsPrefab(context.Root, prefabPath);
                         *
                         * var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.ToUnityRelativePath());
                         * Selection.activeObject = prefab;
                         */

                        context.SaveAsAsset();
                        context.Destroy(false);
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError(ex);
                        if (context != null)
                        {
                            context.Destroy(true);
                        }
                    }
                }
            }
        }
Exemple #18
0
        private static Schedulable <GameObject> LoadVrmAsyncInternal(VRMImporterContext ctx)
        {
            var schedulable = Schedulable.Create();

            return(schedulable
                   .AddTask(Scheduler.ThreadPool, () =>
            {
                ctx.GLTF.baseDir = Path.GetDirectoryName(ctx.Path);
                return Unit.Default;
            })
                   .ContinueWith(Scheduler.ThreadPool, _ =>
            {
                return glTF_VRM_Material.Parse(ctx.Json);
            })
                   .ContinueWith(Scheduler.MainThread, x =>
            {
                // material function
                ctx.CreateMaterial = VRMImporter.GetMaterialFunc(x);
            })
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // textures
                for (int i = 0; i < ctx.GLTF.textures.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.MainThread,
                                   () =>
                    {
                        var texture = new TextureItem(ctx.GLTF, index);
                        texture.Process();
                        return texture;
                    })
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.Textures.Add(x));
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () => LoadMaterials(ctx))
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // meshes
                for (int i = 0; i < ctx.GLTF.meshes.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.ThreadPool,
                                   () => gltfImporter.ReadMesh(ctx, index))
                    .ContinueWith(Scheduler.MainThread, x => gltfImporter.BuildMesh(ctx, x))
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.Meshes.Add(x))
                    ;
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () => LoadNodes(ctx))
                   .ContinueWithCoroutine(Scheduler.MainThread, () => BuildHierarchy(ctx))
                   .ContinueWith(Scheduler.MainThread, _ => VRMImporter.OnLoadModel(ctx))
                   .ContinueWith(Scheduler.MainThread,
                                 _ =>
            {
                /*
                 * Debug.LogFormat("task end: {0}/{1}/{2}/{3}",
                 *  ctx.Textures.Count,
                 *  ctx.Materials.Count,
                 *  ctx.Meshes.Count,
                 *  ctx.Nodes.Count
                 *  );
                 */
                ctx.Root.name = Path.GetFileNameWithoutExtension(ctx.Path);

                // 非表示のメッシュを表示する
                ctx.ShowMeshes();

                return ctx.Root;
            }));
        }
Exemple #19
0
        private static Schedulable <GameObject> LoadVrmAsyncInternal(VRMImporterContext ctx, bool show)
        {
            return(Schedulable.Create()
                   .AddTask(Scheduler.ThreadPool, () =>
            {
                using (ctx.MeasureTime("glTF_VRM_Material.Parse"))
                {
                    return glTF_VRM_Material.Parse(ctx.Json);
                }
            })
                   .ContinueWith(Scheduler.MainThread, gltfMaterials =>
            {
                using (ctx.MeasureTime("new VRMMaterialImporter"))
                {
                    ctx.MaterialImporter = new VRMMaterialImporter(ctx, gltfMaterials);
                }
            })
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // textures
                for (int i = 0; i < ctx.GLTF.textures.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.MainThread,
                                   () =>
                    {
                        using (ctx.MeasureTime("texture.Process"))
                        {
                            var texture = new TextureItem(ctx.GLTF, index);
                            texture.Process(ctx.GLTF, ctx.Storage);
                            return texture;
                        }
                    })
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.AddTexture(x));
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () => LoadMaterials(ctx))
                   .OnExecute(Scheduler.ThreadPool, parent =>
            {
                // meshes
                for (int i = 0; i < ctx.GLTF.meshes.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(Scheduler.ThreadPool,
                                   () =>
                    {
                        using (ctx.MeasureTime("ReadMesh"))
                        {
                            return gltfImporter.ReadMesh(ctx, index);
                        }
                    })
                    .ContinueWith(Scheduler.MainThread, x =>
                    {
                        using (ctx.MeasureTime("BuildMesh"))
                        {
                            return gltfImporter.BuildMesh(ctx, x);
                        }
                    })
                    .ContinueWith(Scheduler.ThreadPool, x => ctx.Meshes.Add(x))
                    ;
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () =>
            {
                using (ctx.MeasureTime("LoadNodes"))
                {
                    return LoadNodes(ctx);
                }
            })
                   .ContinueWithCoroutine(Scheduler.MainThread, () =>
            {
                using (ctx.MeasureTime("BuildHierarchy"))
                {
                    return BuildHierarchy(ctx);
                }
            })
                   .ContinueWith(Scheduler.CurrentThread, _ =>
            {
                //using (ctx.MeasureTime("OnLoadModel"))
                {
                    return VRMImporter.OnLoadModel(ctx);
                }
            })
                   .ContinueWith(Scheduler.CurrentThread,
                                 _ =>
            {
                ctx.Root.name = "VRM";

                if (show)
                {
                    ctx.ShowMeshes();
                }

                Debug.Log(ctx.GetSpeedLog());
                return ctx.Root;
            }));
        }
Exemple #20
0
        public static void LoadVrmAsync(VRMImporterContext ctx, ArraySegment <Byte> chunkData, Action <GameObject> onLoaded)
        {
            var schedulable = Schedulable.Create();

            schedulable
            .AddTask(MainThreadDispatcher.Instance.ThreadScheduler, () =>
            {
                ctx.GLTF.baseDir = Path.GetDirectoryName(ctx.Path);
                foreach (var buffer in ctx.GLTF.buffers)
                {
                    buffer.OpenStorage(ctx.GLTF.baseDir, chunkData);
                }
                return(Unit.Default);
            })
            .ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, _ =>
            {
                return(glTF_VRM_Material.Parse(ctx.Json));
            })
            .ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, x =>
            {
                // material function
                ctx.CreateMaterial = VRMImporter.GetMaterialFunc(x);
            })
            .OnExecute(MainThreadDispatcher.Instance.UnityScheduler, parent =>
            {
                // textures
                for (int i = 0; i < ctx.GLTF.textures.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(MainThreadDispatcher.Instance.UnityScheduler,
                                   () => gltfImporter.ImportTexture(ctx.GLTF, index))
                    .ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, x => ctx.Textures.Add(x));
                }
            })
            .ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => LoadMaterials(ctx))
            .OnExecute(MainThreadDispatcher.Instance.UnityScheduler, parent =>
            {
                // meshes
                for (int i = 0; i < ctx.GLTF.meshes.Count; ++i)
                {
                    var index = i;
                    parent.AddTask(MainThreadDispatcher.Instance.ThreadScheduler,
                                   () => gltfImporter.ReadMesh(ctx, index))
                    .ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, x => gltfImporter.BuildMesh(ctx, x))
                    .ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, x => ctx.Meshes.Add(x))
                    ;
                }
            })
            .ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => LoadNodes(ctx))
            .ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => BuildHierarchy(ctx))
            .ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, _ => VRMImporter.OnLoadModel(ctx))
            .Subscribe(MainThreadDispatcher.Instance.UnityScheduler,
                       _ =>
            {
                /*
                 * Debug.LogFormat("task end: {0}/{1}/{2}/{3}",
                 *  ctx.Textures.Count,
                 *  ctx.Materials.Count,
                 *  ctx.Meshes.Count,
                 *  ctx.Nodes.Count
                 *  );
                 */
                ctx.Root.name = Path.GetFileNameWithoutExtension(ctx.Path);

                // 非表示のメッシュを表示する
                ctx.ShowMeshes();

                onLoaded(ctx.Root);
            }, ex =>
            {
                Debug.LogError(ex);
            })
            ;
        }