如何从 .usdz 文件创建一个 SCNNode?

11
我已经下载了苹果提供的.usdz模型:https://developer.apple.com/arkit/gallery/ 但是现在我想用其中一个模型创建一个SCNNode,所以我正在执行以下操作来获取该节点:
guard let urlPath = Bundle.main.url(forResource: "retrotv", withExtension: "usdz") else {
    return
}
let mdlAsset = MDLAsset(url: urlPath)
let modelRootNode = SCNScene(mdlAsset: mdlAsset).rootNode

然后我将其添加到场景中,结果如下所示: enter image description here 为什么它没有纹理?
我已经将下载的.usdz文件放入项目目录中的一个文件夹中,如您所见: enter image description here

老兄,你只是简单地忘记了 .loadTextures() 这个方法! - Fattie
4个回答

19

添加 .USDZ 对象的正确方法实际上是使用文件的 URL 创建场景:

 let scene = try! SCNScene(url: usdzURL, options: [.checkConsistency: true])

甚至可以通过参考节点进行创建:

 let referenceNode = SCNReferenceNode(url: usdzURL)
 referenceNode.load()

我能否在不创建新场景的情况下使用USD更新当前场景? - iaomw
这个较早的答案并不正确;你可以将一个文件作为“整个场景”加载,但几乎没有或根本没有这样做的情况。将文件作为节点加载是容易和正常的,这也是你想要的。 - Fattie

4

可以将usdz加载到ARKit场景中。

有以下import很重要

import ARKit
import SceneKit
import SceneKit.ModelIO

通过URL加载usdz文件。

guard let urlPath = Bundle.main.url(forResource: "retrotv", withExtension: "usdz") else {
    return
}
let mdlAsset = MDLAsset(url: urlPath)
// you can load the textures on an MDAsset so it's not white
mdlAsset.loadTextures()

将其包裹在一个节点中。
let asset = mdlAsset.object(at: 0) // extract first object
let assetNode = SCNNode(mdlObject: asset)

现在,您可以在ARKit中附加此节点。您需要对该对象进行缩放和定位,以使其出现在真实世界的预期位置。但是,具体的代码取决于您试图做什么,因此我已将其省略。


1
这是唯一真正正确的答案。秘密酱是 object#at。只是一个注意事项,这对场景工具包(以及在此答案中提到的 AR)完美地运作。 - Fattie

0

字面上的答案是OP忘记加载纹理了。

这很容易发生,

asset.loadTextures()

针对 2023 年。将您的 .usdz 文件转换为节点非常容易。

理解的秘诀是,.usdz 文件包含(理论上)一个或多个不同的项目或角色。

秘密在于提取文件中的“第一个”(唯一)项目或角色。

几乎总是,usdz 文件只有一个对象。因此,您只需提取“第一个”(几乎始终是唯一的)对象即可。

var fireDragon: SCNNode!

func loadDragonModel() {
  let p = Bundle.main.url(forResource: "Amy1", withExtension: "usdz")!
  let ass = MDLAsset(url: p)
  ass.loadTextures()
  fireDragon = SCNNode(mdlObject: ass.object(at: 0))
}

就是这样。然后,

  // .. some node of yours .. .addChildNode(fireDragon)
  // for example
  castleWall.addChildNode(fireDragon)

这是针对SceneKit和ARKit的。

所有这些都适用于SceneKit和ARKit。它在tvOS、iOS、iPad、iPhone、Mac等设备上完美运行。


-1

访问内部USDZ节点的正确方法

func usdzNodeFrom(file: String, exten: String, internal_node: String) -> SCNNode? {
    let rootNode = SCNNode()
    let scale = 1

    guard let fileUrl = Bundle.main.url(forResource: file, withExtension: exten) else {
        fatalError()
    }
    
    let scene = try! SCNScene(url: fileUrl, options: [.checkConsistency: true])
    let node = scene.rootNode.childNode(withName: internal_node, recursively: true)!
    node.name = internal_node
    let height = node.boundingBox.max.y - node.boundingBox.min.y
    node.position = SCNVector3(0, 0, 0)
    tNode.scale = SCNVector3(scale, scale, scale)
    rootNode.addChildNode(tNode)
    return rootNode
}

进一步纹理未显示是不同的问题,可以通过在环境中添加光照来解决。


1
你为什么有tNode?内部节点名称是什么? - jgvb
这似乎是一个无关的复制粘贴,请点击删除。 - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接