是否可以使用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)
}
当我按下保存按钮时,我会收到一个错误。
首先,需要渲染SCNScene才能正确导出。您不能创建一堆节点,将它们填充到场景的根节点中,然后调用write((并获得正确渲染的usdz。它必须首先放在SwiftUISceneView
的屏幕上,这会导致加载所有资产,等等。我想你可以实例化一个SCNRenderer
并在根节点上调用prepare()
,但这会带来一些额外的复杂性。
其次,沙盒阻止直接导出到.fileExporter()
提供的URL。这是因为Scene.write()
分为两个步骤:它首先创建一个.usdc
导出,并将生成的文件压缩为一个单独的.usdz
。中间文件没有.fileExporter()
提供的URL所具有的写入权限(假设您已将沙盒"用户选择的文件"权限设置为"读/写"(,因此即使目标URL是可写的,如果目标目录在沙盒之外,Scene.write()
也会失败。
我的解决方案是编写一个自定义的FileWrapper,如果WriteConfiguration UTType是.usz:,我会返回它
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)
}
2023年1月8日
目前iOS开发者仍然只能导出.usd
、.usda
和.usdc
文件;您可以使用canExportFileExtension(_:(类型的方法进行检查:
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)