commit 736b3d3bb8e62ad214895b0664483913c11f41b1 Author: Shimazue Eiichi Date: Sat Nov 7 14:25:45 2026 +0900 [依頼111] TwinCheck のシーンの写しを記録に残さず、動かすたびに元のシーンから写して開く形にする - TwinCheckA.OpenWorkScene: 元のシーン Assets/Scenes/R2R2R-20260224.unity を Assets/TwinCheck/_work/TwinCheck_Base.unity に写して開く(保存しない)。 元のパス・MD5・changeset(Unity Version Control の版の中身と MD5 が一致したときだけ)を 出力フォルダの scene_source.json に書く - TwinScene.Open・TwinCheckA.Run・TwinCheckDump.RunScene をこの関数に揃えた - .gitignore に _work/・_work.meta・Python~/__pycache__/ を足した Co-Authored-By: Claude Opus 5.5 diff --git a/.gitignore b/.gitignore index 09a16a6..6805b63 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,6 @@ Packages/packages-lock.json Assets/GaussianAssets/*.bytes .tmp.driveupload/ +Assets/TwinCheck/_work/ +Assets/TwinCheck/_work.meta +Assets/TwinCheck/Python~/__pycache__/ diff --git a/Assets/TwinCheck/Editor/TwinCheckA.cs b/Assets/TwinCheck/Editor/TwinCheckA.cs index 0b4d62d..ad87476 100644 --- a/Assets/TwinCheck/Editor/TwinCheckA.cs +++ b/Assets/TwinCheck/Editor/TwinCheckA.cs @@ -10,12 +10,90 @@ using System.Globalization; /// TwinCheckA /// 依頼107 の A。FK テーブルから選んだ行の関節角を、シーンの写しの20体それぞれに置き、 /// 爪先の位置(根元からの差)を書き出す。表の ee との比較は Python 側で行う。 -/// シーンは開くだけで保存しない。 +/// シーンは開くだけで保存しない。シーンの写しは OpenWorkScene で毎回作る(依頼111)。 /// 実行: Unity -batchmode -quit -executeMethod TwinCheckA.Run -twinIn {行の CSV} -twinOut {出力 CSV} [-twinSign 1|-1] /// public static class TwinCheckA { - public const string ScenePath = "Assets/TwinCheck/TwinCheck_Base.unity"; + // 元のシーン(読むだけ)と、動かすたびに作る写し(_work/ は記録に入れない。Unity は Assets の外のシーンを開けない) + public const string SourceScenePath = "Assets/Scenes/R2R2R-20260224.unity"; + public const string ScenePath = "Assets/TwinCheck/_work/TwinCheck_Base.unity"; + + /// + /// 元のシーンを _work/ に写して開く(保存しない)。元のパス・MD5・changeset を + /// 出力フォルダ(-twinOut、なければ -twinShots のあるフォルダ)の scene_source.json に書く。 + /// changeset は Unity Version Control で読み込んでいる版の中身と MD5 が一致したときだけ書く。 + /// + public static void OpenWorkScene() + { + Directory.CreateDirectory(Path.GetDirectoryName(ScenePath)); + File.Copy(SourceScenePath, ScenePath, true); + AssetDatabase.ImportAsset(ScenePath, ImportAssetOptions.ForceUpdate); + string md5 = Md5(SourceScenePath); + string cs = Changeset(SourceScenePath, md5, out string note); + + string outArg = TwinCheckArgs.Get("-twinOut", ""); + if (outArg == "") outArg = TwinCheckArgs.Get("-twinShots", ""); + if (outArg != "") + { + string dir = Path.GetDirectoryName(Path.GetFullPath(outArg)); + Directory.CreateDirectory(dir); + var sb = new StringBuilder("{\n"); + sb.Append($" \"source\": \"{SourceScenePath}\",\n"); + sb.Append($" \"md5\": \"{md5}\",\n"); + sb.Append($" \"changeset\": {(cs == null ? "null" : cs)},\n"); + sb.Append($" \"changeset_note\": \"{note}\",\n"); + sb.Append($" \"copied_to\": \"{ScenePath}\",\n"); + sb.Append($" \"copied_at\": \"{System.DateTime.Now:yyyy-MM-ddTHH:mm:ss}\"\n"); + sb.Append("}\n"); + File.WriteAllText(Path.Combine(dir, "scene_source.json"), sb.ToString()); + } + EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); + Debug.Log($"[TwinCheck] scene {SourceScenePath} md5={md5} cs={cs ?? "-"} ({note})"); + } + + static string Md5(string path) + { + using (var m = System.Security.Cryptography.MD5.Create()) + using (var f = File.OpenRead(path)) + return System.BitConverter.ToString(m.ComputeHash(f)).Replace("-", "").ToLowerInvariant(); + } + + /// cm で読み込んでいる版の番号を調べ、その版の中身の MD5 が今のファイルと同じなら番号を返す。 + static string Changeset(string path, string md5, out string note) + { + string full = Path.GetFullPath(path); + string cs = Cm($"ls \"{full}\" --format={{changeset}}").Trim(); + if (!int.TryParse(cs, out _)) { note = "cm で版を調べられなかった"; return null; } + string tmp = Path.Combine(Path.GetTempPath(), $"twincheck_scene_cs{cs}.unity"); + Cm($"cat \"{full}#cs:{cs}\" --file=\"{tmp}\""); + if (!File.Exists(tmp)) { note = $"cs:{cs} の中身を取り出せなかった"; return null; } + bool same = Md5(tmp) == md5; + File.Delete(tmp); + if (!same) { note = $"読み込んでいる版 cs:{cs} から変更あり(チェックインされていない)"; return null; } + note = $"cs:{cs} の中身と MD5 が一致"; + return cs; + } + + static string Cm(string args) + { + foreach (var exe in new[] { "/usr/local/bin/cm", "cm" }) + { + try + { + var psi = new System.Diagnostics.ProcessStartInfo(exe, args) + { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false }; + using (var p = System.Diagnostics.Process.Start(psi)) + { + string o = p.StandardOutput.ReadToEnd(); + p.WaitForExit(60000); + return o; + } + } + catch (System.Exception) { } + } + return ""; + } public static void Run() { @@ -23,7 +101,7 @@ public static class TwinCheckA string outPath = TwinCheckArgs.Get("-twinOut", ""); float sign = float.Parse(TwinCheckArgs.Get("-twinSign", "1"), CultureInfo.InvariantCulture); - EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); + OpenWorkScene(); var arms = FindArms(); var lines = File.ReadAllLines(inPath); diff --git a/Assets/TwinCheck/Editor/TwinCheckDump.cs b/Assets/TwinCheck/Editor/TwinCheckDump.cs index 67b540f..4144ecb 100644 --- a/Assets/TwinCheck/Editor/TwinCheckDump.cs +++ b/Assets/TwinCheck/Editor/TwinCheckDump.cs @@ -48,7 +48,7 @@ public static class TwinCheckDump { string outPath = TwinCheckArgs.Get("-twinOut", "/tmp/twincheck/scene.txt"); var sb = new StringBuilder(); - EditorSceneManager.OpenScene(TwinCheckA.ScenePath, OpenSceneMode.Single); + TwinCheckA.OpenWorkScene(); foreach (var go in UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects()) { var t = go.transform; diff --git a/Assets/TwinCheck/Editor/TwinScene.cs b/Assets/TwinCheck/Editor/TwinScene.cs index 564a578..ee2a600 100644 --- a/Assets/TwinCheck/Editor/TwinScene.cs +++ b/Assets/TwinCheck/Editor/TwinScene.cs @@ -1,11 +1,10 @@ using UnityEngine; -using UnityEditor.SceneManagement; using System.Globalization; using System.IO; /// /// TwinScene -/// 依頼107。シーンの写し(Assets/TwinCheck/TwinCheck_Base.unity)を開き、 +/// 依頼107。元のシーンの写し(TwinCheckA.OpenWorkScene が毎回 Assets/TwinCheck/_work/ に作る)を開き、 /// 1体目のアーム(mecharm270mpi_1_0、根元が原点)・赤ブロック・白箱・床・光だけを残して見せる。 /// 他の物は SetActive(false) にするだけで、シーンは保存しない。 /// @@ -16,7 +15,7 @@ public class TwinScene public static TwinScene Open() { - EditorSceneManager.OpenScene(TwinCheckA.ScenePath, OpenSceneMode.Single); + TwinCheckA.OpenWorkScene(); var s = new TwinScene(); var keep = new[] { "mecharm270mpi_1_0", "RedBlock_arm1", "WhiteBox_arm1", "Plane1", "Directional Light", "Main Camera" }; foreach (var go in UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects())