ARKit не может правильно вращать узел SCNN в вертикальной плоскости - PullRequest
1 голос
/ 25 октября 2019

Я хочу повернуть узел SCNNode, представляющий собой изображение для рисования.

Я могу повернуть его на полу, но не могу правильно повернуть его на стене.

(я использую UIRotationGestureRecognizer)

...
  func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {

    ...

    // Add the painting
    let newPaintingNode = SCNNode(geometry: planeGeometry)
    newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
    newPaintingNode.eulerAngles = SCNVector3(newPaintingNode.eulerAngles.x + (-Float.pi / 2), newPaintingNode.eulerAngles.y, newPaintingNode.eulerAngles.z)
    newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
    self.paintingNode = newPaintingNode

    self.currentAngleY = newPaintingNode.eulerAngles.y
    self.currentAngleZ = newPaintingNode.eulerAngles.z

    augmentedRealityView.scene.rootNode.addChildNode(self.paintingNode!)
    grid.removeFromParentNode()
  }
...

  @objc func rotateNode(_ gesture: UIRotationGestureRecognizer){

    if let currentNode = self.paintingNode {
      //1. Get The Current Rotation From The Gesture
      let rotation = Float(gesture.rotation)

      if self.paintingAlignment == "horizontal" {

        log.verbose("rotate horizontal!")

        //2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
        if gesture.state == .changed {
          currentNode.eulerAngles.y = currentAngleY + rotation
        }

        //3. If The Gesture Has Ended Store The Last Angle Of The Cube
        if(gesture.state == .ended) {
          currentAngleY = currentNode.eulerAngles.y
        }

      } else if self.paintingAlignment == "vertical" {

        log.verbose("rotate vertical!")

        //2. If The Gesture State Has Changed Set The Nodes EulerAngles.z
        if gesture.state == .changed {
          currentNode.eulerAngles.z = currentAngleZ + rotation
        }

        //3. If The Gesture Has Ended Store The Last Angle Of The Cube
        if(gesture.state == .ended) {
          currentAngleZ = currentNode.eulerAngles.z
        }
      }
    }
  }

enter image description here

Кто-нибудь знает, как правильно повернуть его на стене? Спасибо!

Ответы [ 2 ]

1 голос
/ 25 октября 2019

Вы используете eulerAngles свойство экземпляра в своем коде:

var eulerAngles: SCNVector3 { get set }

Согласно документации Apple:

SceneKit применяет eulerAngles поворотов относительно оси вращения узласвойство в обратном порядке компонентов: сначала roll ( Z ), затем yaw ( Y ), затем pitch ( X ).

... но трехкомпонентное вращение может привести к блокировке карданного подвеса .

блокировке карданного подвеса является потеря одной степени свободы втрехмерный механизм с тремя подвесками, который возникает, когда оси двух из трех подвесов приводятся в параллельную конфигурацию, «блокируя» систему во вращении в вырожденном двумерном пространстве.

Таким образом, вам нужно использовать свойство четырехкомпонентного вращения:

var rotation: SCNVector4 { get set }

Вектор вращения из четырех компонентов указывает направление оси вращенияв первых трех компонентах ( XYZ ) и угол поворота, выраженный в radians, в четвертом ( Ш ).

currentNode.rotation = SCNVector4(x: 1, 
                                  y: 0, 
                                  z: 0,
                                  w: -Float.pi / 2)

Если вы хотите узнать больше о SCNVector4 структуре и четырехкомпонентном вращении (и его W-компоненте, выраженной в радианах), посмотрите ЭТОТ ПОСТ и ЭТО ПОЧТА .

PS

In RealityKit Framework вместо SCNVector4 структура, которую нужно использовать SIMD4<Float> общая структура или simd_float4 псевдоним типа.

var rotation: simd_float4 { get set }
0 голосов
/ 06 ноября 2019

Я пытался SCNNode eulerAngles и SCNNode rotation, и мне не повезло ...

Я думаю, это потому, что я вызвал SCNNode.transform(), когда добавляю картину. Когда позже я изменю SCNNode.eulerAngles и вызову SCNNode.rotation(), они могут повлиять на первые transform.

. Наконец-то я получу работу, используя SCNMatrix4Rotate, а не eulerAngles и rotation.

  func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
    ...
    let newPaintingNode = SCNNode(geometry: planeGeometry)
    newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
    newPaintingNode.transform = SCNMatrix4Rotate(newPaintingNode.transform, -Float.pi / 2.0, 1.0, 0.0, 0.0)
    newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
    self.paintingNode = newPaintingNode
    ...
  }
...
  @objc func rotateNode(_ gesture: UIRotationGestureRecognizer){

    if let _ = self.paintingNode {

      // Get The Current Rotation From The Gesture
      let rotation = Float(gesture.rotation)

      if gesture.state == .began {
        self.rotationZ = rotation
      }

      if gesture.state == .changed {
        let diff = rotation - self.rotationZ
        self.paintingNode?.transform = SCNMatrix4Rotate(self.paintingNode!.transform, diff, 0.0, 0.0, 1.0)
        self.rotationZ = rotation
      }

    }

  }

А приведенный выше код работает как в вертикальной, так и в горизонтальной плоскости.

...