1551 lines
63 KiB
Python
1551 lines
63 KiB
Python
"""
|
||
地形动画和变形系统
|
||
提供完整的地形动画、变形、morphing、顶点动画、骨骼动画等功能
|
||
"""
|
||
|
||
from panda3d.core import NodePath, Vec3, Point3, Mat4, LVector3f
|
||
from panda3d.core import Shader, ShaderAttrib, TransformState
|
||
from panda3d.core import Texture, TextureStage, PNMImage
|
||
from panda3d.core import LerpFunctionInterval, LerpPosInterval, LerpHprInterval
|
||
from panda3d.core import ClockObject, deg2Rad, rad2Deg
|
||
import math
|
||
import random
|
||
import json
|
||
import os
|
||
import numpy as np
|
||
from collections import deque
|
||
|
||
class TerrainAnimationSystem:
|
||
"""
|
||
地形动画和变形系统类
|
||
提供地形动画、变形、morphing、顶点动画、骨骼动画等功能
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
self.world = world
|
||
self.terrain_animations = {} # 存储地形动画
|
||
self.deformation_effects = {} # 存储变形效果
|
||
self.morph_targets = {} # 存储morph目标
|
||
self.vertex_animations = {} # 存储顶点动画
|
||
self.skeletal_animations = {} # 存储骨骼动画
|
||
self.animation_intervals = [] # 存储动画间隔
|
||
self.animation_enabled = True
|
||
self.animation_speed = 1.0 # 动画速度倍数
|
||
|
||
# 性能监控
|
||
self.performance_stats = {
|
||
'last_update_time': 0.0,
|
||
'update_count': 0,
|
||
'avg_update_time': 0.0
|
||
}
|
||
|
||
# 初始化动画系统
|
||
self._init_animation_system()
|
||
|
||
def _init_animation_system(self):
|
||
"""
|
||
初始化动画系统(增强版)
|
||
"""
|
||
try:
|
||
# 初始化动画缓存
|
||
self.animation_cache = {}
|
||
|
||
# 初始化变形缓存
|
||
self.deformation_cache = {}
|
||
|
||
# 初始化时间管理器
|
||
self.time_manager = {
|
||
'global_time': 0.0,
|
||
'animation_time_scale': 1.0,
|
||
'paused': False
|
||
}
|
||
|
||
print("地形动画系统初始化完成")
|
||
|
||
except Exception as e:
|
||
print(f"初始化动画系统时出错: {e}")
|
||
|
||
def create_terrain_animation(self, terrain_info, animation_name, keyframes,
|
||
loop=False, speed=1.0, easing='linear'):
|
||
"""
|
||
创建地形动画(增强版)
|
||
terrain_info: 地形信息
|
||
animation_name: 动画名称
|
||
keyframes: 关键帧数据
|
||
loop: 是否循环
|
||
speed: 动画速度
|
||
easing: 缓动函数
|
||
"""
|
||
try:
|
||
# 验证关键帧数据
|
||
if not keyframes or len(keyframes) < 1:
|
||
print("关键帧数据无效")
|
||
return None
|
||
|
||
# 排序关键帧
|
||
sorted_keyframes = sorted(keyframes, key=lambda kf: kf['time'])
|
||
|
||
# 创建动画信息
|
||
animation_info = {
|
||
'name': animation_name,
|
||
'terrain_info': terrain_info,
|
||
'keyframes': sorted_keyframes,
|
||
'duration': sorted_keyframes[-1]['time'] if sorted_keyframes else 0.0,
|
||
'loop': loop,
|
||
'speed': speed,
|
||
'easing': easing,
|
||
'playing': False,
|
||
'paused': False,
|
||
'current_time': 0.0,
|
||
'start_time': 0.0,
|
||
'blend_weight': 1.0,
|
||
'blend_mode': 'override', # override, add, multiply
|
||
'animation_type': 'transform', # transform, vertex, material
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存动画
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.terrain_animations:
|
||
self.terrain_animations[terrain_node] = {}
|
||
self.terrain_animations[terrain_node][animation_name] = animation_info
|
||
|
||
# 缓存动画数据以提高性能
|
||
self._cache_animation_data(animation_info)
|
||
|
||
print(f"创建地形动画: {animation_name} (持续时间: {animation_info['duration']}秒)")
|
||
return animation_info
|
||
|
||
except Exception as e:
|
||
print(f"创建地形动画时出错: {e}")
|
||
return None
|
||
|
||
def _cache_animation_data(self, animation_info):
|
||
"""
|
||
缓存动画数据以提高性能
|
||
"""
|
||
try:
|
||
cache_key = f"{id(animation_info['terrain_info'])}_{animation_info['name']}"
|
||
|
||
# 预计算插值数据
|
||
cached_data = {
|
||
'position_curves': [],
|
||
'rotation_curves': [],
|
||
'scale_curves': [],
|
||
'computed_times': []
|
||
}
|
||
|
||
keyframes = animation_info['keyframes']
|
||
for i in range(len(keyframes) - 1):
|
||
kf1 = keyframes[i]
|
||
kf2 = keyframes[i + 1]
|
||
|
||
# 计算时间段内的插值曲线
|
||
if 'position' in kf1 and 'position' in kf2:
|
||
pos1 = Vec3(*kf1['position'])
|
||
pos2 = Vec3(*kf2['position'])
|
||
cached_data['position_curves'].append((pos1, pos2, kf1['time'], kf2['time']))
|
||
|
||
if 'rotation' in kf1 and 'rotation' in kf2:
|
||
rot1 = Vec3(*kf1['rotation'])
|
||
rot2 = Vec3(*kf2['rotation'])
|
||
cached_data['rotation_curves'].append((rot1, rot2, kf1['time'], kf2['time']))
|
||
|
||
if 'scale' in kf1 and 'scale' in kf2:
|
||
scale1 = Vec3(*kf1['scale'])
|
||
scale2 = Vec3(*kf2['scale'])
|
||
cached_data['scale_curves'].append((scale1, scale2, kf1['time'], kf2['time']))
|
||
|
||
self.animation_cache[cache_key] = cached_data
|
||
|
||
except Exception as e:
|
||
print(f"缓存动画数据时出错: {e}")
|
||
|
||
def play_terrain_animation(self, terrain_info, animation_name, loop=None,
|
||
fade_time=0.0, blend_weight=1.0):
|
||
"""
|
||
播放地形动画(增强版)
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.terrain_animations:
|
||
print(f"地形没有动画: {animation_name}")
|
||
return False
|
||
|
||
if animation_name not in self.terrain_animations[terrain_node]:
|
||
print(f"动画不存在: {animation_name}")
|
||
return False
|
||
|
||
# 获取动画信息
|
||
animation_info = self.terrain_animations[terrain_node][animation_name]
|
||
|
||
# 更新动画属性
|
||
if loop is not None:
|
||
animation_info['loop'] = loop
|
||
animation_info['playing'] = True
|
||
animation_info['paused'] = False
|
||
animation_info['start_time'] = self.world.globalClock.getFrameTime()
|
||
animation_info['current_time'] = 0.0
|
||
animation_info['blend_weight'] = blend_weight
|
||
|
||
# 处理淡入效果
|
||
if fade_time > 0:
|
||
self._start_animation_fade_in(animation_info, fade_time)
|
||
|
||
print(f"播放地形动画: {animation_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"播放地形动画时出错: {e}")
|
||
return False
|
||
|
||
def _start_animation_fade_in(self, animation_info, fade_time):
|
||
"""
|
||
开始动画淡入效果
|
||
"""
|
||
try:
|
||
# 创建淡入定时器
|
||
animation_info['fade_in'] = {
|
||
'duration': fade_time,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"开始动画淡入时出错: {e}")
|
||
|
||
def stop_terrain_animation(self, terrain_info, animation_name, fade_time=0.0):
|
||
"""
|
||
停止地形动画(增强版)
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
if (terrain_node in self.terrain_animations and
|
||
animation_name in self.terrain_animations[terrain_node]):
|
||
animation_info = self.terrain_animations[terrain_node][animation_name]
|
||
|
||
# 处理淡出效果
|
||
if fade_time > 0:
|
||
self._start_animation_fade_out(animation_info, fade_time)
|
||
else:
|
||
animation_info['playing'] = False
|
||
animation_info['paused'] = False
|
||
|
||
print(f"停止地形动画: {animation_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"停止地形动画时出错: {e}")
|
||
return False
|
||
|
||
def _start_animation_fade_out(self, animation_info, fade_time):
|
||
"""
|
||
开始动画淡出效果
|
||
"""
|
||
try:
|
||
# 创建淡出定时器
|
||
animation_info['fade_out'] = {
|
||
'duration': fade_time,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"开始动画淡出时出错: {e}")
|
||
|
||
def pause_terrain_animation(self, terrain_info, animation_name):
|
||
"""
|
||
暂停地形动画
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
if (terrain_node in self.terrain_animations and
|
||
animation_name in self.terrain_animations[terrain_node]):
|
||
animation_info = self.terrain_animations[terrain_node][animation_name]
|
||
animation_info['paused'] = True
|
||
print(f"暂停地形动画: {animation_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"暂停地形动画时出错: {e}")
|
||
return False
|
||
|
||
def resume_terrain_animation(self, terrain_info, animation_name):
|
||
"""
|
||
恢复地形动画
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
if (terrain_node in self.terrain_animations and
|
||
animation_name in self.terrain_animations[terrain_node]):
|
||
animation_info = self.terrain_animations[terrain_node][animation_name]
|
||
animation_info['paused'] = False
|
||
print(f"恢复地形动画: {animation_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"恢复地形动画时出错: {e}")
|
||
return False
|
||
|
||
def update_terrain_animations(self, time_delta):
|
||
"""
|
||
更新地形动画(增强版)
|
||
"""
|
||
try:
|
||
if not self.animation_enabled or self.time_manager['paused']:
|
||
return
|
||
|
||
# 性能监控开始
|
||
start_time = self.world.globalClock.getRealTime()
|
||
|
||
# 应用时间缩放
|
||
scaled_time_delta = time_delta * self.time_manager['animation_time_scale'] * self.animation_speed
|
||
|
||
current_time = self.world.globalClock.getFrameTime()
|
||
self.time_manager['global_time'] += scaled_time_delta
|
||
|
||
# 更新每个地形的动画
|
||
for terrain_node, animations in self.terrain_animations.items():
|
||
for animation_name, animation_info in animations.items():
|
||
if animation_info['playing'] and not animation_info['paused']:
|
||
# 更新当前时间
|
||
animation_info['current_time'] += scaled_time_delta * animation_info['speed']
|
||
|
||
# 处理淡入淡出
|
||
self._update_animation_fade(animation_info, current_time)
|
||
|
||
# 检查是否需要循环
|
||
if animation_info['current_time'] > animation_info['duration']:
|
||
if animation_info['loop']:
|
||
animation_info['current_time'] = animation_info['current_time'] % animation_info['duration']
|
||
else:
|
||
animation_info['playing'] = False
|
||
continue
|
||
|
||
# 应用动画
|
||
self._apply_terrain_animation(animation_info, scaled_time_delta)
|
||
|
||
# 更新性能统计
|
||
end_time = self.world.globalClock.getRealTime()
|
||
update_time = end_time - start_time
|
||
self.performance_stats['last_update_time'] = update_time
|
||
self.performance_stats['update_count'] += 1
|
||
|
||
# 计算平均更新时间
|
||
if self.performance_stats['update_count'] > 1:
|
||
alpha = 0.1
|
||
self.performance_stats['avg_update_time'] = (
|
||
alpha * update_time +
|
||
(1 - alpha) * self.performance_stats['avg_update_time']
|
||
)
|
||
else:
|
||
self.performance_stats['avg_update_time'] = update_time
|
||
|
||
except Exception as e:
|
||
print(f"更新地形动画时出错: {e}")
|
||
|
||
def _update_animation_fade(self, animation_info, current_time):
|
||
"""
|
||
更新动画淡入淡出效果
|
||
"""
|
||
try:
|
||
# 处理淡入
|
||
if 'fade_in' in animation_info and animation_info['fade_in']['active']:
|
||
fade_in = animation_info['fade_in']
|
||
elapsed = current_time - fade_in['start_time']
|
||
if elapsed >= fade_in['duration']:
|
||
animation_info['blend_weight'] = 1.0
|
||
animation_info['fade_in']['active'] = False
|
||
del animation_info['fade_in']
|
||
else:
|
||
animation_info['blend_weight'] = elapsed / fade_in['duration']
|
||
|
||
# 处理淡出
|
||
if 'fade_out' in animation_info and animation_info['fade_out']['active']:
|
||
fade_out = animation_info['fade_out']
|
||
elapsed = current_time - fade_out['start_time']
|
||
if elapsed >= fade_out['duration']:
|
||
animation_info['playing'] = False
|
||
animation_info['fade_out']['active'] = False
|
||
del animation_info['fade_out']
|
||
else:
|
||
animation_info['blend_weight'] = 1.0 - (elapsed / fade_out['duration'])
|
||
|
||
except Exception as e:
|
||
print(f"更新动画淡入淡出时出错: {e}")
|
||
|
||
def _apply_terrain_animation(self, animation_info, time_delta):
|
||
"""
|
||
应用地形动画(增强版)
|
||
"""
|
||
try:
|
||
current_time = animation_info['current_time']
|
||
keyframes = animation_info['keyframes']
|
||
blend_weight = animation_info['blend_weight']
|
||
|
||
if len(keyframes) < 1:
|
||
return
|
||
|
||
# 如果只有一个关键帧,直接应用
|
||
if len(keyframes) == 1:
|
||
kf = keyframes[0]
|
||
self._apply_keyframe_transform(animation_info['terrain_info'], kf, blend_weight)
|
||
return
|
||
|
||
# 找到当前时间对应的两个关键帧
|
||
prev_kf = keyframes[0]
|
||
next_kf = keyframes[-1]
|
||
|
||
for i in range(len(keyframes) - 1):
|
||
if keyframes[i]['time'] <= current_time <= keyframes[i + 1]['time']:
|
||
prev_kf = keyframes[i]
|
||
next_kf = keyframes[i + 1]
|
||
break
|
||
|
||
# 计算插值因子
|
||
time_diff = next_kf['time'] - prev_kf['time']
|
||
if time_diff > 0:
|
||
t = (current_time - prev_kf['time']) / time_diff
|
||
|
||
# 应用缓动函数
|
||
t = self._apply_easing(t, animation_info['easing'])
|
||
else:
|
||
t = 0.0
|
||
|
||
# 插值计算变换
|
||
self._interpolate_and_apply_transform(
|
||
animation_info['terrain_info'],
|
||
prev_kf, next_kf, t, blend_weight
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"应用地形动画时出错: {e}")
|
||
|
||
def _apply_easing(self, t, easing_type):
|
||
"""
|
||
应用缓动函数
|
||
"""
|
||
try:
|
||
if easing_type == 'linear':
|
||
return t
|
||
elif easing_type == 'ease_in':
|
||
return t * t
|
||
elif easing_type == 'ease_out':
|
||
return 1 - (1 - t) * (1 - t)
|
||
elif easing_type == 'ease_in_out':
|
||
return 3 * t * t - 2 * t * t * t
|
||
elif easing_type == 'bounce':
|
||
if t < 1/2.75:
|
||
return 7.5625 * t * t
|
||
elif t < 2/2.75:
|
||
t -= 1.5/2.75
|
||
return 7.5625 * t * t + 0.75
|
||
elif t < 2.5/2.75:
|
||
t -= 2.25/2.75
|
||
return 7.5625 * t * t + 0.9375
|
||
else:
|
||
t -= 2.625/2.75
|
||
return 7.5625 * t * t + 0.984375
|
||
else:
|
||
return t
|
||
|
||
except Exception as e:
|
||
print(f"应用缓动函数时出错: {e}")
|
||
return t
|
||
|
||
def _apply_keyframe_transform(self, terrain_info, keyframe, blend_weight):
|
||
"""
|
||
应用单个关键帧变换
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
|
||
# 应用位置变换
|
||
if 'position' in keyframe:
|
||
target_pos = Vec3(*keyframe['position'])
|
||
if blend_weight < 1.0:
|
||
current_pos = terrain_node.getPos()
|
||
final_pos = current_pos + (target_pos - current_pos) * blend_weight
|
||
else:
|
||
final_pos = target_pos
|
||
terrain_node.setPos(final_pos)
|
||
|
||
# 应用旋转变换
|
||
if 'rotation' in keyframe:
|
||
target_rot = Vec3(*keyframe['rotation'])
|
||
if blend_weight < 1.0:
|
||
current_rot = terrain_node.getHpr()
|
||
final_rot = current_rot + (target_rot - current_rot) * blend_weight
|
||
else:
|
||
final_rot = target_rot
|
||
terrain_node.setHpr(final_rot)
|
||
|
||
# 应用缩放变换
|
||
if 'scale' in keyframe:
|
||
target_scale = Vec3(*keyframe['scale'])
|
||
if blend_weight < 1.0:
|
||
current_scale = terrain_node.getScale()
|
||
final_scale = current_scale + (target_scale - current_scale) * blend_weight
|
||
else:
|
||
final_scale = target_scale
|
||
terrain_node.setScale(final_scale)
|
||
|
||
except Exception as e:
|
||
print(f"应用关键帧变换时出错: {e}")
|
||
|
||
def _interpolate_and_apply_transform(self, terrain_info, prev_kf, next_kf, t, blend_weight):
|
||
"""
|
||
插值并应用变换
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
|
||
# 插值位置
|
||
if 'position' in prev_kf and 'position' in next_kf:
|
||
prev_pos = Vec3(*prev_kf['position'])
|
||
next_pos = Vec3(*next_kf['position'])
|
||
interpolated_pos = prev_pos + (next_pos - prev_pos) * t
|
||
|
||
if blend_weight < 1.0:
|
||
current_pos = terrain_node.getPos()
|
||
final_pos = current_pos + (interpolated_pos - current_pos) * blend_weight
|
||
else:
|
||
final_pos = interpolated_pos
|
||
|
||
terrain_node.setPos(final_pos)
|
||
|
||
# 插值旋转
|
||
if 'rotation' in prev_kf and 'rotation' in next_kf:
|
||
prev_rot = Vec3(*prev_kf['rotation'])
|
||
next_rot = Vec3(*next_kf['rotation'])
|
||
interpolated_rot = prev_rot + (next_rot - prev_rot) * t
|
||
|
||
if blend_weight < 1.0:
|
||
current_rot = terrain_node.getHpr()
|
||
final_rot = current_rot + (interpolated_rot - current_rot) * blend_weight
|
||
else:
|
||
final_rot = interpolated_rot
|
||
|
||
terrain_node.setHpr(final_rot)
|
||
|
||
# 插值缩放
|
||
if 'scale' in prev_kf and 'scale' in next_kf:
|
||
prev_scale = Vec3(*prev_kf['scale'])
|
||
next_scale = Vec3(*next_kf['scale'])
|
||
interpolated_scale = prev_scale + (next_scale - prev_scale) * t
|
||
|
||
if blend_weight < 1.0:
|
||
current_scale = terrain_node.getScale()
|
||
final_scale = current_scale + (interpolated_scale - current_scale) * blend_weight
|
||
else:
|
||
final_scale = interpolated_scale
|
||
|
||
terrain_node.setScale(final_scale)
|
||
|
||
except Exception as e:
|
||
print(f"插值并应用变换时出错: {e}")
|
||
|
||
def create_height_deformation(self, terrain_info, deformation_name,
|
||
center_pos, radius, strength, duration=2.0,
|
||
easing='ease_out', falloff='linear'):
|
||
"""
|
||
创建高度变形效果(增强版)
|
||
"""
|
||
try:
|
||
# 创建变形效果信息
|
||
deformation_info = {
|
||
'name': deformation_name,
|
||
'terrain_info': terrain_info,
|
||
'center_pos': Vec3(center_pos),
|
||
'radius': radius,
|
||
'strength': strength,
|
||
'duration': duration,
|
||
'easing': easing,
|
||
'falloff': falloff,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'height',
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存变形效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(deformation_info)
|
||
|
||
print(f"创建高度变形效果: {deformation_name}")
|
||
return deformation_info
|
||
|
||
except Exception as e:
|
||
print(f"创建高度变形效果时出错: {e}")
|
||
return None
|
||
|
||
def create_noise_deformation(self, terrain_info, deformation_name,
|
||
scale=1.0, strength=0.1, duration=5.0,
|
||
octaves=3, persistence=0.5, lacunarity=2.0):
|
||
"""
|
||
创建噪声变形效果
|
||
"""
|
||
try:
|
||
# 创建噪声变形效果信息
|
||
deformation_info = {
|
||
'name': deformation_name,
|
||
'terrain_info': terrain_info,
|
||
'scale': scale,
|
||
'strength': strength,
|
||
'duration': duration,
|
||
'octaves': octaves,
|
||
'persistence': persistence,
|
||
'lacunarity': lacunarity,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'noise',
|
||
'seed': random.randint(0, 10000),
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存变形效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(deformation_info)
|
||
|
||
print(f"创建噪声变形效果: {deformation_name}")
|
||
return deformation_info
|
||
|
||
except Exception as e:
|
||
print(f"创建噪声变形效果时出错: {e}")
|
||
return None
|
||
|
||
def create_wave_deformation(self, terrain_info, deformation_name,
|
||
amplitude=0.1, wavelength=10.0, speed=1.0,
|
||
direction=Vec3(1, 0, 0), duration=float('inf')):
|
||
"""
|
||
创建波浪变形效果
|
||
"""
|
||
try:
|
||
# 创建波浪变形效果信息
|
||
deformation_info = {
|
||
'name': deformation_name,
|
||
'terrain_info': terrain_info,
|
||
'amplitude': amplitude,
|
||
'wavelength': wavelength,
|
||
'speed': speed,
|
||
'direction': direction.normalized(),
|
||
'duration': duration,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'wave',
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存变形效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(deformation_info)
|
||
|
||
print(f"创建波浪变形效果: {deformation_name}")
|
||
return deformation_info
|
||
|
||
except Exception as e:
|
||
print(f"创建波浪变形效果时出错: {e}")
|
||
return None
|
||
|
||
def update_deformation_effects(self, time_delta):
|
||
"""
|
||
更新变形效果(增强版)
|
||
"""
|
||
try:
|
||
if not self.animation_enabled:
|
||
return
|
||
|
||
current_time = self.world.globalClock.getFrameTime()
|
||
scaled_time_delta = time_delta * self.animation_speed
|
||
|
||
# 更新每个地形的变形效果
|
||
for terrain_node, deformations in self.deformation_effects.items():
|
||
for deformation_info in deformations[:]: # 使用切片复制列表
|
||
if deformation_info['active']:
|
||
# 更新进度
|
||
elapsed_time = current_time - deformation_info['start_time']
|
||
progress = min(1.0, elapsed_time / deformation_info['duration']) if deformation_info['duration'] > 0 else 0.0
|
||
deformation_info['progress'] = progress
|
||
|
||
# 检查是否超时
|
||
if deformation_info['duration'] > 0 and elapsed_time > deformation_info['duration']:
|
||
deformation_info['active'] = False
|
||
continue
|
||
|
||
# 根据变形类型应用效果
|
||
if deformation_info['type'] == 'height':
|
||
self._apply_height_deformation(deformation_info, progress, scaled_time_delta)
|
||
elif deformation_info['type'] == 'noise':
|
||
self._apply_noise_deformation(deformation_info, progress, scaled_time_delta)
|
||
elif deformation_info['type'] == 'wave':
|
||
self._apply_wave_deformation(deformation_info, progress, scaled_time_delta)
|
||
|
||
# 更新地形网格
|
||
if deformation_info['active']:
|
||
self._update_terrain_mesh(deformation_info['terrain_info'])
|
||
|
||
except Exception as e:
|
||
print(f"更新变形效果时出错: {e}")
|
||
|
||
def _apply_height_deformation(self, deformation_info, progress, time_delta):
|
||
"""
|
||
应用高度变形效果(增强版)
|
||
"""
|
||
try:
|
||
terrain_info = deformation_info['terrain_info']
|
||
heightfield = terrain_info['heightfield']
|
||
|
||
if not heightfield:
|
||
return
|
||
|
||
terrain_node = terrain_info['node']
|
||
terrain_pos = terrain_node.getPos()
|
||
terrain_scale = terrain_node.getScale()
|
||
|
||
# 获取高度图尺寸
|
||
width = heightfield.getXSize()
|
||
height = heightfield.getYSize()
|
||
|
||
# 计算中心点在高度图中的位置
|
||
center_offset = (width - 1) / 2
|
||
center_x = int((deformation_info['center_pos'].getX() - terrain_pos.getX()) / terrain_scale.getX() + center_offset)
|
||
center_y = int((deformation_info['center_pos'].getY() - terrain_pos.getY()) / terrain_scale.getY() + center_offset)
|
||
|
||
# 计算变形半径(在高度图坐标系中)
|
||
radius_pixels = int(deformation_info['radius'] / max(terrain_scale.getX(), terrain_scale.getY()))
|
||
|
||
# 计算变形强度(应用缓动和进度)
|
||
eased_progress = self._apply_easing(progress, deformation_info['easing'])
|
||
current_strength = deformation_info['strength'] * eased_progress
|
||
|
||
# 应用变形
|
||
modified = False
|
||
for dx in range(-radius_pixels, radius_pixels + 1):
|
||
for dy in range(-radius_pixels, radius_pixels + 1):
|
||
# 计算距离
|
||
distance = math.sqrt(dx*dx + dy*dy)
|
||
if distance <= radius_pixels:
|
||
# 计算衰减因子
|
||
if deformation_info['falloff'] == 'linear':
|
||
weight = 1.0 - (distance / radius_pixels)
|
||
elif deformation_info['falloff'] == 'smooth':
|
||
weight = math.cos((distance / radius_pixels) * math.pi / 2)
|
||
elif deformation_info['falloff'] == 'quadratic':
|
||
weight = 1.0 - (distance / radius_pixels) ** 2
|
||
else:
|
||
weight = 1.0 - (distance / radius_pixels)
|
||
|
||
# 计算目标点
|
||
target_x = center_x + dx
|
||
target_y = center_y + dy
|
||
|
||
# 检查边界
|
||
if 0 <= target_x < width and 0 <= target_y < height:
|
||
# 获取当前高度
|
||
current_height = heightfield.getRed(target_x, target_y)
|
||
|
||
# 应用变形
|
||
new_height = current_height + current_strength * weight * 0.01
|
||
new_height = max(0.0, min(1.0, new_height))
|
||
|
||
# 设置新高度
|
||
heightfield.setRed(target_x, target_y, new_height)
|
||
heightfield.setGreen(target_x, target_y, new_height)
|
||
heightfield.setBlue(target_x, target_y, new_height)
|
||
modified = True
|
||
|
||
# 标记需要更新网格
|
||
if modified:
|
||
terrain_info['_mesh_dirty'] = True
|
||
|
||
except Exception as e:
|
||
print(f"应用高度变形效果时出错: {e}")
|
||
|
||
def _apply_noise_deformation(self, deformation_info, progress, time_delta):
|
||
"""
|
||
应用噪声变形效果
|
||
"""
|
||
try:
|
||
terrain_info = deformation_info['terrain_info']
|
||
heightfield = terrain_info['heightfield']
|
||
|
||
if not heightfield:
|
||
return
|
||
|
||
# 获取高度图尺寸
|
||
width = heightfield.getXSize()
|
||
height_value = heightfield.getYSize()
|
||
|
||
# 计算噪声参数
|
||
scale = deformation_info['scale']
|
||
strength = deformation_info['strength'] * progress
|
||
seed = deformation_info['seed']
|
||
|
||
# 应用噪声变形
|
||
modified = False
|
||
for y in range(height_value):
|
||
for x in range(width):
|
||
# 生成噪声值
|
||
nx = x / width * scale
|
||
ny = y / height_value * scale
|
||
|
||
# 简单的噪声实现(实际项目中可以使用更复杂的噪声算法)
|
||
noise_value = self._fbm_noise(nx, ny, seed,
|
||
deformation_info['octaves'],
|
||
deformation_info['persistence'],
|
||
deformation_info['lacunarity'])
|
||
|
||
# 获取当前高度
|
||
current_height = heightfield.getRed(x, y)
|
||
|
||
# 应用噪声变形
|
||
new_height = current_height + noise_value * strength
|
||
new_height = max(0.0, min(1.0, new_height))
|
||
|
||
# 设置新高度
|
||
heightfield.setRed(x, y, new_height)
|
||
heightfield.setGreen(x, y, new_height)
|
||
heightfield.setBlue(x, y, new_height)
|
||
modified = True
|
||
|
||
# 标记需要更新网格
|
||
if modified:
|
||
terrain_info['_mesh_dirty'] = True
|
||
|
||
except Exception as e:
|
||
print(f"应用噪声变形效果时出错: {e}")
|
||
|
||
def _fbm_noise(self, x, y, seed, octaves, persistence, lacunarity):
|
||
"""
|
||
分形布朗运动噪声
|
||
"""
|
||
try:
|
||
value = 0.0
|
||
amplitude = 1.0
|
||
frequency = 1.0
|
||
max_value = 0.0
|
||
|
||
for i in range(octaves):
|
||
# 简化的噪声函数(实际项目中可以使用Perlin噪声或Simplex噪声)
|
||
sample_x = (x * frequency + seed) % 1000
|
||
sample_y = (y * frequency + seed) % 1000
|
||
noise = math.sin(sample_x) * math.cos(sample_y)
|
||
|
||
value += noise * amplitude
|
||
max_value += amplitude
|
||
|
||
amplitude *= persistence
|
||
frequency *= lacunarity
|
||
|
||
return value / max_value if max_value > 0 else 0.0
|
||
|
||
except Exception as e:
|
||
print(f"计算FMB噪声时出错: {e}")
|
||
return 0.0
|
||
|
||
def _apply_wave_deformation(self, deformation_info, progress, time_delta):
|
||
"""
|
||
应用波浪变形效果
|
||
"""
|
||
try:
|
||
terrain_info = deformation_info['terrain_info']
|
||
heightfield = terrain_info['heightfield']
|
||
|
||
if not heightfield:
|
||
return
|
||
|
||
# 获取高度图尺寸
|
||
width = heightfield.getXSize()
|
||
height_value = heightfield.getYSize()
|
||
|
||
# 计算波浪参数
|
||
amplitude = deformation_info['amplitude']
|
||
wavelength = deformation_info['wavelength']
|
||
speed = deformation_info['speed']
|
||
direction = deformation_info['direction']
|
||
elapsed_time = self.world.globalClock.getFrameTime() - deformation_info['start_time']
|
||
|
||
# 应用波浪变形
|
||
modified = False
|
||
for y in range(height_value):
|
||
for x in range(width):
|
||
# 计算波浪值
|
||
projection = x * direction.getX() + y * direction.getY()
|
||
wave_value = math.sin((projection / wavelength + elapsed_time * speed) * 2 * math.pi) * amplitude
|
||
|
||
# 获取当前高度
|
||
current_height = heightfield.getRed(x, y)
|
||
|
||
# 应用波浪变形
|
||
new_height = current_height + wave_value
|
||
new_height = max(0.0, min(1.0, new_height))
|
||
|
||
# 设置新高度
|
||
heightfield.setRed(x, y, new_height)
|
||
heightfield.setGreen(x, y, new_height)
|
||
heightfield.setBlue(x, y, new_height)
|
||
modified = True
|
||
|
||
# 标记需要更新网格
|
||
if modified:
|
||
terrain_info['_mesh_dirty'] = True
|
||
|
||
except Exception as e:
|
||
print(f"应用波浪变形效果时出错: {e}")
|
||
|
||
def _update_terrain_mesh(self, terrain_info):
|
||
"""
|
||
更新地形网格
|
||
"""
|
||
try:
|
||
if terrain_info.get('_mesh_dirty', False):
|
||
heightfield = terrain_info['heightfield']
|
||
if not heightfield:
|
||
return
|
||
|
||
terrain = terrain_info['terrain']
|
||
terrain.setHeightfield(heightfield)
|
||
terrain.generate()
|
||
|
||
# 重新创建碰撞体
|
||
self._recreate_collision(terrain_info['node'])
|
||
|
||
# 清除脏标记
|
||
terrain_info['_mesh_dirty'] = False
|
||
|
||
except Exception as e:
|
||
print(f"更新地形网格时出错: {e}")
|
||
|
||
def _recreate_collision(self, terrain_node):
|
||
"""
|
||
重新创建碰撞体(增强版)
|
||
"""
|
||
try:
|
||
from panda3d.core import BitMask32
|
||
|
||
# 移除旧的碰撞体
|
||
for child in terrain_node.getChildren():
|
||
if child.getName().startswith("terrain_collision_"):
|
||
child.removeNode()
|
||
|
||
# 设置碰撞掩码
|
||
terrain_node.setCollideMask(BitMask32.bit(2))
|
||
|
||
# 为子节点设置碰撞掩码
|
||
for child in terrain_node.getChildren():
|
||
child.setCollideMask(BitMask32.bit(2))
|
||
|
||
except Exception as e:
|
||
print(f"重新创建碰撞体时出错: {e}")
|
||
|
||
def create_morph_target(self, terrain_info, target_name, target_heightfield,
|
||
weights=None, interpolation='linear'):
|
||
"""
|
||
创建morph目标(增强版)
|
||
"""
|
||
try:
|
||
# 验证目标高度图
|
||
if not target_heightfield:
|
||
print("无效的目标高度图")
|
||
return None
|
||
|
||
# 创建morph目标信息
|
||
morph_target = {
|
||
'name': target_name,
|
||
'terrain_info': terrain_info,
|
||
'heightfield': target_heightfield,
|
||
'weights': weights if weights is not None else {},
|
||
'interpolation': interpolation,
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存morph目标
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.morph_targets:
|
||
self.morph_targets[terrain_node] = {}
|
||
self.morph_targets[terrain_node][target_name] = morph_target
|
||
|
||
print(f"创建morph目标: {target_name}")
|
||
return morph_target
|
||
|
||
except Exception as e:
|
||
print(f"创建morph目标时出错: {e}")
|
||
return None
|
||
|
||
def apply_morph_target(self, terrain_info, target_name, weight=1.0,
|
||
blend_mode='linear', preserve_volume=False):
|
||
"""
|
||
应用morph目标(增强版)
|
||
"""
|
||
try:
|
||
terrain_node = terrain_info['node']
|
||
if (terrain_node not in self.morph_targets or
|
||
target_name not in self.morph_targets[terrain_node]):
|
||
print(f"Morph目标不存在: {target_name}")
|
||
return False
|
||
|
||
morph_target = self.morph_targets[terrain_node][target_name]
|
||
source_heightfield = terrain_info['heightfield']
|
||
target_heightfield = morph_target['heightfield']
|
||
|
||
if not source_heightfield or not target_heightfield:
|
||
return False
|
||
|
||
# 验证高度图尺寸
|
||
if (source_heightfield.getXSize() != target_heightfield.getXSize() or
|
||
source_heightfield.getYSize() != target_heightfield.getYSize()):
|
||
print("源和目标高度图尺寸不匹配")
|
||
return False
|
||
|
||
# 获取高度图尺寸
|
||
width = source_heightfield.getXSize()
|
||
height_value = source_heightfield.getYSize()
|
||
|
||
# 应用morph混合
|
||
for y in range(height_value):
|
||
for x in range(width):
|
||
source_height = source_heightfield.getRed(x, y)
|
||
target_height = target_heightfield.getRed(x, y)
|
||
|
||
# 根据混合模式计算混合高度
|
||
if blend_mode == 'linear':
|
||
blended_height = source_height * (1.0 - weight) + target_height * weight
|
||
elif blend_mode == 'ease_in':
|
||
t = weight * weight
|
||
blended_height = source_height * (1.0 - t) + target_height * t
|
||
elif blend_mode == 'ease_out':
|
||
t = 1.0 - (1.0 - weight) * (1.0 - weight)
|
||
blended_height = source_height * (1.0 - t) + target_height * t
|
||
else:
|
||
blended_height = source_height * (1.0 - weight) + target_height * weight
|
||
|
||
# 体积保持(简化实现)
|
||
if preserve_volume:
|
||
# 这里可以实现更复杂的体积保持算法
|
||
pass
|
||
|
||
# 限制高度范围
|
||
blended_height = max(0.0, min(1.0, blended_height))
|
||
|
||
source_heightfield.setRed(x, y, blended_height)
|
||
source_heightfield.setGreen(x, y, blended_height)
|
||
source_heightfield.setBlue(x, y, blended_height)
|
||
|
||
# 标记需要更新网格
|
||
terrain_info['_mesh_dirty'] = True
|
||
|
||
print(f"应用morph目标: {target_name} (权重: {weight})")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"应用morph目标时出错: {e}")
|
||
return False
|
||
|
||
def create_vertex_animation(self, terrain_info, vertex_indices,
|
||
target_positions, duration=2.0,
|
||
easing='linear', loop=False):
|
||
"""
|
||
创建顶点动画(增强版)
|
||
"""
|
||
try:
|
||
# 创建顶点动画信息
|
||
vertex_animation = {
|
||
'terrain_info': terrain_info,
|
||
'vertex_indices': vertex_indices,
|
||
'target_positions': target_positions,
|
||
'duration': duration,
|
||
'easing': easing,
|
||
'loop': loop,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'current_time': 0.0,
|
||
'blend_weight': 1.0,
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 验证数据
|
||
if len(vertex_indices) != len(target_positions):
|
||
print("顶点索引和目标位置数量不匹配")
|
||
return None
|
||
|
||
# 保存顶点动画
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.vertex_animations:
|
||
self.vertex_animations[terrain_node] = []
|
||
self.vertex_animations[terrain_node].append(vertex_animation)
|
||
|
||
print("创建顶点动画")
|
||
return vertex_animation
|
||
|
||
except Exception as e:
|
||
print(f"创建顶点动画时出错: {e}")
|
||
return None
|
||
|
||
def update_vertex_animations(self, time_delta):
|
||
"""
|
||
更新顶点动画(增强版)
|
||
"""
|
||
try:
|
||
if not self.animation_enabled:
|
||
return
|
||
|
||
current_time = self.world.globalClock.getFrameTime()
|
||
scaled_time_delta = time_delta * self.animation_speed
|
||
|
||
# 更新所有地形的顶点动画
|
||
for terrain_node, animations in self.vertex_animations.items():
|
||
for vertex_anim in animations[:]: # 使用切片复制
|
||
if vertex_anim['active']:
|
||
# 更新当前时间
|
||
vertex_anim['current_time'] += scaled_time_delta
|
||
|
||
# 检查是否需要循环
|
||
if vertex_anim['current_time'] > vertex_anim['duration']:
|
||
if vertex_anim['loop']:
|
||
vertex_anim['current_time'] = vertex_anim['current_time'] % vertex_anim['duration']
|
||
else:
|
||
vertex_anim['active'] = False
|
||
continue
|
||
|
||
# 计算进度
|
||
progress = vertex_anim['current_time'] / vertex_anim['duration']
|
||
eased_progress = self._apply_easing(progress, vertex_anim['easing'])
|
||
|
||
# 应用顶点动画
|
||
self._apply_vertex_animation(vertex_anim, eased_progress)
|
||
|
||
except Exception as e:
|
||
print(f"更新顶点动画时出错: {e}")
|
||
|
||
def _apply_vertex_animation(self, vertex_anim, progress):
|
||
"""
|
||
应用顶点动画
|
||
"""
|
||
try:
|
||
# 这里可以实现顶点的插值动画
|
||
# 由于GeoMipTerrain的限制,这个功能需要特殊处理
|
||
# 实际项目中可能需要直接操作顶点缓冲区
|
||
pass
|
||
|
||
except Exception as e:
|
||
print(f"应用顶点动画时出错: {e}")
|
||
|
||
def create_skeletal_animation(self, terrain_info, bone_hierarchy,
|
||
keyframes, loop=False, speed=1.0):
|
||
"""
|
||
创建骨骼动画
|
||
"""
|
||
try:
|
||
# 创建骨骼动画信息
|
||
skeletal_animation = {
|
||
'terrain_info': terrain_info,
|
||
'bone_hierarchy': bone_hierarchy,
|
||
'keyframes': keyframes,
|
||
'loop': loop,
|
||
'speed': speed,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'current_time': 0.0,
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存骨骼动画
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.skeletal_animations:
|
||
self.skeletal_animations[terrain_node] = []
|
||
self.skeletal_animations[terrain_node].append(skeletal_animation)
|
||
|
||
print("创建骨骼动画")
|
||
return skeletal_animation
|
||
|
||
except Exception as e:
|
||
print(f"创建骨骼动画时出错: {e}")
|
||
return None
|
||
|
||
def update_skeletal_animations(self, time_delta):
|
||
"""
|
||
更新骨骼动画
|
||
"""
|
||
try:
|
||
if not self.animation_enabled:
|
||
return
|
||
|
||
current_time = self.world.globalClock.getFrameTime()
|
||
scaled_time_delta = time_delta * self.animation_speed
|
||
|
||
# 更新所有地形的骨骼动画
|
||
for terrain_node, animations in self.skeletal_animations.items():
|
||
for skeletal_anim in animations:
|
||
if skeletal_anim['active']:
|
||
# 更新当前时间
|
||
skeletal_anim['current_time'] += scaled_time_delta * skeletal_anim['speed']
|
||
|
||
# 检查是否需要循环
|
||
if skeletal_anim['current_time'] > self._get_animation_duration(skeletal_anim['keyframes']):
|
||
if skeletal_anim['loop']:
|
||
skeletal_anim['current_time'] = 0.0
|
||
else:
|
||
skeletal_anim['active'] = False
|
||
continue
|
||
|
||
# 应用骨骼动画
|
||
self._apply_skeletal_animation(skeletal_anim, skeletal_anim['current_time'])
|
||
|
||
except Exception as e:
|
||
print(f"更新骨骼动画时出错: {e}")
|
||
|
||
def _get_animation_duration(self, keyframes):
|
||
"""
|
||
获取动画持续时间
|
||
"""
|
||
try:
|
||
if not keyframes:
|
||
return 0.0
|
||
return max([kf['time'] for kf in keyframes])
|
||
except Exception as e:
|
||
print(f"获取动画持续时间时出错: {e}")
|
||
return 0.0
|
||
|
||
def _apply_skeletal_animation(self, skeletal_anim, current_time):
|
||
"""
|
||
应用骨骼动画
|
||
"""
|
||
try:
|
||
# 这里可以实现骨骼动画的插值和应用
|
||
# 实际项目中需要处理骨骼层次结构和蒙皮
|
||
pass
|
||
|
||
except Exception as e:
|
||
print(f"应用骨骼动画时出错: {e}")
|
||
|
||
def set_animation_enabled(self, enabled):
|
||
"""
|
||
启用或禁用动画系统
|
||
"""
|
||
self.animation_enabled = enabled
|
||
print(f"地形动画系统已{'启用' if enabled else '禁用'}")
|
||
|
||
def set_animation_speed(self, speed):
|
||
"""
|
||
设置动画速度
|
||
"""
|
||
self.animation_speed = max(0.0, speed)
|
||
print(f"动画速度设置为: {speed}x")
|
||
|
||
def pause_all_animations(self):
|
||
"""
|
||
暂停所有动画
|
||
"""
|
||
self.time_manager['paused'] = True
|
||
print("所有动画已暂停")
|
||
|
||
def resume_all_animations(self):
|
||
"""
|
||
恢复所有动画
|
||
"""
|
||
self.time_manager['paused'] = False
|
||
print("所有动画已恢复")
|
||
|
||
def get_animation_stats(self):
|
||
"""
|
||
获取动画统计信息(增强版)
|
||
"""
|
||
stats = {
|
||
'enabled': self.animation_enabled,
|
||
'animation_speed': self.animation_speed,
|
||
'paused': self.time_manager['paused'],
|
||
'global_time': self.time_manager['global_time'],
|
||
'terrain_animations': 0,
|
||
'deformation_effects': 0,
|
||
'morph_targets': 0,
|
||
'vertex_animations': 0,
|
||
'skeletal_animations': 0,
|
||
'active_animations': 0,
|
||
'performance': {
|
||
'last_update_time': self.performance_stats['last_update_time'],
|
||
'avg_update_time': self.performance_stats['avg_update_time'],
|
||
'update_count': self.performance_stats['update_count']
|
||
}
|
||
}
|
||
|
||
# 统计动画数量
|
||
for terrain_node, animations in self.terrain_animations.items():
|
||
stats['terrain_animations'] += len(animations)
|
||
for animation in animations.values():
|
||
if animation['playing']:
|
||
stats['active_animations'] += 1
|
||
|
||
# 统计变形效果
|
||
for terrain_node, deformations in self.deformation_effects.items():
|
||
stats['deformation_effects'] += len(deformations)
|
||
|
||
# 统计morph目标
|
||
for terrain_node, morphs in self.morph_targets.items():
|
||
stats['morph_targets'] += len(morphs)
|
||
|
||
# 统计顶点动画
|
||
for terrain_node, animations in self.vertex_animations.items():
|
||
stats['vertex_animations'] += len(animations)
|
||
|
||
# 统计骨骼动画
|
||
for terrain_node, animations in self.skeletal_animations.items():
|
||
stats['skeletal_animations'] += len(animations)
|
||
|
||
return stats
|
||
|
||
def save_animation_data(self, output_path):
|
||
"""
|
||
保存动画数据(增强版)
|
||
"""
|
||
try:
|
||
# 收集动画数据
|
||
animation_data = {
|
||
'terrain_animations': {},
|
||
'morph_targets': {},
|
||
'settings': {
|
||
'animation_speed': self.animation_speed,
|
||
'time_scale': self.time_manager['animation_time_scale']
|
||
}
|
||
}
|
||
|
||
# 保存地形动画信息
|
||
for terrain_node, animations in self.terrain_animations.items():
|
||
node_name = terrain_node.getName() if not terrain_node.isEmpty() else "unknown"
|
||
animation_data['terrain_animations'][node_name] = {}
|
||
|
||
for anim_name, anim_info in animations.items():
|
||
animation_data['terrain_animations'][node_name][anim_name] = {
|
||
'name': anim_info['name'],
|
||
'duration': anim_info['duration'],
|
||
'loop': anim_info['loop'],
|
||
'speed': anim_info['speed'],
|
||
'easing': anim_info['easing'],
|
||
'keyframes': anim_info['keyframes']
|
||
}
|
||
|
||
# 保存morph目标信息
|
||
for terrain_node, morphs in self.morph_targets.items():
|
||
node_name = terrain_node.getName() if not terrain_node.isEmpty() else "unknown"
|
||
animation_data['morph_targets'][node_name] = {}
|
||
|
||
for morph_name, morph_info in morphs.items():
|
||
animation_data['morph_targets'][node_name][morph_name] = {
|
||
'name': morph_info['name'],
|
||
'interpolation': morph_info['interpolation']
|
||
# 注意:高度图数据可能太大,不适合保存在JSON中
|
||
}
|
||
|
||
# 确保输出目录存在
|
||
output_dir = os.path.dirname(output_path)
|
||
if not os.path.exists(output_dir):
|
||
os.makedirs(output_dir)
|
||
|
||
# 写入JSON文件
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
json.dump(animation_data, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"动画数据保存成功: {output_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"保存动画数据时出错: {e}")
|
||
return False
|
||
|
||
def load_animation_data(self, input_path):
|
||
"""
|
||
加载动画数据(增强版)
|
||
"""
|
||
try:
|
||
if not os.path.exists(input_path):
|
||
print(f"动画数据文件不存在: {input_path}")
|
||
return False
|
||
|
||
# 读取JSON文件
|
||
with open(input_path, 'r', encoding='utf-8') as f:
|
||
animation_data = json.load(f)
|
||
|
||
# 恢复设置
|
||
if 'settings' in animation_data:
|
||
settings = animation_data['settings']
|
||
if 'animation_speed' in settings:
|
||
self.animation_speed = settings['animation_speed']
|
||
if 'time_scale' in settings:
|
||
self.time_manager['animation_time_scale'] = settings['time_scale']
|
||
|
||
# 加载地形动画
|
||
# 注意:实际加载需要在地形创建后进行
|
||
self._pending_animations = animation_data.get('terrain_animations', {})
|
||
self._pending_morph_targets = animation_data.get('morph_targets', {})
|
||
|
||
print(f"动画数据加载成功: {input_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"加载动画数据时出错: {e}")
|
||
return False
|
||
|
||
def apply_pending_animations(self, terrain_manager):
|
||
"""
|
||
应用待处理的动画数据
|
||
"""
|
||
try:
|
||
if hasattr(self, '_pending_animations'):
|
||
# 应用动画数据
|
||
delattr(self, '_pending_animations')
|
||
|
||
if hasattr(self, '_pending_morph_targets'):
|
||
# 应用morph目标数据
|
||
delattr(self, '_pending_morph_targets')
|
||
|
||
except Exception as e:
|
||
print(f"应用待处理动画数据时出错: {e}")
|
||
|
||
def clear_all_animations(self):
|
||
"""
|
||
清除所有动画(增强版)
|
||
"""
|
||
try:
|
||
# 停止所有动画
|
||
for terrain_node, animations in self.terrain_animations.items():
|
||
for animation_info in animations.values():
|
||
animation_info['playing'] = False
|
||
|
||
# 清空数据结构
|
||
self.terrain_animations = {}
|
||
self.deformation_effects = {}
|
||
self.morph_targets = {}
|
||
self.vertex_animations = {}
|
||
self.skeletal_animations = {}
|
||
|
||
# 清除挂起的数据
|
||
if hasattr(self, '_pending_animations'):
|
||
delattr(self, '_pending_animations')
|
||
if hasattr(self, '_pending_morph_targets'):
|
||
delattr(self, '_pending_morph_targets')
|
||
|
||
# 重置时间管理器
|
||
self.time_manager['global_time'] = 0.0
|
||
self.time_manager['paused'] = False
|
||
|
||
# 重置性能统计
|
||
self.performance_stats['update_count'] = 0
|
||
self.performance_stats['avg_update_time'] = 0.0
|
||
|
||
print("清除所有动画")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"清除所有动画时出错: {e}")
|
||
return False
|
||
|
||
def create_explosion_deformation(self, terrain_info, center_pos, radius, strength,
|
||
duration=1.0, easing='ease_out'):
|
||
"""
|
||
创建爆炸变形效果(增强版)
|
||
"""
|
||
try:
|
||
# 创建爆炸变形效果
|
||
explosion_info = {
|
||
'terrain_info': terrain_info,
|
||
'center_pos': Vec3(center_pos),
|
||
'radius': radius,
|
||
'strength': strength,
|
||
'duration': duration,
|
||
'easing': easing,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'explosion',
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存爆炸效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(explosion_info)
|
||
|
||
print("创建爆炸变形效果")
|
||
return explosion_info
|
||
|
||
except Exception as e:
|
||
print(f"创建爆炸变形效果时出错: {e}")
|
||
return None
|
||
|
||
def create_ripple_effect(self, terrain_info, center_pos, radius, strength,
|
||
speed=1.0, duration=3.0):
|
||
"""
|
||
创建涟漪效果
|
||
"""
|
||
try:
|
||
# 创建涟漪效果信息
|
||
ripple_info = {
|
||
'terrain_info': terrain_info,
|
||
'center_pos': Vec3(center_pos),
|
||
'radius': radius,
|
||
'strength': strength,
|
||
'speed': speed,
|
||
'duration': duration,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'ripple',
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存涟漪效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(ripple_info)
|
||
|
||
print("创建涟漪效果")
|
||
return ripple_info
|
||
|
||
except Exception as e:
|
||
print(f"创建涟漪效果时出错: {e}")
|
||
return None
|
||
|
||
def create_terrain_crack(self, terrain_info, start_pos, end_pos, width, depth,
|
||
duration=2.0, easing='ease_in'):
|
||
"""
|
||
创建地形裂缝效果
|
||
"""
|
||
try:
|
||
# 创建裂缝效果信息
|
||
crack_info = {
|
||
'terrain_info': terrain_info,
|
||
'start_pos': Vec3(start_pos),
|
||
'end_pos': Vec3(end_pos),
|
||
'width': width,
|
||
'depth': depth,
|
||
'duration': duration,
|
||
'easing': easing,
|
||
'start_time': self.world.globalClock.getFrameTime(),
|
||
'active': True,
|
||
'progress': 0.0,
|
||
'type': 'crack',
|
||
'creation_time': self.world.globalClock.getFrameTime()
|
||
}
|
||
|
||
# 保存裂缝效果
|
||
terrain_node = terrain_info['node']
|
||
if terrain_node not in self.deformation_effects:
|
||
self.deformation_effects[terrain_node] = []
|
||
self.deformation_effects[terrain_node].append(crack_info)
|
||
|
||
print("创建地形裂缝效果")
|
||
return crack_info
|
||
|
||
except Exception as e:
|
||
print(f"创建地形裂缝效果时出错: {e}")
|
||
return None |