From 89ac8068145516b0f95400dc4efff7c96f7ffd8b Mon Sep 17 00:00:00 2001 From: Justin Oros Date: Thu, 24 Sep 2026 21:59:31 -0700 Subject: [PATCH] Split rigscene into one file per character root and bump to 0.3.0 --- README.md | 2 +- RigProbe.cs | 65 +++++++++++++++++++++++++++++++++++++++++++------ RigProbe.csproj | 2 +- manifest.json | 2 +- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a55e7c8..37a9fa8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Run these in the in-game console. |---|---| | `rigprobe` | Dump the player, carry bodies, all actor prefabs and live actors | | `rigprobe ` | Same, limited to actor types containing the filter, e.g. `rigprobe female` | -| `rigscene` | Dump every skinned mesh root in the loaded scenes, including cutscene characters | +| `rigscene` | Dump every character in the loaded scenes (posers, cutscene characters, props with skinned meshes), one file per character | | `rigspawn [variation]` | Spawn an actor in front of you so its live variant gets dumped, e.g. `rigspawn Virginia` | ## Output diff --git a/RigProbe.cs b/RigProbe.cs index 35e73db..c479094 100644 --- a/RigProbe.cs +++ b/RigProbe.cs @@ -164,16 +164,27 @@ public class RigProbe : SonsMod skip.Add(t.GetInstanceID()); var roots = new Dictionary(); + var members = new Dictionary>(); foreach (var smr in Resources.FindObjectsOfTypeAll()) { if (!smr || !smr.gameObject.scene.IsValid() || skip.Contains(smr.transform.GetInstanceID())) continue; - var root = smr.transform.root; - if (!roots.ContainsKey(root.GetInstanceID())) - roots[root.GetInstanceID()] = root; + var root = CharacterRoot(smr); + if (!root) + continue; + var key = root.GetInstanceID(); + if (!roots.ContainsKey(key)) + { + roots[key] = root; + members[key] = new List(); + } + members[key].Add(smr); + } + foreach (var kv in roots) + { + var root = kv.Value; + dumps.Add(DumpObject(root.gameObject, "scene", $"{root.name}_{kv.Key}", null, members[kv.Key])); } - foreach (var root in roots.Values) - dumps.Add(DumpObject(root.gameObject, "scene", $"{root.name}_{root.GetInstanceID()}", null)); } foreach (var d in dumps) @@ -189,7 +200,7 @@ public class RigProbe : SonsMod Say($"RigProbe: {dumps.Count} characters written to {outDir}"); } - private static CharacterDump DumpObject(GameObject go, string source, string name, Transform exclude) + private static CharacterDump DumpObject(GameObject go, string source, string name, Transform exclude, List only = null) { var root = go.transform; var dump = new CharacterDump @@ -200,7 +211,7 @@ public class RigProbe : SonsMod Active = go.activeInHierarchy }; - var smrs = go.GetComponentsInChildren(true) + var smrs = only ?? go.GetComponentsInChildren(true) .Where(r => r && !(exclude && IsUnder(r.transform, exclude))) .ToList(); @@ -220,6 +231,8 @@ public class RigProbe : SonsMod { if (!a || (exclude && IsUnder(a.transform, exclude))) continue; + if (only != null && a.transform != root && !rigIds.Contains(a.transform.GetInstanceID())) + continue; AddWithAncestors(a.transform, root, rigIds); dump.Animators.Add(DumpAnimator(a, root)); } @@ -496,6 +509,44 @@ public class RigProbe : SonsMod return $"{hit}/{names.Count} ({hit * 100 / names.Count}%)"; } + private static Transform CharacterRoot(SkinnedMeshRenderer smr) + { + var t = smr.transform.parent; + while (t) + { + if (t.GetComponent() || t.name.IndexOf("Poser", StringComparison.OrdinalIgnoreCase) >= 0) + return t; + t = t.parent; + } + + var bone = smr.rootBone; + if (!bone && smr.bones != null && smr.bones.Length > 0) + bone = smr.bones[0]; + var lca = CommonAncestor(smr.transform, bone); + return lca ? lca : smr.transform.parent ? smr.transform.parent : smr.transform; + } + + private static Transform CommonAncestor(Transform a, Transform b) + { + if (!a || !b) + return null; + var chain = new HashSet(); + var t = a; + while (t) + { + chain.Add(t.GetInstanceID()); + t = t.parent; + } + t = b; + while (t) + { + if (chain.Contains(t.GetInstanceID())) + return t; + t = t.parent; + } + return null; + } + private static void AddWithAncestors(Transform t, Transform root, HashSet ids) { while (t) diff --git a/RigProbe.csproj b/RigProbe.csproj index 9b1592a..329d305 100644 --- a/RigProbe.csproj +++ b/RigProbe.csproj @@ -6,7 +6,7 @@ latest RigProbe RigProbe - 1.0.0 + 0.3.0 CS0436;CS1701;CS1702;MSB3277;MSB3106 diff --git a/manifest.json b/manifest.json index 1826f3d..66d659b 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "id": "RigProbe", "name": "RigProbe", "author": "JustinOros", - "version": "0.2.0", + "version": "0.3.0", "description": "Dumps rig, mesh, material and animator data for every Sons of the Forest character to JSON for mod development", "gameVersion": "1.0.0", "platform": "Client",