forked from Rowland/EG
362 lines
13 KiB
Python
362 lines
13 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
FBX模型导入测试 - 演示新的缩放处理选项
|
||
|
||
修复内容:
|
||
1. 默认保持模型原有缩放结构
|
||
2. 提供可选的单位转换功能
|
||
3. 智能缩放标准化(处理子节点大缩放值)
|
||
4. 避免缩放层级混乱问题
|
||
|
||
使用说明:
|
||
1. 运行脚本启动3D编辑器
|
||
2. 通过文件菜单或拖拽导入FBX模型
|
||
3. 观察模型的缩放层级结构
|
||
4. 按U键切换单位转换模式
|
||
5. 按N键切换缩放标准化模式
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
|
||
# 添加主目录到Python路径
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||
|
||
from main import MyWorld
|
||
from ui.main_window import setup_main_window
|
||
from PyQt5.QtCore import Qt
|
||
|
||
|
||
def setup_fbx_import_demo():
|
||
"""设置FBX导入演示"""
|
||
|
||
# 创建世界对象
|
||
world = MyWorld()
|
||
|
||
# 使用新的UI模块创建主窗口
|
||
app, main_window = setup_main_window(world)
|
||
|
||
# 设置窗口标题
|
||
main_window.setWindowTitle("FBX导入测试 - 缩放层级修复")
|
||
|
||
# 设置焦点策略
|
||
main_window.setFocusPolicy(Qt.StrongFocus)
|
||
main_window.setFocus()
|
||
|
||
# 导入选项
|
||
unit_conversion_enabled = False
|
||
scale_normalization_enabled = True # 默认开启缩放标准化
|
||
|
||
def keyPressEvent(event):
|
||
nonlocal unit_conversion_enabled, scale_normalization_enabled
|
||
key = event.key()
|
||
|
||
if key == Qt.Key_U: # U键切换单位转换模式
|
||
unit_conversion_enabled = not unit_conversion_enabled
|
||
status = "开启" if unit_conversion_enabled else "关闭"
|
||
print(f"\n=== 单位转换模式已{status} ===")
|
||
print("下次导入FBX文件时将使用此设置")
|
||
event.accept()
|
||
return
|
||
elif key == Qt.Key_N: # N键切换缩放标准化模式
|
||
scale_normalization_enabled = not scale_normalization_enabled
|
||
status = "开启" if scale_normalization_enabled else "关闭"
|
||
print(f"\n=== 缩放标准化模式已{status} ===")
|
||
print("下次导入FBX文件时将使用此设置")
|
||
event.accept()
|
||
return
|
||
elif key == Qt.Key_I: # I键显示导入信息
|
||
print_import_info(world)
|
||
event.accept()
|
||
return
|
||
elif key == Qt.Key_R: # R键切换射线显示
|
||
state = world.toggleRayDisplay()
|
||
status = "开启" if state else "关闭"
|
||
print(f"\n=== 射线显示已{status} ===")
|
||
event.accept()
|
||
return
|
||
elif key == Qt.Key_S: # S键显示当前设置
|
||
print_current_settings(unit_conversion_enabled, scale_normalization_enabled)
|
||
event.accept()
|
||
return
|
||
|
||
# 调用原始键盘事件处理
|
||
if hasattr(main_window, '_original_keyPressEvent'):
|
||
main_window._original_keyPressEvent(event)
|
||
else:
|
||
event.ignore()
|
||
|
||
# 覆盖importModel方法以使用当前的导入设置
|
||
original_import = world.scene_manager.importModel
|
||
|
||
def enhanced_import(filepath):
|
||
"""增强的导入方法,使用当前导入设置"""
|
||
print(f"\n" + "="*60)
|
||
print(f"导入模型: {os.path.basename(filepath)}")
|
||
print(f"单位转换: {'开启' if unit_conversion_enabled else '关闭'}")
|
||
print(f"缩放标准化: {'开启' if scale_normalization_enabled else '关闭'}")
|
||
print("="*60)
|
||
|
||
result = original_import(
|
||
filepath,
|
||
apply_unit_conversion=unit_conversion_enabled,
|
||
normalize_scales=scale_normalization_enabled
|
||
)
|
||
|
||
if result:
|
||
print("\n导入后的模型结构:")
|
||
print_model_structure(result, max_depth=3, world=world) # 限制显示深度
|
||
|
||
return result
|
||
|
||
world.scene_manager.importModel = enhanced_import
|
||
|
||
# 保存原始键盘事件处理器
|
||
if hasattr(main_window, 'keyPressEvent'):
|
||
main_window._original_keyPressEvent = main_window.keyPressEvent
|
||
main_window.keyPressEvent = keyPressEvent
|
||
|
||
# 添加自定义菜单
|
||
add_custom_menus(main_window, world, unit_conversion_enabled, scale_normalization_enabled)
|
||
|
||
# 输出使用说明
|
||
print_usage_instructions()
|
||
|
||
return app, main_window, world
|
||
|
||
|
||
def add_custom_menus(main_window, world, unit_conversion_enabled, scale_normalization_enabled):
|
||
"""添加自定义菜单选项"""
|
||
# 添加FBX测试菜单
|
||
fbx_menu = main_window.menuBar().addMenu('FBX测试')
|
||
|
||
# 切换单位转换
|
||
toggle_unit_action = fbx_menu.addAction('切换单位转换 (U)')
|
||
toggle_unit_action.triggered.connect(lambda: toggle_unit_conversion())
|
||
|
||
# 切换缩放标准化
|
||
toggle_scale_action = fbx_menu.addAction('切换缩放标准化 (N)')
|
||
toggle_scale_action.triggered.connect(lambda: toggle_scale_normalization())
|
||
|
||
fbx_menu.addSeparator()
|
||
|
||
# 显示当前设置
|
||
settings_action = fbx_menu.addAction('显示当前设置 (S)')
|
||
settings_action.triggered.connect(lambda: print_current_settings(unit_conversion_enabled, scale_normalization_enabled))
|
||
|
||
# 显示导入信息
|
||
info_action = fbx_menu.addAction('显示导入信息 (I)')
|
||
info_action.triggered.connect(lambda: print_import_info(world))
|
||
|
||
# 射线显示切换
|
||
ray_action = fbx_menu.addAction('切换射线显示 (R)')
|
||
ray_action.triggered.connect(lambda: world.toggleRayDisplay())
|
||
|
||
fbx_menu.addSeparator()
|
||
|
||
# 手动标准化当前模型
|
||
normalize_action = fbx_menu.addAction('手动标准化当前模型缩放')
|
||
normalize_action.triggered.connect(lambda: manual_normalize_current_models(world))
|
||
|
||
# 重置所有模型缩放
|
||
reset_action = fbx_menu.addAction('重置所有模型缩放为1.0')
|
||
reset_action.triggered.connect(lambda: reset_all_model_scales(world))
|
||
|
||
def toggle_unit_conversion():
|
||
nonlocal unit_conversion_enabled
|
||
unit_conversion_enabled = not unit_conversion_enabled
|
||
status = "开启" if unit_conversion_enabled else "关闭"
|
||
print(f"\n=== 单位转换模式已{status} ===")
|
||
|
||
def toggle_scale_normalization():
|
||
nonlocal scale_normalization_enabled
|
||
scale_normalization_enabled = not scale_normalization_enabled
|
||
status = "开启" if scale_normalization_enabled else "关闭"
|
||
print(f"\n=== 缩放标准化模式已{status} ===")
|
||
|
||
|
||
def print_model_structure(model, depth=0, max_depth=5, world=None):
|
||
"""打印模型的层级结构、缩放和位置信息"""
|
||
if depth > max_depth:
|
||
return
|
||
|
||
indent = " " * depth
|
||
scale = model.getScale()
|
||
local_pos = model.getPos()
|
||
|
||
# 如果有world引用,显示世界位置
|
||
if world:
|
||
world_pos = model.getPos(world.render)
|
||
pos_info = f"本地{local_pos} / 世界{world_pos}"
|
||
else:
|
||
pos_info = f"{local_pos}"
|
||
|
||
# 计算最大缩放分量
|
||
max_scale = max(abs(scale.x), abs(scale.y), abs(scale.z))
|
||
scale_status = "🔴大" if max_scale > 10 else "🟢正常"
|
||
|
||
print(f"{indent}📦 {model.getName()}")
|
||
print(f"{indent} 缩放: {scale} {scale_status}")
|
||
print(f"{indent} 位置: {pos_info}")
|
||
print(f"{indent} 类型: {model.node().__class__.__name__}")
|
||
|
||
# 递归打印重要子节点(限制数量避免输出过多)
|
||
child_count = model.getNumChildren()
|
||
max_children_to_show = 3 if depth == 0 else 2
|
||
|
||
for i in range(min(child_count, max_children_to_show)):
|
||
child = model.getChild(i)
|
||
print_model_structure(child, depth + 1, max_depth, world)
|
||
|
||
if child_count > max_children_to_show:
|
||
print(f"{indent} ... 还有 {child_count - max_children_to_show} 个子节点")
|
||
|
||
|
||
def print_import_info(world):
|
||
"""打印当前导入的模型信息"""
|
||
print("\n" + "="*60)
|
||
print("🔍 当前场景中的模型信息")
|
||
print("="*60)
|
||
|
||
if not world.models:
|
||
print("❌ 场景中没有模型")
|
||
return
|
||
|
||
for i, model in enumerate(world.models):
|
||
print(f"\n📦 模型 {i+1}: {model.getName()}")
|
||
print(f" 文件: {model.getTag('file') if model.hasTag('file') else '未知'}")
|
||
print(f" 单位转换: {'✅是' if model.hasTag('unit_conversion_applied') else '❌否'}")
|
||
print(f" 缩放标准化: {'✅是' if model.hasTag('scale_normalization_applied') else '❌否'}")
|
||
print(f" 根节点位置: {model.getPos()}")
|
||
print(f" 根节点缩放: {model.getScale()}")
|
||
print(f" 子节点数量: {model.getNumChildren()}")
|
||
|
||
# 检查子节点的缩放情况
|
||
large_scale_children = 0
|
||
if model.getNumChildren() > 0:
|
||
print(" 📋 子节点缩放概况:")
|
||
for j in range(min(5, model.getNumChildren())): # 只检查前5个子节点
|
||
child = model.getChild(j)
|
||
child_scale = child.getScale()
|
||
max_scale = max(abs(child_scale.x), abs(child_scale.y), abs(child_scale.z))
|
||
|
||
if max_scale > 10:
|
||
large_scale_children += 1
|
||
status = "🔴大缩放"
|
||
else:
|
||
status = "🟢正常"
|
||
|
||
print(f" {child.getName()}: {child_scale} {status}")
|
||
|
||
if model.getNumChildren() > 5:
|
||
print(f" ... 还有 {model.getNumChildren() - 5} 个子节点")
|
||
|
||
if large_scale_children > 0:
|
||
print(f" ⚠️ 发现 {large_scale_children} 个子节点有大缩放值")
|
||
|
||
print("="*60)
|
||
|
||
|
||
def print_current_settings(unit_conversion_enabled, scale_normalization_enabled):
|
||
"""显示当前导入设置"""
|
||
print("\n" + "="*40)
|
||
print("⚙️ 当前FBX导入设置")
|
||
print("="*40)
|
||
print(f"单位转换 (U键): {'🟢开启' if unit_conversion_enabled else '🔴关闭'}")
|
||
print(f"缩放标准化 (N键): {'🟢开启' if scale_normalization_enabled else '🔴关闭'}")
|
||
print("\n说明:")
|
||
print("• 单位转换: 将FBX的厘米单位转换为米")
|
||
print("• 缩放标准化: 自动处理子节点的大缩放值(如100)")
|
||
print("="*40)
|
||
|
||
|
||
def manual_normalize_current_models(world):
|
||
"""手动标准化当前所有模型的缩放"""
|
||
print("\n🔧 手动标准化所有模型缩放...")
|
||
|
||
if not world.models:
|
||
print("❌ 没有模型需要处理")
|
||
return
|
||
|
||
for model in world.models:
|
||
print(f"\n处理模型: {model.getName()}")
|
||
world.scene_manager._normalizeModelScales(model)
|
||
|
||
print("✅ 手动标准化完成")
|
||
|
||
|
||
def reset_all_model_scales(world):
|
||
"""重置所有模型的缩放为1.0(调试用)"""
|
||
print("\n🔄 重置所有模型缩放为1.0...")
|
||
|
||
count = 0
|
||
for model in world.models:
|
||
# 递归重置所有节点
|
||
reset_node_scale_recursive(model)
|
||
count += 1
|
||
|
||
print(f"✅ 已重置 {count} 个模型的所有节点缩放")
|
||
|
||
|
||
def reset_node_scale_recursive(node, depth=0):
|
||
"""递归重置节点缩放"""
|
||
indent = " " * depth
|
||
node.setScale(1.0, 1.0, 1.0)
|
||
print(f"{indent}重置 {node.getName()}")
|
||
|
||
for i in range(node.getNumChildren()):
|
||
child = node.getChild(i)
|
||
reset_node_scale_recursive(child, depth + 1)
|
||
|
||
|
||
def print_usage_instructions():
|
||
"""打印使用说明"""
|
||
print("\n" + "="*60)
|
||
print("🚀 FBX导入测试启动完成!")
|
||
print("="*60)
|
||
print("🎯 主要改进:")
|
||
print("✅ 智能缩放标准化 - 自动处理子节点大缩放值")
|
||
print("✅ 保持模型原有缩放结构")
|
||
print("✅ 避免根节点0.01 + 子节点100的复杂层级")
|
||
print("✅ 缩放时保持世界位置不变 - 修复位置偏移问题")
|
||
print("✅ 提供灵活的导入选项")
|
||
print("")
|
||
print("⌨️ 键盘快捷键:")
|
||
print("• U键 - 切换单位转换模式")
|
||
print("• N键 - 切换缩放标准化模式")
|
||
print("• S键 - 显示当前设置")
|
||
print("• I键 - 显示模型信息")
|
||
print("• R键 - 切换射线显示")
|
||
print("")
|
||
print("📁 导入方式:")
|
||
print("• 拖拽FBX文件到3D场景")
|
||
print("• 使用菜单 [文件] -> [导入模型]")
|
||
print("• 使用菜单 [FBX测试] 查看更多选项")
|
||
print("")
|
||
print("🎛️ 缩放处理模式:")
|
||
print("• 关闭单位转换 + 开启缩放标准化(推荐)")
|
||
print(" → 保持FBX结构,但标准化大缩放值")
|
||
print("• 开启单位转换 + 关闭缩放标准化")
|
||
print(" → 传统方式,应用0.01根缩放")
|
||
print("• 两者都关闭")
|
||
print(" → 完全保持原始FBX结构")
|
||
print("")
|
||
print("📊 当前设置:")
|
||
print("• 单位转换: 🔴关闭")
|
||
print("• 缩放标准化: 🟢开启 (推荐)")
|
||
print("="*60)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
app, main_window, world = setup_fbx_import_demo()
|
||
|
||
# 启动应用程序
|
||
sys.exit(app.exec_())
|
||
|
||
except Exception as e:
|
||
print(f"❌ 启动失败: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc() |