236 lines
8.7 KiB
Python
236 lines
8.7 KiB
Python
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)}") |