Search code examples
swiftarkitrealitykitreality-composer

How can I load a scene from RC as a ModelEntity instead of a plain Entity?


I am trying to load an Entity into my code from a scene in a Reality Composer project.

I am reaching out directly to the autogenerated .reality file because I do not want it placed automatically on the first plane it detects. I followed the documentation found in the article Taking Control of Scene Anchoring.

The sample project name is Experience and in there I have 2 scenes.

  1. The autogenerated Box that comes automatically with a newly created project.
  2. A Hexagon shaped imported USDZ file.

The code I use to load both as a ModelEntity is the following:

let url = Bundle.main.url(forResource: "Experience", 
                        withExtension: "reality")!
            .appending(path: "SceneName", directoryHint: .notDirectory)
        
let modelEntity = try! Entity.loadModel(contentsOf: url)

where SceneName is either the Box or the Hexagon.

Both fail to load.

If I instead use the following code they succeed, but they are loaded as an Entity not as a ModelEntity:

let modelEntity = try! Entity.load(contentsOf: url)

Is it possible to have objects loaded as a ModelEntity instead of an Entity? The method I am using so far, which I found in this post, is to traverse the children hierarchy, find the first ModelEntity, and copy its model and physics onto its parent, (which in my case works since I only have one object in the scene).


Edit: Even if I name the objects within the Scene and then try to get them with the findEntity(named:) method on the "root" Entity object found in the Scene, I again get an Entity and not a ModelEntity.


Solution

  • Reality Composer's .rcproject

    In case you're using Reality Composer's Experience.rcproject file, try the following technique. Implement findEntity(named:) method and then use typecast as! operator to grab the desired model entity from RC scene.

    import UIKit
    import RealityKit
    
    class ViewController: UIViewController {
        
        @IBOutlet var arView: ARView!
        typealias ModelPack = ModelEntity & HasPhysicsBody
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let scene1 = try! Experience.loadBox()
            let scene2 = try! Experience.loadHexagon()
            
            print(scene1); print(scene2)
            
            let boxModel = scene1.findEntity(named: "simpBld_root") as! ModelPack
            let hexModel = scene2.findEntity(named: "simpBld_root") as! ModelPack
            boxModel.position.x = -0.075
            hexModel.position.x = 0.075
            
            let anchor = AnchorEntity()     // any RealityKit or ARKit anchor
            anchor.scale *= 3
            anchor.addChild(boxModel)
            anchor.addChild(hexModel)
            
            arView.scene.anchors.append(anchor)
        }
    }
    

    .reality file

    Apple proprietary .reality format serves for a faster uploading time. Use the following approach to load .reality file into your 3D scene. Remember every RealityKit's scene is entities' hierarchy.

    import UIKit
    import RealityKit
    
    class ViewController: UIViewController {
        
        @IBOutlet var arView: ARView!
        typealias ModelPack = ModelEntity & HasPhysicsBody
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let entity = try! Entity.load(named: "SomeScene.reality")
            print(entity)
            
            let boxModel = entity.findEntity(named: "simpBld_root") as! ModelPack
            
            let anchor = AnchorEntity(.face)
            anchor.addChild(boxModel)
            arView.scene.anchors.append(anchor)
        }
    }