Search code examples
swiftmatrixvectorscenekitmetal

How to move a rotated SCNNode in SceneKit, on its "own" axis?


The image below shows a rotated box that should be moved horizontally on the X and Z axes. Y should stay unaffected to simplify the scenario. The box could also be the SCNNode of the camera, so I guess a projection does not make sense at this point.

So lets say we want to move the box in the direction of the red arrow. How to achieve this using SceneKit?

The red arrow indicates -Z direction of the box. It also shows us it is not parallel to the camera's projection or to the global axes that are shown as dark grey lines of the grid.

My last approach is the product of a translation matrix and a rotation matrix that results in a new transformation matrix. Do I have to add the current transform to the new transform then?

If yes, where is the SceneKit function for the addition of matrices like SCNMatrix4Mult for multiplication or do I have to write it myself using Metal?

If no, what I'm missing out with the matrix calculations?

I don't want to make use of GLKit.

enter image description here


Solution

  • So my understanding is that you want to move the Box Node along its own X axis (not it's parents X axis). And because the Box Node is rotated, its X axis is not aligned with its parent's one, so you have the problem to convert the translation between the two coordinate systems.

    The node hierarchy is

    parentNode
        |
        |----boxNode // rotated around Y (vertical) axis
    

    Using Transformation Matrices

    To move boxNode along its own X axis

    // First let's get the current boxNode transformation matrix
    SCNMatrix4 boxTransform = boxNode.transform;
    
    // Let's make a new matrix for translation +2 along X axis
    SCNMatrix4 xTranslation = SCNMatrix4MakeTranslation(2, 0, 0);
    
    // Combine the two matrices, THE ORDER MATTERS !
    // if you swap the parameters you will move it in parent's coord system
    SCNMatrix4 newTransform = SCNMatrix4Mult(xTranslation, boxTransform);
    
    // Allply the newly generated transform
    boxNode.transform = newTransform;
    

    Please Note: The order matters when multiplying matrices

    Another option:

    Using SCNNode coordinate conversion functions, looks more straight forward to me

    // Get the boxNode current position in parent's coord system
    SCNVector3 positionInParent = boxNode.position;
    
    // Convert that coordinate to boxNode's own coord system
    SCNVector3 positionInSelf = [boxNode convertPosition:positionInParent fromNode:parentNode];
    
    // Translate along own X axis by +2 points
    positionInSelf = SCNVector3Make(positionInSelf.x + 2,
                                    positionInSelf.y,
                                    positionInSelf.z);
    
    // Convert that back to parent's coord system
    positionInParent = [parentNode convertPosition: positionInSelf fromNode:boxNode];
    
    // Apply the new position
    boxNode.position = positionInParent;