修复打包失败

This commit is contained in:
Hector 2025-10-20 09:27:53 +08:00
parent 9e8ff3ec25
commit cd21f8eb0a
8 changed files with 411 additions and 327 deletions

View File

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

View File

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

View File

@ -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):
"""获取当前选中的节点"""

View File

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

View File

@ -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 命令"""

View File

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

View File

@ -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 的最后部分,修改为:

View File

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