EG/demo/scale_position_test.py
2025-07-10 09:19:51 +08:00

261 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()