Getting direction that SCNNode is facing
Asked Answered
S

2

6

I am getting lost in how nodespace coordinates and rotation are handled in scenekit.

How do I get the direction a node is facing after rotation so I can then apply force in that direction.

I assumed that a force along the -z axis applied to the node would always move it forward relative to the node however this is not the case.

Sandbox answered 9/11, 2014 at 3:43 Comment(0)
G
2

Negative z is the "facing" direction of a node only in its own coordinate space (i.e. the one its children and geometry are positioned in). When you apply a force, you're working in the coordinate space containing the node.

So, if you want to apply a force in a node's forward direction, you'll need to convert a vector like {0,0,-1} from the node's space to its parent's using a method like convertPosition:toNode:.

Gelatinate answered 9/11, 2014 at 3:58 Comment(2)
I've been playing around with that method, however I can't seem to get exactly what I am looking for. Converting a vector like (0,0,1) to the presentationNode coordinate space of the node intended to move works as intended until the node is moving, once moving the conversion no longer represents the facing direction of the node.Sandbox
@Sandbox would you mind sharing your solution with those still in need of a solution, please?Polygynist
T
6

I also couldn't get convertPosition:toNode to work. Variations on ship.convertPosition(SCNVector3(0,0,-0.1), toNode: ship.parentNode) made the node fly off at unpredictable directions and speeds.

What worked for me, was to grab the third row of the node's worldTransform matrix, which corresponds to it's z-forward axis:

func getZForward(node: SCNNode) -> SCNVector3 {
    return SCNVector3(node.worldTransform.m31, node.worldTransform.m32, node.worldTransform.m33)
}

ship.position += getZForward(ship) * speed
// if node has a physics body, you might need to use the presentationNode, eg:
// getZForward(ship.presentationNode)
// though you probably don't want to be directly modifying the position of a node with a physics body

// overrides for SCNVector arithmetic
#if os(iOS)
    typealias VecFloat = Float
#elseif os (OSX)
    typealias VecFloat = CGFloat
#endif

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

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

func += (lhs: inout SCNVector3, rhs: SCNVector3) {
    lhs = lhs + rhs
}

If the node has a physics body you might have to pass the node.presentationNode into the function.

When iOS 11/ High Sierra come out, there'll be less need for overriding SCNVector3, because all the SCNNode properties have simd equivalents, so as well as .position there's .simdPosition and so on, and there are a lot of common simd operations built in.

iOS 11 update

iOS 11 adds handy convenience functions for getting the orientation of a node. In this case the worldForward property is the one you want. Also, all of the properties on SCNNode that return SCNVector and matrix types now have versions that return simd types. Because simd already has overloads for the arithmetic operators, you no longer need to add sets of arithmetic overrides for the SCNVector and SCNMatrix types.

So we can get rid of out getZForward method above, and just have the line:

ship.simdPosition += ship.simdWorldFront * speed

The other handy set of methods that iOS 11 adds, are a set of convertVector methods, to complement the existing convertPosition methods. convertVector is the equivalent of multiplying the matrix by the vector with 0 in the w position, so that the translation of the matrix is ignored. These are the appropriate methods to use for converting things like normals, directions and so on from one node's space to another.

Because the accepted answer uses convertPosition, I believe it will only produce correct results for nodes whose translation is the 0,0,0 origin

Trouper answered 14/6, 2016 at 0:26 Comment(4)
Binary operation "+" cannot be performed on SCNVector:Shay
@johndoe I updated the answer with the SCNVector overridesTrouper
worldFront is the property you are looking for . . .Pin
thanks, ship.simdPosition += ship.simdWorldFront * speed was exactly what I needed. It gave me the front facing vector regardless what my rotation was.Lippold
G
2

Negative z is the "facing" direction of a node only in its own coordinate space (i.e. the one its children and geometry are positioned in). When you apply a force, you're working in the coordinate space containing the node.

So, if you want to apply a force in a node's forward direction, you'll need to convert a vector like {0,0,-1} from the node's space to its parent's using a method like convertPosition:toNode:.

Gelatinate answered 9/11, 2014 at 3:58 Comment(2)
I've been playing around with that method, however I can't seem to get exactly what I am looking for. Converting a vector like (0,0,1) to the presentationNode coordinate space of the node intended to move works as intended until the node is moving, once moving the conversion no longer represents the facing direction of the node.Sandbox
@Sandbox would you mind sharing your solution with those still in need of a solution, please?Polygynist

© 2022 - 2024 — McMap. All rights reserved.