Swift:ARKit根据2个节点放置一个矩形

时间:2018-04-16 06:09:59

标签: swift arkit scnnode

我的想法是我想在选定的位置放置2个球形节点。从那时起,我基本上想要绘制一个矩形,用滑块调整高度。基本上这意味着2个球体将代表开头的所有4个角落。但是在测试代码时,我使用1米高度作为测试。

我遇到的问题是我似乎无法将矩形放在正确的位置,如下图所示:

image

图像中的矩形具有比点更高的y点,它稍微旋转并且其中心点在节点2之上而不在节点之间。我不想使用node.rotation因为它不会动态工作。

这是我用来放置2个节点并绘制+添加矩形的代码。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let location =  touches.first?.location(in: sceneView) else {return}
        let hitTest = sceneView.hitTest(location, types: [ARHitTestResult.ResultType.featurePoint])
        guard let result = hitTest.last else {return}

        // Converts the matrix_float4x4 to an SCNMatrix4 to be used with SceneKit
        let transform = SCNMatrix4.init(result.worldTransform)

        // Creates an SCNVector3 with certain indexes in the matrix
        let vector = SCNVector3Make(transform.m41, transform.m42, transform.m43)
        let node = addSphere(withPosition: vector)
        nodeArray.append(node)
        sceneView.scene.rootNode.addChildNode(node)

        if nodeArray.count == 2 {
            let node1 = sceneView.scene.rootNode.childNodes[0]
            let node2 = sceneView.scene.rootNode.childNodes[1]

            let bezeierPath = UIBezierPath()
            bezeierPath.lineWidth = 0.01
            bezeierPath.move(to: CGPoint(x: CGFloat(node1.position.x), y: CGFloat(node1.position.y)))
            bezeierPath.addLine(to: CGPoint(x: CGFloat(node2.position.x), y: CGFloat(node2.position.y)))
            bezeierPath.addLine(to: CGPoint(x: CGFloat(node2.position.x), y: CGFloat(node2.position.y+1.0)))
            bezeierPath.addLine(to: CGPoint(x: CGFloat(node1.position.x), y: CGFloat(node1.position.y+1.0)))
            bezeierPath.close()
            bezeierPath.fill()
            let shape = SCNShape(path: bezeierPath, extrusionDepth: 0.02)
            shape.firstMaterial?.diffuse.contents = UIColor.red.withAlphaComponent(0.8)
            let node = SCNNode.init(geometry: shape)
            node.position = SCNVector3(CGFloat(abs(node1.position.x-node2.position.x)/2), CGFloat(abs((node1.position.y)-(node2.position.y))/2), CGFloat(node1.position.z))
            sceneView.scene.rootNode.addChildNode(node)
        }

    }

另请注意,这不是我的最终代码。一旦我完成所有工作,它将被重构:)。

1 个答案:

答案 0 :(得分:0)

当前正在工作。我使用了错误的计算方法将其设置在2点的中间。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // Check if a window is placed already and fetch location in sceneView
    guard debugMode, let location = touches.first?.location(in: sceneView) else {return}
    // Fetch targets at the current location
    let hitTest = sceneView.hitTest(location, types: [ARHitTestResult.ResultType.featurePoint])
    guard let result = hitTest.last else {return}

    // Create sphere are position. getPosition() is an extension
    createSphere(withPosition: result.getPosition())

    // When 2 nodes has been placed, create a window and hide the spheres
    if nodeArray.count == 2 {
        let firstNode = nodeArray[nodeArray.count-1]
        let secondNode = nodeArray[nodeArray.count-2]
        firstNode.isHidden = true
        secondNode.isHidden = true
        createWindow(firstNode: firstNode, secondNode: secondNode)
        sceneView.debugOptions = []
    }
}

func createSphere(withPosition position: SCNVector3) {
    // Create an geometry which you will apply materials to and convert to a node
    let object = SCNSphere(radius: 0.01)
    let material = SCNMaterial()
    material.diffuse.contents = UIColor.red
    object.materials = [material]
    let node = SCNNode(geometry: object)
    node.position = position
    nodeArray.append(node)
    sceneView.scene.rootNode.addChildNode(node)
}

func createWindow(firstNode: SCNNode, secondNode: SCNNode) {
    // Create an geometry which you will apply materials to and convert to a node
    let shape = SCNBox(width: CGFloat(abs(firstNode.worldPosition.x-secondNode.worldPosition.x)), height: 0.01, length: 0.02, chamferRadius: 0)
    let material = SCNMaterial()
    material.diffuse.contents = UIColor.red.withAlphaComponent(0.8)
    // Each side of a cube can have different materials
    // https://stackoverflow.com/questions/27509092/scnbox-different-colour-or-texture-on-each-face
    shape.materials = [material]
    let node = SCNNode(geometry: shape)
    let minX = min(firstNode.worldPosition.x, secondNode.worldPosition.x)
    let maxX = max(firstNode.worldPosition.x, secondNode.worldPosition.x)

    // The nodes pivot Y-axis should be split in half of the nodes height, this will cause the node to
    // only scale in one direction, when scaling it
    // https://stackoverflow.com/questions/42568420/scenekit-understanding-the-pivot-property-of-scnnode/42613002#42613002
    node.position = SCNVector3(((maxX-minX)/2)+minX, firstNode.worldPosition.y, firstNode.worldPosition.z)
    node.pivot = SCNMatrix4MakeTranslation(0, 0.005, 0)
    node.scale = SCNVector3Make(1, -50, 1)
    node.name = "window"

    sceneView.scene.rootNode.addChildNode(node)
}

还添加:

 @objc func resetSceneView() {
    // Clear all the nodes
    sceneView.scene.rootNode.enumerateHierarchy { (node, stop) in
        node.childNodes.forEach({
            $0.removeFromParentNode()
        })
        node.removeFromParentNode()
    }

    // Reset the session
    sceneView.session.pause()
    sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
    sceneView.session.run(configuration, options: .resetTracking)
    let matrix = sceneView.session.currentFrame!.camera.transform
    sceneView.session.setWorldOrigin(relativeTransform: matrix)
    nodeArray = []
}

确保将世界对齐方式重置为相机位置