I have an SCNBox
in an SCNScene. Once the scene animates the SCNBox
changes is orientation which can be seen by checking its presentationNode.ori
node.orientation will give you a quaternion. You can also use node.eulerAngles or node.rotation (axis angle) if that makes your maths easier.
In all cases I think you need to apply the inverse rotation to the vector [0, 1, 0] (up vector). The result vector (V) will give you the direction of the up face. To then find the up face, compare the dot products of the box faces and V. The highest dot product is the one of the face that is facing up.
(An alternative way is to rotate the box's faces instead and do the dot products with [0,1,0].)
Here is a method that returns the index of the face that's facing up. It assumes that "boxNode" is a box made of 6 faces with the following (arbitrary) order: front / right / back / left / up / bottom. It returns the index of the face that is facing up. Don't forget to import then . For an arbitrary mesh, you would have to use the face normals instead of "boxNormals" (which is not obvious to compute since SceneKit meshes have one normal per vertex, not one normal per face, so you would have to compute the normals per face yourself).
- (NSUInteger) boxUpIndex:(SCNNode *)boxNode
{
SCNVector4 rotation = boxNode.rotation;
SCNVector4 invRotation = rotation; invRotation.w = -invRotation.w;
SCNVector3 up = SCNVector3Make(0,1,0);
//rotate up by invRotation
SCNMatrix4 transform = SCNMatrix4MakeRotation(invRotation.w, invRotation.x, invRotation.y, invRotation.z);
GLKMatrix4 glkTransform = SCNMatrix4ToGLKMatrix4(transform);
GLKVector3 glkUp = SCNVector3ToGLKVector3(up);
GLKVector3 rotatedUp = GLKMatrix4MultiplyVector3(glkTransform, glkUp);
//build box normals (arbitrary order here)
GLKVector3 boxNormals[6] = {{{0,0,1}},
{{1,0,0}},
{{0,0,-1}},
{{-1,0,0}},
{{0,1,0}},
{{0,-1,0}},
};
int bestIndex = 0;
float maxDot = -1;
for(int i=0; i<6; i++){
float dot = GLKVector3DotProduct(boxNormals[i], rotatedUp);
if(dot > maxDot){
maxDot = dot;
bestIndex = i;
}
}
return bestIndex;
}