EG/plugins/user/skeletal_animation_system/advanced_features.py
2025-12-12 16:16:15 +08:00

588 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
骨骼动画系统插件 - 高级功能扩展
提供动画状态机、IK系统、动画曲线编辑等高级功能
"""
import math
from panda3d.core import *
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
class AnimationStateMachine:
"""
动画状态机
管理复杂的动画状态转换和混合
"""
def __init__(self, actor):
self.actor = actor
self.states = {} # 状态字典 {state_name: state_data}
self.current_state = None
self.transitions = {} # 状态转换 {from_state: {to_state: condition_func}}
self.state_callbacks = {} # 状态回调 {state_name: {event: callback_func}}
def add_state(self, state_name, animations, loop=True, play_rate=1.0, blend_time=0.3):
"""
添加动画状态
:param state_name: 状态名称
:param animations: 动画列表或单个动画名称
:param loop: 是否循环
:param play_rate: 播放速度
:param blend_time: 状态切换混合时间
"""
if isinstance(animations, str):
animations = [animations]
self.states[state_name] = {
'animations': animations,
'loop': loop,
'play_rate': play_rate,
'blend_time': blend_time,
'active_interval': None
}
print(f"添加动画状态: {state_name}")
def remove_state(self, state_name):
"""
移除动画状态
:param state_name: 状态名称
"""
if state_name in self.states:
# 如果是当前状态,先停止
if self.current_state == state_name:
self.stop_current_state()
del self.states[state_name]
print(f"移除动画状态: {state_name}")
def set_transition(self, from_state, to_state, condition_func=None):
"""
设置状态转换条件
:param from_state: 起始状态
:param to_state: 目标状态
:param condition_func: 转换条件函数返回True时执行转换
"""
if from_state not in self.transitions:
self.transitions[from_state] = {}
self.transitions[from_state][to_state] = condition_func
print(f"设置状态转换: {from_state} -> {to_state}")
def set_state_callback(self, state_name, event, callback_func):
"""
设置状态回调
:param state_name: 状态名称
:param event: 事件类型 ('enter', 'exit', 'update')
:param callback_func: 回调函数
"""
if state_name not in self.state_callbacks:
self.state_callbacks[state_name] = {}
self.state_callbacks[state_name][event] = callback_func
def enter_state(self, state_name):
"""
进入指定状态
:param state_name: 状态名称
"""
if state_name not in self.states:
print(f"错误: 状态 {state_name} 不存在")
return False
# 退出当前状态
if self.current_state:
self.exit_current_state()
# 进入新状态
state_data = self.states[state_name]
# 创建动画序列
intervals = []
for anim_name in state_data['animations']:
if anim_name in self.actor.getAnimNames():
interval = self.actor.actorInterval(
anim_name,
loop=state_data['loop'],
playRate=state_data['play_rate']
)
intervals.append(interval)
if intervals:
if len(intervals) == 1:
state_data['active_interval'] = intervals[0]
else:
state_data['active_interval'] = Sequence(*intervals)
# 启动动画
state_data['active_interval'].start()
self.current_state = state_name
# 调用进入回调
if state_name in self.state_callbacks and 'enter' in self.state_callbacks[state_name]:
self.state_callbacks[state_name]['enter']()
print(f"进入动画状态: {state_name}")
return True
def exit_current_state(self):
"""
退出当前状态
"""
if not self.current_state:
return
state_data = self.states[self.current_state]
# 停止当前动画
if state_data['active_interval']:
state_data['active_interval'].finish()
state_data['active_interval'] = None
# 调用退出回调
if self.current_state in self.state_callbacks and 'exit' in self.state_callbacks[self.current_state]:
self.state_callbacks[self.current_state]['exit']()
print(f"退出动画状态: {self.current_state}")
self.current_state = None
def stop_current_state(self):
"""
停止当前状态
"""
if not self.current_state:
return
state_data = self.states[self.current_state]
# 停止当前动画
if state_data['active_interval']:
state_data['active_interval'].finish()
state_data['active_interval'] = None
def update(self, dt):
"""
更新状态机
:param dt: 时间增量
"""
if not self.current_state:
return
# 调用更新回调
if self.current_state in self.state_callbacks and 'update' in self.state_callbacks[self.current_state]:
self.state_callbacks[self.current_state]['update'](dt)
# 检查状态转换
if self.current_state in self.transitions:
for to_state, condition_func in self.transitions[self.current_state].items():
if condition_func is None or condition_func():
self.enter_state(to_state)
break
def get_current_state(self):
"""
获取当前状态
:return: 当前状态名称
"""
return self.current_state
def get_available_states(self):
"""
获取所有可用状态
:return: 状态名称列表
"""
return list(self.states.keys())
class InverseKinematicsSystem:
"""
逆向运动学系统
实现骨骼的IK控制
"""
def __init__(self, actor):
self.actor = actor
self.ik_chains = {} # IK链 {chain_name: chain_data}
self.targets = {} # IK目标 {target_name: target_node}
def add_ik_chain(self, chain_name, end_effector, pole_vector=None, iterations=10):
"""
添加IK链
:param chain_name: 链名称
:param end_effector: 终端效应器(骨骼名称)
:param pole_vector: 极向量(用于控制弯曲方向)
:param iterations: 迭代次数
"""
# 查找骨骼
effector_joint = self.actor.exposeJoint(None, "modelRoot", end_effector)
if not effector_joint:
print(f"错误: 未找到骨骼 {end_effector}")
return
self.ik_chains[chain_name] = {
'end_effector': end_effector,
'effector_joint': effector_joint,
'pole_vector': pole_vector,
'iterations': iterations,
'enabled': True
}
print(f"添加IK链: {chain_name} -> {end_effector}")
def remove_ik_chain(self, chain_name):
"""
移除IK链
:param chain_name: 链名称
"""
if chain_name in self.ik_chains:
del self.ik_chains[chain_name]
print(f"移除IK链: {chain_name}")
def set_ik_target(self, chain_name, target_pos):
"""
设置IK目标位置
:param chain_name: 链名称
:param target_pos: 目标位置 (x, y, z)
"""
if chain_name not in self.ik_chains:
print(f"错误: IK链 {chain_name} 不存在")
return
chain_data = self.ik_chains[chain_name]
if not chain_data['enabled']:
return
# 简化的IK实现CCD算法
effector_name = chain_data['end_effector']
iterations = chain_data['iterations']
# 获取当前终端效应器位置
effector_joint = chain_data['effector_joint']
current_pos = effector_joint.getPos(self.actor)
target_pos = Point3(*target_pos)
# 计算距离
distance = (target_pos - current_pos).length()
if distance < 0.01: # 阈值
return
# CCD算法迭代
for _ in range(iterations):
# 从终端向根部迭代调整骨骼
# 这里简化处理,实际应该遍历整条骨骼链
# 为了示例,我们只调整终端效应器朝向
direction = target_pos - current_pos
direction.normalize()
# 简化的朝向调整
effector_joint.lookAt(target_pos)
# 更新位置
current_pos = effector_joint.getPos(self.actor)
distance = (target_pos - current_pos).length()
if distance < 0.01:
break
def enable_ik_chain(self, chain_name, enabled=True):
"""
启用/禁用IK链
:param chain_name: 链名称
:param enabled: 是否启用
"""
if chain_name in self.ik_chains:
self.ik_chains[chain_name]['enabled'] = enabled
status = "启用" if enabled else "禁用"
print(f"{status}IK链: {chain_name}")
def batch_set_targets(self, targets_dict):
"""
批量设置IK目标
:param targets_dict: {chain_name: target_pos}
"""
for chain_name, target_pos in targets_dict.items():
self.set_ik_target(chain_name, target_pos)
class AnimationCurveEditor:
"""
动画曲线编辑器
支持对动画属性进行曲线编辑和自定义
"""
def __init__(self, actor):
self.actor = actor
self.curves = {} # 曲线字典 {curve_name: curve_data}
self.curve_targets = {} # 曲线目标 {target_name: target_data}
def create_curve(self, curve_name, keyframes, interpolation='linear'):
"""
创建动画曲线
:param curve_name: 曲线名称
:param keyframes: 关键帧列表 [(time, value), ...]
:param interpolation: 插值方式 ('linear', 'smooth', 'step')
"""
self.curves[curve_name] = {
'keyframes': sorted(keyframes, key=lambda x: x[0]),
'interpolation': interpolation,
'current_value': 0.0
}
print(f"创建动画曲线: {curve_name}")
def remove_curve(self, curve_name):
"""
移除动画曲线
:param curve_name: 曲线名称
"""
if curve_name in self.curves:
del self.curves[curve_name]
print(f"移除动画曲线: {curve_name}")
def add_curve_target(self, target_name, curve_name, property_path, transform_type='scale'):
"""
添加曲线目标
:param target_name: 目标名称
:param curve_name: 曲线名称
:param property_path: 属性路径(如骨骼名称)
:param transform_type: 变换类型 ('scale', 'position', 'rotation')
"""
if curve_name not in self.curves:
print(f"错误: 曲线 {curve_name} 不存在")
return
self.curve_targets[target_name] = {
'curve_name': curve_name,
'property_path': property_path,
'transform_type': transform_type
}
print(f"添加曲线目标: {target_name} -> {curve_name}")
def evaluate_curve(self, curve_name, time):
"""
计算曲线在指定时间的值
:param curve_name: 曲线名称
:param time: 时间
:return: 曲线值
"""
if curve_name not in self.curves:
return 0.0
curve_data = self.curves[curve_name]
keyframes = curve_data['keyframes']
if not keyframes:
return 0.0
# 边界情况
if time <= keyframes[0][0]:
return keyframes[0][1]
if time >= keyframes[-1][0]:
return keyframes[-1][1]
# 查找相邻关键帧
for i in range(len(keyframes) - 1):
if keyframes[i][0] <= time <= keyframes[i + 1][0]:
t1, v1 = keyframes[i]
t2, v2 = keyframes[i + 1]
# 根据插值方式计算值
if curve_data['interpolation'] == 'step':
return v1
elif curve_data['interpolation'] == 'smooth':
# 平滑插值(三次 Hermite
t = (time - t1) / (t2 - t1)
t2_sq = t * t
t3_sq = t2_sq * t
h00 = 2 * t3_sq - 3 * t2_sq + 1
h10 = t3_sq - 2 * t2_sq + t
h01 = -2 * t3_sq + 3 * t2_sq
h11 = t3_sq - t2_sq
return h00 * v1 + h10 * (t2 - t1) + h01 * v2 + h11 * (t2 - t1)
else: # linear
t = (time - t1) / (t2 - t1)
return v1 + (v2 - v1) * t
return 0.0
def update_curves(self, time):
"""
更新所有曲线
:param time: 当前时间
"""
for target_name, target_data in self.curve_targets.items():
curve_name = target_data['curve_name']
property_path = target_data['property_path']
transform_type = target_data['transform_type']
# 计算曲线值
value = self.evaluate_curve(curve_name, time)
# 应用到目标
self.apply_curve_value(property_path, transform_type, value)
# 更新曲线数据
if curve_name in self.curves:
self.curves[curve_name]['current_value'] = value
def apply_curve_value(self, property_path, transform_type, value):
"""
应用曲线值到目标属性
:param property_path: 属性路径
:param transform_type: 变换类型
:param value: 值
"""
# 查找目标骨骼
joint = self.actor.exposeJoint(None, "modelRoot", property_path)
if not joint:
return
# 根据变换类型应用值
if transform_type == 'scale':
joint.setScale(value, value, value)
elif transform_type == 'position':
current_pos = joint.getPos()
if property_path.endswith('.x'):
joint.setPos(value, current_pos.y, current_pos.z)
elif property_path.endswith('.y'):
joint.setPos(current_pos.x, value, current_pos.z)
elif property_path.endswith('.z'):
joint.setPos(current_pos.x, current_pos.y, value)
elif transform_type == 'rotation':
# 简化处理,实际应该分解为具体的旋转轴
joint.setHpr(value, value, value)
class AdvancedAnimationManager:
"""
高级动画管理器
集成状态机、IK系统和曲线编辑器
"""
def __init__(self, actor):
self.actor = actor
self.state_machine = AnimationStateMachine(actor)
self.ik_system = InverseKinematicsSystem(actor)
self.curve_editor = AnimationCurveEditor(actor)
self.enabled_systems = {
'state_machine': True,
'ik_system': True,
'curve_editor': True
}
def update(self, dt, time):
"""
更新所有高级动画系统
:param dt: 时间增量
:param time: 当前时间
"""
if self.enabled_systems['state_machine']:
self.state_machine.update(dt)
if self.enabled_systems['curve_editor']:
self.curve_editor.update_curves(time)
def enable_system(self, system_name, enabled=True):
"""
启用/禁用子系统
:param system_name: 系统名称 ('state_machine', 'ik_system', 'curve_editor')
:param enabled: 是否启用
"""
if system_name in self.enabled_systems:
self.enabled_systems[system_name] = enabled
status = "启用" if enabled else "禁用"
print(f"{status}动画子系统: {system_name}")
# 使用示例和测试代码
def example_state_machine_usage(actor):
"""
状态机使用示例
"""
print("=== 动画状态机使用示例 ===")
# 创建状态机
state_machine = AnimationStateMachine(actor)
# 添加状态
state_machine.add_state("idle", "idle_anim", loop=True)
state_machine.add_state("walk", "walk_anim", loop=True)
state_machine.add_state("run", "run_anim", loop=True)
state_machine.add_state("jump", ["jump_start", "jump_loop", "jump_end"], loop=False)
# 设置状态转换
state_machine.set_transition("idle", "walk")
state_machine.set_transition("walk", "run")
state_machine.set_transition("run", "idle")
# 进入初始状态
state_machine.enter_state("idle")
print("状态机示例设置完成")
return state_machine
def example_ik_system_usage(actor):
"""
IK系统使用示例
"""
print("=== 逆向运动学系统使用示例 ===")
# 创建IK系统
ik_system = InverseKinematicsSystem(actor)
# 添加IK链
ik_system.add_ik_chain("right_arm", "right_hand")
ik_system.add_ik_chain("left_arm", "left_hand")
ik_system.add_ik_chain("right_leg", "right_foot")
ik_system.add_ik_chain("left_leg", "left_foot")
# 设置IK目标
ik_system.set_ik_target("right_arm", (1, 0, 1))
ik_system.set_ik_target("left_arm", (-1, 0, 1))
print("IK系统示例设置完成")
return ik_system
def example_curve_editor_usage(actor):
"""
曲线编辑器使用示例
"""
print("=== 动画曲线编辑器使用示例 ===")
# 创建曲线编辑器
curve_editor = AnimationCurveEditor(actor)
# 创建曲线
scale_curve_frames = [(0, 1.0), (1, 1.5), (2, 1.0), (3, 0.8), (4, 1.0)]
curve_editor.create_curve("bounce_scale", scale_curve_frames, 'smooth')
position_curve_frames = [(0, 0), (1, 0.5), (2, 0), (3, -0.3), (4, 0)]
curve_editor.create_curve("bounce_position", position_curve_frames, 'smooth')
# 添加曲线目标
curve_editor.add_curve_target("character_scale", "bounce_scale", "character", "scale")
curve_editor.add_curve_target("character_height", "bounce_position", "character", "position")
print("曲线编辑器示例设置完成")
return curve_editor
def run_advanced_examples(actor):
"""
运行所有高级功能示例
"""
print("开始运行高级动画功能示例...")
state_machine = example_state_machine_usage(actor)
ik_system = example_ik_system_usage(actor)
curve_editor = example_curve_editor_usage(actor)
print("所有高级功能示例运行完成!")
return state_machine, ik_system, curve_editor
if __name__ == "__main__":
# 这里可以添加更多测试代码
print("高级动画功能模块加载完成")