如何使用 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 日。
目前 Apple 开发人员可以使用 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 的写入权限(假设您已将沙盒“用户选择的文件”权限设置为“Read/Write”),因此 Scene.write()
失败,即使目标 URL 是可写的,如果目标目录在沙箱之外。
我的解决方案是编写自定义 FileWrapper,如果 WriteConfiguration UTType 是 .usdz,我 return:
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)
}
是否可以使用 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 日。
目前 Apple 开发人员可以使用 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:)
.
.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 的写入权限(假设您已将沙盒“用户选择的文件”权限设置为“Read/Write”),因此 Scene.write()
失败,即使目标 URL 是可写的,如果目标目录在沙箱之外。
我的解决方案是编写自定义 FileWrapper,如果 WriteConfiguration UTType 是 .usdz,我 return:
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)
}