Fix SSBO root selection behavior and optimize group sync FPS

This commit is contained in:
Your Name 2026-02-27 16:02:40 +08:00
parent 756db5b010
commit 2183d3fc3e
2 changed files with 82 additions and 1 deletions

View File

@ -78,6 +78,11 @@ class SSBOEditor:
self.pick_mask = BitMask32.bit(29)
self.pick_buffer = None
self._empty_pick_scene = NodePath("ssbo_pick_empty")
# Avoid heavy per-frame sync for huge group selections.
self._pick_sync_bg_limit = 256
self._last_group_sync_mat = None
self._last_single_sync_gid = None
self._last_single_sync_mat = None
# Initialize ImGui Backend if not already present
if not hasattr(self.base, 'imgui_backend'):
@ -389,6 +394,11 @@ class SSBOEditor:
self._sync_pick_transforms()
return task.cont
def _reset_pick_sync_cache(self):
self._last_group_sync_mat = None
self._last_single_sync_gid = None
self._last_single_sync_mat = None
def _matrices_close(self, a, b, eps=1e-5):
"""Small helper for robust matrix change detection."""
for r in range(4):
@ -424,6 +434,45 @@ class SSBOEditor:
if not self.controller:
return
self._sync_pick_root_transform()
if not self.selected_ids:
return
# Group selection can contain thousands of objects.
# Only resync when proxy transform has changed.
proxy = getattr(self, "_group_proxy", None)
if proxy and not proxy.is_empty() and len(self.selected_ids) > 1:
proxy_world = proxy.get_mat(self.base.render)
if self._last_group_sync_mat and self._matrices_close(proxy_world, self._last_group_sync_mat):
return
self._last_group_sync_mat = LMatrix4f(proxy_world)
else:
self._last_group_sync_mat = None
if len(self.selected_ids) == 1:
gid = self.selected_ids[0]
obj_np = self.controller.id_to_object_np.get(gid)
pick_np = self.controller.id_to_pick_np.get(gid)
if not (obj_np and pick_np and not obj_np.is_empty() and not pick_np.is_empty()):
return
obj_world_mat = obj_np.get_mat(self.base.render)
if (
self._last_single_sync_gid == gid and
self._last_single_sync_mat and
self._matrices_close(obj_world_mat, self._last_single_sync_mat)
):
return
self._last_single_sync_gid = gid
self._last_single_sync_mat = LMatrix4f(obj_world_mat)
pick_world_mat = pick_np.get_mat(self.base.render)
if not self._matrices_close(obj_world_mat, pick_world_mat):
pick_np.set_mat(self.base.render, obj_world_mat)
chunk_id = self.controller.id_to_chunk.get(gid)
if chunk_id is not None and chunk_id in self.controller.chunks:
self.controller.chunks[chunk_id]["dirty"] = True
return
self._last_single_sync_gid = None
self._last_single_sync_mat = None
for gid in self.selected_ids:
obj_np = self.controller.id_to_object_np.get(gid)
pick_np = self.controller.id_to_pick_np.get(gid)
@ -440,6 +489,7 @@ class SSBOEditor:
def clear_selection(self):
self._stop_pick_sync_task()
self._reset_pick_sync_cache()
self._cleanup_group_proxy()
self.selected_name = None
self.selected_ids = []
@ -488,6 +538,7 @@ class SSBOEditor:
def select_node(self, key):
# Clean up previous group proxy before changing selection
self._cleanup_group_proxy()
self._reset_pick_sync_cache()
self.selected_name = key
self.selected_ids = self.controller.name_to_ids.get(key, [])
@ -532,7 +583,12 @@ class SSBOEditor:
obj_np.wrt_reparent_to(proxy)
self._transform_gizmo.attach(proxy)
self._start_pick_sync_task()
# For huge groups, avoid per-frame full sync; we still sync on demand
# right before picking via pick_object().
if len(valid) <= self._pick_sync_bg_limit:
self._start_pick_sync_task()
else:
self._stop_pick_sync_task()
def _rebuild_filtered_tree_rows(self):
"""

View File

@ -468,8 +468,33 @@ class EditorPanels:
# 处理节点选择
if imgui.is_item_clicked():
# In SSBO mode, clicking model root should finally bind gizmo to
# SSBO group proxy (not legacy model root). So run legacy
# selection first, then force SSBO root selection at the end.
force_ssbo_root_key = None
ssbo_editor_ref = None
try:
ssbo_editor = getattr(self.app, "ssbo_editor", None)
controller = getattr(ssbo_editor, "controller", None) if ssbo_editor else None
ssbo_model = getattr(ssbo_editor, "model", None) if ssbo_editor else None
root_key = getattr(controller, "tree_root_key", None) if controller else None
if (
ssbo_editor and controller and ssbo_model and node == ssbo_model
and root_key and root_key in controller.tree_nodes
):
force_ssbo_root_key = root_key
ssbo_editor_ref = ssbo_editor
except Exception:
pass
if hasattr(self.app, 'selection') and self.app.selection:
self.app.selection.updateSelection(node)
if force_ssbo_root_key and ssbo_editor_ref:
try:
ssbo_editor_ref.select_node(force_ssbo_root_key)
except Exception:
pass
# Clear LUI selection when a scene node is selected
if hasattr(self.app, 'lui_manager'):
self.app.lui_manager.selected_index = -1