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">
|
||||
<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>
|
||||
</tool-use>
|
||||
@@ -14447,7 +14447,9 @@ Parameters:
|
||||
```json
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"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:
|
||||
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):
|
||||
"""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)
|
||||
candidates = set(rep_descendants)
|
||||
for ob in bpy.data.objects:
|
||||
|
||||
+32
-1
@@ -6,6 +6,7 @@
|
||||
import bpy
|
||||
import os
|
||||
from bpy.types import Operator
|
||||
from bpy.props import StringProperty, BoolProperty
|
||||
from bpy.props import StringProperty, IntProperty
|
||||
|
||||
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):
|
||||
bl_idname = "dlm.migrator_fk_rotations"
|
||||
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_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -429,6 +430,35 @@ class DLM_OT_migrator_fk_rotations(Operator):
|
||||
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 = (
|
||||
"dlm.migrator_copy_attributes",
|
||||
"dlm.migrator_migrate_nla",
|
||||
@@ -718,4 +748,5 @@ OPERATOR_CLASSES = [
|
||||
DLM_OT_tweak_remove_both,
|
||||
DLM_OT_tweak_bake_both,
|
||||
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_retarget_relations", text="RetargRelatives", icon="ORIENTATION_PARENT")
|
||||
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_bake", text="Bake", icon="KEYFRAME")
|
||||
|
||||
# Tweak Tools
|
||||
tweak_box = layout.box()
|
||||
|
||||
Reference in New Issue
Block a user