763 lines
26 KiB
Python
763 lines
26 KiB
Python
"""
|
||
高级骨骼动画系统 - 优化和压缩模块
|
||
提供动画压缩、LOD、缓存优化等功能
|
||
"""
|
||
|
||
import math
|
||
import json
|
||
import os
|
||
from typing import Dict, List, Optional, Tuple, Any, Callable
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum
|
||
|
||
from panda3d.core import *
|
||
from direct.actor.Actor import Actor
|
||
|
||
# 定义常量
|
||
OPTIMIZATION_SETTINGS = {
|
||
'compression_threshold': 0.001,
|
||
'lod_distance_thresholds': [10.0, 25.0, 50.0],
|
||
'lod_frame_rates': [1.0, 0.5, 0.25, 0.1],
|
||
'cache_max_size': 500,
|
||
'cache_cleanup_interval': 30.0
|
||
}
|
||
|
||
# 枚举定义
|
||
class CompressionMethod(Enum):
|
||
"""压缩方法枚举"""
|
||
KEYFRAME_REDUCTION = 0
|
||
QUANTIZATION = 1
|
||
DELTA_COMPRESSION = 2
|
||
RUN_LENGTH_ENCODING = 3
|
||
|
||
class LODStrategy(Enum):
|
||
"""LOD策略枚举"""
|
||
DISTANCE_BASED = 0
|
||
PERFORMANCE_BASED = 1
|
||
HYBRID = 2
|
||
|
||
class CachePolicy(Enum):
|
||
"""缓存策略枚举"""
|
||
LRU = 0 # 最近最少使用
|
||
LFU = 1 # 最不经常使用
|
||
FIFO = 2 # 先进先出
|
||
|
||
@dataclass
|
||
class CompressedAnimationData:
|
||
"""压缩动画数据类"""
|
||
name: str
|
||
frame_rate: float
|
||
duration: float
|
||
bone_data: Dict[str, List[Tuple[float, Tuple[float, float, float, float, float, float, float]]]] # 时间, (位置+旋转+缩放)
|
||
compression_ratio: float = 1.0
|
||
original_size: int = 0
|
||
compressed_size: int = 0
|
||
|
||
@dataclass
|
||
class LODSettings:
|
||
"""LOD设置数据类"""
|
||
strategy: LODStrategy
|
||
distance_thresholds: List[float]
|
||
frame_rates: List[float]
|
||
enable_auto_lod: bool = True
|
||
performance_threshold: float = 30.0 # FPS阈值
|
||
|
||
@dataclass
|
||
class CacheEntry:
|
||
"""缓存条目数据类"""
|
||
key: str
|
||
data: Any
|
||
timestamp: float
|
||
access_count: int = 1
|
||
size: int = 0
|
||
|
||
class AnimationCompressor:
|
||
"""
|
||
动画压缩系统
|
||
支持多种压缩算法
|
||
"""
|
||
|
||
def __init__(self):
|
||
"""初始化动画压缩系统"""
|
||
self.settings = {
|
||
'position_threshold': 0.001,
|
||
'rotation_threshold': 0.01,
|
||
'scale_threshold': 0.001,
|
||
'quantization_bits': 16,
|
||
'enable_quantization': True
|
||
}
|
||
|
||
print("动画压缩系统初始化完成")
|
||
|
||
def compress_animation(self, actor: Actor, anim_name: str,
|
||
method: CompressionMethod = CompressionMethod.KEYFRAME_REDUCTION) -> Optional[CompressedAnimationData]:
|
||
"""
|
||
压缩指定动画
|
||
:param actor: Actor对象
|
||
:param anim_name: 动画名称
|
||
:param method: 压缩方法
|
||
:return: 压缩后的动画数据
|
||
"""
|
||
if anim_name not in actor.getAnimNames():
|
||
print(f"错误: 动画 {anim_name} 不存在")
|
||
return None
|
||
|
||
print(f"开始压缩动画: {anim_name} (方法: {method.name})")
|
||
|
||
# 获取动画控制
|
||
anim_control = actor.getAnimControl(anim_name)
|
||
if not anim_control:
|
||
print(f"错误: 无法获取动画控制 {anim_name}")
|
||
return None
|
||
|
||
# 获取动画数据
|
||
num_frames = anim_control.getNumFrames()
|
||
frame_rate = anim_control.getFrameRate()
|
||
duration = anim_control.getDuration()
|
||
|
||
print(f" 动画信息: {num_frames} 帧, {frame_rate} FPS, {duration:.2f} 秒")
|
||
|
||
# 提取骨骼动画数据
|
||
bone_data = self._extract_bone_animation_data(actor, anim_name, num_frames)
|
||
|
||
# 根据方法进行压缩
|
||
if method == CompressionMethod.KEYFRAME_REDUCTION:
|
||
compressed_bone_data = self._compress_keyframe_reduction(bone_data)
|
||
elif method == CompressionMethod.QUANTIZATION:
|
||
compressed_bone_data = self._compress_quantization(bone_data)
|
||
elif method == CompressionMethod.DELTA_COMPRESSION:
|
||
compressed_bone_data = self._compress_delta(bone_data)
|
||
elif method == CompressionMethod.RUN_LENGTH_ENCODING:
|
||
compressed_bone_data = self._compress_rle(bone_data)
|
||
else:
|
||
compressed_bone_data = bone_data
|
||
|
||
# 计算压缩比
|
||
original_size = self._calculate_data_size(bone_data)
|
||
compressed_size = self._calculate_data_size(compressed_bone_data)
|
||
compression_ratio = original_size / compressed_size if compressed_size > 0 else 1.0
|
||
|
||
# 创建压缩数据
|
||
compressed_data = CompressedAnimationData(
|
||
name=anim_name,
|
||
frame_rate=frame_rate,
|
||
duration=duration,
|
||
bone_data=compressed_bone_data,
|
||
compression_ratio=compression_ratio,
|
||
original_size=original_size,
|
||
compressed_size=compressed_size
|
||
)
|
||
|
||
print(f" 压缩完成: 原始大小={original_size}, 压缩大小={compressed_size}, 压缩比={compression_ratio:.2f}:1")
|
||
return compressed_data
|
||
|
||
def _extract_bone_animation_data(self, actor: Actor, anim_name: str, num_frames: int) -> Dict[str, List]:
|
||
"""
|
||
提取骨骼动画数据
|
||
:param actor: Actor对象
|
||
:param anim_name: 动画名称
|
||
:param num_frames: 帧数
|
||
:return: 骨骼动画数据
|
||
"""
|
||
bone_data = {}
|
||
|
||
# 获取所有骨骼
|
||
bundle = actor.getPartBundleDict().get('modelRoot')
|
||
if not bundle:
|
||
return bone_data
|
||
|
||
# 遍历骨骼
|
||
for i in range(bundle.getNumChildren()):
|
||
child = bundle.getChild(i)
|
||
if hasattr(child, 'getName'):
|
||
bone_name = child.getName()
|
||
bone_data[bone_name] = self._extract_bone_frames(actor, bone_name, anim_name, num_frames)
|
||
|
||
return bone_data
|
||
|
||
def _extract_bone_frames(self, actor: Actor, bone_name: str, anim_name: str, num_frames: int) -> List:
|
||
"""
|
||
提取单个骨骼的帧数据
|
||
:param actor: Actor对象
|
||
:param bone_name: 骨骼名称
|
||
:param anim_name: 动画名称
|
||
:param num_frames: 帧数
|
||
:return: 帧数据列表
|
||
"""
|
||
frames = []
|
||
|
||
# 为每一帧提取数据
|
||
for frame in range(num_frames):
|
||
# 设置到指定帧
|
||
actor.pose(anim_name, frame)
|
||
|
||
# 获取骨骼变换
|
||
joint = actor.exposeJoint(None, "modelRoot", bone_name)
|
||
if joint:
|
||
pos = joint.getPos()
|
||
hpr = joint.getHpr()
|
||
scale = joint.getScale()
|
||
|
||
# 存储为 (时间, (px, py, pz, rx, ry, rz, sx, sy, sz))
|
||
time = frame / actor.getFrameRate(anim_name)
|
||
frame_data = (
|
||
time,
|
||
(pos.x, pos.y, pos.z,
|
||
hpr.x, hpr.y, hpr.z,
|
||
scale.x, scale.y, scale.z)
|
||
)
|
||
frames.append(frame_data)
|
||
|
||
return frames
|
||
|
||
def _compress_keyframe_reduction(self, bone_data: Dict[str, List]) -> Dict[str, List]:
|
||
"""
|
||
关键帧简化压缩
|
||
:param bone_data: 骨骼数据
|
||
:return: 压缩后的骨骼数据
|
||
"""
|
||
compressed_data = {}
|
||
|
||
for bone_name, frames in bone_data.items():
|
||
if len(frames) <= 2:
|
||
compressed_data[bone_name] = frames
|
||
continue
|
||
|
||
compressed_frames = [frames[0]] # 总是保留第一帧
|
||
last_frame = frames[0]
|
||
|
||
for i in range(1, len(frames) - 1):
|
||
current_frame = frames[i]
|
||
next_frame = frames[i + 1]
|
||
|
||
# 检查当前帧是否可以被简化
|
||
if not self._is_frame_necessary(last_frame, current_frame, next_frame):
|
||
continue
|
||
|
||
compressed_frames.append(current_frame)
|
||
last_frame = current_frame
|
||
|
||
compressed_frames.append(frames[-1]) # 总是保留最后一帧
|
||
compressed_data[bone_name] = compressed_frames
|
||
|
||
return compressed_data
|
||
|
||
def _is_frame_necessary(self, prev_frame: Tuple, current_frame: Tuple, next_frame: Tuple) -> bool:
|
||
"""
|
||
判断帧是否必要
|
||
:param prev_frame: 前一帧
|
||
:param current_frame: 当前帧
|
||
:param next_frame: 后一帧
|
||
:return: 是否必要
|
||
"""
|
||
prev_time, prev_data = prev_frame
|
||
curr_time, curr_data = current_frame
|
||
next_time, next_data = next_frame
|
||
|
||
# 计算线性插值
|
||
t = (curr_time - prev_time) / (next_time - prev_time) if (next_time - prev_time) > 0 else 0
|
||
interpolated = tuple(prev_data[i] + t * (next_data[i] - prev_data[i]) for i in range(len(prev_data)))
|
||
|
||
# 计算与实际值的差异
|
||
total_diff = sum(abs(curr_data[i] - interpolated[i]) for i in range(len(curr_data)))
|
||
|
||
return total_diff > self.settings['position_threshold'] * 3 # 简化阈值检查
|
||
|
||
def _compress_quantization(self, bone_data: Dict[str, List]) -> Dict[str, List]:
|
||
"""
|
||
量化压缩
|
||
:param bone_data: 骨骼数据
|
||
:return: 量化后的骨骼数据
|
||
"""
|
||
# 量化实现 - 简化版本
|
||
return bone_data # 实际项目中需要实现具体的量化算法
|
||
|
||
def _compress_delta(self, bone_data: Dict[str, List]) -> Dict[str, List]:
|
||
"""
|
||
差分压缩
|
||
:param bone_data: 骨骼数据
|
||
:return: 差分压缩后的骨骼数据
|
||
"""
|
||
# 差分压缩实现 - 简化版本
|
||
return bone_data # 实际项目中需要实现具体的差分算法
|
||
|
||
def _compress_rle(self, bone_data: Dict[str, List]) -> Dict[str, List]:
|
||
"""
|
||
游程编码压缩
|
||
:param bone_data: 骨骼数据
|
||
:return: RLE压缩后的骨骼数据
|
||
"""
|
||
# RLE压缩实现 - 简化版本
|
||
return bone_data # 实际项目中需要实现具体的RLE算法
|
||
|
||
def _calculate_data_size(self, bone_data: Dict[str, List]) -> int:
|
||
"""
|
||
计算数据大小
|
||
:param bone_data: 骨骼数据
|
||
:return: 数据大小(字节)
|
||
"""
|
||
size = 0
|
||
for frames in bone_data.values():
|
||
size += len(frames) * 100 # 简化估算,每帧约100字节
|
||
return size
|
||
|
||
def decompress_animation(self, compressed_data: CompressedAnimationData) -> Dict[str, List]:
|
||
"""
|
||
解压缩动画数据
|
||
:param compressed_data: 压缩数据
|
||
:return: 解压缩后的骨骼数据
|
||
"""
|
||
# 解压缩实现 - 简化版本
|
||
return compressed_data.bone_data
|
||
|
||
def get_compression_stats(self, compressed_data: CompressedAnimationData) -> Dict[str, Any]:
|
||
"""
|
||
获取压缩统计信息
|
||
:param compressed_data: 压缩数据
|
||
:return: 统计信息
|
||
"""
|
||
return {
|
||
'name': compressed_data.name,
|
||
'compression_ratio': compressed_data.compression_ratio,
|
||
'original_size': compressed_data.original_size,
|
||
'compressed_size': compressed_data.compressed_size,
|
||
'space_saved': compressed_data.original_size - compressed_data.compressed_size,
|
||
'percentage_saved': (1 - compressed_data.compressed_size / compressed_data.original_size) * 100 if compressed_data.original_size > 0 else 0
|
||
}
|
||
|
||
class AnimationLODSystem:
|
||
"""
|
||
动画LOD系统
|
||
根据距离和性能自动调整动画质量
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
"""
|
||
初始化LOD系统
|
||
:param world: 世界对象
|
||
"""
|
||
self.world = world
|
||
self.settings = LODSettings(
|
||
strategy=LODStrategy.DISTANCE_BASED,
|
||
distance_thresholds=[10.0, 25.0, 50.0],
|
||
frame_rates=[1.0, 0.5, 0.25, 0.1],
|
||
enable_auto_lod=True,
|
||
performance_threshold=30.0
|
||
)
|
||
self.actor_lod_levels = {} # {actor_id: lod_level}
|
||
self.performance_monitor = {
|
||
'last_fps': 60.0,
|
||
'frame_times': [],
|
||
'last_update': 0.0
|
||
}
|
||
self.camera_node = None
|
||
|
||
print("动画LOD系统初始化完成")
|
||
|
||
def set_lod_strategy(self, strategy: LODStrategy) -> None:
|
||
"""
|
||
设置LOD策略
|
||
:param strategy: LOD策略
|
||
"""
|
||
self.settings.strategy = strategy
|
||
print(f"LOD策略设置为: {strategy.name}")
|
||
|
||
def set_distance_thresholds(self, thresholds: List[float]) -> None:
|
||
"""
|
||
设置距离阈值
|
||
:param thresholds: 距离阈值列表
|
||
"""
|
||
self.settings.distance_thresholds = sorted(thresholds)
|
||
print(f"距离阈值设置为: {thresholds}")
|
||
|
||
def set_frame_rates(self, frame_rates: List[float]) -> None:
|
||
"""
|
||
设置帧率级别
|
||
:param frame_rates: 帧率列表
|
||
"""
|
||
self.settings.frame_rates = frame_rates
|
||
print(f"帧率级别设置为: {frame_rates}")
|
||
|
||
def enable_auto_lod(self, enabled: bool = True) -> None:
|
||
"""
|
||
启用/禁用自动LOD
|
||
:param enabled: 是否启用
|
||
"""
|
||
self.settings.enable_auto_lod = enabled
|
||
status = "启用" if enabled else "禁用"
|
||
print(f"自动LOD已{status}")
|
||
|
||
def set_performance_threshold(self, threshold: float) -> None:
|
||
"""
|
||
设置性能阈值
|
||
:param threshold: FPS阈值
|
||
"""
|
||
self.settings.performance_threshold = threshold
|
||
print(f"性能阈值设置为: {threshold} FPS")
|
||
|
||
def set_camera_node(self, camera_node) -> None:
|
||
"""
|
||
设置摄像机节点
|
||
:param camera_node: 摄像机节点
|
||
"""
|
||
self.camera_node = camera_node
|
||
|
||
def update_lod_levels(self, actors: Dict[str, Any]) -> None:
|
||
"""
|
||
更新所有Actor的LOD级别
|
||
:param actors: Actor字典 {actor_id: actor_info}
|
||
"""
|
||
if not self.settings.enable_auto_lod:
|
||
return
|
||
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 每隔一定时间更新一次
|
||
if current_time - self.performance_monitor['last_update'] < 0.5:
|
||
return
|
||
|
||
self.performance_monitor['last_update'] = current_time
|
||
|
||
# 获取摄像机位置
|
||
camera_pos = Point3(0, 0, 0)
|
||
if self.camera_node:
|
||
camera_pos = self.camera_node.getPos()
|
||
elif hasattr(self.world, 'camera') and self.world.camera:
|
||
camera_pos = self.world.camera.getPos()
|
||
|
||
# 根据策略更新LOD
|
||
if self.settings.strategy == LODStrategy.DISTANCE_BASED:
|
||
self._update_distance_based_lod(actors, camera_pos)
|
||
elif self.settings.strategy == LODStrategy.PERFORMANCE_BASED:
|
||
self._update_performance_based_lod(actors)
|
||
elif self.settings.strategy == LODStrategy.HYBRID:
|
||
self._update_hybrid_lod(actors, camera_pos)
|
||
|
||
def _update_distance_based_lod(self, actors: Dict[str, Any], camera_pos: Point3) -> None:
|
||
"""
|
||
更新基于距离的LOD
|
||
:param actors: Actor字典
|
||
:param camera_pos: 摄像机位置
|
||
"""
|
||
for actor_id, actor_info in actors.items():
|
||
actor = actor_info.actor
|
||
|
||
# 计算距离
|
||
distance = (actor.getPos() - camera_pos).length()
|
||
|
||
# 确定LOD级别
|
||
lod_level = 0
|
||
for i, threshold in enumerate(self.settings.distance_thresholds):
|
||
if distance > threshold:
|
||
lod_level = i + 1
|
||
|
||
# 应用LOD设置
|
||
self._apply_lod_settings(actor_info, lod_level)
|
||
self.actor_lod_levels[actor_id] = lod_level
|
||
|
||
def _update_performance_based_lod(self, actors: Dict[str, Any]) -> None:
|
||
"""
|
||
更新基于性能的LOD
|
||
:param actors: Actor字典
|
||
"""
|
||
# 根据FPS调整LOD
|
||
fps = self.performance_monitor['last_fps']
|
||
if fps < self.settings.performance_threshold:
|
||
# 降低所有Actor的LOD级别
|
||
for actor_id, actor_info in actors.items():
|
||
current_lod = self.actor_lod_levels.get(actor_id, 0)
|
||
new_lod = min(current_lod + 1, len(self.settings.frame_rates) - 1)
|
||
self._apply_lod_settings(actor_info, new_lod)
|
||
self.actor_lod_levels[actor_id] = new_lod
|
||
|
||
def _update_hybrid_lod(self, actors: Dict[str, Any], camera_pos: Point3) -> None:
|
||
"""
|
||
更新混合LOD
|
||
:param actors: Actor字典
|
||
:param camera_pos: 摄像机位置
|
||
"""
|
||
# 结合距离和性能的LOD策略
|
||
self._update_distance_based_lod(actors, camera_pos)
|
||
self._update_performance_based_lod(actors)
|
||
|
||
def _apply_lod_settings(self, actor_info: Any, lod_level: int) -> None:
|
||
"""
|
||
应用LOD设置到Actor
|
||
:param actor_info: Actor信息
|
||
:param lod_level: LOD级别
|
||
"""
|
||
actor = actor_info.actor
|
||
frame_rate_multiplier = self.settings.frame_rates[lod_level] if lod_level < len(self.settings.frame_rates) else 0.1
|
||
|
||
# 调整所有动画的播放速度
|
||
for anim_name in actor.getAnimNames():
|
||
current_rate = actor.getPlayRate(anim_name)
|
||
new_rate = current_rate * frame_rate_multiplier
|
||
actor.setPlayRate(new_rate, anim_name)
|
||
|
||
# 更新Actor信息中的LOD级别
|
||
actor_info.lod_level = lod_level
|
||
|
||
def update_performance_stats(self, current_fps: float, frame_time: float) -> None:
|
||
"""
|
||
更新性能统计
|
||
:param current_fps: 当前FPS
|
||
:param frame_time: 帧时间
|
||
"""
|
||
self.performance_monitor['last_fps'] = current_fps
|
||
self.performance_monitor['frame_times'].append(frame_time)
|
||
|
||
# 保持最近30帧的统计数据
|
||
if len(self.performance_monitor['frame_times']) > 30:
|
||
self.performance_monitor['frame_times'] = self.performance_monitor['frame_times'][-30:]
|
||
|
||
def get_lod_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取LOD统计信息
|
||
:return: 统计信息
|
||
"""
|
||
lod_counts = {}
|
||
for lod_level in self.actor_lod_levels.values():
|
||
if lod_level not in lod_counts:
|
||
lod_counts[lod_level] = 0
|
||
lod_counts[lod_level] += 1
|
||
|
||
return {
|
||
'actor_lod_distribution': lod_counts,
|
||
'current_fps': self.performance_monitor['last_fps'],
|
||
'average_frame_time': sum(self.performance_monitor['frame_times']) / len(self.performance_monitor['frame_times'])
|
||
if self.performance_monitor['frame_times'] else 0,
|
||
'lod_settings': {
|
||
'strategy': self.settings.strategy.name,
|
||
'distance_thresholds': self.settings.distance_thresholds,
|
||
'frame_rates': self.settings.frame_rates
|
||
}
|
||
}
|
||
|
||
class AnimationCacheManager:
|
||
"""
|
||
动画缓存管理器
|
||
提供高效的动画数据缓存
|
||
"""
|
||
|
||
def __init__(self, max_size: int = 500):
|
||
"""
|
||
初始化缓存管理器
|
||
:param max_size: 最大缓存大小
|
||
"""
|
||
self.max_size = max_size
|
||
self.cache: Dict[str, CacheEntry] = {}
|
||
self.policy = CachePolicy.LRU
|
||
self.cleanup_interval = 30.0
|
||
self.last_cleanup_time = 0.0
|
||
|
||
print(f"动画缓存管理器初始化完成 (最大大小: {max_size})")
|
||
|
||
def set_cache_policy(self, policy: CachePolicy) -> None:
|
||
"""
|
||
设置缓存策略
|
||
:param policy: 缓存策略
|
||
"""
|
||
self.policy = policy
|
||
print(f"缓存策略设置为: {policy.name}")
|
||
|
||
def set_max_size(self, max_size: int) -> None:
|
||
"""
|
||
设置最大缓存大小
|
||
:param max_size: 最大大小
|
||
"""
|
||
self.max_size = max_size
|
||
self._cleanup_cache()
|
||
print(f"最大缓存大小设置为: {max_size}")
|
||
|
||
def put(self, key: str, data: Any, size: int = 0) -> None:
|
||
"""
|
||
放入缓存项
|
||
:param key: 键
|
||
:param data: 数据
|
||
:param size: 数据大小
|
||
"""
|
||
current_time = globalClock.getRealTime()
|
||
|
||
if key in self.cache:
|
||
# 更新现有条目
|
||
entry = self.cache[key]
|
||
entry.data = data
|
||
entry.timestamp = current_time
|
||
entry.access_count += 1
|
||
entry.size = size or entry.size
|
||
else:
|
||
# 添加新条目
|
||
entry = CacheEntry(
|
||
key=key,
|
||
data=data,
|
||
timestamp=current_time,
|
||
access_count=1,
|
||
size=size
|
||
)
|
||
self.cache[key] = entry
|
||
|
||
# 检查是否需要清理缓存
|
||
self._check_cleanup()
|
||
|
||
def get(self, key: str) -> Optional[Any]:
|
||
"""
|
||
获取缓存项
|
||
:param key: 键
|
||
:return: 数据或None
|
||
"""
|
||
if key in self.cache:
|
||
entry = self.cache[key]
|
||
entry.access_count += 1
|
||
entry.timestamp = globalClock.getRealTime()
|
||
return entry.data
|
||
return None
|
||
|
||
def remove(self, key: str) -> bool:
|
||
"""
|
||
移除缓存项
|
||
:param key: 键
|
||
:return: 是否成功移除
|
||
"""
|
||
if key in self.cache:
|
||
del self.cache[key]
|
||
return True
|
||
return False
|
||
|
||
def clear(self) -> None:
|
||
"""清空缓存"""
|
||
self.cache.clear()
|
||
print("动画缓存已清空")
|
||
|
||
def _check_cleanup(self) -> None:
|
||
"""
|
||
检查是否需要清理缓存
|
||
"""
|
||
current_time = globalClock.getRealTime()
|
||
if current_time - self.last_cleanup_time > self.cleanup_interval:
|
||
self._cleanup_cache()
|
||
self.last_cleanup_time = current_time
|
||
|
||
def _cleanup_cache(self) -> None:
|
||
"""
|
||
清理缓存
|
||
"""
|
||
if len(self.cache) <= self.max_size:
|
||
return
|
||
|
||
# 根据策略选择要移除的条目
|
||
entries_to_remove = len(self.cache) - self.max_size
|
||
|
||
if self.policy == CachePolicy.LRU:
|
||
# 移除最近最少使用的条目
|
||
sorted_entries = sorted(self.cache.values(), key=lambda x: x.timestamp)
|
||
elif self.policy == CachePolicy.LFU:
|
||
# 移除最不经常使用的条目
|
||
sorted_entries = sorted(self.cache.values(), key=lambda x: x.access_count)
|
||
else: # FIFO
|
||
# 移除最早添加的条目
|
||
sorted_entries = sorted(self.cache.values(), key=lambda x: x.timestamp)
|
||
|
||
# 移除条目
|
||
for i in range(min(entries_to_remove, len(sorted_entries))):
|
||
entry = sorted_entries[i]
|
||
del self.cache[entry.key]
|
||
|
||
print(f"缓存清理完成,移除了 {entries_to_remove} 个条目")
|
||
|
||
def get_cache_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取缓存统计信息
|
||
:return: 统计信息
|
||
"""
|
||
total_size = sum(entry.size for entry in self.cache.values())
|
||
total_access = sum(entry.access_count for entry in self.cache.values())
|
||
avg_access = total_access / len(self.cache) if self.cache else 0
|
||
|
||
return {
|
||
'size': len(self.cache),
|
||
'max_size': self.max_size,
|
||
'total_size': total_size,
|
||
'hit_rate': 0, # 简化实现,实际项目中需要跟踪命中率
|
||
'average_access_count': avg_access,
|
||
'policy': self.policy.name
|
||
}
|
||
|
||
# 使用示例和测试代码
|
||
def example_compression_usage(actor: Actor):
|
||
"""
|
||
动画压缩使用示例
|
||
"""
|
||
print("=== 动画压缩使用示例 ===")
|
||
|
||
compressor = AnimationCompressor()
|
||
|
||
# 压缩所有动画
|
||
for anim_name in actor.getAnimNames():
|
||
print(f"压缩动画: {anim_name}")
|
||
compressed_data = compressor.compress_animation(
|
||
actor,
|
||
anim_name,
|
||
CompressionMethod.KEYFRAME_REDUCTION
|
||
)
|
||
if compressed_data:
|
||
stats = compressor.get_compression_stats(compressed_data)
|
||
print(f" 压缩统计: {stats}")
|
||
|
||
print("动画压缩示例完成")
|
||
|
||
def example_lod_usage(world, actors: Dict[str, Any]):
|
||
"""
|
||
LOD系统使用示例
|
||
"""
|
||
print("=== 动画LOD使用示例 ===")
|
||
|
||
lod_system = AnimationLODSystem(world)
|
||
|
||
# 设置LOD参数
|
||
lod_system.set_lod_strategy(LODStrategy.HYBRID)
|
||
lod_system.set_distance_thresholds([15.0, 30.0, 60.0])
|
||
lod_system.set_frame_rates([1.0, 0.7, 0.4, 0.1])
|
||
|
||
# 更新LOD级别
|
||
lod_system.update_lod_levels(actors)
|
||
|
||
# 更新性能统计
|
||
lod_system.update_performance_stats(45, 0.022) # 45 FPS, 22ms per frame
|
||
|
||
# 获取LOD统计
|
||
stats = lod_system.get_lod_stats()
|
||
print(f" LOD统计: {stats}")
|
||
|
||
print("动画LOD示例完成")
|
||
|
||
def example_cache_usage():
|
||
"""
|
||
缓存管理使用示例
|
||
"""
|
||
print("=== 动画缓存管理使用示例 ===")
|
||
|
||
cache_manager = AnimationCacheManager(max_size=100)
|
||
|
||
# 设置缓存策略
|
||
cache_manager.set_cache_policy(CachePolicy.LRU)
|
||
|
||
# 添加缓存项
|
||
dummy_data = {'frames': 100, 'bones': 20, 'data': 'animation_data'}
|
||
cache_manager.put("actor_1_walk", dummy_data, size=1024)
|
||
cache_manager.put("actor_1_run", dummy_data, size=2048)
|
||
cache_manager.put("actor_2_idle", dummy_data, size=512)
|
||
|
||
# 获取缓存项
|
||
data = cache_manager.get("actor_1_walk")
|
||
if data:
|
||
print(" 成功获取缓存数据")
|
||
|
||
# 查看缓存统计
|
||
stats = cache_manager.get_cache_stats()
|
||
print(f" 缓存统计: {stats}")
|
||
|
||
print("动画缓存管理示例完成")
|
||
|
||
if __name__ == "__main__":
|
||
print("动画优化和压缩模块加载完成") |