980 lines
35 KiB
Python
980 lines
35 KiB
Python
"""
|
||
Morphing和变形动画插件 - 高级功能模块
|
||
提供动画状态机、曲线编辑器、物理集成等高级功能
|
||
"""
|
||
|
||
import math
|
||
import numpy as np
|
||
from typing import Dict, List, Tuple, Optional, Any, Callable, Union
|
||
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
|
||
from direct.interval.IntervalGlobal import *
|
||
|
||
# 导入核心模块
|
||
from .core import MorphingCore, InterpolationMode
|
||
|
||
# 定义常量
|
||
ADVANCED_SETTINGS = {
|
||
'state_machine_update_rate': 30,
|
||
'curve_editor_points': 100,
|
||
'physics_update_rate': 60
|
||
}
|
||
|
||
# 枚举定义
|
||
class AnimationStateType(Enum):
|
||
"""动画状态类型枚举"""
|
||
IDLE = 0
|
||
PLAYING = 1
|
||
TRANSITION = 2
|
||
BLENDING = 3
|
||
PAUSED = 4
|
||
|
||
class TransitionType(Enum):
|
||
"""转换类型枚举"""
|
||
INSTANT = 0 # 瞬间转换
|
||
SMOOTH = 1 # 平滑转换
|
||
BLEND = 2 # 混合转换
|
||
CONDITIONAL = 3 # 条件转换
|
||
|
||
class CurveType(Enum):
|
||
"""曲线类型枚举"""
|
||
LINEAR = 0 # 线性
|
||
BEZIER = 1 # 贝塞尔
|
||
SPLINE = 2 # 样条
|
||
STEP = 3 # 阶梯
|
||
NOISE = 4 # 噪声
|
||
|
||
@dataclass
|
||
class AnimationState:
|
||
"""动画状态数据类"""
|
||
name: str
|
||
morph_targets: Dict[str, float] # {target_name: weight}
|
||
duration: float = 0.0
|
||
loop: bool = False
|
||
play_rate: float = 1.0
|
||
active: bool = True
|
||
|
||
@dataclass
|
||
class StateTransition:
|
||
"""状态转换数据类"""
|
||
from_state: str
|
||
to_state: str
|
||
transition_type: TransitionType
|
||
condition: Optional[Callable] = None # 转换条件函数
|
||
transition_time: float = 0.3 # 转换时间
|
||
blend_mode: InterpolationMode = InterpolationMode.LINEAR
|
||
|
||
@dataclass
|
||
class AnimationCurve:
|
||
"""动画曲线数据类"""
|
||
name: str
|
||
curve_type: CurveType
|
||
control_points: List[Tuple[float, float]] # 控制点 [(time, value)]
|
||
interpolation: InterpolationMode = InterpolationMode.LINEAR
|
||
active: bool = True
|
||
|
||
@dataclass
|
||
class PhysicsConstraint:
|
||
"""物理约束数据类"""
|
||
name: str
|
||
constraint_type: str # 'spring', 'distance', 'angle'
|
||
target_bone: str
|
||
stiffness: float = 1.0
|
||
damping: float = 0.1
|
||
rest_value: float = 0.0
|
||
active: bool = True
|
||
|
||
class AnimationStateMachine:
|
||
"""
|
||
动画状态机
|
||
管理复杂的动画状态转换和混合
|
||
"""
|
||
|
||
def __init__(self, morphing_core: MorphingCore, model_id: str):
|
||
"""
|
||
初始化动画状态机
|
||
:param morphing_core: Morphing核心系统
|
||
:param model_id: 模型ID
|
||
"""
|
||
self.morphing_core = morphing_core
|
||
self.model_id = model_id
|
||
self.states: Dict[str, AnimationState] = {}
|
||
self.transitions: List[StateTransition] = []
|
||
self.current_state: Optional[str] = None
|
||
self.previous_state: Optional[str] = None
|
||
self.transition_progress: float = 0.0
|
||
self.is_transitioning: bool = False
|
||
self.is_updating: bool = False
|
||
self.update_rate = ADVANCED_SETTINGS['state_machine_update_rate']
|
||
self.last_update_time: float = 0.0
|
||
|
||
print(f"动画状态机初始化完成 (模型: {model_id})")
|
||
|
||
def add_state(self, state_name: str, morph_targets: Dict[str, float],
|
||
duration: float = 0.0, loop: bool = False, play_rate: float = 1.0) -> bool:
|
||
"""
|
||
添加动画状态
|
||
:param state_name: 状态名称
|
||
:param morph_targets: Morph目标权重字典 {target_name: weight}
|
||
:param duration: 持续时间
|
||
:param loop: 是否循环
|
||
:param play_rate: 播放速度
|
||
:return: 是否成功添加
|
||
"""
|
||
# 验证Morph目标是否存在
|
||
for target_name in morph_targets.keys():
|
||
if (self.model_id not in self.morphing_core.morph_targets or
|
||
target_name not in self.morphing_core.morph_targets[self.model_id]):
|
||
print(f"错误: Morph目标 {target_name} 不存在于模型 {self.model_id} 中")
|
||
return False
|
||
|
||
# 创建状态
|
||
state = AnimationState(
|
||
name=state_name,
|
||
morph_targets=morph_targets,
|
||
duration=duration,
|
||
loop=loop,
|
||
play_rate=play_rate,
|
||
active=True
|
||
)
|
||
|
||
# 存储状态
|
||
self.states[state_name] = state
|
||
print(f"动画状态已添加: {state_name} (模型: {self.model_id})")
|
||
return True
|
||
|
||
def remove_state(self, state_name: str) -> bool:
|
||
"""
|
||
移除动画状态
|
||
:param state_name: 状态名称
|
||
:return: 是否成功移除
|
||
"""
|
||
if state_name not in self.states:
|
||
print(f"错误: 动画状态 {state_name} 不存在")
|
||
return False
|
||
|
||
del self.states[state_name]
|
||
|
||
# 移除相关的转换
|
||
self.transitions = [t for t in self.transitions
|
||
if t.from_state != state_name and t.to_state != state_name]
|
||
|
||
print(f"动画状态已移除: {state_name} (模型: {self.model_id})")
|
||
return True
|
||
|
||
def set_transition(self, from_state: str, to_state: str,
|
||
transition_type: TransitionType = TransitionType.SMOOTH,
|
||
condition: Optional[Callable] = None,
|
||
transition_time: float = 0.3,
|
||
blend_mode: InterpolationMode = InterpolationMode.LINEAR) -> bool:
|
||
"""
|
||
设置状态转换
|
||
:param from_state: 起始状态
|
||
:param to_state: 目标状态
|
||
:param transition_type: 转换类型
|
||
:param condition: 转换条件函数
|
||
:param transition_time: 转换时间
|
||
:param blend_mode: 混合模式
|
||
:return: 是否成功设置
|
||
"""
|
||
# 验证状态是否存在
|
||
if from_state not in self.states:
|
||
print(f"错误: 起始状态 {from_state} 不存在")
|
||
return False
|
||
|
||
if to_state not in self.states:
|
||
print(f"错误: 目标状态 {to_state} 不存在")
|
||
return False
|
||
|
||
# 创建转换
|
||
transition = StateTransition(
|
||
from_state=from_state,
|
||
to_state=to_state,
|
||
transition_type=transition_type,
|
||
condition=condition,
|
||
transition_time=transition_time,
|
||
blend_mode=blend_mode
|
||
)
|
||
|
||
# 存储转换
|
||
self.transitions.append(transition)
|
||
print(f"状态转换已设置: {from_state} -> {to_state} (模型: {self.model_id})")
|
||
return True
|
||
|
||
def enter_state(self, state_name: str) -> bool:
|
||
"""
|
||
进入指定状态
|
||
:param state_name: 状态名称
|
||
:return: 是否成功进入
|
||
"""
|
||
if state_name not in self.states:
|
||
print(f"错误: 动画状态 {state_name} 不存在")
|
||
return False
|
||
|
||
self.previous_state = self.current_state
|
||
self.current_state = state_name
|
||
self.is_transitioning = False
|
||
|
||
# 应用状态的Morph目标权重
|
||
state = self.states[state_name]
|
||
for target_name, weight in state.morph_targets.items():
|
||
self.morphing_core.set_morph_target_weight(self.model_id, target_name, weight)
|
||
|
||
print(f"进入动画状态: {state_name} (模型: {self.model_id})")
|
||
return True
|
||
|
||
def trigger_transition(self, to_state: str) -> bool:
|
||
"""
|
||
触发状态转换
|
||
:param to_state: 目标状态
|
||
:return: 是否成功触发
|
||
"""
|
||
if not self.current_state:
|
||
print(f"错误: 当前没有活动状态")
|
||
return False
|
||
|
||
if to_state not in self.states:
|
||
print(f"错误: 目标状态 {to_state} 不存在")
|
||
return False
|
||
|
||
# 查找转换
|
||
transition = None
|
||
for t in self.transitions:
|
||
if t.from_state == self.current_state and t.to_state == to_state:
|
||
transition = t
|
||
break
|
||
|
||
if not transition:
|
||
print(f"错误: 从 {self.current_state} 到 {to_state} 的转换不存在")
|
||
return False
|
||
|
||
# 开始转换
|
||
self.is_transitioning = True
|
||
self.transition_progress = 0.0
|
||
self.previous_state = self.current_state
|
||
self.current_state = to_state
|
||
|
||
print(f"触发状态转换: {self.previous_state} -> {to_state} (模型: {self.model_id})")
|
||
return True
|
||
|
||
def update(self, dt: float) -> None:
|
||
"""
|
||
更新状态机
|
||
:param dt: 时间增量
|
||
"""
|
||
if not self.is_updating:
|
||
return
|
||
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 限制更新频率
|
||
if current_time - self.last_update_time < 1.0 / self.update_rate:
|
||
return
|
||
|
||
self.last_update_time = current_time
|
||
|
||
# 更新状态转换
|
||
if self.is_transitioning:
|
||
self._update_transition(dt)
|
||
|
||
# 检查条件转换
|
||
self._check_conditional_transitions()
|
||
|
||
def _update_transition(self, dt: float) -> None:
|
||
"""
|
||
更新状态转换
|
||
:param dt: 时间增量
|
||
"""
|
||
if not self.is_transitioning or not self.previous_state or not self.current_state:
|
||
return
|
||
|
||
# 更新转换进度
|
||
transition = None
|
||
for t in self.transitions:
|
||
if t.from_state == self.previous_state and t.to_state == self.current_state:
|
||
transition = t
|
||
break
|
||
|
||
if not transition:
|
||
self.is_transitioning = False
|
||
return
|
||
|
||
self.transition_progress += dt / transition.transition_time
|
||
|
||
# 检查转换是否完成
|
||
if self.transition_progress >= 1.0:
|
||
self.is_transitioning = False
|
||
self.transition_progress = 1.0
|
||
print(f"状态转换完成: {self.previous_state} -> {self.current_state} (模型: {self.model_id})")
|
||
return
|
||
|
||
# 计算插值因子
|
||
t = self.transition_progress
|
||
if transition.blend_mode == InterpolationMode.EASE_IN:
|
||
t = t * t
|
||
elif transition.blend_mode == InterpolationMode.EASE_OUT:
|
||
t = 1.0 - (1.0 - t) * (1.0 - t)
|
||
elif transition.blend_mode == InterpolationMode.EASE_IN_OUT:
|
||
t = t * t * (3.0 - 2.0 * t)
|
||
|
||
# 混合两个状态的权重
|
||
from_state = self.states[self.previous_state]
|
||
to_state = self.states[self.current_state]
|
||
|
||
# 获取所有涉及的Morph目标
|
||
all_targets = set(from_state.morph_targets.keys()) | set(to_state.morph_targets.keys())
|
||
|
||
for target_name in all_targets:
|
||
from_weight = from_state.morph_targets.get(target_name, 0.0)
|
||
to_weight = to_state.morph_targets.get(target_name, 0.0)
|
||
|
||
# 计算混合权重
|
||
blended_weight = from_weight * (1.0 - t) + to_weight * t
|
||
|
||
# 应用权重
|
||
self.morphing_core.set_morph_target_weight(self.model_id, target_name, blended_weight)
|
||
|
||
def _check_conditional_transitions(self) -> None:
|
||
"""
|
||
检查条件转换
|
||
"""
|
||
if not self.current_state:
|
||
return
|
||
|
||
# 检查从当前状态出发的条件转换
|
||
for transition in self.transitions:
|
||
if (transition.from_state == self.current_state and
|
||
transition.transition_type == TransitionType.CONDITIONAL and
|
||
transition.condition):
|
||
|
||
try:
|
||
# 检查条件是否满足
|
||
if transition.condition():
|
||
self.trigger_transition(transition.to_state)
|
||
break
|
||
except Exception as e:
|
||
print(f"检查转换条件时出错: {e}")
|
||
|
||
def get_state_info(self, state_name: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取状态信息
|
||
:param state_name: 状态名称
|
||
:return: 状态信息字典
|
||
"""
|
||
if state_name not in self.states:
|
||
print(f"错误: 动画状态 {state_name} 不存在")
|
||
return None
|
||
|
||
state = self.states[state_name]
|
||
|
||
return {
|
||
'name': state.name,
|
||
'morph_targets': state.morph_targets,
|
||
'duration': state.duration,
|
||
'loop': state.loop,
|
||
'play_rate': state.play_rate,
|
||
'active': state.active
|
||
}
|
||
|
||
def get_current_state(self) -> Optional[str]:
|
||
"""
|
||
获取当前状态
|
||
:return: 当前状态名称
|
||
"""
|
||
return self.current_state
|
||
|
||
def start_updating(self) -> None:
|
||
"""
|
||
开始更新状态机
|
||
"""
|
||
if not self.is_updating:
|
||
self.is_updating = True
|
||
print(f"动画状态机更新已启动 (模型: {self.model_id})")
|
||
|
||
def stop_updating(self) -> None:
|
||
"""
|
||
停止更新状态机
|
||
"""
|
||
if self.is_updating:
|
||
self.is_updating = False
|
||
print(f"动画状态机更新已停止 (模型: {self.model_id})")
|
||
|
||
class AnimationCurveEditor:
|
||
"""
|
||
动画曲线编辑器
|
||
支持对动画属性进行曲线编辑和自定义
|
||
"""
|
||
|
||
def __init__(self, morphing_core: MorphingCore):
|
||
"""
|
||
初始化动画曲线编辑器
|
||
:param morphing_core: Morphing核心系统
|
||
"""
|
||
self.morphing_core = morphing_core
|
||
self.curves: Dict[str, AnimationCurve] = {}
|
||
self.curve_values: Dict[str, List[Tuple[float, float]]] = {} # {curve_name: [(time, value)]}
|
||
self.is_updating = False
|
||
self.update_rate = ADVANCED_SETTINGS['curve_editor_points']
|
||
self.last_update_time: float = 0.0
|
||
|
||
print("动画曲线编辑器初始化完成")
|
||
|
||
def create_curve(self, curve_name: str, curve_type: CurveType,
|
||
control_points: List[Tuple[float, float]],
|
||
interpolation: InterpolationMode = InterpolationMode.LINEAR) -> bool:
|
||
"""
|
||
创建动画曲线
|
||
:param curve_name: 曲线名称
|
||
:param curve_type: 曲线类型
|
||
:param control_points: 控制点列表 [(time, value)]
|
||
:param interpolation: 插值模式
|
||
:return: 是否成功创建
|
||
"""
|
||
# 验证控制点
|
||
if not control_points:
|
||
print(f"错误: 控制点列表为空")
|
||
return False
|
||
|
||
# 按时间排序控制点
|
||
sorted_points = sorted(control_points, key=lambda p: p[0])
|
||
|
||
# 创建曲线
|
||
curve = AnimationCurve(
|
||
name=curve_name,
|
||
curve_type=curve_type,
|
||
control_points=sorted_points,
|
||
interpolation=interpolation,
|
||
active=True
|
||
)
|
||
|
||
# 存储曲线
|
||
self.curves[curve_name] = curve
|
||
|
||
# 预计算曲线值
|
||
self._precompute_curve_values(curve_name)
|
||
|
||
print(f"动画曲线已创建: {curve_name}")
|
||
return True
|
||
|
||
def remove_curve(self, curve_name: str) -> bool:
|
||
"""
|
||
移除动画曲线
|
||
:param curve_name: 曲线名称
|
||
:return: 是否成功移除
|
||
"""
|
||
if curve_name not in self.curves:
|
||
print(f"错误: 动画曲线 {curve_name} 不存在")
|
||
return False
|
||
|
||
del self.curves[curve_name]
|
||
|
||
if curve_name in self.curve_values:
|
||
del self.curve_values[curve_name]
|
||
|
||
print(f"动画曲线已移除: {curve_name}")
|
||
return True
|
||
|
||
def _precompute_curve_values(self, curve_name: str) -> None:
|
||
"""
|
||
预计算曲线值
|
||
:param curve_name: 曲线名称
|
||
"""
|
||
if curve_name not in self.curves:
|
||
return
|
||
|
||
curve = self.curves[curve_name]
|
||
values = []
|
||
|
||
# 根据曲线类型生成值
|
||
if curve.curve_type == CurveType.LINEAR:
|
||
values = self._compute_linear_curve(curve.control_points)
|
||
elif curve.curve_type == CurveType.BEZIER:
|
||
values = self._compute_bezier_curve(curve.control_points)
|
||
elif curve.curve_type == CurveType.SPLINE:
|
||
values = self._compute_spline_curve(curve.control_points)
|
||
elif curve.curve_type == CurveType.STEP:
|
||
values = self._compute_step_curve(curve.control_points)
|
||
elif curve.curve_type == CurveType.NOISE:
|
||
values = self._compute_noise_curve(curve.control_points)
|
||
|
||
self.curve_values[curve_name] = values
|
||
|
||
def _compute_linear_curve(self, control_points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
"""
|
||
计算线性曲线值
|
||
:param control_points: 控制点列表
|
||
:return: 曲线值列表
|
||
"""
|
||
if len(control_points) < 2:
|
||
return control_points
|
||
|
||
values = []
|
||
num_points = ADVANCED_SETTINGS['curve_editor_points']
|
||
|
||
for i in range(len(control_points) - 1):
|
||
start_point = control_points[i]
|
||
end_point = control_points[i + 1]
|
||
|
||
# 计算这一段的点数
|
||
segment_duration = end_point[0] - start_point[0]
|
||
total_duration = control_points[-1][0] - control_points[0][0]
|
||
segment_points = int(num_points * (segment_duration / total_duration)) if total_duration > 0 else num_points
|
||
|
||
for j in range(segment_points):
|
||
t = j / max(1, segment_points - 1)
|
||
time = start_point[0] + (end_point[0] - start_point[0]) * t
|
||
value = start_point[1] + (end_point[1] - start_point[1]) * t
|
||
values.append((time, value))
|
||
|
||
return values
|
||
|
||
def _compute_bezier_curve(self, control_points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
"""
|
||
计算贝塞尔曲线值
|
||
:param control_points: 控制点列表
|
||
:return: 曲线值列表
|
||
"""
|
||
if len(control_points) < 2:
|
||
return control_points
|
||
|
||
values = []
|
||
num_points = ADVANCED_SETTINGS['curve_editor_points']
|
||
|
||
# 对于贝塞尔曲线,我们使用相邻的4个点
|
||
for i in range(max(0, len(control_points) - 3)):
|
||
if i + 3 >= len(control_points):
|
||
break
|
||
|
||
p0 = control_points[i]
|
||
p1 = control_points[i + 1]
|
||
p2 = control_points[i + 2]
|
||
p3 = control_points[i + 3]
|
||
|
||
# 计算这一段的点数
|
||
segment_points = num_points // max(1, len(control_points) - 3)
|
||
|
||
for j in range(segment_points):
|
||
t = j / max(1, segment_points - 1)
|
||
|
||
# 贝塞尔曲线公式
|
||
x = ((1 - t) ** 3) * p0[0] + 3 * ((1 - t) ** 2) * t * p1[0] + 3 * (1 - t) * (t ** 2) * p2[0] + (t ** 3) * p3[0]
|
||
y = ((1 - t) ** 3) * p0[1] + 3 * ((1 - t) ** 2) * t * p1[1] + 3 * (1 - t) * (t ** 2) * p2[1] + (t ** 3) * p3[1]
|
||
|
||
values.append((x, y))
|
||
|
||
return values
|
||
|
||
def _compute_spline_curve(self, control_points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
"""
|
||
计算样条曲线值
|
||
:param control_points: 控制点列表
|
||
:return: 曲线值列表
|
||
"""
|
||
# 简化的样条实现,使用Catmull-Rom样条
|
||
if len(control_points) < 2:
|
||
return control_points
|
||
|
||
values = []
|
||
num_points = ADVANCED_SETTINGS['curve_editor_points']
|
||
|
||
for i in range(len(control_points) - 1):
|
||
# 获取四个控制点(处理边界情况)
|
||
p0 = control_points[max(0, i - 1)]
|
||
p1 = control_points[i]
|
||
p2 = control_points[i + 1]
|
||
p3 = control_points[min(len(control_points) - 1, i + 2)]
|
||
|
||
# 计算这一段的点数
|
||
segment_points = num_points // max(1, len(control_points) - 1)
|
||
|
||
for j in range(segment_points):
|
||
t = j / max(1, segment_points - 1)
|
||
|
||
# Catmull-Rom样条公式
|
||
t2 = t * t
|
||
t3 = t2 * t
|
||
|
||
x = 0.5 * (
|
||
(2 * p1[0]) +
|
||
(-p0[0] + p2[0]) * t +
|
||
(2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 +
|
||
(-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3
|
||
)
|
||
|
||
y = 0.5 * (
|
||
(2 * p1[1]) +
|
||
(-p0[1] + p2[1]) * t +
|
||
(2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 +
|
||
(-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3
|
||
)
|
||
|
||
values.append((x, y))
|
||
|
||
return values
|
||
|
||
def _compute_step_curve(self, control_points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
"""
|
||
计算阶梯曲线值
|
||
:param control_points: 控制点列表
|
||
:return: 曲线值列表
|
||
"""
|
||
if not control_points:
|
||
return []
|
||
|
||
values = []
|
||
num_points = ADVANCED_SETTINGS['curve_editor_points']
|
||
|
||
# 在每个控制点之间创建阶梯
|
||
for i in range(len(control_points)):
|
||
point = control_points[i]
|
||
next_point = control_points[i + 1] if i + 1 < len(control_points) else point
|
||
|
||
# 计算这一段的点数
|
||
segment_points = num_points // max(1, len(control_points))
|
||
|
||
for j in range(segment_points):
|
||
# 阶梯曲线保持前一个点的值直到下一个点
|
||
values.append((point[0] + (next_point[0] - point[0]) * j / max(1, segment_points - 1), point[1]))
|
||
|
||
return values
|
||
|
||
def _compute_noise_curve(self, control_points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
"""
|
||
计算噪声曲线值
|
||
:param control_points: 控制点列表
|
||
:return: 曲线值列表
|
||
"""
|
||
if not control_points:
|
||
return []
|
||
|
||
values = []
|
||
num_points = ADVANCED_SETTINGS['curve_editor_points']
|
||
|
||
# 基于控制点生成噪声曲线
|
||
for i in range(num_points):
|
||
t = i / max(1, num_points - 1)
|
||
|
||
# 计算基础值(线性插值)
|
||
base_value = 0.0
|
||
for j in range(len(control_points) - 1):
|
||
if control_points[j][0] <= t <= control_points[j + 1][0]:
|
||
t_segment = (t - control_points[j][0]) / (control_points[j + 1][0] - control_points[j][0])
|
||
base_value = control_points[j][1] + (control_points[j + 1][1] - control_points[j][1]) * t_segment
|
||
break
|
||
|
||
# 添加噪声
|
||
noise = (np.random.random() - 0.5) * 0.2 # ±10%的噪声
|
||
values.append((t, base_value + noise))
|
||
|
||
return values
|
||
|
||
def get_curve_value(self, curve_name: str, time: float) -> Optional[float]:
|
||
"""
|
||
获取曲线在指定时间的值
|
||
:param curve_name: 曲线名称
|
||
:param time: 时间
|
||
:return: 曲线值
|
||
"""
|
||
if curve_name not in self.curve_values:
|
||
return None
|
||
|
||
values = self.curve_values[curve_name]
|
||
if not values:
|
||
return None
|
||
|
||
# 找到最接近的时间点
|
||
closest_value = values[0][1]
|
||
min_diff = abs(values[0][0] - time)
|
||
|
||
for t, v in values:
|
||
diff = abs(t - time)
|
||
if diff < min_diff:
|
||
min_diff = diff
|
||
closest_value = v
|
||
|
||
return closest_value
|
||
|
||
def apply_curve_to_morph_target(self, curve_name: str, model_id: str,
|
||
target_name: str, time: float,
|
||
base_weight: float = 1.0) -> bool:
|
||
"""
|
||
将曲线应用到Morph目标
|
||
:param curve_name: 曲线名称
|
||
:param model_id: 模型ID
|
||
:param target_name: Morph目标名称
|
||
:param time: 时间
|
||
:param base_weight: 基础权重
|
||
:return: 是否成功应用
|
||
"""
|
||
# 获取曲线值
|
||
curve_value = self.get_curve_value(curve_name, time)
|
||
if curve_value is None:
|
||
return False
|
||
|
||
# 计算最终权重
|
||
final_weight = base_weight * curve_value
|
||
|
||
# 应用到Morph目标
|
||
return self.morphing_core.set_morph_target_weight(model_id, target_name, final_weight)
|
||
|
||
def get_curve_info(self, curve_name: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取曲线信息
|
||
:param curve_name: 曲线名称
|
||
:return: 曲线信息字典
|
||
"""
|
||
if curve_name not in self.curves:
|
||
print(f"错误: 动画曲线 {curve_name} 不存在")
|
||
return None
|
||
|
||
curve = self.curves[curve_name]
|
||
|
||
return {
|
||
'name': curve.name,
|
||
'curve_type': curve.curve_type.name,
|
||
'control_points': curve.control_points,
|
||
'interpolation': curve.interpolation.name,
|
||
'active': curve.active,
|
||
'precomputed_points': len(self.curve_values.get(curve_name, []))
|
||
}
|
||
|
||
def update(self, dt: float) -> None:
|
||
"""
|
||
更新曲线编辑器
|
||
:param dt: 时间增量
|
||
"""
|
||
if not self.is_updating:
|
||
return
|
||
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 限制更新频率
|
||
if current_time - self.last_update_time < 1.0 / self.update_rate:
|
||
return
|
||
|
||
self.last_update_time = current_time
|
||
|
||
# 这里可以添加实时曲线编辑的更新逻辑
|
||
pass
|
||
|
||
def start_updating(self) -> None:
|
||
"""
|
||
开始更新曲线编辑器
|
||
"""
|
||
if not self.is_updating:
|
||
self.is_updating = True
|
||
print("动画曲线编辑器更新已启动")
|
||
|
||
def stop_updating(self) -> None:
|
||
"""
|
||
停止更新曲线编辑器
|
||
"""
|
||
if self.is_updating:
|
||
self.is_updating = False
|
||
print("动画曲线编辑器更新已停止")
|
||
|
||
class PhysicsAnimationIntegration:
|
||
"""
|
||
物理动画集成系统
|
||
结合物理模拟与骨骼动画
|
||
"""
|
||
|
||
def __init__(self, morphing_core: MorphingCore):
|
||
"""
|
||
初始化物理动画集成系统
|
||
:param morphing_core: Morphing核心系统
|
||
"""
|
||
self.morphing_core = morphing_core
|
||
self.constraints: Dict[str, Dict[str, PhysicsConstraint]] = {} # {model_id: {constraint_name: PhysicsConstraint}}
|
||
self.is_updating = False
|
||
self.update_rate = ADVANCED_SETTINGS['physics_update_rate']
|
||
self.last_update_time: float = 0.0
|
||
|
||
print("物理动画集成系统初始化完成")
|
||
|
||
def add_constraint(self, model_id: str, constraint_name: str, constraint_type: str,
|
||
target_bone: str, stiffness: float = 1.0, damping: float = 0.1,
|
||
rest_value: float = 0.0) -> bool:
|
||
"""
|
||
添加物理约束
|
||
:param model_id: 模型ID
|
||
:param constraint_name: 约束名称
|
||
:param constraint_type: 约束类型 ('spring', 'distance', 'angle')
|
||
:param target_bone: 目标骨骼
|
||
:param stiffness: 刚度
|
||
:param damping: 阻尼
|
||
:param rest_value: 静止值
|
||
:return: 是否成功添加
|
||
"""
|
||
if model_id not in self.morphing_core.model_data:
|
||
print(f"错误: 模型 {model_id} 未注册")
|
||
return False
|
||
|
||
# 创建约束
|
||
constraint = PhysicsConstraint(
|
||
name=constraint_name,
|
||
constraint_type=constraint_type,
|
||
target_bone=target_bone,
|
||
stiffness=stiffness,
|
||
damping=damping,
|
||
rest_value=rest_value,
|
||
active=True
|
||
)
|
||
|
||
# 存储约束
|
||
if model_id not in self.constraints:
|
||
self.constraints[model_id] = {}
|
||
self.constraints[model_id][constraint_name] = constraint
|
||
|
||
print(f"物理约束已添加: {constraint_name} (模型: {model_id})")
|
||
return True
|
||
|
||
def remove_constraint(self, model_id: str, constraint_name: str) -> bool:
|
||
"""
|
||
移除物理约束
|
||
:param model_id: 模型ID
|
||
:param constraint_name: 约束名称
|
||
:return: 是否成功移除
|
||
"""
|
||
if model_id not in self.constraints or constraint_name not in self.constraints[model_id]:
|
||
print(f"错误: 物理约束 {constraint_name} 不存在于模型 {model_id} 中")
|
||
return False
|
||
|
||
del self.constraints[model_id][constraint_name]
|
||
print(f"物理约束已移除: {constraint_name} (模型: {model_id})")
|
||
return True
|
||
|
||
def update(self, dt: float) -> None:
|
||
"""
|
||
更新物理动画集成系统
|
||
:param dt: 时间增量
|
||
"""
|
||
if not self.is_updating:
|
||
return
|
||
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 限制更新频率
|
||
if current_time - self.last_update_time < 1.0 / self.update_rate:
|
||
return
|
||
|
||
self.last_update_time = current_time
|
||
|
||
# 更新每个模型的物理约束
|
||
for model_id, constraints in self.constraints.items():
|
||
self._update_model_constraints(model_id, constraints, dt)
|
||
|
||
def _update_model_constraints(self, model_id: str, constraints: Dict[str, PhysicsConstraint], dt: float) -> None:
|
||
"""
|
||
更新模型的物理约束
|
||
:param model_id: 模型ID
|
||
:param constraints: 约束字典
|
||
:param dt: 时间增量
|
||
"""
|
||
# 这里实现简化的弹簧物理系统
|
||
for constraint_name, constraint in constraints.items():
|
||
if not constraint.active:
|
||
continue
|
||
|
||
# 根据约束类型应用不同的物理效果
|
||
if constraint.constraint_type == 'spring':
|
||
self._apply_spring_constraint(model_id, constraint, dt)
|
||
elif constraint.constraint_type == 'distance':
|
||
self._apply_distance_constraint(model_id, constraint, dt)
|
||
elif constraint.constraint_type == 'angle':
|
||
self._apply_angle_constraint(model_id, constraint, dt)
|
||
|
||
def _apply_spring_constraint(self, model_id: str, constraint: PhysicsConstraint, dt: float) -> None:
|
||
"""
|
||
应用弹簧约束
|
||
:param model_id: 模型ID
|
||
:param constraint: 约束对象
|
||
:param dt: 时间增量
|
||
"""
|
||
# 简化的弹簧物理实现
|
||
# 在实际项目中,这里会涉及更复杂的物理计算
|
||
|
||
# 模拟弹簧运动
|
||
# F = -k * x - d * v (胡克定律与阻尼)
|
||
|
||
# 获取当前值(这里简化处理)
|
||
current_value = 0.0 # 在实际实现中,需要获取当前的morph目标权重或其他值
|
||
|
||
# 计算位移
|
||
displacement = current_value - constraint.rest_value
|
||
|
||
# 计算力
|
||
force = -constraint.stiffness * displacement # 弹簧力
|
||
# 这里简化处理,省略速度和阻尼计算
|
||
|
||
# 计算加速度 (F = ma, 假设质量为1)
|
||
acceleration = force
|
||
|
||
# 更新值(简化积分)
|
||
new_value = current_value + acceleration * dt * dt
|
||
|
||
# 应用到目标(示例:应用到morph目标)
|
||
# self.morphing_core.set_morph_target_weight(model_id, constraint.target_bone, new_value)
|
||
|
||
def _apply_distance_constraint(self, model_id: str, constraint: PhysicsConstraint, dt: float) -> None:
|
||
"""
|
||
应用距离约束
|
||
:param model_id: 模型ID
|
||
:param constraint: 约束对象
|
||
:param dt: 时间增量
|
||
"""
|
||
# 距离约束实现
|
||
pass # 简化处理,实际项目中需要具体实现
|
||
|
||
def _apply_angle_constraint(self, model_id: str, constraint: PhysicsConstraint, dt: float) -> None:
|
||
"""
|
||
应用角度约束
|
||
:param model_id: 模型ID
|
||
:param constraint: 约束对象
|
||
:param dt: 时间增量
|
||
"""
|
||
# 角度约束实现
|
||
pass # 简化处理,实际项目中需要具体实现
|
||
|
||
def get_constraint_info(self, model_id: str, constraint_name: str) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
获取约束信息
|
||
:param model_id: 模型ID
|
||
:param constraint_name: 约束名称
|
||
:return: 约束信息字典
|
||
"""
|
||
if model_id not in self.constraints or constraint_name not in self.constraints[model_id]:
|
||
print(f"错误: 物理约束 {constraint_name} 不存在于模型 {model_id} 中")
|
||
return None
|
||
|
||
constraint = self.constraints[model_id][constraint_name]
|
||
|
||
return {
|
||
'name': constraint.name,
|
||
'constraint_type': constraint.constraint_type,
|
||
'target_bone': constraint.target_bone,
|
||
'stiffness': constraint.stiffness,
|
||
'damping': constraint.damping,
|
||
'rest_value': constraint.rest_value,
|
||
'active': constraint.active
|
||
}
|
||
|
||
def start_updating(self) -> None:
|
||
"""
|
||
开始更新物理动画集成系统
|
||
"""
|
||
if not self.is_updating:
|
||
self.is_updating = True
|
||
print("物理动画集成系统更新已启动")
|
||
|
||
def stop_updating(self) -> None:
|
||
"""
|
||
停止更新物理动画集成系统
|
||
"""
|
||
if self.is_updating:
|
||
self.is_updating = False
|
||
print("物理动画集成系统更新已停止") |