如何使SCNNode面向ARAnchor

时间:2018-11-29 00:45:39

标签: ios swift arkit

如何使ARNode指向ARAnchor

我想使用art.scnassets/ship.scn在屏幕中央显示,并指​​向我刚刚放置在场景中某处的对象。

/// 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)
    }

}

我尝试了SCNLookAtConstraint,但没有按预期运行,有任何建议吗?

demo

3 个答案:

答案 0 :(得分:3)

我不知道这对您有没有帮助。 (否则,我将删除答案)

所以我也遇到了同样的问题。我在两个SCNVector对象之间绘制墙的地方。问题是墙壁是双面的,因此在某些情况下会翻转相机可见的部分。

like如果您从两个向量绘制墙,然后添加另一个具有相同欧拉角的节点(平面节点),则节点(平面)会垂直翻转。

我已经搜索了许多解决方案,并尝试了许多事情,但这对我有用。

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
}

扩展名

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()
    }

答案 1 :(得分:2)

void CROpportunity_RowPersisting(PXCache sender, PXRowPersistingEventArgs e, PXRowPersisting del) { // Don't call save action or persist // Just modify data and let the system save } 更新通过委托方法ARKit提供的锚点和相应节点的位置

因此约束条件的想法是正确的,但是您需要通过委托方法提供节点,而不是直接将其添加到场景中。

类似的东西-

renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?

答案 2 :(得分:1)

我注意到SCNLookAtConstraint在手机的方向平行于水平线时有效。 (屏幕朝上)

这行代码神奇! guideNode.pivot = SCNMatrix4Rotate(guideNode.pivot, Float.pi, 0, 1, 1)

如果您有兴趣,请参见here

 public func showDirection(of object: arItemList) {

    guard let pointOfView = sceneView.pointOfView else { return }

    // remove previous instruction
    for node in pointOfView.childNodes {
        node.removeFromParentNode()
    }

    // target
    let desNode = SCNNode()
    let targetPoint = SCNVector3.positionFromTransform(object.1.transform)
    desNode.worldPosition = targetPoint

    // guide
    let startPoint = SCNVector3(0, 0 , -1.0)
    let guideNode = loadObject()
    guideNode.scale = SCNVector3(0.7, 0.7, 0.7)
    guideNode.position = startPoint

    let lookAtConstraints = SCNLookAtConstraint(target: desNode)
    lookAtConstraints.isGimbalLockEnabled = true
    // Here's the magic
    guideNode.pivot = SCNMatrix4Rotate(guideNode.pivot, Float.pi, 0, 1, 1)

    guideNode.constraints = [lookAtConstraints]
    pointOfView.addChildNode(guideNode)
}