簡體   English   中英

如何使用 ModelIO 以編程方式將 3D 網格導出為 USDZ?

[英]How to programmatically export 3D mesh as USDZ using ModelIO?

是否可以使用 ModelIO 和 MetalKit 框架以編程方式將 3D 網格導出為.usdz文件格式?

這是一個代碼:

import ARKit
import RealityKit
import MetalKit
import ModelIO

let asset = MDLAsset(bufferAllocator: allocator)
asset.add(mesh)

let filePath = FileManager.default.urls(for: .documentDirectory, 
                                         in: .userDomainMask).first!
    
let usdz: URL = filePath.appendingPathComponent("model.usdz")

do {
    try asset.export(to: usdz)               
    let controller = UIActivityViewController(activityItems: [usdz], 
                                      applicationActivities: nil)
    controller.popoverPresentationController?.sourceView = sender
    self.present(controller, animated: true, completion: nil)
} catch let error {
    fatalError(error.localizedDescription)
}

當我按下保存按鈕時,出現錯誤。

2021 年 6 月 24 日

目前蘋果開發者可以使用canExportFileExtension(_:)類型方法導出.usd.usda.usdc文件:

let usd = MDLAsset.canExportFileExtension("usd")
let usda = MDLAsset.canExportFileExtension("usda")
let usdc = MDLAsset.canExportFileExtension("usdc")
let usdz = MDLAsset.canExportFileExtension("usdz")
    
print(usd, usda, usdc, usdz)

它打印:

true true true false

但是,您可以使用名為write(to:options:delegate:progressHandler:)實例方法輕松將 SceneKit 的場景導出為.usdz文件。

let path = FileManager.default.urls(for: .documentDirectory,
                                     in: .userDomainMask)[0]
                                         .appendingPathComponent("file.usdz")
    
sceneKitScene.write(to: path, 
               options: nil, 
              delegate: nil, 
       progressHandler: nil)

Andy Jazz 的答案是正確的,但需要修改才能在 SwiftUI 沙盒應用程序中工作:

首先,需要渲染 SCNScene 才能正確導出。 您不能創建一堆節點,將它們塞入場景的根節點並調用 write() 並獲得正確渲染的 usdz。 它必須首先放在 SwiftUI SceneView的屏幕上,這會導致加載所有資產等。我想你可以實例化一個SCNRenderer並在根節點上調用prepare() ,但這有一些額外的復雜性。

其次,沙盒阻止直接導出到.fileExporter()提供的 URL。 這是因為Scene.write()分兩步工作:它首先創建一個.usdc導出,然后將生成的文件壓縮到一個.usdz中。 中間文件沒有.fileExporter()提供的 URL 的寫入權限(假設您已將沙盒“用戶選擇的文件”權限設置為“讀/寫”),因此Scene.write()失敗,即使如果目標 URL 是可寫的,如果目標目錄在沙箱之外。

我的解決方案是編寫自定義 FileWrapper,如果 WriteConfiguration UTType 是 .usdz,我會返回它:

public class USDZExportFileWrapper: FileWrapper {
    var exportScene: SCNScene

    public init(scene: SCNScene) {
        exportScene = scene
        super.init(regularFileWithContents: Data())
    }

    required init?(coder inCoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override public func write(to url: URL,
                               options: FileWrapper.WritingOptions = [],
                               originalContentsURL: URL?) throws {
        let tempFilePath = NSTemporaryDirectory() + UUID().uuidString + ".usdz"
        let tempURL = URL(fileURLWithPath: tempFilePath)
        exportScene.write(to: tempURL, delegate: nil)
        try FileManager.default.moveItem(at: tempURL, to: url)
    }
}

ReferenceFileDocument中的用法:

public func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper {
    if configuration.contentType == .usdz {
        return USDZExportFileWrapper(scene: scene)
    }

    return .init(regularFileWithContents: snapshot)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM