Reputation: 191
I want to rotate a SCNNode, which is a painting image.
I can rotate it on the floor, but I cannot rotate it correctly on wall.
(I am using UIRotationGestureRecognizer)
...
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
// Add the painting
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.eulerAngles = SCNVector3(newPaintingNode.eulerAngles.x + (-Float.pi / 2), newPaintingNode.eulerAngles.y, newPaintingNode.eulerAngles.z)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
self.currentAngleY = newPaintingNode.eulerAngles.y
self.currentAngleZ = newPaintingNode.eulerAngles.z
augmentedRealityView.scene.rootNode.addChildNode(self.paintingNode!)
grid.removeFromParentNode()
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let currentNode = self.paintingNode {
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if self.paintingAlignment == "horizontal" {
log.verbose("rotate horizontal!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed {
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
}
} else if self.paintingAlignment == "vertical" {
log.verbose("rotate vertical!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.z
if gesture.state == .changed {
currentNode.eulerAngles.z = currentAngleZ + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleZ = currentNode.eulerAngles.z
}
}
}
}
Does anyone know how can I rotate it correctly on wall? Thank you!
Upvotes: 2
Views: 1305
Reputation: 58063
You're using eulerAngles
instance property in your code:
var eulerAngles: SCNVector3 { get set }
According to Apple documentation:
SceneKit applies
eulerAngles
rotations relative to the node’s pivot property in the reverse order of the components: firstroll
(Z), thenyaw
(Y), thenpitch
(X).
Gimbal lock is the loss of one degree of freedom in a three-dimensional, three-gimbal mechanism that occurs when the axes of two of the three gimbals are driven into a parallel configuration, "locking" the system into rotation in a degenerate two-dimensional space.
So you need to use a four-component rotation property:
var rotation: SCNVector4 { get set }
The four-component rotation vector specifies the direction of the rotation axis in the first three components (XYZ) and the angle of rotation, expressed in
radians
, in the fourth (W).
currentNode.rotation = SCNVector4(x: 1,
y: 0,
z: 0,
w: -Float.pi / 2)
If you want to know more about SCNVector4
structure and four-component rotation (and its W component expressed in radians) look at THIS POST and THIS POST.
P.S.
In RealityKit framework instead of SCNVector4
structure you need to use SIMD4<Float>
generic structure or simd_float4
type alias.
var rotation: simd_float4 { get set }
Upvotes: 2
Reputation: 191
I tried SCNNode eulerAngles
and SCNNode rotation
and I have no luck...
I guess that is because I invoked SCNNode.transform()
when I add the painting. When later I modify SCNNode.eulerAngles
and invoke SCNNode.rotation()
, they may have influence on the first transform
.
I finally get it working using SCNMatrix4Rotate
, not using eulerAngles
and rotation
.
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.transform = SCNMatrix4Rotate(newPaintingNode.transform, -Float.pi / 2.0, 1.0, 0.0, 0.0)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
...
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let _ = self.paintingNode {
// Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if gesture.state == .began {
self.rotationZ = rotation
}
if gesture.state == .changed {
let diff = rotation - self.rotationZ
self.paintingNode?.transform = SCNMatrix4Rotate(self.paintingNode!.transform, diff, 0.0, 0.0, 1.0)
self.rotationZ = rotation
}
}
}
And the above code works on both vertical and horizontal plane.
Upvotes: 0