0
votes

I'm testing ARKit implementation, and a part of my experiments is just to import an animated model and let it follow the AR Scene's movement like other static models included in Apple's sample projects.

I grabbed an animated 3D model from Apple's SceneKit example https://developer.apple.com/library/content/samplecode/SceneKitAnimations/Introduction/Intro.html and put it into the simple ARKit test project. https://viewar.com/apple-augmented-reality-arkit-for-ios/

The result is: it shows the model, and is animated as well but the model keeps "following" me instead of staying in the same position in the AR space. What's strange is it's not just sticking on the same position in the screen but it keeps floating above my head in the AR space.

I'm too new to 3D things to figure out what's going on. Do you guys have any idea?

The code is like this.

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!

    var _animations = [CAAnimation]()
    var charNode : SCNNode!

    override func viewDidLoad() {
        super.viewDidLoad()        
        // Set the view's delegate
        sceneView.delegate = self        
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true        
        // Create a new scene (Keep this ship just as a reference)
        let scene = SCNScene(named: "art.scnassets/ship.scn")!        
        // Set the scene to the view
        sceneView.scene = scene        
        // Load the DAE file and the associated animations
        loadSceneAndAnimations();        
        // Be idle by default
        playAnimation(ASCAnimation.Walk);
    }

    // MARK: Playing animations  
    func playAnimation(_ animation: ASCAnimation) {
        // Use the same animation key for all the animations except "idle".
        // When we will add an animation it will replace the animation currently
        // playing (if any) but the idle animation will remain active for ever.
        let key:String = animation == .Idle ? "idleAnimation" : "otherAnimation";

        // Add the animation - it will start playing right away
        sceneView.scene.rootNode.addAnimation(_animations[animation.rawValue], forKey: key);
    }

    // MARK: Animation loading    
    func loadSceneAndAnimations () {        
        // Load the character from one of our dae documents, for instance "idle.dae"
        let idleURL = Bundle.main.url(forResource: "art.scnassets/idle", withExtension: "dae");
        let idleScene = try! SCNScene(url: idleURL!, options: nil);

        // Merge the loaded scene into our main scene in order to
        //   place the character in our own scene
        for child in idleScene.rootNode.childNodes {
            sceneView.scene.rootNode.addChildNode(child)
        }

        // Load all the animations from their respective dae document
        // The animation identifier can be found in the Node Properties inspector of the Scene Kit editor integrated into Xcode
        loadAnimation(animation: .Attack, sceneName: "art.scnassets/attack", animationIdentifier: "attackID");
        loadAnimation(animation: .Die,    sceneName: "art.scnassets/die",    animationIdentifier: "DeathID");
        loadAnimation(animation: .Idle,   sceneName: "art.scnassets/idle",   animationIdentifier: "idleAnimationID");
        loadAnimation(animation: .Run,    sceneName: "art.scnassets/run",    animationIdentifier: "RunID");
        loadAnimation(animation: .Walk,   sceneName: "art.scnassets/walk",   animationIdentifier: "WalkID");
    }

    func loadAnimation(animation:ASCAnimation, sceneName:String, animationIdentifier:String) {
        let sceneURL = Bundle.main.url(forResource: sceneName, withExtension: "dae");
        let sceneSource = SCNSceneSource(url: sceneURL!, options: nil);
        let animationObject = sceneSource?.entryWithIdentifier(animationIdentifier, withClass: CAAnimation.self)

        // Store the animation for later use
        _animations.append(animationObject!);

        // Whether or not the animation should loop
        if animation == .Idle || animation == .Run || animation == .Walk {
            animationObject?.repeatCount = MAXFLOAT;
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)        
        // Create a session configuration
        let configuration = ARWorldTrackingSessionConfiguration()        
        // Run the view's session
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)        
        // Pause the view's session
        sceneView.session.pause()
    }

    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
    }    
    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for example, by presenting an overlay        
    }    
    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent tracking is required        
    }
}
2

2 Answers

1
votes

I solved it by myself. it's so silly but it was just because the model was rendered in very huge size, like a giant of hundreds of meters. As it was too big my movement around a few meters didn't affect at all. What was bad is it was also floating very high, so it looked in a decent size in a way. By giving a scale transformation of around 1/100 it became normal.

0
votes

Have a look at the ARSceneViewDelegate, there is a method

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)

When ARKit detects a surface it adds a node to the scene where you can place your 3D model. You also need to set up your session appropriately:

let configuration = ARWorldTrackingSessionConfiguration()
configuration.planeDetection = .horizontal

For more information look at the ARKit sample code provided by Apple: https://github.com/gao0122/ARKit-Example-by-Apple/tree/master/ARKitExample