261 lines
9.0 KiB
Python
261 lines
9.0 KiB
Python
#!/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() |