diff --git a/.idea/EG.iml b/.idea/EG.iml index c608c0d0..04720f01 100644 --- a/.idea/EG.iml +++ b/.idea/EG.iml @@ -5,7 +5,7 @@ - + diff --git a/Start_Run.py b/Start_Run.py index 8e6a1214..81951385 100644 --- a/Start_Run.py +++ b/Start_Run.py @@ -5,6 +5,9 @@ import sys project_root = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, project_root) +# 设置工作目录为项目根目录 +os.chdir(project_root) + # 添加 RenderPipeline 到路径(注意路径名称应与实际目录一致) render_pipeline_path = os.path.join(project_root, "RenderPipeline") sys.path.insert(0, render_pipeline_path) diff --git a/core/vr/config/vr_settings.json b/core/vr/config/vr_settings.json index 902d31fb..556f68a2 100644 --- a/core/vr/config/vr_settings.json +++ b/core/vr/config/vr_settings.json @@ -11,5 +11,8 @@ "enable_ssr": false, "shadow_quality": "medium", "ao_quality": "low" - } + }, + "anti_aliasing": "4x", + "refresh_rate": "90Hz", + "async_reprojection": true } \ No newline at end of file diff --git a/tools/open_source_rate.py b/tools/open_source_rate.py new file mode 100644 index 00000000..cf389707 --- /dev/null +++ b/tools/open_source_rate.py @@ -0,0 +1,95 @@ +import os +from pathlib import Path + +THIRD_PARTY_DIR_NAMES = { + 'node_modules', 'vendor', 'third_party', 'third-party', 'extern', 'external', 'deps', + 'Cesium', 'Cesium-1.132', 'dist', 'build' +} + +CODE_EXTENSIONS = { + '.py', '.js', '.ts', '.tsx', '.jsx', '.css', '.scss', '.html', '.c', '.h', '.cpp', '.hpp', + '.cc', '.hh', '.m', '.mm', '.java', '.go', '.rs', '.cs', '.vue', '.svelte' +} + +SKIP_DIR_NAMES = { + '.git', '.hg', '.svn', '__pycache__', '.mypy_cache', '.pytest_cache', '.idea', '.vscode', + '.venv', 'venv', 'env', '.tox', '.cache', 'Resources', 'icons', 'tex', 'terminal' +} + +MAX_FILE_SIZE_BYTES = 2 * 1024 * 1024 # 2 MiB + + +def is_third_party(path: Path) -> bool: + for part in path.parts: + # normalize case on Windows + name = part.lower() + if name in {n.lower() for n in THIRD_PARTY_DIR_NAMES}: + return True + return False + + +def should_skip_dir(dirname: str) -> bool: + return dirname.lower() in {n.lower() for n in SKIP_DIR_NAMES} + + +def is_code_file(path: Path) -> bool: + return path.suffix.lower() in CODE_EXTENSIONS + + +def count_lines(path: Path) -> int: + try: + if path.stat().st_size > MAX_FILE_SIZE_BYTES: + return 0 + # Try text read with universal newlines, fallback to binary + try: + with path.open('r', encoding='utf-8', errors='ignore') as f: + return sum(1 for _ in f) + except Exception: + with path.open('rb') as f: + return f.read().count(b'\n') + except Exception: + return 0 + + +def main(root: Path) -> None: + third_party_loc = 0 + first_party_loc = 0 + third_party_files = 0 + first_party_files = 0 + + for dirpath, dirnames, filenames in os.walk(root): + # prune directories + dirnames[:] = [d for d in dirnames if not should_skip_dir(d)] + + current = Path(dirpath) + current_is_third = is_third_party(current) + + for fn in filenames: + p = current / fn + if not is_code_file(p): + continue + loc = count_lines(p) + if current_is_third: + third_party_loc += loc + third_party_files += 1 + else: + first_party_loc += loc + first_party_files += 1 + + total_loc = first_party_loc + third_party_loc + open_source_rate = (third_party_loc / total_loc) * 100 if total_loc else 0.0 + + print('Open-Source Rate (by LOC): {:.2f}%'.format(open_source_rate)) + print('\nBreakdown:') + print(' First-party LOC : {} ({} files)'.format(first_party_loc, first_party_files)) + print(' Third-party LOC : {} ({} files)'.format(third_party_loc, third_party_files)) + print(' Total LOC : {}'.format(total_loc)) + print('\nNotes:') + print(' - Third-party directories are heuristically detected by common names: {}'.format(', '.join(sorted(THIRD_PARTY_DIR_NAMES)))) + print(' - Static asset directories are skipped: {}'.format(', '.join(sorted(SKIP_DIR_NAMES)))) + print(' - Counted code extensions: {}'.format(', '.join(sorted(CODE_EXTENSIONS)))) + + +if __name__ == '__main__': + main(Path('.').resolve()) + diff --git a/ui/property_panel.py b/ui/property_panel.py index 65a5e874..3411306f 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -9790,6 +9790,12 @@ except Exception as e: collision_group.setLayout(collision_layout) self._propertyLayout.addWidget(collision_group) + # 同步一次状态,确保主按钮与可见性按钮齐全 + try: + self._updateCollisionPanelState(model) + except Exception: + pass + except Exception as e: print(f"创建碰撞面板失败: {e}") import traceback @@ -10144,8 +10150,9 @@ except Exception as e: collision_np.show() print(f"显示碰撞:{model.getName()}") - # 立即更新按钮状态 + # 立即更新按钮状态,并同步更新面板(确保缺失主按钮时补建) self._updateCollisionVisibilityButton(model) + self._updateCollisionPanelState(model) except Exception as e: print(f"切换碰撞可见性失败: {e}") @@ -10353,26 +10360,18 @@ except Exception as e: has_collision = self._hasCollision(model) print(f"模型 {model.getName()} 是否有碰撞体: {has_collision}-------------------------------------------------") - # 更新状态徽章(使用固定宽度) - if has_collision: - new_badge = self.createFixedStatusBadge("已启用", "green") - else: - new_badge = self.createFixedStatusBadge("未启用", "red") - - # 替换旧的徽章 - old_badge = self.collision_status_badge - parent_layout = old_badge.parent().layout() - if parent_layout: - # 找到旧徽章在布局中的位置 - for i in range(parent_layout.count()): - item = parent_layout.itemAt(i) - if item and item.widget() == old_badge: - # 移除旧徽章并添加新徽章 - parent_layout.removeWidget(old_badge) - old_badge.deleteLater() - parent_layout.addWidget(new_badge, 0, 0, 1, 1) # 状态徽章在第0行第1列 - self.collision_status_badge = new_badge - break + # 更新状态徽章(直接修改现有控件,避免布局冲突) + if hasattr(self, 'collision_status_badge') and self.collision_status_badge: + if has_collision: + self.collision_status_badge.setText("已启用") + # 使用固定宽度的绿色样式,保持尺寸一致 + self.collision_status_badge.setStyleSheet(self.badge_style_green_fixed) + else: + self.collision_status_badge.setText("未启用") + # 使用固定宽度的红色样式,保持尺寸一致 + self.collision_status_badge.setStyleSheet(self.badge_style_red_fixed) + # 触发重绘,确保立即可见 + self.collision_status_badge.update() if has_collision: # 有碰撞:显示移除按钮,下拉框变为只读并显示当前类型 @@ -10388,6 +10387,18 @@ except Exception as e: except: pass self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + else: + # 若不存在主按钮,则创建“移除碰撞”按钮并加入布局 + try: + self.collision_button = self.createModernButton("移除碰撞") + self.collision_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_button.setStyleSheet(self._collision_button_style()) + self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + if hasattr(self, 'collision_layout') and self.collision_layout is not None: + row = self.collision_layout.rowCount() + self.collision_layout.addWidget(self.collision_button, row, 0, 1, 4) + except Exception as _e: + print(f"创建移除碰撞按钮失败: {_e}") # 获取并显示当前碰撞类型,设置为只读 current_shape = self._getCurrentCollisionShape(model) @@ -10407,6 +10418,13 @@ except Exception as e: self.collision_visibility_button.setVisible(True) self._updateCollisionVisibilityButton(model) + # 确保按钮顺序:可见性按钮在上,主按钮在下 + if hasattr(self, 'collision_layout'): + try: + self._repositionButtons(self.collision_layout.rowCount()) + except Exception: + pass + print(f"碰撞面板状态更新:有碰撞 - {current_shape}") else: @@ -10422,6 +10440,18 @@ except Exception as e: except: pass self.collision_button.clicked.connect(lambda: self._addCollisionAndUpdate(model)) + else: + # 若不存在主按钮,则创建“添加碰撞”按钮并加入布局 + try: + self.collision_button = self.createModernButton("添加碰撞") + self.collision_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_button.setStyleSheet(self._collision_button_style()) + self.collision_button.clicked.connect(lambda: self._addCollisionAndUpdate(model)) + if hasattr(self, 'collision_layout') and self.collision_layout is not None: + row = self.collision_layout.rowCount() + self.collision_layout.addWidget(self.collision_button, row, 0, 1, 4) + except Exception as _e: + print(f"创建添加碰撞按钮失败: {_e}") # 恢复为可编辑状态 self.collision_shape_combo.setEnabled(True) @@ -10435,6 +10465,13 @@ except Exception as e: if hasattr(self, 'collision_visibility_button'): self.collision_visibility_button.setVisible(False) + # 确保按钮顺序:主按钮位于底部 + if hasattr(self, 'collision_layout'): + try: + self._repositionButtons(self.collision_layout.rowCount()) + except Exception: + pass + print(f"碰撞面板状态更新:无碰撞 - 可编辑") except Exception as e: @@ -11097,13 +11134,24 @@ except Exception as e: layout = self.collision_layout is_collision_visible = self._isCollisionVisible(model) - # 找到合适的行位置 + # 放在当前最后一行 current_row = layout.rowCount() self.collision_visibility_button = QPushButton("隐藏碰撞" if is_collision_visible else "显示碰撞") self.collision_visibility_button.setStyleSheet(self._collision_button_style()) self.collision_visibility_button.clicked.connect(lambda: self._toggleCollisionVisibility(model)) - layout.addWidget(self.collision_visibility_button, current_row - 1, 0, 1, 4) + layout.addWidget(self.collision_visibility_button, current_row, 0, 1, 4) + + # 确保存在主按钮(有碰撞时应显示移除碰撞) + if self._hasCollision(model) and not hasattr(self, 'collision_button'): + try: + self.collision_button = self.createModernButton("移除碰撞") + self.collision_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.collision_button.setStyleSheet(self._collision_button_style()) + self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model)) + layout.addWidget(self.collision_button, current_row + 1, 0, 1, 4) + except Exception as _e: + print(f"在可见性按钮后创建移除碰撞按钮失败: {_e}") except Exception as e: print(f"添加可见性按钮失败: {e}") @@ -11114,22 +11162,39 @@ except Exception as e: if hasattr(self, 'collision_layout'): layout = self.collision_layout - # 移动可见性按钮 - if hasattr(self, 'collision_visibility_button'): - layout.addWidget(self.collision_visibility_button, new_row, 0, 1, 4) - new_row += 1 + def _remove_if_present(w): + try: + if not w: + return + # 从布局中移除该控件(如果已存在) + for i in range(layout.count()): + item = layout.itemAt(i) + if item and item.widget() is w: + layout.removeWidget(w) + break + except Exception: + pass - # 移动主按钮 - if hasattr(self, 'collision_button'): - layout.addWidget(self.collision_button, new_row, 0, 1, 4) + # 计算底部行 + bottom_row = layout.rowCount() + + # 按固定顺序重新放置:先可见性按钮,再主按钮 + if hasattr(self, 'collision_visibility_button') and self.collision_visibility_button: + _remove_if_present(self.collision_visibility_button) + layout.addWidget(self.collision_visibility_button, bottom_row, 0, 1, 4) + bottom_row += 1 + + if hasattr(self, 'collision_button') and self.collision_button: + _remove_if_present(self.collision_button) + layout.addWidget(self.collision_button, bottom_row, 0, 1, 4) except Exception as e: print(f"重新定位按钮失败: {e}") def _hideCollisionParameterControls(self): - """隐藏碰撞参数控件(保留按钮)""" + """隐藏并移除碰撞参数控件与其所在的行(保留状态/形状/按钮行)""" try: - # 清理属性引用,但保留按钮 + # 1) 先清理对象属性引用,避免残留信号 param_attrs = [ 'collision_pos_x', 'collision_pos_y', 'collision_pos_z', 'collision_radius', @@ -11137,47 +11202,72 @@ except Exception as e: 'collision_capsule_radius', 'collision_capsule_height', 'collision_normal_x', 'collision_normal_y', 'collision_normal_z' ] - - # 隐藏并删除参数控件 for attr in param_attrs: if hasattr(self, attr): widget = getattr(self, attr) - if widget: - widget.setVisible(False) - widget.setParent(None) - widget.deleteLater() + try: + if widget: + widget.setParent(None) + widget.deleteLater() + except Exception: + pass delattr(self, attr) - # 同时清理可能的标签控件 - if hasattr(self, 'collision_layout'): + # 2) 深度移除布局中第2行(索引>=2)之后的所有参数相关项,保留按钮 + if hasattr(self, 'collision_layout') and self.collision_layout is not None: layout = self.collision_layout - # 收集需要移除的控件(不包括基本控件和按钮) - widgets_to_remove = [] + def _remove_layout_recursive(q_layout): + try: + while q_layout.count(): + child = q_layout.takeAt(0) + if child.widget(): + w = child.widget() + w.setParent(None) + w.deleteLater() + elif child.layout(): + _remove_layout_recursive(child.layout()) + except Exception: + pass - for i in range(layout.rowCount()): - if i >= 2: # 从第3行开始检查 - for j in range(layout.columnCount()): - item = layout.itemAtPosition(i, j) - if item: - widget = item.widget() - if widget and hasattr(widget, 'text'): - # 检查是否是参数相关的标签 - text = widget.text() - if any(keyword in text for keyword in - ['位置偏移', 'X:', 'Y:', 'Z:', '半径:', '尺寸:', '宽度:', '长度:', '高度:', - '法向量:', 'Nx:', 'Ny:', 'Nz:']): - widgets_to_remove.append(widget) + row_count = layout.rowCount() + col_count = layout.columnCount() + # 从参数区域开始清理(行索引2及以后),但跳过我们要保留的按钮 + for i in range(2, row_count): + for j in range(0, col_count): + item = layout.itemAtPosition(i, j) + if not item: + continue + # 若是按钮,且是保留的按钮,则跳过 + w = item.widget() + if w is not None: + try: + if (hasattr(self, 'collision_button') and w == self.collision_button) or (hasattr(self, 'collision_visibility_button') and w == self.collision_visibility_button): + continue + except Exception: + pass + layout.removeWidget(w) + w.setParent(None) + w.deleteLater() + elif item.layout(): + # 移除嵌套布局(包含标签与输入框) + nested = item.layout() + _remove_layout_recursive(nested) + layout.removeItem(nested) - # 移除参数标签 - for widget in widgets_to_remove: - widget.setVisible(False) - widget.setParent(None) - widget.deleteLater() + # 尝试把保留的按钮移动到参数区域第一行(行2)下方,保持整洁 + try: + next_row = 2 + if hasattr(self, 'collision_visibility_button'): + self.collision_visibility_button.setVisible(False) + if hasattr(self, 'collision_button') and self.collision_button is not None: + layout.addWidget(self.collision_button, next_row, 0, 1, 4) + except Exception: + pass - print("隐藏碰撞参数控件完成(保留按钮)") + print("隐藏碰撞参数控件完成(仅保留状态、形状与按钮)") except Exception as e: print(f"隐藏碰撞参数控件失败: {e}") import traceback - traceback.print_exc() \ No newline at end of file + traceback.print_exc()