588 lines
20 KiB
Python
588 lines
20 KiB
Python
"""
|
||
骨骼动画系统插件 - 高级功能扩展
|
||
提供动画状态机、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("高级动画功能模块加载完成") |