AnimLayers detection fix
This commit is contained in:
@@ -13103,3 +13103,784 @@ _**User**_
|
||||
|
||||
---
|
||||
|
||||
_**Agent (model default, mode Agent)**_
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The user wants me to add debug to MigCustProps because custom properties aren't being copied. They also said I can use Blender MCP to view the scene if needed. Let me first add debug prints to the migrator so we can see:
|
||||
1. What keys exist on the orig armature and on each orig pose bone (especially Settings)
|
||||
2. Whether we're copying and if any exceptions occur
|
||||
3. What the rep has before/after
|
||||
|
||||
Then I could use Blender MCP to inspect the scene - e.g. check Kennedy's pose.bones["Settings"] and what keys it has. Let me add debug first, then optionally use MCP to see the scene structure.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Adding debug to MigCustProps and checking the scene via Blender MCP to see how custom properties are stored.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||
<details>
|
||||
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"pattern": "blender|mcp|execute",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"glob": "*.json",
|
||||
"caseInsensitive": false
|
||||
}
|
||||
```
|
||||
Additional data:
|
||||
|
||||
```json
|
||||
{
|
||||
"isPruned": true,
|
||||
"pattern": "blender|mcp|execute",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"outputMode": "content",
|
||||
"totalFiles": 0,
|
||||
"totalMatches": 0,
|
||||
"topFiles": []
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I need to add debug that:
|
||||
1. Logs orig/rep armature names and how many keys on orig (armature object)
|
||||
2. For each bone that has keys, log bone name and keys (and if Settings bone, log its keys and nested keys like Devices)
|
||||
3. Log when we try to copy each key and if an exception happens, print it
|
||||
4. After copy, log rep armature and rep Settings bone keys so we can see if anything was written
|
||||
|
||||
I'll add a debug block that prints to console. I don't have a direct "run in Blender" MCP - the user said "use blendermcp to view my scene if you need to". Let me check available MCP tools - I have mcp_web_fetch, call_mcp_tool, etc. I don't see a Blender MCP in my tool list. I'll add the debug and if the user has Blender MCP they can run the operator and we'll see the debug output in the terminal.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Adding detailed debug logging to MigCustProps.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="write" data-tool-name="edit_file_v2">
|
||||
|
||||
<details><summary>Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I need to fix the call to _copy_id_prop_recursive - I added a debug parameter but the recursive call passes debug_path and debug. Let me check - _copy_custom_props_from now passes debug_label and debug. Inside _copy_id_prop_recursive we're calling with (orig_val, rep_group, k, f"{debug_path}.{key}", debug). So the signature is (orig_container, rep_container, key, debug_path="", debug=False). Good. But _copy_custom_props_from doesn't pass debug to _copy_id_prop_recursive - it passes debug_label as the 4th arg. So the 4th parameter is debug_path and 5th is debug. So we need _copy_id_prop_recursive(orig_obj, rep_obj, key, debug_label, debug). Good.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="read_lints">
|
||||
<details>
|
||||
<summary>Tool use: **read_lints**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"paths": [
|
||||
"T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
]
|
||||
}
|
||||
```
|
||||
Result:
|
||||
|
||||
```json
|
||||
{
|
||||
"linterErrorsByFile": [
|
||||
{
|
||||
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
Let me double-check: when we call _copy_id_prop_recursive from _copy_custom_props_from we do:
|
||||
_copy_id_prop_recursive(orig_obj, rep_obj, key, debug_label, debug)
|
||||
So debug_path gets debug_label (e.g. "bone:Settings") and debug gets debug (True). Good.
|
||||
When we recurse we do _copy_id_prop_recursive(orig_val, rep_group, k, f"{debug_path}.{key}", debug). Good.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Summary of what was added:
|
||||
|
||||
**Debug in MigCustProps (always on for now):**
|
||||
|
||||
1. **Operator start** – Logs `orig` and `rep` armature names.
|
||||
2. **Armature object** – Logs all keys on the original armature (including excluded ones) as `armature orig keys (all): [...]`.
|
||||
3. **Bones with id_props** – Logs every bone that has at least one key and their key lists: `bones with id_props: [(bone_name, [keys]), ...]`. If this is empty, no pose bones have ID properties.
|
||||
4. **Per-bone copy** – For each bone that has keys (after EXCLUDE_PROPS), logs `bone:X keys: [list]` and for each key either:
|
||||
- `group path.key: copied N sub-keys`, or
|
||||
- `leaf path.key = value`, or
|
||||
- `FAILED path.key: <exception>`.
|
||||
5. **After copy** – Logs `rep armature keys after: [...]` and, if a **Settings** bone exists, `rep bone Settings keys after: [...]` plus each `Settings[key]` (and for groups, their sub-keys).
|
||||
|
||||
Reload the add-on, run MigCustProps, and watch the Blender console (or terminal 3). You’ll see:
|
||||
|
||||
- Whether the original has any armature-level or bone-level ID props.
|
||||
- If “Settings”/“Devices” are missing from `bones with id_props`, they’re not ID properties (e.g. they’re RNA/PropertyGroup), which would explain why they’re not copied.
|
||||
- Any exception message if a copy fails.
|
||||
|
||||
If you want, we can then use Blender MCP to inspect the scene (e.g. `bpy.data.objects["Kennedy"].pose.bones["Settings"]` and its `keys()` / attributes) and I can suggest the exact calls to run.
|
||||
|
||||
---
|
||||
|
||||
_**User**_
|
||||
|
||||
AnimLayers detection isn't working. It says they weren't found, but they are present. @c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt use blender mcp if necessary to figure out how AnimLayers can be interacted with.
|
||||
|
||||
---
|
||||
|
||||
_**Agent (model default, mode Agent)**_
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The user says AnimLayers detection isn't working - it reports they weren't found but they are present. I need to:
|
||||
1. Find where we detect "Animation Layers" / als.turn_on in the migrator and what we're checking
|
||||
2. Use Blender MCP if needed to see how AnimLayers are actually stored (custom prop, RNA, etc.)
|
||||
3. Fix the detection
|
||||
|
||||
Let me search the codebase for the AnimLayers / als / animation layer detection and the report message.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||
<details>
|
||||
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"pattern": "Animation Layer|als\\.turn_on|anim.*layer|NLA.*detected",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"caseInsensitive": true
|
||||
}
|
||||
```
|
||||
Additional data:
|
||||
|
||||
```json
|
||||
{
|
||||
"isPruned": true,
|
||||
"pattern": "Animation Layer|als\\.turn_on|anim.*layer|NLA.*detected",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"outputMode": "content",
|
||||
"totalFiles": 5,
|
||||
"totalMatches": 161,
|
||||
"topFiles": [
|
||||
{
|
||||
"uri": ".specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md",
|
||||
"matchCount": 74
|
||||
},
|
||||
{
|
||||
"uri": "CHANGELOG.md",
|
||||
"matchCount": 1
|
||||
},
|
||||
{
|
||||
"uri": "ops\\migrator.py",
|
||||
"matchCount": 6
|
||||
},
|
||||
{
|
||||
"uri": "ops/migrator.py",
|
||||
"matchCount": 6
|
||||
},
|
||||
{
|
||||
"uri": ".specstory\\history\\2026-02-18_23-39Z-character-migrator-workflow-integration.md",
|
||||
"matchCount": 74
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The terminal doesn't show the "No Animation Layers found" message in the first 80 lines - it's mostly MigCustProps RNA errors. Let me find where we report "Animation Layer" and how we detect them in the migrator (run_mig_nla).
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||
<details>
|
||||
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"pattern": "Animation Layer|als\\.turn_on|No Animation Layers|_mirror_als",
|
||||
"path": "T:\\Dynamic-Link-Manager\\ops\\migrator.py",
|
||||
"caseInsensitive": false
|
||||
}
|
||||
```
|
||||
Additional data:
|
||||
|
||||
```json
|
||||
{
|
||||
"isPruned": true,
|
||||
"pattern": "Animation Layer|als\\.turn_on|No Animation Layers|_mirror_als",
|
||||
"path": "T:\\Dynamic-Link-Manager\\ops\\migrator.py",
|
||||
"outputMode": "content",
|
||||
"totalFiles": 1,
|
||||
"totalMatches": 8,
|
||||
"topFiles": [
|
||||
{
|
||||
"uri": "ops\\migrator.py",
|
||||
"matchCount": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I see. We detect "Animation Layers" by checking if "als.turn_on" is in orig (armature object) or in any pose bone. So we're only looking for the **id property** key "als.turn_on". The Animation Layers addon might store this differently - e.g. under a different key, or as RNA, or on a different data-block (armature.data, or a specific bone with a different name). Let me read the rest of run_mig_nla to see the exact report logic, then search for how the animation_layers addon stores its state (the terminal mentioned "animation_layers_updater addon").
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="search" data-tool-name="web_search">
|
||||
<details>
|
||||
<summary>Tool use: **web_search** • Searched web `undefined` • **1** results</summary>
|
||||
|
||||
| Title | URL | Chunk |
|
||||
|-------|-----|-------|
|
||||
| Web search results for query: Blender animation layers addon als turn_on property where stored | | Links:<br/>1. [Layered Actions - Blender Developer Documentation](https://developer.blender.org/docs/features/animation/animation_system/layered/)<br/>2. [Blender Extensions](https://blender4.com/add-ons/animation-layers/versions/)<br/>3. [Animation Layers addon for Blender \| Animation layer in blender](https://www.youtube.com/watch?v=dMVP_xY-Tz0)<br/>4. [Blender Extensions](https://blender4.com/add-ons/animation-layers/)<br/>5. [Animation Layers](https://blender-addons.org/animation-layers/)<br/><br/>I could not find specific information about where the `turn_on` property is stored in the Blender Animation Layers addon based on these search results.<br/><br/>The search results describe the Animation Layers addon as a tool that simplifies NLA (Non-Linear Animation) editing into a standard animation layers workflow.[2][3][5] It allows users to create, edit, and manage animation layers with features like layer management, smart baking, and various animation tools.[5]<br/><br/>However, the results don't contain technical documentation or source code details about the addon's internal properties, including where the `turn_on` property is specifically stored. To find this information, you would likely need to:<br/><br/>- Examine the addon's source code directly (available in the downloaded addon files)<br/>- Check the addon's documentation or GitHub repository<br/>- Contact the addon developer (xz-blender)[2][4] |
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The terminal log showed "animation_layers_updater addon" - so the user has an Animation Layers addon. We're only checking for the id property "als.turn_on" on the armature object and pose bones. The addon might:
|
||||
1. Use a different key (e.g. "als" as a group with "turn_on" inside, or a different name)
|
||||
2. Store on armature.data instead of the object
|
||||
3. Use RNA/custom property with a different identifier
|
||||
|
||||
I'll add debug to MigNLA to print what we're checking and expand detection to also look for:
|
||||
- orig.data (armature data) for "als.turn_on" or similar
|
||||
- Common variants like "als" as a group with "turn_on" inside
|
||||
- Any key containing "als" or "turn_on" on orig and bones for debugging
|
||||
|
||||
And I'll try using Blender MCP to run Python in the user's scene to list all keys on the armature and pose bones so we can see how AnimLayers actually stores it. Let me check if there's an execute_blender_code or similar in the project.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Checking for a way to run code in Blender and expanding AnimLayers detection.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||
<details>
|
||||
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"pattern": "execute|blender.*python|run.*blender",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"glob": "*.py",
|
||||
"caseInsensitive": false
|
||||
}
|
||||
```
|
||||
Additional data:
|
||||
|
||||
```json
|
||||
{
|
||||
"isPruned": true,
|
||||
"pattern": "execute|blender.*python|run.*blender",
|
||||
"path": "T:\\Dynamic-Link-Manager",
|
||||
"outputMode": "content",
|
||||
"totalFiles": 1,
|
||||
"totalMatches": 30,
|
||||
"topFiles": [
|
||||
{
|
||||
"uri": "ui/operators.py",
|
||||
"matchCount": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="search" data-tool-name="web_search">
|
||||
<details>
|
||||
<summary>Tool use: **web_search** • Searched web `undefined` • **1** results</summary>
|
||||
|
||||
| Title | URL | Chunk |
|
||||
|-------|-----|-------|
|
||||
| Web search results for query: Blender Animation Layers addon NLA armature property "als" custom property | | Links:<br/>1. [Animation Layers](https://blendermarket.com/products/animation-layers/docs)<br/>2. [Animation Layers](https://blender-addons.org/animation-layers/)<br/>3. [](https://www.blendernation.com/2022/08/02/animation-layers-2-1-for-blender-3-2/)<br/>4. [Blender Extensions](https://blender4.com/add-ons/animation-layers/)<br/>5. [Layered Actions - Blender Developer Documentation](https://developer.blender.org/docs/features/animation/animation_system/layered/)<br/><br/># Animation Layers Addon - NLA and Armature Integration<br/><br/>The Animation Layers addon for Blender is a tool that simplifies NLA (Non-Linear Animation) Editor workflows by providing a layer-based animation editing system.[1][2] However, the search results provided do not contain specific information about an "als" custom property on armatures or detailed technical documentation about armature-specific custom properties used by this addon.<br/><br/>## What the addon does:<br/><br/>The addon creates animation layers that work with Blender's NLA system.[1] Each object and armature gets separate layers, and the addon manages actions through these layers.[1] The addon uses NLA's strip influence property to control layer influence and supports various blend types like 'Replace,' 'Combine,' and 'Add.'[1]<br/><br/>## Relevant NLA note:<br/><br/>The addon documentation explicitly states: *"Animation Layers are using a different solo property than the Solo property in the NLA. It is better to avoid using NLA's Solo while using Animation Layers."*[1] This indicates the addon stores its own properties separately from standard NLA properties.<br/><br/>**Note:** To find specific information about the "als" custom property on armatures, you would need to consult the addon's source code or more detailed technical documentation not present in these results. |
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I'll add robust detection that:
|
||||
1. Checks for "als.turn_on" on object and pose bones (current)
|
||||
2. Also checks for "als" as a group and "turn_on" inside it: orig.get("als") and "turn_on" in that group
|
||||
3. Checks armature.data (orig.data) for "als.turn_on" or "als"
|
||||
4. Adds debug: print all keys on orig, orig.data, and first few bones so we can see what the addon actually uses
|
||||
|
||||
Then the report can consider "Animation Layers found" if any of these are present. I'll also add a helper that returns True if we found and mirrored any als-related prop, and expand _mirror_als_turn_on to try multiple locations (object, data, bone) and nested "als"["turn_on"].
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Adding detection for alternate AnimLayers storage (e.g. `als` group with `turn_on`, or on `armature.data`) and debug logging.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="write" data-tool-name="edit_file_v2">
|
||||
|
||||
<details><summary>Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
Now I need to update the report logic to use _has_als_anywhere(orig) instead of the inline check, and add debug that prints what we found (all keys on orig, orig.data, and sample bones) so the user can see in the terminal how AnimLayers is stored.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="write" data-tool-name="edit_file_v2">
|
||||
|
||||
<details><summary>Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The Animation Layers addon might use a completely different storage - e.g. a scene-level layer stack, or a property on the armature that isn't id props. Let me also add a check for RNA properties on the armature that might be animation-layer related - e.g. if the addon registers a property like `animation_layers` or `use_animation_layers` on the armature type. We could iterate orig.bl_rna.properties and look for names containing "layer" or "als". But that could be fragile. For now the debug will show all keys; if the user runs MigNLA and the console shows orig (object) keys and orig.data keys, we can add detection for whatever key actually appears. Let me also ensure we don't assume .keys() exists on orig.data - in Blender, ID (armature) might not have id_properties in the same way. Let me check - bpy.types.Armature might have animation_data but id properties are on the ID base. Actually in Blender, bpy.types.Object has [] for id properties. Armature is a datablock (ID) - so orig.data is an Armature datablock. Does Armature have id properties? I believe only Object and a few others have the bracket id_properties in the Python API. Let me keep the hasattr(orig.data, "keys") check so we don't error. If orig.data doesn't have keys(), the list(orig.data.keys()) would fail - so we need hasattr(orig.data, "keys"). Armature in Blender does have id_properties (all IDs can have them). So orig.data.keys() should work. I'll leave as is.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="read_lints">
|
||||
<details>
|
||||
<summary>Tool use: **read_lints**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"paths": [
|
||||
"T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
]
|
||||
}
|
||||
```
|
||||
Result:
|
||||
|
||||
```json
|
||||
{
|
||||
"linterErrorsByFile": [
|
||||
{
|
||||
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
Good. Let me provide a short summary. I've also considered: maybe the addon stores "animation_layers" or the NLA tracks themselves *are* the "layers" and we're supposed to consider "Animation Layers found" whenever we have NLA tracks (since the addon creates NLA tracks as layers). So "No Animation Layers found" might mean "we didn't find the als.turn_on toggle" but the user considers the NLA tracks as the "layers". So improving detection of als.turn_on (including nested als["turn_on"] and data) is the right fix. If the addon uses yet another storage we'll see it in the debug output.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Summary of changes:
|
||||
|
||||
**1. Broader AnimLayers detection (`_has_als_anywhere`)**
|
||||
- **Flat:** `"als.turn_on"` on the armature **object** or any **pose bone**.
|
||||
- **Nested:** `"als"` group with `"turn_on"` on the object or any bone (e.g. `orig["als"]["turn_on"]`).
|
||||
- **Data:** `"als.turn_on"` on **armature data** (`orig.data`), if it has id props.
|
||||
|
||||
**2. Broader mirroring (`_mirror_als_turn_on`)**
|
||||
- Copies the flat `"als.turn_on"` on object and bones (unchanged).
|
||||
- Copies nested `orig["als"]["turn_on"]` to `rep["als"]["turn_on"]` on object and bones.
|
||||
- Copies `orig.data["als.turn_on"]` to `rep.data["als.turn_on"]` when present.
|
||||
|
||||
**3. Debug (MigNLA, when NLA is detected)**
|
||||
- `[DLM MigNLA] AnimLayers check: has_als=True/False`
|
||||
- `orig (object) keys: [...]`
|
||||
- `orig.data keys: [...]` (if `orig.data` has `keys`)
|
||||
- First bone’s keys and, if present, **Settings** bone keys.
|
||||
|
||||
After reloading the addon and running MigNLA on a rig that has Animation Layers, check the console:
|
||||
|
||||
- If **has_als** becomes **True**, the new detection paths are matching your addon’s storage.
|
||||
- If it stays **False**, the debug lines show where the addon actually stores its state (object/data/bone and key names), and we can add support for that next.
|
||||
|
||||
If you can run Blender MCP and run a small script that prints `list(armature.keys())`, `list(armature.data.keys())`, and for one bone `list(armature.pose.bones["Settings"].keys())` (or the first bone), we can use that output to add any remaining storage format.
|
||||
|
||||
---
|
||||
|
||||
_**User**_
|
||||
|
||||
@c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt still none found. add debug
|
||||
|
||||
---
|
||||
|
||||
_**Agent (model default, mode Agent)**_
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
The user says AnimLayers are still not found and wants more debug. I need to add more comprehensive debug that:
|
||||
1. Prints everything we're checking (so we can see what's actually on the armature)
|
||||
2. Maybe check scene, or other places where Animation Layers might store state
|
||||
3. Print all keys on ALL bones that have any keys (not just first and Settings) so we don't miss where "als" lives
|
||||
4. Check RNA properties on the object - maybe the addon uses a registered property like obj.animation_layers or similar
|
||||
|
||||
Let me read the current migrator NLA section and add more verbose debug.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I need to add much more debug that:
|
||||
1. Dumps ALL id props on orig (object) - every key and if it's a group, its sub-keys
|
||||
2. Dumps orig.data keys if present
|
||||
3. Lists every bone that has keys and their full key list (and for each key that is a group, list sub-keys)
|
||||
4. Also dump RNA property names on orig that contain "layer" or "als" or "turn" or "anim" so we can see if it's stored as RNA
|
||||
5. Step through _has_als_anywhere and print why each check passed or failed
|
||||
|
||||
This will be verbose but we need to see where the addon actually stores the toggle.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
||||
<details><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
I'll add a dedicated _debug_als_lookup(orig) that runs before the report and:
|
||||
1. Prints each step of _has_als_anywhere (key in orig? data? als group? each bone?)
|
||||
2. Lists every bone that has any keys and for each, every key and if group the sub-keys
|
||||
3. Lists orig RNA property names that might be animation-layer related (bl_rna.properties)
|
||||
4. Optionally dump full orig.keys() with type of each value (group vs leaf)
|
||||
|
||||
Then call this from the NLA path when we're about to report.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Adding comprehensive debug: a helper that dumps all id props (including nested) and relevant RNA, then calling it before the report.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="write" data-tool-name="edit_file_v2">
|
||||
|
||||
<details><summary>Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
Now I need to call _debug_als_lookup(orig) in the NLA path right before we compute has_als and report. So after _mirror_als_turn_on(orig, rep) and before the if report block.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="write" data-tool-name="edit_file_v2">
|
||||
|
||||
<details><summary>Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py</summary>
|
||||
|
||||
</details>
|
||||
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
Good. Now the NLA path will run _debug_als_lookup(orig) which prints:
|
||||
- Whether "als.turn_on" is in orig
|
||||
- orig.data keys and whether "als.turn_on" is there
|
||||
- orig.get("als") and if it's a group, its keys and whether "turn_on" is in it
|
||||
- Full list of orig (object) keys and for each key the value or sub-keys
|
||||
- RNA property names on orig that contain layer/als/turn/anim
|
||||
- List of all bones that have id_props (name + keys), up to 20
|
||||
- For first 10 such bones, each key and value or sub-keys
|
||||
|
||||
That should reveal where the Animation Layers addon stores its state.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
<tool-use data-tool-type="unknown" data-tool-name="read_lints">
|
||||
<details>
|
||||
<summary>Tool use: **read_lints**</summary>
|
||||
|
||||
|
||||
Parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"paths": [
|
||||
"T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
]
|
||||
}
|
||||
```
|
||||
Result:
|
||||
|
||||
```json
|
||||
{
|
||||
"linterErrorsByFile": [
|
||||
{
|
||||
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\migrator.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Status: **completed**
|
||||
|
||||
|
||||
</details>
|
||||
</tool-use>
|
||||
|
||||
---
|
||||
|
||||
<think><details><summary>Thought Process</summary>
|
||||
|
||||
All good. Let me give a short summary to the user.
|
||||
</details></think>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Summary of the new debug:
|
||||
|
||||
**When MigNLA runs (with NLA tracks present), it now prints:**
|
||||
|
||||
1. **Direct checks**
|
||||
- `'als.turn_on' in orig (object)`: True/False
|
||||
- `orig.data` has `keys()` and `'als.turn_on' in orig.data`
|
||||
- `orig.get('als')` is a group and, if so, its keys and whether `'turn_on'` is in it
|
||||
|
||||
2. **Object id props**
|
||||
- All keys on `orig`
|
||||
- For each key: value, or if it’s a group, its sub-keys
|
||||
|
||||
3. **RNA**
|
||||
- Any RNA property on `orig` whose name contains `layer`, `als`, `turn`, or `anim`
|
||||
|
||||
4. **Bones**
|
||||
- All bones that have id props (bone name + list of keys), up to 20 bones
|
||||
- For the first 10 of those bones: each key and its value or sub-keys
|
||||
|
||||
After reloading the addon and running MigNLA, check the console for the block between `=== AnimLayers debug ===` and `=== end AnimLayers debug ===`. From that we can see:
|
||||
- Where the addon stores its state (object, data, or bone; key names)
|
||||
- Whether it uses id props or RNA
|
||||
- The exact key path (e.g. another name than `als` / `turn_on`)
|
||||
|
||||
Share that debug block (or the relevant lines) and we can add detection and mirroring for it.
|
||||
|
||||
---
|
||||
|
||||
_**User**_
|
||||
|
||||
@c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt still detecting improperly. do you need to grep how the animlayers addon works?
|
||||
|
||||
---
|
||||
|
||||
|
||||
+128
-7
@@ -47,19 +47,139 @@ def run_copy_attr(orig, rep):
|
||||
rep.scale = orig.scale.copy()
|
||||
|
||||
|
||||
def _has_als_anywhere(orig):
|
||||
"""Return True if orig has Animation Layers (addon uses Object.als RNA PropertyGroup, obj.als.turn_on)."""
|
||||
# Animation Layers addon: bpy.types.Object.als (RNA), not id props
|
||||
if getattr(orig, "als", None) is not None:
|
||||
return True
|
||||
key = "als.turn_on"
|
||||
if key in orig:
|
||||
return True
|
||||
if getattr(orig, "data", None) and hasattr(orig.data, "keys") and key in orig.data:
|
||||
return True
|
||||
try:
|
||||
als = orig.get("als")
|
||||
if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
for pb in orig.pose.bones:
|
||||
if key in pb:
|
||||
return True
|
||||
try:
|
||||
als = pb.get("als")
|
||||
if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _debug_als_lookup(orig):
|
||||
"""Print full debug for AnimLayers: RNA obj.als and every id_prop on orig."""
|
||||
key = "als.turn_on"
|
||||
print("[DLM MigNLA] === AnimLayers debug ===")
|
||||
als_rna = getattr(orig, "als", None)
|
||||
print(f"[DLM MigNLA] orig.als (RNA): {als_rna!r}, turn_on={getattr(als_rna, 'turn_on', 'N/A') if als_rna else 'N/A'}")
|
||||
print(f"[DLM MigNLA] 'als.turn_on' in orig (object): {key in orig}")
|
||||
if getattr(orig, "data", None):
|
||||
has_data_keys = hasattr(orig.data, "keys")
|
||||
print(f"[DLM MigNLA] orig.data has keys(): {has_data_keys}")
|
||||
if has_data_keys:
|
||||
print(f"[DLM MigNLA] 'als.turn_on' in orig.data: {key in orig.data}")
|
||||
print(f"[DLM MigNLA] orig.data keys: {list(orig.data.keys())}")
|
||||
try:
|
||||
als = orig.get("als")
|
||||
has_als = als is not None and callable(getattr(als, "keys", None))
|
||||
print(f"[DLM MigNLA] orig.get('als') is group: {has_als}")
|
||||
if has_als:
|
||||
print(f"[DLM MigNLA] orig['als'] keys: {list(als.keys())}")
|
||||
print(f"[DLM MigNLA] 'turn_on' in orig['als']: {'turn_on' in als.keys()}")
|
||||
except Exception as e:
|
||||
print(f"[DLM MigNLA] orig.get('als') error: {e}")
|
||||
print(f"[DLM MigNLA] orig (object) all keys: {list(orig.keys())}")
|
||||
for k in list(orig.keys()):
|
||||
try:
|
||||
v = orig[k]
|
||||
if callable(getattr(v, "keys", None)):
|
||||
print(f"[DLM MigNLA] orig[{k!r}] (group) keys: {list(v.keys())}")
|
||||
else:
|
||||
print(f"[DLM MigNLA] orig[{k!r}] = {v!r}")
|
||||
except Exception as e:
|
||||
print(f"[DLM MigNLA] orig[{k!r}] error: {e}")
|
||||
# RNA props that might be animation-layer related
|
||||
try:
|
||||
rna_props = list(orig.bl_rna.properties.keys())
|
||||
layer_like = [p for p in rna_props if "layer" in p.lower() or "als" in p.lower() or "turn" in p.lower() or "anim" in p.lower()]
|
||||
print(f"[DLM MigNLA] orig RNA props (layer/als/turn/anim): {layer_like}")
|
||||
except Exception as e:
|
||||
print(f"[DLM MigNLA] orig bl_rna.properties error: {e}")
|
||||
# Every bone that has keys
|
||||
bones_with_keys = []
|
||||
for pb in orig.pose.bones:
|
||||
if pb.keys():
|
||||
bones_with_keys.append((pb.name, list(pb.keys())))
|
||||
print(f"[DLM MigNLA] bones with id_props ({len(bones_with_keys)}): {bones_with_keys[:20]}{'...' if len(bones_with_keys) > 20 else ''}")
|
||||
for bname, bkeys in bones_with_keys[:10]:
|
||||
pb = orig.pose.bones[bname]
|
||||
print(f"[DLM MigNLA] bone {bname!r}: keys={bkeys}")
|
||||
for k in bkeys:
|
||||
try:
|
||||
v = pb[k]
|
||||
if callable(getattr(v, "keys", None)):
|
||||
print(f"[DLM MigNLA] [{k!r}] (group) keys: {list(v.keys())}")
|
||||
else:
|
||||
print(f"[DLM MigNLA] [{k!r}] = {v!r}")
|
||||
except Exception as e:
|
||||
print(f"[DLM MigNLA] [{k!r}] error: {e}")
|
||||
print("[DLM MigNLA] === end AnimLayers debug ===")
|
||||
|
||||
|
||||
def _mirror_als_turn_on(orig, rep):
|
||||
"""Mirror als.turn_on (animation layers) from original to replacement armature."""
|
||||
"""Mirror Animation Layers state: obj.als.turn_on (RNA) and id-property fallbacks."""
|
||||
# Animation Layers addon: Object.als is RNA PropertyGroup
|
||||
orig_als = getattr(orig, "als", None)
|
||||
rep_als = getattr(rep, "als", None)
|
||||
if orig_als is not None and rep_als is not None:
|
||||
try:
|
||||
rep_als.turn_on = orig_als.turn_on
|
||||
except Exception:
|
||||
pass
|
||||
key = "als.turn_on"
|
||||
if key in orig:
|
||||
try:
|
||||
rep[key] = orig[key]
|
||||
except Exception:
|
||||
pass
|
||||
for pbone in orig.pose.bones:
|
||||
if pbone.name not in rep.pose.bones or key not in pbone:
|
||||
continue
|
||||
try:
|
||||
als = orig.get("als")
|
||||
if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
|
||||
if "als" not in rep:
|
||||
rep["als"] = {}
|
||||
rep["als"]["turn_on"] = als["turn_on"]
|
||||
except Exception:
|
||||
pass
|
||||
if getattr(orig, "data", None) and hasattr(orig.data, "keys") and key in orig.data:
|
||||
try:
|
||||
rep.pose.bones[pbone.name][key] = pbone[key]
|
||||
if rep.data is not None and hasattr(rep.data, "keys"):
|
||||
rep.data[key] = orig.data[key]
|
||||
except Exception:
|
||||
pass
|
||||
for pbone in orig.pose.bones:
|
||||
if pbone.name not in rep.pose.bones:
|
||||
continue
|
||||
rbone = rep.pose.bones[pbone.name]
|
||||
if key in pbone:
|
||||
try:
|
||||
rbone[key] = pbone[key]
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
als = pbone.get("als")
|
||||
if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
|
||||
if "als" not in rbone:
|
||||
rbone["als"] = {}
|
||||
rbone["als"]["turn_on"] = als["turn_on"]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -156,8 +276,9 @@ def run_mig_nla(orig, rep, report=None):
|
||||
prev_track = new_track
|
||||
_mirror_als_turn_on(orig, rep)
|
||||
if report:
|
||||
key = "als.turn_on"
|
||||
has_als = key in orig or any(key in pb for pb in orig.pose.bones)
|
||||
_debug_als_lookup(orig)
|
||||
has_als = _has_als_anywhere(orig)
|
||||
print(f"[DLM MigNLA] AnimLayers check: has_als={has_als}")
|
||||
if has_als:
|
||||
report({"INFO"}, "NLA layers detected, Animation Layer attributes migrated to Replacement Armature.")
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user