Pascal
Pascal

Reputation: 2700

SceneKit: rotate node around itself

I want to rotate a node around it's x-axis (more specifically, I want to turn a wheel).

The wheel is a subnode of a car. I found a couple of threads regarding this problem on stackoverflow, e.g. this but non of the solutions worked for me:

I tried the following code:

    if let wheel = sceneView.scene.rootNode.childNode(withName: "WheelRearL", recursively: true) {

        // also tried:
        // wheel.pivot = SCNMatrix4MakeTranslation(0.5, 0.5, 0.5)

        let (minVec, maxVec) = (wheel.boundingBox)
        wheel.pivot = SCNMatrix4MakeTranslation(-25, (maxVec.y - minVec.y) / 2 + minVec.y, (maxVec.z - minVec.z) / 2 + minVec.z)

    }

But the wheel still turns around the center of the whole car.

The following code is used for the rotation itself, but should not be relevant to the solution I think:

        let spin = CABasicAnimation.init(keyPath: "rotation")
        spin.fromValue = NSValue(scnVector4: SCNVector4(x: 25, y: 0, z: 0, w: 0))
        spin.toValue = NSValue(scnVector4: SCNVector4(x:25, y: 0, z: 0, w: Float(CGFloat(2 * M_PI))))
        spin.duration = 3
        spin.repeatCount = .infinity
        let wheel = sceneView.scene.rootNode.childNode(withName: "WheelRearL", recursively: true)
        wheel?.addAnimation(spin, forKey: "spin around")

(btw: I need to change the pivot in code, without setting an additional container node in SceneKit interface builder and moving it to the nodes center)

Upvotes: 6

Views: 3104

Answers (3)

Rufus
Rufus

Reputation: 679

I know it is very late to answer. But I found a way to rotate a node around the axis of itself . The key point is : Create a parent node and add destination node as a child node. You can use the code below to rotate "hudNode" around its X-axis:

 let containerNode = SCNNode()
 containerNode.addChildNode(hudNode)  
 let roteAction = SCNAction.rotate(by: 3.14, around: SCNVector3(1,0,0), duration: 0)
        hudNode.runAction(roteAction)

Then you can put containerNode to anywhere you like.

Upvotes: 1

Clay
Clay

Reputation: 1761

The local axis for the node is usually not at the centre of the geometry like the case of this heart.

enter image description here

You have two options to re-centre the axis. You can add a function to your code.

 func centerPivot(for node: SCNNode) {
    var min = SCNVector3Zero
    var max = SCNVector3Zero
    node.__getBoundingBoxMin(&min, max: &max)
    node.pivot = SCNMatrix4MakeTranslation(
        min.x + (max.x - min.x)/2,
        min.y + (max.y - min.y)/2,
        min.z + (max.z - min.z)/2
    )
}

to call the function

centrePivot(for: wheelNode)

which you apply to the node like so. This will place the pivot at the centre of the bounding box of the node. But I’ve found you sometimes need to modify that function depending on where you need to pivot from... it can be a trial and error process until its position perfectly.

The other way is to open the collada (dae file) up in 3D authoring tool such as SketchUp and make in this case (each wheel geometry) into a component (if it isn’t already). From there you can reset the components axis, and reposition the axis to the centre of the geometry for each wheel.

In many cases, free dae models for cars, helicopters, and other models you want to animate etc were not created by the author with the intend to animate, so the geometry grouping and axis for the animating geometry isn’t in the correct pivot position. You might also want the axis to be on hinge for a swinging door etc so it will be off-centre and that can be fiddle to get right. So I think its best to know how to do this axis adjustment (for pivot points) in the 3D model itself.

Upvotes: 5

Pascal
Pascal

Reputation: 2700

Alright, somehow I found a working solution: (don't know if that's the best way - but it definitely works for me.)

    if let w = sceneView.scene.rootNode.childNode(withName: name, recursively: true) {
        let (minVec, maxVec) = w.boundingBox
        w.position.x = -25 / 100
        w.position.y = ((maxVec.y - minVec.y) / 2 + minVec.y) / 100
        w.position.z = ((maxVec.z - minVec.z) / 2 + minVec.z) / 100
        w.pivot = SCNMatrix4MakeTranslation(-25, (maxVec.y - minVec.y) / 2 + minVec.y, (maxVec.z - minVec.z) / 2 + minVec.z)
    }

Upvotes: 0

Related Questions