How to Move a Rotated Scnnode in Scenekit

How to move a rotated SCNNode in SceneKit?

So my understanding is that you want to move the Box Node along its own X axis (not it's parents X axis). And because the Box Node is rotated, its X axis is not aligned with its parent's one, so you have the problem to convert the translation between the two coordinate systems.

The node hierarchy is

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

Using Transformation Matrices

To move boxNode along its own X axis

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

Please Note: The order matters when multiplying matrices

Another option:

Using SCNNode coordinate conversion functions, looks more straight forward to me

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

Rotate SCNNode then move relative to rotation?

Assuming I have understood you correctly, once you have rotated your SCNNode, you want to move it forward it in that direction.

This can be done by using the worldFront value which is simply:

The local unit -Z axis (0,0,-1) in world space.

As such this may be the answer you are looking for:

    //1. Create A Node Holder
let nodeToAdd = SCNNode()

//2. Create An SCNBox Geometry
let nodeGeometry = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
nodeGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
nodeGeometry.firstMaterial?.lightingModel = .constant
nodeToAdd.geometry = nodeGeometry

//3. Position It 1.5m Away From The Camera
nodeToAdd.position = SCNVector3(0, 0, -1.5)

//4. Rotate The Node By 90 Degrees On It's Y Axis
nodeToAdd.rotation = SCNVector4Make(0, 1, 0, .pi / 2)

//5. Add The Node To The ARSCNView
augmentedRealityView.scene.rootNode.addChildNode(nodeToAdd)

//6. Use The World Front To Move The Node Forward e.g

/*
nodeToAdd.simdPosition += nodeToAdd.simdWorldFront * 1.2
*/

nodeToAdd.simdPosition += nodeToAdd.simdWorldFront

//7. Print The Position
print(nodeToAdd.position)

/*
SCNVector3(x: -0.999999881, y: 0.0, z: -1.50000024)
*/

How to move a SCNNode in the direction it is pointing at after an rotation is applied

I've found that the easiest way to do this is to grab the third row of the node's worldTransform property, which corresponds to its z-forward axis.

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

ship.position += getZForward(ship) * speed // nb scalar multiply requires overload of * func
// 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

See the discussion here Getting direction that SCNNode is facing

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

Scenekit - multiple rotation of SCNNode and direction of axes

After further research i realised i chose completely wrong approach. The way to achieve what i want was to rotate the node using the axes of the rootNode - this way i don't need to worry about local axes of my node.

EDIT: updated code based on Xartec's suggestions

let rotation = SCNMatrix4MakeRotation(angle,  x, y, Float(0))
let newTransform = SCNMatrix4Mult(bricksNode.transform, rotation)
let animation = CABasicAnimation(keyPath: "transform")
animation.fromValue = bricksNode.transform
animation.toValue = scnScene.rootNode.convertTransform(newTransform,from: nil)
animation.duration = 0.5
bricksNode.addAnimation(animation, forKey: nil)

How do I rotate a SCNNode to look at another node, whilst keeping its roll and pitch level with the camera?

Have you tried setting isGimbalLockEnabled to true to constrain the roll rotation? In the documentation Apple mentions: "For example, when constraining a camera to follow a moving object, setting this property to true ensures that the horizon remains level from the camera’s point of view."

If that still isn't what you're looking for, you may need to write a custom constraint using the class function SCNTransformConstraint.orientationConstraint. You could write the constraint as a secondary constraint that basically restricts rotations on the X and Z axes, or you could write your own look-at constraint with more restrictions (in which case I'd recommend looking at the simd.look(at:) function and then restricting the orientation axes from there).

This should get you most of the way there, or I can add some code in later if it doesn't seem to be working. Good luck!



Related Topics



Leave a reply



Submit