4
votes

I have main scene file: main.scn and several other scene files with additional SCNNodes: scene1.scn, scene2.scn, scene3.scn, ... At the app start I load needed nodes from scene1.scn and add them to main.scn (show on screen). During runtime I need to add additional nodes from other sceneN.scn files. I tried two methods and each one is unused:

1) Inside renderer(_, updateAtTime) I just load needed nodes:

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    let scene2 = SCNScene(named: "game.scnassets/scene2.scn")!
    let node = scene2.rootNode.childNode(withName: "nodeName", recursively: false)!
    mainScene.rootNode.addChildNode(node)
}

In this case I get error: [SceneKit] Error: Scene is modified within a rendering callback of another scene (). This is not allowed and may lead to crash

2) Load scene2 and node in background, add it so some nodesToShow array and inside renderer(_, updateAtTime) show nodes from the array. In this case sometimes I got error:

com.apple.scenekit.scnview-renderer (17): EXC_BAD_ACCESS (code=1, address=0xf000000010a10c10)

May be you know some true solution to load and present nodes from other scene files at runtime?

1

1 Answers

4
votes

You can use SCNReferenceNode to load content from another scene file:

A scene graph node that serves as a placeholder for content to be loaded from a separate scene file.

Then you should enclose the .load() command in an SCNTransaction block to create an atomic update:

This transaction groups any additional changes you make from the same thread during the current iteration of that thread’s run loop. When the run loop next iterates, SceneKit automatically commits the transaction, atomically applying all changes made during the transaction to the presentation scene graph (that is, the, version of the scene graph currently being displayed).

The Swift code:

let url = Bundle.main.url(forResource: "ship", withExtension: "scn", subdirectory: "art.scnassets")!
let referenceNode = SCNReferenceNode(url: url)!
scnView.scene!.rootNode.addChildNode(referenceNode)
SCNTransaction.begin()
referenceNode.load()
SCNTransaction.commit()