2026-02-16
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Utility modules for Remove Static FCurves extension
|
||||
"""
|
||||
|
||||
# Handle module reloading
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
if "compat" in locals():
|
||||
importlib.reload(compat)
|
||||
if "version" in locals():
|
||||
importlib.reload(version)
|
||||
else:
|
||||
from . import compat
|
||||
from . import version
|
||||
|
||||
import bpy
|
||||
|
||||
__all__ = ["compat", "version"]
|
||||
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
API compatibility functions for handling differences between Blender versions.
|
||||
"""
|
||||
import bpy
|
||||
from bpy_extras import anim_utils
|
||||
from . import version
|
||||
|
||||
|
||||
def get_action_fcurves(action):
|
||||
"""
|
||||
Get the fcurves collection from an action, handling version differences.
|
||||
|
||||
In Blender 4.x: action.fcurves
|
||||
In Blender 5.0+: Uses channelbag.fcurves via action slots
|
||||
|
||||
Args:
|
||||
action: The action object
|
||||
|
||||
Returns:
|
||||
Collection of fcurves, or None if not available
|
||||
"""
|
||||
if version.is_version_at_least(5, 0, 0):
|
||||
# Blender 5.0+ uses channelbags and slots
|
||||
# Try to get fcurves from all slots in the action
|
||||
all_fcurves = []
|
||||
|
||||
# Iterate through all slots in the action
|
||||
if hasattr(action, 'slots') and action.slots:
|
||||
for slot in action.slots:
|
||||
try:
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(
|
||||
action, slot)
|
||||
if channelbag and hasattr(channelbag, 'fcurves'):
|
||||
all_fcurves.extend(channelbag.fcurves)
|
||||
except (AttributeError, RuntimeError):
|
||||
continue
|
||||
|
||||
return all_fcurves if all_fcurves else None
|
||||
else:
|
||||
# Blender 4.x uses direct fcurves attribute
|
||||
if hasattr(action, 'fcurves'):
|
||||
return action.fcurves
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_fcurves_from_object(obj):
|
||||
"""
|
||||
Get FCurves from an object's animation data, handling version differences.
|
||||
|
||||
Args:
|
||||
obj: The object with animation data
|
||||
|
||||
Returns:
|
||||
List of (action, fcurve, slot) tuples, or empty list
|
||||
"""
|
||||
if not obj.animation_data or not obj.animation_data.action:
|
||||
return []
|
||||
|
||||
action = obj.animation_data.action
|
||||
fcurves_list = []
|
||||
|
||||
if version.is_version_at_least(5, 0, 0):
|
||||
# Blender 5.0+: Get the channelbag for the object's current action slot
|
||||
if hasattr(obj.animation_data, 'action_slot') and obj.animation_data.action_slot:
|
||||
try:
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(
|
||||
action,
|
||||
obj.animation_data.action_slot
|
||||
)
|
||||
if channelbag and hasattr(channelbag, 'fcurves'):
|
||||
for fcurve in channelbag.fcurves:
|
||||
fcurves_list.append(
|
||||
(action, fcurve, obj.animation_data.action_slot))
|
||||
except (AttributeError, RuntimeError) as e:
|
||||
print(f"Error getting channelbag for {obj.name}: {e}")
|
||||
else:
|
||||
# Fallback: try all slots if no specific slot is set
|
||||
if hasattr(action, 'slots') and action.slots:
|
||||
for slot in action.slots:
|
||||
try:
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(
|
||||
action, slot)
|
||||
if channelbag and hasattr(channelbag, 'fcurves'):
|
||||
for fcurve in channelbag.fcurves:
|
||||
fcurves_list.append((action, fcurve, slot))
|
||||
except (AttributeError, RuntimeError):
|
||||
continue
|
||||
else:
|
||||
# Blender 4.x: Direct access to fcurves
|
||||
if hasattr(action, 'fcurves'):
|
||||
for fcurve in action.fcurves:
|
||||
fcurves_list.append((action, fcurve, None))
|
||||
|
||||
return fcurves_list
|
||||
|
||||
|
||||
def remove_action_fcurve(action, fcurve, slot=None):
|
||||
"""
|
||||
Remove an fcurve from an action, handling version differences.
|
||||
|
||||
Args:
|
||||
action: The action object
|
||||
fcurve: The fcurve to remove
|
||||
slot: (Blender 5.0+) The action slot containing the fcurve
|
||||
|
||||
Returns:
|
||||
bool: True if removal succeeded, False otherwise
|
||||
"""
|
||||
if version.is_version_at_least(5, 0, 0):
|
||||
# Blender 5.0+: Need to remove from the channelbag
|
||||
if slot is None:
|
||||
# Try to find which slot contains this fcurve
|
||||
if hasattr(action, 'slots'):
|
||||
for try_slot in action.slots:
|
||||
try:
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(
|
||||
action, try_slot)
|
||||
if channelbag and hasattr(channelbag, 'fcurves'):
|
||||
if fcurve in channelbag.fcurves:
|
||||
channelbag.fcurves.remove(fcurve)
|
||||
return True
|
||||
except (ValueError, AttributeError, RuntimeError):
|
||||
continue
|
||||
else:
|
||||
# We know which slot to use
|
||||
try:
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(
|
||||
action, slot)
|
||||
if channelbag and hasattr(channelbag, 'fcurves'):
|
||||
channelbag.fcurves.remove(fcurve)
|
||||
return True
|
||||
except (ValueError, AttributeError, RuntimeError) as e:
|
||||
print(f"Error removing fcurve: {e}")
|
||||
else:
|
||||
# Blender 4.x: Direct removal
|
||||
if hasattr(action, 'fcurves'):
|
||||
try:
|
||||
action.fcurves.remove(fcurve)
|
||||
return True
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
return False
|
||||
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
Version detection and comparison utilities for multi-version Blender support.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def get_blender_version():
|
||||
"""
|
||||
Returns the current Blender version as a tuple (major, minor, patch).
|
||||
|
||||
Returns:
|
||||
tuple: (major, minor, patch) version numbers
|
||||
"""
|
||||
return bpy.app.version
|
||||
|
||||
|
||||
def get_version_string():
|
||||
"""
|
||||
Returns the current Blender version as a string (e.g., "4.2.0").
|
||||
|
||||
Returns:
|
||||
str: Version string in format "major.minor.patch"
|
||||
"""
|
||||
version = get_blender_version()
|
||||
return f"{version[0]}.{version[1]}.{version[2]}"
|
||||
|
||||
|
||||
def is_version_at_least(major, minor=0, patch=0):
|
||||
"""
|
||||
Check if the current Blender version is at least the specified version.
|
||||
|
||||
Args:
|
||||
major (int): Major version number
|
||||
minor (int): Minor version number (default: 0)
|
||||
patch (int): Patch version number (default: 0)
|
||||
|
||||
Returns:
|
||||
bool: True if current version >= specified version
|
||||
"""
|
||||
current = get_blender_version()
|
||||
target = (major, minor, patch)
|
||||
|
||||
if current[0] != target[0]:
|
||||
return current[0] > target[0]
|
||||
if current[1] != target[1]:
|
||||
return current[1] > target[1]
|
||||
return current[2] >= target[2]
|
||||
|
||||
|
||||
def is_version_less_than(major, minor=0, patch=0):
|
||||
"""
|
||||
Check if the current Blender version is less than the specified version.
|
||||
|
||||
Args:
|
||||
major (int): Major version number
|
||||
minor (int): Minor version number (default: 0)
|
||||
patch (int): Patch version number (default: 0)
|
||||
|
||||
Returns:
|
||||
bool: True if current version < specified version
|
||||
"""
|
||||
return not is_version_at_least(major, minor, patch)
|
||||
|
||||
|
||||
def is_version_5_0():
|
||||
"""Check if running Blender 5.0 or later."""
|
||||
return is_version_at_least(5, 0, 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user