This seems to be a popular question. A few minutes ago I answered a similar question. See if this function helps center the axis of the node below without moving its position.
set the SCNNode axis to the bounding center of the volume without changing the position of the node
func centerPivotWithOutMoving(for node: SCNNode) -> SCNNode { let initialPos = treenode.presentation.position 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 ) let correctX = Float(min.x + (max.x - min.x)/2) let correctY = Float(min.y + (max.y - min.y)/2) let correctZ = Float(min.z + (max.z - min.z)/2) if node.convertVector(SCNVector3(0,0,1), from: parentNode).z < 0 { // if blue local z-axis is pointing downwards node.position = SCNVector3(initialPos.x - correctX, initialPos.y - correctY, initialPos.z - correctZ) } else { // if blue local z-axis is pointing upwards node.position = SCNVector3(initialPos.x + correctX, initialPos.y + correctY, initialPos.z + correctZ) } return node }
use the above function:
centerPivotWithOutMoving(for: treenode)
Edit: Ive realized that my approach above was not the most efficient way to solve the centerPivotWithOutMoving center for all cases , because in some cases the local axis of your child node trying to center the rotation axis cannot be coplanar (the local axis and the parent axis sharing the same same plane) with the ParentNode axis. It is best to place a wrapper around each node for which you want to center the axis.
something like this will wrap each node fixing this potential problem when the axis is not coplanar.
for child in (self.parentNode?.childNodes)! { let wrapperNode = SCNNode() child.geometry?.firstMaterial?.lightingModel = .physicallyBased wrapperNode.addChildNode(child) wrapperNode.name = child.name! + "_wrapper" self.parentNode.addChildNode(wrapperNode) }
I have done this for now to call CenerPivotWithOutMoving in all child Nodes while clicking on the scene.
for child in parentNode.childNodes { let delayInSeconds = Double(0.1) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) { self.centerPivotWithOutMoving(for: child) } }
here is a modified centerPivotWithOutMoving, with some code added to test the local axis against the parent to show that they are now 1,1,1 for all shell nodes. Without wrapper nodes, these numbers were in most cases never 1,1,1.
func centerPivot(for node: SCNNode) -> 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 ) return node } func centerPivotWithOutMoving(for node: SCNNode) -> SCNNode { let initialPos = node.presentation.position var min = SCNVector3Zero var max = SCNVector3Zero node.__getBoundingBoxMin(&min, max: &max) // corrective factor for each axis let correctX = Float(min.x + (max.x - min.x)/2) let correctY = Float(min.y + (max.y - min.y)/2) let correctZ = Float(min.z + (max.z - min.z)/2) var xAxis = node.convertVector(SCNVector3(1,0,0), from: parentNode).x var yAxis = node.convertVector(SCNVector3(0,1,0), from: parentNode).y var zAxis = node.convertVector(SCNVector3(0,0,1), from: parentNode).z // check the local axis is coplanar with parentNode axis if (xAxis == 1) && (yAxis == 1) && (zAxis == 1) { print(node.name) centerPivot(for: node) node.position = SCNVector3(initialPos.x + correctX, initialPos.y + correctY, initialPos.z + correctZ) } else { print("node axis is not coplanar with parent") } return node }