Add SCNText to SCNPlane - swift

i am adding an SCNPlane using
artPlane = SCNPlane(width: CGFloat(widthRequired) * 0.0254,
height: CGFloat(size["width"]!) * 0.0254)
artworkNode = SCNNode(geometry: artPlane)
artworkNode?.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "loadingar")
let translation = SCNMatrix4MakeTranslation(0, -1, 0)
let rotation = SCNMatrix4MakeRotation(Float.pi / 2, 0, 0, 1)
artworkNode?.geometry?.firstMaterial?.diffuse.contentsTransform = SCNMatrix4Mult(translation, rotation)
artworkNode?.geometry?.firstMaterial?.isDoubleSided = true
artworkNode?.simdTransform = matrix
self.sceneView.scene.rootNode.addChildNode(artworkNode!)
Now i want to add an SCNText to the center of the above plane, how do i specify the position of the new textNode so that its in the center of the Plane?

Related

SCNNode rotation around a generic axis with animation

I need to rotate the gray bar around the red dot (see picture below).
I managed to do that translating the center point of the bar, rotating the bar and then translating the bar back to the original position.
Down here my code:
struct TestView2: View {
var scene: SCNScene
var arm: SCNNode
var length = 16.0
var with = 1.0
init() {
scene = SCNScene()
scene.setAttribute(400000, forKey: SCNScene.Attribute.endTime.rawValue)
let rootNode = scene.rootNode
let armShape = SCNBox(width: with, height: 0.5, length: length, chamferRadius: 0)
armShape.firstMaterial?.diffuse.contents = NSColor.gray
arm = SCNNode()
arm.geometry = armShape
arm.position = SCNVector3(0, 0, 0)
rootNode.addChildNode(arm)
let axisShape = SCNCylinder(radius: with/2, height: 0.51)
axisShape.firstMaterial?.diffuse.contents = NSColor.red
let axis = SCNNode()
axis.geometry = axisShape
axis.position = SCNVector3(0, 0, length/2-with/2)
rootNode.addChildNode(axis)
// Camera
let camera = SCNNode()
camera.camera = SCNCamera()
camera.camera?.usesOrthographicProjection = true
camera.camera?.orthographicScale = 15
camera.camera?.zNear = 0
camera.camera?.zFar = 100
camera.position = SCNVector3(0, 0, 50)
let cameraOrbit = SCNNode()
cameraOrbit.addChildNode(camera)
rootNode.addChildNode(cameraOrbit)
var eulerAngles = cameraOrbit.eulerAngles
eulerAngles.x = CGFloat(Float.pi / 2.0)
eulerAngles.y = 0
eulerAngles.z = 0
cameraOrbit.eulerAngles = eulerAngles
}
var body: some View {
VStack {
SceneView(scene: scene, options: [.allowsCameraControl, .autoenablesDefaultLighting])
Button("Rotate") {
let currentTransform = arm.transform
let translationTransform = SCNMatrix4MakeTranslation(0, 0, length/2-with/2)
let rotationTrasform = SCNMatrix4MakeRotation(90 * .pi / 180.0, 0, 1, 0)
let backTranslationTransform = SCNMatrix4MakeTranslation(0, 0, -(length/2-with/2))
let newTransform = SCNMatrix4Mult(backTranslationTransform,
SCNMatrix4Mult(rotationTrasform,
SCNMatrix4Mult(translationTransform, currentTransform)))
arm.transform = newTransform
}
}
}
}
The bar rotate as expected. I then added an animation:
...
let animation = CABasicAnimation(keyPath: "transform")
let currentTransform = arm.transform
let translationTransform = SCNMatrix4MakeTranslation(0, 0, length/2-with/2)
let rotationTrasform = SCNMatrix4MakeRotation(90 * .pi / 180.0, 0, 1, 0)
let backTranslationTransform = SCNMatrix4MakeTranslation(0, 0, -(length/2-with/2))
let newTransform = SCNMatrix4Mult(backTranslationTransform,
SCNMatrix4Mult(rotationTrasform,
SCNMatrix4Mult(translationTransform, currentTransform)))
animation.fromValue = currentTransform
animation.toValue = newTransform
animation.duration = 1.0
arm.addAnimation(animation, forKey: nil)
arm.transform = newTransform
...
The result is very clumsy though because the translations before and after the rotation are visible:
Since I am new to SceneKit, I think my approach to rotate the bar is completely wrong.
How can I improve the code to have both the rotation and animation at the same time?

swift SceneKit calculate node angle

i trying to add a node (pin) to sphere node (when taping on sphere node), but can't correctly calculate an angle, can u help me pls ?
private func addPinToSphere(result: SCNHitTestResult) {
guard let scene = SCNScene(named: "art.scnassets/tree.scn") else { return }
guard let treeNode = scene.rootNode.childNode(withName: "Trunk", recursively: true) else { return }
let x = CGFloat(result.worldCoordinates.x)
let y = CGFloat(result.worldCoordinates.y)
treeNode.position = result.worldCoordinates
treeNode.eulerAngles = SCNVector3((x) * .pi / CGFloat(180), 0, (y - 90) * .pi / CGFloat(180)) // y axis is ignorable
result.node.addChildNode(treeNode)
}
when run code i have this
but want like this
You can do it similar to this code. This is a missile launcher, but it's basically the same thing. Your pin = my fireTube, so create a base node, then add a subnode with your materials to it and rotate it into place. You'll have to experiment with where to place the tube initially, but once you get it into the right place the lookat constraint will always point it in the right direction.
case .d1, .d2, .d3, .d4, .d5, .d6, .d7, .d8, .d9, .d10:
let BoxGeometry = SCNBox(width: 0.8, height: 0.8, length: 0.8, chamferRadius: 0.0)
let vNode = SCNNode(geometry: BoxGeometry)
BoxGeometry.materials = setDefenseTextures(vGameType: vGameType)
let tubeGeometry = SCNTube(innerRadius: 0.03, outerRadius: 0.05, height: 0.9)
let fireTube = SCNNode(geometry: tubeGeometry)
tubeGeometry.firstMaterial?.diffuse.contents = data.getTextureColor(vTheme: 0, vTextureType: .barrelColor)
fireTube.position = SCNVector3(0, 0.2, -0.3)
let vRotateX = SCNAction.rotateBy(x: CGFloat(Float(GLKMathDegreesToRadians(-90))), y: 0, z: 0, duration: 0)
fireTube.runAction(vRotateX)
vNode.addChildNode(fireTube)
return vNode
Then set target on your base node and your subnode will rotate with it:
func setTarget()
{
node.constraints = []
let vConstraint = SCNLookAtConstraint(target: targetNode)
vConstraint.isGimbalLockEnabled = true
node.constraints = [vConstraint]
}
In your case, target equals center mass of your sphere and the pin will always point to it, provided you built and aligned your box and pin correctly.

simd_float4x4 Columns

I want to translate a plane without rotating the image. For any reason my image is being rotated.
var translation = matrix_identity_float4x4
translation.colum = -0.2
let transform = simd_mul(currentFrame.camera.transform, translation)
planeNode.simdWorldTransform = matrix_multiply(currentFrame.camera.transform, translation)
Also, I notice that matrix_identity_float4x4 contains 4 columns but the documentation is not available.
Why 4 columns? Are there the frame of the plane?
The simplest way to do it is to use the following code for positioning:
let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 20, height: 20)
// At first we need to rotate a plane about its x axis in radians:
planeNode.rotation = SCNVector4(1, 0, 0, -Double.pi/2)
planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red
planeNode.position.x = 10
planeNode.position.z = 10
// planeNode.position = SCNVector3(x: 10, y: 0, z: 10)
scene.rootNode.addChildNode(planeNode)
or this way:
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
scene.rootNode.addChildNode(cameraNode)
let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 20, height: 20)
planeNode.rotation = SCNVector4(1, 0, 0, -Double.pi/2)
planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red
let distance: Float = 50
planeNode.simdPosition = cameraNode.simdWorldFront * distance // -Z axis
planeNode.simdPosition = cameraNode.simdWorldRight * distance // +X axis
scene.rootNode.addChildNode(planeNode)
If you wanna know more about matrices used in ARKit and SceneKit frameworks just look at Figure 1-8 Matrix configurations for common transformations.
Hope this helps.

SCNTransformConstraint not allowing to restrict Eulerangle

I want to limit the angle of the camera in Scenekit to not allow the camera to look upwards (point to the sky). For some reason I am not getting the SCNTransformConstraint right. What to do?
let cameraNode = SCNNode()
let camera = SCNCamera()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 10, z: 30)
let center = SCNNode()
center.position = SCNVector3Make(15, 0, 10)
let lookConstraint = SCNLookAtConstraint(target: center)
lookConstraint.isGimbalLockEnabled = true
// Make the constraint to not allow the camera to point upwards
let transformConstraint = SCNTransformConstraint(inWorldSpace: true, with: {
node, matrix in
var newMatrix = matrix
let currentNode = node as SCNNode
if (currentNode.presentation.eulerAngles.x > 0) {
newMatrix.m31 = 0.0
}
return newMatrix
})
cameraNode.constraints = [lookConstraint, transformConstraint]
scene.rootNode.addChildNode(cameraNode)
I guess I am setting the newMatrix.m31 incorrectly?

Simple SceneKit scene shows black screen instead of SCNPlane

I am getting my feet wet with iOS SceneKit, however the following sample code (which executes inside viewDidLoad) is not behaving as expected. I want it to
place a camera at origin with direction of view towards positive z axis
place a red rectangle parallel to xy-plane at z = 100
Why does the rendering not reveal the red rectangle but only a black screen?
let scene = SCNScene()
// prepare camera
let camera = SCNCamera()
camera.zNear = 90
camera.zFar = 110
let cameraNode = SCNNode()
cameraNode.position = SCNVector3Make(0, 0, 0)
cameraNode.rotation = SCNVector4Make(1, 0, 0, Float(M_PI))
cameraNode.camera = camera
scene.rootNode.addChildNode(cameraNode)
// prepare light
let light = SCNLight()
light.type = SCNLightTypeOmni
light.color = SKColor(white: 0.3, alpha: 1.0)
let lightNode = SCNNode()
lightNode.light = light;
scene.rootNode.addChildNode(lightNode)
// prepare plane
let plane = SCNPlane(width: 400, height: 400)
plane.firstMaterial!.doubleSided = true
plane.firstMaterial!.diffuse.contents = UIColor.redColor().CGColor
let planeNode = SCNNode(geometry: plane)
planeNode.position = SCNVector3Make(0, 0, 100)
scene.rootNode.addChildNode(planeNode)
// prepare view as SCNView
let sceneView = view as SCNView
sceneView.backgroundColor = SKColor.blackColor()
sceneView.scene = scene
sceneView.delegate = self
sceneView.jitteringEnabled = true // i.e. improve visual rendering
sceneView.pointOfView = cameraNode
looks like you rotate around the x axis instead of the y axis (so that the camera looks in the desired direction)