From 7030a4ddf4846db6efd184fdba63d3c04272a911 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 2 Jun 2022 17:56:36 +0900 Subject: [PATCH 1/4] =?UTF-8?q?VRMShaders.PathObject=20=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=80=82=20MigrationMenu=20=E3=81=A7=E4=BD=BF?= =?UTF-8?q?=E3=81=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs | 4 +- Assets/VRM10/Editor/MigrationMenu.cs | 37 ++-- .../VRMShaders/GLTF/IO/Editor/PathObject.cs | 171 ++++++++++++++++++ .../GLTF/IO/Editor/PathObject.cs.meta | 11 ++ Assets/VRMShaders/VRM/IO/Runtime/VRM.meta | 8 - 5 files changed, 206 insertions(+), 25 deletions(-) create mode 100644 Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs create mode 100644 Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs.meta delete mode 100644 Assets/VRMShaders/VRM/IO/Runtime/VRM.meta 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..51a53f800 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs @@ -0,0 +1,171 @@ +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace VRMShaders +{ + /// + /// UniGLTF.UnityPath (Assets の ひとつ上がルート) をすべてのパスが扱えるように拡張するのが趣旨。 + /// readonly struct で Immutable であるという主張。 + /// + public readonly struct PathObject + { + /// + /// * Delemeter は / を保証 + /// * .. を解決済み + /// * フルパス + /// + readonly string _FullPath; + + public string FullPath => _FullPath; + + public string Extension => Path.GetExtension(_FullPath); + + public string Stem => Path.GetFileNameWithoutExtension(_FullPath); + + public PathObject Parent => FromFullPath(Path.GetDirectoryName(_FullPath)); + + public bool IsUnderAsset + { + get + { + var assets = UnityAssets; + return _FullPath.StartsWith(assets.FullPath); + } + } + + /// + /// relative from UnityEngine.Application.dataPath + /// + /// + public string UnityPath + { + get + { + var root = UnityRoot; + if (!_FullPath.StartsWith(root.FullPath)) + { + throw new ArgumentException($"{_FullPath} is not under UnityPath"); + } + return _FullPath.Substring(root.FullPath.Length); + } + } + + 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 = src.Replace('\\', '/'); + 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); + } + + /// relative from UnityEngine.Application.dataPath + /// + 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 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/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: From 13dab3aa34ba2f30f823739fd793512eb34a89c2 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 2 Jun 2022 18:14:53 +0900 Subject: [PATCH 2/4] public readonly string FullPath { get; } --- .../VRMShaders/GLTF/IO/Editor/PathObject.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs index 51a53f800..aef3765e0 100644 --- a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs +++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs @@ -16,22 +16,20 @@ namespace VRMShaders /// * .. を解決済み /// * フルパス /// - readonly string _FullPath; + public readonly string FullPath { get; } - public string FullPath => _FullPath; + public string Extension => Path.GetExtension(FullPath); - public string Extension => Path.GetExtension(_FullPath); + public string Stem => Path.GetFileNameWithoutExtension(FullPath); - public string Stem => Path.GetFileNameWithoutExtension(_FullPath); - - public PathObject Parent => FromFullPath(Path.GetDirectoryName(_FullPath)); + public PathObject Parent => FromFullPath(Path.GetDirectoryName(FullPath)); public bool IsUnderAsset { get { var assets = UnityAssets; - return _FullPath.StartsWith(assets.FullPath); + return FullPath.StartsWith(assets.FullPath); } } @@ -44,11 +42,11 @@ namespace VRMShaders get { var root = UnityRoot; - if (!_FullPath.StartsWith(root.FullPath)) + if (!FullPath.StartsWith(root.FullPath)) { - throw new ArgumentException($"{_FullPath} is not under UnityPath"); + throw new ArgumentException($"{FullPath} is not under UnityPath"); } - return _FullPath.Substring(root.FullPath.Length); + return FullPath.Substring(root.FullPath.Length); } } @@ -76,13 +74,13 @@ namespace VRMShaders src = src.Replace('\\', '/'); if (src[0] == '/') { - _FullPath = src; + FullPath = src; } else { if (src.Length >= 3 && src[1] == ':' && src[2] == '/') { - _FullPath = src; + FullPath = src; } else { @@ -100,7 +98,7 @@ namespace VRMShaders } catch (ArgumentException) { - return $""; + return $""; } } @@ -134,17 +132,17 @@ namespace VRMShaders public PathObject Child(string child) { - return FromFullPath(Path.Combine(_FullPath, child)); + return FromFullPath(Path.Combine(FullPath, child)); } public byte[] ReadAllBytes() { - return File.ReadAllBytes(_FullPath); + return File.ReadAllBytes(FullPath); } public void WriteAllBytes(byte[] data) { - File.WriteAllBytes(_FullPath, data); + File.WriteAllBytes(FullPath, data); } public void ImportAsset() @@ -156,7 +154,7 @@ namespace VRMShaders { var path = EditorUtility.SaveFilePanel( title, - _FullPath, + FullPath, name, "vrm"); if (string.IsNullOrEmpty(path)) From de8d65cb5deab596ca8570f8755b1cb9677ac548 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 2 Jun 2022 18:31:17 +0900 Subject: [PATCH 3/4] =?UTF-8?q?public=20string=20FullPath=20{=20get;=20}.?= =?UTF-8?q?=20=E6=9C=AB=E5=B0=BE=E3=81=AB=20'/'=20=E3=82=92=E4=BB=98?= =?UTF-8?q?=E3=81=91=E3=81=AA=E3=81=84=E4=BB=95=E6=A7=98=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VRMShaders/GLTF/IO/Editor/PathObject.cs | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs index aef3765e0..6401362d1 100644 --- a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs +++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs @@ -15,8 +15,9 @@ namespace VRMShaders /// * Delemeter は / を保証 /// * .. を解決済み /// * フルパス + /// * 末尾に / を付けない /// - public readonly string FullPath { get; } + public string FullPath { get; } public string Extension => Path.GetExtension(FullPath); @@ -24,14 +25,7 @@ namespace VRMShaders public PathObject Parent => FromFullPath(Path.GetDirectoryName(FullPath)); - public bool IsUnderAsset - { - get - { - var assets = UnityAssets; - return FullPath.StartsWith(assets.FullPath); - } - } + public bool IsUnderAsset => IsDescendantOf(UnityAssets); /// /// relative from UnityEngine.Application.dataPath @@ -42,11 +36,11 @@ namespace VRMShaders get { var root = UnityRoot; - if (!FullPath.StartsWith(root.FullPath)) + if (!IsDescendantOf(UnityRoot)) { throw new ArgumentException($"{FullPath} is not under UnityPath"); } - return FullPath.Substring(root.FullPath.Length); + return FullPath.Substring(root.FullPath.Length + 1); } } @@ -57,7 +51,7 @@ namespace VRMShaders { if (!_root.HasValue) { - _root = FromFullPath(Path.GetDirectoryName(Application.dataPath) + "/"); + _root = FromFullPath(Path.GetDirectoryName(Application.dataPath)); } return _root.Value; } @@ -71,7 +65,12 @@ namespace VRMShaders { throw new ArgumentNullException(); } - src = src.Replace('\\', '/'); + 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; @@ -135,6 +134,19 @@ namespace VRMShaders return FromFullPath(Path.Combine(FullPath, child)); } + public bool IsDescendantOf(PathObject ascendant) + { + if (!FullPath.StartsWith(ascendant.FullPath)) + { + return false; + } + if (FullPath[ascendant.FullPath.Length] != '/') + { + return false; + } + return true; + } + public byte[] ReadAllBytes() { return File.ReadAllBytes(FullPath); From 52d0ae691df8871220487b61610ab59276547af3 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 2 Jun 2022 18:41:17 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Test=E8=BF=BD=E5=8A=A0=E3=80=82Application.?= =?UTF-8?q?dataPath=20=E3=81=AE=E3=81=B2=E3=81=A8=E3=81=A4=E4=B8=8A?= =?UTF-8?q?=E3=81=8C=20AssetDatabase=20=E3=81=AE=20root=20=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VRMShaders/GLTF/IO/Editor/PathObject.cs | 8 ++++-- .../GLTF/IO/Tests/PathObjectTests.cs | 28 +++++++++++++++++++ .../GLTF/IO/Tests/PathObjectTests.cs.meta | 11 ++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs create mode 100644 Assets/VRMShaders/GLTF/IO/Tests/PathObjectTests.cs.meta diff --git a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs index 6401362d1..aef30c3b1 100644 --- a/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs +++ b/Assets/VRMShaders/GLTF/IO/Editor/PathObject.cs @@ -28,7 +28,9 @@ namespace VRMShaders public bool IsUnderAsset => IsDescendantOf(UnityAssets); /// - /// relative from UnityEngine.Application.dataPath + /// AssetDatabase の引き数になるパスを想定。 + /// Assets のひとつ上を 基準とする相対パス。 + /// Application.dataPath は Assets を得る。 /// /// public string UnityPath @@ -108,7 +110,7 @@ namespace VRMShaders return new PathObject(src); } - /// relative from UnityEngine.Application.dataPath + /// AssetDatabase が使うパス /// public static PathObject FromUnityPath(string src) { @@ -140,7 +142,7 @@ namespace VRMShaders { return false; } - if (FullPath[ascendant.FullPath.Length] != '/') + if (FullPath.Length <= ascendant.FullPath.Length || FullPath[ascendant.FullPath.Length] != '/') { return false; } 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: