diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
index ff74aeedf..5e3d312c6 100644
--- a/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
@@ -343,8 +343,8 @@ namespace UniGLTF
}
public static UnityPath FromAsset(UnityEngine.Object asset)
- {
- var assetPath = AssetDatabase.GetAssetPath(asset);
+ {
+ var assetPath = AssetDatabase.GetAssetPath(asset);
if (string.IsNullOrEmpty(assetPath))
{
throw new System.ArgumentNullException();
diff --git a/Assets/VRM10/Editor/MigrationMenu.cs b/Assets/VRM10/Editor/MigrationMenu.cs
index 39c62b578..d62cb76d8 100644
--- a/Assets/VRM10/Editor/MigrationMenu.cs
+++ b/Assets/VRM10/Editor/MigrationMenu.cs
@@ -6,14 +6,18 @@ namespace UniVRM10
{
public static class MigrationMenu
{
- static string s_lastPath = Application.dataPath;
+ static VRMShaders.PathObject s_lastPath = VRMShaders.PathObject.UnityAssets;
const string CONTEXT_MENU = "Assets/Migration: Vrm1";
[MenuItem(CONTEXT_MENU, true)]
static bool Enable()
{
- var path = UniGLTF.UnityPath.FromAsset(Selection.activeObject);
+ if (Selection.activeObject == null)
+ {
+ return false;
+ }
+ var path = VRMShaders.PathObject.FromAsset(Selection.activeObject);
var isVrm = path.Extension.ToLower() == ".vrm";
return isVrm;
}
@@ -21,27 +25,30 @@ namespace UniVRM10
[MenuItem(CONTEXT_MENU, false)]
static void Exec()
{
- var path = UniGLTF.UnityPath.FromAsset(Selection.activeObject);
- var isVrm = path.Extension.ToLower() == ".vrm";
+ var path = VRMShaders.PathObject.FromAsset(Selection.activeObject);
- var vrm1Bytes = MigrationVrm.Migrate(File.ReadAllBytes(path.FullPath));
+ // migrate
+ var vrm1Bytes = MigrationVrm.Migrate(path.ReadAllBytes());
- var dst = EditorUtility.SaveFilePanel(
- "Save vrm1 file",
- s_lastPath,
- $"{path.FileNameWithoutExtension}_vrm1",
- "vrm");
- if (string.IsNullOrEmpty(dst))
+ if (!s_lastPath.TrySaveDialog("Save vrm1 file", $"{path.Stem}_vrm1", out VRMShaders.PathObject dst))
{
return;
}
- s_lastPath = Path.GetDirectoryName(dst);
+ s_lastPath = dst.Parent;
// write result
- File.WriteAllBytes(dst, vrm1Bytes);
+ dst.WriteAllBytes(vrm1Bytes);
- // immediately import for GUI update
- UniGLTF.UnityPath.FromFullpath(dst).ImportAsset();
+ if (dst.IsUnderAsset)
+ {
+ // immediately import for GUI update
+ Debug.Log($"import: {dst}");
+ dst.ImportAsset();
+ }
+ else
+ {
+ Debug.Log($"write: {dst}");
+ }
}
}
}
diff --git a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs
new file mode 100644
index 000000000..aef30c3b1
--- /dev/null
+++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs
@@ -0,0 +1,183 @@
+using System;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRMShaders
+{
+ ///
+ /// UniGLTF.UnityPath (Assets の ひとつ上がルート) をすべてのパスが扱えるように拡張するのが趣旨。
+ /// readonly struct で Immutable であるという主張。
+ ///
+ public readonly struct PathObject
+ {
+ ///
+ /// * Delemeter は / を保証
+ /// * .. を解決済み
+ /// * フルパス
+ /// * 末尾に / を付けない
+ ///
+ public string FullPath { get; }
+
+ public string Extension => Path.GetExtension(FullPath);
+
+ public string Stem => Path.GetFileNameWithoutExtension(FullPath);
+
+ public PathObject Parent => FromFullPath(Path.GetDirectoryName(FullPath));
+
+ public bool IsUnderAsset => IsDescendantOf(UnityAssets);
+
+ ///
+ /// AssetDatabase の引き数になるパスを想定。
+ /// Assets のひとつ上を 基準とする相対パス。
+ /// Application.dataPath は Assets を得る。
+ ///
+ ///
+ public string UnityPath
+ {
+ get
+ {
+ var root = UnityRoot;
+ if (!IsDescendantOf(UnityRoot))
+ {
+ throw new ArgumentException($"{FullPath} is not under UnityPath");
+ }
+ return FullPath.Substring(root.FullPath.Length + 1);
+ }
+ }
+
+ static PathObject? _root;
+ public static PathObject UnityRoot
+ {
+ get
+ {
+ if (!_root.HasValue)
+ {
+ _root = FromFullPath(Path.GetDirectoryName(Application.dataPath));
+ }
+ return _root.Value;
+ }
+ }
+
+ public static PathObject UnityAssets => UnityRoot.Child("Assets/");
+
+ PathObject(string src)
+ {
+ if (string.IsNullOrEmpty(src))
+ {
+ throw new ArgumentNullException();
+ }
+ src = Path.GetFullPath(src).Replace('\\', '/');
+ if (src.Length > 1 && src[src.Length - 1] == '/')
+ {
+ // drop last /
+ src = src.Substring(0, src.Length - 1);
+ }
+ if (src[0] == '/')
+ {
+ FullPath = src;
+ }
+ else
+ {
+ if (src.Length >= 3 && src[1] == ':' && src[2] == '/')
+ {
+ FullPath = src;
+ }
+ else
+ {
+ throw new ArgumentException($"{src} is not fullpath");
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ try
+ {
+ var unityPath = UnityPath;
+ return $"";
+ }
+ catch (ArgumentException)
+ {
+ return $"";
+ }
+ }
+
+ /// start with "X:/" on Windows else "/"
+ ///
+ public static PathObject FromFullPath(string src)
+ {
+ return new PathObject(src);
+ }
+
+ /// AssetDatabase が使うパス
+ ///
+ public static PathObject FromUnityPath(string src)
+ {
+ return UnityRoot.Child(src);
+ }
+
+ public static PathObject FromAsset(UnityEngine.Object src)
+ {
+ if (src == null)
+ {
+ throw new ArgumentNullException();
+ }
+ var assetPath = AssetDatabase.GetAssetPath(src);
+ if (string.IsNullOrEmpty(assetPath))
+ {
+ throw new ArgumentException($"{src} is not asset");
+ }
+ return FromUnityPath(assetPath);
+ }
+
+ public PathObject Child(string child)
+ {
+ return FromFullPath(Path.Combine(FullPath, child));
+ }
+
+ public bool IsDescendantOf(PathObject ascendant)
+ {
+ if (!FullPath.StartsWith(ascendant.FullPath))
+ {
+ return false;
+ }
+ if (FullPath.Length <= ascendant.FullPath.Length || FullPath[ascendant.FullPath.Length] != '/')
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public byte[] ReadAllBytes()
+ {
+ return File.ReadAllBytes(FullPath);
+ }
+
+ public void WriteAllBytes(byte[] data)
+ {
+ File.WriteAllBytes(FullPath, data);
+ }
+
+ public void ImportAsset()
+ {
+ AssetDatabase.ImportAsset(UnityPath);
+ }
+
+ public bool TrySaveDialog(string title, string name, out PathObject dst)
+ {
+ var path = EditorUtility.SaveFilePanel(
+ title,
+ FullPath,
+ name,
+ "vrm");
+ if (string.IsNullOrEmpty(path))
+ {
+ dst = default;
+ return false;
+ }
+ dst = PathObject.FromFullPath(path);
+ return true;
+ }
+ }
+}
diff --git a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs.meta b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs.meta
new file mode 100644
index 000000000..0c5fa088f
--- /dev/null
+++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d16607c9cf9d05e46b5beec82002d4ef
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs b/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs
new file mode 100644
index 000000000..04028497f
--- /dev/null
+++ b/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs
@@ -0,0 +1,28 @@
+using System.Linq;
+using NUnit.Framework;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRMShaders
+{
+ public sealed class PathObjectTests
+ {
+ [Test]
+ public void Test()
+ {
+ var dataPath = PathObject.FromFullPath(Application.dataPath);
+
+ Assert.AreEqual("Assets", dataPath.Stem);
+
+ // UnityRoot
+ Assert.True(dataPath.IsDescendantOf(PathObject.UnityRoot));
+ // UnityRoot/Assets
+ Assert.False(dataPath.IsDescendantOf(PathObject.UnityAssets));
+ Assert.AreEqual(dataPath, PathObject.UnityAssets);
+
+ Assert.AreEqual(PathObject.UnityRoot.Child("Assets"), PathObject.UnityAssets);
+ Assert.AreEqual(PathObject.UnityAssets.Parent, PathObject.UnityRoot);
+ Assert.AreEqual("Assets", PathObject.UnityAssets.UnityPath);
+ }
+ }
+}
diff --git a/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs.meta b/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs.meta
new file mode 100644
index 000000000..23b001bcf
--- /dev/null
+++ b/Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 45383f6fca5a57246b70f3919b582b7d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRMShaders/VRM/IO/Runtime/VRM.meta b/Assets/VRMShaders/VRM/IO/Runtime/VRM.meta
deleted file mode 100644
index 6d0ee19da..000000000
--- a/Assets/VRMShaders/VRM/IO/Runtime/VRM.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: eb31f565eeca6164694b06ccfe3bc251
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant: