Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make an SCNNode facing toward ARAnchor

Tags:

ios

swift

arkit

How can I make an ARNode pointing to an ARAnchor?

I want to use art.scnassets/ship.scn showing in the center of the screen and pointing to the object I just placed somewhere in the scene.

/// ViewController Class
func placeObject() {
    let screenCentre : CGPoint = CGPoint(x: self.sceneView.bounds.midX, y: self.sceneView.bounds.midY)
    guard let hitTestResult = sceneView.hitTest(screenCentre, types: [.featurePoint]).first else { return }

    // Place an anchor for a virtual character.
    let anchor = ARAnchor(name: identifierString, transform: hitTestResult.worldTransform)
    sceneView.session.add(anchor: anchor)
    // add to item model
    ItemModel.shared.anchors.append((identifierString, anchor)

}

func showDirection(of object: ARAnchor) { // object: saved anchor

    if !Guide.shared.isExist {
        let startPoint = SCNVector3(0, 0 , -1)
        let targetPoint = SCNVector3(object.transform.columns.3.x, object.transform.columns.3.y, object.transform.columns.3.z)

        let guideNode = Guide.shared.setPosition(from: startPoint, to: targetPoint)

        // add the ship in the center of the view
        sceneView.pointOfView?.addChildNode(guideNode)

    }
}
/// Guide Class   
func setPosition(from start: SCNVector3, to target: SCNVector3) -> SCNNode {
    isExist = true
    guideNode.position = start
    targetPosition = target

    // create target node from saved anchor
    let desNode = SCNNode()
    desNode.position = targetPosition

    let lookAtConstraints = SCNLookAtConstraint(target: desNode)
    guideNode.constraints = [lookAtConstraints]

    return guideNode
}

// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    if let name = anchor.name, name.hasPrefix(identifierString) {
        // Create 3D Text
        let textNode: SCNNode = createNewBubbleParentNode(identifierString)
        node.addChildNode(textNode)
    }

}

I tried SCNLookAtConstraint but it does not work as expected, any suggestions?

demo

like image 726
Willjay Avatar asked Nov 29 '18 00:11

Willjay


2 Answers

I don't know this will help you in your case or not. (If not i will delete my answer)

So I have faced same issue. Where I am drawing wall between two SCNVector object. Issue is wall is double sided so in some case part visible to camera is flipped.

like If you draw wall from two vectors and then add another Node (Plane Node) with same Euler Angle added Node (Plane) is Vertically Flipped.

I have searched many solution and tried many things but this working for me.

class func node(from:SCNVector3,
                to:SCNVector3,height:Float,needToAlignToCamera:Bool) -> SCNNode {
    let distance = MathHelper().getMeasurementBetween(vector1: from, and: to)

    let wall = SCNPlane(width: CGFloat(distance),
                        height: CGFloat(height))
    wall.firstMaterial = wallMaterial()
    let node = SCNNode(geometry: wall)

    // always render before the beachballs
    node.renderingOrder = -10


    // HERE IS MAGIC LINES 
    //============================================

    let normalizedTO = to.normalized()
    let normalizedFrom = from.normalized()
    let angleBetweenTwoVectors = normalizedTO.cross(normalizedFrom)

    var from = from
    var to = to

    if angleBetweenTwoVectors.y > 0 && needToAlignToCamera {
        let temp = from
        from = to
        to = temp
    }
    //============================================


    node.position = SCNVector3(from.x + (to.x - from.x) * 0.5,
                               from.y + height * 0.5,
                               from.z + (to.z - from.z) * 0.5)

    // orientation of the wall is fairly simple. we only need to orient it around the y axis,
    // and the angle is calculated with 2d math.. now this calculation does not factor in the position of the
    // camera, and so if you move the cursor right relative to the starting position the
    // wall will be oriented away from the camera (in this app the wall material is set as double sided so you will not notice)
    // - obviously if you want to render something on the walls, this issue will need to be resolved.


    node.eulerAngles = SCNVector3(0, -atan2(to.x - node.position.x, from.z - node.position.z) - Float.pi * 0.5, 0)


    return node
}

Extension

func cross(_ vec: SCNVector3) -> SCNVector3 {
        return SCNVector3(self.y * vec.z - self.z * vec.y, self.z * vec.x - self.x * vec.z, self.x * vec.y - self.y * vec.x)
    }

func normalized() -> SCNVector3 {
        if self.length() == 0 {
            return self
        }

        return self / self.length()
    }
like image 82
Prashant Tukadiya Avatar answered Sep 20 '22 16:09

Prashant Tukadiya


ARKit updates positions of anchors and corresponding nodes provided via delegate method renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?

So your constraints idea is correct, but you need to provide nodes via the delegate method instead of adding them directly to the scene.

Something like that -

    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        switch anchor {
        case MyAnchor:
          let constraint = // ...
          let node = // ...
          node.constraints = [constraint]
          return node
        default:
          return nil
        }
    }
like image 42
Maxim Volgin Avatar answered Sep 23 '22 16:09

Maxim Volgin