a.palo
a.palo

Reputation: 288

Object of the same size always appears to be stuck in front of the camera

I have created a small Augmented reality demo app where I wanted to place a large object in AR. But, it always appears to be stuck in front of the camera when the object is scaled to 1.0 and I am not able to see the object from all angles. Where as if I scaled it to 0.001 I am able to view the entire object from all angles. Can anyone please explain why I am facing such type of issue with large models and is there any way to fix this issue.

Dimension of the model:-

enter image description here

import UIKit
import ARKit
import SceneKit

class ViewController: UIViewController {

    @IBOutlet weak var sceneView: ARSCNView!
    private let configuration = ARWorldTrackingConfiguration()
    private var node: SCNNode!

    //MARK: - Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        self.sceneView.showsStatistics = false
        self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
        self.sceneView.automaticallyUpdatesLighting = false
        self.sceneView.delegate = self
        self.addTapGesture()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

       configuration.planeDetection = .horizontal
       self.sceneView.session.run(configuration)

    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.sceneView.session.pause()
    }

    //MARK: - Methods

    func addObject(hitTestResult: ARHitTestResult) {

        let scene = SCNScene(named: "art.scnassets/Cube.obj")!
        let modelNode = scene.rootNode.childNodes.first
        modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.1.x,
                                         hitTestResult.worldTransform.columns.2.y,
                                         -800)
        let scale = 1
        modelNode?.scale = SCNVector3(scale, scale, scale)
        self.node = modelNode
        self.sceneView.scene.rootNode.addChildNode(modelNode!)


       let lightNode = SCNNode()
       lightNode.light = SCNLight()
       lightNode.light?.type = .omni
       lightNode.position = SCNVector3(x: 0, y: 10, z: 20)
       self.sceneView.scene.rootNode.addChildNode(lightNode)

       let ambientLightNode = SCNNode()
       ambientLightNode.light = SCNLight()
       ambientLightNode.light?.type = .ambient
       ambientLightNode.light?.color = UIColor.darkGray
       self.sceneView.scene.rootNode.addChildNode(ambientLightNode)


    }

    private func addTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
        self.sceneView.addGestureRecognizer(tapGesture)
    }

    @objc func didTap(_ gesture: UIPanGestureRecognizer) {
        let tapLocation = gesture.location(in: self.sceneView)
        let results = self.sceneView.hitTest(tapLocation, types: .featurePoint)

        guard let result = results.first else {
            return
        }

        let translation = result.worldTransform.translation

        guard let node = self.node else {
            self.addObject(hitTestResult: result)
            return
        }
        node.position = SCNVector3Make(translation.x, translation.y, translation.z)
        self.sceneView.scene.rootNode.addChildNode(self.node)
    }

}

extension float4x4 {
    var translation: SIMD3<Float> {
        let translation = self.columns.3
        return SIMD3<Float>(translation.x, translation.y, translation.z)
    }
}

Also, please find attached the link of the videos:-

  1. Video capture after scaling the model to 0.001 https://drive.google.com/open?id=1sBFEyleSIBMmQV5MgA6rOqJZEc0YUyKR

  2. Video capture after scaling the model to 1 https://drive.google.com/open?id=1k5-adqLCcwaXoYY1ZePE2D-mOdyLvHvi

With the scale of 1, I am not able to see the entire object from all angles and the object also moves with the iPad camera. Is there any way to position the object at a fixed place?

Thank you in advance.

Upvotes: 1

Views: 886

Answers (1)

Andy Jazz
Andy Jazz

Reputation: 58043

Updated

I've checked it. Your model is in a wrong initial scale. It's in meters!

  • Width = 786 meters
  • Height = 676 meters
  • Depth = 456 meters

Use the following values to scale a model down:

let scene = SCNScene(named: "art.scnassets/Cube.scn")!
    
let modelNode: SCNNode? = scene.rootNode.childNode(withName: "_material_1", 
                                                recursively: true)

let scale: Float = 0.002           // 500 times smaller

modelNode?.scale = SCNVector3(scale, scale, scale)

...and return your model back along Z axis:

modelNode?.position.z = 0

That's why your model is stuck in front of the camera – it's due to the fact that you've placed a model 800 meters away from the origin of coordinates. So you couldn't look at it at a right parallax.


Try the following code in order to accommodate your model at expected hit-test position:

@objc func tap(gesture: UITapGestureRecognizer) {
    
    let touch: CGPoint = gesture.location(in: sceneView)

    let result: [ARHitTestResult] = sceneView.hitTest(touch, 
                                               types: .existingPlaneUsingExtent)
    
    guard let hitTest: ARHitTestResult = result.first 
    else { return }

    self.loadModel(hitTest)
}

Then create a content for a loadModel(:_) method:

func loadModel(_ result: ARHitTestResult) {

    let scene = SCNScene(named: "art.scnassets/myScene.scn")!

    let node: SCNNode? = scene.rootNode.childNode(withName: "model", 
                                               recursively: true)
    
    node?.position = SCNVector3(result.worldTransform.columns.3.x,
                                result.worldTransform.columns.3.y,
                                result.worldTransform.columns.3.z)

    // Please consider that ARKit's and SceneKit's scenes are in Meters
    node?.scale = SCNVector3(0.5, 2.0, 0.5)
       
    self.sceneView.scene.rootNode.addChildNode(node!)
}

And then feed a #selector with @objc tap method:

override func viewDidLoad() {
    super.viewDidLoad()
 
    let gestureRecognizer = UITapGestureRecognizer(target: self, 
                                                   action: #selector(tap))

    self.sceneView.addGestureRecognizer(gestureRecognizer)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let config = ARWorldTrackingConfiguration()
    config.planeDetection = .horizontal
    self.sceneView.session.run(config)
}

And it seems to me that you use a wrong transform, 'cause it's located at the last column of 4x4 transformation matrix:

  ┌               ┐
  |  1  0  0  tx  |
  |  0  1  0  ty  |
  |  0  0  1  tz  |
  |  0  0  0  1   |
  └               ┘
  • 1. check where is a pivot point of your 3D model? If pivot point of your model is offset along +Z axes then it always stands in front of your camera.

  • 2. Check if a scale of your model is not in tens or hundreds of meters.


#Scale Solution:

If you wanna have a node with a default scale (1.0) put your modelNode with scale=0.002 into parent node with scale=1.0:

let scene = SCNScene(named: "art.scnassets/Cube.scn")!

let modelNode: SCNNode? = scene.rootNode.childNode(withName: "_material_1", 
                                                recursively: true)

let scale: Float = 0.002           // 500 times smaller

modelNode?.scale = SCNVector3(scale, scale, scale)

let defaultScaleNode = SCNNode()
defaultScaleNode.addChildNode(modelNode!)
defaultScaleNode.scale = SCNVector3(1.0, 1.0, 1.0)

Upvotes: 1

Related Questions