#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 缩放位置测试 - 验证缩放标准化时位置是否正确保持 测试内容: 1. 创建模拟FBX层级结构 2. 验证缩放标准化前后的世界位置 3. 对比修复前后的效果 控制说明: - I键:显示当前模型信息 - N键:切换缩放标准化 - R键:重置模型 - T键:运行位置测试 - Q键:退出 """ import sys import os sys.path.append('..') # 添加父目录到路径 from direct.showbase.ShowBase import ShowBase from panda3d.core import (CardMaker, Vec3, Point3, Material, ModelRoot, PandaNode, LineSegs, ColorAttrib, RenderState, DepthTestAttrib) from PyQt5.QtWidgets import QApplication def create_test_fbx_structure(world): """创建模拟FBX结构用于测试""" print("\n=== 创建模拟FBX结构 ===") # 创建根节点 root = world.render.attachNewNode(ModelRoot("test_fbx_model")) root.setScale(1.0) # 根节点缩放为1 root.setPos(10, 5, 2) # 给根节点一个偏移位置 # 创建子节点1 - 模拟有大缩放值的子节点 child1_node = PandaNode("child1") child1 = root.attachNewNode(child1_node) child1.setScale(100.0) # 大缩放值 child1.setPos(5, 0, 0) # 相对于父节点的位置 # 创建子节点2 - 另一个有大缩放值的子节点 child2_node = PandaNode("child2") child2 = root.attachNewNode(child2_node) child2.setScale(100.0) # 大缩放值 child2.setPos(-3, 8, 1) # 相对于父节点的位置 # 创建孙子节点 - 嵌套结构 grandchild_node = PandaNode("grandchild") grandchild = child1.attachNewNode(grandchild_node) grandchild.setScale(100.0) # 大缩放值 grandchild.setPos(2, 2, 1) # 相对于父节点的位置 # 创建可视化几何体 def create_marker(node, color): """为节点创建可视化标记""" cm = CardMaker(f'marker_{node.getName()}') cm.setFrame(-0.5, 0.5, -0.5, 0.5) marker = node.attachNewNode(cm.generate()) marker.setColor(*color) marker.setBillboardAxis() # 始终面向相机 return marker # 为各节点创建可视化标记 create_marker(root, (1, 0, 0, 1)) # 红色 - 根节点 create_marker(child1, (0, 1, 0, 1)) # 绿色 - 子节点1 create_marker(child2, (0, 0, 1, 1)) # 蓝色 - 子节点2 create_marker(grandchild, (1, 1, 0, 1)) # 黄色 - 孙子节点 # 添加到模型列表 world.scene_manager.models.append(root) print("✓ 模拟FBX结构创建完成") return root def print_position_info(node, label, depth=0): """打印节点的位置和缩放信息""" indent = " " * depth local_pos = node.getPos() world_pos = node.getPos(node.getTopParent()) scale = node.getScale() print(f"{indent}{label}:") print(f"{indent} 本地位置: {local_pos}") print(f"{indent} 世界位置: {world_pos}") print(f"{indent} 缩放: {scale}") def run_position_test(world, model): """运行位置测试""" print("\n=== 位置测试开始 ===") # 收集所有节点 nodes = [] def collect_nodes(node): nodes.append(node) for i in range(node.getNumChildren()): collect_nodes(node.getChild(i)) collect_nodes(model) # 记录标准化前的位置 print("\n--- 标准化前的位置信息 ---") before_positions = {} for i, node in enumerate(nodes): label = f"节点{i+1}({node.getName()})" print_position_info(node, label) before_positions[node.getName()] = { 'local': node.getPos(), 'world': node.getPos(world.render), 'scale': node.getScale() } # 应用缩放标准化 print("\n--- 应用缩放标准化 ---") world.scene_manager._normalizeModelScales(model) # 记录标准化后的位置 print("\n--- 标准化后的位置信息 ---") after_positions = {} for i, node in enumerate(nodes): label = f"节点{i+1}({node.getName()})" print_position_info(node, label) after_positions[node.getName()] = { 'local': node.getPos(), 'world': node.getPos(world.render), 'scale': node.getScale() } # 分析位置和缩放变化 print("\n--- 位置和缩放变化分析 ---") for name in before_positions: before = before_positions[name] after = after_positions[name] scale_change = after['scale'] - before['scale'] local_pos_change = after['local'] - before['local'] world_pos_change = after['world'] - before['world'] local_pos_distance = local_pos_change.length() world_pos_distance = world_pos_change.length() print(f"\n{name}:") print(f" 缩放变化: {before['scale']} -> {after['scale']}") print(f" 本地位置变化: {before['local']} -> {after['local']}") print(f" 本地位置变化距离: {local_pos_distance:.6f}") print(f" 世界位置变化距离: {world_pos_distance:.6f}") # 检查是否按比例缩放 if before['scale'].x > 10: # 如果原来有大缩放 expected_scale_factor = 0.01 # 期望的缩放因子 actual_scale_factor = after['scale'].x / before['scale'].x if before['scale'].x != 0 else 0 expected_pos = before['local'] * expected_scale_factor pos_error = (after['local'] - expected_pos).length() print(f" 期望缩放因子: {expected_scale_factor}") print(f" 实际缩放因子: {actual_scale_factor:.6f}") print(f" 位置缩放误差: {pos_error:.6f}") if abs(actual_scale_factor - expected_scale_factor) < 0.001 and pos_error < 0.01: print(f" ✓ 缩放和位置标准化正确") else: print(f" ⚠ 缩放或位置标准化可能有问题") else: print(f" ℹ 未标准化(缩放值正常)") print("\n=== 位置测试完成 ===") class ScalePositionTest(ShowBase): def __init__(self): ShowBase.__init__(self) # 导入我们的模块 from main import MyWorld # 创建世界实例 self.world = MyWorld() # 设置相机 self.cam.setPos(0, -30, 10) self.cam.lookAt(0, 0, 0) # 创建测试模型 self.test_model = create_test_fbx_structure(self.world) # 设置键盘事件 self.setupKeyEvents() print("\n=== 缩放位置测试程序启动 ===") print("控制说明:") print("I键:显示当前模型信息") print("N键:应用缩放标准化") print("R键:重置模型") print("T键:运行完整位置测试") print("Q键:退出程序") print("================================") def setupKeyEvents(self): """设置键盘事件""" self.accept('i', self.showModelInfo) self.accept('n', self.applyNormalization) self.accept('r', self.resetModel) self.accept('t', self.runPositionTest) self.accept('q', self.quit) self.accept('escape', self.quit) def showModelInfo(self): """显示模型信息""" print("\n=== 当前模型信息 ===") if self.test_model: def show_node_info(node, depth=0): indent = " " * depth print(f"{indent}节点: {node.getName()}") print(f"{indent} 本地位置: {node.getPos()}") print(f"{indent} 世界位置: {node.getPos(self.world.render)}") print(f"{indent} 缩放: {node.getScale()}") for i in range(node.getNumChildren()): child = node.getChild(i) show_node_info(child, depth + 1) show_node_info(self.test_model) print("==================") def applyNormalization(self): """应用缩放标准化""" print("\n=== 应用缩放标准化 ===") if self.test_model: self.world.scene_manager._normalizeModelScales(self.test_model) print("✓ 缩放标准化完成") else: print("× 没有找到测试模型") def resetModel(self): """重置模型""" print("\n=== 重置模型 ===") if self.test_model: self.test_model.removeNode() # 重新创建 self.test_model = create_test_fbx_structure(self.world) print("✓ 模型重置完成") def runPositionTest(self): """运行完整位置测试""" if self.test_model: run_position_test(self.world, self.test_model) else: print("× 没有找到测试模型") def quit(self): """退出程序""" print("\n退出缩放位置测试程序") sys.exit() if __name__ == "__main__": app = QApplication(sys.argv) test = ScalePositionTest() test.run()