动画播放bug修正,fbx模型自动转换glb格式
This commit is contained in:
parent
6a7e6e5657
commit
ee0fe2741d
@ -135,9 +135,9 @@ class Panda3DWorld(ShowBase):
|
||||
#render_pipeline.set_camera(self.cam)
|
||||
|
||||
#添加渲染效果<E69588><E69E9C>
|
||||
self.cam = self.render_pipeline._showbase.cam
|
||||
self.camNode = self.cam.node()
|
||||
self.camLens = self.camNode.get_lens()
|
||||
#self.cam = self.render_pipeline._showbase.cam
|
||||
#self.camNode = self.cam.node()
|
||||
#self.camLens = self.camNode.get_lens()
|
||||
self.render_pipeline._showbase.camera = self.render_pipeline._showbase.cam
|
||||
#self.render_pipeline.daytime_mgr.update()
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
26
RenderPipelineFile/effects/simple_transparent.yaml
Normal file
26
RenderPipelineFile/effects/simple_transparent.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
# simple_transparent.yaml
|
||||
vertex_shader: |
|
||||
#version 330
|
||||
uniform mat4 p3d_ModelViewProjectionMatrix;
|
||||
in vec4 p3d_Vertex;
|
||||
in vec2 p3d_MultiTexCoord0;
|
||||
out vec2 texcoord;
|
||||
void main() {
|
||||
gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
|
||||
texcoord = p3d_MultiTexCoord0;
|
||||
}
|
||||
|
||||
fragment_shader: |
|
||||
#version 330
|
||||
uniform sampler2D p3d_Texture0;
|
||||
uniform float material_opacity = 1.0;
|
||||
in vec2 texcoord;
|
||||
out vec4 o_color;
|
||||
void main() {
|
||||
vec4 c = texture(p3d_Texture0, texcoord);
|
||||
o_color = vec4(c.rgb, c.a * material_opacity);
|
||||
}
|
||||
|
||||
render_states:
|
||||
TransparencyAttrib: M_alpha
|
||||
DepthWriteAttrib: 0
|
||||
@ -398,7 +398,6 @@ class SelectionSystem:
|
||||
material.setRoughness(1.0)
|
||||
material.setMetallic(0.0)
|
||||
axis_nodes[axis].setMaterial(material)
|
||||
print("自发光材质设置完成")
|
||||
except Exception as e:
|
||||
print(f"自发光材质设置失败: {str(e)}")
|
||||
|
||||
@ -419,8 +418,6 @@ class SelectionSystem:
|
||||
axis_node.setDepthWrite(False)
|
||||
axis_node.setLightOff()
|
||||
|
||||
print("✓ 坐标轴渲染设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"设置坐标轴渲染失败!!!!!!: {str(e)}")
|
||||
|
||||
@ -440,10 +437,8 @@ class SelectionSystem:
|
||||
# 第三步:强制设置每个轴的独立渲染
|
||||
self._forceAxisIndependentRendering()
|
||||
|
||||
print("✅ 激进 RenderPipeline 兼容坐标轴设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 激进设置失败: {e}")
|
||||
# 使用最后的备用方案
|
||||
self._setupUltimateGizmoFallback()
|
||||
|
||||
@ -488,8 +483,6 @@ class SelectionSystem:
|
||||
axis_node.setTag("unlit","1")
|
||||
axis_node.setColorScale(2.0,2.0,2.0,1.0)
|
||||
|
||||
print(" ✓ 坐标轴完全隔离")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 隔离失败: {e}")
|
||||
|
||||
@ -514,8 +507,6 @@ class SelectionSystem:
|
||||
)
|
||||
self.gizmo.setState(minimal_state, 10000)
|
||||
|
||||
print(" ✓ 最简渲染设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 最简渲染设置失败: {e}")
|
||||
|
||||
@ -534,7 +525,6 @@ class SelectionSystem:
|
||||
# 每个轴都完全独立设置
|
||||
self._setupSingleAxisRendering(axis_node, name, color, 0)
|
||||
|
||||
print(" ✓ 独立轴渲染设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 独立轴渲染设置失败: {e}")
|
||||
@ -575,8 +565,6 @@ class SelectionSystem:
|
||||
def _setupUltimateGizmoFallback(self):
|
||||
"""最后的备用方案 - 最简单的渲染"""
|
||||
try:
|
||||
print("🚨 使用最后备用方案...")
|
||||
|
||||
# 最简单的设置
|
||||
self.gizmo.setLightOff()
|
||||
self.gizmo.setFogOff()
|
||||
@ -603,8 +591,6 @@ class SelectionSystem:
|
||||
self.gizmoZAxis.setBin("gui-popup", 20003)
|
||||
self.gizmoZAxis.setDepthTest(False)
|
||||
|
||||
print("✅ 最后备用方案设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 最后备用方案也失败: {e}")
|
||||
|
||||
@ -672,7 +658,6 @@ class SelectionSystem:
|
||||
self.gizmoTargetStartPos = None
|
||||
self.gizmoStartPos = None
|
||||
|
||||
print("清除了坐标轴")
|
||||
|
||||
def setGizmoAxisColor(self, axis, color):
|
||||
"""设置坐标轴颜色 - RenderPipeline 兼容版本"""
|
||||
@ -772,7 +757,6 @@ class SelectionSystem:
|
||||
axis_node.setTag("gizmo_axis", axis_name)
|
||||
axis_node.setTag("pickable", "1")
|
||||
|
||||
print("✓ 坐标轴鼠标事件设置完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"设置坐标轴鼠标事件失败: {e}")
|
||||
@ -782,21 +766,15 @@ class SelectionSystem:
|
||||
def checkGizmoClick(self, mouseX, mouseY):
|
||||
"""使用屏幕空间检测是否点击了坐标轴"""
|
||||
if not self.gizmo or not self.gizmoTarget:
|
||||
print("坐标轴点击检测:坐标轴或目标不存在")
|
||||
return None
|
||||
|
||||
# 基本参数验证
|
||||
if not isinstance(mouseX, (int, float)) or not isinstance(mouseY, (int, float)):
|
||||
print(f"坐标轴点击检测:无效的鼠标坐标 ({mouseX}, {mouseY})")
|
||||
return None
|
||||
|
||||
try:
|
||||
print(f"\n=== 坐标轴点击检测 ===")
|
||||
print(f"鼠标位置: ({mouseX}, {mouseY})")
|
||||
|
||||
# 获取坐标轴中心的世界坐标
|
||||
gizmo_world_pos = self.gizmo.getPos(self.world.render)
|
||||
print(f"坐标轴世界位置: {gizmo_world_pos}")
|
||||
|
||||
# 计算各轴端点的世界坐标
|
||||
x_end = gizmo_world_pos + Vec3(self.axis_length, 0, 0)
|
||||
@ -833,7 +811,6 @@ class SelectionSystem:
|
||||
|
||||
# 如果无法获得屏幕坐标,使用备用方法
|
||||
if not center_screen:
|
||||
print("使用备用检测方法...")
|
||||
return self.checkGizmoClickFallback(mouseX, mouseY)
|
||||
|
||||
# 计算点击阈值
|
||||
@ -872,7 +849,6 @@ class SelectionSystem:
|
||||
print(f"✓ 点击了{axis_label}")
|
||||
return axis_name
|
||||
|
||||
print("× 没有点击任何轴")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
@ -883,7 +859,6 @@ class SelectionSystem:
|
||||
|
||||
def checkGizmoClickFallback(self, mouseX, mouseY):
|
||||
"""备用检测方法:使用固定的屏幕区域"""
|
||||
print("使用备用点击检测...")
|
||||
|
||||
# 获取准确的窗口尺寸
|
||||
win_width, win_height = self.world.getWindowSize()
|
||||
|
||||
@ -37,27 +37,29 @@ class CoreWorld(Panda3DWorld):
|
||||
self._setupLighting()
|
||||
self._setupGround()
|
||||
self._loadFont()
|
||||
#self.load_and_play_fbx_model()
|
||||
#self.load_and_play_glb_model()
|
||||
|
||||
def load_and_play_fbx_model(self):
|
||||
def load_and_play_glb_model(self):
|
||||
"""加载 glTF 模型并播放动画"""
|
||||
try:
|
||||
from direct.actor.Actor import Actor
|
||||
|
||||
# 使用 Actor 类加载 glTF 模型
|
||||
self.model = Actor("/home/tiger/Women.glb")
|
||||
self.model = Actor("/home/tiger/cube.glb")
|
||||
print("模型加载成功!")
|
||||
self.model.reparentTo(self.render)
|
||||
self.model.setPos(0, 10, 0)
|
||||
self.model.setScale(10)
|
||||
|
||||
# 列出所有可用动画
|
||||
anims = self.model.getAnimNames()
|
||||
print("可用动画:", anims)
|
||||
|
||||
# 播放特定动画
|
||||
if anims:
|
||||
self.model.loop('Armature|mixamo.com|Layer0')
|
||||
# 找出所有 AnimBundleNode
|
||||
print(f"开始寻找动画AnimationBundleNode...")
|
||||
for np in self.model.findAllMatches("**/+AnimBundleNode"):
|
||||
print(f"找到AnimBundleNode: {np.getName()}")
|
||||
bundle = np.node().getBundle()
|
||||
for i in range(bundle.getNumAnimations()):
|
||||
anim_name = bundle.getAnimation(i).getName()
|
||||
print("动画名:", anim_name)
|
||||
# 这里不能直接 play,需要手动把 AnimControl 绑定到节点
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
38
install_fbx2gltf.sh
Executable file
38
install_fbx2gltf.sh
Executable file
@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# FBX2glTF 安装脚本
|
||||
echo "开始安装 FBX2glTF..."
|
||||
|
||||
# 创建工具目录
|
||||
mkdir -p ~/tools
|
||||
|
||||
# 方案1: 尝试下载 Godot 维护的版本
|
||||
echo "尝试下载 Godot 维护的 FBX2glTF..."
|
||||
if wget -O ~/tools/FBX2glTF https://github.com/godotengine/FBX2glTF/releases/download/v0.13.1/FBX2glTF-linux-x64 2>/dev/null; then
|
||||
chmod +x ~/tools/FBX2glTF
|
||||
echo "✓ FBX2glTF 下载成功"
|
||||
else
|
||||
echo "× Godot版本下载失败,尝试原版..."
|
||||
|
||||
# 方案2: 尝试下载 Facebook 原版
|
||||
if wget -O ~/tools/FBX2glTF https://github.com/facebookincubator/FBX2glTF/releases/download/v0.9.7/FBX2glTF-linux-x64 2>/dev/null; then
|
||||
chmod +x ~/tools/FBX2glTF
|
||||
echo "✓ FBX2glTF 原版下载成功"
|
||||
else
|
||||
echo "× 下载失败,请手动安装"
|
||||
echo " 1. 访问: https://github.com/godotengine/FBX2glTF/releases"
|
||||
echo " 2. 下载 FBX2glTF-linux-x64"
|
||||
echo " 3. 移动到 ~/tools/FBX2glTF"
|
||||
echo " 4. 运行: chmod +x ~/tools/FBX2glTF"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 添加到 PATH
|
||||
if ! grep -q "~/tools" ~/.bashrc; then
|
||||
echo 'export PATH="$HOME/tools:$PATH"' >> ~/.bashrc
|
||||
echo "✓ 已添加到 PATH"
|
||||
fi
|
||||
|
||||
echo "✓ FBX2glTF 安装完成"
|
||||
echo "请运行: source ~/.bashrc 或重启终端"
|
||||
7
main.py
7
main.py
@ -370,13 +370,16 @@ class MyWorld(CoreWorld):
|
||||
"""更新属性面板显示 - 代理到property_panel"""
|
||||
return self.property_panel.updatePropertyPanel(item)
|
||||
|
||||
def addAnimationPanel(self,originmodel,filepath):
|
||||
return self.property_panel._addAnimationPanel(originmodel,filepath)
|
||||
# def addAnimationPanel(self,originmodel,filepath):
|
||||
# return self.property_panel._addAnimationPanel(originmodel,filepath)
|
||||
|
||||
def updateGUIPropertyPanel(self, gui_element):
|
||||
"""更新GUI元素属性面板 - 代理到property_panel"""
|
||||
return self.property_panel.updateGUIPropertyPanel(gui_element)
|
||||
|
||||
def removeActorForModel(self,model):
|
||||
return self.property_panel.removeActorForModel( model)
|
||||
|
||||
# ==================== 工具管理代理 ====================
|
||||
|
||||
def setCurrentTool(self, tool):
|
||||
|
||||
@ -38,19 +38,40 @@ class SceneManager:
|
||||
|
||||
# ==================== 模型导入和处理 ====================
|
||||
|
||||
def importModel(self, filepath, apply_unit_conversion=False, normalize_scales=True):
|
||||
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 '关闭'}")
|
||||
|
||||
filepath = util.normalize_model_path(filepath)
|
||||
original_filepath = filepath
|
||||
|
||||
# 检查是否需要转换为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
|
||||
# 显示成功消息
|
||||
try:
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
original_ext = os.path.splitext(original_filepath)[1].upper()
|
||||
QMessageBox.information(None, "转换成功",
|
||||
f"已将 {original_ext} 格式自动转换为 GLB 格式\n以获得更好的动画支持!")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print(f"⚠️ 转换失败,使用原始文件")
|
||||
|
||||
# 总是重新加载模型以确保材质信息完整
|
||||
# 不使用ModelPool缓存,避免材质信息丢失问题
|
||||
@ -66,6 +87,12 @@ class SceneManager:
|
||||
|
||||
# 将模型添加到场景
|
||||
model.reparentTo(self.world.render)
|
||||
# 保存原始路径和转换后的路径
|
||||
model.setTag("model_path", filepath)
|
||||
model.setTag("original_path", original_filepath)
|
||||
if filepath != original_filepath:
|
||||
model.setTag("converted_from", os.path.splitext(original_filepath)[1])
|
||||
model.setTag("converted_to_glb", "true")
|
||||
|
||||
# 可选的单位转换(主要针对FBX)
|
||||
if apply_unit_conversion and filepath.lower().endswith('.fbx'):
|
||||
@ -952,4 +979,265 @@ class SceneManager:
|
||||
self.world.updateSceneTree()
|
||||
|
||||
return light,light_np
|
||||
|
||||
# ==================== GLB 转换方法 ====================
|
||||
|
||||
def _shouldConvertToGLB(self, filepath):
|
||||
"""判断是否应该转换为GLB格式"""
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
# 需要转换的格式:FBX, OBJ, DAE等(但不转换已经是GLB/GLTF的)
|
||||
convert_formats = ['.fbx', '.obj', '.dae', '.3ds', '.blend']
|
||||
return ext in convert_formats
|
||||
|
||||
def _convertToGLBWithProgress(self, filepath):
|
||||
"""带进度显示的GLB转换"""
|
||||
try:
|
||||
from PyQt5.QtWidgets import QProgressDialog, QApplication
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 创建进度对话框
|
||||
progress = QProgressDialog("正在转换模型格式以获得更好的动画支持...", "取消", 0, 100)
|
||||
progress.setWindowTitle("模型格式转换")
|
||||
progress.setWindowModality(Qt.WindowModal)
|
||||
progress.show()
|
||||
QApplication.processEvents()
|
||||
|
||||
try:
|
||||
result = self._convertToGLB(filepath, progress)
|
||||
progress.hide()
|
||||
return result
|
||||
except Exception as e:
|
||||
progress.hide()
|
||||
print(f"转换过程出错: {e}")
|
||||
return None
|
||||
|
||||
except ImportError:
|
||||
# 如果没有 PyQt5,直接转换
|
||||
return self._convertToGLB(filepath)
|
||||
|
||||
def _convertToGLB(self, filepath, progress=None):
|
||||
"""将模型文件转换为GLB格式"""
|
||||
try:
|
||||
print(f"[GLB转换] 开始转换: {filepath}")
|
||||
|
||||
if progress:
|
||||
progress.setValue(10)
|
||||
progress.setLabelText("准备转换...")
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
QApplication.processEvents()
|
||||
|
||||
# 准备输出路径
|
||||
base_name = os.path.splitext(os.path.basename(filepath))[0]
|
||||
output_dir = os.path.dirname(filepath)
|
||||
glb_path = os.path.join(output_dir, f"{base_name}_auto_converted.glb")
|
||||
|
||||
# 如果已经存在转换后的文件,直接使用
|
||||
if os.path.exists(glb_path):
|
||||
# 检查文件时间,如果原文件更新则重新转换
|
||||
original_time = os.path.getmtime(filepath)
|
||||
converted_time = os.path.getmtime(glb_path)
|
||||
if converted_time > original_time:
|
||||
print(f"[GLB转换] 使用现有转换文件: {glb_path}")
|
||||
if progress:
|
||||
progress.setValue(100)
|
||||
return glb_path
|
||||
|
||||
if progress:
|
||||
progress.setValue(20)
|
||||
progress.setLabelText("尝试 Blender 转换...")
|
||||
QApplication.processEvents()
|
||||
|
||||
# 方法1: 使用 Blender 进行转换
|
||||
if self._convertWithBlender(filepath, glb_path, progress):
|
||||
return glb_path
|
||||
|
||||
if progress:
|
||||
progress.setValue(60)
|
||||
progress.setLabelText("尝试 FBX2glTF 转换...")
|
||||
QApplication.processEvents()
|
||||
|
||||
# 方法2: 使用 FBX2glTF (如果可用)
|
||||
if self._convertWithFBX2glTF(filepath, glb_path, progress):
|
||||
return glb_path
|
||||
|
||||
if progress:
|
||||
progress.setValue(80)
|
||||
progress.setLabelText("尝试 Assimp 转换...")
|
||||
QApplication.processEvents()
|
||||
|
||||
# 方法3: 使用 Assimp
|
||||
if self._convertWithAssimp(filepath, glb_path, progress):
|
||||
return glb_path
|
||||
|
||||
print(f"[GLB转换] 所有转换方法都失败")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"[GLB转换] 转换过程出错: {e}")
|
||||
return None
|
||||
|
||||
def _convertWithBlender(self, input_path, output_path, progress=None):
|
||||
"""使用 Blender 进行转换"""
|
||||
try:
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
print(f"[Blender转换] {input_path} → {output_path}")
|
||||
|
||||
# 创建 Blender 脚本
|
||||
script_content = f'''
|
||||
import bpy
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 清理默认场景
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
|
||||
print("开始导入文件...")
|
||||
|
||||
# 根据文件类型选择导入方法
|
||||
input_file = "{input_path}"
|
||||
output_file = "{output_path}"
|
||||
|
||||
try:
|
||||
ext = os.path.splitext(input_file)[1].lower()
|
||||
|
||||
if ext == '.fbx':
|
||||
bpy.ops.import_scene.fbx(filepath=input_file)
|
||||
elif ext == '.obj':
|
||||
bpy.ops.import_scene.obj(filepath=input_file)
|
||||
elif ext == '.dae':
|
||||
bpy.ops.wm.collada_import(filepath=input_file)
|
||||
elif ext == '.blend':
|
||||
bpy.ops.wm.open_mainfile(filepath=input_file)
|
||||
else:
|
||||
print(f"不支持的格式: {{ext}}")
|
||||
sys.exit(1)
|
||||
|
||||
print("导入成功,开始导出GLB...")
|
||||
|
||||
# 导出为 GLB,保留动画
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=output_file,
|
||||
export_format='GLB',
|
||||
export_animations=True,
|
||||
export_force_sampling=True,
|
||||
export_frame_range=True,
|
||||
export_current_frame=False,
|
||||
export_skins=True,
|
||||
export_morph=True,
|
||||
export_lights=True,
|
||||
export_cameras=False
|
||||
)
|
||||
|
||||
print("GLB导出成功!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"转换失败: {{e}}")
|
||||
sys.exit(1)
|
||||
'''
|
||||
|
||||
# 写入临时脚本文件
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as temp_file:
|
||||
temp_file.write(script_content)
|
||||
script_path = temp_file.name
|
||||
|
||||
try:
|
||||
# 执行 Blender 转换
|
||||
result = subprocess.run([
|
||||
'blender', '--background', '--python', script_path
|
||||
], capture_output=True, text=True, timeout=180)
|
||||
|
||||
# 清理临时文件
|
||||
os.unlink(script_path)
|
||||
|
||||
if result.returncode == 0 and os.path.exists(output_path):
|
||||
print(f"[Blender转换] 转换成功")
|
||||
return True
|
||||
else:
|
||||
print(f"[Blender转换] 转换失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"[Blender转换] 转换超时")
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
print(f"[Blender转换] Blender 未安装")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Blender转换] 转换过程出错: {e}")
|
||||
return False
|
||||
|
||||
def _convertWithFBX2glTF(self, input_path, output_path, progress=None):
|
||||
"""使用 FBX2glTF 进行转换(仅支持FBX)"""
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
if not input_path.lower().endswith('.fbx'):
|
||||
return False
|
||||
|
||||
print(f"[FBX2glTF转换] {input_path} → {output_path}")
|
||||
|
||||
# 使用 FBX2glTF 转换
|
||||
result = subprocess.run([
|
||||
'FBX2glTF', input_path, '--output', output_path, '--binary'
|
||||
], capture_output=True, text=True, timeout=120)
|
||||
|
||||
if result.returncode == 0 and os.path.exists(output_path):
|
||||
print(f"[FBX2glTF转换] 转换成功")
|
||||
return True
|
||||
else:
|
||||
print(f"[FBX2glTF转换] 转换失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"[FBX2glTF转换] 转换超时")
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
print(f"[FBX2glTF转换] FBX2glTF 未安装")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"[FBX2glTF转换] 转换过程出错: {e}")
|
||||
return False
|
||||
|
||||
def _convertWithAssimp(self, input_path, output_path, progress=None):
|
||||
"""使用 PyAssimp 进行转换"""
|
||||
try:
|
||||
import pyassimp
|
||||
|
||||
print(f"[PyAssimp转换] {input_path} → {output_path}")
|
||||
|
||||
# 加载模型
|
||||
scene = pyassimp.load(input_path)
|
||||
if not scene:
|
||||
print(f"[PyAssimp转换] 加载模型失败")
|
||||
return False
|
||||
|
||||
if progress:
|
||||
progress.setValue(30)
|
||||
|
||||
# 导出为GLB格式
|
||||
pyassimp.export(scene, output_path, "glb2")
|
||||
|
||||
if progress:
|
||||
progress.setValue(80)
|
||||
|
||||
# 释放资源
|
||||
pyassimp.release(scene)
|
||||
|
||||
if os.path.exists(output_path):
|
||||
print(f"[PyAssimp转换] 转换成功")
|
||||
return True
|
||||
else:
|
||||
print(f"[PyAssimp转换] 转换失败: 输出文件未生成")
|
||||
return False
|
||||
|
||||
except ImportError:
|
||||
print(f"[PyAssimp转换] PyAssimp 未安装")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"[PyAssimp转换] 转换过程出错: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@ -91,6 +91,7 @@ class InterfaceManager:
|
||||
"""删除节点"""
|
||||
try:
|
||||
# 从场景中移除
|
||||
self.world.property_panel.removeActorForModel(nodePath)
|
||||
nodePath.removeNode()
|
||||
|
||||
# 如果是模型根节点,从模型列表中移除
|
||||
|
||||
1462
ui/property_panel.py
1462
ui/property_panel.py
File diff suppressed because it is too large
Load Diff
@ -150,7 +150,7 @@ class CustomPanda3DWidget(QPanda3DWidget):
|
||||
filepath = url.toLocalFile()
|
||||
if filepath.lower().endswith(('.egg', '.bam', '.obj', '.fbx', '.gltf', '.glb')):
|
||||
self.world.importModel(filepath)
|
||||
self.world.addAnimationPanel(None,filepath)
|
||||
#self.world.addAnimationPanel(None,filepath)
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user