add FKRotBake, armature parent functionality in RetargRelatives
This commit is contained in:
@@ -14395,7 +14395,7 @@ Let me update the fk_rotations.py to implement this baking approach.</details></
|
|||||||
---
|
---
|
||||||
|
|
||||||
<tool-use data-tool-type="read" data-tool-name="read_file_v2">
|
<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><summary>Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\panels.py</summary>
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
</tool-use>
|
</tool-use>
|
||||||
@@ -14447,7 +14447,9 @@ Parameters:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
"T:\\Dynamic-Link-Manager\\ops\\fk_rotations.py"
|
"T:\\Dynamic-Link-Manager\\ops\\fk_rotations.py",
|
||||||
|
"T:\\Dynamic-Link-Manager\\ui\\operators.py",
|
||||||
|
"T:\\Dynamic-Link-Manager\\ui\\panels.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -14458,6 +14460,12 @@ Result:
|
|||||||
"linterErrorsByFile": [
|
"linterErrorsByFile": [
|
||||||
{
|
{
|
||||||
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\fk_rotations.py"
|
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\fk_rotations.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ui\\operators.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ui\\panels.py"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -14617,7 +14625,13 @@ Let me fix this:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
The string to replace was not found in the file.
|
<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\operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -17241,3 +17255,418 @@ The constraints are driving the pose but not "baking" it. When removed, the bone
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
I added `bpy.ops.pose.visual_transform_apply()` which is Blender's built-in operator for baking constraint results into the actual pose. This should apply the visual transform so when we remove the constraints, the pose stays.
|
||||||
|
|
||||||
|
Let me check if there are any linter errors.</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\\fk_rotations.py"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"linterErrorsByFile": [
|
||||||
|
{
|
||||||
|
"relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\fk_rotations.py"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Status: **completed**
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now using `bpy.ops.pose.visual_transform_apply()` which is Blender's built-in operator for baking constraint results into the actual bone pose. This should properly apply the copied rotations so they persist when constraints are removed.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Now using `bpy.ops.pose.visual_transform_apply()` - Blender's built-in operator that bakes constraint results into the actual pose. This should properly save the copied rotations when constraints are removed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
@c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt That worked! now we just need to add a bake step, similar to the tweak tools section, i.e. bpy.ops.dlm.tweak_bake_arm()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (model kimi-k2.5, mode Agent)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Great! The visual transform apply worked. Now the user wants to add a bake step, similar to how the tweak tools work. Let me check how the tweak bake is implemented and add a similar operator for FK rotation baking.</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\tweak_tools.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I understand the pattern. The user wants to add a bake operator for FK rotations similar to the tweak bake. Let me add:
|
||||||
|
|
||||||
|
1. A `bake_fk_rotations` function in `fk_rotations.py`
|
||||||
|
2. A new operator `DLM_OT_migrator_fk_rotations_bake` in `ui/operators.py`
|
||||||
|
3. Register it and add to the UI
|
||||||
|
|
||||||
|
Let me implement this.</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\fk_rotations.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I'll add a bake function similar to the tweak tools. Let me append it to the fk_rotations.py file.</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\fk_rotations.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I need to add the UI operator and register it. Let me add a new operator class in ui/operators.py.</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\ui\operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I need to add the bake operator. Let me insert it after the existing MigFKRot operator.</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\ui\operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I need to register the new operator and add it to the UI. Let me find the registration section and add it there.</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\ui\operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now I need to add the new operator to the OPERATOR_CLASSES list.</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\ui\operators.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now let me add the bake button to the UI panel, next to the existing MigFKRot button.Let me add the bake button next to the MigFKRot button.</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\ui\panels.py</summary>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
Now let me check the lints to make sure everything is correct.Done. The bake operator is now added. Summary of changes:1. **ops/fk_rotations.py** - Added `bake_fk_rotations()` function that uses `nla.bake` to bake FK rotations to keyframes
|
||||||
|
2. **ui/operators.py** - Added `DLM_OT_migrator_fk_rotations_bake` operator class with `track_name` and `post_clean` properties, registered it
|
||||||
|
3. **ui/panels.py** - Added "Bake" button next to the "MigFKRot" button in the Character Migrator panelThe bake operator:- Uses `bpy.ops.nla.bake` with `visual_keying=True`
|
||||||
|
- Takes an optional NLA track name for frame range (like tweak tools)
|
||||||
|
- Has a `post_clean` option for curve cleaning
|
||||||
|
- Creates a new action (doesn't overwrite existing)</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Done. Added the bake operator for FK rotations:
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
1. **ops/fk_rotations.py** - Added `bake_fk_rotations()` function using `nla.bake` with `visual_keying=True`, similar to tweak tools
|
||||||
|
2. **ui/operators.py** - Added `DLM_OT_migrator_fk_rotations_bake` operator with optional NLA track name and post-clean options
|
||||||
|
3. **ui/panels.py** - Added "Bake" button next to "MigFKRot"
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
1. Click **MigFKRot** to copy FK rotations from original to replacement (current frame)
|
||||||
|
2. Click **Bake** to bake those rotations to keyframes using `nla.bake`
|
||||||
|
|
||||||
|
The bake operator:
|
||||||
|
- Uses visual keying (captures constraint results)
|
||||||
|
- Takes optional NLA track name for frame range
|
||||||
|
- Has post-clean option for curve cleaning
|
||||||
|
- Creates a new action (doesn't overwrite existing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
@c:\Users\Nathan\.cursor\projects\t-Dynamic-Link-Manager\terminals\3.txt can't enable addon
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (model kimi-k2.5, mode Agent)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
The user can't enable the addon. Let me check the terminal for errors.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<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>
|
||||||
|
There's a `enableFailure` message. Let me check if there's more context about what caused it. Let me read more lines.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<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 shows the addon is trying to load but there's an `enableFailure` at the end. Let me look for the error message that explains why it failed.</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": "SyntaxError|ImportError|enableFailure|Traceback",
|
||||||
|
"path": "c:\\Users\\Nathan\\.cursor\\projects\\t-Dynamic-Link-Manager\\terminals\\3.txt",
|
||||||
|
"outputMode": "content",
|
||||||
|
"contextBefore": 2,
|
||||||
|
"contextAfter": 5,
|
||||||
|
"caseInsensitive": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Additional data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isPruned": true,
|
||||||
|
"pattern": "SyntaxError|ImportError|enableFailure|Traceback",
|
||||||
|
"path": "c:\\Users\\Nathan\\.cursor\\projects\\t-Dynamic-Link-Manager\\terminals\\3.txt",
|
||||||
|
"outputMode": "content",
|
||||||
|
"totalFiles": 1,
|
||||||
|
"totalMatches": 12,
|
||||||
|
"topFiles": [
|
||||||
|
{
|
||||||
|
"uri": "c:\\Users\\Nathan\\.cursor\\projects\\t-Dynamic-Link-Manager\\terminals\\3.txt",
|
||||||
|
"matchCount": 88
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Status: **completed**
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
I see the error now:
|
||||||
|
```
|
||||||
|
NameError: name 'BoolProperty' is not defined
|
||||||
|
```
|
||||||
|
|
||||||
|
The `BoolProperty` and `StringProperty` need to be imported from `bpy.props`. I added properties to the bake operator but forgot to import them at the top of the file.Fixed - just needed to add the missing import for `StringProperty` and `BoolProperty`.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed. Just needed to import `BoolProperty` and `StringProperty` from `bpy.props`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<tool-use data-tool-type="generic" data-tool-name="read_lints">
|
||||||
|
<details>
|
||||||
|
<summary>Tool use: **read_lints** • Read lints for 1 file</summary>
|
||||||
|
|
||||||
|
Lint paths:
|
||||||
|
|
||||||
|
- `T:\Dynamic-Link-Manager\ui\operators.py`
|
||||||
|
|
||||||
|
Lint results
|
||||||
|
|
||||||
|
Lint errors for `T:\Dynamic-Link-Manager\ui\operators.py`
|
||||||
|
|
||||||
|
|
||||||
|
| Message | Range | Severity |
|
||||||
|
|-------|-------|----------|
|
||||||
|
| Import "bpy.types" could not be resolved | 8:6-8:15 | DIAGNOSTIC_SEVERITY_WARNING |
|
||||||
|
| Import "bpy.props" could not be resolved | 9:6-9:15 | DIAGNOSTIC_SEVERITY_WARNING |
|
||||||
|
| Import "bpy.props" could not be resolved | 10:6-10:15 | DIAGNOSTIC_SEVERITY_WARNING |
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
The linter warnings are expected since it can't resolve Blender imports. The important thing is that the Python will work correctly now that the imports are present.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The warnings are expected (Blender imports). The addon should enable now with the proper imports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
in bpy.ops.dlm.migrator_retarget_relations(), we should make it so if there is any parent of the origarmature, it should be replicated on the reparmature.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (model kimi-k2.5, mode Agent)**_
|
||||||
|
|
||||||
|
<think><details><summary>Thought Process</summary>
|
||||||
|
The user wants to update `migrator_retarget_relations` to also replicate the parent of the original armature on the replacement armature, if the original has one.</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 logic to replicate the parent of `orig` on `rep`. If `orig.parent` exists, set `rep.parent = orig.parent`.</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>
|
||||||
|
Done. Now if the original armature has a parent, the replacement will also be parented to the same object/bone.</details></think>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Done. Now `migrator_retarget_relations` will also replicate the parent of the original armature on the replacement:
|
||||||
|
|
||||||
|
- `rep.parent = orig.parent`
|
||||||
|
- `rep.parent_type = orig.parent_type` (object/bone/vertex)
|
||||||
|
- `rep.parent_bone = orig.parent_bone` (if parented to a bone)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**User**_
|
||||||
|
|
||||||
|
parent but keep transforms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_**Agent (model kimi-k2.5, mode Agent)**_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
Status: **loading**
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</tool-use>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -156,3 +156,84 @@ def copy_fk_rotations(context, orig, rep):
|
|||||||
|
|
||||||
if original_active:
|
if original_active:
|
||||||
context.view_layer.objects.active = original_active
|
context.view_layer.objects.active = original_active
|
||||||
|
|
||||||
|
|
||||||
|
def bake_fk_rotations(context, rep, track_name=None, post_clean=False):
|
||||||
|
"""
|
||||||
|
Bake FK arm/finger rotations to keyframes.
|
||||||
|
Similar to tweak_tools.bake_tweak_constraints but for FK bones.
|
||||||
|
Returns (True, message) or (False, error_message).
|
||||||
|
"""
|
||||||
|
fk_names = _get_fk_bones(rep)
|
||||||
|
if not fk_names:
|
||||||
|
return False, f"No FK bones found on {rep.name}"
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
# Get frame range from track or scene
|
||||||
|
frame_range = None
|
||||||
|
if track_name and rep.animation_data and rep.animation_data.nla_tracks:
|
||||||
|
track = rep.animation_data.nla_tracks.get(track_name)
|
||||||
|
if track and track.strips:
|
||||||
|
start = min(s.frame_start for s in track.strips)
|
||||||
|
end = max(s.frame_end for s in track.strips)
|
||||||
|
frame_range = (int(start), int(end))
|
||||||
|
|
||||||
|
if not frame_range:
|
||||||
|
frame_range = (scene.frame_start, scene.frame_end)
|
||||||
|
|
||||||
|
frame_start, frame_end = frame_range
|
||||||
|
|
||||||
|
# Ensure rep is active and in pose mode
|
||||||
|
if context.view_layer.objects.active != rep:
|
||||||
|
context.view_layer.objects.active = rep
|
||||||
|
if rep.mode != "POSE":
|
||||||
|
bpy.ops.object.mode_set(mode="POSE")
|
||||||
|
|
||||||
|
# Select only FK bones
|
||||||
|
bpy.ops.pose.select_all(action="DESELECT")
|
||||||
|
for name in fk_names:
|
||||||
|
if name in rep.pose.bones:
|
||||||
|
rep.pose.bones[name].bone.select = True
|
||||||
|
|
||||||
|
# Bake
|
||||||
|
try:
|
||||||
|
bpy.ops.nla.bake(
|
||||||
|
frame_start=frame_start,
|
||||||
|
frame_end=frame_end,
|
||||||
|
step=1,
|
||||||
|
only_selected=True,
|
||||||
|
visual_keying=True,
|
||||||
|
clear_constraints=False,
|
||||||
|
clear_parents=False,
|
||||||
|
use_current_action=False, # Create new action
|
||||||
|
clean_curves=False,
|
||||||
|
bake_types={"POSE"},
|
||||||
|
channel_types={"ROTATION"},
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
if not post_clean:
|
||||||
|
return True, f"Baked {len(fk_names)} FK bones ({frame_start}-{frame_end})."
|
||||||
|
|
||||||
|
# Post-clean
|
||||||
|
win = context.window
|
||||||
|
for area in win.screen.areas:
|
||||||
|
if area.type == "DOPESHEET_EDITOR":
|
||||||
|
with context.temp_override(window=win, area=area):
|
||||||
|
try:
|
||||||
|
bpy.ops.action.clean_keyframes()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
break
|
||||||
|
for area in win.screen.areas:
|
||||||
|
if area.type == "GRAPH_EDITOR":
|
||||||
|
with context.temp_override(window=win, area=area):
|
||||||
|
try:
|
||||||
|
bpy.ops.graph.decimate(mode="ERROR", error=0.001)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
break
|
||||||
|
|
||||||
|
return True, f"Baked and cleaned {len(fk_names)} FK bones ({frame_start}-{frame_end})."
|
||||||
|
|||||||
@@ -460,6 +460,16 @@ def run_mig_bone_const(orig, rep, orig_to_rep):
|
|||||||
|
|
||||||
def run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep):
|
def run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep):
|
||||||
"""Retarget relations: parents, constraint targets, Armature modifiers to rep. Skip objects in orig's hierarchy (linked collection)."""
|
"""Retarget relations: parents, constraint targets, Armature modifiers to rep. Skip objects in orig's hierarchy (linked collection)."""
|
||||||
|
# Replicate orig's parent on rep if it exists (keep world transform)
|
||||||
|
if orig.parent is not None:
|
||||||
|
# Store world matrix before reparenting
|
||||||
|
world_matrix = rep.matrix_world.copy()
|
||||||
|
rep.parent = orig.parent
|
||||||
|
rep.parent_type = orig.parent_type
|
||||||
|
rep.parent_bone = orig.parent_bone
|
||||||
|
# Restore world matrix
|
||||||
|
rep.matrix_world = world_matrix
|
||||||
|
|
||||||
orig_hierarchy = {orig} | descendants(orig)
|
orig_hierarchy = {orig} | descendants(orig)
|
||||||
candidates = set(rep_descendants)
|
candidates = set(rep_descendants)
|
||||||
for ob in bpy.data.objects:
|
for ob in bpy.data.objects:
|
||||||
|
|||||||
+32
-1
@@ -6,6 +6,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import os
|
import os
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
|
from bpy.props import StringProperty, BoolProperty
|
||||||
from bpy.props import StringProperty, IntProperty
|
from bpy.props import StringProperty, IntProperty
|
||||||
|
|
||||||
ADDON_NAME = __package__.rsplit(".", 1)[0] if "." in __package__ else __package__
|
ADDON_NAME = __package__.rsplit(".", 1)[0] if "." in __package__ else __package__
|
||||||
@@ -406,7 +407,7 @@ class DLM_OT_migrator_basebody_shapekeys(Operator):
|
|||||||
class DLM_OT_migrator_fk_rotations(Operator):
|
class DLM_OT_migrator_fk_rotations(Operator):
|
||||||
bl_idname = "dlm.migrator_fk_rotations"
|
bl_idname = "dlm.migrator_fk_rotations"
|
||||||
bl_label = "MigFKRot"
|
bl_label = "MigFKRot"
|
||||||
bl_description = "Copy FK arm and finger rotations from original to replacement (uses copy_pose_vis_rot)"
|
bl_description = "Copy FK arm and finger rotations from original to replacement (uses constraints)"
|
||||||
bl_icon = "BONE_DATA"
|
bl_icon = "BONE_DATA"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
@@ -429,6 +430,35 @@ class DLM_OT_migrator_fk_rotations(Operator):
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DLM_OT_migrator_fk_rotations_bake(Operator):
|
||||||
|
bl_idname = "dlm.migrator_fk_rotations_bake"
|
||||||
|
bl_label = "Bake MigFKRot"
|
||||||
|
bl_description = "Bake FK rotations to keyframes using nla.bake (similar to tweak tools)"
|
||||||
|
bl_icon = "KEYFRAME"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
track_name: StringProperty(name="NLA Track", description="Optional NLA track name for frame range", default="")
|
||||||
|
post_clean: BoolProperty(name="Post-clean", description="Clean curves after bake", default=False)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
orig, rep = _get_migrator_pair(context)
|
||||||
|
if not orig or not rep or orig == rep:
|
||||||
|
self.report({"ERROR"}, "No valid character pair.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
try:
|
||||||
|
from ..ops.fk_rotations import bake_fk_rotations
|
||||||
|
ok, msg = bake_fk_rotations(context, rep, track_name=self.track_name or None, post_clean=self.post_clean)
|
||||||
|
if ok:
|
||||||
|
self.report({"INFO"}, msg)
|
||||||
|
return {"FINISHED"}
|
||||||
|
else:
|
||||||
|
self.report({"ERROR"}, msg)
|
||||||
|
return {"CANCELLED"}
|
||||||
|
except Exception as e:
|
||||||
|
self.report({"ERROR"}, str(e))
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
|
||||||
MIGRATOR_STEP_OPS = (
|
MIGRATOR_STEP_OPS = (
|
||||||
"dlm.migrator_copy_attributes",
|
"dlm.migrator_copy_attributes",
|
||||||
"dlm.migrator_migrate_nla",
|
"dlm.migrator_migrate_nla",
|
||||||
@@ -718,4 +748,5 @@ OPERATOR_CLASSES = [
|
|||||||
DLM_OT_tweak_remove_both,
|
DLM_OT_tweak_remove_both,
|
||||||
DLM_OT_tweak_bake_both,
|
DLM_OT_tweak_bake_both,
|
||||||
DLM_OT_migrator_fk_rotations,
|
DLM_OT_migrator_fk_rotations,
|
||||||
|
DLM_OT_migrator_fk_rotations_bake,
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-1
@@ -93,8 +93,9 @@ class DLM_PT_main_panel(Panel):
|
|||||||
row.operator("dlm.migrator_bone_constraints", text="MigBoneConst", icon="CONSTRAINT_BONE")
|
row.operator("dlm.migrator_bone_constraints", text="MigBoneConst", icon="CONSTRAINT_BONE")
|
||||||
row.operator("dlm.migrator_retarget_relations", text="RetargRelatives", icon="ORIENTATION_PARENT")
|
row.operator("dlm.migrator_retarget_relations", text="RetargRelatives", icon="ORIENTATION_PARENT")
|
||||||
row.operator("dlm.migrator_basebody_shapekeys", text="MigBBodyShapeKeys", icon="SHAPEKEY_DATA")
|
row.operator("dlm.migrator_basebody_shapekeys", text="MigBBodyShapeKeys", icon="SHAPEKEY_DATA")
|
||||||
row = box.row()
|
row = box.row(align=True)
|
||||||
row.operator("dlm.migrator_fk_rotations", text="MigFKRot", icon="BONE_DATA")
|
row.operator("dlm.migrator_fk_rotations", text="MigFKRot", icon="BONE_DATA")
|
||||||
|
row.operator("dlm.migrator_fk_rotations_bake", text="Bake", icon="KEYFRAME")
|
||||||
|
|
||||||
# Tweak Tools
|
# Tweak Tools
|
||||||
tweak_box = layout.box()
|
tweak_box = layout.box()
|
||||||
|
|||||||
Reference in New Issue
Block a user