【问题标题】:Swift: ARKit place a rectangle based on 2 nodesSwift:ARKit 基于 2 个节点放置一个矩形
【发布时间】:2018-09-25 19:07:02
【问题描述】:

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

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

图像中的矩形具有比这些点更高的 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)
        }

    }

另请注意,这不是我的最终代码。一旦一切正常,它将被重构:)。

【问题讨论】:

  • 你有没有找到解决这个问题的方法?我对这种实现也有类似的问题。
  • 我发布了我当前的解决方案。尽管我已经重构了很多代码。希望它会有所帮助

标签: swift arkit scnnode


【解决方案1】:

目前正在运行。我用了错误的计算将它设置在 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 = []
}

确保世界对齐重置为您的相机位置

【讨论】:

  • 如果你要绘制一个连接多个点的填充多边形,你会如何做到这一点。
  • @Waruna 然后我会使用UIBezeierPath 在分配的点之间绘制它。然后我会计算多边形的中间:mathopenref.com/polygonradius.html,然后将其应用为位置
猜你喜欢
  • 1970-01-01
  • 2013-05-09
  • 2013-08-26
  • 2018-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多