Reputation: 11
I'm new to Swift programming and SceneKit framework. I'm try to build a Rubik's twist app and what I've got so far is the chain of pieces positioned at my camera's center. Rubik's twist chain
This is how I achieved it:
var snake = [SCNNode]()
var bounds_z = Float()
var bounds_x = Float()
var j = 0
for i in 0...11{
let piece_scene_1 = SCNScene(named: "piece.scnassets/piece.scn")!
let piece_scene_2 = SCNScene(named: "piece.scnassets/piece.scn")!
piece_scene_1.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
piece_scene_2.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
bounds_z = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.max.z ?? 0.0))
bounds_x = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.min.x ?? 0.0))
snake.append(piece_scene_1.rootNode.childNodes[0])
snake.append(piece_scene_2.rootNode.childNodes[0])
snake[j].name = "piece_\(j)"
snake[j+1].name = "piece_\(j+1)"
snake[j+1].rotation = SCNVector4(0, 1, 0, Float.pi)
snake[j].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i)*bounds_z)
snake[j+1].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i+1)*bounds_z)
scene.rootNode.addChildNode(snake[j])
scene.rootNode.addChildNode(snake[j+1])
j+=2
}
cameraNode.look(at: snake[(snake.count/2)-1].position)
My problem is now to make a set of pieces rotate when tapped. My idea was to make a container node, add all nodes before the one tapped and then rotate the container around the normal axis of the tapped piece face (adjacent to the container). This is my try:
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer){
let sceneView = self.view as! SCNView
let p = gestureRecognize.location(in: sceneView)
let hitResults = sceneView.hitTest(p, options: [:])
if hitResults.count > 0 {
// retrieved the first clicked object
let result: SCNHitTestResult = hitResults[0]
let container = SCNNode()
let id_name = result.node.name
let id_array = id_name?.components(separatedBy: CharacterSet.decimalDigits.inverted)
// add nodes to container
for item in id_array! {
if let id = Int(item){
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
// add container to scene
scene.rootNode.addChildNode(container)
// get container orientation
var GLKQuat = GLKQuaternionMake(container.orientation.x, container.orientation.y, container.orientation.z, container.orientation.w)
// get future orientation
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
// assign new orientation to container
GLKQuat = GLKQuaternionMultiply(GLKQuat, multiplier)
container.orientation = SCNQuaternion(GLKQuat.x, GLKQuat.y, GLKQuat.z, GLKQuat.w)
// maintain childs position after rotation
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)}
}
}
}
}
Problem is that since container is a scene.rootNode.child, will rotate around its z axis like this: Rubiks twist chain after rotation
Upvotes: 0
Views: 898
Reputation: 11
After days of struggling I finally found a solution:
let container = SCNNode()
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
container.name = "body_to_rotate"
// add the container to scene root node
scene.rootNode.addChildNode(container)
// transform from container to tapped node
let container_transform = container.parent!.convertTransform(container.transform, to: result.node)
container.transform = container_transform
// add container as child node to tapped node
result.node.addChildNode(container)
// make the rotation happen
let old_rotation = result.node.orientation
var quat_rot = GLKQuaternionMake(old_rotation.x, old_rotation.y, old_rotation.z, old_rotation.w)
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
quat_rot = GLKQuaternionMultiply(quat_rot, multiplier)
result.node.orientation = SCNQuaternion(quat_rot.x, quat_rot.y, quat_rot.z, quat_rot.w)
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)
}
// delete container
container.removeFromParentNode()
Upvotes: 1
Reputation: 5451
Rotation around z axis is caused by this line of code
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
If you want to rotate around another axis you have to specify the x, y and z component: from the Apple documentation
Upvotes: 0