800 lines
27 KiB
Python
800 lines
27 KiB
Python
"""
|
||
Morphing和变形动画插件 - 优化模块
|
||
提供动画压缩、LOD系统、缓存管理等优化功能
|
||
"""
|
||
|
||
import math
|
||
import json
|
||
import numpy as np
|
||
from typing import Dict, List, Tuple, Optional, Any, Union
|
||
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_size': 500,
|
||
'max_buffer_size': 100
|
||
}
|
||
|
||
# 枚举定义
|
||
class CompressionMethod(Enum):
|
||
"""压缩方法枚举"""
|
||
KEYFRAME_REDUCTION = 0 # 关键帧简化
|
||
QUANTIZATION = 1 # 量化压缩
|
||
DELTA_COMPRESSION = 2 # 差分压缩
|
||
RLE = 3 # 游程编码
|
||
|
||
class LODStrategy(Enum):
|
||
"""LOD策略枚举"""
|
||
DISTANCE_BASED = 0 # 基于距离
|
||
PERFORMANCE_BASED = 1 # 基于性能
|
||
HYBRID = 2 # 混合策略
|
||
|
||
@dataclass
|
||
class CompressedMorphData:
|
||
"""压缩的Morph数据类"""
|
||
method: CompressionMethod
|
||
original_size: int
|
||
compressed_size: int
|
||
compression_ratio: float
|
||
data: Any # 压缩后的数据
|
||
|
||
@dataclass
|
||
class LODLevel:
|
||
"""LOD级别数据类"""
|
||
level: int
|
||
distance_threshold: float
|
||
frame_rate: float
|
||
quality_factor: float
|
||
active: bool = True
|
||
|
||
@dataclass
|
||
class CacheEntry:
|
||
"""缓存条目数据类"""
|
||
key: str
|
||
data: Any
|
||
size: int
|
||
access_time: float
|
||
access_count: int = 1
|
||
|
||
class AnimationCompressor:
|
||
"""
|
||
动画压缩系统
|
||
支持多种压缩算法减少动画数据大小
|
||
"""
|
||
|
||
def __init__(self):
|
||
"""初始化动画压缩系统"""
|
||
self.compression_threshold = OPTIMIZATION_SETTINGS['compression_threshold']
|
||
self.stats = {
|
||
'animations_compressed': 0,
|
||
'data_saved': 0,
|
||
'compression_time': 0.0
|
||
}
|
||
|
||
print("动画压缩系统初始化完成")
|
||
|
||
def compress_morph_data(self, morph_data: List[Tuple[float, float, float]],
|
||
method: CompressionMethod = CompressionMethod.KEYFRAME_REDUCTION) -> Optional[CompressedMorphData]:
|
||
"""
|
||
压缩Morph数据
|
||
:param morph_data: Morph数据列表
|
||
:param method: 压缩方法
|
||
:return: 压缩后的数据
|
||
"""
|
||
import time
|
||
start_time = time.time()
|
||
|
||
original_size = len(morph_data) * 3 * 4 # 3个float,每个4字节
|
||
|
||
try:
|
||
if method == CompressionMethod.KEYFRAME_REDUCTION:
|
||
compressed_data = self._compress_keyframe_reduction(morph_data)
|
||
elif method == CompressionMethod.QUANTIZATION:
|
||
compressed_data = self._compress_quantization(morph_data)
|
||
elif method == CompressionMethod.DELTA_COMPRESSION:
|
||
compressed_data = self._compress_delta(morph_data)
|
||
elif method == CompressionMethod.RLE:
|
||
compressed_data = self._compress_rle(morph_data)
|
||
else:
|
||
compressed_data = morph_data
|
||
|
||
compressed_size = len(str(compressed_data))
|
||
compression_ratio = original_size / max(1, compressed_size)
|
||
|
||
# 创建压缩数据对象
|
||
result = CompressedMorphData(
|
||
method=method,
|
||
original_size=original_size,
|
||
compressed_size=compressed_size,
|
||
compression_ratio=compression_ratio,
|
||
data=compressed_data
|
||
)
|
||
|
||
# 更新统计信息
|
||
self.stats['animations_compressed'] += 1
|
||
self.stats['data_saved'] += original_size - compressed_size
|
||
self.stats['compression_time'] += time.time() - start_time
|
||
|
||
print(f"Morph数据压缩完成: 原始大小={original_size}字节, 压缩后={compressed_size}字节, 压缩比={compression_ratio:.2f}")
|
||
return result
|
||
|
||
except Exception as e:
|
||
print(f"压缩Morph数据时出错: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def _compress_keyframe_reduction(self, morph_data: List[Tuple[float, float, float]]) -> List[Tuple[float, float, float]]:
|
||
"""
|
||
关键帧简化压缩
|
||
:param morph_data: Morph数据列表
|
||
:return: 压缩后的数据
|
||
"""
|
||
if len(morph_data) <= 2:
|
||
return morph_data
|
||
|
||
compressed = [morph_data[0]] # 总是保留第一帧
|
||
last_frame = morph_data[0]
|
||
|
||
for i in range(1, len(morph_data) - 1):
|
||
current_frame = morph_data[i]
|
||
next_frame = morph_data[i + 1]
|
||
|
||
# 检查当前帧是否可以被简化
|
||
if not self._is_frame_necessary(last_frame, current_frame, next_frame):
|
||
continue
|
||
|
||
compressed.append(current_frame)
|
||
last_frame = current_frame
|
||
|
||
compressed.append(morph_data[-1]) # 总是保留最后一帧
|
||
return compressed
|
||
|
||
def _is_frame_necessary(self, prev_frame: Tuple[float, float, float],
|
||
current_frame: Tuple[float, float, float],
|
||
next_frame: Tuple[float, float, float]) -> bool:
|
||
"""
|
||
判断帧是否必要
|
||
:param prev_frame: 前一帧
|
||
:param current_frame: 当前帧
|
||
:param next_frame: 后一帧
|
||
:return: 是否必要
|
||
"""
|
||
# 计算线性插值
|
||
t = 0.5 # 简化处理,实际应该基于时间计算
|
||
interpolated = (
|
||
prev_frame[0] + t * (next_frame[0] - prev_frame[0]),
|
||
prev_frame[1] + t * (next_frame[1] - prev_frame[1]),
|
||
prev_frame[2] + t * (next_frame[2] - prev_frame[2])
|
||
)
|
||
|
||
# 计算与实际值的差异
|
||
diff = math.sqrt(
|
||
(current_frame[0] - interpolated[0]) ** 2 +
|
||
(current_frame[1] - interpolated[1]) ** 2 +
|
||
(current_frame[2] - interpolated[2]) ** 2
|
||
)
|
||
|
||
return diff > self.compression_threshold
|
||
|
||
def _compress_quantization(self, morph_data: List[Tuple[float, float, float]]) -> List[Tuple[int, int, int]]:
|
||
"""
|
||
量化压缩
|
||
:param morph_data: Morph数据列表
|
||
:return: 压缩后的数据
|
||
"""
|
||
# 找到数据的范围
|
||
min_vals = [float('inf')] * 3
|
||
max_vals = [float('-inf')] * 3
|
||
|
||
for frame in morph_data:
|
||
for i in range(3):
|
||
min_vals[i] = min(min_vals[i], frame[i])
|
||
max_vals[i] = max(max_vals[i], frame[i])
|
||
|
||
# 量化到16位整数
|
||
quantized_data = []
|
||
for frame in morph_data:
|
||
quantized_frame = []
|
||
for i in range(3):
|
||
# 将浮点数映射到0-65535范围
|
||
if max_vals[i] != min_vals[i]:
|
||
quantized_value = int((frame[i] - min_vals[i]) / (max_vals[i] - min_vals[i]) * 65535)
|
||
else:
|
||
quantized_value = 0
|
||
quantized_frame.append(quantized_value)
|
||
quantized_data.append(tuple(quantized_frame))
|
||
|
||
return quantized_data
|
||
|
||
def _compress_delta(self, morph_data: List[Tuple[float, float, float]]) -> List[Tuple[float, float, float]]:
|
||
"""
|
||
差分压缩
|
||
:param morph_data: Morph数据列表
|
||
:return: 压缩后的数据
|
||
"""
|
||
if not morph_data:
|
||
return morph_data
|
||
|
||
delta_data = [morph_data[0]] # 第一帧保持原样
|
||
|
||
for i in range(1, len(morph_data)):
|
||
delta = (
|
||
morph_data[i][0] - morph_data[i-1][0],
|
||
morph_data[i][1] - morph_data[i-1][1],
|
||
morph_data[i][2] - morph_data[i-1][2]
|
||
)
|
||
delta_data.append(delta)
|
||
|
||
return delta_data
|
||
|
||
def _compress_rle(self, morph_data: List[Tuple[float, float, float]]) -> List[Tuple[int, Tuple[float, float, float]]]:
|
||
"""
|
||
游程编码压缩
|
||
:param morph_data: Morph数据列表
|
||
:return: 压缩后的数据
|
||
"""
|
||
if not morph_data:
|
||
return []
|
||
|
||
rle_data = []
|
||
current_value = morph_data[0]
|
||
count = 1
|
||
|
||
for i in range(1, len(morph_data)):
|
||
if morph_data[i] == current_value:
|
||
count += 1
|
||
else:
|
||
rle_data.append((count, current_value))
|
||
current_value = morph_data[i]
|
||
count = 1
|
||
|
||
rle_data.append((count, current_value))
|
||
return rle_data
|
||
|
||
def decompress_morph_data(self, compressed_data: CompressedMorphData) -> Optional[List[Tuple[float, float, float]]]:
|
||
"""
|
||
解压缩Morph数据
|
||
:param compressed_data: 压缩的数据
|
||
:return: 解压后的数据
|
||
"""
|
||
try:
|
||
method = compressed_data.method
|
||
data = compressed_data.data
|
||
|
||
if method == CompressionMethod.KEYFRAME_REDUCTION:
|
||
# 关键帧简化数据不需要特殊解压
|
||
return data
|
||
elif method == CompressionMethod.QUANTIZATION:
|
||
return self._decompress_quantization(data)
|
||
elif method == CompressionMethod.DELTA_COMPRESSION:
|
||
return self._decompress_delta(data)
|
||
elif method == CompressionMethod.RLE:
|
||
return self._decompress_rle(data)
|
||
else:
|
||
return data
|
||
|
||
except Exception as e:
|
||
print(f"解压缩Morph数据时出错: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
def _decompress_quantization(self, quantized_data: List[Tuple[int, int, int]]) -> List[Tuple[float, float, float]]:
|
||
"""
|
||
解压量化数据
|
||
:param quantized_data: 量化数据
|
||
:return: 解压后的数据
|
||
"""
|
||
# 这里需要存储原始的min_vals和max_vals来进行解压
|
||
# 由于简化实现,我们返回原始数据
|
||
return [(float(x), float(y), float(z)) for x, y, z in quantized_data]
|
||
|
||
def _decompress_delta(self, delta_data: List[Tuple[float, float, float]]) -> List[Tuple[float, float, float]]:
|
||
"""
|
||
解压差分数据
|
||
:param delta_data: 差分数据
|
||
:return: 解压后的数据
|
||
"""
|
||
if not delta_data:
|
||
return []
|
||
|
||
original_data = [delta_data[0]] # 第一帧保持原样
|
||
|
||
for i in range(1, len(delta_data)):
|
||
original_frame = (
|
||
original_data[i-1][0] + delta_data[i][0],
|
||
original_data[i-1][1] + delta_data[i][1],
|
||
original_data[i-1][2] + delta_data[i][2]
|
||
)
|
||
original_data.append(original_frame)
|
||
|
||
return original_data
|
||
|
||
def _decompress_rle(self, rle_data: List[Tuple[int, Tuple[float, float, float]]]) -> List[Tuple[float, float, float]]:
|
||
"""
|
||
解压RLE数据
|
||
:param rle_data: RLE数据
|
||
:return: 解压后的数据
|
||
"""
|
||
original_data = []
|
||
|
||
for count, value in rle_data:
|
||
original_data.extend([value] * count)
|
||
|
||
return original_data
|
||
|
||
def get_compression_stats(self, compressed_data: Optional[CompressedMorphData] = None) -> Dict[str, Any]:
|
||
"""
|
||
获取压缩统计信息
|
||
:param compressed_data: 压缩数据(可选)
|
||
:return: 统计信息字典
|
||
"""
|
||
stats = self.stats.copy()
|
||
|
||
if compressed_data:
|
||
stats['current_compression_ratio'] = compressed_data.compression_ratio
|
||
stats['current_original_size'] = compressed_data.original_size
|
||
stats['current_compressed_size'] = compressed_data.compressed_size
|
||
|
||
return stats
|
||
|
||
class AnimationLODSystem:
|
||
"""
|
||
动画LOD系统
|
||
根据距离和性能自动调整动画质量
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
"""
|
||
初始化动画LOD系统
|
||
:param world: 世界对象
|
||
"""
|
||
self.world = world
|
||
self.strategy = LODStrategy.DISTANCE_BASED
|
||
self.distance_thresholds = OPTIMIZATION_SETTINGS['lod_distance_thresholds']
|
||
self.frame_rates = OPTIMIZATION_SETTINGS['lod_frame_rates']
|
||
self.lod_levels: List[LODLevel] = []
|
||
self.model_lod_levels: Dict[str, int] = {} # {model_id: lod_level}
|
||
self.camera_node = None
|
||
|
||
# 创建LOD级别
|
||
self._create_lod_levels()
|
||
|
||
print("动画LOD系统初始化完成")
|
||
|
||
def _create_lod_levels(self) -> None:
|
||
"""
|
||
创建LOD级别
|
||
"""
|
||
self.lod_levels = []
|
||
|
||
for i, (distance, frame_rate) in enumerate(zip(self.distance_thresholds, self.frame_rates)):
|
||
level = LODLevel(
|
||
level=i,
|
||
distance_threshold=distance,
|
||
frame_rate=frame_rate,
|
||
quality_factor=1.0 - (i / len(self.distance_thresholds))
|
||
)
|
||
self.lod_levels.append(level)
|
||
|
||
# 添加最高LOD级别(最近距离,最高质量)
|
||
highest_level = LODLevel(
|
||
level=len(self.lod_levels),
|
||
distance_threshold=0.0,
|
||
frame_rate=1.0,
|
||
quality_factor=1.0
|
||
)
|
||
self.lod_levels.append(highest_level)
|
||
|
||
def set_lod_strategy(self, strategy: LODStrategy) -> None:
|
||
"""
|
||
设置LOD策略
|
||
:param strategy: LOD策略
|
||
"""
|
||
self.strategy = strategy
|
||
print(f"LOD策略设置为: {strategy.name}")
|
||
|
||
def set_distance_thresholds(self, thresholds: List[float]) -> None:
|
||
"""
|
||
设置距离阈值
|
||
:param thresholds: 距离阈值列表
|
||
"""
|
||
self.distance_thresholds = thresholds
|
||
self._create_lod_levels()
|
||
print(f"距离阈值已更新: {thresholds}")
|
||
|
||
def set_frame_rates(self, frame_rates: List[float]) -> None:
|
||
"""
|
||
设置帧率级别
|
||
:param frame_rates: 帧率列表
|
||
"""
|
||
self.frame_rates = frame_rates
|
||
self._create_lod_levels()
|
||
print(f"帧率级别已更新: {frame_rates}")
|
||
|
||
def set_camera(self, camera_node: NodePath) -> None:
|
||
"""
|
||
设置相机节点
|
||
:param camera_node: 相机节点
|
||
"""
|
||
self.camera_node = camera_node
|
||
|
||
def update_lod_levels(self, models: Dict[str, Any]) -> None:
|
||
"""
|
||
更新LOD级别
|
||
:param models: 模型字典 {model_id: model_data}
|
||
"""
|
||
if not self.camera_node:
|
||
return
|
||
|
||
camera_pos = self.camera_node.getPos()
|
||
|
||
# 根据策略更新每个模型的LOD级别
|
||
for model_id, model_data in models.items():
|
||
if self.strategy == LODStrategy.DISTANCE_BASED:
|
||
self._update_distance_based_lod(model_id, model_data, camera_pos)
|
||
elif self.strategy == LODStrategy.PERFORMANCE_BASED:
|
||
self._update_performance_based_lod(model_id, model_data)
|
||
elif self.strategy == LODStrategy.HYBRID:
|
||
self._update_hybrid_lod(model_id, model_data, camera_pos)
|
||
|
||
def _update_distance_based_lod(self, model_id: str, model_data: Any, camera_pos: Vec3) -> None:
|
||
"""
|
||
更新基于距离的LOD
|
||
:param model_id: 模型ID
|
||
:param model_data: 模型数据
|
||
:param camera_pos: 相机位置
|
||
"""
|
||
try:
|
||
model_pos = model_data['node_path'].getPos()
|
||
distance = (camera_pos - model_pos).length()
|
||
|
||
# 找到合适的LOD级别
|
||
lod_level = len(self.lod_levels) - 1 # 默认最高质量
|
||
for i, level in enumerate(self.lod_levels):
|
||
if distance <= level.distance_threshold:
|
||
lod_level = i
|
||
break
|
||
|
||
self.model_lod_levels[model_id] = lod_level
|
||
|
||
except Exception as e:
|
||
print(f"更新模型 {model_id} 的距离LOD时出错: {e}")
|
||
|
||
def _update_performance_based_lod(self, model_id: str, model_data: Any) -> None:
|
||
"""
|
||
更新基于性能的LOD
|
||
:param model_id: 模型ID
|
||
:param model_data: 模型数据
|
||
"""
|
||
# 基于FPS或其他性能指标调整LOD
|
||
# 这里简化处理,实际项目中需要获取性能数据
|
||
current_fps = 60.0 # 假设值
|
||
|
||
# 根据FPS调整LOD级别
|
||
if current_fps < 30:
|
||
lod_level = min(2, len(self.lod_levels) - 1)
|
||
elif current_fps < 45:
|
||
lod_level = min(1, len(self.lod_levels) - 1)
|
||
else:
|
||
lod_level = 0
|
||
|
||
self.model_lod_levels[model_id] = lod_level
|
||
|
||
def _update_hybrid_lod(self, model_id: str, model_data: Any, camera_pos: Vec3) -> None:
|
||
"""
|
||
更新混合LOD
|
||
:param model_id: 模型ID
|
||
:param model_data: 模型数据
|
||
:param camera_pos: 相机位置
|
||
"""
|
||
# 结合距离和性能的混合策略
|
||
self._update_distance_based_lod(model_id, model_data, camera_pos)
|
||
# 可以进一步根据性能调整
|
||
|
||
def get_model_lod_level(self, model_id: str) -> Optional[int]:
|
||
"""
|
||
获取模型的LOD级别
|
||
:param model_id: 模型ID
|
||
:return: LOD级别
|
||
"""
|
||
return self.model_lod_levels.get(model_id)
|
||
|
||
def get_lod_info(self, model_id: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取模型的LOD信息
|
||
:param model_id: 模型ID
|
||
:return: LOD信息字典
|
||
"""
|
||
lod_level = self.model_lod_levels.get(model_id)
|
||
if lod_level is None or lod_level >= len(self.lod_levels):
|
||
return None
|
||
|
||
level_info = self.lod_levels[lod_level]
|
||
|
||
return {
|
||
'model_id': model_id,
|
||
'lod_level': lod_level,
|
||
'distance_threshold': level_info.distance_threshold,
|
||
'frame_rate': level_info.frame_rate,
|
||
'quality_factor': level_info.quality_factor
|
||
}
|
||
|
||
def apply_lod_to_model(self, model_id: str, morphing_core) -> None:
|
||
"""
|
||
应用LOD到模型
|
||
:param model_id: 模型ID
|
||
:param morphing_core: Morphing核心系统
|
||
"""
|
||
lod_level = self.model_lod_levels.get(model_id)
|
||
if lod_level is None or lod_level >= len(self.lod_levels):
|
||
return
|
||
|
||
level_info = self.lod_levels[lod_level]
|
||
|
||
# 这里可以应用LOD调整,例如:
|
||
# 1. 降低更新频率
|
||
# 2. 减少变形精度
|
||
# 3. 简化morph目标
|
||
# 4. 降低渲染质量
|
||
|
||
# 示例:根据质量因子调整morph目标权重
|
||
if model_id in morphing_core.morph_targets:
|
||
for target_name, morph_target in morphing_core.morph_targets[model_id].items():
|
||
# 简化处理,实际项目中需要更复杂的逻辑
|
||
adjusted_weights = [w * level_info.quality_factor for w in morph_target.weights]
|
||
morph_target.weights = adjusted_weights
|
||
|
||
class AnimationCacheManager:
|
||
"""
|
||
动画缓存管理器
|
||
提供高效的动画数据缓存机制
|
||
"""
|
||
|
||
def __init__(self):
|
||
"""初始化动画缓存管理器"""
|
||
self.max_size = OPTIMIZATION_SETTINGS['cache_size']
|
||
self.cache: Dict[str, CacheEntry] = {}
|
||
self.access_order: List[str] = [] # 最近最少使用顺序
|
||
self.stats = {
|
||
'hits': 0,
|
||
'misses': 0,
|
||
'evictions': 0
|
||
}
|
||
|
||
print("动画缓存管理器初始化完成")
|
||
|
||
def get(self, key: str) -> Optional[Any]:
|
||
"""
|
||
获取缓存项
|
||
:param key: 缓存键
|
||
:return: 缓存数据
|
||
"""
|
||
if key in self.cache:
|
||
# 缓存命中
|
||
self.stats['hits'] += 1
|
||
|
||
# 更新访问时间
|
||
entry = self.cache[key]
|
||
entry.access_time = globalClock.getRealTime()
|
||
entry.access_count += 1
|
||
|
||
# 更新访问顺序
|
||
if key in self.access_order:
|
||
self.access_order.remove(key)
|
||
self.access_order.append(key)
|
||
|
||
return entry.data
|
||
else:
|
||
# 缓存未命中
|
||
self.stats['misses'] += 1
|
||
return None
|
||
|
||
def put(self, key: str, data: Any, size: int = 1) -> None:
|
||
"""
|
||
放入缓存项
|
||
:param key: 缓存键
|
||
:param data: 缓存数据
|
||
:param size: 数据大小
|
||
"""
|
||
# 如果缓存已满,移除最久未使用的项
|
||
while len(self.cache) >= self.max_size and self.cache:
|
||
# 找到最久未使用的项
|
||
if self.access_order:
|
||
oldest_key = self.access_order.pop(0)
|
||
del self.cache[oldest_key]
|
||
self.stats['evictions'] += 1
|
||
else:
|
||
# 如果访问顺序列表为空,移除任意项
|
||
oldest_key = next(iter(self.cache))
|
||
del self.cache[oldest_key]
|
||
self.stats['evictions'] += 1
|
||
|
||
# 创建缓存条目
|
||
entry = CacheEntry(
|
||
key=key,
|
||
data=data,
|
||
size=size,
|
||
access_time=globalClock.getRealTime(),
|
||
access_count=1
|
||
)
|
||
|
||
# 存储缓存条目
|
||
self.cache[key] = entry
|
||
self.access_order.append(key)
|
||
|
||
def invalidate(self, key: str) -> None:
|
||
"""
|
||
使缓存项失效
|
||
:param key: 缓存键
|
||
"""
|
||
if key in self.cache:
|
||
del self.cache[key]
|
||
if key in self.access_order:
|
||
self.access_order.remove(key)
|
||
|
||
def clear(self) -> None:
|
||
"""
|
||
清空缓存
|
||
"""
|
||
self.cache.clear()
|
||
self.access_order.clear()
|
||
|
||
def get_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取缓存统计信息
|
||
:return: 统计信息字典
|
||
"""
|
||
total_requests = self.stats['hits'] + self.stats['misses']
|
||
hit_rate = self.stats['hits'] / max(1, total_requests)
|
||
|
||
return {
|
||
'size': len(self.cache),
|
||
'max_size': self.max_size,
|
||
'hits': self.stats['hits'],
|
||
'misses': self.stats['misses'],
|
||
'evictions': self.stats['evictions'],
|
||
'hit_rate': hit_rate,
|
||
'total_requests': total_requests
|
||
}
|
||
|
||
def get_cache_info(self, key: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取缓存项信息
|
||
:param key: 缓存键
|
||
:return: 缓存项信息字典
|
||
"""
|
||
if key not in self.cache:
|
||
return None
|
||
|
||
entry = self.cache[key]
|
||
|
||
return {
|
||
'key': entry.key,
|
||
'size': entry.size,
|
||
'access_time': entry.access_time,
|
||
'access_count': entry.access_count
|
||
}
|
||
|
||
# 使用示例和测试代码
|
||
def example_compressor_usage():
|
||
"""
|
||
动画压缩系统使用示例
|
||
"""
|
||
print("=== 动画压缩系统使用示例 ===")
|
||
|
||
# 创建压缩器
|
||
compressor = AnimationCompressor()
|
||
|
||
# 创建测试数据
|
||
test_data = [
|
||
(0.0, 0.0, 0.0),
|
||
(0.1, 0.05, 0.02),
|
||
(0.2, 0.1, 0.04),
|
||
(0.3, 0.15, 0.06),
|
||
(0.4, 0.2, 0.08),
|
||
(0.5, 0.25, 0.1),
|
||
(0.6, 0.3, 0.12),
|
||
(0.7, 0.35, 0.14),
|
||
(0.8, 0.4, 0.16),
|
||
(0.9, 0.45, 0.18),
|
||
(1.0, 0.5, 0.2)
|
||
]
|
||
|
||
# 测试不同压缩方法
|
||
methods = [
|
||
CompressionMethod.KEYFRAME_REDUCTION,
|
||
CompressionMethod.QUANTIZATION,
|
||
CompressionMethod.DELTA_COMPRESSION,
|
||
CompressionMethod.RLE
|
||
]
|
||
|
||
for method in methods:
|
||
print(f"\n测试 {method.name} 压缩:")
|
||
compressed = compressor.compress_morph_data(test_data, method)
|
||
if compressed:
|
||
print(f" 原始大小: {compressed.original_size} 字节")
|
||
print(f" 压缩后大小: {compressed.compressed_size} 字节")
|
||
print(f" 压缩比: {compressed.compression_ratio:.2f}")
|
||
|
||
# 测试解压缩
|
||
decompressed = compressor.decompress_morph_data(compressed)
|
||
if decompressed:
|
||
print(f" 解压数据点数: {len(decompressed)}")
|
||
|
||
# 获取统计信息
|
||
stats = compressor.get_compression_stats()
|
||
print(f"\n压缩统计: {stats}")
|
||
|
||
print("=== 动画压缩系统示例结束 ===\n")
|
||
|
||
def example_lod_usage(world):
|
||
"""
|
||
动画LOD系统使用示例
|
||
"""
|
||
print("=== 动画LOD系统使用示例 ===")
|
||
|
||
# 创建LOD系统
|
||
lod_system = AnimationLODSystem(world)
|
||
|
||
# 设置LOD策略
|
||
lod_system.set_lod_strategy(LODStrategy.DISTANCE_BASED)
|
||
|
||
# 设置距离阈值
|
||
lod_system.set_distance_thresholds([5.0, 15.0, 30.0])
|
||
|
||
# 设置帧率
|
||
lod_system.set_frame_rates([1.0, 0.7, 0.4, 0.1])
|
||
|
||
print("LOD系统配置完成")
|
||
print("=== 动画LOD系统示例结束 ===\n")
|
||
|
||
return lod_system
|
||
|
||
def example_cache_usage():
|
||
"""
|
||
动画缓存管理器使用示例
|
||
"""
|
||
print("=== 动画缓存管理器使用示例 ===")
|
||
|
||
# 创建缓存管理器
|
||
cache_manager = AnimationCacheManager()
|
||
|
||
# 添加缓存项
|
||
cache_manager.put("test_key_1", "test_data_1", 10)
|
||
cache_manager.put("test_key_2", "test_data_2", 20)
|
||
cache_manager.put("test_key_3", "test_data_3", 30)
|
||
|
||
# 获取缓存项
|
||
data1 = cache_manager.get("test_key_1")
|
||
print(f"获取缓存项1: {data1}")
|
||
|
||
data2 = cache_manager.get("test_key_2")
|
||
print(f"获取缓存项2: {data2}")
|
||
|
||
# 测试缓存未命中
|
||
data4 = cache_manager.get("test_key_4")
|
||
print(f"获取不存在的缓存项: {data4}")
|
||
|
||
# 获取统计信息
|
||
stats = cache_manager.get_stats()
|
||
print(f"缓存统计: {stats}")
|
||
|
||
# 获取缓存项信息
|
||
info = cache_manager.get_cache_info("test_key_1")
|
||
print(f"缓存项信息: {info}")
|
||
|
||
print("=== 动画缓存管理器示例结束 ===\n")
|
||
|
||
if __name__ == "__main__":
|
||
example_compressor_usage()
|
||
# example_lod_usage(None) # 需要world对象
|
||
example_cache_usage() |