Reputation: 288
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:-
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:-
Video capture after scaling the model to 0.001 https://drive.google.com/open?id=1sBFEyleSIBMmQV5MgA6rOqJZEc0YUyKR
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
Reputation: 58043
Updated
I've checked it. Your model is in a wrong initial scale. It's in meters!
Width = 786
metersHeight = 676
metersDepth = 456
metersUse 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