修复打包失败
This commit is contained in:
parent
9e8ff3ec25
commit
cd21f8eb0a
@ -142,7 +142,7 @@ class Panda3DWorld(ShowBase):
|
||||
|
||||
#render_pipeline.set_camera(self.cam)
|
||||
|
||||
#添加渲染效果<EFBFBD><EFBFBD>
|
||||
#添加渲染效果
|
||||
#self.cam = self.render_pipeline._showbase.cam
|
||||
#self.camNode = self.cam.node()
|
||||
#self.camLens = self.camNode.get_lens()
|
||||
@ -160,6 +160,19 @@ class Panda3DWorld(ShowBase):
|
||||
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
|
||||
@ -198,4 +211,4 @@ def resize_buffer(self, width: int, height: int):
|
||||
self.camLens.set_film_size(width, height) # 或 set_aspect_ratio(width / height)
|
||||
|
||||
# 强制更新窗口(有时在 Qt 内嵌时需要)
|
||||
self.graphicsEngine.open_windows()
|
||||
self.graphicsEngine.open_windows()
|
||||
@ -33,17 +33,34 @@ class QPanda3DSynchronizer(QTimer):
|
||||
self.setInterval(int(dt))
|
||||
self.timeout.connect(self.tick)
|
||||
|
||||
# def tick(self):
|
||||
# taskMgr.step()
|
||||
# self.qPanda3DWidget.update()
|
||||
|
||||
def tick(self):
|
||||
try:
|
||||
# 在渲染前清理可能损坏的TransformState对象
|
||||
from panda3d.core import TransformState
|
||||
TransformState.clear_cache()
|
||||
|
||||
taskMgr.step()
|
||||
self.qPanda3DWidget.update()
|
||||
except:
|
||||
# 静默处理所有异常,包括 has_mat() 断言错误
|
||||
pass
|
||||
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.qPanda3DWidget.update()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# 重新抛出其他断言错误
|
||||
raise
|
||||
except Exception as e:
|
||||
# 静默处理其他所有异常
|
||||
print(f"警告: 检测到异常,已静默处理: {e}")
|
||||
|
||||
|
||||
def get_panda_key_modifiers(evt):
|
||||
@ -242,7 +259,7 @@ class QPanda3DWidget(QWidget):
|
||||
#
|
||||
# data = tex.getRamImage().getData()
|
||||
# width = tex.getXSize()
|
||||
# height = tex.getYSize()
|
||||
# height = tex.getYSize()
|
||||
# expected_len = width * height * 4
|
||||
#
|
||||
# if len(data) != expected_len:
|
||||
@ -335,10 +352,4 @@ class QPanda3DWidget(QWidget):
|
||||
print(f"Panda3D 窗口尺寸已同步为: {adjusted_width} x {adjusted_height}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"同步 Panda3D 窗口尺寸失败: {str(e)}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print(f"同步 Panda3D 窗口尺寸失败: {str(e)}")
|
||||
@ -272,7 +272,7 @@ class SelectionSystem:
|
||||
def updateSelectionBoxTask(self, task):
|
||||
"""选择框更新任务 - 平衡性能和实时性"""
|
||||
try:
|
||||
update_interval = 0.1
|
||||
update_interval = 0.05
|
||||
|
||||
if not hasattr(self, '_last_selection_box_update'):
|
||||
self._last_selection_box_update = 0
|
||||
@ -2023,44 +2023,44 @@ class SelectionSystem:
|
||||
# 添加到新父项
|
||||
new_parent_item.addChild(item)
|
||||
|
||||
def _updateSelectionVisuals(self, nodePath):
|
||||
"""更新选择的视觉效果(选择框和坐标轴)"""
|
||||
try:
|
||||
if nodePath and not nodePath.isEmpty():
|
||||
node_name = nodePath.getName()
|
||||
print(f"开始为节点 {node_name} 创建选择框和坐标轴...")
|
||||
|
||||
# 创建选择框
|
||||
print("创建选择框...")
|
||||
self.createSelectionBox(nodePath)
|
||||
if self.selectionBox:
|
||||
box_name = "Unknown"
|
||||
if self.selectionBox and not self.selectionBox.isEmpty():
|
||||
box_name = self.selectionBox.getName()
|
||||
print(f"✓ 选择框创建成功: {box_name}")
|
||||
else:
|
||||
print("× 选择框创建失败")
|
||||
|
||||
# 创建坐标轴
|
||||
print("创建坐标轴...")
|
||||
self.createGizmo(nodePath)
|
||||
if self.gizmo:
|
||||
gizmo_name = "Unknown"
|
||||
if self.gizmo and not self.gizmo.isEmpty():
|
||||
gizmo_name = self.gizmo.getName()
|
||||
print(f"✓ 坐标轴创建成功: {gizmo_name}")
|
||||
else:
|
||||
print("× 坐标轴创建失败")
|
||||
|
||||
print(f"✓ 选中了节点: {node_name}")
|
||||
else:
|
||||
print("清除选择...")
|
||||
self.clearSelectionBox()
|
||||
self.clearGizmo()
|
||||
print("✓ 取消选择")
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新选择视觉效果失败: {e}")
|
||||
# def _updateSelectionVisuals(self, nodePath):
|
||||
# """更新选择的视觉效果(选择框和坐标轴)"""
|
||||
# try:
|
||||
# if nodePath and not nodePath.isEmpty():
|
||||
# node_name = nodePath.getName()
|
||||
# print(f"开始为节点 {node_name} 创建选择框和坐标轴...")
|
||||
#
|
||||
# # 创建选择框
|
||||
# print("创建选择框...")
|
||||
# self.createSelectionBox(nodePath)
|
||||
# if self.selectionBox:
|
||||
# box_name = "Unknown"
|
||||
# if self.selectionBox and not self.selectionBox.isEmpty():
|
||||
# box_name = self.selectionBox.getName()
|
||||
# print(f"✓ 选择框创建成功: {box_name}")
|
||||
# else:
|
||||
# print("× 选择框创建失败")
|
||||
#
|
||||
# # 创建坐标轴
|
||||
# print("创建坐标轴...")
|
||||
# self.createGizmo(nodePath)
|
||||
# if self.gizmo:
|
||||
# gizmo_name = "Unknown"
|
||||
# if self.gizmo and not self.gizmo.isEmpty():
|
||||
# gizmo_name = self.gizmo.getName()
|
||||
# print(f"✓ 坐标轴创建成功: {gizmo_name}")
|
||||
# else:
|
||||
# print("× 坐标轴创建失败")
|
||||
#
|
||||
# print(f"✓ 选中了节点: {node_name}")
|
||||
# else:
|
||||
# print("清除选择...")
|
||||
# self.clearSelectionBox()
|
||||
# self.clearGizmo()
|
||||
# print("✓ 取消选择")
|
||||
#
|
||||
# except Exception as e:
|
||||
# print(f"更新选择视觉效果失败: {e}")
|
||||
|
||||
def getSelectedNode(self):
|
||||
"""获取当前选中的节点"""
|
||||
|
||||
4
main.py
4
main.py
@ -49,10 +49,6 @@ from direct.actor.Actor import Actor
|
||||
from PyQt5.sip import wrapinstance
|
||||
#from RenderPipelineFile.toolkit.material_editor.main import MaterialEditor
|
||||
|
||||
from setup_panda3d import setup_panda3d
|
||||
setup_panda3d()
|
||||
|
||||
|
||||
class MyWorld(CoreWorld):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@ -404,7 +404,7 @@ class ProjectManager:
|
||||
success = self._executeStandardBuild(build_dir, parent_window)
|
||||
|
||||
if success:
|
||||
QMessageBox.information(parent_window, "成功",
|
||||
QMessageBox.information(parent_window, "成功",
|
||||
"打包完成!\n可执行文件在 build/dist/ 目录中。\n"
|
||||
"支持的格式:\n"
|
||||
"- Windows: .exe 安装程序\n"
|
||||
@ -413,7 +413,7 @@ class ProjectManager:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(parent_window, "错误", f"打包过程出错:{str(e)}")
|
||||
return False
|
||||
@ -1092,205 +1092,299 @@ class ProjectManager:
|
||||
# f.write(app_code)
|
||||
|
||||
def _createStandardSetupFile(self, build_dir, project_name):
|
||||
"""创建标准的setup.py文件 - 按照Panda3D官方文档"""
|
||||
"""创建优化的标准setup.py文件"""
|
||||
setup_code = f'''#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
{project_name} 打包配置文件
|
||||
使用 Panda3D 标准打包工具
|
||||
"""
|
||||
"""
|
||||
{project_name} 打包配置文件
|
||||
使用 Panda3D 标准打包工具
|
||||
"""
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import setup
|
||||
|
||||
# 应用程序配置
|
||||
APP_NAME = "{project_name}"
|
||||
APP_VERSION = "1.0.0"
|
||||
MAIN_SCRIPT = "main.py"
|
||||
# 应用程序配置
|
||||
APP_NAME = "{project_name}"
|
||||
APP_VERSION = "1.0.0"
|
||||
MAIN_SCRIPT = "main.py"
|
||||
|
||||
setup(
|
||||
name=APP_NAME,
|
||||
version=APP_VERSION,
|
||||
|
||||
# Panda3D 打包选项
|
||||
options={{
|
||||
'build_apps': {{
|
||||
# GUI应用程序
|
||||
'gui_apps': {{
|
||||
APP_NAME: MAIN_SCRIPT,
|
||||
}},
|
||||
|
||||
# 包含的文件模式
|
||||
'include_patterns': [
|
||||
'*.bam', # 场景文件
|
||||
'*.egg', # 模型文件
|
||||
'*.jpg', '*.png', # 纹理文件
|
||||
'*.wav', '*.ogg', # 音频文件
|
||||
'*.ttf', '*.otf', # 字体文件
|
||||
],
|
||||
|
||||
# 排除的文件模式
|
||||
'exclude_patterns': [
|
||||
'*.pyc',
|
||||
'__pycache__/**',
|
||||
'.git/**',
|
||||
'.vscode/**',
|
||||
'*.log',
|
||||
],
|
||||
|
||||
# Panda3D 插件
|
||||
'plugins': [
|
||||
'pandagl', # OpenGL渲染器
|
||||
'pandaegg', # Egg文件支持
|
||||
'p3openal_audio', # OpenAL音频
|
||||
],
|
||||
|
||||
# 包含的Python模块
|
||||
'include_modules': {{
|
||||
'*': [
|
||||
'direct.showbase.ShowBase',
|
||||
'direct.task',
|
||||
'direct.actor',
|
||||
'direct.interval',
|
||||
'direct.stdpy.file',
|
||||
'direct.stdpy.pickle',
|
||||
'panda3d.core',
|
||||
'panda3d.direct',
|
||||
'rpcore',
|
||||
'rpcore.util.movement_controller',
|
||||
'rpcore.native',
|
||||
'rpcore.render_pipeline',
|
||||
'rplibs',
|
||||
'rpplugins',
|
||||
'rpplugins.scattering',
|
||||
'rpplugins.pssm',
|
||||
'rpplugins.godrays',
|
||||
'json',
|
||||
'os',
|
||||
'sys',
|
||||
'six',
|
||||
'collections',
|
||||
'collections.abs',
|
||||
'weakref',
|
||||
'copy',
|
||||
'itertools',
|
||||
'importlib',
|
||||
'importlib.util',
|
||||
'importlib.machinery',
|
||||
setup(
|
||||
name=APP_NAME,
|
||||
version=APP_VERSION,
|
||||
|
||||
# Panda3D 打包选项
|
||||
options={{
|
||||
'build_apps': {{
|
||||
# GUI应用程序
|
||||
'gui_apps': {{
|
||||
APP_NAME: MAIN_SCRIPT,
|
||||
}},
|
||||
|
||||
# 包含的文件模式
|
||||
'include_patterns': [
|
||||
'*.bam', # 场景文件
|
||||
'*.egg', # 模型文件
|
||||
'*.jpg', '*.png', '*.tga', '*.dds', # 纹理文件
|
||||
'*.wav', '*.ogg', '*.mp3', # 音频文件
|
||||
'*.ttf', '*.otf', # 字体文件
|
||||
'config/*.yaml', # 配置文件
|
||||
'shaders/**/*.glsl', # 着色器文件
|
||||
],
|
||||
}},
|
||||
|
||||
# 排除的Python模块(减小体积)
|
||||
'exclude_modules': {{
|
||||
'*': [
|
||||
'tkinter', # Tkinter GUI
|
||||
'matplotlib', # 绘图库
|
||||
'numpy', # 数值计算(如果不需要)
|
||||
'scipy', # 科学计算(如果不需要)
|
||||
'PIL', # 图像处理(如果不需要)
|
||||
'wx', # wxPython
|
||||
'PyQt5', # Qt界面库
|
||||
'setuptools', # 安装工具
|
||||
'distutils', # 分发工具
|
||||
|
||||
# 排除的文件模式
|
||||
'exclude_patterns': [
|
||||
'*.pyc',
|
||||
'__pycache__/**',
|
||||
'.git/**',
|
||||
'.vscode/**',
|
||||
'*.log',
|
||||
'**/*.tmp',
|
||||
'**/temp/**',
|
||||
'samples/**', # 示例文件
|
||||
'**/.DS_Store',
|
||||
'**/Thumbs.db',
|
||||
],
|
||||
|
||||
# Panda3D 插件
|
||||
'plugins': [
|
||||
'pandagl', # OpenGL渲染器
|
||||
'pandaegl', # EGL支持
|
||||
'p3assimp', # Assimp模型导入
|
||||
'p3ffmpeg', # FFmpeg视频支持
|
||||
'p3openal_audio', # OpenAL音频
|
||||
],
|
||||
|
||||
# 包含的Python模块
|
||||
'include_modules': {{
|
||||
'*': [
|
||||
# 核心模块
|
||||
'direct.showbase.ShowBase',
|
||||
'direct.task',
|
||||
'direct.actor',
|
||||
'direct.interval',
|
||||
'direct.gui',
|
||||
'direct.stdpy.file',
|
||||
'direct.stdpy.pickle',
|
||||
'panda3d.core',
|
||||
'panda3d.direct',
|
||||
|
||||
# 渲染管线相关
|
||||
'rpcore',
|
||||
'rpcore.util.movement_controller',
|
||||
'rpcore.native',
|
||||
'rpcore.render_pipeline',
|
||||
'rplibs',
|
||||
'rpplugins',
|
||||
|
||||
# 标准库
|
||||
'json',
|
||||
'os',
|
||||
'sys',
|
||||
'math',
|
||||
'random',
|
||||
'collections',
|
||||
'copy',
|
||||
'itertools',
|
||||
'importlib',
|
||||
],
|
||||
}},
|
||||
|
||||
# 排除的Python模块(减小体积)
|
||||
'exclude_modules': {{
|
||||
'*': [
|
||||
# GUI框架
|
||||
'tkinter',
|
||||
'matplotlib',
|
||||
'numpy',
|
||||
'scipy',
|
||||
'PIL',
|
||||
'wx',
|
||||
'PyQt5',
|
||||
'PySide2',
|
||||
'kivy',
|
||||
|
||||
# 开发工具
|
||||
'setuptools',
|
||||
'pip',
|
||||
'wheel',
|
||||
'twine',
|
||||
'pytest',
|
||||
'unittest',
|
||||
|
||||
# 其他不必要的模块
|
||||
'sqlite3',
|
||||
'xmlrpc',
|
||||
'ftplib',
|
||||
'telnetlib',
|
||||
],
|
||||
}},
|
||||
|
||||
# 平台设置
|
||||
'platforms': [
|
||||
'win_amd64', # Windows 64位
|
||||
'linux_x86_64', # Linux 64位
|
||||
],
|
||||
|
||||
# 优化设置
|
||||
'strip_docstrings': True, # 移除文档字符串
|
||||
'optimize': 1, # 字节码优化级别
|
||||
}},
|
||||
|
||||
# 平台设置
|
||||
'platforms': [
|
||||
'win_amd64', # Windows 64位
|
||||
'linux_x86_64', # Linux 64位
|
||||
# 'macosx_10_9_x86_64', # macOS(如果需要)
|
||||
],
|
||||
|
||||
# 优化设置
|
||||
'strip_docstrings': True, # 移除文档字符串
|
||||
}},
|
||||
}},
|
||||
|
||||
# 标准setuptools选项
|
||||
author="Panda3D 引擎编辑器",
|
||||
author_email="user@example.com",
|
||||
description=f"{{APP_NAME}} - 使用Panda3D创建的3D应用程序",
|
||||
long_description="这是一个使用Panda3D引擎编辑器创建的3D应用程序。",
|
||||
|
||||
# 依赖项
|
||||
install_requires=[
|
||||
'panda3d>=1.10.13',
|
||||
],
|
||||
|
||||
# Python版本要求
|
||||
python_requires='>=3.7',
|
||||
|
||||
# 分类信息
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Topic :: Games/Entertainment',
|
||||
'Topic :: Multimedia :: Graphics :: 3D Rendering',
|
||||
],
|
||||
|
||||
# 使用现代的许可证表达式
|
||||
license='MIT',
|
||||
)
|
||||
'''
|
||||
|
||||
|
||||
# 标准setuptools选项
|
||||
author="Panda3D Engine Editor User",
|
||||
author_email="user@example.com",
|
||||
description=f"{{APP_NAME}} - 使用Panda3D创建的3D应用程序",
|
||||
long_description="这是一个使用Panda3D引擎编辑器创建的3D应用程序。",
|
||||
|
||||
# 依赖项
|
||||
install_requires=[
|
||||
'panda3d>=1.10.13',
|
||||
],
|
||||
|
||||
# Python版本要求
|
||||
python_requires='>=3.7',
|
||||
|
||||
# 分类信息
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Topic :: Games/Entertainment',
|
||||
'Topic :: Multimedia :: Graphics :: 3D Rendering',
|
||||
],
|
||||
|
||||
license='MIT',
|
||||
)
|
||||
'''
|
||||
|
||||
setup_path = os.path.join(build_dir, "setup.py")
|
||||
with open(setup_path, "w", encoding="utf-8") as f:
|
||||
f.write(setup_code)
|
||||
|
||||
|
||||
def _executeStandardBuild(self, build_dir, parent_window):
|
||||
"""执行标准的Panda3D打包命令"""
|
||||
"""执行标准的Panda3D打包命令 - 增强版"""
|
||||
try:
|
||||
print(f"开始打包,工作目录: {build_dir}")
|
||||
|
||||
# 首先尝试 bdist_apps(推荐方式)
|
||||
print("执行标准打包命令: python setup.py bdist_apps")
|
||||
|
||||
|
||||
# 显示打包进度提示
|
||||
QMessageBox.information(parent_window, "打包进行中",
|
||||
"正在打包项目,请稍候...\n"
|
||||
"此过程可能需要几分钟时间。\n"
|
||||
"请勿关闭程序。")
|
||||
|
||||
# 使用线程执行打包以避免界面冻结
|
||||
import threading
|
||||
from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
class BuildSignals(QObject):
|
||||
finished = pyqtSignal(bool, str)
|
||||
progress = pyqtSignal(str)
|
||||
|
||||
signals = BuildSignals()
|
||||
|
||||
def build_process():
|
||||
try:
|
||||
print("执行标准打包命令: python setup.py bdist_apps")
|
||||
signals.progress.emit("正在初始化打包过程...")
|
||||
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, "setup.py", "bdist_apps"],
|
||||
cwd=build_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
if output == '' and process.poll() is not None:
|
||||
break
|
||||
if output:
|
||||
print(output.strip())
|
||||
if "copying" in output.lower():
|
||||
signals.progress.emit(f"正在处理文件: {output.strip()[-50:]}")
|
||||
elif "building" in output.lower():
|
||||
signals.progress.emit(output.strip())
|
||||
|
||||
# 获取错误输出
|
||||
stderr = process.stderr.read()
|
||||
if stderr:
|
||||
print("错误输出:", stderr)
|
||||
|
||||
except Exception as e:
|
||||
print(f"执行打包命令时出错: {str(e)}")
|
||||
signals.finished.emit(False, f"打包失败:{str(e)}")
|
||||
|
||||
# 连接信号
|
||||
def on_finished(success, message):
|
||||
if success:
|
||||
QMessageBox.information(parent_window, "成功", message)
|
||||
else:
|
||||
QMessageBox.critical(parent_window, "错误", message)
|
||||
|
||||
def on_progress(message):
|
||||
print(f"[打包进度] {message}")
|
||||
|
||||
signals.finished.connect(on_finished)
|
||||
signals.progress.connect(on_progress)
|
||||
|
||||
# 启动打包线程
|
||||
build_thread = threading.Thread(target=build_process)
|
||||
build_thread.daemon = True
|
||||
build_thread.start()
|
||||
|
||||
return True # 线程已启动
|
||||
|
||||
except Exception as e:
|
||||
print(f"启动打包过程时出错: {str(e)}")
|
||||
QMessageBox.critical(parent_window, "错误", f"启动打包失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def _tryBuildAppsThreaded(self, build_dir, signals):
|
||||
"""尝试使用 build_apps 命令(线程安全版本)"""
|
||||
try:
|
||||
print("执行备用打包命令: python setup.py build_apps")
|
||||
signals.progress.emit("正在执行备用打包方法...")
|
||||
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, "setup.py", "bdist_apps"],
|
||||
[sys.executable, "setup.py", "build_apps"],
|
||||
cwd=build_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
|
||||
# 实时显示输出
|
||||
stdout_lines = []
|
||||
stderr_lines = []
|
||||
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
if output == '' and process.poll() is not None:
|
||||
break
|
||||
if output:
|
||||
print(output.strip())
|
||||
stdout_lines.append(output.strip())
|
||||
|
||||
# 获取错误输出
|
||||
signals.progress.emit(output.strip())
|
||||
|
||||
stderr = process.stderr.read()
|
||||
if stderr:
|
||||
print("错误输出:", stderr)
|
||||
stderr_lines.append(stderr)
|
||||
|
||||
# 检查返回码
|
||||
|
||||
if process.returncode == 0:
|
||||
print("✓ 打包成功完成")
|
||||
return True
|
||||
print("✓ build_apps 成功完成")
|
||||
signals.finished.emit(True, "打包成功完成!\n可执行文件在 dist/ 目录中。")
|
||||
else:
|
||||
# 如果bdist_apps失败,尝试build_apps
|
||||
print(f"bdist_apps 失败 (返回码: {process.returncode}),尝试 build_apps...")
|
||||
return self._tryBuildApps(build_dir, parent_window)
|
||||
|
||||
error_msg = f"打包失败,返回码:{process.returncode}"
|
||||
if stderr:
|
||||
error_msg += f"\n错误信息:{stderr}"
|
||||
signals.finished.emit(False, error_msg)
|
||||
|
||||
except Exception as e:
|
||||
print(f"执行打包命令时出错: {str(e)}")
|
||||
QMessageBox.critical(parent_window, "错误", f"打包失败:{str(e)}")
|
||||
return False
|
||||
signals.finished.emit(False, f"执行 build_apps 失败:{str(e)}")
|
||||
|
||||
def _tryBuildApps(self, build_dir, parent_window):
|
||||
"""尝试使用 build_apps 命令"""
|
||||
|
||||
@ -15,7 +15,7 @@ from PyQt5.QtWidgets import QTreeWidgetItem
|
||||
from panda3d.core import (
|
||||
ModelPool, ModelRoot, Filename, NodePath, GeomNode, Material, Vec4, Vec3,
|
||||
MaterialAttrib, ColorAttrib, Point3, CollisionNode, CollisionSphere,
|
||||
BitMask32, TransparencyAttrib, LColor, TransformState
|
||||
BitMask32, TransparencyAttrib, LColor, TransformState, RenderModeAttrib
|
||||
)
|
||||
import json
|
||||
import aiohttp
|
||||
@ -94,45 +94,33 @@ class SceneManager:
|
||||
# ==================== 模型导入和处理 ====================
|
||||
|
||||
def importModel(self, filepath, apply_unit_conversion=False, normalize_scales=True, auto_convert_to_glb=True):
|
||||
"""导入模型到场景
|
||||
|
||||
Args:
|
||||
filepath: 模型文件路径
|
||||
apply_unit_conversion: 是否应用单位转换(主要针对FBX文件)
|
||||
normalize_scales: 是否标准化子节点缩放(推荐开启)
|
||||
auto_convert_to_glb: 是否自动将非GLB格式转换为GLB以获得更好的动画支持
|
||||
"""
|
||||
|
||||
try:
|
||||
print(f"\n=== 开始导入模型: {filepath} ===")
|
||||
#print(f"单位转换: {'开启' if apply_unit_conversion else '关闭'}")
|
||||
#print(f"自动转换GLB: {'开启' if auto_convert_to_glb else '关闭'}")
|
||||
if not os.path.exists(filepath):
|
||||
print("文件不存在")
|
||||
return None
|
||||
|
||||
filepath = util.normalize_model_path(filepath)
|
||||
original_filepath = filepath
|
||||
|
||||
# 在加载前设置忽略未知属性
|
||||
from panda3d.core import ConfigVariableBool
|
||||
ConfigVariableBool("model-cache-ignore-unknown-properties").setValue(True)
|
||||
# # 在加载前设置忽略未知属性
|
||||
# from panda3d.core import ConfigVariableBool
|
||||
# ConfigVariableBool("model-cache-ignore-unknown-properties").setValue(True)
|
||||
#
|
||||
# # 清除可能存在的模型缓存
|
||||
# from panda3d.core import ModelPool
|
||||
# ModelPool.releaseAllModels()
|
||||
#
|
||||
# # 检查是否需要转换为GLB以获得更好的动画支持
|
||||
# if auto_convert_to_glb and self._shouldConvertToGLB(filepath):
|
||||
# print(f"🔄 检测到需要转换的格式,尝试转换为GLB...")
|
||||
# converted_path = self._convertToGLBWithProgress(filepath)
|
||||
# if converted_path:
|
||||
# print(f"✅ 转换成功: {converted_path}")
|
||||
# filepath = converted_path
|
||||
# # 转换成功的消息已在控制台显示,不再弹窗提示
|
||||
# else:
|
||||
# print(f"⚠️ 转换失败,使用原始文件")
|
||||
|
||||
# 清除可能存在的模型缓存
|
||||
from panda3d.core import ModelPool
|
||||
ModelPool.releaseAllModels()
|
||||
|
||||
# 检查是否需要转换为GLB以获得更好的动画支持
|
||||
if auto_convert_to_glb and self._shouldConvertToGLB(filepath):
|
||||
print(f"🔄 检测到需要转换的格式,尝试转换为GLB...")
|
||||
converted_path = self._convertToGLBWithProgress(filepath)
|
||||
if converted_path:
|
||||
print(f"✅ 转换成功: {converted_path}")
|
||||
filepath = converted_path
|
||||
# 转换成功的消息已在控制台显示,不再弹窗提示
|
||||
else:
|
||||
print(f"⚠️ 转换失败,使用原始文件")
|
||||
|
||||
# 总是重新加载模型以确保材质信息完整
|
||||
# 不使用ModelPool缓存,避免材质信息丢失问题
|
||||
#print("直接从文件加载模型...")
|
||||
model = self.world.loader.loadModel(filepath)
|
||||
if not model:
|
||||
print("加载模型失败")
|
||||
@ -171,23 +159,13 @@ class SceneManager:
|
||||
model.setHpr(0, 90, 0)
|
||||
print("设置模型旋转为 (0, 90, 0)")
|
||||
|
||||
# # 可选的单位转换(主要针对FBX
|
||||
# if apply_unit_conversion and filepath.lower().endswith('.fbx'):
|
||||
# #print("应用FBX单位转换(厘米到米)...")
|
||||
# self._applyUnitConversion(model, 0.01)
|
||||
#
|
||||
# # 智能缩放标准化(处理FBX子节点的大缩放值)
|
||||
# if normalize_scales and filepath.lower().endswith('.fbx'):
|
||||
# #print("标准化FBX模型缩放层级...")
|
||||
# self._normalizeModelScales(model)
|
||||
|
||||
# 调整模型位置到地面
|
||||
model.setPos(0,0,0)
|
||||
#self._adjustModelToGround(model)
|
||||
|
||||
# 创建并设置基础材质
|
||||
print("\n=== 开始设置材质 ===")
|
||||
self._applyMaterialsToModel(model)
|
||||
#self._applyMaterialsToModel(model)
|
||||
|
||||
# 设置碰撞检测(重要!用于选择功能)
|
||||
print("\n=== 设置碰撞检测 ===")
|
||||
@ -540,54 +518,6 @@ class SceneManager:
|
||||
# 失败时使用默认位置
|
||||
model.setPos(0, 0, 0)
|
||||
|
||||
def _applyUnitConversion(self, model, scale_factor):
|
||||
"""应用单位转换缩放
|
||||
|
||||
Args:
|
||||
model: 要转换的模型
|
||||
scale_factor: 缩放因子(如0.01表示从厘米转换到米)
|
||||
"""
|
||||
try:
|
||||
print(f"应用单位转换缩放: {scale_factor}")
|
||||
|
||||
# 检查模型是否已经应用过单位转换
|
||||
if model.hasTag("unit_conversion_applied"):
|
||||
print("模型已应用过单位转换,跳过")
|
||||
return
|
||||
|
||||
|
||||
|
||||
# 获取当前边界用于后续位置调整
|
||||
original_bounds = model.getBounds()
|
||||
|
||||
# 应用缩放
|
||||
model.setScale(scale_factor)
|
||||
|
||||
# 应用缩放(添加异常处理)
|
||||
try:
|
||||
model.setScale(scale_factor)
|
||||
except Exception as e:
|
||||
print(f"直接设置缩放失败: {e},尝试使用变换状态")
|
||||
try:
|
||||
model.setTransform(TransformState.makeScale(scale_factor))
|
||||
except Exception as e2:
|
||||
print(f"使用变换状态设置缩放也失败: {e2}")
|
||||
|
||||
# 应用缩放后验证变换
|
||||
self._validateAndFixTransform(model)
|
||||
# 重新调整位置(因为缩放会影响边界)
|
||||
if original_bounds and not original_bounds.isEmpty():
|
||||
new_bounds = model.getBounds()
|
||||
min_point = new_bounds.getMin()
|
||||
ground_offset = -min_point.getZ()
|
||||
model.setZ(ground_offset)
|
||||
print(f"缩放后重新调整位置: Z偏移 = {ground_offset}")
|
||||
|
||||
print(f"单位转换完成,缩放因子: {scale_factor}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"应用单位转换失败: {str(e)}")
|
||||
|
||||
def _normalizeModelScales(self, model):
|
||||
"""智能标准化模型缩放层级
|
||||
|
||||
@ -679,7 +609,9 @@ class SceneManager:
|
||||
return None
|
||||
|
||||
def _applyScaleNormalization(self, node, normalize_factor, depth=0):
|
||||
"""递归应用缩放标准化,同时调整位置以保持视觉一致性"""
|
||||
"""
|
||||
安全地应用缩放标准化
|
||||
"""
|
||||
try:
|
||||
indent = " " * depth
|
||||
current_scale = node.getScale()
|
||||
@ -689,8 +621,19 @@ class SceneManager:
|
||||
max_scale_component = max(abs(current_scale.x), abs(current_scale.y), abs(current_scale.z))
|
||||
|
||||
if max_scale_component > 10: # 只标准化明显的大缩放
|
||||
# 确保标准化因子有效
|
||||
if normalize_factor <= 0 or normalize_factor > 1000:
|
||||
print(f"{indent}无效的标准化因子: {normalize_factor},跳过")
|
||||
return
|
||||
|
||||
# 应用新的缩放
|
||||
new_scale = current_scale * normalize_factor
|
||||
|
||||
# 检查新缩放是否有效
|
||||
if any(s <= 0 for s in [new_scale.x, new_scale.y, new_scale.z]):
|
||||
print(f"{indent}标准化后产生无效缩放,跳过")
|
||||
return
|
||||
|
||||
node.setScale(new_scale)
|
||||
|
||||
# 同时调整位置:当缩放变小时,位置也应该相应变小以保持视觉相对位置
|
||||
@ -702,11 +645,6 @@ class SceneManager:
|
||||
print(f"{indent} 缩放: {current_scale} -> {new_scale}")
|
||||
print(f"{indent} 位置: {current_pos} -> {new_pos}")
|
||||
|
||||
# 递归处理子节点
|
||||
for i in range(node.getNumChildren()):
|
||||
child = node.getChild(i)
|
||||
self._applyScaleNormalization(child, normalize_factor, depth + 1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"应用缩放标准化失败 ({node.getName()}): {str(e)}")
|
||||
|
||||
@ -1588,6 +1526,8 @@ class SceneManager:
|
||||
nodePath.setScale(scale)
|
||||
print(f"{indent}恢复缩放: {scale}")
|
||||
|
||||
nodePath.show()
|
||||
|
||||
if nodePath.hasTag("has_scripts") and nodePath.getTag("has_scripts") == "true":
|
||||
if hasattr(self.world,'script_manager') and self.world.script_manager:
|
||||
try:
|
||||
|
||||
@ -132,6 +132,8 @@ class MainApp(ShowBase):
|
||||
self.setupMouseClickHandler()
|
||||
self.cTrav = CollisionTraverser()
|
||||
|
||||
self.createFrameRateDisplay()
|
||||
|
||||
self.women_actor = None
|
||||
self.current_actor = None
|
||||
|
||||
@ -1518,7 +1520,35 @@ class MainApp(ShowBase):
|
||||
except Exception as e:
|
||||
print(f"处理鼠标点击失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
traceback.print_exc()\
|
||||
|
||||
def createFrameRateDisplay(self):
|
||||
from direct.gui.DirectGui import DirectLabel
|
||||
from panda3d.core import TextNode
|
||||
|
||||
self.fps_label = DirectLabel(
|
||||
text="FPS:0",
|
||||
pos=(-1.8,0,0.9),
|
||||
scale=0.05,
|
||||
text_fg=(1,1,1,1),
|
||||
text_align=TextNode.ALeft,
|
||||
frameSize=(-0.1,0.3,-0.05,0.05),
|
||||
frameColor=(0,0,0,0.5),
|
||||
text_font=self.getChineseFont() if self.getChineseFont() else None,
|
||||
#parent=self.a2dTopLeft
|
||||
)
|
||||
|
||||
taskMgr.add(self.updateFrameRate,"updateFrameRateTask")
|
||||
|
||||
def updateFrameRate(self,task):
|
||||
try:
|
||||
fps = globalClock.getAverageFrameRate()
|
||||
|
||||
if self.fps_label:
|
||||
self.fps_label.setText(f"FPS:{fps:.1f}")
|
||||
except Exception as e:
|
||||
print(f"更新帧率失败: {e}")
|
||||
return task.cont
|
||||
|
||||
|
||||
# 在 main.py 的最后部分,修改为:
|
||||
|
||||
@ -1386,7 +1386,7 @@ class CustomConsoleDockWidget(QWidget):
|
||||
# 帧率更新定时器
|
||||
self.fpsTimer = QTimer()
|
||||
self.fpsTimer.timeout.connect(self.updateFPS)
|
||||
self.fpsTimer.start(1000) # 每秒更新一次
|
||||
self.fpsTimer.start(100) # 每秒更新一次
|
||||
|
||||
toolbar.addStretch()
|
||||
layout.addLayout(toolbar)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user