ui替换
This commit is contained in:
parent
69e2bda47e
commit
1fc3e154d1
@ -1,192 +1,188 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
from core.CustomMouseController import CustomMouseController
|
||||
|
||||
# 获取 RenderPipelineFile 的路径
|
||||
render_pipeline_path = './RenderPipelineFile'
|
||||
# 将该路径添加到 sys.path 中,确保 Python 能够找到它
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, project_root)
|
||||
sys.path.insert(0, render_pipeline_path)
|
||||
|
||||
from RenderPipelineFile.rpcore import RenderPipeline
|
||||
_global_render_pipeline = None
|
||||
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
from panda3d.core import *
|
||||
from panda3d.core import GraphicsOutput, Texture, ConfigVariableManager, WindowProperties
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
import platform
|
||||
|
||||
from QMeta3D.QMouseWatcherNode import QMouseWatcherNode
|
||||
|
||||
from RenderPipelineFile.rpcore.render_target import RenderTarget
|
||||
__all__ = ["Meta3DWorld"]
|
||||
|
||||
_global_world_instance=None
|
||||
import builtins
|
||||
|
||||
|
||||
class Meta3DWorld(ShowBase):
|
||||
def __init__(self, width=1380, height=750, is_fullscreen=False, size=1.0, clear_color=LVecBase4f(0, 0.5, 0, 1),
|
||||
name="qMeta3D"):
|
||||
|
||||
global _global_world_instance
|
||||
global _global_render_pipeline
|
||||
|
||||
_global_world_instance = self
|
||||
|
||||
sort = -100
|
||||
self.parent = None
|
||||
|
||||
# 设置基本配置
|
||||
loadPrcFileData("", "show-frame-rate-meter 0")
|
||||
loadPrcFileData("", "window-type offscreen") # 设置为离屏渲染
|
||||
loadPrcFileData("", f"win-size {width} {height}")
|
||||
loadPrcFileData("", "win-fixed-size #f") # 允许窗口调整大小
|
||||
|
||||
# 🚀 VR性能优化配置
|
||||
loadPrcFileData("", "prefer-single-buffer true") # 减少缓冲区交换开销
|
||||
loadPrcFileData("", "gl-force-flush false") # 避免强制glFlush导致的性能损失
|
||||
loadPrcFileData("", "sync-video false") # 禁用默认VSync,让OpenVR控制
|
||||
loadPrcFileData("", "support-stencil false") # 禁用不必要的模板缓冲区
|
||||
loadPrcFileData("", "clock-mode non-real-time")
|
||||
# loadPrcFileData("", "gl-debug true")
|
||||
|
||||
if (is_fullscreen):
|
||||
loadPrcFileData("", "fullscreen #t")
|
||||
|
||||
self.render_pipeline = RenderPipeline()
|
||||
self.render_pipeline.pre_showbase_init()
|
||||
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# 初始化渲染管线并设置可调整大小的标志
|
||||
self.render_pipeline.create(self)
|
||||
_global_render_pipeline = self.render_pipeline
|
||||
|
||||
# 创建 Qt 能读的 RGBA8 贴图
|
||||
self.qt_output_tex = Texture("qt_output_tex")
|
||||
self.qt_output_tex.set_format(Texture.F_rgba8)
|
||||
self.qt_output_tex.set_component_type(Texture.T_unsigned_byte)
|
||||
|
||||
# # 获取图形管线对象
|
||||
# gsg = self.win.getGsg()
|
||||
# host = gsg.getEngine().getHost()
|
||||
|
||||
self.win.add_render_texture(self.qt_output_tex, GraphicsOutput.RTM_copy_ram)
|
||||
|
||||
#self.screenTexture = Texture()
|
||||
#self.screenTexture.setMinfilter(Texture.FTLinear)
|
||||
#self.screenTexture.setFormat(Texture.FRgba32)
|
||||
#self.screenTexture.set_wrap_u(Texture.WM_clamp)
|
||||
#self.screenTexture.set_wrap_v(Texture.WM_clamp)
|
||||
|
||||
# buff_size_x = int(self.win.get_x_size() * size)
|
||||
# buff_size_y = int(self.win.get_y_size() * size)
|
||||
# winprops = WindowProperties()
|
||||
# winprops.set_size(buff_size_x, buff_size_y)
|
||||
#
|
||||
#
|
||||
# props = FrameBufferProperties()
|
||||
# props.set_rgb_color(True)
|
||||
# props.set_rgba_bits(8, 8, 8, 8)
|
||||
# props.set_depth_bits(8)
|
||||
|
||||
# self.buff = self.graphicsEngine.make_output(
|
||||
# self.pipe, name, sort,
|
||||
# props, winprops,
|
||||
# GraphicsPipe.BF_resizeable,
|
||||
# self.win.get_gsg(), self.win)
|
||||
|
||||
#self.screenTexture = render_pipeline._final_stage.target.color_tex
|
||||
|
||||
#self.buff.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)
|
||||
# self.buff.set_sort(sort)
|
||||
|
||||
|
||||
|
||||
#self.cam = self.makeCamera(self.buff)
|
||||
#self.render_pipeline._showbase.cam=self.makeCamera(self.buff)
|
||||
|
||||
#self.render_pipeline._showbase.cam=self.makeCamera(self.buff)
|
||||
|
||||
#self.render_pipeline._showbase.camera.reparentTo(self.render)
|
||||
#base.camera.reparentTo(self.render)
|
||||
#self.cam.reparentTo(self.render) # 可选同步
|
||||
|
||||
#render_pipeline.set_camera(self.cam)
|
||||
|
||||
#添加渲染效果
|
||||
#self.cam = self.render_pipeline._showbase.cam
|
||||
#self.camNode = self.cam.node()
|
||||
#self.camLens = self.camNode.get_lens()
|
||||
self.render_pipeline._showbase.camera = self.render_pipeline._showbase.cam
|
||||
#self.render_pipeline.daytime_mgr.update()
|
||||
|
||||
|
||||
# if clear_color is None:
|
||||
# self.buff.set_clear_active(GraphicsOutput.RTPColor, False)
|
||||
# else:
|
||||
# self.buff.set_clear_color(clear_color)
|
||||
# self.buff.set_clear_active(GraphicsOutput.RTPColor, True)
|
||||
|
||||
# self.disableMouse()
|
||||
self.mouse_controller = CustomMouseController(self)
|
||||
self.mouse_controller.setUp()
|
||||
|
||||
# 添加错误处理钩子
|
||||
self.accept("transform_state_error", self._handle_transform_error)
|
||||
|
||||
def _handle_transform_error(self):
|
||||
"""处理TransformState相关的错误"""
|
||||
try:
|
||||
from panda3d.core import TransformState, RenderState
|
||||
TransformState.clear_cache()
|
||||
RenderState.clear_cache()
|
||||
print("已清理TransformState和RenderState缓存")
|
||||
except Exception as e:
|
||||
print(f"清理缓存时出错: {e}")
|
||||
|
||||
def render_pipeline(self):
|
||||
"""获取 RenderPipeline 实例"""
|
||||
return self._render_pipeline
|
||||
|
||||
|
||||
def set_parent(self, parent: QWidget):
|
||||
self.parent = parent
|
||||
self.mouseWatcherNode = QMouseWatcherNode(parent)
|
||||
|
||||
def getAspectRatio(self, win = None):
|
||||
if win is None and self.parent is not None:
|
||||
return float(self.parent.width()) / float(self.parent.height())
|
||||
else:
|
||||
return super().getAspectRatio(win)
|
||||
|
||||
def get_render_pipeline():
|
||||
"""获取全局 RenderPipeline 单例"""
|
||||
if _global_render_pipeline is None:
|
||||
raise RuntimeError(
|
||||
"RenderPipeline has not been initialized yet. Please create a 3DWorld instance first.")
|
||||
return _global_render_pipeline
|
||||
|
||||
def resize_buffer(self, width: int, height: int):
|
||||
props = WindowProperties()
|
||||
props.set_size(width, height)
|
||||
self.win.request_properties(props)
|
||||
|
||||
# 重新分配输出贴图的大小
|
||||
self.qt_output_tex.set_x_size(width)
|
||||
self.qt_output_tex.set_y_size(height)
|
||||
|
||||
# 更新 lens 的 aspect ratio
|
||||
if self.camLens:
|
||||
self.camLens.set_film_size(width, height) # 或 set_aspect_ratio(width / height)
|
||||
|
||||
# 强制更新窗口(有时在 Qt 内嵌时需要)
|
||||
import sys
|
||||
import os
|
||||
|
||||
from core.CustomMouseController import CustomMouseController
|
||||
|
||||
# 获取 RenderPipelineFile 的路径
|
||||
render_pipeline_path = './RenderPipelineFile'
|
||||
# 将该路径添加到 sys.path 中,确保 Python 能够找到它
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, project_root)
|
||||
sys.path.insert(0, render_pipeline_path)
|
||||
|
||||
from RenderPipelineFile.rpcore import RenderPipeline
|
||||
_global_render_pipeline = None
|
||||
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
from panda3d.core import *
|
||||
from panda3d.core import GraphicsOutput, Texture, ConfigVariableManager, WindowProperties
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
import platform
|
||||
|
||||
from QMeta3D.QMouseWatcherNode import QMouseWatcherNode
|
||||
|
||||
from RenderPipelineFile.rpcore.render_target import RenderTarget
|
||||
__all__ = ["Meta3DWorld"]
|
||||
|
||||
_global_world_instance=None
|
||||
import builtins
|
||||
|
||||
|
||||
class Meta3DWorld(ShowBase):
|
||||
def __init__(self, width=1380, height=750, is_fullscreen=False, size=1.0, clear_color=LVecBase4f(0, 0.5, 0, 1),
|
||||
name="qMeta3D"):
|
||||
|
||||
global _global_world_instance
|
||||
global _global_render_pipeline
|
||||
|
||||
_global_world_instance = self
|
||||
|
||||
sort = -100
|
||||
self.parent = None
|
||||
|
||||
# 设置基本配置
|
||||
loadPrcFileData("", "show-frame-rate-meter 0")
|
||||
loadPrcFileData("", "window-type onscreen") # 改为正常窗口渲染
|
||||
loadPrcFileData("", f"win-size {width} {height}")
|
||||
loadPrcFileData("", "win-fixed-size #f") # 允许窗口调整大小
|
||||
|
||||
# 🚀 VR性能优化配置
|
||||
loadPrcFileData("", "prefer-single-buffer true") # 减少缓冲区交换开销
|
||||
loadPrcFileData("", "gl-force-flush false") # 避免强制glFlush导致的性能损失
|
||||
loadPrcFileData("", "sync-video false") # 禁用默认VSync,让OpenVR控制
|
||||
loadPrcFileData("", "support-stencil false") # 禁用不必要的模板缓冲区
|
||||
loadPrcFileData("", "clock-mode non-real-time")
|
||||
# loadPrcFileData("", "gl-debug true")
|
||||
|
||||
if (is_fullscreen):
|
||||
loadPrcFileData("", "fullscreen #t")
|
||||
|
||||
self.render_pipeline = RenderPipeline()
|
||||
self.render_pipeline.pre_showbase_init()
|
||||
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# 初始化渲染管线并设置可调整大小的标志
|
||||
self.render_pipeline.create(self)
|
||||
_global_render_pipeline = self.render_pipeline
|
||||
|
||||
# 正常窗口渲染不需要额外的输出纹理
|
||||
# 注释掉离屏渲染相关的纹理创建
|
||||
# self.qt_output_tex = Texture("qt_output_tex")
|
||||
# self.qt_output_tex.set_format(Texture.F_rgba8)
|
||||
# self.qt_output_tex.set_component_type(Texture.T_unsigned_byte)
|
||||
# self.win.add_render_texture(self.qt_output_tex, GraphicsOutput.RTM_copy_ram)
|
||||
|
||||
#self.screenTexture = Texture()
|
||||
#self.screenTexture.setMinfilter(Texture.FTLinear)
|
||||
#self.screenTexture.setFormat(Texture.FRgba32)
|
||||
#self.screenTexture.set_wrap_u(Texture.WM_clamp)
|
||||
#self.screenTexture.set_wrap_v(Texture.WM_clamp)
|
||||
|
||||
# buff_size_x = int(self.win.get_x_size() * size)
|
||||
# buff_size_y = int(self.win.get_y_size() * size)
|
||||
# winprops = WindowProperties()
|
||||
# winprops.set_size(buff_size_x, buff_size_y)
|
||||
#
|
||||
#
|
||||
# props = FrameBufferProperties()
|
||||
# props.set_rgb_color(True)
|
||||
# props.set_rgba_bits(8, 8, 8, 8)
|
||||
# props.set_depth_bits(8)
|
||||
|
||||
# self.buff = self.graphicsEngine.make_output(
|
||||
# self.pipe, name, sort,
|
||||
# props, winprops,
|
||||
# GraphicsPipe.BF_resizeable,
|
||||
# self.win.get_gsg(), self.win)
|
||||
|
||||
#self.screenTexture = render_pipeline._final_stage.target.color_tex
|
||||
|
||||
#self.buff.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)
|
||||
# self.buff.set_sort(sort)
|
||||
|
||||
|
||||
|
||||
#self.cam = self.makeCamera(self.buff)
|
||||
#self.render_pipeline._showbase.cam=self.makeCamera(self.buff)
|
||||
|
||||
#self.render_pipeline._showbase.cam=self.makeCamera(self.buff)
|
||||
|
||||
#self.render_pipeline._showbase.camera.reparentTo(self.render)
|
||||
#base.camera.reparentTo(self.render)
|
||||
#self.cam.reparentTo(self.render) # 可选同步
|
||||
|
||||
#render_pipeline.set_camera(self.cam)
|
||||
|
||||
#添加渲染效果
|
||||
#self.cam = self.render_pipeline._showbase.cam
|
||||
#self.camNode = self.cam.node()
|
||||
#self.camLens = self.camNode.get_lens()
|
||||
self.render_pipeline._showbase.camera = self.render_pipeline._showbase.cam
|
||||
#self.render_pipeline.daytime_mgr.update()
|
||||
|
||||
|
||||
# if clear_color is None:
|
||||
# self.buff.set_clear_active(GraphicsOutput.RTPColor, False)
|
||||
# else:
|
||||
# self.buff.set_clear_color(clear_color)
|
||||
# self.buff.set_clear_active(GraphicsOutput.RTPColor, True)
|
||||
|
||||
# self.disableMouse()
|
||||
self.mouse_controller = CustomMouseController(self)
|
||||
self.mouse_controller.setUp()
|
||||
|
||||
# 添加错误处理钩子
|
||||
self.accept("transform_state_error", self._handle_transform_error)
|
||||
|
||||
def _handle_transform_error(self):
|
||||
"""处理TransformState相关的错误"""
|
||||
try:
|
||||
from panda3d.core import TransformState, RenderState
|
||||
TransformState.clear_cache()
|
||||
RenderState.clear_cache()
|
||||
print("已清理TransformState和RenderState缓存")
|
||||
except Exception as e:
|
||||
print(f"清理缓存时出错: {e}")
|
||||
|
||||
def render_pipeline(self):
|
||||
"""获取 RenderPipeline 实例"""
|
||||
return self._render_pipeline
|
||||
|
||||
|
||||
def set_parent(self, parent: QWidget):
|
||||
self.parent = parent
|
||||
self.mouseWatcherNode = QMouseWatcherNode(parent)
|
||||
|
||||
def getAspectRatio(self, win = None):
|
||||
if win is None and self.parent is not None:
|
||||
return float(self.parent.width()) / float(self.parent.height())
|
||||
else:
|
||||
return super().getAspectRatio(win)
|
||||
|
||||
def get_render_pipeline():
|
||||
"""获取全局 RenderPipeline 单例"""
|
||||
if _global_render_pipeline is None:
|
||||
raise RuntimeError(
|
||||
"RenderPipeline has not been initialized yet. Please create a 3DWorld instance first.")
|
||||
return _global_render_pipeline
|
||||
|
||||
def resize_buffer(self, width: int, height: int):
|
||||
props = WindowProperties()
|
||||
props.set_size(width, height)
|
||||
self.win.request_properties(props)
|
||||
|
||||
# 重新分配输出贴图的大小
|
||||
self.qt_output_tex.set_x_size(width)
|
||||
self.qt_output_tex.set_y_size(height)
|
||||
|
||||
# 更新 lens 的 aspect ratio
|
||||
if self.camLens:
|
||||
self.camLens.set_film_size(width, height) # 或 set_aspect_ratio(width / height)
|
||||
|
||||
# 强制更新窗口(有时在 Qt 内嵌时需要)
|
||||
self.graphicsEngine.open_windows()
|
||||
@ -1,252 +1,236 @@
|
||||
from PyQt5 import QtWidgets, QtGui
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
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
|
||||
|
||||
__all__ = ["QMeta3DWidget"]
|
||||
|
||||
|
||||
class QMeta3DSynchronizer(QTimer):
|
||||
def __init__(self, qMeta3DWidget, FPS=60):
|
||||
QTimer.__init__(self)
|
||||
self.qMeta3DWidget = qMeta3DWidget
|
||||
dt = 1000 / FPS
|
||||
self.setInterval(int(dt))
|
||||
self.timeout.connect(self.tick)
|
||||
|
||||
def tick(self):
|
||||
try:
|
||||
# 在渲染前清理可能损坏的TransformState对象
|
||||
from panda3d.core import TransformState
|
||||
TransformState.clear_cache()
|
||||
|
||||
taskMgr.step()
|
||||
self.qMeta3DWidget.update()
|
||||
except AssertionError as e:
|
||||
# 专门处理 TransformState has_mat() 断言错误
|
||||
if "has_mat" in str(e):
|
||||
print(f"警告: 检测到TransformState断言错误,已静默处理: {e}")
|
||||
# 尝试恢复渲染状态
|
||||
try:
|
||||
# 强制清理缓存并重试
|
||||
from panda3d.core import TransformState, RenderState
|
||||
TransformState.clear_cache()
|
||||
RenderState.clear_cache()
|
||||
taskMgr.step()
|
||||
self.qMeta3DWidget.update()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# 重新抛出其他断言错误
|
||||
raise
|
||||
except Exception as e:
|
||||
# 静默处理其他所有异常
|
||||
print(f"警告: 检测到异常,已静默处理: {e}")
|
||||
|
||||
|
||||
def get_panda_key_modifiers(evt):
|
||||
panda_mods = []
|
||||
qt_mods = evt.modifiers()
|
||||
for qt_mod, panda_mod in QMeta3D_Modifier_translation.items():
|
||||
if (qt_mods & qt_mod) == qt_mod:
|
||||
panda_mods.append(panda_mod)
|
||||
return panda_mods
|
||||
|
||||
|
||||
def get_panda_key_modifiers_prefix(evt):
|
||||
# join all modifiers (except NoModifier, which is None) with '-'
|
||||
prefix = "-".join([mod for mod in get_panda_key_modifiers(evt) if mod is not None])
|
||||
|
||||
# if the prefix is not empty, append a '-'
|
||||
if prefix:
|
||||
prefix += '-'
|
||||
|
||||
return prefix
|
||||
|
||||
|
||||
class QMeta3DWidget(QWidget):
|
||||
def __init__(self, Meta3DWorld, parent=None, FPS=60, debug=False):
|
||||
QWidget.__init__(self, parent)
|
||||
self.rp_sync_requested = False
|
||||
# set fixed geometry
|
||||
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()
|
||||
# pandaTimer.start(0)
|
||||
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
|
||||
# Setup another timer that redraws this widget in a specific FPS
|
||||
# redrawTimer = QTimer(self)
|
||||
# redrawTimer.timeout.connect(self.update)
|
||||
# redrawTimer.start(1000/FPS)
|
||||
|
||||
self.paintSurface = QPainter()
|
||||
self.rotate = QTransform()
|
||||
self.rotate.rotate(180)
|
||||
self.out_image = QImage()
|
||||
|
||||
size = self.Meta3DWorld.cam.node().get_lens().get_film_size()
|
||||
self.initial_film_size = QSizeF(size.x, size.y)
|
||||
self.initial_size = self.size()
|
||||
|
||||
self.synchronizer = QMeta3DSynchronizer(self, FPS)
|
||||
self.synchronizer.start()
|
||||
|
||||
self.debug = debug
|
||||
|
||||
def mousePressEvent(self, evt):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button])
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def mouseMoveEvent(self, evt:QtGui.QMouseEvent):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "mouse-move"
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
|
||||
def mouseReleaseEvent(self, evt):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button])
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def wheelEvent(self, evt):
|
||||
delta = evt.angleDelta().y()
|
||||
try:
|
||||
if self.debug:
|
||||
print(f"wheel {delta}")
|
||||
messenger.send('wheel',[{"delta":delta}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def keyPressEvent(self, evt):
|
||||
key = evt.key()
|
||||
try:
|
||||
k = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key])
|
||||
if self.debug:
|
||||
print(k)
|
||||
messenger.send(k)
|
||||
except:
|
||||
print("Unimplemented key. Please send an issue on github to fix this problem")
|
||||
|
||||
def keyReleaseEvent(self, evt):
|
||||
key = evt.key()
|
||||
try:
|
||||
k = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key])
|
||||
if self.debug:
|
||||
print(k)
|
||||
messenger.send(k)
|
||||
except:
|
||||
print("Unimplemented key. Please send an issue on github to fix this problem")
|
||||
|
||||
def resizeEvent(self, evt):
|
||||
width = evt.size().width()
|
||||
height = evt.size().height()
|
||||
#print(f"width:{width}")
|
||||
#print(f"height:{height}")
|
||||
|
||||
from Meta3DWorld import resize_buffer
|
||||
|
||||
lens = self.Meta3DWorld.cam.node().get_lens()
|
||||
|
||||
def minimumSizeHint(self):
|
||||
return QSize(400, 300)
|
||||
|
||||
def paintEvent(self, event):
|
||||
tex = self.Meta3DWorld.qt_output_tex
|
||||
gsg = base.win.getGsg()
|
||||
|
||||
if not gsg:
|
||||
self.update()
|
||||
return
|
||||
|
||||
if not tex.hasRamImage():
|
||||
base.graphicsEngine.extractTextureData(tex, gsg)
|
||||
self.update()
|
||||
return
|
||||
|
||||
data = tex.getRamImage().getData()
|
||||
tex_width = tex.getXSize()
|
||||
tex_height = tex.getYSize()
|
||||
expected_len = tex_width * tex_height * 4
|
||||
|
||||
if len(data) != expected_len:
|
||||
print(f"⚠️ 像素数据长度异常({len(data)} != {expected_len}),跳过绘制")
|
||||
self.update()
|
||||
return
|
||||
|
||||
img = QImage(data, tex_width, tex_height, QImage.Format_ARGB32).mirrored()
|
||||
|
||||
widget_width = self.width()
|
||||
widget_height = self.height()
|
||||
|
||||
painter = QPainter(self)
|
||||
|
||||
# 【保持宽高比的缩放】
|
||||
scaled_img = img.scaled(widget_width, widget_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
|
||||
# 居中绘制
|
||||
x_offset = (widget_width - scaled_img.width()) // 2
|
||||
y_offset = (widget_height - scaled_img.height()) // 2
|
||||
|
||||
painter.drawImage(x_offset, y_offset, scaled_img)
|
||||
painter.end()
|
||||
|
||||
def sync_Meta3d_window_size(self, width, height):
|
||||
try:
|
||||
from panda3d.core import WindowProperties, LVecBase2i
|
||||
|
||||
# 确保尺寸是4的倍数(RenderPipeline 要求)
|
||||
adjusted_width = width - width % 4
|
||||
adjusted_height = height - height % 4
|
||||
|
||||
# 更新窗口属性
|
||||
props = WindowProperties()
|
||||
props.setSize(adjusted_width, adjusted_height)
|
||||
|
||||
# 对于 offscreen 渲染,直接更新窗口属性
|
||||
if self.Meta3DWorld.win:
|
||||
self.Meta3DWorld.win.request_properties(props)
|
||||
|
||||
# 手动触发 RenderPipeline 的尺寸更新逻辑
|
||||
if hasattr(self.Meta3DWorld, 'render_pipeline'):
|
||||
# 更新全局分辨率
|
||||
from RenderPipelineFile.rpcore.globals import Globals
|
||||
Globals.native_resolution = LVecBase2i(adjusted_width, adjusted_height)
|
||||
|
||||
# 重新计算渲染分辨率
|
||||
self.Meta3DWorld.render_pipeline._compute_render_resolution()
|
||||
|
||||
# 通知各个管理器处理尺寸变化
|
||||
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.Meta3DWorld.render_pipeline.plugin_mgr.trigger_hook("window_resized")
|
||||
except Exception as e:
|
||||
from PyQt5 import QtWidgets, QtGui
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
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
|
||||
|
||||
__all__ = ["QMeta3DWidget"]
|
||||
|
||||
|
||||
class QMeta3DSynchronizer(QTimer):
|
||||
def __init__(self, qMeta3DWidget, FPS=60):
|
||||
QTimer.__init__(self)
|
||||
self.qMeta3DWidget = qMeta3DWidget
|
||||
dt = 1000 / FPS
|
||||
self.setInterval(int(dt))
|
||||
self.timeout.connect(self.tick)
|
||||
|
||||
def tick(self):
|
||||
try:
|
||||
# 在渲染前清理可能损坏的TransformState对象
|
||||
from panda3d.core import TransformState
|
||||
TransformState.clear_cache()
|
||||
|
||||
taskMgr.step()
|
||||
self.qMeta3DWidget.update()
|
||||
except AssertionError as e:
|
||||
# 专门处理 TransformState has_mat() 断言错误
|
||||
if "has_mat" in str(e):
|
||||
print(f"警告: 检测到TransformState断言错误,已静默处理: {e}")
|
||||
# 尝试恢复渲染状态
|
||||
try:
|
||||
# 强制清理缓存并重试
|
||||
from panda3d.core import TransformState, RenderState
|
||||
TransformState.clear_cache()
|
||||
RenderState.clear_cache()
|
||||
taskMgr.step()
|
||||
self.qMeta3DWidget.update()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# 重新抛出其他断言错误
|
||||
raise
|
||||
except Exception as e:
|
||||
# 静默处理其他所有异常
|
||||
print(f"警告: 检测到异常,已静默处理: {e}")
|
||||
|
||||
|
||||
def get_panda_key_modifiers(evt):
|
||||
panda_mods = []
|
||||
qt_mods = evt.modifiers()
|
||||
for qt_mod, panda_mod in QMeta3D_Modifier_translation.items():
|
||||
if (qt_mods & qt_mod) == qt_mod:
|
||||
panda_mods.append(panda_mod)
|
||||
return panda_mods
|
||||
|
||||
|
||||
def get_panda_key_modifiers_prefix(evt):
|
||||
# join all modifiers (except NoModifier, which is None) with '-'
|
||||
prefix = "-".join([mod for mod in get_panda_key_modifiers(evt) if mod is not None])
|
||||
|
||||
# if the prefix is not empty, append a '-'
|
||||
if prefix:
|
||||
prefix += '-'
|
||||
|
||||
return prefix
|
||||
|
||||
|
||||
class QMeta3DWidget(QWidget):
|
||||
def __init__(self, Meta3DWorld, parent=None, FPS=60, debug=False):
|
||||
QWidget.__init__(self, parent)
|
||||
self.rp_sync_requested = False
|
||||
# set fixed geometry
|
||||
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()
|
||||
# pandaTimer.start(0)
|
||||
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
|
||||
# Setup another timer that redraws this widget in a specific FPS
|
||||
# redrawTimer = QTimer(self)
|
||||
# redrawTimer.timeout.connect(self.update)
|
||||
# redrawTimer.start(1000/FPS)
|
||||
|
||||
self.paintSurface = QPainter()
|
||||
self.rotate = QTransform()
|
||||
self.rotate.rotate(180)
|
||||
self.out_image = QImage()
|
||||
|
||||
size = self.Meta3DWorld.cam.node().get_lens().get_film_size()
|
||||
self.initial_film_size = QSizeF(size.x, size.y)
|
||||
self.initial_size = self.size()
|
||||
|
||||
self.synchronizer = QMeta3DSynchronizer(self, FPS)
|
||||
self.synchronizer.start()
|
||||
|
||||
self.debug = debug
|
||||
|
||||
def mousePressEvent(self, evt):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button])
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def mouseMoveEvent(self, evt:QtGui.QMouseEvent):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "mouse-move"
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
|
||||
def mouseReleaseEvent(self, evt):
|
||||
button = evt.button()
|
||||
try:
|
||||
b = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button])
|
||||
if self.debug:
|
||||
print(b)
|
||||
messenger.send(b,[{"x":evt.x(),"y":evt.y()}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def wheelEvent(self, evt):
|
||||
delta = evt.angleDelta().y()
|
||||
try:
|
||||
if self.debug:
|
||||
print(f"wheel {delta}")
|
||||
messenger.send('wheel',[{"delta":delta}])
|
||||
except:
|
||||
print("Unimplemented button. Please send an issue on github to fix this problem")
|
||||
|
||||
def keyPressEvent(self, evt):
|
||||
key = evt.key()
|
||||
try:
|
||||
k = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key])
|
||||
if self.debug:
|
||||
print(k)
|
||||
messenger.send(k)
|
||||
except:
|
||||
print("Unimplemented key. Please send an issue on github to fix this problem")
|
||||
|
||||
def keyReleaseEvent(self, evt):
|
||||
key = evt.key()
|
||||
try:
|
||||
k = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key])
|
||||
if self.debug:
|
||||
print(k)
|
||||
messenger.send(k)
|
||||
except:
|
||||
print("Unimplemented key. Please send an issue on github to fix this problem")
|
||||
|
||||
def resizeEvent(self, evt):
|
||||
width = evt.size().width()
|
||||
height = evt.size().height()
|
||||
#print(f"width:{width}")
|
||||
#print(f"height:{height}")
|
||||
|
||||
from Meta3DWorld import resize_buffer
|
||||
|
||||
lens = self.Meta3DWorld.cam.node().get_lens()
|
||||
|
||||
def minimumSizeHint(self):
|
||||
return QSize(400, 300)
|
||||
|
||||
def paintEvent(self, event):
|
||||
# 正常窗口渲染模式下,Panda3D 会直接渲染到窗口
|
||||
# 不需要从纹理复制数据
|
||||
# 只需要确保渲染循环继续运行
|
||||
|
||||
# 触发一次渲染更新
|
||||
if hasattr(self.Meta3DWorld, 'win') and self.Meta3DWorld.win:
|
||||
# 确保窗口大小匹配
|
||||
width = self.width()
|
||||
height = self.height()
|
||||
|
||||
# 如果窗口大小不匹配,更新窗口属性
|
||||
if (self.Meta3DWorld.win.get_x_size() != width or
|
||||
self.Meta3DWorld.win.get_y_size() != height):
|
||||
from panda3d.core import WindowProperties
|
||||
props = WindowProperties()
|
||||
props.setSize(width, height)
|
||||
self.Meta3DWorld.win.request_properties(props)
|
||||
|
||||
# 继续下一次更新
|
||||
self.update()
|
||||
|
||||
def sync_Meta3d_window_size(self, width, height):
|
||||
try:
|
||||
from panda3d.core import WindowProperties
|
||||
|
||||
# 更新窗口属性
|
||||
props = WindowProperties()
|
||||
props.setSize(width, height)
|
||||
|
||||
# 对于正常窗口渲染,直接更新窗口属性
|
||||
if self.Meta3DWorld.win:
|
||||
self.Meta3DWorld.win.request_properties(props)
|
||||
|
||||
# 如果使用 RenderPipeline,更新相关设置
|
||||
if hasattr(self.Meta3DWorld, 'render_pipeline'):
|
||||
try:
|
||||
from RenderPipelineFile.rpcore.globals import Globals
|
||||
from panda3d.core import LVecBase2i
|
||||
|
||||
# 更新全局分辨率
|
||||
Globals.native_resolution = LVecBase2i(width, height)
|
||||
|
||||
# 重新计算渲染分辨率
|
||||
self.Meta3DWorld.render_pipeline._compute_render_resolution()
|
||||
|
||||
# 通知各个管理器处理尺寸变化
|
||||
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.Meta3DWorld.render_pipeline.plugin_mgr.trigger_hook("window_resized")
|
||||
except Exception as rp_e:
|
||||
print(f"RenderPipeline 尺寸更新失败: {rp_e}")
|
||||
except Exception as e:
|
||||
print(f"同步 Meta3D 窗口尺寸失败: {str(e)}")
|
||||
@ -199,11 +199,11 @@ class EmbeddedWebBrowser(QWidget):
|
||||
|
||||
def main():
|
||||
|
||||
# 设置标准输出编码为 UTF-8,避免 Windows GBK 编码问题
|
||||
if sys.platform == 'win32':
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
# 注释掉标准输出重定向,让输出正常显示在终端
|
||||
# if sys.platform == 'win32':
|
||||
# import io
|
||||
# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
# sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
print("=" * 60)
|
||||
print("[WebEngine] 启动独立 WebEngine 进程")
|
||||
|
||||
8619
ui/widgets.py
8619
ui/widgets.py
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user