动画存在问题
This commit is contained in:
parent
4723bd9746
commit
90239b7051
26
imgui.ini
26
imgui.ini
@ -31,21 +31,21 @@ DockId=0x0000000D,0
|
||||
|
||||
[Window][场景树]
|
||||
Pos=0,20
|
||||
Size=339,1084
|
||||
Size=339,611
|
||||
Collapsed=0
|
||||
DockId=0x00000007,0
|
||||
DockId=0x00000003,0
|
||||
|
||||
[Window][属性面板]
|
||||
Pos=1694,20
|
||||
Size=346,1084
|
||||
Pos=1574,20
|
||||
Size=346,1012
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][控制台]
|
||||
Pos=341,705
|
||||
Size=1351,399
|
||||
Pos=0,633
|
||||
Size=339,399
|
||||
Collapsed=0
|
||||
DockId=0x00000006,1
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][脚本管理]
|
||||
Pos=1950,20
|
||||
@ -59,7 +59,7 @@ Collapsed=0
|
||||
|
||||
[Window][WindowOverViewport_11111111]
|
||||
Pos=0,20
|
||||
Size=2040,1084
|
||||
Size=1920,1012
|
||||
Collapsed=0
|
||||
|
||||
[Window][测试窗口1]
|
||||
@ -98,8 +98,8 @@ Size=600,500
|
||||
Collapsed=0
|
||||
|
||||
[Window][资源管理器]
|
||||
Pos=341,705
|
||||
Size=1351,399
|
||||
Pos=341,633
|
||||
Size=1231,399
|
||||
Collapsed=0
|
||||
DockId=0x00000006,0
|
||||
|
||||
@ -226,9 +226,11 @@ Size=460,260
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=2040,1084 Split=X
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1920,1012 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=2212,1012 Split=X
|
||||
DockNode ID=0x00000007 Parent=0x00000001 SizeRef=339,1084 Selected=0xE0015051
|
||||
DockNode ID=0x00000007 Parent=0x00000001 SizeRef=339,1084 Split=Y Selected=0xE0015051
|
||||
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=339,611 Selected=0xE0015051
|
||||
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=339,399 Selected=0x5428E753
|
||||
DockNode ID=0x00000008 Parent=0x00000001 SizeRef=1871,1084 Split=Y
|
||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=2048,683 Split=Y
|
||||
DockNode ID=0x0000000D Parent=0x00000005 SizeRef=1318,383 HiddenTabBar=1 Selected=0x43A39006
|
||||
|
||||
@ -686,6 +686,101 @@ class AnimationTools:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _find_animation_driver_node(self, node):
|
||||
"""Find the concrete node that should drive animation playback."""
|
||||
try:
|
||||
if not node or node.isEmpty():
|
||||
return None
|
||||
|
||||
cached_driver = node.getPythonTag("animation_driver_node")
|
||||
if cached_driver and (not cached_driver.isEmpty()):
|
||||
return cached_driver
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
candidates = []
|
||||
|
||||
def add_candidate(candidate):
|
||||
try:
|
||||
if not candidate or candidate.isEmpty():
|
||||
return
|
||||
for existing in candidates:
|
||||
if existing == candidate:
|
||||
return
|
||||
candidates.append(candidate)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
add_candidate(node)
|
||||
|
||||
current = node
|
||||
for _ in range(32):
|
||||
if not current or current.isEmpty() or self._is_scene_root_node(current):
|
||||
break
|
||||
add_candidate(current)
|
||||
try:
|
||||
for child in current.getChildren():
|
||||
add_candidate(child)
|
||||
except Exception:
|
||||
pass
|
||||
parent = current.getParent()
|
||||
if not parent or parent.isEmpty() or parent == current:
|
||||
break
|
||||
current = parent
|
||||
|
||||
best = None
|
||||
best_score = -1
|
||||
for candidate in candidates:
|
||||
try:
|
||||
has_anim = self._node_has_animation_nodes(candidate)
|
||||
if not has_anim:
|
||||
continue
|
||||
has_geom = self._node_has_geom(candidate)
|
||||
character_count = candidate.findAllMatches("**/+Character").getNumPaths()
|
||||
bundle_count = candidate.findAllMatches("**/+AnimBundleNode").getNumPaths()
|
||||
is_character = character_count > 0
|
||||
is_bundle = bundle_count > 0
|
||||
is_same_node = candidate == node
|
||||
is_descendant = False
|
||||
is_ancestor = False
|
||||
try:
|
||||
if candidate != node:
|
||||
is_descendant = node.isAncestorOf(candidate)
|
||||
is_ancestor = candidate.isAncestorOf(node)
|
||||
except Exception:
|
||||
is_descendant = False
|
||||
is_ancestor = False
|
||||
score = 0
|
||||
score += 140 if has_anim and has_geom else 0
|
||||
score += 90 if has_geom else 0
|
||||
score += 80 if is_character else 0
|
||||
score += 40 if is_bundle else 0
|
||||
score += 35 if is_descendant else 0
|
||||
score += 15 if is_same_node else 0
|
||||
score -= 20 if is_ancestor else 0
|
||||
score += character_count * 5
|
||||
score += bundle_count * 3
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best = candidate
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if best:
|
||||
try:
|
||||
node.setPythonTag("animation_driver_node", best)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
best.setPythonTag("animation_driver_node", best)
|
||||
except Exception:
|
||||
pass
|
||||
return best
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _recover_model_path_from_tags(self, node):
|
||||
"""从常见标签恢复模型路径,尽量避免退化到纯内存 autoBind。"""
|
||||
try:
|
||||
@ -905,6 +1000,16 @@ class AnimationTools:
|
||||
|
||||
def _resolve_animation_owner_model(self, node):
|
||||
"""向上查找动画所属的模型根节点,避免选中子节点时绑定失败。"""
|
||||
try:
|
||||
if node and (not node.isEmpty()):
|
||||
# If the user explicitly selected the animated skeleton node,
|
||||
# respect that directly. More aggressive driver remapping made
|
||||
# playback regress to "cannot play at all".
|
||||
if self._node_has_animation_nodes(node):
|
||||
return node
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ssbo_owner = self._resolve_ssbo_source_owner(node)
|
||||
if ssbo_owner and not ssbo_owner.isEmpty():
|
||||
try:
|
||||
@ -1431,6 +1536,13 @@ class AnimationTools:
|
||||
except Exception:
|
||||
has_animation_nodes = False
|
||||
|
||||
filepath = owner_model.getTag("model_path") if owner_model.hasTag("model_path") else ""
|
||||
if not filepath and owner_model.hasTag("original_path"):
|
||||
filepath = owner_model.getTag("original_path")
|
||||
|
||||
lower_filepath = str(filepath).lower() if filepath else ""
|
||||
is_gltf_family = lower_filepath.endswith(".glb") or lower_filepath.endswith(".gltf")
|
||||
|
||||
def _try_memory_fallback():
|
||||
def _collect_autobind_source_candidates():
|
||||
candidates = []
|
||||
@ -1554,6 +1666,34 @@ class AnimationTools:
|
||||
return mem_proxy
|
||||
return None
|
||||
|
||||
if is_gltf_family and has_animation_nodes:
|
||||
# For imported GLTF/GLB, prefer binding controls directly onto the
|
||||
# visible scene model. A separate runtime Armature proxy can report
|
||||
# playing=True while not driving the visible mesh the user selected.
|
||||
direct_proxy = _try_create_autobind_proxy(
|
||||
owner_model,
|
||||
f"当前模型({owner_model.getName()})",
|
||||
owns_node=False,
|
||||
)
|
||||
if direct_proxy:
|
||||
self._mark_runtime_animation_node(direct_proxy, owner_model)
|
||||
self._actor_cache[owner_model] = direct_proxy
|
||||
owner_model.setTag("has_animations", "true")
|
||||
return direct_proxy
|
||||
|
||||
if filepath:
|
||||
actor = _try_create_actor_via_gltf_path(filepath)
|
||||
if actor:
|
||||
owner_model.setTag("model_path", filepath)
|
||||
owner_model.setTag("has_animations", "true")
|
||||
self._mark_runtime_animation_node(actor, owner_model)
|
||||
self._actor_cache[owner_model] = actor
|
||||
return actor
|
||||
|
||||
actor = _try_memory_fallback()
|
||||
if actor:
|
||||
return actor
|
||||
|
||||
# 始终优先尝试从文件路径加载,因为底层 gltf 插件只有在加载文件时才能抽取完整的动画名称。
|
||||
self._anim_log(f"[Actor加载调试] 传入的 origin_model: {origin_model.getName() if origin_model else 'None'}")
|
||||
self._anim_log(f"[Actor加载调试] origin_model tags: {origin_model.getTags() if origin_model else 'None'}")
|
||||
@ -1564,10 +1704,6 @@ class AnimationTools:
|
||||
except Exception as e:
|
||||
self._anim_log(f"[Actor加载调试] 获取 tags 异常: {e}")
|
||||
|
||||
filepath = owner_model.getTag("model_path") if owner_model.hasTag("model_path") else ""
|
||||
if not filepath and owner_model.hasTag("original_path"):
|
||||
filepath = owner_model.getTag("original_path")
|
||||
|
||||
self._anim_log(f"[Actor加载调试] 获取到的 filepath: '{filepath}'")
|
||||
if not filepath:
|
||||
self._anim_log(f"[Actor加载调试] filepath为空,触发 _try_memory_fallback()")
|
||||
@ -1622,14 +1758,24 @@ class AnimationTools:
|
||||
|
||||
for p in load_paths:
|
||||
self._anim_log(f"[Actor加载验证] 正在尝试通过路径读取骨骼和动画文件: {p}")
|
||||
actor = _try_create_actor_from_source(p, f"文件路径({p})")
|
||||
if actor:
|
||||
owner_model.setTag("model_path", p)
|
||||
owner_model.setTag("has_animations", "true")
|
||||
self._actor_cache[owner_model] = actor
|
||||
return actor
|
||||
lower_p = str(p).lower()
|
||||
|
||||
if lower_p.endswith(".glb") or lower_p.endswith(".gltf"):
|
||||
actor = _try_create_actor_via_gltf_path(p)
|
||||
if actor:
|
||||
owner_model.setTag("model_path", p)
|
||||
owner_model.setTag("has_animations", "true")
|
||||
self._mark_runtime_animation_node(actor, owner_model)
|
||||
self._actor_cache[owner_model] = actor
|
||||
return actor
|
||||
else:
|
||||
actor = _try_create_actor_from_source(p, f"文件路径({p})")
|
||||
if actor:
|
||||
owner_model.setTag("model_path", p)
|
||||
owner_model.setTag("has_animations", "true")
|
||||
self._actor_cache[owner_model] = actor
|
||||
return actor
|
||||
|
||||
# 标准 Actor 路径失败时,针对 GLTF/GLB 走插件加载兜底
|
||||
actor = _try_create_actor_via_gltf_path(p)
|
||||
if actor:
|
||||
owner_model.setTag("model_path", p)
|
||||
@ -1638,7 +1784,9 @@ class AnimationTools:
|
||||
self._actor_cache[owner_model] = actor
|
||||
return actor
|
||||
|
||||
# 路径 Actor 失败后,再尝试把文件作为普通模型加载并 autoBind
|
||||
if is_gltf_family:
|
||||
continue
|
||||
|
||||
try:
|
||||
model_source = p
|
||||
if isinstance(p, (str, os.PathLike)):
|
||||
@ -1831,6 +1979,8 @@ class AnimationTools:
|
||||
pass
|
||||
|
||||
valid_names = []
|
||||
best_name = None
|
||||
best_frames = -1
|
||||
for anim_name in anim_names:
|
||||
try:
|
||||
control = actor.getAnimControl(anim_name)
|
||||
@ -1838,12 +1988,17 @@ class AnimationTools:
|
||||
control = None
|
||||
if not control:
|
||||
continue
|
||||
frame_count = -1
|
||||
try:
|
||||
if control.getNumFrames() <= 1:
|
||||
frame_count = control.getNumFrames()
|
||||
if frame_count <= 1:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
valid_names.append(anim_name)
|
||||
if frame_count > best_frames:
|
||||
best_frames = frame_count
|
||||
best_name = anim_name
|
||||
|
||||
if not valid_names:
|
||||
valid_names = list(anim_names)
|
||||
@ -1855,7 +2010,7 @@ class AnimationTools:
|
||||
break
|
||||
|
||||
if current_anim not in valid_names:
|
||||
current_anim = valid_names[0]
|
||||
current_anim = best_name or valid_names[0]
|
||||
|
||||
try:
|
||||
origin_model.setPythonTag("selected_animation", current_anim)
|
||||
@ -2078,6 +2233,18 @@ class AnimationTools:
|
||||
|
||||
is_scene_bound_proxy = self._sync_actor_transform_for_playback(owner_model, actor)
|
||||
|
||||
debug_actor_name = getattr(actor, "getName", None)
|
||||
try:
|
||||
debug_actor_name = debug_actor_name() if callable(debug_actor_name) else getattr(getattr(actor, "_node", None), "getName", lambda: "Unknown")()
|
||||
except Exception:
|
||||
debug_actor_name = "Unknown"
|
||||
|
||||
debug_frames = -1
|
||||
try:
|
||||
debug_frames = control.getNumFrames()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self._can_swap_actor_owner_visibility(owner_model, actor):
|
||||
self._ensure_owner_hidden_lock_task(owner_model, enabled=True)
|
||||
self._apply_actor_owner_visibility(owner_model, actor, prefer_actor_visible=True)
|
||||
@ -2093,6 +2260,11 @@ class AnimationTools:
|
||||
except Exception:
|
||||
pass
|
||||
actor.play(current_anim)
|
||||
try:
|
||||
is_playing = control.isPlaying()
|
||||
except Exception:
|
||||
is_playing = "unknown"
|
||||
print(f"[动画调试] owner={owner_model.getName()} actor={debug_actor_name} anim={current_anim} frames={debug_frames} playing={is_playing} scene_proxy={is_scene_bound_proxy}")
|
||||
print(f"『动画播放』:{current_anim}")
|
||||
|
||||
def _pauseAnimation(self, origin_model):
|
||||
|
||||
@ -1541,6 +1541,8 @@ class AppActions:
|
||||
Legacy mode: load via SceneManager.
|
||||
"""
|
||||
animated_model = bool(file_path and self._model_file_has_animation(file_path))
|
||||
lower_path = str(file_path).lower() if file_path else ""
|
||||
is_gltf_family = lower_path.endswith((".glb", ".gltf"))
|
||||
if animated_model:
|
||||
prefer_scene_manager = True
|
||||
ssbo_editor = getattr(self, 'ssbo_editor', None)
|
||||
@ -1550,6 +1552,11 @@ class AppActions:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if is_gltf_family:
|
||||
fallback_model = self._import_model_via_gltf_fallback(file_path)
|
||||
if fallback_model:
|
||||
return fallback_model
|
||||
|
||||
if self.use_ssbo_mouse_picking and getattr(self, 'ssbo_editor', None):
|
||||
if animated_model:
|
||||
print(f"[AnimationImport] 检测到动画模型,跳过SSBO导入: {file_path}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user