You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中RoomPlan API生成USDZ转FBX失败,请求代码排查

USDZ转FBX失败排查(OBJ转换正常)

我通过RoomPlan API生成了USDZ文件,需在Unity中使用,但Unity不支持USDZ格式,因此需要将其转换为FBX与OBJ格式。目前OBJ转换正常,但FBX转换失败,目标路径为空,烦请帮忙排查以下代码:

let inputFileName = "Rom2.usdz"//"plantpot.usdz"
let outputFileName = "Rom3.obj"//"Rom3.fbx"

override func viewDidLoad() {
    super.viewDidLoad()
    
    let fm = FileManager.default
    let docsurl = try! fm.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    debugPrint(docsurl)
    
    guard let usdzURL = Bundle.main.url(forResource: "plantpot", withExtension: "usdz") else {
        fatalError("Failed to find USDZ file in bundle")
    }
    
    // Load the USDZ file using SceneKit
    guard let scene = try? SCNScene(url: usdzURL, options: [.checkConsistency: true]) else {
        print("Failed to load scene")
        return
    }
    
    // Create a Model I/O asset from the SceneKit scene
    let asset = MDLAsset(scnScene: scene)
    debugPrint("Count",asset.count)
    
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let fbxURL = documentsDirectory.appendingPathComponent(outputFileName)

    if FileManager.default.fileExists(atPath: fbxURL.path) {
        print("Output file already exists")
        return
    }
    
    if !FileManager.default.isWritableFile(atPath: fbxURL.path) {
        print("The file at \(fbxURL.path) exists but is not writable.")
    } else {
        print("The file at \(fbxURL.path) exists and is writable.")
    }

    do {
        try asset.export(to: fbxURL)
        print("Conversion complete. Output file: \(fbxURL.path)")
    } catch let error {
        print("Error exporting to FBX: \(error.localizedDescription)")
    }

    print("Conversion complete. Output file: \(fbxURL.path)")
}

问题排查与修复点

1. 输出文件名配置错误

当前代码中outputFileName设置为"Rom3.obj",注释掉了FBX格式的文件名。要转换FBX,必须将outputFileName改为"Rom3.fbx",否则代码会生成OBJ文件而非FBX,导致后续逻辑错误。

2. 输入文件未匹配目标USDZ

代码硬编码加载了"plantpot.usdz",但你实际需要转换的是"Rom2.usdz"。需修改资源加载逻辑,使用inputFileName对应的文件:

// 拆分文件名和扩展名
let inputName = inputFileName.components(separatedBy: ".").first!
let inputExt = inputFileName.components(separatedBy: ".").last!
guard let usdzURL = Bundle.main.url(forResource: inputName, withExtension: inputExt) else {
    fatalError("Failed to find USDZ file in bundle")
}

3. 文件可写性判断逻辑无效

当目标FBX文件不存在时,isWritableFile(atPath:)会返回false(因为文件尚未创建),这个判断没有实际意义,反而会输出错误提示。建议移除该判断,改为检查文档目录的可写性:

guard documentsDirectory.hasDirectoryPath, FileManager.default.isWritableFile(atPath: documentsDirectory.path) else {
    print("Documents directory is not writable")
    return
}

4. MDLAsset导出FBX的格式选项

MDLAsset导出FBX时,可指定导出选项确保层级结构和数据完整性,避免因格式兼容问题导致导出失败:

let exportOptions: [MDLAssetExportOption: Any] = [
    .preserveTopLevelHierarchy: true,
    .exportTextures: true
]
try asset.export(to: fbxURL, options: exportOptions)

修正后的完整代码

let inputFileName = "Rom2.usdz"
let outputFileName = "Rom3.fbx"

override func viewDidLoad() {
    super.viewDidLoad()
    
    let fm = FileManager.default
    let docsurl = try! fm.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    debugPrint(docsurl)
    
    // 正确加载目标USDZ文件
    let inputName = inputFileName.components(separatedBy: ".").first!
    let inputExt = inputFileName.components(separatedBy: ".").last!
    guard let usdzURL = Bundle.main.url(forResource: inputName, withExtension: inputExt) else {
        fatalError("Failed to find USDZ file in bundle")
    }
    
    // Load the USDZ file using SceneKit
    guard let scene = try? SCNScene(url: usdzURL, options: [.checkConsistency: true]) else {
        print("Failed to load scene")
        return
    }
    
    // Create a Model I/O asset from the SceneKit scene
    let asset = MDLAsset(scnScene: scene)
    debugPrint("Asset count: \(asset.count)")
    
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let outputURL = documentsDirectory.appendingPathComponent(outputFileName)

    // 检查目录可写性
    guard documentsDirectory.hasDirectoryPath, FileManager.default.isWritableFile(atPath: documentsDirectory.path) else {
        print("Documents directory is not writable")
        return
    }
    
    // 检查文件是否已存在
    if FileManager.default.fileExists(atPath: outputURL.path) {
        print("Output file already exists: \(outputURL.path)")
        return
    }

    do {
        // 指定FBX导出选项
        let exportOptions: [MDLAssetExportOption: Any] = [
            .preserveTopLevelHierarchy: true,
            .exportTextures: true
        ]
        try asset.export(to: outputURL, options: exportOptions)
        print("Conversion complete. Output file: \(outputURL.path)")
    } catch let error {
        print("Error exporting to FBX: \(error.localizedDescription)")
        print("Full error: \(error)") // 打印完整错误信息便于排查
    }
}

内容的提问来源于stack exchange,提问作者Nishant Chandwani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 20:25:16