add manual meshes to migbbody shapekeys
This commit is contained in:
@@ -4152,4 +4152,793 @@ I'm using the view layer API to properly deselect all objects before setting the
|
|||||||
|
|
||||||
I need to switch modes before setting the replacement object as active to avoid conflicts with armature parenting. I need to ensure proper mode switching before setting the replacement object as active to avoid armature parenting conflicts. I'm implementing robust activation logic that ensures proper mode switching before setting the replacement object as active. I'm refining the object selection logic to properly set the active object in Blender. I'm optimizing object selection by using `bpy.ops.object.select_all` instead of looping through objects. I'm switching to OBJECT mode from POSE mode and deselecting all objects before selecting the replacement object.
|
I need to switch modes before setting the replacement object as active to avoid conflicts with armature parenting. I need to ensure proper mode switching before setting the replacement object as active to avoid armature parenting conflicts. I'm implementing robust activation logic that ensures proper mode switching before setting the replacement object as active. I'm refining the object selection logic to properly set the active object in Blender. I'm optimizing object selection by using `bpy.ops.object.select_all` instead of looping through objects. I'm switching to OBJECT mode from POSE mode and deselecting all objects before selecting the replacement object.
|
||||||
|
|
||||||
I'm updating the `_rep_active_for_animlayers` function to enter OBJECT mode, select the replacement object, and skip restoring the active object afterward. I'm implementing the selection restoration logic with try/except for `select_all` and using `context.temp_override` for proper window context. </details></think>
|
I'm updating the `_rep_active_for_animlayers` function to enter OBJECT mode, select the replacement object, and skip restoring the active object afterward. I'm implementing the selection restoration logic with try/except for `select_all` and using `context.temp_override` for proper window context. I'm adjusting the mode switching logic to ensure animlayers are properly activated and OBJECT mode is maintained when needed. I'm removing the restoration of `prev` mode after `_mirror_als_turn_on` and ensuring `bpy.context.view_layer` is properly synced. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Fixing activation: restoring the previous active object in `finally` immediately returns selection to orig, so AnimLayers won't stay on rep. We should also switch to Object mode before changing the active armature (Pose mode on orig can block switching active). Implementing: force Object mode, deselect all, select `rep`, set active to `rep`, run mirror, and leave `rep` active (don't restore orig).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Fixing a broken `try` block (incomplete structure).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="read_lints"><details>
|
||||||
|
<summary>Tool use: **read_lints**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Checking Blender API**
|
||||||
|
|
||||||
|
I'm verifying if `select_set` exists on Object in Blender 5 and checking for selection API changes. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||||
|
<details>
|
||||||
|
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||||
|
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"caseInsensitive": false,
|
||||||
|
"glob": "*.py",
|
||||||
|
"path": "T:/Dynamic-Link-Manager",
|
||||||
|
"pattern": "temp_override"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Additional data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isPruned": true,
|
||||||
|
"outputMode": "content",
|
||||||
|
"path": "T:/Dynamic-Link-Manager",
|
||||||
|
"pattern": "temp_override",
|
||||||
|
"topFiles": [
|
||||||
|
{
|
||||||
|
"matchCount": 2,
|
||||||
|
"uri": "ops/fk_rotations.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 2,
|
||||||
|
"uri": "ops/tweak_tools.py"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"totalFiles": 2,
|
||||||
|
"totalMatches": 4
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Status: **completed**
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
|
||||||
|
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/fk_rotations.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Adding view3d helper**
|
||||||
|
|
||||||
|
I'm adding a helper function to find the VIEW_3D area and using temp_override for mode_set and select_all operations. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Using a VIEW_3D `temp_override` so `bpy.ops.object.mode_set` and `select_all` run correctly (they often fail without a 3D View context).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Setting active object**
|
||||||
|
|
||||||
|
I'm ensuring the active object is set correctly in the view layer to avoid addon issues with `bpy.context.object`. I'm now setting both `vl.objects.active` and syncing `bpy.context` only when view layers match to prevent context mismatches. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="read_lints"><details>
|
||||||
|
<summary>Tool use: **read_lints**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Here’s what was going wrong and what we changed:
|
||||||
|
|
||||||
|
### Causes
|
||||||
|
1. **Selection was restored** – The old `finally` set the active object back to **orig**, so AnimLayers never stayed on **rep** and any addon logic tied to the active object ran on the wrong rig.
|
||||||
|
2. **Active armature in Pose mode** – With **orig** in **Pose** mode, only assigning `active = rep` often **does nothing**; you have to go to **Object** mode first, then change selection/active.
|
||||||
|
3. **Ops need a 3D View** – `bpy.ops.object.mode_set` / `select_all` can **no-op** without a **VIEW_3D** `area`, so we now use `context.temp_override(window=..., area=...)` when a 3D View exists.
|
||||||
|
|
||||||
|
### Current behavior
|
||||||
|
- Prefer **Object** mode (via override when possible).
|
||||||
|
- **Deselect all**, then **`rep.select_set(True)`** and **`view_layer.objects.active = rep`**.
|
||||||
|
- **Do not** restore the previous active object — **rep** stays active after MigNLA so AnimLayers stays on the replacement.
|
||||||
|
- Sync **`bpy.context.view_layer.objects.active`** when it matches the same view layer (helps addons that read **`bpy.context`**).
|
||||||
|
|
||||||
|
After running MigNLA, **rep** should remain the active, selected armature in Object mode so you don’t have to pick it again to turn AnimLayers on.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
Excellent. Explain to me what migcustprops currently does?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Here’s what **MigCustProps** (`run_mig_cust_props`) does in plain terms:
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
It copies **Blender ID custom properties** (the things you see in the Object Data Properties / bone panels as **ID Properties**, not regular RNA) from the **original armature** to the **replacement armature**, for the **armature object** and for **matching pose bones by name**.
|
||||||
|
|
||||||
|
### What gets copied
|
||||||
|
- **Armature object** (`orig` → `rep`): every key in `orig.keys()` except:
|
||||||
|
- `_RNA_UI`
|
||||||
|
- `rigify_type`
|
||||||
|
- `rigify_parameters`
|
||||||
|
- **Pose bones**: for each bone on `orig` that also exists on `rep`, it copies that bone’s ID props the same way.
|
||||||
|
|
||||||
|
### How nested data is handled
|
||||||
|
Values can be **leaf** values (numbers, strings, etc.) or **nested groups** (dict-like ID groups).
|
||||||
|
`_copy_id_prop_recursive` walks groups **recursively** and rebuilds the same structure on the rep side (e.g. nested `Settings` / `Devices`-style trees).
|
||||||
|
|
||||||
|
### What it does *not* do
|
||||||
|
- It does **not** copy normal **RNA** properties (only **ID props** via `.keys()`).
|
||||||
|
- It does **not** touch bones that aren’t on both rigs (same bone name on `rep`).
|
||||||
|
|
||||||
|
### Debug / logging
|
||||||
|
In the current code, **`debug = True`**, so it **prints a lot** to the console: keys before/after, bone lists, and `Settings` bone details when `Settings` exists on the rep rig.
|
||||||
|
|
||||||
|
So in one line: **MigCustProps = mirror addon/rig custom ID props from orig character to rep character (object + bones), with nested groups supported, and a few Rigify keys excluded.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
@c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt looks like it recognizes everything. it's probably good for ARP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
That lines up with how **MigCustProps** is written: it doesn’t care about Rigify vs ARP labels—it only copies **ID properties** from the armature object and from **pose bones that exist on both** rigs under the **same names**.
|
||||||
|
|
||||||
|
So for ARP, as long as the replacement rig’s bones are named the same as the original (typical after a duplicate / same metarig workflow), those custom props (rig UI, Settings-style groups, etc.) should migrate the same way. The main differences for ARP in your pipeline are things that *are* naming- or structure-specific (automatic pair discovery, tweak/FK bone lists, Remove Original collection logic)—not this step.
|
||||||
|
|
||||||
|
If you ever hit a prop that doesn’t copy, it’s usually a bone rename mismatch or an excluded key like `rigify_type` / `rigify_parameters`—we could add ARP-specific excludes later if something shouldn’t be mirrored.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
bpy.ops.dlm.migrator_bone_constraints() is definitely going to need some caution at least. does our current code ignore bone constraints that are on the linked armature in the first place?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Checking bone constraints**
|
||||||
|
|
||||||
|
I'm checking if the code properly handles linked armatures by examining `orig.library` and `pbone.constraints`. I'm testing if reading constraints from `orig.pose.bones` works for linked data and writing to `rep` constraints. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
**No.** `run_mig_bone_const` does **not** skip or special-case bone constraints because the armature is linked.
|
||||||
|
|
||||||
|
What it actually does:
|
||||||
|
|
||||||
|
1. **Clears** constraints on `rep` whose **target** is another “original” in `orig_to_rep` (only matters if you had multiple originals in that map; the usual pair is just `{orig: rep}`).
|
||||||
|
|
||||||
|
2. **For every bone name that exists on both `orig` and `rep`**, it walks **`pbone.constraints` on `orig`** and, for each constraint, does **`rbone.constraints.new(type=c.type)`** on `rep` and copies RNA props via **`_copy_constraint_props`** (with pointer retargeting for `orig` → `rep`).
|
||||||
|
|
||||||
|
There is **no** check for:
|
||||||
|
|
||||||
|
- `orig.library` / `orig.override_library`
|
||||||
|
- `rep.library` / `rep.override_library`
|
||||||
|
- whether constraints “came from” the linked file vs. were added locally
|
||||||
|
|
||||||
|
So if **`orig` is linked**, the addon still **reads** those constraints and **tries to mirror** them onto **`rep`** (assuming `rep` is editable). If **`rep` is still fully linked and not overriden**, adding constraints can **fail** (Blender won’t let you edit linked pose data).
|
||||||
|
|
||||||
|
So for ARP / library rigs, **caution is warranted**: you’re not ignoring linked rig internals; you’re **intentionally cloning** whatever is on `orig`’s bones onto `rep`, unless Blender blocks writes. If you want “don’t copy library-only rig constraints,” that would be a **new** rule (e.g. skip when `orig` is linked, or skip by constraint name/type, or only run when `rep` is local/overridden).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
Ok, bpy.ops.dlm.migrator_basebody_shapekeys() doesn't work because this isn't a cc/ic rig. Let's just add 2 new fields above the migbbody button that allows the user to select the body of the orig and repchars, deactivated by default, and activated when migbbody fails to find its target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Adding mesh properties**
|
||||||
|
|
||||||
|
I'm adding two new PointerProperty fields for manual mesh selection. I'm implementing conditional UI activation for mesh properties based on MigBBody failure detection. I'm adding two optional object pointers for body meshes with UI disabled by default and activated on MigBBody failure. I'm implementing two optional Mesh PointerProperties (`migbbody_orig_body`, `migbbody_rep_body`) with a BoolProperty (`migbbody_use_manual_bodies`) to control their activation. I'm implementing automatic activation of manual body properties when shapekey detection fails during MigBBody execution. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Inspecting how MigBBody finds base bodies and how the operator invokes it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
|
||||||
|
<details>
|
||||||
|
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||||
|
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"caseInsensitive": false,
|
||||||
|
"glob": "*.py",
|
||||||
|
"path": "T:/Dynamic-Link-Manager",
|
||||||
|
"pattern": "migrator_basebody|run_mig_bbody"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Additional data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isPruned": true,
|
||||||
|
"outputMode": "content",
|
||||||
|
"path": "T:/Dynamic-Link-Manager",
|
||||||
|
"pattern": "migrator_basebody|run_mig_bbody",
|
||||||
|
"topFiles": [
|
||||||
|
{
|
||||||
|
"matchCount": 5,
|
||||||
|
"uri": "ui/operators.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 1,
|
||||||
|
"uri": "ui\\panels.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 1,
|
||||||
|
"uri": "ui/panels.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 5,
|
||||||
|
"uri": "ui\\operators.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 2,
|
||||||
|
"uri": "ops\\migrator.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchCount": 2,
|
||||||
|
"uri": "ops/migrator.py"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"totalFiles": 6,
|
||||||
|
"totalMatches": 16
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Status: **completed**
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
|
||||||
|
<summary>Tool use: **ripgrep_raw_search**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Updating migration logic**
|
||||||
|
|
||||||
|
I'm adding explicit mesh object parameters for manual override in migration logic.
|
||||||
|
|
||||||
|
**Adding mesh pointers**
|
||||||
|
|
||||||
|
I'm adding pointer properties for original and replacement body meshes with UI rows disabled by default. I'm implementing pointer properties for original and replacement body meshes with manual enable flag. I'm implementing a BoolProperty `migbbody_manual_active` that enables UI rows when auto migration fails. I'm replacing the bool property with a single flag `migbbody_use_manual_bodies` that user enables to activate pickers and automatically sets True when operator detects failure. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/ui/operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/ui/panels.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Refactoring loop logic**
|
||||||
|
|
||||||
|
I'm extracting the core loop body logic into a new function `_process_mig_bbody_pair` to improve readability. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Implementing manual body pointers, UI (disabled until auto fails or user enables), and refactoring the mesh-processing block into a reusable helper.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Refactoring loop body**
|
||||||
|
|
||||||
|
I'm extracting the inner loop logic into `_process_mig_bbody_mesh` that modifies the mesh object in-place. I'm moving lines 645-748 into `override_hierarchy_create` and will pass `manual_orig_body` and `manual_rep_body` parameters to `run_mig_bbody_shapekeys`. I'm extracting manual override logic from `run_mig_bbody_shapekeys` into a separate function. I'm tracking processed count and found issues when orig_base is missing during loop iterations. I'm simplifying the loop logic to track when no shapes are processed and set auto_found_none flag accordingly. I'm adding an `any_auto` flag to track when auto-processing occurs in the loop.
|
||||||
|
|
||||||
|
I'm setting `any_auto` to True when auto-processing occurs in the loop. I'm implementing auto-first logic for mesh migration processing. I'm adding logic to enable UI rows on failure and support manual body selection for mesh migration.
|
||||||
|
|
||||||
|
I'm implementing conditional logic for manual body selection in mesh migration. I'm simplifying manual body selection logic to trigger on both pointers being set.
|
||||||
|
|
||||||
|
I'm implementing `migbbody_manual_override` bool property to control manual vs auto path logic.
|
||||||
|
|
||||||
|
**Implementing override property**
|
||||||
|
|
||||||
|
I'm implementing `migbbody_manual_override` property with default False and enabling UI rows based on its value. I'm adding `migbbody_manual_override` BoolProperty with default False and connecting it to UI row visibility.
|
||||||
|
|
||||||
|
**Connecting override property**
|
||||||
|
|
||||||
|
I'm linking the override property to UI row visibility and setting default values. I'm implementing the migration shapekeys logic with manual override handling.
|
||||||
|
|
||||||
|
**Handling override property**
|
||||||
|
|
||||||
|
I'm implementing the logic to set `migbbody_manual_override` when auto-processing fails and properties exist. I'm prioritizing auto-processing over manual override, skipping auto only when manual override is active and both fields are filled.
|
||||||
|
|
||||||
|
I'm prioritizing manual override path on first run when both bodies are set, skipping auto processing to avoid useless second run. I'm setting `props.migbbody_manual_override = True` only when auto processing fails, not on successful auto runs. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
Refactoring mesh processing into `_process_mig_bbody_mesh`, adding scene properties and UI, and implementing manual-first when override is set with both pointers.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/ui/properties.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
|
||||||
|
<summary>Tool use: **read_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
**Adding body mesh props**
|
||||||
|
|
||||||
|
I'm adding new properties to control manual body mesh detection in the migration tool. </details></think>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/ui/properties.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|
||||||
|
_**Agent (composer-2-fast)**_
|
||||||
|
|
||||||
|
<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/ui/panels.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
|
||||||
|
<summary>Tool use: **edit_file_v2**</summary>
|
||||||
|
|
||||||
|
</details></tool-use>
|
||||||
|
|||||||
@@ -226,13 +226,13 @@
|
|||||||
"last_updated": "2026-03-25T20:22:34Z"
|
"last_updated": "2026-03-25T20:22:34Z"
|
||||||
},
|
},
|
||||||
"f09112e7-3ddb-4e4e-b91a-418e38b881bf": {
|
"f09112e7-3ddb-4e4e-b91a-418e38b881bf": {
|
||||||
"user_message_count": 16,
|
"user_message_count": 20,
|
||||||
"agent_message_count": 243,
|
"agent_message_count": 298,
|
||||||
"start_timestamp": "2026-03-13T14:29:12-06:00",
|
"start_timestamp": "2026-03-13T14:29:12-06:00",
|
||||||
"end_timestamp": "2026-03-13T14:29:12-06:00",
|
"end_timestamp": "2026-03-13T14:29:12-06:00",
|
||||||
"markdown_size_bytes": 253227,
|
"markdown_size_bytes": 277098,
|
||||||
"provider": "cursoride",
|
"provider": "cursoride",
|
||||||
"last_updated": "2026-03-25T20:37:28Z"
|
"last_updated": "2026-03-25T20:47:25Z"
|
||||||
},
|
},
|
||||||
"f1bb25c5-e5d4-4aad-88a3-f562ab877040": {
|
"f1bb25c5-e5d4-4aad-88a3-f562ab877040": {
|
||||||
"user_message_count": 1,
|
"user_message_count": 1,
|
||||||
|
|||||||
+121
-104
@@ -629,9 +629,126 @@ def _find_base_body(armature, descendants_iter, rep_base_name=None):
|
|||||||
return candidates[0]
|
return candidates[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _process_mig_bbody_mesh(orig_base, ob, context):
|
||||||
|
"""Library overrides on rep mesh ob; copy shape key values and action from orig_base mesh."""
|
||||||
|
# Debug: base body mesh state before override handling.
|
||||||
|
_lib = getattr(ob.data, "library", None)
|
||||||
|
_ol = getattr(ob.data, "override_library", None)
|
||||||
|
_sys = getattr(_ol, "is_system_override", None) if _ol else None
|
||||||
|
print(f"[DLM step6] {ob.name} data: linked={_lib is not None}, override={_ol is not None}, is_system_override={_sys}")
|
||||||
|
# Library override: use hierarchy create (fully editable) when context available, else single-id override.
|
||||||
|
if getattr(ob, "library", None):
|
||||||
|
if context:
|
||||||
|
try:
|
||||||
|
ob = ob.override_hierarchy_create(
|
||||||
|
context.scene, context.view_layer, do_fully_editable=True
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
ob.override_create()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ob.override_create()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if getattr(ob.data, "library", None):
|
||||||
|
try:
|
||||||
|
ob.data.override_create(remap_local_usages=True)
|
||||||
|
# Make override user-editable (same as shift-click in data tab).
|
||||||
|
ol = getattr(ob.data, "override_library", None)
|
||||||
|
if ol is not None and getattr(ol, "is_system_override", None) is not None:
|
||||||
|
try:
|
||||||
|
ol.is_system_override = False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DLM step6] {ob.name} set is_system_override=False: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DLM step6] {ob.name} ob.data.override_create: {e}")
|
||||||
|
elif getattr(ob.data, "override_library", None):
|
||||||
|
ol = ob.data.override_library
|
||||||
|
if getattr(ol, "is_system_override", False):
|
||||||
|
try:
|
||||||
|
ol.is_system_override = False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DLM step6] {ob.name} set is_system_override=False: {e}")
|
||||||
|
# Debug: state after override handling.
|
||||||
|
_ol2 = getattr(ob.data, "override_library", None)
|
||||||
|
_sys2 = getattr(_ol2, "is_system_override", None) if _ol2 else None
|
||||||
|
print(f"[DLM step6] {ob.name} after: override={_ol2 is not None}, is_system_override={_sys2} (False=editable)")
|
||||||
|
if ob.data.shape_keys:
|
||||||
|
# Ensure we can write shape key values: override the Key block if it is linked.
|
||||||
|
sk = ob.data.shape_keys
|
||||||
|
if getattr(sk, "library", None):
|
||||||
|
try:
|
||||||
|
sk.override_create(remap_local_usages=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DLM step6] {ob.name} shape_keys.override_create: {e}")
|
||||||
|
# Copy shape key values from original base body to replacement (by matching key name).
|
||||||
|
if orig_base and orig_base.data.shape_keys:
|
||||||
|
rep_blocks = ob.data.shape_keys.key_blocks
|
||||||
|
orig_blocks = orig_base.data.shape_keys.key_blocks
|
||||||
|
n_copied = 0
|
||||||
|
for orig_key in orig_blocks:
|
||||||
|
rep_key = rep_blocks.get(orig_key.name)
|
||||||
|
if rep_key is not None:
|
||||||
|
rep_key.value = orig_key.value
|
||||||
|
n_copied += 1
|
||||||
|
print(f"[DLM step6] {ob.name} shapekey values: copied {n_copied}/{len(orig_blocks)} from {orig_base.name}")
|
||||||
|
else:
|
||||||
|
if not orig_base:
|
||||||
|
print(f"[DLM step6] {ob.name} no orig base body found")
|
||||||
|
elif not orig_base.data.shape_keys:
|
||||||
|
print(f"[DLM step6] {ob.name} orig base body has no shape_keys")
|
||||||
|
if not ob.data.shape_keys.animation_data:
|
||||||
|
ob.data.shape_keys.animation_data_create()
|
||||||
|
sk_ad = ob.data.shape_keys.animation_data
|
||||||
|
# Prefer action (and slot) from original base body; fallback to name lookup.
|
||||||
|
orig_sk_ad = None
|
||||||
|
if orig_base and orig_base.data.shape_keys:
|
||||||
|
orig_sk_ad = orig_base.data.shape_keys.animation_data
|
||||||
|
action = None
|
||||||
|
if orig_sk_ad and getattr(orig_sk_ad, "action", None):
|
||||||
|
action = orig_sk_ad.action
|
||||||
|
if action is None:
|
||||||
|
body_name = ob.name
|
||||||
|
action = (
|
||||||
|
bpy.data.actions.get(body_name + "Action")
|
||||||
|
or bpy.data.actions.get(ob.data.name + "Action")
|
||||||
|
or bpy.data.actions.get(body_name + "Action.001")
|
||||||
|
)
|
||||||
|
if action:
|
||||||
|
# Duplicate action so repchar has independent copy
|
||||||
|
dup_action = _duplicate_action(action, suffix=".rep")
|
||||||
|
# Copy slot-related props before action so slot is applied (Blender 4.4+).
|
||||||
|
if orig_sk_ad and hasattr(sk_ad, "last_slot_identifier") and hasattr(orig_sk_ad, "last_slot_identifier") and orig_sk_ad.last_slot_identifier:
|
||||||
|
sk_ad.last_slot_identifier = orig_sk_ad.last_slot_identifier
|
||||||
|
sk_ad.action = dup_action
|
||||||
|
if orig_sk_ad and getattr(orig_sk_ad, "action_slot", None) and getattr(sk_ad, "action_slot", None):
|
||||||
|
try:
|
||||||
|
sk_ad.action_slot = orig_sk_ad.action_slot
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for prop in ("action_blend_type", "action_extrapolation", "action_influence"):
|
||||||
|
if orig_sk_ad and hasattr(orig_sk_ad, prop) and hasattr(sk_ad, prop):
|
||||||
|
try:
|
||||||
|
setattr(sk_ad, prop, getattr(orig_sk_ad, prop))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def run_mig_bbody_shapekeys(orig, rep, rep_descendants, context=None):
|
def run_mig_bbody_shapekeys(orig, rep, rep_descendants, context=None):
|
||||||
"""Replacement base body: library override (fully editable when context given), copy shapekey values, then shape-key action."""
|
"""Replacement base body: library override (fully editable when context given), copy shapekey values, then shape-key action."""
|
||||||
|
props = getattr(context.scene, "dynamic_link_manager", None) if context else None
|
||||||
|
if props and getattr(props, "migbbody_manual_override", False):
|
||||||
|
mo = getattr(props, "migbbody_orig_body", None)
|
||||||
|
mr = getattr(props, "migbbody_rep_body", None)
|
||||||
|
if mo and mr and mo.type == "MESH" and mr.type == "MESH":
|
||||||
|
_process_mig_bbody_mesh(mo, mr, context)
|
||||||
|
return
|
||||||
|
|
||||||
orig_descendants = list(descendants(orig))
|
orig_descendants = list(descendants(orig))
|
||||||
|
any_auto = False
|
||||||
for ob in list(rep_descendants):
|
for ob in list(rep_descendants):
|
||||||
if not _base_body_name_match(ob):
|
if not _base_body_name_match(ob):
|
||||||
continue
|
continue
|
||||||
@@ -641,111 +758,11 @@ def run_mig_bbody_shapekeys(orig, rep, rep_descendants, context=None):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
any_auto = True
|
||||||
orig_base = _find_base_body(orig, orig_descendants, rep_base_name=ob.name)
|
orig_base = _find_base_body(orig, orig_descendants, rep_base_name=ob.name)
|
||||||
# Debug: base body mesh state before override handling.
|
_process_mig_bbody_mesh(orig_base, ob, context)
|
||||||
_lib = getattr(ob.data, "library", None)
|
if not any_auto and props is not None:
|
||||||
_ol = getattr(ob.data, "override_library", None)
|
props.migbbody_manual_override = True
|
||||||
_sys = getattr(_ol, "is_system_override", None) if _ol else None
|
|
||||||
print(f"[DLM step6] {ob.name} data: linked={_lib is not None}, override={_ol is not None}, is_system_override={_sys}")
|
|
||||||
# Library override: use hierarchy create (fully editable) when context available, else single-id override.
|
|
||||||
if getattr(ob, "library", None):
|
|
||||||
if context:
|
|
||||||
try:
|
|
||||||
ob = ob.override_hierarchy_create(
|
|
||||||
context.scene, context.view_layer, do_fully_editable=True
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
ob.override_create()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
ob.override_create()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if getattr(ob.data, "library", None):
|
|
||||||
try:
|
|
||||||
ob.data.override_create(remap_local_usages=True)
|
|
||||||
# Make override user-editable (same as shift-click in data tab).
|
|
||||||
ol = getattr(ob.data, "override_library", None)
|
|
||||||
if ol is not None and getattr(ol, "is_system_override", None) is not None:
|
|
||||||
try:
|
|
||||||
ol.is_system_override = False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DLM step6] {ob.name} set is_system_override=False: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DLM step6] {ob.name} ob.data.override_create: {e}")
|
|
||||||
elif getattr(ob.data, "override_library", None):
|
|
||||||
ol = ob.data.override_library
|
|
||||||
if getattr(ol, "is_system_override", False):
|
|
||||||
try:
|
|
||||||
ol.is_system_override = False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DLM step6] {ob.name} set is_system_override=False: {e}")
|
|
||||||
# Debug: state after override handling.
|
|
||||||
_ol2 = getattr(ob.data, "override_library", None)
|
|
||||||
_sys2 = getattr(_ol2, "is_system_override", None) if _ol2 else None
|
|
||||||
print(f"[DLM step6] {ob.name} after: override={_ol2 is not None}, is_system_override={_sys2} (False=editable)")
|
|
||||||
if ob.data.shape_keys:
|
|
||||||
# Ensure we can write shape key values: override the Key block if it is linked.
|
|
||||||
sk = ob.data.shape_keys
|
|
||||||
if getattr(sk, "library", None):
|
|
||||||
try:
|
|
||||||
sk.override_create(remap_local_usages=True)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DLM step6] {ob.name} shape_keys.override_create: {e}")
|
|
||||||
# Copy shape key values from original base body to replacement (by matching key name).
|
|
||||||
if orig_base and orig_base.data.shape_keys:
|
|
||||||
rep_blocks = ob.data.shape_keys.key_blocks
|
|
||||||
orig_blocks = orig_base.data.shape_keys.key_blocks
|
|
||||||
n_copied = 0
|
|
||||||
for orig_key in orig_blocks:
|
|
||||||
rep_key = rep_blocks.get(orig_key.name)
|
|
||||||
if rep_key is not None:
|
|
||||||
rep_key.value = orig_key.value
|
|
||||||
n_copied += 1
|
|
||||||
print(f"[DLM step6] {ob.name} shapekey values: copied {n_copied}/{len(orig_blocks)} from {orig_base.name}")
|
|
||||||
else:
|
|
||||||
if not orig_base:
|
|
||||||
print(f"[DLM step6] {ob.name} no orig base body found for armature {orig.name}")
|
|
||||||
elif not orig_base.data.shape_keys:
|
|
||||||
print(f"[DLM step6] {ob.name} orig base body has no shape_keys")
|
|
||||||
if not ob.data.shape_keys.animation_data:
|
|
||||||
ob.data.shape_keys.animation_data_create()
|
|
||||||
sk_ad = ob.data.shape_keys.animation_data
|
|
||||||
# Prefer action (and slot) from original base body; fallback to name lookup.
|
|
||||||
orig_sk_ad = None
|
|
||||||
if orig_base and orig_base.data.shape_keys:
|
|
||||||
orig_sk_ad = orig_base.data.shape_keys.animation_data
|
|
||||||
action = None
|
|
||||||
if orig_sk_ad and getattr(orig_sk_ad, "action", None):
|
|
||||||
action = orig_sk_ad.action
|
|
||||||
if action is None:
|
|
||||||
body_name = ob.name
|
|
||||||
action = (
|
|
||||||
bpy.data.actions.get(body_name + "Action")
|
|
||||||
or bpy.data.actions.get(ob.data.name + "Action")
|
|
||||||
or bpy.data.actions.get(body_name + "Action.001")
|
|
||||||
)
|
|
||||||
if action:
|
|
||||||
# Duplicate action so repchar has independent copy
|
|
||||||
dup_action = _duplicate_action(action, suffix=".rep")
|
|
||||||
# Copy slot-related props before action so slot is applied (Blender 4.4+).
|
|
||||||
if orig_sk_ad and hasattr(sk_ad, "last_slot_identifier") and hasattr(orig_sk_ad, "last_slot_identifier") and orig_sk_ad.last_slot_identifier:
|
|
||||||
sk_ad.last_slot_identifier = orig_sk_ad.last_slot_identifier
|
|
||||||
sk_ad.action = dup_action
|
|
||||||
if orig_sk_ad and getattr(orig_sk_ad, "action_slot", None) and getattr(sk_ad, "action_slot", None):
|
|
||||||
try:
|
|
||||||
sk_ad.action_slot = orig_sk_ad.action_slot
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
for prop in ("action_blend_type", "action_extrapolation", "action_influence"):
|
|
||||||
if orig_sk_ad and hasattr(orig_sk_ad, prop) and hasattr(sk_ad, prop):
|
|
||||||
try:
|
|
||||||
setattr(sk_ad, prop, getattr(orig_sk_ad, prop))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def run_full_migration(context):
|
def run_full_migration(context):
|
||||||
|
|||||||
+10
-1
@@ -399,7 +399,16 @@ class DLM_OT_migrator_basebody_shapekeys(Operator):
|
|||||||
from ..utils import descendants
|
from ..utils import descendants
|
||||||
rep_descendants = descendants(rep)
|
rep_descendants = descendants(rep)
|
||||||
run_mig_bbody_shapekeys(orig, rep, rep_descendants, context)
|
run_mig_bbody_shapekeys(orig, rep, rep_descendants, context)
|
||||||
self.report({"INFO"}, "Migrate BaseBody shapekeys done.")
|
props = context.scene.dynamic_link_manager
|
||||||
|
if props.migbbody_manual_override and (
|
||||||
|
not props.migbbody_orig_body or not props.migbbody_rep_body
|
||||||
|
):
|
||||||
|
self.report(
|
||||||
|
{"WARNING"},
|
||||||
|
"MigBBody: no CC-style base mesh matched. Pick Original/Replacement body meshes, then run again.",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.report({"INFO"}, "Migrate BaseBody shapekeys done.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.report({"ERROR"}, str(e))
|
self.report({"ERROR"}, str(e))
|
||||||
|
|||||||
@@ -98,6 +98,14 @@ class DLM_PT_main_panel(Panel):
|
|||||||
# Situational
|
# Situational
|
||||||
situational_box = layout.box()
|
situational_box = layout.box()
|
||||||
situational_box.label(text="Situational Fixes", icon="QUESTION")
|
situational_box.label(text="Situational Fixes", icon="QUESTION")
|
||||||
|
row = situational_box.row()
|
||||||
|
row.prop(props, "migbbody_manual_override", text="Manual body meshes")
|
||||||
|
row = situational_box.row()
|
||||||
|
row.enabled = props.migbbody_manual_override
|
||||||
|
row.prop(props, "migbbody_orig_body", text="Original body")
|
||||||
|
row = situational_box.row()
|
||||||
|
row.enabled = props.migbbody_manual_override
|
||||||
|
row.prop(props, "migbbody_rep_body", text="Replacement body")
|
||||||
row = situational_box.row(align=True)
|
row = situational_box.row(align=True)
|
||||||
row.operator("dlm.migrator_basebody_shapekeys", text="MigBBodyShapeKeys", icon="SHAPEKEY_DATA")
|
row.operator("dlm.migrator_basebody_shapekeys", text="MigBBodyShapeKeys", icon="SHAPEKEY_DATA")
|
||||||
row = situational_box.row(align=True)
|
row = situational_box.row(align=True)
|
||||||
|
|||||||
@@ -78,6 +78,28 @@ class DynamicLinkManagerProperties(PropertyGroup):
|
|||||||
poll=lambda self, obj: obj and obj.type == "ARMATURE",
|
poll=lambda self, obj: obj and obj.type == "ARMATURE",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# MigBBody: manual mesh pair when CC/iClone-style auto-detection fails
|
||||||
|
migbbody_manual_override: BoolProperty(
|
||||||
|
name="Manual body meshes",
|
||||||
|
description=(
|
||||||
|
"Enable to pick original and replacement body meshes manually. "
|
||||||
|
"Auto-enables if MigBBody cannot find a base mesh (non-CC rigs)."
|
||||||
|
),
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
migbbody_orig_body: PointerProperty(
|
||||||
|
name="Original body",
|
||||||
|
description="Original character body mesh (shape key source)",
|
||||||
|
type=bpy.types.Object,
|
||||||
|
poll=lambda self, obj: obj and obj.type == "MESH",
|
||||||
|
)
|
||||||
|
migbbody_rep_body: PointerProperty(
|
||||||
|
name="Replacement body",
|
||||||
|
description="Replacement character body mesh (shape key target)",
|
||||||
|
type=bpy.types.Object,
|
||||||
|
poll=lambda self, obj: obj and obj.type == "MESH",
|
||||||
|
)
|
||||||
|
|
||||||
# Tweak tools (collapsible section)
|
# Tweak tools (collapsible section)
|
||||||
tweak_tools_section_expanded: BoolProperty(
|
tweak_tools_section_expanded: BoolProperty(
|
||||||
name="Tweak Tools Expanded",
|
name="Tweak Tools Expanded",
|
||||||
|
|||||||
Reference in New Issue
Block a user