1224 lines
45 KiB
Python
1224 lines
45 KiB
Python
"""
|
||
骨骼动画系统插件
|
||
提供完整的骨骼动画支持,包括加载、播放、控制、混合等高级功能
|
||
"""
|
||
|
||
from plugins.plugin_manager import BasePlugin
|
||
from direct.actor.Actor import Actor
|
||
from direct.interval.IntervalGlobal import *
|
||
from panda3d.core import Filename, ConfigVariableBool
|
||
from panda3d.core import NodePath, Vec3, Point3
|
||
import os
|
||
import json
|
||
import math
|
||
|
||
# 导入高级功能模块
|
||
from .advanced_features import AdvancedAnimationManager
|
||
from .animation_optimization import AnimationCompression, AnimationCaching, AnimationLOD
|
||
from .network_sync import DistributedAnimationManager, AnimationStateSerializer
|
||
|
||
|
||
class SkeletalAnimationManager:
|
||
"""
|
||
骨骼动画管理器类
|
||
负责管理场景中所有的骨骼动画Actor
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
self.world = world
|
||
self.actors = {} # 存储所有Actor {actor_id: actor_object}
|
||
self.actor_configs = {} # 存储Actor配置 {actor_id: config_dict}
|
||
self.active_animations = {} # 存储活动动画 {actor_id: {anim_name: control_object}}
|
||
self.animation_intervals = {} # 存储动画间隔 {actor_id: {interval_name: interval_object}}
|
||
self.blend_intervals = {} # 存储混合动画 {actor_id: {blend_name: blend_object}}
|
||
|
||
# 高级功能管理器
|
||
self.advanced_managers = {} # {actor_id: AdvancedAnimationManager}
|
||
self.compression_system = AnimationCompression()
|
||
self.caching_system = AnimationCaching()
|
||
self.lod_system = AnimationLOD(world)
|
||
self.distributed_manager = DistributedAnimationManager(world)
|
||
self.serializer = AnimationStateSerializer()
|
||
|
||
def load_actor(self, model_path, anim_dict=None, actor_id=None, position=None, scale=None):
|
||
"""
|
||
加载Actor模型
|
||
:param model_path: 模型文件路径
|
||
:param anim_dict: 动画字典,格式 {'anim_name': 'anim_path'}
|
||
:param actor_id: Actor标识符,如果为None则自动生成
|
||
:param position: 位置坐标 (x, y, z)
|
||
:param scale: 缩放比例 (x, y, z) 或单个数值
|
||
:return: Actor对象和actor_id
|
||
"""
|
||
try:
|
||
if not os.path.exists(model_path):
|
||
print(f"错误: 模型文件不存在: {model_path}")
|
||
return None, None
|
||
|
||
if actor_id is None:
|
||
actor_id = f"actor_{len(self.actors)}"
|
||
|
||
# 检查缓存
|
||
cached_actor = self.caching_system.get_cached_animation(actor_id, "model")
|
||
if cached_actor:
|
||
actor = cached_actor
|
||
else:
|
||
# 创建Actor
|
||
if anim_dict:
|
||
# 验证动画文件存在性
|
||
validated_anims = {}
|
||
for anim_name, anim_path in anim_dict.items():
|
||
if os.path.exists(anim_path):
|
||
validated_anims[anim_name] = anim_path
|
||
else:
|
||
print(f"警告: 动画文件不存在: {anim_path}")
|
||
|
||
actor = Actor(model_path, validated_anims)
|
||
else:
|
||
actor = Actor(model_path)
|
||
|
||
# 缓存Actor
|
||
self.caching_system.cache_animation(actor_id, "model", actor)
|
||
|
||
# 设置位置和缩放
|
||
if position:
|
||
actor.setPos(*position)
|
||
if scale:
|
||
if isinstance(scale, (int, float)):
|
||
actor.setScale(scale)
|
||
else:
|
||
actor.setScale(*scale)
|
||
|
||
# 将Actor添加到场景中
|
||
actor.reparentTo(self.world.render)
|
||
actor.setName(actor_id)
|
||
|
||
# 存储Actor和配置
|
||
self.actors[actor_id] = actor
|
||
self.actor_configs[actor_id] = {
|
||
'model_path': model_path,
|
||
'anim_dict': anim_dict,
|
||
'position': position,
|
||
'scale': scale
|
||
}
|
||
|
||
# 创建高级动画管理器
|
||
self.advanced_managers[actor_id] = AdvancedAnimationManager(actor)
|
||
|
||
print(f"成功加载Actor: {actor_id}")
|
||
print(f" 模型: {model_path}")
|
||
print(f" 位置: {position}")
|
||
print(f" 缩放: {scale}")
|
||
print(f" 可用动画: {actor.getAnimNames()}")
|
||
|
||
return actor, actor_id
|
||
|
||
except Exception as e:
|
||
print(f"加载Actor失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None, None
|
||
|
||
def unload_actor(self, actor_id):
|
||
"""
|
||
卸载Actor
|
||
:param actor_id: 要卸载的Actor标识符
|
||
"""
|
||
if actor_id in self.actors:
|
||
# 停止所有活动动画
|
||
self.stop_all_animations(actor_id)
|
||
|
||
# 清理混合动画
|
||
if actor_id in self.blend_intervals:
|
||
for blend in self.blend_intervals[actor_id].values():
|
||
if blend:
|
||
blend.finish()
|
||
del self.blend_intervals[actor_id]
|
||
|
||
# 清理动画间隔
|
||
if actor_id in self.animation_intervals:
|
||
for interval in self.animation_intervals[actor_id].values():
|
||
if interval:
|
||
interval.finish()
|
||
del self.animation_intervals[actor_id]
|
||
|
||
# 清理高级动画管理器
|
||
if actor_id in self.advanced_managers:
|
||
del self.advanced_managers[actor_id]
|
||
|
||
# 清理Actor
|
||
actor = self.actors[actor_id]
|
||
actor.cleanup()
|
||
del self.actors[actor_id]
|
||
|
||
# 清理配置
|
||
if actor_id in self.actor_configs:
|
||
del self.actor_configs[actor_id]
|
||
|
||
# 清理活动动画记录
|
||
if actor_id in self.active_animations:
|
||
del self.active_animations[actor_id]
|
||
|
||
# 使缓存失效
|
||
self.caching_system.invalidate_cache(actor_id)
|
||
|
||
print(f"已卸载Actor: {actor_id}")
|
||
|
||
def get_actor(self, actor_id):
|
||
"""
|
||
获取Actor对象
|
||
:param actor_id: Actor标识符
|
||
:return: Actor对象或None
|
||
"""
|
||
return self.actors.get(actor_id)
|
||
|
||
def get_actor_animations(self, actor_id):
|
||
"""
|
||
获取Actor的所有动画名称
|
||
:param actor_id: Actor标识符
|
||
:return: 动画名称列表
|
||
"""
|
||
if actor_id in self.actors:
|
||
return self.actors[actor_id].getAnimNames()
|
||
return []
|
||
|
||
def play_animation(self, actor_id, anim_name, loop=False, play_rate=1.0, start_frame=None, end_frame=None):
|
||
"""
|
||
播放动画
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称
|
||
:param loop: 是否循环播放
|
||
:param play_rate: 播放速度
|
||
:param start_frame: 起始帧
|
||
:param end_frame: 结束帧
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return False
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
# 检查动画是否存在
|
||
if anim_name not in actor.getAnimNames():
|
||
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
print(f" 可用动画: {actor.getAnimNames()}")
|
||
return False
|
||
|
||
try:
|
||
# 设置播放速度
|
||
actor.setPlayRate(play_rate, anim_name)
|
||
|
||
# 播放动画
|
||
if loop:
|
||
if start_frame is not None and end_frame is not None:
|
||
actor.loop(anim_name, restart=False, fromFrame=start_frame, toFrame=end_frame)
|
||
else:
|
||
actor.loop(anim_name, restart=False)
|
||
else:
|
||
if start_frame is not None and end_frame is not None:
|
||
actor.play(anim_name, fromFrame=start_frame, toFrame=end_frame)
|
||
else:
|
||
actor.play(anim_name)
|
||
|
||
# 记录活动动画
|
||
if actor_id not in self.active_animations:
|
||
self.active_animations[actor_id] = {}
|
||
self.active_animations[actor_id][anim_name] = actor.getAnimControl(anim_name)
|
||
|
||
print(f"播放动画: {anim_name} (Actor: {actor_id}, 循环: {loop}, 速度: {play_rate}x)")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"播放动画失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def stop_animation(self, actor_id, anim_name=None):
|
||
"""
|
||
停止动画
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称,如果为None则停止所有动画
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
try:
|
||
if anim_name:
|
||
# 停止指定动画
|
||
if anim_name in actor.getAnimNames():
|
||
actor.stop(anim_name)
|
||
# 从活动动画中移除
|
||
if actor_id in self.active_animations and anim_name in self.active_animations[actor_id]:
|
||
del self.active_animations[actor_id][anim_name]
|
||
print(f"停止动画: {anim_name} (Actor: {actor_id})")
|
||
else:
|
||
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
else:
|
||
# 停止所有动画
|
||
actor.stop()
|
||
# 清空活动动画记录
|
||
if actor_id in self.active_animations:
|
||
self.active_animations[actor_id].clear()
|
||
print(f"停止所有动画 (Actor: {actor_id})")
|
||
|
||
except Exception as e:
|
||
print(f"停止动画失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def stop_all_animations(self, actor_id):
|
||
"""
|
||
停止指定Actor的所有动画
|
||
:param actor_id: Actor标识符
|
||
"""
|
||
self.stop_animation(actor_id)
|
||
|
||
def set_animation_speed(self, actor_id, anim_name, speed):
|
||
"""
|
||
设置动画播放速度
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称
|
||
:param speed: 播放速度
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
try:
|
||
if anim_name in actor.getAnimNames():
|
||
actor.setPlayRate(speed, anim_name)
|
||
print(f"设置动画速度: {anim_name} 为 {speed}x (Actor: {actor_id})")
|
||
else:
|
||
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
|
||
except Exception as e:
|
||
print(f"设置动画速度失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def pause_animation(self, actor_id, anim_name=None):
|
||
"""
|
||
暂停动画
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称,如果为None则暂停所有动画
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
try:
|
||
if anim_name:
|
||
if anim_name in actor.getAnimNames():
|
||
control = actor.getAnimControl(anim_name)
|
||
if control and control.isPlaying():
|
||
control.setPlayRate(0) # 通过设置播放速度为0来模拟暂停
|
||
print(f"暂停动画: {anim_name} (Actor: {actor_id})")
|
||
else:
|
||
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
else:
|
||
# 暂停所有活动动画
|
||
if actor_id in self.active_animations:
|
||
for anim_name, control in self.active_animations[actor_id].items():
|
||
if control and control.isPlaying():
|
||
control.setPlayRate(0)
|
||
print(f"暂停所有动画 (Actor: {actor_id})")
|
||
|
||
except Exception as e:
|
||
print(f"暂停动画失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def resume_animation(self, actor_id, anim_name=None):
|
||
"""
|
||
恢复动画播放
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称,如果为None则恢复所有动画
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
try:
|
||
if anim_name:
|
||
if anim_name in actor.getAnimNames():
|
||
control = actor.getAnimControl(anim_name)
|
||
if control and control.getPlayRate() == 0:
|
||
control.setPlayRate(1.0) # 恢复正常播放速度
|
||
print(f"恢复动画: {anim_name} (Actor: {actor_id})")
|
||
else:
|
||
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
else:
|
||
# 恢复所有暂停的动画
|
||
if actor_id in self.active_animations:
|
||
for anim_name, control in self.active_animations[actor_id].items():
|
||
if control and control.getPlayRate() == 0:
|
||
control.setPlayRate(1.0)
|
||
print(f"恢复所有动画 (Actor: {actor_id})")
|
||
|
||
except Exception as e:
|
||
print(f"恢复动画失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def get_animation_info(self, actor_id, anim_name):
|
||
"""
|
||
获取动画信息
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称
|
||
:return: 动画信息字典
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return None
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
if anim_name not in actor.getAnimNames():
|
||
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
return None
|
||
|
||
try:
|
||
control = actor.getAnimControl(anim_name)
|
||
info = {
|
||
'name': anim_name,
|
||
'frame_rate': actor.getFrameRate(anim_name),
|
||
'num_frames': actor.getNumFrames(anim_name),
|
||
'play_rate': actor.getPlayRate(anim_name),
|
||
'is_playing': control.isPlaying() if control else False,
|
||
'is_paused': control.getPlayRate() == 0 if control else False,
|
||
'current_frame': actor.getCurrentFrame(anim_name) if control else 0
|
||
}
|
||
return info
|
||
|
||
except Exception as e:
|
||
print(f"获取动画信息失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def blend_animations(self, actor_id, from_anim, to_anim, blend_time=1.0, loop=True):
|
||
"""
|
||
混合两个动画
|
||
:param actor_id: Actor标识符
|
||
:param from_anim: 起始动画名称
|
||
:param to_anim: 目标动画名称
|
||
:param blend_time: 混合时间
|
||
:param loop: 是否循环混合
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return None
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
# 检查动画是否存在
|
||
if from_anim not in actor.getAnimNames():
|
||
print(f"错误: 起始动画 {from_anim} 不存在于Actor {actor_id} 中")
|
||
return None
|
||
|
||
if to_anim not in actor.getAnimNames():
|
||
print(f"错误: 目标动画 {to_anim} 不存在于Actor {actor_id} 中")
|
||
return None
|
||
|
||
try:
|
||
# 创建动画混合
|
||
blend_name = f"{from_anim}_to_{to_anim}"
|
||
blend = actor.actorInterval(from_anim, endTime=actor.getDuration(from_anim)) \
|
||
+ actor.actorInterval(to_anim, loop=loop)
|
||
|
||
# 存储混合动画
|
||
if actor_id not in self.blend_intervals:
|
||
self.blend_intervals[actor_id] = {}
|
||
self.blend_intervals[actor_id][blend_name] = blend
|
||
|
||
# 播放混合动画
|
||
blend.start()
|
||
|
||
print(f"开始动画混合: {from_anim} -> {to_anim} (Actor: {actor_id}, 时间: {blend_time}s)")
|
||
return blend
|
||
|
||
except Exception as e:
|
||
print(f"动画混合失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def stop_blend(self, actor_id, blend_name=None):
|
||
"""
|
||
停止动画混合
|
||
:param actor_id: Actor标识符
|
||
:param blend_name: 混合动画名称,如果为None则停止所有混合
|
||
"""
|
||
if actor_id not in self.blend_intervals:
|
||
return
|
||
|
||
try:
|
||
if blend_name:
|
||
if blend_name in self.blend_intervals[actor_id]:
|
||
blend = self.blend_intervals[actor_id][blend_name]
|
||
if blend:
|
||
blend.finish()
|
||
del self.blend_intervals[actor_id][blend_name]
|
||
print(f"停止动画混合: {blend_name} (Actor: {actor_id})")
|
||
else:
|
||
# 停止所有混合
|
||
for blend in self.blend_intervals[actor_id].values():
|
||
if blend:
|
||
blend.finish()
|
||
self.blend_intervals[actor_id].clear()
|
||
print(f"停止所有动画混合 (Actor: {actor_id})")
|
||
|
||
except Exception as e:
|
||
print(f"停止动画混合失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def create_animation_sequence(self, actor_id, anim_list, loop=False):
|
||
"""
|
||
创建动画序列
|
||
:param actor_id: Actor标识符
|
||
:param anim_list: 动画名称列表
|
||
:param loop: 是否循环播放序列
|
||
:return: 序列间隔对象
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return None
|
||
|
||
actor = self.actors[actor_id]
|
||
|
||
try:
|
||
# 验证动画存在性
|
||
for anim_name in anim_list:
|
||
if anim_name not in actor.getAnimNames():
|
||
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id} 中")
|
||
return None
|
||
|
||
# 创建动画序列
|
||
sequence = Sequence()
|
||
for anim_name in anim_list:
|
||
interval = actor.actorInterval(anim_name)
|
||
sequence.append(interval)
|
||
|
||
# 存储序列
|
||
sequence_name = f"seq_{'_'.join(anim_list)}"
|
||
if actor_id not in self.animation_intervals:
|
||
self.animation_intervals[actor_id] = {}
|
||
self.animation_intervals[actor_id][sequence_name] = sequence
|
||
|
||
# 设置循环
|
||
if loop:
|
||
sequence.loop()
|
||
else:
|
||
sequence.start()
|
||
|
||
print(f"创建动画序列: {sequence_name} (Actor: {actor_id})")
|
||
return sequence
|
||
|
||
except Exception as e:
|
||
print(f"创建动画序列失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def stop_animation_sequence(self, actor_id, sequence_name=None):
|
||
"""
|
||
停止动画序列
|
||
:param actor_id: Actor标识符
|
||
:param sequence_name: 序列名称,如果为None则停止所有序列
|
||
"""
|
||
if actor_id not in self.animation_intervals:
|
||
return
|
||
|
||
try:
|
||
if sequence_name:
|
||
if sequence_name in self.animation_intervals[actor_id]:
|
||
sequence = self.animation_intervals[actor_id][sequence_name]
|
||
if sequence:
|
||
sequence.finish()
|
||
del self.animation_intervals[actor_id][sequence_name]
|
||
print(f"停止动画序列: {sequence_name} (Actor: {actor_id})")
|
||
else:
|
||
# 停止所有序列
|
||
for sequence in self.animation_intervals[actor_id].values():
|
||
if sequence:
|
||
sequence.finish()
|
||
self.animation_intervals[actor_id].clear()
|
||
print(f"停止所有动画序列 (Actor: {actor_id})")
|
||
|
||
except Exception as e:
|
||
print(f"停止动画序列失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def get_actor_info(self, actor_id):
|
||
"""
|
||
获取Actor详细信息
|
||
:param actor_id: Actor标识符
|
||
:return: Actor信息字典
|
||
"""
|
||
if actor_id not in self.actors:
|
||
print(f"错误: Actor {actor_id} 不存在")
|
||
return None
|
||
|
||
actor = self.actors[actor_id]
|
||
config = self.actor_configs.get(actor_id, {})
|
||
|
||
try:
|
||
info = {
|
||
'id': actor_id,
|
||
'model_path': config.get('model_path'),
|
||
'position': actor.getPos().toTuple(),
|
||
'scale': actor.getScale().toTuple(),
|
||
'available_animations': actor.getAnimNames(),
|
||
'active_animations': list(self.active_animations.get(actor_id, {}).keys()),
|
||
'has_character': actor.hasCharacter(),
|
||
'num_parts': actor.getNumParts(),
|
||
'bounds': {
|
||
'min': actor.getTightBounds()[0].toTuple() if actor.getTightBounds() else None,
|
||
'max': actor.getTightBounds()[1].toTuple() if actor.getTightBounds() else None
|
||
}
|
||
}
|
||
return info
|
||
|
||
except Exception as e:
|
||
print(f"获取Actor信息失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def set_actor_position(self, actor_id, x, y, z):
|
||
"""
|
||
设置Actor位置
|
||
:param actor_id: Actor标识符
|
||
:param x: X坐标
|
||
:param y: Y坐标
|
||
:param z: Z坐标
|
||
"""
|
||
if actor_id in self.actors:
|
||
self.actors[actor_id].setPos(x, y, z)
|
||
# 更新配置
|
||
if actor_id in self.actor_configs:
|
||
self.actor_configs[actor_id]['position'] = (x, y, z)
|
||
print(f"设置Actor {actor_id} 位置: ({x}, {y}, {z})")
|
||
|
||
def set_actor_scale(self, actor_id, scale):
|
||
"""
|
||
设置Actor缩放
|
||
:param actor_id: Actor标识符
|
||
:param scale: 缩放值 (数值或(x, y, z)元组)
|
||
"""
|
||
if actor_id in self.actors:
|
||
if isinstance(scale, (int, float)):
|
||
self.actors[actor_id].setScale(scale)
|
||
else:
|
||
self.actors[actor_id].setScale(*scale)
|
||
# 更新配置
|
||
if actor_id in self.actor_configs:
|
||
self.actor_configs[actor_id]['scale'] = scale
|
||
print(f"设置Actor {actor_id} 缩放: {scale}")
|
||
|
||
def get_advanced_manager(self, actor_id):
|
||
"""
|
||
获取指定Actor的高级动画管理器
|
||
:param actor_id: Actor标识符
|
||
:return: AdvancedAnimationManager实例
|
||
"""
|
||
return self.advanced_managers.get(actor_id)
|
||
|
||
def compress_animation(self, actor_id, anim_name, output_path=None):
|
||
"""
|
||
压缩指定动画
|
||
:param actor_id: Actor标识符
|
||
:param anim_name: 动画名称
|
||
:param output_path: 输出路径
|
||
"""
|
||
if actor_id in self.actors:
|
||
actor = self.actors[actor_id]
|
||
return self.compression_system.compress_animation(actor, anim_name, output_path)
|
||
return None
|
||
|
||
def get_cache_stats(self):
|
||
"""
|
||
获取缓存统计信息
|
||
:return: 缓存统计
|
||
"""
|
||
return self.caching_system.get_cache_stats()
|
||
|
||
def update_lod(self, camera_pos):
|
||
"""
|
||
更新LOD系统
|
||
:param camera_pos: 摄像机位置
|
||
"""
|
||
self.lod_system.update_lod_levels(camera_pos)
|
||
|
||
def get_lod_stats(self):
|
||
"""
|
||
获取LOD统计信息
|
||
:return: LOD统计
|
||
"""
|
||
return self.lod_system.get_lod_stats()
|
||
|
||
def enable_distributed_mode(self, mode='server'):
|
||
"""
|
||
启用分布式模式
|
||
:param mode: 模式 ('server' 或 'client')
|
||
"""
|
||
self.distributed_manager.enable_distributed_mode(mode)
|
||
|
||
def create_distributed_actor(self, actor_id, model_path, anim_dict=None):
|
||
"""
|
||
创建分布式Actor
|
||
:param actor_id: Actor ID
|
||
:param model_path: 模型路径
|
||
:param anim_dict: 动画字典
|
||
:return: Actor对象
|
||
"""
|
||
return self.distributed_manager.create_distributed_actor(actor_id, model_path, anim_dict)
|
||
|
||
def serialize_actor_state(self, actor_id, file_path=None):
|
||
"""
|
||
序列化Actor状态
|
||
:param actor_id: Actor ID
|
||
:param file_path: 文件路径
|
||
"""
|
||
if actor_id in self.actors:
|
||
actor = self.actors[actor_id]
|
||
if file_path:
|
||
self.serializer.save_state_to_file(actor, file_path)
|
||
else:
|
||
return self.serializer.serialize_actor_state(actor)
|
||
return None
|
||
|
||
|
||
class AnimationEventManager:
|
||
"""
|
||
动画事件管理器
|
||
处理动画事件和回调
|
||
"""
|
||
|
||
def __init__(self, world, animation_manager):
|
||
self.world = world
|
||
self.animation_manager = animation_manager
|
||
self.event_callbacks = {} # 存储事件回调 {event_name: [callback_list]}
|
||
|
||
def register_event_callback(self, event_name, callback):
|
||
"""
|
||
注册事件回调
|
||
:param event_name: 事件名称
|
||
:param callback: 回调函数
|
||
"""
|
||
if event_name not in self.event_callbacks:
|
||
self.event_callbacks[event_name] = []
|
||
self.event_callbacks[event_name].append(callback)
|
||
print(f"注册事件回调: {event_name}")
|
||
|
||
def unregister_event_callback(self, event_name, callback):
|
||
"""
|
||
注销事件回调
|
||
:param event_name: 事件名称
|
||
:param callback: 回调函数
|
||
"""
|
||
if event_name in self.event_callbacks:
|
||
if callback in self.event_callbacks[event_name]:
|
||
self.event_callbacks[event_name].remove(callback)
|
||
print(f"注销事件回调: {event_name}")
|
||
|
||
def trigger_event(self, event_name, *args, **kwargs):
|
||
"""
|
||
触发事件
|
||
:param event_name: 事件名称
|
||
:param args: 位置参数
|
||
:param kwargs: 关键字参数
|
||
"""
|
||
if event_name in self.event_callbacks:
|
||
for callback in self.event_callbacks[event_name]:
|
||
try:
|
||
callback(*args, **kwargs)
|
||
except Exception as e:
|
||
print(f"执行事件回调失败 {event_name}: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def setup_default_events(self):
|
||
"""
|
||
设置默认事件处理器
|
||
"""
|
||
# 注册基本动画控制事件
|
||
self.world.accept("skeletal.load_actor", self._handle_load_actor)
|
||
self.world.accept("skeletal.play_animation", self._handle_play_animation)
|
||
self.world.accept("skeletal.stop_animation", self._handle_stop_animation)
|
||
self.world.accept("skeletal.set_animation_speed", self._handle_set_animation_speed)
|
||
self.world.accept("skeletal.pause_animation", self._handle_pause_animation)
|
||
self.world.accept("skeletal.resume_animation", self._handle_resume_animation)
|
||
print("已设置默认动画事件处理器")
|
||
|
||
def _handle_load_actor(self, model_path, anim_dict=None, actor_id=None, position=None, scale=None):
|
||
"""处理加载Actor事件"""
|
||
self.animation_manager.load_actor(model_path, anim_dict, actor_id, position, scale)
|
||
|
||
def _handle_play_animation(self, actor_id, anim_name, loop=False, play_rate=1.0):
|
||
"""处理播放动画事件"""
|
||
self.animation_manager.play_animation(actor_id, anim_name, loop, play_rate)
|
||
|
||
def _handle_stop_animation(self, actor_id, anim_name=None):
|
||
"""处理停止动画事件"""
|
||
self.animation_manager.stop_animation(actor_id, anim_name)
|
||
|
||
def _handle_set_animation_speed(self, actor_id, anim_name, speed):
|
||
"""处理设置动画速度事件"""
|
||
self.animation_manager.set_animation_speed(actor_id, anim_name, speed)
|
||
|
||
def _handle_pause_animation(self, actor_id, anim_name=None):
|
||
"""处理暂停动画事件"""
|
||
self.animation_manager.pause_animation(actor_id, anim_name)
|
||
|
||
def _handle_resume_animation(self, actor_id, anim_name=None):
|
||
"""处理恢复动画事件"""
|
||
self.animation_manager.resume_animation(actor_id, anim_name)
|
||
|
||
|
||
class SkeletalAnimationGUI:
|
||
"""
|
||
骨骼动画GUI界面
|
||
提供可视化控制界面
|
||
"""
|
||
|
||
def __init__(self, world, animation_manager, event_manager):
|
||
self.world = world
|
||
self.animation_manager = animation_manager
|
||
self.event_manager = event_manager
|
||
self.panel = None
|
||
self.actor_list_elements = {} # 存储Actor列表元素
|
||
self.animation_list_elements = {} # 存储动画列表元素
|
||
|
||
def create_control_panel(self):
|
||
"""
|
||
创建控制面板
|
||
"""
|
||
if not hasattr(self.world, 'gui_manager') or not self.world.gui_manager:
|
||
print("警告: GUI管理器不可用,无法创建控制面板")
|
||
return
|
||
|
||
try:
|
||
# 创建主面板
|
||
self.panel = self.world.gui_manager.createGUIPanel(
|
||
pos=(0.7, 0, 0.5),
|
||
size=(0.25, 0.4),
|
||
title="骨骼动画控制面板"
|
||
)
|
||
|
||
# 创建Actor控制区域
|
||
actor_label = self.world.gui_manager.createGUIText(
|
||
parent=self.panel,
|
||
text="Actor列表:",
|
||
pos=(0.02, 0, 0.35),
|
||
scale=0.04
|
||
)
|
||
|
||
# 创建Actor列表框
|
||
self.actor_listbox = self.world.gui_manager.createGUIScrollableFrame(
|
||
parent=self.panel,
|
||
pos=(0.02, 0, 0.15),
|
||
size=(0.22, 0.18)
|
||
)
|
||
|
||
# 创建动画控制区域
|
||
anim_label = self.world.gui_manager.createGUIText(
|
||
parent=self.panel,
|
||
text="动画控制:",
|
||
pos=(0.02, 0, 0.1),
|
||
scale=0.04
|
||
)
|
||
|
||
# 创建控制按钮
|
||
button_y = 0.02
|
||
button_spacing = 0.06
|
||
|
||
self.play_button = self.world.gui_manager.createGUIButton(
|
||
parent=self.panel,
|
||
pos=(0.03, 0, button_y),
|
||
size=0.05,
|
||
text="播放"
|
||
)
|
||
self.world.accept("skeletal_gui_play", self._on_play_clicked)
|
||
|
||
self.stop_button = self.world.gui_manager.createGUIButton(
|
||
parent=self.panel,
|
||
pos=(0.09, 0, button_y),
|
||
size=0.05,
|
||
text="停止"
|
||
)
|
||
self.world.accept("skeletal_gui_stop", self._on_stop_clicked)
|
||
|
||
self.pause_button = self.world.gui_manager.createGUIButton(
|
||
parent=self.panel,
|
||
pos=(0.15, 0, button_y),
|
||
size=0.05,
|
||
text="暂停"
|
||
)
|
||
self.world.accept("skeletal_gui_pause", self._on_pause_clicked)
|
||
|
||
# 创建速度控制
|
||
speed_label = self.world.gui_manager.createGUIText(
|
||
parent=self.panel,
|
||
text="速度:",
|
||
pos=(0.02, 0, button_y - button_spacing),
|
||
scale=0.03
|
||
)
|
||
|
||
self.speed_slider = self.world.gui_manager.createGUISlider(
|
||
parent=self.panel,
|
||
pos=(0.08, 0, button_y - button_spacing),
|
||
size=0.15,
|
||
min_value=0.0,
|
||
max_value=3.0,
|
||
initial_value=1.0
|
||
)
|
||
|
||
print("骨骼动画控制面板创建成功")
|
||
|
||
except Exception as e:
|
||
print(f"创建控制面板失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update_actor_list(self):
|
||
"""
|
||
更新Actor列表
|
||
"""
|
||
if not hasattr(self, 'actor_listbox') or not self.actor_listbox:
|
||
return
|
||
|
||
try:
|
||
# 清除现有元素
|
||
for element in self.actor_list_elements.values():
|
||
self.world.gui_manager.deleteGUIElement(element)
|
||
self.actor_list_elements.clear()
|
||
|
||
# 添加Actor列表项
|
||
actors = self.animation_manager.actors
|
||
y_offset = 0.15
|
||
y_spacing = 0.03
|
||
|
||
for i, (actor_id, actor) in enumerate(actors.items()):
|
||
# 创建Actor列表项
|
||
actor_button = self.world.gui_manager.createGUIButton(
|
||
parent=self.actor_listbox,
|
||
pos=(0.01, 0, y_offset - i * y_spacing),
|
||
size=0.04,
|
||
text=actor_id
|
||
)
|
||
|
||
# 注册点击事件
|
||
self.world.accept(f"skeletal_select_actor_{actor_id}",
|
||
lambda aid=actor_id: self._on_actor_selected(aid))
|
||
|
||
self.actor_list_elements[actor_id] = actor_button
|
||
|
||
except Exception as e:
|
||
print(f"更新Actor列表失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _on_actor_selected(self, actor_id):
|
||
"""
|
||
当Actor被选中时的处理函数
|
||
:param actor_id: 选中的Actor ID
|
||
"""
|
||
print(f"选中Actor: {actor_id}")
|
||
# 更新动画列表
|
||
self.update_animation_list(actor_id)
|
||
|
||
def update_animation_list(self, actor_id):
|
||
"""
|
||
更新动画列表
|
||
:param actor_id: Actor ID
|
||
"""
|
||
if not hasattr(self, 'actor_listbox') or not self.actor_listbox:
|
||
return
|
||
|
||
try:
|
||
# 清除现有元素
|
||
for element in self.animation_list_elements.values():
|
||
self.world.gui_manager.deleteGUIElement(element)
|
||
self.animation_list_elements.clear()
|
||
|
||
# 获取Actor动画列表
|
||
animations = self.animation_manager.get_actor_animations(actor_id)
|
||
y_offset = -0.05
|
||
y_spacing = 0.03
|
||
|
||
for i, anim_name in enumerate(animations):
|
||
# 创建动画列表项
|
||
anim_button = self.world.gui_manager.createGUIButton(
|
||
parent=self.actor_listbox,
|
||
pos=(0.01, 0, y_offset - i * y_spacing),
|
||
size=0.04,
|
||
text=anim_name
|
||
)
|
||
|
||
# 注册点击事件
|
||
self.world.accept(f"skeletal_select_anim_{actor_id}_{anim_name}",
|
||
lambda aid=actor_id, an=anim_name: self._on_animation_selected(aid, an))
|
||
|
||
self.animation_list_elements[f"{actor_id}_{anim_name}"] = anim_button
|
||
|
||
except Exception as e:
|
||
print(f"更新动画列表失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _on_animation_selected(self, actor_id, anim_name):
|
||
"""
|
||
当动画被选中时的处理函数
|
||
:param actor_id: Actor ID
|
||
:param anim_name: 动画名称
|
||
"""
|
||
print(f"选中动画: {anim_name} (Actor: {actor_id})")
|
||
|
||
def _on_play_clicked(self):
|
||
"""
|
||
当播放按钮被点击时的处理函数
|
||
"""
|
||
print("播放按钮被点击")
|
||
# 这里应该实现实际的播放逻辑
|
||
|
||
def _on_stop_clicked(self):
|
||
"""
|
||
当停止按钮被点击时的处理函数
|
||
"""
|
||
print("停止按钮被点击")
|
||
# 这里应该实现实际的停止逻辑
|
||
|
||
def _on_pause_clicked(self):
|
||
"""
|
||
当暂停按钮被点击时的处理函数
|
||
"""
|
||
print("暂停按钮被点击")
|
||
# 这里应该实现实际的暂停逻辑
|
||
|
||
def destroy(self):
|
||
"""
|
||
销毁GUI界面
|
||
"""
|
||
try:
|
||
if self.panel:
|
||
self.world.gui_manager.deleteGUIElement(self.panel)
|
||
self.panel = None
|
||
print("骨骼动画GUI界面已销毁")
|
||
except Exception as e:
|
||
print(f"销毁GUI界面失败: {e}")
|
||
|
||
|
||
class Plugin(BasePlugin):
|
||
"""
|
||
骨骼动画系统插件
|
||
提供完整的骨骼动画支持,包括加载、播放、控制、混合等高级功能
|
||
"""
|
||
|
||
def __init__(self, plugin_manager, name):
|
||
super().__init__(plugin_manager, name)
|
||
self.config = {
|
||
"version": "1.0.0",
|
||
"author": "EG Team",
|
||
"description": "骨骼动画系统,支持模型动画加载、播放控制、混合等高级功能"
|
||
}
|
||
self.animation_manager = None
|
||
self.event_manager = None
|
||
self.gui = None
|
||
self.is_enabled = False
|
||
self.update_task = None
|
||
|
||
def initialize(self) -> bool:
|
||
"""
|
||
初始化插件
|
||
"""
|
||
print(f"初始化骨骼动画插件: {self.name}")
|
||
|
||
try:
|
||
# 创建动画管理器
|
||
self.animation_manager = SkeletalAnimationManager(self.plugin_manager.world)
|
||
|
||
# 创建事件管理器
|
||
self.event_manager = AnimationEventManager(self.plugin_manager.world, self.animation_manager)
|
||
|
||
# 创建GUI
|
||
self.gui = SkeletalAnimationGUI(self.plugin_manager.world,
|
||
self.animation_manager,
|
||
self.event_manager)
|
||
|
||
print(f"骨骼动画插件初始化成功: {self.name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"骨骼动画插件初始化失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def enable(self) -> bool:
|
||
"""
|
||
启用插件
|
||
"""
|
||
if not super().enable():
|
||
return False
|
||
|
||
if self.is_enabled:
|
||
print(f"骨骼动画插件已启用: {self.name}")
|
||
return True
|
||
|
||
print(f"启用骨骼动画插件: {self.name}")
|
||
|
||
try:
|
||
# 设置默认事件处理器
|
||
self.event_manager.setup_default_events()
|
||
|
||
# 创建GUI控制面板
|
||
self.gui.create_control_panel()
|
||
|
||
# 启动更新任务
|
||
self.update_task = self.plugin_manager.world.taskMgr.add(
|
||
self._update_task,
|
||
f"skeletal_animation_update_{self.name}",
|
||
sort=30
|
||
)
|
||
|
||
# 标记为已启用
|
||
self.is_enabled = True
|
||
|
||
print(f"骨骼动画插件启用成功: {self.name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"骨骼动画插件启用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def disable(self) -> bool:
|
||
"""
|
||
禁用插件
|
||
"""
|
||
if not super().disable():
|
||
return False
|
||
|
||
if not self.is_enabled:
|
||
print(f"骨骼动画插件已禁用: {self.name}")
|
||
return True
|
||
|
||
print(f"禁用骨骼动画插件: {self.name}")
|
||
|
||
try:
|
||
# 停止更新任务
|
||
if self.update_task:
|
||
self.plugin_manager.world.taskMgr.remove(self.update_task)
|
||
self.update_task = None
|
||
|
||
# 移除事件处理器
|
||
self.plugin_manager.world.ignore("skeletal.load_actor")
|
||
self.plugin_manager.world.ignore("skeletal.play_animation")
|
||
self.plugin_manager.world.ignore("skeletal.stop_animation")
|
||
self.plugin_manager.world.ignore("skeletal.set_animation_speed")
|
||
self.plugin_manager.world.ignore("skeletal.pause_animation")
|
||
self.plugin_manager.world.ignore("skeletal.resume_animation")
|
||
|
||
# 销毁GUI
|
||
self.gui.destroy()
|
||
|
||
# 标记为已禁用
|
||
self.is_enabled = False
|
||
|
||
print(f"骨骼动画插件禁用成功: {self.name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"骨骼动画插件禁用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def finalize(self):
|
||
"""
|
||
清理插件资源
|
||
"""
|
||
print(f"清理骨骼动画插件资源: {self.name}")
|
||
|
||
try:
|
||
# 停止更新任务
|
||
if self.update_task:
|
||
self.plugin_manager.world.taskMgr.remove(self.update_task)
|
||
self.update_task = None
|
||
|
||
# 停止所有动画
|
||
for actor_id in list(self.animation_manager.actors.keys()):
|
||
self.animation_manager.unload_actor(actor_id)
|
||
|
||
# 清理事件处理器
|
||
self.event_manager = None
|
||
|
||
# 清理GUI
|
||
self.gui = None
|
||
|
||
# 清理动画管理器
|
||
self.animation_manager = None
|
||
|
||
print(f"骨骼动画插件资源清理完成: {self.name}")
|
||
|
||
except Exception as e:
|
||
print(f"清理骨骼动画插件资源失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _update_task(self, task):
|
||
"""
|
||
插件更新任务
|
||
"""
|
||
try:
|
||
# 获取摄像机位置用于LOD计算
|
||
if hasattr(self.plugin_manager.world, 'camera'):
|
||
camera_pos = self.plugin_manager.world.camera.getPos()
|
||
self.animation_manager.update_lod(camera_pos)
|
||
|
||
# 更新高级动画系统
|
||
dt = globalClock.getDt()
|
||
time = globalClock.getRealTime()
|
||
|
||
for actor_id, advanced_manager in self.animation_manager.advanced_managers.items():
|
||
advanced_manager.update(dt, time)
|
||
|
||
except Exception as e:
|
||
print(f"插件更新任务出错: {e}")
|
||
|
||
return task.cont
|
||
|
||
def get_animation_manager(self):
|
||
"""
|
||
获取动画管理器
|
||
:return: SkeletalAnimationManager实例
|
||
"""
|
||
return self.animation_manager
|
||
|
||
def get_event_manager(self):
|
||
"""
|
||
获取事件管理器
|
||
:return: AnimationEventManager实例
|
||
"""
|
||
return self.event_manager
|
||
|
||
def get_gui(self):
|
||
"""
|
||
获取GUI界面
|
||
:return: SkeletalAnimationGUI实例
|
||
"""
|
||
return self.gui |