Загрузить большой 3D-объект .scn файл в ARSCNView Aspect Fit на экран ARKIT Swift iOS

Я занимаюсь разработкой приложения ARKit с использованием 3d моделей. Для этого я использовал 3d-модели и добавил жесты для перемещения, поворота и масштабирования 3d-моделей.

Сейчас я сталкиваюсь только с 1 проблемой, но я не уверен, относится ли эта проблема к чему. Есть ли проблема в 3d-модели или что-то отсутствует в моей программе?

Проблема в том, что 3d модель, которую я использую, показывает очень большие и выходит за пределы экрана. Я пытаюсь уменьшить его размер, но он очень большой.

Вот мой код:

@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()
    }
}

Мой вопрос:

Можем ли мы установить любой размер 3d-модели объекта Аспект вписывается в размер экрана в центре экрана? Пожалуйста, предложите, если есть какой-то способ для этого.

Любые рекомендации или предложения будут высоко оценены.

Ответы

Ответ 1

Вам нужно использовать getBoundingSphereCenter, чтобы получить размер ограничивающей сферы, а затем спроецировать это на экран. Или, в качестве альтернативы, получите отношение этого радиуса к расстоянию между камерой сцены и положением объекта. Таким образом, вы будете знать, насколько большой объект будет выглядеть на экране. Чтобы уменьшить масштаб, вы просто устанавливаете свойство scale вашего объекта.

Для второй части вы можете использовать projectPoint.

Ответ 2

То, как я справился с этим, - убедиться, что 3D-модель всегда имеет фиксированный размер.

Например, если 3D-модель представляет собой маленькую чашку или большой дом, я гарантирую, что она всегда имеет ширину 25 см в координатном пространстве сцены (при сохранении соотношений между x y z).

Вы можете рассчитать ширину ограничительной рамки узла следующим образом:

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

Вы также можете рассчитать значение масштаба на основе высоты или глубины, используя значение y или z ограничительной рамки.