Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/95.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios Scenekit-基于世界轴的旋转子节点变换_Ios_Swift_Rotation_Transform_Scenekit - Fatal编程技术网

Ios Scenekit-基于世界轴的旋转子节点变换

Ios Scenekit-基于世界轴的旋转子节点变换,ios,swift,rotation,transform,scenekit,Ios,Swift,Rotation,Transform,Scenekit,我有一个场景: [Root Node] | [Main Container] | | [Node A Wrapper] [Node B Wrapper] | | [Node A] [Node B] 我已经设置了平移手势识别器,当你在开阔空间平移时,[主容器]在选定方向上旋转+/-Double.pi/2(90度)。

我有一个场景:

         [Root Node]
              |
      [Main Container]
        |           |          
[Node A Wrapper]   [Node B Wrapper] 
   |                   |
[Node A]            [Node B]
我已经设置了平移手势识别器,当你在开阔空间平移时,[主容器]在选定方向上旋转+/-Double.pi/2(90度)。当平移在子节点A、B中的一个子节点上开始时(我正在测试此触控开始),我希望沿世界轴的方向旋转子节点(再次以90度为增量)

我正在使用根节点的convertTransform()旋转[Main Container],这很好,并且旋转是沿着世界轴执行的-主容器的位置是(0,0,0),我相信这会更容易

我之所以包装子节点,是因为它们在包装器中具有局部位置(0,0,0),这有助于围绕其原点旋转。但是,当我在[Main Container]上执行旋转时,它们也会旋转,因此它们的局部轴的方向会改变,并且旋转的轴与我想要的轴不同


在我对变换矩阵(非常有限)的理解中,我假设我需要以某种方式链接和乘以父节点的convertTransform生成的矩阵,或者以某种方式使用worldTransform属性,但我尝试的任何操作都会导致奇怪的旋转。任何帮助都将不胜感激

我基于SceneKit模板设置了一个小样本项目,其控件与您描述的类似。在目标C中,但相关部分基本相同:

- (void) handlePan:(UIPanGestureRecognizer*)gestureRecognize {

   CGPoint delta = [gestureRecognize translationInView:(SCNView *)self.view];
   if (gestureRecognize.state == UIGestureRecognizerStateChanged) {
       panHorizontal = NO;
       if (fabs(delta.x) > fabs(delta.y)) {
           panHorizontal = YES;
       }

   } else if (gestureRecognize.state == UIGestureRecognizerStateEnded) {

       SCNMatrix4 rotMat;
       int direction = 0;
       if (panHorizontal) {
           if (delta.x <0) {
               direction = -1;
           } else if (delta.x >1) {
               direction = 1;
           }
           rotMat= SCNMatrix4Rotate(SCNMatrix4Identity, M_PI_2, 0, direction, 0);
       } else {
           if (delta.y <0) {
               direction = -1;
           } else if (delta.y >1) {
               direction = 1;
           }
           rotMat= SCNMatrix4Rotate(SCNMatrix4Identity, M_PI_2, direction, 0, 0);
       }


        if (selectedNode == mainPlanet) {
            selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, rotMat);

        } else { //_selectedNode is a child node of mainPlanet, i.e. moons.
            //get the translation matrix of the child node
            SCNMatrix4 transMat = SCNMatrix4MakeTranslation(selectedNode.position.x, selectedNode.position.y, selectedNode.position.z);

            //move the child node the origin of its parent (but keep its local rotation)
            selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, SCNMatrix4Invert(transMat));

            //apply the "rotation" of the mainPlanet extra (we can use the transform because mainPlanet is at world origin)
            selectedNode.transform = SCNMatrix4Mult( selectedNode.transform, mainPlanet.transform);

            //perform the rotation based on the pan gesture
            selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, rotMat);

            //remove the extra "rotation" of the mainPlanet (we can use the transform because mainPlanet is at world origin)
            selectedNode.transform = SCNMatrix4Mult(selectedNode.transform,SCNMatrix4Invert(mainPlanet.transform));

            //add back the translation mat
            selectedNode.transform = SCNMatrix4Mult(selectedNode.transform,transMat);

        }
    }
}
在viewDidLoad中:

 mainPlanet = [scene.rootNode childNodeWithName:@"MainPlanet" recursively:YES];
 orangeMoon = [scene.rootNode childNodeWithName:@"orangeMoon" recursively:YES];
 yellowMoon = [scene.rootNode childNodeWithName:@"yellowMoon" recursively:YES];

UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[gestureRecognizers addObject:panGesture];
和本地VAR:

SCNNode *mainPlanet;
SCNNode *orangeMoon;
SCNNode *yellowMoon;
SCNNode *selectedNode;
BOOL panHorizontal;

MainPlanet将是您的主容器,不必是可见的(在我的示例中是可见的,因为必须点击它才能知道旋转什么…)。这两个卫星是节点A和B,它们是主节点的子节点。不需要包装。关键部分显然是评论部分

通常在局部空间中旋转子节点(如果父节点位于0,0,0)

  • 首先,将其变换与仅平移的倒数相乘,将其移回节点
  • 应用旋转矩阵
  • 应用我们在步骤1中删除的原始翻译
  • 正如您所注意到的,这将在其局部轴点上并在其局部轴上旋转子节点。在旋转父节点之前,此操作可以正常工作。解决方案是在基于平移手势旋转子节点之前(步骤2),将相同的旋转应用于子节点,然后再次移除它

    因此,要获得您想要的结果:

  • 首先,将其变换与仅平移的倒数相乘,将其移回节点
  • 应用父节点的旋转(因为它位于0,0,0,并且我假设未缩放,所以可以使用变换)
  • 基于平移手势应用旋转矩阵
  • 删除父节点的旋转
  • 应用我们在步骤1中删除的原始翻译
    我确信还有其他可能的路径,也许可以使用convert to/from将旋转矩阵转换为主节点,而不是步骤2和步骤4,但这样您可以清楚地知道发生了什么。

    使用包装器节点只会使事情变得不必要的复杂。有许多其他方法可以实现这一点。在我试图回答之前,我说你想要两样东西是对的:1。如果平移没有在这些节点上启动,请同时旋转节点a和节点B(这意味着您可以旋转相机,而不是主容器和所有子节点)。二,。在世界空间中旋转子节点(如果平移从这些节点开始)。谢谢Xartec。。澄清-1。节点A和B与主容器一起旋转,因为它们是主容器的子容器。在这种情况下,我不想旋转相机。2.不确定我是否正确地解释了这一点,我希望子节点使用其局部原点(即立方体的中心)旋转,但旋转它就像它的局部轴与世界对齐一样。。就像我将主容器沿Y轴旋转90度一样,现在A&B节点的Z轴指向右侧。如果我现在想向下旋转A节点(向下滑动),旋转应该是沿着它的局部Z轴旋转,该轴现在与世界X平行。先生,你是我书中的绝对英雄。正是这样的答案让Stackowerflow成为了最好的去处——你的答案不仅为我的问题提供了一个精确的解决方案,而且在我花了几天时间在纸上写下矩阵并试图理解变换矩阵的实际工作原理之后,它还为我的学习曲线带来了一个巨大的“啊哈”时刻。太感谢你了!很高兴听到解决方案适合您,并感谢您的友好反馈。
    SCNNode *mainPlanet;
    SCNNode *orangeMoon;
    SCNNode *yellowMoon;
    SCNNode *selectedNode;
    BOOL panHorizontal;