Scenekit - 基于世界轴的旋转子子节点的 convertTransform [英] Scenekit - convertTransform of a rotated sub-subNode based on world axes

查看:61
本文介绍了Scenekit - 基于世界轴的旋转子子节点的 convertTransform的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下场景:

         [Root Node]
              |
      [Main Container]
        |           |          
[Node A Wrapper]   [Node B Wrapper] 
   |                   |
[Node A]            [Node B]

我已经设置了平移手势识别器,当您在开放空间中平移时,[主容器] 会沿选定方向旋转 +/- Double.pi/2 (90deg).当平移在子节点 A、B 之一上开始时(我在 touchesBegan 上对此进行了测试),我想沿世界轴的方向旋转子节点(再次以 90 度为增量).

I've set up pan gesture recognizers in a way that when u pan in open space, the [Main Container] rotates in the selected direction by +/- Double.pi/2 (90deg). When the pan starts on one of the subnodes A, B (i'm hittesting for this on touchesBegan), i want to rotate the subnode along the direction of world axis (again 90deg increments).

我正在使用 rootNode 中的 convertTransform() 旋转 [Main Container],效果很好,并且旋转是沿世界轴执行的 - 我认为主容器的位置是 (0,0,0)容易多了.

I'm rotating the [Main Container] using convertTransform() from rootNode, which works fine, and the rotations are performed along the world axes - the position of main container is (0,0,0) which i believe makes it lot easier.

我包装子节点的原因是它们在包装器内具有局部位置 (0,0,0),这应该有助于围绕它们的原点旋转.但是当我在 [Main Container] 上执行旋转时,它们也会旋转,因此它们的局部轴的方向发生了变化,并且旋转是围绕与我想要的不同的轴进行的.

The reason why i wrapped the subnodes is so they have local positions (0,0,0) inside the wrapper, which should help with the rotation around their origin. But as they are rotated also when i perform rotate on [Main Container] , the direction of their local axes is changed and the rotation is performed around different axis than what i want.

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

In my (very limited) understanding of transformation matrices, i assume i need to somehow chain and multiply the matrices produced by convertTransform of the parent nodes, or to use the worldTransform property somehow, but anything i tried results in weird rotations. Any help would be appreciated!

推荐答案

我已经建立了一个基于 SceneKit 模板的小型示例项目,其中的控件与您所描述的类似.它在 Objective C 中,但相关部分几乎相同:

I've set up a small sample project based on the SceneKit template, with controls similar as what you described. It's in Objective C but the relevant parts are pretty much the same:

- (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);

        }
    }
}

在 handleTap 中:

In handleTap:

selectedNode = result.node;

在 viewDidLoad 中:

In 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];

和本地变量:

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

MainPlanet 将是您的 mainContainer 并且不必是可见的(在我的示例中是可见的,因为必须点击它才能知道要旋转什么......).两个卫星是你的节点 A 和 B,主节点的子节点.不需要包装器.关键部分显然是注释部分.

MainPlanet would be your mainContainer and doesn't have to be visible (it does in my example because it has to be tapped to know what to rotate...). The two moons are your node A and B, child nodes of the main node. No wrappers necessary. The key part is obviously the commented portion.

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

Normally to rotate a child node in local space (IF the parent node is at 0,0,0)

  1. 首先将其变换与其平移的倒数相乘,从而将其移回节点.
  2. 应用旋转矩阵.
  3. 应用我们在第 1 步中删除的原始翻译.

正如您所注意到的,这将在其本地枢轴点和其本地轴上旋转子节点.这工作正常,直到您旋转父节点.解决方案是在基于平移手势旋转它之前将相同的旋转应用于子节点(步骤 2),然后再次将其移除.

As you noticed that will rotate the child node on its local pivot point and over its local axis. This works fine until you rotate the parent node. The solution is to apply that same rotation to the child node before rotating it based on the pan gesture (step 2), and then after that remove it again.

为了得到你想要的结果:

So to get the results you desire:

  1. 首先将其变换与其平移的倒数相乘,将其移回节点.
  2. 应用父节点的旋转(因为它在 0,0,0 并且我假设没有缩放,我们可以使用变换).
  3. 根据平移手势应用旋转矩阵.
  4. 去除父节点的旋转
  5. 应用我们在第 1 步中删除的原始翻译.

我确定还有其他可能的路线,也许可以使用 convert to/from 将旋转矩阵转换为主节点,而不是第 2 步和第 4 步,但这样您就可以清楚地知道发生了什么.

I’m sure there are other possible routes and perhaps instead of step 2 and 4 the rotation matrix could be converted to the main node using convert to/from but this way you can clearly tell what’s going on.

这篇关于Scenekit - 基于世界轴的旋转子子节点的 convertTransform的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆