From 5292d67d2cf561aee556058dd381be3bdf2b3d9b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 08:03:33 +0000 Subject: [PATCH 1/3] Fix GUI type filter missing lazy-loaded asset types Added `case LazyObject` to the exportability switch in BuildAssetData. Lazy placeholders (Mesh, AnimationClip, Font, TextAsset, MovieTexture, Shader) were falling through to `case NamedObject` and never marked exportable, so those types were absent from the GUI FilterType menu. Exportability is now derived from the real ClassIDType. Avatar and AnimatorController placeholders (also lazy) stay non-exportable. CLI (UnityRiftCLI/Studio.cs) unaffected -- it uses asset.type directly. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019E5uLMZZXCMvZcw7sXrgts --- UnityRiftGUI/Studio.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/UnityRiftGUI/Studio.cs b/UnityRiftGUI/Studio.cs index 0b39181..cd60d5b 100644 --- a/UnityRiftGUI/Studio.cs +++ b/UnityRiftGUI/Studio.cs @@ -368,6 +368,24 @@ public static (string, List) BuildAssetData() containers.Add((m_Container.Value, m_Container.Key)); } break; + case LazyObject lazyAsset: + // Heavy assets are loaded as placeholders; derive exportability from + // the real ClassIDType so lazy Mesh/AnimationClip/Shader/Font/TextAsset/ + // MovieTexture are not dropped from the type filter. Avatar and + // AnimatorController placeholders stay non-exportable. + assetItem.Text = lazyAsset.m_Name; + switch (lazyAsset.type) + { + case ClassIDType.Mesh: + case ClassIDType.AnimationClip: + case ClassIDType.Shader: + case ClassIDType.Font: + case ClassIDType.TextAsset: + case ClassIDType.MovieTexture: + exportable = true; + break; + } + break; case NamedObject m_NamedObject: assetItem.Text = m_NamedObject.m_Name; break; From 9548b833f27d886a209df6d1245729aa88535959 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 08:03:40 +0000 Subject: [PATCH 2/3] Fix AnimatorOverrideController clips exported/previewed as empty (0.00s) When the Animator's controller is an AnimatorOverrideController, CollectAnimationClip iterated the base controller's m_AnimationClips, which are the empty template_* placeholder clips, and ignored the override map. Now its m_Clips (original -> override) is applied so the real, playable clips are collected. Fixes clips showing 0.00s / unplayable in the GUI animator preview and empty takes in FBX export. The shared converter drives GUI preview and CLI/GUI FBX export, so all paths are fixed. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019E5uLMZZXCMvZcw7sXrgts --- UnityRiftUtility/ModelConverter.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/UnityRiftUtility/ModelConverter.cs b/UnityRiftUtility/ModelConverter.cs index 7adcf7d..59dab4e 100644 --- a/UnityRiftUtility/ModelConverter.cs +++ b/UnityRiftUtility/ModelConverter.cs @@ -305,10 +305,22 @@ private void CollectAnimationClip(Animator m_Animator) { AnimatorController m_AnimatorController; var animationList = new List(); + var overrideMap = new Dictionary(); if (m_Controller is AnimatorOverrideController overrideController) { if (!overrideController.m_Controller.TryGet(out m_AnimatorController)) return; + // The base controller only holds empty template_* placeholder clips; the + // real animation data lives in the override map. Map each original clip to + // its override so we collect the playable clips instead of 0.00s templates. + foreach (var clipOverride in overrideController.m_Clips) + { + if (clipOverride.m_OriginalClip.TryGet(out var originalClip) + && clipOverride.m_OverrideClip.TryGet(out var overrideClip)) + { + overrideMap[originalClip] = overrideClip; + } + } } else { @@ -319,7 +331,9 @@ private void CollectAnimationClip(Animator m_Animator) { if (pptr.TryGet(out var m_AnimationClip)) { - animationList.Add(m_AnimationClip); + animationList.Add(overrideMap.TryGetValue(m_AnimationClip, out var overrideClip) + ? overrideClip + : m_AnimationClip); } } animationClipUniqArray = animationList.Distinct(animationClipEqComparer).ToArray(); From 99a51df3a9353e8de9a4742af9e904fd893bcdfa Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 08:03:53 +0000 Subject: [PATCH 3/3] Fix MonoBehaviour preview: blank assembly dialog + "Failed to read type" spam Auto-load the Mono Managed folder (via AssemblyLoader.FindManagedFolder over the loaded files' paths) before showing the manual "Select Assembly Folder" picker in SelectAssemblyFolder. Standalone builds strip MonoBehaviour script fields, so without the assembly the preview read only the 32-byte base and logged "Failed to read type, read 32 bytes but expected N bytes" for every field. Now Mono games resolve MonoBehaviour fields on click with no dialog and no errors. IL2CPP is unchanged: no Managed folder is found, so it falls through to the manual picker. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019E5uLMZZXCMvZcw7sXrgts --- UnityRiftGUI/Studio.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/UnityRiftGUI/Studio.cs b/UnityRiftGUI/Studio.cs index cd60d5b..e0be927 100644 --- a/UnityRiftGUI/Studio.cs +++ b/UnityRiftGUI/Studio.cs @@ -1002,6 +1002,25 @@ private static void SelectAssemblyFolder() { if (!assemblyLoader.Loaded) { + // Standalone builds strip MonoBehaviour script fields, so without the game's + // assemblies the preview reads only the 32-byte base and logs "Failed to read + // type, read 32 bytes but expected N bytes" for every field. Auto-load the Mono + // "Managed" folder found next to the loaded files before falling back to the + // manual picker. IL2CPP games have no Managed folder, so they still fall through + // to the manual "Select Assembly Folder" dialog. + var paths = assetsManager.AssetsFileList + .Select(f => f.originalPath ?? f.fullName) + .Where(p => p != null) + .Distinct() + .ToList(); + var managed = AssemblyLoader.FindManagedFolder(paths); + if (managed != null) + { + assemblyLoader.Load(managed); + AssembliesLoaded?.Invoke(); + return; + } + var openFolderDialog = new OpenFolderDialog(); openFolderDialog.Title = "Select Assembly Folder"; if (openFolderDialog.ShowDialog() == DialogResult.OK)