Search code examples
swiftscenekit

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

enter image description here

but want like this

enter image description here


Solution

  • 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.