Fix light registration handling

This commit is contained in:
ayuan9957 2026-03-13 14:15:38 +08:00
parent 6e76b54ddb
commit 8a80af9825
4 changed files with 208 additions and 73 deletions

View File

@ -8,10 +8,109 @@ def _is_valid_node(node) -> bool:
return bool(node) and hasattr(node, "isEmpty") and (not node.isEmpty())
def _is_light_node(node: NodePath) -> bool:
return bool(node) and hasattr(node, "hasTag") and node.hasTag("light_type")
def _is_terrain_node(node: NodePath) -> bool:
return bool(node) and hasattr(node, "hasTag") and node.hasTag("tree_item_type") and node.getTag("tree_item_type") == "TERRAIN_NODE"
def _set_light_registration(world, node: NodePath, registered: bool):
if not world or not _is_valid_node(node) or not _is_light_node(node):
return
scene_manager = getattr(world, "scene_manager", None)
light_type = node.getTag("light_type")
light_lists = []
if scene_manager:
if light_type == "spot_light" and hasattr(scene_manager, "Spotlight"):
light_lists.append(scene_manager.Spotlight)
elif light_type == "point_light" and hasattr(scene_manager, "Pointlight"):
light_lists.append(scene_manager.Pointlight)
rp_light = node.getPythonTag("rp_light_object") if hasattr(node, "hasPythonTag") and node.hasPythonTag("rp_light_object") else None
current_registered = bool(node.getPythonTag("engine_light_registered")) if hasattr(node, "hasPythonTag") and node.hasPythonTag("engine_light_registered") else False
if registered:
for light_list in light_lists:
if node not in light_list:
light_list.append(node)
if not current_registered:
try:
if rp_light is not None and getattr(world, "render_pipeline", None):
world.render_pipeline.add_light(rp_light)
elif hasattr(world, "render") and world.render:
world.render.setLight(node)
except Exception:
pass
try:
node.setPythonTag("engine_light_registered", True)
except Exception:
pass
return
for light_list in light_lists:
try:
while node in light_list:
light_list.remove(node)
except Exception:
pass
if current_registered:
try:
if rp_light is not None and getattr(world, "render_pipeline", None):
world.render_pipeline.remove_light(rp_light)
elif hasattr(world, "render") and world.render:
world.render.clearLight(node)
except Exception:
pass
try:
node.setPythonTag("engine_light_registered", False)
except Exception:
pass
def _set_terrain_registration(world, node: NodePath, registered: bool):
if not world or not _is_valid_node(node) or not _is_terrain_node(node):
return
terrain_manager = getattr(world, "terrain_manager", None)
if not terrain_manager or not hasattr(terrain_manager, "terrains"):
return
terrain_info = None
if hasattr(node, "hasPythonTag") and node.hasPythonTag("terrain_info"):
terrain_info = node.getPythonTag("terrain_info")
else:
for info in getattr(terrain_manager, "terrains", []):
if info.get("node") == node:
terrain_info = info
break
if terrain_info is not None:
try:
node.setPythonTag("terrain_info", terrain_info)
except Exception:
pass
if registered:
if terrain_info is not None:
terrain_info["node"] = node
if all(info.get("node") != node for info in terrain_manager.terrains):
terrain_manager.terrains.append(terrain_info)
return
try:
terrain_manager.terrains = [info for info in terrain_manager.terrains if info.get("node") != node]
except Exception:
pass
def _register_scene_node(world, node: NodePath):
if not world or not _is_valid_node(node):
return
scene_manager = getattr(world, "scene_manager", None)
_set_light_registration(world, node, True)
_set_terrain_registration(world, node, True)
if scene_manager and hasattr(scene_manager, "models") and node not in scene_manager.models:
scene_manager.models.append(node)
try:
@ -25,6 +124,8 @@ def _unregister_scene_node(world, node: NodePath):
if not world or not node:
return
scene_manager = getattr(world, "scene_manager", None)
_set_light_registration(world, node, False)
_set_terrain_registration(world, node, False)
if scene_manager and hasattr(scene_manager, "models"):
try:
while node in scene_manager.models:
@ -331,11 +432,11 @@ class DeleteNodeCommand(Command):
if node.hasTag("tileset_url"):
self.extra_data["tileset_url"] = node.getTag("tileset_url")
def execute(self):
"""
执行删除操作
"""
# 从world的相应列表中移除节点引用
def execute(self):
"""
执行删除操作
"""
# 从world的相应列表中移除节点引用
if self.world and hasattr(self.world, 'scene_manager'):
scene_manager = self.world.scene_manager
if self.node_type == "LIGHT_NODE":
@ -353,19 +454,21 @@ class DeleteNodeCommand(Command):
if self.node_type.startswith("GUI_") and hasattr(self.world,
'gui_elements') and self.node in self.world.gui_elements:
self.world.gui_elements.remove(self.node)
elif self.node_type == "CESIUM_TILESET_NODE":
# 从tilesets列表中移除
if hasattr(scene_manager, 'tilesets'):
tilesets_to_remove = []
for i, tileset_info in enumerate(scene_manager.tilesets):
elif self.node_type == "CESIUM_TILESET_NODE":
# 从tilesets列表中移除
if hasattr(scene_manager, 'tilesets'):
tilesets_to_remove = []
for i, tileset_info in enumerate(scene_manager.tilesets):
if tileset_info.get('node') == self.node:
tilesets_to_remove.append(i)
for i in reversed(tilesets_to_remove):
del scene_manager.tilesets[i]
# 从场景图中移除节点,使用 detachNode 而不是 removeNode 以便可以撤销
if self.node and not self.node.isEmpty():
self.node.detachNode()
for i in reversed(tilesets_to_remove):
del scene_manager.tilesets[i]
_unregister_scene_node(self.world, self.node)
# 从场景图中移除节点,使用 detachNode 而不是 removeNode 以便可以撤销
if self.node and not self.node.isEmpty():
self.node.detachNode()
def undo(self):
"""
@ -392,14 +495,16 @@ class DeleteNodeCommand(Command):
if self.node_type.startswith("GUI_") and hasattr(self.world, 'gui_elements') and self.node not in self.world.gui_elements:
self.world.gui_elements.append(self.node)
elif self.node_type == "CESIUM_TILESET_NODE":
# 简单恢复到 tilesets
if hasattr(scene_manager, 'tilesets'):
scene_manager.tilesets.append({'node': self.node, 'url': self.extra_data.get('tileset_url', '')})
print(f"✅ 成功撤销删除操作,节点 {self.node_name} 已恢复")
else:
print("❌ 无法撤销删除操作,节点引用已丢失")
elif self.node_type == "CESIUM_TILESET_NODE":
# 简单恢复到 tilesets
if hasattr(scene_manager, 'tilesets'):
scene_manager.tilesets.append({'node': self.node, 'url': self.extra_data.get('tileset_url', '')})
_register_scene_node(self.world, self.node)
print(f"✅ 成功撤销删除操作,节点 {self.node_name} 已恢复")
else:
print("❌ 无法撤销删除操作,节点引用已丢失")
except Exception as e:
print(f"❌ 撤销删除操作时出错: {e}")

View File

@ -129,19 +129,20 @@ class TerrainManager:
terrain_node.setPythonTag("selectable", True)
# 保存地形信息(包括高度图的副本)
terrain_info = {
'terrain': terrain,
'node': terrain_node,
'heightmap': heightmap_path,
'heightfield': height_image, # 保存高度图副本
terrain_info = {
'terrain': terrain,
'node': terrain_node,
'heightmap': heightmap_path,
'heightfield': height_image, # 保存高度图副本
'scale': scale,
'name': node_name
}
self.terrains.append(terrain_info)
parent_name = parent_item.text(0) if parent_item else "root"
print(f"✅ 为 {parent_name} 创建高度图地形: {terrain_name}")
}
self.terrains.append(terrain_info)
terrain_node.setPythonTag("terrain_info", terrain_info)
parent_name = parent_item.text(0) if parent_item else "root"
print(f"✅ 为 {parent_name} 创建高度图地形: {terrain_name}")
# 在Qt树形控件中添加对应节点
qt_item = None
@ -278,19 +279,20 @@ class TerrainManager:
terrain_node.setPythonTag("selectable", True)
# 保存地形信息(包括高度图)
terrain_info = {
'terrain': terrain,
'node': terrain_node,
'heightmap': None,
'heightfield': height_image, # 保存高度图
terrain_info = {
'terrain': terrain,
'node': terrain_node,
'heightmap': None,
'heightfield': height_image, # 保存高度图
'scale': (size[0], size[1], 50),
'name': node_name
}
self.terrains.append(terrain_info)
parent_name = parent_item.text(0) if parent_item else "root"
print(f"✅ 为 {parent_name} 创建平面地形: {terrain_name}")
}
self.terrains.append(terrain_info)
terrain_node.setPythonTag("terrain_info", terrain_info)
parent_name = parent_item.text(0) if parent_item else "root"
print(f"✅ 为 {parent_name} 创建平面地形: {terrain_name}")
# 在Qt树形控件中添加对应节点
qt_item = None

View File

@ -326,6 +326,7 @@ class SceneManagerLightMixin:
spotlight_node.setTag("created_by_user", "1")
spotlight_node.setTag("element_type", "spotlight")
spotlight_node.setPythonTag("rp_light_object", spotlight)
spotlight_node.setPythonTag("engine_light_registered", True)
self.Spotlight.append(spotlight_node)
return spotlight_node
else:
@ -348,6 +349,7 @@ class SceneManagerLightMixin:
spotlight_node.setTag("tree_item_type", "LIGHT_NODE")
spotlight_node.setTag("created_by_user", "1")
spotlight_node.setTag("element_type", "spotlight")
spotlight_node.setPythonTag("engine_light_registered", True)
# 设置聚光灯方向(向下照射)
spotlight_node.lookAt(pos[0], pos[1], pos[2] - 5) # 向下看5个单位
@ -414,6 +416,7 @@ class SceneManagerLightMixin:
pointlight_node.setTag("created_by_user", "1")
pointlight_node.setTag("element_type", "pointlight")
pointlight_node.setPythonTag("rp_light_object", pointlight)
pointlight_node.setPythonTag("engine_light_registered", True)
self.Pointlight.append(pointlight_node)
return pointlight_node
else:
@ -436,6 +439,7 @@ class SceneManagerLightMixin:
pointlight_node.setTag("tree_item_type", "LIGHT_NODE")
pointlight_node.setTag("created_by_user", "1")
pointlight_node.setTag("element_type", "pointlight")
pointlight_node.setPythonTag("engine_light_registered", True)
# 添加到光源列表
self.Pointlight.append(pointlight_node)

View File

@ -19,6 +19,21 @@ class DialogPanels:
else:
setattr(self.app, name, value)
def _execute_scene_create_command(self, creator):
"""Create a scene node through the shared undo/redo stack."""
command_manager = getattr(self, "command_manager", None)
if not command_manager:
return creator()
from core.Command_System import CreateNodeCommand
command = CreateNodeCommand(lambda _parent: creator(), getattr(self, "render", None), world=self)
command_manager.execute_command(command)
if not command.created_node:
command_manager.pop_last_command()
return None
return command.created_node
def _draw_new_project_dialog(self):
"""绘制新建项目对话框"""
@ -554,17 +569,20 @@ class DialogPanels:
if imgui.button("创建"):
try:
pos = tuple(self.dialog_params['spotlight_pos'])
result = self.createSpotLight(pos)
def create_spotlight():
result = self.createSpotLight(pos)
if result:
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['spotlight_color'])
light.setColor(color + (1.0,))
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['spotlight_intensity'])
return result
result = self._execute_scene_create_command(create_spotlight)
if result:
# 设置颜色和强度
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['spotlight_color'])
light.setColor(color + (1.0,)) # 添加alpha通道
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['spotlight_intensity'])
self.add_success_message("聚光灯创建成功")
self.show_spot_light_dialog = False
else:
@ -643,21 +661,23 @@ class DialogPanels:
if imgui.button("创建"):
try:
pos = tuple(self.dialog_params['pointlight_pos'])
result = self.createPointLight(pos)
def create_pointlight():
result = self.createPointLight(pos)
if result:
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['pointlight_color'])
light.setColor(color + (1.0,))
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['pointlight_intensity'])
if hasattr(light, 'setAttenuation'):
radius = self.dialog_params['pointlight_radius']
light.setAttenuation((1.0, 0.5 / radius, 0.5 / (radius * radius)))
return result
result = self._execute_scene_create_command(create_pointlight)
if result:
# 设置颜色和强度
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['pointlight_color'])
light.setColor(color + (1.0,)) # 添加alpha通道
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['pointlight_intensity'])
if hasattr(light, 'setAttenuation'):
# 设置衰减: (constant, linear, quadratic)
radius = self.dialog_params['pointlight_radius']
light.setAttenuation((1.0, 0.5/radius, 0.5/(radius*radius)))
self.add_success_message("点光源创建成功")
self.show_point_light_dialog = False
else:
@ -728,8 +748,10 @@ class DialogPanels:
# 转换为地形管理器期望的格式
size = (width, height)
result = self.createFlatTerrain(size, resolution)
result = self._execute_scene_create_command(
lambda: self.createFlatTerrain(size, resolution)
)
if result:
self.add_success_message("平面地形创建成功")
self.show_terrain_dialog = False
@ -1061,7 +1083,9 @@ class DialogPanels:
try:
# 使用默认缩放参数创建地形
scale = (1.0, 1.0, 10.0) # X, Y, Z缩放
result = self.createTerrainFromHeightMap(self.heightmap_file_path, scale)
result = self._execute_scene_create_command(
lambda: self.createTerrainFromHeightMap(self.heightmap_file_path, scale)
)
if result:
self.add_success_message("高度图地形创建成功")
self.show_heightmap_browser = False