如何在iOS SceneKit中将.dae文件加载到SCNNode中?

22

我对如何将.dae文件中的几何图形加载到iOS SceneKit场景图中感到困惑。

我知道可以直接将.dae文件加载到SCNScene中:

// create a new scene
let scene = SCNScene("test.scnassets/test.dae")

我也知道你可以创建一个空的SCNScene,并将内置的3D对象添加为其子节点:

let scene = SCNScene()
var sphereNode = SCNNode()
sphereNode.geometry = SCNSphere(radius: 1.0)
scene.rootNode.addChildNode(sphereNode)
在第二种情况下,你是否可以将.dae文件加载到SCNNode中而不是使用内置对象?或者需要先将所有的.dae文件加载到SCNScene中?如果后者为真,则如何将SCNNode对象放置在SCNScene下正确的位置? 编辑#1:

现在这对我有用了,这是@FlippinFun建议的修改版本。

let url = NSBundle.mainBundle().URLForResource("test.scnassets/test", withExtension: "dae")
let source = SCNSceneSource(URL: url, options: nil)
let block = source.entryWithIdentifier("ID10", withClass: SCNGeometry.self) as SCNGeometry
scene.rootNode.addChildNode(SCNNode(geometry: block))

也许有更好的方式吗?上述方法需要手动从.dae树中加载每个标识符。在我的文件中,有一个名为"SketchUp"的节点,其子标识符为"ID2"和"ID10"。这种方法不能加载根目录"SketchUp"。(我会得到运行时错误。)


1
我已经发布了这个。这是链接。https://dev59.com/X18f5IYBdhLWcg3wHPeN - FlippinFun
谢谢。所以你的建议是使用SCNSceneSource。不幸的是,目前这对我来说会导致崩溃。 - M-V
你的代码中似乎存在一个检索几何体的错误。该类应该是SCNGeometry而不是SCNNode。例如:let block = source.entryWithIdentifier("ID10", withClass: SCNGeometry.self) as SCNGeometry。通过这个修复,它对我来说可以工作了。 - M-V
是的,有一个 Bug。我会进行修改。谢谢! - FlippinFun
关于这个10年前的问题,现在(2023年)的完整答案是https://dev59.com/78Xsa4cB1Zd3GeqPsbUh#75093081。 - Fattie
7个回答

11

通常的模式是加载场景并使用其名称检索您感兴趣的节点

[scene.rootNode childNodeWithName:aName recursively:YES];

然后您可以将此节点重新指定到您的主场景。


但是在这种情况下,节点已经是根节点的直接子节点了,对吗?所以如果我需要它位于场景图中的其他节点下面,我就需要重新定位它,采用这种方法。这就是为什么我问是否可以独立加载SCNNode的几何体,而不依赖于SCNScene的原因。 - M-V
无论如何,这是一种有用的方法。谢谢。 - M-V
通常情况下,您会使用“其他”dae文件创建一个新场景,检索您感兴趣的节点,并将其重新父级化到您的主场景中。 - mnuages
你通常如何重新指定节点的父级? - M-V
1
你可以在WWDC 2014示例代码中查看-[SCNNode asc_addChildNodeNamed:fromSceneNamed:withScale:]扩展方法。构建场景,使用-childNodeWithName:recursively:获取节点,然后将其简单地添加到主场景中的一个节点中。 - mnuages
为什么没有将scene1.rootNode添加到scene2中?我的.scn文件只有在rootNode上的动画,该如何处理? - ooOlly

10

我曾经遇到过一个类似的问题,我有一个.dae文件,我想从该文件中加载多个节点到一个节点中。这对我起作用:

var node = SCNNode()
let scene = SCNScene(named: "helloScene.dae")
var nodeArray = scene.rootNode.childNodes

for childNode in nodeArray {
  node.addChildNode(childNode as SCNNode)
}

首先,我设置了一个节点来将所有内容组合在一起,使其看起来与原始文件相同。接下来,我们导入我们想要的.dae文件并复制其中的所有节点。最后,对于数组中的每个节点,我将其添加到该节点中。


我将“var nodeArray = scene.rootNode.childNodes”替换为“var nodeArray = scene!.rootNode.childNodes”。 - Alessandro Pirovano

7
感谢您的帮助!
这是一个简单的collada2Node类。
请注意,此处保留了HTML标记。
//collada2SCNNode
class func collada2SCNNode(filepath:String) -> SCNNode {
    var node = SCNNode()
    let scene = SCNScene(named: filepath)
    var nodeArray = scene!.rootNode.childNodes

    for childNode in nodeArray {
        node.addChildNode(childNode as SCNNode)
    }
    return node
}

4
func nodeWithFile(path: String) -> SCNNode {

    if let scene = SCNScene(named: path) {

        let node = scene.rootNode.childNodes[0] as SCNNode
        return node

    } else {
        println("Invalid path supplied")
        return SCNNode()
    }

}

3
以下答案对我没有用。
以下是在Xcode 7.0 beta 5,iOS 9.0 beta / Swift 2.0中可行的方法:
3D模型类型:Collada 3D模型名称:“model.dae”
  1. Load the scene:

    let lampScene = SCNScene(named: "art.scnassets/model.dae")
    
  2. Access the child SCNode which is in the SCNScene:

    let lampRootNode = lampScene?.rootNode.childNodes[0]
    
  3. Add the lamp node from the loaded scene into the current loaded and viewed scene:

    self.scene.rootNode.addChildNode(lampRootNode!)
    

注意: self.scene 的存在是由以下原因造成的:

private var scene:SCNScene!

在添加对象到场景之前,我根据场景工具包的示例进行了一些场景设置。如果你需要代码,请在评论中提出。
希望这有所帮助。

1
你为什么没有使用SCNReferenceNode?这种方法有什么优点吗? - Crashalot

1

我们找到的唯一实用的方法是:2023年。

(1) 简单地使用普通的文本编辑器(如VSCode),打开YourFileName.dae文件。

enter image description here

请注意,文件中包含材料、效果等内容。

只需滚动到library_geometries,您将立即看到id,

在这种情况下为“geometry316”。

(请注意,几乎总是library_geometries仅是文件的最终长行,易于查找。)

非常简单,id(例如,“geometry316”)在XCODE中不显示,这只是一个Xcode的bug,他们很快就会修复。

(2) 然后,要获取并显示节点,只需:

var kingDragon: SCNNode!
...


let p = Bundle.main.url(forResource: "Badass Dragon", withExtension: "dae")!
let source = SCNSceneSource(url: p, options: nil)!
let g = source.entryWithIdentifier("geometry316",
              withClass: SCNGeometry.self)! as SCNGeometry
kingDragon = SCNNode(geometry: g)
castleWall.addChildNode(kingDragon)

就是这样了。

注意!

如果您想进一步加载和使用角色动画,总体解决方案在此处概述:https://dev59.com/78Xsa4cB1Zd3GeqPsbUh#75093081


0
guard let shipScene = SCNScene(named: "ship.dae") else { return }
let shipNode = SCNNode()
let shipSceneChildNodes = shipScene.rootNode.childNodes
for childNode in shipSceneChildNodes {
    shipNode.addChildNode(childNode)
}

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