ui替换
This commit is contained in:
parent
bde899a04f
commit
fbf71fd6a2
606
demo.py
606
demo.py
@ -5,6 +5,7 @@ from direct.showbase.ShowBase import ShowBase
|
||||
from direct.task import Task
|
||||
from direct.actor.Actor import Actor
|
||||
from direct.interval.IntervalGlobal import Sequence
|
||||
from panda3d.core import NodePath
|
||||
|
||||
import p3dimgui
|
||||
|
||||
@ -155,6 +156,10 @@ class MyWorld(CoreWorld):
|
||||
# 初始化资源管理器
|
||||
self.resource_manager = ResourceManager(self)
|
||||
|
||||
# 初始化Actor缓存系统(用于动画控制)
|
||||
self._actor_cache = {}
|
||||
print("✓ Actor缓存系统初始化完成")
|
||||
|
||||
# 初始化自定义鼠标控制器(视角移动)
|
||||
self.mouse_controller = CustomMouseController(self)
|
||||
self.mouse_controller.setUp(mouse_speed=25, move_speed=20)
|
||||
@ -1463,6 +1468,10 @@ class MyWorld(CoreWorld):
|
||||
elif node_type == "模型":
|
||||
if imgui.collapsing_header("模型属性"):
|
||||
self._draw_model_properties(node)
|
||||
|
||||
# 动画控制组(只对模型显示)
|
||||
if imgui.collapsing_header("动画控制"):
|
||||
self._draw_animation_properties(node)
|
||||
|
||||
# 外观属性组(通用)
|
||||
if imgui.collapsing_header("外观属性"):
|
||||
@ -1476,6 +1485,236 @@ class MyWorld(CoreWorld):
|
||||
if imgui.collapsing_header("操作"):
|
||||
self._draw_property_actions(node)
|
||||
|
||||
def _getActor(self, origin_model):
|
||||
"""
|
||||
获取或创建模型的Actor,用于动画控制
|
||||
复用Qt版本经过验证的实现方式
|
||||
"""
|
||||
# 检查缓存
|
||||
if origin_model in self._actor_cache:
|
||||
return self._actor_cache[origin_model]
|
||||
|
||||
# 尝试直接从内存创建
|
||||
if origin_model.hasTag("can_create_actor_from_memory"):
|
||||
try:
|
||||
test_actor = Actor(origin_model)
|
||||
anims = test_actor.getAnimNames()
|
||||
self._actor_cache[origin_model] = test_actor
|
||||
print(f"[Actor加载] 内存创建检测到动画: {anims}")
|
||||
if anims:
|
||||
return test_actor
|
||||
else:
|
||||
test_actor.cleanup()
|
||||
test_actor.removeNode()
|
||||
except Exception as e:
|
||||
print(f"从内存模型创建Actor失败: {e}")
|
||||
|
||||
# 如果不能直接从内存创建,再尝试通过文件路径加载
|
||||
filepath = origin_model.getTag("model_path")
|
||||
if not filepath:
|
||||
return None
|
||||
|
||||
print(f"[Actor加载] 尝试加载: {filepath}")
|
||||
|
||||
# 处理跨平台路径问题
|
||||
import os
|
||||
# 检查路径是否有效,如果无效则尝试修复
|
||||
if not os.path.exists(filepath):
|
||||
original_filepath = filepath
|
||||
# 尝试多种修复策略
|
||||
fixed = False
|
||||
|
||||
import platform
|
||||
# 策略1: 处理Linux风格路径在Windows上的问题
|
||||
if filepath.startswith('/') and platform.system() == "Windows":
|
||||
print("[路径转换] 尝试处理Linux风格路径:", filepath)
|
||||
path_parts = filepath.split('/')
|
||||
print(platform.system())
|
||||
if len(path_parts) > 1:
|
||||
drive_letter = path_parts[1].upper() + ':\\' # 添加反斜杠确保正确路径格式
|
||||
remaining_path = '\\'.join(path_parts[2:]) if len(path_parts) > 2 else ''
|
||||
potential_path = os.path.join(drive_letter, remaining_path)
|
||||
print(f"[路径转换] 构造的潜在路径: {potential_path}")
|
||||
if os.path.exists(potential_path):
|
||||
filepath = potential_path
|
||||
fixed = True
|
||||
print(f"[路径转换] 成功: {original_filepath} -> {filepath}")
|
||||
else:
|
||||
print(f"[路径转换] 文件不存在: {potential_path}")
|
||||
|
||||
|
||||
# 策略2: 处理路径分隔符问题
|
||||
if not fixed:
|
||||
# 尝试规范化路径
|
||||
normalized_path = os.path.normpath(filepath)
|
||||
print(f"[路径规范化] 尝试规范化路径: {filepath} -> {normalized_path}")
|
||||
if os.path.exists(normalized_path):
|
||||
filepath = normalized_path
|
||||
fixed = True
|
||||
print(f"[路径规范化] 成功: {filepath}")
|
||||
else:
|
||||
print(f"[路径规范化] 文件不存在: {normalized_path}")
|
||||
|
||||
# 策略3: 在Resources目录中查找
|
||||
if not fixed:
|
||||
# 尝试在Resources目录中查找文件
|
||||
resources_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "Resources")
|
||||
filename = os.path.basename(filepath)
|
||||
potential_path = os.path.join(resources_path, filename)
|
||||
print(f"[Resources查找] 尝试在Resources目录查找: {potential_path}")
|
||||
if os.path.exists(potential_path):
|
||||
filepath = potential_path
|
||||
fixed = True
|
||||
print(f"[Resources查找] 成功: {filepath}")
|
||||
else:
|
||||
print(f"[Resources查找] 文件不存在: {potential_path}")
|
||||
|
||||
if fixed:
|
||||
print(f"路径修复: {original_filepath} -> {filepath}")
|
||||
# 更新模型标签
|
||||
origin_model.setTag("model_path", filepath)
|
||||
else:
|
||||
print(f"[警告] 模型文件不存在: {filepath}")
|
||||
return None
|
||||
|
||||
# 检查是否是 FBX 文件,如果是,使用专门的 FBX 动画加载器
|
||||
if filepath.lower().endswith('.fbx'):
|
||||
pass
|
||||
#return self._createFBXActor(origin_model, filepath)
|
||||
|
||||
# 其他格式使用标准 Actor 加载
|
||||
try:
|
||||
import gltf
|
||||
from panda3d.core import Filename
|
||||
|
||||
# 将Panda3D路径转换为操作系统特定路径
|
||||
panda_filename = Filename(filepath)
|
||||
os_specific_path = panda_filename.to_os_specific()
|
||||
print(f"[路径转换] {filepath} -> {os_specific_path}")
|
||||
|
||||
print(f"[GLTF加载] 尝试加载: {os_specific_path}")
|
||||
|
||||
# 使用明确的设置确保动画被加载
|
||||
gltf_settings = gltf.GltfSettings(skip_animations=False)
|
||||
model_root = gltf.load_model(os_specific_path, gltf_settings)
|
||||
model_node = NodePath(model_root)
|
||||
test_actor = Actor(model_node)
|
||||
anims = test_actor.getAnimNames()
|
||||
test_actor.reparentTo(self.render)
|
||||
self._actor_cache[origin_model] = test_actor
|
||||
print(f"[Actor加载] 标准加载检测到动画: {anims}")
|
||||
if not anims:
|
||||
test_actor.cleanup()
|
||||
test_actor.removeNode()
|
||||
return None
|
||||
return test_actor
|
||||
except Exception as e:
|
||||
print(f"创建Actor失败: {e}")
|
||||
return None
|
||||
|
||||
def _getModelFormat(self, origin_model):
|
||||
"""获取模型格式信息"""
|
||||
filepath = origin_model.getTag("model_path")
|
||||
original_path = origin_model.getTag("original_path")
|
||||
converted_from = origin_model.getTag("converted_from")
|
||||
|
||||
if filepath:
|
||||
ext = filepath.lower().split('.')[-1]
|
||||
format_name = ext.upper()
|
||||
|
||||
# 如果是转换后的文件,显示转换信息
|
||||
if converted_from and original_path:
|
||||
original_ext = converted_from.upper()
|
||||
format_name = f"{format_name} (从{original_ext}转换)"
|
||||
|
||||
return format_name
|
||||
return "未知"
|
||||
|
||||
def _processAnimationNames(self, origin_model, anim_names):
|
||||
"""处理和分析动画名称,返回 [(显示名称, 原始名称), ...]"""
|
||||
format_info = self._getModelFormat(origin_model)
|
||||
processed = []
|
||||
|
||||
print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||
|
||||
for name in anim_names:
|
||||
display_name = name
|
||||
original_name = name
|
||||
|
||||
if format_info == "GLB":
|
||||
# GLB 格式通常有真实的动画名称
|
||||
if "|" in name:
|
||||
# 处理类似 'Armature|mixamo.com|Layer0' 的名称
|
||||
parts = name.split("|")
|
||||
if "mixamo" in name.lower():
|
||||
# Mixamo 动画
|
||||
display_name = f"Mixamo_{parts[-1]}" if len(parts) > 1 else name
|
||||
elif len(parts) > 2:
|
||||
# 其他复杂命名
|
||||
display_name = f"{parts[0]}_{parts[-1]}"
|
||||
else:
|
||||
display_name = parts[-1]
|
||||
|
||||
elif format_info == "FBX":
|
||||
# FBX 格式可能需要特殊处理
|
||||
if self._isLikelyBoneGroup(name):
|
||||
# 检查是否是骨骼组而非动画
|
||||
print(f"[警告] '{name}' 可能不是真正的动画序列,而是骨骼组")
|
||||
display_name = f"⚠️ {name} (可能非动画)"
|
||||
else:
|
||||
display_name = name
|
||||
|
||||
elif format_info in ["EGG", "BAM"]:
|
||||
# 原生格式通常命名规范
|
||||
display_name = name
|
||||
|
||||
processed.append((display_name, original_name))
|
||||
print(f"[动画分析] {original_name} → {display_name}")
|
||||
|
||||
return processed
|
||||
|
||||
def _isLikelyBoneGroup(self, name):
|
||||
"""判断动画名称是否更像骨骼组而不是动画序列"""
|
||||
bone_indicators = ['joints', 'bones', 'skeleton', 'surface', 'mesh', 'beta', 'rig']
|
||||
name_lower = name.lower()
|
||||
|
||||
# 如果包含这些关键词,可能是骨骼组
|
||||
for indicator in bone_indicators:
|
||||
if indicator in name_lower:
|
||||
return True
|
||||
|
||||
# 如果名称太简单(少于3个字符),可能不是动画
|
||||
if len(name) < 3:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _analyzeAnimationQuality(self, actor, anim_names, format_info):
|
||||
"""分析动画质量和类型(优化版本,减少详细分析以提高性能)"""
|
||||
try:
|
||||
valid_anims = 0
|
||||
|
||||
# 简化分析:只检查动画是否存在,不详细分析帧数
|
||||
for anim_name in anim_names:
|
||||
try:
|
||||
control = actor.getAnimControl(anim_name)
|
||||
if control and control.getNumFrames() > 1:
|
||||
valid_anims += 1
|
||||
except Exception:
|
||||
# 忽略单个动画的分析错误,继续处理其他动画
|
||||
continue
|
||||
|
||||
if valid_anims == 0:
|
||||
return "⚠️ 无有效动画"
|
||||
elif valid_anims < len(anim_names):
|
||||
return f"⚠️ {valid_anims}/{len(anim_names)} 个有效"
|
||||
else:
|
||||
return f"✓ {valid_anims} 个动画"
|
||||
|
||||
except Exception as e:
|
||||
# 简化错误处理
|
||||
return "分析异常"
|
||||
|
||||
def _get_node_type_from_node(self, node):
|
||||
"""从节点判断其类型"""
|
||||
# 检查是否为GUI元素
|
||||
@ -1538,8 +1777,36 @@ class MyWorld(CoreWorld):
|
||||
if has_script:
|
||||
badges.append(("脚本", (0.8, 0.4, 0.8, 1.0))) # 紫色
|
||||
|
||||
# 动画徽章
|
||||
has_animation = hasattr(node, 'getPythonTag') and node.getPythonTag('animation')
|
||||
# 动画徽章(优化检测逻辑,避免重复创建Actor)
|
||||
has_animation = False
|
||||
if node_type == "模型": # 只对模型类型进行动画检测
|
||||
# 首先检查是否已经缓存了检测结果
|
||||
cached_result = node.getPythonTag('animation')
|
||||
if cached_result is not None:
|
||||
has_animation = cached_result
|
||||
else:
|
||||
# 只有在未缓存时才进行检测
|
||||
try:
|
||||
# 使用轻量级检测:先检查文件扩展名
|
||||
model_path = node.getTag("model_path")
|
||||
if model_path and model_path.lower().endswith(('.glb', '.gltf', '.fbx')):
|
||||
# 对于可能包含动画的格式,才进行Actor检测
|
||||
actor = self._getActor(node)
|
||||
if actor and actor.getAnimNames():
|
||||
has_animation = True
|
||||
# 缓存检测结果
|
||||
node.setPythonTag('animation', has_animation)
|
||||
print(f"[动画检测] {node.getName()}: {'有动画' if has_animation else '无动画'}")
|
||||
else:
|
||||
# 对于不太可能有动画的格式,直接标记为无动画
|
||||
node.setPythonTag('animation', False)
|
||||
except Exception as e:
|
||||
print(f"动画检测失败: {e}")
|
||||
node.setPythonTag('animation', False)
|
||||
else:
|
||||
# 对于非模型类型,检查已有的动画标签
|
||||
has_animation = hasattr(node, 'getPythonTag') and node.getPythonTag('animation')
|
||||
|
||||
if has_animation:
|
||||
badges.append(("动画", (0.4, 0.8, 0.4, 1.0))) # 绿色
|
||||
|
||||
@ -1955,13 +2222,338 @@ class MyWorld(CoreWorld):
|
||||
|
||||
def _draw_model_properties(self, node):
|
||||
"""绘制模型属性"""
|
||||
imgui.text("模型属性")
|
||||
# 获取模型信息
|
||||
model_path = node.getTag("model_path") if node.hasTag("model_path") else "未知"
|
||||
|
||||
# 模型路径
|
||||
imgui.text("模型路径: (暂不支持显示)")
|
||||
imgui.text("模型路径:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_path)
|
||||
|
||||
# 材质数量
|
||||
imgui.text("材质数量: (暂不支持显示)")
|
||||
# 模型基本信息
|
||||
imgui.text("模型名称:")
|
||||
imgui.same_line()
|
||||
model_name = node.getName() or "未命名模型"
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_name)
|
||||
|
||||
# 模型位置信息
|
||||
imgui.text("位置:")
|
||||
imgui.same_line()
|
||||
pos = node.getPos()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{pos.x:.2f} Y:{pos.y:.2f} Z:{pos.z:.2f}")
|
||||
|
||||
# 模型缩放信息
|
||||
imgui.text("缩放:")
|
||||
imgui.same_line()
|
||||
scale = node.getScale()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{scale.x:.2f} Y:{scale.y:.2f} Z:{scale.z:.2f}")
|
||||
|
||||
# 模型旋转信息
|
||||
imgui.text("旋转:")
|
||||
imgui.same_line()
|
||||
hpr = node.getHpr()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"H:{hpr.x:.1f}° P:{hpr.y:.1f}° R:{hpr.z:.1f}°")
|
||||
|
||||
def _draw_animation_properties(self, node):
|
||||
"""绘制动画控制属性面板(优化版本,使用缓存避免重复计算)"""
|
||||
# 检查是否已经缓存了动画信息
|
||||
cached_anim_info = node.getPythonTag("cached_anim_info")
|
||||
cached_processed_names = node.getPythonTag("cached_processed_names")
|
||||
|
||||
# 只有在没有缓存时才进行完整的动画检测和处理
|
||||
if cached_anim_info is None or cached_processed_names is None:
|
||||
# 获取Actor
|
||||
actor = self._getActor(node)
|
||||
if not actor:
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "此模型不包含动画")
|
||||
return
|
||||
|
||||
# 获取和分析动画名称
|
||||
anim_names = actor.getAnimNames()
|
||||
processed_names = self._processAnimationNames(node, anim_names)
|
||||
|
||||
if not processed_names:
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
|
||||
# 缓存空结果
|
||||
node.setPythonTag("cached_processed_names", [])
|
||||
node.setPythonTag("cached_anim_info", "无动画")
|
||||
return
|
||||
|
||||
# 计算并缓存动画信息
|
||||
format_info = self._getModelFormat(node)
|
||||
animation_info = self._analyzeAnimationQuality(actor, anim_names, format_info)
|
||||
info_text = f"格式: {format_info} | 动画数量: {len(processed_names)}"
|
||||
if animation_info:
|
||||
info_text += f" | {animation_info}"
|
||||
|
||||
# 缓存结果
|
||||
node.setPythonTag("cached_anim_info", info_text)
|
||||
node.setPythonTag("cached_processed_names", processed_names)
|
||||
|
||||
else:
|
||||
# 使用缓存的数据
|
||||
info_text = cached_anim_info
|
||||
processed_names = cached_processed_names
|
||||
|
||||
# 如果缓存的空结果,直接返回
|
||||
if not processed_names:
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
|
||||
return
|
||||
|
||||
# 显示动画信息(使用缓存的数据)
|
||||
imgui.text("信息:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), info_text)
|
||||
|
||||
imgui.spacing()
|
||||
|
||||
# 动画选择下拉框
|
||||
imgui.text("动画名称:")
|
||||
imgui.same_line()
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = node.getPythonTag("selected_animation")
|
||||
if current_anim is None:
|
||||
current_anim = processed_names[0][1] if processed_names else ""
|
||||
node.setPythonTag("selected_animation", current_anim)
|
||||
|
||||
# 查找当前动画的索引
|
||||
current_index = 0
|
||||
for i, (display_name, original_name) in enumerate(processed_names):
|
||||
if original_name == current_anim:
|
||||
current_index = i
|
||||
break
|
||||
|
||||
# 创建下拉框选项
|
||||
animation_options = [display_name for display_name, _ in processed_names]
|
||||
changed, new_index = imgui.combo("##animation_combo", current_index, animation_options)
|
||||
|
||||
if changed and new_index < len(processed_names):
|
||||
selected_display, selected_original = processed_names[new_index]
|
||||
node.setPythonTag("selected_animation", selected_original)
|
||||
print(f"选择动画: {selected_display} (原始名称: {selected_original})")
|
||||
|
||||
imgui.spacing()
|
||||
|
||||
# 控制按钮组
|
||||
imgui.text("控制:")
|
||||
|
||||
# 播放按钮
|
||||
if imgui.button("播放##play_animation"):
|
||||
self._playAnimation(node)
|
||||
imgui.same_line()
|
||||
|
||||
# 暂停按钮
|
||||
if imgui.button("暂停##pause_animation"):
|
||||
self._pauseAnimation(node)
|
||||
imgui.same_line()
|
||||
|
||||
# 停止按钮
|
||||
if imgui.button("停止##stop_animation"):
|
||||
self._stopAnimation(node)
|
||||
imgui.same_line()
|
||||
|
||||
# 循环按钮
|
||||
if imgui.button("循环##loop_animation"):
|
||||
self._loopAnimation(node)
|
||||
|
||||
imgui.spacing()
|
||||
|
||||
# 播放速度控制
|
||||
imgui.text("播放速度:")
|
||||
imgui.same_line()
|
||||
|
||||
# 获取当前速度
|
||||
current_speed = node.getPythonTag("anim_speed")
|
||||
if current_speed is None:
|
||||
current_speed = 1.0
|
||||
node.setPythonTag("anim_speed", current_speed)
|
||||
|
||||
# 速度滑块
|
||||
changed, new_speed = imgui.slider_float("##anim_speed", current_speed, 0.1, 5.0, "%.1f")
|
||||
if changed:
|
||||
node.setPythonTag("anim_speed", new_speed)
|
||||
self._setAnimationSpeed(node, new_speed)
|
||||
|
||||
imgui.same_line()
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "倍速")
|
||||
|
||||
def _playAnimation(self, origin_model):
|
||||
"""播放动画"""
|
||||
actor = self._getActor(origin_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 保存原始世界坐标
|
||||
original_world_pos = origin_model.getPos(self.render)
|
||||
original_world_hpr = origin_model.getHpr(self.render)
|
||||
original_world_scale = origin_model.getScale(self.render)
|
||||
|
||||
# 设置Actor位置和姿态
|
||||
actor.setPos(origin_model.getPos())
|
||||
actor.setHpr(origin_model.getHpr())
|
||||
actor.setScale(origin_model.getScale())
|
||||
|
||||
# 隐藏原始模型,显示Actor
|
||||
origin_model.hide()
|
||||
actor.show()
|
||||
|
||||
# 创建任务来维持世界坐标不变
|
||||
def maintainWorldPosition(task):
|
||||
try:
|
||||
if not actor.isEmpty():
|
||||
actor.setPos(self.render, original_world_pos)
|
||||
actor.setHpr(self.render, original_world_hpr)
|
||||
actor.setScale(self.render, original_world_scale)
|
||||
return task.cont
|
||||
else:
|
||||
return task.done
|
||||
except:
|
||||
return task.done
|
||||
|
||||
# 添加维持位置的任务
|
||||
taskMgr.add(maintainWorldPosition, f"maintain_anim_pos_{id(actor)}")
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = origin_model.getPythonTag("selected_animation")
|
||||
if current_anim:
|
||||
actor.play(current_anim)
|
||||
print(f"『动画播放』:{current_anim}")
|
||||
else:
|
||||
# 兜底:使用第一个可用动画
|
||||
anim_names = actor.getAnimNames()
|
||||
if anim_names:
|
||||
actor.play(anim_names[0])
|
||||
print(f"『动画播放』:{anim_names[0]}")
|
||||
|
||||
def _pauseAnimation(self, origin_model):
|
||||
"""暂停动画"""
|
||||
actor = self._getActor(origin_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 设置Actor位置和姿态
|
||||
actor.setPos(origin_model.getPos())
|
||||
actor.setHpr(origin_model.getHpr())
|
||||
actor.setScale(origin_model.getScale())
|
||||
|
||||
# 隐藏原始模型,显示Actor
|
||||
origin_model.hide()
|
||||
actor.show()
|
||||
|
||||
# 停止动画(保持当前姿势)
|
||||
actor.stop()
|
||||
print("『动画』暂停")
|
||||
|
||||
def _stopAnimation(self, origin_model):
|
||||
"""停止动画"""
|
||||
actor = self._getActor(origin_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 停止动画
|
||||
actor.stop()
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = origin_model.getPythonTag("selected_animation")
|
||||
if current_anim and actor.getAnimControl(current_anim):
|
||||
actor.getAnimControl(current_anim).pose(0)
|
||||
|
||||
# 隐藏Actor,显示原始模型
|
||||
actor.hide()
|
||||
origin_model.show()
|
||||
|
||||
# 移除维持位置的任务
|
||||
taskMgr.remove(f"maintain_anim_pos_{id(actor)}")
|
||||
|
||||
print("『动画』停止切换至原始模型")
|
||||
|
||||
def _loopAnimation(self, origin_model):
|
||||
"""循环播放动画"""
|
||||
actor = self._getActor(origin_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 保存原始世界坐标
|
||||
original_world_pos = origin_model.getPos(self.render)
|
||||
original_world_hpr = origin_model.getHpr(self.render)
|
||||
original_world_scale = origin_model.getScale(self.render)
|
||||
|
||||
# 设置Actor位置和姿态
|
||||
actor.setPos(origin_model.getPos())
|
||||
actor.setHpr(origin_model.getHpr())
|
||||
actor.setScale(origin_model.getScale())
|
||||
|
||||
# 隐藏原始模型,显示Actor
|
||||
origin_model.hide()
|
||||
actor.show()
|
||||
|
||||
# 创建任务来维持世界坐标不变
|
||||
def maintainWorldPosition(task):
|
||||
try:
|
||||
if not actor.isEmpty():
|
||||
actor.setPos(self.render, original_world_pos)
|
||||
actor.setHpr(self.render, original_world_hpr)
|
||||
actor.setScale(self.render, original_world_scale)
|
||||
return task.cont
|
||||
else:
|
||||
return task.done
|
||||
except:
|
||||
return task.done
|
||||
|
||||
# 添加维持位置的任务
|
||||
taskMgr.add(maintainWorldPosition, f"maintain_anim_pos_{id(actor)}")
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = origin_model.getPythonTag("selected_animation")
|
||||
if current_anim:
|
||||
actor.loop(current_anim)
|
||||
print(f"[动画] 循环: {current_anim}")
|
||||
else:
|
||||
# 兜底:使用第一个可用动画
|
||||
anim_names = actor.getAnimNames()
|
||||
if anim_names:
|
||||
actor.loop(anim_names[0])
|
||||
print(f"[动画] 循环: {anim_names[0]}")
|
||||
|
||||
def _setAnimationSpeed(self, origin_model, speed):
|
||||
"""设置动画播放速度"""
|
||||
actor = self._getActor(origin_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = origin_model.getPythonTag("selected_animation")
|
||||
if current_anim:
|
||||
actor.setPlayRate(speed, current_anim)
|
||||
print(f"[动画] 速度设为: {speed} ({current_anim})")
|
||||
else:
|
||||
# 兜底:尝试所有动画
|
||||
anim_names = actor.getAnimNames()
|
||||
for anim_name in anim_names:
|
||||
actor.setPlayRate(speed, anim_name)
|
||||
print(f"[动画] 速度设为: {speed} (所有动画)")
|
||||
|
||||
def _clear_animation_cache(self, node):
|
||||
"""清除节点的动画缓存,当模型发生变化时调用"""
|
||||
node.setPythonTag("cached_anim_info", None)
|
||||
node.setPythonTag("cached_processed_names", None)
|
||||
node.setPythonTag("animation", None) # 同时清除动画检测结果
|
||||
|
||||
# 如果Actor在缓存中,也需要清理
|
||||
if node in self._actor_cache:
|
||||
actor = self._actor_cache[node]
|
||||
try:
|
||||
# 清理相关任务
|
||||
taskMgr.remove(f"maintain_anim_pos_{id(actor)}")
|
||||
# 清理Actor
|
||||
if not actor.isEmpty():
|
||||
actor.cleanup()
|
||||
actor.removeNode()
|
||||
except Exception as e:
|
||||
print(f"清理Actor缓存失败: {e}")
|
||||
finally:
|
||||
del self._actor_cache[node]
|
||||
print(f"[缓存清理] 清除节点 {node.getName()} 的动画缓存")
|
||||
|
||||
def _draw_collision_properties(self, node):
|
||||
"""绘制碰撞检测属性"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user