From 944ee53d645091089e5ab217bf773089e57e0940 Mon Sep 17 00:00:00 2001 From: Hector <145347438+hudomn@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:56:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwindows=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=9D=90=E6=A0=87=E8=BD=B4=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/EG.iml | 2 +- .idea/misc.xml | 2 +- QMeta3D/Helpers/Env_Grid_Maker.py | 8 - QMeta3D/Helpers/__init__.py | 2 +- QMeta3D/Meta3DWorld.py | 24 +-- QMeta3D/QMeta3DWidget.py | 145 +++--------------- QMeta3D/QMeta3D_Buttons_Translation.py | 9 -- QMeta3D/QMeta3D_Keys_Translation.py | 9 -- QMeta3D/QMeta3D_Modifiers_Translation.py | 9 -- QMeta3D/Tools/__init__.py | 2 +- .../Tools/generate_qt_to_pd3d_translator.py | 8 +- QMeta3D/__init__.py | 2 +- core/selection.py | 47 +++--- core/world.py | 8 +- 14 files changed, 65 insertions(+), 212 deletions(-) diff --git a/.idea/EG.iml b/.idea/EG.iml index ba1e88cf..c608c0d0 100644 --- a/.idea/EG.iml +++ b/.idea/EG.iml @@ -5,7 +5,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index b4e62875..51b9fc17 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/QMeta3D/Helpers/Env_Grid_Maker.py b/QMeta3D/Helpers/Env_Grid_Maker.py index 364abf05..a498611e 100644 --- a/QMeta3D/Helpers/Env_Grid_Maker.py +++ b/QMeta3D/Helpers/Env_Grid_Maker.py @@ -1,11 +1,3 @@ -# -*- coding: utf-8-*- -""" -Module : Env_Grid_Maker -Author : Saifeddine ALOUI + code from Forklift 's code snippet could be found on panda3D's website -Description : - Creates a 3D implementation of grid that shows axes in 3 possible planes - -""" from panda3d.core import * from direct.interval.IntervalGlobal import * diff --git a/QMeta3D/Helpers/__init__.py b/QMeta3D/Helpers/__init__.py index fc125075..e58d61a7 100644 --- a/QMeta3D/Helpers/__init__.py +++ b/QMeta3D/Helpers/__init__.py @@ -1,2 +1,2 @@ -name="QPanda3D" +name="QMeta3D" __all__ = ["Env_Grid_Maker"] \ No newline at end of file diff --git a/QMeta3D/Meta3DWorld.py b/QMeta3D/Meta3DWorld.py index f490791d..180a274e 100644 --- a/QMeta3D/Meta3DWorld.py +++ b/QMeta3D/Meta3DWorld.py @@ -1,10 +1,3 @@ -# -*- coding: utf-8-*- -""" -Module : Panda3DWorld -Author : Saifeddine ALOUI -Description : - Inherit this object to create your custom world -""" import sys import os @@ -29,9 +22,6 @@ from PyQt5.QtWidgets import * # Panda imports from panda3d.core import * -# from panda3d.core import loadPrcFileData -# loadPrcFileData("", "window-type none") # Set Panda to draw its main window in an offscreen buffer -from direct.showbase.DirectObject import DirectObject from panda3d.core import GraphicsOutput, Texture, ConfigVariableManager, WindowProperties # Set up Panda environment @@ -49,12 +39,8 @@ import builtins class Meta3DWorld(ShowBase): - """ - Panda3DWorld : A class to handle all panda3D world manipulation - """ - def __init__(self, width=1380, height=750, is_fullscreen=False, size=1.0, clear_color=LVecBase4f(0, 0.5, 0, 1), - name="qpanda3D"): + name="qMeta3D"): global _global_world_instance global _global_render_pipeline @@ -75,8 +61,8 @@ class Meta3DWorld(ShowBase): loadPrcFileData("", "gl-force-flush false") # 避免强制glFlush导致的性能损失 loadPrcFileData("", "sync-video false") # 禁用默认VSync,让OpenVR控制 loadPrcFileData("", "support-stencil false") # 禁用不必要的模板缓冲区 - loadPrcFileData("", "clock-mode non-real-time") # 禁用Panda3D帧率控制,让OpenVR控制 - # loadPrcFileData("", "gl-debug true") # 调试时可启用OpenGL调试 + loadPrcFileData("", "clock-mode non-real-time") + # loadPrcFileData("", "gl-debug true") if (is_fullscreen): loadPrcFileData("", "fullscreen #t") @@ -192,12 +178,10 @@ def get_render_pipeline(): """获取全局 RenderPipeline 单例""" if _global_render_pipeline is None: raise RuntimeError( - "RenderPipeline has not been initialized yet. Please create a Panda3DWorld instance first.") + "RenderPipeline has not been initialized yet. Please create a 3DWorld instance first.") return _global_render_pipeline def resize_buffer(self, width: int, height: int): - """根据新窗口尺寸调整 Panda3D 渲染输出尺寸""" - # 设置 Panda3D 的窗口尺寸(offscreen 模式下对应输出区域) props = WindowProperties() props.set_size(width, height) self.win.request_properties(props) diff --git a/QMeta3D/QMeta3DWidget.py b/QMeta3D/QMeta3DWidget.py index 9262811c..a39e1869 100644 --- a/QMeta3D/QMeta3DWidget.py +++ b/QMeta3D/QMeta3DWidget.py @@ -1,23 +1,9 @@ -# -*- coding: utf-8-*- -""" -Module : QPanda3DWidget -Author : Saifeddine ALOUI -Description : - This is the QWidget to be inserted in your standard PyQt5 application. - It takes a Panda3DWorld object at init time. - You should first create the Panda3DWorkd object before creating this widget. -""" -# PyQt imports from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from direct.task.TaskManagerGlobal import taskMgr -# Panda imports -from panda3d.core import Texture, WindowProperties, CallbackGraphicsWindow -from panda3d.core import loadPrcFileData - from QMeta3D.QMeta3D_Buttons_Translation import QMeta3D_Button_translation from QMeta3D.QMeta3D_Keys_Translation import QMeta3D_Key_translation from QMeta3D.QMeta3D_Modifiers_Translation import QMeta3D_Modifier_translation @@ -26,9 +12,9 @@ __all__ = ["QMeta3DWidget"] class QMeta3DSynchronizer(QTimer): - def __init__(self, qPanda3DWidget, FPS=60): + def __init__(self, qMeta3DWidget, FPS=60): QTimer.__init__(self) - self.qPanda3DWidget = qPanda3DWidget + self.qMeta3DWidget = qMeta3DWidget dt = 1000 / FPS self.setInterval(int(dt)) self.timeout.connect(self.tick) @@ -40,7 +26,7 @@ class QMeta3DSynchronizer(QTimer): TransformState.clear_cache() taskMgr.step() - self.qPanda3DWidget.update() + self.qMeta3DWidget.update() except AssertionError as e: # 专门处理 TransformState has_mat() 断言错误 if "has_mat" in str(e): @@ -52,7 +38,7 @@ class QMeta3DSynchronizer(QTimer): TransformState.clear_cache() RenderState.clear_cache() taskMgr.step() - self.qPanda3DWidget.update() + self.qMeta3DWidget.update() except: pass else: @@ -84,19 +70,12 @@ def get_panda_key_modifiers_prefix(evt): class QMeta3DWidget(QWidget): - """ - An interactive panda3D QWidget - Parent : Parent QT Widget - FPS : Number of frames per socond to refresh the screen - debug: Switch printing key events to console on/off - """ - - def __init__(self, panda3DWorld, parent=None, FPS=60, debug=False): + def __init__(self, Meta3DWorld, parent=None, FPS=60, debug=False): QWidget.__init__(self, parent) self.rp_sync_requested = False # set fixed geometry - self.panda3DWorld = panda3DWorld - self.panda3DWorld.set_parent(self) + self.Meta3DWorld = Meta3DWorld + self.Meta3DWorld.set_parent(self) # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop # pandaTimer = QTimer(self) # pandaTimer.timeout.connect() @@ -114,7 +93,7 @@ class QMeta3DWidget(QWidget): self.rotate.rotate(180) self.out_image = QImage() - size = self.panda3DWorld.cam.node().get_lens().get_film_size() + size = self.Meta3DWorld.cam.node().get_lens().get_film_size() self.initial_film_size = QSizeF(size.x, size.y) self.initial_size = self.size() @@ -190,92 +169,14 @@ class QMeta3DWidget(QWidget): #print(f"height:{height}") from Meta3DWorld import resize_buffer - #resize_buffer(width, height) - lens = self.panda3DWorld.cam.node().get_lens() - # lens.set_film_size(self.initial_film_size.width() * evt.size().width() / self.initial_size.width(), - # self.initial_film_size.height() * evt.size().height() / self.initial_size.height()) - #self.sync_panda3d_window_size(width, height) - - #self.panda3DWorld.buff.setSize(evt.size().width(), evt.size().height()) + lens = self.Meta3DWorld.cam.node().get_lens() def minimumSizeHint(self): return QSize(400, 300) - # Use the paint event to pull the contents of the panda texture to the widget - # def paintEvent(self, event): - # if self.panda3DWorld.screenTexture.mightHaveRamImage(): - # tex = self.panda3DWorld.screenTexture - # gsg = base.win.getGsg() - # #self.panda3DWorld.screenTexture.store(gsg) - # base.graphicsEngine.extractTextureData(tex, gsg) - # self.panda3DWorld.screenTexture.setFormat(Texture.FRgba32) - # data = self.panda3DWorld.screenTexture.getRamImage().getData() - # img = QImage(data, self.panda3DWorld.screenTexture.getXSize(), self.panda3DWorld.screenTexture.getYSize(), - # QImage.Format_ARGB32).mirrored() - # self.paintSurface.begin(self) - # self.paintSurface.drawImage(0, 0, img) - # self.paintSurface.end() - - # def paintEvent(self, event): - # tex = self.panda3DWorld.qt_output_tex - # - # gsg = base.win.getGsg() - # - # - # if not self.rp_sync_requested: - # base.graphicsEngine.extractTextureData(tex, gsg) - # self.rp_sync_requested = True - # self.update() - # return - # - # if tex.hasRamImage(): - # data = tex.getRamImage().getData() - # width = tex.getXSize() - # height = tex.getYSize() - # - # # ✅ 应该 data 长度 = width * height * 4 - # img = QImage(data, width, height, QImage.Format_ARGB32).mirrored() - # - # painter = QPainter(self) - # painter.drawImage(0, 0, img) - # painter.end() - # - # self.rp_sync_requested = False - # else: - # print("⚠️ Texture has no RAM image yet, retrying next frame") - # self.rp_sync_requested = False - # self.update() - - # def paintEvent(self, event): - # tex = self.panda3DWorld.qt_output_tex - # - # gsg = base.win.getGsg() - # - # if not tex.hasRamImage(): - # base.graphicsEngine.extractTextureData(tex, gsg) - # self.update() # 请求下一帧更新 - # return - # - # data = tex.getRamImage().getData() - # width = tex.getXSize() - # height = tex.getYSize() - # expected_len = width * height * 4 - # - # if len(data) != expected_len: - # print(f"⚠️ 像素数据长度异常({len(data)} != {expected_len}),跳过绘制") - # self.update() - # return - # - # # 一切正常才绘制 - # img = QImage(data, width, height, QImage.Format_ARGB32).mirrored() - # - # painter = QPainter(self) - # painter.drawImage(0, 0, img) - # painter.end() - def paintEvent(self, event): - tex = self.panda3DWorld.qt_output_tex + tex = self.Meta3DWorld.qt_output_tex gsg = base.win.getGsg() if not gsg: @@ -314,8 +215,7 @@ class QMeta3DWidget(QWidget): painter.drawImage(x_offset, y_offset, scaled_img) painter.end() - def sync_panda3d_window_size(self, width, height): - """同步 Panda3D 窗口尺寸到 Qt 窗口尺寸""" + def sync_Meta3d_window_size(self, width, height): try: from panda3d.core import WindowProperties, LVecBase2i @@ -328,28 +228,25 @@ class QMeta3DWidget(QWidget): props.setSize(adjusted_width, adjusted_height) # 对于 offscreen 渲染,直接更新窗口属性 - if self.panda3DWorld.win: - self.panda3DWorld.win.request_properties(props) + if self.Meta3DWorld.win: + self.Meta3DWorld.win.request_properties(props) # 手动触发 RenderPipeline 的尺寸更新逻辑 - if hasattr(self.panda3DWorld, 'render_pipeline'): + if hasattr(self.Meta3DWorld, 'render_pipeline'): # 更新全局分辨率 from RenderPipelineFile.rpcore.globals import Globals Globals.native_resolution = LVecBase2i(adjusted_width, adjusted_height) # 重新计算渲染分辨率 - self.panda3DWorld.render_pipeline._compute_render_resolution() + self.Meta3DWorld.render_pipeline._compute_render_resolution() # 通知各个管理器处理尺寸变化 - self.panda3DWorld.render_pipeline.light_mgr.compute_tile_size() - self.panda3DWorld.render_pipeline.stage_mgr.handle_window_resize() - if hasattr(self.panda3DWorld.render_pipeline, 'debugger'): - self.panda3DWorld.render_pipeline.debugger.handle_window_resize() + self.Meta3DWorld.render_pipeline.light_mgr.compute_tile_size() + self.Meta3DWorld.render_pipeline.stage_mgr.handle_window_resize() + if hasattr(self.Meta3DWorld.render_pipeline, 'debugger'): + self.Meta3DWorld.render_pipeline.debugger.handle_window_resize() # 触发插件的窗口尺寸变化钩子 - self.panda3DWorld.render_pipeline.plugin_mgr.trigger_hook("window_resized") - - print(f"Panda3D 窗口尺寸已同步为: {adjusted_width} x {adjusted_height}") - + self.Meta3DWorld.render_pipeline.plugin_mgr.trigger_hook("window_resized") except Exception as e: - print(f"同步 Panda3D 窗口尺寸失败: {str(e)}") \ No newline at end of file + print(f"同步 Meta3D 窗口尺寸失败: {str(e)}") \ No newline at end of file diff --git a/QMeta3D/QMeta3D_Buttons_Translation.py b/QMeta3D/QMeta3D_Buttons_Translation.py index e0ae0328..4a69d0d6 100644 --- a/QMeta3D/QMeta3D_Buttons_Translation.py +++ b/QMeta3D/QMeta3D_Buttons_Translation.py @@ -1,12 +1,3 @@ -# -*- coding: utf-8-*- -""" -Module : QPanda3D_Translation_Buttons -Author : Niklas Mevenkamp -Description : - Contains a dictionary that translates QT mouse events to panda3d - mouse events. -""" -# PyQt imports from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * diff --git a/QMeta3D/QMeta3D_Keys_Translation.py b/QMeta3D/QMeta3D_Keys_Translation.py index b8a7dcc5..0b0c8dda 100644 --- a/QMeta3D/QMeta3D_Keys_Translation.py +++ b/QMeta3D/QMeta3D_Keys_Translation.py @@ -1,12 +1,3 @@ -# -*- coding: utf-8-*- -""" -Module : QPanda3D_Translation_Keys -Author : Saifeddine ALOUI -Description : - Contains a dictionary that translates QT keyboard events to panda3d - keyboard events. -""" -# PyQt imports from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * diff --git a/QMeta3D/QMeta3D_Modifiers_Translation.py b/QMeta3D/QMeta3D_Modifiers_Translation.py index 40866ecf..17684818 100644 --- a/QMeta3D/QMeta3D_Modifiers_Translation.py +++ b/QMeta3D/QMeta3D_Modifiers_Translation.py @@ -1,12 +1,3 @@ -# -*- coding: utf-8-*- -""" -Module : QPanda3D_Translation_Modifiers -Author : Niklas Mevenkamp -Description : - Contains a dictionary that translates QT keyboard events to panda3d - keyboard events. -""" -# PyQt imports from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * diff --git a/QMeta3D/Tools/__init__.py b/QMeta3D/Tools/__init__.py index c353614c..449353ac 100644 --- a/QMeta3D/Tools/__init__.py +++ b/QMeta3D/Tools/__init__.py @@ -1,2 +1,2 @@ -name="QPanda3D" +name="QMeta3D" __all__ = ["generate_qt_to_pd3d_translator"] \ No newline at end of file diff --git a/QMeta3D/Tools/generate_qt_to_pd3d_translator.py b/QMeta3D/Tools/generate_qt_to_pd3d_translator.py index f167e513..3f296230 100644 --- a/QMeta3D/Tools/generate_qt_to_pd3d_translator.py +++ b/QMeta3D/Tools/generate_qt_to_pd3d_translator.py @@ -4,8 +4,8 @@ from PyQt5.QtWidgets import * H=Qt.__dict__ lst = [a for a in H if a.startswith("Key_")] -QPanda3D_Key_translation ="QPanda3D_Key_translation ={" +QMeta3D_Key_translation = "QMeta3D_Key_translation ={" for i in range(len(lst)): - QPanda3D_Key_translation +="Qt.{}:'unknown',\n".format(lst[i]) -QPanda3D_Key_translation += "}" -print(QPanda3D_Key_translation) \ No newline at end of file + QMeta3D_Key_translation += "Qt.{}:'unknown',\n".format(lst[i]) +QMeta3D_Key_translation += "}" +print(QMeta3D_Key_translation) \ No newline at end of file diff --git a/QMeta3D/__init__.py b/QMeta3D/__init__.py index 5eac2f06..b94cb4e0 100644 --- a/QMeta3D/__init__.py +++ b/QMeta3D/__init__.py @@ -1,2 +1,2 @@ -name="QPanda3D" +name="QMeta3D" __all__ = ["QMeta3DWidget.py", "Meta3DWorld.py", "QMeta3D_Keys_Translation.py"] \ No newline at end of file diff --git a/core/selection.py b/core/selection.py index 66295b8f..5d70c9d8 100644 --- a/core/selection.py +++ b/core/selection.py @@ -422,22 +422,26 @@ class SelectionSystem: is_rotate_tool = self.world.tool_manager.isRotateTool() if self.world.tool_manager else False import os + from panda3d.core import getModelPath, Filename + + # 获取项目根目录 base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # 确保core目录在模型搜索路径中 + model_path = getModelPath() + core_dir = os.path.join(base_dir, "core") + core_filename = Filename.from_os_specific(core_dir) + if not model_path.findFile(core_filename): + model_path.appendDirectory(core_filename) + print(f"✓ 添加core目录到模型搜索路径: {core_dir}") if is_scale_tool: - model_paths = [ - os.path.join(base_dir,"core/UniformScaleHandle.fbx"), - ] + model_path = "core/UniformScaleHandle.fbx" elif is_rotate_tool: - model_paths = [ - os.path.join(base_dir,"core/RotationHandleQuarter.fbx"), - ] + model_path = "core/RotationHandleQuarter.fbx" + arrow_path = "core/TranslateArrowHandle.fbx" else: - model_paths = [ - os.path.join(base_dir, "core/TranslateArrowHandle.fbx"), - ] - - arrow_path = os.path.join(base_dir, "core/TranslateArrowHandle.fbx") + model_path = "core/TranslateArrowHandle.fbx" # model_paths = [ # "core/TranslateArrowHandle.fbx", @@ -445,18 +449,15 @@ class SelectionSystem: # ] gizmo_model = None gizmoRot_model = None - for path in model_paths: - try: - if is_rotate_tool: - gizmo_model = self.world.loader.loadModel(arrow_path) - gizmoRot_model = self.world.loader.loadModel(path) - else: - gizmo_model = self.world.loader.loadModel(path) - if gizmo_model: - #print(f"成功加载模型: {path}") - break - except: - continue + try: + if is_rotate_tool: + gizmo_model = self.world.loader.loadModel(arrow_path) + gizmoRot_model = self.world.loader.loadModel(model_path) + else: + gizmo_model = self.world.loader.loadModel(model_path) + except Exception as e: + print(f"加载模型失败: {e}") + return x_rHandle = None y_rHandle = None diff --git a/core/world.py b/core/world.py index 72ad100e..c4ad9420 100644 --- a/core/world.py +++ b/core/world.py @@ -63,6 +63,13 @@ class CoreWorld(Meta3DWorld): model_path.appendDirectory(resources_filename) print(f"✓ 添加Resources到模型搜索路径: {resources_dir}") + # 添加core目录到搜索路径 + core_dir = os.path.join(project_root, "core") + core_filename = Filename.from_os_specific(core_dir) + if not model_path.findFile(core_filename): + model_path.appendDirectory(core_filename) + print(f"✓ 添加core目录到模型搜索路径: {core_dir}") + # 同时添加各个子目录到搜索路径 subdirs = ['models', 'textures', 'animations', 'icons', 'materials'] for subdir in subdirs: @@ -199,7 +206,6 @@ class CoreWorld(Meta3DWorld): temp_anim.removeNode() # 清理临时动画 except Exception as e: print(f"❌ 动画文件加载失败: {e}") - return None # 4. 尝试创建Actor print(f"4. Actor创建测试:")