1
votes

I am learning how to play with SceneKit, and I am deeply confused with methods/properties that involve a SCNVector4, such as SCNPhysicsBody.applyTorque and SCNNode.rotation. Apparently, you can express a rotation of a 3D object with just 4 values. I do not understand why this is at all.

I searched online for "express 3d rotation with 4d vector" and found lots of stuff about rotations in 4D space, which is not what I want. I also see a lot of webpages that talk about sin and cos, but I failed to understand what they mean. My knowledge of sin and cos is only limited to finding the angles/side lengths of right angled triangles.

In my mind, a rotation of a 3D object needs to be expressed with 7 values i.e. a 7D vector:

  • 3 values for the position of one end of the rotation axis
  • 3 values for the position of the other end of the rotation axis
  • 1 value for the angle you rotate by

I think that I understood 2D rotation in SpriteKit pretty well: each node has an anchor point and so the rotation property is just how many radians about the anchor. However, I can't find such an anchor point in SCNNode. When I look up "anchor point scnnode" I get stuff about ARSCNNode which is not what I want.

I also looked at this, which says:

The x-, y- and z-components determine the rotation axis, while the w-component determines the rotation angle, that is, the magnitude of the torque.

How can just the x, y, z components specify the rotation axis?

Can you explain how I can wrap my head around this craziness?

2
IIRC you should only need three coordinates to uniquely specify a rotation (assuming the rotation axis includes the origin) - for example, two angles to specify the rotation axis, and one to specify the rotation angle. See en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions for more info.Oliver Charlesworth
@OliverCharlesworth I can kind of get how you express the rotation axis with two angles if I try really hard. I guess now the question becomes "where on the node is the 'origin'" and "how do you express 2 angles with 3 scalars"...Sweeper
Well, for a start you only need two scalars to specify two angles :) The choice of origin is up to you - it's basically how you choose to model your coordinate system. You can express a rotation around something other than the model by first applying a translation, then the rotation, and then the opposite translation.Oliver Charlesworth
@OliverCharlesworth If I am writing my own game engine, then I suppose I can specify my own origin... But I am using SceneKit, so I have to stick with its APIs. And somehow the applyTorque method takes a SCNVector4. This is what I am trying to understand.Sweeper
Alas I know nothing about SceneKit :/ My comment is really just to address the "need 7 values" thing.Oliver Charlesworth

2 Answers

3
votes

I guess now the question becomes "where on the node is the 'origin'"

That is given by (minus the) SCNNode.position (source). This is the translation applied to it with respect to its parent node.

... and "how do you express 2 angles with 3 scalars" ...

According to Euler's Displacement Theorem, any rotation in 3D can be represented by an axis and a rotation angle. These can in turn be stored as:

  1. Either (as @OliverCharlesworth suggested) 2 spherical polar angles specifying the axis and another the rotation angle
  2. Or a vector parallel to the axis with magnitude proportional to the clockwise (or anti-clockwise) rotation around it; this is e.g. typically how angular velocity is represented

SceneKit merely uses a 4D vector to avoid having to re-compute the axis in Cartesian coordinates (needed for (1)) or retrieve the angle (for (2)).

In my mind, a rotation of a 3D object needs to be expressed with 7 values i.e. a 7D vector:

  • 3 values for the position of one end of the rotation axis
  • 3 values for the position of the other end of the rotation axis
  • 1 value for the angle you rotate by

Here lies the problem: you are confusing two definitions of the word "axis", which can be either:

  • A fixed line in space, e.g. the X-axis
  • The common direction of a family of lines parallel to each other

By convention the second one is used for specifying rotations. The center of this rotation is the central position of the node (often the midpoint of its AABB).

0
votes

I realised that I could write a little "SceneKit console" thing to test out what each of the four values in the vector does.

I put a textfield, a button and a SCNView into a view controller's view. In the scene, I added a ground node with a kinematic physics body and a box node with a dynamic physics body. I then made it so that when the button is tapped, the command entered in the text field would run. This is how I parsed the command:

func executeCommand(_ command: String) {
    let parts = command.components(separatedBy: " ")
    let node: SCNNode
    guard let nodeString = parts.first else { return }
    switch nodeString {
    case "box": node = box
    case "ground": node = ground
    default: return
    }
    guard let selector = parts.dropFirst().first else { return }
    switch selector {
    case "applyTorque":
        if parts.count == 6 {
            let x = Double(parts[2])!
            let y = Double(parts[3])!
            let z = Double(parts[4])!
            let w = Double(parts[5])!
            let direction = SCNVector4(x, y, z, w)
            node.physicsBody?.applyTorque(direction, asImpulse: true)
        }
    // I decided to add other commands as well, but they're irrelevant so I left them out
    default: break
    }
}

Now, I tried entering commands like box applyTorque 1 0 0 1 and saw what happened. Here are my findings:

  • The first three values represent how much the node should be rotated about the x, y, and z axes respectively.
  • The last value seem to be like an "overall force". If I set this to 0 the box just wouldn't move regardless of what the other values are.
  • Rotation about the y axis seems "easier". A Y value of 1 can "spin" the cube 90 degrees, but an X value of 1 can only raise the box up from one side a little bit.

Here's a screenshot:

enter image description here