如何在SceneKit中移动旋转的SCNNode?

时间:2017-04-05 17:25:41

标签: swift matrix vector scenekit metal

下图显示了一个旋转的框,应该在X和Z轴上水平移动。 Y应该不受影响以简化方案。这个盒子也可能是相机的SCNNode,所以我猜这个投影在这一点上没有意义。

所以我们要说我们想要沿着红色箭头的方向移动盒子。如何使用SceneKit实现这一目标?

红色箭头表示方框的-Z方向。它还向我们展示了它与摄像机的投影或与网格显示为深灰色线条的全局轴不平行。

我的最后一种方法是平移矩阵和旋转矩阵的乘积,它产生一个新的变换矩阵。我是否必须将当前变换添加到新变换中?

如果是,SceneKit函数在哪里添加像SCNMatrix4Mult这样的矩阵用于乘法,或者我是否必须自己使用Metal编写它?

如果没有,我错过了矩阵计算?

我不想使用GLKit

enter image description here

2 个答案:

答案 0 :(得分:10)

所以我的理解是你想要沿着自己的X轴移动Box节点(而不是它的父X轴)。并且因为Box节点被旋转,其X轴不与其父节点对齐,所以你有问题转换两个坐标系之间的平移。

节点层次结构

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

使用转换矩阵

沿自己的 X轴移动boxNode

// 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;

请注意:在汇总矩阵时,订单很重要

另一种选择:

使用SCNNode坐标转换功能,看起来更直接

// 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;

答案 1 :(得分:0)

以@Sulevus的正确答案为基础,这是SCNNode的扩展,它通过在Swift中使用convertVector而不是convertPosition转换来简化事情。

我已经完成了var返回单位向量的操作,并提供了SCNVector3乘法的重载,所以您可以说类似

let action = SCNAction.move(by: 2 * cameraNode.leftUnitVectorInParent, duration: 1)


public extension SCNNode {
    var leftUnitVectorInParent: SCNVector3 {
        let vectorInSelf = SCNVector3(x: 1, y: 0, z: 0)
        guard let parent = self.parent else { return vectorInSelf }
        // Convert to parent's coord system
        return parent.convertVector(vectorInSelf, from: self)
    }
    var forwardUnitVectorInParent: SCNVector3 {
        let vectorInSelf = SCNVector3(x: 0, y: 0, z: 1)
        guard let parent = self.parent else { return vectorInSelf }
        // Convert to parent's coord system
        return parent.convertVector(vectorInSelf, from: self)
    }

    func *(lhs: SCNVector3, rhs: CGFloat) -> SCNVector3 {
        return SCNVector3(x: lhs.x * rhs, y: lhs.y * rhs, z: lhs.z * rhs)
    }
    func *(lhs: CGFloat, rhs: SCNVector3) -> SCNVector3 {
        return SCNVector3(x: lhs * rhs.x, y: lhs * rhs.y, z: lhs * rhs.z)
    }
}