""" Morphing和变形动画插件 - 核心模块 提供顶点变形、morphing目标、网格变形等核心功能 """ import math import numpy as np from typing import Dict, List, Tuple, Optional, Any, Callable from dataclasses import dataclass, field from enum import Enum from panda3d.core import * from direct.actor.Actor import Actor from direct.showbase.DirectObject import DirectObject # 定义常量 MORPHING_SETTINGS = { 'max_morph_targets': 100, 'default_interpolation': 'linear', 'update_rate': 60, 'cache_size': 200 } # 枚举定义 class MorphingMode(Enum): """Morphing模式枚举""" VERTEX_BLEND = 0 # 顶点混合 NORMAL_BLEND = 1 # 法线混合 TANGENT_BLEND = 2 # 切线混合 FULL_BLEND = 3 # 完全混合 class InterpolationMode(Enum): """插值模式枚举""" LINEAR = 0 # 线性插值 EASE_IN = 1 # 缓入 EASE_OUT = 2 # 缓出 EASE_IN_OUT = 3 # 缓入缓出 SPLINE = 4 # 样条插值 class DeformationType(Enum): """变形类型枚举""" MORPH_TARGET = 0 # Morph目标 VERTEX_ANIMATION = 1 # 顶点动画 SKELETAL_DEFORM = 2 # 骨骼变形 PROCEDURAL = 3 # 程序化变形 @dataclass class MorphTarget: """Morph目标数据类""" name: str vertices: List[Tuple[float, float, float]] # 顶点位置偏移 normals: Optional[List[Tuple[float, float, float]]] = None # 法线偏移 tangents: Optional[List[Tuple[float, float, float]]] = None # 切线偏移 weights: List[float] = field(default_factory=list) # 权重列表 active: bool = True @dataclass class MorphingAnimation: """Morphing动画数据类""" name: str target_weights: Dict[str, float] # 目标权重 {target_name: weight} duration: float # 持续时间 interpolation: InterpolationMode = InterpolationMode.LINEAR loop: bool = False start_time: float = 0.0 current_time: float = 0.0 playing: bool = False paused: bool = False @dataclass class VertexDeformation: """顶点变形数据类""" name: str vertex_indices: List[int] # 顶点索引 target_positions: List[Tuple[float, float, float]] # 目标位置 duration: float # 持续时间 start_time: float = 0.0 current_time: float = 0.0 active: bool = True interpolation: InterpolationMode = InterpolationMode.LINEAR @dataclass class ProceduralDeformation: """程序化变形数据类""" name: str deformation_function: Callable # 变形函数 parameters: Dict[str, Any] # 参数 update_rate: float = 1.0 # 更新频率 last_update: float = 0.0 active: bool = True class MorphingCache: """Morphing缓存系统""" def __init__(self, max_size: int = 200): self.max_size = max_size self.cache: Dict[str, Any] = {} self.access_order: List[str] = [] def get(self, key: str) -> Optional[Any]: """获取缓存项""" if key in self.cache: # 更新访问顺序 if key in self.access_order: self.access_order.remove(key) self.access_order.append(key) return self.cache[key] return None def put(self, key: str, value: Any) -> None: """放入缓存项""" # 如果缓存已满,移除最久未使用的项 if len(self.cache) >= self.max_size: if self.access_order: oldest_key = self.access_order.pop(0) del self.cache[oldest_key] self.cache[key] = value self.access_order.append(key) def invalidate(self, key: str) -> None: """使缓存项失效""" 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() class MorphingCore(DirectObject): """ Morphing核心系统 提供完整的顶点变形、morphing目标、网格变形等功能 """ def __init__(self, world): """ 初始化Morphing核心系统 :param world: 世界对象 """ super().__init__() self.world = world self.morph_targets: Dict[str, Dict[str, MorphTarget]] = {} # {model_id: {target_name: MorphTarget}} self.morphing_animations: Dict[str, Dict[str, MorphingAnimation]] = {} # {model_id: {anim_name: MorphingAnimation}} self.vertex_deformations: Dict[str, List[VertexDeformation]] = {} # {model_id: [VertexDeformation]} self.procedural_deformations: Dict[str, List[ProceduralDeformation]] = {} # {model_id: [ProceduralDeformation]} self.model_data: Dict[str, Dict[str, Any]] = {} # {model_id: {原始数据}} self.cache = MorphingCache(MORPHING_SETTINGS['cache_size']) self.is_updating = False self.update_task = None self.update_rate = MORPHING_SETTINGS['update_rate'] # 性能统计 self.stats = { 'models_processed': 0, 'morph_targets_created': 0, 'animations_played': 0, 'deformations_applied': 0, 'cache_hits': 0, 'cache_misses': 0 } print("Morphing核心系统初始化完成") def register_model(self, model_node: NodePath, model_id: Optional[str] = None) -> str: """ 注册模型以支持morphing :param model_node: 模型节点 :param model_id: 模型ID,如果为None则自动生成 :return: 模型ID """ if model_id is None: model_id = f"model_{len(self.model_data)}" # 检查模型是否已注册 if model_id in self.model_data: print(f"警告: 模型 {model_id} 已注册,将被替换") self.unregister_model(model_id) # 提取模型数据 model_data = self._extract_model_data(model_node) if not model_data: print(f"错误: 无法提取模型 {model_id} 的数据") return "" # 存储模型数据 self.model_data[model_id] = model_data self.morph_targets[model_id] = {} self.morphing_animations[model_id] = {} self.vertex_deformations[model_id] = [] self.procedural_deformations[model_id] = [] self.stats['models_processed'] += 1 print(f"模型已注册: {model_id}") return model_id def unregister_model(self, model_id: str) -> bool: """ 注销模型 :param model_id: 模型ID :return: 是否成功注销 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return False # 清理相关数据 if model_id in self.morph_targets: del self.morph_targets[model_id] if model_id in self.morphing_animations: del self.morphing_animations[model_id] if model_id in self.vertex_deformations: del self.vertex_deformations[model_id] if model_id in self.procedural_deformations: del self.procedural_deformations[model_id] if model_id in self.model_data: del self.model_data[model_id] # 使相关缓存失效 cache_keys_to_invalidate = [key for key in self.cache.cache.keys() if key.startswith(f"{model_id}_")] for key in cache_keys_to_invalidate: self.cache.invalidate(key) print(f"模型已注销: {model_id}") return True def _extract_model_data(self, model_node: NodePath) -> Optional[Dict[str, Any]]: """ 提取模型数据 :param model_node: 模型节点 :return: 模型数据字典 """ try: # 查找GeomNode geom_nodes = model_node.findAllMatches("**/+GeomNode") if not geom_nodes: return None model_data = { 'node_path': model_node, 'geom_nodes': [], 'vertex_data': [], 'original_vertices': [], 'original_normals': [], 'original_tangents': [] } # 提取每个GeomNode的数据 for geom_node in geom_nodes: geom_node_obj = geom_node.node() for i in range(geom_node_obj.getNumGeoms()): geom = geom_node_obj.getGeom(i) vertex_data = geom.getVertexData() model_data['geom_nodes'].append(geom_node) model_data['vertex_data'].append(vertex_data) # 提取原始顶点数据 vertex_reader = GeomVertexReader(vertex_data, 'vertex') vertices = [] while not vertex_reader.isAtEnd(): vertex = vertex_reader.getData3f() vertices.append((vertex.x, vertex.y, vertex.z)) model_data['original_vertices'].append(vertices) # 提取原始法线数据(如果存在) if vertex_data.hasColumn('normal'): normal_reader = GeomVertexReader(vertex_data, 'normal') normals = [] while not normal_reader.isAtEnd(): normal = normal_reader.getData3f() normals.append((normal.x, normal.y, normal.z)) model_data['original_normals'].append(normals) # 提取原始切线数据(如果存在) if vertex_data.hasColumn('tangent'): tangent_reader = GeomVertexReader(vertex_data, 'tangent') tangents = [] while not tangent_reader.isAtEnd(): tangent = tangent_reader.getData3f() tangents.append((tangent.x, tangent.y, tangent.z)) model_data['original_tangents'].append(tangents) return model_data except Exception as e: print(f"提取模型数据时出错: {e}") import traceback traceback.print_exc() return None def create_morph_target(self, model_id: str, target_name: str, vertex_offsets: List[Tuple[float, float, float]], normal_offsets: Optional[List[Tuple[float, float, float]]] = None, tangent_offsets: Optional[List[Tuple[float, float, float]]] = None) -> bool: """ 创建Morph目标 :param model_id: 模型ID :param target_name: 目标名称 :param vertex_offsets: 顶点偏移列表 :param normal_offsets: 法线偏移列表(可选) :param tangent_offsets: 切线偏移列表(可选) :return: 是否成功创建 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return False # 检查模型是否有足够的顶点 total_vertices = sum(len(vertices) for vertices in self.model_data[model_id]['original_vertices']) if len(vertex_offsets) != total_vertices: print(f"错误: 顶点偏移数量 ({len(vertex_offsets)}) 与模型顶点数量 ({total_vertices}) 不匹配") return False # 创建Morph目标 morph_target = MorphTarget( name=target_name, vertices=vertex_offsets, normals=normal_offsets, tangents=tangent_offsets, weights=[0.0] * len(vertex_offsets) ) # 存储Morph目标 self.morph_targets[model_id][target_name] = morph_target self.stats['morph_targets_created'] += 1 print(f"Morph目标已创建: {target_name} (模型: {model_id})") return True def remove_morph_target(self, model_id: str, target_name: str) -> bool: """ 移除Morph目标 :param model_id: 模型ID :param target_name: 目标名称 :return: 是否成功移除 """ if model_id not in self.morph_targets or target_name not in self.morph_targets[model_id]: print(f"错误: Morph目标 {target_name} 不存在于模型 {model_id} 中") return False del self.morph_targets[model_id][target_name] print(f"Morph目标已移除: {target_name} (模型: {model_id})") return True def set_morph_target_weight(self, model_id: str, target_name: str, weight: float) -> bool: """ 设置Morph目标权重 :param model_id: 模型ID :param target_name: 目标名称 :param weight: 权重值 (0.0-1.0) :return: 是否成功设置 """ if model_id not in self.morph_targets or target_name not in self.morph_targets[model_id]: print(f"错误: Morph目标 {target_name} 不存在于模型 {model_id} 中") return False # 限制权重范围 weight = max(0.0, min(1.0, weight)) # 更新权重 morph_target = self.morph_targets[model_id][target_name] morph_target.weights = [weight] * len(morph_target.weights) # 应用变形 self._apply_morph_target(model_id, target_name, weight) return True def _apply_morph_target(self, model_id: str, target_name: str, weight: float) -> None: """ 应用Morph目标 :param model_id: 模型ID :param target_name: 目标名称 :param weight: 权重值 """ if model_id not in self.morph_targets or target_name not in self.morph_targets[model_id]: return morph_target = self.morph_targets[model_id][target_name] model_data = self.model_data[model_id] # 应用到每个GeomNode vertex_offset_index = 0 for i, (geom_node, vertex_data) in enumerate(zip(model_data['geom_nodes'], model_data['vertex_data'])): # 创建可写的顶点数据 writable_vertex_data = vertex_data.copy() # 更新顶点位置 vertex_writer = GeomVertexWriter(writable_vertex_data, 'vertex') original_vertices = model_data['original_vertices'][i] for j, original_vertex in enumerate(original_vertices): if vertex_offset_index + j < len(morph_target.vertices): offset = morph_target.vertices[vertex_offset_index + j] new_vertex = ( original_vertex[0] + offset[0] * weight, original_vertex[1] + offset[1] * weight, original_vertex[2] + offset[2] * weight ) vertex_writer.setRow(j) vertex_writer.setData3f(new_vertex[0], new_vertex[1], new_vertex[2]) # 更新法线(如果存在) if morph_target.normals and writable_vertex_data.hasColumn('normal'): normal_writer = GeomVertexWriter(writable_vertex_data, 'normal') original_normals = model_data['original_normals'][i] if i < len(model_data['original_normals']) else [] for j, original_normal in enumerate(original_normals): if vertex_offset_index + j < len(morph_target.normals): offset = morph_target.normals[vertex_offset_index + j] new_normal = ( original_normal[0] + offset[0] * weight, original_normal[1] + offset[1] * weight, original_normal[2] + offset[2] * weight ) normal_writer.setRow(j) normal_writer.setData3f(new_normal[0], new_normal[1], new_normal[2]) # 更新切线(如果存在) if morph_target.tangents and writable_vertex_data.hasColumn('tangent'): tangent_writer = GeomVertexWriter(writable_vertex_data, 'tangent') original_tangents = model_data['original_tangents'][i] if i < len(model_data['original_tangents']) else [] for j, original_tangent in enumerate(original_tangents): if vertex_offset_index + j < len(morph_target.tangents): offset = morph_target.tangents[vertex_offset_index + j] new_tangent = ( original_tangent[0] + offset[0] * weight, original_tangent[1] + offset[1] * weight, original_tangent[2] + offset[2] * weight ) tangent_writer.setRow(j) tangent_writer.setData3f(new_tangent[0], new_tangent[1], new_tangent[2]) # 更新GeomNode的顶点数据 geom_node.node().modifyGeom(0).setVertexData(writable_vertex_data) # 更新顶点偏移索引 vertex_offset_index += len(original_vertices) self.stats['deformations_applied'] += 1 def create_morphing_animation(self, model_id: str, anim_name: str, target_weights: Dict[str, float], duration: float, interpolation: InterpolationMode = InterpolationMode.LINEAR, loop: bool = False) -> bool: """ 创建Morphing动画 :param model_id: 模型ID :param anim_name: 动画名称 :param target_weights: 目标权重字典 {target_name: weight} :param duration: 持续时间 :param interpolation: 插值模式 :param loop: 是否循环 :return: 是否成功创建 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return False # 验证Morph目标是否存在 for target_name in target_weights.keys(): if target_name not in self.morph_targets[model_id]: print(f"错误: Morph目标 {target_name} 不存在于模型 {model_id} 中") return False # 创建动画 animation = MorphingAnimation( name=anim_name, target_weights=target_weights, duration=duration, interpolation=interpolation, loop=loop, start_time=globalClock.getRealTime(), current_time=0.0, playing=False, paused=False ) # 存储动画 self.morphing_animations[model_id][anim_name] = animation print(f"Morphing动画已创建: {anim_name} (模型: {model_id})") return True def play_morphing_animation(self, model_id: str, anim_name: str) -> bool: """ 播放Morphing动画 :param model_id: 模型ID :param anim_name: 动画名称 :return: 是否成功播放 """ if (model_id not in self.morphing_animations or anim_name not in self.morphing_animations[model_id]): print(f"错误: Morphing动画 {anim_name} 不存在于模型 {model_id} 中") return False animation = self.morphing_animations[model_id][anim_name] animation.playing = True animation.paused = False animation.start_time = globalClock.getRealTime() animation.current_time = 0.0 self.stats['animations_played'] += 1 print(f"Morphing动画开始播放: {anim_name} (模型: {model_id})") return True def stop_morphing_animation(self, model_id: str, anim_name: Optional[str] = None) -> bool: """ 停止Morphing动画 :param model_id: 模型ID :param anim_name: 动画名称,如果为None则停止所有动画 :return: 是否成功停止 """ if model_id not in self.morphing_animations: print(f"错误: 模型 {model_id} 未注册") return False if anim_name: # 停止单个动画 if anim_name not in self.morphing_animations[model_id]: print(f"错误: Morphing动画 {anim_name} 不存在于模型 {model_id} 中") return False animation = self.morphing_animations[model_id][anim_name] animation.playing = False animation.paused = False print(f"Morphing动画已停止: {anim_name} (模型: {model_id})") else: # 停止所有动画 for animation in self.morphing_animations[model_id].values(): animation.playing = False animation.paused = False print(f"所有Morphing动画已停止 (模型: {model_id})") return True def pause_morphing_animation(self, model_id: str, anim_name: str) -> bool: """ 暂停Morphing动画 :param model_id: 模型ID :param anim_name: 动画名称 :return: 是否成功暂停 """ if (model_id not in self.morphing_animations or anim_name not in self.morphing_animations[model_id]): print(f"错误: Morphing动画 {anim_name} 不存在于模型 {model_id} 中") return False animation = self.morphing_animations[model_id][anim_name] if animation.playing and not animation.paused: animation.paused = True print(f"Morphing动画已暂停: {anim_name} (模型: {model_id})") return True return False def resume_morphing_animation(self, model_id: str, anim_name: str) -> bool: """ 恢复Morphing动画 :param model_id: 模型ID :param anim_name: 动画名称 :return: 是否成功恢复 """ if (model_id not in self.morphing_animations or anim_name not in self.morphing_animations[model_id]): print(f"错误: Morphing动画 {anim_name} 不存在于模型 {model_id} 中") return False animation = self.morphing_animations[model_id][anim_name] if animation.playing and animation.paused: animation.paused = False # 调整开始时间以补偿暂停时间 animation.start_time = globalClock.getRealTime() - animation.current_time print(f"Morphing动画已恢复: {anim_name} (模型: {model_id})") return True return False def create_vertex_deformation(self, model_id: str, deformation_name: str, vertex_indices: List[int], target_positions: List[Tuple[float, float, float]], duration: float, interpolation: InterpolationMode = InterpolationMode.LINEAR) -> bool: """ 创建顶点变形 :param model_id: 模型ID :param deformation_name: 变形名称 :param vertex_indices: 顶点索引列表 :param target_positions: 目标位置列表 :param duration: 持续时间 :param interpolation: 插值模式 :return: 是否成功创建 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return False if len(vertex_indices) != len(target_positions): print(f"错误: 顶点索引数量 ({len(vertex_indices)}) 与目标位置数量 ({len(target_positions)}) 不匹配") return False # 创建顶点变形 deformation = VertexDeformation( name=deformation_name, vertex_indices=vertex_indices, target_positions=target_positions, duration=duration, interpolation=interpolation, start_time=globalClock.getRealTime(), current_time=0.0, active=True ) # 存储变形 self.vertex_deformations[model_id].append(deformation) print(f"顶点变形已创建: {deformation_name} (模型: {model_id})") return True def remove_vertex_deformation(self, model_id: str, deformation_name: str) -> bool: """ 移除顶点变形 :param model_id: 模型ID :param deformation_name: 变形名称 :return: 是否成功移除 """ if model_id not in self.vertex_deformations: print(f"错误: 模型 {model_id} 未注册") return False # 查找并移除变形 deformations = self.vertex_deformations[model_id] for i, deformation in enumerate(deformations): if deformation.name == deformation_name: deformations.pop(i) print(f"顶点变形已移除: {deformation_name} (模型: {model_id})") return True print(f"错误: 顶点变形 {deformation_name} 不存在于模型 {model_id} 中") return False def add_procedural_deformation(self, model_id: str, deformation_name: str, deformation_function: Callable, parameters: Dict[str, Any], update_rate: float = 1.0) -> bool: """ 添加程序化变形 :param model_id: 模型ID :param deformation_name: 变形名称 :param deformation_function: 变形函数 :param parameters: 参数字典 :param update_rate: 更新频率 :return: 是否成功添加 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return False # 创建程序化变形 deformation = ProceduralDeformation( name=deformation_name, deformation_function=deformation_function, parameters=parameters, update_rate=update_rate, last_update=globalClock.getRealTime(), active=True ) # 存储变形 self.procedural_deformations[model_id].append(deformation) print(f"程序化变形已添加: {deformation_name} (模型: {model_id})") return True def remove_procedural_deformation(self, model_id: str, deformation_name: str) -> bool: """ 移除程序化变形 :param model_id: 模型ID :param deformation_name: 变形名称 :return: 是否成功移除 """ if model_id not in self.procedural_deformations: print(f"错误: 模型 {model_id} 未注册") return False # 查找并移除变形 deformations = self.procedural_deformations[model_id] for i, deformation in enumerate(deformations): if deformation.name == deformation_name: deformations.pop(i) print(f"程序化变形已移除: {deformation_name} (模型: {model_id})") return True print(f"错误: 程序化变形 {deformation_name} 不存在于模型 {model_id} 中") return False def update(self, dt: float) -> None: """ 更新Morphing系统 :param dt: 时间增量 """ if not self.is_updating: return current_time = globalClock.getRealTime() # 更新每个模型 for model_id in self.model_data.keys(): # 更新Morphing动画 self._update_morphing_animations(model_id, current_time, dt) # 更新顶点变形 self._update_vertex_deformations(model_id, current_time, dt) # 更新程序化变形 self._update_procedural_deformations(model_id, current_time, dt) def _update_morphing_animations(self, model_id: str, current_time: float, dt: float) -> None: """ 更新Morphing动画 :param model_id: 模型ID :param current_time: 当前时间 :param dt: 时间增量 """ if model_id not in self.morphing_animations: return # 更新每个动画 animations_to_remove = [] for anim_name, animation in self.morphing_animations[model_id].items(): if not animation.playing or animation.paused: continue # 更新动画时间 animation.current_time = current_time - animation.start_time # 检查动画是否完成 if animation.current_time >= animation.duration: if animation.loop: # 循环播放 animation.start_time = current_time animation.current_time = 0.0 else: # 停止动画 animation.playing = False continue # 计算插值因子 t = animation.current_time / animation.duration if animation.interpolation == InterpolationMode.EASE_IN: t = t * t elif animation.interpolation == InterpolationMode.EASE_OUT: t = 1.0 - (1.0 - t) * (1.0 - t) elif animation.interpolation == InterpolationMode.EASE_IN_OUT: t = t * t * (3.0 - 2.0 * t) elif animation.interpolation == InterpolationMode.SPLINE: # 简化的样条插值 t = t * t * (3.0 - 2.0 * t) # 应用每个目标的权重 for target_name, target_weight in animation.target_weights.items(): self.set_morph_target_weight(model_id, target_name, target_weight * t) def _update_vertex_deformations(self, model_id: str, current_time: float, dt: float) -> None: """ 更新顶点变形 :param model_id: 模型ID :param current_time: 当前时间 :param dt: 时间增量 """ if model_id not in self.vertex_deformations: return # 更新每个变形 deformations = self.vertex_deformations[model_id] active_deformations = [d for d in deformations if d.active] if not active_deformations: return model_data = self.model_data[model_id] # 对每个GeomNode应用变形 for i, (geom_node, vertex_data) in enumerate(zip(model_data['geom_nodes'], model_data['vertex_data'])): # 创建可写的顶点数据 writable_vertex_data = vertex_data.copy() vertex_writer = GeomVertexWriter(writable_vertex_data, 'vertex') original_vertices = model_data['original_vertices'][i] # 应用每个活动变形 for deformation in active_deformations: # 更新变形时间 deformation.current_time = current_time - deformation.start_time # 检查变形是否完成 if deformation.current_time >= deformation.duration: deformation.active = False continue # 计算插值因子 t = deformation.current_time / deformation.duration if deformation.interpolation == InterpolationMode.EASE_IN: t = t * t elif deformation.interpolation == InterpolationMode.EASE_OUT: t = 1.0 - (1.0 - t) * (1.0 - t) elif deformation.interpolation == InterpolationMode.EASE_IN_OUT: t = t * t * (3.0 - 2.0 * t) elif deformation.interpolation == InterpolationMode.SPLINE: t = t * t * (3.0 - 2.0 * t) # 应用变形到指定顶点 for j, vertex_index in enumerate(deformation.vertex_indices): if vertex_index < len(original_vertices): original_vertex = original_vertices[vertex_index] target_position = deformation.target_positions[j] # 计算插值位置 interpolated_position = ( original_vertex[0] + (target_position[0] - original_vertex[0]) * t, original_vertex[1] + (target_position[1] - original_vertex[1]) * t, original_vertex[2] + (target_position[2] - original_vertex[2]) * t ) # 更新顶点位置 vertex_writer.setRow(vertex_index) vertex_writer.setData3f( interpolated_position[0], interpolated_position[1], interpolated_position[2] ) # 更新GeomNode的顶点数据 geom_node.node().modifyGeom(0).setVertexData(writable_vertex_data) # 移除已完成的变形 self.vertex_deformations[model_id] = [d for d in deformations if d.active] def _update_procedural_deformations(self, model_id: str, current_time: float, dt: float) -> None: """ 更新程序化变形 :param model_id: 模型ID :param current_time: 当前时间 :param dt: 时间增量 """ if model_id not in self.procedural_deformations: return # 更新每个变形 deformations = self.procedural_deformations[model_id] active_deformations = [d for d in deformations if d.active] for deformation in active_deformations: # 检查更新时间 if current_time - deformation.last_update < 1.0 / deformation.update_rate: continue deformation.last_update = current_time try: # 调用变形函数 deformation.deformation_function( self.model_data[model_id]['node_path'], **deformation.parameters ) except Exception as e: print(f"执行程序化变形函数时出错: {e}") # 移除非活动变形 self.procedural_deformations[model_id] = [d for d in deformations if d.active] def get_model_info(self, model_id: str) -> Optional[Dict[str, Any]]: """ 获取模型信息 :param model_id: 模型ID :return: 模型信息字典 """ if model_id not in self.model_data: print(f"错误: 模型 {model_id} 未注册") return None model_data = self.model_data[model_id] return { 'model_id': model_id, 'morph_targets': list(self.morph_targets[model_id].keys()), 'morphing_animations': list(self.morphing_animations[model_id].keys()), 'vertex_deformations': [d.name for d in self.vertex_deformations[model_id]], 'procedural_deformations': [d.name for d in self.procedural_deformations[model_id]], 'vertex_count': sum(len(vertices) for vertices in model_data['original_vertices']), 'geom_node_count': len(model_data['geom_nodes']) } def get_morph_target_info(self, model_id: str, target_name: str) -> Optional[Dict[str, Any]]: """ 获取Morph目标信息 :param model_id: 模型ID :param target_name: 目标名称 :return: Morph目标信息字典 """ if (model_id not in self.morph_targets or target_name not in self.morph_targets[model_id]): print(f"错误: Morph目标 {target_name} 不存在于模型 {model_id} 中") return None morph_target = self.morph_targets[model_id][target_name] return { 'name': morph_target.name, 'vertex_count': len(morph_target.vertices), 'has_normals': morph_target.normals is not None, 'has_tangents': morph_target.tangents is not None, 'active': morph_target.active } def get_animation_info(self, model_id: str, anim_name: str) -> Optional[Dict[str, Any]]: """ 获取动画信息 :param model_id: 模型ID :param anim_name: 动画名称 :return: 动画信息字典 """ if (model_id not in self.morphing_animations or anim_name not in self.morphing_animations[model_id]): print(f"错误: Morphing动画 {anim_name} 不存在于模型 {model_id} 中") return None animation = self.morphing_animations[model_id][anim_name] return { 'name': animation.name, 'target_weights': animation.target_weights, 'duration': animation.duration, 'interpolation': animation.interpolation.name, 'loop': animation.loop, 'current_time': animation.current_time, 'playing': animation.playing, 'paused': animation.paused } def start_updating(self) -> None: """ 开始更新Morphing系统 """ if not self.is_updating: self.is_updating = True print("Morphing系统更新已启动") def stop_updating(self) -> None: """ 停止更新Morphing系统 """ if self.is_updating: self.is_updating = False print("Morphing系统更新已停止") def get_system_stats(self) -> Dict[str, Any]: """ 获取系统统计信息 :return: 统计信息字典 """ cache_stats = { 'size': len(self.cache.cache), 'max_size': self.cache.max_size } return { 'models_processed': self.stats['models_processed'], 'morph_targets_created': self.stats['morph_targets_created'], 'animations_played': self.stats['animations_played'], 'deformations_applied': self.stats['deformations_applied'], 'cache_stats': cache_stats, 'registered_models': len(self.model_data), 'total_morph_targets': sum(len(targets) for targets in self.morph_targets.values()), 'total_animations': sum(len(anims) for anims in self.morphing_animations.values()) } def reset_stats(self) -> None: """ 重置统计信息 """ self.stats = { 'models_processed': 0, 'morph_targets_created': 0, 'animations_played': 0, 'deformations_applied': 0, 'cache_hits': 0, 'cache_misses': 0 } print("统计信息已重置")