diff --git a/Assets/Models/box1.glb b/Assets/Models/box1.glb new file mode 100644 index 00000000..06712bc4 Binary files /dev/null and b/Assets/Models/box1.glb differ diff --git a/Assets/Models/box1.glb.meta b/Assets/Models/box1.glb.meta new file mode 100644 index 00000000..7d65be38 --- /dev/null +++ b/Assets/Models/box1.glb.meta @@ -0,0 +1,8 @@ +{ + "guid": "2c994aea36c94385a8f9a5274c406a67", + "asset_type": "model", + "source_hash": "fc694905c47a9b0005d77b701cc41852b56ef08c7406829a306e98f3ce158a64", + "importer": "model_importer", + "import_settings": {}, + "dependency_guids": [] +} \ No newline at end of file diff --git a/core/selection.py b/core/selection.py index 03203293..a7aa13f0 100644 --- a/core/selection.py +++ b/core/selection.py @@ -367,6 +367,20 @@ class SelectionSystem: if not self.enable_unity_outline: self.outline_manager.clear() return + + if nodePath and not nodePath.isEmpty(): + has_geom = False + try: + has_geom = nodePath.find_all_matches("**/+GeomNode").get_num_paths() > 0 + except Exception: + try: + has_geom = nodePath.findAllMatches("**/+GeomNode").getNumPaths() > 0 + except Exception: + has_geom = False + if not has_geom: + self.outline_manager.clear() + return + if nodePath and not nodePath.isEmpty(): self.outline_manager.set_targets([nodePath]) else: @@ -2742,4 +2756,3 @@ class SelectionSystem: print("已清除选择") except Exception as e: print(f"清除选择失败: {e}") - diff --git a/packaging/debian/README.md b/packaging/debian/README.md new file mode 100644 index 00000000..e61bb565 --- /dev/null +++ b/packaging/debian/README.md @@ -0,0 +1,75 @@ +# EG `.deb` 打包 + +这套脚本的目标不是重新编译项目,而是把已经生成好的 PyInstaller 发行目录再封装成 Debian 安装包。 + +默认输入目录是: + +```text +dist/EGEditor +``` + +默认输出目录是: + +```text +dist-deb +``` + +## 最常用流程 + +1. 先生成 PyInstaller 发行目录: + +```bash +python -m PyInstaller --clean packaging/eg_editor.spec +``` + +2. 再生成 `.deb`: + +```bash +bash packaging/debian/build_deb.sh 0.1.0 +``` + +生成结果类似: + +```text +dist-deb/eg-editor_0.1.0_amd64.deb +``` + +## 安装与卸载 + +安装: + +```bash +sudo apt install ./dist-deb/eg-editor_0.1.0_amd64.deb +``` + +卸载: + +```bash +sudo apt remove eg-editor +``` + +## 安装后路径 + +- 程序主体:`/opt/eg-editor` +- 命令行启动:`/usr/bin/eg-editor` +- 桌面启动器:`/usr/share/applications/eg-editor.desktop` +- 图标:`/usr/share/pixmaps/eg-editor.png` + +## 可选环境变量 + +- `APP_DIR`:指定要封装的 PyInstaller 目录 +- `PACKAGE_NAME`:Debian 包名,默认 `eg-editor` +- `DISPLAY_NAME`:桌面显示名称 +- `ARCH`:包架构,默认自动读取 `dpkg-architecture` +- `MAINTAINER`:维护者字段 +- `OUTPUT_DIR`:`.deb` 输出目录 +- `ICON_SOURCE`:图标文件路径 + +示例: + +```bash +APP_DIR=/path/to/dist/EGEditor \ +DISPLAY_NAME="EG Editor" \ +MAINTAINER="hello " \ +bash packaging/debian/build_deb.sh 0.1.0 +``` diff --git a/packaging/debian/build_deb.sh b/packaging/debian/build_deb.sh new file mode 100755 index 00000000..bd5f9d14 --- /dev/null +++ b/packaging/debian/build_deb.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +APP_DIR="${APP_DIR:-$ROOT_DIR/dist/EGEditor}" +PACKAGE_NAME="${PACKAGE_NAME:-eg-editor}" +DISPLAY_NAME="${DISPLAY_NAME:-EG Editor}" +VERSION="${1:-${VERSION:-0.1.0}}" +ARCH="${ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH 2>/dev/null || echo amd64)}" +MAINTAINER="${MAINTAINER:-EG Team }" +DESCRIPTION="${DESCRIPTION:-EG 3D editor and engine packaged from the PyInstaller distribution directory.}" + +INSTALL_DIR="/opt/${PACKAGE_NAME}" +STAGE_BASE="$ROOT_DIR/build/deb/${PACKAGE_NAME}_${VERSION}_${ARCH}" +PKG_ROOT="$STAGE_BASE/${PACKAGE_NAME}" +OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/dist-deb}" +OUTPUT_FILE="$OUTPUT_DIR/${PACKAGE_NAME}_${VERSION}_${ARCH}.deb" +ICON_SOURCE="${ICON_SOURCE:-$ROOT_DIR/icons/logo.png}" + +if [[ ! -x "$APP_DIR/EGEditor" ]]; then + echo "error: executable not found: $APP_DIR/EGEditor" >&2 + echo "hint: build the PyInstaller bundle first, or set APP_DIR=/path/to/EGEditor" >&2 + exit 1 +fi + +if [[ ! -f "$ICON_SOURCE" ]]; then + echo "error: icon not found: $ICON_SOURCE" >&2 + exit 1 +fi + +rm -rf "$STAGE_BASE" +mkdir -p \ + "$PKG_ROOT/DEBIAN" \ + "$PKG_ROOT$INSTALL_DIR" \ + "$PKG_ROOT/usr/bin" \ + "$PKG_ROOT/usr/share/applications" \ + "$PKG_ROOT/usr/share/pixmaps" \ + "$OUTPUT_DIR" + +cp -a "$APP_DIR/." "$PKG_ROOT$INSTALL_DIR/" +cp "$ICON_SOURCE" "$PKG_ROOT/usr/share/pixmaps/${PACKAGE_NAME}.png" + +cat > "$PKG_ROOT/usr/bin/${PACKAGE_NAME}" < "$PKG_ROOT/usr/share/applications/${PACKAGE_NAME}.desktop" < "$PKG_ROOT/DEBIAN/control" <= 2.35), libgl1 +Description: ${DISPLAY_NAME} + ${DESCRIPTION} +EOF +chmod 0644 "$PKG_ROOT/DEBIAN/control" + +dpkg-deb --build --root-owner-group "$PKG_ROOT" "$OUTPUT_FILE" + +echo "built: $OUTPUT_FILE" diff --git a/project/project_manager.py b/project/project_manager.py index 13d12abc..0334fbd1 100644 --- a/project/project_manager.py +++ b/project/project_manager.py @@ -1880,7 +1880,15 @@ class ProjectManager: except Exception: imported_np = None if imported_np and not imported_np.isEmpty(): - self._apply_scene_description_state_to_node(imported_np, node, project_path) + # Animated/Actor imports still need the saved child-node + # overrides (transform/material/visibility), not just + # the model root state. + self._apply_scene_description_state_to_subtree( + imported_np, + node, + project_path, + node_lookup, + ) loaded_any = True continue diff --git a/ui/panels/editor_panels_right.py b/ui/panels/editor_panels_right.py index a2e1db9a..5e1f08e0 100644 --- a/ui/panels/editor_panels_right.py +++ b/ui/panels/editor_panels_right.py @@ -571,7 +571,14 @@ class EditorPanelsRightMixin( """绘制光源属性""" imgui.text("光源属性") - color = self.app._get_light_color(node) + color = self.app._get_light_color(node) or (1.0, 1.0, 1.0, 1.0) + if len(color) < 4: + color = ( + float(color[0]) if len(color) > 0 else 1.0, + float(color[1]) if len(color) > 1 else 1.0, + float(color[2]) if len(color) > 2 else 1.0, + 1.0, + ) changed, new_color = imgui.color_edit3( "颜色##light_color", (color[0], color[1], color[2]), @@ -917,4 +924,3 @@ class EditorPanelsRightMixin( if self.app.style_manager.draw_toolbar_button("删除", size=(64, 28), tooltip="删除当前对象"): if hasattr(self, 'selection') and self.selection: self.selection.deleteSelectedNode() - diff --git a/ui/panels/panel_delegates.py b/ui/panels/panel_delegates.py index 94431cd6..8e6d7461 100644 --- a/ui/panels/panel_delegates.py +++ b/ui/panels/panel_delegates.py @@ -153,14 +153,40 @@ class PanelDelegates: # 检查是否为GUI元素 if hasattr(node, 'getPythonTag') and node.getPythonTag('gui_element'): return "GUI元素" - - # 检查是否为光源 + + try: + if node.hasTag("tree_item_type"): + tree_item_type = str(node.getTag("tree_item_type") or "").strip().upper() + if tree_item_type == "CAMERA_NODE": + return "相机" + if tree_item_type in {"LIGHT_NODE", "SPOT_LIGHT_NODE", "POINT_LIGHT_NODE"}: + return "光源" + if node.hasTag("light_type"): + return "光源" + except Exception: + pass + + try: + panda_node = node.node() if hasattr(node, "node") else None + if panda_node is not None and hasattr(panda_node, "getClassType"): + class_name = panda_node.getClassType().getName() or "" + class_name_lower = class_name.lower() + if "camera" in class_name_lower: + return "相机" + if "light" in class_name_lower: + return "光源" + except Exception: + pass + node_name = node.getName() or "" - if "light" in node_name.lower() or "Light" in node_name: + node_name_lower = node_name.lower() + + # 检查是否为光源 + if "light" in node_name_lower: return "光源" - + # 检查是否为相机 - if "camera" in node_name.lower() or "Camera" in node_name: + if "camera" in node_name_lower: return "相机" # 检查是否为模型 diff --git a/ui/panels/property_helpers.py b/ui/panels/property_helpers.py index cb222d76..853b4746 100644 --- a/ui/panels/property_helpers.py +++ b/ui/panels/property_helpers.py @@ -769,6 +769,22 @@ class PropertyHelpers: float(color[3]) if len(color) > 3 else 1.0, ) + panda_node = node.node() if hasattr(node, "node") else None + if panda_node is not None: + try: + get_color = getattr(panda_node, "getColor", None) or getattr(panda_node, "get_color", None) + if callable(get_color): + light_color = get_color() + if light_color is not None and len(light_color) >= 3: + return ( + float(light_color[0]), + float(light_color[1]), + float(light_color[2]), + float(light_color[3]) if len(light_color) > 3 else 1.0, + ) + except Exception: + pass + rp_light = node.getPythonTag("rp_light_object") if rp_light is not None: try: @@ -784,6 +800,7 @@ class PropertyHelpers: pass except Exception: return (1.0, 1.0, 1.0, 1.0) + return (1.0, 1.0, 1.0, 1.0) def _apply_light_color(self, node, color): """Apply light color to both editor node and RP light object.""" @@ -799,6 +816,15 @@ class PropertyHelpers: except Exception: pass + try: + panda_node = node.node() if hasattr(node, "node") else None + if panda_node is not None: + set_color = getattr(panda_node, "setColor", None) or getattr(panda_node, "set_color", None) + if callable(set_color): + set_color(*rgba) + except Exception: + pass + try: rp_light = node.getPythonTag("rp_light_object") if rp_light is not None: