修复windows无法显示坐标轴的问题

This commit is contained in:
Hector 2025-10-27 17:56:46 +08:00
parent 6540870d7e
commit 944ee53d64
14 changed files with 65 additions and 212 deletions

2
.idea/EG.iml generated
View File

@ -5,7 +5,7 @@
<excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 virtualenv at ~/EG/venv" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.10 (EG)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">

2
.idea/misc.xml generated
View File

@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.12 (PythonProject)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 virtualenv at ~/EG/venv" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (EG)" project-jdk-type="Python SDK" />
</project>

View File

@ -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 *

View File

@ -1,2 +1,2 @@
name="QPanda3D"
name="QMeta3D"
__all__ = ["Env_Grid_Maker"]

View File

@ -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)

View File

@ -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)}")
print(f"同步 Meta3D 窗口尺寸失败: {str(e)}")

View File

@ -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 *

View File

@ -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 *

View File

@ -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 *

View File

@ -1,2 +1,2 @@
name="QPanda3D"
name="QMeta3D"
__all__ = ["generate_qt_to_pd3d_translator"]

View File

@ -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)
QMeta3D_Key_translation += "Qt.{}:'unknown',\n".format(lst[i])
QMeta3D_Key_translation += "}"
print(QMeta3D_Key_translation)

View File

@ -1,2 +1,2 @@
name="QPanda3D"
name="QMeta3D"
__all__ = ["QMeta3DWidget.py", "Meta3DWorld.py", "QMeta3D_Keys_Translation.py"]

View File

@ -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

View File

@ -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创建测试:")