Search code examples
iosswiftaugmented-realityarkitscnnode

Load large 3d Object .scn file in ARSCNView Aspect Fit in to the screen ARKIT Swift iOS


I am developing ARKit Application using 3d models. So for that I have used 3d models & added gestures for move, rotate & zoom 3d models.

Now I am facing only 1 issue but I am not sure if this issue relates to what. Is there an issue in 3d model or if anything missing in my program.

Issue is the 3d model I am using shows very big & goes out of the screen. I am trying to scale it down size but its very big.

Here is my code :

@IBOutlet var mySceneView: ARSCNView!
var selectedNode = SCNNode()
var prevLoc = CGPoint()
var touchCount : Int = 0

override func viewDidLoad() {
    super.viewDidLoad()
    self.lblTitle.text = self.sceneTitle
    let mySCN = SCNScene.init(named: "art.scnassets/\(self.sceneImagename).scn")!
    self.mySceneView.scene = mySCN

    let cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    cameraNode.position = SCNVector3Make(0, 0, 0)
    self.mySceneView.scene.rootNode.addChildNode(cameraNode)
    self.mySceneView.allowsCameraControl = true
    self.mySceneView.autoenablesDefaultLighting = true

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(detailPage.doHandleTap(_:)))
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(detailPage.doHandlePan(_:)))
    let gesturesArray = NSMutableArray()
    gesturesArray.add(tapGesture)
    gesturesArray.add(panGesture)
    gesturesArray.addObjects(from: self.mySceneView.gestureRecognizers!)
    self.mySceneView.gestureRecognizers = (gesturesArray as! [UIGestureRecognizer])
}

//MARK:- Handle Gesture
@objc func doHandlePan(_ sender: UIPanGestureRecognizer) {
    var delta = sender.translation(in: self.view)
    let loc = sender.location(in: self.view)
    if sender.state == .began {
        self.prevLoc = loc
        self.touchCount = sender.numberOfTouches
    } else if sender.state == .changed {
        delta = CGPoint(x: loc.x - prevLoc.x, y: loc.y - prevLoc.y)
        prevLoc = loc
        if self.touchCount != sender.numberOfTouches {
            return
        }

        var rotMat = SCNMatrix4()
        if touchCount == 2 {
            rotMat = SCNMatrix4MakeTranslation(Float(delta.x * 0.025), Float(delta.y * -0.025), 0)
        } else {
            let rotMatX = SCNMatrix4Rotate(SCNMatrix4Identity, Float((1.0/100) * delta.y), 1, 0, 0)
            let rotMatY = SCNMatrix4Rotate(SCNMatrix4Identity, Float((1.0/100) * delta.x), 0, 1, 0)
            rotMat = SCNMatrix4Mult(rotMatX, rotMatY)
        }

        let transMat = SCNMatrix4MakeTranslation(selectedNode.position.x, selectedNode.position.y, selectedNode.position.z)
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, SCNMatrix4Invert(transMat))

        let parentNodeTransMat = SCNMatrix4MakeTranslation((selectedNode.parent?.worldPosition.x)!, (selectedNode.parent?.worldPosition.y)!, (selectedNode.parent?.worldPosition.z)!)
        let parentNodeMatWOTrans = SCNMatrix4Mult(selectedNode.parent!.worldTransform, SCNMatrix4Invert(parentNodeTransMat))
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, parentNodeMatWOTrans)

        let camorbitNodeTransMat = SCNMatrix4MakeTranslation((self.mySceneView.pointOfView?.worldPosition.x)!, (self.mySceneView.pointOfView?.worldPosition.y)!, (self.mySceneView.pointOfView?.worldPosition.z)!)
        let camorbitNodeMatWOTrans = SCNMatrix4Mult(self.mySceneView.pointOfView!.worldTransform, SCNMatrix4Invert(camorbitNodeTransMat))
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, SCNMatrix4Invert(camorbitNodeMatWOTrans))
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, rotMat)

        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, camorbitNodeMatWOTrans)
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, SCNMatrix4Invert(parentNodeMatWOTrans))
        selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, transMat)
    }
}

@objc func doHandleTap(_ sender: UITapGestureRecognizer) {
    let p = sender.location(in: self.mySceneView)
    var hitResults = self.mySceneView.hitTest(p, options: nil)

    if (p.x > self.mySceneView.frame.size.width-100 || p.y < 100) {
        self.mySceneView.allowsCameraControl = !self.mySceneView.allowsCameraControl
    }

    if hitResults.count > 0 {
        let result = hitResults[0]
        let material = result.node.geometry?.firstMaterial
        selectedNode = result.node

        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.3

        SCNTransaction.completionBlock = {
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.3
            SCNTransaction.commit()
        }
        material?.emission.contents = UIColor.white
        SCNTransaction.commit()
    }
}

My Question is :

Can we set any size of 3d object model Aspect fit in screen size in the centre of the screen ? Please suggest if there is some way for it.

Any guidence or suggestions will be highly appreciated.


Solution

  • The way I handled this is making sure the 3D model always has a fixed size.

    For example, if the 3D model is a small cup or a large house, I insure it always has a width of 25 cm on the scene's coordinate space (while maintaining the ratios between x y z).

    You can calculate the width of the bounding box of the node like this:

    let mySCN = SCNScene(named: "art.scnassets/\(self.sceneImagename).scn")!
    
    let minX = mySCN.rootNode.boundingBox.min.x
    let maxX = mySCN.rootNode.boundingBox.max.x
    
    // change 0.25 to whatever you need
    // this value is in meters
    let scaleValue = 0.25 / abs(minX - maxX)
    
    // scale all axes of the node using `scaleValue`
    // this maintains ratios and does not stretch the model
    mySCN.rootNode.scale = SCNVector3(scaleValue, scaleValue, scaleValue)
    
    self.mySceneView.scene = mySCN
    

    You can also calculate the scale value based on height or depth by using the y or z value of the bounding box.